Allow for dynamically expanded PSK.
[freeradius.git] / src / main / tls.c
index 06e3683..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,7 +51,26 @@ USES_APPLE_DEPRECATED_API    /* OpenSSL API has been deprecated by Apple */
 #include <openssl/ocsp.h>
 #endif
 
-static void tls_server_conf_free(fr_tls_server_conf_t *conf);
+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);
@@ -61,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(conf->psk_password, psk, 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,
@@ -101,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(conf->psk_password, psk, psk_len);
+       return fr_hex2bin(psk, max_psk_len, conf->psk_password, psk_len);
 }
 
 #endif
@@ -110,13 +201,23 @@ 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;
 
        ssn->ctx = conf->ctx;
+
+       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
@@ -140,10 +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;
        }
 
@@ -152,14 +254,30 @@ 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;
        int             verify_mode = 0;
        VALUE_PAIR      *vp;
 
+       rad_assert(request != NULL);
+
        /*
         *      Manually flush the sessions every so often.  If HALF
         *      of the session lifetime has passed since we last
@@ -168,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));
 
@@ -185,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;
@@ -262,7 +381,7 @@ tls_session_t *tls_new_session(fr_tls_server_conf_t *conf, REQUEST *request,
        if (conf->session_cache_enable) {
                state->allow_session_resumption = 1; /* otherwise it's zero */
        }
-       
+
        RDEBUG2("Initiate");
 
        return state;
@@ -349,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,
@@ -373,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");
        }
 
@@ -415,7 +536,7 @@ int tls_handshake_recv(REQUEST *request, tls_session_t *ssn)
 }
 
 /*
- *     Take clear-text user data, and encrypt it into the output buffer,
+ *     Take cleartext user data, and encrypt it into the output buffer,
  *     to send to the client at the other end of the SSL connection.
  */
 int tls_handshake_send(REQUEST *request, tls_session_t *ssn)
@@ -470,12 +591,14 @@ void session_init(tls_session_t *ssn)
 }
 
 void session_close(tls_session_t *ssn)
