Allow for dynamically expanded PSK.
[freeradius.git] / src / main / tls.c
index aa8b10f..50b1edc 100644 (file)
@@ -40,6 +40,7 @@ USES_APPLE_DEPRECATED_API     /* OpenSSL API has been deprecated by Apple */
 #ifdef HAVE_UTIME_H
 #include <utime.h>
 #endif
+#include <ctype.h>
 
 #ifdef WITH_TLS
 #ifdef HAVE_OPENSSL_RAND_H
@@ -50,6 +51,27 @@ USES_APPLE_DEPRECATED_API    /* OpenSSL API has been deprecated by Apple */
 #include <openssl/ocsp.h>
 #endif
 
+typedef struct libssl_defect {
+       uint64_t        high;
+       uint64_t        low;
+
+       char const      *id;
+       char const      *name;
+       char const      *comment;
+} libssl_defect_t;
+
+/* Record critical defects in libssl here (newest first)*/
+static libssl_defect_t libssl_defects[] =
+{
+       {
+               .low            = 0x010001000,          /* 1.0.1  */
+               .high           = 0x01000106f,          /* 1.0.1f */
+               .id             = "CVE-2014-0160",
+               .name           = "Heartbleed",
+               .comment        = "For more information see http://heartbleed.com"
+       }
+};
+
 /* record */
 static void            record_init(record_t *buf);
 static void            record_close(record_t *buf);
@@ -59,28 +81,99 @@ static unsigned int         record_minus(record_t *buf, void *ptr,
                                     unsigned int size);
 
 #ifdef PSK_MAX_IDENTITY_LEN
