[PATCH 2/2] Added OpenSSL crypto backend



---
 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]