* Copyright 2006 The FreeRADIUS server project
*/
-#include <freeradius-devel/ident.h>
RCSID("$Id$")
+USES_APPLE_DEPRECATED_API /* OpenSSL API has been deprecated by Apple */
#include <freeradius-devel/radiusd.h>
#include <freeradius-devel/process.h>
#ifdef HAVE_UTIME_H
#include <utime.h>
#endif
+#include <ctype.h>
#ifdef WITH_TLS
#ifdef HAVE_OPENSSL_RAND_H
#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);
static void record_close(record_t *buf);
-static unsigned int record_plus(record_t *buf, const void *ptr,
+static unsigned int record_plus(record_t *buf, void const *ptr,
unsigned int size);
static unsigned int record_minus(record_t *buf, void *ptr,
unsigned int size);
#ifdef PSK_MAX_IDENTITY_LEN
+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 const char *hint,
+static unsigned int psk_client_callback(SSL *ssl, UNUSED char const *hint,
char *identity, unsigned int max_identity_len,
unsigned char *psk, unsigned int max_psk_len)
{
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
{
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
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;
}
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
* 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));
}
if ((new_tls = SSL_new(conf->ctx)) == NULL) {
- radlog(L_ERR, "SSL: Error creating new SSL: %s",
+ ERROR("SSL: Error creating new SSL: %s",
ERR_error_string(ERR_get_error(), NULL));
return NULL;
}
/* 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;
if (conf->session_cache_enable) {
state->allow_session_resumption = 1; /* otherwise it's zero */
}
-
+
RDEBUG2("Initiate");
return state;
/*
* Print out some text describing the error.
*/
-static int int_ssl_check(REQUEST *request, SSL *s, int ret, const char *text)
+static int int_ssl_check(REQUEST *request, SSL *s, int ret, char const *text)
{
int e;
unsigned long l;
if ((l = ERR_get_error()) != 0) {
- const char *p = ERR_error_string(l, NULL);
+ char const *p = ERR_error_string(l, NULL);
- if (request && p) RDEBUGE("SSL says: %s", p);
+ if (request && p) REDEBUG("SSL says: %s", p);
}
e = SSL_get_error(s, ret);
* being regarded as "dead".
*/
case SSL_ERROR_SYSCALL:
- radlog(L_ERR, "SSL: %s failed in a system call (%d), TLS session fails.",
+ ERROR("SSL: %s failed in a system call (%d), TLS session fails.",
text, ret);
return 0;
case SSL_ERROR_SSL:
- radlog(L_ERR, "SSL: %s failed inside of TLS (%d), TLS session fails.",
+ ERROR("SSL: %s failed inside of TLS (%d), TLS session fails.",
text, ret);
return 0;
* them - so "politely inform" the caller that
* the code needs updating here.
*/
- radlog(L_ERR, "SSL: FATAL SSL error ..... %d\n", e);
+ ERROR("SSL: FATAL SSL error ..... %d\n", e);
return 0;
}
{
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,
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");
}
}
/*
- * 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)
}
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);
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;
* Copy data to the intermediate buffer, before we send
* it somewhere.
*/
-static unsigned int record_plus(record_t *rec, const void *ptr,
+static unsigned int record_plus(record_t *rec, void const *ptr,
unsigned int size)
{
unsigned int added = MAX_RECORD_SIZE - rec->used;
void tls_session_information(tls_session_t *tls_session)
{
- const char *str_write_p, *str_version, *str_content_type = "";
- const char *str_details1 = "", *str_details2= "";
+ char const *str_write_p, *str_version, *str_content_type = "";
+ char const *str_details1 = "", *str_details2= "";
REQUEST *request;
/*
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;
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;
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, (const void *) cache_config },
+ { "cache", FR_CONF_POINTER(PW_TYPE_SUBSECTION, NULL), (void const *) cache_config },
- { "verify", PW_TYPE_SUBSECTION, 0, NULL, (const void *) 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, (const void *) ocsp_config },
+ { "ocsp", FR_CONF_POINTER(PW_TYPE_SUBSECTION, NULL), (void const *) ocsp_config },
#endif
{ NULL, -1, 0, NULL, NULL } /* end the list */
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 */
};
DH *dh = NULL;
BIO *bio;
+ if (!file) return 0;
+
if ((bio = BIO_new_file(file, "r")) == NULL) {
- radlog(L_ERR, "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) {
- DEBUG2W("rlm_eap_tls: Unable to set DH parameters. DH cipher suites may not work!");
- DEBUG2W("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) {
- radlog(L_ERR, "rlm_eap_tls: Unable to set DH parameters");
+ ERROR("tls: Unable to set DH parameters");
DH_free(dh);
return -1;
}
rsa = RSA_generate_key(512, RSA_F4, NULL, NULL);
if (!SSL_CTX_set_tmp_rsa(ctx, rsa)) {
- radlog(L_ERR, "rlm_eap_tls: Couldn't set ephemeral RSA key");
+ ERROR("tls: Couldn't set ephemeral RSA key");
return -1;
}
* 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.
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);
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;
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);
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;
}
/* 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;
}
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;
}
}
error:
- if (sess_blob) talloc_free(sess_blob);
+ free(sess_blob);
return 0;
}
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;
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];
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
- );
- rv = pairlist_read(filename, &pairlist, 1);
+ 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 */
DEBUG2(" SSL: could not load persisted VPs for session %s", buffer);
}
/* 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;
}
- sess_data = talloc_array(conf, unsigned char, st.st_size);
+ sess_data = talloc_array(NULL, unsigned char, st.st_size);
if (!sess_data) {
DEBUG2(" SSL: could not alloc buffer for persisted session len=%d", (int) st.st_size);
close(fd);
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;
}
/* openssl mutates &p */
p = sess_data;
- sess = d2i_SSL_SESSION(NULL, (const unsigned char **) &p, st.st_size);
+ sess = d2i_SSL_SESSION(NULL, (unsigned char const **)(void **) &p, st.st_size);
if (!sess) {
DEBUG2(" SSL: OpenSSL failed to load persisted session: %s", ERR_error_string(ERR_get_error(), NULL));
}
/* cache the VPs into the session */
- vp = paircopy(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);
}
*/
/* 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 */
/* Send OCSP request and wait for response */
resp = OCSP_sendreq_bio(cbio, path, req);
if (!resp) {
- radlog(L_ERR, "Error: Couldn't get OCSP response");
+ ERROR("Couldn't get OCSP response");
ocsp_ok = 2;
goto ocsp_end;
}
rc = BIO_do_connect(cbio);
if ((rc <= 0) && ((!conf->ocsp_timeout) || !BIO_should_retry(cbio))) {
- radlog(L_ERR, "Error: Couldn't connect to OCSP responder");
+ ERROR("Couldn't connect to OCSP responder");
ocsp_ok = 2;
goto ocsp_end;
}
ctx = OCSP_sendreq_new(cbio, path, req, -1);
if (!ctx) {
- radlog(L_ERR, "Error: Couldn't send OCSP request");
+ ERROR("Couldn't send OCSP request");
ocsp_ok = 2;
goto ocsp_end;
}
} while ((rc == -1) && BIO_should_retry(cbio));
if (conf->ocsp_timeout && (rc == -1) && BIO_should_retry(cbio)) {
- radlog(L_ERR, "Error: OCSP response timed out");
+ ERROR("OCSP response timed out");
ocsp_ok = 2;
goto ocsp_end;
}
OCSP_REQ_CTX_free(ctx);
if (rc == 0) {
- radlog(L_ERR, "Error: Couldn't get OCSP response");
+ ERROR("Couldn't get OCSP response");
ocsp_ok = 2;
goto ocsp_end;
}
status = OCSP_response_status(resp);
DEBUG2("[ocsp] --> Response status: %s",OCSP_response_status_str(status));
if(status != OCSP_RESPONSE_STATUS_SUCCESSFUL) {
- radlog(L_ERR, "Error: OCSP response status: %s", OCSP_response_status_str(status));
+ ERROR("OCSP response status: %s", OCSP_response_status_str(status));
goto ocsp_end;
}
bresp = OCSP_response_get1_basic(resp);
if(conf->ocsp_use_nonce && OCSP_check_nonce(req, bresp)!=1) {
- radlog(L_ERR, "Error: OCSP response has wrong nonce value");
+ ERROR("OCSP response has wrong nonce value");
goto ocsp_end;
}
if(OCSP_basic_verify(bresp, NULL, store, 0)!=1){
- radlog(L_ERR, "Error: Couldn't verify OCSP basic response");
+ ERROR("Couldn't verify OCSP basic response");
goto ocsp_end;
}
/* Verify OCSP cert status */
if(!OCSP_resp_find_status(bresp, certid, &status, &reason,
&rev, &thisupd, &nextupd)) {
- radlog(L_ERR, "ERROR: No Status found.\n");
+ ERROR("No Status found.\n");
goto 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!");
/*
* For creating certificate attributes.
*/
-static const char *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)
#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
{
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];
X509 *client_cert;
+ X509_CINF *client_inf;
+ STACK_OF(X509_EXTENSION) *ext_list;
SSL *ssl;
int err, depth, lookup, loc;
fr_tls_server_conf_t *conf;
X509_STORE *ocsp_store = NULL;
X509 *issuer_cert;
#endif
- tls_session_t *ssn;
+ TALLOC_CTX *talloc_ctx;
client_cert = X509_STORE_CTX_get_current_cert(ctx);
err = X509_STORE_CTX_get_error(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
- ssn = (tls_session_t *)SSL_get_ex_data(ssl, FR_TLS_EX_INDEX_SSN);
+
+ talloc_ctx = SSL_get_ex_data(ssl, FR_TLS_EX_INDEX_TALLOC);
/*
* Get the Serial Number
* 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;
sprintf(p, "%02x", (unsigned int)sn->data[i]);
p += 2;
}
- pairmake(ssn, 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);
}
*/
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(ssn, 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);
}
/*
X509_NAME_oneline(X509_get_subject_name(client_cert), subject,
sizeof(subject));
subject[sizeof(subject) - 1] = '\0';
- if (identity && (lookup <= 1) && subject[0] &&
- (strlen(subject) < MAX_STRING_LEN)) {
- pairmake(ssn, 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] &&
- (strlen(issuer) < MAX_STRING_LEN)) {
- pairmake(ssn, 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);
}
/*
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] &&
- (strlen(common_name) < MAX_STRING_LEN)) {
- pairmake(ssn, 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;
GENERAL_NAME *name = sk_GENERAL_NAME_value(names, i);
switch (name->type) {
+#ifdef GEN_EMAIL
case GEN_EMAIL:
- if (ASN1_STRING_length(name->d.rfc822Name) >= MAX_STRING_LEN)
- break;
-
- pairmake(ssn, 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;
if (names != NULL)
sk_GENERAL_NAME_free(names);
}
-#endif /* GEN_EMAIL */
/*
* If the CRL has expired, that might still be OK.
}
if (!my_ok) {
- const char *p = X509_verify_cert_error_string(err);
- radlog(L_ERR,"--> verify error:num=%d:%s\n",err, p);
- RDEBUGE("SSL says error %d : %s", err, p);
+ char const *p = X509_verify_cert_error_string(err);
+ ERROR("--> verify error:num=%d:%s\n",err, p);
+ REDEBUG("SSL says error %d : %s", err, p);
return my_ok;
}
+ if (lookup == 0) {
+ client_inf = client_cert->cert_info;
+ ext_list = client_inf->extensions;
+ } else {
+ ext_list = NULL;
+ }
+
+ /*
+ * Grab the X509 extensions, and create attributes out of them.
+ * For laziness, we re-use the OpenSSL names
+ */
+ if (sk_X509_EXTENSION_num(ext_list) > 0) {
+ int i, len;
+ char *p;
+ BIO *out;
+
+ out = BIO_new(BIO_s_mem());
+ strlcpy(attribute, "TLS-Client-Cert-", sizeof(attribute));
+
+ for (i = 0; i < sk_X509_EXTENSION_num(ext_list); i++) {
+ ASN1_OBJECT *obj;
+ X509_EXTENSION *ext;
+ VALUE_PAIR *vp;
+
+ ext = sk_X509_EXTENSION_value(ext_list, i);
+
+ obj = X509_EXTENSION_get_object(ext);
+ i2a_ASN1_OBJECT(out, obj);
+ len = BIO_read(out, attribute + 16 , sizeof(attribute) - 16 - 1);
+ if (len <= 0) continue;
+
+ attribute[16 + len] = '\0';
+
+ X509V3_EXT_print(out, ext, 0, 0);
+ len = BIO_read(out, value , sizeof(value) - 1);
+ if (len <= 0) continue;
+
+ value[len] = '\0';
+
+ /*
+ * Mash the OpenSSL name to our name, and
+ * create the attribute.
+ */
+ for (p = value + 16; *p != '\0'; p++) {
+ if (*p == ' ') *p = '-';
+ }
+
+ 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:
- radlog(L_ERR, "issuer= %s\n", issuer);
+ ERROR("issuer= %s\n", issuer);
break;
case X509_V_ERR_CERT_NOT_YET_VALID:
case X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD:
- radlog(L_ERR, "notBefore=");
+ ERROR("notBefore=");
#if 0
ASN1_TIME_print(bio_err, X509_get_notBefore(ctx->current_cert));
#endif
break;
case X509_V_ERR_CERT_HAS_EXPIRED:
case X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD:
- radlog(L_ERR, "notAfter=");
+ ERROR("notAfter=");
#if 0
ASN1_TIME_print(bio_err, X509_get_notAfter(ctx->current_cert));
#endif
*/
if (conf->check_cert_issuer &&
(strcmp(issuer, conf->check_cert_issuer) != 0)) {
- radlog(L_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;
}
* previous checks passed.
*/
if (my_ok && conf->check_cert_cn) {
- if (!radius_xlat(cn_str, sizeof(cn_str), conf->check_cert_cn, request, NULL, NULL)) {
- radlog(L_ERR, "rlm_eap_tls (%s): xlat failed.",
- conf->check_cert_cn);
+ if (radius_xlat(cn_str, sizeof(cn_str), request, conf->check_cert_cn, NULL, NULL) < 0) {
/* if this fails, fail the verification */
my_ok = 0;
} else {
RDEBUG2("checking certificate CN (%s) with xlat'ed value (%s)", common_name, cn_str);
if (strcmp(cn_str, common_name) != 0) {
- radlog(L_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;
}
}
if (my_ok && conf->ocsp_enable){
RDEBUG2("--> Starting OCSP Request");
if(X509_STORE_CTX_get1_issuer(&issuer_cert, ctx, client_cert)!=1) {
- radlog(L_ERR, "Error: Couldn't get issuer_cert for %s", common_name);
+ ERROR("Couldn't get issuer_cert for %s", common_name);
}
my_ok = ocsp_check(ocsp_store, issuer_cert, client_cert, conf);
}
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;
}
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) {
- radlog(L_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);
/* Load the CAs we trust */
if (conf->ca_file || conf->ca_path)
if(!X509_STORE_load_locations(store, conf->ca_file, conf->ca_path)) {
- radlog(L_ERR, "rlm_eap: X509_STORE error %s", ERR_error_string(ERR_get_error(), NULL));
- radlog(L_ERR, "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;
}
#if OPENSSL_VERSION_NUMBER >= 0x0090800fL
#ifndef OPENSSL_NO_ECDH
-static int set_ecdh_curve(SSL_CTX *ctx, const char *ecdh_curve)
+static int set_ecdh_curve(SSL_CTX *ctx, char const *ecdh_curve)
{
int nid;
EC_KEY *ecdh;
nid = OBJ_sn2nid(ecdh_curve);
if (!nid) {
- radlog(L_ERR, "Unknown ecdh_curve \"%s\"", ecdh_curve);
+ ERROR("Unknown ecdh_curve \"%s\"", ecdh_curve);
return -1;
}
ecdh = EC_KEY_new_by_curve_name(nid);
if (!ecdh) {
- radlog(L_ERR, "Unable to create new curve \"%s\"", ecdh_curve);
+ ERROR("Unable to create new curve \"%s\"", ecdh_curve);
return -1;
}
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
* - 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;
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.
#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
* for our special string which indicates we should get the password
* programmatically.
*/
- const char* special_string = "Apple:UseCertAdmin";
- if (strncmp(conf->private_key_password,
- special_string,
- strlen(special_string)) == 0)
- {
+ char const* special_string = "Apple:UseCertAdmin";
+ 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) {
- radlog(L_ERR, "TLS: %s command failed. Unable to get private_key_password", cmd);
- radlog(L_ERR, "Error reading private_key_file %s", conf->private_key_file);
+ ERROR("TLS: %s command failed. Unable to get private_key_password", cmd);
+ ERROR("Error reading private_key_file %s", conf->private_key_file);
return NULL;
}
- talloc_free(conf->private_key_password);
- conf->private_key_password = talloc_array(conf, char, max_password_len);
- if (!conf->private_key_password) {
- radlog(L_ERR, "TLS: Can't allocate space for private_key_password");
- radlog(L_ERR, "TLS: Error reading private_key_file %s", conf->private_key_file);
+ 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) ||
(conf->psk_password && !*conf->psk_password)) {
- radlog(L_ERR, "Invalid PSK Configuration: psk_identity or psk_password are empty");
+ ERROR("Invalid PSK Configuration: psk_identity or psk_password are empty");
return NULL;
}
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 ||
conf->ca_file || conf->ca_path) {
- radlog(L_ERR, "When PSKs are used, No certificate configuration is permitted");
+ ERROR("When PSKs are used, No certificate configuration is permitted");
return NULL;
}
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)) {
- radlog(L_ERR, "psk_hexphrase is too long (max %d)",
+ 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)) {
- radlog(L_ERR, "psk_hexphrase is not all hex");
- return NULL;
+ ERROR("psk_hexphrase is not all hex");
+ return NULL;
}
goto post_ca;
}
#else
- client = client; /* -Wunused */
+ (void) client; /* -Wunused */
#endif
/*
if (type == SSL_FILETYPE_PEM) {
if (!(SSL_CTX_use_certificate_chain_file(ctx, conf->certificate_file))) {
- radlog(L_ERR, "Error reading certificate file %s:%s",
+ ERROR("Error reading certificate file %s:%s",
conf->certificate_file,
ERR_error_string(ERR_get_error(), NULL));
return NULL;
}
} else if (!(SSL_CTX_use_certificate_file(ctx, conf->certificate_file, type))) {
- radlog(L_ERR, "Error reading certificate file %s:%s",
+ ERROR("Error reading certificate file %s:%s",
conf->certificate_file,
ERR_error_string(ERR_get_error(), NULL));
return NULL;
load_ca:
if (conf->ca_file || conf->ca_path) {
if (!SSL_CTX_load_verify_locations(ctx, conf->ca_file, conf->ca_path)) {
- radlog(L_ERR, "rlm_eap: SSL error %s", ERR_error_string(ERR_get_error(), NULL));
- radlog(L_ERR, "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;
}
}
if (conf->private_key_file) {
if (!(SSL_CTX_use_PrivateKey_file(ctx, conf->private_key_file, type))) {
- radlog(L_ERR, "Failed reading private key file %s:%s",
+ ERROR("Failed reading private key file %s:%s",
conf->private_key_file,
ERR_error_string(ERR_get_error(), NULL));
return NULL;
}
-
+
/*
* Check if the loaded private key is the right one
*/
if (!SSL_CTX_check_private_key(ctx)) {
- radlog(L_ERR, "Private key does not match the certificate public key");
+ ERROR("Private key does not match the certificate public key");
return NULL;
}
}
/*
* 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);
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);
}
/*
*/
#ifdef X509_V_FLAG_CRL_CHECK
if (conf->check_crl) {
- certstore = SSL_CTX_get_cert_store(ctx);
- if (certstore == NULL) {
- radlog(L_ERR, "rlm_eap: SSL error %s", ERR_error_string(ERR_get_error(), NULL));
- radlog(L_ERR, "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
}
/* Load randomness */
- if (!(RAND_load_file(conf->random_file, 1024*1024))) {
- radlog(L_ERR, "rlm_eap: SSL error %s", ERR_error_string(ERR_get_error(), NULL));
- radlog(L_ERR, "rlm_eap_tls: Error loading randomness");
- return NULL;
+ if (conf->random_file) {
+ 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;
+ }
}
/*
*/
if (conf->cipher_list) {
if (!SSL_CTX_set_cipher_list(ctx, conf->cipher_list)) {
- radlog(L_ERR, "rlm_eap_tls: Error setting cipher list");
+ ERROR("tls: Error setting cipher list");
return NULL;
}
}
* 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
#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;
}
*/
conf = cf_data_find(cs, "tls-conf");
if (conf) {
- DEBUG(" debug: Using cached TLS configuration from previous invocation");
+ DEBUG("Using cached TLS configuration from previous invocation");
return conf;
}
- conf = talloc_zero(cs, fr_tls_server_conf_t);
- if (!conf) {
- radlog(L_ERR, "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;
}
*/
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) {
- radlog(L_ERR, "TLS Server requires a private key file");
+ ERROR("TLS Server requires a private key file");
goto error;
}
if (!conf->certificate_file) {
- radlog(L_ERR, "TLS Server requires a certificate file");
+ ERROR("TLS Server requires a certificate file");
goto error;
}
/*
* Initialize TLS
*/
- conf->ctx = init_tls_ctx(conf, 0);
+ conf->ctx = tls_init_ctx(conf, 0);
if (conf->ctx == NULL) {
goto error;
}
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) {
if (conf->verify_tmp_dir) {
if (chmod(conf->verify_tmp_dir, S_IRWXU) < 0) {
- radlog(L_ERR, "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;
}
}
if (conf->verify_client_cert_cmd && !conf->verify_tmp_dir) {
- radlog(L_ERR, "You MUST set the verify directory in order to use verify_client_cmd");
+ ERROR("You MUST set the verify directory in order to use verify_client_cmd");
goto error;
}
/*
* 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;
}
conf = cf_data_find(cs, "tls-conf");
if (conf) {
- DEBUG(" debug: Using cached TLS configuration from previous invocation");
+ DEBUG("Using cached TLS configuration from previous invocation");
return conf;
}
- conf = talloc_zero(cs, fr_tls_server_conf_t);
- if (!conf) {
- radlog(L_ERR, "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;
}
/*
* 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;
}
{
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
* 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.
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(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(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(request->reply->vps, PW_CACHED_SESSION_POLICY, 0, TAG_ANY);
+
+ vp = paircopy2(talloc_ctx, request->reply->vps, PW_CHARGEABLE_USER_IDENTITY, 0, TAG_ANY);
+ if (vp) pairadd(&vps, vp);
+
+ 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);
* Hmm... the certs should probably be session data.
*/
if (certs) {
- pairadd(&vps, paircopy(*certs));
+ /*
+ * @todo: some go into reply, others into
+ * request
+ */
+ pairadd(&vps, paircopy(talloc_ctx, *certs));
}
if (vps) {
);
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=vps; vp; vp = vp->next) {
+ for (vp = fr_cursor_init(&cursor, &vps);
+ vp;
+ vp = fr_cursor_next(&cursor)) {
vp_prints(buf, sizeof(buf), vp);
- fprintf(vp_file, "\t%s%s\n", buf, vp->next ? "," : "");
+ fprintf(vp_file, "\t%s,\n", buf);
}
fclose(vp_file);
}
}
} else {
- RDEBUG2W("No information to cache: session caching will be disabled for session %s", buffer);
+ RWDEBUG2("No information to cache: session caching will be disabled for session %s", buffer);
SSL_CTX_remove_session(ssn->ctx,
ssn->ssl->session);
}
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);
if (!vps) {
- RDEBUGW("No information in cached session %s", buffer);
+ RWDEBUG("No information in cached session %s", buffer);
return -1;
} else {
- RDEBUG("Adding cached attributes for session %s:",
- buffer);
+ vp_cursor_t cursor;
+
+ RDEBUG("Adding cached attributes for session %s:", buffer);
debug_pair_list(vps);
- for (vp = vps; vp != NULL; vp = vp->next) {
+ for (vp = fr_cursor_init(&cursor, &vps);
+ vp;
+ vp = fr_cursor_next(&cursor)) {
/*
* TLS-* attrs get added back to
* the request list.
(vp->da->attr >= 1910) &&
(vp->da->attr < 1929)) {
pairadd(&request->packet->vps,
- paircopyvp(vp));
+ paircopyvp(request->packet, vp));
} else {
pairadd(&request->reply->vps,
- paircopyvp(vp));
+ paircopyvp(request->reply, vp));
}
}
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,
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
*/
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:
break;
default:
- DEBUG("Error in fragmentation logic: ?");
+ DEBUG("Error in fragmentation logic: %s",
+ ERR_error_string(code, NULL));
/*
* FIXME: Call int_ssl_check?
}
return FR_TLS_FAIL;
}
-
+
if (err == 0) {
- RDEBUGW("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;
}
RDEBUG2("Received TLS ACK");
if (ssn == NULL){
- radlog_request(L_ERR, 0, request, "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)) {
- radlog_request(L_ERR, 0, request, "FAIL: ACK without earlier message.");
+ RERROR("FAIL: ACK without earlier message");
return FR_TLS_INVALID;
}
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.
*/
default:
RDEBUG2("ACK default");
- radlog_request(L_ERR, 0, request, "Invalid ACK received: %d",
+ RERROR("Invalid ACK received: %d",
ssn->info.content_type);
return FR_TLS_INVALID;
}
}
#endif /* WITH_TLS */
+