summaryrefslogtreecommitdiff
path: root/src/openvpn/ssl_polarssl.c
diff options
context:
space:
mode:
authorAlberto Gonzalez Iniesta <agi@inittab.org>2012-11-05 16:28:09 +0100
committerAlberto Gonzalez Iniesta <agi@inittab.org>2012-11-05 16:28:09 +0100
commit8dd0350e1607aa30f7a043c8d5ec7a7eeb874115 (patch)
tree566d0620eb693320cb121dfd93a5675fa704a30b /src/openvpn/ssl_polarssl.c
parent349cfa7acb95abe865209a28e417ec74b56f9bba (diff)
Imported Upstream version 2.3_rc1
Diffstat (limited to 'src/openvpn/ssl_polarssl.c')
-rw-r--r--src/openvpn/ssl_polarssl.c870
1 files changed, 870 insertions, 0 deletions
diff --git a/src/openvpn/ssl_polarssl.c b/src/openvpn/ssl_polarssl.c
new file mode 100644
index 0000000..12318b3
--- /dev/null
+++ b/src/openvpn/ssl_polarssl.c
@@ -0,0 +1,870 @@
+/*
+ * 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
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#elif defined(_MSC_VER)
+#include "config-msvc.h"
+#endif
+
+#include "syshead.h"
+
+#if defined(ENABLE_SSL) && defined(ENABLE_CRYPTO_POLARSSL)
+
+#include "errlevel.h"
+#include "ssl_backend.h"
+#include "buffer.h"
+#include "misc.h"
+#include "manage.h"
+#include "ssl_common.h"
+
+#include <polarssl/sha2.h>
+#include <polarssl/havege.h>
+
+#include "ssl_verify_polarssl.h"
+#include <polarssl/pem.h>
+
+void
+tls_init_lib()
+{
+}
+
+void
+tls_free_lib()
+{
+}
+
+void
+tls_clear_error()
+{
+}
+
+static int default_ciphersuites[] =
+{
+ 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->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->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
+
+ 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, *token;
+ 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);
+
+ token = strtok (tmp_ciphers, ":");
+ while(token)
+ {
+ ctx->allowed_ciphers[i] = ssl_get_ciphersuite_id (token);
+ if (0 != ctx->allowed_ciphers[i])
+ i++;
+ token = strtok (NULL, ":");
+ }
+ free(tmp_ciphers_orig);
+}
+
+void
+tls_ctx_load_dh_params (struct tls_root_ctx *ctx, const char *dh_file,
+ const char *dh_file_inline
+ )
+{
+ 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
+ {
+ 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 " counter_format " bit key",
+ (counter_type) 8 * mpi_size(&ctx->dhm_ctx->P));
+}
+
+int
+tls_ctx_load_pkcs12(struct tls_root_ctx *ctx, const char *pkcs12_file,
+ const char *pkcs12_file_inline,
+ bool load_ca_file
+ )
+{
+ msg(M_FATAL, "PKCS #12 files not yet supported for PolarSSL.");
+ return 0;
+}
+
+#ifdef ENABLE_CRYPTOAPI
+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,
+ const char *cert_file_inline,
+ openvpn_x509_cert_t **x509
+ )
+{
+ ASSERT(NULL != ctx);
+ if (NULL != x509)
+ ASSERT(NULL == *x509);
+
+ 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
+ {
+ 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;
+ }
+}
+
+void
+tls_ctx_free_cert_file (openvpn_x509_cert_t *x509)
+{
+ x509_free(x509);
+}
+
+int
+tls_ctx_load_priv_file (struct tls_root_ctx *ctx, const char *priv_key_file,
+ const char *priv_key_file_inline
+ )
+{
+ int status;
+ ASSERT(NULL != ctx);
+
+ 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_PEM_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
+ {
+ status = x509parse_keyfile(ctx->priv_key, priv_key_file, NULL);
+ if (POLARSSL_ERR_PEM_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_PEM_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 0
+ if (!SSL_CTX_check_private_key (ctx))
+ msg (M_SSLERR, "Private key does not match the certificate");
+#endif
+ return 0;
+}
+
+#ifdef MANAGMENT_EXTERNAL_KEY
+
+int
+tls_ctx_use_external_private_key (struct tls_root_ctx *ctx, openvpn_x509_cert_t *cert)
+{
+ msg(M_FATAL, "Use of management external keys not yet supported for PolarSSL.");
+ return false;
+}
+
+#endif
+
+void tls_ctx_load_ca (struct tls_root_ctx *ctx, const char *ca_file,
+ const char *ca_file_inline,
+ const char *ca_path, bool tls_server
+ )
+{
+ if (ca_path)
+ msg(M_FATAL, "ERROR: PolarSSL cannot handle the capath directive");
+
+ 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
+ {
+ /* 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,
+ const char *extra_certs_file_inline
+ )
+{
+ ASSERT(NULL != ctx);
+
+ 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
+ {
+ 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, size_t out_len )
+{
+ endless_buffer *in = (endless_buffer *) ctx;
+ size_t read_len = 0;
+
+ if (in->first_block == NULL)
+ return POLARSSL_ERR_NET_WANT_READ;
+
+ 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, const unsigned char *in, size_t 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);
+ }
+}
+
+/*
+ * Further personalise the RNG using a hash of the public key
+ */
+void tls_ctx_personalise_random(struct tls_root_ctx *ctx)
+{
+ static char old_sha256_hash[32] = {0};
+ char sha256_hash[32] = {0};
+ ctr_drbg_context *cd_ctx = rand_ctx_get();
+
+ if (NULL != ctx->crt_chain)
+ {
+ x509_cert *cert = ctx->crt_chain;
+
+ sha2(cert->tbs.p, cert->tbs.len, sha256_hash, false);
+ if ( 0 != memcmp(old_sha256_hash, sha256_hash, sizeof(sha256_hash)))
+ {
+ ctr_drbg_update(cd_ctx, sha256_hash, 32);
+ memcpy(old_sha256_hash, sha256_hash, sizeof(old_sha256_hash));
+ }
+ }
+}
+
+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, ctr_drbg_random, rand_ctx_get());
+
+ 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_ciphersuites (ks_ssl->ctx, ssl_ctx->allowed_ciphers);
+ else
+ ssl_set_ciphersuites (ks_ssl->ctx, default_ciphersuites);
+
+ /* Initialise authentication information */
+ if (is_server)
+ ssl_set_dh_param_ctx (ks_ssl->ctx, ssl_ctx->dhm_ctx );
+#if defined(ENABLE_PKCS11)
+ 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
+#endif
+ 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_WANT_WRITE == retval || POLARSSL_ERR_NET_WANT_READ == 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_WANT_WRITE == retval || POLARSSL_ERR_NET_WANT_READ == 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_WANT_WRITE == retval || POLARSSL_ERR_NET_WANT_READ == 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_WANT_WRITE == retval || POLARSSL_ERR_NET_WANT_READ == 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_WANT_WRITE == retval || POLARSSL_ERR_NET_WANT_READ == 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_ciphersuite(ks_ssl->ctx));
+
+ cert = ks_ssl->ctx->peer_cert;
+ if (cert != NULL)
+ {
+ openvpn_snprintf (s2, sizeof (s2), ", " counter_format " bit RSA", (counter_type) cert->rsa.len * 8);
+ }
+
+ msg (D_HANDSHAKE, "%s%s", s1, s2);
+}
+
+void
+show_available_tls_ciphers ()
+{
+ const int *ciphers = ssl_list_ciphersuites();
+
+#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_ciphersuite_name(*ciphers));
+ ciphers++;
+ }
+ printf ("\n");
+}
+
+void
+get_highest_preference_tls_cipher (char *buf, int size)
+{
+ const char *cipher_name;
+ const int *ciphers = ssl_list_ciphersuites();
+ if (*ciphers == 0)
+ msg (M_FATAL, "Cannot retrieve list of supported SSL ciphers.");
+
+ cipher_name = ssl_get_ciphersuite_name(*ciphers);
+ strncpynt (buf, cipher_name, size);
+}
+
+#endif /* defined(ENABLE_SSL) && defined(ENABLE_CRYPTO_POLARSSL) */