From 215306f5a1e4dc38ec02a484c31470bb048d668b Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Mon, 10 Jan 2011 23:39:12 -0600 Subject: [PATCH] core: add AddAndActivate D-Bus method Given connection details, complete the connection as well as possible using the given specific object and device, add it to system settings, and activate it all in one method. --- .gitignore | 1 + introspection/nm-manager.xml | 49 ++ src/Makefile.am | 23 +- src/modem-manager/nm-modem-cdma.c | 48 +- src/modem-manager/nm-modem-gsm.c | 53 +- src/modem-manager/nm-modem.c | 11 + src/modem-manager/nm-modem.h | 10 + src/nm-device-bt.c | 158 ++++- src/nm-device-ethernet.c | 64 +- src/nm-device-modem.c | 13 + src/nm-device-modem.h | 2 +- src/nm-device-olpc-mesh.c | 47 +- src/nm-device-private.h | 8 +- src/nm-device-wifi.c | 226 ++++++- src/nm-device.c | 149 ++++- src/nm-device.h | 14 +- src/nm-manager.c | 217 +++++-- src/nm-wifi-ap-utils.c | 699 +++++++++++++++++++++ src/nm-wifi-ap-utils.h | 43 ++ src/nm-wifi-ap.c | 22 + src/nm-wifi-ap.h | 7 +- src/settings/nm-settings.c | 71 ++- src/settings/nm-settings.h | 12 + src/tests/Makefile.am | 20 +- src/tests/test-wifi-ap-utils.c | 973 ++++++++++++++++++++++++++++++ 25 files changed, 2839 insertions(+), 101 deletions(-) create mode 100644 src/nm-wifi-ap-utils.c create mode 100644 src/nm-wifi-ap-utils.h create mode 100644 src/tests/test-wifi-ap-utils.c diff --git a/.gitignore b/.gitignore index 9807641988..848da36b55 100644 --- a/.gitignore +++ b/.gitignore @@ -94,6 +94,7 @@ libnm-util/tests/test-setting-8021x libnm-glib/tests/test-remote-settings-client src/tests/test-dhcp-options src/tests/test-policy-hosts +src/tests/test-wifi-ap-utils system-settings/plugins/keyfile/tests/test-keyfile system-settings/plugins/ifcfg-rh/tests/test-ifcfg-rh diff --git a/introspection/nm-manager.xml b/introspection/nm-manager.xml index 92a78b349a..5e087a2634 100644 --- a/introspection/nm-manager.xml +++ b/introspection/nm-manager.xml @@ -66,6 +66,55 @@ + + + + + Adds a new connection using the given details (if any) as a template + (automatically filling in missing settings with the capabilities of the + given device and specific object), then activate the new connection. + Cannot be used for VPN connections at this time. + + + + Connection settings and properties; if incomplete missing settings will + be automatically completed using the given device and specific object. + + + + + The object path of device to be activated using the given connection. + + + + + The path of a connection-type-specific object this activation should use. + This parameter is currently ignored for wired and mobile broadband connections, + and the value of "/" should be used (ie, no specific object). For WiFi + connections, pass the object path of a specific AP from the card's scan + list, which will be used to complete the details of the newly added + connection. + + + + + Object path of the new connection that was just added. + + + + + The path of the active connection object representing this active connection. + + + + + + + The connection is invalid for this device. + + + + Deactivate an active connection. diff --git a/src/Makefile.am b/src/Makefile.am index 66be644c4b..5f1b63c235 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -35,7 +35,10 @@ INCLUDES = -I${top_srcdir} \ # Test libraries ########################################### -noinst_LTLIBRARIES = libtest-dhcp.la libtest-policy-hosts.la +noinst_LTLIBRARIES = \ + libtest-dhcp.la \ + libtest-policy-hosts.la \ + libtest-wifi-ap-utils.la ########################################### # DHCP test library @@ -76,6 +79,22 @@ libtest_policy_hosts_la_LIBADD = \ $(GLIB_LIBS) +########################################### +# Wifi ap utils +########################################### + +libtest_wifi_ap_utils_la_SOURCES = \ + nm-wifi-ap-utils.c \ + nm-wifi-ap-utils.h + +libtest_wifi_ap_utils_la_CPPFLAGS = \ + $(GLIB_CFLAGS) + +libtest_wifi_ap_utils_la_LIBADD = \ + ${top_builddir}/libnm-util/libnm-util.la \ + $(GLIB_LIBS) + + ########################################### # NetworkManager ########################################### @@ -106,6 +125,8 @@ NetworkManager_SOURCES = \ nm-device-gsm.h \ nm-wifi-ap.c \ nm-wifi-ap.h \ + nm-wifi-ap-utils.c \ + nm-wifi-ap-utils.h \ nm-dbus-manager.h \ nm-dbus-manager.c \ nm-udev-manager.c \ diff --git a/src/modem-manager/nm-modem-cdma.c b/src/modem-manager/nm-modem-cdma.c index c32c18222c..63b2d66fdc 100644 --- a/src/modem-manager/nm-modem-cdma.c +++ b/src/modem-manager/nm-modem-cdma.c @@ -15,19 +15,23 @@ * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * - * Copyright (C) 2009 - 2010 Red Hat, Inc. + * Copyright (C) 2009 - 2011 Red Hat, Inc. * Copyright (C) 2009 Novell, Inc. */ #include +#include #include "nm-dbus-glib-types.h" #include "nm-modem-cdma.h" #include "nm-modem-types.h" #include "nm-device.h" +#include "nm-device-private.h" #include "nm-dbus-manager.h" #include "nm-setting-connection.h" #include "nm-setting-cdma.h" +#include "nm-setting-serial.h" +#include "nm-setting-ppp.h" #include "NetworkManagerUtils.h" #include "nm-logging.h" @@ -269,6 +273,47 @@ real_check_connection_compatible (NMModem *modem, return TRUE; } +static gboolean +real_complete_connection (NMModem *modem, + NMConnection *connection, + const GSList *existing_connections, + GError **error) +{ + NMSettingCdma *s_cdma; + NMSettingSerial *s_serial; + NMSettingPPP *s_ppp; + + s_cdma = (NMSettingCdma *) nm_connection_get_setting (connection, NM_TYPE_SETTING_CDMA); + s_serial = (NMSettingSerial *) nm_connection_get_setting (connection, NM_TYPE_SETTING_SERIAL); + s_ppp = (NMSettingPPP *) nm_connection_get_setting (connection, NM_TYPE_SETTING_PPP); + + if (!s_cdma) { + s_cdma = (NMSettingCdma *) nm_setting_cdma_new (); + nm_connection_add_setting (connection, NM_SETTING (s_cdma)); + } + + if (!nm_setting_cdma_get_number (s_cdma)) + g_object_set (G_OBJECT (s_cdma), NM_SETTING_CDMA_NUMBER, "#777", NULL); + + /* Need serial and PPP settings at least */ + if (!s_serial) { + s_serial = (NMSettingSerial *) nm_setting_serial_new (); + nm_connection_add_setting (connection, NM_SETTING (s_serial)); + } + if (!s_ppp) { + s_ppp = (NMSettingPPP *) nm_setting_ppp_new (); + nm_connection_add_setting (connection, NM_SETTING (s_ppp)); + } + + nm_device_complete_generic (connection, + NM_SETTING_CDMA_SETTING_NAME, + existing_connections, + _("CDMA connection %d"), + NULL); + + return TRUE; +} + static gboolean real_get_user_pass (NMModem *modem, NMConnection *connection, @@ -344,6 +389,7 @@ nm_modem_cdma_class_init (NMModemCdmaClass *klass) modem_class->get_setting_name = real_get_setting_name; modem_class->get_best_auto_connection = real_get_best_auto_connection; modem_class->check_connection_compatible = real_check_connection_compatible; + modem_class->complete_connection = real_complete_connection; modem_class->act_stage1_prepare = real_act_stage1_prepare; modem_class->deactivate_quickly = real_deactivate_quickly; diff --git a/src/modem-manager/nm-modem-gsm.c b/src/modem-manager/nm-modem-gsm.c index 9a79535ebc..4672f4cee1 100644 --- a/src/modem-manager/nm-modem-gsm.c +++ b/src/modem-manager/nm-modem-gsm.c @@ -15,16 +15,21 @@ * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * - * Copyright (C) 2009 - 2010 Red Hat, Inc. + * Copyright (C) 2009 - 2011 Red Hat, Inc. * Copyright (C) 2009 Novell, Inc. */ #include +#include + #include "nm-dbus-glib-types.h" #include "nm-modem-gsm.h" #include "nm-device.h" +#include "nm-device-private.h" #include "nm-setting-connection.h" #include "nm-setting-gsm.h" +#include "nm-setting-serial.h" +#include "nm-setting-ppp.h" #include "nm-modem-types.h" #include "nm-logging.h" #include "NetworkManagerUtils.h" @@ -467,6 +472,51 @@ real_check_connection_compatible (NMModem *modem, return TRUE; } +static gboolean +real_complete_connection (NMModem *modem, + NMConnection *connection, + const GSList *existing_connections, + GError **error) +{ + NMSettingGsm *s_gsm; + NMSettingSerial *s_serial; + NMSettingPPP *s_ppp; + + s_gsm = (NMSettingGsm *) nm_connection_get_setting (connection, NM_TYPE_SETTING_GSM); + s_serial = (NMSettingSerial *) nm_connection_get_setting (connection, NM_TYPE_SETTING_SERIAL); + s_ppp = (NMSettingPPP *) nm_connection_get_setting (connection, NM_TYPE_SETTING_PPP); + + if (!s_gsm || !nm_setting_gsm_get_apn (s_gsm)) { + /* Need an APN at least */ + g_set_error_literal (error, + NM_SETTING_GSM_ERROR, + NM_SETTING_GSM_ERROR_MISSING_PROPERTY, + NM_SETTING_GSM_APN); + return FALSE; + } + + if (!nm_setting_gsm_get_number (s_gsm)) + g_object_set (G_OBJECT (s_gsm), NM_SETTING_GSM_NUMBER, "*99#", NULL); + + /* Need serial and PPP settings at least */ + if (!s_serial) { + s_serial = (NMSettingSerial *) nm_setting_serial_new (); + nm_connection_add_setting (connection, NM_SETTING (s_serial)); + } + if (!s_ppp) { + s_ppp = (NMSettingPPP *) nm_setting_ppp_new (); + nm_connection_add_setting (connection, NM_SETTING (s_ppp)); + } + + nm_device_complete_generic (connection, + NM_SETTING_GSM_SETTING_NAME, + existing_connections, + _("GSM connection %d"), + NULL); + + return TRUE; +} + static gboolean real_get_user_pass (NMModem *modem, NMConnection *connection, @@ -545,6 +595,7 @@ nm_modem_gsm_class_init (NMModemGsmClass *klass) modem_class->get_setting_name = real_get_setting_name; modem_class->get_best_auto_connection = real_get_best_auto_connection; modem_class->check_connection_compatible = real_check_connection_compatible; + modem_class->complete_connection = real_complete_connection; modem_class->act_stage1_prepare = real_act_stage1_prepare; modem_class->deactivate_quickly = real_deactivate_quickly; diff --git a/src/modem-manager/nm-modem.c b/src/modem-manager/nm-modem.c index 6f62eab498..5bba7030ba 100644 --- a/src/modem-manager/nm-modem.c +++ b/src/modem-manager/nm-modem.c @@ -619,6 +619,17 @@ nm_modem_check_connection_compatible (NMModem *self, return FALSE; } +gboolean +nm_modem_complete_connection (NMModem *self, + NMConnection *connection, + const GSList *existing_connections, + GError **error) +{ + if (NM_MODEM_GET_CLASS (self)->complete_connection) + return NM_MODEM_GET_CLASS (self)->complete_connection (self, connection, existing_connections, error); + return FALSE; +} + static void real_deactivate_quickly (NMModem *self, NMDevice *device) { diff --git a/src/modem-manager/nm-modem.h b/src/modem-manager/nm-modem.h index 15933e49d8..f4f7be7985 100644 --- a/src/modem-manager/nm-modem.h +++ b/src/modem-manager/nm-modem.h @@ -67,6 +67,11 @@ typedef struct { NMConnection *connection, GError **error); + gboolean (*complete_connection) (NMModem *modem, + NMConnection *connection, + const GSList *existing_connections, + GError **error); + NMConnection * (*get_best_auto_connection) (NMModem *modem, GSList *connections, char **specific_object); @@ -107,6 +112,11 @@ gboolean nm_modem_check_connection_compatible (NMModem *self, NMConnection *connection, GError **error); +gboolean nm_modem_complete_connection (NMModem *self, + NMConnection *connection, + const GSList *existing_connections, + GError **error); + NMActStageReturn nm_modem_act_stage1_prepare (NMModem *modem, NMActRequest *req, NMDeviceStateReason *reason); diff --git a/src/nm-device-bt.c b/src/nm-device-bt.c index f6bd284ccf..f2ce78e638 100644 --- a/src/nm-device-bt.c +++ b/src/nm-device-bt.c @@ -15,12 +15,15 @@ * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * - * Copyright (C) 2009 - 2010 Red Hat, Inc. + * Copyright (C) 2009 - 2011 Red Hat, Inc. */ #include #include #include +#include + +#include #include "nm-glib-compat.h" #include "nm-bluez-common.h" @@ -36,6 +39,8 @@ #include "nm-setting-bluetooth.h" #include "nm-setting-cdma.h" #include "nm-setting-gsm.h" +#include "nm-setting-serial.h" +#include "nm-setting-ppp.h" #include "nm-device-bt-glue.h" #include "NetworkManagerUtils.h" @@ -250,6 +255,156 @@ real_check_connection_compatible (NMDevice *device, return addr_match; } +static gboolean +real_complete_connection (NMDevice *device, + NMConnection *connection, + const char *specific_object, + const GSList *existing_connections, + GError **error) +{ + NMDeviceBtPrivate *priv = NM_DEVICE_BT_GET_PRIVATE (device); + NMSettingBluetooth *s_bt; + const GByteArray *setting_bdaddr; + struct ether_addr *devaddr = ether_aton (priv->bdaddr); + const char *ctype; + gboolean is_dun = FALSE, is_pan = FALSE; + NMSettingGsm *s_gsm; + NMSettingCdma *s_cdma; + NMSettingSerial *s_serial; + NMSettingPPP *s_ppp; + const char *format = NULL, *preferred = NULL; + + s_gsm = (NMSettingGsm *) nm_connection_get_setting (connection, NM_TYPE_SETTING_GSM); + s_cdma = (NMSettingCdma *) nm_connection_get_setting (connection, NM_TYPE_SETTING_CDMA); + s_serial = (NMSettingSerial *) nm_connection_get_setting (connection, NM_TYPE_SETTING_SERIAL); + s_ppp = (NMSettingPPP *) nm_connection_get_setting (connection, NM_TYPE_SETTING_PPP); + + s_bt = (NMSettingBluetooth *) nm_connection_get_setting (connection, NM_TYPE_SETTING_BLUETOOTH); + if (!s_bt) { + s_bt = (NMSettingBluetooth *) nm_setting_bluetooth_new (); + nm_connection_add_setting (connection, NM_SETTING (s_bt)); + } + + ctype = nm_setting_bluetooth_get_connection_type (s_bt); + if (ctype) { + if (!strcmp (ctype, NM_SETTING_BLUETOOTH_TYPE_DUN)) + is_dun = TRUE; + else if (!strcmp (ctype, NM_SETTING_BLUETOOTH_TYPE_PANU)) + is_pan = TRUE; + } else { + if (s_gsm || s_cdma) + is_dun = TRUE; + else if (priv->capabilities & NM_BT_CAPABILITY_NAP) + is_pan = TRUE; + } + + if (is_pan) { + /* Make sure the device supports PAN */ + if (!(priv->capabilities & NM_BT_CAPABILITY_NAP)) { + g_set_error_literal (error, + NM_SETTING_BLUETOOTH_ERROR, + NM_SETTING_BLUETOOTH_ERROR_INVALID_PROPERTY, + "PAN required but Bluetooth device does not support NAP"); + return FALSE; + } + + /* PAN can't use any DUN-related settings */ + if (s_gsm || s_cdma || s_serial) { + g_set_error_literal (error, + NM_SETTING_BLUETOOTH_ERROR, + NM_SETTING_BLUETOOTH_ERROR_INVALID_PROPERTY, + "PAN incompatible with GSM, CDMA, or serial settings"); + return FALSE; + } + + g_object_set (G_OBJECT (s_bt), + NM_SETTING_BLUETOOTH_TYPE, NM_SETTING_BLUETOOTH_TYPE_PANU, + NULL); + + format = _("PAN connection %d"); + } else if (is_dun) { + /* Make sure the device supports PAN */ + if (!(priv->capabilities & NM_BT_CAPABILITY_DUN)) { + g_set_error_literal (error, + NM_SETTING_BLUETOOTH_ERROR, + NM_SETTING_BLUETOOTH_ERROR_INVALID_PROPERTY, + "DUN required but Bluetooth device does not support DUN"); + return FALSE; + } + + /* Need at least a GSM or a CDMA setting */ + if (!s_gsm && !s_cdma) { + g_set_error_literal (error, + NM_SETTING_BLUETOOTH_ERROR, + NM_SETTING_BLUETOOTH_ERROR_INVALID_PROPERTY, + "Setting requires DUN but no GSM or CDMA setting is present"); + return FALSE; + } + + g_object_set (G_OBJECT (s_bt), + NM_SETTING_BLUETOOTH_TYPE, NM_SETTING_BLUETOOTH_TYPE_DUN, + NULL); + + if (s_gsm) { + format = _("GSM connection %d"); + if (!nm_setting_gsm_get_number (s_gsm)) + g_object_set (G_OBJECT (s_gsm), NM_SETTING_GSM_NUMBER, "*99#", NULL); + } else if (s_cdma) { + format = _("CDMA connection %d"); + if (!nm_setting_cdma_get_number (s_cdma)) + g_object_set (G_OBJECT (s_cdma), NM_SETTING_GSM_NUMBER, "#777", NULL); + } else + format = _("DUN connection %d"); + + /* Need serial and PPP settings */ + if (!s_serial) { + s_serial = (NMSettingSerial *) nm_setting_serial_new (); + nm_connection_add_setting (connection, NM_SETTING (s_serial)); + } + if (!s_ppp) { + s_ppp = (NMSettingPPP *) nm_setting_ppp_new (); + nm_connection_add_setting (connection, NM_SETTING (s_ppp)); + } + } else { + g_set_error_literal (error, + NM_SETTING_BLUETOOTH_ERROR, + NM_SETTING_BLUETOOTH_ERROR_INVALID_PROPERTY, + "Unknown/unhandled Bluetooth connection type"); + return FALSE; + } + + nm_device_complete_generic (connection, + NM_SETTING_BLUETOOTH_SETTING_NAME, + existing_connections, + format, + preferred); + + setting_bdaddr = nm_setting_bluetooth_get_bdaddr (s_bt); + if (setting_bdaddr) { + /* Make sure the setting BT Address (if any) matches the device's */ + if (memcmp (setting_bdaddr->data, devaddr->ether_addr_octet, ETH_ALEN)) { + g_set_error_literal (error, + NM_SETTING_BLUETOOTH_ERROR, + NM_SETTING_BLUETOOTH_ERROR_INVALID_PROPERTY, + NM_SETTING_BLUETOOTH_BDADDR); + return FALSE; + } + } else { + GByteArray *bdaddr; + const guint8 null_mac[ETH_ALEN] = { 0, 0, 0, 0, 0, 0 }; + + /* Lock the connection to this device by default */ + if (memcmp (devaddr->ether_addr_octet, null_mac, ETH_ALEN)) { + bdaddr = g_byte_array_sized_new (ETH_ALEN); + g_byte_array_append (bdaddr, devaddr->ether_addr_octet, ETH_ALEN); + g_object_set (G_OBJECT (s_bt), NM_SETTING_BLUETOOTH_BDADDR, bdaddr, NULL); + g_byte_array_free (bdaddr, TRUE); + } + } + + return TRUE; +} + static guint32 real_get_generic_capabilities (NMDevice *dev) { @@ -1000,6 +1155,7 @@ nm_device_bt_class_init (NMDeviceBtClass *klass) device_class->act_stage3_ip4_config_start = real_act_stage3_ip4_config_start; device_class->act_stage4_get_ip4_config = real_act_stage4_get_ip4_config; device_class->check_connection_compatible = real_check_connection_compatible; + device_class->complete_connection = real_complete_connection; /* Properties */ g_object_class_install_property diff --git a/src/nm-device-ethernet.c b/src/nm-device-ethernet.c index d759df2905..e38d58576b 100644 --- a/src/nm-device-ethernet.c +++ b/src/nm-device-ethernet.c @@ -15,7 +15,7 @@ * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * - * Copyright (C) 2005 - 2010 Red Hat, Inc. + * Copyright (C) 2005 - 2011 Red Hat, Inc. * Copyright (C) 2006 - 2008 Novell, Inc. */ @@ -1624,6 +1624,67 @@ real_check_connection_compatible (NMDevice *device, return TRUE; } +static gboolean +real_complete_connection (NMDevice *device, + NMConnection *connection, + const char *specific_object, + const GSList *existing_connections, + GError **error) +{ + NMDeviceEthernetPrivate *priv = NM_DEVICE_ETHERNET_GET_PRIVATE (device); + NMSettingWired *s_wired; + NMSettingPPPOE *s_pppoe; + const GByteArray *setting_mac; + + s_pppoe = (NMSettingPPPOE *) nm_connection_get_setting (connection, NM_TYPE_SETTING_PPPOE); + + /* We can't telepathically figure out the service name or username, so if + * those weren't given, we can't complete the connection. + */ + if (s_pppoe && !nm_setting_verify (NM_SETTING (s_pppoe), NULL, error)) + return FALSE; + + /* Default to an ethernet-only connection, but if a PPPoE setting was given + * then PPPoE should be our connection type. + */ + nm_device_complete_generic (connection, + s_pppoe ? NM_SETTING_PPPOE_SETTING_NAME : NM_SETTING_CONNECTION_SETTING_NAME, + existing_connections, + s_pppoe ? _("PPPoE connection %d") : _("Wired connection %d"), + NULL); + + s_wired = (NMSettingWired *) nm_connection_get_setting (connection, NM_TYPE_SETTING_WIRED); + if (!s_wired) { + s_wired = (NMSettingWired *) nm_setting_wired_new (); + nm_connection_add_setting (connection, NM_SETTING (s_wired)); + } + + setting_mac = nm_setting_wired_get_mac_address (s_wired); + if (setting_mac) { + /* Make sure the setting MAC (if any) matches the device's permanent MAC */ + if (memcmp (setting_mac->data, priv->perm_hw_addr, ETH_ALEN)) { + g_set_error_literal (error, + NM_SETTING_WIRED_ERROR, + NM_SETTING_WIRED_ERROR_INVALID_PROPERTY, + NM_SETTING_WIRED_MAC_ADDRESS); + return FALSE; + } + } else { + GByteArray *mac; + const guint8 null_mac[ETH_ALEN] = { 0, 0, 0, 0, 0, 0 }; + + /* Lock the connection to this device by default */ + if (memcmp (priv->perm_hw_addr, null_mac, ETH_ALEN)) { + mac = g_byte_array_sized_new (ETH_ALEN); + g_byte_array_append (mac, priv->perm_hw_addr, ETH_ALEN); + g_object_set (G_OBJECT (s_wired), NM_SETTING_WIRED_MAC_ADDRESS, mac, NULL); + g_byte_array_free (mac, TRUE); + } + } + + return TRUE; +} + static gboolean spec_match_list (NMDevice *device, const GSList *specs) { @@ -1933,6 +1994,7 @@ nm_device_ethernet_class_init (NMDeviceEthernetClass *klass) parent_class->get_best_auto_connection = real_get_best_auto_connection; parent_class->is_available = real_is_available; parent_class->check_connection_compatible = real_check_connection_compatible; + parent_class->complete_connection = real_complete_connection; parent_class->act_stage1_prepare = real_act_stage1_prepare; parent_class->act_stage2_config = real_act_stage2_config; diff --git a/src/nm-device-modem.c b/src/nm-device-modem.c index 440db82f2a..362ff8575c 100644 --- a/src/nm-device-modem.c +++ b/src/nm-device-modem.c @@ -220,6 +220,18 @@ real_check_connection_compatible (NMDevice *device, return nm_modem_check_connection_compatible (priv->modem, connection, error); } +static gboolean +real_complete_connection (NMDevice *device, + NMConnection *connection, + const char *specific_object, + const GSList *existing_connections, + GError **error) +{ + NMDeviceModemPrivate *priv = NM_DEVICE_MODEM_GET_PRIVATE (device); + + return nm_modem_complete_connection (priv->modem, connection, existing_connections, error); +} + static gboolean real_hw_is_up (NMDevice *device) { @@ -401,6 +413,7 @@ nm_device_modem_class_init (NMDeviceModemClass *mclass) device_class->get_generic_capabilities = real_get_generic_capabilities; device_class->get_best_auto_connection = real_get_best_auto_connection; device_class->check_connection_compatible = real_check_connection_compatible; + device_class->complete_connection = real_complete_connection; device_class->hw_is_up = real_hw_is_up; device_class->hw_bring_up = real_hw_bring_up; device_class->deactivate_quickly = real_deactivate_quickly; diff --git a/src/nm-device-modem.h b/src/nm-device-modem.h index c6ef4d2184..806676e5be 100644 --- a/src/nm-device-modem.h +++ b/src/nm-device-modem.h @@ -15,7 +15,7 @@ * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * - * Copyright (C) 2010 Red Hat, Inc. + * Copyright (C) 2011 Red Hat, Inc. */ #ifndef NM_DEVICE_MODEM_H diff --git a/src/nm-device-olpc-mesh.c b/src/nm-device-olpc-mesh.c index 36b968ed8d..e0a05df95f 100644 --- a/src/nm-device-olpc-mesh.c +++ b/src/nm-device-olpc-mesh.c @@ -19,7 +19,7 @@ * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * - * (C) Copyright 2005 - 2010 Red Hat, Inc. + * (C) Copyright 2005 - 2011 Red Hat, Inc. * (C) Copyright 2008 Collabora Ltd. * (C) Copyright 2009 One Laptop per Child */ @@ -381,6 +381,50 @@ real_check_connection_compatible (NMDevice *device, return TRUE; } +#define DEFAULT_SSID "olpc-mesh" + +static gboolean +real_complete_connection (NMDevice *device, + NMConnection *connection, + const char *specific_object, + const GSList *existing_connections, + GError **error) +{ + NMSettingOlpcMesh *s_mesh; + GByteArray *tmp; + + s_mesh = (NMSettingOlpcMesh *) nm_connection_get_setting (connection, NM_TYPE_SETTING_OLPC_MESH); + if (!s_mesh) { + s_mesh = (NMSettingOlpcMesh *) nm_setting_olpc_mesh_new (); + nm_connection_add_setting (connection, NM_SETTING (s_mesh)); + } + + if (!nm_setting_olpc_mesh_get_ssid (s_mesh)) { + tmp = g_byte_array_sized_new (strlen (DEFAULT_SSID)); + g_byte_array_append (tmp, (const guint8 *) DEFAULT_SSID, strlen (DEFAULT_SSID)); + g_object_set (G_OBJECT (s_mesh), NM_SETTING_OLPC_MESH_SSID, tmp, NULL); + g_byte_array_free (tmp, TRUE); + } + + if (!nm_setting_olpc_mesh_get_dhcp_anycast_address (s_mesh)) { + const guint8 anycast[ETH_ALEN] = { 0xC0, 0x27, 0xC0, 0x27, 0xC0, 0x27 }; + + tmp = g_byte_array_sized_new (ETH_ALEN); + g_byte_array_append (tmp, anycast, sizeof (anycast)); + g_object_set (G_OBJECT (s_mesh), NM_SETTING_OLPC_MESH_DHCP_ANYCAST_ADDRESS, tmp, NULL); + g_byte_array_free (tmp, TRUE); + + } + + nm_device_complete_generic (connection, + NM_SETTING_OLPC_MESH_SETTING_NAME, + existing_connections, + _("Mesh %d"), + NULL); + + return TRUE; +} + /* * nm_device_olpc_mesh_get_address * @@ -722,6 +766,7 @@ nm_device_olpc_mesh_class_init (NMDeviceOlpcMeshClass *klass) parent_class->take_down = real_take_down; parent_class->update_hw_address = real_update_hw_address; parent_class->check_connection_compatible = real_check_connection_compatible; + parent_class->complete_connection = real_complete_connection; parent_class->act_stage1_prepare = real_act_stage1_prepare; parent_class->act_stage2_config = real_act_stage2_config; diff --git a/src/nm-device-private.h b/src/nm-device-private.h index f4f968a949..82f429fc61 100644 --- a/src/nm-device-private.h +++ b/src/nm-device-private.h @@ -16,7 +16,7 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * * Copyright (C) 2007 - 2008 Novell, Inc. - * Copyright (C) 2007 - 2008 Red Hat, Inc. + * Copyright (C) 2007 - 2011 Red Hat, Inc. */ #ifndef NM_DEVICE_PRIVATE_H @@ -46,4 +46,10 @@ gboolean nm_device_get_firmware_missing (NMDevice *self); void nm_device_set_firmware_missing (NMDevice *self, gboolean missing); +void nm_device_complete_generic (NMConnection *connection, + const char *ctype, + const GSList *existing, + const char *format, + const char *preferred); + #endif /* NM_DEVICE_PRIVATE_H */ diff --git a/src/nm-device-wifi.c b/src/nm-device-wifi.c index 784a14976d..a86890b917 100644 --- a/src/nm-device-wifi.c +++ b/src/nm-device-wifi.c @@ -15,7 +15,7 @@ * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * - * Copyright (C) 2005 - 2010 Red Hat, Inc. + * Copyright (C) 2005 - 2011 Red Hat, Inc. * Copyright (C) 2006 - 2008 Novell, Inc. */ @@ -104,15 +104,6 @@ enum { static guint signals[LAST_SIGNAL] = { 0 }; -typedef enum { - NM_WIFI_ERROR_CONNECTION_NOT_WIRELESS = 0, - NM_WIFI_ERROR_CONNECTION_INVALID, - NM_WIFI_ERROR_CONNECTION_INCOMPATIBLE, -} NMWifiError; - -#define NM_WIFI_ERROR (nm_wifi_error_quark ()) -#define NM_TYPE_WIFI_ERROR (nm_wifi_error_get_type ()) - #define SUP_SIG_ID_LEN 5 typedef struct Supplicant { @@ -208,6 +199,18 @@ static guint32 nm_device_wifi_get_bitrate (NMDeviceWifi *self); static void cull_scan_list (NMDeviceWifi *self); +/*****************************************************************/ + +typedef enum { + NM_WIFI_ERROR_CONNECTION_NOT_WIRELESS = 0, + NM_WIFI_ERROR_CONNECTION_INVALID, + NM_WIFI_ERROR_CONNECTION_INCOMPATIBLE, + NM_WIFI_ERROR_ACCESS_POINT_NOT_FOUND, +} NMWifiError; + +#define NM_WIFI_ERROR (nm_wifi_error_quark ()) +#define NM_TYPE_WIFI_ERROR (nm_wifi_error_get_type ()) + static GQuark nm_wifi_error_quark (void) { @@ -233,6 +236,8 @@ nm_wifi_error_get_type (void) ENUM_ENTRY (NM_WIFI_ERROR_CONNECTION_INVALID, "ConnectionInvalid"), /* Connection does not apply to this device. */ ENUM_ENTRY (NM_WIFI_ERROR_CONNECTION_INCOMPATIBLE, "ConnectionIncompatible"), + /* Given access point was not in this device's scan list. */ + ENUM_ENTRY (NM_WIFI_ERROR_ACCESS_POINT_NOT_FOUND, "AccessPointNotFound"), { 0, 0, 0 } }; etype = g_enum_register_static ("NMWifiError", values); @@ -240,6 +245,8 @@ nm_wifi_error_get_type (void) return etype; } +/*****************************************************************/ + /* IPW rfkill handling (until 2.6.33) */ RfKillState nm_device_wifi_get_ipw_rfkill_state (NMDeviceWifi *self) @@ -298,6 +305,8 @@ ipw_rfkill_state_work (gpointer user_data) return TRUE; } +/*****************************************************************/ + /* * nm_device_wifi_update_signal_strength * @@ -732,6 +741,20 @@ supplicant_interface_release (NMDeviceWifi *self) } } + +static NMAccessPoint * +get_ap_by_path (NMDeviceWifi *self, const char *path) +{ + NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self); + GSList *iter; + + for (iter = priv->ap_list; iter; iter = g_slist_next (iter)) { + if (strcmp (path, nm_ap_get_dbus_path (NM_AP (iter->data))) == 0) + return NM_AP (iter->data); + } + return NULL; +} + static NMAccessPoint * get_active_ap (NMDeviceWifi *self, NMAccessPoint *ignore_ap, @@ -1222,6 +1245,174 @@ real_check_connection_compatible (NMDevice *device, return TRUE; } +/* + * List of manufacturer default SSIDs that are often unchanged by users. + * + * NOTE: this list should *not* contain networks that you would like to + * automatically roam to like "Starbucks" or "AT&T" or "T-Mobile HotSpot". + */ +static const char * +manf_defaults[] = { + "linksys", + "linksys-a", + "linksys-g", + "default", + "belkin54g", + "NETGEAR", + "o2DSL", + "WLAN", + "ALICE-WLAN", +}; + +#define ARRAY_SIZE(a) (sizeof (a) / sizeof (a[0])) + +static gboolean +is_manf_default_ssid (const GByteArray *ssid) +{ + int i; + + for (i = 0; i < ARRAY_SIZE (manf_defaults); i++) { + if (ssid->len == strlen (manf_defaults[i])) { + if (memcmp (manf_defaults[i], ssid->data, ssid->len) == 0) + return TRUE; + } + } + return FALSE; +} + +static gboolean +real_complete_connection (NMDevice *device, + NMConnection *connection, + const char *specific_object, + const GSList *existing_connections, + GError **error) +{ + NMDeviceWifi *self = NM_DEVICE_WIFI (device); + NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self); + NMSettingWireless *s_wifi; + NMSettingWirelessSecurity *s_wsec; + NMSetting8021x *s_8021x; + const GByteArray *setting_mac; + char *format, *str_ssid = NULL; + NMAccessPoint *ap = NULL; + const GByteArray *ssid = NULL; + GSList *iter; + char buf[33]; + int buf_len; + + s_wifi = (NMSettingWireless *) nm_connection_get_setting (connection, NM_TYPE_SETTING_WIRELESS); + s_wsec = (NMSettingWirelessSecurity *) nm_connection_get_setting (connection, NM_TYPE_SETTING_WIRELESS_SECURITY); + s_8021x = (NMSetting8021x *) nm_connection_get_setting (connection, NM_TYPE_SETTING_802_1X); + + if (!specific_object) { + /* If not given a specific object, we need at minimum an SSID */ + if (!s_wifi) { + g_set_error_literal (error, + NM_WIFI_ERROR, + NM_WIFI_ERROR_CONNECTION_INVALID, + "A 'wireless' setting is required if no AP path was given."); + return FALSE; + } + + ssid = nm_setting_wireless_get_ssid (s_wifi); + if (!ssid || !ssid->len) { + g_set_error_literal (error, + NM_WIFI_ERROR, + NM_WIFI_ERROR_CONNECTION_INVALID, + "A 'wireless' setting with a valid SSID is required if no AP path was given."); + return FALSE; + } + + /* Find a compatible AP in the scan list */ + for (iter = priv->ap_list; iter; iter = g_slist_next (iter)) { + if (nm_ap_check_compatible (NM_AP (iter->data), connection)) { + ap = NM_AP (iter->data); + break; + } + } + + /* If we still don't have an AP, then the WiFI settings needs to be + * fully specified by the client. Might not be able to find an AP + * if the network isn't broadcasting the SSID for example. + */ + if (!ap) { + GSList *settings = NULL; + gboolean valid; + + settings = g_slist_prepend (settings, s_wifi); + settings = g_slist_prepend (settings, s_wsec); + settings = g_slist_prepend (settings, s_8021x); + valid = nm_setting_verify (NM_SETTING (s_wifi), settings, error); + g_slist_free (settings); + if (!valid) + return FALSE; + } + } else { + ap = get_ap_by_path (self, specific_object); + if (!ap) { + g_set_error (error, + NM_WIFI_ERROR, + NM_WIFI_ERROR_ACCESS_POINT_NOT_FOUND, + "The access point %s was not in the scan list.", + specific_object); + return FALSE; + } + } + + if (ap) { + ssid = nm_ap_get_ssid (ap); + + /* If the SSID is a well-known SSID, lock the connection to the AP's + * specific BSSID so NM doesn't autoconnect to some random wifi net. + */ + if (!nm_ap_complete_connection (ap, + connection, + is_manf_default_ssid (ssid), + error)) + return FALSE; + } + + g_assert (ssid); + memset (buf, 0, sizeof (buf)); + buf_len = MIN (ssid->len, sizeof (buf) - 1); + memcpy (buf, ssid->data, buf_len); + str_ssid = nm_utils_ssid_to_utf8 (buf, buf_len); + format = g_strdup_printf ("%s %%d", str_ssid); + + nm_device_complete_generic (connection, + NM_SETTING_WIRELESS_SETTING_NAME, + existing_connections, + format, + str_ssid); + g_free (str_ssid); + g_free (format); + + setting_mac = nm_setting_wireless_get_mac_address (s_wifi); + if (setting_mac) { + /* Make sure the setting MAC (if any) matches the device's permanent MAC */ + if (memcmp (setting_mac->data, priv->perm_hw_addr, ETH_ALEN)) { + g_set_error (error, + NM_SETTING_WIRELESS_ERROR, + NM_SETTING_WIRELESS_ERROR_INVALID_PROPERTY, + NM_SETTING_WIRELESS_MAC_ADDRESS); + return FALSE; + } + } else { + GByteArray *mac; + const guint8 null_mac[ETH_ALEN] = { 0, 0, 0, 0, 0, 0 }; + + /* Lock the connection to this device by default */ + if (memcmp (priv->perm_hw_addr, null_mac, ETH_ALEN)) { + mac = g_byte_array_sized_new (ETH_ALEN); + g_byte_array_append (mac, priv->perm_hw_addr, ETH_ALEN); + g_object_set (G_OBJECT (s_wifi), NM_SETTING_WIRELESS_MAC_ADDRESS, mac, NULL); + g_byte_array_free (mac, TRUE); + } + } + + return TRUE; +} + static gboolean real_is_available (NMDevice *dev) { @@ -3449,10 +3640,8 @@ device_state_changed (NMDevice *device, NMAccessPoint * nm_device_wifi_get_activation_ap (NMDeviceWifi *self) { - NMDeviceWifiPrivate *priv; NMActRequest *req; const char *ap_path; - GSList * elt; g_return_val_if_fail (NM_IS_DEVICE_WIFI (self), NULL); @@ -3461,18 +3650,8 @@ nm_device_wifi_get_activation_ap (NMDeviceWifi *self) return NULL; ap_path = nm_act_request_get_specific_object (req); - if (!ap_path) - return NULL; - /* Find the AP by it's object path */ - priv = NM_DEVICE_WIFI_GET_PRIVATE (self); - for (elt = priv->ap_list; elt; elt = g_slist_next (elt)) { - NMAccessPoint *ap = NM_AP (elt->data); - - if (!strcmp (ap_path, nm_ap_get_dbus_path (ap))) - return ap; - } - return NULL; + return ap_path ? get_ap_by_path (self, ap_path) : NULL; } static void @@ -3700,6 +3879,7 @@ nm_device_wifi_class_init (NMDeviceWifiClass *klass) parent_class->get_best_auto_connection = real_get_best_auto_connection; parent_class->is_available = real_is_available; parent_class->check_connection_compatible = real_check_connection_compatible; + parent_class->complete_connection = real_complete_connection; parent_class->act_stage1_prepare = real_act_stage1_prepare; parent_class->act_stage2_config = real_act_stage2_config; diff --git a/src/nm-device.c b/src/nm-device.c index 2765a5de36..7c531c7bdb 100644 --- a/src/nm-device.c +++ b/src/nm-device.c @@ -15,7 +15,7 @@ * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * - * Copyright (C) 2005 - 2010 Red Hat, Inc. + * Copyright (C) 2005 - 2011 Red Hat, Inc. * Copyright (C) 2006 - 2008 Novell, Inc. */ @@ -572,6 +572,153 @@ nm_device_get_best_auto_connection (NMDevice *dev, return NM_DEVICE_GET_CLASS (dev)->get_best_auto_connection (dev, connections, specific_object); } +gboolean +nm_device_complete_connection (NMDevice *self, + NMConnection *connection, + const char *specific_object, + const GSList *existing_connections, + GError **error) +{ + gboolean success = FALSE; + + g_return_val_if_fail (self != NULL, FALSE); + g_return_val_if_fail (connection != NULL, FALSE); + + if (!NM_DEVICE_GET_CLASS (self)->complete_connection) { + g_set_error (error, + NM_DEVICE_INTERFACE_ERROR, + NM_DEVICE_INTERFACE_ERROR_CONNECTION_INVALID, + "Device class %s had no complete_connection method", + G_OBJECT_TYPE_NAME (self)); + return FALSE; + } + + success = NM_DEVICE_GET_CLASS (self)->complete_connection (self, + connection, + specific_object, + existing_connections, + error); + if (success) + success = nm_connection_verify (connection, error); + + return success; +} + +static char * +nm_device_new_connection_name (const GSList *existing, + const char *format, + const char *preferred) +{ + GSList *names = NULL; + const GSList *iter; + char *cname = NULL; + int i = 0; + gboolean preferred_found = FALSE; + + for (iter = existing; iter; iter = g_slist_next (iter)) { + NMConnection *candidate = NM_CONNECTION (iter->data); + NMSettingConnection *s_con; + const char *id; + + s_con = NM_SETTING_CONNECTION (nm_connection_get_setting (candidate, NM_TYPE_SETTING_CONNECTION)); + id = nm_setting_connection_get_id (s_con); + g_assert (id); + names = g_slist_append (names, (gpointer) id); + + if (preferred && !preferred_found && (strcmp (preferred, id) == 0)) + preferred_found = TRUE; + } + + /* Return the preferred name if it was unique */ + if (preferred && !preferred_found) { + g_slist_free (names); + return g_strdup (preferred); + } + + /* Otherwise find the next available unique connection name using the given + * connection name template. + */ + while (!cname && (i++ < 10000)) { + char *temp; + gboolean found = FALSE; + + temp = g_strdup_printf (format, i); + for (iter = names; iter; iter = g_slist_next (iter)) { + if (!strcmp (iter->data, temp)) { + found = TRUE; + break; + } + } + if (!found) + cname = temp; + else + g_free (temp); + } + + g_slist_free (names); + return cname; +} + +void +nm_device_complete_generic (NMConnection *connection, + const char *ctype, + const GSList *existing, + const char *format, + const char *preferred) +{ + NMSettingConnection *s_con; + NMSettingIP4Config *s_ip4; + NMSettingIP6Config *s_ip6; + const char *method; + char *id, *uuid; + + s_con = (NMSettingConnection *) nm_connection_get_setting (connection, NM_TYPE_SETTING_CONNECTION); + if (!s_con) { + s_con = (NMSettingConnection *) nm_setting_connection_new (); + nm_connection_add_setting (connection, NM_SETTING (s_con)); + } + g_object_set (G_OBJECT (s_con), NM_SETTING_CONNECTION_TYPE, ctype, NULL); + + if (!nm_setting_connection_get_uuid (s_con)) { + uuid = nm_utils_uuid_generate (); + g_object_set (G_OBJECT (s_con), NM_SETTING_CONNECTION_UUID, uuid, NULL); + g_free (uuid); + } + + /* Add a connection ID if absent */ + if (!nm_setting_connection_get_id (s_con)) { + id = nm_device_new_connection_name (existing, format, preferred); + g_object_set (G_OBJECT (s_con), NM_SETTING_CONNECTION_ID, id, NULL); + g_free (id); + } + + /* Add an 'auto' IPv4 connection if present */ + s_ip4 = (NMSettingIP4Config *) nm_connection_get_setting (connection, NM_TYPE_SETTING_IP4_CONFIG); + if (!s_ip4) { + s_ip4 = (NMSettingIP4Config *) nm_setting_ip4_config_new (); + nm_connection_add_setting (connection, NM_SETTING (s_ip4)); + } + method = nm_setting_ip4_config_get_method (s_ip4); + if (!method) { + g_object_set (G_OBJECT (s_ip4), + NM_SETTING_IP4_CONFIG_METHOD, NM_SETTING_IP4_CONFIG_METHOD_AUTO, + NULL); + } + + s_ip6 = (NMSettingIP6Config *) nm_connection_get_setting (connection, NM_TYPE_SETTING_IP6_CONFIG); + if (!s_ip6) { + s_ip6 = (NMSettingIP6Config *) nm_setting_ip6_config_new (); + nm_connection_add_setting (connection, NM_SETTING (s_ip6)); + } + method = nm_setting_ip6_config_get_method (s_ip6); + if (!method) { + g_object_set (G_OBJECT (s_ip6), + NM_SETTING_IP6_CONFIG_METHOD, NM_SETTING_IP6_CONFIG_METHOD_AUTO, + NM_SETTING_IP6_CONFIG_MAY_FAIL, TRUE, + NULL); + } +} + static void dnsmasq_state_changed_cb (NMDnsMasqManager *manager, guint32 status, gpointer user_data) { diff --git a/src/nm-device.h b/src/nm-device.h index e7d347cbca..b96da33024 100644 --- a/src/nm-device.h +++ b/src/nm-device.h @@ -15,7 +15,7 @@ * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * - * Copyright (C) 2005 - 2010 Red Hat, Inc. + * Copyright (C) 2005 - 2011 Red Hat, Inc. * Copyright (C) 2006 - 2008 Novell, Inc. */ @@ -88,6 +88,12 @@ typedef struct { NMConnection *connection, GError **error); + gboolean (* complete_connection) (NMDevice *self, + NMConnection *connection, + const char *specific_object, + const GSList *existing_connections, + GError **error); + NMActStageReturn (* act_stage1_prepare) (NMDevice *self, NMDeviceStateReason *reason); NMActStageReturn (* act_stage2_config) (NMDevice *self, @@ -157,6 +163,12 @@ NMConnection * nm_device_get_best_auto_connection (NMDevice *dev, GSList *connections, char **specific_object); +gboolean nm_device_complete_connection (NMDevice *device, + NMConnection *connection, + const char *specific_object, + const GSList *existing_connection, + GError **error); + void nm_device_activate_schedule_stage1_device_prepare (NMDevice *device); void nm_device_activate_schedule_stage2_device_config (NMDevice *device); void nm_device_activate_schedule_stage4_ip4_config_get (NMDevice *device); diff --git a/src/nm-manager.c b/src/nm-manager.c index 23551d381d..1067575532 100644 --- a/src/nm-manager.c +++ b/src/nm-manager.c @@ -16,7 +16,7 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * * Copyright (C) 2007 - 2009 Novell, Inc. - * Copyright (C) 2007 - 2010 Red Hat, Inc. + * Copyright (C) 2007 - 2011 Red Hat, Inc. */ #include @@ -69,6 +69,12 @@ static void impl_manager_activate_connection (NMManager *manager, const char *specific_object_path, DBusGMethodInvocation *context); +static void impl_manager_add_and_activate_connection (NMManager *manager, + GHashTable *settings, + const char *device_path, + const char *specific_object_path, + DBusGMethodInvocation *context); + static void impl_manager_deactivate_connection (NMManager *manager, const char *connection_path, DBusGMethodInvocation *context); @@ -170,9 +176,8 @@ struct PendingActivation { PendingActivationFunc callback; NMAuthChain *chain; - gboolean authorized; - char *connection_path; + NMConnection *connection; char *specific_object_path; char *device_path; }; @@ -270,8 +275,10 @@ enum { LAST_PROP }; -typedef enum -{ + +/************************************************************************/ + +typedef enum { NM_MANAGER_ERROR_UNKNOWN_CONNECTION = 0, NM_MANAGER_ERROR_UNKNOWN_DEVICE, NM_MANAGER_ERROR_UNMANAGED_DEVICE, @@ -327,6 +334,32 @@ nm_manager_error_get_type (void) return etype; } +/************************************************************************/ + +static NMDevice * +nm_manager_get_device_by_udi (NMManager *manager, const char *udi) +{ + GSList *iter; + + for (iter = NM_MANAGER_GET_PRIVATE (manager)->devices; iter; iter = iter->next) { + if (!strcmp (nm_device_get_udi (NM_DEVICE (iter->data)), udi)) + return NM_DEVICE (iter->data); + } + return NULL; +} + +static NMDevice * +nm_manager_get_device_by_path (NMManager *manager, const char *path) +{ + GSList *iter; + + for (iter = NM_MANAGER_GET_PRIVATE (manager)->devices; iter; iter = iter->next) { + if (!strcmp (nm_device_get_path (NM_DEVICE (iter->data)), path)) + return NM_DEVICE (iter->data); + } + return NULL; +} + static gboolean manager_sleeping (NMManager *self) { @@ -618,10 +651,17 @@ pending_activation_new (NMManager *manager, DBusGMethodInvocation *context, const char *device_path, const char *connection_path, + GHashTable *settings, const char *specific_object_path, - PendingActivationFunc callback) + PendingActivationFunc callback, + GError **error) { + NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (manager); PendingActivation *pending; + NMDevice *device; + NMConnection *connection = NULL; + GSList *all_connections = NULL; + gboolean success; g_return_val_if_fail (manager != NULL, NULL); g_return_val_if_fail (authority != NULL, NULL); @@ -629,6 +669,35 @@ pending_activation_new (NMManager *manager, g_return_val_if_fail (device_path != NULL, NULL); g_return_val_if_fail (connection_path != NULL, NULL); + /* Create the partial connection from the given settings */ + if (settings) { + device = nm_manager_get_device_by_path (manager, device_path); + if (!device) { + g_set_error_literal (error, + NM_MANAGER_ERROR, + NM_MANAGER_ERROR_UNKNOWN_DEVICE, + "Device not found"); + return NULL; + } + + connection = nm_connection_new (); + nm_connection_replace_settings (connection, settings, NULL); + + /* Let each device subclass complete the connection */ + all_connections = nm_settings_get_connections (priv->settings); + success = nm_device_complete_connection (device, + connection, + specific_object_path, + all_connections, + error); + g_slist_free (all_connections); + + if (success == FALSE) { + g_object_unref (connection); + return NULL; + } + } + pending = g_slice_new0 (PendingActivation); pending->manager = manager; pending->authority = authority; @@ -637,6 +706,7 @@ pending_activation_new (NMManager *manager, pending->device_path = g_strdup (device_path); pending->connection_path = g_strdup (connection_path); + pending->connection = connection; /* "/" is special-cased to NULL to get through D-Bus */ if (specific_object_path && strcmp (specific_object_path, "/")) @@ -731,14 +801,22 @@ pending_activation_destroy (PendingActivation *pending, { g_return_if_fail (pending != NULL); + if (error) + dbus_g_method_return_error (pending->context, error); + else if (ac_path) { + if (pending->connection) { + dbus_g_method_return (pending->context, + pending->connection_path, + ac_path); + } else + dbus_g_method_return (pending->context, ac_path); + } + g_free (pending->connection_path); g_free (pending->specific_object_path); g_free (pending->device_path); - - if (error) - dbus_g_method_return_error (pending->context, error); - else if (ac_path) - dbus_g_method_return (pending->context, ac_path); + if (pending->connection) + g_object_unref (pending->connection); if (pending->chain) nm_auth_chain_unref (pending->chain); @@ -840,30 +918,6 @@ system_hostname_changed_cb (NMSettings *settings, /* General NMManager stuff */ /*******************************************************************/ -static NMDevice * -nm_manager_get_device_by_udi (NMManager *manager, const char *udi) -{ - GSList *iter; - - for (iter = NM_MANAGER_GET_PRIVATE (manager)->devices; iter; iter = iter->next) { - if (!strcmp (nm_device_get_udi (NM_DEVICE (iter->data)), udi)) - return NM_DEVICE (iter->data); - } - return NULL; -} - -static NMDevice * -nm_manager_get_device_by_path (NMManager *manager, const char *path) -{ - GSList *iter; - - for (iter = NM_MANAGER_GET_PRIVATE (manager)->devices; iter; iter = iter->next) { - if (!strcmp (nm_device_get_path (NM_DEVICE (iter->data)), path)) - return NM_DEVICE (iter->data); - } - return NULL; -} - /* Store value into key-file; supported types: boolean, int, string */ static gboolean write_value_to_state_file (const char *filename, @@ -1870,7 +1924,7 @@ nm_manager_activate_connection (NMManager *manager, * it. */ static void -check_pending_ready (NMManager *self, PendingActivation *pending) +pending_activate (NMManager *self, PendingActivation *pending) { NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self); NMSysconfigConnection *connection; @@ -1907,14 +1961,10 @@ out: static void activation_auth_done (PendingActivation *pending, GError *error) { - if (error) { + if (error) pending_activation_destroy (pending, error, NULL); - return; - } else { - pending->authorized = TRUE; - - check_pending_ready (pending->manager, pending); - } + else + pending_activate (pending->manager, pending); } static void @@ -1926,6 +1976,7 @@ impl_manager_activate_connection (NMManager *self, { NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self); PendingActivation *pending; + GError *error = NULL; /* Need to check the caller's permissions and stuff before we can * activate the connection. @@ -1935,10 +1986,86 @@ impl_manager_activate_connection (NMManager *self, context, device_path, connection_path, + NULL, specific_object_path, - activation_auth_done); + activation_auth_done, + &error); + if (pending) + pending_activation_check_authorized (pending, priv->dbus_mgr); + else { + g_assert (error); + dbus_g_method_return_error (context, error); + g_error_free (error); + } +} - pending_activation_check_authorized (pending, priv->dbus_mgr); +static void +activation_add_done (NMSettings *self, + NMSysconfigConnection *connection, + GError *error, + DBusGMethodInvocation *context, + gpointer user_data) +{ + PendingActivation *pending = user_data; + + if (error) + pending_activation_destroy (pending, error, NULL); + else { + /* Save the new connection's D-Bus path */ + pending->connection_path = g_strdup (nm_connection_get_path (NM_CONNECTION (connection))); + + /* And activate it */ + pending_activate (pending->manager, pending); + } +} + +static void +add_and_activate_auth_done (PendingActivation *pending, GError *error) +{ + if (error) + pending_activation_destroy (pending, error, NULL); + else { + NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (pending->manager); + + /* Basic sender auth checks performed; try to add the connection */ + nm_settings_add_connection (priv->settings, + pending->connection, + pending->context, + activation_add_done, + pending); + } +} + +static void +impl_manager_add_and_activate_connection (NMManager *self, + GHashTable *settings, + const char *device_path, + const char *specific_object_path, + DBusGMethodInvocation *context) +{ + NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self); + PendingActivation *pending; + GError *error = NULL; + + /* Need to check the caller's permissions and stuff before we can + * activate the connection. + */ + pending = pending_activation_new (self, + priv->authority, + context, + device_path, + NULL, + settings, + specific_object_path, + add_and_activate_auth_done, + &error); + if (pending) + pending_activation_check_authorized (pending, priv->dbus_mgr); + else { + g_assert (error); + dbus_g_method_return_error (context, error); + g_error_free (error); + } } gboolean diff --git a/src/nm-wifi-ap-utils.c b/src/nm-wifi-ap-utils.c new file mode 100644 index 0000000000..a5bf190b84 --- /dev/null +++ b/src/nm-wifi-ap-utils.c @@ -0,0 +1,699 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * 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 2011 Red Hat, Inc. + */ + +#include + +#include "nm-wifi-ap-utils.h" + +static gboolean +verify_no_wep (NMSettingWirelessSecurity *s_wsec, const char *tag, GError **error) +{ + if ( nm_setting_wireless_security_get_wep_key (s_wsec, 0) + || nm_setting_wireless_security_get_wep_key (s_wsec, 1) + || nm_setting_wireless_security_get_wep_key (s_wsec, 2) + || nm_setting_wireless_security_get_wep_key (s_wsec, 3) + || nm_setting_wireless_security_get_wep_tx_keyidx (s_wsec) + || nm_setting_wireless_security_get_wep_key_type (s_wsec)) { + /* Dynamic WEP cannot have any WEP keys set */ + g_set_error (error, + NM_SETTING_WIRELESS_SECURITY_ERROR, + NM_SETTING_WIRELESS_SECURITY_ERROR_INVALID_PROPERTY, + "%s is incompatible with static WEP keys", tag); + return FALSE; + } + + return TRUE; +} + +static gboolean +verify_leap (NMSettingWirelessSecurity *s_wsec, + NMSetting8021x *s_8021x, + gboolean adhoc, + GError **error) +{ + const char *key_mgmt, *auth_alg, *leap_username; + + key_mgmt = nm_setting_wireless_security_get_key_mgmt (s_wsec); + auth_alg = nm_setting_wireless_security_get_auth_alg (s_wsec); + leap_username = nm_setting_wireless_security_get_leap_username (s_wsec); + + /* One (or both) of two things indicates we want LEAP: + * 1) auth_alg == 'leap' + * 2) valid leap_username + * + * LEAP always requires a LEAP username. + */ + + if (auth_alg) { + if (!strcmp (auth_alg, "leap")) { + /* LEAP authentication requires at least a LEAP username */ + if (!leap_username) { + g_set_error_literal (error, + NM_SETTING_WIRELESS_SECURITY_ERROR, + NM_SETTING_WIRELESS_SECURITY_ERROR_LEAP_REQUIRES_USERNAME, + "LEAP requires a LEAP username"); + return FALSE; + } + } else if (leap_username) { + /* Leap username requires 'leap' auth */ + g_set_error_literal (error, + NM_SETTING_WIRELESS_SECURITY_ERROR, + NM_SETTING_WIRELESS_SECURITY_ERROR_INVALID_PROPERTY, + "LEAP requires 'leap' authentication"); + return FALSE; + } + } + + if (leap_username) { + if (key_mgmt && strcmp (key_mgmt, "ieee8021x")) { + /* LEAP requires ieee8021x key management */ + g_set_error_literal (error, + NM_SETTING_WIRELESS_SECURITY_ERROR, + NM_SETTING_WIRELESS_SECURITY_ERROR_LEAP_REQUIRES_802_1X, + "LEAP requires IEEE 802.1x key management"); + return FALSE; + } + } + + /* At this point if auth_alg is set it must be 'leap', and if key_mgmt + * is set it must be 'ieee8021x'. + */ + if (leap_username) { + if (auth_alg) + g_assert (strcmp (auth_alg, "leap") == 0); + if (key_mgmt) + g_assert (strcmp (key_mgmt, "ieee8021x") == 0); + + if (adhoc) { + g_set_error_literal (error, + NM_SETTING_WIRELESS_SECURITY_ERROR, + NM_SETTING_WIRELESS_SECURITY_ERROR_INVALID_PROPERTY, + "LEAP incompatible with Ad-Hoc mode"); + return FALSE; + } + + if (!verify_no_wep (s_wsec, "LEAP", error)) + return FALSE; + + if (s_8021x) { + g_set_error_literal (error, + NM_SETTING_WIRELESS_SECURITY_ERROR, + NM_SETTING_WIRELESS_SECURITY_ERROR_LEAP_REQUIRES_USERNAME, + "LEAP incompatible with 802.1x setting"); + return FALSE; + } + } + + return TRUE; +} + +static gboolean +verify_no_wpa (NMSettingWirelessSecurity *s_wsec, + const char *tag, + GError **error) +{ + const char *key_mgmt; + int n, i; + + key_mgmt = nm_setting_wireless_security_get_key_mgmt (s_wsec); + if (key_mgmt && !strncmp (key_mgmt, "wpa", 3)) { + g_set_error (error, + NM_SETTING_WIRELESS_SECURITY_ERROR, + NM_SETTING_WIRELESS_SECURITY_ERROR_INVALID_PROPERTY, + "%s incompatible with any WPA key management", tag); + return FALSE; + } + + if (nm_setting_wireless_security_get_num_protos (s_wsec)) { + g_set_error (error, + NM_SETTING_WIRELESS_SECURITY_ERROR, + NM_SETTING_WIRELESS_SECURITY_ERROR_INVALID_PROPERTY, + "%s incompatible with any 'proto' setting", tag); + return FALSE; + } + + n = nm_setting_wireless_security_get_num_pairwise (s_wsec); + for (i = 0; i < n; i++) { + const char *pw; + + pw = nm_setting_wireless_security_get_pairwise (s_wsec, i); + if (strcmp (pw, "wep40") && strcmp (pw, "wep104")) { + g_set_error (error, + NM_SETTING_WIRELESS_SECURITY_ERROR, + NM_SETTING_WIRELESS_SECURITY_ERROR_INVALID_PROPERTY, + "%s is incompatible with WPA pairwise ciphers", tag); + return FALSE; + } + } + + n = nm_setting_wireless_security_get_num_groups (s_wsec); + for (i = 0; i < n; i++) { + const char *gr; + + gr = nm_setting_wireless_security_get_group (s_wsec, i); + if (strcmp (gr, "wep40") && strcmp (gr, "wep104")) { + g_set_error (error, + NM_SETTING_WIRELESS_SECURITY_ERROR, + NM_SETTING_WIRELESS_SECURITY_ERROR_INVALID_PROPERTY, + "%s is incompatible with WPA group ciphers", tag); + return FALSE; + } + } + + if (nm_setting_wireless_security_get_psk (s_wsec)) { + g_set_error (error, + NM_SETTING_WIRELESS_SECURITY_ERROR, + NM_SETTING_WIRELESS_SECURITY_ERROR_INVALID_PROPERTY, + "%s is incompatible with a WPA Pre-Shared Key", tag); + return FALSE; + } + + return TRUE; +} + +static gboolean +verify_dynamic_wep (NMSettingWirelessSecurity *s_wsec, + NMSetting8021x *s_8021x, + gboolean adhoc, + GError **error) +{ + const char *key_mgmt, *auth_alg, *leap_username; + + key_mgmt = nm_setting_wireless_security_get_key_mgmt (s_wsec); + auth_alg = nm_setting_wireless_security_get_auth_alg (s_wsec); + leap_username = nm_setting_wireless_security_get_leap_username (s_wsec); + + g_return_val_if_fail (leap_username == NULL, TRUE); + + if (key_mgmt) { + if (!strcmp (key_mgmt, "ieee8021x")) { + if (!s_8021x) { + /* 802.1x key management requires an 802.1x setting */ + g_set_error_literal (error, + NM_SETTING_WIRELESS_SECURITY_ERROR, + NM_SETTING_WIRELESS_SECURITY_ERROR_INVALID_PROPERTY, + "Dynamic WEP requires an 802.1x setting"); + return FALSE; + } + + if (auth_alg && strcmp (auth_alg, "open")) { + /* 802.1x key management must use "open" authentication */ + g_set_error_literal (error, + NM_SETTING_WIRELESS_SECURITY_ERROR, + NM_SETTING_WIRELESS_SECURITY_ERROR_INVALID_PROPERTY, + "Dynamic WEP requires 'open' authentication"); + return FALSE; + } + + /* Dynamic WEP incompatible with anything static WEP related */ + if (!verify_no_wep (s_wsec, "Dynamic WEP", error)) + return FALSE; + } else if (!strcmp (key_mgmt, "none")) { + if (s_8021x) { + /* 802.1x setting requires 802.1x key management */ + g_set_error_literal (error, + NM_SETTING_WIRELESS_SECURITY_ERROR, + NM_SETTING_WIRELESS_SECURITY_ERROR_INVALID_PROPERTY, + "Dynamic WEP requires 'ieee8021x' key management"); + return FALSE; + } + } + } else if (s_8021x) { + /* 802.1x setting incompatible with anything but 'open' auth */ + if (auth_alg && strcmp (auth_alg, "open")) { + /* 802.1x key management must use "open" authentication */ + g_set_error_literal (error, + NM_SETTING_WIRELESS_SECURITY_ERROR, + NM_SETTING_WIRELESS_SECURITY_ERROR_INVALID_PROPERTY, + "Dynamic WEP requires 'open' authentication"); + return FALSE; + } + + /* Dynamic WEP incompatible with anything static WEP related */ + if (!verify_no_wep (s_wsec, "Dynamic WEP", error)) + return FALSE; + } + + return TRUE; +} + +static gboolean +verify_wpa_psk (NMSettingWirelessSecurity *s_wsec, + NMSetting8021x *s_8021x, + gboolean adhoc, + guint32 wpa_flags, + guint32 rsn_flags, + GError **error) +{ + const char *key_mgmt, *auth_alg, *tmp; + int n; + + key_mgmt = nm_setting_wireless_security_get_key_mgmt (s_wsec); + auth_alg = nm_setting_wireless_security_get_auth_alg (s_wsec); + + if (key_mgmt) { + if (!strcmp (key_mgmt, "wpa-psk") || !strcmp (key_mgmt, "wpa-none")) { + if (s_8021x) { + g_set_error_literal (error, + NM_SETTING_WIRELESS_SECURITY_ERROR, + NM_SETTING_WIRELESS_SECURITY_ERROR_INVALID_PROPERTY, + "WPA-PSK incompatible with 802.1x"); + return FALSE; + } + + if (auth_alg && strcmp (auth_alg, "open")) { + /* WPA must use "open" authentication */ + g_set_error_literal (error, + NM_SETTING_WIRELESS_SECURITY_ERROR, + NM_SETTING_WIRELESS_SECURITY_ERROR_INVALID_PROPERTY, + "WPA-PSK requires 'open' authentication"); + return FALSE; + } + } + + if (!strcmp (key_mgmt, "wpa-none")) { + if (!adhoc) { + g_set_error_literal (error, + NM_SETTING_WIRELESS_SECURITY_ERROR, + NM_SETTING_WIRELESS_SECURITY_ERROR_INVALID_PROPERTY, + "WPA Ad-Hoc requires an Ad-Hoc mode AP"); + return FALSE; + } + + /* Ad-Hoc WPA requires 'wpa' proto, 'none' pairwise, and 'tkip' group */ + n = nm_setting_wireless_security_get_num_protos (s_wsec); + tmp = (n > 0) ? nm_setting_wireless_security_get_proto (s_wsec, 0) : NULL; + if (n > 1 || strcmp (tmp, "wpa")) { + g_set_error_literal (error, + NM_SETTING_WIRELESS_SECURITY_ERROR, + NM_SETTING_WIRELESS_SECURITY_ERROR_INVALID_PROPERTY, + "WPA Ad-Hoc requires 'wpa' proto"); + return FALSE; + } + + n = nm_setting_wireless_security_get_num_pairwise (s_wsec); + tmp = (n > 0) ? nm_setting_wireless_security_get_pairwise (s_wsec, 0) : NULL; + if (n > 1 || strcmp (tmp, "none")) { + g_set_error_literal (error, + NM_SETTING_WIRELESS_SECURITY_ERROR, + NM_SETTING_WIRELESS_SECURITY_ERROR_INVALID_PROPERTY, + "WPA Ad-Hoc requires 'none' pairwise cipher"); + return FALSE; + } + + n = nm_setting_wireless_security_get_num_groups (s_wsec); + tmp = (n > 0) ? nm_setting_wireless_security_get_group (s_wsec, 0) : NULL; + if (n > 1 || strcmp (tmp, "tkip")) { + g_set_error_literal (error, + NM_SETTING_WIRELESS_SECURITY_ERROR, + NM_SETTING_WIRELESS_SECURITY_ERROR_INVALID_PROPERTY, + "WPA Ad-Hoc requires 'tkip' group cipher"); + return FALSE; + } + } + + if (!strcmp (key_mgmt, "wpa-psk")) { + /* Make sure the AP's capabilities support WPA-PSK */ + if ( !(wpa_flags & NM_802_11_AP_SEC_KEY_MGMT_PSK) + && !(rsn_flags & NM_802_11_AP_SEC_KEY_MGMT_PSK)) { + g_set_error_literal (error, + NM_SETTING_WIRELESS_SECURITY_ERROR, + NM_SETTING_WIRELESS_SECURITY_ERROR_INVALID_PROPERTY, + "AP does not support PSK but setting requires it"); + return FALSE; + } + } + } + + return TRUE; +} + +static gboolean +verify_wpa_eap (NMSettingWirelessSecurity *s_wsec, + NMSetting8021x *s_8021x, + guint32 wpa_flags, + guint32 rsn_flags, + GError **error) +{ + const char *key_mgmt, *auth_alg; + gboolean is_wpa_eap = FALSE; + + key_mgmt = nm_setting_wireless_security_get_key_mgmt (s_wsec); + auth_alg = nm_setting_wireless_security_get_auth_alg (s_wsec); + + if (key_mgmt) { + if (!strcmp (key_mgmt, "wpa-eap")) { + if (!s_8021x) { + g_set_error_literal (error, + NM_SETTING_WIRELESS_SECURITY_ERROR, + NM_SETTING_WIRELESS_SECURITY_ERROR_INVALID_PROPERTY, + "WPA-EAP requires an 802.1x setting"); + return FALSE; + } + + if (auth_alg && strcmp (auth_alg, "open")) { + /* WPA must use "open" authentication */ + g_set_error_literal (error, + NM_SETTING_WIRELESS_SECURITY_ERROR, + NM_SETTING_WIRELESS_SECURITY_ERROR_INVALID_PROPERTY, + "WPA-EAP requires 'open' authentication"); + return FALSE; + } + + is_wpa_eap = TRUE; + } else if (s_8021x) { + g_set_error_literal (error, + NM_SETTING_WIRELESS_SECURITY_ERROR, + NM_SETTING_WIRELESS_SECURITY_ERROR_INVALID_PROPERTY, + "Setting requires 802.1x but does not use 'wpa-eap' key management"); + return FALSE; + } + } + + if (is_wpa_eap || s_8021x) { + /* Make sure the AP's capabilities support WPA-EAP */ + if ( !(wpa_flags & NM_802_11_AP_SEC_KEY_MGMT_802_1X) + && !(rsn_flags & NM_802_11_AP_SEC_KEY_MGMT_802_1X)) { + g_set_error_literal (error, + NM_SETTING_WIRELESS_SECURITY_ERROR, + NM_SETTING_WIRELESS_SECURITY_ERROR_INVALID_PROPERTY, + "AP does not support 802.1x but setting requires it"); + return FALSE; + } + } + + return TRUE; +} + +static gboolean +verify_adhoc (NMSettingWirelessSecurity *s_wsec, + NMSetting8021x *s_8021x, + gboolean adhoc, + GError **error) +{ + const char *key_mgmt = NULL, *leap_username = NULL, *auth_alg = NULL; + + if (s_wsec) { + key_mgmt = nm_setting_wireless_security_get_key_mgmt (s_wsec); + auth_alg = nm_setting_wireless_security_get_auth_alg (s_wsec); + leap_username = nm_setting_wireless_security_get_leap_username (s_wsec); + } + + if (adhoc) { + if (key_mgmt && strcmp (key_mgmt, "wpa-none") && strcmp (key_mgmt, "none")) { + g_set_error_literal (error, + NM_SETTING_WIRELESS_SECURITY_ERROR, + NM_SETTING_WIRELESS_SECURITY_ERROR_INVALID_PROPERTY, + "AP mode is Ad-Hoc but setting requires Infrastructure security"); + return FALSE; + } + + if (s_8021x) { + g_set_error_literal (error, + NM_SETTING_WIRELESS_SECURITY_ERROR, + NM_SETTING_WIRELESS_SECURITY_ERROR_INVALID_PROPERTY, + "Ad-Hoc mode incompatible with 802.1x security"); + return FALSE; + } + + if (leap_username) { + g_set_error_literal (error, + NM_SETTING_WIRELESS_SECURITY_ERROR, + NM_SETTING_WIRELESS_SECURITY_ERROR_INVALID_PROPERTY, + "Ad-Hoc mode incompatible with LEAP security"); + return FALSE; + } + + if (auth_alg && strcmp (auth_alg, "open")) { + g_set_error_literal (error, + NM_SETTING_WIRELESS_SECURITY_ERROR, + NM_SETTING_WIRELESS_SECURITY_ERROR_INVALID_PROPERTY, + "Ad-Hoc mode requires 'open' authentication"); + return FALSE; + } + } else { + if (key_mgmt && !strcmp (key_mgmt, "wpa-none")) { + g_set_error_literal (error, + NM_SETTING_WIRELESS_SECURITY_ERROR, + NM_SETTING_WIRELESS_SECURITY_ERROR_INVALID_PROPERTY, + "AP mode is Infrastructure but setting requires Ad-Hoc security"); + return FALSE; + } + } + + return TRUE; +} + +gboolean +nm_ap_utils_complete_connection (const GByteArray *ap_ssid, + const guint8 ap_bssid[ETH_ALEN], + NM80211Mode ap_mode, + guint32 ap_flags, + guint32 ap_wpa_flags, + guint32 ap_rsn_flags, + NMConnection *connection, + gboolean lock_bssid, + GError **error) +{ + NMSettingWireless *s_wifi; + NMSettingWirelessSecurity *s_wsec; + NMSetting8021x *s_8021x; + const GByteArray *ssid; + const char *mode, *key_mgmt, *leap_username; + gboolean adhoc = FALSE; + + s_wifi = (NMSettingWireless *) nm_connection_get_setting (connection, NM_TYPE_SETTING_WIRELESS); + s_wsec = (NMSettingWirelessSecurity *) nm_connection_get_setting (connection, NM_TYPE_SETTING_WIRELESS_SECURITY); + s_8021x = (NMSetting8021x *) nm_connection_get_setting (connection, NM_TYPE_SETTING_802_1X); + + if (!s_wifi) { + s_wifi = (NMSettingWireless *) nm_setting_wireless_new (); + nm_connection_add_setting (connection, NM_SETTING (s_wifi)); + } + + /* Fill in missing SSID */ + ssid = nm_setting_wireless_get_ssid (s_wifi); + if (!ssid) + g_object_set (G_OBJECT (s_wifi), NM_SETTING_WIRELESS_SSID, ap_ssid, NULL); + else if ( ssid->len != ap_ssid->len + || memcmp (ssid->data, ap_ssid->data, ssid->len)) { + g_set_error_literal (error, + NM_SETTING_WIRELESS_ERROR, + NM_SETTING_WIRELESS_ERROR_INVALID_PROPERTY, + "Setting SSID did not match AP SSID"); + return FALSE; + } + + if (lock_bssid && !nm_setting_wireless_get_bssid (s_wifi)) { + GByteArray *bssid; + + bssid = g_byte_array_sized_new (ETH_ALEN); + g_byte_array_append (bssid, ap_bssid, ETH_ALEN); + g_object_set (G_OBJECT (s_wifi), NM_SETTING_WIRELESS_BSSID, bssid, NULL); + g_byte_array_free (bssid, TRUE); + } + + /* And mode */ + mode = nm_setting_wireless_get_mode (s_wifi); + if (mode) { + gboolean valid = FALSE; + + /* Make sure the supplied mode matches the AP's */ + if (!strcmp (mode, NM_SETTING_WIRELESS_MODE_INFRA)) { + if (ap_mode == NM_802_11_MODE_INFRA) + valid = TRUE; + } else if (!strcmp (mode, NM_SETTING_WIRELESS_MODE_ADHOC)) { + if (ap_mode == NM_802_11_MODE_ADHOC) + valid = TRUE; + adhoc = TRUE; + } + + if (valid == FALSE) { + g_set_error (error, + NM_SETTING_WIRELESS_ERROR, + NM_SETTING_WIRELESS_ERROR_INVALID_PROPERTY, + NM_SETTING_WIRELESS_MODE); + return FALSE; + } + } else { + mode = NM_SETTING_WIRELESS_MODE_INFRA; + if (ap_mode == NM_802_11_MODE_ADHOC) { + mode = NM_SETTING_WIRELESS_MODE_ADHOC; + adhoc = TRUE; + } + g_object_set (G_OBJECT (s_wifi), NM_SETTING_WIRELESS_MODE, mode, NULL); + } + + /* Security */ + + /* Open */ + if ( !(ap_flags & NM_802_11_AP_FLAGS_PRIVACY) + && (ap_wpa_flags == NM_802_11_AP_SEC_NONE) + && (ap_rsn_flags == NM_802_11_AP_SEC_NONE)) { + /* Make sure the connection doesn't specify security */ + if (nm_setting_wireless_get_security (s_wifi) || s_wsec || s_8021x) { + g_set_error_literal (error, + NM_SETTING_WIRELESS_SECURITY_ERROR, + NM_SETTING_WIRELESS_SECURITY_ERROR_INVALID_PROPERTY, + "AP is unencrypted but setting specifies security"); + return FALSE; + } + return TRUE; + } + + /* Everything else requires security */ + g_object_set (G_OBJECT (s_wifi), + NM_SETTING_WIRELESS_SEC, NM_SETTING_WIRELESS_SECURITY_SETTING_NAME, + NULL); + if (!s_wsec) { + s_wsec = (NMSettingWirelessSecurity *) nm_setting_wireless_security_new (); + nm_connection_add_setting (connection, NM_SETTING (s_wsec)); + } + + key_mgmt = nm_setting_wireless_security_get_key_mgmt (s_wsec); + leap_username = nm_setting_wireless_security_get_leap_username (s_wsec); + + /* Ad-Hoc checks */ + if (!verify_adhoc (s_wsec, s_8021x, adhoc, error)) + return FALSE; + + /* Static WEP, Dynamic WEP, or LEAP */ + if ( (ap_flags & NM_802_11_AP_FLAGS_PRIVACY) + && (ap_wpa_flags == NM_802_11_AP_SEC_NONE) + && (ap_rsn_flags == NM_802_11_AP_SEC_NONE)) { + const char *tag = "WEP"; + gboolean is_dynamic_wep = FALSE; + + if (!verify_leap (s_wsec, s_8021x, adhoc, error)) + return FALSE; + + if (leap_username) { + tag = "LEAP"; + } else { + /* Static or Dynamic WEP */ + if (!verify_dynamic_wep (s_wsec, s_8021x, adhoc, error)) + return FALSE; + + if (s_8021x || (key_mgmt && !strcmp (key_mgmt, "ieee8021x"))) { + is_dynamic_wep = TRUE; + tag = "Dynamic WEP"; + } + } + + /* Nothing WPA-related can be set */ + if (!verify_no_wpa (s_wsec, tag, error)) + return FALSE; + + if (leap_username) { + /* LEAP */ + g_object_set (s_wsec, + NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, "ieee8021x", + NM_SETTING_WIRELESS_SECURITY_AUTH_ALG, "leap", + NULL); + } else if (is_dynamic_wep) { + /* Dynamic WEP */ + g_object_set (s_wsec, + NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, "ieee8021x", + NM_SETTING_WIRELESS_SECURITY_AUTH_ALG, "open", + NULL); + nm_setting_wireless_security_add_pairwise (s_wsec, "wep40"); + nm_setting_wireless_security_add_pairwise (s_wsec, "wep104"); + nm_setting_wireless_security_add_group (s_wsec, "wep40"); + nm_setting_wireless_security_add_group (s_wsec, "wep104"); + + if (s_8021x) { + /* Dynamic WEP requires a valid 802.1x setting since we can't + * autocomplete 802.1x. + */ + if (!nm_setting_verify (NM_SETTING (s_8021x), NULL, error)) + return FALSE; + } + } else { + /* Static WEP */ + g_object_set (s_wsec, + NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, "none", + NULL); + } + + return TRUE; + } + + /* WPA/RSN */ + g_assert (ap_wpa_flags != NM_802_11_AP_SEC_NONE); + g_assert (ap_rsn_flags != NM_802_11_AP_SEC_NONE); + + if (leap_username) { + g_set_error_literal (error, + NM_SETTING_WIRELESS_SECURITY_ERROR, + NM_SETTING_WIRELESS_SECURITY_ERROR_INVALID_PROPERTY, + "WPA incompatible with non-EAP (original) LEAP"); + return FALSE; + } + + if (!verify_no_wep (s_wsec, "WPA", error)) + return FALSE; + + if (!verify_wpa_psk (s_wsec, s_8021x, adhoc, ap_wpa_flags, ap_rsn_flags, error)) + return FALSE; + + if (!adhoc && !verify_wpa_eap (s_wsec, s_8021x, ap_wpa_flags, ap_rsn_flags, error)) + return FALSE; + + + if (adhoc) { + g_object_set (s_wsec, NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, "wpa-none", NULL); + /* Ad-Hoc does not support RSN/WPA2 */ + nm_setting_wireless_security_add_proto (s_wsec, "wpa"); + nm_setting_wireless_security_add_pairwise (s_wsec, "none"); + nm_setting_wireless_security_add_group (s_wsec, "tkip"); + } else if (s_8021x) { + g_object_set (s_wsec, + NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, "wpa-eap", + NM_SETTING_WIRELESS_SECURITY_AUTH_ALG, "open", + NULL); + /* Leave proto/pairwise/group as client set them; if they are unset the + * supplicant will figure out the best combination at connect time. + */ + + /* 802.1x also requires the client to completely fill in the 8021x + * setting. Since there's so much configuration required for it, there's + * no way it can be automatically completed. + */ + } else if ( (key_mgmt && !strcmp (key_mgmt, "wpa-psk")) + || (ap_wpa_flags & NM_802_11_AP_SEC_KEY_MGMT_PSK) + || (ap_rsn_flags & NM_802_11_AP_SEC_KEY_MGMT_PSK)) { + g_object_set (s_wsec, + NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, "wpa-psk", + NM_SETTING_WIRELESS_SECURITY_AUTH_ALG, "open", + NULL); + /* Leave proto/pairwise/group as client set them; if they are unset the + * supplicant will figure out the best combination at connect time. + */ + } else { + g_set_error_literal (error, + NM_SETTING_WIRELESS_SECURITY_ERROR, + NM_SETTING_WIRELESS_SECURITY_ERROR_INVALID_PROPERTY, + "Failed to determine AP security information"); + return FALSE; + } + + return TRUE; +} + diff --git a/src/nm-wifi-ap-utils.h b/src/nm-wifi-ap-utils.h new file mode 100644 index 0000000000..a464867b96 --- /dev/null +++ b/src/nm-wifi-ap-utils.h @@ -0,0 +1,43 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * 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 2011 Red Hat, Inc. + */ + +#ifndef NM_WIFI_AP_UTILS_H +#define NM_WIFI_AP_UTILS_H + +#include + +#include +#include +#include +#include +#include + +gboolean nm_ap_utils_complete_connection (const GByteArray *ssid, + const guint8 bssid[ETH_ALEN], + NM80211Mode mode, + guint32 flags, + guint32 wpa_flags, + guint32 rsn_flags, + NMConnection *connection, + gboolean lock_bssid, + GError **error); + +#endif /* NM_WIFI_AP_UTILS_H */ + diff --git a/src/nm-wifi-ap.c b/src/nm-wifi-ap.c index 7770b8bc42..e596a08b8c 100644 --- a/src/nm-wifi-ap.c +++ b/src/nm-wifi-ap.c @@ -24,6 +24,7 @@ #include #include "nm-wifi-ap.h" +#include "nm-wifi-ap-utils.h" #include "NetworkManagerUtils.h" #include "nm-utils.h" #include "nm-logging.h" @@ -1275,6 +1276,27 @@ nm_ap_check_compatible (NMAccessPoint *self, nm_ap_get_mode (self)); } +gboolean +nm_ap_complete_connection (NMAccessPoint *self, + NMConnection *connection, + gboolean lock_bssid, + GError **error) +{ + NMAccessPointPrivate *priv = NM_AP_GET_PRIVATE (self); + + g_return_val_if_fail (connection != NULL, FALSE); + + return nm_ap_utils_complete_connection (priv->ssid, + priv->address.ether_addr_octet, + priv->mode, + priv->flags, + priv->wpa_flags, + priv->rsn_flags, + connection, + lock_bssid, + error); +} + static gboolean capabilities_compatible (guint32 a_flags, guint32 b_flags) { diff --git a/src/nm-wifi-ap.h b/src/nm-wifi-ap.h index 86b785a316..62457765f4 100644 --- a/src/nm-wifi-ap.h +++ b/src/nm-wifi-ap.h @@ -15,7 +15,7 @@ * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * - * Copyright (C) 2004 - 2010 Red Hat, Inc. + * Copyright (C) 2004 - 2011 Red Hat, Inc. * Copyright (C) 2006 - 2008 Novell, Inc. */ @@ -117,6 +117,11 @@ guint32 nm_ap_add_security_from_ie (guint32 flags, gboolean nm_ap_check_compatible (NMAccessPoint *self, NMConnection *connection); +gboolean nm_ap_complete_connection (NMAccessPoint *self, + NMConnection *connection, + gboolean lock_bssid, + GError **error); + NMAccessPoint * nm_ap_match_in_list (NMAccessPoint *find_ap, GSList *ap_list, gboolean strict_match); diff --git a/src/settings/nm-settings.c b/src/settings/nm-settings.c index 7c2e7c2c2e..2f47b7a62c 100644 --- a/src/settings/nm-settings.c +++ b/src/settings/nm-settings.c @@ -776,9 +776,11 @@ pk_add_cb (NMAuthChain *chain, NMAuthCallResult result; GError *error = NULL, *add_error = NULL; NMConnection *connection; - NMSysconfigConnection *added; + NMSysconfigConnection *added = NULL; gulong caller_uid = G_MAXULONG; char *error_desc = NULL; + NMSettingsAddCallback callback; + gpointer callback_data; priv->auths = g_slist_remove (priv->auths, chain); @@ -830,9 +832,7 @@ pk_add_cb (NMAuthChain *chain, } added = add_new_connection (self, connection, &add_error); - if (added) - dbus_g_method_return (context, nm_connection_get_path (NM_CONNECTION (added))); - else { + if (!added) { error = g_error_new (NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_ADD_FAILED, "Saving connection failed: (%d) %s", @@ -842,37 +842,45 @@ pk_add_cb (NMAuthChain *chain, } done: - if (error) - dbus_g_method_return_error (context, error); + callback = nm_auth_chain_get_data (chain, "callback"); + callback_data = nm_auth_chain_get_data (chain, "callback-data"); + + callback (self, added, error, context, callback_data); g_clear_error (&error); nm_auth_chain_unref (chain); } static void -impl_settings_add_connection (NMSettings *self, - GHashTable *settings, - DBusGMethodInvocation *context) +add_cb (NMSettings *self, + NMSysconfigConnection *connection, + GError *error, + DBusGMethodInvocation *context, + gpointer user_data) +{ + if (error) + dbus_g_method_return_error (context, error); + else + dbus_g_method_return (context, nm_connection_get_path (NM_CONNECTION (connection))); +} + +void +nm_settings_add_connection (NMSettings *self, + NMConnection *connection, + DBusGMethodInvocation *context, + NMSettingsAddCallback callback, + gpointer user_data) { NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self); NMAuthChain *chain; - NMConnection *connection; - GError *error = NULL; - - connection = nm_connection_new_from_hash (settings, &error); - if (!connection) { - g_assert (error); - dbus_g_method_return_error (context, error); - g_error_free (error); - return; - } + GError *error; /* Do any of the plugins support adding? */ if (!get_plugin (self, NM_SYSTEM_CONFIG_INTERFACE_CAP_MODIFY_CONNECTIONS)) { error = g_error_new_literal (NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_ADD_NOT_SUPPORTED, "None of the registered plugins support add."); - dbus_g_method_return_error (context, error); + callback (self, NULL, error, context, user_data); g_error_free (error); return; } @@ -882,7 +890,28 @@ impl_settings_add_connection (NMSettings *self, g_assert (chain); priv->auths = g_slist_append (priv->auths, chain); nm_auth_chain_add_call (chain, NM_AUTH_PERMISSION_SETTINGS_CONNECTION_MODIFY, TRUE); - nm_auth_chain_set_data (chain, "connection", connection, g_object_unref); + nm_auth_chain_set_data (chain, "connection", g_object_ref (connection), g_object_unref); + nm_auth_chain_set_data (chain, "callback", callback, NULL); + nm_auth_chain_set_data (chain, "callback-data", user_data, NULL); +} + +static void +impl_settings_add_connection (NMSettings *self, + GHashTable *settings, + DBusGMethodInvocation *context) +{ + NMConnection *connection; + GError *error = NULL; + + connection = nm_connection_new_from_hash (settings, &error); + if (connection) { + nm_settings_add_connection (self, connection, context, add_cb, NULL); + g_object_unref (connection); + } else { + g_assert (error); + dbus_g_method_return_error (context, error); + g_error_free (error); + } } static void diff --git a/src/settings/nm-settings.h b/src/settings/nm-settings.h index 51c454fd99..0ba3170d17 100644 --- a/src/settings/nm-settings.h +++ b/src/settings/nm-settings.h @@ -84,6 +84,18 @@ void nm_settings_for_each_connection (NMSettings *settings, NMSettingsForEachFunc for_each_func, gpointer user_data); +typedef void (*NMSettingsAddCallback) (NMSettings *settings, + NMSysconfigConnection *connection, + GError *error, + DBusGMethodInvocation *context, + gpointer user_data); + +void nm_settings_add_connection (NMSettings *self, + NMConnection *connection, + DBusGMethodInvocation *context, + NMSettingsAddCallback callback, + gpointer user_data); + /* Returns a list of NMSysconfigConnections. Caller must free the list with * g_slist_free(). */ diff --git a/src/tests/Makefile.am b/src/tests/Makefile.am index 88c4c683f4..2b889cc313 100644 --- a/src/tests/Makefile.am +++ b/src/tests/Makefile.am @@ -6,7 +6,10 @@ INCLUDES = \ -I$(top_srcdir)/src \ -I$(top_builddir)/src -noinst_PROGRAMS = test-dhcp-options test-policy-hosts +noinst_PROGRAMS = \ + test-dhcp-options \ + test-policy-hosts \ + test-wifi-ap-utils ####### DHCP options test ####### @@ -39,6 +42,20 @@ test_policy_hosts_LDADD = \ $(top_builddir)/src/libtest-policy-hosts.la \ $(GLIB_LIBS) +####### wifi ap utils test ####### + +test_wifi_ap_utils_SOURCES = \ + test-wifi-ap-utils.c + +test_wifi_ap_utils_CPPFLAGS = \ + $(GLIB_CFLAGS) + +test_wifi_ap_utils_LDADD = \ + $(top_builddir)/libnm-util/libnm-util.la \ + $(top_builddir)/src/libtest-wifi-ap-utils.la \ + $(GLIB_LIBS) \ + $(DBUS_LIBS) + ####### secret agent interface test ####### EXTRA_DIST = test-secret-agent.py @@ -50,6 +67,7 @@ if WITH_TESTS check-local: test-dhcp-options test-policy-hosts $(abs_builddir)/test-dhcp-options $(abs_builddir)/test-policy-hosts + $(abs_builddir)/test-wifi-ap-utils endif diff --git a/src/tests/test-wifi-ap-utils.c b/src/tests/test-wifi-ap-utils.c new file mode 100644 index 0000000000..b2cd234ece --- /dev/null +++ b/src/tests/test-wifi-ap-utils.c @@ -0,0 +1,973 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright (C) 2011 Red Hat, Inc. + * + */ + +#include +#include + +#include "nm-wifi-ap-utils.h" + +#include "nm-setting-connection.h" +#include "nm-setting-wireless.h" +#include "nm-setting-wireless-security.h" +#include "nm-setting-8021x.h" + +#define DEBUG 1 + +/*******************************************/ + +#define COMPARE(src, expected, success, error, edomain, ecode) \ +{ \ + if (expected) { \ + if (!success) { \ + g_assert (error != NULL); \ + g_warning ("Failed to complete connection: (%d) %s", error->code, error->message); \ + } \ + g_assert (success == TRUE); \ + g_assert (error == NULL); \ +\ + success = nm_connection_compare (src, expected, NM_SETTING_COMPARE_FLAG_EXACT); \ + if (success == FALSE && DEBUG) { \ + g_message ("\n- COMPLETED ---------------------------------\n"); \ + nm_connection_dump (src); \ + g_message ("+ EXPECTED ++++++++++++++++++++++++++++++++++++\n"); \ + nm_connection_dump (expected); \ + g_message ("^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n"); \ + } \ + g_assert (success == TRUE); \ + } else { \ + if (success) { \ + g_message ("\n- COMPLETED ---------------------------------\n"); \ + nm_connection_dump (src); \ + } \ + g_assert (success == FALSE); \ + g_assert_error (error, edomain, ecode); \ + } \ + \ + g_clear_error (&error); \ +} + +static gboolean +complete_connection (const char *ssid, + const guint8 bssid[ETH_ALEN], + NM80211Mode mode, + guint32 flags, + guint32 wpa_flags, + guint32 rsn_flags, + gboolean lock_bssid, + NMConnection *src, + GError **error) +{ + GByteArray *tmp; + gboolean success; + + tmp = g_byte_array_sized_new (strlen (ssid)); + g_byte_array_append (tmp, (const guint8 *) ssid, strlen (ssid)); + + success = nm_ap_utils_complete_connection (tmp, + bssid, + mode, + flags, + wpa_flags, + rsn_flags, + src, + lock_bssid, + error); + g_byte_array_free (tmp, TRUE); + return success; +} + +static NMConnection * +create_expected (const char *ssid, + const guint8 *bssid, + NM80211Mode mode, + gboolean add_security, + gboolean add_8021x, + const char *key_mgmt, + const char *auth_alg, + NMSettingWireless **out_s_wifi, + NMSettingWirelessSecurity **out_s_wsec, + NMSetting8021x **out_s_8021x) +{ + NMConnection *connection; + NMSettingWireless *s_wifi = NULL; + NMSettingWirelessSecurity *s_wsec = NULL; + NMSetting8021x *s_8021x = NULL; + GByteArray *tmp; + + connection = nm_connection_new (); + + s_wifi = (NMSettingWireless *) nm_setting_wireless_new (); + nm_connection_add_setting (connection, NM_SETTING (s_wifi)); + + /* SSID */ + tmp = g_byte_array_sized_new (strlen (ssid)); + g_byte_array_append (tmp, (const guint8 *) ssid, strlen (ssid)); + g_object_set (G_OBJECT (s_wifi), NM_SETTING_WIRELESS_SSID, tmp, NULL); + g_byte_array_free (tmp, TRUE); + + /* BSSID */ + if (bssid) { + tmp = g_byte_array_sized_new (ETH_ALEN); + g_byte_array_append (tmp, bssid, ETH_ALEN); + g_object_set (G_OBJECT (s_wifi), NM_SETTING_WIRELESS_BSSID, tmp, NULL); + g_byte_array_free (tmp, TRUE); + } + + if (mode == NM_802_11_MODE_INFRA) + g_object_set (G_OBJECT (s_wifi), NM_SETTING_WIRELESS_MODE, "infrastructure", NULL); + else if (mode == NM_802_11_MODE_ADHOC) + g_object_set (G_OBJECT (s_wifi), NM_SETTING_WIRELESS_MODE, "adhoc", NULL); + else + g_assert_not_reached (); + + if (add_security) { + s_wsec = (NMSettingWirelessSecurity *) nm_setting_wireless_security_new (); + nm_connection_add_setting (connection, NM_SETTING (s_wsec)); + + g_object_set (G_OBJECT (s_wifi), + NM_SETTING_WIRELESS_SEC, NM_SETTING_WIRELESS_SECURITY_SETTING_NAME, + NULL); + if (key_mgmt) + g_object_set (G_OBJECT (s_wsec), NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, key_mgmt, NULL); + if (auth_alg) + g_object_set (G_OBJECT (s_wsec), NM_SETTING_WIRELESS_SECURITY_AUTH_ALG, auth_alg, NULL); + + if (add_8021x) { + s_8021x = (NMSetting8021x *) nm_setting_802_1x_new (); + nm_connection_add_setting (connection, NM_SETTING (s_8021x)); + } + } + + if (out_s_wifi) + *out_s_wifi = s_wifi; + if (out_s_wsec) + *out_s_wsec = s_wsec; + if (out_s_8021x) + *out_s_8021x = s_8021x; + return connection; +} + +static NMSettingWireless * +get_wifi_setting (NMConnection *connection, gboolean add_if_absent) +{ + NMSettingWireless *s_wifi; + + s_wifi = (NMSettingWireless *) nm_connection_get_setting (connection, NM_TYPE_SETTING_WIRELESS); + if (add_if_absent) { + if (!s_wifi) { + s_wifi = (NMSettingWireless *) nm_setting_wireless_new (); + nm_connection_add_setting (connection, NM_SETTING (s_wifi)); + } + g_object_set (G_OBJECT (s_wifi), + NM_SETTING_WIRELESS_SEC, NM_SETTING_WIRELESS_SECURITY_SETTING_NAME, + NULL); + } + return s_wifi; +} + +static void +fill_wep (NMConnection *connection, + const char *key0, + guint32 tx_keyidx, + const char *auth_alg, + gboolean set_s_wifi) +{ + NMSettingWireless *s_wifi; + NMSettingWirelessSecurity *s_wsec; + + s_wifi = get_wifi_setting (connection, set_s_wifi); + + s_wsec = (NMSettingWirelessSecurity *) nm_connection_get_setting (connection, NM_TYPE_SETTING_WIRELESS_SECURITY); + if (!s_wsec) { + s_wsec = (NMSettingWirelessSecurity *) nm_setting_wireless_security_new (); + nm_connection_add_setting (connection, NM_SETTING (s_wsec)); + } + + if (key0) { + g_object_set (G_OBJECT (s_wsec), + NM_SETTING_WIRELESS_SECURITY_WEP_KEY0, key0, + NM_SETTING_WIRELESS_SECURITY_WEP_TX_KEYIDX, tx_keyidx, + NULL); + } + + if (auth_alg) + g_object_set (G_OBJECT (s_wsec), NM_SETTING_WIRELESS_SECURITY_AUTH_ALG, auth_alg, NULL); +} + +static void +fill_leap (NMConnection *connection, + const char *leap_username, + gboolean set_auth_alg, + gboolean set_key_mgmt, + gboolean set_s_wifi) +{ + NMSettingWireless *s_wifi; + NMSettingWirelessSecurity *s_wsec; + + s_wifi = get_wifi_setting (connection, set_s_wifi); + + s_wsec = (NMSettingWirelessSecurity *) nm_connection_get_setting (connection, NM_TYPE_SETTING_WIRELESS_SECURITY); + if (!s_wsec) { + s_wsec = (NMSettingWirelessSecurity *) nm_setting_wireless_security_new (); + nm_connection_add_setting (connection, NM_SETTING (s_wsec)); + } + + if (leap_username) + g_object_set (G_OBJECT (s_wsec), NM_SETTING_WIRELESS_SECURITY_LEAP_USERNAME, leap_username, NULL); + + if (set_auth_alg) + g_object_set (G_OBJECT (s_wsec), NM_SETTING_WIRELESS_SECURITY_AUTH_ALG, "leap", NULL); + + if (set_key_mgmt) + g_object_set (G_OBJECT (s_wsec), NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, "ieee8021x", NULL); +} + +static void +fill_wpa_psk (NMConnection *connection, + const char *key_mgmt, + const char *psk, + const char *auth_alg, + gboolean set_s_wifi) +{ + NMSettingWireless *s_wifi; + NMSettingWirelessSecurity *s_wsec; + + s_wifi = get_wifi_setting (connection, set_s_wifi); + + s_wsec = (NMSettingWirelessSecurity *) nm_connection_get_setting (connection, NM_TYPE_SETTING_WIRELESS_SECURITY); + if (!s_wsec) { + s_wsec = (NMSettingWirelessSecurity *) nm_setting_wireless_security_new (); + nm_connection_add_setting (connection, NM_SETTING (s_wsec)); + } + + if (key_mgmt) + g_object_set (G_OBJECT (s_wsec), NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, key_mgmt, NULL); + if (psk) + g_object_set (G_OBJECT (s_wsec), NM_SETTING_WIRELESS_SECURITY_PSK, psk, NULL); + if (auth_alg) + g_object_set (G_OBJECT (s_wsec), NM_SETTING_WIRELESS_SECURITY_AUTH_ALG, auth_alg, NULL); +} + +static void +fill_8021x (NMConnection *connection, + NMSetting8021x *s_8021x, + const char *key_mgmt, + const char *auth_alg, + gboolean set_s_wifi) +{ + NMSettingWireless *s_wifi; + NMSettingWirelessSecurity *s_wsec; + + s_wifi = get_wifi_setting (connection, set_s_wifi); + + s_wsec = (NMSettingWirelessSecurity *) nm_connection_get_setting (connection, NM_TYPE_SETTING_WIRELESS_SECURITY); + if (!s_wsec) { + s_wsec = (NMSettingWirelessSecurity *) nm_setting_wireless_security_new (); + nm_connection_add_setting (connection, NM_SETTING (s_wsec)); + } + + if (key_mgmt) + g_object_set (G_OBJECT (s_wsec), NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, key_mgmt, NULL); + if (auth_alg) + g_object_set (G_OBJECT (s_wsec), NM_SETTING_WIRELESS_SECURITY_AUTH_ALG, auth_alg, NULL); + + nm_connection_add_setting (connection, NM_SETTING (s_8021x)); +} + +/*******************************************/ + +static void +test_lock_bssid (void) +{ + NMConnection *src, *expected; + const guint8 bssid[ETH_ALEN] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06 }; + const char *ssid = "blahblah"; + gboolean success; + GError *error = NULL; + + src = nm_connection_new (); + success = complete_connection (ssid, bssid, + NM_802_11_MODE_INFRA, NM_802_11_AP_FLAGS_NONE, + NM_WIFI_DEVICE_CAP_NONE, NM_WIFI_DEVICE_CAP_NONE, + TRUE, + src, &error); + + expected = create_expected (ssid, bssid, NM_802_11_MODE_INFRA, FALSE, FALSE, NULL, NULL, NULL, NULL, NULL); + COMPARE (src, expected, success, error, 0, 0); + + g_object_unref (src); + g_object_unref (expected); +} + +/*******************************************/ + +static void +test_open_ap_empty_connection (void) +{ + NMConnection *src, *expected; + const guint8 bssid[ETH_ALEN] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06 }; + const char *ssid = "blahblah"; + gboolean success; + GError *error = NULL; + + src = nm_connection_new (); + success = complete_connection (ssid, bssid, + NM_802_11_MODE_INFRA, NM_802_11_AP_FLAGS_NONE, + NM_WIFI_DEVICE_CAP_NONE, NM_WIFI_DEVICE_CAP_NONE, + FALSE, + src, &error); + + expected = create_expected (ssid, NULL, NM_802_11_MODE_INFRA, FALSE, FALSE, NULL, NULL, NULL, NULL, NULL); + COMPARE (src, expected, success, error, 0, 0); + + g_object_unref (src); + g_object_unref (expected); +} + +/*******************************************/ + +static void +test_open_ap_leap_connection_1 (gboolean fill_wifi) +{ + NMConnection *src; + NMSettingWireless *s_wifi; + NMSettingWirelessSecurity *s_wsec; + const guint8 bssid[ETH_ALEN] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06 }; + gboolean success; + GError *error = NULL; + + src = nm_connection_new (); + s_wifi = get_wifi_setting (src, fill_wifi); + s_wsec = (NMSettingWirelessSecurity *) nm_setting_wireless_security_new (); + g_object_set (G_OBJECT (s_wsec), NM_SETTING_WIRELESS_SECURITY_LEAP_USERNAME, "Bill Smith", NULL); + nm_connection_add_setting (src, NM_SETTING (s_wsec)); + + success = complete_connection ("blahblah", bssid, + NM_802_11_MODE_INFRA, NM_802_11_AP_FLAGS_NONE, + NM_WIFI_DEVICE_CAP_NONE, NM_WIFI_DEVICE_CAP_NONE, + FALSE, + src, &error); + /* We expect failure */ + COMPARE (src, NULL, success, error, NM_SETTING_WIRELESS_SECURITY_ERROR, NM_SETTING_WIRELESS_SECURITY_ERROR_INVALID_PROPERTY); + + g_object_unref (src); +} + +/*******************************************/ + +static void +test_open_ap_leap_connection_2 (void) +{ + NMConnection *src; + NMSettingWireless *s_wifi; + NMSettingWirelessSecurity *s_wsec; + const guint8 bssid[ETH_ALEN] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06 }; + gboolean success; + GError *error = NULL; + + src = nm_connection_new (); + s_wifi = get_wifi_setting (src, TRUE); + s_wsec = (NMSettingWirelessSecurity *) nm_setting_wireless_security_new (); + g_object_set (G_OBJECT (s_wsec), NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, "ieee8021x", NULL); + nm_connection_add_setting (src, NM_SETTING (s_wsec)); + + success = complete_connection ("blahblah", bssid, + NM_802_11_MODE_INFRA, NM_802_11_AP_FLAGS_NONE, + NM_WIFI_DEVICE_CAP_NONE, NM_WIFI_DEVICE_CAP_NONE, + FALSE, + src, &error); + /* We expect failure */ + COMPARE (src, NULL, success, error, NM_SETTING_WIRELESS_SECURITY_ERROR, NM_SETTING_WIRELESS_SECURITY_ERROR_INVALID_PROPERTY); + + g_object_unref (src); +} + +/*******************************************/ + +static void +test_open_ap_wep_connection (gboolean fill_wifi) +{ + NMConnection *src; + const guint8 bssid[ETH_ALEN] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06 }; + gboolean success; + GError *error = NULL; + + src = nm_connection_new (); + fill_wep (src, "11111111111111111111111111", 0, "open", fill_wifi); + success = complete_connection ("blahblah", bssid, + NM_802_11_MODE_INFRA, NM_802_11_AP_FLAGS_NONE, + NM_WIFI_DEVICE_CAP_NONE, NM_WIFI_DEVICE_CAP_NONE, + FALSE, + src, &error); + /* We expect failure */ + COMPARE (src, NULL, success, error, NM_SETTING_WIRELESS_SECURITY_ERROR, NM_SETTING_WIRELESS_SECURITY_ERROR_INVALID_PROPERTY); + + g_object_unref (src); +} + +/*******************************************/ + +static void +test_ap_wpa_psk_connection_base (const char *key_mgmt, + const char *auth_alg, + guint32 flags, + guint32 wpa_flags, + guint32 rsn_flags, + gboolean fill_wifi) +{ + NMConnection *src; + const guint8 bssid[ETH_ALEN] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06 }; + gboolean success; + GError *error = NULL; + + src = nm_connection_new (); + fill_wpa_psk (src, key_mgmt, "asdfasdfasdfasdfasdfafs", auth_alg, fill_wifi); + success = complete_connection ("blahblah", bssid, NM_802_11_MODE_INFRA, + flags, wpa_flags, rsn_flags, + FALSE, src, &error); + /* We expect failure */ + COMPARE (src, NULL, success, error, NM_SETTING_WIRELESS_SECURITY_ERROR, NM_SETTING_WIRELESS_SECURITY_ERROR_INVALID_PROPERTY); + + g_object_unref (src); +} + +static void +test_open_ap_wpa_psk_connection_1 (void) +{ + test_ap_wpa_psk_connection_base (NULL, NULL, + NM_802_11_AP_FLAGS_NONE, + NM_802_11_AP_SEC_NONE, + NM_802_11_AP_SEC_NONE, + FALSE); +} + +static void +test_open_ap_wpa_psk_connection_2 (void) +{ + test_ap_wpa_psk_connection_base (NULL, NULL, + NM_802_11_AP_FLAGS_NONE, + NM_802_11_AP_SEC_NONE, + NM_802_11_AP_SEC_NONE, + FALSE); +} + +static void +test_open_ap_wpa_psk_connection_3 (void) +{ + test_ap_wpa_psk_connection_base (NULL, "open", + NM_802_11_AP_FLAGS_NONE, + NM_802_11_AP_SEC_NONE, + NM_802_11_AP_SEC_NONE, + FALSE); +} + +static void +test_open_ap_wpa_psk_connection_4 (void) +{ + test_ap_wpa_psk_connection_base (NULL, "shared", + NM_802_11_AP_FLAGS_NONE, + NM_802_11_AP_SEC_NONE, + NM_802_11_AP_SEC_NONE, + FALSE); +} + +static void +test_open_ap_wpa_psk_connection_5 (void) +{ + test_ap_wpa_psk_connection_base ("wpa-psk", "open", + NM_802_11_AP_FLAGS_NONE, + NM_802_11_AP_SEC_NONE, + NM_802_11_AP_SEC_NONE, + FALSE); +} + +/*******************************************/ + +static void +test_ap_wpa_eap_connection_base (const char *key_mgmt, + const char *auth_alg, + guint32 flags, + guint32 wpa_flags, + guint32 rsn_flags, + gboolean fill_wifi, + guint error_domain, + guint error_code) +{ + NMConnection *src; + NMSetting8021x *s_8021x; + const guint8 bssid[ETH_ALEN] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06 }; + gboolean success; + GError *error = NULL; + const char *ssid = "blahblah"; + + src = nm_connection_new (); + s_8021x = (NMSetting8021x *) nm_setting_802_1x_new (); + fill_8021x (src, s_8021x, key_mgmt, auth_alg, fill_wifi); + success = complete_connection (ssid, bssid, NM_802_11_MODE_INFRA, + flags, wpa_flags, rsn_flags, + FALSE, src, &error); + if (!wpa_flags && !rsn_flags) { + if (!flags) { + /* Failure expected */ + COMPARE (src, NULL, success, error, NM_SETTING_WIRELESS_SECURITY_ERROR, NM_SETTING_WIRELESS_SECURITY_ERROR_INVALID_PROPERTY); + } else if (flags & NM_802_11_AP_FLAGS_PRIVACY) { + COMPARE (src, NULL, success, error, error_domain, error_code); + } + } else + g_assert_not_reached (); + + g_object_unref (src); +} + +static void +test_open_ap_wpa_eap_connection_1 (void) +{ + test_ap_wpa_eap_connection_base (NULL, NULL, + NM_802_11_AP_FLAGS_NONE, + NM_802_11_AP_SEC_NONE, + NM_802_11_AP_SEC_NONE, + FALSE, 0, 0); +} + +static void +test_open_ap_wpa_eap_connection_2 (void) +{ + test_ap_wpa_eap_connection_base (NULL, NULL, + NM_802_11_AP_FLAGS_NONE, + NM_802_11_AP_SEC_NONE, + NM_802_11_AP_SEC_NONE, + TRUE, 0, 0); +} + +static void +test_open_ap_wpa_eap_connection_3 (void) +{ + test_ap_wpa_eap_connection_base (NULL, "open", + NM_802_11_AP_FLAGS_NONE, + NM_802_11_AP_SEC_NONE, + NM_802_11_AP_SEC_NONE, + FALSE, 0, 0); +} + +static void +test_open_ap_wpa_eap_connection_4 (void) +{ + test_ap_wpa_eap_connection_base (NULL, "shared", + NM_802_11_AP_FLAGS_NONE, + NM_802_11_AP_SEC_NONE, + NM_802_11_AP_SEC_NONE, + FALSE, 0, 0); +} + +static void +test_open_ap_wpa_eap_connection_5 (void) +{ + test_ap_wpa_eap_connection_base ("wpa-eap", "open", + NM_802_11_AP_FLAGS_NONE, + NM_802_11_AP_SEC_NONE, + NM_802_11_AP_SEC_NONE, + FALSE, 0, 0); +} + +/*******************************************/ + +static void +test_priv_ap_empty_connection (void) +{ + NMConnection *src, *expected; + NMSettingWirelessSecurity *s_wsec; + const guint8 bssid[ETH_ALEN] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06 }; + const char *ssid = "blahblah"; + gboolean success; + GError *error = NULL; + + src = nm_connection_new (); + success = complete_connection (ssid, bssid, + NM_802_11_MODE_INFRA, NM_802_11_AP_FLAGS_PRIVACY, + NM_WIFI_DEVICE_CAP_NONE, NM_WIFI_DEVICE_CAP_NONE, + FALSE, + src, &error); + + /* Static WEP connection expected */ + expected = create_expected (ssid, NULL, NM_802_11_MODE_INFRA, TRUE, FALSE, "none", NULL, NULL, &s_wsec, NULL); + COMPARE (src, expected, success, error, 0, 0); + + g_object_unref (src); + g_object_unref (expected); +} + +/*******************************************/ + +static void +test_priv_ap_leap_connection_1 (gboolean fill_wifi) +{ + NMConnection *src, *expected; + NMSettingWirelessSecurity *s_wsec; + const char *ssid = "blahblah"; + const guint8 bssid[ETH_ALEN] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06 }; + const char *leap_username = "Bill Smith"; + gboolean success; + GError *error = NULL; + + src = nm_connection_new (); + fill_leap (src, leap_username, TRUE, FALSE, TRUE); + success = complete_connection (ssid, bssid, + NM_802_11_MODE_INFRA, NM_802_11_AP_FLAGS_PRIVACY, + NM_WIFI_DEVICE_CAP_NONE, NM_WIFI_DEVICE_CAP_NONE, + FALSE, + src, &error); + /* We expect success here; since LEAP APs just set the 'privacy' flag + * there's no way to determine from the AP's beacon whether it's static WEP, + * dynamic WEP, or LEAP. + */ + s_wsec = NULL; + expected = create_expected (ssid, NULL, NM_802_11_MODE_INFRA, TRUE, FALSE, "ieee8021x", "leap", NULL, &s_wsec, NULL); + g_assert (s_wsec); + g_object_set (G_OBJECT (s_wsec), NM_SETTING_WIRELESS_SECURITY_LEAP_USERNAME, leap_username, NULL); + COMPARE (src, expected, success, error, 0, 0); + + g_object_unref (src); +} + +/*******************************************/ + +static void +test_priv_ap_leap_connection_2 (void) +{ + NMConnection *src; + const guint8 bssid[ETH_ALEN] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06 }; + gboolean success; + GError *error = NULL; + + src = nm_connection_new (); + fill_leap (src, NULL, TRUE, TRUE, TRUE); + success = complete_connection ("blahblah", bssid, + NM_802_11_MODE_INFRA, NM_802_11_AP_FLAGS_PRIVACY, + NM_WIFI_DEVICE_CAP_NONE, NM_WIFI_DEVICE_CAP_NONE, + FALSE, + src, &error); + /* We expect failure here, we need a LEAP username */ + COMPARE (src, NULL, success, error, NM_SETTING_WIRELESS_SECURITY_ERROR, NM_SETTING_WIRELESS_SECURITY_ERROR_LEAP_REQUIRES_USERNAME); + + g_object_unref (src); +} + +/*******************************************/ + +static void +test_priv_ap_dynamic_wep_1 (void) +{ + NMConnection *src, *expected; + const char *ssid = "blahblah"; + const guint8 bssid[ETH_ALEN] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06 }; + NMSettingWirelessSecurity *s_wsec; + NMSetting8021x *s_8021x; + gboolean success; + GError *error = NULL; + + src = nm_connection_new (); + s_8021x = (NMSetting8021x *) nm_setting_802_1x_new (); + nm_setting_802_1x_add_eap_method (s_8021x, "peap"); + g_object_set (G_OBJECT (s_8021x), + NM_SETTING_802_1X_IDENTITY, "Bill Smith", + NM_SETTING_802_1X_PHASE2_AUTH, "mschapv2", + NULL); + fill_8021x (src, s_8021x, "ieee8021x", "open", TRUE); + success = complete_connection (ssid, bssid, + NM_802_11_MODE_INFRA, NM_802_11_AP_FLAGS_PRIVACY, + NM_WIFI_DEVICE_CAP_NONE, NM_WIFI_DEVICE_CAP_NONE, + FALSE, + src, &error); + + /* We expect a completed Dynamic WEP connection */ + s_8021x = NULL; + expected = create_expected (ssid, NULL, NM_802_11_MODE_INFRA, TRUE, TRUE, "ieee8021x", "open", NULL, &s_wsec, &s_8021x); + nm_setting_wireless_security_add_pairwise (s_wsec, "wep40"); + nm_setting_wireless_security_add_pairwise (s_wsec, "wep104"); + nm_setting_wireless_security_add_group (s_wsec, "wep40"); + nm_setting_wireless_security_add_group (s_wsec, "wep104"); + nm_setting_802_1x_add_eap_method (s_8021x, "peap"); + g_object_set (G_OBJECT (s_8021x), + NM_SETTING_802_1X_IDENTITY, "Bill Smith", + NM_SETTING_802_1X_PHASE2_AUTH, "mschapv2", + NULL); + + COMPARE (src, expected, success, error, 0, 0); + + g_object_unref (src); +} + +/*******************************************/ + +static void +test_priv_ap_dynamic_wep_2 (void) +{ + NMConnection *src, *expected; + const char *ssid = "blahblah"; + const guint8 bssid[ETH_ALEN] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06 }; + NMSettingWirelessSecurity *s_wsec; + NMSetting8021x *s_8021x; + gboolean success; + GError *error = NULL; + + src = nm_connection_new (); + s_8021x = (NMSetting8021x *) nm_setting_802_1x_new (); + nm_setting_802_1x_add_eap_method (s_8021x, "peap"); + g_object_set (G_OBJECT (s_8021x), + NM_SETTING_802_1X_IDENTITY, "Bill Smith", + NM_SETTING_802_1X_PHASE2_AUTH, "mschapv2", + NULL); + fill_8021x (src, s_8021x, NULL, "open", TRUE); + success = complete_connection (ssid, bssid, + NM_802_11_MODE_INFRA, NM_802_11_AP_FLAGS_PRIVACY, + NM_WIFI_DEVICE_CAP_NONE, NM_WIFI_DEVICE_CAP_NONE, + FALSE, + src, &error); + + /* We expect a completed Dynamic WEP connection */ + s_8021x = NULL; + expected = create_expected (ssid, NULL, NM_802_11_MODE_INFRA, TRUE, TRUE, "ieee8021x", "open", NULL, &s_wsec, &s_8021x); + nm_setting_wireless_security_add_pairwise (s_wsec, "wep40"); + nm_setting_wireless_security_add_pairwise (s_wsec, "wep104"); + nm_setting_wireless_security_add_group (s_wsec, "wep40"); + nm_setting_wireless_security_add_group (s_wsec, "wep104"); + nm_setting_802_1x_add_eap_method (s_8021x, "peap"); + g_object_set (G_OBJECT (s_8021x), + NM_SETTING_802_1X_IDENTITY, "Bill Smith", + NM_SETTING_802_1X_PHASE2_AUTH, "mschapv2", + NULL); + + COMPARE (src, expected, success, error, 0, 0); + + g_object_unref (src); +} + +/*******************************************/ + +static void +test_priv_ap_dynamic_wep_3 (void) +{ + NMConnection *src; + const guint8 bssid[ETH_ALEN] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06 }; + NMSetting8021x *s_8021x; + gboolean success; + GError *error = NULL; + + src = nm_connection_new (); + s_8021x = (NMSetting8021x *) nm_setting_802_1x_new (); + nm_setting_802_1x_add_eap_method (s_8021x, "peap"); + g_object_set (G_OBJECT (s_8021x), + NM_SETTING_802_1X_IDENTITY, "Bill Smith", + NM_SETTING_802_1X_PHASE2_AUTH, "mschapv2", + NULL); + fill_8021x (src, s_8021x, "ieee8021x", "shared", TRUE); + success = complete_connection ("blahblah", bssid, + NM_802_11_MODE_INFRA, NM_802_11_AP_FLAGS_PRIVACY, + NM_WIFI_DEVICE_CAP_NONE, NM_WIFI_DEVICE_CAP_NONE, + FALSE, + src, &error); + /* Expect failure; shared is not compatible with dynamic WEP */ + COMPARE (src, NULL, success, error, NM_SETTING_WIRELESS_SECURITY_ERROR, NM_SETTING_WIRELESS_SECURITY_ERROR_INVALID_PROPERTY); + + g_object_unref (src); +} + +/*******************************************/ + +static void +test_priv_ap_wpa_psk_connection_1 (void) +{ + test_ap_wpa_psk_connection_base (NULL, NULL, + NM_802_11_AP_FLAGS_PRIVACY, + NM_802_11_AP_SEC_NONE, + NM_802_11_AP_SEC_NONE, + FALSE); +} + +static void +test_priv_ap_wpa_psk_connection_2 (void) +{ + test_ap_wpa_psk_connection_base (NULL, NULL, + NM_802_11_AP_FLAGS_PRIVACY, + NM_802_11_AP_SEC_NONE, + NM_802_11_AP_SEC_NONE, + TRUE); +} + +static void +test_priv_ap_wpa_psk_connection_3 (void) +{ + test_ap_wpa_psk_connection_base (NULL, "open", + NM_802_11_AP_FLAGS_PRIVACY, + NM_802_11_AP_SEC_NONE, + NM_802_11_AP_SEC_NONE, + FALSE); +} + +static void +test_priv_ap_wpa_psk_connection_4 (void) +{ + test_ap_wpa_psk_connection_base (NULL, "shared", + NM_802_11_AP_FLAGS_PRIVACY, + NM_802_11_AP_SEC_NONE, + NM_802_11_AP_SEC_NONE, + FALSE); +} + +static void +test_priv_ap_wpa_psk_connection_5 (void) +{ + test_ap_wpa_psk_connection_base ("wpa-psk", "open", + NM_802_11_AP_FLAGS_PRIVACY, + NM_802_11_AP_SEC_NONE, + NM_802_11_AP_SEC_NONE, + FALSE); +} + +/*******************************************/ + +static void +test_priv_ap_wpa_eap_connection_1 (void) +{ + test_ap_wpa_eap_connection_base (NULL, NULL, + NM_802_11_AP_FLAGS_PRIVACY, + NM_802_11_AP_SEC_NONE, + NM_802_11_AP_SEC_NONE, + FALSE, + NM_SETTING_802_1X_ERROR, + NM_SETTING_802_1X_ERROR_MISSING_PROPERTY); +} + +static void +test_priv_ap_wpa_eap_connection_2 (void) +{ + test_ap_wpa_eap_connection_base (NULL, NULL, + NM_802_11_AP_FLAGS_PRIVACY, + NM_802_11_AP_SEC_NONE, + NM_802_11_AP_SEC_NONE, + TRUE, + NM_SETTING_802_1X_ERROR, + NM_SETTING_802_1X_ERROR_MISSING_PROPERTY); +} + +static void +test_priv_ap_wpa_eap_connection_3 (void) +{ + test_ap_wpa_eap_connection_base (NULL, "open", + NM_802_11_AP_FLAGS_PRIVACY, + NM_802_11_AP_SEC_NONE, + NM_802_11_AP_SEC_NONE, + FALSE, + NM_SETTING_802_1X_ERROR, + NM_SETTING_802_1X_ERROR_MISSING_PROPERTY); +} + +static void +test_priv_ap_wpa_eap_connection_4 (void) +{ + test_ap_wpa_eap_connection_base (NULL, "shared", + NM_802_11_AP_FLAGS_PRIVACY, + NM_802_11_AP_SEC_NONE, + NM_802_11_AP_SEC_NONE, + FALSE, + NM_SETTING_WIRELESS_SECURITY_ERROR, + NM_SETTING_WIRELESS_SECURITY_ERROR_INVALID_PROPERTY); +} + +static void +test_priv_ap_wpa_eap_connection_5 (void) +{ + test_ap_wpa_eap_connection_base ("wpa-eap", "open", + NM_802_11_AP_FLAGS_PRIVACY, + NM_802_11_AP_SEC_NONE, + NM_802_11_AP_SEC_NONE, + FALSE, + NM_SETTING_WIRELESS_SECURITY_ERROR, + NM_SETTING_WIRELESS_SECURITY_ERROR_INVALID_PROPERTY); +} + +/*******************************************/ + +#if GLIB_CHECK_VERSION(2,25,12) +typedef GTestFixtureFunc TCFunc; +#else +typedef void (*TCFunc)(void); +#endif + +#define TESTCASE(t, d) g_test_create_case (#t, 0, (gconstpointer) d, NULL, (TCFunc) t, NULL) + +int main (int argc, char **argv) +{ + GTestSuite *suite; + + g_type_init (); + g_test_init (&argc, &argv, NULL); + + suite = g_test_get_root (); + + g_test_suite_add (suite, TESTCASE (test_lock_bssid, NULL)); + + /* Open AP tests; make sure that connections to be completed that have + * various security-related settings already set cause the completion + * to fail. + */ + g_test_suite_add (suite, TESTCASE (test_open_ap_empty_connection, NULL)); + g_test_suite_add (suite, TESTCASE (test_open_ap_leap_connection_1, TRUE)); + g_test_suite_add (suite, TESTCASE (test_open_ap_leap_connection_1, FALSE)); + g_test_suite_add (suite, TESTCASE (test_open_ap_leap_connection_2, NULL)); + g_test_suite_add (suite, TESTCASE (test_open_ap_wep_connection, TRUE)); + g_test_suite_add (suite, TESTCASE (test_open_ap_wep_connection, FALSE)); + + g_test_suite_add (suite, TESTCASE (test_open_ap_wpa_psk_connection_1, NULL)); + g_test_suite_add (suite, TESTCASE (test_open_ap_wpa_psk_connection_2, NULL)); + g_test_suite_add (suite, TESTCASE (test_open_ap_wpa_psk_connection_3, NULL)); + g_test_suite_add (suite, TESTCASE (test_open_ap_wpa_psk_connection_4, NULL)); + g_test_suite_add (suite, TESTCASE (test_open_ap_wpa_psk_connection_5, NULL)); + + g_test_suite_add (suite, TESTCASE (test_open_ap_wpa_eap_connection_1, NULL)); + g_test_suite_add (suite, TESTCASE (test_open_ap_wpa_eap_connection_2, NULL)); + g_test_suite_add (suite, TESTCASE (test_open_ap_wpa_eap_connection_3, NULL)); + g_test_suite_add (suite, TESTCASE (test_open_ap_wpa_eap_connection_4, NULL)); + g_test_suite_add (suite, TESTCASE (test_open_ap_wpa_eap_connection_5, NULL)); + + /* WEP AP tests */ + g_test_suite_add (suite, TESTCASE (test_priv_ap_empty_connection, NULL)); + g_test_suite_add (suite, TESTCASE (test_priv_ap_leap_connection_1, FALSE)); + g_test_suite_add (suite, TESTCASE (test_priv_ap_leap_connection_2, FALSE)); + + g_test_suite_add (suite, TESTCASE (test_priv_ap_dynamic_wep_1, NULL)); + g_test_suite_add (suite, TESTCASE (test_priv_ap_dynamic_wep_2, NULL)); + g_test_suite_add (suite, TESTCASE (test_priv_ap_dynamic_wep_3, NULL)); + + g_test_suite_add (suite, TESTCASE (test_priv_ap_wpa_psk_connection_1, NULL)); + g_test_suite_add (suite, TESTCASE (test_priv_ap_wpa_psk_connection_2, NULL)); + g_test_suite_add (suite, TESTCASE (test_priv_ap_wpa_psk_connection_3, NULL)); + g_test_suite_add (suite, TESTCASE (test_priv_ap_wpa_psk_connection_4, NULL)); + g_test_suite_add (suite, TESTCASE (test_priv_ap_wpa_psk_connection_5, NULL)); + + g_test_suite_add (suite, TESTCASE (test_priv_ap_wpa_eap_connection_1, NULL)); + g_test_suite_add (suite, TESTCASE (test_priv_ap_wpa_eap_connection_2, NULL)); + g_test_suite_add (suite, TESTCASE (test_priv_ap_wpa_eap_connection_3, NULL)); + g_test_suite_add (suite, TESTCASE (test_priv_ap_wpa_eap_connection_4, NULL)); + g_test_suite_add (suite, TESTCASE (test_priv_ap_wpa_eap_connection_5, NULL)); + + return g_test_run (); +} +