NetworkManager r3673 - in trunk: . libnm-util po system-settings/plugins/ifcfg-suse



Author: tambeti
Date: Mon May 19 07:43:13 2008
New Revision: 3673
URL: http://svn.gnome.org/viewvc/NetworkManager?rev=3673&view=rev

Log:
2008-05-15  Tambet Ingo  <tambet gmail com>

	Move crypto functions from nm-applet to libnm-util.

	* libnm-util/nm-setting-8021x.c (nm_setting_802_1x_set_ca_cert)
	(nm_setting_802_1x_set_client_cert)
	(nm_setting_802_1x_set_phase2_ca_cert)
	(nm_setting_802_1x_set_phase2_client_cert)
	(nm_setting_802_1x_set_private_key)
	(nm_setting_802_1x_set_phase2_private_key): Implement. Given a certificate
	file (or private key and it's password), read the certificate data.

	* libnm-util/crypto_nss.c:
	* libnm-util/crypto_gnutls.c:
	* libnm-util/crypto.[ch]: Move here from nm-applet.

	* configure.in: Check for NSS and gnutls here (moved here from nm-applet).

	* system-settings/plugins/ifcfg-suse/parser.c (read_wpa_eap_settings):
	Imlement WPA-EAP configuration reading from sysconfig.


Added:
   trunk/libnm-util/crypto.c
   trunk/libnm-util/crypto.h
   trunk/libnm-util/crypto_gnutls.c
   trunk/libnm-util/crypto_nss.c
   trunk/libnm-util/test-crypto.c
Modified:
   trunk/ChangeLog
   trunk/configure.in
   trunk/libnm-util/Makefile.am
   trunk/libnm-util/nm-setting-8021x.c
   trunk/libnm-util/nm-setting-8021x.h
   trunk/po/POTFILES.in
   trunk/system-settings/plugins/ifcfg-suse/parser.c

Modified: trunk/configure.in
==============================================================================
--- trunk/configure.in	(original)
+++ trunk/configure.in	Mon May 19 07:43:13 2008
@@ -261,6 +261,44 @@
 
 AC_PATH_PROG([POLKIT_POLICY_FILE_VALIDATE], [polkit-policy-file-validate], [false])
 
+AC_ARG_WITH(nss, AC_HELP_STRING([--with-nss], [Use NSS library for certificate and key operations]), ac_nss=$withval, ac_nss=auto)
+if test x"$ac_nss" != xno; then
+  PKG_CHECK_MODULES(NSS, [nss >= 3.11])
+  AC_DEFINE(HAVE_NSS, 1, [Define if you have NSS])
+fi
+AM_CONDITIONAL(WITH_NSS, test x"$ac_nss" != xno)
+
+AC_ARG_WITH(gnutls, AC_HELP_STRING([--with-gnutls], [Use gnutls and gcrypt libraries for certificate and key operations]), ac_gnutls=$withval, ac_gnutls=no)
+if test x"$ac_gnutls" != xno; then
+  PKG_CHECK_MODULES(GNUTLS, [gnutls >= 1.2])
+  AC_PATH_PROG(LIBGCRYPT_CONFIG, libgcrypt-config, no)
+else
+  LIBGCRYPT_CONFIG=no
+fi 
+if test x"$LIBGCRYPT_CONFIG" = xno; then
+   if test x"$ac_gnutls" = xyes; then
+      AC_MSG_ERROR([gnutls explicitly requested but gcrypt not found on system])
+   fi
+   ac_gnutls=no
+else
+   if test x"$ac_gnutls" != xno; then
+     AC_DEFINE(HAVE_GNUTLS, 1, [Define if you have libgnutls])
+     LIBGCRYPT_CFLAGS=`$LIBGCRYPT_CONFIG --cflags`
+     LIBGCRYPT_LIBS=`$LIBGCRYPT_CONFIG --libs`
+     AC_SUBST(LIBGCRYPT_CFLAGS)
+     AC_SUBST(LIBGCRYPT_LIBS)
+   fi
+fi
+AM_CONDITIONAL(WITH_GNUTLS, test x"$ac_gnutls" != xno)
+
+if test x"$ac_nss" = xno -a x"$ac_gnutls" = xno; then
+  AC_MSG_ERROR([Please choose either NSS or gnutls for certificate and key operations])
+fi
+
+if test x"$ac_nss" = xyes -a x"$ac_gnutls" = xyes; then
+  AC_MSG_ERROR([Please choose _one_ of NSS or gnutls for certificate and key operations])
+fi
+
 GLIB_GENMARSHAL=`pkg-config --variable=glib_genmarshal glib-2.0`
 AC_SUBST(GLIB_GENMARSHAL)
 

Modified: trunk/libnm-util/Makefile.am
==============================================================================
--- trunk/libnm-util/Makefile.am	(original)
+++ trunk/libnm-util/Makefile.am	Mon May 19 07:43:13 2008
@@ -28,6 +28,8 @@
 	nm-utils.h
 
 libnm_util_la_SOURCES=			\
+	crypto.c			\
+	crypto.h			\
 	nm-connection.c			\
 	nm-param-spec-specialized.c	\
 	nm-setting.c			\
@@ -51,8 +53,28 @@
 
 libnm_util_la_CFLAGS=-fPIC
 
+libnm_util_la_LIBADD = 
+
+if WITH_GNUTLS
+libnm_util_la_SOURCES += crypto_gnutls.c
+libnm_util_la_CPPFLAGS += $(LIBGCRYPT_CFLAGS) $(GNUTLS_CFLAGS)
+libnm_util_la_LIBADD += $(LIBGCRYPT_LIBS) $(GNUTLS_LIBS)
+endif
+
+if WITH_NSS
+libnm_util_la_SOURCES += crypto_nss.c
+libnm_util_la_CPPFLAGS += $(NSS_CFLAGS)
+libnm_util_la_LIBADD += $(NSS_LIBS)
+endif
+
 libnm_util_includedir=$(includedir)/NetworkManager
 
+noinst_PROGRAMS = test-crypto
+
+test_crypto_SOURCES = test-crypto.c
+test_crypto_CPPFLAGS = $(GLIB_CFLAGS) -D_GNU_SOURCE
+test_crypto_LDADD = $(GLIB_LIBS) ${top_builddir}/libnm-util/libnm-util.la
+
 pkgconfigdir = $(libdir)/pkgconfig
 pkgconfig_DATA = libnm-util.pc
 

Added: trunk/libnm-util/crypto.c
==============================================================================
--- (empty file)
+++ trunk/libnm-util/crypto.c	Mon May 19 07:43:13 2008
@@ -0,0 +1,547 @@
+/* NetworkManager Wireless Applet -- Display wireless access points and allow user control
+ *
+ * Dan Williams <dcbw redhat 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * (C) Copyright 2007 Red Hat, Inc.
+ */
+
+#include <glib.h>
+#include <string.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <glib/gi18n.h>
+
+#include "crypto.h"
+
+GQuark
+nm_crypto_error_quark (void)
+{
+	static GQuark quark;
+
+	if (G_UNLIKELY (!quark))
+		quark = g_quark_from_static_string ("nm-crypto-error-quark");
+	return quark;
+}
+
+
+static const char *pem_rsa_key_begin = "-----BEGIN RSA PRIVATE KEY-----";
+static const char *pem_rsa_key_end = "-----END RSA PRIVATE KEY-----";
+
+static const char *pem_dsa_key_begin = "-----BEGIN DSA PRIVATE KEY-----";
+static const char *pem_dsa_key_end = "-----END DSA PRIVATE KEY-----";
+
+static const char *pem_cert_begin = "-----BEGIN CERTIFICATE-----";
+static const char *pem_cert_end = "-----END CERTIFICATE-----";
+
+static const char *
+find_tag (const char *tag, const char *buf, gsize len)
+{
+	gsize i, taglen;
+
+	taglen = strlen (tag);
+	if (len < taglen)
+		return NULL;
+
+	for (i = 0; i < len - taglen; i++) {
+		if (memcmp (buf + i, tag, taglen) == 0)
+			return buf + i;
+	}
+	return NULL;
+}
+
+#define DEK_INFO_TAG "DEK-Info: "
+#define PROC_TYPE_TAG "Proc-Type: "
+
+static char *
+parse_key_file (const char *filename,
+                int key_type,
+                gsize *out_length,
+                char **out_cipher,
+                char **out_iv,
+                GError **error)
+{
+	char *contents = NULL;
+	char **lines = NULL;
+	char **ln = NULL;
+	gsize length = 0;
+	const char *pos;
+	const char *end;
+	GString *str = NULL;
+	int enc_tags = 0;
+	char *iv = NULL;
+	char *cipher = NULL;
+	char *bindata = NULL;
+	gsize bindata_len = 0;
+	const char *start_tag;
+	const char *end_tag;
+
+	switch (key_type) {
+	case NM_CRYPTO_KEY_TYPE_RSA:
+		start_tag = pem_rsa_key_begin;
+		end_tag = pem_rsa_key_end;
+		break;
+	case NM_CRYPTO_KEY_TYPE_DSA:
+		start_tag = pem_dsa_key_begin;
+		end_tag = pem_dsa_key_end;
+		break;
+	default:
+		g_set_error (error, NM_CRYPTO_ERROR,
+		             NM_CRYPTO_ERR_UNKNOWN_KEY_TYPE,
+		             "Unknown key type %d",
+		             key_type);
+		g_assert_not_reached ();
+		return NULL;
+	}
+
+	if (!g_file_get_contents (filename, &contents, &length, error))
+		return NULL;
+
+	pos = find_tag (start_tag, contents, length);
+	if (!pos)
+		goto parse_error;
+
+	pos += strlen (start_tag);
+
+	end = find_tag (end_tag, pos, contents + length - pos);
+	if (end == NULL) {
+		g_set_error (error, NM_CRYPTO_ERROR,
+		             NM_CRYPTO_ERR_PEM_FORMAT_INVALID,
+		             _("PEM key file had no end tag '%s'."),
+		             end_tag);
+		goto parse_error;
+	}
+	*((char *) end) = '\0';
+
+	lines = g_strsplit (pos, "\n", 0);
+	if (!lines || g_strv_length (lines) <= 1) {
+		g_set_error (error, NM_CRYPTO_ERROR,
+		             NM_CRYPTO_ERR_PEM_FORMAT_INVALID,
+		             _("Doesn't look like a PEM private key file."));
+		goto parse_error;
+	}
+
+	str = g_string_new_len (NULL, end - pos);
+	if (!str) {
+		g_set_error (error, NM_CRYPTO_ERROR,
+		             NM_CRYPTO_ERR_OUT_OF_MEMORY,
+		             _("Not enough memory to store PEM file data."));
+		goto parse_error;
+	}
+
+	for (ln = lines; *ln; ln++) {
+		char *p = *ln;
+
+		/* Chug leading spaces */
+		p = g_strstrip (p);
+		if (!*p)
+			continue;
+
+		if (!strncmp (p, PROC_TYPE_TAG, strlen (PROC_TYPE_TAG))) {
+			if (enc_tags++ != 0) {
+				g_set_error (error, NM_CRYPTO_ERROR,
+				             NM_CRYPTO_ERR_PEM_FORMAT_INVALID,
+				             _("Malformed PEM file: Proc-Type was not first tag."));
+				goto parse_error;
+			}
+
+			p += strlen (PROC_TYPE_TAG);
+			if (strcmp (p, "4,ENCRYPTED")) {
+				g_set_error (error, NM_CRYPTO_ERROR,
+				             NM_CRYPTO_ERR_PEM_FORMAT_INVALID,
+				             _("Malformed PEM file: unknown Proc-Type tag '%s'."),
+				             p);
+				goto parse_error;
+			}
+		} else if (!strncmp (p, DEK_INFO_TAG, strlen (DEK_INFO_TAG))) {
+			char *comma;
+
+			if (enc_tags++ != 1) {
+				g_set_error (error, NM_CRYPTO_ERROR,
+				             NM_CRYPTO_ERR_PEM_FORMAT_INVALID,
+				             _("Malformed PEM file: DEK-Info was not the second tag."));
+				goto parse_error;
+			}
+
+			p += strlen (DEK_INFO_TAG);
+
+			/* Grab the IV first */
+			comma = strchr (p, ',');
+			if (!comma || (*(comma + 1) == '\0')) {
+				g_set_error (error, NM_CRYPTO_ERROR,
+				             NM_CRYPTO_ERR_PEM_FORMAT_INVALID,
+				             _("Malformed PEM file: no IV found in DEK-Info tag."));
+				goto parse_error;
+			}
+			*comma++ = '\0';
+			if (!g_ascii_isxdigit (*comma)) {
+				g_set_error (error, NM_CRYPTO_ERROR,
+				             NM_CRYPTO_ERR_PEM_FORMAT_INVALID,
+				             _("Malformed PEM file: invalid format of IV in DEK-Info tag."));
+				goto parse_error;
+			}
+			iv = g_strdup (comma);
+
+			/* Get the private key cipher */
+			if (!strcasecmp (p, "DES-EDE3-CBC")) {
+				cipher = g_strdup (p);
+			} else if (!strcasecmp (p, "DES-CBC")) {
+				cipher = g_strdup (p);
+			} else {
+				g_set_error (error, NM_CRYPTO_ERROR,
+				             NM_CRYPTO_ERR_UNKNOWN_KEY_TYPE,
+				             _("Malformed PEM file: unknown private key cipher '%s'."),
+				             p);
+				goto parse_error;
+			}
+		} else {
+			if ((enc_tags != 0) && (enc_tags != 2)) {
+				g_set_error (error, NM_CRYPTO_ERROR,
+				             NM_CRYPTO_ERR_PEM_FORMAT_INVALID,
+				             "Malformed PEM file: both Proc-Type and DEK-Info tags are required.");
+				goto parse_error;
+			}
+			g_string_append (str, p);
+		}
+	}
+
+	bindata = (char *) g_base64_decode (str->str, &bindata_len);
+	if (bindata == NULL || !bindata_len) {
+		g_set_error (error, NM_CRYPTO_ERROR,
+		             NM_CRYPTO_ERR_DECODE_FAILED,
+		             _("Could not decode private key."));
+		goto parse_error;
+	}
+
+	if (lines)
+		g_strfreev (lines);
+	g_free (contents);
+
+	*out_iv = iv;
+	*out_cipher = cipher;
+	*out_length = bindata_len;
+	return bindata;
+
+parse_error:
+	g_free (bindata);
+	g_free (cipher);
+	g_free (iv);
+	if (lines)
+		g_strfreev (lines);
+	g_free (contents);
+	return NULL;
+}
+
+static GByteArray *
+file_to_g_byte_array (const char *filename,
+                      GError **error)
+{
+	char *contents, *der = NULL;
+	GByteArray *array = NULL;
+	gsize length = 0;
+	const char *pos;
+
+	if (!g_file_get_contents (filename, &contents, &length, error))
+		return NULL;
+
+	pos = find_tag (pem_cert_begin, contents, length);
+	if (pos) {
+		const char *end;
+
+		pos += strlen (pem_cert_begin);
+		end = find_tag (pem_cert_end, pos, contents + length - pos);
+		if (end == NULL) {
+			g_set_error (error, NM_CRYPTO_ERROR,
+			             NM_CRYPTO_ERR_PEM_FORMAT_INVALID,
+			             _("PEM certificate '%s' had no end tag '%s'."),
+			             filename, pem_cert_end);
+			goto done;
+		}
+
+		contents[end - contents - 1] = '\0';
+		der = (char *) g_base64_decode (pos, &length);
+		if (der == NULL || !length) {
+			g_set_error (error, NM_CRYPTO_ERROR,
+			             NM_CRYPTO_ERR_DECODE_FAILED,
+			             _("Failed to decode certificate."));
+			goto done;
+		}
+	}
+
+	array = g_byte_array_sized_new (length);
+	if (!array) {
+		g_set_error (error, NM_CRYPTO_ERROR,
+		             NM_CRYPTO_ERR_OUT_OF_MEMORY,
+		             _("Not enough memory to store certificate data."));
+		goto done;
+	}
+
+	g_byte_array_append (array, der ? (unsigned char *) der : (unsigned char *) contents, length);
+	if (array->len != length) {
+		g_set_error (error, NM_CRYPTO_ERROR,
+		             NM_CRYPTO_ERR_OUT_OF_MEMORY,
+		             _("Not enough memory to store certificate data."));
+		g_byte_array_free (array, TRUE);
+		array = NULL;
+	}
+
+done:
+	g_free (der);
+	g_free (contents);
+	return array;
+}
+
+/*
+ * Convert a hex string into bytes.
+ */
+static char *
+convert_iv (const char *src,
+            gsize *out_len,
+            GError **error)
+{
+	int num;
+    int i;
+    char conv[3];
+    char *c;
+
+	g_return_val_if_fail (src != NULL, NULL);
+
+	num = strlen (src);
+	if (num % 2) {
+		g_set_error (error, NM_CRYPTO_ERROR,
+		             NM_CRYPTO_ERR_RAW_IV_INVALID,
+		             _("IV must be an even number of bytes in length."));
+		return NULL;
+	}
+
+	num /= 2;
+    c = g_malloc0 (num + 1);
+    if (c == NULL) {
+		g_set_error (error, NM_CRYPTO_ERROR,
+		             NM_CRYPTO_ERR_OUT_OF_MEMORY,
+		             _("Not enough memory to store the IV."));
+        return NULL;
+	}
+
+    conv[2] = '\0';
+    for (i = 0; i < num; i++) {
+        conv[0] = src[(i * 2)];
+        conv[1] = src[(i * 2) + 1];
+		if (!g_ascii_isxdigit (conv[0]) || !g_ascii_isxdigit (conv[1])) {
+			g_set_error (error, NM_CRYPTO_ERROR,
+			             NM_CRYPTO_ERR_RAW_IV_INVALID,
+			             _("IV contains non-hexadecimal digits."));
+			goto error;
+		}
+
+        c[i] = strtol(conv, NULL, 16);
+    }
+    *out_len = num;
+    return c;
+
+error:
+	g_free (c);
+	return NULL;
+}
+
+static char *
+make_des_key (const char *cipher,
+              const char *salt,
+              const gsize salt_len,
+              const char *password,
+              gsize *out_len,
+              GError **error)
+{
+	char *key;
+	guint32 digest_len;
+
+	g_return_val_if_fail (cipher != NULL, NULL);
+	g_return_val_if_fail (salt != NULL, NULL);
+	g_return_val_if_fail (salt_len >= 8, NULL);
+	g_return_val_if_fail (password != NULL, NULL);
+	g_return_val_if_fail (out_len != NULL, NULL);
+
+	if (!strcmp (cipher, "DES-EDE3-CBC"))
+		digest_len = 24;
+	else if (!strcmp (cipher, "DES-CBC"))
+		digest_len = 8;
+	else {
+		g_set_error (error, NM_CRYPTO_ERROR,
+		             NM_CRYPTO_ERR_UNKNOWN_CIPHER,
+		             _("Private key cipher '%s' was unknown."),
+		             cipher);
+		return NULL;
+	}
+
+	key = g_malloc0 (digest_len + 1);
+	if (!key) {
+		g_set_error (error, NM_CRYPTO_ERROR,
+		             NM_CRYPTO_ERR_OUT_OF_MEMORY,
+		             _("Not enough memory to create private key decryption key."));
+		return NULL;
+	}
+
+	if (!crypto_md5_hash (salt,
+	                      salt_len,
+	                      password,
+	                      strlen (password),
+	                      key,
+	                      digest_len,
+	                      error))
+		goto error;
+
+	*out_len = digest_len;
+	return key;
+
+error:
+	if (key) {
+		/* Don't leak stale key material */
+		memset (key, 0, digest_len);
+		g_free (key);
+	}
+	return NULL;
+}
+
+static char *
+decrypt_key (const char *cipher,
+             int key_type,
+             const char *data,
+             gsize data_len,
+             const char *iv,
+             const char *password,
+             gsize *out_len,
+             GError **error)
+{
+	char *bin_iv = NULL;
+	gsize bin_iv_len = 0;
+	char *key = NULL;
+	gsize key_len = 0;
+	char *output = NULL;
+
+	bin_iv = convert_iv (iv, &bin_iv_len, error);
+	if (!bin_iv)
+		return NULL;
+
+	/* Convert the PIN and IV into a DES key */
+	key = make_des_key (cipher, bin_iv, bin_iv_len, password, &key_len, error);
+	if (!key || !key_len)
+		goto out;
+
+	output = crypto_decrypt (cipher, key_type,
+	                         data, data_len,
+	                         bin_iv, bin_iv_len,
+	                         key, key_len,
+	                         out_len,
+	                         error);
+	if (!output)
+		goto out;
+
+	if (*out_len == 0) {
+		g_free (output);
+		output = NULL;
+		goto out;
+	}
+ 
+out:
+	if (key) {
+		/* Don't leak stale key material */
+		memset (key, 0, key_len);
+		g_free (key);
+	}
+	g_free (bin_iv);
+	return output;
+}
+
+
+GByteArray *
+crypto_get_private_key (const char *file,
+                        const char *password,
+                        guint32 *out_key_type,
+                        GError **error)
+{
+	GByteArray *array = NULL;
+	guint32 key_type = NM_CRYPTO_KEY_TYPE_RSA;
+	char *data = NULL;
+	gsize data_len = 0;
+	char *iv = NULL;
+	char *cipher = NULL;
+	char *decrypted = NULL;
+	gsize decrypted_len = 0;
+
+	/* Try RSA first */
+	data = parse_key_file (file, key_type, &data_len, &cipher, &iv, error);
+	if (!data) {
+		g_clear_error (error);
+
+		/* DSA next */
+		key_type = NM_CRYPTO_KEY_TYPE_DSA;
+		data = parse_key_file (file, key_type, &data_len, &cipher, &iv, error);
+		if (!data)
+			goto out;
+	}
+
+	decrypted = decrypt_key (cipher,
+	                         key_type,
+	                         data,
+	                         data_len,
+	                         iv,
+	                         password,
+	                         &decrypted_len,
+	                         error);
+	if (!decrypted)
+		goto out;
+
+	array = g_byte_array_sized_new (decrypted_len);
+	if (!array) {
+		g_set_error (error, NM_CRYPTO_ERROR,
+		             NM_CRYPTO_ERR_OUT_OF_MEMORY,
+		             _("Not enough memory to store decrypted private key."));
+		goto out;
+	}
+
+	g_byte_array_append (array, (const guint8 *) decrypted, decrypted_len);
+	*out_key_type = key_type;
+
+out:
+	if (decrypted) {
+		/* Don't expose key material */
+		memset (decrypted, 0, decrypted_len);
+		g_free (decrypted);
+	}
+	g_free (data);
+	g_free (cipher);
+	g_free (iv);
+	return array;
+}
+
+GByteArray *
+crypto_load_and_verify_certificate (const char *file,
+                                    GError **error)
+{
+	GByteArray *array;
+
+	array = file_to_g_byte_array (file, error);
+	if (!array)
+		return NULL;
+
+	if (!crypto_verify_cert (array->data, array->len, error)) {
+		g_byte_array_free (array, TRUE);
+		array = NULL;
+	}
+
+	return array;
+}
+

Added: trunk/libnm-util/crypto.h
==============================================================================
--- (empty file)
+++ trunk/libnm-util/crypto.h	Mon May 19 07:43:13 2008
@@ -0,0 +1,92 @@
+/* NetworkManager Wireless Applet -- Display wireless access points and allow user control
+ *
+ * Dan Williams <dcbw redhat 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * (C) Copyright 2007 Red Hat, Inc.
+ */
+
+#include <glib.h>
+
+#define MD5_HASH_LEN 20
+#define CIPHER_DES_EDE3_CBC "DES-EDE3-CBC"
+#define CIPHER_DES_CBC "DES-CBC"
+
+enum {
+	NM_CRYPTO_ERR_NONE = 0,
+	NM_CRYPTO_ERR_CANT_READ_FILE,
+	NM_CRYPTO_ERR_PEM_FORMAT_INVALID,
+	NM_CRYPTO_ERR_CERT_FORMAT_INVALID,
+	NM_CRYPTO_ERR_DECODE_FAILED,
+	NM_CRYPTO_ERR_OUT_OF_MEMORY,
+	NM_CRYPTO_ERR_UNKNOWN_KEY_TYPE,
+	NM_CRYPTO_ERR_UNKNOWN_CIPHER,
+	NM_CRYPTO_ERR_RAW_IV_INVALID,
+	NM_CRYPTO_ERR_MD5_INIT_FAILED,
+	NM_CRYPTO_ERR_CIPHER_INIT_FAILED,
+	NM_CRYPTO_ERR_CIPHER_SET_KEY_FAILED,
+	NM_CRYPTO_ERR_CIPHER_SET_IV_FAILED,
+	NM_CRYPTO_ERR_CIPHER_DECRYPT_FAILED,
+};
+
+enum {
+	NM_CRYPTO_KEY_TYPE_UNKNOWN = 0,
+	NM_CRYPTO_KEY_TYPE_RSA,
+	NM_CRYPTO_KEY_TYPE_DSA,
+};
+
+
+#define NM_CRYPTO_ERROR nm_crypto_error_quark ()
+GQuark nm_crypto_error_quark (void);
+
+gboolean crypto_init (GError **error);
+
+void crypto_deinit (void);
+
+GByteArray * crypto_get_private_key (const char *file,
+                                     const char *password,
+                                     guint32 *out_key_type,
+                                     GError **error);
+
+GByteArray * crypto_load_and_verify_certificate (const char *file,
+                                                 GError **error);
+
+/* Internal utils API bits for crypto providers */
+
+gboolean crypto_md5_hash (const char *salt,
+                          const gsize salt_len,
+                          const char *password,
+                          gsize password_len,
+                          char *buffer,
+                          gsize buflen,
+                          GError **error);
+
+char * crypto_decrypt (const char *cipher,
+                       int key_type,
+                       const char *data,
+                       gsize data_len,
+                       const char *iv,
+                       const gsize iv_len,
+                       const char *key,
+                       const gsize key_len,
+                       gsize *out_len,
+                       GError **error);
+
+gboolean crypto_verify_cert (const unsigned char *data,
+                             gsize len,
+                             GError **error);
+
+

Added: trunk/libnm-util/crypto_gnutls.c
==============================================================================
--- (empty file)
+++ trunk/libnm-util/crypto_gnutls.c	Mon May 19 07:43:13 2008
@@ -0,0 +1,227 @@
+/* NetworkManager Wireless Applet -- Display wireless access points and allow user control
+ *
+ * Dan Williams <dcbw redhat 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * (C) Copyright 2007 Red Hat, Inc.
+ */
+
+#include <glib.h>
+#include <glib/gi18n.h>
+
+#include <gcrypt.h>
+#include <gnutls/gnutls.h>
+#include <gnutls/x509.h>
+
+#include "crypto.h"
+
+gboolean
+crypto_init (GError **error)
+{
+	gnutls_global_init();
+	return TRUE;
+}
+
+void
+crypto_deinit (void)
+{
+	gnutls_global_deinit();
+}
+
+gboolean
+crypto_md5_hash (const char *salt,
+                 const gsize salt_len,
+                 const char *password,
+                 gsize password_len,
+                 char *buffer,
+                 gsize buflen,
+                 GError **error)
+{
+	gcry_md_hd_t ctx;
+	gcry_error_t err;
+	int nkey = buflen;
+	const gsize digest_len = 16;
+	int count = 0;
+	char digest[MD5_HASH_LEN];
+	char *p = buffer;
+
+	g_return_val_if_fail (salt != NULL, FALSE);
+	g_return_val_if_fail (salt_len >= 8, FALSE);
+	g_return_val_if_fail (password != NULL, FALSE);
+	g_return_val_if_fail (password_len > 0, FALSE);
+	g_return_val_if_fail (buffer != NULL, FALSE);
+	g_return_val_if_fail (buflen > 0, FALSE);
+
+	err = gcry_md_open (&ctx, GCRY_MD_MD5, 0);
+	if (err) {
+		g_set_error (error, NM_CRYPTO_ERROR,
+		             NM_CRYPTO_ERR_MD5_INIT_FAILED,
+		             _("Failed to initialize the MD5 engine: %s / %s."),
+		             gcry_strsource (err), gcry_strerror (err));
+		return FALSE;
+	}
+
+	while (nkey > 0) {
+		int i = 0;
+
+		if (count++)
+			gcry_md_write (ctx, digest, digest_len);
+		gcry_md_write (ctx, password, password_len);
+		gcry_md_write (ctx, salt, 8); /* Only use 8 bytes of salt */
+		gcry_md_final (ctx);
+		memcpy (digest, gcry_md_read (ctx, 0), digest_len);
+		gcry_md_reset (ctx);
+		
+		while (nkey && (i < digest_len)) {
+			*(p++) = digest[i++];
+			nkey--;
+		}
+	}
+
+	memset (digest, 0, sizeof (digest));
+	gcry_md_close (ctx);
+	return TRUE;
+}
+
+char *
+crypto_decrypt (const char *cipher,
+                int key_type,
+                const char *data,
+                gsize data_len,
+                const char *iv,
+                const gsize iv_len,
+                const char *key,
+                const gsize key_len,
+                gsize *out_len,
+                GError **error)
+{
+	gcry_cipher_hd_t ctx;
+	gcry_error_t err;
+	int cipher_mech;
+	char *output = NULL;
+	gboolean success = FALSE;
+	gsize len;
+
+	if (!strcmp (cipher, CIPHER_DES_EDE3_CBC))
+		cipher_mech = GCRY_CIPHER_3DES;
+	else if (!strcmp (cipher, CIPHER_DES_CBC))
+		cipher_mech = GCRY_CIPHER_DES;
+	else {
+		g_set_error (error, NM_CRYPTO_ERROR,
+		             NM_CRYPTO_ERR_UNKNOWN_CIPHER,
+		             _("Private key cipher '%s' was unknown."),
+		             cipher);
+		return NULL;
+	}
+
+	output = g_malloc0 (data_len + 1);
+	if (!output) {
+		g_set_error (error, NM_CRYPTO_ERROR,
+		             NM_CRYPTO_ERR_OUT_OF_MEMORY,
+		             _("Not enough memory for decrypted key buffer."));
+		return NULL;
+	}
+
+	err = gcry_cipher_open (&ctx, cipher_mech, GCRY_CIPHER_MODE_CBC, 0);
+	if (err) {
+		g_set_error (error, NM_CRYPTO_ERROR,
+		             NM_CRYPTO_ERR_CIPHER_INIT_FAILED,
+		             _("Failed to initialize the decryption cipher context: %s / %s."),
+		             gcry_strsource (err), gcry_strerror (err));
+		goto out;
+	}
+
+	err = gcry_cipher_setkey (ctx, key, key_len);
+	if (err) {
+		g_set_error (error, NM_CRYPTO_ERROR,
+		             NM_CRYPTO_ERR_CIPHER_SET_KEY_FAILED,
+		             _("Failed to set symmetric key for decryption: %s / %s."),
+		             gcry_strsource (err), gcry_strerror (err));
+		goto out;
+	}
+
+	err = gcry_cipher_setiv (ctx, iv, iv_len);
+	if (err) {
+		g_set_error (error, NM_CRYPTO_ERROR,
+		             NM_CRYPTO_ERR_CIPHER_SET_IV_FAILED,
+		             _("Failed to set IV for decryption: %s / %s."),
+		             gcry_strsource (err), gcry_strerror (err));
+		goto out;
+	}
+
+	err = gcry_cipher_decrypt (ctx, output, data_len, data, data_len);
+	if (err) {
+		g_set_error (error, NM_CRYPTO_ERROR,
+		             NM_CRYPTO_ERR_CIPHER_DECRYPT_FAILED,
+		             _("Failed to decrypt the private key: %s / %s."),
+		             gcry_strsource (err), gcry_strerror (err));
+		goto out;
+	}
+	len = data_len - output[data_len - 1];
+	if (len > data_len)
+		goto out;
+
+	*out_len = len;
+	output[*out_len] = '\0';
+	success = TRUE;
+
+out:
+	if (!success) {
+		if (output) {
+			/* Don't expose key material */
+			memset (output, 0, data_len);
+			g_free (output);
+			output = NULL;
+		}
+	}
+	gcry_cipher_close (ctx);
+	return output;
+}
+
+gboolean
+crypto_verify_cert (const unsigned char *data,
+                    gsize len,
+                    GError **error)
+{
+	gnutls_x509_crt_t crt;
+	gnutls_datum dt;
+	int err;
+
+	err = gnutls_x509_crt_init (&crt);
+	if (err < 0) {
+		g_set_error (error, NM_CRYPTO_ERROR,
+		             NM_CRYPTO_ERR_CERT_FORMAT_INVALID,
+		             _("Error initializing certificate data: %s"),
+		             gnutls_strerror (err));
+		return FALSE;
+	}
+
+	dt.data = (unsigned char *) data;
+	dt.size = len;
+
+	err = gnutls_x509_crt_import (crt, &dt, GNUTLS_X509_FMT_DER);
+	if (err < 0) {
+		g_set_error (error, NM_CRYPTO_ERROR,
+		             NM_CRYPTO_ERR_CERT_FORMAT_INVALID,
+		             _("Couldn't decode certificate: %s"),
+		             gnutls_strerror (err));
+		return FALSE;
+	}
+
+	gnutls_x509_crt_deinit (crt);
+	return TRUE;
+}
+

Added: trunk/libnm-util/crypto_nss.c
==============================================================================
--- (empty file)
+++ trunk/libnm-util/crypto_nss.c	Mon May 19 07:43:13 2008
@@ -0,0 +1,256 @@
+/* NetworkManager Wireless Applet -- Display wireless access points and allow user control
+ *
+ * Dan Williams <dcbw redhat 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * (C) Copyright 2007 Red Hat, Inc.
+ */
+
+#include <glib.h>
+#include <glib/gi18n.h>
+
+#include <prinit.h>
+#include <nss.h>
+#include <pk11pub.h>
+#include <pkcs11t.h>
+#include <cert.h>
+
+#include "crypto.h"
+
+
+gboolean
+crypto_init (GError **error)
+{
+	PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 1);
+	NSS_NoDB_Init (NULL);
+	return TRUE;
+}
+
+void
+crypto_deinit (void)
+{
+	NSS_Shutdown ();
+	PR_Cleanup ();
+}
+
+gboolean
+crypto_md5_hash (const char *salt,
+                 const gsize salt_len,
+                 const char *password,
+                 gsize password_len,
+                 char *buffer,
+                 gsize buflen,
+                 GError **error)
+{
+	PK11Context *ctx;
+	int nkey = buflen;
+	unsigned int digest_len;
+	int count = 0;
+	char digest[MD5_HASH_LEN];
+	char *p = buffer;
+
+	g_return_val_if_fail (salt != NULL, FALSE);
+	g_return_val_if_fail (salt_len >= 8, FALSE);
+	g_return_val_if_fail (password != NULL, FALSE);
+	g_return_val_if_fail (password_len > 0, FALSE);
+	g_return_val_if_fail (buffer != NULL, FALSE);
+	g_return_val_if_fail (buflen > 0, FALSE);
+
+	ctx = PK11_CreateDigestContext (SEC_OID_MD5);
+	if (!ctx) {
+		g_set_error (error, NM_CRYPTO_ERROR,
+		             NM_CRYPTO_ERR_MD5_INIT_FAILED,
+		             _("Failed to initialize the MD5 context: %d."),
+		             PORT_GetError ());
+		return FALSE;
+	}
+
+	while (nkey > 0) {
+		int i = 0;
+
+		PK11_DigestBegin (ctx);
+		if (count++)
+			PK11_DigestOp (ctx, (const unsigned char *) digest, digest_len);
+		PK11_DigestOp (ctx, (const unsigned char *) password, password_len);
+		PK11_DigestOp (ctx, (const unsigned char *) salt, 8); /* Only use 8 bytes of salt */
+		PK11_DigestFinal (ctx, (unsigned char *) digest, &digest_len, sizeof (digest));
+
+		while (nkey && (i < digest_len)) {
+			*(p++) = digest[i++];
+			nkey--;
+		}
+	}
+
+	memset (digest, 0, sizeof (digest));
+	PK11_DestroyContext (ctx, PR_TRUE);
+	return TRUE;
+}
+
+char *
+crypto_decrypt (const char *cipher,
+                int key_type,
+                const char *data,
+                gsize data_len,
+                const char *iv,
+                const gsize iv_len,
+                const char *key,
+                const gsize key_len,
+                gsize *out_len,
+                GError **error)
+{
+	char *output = NULL;
+	int tmp1_len = 0;
+	unsigned int tmp2_len = 0;
+	CK_MECHANISM_TYPE cipher_mech;
+	PK11SlotInfo *slot = NULL;
+	SECItem key_item;
+	PK11SymKey *sym_key = NULL;
+	SECItem *sec_param = NULL;
+	PK11Context *ctx = NULL;
+	SECStatus s;
+	gboolean success = FALSE;
+	gsize len;
+
+	if (!strcmp (cipher, CIPHER_DES_EDE3_CBC))
+		cipher_mech = CKM_DES3_CBC_PAD;
+	else if (!strcmp (cipher, CIPHER_DES_CBC))
+		cipher_mech = CKM_DES_CBC_PAD;
+	else {
+		g_set_error (error, NM_CRYPTO_ERROR,
+		             NM_CRYPTO_ERR_UNKNOWN_CIPHER,
+		             _("Private key cipher '%s' was unknown."),
+		             cipher);
+		return NULL;
+	}
+
+	output = g_malloc0 (data_len + 1);
+	if (!output) {
+		g_set_error (error, NM_CRYPTO_ERROR,
+		             NM_CRYPTO_ERR_OUT_OF_MEMORY,
+		             _("Not enough memory for decrypted key buffer."));
+		return NULL;
+	}
+
+	slot = PK11_GetBestSlot (cipher_mech, NULL);
+	if (!slot) {
+		g_set_error (error, NM_CRYPTO_ERROR,
+		             NM_CRYPTO_ERR_CIPHER_INIT_FAILED,
+		             _("Failed to initialize the decryption cipher slot."));
+		goto out;
+	}
+
+	key_item.data = (unsigned char *) key;
+	key_item.len = key_len;
+	sym_key = PK11_ImportSymKey (slot, cipher_mech, PK11_OriginUnwrap, CKA_DECRYPT, &key_item, NULL);
+	if (!sym_key) {
+		g_set_error (error, NM_CRYPTO_ERROR,
+		             NM_CRYPTO_ERR_CIPHER_SET_KEY_FAILED,
+		             _("Failed to set symmetric key for decryption."));
+		goto out;
+	}
+
+	key_item.data = (unsigned char *) iv;
+	key_item.len = iv_len;
+	sec_param = PK11_ParamFromIV (cipher_mech, &key_item);
+	if (!sec_param) {
+		g_set_error (error, NM_CRYPTO_ERROR,
+		             NM_CRYPTO_ERR_CIPHER_SET_IV_FAILED,
+		             _("Failed to set IV for decryption."));
+		goto out;
+	}
+
+	ctx = PK11_CreateContextBySymKey (cipher_mech, CKA_DECRYPT, sym_key, sec_param);
+	if (!ctx) {
+		g_set_error (error, NM_CRYPTO_ERROR,
+		             NM_CRYPTO_ERR_CIPHER_INIT_FAILED,
+		             _("Failed to initialize the decryption context."));
+		goto out;
+	}
+
+	s = PK11_CipherOp (ctx,
+	                   (unsigned char *) output,
+	                   &tmp1_len,
+	                   data_len,
+	                   (unsigned char *) data,
+	                   data_len);
+	if (s != SECSuccess) {
+		g_set_error (error, NM_CRYPTO_ERROR,
+		             NM_CRYPTO_ERR_CIPHER_DECRYPT_FAILED,
+		             _("Failed to decrypt the private key: %d."),
+		             PORT_GetError ());
+		goto out;
+	}
+
+	s = PK11_DigestFinal (ctx,
+	                      (unsigned char *) (output + tmp1_len),
+	                      &tmp2_len,
+	                      data_len - tmp1_len);
+	if (s != SECSuccess) {
+		g_set_error (error, NM_CRYPTO_ERROR,
+		             NM_CRYPTO_ERR_CIPHER_DECRYPT_FAILED,
+		             _("Failed to finalize decryption of the private key: %d."),
+		             PORT_GetError ());
+		goto out;
+	}
+	len = tmp1_len + tmp2_len;
+	if (len > data_len)
+		goto out;
+
+	*out_len = len;
+	output[*out_len] = '\0';
+	success = TRUE;
+
+out:
+	if (ctx)
+		PK11_DestroyContext (ctx, PR_TRUE);
+	if (sym_key)
+		PK11_FreeSymKey (sym_key);
+	if (sec_param)
+		SECITEM_FreeItem (sec_param, PR_TRUE);
+	if (slot)
+		PK11_FreeSlot (slot);
+
+	if (!success) {
+		if (output) {
+			/* Don't expose key material */
+			memset (output, 0, data_len);
+			g_free (output);
+			output = NULL;
+		}
+	}
+	return output;
+}
+
+gboolean
+crypto_verify_cert (const unsigned char *data,
+                    gsize len,
+                    GError **error)
+{
+	CERTCertificate *cert;
+
+	cert = CERT_DecodeCertFromPackage ((char *) data, len);
+	if (!cert) {
+		g_set_error (error, NM_CRYPTO_ERROR,
+		             NM_CRYPTO_ERR_CERT_FORMAT_INVALID,
+		             _("Couldn't decode certificate: %d"),
+		             PORT_GetError());
+		return FALSE;
+	}
+
+    CERT_DestroyCertificate (cert);
+	return TRUE;
+}
+

