[PATCH 2/2] Added OpenSSL crypto backend
- From: Joel Holdsworth <joel holdsworth vcatechnology com>
- To: networkmanager-list gnome org
- Subject: [PATCH 2/2] Added OpenSSL crypto backend
- Date: Thu, 19 Nov 2015 00:01:09 +0000
---
configure.ac | 16 ++-
libnm-core/Makefile.am | 6 +
libnm-core/crypto_openssl.c | 324 ++++++++++++++++++++++++++++++++++++++++++++
po/POTFILES.in | 1 +
4 files changed, 340 insertions(+), 7 deletions(-)
create mode 100644 libnm-core/crypto_openssl.c
diff --git a/configure.ac b/configure.ac
index 7b4ca9a..83ae5a0 100644
--- a/configure.ac
+++ b/configure.ac
@@ -594,10 +594,12 @@ else
fi
AC_SUBST(NM_MODIFY_SYSTEM_POLICY)
-AC_ARG_WITH(crypto, AS_HELP_STRING([--with-crypto=nss|gnutls], [Cryptography library to use for certificate
and key operations]),ac_crypto=$withval, ac_crypto=nss)
+AC_ARG_WITH(crypto, AS_HELP_STRING([--with-crypto=nss|gnutls|openssl],
+ [Cryptography library to use for certificate and key operations]), ac_crypto=$withval, ac_crypto=nss)
with_nss=no
with_gnutls=no
+with_openssl=no
if test x"$ac_crypto" = xnss; then
PKG_CHECK_MODULES(NSS, [nss >= 3.11])
@@ -613,16 +615,16 @@ elif test x"$ac_crypto" = xgnutls; then
PKG_CHECK_MODULES(GNUTLS, [gnutls >= 2.12])
AC_DEFINE(HAVE_GNUTLS, 1, [Define if you have libgnutls])
with_gnutls=yes
+elif test x"$ac_crypto" = xopenssl; then
+ PKG_CHECK_MODULES(OPENSSL, [libcrypto >= 1.0.1a])
+ AC_DEFINE(HAVE_OPENSSL, 1, [Define if you have OpenSSL])
+ with_openssl=yes
else
- AC_MSG_ERROR([Please choose either 'nss' or 'gnutls' for certificate and crypto operations])
+ AC_MSG_ERROR([Please choose either 'nss', 'gnutls', 'openssl' for certificate and crypto operations])
fi
AM_CONDITIONAL(WITH_NSS, test x"$with_nss" != xno)
AM_CONDITIONAL(WITH_GNUTLS, test x"$with_gnutls" != xno)
-
-# Shouldn't ever trigger this, but just in case...
-if test x"$ac_nss" = xno -a x"$ac_gnutls" = xno; then
- AC_MSG_ERROR([Could not find required development headers and libraries for '$ac_crypto'])
-fi
+AM_CONDITIONAL(WITH_OPENSSL, test x"$with_openssl" != xno)
GLIB_MAKEFILE='$(top_srcdir)/Makefile.glib'
AC_SUBST(GLIB_MAKEFILE)
diff --git a/libnm-core/Makefile.am b/libnm-core/Makefile.am
index 55ed2f6..23fba5b 100644
--- a/libnm-core/Makefile.am
+++ b/libnm-core/Makefile.am
@@ -47,5 +47,11 @@ libnm_core_la_SOURCES += crypto_nss.c
libnm_core_la_LIBADD += $(NSS_LIBS)
endif
+if WITH_OPENSSL
+AM_CPPFLAGS += $(OPENSSL_CFLAGS)
+libnm_core_la_SOURCES += crypto_openssl.c
+libnm_core_la_LIBADD += $(OPENSSL_LIBS)
+endif
+
BUILT_SOURCES = $(GLIB_GENERATED)
CLEANFILES = $(BUILT_SOURCES)
diff --git a/libnm-core/crypto_openssl.c b/libnm-core/crypto_openssl.c
new file mode 100644
index 0000000..2952abb
--- /dev/null
+++ b/libnm-core/crypto_openssl.c
@@ -0,0 +1,324 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+
+/*
+ * Joel Holdsworth <joel holdsworth vcatechnology com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ *
+ * Copyright 2015 VCA Technology Ltd.
+ */
+
+#include "config.h"
+
+#include <openssl/engine.h>
+#include <openssl/evp.h>
+#include <openssl/pkcs12.h>
+#include <openssl/rand.h>
+#include <openssl/ssl.h>
+
+#include "nm-default.h"
+#include "crypto.h"
+#include "nm-errors.h"
+
+static gboolean initialized = FALSE;
+
+gboolean
+crypto_init (GError **error)
+{
+ if (initialized)
+ return TRUE;
+
+ CRYPTO_malloc_init();
+ OpenSSL_add_all_algorithms();
+ ENGINE_load_builtin_engines();
+
+ initialized = TRUE;
+ return TRUE;
+}
+
+static const EVP_CIPHER*
+get_cipher (const char *cipher,
+ GError **error)
+{
+ if (strcmp (cipher, CIPHER_DES_EDE3_CBC) == 0)
+ return EVP_des_ede3_cbc();
+ else if (strcmp (cipher, CIPHER_DES_CBC) == 0)
+ return EVP_des_cbc();
+ else if (strcmp (cipher, CIPHER_AES_CBC) == 0)
+ return EVP_aes_128_cbc();
+ else {
+ g_set_error (error, NM_CRYPTO_ERROR,
+ NM_CRYPTO_ERROR_UNKNOWN_CIPHER,
+ _("Private key cipher '%s' was unknown."),
+ cipher);
+ return NULL;
+ }
+}
+
+char *
+crypto_decrypt (const char *cipher,
+ int key_type,
+ const guint8 *data,
+ gsize data_len,
+ const char *iv,
+ const gsize iv_len,
+ const char *key,
+ const gsize key_len,
+ gsize *out_len,
+ GError **error)
+{
+ const EVP_CIPHER *evp_cipher = NULL;
+ EVP_CIPHER_CTX ctx;
+ char *output = NULL;
+ gboolean success = FALSE;
+ gsize real_iv_len = 0;
+ int initial_len = 0, final_len = 0;
+
+ if (!(evp_cipher = get_cipher(cipher, error)))
+ return NULL;
+
+ real_iv_len = EVP_CIPHER_iv_length(evp_cipher);
+ if (iv_len < real_iv_len) {
+ g_set_error (error, NM_CRYPTO_ERROR,
+ NM_CRYPTO_ERROR_INVALID_DATA,
+ _("Invalid IV length (must be at least %zd)."),
+ real_iv_len);
+ return NULL;
+ }
+
+ EVP_CIPHER_CTX_init(&ctx);
+ if (!EVP_DecryptInit_ex(&ctx, evp_cipher, NULL, (const unsigned char*)key, (const unsigned char*)iv))
{
+ g_set_error (error, NM_CRYPTO_ERROR,
+ NM_CRYPTO_ERROR_DECRYPTION_FAILED,
+ _("Failed to initialize the decryption cipher context."));
+ goto out;
+ }
+
+ output = g_malloc0 (data_len);
+
+ if (!EVP_DecryptUpdate(&ctx, (unsigned char*)output, &initial_len, data, data_len)) {
+ g_set_error (error, NM_CRYPTO_ERROR,
+ NM_CRYPTO_ERROR_DECRYPTION_FAILED,
+ _("Failed to decrypt the private key."));
+ goto out;
+ }
+
+ /* Finalise decryption, and check the padding */
+ if (!EVP_DecryptFinal_ex(&ctx, (unsigned char*)output + initial_len, &final_len)) {
+ g_set_error (error, NM_CRYPTO_ERROR,
+ NM_CRYPTO_ERROR_DECRYPTION_FAILED,
+ _("Failed to finalize decryption of the private key."));
+ goto out;
+ }
+
+ *out_len = initial_len + final_len;
+ success = TRUE;
+
+out:
+ if (!success && output) {
+ /* Don't expose key material */
+ memset (output, 0, data_len);
+ g_free (output);
+ output = NULL;
+ }
+ EVP_CIPHER_CTX_cleanup(&ctx);
+ return output;
+}
+
+char *
+crypto_encrypt (const char *cipher,
+ const guint8 *data,
+ gsize data_len,
+ const char *iv,
+ const gsize iv_len,
+ const char *key,
+ gsize key_len,
+ gsize *out_len,
+ GError **error)
+{
+ const EVP_CIPHER *evp_cipher = NULL;
+ EVP_CIPHER_CTX ctx;
+ char *output = NULL;
+ gboolean success = FALSE;
+ gsize pad_len, output_len;
+ int initial_len = 0, final_len = 0;
+
+ if (!(evp_cipher = get_cipher(cipher, error)))
+ return NULL;
+
+ /* If data_len % ivlen == 0, then we add another complete block
+ * onto the end so that the decrypter knows there's padding.
+ */
+ pad_len = iv_len - (data_len % iv_len);
+ output_len = data_len + pad_len;
+ output = g_malloc0(output_len);
+
+ EVP_CIPHER_CTX_init(&ctx);
+ if (!EVP_EncryptInit_ex(&ctx, evp_cipher, NULL, (const unsigned char*)key, (const unsigned char*)iv))
{
+ g_set_error (error, NM_CRYPTO_ERROR,
+ NM_CRYPTO_ERROR_DECRYPTION_FAILED,
+ _("Failed to initialize the encryption cipher context."));
+ goto out;
+ }
+
+ if (!EVP_EncryptUpdate(&ctx, (unsigned char*)output, &initial_len, data, data_len)) {
+ g_set_error (error, NM_CRYPTO_ERROR,
+ NM_CRYPTO_ERROR_DECRYPTION_FAILED,
+ _("Failed to encrypt the private key."));
+ goto out;
+ }
+
+ /* Finalise encryption, and add the padding */
+ if (!EVP_EncryptFinal_ex(&ctx, (unsigned char*)output + initial_len, &final_len)) {
+ g_set_error (error, NM_CRYPTO_ERROR,
+ NM_CRYPTO_ERROR_DECRYPTION_FAILED,
+ _("Failed to finalize encryption of the private key."));
+ goto out;
+ }
+
+ *out_len = initial_len + final_len;
+ success = TRUE;
+
+out:
+ if (!success && output) {
+ /* Don't expose key material */
+ memset (output, 0, output_len);
+ g_free (output);
+ output = NULL;
+ }
+ EVP_CIPHER_CTX_cleanup(&ctx);
+ return output;
+}
+
+NMCryptoFileFormat
+crypto_verify_cert (const unsigned char *data,
+ gsize len,
+ GError **error)
+{
+ BIO *in = NULL;
+ X509 *x = NULL;
+
+ /* Try PEM */
+ in = BIO_new_mem_buf((void*)data, len);
+ x = PEM_read_bio_X509_AUX(in, NULL, NULL, NULL);
+ BIO_free(in);
+ X509_free(x);
+ if (x)
+ return NM_CRYPTO_FILE_FORMAT_X509;
+
+ /* Try DER */
+ in = BIO_new_mem_buf((void*)data, len);
+ x = d2i_X509_bio(in, NULL);
+ BIO_free(in);
+ X509_free(x);
+ if (x)
+ return NM_CRYPTO_FILE_FORMAT_X509;
+
+ g_set_error (error, NM_CRYPTO_ERROR,
+ NM_CRYPTO_ERROR_INVALID_DATA,
+ _("Couldn't decode certificate"));
+ return NM_CRYPTO_FILE_FORMAT_UNKNOWN;
+}
+
+gboolean
+crypto_verify_pkcs12 (const guint8 *data,
+ gsize data_len,
+ const char *password,
+ GError **error)
+{
+ BIO *in = NULL;
+ PKCS12 *p12 = NULL;
+ gboolean success = FALSE;
+
+ g_return_val_if_fail (data != NULL, FALSE);
+
+ in = BIO_new_mem_buf((void*)data, data_len);
+ p12 = d2i_PKCS12_bio(in, NULL);
+ BIO_free(in);
+
+ if (!p12) {
+ /* Currently only DER format PKCS12 files are supported. */
+ g_set_error (error, NM_CRYPTO_ERROR,
+ NM_CRYPTO_ERROR_INVALID_DATA,
+ _("Couldn't decode PKCS#12 file"));
+ goto out;
+ }
+
+ if (password) {
+ if (!(success = PKCS12_verify_mac(p12, password, -1)))
+ g_set_error (error, NM_CRYPTO_ERROR,
+ NM_CRYPTO_ERROR_DECRYPTION_FAILED,
+ _("Couldn't verify PKCS#12 file."));
+ } else
+ success = TRUE;
+
+out:
+ if (p12)
+ PKCS12_free(p12);
+ return success;
+}
+
+gboolean
+crypto_verify_pkcs8 (const guint8 *data,
+ gsize data_len,
+ gboolean is_encrypted,
+ const char *password,
+ GError **error)
+{
+ BIO *in = NULL;
+ X509_SIG *p8 = NULL;
+ PKCS8_PRIV_KEY_INFO *p8inf = NULL;
+
+ g_return_val_if_fail (data != NULL, FALSE);
+
+ if (is_encrypted) {
+ in = BIO_new_mem_buf((void*)data, data_len);
+ p8 = d2i_PKCS8_bio(in, NULL);
+ BIO_free(in);
+
+ if (p8) {
+ X509_SIG_free(p8);
+ return TRUE;
+ } else {
+ g_set_error (error, NM_CRYPTO_ERROR,
+ NM_CRYPTO_ERROR_INVALID_DATA,
+ _("Couldn't decode PKCS#8 file"));
+ }
+ } else {
+ in = BIO_new_mem_buf((void*)data, data_len);
+ p8inf = d2i_PKCS8_PRIV_KEY_INFO_bio(in, NULL);
+ BIO_free(in);
+
+ if (p8inf) {
+ PKCS8_PRIV_KEY_INFO_free(p8inf);
+ return p8inf->broken == 0;
+ } else {
+ g_set_error (error, NM_CRYPTO_ERROR,
+ NM_CRYPTO_ERROR_INVALID_DATA,
+ _("Couldn't decode PKCS#8 file"));
+ }
+ }
+
+ return FALSE;
+}
+
+gboolean
+crypto_randomize (void *buffer, gsize buffer_len, GError **error)
+{
+ RAND_bytes(buffer, buffer_len);
+ buffer_len = (buffer_len > 16) ? 16 : buffer_len;
+ return TRUE;
+}
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 8bffd70..759e222 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -47,6 +47,7 @@ clients/tui/nmtui.c
libnm-core/crypto.c
libnm-core/crypto_gnutls.c
libnm-core/crypto_nss.c
+libnm-core/crypto_openssl.c
libnm-core/nm-connection.c
libnm-core/nm-dbus-utils.c
libnm-core/nm-keyfile-reader.c
--
1.9.1
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]