-static unsigned int psk_server_callback(SSL *ssl, char const *identity,
+static bool identity_is_safe(const char *identity)
+{
+       char c;
+
+       if (!identity) return true;
+
+       while ((c = *(identity++)) != '\0') {
+               if (isalpha((int) c) || isdigit((int) c) || isspace((int) c) ||
+                   (c == '@') || (c == '-') || (c == '_') || (c == '.')) {
+                       continue;
+               }
+
+               return false;
+       }
+
+       return true;
+}
+
+
+/*
+ *     When a client uses TLS-PSK to talk to a server, this callback
+ *     is used by the server to determine the PSK to use.
+ */
+static unsigned int psk_server_callback(SSL *ssl, const char *identity,
                                        unsigned char *psk,
                                        unsigned int max_psk_len)
 {
-       unsigned int psk_len;
+       unsigned int psk_len = 0;
        fr_tls_server_conf_t *conf;
+       REQUEST *request;
 
        conf = (fr_tls_server_conf_t *)SSL_get_ex_data(ssl,
                                                       FR_TLS_EX_INDEX_CONF);
        if (!conf) return 0;
 
+       request = (REQUEST *)SSL_get_ex_data(ssl,
+                                            FR_TLS_EX_INDEX_REQUEST);
+       if (request && conf->psk_query) {
+               size_t hex_len;
+               VALUE_PAIR *vp;
+               char buffer[2 * PSK_MAX_PSK_LEN + 4]; /* allow for too-long keys */
+
+               /*
+                *      The passed identity is weird.  Deny it.
+                */
+               if (!identity_is_safe(identity)) {
+                       RWDEBUG("Invalid characters in PSK identity %s", identity);
+                       return 0;
+               }
+
+               vp = pairmake_packet("TLS-PSK-Identity", identity, T_OP_SET);
+               if (!vp) return 0;
+
+               hex_len = radius_xlat(buffer, sizeof(buffer), request, conf->psk_query,
+                                     NULL, NULL);
+               if (!hex_len) {
+                       RWDEBUG("PSK expansion returned an empty string.");
+                       return 0;
+               }
+
+               /*
+                *      The returned key is truncated at MORE than
+                *      OpenSSL can handle.  That way we can detect
+                *      the truncation, and complain about it.
+                */
+               if (hex_len > (2 * max_psk_len)) {
+                       RWDEBUG("Returned PSK is too long (%u > %u)",
+                               (unsigned int) hex_len, 2 * max_psk_len);
+                       return 0;
+               }
+
+               /*
+                *      Leave the TLS-PSK-Identity in the request, and
+                *      convert the expansion from printable string
+                *      back to hex.
+                */
+               return fr_hex2bin(psk, max_psk_len, buffer, hex_len);
+       }
+
        /*
-        *      FIXME: Look up the PSK password based on the identity!
+        *      No REQUEST, or no dynamic query.  Just look for a
+        *      static identity.
         */
        if (strcmp(identity, conf->psk_identity) != 0) {
+               ERROR("Supplied PSK identity %s does not match configuration.  Rejecting.",
+                     identity);
                return 0;
        }
 
        psk_len = strlen(conf->psk_password);
        if (psk_len > (2 * max_psk_len)) return 0;
 
-       return fr_hex2bin(psk, conf->psk_password, psk_len);
+       return fr_hex2bin(psk, max_psk_len, conf->psk_password, psk_len);
 }
 
 static unsigned int psk_client_callback(SSL *ssl, UNUSED char const *hint,
@@ -99,7 +192,7 @@ static unsigned int psk_client_callback(SSL *ssl, UNUSED char const *hint,
 
        strlcpy(identity, conf->psk_identity, max_identity_len);
 
-       return fr_hex2bin(psk, conf->psk_password, psk_len);
+       return fr_hex2bin(psk, max_psk_len, conf->psk_password, psk_len);
 }
 
 #endif
@@ -108,6 +201,7 @@ tls_session_t *tls_new_client_session(fr_tls_server_conf_t *conf, int fd)
 {
        int verify_mode;
        tls_session_t *ssn = NULL;
+       REQUEST *request;
 
        ssn = talloc_zero(conf, tls_session_t);
        if (!ssn) return NULL;
@@ -117,7 +211,13 @@ tls_session_t *tls_new_client_session(fr_tls_server_conf_t *conf, int fd)
        SSL_CTX_set_mode(ssn->ctx, SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER | SSL_MODE_AUTO_RETRY);
 
        ssn->ssl = SSL_new(ssn->ctx);
-       rad_assert(ssn->ssl != NULL);
+       if (!ssn->ssl) {
+               talloc_free(ssn);
+               return NULL;
+       }
+
+       request = request_alloc(ssn);
+       SSL_set_ex_data(ssn->ssl, FR_TLS_EX_INDEX_REQUEST, (void *)request);
 
        /*
         *      Add the message callback to identify what type of
@@ -141,11 +241,11 @@ tls_session_t *tls_new_client_session(fr_tls_server_conf_t *conf, int fd)
        if (SSL_connect(ssn->ssl) <= 0) {
                int err;
                while ((err = ERR_get_error())) {
-                       DEBUG("OpenSSL Err says %s",
-                             ERR_error_string(err, NULL));
+                       ERROR("tls: %s", ERR_error_string(err, NULL));
                }
                SSL_free(ssn->ssl);
                talloc_free(ssn);
+
                return NULL;
        }
 
@@ -154,8 +254,22 @@ tls_session_t *tls_new_client_session(fr_tls_server_conf_t *conf, int fd)
        return ssn;
 }
 
-tls_session_t *tls_new_session(fr_tls_server_conf_t *conf, REQUEST *request,
-                              int client_cert)
+static int _tls_session_free(tls_session_t *ssn)
+{
+       /*
+        *      Free any opaque TTLS or PEAP data.
+        */
+       if ((ssn->opaque) && (ssn->free_opaque)) {
+               ssn->free_opaque(ssn->opaque);
+               ssn->opaque = NULL;
+       }
+
+       session_close(ssn);
+
+       return 0;
+}
+
+tls_session_t *tls_new_session(TALLOC_CTX *ctx, fr_tls_server_conf_t *conf, REQUEST *request, bool client_cert)
 {
        tls_session_t *state = NULL;
        SSL *new_tls = NULL;
@@ -172,7 +286,7 @@ tls_session_t *tls_new_session(fr_tls_server_conf_t *conf, REQUEST *request,
         *      FIXME: Also do it every N sessions?
         */
        if (conf->session_cache_enable &&
-           ((conf->session_last_flushed + (conf->session_timeout * 1800)) <= request->timestamp)){
+           ((conf->session_last_flushed + ((int)conf->session_timeout * 1800)) <= request->timestamp)){
                RDEBUG2("Flushing SSL sessions (of #%ld)",
                        SSL_CTX_sess_number(conf->ctx));
 
@@ -189,8 +303,9 @@ tls_session_t *tls_new_session(fr_tls_server_conf_t *conf, REQUEST *request,
        /* We use the SSL's "app_data" to indicate a call-back */
        SSL_set_app_data(new_tls, NULL);
 
-       state = talloc_zero(conf, tls_session_t);
+       state = talloc_zero(ctx, tls_session_t);
        session_init(state);
+       talloc_set_destructor(state, _tls_session_free);
 
        state->ctx = conf->ctx;
        state->ssl = new_tls;
@@ -353,6 +468,8 @@ int tls_handshake_recv(REQUEST *request, tls_session_t *ssn)
 {
        int err;
 
+       if (ssn->invalid_hb_used) return 0;
+
        err = BIO_write(ssn->into_ssl, ssn->dirty_in.data, ssn->dirty_in.used);
        if (err != (int) ssn->dirty_in.used) {
                RDEBUG("Failed writing %d to SSL BIO: %d", ssn->dirty_in.used,
@@ -377,16 +494,16 @@ int tls_handshake_recv(REQUEST *request, tls_session_t *ssn)
        if (SSL_is_init_finished(ssn->ssl)) {
                DEBUG2("SSL Connection Established\n");
        }
-       if (SSL_in_init(ssn->ssl)) {
+       if (SSL_in_init(ssn->ssl)) {
                DEBUG2("In SSL Handshake Phase\n");
        }
-       if (SSL_in_before(ssn->ssl)) {
+       if (SSL_in_before(ssn->ssl)) {
                DEBUG2("Before SSL Handshake Phase\n");
        }
-       if (SSL_in_accept_init(ssn->ssl)) {
+       if (SSL_in_accept_init(ssn->ssl)) {
                DEBUG2("In SSL Accept mode \n");
        }
-       if (SSL_in_connect_init(ssn->ssl)) {
+       if (SSL_in_connect_init(ssn->ssl)) {
                DEBUG2("In SSL Connect mode \n");
        }
 
@@ -490,25 +607,6 @@ void session_close(tls_session_t *ssn)
        session_init(ssn);
 }
 
-void session_free(void *ssn)
-{
-       tls_session_t *sess = (tls_session_t *)ssn;
-
-       if (!ssn) return;
-
-       /*
-        *      Free any opaque TTLS or PEAP data.
-        */
-       if ((sess->opaque) && (sess->free_opaque)) {
-               sess->free_opaque(sess->opaque);
-               sess->opaque = NULL;
-       }
-
-       session_close(sess);
-
-       talloc_free(sess);
-}
-
 static void record_init(record_t *rec)
 {
        rec->used = 0;
@@ -749,117 +847,82 @@ void tls_session_information(tls_session_t *tls_session)
                 str_details1, str_details2);
 
        request = SSL_get_ex_data(tls_session->ssl, FR_TLS_EX_INDEX_REQUEST);
-
-       RDEBUG2("%s\n", tls_session->info.info_description);
+       if (request) {
+               RDEBUG2("%s", tls_session->info.info_description);
+       } else {
+               DEBUG2("%s", tls_session->info.info_description);
+       }
 }
 
 static CONF_PARSER cache_config[] = {
-       { "enable", PW_TYPE_BOOLEAN,
-         offsetof(fr_tls_server_conf_t, session_cache_enable), NULL, "no" },
-       { "lifetime", PW_TYPE_INTEGER,
-         offsetof(fr_tls_server_conf_t, session_timeout), NULL, "24" },
-       { "max_entries", PW_TYPE_INTEGER,
-         offsetof(fr_tls_server_conf_t, session_cache_size), NULL, "255" },
-       { "name", PW_TYPE_STRING_PTR,
-         offsetof(fr_tls_server_conf_t, session_id_name), NULL, NULL},
-       { "persist_dir", PW_TYPE_STRING_PTR,
-         offsetof(fr_tls_server_conf_t, session_cache_path), NULL, NULL},
+       { "enable", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, fr_tls_server_conf_t, session_cache_enable), "no" },
+       { "lifetime", FR_CONF_OFFSET(PW_TYPE_INTEGER, fr_tls_server_conf_t, session_timeout), "24" },
+       { "max_entries", FR_CONF_OFFSET(PW_TYPE_INTEGER, fr_tls_server_conf_t, session_cache_size), "255" },
+       { "name", FR_CONF_OFFSET(PW_TYPE_STRING, fr_tls_server_conf_t, session_id_name), NULL },
+       { "persist_dir", FR_CONF_OFFSET(PW_TYPE_STRING, fr_tls_server_conf_t, session_cache_path), NULL },
        { NULL, -1, 0, NULL, NULL }        /* end the list */
 };
 
 static CONF_PARSER verify_config[] = {
-       { "tmpdir", PW_TYPE_STRING_PTR,
-         offsetof(fr_tls_server_conf_t, verify_tmp_dir), NULL, NULL},
-       { "client", PW_TYPE_STRING_PTR,
-         offsetof(fr_tls_server_conf_t, verify_client_cert_cmd), NULL, NULL},
+       { "tmpdir", FR_CONF_OFFSET(PW_TYPE_STRING, fr_tls_server_conf_t, verify_tmp_dir), NULL },
+       { "client", FR_CONF_OFFSET(PW_TYPE_STRING, fr_tls_server_conf_t, verify_client_cert_cmd), NULL },
        { NULL, -1, 0, NULL, NULL }        /* end the list */
 };
 
 #ifdef HAVE_OPENSSL_OCSP_H
 static CONF_PARSER ocsp_config[] = {
-       { "enable", PW_TYPE_BOOLEAN,
-         offsetof(fr_tls_server_conf_t, ocsp_enable), NULL, "no"},
-       { "override_cert_url", PW_TYPE_BOOLEAN,
-         offsetof(fr_tls_server_conf_t, ocsp_override_url), NULL, "no"},
-       { "url", PW_TYPE_STRING_PTR,
-         offsetof(fr_tls_server_conf_t, ocsp_url), NULL, NULL },
-       { "use_nonce", PW_TYPE_BOOLEAN,
-         offsetof(fr_tls_server_conf_t, ocsp_use_nonce), NULL, "yes"},
-       { "timeout", PW_TYPE_INTEGER,
-         offsetof(fr_tls_server_conf_t, ocsp_timeout), NULL, "yes"},
-       { "softfail", PW_TYPE_BOOLEAN,
-         offsetof(fr_tls_server_conf_t, ocsp_softfail), NULL, "yes"},
+       { "enable", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, fr_tls_server_conf_t, ocsp_enable), "no" },
+       { "override_cert_url", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, fr_tls_server_conf_t, ocsp_override_url), "no" },
+       { "url", FR_CONF_OFFSET(PW_TYPE_STRING, fr_tls_server_conf_t, ocsp_url), NULL },
+       { "use_nonce", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, fr_tls_server_conf_t, ocsp_use_nonce), "yes" },
+       { "timeout", FR_CONF_OFFSET(PW_TYPE_INTEGER, fr_tls_server_conf_t, ocsp_timeout), "yes" },
+       { "softfail", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, fr_tls_server_conf_t, ocsp_softfail), "yes" },
        { NULL, -1, 0, NULL, NULL }        /* end the list */
 };
 #endif
 
 static CONF_PARSER tls_server_config[] = {
-       { "rsa_key_exchange", PW_TYPE_BOOLEAN,
-         offsetof(fr_tls_server_conf_t, rsa_key), NULL, "no" },
-       { "dh_key_exchange", PW_TYPE_BOOLEAN,
-         offsetof(fr_tls_server_conf_t, dh_key), NULL, "yes" },
-       { "rsa_key_length", PW_TYPE_INTEGER,
-         offsetof(fr_tls_server_conf_t, rsa_key_length), NULL, "512" },
-       { "dh_key_length", PW_TYPE_INTEGER,
-         offsetof(fr_tls_server_conf_t, dh_key_length), NULL, "512" },
-       { "verify_depth", PW_TYPE_INTEGER,
-         offsetof(fr_tls_server_conf_t, verify_depth), NULL, "0" },
-       { "CA_path", PW_TYPE_FILE_INPUT | PW_TYPE_DEPRECATED,
-         offsetof(fr_tls_server_conf_t, ca_path), NULL, NULL },
-       { "ca_path", PW_TYPE_FILE_INPUT,
-         offsetof(fr_tls_server_conf_t, ca_path), NULL, NULL },
-       { "pem_file_type", PW_TYPE_BOOLEAN,
-         offsetof(fr_tls_server_conf_t, file_type), NULL, "yes" },
-       { "private_key_file", PW_TYPE_FILE_INPUT,
-         offsetof(fr_tls_server_conf_t, private_key_file), NULL, NULL },
-       { "certificate_file", PW_TYPE_FILE_INPUT,
-         offsetof(fr_tls_server_conf_t, certificate_file), NULL, NULL },
-       { "CA_file", PW_TYPE_FILE_INPUT | PW_TYPE_DEPRECATED,
-         offsetof(fr_tls_server_conf_t, ca_file), NULL, NULL },
-       { "ca_file", PW_TYPE_FILE_INPUT,
-         offsetof(fr_tls_server_conf_t, ca_file), NULL, NULL },
-       { "private_key_password", PW_TYPE_STRING_PTR | PW_TYPE_SECRET,
-         offsetof(fr_tls_server_conf_t, private_key_password), NULL, NULL },
+       { "rsa_key_exchange", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, fr_tls_server_conf_t, rsa_key), "no" },
+       { "dh_key_exchange", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, fr_tls_server_conf_t, dh_key), "yes" },
+       { "rsa_key_length", FR_CONF_OFFSET(PW_TYPE_INTEGER, fr_tls_server_conf_t, rsa_key_length), "512" },
+       { "dh_key_length", FR_CONF_OFFSET(PW_TYPE_INTEGER, fr_tls_server_conf_t, dh_key_length), "512" },
+       { "verify_depth", FR_CONF_OFFSET(PW_TYPE_INTEGER, fr_tls_server_conf_t, verify_depth), "0" },
+       { "CA_path", FR_CONF_OFFSET(PW_TYPE_FILE_INPUT | PW_TYPE_DEPRECATED, fr_tls_server_conf_t, ca_path), NULL },
+       { "ca_path", FR_CONF_OFFSET(PW_TYPE_FILE_INPUT, fr_tls_server_conf_t, ca_path), NULL },
+       { "pem_file_type", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, fr_tls_server_conf_t, file_type), "yes" },
+       { "private_key_file", FR_CONF_OFFSET(PW_TYPE_FILE_INPUT, fr_tls_server_conf_t, private_key_file), NULL },
+       { "certificate_file", FR_CONF_OFFSET(PW_TYPE_FILE_INPUT, fr_tls_server_conf_t, certificate_file), NULL },
+       { "CA_file", FR_CONF_OFFSET(PW_TYPE_FILE_INPUT | PW_TYPE_DEPRECATED, fr_tls_server_conf_t, ca_file), NULL },
+       { "ca_file", FR_CONF_OFFSET(PW_TYPE_FILE_INPUT, fr_tls_server_conf_t, ca_file), NULL },
+       { "private_key_password", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_SECRET, fr_tls_server_conf_t, private_key_password), NULL },
 #ifdef PSK_MAX_IDENTITY_LEN
-       { "psk_identity", PW_TYPE_STRING_PTR,
-         offsetof(fr_tls_server_conf_t, psk_identity), NULL, NULL },
-       { "psk_hexphrase", PW_TYPE_STRING_PTR | PW_TYPE_SECRET,
-         offsetof(fr_tls_server_conf_t, psk_password), NULL, NULL },
+       { "psk_identity", FR_CONF_OFFSET(PW_TYPE_STRING, fr_tls_server_conf_t, psk_identity), NULL },
+       { "psk_hexphrase", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_SECRET, fr_tls_server_conf_t, psk_password), NULL },
+       { "psk_query", FR_CONF_OFFSET(PW_TYPE_STRING, fr_tls_server_conf_t, psk_query), NULL },
 #endif
-       { "dh_file", PW_TYPE_STRING_PTR,
-         offsetof(fr_tls_server_conf_t, dh_file), NULL, NULL },
-       { "random_file", PW_TYPE_STRING_PTR,
-         offsetof(fr_tls_server_conf_t, random_file), NULL, NULL },
-       { "fragment_size", PW_TYPE_INTEGER,
-         offsetof(fr_tls_server_conf_t, fragment_size), NULL, "1024" },
-       { "include_length", PW_TYPE_BOOLEAN,
-         offsetof(fr_tls_server_conf_t, include_length), NULL, "yes" },
-       { "check_crl", PW_TYPE_BOOLEAN,
-         offsetof(fr_tls_server_conf_t, check_crl), NULL, "no"},
-       { "allow_expired_crl", PW_TYPE_BOOLEAN,
-         offsetof(fr_tls_server_conf_t, allow_expired_crl), NULL, NULL},
-       { "check_cert_cn", PW_TYPE_STRING_PTR,
-         offsetof(fr_tls_server_conf_t, check_cert_cn), NULL, NULL},
-       { "cipher_list", PW_TYPE_STRING_PTR,
-         offsetof(fr_tls_server_conf_t, cipher_list), NULL, NULL},
-       { "check_cert_issuer", PW_TYPE_STRING_PTR,
-         offsetof(fr_tls_server_conf_t, check_cert_issuer), NULL, NULL},
-       { "require_client_cert", PW_TYPE_BOOLEAN,
-         offsetof(fr_tls_server_conf_t, require_client_cert), NULL, NULL },
+       { "dh_file", FR_CONF_OFFSET(PW_TYPE_STRING, fr_tls_server_conf_t, dh_file), NULL },
+       { "random_file", FR_CONF_OFFSET(PW_TYPE_STRING, fr_tls_server_conf_t, random_file), NULL },
+       { "fragment_size", FR_CONF_OFFSET(PW_TYPE_INTEGER, fr_tls_server_conf_t, fragment_size), "1024" },
+       { "include_length", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, fr_tls_server_conf_t, include_length), "yes" },
+       { "check_crl", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, fr_tls_server_conf_t, check_crl), "no" },
+       { "allow_expired_crl", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, fr_tls_server_conf_t, allow_expired_crl), NULL },
+       { "check_cert_cn", FR_CONF_OFFSET(PW_TYPE_STRING, fr_tls_server_conf_t, check_cert_cn), NULL },
+       { "cipher_list", FR_CONF_OFFSET(PW_TYPE_STRING, fr_tls_server_conf_t, cipher_list), NULL },
+       { "check_cert_issuer", FR_CONF_OFFSET(PW_TYPE_STRING, fr_tls_server_conf_t, check_cert_issuer), NULL },
+       { "require_client_cert", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, fr_tls_server_conf_t, require_client_cert), NULL },
 
 #if OPENSSL_VERSION_NUMBER >= 0x0090800fL
 #ifndef OPENSSL_NO_ECDH