Modified: trunk/libnm-util/nm-setting-8021x.c
==============================================================================
--- trunk/libnm-util/nm-setting-8021x.c	(original)
+++ trunk/libnm-util/nm-setting-8021x.c	Mon May 19 07:43:13 2008
@@ -7,6 +7,7 @@
 #include "nm-param-spec-specialized.h"
 #include "nm-utils.h"
 #include "nm-dbus-glib-types.h"
+#include "crypto.h"
 
 G_DEFINE_TYPE (NMSetting8021x, nm_setting_802_1x, NM_TYPE_SETTING)
 
@@ -41,6 +42,117 @@
 	return (NMSetting *) g_object_new (NM_TYPE_SETTING_802_1X, NULL);
 }
 
+gboolean
+nm_setting_802_1x_set_ca_cert (NMSetting8021x *self,
+						 const char *filename,
+						 GError **err)
+{
+	g_return_val_if_fail (NM_IS_SETTING_802_1X (self), FALSE);
+	g_return_val_if_fail (filename != NULL, FALSE);
+
+	if (self->ca_cert)
+		g_byte_array_free (self->ca_cert, TRUE);
+
+	self->ca_cert = crypto_load_and_verify_certificate (filename, err);
+
+	return self->ca_cert != NULL;
+}
+
+gboolean
+nm_setting_802_1x_set_client_cert (NMSetting8021x *self,
+							const char *filename,
+							GError **err)
+{
+	g_return_val_if_fail (NM_IS_SETTING_802_1X (self), FALSE);
+	g_return_val_if_fail (filename != NULL, FALSE);
+
+	if (self->client_cert)
+		g_byte_array_free (self->client_cert, TRUE);
+
+	self->client_cert = crypto_load_and_verify_certificate (filename, err);
+
+	return self->client_cert != NULL;
+}
+
+gboolean
+nm_setting_802_1x_set_phase2_ca_cert (NMSetting8021x *self,
+							   const char *filename,
+							   GError **err)
+{
+	g_return_val_if_fail (NM_IS_SETTING_802_1X (self), FALSE);
+	g_return_val_if_fail (filename != NULL, FALSE);
+
+	if (self->phase2_ca_cert)
+		g_byte_array_free (self->phase2_ca_cert, TRUE);
+
+	self->phase2_ca_cert = crypto_load_and_verify_certificate (filename, err);
+
+	return self->phase2_ca_cert != NULL;
+
+}
+
+gboolean
+nm_setting_802_1x_set_phase2_client_cert (NMSetting8021x *self,
+								  const char *filename,
+								  GError **err)
+{
+	g_return_val_if_fail (NM_IS_SETTING_802_1X (self), FALSE);
+	g_return_val_if_fail (filename != NULL, FALSE);
+
+	if (self->phase2_client_cert)
+		g_byte_array_free (self->phase2_client_cert, TRUE);
+
+	self->phase2_client_cert = crypto_load_and_verify_certificate (filename, err);
+
+	return self->phase2_client_cert != NULL;
+}
+
+gboolean
+nm_setting_802_1x_set_private_key (NMSetting8021x *self,
+							const char *filename,
+							const char *password,
+							GError **err)
+{
+	guint32 ignore;
+
+	g_return_val_if_fail (NM_IS_SETTING_802_1X (self), FALSE);
+	g_return_val_if_fail (filename != NULL, FALSE);
+	g_return_val_if_fail (password != NULL, FALSE);
+
+	if (self->private_key) {
+		/* Try not to leave the decrypted private key around in memory */
+		memset (self->private_key, 0, self->private_key->len);
+		g_byte_array_free (self->private_key, TRUE);
+	}
+
+	self->private_key = crypto_get_private_key (filename, password, &ignore, err);
+
+	return self->private_key != NULL;
+}
+
+gboolean
+nm_setting_802_1x_set_phase2_private_key (NMSetting8021x *self,
+								  const char *filename,
+								  const char *password,
+								  GError **err)
+{
+	guint32 ignore;
+
+	g_return_val_if_fail (NM_IS_SETTING_802_1X (self), FALSE);
+	g_return_val_if_fail (filename != NULL, FALSE);
+	g_return_val_if_fail (password != NULL, FALSE);
+
+	if (self->phase2_private_key) {
+		/* Try not to leave the decrypted private key around in memory */
+		memset (self->phase2_private_key, 0, self->phase2_private_key->len);
+		g_byte_array_free (self->phase2_private_key, TRUE);
+	}
+
+	self->phase2_private_key = crypto_get_private_key (filename, password, &ignore, err);
+
+	return self->phase2_private_key != NULL;
+}
+
 static void
 need_secrets_password (NMSetting8021x *self,
                        GPtrArray *secrets,
@@ -487,6 +599,7 @@
 {
 	GObjectClass *object_class = G_OBJECT_CLASS (setting_class);
 	NMSettingClass *parent_class = NM_SETTING_CLASS (setting_class);
+	GError *error = NULL;
 
 	/* virtual methods */
 	object_class->set_property = set_property;
@@ -632,4 +745,12 @@
 							   "Phase2 private key",
 							   DBUS_TYPE_G_UCHAR_ARRAY,
 							   G_PARAM_READWRITE | NM_SETTING_PARAM_SERIALIZE | NM_SETTING_PARAM_SECRET));
