Files
NetworkManager/system-settings/plugins/ifnet/wpa_parser.c
Dan Williams 8a6e2b6560 ifnet: constify tons of stuff
Use 'const char *' where appropriate.
2010-10-29 13:56:23 -05:00

560 lines
14 KiB
C

/* -*- 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, const char *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;
const char *ssid, *value;
char *ssid_key;
gboolean is_hex_ssid;
/* Every security information should have a ssid */
ssid = g_hash_table_lookup (security, "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;
const char *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 (const char *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 ();
}
const char *
wpa_get_value (const char *ssid, const char *key)
{
GHashTable *target = g_hash_table_lookup (wsec_table, ssid);
if (target)
return g_hash_table_lookup (target, key);
return NULL;
}
gboolean
exist_ssid (const char *ssid)
{
return g_hash_table_lookup (wsec_table, ssid) != NULL;
}
GHashTable *
_get_hash_table (const char *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 (const char *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 (const char *ssid, const char *key, const char *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 (const char *ssid)
{
return g_hash_table_lookup (wsec_table, ssid) != NULL;
}
gboolean
wpa_add_security (const char *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 (const char *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;
}
}