-       { "ecdh_curve", PW_TYPE_STRING_PTR,
-         offsetof(fr_tls_server_conf_t, ecdh_curve), NULL, "prime256v1"},
+       { "ecdh_curve", FR_CONF_OFFSET(PW_TYPE_STRING, fr_tls_server_conf_t, ecdh_curve), "prime256v1" },
 #endif
 #endif
 
-       { "cache", PW_TYPE_SUBSECTION, 0, NULL, (void const *) cache_config },
+       { "cache", FR_CONF_POINTER(PW_TYPE_SUBSECTION, NULL), (void const *) cache_config },
 
-       { "verify", PW_TYPE_SUBSECTION, 0, NULL, (void const *) verify_config },
+       { "verify", FR_CONF_POINTER(PW_TYPE_SUBSECTION, NULL), (void const *) verify_config },
 
 #ifdef HAVE_OPENSSL_OCSP_H
-       { "ocsp", PW_TYPE_SUBSECTION, 0, NULL, (void const *) ocsp_config },
+       { "ocsp", FR_CONF_POINTER(PW_TYPE_SUBSECTION, NULL), (void const *) ocsp_config },
 #endif
 
        { NULL, -1, 0, NULL, NULL }        /* end the list */
@@ -867,53 +930,33 @@ static CONF_PARSER tls_server_config[] = {
 
 
 static CONF_PARSER tls_client_config[] = {
-       { "rsa_key_exchange", PW_TYPE_BOOLEAN,
-         offsetof(fr_tls_server_conf_t, rsa_key), NULL, "no" },
-       { "dh_key_exchange", PW_TYPE_BOOLEAN,
-         offsetof(fr_tls_server_conf_t, dh_key), NULL, "yes" },
-       { "rsa_key_length", PW_TYPE_INTEGER,
-         offsetof(fr_tls_server_conf_t, rsa_key_length), NULL, "512" },
-       { "dh_key_length", PW_TYPE_INTEGER,
-         offsetof(fr_tls_server_conf_t, dh_key_length), NULL, "512" },
-       { "verify_depth", PW_TYPE_INTEGER,
-         offsetof(fr_tls_server_conf_t, verify_depth), NULL, "0" },
-       { "ca_path", PW_TYPE_FILE_INPUT,
-         offsetof(fr_tls_server_conf_t, ca_path), NULL, NULL },
-       { "pem_file_type", PW_TYPE_BOOLEAN,
-         offsetof(fr_tls_server_conf_t, file_type), NULL, "yes" },
-       { "private_key_file", PW_TYPE_FILE_INPUT,
-         offsetof(fr_tls_server_conf_t, private_key_file), NULL, NULL },
-       { "certificate_file", PW_TYPE_FILE_INPUT,
-         offsetof(fr_tls_server_conf_t, certificate_file), NULL, NULL },
-       { "ca_file", PW_TYPE_FILE_INPUT,
-         offsetof(fr_tls_server_conf_t, ca_file), NULL, NULL },
-       { "private_key_password", PW_TYPE_STRING_PTR | PW_TYPE_SECRET,
-         offsetof(fr_tls_server_conf_t, private_key_password), NULL, NULL },
-       { "dh_file", PW_TYPE_STRING_PTR,
-         offsetof(fr_tls_server_conf_t, dh_file), NULL, NULL },
-       { "random_file", PW_TYPE_STRING_PTR,
-         offsetof(fr_tls_server_conf_t, random_file), NULL, NULL },
-       { "fragment_size", PW_TYPE_INTEGER,
-         offsetof(fr_tls_server_conf_t, fragment_size), NULL, "1024" },
-       { "include_length", PW_TYPE_BOOLEAN,
-         offsetof(fr_tls_server_conf_t, include_length), NULL, "yes" },
-       { "check_crl", PW_TYPE_BOOLEAN,
-         offsetof(fr_tls_server_conf_t, check_crl), NULL, "no"},
-       { "check_cert_cn", PW_TYPE_STRING_PTR,
-         offsetof(fr_tls_server_conf_t, check_cert_cn), NULL, NULL},
-       { "cipher_list", PW_TYPE_STRING_PTR,
-         offsetof(fr_tls_server_conf_t, cipher_list), NULL, NULL},
-       { "check_cert_issuer", PW_TYPE_STRING_PTR,
-         offsetof(fr_tls_server_conf_t, check_cert_issuer), NULL, NULL},
+       { "rsa_key_exchange", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, fr_tls_server_conf_t, rsa_key), "no" },
+       { "dh_key_exchange", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, fr_tls_server_conf_t, dh_key), "yes" },
+       { "rsa_key_length", FR_CONF_OFFSET(PW_TYPE_INTEGER, fr_tls_server_conf_t, rsa_key_length), "512" },
+       { "dh_key_length", FR_CONF_OFFSET(PW_TYPE_INTEGER, fr_tls_server_conf_t, dh_key_length), "512" },
+       { "verify_depth", FR_CONF_OFFSET(PW_TYPE_INTEGER, fr_tls_server_conf_t, verify_depth), "0" },
+       { "ca_path", FR_CONF_OFFSET(PW_TYPE_FILE_INPUT, fr_tls_server_conf_t, ca_path), NULL },
+       { "pem_file_type", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, fr_tls_server_conf_t, file_type), "yes" },
+       { "private_key_file", FR_CONF_OFFSET(PW_TYPE_FILE_INPUT, fr_tls_server_conf_t, private_key_file), NULL },
+       { "certificate_file", FR_CONF_OFFSET(PW_TYPE_FILE_INPUT, fr_tls_server_conf_t, certificate_file), NULL },
+       { "ca_file", FR_CONF_OFFSET(PW_TYPE_FILE_INPUT, fr_tls_server_conf_t, ca_file), NULL },
+       { "private_key_password", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_SECRET, fr_tls_server_conf_t, private_key_password), NULL },
+       { "dh_file", FR_CONF_OFFSET(PW_TYPE_STRING, fr_tls_server_conf_t, dh_file), NULL },
+       { "random_file", FR_CONF_OFFSET(PW_TYPE_STRING, fr_tls_server_conf_t, random_file), NULL },
+       { "fragment_size", FR_CONF_OFFSET(PW_TYPE_INTEGER, fr_tls_server_conf_t, fragment_size), "1024" },
+       { "include_length", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, fr_tls_server_conf_t, include_length), "yes" },
+       { "check_crl", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, fr_tls_server_conf_t, check_crl), "no" },
+       { "check_cert_cn", FR_CONF_OFFSET(PW_TYPE_STRING, fr_tls_server_conf_t, check_cert_cn), NULL },
+       { "cipher_list", FR_CONF_OFFSET(PW_TYPE_STRING, fr_tls_server_conf_t, cipher_list), NULL },
+       { "check_cert_issuer", FR_CONF_OFFSET(PW_TYPE_STRING, fr_tls_server_conf_t, check_cert_issuer), NULL },
 
 #if OPENSSL_VERSION_NUMBER >= 0x0090800fL
 #ifndef OPENSSL_NO_ECDH