+
+	/* Initialize crypto lbrary. */
+	if (!crypto_init (&error)) {
+		g_warning ("Couldn't initilize crypto system: %d %s",
+		           error->code, error->message);
+		g_error_free (error);
+	}
+
 }

Modified: trunk/libnm-util/nm-setting-8021x.h
==============================================================================
--- trunk/libnm-util/nm-setting-8021x.h	(original)
+++ trunk/libnm-util/nm-setting-8021x.h	Mon May 19 07:43:13 2008
@@ -68,6 +68,32 @@
 
 NMSetting *nm_setting_802_1x_new (void);
 
+gboolean nm_setting_802_1x_set_ca_cert (NMSetting8021x *self,
+								const char *filename,
+								GError **err);
+
+gboolean nm_setting_802_1x_set_client_cert (NMSetting8021x *self,
+								    const char *filename,
+								    GError **err);
+
+gboolean nm_setting_802_1x_set_phase2_ca_cert (NMSetting8021x *self,
+									  const char *filename,
+									  GError **err);
+
+gboolean nm_setting_802_1x_set_phase2_client_cert (NMSetting8021x *self,
+										 const char *filename,
+										 GError **err);
+
+gboolean nm_setting_802_1x_set_private_key (NMSetting8021x *self,
+								    const char *filename,
+								    const char *password,
+								    GError **err);
+
+gboolean nm_setting_802_1x_set_phase2_private_key (NMSetting8021x *self,
+										 const char *filename,
+										 const char *password,
+										 GError **err);
+
 G_END_DECLS
 
 #endif /* NM_SETTING_8021X_H */