-{      
+{
        SSL_set_quiet_shutdown(ssn->ssl, 1);
        SSL_shutdown(ssn->ssl);
 
-       if(ssn->ssl)
+       if (ssn->ssl) {
                SSL_free(ssn->ssl);
+               ssn->ssl = NULL;
+       }
 
        record_close(&ssn->clean_in);
        record_close(&ssn->clean_out);
@@ -484,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;
@@ -572,8 +676,7 @@ void tls_session_information(tls_session_t *tls_session)
 
        str_write_p = tls_session->info.origin ? ">>>" : "<<<";
 
-       switch (tls_session->info.version)
-       {
+       switch (tls_session->info.version) {
        case SSL2_VERSION:
                str_version = "SSL 2.0";
                break;
@@ -701,8 +804,7 @@ void tls_session_information(tls_session_t *tls_session)
                        str_details1 = "???";
 
                        if (tls_session->info.record_len > 0)
-                       switch (tls_session->info.handshake_type)
-                       {
+                       switch (tls_session->info.handshake_type) {
                        case SSL3_MT_HELLO_REQUEST:
                                str_details1 = ", HelloRequest";
                                break;
@@ -745,115 +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_FILENAME,
-         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_FILENAME,
-         offsetof(fr_tls_server_conf_t, private_key_file), NULL, NULL },
-       { "certificate_file", PW_TYPE_FILENAME,
-         offsetof(fr_tls_server_conf_t, certificate_file), NULL, NULL },
-       { "CA_file", PW_TYPE_FILENAME,
-         offsetof(fr_tls_server_conf_t, ca_file), NULL, NULL },
-       { "private_key_password", PW_TYPE_STRING_PTR,
-         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,
-         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},
-       { "make_cert_command", PW_TYPE_STRING_PTR,
-         offsetof(fr_tls_server_conf_t, make_cert_command), 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 */
@@ -861,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_FILENAME,
-         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_FILENAME,
-         offsetof(fr_tls_server_conf_t, private_key_file), NULL, NULL },
-       { "certificate_file", PW_TYPE_FILENAME,
-         offsetof(fr_tls_server_conf_t, certificate_file), NULL, NULL },
-       { "CA_file", PW_TYPE_FILENAME,
-         offsetof(fr_tls_server_conf_t, ca_file), NULL, NULL },
-       { "private_key_password", PW_TYPE_STRING_PTR,
-         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 */
 };
 
 
@@ -919,21 +968,23 @@ static int load_dh_params(SSL_CTX *ctx, char *file)
        DH *dh = NULL;
        BIO *bio;
 
+       if (!file) return 0;
+
        if ((bio = BIO_new_file(file, "r")) == NULL) {
-               ERROR("rlm_eap_tls: Unable to open DH file - %s", file);
+               ERROR("tls: Unable to open DH file - %s", file);
                return -1;
        }
 
        dh = PEM_read_bio_DHparams(bio, NULL, NULL, NULL);
        BIO_free(bio);
        if (!dh) {
-               WDEBUG2("rlm_eap_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;
        }
 
        if (SSL_CTX_set_tmp_dh(ctx, dh) < 0) {
-               ERROR("rlm_eap_tls: Unable to set DH parameters");
+               ERROR("tls: Unable to set DH parameters");
                DH_free(dh);
                return -1;
        }
@@ -953,7 +1004,7 @@ static int generate_eph_rsa_key(SSL_CTX *ctx)
        rsa = RSA_generate_key(512, RSA_F4, NULL, NULL);
 
        if (!SSL_CTX_set_tmp_rsa(ctx, rsa)) {
-               ERROR("rlm_eap_tls: Couldn't set ephemeral RSA key");
+               ERROR("tls: Couldn't set ephemeral RSA key");
                return -1;
        }
 
@@ -965,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.
@@ -984,7 +1036,7 @@ static void cbtls_remove_session(SSL_CTX *ctx, SSL_SESSION *sess)
        size = sess->session_id_length;
        if (size > MAX_SESSION_SIZE) size = MAX_SESSION_SIZE;
 
-       fr_bin2hex(sess->session_id, buffer, size);
+       fr_bin2hex(buffer, sess->session_id, size);
 
        DEBUG2("  SSL: Removing session %s from the cache", buffer);
        conf = (fr_tls_server_conf_t *)SSL_CTX_get_app_data(ctx);
@@ -993,18 +1045,16 @@ static void cbtls_remove_session(SSL_CTX *ctx, SSL_SESSION *sess)
                char filename[256];
 
                /* remove session and any cached VPs */
-               rv = snprintf(filename, sizeof(filename), "%s%c%s.asn1",
-                       conf->session_cache_path, FR_DIR_SEP, buffer
-                       );
+               snprintf(filename, sizeof(filename), "%s%c%s.asn1",
+                        conf->session_cache_path, FR_DIR_SEP, buffer);
                rv = unlink(filename);
                if (rv != 0) {
-                       DEBUG2("  SSL: could not remove persisted session file %s: %s", filename, strerror(errno));
+                       DEBUG2("  SSL: could not remove persisted session file %s: %s", filename, fr_syserror(errno));
                }
                /* VPs might be absent; might not have been written to disk yet */
-               rv = snprintf(filename, sizeof(filename), "%s%c%s.vps",
-                       conf->session_cache_path, FR_DIR_SEP, buffer
-                       );
-               rv = unlink(filename);
+               snprintf(filename, sizeof(filename), "%s%c%s.vps",
+                        conf->session_cache_path, FR_DIR_SEP, buffer);
+               unlink(filename);
        }
 
        return;
@@ -1020,7 +1070,7 @@ static int cbtls_new_session(SSL *ssl, SSL_SESSION *sess)
        size = sess->session_id_length;
        if (size > MAX_SESSION_SIZE) size = MAX_SESSION_SIZE;
 
-       fr_bin2hex(sess->session_id, buffer, size);
+       fr_bin2hex(buffer, sess->session_id, size);
 
        DEBUG2("  SSL: adding session %s to cache", buffer);
 
@@ -1038,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;
@@ -1053,11 +1105,11 @@ static int cbtls_new_session(SSL *ssl, SSL_SESSION *sess)
                }
 
                /* open output file */
-               rv = snprintf(filename, sizeof(filename), "%s%c%s.asn1",
-                             conf->session_cache_path, FR_DIR_SEP, buffer);
+               snprintf(filename, sizeof(filename), "%s%c%s.asn1",
+                        conf->session_cache_path, FR_DIR_SEP, buffer);
                fd = open(filename, O_RDWR|O_CREAT|O_EXCL, 0600);
                if (fd < 0) {
-                       DEBUG2("  SSL: could not open session file %s: %s", filename, strerror(errno));
+                       DEBUG2("  SSL: could not open session file %s: %s", filename, fr_syserror(errno));
                        goto error;
                }
 
@@ -1066,7 +1118,7 @@ static int cbtls_new_session(SSL *ssl, SSL_SESSION *sess)
                while (todo > 0) {
                        rv = write(fd, p, todo);
                        if (rv < 1) {
-                               DEBUG2("  SSL: failed writing session: %s", strerror(errno));
+                               DEBUG2("  SSL: failed writing session: %s", fr_syserror(errno));
                                close(fd);
                                goto error;
                        }
@@ -1078,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;
 }
@@ -1090,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;
@@ -1098,11 +1151,12 @@ static SSL_SESSION *cbtls_get_session(SSL *ssl,
        size = len;
        if (size > MAX_SESSION_SIZE) size = MAX_SESSION_SIZE;
 
-       fr_bin2hex(data, buffer, size);
+       fr_bin2hex(buffer, data, size);
 
        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];
@@ -1111,9 +1165,8 @@ static SSL_SESSION *cbtls_get_session(SSL *ssl,
                VALUE_PAIR *vp;
 
                /* read in the cached VPs from the .vps file */
-               rv = snprintf(filename, sizeof(filename), "%s%c%s.vps",
-                       conf->session_cache_path, FR_DIR_SEP, buffer
-                       );
+               snprintf(filename, sizeof(filename), "%s%c%s.vps",
+                        conf->session_cache_path, FR_DIR_SEP, buffer);
                rv = pairlist_read(NULL, filename, &pairlist, 1);
                if (rv < 0) {
                        /* not safe to un-persist a session w/o VPs */
@@ -1122,18 +1175,17 @@ static SSL_SESSION *cbtls_get_session(SSL *ssl,
                }
 
                /* load the actual SSL session */
-               rv = snprintf(filename, sizeof(filename), "%s%c%s.asn1",
-                       conf->session_cache_path, FR_DIR_SEP, buffer
-                       );
+               snprintf(filename, sizeof(filename), "%s%c%s.asn1",
+                        conf->session_cache_path, FR_DIR_SEP, buffer);
                fd = open(filename, O_RDONLY);
-               if (fd == -1) {
-                       DEBUG2("  SSL: could not find persisted session file %s: %s", filename, strerror(errno));
+               if (fd < 0) {
+                       DEBUG2("  SSL: could not find persisted session file %s: %s", filename, fr_syserror(errno));
                        goto err;
                }
 
                rv = fstat(fd, &st);
-               if (rv == -1) {
-                       DEBUG2("  SSL: could not stat persisted session file %s: %s", filename, strerror(errno));
+               if (rv < 0) {
+                       DEBUG2("  SSL: could not stat persisted session file %s: %s", filename, fr_syserror(errno));
                        close(fd);
                        goto err;
                }
@@ -1150,7 +1202,7 @@ static SSL_SESSION *cbtls_get_session(SSL *ssl,
                while (todo > 0) {
                        rv = read(fd, p, todo);
                        if (rv < 1) {
-                               DEBUG2("  SSL: could not read from persisted session: %s", strerror(errno));
+                               DEBUG2("  SSL: could not read from persisted session: %s", fr_syserror(errno));
                                close(fd);
                                goto err;
                        }
@@ -1169,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);
        }
@@ -1256,19 +1308,23 @@ 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;
        }
-       
+
        DEBUG2("[ocsp] --> Responder URL = http://%s:%s%s", host, port, path);
 
        /* Setup BIO socket to OCSP responder */
@@ -1404,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!");
@@ -1424,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)
@@ -1439,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
@@ -1469,6 +1529,8 @@ int cbtls_verify(int ok, X509_STORE_CTX *ctx)
 {
        char subject[1024]; /* Used for the subject name */
        char issuer[1024]; /* Used for the issuer name */
+       char attribute[1024];
+       char value[1024];
        char common_name[1024];
        char cn_str[1024];
        char buf[64];
@@ -1488,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);
@@ -1510,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
         */
@@ -1533,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;
@@ -1542,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);
        }
 
 
@@ -1551,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);
        }
 
        /*
@@ -1565,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);
        }
 
        /*
@@ -1582,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;
@@ -1602,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;
@@ -1615,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.
@@ -1651,7 +1735,7 @@ int cbtls_verify(int ok, X509_STORE_CTX *ctx)
                BIO *out;
 
                out = BIO_new(BIO_s_mem());
-               strlcpy(subject, "TLS-Client-Cert-", sizeof(subject));
+               strlcpy(attribute, "TLS-Client-Cert-", sizeof(attribute));
 
                for (i = 0; i < sk_X509_EXTENSION_num(ext_list); i++) {
                        ASN1_OBJECT *obj;
@@ -1662,32 +1746,32 @@ int cbtls_verify(int ok, X509_STORE_CTX *ctx)
 
                        obj = X509_EXTENSION_get_object(ext);
                        i2a_ASN1_OBJECT(out, obj);
-                       len = BIO_read(out, subject + 16 , sizeof(subject) - 16 - 1);
+                       len = BIO_read(out, attribute + 16 , sizeof(attribute) - 16 - 1);
                        if (len <= 0) continue;
 
-                       subject[16 + len] = '\0';
+                       attribute[16 + len] = '\0';
 
                        X509V3_EXT_print(out, ext, 0, 0);
-                       len = BIO_read(out, issuer , sizeof(issuer) - 1);
+                       len = BIO_read(out, value , sizeof(value) - 1);
                        if (len <= 0) continue;
 
-                       issuer[len] = '\0';
+                       value[len] = '\0';
 
                        /*
                         *      Mash the OpenSSL name to our name, and
                         *      create the attribute.
                         */
-                       for (p = subject + 16; *p != '\0'; p++) {
+                       for (p = value + 16; *p != '\0'; p++) {
                                if (*p == ' ') *p = '-';
                        }
-                               
-                       vp = pairmake(NULL, certs, subject, issuer, T_OP_ADD);
+
+                       vp = pairmake(talloc_ctx, certs, attribute, value, T_OP_ADD);
                        if (vp) debug_pair_list(vp);
                }
 
                BIO_free_all(out);
        }
-       
+
        switch (ctx->error) {
 
        case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT:
@@ -1721,7 +1805,7 @@ int cbtls_verify(int ok, X509_STORE_CTX *ctx)
                 */
                if (conf->check_cert_issuer &&
                    (strcmp(issuer, conf->check_cert_issuer) != 0)) {
-                       AUTH("rlm_eap_tls: Certificate issuer (%s) does not match specified value (%s)!", issuer, conf->check_cert_issuer);
+                       AUTH("tls: Certificate issuer (%s) does not match specified value (%s)!", issuer, conf->check_cert_issuer);
                        my_ok = 0;
                }
 
@@ -1737,7 +1821,7 @@ int cbtls_verify(int ok, X509_STORE_CTX *ctx)
                        } else {
                                RDEBUG2("checking certificate CN (%s) with xlat'ed value (%s)", common_name, cn_str);
                                if (strcmp(cn_str, common_name) != 0) {
-                                       AUTH("rlm_eap_tls: Certificate CN (%s) does not match specified value (%s)!", common_name, cn_str);
+                                       AUTH("tls: Certificate CN (%s) does not match specified value (%s)!", common_name, cn_str);
                                        my_ok = 0;
                                }
                        }
@@ -1763,14 +1847,15 @@ int cbtls_verify(int ok, X509_STORE_CTX *ctx)
                        fd = mkstemp(filename);
                        if (fd < 0) {
                                RDEBUG("Failed creating file in %s: %s",
-                                      conf->verify_tmp_dir, strerror(errno));
+                                      conf->verify_tmp_dir, fr_syserror(errno));
                                break;
                        }
 
                        fp = fdopen(fd, "w");
                        if (!fp) {
+                               close(fd);
                                RDEBUG("Failed opening file %s: %s",
-                                      filename, strerror(errno));
+                                      filename, fr_syserror(errno));
                                break;
                        }
 