-       { "ecdh_curve", PW_TYPE_STRING_PTR,
-         offsetof(fr_tls_server_conf_t, ecdh_curve), NULL, "prime256v1"},
+       { "ecdh_curve", FR_CONF_OFFSET(PW_TYPE_STRING, fr_tls_server_conf_t, ecdh_curve), "prime256v1" },
 #endif
 #endif
 
-       { NULL, -1, 0, NULL, NULL }        /* end the list */
+       { NULL, -1, 0, NULL, NULL }        /* end the list */
 };
 
 
@@ -935,8 +978,8 @@ static int load_dh_params(SSL_CTX *ctx, char *file)
        dh = PEM_read_bio_DHparams(bio, NULL, NULL, NULL);
        BIO_free(bio);
        if (!dh) {
-               WDEBUG2("tls: Unable to set DH parameters.  DH cipher suites may not work!");
-               WDEBUG2("Fix this by running the OpenSSL command listed in eap.conf");
+               WARN("tls: Unable to set DH parameters.  DH cipher suites may not work!");
+               WARN("Fix this by running the OpenSSL command listed in eap.conf");
                return 0;
        }
 
@@ -973,6 +1016,7 @@ static int generate_eph_rsa_key(SSL_CTX *ctx)
  * needs to be dynamic so we can supply a "free" function
  */
 static int FR_TLS_EX_INDEX_VPS = -1;
+int FR_TLS_EX_INDEX_CERTS = -1;
 
 /*
  *     Print debugging messages, and free data.
@@ -1044,8 +1088,10 @@ static int cbtls_new_session(SSL *ssl, SSL_SESSION *sess)
                        return 0;
                }
 
+
+               /* Do not convert to TALLOC - Thread safety */
                /* alloc and convert to ASN.1 */
-               sess_blob = talloc_array(conf, unsigned char, blob_len);
+               sess_blob = malloc(blob_len);
                if (!sess_blob) {
                        DEBUG2("  SSL: could not allocate buffer len=%d to persist session", blob_len);
                        return 0;
@@ -1084,7 +1130,7 @@ static int cbtls_new_session(SSL *ssl, SSL_SESSION *sess)
        }
 
 error:
-       if (sess_blob) talloc_free(sess_blob);
+       free(sess_blob);
 
        return 0;
 }