Added: trunk/libnm-util/test-crypto.c
==============================================================================
--- (empty file)
+++ trunk/libnm-util/test-crypto.c	Mon May 19 07:43:13 2008
@@ -0,0 +1,171 @@
+/* NetworkManager Wireless Applet -- Display wireless access points and allow user control
+ *
+ * Dan Williams <dcbw redhat 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * (C) Copyright 2007 Red Hat, Inc.
+ */
+
+#include <glib.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <glib/gi18n.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "crypto.h"
+
+static const char *pem_rsa_key_begin = "-----BEGIN RSA PRIVATE KEY-----";
+static const char *pem_rsa_key_end = "-----END RSA PRIVATE KEY-----";
+
+static const char *pem_dsa_key_begin = "-----BEGIN DSA PRIVATE KEY-----";
+static const char *pem_dsa_key_end = "-----END DSA PRIVATE KEY-----";
+
+static void
+dump_key_to_pem (const char *key, gsize key_len, int key_type)
+{
+	char *b64 = NULL;
+	GString *str = NULL;
+	const char *start_tag;
+	const char *end_tag;
+	char *p;
+
+	switch (key_type) {
+	case NM_CRYPTO_KEY_TYPE_RSA:
+		start_tag = pem_rsa_key_begin;
+		end_tag = pem_rsa_key_end;
+		break;
+	case NM_CRYPTO_KEY_TYPE_DSA:
+		start_tag = pem_dsa_key_begin;
+		end_tag = pem_dsa_key_end;
+		break;
+	default:
+		g_warning ("Unknown key type %d", key_type);
+		return;
+	}
+
+	b64 = g_base64_encode ((const unsigned char *) key, key_len);
+	if (!b64) {
+		g_warning ("Couldn't base64 encode the key.");
+		goto out;
+	}
+
+	str = g_string_new (NULL);
+	if (!str) {
+		g_warning ("Couldn't allocate buffer to write out key.");
+		goto out;
+	}
+
+	g_string_append (str, start_tag);
+	g_string_append_c (str, '\n');
+
+	for (p = b64; p < (b64 + strlen (b64)); p += 64) {
+		g_string_append_len (str, p, strnlen (p, 64));
+		g_string_append_c (str, '\n');
+	}
+
+	g_string_append (str, end_tag);
+	g_string_append_c (str, '\n');
+
+	g_message ("Decrypted private key:\n\n%s", str->str);
+
+out:
+	g_free (b64);
+	if (str)
+		g_string_free (str, TRUE);
+}
+
+static void
+usage (const char *prgname)
+{
+	fprintf (stderr, "Usage: %s cert <file>\n"
+	                 "       %s key <file> <password>\n",
+	                 prgname, prgname);
+}
+
+#define MODE_CERT 1
+#define MODE_KEY  2
+
+int main (int argc, char **argv)
+{
+	guint32 key_type = 0;
+	int mode = 0;
+	const char *file;
+	GError *error = NULL;
+
+	if (argc < 2) {
+		usage (argv[0]);
+		return 1;
+	}
+
+	if (!strcmp (argv[1], "key")) {
+		if (argc < 4) {
+			usage (argv[0]);
+			return 1;
+		}
+		mode = MODE_KEY;
+	} else if (!strcmp (argv[1], "cert")) {
+		if (argc < 3) {
+			usage (argv[0]);
+			return 1;
+		}
+		mode = MODE_CERT;
+	} else {
+		usage (argv[0]);
+		return 1;
+	}
+
+	if (!crypto_init (&error)) {
+		g_warning ("Couldn't initialize crypto library: %d %s.",
+		           error->code, error->message);
+		return 1;
+	}
+
+	file = argv[2];
+
+	if (mode == MODE_CERT) {
+		GByteArray *array;
+
+		array = crypto_load_and_verify_certificate (file, &error);
+		if (!array) {
+			g_warning ("Couldn't read certificate file '%s': %d %s",
+			           file, error->code, error->message);
+			goto out;
+		}
+		g_byte_array_free (array, TRUE);
+	} else if (mode == MODE_KEY) {
+		const char *password = argv[3];
+		GByteArray *array;
+
+		array = crypto_get_private_key (file, password, &key_type, &error);
+		if (!array) {
+			g_warning ("Couldn't read key file '%s': %d %s",
+			           file, error->code, error->message);
+			goto out;
+		}
+
+		dump_key_to_pem ((const char *) array->data, array->len, key_type);
+		g_byte_array_free (array, TRUE);
+	} else {
+		g_assert_not_reached ();
+	}
+
+out:
+	crypto_deinit ();
+
+	return 0;
+}
+

