.id = "CVE-2014-0160",
.name = "Heartbleed",
.comment = "For more information see http://heartbleed.com"
- }
+ },
+ {
+ .low = 0x01000100f, /* 1.0.1 */
+ .high = 0x01000114f, /* 1.0.1t */
+ .id = "CVE-2016-6304",
+ .name = "OCSP status request extension",
+ .comment = "For more information see https://www.openssl.org/news/secadv/20160922.txt"
+ },
+ {
+ .low = 0x01000200f, /* 1.0.2 */
+ .high = 0x01000208f, /* 1.0.2h */
+ .id = "CVE-2016-6304",
+ .name = "OCSP status request extension",
+ .comment = "For more information see https://www.openssl.org/news/secadv/20160922.txt"
+ },
+ {
+ .low = 0x01010100f, /* 1.1.0 */
+ .high = 0x01010100f, /* 1.1.0 */
+ .id = "CVE-2016-6304",
+ .name = "OCSP status request extension",
+ .comment = "For more information see https://www.openssl.org/news/secadv/20160922.txt"
+ },
};
#endif /* ENABLE_OPENSSL_VERSION_CHECK */
static unsigned int record_minus(record_t *buf, void *ptr,
unsigned int size);
+DIAG_OFF(format-nonliteral)
+/** Print errors in the TLS thread local error stack
+ *
+ * Drains the thread local OpenSSL error queue, and prints out errors.
+ *
+ * @param[in] request The current request (may be NULL).
+ * @param[in] msg Error message describing the operation being attempted.
+ * @param[in] ap Arguments for msg.
+ * @return the number of errors drained from the stack.
+ */
+static int tls_verror_log(REQUEST *request, char const *msg, va_list ap)
+{
+ unsigned long error;
+ char *p;
+ int in_stack = 0;
+ char buffer[256];
+
+ int line;
+ char const *file;
+
+ /*
+ * Pop the first error, so ERR_peek_error()
+ * can be used to determine if there are
+ * multiple errors.
+ */
+ error = ERR_get_error_line(&file, &line);
+
+ if (msg) {
+ p = talloc_vasprintf(request, msg, ap);
+
+ /*
+ * Single line mode (there's only one error)
+ */
+ if (error && !ERR_peek_error()) {
+ ERR_error_string_n(error, buffer, sizeof(buffer));
+
+ /* Extra verbose */
+ if ((request && RDEBUG_ENABLED3) || DEBUG_ENABLED3) {
+ ROPTIONAL(REDEBUG, ERROR, "%s: %s[%i]:%s", p, file, line, buffer);
+ } else {
+ ROPTIONAL(REDEBUG, ERROR, "%s: %s", p, buffer);
+ }
+
+ talloc_free(p);
+
+ return 1;
+ }
+
+ /*
+ * Print the error we were given, irrespective
+ * of whether there were any OpenSSL errors.
+ */
+ ROPTIONAL(RERROR, ERROR, "%s", p);
+ talloc_free(p);
+ }
+
+ /*
+ * Stack mode (there are multiple errors)
+ */
+ if (!error) return 0;
+ do {
+ ERR_error_string_n(error, buffer, sizeof(buffer));
+ /* Extra verbose */
+ if ((request && RDEBUG_ENABLED3) || DEBUG_ENABLED3) {
+ ROPTIONAL(REDEBUG, ERROR, "%s[%i]:%s", file, line, buffer);
+ } else {
+ ROPTIONAL(REDEBUG, ERROR, "%s", buffer);
+ }
+ in_stack++;
+ } while ((error = ERR_get_error_line(&file, &line)));
+
+ return in_stack;
+}
+DIAG_ON(format-nonliteral)
+
+/** Print errors in the TLS thread local error stack
+ *
+ * Drains the thread local OpenSSL error queue, and prints out errors.
+ *
+ * @param[in] request The current request (may be NULL).
+ * @param[in] msg Error message describing the operation being attempted.
+ * @param[in] ... Arguments for msg.
+ * @return the number of errors drained from the stack.
+ */
+int tls_error_log(REQUEST *request, char const *msg, ...)
+{
+ va_list ap;
+ int ret;
+
+ va_start(ap, msg);
+ ret = tls_verror_log(request, msg, ap);
+ va_end(ap);
+
+ return ret;
+}
+
+/** Print errors raised by OpenSSL I/O functions
+ *
+ * Drains the thread local OpenSSL error queue, and prints out errors
+ * based on the SSL handle and the return code of the I/O function.
+ *
+ * OpenSSL lists I/O functions to be:
+ * - SSL_connect
+ * - SSL_accept
+ * - SSL_do_handshake
+ * - SSL_read
+ * - SSL_peek
+ * - SSL_write
+ *
+ * @param request The current request (may be NULL).
+ * @param session The current tls_session.
+ * @param ret from the I/O operation.
+ * @param msg Error message describing the operation being attempted.
+ * @param ... Arguments for msg.
+ * @return
+ * - 0 TLS session cannot continue.
+ * - 1 TLS session may still be viable.
+ */
+int tls_error_io_log(REQUEST *request, tls_session_t *session, int ret, char const *msg, ...)
+{
+ int error;
+ va_list ap;
+
+ if (ERR_peek_error()) {
+ va_start(ap, msg);
+ tls_verror_log(request, msg, ap);
+ va_end(ap);
+ }
+
+ error = SSL_get_error(session->ssl, ret);
+ switch (error) {
+ /*
+ * These seem to be harmless and already "dealt
+ * with" by our non-blocking environment. NB:
+ * "ZERO_RETURN" is the clean "error"
+ * indicating a successfully closed SSL
+ * tunnel. We let this happen because our IO
+ * loop should not appear to have broken on
+ * this condition - and outside the IO loop, the
+ * "shutdown" state is checked.
+ *
+ * Don't print anything if we ignore the error.
+ */
+ case SSL_ERROR_NONE:
+ case SSL_ERROR_WANT_READ:
+ case SSL_ERROR_WANT_WRITE:
+ case SSL_ERROR_WANT_X509_LOOKUP:
+ case SSL_ERROR_ZERO_RETURN:
+ break;
+
+ /*
+ * These seem to be indications of a genuine
+ * error that should result in the SSL tunnel
+ * being regarded as "dead".
+ */
+ case SSL_ERROR_SYSCALL:
+ ROPTIONAL(REDEBUG, ERROR, "System call (I/O) error (%i)", ret);
+ return 0;
+
+ case SSL_ERROR_SSL:
+ ROPTIONAL(REDEBUG, ERROR, "TLS protocol error (%i)", ret);
+ return 0;
+
+ /*
+ * For any other errors that (a) exist, and (b)
+ * crop up - we need to interpret what to do with
+ * them - so "politely inform" the caller that
+ * the code needs updating here.
+ */
+ default:
+ ROPTIONAL(REDEBUG, ERROR, "TLS session error %i (%i)", error, ret);
+ return 0;
+ }
+
+ return 1;
+}
+
#ifdef PSK_MAX_IDENTITY_LEN
static bool identity_is_safe(const char *identity)
{
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.
tls_session_t *tls_new_client_session(TALLOC_CTX *ctx, fr_tls_server_conf_t *conf, int fd)
{
+ int ret;
int verify_mode;
tls_session_t *ssn = NULL;
REQUEST *request;
SSL_set_ex_data(ssn->ssl, FR_TLS_EX_INDEX_CONF, (void *)conf);
SSL_set_ex_data(ssn->ssl, FR_TLS_EX_INDEX_SSN, (void *)ssn);
SSL_set_fd(ssn->ssl, fd);
- if (SSL_connect(ssn->ssl) <= 0) {
- int err;
- while ((err = ERR_get_error())) {
- ERROR("tls: %s", ERR_error_string(err, NULL));
- }
+ ret = SSL_connect(ssn->ssl);
+ if (ret <= 0) {
+ tls_error_io_log(NULL, ssn, ret, "Failed in " STRINGIFY(__FUNCTION__) " (SSL_connect)");
talloc_free(ssn);
return NULL;
conf->session_last_flushed = request->timestamp;
}
- if ((new_tls = SSL_new(conf->ctx)) == NULL) {
- RERROR("Error creating new SSL session: %s", ERR_error_string(ERR_get_error(), NULL));
+ new_tls = SSL_new(conf->ctx);
+ if (new_tls == NULL) {
+ tls_error_log(request, "Error creating new TLS session");
return NULL;
}
}
/*
- * Print out some text describing the error.
- */
-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) {
- char const *p = ERR_error_string(l, NULL);
-
- if (p) ROPTIONAL(REDEBUG, ERROR, "SSL says: %s", p);
- }
-
- e = SSL_get_error(s, ret);
- switch (e) {
- /*
- * These seem to be harmless and already "dealt
- * with" by our non-blocking environment. NB:
- * "ZERO_RETURN" is the clean "error"
- * indicating a successfully closed SSL
- * tunnel. We let this happen because our IO
- * loop should not appear to have broken on
- * this condition - and outside the IO loop, the
- * "shutdown" state is checked.
- *
- * Don't print anything if we ignore the error.
- */
- case SSL_ERROR_NONE:
- case SSL_ERROR_WANT_READ:
- case SSL_ERROR_WANT_WRITE:
- case SSL_ERROR_WANT_X509_LOOKUP:
- case SSL_ERROR_ZERO_RETURN:
- break;
-
- /*
- * These seem to be indications of a genuine
- * error that should result in the SSL tunnel
- * being regarded as "dead".
- */
- case SSL_ERROR_SYSCALL:
- ROPTIONAL(REDEBUG, ERROR, "%s failed in a system call (%d), TLS session failed", text, ret);
- return 0;
-
- case SSL_ERROR_SSL:
- ROPTIONAL(REDEBUG, ERROR, "%s failed inside of TLS (%d), TLS session failed", text, ret);
- return 0;
-
- /*
- * For any other errors that (a) exist, and (b)
- * crop up - we need to interpret what to do with
- * them - so "politely inform" the caller that
- * the code needs updating here.
- */
- default:
- ROPTIONAL(REDEBUG, ERROR, "FATAL SSL error: %d", e);
- return 0;
- }
-
- return 1;
-}
-
-/*
* We are the server, we always get the dirty data
* (Handshake data is also considered as dirty data)
* During handshake, since SSL API handles itself,
return 1;
}
- if (!int_ssl_check(request, ssn->ssl, err, "SSL_read")) return 0;
+ if (!tls_error_io_log(request, ssn, err, "Failed in " STRINGIFY(__FUNCTION__) " (SSL_read)")) return 0;
/* Some Extra STATE information for easy debugging */
if (SSL_is_init_finished(ssn->ssl)) RDEBUG2("SSL Connection Established");
return 1;
} else {
- int_ssl_check(request, ssn->ssl, err, "BIO_read");
+ tls_error_log(NULL, NULL);
record_init(&ssn->dirty_in);
return 0;
}
if (err > 0) {
ssn->dirty_out.used = err;
} else {
- int_ssl_check(request, ssn->ssl, err, "handshake_send");
+ if (!tls_error_io_log(request, ssn, err,
+ "Failed in " STRINGIFY(__FUNCTION__) " (SSL_write)")) {
+ return 0;
+ }
}
}
str_details1, str_details2);
request = SSL_get_ex_data(tls_session->ssl, FR_TLS_EX_INDEX_REQUEST);
- if (request) {
- RDEBUG2("%s", tls_session->info.info_description);
- } else {
- DEBUG2("%s", tls_session->info.info_description);
- }
+ ROPTIONAL(RDEBUG2, DEBUG2, "%s", tls_session->info.info_description);
}
static CONF_PARSER cache_config[] = {
};
static CONF_PARSER verify_config[] = {
+ { "skip_if_ocsp_ok", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, fr_tls_server_conf_t, verify_skip_if_ocsp_ok), "no" },
{ "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 },
CONF_PARSER_TERMINATOR
#endif
static CONF_PARSER tls_server_config[] = {
- { "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 },
{ "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", 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 },
+ { "dh_file", FR_CONF_OFFSET(PW_TYPE_FILE_INPUT, fr_tls_server_conf_t, dh_file), NULL },
+ { "random_file", FR_CONF_OFFSET(PW_TYPE_FILE_EXISTS, 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" },
+ { "auto_chain", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, fr_tls_server_conf_t, auto_chain), "yes" },
+ { "disable_single_dh_use", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, fr_tls_server_conf_t, disable_single_dh_use), NULL },
{ "check_crl", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, fr_tls_server_conf_t, check_crl), "no" },
#ifdef X509_V_FLAG_CRL_CHECK_ALL
{ "check_all_crl", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, fr_tls_server_conf_t, check_all_crl), "no" },
static CONF_PARSER tls_client_config[] = {
- { "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" },
{
int rv, fd, todo;
char filename[256];
- unsigned char *p;
+
+ unsigned char const **o;
+ unsigned char **p;
+ uint8_t *q;
+
struct stat st;
VALUE_PAIR *vps = NULL;
goto err;
}
- p = sess_data;
+ q = sess_data;
todo = st.st_size;
while (todo > 0) {
- rv = read(fd, p, todo);
+ rv = read(fd, q, todo);
if (rv < 1) {
RWDEBUG("Failed reading persisted session: %s", fr_syserror(errno));
close(fd);
goto err;
}
todo -= rv;
- p += rv;
+ q += rv;
}
close(fd);
- /* openssl mutates &p */
- p = sess_data;
- sess = d2i_SSL_SESSION(NULL, (unsigned char const **)(void **) &p, st.st_size);
-
+ /*
+ * OpenSSL mutates what's passed in, so we assign sess_data to q,
+ * so the value of q gets mutated, and not the value of sess_data.
+ *
+ * We then need a pointer to hold &q, but it can't be const, because
+ * clang complains about lack of consting in nested pointer types.
+ *
+ * So we memcpy the value of that pointer, to one that
+ * does have a const, which we then pass into d2i_SSL_SESSION *sigh*.
+ */
+ q = sess_data;
+ p = &q;
+ memcpy(&o, &p, sizeof(o));
+ sess = d2i_SSL_SESSION(NULL, o, st.st_size);
if (!sess) {
RWDEBUG("Failed loading persisted session: %s", ERR_error_string(ERR_get_error(), NULL));
goto err;
/* Maximum leeway in validity period: default 5 minutes */
#define MAX_VALIDITY_PERIOD (5 * 60)
-static int ocsp_check(REQUEST *request, X509_STORE *store, X509 *issuer_cert, X509 *client_cert,
- fr_tls_server_conf_t *conf)
+typedef enum {
+ OCSP_STATUS_FAILED = 0,
+ OCSP_STATUS_OK = 1,
+ OCSP_STATUS_SKIPPED = 2,
+} ocsp_status_t;
+
+static ocsp_status_t ocsp_check(REQUEST *request, X509_STORE *store, X509 *issuer_cert, X509 *client_cert,
+ fr_tls_server_conf_t *conf)
{
OCSP_CERTID *certid;
OCSP_REQUEST *req;
int use_ssl = -1;
long nsec = MAX_VALIDITY_PERIOD, maxage = -1;
BIO *cbio, *bio_out;
- int ocsp_ok = 0;
+ ocsp_status_t ocsp_status = OCSP_STATUS_FAILED;
int status;
ASN1_GENERALIZEDTIME *rev, *thisupd, *nextupd;
int reason;
struct timeval now;
struct timeval when;
#endif
+ VALUE_PAIR *vp;
/*
* Create OCSP Request
OCSP_parse_url(url, &host, &port, &path, &use_ssl);
if (!host || !port || !path) {
RWDEBUG("ocsp: Host or port or path missing from configured URL \"%s\". Not doing OCSP", url);
- ocsp_ok = 2;
- goto ocsp_skip;
+ goto skipped;
}
} else {
int ret;
goto use_ocsp_url;
}
RWDEBUG("ocsp: No OCSP URL in certificate. Not doing OCSP");
- ocsp_ok = 2;
- goto ocsp_skip;
+ goto skipped;
case 1:
break;
/* Check host and port length are sane, then create Host: HTTP header */
if ((strlen(host) + strlen(port) + 2) > sizeof(hostheader)) {
RWDEBUG("ocsp: Host and port too long");
- goto ocsp_skip;
+ goto skipped;
}
snprintf(hostheader, sizeof(hostheader), "%s:%s", host, port);
resp = OCSP_sendreq_bio(cbio, path, req);
if (!resp) {
REDEBUG("ocsp: Couldn't get OCSP response");
- ocsp_ok = 2;
+ ocsp_status = OCSP_STATUS_SKIPPED;
goto ocsp_end;
}
#else
rc = BIO_do_connect(cbio);
if ((rc <= 0) && ((!conf->ocsp_timeout) || !BIO_should_retry(cbio))) {
REDEBUG("ocsp: Couldn't connect to OCSP responder");
- ocsp_ok = 2;
+ ocsp_status = OCSP_STATUS_SKIPPED;
goto ocsp_end;
}
ctx = OCSP_sendreq_new(cbio, path, NULL, -1);
if (!ctx) {
REDEBUG("ocsp: Couldn't create OCSP request");
- ocsp_ok = 2;
+ ocsp_status = OCSP_STATUS_SKIPPED;
goto ocsp_end;
}
if (!OCSP_REQ_CTX_add1_header(ctx, "Host", hostheader)) {
REDEBUG("ocsp: Couldn't set Host header");
- ocsp_ok = 2;
+ ocsp_status = OCSP_STATUS_SKIPPED;
goto ocsp_end;
}
if (!OCSP_REQ_CTX_set1_req(ctx, req)) {
REDEBUG("ocsp: Couldn't add data to OCSP request");
- ocsp_ok = 2;
+ ocsp_status = OCSP_STATUS_SKIPPED;
goto ocsp_end;
}
if (conf->ocsp_timeout && (rc == -1) && BIO_should_retry(cbio)) {
REDEBUG("ocsp: Response timed out");
- ocsp_ok = 2;
+ ocsp_status = OCSP_STATUS_SKIPPED;
goto ocsp_end;
}
if (rc == 0) {
REDEBUG("ocsp: Couldn't get OCSP response");
- ocsp_ok = 2;
+ ocsp_status = OCSP_STATUS_SKIPPED;
goto ocsp_end;
}
#endif
switch (status) {
case V_OCSP_CERTSTATUS_GOOD:
RDEBUG2("ocsp: Cert status: good");
- ocsp_ok = 1;
+ vp = pair_make_request("TLS-OCSP-Cert-Valid", NULL, T_OP_SET);
+ vp->vp_integer = 1; /* yes */
+ ocsp_status = OCSP_STATUS_OK;
break;
default:
if (bio_out) BIO_free(bio_out);
OCSP_BASICRESP_free(bresp);
- ocsp_skip:
- switch (ocsp_ok) {
- case 1:
+ switch (ocsp_status) {
+ case OCSP_STATUS_OK:
RDEBUG2("ocsp: Certificate is valid");
break;
- case 2:
+ case OCSP_STATUS_SKIPPED:
+ skipped:
+ vp = pair_make_request("TLS-OCSP-Cert-Valid", NULL, T_OP_SET);
+ vp->vp_integer = 2; /* skipped */
if (conf->ocsp_softfail) {
RWDEBUG("ocsp: Unable to check certificate, assuming it's valid");
RWDEBUG("ocsp: This may be insecure");
- ocsp_ok = 1;
+
+ /* Remove OpenSSL errors from queue or handshake will fail */
+ while (ERR_get_error());
+
+ ocsp_status = OCSP_STATUS_SKIPPED;
} else {
REDEBUG("ocsp: Unable to check certificate, failing");
- ocsp_ok = 0;
+ ocsp_status = OCSP_STATUS_FAILED;
}
break;
default:
+ vp = pair_make_request("TLS-OCSP-Cert-Valid", NULL, T_OP_SET);
+ vp->vp_integer = 0; /* no */
REDEBUG("ocsp: Certificate has been expired/revoked");
break;
}
- return ocsp_ok;
+ return ocsp_status;
}
#endif /* HAVE_OPENSSL_OCSP_H */
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);
+ ocsp_store = conf->ocsp_store;
#endif
talloc_ctx = SSL_get_ex_data(ssl, FR_TLS_EX_INDEX_TALLOC);
#ifdef HAVE_OPENSSL_OCSP_H
if (my_ok && conf->ocsp_enable){
RDEBUG2("Starting OCSP Request");
- if (X509_STORE_CTX_get1_issuer(&issuer_cert, ctx, client_cert) != 1) {
+ if ((X509_STORE_CTX_get1_issuer(&issuer_cert, ctx, client_cert) != 1) ||
+ !issuer_cert) {
RERROR("Couldn't get issuer_cert for %s", common_name);
} else {
my_ok = ocsp_check(request, ocsp_store, issuer_cert, client_cert, conf);
}
#endif
- while (conf->verify_client_cert_cmd) {
+ /*
+ * If OCSP returns fail (0), the certificate has expired.
+ * Don't run the verify routines/
+ *
+ * If OCSP returns success (1), we MAY want to run the verify section.
+ * but only if verify_skip_if_ocsp_ok is false.
+ *
+ * If OCSP returns skipped (2), we run the verify command, unless
+ * conf->verify_skip_if_ocsp_ok is true.
+ */
+ if ((my_ok != OCSP_STATUS_FAILED)
+#ifdef HAVE_OPENSSL_OCSP_H
+ && conf->ocsp_enable &&
+ (((my_ok == OCSP_STATUS_OK) && !conf->verify_skip_if_ocsp_ok) ||
+ ((my_ok == OCSP_STATUS_SKIPPED) && conf->verify_skip_if_ocsp_ok))
+
+#endif
+ ) while (conf->verify_client_cert_cmd) {
char filename[256];
int fd;
FILE *fp;
snprintf(filename, sizeof(filename), "%s/%s.client.XXXXXXXX",
- conf->verify_tmp_dir, progname);
+ conf->verify_tmp_dir, main_config.name);
fd = mkstemp(filename);
if (fd < 0) {
RDEBUG("Failed creating file in %s: %s",
unlink(filename);
break;
}
-
-
} /* depth == 0 */
+ if (certs && request && !my_ok) {
+ fr_pair_add(&request->packet->vps, fr_pair_list_copy(request->packet, *certs));
+ }
+
if (RDEBUG_ENABLED3) {
RDEBUG3("chain-depth : %d", depth);
RDEBUG3("error : %d", err);
RDEBUG3("issuer : %s", issuer);
RDEBUG3("verify return : %d", my_ok);
}
- return my_ok;
+
+ return (my_ok != 0);
}
/* 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(LOG_PREFIX ": X509_STORE error %s", ERR_error_string(ERR_get_error(), NULL));
- ERROR(LOG_PREFIX ": Error reading Trusted root CA list %s",conf->ca_file );
+ if (!X509_STORE_load_locations(store, conf->ca_file, conf->ca_path)) {
+ tls_error_log(NULL, "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, char const *ecdh_curve)
+static int set_ecdh_curve(SSL_CTX *ctx, char const *ecdh_curve, bool disable_single_dh_use)
{
int nid;
EC_KEY *ecdh;
SSL_CTX_set_tmp_ecdh(ctx, ecdh);
- SSL_CTX_set_options(ctx, SSL_OP_SINGLE_ECDH_USE);
+ if (!disable_single_dh_use) {
+ SSL_CTX_set_options(ctx, SSL_OP_SINGLE_ECDH_USE);
+ }
EC_KEY_free(ecdh);
ctx = SSL_CTX_new(SSLv23_method()); /* which is really "all known SSL / TLS methods". Idiots. */
if (!ctx) {
- int err;
- while ((err = ERR_get_error())) {
- ERROR(LOG_PREFIX ": Failed creating SSL context: %s", ERR_error_string(err, NULL));
- return NULL;
- }
+ tls_error_log(NULL, "Failed creating TLS context");
+ return NULL;
}
/*
if (type == SSL_FILETYPE_PEM) {
if (!(SSL_CTX_use_certificate_chain_file(ctx, conf->certificate_file))) {
- ERROR(LOG_PREFIX ": Error reading certificate file %s:%s", conf->certificate_file,
- ERR_error_string(ERR_get_error(), NULL));
+ tls_error_log(NULL, "Failed reading certificate file \"%s\"",
+ conf->certificate_file);
return NULL;
}
} else if (!(SSL_CTX_use_certificate_file(ctx, conf->certificate_file, type))) {
- ERROR(LOG_PREFIX ": Error reading certificate file %s:%s",
- conf->certificate_file,
- ERR_error_string(ERR_get_error(), NULL));
+ tls_error_log(NULL, "Failed reading certificate file \"%s\"",
+ conf->certificate_file);
return NULL;
}
load_ca:
if (conf->ca_file || conf->ca_path) {
if (!SSL_CTX_load_verify_locations(ctx, conf->ca_file, conf->ca_path)) {
- ERROR(LOG_PREFIX ": SSL error %s", ERR_error_string(ERR_get_error(), NULL));
- ERROR(LOG_PREFIX ": Error reading Trusted root CA list %s",conf->ca_file );
+ tls_error_log(NULL, "Failed 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))) {
- ERROR(LOG_PREFIX ": Failed reading private key file %s:%s",
- conf->private_key_file,
- ERR_error_string(ERR_get_error(), NULL));
+ tls_error_log(NULL, "Failed reading private key file \"%s\"",
+ conf->private_key_file);
return NULL;
}
ctx_tls_versions |= SSL_OP_NO_TLSv1_1;
#endif
#ifdef SSL_OP_NO_TLSv1_2
+
if (conf->disable_tlsv1_2) ctx_options |= SSL_OP_NO_TLSv1_2;
ctx_tls_versions |= SSL_OP_NO_TLSv1_2;
+
#endif
if ((ctx_options & ctx_tls_versions) == ctx_tls_versions) {
ctx_options |= SSL_OP_NO_TICKET;
#endif
- /*
- * SSL_OP_SINGLE_DH_USE must be used in order to prevent
- * small subgroup attacks and forward secrecy. Always
- * using
- *
- * SSL_OP_SINGLE_DH_USE has an impact on the computer
- * time needed during negotiation, but it is not very
- * large.
- */
- ctx_options |= SSL_OP_SINGLE_DH_USE;
+ if (!conf->disable_single_dh_use) {
+ /*
+ * SSL_OP_SINGLE_DH_USE must be used in order to prevent
+ * small subgroup attacks and forward secrecy. Always
+ * using SSL_OP_SINGLE_DH_USE has an impact on the
+ * computer time needed during negotiation, but it is not
+ * very large.
+ */
+ ctx_options |= SSL_OP_SINGLE_DH_USE;
+ }
/*
* SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS to work around issues
*/
#if OPENSSL_VERSION_NUMBER >= 0x0090800fL
#ifndef OPENSSL_NO_ECDH
- if (set_ecdh_curve(ctx, conf->ecdh_curve) < 0) {
+ if (set_ecdh_curve(ctx, conf->ecdh_curve, conf->disable_single_dh_use) < 0) {
return NULL;
}
#endif
#endif
+ /*
+ * OpenSSL will automatically create certificate chains,
+ * unless we tell it to not do that. The problem is that
+ * it sometimes gets the chains right from a certificate
+ * signature view, but wrong from the clients view.
+ */
+ if (!conf->auto_chain) {
+ SSL_CTX_set_mode(ctx, SSL_MODE_NO_AUTO_CHAIN);
+ }
+
/* Set Info callback */
SSL_CTX_set_info_callback(ctx, cbtls_info);
if (conf->check_crl) {
certstore = SSL_CTX_get_cert_store(ctx);
if (certstore == NULL) {
- ERROR(LOG_PREFIX ": SSL error %s", ERR_error_string(ERR_get_error(), NULL));
- ERROR(LOG_PREFIX ": Error reading Certificate Store");
+ tls_error_log(NULL, "Error reading Certificate Store");
return NULL;
}
X509_STORE_set_flags(certstore, X509_V_FLAG_CRL_CHECK);
/* Load randomness */
if (conf->random_file) {
if (!(RAND_load_file(conf->random_file, 1024*10))) {
- ERROR(LOG_PREFIX ": SSL error %s", ERR_error_string(ERR_get_error(), NULL));
- ERROR(LOG_PREFIX ": Error loading randomness");
+ tls_error_log(NULL, "Failed loading randomness");
return NULL;
}
}
*/
if (conf->cipher_list) {
if (!SSL_CTX_set_cipher_list(ctx, conf->cipher_list)) {
- ERROR(LOG_PREFIX ": Error setting cipher list");
+ tls_error_log(NULL, "Failed setting cipher list");
return NULL;
}
}
goto error;
}
+#ifdef SSL_OP_NO_TLSv1_2
+ /*
+ * OpenSSL 1.0.1f and 1.0.1g get the MS-MPPE keys wrong.
+ */
+#if (OPENSSL_VERSION_NUMBER >= 0x10010060L) && (OPENSSL_VERSION_NUMBER < 0x10010060L)
+ conf->disable_tlsv1_2 = true;
+ WARN(LOG_PREFIX ": Disabling TLSv1.2 due to OpenSSL bugs");
+#endif
+#endif
+
/*
* Cache conf in cs in case we're asked to parse this again.
*/
break;
default:
- DEBUG("Error in fragmentation logic: %s", ERR_error_string(code, NULL));
-
- /*
- * FIXME: Call int_ssl_check?
- */
+ REDEBUG("Error in fragmentation logic");
+ tls_error_io_log(request, ssn, err,
+ "Failed in " STRINGIFY(__FUNCTION__) " (SSL_read)");
break;
}
return FR_TLS_FAIL;