summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog21
-rw-r--r--configure.in38
-rw-r--r--libnm-util/Makefile.am22
-rw-r--r--libnm-util/crypto.c547
-rw-r--r--libnm-util/crypto.h92
-rw-r--r--libnm-util/crypto_gnutls.c227
-rw-r--r--libnm-util/crypto_nss.c256
-rw-r--r--libnm-util/nm-setting-8021x.c121
-rw-r--r--libnm-util/nm-setting-8021x.h26
-rw-r--r--libnm-util/test-crypto.c171
-rw-r--r--po/POTFILES.in3
-rw-r--r--system-settings/plugins/ifcfg-suse/parser.c80
12 files changed, 1575 insertions, 29 deletions
diff --git a/ChangeLog b/ChangeLog
index 7a55f44a09..ff245e8d8f 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,24 @@
+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.
+
2008-05-16 Dan Williams <dcbw@redhat.com>
* src/nm-device-802-11-wireless.c
diff --git a/configure.in b/configure.in
index 9dccbebb01..6e469ff52a 100644
--- a/configure.in
+++ b/configure.in
@@ -261,6 +261,44 @@ AC_SUBST(POLKIT_CFLAGS)
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)
diff --git a/libnm-util/Makefile.am b/libnm-util/Makefile.am
index 665372810e..e9f25b6de4 100644
--- a/libnm-util/Makefile.am
+++ b/libnm-util/Makefile.am
@@ -28,6 +28,8 @@ libnm_util_include_HEADERS = \
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_LDFLAGS= $(GLIB_LIBS) $(DBUS_LIBS)
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
diff --git a/libnm-util/crypto.c b/libnm-util/crypto.c
new file mode 100644
index 0000000000..f569aee767
--- /dev/null
+++ b/libnm-util/crypto.c
@@ -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;
+}
+
diff --git a/libnm-util/crypto.h b/libnm-util/crypto.h
new file mode 100644
index 0000000000..d970ce1326
--- /dev/null
+++ b/libnm-util/crypto.h
@@ -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);
+
+
diff --git a/libnm-util/crypto_gnutls.c b/libnm-util/crypto_gnutls.c
new file mode 100644
index 0000000000..25bc6fde7f
--- /dev/null
+++ b/libnm-util/crypto_gnutls.c
@@ -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;
+}
+
diff --git a/libnm-util/crypto_nss.c b/libnm-util/crypto_nss.c
new file mode 100644
index 0000000000..4e5d83d772
--- /dev/null
+++ b/libnm-util/crypto_nss.c
@@ -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;
+}
+
diff --git a/libnm-util/nm-setting-8021x.c b/libnm-util/nm-setting-8021x.c
index 536736c12a..3205a36c39 100644
--- a/libnm-util/nm-setting-8021x.c
+++ b/libnm-util/nm-setting-8021x.c
@@ -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 @@ nm_setting_802_1x_new (void)
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 @@ nm_setting_802_1x_class_init (NMSetting8021xClass *setting_class)
{
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 @@ nm_setting_802_1x_class_init (NMSetting8021xClass *setting_class)
"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);
+ }
+
}
diff --git a/libnm-util/nm-setting-8021x.h b/libnm-util/nm-setting-8021x.h
index d3283a3d98..babf93f7cb 100644
--- a/libnm-util/nm-setting-8021x.h
+++ b/libnm-util/nm-setting-8021x.h
@@ -68,6 +68,32 @@ GType nm_setting_802_1x_get_type (void);
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 */
diff --git a/libnm-util/test-crypto.c b/libnm-util/test-crypto.c
new file mode 100644
index 0000000000..a237d59415
--- /dev/null
+++ b/libnm-util/test-crypto.c
@@ -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;
+}
+
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 9d8bb4335e..249b8bd172 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -6,4 +6,7 @@ src/NetworkManager.c
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
diff --git a/system-settings/plugins/ifcfg-suse/parser.c b/system-settings/plugins/ifcfg-suse/parser.c
index 0b0aef8183..d2c7e20549 100644
--- a/system-settings/plugins/ifcfg-suse/parser.c
+++ b/system-settings/plugins/ifcfg-suse/parser.c
@@ -436,12 +436,14 @@ read_wpa_psk_settings (shvarFile *ifcfg,
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 @@ read_wpa_eap_settings (shvarFile *ifcfg, NMSettingWirelessSecurity *security)
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);
+ }
- GByteArray *ca_cert;
- "WIRELESS_CA_CERT";
+ 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 *client_cert;
- "WIRELESS_CLIENT_CERT";
+ g_free (str);
+ }
- GByteArray *private_key;
- "WIRELESS_CLIENT_KEY";
+ str = svGetValue (ifcfg, "WIRELESS_CLIENT_KEY");
+ if (str) {
+ char *password;
+
+ 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 @@ make_wireless_security_setting (shvarFile *ifcfg, NMSettingWireless *s_wireless)
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 @@ make_wireless_security_setting (shvarFile *ifcfg, NMSettingWireless *s_wireless)
} 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);