Modified: trunk/po/POTFILES.in
==============================================================================
--- trunk/po/POTFILES.in	(original)
+++ trunk/po/POTFILES.in	Mon May 19 07:43:13 2008
@@ -6,4 +6,7 @@
 src/named-manager/nm-named-manager.c
 system-settings/plugins/ifcfg-fedora/plugin.c
 system-settings/src/main.c
+libnm-util/crypto.c
+libnm-util/crypto_gnutls.c
+libnm-util/crypto_nss.c
 

Modified: trunk/system-settings/plugins/ifcfg-suse/parser.c
==============================================================================
--- trunk/system-settings/plugins/ifcfg-suse/parser.c	(original)
+++ trunk/system-settings/plugins/ifcfg-suse/parser.c	Mon May 19 07:43:13 2008
@@ -436,12 +436,14 @@
 		g_warning ("Missing WPA-PSK key");
 }
 
-#if 0
-
-static void
-read_wpa_eap_settings (shvarFile *ifcfg, NMSettingWirelessSecurity *security)
+static NMSetting *
+read_wpa_eap_settings (shvarFile *ifcfg)
 {
+	NMSetting8021x *s_802_1x;
 	char *str;
+	GError *err = NULL;
+
+	s_802_1x = NM_SETTING_802_1X (nm_setting_802_1x_new ());
 
 	str = svGetValue (ifcfg, "WIRELESS_EAP_MODE");
 	if (str) {
@@ -450,35 +452,61 @@
 
 		pieces = g_strsplit (str, " ", 0);
 		for (i = 0; pieces[i]; i++)
-			s_8021x->eap = g_slist_append (s_8021x->eap, pieces[i]);
+			s_802_1x->eap = g_slist_append (s_802_1x->eap, pieces[i]);
 
 		g_free (pieces);
 		g_free (str);
 	}
 
 	s_802_1x->anonymous_identity = svGetValue (ifcfg, "WIRELESS_WPA_ANONID");
+	s_802_1x->phase1_peapver = svGetValue (ifcfg, "WIRELESS_PEAP_VERSION");
+	s_802_1x->phase2_auth = svGetValue (ifcfg, "WIRELESS_EAP_AUTH");
+	s_802_1x->identity = svGetValue (ifcfg, "WIRELESS_WPA_IDENTITY");
+	s_802_1x->password = svGetValue (ifcfg, "WIRELESS_WPA_PASSWORD");
 
-	char *ca_path;
+	str = svGetValue (ifcfg, "WIRELESS_CA_CERT");
+	if (str) {
+		nm_setting_802_1x_set_ca_cert (s_802_1x, str, &err);
+		if (err) {
+			g_warning ("Error loading WIRELESS_CA_CERT: %s", err->message);
+			g_error_free (err);
+		}
+
+		g_free (str);
+	}
+
+	str = svGetValue (ifcfg, "WIRELESS_CLIENT_CERT");
+	if (str) {
+		nm_setting_802_1x_set_client_cert (s_802_1x, str, &err);
+		if (err) {
+			g_warning ("Error loading WIRELESS_CLIENT_CERT: %s", err->message);
+			g_error_free (err);
+		}
 
-	GByteArray *ca_cert;
-	"WIRELESS_CA_CERT";
+		g_free (str);
+	}
 
-	GByteArray *client_cert;
-	"WIRELESS_CLIENT_CERT";
+	str = svGetValue (ifcfg, "WIRELESS_CLIENT_KEY");
+	if (str) {
+		char *password;
 
-	GByteArray *private_key;
-	"WIRELESS_CLIENT_KEY";
+		password = svGetValue (ifcfg, "WIRELESS_CLIENT_KEY_PASSWORD");
+		if (password) {
+			nm_setting_802_1x_set_private_key (s_802_1x, str, password, &err);
+			if (err) {
+				g_warning ("Error loading WIRELESS_CLIENT_KEY: %s", err->message);
+				g_error_free (err);
+			}
 
-	private_key_passwd
-	"WIRELESS_CLIENT_KEY_PASSWORD";
+			g_free (password);
+		} else
+			g_warning ("Missing WIRELESS_CLIENT_KEY_PASSWORD");
 
+		g_free (str);
+	}
 
-	s_802_1x->phase1_peapver = svGetValue (ifcfg, "WIRELESS_PEAP_VERSION");
-	s_802_1x->phase2_auth = svGetValue (ifcfg, "WIRELESS_EAP_AUTH");
-	s_802_1x->identity = svGetValue (ifcfg, "WIRELESS_WPA_IDENTITY");
-	s_802_1x->password = svGetValue (ifcfg, "WIRELESS_WPA_PASSWORD");
+	return (NMSetting *) s_802_1x;
 }