@@ -1788,13 +1873,10 @@ int cbtls_verify(int ok, X509_STORE_CTX *ctx)
                                goto do_unlink;
                        }
 
-                       RDEBUG("Verifying client certificate: %s",
-                              conf->verify_client_cert_cmd);
-                       if (radius_exec_program(conf->verify_client_cert_cmd,
-                                               request, 1, NULL, 0,
-                                               request->packet->vps,
-                                               NULL, 1) != 0) {
-                               AUTH("rlm_eap_tls: Certificate CN (%s) fails external verification!", common_name);
+                       RDEBUG("Verifying client certificate: %s", conf->verify_client_cert_cmd);
+                       if (radius_exec_program(request, conf->verify_client_cert_cmd, true, true, NULL, 0,
+                                               EXEC_TIMEOUT, request->packet->vps, NULL) != 0) {
+                               AUTH("tls: Certificate CN (%s) fails external verification!", common_name);
                                my_ok = 0;
                        } else {
                                RDEBUG("Client certificate CN %s passed external validation", common_name);
@@ -1839,8 +1921,8 @@ static X509_STORE *init_revocation_store(fr_tls_server_conf_t *conf)
        /* Load the CAs we trust */
        if (conf->ca_file || conf->ca_path)
                if(!X509_STORE_load_locations(store, conf->ca_file, conf->ca_path)) {
-                       ERROR("rlm_eap: X509_STORE error %s", ERR_error_string(ERR_get_error(), NULL));
-                       ERROR("rlm_eap_tls: Error reading Trusted root CA list %s",conf->ca_file );
+                       ERROR("tls: X509_STORE error %s", ERR_error_string(ERR_get_error(), NULL));
+                       ERROR("tls: Error reading Trusted root CA list %s",conf->ca_file );
                        return NULL;
                }
 
@@ -1899,11 +1981,87 @@ 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);
 }
 
