diff options
author | Alberto Gonzalez Iniesta <agi@inittab.org> | 2012-02-21 15:53:40 +0100 |
---|---|---|
committer | Alberto Gonzalez Iniesta <agi@inittab.org> | 2012-02-21 15:53:40 +0100 |
commit | 349cfa7acb95abe865209a28e417ec74b56f9bba (patch) | |
tree | ad65334821b587c4ecdd461be84c94305ffdb888 /ssl.c |
Imported Upstream version 2.2.1upstream/2.2.1
Diffstat (limited to 'ssl.c')
-rw-r--r-- | ssl.c | 5385 |
1 files changed, 5385 insertions, 0 deletions
@@ -0,0 +1,5385 @@ +/* + * 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> + * + * Additions for eurephia plugin done by: + * David Sommerseth <dazo@users.sourceforge.net> Copyright (C) 2008-2009 + * + * + * 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 + */ + +/* + * The routines in this file deal with dynamically negotiating + * the data channel HMAC and cipher keys through a TLS session. + * + * Both the TLS session and the data channel are multiplexed + * over the same TCP/UDP port. + */ + +#include "syshead.h" + +#if defined(USE_CRYPTO) && defined(USE_SSL) + +#include "ssl.h" +#include "error.h" +#include "common.h" +#include "integer.h" +#include "socket.h" +#include "misc.h" +#include "fdmisc.h" +#include "interval.h" +#include "perf.h" +#include "status.h" +#include "gremlin.h" +#include "pkcs11.h" +#include "list.h" + +#ifdef WIN32 +#include "cryptoapi.h" +#endif + +#include "memdbg.h" + +#ifndef ENABLE_OCC +static const char ssl_default_options_string[] = "V0 UNDEF"; +#endif + +static inline const char * +local_options_string (const struct tls_session *session) +{ +#ifdef ENABLE_OCC + return session->opt->local_options; +#else + return ssl_default_options_string; +#endif +} + +#ifdef MEASURE_TLS_HANDSHAKE_STATS + +static int tls_handshake_success; /* GLOBAL */ +static int tls_handshake_error; /* GLOBAL */ +static int tls_packets_generated; /* GLOBAL */ +static int tls_packets_sent; /* GLOBAL */ + +#define INCR_SENT ++tls_packets_sent +#define INCR_GENERATED ++tls_packets_generated +#define INCR_SUCCESS ++tls_handshake_success +#define INCR_ERROR ++tls_handshake_error + +void +show_tls_performance_stats(void) +{ + msg (D_TLS_DEBUG_LOW, "TLS Handshakes, success=%f%% (good=%d, bad=%d), retransmits=%f%%", + (double) tls_handshake_success / (tls_handshake_success + tls_handshake_error) * 100.0, + tls_handshake_success, tls_handshake_error, + (double) (tls_packets_sent - tls_packets_generated) / tls_packets_generated * 100.0); +} +#else + +#define INCR_SENT +#define INCR_GENERATED +#define INCR_SUCCESS +#define INCR_ERROR + +#endif + +#ifdef BIO_DEBUG + +#warning BIO_DEBUG defined + +static FILE *biofp; /* GLOBAL */ +static bool biofp_toggle; /* GLOBAL */ +static time_t biofp_last_open; /* GLOBAL */ +static const int biofp_reopen_interval = 600; /* GLOBAL */ + +static void +close_biofp() +{ + if (biofp) + { + ASSERT (!fclose (biofp)); + biofp = NULL; + } +} + +static void +open_biofp() +{ + const time_t current = time (NULL); + const pid_t pid = getpid (); + + if (biofp_last_open + biofp_reopen_interval < current) + close_biofp(); + if (!biofp) + { + char fn[256]; + openvpn_snprintf(fn, sizeof(fn), "bio/%d-%d.log", pid, biofp_toggle); + biofp = fopen (fn, "w"); + ASSERT (biofp); + biofp_last_open = time (NULL); + biofp_toggle ^= 1; + } +} + +static void +bio_debug_data (const char *mode, BIO *bio, const uint8_t *buf, int len, const char *desc) +{ + struct gc_arena gc = gc_new (); + if (len > 0) + { + open_biofp(); + fprintf(biofp, "BIO_%s %s time=" time_format " bio=" ptr_format " len=%d data=%s\n", + mode, desc, time (NULL), (ptr_type)bio, len, format_hex (buf, len, 0, &gc)); + fflush (biofp); + } + gc_free (&gc); +} + +static void +bio_debug_oc (const char *mode, BIO *bio) +{ + open_biofp(); + fprintf(biofp, "BIO %s time=" time_format " bio=" ptr_format "\n", + mode, time (NULL), (ptr_type)bio); + fflush (biofp); +} + +#endif + +/* + * Max number of bytes we will add + * for data structures common to both + * data and control channel packets. + * (opcode only). + */ +void +tls_adjust_frame_parameters(struct frame *frame) +{ + frame_add_to_extra_frame (frame, 1); /* space for opcode */ +} + +/* + * Max number of bytes we will add + * to control channel packet. + */ +static void +tls_init_control_channel_frame_parameters(const struct frame *data_channel_frame, + struct frame *frame) +{ + /* + * frame->extra_frame is already initialized with tls_auth buffer requirements, + * if --tls-auth is enabled. + */ + + /* inherit link MTU and extra_link from data channel */ + frame->link_mtu = data_channel_frame->link_mtu; + frame->extra_link = data_channel_frame->extra_link; + + /* set extra_frame */ + tls_adjust_frame_parameters (frame); + reliable_ack_adjust_frame_parameters (frame, CONTROL_SEND_ACK_MAX); + frame_add_to_extra_frame (frame, SID_SIZE + sizeof (packet_id_type)); + + /* set dynamic link MTU to minimum value */ + frame_set_mtu_dynamic (frame, 0, SET_MTU_TUN); +} + +/* + * Allocate space in SSL objects + * in which to store a struct tls_session + * pointer back to parent. + */ + +static int mydata_index; /* GLOBAL */ + +static void +ssl_set_mydata_index () +{ + mydata_index = SSL_get_ex_new_index (0, "struct session *", NULL, NULL, NULL); + ASSERT (mydata_index >= 0); +} + +void +init_ssl_lib () +{ + SSL_library_init (); + SSL_load_error_strings (); + OpenSSL_add_all_algorithms (); + + init_crypto_lib(); + + /* + * If you build the OpenSSL library and OpenVPN with + * CRYPTO_MDEBUG, you will get a listing of OpenSSL + * memory leaks on program termination. + */ +#ifdef CRYPTO_MDEBUG + CRYPTO_mem_ctrl(CRYPTO_MEM_CHECK_ON); +#endif + + ssl_set_mydata_index (); +} + +void +free_ssl_lib () +{ +#ifdef CRYPTO_MDEBUG + FILE* fp = fopen ("sdlog", "w"); + ASSERT (fp); + CRYPTO_mem_leaks_fp (fp); + fclose (fp); +#endif + + uninit_crypto_lib (); + EVP_cleanup (); + ERR_free_strings (); +} + +/* + * OpenSSL library calls pem_password_callback if the + * private key is protected by a password. + */ + +static struct user_pass passbuf; /* GLOBAL */ + +void +pem_password_setup (const char *auth_file) +{ + if (!strlen (passbuf.password)) + get_user_pass (&passbuf, auth_file, UP_TYPE_PRIVATE_KEY, GET_USER_PASS_MANAGEMENT|GET_USER_PASS_SENSITIVE|GET_USER_PASS_PASSWORD_ONLY); +} + +int +pem_password_callback (char *buf, int size, int rwflag, void *u) +{ + if (buf) + { + /* prompt for password even if --askpass wasn't specified */ + pem_password_setup (NULL); + strncpynt (buf, passbuf.password, size); + purge_user_pass (&passbuf, false); + + return strlen (buf); + } + return 0; +} + +/* + * Auth username/password handling + */ + +static bool auth_user_pass_enabled; /* GLOBAL */ +static struct user_pass auth_user_pass; /* GLOBAL */ + +#ifdef ENABLE_CLIENT_CR +static char *auth_challenge; /* GLOBAL */ +#endif + +void +auth_user_pass_setup (const char *auth_file) +{ + auth_user_pass_enabled = true; + if (!auth_user_pass.defined) + { +#if AUTO_USERID + get_user_pass_auto_userid (&auth_user_pass, auth_file); +#elif defined(ENABLE_CLIENT_CR) + get_user_pass_cr (&auth_user_pass, auth_file, UP_TYPE_AUTH, GET_USER_PASS_MANAGEMENT|GET_USER_PASS_SENSITIVE, auth_challenge); +#else + get_user_pass (&auth_user_pass, auth_file, UP_TYPE_AUTH, GET_USER_PASS_MANAGEMENT|GET_USER_PASS_SENSITIVE); +#endif + } +} + +/* + * Disable password caching + */ +void +ssl_set_auth_nocache (void) +{ + passbuf.nocache = true; + auth_user_pass.nocache = true; +} + +/* + * Forget private key password AND auth-user-pass username/password. + */ +void +ssl_purge_auth (void) +{ +#ifdef USE_PKCS11 + pkcs11_logout (); +#endif + purge_user_pass (&passbuf, true); + purge_user_pass (&auth_user_pass, true); +#ifdef ENABLE_CLIENT_CR + ssl_purge_auth_challenge(); +#endif +} + +#ifdef ENABLE_CLIENT_CR + +void +ssl_purge_auth_challenge (void) +{ + free (auth_challenge); + auth_challenge = NULL; +} + +void +ssl_put_auth_challenge (const char *cr_str) +{ + ssl_purge_auth_challenge(); + auth_challenge = string_alloc(cr_str, NULL); +} + +#endif + +/* + * OpenSSL callback to get a temporary RSA key, mostly + * used for export ciphers. + */ +static RSA * +tmp_rsa_cb (SSL * s, int is_export, int keylength) +{ + static RSA *rsa_tmp = NULL; + if (rsa_tmp == NULL) + { + msg (D_HANDSHAKE, "Generating temp (%d bit) RSA key", keylength); + rsa_tmp = RSA_generate_key (keylength, RSA_F4, NULL, NULL); + } + return (rsa_tmp); +} + +/* + * Cert hash functions + */ +static void +cert_hash_remember (struct tls_session *session, const int error_depth, const unsigned char *sha1_hash) +{ + if (error_depth >= 0 && error_depth < MAX_CERT_DEPTH) + { + if (!session->cert_hash_set) + ALLOC_OBJ_CLEAR (session->cert_hash_set, struct cert_hash_set); + if (!session->cert_hash_set->ch[error_depth]) + ALLOC_OBJ (session->cert_hash_set->ch[error_depth], struct cert_hash); + { + struct cert_hash *ch = session->cert_hash_set->ch[error_depth]; + memcpy (ch->sha1_hash, sha1_hash, SHA_DIGEST_LENGTH); + } + } +} + +#if 0 +static void +cert_hash_print (const struct cert_hash_set *chs, int msglevel) +{ + struct gc_arena gc = gc_new (); + msg (msglevel, "CERT_HASH"); + if (chs) + { + int i; + for (i = 0; i < MAX_CERT_DEPTH; ++i) + { + const struct cert_hash *ch = chs->ch[i]; + if (ch) + msg (msglevel, "%d:%s", i, format_hex(ch->sha1_hash, SHA_DIGEST_LENGTH, 0, &gc)); + } + } + gc_free (&gc); +} +#endif + +static void +cert_hash_free (struct cert_hash_set *chs) +{ + if (chs) + { + int i; + for (i = 0; i < MAX_CERT_DEPTH; ++i) + free (chs->ch[i]); + free (chs); + } +} + +static bool +cert_hash_compare (const struct cert_hash_set *chs1, const struct cert_hash_set *chs2) +{ + if (chs1 && chs2) + { + int i; + for (i = 0; i < MAX_CERT_DEPTH; ++i) + { + const struct cert_hash *ch1 = chs1->ch[i]; + const struct cert_hash *ch2 = chs2->ch[i]; + + if (!ch1 && !ch2) + continue; + else if (ch1 && ch2 && !memcmp (ch1->sha1_hash, ch2->sha1_hash, SHA_DIGEST_LENGTH)) + continue; + else + return false; + } + return true; + } + else if (!chs1 && !chs2) + return true; + else + return false; +} + +static struct cert_hash_set * +cert_hash_copy (const struct cert_hash_set *chs) +{ + struct cert_hash_set *dest = NULL; + if (chs) + { + int i; + ALLOC_OBJ_CLEAR (dest, struct cert_hash_set); + for (i = 0; i < MAX_CERT_DEPTH; ++i) + { + const struct cert_hash *ch = chs->ch[i]; + if (ch) + { + ALLOC_OBJ (dest->ch[i], struct cert_hash); + memcpy (dest->ch[i]->sha1_hash, ch->sha1_hash, SHA_DIGEST_LENGTH); + } + } + } + return dest; +} + +/* + * Extract a field from an X509 subject name. + * + * Example: + * + * /C=US/ST=CO/L=Denver/O=ORG/CN=First-CN/CN=Test-CA/Email=jim@yonan.net + * + * The common name is 'Test-CA' + * + * Return true on success, false on error (insufficient buffer size in 'out' + * to contain result is grounds for error). + */ +static bool +extract_x509_field_ssl (X509_NAME *x509, const char *field_name, char *out, int size) +{ + int lastpos = -1; + int tmp = -1; + X509_NAME_ENTRY *x509ne = 0; + ASN1_STRING *asn1 = 0; + unsigned char *buf = (unsigned char *)1; /* bug in OpenSSL 0.9.6b ASN1_STRING_to_UTF8 requires this workaround */ + int nid = OBJ_txt2nid((char *)field_name); + + ASSERT (size > 0); + *out = '\0'; + do { + lastpos = tmp; + tmp = X509_NAME_get_index_by_NID(x509, nid, lastpos); + } while (tmp > -1); + + /* Nothing found */ + if (lastpos == -1) + return false; + + x509ne = X509_NAME_get_entry(x509, lastpos); + if (!x509ne) + return false; + + asn1 = X509_NAME_ENTRY_get_data(x509ne); + if (!asn1) + return false; + tmp = ASN1_STRING_to_UTF8(&buf, asn1); + if (tmp <= 0) + return false; + + strncpynt(out, (char *)buf, size); + + { + const bool ret = (strlen ((char *)buf) < size); + OPENSSL_free (buf); + return ret; + } +} + +/* + * Save X509 fields to environment, using the naming convention: + * + * X509_{cert_depth}_{name}={value} + */ +static void +setenv_x509 (struct env_set *es, const int error_depth, X509_NAME *x509) +{ + int i, n; + int fn_nid; + ASN1_OBJECT *fn; + ASN1_STRING *val; + X509_NAME_ENTRY *ent; + const char *objbuf; + unsigned char *buf; + char *name_expand; + size_t name_expand_size; + + n = X509_NAME_entry_count (x509); + for (i = 0; i < n; ++i) + { + ent = X509_NAME_get_entry (x509, i); + if (!ent) + continue; + fn = X509_NAME_ENTRY_get_object (ent); + if (!fn) + continue; + val = X509_NAME_ENTRY_get_data (ent); + if (!val) + continue; + fn_nid = OBJ_obj2nid (fn); + if (fn_nid == NID_undef) + continue; + objbuf = OBJ_nid2sn (fn_nid); + if (!objbuf) + continue; + buf = (unsigned char *)1; /* bug in OpenSSL 0.9.6b ASN1_STRING_to_UTF8 requires this workaround */ + if (ASN1_STRING_to_UTF8 (&buf, val) <= 0) + continue; + name_expand_size = 64 + strlen (objbuf); + name_expand = (char *) malloc (name_expand_size); + check_malloc_return (name_expand); + openvpn_snprintf (name_expand, name_expand_size, "X509_%d_%s", error_depth, objbuf); + string_mod (name_expand, CC_PRINT, CC_CRLF, '_'); + string_mod ((char*)buf, CC_PRINT, CC_CRLF, '_'); + setenv_str (es, name_expand, (char*)buf); + free (name_expand); + OPENSSL_free (buf); + } +} + +static void +setenv_untrusted (struct tls_session *session) +{ + setenv_link_socket_actual (session->opt->es, "untrusted", &session->untrusted_addr, SA_IP_PORT); +} + +static void +set_common_name (struct tls_session *session, const char *common_name) +{ + if (session->common_name) + { + free (session->common_name); + session->common_name = NULL; +#ifdef ENABLE_PF + session->common_name_hashval = 0; +#endif + } + if (common_name) + { + session->common_name = string_alloc (common_name, NULL); +#ifdef ENABLE_PF + { + const uint32_t len = (uint32_t) strlen (common_name); + if (len) + session->common_name_hashval = hash_func ((const uint8_t*)common_name, len+1, 0); + else + session->common_name_hashval = 0; + } +#endif + } +} + +#if OPENSSL_VERSION_NUMBER >= 0x00907000L + +bool verify_cert_eku (X509 *x509, const char * const expected_oid) { + + EXTENDED_KEY_USAGE *eku = NULL; + bool fFound = false; + + if ((eku = (EXTENDED_KEY_USAGE *)X509_get_ext_d2i (x509, NID_ext_key_usage, NULL, NULL)) == NULL) { + msg (D_HANDSHAKE, "Certificate does not have extended key usage extension"); + } + else { + int i; + + msg (D_HANDSHAKE, "Validating certificate extended key usage"); + for(i = 0; !fFound && i < sk_ASN1_OBJECT_num (eku); i++) { + ASN1_OBJECT *oid = sk_ASN1_OBJECT_value (eku, i); + char szOid[1024]; + + if (!fFound && OBJ_obj2txt (szOid, sizeof (szOid), oid, 0) != -1) { + msg (D_HANDSHAKE, "++ Certificate has EKU (str) %s, expects %s", szOid, expected_oid); + if (!strcmp (expected_oid, szOid)) { + fFound = true; + } + } + if (!fFound && OBJ_obj2txt (szOid, sizeof (szOid), oid, 1) != -1) { + msg (D_HANDSHAKE, "++ Certificate has EKU (oid) %s, expects %s", szOid, expected_oid); + if (!strcmp (expected_oid, szOid)) { + fFound = true; + } + } + } + } + + if (eku != NULL) { + sk_ASN1_OBJECT_pop_free (eku, ASN1_OBJECT_free); + } + + return fFound; +} + +bool verify_cert_ku (X509 *x509, const unsigned * const expected_ku, int expected_len) { + + ASN1_BIT_STRING *ku = NULL; + bool fFound = false; + + if ((ku = (ASN1_BIT_STRING *)X509_get_ext_d2i (x509, NID_key_usage, NULL, NULL)) == NULL) { + msg (D_HANDSHAKE, "Certificate does not have key usage extension"); + } + else { + unsigned nku = 0; + int i; + for (i=0;i<8;i++) { + if (ASN1_BIT_STRING_get_bit (ku, i)) { + nku |= 1<<(7-i); + } + } + + /* + * Fixup if no LSB bits + */ + if ((nku & 0xff) == 0) { + nku >>= 8; + } + + 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; + } + } + } + } + + if (ku != NULL) { + ASN1_BIT_STRING_free (ku); + } + + return fFound; +} + +#endif /* OPENSSL_VERSION_NUMBER */ + +/* + * nsCertType checking + */ + +#define verify_nsCertType(x, usage) (((x)->ex_flags & EXFLAG_NSCERT) && ((x)->ex_nscert & (usage))) + +static const char * +print_nsCertType (int type) +{ + switch (type) + { + case NS_SSL_SERVER: + return "SERVER"; + case NS_SSL_CLIENT: + return "CLIENT"; + default: + return "?"; + } +} + +static void +string_mod_sslname (char *str, const unsigned int restrictive_flags, const unsigned int ssl_flags) +{ + if (ssl_flags & SSLF_NO_NAME_REMAPPING) + string_mod (str, CC_PRINT, CC_CRLF, '_'); + else + string_mod (str, restrictive_flags, 0, '_'); +} + +/* Get peer cert and store it in pem format in a temporary file + * in tmp_dir + */ + +const char * +get_peer_cert(X509_STORE_CTX *ctx, const char *tmp_dir, struct gc_arena *gc) +{ + X509 *peercert; + FILE *peercert_file; + const char *peercert_filename=""; + + if(!tmp_dir) + return NULL; + + /* get peer cert */ + peercert = X509_STORE_CTX_get_current_cert(ctx); + if(!peercert) + { + msg (M_ERR, "Unable to get peer certificate from current context"); + 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, "Failed to write peer certificate in PEM format"); + fclose(peercert_file); + return NULL; + } + + fclose(peercert_file); + return peercert_filename; +} + +char * x509_username_field; /* GLOBAL */ + +/* + * Our verify callback function -- check + * that an incoming peer certificate is good. + */ + +static int +verify_callback (int preverify_ok, X509_STORE_CTX * ctx) +{ + char *subject = NULL; + char envname[64]; + char common_name[TLS_USERNAME_LEN]; + SSL *ssl; + struct tls_session *session; + const struct tls_options *opt; + const int max_depth = MAX_CERT_DEPTH; + struct argv argv = argv_new (); + + /* get the tls_session pointer */ + ssl = X509_STORE_CTX_get_ex_data (ctx, SSL_get_ex_data_X509_STORE_CTX_idx()); + ASSERT (ssl); + session = (struct tls_session *) SSL_get_ex_data (ssl, mydata_index); + ASSERT (session); + opt = session->opt; + ASSERT (opt); + + session->verified = false; + + /* get the X509 name */ + subject = X509_NAME_oneline (X509_get_subject_name (ctx->current_cert), NULL, 0); + if (!subject) + { + msg (D_TLS_ERRORS, "VERIFY ERROR: depth=%d, could not extract X509 subject string from certificate", ctx->error_depth); + goto err; + } + + /* Save X509 fields in environment */ + setenv_x509 (opt->es, ctx->error_depth, X509_get_subject_name (ctx->current_cert)); + + /* enforce character class restrictions in X509 name */ + string_mod_sslname (subject, X509_NAME_CHAR_CLASS, opt->ssl_flags); + string_replace_leading (subject, '-', '_'); + + /* extract the username (default is CN) */ + if (!extract_x509_field_ssl (X509_get_subject_name (ctx->current_cert), x509_username_field, common_name, sizeof(common_name))) + { + if (!ctx->error_depth) + { + msg (D_TLS_ERRORS, "VERIFY ERROR: could not extract %s from X509 subject string ('%s') -- note that the username length is limited to %d characters", + x509_username_field, + subject, + TLS_USERNAME_LEN); + goto err; + } + } + + + string_mod_sslname (common_name, COMMON_NAME_CHAR_CLASS, opt->ssl_flags); + + cert_hash_remember (session, ctx->error_depth, ctx->current_cert->sha1_hash); + +#if 0 /* print some debugging info */ + { + struct gc_arena gc = gc_new (); + msg (M_INFO, "LOCAL OPT[%d]: %s", ctx->error_depth, opt->local_options); + msg (M_INFO, "X509[%d]: %s", ctx->error_depth, subject); + msg (M_INFO, "SHA1[%d]: %s", ctx->error_depth, format_hex(ctx->current_cert->sha1_hash, SHA_DIGEST_LENGTH, 0, &gc)); + gc_free (&gc); + } +#endif + + /* did peer present cert which was signed our root cert? */ + if (!preverify_ok) + { + /* Remote site specified a certificate, but it's not correct */ + msg (D_TLS_ERRORS, "VERIFY ERROR: depth=%d, error=%s: %s", + ctx->error_depth, X509_verify_cert_error_string (ctx->error), subject); + goto err; /* Reject connection */ + } + + /* warn if cert chain is too deep */ + if (ctx->error_depth >= max_depth) + { + msg (D_TLS_ERRORS, "TLS Error: Convoluted certificate chain detected with depth [%d] greater than %d", ctx->error_depth, max_depth); + goto err; /* Reject connection */ + } + + /* save common name in session object */ + if (ctx->error_depth == 0) + set_common_name (session, common_name); + + /* export subject name string as environmental variable */ + session->verify_maxlevel = max_int (session->verify_maxlevel, ctx->error_depth); + openvpn_snprintf (envname, sizeof(envname), "tls_id_%d", ctx->error_depth); + setenv_str (opt->es, envname, subject); + +#ifdef ENABLE_EUREPHIA + /* export X509 cert SHA1 fingerprint */ + { + struct gc_arena gc = gc_new (); + openvpn_snprintf (envname, sizeof(envname), "tls_digest_%d", ctx->error_depth); + setenv_str (opt->es, envname, + format_hex_ex(ctx->current_cert->sha1_hash, SHA_DIGEST_LENGTH, 0, 1, ":", &gc)); + gc_free(&gc); + } +#endif +#if 0 + /* export common name string as environmental variable */ + openvpn_snprintf (envname, sizeof(envname), "tls_common_name_%d", ctx->error_depth); + setenv_str (opt->es, envname, common_name); +#endif + + /* export serial number as environmental variable */ + { + BIO *bio = NULL; + char serial[100]; + int n1, n2; + + CLEAR (serial); + if ((bio = BIO_new (BIO_s_mem ())) == NULL) + { + msg (M_WARN, "CALLBACK: Cannot create BIO (for tls_serial_%d)", ctx->error_depth); + } + else + { + /* "prints" the serial number onto the BIO and read it back */ + if ( ! ( ( (n1 = i2a_ASN1_INTEGER(bio, X509_get_serialNumber (ctx->current_cert))) >= 0 ) && + ( (n2 = BIO_read (bio, serial, sizeof (serial)-1)) >= 0 ) && + ( n1 == n2 ) ) ) + { + msg (M_WARN, "CALLBACK: Error reading/writing BIO (for tls_serial_%d)", ctx->error_depth); + CLEAR (serial); /* empty string */ + } + + openvpn_snprintf (envname, sizeof(envname), "tls_serial_%d", ctx->error_depth); + setenv_str (opt->es, envname, serial); + BIO_free(bio); + } + } + + /* export current untrusted IP */ + setenv_untrusted (session); + + /* verify certificate nsCertType */ + if (opt->ns_cert_type && ctx->error_depth == 0) + { + if (verify_nsCertType (ctx->current_cert, opt->ns_cert_type)) + { + msg (D_HANDSHAKE, "VERIFY OK: nsCertType=%s", + print_nsCertType (opt->ns_cert_type)); + } + else + { + msg (D_HANDSHAKE, "VERIFY nsCertType ERROR: %s, require nsCertType=%s", + subject, print_nsCertType (opt->ns_cert_type)); + goto err; /* Reject connection */ + } + } + +#if OPENSSL_VERSION_NUMBER >= 0x00907000L + + /* verify certificate ku */ + if (opt->remote_cert_ku[0] != 0 && ctx->error_depth == 0) + { + if (verify_cert_ku (ctx->current_cert, opt->remote_cert_ku, MAX_PARMS)) + { + msg (D_HANDSHAKE, "VERIFY KU OK"); + } + else + { + msg (D_HANDSHAKE, "VERIFY KU ERROR"); + goto err; /* Reject connection */ + } + } + + /* verify certificate eku */ + if (opt->remote_cert_eku != NULL && ctx->error_depth == 0) + { + if (verify_cert_eku (ctx->current_cert, opt->remote_cert_eku)) + { + msg (D_HANDSHAKE, "VERIFY EKU OK"); + } + else + { + msg (D_HANDSHAKE, "VERIFY EKU ERROR"); + goto err; /* Reject connection */ + } + } + +#endif /* OPENSSL_VERSION_NUMBER */ + + /* verify X509 name or common name against --tls-remote */ + if (opt->verify_x509name && strlen (opt->verify_x509name) > 0 && ctx->error_depth == 0) + { + if (strcmp (opt->verify_x509name, subject) == 0 + || strncmp (opt->verify_x509name, common_name, strlen (opt->verify_x509name)) == 0) + msg (D_HANDSHAKE, "VERIFY X509NAME OK: %s", subject); + else + { + msg (D_HANDSHAKE, "VERIFY X509NAME ERROR: %s, must be %s", + subject, opt->verify_x509name); + goto err; /* Reject connection */ + } + } + + /* call --tls-verify plug-in(s) */ + if (plugin_defined (opt->plugins, OPENVPN_PLUGIN_TLS_VERIFY)) + { + int ret; + + argv_printf (&argv, "%d %s", + ctx->error_depth, + subject); + + ret = plugin_call (opt->plugins, OPENVPN_PLUGIN_TLS_VERIFY, &argv, NULL, opt->es); + + if (ret == OPENVPN_PLUGIN_FUNC_SUCCESS) + { + msg (D_HANDSHAKE, "VERIFY PLUGIN OK: depth=%d, %s", + ctx->error_depth, subject); + } + else + { + msg (D_HANDSHAKE, "VERIFY PLUGIN ERROR: depth=%d, %s", + ctx->error_depth, subject); + goto err; /* Reject connection */ + } + } + + /* run --tls-verify script */ + if (opt->verify_command) + { + const char *tmp_file = NULL; + struct gc_arena gc; + int ret; + + setenv_str (opt->es, "script_type", "tls-verify"); + + if (opt->verify_export_cert) + { + gc = gc_new(); + if ((tmp_file=get_peer_cert(ctx, opt->verify_export_cert,&gc))) + { + setenv_str(opt->es, "peer_cert", tmp_file); + } + } + + argv_printf (&argv, "%sc %d %s", + opt->verify_command, + ctx->error_depth, + subject); + argv_msg_prefix (D_TLS_DEBUG, &argv, "TLS: executing verify command"); + ret = openvpn_run_script (&argv, opt->es, 0, "--tls-verify script"); + + if (opt->verify_export_cert) + { + if (tmp_file) + delete_file(tmp_file); + gc_free(&gc); + } + + if (ret) + { + msg (D_HANDSHAKE, "VERIFY SCRIPT OK: depth=%d, %s", + ctx->error_depth, subject); + } + else + { + msg (D_HANDSHAKE, "VERIFY SCRIPT ERROR: depth=%d, %s", + ctx->error_depth, subject); + goto err; /* Reject connection */ + } + } + + /* check peer cert against CRL */ + if (opt->crl_file) + { + X509_CRL *crl=NULL; + X509_REVOKED *revoked; + BIO *in=NULL; + int n,i,retval = 0; + + in=BIO_new(BIO_s_file()); + + if (in == NULL) { + msg (M_ERR, "CRL: BIO err"); + goto end; + } + if (BIO_read_filename(in, opt->crl_file) <= 0) { + msg (M_ERR, "CRL: cannot read: %s", opt->crl_file); + goto end; + } + crl=PEM_read_bio_X509_CRL(in,NULL,NULL,NULL); + if (crl == NULL) { + msg (M_ERR, "CRL: cannot read CRL from file %s", opt->crl_file); + goto end; + } + + if (X509_NAME_cmp(X509_CRL_get_issuer(crl), X509_get_issuer_name(ctx->current_cert)) != 0) { + msg (M_WARN, "CRL: CRL %s is from a different issuer than the issuer of certificate %s", opt->crl_file, subject); + retval = 1; + goto end; + } + + n = sk_X509_REVOKED_num(X509_CRL_get_REVOKED(crl)); + + for (i = 0; i < n; i++) { + revoked = (X509_REVOKED *)sk_X509_REVOKED_value(X509_CRL_get_REVOKED(crl), i); + if (ASN1_INTEGER_cmp(revoked->serialNumber, X509_get_serialNumber(ctx->current_cert)) == 0) { + msg (D_HANDSHAKE, "CRL CHECK FAILED: %s is REVOKED",subject); + goto end; + } + } + + retval = 1; + msg (D_HANDSHAKE, "CRL CHECK OK: %s",subject); + + end: + + BIO_free(in); + if (crl) + X509_CRL_free (crl); + if (!retval) + goto err; + } + + msg (D_HANDSHAKE, "VERIFY OK: depth=%d, %s", ctx->error_depth, subject); + + session->verified = true; + OPENSSL_free (subject); + argv_reset (&argv); + return 1; /* Accept connection */ + + err: + ERR_clear_error (); + OPENSSL_free (subject); + argv_reset (&argv); + return 0; /* Reject connection */ +} + +void +tls_set_common_name (struct tls_multi *multi, const char *common_name) +{ + if (multi) + set_common_name (&multi->session[TM_ACTIVE], common_name); +} + +const char * +tls_common_name (const struct tls_multi *multi, const bool null) +{ + const char *ret = NULL; + if (multi) + ret = multi->session[TM_ACTIVE].common_name; + if (ret && strlen (ret)) + return ret; + else if (null) + return NULL; + else + return "UNDEF"; +} + +void +tls_lock_common_name (struct tls_multi *multi) +{ + const char *cn = multi->session[TM_ACTIVE].common_name; + if (cn && !multi->locked_cn) + multi->locked_cn = string_alloc (cn, NULL); +} + +void +tls_lock_cert_hash_set (struct tls_multi *multi) +{ + const struct cert_hash_set *chs = multi->session[TM_ACTIVE].cert_hash_set; + if (chs && !multi->locked_cert_hash_set) + multi->locked_cert_hash_set = cert_hash_copy (chs); +} + +static bool +tls_lock_username (struct tls_multi *multi, const char *username) +{ + if (multi->locked_username) + { + if (!username || strcmp (username, multi->locked_username)) + { + msg (D_TLS_ERRORS, "TLS Auth Error: username attempted to change from '%s' to '%s' -- tunnel disabled", + multi->locked_username, + np(username)); + + /* disable the tunnel */ + tls_deauthenticate (multi); + return false; + } + } + else + { + if (username) + multi->locked_username = string_alloc (username, NULL); + } + return true; +} + +#ifdef ENABLE_DEF_AUTH +/* key_state_test_auth_control_file return values, + NOTE: acf_merge indexing depends on these values */ +#define ACF_UNDEFINED 0 +#define ACF_SUCCEEDED 1 +#define ACF_DISABLED 2 +#define ACF_FAILED 3 +#endif + +#ifdef MANAGEMENT_DEF_AUTH +static void +man_def_auth_set_client_reason (struct tls_multi *multi, const char *client_reason) +{ + if (multi->client_reason) + { + free (multi->client_reason); + multi->client_reason = NULL; + } + if (client_reason && strlen (client_reason)) + multi->client_reason = string_alloc (client_reason, NULL); +} + +static inline unsigned int +man_def_auth_test (const struct key_state *ks) +{ + if (management_enable_def_auth (management)) + return ks->mda_status; + else + return ACF_DISABLED; +} +#endif + +#ifdef PLUGIN_DEF_AUTH + +/* + * auth_control_file functions + */ + +static void +key_state_rm_auth_control_file (struct key_state *ks) +{ + if (ks && ks->auth_control_file) + { + delete_file (ks->auth_control_file); + free (ks->auth_control_file); + ks->auth_control_file = NULL; + } +} + +static void +key_state_gen_auth_control_file (struct key_state *ks, const struct tls_options *opt) +{ + struct gc_arena gc = gc_new (); + const char *acf; + + key_state_rm_auth_control_file (ks); + acf = create_temp_file (opt->tmp_dir, "acf", &gc); + if( acf ) { + ks->auth_control_file = string_alloc (acf, NULL); + setenv_str (opt->es, "auth_control_file", ks->auth_control_file); + } /* FIXME: Should have better error handling? */ + gc_free (&gc); +} + +static unsigned int +key_state_test_auth_control_file (struct key_state *ks) +{ + if (ks && ks->auth_control_file) + { + unsigned int ret = ks->auth_control_status; + if (ret == ACF_UNDEFINED) + { + FILE *fp = fopen (ks->auth_control_file, "r"); + if (fp) + { + const int c = fgetc (fp); + if (c == '1') + ret = ACF_SUCCEEDED; + else if (c == '0') + ret = ACF_FAILED; + fclose (fp); + ks->auth_control_status = ret; + } + } + return ret; + } + return ACF_DISABLED; +} + +#endif + +/* + * Return current session authentication state. Return + * value is TLS_AUTHENTICATION_x. + */ + +int +tls_authentication_status (struct tls_multi *multi, const int latency) +{ + bool deferred = false; + bool success = false; + bool active = false; + +#ifdef ENABLE_DEF_AUTH + static const unsigned char acf_merge[] = + { + ACF_UNDEFINED, /* s1=ACF_UNDEFINED s2=ACF_UNDEFINED */ + ACF_UNDEFINED, /* s1=ACF_UNDEFINED s2=ACF_SUCCEEDED */ + ACF_UNDEFINED, /* s1=ACF_UNDEFINED s2=ACF_DISABLED */ + ACF_FAILED, /* s1=ACF_UNDEFINED s2=ACF_FAILED */ + ACF_UNDEFINED, /* s1=ACF_SUCCEEDED s2=ACF_UNDEFINED */ + ACF_SUCCEEDED, /* s1=ACF_SUCCEEDED s2=ACF_SUCCEEDED */ + ACF_SUCCEEDED, /* s1=ACF_SUCCEEDED s2=ACF_DISABLED */ + ACF_FAILED, /* s1=ACF_SUCCEEDED s2=ACF_FAILED */ + ACF_UNDEFINED, /* s1=ACF_DISABLED s2=ACF_UNDEFINED */ + ACF_SUCCEEDED, /* s1=ACF_DISABLED s2=ACF_SUCCEEDED */ + ACF_DISABLED, /* s1=ACF_DISABLED s2=ACF_DISABLED */ + ACF_FAILED, /* s1=ACF_DISABLED s2=ACF_FAILED */ + ACF_FAILED, /* s1=ACF_FAILED s2=ACF_UNDEFINED */ + ACF_FAILED, /* s1=ACF_FAILED s2=ACF_SUCCEEDED */ + ACF_FAILED, /* s1=ACF_FAILED s2=ACF_DISABLED */ + ACF_FAILED /* s1=ACF_FAILED s2=ACF_FAILED */ + }; +#endif + + if (multi) + { + int i; + +#ifdef ENABLE_DEF_AUTH + if (latency && multi->tas_last && multi->tas_last + latency >= now) + return TLS_AUTHENTICATION_UNDEFINED; + multi->tas_last = now; +#endif + + for (i = 0; i < KEY_SCAN_SIZE; ++i) + { + struct key_state *ks = multi->key_scan[i]; + if (DECRYPT_KEY_ENABLED (multi, ks)) + { + active = true; + if (ks->authenticated) + { +#ifdef ENABLE_DEF_AUTH + unsigned int s1 = ACF_DISABLED; + unsigned int s2 = ACF_DISABLED; +#ifdef PLUGIN_DEF_AUTH + s1 = key_state_test_auth_control_file (ks); +#endif +#ifdef MANAGEMENT_DEF_AUTH + s2 = man_def_auth_test (ks); +#endif + ASSERT (s1 < 4 && s2 < 4); + switch (acf_merge[(s1<<2) + s2]) + { + case ACF_SUCCEEDED: + case ACF_DISABLED: + success = true; + ks->auth_deferred = false; + break; + case ACF_UNDEFINED: + if (now < ks->auth_deferred_expire) + deferred = true; + break; + case ACF_FAILED: + ks->authenticated = false; + break; + default: + ASSERT (0); + } +#else + success = true; +#endif + } + } + } + } + +#if 0 + dmsg (D_TLS_ERRORS, "TAS: a=%d s=%d d=%d", active, success, deferred); +#endif + + if (success) + return TLS_AUTHENTICATION_SUCCEEDED; + else if (!active || deferred) + return TLS_AUTHENTICATION_DEFERRED; + else + return TLS_AUTHENTICATION_FAILED; +} + +#ifdef MANAGEMENT_DEF_AUTH +/* + * For deferred auth, this is where the management interface calls (on server) + * to indicate auth failure/success. + */ +bool +tls_authenticate_key (struct tls_multi *multi, const unsigned int mda_key_id, const bool auth, const char *client_reason) +{ + bool ret = false; + if (multi) + { + int i; + man_def_auth_set_client_reason (multi, client_reason); + for (i = 0; i < KEY_SCAN_SIZE; ++i) + { + struct key_state *ks = multi->key_scan[i]; + if (ks->mda_key_id == mda_key_id) + { + ks->mda_status = auth ? ACF_SUCCEEDED : ACF_FAILED; + ret = true; + } + } + } + return ret; +} +#endif + +void +tls_deauthenticate (struct tls_multi *multi) +{ + if (multi) + { + int i, j; + for (i = 0; i < TM_SIZE; ++i) + for (j = 0; j < KS_SIZE; ++j) + multi->session[i].key[j].authenticated = false; + } +} + +/* + * Print debugging information on SSL/TLS session negotiation. + */ +static void +info_callback (INFO_CALLBACK_SSL_CONST SSL * s, int where, int ret) +{ + if (where & SSL_CB_LOOP) + { + dmsg (D_HANDSHAKE_VERBOSE, "SSL state (%s): %s", + where & SSL_ST_CONNECT ? "connect" : + where & SSL_ST_ACCEPT ? "accept" : + "undefined", SSL_state_string_long (s)); + } + else if (where & SSL_CB_ALERT) + { + dmsg (D_HANDSHAKE_VERBOSE, "SSL alert (%s): %s: %s", + where & SSL_CB_READ ? "read" : "write", + SSL_alert_type_string_long (ret), + SSL_alert_desc_string_long (ret)); + } +} + +#if ENABLE_INLINE_FILES + +static int +use_inline_load_verify_locations (SSL_CTX *ctx, const char *ca_string) +{ + X509_STORE *store = NULL; + X509* cert = NULL; + BIO *in = NULL; + int ret = 0; + + in = BIO_new_mem_buf ((char *)ca_string, -1); + if (!in) + goto err; + + for (;;) + { + if (!PEM_read_bio_X509 (in, &cert, 0, NULL)) + { + ret = 1; + break; + } + if (!cert) + break; + + store = SSL_CTX_get_cert_store (ctx); + if (!store) + break; + + if (!X509_STORE_add_cert (store, cert)) + break; + + if (cert) + { + X509_free (cert); + cert = NULL; + } + } + + err: + if (cert) + X509_free (cert); + if (in) + BIO_free (in); + return ret; +} + +static int +xname_cmp(const X509_NAME * const *a, const X509_NAME * const *b) +{ + return(X509_NAME_cmp(*a,*b)); +} + +static STACK_OF(X509_NAME) * +use_inline_load_client_CA_file (SSL_CTX *ctx, const char *ca_string) +{ + BIO *in = NULL; + X509 *x = NULL; + X509_NAME *xn = NULL; + STACK_OF(X509_NAME) *ret = NULL, *sk; + + sk=sk_X509_NAME_new(xname_cmp); + + in = BIO_new_mem_buf ((char *)ca_string, -1); + if (!in) + goto err; + + if ((sk == NULL) || (in == NULL)) + goto err; + + for (;;) + { + if (PEM_read_bio_X509(in,&x,NULL,NULL) == NULL) + break; + if (ret == NULL) + { + ret = sk_X509_NAME_new_null(); + if (ret == NULL) + goto err; + } + if ((xn=X509_get_subject_name(x)) == NULL) goto err; + /* check for duplicates */ + xn=X509_NAME_dup(xn); + if (xn == NULL) goto err; + if (sk_X509_NAME_find(sk,xn) >= 0) + X509_NAME_free(xn); + else + { + sk_X509_NAME_push(sk,xn); + sk_X509_NAME_push(ret,xn); + } + } + + if (0) + { + err: + if (ret != NULL) sk_X509_NAME_pop_free(ret,X509_NAME_free); + ret=NULL; + } + if (sk != NULL) sk_X509_NAME_free(sk); + if (in != NULL) BIO_free(in); + if (x != NULL) X509_free(x); + if (ret != NULL) + ERR_clear_error(); + return(ret); +} + +static int +use_inline_certificate_file (SSL_CTX *ctx, const char *cert_string) +{ + BIO *in = NULL; + X509 *x = NULL; + int ret = 0; + + in = BIO_new_mem_buf ((char *)cert_string, -1); + if (!in) + goto end; + + x = PEM_read_bio_X509 (in, + NULL, + ctx->default_passwd_callback, + ctx->default_passwd_callback_userdata); + if (!x) + goto end; + + ret = SSL_CTX_use_certificate(ctx, x); + + end: + if (x) + X509_free (x); + if (in) + BIO_free (in); + return ret; +} + +static int +use_inline_PrivateKey_file (SSL_CTX *ctx, const char *key_string) +{ + BIO *in = NULL; + EVP_PKEY *pkey = NULL; + int ret = 0; + + in = BIO_new_mem_buf ((char *)key_string, -1); + if (!in) + goto end; + + pkey = PEM_read_bio_PrivateKey (in, + NULL, + ctx->default_passwd_callback, + ctx->default_passwd_callback_userdata); + if (!pkey) + goto end; + + ret = SSL_CTX_use_PrivateKey (ctx, pkey); + + end: + if (pkey) + EVP_PKEY_free (pkey); + if (in) + BIO_free (in); + return ret; +} + +#endif + +/* + * Initialize SSL context. + * All files are in PEM format. + */ +SSL_CTX * +init_ssl (const struct options *options) +{ + SSL_CTX *ctx = NULL; + DH *dh; + BIO *bio; + bool using_cert_file = false; + + ERR_clear_error (); + + if (options->tls_server) + { + ctx = SSL_CTX_new (TLSv1_server_method ()); + if (ctx == NULL) + msg (M_SSLERR, "SSL_CTX_new TLSv1_server_method"); + + SSL_CTX_set_tmp_rsa_callback (ctx, tmp_rsa_cb); + +#if ENABLE_INLINE_FILES + if (!strcmp (options->dh_file, INLINE_FILE_TAG) && options->dh_file_inline) + { + if (!(bio = BIO_new_mem_buf ((char *)options->dh_file_inline, -1))) + msg (M_SSLERR, "Cannot open memory BIO for inline DH parameters"); + } + else +#endif + { + /* Get Diffie Hellman Parameters */ + if (!(bio = BIO_new_file (options->dh_file, "r"))) + msg (M_SSLERR, "Cannot open %s for DH parameters", options->dh_file); + } + + dh = PEM_read_bio_DHparams (bio, NULL, NULL, NULL); + BIO_free (bio); + if (!dh) + msg (M_SSLERR, "Cannot load DH parameters from %s", options->dh_file); + if (!SSL_CTX_set_tmp_dh (ctx, dh)) + msg (M_SSLERR, "SSL_CTX_set_tmp_dh"); + msg (D_TLS_DEBUG_LOW, "Diffie-Hellman initialized with %d bit key", + 8 * DH_size (dh)); + DH_free (dh); + } + else /* if client */ + { + ctx = SSL_CTX_new (TLSv1_client_method ()); + if (ctx == NULL) + msg (M_SSLERR, "SSL_CTX_new TLSv1_client_method"); + } + + /* Set SSL options */ + SSL_CTX_set_session_cache_mode (ctx, SSL_SESS_CACHE_OFF); + SSL_CTX_set_options (ctx, SSL_OP_SINGLE_DH_USE); + + /* Set callback for getting password from user to decrypt private key */ + SSL_CTX_set_default_passwd_cb (ctx, pem_password_callback); + + if (options->pkcs12_file) + { + /* Use PKCS #12 file for key, cert and CA certs */ + + FILE *fp; + EVP_PKEY *pkey; + X509 *cert; + STACK_OF(X509) *ca = NULL; + PKCS12 *p12=NULL; + int i; + char password[256]; + +#if ENABLE_INLINE_FILES + if (!strcmp (options->pkcs12_file, INLINE_FILE_TAG) && options->pkcs12_file_inline) + { + BIO *b64 = BIO_new (BIO_f_base64()); + BIO *bio = BIO_new_mem_buf ((void *)options->pkcs12_file_inline, (int)strlen(options->pkcs12_file_inline)); + ASSERT(b64 && bio); + BIO_push (b64, bio); + p12 = d2i_PKCS12_bio(b64, NULL); + if (!p12) + msg (M_SSLERR, "Error reading inline PKCS#12 file"); + BIO_free (b64); + BIO_free (bio); + } + else +#endif + { + /* Load the PKCS #12 file */ + if (!(fp = fopen(options->pkcs12_file, "rb"))) + msg (M_SSLERR, "Error opening file %s", options->pkcs12_file); + p12 = d2i_PKCS12_fp(fp, NULL); + fclose (fp); + if (!p12) + msg (M_SSLERR, "Error reading PKCS#12 file %s", options->pkcs12_file); + } + + /* Parse the PKCS #12 file */ + if (!PKCS12_parse(p12, "", &pkey, &cert, &ca)) + { + pem_password_callback (password, sizeof(password) - 1, 0, NULL); + /* Reparse the PKCS #12 file with password */ + ca = NULL; + if (!PKCS12_parse(p12, password, &pkey, &cert, &ca)) + { +#ifdef ENABLE_MANAGEMENT + if (management && (ERR_GET_REASON (ERR_peek_error()) == PKCS12_R_MAC_VERIFY_FAILURE)) + management_auth_failure (management, UP_TYPE_PRIVATE_KEY, NULL); +#endif + PKCS12_free(p12); + msg (M_INFO, "OpenSSL ERROR code: %d", (ERR_GET_REASON (ERR_peek_error()))); // fixme + goto err; + } + } + PKCS12_free(p12); + + /* Load Certificate */ + if (!SSL_CTX_use_certificate (ctx, cert)) + msg (M_SSLERR, "Cannot use certificate"); + + /* Load Private Key */ + if (!SSL_CTX_use_PrivateKey (ctx, pkey)) + msg (M_SSLERR, "Cannot use private key"); + warn_if_group_others_accessible (options->pkcs12_file); + + /* Check Private Key */ + if (!SSL_CTX_check_private_key (ctx)) + msg (M_SSLERR, "Private key does not match the certificate"); + + /* Set Certificate Verification chain */ + if (!options->ca_file) + { + if (ca && sk_X509_num(ca)) + { + for (i = 0; i < sk_X509_num(ca); i++) + { + if (!X509_STORE_add_cert(ctx->cert_store,sk_X509_value(ca, i))) + msg (M_SSLERR, "Cannot add certificate to certificate chain (X509_STORE_add_cert)"); + if (!SSL_CTX_add_client_CA(ctx, sk_X509_value(ca, i))) + msg (M_SSLERR, "Cannot add certificate to client CA list (SSL_CTX_add_client_CA)"); + } + } + } + } + else + { + /* Use seperate PEM files for key, cert and CA certs */ + +#ifdef ENABLE_PKCS11 + if (options->pkcs11_providers[0]) + { + /* Load Certificate and Private Key */ + if (!SSL_CTX_use_pkcs11 (ctx, options->pkcs11_id_management, options->pkcs11_id)) + { + msg (M_WARN, "Cannot load certificate \"%s\" using PKCS#11 interface", options->pkcs11_id); + goto err; + } + } + else +#endif + +#ifdef WIN32 + if (options->cryptoapi_cert) + { + /* Load Certificate and Private Key */ + if (!SSL_CTX_use_CryptoAPI_certificate (ctx, options->cryptoapi_cert)) + msg (M_SSLERR, "Cannot load certificate \"%s\" from Microsoft Certificate Store", + options->cryptoapi_cert); + } + else +#endif + { + /* Load Certificate */ + if (options->cert_file) + { +#if ENABLE_INLINE_FILES + if (!strcmp (options->cert_file, INLINE_FILE_TAG) && options->cert_file_inline) + { + if (!use_inline_certificate_file (ctx, options->cert_file_inline)) + msg (M_SSLERR, "Cannot load inline certificate file"); + } + else +#endif + { + if (!SSL_CTX_use_certificate_file (ctx, options->cert_file, SSL_FILETYPE_PEM)) + msg (M_SSLERR, "Cannot load certificate file %s", options->cert_file); + using_cert_file = true; + } + } + + /* Load Private Key */ + if (options->priv_key_file) + { + int status; + +#if ENABLE_INLINE_FILES + if (!strcmp (options->priv_key_file, INLINE_FILE_TAG) && options->priv_key_file_inline) + { + status = use_inline_PrivateKey_file (ctx, options->priv_key_file_inline); + } + else +#endif + { + status = SSL_CTX_use_PrivateKey_file (ctx, options->priv_key_file, SSL_FILETYPE_PEM); + } + if (!status) + { +#ifdef ENABLE_MANAGEMENT + if (management && (ERR_GET_REASON (ERR_peek_error()) == EVP_R_BAD_DECRYPT)) + management_auth_failure (management, UP_TYPE_PRIVATE_KEY, NULL); +#endif + msg (M_WARN|M_SSL, "Cannot load private key file %s", options->priv_key_file); + goto err; + } + warn_if_group_others_accessible (options->priv_key_file); + + /* Check Private Key */ + if (!SSL_CTX_check_private_key (ctx)) + msg (M_SSLERR, "Private key does not match the certificate"); + } + } + } + + if (options->ca_file || options->ca_path) + { + int status; + +#if ENABLE_INLINE_FILES + if (options->ca_file && !strcmp (options->ca_file, INLINE_FILE_TAG) && options->ca_file_inline) + { + status = use_inline_load_verify_locations (ctx, options->ca_file_inline); + } + else +#endif + { + /* Load CA file for verifying peer supplied certificate */ + status = SSL_CTX_load_verify_locations (ctx, options->ca_file, options->ca_path); + } + + if (!status) + msg (M_SSLERR, "Cannot load CA certificate file %s path %s (SSL_CTX_load_verify_locations)", options->ca_file, options->ca_path); + + /* Set a store for certs (CA & CRL) with a lookup on the "capath" hash directory */ + if (options->ca_path) { + X509_STORE *store = SSL_CTX_get_cert_store(ctx); + + if (store) + { + X509_LOOKUP *lookup = X509_STORE_add_lookup(store, X509_LOOKUP_hash_dir()); + if (!X509_LOOKUP_add_dir(lookup, options->ca_path, X509_FILETYPE_PEM)) + X509_LOOKUP_add_dir(lookup, NULL, X509_FILETYPE_DEFAULT); + else + msg(M_WARN, "WARNING: experimental option --capath %s", options->ca_path); +#if OPENSSL_VERSION_NUMBER >= 0x00907000L + X509_STORE_set_flags(store, X509_V_FLAG_CRL_CHECK | X509_V_FLAG_CRL_CHECK_ALL); +#else + msg(M_WARN, "WARNING: this version of OpenSSL cannot handle CRL files in capath"); +#endif + } + else + msg(M_SSLERR, "Cannot get certificate store (SSL_CTX_get_cert_store)"); + } + + /* Load names of CAs from file and use it as a client CA list */ + if (options->ca_file) { + STACK_OF(X509_NAME) *cert_names = NULL; +#if ENABLE_INLINE_FILES + if (!strcmp (options->ca_file, INLINE_FILE_TAG) && options->ca_file_inline) + { + cert_names = use_inline_load_client_CA_file (ctx, options->ca_file_inline); + } + else +#endif + { + cert_names = SSL_load_client_CA_file (options->ca_file); + } + if (!cert_names) + msg (M_SSLERR, "Cannot load CA certificate file %s (SSL_load_client_CA_file)", options->ca_file); + SSL_CTX_set_client_CA_list (ctx, cert_names); + } + } + + /* Enable the use of certificate chains */ + if (using_cert_file) + { + if (!SSL_CTX_use_certificate_chain_file (ctx, options->cert_file)) + msg (M_SSLERR, "Cannot load certificate chain file %s (SSL_use_certificate_chain_file)", options->cert_file); + } + + /* Require peer certificate verification */ +#if P2MP_SERVER + if (options->ssl_flags & SSLF_CLIENT_CERT_NOT_REQUIRED) + { + msg (M_WARN, "WARNING: POTENTIALLY DANGEROUS OPTION --client-cert-not-required may accept clients which do not present a certificate"); + } + else +#endif + { +#ifdef ENABLE_X509ALTUSERNAME + x509_username_field = (char *) options->x509_username_field; +#else + x509_username_field = X509_USERNAME_FIELD_DEFAULT; +#endif + SSL_CTX_set_verify (ctx, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, + verify_callback); + } + + /* Connection information callback */ + SSL_CTX_set_info_callback (ctx, info_callback); + + /* Allowable ciphers */ + if (options->cipher_list) + { + if (!SSL_CTX_set_cipher_list (ctx, options->cipher_list)) + msg (M_SSLERR, "Problem with cipher list: %s", options->cipher_list); + } + + ERR_clear_error (); + + return ctx; + + err: + ERR_clear_error (); + if (ctx) + SSL_CTX_free (ctx); + return NULL; +} + +/* + * Print a one line summary of SSL/TLS session handshake. + */ +static void +print_details (SSL * c_ssl, const char *prefix) +{ + const SSL_CIPHER *ciph; + X509 *cert; + char s1[256]; + char s2[256]; + + s1[0] = s2[0] = 0; + ciph = SSL_get_current_cipher (c_ssl); + openvpn_snprintf (s1, sizeof (s1), "%s %s, cipher %s %s", + prefix, + SSL_get_version (c_ssl), + SSL_CIPHER_get_version (ciph), + SSL_CIPHER_get_name (ciph)); + cert = SSL_get_peer_certificate (c_ssl); + if (cert != NULL) + { + EVP_PKEY *pkey = X509_get_pubkey (cert); + if (pkey != NULL) + { + if (pkey->type == EVP_PKEY_RSA && pkey->pkey.rsa != NULL + && pkey->pkey.rsa->n != NULL) + { + openvpn_snprintf (s2, sizeof (s2), ", %d bit RSA", + BN_num_bits (pkey->pkey.rsa->n)); + } + else if (pkey->type == EVP_PKEY_DSA && pkey->pkey.dsa != NULL + && pkey->pkey.dsa->p != NULL) + { + openvpn_snprintf (s2, sizeof (s2), ", %d bit DSA", + BN_num_bits (pkey->pkey.dsa->p)); + } + EVP_PKEY_free (pkey); + } + X509_free (cert); + } + /* The SSL API does not allow us to look at temporary RSA/DH keys, + * otherwise we should print their lengths too */ + msg (D_HANDSHAKE, "%s%s", s1, s2); +} + +/* + * Show the TLS ciphers that are available for us to use + * in the OpenSSL library. + */ +void +show_available_tls_ciphers () +{ + SSL_CTX *ctx; + SSL *ssl; + const char *cipher_name; + int priority = 0; + + ctx = SSL_CTX_new (TLSv1_method ()); + if (!ctx) + msg (M_SSLERR, "Cannot create SSL_CTX object"); + ssl = SSL_new (ctx); + if (!ssl) + msg (M_SSLERR, "Cannot create SSL object"); + + printf ("Available TLS Ciphers,\n"); + printf ("listed in order of preference:\n\n"); + while ((cipher_name = SSL_get_cipher_list (ssl, priority++))) + printf ("%s\n", cipher_name); + printf ("\n"); + + SSL_free (ssl); + SSL_CTX_free (ctx); +} + +/* + * The OpenSSL library has a notion of preference in TLS + * ciphers. Higher preference == more secure. + * Return the highest preference cipher. + */ +void +get_highest_preference_tls_cipher (char *buf, int size) +{ + SSL_CTX *ctx; + SSL *ssl; + const char *cipher_name; + + ctx = SSL_CTX_new (TLSv1_method ()); + if (!ctx) + msg (M_SSLERR, "Cannot create SSL_CTX object"); + ssl = SSL_new (ctx); + if (!ssl) + msg (M_SSLERR, "Cannot create SSL object"); + + cipher_name = SSL_get_cipher_list (ssl, 0); + strncpynt (buf, cipher_name, size); + + SSL_free (ssl); + SSL_CTX_free (ctx); +} + +/* + * Map internal constants to ascii names. + */ +static const char * +state_name (int state) +{ + switch (state) + { + case S_UNDEF: + return "S_UNDEF"; + case S_INITIAL: + return "S_INITIAL"; + case S_PRE_START: + return "S_PRE_START"; + case S_START: + return "S_START"; + case S_SENT_KEY: + return "S_SENT_KEY"; + case S_GOT_KEY: + return "S_GOT_KEY"; + case S_ACTIVE: + return "S_ACTIVE"; + case S_NORMAL_OP: + return "S_NORMAL_OP"; + case S_ERROR: + return "S_ERROR"; + default: + return "S_???"; + } +} + +static const char * +packet_opcode_name (int op) +{ + switch (op) + { + case P_CONTROL_HARD_RESET_CLIENT_V1: + return "P_CONTROL_HARD_RESET_CLIENT_V1"; + case P_CONTROL_HARD_RESET_SERVER_V1: + return "P_CONTROL_HARD_RESET_SERVER_V1"; + case P_CONTROL_HARD_RESET_CLIENT_V2: + return "P_CONTROL_HARD_RESET_CLIENT_V2"; + case P_CONTROL_HARD_RESET_SERVER_V2: + return "P_CONTROL_HARD_RESET_SERVER_V2"; + case P_CONTROL_SOFT_RESET_V1: + return "P_CONTROL_SOFT_RESET_V1"; + case P_CONTROL_V1: + return "P_CONTROL_V1"; + case P_ACK_V1: + return "P_ACK_V1"; + case P_DATA_V1: + return "P_DATA_V1"; + default: + return "P_???"; + } +} + +static const char * +session_index_name (int index) +{ + switch (index) + { + case TM_ACTIVE: + return "TM_ACTIVE"; + case TM_UNTRUSTED: + return "TM_UNTRUSTED"; + case TM_LAME_DUCK: + return "TM_LAME_DUCK"; + default: + return "TM_???"; + } +} + +/* + * For debugging. + */ +static const char * +print_key_id (struct tls_multi *multi, struct gc_arena *gc) +{ + int i; + struct buffer out = alloc_buf_gc (256, gc); + + for (i = 0; i < KEY_SCAN_SIZE; ++i) + { + struct key_state *ks = multi->key_scan[i]; + buf_printf (&out, " [key#%d state=%s id=%d sid=%s]", i, + state_name (ks->state), ks->key_id, + session_id_print (&ks->session_id_remote, gc)); + } + + return BSTR (&out); +} + +/* + * Given a key_method, return true if op + * represents the required form of hard_reset. + * + * If key_method = 0, return true if any + * form of hard reset is used. + */ +static bool +is_hard_reset (int op, int key_method) +{ + if (!key_method || key_method == 1) + if (op == P_CONTROL_HARD_RESET_CLIENT_V1 || op == P_CONTROL_HARD_RESET_SERVER_V1) + return true; + + if (!key_method || key_method >= 2) + if (op == P_CONTROL_HARD_RESET_CLIENT_V2 || op == P_CONTROL_HARD_RESET_SERVER_V2) + return true; + + return false; +} + +/* + * OpenVPN's interface to SSL/TLS authentication, + * encryption, and decryption is exclusively + * through "memory BIOs". + */ +static BIO * +getbio (BIO_METHOD * type, const char *desc) +{ + BIO *ret; + ret = BIO_new (type); + if (!ret) + msg (M_SSLERR, "Error creating %s BIO", desc); + return ret; +} + +/* + * Write to an OpenSSL BIO in non-blocking mode. + */ +static int +bio_write (struct tls_multi* multi, BIO *bio, const uint8_t *data, int size, const char *desc) +{ + int i; + int ret = 0; + ASSERT (size >= 0); + if (size) + { + /* + * Free the L_TLS lock prior to calling BIO routines + * so that foreground thread can still call + * tls_pre_decrypt or tls_pre_encrypt, + * allowing tunnel packet forwarding to continue. + */ +#ifdef BIO_DEBUG + bio_debug_data ("write", bio, data, size, desc); +#endif + i = BIO_write (bio, data, size); + + if (i < 0) + { + if (BIO_should_retry (bio)) + { + ; + } + else + { + msg (D_TLS_ERRORS | M_SSL, "TLS ERROR: BIO write %s error", + desc); + ret = -1; + ERR_clear_error (); + } + } + else if (i != size) + { + msg (D_TLS_ERRORS | M_SSL, + "TLS ERROR: BIO write %s incomplete %d/%d", desc, i, size); + ret = -1; + ERR_clear_error (); + } + else + { /* successful write */ + dmsg (D_HANDSHAKE_VERBOSE, "BIO write %s %d bytes", desc, i); + ret = 1; + } + } + return ret; +} + +/* + * Read from an OpenSSL BIO in non-blocking mode. + */ +static int +bio_read (struct tls_multi* multi, BIO *bio, struct buffer *buf, int maxlen, const char *desc) +{ + int i; + int ret = 0; + ASSERT (buf->len >= 0); + if (buf->len) + { + ; + } + else + { + int len = buf_forward_capacity (buf); + if (maxlen < len) + len = maxlen; + + /* + * BIO_read brackets most of the serious RSA + * key negotiation number crunching. + */ + i = BIO_read (bio, BPTR (buf), len); + + VALGRIND_MAKE_READABLE ((void *) &i, sizeof (i)); + +#ifdef BIO_DEBUG + bio_debug_data ("read", bio, BPTR (buf), i, desc); +#endif + if (i < 0) + { + if (BIO_should_retry (bio)) + { + ; + } + else + { + msg (D_TLS_ERRORS | M_SSL, "TLS_ERROR: BIO read %s error", + desc); + buf->len = 0; + ret = -1; + ERR_clear_error (); + } + } + else if (!i) + { + buf->len = 0; + } + else + { /* successful read */ + dmsg (D_HANDSHAKE_VERBOSE, "BIO read %s %d bytes", desc, i); + buf->len = i; + ret = 1; + VALGRIND_MAKE_READABLE ((void *) BPTR (buf), BLEN (buf)); + } + } + return ret; +} + +/* + * Inline functions for reading from and writing + * to BIOs. + */ + +static void +bio_write_post (const int status, struct buffer *buf) +{ + if (status == 1) /* success status return from bio_write? */ + { + memset (BPTR (buf), 0, BLEN (buf)); /* erase data just written */ + buf->len = 0; + } +} + +static int +key_state_write_plaintext (struct tls_multi *multi, struct key_state *ks, struct buffer *buf) +{ + int ret; + perf_push (PERF_BIO_WRITE_PLAINTEXT); + ret = bio_write (multi, ks->ssl_bio, BPTR(buf), BLEN(buf), "tls_write_plaintext"); + bio_write_post (ret, buf); + perf_pop (); + return ret; +} + +static int +key_state_write_plaintext_const (struct tls_multi *multi, struct key_state *ks, const uint8_t *data, int len) +{ + int ret; + perf_push (PERF_BIO_WRITE_PLAINTEXT); + ret = bio_write (multi, ks->ssl_bio, data, len, "tls_write_plaintext_const"); + perf_pop (); + return ret; +} + +static int +key_state_write_ciphertext (struct tls_multi *multi, struct key_state *ks, struct buffer *buf) +{ + int ret; + perf_push (PERF_BIO_WRITE_CIPHERTEXT); + ret = bio_write (multi, ks->ct_in, BPTR(buf), BLEN(buf), "tls_write_ciphertext"); + bio_write_post (ret, buf); + perf_pop (); + return ret; +} + +static int +key_state_read_plaintext (struct tls_multi *multi, struct key_state *ks, struct buffer *buf, + int maxlen) +{ + int ret; + perf_push (PERF_BIO_READ_PLAINTEXT); + ret = bio_read (multi, ks->ssl_bio, buf, maxlen, "tls_read_plaintext"); + perf_pop (); + return ret; +} + +static int +key_state_read_ciphertext (struct tls_multi *multi, struct key_state *ks, struct buffer *buf, + int maxlen) +{ + int ret; + perf_push (PERF_BIO_READ_CIPHERTEXT); + ret = bio_read (multi, ks->ct_out, buf, maxlen, "tls_read_ciphertext"); + perf_pop (); + return ret; +} + +/* + * Initialize a key_state. Each key_state corresponds to + * a specific SSL/TLS session. + */ +static void +key_state_init (struct tls_session *session, struct key_state *ks) +{ + update_time (); + + /* + * Build TLS object that reads/writes ciphertext + * to/from memory BIOs. + */ + CLEAR (*ks); + + ks->ssl = SSL_new (session->opt->ssl_ctx); + if (!ks->ssl) + msg (M_SSLERR, "SSL_new failed"); + + /* put session * in ssl object so we can access it + from verify callback*/ + SSL_set_ex_data (ks->ssl, mydata_index, session); + + ks->ssl_bio = getbio (BIO_f_ssl (), "ssl_bio"); + ks->ct_in = getbio (BIO_s_mem (), "ct_in"); + ks->ct_out = getbio (BIO_s_mem (), "ct_out"); + +#ifdef BIO_DEBUG + bio_debug_oc ("open ssl_bio", ks->ssl_bio); + bio_debug_oc ("open ct_in", ks->ct_in); + bio_debug_oc ("open ct_out", ks->ct_out); +#endif + + if (session->opt->server) + SSL_set_accept_state (ks->ssl); + else + SSL_set_connect_state (ks->ssl); + + SSL_set_bio (ks->ssl, ks->ct_in, ks->ct_out); + BIO_set_ssl (ks->ssl_bio, ks->ssl, BIO_NOCLOSE); + + /* Set control-channel initiation mode */ + ks->initial_opcode = session->initial_opcode; + session->initial_opcode = P_CONTROL_SOFT_RESET_V1; + ks->state = S_INITIAL; + ks->key_id = session->key_id; + + /* + * key_id increments to KEY_ID_MASK then recycles back to 1. + * This way you know that if key_id is 0, it is the first key. + */ + ++session->key_id; + session->key_id &= P_KEY_ID_MASK; + if (!session->key_id) + session->key_id = 1; + + /* allocate key source material object */ + ALLOC_OBJ_CLEAR (ks->key_src, struct key_source2); + + /* allocate reliability objects */ + ALLOC_OBJ_CLEAR (ks->send_reliable, struct reliable); + ALLOC_OBJ_CLEAR (ks->rec_reliable, struct reliable); + ALLOC_OBJ_CLEAR (ks->rec_ack, struct reliable_ack); + + /* allocate buffers */ + ks->plaintext_read_buf = alloc_buf (TLS_CHANNEL_BUF_SIZE); + ks->plaintext_write_buf = alloc_buf (TLS_CHANNEL_BUF_SIZE); + ks->ack_write_buf = alloc_buf (BUF_SIZE (&session->opt->frame)); + reliable_init (ks->send_reliable, BUF_SIZE (&session->opt->frame), + FRAME_HEADROOM (&session->opt->frame), TLS_RELIABLE_N_SEND_BUFFERS, + ks->key_id ? false : session->opt->xmit_hold); + reliable_init (ks->rec_reliable, BUF_SIZE (&session->opt->frame), + FRAME_HEADROOM (&session->opt->frame), TLS_RELIABLE_N_REC_BUFFERS, + false); + reliable_set_timeout (ks->send_reliable, session->opt->packet_timeout); + + /* init packet ID tracker */ + packet_id_init (&ks->packet_id, + session->opt->replay_window, + session->opt->replay_time); + +#ifdef MANAGEMENT_DEF_AUTH + ks->mda_key_id = session->opt->mda_context->mda_key_id_counter++; +#endif +} + +static void +key_state_free (struct key_state *ks, bool clear) +{ + ks->state = S_UNDEF; + + if (ks->ssl) { +#ifdef BIO_DEBUG + bio_debug_oc ("close ssl_bio", ks->ssl_bio); + bio_debug_oc ("close ct_in", ks->ct_in); + bio_debug_oc ("close ct_out", ks->ct_out); +#endif + BIO_free_all(ks->ssl_bio); + SSL_free (ks->ssl); + } + + free_key_ctx_bi (&ks->key); + free_buf (&ks->plaintext_read_buf); + free_buf (&ks->plaintext_write_buf); + free_buf (&ks->ack_write_buf); + buffer_list_free(ks->paybuf); + + if (ks->send_reliable) + { + reliable_free (ks->send_reliable); + free (ks->send_reliable); + } + + if (ks->rec_reliable) + { + reliable_free (ks->rec_reliable); + free (ks->rec_reliable); + } + + if (ks->rec_ack) + free (ks->rec_ack); + + if (ks->key_src) + free (ks->key_src); + + packet_id_free (&ks->packet_id); + +#ifdef PLUGIN_DEF_AUTH + key_state_rm_auth_control_file (ks); +#endif + + if (clear) + CLEAR (*ks); +} + +/* + * Must be called if we move a tls_session in memory. + */ +static inline void tls_session_set_self_referential_pointers (struct tls_session* session) { + session->tls_auth.packet_id = &session->tls_auth_pid; +} + +/* + * Initialize a TLS session. A TLS session normally has 2 key_state objects, + * one for the current key, and one for the lame duck (i.e. retiring) key. + */ +static void +tls_session_init (struct tls_multi *multi, struct tls_session *session) +{ + struct gc_arena gc = gc_new (); + + dmsg (D_TLS_DEBUG, "TLS: tls_session_init: entry"); + + CLEAR (*session); + + /* Set options data to point to parent's option structure */ + session->opt = &multi->opt; + + /* Randomize session # if it is 0 */ + while (!session_id_defined(&session->session_id)) + session_id_random (&session->session_id); + + /* Are we a TLS server or client? */ + ASSERT (session->opt->key_method >= 1); + if (session->opt->key_method == 1) + { + session->initial_opcode = session->opt->server ? + P_CONTROL_HARD_RESET_SERVER_V1 : P_CONTROL_HARD_RESET_CLIENT_V1; + } + else /* session->opt->key_method >= 2 */ + { + session->initial_opcode = session->opt->server ? + P_CONTROL_HARD_RESET_SERVER_V2 : P_CONTROL_HARD_RESET_CLIENT_V2; + } + + /* Initialize control channel authentication parameters */ + session->tls_auth = session->opt->tls_auth; + + /* Set session internal pointers (also called if session object is moved in memory) */ + tls_session_set_self_referential_pointers (session); + + /* initialize packet ID replay window for --tls-auth */ + packet_id_init (session->tls_auth.packet_id, + session->opt->replay_window, + session->opt->replay_time); + + /* load most recent packet-id to replay protect on --tls-auth */ + packet_id_persist_load_obj (session->tls_auth.pid_persist, session->tls_auth.packet_id); + + key_state_init (session, &session->key[KS_PRIMARY]); + + dmsg (D_TLS_DEBUG, "TLS: tls_session_init: new session object, sid=%s", + session_id_print (&session->session_id, &gc)); + + gc_free (&gc); +} + +static void +tls_session_free (struct tls_session *session, bool clear) +{ + int i; + + if (session->tls_auth.packet_id) + packet_id_free (session->tls_auth.packet_id); + + for (i = 0; i < KS_SIZE; ++i) + key_state_free (&session->key[i], false); + + if (session->common_name) + free (session->common_name); + + cert_hash_free (session->cert_hash_set); + + if (clear) + CLEAR (*session); +} + +static void +move_session (struct tls_multi* multi, int dest, int src, bool reinit_src) +{ + msg (D_TLS_DEBUG_LOW, "TLS: move_session: dest=%s src=%s reinit_src=%d", + session_index_name(dest), + session_index_name(src), + reinit_src); + ASSERT (src != dest); + ASSERT (src >= 0 && src < TM_SIZE); + ASSERT (dest >= 0 && dest < TM_SIZE); + tls_session_free (&multi->session[dest], false); + multi->session[dest] = multi->session[src]; + tls_session_set_self_referential_pointers (&multi->session[dest]); + + if (reinit_src) + tls_session_init (multi, &multi->session[src]); + else + CLEAR (multi->session[src]); + + dmsg (D_TLS_DEBUG, "TLS: move_session: exit"); +} + +static void +reset_session (struct tls_multi *multi, struct tls_session *session) +{ + tls_session_free (session, false); + tls_session_init (multi, session); +} + +#if 0 +/* + * Transmit a TLS reset on our untrusted channel. + */ +static void +initiate_untrusted_session (struct tls_multi *multi, struct sockaddr_in *to) +{ + struct tls_session *session = &multi->session[TM_UNTRUSTED]; + struct key_state *ks = &session->key[KS_PRIMARY]; + + reset_session (multi, session); + ks->remote_addr = *to; + msg (D_TLS_DEBUG_LOW, "TLS: initiate_untrusted_session: addr=%s", print_sockaddr (to)); +} +#endif + +/* + * Used to determine in how many seconds we should be + * called again. + */ +static inline void +compute_earliest_wakeup (interval_t *earliest, interval_t seconds_from_now) { + if (seconds_from_now < *earliest) + *earliest = seconds_from_now; + if (*earliest < 0) + *earliest = 0; +} + +/* + * Return true if "lame duck" or retiring key has expired and can + * no longer be used. + */ +static inline bool +lame_duck_must_die (const struct tls_session* session, interval_t *wakeup) +{ + const struct key_state* lame = &session->key[KS_LAME_DUCK]; + if (lame->state >= S_INITIAL) + { + const time_t local_now = now; + ASSERT (lame->must_die); /* a lame duck key must always have an expiration */ + if (local_now < lame->must_die) + { + compute_earliest_wakeup (wakeup, lame->must_die - local_now); + return false; + } + else + return true; + } + else if (lame->state == S_ERROR) + return true; + else + return false; +} + +/* + * A tls_multi object fully encapsulates OpenVPN's TLS state. + * See ssl.h for more comments. + */ +struct tls_multi * +tls_multi_init (struct tls_options *tls_options) +{ + struct tls_multi *ret; + + ALLOC_OBJ_CLEAR (ret, struct tls_multi); + + /* get command line derived options */ + ret->opt = *tls_options; + + /* set up pointer to HMAC object for TLS packet authentication */ + ret->opt.tls_auth.key_ctx_bi = &ret->opt.tls_auth_key; + + /* set up list of keys to be scanned by data channel encrypt and decrypt routines */ + ASSERT (SIZE (ret->key_scan) == 3); + ret->key_scan[0] = &ret->session[TM_ACTIVE].key[KS_PRIMARY]; + ret->key_scan[1] = &ret->session[TM_ACTIVE].key[KS_LAME_DUCK]; + ret->key_scan[2] = &ret->session[TM_LAME_DUCK].key[KS_LAME_DUCK]; + + return ret; +} + +/* + * Finalize our computation of frame sizes. + */ +void +tls_multi_init_finalize (struct tls_multi* multi, const struct frame* frame) +{ + tls_init_control_channel_frame_parameters (frame, &multi->opt.frame); + + /* initialize the active and untrusted sessions */ + + tls_session_init (multi, &multi->session[TM_ACTIVE]); + + if (!multi->opt.single_session) + tls_session_init (multi, &multi->session[TM_UNTRUSTED]); +} + +/* + * Initialize and finalize a standalone tls-auth verification object. + */ + +struct tls_auth_standalone * +tls_auth_standalone_init (struct tls_options *tls_options, + struct gc_arena *gc) +{ + struct tls_auth_standalone *tas; + + ALLOC_OBJ_CLEAR_GC (tas, struct tls_auth_standalone, gc); + + /* set up pointer to HMAC object for TLS packet authentication */ + tas->tls_auth_key = tls_options->tls_auth_key; + tas->tls_auth_options.key_ctx_bi = &tas->tls_auth_key; + tas->tls_auth_options.flags |= CO_PACKET_ID_LONG_FORM; + + /* get initial frame parms, still need to finalize */ + tas->frame = tls_options->frame; + + return tas; +} + +void +tls_auth_standalone_finalize (struct tls_auth_standalone *tas, + const struct frame *frame) +{ + tls_init_control_channel_frame_parameters (frame, &tas->frame); +} + +/* + * Set local and remote option compatibility strings. + * Used to verify compatibility of local and remote option + * sets. + */ +void +tls_multi_init_set_options (struct tls_multi* multi, + const char *local, + const char *remote) +{ +#ifdef ENABLE_OCC + /* initialize options string */ + multi->opt.local_options = local; + multi->opt.remote_options = remote; +#endif +} + +void +tls_multi_free (struct tls_multi *multi, bool clear) +{ + int i; + + ASSERT (multi); + +#ifdef MANAGEMENT_DEF_AUTH + man_def_auth_set_client_reason(multi, NULL); + + free (multi->peer_info); +#endif + + if (multi->locked_cn) + free (multi->locked_cn); + + if (multi->locked_username) + free (multi->locked_username); + + cert_hash_free (multi->locked_cert_hash_set); + + for (i = 0; i < TM_SIZE; ++i) + tls_session_free (&multi->session[i], false); + + if (clear) + CLEAR (*multi); + + free(multi); +} + +/* + * Move a packet authentication HMAC + related fields to or from the front + * of the buffer so it can be processed by encrypt/decrypt. + */ + +/* + * Dependent on hmac size, opcode size, and session_id size. + * Will assert if too small. + */ +#define SWAP_BUF_SIZE 256 + +static bool +swap_hmac (struct buffer *buf, const struct crypto_options *co, bool incoming) +{ + struct key_ctx *ctx; + + ASSERT (co); + + ctx = (incoming ? &co->key_ctx_bi->decrypt : &co->key_ctx_bi->encrypt); + ASSERT (ctx->hmac); + + { + /* hmac + packet_id (8 bytes) */ + const int hmac_size = HMAC_size (ctx->hmac) + packet_id_size (true); + + /* opcode + session_id */ + const int osid_size = 1 + SID_SIZE; + + int e1, e2; + uint8_t *b = BPTR (buf); + uint8_t buf1[SWAP_BUF_SIZE]; + uint8_t buf2[SWAP_BUF_SIZE]; + + if (incoming) + { + e1 = osid_size; + e2 = hmac_size; + } + else + { + e1 = hmac_size; + e2 = osid_size; + } + + ASSERT (e1 <= SWAP_BUF_SIZE && e2 <= SWAP_BUF_SIZE); + + if (buf->len >= e1 + e2) + { + memcpy (buf1, b, e1); + memcpy (buf2, b + e1, e2); + memcpy (b, buf2, e2); + memcpy (b + e2, buf1, e1); + return true; + } + else + return false; + } +} + +#undef SWAP_BUF_SIZE + +/* + * Write a control channel authentication record. + */ +static void +write_control_auth (struct tls_session *session, + struct key_state *ks, + struct buffer *buf, + struct link_socket_actual **to_link_addr, + int opcode, + int max_ack, + bool prepend_ack) +{ + uint8_t *header; + struct buffer null = clear_buf (); + + ASSERT (link_socket_actual_defined (&ks->remote_addr)); + ASSERT (reliable_ack_write + (ks->rec_ack, buf, &ks->session_id_remote, max_ack, prepend_ack)); + ASSERT (session_id_write_prepend (&session->session_id, buf)); + ASSERT (header = buf_prepend (buf, 1)); + *header = ks->key_id | (opcode << P_OPCODE_SHIFT); + if (session->tls_auth.key_ctx_bi->encrypt.hmac) + { + /* no encryption, only write hmac */ + openvpn_encrypt (buf, null, &session->tls_auth, NULL); + ASSERT (swap_hmac (buf, &session->tls_auth, false)); + } + *to_link_addr = &ks->remote_addr; +} + +/* + * Read a control channel authentication record. + */ +static bool +read_control_auth (struct buffer *buf, + const struct crypto_options *co, + const struct link_socket_actual *from) +{ + struct gc_arena gc = gc_new (); + + if (co->key_ctx_bi->decrypt.hmac) + { + struct buffer null = clear_buf (); + + /* move the hmac record to the front of the packet */ + if (!swap_hmac (buf, co, true)) + { + msg (D_TLS_ERRORS, + "TLS Error: cannot locate HMAC in incoming packet from %s", + print_link_socket_actual (from, &gc)); + gc_free (&gc); + return false; + } + + /* authenticate only (no decrypt) and remove the hmac record + from the head of the buffer */ + openvpn_decrypt (buf, null, co, NULL); + if (!buf->len) + { + msg (D_TLS_ERRORS, + "TLS Error: incoming packet authentication failed from %s", + print_link_socket_actual (from, &gc)); + gc_free (&gc); + return false; + } + + } + + /* advance buffer pointer past opcode & session_id since our caller + already read it */ + buf_advance (buf, SID_SIZE + 1); + + gc_free (&gc); + return true; +} + +/* + * For debugging, print contents of key_source2 structure. + */ + +static void +key_source_print (const struct key_source *k, + const char *prefix) +{ + struct gc_arena gc = gc_new (); + + VALGRIND_MAKE_READABLE ((void *)k->pre_master, sizeof (k->pre_master)); + VALGRIND_MAKE_READABLE ((void *)k->random1, sizeof (k->random1)); + VALGRIND_MAKE_READABLE ((void *)k->random2, sizeof (k->random2)); + + dmsg (D_SHOW_KEY_SOURCE, + "%s pre_master: %s", + prefix, + format_hex (k->pre_master, sizeof (k->pre_master), 0, &gc)); + dmsg (D_SHOW_KEY_SOURCE, + "%s random1: %s", + prefix, + format_hex (k->random1, sizeof (k->random1), 0, &gc)); + dmsg (D_SHOW_KEY_SOURCE, + "%s random2: %s", + prefix, + format_hex (k->random2, sizeof (k->random2), 0, &gc)); + + gc_free (&gc); +} + +static void +key_source2_print (const struct key_source2 *k) +{ + key_source_print (&k->client, "Client"); + key_source_print (&k->server, "Server"); +} + +/* + * Use the TLS PRF function for generating data channel keys. + * This code is taken from the OpenSSL library. + * + * TLS generates keys as such: + * + * master_secret[48] = PRF(pre_master_secret[48], "master secret", + * ClientHello.random[32] + ServerHello.random[32]) + * + * key_block[] = PRF(SecurityParameters.master_secret[48], + * "key expansion", + * SecurityParameters.server_random[32] + + * SecurityParameters.client_random[32]); + * + * Notes: + * + * (1) key_block contains a full set of 4 keys. + * (2) The pre-master secret is generated by the client. + */ + +static void +tls1_P_hash(const EVP_MD *md, + const uint8_t *sec, + int sec_len, + const uint8_t *seed, + int seed_len, + uint8_t *out, + int olen) +{ + struct gc_arena gc = gc_new (); + int chunk,n; + unsigned int j; + HMAC_CTX ctx; + HMAC_CTX ctx_tmp; + uint8_t A1[EVP_MAX_MD_SIZE]; + unsigned int A1_len; + +#ifdef ENABLE_DEBUG + const int olen_orig = olen; + const uint8_t *out_orig = out; +#endif + + dmsg (D_SHOW_KEY_SOURCE, "tls1_P_hash sec: %s", format_hex (sec, sec_len, 0, &gc)); + dmsg (D_SHOW_KEY_SOURCE, "tls1_P_hash seed: %s", format_hex (seed, seed_len, 0, &gc)); + + chunk=EVP_MD_size(md); + + HMAC_CTX_init(&ctx); + HMAC_CTX_init(&ctx_tmp); + HMAC_Init_ex(&ctx,sec,sec_len,md, NULL); + HMAC_Init_ex(&ctx_tmp,sec,sec_len,md, NULL); + HMAC_Update(&ctx,seed,seed_len); + HMAC_Final(&ctx,A1,&A1_len); + + n=0; + for (;;) + { + HMAC_Init_ex(&ctx,NULL,0,NULL,NULL); /* re-init */ + HMAC_Init_ex(&ctx_tmp,NULL,0,NULL,NULL); /* re-init */ + HMAC_Update(&ctx,A1,A1_len); + HMAC_Update(&ctx_tmp,A1,A1_len); + HMAC_Update(&ctx,seed,seed_len); + + if (olen > chunk) + { + HMAC_Final(&ctx,out,&j); + out+=j; + olen-=j; + HMAC_Final(&ctx_tmp,A1,&A1_len); /* calc the next A1 value */ + } + else /* last one */ + { + HMAC_Final(&ctx,A1,&A1_len); + memcpy(out,A1,olen); + break; + } + } + HMAC_CTX_cleanup(&ctx); + HMAC_CTX_cleanup(&ctx_tmp); + CLEAR (A1); + + dmsg (D_SHOW_KEY_SOURCE, "tls1_P_hash out: %s", format_hex (out_orig, olen_orig, 0, &gc)); + gc_free (&gc); +} + +static void +tls1_PRF(uint8_t *label, + int label_len, + const uint8_t *sec, + int slen, + uint8_t *out1, + int olen) +{ + struct gc_arena gc = gc_new (); + const EVP_MD *md5 = EVP_md5(); + const EVP_MD *sha1 = EVP_sha1(); + int len,i; + const uint8_t *S1,*S2; + uint8_t *out2; + + out2 = (uint8_t *) gc_malloc (olen, false, &gc); + + len=slen/2; + S1=sec; + S2= &(sec[len]); + len+=(slen&1); /* add for odd, make longer */ + + + tls1_P_hash(md5 ,S1,len,label,label_len,out1,olen); + tls1_P_hash(sha1,S2,len,label,label_len,out2,olen); + + for (i=0; i<olen; i++) + out1[i]^=out2[i]; + + memset (out2, 0, olen); + + dmsg (D_SHOW_KEY_SOURCE, "tls1_PRF out[%d]: %s", olen, format_hex (out1, olen, 0, &gc)); + + gc_free (&gc); +} + +static void +openvpn_PRF (const uint8_t *secret, + int secret_len, + const char *label, + const uint8_t *client_seed, + int client_seed_len, + const uint8_t *server_seed, + int server_seed_len, + const struct session_id *client_sid, + const struct session_id *server_sid, + uint8_t *output, + int output_len) +{ + /* concatenate seed components */ + + struct buffer seed = alloc_buf (strlen (label) + + client_seed_len + + server_seed_len + + SID_SIZE * 2); + + ASSERT (buf_write (&seed, label, strlen (label))); + ASSERT (buf_write (&seed, client_seed, client_seed_len)); + ASSERT (buf_write (&seed, server_seed, server_seed_len)); + + if (client_sid) + ASSERT (buf_write (&seed, client_sid->id, SID_SIZE)); + if (server_sid) + ASSERT (buf_write (&seed, server_sid->id, SID_SIZE)); + + /* compute PRF */ + tls1_PRF (BPTR(&seed), BLEN(&seed), secret, secret_len, output, output_len); + + buf_clear (&seed); + free_buf (&seed); + + VALGRIND_MAKE_READABLE ((void *)output, output_len); +} + +/* + * Using source entropy from local and remote hosts, mix into + * master key. + */ +static bool +generate_key_expansion (struct key_ctx_bi *key, + const struct key_type *key_type, + const struct key_source2 *key_src, + const struct session_id *client_sid, + const struct session_id *server_sid, + bool server) +{ + uint8_t master[48]; + struct key2 key2; + bool ret = false; + int i; + + CLEAR (master); + CLEAR (key2); + + /* debugging print of source key material */ + key_source2_print (key_src); + + /* compute master secret */ + openvpn_PRF (key_src->client.pre_master, + sizeof(key_src->client.pre_master), + KEY_EXPANSION_ID " master secret", + key_src->client.random1, + sizeof(key_src->client.random1), + key_src->server.random1, + sizeof(key_src->server.random1), + NULL, + NULL, + master, + sizeof(master)); + + /* compute key expansion */ + openvpn_PRF (master, + sizeof(master), + KEY_EXPANSION_ID " key expansion", + key_src->client.random2, + sizeof(key_src->client.random2), + key_src->server.random2, + sizeof(key_src->server.random2), + client_sid, + server_sid, + (uint8_t*)key2.keys, + sizeof(key2.keys)); + + key2.n = 2; + + key2_print (&key2, key_type, "Master Encrypt", "Master Decrypt"); + + /* check for weak keys */ + for (i = 0; i < 2; ++i) + { + fixup_key (&key2.keys[i], key_type); + if (!check_key (&key2.keys[i], key_type)) + { + msg (D_TLS_ERRORS, "TLS Error: Bad dynamic key generated"); + goto exit; + } + } + + /* Initialize OpenSSL key contexts */ + + ASSERT (server == true || server == false); + + init_key_ctx (&key->encrypt, + &key2.keys[(int)server], + key_type, + DO_ENCRYPT, + "Data Channel Encrypt"); + + init_key_ctx (&key->decrypt, + &key2.keys[1-(int)server], + key_type, + DO_DECRYPT, + "Data Channel Decrypt"); + + ret = true; + + exit: + CLEAR (master); + CLEAR (key2); + + return ret; +} + +static bool +random_bytes_to_buf (struct buffer *buf, + uint8_t *out, + int outlen) +{ + if (!RAND_bytes (out, outlen)) + msg (M_FATAL, "ERROR: Random number generator cannot obtain entropy for key generation [SSL]"); + if (!buf_write (buf, out, outlen)) + return false; + return true; +} + +static bool +key_source2_randomize_write (struct key_source2 *k2, + struct buffer *buf, + bool server) +{ + struct key_source *k = &k2->client; + if (server) + k = &k2->server; + + CLEAR (*k); + + if (!server) + { + if (!random_bytes_to_buf (buf, k->pre_master, sizeof (k->pre_master))) + return false; + } + + if (!random_bytes_to_buf (buf, k->random1, sizeof (k->random1))) + return false; + if (!random_bytes_to_buf (buf, k->random2, sizeof (k->random2))) + return false; + + return true; +} + +static int +key_source2_read (struct key_source2 *k2, + struct buffer *buf, + bool server) +{ + struct key_source *k = &k2->client; + + if (!server) + k = &k2->server; + + CLEAR (*k); + + if (server) + { + if (!buf_read (buf, k->pre_master, sizeof (k->pre_master))) + return 0; + } + + if (!buf_read (buf, k->random1, sizeof (k->random1))) + return 0; + if (!buf_read (buf, k->random2, sizeof (k->random2))) + return 0; + + return 1; +} + +static void +flush_payload_buffer (struct tls_multi *multi, struct key_state *ks) +{ + struct buffer *b; + while ((b = buffer_list_peek (ks->paybuf))) + { + key_state_write_plaintext_const (multi, ks, b->data, b->len); + buffer_list_pop (ks->paybuf); + } +} + +/* + * Macros for key_state_soft_reset & tls_process + */ +#define ks (&session->key[KS_PRIMARY]) /* primary key */ +#define ks_lame (&session->key[KS_LAME_DUCK]) /* retiring key */ + +/* true if no in/out acknowledgements pending */ +#define FULL_SYNC \ + (reliable_empty(ks->send_reliable) && reliable_ack_empty (ks->rec_ack)) + +/* + * Move the active key to the lame duck key and reinitialize the + * active key. + */ +static void +key_state_soft_reset (struct tls_session *session) +{ + ks->must_die = now + session->opt->transition_window; /* remaining lifetime of old key */ + key_state_free (ks_lame, false); + *ks_lame = *ks; + + key_state_init (session, ks); + ks->session_id_remote = ks_lame->session_id_remote; + ks->remote_addr = ks_lame->remote_addr; +} + +/* + * Read/write strings from/to a struct buffer with a u16 length prefix. + */ + +static bool +write_string (struct buffer *buf, const char *str, const int maxlen) +{ + const int len = strlen (str) + 1; + if (len < 1 || (maxlen >= 0 && len > maxlen)) + return false; + if (!buf_write_u16 (buf, len)) + return false; + if (!buf_write (buf, str, len)) + return false; + return true; +} + +static bool +write_empty_string (struct buffer *buf) +{ + if (!buf_write_u16 (buf, 0)) + return false; + return true; +} + +static bool +read_string (struct buffer *buf, char *str, const unsigned int capacity) +{ + const int len = buf_read_u16 (buf); + if (len < 1 || len > (int)capacity) + return false; + if (!buf_read (buf, str, len)) + return false; + str[len-1] = '\0'; + return true; +} + +static char * +read_string_alloc (struct buffer *buf) +{ + const int len = buf_read_u16 (buf); + char *str; + + if (len < 1) + return NULL; + str = (char *) malloc(len); + check_malloc_return(str); + if (!buf_read (buf, str, len)) + { + free (str); + return NULL; + } + str[len-1] = '\0'; + return str; +} + +void +read_string_discard (struct buffer *buf) +{ + char *data = read_string_alloc(buf); + if (data) + free (data); +} + +/* + * Authenticate a client using username/password. + * Runs on server. + * + * If you want to add new authentication methods, + * this is the place to start. + */ + +static bool +verify_user_pass_script (struct tls_session *session, const struct user_pass *up) +{ + struct gc_arena gc = gc_new (); + struct argv argv = argv_new (); + const char *tmp_file = ""; + bool ret = false; + + /* Is username defined? */ + if ((session->opt->ssl_flags & SSLF_AUTH_USER_PASS_OPTIONAL) || strlen (up->username)) + { + /* Set environmental variables prior to calling script */ + setenv_str (session->opt->es, "script_type", "user-pass-verify"); + + if (session->opt->auth_user_pass_verify_script_via_file) + { + struct status_output *so; + + tmp_file = create_temp_file (session->opt->tmp_dir, "up", &gc); + if( tmp_file ) { + so = status_open (tmp_file, 0, -1, NULL, STATUS_OUTPUT_WRITE); + status_printf (so, "%s", up->username); + status_printf (so, "%s", up->password); + if (!status_close (so)) + { + msg (D_TLS_ERRORS, "TLS Auth Error: could not write username/password to file: %s", + tmp_file); + goto done; + } + } else { + msg (D_TLS_ERRORS, "TLS Auth Error: could not create write " + "username/password to temp file"); + } + } + else + { + setenv_str (session->opt->es, "username", up->username); + setenv_str (session->opt->es, "password", up->password); + } + + /* setenv incoming cert common name for script */ + setenv_str (session->opt->es, "common_name", session->common_name); + + /* setenv client real IP address */ + setenv_untrusted (session); + + /* format command line */ + argv_printf (&argv, "%sc %s", session->opt->auth_user_pass_verify_script, tmp_file); + + /* call command */ + ret = openvpn_run_script (&argv, session->opt->es, 0, + "--auth-user-pass-verify"); + + if (!session->opt->auth_user_pass_verify_script_via_file) + setenv_del (session->opt->es, "password"); + } + else + { + msg (D_TLS_ERRORS, "TLS Auth Error: peer provided a blank username"); + } + + done: + if (tmp_file && strlen (tmp_file) > 0) + delete_file (tmp_file); + + argv_reset (&argv); + gc_free (&gc); + return ret; +} + +static int +verify_user_pass_plugin (struct tls_session *session, const struct user_pass *up, const char *raw_username) +{ + int retval = OPENVPN_PLUGIN_FUNC_ERROR; + + /* Is username defined? */ + if ((session->opt->ssl_flags & SSLF_AUTH_USER_PASS_OPTIONAL) || strlen (up->username)) + { + /* set username/password in private env space */ + setenv_str (session->opt->es, "username", raw_username); + setenv_str (session->opt->es, "password", up->password); + + /* setenv incoming cert common name for script */ + setenv_str (session->opt->es, "common_name", session->common_name); + + /* setenv client real IP address */ + setenv_untrusted (session); + +#ifdef PLUGIN_DEF_AUTH + /* generate filename for deferred auth control file */ + key_state_gen_auth_control_file (ks, session->opt); +#endif + + /* call command */ + retval = plugin_call (session->opt->plugins, OPENVPN_PLUGIN_AUTH_USER_PASS_VERIFY, NULL, NULL, session->opt->es); + +#ifdef PLUGIN_DEF_AUTH + /* purge auth control filename (and file itself) for non-deferred returns */ + if (retval != OPENVPN_PLUGIN_FUNC_DEFERRED) + key_state_rm_auth_control_file (ks); +#endif + + setenv_del (session->opt->es, "password"); + setenv_str (session->opt->es, "username", up->username); + } + else + { + msg (D_TLS_ERRORS, "TLS Auth Error (verify_user_pass_plugin): peer provided a blank username"); + } + + return retval; +} + +/* + * MANAGEMENT_DEF_AUTH internal ssl.c status codes + */ +#define KMDA_ERROR 0 +#define KMDA_SUCCESS 1 +#define KMDA_UNDEF 2 +#define KMDA_DEF 3 + +#ifdef MANAGEMENT_DEF_AUTH +static int +verify_user_pass_management (struct tls_session *session, const struct user_pass *up, const char *raw_username) +{ + int retval = KMDA_ERROR; + + /* Is username defined? */ + if ((session->opt->ssl_flags & SSLF_AUTH_USER_PASS_OPTIONAL) || strlen (up->username)) + { + /* set username/password in private env space */ + setenv_str (session->opt->es, "username", raw_username); + setenv_str (session->opt->es, "password", up->password); + + /* setenv incoming cert common name for script */ + setenv_str (session->opt->es, "common_name", session->common_name); + + /* setenv client real IP address */ + setenv_untrusted (session); + + if (management) + management_notify_client_needing_auth (management, ks->mda_key_id, session->opt->mda_context, session->opt->es); + + setenv_del (session->opt->es, "password"); + setenv_str (session->opt->es, "username", up->username); + + retval = KMDA_SUCCESS; + } + else + { + msg (D_TLS_ERRORS, "TLS Auth Error (verify_user_pass_management): peer provided a blank username"); + } + + return retval; +} +#endif + +/* + * Handle the reading and writing of key data to and from + * the TLS control channel (cleartext). + */ + +static bool +key_method_1_write (struct buffer *buf, struct tls_session *session) +{ + struct key key; + + ASSERT (session->opt->key_method == 1); + ASSERT (buf_init (buf, 0)); + + generate_key_random (&key, &session->opt->key_type); + if (!check_key (&key, &session->opt->key_type)) + { + msg (D_TLS_ERRORS, "TLS Error: Bad encrypting key generated"); + return false; + } + + if (!write_key (&key, &session->opt->key_type, buf)) + { + msg (D_TLS_ERRORS, "TLS Error: write_key failed"); + return false; + } + + init_key_ctx (&ks->key.encrypt, &key, &session->opt->key_type, + DO_ENCRYPT, "Data Channel Encrypt"); + CLEAR (key); + + /* send local options string */ + { + const char *local_options = local_options_string (session); + const int optlen = strlen (local_options) + 1; + if (!buf_write (buf, local_options, optlen)) + { + msg (D_TLS_ERRORS, "TLS Error: KM1 write options failed"); + return false; + } + } + + return true; +} + +static bool +push_peer_info(struct buffer *buf, struct tls_session *session) +{ + struct gc_arena gc = gc_new (); + bool ret = false; + +#ifdef ENABLE_PUSH_PEER_INFO + if (session->opt->push_peer_info) /* write peer info */ + { + struct env_set *es = session->opt->es; + struct env_item *e; + struct buffer out = alloc_buf_gc (512*3, &gc); + + /* push version */ + buf_printf (&out, "IV_VER=%s\n", PACKAGE_VERSION); + + /* push platform */ +#if defined(TARGET_LINUX) + buf_printf (&out, "IV_PLAT=linux\n"); +#elif defined(TARGET_SOLARIS) + buf_printf (&out, "IV_PLAT=solaris\n"); +#elif defined(TARGET_OPENBSD) + buf_printf (&out, "IV_PLAT=openbsd\n"); +#elif defined(TARGET_DARWIN) + buf_printf (&out, "IV_PLAT=mac\n"); +#elif defined(TARGET_NETBSD) + buf_printf (&out, "IV_PLAT=netbsd\n"); +#elif defined(TARGET_FREEBSD) + buf_printf (&out, "IV_PLAT=freebsd\n"); +#elif defined(WIN32) + buf_printf (&out, "IV_PLAT=win\n"); +#endif + + /* push mac addr */ + { + bool get_default_gateway_mac_addr (unsigned char *macaddr); + uint8_t macaddr[6]; + get_default_gateway_mac_addr (macaddr); + buf_printf (&out, "IV_HWADDR=%s\n", format_hex_ex (macaddr, 6, 0, 1, ":", &gc)); + } + + /* push env vars that begin with UV_ */ + for (e=es->list; e != NULL; e=e->next) + { + if (e->string) + { + if (!strncmp(e->string, "UV_", 3) && buf_safe(&out, strlen(e->string)+1)) + buf_printf (&out, "%s\n", e->string); + } + } + + if (!write_string(buf, BSTR(&out), -1)) + goto error; + } + else +#endif + { + if (!write_empty_string (buf)) /* no peer info */ + goto error; + } + ret = true; + + error: + gc_free (&gc); + return ret; +} + +static bool +key_method_2_write (struct buffer *buf, struct tls_session *session) +{ + ASSERT (session->opt->key_method == 2); + ASSERT (buf_init (buf, 0)); + + /* write a uint32 0 */ + if (!buf_write_u32 (buf, 0)) + goto error; + + /* write key_method + flags */ + if (!buf_write_u8 (buf, (session->opt->key_method & KEY_METHOD_MASK))) + goto error; + + /* write key source material */ + if (!key_source2_randomize_write (ks->key_src, buf, session->opt->server)) + goto error; + + /* write options string */ + { + if (!write_string (buf, local_options_string (session), TLS_OPTIONS_LEN)) + goto error; + } + + /* write username/password if specified */ + if (auth_user_pass_enabled) + { + auth_user_pass_setup (NULL); + if (!write_string (buf, auth_user_pass.username, -1)) + goto error; + if (!write_string (buf, auth_user_pass.password, -1)) + goto error; + purge_user_pass (&auth_user_pass, false); + } + else + { + if (!write_empty_string (buf)) /* no username */ + goto error; + if (!write_empty_string (buf)) /* no password */ + goto error; + } + + if (!push_peer_info (buf, session)) + goto error; + + /* + * generate tunnel keys if server + */ + if (session->opt->server) + { + if (ks->authenticated) + { + if (!generate_key_expansion (&ks->key, + &session->opt->key_type, + ks->key_src, + &ks->session_id_remote, + &session->session_id, + true)) + { + msg (D_TLS_ERRORS, "TLS Error: server generate_key_expansion failed"); + goto error; + } + } + + CLEAR (*ks->key_src); + } + + return true; + + error: + msg (D_TLS_ERRORS, "TLS Error: Key Method #2 write failed"); + CLEAR (*ks->key_src); + return false; +} + +static bool +key_method_1_read (struct buffer *buf, struct tls_session *session) +{ + int status; + struct key key; + + ASSERT (session->opt->key_method == 1); + + if (!session->verified) + { + msg (D_TLS_ERRORS, + "TLS Error: Certificate verification failed (key-method 1)"); + goto error; + } + + status = read_key (&key, &session->opt->key_type, buf); + if (status != 1) + { + msg (D_TLS_ERRORS, + "TLS Error: Error reading data channel key from plaintext buffer"); + goto error; + } + + if (!check_key (&key, &session->opt->key_type)) + { + msg (D_TLS_ERRORS, "TLS Error: Bad decrypting key received from peer"); + goto error; + } + + if (buf->len < 1) + { + msg (D_TLS_ERRORS, "TLS Error: Missing options string"); + goto error; + } + +#ifdef ENABLE_OCC + /* compare received remote options string + with our locally computed options string */ + if (!session->opt->disable_occ && + !options_cmp_equal_safe ((char *) BPTR (buf), session->opt->remote_options, buf->len)) + { + options_warning_safe ((char *) BPTR (buf), session->opt->remote_options, buf->len); + } +#endif + + buf_clear (buf); + + init_key_ctx (&ks->key.decrypt, &key, &session->opt->key_type, + DO_DECRYPT, "Data Channel Decrypt"); + CLEAR (key); + ks->authenticated = true; + return true; + + error: + buf_clear (buf); + CLEAR (key); + return false; +} + +static bool +key_method_2_read (struct buffer *buf, struct tls_multi *multi, struct tls_session *session) +{ + struct gc_arena gc = gc_new (); + int key_method_flags; + char *options; + struct user_pass *up; + + bool man_def_auth = KMDA_UNDEF; + +#ifdef MANAGEMENT_DEF_AUTH + if (management_enable_def_auth (management)) + man_def_auth = KMDA_DEF; +#endif + + ASSERT (session->opt->key_method == 2); + + /* allocate temporary objects */ + ALLOC_ARRAY_CLEAR_GC (options, char, TLS_OPTIONS_LEN, &gc); + + /* discard leading uint32 */ + ASSERT (buf_advance (buf, 4)); + + /* get key method */ + key_method_flags = buf_read_u8 (buf); + if ((key_method_flags & KEY_METHOD_MASK) != 2) + { + msg (D_TLS_ERRORS, + "TLS ERROR: Unknown key_method/flags=%d received from remote host", + key_method_flags); + goto error; + } + + /* get key source material (not actual keys yet) */ + if (!key_source2_read (ks->key_src, buf, session->opt->server)) + { + msg (D_TLS_ERRORS, "TLS Error: Error reading remote data channel key source entropy from plaintext buffer"); + goto error; + } + + /* get options */ + if (!read_string (buf, options, TLS_OPTIONS_LEN)) + { + msg (D_TLS_ERRORS, "TLS Error: Failed to read required OCC options string"); + goto error; + } + + /* should we check username/password? */ + ks->authenticated = false; + if (session->opt->auth_user_pass_verify_script + || plugin_defined (session->opt->plugins, OPENVPN_PLUGIN_AUTH_USER_PASS_VERIFY) + || man_def_auth == KMDA_DEF) + { + int s1 = OPENVPN_PLUGIN_FUNC_SUCCESS; + bool s2 = true; + char *raw_username; + bool username_status, password_status; + + /* get username/password from plaintext buffer */ + ALLOC_OBJ_CLEAR_GC (up, struct user_pass, &gc); + username_status = read_string (buf, up->username, USER_PASS_LEN); + password_status = read_string (buf, up->password, USER_PASS_LEN); + if (!username_status || !password_status) + { + CLEAR (*up); + if (!(session->opt->ssl_flags & SSLF_AUTH_USER_PASS_OPTIONAL)) + { + msg (D_TLS_ERRORS, "TLS Error: Auth Username/Password was not provided by peer"); + goto error; + } + } + + /* preserve raw username before string_mod remapping, for plugins */ + ALLOC_ARRAY_CLEAR_GC (raw_username, char, USER_PASS_LEN, &gc); + strcpy (raw_username, up->username); + string_mod (raw_username, CC_PRINT, CC_CRLF, '_'); + + /* enforce character class restrictions in username/password */ + string_mod_sslname (up->username, COMMON_NAME_CHAR_CLASS, session->opt->ssl_flags); + string_mod (up->password, CC_PRINT, CC_CRLF, '_'); + + /* call plugin(s) and/or script */ +#ifdef MANAGEMENT_DEF_AUTH + /* get peer info from control channel */ + free (multi->peer_info); + multi->peer_info = read_string_alloc (buf); + + if (man_def_auth == KMDA_DEF) + man_def_auth = verify_user_pass_management (session, up, raw_username); +#endif + if (plugin_defined (session->opt->plugins, OPENVPN_PLUGIN_AUTH_USER_PASS_VERIFY)) + s1 = verify_user_pass_plugin (session, up, raw_username); + if (session->opt->auth_user_pass_verify_script) + s2 = verify_user_pass_script (session, up); + + /* check sizing of username if it will become our common name */ + if ((session->opt->ssl_flags & SSLF_USERNAME_AS_COMMON_NAME) && strlen (up->username) >= TLS_USERNAME_LEN) + { + msg (D_TLS_ERRORS, "TLS Auth Error: --username-as-common name specified and username is longer than the maximum permitted Common Name length of %d characters", TLS_USERNAME_LEN); + s1 = OPENVPN_PLUGIN_FUNC_ERROR; + } + + /* auth succeeded? */ + if ((s1 == OPENVPN_PLUGIN_FUNC_SUCCESS +#ifdef PLUGIN_DEF_AUTH + || s1 == OPENVPN_PLUGIN_FUNC_DEFERRED +#endif + ) && s2 && man_def_auth != KMDA_ERROR + && tls_lock_username (multi, up->username)) + { + ks->authenticated = true; +#ifdef PLUGIN_DEF_AUTH + if (s1 == OPENVPN_PLUGIN_FUNC_DEFERRED) + ks->auth_deferred = true; +#endif +#ifdef MANAGEMENT_DEF_AUTH + if (man_def_auth != KMDA_UNDEF) + ks->auth_deferred = true; +#endif + if ((session->opt->ssl_flags & SSLF_USERNAME_AS_COMMON_NAME)) + set_common_name (session, up->username); +#ifdef ENABLE_DEF_AUTH + msg (D_HANDSHAKE, "TLS: Username/Password authentication %s for username '%s' %s", + ks->auth_deferred ? "deferred" : "succeeded", + up->username, + (session->opt->ssl_flags & SSLF_USERNAME_AS_COMMON_NAME) ? "[CN SET]" : ""); +#else + msg (D_HANDSHAKE, "TLS: Username/Password authentication %s for username '%s' %s", + "succeeded", + up->username, + (session->opt->ssl_flags & SSLF_USERNAME_AS_COMMON_NAME) ? "[CN SET]" : ""); +#endif + } + else + { + msg (D_TLS_ERRORS, "TLS Auth Error: Auth Username/Password verification failed for peer"); + } + + CLEAR (*up); + } + else + { + if (!session->verified) + { + msg (D_TLS_ERRORS, + "TLS Error: Certificate verification failed (key-method 2)"); + goto error; + } + ks->authenticated = true; + } + + /* While it shouldn't really happen, don't allow the common name to be NULL */ + if (!session->common_name) + set_common_name (session, ""); + + /* Don't allow the CN to change once it's been locked */ + if (ks->authenticated && multi->locked_cn) + { + const char *cn = session->common_name; + if (cn && strcmp (cn, multi->locked_cn)) + { + msg (D_TLS_ERRORS, "TLS Auth Error: TLS object CN attempted to change from '%s' to '%s' -- tunnel disabled", + multi->locked_cn, + cn); + + /* change the common name back to its original value and disable the tunnel */ + set_common_name (session, multi->locked_cn); + tls_deauthenticate (multi); + } + } + + /* Don't allow the cert hashes to change once they have been locked */ + if (ks->authenticated && multi->locked_cert_hash_set) + { + const struct cert_hash_set *chs = session->cert_hash_set; + if (chs && !cert_hash_compare (chs, multi->locked_cert_hash_set)) + { + msg (D_TLS_ERRORS, "TLS Auth Error: TLS object CN=%s client-provided SSL certs unexpectedly changed during mid-session reauth", + session->common_name); + + /* disable the tunnel */ + tls_deauthenticate (multi); + } + } + + /* verify --client-config-dir based authentication */ + if (ks->authenticated && session->opt->client_config_dir_exclusive) + { + const char *cn = session->common_name; + const char *path = gen_path (session->opt->client_config_dir_exclusive, cn, &gc); + if (!cn || !strcmp (cn, CCD_DEFAULT) || !test_file (path)) + { + ks->authenticated = false; + msg (D_TLS_ERRORS, "TLS Auth Error: --client-config-dir authentication failed for common name '%s' file='%s'", + session->common_name, + path ? path : "UNDEF"); + } + } + +#ifdef ENABLE_OCC + /* check options consistency */ + if (!session->opt->disable_occ && + !options_cmp_equal (options, session->opt->remote_options)) + { + options_warning (options, session->opt->remote_options); + if (session->opt->ssl_flags & SSLF_OPT_VERIFY) + { + msg (D_TLS_ERRORS, "Option inconsistency warnings triggering disconnect due to --opt-verify"); + ks->authenticated = false; + } + } +#endif + + buf_clear (buf); + + /* + * Call OPENVPN_PLUGIN_TLS_FINAL plugin if defined, for final + * veto opportunity over authentication decision. + */ + if (ks->authenticated && plugin_defined (session->opt->plugins, OPENVPN_PLUGIN_TLS_FINAL)) + { + if (plugin_call (session->opt->plugins, OPENVPN_PLUGIN_TLS_FINAL, NULL, NULL, session->opt->es) != OPENVPN_PLUGIN_FUNC_SUCCESS) + ks->authenticated = false; + } + + /* + * Generate tunnel keys if client + */ + if (!session->opt->server) + { + if (!generate_key_expansion (&ks->key, + &session->opt->key_type, + ks->key_src, + &session->session_id, + &ks->session_id_remote, + false)) + { + msg (D_TLS_ERRORS, "TLS Error: client generate_key_expansion failed"); + goto error; + } + + CLEAR (*ks->key_src); + } + + gc_free (&gc); + return true; + + error: + CLEAR (*ks->key_src); + buf_clear (buf); + gc_free (&gc); + return false; +} + +static int +auth_deferred_expire_window (const struct tls_options *o) +{ + int ret = o->handshake_window; + const int r2 = o->renegotiate_seconds / 2; + + if (o->renegotiate_seconds && r2 < ret) + ret = r2; + return ret; +} + +/* + * This is the primary routine for processing TLS stuff inside the + * the main event loop. When this routine exits + * with non-error status, it will set *wakeup to the number of seconds + * when it wants to be called again. + * + * Return value is true if we have placed a packet in *to_link which we + * want to send to our peer. + */ +static bool +tls_process (struct tls_multi *multi, + struct tls_session *session, + struct buffer *to_link, + struct link_socket_actual **to_link_addr, + struct link_socket_info *to_link_socket_info, + interval_t *wakeup) +{ + struct gc_arena gc = gc_new (); + struct buffer *buf; + bool state_change = false; + bool active = false; + + /* Make sure we were initialized and that we're not in an error state */ + ASSERT (ks->state != S_UNDEF); + ASSERT (ks->state != S_ERROR); + ASSERT (session_id_defined (&session->session_id)); + + /* Should we trigger a soft reset? -- new key, keeps old key for a while */ + if (ks->state >= S_ACTIVE && + ((session->opt->renegotiate_seconds + && now >= ks->established + session->opt->renegotiate_seconds) + || (session->opt->renegotiate_bytes + && ks->n_bytes >= session->opt->renegotiate_bytes) + || (session->opt->renegotiate_packets + && ks->n_packets >= session->opt->renegotiate_packets) + || (packet_id_close_to_wrapping (&ks->packet_id.send)))) + { + msg (D_TLS_DEBUG_LOW, + "TLS: soft reset sec=%d bytes=" counter_format "/%d pkts=" counter_format "/%d", + (int)(ks->established + session->opt->renegotiate_seconds - now), + ks->n_bytes, session->opt->renegotiate_bytes, + ks->n_packets, session->opt->renegotiate_packets); + key_state_soft_reset (session); + } + + /* Kill lame duck key transition_window seconds after primary key negotiation */ + if (lame_duck_must_die (session, wakeup)) { + key_state_free (ks_lame, true); + msg (D_TLS_DEBUG_LOW, "TLS: tls_process: killed expiring key"); + } + + do + { + update_time (); + + dmsg (D_TLS_DEBUG, "TLS: tls_process: chg=%d ks=%s lame=%s to_link->len=%d wakeup=%d", + state_change, + state_name (ks->state), + state_name (ks_lame->state), + to_link->len, + *wakeup); + + state_change = false; + + /* + * TLS activity is finished once we get to S_ACTIVE, + * though we will still process acknowledgements. + * + * CHANGED with 2.0 -> now we may send tunnel configuration + * info over the control channel. + */ + if (true) + { + /* Initial handshake */ + if (ks->state == S_INITIAL) + { + buf = reliable_get_buf_output_sequenced (ks->send_reliable); + if (buf) + { + ks->must_negotiate = now + session->opt->handshake_window; + ks->auth_deferred_expire = now + auth_deferred_expire_window (session->opt); + + /* null buffer */ + reliable_mark_active_outgoing (ks->send_reliable, buf, ks->initial_opcode); + INCR_GENERATED; + + ks->state = S_PRE_START; + state_change = true; + dmsg (D_TLS_DEBUG, "TLS: Initial Handshake, sid=%s", + session_id_print (&session->session_id, &gc)); + +#ifdef ENABLE_MANAGEMENT + if (management && ks->initial_opcode != P_CONTROL_SOFT_RESET_V1) + { + management_set_state (management, + OPENVPN_STATE_WAIT, + NULL, + 0, + 0); + } +#endif + } + } + + /* Are we timed out on receive? */ + if (now >= ks->must_negotiate) + { + if (ks->state < S_ACTIVE) + { + msg (D_TLS_ERRORS, + "TLS Error: TLS key negotiation failed to occur within %d seconds (check your network connectivity)", + session->opt->handshake_window); + goto error; + } + else /* assume that ks->state == S_ACTIVE */ + { + dmsg (D_TLS_DEBUG_MED, "STATE S_NORMAL_OP"); + ks->state = S_NORMAL_OP; + ks->must_negotiate = 0; + } + } + + /* Wait for Initial Handshake ACK */ + if (ks->state == S_PRE_START && FULL_SYNC) + { + ks->state = S_START; + state_change = true; + dmsg (D_TLS_DEBUG_MED, "STATE S_START"); + } + + /* Wait for ACK */ + if (((ks->state == S_GOT_KEY && !session->opt->server) || + (ks->state == S_SENT_KEY && session->opt->server))) + { + if (FULL_SYNC) + { + ks->established = now; + dmsg (D_TLS_DEBUG_MED, "STATE S_ACTIVE"); + if (check_debug_level (D_HANDSHAKE)) + print_details (ks->ssl, "Control Channel:"); + state_change = true; + ks->state = S_ACTIVE; + INCR_SUCCESS; + + /* Set outgoing address for data channel packets */ + link_socket_set_outgoing_addr (NULL, to_link_socket_info, &ks->remote_addr, session->common_name, session->opt->es); + + /* Flush any payload packets that were buffered before our state transitioned to S_ACTIVE */ + flush_payload_buffer (multi, ks); + +#ifdef MEASURE_TLS_HANDSHAKE_STATS + show_tls_performance_stats(); +#endif + } + } + + /* Reliable buffer to outgoing TCP/UDP (send up to CONTROL_SEND_ACK_MAX ACKs + for previously received packets) */ + if (!to_link->len && reliable_can_send (ks->send_reliable)) + { + int opcode; + struct buffer b; + + buf = reliable_send (ks->send_reliable, &opcode); + ASSERT (buf); + b = *buf; + INCR_SENT; + + write_control_auth (session, ks, &b, to_link_addr, opcode, + CONTROL_SEND_ACK_MAX, true); + *to_link = b; + active = true; + state_change = true; + dmsg (D_TLS_DEBUG, "Reliable -> TCP/UDP"); + break; + } + +#ifndef TLS_AGGREGATE_ACK + /* Send 1 or more ACKs (each received control packet gets one ACK) */ + if (!to_link->len && !reliable_ack_empty (ks->rec_ack)) + { + buf = &ks->ack_write_buf; + ASSERT (buf_init (buf, FRAME_HEADROOM (&multi->opt.frame))); + write_control_auth (session, ks, buf, to_link_addr, P_ACK_V1, + RELIABLE_ACK_SIZE, false); + *to_link = *buf; + active = true; + state_change = true; + dmsg (D_TLS_DEBUG, "Dedicated ACK -> TCP/UDP"); + break; + } +#endif + + /* Write incoming ciphertext to TLS object */ + buf = reliable_get_buf_sequenced (ks->rec_reliable); + if (buf) + { + int status = 0; + if (buf->len) + { + status = key_state_write_ciphertext (multi, ks, buf); + if (status == -1) + { + msg (D_TLS_ERRORS, + "TLS Error: Incoming Ciphertext -> TLS object write error"); + goto error; + } + } + else + { + status = 1; + } + if (status == 1) + { + reliable_mark_deleted (ks->rec_reliable, buf, true); + state_change = true; + dmsg (D_TLS_DEBUG, "Incoming Ciphertext -> TLS"); + } + } + + /* Read incoming plaintext from TLS object */ + buf = &ks->plaintext_read_buf; + if (!buf->len) + { + int status; + + ASSERT (buf_init (buf, 0)); + status = key_state_read_plaintext (multi, ks, buf, TLS_CHANNEL_BUF_SIZE); + update_time (); + if (status == -1) + { + msg (D_TLS_ERRORS, "TLS Error: TLS object -> incoming plaintext read error"); + goto error; + } + if (status == 1) + { + state_change = true; + dmsg (D_TLS_DEBUG, "TLS -> Incoming Plaintext"); + } +#if 0 /* show null plaintext reads */ + if (!status) + msg (M_INFO, "TLS plaintext read -> NULL return"); +#endif + } + + /* Send Key */ + buf = &ks->plaintext_write_buf; + if (!buf->len && ((ks->state == S_START && !session->opt->server) || + (ks->state == S_GOT_KEY && session->opt->server))) + { + if (session->opt->key_method == 1) + { + if (!key_method_1_write (buf, session)) + goto error; + } + else if (session->opt->key_method == 2) + { + if (!key_method_2_write (buf, session)) + goto error; + } + else + { + ASSERT (0); + } + + state_change = true; + dmsg (D_TLS_DEBUG_MED, "STATE S_SENT_KEY"); + ks->state = S_SENT_KEY; + } + + /* Receive Key */ + buf = &ks->plaintext_read_buf; + if (buf->len + && ((ks->state == S_SENT_KEY && !session->opt->server) + || (ks->state == S_START && session->opt->server))) + { + if (session->opt->key_method == 1) + { + if (!key_method_1_read (buf, session)) + goto error; + } + else if (session->opt->key_method == 2) + { + if (!key_method_2_read (buf, multi, session)) + goto error; + } + else + { + ASSERT (0); + } + + state_change = true; + dmsg (D_TLS_DEBUG_MED, "STATE S_GOT_KEY"); + ks->state = S_GOT_KEY; + } + + /* Write outgoing plaintext to TLS object */ + buf = &ks->plaintext_write_buf; + if (buf->len) + { + int status = key_state_write_plaintext (multi, ks, buf); + if (status == -1) + { + msg (D_TLS_ERRORS, + "TLS ERROR: Outgoing Plaintext -> TLS object write error"); + goto error; + } + if (status == 1) + { + state_change = true; + dmsg (D_TLS_DEBUG, "Outgoing Plaintext -> TLS"); + } + } + + /* Outgoing Ciphertext to reliable buffer */ + if (ks->state >= S_START) + { + buf = reliable_get_buf_output_sequenced (ks->send_reliable); + if (buf) + { + int status = key_state_read_ciphertext (multi, ks, buf, PAYLOAD_SIZE_DYNAMIC (&multi->opt.frame)); + if (status == -1) + { + msg (D_TLS_ERRORS, + "TLS Error: Ciphertext -> reliable TCP/UDP transport read error"); + goto error; + } + if (status == 1) + { + reliable_mark_active_outgoing (ks->send_reliable, buf, P_CONTROL_V1); + INCR_GENERATED; + state_change = true; + dmsg (D_TLS_DEBUG, "Outgoing Ciphertext -> Reliable"); + } + } + } + } + } + while (state_change); + + update_time (); + +#ifdef TLS_AGGREGATE_ACK + /* Send 1 or more ACKs (each received control packet gets one ACK) */ + if (!to_link->len && !reliable_ack_empty (ks->rec_ack)) + { + buf = &ks->ack_write_buf; + ASSERT (buf_init (buf, FRAME_HEADROOM (&multi->opt.frame))); + write_control_auth (session, ks, buf, to_link_addr, P_ACK_V1, + RELIABLE_ACK_SIZE, false); + *to_link = *buf; + active = true; + state_change = true; + dmsg (D_TLS_DEBUG, "Dedicated ACK -> TCP/UDP"); + } +#endif + + /* When should we wake up again? */ + { + if (ks->state >= S_INITIAL) + { + compute_earliest_wakeup (wakeup, + reliable_send_timeout (ks->send_reliable)); + + if (ks->must_negotiate) + compute_earliest_wakeup (wakeup, ks->must_negotiate - now); + } + + if (ks->established && session->opt->renegotiate_seconds) + compute_earliest_wakeup (wakeup, + ks->established + session->opt->renegotiate_seconds - now); + + /* prevent event-loop spinning by setting minimum wakeup of 1 second */ + if (*wakeup <= 0) + { + *wakeup = 1; + + /* if we had something to send to remote, but to_link was busy, + let caller know we need to be called again soon */ + active = true; + } + + dmsg (D_TLS_DEBUG, "TLS: tls_process: timeout set to %d", *wakeup); + + gc_free (&gc); + return active; + } + +error: + ERR_clear_error (); + ks->state = S_ERROR; + msg (D_TLS_ERRORS, "TLS Error: TLS handshake failed"); + INCR_ERROR; + gc_free (&gc); + return false; +} + +#undef ks +#undef ks_lame + +/* + * Called by the top-level event loop. + * + * Basically decides if we should call tls_process for + * the active or untrusted sessions. + */ + +int +tls_multi_process (struct tls_multi *multi, + struct buffer *to_link, + struct link_socket_actual **to_link_addr, + struct link_socket_info *to_link_socket_info, + interval_t *wakeup) +{ + struct gc_arena gc = gc_new (); + int i; + int active = TLSMP_INACTIVE; + bool error = false; + int tas; + + perf_push (PERF_TLS_MULTI_PROCESS); + + ERR_clear_error (); + + /* + * Process each session object having state of S_INITIAL or greater, + * and which has a defined remote IP addr. + */ + + for (i = 0; i < TM_SIZE; ++i) + { + struct tls_session *session = &multi->session[i]; + struct key_state *ks = &session->key[KS_PRIMARY]; + struct key_state *ks_lame = &session->key[KS_LAME_DUCK]; + + /* set initial remote address */ + if (i == TM_ACTIVE && ks->state == S_INITIAL && + link_socket_actual_defined (&to_link_socket_info->lsa->actual)) + ks->remote_addr = to_link_socket_info->lsa->actual; + + dmsg (D_TLS_DEBUG, + "TLS: tls_multi_process: i=%d state=%s, mysid=%s, stored-sid=%s, stored-ip=%s", + i, + state_name (ks->state), + session_id_print (&session->session_id, &gc), + session_id_print (&ks->session_id_remote, &gc), + print_link_socket_actual (&ks->remote_addr, &gc)); + + if (ks->state >= S_INITIAL && link_socket_actual_defined (&ks->remote_addr)) + { + struct link_socket_actual *tla = NULL; + + update_time (); + + if (tls_process (multi, session, to_link, &tla, + to_link_socket_info, wakeup)) + active = TLSMP_ACTIVE; + + /* + * If tls_process produced an outgoing packet, + * return the link_socket_actual object (which + * contains the outgoing address). + */ + if (tla) + { + multi->to_link_addr = *tla; + *to_link_addr = &multi->to_link_addr; + } + + /* + * If tls_process hits an error: + * (1) If the session has an unexpired lame duck key, preserve it. + * (2) Reinitialize the session. + * (3) Increment soft error count + */ + if (ks->state == S_ERROR) + { + ++multi->n_soft_errors; + + if (i == TM_ACTIVE) + error = true; + + if (i == TM_ACTIVE + && ks_lame->state >= S_ACTIVE + && !multi->opt.single_session) + move_session (multi, TM_LAME_DUCK, TM_ACTIVE, true); + else + reset_session (multi, session); + } + } + } + + update_time (); + + tas = tls_authentication_status (multi, TLS_MULTI_AUTH_STATUS_INTERVAL); + + /* + * If lame duck session expires, kill it. + */ + if (lame_duck_must_die (&multi->session[TM_LAME_DUCK], wakeup)) { + tls_session_free (&multi->session[TM_LAME_DUCK], true); + msg (D_TLS_DEBUG_LOW, "TLS: tls_multi_process: killed expiring key"); + } + + /* + * If untrusted session achieves TLS authentication, + * move it to active session, usurping any prior session. + * + * A semi-trusted session is one in which the certificate authentication + * succeeded (if cert verification is enabled) but the username/password + * verification failed. A semi-trusted session can forward data on the + * TLS control channel but not on the tunnel channel. + */ + if (DECRYPT_KEY_ENABLED (multi, &multi->session[TM_UNTRUSTED].key[KS_PRIMARY])) { + move_session (multi, TM_ACTIVE, TM_UNTRUSTED, true); + msg (D_TLS_DEBUG_LOW, "TLS: tls_multi_process: untrusted session promoted to %strusted", + tas == TLS_AUTHENTICATION_SUCCEEDED ? "" : "semi-"); + } + + /* + * A hard error means that TM_ACTIVE hit an S_ERROR state and that no + * other key state objects are S_ACTIVE or higher. + */ + if (error) + { + for (i = 0; i < (int) SIZE (multi->key_scan); ++i) + { + if (multi->key_scan[i]->state >= S_ACTIVE) + goto nohard; + } + ++multi->n_hard_errors; + } + nohard: + +#ifdef ENABLE_DEBUG + /* DEBUGGING -- flood peer with repeating connection attempts */ + { + const int throw_level = GREMLIN_CONNECTION_FLOOD_LEVEL (multi->opt.gremlin); + if (throw_level) + { + for (i = 0; i < (int) SIZE (multi->key_scan); ++i) + { + if (multi->key_scan[i]->state >= throw_level) + { + ++multi->n_hard_errors; + ++multi->n_soft_errors; + } + } + } + } +#endif + + perf_pop (); + gc_free (&gc); + + return (tas == TLS_AUTHENTICATION_FAILED) ? TLSMP_KILL : active; +} + +/* + * Pre and post-process the encryption & decryption buffers in order + * to implement a multiplexed TLS channel over the TCP/UDP port. + */ + +/* + * + * When we are in TLS mode, this is the first routine which sees + * an incoming packet. + * + * If it's a data packet, we set opt so that our caller can + * decrypt it. We also give our caller the appropriate decryption key. + * + * If it's a control packet, we authenticate it and process it, + * possibly creating a new tls_session if it represents the + * first packet of a new session. For control packets, we will + * also zero the size of *buf so that our caller ignores the + * packet on our return. + * + * Note that openvpn only allows one active session at a time, + * so a new session (once authenticated) will always usurp + * an old session. + * + * Return true if input was an authenticated control channel + * packet. + * + * If we are running in TLS thread mode, all public routines + * below this point must be called with the L_TLS lock held. + */ + +bool +tls_pre_decrypt (struct tls_multi *multi, + const struct link_socket_actual *from, + struct buffer *buf, + struct crypto_options *opt) +{ + struct gc_arena gc = gc_new (); + bool ret = false; + + if (buf->len > 0) + { + int i; + int op; + int key_id; + + /* get opcode and key ID */ + { + uint8_t c = *BPTR (buf); + op = c >> P_OPCODE_SHIFT; + key_id = c & P_KEY_ID_MASK; + } + + if (op == P_DATA_V1) + { /* data channel packet */ + for (i = 0; i < KEY_SCAN_SIZE; ++i) + { + struct key_state *ks = multi->key_scan[i]; + + /* + * This is the basic test of TLS state compatibility between a local OpenVPN + * instance and its remote peer. + * + * If the test fails, it tells us that we are getting a packet from a source + * which claims reference to a prior negotiated TLS session, but the local + * OpenVPN instance has no memory of such a negotiation. + * + * It almost always occurs on UDP sessions when the passive side of the + * connection is restarted without the active side restarting as well (the + * passive side is the server which only listens for the connections, the + * active side is the client which initiates connections). + */ + if (DECRYPT_KEY_ENABLED (multi, ks) + && key_id == ks->key_id + && ks->authenticated +#ifdef ENABLE_DEF_AUTH + && !ks->auth_deferred +#endif + && link_socket_actual_match (from, &ks->remote_addr)) + { + /* return appropriate data channel decrypt key in opt */ + opt->key_ctx_bi = &ks->key; + opt->packet_id = multi->opt.replay ? &ks->packet_id : NULL; + opt->pid_persist = NULL; + opt->flags &= multi->opt.crypto_flags_and; + opt->flags |= multi->opt.crypto_flags_or; + ASSERT (buf_advance (buf, 1)); + ++ks->n_packets; + ks->n_bytes += buf->len; + dmsg (D_TLS_KEYSELECT, + "TLS: tls_pre_decrypt, key_id=%d, IP=%s", + key_id, print_link_socket_actual (from, &gc)); + gc_free (&gc); + return ret; + } +#if 0 /* keys out of sync? */ + else + { + dmsg (D_TLS_ERRORS, "TLS_PRE_DECRYPT: [%d] dken=%d rkid=%d lkid=%d auth=%d def=%d match=%d", + i, + DECRYPT_KEY_ENABLED (multi, ks), + key_id, + ks->key_id, + ks->authenticated, +#ifdef ENABLE_DEF_AUTH + ks->auth_deferred, +#else + -1, +#endif + link_socket_actual_match (from, &ks->remote_addr)); + } +#endif + } + + msg (D_TLS_ERRORS, + "TLS Error: local/remote TLS keys are out of sync: %s [%d]", + print_link_socket_actual (from, &gc), key_id); + goto error_lite; + } + else /* control channel packet */ + { + bool do_burst = false; + bool new_link = false; + struct session_id sid; /* remote session ID */ + + /* verify legal opcode */ + if (op < P_FIRST_OPCODE || op > P_LAST_OPCODE) + { + msg (D_TLS_ERRORS, + "TLS Error: unknown opcode received from %s op=%d", + print_link_socket_actual (from, &gc), op); + goto error; + } + + /* hard reset ? */ + if (is_hard_reset (op, 0)) + { + /* verify client -> server or server -> client connection */ + if (((op == P_CONTROL_HARD_RESET_CLIENT_V1 + || op == P_CONTROL_HARD_RESET_CLIENT_V2) && !multi->opt.server) + || ((op == P_CONTROL_HARD_RESET_SERVER_V1 + || op == P_CONTROL_HARD_RESET_SERVER_V2) && multi->opt.server)) + { + msg (D_TLS_ERRORS, + "TLS Error: client->client or server->server connection attempted from %s", + print_link_socket_actual (from, &gc)); + goto error; + } + } + + /* + * Authenticate Packet + */ + dmsg (D_TLS_DEBUG, "TLS: control channel, op=%s, IP=%s", + packet_opcode_name (op), print_link_socket_actual (from, &gc)); + + /* get remote session-id */ + { + struct buffer tmp = *buf; + buf_advance (&tmp, 1); + if (!session_id_read (&sid, &tmp) || !session_id_defined (&sid)) + { + msg (D_TLS_ERRORS, + "TLS Error: session-id not found in packet from %s", + print_link_socket_actual (from, &gc)); + goto error; + } + } + + /* use session ID to match up packet with appropriate tls_session object */ + for (i = 0; i < TM_SIZE; ++i) + { + struct tls_session *session = &multi->session[i]; + struct key_state *ks = &session->key[KS_PRIMARY]; + + dmsg (D_TLS_DEBUG, + "TLS: initial packet test, i=%d state=%s, mysid=%s, rec-sid=%s, rec-ip=%s, stored-sid=%s, stored-ip=%s", + i, + state_name (ks->state), + session_id_print (&session->session_id, &gc), + session_id_print (&sid, &gc), + print_link_socket_actual (from, &gc), + session_id_print (&ks->session_id_remote, &gc), + print_link_socket_actual (&ks->remote_addr, &gc)); + + if (session_id_equal (&ks->session_id_remote, &sid)) + /* found a match */ + { + if (i == TM_LAME_DUCK) { + msg (D_TLS_ERRORS, + "TLS ERROR: received control packet with stale session-id=%s", + session_id_print (&sid, &gc)); + goto error; + } + dmsg (D_TLS_DEBUG, + "TLS: found match, session[%d], sid=%s", + i, session_id_print (&sid, &gc)); + break; + } + } + + /* + * Initial packet received. + */ + + if (i == TM_SIZE && is_hard_reset (op, 0)) + { + struct tls_session *session = &multi->session[TM_ACTIVE]; + struct key_state *ks = &session->key[KS_PRIMARY]; + + if (!is_hard_reset (op, multi->opt.key_method)) + { + msg (D_TLS_ERRORS, "TLS ERROR: initial packet local/remote key_method mismatch, local key_method=%d, op=%s", + multi->opt.key_method, + packet_opcode_name (op)); + goto error; + } + + /* + * If we have no session currently in progress, the initial packet will + * open a new session in TM_ACTIVE rather than TM_UNTRUSTED. + */ + if (!session_id_defined (&ks->session_id_remote)) + { + if (multi->opt.single_session && multi->n_sessions) + { + msg (D_TLS_ERRORS, + "TLS Error: Cannot accept new session request from %s due to session context expire or --single-session [1]", + print_link_socket_actual (from, &gc)); + goto error; + } + +#ifdef ENABLE_MANAGEMENT + if (management) + { + management_set_state (management, + OPENVPN_STATE_AUTH, + NULL, + 0, + 0); + } +#endif + + msg (D_TLS_DEBUG_LOW, + "TLS: Initial packet from %s, sid=%s", + print_link_socket_actual (from, &gc), + session_id_print (&sid, &gc)); + + do_burst = true; + new_link = true; + i = TM_ACTIVE; + session->untrusted_addr = *from; + } + } + + if (i == TM_SIZE && is_hard_reset (op, 0)) + { + /* + * No match with existing sessions, + * probably a new session. + */ + struct tls_session *session = &multi->session[TM_UNTRUSTED]; + + /* + * If --single-session, don't allow any hard-reset connection request + * unless it the the first packet of the session. + */ + if (multi->opt.single_session) + { + msg (D_TLS_ERRORS, + "TLS Error: Cannot accept new session request from %s due to session context expire or --single-session [2]", + print_link_socket_actual (from, &gc)); + goto error; + } + + if (!is_hard_reset (op, multi->opt.key_method)) + { + msg (D_TLS_ERRORS, "TLS ERROR: new session local/remote key_method mismatch, local key_method=%d, op=%s", + multi->opt.key_method, + packet_opcode_name (op)); + goto error; + } + + if (!read_control_auth (buf, &session->tls_auth, from)) + goto error; + + /* + * New session-initiating control packet is authenticated at this point, + * assuming that the --tls-auth command line option was used. + * + * Without --tls-auth, we leave authentication entirely up to TLS. + */ + msg (D_TLS_DEBUG_LOW, + "TLS: new session incoming connection from %s", + print_link_socket_actual (from, &gc)); + + new_link = true; + i = TM_UNTRUSTED; + session->untrusted_addr = *from; + } + else + { + struct tls_session *session = &multi->session[i]; + struct key_state *ks = &session->key[KS_PRIMARY]; + + /* + * Packet must belong to an existing session. + */ + if (i != TM_ACTIVE && i != TM_UNTRUSTED) + { + msg (D_TLS_ERRORS, + "TLS Error: Unroutable control packet received from %s (si=%d op=%s)", + print_link_socket_actual (from, &gc), + i, + packet_opcode_name (op)); + goto error; + } + + /* + * Verify remote IP address + */ + if (!new_link && !link_socket_actual_match (&ks->remote_addr, from)) + { + msg (D_TLS_ERRORS, "TLS Error: Received control packet from unexpected IP addr: %s", + print_link_socket_actual (from, &gc)); + goto error; + } + + /* + * Remote is requesting a key renegotiation + */ + if (op == P_CONTROL_SOFT_RESET_V1 + && DECRYPT_KEY_ENABLED (multi, ks)) + { + if (!read_control_auth (buf, &session->tls_auth, from)) + goto error; + + key_state_soft_reset (session); + + dmsg (D_TLS_DEBUG, + "TLS: received P_CONTROL_SOFT_RESET_V1 s=%d sid=%s", + i, session_id_print (&sid, &gc)); + } + else + { + /* + * Remote responding to our key renegotiation request? + */ + if (op == P_CONTROL_SOFT_RESET_V1) + do_burst = true; + + if (!read_control_auth (buf, &session->tls_auth, from)) + goto error; + + dmsg (D_TLS_DEBUG, + "TLS: received control channel packet s#=%d sid=%s", + i, session_id_print (&sid, &gc)); + } + } + + /* + * We have an authenticated packet (if --tls-auth was set). + * Now pass to our reliability level which deals with + * packet acknowledgements, retransmits, sequencing, etc. + */ + { + struct tls_session *session = &multi->session[i]; + struct key_state *ks = &session->key[KS_PRIMARY]; + + /* Make sure we were initialized and that we're not in an error state */ + ASSERT (ks->state != S_UNDEF); + ASSERT (ks->state != S_ERROR); + ASSERT (session_id_defined (&session->session_id)); + + /* Let our caller know we processed a control channel packet */ + ret = true; + + /* + * Set our remote address and remote session_id + */ + if (new_link) + { + ks->session_id_remote = sid; + ks->remote_addr = *from; + ++multi->n_sessions; + } + else if (!link_socket_actual_match (&ks->remote_addr, from)) + { + msg (D_TLS_ERRORS, + "TLS Error: Existing session control channel packet from unknown IP address: %s", + print_link_socket_actual (from, &gc)); + goto error; + } + + /* + * Should we do a retransmit of all unacknowledged packets in + * the send buffer? This improves the start-up efficiency of the + * initial key negotiation after the 2nd peer comes online. + */ + if (do_burst && !session->burst) + { + reliable_schedule_now (ks->send_reliable); + session->burst = true; + } + + /* Check key_id */ + if (ks->key_id != key_id) + { + msg (D_TLS_ERRORS, + "TLS ERROR: local/remote key IDs out of sync (%d/%d) ID: %s", + ks->key_id, key_id, print_key_id (multi, &gc)); + goto error; + } + + /* + * Process incoming ACKs for packets we can now + * delete from reliable send buffer + */ + { + /* buffers all packet IDs to delete from send_reliable */ + struct reliable_ack send_ack; + + send_ack.len = 0; + if (!reliable_ack_read (&send_ack, buf, &session->session_id)) + { + msg (D_TLS_ERRORS, + "TLS Error: reading acknowledgement record from packet"); + goto error; + } + reliable_send_purge (ks->send_reliable, &send_ack); + } + + if (op != P_ACK_V1 && reliable_can_get (ks->rec_reliable)) + { + packet_id_type id; + + /* Extract the packet ID from the packet */ + if (reliable_ack_read_packet_id (buf, &id)) + { + /* Avoid deadlock by rejecting packet that would de-sequentialize receive buffer */ + if (reliable_wont_break_sequentiality (ks->rec_reliable, id)) + { + if (reliable_not_replay (ks->rec_reliable, id)) + { + /* Save incoming ciphertext packet to reliable buffer */ + struct buffer *in = reliable_get_buf (ks->rec_reliable); + ASSERT (in); + ASSERT (buf_copy (in, buf)); + reliable_mark_active_incoming (ks->rec_reliable, in, id, op); + } + + /* Process outgoing acknowledgment for packet just received, even if it's a replay */ + reliable_ack_acknowledge_packet_id (ks->rec_ack, id); + } + } + } + } + } + } + + done: + buf->len = 0; + opt->key_ctx_bi = NULL; + opt->packet_id = NULL; + opt->pid_persist = NULL; + opt->flags &= multi->opt.crypto_flags_and; + gc_free (&gc); + return ret; + + error: + ++multi->n_soft_errors; + error_lite: + ERR_clear_error (); + goto done; +} + +/* + * This function is similar to tls_pre_decrypt, except it is called + * when we are in server mode and receive an initial incoming + * packet. Note that we don't modify + * any state in our parameter objects. The purpose is solely to + * determine whether we should generate a client instance + * object, in which case true is returned. + * + * This function is essentially the first-line HMAC firewall + * on the UDP port listener in --mode server mode. + */ +bool +tls_pre_decrypt_lite (const struct tls_auth_standalone *tas, + const struct link_socket_actual *from, + const struct buffer *buf) + +{ + struct gc_arena gc = gc_new (); + bool ret = false; + + if (buf->len > 0) + { + int op; + int key_id; + + /* get opcode and key ID */ + { + uint8_t c = *BPTR (buf); + op = c >> P_OPCODE_SHIFT; + key_id = c & P_KEY_ID_MASK; + } + + /* this packet is from an as-yet untrusted source, so + scrutinize carefully */ + + if (op != P_CONTROL_HARD_RESET_CLIENT_V2) + { + /* + * This can occur due to bogus data or DoS packets. + */ + dmsg (D_TLS_STATE_ERRORS, + "TLS State Error: No TLS state for client %s, opcode=%d", + print_link_socket_actual (from, &gc), + op); + goto error; + } + + if (key_id != 0) + { + dmsg (D_TLS_STATE_ERRORS, + "TLS State Error: Unknown key ID (%d) received from %s -- 0 was expected", + key_id, + print_link_socket_actual (from, &gc)); + goto error; + } + + if (buf->len > EXPANDED_SIZE_DYNAMIC (&tas->frame)) + { + dmsg (D_TLS_STATE_ERRORS, + "TLS State Error: Large packet (size %d) received from %s -- a packet no larger than %d bytes was expected", + buf->len, + print_link_socket_actual (from, &gc), + EXPANDED_SIZE_DYNAMIC (&tas->frame)); + goto error; + } + + { + struct buffer newbuf = clone_buf (buf); + struct crypto_options co = tas->tls_auth_options; + bool status; + + /* + * We are in read-only mode at this point with respect to TLS + * control channel state. After we build a new client instance + * object, we will process this session-initiating packet for real. + */ + co.flags |= CO_IGNORE_PACKET_ID; + + /* HMAC test, if --tls-auth was specified */ + status = read_control_auth (&newbuf, &co, from); + free_buf (&newbuf); + if (!status) + goto error; + + /* + * At this point, if --tls-auth is being used, we know that + * the packet has passed the HMAC test, but we don't know if + * it is a replay yet. We will attempt to defeat replays + * by not advancing to the S_START state until we + * receive an ACK from our first reply to the client + * that includes an HMAC of our randomly generated 64 bit + * session ID. + * + * On the other hand if --tls-auth is not being used, we + * will proceed to begin the TLS authentication + * handshake with only cursory integrity checks having + * been performed, since we will be leaving the task + * of authentication solely up to TLS. + */ + + ret = true; + } + } + gc_free (&gc); + return ret; + + error: + ERR_clear_error (); + gc_free (&gc); + return ret; +} + +/* Choose the key with which to encrypt a data packet */ +void +tls_pre_encrypt (struct tls_multi *multi, + struct buffer *buf, struct crypto_options *opt) +{ + multi->save_ks = NULL; + if (buf->len > 0) + { + int i; + struct key_state *ks_select = NULL; + for (i = 0; i < KEY_SCAN_SIZE; ++i) + { + struct key_state *ks = multi->key_scan[i]; + if (ks->state >= S_ACTIVE + && ks->authenticated +#ifdef ENABLE_DEF_AUTH + && !ks->auth_deferred +#endif + ) + { + if (!ks_select) + ks_select = ks; + if (now >= ks->auth_deferred_expire) + { + ks_select = ks; + break; + } + } + } + + if (ks_select) + { + opt->key_ctx_bi = &ks_select->key; + opt->packet_id = multi->opt.replay ? &ks_select->packet_id : NULL; + opt->pid_persist = NULL; + opt->flags &= multi->opt.crypto_flags_and; + opt->flags |= multi->opt.crypto_flags_or; + multi->save_ks = ks_select; + dmsg (D_TLS_KEYSELECT, "TLS: tls_pre_encrypt: key_id=%d", ks_select->key_id); + return; + } + else + { + struct gc_arena gc = gc_new (); + dmsg (D_TLS_KEYSELECT, "TLS Warning: no data channel send key available: %s", + print_key_id (multi, &gc)); + gc_free (&gc); + } + } + + buf->len = 0; + opt->key_ctx_bi = NULL; + opt->packet_id = NULL; + opt->pid_persist = NULL; + opt->flags &= multi->opt.crypto_flags_and; +} + +/* Prepend the appropriate opcode to encrypted buffer prior to TCP/UDP send */ +void +tls_post_encrypt (struct tls_multi *multi, struct buffer *buf) +{ + struct key_state *ks; + uint8_t *op; + + ks = multi->save_ks; + multi->save_ks = NULL; + if (buf->len > 0) + { + ASSERT (ks); + ASSERT (op = buf_prepend (buf, 1)); + *op = (P_DATA_V1 << P_OPCODE_SHIFT) | ks->key_id; + ++ks->n_packets; + ks->n_bytes += buf->len; + } +} + +/* + * Send a payload over the TLS control channel. + * Called externally. + */ + +bool +tls_send_payload (struct tls_multi *multi, + const uint8_t *data, + int size) +{ + struct tls_session *session; + struct key_state *ks; + bool ret = false; + + ERR_clear_error (); + + ASSERT (multi); + + session = &multi->session[TM_ACTIVE]; + ks = &session->key[KS_PRIMARY]; + + if (ks->state >= S_ACTIVE) + { + if (key_state_write_plaintext_const (multi, ks, data, size) == 1) + ret = true; + } + else + { + if (!ks->paybuf) + ks->paybuf = buffer_list_new (0); + buffer_list_push_data (ks->paybuf, data, (size_t)size); + ret = true; + } + + ERR_clear_error (); + + return ret; +} + +bool +tls_rec_payload (struct tls_multi *multi, + struct buffer *buf) +{ + struct tls_session *session; + struct key_state *ks; + bool ret = false; + + ERR_clear_error (); + + ASSERT (multi); + + session = &multi->session[TM_ACTIVE]; + ks = &session->key[KS_PRIMARY]; + + if (ks->state >= S_ACTIVE && BLEN (&ks->plaintext_read_buf)) + { + if (buf_copy (buf, &ks->plaintext_read_buf)) + ret = true; + ks->plaintext_read_buf.len = 0; + } + + ERR_clear_error (); + + return ret; +} + +/* + * Dump a human-readable rendition of an openvpn packet + * into a garbage collectable string which is returned. + */ +const char * +protocol_dump (struct buffer *buffer, unsigned int flags, struct gc_arena *gc) +{ + struct buffer out = alloc_buf_gc (256, gc); + struct buffer buf = *buffer; + + uint8_t c; + int op; + int key_id; + + int tls_auth_hmac_size = (flags & PD_TLS_AUTH_HMAC_SIZE_MASK); + + if (buf.len <= 0) + { + buf_printf (&out, "DATA UNDEF len=%d", buf.len); + goto done; + } + + if (!(flags & PD_TLS)) + goto print_data; + + /* + * Initial byte (opcode) + */ + if (!buf_read (&buf, &c, sizeof (c))) + goto done; + op = (c >> P_OPCODE_SHIFT); + key_id = c & P_KEY_ID_MASK; + buf_printf (&out, "%s kid=%d", packet_opcode_name (op), key_id); + + if (op == P_DATA_V1) + goto print_data; + + /* + * Session ID + */ + { + struct session_id sid; + + if (!session_id_read (&sid, &buf)) + goto done; + if (flags & PD_VERBOSE) + buf_printf (&out, " sid=%s", session_id_print (&sid, gc)); + } + + /* + * tls-auth hmac + packet_id + */ + if (tls_auth_hmac_size) + { + struct packet_id_net pin; + uint8_t tls_auth_hmac[MAX_HMAC_KEY_LENGTH]; + + ASSERT (tls_auth_hmac_size <= MAX_HMAC_KEY_LENGTH); + + if (!buf_read (&buf, tls_auth_hmac, tls_auth_hmac_size)) + goto done; + if (flags & PD_VERBOSE) + buf_printf (&out, " tls_hmac=%s", format_hex (tls_auth_hmac, tls_auth_hmac_size, 0, gc)); + + if (!packet_id_read (&pin, &buf, true)) + goto done; + buf_printf(&out, " pid=%s", packet_id_net_print (&pin, (flags & PD_VERBOSE), gc)); + } + + /* + * ACK list + */ + buf_printf (&out, " %s", reliable_ack_print(&buf, (flags & PD_VERBOSE), gc)); + + if (op == P_ACK_V1) + goto done; + + /* + * Packet ID + */ + { + packet_id_type l; + if (!buf_read (&buf, &l, sizeof (l))) + goto done; + l = ntohpid (l); + buf_printf (&out, " pid=" packet_id_format, (packet_id_print_type)l); + } + +print_data: + if (flags & PD_SHOW_DATA) + buf_printf (&out, " DATA %s", format_hex (BPTR (&buf), BLEN (&buf), 80, gc)); + else + buf_printf (&out, " DATA len=%d", buf.len); + +done: + return BSTR (&out); +} + +#else +static void dummy(void) {} +#endif /* USE_CRYPTO && USE_SSL*/ |