summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Makefile.am7
-rw-r--r--README.polarssl23
-rw-r--r--configure.ac67
-rw-r--r--crypto_backend.h4
-rw-r--r--crypto_polarssl.c577
-rw-r--r--crypto_polarssl.h71
-rw-r--r--doxygen/doc_key_generation.h11
-rw-r--r--openvpn-plugin.h3
-rw-r--r--options.c25
-rw-r--r--pkcs11_polarssl.c121
-rw-r--r--ssl_backend.h4
-rw-r--r--ssl_polarssl.c848
-rw-r--r--ssl_polarssl.h85
-rw-r--r--ssl_verify.c2
-rw-r--r--ssl_verify.h3
-rw-r--r--ssl_verify_polarssl.c446
-rw-r--r--ssl_verify_polarssl.h79
17 files changed, 2348 insertions, 28 deletions
diff --git a/Makefile.am b/Makefile.am
index 0108d8f..3228798 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -160,6 +160,13 @@ openvpn_SOURCES += \
ssl_openssl.c ssl_openssl.h \
ssl_verify_openssl.c ssl_verify_openssl.h
endif
+if USE_POLARSSL
+openvpn_SOURCES += \
+ crypto_polarssl.c crypto_polarssl.h \
+ pkcs11_polarssl.c \
+ ssl_polarssl.c ssl_polarssl.h \
+ ssl_verify_polarssl.c ssl_verify_polarssl.h
+endif
dist-hook:
cd $(distdir) && for i in $(EXTRA_DIST) $(SUBDIRS) ; do find $$i -name .svn -type d -prune -exec rm -rf '{}' ';' ; rm -f `find $$i -type f | grep -E '(^|\/)\.?\#|\~$$|\.s?o$$'` ; done
diff --git a/README.polarssl b/README.polarssl
new file mode 100644
index 0000000..bd066e4
--- /dev/null
+++ b/README.polarssl
@@ -0,0 +1,23 @@
+This version of OpenVPN has PolarSSL support. To enable follow the following
+instructions:
+
+To Build and Install,
+
+ ./configure --with-ssl-type=polarssl
+ make
+ make install
+
+*************************************************************************
+
+The following features are missing in the PolarSSL version of OpenVPN:
+
+ * ca_path support - Loading certificate authorities from a directory
+ * PKCS#12 file support
+ * Windows CryptoAPI support
+ * Management external key support
+ * X509 alternative username fields (must be "CN")
+
+ TODO:
+ * serial is in Hex
+ * X509 certificate export
+ * X.509 tracking
diff --git a/configure.ac b/configure.ac
index b80439b..bbdd713 100644
--- a/configure.ac
+++ b/configure.ac
@@ -291,14 +291,16 @@ AC_ARG_WITH(mem-check,
)
AC_ARG_WITH([ssl-type],
- [ --with-ssl-type=TYPE Build with the given SSL library, TYPE = openssl ],
+ [ --with-ssl-type=TYPE Build with the given SSL library, TYPE = openssl or polarssl ],
[case "${withval}" in
openssl) SSL_LIB=openssl ;;
+ polarssl) SSL_LIB=polarssl ;;
*) AC_MSG_ERROR([bad value ${withval} for --with-ssl-type]) ;;
esac],
[SSL_LIB="openssl"]
)
AM_CONDITIONAL([USE_OPENSSL], [test x$SSL_LIB = xopenssl])
+AM_CONDITIONAL([USE_POLARSSL], [test x$SSL_LIB = xpolarssl])
dnl fix search path, to allow compilers to find syshead.h
CPPFLAGS="$CPPFLAGS -I${srcdir}"
@@ -710,6 +712,24 @@ if test "$LZO_STUB" = "yes"; then
fi
dnl
+dnl enable pkcs11 capability
+dnl
+
+if test "$PKCS11" = "yes"; then
+ AC_CHECKING([for pkcs11-helper Library and Header files])
+ AC_CHECK_HEADER(pkcs11-helper-1.0/pkcs11h-core.h,
+ [AC_CHECK_LIB(pkcs11-helper, pkcs11h_initialize,
+ [
+ AC_DEFINE(USE_PKCS11, 1, [Enable PKCS11 capability])
+ OPENVPN_ADD_LIBS(-lpkcs11-helper)
+ ],
+ [AC_MSG_RESULT([pkcs11-helper library not found.])]
+ )],
+ [AC_MSG_RESULT([pkcs11-helper headers not found.])]
+ )
+fi
+
+dnl
dnl check for SSL-crypto library
dnl
@@ -752,7 +772,20 @@ if test "$CRYPTO" = "yes"; then
[AC_MSG_ERROR([OpenSSL crypto Library is too old.])]
)
fi
-
+ if test "$SSL_LIB" = "polarssl"; then
+ AC_CHECKING([for PolarSSL Crypto Library and Header files])
+ AC_CHECK_HEADER(polarssl/aes.h,
+ [AC_CHECK_LIB(polarssl, aes_crypt_cbc,
+ [
+ OPENVPN_ADD_LIBS(-lpolarssl)
+ AC_DEFINE(USE_CRYPTO, 1, [Use crypto library])
+ AC_DEFINE(USE_POLARSSL, 1, [Use PolarSSL library])
+ ],
+ [AC_MSG_ERROR([PolarSSL Crypto library not found.])]
+ )],
+ [AC_MSG_ERROR([PolarSSL Crypto headers not found.])]
+ )
+ fi
dnl
dnl check for OpenSSL-SSL library
dnl
@@ -788,6 +821,20 @@ if test "$CRYPTO" = "yes"; then
AC_DEFINE(USE_SSL, 1, [Use OpenSSL SSL library])
fi
+ if test "$SSL_LIB" = "polarssl"; then
+ AC_CHECKING([for PolarSSL SSL Library and Header files])
+ AC_CHECK_HEADER(polarssl/ssl.h,
+ [AC_CHECK_LIB(polarssl, ssl_init,
+ [
+ OPENVPN_ADD_LIBS(-lpolarssl)
+ AC_DEFINE(USE_SSL, 1, [Use SSL library])
+ AC_DEFINE(USE_POLARSSL, 1, [Use PolarSSL library])
+ ],
+ [AC_MSG_ERROR([PolarSSL SSL library not found.])]
+ )],
+ [AC_MSG_ERROR([PolarSSL SSL headers not found.])]
+ )
+ fi
fi
fi
@@ -796,22 +843,6 @@ if test "$X509ALTUSERNAME" = "yes"; then
AC_DEFINE(ENABLE_X509ALTUSERNAME, 1, [Enable --x509-username-field feature])
fi
-dnl enable pkcs11 capability
-
-if test "$PKCS11" = "yes"; then
- AC_CHECKING([for pkcs11-helper Library and Header files])
- AC_CHECK_HEADER(pkcs11-helper-1.0/pkcs11h-core.h,
- [AC_CHECK_LIB(pkcs11-helper, pkcs11h_initialize,
- [
- AC_DEFINE(USE_PKCS11, 1, [Enable PKCS11 capability])
- OPENVPN_ADD_LIBS(-lpkcs11-helper)
- ],
- [AC_MSG_RESULT([pkcs11-helper library not found.])]
- )],
- [AC_MSG_RESULT([pkcs11-helper headers not found.])]
- )
-fi
-
dnl enable multi-client mode
if test "$MULTI" = "yes"; then
AC_DEFINE(ENABLE_CLIENT_SERVER, 1, [Enable client/server capability])
diff --git a/crypto_backend.h b/crypto_backend.h
index 8d1f37e..71c3f47 100644
--- a/crypto_backend.h
+++ b/crypto_backend.h
@@ -35,7 +35,9 @@
#ifdef USE_OPENSSL
#include "crypto_openssl.h"
#endif
-
+#ifdef USE_POLARSSL
+#include "crypto_polarssl.h"
+#endif
#include "basic.h"
diff --git a/crypto_polarssl.c b/crypto_polarssl.c
new file mode 100644
index 0000000..59aade8
--- /dev/null
+++ b/crypto_polarssl.c
@@ -0,0 +1,577 @@
+/*
+ * OpenVPN -- An application to securely tunnel IP networks
+ * over a single TCP/UDP port, with support for SSL/TLS-based
+ * session authentication and key exchange,
+ * packet encryption, packet authentication, and
+ * packet compression.
+ *
+ * Copyright (C) 2002-2010 OpenVPN Technologies, Inc. <sales@openvpn.net>
+ * Copyright (C) 2010 Fox Crypto B.V. <openvpn@fox-it.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ *
+ * 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 (see the file COPYING included with this
+ * distribution); if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/**
+ * @file Data Channel Cryptography PolarSSL-specific backend interface
+ */
+
+#include "syshead.h"
+
+#include "errlevel.h"
+#include "basic.h"
+#include "buffer.h"
+#include "integer.h"
+#include "crypto_backend.h"
+
+#include <polarssl/des.h>
+#include <polarssl/md5.h>
+#include <polarssl/cipher.h>
+#include <polarssl/havege.h>
+
+/*
+ *
+ * Hardware engine support. Allows loading/unloading of engines.
+ *
+ */
+
+void
+crypto_init_lib_engine (const char *engine_name)
+{
+ msg (M_WARN, "Note: PolarSSL hardware crypto engine functionality is not "
+ "available");
+}
+
+/*
+ *
+ * Functions related to the core crypto library
+ *
+ */
+
+void
+crypto_init_lib (void)
+{
+}
+
+void
+crypto_uninit_lib (void)
+{
+ prng_uninit();
+}
+
+void
+crypto_clear_error (void)
+{
+}
+
+#ifdef DMALLOC
+void
+crypto_init_dmalloc (void)
+{
+ msg (M_ERR, "Error: dmalloc support is not available for PolarSSL.");
+}
+#endif /* DMALLOC */
+
+void
+show_available_ciphers ()
+{
+ const int *ciphers = cipher_list();
+
+#ifndef ENABLE_SMALL
+ printf ("The following ciphers and cipher modes are available\n"
+ "for use with " PACKAGE_NAME ". Each cipher shown below may be\n"
+ "used as a parameter to the --cipher option. The default\n"
+ "key size is shown as well as whether or not it can be\n"
+ "changed with the --keysize directive. Using a CBC mode\n"
+ "is recommended.\n\n");
+#endif
+
+ while (*ciphers != 0)
+ {
+ const cipher_info_t *info = cipher_info_from_type(*ciphers);
+
+ if (info && info->mode == POLARSSL_MODE_CBC)
+ printf ("%s %d bit default key\n",
+ info->name, info->key_length);
+
+ ciphers++;
+ }
+ printf ("\n");
+}
+
+void
+show_available_digests ()
+{
+ const int *digests = md_list();
+
+#ifndef ENABLE_SMALL
+ printf ("The following message digests are available for use with\n"
+ PACKAGE_NAME ". A message digest is used in conjunction with\n"
+ "the HMAC function, to authenticate received packets.\n"
+ "You can specify a message digest as parameter to\n"
+ "the --auth option.\n\n");
+#endif
+
+ while (*digests != 0)
+ {
+ const md_info_t *info = md_info_from_type(*digests);
+
+ if (info)
+ printf ("%s %d bit default key\n",
+ info->name, info->size * 8);
+ digests++;
+ }
+ printf ("\n");
+}
+
+void
+show_available_engines ()
+{
+ printf ("Sorry, PolarSSL hardware crypto engine functionality is not "
+ "available\n");
+}
+
+
+/*
+ *
+ * Random number functions, used in cases where we want
+ * reasonably strong cryptographic random number generation
+ * without depleting our entropy pool. Used for random
+ * IV values and a number of other miscellaneous tasks.
+ *
+ */
+
+int
+rand_bytes (uint8_t *output, int len)
+{
+ static havege_state hs = {0};
+ static bool hs_initialised = false;
+ const int int_size = sizeof(int);
+
+ if (!hs_initialised)
+ {
+ /* Initialise PolarSSL RNG */
+ havege_init(&hs);
+ hs_initialised = true;
+ }
+
+ while (len > 0)
+ {
+ const int blen = min_int (len, int_size);
+ const int rand_int = havege_rand(&hs);
+
+ memcpy (output, &rand_int, blen);
+ output += blen;
+ len -= blen;
+ }
+ return 1;
+}
+
+/*
+ *
+ * Key functions, allow manipulation of keys.
+ *
+ */
+
+
+int
+key_des_num_cblocks (const cipher_info_t *kt)
+{
+ int ret = 0;
+ if (kt->type == POLARSSL_CIPHER_DES_CBC)
+ ret = 1;
+ if (kt->type == POLARSSL_CIPHER_DES_EDE_CBC)
+ ret = 2;
+ if (kt->type == POLARSSL_CIPHER_DES_EDE3_CBC)
+ ret = 3;
+
+ dmsg (D_CRYPTO_DEBUG, "CRYPTO INFO: n_DES_cblocks=%d", ret);
+ return ret;
+}
+
+bool
+key_des_check (uint8_t *key, int key_len, int ndc)
+{
+ int i;
+ struct buffer b;
+
+ buf_set_read (&b, key, key_len);
+
+ for (i = 0; i < ndc; ++i)
+ {
+ unsigned char *key = buf_read_alloc(&b, DES_KEY_SIZE);
+ if (!key)
+ {
+ msg (D_CRYPT_ERRORS, "CRYPTO INFO: check_key_DES: insufficient key material");
+ goto err;
+ }
+ if (0 != des_key_check_weak(key))
+ {
+ msg (D_CRYPT_ERRORS, "CRYPTO INFO: check_key_DES: weak key detected");
+ goto err;
+ }
+ if (0 != des_key_check_key_parity(key))
+ {
+ msg (D_CRYPT_ERRORS, "CRYPTO INFO: check_key_DES: bad parity detected");
+ goto err;
+ }
+ }
+ return true;
+
+ err:
+ return false;
+}
+
+void
+key_des_fixup (uint8_t *key, int key_len, int ndc)
+{
+ int i;
+ struct buffer b;
+
+ buf_set_read (&b, key, key_len);
+ for (i = 0; i < ndc; ++i)
+ {
+ unsigned char *key = buf_read_alloc(&b, DES_KEY_SIZE);
+ if (!key)
+ {
+ msg (D_CRYPT_ERRORS, "CRYPTO INFO: fixup_key_DES: insufficient key material");
+ return;
+ }
+ des_key_set_parity(key);
+ }
+}
+
+/*
+ *
+ * Generic cipher key type functions
+ *
+ */
+
+
+const cipher_info_t *
+cipher_kt_get (const char *ciphername)
+{
+ const cipher_info_t *cipher = NULL;
+
+ ASSERT (ciphername);
+
+ cipher = cipher_info_from_string(ciphername);
+
+ if (NULL == cipher)
+ msg (M_FATAL, "Cipher algorithm '%s' not found", ciphername);
+
+ if (cipher->key_length/8 > MAX_CIPHER_KEY_LENGTH)
+ msg (M_FATAL, "Cipher algorithm '%s' uses a default key size (%d bytes) which is larger than " PACKAGE_NAME "'s current maximum key size (%d bytes)",
+ ciphername,
+ cipher->key_length/8,
+ MAX_CIPHER_KEY_LENGTH);
+
+ return cipher;
+}
+
+const char *
+cipher_kt_name (const cipher_info_t *cipher_kt)
+{
+ if (NULL == cipher_kt)
+ return "[null-cipher]";
+ return cipher_kt->name;
+}
+
+int
+cipher_kt_key_size (const cipher_info_t *cipher_kt)
+{
+ if (NULL == cipher_kt)
+ return 0;
+ return cipher_kt->key_length/8;
+}
+
+int
+cipher_kt_iv_size (const cipher_info_t *cipher_kt)
+{
+ if (NULL == cipher_kt)
+ return 0;
+ return cipher_kt->iv_size;
+}
+
+int
+cipher_kt_block_size (const cipher_info_t *cipher_kt)
+{
+ if (NULL == cipher_kt)
+ return 0;
+ return cipher_kt->block_size;
+}
+
+bool
+cipher_kt_mode (const cipher_info_t *cipher_kt)
+{
+ ASSERT(NULL != cipher_kt);
+ return cipher_kt->mode;
+}
+
+
+/*
+ *
+ * Generic cipher context functions
+ *
+ */
+
+
+void
+cipher_ctx_init (cipher_context_t *ctx, uint8_t *key, int key_len,
+ const cipher_info_t *kt, int enc, const char *prefix)
+{
+ struct gc_arena gc = gc_new ();
+
+ ASSERT(NULL != kt && NULL != ctx);
+
+ CLEAR (*ctx);
+
+ if (0 != cipher_init_ctx(ctx, kt))
+ msg (M_FATAL, "PolarSSL cipher context init #1");
+
+ if (0 != cipher_setkey(ctx, key, key_len*8, enc))
+ msg (M_FATAL, "PolarSSL cipher set key");
+
+ msg (D_HANDSHAKE, "%s: Cipher '%s' initialized with %d bit key",
+ prefix,
+ cipher_kt_name(kt),
+ cipher_get_key_size(ctx));
+
+ /* make sure we used a big enough key */
+ ASSERT (ctx->key_length <= key_len*8);
+
+ dmsg (D_SHOW_KEYS, "%s: CIPHER KEY: %s", prefix,
+ format_hex (key, key_len, 0, &gc));
+ dmsg (D_CRYPTO_DEBUG, "%s: CIPHER block_size=%d iv_size=%d",
+ prefix,
+ cipher_get_block_size(ctx),
+ cipher_get_iv_size(ctx));
+
+ gc_free (&gc);
+}
+
+void cipher_ctx_cleanup (cipher_context_t *ctx)
+{
+ cipher_free_ctx(ctx);
+}
+
+int cipher_ctx_iv_length (const cipher_context_t *ctx)
+{
+ return cipher_get_iv_size(ctx);
+}
+
+int cipher_ctx_block_size(const cipher_context_t *ctx)
+{
+ return cipher_get_block_size(ctx);
+}
+
+int cipher_ctx_mode (const cipher_context_t *ctx)
+{
+ ASSERT(NULL != ctx);
+
+ return cipher_kt_mode(ctx->cipher_info);
+}
+
+int cipher_ctx_reset (cipher_context_t *ctx, uint8_t *iv_buf)
+{
+ return 0 == cipher_reset(ctx, iv_buf);
+}
+
+int cipher_ctx_update (cipher_context_t *ctx, uint8_t *dst, int *dst_len,
+ uint8_t *src, int src_len)
+{
+ return 0 == cipher_update(ctx, src, src_len, dst, dst_len);
+}
+
+int cipher_ctx_final (cipher_context_t *ctx, uint8_t *dst, int *dst_len)
+{
+ return 0 == cipher_finish(ctx, dst, dst_len);
+}
+
+void
+cipher_des_encrypt_ecb (const unsigned char key[DES_KEY_SIZE],
+ unsigned char *src,
+ unsigned char *dst)
+{
+ des_context ctx;
+
+ des_setkey_enc(&ctx, key);
+ des_crypt_ecb(&ctx, src, dst);
+}
+
+
+
+/*
+ *
+ * Generic message digest information functions
+ *
+ */
+
+
+const md_info_t *
+md_kt_get (const char *digest)
+{
+ const md_info_t *md = NULL;
+ ASSERT (digest);
+
+ md = md_info_from_string(digest);
+ if (!md)
+ msg (M_FATAL, "Message hash algorithm '%s' not found", digest);
+ if (md->size > MAX_HMAC_KEY_LENGTH)
+ msg (M_FATAL, "Message hash algorithm '%s' uses a default hash size (%d bytes) which is larger than " PACKAGE_NAME "'s current maximum hash size (%d bytes)",
+ digest,
+ md->size,
+ MAX_HMAC_KEY_LENGTH);
+ return md;
+}
+
+const char *
+md_kt_name (const md_info_t *kt)
+{
+ if (NULL == kt)
+ return "[null-digest]";
+ return md_get_name (kt);
+}
+
+int
+md_kt_size (const md_info_t *kt)
+{
+ if (NULL == kt)
+ return 0;
+ return md_get_size(kt);
+}
+
+/*
+ *
+ * Generic message digest functions
+ *
+ */
+
+int
+md_full (const md_kt_t *kt, const uint8_t *src, int src_len, uint8_t *dst)
+{
+ return 0 == md(kt, src, src_len, dst);
+}
+
+
+void
+md_ctx_init (md_context_t *ctx, const md_info_t *kt)
+{
+ ASSERT(NULL != ctx && NULL != kt);
+
+ CLEAR(*ctx);
+
+ ASSERT(0 == md_starts(kt, ctx));
+}
+
+void
+md_ctx_cleanup(md_context_t *ctx)
+{
+ ASSERT(0 == md_free_ctx(ctx));
+}
+
+int
+md_ctx_size (const md_context_t *ctx)
+{
+ if (NULL == ctx)
+ return 0;
+ return md_get_size(ctx->md_info);
+}
+
+void
+md_ctx_update (md_context_t *ctx, const uint8_t *src, int src_len)
+{
+ ASSERT(0 == md_update(ctx, src, src_len));
+}
+
+void
+md_ctx_final (md_context_t *ctx, uint8_t *dst)
+{
+ ASSERT(0 == md_finish(ctx, dst));
+}
+
+
+/*
+ *
+ * Generic HMAC functions
+ *
+ */
+
+
+/*
+ * TODO: re-enable dmsg for crypto debug
+ */
+void
+hmac_ctx_init (md_context_t *ctx, const uint8_t *key, int key_len, const md_info_t *kt,
+ const char *prefix)
+{
+ struct gc_arena gc = gc_new ();
+
+ ASSERT(NULL != kt && NULL != ctx);
+
+ CLEAR(*ctx);
+
+ ASSERT(0 == md_hmac_starts(kt, ctx, key, key_len));
+
+ if (prefix)
+ msg (D_HANDSHAKE,
+ "%s: Using %d bit message hash '%s' for HMAC authentication",
+ prefix, md_get_size(kt) * 8, md_get_name(kt));
+
+ /* make sure we used a big enough key */
+ ASSERT (md_get_size(kt) <= key_len);
+
+ if (prefix)
+ dmsg (D_SHOW_KEYS, "%s: HMAC KEY: %s", prefix,
+ format_hex (key, key_len, 0, &gc));
+// if (prefix)
+// dmsg (D_CRYPTO_DEBUG, "%s: HMAC size=%d block_size=%d",
+// prefix,
+// md_get_size(md_info),
+// EVP_MD_block_size (md_info));
+
+ gc_free (&gc);
+}
+
+void
+hmac_ctx_cleanup(md_context_t *ctx)
+{
+ ASSERT(0 == md_free_ctx(ctx));
+}
+
+int
+hmac_ctx_size (const md_context_t *ctx)
+{
+ if (NULL == ctx)
+ return 0;
+ return md_get_size(ctx->md_info);
+}
+
+void
+hmac_ctx_reset (md_context_t *ctx)
+{
+ ASSERT(0 == md_hmac_reset(ctx));
+}
+
+void
+hmac_ctx_update (md_context_t *ctx, const uint8_t *src, int src_len)
+{
+ ASSERT(0 == md_hmac_update(ctx, src, src_len));
+}
+
+void
+hmac_ctx_final (md_context_t *ctx, uint8_t *dst)
+{
+ ASSERT(0 == md_hmac_finish(ctx, dst));
+}
diff --git a/crypto_polarssl.h b/crypto_polarssl.h
new file mode 100644
index 0000000..190c75d
--- /dev/null
+++ b/crypto_polarssl.h
@@ -0,0 +1,71 @@
+/*
+ * OpenVPN -- An application to securely tunnel IP networks
+ * over a single TCP/UDP port, with support for SSL/TLS-based
+ * session authentication and key exchange,
+ * packet encryption, packet authentication, and
+ * packet compression.
+ *
+ * Copyright (C) 2002-2010 OpenVPN Technologies, Inc. <sales@openvpn.net>
+ * Copyright (C) 2010 Fox Crypto B.V. <openvpn@fox-it.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ *
+ * 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 (see the file COPYING included with this
+ * distribution); if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/**
+ * @file Data Channel Cryptography PolarSSL-specific backend interface
+ */
+
+#ifndef CRYPTO_POLARSSL_H_
+#define CRYPTO_POLARSSL_H_
+
+#include <polarssl/cipher.h>
+#include <polarssl/md.h>
+
+/** Generic cipher key type %context. */
+typedef cipher_info_t cipher_kt_t;
+
+/** Generic message digest key type %context. */
+typedef md_info_t md_kt_t;
+
+/** Generic cipher %context. */
+typedef cipher_context_t cipher_ctx_t;
+
+/** Generic message digest %context. */
+typedef md_context_t md_ctx_t;
+
+/** Generic HMAC %context. */
+typedef md_context_t hmac_ctx_t;
+
+/** Maximum length of an IV */
+#define OPENVPN_MAX_IV_LENGTH POLARSSL_MAX_IV_LENGTH
+
+/** Cipher is in CBC mode */
+#define OPENVPN_MODE_CBC POLARSSL_MODE_CBC
+
+/** Cipher is in OFB mode */
+#define OPENVPN_MODE_OFB POLARSSL_MODE_OFB
+
+/** Cipher is in CFB mode */
+#define OPENVPN_MODE_CFB POLARSSL_MODE_CFB
+
+/** Cipher should encrypt */
+#define OPENVPN_OP_ENCRYPT POLARSSL_ENCRYPT
+
+/** Cipher should decrypt */
+#define OPENVPN_OP_DECRYPT POLARSSL_DECRYPT
+
+#define MD5_DIGEST_LENGTH 16
+
+#endif /* CRYPTO_POLARSSL_H_ */
diff --git a/doxygen/doc_key_generation.h b/doxygen/doc_key_generation.h
index 4e20d3a..903a465 100644
--- a/doxygen/doc_key_generation.h
+++ b/doxygen/doc_key_generation.h
@@ -79,9 +79,11 @@
*
* @subsection key_generation_random Source of random material
*
- * OpenVPN uses the OpenSSL library as its source of random material. More
- * specifically, the \c RAND_bytes() function is called to supply
- * cryptographically strong pseudo-random data. The following links
+ * OpenVPN uses the either the OpenSSL library or the PolarSSL library as its
+ * source of random material.
+ *
+ * In OpenSSL, the \c RAND_bytes() function is called
+ * to supply cryptographically strong pseudo-random data. The following links
* contain more information on this subject:
* - For OpenSSL's \c RAND_bytes() function:
* http://www.openssl.org/docs/crypto/RAND_bytes.html
@@ -90,6 +92,9 @@
* - For OpenSSL's support for external crypto modules:
* http://www.openssl.org/docs/crypto/engine.html
*
+ * In PolarSSL, the Havege random number generator is used. For details, see
+ * the PolarSSL documentation.
+ *
* @section key_generation_exchange Key exchange:
*
* The %key exchange process is initiated by the OpenVPN process running
diff --git a/openvpn-plugin.h b/openvpn-plugin.h
index 8bbafa2..9cce9d8 100644
--- a/openvpn-plugin.h
+++ b/openvpn-plugin.h
@@ -28,6 +28,9 @@
#ifdef USE_OPENSSL
#include "ssl_verify_openssl.h"
#endif
+#ifdef USE_POLARSSL
+#include "ssl_verify_polarssl.h"
+#endif
#define OPENVPN_PLUGIN_VERSION 3
diff --git a/options.c b/options.c
index f055185..9ed32b6 100644
--- a/options.c
+++ b/options.c
@@ -508,7 +508,9 @@ static const char usage_message[] =
"--keysize n : Size of cipher key in bits (optional).\n"
" If unspecified, defaults to cipher-specific default.\n"
#endif
+#ifndef USE_POLARSSL
"--engine [name] : Enable OpenSSL hardware crypto engine functionality.\n"
+#endif
"--no-replay : Disable replay protection.\n"
"--mute-replay-warnings : Silence the output of replay warnings to log file.\n"
"--replay-window n [t] : Use a replay protection sliding window of size n\n"
@@ -529,13 +531,15 @@ static const char usage_message[] =
" number, such as 1 (default), 2, etc.\n"
"--ca file : Certificate authority file in .pem format containing\n"
" root certificate.\n"
+#ifndef USE_POLARSSL
"--capath dir : A directory of trusted certificates (CAs"
#if OPENSSL_VERSION_NUMBER >= 0x00907000L
" and CRLs).\n"
-#else
+#else /* OPENSSL_VERSION_NUMBER >= 0x00907000L */
").\n"
" WARNING: no support of CRL available with this version.\n"
-#endif
+#endif /* OPENSSL_VERSION_NUMBER >= 0x00907000L */
+#endif /* USE_POLARSSL */
"--dh file : File containing Diffie Hellman parameters\n"
" in .pem format (for --tls-server only).\n"
" Use \"openssl dhparam -out dh1024.pem 1024\" to generate.\n"
@@ -590,7 +594,7 @@ static const char usage_message[] =
" nsCertType designation t = 'client' | 'server'.\n"
"--x509-track x : Save peer X509 attribute x in environment for use by\n"
" plugins and management interface.\n"
-#if OPENSSL_VERSION_NUMBER >= 0x00907000L
+#if OPENSSL_VERSION_NUMBER >= 0x00907000L || USE_POLARSSL
"--remote-cert-ku v ... : Require that the peer certificate was signed with\n"
" explicit key usage, you can specify more than one value.\n"
" value should be given in hex format.\n"
@@ -600,7 +604,7 @@ static const char usage_message[] =
"--remote-cert-tls t: Require that peer certificate was signed with explicit\n"
" key usage and extended key usage based on RFC3280 TLS rules.\n"
" t = 'client' | 'server'.\n"
-#endif /* OPENSSL_VERSION_NUMBER */
+#endif /* OPENSSL_VERSION_NUMBER || USE_POLARSSL */
#endif /* USE_SSL */
#ifdef ENABLE_PKCS11
"\n"
@@ -1537,7 +1541,9 @@ show_settings (const struct options *o)
SHOW_STR (prng_hash);
SHOW_INT (prng_nonce_secret_len);
SHOW_INT (keysize);
+#ifndef USE_POLARSSL
SHOW_BOOL (engine);
+#endif /* USE_POLARSSL */
SHOW_BOOL (replay);
SHOW_BOOL (mute_replay_warnings);
SHOW_INT (replay_window);
@@ -2268,8 +2274,13 @@ options_postprocess_verify_ce (const struct options *options, const struct conne
}
else
{
+#ifdef USE_POLARSSL
+ if (!(options->ca_file))
+ msg(M_USAGE, "You must define CA file (--ca)");
+#else
if ((!(options->ca_file)) && (!(options->ca_path)))
msg(M_USAGE, "You must define CA file (--ca) or CA path (--capath)");
+#endif
if (pull)
{
const int sum = (options->cert_file != NULL) + (options->priv_key_file != NULL);
@@ -6114,6 +6125,7 @@ add_option (struct options *options,
VERIFY_PERMISSION (OPT_P_GENERAL);
options->test_crypto = true;
}
+#ifndef USE_POLARSSL
else if (streq (p[0], "engine"))
{
VERIFY_PERMISSION (OPT_P_GENERAL);
@@ -6124,6 +6136,7 @@ add_option (struct options *options,
else
options->engine = "auto";
}
+#endif /* USE_POLARSSL */
#ifdef HAVE_EVP_CIPHER_CTX_SET_KEY_LENGTH
else if (streq (p[0], "keysize") && p[1])
{
@@ -6166,11 +6179,13 @@ add_option (struct options *options,
}
#endif
}
+#ifndef USE_POLARSSL
else if (streq (p[0], "capath") && p[1])
{
VERIFY_PERMISSION (OPT_P_GENERAL);
options->ca_path = p[1];
}
+#endif /* USE_POLARSSL */
else if (streq (p[0], "dh") && p[1])
{
VERIFY_PERMISSION (OPT_P_GENERAL);
@@ -6322,7 +6337,7 @@ add_option (struct options *options,
goto err;
}
}
-#if OPENSSL_VERSION_NUMBER >= 0x00907000L
+#if OPENSSL_VERSION_NUMBER >= 0x00907000L || USE_POLARSSL
else if (streq (p[0], "remote-cert-ku"))
{
int j;
diff --git a/pkcs11_polarssl.c b/pkcs11_polarssl.c
new file mode 100644
index 0000000..49d6a6b
--- /dev/null
+++ b/pkcs11_polarssl.c
@@ -0,0 +1,121 @@
+/*
+ * OpenVPN -- An application to securely tunnel IP networks
+ * over a single TCP/UDP port, with support for SSL/TLS-based
+ * session authentication and key exchange,
+ * packet encryption, packet authentication, and
+ * packet compression.
+ *
+ * Copyright (C) 2002-2010 OpenVPN Technologies, Inc. <sales@openvpn.net>
+ * Copyright (C) 2010 Fox Crypto B.V. <openvpn@fox-it.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ *
+ * 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 (see the file COPYING included with this
+ * distribution); if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/**
+ * @file PKCS #11 PolarSSL backend
+ */
+
+#include "syshead.h"
+
+#if defined(ENABLE_PKCS11)
+
+#include "errlevel.h"
+#include "pkcs11_backend.h"
+#include <polarssl/pkcs11.h>
+
+int
+pkcs11_init_tls_session(pkcs11h_certificate_t certificate,
+ struct tls_root_ctx * const ssl_ctx)
+{
+ int ret = 1;
+ pkcs11_context pkcs11_ctx;
+
+ ASSERT (NULL != ssl_ctx);
+
+ if (pkcs11_x509_cert_init(ssl_ctx->crt_chain, certificate)) {
+ msg (M_FATAL, "PKCS#11: Cannot retrieve PolarSSL certificate object");
+ goto cleanup;
+ }
+
+ ssl_ctx->priv_key_pkcs11 = malloc(sizeof(pkcs11_context));
+
+ if (ssl_ctx->priv_key_pkcs11 == NULL) {
+ msg (M_FATAL, "PKCS#11: Cannot allocate PolarSSL private key object");
+ goto cleanup;
+ }
+
+ if (pkcs11_priv_key_init(ssl_ctx->priv_key_pkcs11, certificate)) {
+ msg (M_FATAL, "PKCS#11: Cannot initialize PolarSSL private key object");
+ goto cleanup;
+ }
+
+ ret = 0;
+
+cleanup:
+ return ret;
+}
+
+int
+pkcs11_certificate_dn (pkcs11h_certificate_t cert, char *dn,
+ size_t dn_len)
+{
+ int ret = 1;
+
+ x509_cert polar_cert = {0};
+
+ if (pkcs11_x509_cert_init(&polar_cert, cert)) {
+ msg (M_FATAL, "PKCS#11: Cannot retrieve PolarSSL certificate object");
+ goto cleanup;
+ }
+
+ if (-1 == x509parse_dn_gets (dn, dn_len, &polar_cert.subject)) {
+ msg (M_FATAL, "PKCS#11: PolarSSL cannot parse subject");
+ goto cleanup;
+ }
+
+ ret = 0;
+
+cleanup:
+ x509_free(&polar_cert);
+
+ return ret;
+}
+
+int
+pkcs11_certificate_serial (pkcs11h_certificate_t cert, char *serial,
+ size_t serial_len)
+{
+ int ret = 1;
+
+ x509_cert polar_cert = {0};
+
+ if (pkcs11_x509_cert_init(&polar_cert, cert)) {
+ msg (M_FATAL, "PKCS#11: Cannot retrieve PolarSSL certificate object");
+ goto cleanup;
+ }
+
+ if (-1 == x509parse_serial_gets (serial, serial_len, &polar_cert.serial)) {
+ msg (M_FATAL, "PKCS#11: PolarSSL cannot parse serial");
+ goto cleanup;
+ }
+
+ ret = 0;
+
+cleanup:
+ x509_free(&polar_cert);
+
+ return ret;
+}
+#endif /* defined(ENABLE_PKCS11) */
diff --git a/ssl_backend.h b/ssl_backend.h
index 324fc33..8573bba 100644
--- a/ssl_backend.h
+++ b/ssl_backend.h
@@ -36,6 +36,10 @@
#ifdef USE_OPENSSL
#include "ssl_openssl.h"
#endif
+#ifdef USE_POLARSSL
+#include "ssl_polarssl.h"
+#include "ssl_verify_polarssl.h"
+#endif
/*
diff --git a/ssl_polarssl.c b/ssl_polarssl.c
new file mode 100644
index 0000000..6ed8735
--- /dev/null
+++ b/ssl_polarssl.c
@@ -0,0 +1,848 @@
+/*
+ * OpenVPN -- An application to securely tunnel IP networks
+ * over a single TCP/UDP port, with support for SSL/TLS-based
+ * session authentication and key exchange,
+ * packet encryption, packet authentication, and
+ * packet compression.
+ *
+ * Copyright (C) 2002-2010 OpenVPN Technologies, Inc. <sales@openvpn.net>
+ * Copyright (C) 2010 Fox Crypto B.V. <openvpn@fox-it.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ *
+ * 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 (see the file COPYING included with this
+ * distribution); if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/**
+ * @file Control Channel PolarSSL Backend
+ */
+
+#include "syshead.h"
+#include "errlevel.h"
+#include "ssl_backend.h"
+#include "buffer.h"
+#include "misc.h"
+#include "manage.h"
+#include "ssl_common.h"
+
+#include "ssl_verify_polarssl.h"
+
+void
+tls_init_lib()
+{
+}
+
+void
+tls_free_lib()
+{
+}
+
+void
+tls_clear_error()
+{
+}
+
+static int default_ciphers[] =
+{
+ SSL_EDH_RSA_AES_256_SHA,
+ SSL_EDH_RSA_CAMELLIA_256_SHA,
+ SSL_EDH_RSA_AES_128_SHA,
+ SSL_EDH_RSA_CAMELLIA_128_SHA,
+ SSL_EDH_RSA_DES_168_SHA,
+ SSL_RSA_AES_256_SHA,
+ SSL_RSA_CAMELLIA_256_SHA,
+ SSL_RSA_AES_128_SHA,
+ SSL_RSA_CAMELLIA_128_SHA,
+ SSL_RSA_DES_168_SHA,
+ SSL_RSA_RC4_128_SHA,
+ SSL_RSA_RC4_128_MD5,
+ 0
+};
+
+void
+tls_ctx_server_new(struct tls_root_ctx *ctx)
+{
+ ASSERT(NULL != ctx);
+ CLEAR(*ctx);
+
+ ALLOC_OBJ_CLEAR(ctx->hs, havege_state);
+ havege_init(ctx->hs);
+
+ ALLOC_OBJ_CLEAR(ctx->dhm_ctx, dhm_context);
+ ALLOC_OBJ_CLEAR(ctx->priv_key, rsa_context);
+
+ ALLOC_OBJ_CLEAR(ctx->ca_chain, x509_cert);
+ ALLOC_OBJ_CLEAR(ctx->crt_chain, x509_cert);
+
+
+ ctx->endpoint = SSL_IS_SERVER;
+ ctx->initialised = true;
+}
+
+void
+tls_ctx_client_new(struct tls_root_ctx *ctx)
+{
+ ASSERT(NULL != ctx);
+
+ CLEAR(*ctx);
+
+ ALLOC_OBJ_CLEAR(ctx->hs, havege_state);
+ havege_init(ctx->hs);
+
+ ALLOC_OBJ_CLEAR(ctx->dhm_ctx, dhm_context);
+ ALLOC_OBJ_CLEAR(ctx->priv_key, rsa_context);
+
+ ALLOC_OBJ_CLEAR(ctx->ca_chain, x509_cert);
+ ALLOC_OBJ_CLEAR(ctx->crt_chain, x509_cert);
+
+ ctx->endpoint = SSL_IS_CLIENT;
+ ctx->initialised = true;
+}
+
+void
+tls_ctx_free(struct tls_root_ctx *ctx)
+{
+ if (ctx)
+ {
+ rsa_free(ctx->priv_key);
+ free(ctx->priv_key);
+
+ x509_free(ctx->ca_chain);
+ free(ctx->ca_chain);
+
+ x509_free(ctx->crt_chain);
+ free(ctx->crt_chain);
+
+ dhm_free(ctx->dhm_ctx);
+ free(ctx->dhm_ctx);
+
+#if defined(ENABLE_PKCS11)
+ if (ctx->priv_key_pkcs11 != NULL) {
+ pkcs11_priv_key_free(ctx->priv_key_pkcs11);
+ free(ctx->priv_key_pkcs11);
+ }
+#endif
+
+ free(ctx->hs);
+
+ if (ctx->allowed_ciphers)
+ free(ctx->allowed_ciphers);
+
+ CLEAR(*ctx);
+
+ ctx->initialised = false;
+
+ }
+}
+
+bool
+tls_ctx_initialised(struct tls_root_ctx *ctx)
+{
+ ASSERT(NULL != ctx);
+ return ctx->initialised;
+}
+
+void
+tls_ctx_set_options (struct tls_root_ctx *ctx, unsigned int ssl_flags)
+{
+}
+
+void
+tls_ctx_restrict_ciphers(struct tls_root_ctx *ctx, const char *ciphers)
+{
+ char *tmp_ciphers, *tmp_ciphers_orig;
+ int i, cipher_count;
+ int ciphers_len = strlen (ciphers);
+
+ ASSERT (NULL != ctx);
+ ASSERT (0 != ciphers_len);
+
+ /* Get number of ciphers */
+ for (i = 0, cipher_count = 1; i < ciphers_len; i++)
+ if (ciphers[i] == ':')
+ cipher_count++;
+
+ /* Allocate an array for them */
+ ALLOC_ARRAY_CLEAR(ctx->allowed_ciphers, int, cipher_count+1)
+
+ /* Parse allowed ciphers, getting IDs */
+ i = 0;
+ tmp_ciphers_orig = tmp_ciphers = strdup(ciphers);
+ while(tmp_ciphers) {
+ ctx->allowed_ciphers[i] = ssl_get_cipher_id (strsep (&tmp_ciphers, ":"));
+ if (ctx->allowed_ciphers[i] != 0)
+ i++;
+ }
+ free(tmp_ciphers_orig);
+}
+
+void
+tls_ctx_load_dh_params (struct tls_root_ctx *ctx, const char *dh_file
+#if ENABLE_INLINE_FILES
+ , const char *dh_file_inline
+#endif /* ENABLE_INLINE_FILES */
+ )
+{
+#if ENABLE_INLINE_FILES
+ if (!strcmp (dh_file, INLINE_FILE_TAG) && dh_file_inline)
+ {
+ if (0 != x509parse_dhm(ctx->dhm_ctx, dh_file_inline, strlen(dh_file_inline)))
+ msg (M_FATAL, "Cannot read inline DH parameters");
+ }
+else
+#endif /* ENABLE_INLINE_FILES */
+ {
+ if (0 != x509parse_dhmfile(ctx->dhm_ctx, dh_file))
+ msg (M_FATAL, "Cannot read DH parameters from file %s", dh_file);
+ }
+
+ msg (D_TLS_DEBUG_LOW, "Diffie-Hellman initialized with %d bit key",
+ 8 * mpi_size(&ctx->dhm_ctx->P));
+}
+
+int
+tls_ctx_load_pkcs12(struct tls_root_ctx *ctx, const char *pkcs12_file,
+#if ENABLE_INLINE_FILES
+ const char *pkcs12_file_inline,
+#endif /* ENABLE_INLINE_FILES */
+ bool load_ca_file
+ )
+{
+ msg(M_FATAL, "PKCS #12 files not yet supported for PolarSSL.");
+}
+
+#ifdef WIN32
+void
+tls_ctx_load_cryptoapi(struct tls_root_ctx *ctx, const char *cryptoapi_cert)
+{
+ msg(M_FATAL, "Windows CryptoAPI not yet supported for PolarSSL.");
+}
+#endif /* WIN32 */
+
+void
+tls_ctx_load_cert_file (struct tls_root_ctx *ctx, const char *cert_file,
+#if ENABLE_INLINE_FILES
+ const char *cert_file_inline,
+#endif
+ x509_cert_t **x509
+ )
+{
+ ASSERT(NULL != ctx);
+ if (NULL != x509)
+ ASSERT(NULL == *x509);
+
+#if ENABLE_INLINE_FILES
+ if (!strcmp (cert_file, INLINE_FILE_TAG) && cert_file_inline)
+ {
+ if (0 != x509parse_crt(ctx->crt_chain, cert_file_inline,
+ strlen(cert_file_inline)))
+ msg (M_FATAL, "Cannot load inline certificate file");
+ }
+ else
+#endif /* ENABLE_INLINE_FILES */
+ {
+ if (0 != x509parse_crtfile(ctx->crt_chain, cert_file))
+ msg (M_FATAL, "Cannot load certificate file %s", cert_file);
+ }
+ if (x509)
+ {
+ *x509 = ctx->crt_chain;
+ }
+}
+
+int
+tls_ctx_load_priv_file (struct tls_root_ctx *ctx, const char *priv_key_file
+#if ENABLE_INLINE_FILES
+ , const char *priv_key_file_inline
+#endif /* ENABLE_INLINE_FILES */
+ )
+{
+ int status;
+ ASSERT(NULL != ctx);
+
+#if ENABLE_INLINE_FILES
+ if (!strcmp (priv_key_file, INLINE_FILE_TAG) && priv_key_file_inline)
+ {
+ status = x509parse_key(ctx->priv_key,
+ priv_key_file_inline, strlen(priv_key_file_inline),
+ NULL, 0);
+ if (POLARSSL_ERR_X509_KEY_PASSWORD_REQUIRED == status)
+ {
+ char passbuf[512] = {0};
+ pem_password_callback(passbuf, 512, 0, NULL);
+ status = x509parse_key(ctx->priv_key,
+ priv_key_file_inline, strlen(priv_key_file_inline),
+ passbuf, strlen(passbuf));
+ }
+ }
+ else
+#endif /* ENABLE_INLINE_FILES */
+ {
+ status = x509parse_keyfile(ctx->priv_key, priv_key_file, NULL);
+ if (POLARSSL_ERR_X509_KEY_PASSWORD_REQUIRED == status)
+ {
+ char passbuf[512] = {0};
+ pem_password_callback(passbuf, 512, 0, NULL);
+ status = x509parse_keyfile(ctx->priv_key, priv_key_file, passbuf);
+ }
+ }
+ if (0 != status)
+ {
+#ifdef ENABLE_MANAGEMENT
+ if (management && (POLARSSL_ERR_X509_KEY_PASSWORD_MISMATCH == status))
+ management_auth_failure (management, UP_TYPE_PRIVATE_KEY, NULL);
+#endif
+ msg (M_WARN, "Cannot load private key file %s", priv_key_file);
+ return 1;
+ }
+
+ warn_if_group_others_accessible (priv_key_file);
+
+ /* TODO: Check Private Key */
+// if (!SSL_CTX_check_private_key (ctx))
+// msg (M_SSLERR, "Private key does not match the certificate");
+ return 0;
+}
+
+#ifdef MANAGMENT_EXTERNAL_KEY
+
+int
+tls_ctx_use_external_private_key (struct tls_root_ctx *ctx, x509_cert_t *cert)
+{
+ msg(M_FATAL, "Use of management external keys not yet supported for PolarSSL.");
+
+}
+
+#endif
+
+void tls_ctx_load_ca (struct tls_root_ctx *ctx, const char *ca_file,
+#if ENABLE_INLINE_FILES
+ const char *ca_file_inline,
+#endif
+ const char *ca_path, bool tls_server
+ )
+{
+ int status;
+
+ if (ca_path)
+ msg(M_FATAL, "ERROR: PolarSSL cannot handle the capath directive");
+
+#if ENABLE_INLINE_FILES
+ if (ca_file && !strcmp (ca_file, INLINE_FILE_TAG) && ca_file_inline)
+ {
+ if (0 != x509parse_crt(ctx->ca_chain, ca_file_inline, strlen(ca_file_inline)));
+ msg (M_FATAL, "Cannot load inline CA certificates");
+ }
+ else
+#endif
+ {
+ /* Load CA file for verifying peer supplied certificate */
+ if (0 != x509parse_crtfile(ctx->ca_chain, ca_file))
+ msg (M_FATAL, "Cannot load CA certificate file %s", ca_file);
+ }
+}
+
+void
+tls_ctx_load_extra_certs (struct tls_root_ctx *ctx, const char *extra_certs_file
+#if ENABLE_INLINE_FILES
+ , const char *extra_certs_file_inline
+#endif
+ )
+{
+ ASSERT(NULL != ctx);
+
+#if ENABLE_INLINE_FILES
+ if (!strcmp (extra_certs_file, INLINE_FILE_TAG) && extra_certs_file_inline)
+ {
+ if (0 != x509parse_crt(ctx->crt_chain, extra_certs_file_inline,
+ strlen(extra_certs_file_inline)))
+ msg (M_FATAL, "Cannot load inline extra-certs file");
+ }
+ else
+#endif /* ENABLE_INLINE_FILES */
+ {
+ if (0 != x509parse_crtfile(ctx->crt_chain, extra_certs_file))
+ msg (M_FATAL, "Cannot load extra-certs file: %s", extra_certs_file);
+ }
+}
+
+/* **************************************
+ *
+ * Key-state specific functions
+ *
+ ***************************************/
+
+/*
+ * "Endless buffer"
+ */
+
+static inline void buf_free_entry(buffer_entry *entry)
+{
+ if (NULL != entry)
+ {
+ free(entry->data);
+ free(entry);
+ }
+}
+
+static void buf_free_entries(endless_buffer *buf)
+{
+ while(buf->first_block)
+ {
+ buffer_entry *cur_block = buf->first_block;
+ buf->first_block = cur_block->next_block;
+ buf_free_entry(cur_block);
+ }
+ buf->last_block = NULL;
+}
+
+static int endless_buf_read( void * ctx, unsigned char * out, int out_len )
+{
+ endless_buffer *in = (endless_buffer *) ctx;
+ int read_len = 0;
+
+ if (in->first_block == NULL)
+ return POLARSSL_ERR_NET_TRY_AGAIN;
+
+ while (in->first_block != NULL && read_len < out_len)
+ {
+ int block_len = in->first_block->length - in->data_start;
+ if (block_len <= out_len - read_len)
+ {
+ buffer_entry *cur_entry = in->first_block;
+ memcpy(out + read_len, cur_entry->data + in->data_start,
+ block_len);
+
+ read_len += block_len;
+
+ in->first_block = cur_entry->next_block;
+ in->data_start = 0;
+
+ if (in->first_block == NULL)
+ in->last_block = NULL;
+
+ buf_free_entry(cur_entry);
+ }
+ else
+ {
+ memcpy(out + read_len, in->first_block->data + in->data_start,
+ out_len - read_len);
+ in->data_start += out_len - read_len;
+ read_len = out_len;
+ }
+ }
+
+ return read_len;
+}
+
+static int endless_buf_write( void *ctx, unsigned char *in, int len )
+{
+ endless_buffer *out = (endless_buffer *) ctx;
+ buffer_entry *new_block = malloc(sizeof(buffer_entry));
+ if (NULL == new_block)
+ return POLARSSL_ERR_NET_SEND_FAILED;
+
+ new_block->data = malloc(len);
+ if (NULL == new_block->data)
+ {
+ free(new_block);
+ return POLARSSL_ERR_NET_SEND_FAILED;
+ }
+
+ new_block->length = len;
+ new_block->next_block = NULL;
+
+ memcpy(new_block->data, in, len);
+
+ if (NULL == out->first_block)
+ out->first_block = new_block;
+
+ if (NULL != out->last_block)
+ out->last_block->next_block = new_block;
+
+ out->last_block = new_block;
+
+ return len;
+}
+
+static void my_debug( void *ctx, int level, const char *str )
+{
+ if (level == 1)
+ {
+ dmsg (D_HANDSHAKE_VERBOSE, "PolarSSL alert: %s", str);
+ }
+}
+
+void key_state_ssl_init(struct key_state_ssl *ks_ssl,
+ const struct tls_root_ctx *ssl_ctx, bool is_server, void *session)
+{
+ ASSERT(NULL != ssl_ctx);
+ ASSERT(ks_ssl);
+ CLEAR(*ks_ssl);
+
+ ALLOC_OBJ_CLEAR(ks_ssl->ctx, ssl_context);
+ if (0 == ssl_init(ks_ssl->ctx))
+ {
+ /* Initialise SSL context */
+ ssl_set_dbg (ks_ssl->ctx, my_debug, NULL);
+ ssl_set_endpoint (ks_ssl->ctx, ssl_ctx->endpoint);
+ ssl_set_rng (ks_ssl->ctx, havege_rand, ssl_ctx->hs);
+ ALLOC_OBJ_CLEAR (ks_ssl->ssn, ssl_session);
+ ssl_set_session (ks_ssl->ctx, 0, 0, ks_ssl->ssn );
+ if (ssl_ctx->allowed_ciphers)
+ ssl_set_ciphers (ks_ssl->ctx, ssl_ctx->allowed_ciphers);
+ else
+ ssl_set_ciphers (ks_ssl->ctx, default_ciphers);
+
+ /* Initialise authentication information */
+ ssl_set_dh_param_ctx (ks_ssl->ctx, ssl_ctx->dhm_ctx );
+ if (ssl_ctx->priv_key_pkcs11 != NULL)
+ ssl_set_own_cert_pkcs11( ks_ssl->ctx, ssl_ctx->crt_chain,
+ ssl_ctx->priv_key_pkcs11 );
+ else
+ ssl_set_own_cert( ks_ssl->ctx, ssl_ctx->crt_chain, ssl_ctx->priv_key );
+
+ /* Initialise SSL verification */
+ ssl_set_authmode (ks_ssl->ctx, SSL_VERIFY_REQUIRED);
+ ssl_set_verify (ks_ssl->ctx, verify_callback, session);
+ /* TODO: PolarSSL does not currently support sending the CA chain to the client */
+ ssl_set_ca_chain (ks_ssl->ctx, ssl_ctx->ca_chain, NULL, NULL );
+
+ /* Initialise BIOs */
+ ALLOC_OBJ_CLEAR (ks_ssl->ct_in, endless_buffer);
+ ALLOC_OBJ_CLEAR (ks_ssl->ct_out, endless_buffer);
+ ssl_set_bio (ks_ssl->ctx, endless_buf_read, ks_ssl->ct_in,
+ endless_buf_write, ks_ssl->ct_out);
+
+ }
+}
+
+void
+key_state_ssl_free(struct key_state_ssl *ks_ssl)
+{
+ if (ks_ssl) {
+ if (ks_ssl->ctx)
+ {
+ ssl_free(ks_ssl->ctx);
+ free(ks_ssl->ctx);
+ }
+ if (ks_ssl->ssn)
+ free(ks_ssl->ssn);
+ if (ks_ssl->ct_in) {
+ buf_free_entries(ks_ssl->ct_in);
+ free(ks_ssl->ct_in);
+ }
+ if (ks_ssl->ct_out) {
+ buf_free_entries(ks_ssl->ct_out);
+ free(ks_ssl->ct_out);
+ }
+ CLEAR(*ks_ssl);
+ }
+}
+
+int
+key_state_write_plaintext (struct key_state_ssl *ks, struct buffer *buf)
+{
+ int retval = 0;
+ perf_push (PERF_BIO_WRITE_PLAINTEXT);
+
+ ASSERT (NULL != ks);
+ ASSERT (buf);
+ ASSERT (buf->len >= 0);
+
+ if (0 == buf->len)
+ {
+ return 0;
+ perf_pop ();
+ }
+
+ retval = ssl_write(ks->ctx, BPTR(buf), buf->len);
+
+ if (retval < 0)
+ {
+ perf_pop ();
+ if (POLARSSL_ERR_NET_TRY_AGAIN == retval )
+ return 0;
+ msg (D_TLS_ERRORS, "TLS ERROR: write tls_write_plaintext error");
+ return -1;
+ }
+
+ if (retval != buf->len)
+ {
+ msg (D_TLS_ERRORS,
+ "TLS ERROR: write tls_write_plaintext incomplete %d/%d",
+ retval, buf->len);
+ perf_pop ();
+ return -1;
+ }
+
+ /* successful write */
+ dmsg (D_HANDSHAKE_VERBOSE, "write tls_write_plaintext %d bytes", retval);
+
+ memset (BPTR (buf), 0, BLEN (buf)); /* erase data just written */
+ buf->len = 0;
+
+ perf_pop ();
+ return 1;
+}
+
+int
+key_state_write_plaintext_const (struct key_state_ssl *ks, const uint8_t *data, int len)
+{
+ int retval = 0;
+ perf_push (PERF_BIO_WRITE_PLAINTEXT);
+
+ ASSERT (NULL != ks);
+ ASSERT (len >= 0);
+
+ if (0 == len)
+ {
+ perf_pop ();
+ return 0;
+ }
+
+ ASSERT (data);
+
+ retval = ssl_write(ks->ctx, data, len);
+
+ if (retval < 0)
+ {
+ perf_pop ();
+ if (POLARSSL_ERR_NET_TRY_AGAIN == retval )
+ return 0;
+ msg (D_TLS_ERRORS, "TLS ERROR: write tls_write_plaintext_const error");
+ return -1;
+ }
+
+ if (retval != len)
+ {
+ msg (D_TLS_ERRORS,
+ "TLS ERROR: write tls_write_plaintext_const incomplete %d/%d",
+ retval, len);
+ perf_pop ();
+ return -1;
+ }
+
+ /* successful write */
+ dmsg (D_HANDSHAKE_VERBOSE, "write tls_write_plaintext_const %d bytes", retval);
+
+ perf_pop ();
+ return 1;
+}
+
+int
+key_state_read_ciphertext (struct key_state_ssl *ks, struct buffer *buf,
+ int maxlen)
+{
+ int retval = 0;
+ int len = 0;
+
+ perf_push (PERF_BIO_READ_CIPHERTEXT);
+
+ ASSERT (NULL != ks);
+ ASSERT (buf);
+ ASSERT (buf->len >= 0);
+
+ if (buf->len)
+ {
+ perf_pop ();
+ return 0;
+ }
+
+ len = buf_forward_capacity (buf);
+ if (maxlen < len)
+ len = maxlen;
+
+ retval = endless_buf_read(ks->ct_out, BPTR(buf), len);
+
+ /* Error during read, check for retry error */
+ if (retval < 0)
+ {
+ perf_pop ();
+ if (POLARSSL_ERR_NET_TRY_AGAIN == retval )
+ return 0;
+ msg (D_TLS_ERRORS, "TLS_ERROR: read tls_read_plaintext error");
+ buf->len = 0;
+ return -1;
+ }
+ /* Nothing read, try again */
+ if (0 == retval)
+ {
+ buf->len = 0;
+ perf_pop ();
+ return 0;
+ }
+
+ /* successful read */
+ dmsg (D_HANDSHAKE_VERBOSE, "read tls_read_ciphertext %d bytes", retval);
+ buf->len = retval;
+ perf_pop ();
+ return 1;
+}
+
+int
+key_state_write_ciphertext (struct key_state_ssl *ks, struct buffer *buf)
+{
+ int retval = 0;
+ perf_push (PERF_BIO_WRITE_CIPHERTEXT);
+
+ ASSERT (NULL != ks);
+ ASSERT (buf);
+ ASSERT (buf->len >= 0);
+
+ if (0 == buf->len)
+ {
+ perf_pop ();
+ return 0;
+ }
+
+ retval = endless_buf_write(ks->ct_in, BPTR(buf), buf->len);
+
+ if (retval < 0)
+ {
+ perf_pop ();
+
+ if (POLARSSL_ERR_NET_TRY_AGAIN == retval )
+ return 0;
+ msg (D_TLS_ERRORS, "TLS ERROR: write tls_write_ciphertext error");
+ return -1;
+ }
+
+ if (retval != buf->len)
+ {
+ msg (D_TLS_ERRORS,
+ "TLS ERROR: write tls_write_ciphertext incomplete %d/%d",
+ retval, buf->len);
+ perf_pop ();
+ return -1;
+ }
+
+ /* successful write */
+ dmsg (D_HANDSHAKE_VERBOSE, "write tls_write_ciphertext %d bytes", retval);
+
+ memset (BPTR (buf), 0, BLEN (buf)); /* erase data just written */
+ buf->len = 0;
+
+ perf_pop ();
+ return 1;
+}
+
+int
+key_state_read_plaintext (struct key_state_ssl *ks, struct buffer *buf,
+ int maxlen)
+{
+ int retval = 0;
+ int len = 0;
+
+ perf_push (PERF_BIO_READ_PLAINTEXT);
+
+ ASSERT (NULL != ks);
+ ASSERT (buf);
+ ASSERT (buf->len >= 0);
+
+ if (buf->len)
+ {
+ perf_pop ();
+ return 0;
+ }
+
+ len = buf_forward_capacity (buf);
+ if (maxlen < len)
+ len = maxlen;
+
+ retval = ssl_read(ks->ctx, BPTR(buf), len);
+
+ /* Error during read, check for retry error */
+ if (retval < 0)
+ {
+ if (POLARSSL_ERR_NET_TRY_AGAIN == retval )
+ return 0;
+ msg (D_TLS_ERRORS, "TLS_ERROR: read tls_read_plaintext error");
+ buf->len = 0;
+ perf_pop ();
+ return -1;
+ }
+ /* Nothing read, try again */
+ if (0 == retval)
+ {
+ buf->len = 0;
+ perf_pop ();
+ return 0;
+ }
+
+ /* successful read */
+ dmsg (D_HANDSHAKE_VERBOSE, "read tls_read_plaintext %d bytes", retval);
+ buf->len = retval;
+
+ perf_pop ();
+ return 1;
+}
+
+/* **************************************
+ *
+ * Information functions
+ *
+ * Print information for the end user.
+ *
+ ***************************************/
+void
+print_details (struct key_state_ssl * ks_ssl, const char *prefix)
+{
+ x509_cert *cert;
+ char s1[256];
+ char s2[256];
+
+ s1[0] = s2[0] = 0;
+ openvpn_snprintf (s1, sizeof (s1), "%s %s, cipher %s",
+ prefix,
+ ssl_get_version (ks_ssl->ctx),
+ ssl_get_cipher(ks_ssl->ctx));
+
+ cert = ks_ssl->ctx->peer_cert;
+ if (cert != NULL)
+ {
+ openvpn_snprintf (s2, sizeof (s2), ", %d bit RSA", cert->rsa.len * 8);
+ }
+
+ msg (D_HANDSHAKE, "%s%s", s1, s2);
+}
+
+void
+show_available_tls_ciphers ()
+{
+ const int *ciphers = ssl_list_ciphers();
+
+#ifndef ENABLE_SMALL
+ printf ("Available TLS Ciphers,\n");
+ printf ("listed in order of preference:\n\n");
+#endif
+
+ while (*ciphers != 0)
+ {
+ printf ("%s\n", ssl_get_cipher_name(*ciphers));
+ ciphers++;
+ }
+ printf ("\n");
+}
+
+void
+get_highest_preference_tls_cipher (char *buf, int size)
+{
+ const char *cipher_name;
+ const int *ciphers = ssl_list_ciphers();
+ if (*ciphers == 0)
+ msg (M_FATAL, "Cannot retrieve list of supported SSL ciphers.");
+
+ cipher_name = ssl_get_cipher_name(*ciphers);
+ strncpynt (buf, cipher_name, size);
+}
diff --git a/ssl_polarssl.h b/ssl_polarssl.h
new file mode 100644
index 0000000..e6149b6
--- /dev/null
+++ b/ssl_polarssl.h
@@ -0,0 +1,85 @@
+/*
+ * OpenVPN -- An application to securely tunnel IP networks
+ * over a single TCP/UDP port, with support for SSL/TLS-based
+ * session authentication and key exchange,
+ * packet encryption, packet authentication, and
+ * packet compression.
+ *
+ * Copyright (C) 2002-2010 OpenVPN Technologies, Inc. <sales@openvpn.net>
+ * Copyright (C) 2010 Fox Crypto B.V. <openvpn@fox-it.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ *
+ * 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 (see the file COPYING included with this
+ * distribution); if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/**
+ * @file Control Channel PolarSSL Backend
+ */
+
+#ifndef SSL_POLARSSL_H_
+#define SSL_POLARSSL_H_
+
+#include <polarssl/havege.h>
+#include <polarssl/ssl.h>
+#include "config.h"
+
+#if defined(ENABLE_PKCS11)
+#include <polarssl/pkcs11.h>
+#endif
+
+typedef struct _buffer_entry buffer_entry;
+
+struct _buffer_entry {
+ size_t length;
+ uint8_t *data;
+ buffer_entry *next_block;
+};
+
+typedef struct {
+ size_t data_start;
+ buffer_entry *first_block;
+ buffer_entry *last_block;
+} endless_buffer;
+
+/**
+ * Structure that wraps the TLS context. Contents differ depending on the
+ * SSL library used.
+ *
+ * Either \c priv_key_pkcs11 or \c priv_key must be filled in.
+ */
+struct tls_root_ctx {
+ bool initialised; /**< True if the context has been initialised */
+
+ int endpoint; /**< Whether or not this is a server or a client */
+
+ havege_state *hs; /**< HAVEGE random number state */
+ dhm_context *dhm_ctx; /**< Diffie-Helmann-Merkle context */
+ x509_cert *crt_chain; /**< Local Certificate chain */
+ x509_cert *ca_chain; /**< CA chain for remote verification */
+ rsa_context *priv_key; /**< Local private key */
+#if defined(ENABLE_PKCS11)
+ pkcs11_context *priv_key_pkcs11; /**< PKCS11 private key */
+#endif
+ int * allowed_ciphers; /**< List of allowed ciphers for this connection */
+};
+
+struct key_state_ssl {
+ ssl_context *ctx;
+ ssl_session *ssn;
+ endless_buffer *ct_in;
+ endless_buffer *ct_out;
+};
+
+
+#endif /* SSL_POLARSSL_H_ */
diff --git a/ssl_verify.c b/ssl_verify.c
index fe14992..0c1296a 100644
--- a/ssl_verify.c
+++ b/ssl_verify.c
@@ -326,7 +326,7 @@ verify_peer_cert(const struct tls_options *opt, x509_cert_t *peer_cert,
}
}
-#if OPENSSL_VERSION_NUMBER >= 0x00907000L
+#if OPENSSL_VERSION_NUMBER >= 0x00907000L || USE_POLARSSL
/* verify certificate ku */
if (opt->remote_cert_ku[0] != 0)
diff --git a/ssl_verify.h b/ssl_verify.h
index 87d401a..1eaf639 100644
--- a/ssl_verify.h
+++ b/ssl_verify.h
@@ -39,6 +39,9 @@
#ifdef USE_OPENSSL
#include "ssl_verify_openssl.h"
#endif
+#ifdef USE_POLARSSL
+#include "ssl_verify_polarssl.h"
+#endif
#include "ssl_verify_backend.h"
diff --git a/ssl_verify_polarssl.c b/ssl_verify_polarssl.c
new file mode 100644
index 0000000..91a699a
--- /dev/null
+++ b/ssl_verify_polarssl.c
@@ -0,0 +1,446 @@
+/*
+ * OpenVPN -- An application to securely tunnel IP networks
+ * over a single TCP/UDP port, with support for SSL/TLS-based
+ * session authentication and key exchange,
+ * packet encryption, packet authentication, and
+ * packet compression.
+ *
+ * Copyright (C) 2002-2010 OpenVPN Technologies, Inc. <sales@openvpn.net>
+ * Copyright (C) 2010 Fox Crypto B.V. <openvpn@fox-it.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ *
+ * 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 (see the file COPYING included with this
+ * distribution); if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/**
+ * @file Control Channel Verification Module PolarSSL backend
+ */
+
+#include "ssl_verify.h"
+#include <polarssl/sha1.h>
+
+#define MAX_SUBJECT_LENGTH 256
+
+int
+verify_callback (void *session_obj, x509_cert *cert, int cert_depth,
+ int preverify_ok)
+{
+ struct tls_session *session = (struct tls_session *) session_obj;
+ unsigned char *sha1_hash = NULL;
+
+ ASSERT (cert);
+ ASSERT (session);
+
+ session->verified = false;
+
+ /* Remember certificate hash */
+ sha1_hash = x509_get_sha1_hash(cert);
+ cert_hash_remember (session, cert_depth, sha1_hash);
+ x509_free_sha1_hash(sha1_hash);
+
+ /* did peer present cert which was signed by our root cert? */
+ if (!preverify_ok)
+ {
+ char subject[MAX_SUBJECT_LENGTH] = {0};
+
+ /* get the X509 name */
+ if (x509parse_dn_gets( subject, MAX_SUBJECT_LENGTH, &cert->subject ) < 0)
+ msg (D_TLS_ERRORS, "VERIFY ERROR: depth=%d, could not extract X509 "
+ "subject string from certificate", cert_depth);
+ else
+ msg (D_TLS_ERRORS, "VERIFY ERROR: depth=%d, %s", cert_depth, subject);
+
+ return 1;
+ }
+
+ /*
+ * verify_cert() returns 1 on success, 0 on failure.
+ * PolarSSL expects the opposite.
+ */
+ return 0 == verify_cert(session, cert, cert_depth);
+}
+
+#ifdef ENABLE_X509ALTUSERNAME
+# warning "X509 alt user name not yet supported for PolarSSL"
+#endif
+
+bool
+x509_get_username (char *cn, int cn_len,
+ char *x509_username_field, x509_cert *cert)
+{
+ x509_name *name;
+
+ ASSERT( cn != NULL );
+
+ name = &cert->subject;
+
+ /* Find common name */
+ while( name != NULL )
+ {
+ if( memcmp( name->oid.p, OID_CN, OID_SIZE(OID_CN) ) == 0)
+ break;
+
+ name = name->next;
+ }
+
+ /* Not found, return an error if this is the peer's certificate */
+ if( name == NULL )
+ return 1;
+
+ /* Found, extract CN */
+ if (cn_len > name->val.len)
+ memcpy( cn, name->val.p, name->val.len );
+ else
+ {
+ memcpy( cn, name->val.p, cn_len);
+ cn[cn_len-1] = '\0';
+ }
+
+ return 0;
+}
+
+char *
+x509_get_serial (x509_cert *cert)
+{
+ int ret = 0;
+ int i = 0;
+ char *buf = NULL;
+ size_t len = cert->serial.len * 3;
+
+ buf = malloc(len);
+ ASSERT(buf);
+
+ if(x509parse_serial_gets(buf, len-1, &cert->serial) < 0)
+ {
+ free(buf);
+ buf = NULL;
+ }
+
+ return buf;
+}
+
+void
+x509_free_serial (char *serial)
+{
+ if (serial)
+ free(serial);
+}
+
+unsigned char *
+x509_get_sha1_hash (x509_cert *cert)
+{
+ unsigned char *sha1_hash = malloc(SHA_DIGEST_LENGTH);
+ sha1(cert->tbs.p, cert->tbs.len, sha1_hash);
+ return sha1_hash;
+}
+
+void
+x509_free_sha1_hash (unsigned char *hash)
+{
+ if (hash)
+ free(hash);
+}
+
+char *
+x509_get_subject(x509_cert *cert)
+{
+ char tmp_subject[MAX_SUBJECT_LENGTH] = {0};
+ char *subject = NULL;
+
+ int ret = 0;
+
+ ret = x509parse_dn_gets( tmp_subject, MAX_SUBJECT_LENGTH-1, &cert->subject );
+ if (ret > 0)
+ {
+ /* Allocate the required space for the subject */
+ subject = malloc(ret + 1);
+ strncpy(subject, tmp_subject, ret+1);
+ }
+
+ return subject;
+}
+
+void
+x509_free_subject (char *subject)
+{
+ if (subject)
+ free(subject);
+}
+
+/*
+ * Save X509 fields to environment, using the naming convention:
+ *
+ * X509_{cert_depth}_{name}={value}
+ */
+void
+x509_setenv (struct env_set *es, int cert_depth, x509_cert_t *cert)
+{
+ int i, ret;
+ unsigned char c;
+ const x509_name *name;
+ char s[128];
+
+ name = &cert->subject;
+
+ memset( s, 0, sizeof( s ) );
+
+ while( name != NULL )
+ {
+ char name_expand[64+8];
+
+ if( name->oid.len == 2 && memcmp( name->oid.p, OID_X520, 2 ) == 0 )
+ {
+ switch( name->oid.p[2] )
+ {
+ case X520_COMMON_NAME:
+ openvpn_snprintf (name_expand, sizeof(name_expand), "X509_%d_CN",
+ cert_depth); break;
+
+ case X520_COUNTRY:
+ openvpn_snprintf (name_expand, sizeof(name_expand), "X509_%d_C",
+ cert_depth); break;
+
+ case X520_LOCALITY:
+ openvpn_snprintf (name_expand, sizeof(name_expand), "X509_%d_L",
+ cert_depth); break;
+
+ case X520_STATE:
+ openvpn_snprintf (name_expand, sizeof(name_expand), "X509_%d_ST",
+ cert_depth); break;
+
+ case X520_ORGANIZATION:
+ openvpn_snprintf (name_expand, sizeof(name_expand), "X509_%d_O",
+ cert_depth); break;
+
+ case X520_ORG_UNIT:
+ openvpn_snprintf (name_expand, sizeof(name_expand), "X509_%d_OU",
+ cert_depth); break;
+
+ default:
+ openvpn_snprintf (name_expand, sizeof(name_expand),
+ "X509_%d_0x%02X", cert_depth, name->oid.p[2]);
+ break;
+ }
+ }
+ else if( name->oid.len == 8 && memcmp( name->oid.p, OID_PKCS9, 8 ) == 0 )
+ {
+ switch( name->oid.p[8] )
+ {
+ case PKCS9_EMAIL:
+ openvpn_snprintf (name_expand, sizeof(name_expand),
+ "X509_%d_emailAddress", cert_depth); break;
+
+ default:
+ openvpn_snprintf (name_expand, sizeof(name_expand),
+ "X509_%d_0x%02X", cert_depth, name->oid.p[8]);
+ break;
+ }
+ }
+ else
+ {
+ openvpn_snprintf (name_expand, sizeof(name_expand), "X509_%d_\?\?",
+ cert_depth);
+ }
+
+ for( i = 0; i < name->val.len; i++ )
+ {
+ if( i >= (int) sizeof( s ) - 1 )
+ break;
+
+ c = name->val.p[i];
+ if( c < 32 || c == 127 || ( c > 128 && c < 160 ) )
+ s[i] = '?';
+ else s[i] = c;
+ }
+ s[i] = '\0';
+
+ /* Check both strings, set environment variable */
+ string_mod (name_expand, CC_PRINT, CC_CRLF, '_');
+ string_mod ((char*)s, CC_PRINT, CC_CRLF, '_');
+ setenv_str (es, name_expand, (char*)s);
+
+ name = name->next;
+ }
+}
+
+bool
+x509_verify_ns_cert_type(const x509_cert *cert, const int usage)
+{
+ if (usage == NS_CERT_CHECK_NONE)
+ return true;
+ if (usage == NS_CERT_CHECK_CLIENT)
+ return ((cert->ext_types & EXT_NS_CERT_TYPE)
+ && (cert->ns_cert_type & NS_CERT_TYPE_SSL_CLIENT));
+ if (usage == NS_CERT_CHECK_SERVER)
+ return ((cert->ext_types & EXT_NS_CERT_TYPE)
+ && (cert->ns_cert_type & NS_CERT_TYPE_SSL_SERVER));
+
+ return false;
+}
+
+bool
+x509_verify_cert_ku (x509_cert *cert, const unsigned * const expected_ku,
+ int expected_len)
+{
+ bool fFound = false;
+
+ if(!(cert->ext_types & EXT_KEY_USAGE))
+ {
+ msg (D_HANDSHAKE, "Certificate does not have key usage extension");
+ }
+ else
+ {
+ int i;
+ unsigned nku = cert->key_usage;
+
+ msg (D_HANDSHAKE, "Validating certificate key usage");
+ for (i=0;!fFound && i<expected_len;i++)
+ {
+ if (expected_ku[i] != 0)
+ {
+ msg (D_HANDSHAKE, "++ Certificate has key usage %04x, expects "
+ "%04x", nku, expected_ku[i]);
+
+ if (nku == expected_ku[i])
+ {
+ fFound = true;
+ }
+ }
+ }
+ }
+ return fFound;
+}
+
+bool
+x509_verify_cert_eku (x509_cert *cert, const char * const expected_oid)
+{
+ bool fFound = false;
+
+ if (!(cert->ext_types & EXT_EXTENDED_KEY_USAGE))
+ {
+ msg (D_HANDSHAKE, "Certificate does not have extended key usage extension");
+ }
+ else
+ {
+ x509_sequence *oid_seq = &(cert->ext_key_usage);
+
+ msg (D_HANDSHAKE, "Validating certificate extended key usage");
+ while (oid_seq != NULL)
+ {
+ x509_buf *oid = &oid_seq->buf;
+ char oid_num_str[1024];
+ const char *oid_str;
+
+ oid_str = x509_oid_get_description(oid);
+ if (oid_str != NULL)
+ {
+ msg (D_HANDSHAKE, "++ Certificate has EKU (str) %s, expects %s",
+ oid_str, expected_oid);
+ if (!strcmp (expected_oid, oid_str))
+ {
+ fFound = true;
+ break;
+ }
+ }
+
+ if (0 == x509_oid_get_numeric_string( oid_num_str,
+ sizeof (oid_num_str), oid))
+ {
+ msg (D_HANDSHAKE, "++ Certificate has EKU (oid) %s, expects %s",
+ oid_num_str, expected_oid);
+ if (!strcmp (expected_oid, oid_num_str))
+ {
+ fFound = true;
+ break;
+ }
+ }
+ oid_seq = oid_seq->next;
+ }
+ }
+
+ return fFound;
+}
+
+const char *
+x509_write_cert(x509_cert *peercert, const char *tmp_dir, struct gc_arena *gc)
+{
+ FILE *peercert_file;
+ const char *peercert_filename="";
+
+ if(!tmp_dir)
+ return NULL;
+
+ /* create tmp file to store peer cert */
+ peercert_filename = create_temp_file (tmp_dir, "pcf", gc);
+
+ /* write peer-cert in tmp-file */
+ peercert_file = fopen(peercert_filename, "w+");
+ if(!peercert_file)
+ {
+ msg (M_ERR, "Failed to open temporary file : %s", peercert_filename);
+ return NULL;
+ }
+
+// if(PEM_write_X509(peercert_file,peercert)<0)
+// {
+ msg (M_ERR, "PolarSSL does not support writing peer certificate in PEM format");
+ fclose(peercert_file);
+ return NULL;
+// }
+
+ fclose(peercert_file);
+ return peercert_filename;
+}
+
+/*
+ * check peer cert against CRL
+ */
+bool
+x509_verify_crl(const char *crl_file, x509_cert *cert, const char *subject)
+{
+ int retval = 0;
+ x509_crl crl = {0};
+
+ if (x509parse_crlfile(&crl, crl_file) != 0)
+ {
+ msg (M_ERR, "CRL: cannot read CRL from file %s", crl_file);
+ goto end;
+ }
+
+ if(cert->issuer_raw.len != crl.issuer_raw.len ||
+ memcmp(crl.issuer_raw.p, cert->issuer_raw.p, crl.issuer_raw.len) != 0)
+ {
+ msg (M_WARN, "CRL: CRL %s is from a different issuer than the issuer of "
+ "certificate %s", crl_file, subject);
+ retval = 1;
+ goto end;
+ }
+
+ if (0 != x509parse_revoked(cert, &crl))
+ {
+ msg (D_HANDSHAKE, "CRL CHECK FAILED: %s is REVOKED", subject);
+ goto end;
+ }
+
+ retval = 1;
+ msg (D_HANDSHAKE, "CRL CHECK OK: %s",subject);
+
+end:
+ x509_crl_free(&crl);
+
+ if (!retval)
+ return true;
+
+ return false;
+}
diff --git a/ssl_verify_polarssl.h b/ssl_verify_polarssl.h
new file mode 100644
index 0000000..cd7eb75
--- /dev/null
+++ b/ssl_verify_polarssl.h
@@ -0,0 +1,79 @@
+/*
+ * OpenVPN -- An application to securely tunnel IP networks
+ * over a single TCP/UDP port, with support for SSL/TLS-based
+ * session authentication and key exchange,
+ * packet encryption, packet authentication, and
+ * packet compression.
+ *
+ * Copyright (C) 2002-2010 OpenVPN Technologies, Inc. <sales@openvpn.net>
+ * Copyright (C) 2010 Fox Crypto B.V. <openvpn@fox-it.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ *
+ * 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 (see the file COPYING included with this
+ * distribution); if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/**
+ * @file Control Channel Verification Module PolarSSL backend
+ */
+
+#ifndef SSL_VERIFY_POLARSSL_H_
+#define SSL_VERIFY_POLARSSL_H_
+
+#include "syshead.h"
+#include "misc.h"
+#include "manage.h"
+#include <polarssl/x509.h>
+
+typedef x509_cert x509_cert_t;
+
+/** @name Function for authenticating a new connection from a remote OpenVPN peer
+ * @{ */
+
+/**
+ * Verify that the remote OpenVPN peer's certificate allows setting up a
+ * VPN tunnel.
+ * @ingroup control_tls
+ *
+ * This callback function is called when a new TLS session is being setup to
+ * determine whether the remote OpenVPN peer's certificate is allowed to
+ * connect. It is called for once for every certificate in the chain. The
+ * callback functionality is configured in the \c init_ssl() function, which
+ * calls the PolarSSL library's \c ssl_set_verify_callback() function with \c
+ * verify_callback() as its callback argument.
+ *
+ * It checks preverify_ok, and registers the certificate hash. If these steps
+ * succeed, it calls the \c verify_cert() function, which performs
+ * OpenVPN-specific verification.
+ *
+ * @param session_obj - The OpenVPN \c tls_session associated with this object,
+ * as set during SSL session setup.
+ * @param cert - The certificate used by PolarSSL.
+ * @param cert_depth - The depth of the current certificate in the chain, with
+ * 0 being the actual certificate.
+ * @param preverify_ok - Whether the remote OpenVPN peer's certificate
+ * past verification. A value of 1 means it
+ * verified successfully, 0 means it failed.
+ *
+ * @return The return value indicates whether the supplied certificate is
+ * allowed to set up a VPN tunnel. The following values can be
+ * returned:
+ * - \c 0: failure, this certificate is not allowed to connect.
+ * - \c 1: success, this certificate is allowed to connect.
+ */
+int verify_callback (void *session_obj, x509_cert *cert, int cert_depth,
+ int preverify_ok);
+
+/** @} name Function for authenticating a new connection from a remote OpenVPN peer */
+
+#endif /* SSL_VERIFY_POLARSSL_H_ */