diff options
-rw-r--r-- | Makefile.am | 7 | ||||
-rw-r--r-- | README.polarssl | 23 | ||||
-rw-r--r-- | configure.ac | 67 | ||||
-rw-r--r-- | crypto_backend.h | 4 | ||||
-rw-r--r-- | crypto_polarssl.c | 577 | ||||
-rw-r--r-- | crypto_polarssl.h | 71 | ||||
-rw-r--r-- | doxygen/doc_key_generation.h | 11 | ||||
-rw-r--r-- | openvpn-plugin.h | 3 | ||||
-rw-r--r-- | options.c | 25 | ||||
-rw-r--r-- | pkcs11_polarssl.c | 121 | ||||
-rw-r--r-- | ssl_backend.h | 4 | ||||
-rw-r--r-- | ssl_polarssl.c | 848 | ||||
-rw-r--r-- | ssl_polarssl.h | 85 | ||||
-rw-r--r-- | ssl_verify.c | 2 | ||||
-rw-r--r-- | ssl_verify.h | 3 | ||||
-rw-r--r-- | ssl_verify_polarssl.c | 446 | ||||
-rw-r--r-- | ssl_verify_polarssl.h | 79 |
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 @@ -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_ */ |