+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, 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 */
+       OpenSSL_add_all_algorithms();   /* required for SHA2 in OpenSSL < 0.9.8o and 1.0.0.a */
+       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();
+}
 
 /*
  *     Create Global context SSL and use it in every new session
@@ -1912,7 +2070,7 @@ static void sess_free_vps(UNUSED void *parent, void *data_ptr,
  *     - 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;
@@ -1921,13 +2079,6 @@ static SSL_CTX *init_tls_ctx(fr_tls_server_conf_t *conf, int client)
        int type;
 
        /*
-        *      Add all the default ciphers and message digests
-        *      Create our context.
-        */
-       SSL_library_init();
-       SSL_load_error_strings();
-
-       /*
         *      SHA256 is in all versions of OpenSSL, but isn't
         *      initialized by default.  It's needed for WiMAX
         *      certificates.
@@ -1937,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
@@ -1964,17 +2123,14 @@ 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];
-                       const long max_password_len = 128;
-                       snprintf(cmd, sizeof(cmd) - 1,
-                                        "/usr/sbin/certadmin --get-private-key-passphrase \"%s\"",
-                                        conf->private_key_file);
+                       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);
 
-                       DEBUG2("rlm_eap: Getting private key passphrase using command \"%s\"", cmd);
+                       DEBUG2("tls: Getting private key passphrase using command \"%s\"", cmd);
 
                        FILE* cmd_pipe = popen(cmd, "r");
                        if (!cmd_pipe) {
@@ -1983,28 +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';
-                       DEBUG2("rlm_eap:  Password from command = \"%s\"", conf->private_key_password);
+                       password[strlen(password) - 1] = '\0';
+
+                       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) ||
@@ -2015,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 ||
@@ -2027,23 +2211,23 @@ 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);
                if (strlen(conf->psk_password) > (2 * PSK_MAX_PSK_LEN)) {
                        ERROR("psk_hexphrase is too long (max %d)",
                               PSK_MAX_PSK_LEN);
-                       return NULL;                    
+                       return NULL;
                }
 
-               hex_len = fr_hex2bin(conf->psk_password,
-                                    (uint8_t *) buffer, 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;                    
+                       return NULL;
                }
 
                goto post_ca;
@@ -2082,8 +2266,8 @@ static SSL_CTX *init_tls_ctx(fr_tls_server_conf_t *conf, int client)
 load_ca:
        if (conf->ca_file || conf->ca_path) {
                if (!SSL_CTX_load_verify_locations(ctx, conf->ca_file, conf->ca_path)) {
-                       ERROR("rlm_eap: SSL error %s", ERR_error_string(ERR_get_error(), NULL));
-                       ERROR("rlm_eap_tls: Error reading Trusted root CA list %s",conf->ca_file );
+                       ERROR("tls: SSL error %s", ERR_error_string(ERR_get_error(), NULL));
+                       ERROR("tls: Error reading Trusted root CA list %s",conf->ca_file );
                        return NULL;
                }
        }
@@ -2096,7 +2280,7 @@ load_ca:
                               ERR_error_string(ERR_get_error(), NULL));
                        return NULL;
                }
-               
+
                /*
                 * Check if the loaded private key is the right one
                 */
