From 44964d3b5ea376de5da9b4a2399b03f5d298627e Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Thu, 26 Mar 2009 16:57:55 -0400 Subject: [PATCH] ifcfg-rh: initial write support for wifi & wired --- system-settings/plugins/ifcfg-rh/Makefile.am | 4 +- system-settings/plugins/ifcfg-rh/common.h | 23 +- system-settings/plugins/ifcfg-rh/plugin.c | 4 +- system-settings/plugins/ifcfg-rh/reader.c | 373 +++++++---- system-settings/plugins/ifcfg-rh/reader.h | 3 +- system-settings/plugins/ifcfg-rh/shvar.c | 15 +- system-settings/plugins/ifcfg-rh/shvar.h | 10 +- .../tests/network-scripts/Makefile.am | 7 + .../network-scripts/ifcfg-test-wifi-leap | 17 + .../ifcfg-test-wifi-open-ssid-bad-hex | 13 + .../ifcfg-test-wifi-open-ssid-hex | 13 + .../ifcfg-test-wifi-open-ssid-long-hex | 13 + .../ifcfg-test-wifi-open-ssid-long-quoted | 13 + .../ifcfg-test-wifi-open-ssid-quoted | 13 + .../tests/network-scripts/keys-test-wifi-leap | 1 + .../plugins/ifcfg-rh/tests/test-ifcfg-rh.c | 320 ++++++++- system-settings/plugins/ifcfg-rh/utils.c | 195 ++++++ system-settings/plugins/ifcfg-rh/utils.h | 43 ++ system-settings/plugins/ifcfg-rh/writer.c | 609 +++++++++++++++++- 19 files changed, 1537 insertions(+), 152 deletions(-) create mode 100644 system-settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wifi-leap create mode 100644 system-settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wifi-open-ssid-bad-hex create mode 100644 system-settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wifi-open-ssid-hex create mode 100644 system-settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wifi-open-ssid-long-hex create mode 100644 system-settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wifi-open-ssid-long-quoted create mode 100644 system-settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wifi-open-ssid-quoted create mode 100644 system-settings/plugins/ifcfg-rh/tests/network-scripts/keys-test-wifi-leap create mode 100644 system-settings/plugins/ifcfg-rh/utils.c create mode 100644 system-settings/plugins/ifcfg-rh/utils.h diff --git a/system-settings/plugins/ifcfg-rh/Makefile.am b/system-settings/plugins/ifcfg-rh/Makefile.am index 75eec298b..acfc38655 100644 --- a/system-settings/plugins/ifcfg-rh/Makefile.am +++ b/system-settings/plugins/ifcfg-rh/Makefile.am @@ -14,7 +14,9 @@ libifcfg_rh_io_la_SOURCES = \ sha1.c \ sha1.h \ errors.c \ - common.h + common.h \ + utils.c \ + utils.h libifcfg_rh_io_la_CPPFLAGS = \ $(GLIB_CFLAGS) \ diff --git a/system-settings/plugins/ifcfg-rh/common.h b/system-settings/plugins/ifcfg-rh/common.h index 67d3180b3..1569132a9 100644 --- a/system-settings/plugins/ifcfg-rh/common.h +++ b/system-settings/plugins/ifcfg-rh/common.h @@ -21,6 +21,8 @@ #ifndef __COMMON_H__ #define __COMMON_H__ +#include + #define IFCFG_TAG "ifcfg-" #define KEYS_TAG "keys-" #define BAK_TAG ".bak" @@ -33,7 +35,26 @@ #define IFCFG_PLUGIN_NAME "ifcfg-rh" #define IFCFG_PLUGIN_INFO "(c) 2007 - 2008 Red Hat, Inc. To report bugs please use the NetworkManager mailing list." -#include +#define TYPE_ETHERNET "Ethernet" +#define TYPE_WIRELESS "Wireless" + +#define TAG_CA_CERT_PATH "ca-cert-path" +#define TAG_CA_CERT_HASH "ca-cert-hash" + +#define TAG_CLIENT_CERT_PATH "client-cert-path" +#define TAG_CLIENT_CERT_HASH "client-cert-hash" + +#define TAG_PRIVATE_KEY_PATH "private-key-path" +#define TAG_PRIVATE_KEY_HASH "private-key-hash" + +#define TAG_PHASE2_CA_CERT_PATH "phase2-ca-cert-path" +#define TAG_PHASE2_CA_CERT_HASH "phase2-ca-cert-hash" + +#define TAG_PHASE2_CLIENT_CERT_PATH "phase2-client-cert-path" +#define TAG_PHASE2_CLIENT_CERT_HASH "phase2-client-cert-hash" + +#define TAG_PHASE2_PRIVATE_KEY_PATH "phase2-private-key-path" +#define TAG_PHASE2_PRIVATE_KEY_HASH "phase2-private-key-hash" GQuark ifcfg_plugin_error_quark (void); diff --git a/system-settings/plugins/ifcfg-rh/plugin.c b/system-settings/plugins/ifcfg-rh/plugin.c index 023300835..9fb6d4e0e 100644 --- a/system-settings/plugins/ifcfg-rh/plugin.c +++ b/system-settings/plugins/ifcfg-rh/plugin.c @@ -522,7 +522,7 @@ plugin_set_hostname (SCPluginIfcfg *plugin, const char *hostname) return FALSE; } - svSetValue (network, "HOSTNAME", hostname); + svSetValue (network, "HOSTNAME", hostname, FALSE); svWriteFile (network, 0644); svCloseFile (network); @@ -640,7 +640,7 @@ get_property (GObject *object, guint prop_id, g_value_set_string (value, IFCFG_PLUGIN_INFO); break; case NM_SYSTEM_CONFIG_INTERFACE_PROP_CAPABILITIES: - g_value_set_uint (value, NM_SYSTEM_CONFIG_INTERFACE_CAP_MODIFY_HOSTNAME); + g_value_set_uint (value, NM_SYSTEM_CONFIG_INTERFACE_CAP_MODIFY_CONNECTIONS | NM_SYSTEM_CONFIG_INTERFACE_CAP_MODIFY_HOSTNAME); break; case NM_SYSTEM_CONFIG_INTERFACE_PROP_HOSTNAME: g_value_set_string (value, priv->hostname); diff --git a/system-settings/plugins/ifcfg-rh/reader.c b/system-settings/plugins/ifcfg-rh/reader.c index 2cdb78f17..b691cb87e 100644 --- a/system-settings/plugins/ifcfg-rh/reader.c +++ b/system-settings/plugins/ifcfg-rh/reader.c @@ -52,6 +52,7 @@ #include "common.h" #include "shvar.h" #include "sha1.h" +#include "utils.h" #include "reader.h" @@ -90,21 +91,6 @@ static gboolean eap_ttls_reader (const char *eap_method, GError **error); -static char * -get_ifcfg_name (const char *file) -{ - char *ifcfg_name; - char *basename; - - basename = g_path_get_basename (file); - if (!basename) - return NULL; - - ifcfg_name = g_strdup (basename + strlen (IFCFG_TAG)); - g_free (basename); - return ifcfg_name; -} - static gboolean get_int (const char *str, int *value) { @@ -128,7 +114,7 @@ make_connection_setting (const char *file, char *new_id = NULL, *uuid = NULL, *value; char *ifcfg_id; - ifcfg_name = get_ifcfg_name (file); + ifcfg_name = utils_get_ifcfg_name (file); if (!ifcfg_name) return NULL; @@ -279,6 +265,14 @@ make_ip4_setting (shvarFile *ifcfg, const char *network_file, GError **error) NM_SETTING_IP4_CONFIG_NEVER_DEFAULT, never_default, NULL); return NM_SETTING (s_ip4); + } else if (!g_ascii_strcasecmp (value, "shared")) { + g_free (value); + s_ip4 = (NMSettingIP4Config *) nm_setting_ip4_config_new (); + g_object_set (s_ip4, + NM_SETTING_IP4_CONFIG_METHOD, NM_SETTING_IP4_CONFIG_METHOD_SHARED, + NM_SETTING_IP4_CONFIG_NEVER_DEFAULT, never_default, + NULL); + return NM_SETTING (s_ip4); } g_free (value); } else { @@ -443,39 +437,6 @@ error: return NULL; } -/* - * utils_bin2hexstr - * - * Convert a byte-array into a hexadecimal string. - * - * Code originally by Alex Larsson and - * copyright Red Hat, Inc. under terms of the LGPL. - * - */ -static char * -utils_bin2hexstr (const char *bytes, int len, int final_len) -{ - static char hex_digits[] = "0123456789abcdef"; - char * result; - int i; - - g_return_val_if_fail (bytes != NULL, NULL); - g_return_val_if_fail (len > 0, NULL); - g_return_val_if_fail (len < 256, NULL); /* Arbitrary limit */ - - result = g_malloc0 (len * 2 + 1); - for (i = 0; i < len; i++) - { - result[2*i] = hex_digits[(bytes[i] >> 4) & 0xf]; - result[2*i+1] = hex_digits[bytes[i] & 0xf]; - } - /* Cut converted key off at the correct length for this cipher type */ - if (final_len > -1) - result[final_len] = '\0'; - - return result; -} - static gboolean read_mac_address (shvarFile *ifcfg, GByteArray **array, GError **error) { @@ -573,44 +534,6 @@ out: return success; } -static char * -get_keys_file_path (const char *parent) -{ - char *ifcfg_name; - char *keys_file = NULL; - char *tmp = NULL; - - ifcfg_name = get_ifcfg_name (parent); - if (!ifcfg_name) - return NULL; - - tmp = g_path_get_dirname (parent); - if (!tmp) - goto out; - - keys_file = g_strdup_printf ("%s/" KEYS_TAG "%s", tmp, ifcfg_name); - -out: - g_free (tmp); - g_free (ifcfg_name); - return keys_file; -} - -static shvarFile * -get_keys_ifcfg (const char *parent) -{ - shvarFile *ifcfg = NULL; - char *keys_file; - - keys_file = get_keys_file_path (parent); - if (!keys_file) - return NULL; - - ifcfg = svNewFile (keys_file); - g_free (keys_file); - return ifcfg; -} - static gboolean read_wep_keys (shvarFile *ifcfg, guint8 def_idx, @@ -666,7 +589,7 @@ make_wep_setting (shvarFile *ifcfg, goto error; /* Try to get keys from the "shadow" key file */ - keys_ifcfg = get_keys_ifcfg (file); + keys_ifcfg = utils_get_keys_ifcfg (file, FALSE); if (keys_ifcfg) { if (!read_wep_keys (keys_ifcfg, default_key_idx, s_wireless_sec, error)) { svCloseFile (keys_ifcfg); @@ -703,7 +626,7 @@ make_wep_setting (shvarFile *ifcfg, g_object_set (s_wireless_sec, NM_SETTING_WIRELESS_SECURITY_AUTH_ALG, "shared", NULL); } else { g_set_error (error, ifcfg_plugin_error_quark (), 0, - "Invalid WEP authentication algoritm '%s'", + "Invalid WEP authentication algorithm '%s'", lcase); g_free (lcase); goto error; @@ -822,7 +745,7 @@ parse_wpa_psk (shvarFile *ifcfg, */ /* Try to get keys from the "shadow" key file */ - keys_ifcfg = get_keys_ifcfg (file); + keys_ifcfg = utils_get_keys_ifcfg (file, FALSE); if (keys_ifcfg) { psk = svGetValue (keys_ifcfg, "WPA_PSK", TRUE); svCloseFile (keys_ifcfg); @@ -897,7 +820,7 @@ static EAPReader eap_readers[] = { { "chap", eap_simple_reader, TRUE }, { "mschap", eap_simple_reader, TRUE }, { "mschapv2", eap_simple_reader, TRUE }, - { "leap", eap_simple_reader, FALSE }, + { "leap", eap_simple_reader, TRUE }, { "tls", eap_tls_reader, FALSE }, { "peap", eap_peap_reader, FALSE }, { "ttls", eap_ttls_reader, FALSE }, @@ -965,6 +888,21 @@ get_cert_file (const char *ifcfg_path, const char *cert_path) return ret; } +static void +set_file_path (NMSetting8021x *s_8021x, + const char *path_tag, + const char *hash_tag, + const char *path, + const char *setting_key) +{ + GByteArray *data = NULL; + + g_object_set_data_full (G_OBJECT (s_8021x), path_tag, g_strdup (path), g_free); + g_object_get (G_OBJECT (s_8021x), setting_key, &data, NULL); + if (data) + g_object_set_data_full (G_OBJECT (s_8021x), hash_tag, utils_hash_byte_array (data), g_free); +} + static gboolean eap_tls_reader (const char *eap_method, shvarFile *ifcfg, @@ -989,9 +927,19 @@ eap_tls_reader (const char *eap_method, if (phase2) { if (!nm_setting_802_1x_set_phase2_ca_cert_from_file (s_8021x, real_path, NULL, error)) goto done; + set_file_path (s_8021x, + TAG_PHASE2_CA_CERT_PATH, + TAG_PHASE2_CA_CERT_HASH, + real_path, + NM_SETTING_802_1X_PHASE2_CA_CERT); } else { if (!nm_setting_802_1x_set_ca_cert_from_file (s_8021x, real_path, NULL, error)) goto done; + set_file_path (s_8021x, + TAG_CA_CERT_PATH, + TAG_CA_CERT_HASH, + real_path, + NM_SETTING_802_1X_CA_CERT); } } else { PLUGIN_WARN (IFCFG_PLUGIN_NAME, " warning: missing %s for EAP" @@ -1036,9 +984,19 @@ eap_tls_reader (const char *eap_method, if (phase2) { if (!nm_setting_802_1x_set_phase2_private_key_from_file (s_8021x, real_path, privkey_password, &privkey_type, error)) goto done; + set_file_path (s_8021x, + TAG_PHASE2_PRIVATE_KEY_PATH, + TAG_PHASE2_PRIVATE_KEY_HASH, + real_path, + NM_SETTING_802_1X_PHASE2_PRIVATE_KEY); } else { if (!nm_setting_802_1x_set_private_key_from_file (s_8021x, real_path, privkey_password, &privkey_type, error)) goto done; + set_file_path (s_8021x, + TAG_PRIVATE_KEY_PATH, + TAG_PRIVATE_KEY_HASH, + real_path, + NM_SETTING_802_1X_PRIVATE_KEY); } /* Per NM requirements, if the private key is pkcs12, set the client cert to the @@ -1048,9 +1006,19 @@ eap_tls_reader (const char *eap_method, if (phase2) { if (!nm_setting_802_1x_set_phase2_client_cert_from_file (s_8021x, real_path, NULL, error)) goto done; + set_file_path (s_8021x, + TAG_PHASE2_CLIENT_CERT_PATH, + TAG_PHASE2_CLIENT_CERT_HASH, + real_path, + NM_SETTING_802_1X_PHASE2_CLIENT_CERT); } else { if (!nm_setting_802_1x_set_client_cert_from_file (s_8021x, real_path, NULL, error)) goto done; + set_file_path (s_8021x, + TAG_CLIENT_CERT_PATH, + TAG_CLIENT_CERT_HASH, + real_path, + NM_SETTING_802_1X_CLIENT_CERT); } } else { /* Set the private key password if not PKCS#12 */ @@ -1078,9 +1046,19 @@ eap_tls_reader (const char *eap_method, if (phase2) { if (!nm_setting_802_1x_set_phase2_client_cert_from_file (s_8021x, real_path, NULL, error)) goto done; + set_file_path (s_8021x, + TAG_PHASE2_CLIENT_CERT_PATH, + TAG_PHASE2_CLIENT_CERT_HASH, + real_path, + NM_SETTING_802_1X_PHASE2_CLIENT_CERT); } else { if (!nm_setting_802_1x_set_client_cert_from_file (s_8021x, real_path, NULL, error)) goto done; + set_file_path (s_8021x, + TAG_CLIENT_CERT_PATH, + TAG_CLIENT_CERT_HASH, + real_path, + NM_SETTING_802_1X_CLIENT_CERT); } } @@ -1116,6 +1094,11 @@ eap_peap_reader (const char *eap_method, real_cert_path = get_cert_file (ifcfg->fileName, ca_cert); if (!nm_setting_802_1x_set_ca_cert_from_file (s_8021x, real_cert_path, NULL, error)) goto done; + set_file_path (s_8021x, + TAG_CA_CERT_PATH, + TAG_CA_CERT_HASH, + real_cert_path, + NM_SETTING_802_1X_CA_CERT); } else { PLUGIN_WARN (IFCFG_PLUGIN_NAME, " warning: missing " "IEEE_8021X_CA_CERT for EAP method '%s'; this is" @@ -1212,6 +1195,11 @@ eap_ttls_reader (const char *eap_method, real_cert_path = get_cert_file (ifcfg->fileName, ca_cert); if (!nm_setting_802_1x_set_ca_cert_from_file (s_8021x, real_cert_path, NULL, error)) goto done; + set_file_path (s_8021x, + TAG_CA_CERT_PATH, + TAG_CA_CERT_HASH, + real_cert_path, + NM_SETTING_802_1X_CA_CERT); } else { PLUGIN_WARN (IFCFG_PLUGIN_NAME, " warning: missing " "IEEE_8021X_CA_CERT for EAP method '%s'; this is" @@ -1301,7 +1289,7 @@ fill_8021x (shvarFile *ifcfg, s_8021x = (NMSetting8021x *) nm_setting_802_1x_new (); /* Read in the lookaside keys file, if present */ - keys = get_keys_ifcfg (file); + keys = utils_get_keys_ifcfg (file, FALSE); /* Validate and handle each EAP method */ for (iter = list; iter && *iter; iter++) { @@ -1437,6 +1425,64 @@ error: return NULL; } +static NMSetting * +make_leap_setting (shvarFile *ifcfg, + const char *file, + GError **error) +{ + NMSettingWirelessSecurity *wsec; + shvarFile *keys_ifcfg; + char *value; + + wsec = NM_SETTING_WIRELESS_SECURITY (nm_setting_wireless_security_new ()); + + value = svGetValue (ifcfg, "KEY_MGMT", FALSE); + if (!value || strcmp (value, "IEEE8021X")) + goto error; /* Not LEAP */ + + g_free (value); + value = svGetValue (ifcfg, "SECURITYMODE", FALSE); + if (!value || strcasecmp (value, "leap")) + goto error; /* Not LEAP */ + + g_free (value); + + value = svGetValue (ifcfg, "IEEE_8021X_PASSWORD", FALSE); + if (!value) { + /* Try to get keys from the "shadow" key file */ + keys_ifcfg = utils_get_keys_ifcfg (file, FALSE); + if (keys_ifcfg) { + value = svGetValue (keys_ifcfg, "IEEE_8021X_PASSWORD", FALSE); + svCloseFile (keys_ifcfg); + } + } + if (value && strlen (value)) + g_object_set (wsec, NM_SETTING_WIRELESS_SECURITY_LEAP_PASSWORD, value, NULL); + g_free (value); + + value = svGetValue (ifcfg, "IEEE_8021X_IDENTITY", FALSE); + if (!value || !strlen (value)) { + g_set_error (error, ifcfg_plugin_error_quark (), 0, + "Missing LEAP identity"); + goto error; + } + g_object_set (wsec, NM_SETTING_WIRELESS_SECURITY_LEAP_USERNAME, value, NULL); + g_free (value); + + g_object_set (wsec, + NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, "ieee8021x", + NM_SETTING_WIRELESS_SECURITY_AUTH_ALG, "leap", + NULL); + + return (NMSetting *) wsec; + +error: + g_free (value); + if (wsec) + g_object_unref (wsec); + return NULL; +} + static NMSetting * make_wireless_security_setting (shvarFile *ifcfg, const char *file, @@ -1447,6 +1493,14 @@ make_wireless_security_setting (shvarFile *ifcfg, { NMSetting *wsec; + if (!adhoc) { + wsec = make_leap_setting (ifcfg, file, error); + if (wsec) + return wsec; + else if (*error) + return NULL; + } + wsec = make_wpa_setting (ifcfg, file, ssid, adhoc, s_8021x, error); if (wsec) return wsec; @@ -1481,23 +1535,64 @@ make_wireless_setting (shvarFile *ifcfg, return NULL; } - value = svGetValue (ifcfg, "ESSID", FALSE); + value = svGetValue (ifcfg, "ESSID", TRUE); if (value) { - gsize len = strlen (value); + gsize ssid_len = 0, value_len = strlen (value); + char *p = value, *tmp; + gboolean quoted = FALSE; + char buf[33]; - if (len > 32 || len == 0) { + ssid_len = value_len; + if ( (value_len >= 2) + && (value[0] == '"') + && (value[value_len - 1] == '"')) { + /* Strip the quotes and unescape */ + p = value + 1; + value[value_len - 1] = '\0'; + svUnescape (p); + ssid_len = strlen (p); + quoted = TRUE; + } else if ((value_len > 2) && (strncmp (value, "0x", 2) == 0)) { + /* Hex representation */ + if (value_len % 2) { + g_set_error (error, ifcfg_plugin_error_quark (), 0, + "Invalid SSID '%s' size (looks like hex but length not multiple of 2)", + value); + g_free (value); + goto error; + } + + p = value + 2; + while (*p) { + if (!isxdigit (*p)) { + g_set_error (error, ifcfg_plugin_error_quark (), 0, + "Invalid SSID '%s' character (looks like hex SSID but '%c' isn't a hex digit)", + value, *p); + g_free (value); + goto error; + } + p++; + } + + tmp = utils_hexstr2bin (value + 2, value_len - 2); + ssid_len = (value_len - 2) / 2; + memcpy (buf, tmp, ssid_len); + p = &buf[0]; + } + + if (ssid_len > 32 || ssid_len == 0) { g_set_error (error, ifcfg_plugin_error_quark (), 0, "Invalid SSID '%s' (size %zu not between 1 and 32 inclusive)", - value, len); + value, ssid_len); g_free (value); goto error; } - array = g_byte_array_sized_new (strlen (value)); - g_byte_array_append (array, (const guint8 *) value, len); - g_free (value); + array = g_byte_array_sized_new (strlen (p)); + g_byte_array_append (array, (const guint8 *) p, ssid_len); g_object_set (s_wireless, NM_SETTING_WIRELESS_SSID, array, NULL); g_byte_array_free (array, TRUE); + g_free (value); } else { /* Only fail on lack of SSID if device is managed */ if (!unmanaged) { @@ -1506,33 +1601,53 @@ make_wireless_setting (shvarFile *ifcfg, } } - if (!unmanaged) { - value = svGetValue (ifcfg, "MODE", FALSE); - if (value) { - char *lcase; - const char *mode = NULL; + if (unmanaged) + goto done; - lcase = g_ascii_strdown (value, -1); - g_free (value); + value = svGetValue (ifcfg, "MODE", FALSE); + if (value) { + char *lcase; + const char *mode = NULL; - if (!strcmp (lcase, "ad-hoc")) { - mode = "adhoc"; - } else if (!strcmp (lcase, "managed")) { - mode = "infrastructure"; - } else { - g_set_error (error, ifcfg_plugin_error_quark (), 0, - "Invalid mode '%s' (not 'Ad-Hoc' or 'Managed')", - lcase); - g_free (lcase); - goto error; - } + lcase = g_ascii_strdown (value, -1); + g_free (value); + + if (!strcmp (lcase, "ad-hoc")) { + mode = "adhoc"; + } else if (!strcmp (lcase, "managed")) { + mode = "infrastructure"; + } else { + g_set_error (error, ifcfg_plugin_error_quark (), 0, + "Invalid mode '%s' (not 'Ad-Hoc' or 'Managed')", + lcase); g_free (lcase); - - g_object_set (s_wireless, NM_SETTING_WIRELESS_MODE, mode, NULL); + goto error; } - // FIXME: channel/freq, other L2 parameters like RTS + g_free (lcase); + + g_object_set (s_wireless, NM_SETTING_WIRELESS_MODE, mode, NULL); } + value = svGetValue (ifcfg, "BSSID", FALSE); + if (value) { + struct ether_addr *eth; + GByteArray *bssid; + + eth = ether_aton (value); + if (!eth) { + g_set_error (error, ifcfg_plugin_error_quark (), 0, + "Invalid BSSID '%s'", value); + goto error; + } + + bssid = g_byte_array_sized_new (ETH_ALEN); + g_byte_array_append (bssid, eth->ether_addr_octet, ETH_ALEN); + g_object_set (s_wireless, NM_SETTING_WIRELESS_BSSID, bssid, NULL); + g_byte_array_free (bssid, TRUE); + } + // FIXME: channel/freq, other L2 parameters like RTS + +done: return NM_SETTING (s_wireless); error: @@ -1655,8 +1770,10 @@ make_wired_setting (shvarFile *ifcfg, } if (read_mac_address (ifcfg, &mac, error)) { - g_object_set (s_wired, NM_SETTING_WIRED_MAC_ADDRESS, mac, NULL); - g_byte_array_free (mac, TRUE); + if (mac) { + g_object_set (s_wired, NM_SETTING_WIRED_MAC_ADDRESS, mac, NULL); + g_byte_array_free (mac, TRUE); + } } else { g_object_unref (s_wired); s_wired = NULL; @@ -1783,7 +1900,6 @@ connection_from_file (const char *filename, gboolean *ignore_error) { NMConnection *connection = NULL; - NMSettingConnection *s_con; shvarFile *parsed; char *type; char *nmc = NULL; @@ -1799,7 +1915,7 @@ connection_from_file (const char *filename, if (!network_file) network_file = SYSCONFDIR "/sysconfig/network"; - ifcfg_name = get_ifcfg_name (filename); + ifcfg_name = utils_get_ifcfg_name (filename); if (!ifcfg_name) { g_set_error (error, ifcfg_plugin_error_quark (), 0, "Ignoring connection '%s' because it's not an ifcfg file.", filename); @@ -1876,13 +1992,6 @@ connection_from_file (const char *filename, g_free (type); - /* We don't write connections yet */ - if (connection) { - s_con = (NMSettingConnection *) nm_connection_get_setting (connection, NM_TYPE_SETTING_CONNECTION); - if (s_con) - g_object_set (s_con, NM_SETTING_CONNECTION_READ_ONLY, TRUE, NULL); - } - /* Don't bother reading the connection fully if it's unmanaged */ if (!connection || *ignored) goto done; @@ -1901,7 +2010,7 @@ connection_from_file (const char *filename, connection = NULL; } - *keyfile = get_keys_file_path (filename); + *keyfile = utils_get_keys_path (filename); done: svCloseFile (parsed); diff --git a/system-settings/plugins/ifcfg-rh/reader.h b/system-settings/plugins/ifcfg-rh/reader.h index 40794b8b7..1fce39b56 100644 --- a/system-settings/plugins/ifcfg-rh/reader.h +++ b/system-settings/plugins/ifcfg-rh/reader.h @@ -24,8 +24,7 @@ #include #include -#define TYPE_ETHERNET "Ethernet" -#define TYPE_WIRELESS "Wireless" +#include "shvar.h" NMConnection *connection_from_file (const char *filename, const char *network_file, diff --git a/system-settings/plugins/ifcfg-rh/shvar.c b/system-settings/plugins/ifcfg-rh/shvar.c index 429e90e0a..1e15d76ff 100644 --- a/system-settings/plugins/ifcfg-rh/shvar.c +++ b/system-settings/plugins/ifcfg-rh/shvar.c @@ -112,8 +112,8 @@ svCreateFile(const char *name) } /* remove escaped characters in place */ -static void -unescape(char *s) { +void +svUnescape(char *s) { int len, i; len = strlen(s); @@ -142,8 +142,8 @@ unescape(char *s) { */ static const char escapees[] = "\"'\\$~`"; /* must be escaped */ static const char spaces[] = " \t|&;()<>"; /* only require "" */ -static char * -escape(const char *s) { +char * +svEscape(const char *s) { char *new; int i, j, mangle = 0, space = 0; int newlen, slen; @@ -202,7 +202,7 @@ svGetValue(shvarFile *s, const char *key, gboolean verbatim) if (!strncmp(keyString, line, len)) { value = g_strdup(line + len); if (!verbatim) - unescape(value); + svUnescape(value); break; } } @@ -272,7 +272,7 @@ svTrueValue(shvarFile *s, const char *key, int def) * */ void -svSetValue(shvarFile *s, const char *key, const char *value) +svSetValue(shvarFile *s, const char *key, const char *value, gboolean verbatim) { char *newval = NULL, *val1 = NULL, *val2 = NULL; char *keyValue; @@ -281,7 +281,8 @@ svSetValue(shvarFile *s, const char *key, const char *value) g_assert(key); /* value may be NULL */ - if (value) newval = escape(value); + if (value) + newval = verbatim ? g_strdup(value) : svEscape(value); keyValue = g_strdup_printf("%s=%s", key, newval ? newval : ""); val1 = svGetValue(s, key, FALSE); diff --git a/system-settings/plugins/ifcfg-rh/shvar.h b/system-settings/plugins/ifcfg-rh/shvar.h index 24022ddd6..4b650d2a9 100644 --- a/system-settings/plugins/ifcfg-rh/shvar.h +++ b/system-settings/plugins/ifcfg-rh/shvar.h @@ -77,7 +77,7 @@ svTrueValue(shvarFile *s, const char *key, int def); * to the top of the file. */ void -svSetValue(shvarFile *s, const char *key, const char *value); +svSetValue(shvarFile *s, const char *key, const char *value, gboolean verbatim); /* Write the current contents iff modified. Returns -1 on error @@ -95,6 +95,14 @@ svWriteFile(shvarFile *s, int mode); int svCloseFile(shvarFile *s); +/* Return a new escaped string */ +char * +svEscape(const char *s); + +/* Unescape a string in-place */ +void +svUnescape(char *s); + #ifdef __cplusplus } #endif /* __cplusplus */ diff --git a/system-settings/plugins/ifcfg-rh/tests/network-scripts/Makefile.am b/system-settings/plugins/ifcfg-rh/tests/network-scripts/Makefile.am index aef499b80..01532b7c5 100644 --- a/system-settings/plugins/ifcfg-rh/tests/network-scripts/Makefile.am +++ b/system-settings/plugins/ifcfg-rh/tests/network-scripts/Makefile.am @@ -11,12 +11,19 @@ EXTRA_DIST = \ keys-test-wired-8021x-peap-mschapv2 \ ifcfg-test-onboot-no \ ifcfg-test-wifi-open \ + ifcfg-test-wifi-open-ssid-quoted \ + ifcfg-test-wifi-open-ssid-long-quoted \ + ifcfg-test-wifi-open-ssid-hex \ + ifcfg-test-wifi-open-ssid-long-hex \ + ifcfg-test-wifi-open-ssid-bad-hex \ ifcfg-test-wifi-wep \ keys-test-wifi-wep \ ifcfg-test-wifi-wep-adhoc \ keys-test-wifi-wep-adhoc \ ifcfg-test-wifi-wep-eap-ttls-chap \ keys-test-wifi-wep-eap-ttls-chap \ + ifcfg-test-wifi-leap \ + keys-test-wifi-leap \ ifcfg-test-wifi-wpa-psk \ keys-test-wifi-wpa-psk \ ifcfg-test-wifi-wpa-psk-adhoc \ diff --git a/system-settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wifi-leap b/system-settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wifi-leap new file mode 100644 index 000000000..50f4fd31e --- /dev/null +++ b/system-settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wifi-leap @@ -0,0 +1,17 @@ +TYPE=Wireless +DEVICE=eth2 +HWADDR=00:16:41:11:22:33 +NM_CONTROLLED=yes +BOOTPROTO=dhcp +ESSID=blahblah +CHANNEL=1 +MODE=Managed +RATE=auto +ONBOOT=yes +USERCTL=yes +PEERDNS=yes +IPV6INIT=no +KEY_MGMT=IEEE8021X +SECURITYMODE=LEAP +IEEE_8021X_IDENTITY="Bill Smith" + diff --git a/system-settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wifi-open-ssid-bad-hex b/system-settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wifi-open-ssid-bad-hex new file mode 100644 index 000000000..32d633428 --- /dev/null +++ b/system-settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wifi-open-ssid-bad-hex @@ -0,0 +1,13 @@ +TYPE=Wireless +DEVICE=eth2 +HWADDR=00:16:41:11:22:33 +NM_CONTROLLED=yes +BOOTPROTO=dhcp +ESSID=0x626cxx +CHANNEL=1 +MODE=Managed +RATE=auto +ONBOOT=yes +USERCTL=yes +PEERDNS=yes +IPV6INIT=no diff --git a/system-settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wifi-open-ssid-hex b/system-settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wifi-open-ssid-hex new file mode 100644 index 000000000..dcb46da7f --- /dev/null +++ b/system-settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wifi-open-ssid-hex @@ -0,0 +1,13 @@ +TYPE=Wireless +DEVICE=eth2 +HWADDR=00:16:41:11:22:33 +NM_CONTROLLED=yes +BOOTPROTO=dhcp +ESSID=0x626c6168626c6168 +CHANNEL=1 +MODE=Managed +RATE=auto +ONBOOT=yes +USERCTL=yes +PEERDNS=yes +IPV6INIT=no diff --git a/system-settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wifi-open-ssid-long-hex b/system-settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wifi-open-ssid-long-hex new file mode 100644 index 000000000..37bb085b5 --- /dev/null +++ b/system-settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wifi-open-ssid-long-hex @@ -0,0 +1,13 @@ +TYPE=Wireless +DEVICE=eth2 +HWADDR=00:16:41:11:22:33 +NM_CONTROLLED=yes +BOOTPROTO=dhcp +ESSID=0x626c6168626c6168626c6168626c6168626c6168626c6168626c6168626c6168AA +CHANNEL=1 +MODE=Managed +RATE=auto +ONBOOT=yes +USERCTL=yes +PEERDNS=yes +IPV6INIT=no diff --git a/system-settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wifi-open-ssid-long-quoted b/system-settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wifi-open-ssid-long-quoted new file mode 100644 index 000000000..0b46acd66 --- /dev/null +++ b/system-settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wifi-open-ssid-long-quoted @@ -0,0 +1,13 @@ +TYPE=Wireless +DEVICE=eth2 +HWADDR=00:16:41:11:22:33 +NM_CONTROLLED=yes +BOOTPROTO=dhcp +ESSID="foo\"bar\\foo\"bar\\foo\"bar\\foo\"bar\\1" +CHANNEL=1 +MODE=Managed +RATE=auto +ONBOOT=yes +USERCTL=yes +PEERDNS=yes +IPV6INIT=no diff --git a/system-settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wifi-open-ssid-quoted b/system-settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wifi-open-ssid-quoted new file mode 100644 index 000000000..08496bb56 --- /dev/null +++ b/system-settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wifi-open-ssid-quoted @@ -0,0 +1,13 @@ +TYPE=Wireless +DEVICE=eth2 +HWADDR=00:16:41:11:22:33 +NM_CONTROLLED=yes +BOOTPROTO=dhcp +ESSID="foo\"bar\\" +CHANNEL=1 +MODE=Managed +RATE=auto +ONBOOT=yes +USERCTL=yes +PEERDNS=yes +IPV6INIT=no diff --git a/system-settings/plugins/ifcfg-rh/tests/network-scripts/keys-test-wifi-leap b/system-settings/plugins/ifcfg-rh/tests/network-scripts/keys-test-wifi-leap new file mode 100644 index 000000000..fe78177e7 --- /dev/null +++ b/system-settings/plugins/ifcfg-rh/tests/network-scripts/keys-test-wifi-leap @@ -0,0 +1 @@ +IEEE_8021X_PASSWORD="foobarblah" diff --git a/system-settings/plugins/ifcfg-rh/tests/test-ifcfg-rh.c b/system-settings/plugins/ifcfg-rh/tests/test-ifcfg-rh.c index d1f380241..a5aba45ac 100644 --- a/system-settings/plugins/ifcfg-rh/tests/test-ifcfg-rh.c +++ b/system-settings/plugins/ifcfg-rh/tests/test-ifcfg-rh.c @@ -39,6 +39,7 @@ #include "nm-test-helpers.h" +#include "common.h" #include "reader.h" typedef enum { @@ -1191,7 +1192,7 @@ test_read_wired_8021x_peap_mschapv2 (void) #define TEST_IFCFG_WIFI_OPEN TEST_DIR"/network-scripts/ifcfg-test-wifi-open" static void -test_read_wifi_unencrypted (void) +test_read_wifi_open (void) { NMConnection *connection; NMSettingConnection *s_con; @@ -1361,6 +1362,180 @@ test_read_wifi_unencrypted (void) g_object_unref (connection); } +#define TEST_IFCFG_WIFI_OPEN_SSID_HEX TEST_DIR"/network-scripts/ifcfg-test-wifi-open-ssid-hex" + +static void +test_read_wifi_open_ssid_hex (void) +{ + NMConnection *connection; + NMSettingConnection *s_con; + NMSettingWireless *s_wireless; + gboolean unmanaged = FALSE; + char *keyfile = NULL; + gboolean ignore_error = FALSE; + GError *error = NULL; + const char *tmp; + const GByteArray *array; + const char *expected_id = "System blahblah (test-wifi-open-ssid-hex)"; + const char *expected_ssid = "blahblah"; + + connection = connection_from_file (TEST_IFCFG_WIFI_OPEN_SSID_HEX, + NULL, + TYPE_WIRELESS, + &unmanaged, + &keyfile, + &error, + &ignore_error); + ASSERT (connection != NULL, + "wifi-open-ssid-hex-read", "failed to read %s: %s", TEST_IFCFG_WIFI_OPEN_SSID_HEX, error->message); + + ASSERT (nm_connection_verify (connection, &error), + "wifi-open-ssid-hex-verify", "failed to verify %s: %s", TEST_IFCFG_WIFI_OPEN_SSID_HEX, error->message); + + /* ===== CONNECTION SETTING ===== */ + + s_con = NM_SETTING_CONNECTION (nm_connection_get_setting (connection, NM_TYPE_SETTING_CONNECTION)); + ASSERT (s_con != NULL, + "wifi-open-ssid-hex-verify-connection", "failed to verify %s: missing %s setting", + TEST_IFCFG_WIFI_OPEN_SSID_HEX, + NM_SETTING_CONNECTION_SETTING_NAME); + + /* ID */ + tmp = nm_setting_connection_get_id (s_con); + ASSERT (tmp != NULL, + "wifi-open-ssid-hex-verify-connection", "failed to verify %s: missing %s / %s key", + TEST_IFCFG_WIFI_OPEN_SSID_HEX, + NM_SETTING_CONNECTION_SETTING_NAME, + NM_SETTING_CONNECTION_ID); + ASSERT (strcmp (tmp, expected_id) == 0, + "wifi-open-ssid-hex-verify-connection", "failed to verify %s: unexpected %s / %s key value", + TEST_IFCFG_WIFI_OPEN_SSID_HEX, + NM_SETTING_CONNECTION_SETTING_NAME, + NM_SETTING_CONNECTION_ID); + + /* ===== WIRELESS SETTING ===== */ + + s_wireless = NM_SETTING_WIRELESS (nm_connection_get_setting (connection, NM_TYPE_SETTING_WIRELESS)); + ASSERT (s_wireless != NULL, + "wifi-open-ssid-hex-verify-wireless", "failed to verify %s: missing %s setting", + TEST_IFCFG_WIFI_OPEN_SSID_HEX, + NM_SETTING_WIRELESS_SETTING_NAME); + + /* SSID */ + array = nm_setting_wireless_get_ssid (s_wireless); + ASSERT (array != NULL, + "wifi-open-ssid-hex-verify-wireless", "failed to verify %s: missing %s / %s key", + TEST_IFCFG_WIFI_OPEN_SSID_HEX, + NM_SETTING_WIRELESS_SETTING_NAME, + NM_SETTING_WIRELESS_SSID); + ASSERT (array->len == strlen (expected_ssid), + "wifi-open-ssid-hex-verify-wireless", "failed to verify %s: unexpected %s / %s key value length", + TEST_IFCFG_WIFI_OPEN_SSID_HEX, + NM_SETTING_WIRELESS_SETTING_NAME, + NM_SETTING_WIRELESS_SSID); + ASSERT (memcmp (array->data, expected_ssid, strlen (expected_ssid)) == 0, + "wifi-open-ssid-hex-verify-wireless", "failed to verify %s: unexpected %s / %s key value", + TEST_IFCFG_WIFI_OPEN_SSID_HEX, + NM_SETTING_WIRELESS_SETTING_NAME, + NM_SETTING_WIRELESS_SSID); + + g_object_unref (connection); +} + +static void +test_read_wifi_open_ssid_bad (const char *file, const char *test) +{ + NMConnection *connection; + gboolean unmanaged = FALSE; + char *keyfile = NULL; + gboolean ignore_error = FALSE; + GError *error = NULL; + + connection = connection_from_file (file, NULL, TYPE_WIRELESS, &unmanaged, &keyfile, &error, &ignore_error); + ASSERT (connection == NULL, test, "unexpected success reading %s", file); + g_clear_error (&error); +} + +#define TEST_IFCFG_WIFI_OPEN_SSID_QUOTED TEST_DIR"/network-scripts/ifcfg-test-wifi-open-ssid-quoted" + +static void +test_read_wifi_open_ssid_quoted (void) +{ + NMConnection *connection; + NMSettingConnection *s_con; + NMSettingWireless *s_wireless; + gboolean unmanaged = FALSE; + char *keyfile = NULL; + gboolean ignore_error = FALSE; + GError *error = NULL; + const char *tmp; + const GByteArray *array; + const char *expected_id = "System foo\"bar\\ (test-wifi-open-ssid-quoted)"; + const char *expected_ssid = "foo\"bar\\"; + + connection = connection_from_file (TEST_IFCFG_WIFI_OPEN_SSID_QUOTED, + NULL, + TYPE_WIRELESS, + &unmanaged, + &keyfile, + &error, + &ignore_error); + ASSERT (connection != NULL, + "wifi-open-ssid-quoted-read", "failed to read %s: %s", TEST_IFCFG_WIFI_OPEN_SSID_QUOTED, error->message); + + ASSERT (nm_connection_verify (connection, &error), + "wifi-open-ssid-quoted-verify", "failed to verify %s: %s", TEST_IFCFG_WIFI_OPEN_SSID_QUOTED, error->message); + + /* ===== CONNECTION SETTING ===== */ + + s_con = NM_SETTING_CONNECTION (nm_connection_get_setting (connection, NM_TYPE_SETTING_CONNECTION)); + ASSERT (s_con != NULL, + "wifi-open-ssid-quoted-verify-connection", "failed to verify %s: missing %s setting", + TEST_IFCFG_WIFI_OPEN_SSID_QUOTED, + NM_SETTING_CONNECTION_SETTING_NAME); + + /* ID */ + tmp = nm_setting_connection_get_id (s_con); + ASSERT (tmp != NULL, + "wifi-open-ssid-quoted-verify-connection", "failed to verify %s: missing %s / %s key", + TEST_IFCFG_WIFI_OPEN_SSID_QUOTED, + NM_SETTING_CONNECTION_SETTING_NAME, + NM_SETTING_CONNECTION_ID); + ASSERT (strcmp (tmp, expected_id) == 0, + "wifi-open-ssid-quoted-verify-connection", "failed to verify %s: unexpected %s / %s key value", + TEST_IFCFG_WIFI_OPEN_SSID_QUOTED, + NM_SETTING_CONNECTION_SETTING_NAME, + NM_SETTING_CONNECTION_ID); + + /* ===== WIRELESS SETTING ===== */ + + s_wireless = NM_SETTING_WIRELESS (nm_connection_get_setting (connection, NM_TYPE_SETTING_WIRELESS)); + ASSERT (s_wireless != NULL, + "wifi-open-ssid-quoted-verify-wireless", "failed to verify %s: missing %s setting", + TEST_IFCFG_WIFI_OPEN_SSID_QUOTED, + NM_SETTING_WIRELESS_SETTING_NAME); + + /* SSID */ + array = nm_setting_wireless_get_ssid (s_wireless); + ASSERT (array != NULL, + "wifi-open-ssid-quoted-verify-wireless", "failed to verify %s: missing %s / %s key", + TEST_IFCFG_WIFI_OPEN_SSID_QUOTED, + NM_SETTING_WIRELESS_SETTING_NAME, + NM_SETTING_WIRELESS_SSID); + ASSERT (array->len == strlen (expected_ssid), + "wifi-open-ssid-quoted-verify-wireless", "failed to verify %s: unexpected %s / %s key value length", + TEST_IFCFG_WIFI_OPEN_SSID_QUOTED, + NM_SETTING_WIRELESS_SETTING_NAME, + NM_SETTING_WIRELESS_SSID); + ASSERT (memcmp (array->data, expected_ssid, strlen (expected_ssid)) == 0, + "wifi-open-ssid-quoted-verify-wireless", "failed to verify %s: unexpected %s / %s key value", + TEST_IFCFG_WIFI_OPEN_SSID_QUOTED, + NM_SETTING_WIRELESS_SETTING_NAME, + NM_SETTING_WIRELESS_SSID); + + g_object_unref (connection); +} + #define TEST_IFCFG_WIFI_WEP TEST_DIR"/network-scripts/ifcfg-test-wifi-wep" static void @@ -1880,6 +2055,137 @@ test_read_wifi_wep_adhoc (void) g_object_unref (connection); } +#define TEST_IFCFG_WIFI_LEAP TEST_DIR"/network-scripts/ifcfg-test-wifi-leap" + +static void +test_read_wifi_leap (void) +{ + NMConnection *connection; + NMSettingConnection *s_con; + NMSettingWireless *s_wireless; + NMSettingWirelessSecurity *s_wsec; + gboolean unmanaged = FALSE; + char *keyfile = NULL; + gboolean ignore_error = FALSE; + GError *error = NULL; + const char *tmp; + const char *expected_id = "System blahblah (test-wifi-leap)"; + const char *expected_identity = "Bill Smith"; + const char *expected_password = "foobarblah"; + + connection = connection_from_file (TEST_IFCFG_WIFI_LEAP, + NULL, + TYPE_WIRELESS, + &unmanaged, + &keyfile, + &error, + &ignore_error); + ASSERT (connection != NULL, + "wifi-leap-read", "failed to read %s: %s", TEST_IFCFG_WIFI_LEAP, error->message); + + ASSERT (nm_connection_verify (connection, &error), + "wifi-leap-verify", "failed to verify %s: %s", TEST_IFCFG_WIFI_LEAP, error->message); + + /* ===== CONNECTION SETTING ===== */ + + s_con = NM_SETTING_CONNECTION (nm_connection_get_setting (connection, NM_TYPE_SETTING_CONNECTION)); + ASSERT (s_con != NULL, + "wifi-leap-verify-connection", "failed to verify %s: missing %s setting", + TEST_IFCFG_WIFI_LEAP, + NM_SETTING_CONNECTION_SETTING_NAME); + + /* ID */ + tmp = nm_setting_connection_get_id (s_con); + ASSERT (tmp != NULL, + "wifi-leap-verify-connection", "failed to verify %s: missing %s / %s key", + TEST_IFCFG_WIFI_LEAP, + NM_SETTING_CONNECTION_SETTING_NAME, + NM_SETTING_CONNECTION_ID); + ASSERT (strcmp (tmp, expected_id) == 0, + "wifi-leap-verify-connection", "failed to verify %s: unexpected %s / %s key value", + TEST_IFCFG_WIFI_LEAP, + NM_SETTING_CONNECTION_SETTING_NAME, + NM_SETTING_CONNECTION_ID); + + /* ===== WIRELESS SETTING ===== */ + + s_wireless = NM_SETTING_WIRELESS (nm_connection_get_setting (connection, NM_TYPE_SETTING_WIRELESS)); + ASSERT (s_wireless != NULL, + "wifi-leap-verify-wireless", "failed to verify %s: missing %s setting", + TEST_IFCFG_WIFI_LEAP, + NM_SETTING_WIRELESS_SETTING_NAME); + + /* Security */ + tmp = nm_setting_wireless_get_security (s_wireless); + ASSERT (tmp != NULL, + "wifi-leap-verify-wireless", "failed to verify %s: missing %s / %s key", + TEST_IFCFG_WIFI_LEAP, + NM_SETTING_WIRELESS_SETTING_NAME, + NM_SETTING_WIRELESS_SEC); + ASSERT (strcmp (tmp, NM_SETTING_WIRELESS_SECURITY_SETTING_NAME) == 0, + "wifi-leap-verify-wireless", "failed to verify %s: unexpected %s / %s key value", + TEST_IFCFG_WIFI_LEAP, + NM_SETTING_WIRELESS_SETTING_NAME, + NM_SETTING_WIRELESS_SEC); + + + /* ===== WIRELESS SECURITY SETTING ===== */ + + s_wsec = NM_SETTING_WIRELESS_SECURITY (nm_connection_get_setting (connection, NM_TYPE_SETTING_WIRELESS_SECURITY)); + ASSERT (s_wsec != NULL, + "wifi-leap-verify-wireless", "failed to verify %s: missing %s setting", + TEST_IFCFG_WIFI_LEAP, + NM_SETTING_WIRELESS_SECURITY_SETTING_NAME); + + /* Key management */ + ASSERT (strcmp (nm_setting_wireless_security_get_key_mgmt (s_wsec), "ieee8021x") == 0, + "wifi-leap-verify-wireless", "failed to verify %s: missing %s / %s key", + TEST_IFCFG_WIFI_LEAP, + NM_SETTING_WIRELESS_SECURITY_SETTING_NAME, + NM_SETTING_WIRELESS_SECURITY_KEY_MGMT); + + /* WEP Authentication mode */ + tmp = nm_setting_wireless_security_get_auth_alg (s_wsec); + ASSERT (tmp != NULL, + "wifi-leap-verify-wireless", "failed to verify %s: missing %s / %s key", + TEST_IFCFG_WIFI_LEAP, + NM_SETTING_WIRELESS_SECURITY_SETTING_NAME, + NM_SETTING_WIRELESS_SECURITY_AUTH_ALG); + ASSERT (strcmp (tmp, "leap") == 0, + "wifi-leap-verify-wireless", "failed to verify %s: unexpected %s / %s key value", + TEST_IFCFG_WIFI_LEAP, + NM_SETTING_WIRELESS_SECURITY_SETTING_NAME, + NM_SETTING_WIRELESS_SECURITY_AUTH_ALG); + + /* LEAP Username */ + tmp = nm_setting_wireless_security_get_leap_username (s_wsec); + ASSERT (tmp != NULL, + "wifi-leap-verify-wireless", "failed to verify %s: missing %s / %s key", + TEST_IFCFG_WIFI_LEAP, + NM_SETTING_WIRELESS_SECURITY_SETTING_NAME, + NM_SETTING_WIRELESS_SECURITY_LEAP_USERNAME); + ASSERT (strcmp (tmp, expected_identity) == 0, + "wifi-leap-verify-wireless", "failed to verify %s: unexpected %s / %s key value", + TEST_IFCFG_WIFI_LEAP, + NM_SETTING_WIRELESS_SECURITY_SETTING_NAME, + NM_SETTING_WIRELESS_SECURITY_LEAP_USERNAME); + + /* LEAP Password */ + tmp = nm_setting_wireless_security_get_leap_password (s_wsec); + ASSERT (tmp != NULL, + "wifi-leap-verify-wireless", "failed to verify %s: missing %s / %s key", + TEST_IFCFG_WIFI_LEAP, + NM_SETTING_WIRELESS_SECURITY_SETTING_NAME, + NM_SETTING_WIRELESS_SECURITY_LEAP_PASSWORD); + ASSERT (strcmp (tmp, expected_password) == 0, + "wifi-leap-verify-wireless", "failed to verify %s: unexpected %s / %s key value", + TEST_IFCFG_WIFI_LEAP, + NM_SETTING_WIRELESS_SECURITY_SETTING_NAME, + NM_SETTING_WIRELESS_SECURITY_LEAP_PASSWORD); + + g_object_unref (connection); +} + #define TEST_IFCFG_WIFI_WPA_PSK TEST_DIR"/network-scripts/ifcfg-test-wifi-wpa-psk" static void @@ -2951,6 +3257,10 @@ test_read_wifi_wep_eap_ttls_chap (void) g_object_unref (connection); } +#define TEST_IFCFG_WIFI_OPEN_SSID_BAD_HEX TEST_DIR"/network-scripts/ifcfg-test-wifi-open-ssid-bad-hex" +#define TEST_IFCFG_WIFI_OPEN_SSID_LONG_QUOTED TEST_DIR"/network-scripts/ifcfg-test-wifi-open-ssid-long-quoted" +#define TEST_IFCFG_WIFI_OPEN_SSID_LONG_HEX TEST_DIR"/network-scripts/ifcfg-test-wifi-open-ssid-long-hex" + int main (int argc, char **argv) { GError *error = NULL; @@ -2972,9 +3282,15 @@ int main (int argc, char **argv) test_read_wired_never_default (); test_read_onboot_no (); test_read_wired_8021x_peap_mschapv2 (); - test_read_wifi_unencrypted (); + test_read_wifi_open (); + test_read_wifi_open_ssid_hex (); + test_read_wifi_open_ssid_bad (TEST_IFCFG_WIFI_OPEN_SSID_BAD_HEX, "wifi-open-ssid-bad-hex-read"); + test_read_wifi_open_ssid_bad (TEST_IFCFG_WIFI_OPEN_SSID_LONG_HEX, "wifi-open-ssid-long-hex-read"); + test_read_wifi_open_ssid_bad (TEST_IFCFG_WIFI_OPEN_SSID_LONG_QUOTED, "wifi-open-ssid-long-quoted-read"); + test_read_wifi_open_ssid_quoted (); test_read_wifi_wep (); test_read_wifi_wep_adhoc (); + test_read_wifi_leap (); test_read_wifi_wpa_psk (); test_read_wifi_wpa_psk_adhoc (); test_read_wifi_wpa_psk_hex (); diff --git a/system-settings/plugins/ifcfg-rh/utils.c b/system-settings/plugins/ifcfg-rh/utils.c new file mode 100644 index 000000000..addee801f --- /dev/null +++ b/system-settings/plugins/ifcfg-rh/utils.c @@ -0,0 +1,195 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* NetworkManager system settings service + * + * 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 of the License, 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. + * + * (C) Copyright 2008 - 2009 Red Hat, Inc. + */ + +#include +#include +#include +#include "utils.h" +#include "sha1.h" +#include "shvar.h" + +/* + * utils_bin2hexstr + * + * Convert a byte-array into a hexadecimal string. + * + * Code originally by Alex Larsson and + * copyright Red Hat, Inc. under terms of the LGPL. + * + */ +char * +utils_bin2hexstr (const char *bytes, int len, int final_len) +{ + static char hex_digits[] = "0123456789abcdef"; + char * result; + int i; + + g_return_val_if_fail (bytes != NULL, NULL); + g_return_val_if_fail (len > 0, NULL); + g_return_val_if_fail (len < 256, NULL); /* Arbitrary limit */ + + result = g_malloc0 (len * 2 + 1); + for (i = 0; i < len; i++) + { + result[2*i] = hex_digits[(bytes[i] >> 4) & 0xf]; + result[2*i+1] = hex_digits[bytes[i] & 0xf]; + } + /* Cut converted key off at the correct length for this cipher type */ + if (final_len > -1) + result[final_len] = '\0'; + + return result; +} + +/* From hostap, Copyright (c) 2002-2005, Jouni Malinen */ + +static int hex2num (char c) +{ + if (c >= '0' && c <= '9') + return c - '0'; + if (c >= 'a' && c <= 'f') + return c - 'a' + 10; + if (c >= 'A' && c <= 'F') + return c - 'A' + 10; + return -1; +} + +static int hex2byte (const char *hex) +{ + int a, b; + a = hex2num(*hex++); + if (a < 0) + return -1; + b = hex2num(*hex++); + if (b < 0) + return -1; + return (a << 4) | b; +} + +char * +utils_hexstr2bin (const char *hex, size_t len) +{ + size_t i; + int a; + const char * ipos = hex; + char * buf = NULL; + char * opos; + + /* Length must be a multiple of 2 */ + if ((len % 2) != 0) + return NULL; + + opos = buf = g_malloc0 ((len / 2) + 1); + for (i = 0; i < len; i += 2) { + a = hex2byte (ipos); + if (a < 0) { + g_free (buf); + return NULL; + } + *opos++ = a; + ipos += 2; + } + return buf; +} + +/* End from hostap */ + +char * +utils_hash_byte_array (const GByteArray *data) +{ + unsigned char buf[SHA1_MAC_LEN]; + static const char *key = "0123456789abcdefghijklmnopqrstuvwxyz"; + + memset (buf, 0, sizeof (buf)); + sha1_mac ((const unsigned char *) key, strlen (key), (const u_int8_t *) data->data, data->len, &buf[0]); + return utils_bin2hexstr ((const char *) &buf[0], SHA1_MAC_LEN, SHA1_MAC_LEN * 2); +} + +char * +utils_cert_path (const char *parent, const char *prefix, const char *suffix) +{ + char *name, *dir, *path; + + name = utils_get_ifcfg_name (parent); + dir = g_path_get_dirname (parent); + path = g_strdup_printf ("%s/%s-%s.%s", dir, prefix, name, suffix); + g_free (dir); + g_free (name); + return path; +} + +char * +utils_get_ifcfg_name (const char *file) +{ + char *ifcfg_name; + char *basename; + + basename = g_path_get_basename (file); + if (!basename) + return NULL; + + ifcfg_name = g_strdup (basename + strlen (IFCFG_TAG)); + g_free (basename); + return ifcfg_name; +} + +char * +utils_get_keys_path (const char *parent) +{ + char *ifcfg_name; + char *keys_file = NULL; + char *tmp = NULL; + + ifcfg_name = utils_get_ifcfg_name (parent); + if (!ifcfg_name) + return NULL; + + tmp = g_path_get_dirname (parent); + if (!tmp) + goto out; + + keys_file = g_strdup_printf ("%s/" KEYS_TAG "%s", tmp, ifcfg_name); + +out: + g_free (tmp); + g_free (ifcfg_name); + return keys_file; +} + +shvarFile * +utils_get_keys_ifcfg (const char *parent, gboolean should_create) +{ + shvarFile *ifcfg = NULL; + char *path; + + path = utils_get_keys_path (parent); + if (!path) + return NULL; + + if (should_create && !g_file_test (path, G_FILE_TEST_EXISTS)) + ifcfg = svCreateFile (path); + + if (!ifcfg) + ifcfg = svNewFile (path); + + g_free (path); + return ifcfg; +} + diff --git a/system-settings/plugins/ifcfg-rh/utils.h b/system-settings/plugins/ifcfg-rh/utils.h new file mode 100644 index 000000000..e9197b1cb --- /dev/null +++ b/system-settings/plugins/ifcfg-rh/utils.h @@ -0,0 +1,43 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* NetworkManager system settings service + * + * 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 of the License, 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. + * + * (C) Copyright 2008 - 2009 Red Hat, Inc. + */ + +#ifndef _UTILS_H_ +#define _UTILS_H_ + +#include +#include "shvar.h" +#include "common.h" + +char *utils_bin2hexstr (const char *bytes, int len, int final_len); + +char *utils_hexstr2bin (const char *hex, size_t len); + +char *utils_hash_byte_array (const GByteArray *data); + +char *utils_cert_path (const char *parent, const char *prefix, const char *suffix); + +char *utils_get_ifcfg_name (const char *file); + +char *utils_get_keys_path (const char *parent); + +shvarFile *utils_get_keys_ifcfg (const char *parent, gboolean should_create); + +#endif /* _UTILS_H_ */ + diff --git a/system-settings/plugins/ifcfg-rh/writer.c b/system-settings/plugins/ifcfg-rh/writer.c index c45507a37..6f7888c13 100644 --- a/system-settings/plugins/ifcfg-rh/writer.c +++ b/system-settings/plugins/ifcfg-rh/writer.c @@ -20,28 +20,607 @@ #include #include +#include +#include +#include + #include #include #include +#include +#include #include "common.h" #include "shvar.h" +#include "reader.h" #include "writer.h" +#include "utils.h" + +#define PLUGIN_WARN(pname, fmt, args...) \ + { g_warning (" " pname ": " fmt, ##args); } + +static void +set_secret (shvarFile *ifcfg, const char *key, const char *value) +{ + shvarFile *keyfile; + + keyfile = utils_get_keys_ifcfg (ifcfg->fileName, TRUE); + if (!keyfile) { + PLUGIN_WARN (IFCFG_PLUGIN_NAME, " warning: could not create key file for '%s'", + ifcfg->fileName); + goto error; + } + + /* Clear the secret from the actual ifcfg */ + svSetValue (ifcfg, key, NULL, FALSE); + + svSetValue (keyfile, key, value, FALSE); + if (!svWriteFile (keyfile, 0600)) { + svCloseFile (keyfile); + PLUGIN_WARN (IFCFG_PLUGIN_NAME, " warning: could not update key file '%s'", + keyfile->fileName); + goto error; + } + svCloseFile (keyfile); + return; + +error: + /* Try setting the secret in the actual ifcfg */ + svSetValue (ifcfg, key, value, FALSE); +} + +static gboolean +write_cert (NMSetting8021x *s_8021x, + shvarFile *ifcfg, + const char *setting_key, + const char *ifcfg_key, + const char *path_tag, + const char *hash_tag, + const char *prefix, + gboolean is_pkcs12, + gboolean *wrote, + GError **error) +{ + const char *orig_hash, *orig_file; + char *new_hash = NULL, *new_file = NULL; + const GByteArray *cert = NULL; + gboolean success = FALSE; + GError *write_error = NULL; + + *wrote = FALSE; + + g_object_get (G_OBJECT (s_8021x), setting_key, &cert, NULL); + if (!cert) { + svSetValue (ifcfg, ifcfg_key, NULL, FALSE); + return TRUE; + } + + new_hash = utils_hash_byte_array (cert); + if (!new_hash) { + g_set_error (error, ifcfg_plugin_error_quark (), 0, + "Could not hash certificate data for %s / %s", + NM_SETTING_802_1X_SETTING_NAME, setting_key); + return FALSE; + } + + orig_hash = g_object_get_data (G_OBJECT (s_8021x), TAG_CA_CERT_HASH); + orig_file = g_object_get_data (G_OBJECT (s_8021x), TAG_CA_CERT_PATH); + + if (!orig_hash || !orig_file || strcmp (new_hash, orig_hash)) { + /* if the cert data has changed, or there wasn't a cert + * originally, write data out to the standard file. + */ + new_file = utils_cert_path (ifcfg->fileName, prefix, is_pkcs12 ? "p12" : "der"); + if (!new_file) { + g_set_error (error, ifcfg_plugin_error_quark (), 0, + "Could not create file path for %s / %s", + NM_SETTING_802_1X_SETTING_NAME, setting_key); + goto out; + } + + if (!g_file_set_contents (new_file, (const char *) cert->data, cert->len, &write_error)) { + g_set_error (error, ifcfg_plugin_error_quark (), 0, + "Could not write certificate for %s / %s: %s", + NM_SETTING_802_1X_SETTING_NAME, setting_key, + (write_error && write_error->message) ? write_error->message : "(unknown)"); + g_clear_error (&write_error); + goto out; + } + *wrote = TRUE; + + svSetValue (ifcfg, ifcfg_key, new_file, FALSE); + g_object_set_data_full (G_OBJECT (s_8021x), path_tag, new_file, g_free); + new_file = NULL; /* g_object_set_data_full() took ownership */ + + g_object_set_data_full (G_OBJECT (s_8021x), hash_tag, new_hash, g_free); + new_hash = NULL; /* g_object_set_data_full() took ownership */ + } else { + /* cert data hasn't changed */ + svSetValue (ifcfg, ifcfg_key, orig_file, FALSE); + } + success = TRUE; + +out: + g_free (new_hash); + g_free (new_file); + return success; +} + +static gboolean +write_8021x_setting (NMConnection *connection, shvarFile *ifcfg, GError **error) +{ + NMSetting8021x *s_8021x; + const char *value; + char *tmp; + gboolean success = FALSE, is_pkcs12 = FALSE, wrote; + + s_8021x = (NMSetting8021x *) nm_connection_get_setting (connection, NM_TYPE_SETTING_802_1X); + if (!s_8021x) + return TRUE; + + /* EAP method */ + if (nm_setting_802_1x_get_num_eap_methods (s_8021x)) { + value = nm_setting_802_1x_get_eap_method (s_8021x, 0); + if (value) + tmp = g_ascii_strup (value, -1); + } + svSetValue (ifcfg, "IEEE_8021X_EAP_METHODS", tmp ? tmp : NULL, FALSE); + g_free (tmp); + + svSetValue (ifcfg, "IEEE_8021X_IDENTITY", + nm_setting_802_1x_get_identity (s_8021x), + FALSE); + + svSetValue (ifcfg, "IEEE_8021X_ANON_IDENTITY", + nm_setting_802_1x_get_anonymous_identity (s_8021x), + FALSE); + + set_secret (ifcfg, "IEEE_8021X_PASSWORD", nm_setting_802_1x_get_password (s_8021x)); + + /* CA certificate */ + if (!write_cert (s_8021x, ifcfg, + NM_SETTING_802_1X_CA_CERT, + "IEEE_8021X_CA_CERT", + TAG_CA_CERT_PATH, + TAG_CA_CERT_HASH, + "ca-cert", + FALSE, &wrote, + error)) + goto out; + + /* Private key */ + if (nm_setting_802_1x_get_private_key (s_8021x)) { + if (nm_setting_802_1x_get_private_key_type (s_8021x) == NM_SETTING_802_1X_CK_TYPE_PKCS12) + is_pkcs12 = TRUE; + } + if (!write_cert (s_8021x, ifcfg, + NM_SETTING_802_1X_PRIVATE_KEY, + "IEEE_8021X_PRIVATE_KEY", + TAG_PRIVATE_KEY_PATH, + TAG_PRIVATE_KEY_HASH, + "private-key", + is_pkcs12, &wrote, + error)) + goto out; + + if (is_pkcs12) { + svSetValue (ifcfg, "IEEE_8021X_PRIVATE_KEY_PASSWORD", + nm_setting_802_1x_get_private_key_password (s_8021x), FALSE); + } else { + /* Clear the private key password for non-pkcs12 private keys that + * we just wrote out, since it will be unencrypted. + */ + if (wrote) + svSetValue (ifcfg, "IEEE_8021X_PRIVATE_KEY_PASSWORD", NULL, FALSE); + + /* Client certificate */ + if (!write_cert (s_8021x, ifcfg, + NM_SETTING_802_1X_CA_CERT, + "IEEE_8021X_CA_CERT", + TAG_CA_CERT_PATH, + TAG_CA_CERT_HASH, + "ca-cert", + FALSE, &wrote, + error)) + goto out; + } + success = TRUE; + +out: + return success; +} + +static gboolean +write_wireless_security_setting (NMConnection *connection, + shvarFile *ifcfg, + gboolean adhoc, + gboolean *no_8021x, + GError **error) +{ + NMSettingWirelessSecurity *s_wsec; + const char *key_mgmt, *auth_alg, *key, *proto, *cipher, *psk; + gboolean wep = FALSE, wpa = FALSE, first; + char *tmp; + guint32 i, num; + GString *str; + + s_wsec = (NMSettingWirelessSecurity *) nm_connection_get_setting (connection, NM_TYPE_SETTING_WIRELESS_SECURITY); + if (!s_wsec) { + g_set_error (error, ifcfg_plugin_error_quark (), 0, + "Missing '%s' setting", NM_SETTING_WIRELESS_SECURITY_SETTING_NAME); + return FALSE; + } + + key_mgmt = nm_setting_wireless_security_get_key_mgmt (s_wsec); + g_assert (key_mgmt); + + auth_alg = nm_setting_wireless_security_get_auth_alg (s_wsec); + + svSetValue (ifcfg, "DEFAULTKEY", NULL, FALSE); + + if (!strcmp (key_mgmt, "none")) { + wep = TRUE; + *no_8021x = TRUE; + } else if (!strcmp (key_mgmt, "wpa-none") || !strcmp (key_mgmt, "wpa-psk")) { + svSetValue (ifcfg, "KEY_MGMT", "WPA-PSK", FALSE); + wpa = TRUE; + *no_8021x = TRUE; + } else if (!strcmp (key_mgmt, "ieee8021x")) { + svSetValue (ifcfg, "KEY_MGMT", "IEEE8021X", FALSE); + } else if (!strcmp (key_mgmt, "wpa-eap")) { + svSetValue (ifcfg, "KEY_MGMT", "WPA-EAP", FALSE); + wpa = TRUE; + } + + svSetValue (ifcfg, "SECURITYMODE", NULL, FALSE); + if (auth_alg) { + if (!strcmp (auth_alg, "shared")) + svSetValue (ifcfg, "SECURITYMODE", "restricted", FALSE); + else if (!strcmp (auth_alg, "open")) + svSetValue (ifcfg, "SECURITYMODE", "open", FALSE); + else if (!strcmp (auth_alg, "leap")) { + svSetValue (ifcfg, "SECURITYMODE", "leap", FALSE); + svSetValue (ifcfg, "IEEE_8021X_USERNAME", + nm_setting_wireless_security_get_leap_username (s_wsec), + FALSE); + set_secret (ifcfg, "IEEE_8021X_PASSWORD", + nm_setting_wireless_security_get_leap_password (s_wsec)); + *no_8021x = TRUE; + } + } + + if (wep) { + /* Default WEP TX key index */ + tmp = g_strdup_printf ("%d", nm_setting_wireless_security_get_wep_tx_keyidx (s_wsec) + 1); + svSetValue (ifcfg, "DEFAULTKEY", tmp, FALSE); + g_free (tmp); + } + + /* WEP keys */ + set_secret (ifcfg, "KEY", NULL); /* Clear any default key */ + for (i = 0; i < 4; i++) { + key = nm_setting_wireless_security_get_wep_key (s_wsec, i); + tmp = g_strdup_printf ("KEY%d", i + 1); + set_secret (ifcfg, tmp, (wep && key) ? key : NULL); + g_free (tmp); + } + + /* WPA protos */ + svSetValue (ifcfg, "WPA_ALLOW_WPA", NULL, FALSE); + svSetValue (ifcfg, "WPA_ALLOW_WPA2", NULL, FALSE); + num = nm_setting_wireless_security_get_num_protos (s_wsec); + for (i = 0; i < num; i++) { + proto = nm_setting_wireless_security_get_proto (s_wsec, i); + if (proto && !strcmp (proto, "wpa")) + svSetValue (ifcfg, "WPA_ALLOW_WPA", "yes", FALSE); + else if (proto && !strcmp (proto, "rsn")) + svSetValue (ifcfg, "WPA_ALLOW_WPA2", "yes", FALSE); + } + + /* WPA Pairwise ciphers */ + svSetValue (ifcfg, "CIPHER_PAIRWISE", NULL, FALSE); + str = g_string_new (NULL); + num = nm_setting_wireless_security_get_num_pairwise (s_wsec); + for (i = 0, first = TRUE; i < num; i++) { + if (!first) + g_string_append_c (str, ' '); + cipher = nm_setting_wireless_security_get_pairwise (s_wsec, i); + tmp = g_ascii_strup (cipher, -1); + g_string_append (str, tmp); + g_free (tmp); + } + if (strlen (str->str)) + svSetValue (ifcfg, "CIPHER_PAIRWISE", str->str, FALSE); + g_string_free (str, TRUE); + + /* WPA Group ciphers */ + svSetValue (ifcfg, "CIPHER_GROUP", NULL, FALSE); + str = g_string_new (NULL); + num = nm_setting_wireless_security_get_num_groups (s_wsec); + for (i = 0, first = TRUE; i < num; i++) { + if (!first) + g_string_append_c (str, ' '); + cipher = nm_setting_wireless_security_get_group (s_wsec, i); + tmp = g_ascii_strup (cipher, -1); + g_string_append (str, tmp); + g_free (tmp); + } + if (strlen (str->str)) + svSetValue (ifcfg, "CIPHER_GROUP", str->str, FALSE); + g_string_free (str, TRUE); + + /* WPA Passphrase */ + psk = nm_setting_wireless_security_get_psk (s_wsec); + set_secret (ifcfg, "WPA_PSK", (wpa && psk) ? psk : NULL); + + return TRUE; +} + +static gboolean +write_wireless_setting (NMConnection *connection, + shvarFile *ifcfg, + gboolean *no_8021x, + GError **error) +{ + NMSettingWireless *s_wireless; + char *tmp, *tmp2; + const GByteArray *ssid, *mac, *bssid; + const char *mode; + char buf[33]; + guint32 mtu, chan, i; + gboolean adhoc = FALSE, hex_ssid = FALSE; + + s_wireless = (NMSettingWireless *) nm_connection_get_setting (connection, NM_TYPE_SETTING_WIRELESS); + if (!s_wireless) { + g_set_error (error, ifcfg_plugin_error_quark (), 0, + "Missing '%s' setting", NM_SETTING_WIRELESS_SETTING_NAME); + return FALSE; + } + + mac = nm_setting_wireless_get_mac_address (s_wireless); + if (mac) { + tmp = g_strdup_printf ("%02X:%02X:%02X:%02X:%02X:%02X", + mac->data[0], mac->data[1], mac->data[2], + mac->data[3], mac->data[4], mac->data[5]); + svSetValue (ifcfg, "HWADDR", tmp, FALSE); + g_free (tmp); + } + + mtu = nm_setting_wireless_get_mtu (s_wireless); + if (mtu) { + tmp = g_strdup_printf ("%u", mtu); + svSetValue (ifcfg, "MTU", tmp, FALSE); + g_free (tmp); + } + + ssid = nm_setting_wireless_get_ssid (s_wireless); + if (!ssid) { + g_set_error (error, ifcfg_plugin_error_quark (), 0, + "Missing SSID in '%s' setting", NM_SETTING_WIRELESS_SETTING_NAME); + return FALSE; + } + if (!ssid->len || ssid->len > 32) { + g_set_error (error, ifcfg_plugin_error_quark (), 0, + "Invalid SSID in '%s' setting", NM_SETTING_WIRELESS_SETTING_NAME); + return FALSE; + } + + /* If the SSID contains any non-printable characters, we need to use the + * hex notation of the SSID instead. + */ + for (i = 0; i < ssid->len; i++) { + if (!isprint (ssid->data[i])) { + hex_ssid = TRUE; + break; + } + } + + if (hex_ssid) { + GString *str; + + /* Hex SSIDs don't get quoted */ + str = g_string_sized_new (ssid->len * 2 + 3); + g_string_append (str, "0x"); + for (i = 0; i < ssid->len; i++) + g_string_append_printf (str, "%02X", ssid->data[i]); + svSetValue (ifcfg, "ESSID", str->str, TRUE); + g_string_free (str, TRUE); + } else { + /* Printable SSIDs get quoted */ + memset (buf, 0, sizeof (buf)); + memcpy (buf, ssid->data, ssid->len); + tmp2 = svEscape (buf); + tmp = g_strdup_printf ("\"%s\"", tmp2); + svSetValue (ifcfg, "ESSID", tmp, TRUE); + g_free (tmp2); + g_free (tmp); + } + + mode = nm_setting_wireless_get_mode (s_wireless); + if (!mode || !strcmp (mode, "infrastructure")) { + svSetValue (ifcfg, "MODE", "Managed", FALSE); + } else if (!strcmp (mode, "adhoc")) { + svSetValue (ifcfg, "MODE", "Ad-Hoc", FALSE); + adhoc = TRUE; + } else { + g_set_error (error, ifcfg_plugin_error_quark (), 0, + "Invalid mode '%s' in '%s' setting", + mode, NM_SETTING_WIRELESS_SETTING_NAME); + return FALSE; + } + + chan = nm_setting_wireless_get_channel (s_wireless); + if (chan) { + tmp = g_strdup_printf ("%d", chan); + svSetValue (ifcfg, "CHANNEL", tmp, FALSE); + g_free (tmp); + } + + bssid = nm_setting_wireless_get_bssid (s_wireless); + if (bssid) { + tmp = g_strdup_printf ("%02X:%02X:%02X:%02X:%02X:%02X", + bssid->data[0], bssid->data[1], bssid->data[2], + bssid->data[3], bssid->data[4], bssid->data[5]); + svSetValue (ifcfg, "BSSID", tmp, FALSE); + g_free (tmp); + } + // FIXME: channel/freq, other L2 parameters like RTS + + if (nm_setting_wireless_get_security (s_wireless)) { + if (!write_wireless_security_setting (connection, ifcfg, adhoc, no_8021x, error)) + return FALSE; + } + + svSetValue (ifcfg, "TYPE", TYPE_WIRELESS, FALSE); + + return TRUE; +} + +static gboolean +write_wired_setting (NMConnection *connection, shvarFile *ifcfg, GError **error) +{ + NMSettingWired *s_wired; + const GByteArray *mac; + char *tmp; + guint32 mtu; + + s_wired = (NMSettingWired *) nm_connection_get_setting (connection, NM_TYPE_SETTING_WIRED); + if (!s_wired) { + g_set_error (error, ifcfg_plugin_error_quark (), 0, + "Missing '%s' setting", NM_SETTING_WIRED_SETTING_NAME); + return FALSE; + } + + mac = nm_setting_wired_get_mac_address (s_wired); + if (mac) { + tmp = g_strdup_printf ("%02X:%02X:%02X:%02X:%02X:%02X", + mac->data[0], mac->data[1], mac->data[2], + mac->data[3], mac->data[4], mac->data[5]); + svSetValue (ifcfg, "HWADDR", tmp, FALSE); + g_free (tmp); + } + + mtu = nm_setting_wired_get_mtu (s_wired); + if (mtu) { + tmp = g_strdup_printf ("%u", mtu); + svSetValue (ifcfg, "MTU", tmp, FALSE); + g_free (tmp); + } + + svSetValue (ifcfg, "TYPE", TYPE_ETHERNET, FALSE); + + return TRUE; +} static void write_connection_setting (NMSettingConnection *s_con, shvarFile *ifcfg) { char *tmp; - svSetValue (ifcfg, "NAME", nm_setting_connection_get_id (s_con)); - svSetValue (ifcfg, "UUID", nm_setting_connection_get_uuid (s_con)); + svSetValue (ifcfg, "NAME", nm_setting_connection_get_id (s_con), FALSE); + svSetValue (ifcfg, "UUID", nm_setting_connection_get_uuid (s_con), FALSE); svSetValue (ifcfg, "ONBOOT", - nm_setting_connection_get_autoconnect (s_con) ? "yes" : "no"); + nm_setting_connection_get_autoconnect (s_con) ? "yes" : "no", + FALSE); tmp = g_strdup_printf ("%llu", nm_setting_connection_get_timestamp (s_con)); - svSetValue (ifcfg, "LAST_CONNECTION", tmp); + svSetValue (ifcfg, "LAST_CONNECTION", tmp, FALSE); g_free (tmp); } +static gboolean +write_ip4_setting (NMConnection *connection, shvarFile *ifcfg, GError **error) +{ + NMSettingIP4Config *s_ip4; + const char *value; + char *addr_key, *prefix_key, *gw_key, *tmp; + guint32 i, num; + + s_ip4 = (NMSettingIP4Config *) nm_connection_get_setting (connection, NM_TYPE_SETTING_IP4_CONFIG); + if (!s_ip4) { + g_set_error (error, ifcfg_plugin_error_quark (), 0, + "Missing '%s' setting", NM_SETTING_IP4_CONFIG_SETTING_NAME); + return FALSE; + } + + value = nm_setting_ip4_config_get_method (s_ip4); + g_assert (value); + if (!strcmp (value, NM_SETTING_IP4_CONFIG_METHOD_AUTO)) + svSetValue (ifcfg, "BOOTPROTO", "dhcp", FALSE); + else if (!strcmp (value, NM_SETTING_IP4_CONFIG_METHOD_MANUAL)) + svSetValue (ifcfg, "BOOTPROTO", "none", FALSE); + else if (!strcmp (value, NM_SETTING_IP4_CONFIG_METHOD_LINK_LOCAL)) + svSetValue (ifcfg, "BOOTPROTO", "autoip", FALSE); + else if (!strcmp (value, NM_SETTING_IP4_CONFIG_METHOD_SHARED)) + svSetValue (ifcfg, "BOOTPROTO", "shared", FALSE); + + num = nm_setting_ip4_config_get_num_addresses (s_ip4); + for (i = 0; i < 254; i++) { + char buf[INET_ADDRSTRLEN + 1]; + NMIP4Address *addr; + guint32 ip; + + if (i == 0) { + addr_key = g_strdup ("IPADDR"); + prefix_key = g_strdup ("PREFIX"); + gw_key = g_strdup ("GATEWAY"); + } else { + addr_key = g_strdup_printf ("IPADDR%d", i); + prefix_key = g_strdup_printf ("PREFIX%d", i); + gw_key = g_strdup_printf ("GATEWAY%d", i); + } + + if (i >= num) { + svSetValue (ifcfg, addr_key, NULL, FALSE); + svSetValue (ifcfg, prefix_key, NULL, FALSE); + svSetValue (ifcfg, gw_key, NULL, FALSE); + } else { + addr = nm_setting_ip4_config_get_address (s_ip4, i); + + memset (buf, 0, sizeof (buf)); + ip = nm_ip4_address_get_address (addr); + inet_ntop (AF_INET, (const void *) &ip, &buf[0], sizeof (buf)); + svSetValue (ifcfg, addr_key, &buf[0], FALSE); + + tmp = g_strdup_printf ("%u", nm_ip4_address_get_prefix (addr)); + svSetValue (ifcfg, prefix_key, tmp, FALSE); + g_free (tmp); + + if (nm_ip4_address_get_gateway (addr)) { + memset (buf, 0, sizeof (buf)); + ip = nm_ip4_address_get_gateway (addr); + inet_ntop (AF_INET, (const void *) &ip, &buf[0], sizeof (buf)); + svSetValue (ifcfg, gw_key, &buf[0], FALSE); + } else + svSetValue (ifcfg, gw_key, NULL, FALSE); + } + + g_free (addr_key); + g_free (prefix_key); + g_free (gw_key); + } + + num = nm_setting_ip4_config_get_num_dns (s_ip4); + for (i = 0; i < 254; i++) { + char buf[INET_ADDRSTRLEN + 1]; + guint32 ip; + + addr_key = g_strdup_printf ("DNS%d", i); + + if (i >= num) + svSetValue (ifcfg, addr_key, NULL, FALSE); + else { + ip = nm_setting_ip4_config_get_dns (s_ip4, i); + + memset (buf, 0, sizeof (buf)); + inet_ntop (AF_INET, (const void *) &ip, &buf[0], sizeof (buf)); + svSetValue (ifcfg, addr_key, &buf[0], FALSE); + } + g_free (addr_key); + } + + return TRUE; +} + static char * escape_id (const char *id) { @@ -56,6 +635,7 @@ escape_id (const char *id) *p = '-'; else if (*p == '\\') *p = '-'; + p++; } return escaped; @@ -73,6 +653,7 @@ write_connection (NMConnection *connection, shvarFile *ifcfg = NULL; char *ifcfg_name = NULL; const char *type; + gboolean no_8021x = FALSE; s_con = NM_SETTING_CONNECTION (nm_connection_get_setting (connection, NM_TYPE_SETTING_CONNECTION)); if (!s_con) { @@ -108,15 +689,35 @@ write_connection (NMConnection *connection, } if (!strcmp (type, NM_SETTING_WIRED_SETTING_NAME)) { + if (!write_wired_setting (connection, ifcfg, error)) + goto out; } else if (!strcmp (type, NM_SETTING_WIRELESS_SETTING_NAME)) { + if (!write_wireless_setting (connection, ifcfg, &no_8021x, error)) + goto out; } else { g_set_error (error, ifcfg_plugin_error_quark (), 0, "Can't write connection type '%s'", type); goto out; } + if (!no_8021x) { + if (!write_8021x_setting (connection, ifcfg, error)) + goto out; + } + + if (!write_ip4_setting (connection, ifcfg, error)) + goto out; + write_connection_setting (s_con, ifcfg); + if (svWriteFile (ifcfg, 0644)) { + g_set_error (error, ifcfg_plugin_error_quark (), 0, + "Can't write connection '%s'", ifcfg->fileName); + goto out; + } + + svCloseFile (ifcfg); + success = TRUE; out: g_free (ifcfg_name);