-#endif
 
 static NMSetting *
 make_wireless_security_setting (shvarFile *ifcfg, NMSettingWireless *s_wireless)
@@ -492,6 +520,9 @@
 		return NULL;
 	}
 
+	if (!g_ascii_strcasecmp (str, "eap"))
+		return read_wpa_eap_settings (ifcfg);
+
 	security = NM_SETTING_WIRELESS_SECURITY (nm_setting_wireless_security_new ());
 
 	if (!g_ascii_strcasecmp (str, "open")) {
@@ -500,18 +531,9 @@
 	} else if (!g_ascii_strcasecmp (str, "sharedkey")) {
 		security->auth_alg = g_strdup ("shared");
 		read_wep_settings (ifcfg, security);
-	}
-
-	else if (!g_ascii_strcasecmp (str, "psk")) {
+	} else if (!g_ascii_strcasecmp (str, "psk")) {
 		security->key_mgmt = g_strdup ("wpa-psk");
 		read_wpa_psk_settings (ifcfg, security, s_wireless);
-	} else if (!g_ascii_strcasecmp (str, "eap")) {
-		/* FIXME */
-/* 		security->key_mgmt = g_strdup ("wps-eap"); */
-/* 		read_wpa_eap_settings (ifcfg, security); */
-		g_warning ("WPA-EAP is currently not supported.");
-		g_object_unref (security);
-		security = NULL;
 	} else
 		g_warning ("Invalid authentication algorithm: '%s'", str);
 



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]