@@ -2170,7 +2354,7 @@ post_ca:
 
        /*
         *      Callbacks, etc. for session resumption.
-        */                                             
+        */
        if (conf->session_cache_enable) {
                SSL_CTX_sess_set_new_cb(ctx, cbtls_new_session);
                SSL_CTX_sess_set_get_cb(ctx, cbtls_get_session);
@@ -2179,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);
        }
 
        /*
@@ -2186,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("rlm_eap: SSL error %s", ERR_error_string(ERR_get_error(), NULL));
-           ERROR("rlm_eap_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
 
@@ -2211,9 +2397,9 @@ post_ca:
 
        /* Load randomness */
        if (conf->random_file) {
-               if (!(RAND_load_file(conf->random_file, 1024*1024))) {
-                       ERROR("rlm_eap: SSL error %s", ERR_error_string(ERR_get_error(), NULL));
-                       ERROR("rlm_eap_tls: Error loading randomness");
+               if (!(RAND_load_file(conf->random_file, 1024*10))) {
+                       ERROR("tls: SSL error %s", ERR_error_string(ERR_get_error(), NULL));
+                       ERROR("tls: Error loading randomness");
                        return NULL;
                }
        }
@@ -2223,7 +2409,7 @@ post_ca:
         */
        if (conf->cipher_list) {
                if (!SSL_CTX_set_cipher_list(ctx, conf->cipher_list)) {
-                       ERROR("rlm_eap_tls: Error setting cipher list");
+                       ERROR("tls: Error setting cipher list");
                        return NULL;
                }
        }
@@ -2280,10 +2466,8 @@ post_ca:
  *     added to automatically free the data when the CONF_SECTION
  *     is freed.
  */
-static void tls_server_conf_free(fr_tls_server_conf_t *conf)
+static int _tls_server_conf_free(fr_tls_server_conf_t *conf)
 {
-       if (!conf) return;
-
        if (conf->ctx) SSL_CTX_free(conf->ctx);
 
 #ifdef HAVE_OPENSSL_OCSP_H
@@ -2294,7 +2478,22 @@ static void tls_server_conf_free(fr_tls_server_conf_t *conf)
 #ifndef NDEBUG
        memset(conf, 0, sizeof(*conf));
 #endif
-       talloc_free(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;
 }
 
 
@@ -2312,15 +2511,11 @@ 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;
-       }
+       conf = tls_server_conf_alloc(cs);
 
        if (cf_section_parse(cs, conf, tls_server_config) < 0) {
        error:
-               tls_server_conf_free(conf);
+               talloc_free(conf);
                return NULL;
        }
 
@@ -2329,26 +2524,6 @@ fr_tls_server_conf_t *tls_server_conf_parse(CONF_SECTION *cs)
         */
        if (conf->fragment_size < 100) conf->fragment_size = 100;
 
-       /*
-        *      This magic makes the administrators life HUGELY easier
-        *      on initial deployments.
-        *
-        *      If the server starts up in debugging mode, AND the
-        *      bootstrap command is configured, AND it exists, AND
-        *      there is no server certificate
-        */
-       if (conf->make_cert_command && (debug_flag >= 2)) {
-               struct stat buf;
-
-               if ((stat(conf->make_cert_command, &buf) == 0) &&
-                   (stat(conf->certificate_file, &buf) < 0) &&
-                   (errno == ENOENT) &&
-                   (radius_exec_program(conf->make_cert_command, NULL, 1,
-                                        NULL, 0, NULL, NULL, 0) != 0)) {
-                       goto error;
-               }
-       }
-
        if (!conf->private_key_file) {
                ERROR("TLS Server requires a private key file");
                goto error;
@@ -2362,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;
        }
@@ -2376,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) {
@@ -2387,7 +2566,7 @@ fr_tls_server_conf_t *tls_server_conf_parse(CONF_SECTION *cs)
 
        if (conf->verify_tmp_dir) {
                if (chmod(conf->verify_tmp_dir, S_IRWXU) < 0) {
-                       ERROR("Failed changing permissions on %s: %s", conf->verify_tmp_dir, strerror(errno));
+                       ERROR("Failed changing permissions on %s: %s", conf->verify_tmp_dir, fr_syserror(errno));
                        goto error;
                }
        }
@@ -2400,7 +2579,7 @@ fr_tls_server_conf_t *tls_server_conf_parse(CONF_SECTION *cs)
        /*
         *      Cache conf in cs in case we're asked to parse this again.
         */
-       cf_data_add(cs, "tls-conf", conf, (void *)(void *) tls_server_conf_free);
+       cf_data_add(cs, "tls-conf", conf, NULL);
 
        return conf;
 }
