mirror of
https://gitlab.freedesktop.org/NetworkManager/NetworkManager
synced 2024-10-15 12:34:55 +00:00
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. git-svn-id: http://svn-archive.gnome.org/svn/NetworkManager/trunk@3673 4912f4e0-d625-0410-9fb7-b9a5a253dbdc
This commit is contained in:
parent
192fe93b3a
commit
40a69f986c
21
ChangeLog
21
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
|
||||
|
|
38
configure.in
38
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)
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
547
libnm-util/crypto.c
Normal file
547
libnm-util/crypto.c
Normal file
|
@ -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;
|
||||
}
|
||||
|
92
libnm-util/crypto.h
Normal file
92
libnm-util/crypto.h
Normal file
|
@ -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);
|
||||
|
||||
|
227
libnm-util/crypto_gnutls.c
Normal file
227
libnm-util/crypto_gnutls.c
Normal file
|
@ -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;
|
||||
}
|
||||
|
256
libnm-util/crypto_nss.c
Normal file
256
libnm-util/crypto_nss.c
Normal file
|
@ -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;
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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 */
|
||||
|
|
171
libnm-util/test-crypto.c
Normal file
171
libnm-util/test-crypto.c
Normal file
|
@ -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;
|
||||
}
|
||||
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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");
|
||||
|
||||
char *ca_path;
|
||||
|
||||
GByteArray *ca_cert;
|
||||
"WIRELESS_CA_CERT";
|
||||
|
||||
GByteArray *client_cert;
|
||||
"WIRELESS_CLIENT_CERT";
|
||||
|
||||
GByteArray *private_key;
|
||||
"WIRELESS_CLIENT_KEY";
|
||||
|
||||
private_key_passwd
|
||||
"WIRELESS_CLIENT_KEY_PASSWORD";
|
||||
|
||||
|
||||
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");
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
g_free (str);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
g_free (password);
|
||||
} else
|
||||
g_warning ("Missing WIRELESS_CLIENT_KEY_PASSWORD");
|
||||
|
||||
g_free (str);
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
|
|
Loading…
Reference in a new issue