diff options
| author | Michael Biebl <biebl@debian.org> | 2010-11-29 19:00:22 +0100 |
|---|---|---|
| committer | Michael Biebl <biebl@debian.org> | 2010-11-29 19:00:22 +0100 |
| commit | a6b89d35fabd9f5934b84d7cde22e9268f92c3d3 (patch) | |
| tree | 9e44755880d5933fcc90c6008d4585a76298a1bd /system-settings/plugins/ifnet/wpa_parser.c | |
| parent | 9fae01fa351805b2903e535736e06971bc0b925e (diff) | |
Imported Upstream version 0.8.2 upstream/0.8.2
Diffstat (limited to 'system-settings/plugins/ifnet/wpa_parser.c')
| -rw-r--r-- | system-settings/plugins/ifnet/wpa_parser.c | 558 |
1 files changed, 558 insertions, 0 deletions
diff --git a/system-settings/plugins/ifnet/wpa_parser.c b/system-settings/plugins/ifnet/wpa_parser.c new file mode 100644 index 00000000..5e94108e --- /dev/null +++ b/system-settings/plugins/ifnet/wpa_parser.c @@ -0,0 +1,558 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * Mu Qiao <qiaomuf@gmail.com> + * + * 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. + * + * Copyright (C) 1999-2010 Gentoo Foundation, Inc. + */ + +#include <string.h> +#include <stdlib.h> +#include <nm-system-config-interface.h> +#include "wpa_parser.h" +#include "net_parser.h" +#include "net_utils.h" + +/* Security information */ +static GHashTable *wsec_table = NULL; + +/* Global information used for writing */ +static GHashTable *wsec_global_table = NULL; + +static gboolean wpa_parser_data_changed = FALSE; + +static long +wpa_get_long (GHashTable * table, gchar * key) +{ + return atol (g_hash_table_lookup (table, key)); +} + +static void +destroy_security (GHashTable * network) +{ + gpointer key, value; + GHashTableIter iter; + + g_return_if_fail (network); + g_hash_table_iter_init (&iter, network); + while (g_hash_table_iter_next (&iter, &key, &value)) { + g_free (key); + g_free (value); + } + + g_hash_table_destroy (network); +} + +static GHashTable * +add_security (GHashTable * security) +{ + GHashTable *oldsecurity; + gchar *ssid = g_hash_table_lookup (security, "ssid"), *ssid_key; + gchar *value; + gboolean is_hex_ssid; + + /* Every security information should have a ssid */ + if (!ssid) { + destroy_security (security); + return NULL; + } + + /* Hex format begins with " */ + is_hex_ssid = (ssid[0] != '"'); + if ((value = g_hash_table_lookup (security, "disabled")) != NULL) { + if (strcmp (value, "1") == 0) + return NULL; + } + + /* Default priority is 1 */ + if (g_hash_table_lookup (security, "priority") == NULL) + g_hash_table_insert (security, g_strdup ("priority"), + g_strdup ("1")); + + oldsecurity = g_hash_table_lookup (wsec_table, ssid); + /* Security with lower priority will be ignored */ + if (oldsecurity != NULL) { + if (wpa_get_long (oldsecurity, "priority") >= + wpa_get_long (security, "priority")) { + destroy_security (security); + return NULL; + } else { + g_hash_table_remove (wsec_table, ssid); + destroy_security (oldsecurity); + } + } + + /* format ssid */ + ssid_key = + is_hex_ssid ? g_strdup_printf ("0x%s", + ssid) : + strip_string (g_strdup (ssid), '"'); + g_hash_table_insert (wsec_table, ssid_key, security); + return security; +} + +static void +add_key_value (GHashTable * network, gchar * line) +{ + gchar **key_value; + + if (g_str_has_prefix (line, "network={")) + line += 9; + strip_string (line, '{'); + strip_string (line, '}'); + if (line[0] == '\0') + return; + key_value = g_strsplit (line, "=", 2); + if (g_strv_length (key_value) != 2) { + g_strfreev (key_value); + return; + } + g_strstrip (key_value[0]); + g_strstrip (key_value[1]); + + /* Reserve quotes for psk, wep_key, ssid + * Quotes will determine whether they are hex format */ + if (strcmp (key_value[0], "psk") != 0 + && !g_str_has_prefix (key_value[0], "wep_key") + && strcmp (key_value[0], "ssid") != 0) + strip_string (key_value[1], '"'); + g_hash_table_insert (network, g_strdup (key_value[0]), + g_strdup (key_value[1])); + g_strfreev (key_value); +} + +static void +add_one_wep_key (GHashTable * table, int key_num, gchar * one_wep_key) +{ + if (one_wep_key[0] == 's') { + //asc key + g_hash_table_insert (table, + g_strdup_printf ("wep_key%d", key_num - 1), + g_strdup_printf ("\"%s\"", + one_wep_key + 2)); + } else { + gchar buf[30]; + int i = 0, j = 0; + + //hex key + while (one_wep_key[i] != '\0') { + if (one_wep_key[i] != '-') + buf[j++] = one_wep_key[i]; + i++; + } + buf[j] = '\0'; + g_hash_table_insert (table, + g_strdup_printf ("wep_key%d", key_num - 1), + g_strdup (buf)); + + } +} + +/* Reading wep security information from /etc/conf.d/net. + * This should not be used in futre, use wpa_supplicant instead. */ +static void +add_keys_from_net () +{ + GList *names = ifnet_get_connection_names (); + GList *iter = names; + gchar *wep_keys = "(\\[([1-4])\\]\\s+(s:\\w{5}|s:\\w{13}|" + "([\\da-fA-F]{4}\\-){2}[\\da-fA-F]{2}|" + "([\\da-fA-F]{4}\\-){6}[\\da-fA-F]{2})\\s+)"; + gchar *key_method = + "\\s+key\\s+\\[([1-4])\\]\\s+enc\\s+(open|restricted)"; + GRegex *regex_keys = g_regex_new (wep_keys, 0, 0, NULL); + GRegex *regex_method = g_regex_new (key_method, 0, 0, NULL); + GMatchInfo *keys_info; + GMatchInfo *method_info; + + while (iter) { + gchar *conn_name = iter->data; + GHashTable *table; + gchar *key_str; + + if ((key_str = ifnet_get_data (conn_name, "key")) == NULL) { + iter = g_list_next (iter); + continue; + } + + wpa_add_security (conn_name); + table = _get_hash_table (conn_name); + /* Give lowest priority */ + wpa_set_data (conn_name, "priority", "0"); + g_regex_match (regex_keys, key_str, 0, &keys_info); + /* add wep keys */ + while (g_match_info_matches (keys_info)) { + gchar *key_num = g_match_info_fetch (keys_info, 2); + gchar *one_wep_key = g_match_info_fetch (keys_info, 3); + + add_one_wep_key (table, atoi (key_num), one_wep_key); + g_free (key_num); + g_free (one_wep_key); + g_match_info_next (keys_info, NULL); + } + g_match_info_free (keys_info); + + g_regex_match (regex_method, key_str, 0, &method_info); + /* set default key index and auth alg */ + if (g_match_info_matches (method_info)) { + gchar *default_idx = + g_match_info_fetch (method_info, 1); + gchar *method = g_match_info_fetch (method_info, 2); + + default_idx[0]--; + g_hash_table_insert (table, g_strdup ("wep_tx_keyidx"), + default_idx); + g_hash_table_insert (table, g_strdup ("auth_alg"), + g_ascii_strup (method, -1)); + } + g_match_info_free (method_info); + add_security (table); + iter = g_list_next (iter); + } + g_list_free (names); + g_regex_unref (regex_keys); + g_regex_unref (regex_method); +} + +static void +add_global_data (gchar * line) +{ + gchar **key_value; + + g_strstrip (line); + key_value = g_strsplit (line, "=", 2); + if (g_strv_length (key_value) != 2) { + PLUGIN_WARN (IFNET_PLUGIN_NAME, "Can't handle this line: %s\n", + line); + g_strfreev (key_value); + return; + } + g_hash_table_insert (wsec_global_table, + g_strdup (g_strstrip (key_value[0])), + g_strdup (g_strstrip (key_value[1]))); + g_strfreev (key_value); +} + +void +wpa_parser_init (gchar * wpa_supplicant_conf) +{ + GIOChannel *channel = NULL; + gchar *line; + gboolean complete = FALSE; + + wpa_parser_data_changed = FALSE; + wsec_table = g_hash_table_new (g_str_hash, g_str_equal); + wsec_global_table = g_hash_table_new (g_str_hash, g_str_equal); + + if (g_file_test (wpa_supplicant_conf, G_FILE_TEST_IS_REGULAR)) + channel = + g_io_channel_new_file (wpa_supplicant_conf, "r", NULL); + if (channel == NULL) { + PLUGIN_WARN (IFNET_PLUGIN_NAME, + "Can't open %s for wireless security", + wpa_supplicant_conf); + return; + } + + while (g_io_channel_read_line (channel, &line, NULL, NULL, NULL) + != G_IO_STATUS_EOF) { + g_strstrip (line); + if (line[0] != '#' && line[0] != '\0') { + if (strstr (line, "network={") == NULL) { + add_global_data (line); + g_free (line); + continue; + } else { + GHashTable *network = + g_hash_table_new (g_str_hash, g_str_equal); + gchar *tmp; + + do { + if (line[0] == '#' || line[0] == '\0') { + g_free (line); + continue; + } + /* ignore inline comments */ + if ((tmp = strchr (line, '#')) != NULL) + *tmp = '\0'; + if (strstr (line, "}") != NULL) + complete = TRUE; + add_key_value (network, line); + g_free (line); + } while (complete == FALSE + && + g_io_channel_read_line + (channel, &line, NULL, + NULL, NULL) != G_IO_STATUS_EOF); + add_security (network); + //EOF in inner loop + if (complete == FALSE) { + g_free (line); + break; + } + complete = FALSE; + } + } else + g_free (line); + } + + g_io_channel_shutdown (channel, FALSE, NULL); + g_io_channel_unref (channel); + + add_keys_from_net (); +} + +gchar * +wpa_get_value (gchar * ssid, gchar * key) +{ + GHashTable *target = g_hash_table_lookup (wsec_table, ssid); + + if (target) + return g_hash_table_lookup (target, key); + return NULL; +} + +gboolean +exist_ssid (gchar * ssid) +{ + return g_hash_table_lookup (wsec_table, ssid) != NULL; +} + +GHashTable * +_get_hash_table (gchar * ssid) +{ + return g_hash_table_lookup (wsec_table, ssid); +} + +static gchar *quoted_keys[] = + { "identity", "cert", "private", "phase", "password", NULL }; + +/* tell whether the key needs quotes when writing is performed */ +static gboolean +need_quote (gchar * key) +{ + int i = 0; + + while (quoted_keys[i] != NULL) { + if (strstr (key, quoted_keys[i])) + return TRUE; + i++; + } + return FALSE; +} + +gboolean +wpa_flush_to_file (gchar * config_file) +{ + GIOChannel *channel; + GError **error = NULL; + gpointer key, value, ssid, security; + GHashTableIter iter, iter_security; + gchar *out_line; + gsize bytes_written; + gboolean result = FALSE; + + if (!wpa_parser_data_changed) + return FALSE; + if (!wsec_table || !wsec_global_table) + return FALSE; + + channel = g_io_channel_new_file (config_file, "w", NULL); + if (!channel) { + PLUGIN_WARN (IFNET_PLUGIN_NAME, + "Can't open file %s for writing", config_file); + return FALSE; + } + g_hash_table_iter_init (&iter, wsec_global_table); + PLUGIN_PRINT (IFNET_PLUGIN_NAME, "Writing to %s", config_file); + g_io_channel_write_chars (channel, + "#Generated by NetworkManager\n" + "###### Global Configuration ######\n", + -1, &bytes_written, error); + + /* Writing global information */ + while (g_hash_table_iter_next (&iter, &key, &value)) { + out_line = + g_strdup_printf ("%s=%s\n", (gchar *) key, (gchar *) value); + g_io_channel_write_chars (channel, out_line, -1, &bytes_written, + error); + if (bytes_written == 0 || (error && *error)) + break; + g_free (out_line); + } + if (error && *error) { + PLUGIN_WARN (IFNET_PLUGIN_NAME, "Found error: %s", + (*error)->message); + goto done; + } + g_io_channel_write_chars (channel, + "\n###### Security Configuration ######\n", + -1, &bytes_written, error); + + g_hash_table_iter_init (&iter, wsec_table); + /* Writing security */ + while (g_hash_table_iter_next (&iter, &ssid, &security)) { + g_hash_table_iter_init (&iter_security, + (GHashTable *) security); + g_io_channel_write_chars (channel, "network={\n", -1, + &bytes_written, error); + while (g_hash_table_iter_next (&iter_security, &key, &value)) { + out_line = + g_strdup_printf (need_quote ((gchar *) key) ? + "\t%s=\"%s\"\n" : "\t%s=%s\n", + (gchar *) key, (gchar *) value); + g_io_channel_write_chars (channel, out_line, -1, + &bytes_written, error); + if (bytes_written == 0 || (error && *error)) + break; + g_free (out_line); + } + g_io_channel_write_chars (channel, + "}\n\n", -1, &bytes_written, error); + + } + if (error && *error) { + PLUGIN_WARN (IFNET_PLUGIN_NAME, "Found error: %s", + (*error)->message); + goto done; + } + g_io_channel_flush (channel, error); + + if (error && *error) { + PLUGIN_WARN (IFNET_PLUGIN_NAME, "Found error: %s", + (*error)->message); + goto done; + } + wpa_parser_data_changed = FALSE; + result = TRUE; + done: + g_io_channel_shutdown (channel, FALSE, NULL); + g_io_channel_unref (channel); + return result; +} + +/* If value is NULL, this method will delete old key value pair */ +void +wpa_set_data (gchar * ssid, gchar * key, gchar * value) +{ + gpointer orig_key = NULL, orig_value = NULL; + GHashTable *security = g_hash_table_lookup (wsec_table, ssid); + + g_return_if_fail (security != NULL); + + /* Remove old key value pairs */ + if (g_hash_table_lookup_extended + (security, key, &orig_key, &orig_value)) { + g_hash_table_remove (security, orig_key); + g_free (orig_key); + g_free (orig_value); + } + + /* Add new key value */ + if (value) { + gchar *new_value = g_strdup (value); + + if (strcmp (key, "ssid") != 0 && strcmp (key, "psk") != 0 + && !g_str_has_prefix (key, "wep_key")) + strip_string (new_value, '"'); + g_hash_table_insert (security, g_strdup (key), new_value); + } + wpa_parser_data_changed = TRUE; +} + +gboolean +wpa_has_security (gchar * ssid) +{ + return g_hash_table_lookup (wsec_table, ssid) != NULL; +} + +gboolean +wpa_add_security (gchar * ssid) +{ + if (wpa_has_security (ssid)) + return FALSE; + else { + GHashTable *security = + g_hash_table_new (g_str_hash, g_str_equal); + gchar *ssid_i; + + PLUGIN_PRINT (IFNET_PLUGIN_NAME, "Adding security for %s", + ssid); + if (g_str_has_prefix (ssid, "0x")) { + /* hex ssid */ + ssid_i = g_strdup (ssid + 2); + } else { + /* ascii ssid requires quotes */ + ssid_i = g_strdup_printf ("\"%s\"", ssid); + } + g_hash_table_insert (security, strdup ("ssid"), ssid_i); + g_hash_table_insert (security, strdup ("priority"), + strdup ("1")); + g_hash_table_insert (wsec_table, g_strdup (ssid), security); + wpa_parser_data_changed = TRUE; + return TRUE; + } +} + +gboolean +wpa_delete_security (gchar * ssid) +{ + gpointer orig_key, orig_value; + + g_return_val_if_fail (wsec_table != NULL && ssid != NULL, FALSE); + PLUGIN_PRINT (IFNET_PLUGIN_NAME, "Deleting security for %s", ssid); + if (!g_hash_table_lookup_extended + (wsec_table, ssid, &orig_key, &orig_value)) + return FALSE; + g_hash_table_remove (wsec_table, orig_key); + g_free (orig_key); + destroy_security ((GHashTable *) orig_value); + wpa_parser_data_changed = TRUE; + return TRUE; + +} + +void +wpa_parser_destroy (void) +{ + GHashTableIter iter; + gpointer key; + gpointer value; + + /* Destroy security */ + if (wsec_table) { + g_hash_table_iter_init (&iter, wsec_table); + while (g_hash_table_iter_next (&iter, &key, &value)) { + destroy_security ((GHashTable *) value); + g_free (key); + } + + g_hash_table_destroy (wsec_table); + wsec_table = NULL; + } + + /* Destroy global data */ + if (wsec_global_table) { + g_hash_table_iter_init (&iter, wsec_global_table); + while (g_hash_table_iter_next (&iter, &key, &value)) { + g_free (key); + g_free (value); + } + + g_hash_table_destroy (wsec_global_table); + wsec_global_table = NULL; + } +} |