@@ -2415,15 +2594,11 @@ 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;
-       }
+       conf = tls_server_conf_alloc(cs);
 
        if (cf_section_parse(cs, conf, tls_client_config) < 0) {
        error:
-               tls_server_conf_free(conf);
+               talloc_free(conf);
                return NULL;
        }
 
@@ -2435,20 +2610,25 @@ 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) {
                goto error;
        }
 
-       cf_data_add(cs, "tls-conf", conf, (void *)(void *) tls_server_conf_free);
+       cf_data_add(cs, "tls-conf", conf, NULL);
 
        return conf;
 }
@@ -2457,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
@@ -2482,10 +2665,10 @@ 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;
                }
-               
+
                /*
                 *      Else resumption IS allowed, so we store the
                 *      user data in the cache.
@@ -2498,15 +2681,18 @@ int tls_success(tls_session_t *ssn, REQUEST *request)
                size = ssn->ssl->session->session_id_length;
                if (size > MAX_SESSION_SIZE) size = MAX_SESSION_SIZE;
 
-               fr_bin2hex(ssn->ssl->session->session_id, buffer, size);
+               fr_bin2hex(buffer, ssn->ssl->session->session_id, size);
+
+               vp = paircopy2(talloc_ctx, request->reply->vps, PW_USER_NAME, 0, TAG_ANY);
+               if (vp) pairadd(&vps, vp);
 
-               vp = paircopy2(NULL, request->reply->vps, PW_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->packet->vps, PW_STRIPPED_USER_NAME, 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);
@@ -2519,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) {
@@ -2536,17 +2722,17 @@ int tls_success(tls_session_t *ssn, REQUEST *request)
                                        );
                                vp_file = fopen(filename, "w");
                                if (vp_file == NULL) {
-                                       RDEBUG2("Could not write session VPs to persistent cache: %s", strerror(errno));
+                                       RDEBUG2("Could not write session VPs to persistent cache: %s", fr_syserror(errno));
                                } else {
                                        vp_cursor_t cursor;
                                        /* generate a dummy user-style entry which is easy to read back */
                                        fprintf(vp_file, "# SSL cached session\n");
                                        fprintf(vp_file, "%s\n", buffer);