@@ -1096,6 +1142,7 @@ static SSL_SESSION *cbtls_get_session(SSL *ssl,
        size_t size;
        char buffer[2 * MAX_SESSION_SIZE + 1];
        fr_tls_server_conf_t *conf;
+       TALLOC_CTX *talloc_ctx;
 
        SSL_SESSION *sess = NULL;
        unsigned char *sess_data = NULL;
@@ -1109,6 +1156,7 @@ static SSL_SESSION *cbtls_get_session(SSL *ssl,
        DEBUG2("  SSL: Client requested cached session %s", buffer);
 
        conf = (fr_tls_server_conf_t *)SSL_get_ex_data(ssl, FR_TLS_EX_INDEX_CONF);
+       talloc_ctx = SSL_get_ex_data(ssl, FR_TLS_EX_INDEX_TALLOC);
        if (conf && conf->session_cache_path) {
                int rv, fd, todo;
                char filename[256];
@@ -1173,7 +1221,7 @@ static SSL_SESSION *cbtls_get_session(SSL *ssl,
                }
 
                /* cache the VPs into the session */
-               vp = paircopy(NULL, pairlist->reply);
+               vp = paircopy(talloc_ctx, pairlist->reply);
                SSL_SESSION_set_ex_data(sess, FR_TLS_EX_INDEX_VPS, vp);
                DEBUG2("  SSL: Successfully restored session %s", buffer);
        }
@@ -1260,15 +1308,19 @@ static int ocsp_check(X509_STORE *store, X509 *issuer_cert, X509 *client_cert,
         */
 
        /* Get OCSP responder URL */
-       if(conf->ocsp_override_url) {
-               OCSP_parse_url(conf->ocsp_url, &host, &port, &path, &use_ssl);
+       if (conf->ocsp_override_url) {
+               char *url;
+
+               memcpy(&url, &conf->ocsp_url, sizeof(url));
+               /* Reading the libssl src, they do a strdup on the URL, so it could of been const *sigh* */
+               OCSP_parse_url(url, &host, &port, &path, &use_ssl);
        }
        else {
                ocsp_parse_cert_url(client_cert, &host, &port, &path, &use_ssl);
        }
 
        if (!host || !port || !path) {
-               DEBUG2("[ocsp] - Host / port / path missing.  Not doing OCSP.");
+               DEBUG2("[ocsp] - Host / port / path missing.  Not doing OCSP");
                ocsp_ok = 2;
                goto ocsp_skip;
        }
@@ -1408,8 +1460,8 @@ ocsp_end:
                break;
        case 2:
                if (conf->ocsp_softfail) {
-                       DEBUG2("[ocsp] --> Unable to check certificate; assuming valid.");
-                       DEBUG2("[ocsp] --> Warning! This may be insecure.");
+                       DEBUG2("[ocsp] --> Unable to check certificate; assuming valid");
+                       DEBUG2("[ocsp] --> Warning! This may be insecure");
                        ocsp_ok = 1;
                } else {
                        DEBUG2("[ocsp] --> Unable to check certificate; failing!");
@@ -1428,13 +1480,15 @@ ocsp_end:
 /*
  *     For creating certificate attributes.
  */
-static char const *cert_attr_names[6][2] = {
+static char const *cert_attr_names[8][2] = {
   { "TLS-Client-Cert-Serial",          "TLS-Cert-Serial" },
   { "TLS-Client-Cert-Expiration",      "TLS-Cert-Expiration" },
   { "TLS-Client-Cert-Subject",         "TLS-Cert-Subject" },
   { "TLS-Client-Cert-Issuer",          "TLS-Cert-Issuer" },
   { "TLS-Client-Cert-Common-Name",     "TLS-Cert-Common-Name" },
-  { "TLS-Client-Cert-Subject-Alt-Name-Email",  "TLS-Cert-Subject-Alt-Name-Email" }
+  { "TLS-Client-Cert-Subject-Alt-Name-Email",  "TLS-Cert-Subject-Alt-Name-Email" },
+  { "TLS-Client-Cert-Subject-Alt-Name-Dns",    "TLS-Cert-Subject-Alt-Name-Dns" },
+  { "TLS-Client-Cert-Subject-Alt-Name-Upn",    "TLS-Cert-Subject-Alt-Name-Upn" }
 };
 
 #define FR_TLS_SERIAL          (0)
@@ -1443,6 +1497,8 @@ static char const *cert_attr_names[6][2] = {
 #define FR_TLS_ISSUER          (3)
 #define FR_TLS_CN              (4)
 #define FR_TLS_SAN_EMAIL               (5)
+#define FR_TLS_SAN_DNS          (6)
+#define FR_TLS_SAN_UPN          (7)
 
 /*
  *     Before trusting a certificate, you must make sure that the
@@ -1494,6 +1550,7 @@ int cbtls_verify(int ok, X509_STORE_CTX *ctx)
        X509_STORE *ocsp_store = NULL;
        X509 *issuer_cert;
 #endif
+       TALLOC_CTX *talloc_ctx;
 
        client_cert = X509_STORE_CTX_get_current_cert(ctx);
        err = X509_STORE_CTX_get_error(ctx);
@@ -1516,17 +1573,16 @@ int cbtls_verify(int ok, X509_STORE_CTX *ctx)
        if (!conf) return 1;
 
        request = (REQUEST *)SSL_get_ex_data(ssl, FR_TLS_EX_INDEX_REQUEST);
-
-       if (!request) return 1; /* FIXME: outbound TLS */
-
        rad_assert(request != NULL);
        certs = (VALUE_PAIR **)SSL_get_ex_data(ssl, FR_TLS_EX_INDEX_CERTS);
-       rad_assert(certs != NULL);
+
        identity = (char **)SSL_get_ex_data(ssl, FR_TLS_EX_INDEX_IDENTITY);
 #ifdef HAVE_OPENSSL_OCSP_H
        ocsp_store = (X509_STORE *)SSL_get_ex_data(ssl, FR_TLS_EX_INDEX_STORE);
 #endif
 
+       talloc_ctx = SSL_get_ex_data(ssl, FR_TLS_EX_INDEX_TALLOC);
+
        /*
         *      Get the Serial Number
         */
@@ -1539,7 +1595,7 @@ int cbtls_verify(int ok, X509_STORE_CTX *ctx)
         *      have a user identity.  i.e. we don't create the
         *      attributes for RadSec connections.
         */
-       if (identity &&
+       if (certs && identity &&
            (lookup <= 1) && sn && ((size_t) sn->length < (sizeof(buf) / 2))) {
                char *p = buf;
                int i;
@@ -1548,7 +1604,7 @@ int cbtls_verify(int ok, X509_STORE_CTX *ctx)
                        sprintf(p, "%02x", (unsigned int)sn->data[i]);
                        p += 2;
                }
-               pairmake(NULL, certs, cert_attr_names[FR_TLS_SERIAL][lookup], buf, T_OP_SET);
+               pairmake(talloc_ctx, certs, cert_attr_names[FR_TLS_SERIAL][lookup], buf, T_OP_SET);
        }
 
 
@@ -1557,11 +1613,11 @@ int cbtls_verify(int ok, X509_STORE_CTX *ctx)
         */
        buf[0] = '\0';
        asn_time = X509_get_notAfter(client_cert);
-       if (identity && (lookup <= 1) && asn_time &&
+       if (certs && identity && (lookup <= 1) && asn_time &&
            (asn_time->length < (int) sizeof(buf))) {
                memcpy(buf, (char*) asn_time->data, asn_time->length);
                buf[asn_time->length] = '\0';
-               pairmake(NULL, certs, cert_attr_names[FR_TLS_EXPIRATION][lookup], buf, T_OP_SET);
+               pairmake(talloc_ctx, certs, cert_attr_names[FR_TLS_EXPIRATION][lookup], buf, T_OP_SET);
        }
 
        /*
@@ -1571,15 +1627,15 @@ int cbtls_verify(int ok, X509_STORE_CTX *ctx)
        X509_NAME_oneline(X509_get_subject_name(client_cert), subject,
                          sizeof(subject));
        subject[sizeof(subject) - 1] = '\0';
-       if (identity && (lookup <= 1) && subject[0]) {
-               pairmake(NULL, certs, cert_attr_names[FR_TLS_SUBJECT][lookup], subject, T_OP_SET);
+       if (certs && identity && (lookup <= 1) && subject[0]) {
+               pairmake(talloc_ctx, certs, cert_attr_names[FR_TLS_SUBJECT][lookup], subject, T_OP_SET);
        }
 
        X509_NAME_oneline(X509_get_issuer_name(ctx->current_cert), issuer,
                          sizeof(issuer));
        issuer[sizeof(issuer) - 1] = '\0';
-       if (identity && (lookup <= 1) && issuer[0]) {
-               pairmake(NULL, certs, cert_attr_names[FR_TLS_ISSUER][lookup], issuer, T_OP_SET);
+       if (certs && identity && (lookup <= 1) && issuer[0]) {
+               pairmake(talloc_ctx, certs, cert_attr_names[FR_TLS_ISSUER][lookup], issuer, T_OP_SET);
        }
 
        /*
@@ -1588,16 +1644,15 @@ int cbtls_verify(int ok, X509_STORE_CTX *ctx)
        X509_NAME_get_text_by_NID(X509_get_subject_name(client_cert),
                                  NID_commonName, common_name, sizeof(common_name));
        common_name[sizeof(common_name) - 1] = '\0';
-       if (identity && (lookup <= 1) && common_name[0] && subject[0]) {
-               pairmake(NULL, certs, cert_attr_names[FR_TLS_CN][lookup], common_name, T_OP_SET);
+       if (certs && identity && (lookup <= 1) && common_name[0] && subject[0]) {
+               pairmake(talloc_ctx, certs, cert_attr_names[FR_TLS_CN][lookup], common_name, T_OP_SET);
        }
 
-#ifdef GEN_EMAIL
        /*
         *      Get the RFC822 Subject Alternative Name
         */
        loc = X509_get_ext_by_NID(client_cert, NID_subject_alt_name, 0);
-       if (lookup <= 1 && loc >= 0) {
+       if (certs && (lookup <= 1) && (loc >= 0)) {
                X509_EXTENSION *ext = NULL;
                GENERAL_NAMES *names = NULL;
                int i;
@@ -1608,10 +1663,34 @@ int cbtls_verify(int ok, X509_STORE_CTX *ctx)
                                GENERAL_NAME *name = sk_GENERAL_NAME_value(names, i);
 
                                switch (name->type) {
+#ifdef GEN_EMAIL
                                case GEN_EMAIL:
-                                       pairmake(NULL, certs, cert_attr_names[FR_TLS_SAN_EMAIL][lookup],
+                                       pairmake(talloc_ctx, certs, cert_attr_names[FR_TLS_SAN_EMAIL][lookup],
                                                 (char *) ASN1_STRING_data(name->d.rfc822Name), T_OP_SET);
                                        break;
+#endif /* GEN_EMAIL */
+#ifdef GEN_DNS
+                               case GEN_DNS:
+                                       pairmake(talloc_ctx, certs, cert_attr_names[FR_TLS_SAN_DNS][lookup],
+                                                (char *) ASN1_STRING_data(name->d.dNSName), T_OP_SET);
+                                       break;
+#endif /* GEN_DNS */
+#ifdef GEN_OTHERNAME
+                               case GEN_OTHERNAME:
+                                       /* look for a MS UPN */
+                                       if (NID_ms_upn == OBJ_obj2nid(name->d.otherName->type_id)) {
+                                           /* we've got a UPN - Must be ASN1-encoded UTF8 string */
+                                           if (name->d.otherName->value->type == V_ASN1_UTF8STRING) {
+                                               pairmake(talloc_ctx, certs, cert_attr_names[FR_TLS_SAN_UPN][lookup],
+                                                        (char *) ASN1_STRING_data(name->d.otherName->value->value.utf8string), T_OP_SET);
+                                               break;
+                                           } else {
+                                               RWARN("Invalid UPN in Subject Alt Name (should be UTF-8)\n");
+                                               break;
+                                           }
+                                       }
+                                       break;
+#endif /* GEN_OTHERNAME */
                                default:
                                        /* XXX TODO handle other SAN types */
                                        break;
@@ -1621,7 +1700,6 @@ int cbtls_verify(int ok, X509_STORE_CTX *ctx)
                if (names != NULL)
                        sk_GENERAL_NAME_free(names);
        }
-#endif /* GEN_EMAIL */
 
        /*
         *      If the CRL has expired, that might still be OK.
@@ -1687,7 +1765,7 @@ int cbtls_verify(int ok, X509_STORE_CTX *ctx)
                                if (*p == ' ') *p = '-';
                        }
 
-                       vp = pairmake(NULL, certs, attribute, value, T_OP_ADD);
+                       vp = pairmake(talloc_ctx, certs, attribute, value, T_OP_ADD);
                        if (vp) debug_pair_list(vp);
                }
 
@@ -1775,6 +1853,7 @@ int cbtls_verify(int ok, X509_STORE_CTX *ctx)
 
                        fp = fdopen(fd, "w");
                        if (!fp) {
+                               close(fd);
                                RDEBUG("Failed opening file %s: %s",
                                       filename, fr_syserror(errno));
                                break;
@@ -1902,24 +1981,86 @@ static void sess_free_vps(UNUSED void *parent, void *data_ptr,
        VALUE_PAIR *vp = data_ptr;
        if (!vp) return;
 
-       DEBUG2("  Freeing cached session VPs %p", vp);
+       DEBUG2("  Freeing cached session VPs");;
 
        pairfree(&vp);
 }
 
-/*
- *     Add all the default ciphers and message digests
- *     Create our context.
+static void sess_free_certs(UNUSED void *parent, void *data_ptr,
+                               UNUSED CRYPTO_EX_DATA *ad, UNUSED int idx,
+                               UNUSED long argl, UNUSED void *argp)
+{
+       VALUE_PAIR **certs = data_ptr;
+       if (!certs) return;
+
+       DEBUG2("  Freeing cached session Certificates");
+
+       pairfree(certs);
+}
+
+/** Add all the default ciphers and message digests reate our context.
  *
- *     This should be called exactly once from main.
+ * This should be called exactly once from main, before reading the main config
+ * or initialising any modules.
  */
 void tls_global_init(void)
 {
        SSL_load_error_strings();       /* readable error messages (examples show call before library_init) */
        SSL_library_init();             /* initialize library */
-#ifdef HAVE_OPENSSL_EVP_H
        OpenSSL_add_all_algorithms();   /* required for SHA2 in OpenSSL < 0.9.8o and 1.0.0.a */
-#endif
+       OPENSSL_config(NULL);
+}
+
+/** Check for vulnerable versions of libssl
+ *
+ * @param acknowledged The highest CVE number a user has confirmed is not present in the system's libssl.
+ * @return 0 if the CVE specified by the user matches the most recent CVE we have, else -1.
+ */
+int tls_global_version_check(char const *acknowledged)
+{
+       uint64_t v;
+
+       if ((strcmp(acknowledged, libssl_defects[0].id) != 0) && (strcmp(acknowledged, "yes") != 0)) {
+               bool bad = false;
+               size_t i;
+
+               /* Check for bad versions */
+               v = (uint64_t) SSLeay();
+
+               for (i = 0; i < (sizeof(libssl_defects) / sizeof(*libssl_defects)); i++) {
+                       libssl_defect_t *defect = &libssl_defects[i];
+
+                       if ((v >= defect->low) && (v <= defect->high)) {
+                               ERROR("Refusing to start with libssl version %s (in range %s)",
+                                     ssl_version(), ssl_version_range(defect->low, defect->high));
+                               ERROR("Security advisory %s (%s)", defect->id, defect->name);
+                               ERROR("%s", defect->comment);
+
+                               bad = true;
+                       }
+               }
+
+               if (bad) {
+                       INFO("Once you have verified libssl has been correctly patched, "
+                            "set security.allow_vulnerable_openssl = '%s'", libssl_defects[0].id);
+                       return -1;
+               }
+       }
+
+       return 0;
+}
+
+/** Free any memory alloced by libssl
+ *
+ */
+void tls_global_cleanup(void)
+{
+       ERR_remove_state(0);
+       ENGINE_cleanup();
+       CONF_modules_unload(1);
+       ERR_free_strings();
+       EVP_cleanup();
+       CRYPTO_cleanup_all_ex_data();
 }
 
 /*
@@ -1929,7 +2070,7 @@ void tls_global_init(void)
  *     - Load the Private key & the certificate
  *     - Set the Context options & Verify options
  */
-static SSL_CTX *init_tls_ctx(fr_tls_server_conf_t *conf, int client)
+static SSL_CTX *tls_init_ctx(fr_tls_server_conf_t *conf, int client)
 {
        SSL_CTX *ctx;
        X509_STORE *certstore;
@@ -1947,6 +2088,14 @@ static SSL_CTX *init_tls_ctx(fr_tls_server_conf_t *conf, int client)
 #endif
 
        ctx = SSL_CTX_new(TLSv1_method());
+       if (!ctx) {
+               int err;
+               while ((err = ERR_get_error())) {
+                       DEBUG("Failed creating SSL context: %s",
+                             ERR_error_string(err, NULL));
+                       return NULL;
+               }
+       }
 
        /*
         * Save the config on the context so that callbacks which
@@ -1974,15 +2123,12 @@ static SSL_CTX *init_tls_ctx(fr_tls_server_conf_t *conf, int client)
                 * programmatically.
                 */
                char const* special_string = "Apple:UseCertAdmin";
-               if (strncmp(conf->private_key_password,
-                                       special_string,
-                                       strlen(special_string)) == 0)
-               {
+               if (strncmp(conf->private_key_password, special_string, strlen(special_string)) == 0) {
                        char cmd[256];
+                       char *password;
                        long const max_password_len = 128;
-                       snprintf(cmd, sizeof(cmd) - 1,
-                                        "/usr/sbin/certadmin --get-private-key-passphrase \"%s\"",
-                                        conf->private_key_file);
+                       snprintf(cmd, sizeof(cmd) - 1, "/usr/sbin/certadmin --get-private-key-passphrase \"%s\"",
+                                conf->private_key_file);
 
                        DEBUG2("tls: Getting private key passphrase using command \"%s\"", cmd);
 
@@ -1993,29 +2139,56 @@ static SSL_CTX *init_tls_ctx(fr_tls_server_conf_t *conf, int client)
                                return NULL;
                        }
 
-                       talloc_free(conf->private_key_password);
-                       conf->private_key_password = talloc_array(conf, char, max_password_len);
-                       if (!conf->private_key_password) {
+                       rad_const_free(conf->private_key_password);
+                       password = talloc_array(conf, char, max_password_len);
+                       if (!password) {
                                ERROR("TLS: Can't allocate space for private_key_password");
                                ERROR("TLS: Error reading private_key_file %s", conf->private_key_file);
                                pclose(cmd_pipe);
                                return NULL;
                        }
 
-                       fgets(conf->private_key_password, max_password_len, cmd_pipe);
+                       fgets(password, max_password_len, cmd_pipe);
                        pclose(cmd_pipe);
 
                        /* Get rid of newline at end of password. */
-                       conf->private_key_password[strlen(conf->private_key_password) - 1] = '\0';
+                       password[strlen(password) - 1] = '\0';
 
-                       DEBUG3("tls:  Password from command = \"%s\"", conf->private_key_password);
+                       DEBUG3("tls:  Password from command = \"%s\"", password);
+                       conf->private_key_password = password;
                }
 #endif
-               SSL_CTX_set_default_passwd_cb_userdata(ctx, conf->private_key_password);
-               SSL_CTX_set_default_passwd_cb(ctx, cbtls_password);
+
+               {
+                       char *password;
+
+                       memcpy(&password, &conf->private_key_password, sizeof(password));
+                       SSL_CTX_set_default_passwd_cb_userdata(ctx, password);
+                       SSL_CTX_set_default_passwd_cb(ctx, cbtls_password);
+               }
        }
 
 #ifdef PSK_MAX_IDENTITY_LEN
+       if (!client) {
+               /*
+                *      No dynamic query exists.  There MUST be a
+                *      statically configured identity and password.
+                */
+               if (conf->psk_query && !*conf->psk_query) {
+                       ERROR("Invalid PSK Configuration: psk_query cannot be empty");
+                       return NULL;
+               }
+
+               SSL_CTX_set_psk_server_callback(ctx, psk_server_callback);
+
+       } else if (conf->psk_query) {
+               ERROR("Invalid PSK Configuration: psk_query cannot be used for outgoing connections");
+               return NULL;
+       }
+
+       /*
+        *      Now check that if PSK is being used, the config is valid.
+        */
        if ((conf->psk_identity && !conf->psk_password) ||
            (!conf->psk_identity && conf->psk_password) ||
            (conf->psk_identity && !*conf->psk_identity) ||
@@ -2026,7 +2199,7 @@ static SSL_CTX *init_tls_ctx(fr_tls_server_conf_t *conf, int client)
 
        if (conf->psk_identity) {
                size_t psk_len, hex_len;
-               char buffer[PSK_MAX_PSK_LEN];
+               uint8_t buffer[PSK_MAX_PSK_LEN];
 
                if (conf->certificate_file ||
                    conf->private_key_password || conf->private_key_file ||
@@ -2038,9 +2211,6 @@ static SSL_CTX *init_tls_ctx(fr_tls_server_conf_t *conf, int client)
                if (client) {
                        SSL_CTX_set_psk_client_callback(ctx,
                                                        psk_client_callback);
-               } else {
-                       SSL_CTX_set_psk_server_callback(ctx,
-                                                       psk_server_callback);
                }
 
                psk_len = strlen(conf->psk_password);
@@ -2050,7 +2220,11 @@ static SSL_CTX *init_tls_ctx(fr_tls_server_conf_t *conf, int client)
                        return NULL;
                }
 
-               hex_len = fr_hex2bin((uint8_t *) buffer, conf->psk_password, psk_len);
+               /*
+                *      Check the password now, so that we don't have
+                *      errors at run-time.
+                */
+               hex_len = fr_hex2bin(buffer, sizeof(buffer), conf->psk_password, psk_len);
                if (psk_len != (2 * hex_len)) {
                        ERROR("psk_hexphrase is not all hex");
                        return NULL;
@@ -2189,6 +2363,8 @@ post_ca:
                SSL_CTX_set_quiet_shutdown(ctx, 1);
                if (FR_TLS_EX_INDEX_VPS < 0)
                        FR_TLS_EX_INDEX_VPS = SSL_SESSION_get_ex_new_index(0, NULL, NULL, NULL, sess_free_vps);
+               if (FR_TLS_EX_INDEX_CERTS < 0)
+                       FR_TLS_EX_INDEX_CERTS = SSL_SESSION_get_ex_new_index(0, NULL, NULL, NULL, sess_free_certs);
        }
 
        /*
@@ -2196,13 +2372,13 @@ post_ca:
         */
 #ifdef X509_V_FLAG_CRL_CHECK
        if (conf->check_crl) {
-         certstore = SSL_CTX_get_cert_store(ctx);
-         if (certstore == NULL) {
-           ERROR("tls: SSL error %s", ERR_error_string(ERR_get_error(), NULL));
-           ERROR("tls: Error reading Certificate Store");
-           return NULL;
-         }
-         X509_STORE_set_flags(certstore, X509_V_FLAG_CRL_CHECK);
+               certstore = SSL_CTX_get_cert_store(ctx);
+               if (certstore == NULL) {
+                       ERROR("tls: SSL error %s", ERR_error_string(ERR_get_error(), NULL));
+                       ERROR("tls: Error reading Certificate Store");
+                       return NULL;
+               }
+               X509_STORE_set_flags(certstore, X509_V_FLAG_CRL_CHECK);
        }
 #endif
 
@@ -2290,7 +2466,7 @@ post_ca:
  *     added to automatically free the data when the CONF_SECTION
  *     is freed.
  */
-static int tls_server_conf_free(fr_tls_server_conf_t *conf)
+static int _tls_server_conf_free(fr_tls_server_conf_t *conf)
 {
        if (conf->ctx) SSL_CTX_free(conf->ctx);
 
@@ -2305,6 +2481,21 @@ static int tls_server_conf_free(fr_tls_server_conf_t *conf)
        return 0;
 }
 
+static fr_tls_server_conf_t *tls_server_conf_alloc(TALLOC_CTX *ctx)
+{
+       fr_tls_server_conf_t *conf;
+
+       conf = talloc_zero(ctx, fr_tls_server_conf_t);
+       if (!conf) {
+               ERROR("Out of memory");
+               return NULL;
+       }
+
+       talloc_set_destructor(conf, _tls_server_conf_free);
+
+       return conf;
+}
+
 
 fr_tls_server_conf_t *tls_server_conf_parse(CONF_SECTION *cs)
 {
@@ -2320,13 +2511,7 @@ fr_tls_server_conf_t *tls_server_conf_parse(CONF_SECTION *cs)
                return conf;
        }
 
-       conf = talloc_zero(cs, fr_tls_server_conf_t);
-       if (!conf) {
-               ERROR("Out of memory");
-               return NULL;
-       }
-
-       talloc_set_destructor(conf, tls_server_conf_free);
+       conf = tls_server_conf_alloc(cs);
 
        if (cf_section_parse(cs, conf, tls_server_config) < 0) {
        error:
@@ -2352,7 +2537,7 @@ fr_tls_server_conf_t *tls_server_conf_parse(CONF_SECTION *cs)
        /*
         *      Initialize TLS
         */
-       conf->ctx = init_tls_ctx(conf, 0);
+       conf->ctx = tls_init_ctx(conf, 0);
        if (conf->ctx == NULL) {
                goto error;
        }
@@ -2366,9 +2551,13 @@ fr_tls_server_conf_t *tls_server_conf_parse(CONF_SECTION *cs)
                if (conf->ocsp_store == NULL) goto error;
        }
 #endif /*HAVE_OPENSSL_OCSP_H*/
+       {
+               char *dh_file;
 
-       if (load_dh_params(conf->ctx, conf->dh_file) < 0) {
-               goto error;
+               memcpy(&dh_file, &conf->dh_file, sizeof(dh_file));
+               if (load_dh_params(conf->ctx, dh_file) < 0) {
+                       goto error;
+               }
        }
 
        if (generate_eph_rsa_key(conf->ctx) < 0) {
@@ -2405,13 +2594,7 @@ fr_tls_server_conf_t *tls_client_conf_parse(CONF_SECTION *cs)
                return conf;
        }
 
-       conf = talloc_zero(cs, fr_tls_server_conf_t);
-       if (!conf) {
-               ERROR("Out of memory");
-               return NULL;
-       }
-
-       talloc_set_destructor(conf, tls_server_conf_free);
+       conf = tls_server_conf_alloc(cs);
 
        if (cf_section_parse(cs, conf, tls_client_config) < 0) {
        error:
@@ -2427,13 +2610,18 @@ fr_tls_server_conf_t *tls_client_conf_parse(CONF_SECTION *cs)
        /*
         *      Initialize TLS
         */
-       conf->ctx = init_tls_ctx(conf, 1);
+       conf->ctx = tls_init_ctx(conf, 1);
        if (conf->ctx == NULL) {
                goto error;
        }
 
-       if (load_dh_params(conf->ctx, conf->dh_file) < 0) {
-               goto error;
+       {
+               char *dh_file;
+
+               memcpy(&dh_file, &conf->dh_file, sizeof(dh_file));
+               if (load_dh_params(conf->ctx, dh_file) < 0) {
+                       goto error;
+               }
        }
 
        if (generate_eph_rsa_key(conf->ctx) < 0) {
@@ -2449,10 +2637,13 @@ int tls_success(tls_session_t *ssn, REQUEST *request)
 {
        VALUE_PAIR *vp, *vps = NULL;
        fr_tls_server_conf_t *conf;
+       TALLOC_CTX *talloc_ctx;
 
        conf = (fr_tls_server_conf_t *)SSL_get_ex_data(ssn->ssl, FR_TLS_EX_INDEX_CONF);
        rad_assert(conf != NULL);
 
+       talloc_ctx = SSL_get_ex_data(ssn->ssl, FR_TLS_EX_INDEX_TALLOC);
+
        /*
         *      If there's no session resumption, delete the entry
         *      from the cache.  This means either it's disabled
@@ -2474,7 +2665,7 @@ int tls_success(tls_session_t *ssn, REQUEST *request)
                 *      not allowed,
                 */
                if (SSL_session_reused(ssn->ssl)) {
-                       RDEBUG("FAIL: Forcibly stopping session resumption as it is not allowed.");
+                       RDEBUG("FAIL: Forcibly stopping session resumption as it is not allowed");
                        return -1;
                }
 
@@ -2492,16 +2683,16 @@ int tls_success(tls_session_t *ssn, REQUEST *request)
 
                fr_bin2hex(buffer, ssn->ssl->session->session_id, size);
 
-               vp = paircopy2(NULL, request->reply->vps, PW_USER_NAME, 0, TAG_ANY);
+               vp = paircopy2(talloc_ctx, request->reply->vps, PW_USER_NAME, 0, TAG_ANY);
                if (vp) pairadd(&vps, vp);
 
-               vp = paircopy2(NULL, request->packet->vps, PW_STRIPPED_USER_NAME, 0, TAG_ANY);
+               vp = paircopy2(talloc_ctx, request->packet->vps, PW_STRIPPED_USER_NAME, 0, TAG_ANY);
                if (vp) pairadd(&vps, vp);
 
-               vp = paircopy2(NULL, request->reply->vps, PW_CHARGEABLE_USER_IDENTITY, 0, TAG_ANY);
+               vp = paircopy2(talloc_ctx, request->reply->vps, PW_CHARGEABLE_USER_IDENTITY, 0, TAG_ANY);
                if (vp) pairadd(&vps, vp);
 
-               vp = paircopy2(NULL, request->reply->vps, PW_CACHED_SESSION_POLICY, 0, TAG_ANY);
+               vp = paircopy2(talloc_ctx, request->reply->vps, PW_CACHED_SESSION_POLICY, 0, TAG_ANY);
                if (vp) pairadd(&vps, vp);
 
                certs = (VALUE_PAIR **)SSL_get_ex_data(ssn->ssl, FR_TLS_EX_INDEX_CERTS);
@@ -2514,7 +2705,7 @@ int tls_success(tls_session_t *ssn, REQUEST *request)
                         *      @todo: some go into reply, others into
                         *      request
                         */
-                       pairadd(&vps, paircopy(NULL, *certs));
+                       pairadd(&vps, paircopy(talloc_ctx, *certs));
                }
 
                if (vps) {
@@ -2591,7 +2782,7 @@ int tls_success(tls_session_t *ssn, REQUEST *request)
                                                paircopyvp(request->packet, vp));
                                } else {
                                        pairadd(&request->reply->vps,
-                                               paircopyvp(request->packet, vp));
+                                               paircopyvp(request->reply, vp));
                                }
                        }
 
@@ -2689,7 +2880,7 @@ fr_tls_status_t tls_application_data(tls_session_t *ssn,
        }
 
        if (err == 0) {
-               RWDEBUG("No data inside of the tunnel.");
+               RWDEBUG("No data inside of the tunnel");
        }
 
        /*
@@ -2712,16 +2903,16 @@ fr_tls_status_t tls_ack_handler(tls_session_t *ssn, REQUEST *request)
        RDEBUG2("Received TLS ACK");
 
        if (ssn == NULL){
-               RERROR("FAIL: Unexpected ACK received.  Could not obtain session information.");
+               RERROR("FAIL: Unexpected ACK received.  Could not obtain session information");
                return FR_TLS_INVALID;
        }
        if (ssn->info.initialized == 0) {
-               RDEBUG("No SSL info available. Waiting for more SSL data.");
+               RDEBUG("No SSL info available. Waiting for more SSL data");
                return FR_TLS_REQUEST;
        }
        if ((ssn->info.content_type == handshake) &&
            (ssn->info.origin == 0)) {
-               RERROR("FAIL: ACK without earlier message.");
+               RERROR("FAIL: ACK without earlier message");
                return FR_TLS_INVALID;
        }