NetworkManager/libnm-util/nm-setting-gsm.c
Dan Williams 5a7cf39a62 libnm-util: add secret flags for each secret describing how the secret is stored
This allows the necessary flexibility when handling secrets; otherwise
it wouldn't be known when NM should save secrets returned from agents
to backing storage, or when the agents should store the secrets. We
can't simply use lack of a secret in persistent storage as the indicator
of this, as (for example) when creating a new connection without
secrets the storage method would be abmiguous.

At the same time, fold in "always ask" functionality for OTP tokens
so user agents don't have to store that attribute themselves out-of-band.
2011-01-29 13:34:24 -06:00

696 lines
21 KiB
C

/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
/*
* Dan Williams <dcbw@redhat.com>
* Tambet Ingo <tambet@gmail.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301 USA.
*
* (C) Copyright 2007 - 2011 Red Hat, Inc.
* (C) Copyright 2007 - 2008 Novell, Inc.
*/
#include <string.h>
#include <ctype.h>
#include "nm-setting-gsm.h"
#include "nm-setting-serial.h"
#include "nm-utils.h"
GQuark
nm_setting_gsm_error_quark (void)
{
static GQuark quark;
if (G_UNLIKELY (!quark))
quark = g_quark_from_static_string ("nm-setting-gsm-error-quark");
return quark;
}
/* This should really be standard. */
#define ENUM_ENTRY(NAME, DESC) { NAME, "" #NAME "", DESC }
GType
nm_setting_gsm_error_get_type (void)
{
static GType etype = 0;
if (etype == 0) {
static const GEnumValue values[] = {
/* Unknown error. */
ENUM_ENTRY (NM_SETTING_GSM_ERROR_UNKNOWN, "UnknownError"),
/* The specified property was invalid. */
ENUM_ENTRY (NM_SETTING_GSM_ERROR_INVALID_PROPERTY, "InvalidProperty"),
/* The specified property was missing and is required. */
ENUM_ENTRY (NM_SETTING_GSM_ERROR_MISSING_PROPERTY, "MissingProperty"),
/* The required serial setting is missing */
ENUM_ENTRY (NM_SETTING_GSM_ERROR_MISSING_SERIAL_SETTING, "MissingSerialSetting"),
{ 0, 0, 0 }
};
etype = g_enum_register_static ("NMSettingGsmError", values);
}
return etype;
}
G_DEFINE_TYPE (NMSettingGsm, nm_setting_gsm, NM_TYPE_SETTING)
#define NM_SETTING_GSM_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_SETTING_GSM, NMSettingGsmPrivate))
typedef struct {
char *number; /* For dialing, duh */
char *username;
char *password;
NMSettingSecretFlags password_flags;
char *apn; /* NULL for dynamic */
char *network_id; /* for manual registration or NULL for automatic */
int network_type; /* One of the NM_SETTING_GSM_NETWORK_TYPE_* */
guint32 allowed_bands; /* A bitfield of NM_SETTING_GSM_BAND_* */
char *pin;
NMSettingSecretFlags pin_flags;
gboolean home_only;
} NMSettingGsmPrivate;
enum {
PROP_0,
PROP_NUMBER,
PROP_USERNAME,
PROP_PASSWORD,
PROP_PASSWORD_FLAGS,
PROP_APN,
PROP_NETWORK_ID,
PROP_NETWORK_TYPE,
PROP_PIN,
PROP_PIN_FLAGS,
PROP_ALLOWED_BANDS,
PROP_HOME_ONLY,
LAST_PROP
};
NMSetting *
nm_setting_gsm_new (void)
{
return (NMSetting *) g_object_new (NM_TYPE_SETTING_GSM, NULL);
}
static gint
find_setting_by_name (gconstpointer a, gconstpointer b)
{
NMSetting *setting = NM_SETTING (a);
const char *str = (const char *) b;
return strcmp (nm_setting_get_name (setting), str);
}
const char *
nm_setting_gsm_get_number (NMSettingGsm *setting)
{
g_return_val_if_fail (NM_IS_SETTING_GSM (setting), NULL);
return NM_SETTING_GSM_GET_PRIVATE (setting)->number;
}
const char *
nm_setting_gsm_get_username (NMSettingGsm *setting)
{
g_return_val_if_fail (NM_IS_SETTING_GSM (setting), NULL);
return NM_SETTING_GSM_GET_PRIVATE (setting)->username;
}
const char *
nm_setting_gsm_get_password (NMSettingGsm *setting)
{
g_return_val_if_fail (NM_IS_SETTING_GSM (setting), NULL);
return NM_SETTING_GSM_GET_PRIVATE (setting)->password;
}
/**
* nm_setting_gsm_get_password_flags:
* @setting: the #NMSettingGsm
*
* Returns: the #NMSettingSecretFlags pertaining to the #NMSettingGsm:password
**/
NMSettingSecretFlags
nm_setting_gsm_get_password_flags (NMSettingGsm *setting)
{
g_return_val_if_fail (NM_IS_SETTING_GSM (setting), NM_SETTING_SECRET_FLAG_SYSTEM_OWNED);
return NM_SETTING_GSM_GET_PRIVATE (setting)->password_flags;
}
const char *
nm_setting_gsm_get_apn (NMSettingGsm *setting)
{
g_return_val_if_fail (NM_IS_SETTING_GSM (setting), NULL);
return NM_SETTING_GSM_GET_PRIVATE (setting)->apn;
}
const char *
nm_setting_gsm_get_network_id (NMSettingGsm *setting)
{
g_return_val_if_fail (NM_IS_SETTING_GSM (setting), NULL);
return NM_SETTING_GSM_GET_PRIVATE (setting)->network_id;
}
int
nm_setting_gsm_get_network_type (NMSettingGsm *setting)
{
g_return_val_if_fail (NM_IS_SETTING_GSM (setting), -1);
return NM_SETTING_GSM_GET_PRIVATE (setting)->network_type;
}
guint32
nm_setting_gsm_get_allowed_bands (NMSettingGsm *setting)
{
g_return_val_if_fail (NM_IS_SETTING_GSM (setting), NM_SETTING_GSM_BAND_UNKNOWN);
return NM_SETTING_GSM_GET_PRIVATE (setting)->allowed_bands;
}
const char *
nm_setting_gsm_get_pin (NMSettingGsm *setting)
{
g_return_val_if_fail (NM_IS_SETTING_GSM (setting), NULL);
return NM_SETTING_GSM_GET_PRIVATE (setting)->pin;
}
/**
* nm_setting_gsm_get_pin_flags:
* @setting: the #NMSettingGsm
*
* Returns: the #NMSettingSecretFlags pertaining to the #NMSettingGsm:pin
**/
NMSettingSecretFlags
nm_setting_gsm_get_pin_flags (NMSettingGsm *setting)
{
g_return_val_if_fail (NM_IS_SETTING_GSM (setting), NM_SETTING_SECRET_FLAG_SYSTEM_OWNED);
return NM_SETTING_GSM_GET_PRIVATE (setting)->pin_flags;
}
gboolean
nm_setting_gsm_get_home_only (NMSettingGsm *setting)
{
g_return_val_if_fail (NM_IS_SETTING_GSM (setting), FALSE);
return NM_SETTING_GSM_GET_PRIVATE (setting)->home_only;
}
static gboolean
verify (NMSetting *setting, GSList *all_settings, GError **error)
{
NMSettingGsmPrivate *priv = NM_SETTING_GSM_GET_PRIVATE (setting);
/* Serial connections require a PPP setting */
if (all_settings &&
!g_slist_find_custom (all_settings, NM_SETTING_SERIAL_SETTING_NAME, find_setting_by_name)) {
g_set_error (error,
NM_SETTING_GSM_ERROR,
NM_SETTING_GSM_ERROR_MISSING_SERIAL_SETTING,
NULL);
return FALSE;
}
if (!priv->number) {
g_set_error (error,
NM_SETTING_GSM_ERROR,
NM_SETTING_GSM_ERROR_MISSING_PROPERTY,
NM_SETTING_GSM_NUMBER);
return FALSE;
} else if (!strlen (priv->number)) {
g_set_error (error,
NM_SETTING_GSM_ERROR,
NM_SETTING_GSM_ERROR_INVALID_PROPERTY,
NM_SETTING_GSM_NUMBER);
return FALSE;
}
if (priv->apn) {
guint32 apn_len = strlen (priv->apn);
guint32 i;
if (apn_len < 1 || apn_len > 64) {
g_set_error (error,
NM_SETTING_GSM_ERROR,
NM_SETTING_GSM_ERROR_INVALID_PROPERTY,
NM_SETTING_GSM_APN);
return FALSE;
}
/* APNs roughly follow the same rules as DNS domain names. Allowed
* characters are a-z, 0-9, . and -. GSM 03.60 Section 14.9.
*/
for (i = 0; i < apn_len; i++) {
if (!isalnum (priv->apn[i]) && (priv->apn[i] != '.') && (priv->apn[i] != '-')) {
g_set_error (error,
NM_SETTING_GSM_ERROR,
NM_SETTING_GSM_ERROR_INVALID_PROPERTY,
NM_SETTING_GSM_APN);
return FALSE;
}
}
}
if (priv->username && !strlen (priv->username)) {
g_set_error (error,
NM_SETTING_GSM_ERROR,
NM_SETTING_GSM_ERROR_INVALID_PROPERTY,
NM_SETTING_GSM_USERNAME);
return FALSE;
}
if (priv->password && !strlen (priv->password)) {
g_set_error (error,
NM_SETTING_GSM_ERROR,
NM_SETTING_GSM_ERROR_INVALID_PROPERTY,
NM_SETTING_GSM_USERNAME);
return FALSE;
}
if (priv->network_id) {
guint32 nid_len = strlen (priv->network_id);
guint32 i;
/* Accept both 5 and 6 digit MCC/MNC codes */
if ((nid_len < 5) || (nid_len > 6)) {
g_set_error (error,
NM_SETTING_GSM_ERROR,
NM_SETTING_GSM_ERROR_INVALID_PROPERTY,
NM_SETTING_GSM_NETWORK_ID);
return FALSE;
}
for (i = 0; i < nid_len; i++) {
if (!isdigit (priv->network_id[i])) {
g_set_error (error,
NM_SETTING_GSM_ERROR,
NM_SETTING_GSM_ERROR_INVALID_PROPERTY,
NM_SETTING_GSM_NETWORK_ID);
return FALSE;
}
}
}
return TRUE;
}
static GPtrArray *
need_secrets (NMSetting *setting)
{
NMSettingGsmPrivate *priv = NM_SETTING_GSM_GET_PRIVATE (setting);
GPtrArray *secrets = NULL;
if (priv->password)
return NULL;
if (priv->username) {
secrets = g_ptr_array_sized_new (1);
g_ptr_array_add (secrets, NM_SETTING_GSM_PASSWORD);
}
return secrets;
}
static void
nm_setting_gsm_init (NMSettingGsm *setting)
{
g_object_set (setting, NM_SETTING_NAME, NM_SETTING_GSM_SETTING_NAME, NULL);
}
static void
finalize (GObject *object)
{
NMSettingGsmPrivate *priv = NM_SETTING_GSM_GET_PRIVATE (object);
g_free (priv->number);
g_free (priv->username);
g_free (priv->password);
g_free (priv->apn);
g_free (priv->network_id);
g_free (priv->pin);
G_OBJECT_CLASS (nm_setting_gsm_parent_class)->finalize (object);
}
static void
set_property (GObject *object, guint prop_id,
const GValue *value, GParamSpec *pspec)
{
NMSettingGsmPrivate *priv = NM_SETTING_GSM_GET_PRIVATE (object);
char *tmp;
switch (prop_id) {
case PROP_NUMBER:
g_free (priv->number);
priv->number = g_value_dup_string (value);
break;
case PROP_USERNAME:
g_free (priv->username);
priv->username = g_value_dup_string (value);
break;
case PROP_PASSWORD:
g_free (priv->password);
priv->password = g_value_dup_string (value);
break;
case PROP_PASSWORD_FLAGS:
priv->password_flags = g_value_get_uint (value);
break;
case PROP_APN:
g_free (priv->apn);
priv->apn = NULL;
tmp = g_value_dup_string (value);
if (tmp)
priv->apn = g_strstrip (tmp);
break;
case PROP_NETWORK_ID:
g_free (priv->network_id);
priv->network_id = NULL;
tmp = g_value_dup_string (value);
if (tmp)
priv->network_id = g_strstrip (tmp);
break;
case PROP_NETWORK_TYPE:
priv->network_type = g_value_get_int (value);
break;
case PROP_ALLOWED_BANDS:
priv->allowed_bands = g_value_get_uint (value);
break;
case PROP_PIN:
g_free (priv->pin);
priv->pin = g_value_dup_string (value);
break;
case PROP_PIN_FLAGS:
priv->pin_flags = g_value_get_uint (value);
break;
case PROP_HOME_ONLY:
priv->home_only = g_value_get_boolean (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
get_property (GObject *object, guint prop_id,
GValue *value, GParamSpec *pspec)
{
NMSettingGsm *setting = NM_SETTING_GSM (object);
switch (prop_id) {
case PROP_NUMBER:
g_value_set_string (value, nm_setting_gsm_get_number (setting));
break;
case PROP_USERNAME:
g_value_set_string (value, nm_setting_gsm_get_username (setting));
break;
case PROP_PASSWORD:
g_value_set_string (value, nm_setting_gsm_get_password (setting));
break;
case PROP_PASSWORD_FLAGS:
g_value_set_uint (value, nm_setting_gsm_get_password_flags (setting));
break;
case PROP_APN:
g_value_set_string (value, nm_setting_gsm_get_apn (setting));
break;
case PROP_NETWORK_ID:
g_value_set_string (value, nm_setting_gsm_get_network_id (setting));
break;
case PROP_NETWORK_TYPE:
g_value_set_int (value, nm_setting_gsm_get_network_type (setting));
break;
case PROP_ALLOWED_BANDS:
g_value_set_uint (value, nm_setting_gsm_get_allowed_bands (setting));
break;
case PROP_PIN:
g_value_set_string (value, nm_setting_gsm_get_pin (setting));
break;
case PROP_PIN_FLAGS:
g_value_set_uint (value, nm_setting_gsm_get_pin_flags (setting));
break;
case PROP_HOME_ONLY:
g_value_set_boolean (value, nm_setting_gsm_get_home_only (setting));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
nm_setting_gsm_class_init (NMSettingGsmClass *setting_class)
{
GObjectClass *object_class = G_OBJECT_CLASS (setting_class);
NMSettingClass *parent_class = NM_SETTING_CLASS (setting_class);
g_type_class_add_private (setting_class, sizeof (NMSettingGsmPrivate));
/* virtual methods */
object_class->set_property = set_property;
object_class->get_property = get_property;
object_class->finalize = finalize;
parent_class->verify = verify;
parent_class->need_secrets = need_secrets;
/* Properties */
/**
* NMSettingGsm:number:
*
* Number to dial when establishing a PPP data session with the GSM-based
* mobile broadband network. In most cases, leave the number blank and a
* number selecting the APN specified in the 'apn' property will be used
* automatically when required.
**/
g_object_class_install_property
(object_class, PROP_NUMBER,
g_param_spec_string (NM_SETTING_GSM_NUMBER,
"Number",
"Number to dial when establishing a PPP data session "
"with the GSM-based mobile broadband network. In most "
"cases, leave the number blank and a number selecting "
"the APN specified in the 'apn' property will be used "
"automatically when required.",
NULL,
G_PARAM_READWRITE | NM_SETTING_PARAM_SERIALIZE));
/**
* NMSettingGsm:username:
*
* The username used to authenticate with the network, if required. Many
* providers do not require a username, or accept any username. But if a
* username is required, it is specified here.
**/
g_object_class_install_property
(object_class, PROP_USERNAME,
g_param_spec_string (NM_SETTING_GSM_USERNAME,
"Username",
"Username used to authenticate with the network, if "
"required. Note that many providers do not require "
"a username or accept any username.",
NULL,
G_PARAM_READWRITE | NM_SETTING_PARAM_SERIALIZE));
/**
* NMSettingGsm:password:
*
* The password used to authenticate with the network, if required. Many
* providers do not require a password, or accept any password. But if a
* password is required, it is specified here.
**/
g_object_class_install_property
(object_class, PROP_PASSWORD,
g_param_spec_string (NM_SETTING_GSM_PASSWORD,
"Password",
"Password used to authenticate with the network, if "
"required. Note that many providers do not require "
"a password or accept any password.",
NULL,
G_PARAM_READWRITE | NM_SETTING_PARAM_SERIALIZE | NM_SETTING_PARAM_SECRET));
/**
* NMSettingGsm:password-flags:
*
* Flags indicating how to handle #NMSettingGsm:password:.
**/
g_object_class_install_property (object_class, PROP_PASSWORD_FLAGS,
g_param_spec_uint (NM_SETTING_GSM_PASSWORD_FLAGS,
"Password Flags",
"Flags indicating how to handle the GSM password.",
NM_SETTING_SECRET_FLAG_SYSTEM_OWNED,
NM_SETTING_SECRET_FLAG_LAST,
NM_SETTING_SECRET_FLAG_SYSTEM_OWNED,
G_PARAM_READWRITE | NM_SETTING_PARAM_SERIALIZE));
/**
* NMSettingGsm:apn:
*
* The GPRS Access Point Name specifying the APN used when establishing a
* data session with the GSM-based network. The APN often determines how
* the user will be billed for their network usage and whether the user has
* access to the Internet or just a provider-specific walled-garden, so it
* is important to use the correct APN for the user's mobile broadband plan.
* The APN may only be composed of the characters a-z, 0-9, ., and - per
* GSM 03.60 Section 14.9.
**/
g_object_class_install_property
(object_class, PROP_APN,
g_param_spec_string (NM_SETTING_GSM_APN,
"APN",
"The GPRS Access Point Name specifying the APN used "
"when establishing a data session with the GSM-based "
"network. The APN often determines how the user will "
"be billed for their network usage and whether the "
"user has access to the Internet or just a provider-"
"specific walled-garden, so it is important to use "
"the correct APN for the user's mobile broadband "
"plan. The APN may only be composed of the characters "
"a-z, 0-9, ., and - per GSM 03.60 Section 14.9.",
NULL,
G_PARAM_READWRITE | NM_SETTING_PARAM_SERIALIZE));
/**
* NMSettingGsm:network-id:
*
* The Network ID (GSM LAI format, ie MCC-MNC) to force specific network
* registration. If the Network ID is specified, NetworkManager will
* attempt to force the device to register only on the specified network.
* This can be used to ensure that the device does not roam when direct
* roaming control of the device is not otherwise possible.
**/
g_object_class_install_property
(object_class, PROP_NETWORK_ID,
g_param_spec_string (NM_SETTING_GSM_NETWORK_ID,
"Network ID",
"The Network ID (GSM LAI format, ie MCC-MNC) to force "
"specific network registration. If the Network ID is "
"specified, NetworkManager will attempt to force the "
"device to register only on the specified network. "
"This can be used to ensure that the device does not "
"roam when direct roaming control of the device is "
"not otherwise possible.",
NULL,
G_PARAM_READWRITE | NM_SETTING_PARAM_SERIALIZE));
/**
* NMSettingGsm:network-type:
*
* Network preference to force the device to only use specific network
* technologies. The permitted values are: -1: any, 0: 3G only,
* 1: GPRS/EDGE only, 2: prefer 3G, and 3: prefer 2G. Note that not all
* devices allow network preference control.
**/
g_object_class_install_property
(object_class, PROP_NETWORK_TYPE,
g_param_spec_int (NM_SETTING_GSM_NETWORK_TYPE,
"Network type",
"Network preference to force the device to only use "
"specific network technologies. The permitted values "
"are: -1: any, 0: 3G only, 1: GPRS/EDGE only, "
"2: prefer 3G, and 3: prefer 2G. Note that not all "
"devices allow network preference control.",
NM_SETTING_GSM_NETWORK_TYPE_ANY,
NM_SETTING_GSM_NETWORK_TYPE_PREFER_GPRS_EDGE,
NM_SETTING_GSM_NETWORK_TYPE_ANY,
G_PARAM_READWRITE | G_PARAM_CONSTRUCT | NM_SETTING_PARAM_SERIALIZE));
/**
* NMSettingGsm:allowed-bands:
*
* Bitfield of allowed frequency bands. Note that not all devices allow
* frequency band control.
**/
g_object_class_install_property
(object_class, PROP_ALLOWED_BANDS,
g_param_spec_uint (NM_SETTING_GSM_ALLOWED_BANDS,
"Allowed Bands",
"Bitfield of allowed frequency bands. Note that "
"not all devices allow frequency band control.",
NM_SETTING_GSM_BAND_UNKNOWN,
NM_SETTING_GSM_BAND_UNKNOWN
|| NM_SETTING_GSM_BAND_ANY
|| NM_SETTING_GSM_BAND_EGSM
|| NM_SETTING_GSM_BAND_DCS
|| NM_SETTING_GSM_BAND_PCS
|| NM_SETTING_GSM_BAND_G850
|| NM_SETTING_GSM_BAND_U2100
|| NM_SETTING_GSM_BAND_U1800
|| NM_SETTING_GSM_BAND_U17IV
|| NM_SETTING_GSM_BAND_U800
|| NM_SETTING_GSM_BAND_U850
|| NM_SETTING_GSM_BAND_U900
|| NM_SETTING_GSM_BAND_U17IX
|| NM_SETTING_GSM_BAND_U1900,
NM_SETTING_GSM_BAND_ANY,
G_PARAM_READWRITE | G_PARAM_CONSTRUCT | NM_SETTING_PARAM_SERIALIZE));
/**
* NMSettingGsm:pin:
*
* If the SIM is locked with a PIN it must be unlocked before any other
* operations are requested. Specify the PIN here to allow operation of
* the device.
**/
g_object_class_install_property
(object_class, PROP_PIN,
g_param_spec_string (NM_SETTING_GSM_PIN,
"PIN",
"If the SIM is locked with a PIN it must be unlocked "
"before any other operations are requested. Specify "
"the PIN here to allow operation of the device.",
NULL,
G_PARAM_READWRITE | NM_SETTING_PARAM_SERIALIZE | NM_SETTING_PARAM_SECRET));
/**
* NMSettingGsm:pin-flags:
*
* Flags indicating how to handle #NMSettingGsm:pin:.
**/
g_object_class_install_property (object_class, PROP_PIN_FLAGS,
g_param_spec_uint (NM_SETTING_GSM_PIN_FLAGS,
"PIN Flags",
"Flags indicating how to handle the GSM SIM PIN.",
NM_SETTING_SECRET_FLAG_SYSTEM_OWNED,
NM_SETTING_SECRET_FLAG_LAST,
NM_SETTING_SECRET_FLAG_SYSTEM_OWNED,
G_PARAM_READWRITE | NM_SETTING_PARAM_SERIALIZE));
/**
* NMSettingGsm:home-only:
*
* When TRUE, only connections to the home network will be allowed.
* Connections to roaming networks will not be made.
**/
g_object_class_install_property
(object_class, PROP_HOME_ONLY,
g_param_spec_boolean (NM_SETTING_GSM_HOME_ONLY,
"PIN",
"When TRUE, only connections to the home network will "
"be allowed. Connections to roaming networks will "
"not be made.",
FALSE,
G_PARAM_READWRITE | NM_SETTING_PARAM_SERIALIZE));
}