-                                       for (vp = paircursor(&cursor, &vps);
+                                       for (vp = fr_cursor_init(&cursor, &vps);
                                             vp;
-                                            vp = pairnext(&cursor)) {
+                                            vp = fr_cursor_next(&cursor)) {
                                                vp_prints(buf, sizeof(buf), vp);
-                                               fprintf(vp_file, "\t%s%s\n", buf, ",");
+                                               fprintf(vp_file, "\t%s,\n", buf);
                                        }
                                        fclose(vp_file);
                                }
@@ -2568,7 +2754,7 @@ int tls_success(tls_session_t *ssn, REQUEST *request)
                size = ssn->ssl->session->session_id_length;
                if (size > MAX_SESSION_SIZE) size = MAX_SESSION_SIZE;
 
-               fr_bin2hex(ssn->ssl->session->session_id, buffer, size);
+               fr_bin2hex(buffer, ssn->ssl->session->session_id, size);
 
                vps = SSL_SESSION_get_ex_data(ssn->ssl->session,
                                             FR_TLS_EX_INDEX_VPS);
@@ -2578,13 +2764,13 @@ int tls_success(tls_session_t *ssn, REQUEST *request)
 
                } else {
                        vp_cursor_t cursor;
-                       
+
                        RDEBUG("Adding cached attributes for session %s:", buffer);
                        debug_pair_list(vps);
 
-                       for (vp = paircursor(&cursor, &vps);
+                       for (vp = fr_cursor_init(&cursor, &vps);
                             vp;
-                            vp = pairnext(&cursor)) {
+                            vp = fr_cursor_next(&cursor)) {
                                /*
                                 *      TLS-* attrs get added back to
                                 *      the request list.
@@ -2596,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));
                                }
                        }
 
@@ -2635,11 +2821,11 @@ void tls_fail(tls_session_t *ssn)
 
 fr_tls_status_t tls_application_data(tls_session_t *ssn,
                                     REQUEST *request)
-                               
+
 {
        int err;
 
-       /*      
+       /*
         *      Decrypt the complete record.
         */
        err = BIO_write(ssn->into_ssl, ssn->dirty_in.data,
@@ -2650,14 +2836,14 @@ fr_tls_status_t tls_application_data(tls_session_t *ssn,
                       ssn->dirty_in.used, err);
                return FR_TLS_FAIL;
        }
-       
+
        /*
         *      Clear the dirty buffer now that we are done with it
         *      and init the clean_out buffer to store decrypted data
         */
        record_init(&ssn->dirty_in);
        record_init(&ssn->clean_out);
-       
+
        /*
         *      Read (and decrypt) the tunneled data from the
         *      SSL session, and put it into the decrypted
@@ -2665,12 +2851,12 @@ fr_tls_status_t tls_application_data(tls_session_t *ssn,
         */
        err = SSL_read(ssn->ssl, ssn->clean_out.data,
                       sizeof(ssn->clean_out.data));
-       
+
        if (err < 0) {
                int code;
 
                RDEBUG("SSL_read Error");
-               
+
                code = SSL_get_error(ssn->ssl, err);
                switch (code) {
                case SSL_ERROR_WANT_READ:
@@ -2682,7 +2868,8 @@ fr_tls_status_t tls_application_data(tls_session_t *ssn,
                        break;
 
                default:
-                       DEBUG("Error in fragmentation logic: ?");
+                       DEBUG("Error in fragmentation logic: %s",
+                             ERR_error_string(code, NULL));
 
                        /*
                         *      FIXME: Call int_ssl_check?
@@ -2691,16 +2878,16 @@ fr_tls_status_t tls_application_data(tls_session_t *ssn,
                }
                return FR_TLS_FAIL;
        }
-       
+
        if (err == 0) {
-               RWDEBUG("No data inside of the tunnel.");
+               RWDEBUG("No data inside of the tunnel");
        }
-       
+
        /*
         *      Passed all checks, successfully decrypted data
         */
        ssn->clean_out.used = err;
-       
+
        return FR_TLS_OK;
 }
 
@@ -2716,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;
        }
 
@@ -2755,7 +2942,7 @@ fr_tls_status_t tls_ack_handler(tls_session_t *ssn, REQUEST *request)
        case application_data:
                RDEBUG2("ACK handshake fragment handler in application data");
                return FR_TLS_REQUEST;
-                                               
+
                /*
                 *      For the rest of the conditions, switch over
                 *      to the default section below.