0
0
mirror of https://github.com/OpenVPN/openvpn.git synced 2024-09-19 19:42:30 +02:00

Added PolarSSL support:

- Crypto library
 - SSL library
 - PKCS#11 support

For missing features, please see README.polarssl

Signed-off-by: Adriaan de Jong <dejong@fox-it.com>
Acked-by: James Yonan <james@openvpn.net>
Acked-by: Gert Doering <gert@greenie.muc.de>
Signed-off-by: David Sommerseth <davids@redhat.com>
This commit is contained in:
Adriaan de Jong 2011-07-01 14:15:11 +02:00 committed by David Sommerseth
parent 725336282d
commit 53f97e1e91
17 changed files with 2348 additions and 28 deletions

View File

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

23
README.polarssl Normal file
View File

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

View File

@ -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}"
@ -709,6 +711,24 @@ if test "$LZO_STUB" = "yes"; then
AC_DEFINE(LZO_STUB, 1, [Enable LZO stub capability])
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])

View File

@ -35,7 +35,9 @@
#ifdef USE_OPENSSL
#include "crypto_openssl.h"
#endif
#ifdef USE_POLARSSL
#include "crypto_polarssl.h"
#endif
#include "basic.h"

577
crypto_polarssl.c Normal file
View File

@ -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));
}

71
crypto_polarssl.h Normal file
View File

@ -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_ */

View File

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

View File

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

View File

@ -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;

121
pkcs11_polarssl.c Normal file
View File

@ -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) */

View File

@ -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
/*

848
ssl_polarssl.c Normal file
View File

@ -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);
}

85
ssl_polarssl.h Normal file
View File

@ -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_ */

View File

@ -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)

View File

@ -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"

446
ssl_verify_polarssl.c Normal file
View File

@ -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;
}

79
ssl_verify_polarssl.h Normal file
View File

@ -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_ */