diff --git a/src/settings/plugins/ifcfg-rh/reader.c b/src/settings/plugins/ifcfg-rh/reader.c index e038f18e13..88fbea89ef 100644 --- a/src/settings/plugins/ifcfg-rh/reader.c +++ b/src/settings/plugins/ifcfg-rh/reader.c @@ -1957,6 +1957,7 @@ parse_wpa_psk (shvarFile *ifcfg, { shvarFile *keys_ifcfg; char *psk = NULL, *p, *hashed = NULL; + size_t plen; gboolean quoted = FALSE; /* Passphrase must be between 10 and 66 characters in length because WPA @@ -1980,8 +1981,10 @@ parse_wpa_psk (shvarFile *ifcfg, return NULL; p = psk; + plen = strlen (p); - if (p[0] == '"' && psk[strlen (psk) - 1] == '"') + if ( (plen >= 2 && (p[0] == '"' || p[0] == '\'') && p[0] == p[plen - 1]) + || (plen >= 3 && p[0] == '$' && p[1] == '\'' && p[1] == p[plen - 1])) quoted = TRUE; if (!quoted && (strlen (psk) == 64)) { @@ -2001,21 +2004,18 @@ parse_wpa_psk (shvarFile *ifcfg, * and between 8 and 63 characters as a passphrase. */ - if (quoted) { - /* Get rid of the quotes */ - p++; - p[strlen (p) - 1] = '\0'; - } + /* Get rid of the quotes */ + hashed = utils_single_unquote_string (p); /* Length check */ - if (strlen (p) < 8 || strlen (p) > 63) { + if (strlen (hashed) < 8 || strlen (hashed) > 63) { g_set_error (error, IFCFG_PLUGIN_ERROR, 0, "Invalid WPA_PSK (passphrases must be between " "8 and 63 characters long (inclusive))"); + g_free (hashed); + hashed = NULL; goto out; } - - hashed = g_strdup (p); } if (!hashed) { diff --git a/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wifi-wpa-psk-2 b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wifi-wpa-psk-2 new file mode 100644 index 0000000000..038b656d2d --- /dev/null +++ b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wifi-wpa-psk-2 @@ -0,0 +1,19 @@ +TYPE=Wireless +DEVICE=eth2 +HWADDR=00:16:41:11:22:33 +NM_CONTROLLED=yes +BOOTPROTO=dhcp +ESSID=ipsum +CHANNEL=6 +MODE=Managed +RATE=auto +ONBOOT=yes +USERCTL=yes +PEERDNS=yes +IPV6INIT=no +CIPHER_PAIRWISE="TKIP CCMP" +CIPHER_GROUP="TKIP CCMP WEP40 WEP104" +KEY_MGMT=WPA-PSK +WPA_ALLOW_WPA=yes +WPA_ALLOW_WPA2=yes + diff --git a/src/settings/plugins/ifcfg-rh/tests/network-scripts/keys-test-wifi-wpa-psk-2 b/src/settings/plugins/ifcfg-rh/tests/network-scripts/keys-test-wifi-wpa-psk-2 new file mode 100644 index 0000000000..bf98da5a1b --- /dev/null +++ b/src/settings/plugins/ifcfg-rh/tests/network-scripts/keys-test-wifi-wpa-psk-2 @@ -0,0 +1,2 @@ +WPA_PSK=$'They\'re really saying I love you. >>`<<' + diff --git a/src/settings/plugins/ifcfg-rh/tests/test-ifcfg-rh.c b/src/settings/plugins/ifcfg-rh/tests/test-ifcfg-rh.c index 67e2825976..5b5c9ca9e0 100644 --- a/src/settings/plugins/ifcfg-rh/tests/test-ifcfg-rh.c +++ b/src/settings/plugins/ifcfg-rh/tests/test-ifcfg-rh.c @@ -4910,6 +4910,111 @@ test_read_wifi_wpa_psk (void) g_object_unref (connection); } +#define TEST_IFCFG_WIFI_WPA_PSK_2 TEST_IFCFG_DIR"/network-scripts/ifcfg-test-wifi-wpa-psk-2" + +static void +test_read_wifi_wpa_psk_2 (void) +{ + NMConnection *connection; + NMSettingConnection *s_con; + NMSettingWireless *s_wireless; + NMSettingWirelessSecurity *s_wsec; + char *unmanaged = NULL; + char *keyfile = NULL; + char *routefile = NULL; + char *route6file = NULL; + gboolean ignore_error = FALSE; + GError *error = NULL; + const char *tmp; + const char *expected_id = "System ipsum (test-wifi-wpa-psk-2)"; + const char *expected_psk = "They're really saying I love you. >>`<<"; + + connection = connection_from_file (TEST_IFCFG_WIFI_WPA_PSK_2, + NULL, + TYPE_WIRELESS, + NULL, + &unmanaged, + &keyfile, + &routefile, + &route6file, + &error, + &ignore_error); + ASSERT (connection != NULL, + "wifi-wpa-psk-2-read", "failed to read %s: %s", TEST_IFCFG_WIFI_WPA_PSK_2, error->message); + + ASSERT (nm_connection_verify (connection, &error), + "wifi-wpa-psk-2-verify", "failed to verify %s: %s", TEST_IFCFG_WIFI_WPA_PSK_2, error->message); + + /* ===== CONNECTION SETTING ===== */ + + s_con = nm_connection_get_setting_connection (connection); + ASSERT (s_con != NULL, + "wifi-wpa-psk-2-verify-connection", "failed to verify %s: missing %s setting", + TEST_IFCFG_WIFI_WPA_PSK_2, + NM_SETTING_CONNECTION_SETTING_NAME); + + /* ID */ + tmp = nm_setting_connection_get_id (s_con); + ASSERT (tmp != NULL, + "wifi-wpa-psk-2-verify-connection", "failed to verify %s: missing %s / %s key", + TEST_IFCFG_WIFI_WPA_PSK_2, + NM_SETTING_CONNECTION_SETTING_NAME, + NM_SETTING_CONNECTION_ID); + ASSERT (strcmp (tmp, expected_id) == 0, + "wifi-wpa-psk-2-verify-connection", "failed to verify %s: unexpected %s / %s key value", + TEST_IFCFG_WIFI_WPA_PSK_2, + NM_SETTING_CONNECTION_SETTING_NAME, + NM_SETTING_CONNECTION_ID); + + /* ===== WIRELESS SETTING ===== */ + + s_wireless = nm_connection_get_setting_wireless (connection); + ASSERT (s_wireless != NULL, + "wifi-wpa-psk-2-verify-wireless", "failed to verify %s: missing %s setting", + TEST_IFCFG_WIFI_WPA_PSK_2, + NM_SETTING_WIRELESS_SETTING_NAME); + + /* Security */ + tmp = nm_setting_wireless_get_security (s_wireless); + ASSERT (tmp != NULL, + "wifi-wpa-psk-2-verify-wireless", "failed to verify %s: missing %s / %s key", + TEST_IFCFG_WIFI_WPA_PSK_2, + NM_SETTING_WIRELESS_SETTING_NAME, + NM_SETTING_WIRELESS_SEC); + ASSERT (strcmp (tmp, NM_SETTING_WIRELESS_SECURITY_SETTING_NAME) == 0, + "wifi-wpa-psk-2-verify-wireless", "failed to verify %s: unexpected %s / %s key value", + TEST_IFCFG_WIFI_WPA_PSK_2, + NM_SETTING_WIRELESS_SETTING_NAME, + NM_SETTING_WIRELESS_SEC); + + /* ===== WIRELESS SECURITY SETTING ===== */ + + s_wsec = nm_connection_get_setting_wireless_security (connection); + ASSERT (s_wsec != NULL, + "wifi-wpa-psk-2-verify-wireless", "failed to verify %s: missing %s setting", + TEST_IFCFG_WIFI_WPA_PSK_2, + NM_SETTING_WIRELESS_SECURITY_SETTING_NAME); + + /* PSK */ + tmp = nm_setting_wireless_security_get_psk (s_wsec); + ASSERT (tmp != NULL, + "wifi-wpa-psk-2-verify-wireless", "failed to verify %s: missing %s / %s key", + TEST_IFCFG_WIFI_WPA_PSK_2, + NM_SETTING_WIRELESS_SECURITY_SETTING_NAME, + NM_SETTING_WIRELESS_SECURITY_PSK); + ASSERT (strcmp (tmp, expected_psk) == 0, + "wifi-wpa-psk-2-verify-wireless", "failed to verify %s: unexpected %s / %s key value", + TEST_IFCFG_WIFI_WPA_PSK_2, + NM_SETTING_WIRELESS_SECURITY_SETTING_NAME, + NM_SETTING_WIRELESS_SECURITY_PSK); + + g_free (unmanaged); + g_free (keyfile); + g_free (routefile); + g_free (route6file); + g_object_unref (connection); +} + #define TEST_IFCFG_WIFI_WPA_PSK_UNQUOTED TEST_IFCFG_DIR"/network-scripts/ifcfg-test-wifi-wpa-psk-unquoted" static void @@ -12678,6 +12783,7 @@ int main (int argc, char **argv) test_read_wifi_leap_secret_flags (TEST_IFCFG_WIFI_LEAP_ALWAYS, NM_SETTING_SECRET_FLAG_AGENT_OWNED | NM_SETTING_SECRET_FLAG_NOT_SAVED); test_read_wifi_wpa_psk (); + test_read_wifi_wpa_psk_2 (); test_read_wifi_wpa_psk_unquoted (); test_read_wifi_wpa_psk_unquoted2 (); test_read_wifi_wpa_psk_adhoc (); @@ -12747,6 +12853,12 @@ int main (int argc, char **argv) TRUE, TRUE, "really insecure passphrase04!"); + test_write_wifi_wpa_psk ("Test Write Wifi WPA WPA2 PSK Passphrase Special Chars", + "wifi-wpa-wpa2-psk-passphrase-write-spec-chars", + FALSE, + TRUE, + TRUE, + "blah`oops\"grr'$*@~!%"); test_write_wifi_wpa_psk_adhoc (); test_write_wifi_wpa_eap_tls (); test_write_wifi_wpa_eap_ttls_tls (); diff --git a/src/settings/plugins/ifcfg-rh/utils.c b/src/settings/plugins/ifcfg-rh/utils.c index eaf1219456..0b7ddf36de 100644 --- a/src/settings/plugins/ifcfg-rh/utils.c +++ b/src/settings/plugins/ifcfg-rh/utils.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. * - * (C) Copyright 2008 - 2010 Red Hat, Inc. + * (C) Copyright 2008 - 2012 Red Hat, Inc. */ #include @@ -115,6 +115,89 @@ utils_hexstr2bin (const char *hex, size_t len) /* End from hostap */ +/* + * utils_single_quote_string + * + * Put string inside single quotes and remove CR, LF characters. If single quote + * is present, escape it with a backslash and prepend the whole string with $ + * in order to have $'string'. That allows us to use single quote inside + * single quotes without breaking bash syntax. (man bash, section QUOTING). + * + * Caller is responsible for freeing the returned string. + */ +char * +utils_single_quote_string (const char *str) +{ + static const char const drop_chars[] = "\r\n"; /* drop CR and LF */ + static const char escape_char = '\\'; /* escape char is backslash */ + static const char quote_char = '\''; /* quote char is single quote */ + size_t i, slen, j = 0; + size_t drop = 0, extra = 0; + char *new_str; + + slen = strlen (str); + for (i = 0; i < slen; i++) { + if (str[i] == quote_char) + extra++; + if (strchr (drop_chars, str[i])) + drop++; + } + new_str = g_malloc0 (slen + extra - drop + 4); /* 4 is for $''\0*/ + if (!new_str) return NULL; + + if (extra > 0) + new_str[j++] = '$'; + new_str[j++] = quote_char; + for (i = 0; i < slen; i++) { + if (strchr (drop_chars, str[i])) + continue; + if (str[i] == quote_char) + new_str[j++] = escape_char; + new_str[j++] = str[i]; + } + new_str[j] = quote_char; + + return new_str; +} + +/* + * utils_single_unquote_string + * + * Remove string from single (or double) quotes, and remove escaping of '. + * Also remove first $ if the string is in the form of $'string'. + * + * Caller is responsible for freeing the returned string. + */ +char * +utils_single_unquote_string (const char *str) +{ + static const char escape_char = '\\'; /* escape char is backslash */ + static const char q_char = '\''; /* quote char is single quote */ + static const char dq_char = '"'; /* double quote char */ + size_t i, slen, j = 0, quote = 0, dollar = 0; + char *new_str; + + slen = strlen (str); + new_str = g_malloc0 (slen + 1); + if (!new_str) return NULL; + + if ( (slen >= 2 && (str[0] == dq_char || str[0] == q_char) && str[0] == str[slen-1]) + || (slen >= 3 && str[0] == '$' && str[1] == q_char && str[1] == str[slen-1])) { + quote = 1; + if (str[0] == '$') dollar = 1; + } + + i = quote + dollar; + while (i < slen - quote) { + if (str[i] == escape_char && str[i+1] == q_char) + i++; + new_str[j++] = str[i++]; + } + new_str[j] = '\0'; + + return new_str; +} + /* * Check ';[a-fA-F0-9]{8}' file suffix used for temporary files by rpm when * installing packages. diff --git a/src/settings/plugins/ifcfg-rh/utils.h b/src/settings/plugins/ifcfg-rh/utils.h index 58dc3c7285..4fe7303b6a 100644 --- a/src/settings/plugins/ifcfg-rh/utils.h +++ b/src/settings/plugins/ifcfg-rh/utils.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. * - * (C) Copyright 2008 - 2009 Red Hat, Inc. + * (C) Copyright 2008 - 2012 Red Hat, Inc. */ #ifndef _UTILS_H_ @@ -30,6 +30,10 @@ char *utils_bin2hexstr (const char *bytes, int len, int final_len); char *utils_hexstr2bin (const char *hex, size_t len); +char *utils_single_quote_string (const char *str); + +char *utils_single_unquote_string (const char *str); + char *utils_cert_path (const char *parent, const char *suffix); const char *utils_get_ifcfg_name (const char *file, gboolean only_ifcfg); diff --git a/src/settings/plugins/ifcfg-rh/writer.c b/src/settings/plugins/ifcfg-rh/writer.c index 4104bd7664..e2aa57ddab 100644 --- a/src/settings/plugins/ifcfg-rh/writer.c +++ b/src/settings/plugins/ifcfg-rh/writer.c @@ -736,24 +736,20 @@ write_wireless_security_setting (NMConnection *connection, /* WPA Passphrase */ if (wpa) { - GString *quoted = NULL; + char *quoted = NULL; psk = nm_setting_wireless_security_get_psk (s_wsec); if (psk && (strlen (psk) != 64)) { /* Quote the PSK since it's a passphrase */ - quoted = g_string_sized_new (strlen (psk) + 2); /* 2 for quotes */ - g_string_append_c (quoted, '"'); - g_string_append (quoted, psk); - g_string_append_c (quoted, '"'); + quoted = utils_single_quote_string (psk); } set_secret (ifcfg, "WPA_PSK", - quoted ? quoted->str : psk, + quoted ? quoted : psk, "WPA_PSK_FLAGS", nm_setting_wireless_security_get_psk_flags (s_wsec), TRUE); - if (quoted) - g_string_free (quoted, TRUE); + g_free (quoted); } else { set_secret (ifcfg, "WPA_PSK",