|
@@ -1,6 +1,6 @@
|
|
|
/*
|
|
|
* SSL/TLS interface functions for OpenSSL
|
|
|
- * Copyright (c) 2004-2011, Jouni Malinen <j@w1.fi>
|
|
|
+ * Copyright (c) 2004-2013, Jouni Malinen <j@w1.fi>
|
|
|
*
|
|
|
* This software may be distributed under the terms of the BSD license.
|
|
|
* See README for more details.
|
|
@@ -51,6 +51,13 @@
|
|
|
#endif
|
|
|
#endif
|
|
|
|
|
|
+#ifdef SSL_set_tlsext_status_type
|
|
|
+#ifndef OPENSSL_NO_TLSEXT
|
|
|
+#define HAVE_OCSP
|
|
|
+#include <openssl/ocsp.h>
|
|
|
+#endif /* OPENSSL_NO_TLSEXT */
|
|
|
+#endif /* SSL_set_tlsext_status_type */
|
|
|
+
|
|
|
static int tls_openssl_ref_count = 0;
|
|
|
|
|
|
struct tls_context {
|
|
@@ -58,6 +65,7 @@ struct tls_context {
|
|
|
union tls_event_data *data);
|
|
|
void *cb_ctx;
|
|
|
int cert_in_cb;
|
|
|
+ char *ocsp_stapling_response;
|
|
|
};
|
|
|
|
|
|
static struct tls_context *tls_global = NULL;
|
|
@@ -88,6 +96,9 @@ struct tls_connection {
|
|
|
u8 srv_cert_hash[32];
|
|
|
|
|
|
unsigned int flags;
|
|
|
+
|
|
|
+ X509 *peer_cert;
|
|
|
+ X509 *peer_issuer;
|
|
|
};
|
|
|
|
|
|
|
|
@@ -827,6 +838,8 @@ void tls_deinit(void *ssl_ctx)
|
|
|
ERR_remove_state(0);
|
|
|
ERR_free_strings();
|
|
|
EVP_cleanup();
|
|
|
+ os_free(tls_global->ocsp_stapling_response);
|
|
|
+ tls_global->ocsp_stapling_response = NULL;
|
|
|
os_free(tls_global);
|
|
|
tls_global = NULL;
|
|
|
}
|
|
@@ -1241,6 +1254,12 @@ static int tls_verify_cb(int preverify_ok, X509_STORE_CTX *x509_ctx)
|
|
|
conn = SSL_get_app_data(ssl);
|
|
|
if (conn == NULL)
|
|
|
return 0;
|
|
|
+
|
|
|
+ if (depth == 0)
|
|
|
+ conn->peer_cert = err_cert;
|
|
|
+ else if (depth == 1)
|
|
|
+ conn->peer_issuer = err_cert;
|
|
|
+
|
|
|
context = conn->context;
|
|
|
match = conn->subject_match;
|
|
|
altmatch = conn->altsubject_match;
|
|
@@ -2755,11 +2774,187 @@ int tls_connection_get_write_alerts(void *ssl_ctx, struct tls_connection *conn)
|
|
|
}
|
|
|
|
|
|
|
|
|
+#ifdef HAVE_OCSP
|
|
|
+
|
|
|
+static void ocsp_debug_print_resp(OCSP_RESPONSE *rsp)
|
|
|
+{
|
|
|
+#ifndef CONFIG_NO_STDOUT_DEBUG
|
|
|
+ extern int wpa_debug_level;
|
|
|
+ BIO *out;
|
|
|
+ size_t rlen;
|
|
|
+ char *txt;
|
|
|
+ int res;
|
|
|
+
|
|
|
+ if (wpa_debug_level > MSG_DEBUG)
|
|
|
+ return;
|
|
|
+
|
|
|
+ out = BIO_new(BIO_s_mem());
|
|
|
+ if (!out)
|
|
|
+ return;
|
|
|
+
|
|
|
+ OCSP_RESPONSE_print(out, rsp, 0);
|
|
|
+ rlen = BIO_ctrl_pending(out);
|
|
|
+ txt = os_malloc(rlen + 1);
|
|
|
+ if (!txt) {
|
|
|
+ BIO_free(out);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ res = BIO_read(out, txt, rlen);
|
|
|
+ if (res > 0) {
|
|
|
+ txt[res] = '\0';
|
|
|
+ wpa_printf(MSG_DEBUG, "OpenSSL: OCSP Response\n%s", txt);
|
|
|
+ }
|
|
|
+ os_free(txt);
|
|
|
+ BIO_free(out);
|
|
|
+#endif /* CONFIG_NO_STDOUT_DEBUG */
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+static int ocsp_resp_cb(SSL *s, void *arg)
|
|
|
+{
|
|
|
+ struct tls_connection *conn = arg;
|
|
|
+ const unsigned char *p;
|
|
|
+ int len, status, reason;
|
|
|
+ OCSP_RESPONSE *rsp;
|
|
|
+ OCSP_BASICRESP *basic;
|
|
|
+ OCSP_CERTID *id;
|
|
|
+ ASN1_GENERALIZEDTIME *produced_at, *this_update, *next_update;
|
|
|
+
|
|
|
+ len = SSL_get_tlsext_status_ocsp_resp(s, &p);
|
|
|
+ if (!p) {
|
|
|
+ wpa_printf(MSG_DEBUG, "OpenSSL: No OCSP response received");
|
|
|
+ return (conn->flags & TLS_CONN_REQUIRE_OCSP) ? 0 : 1;
|
|
|
+ }
|
|
|
+
|
|
|
+ wpa_hexdump(MSG_DEBUG, "OpenSSL: OCSP response", p, len);
|
|
|
+
|
|
|
+ rsp = d2i_OCSP_RESPONSE(NULL, &p, len);
|
|
|
+ if (!rsp) {
|
|
|
+ wpa_printf(MSG_INFO, "OpenSSL: Failed to parse OCSP response");
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ ocsp_debug_print_resp(rsp);
|
|
|
+
|
|
|
+ status = OCSP_response_status(rsp);
|
|
|
+ if (status != OCSP_RESPONSE_STATUS_SUCCESSFUL) {
|
|
|
+ wpa_printf(MSG_INFO, "OpenSSL: OCSP responder error %d (%s)",
|
|
|
+ status, OCSP_response_status_str(status));
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ basic = OCSP_response_get1_basic(rsp);
|
|
|
+ if (!basic) {
|
|
|
+ wpa_printf(MSG_INFO, "OpenSSL: Could not find BasicOCSPResponse");
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ status = OCSP_basic_verify(basic, NULL, SSL_CTX_get_cert_store(s->ctx),
|
|
|
+ 0);
|
|
|
+ if (status <= 0) {
|
|
|
+ tls_show_errors(MSG_INFO, __func__,
|
|
|
+ "OpenSSL: OCSP response failed verification");
|
|
|
+ OCSP_BASICRESP_free(basic);
|
|
|
+ OCSP_RESPONSE_free(rsp);
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ wpa_printf(MSG_DEBUG, "OpenSSL: OCSP response verification succeeded");
|
|
|
+
|
|
|
+ if (!conn->peer_cert || !conn->peer_issuer) {
|
|
|
+ wpa_printf(MSG_DEBUG, "OpenSSL: Peer certificate or issue certificate not available for OCSP status check");
|
|
|
+ OCSP_BASICRESP_free(basic);
|
|
|
+ OCSP_RESPONSE_free(rsp);
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ id = OCSP_cert_to_id(NULL, conn->peer_cert, conn->peer_issuer);
|
|
|
+ if (!id) {
|
|
|
+ wpa_printf(MSG_DEBUG, "OpenSSL: Could not create OCSP certificate identifier");
|
|
|
+ OCSP_BASICRESP_free(basic);
|
|
|
+ OCSP_RESPONSE_free(rsp);
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!OCSP_resp_find_status(basic, id, &status, &reason, &produced_at,
|
|
|
+ &this_update, &next_update)) {
|
|
|
+ wpa_printf(MSG_INFO, "OpenSSL: Could not find current server certificate from OCSP response%s",
|
|
|
+ (conn->flags & TLS_CONN_REQUIRE_OCSP) ? "" :
|
|
|
+ " (OCSP not required)");
|
|
|
+ OCSP_BASICRESP_free(basic);
|
|
|
+ OCSP_RESPONSE_free(rsp);
|
|
|
+ return (conn->flags & TLS_CONN_REQUIRE_OCSP) ? 0 : 1;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!OCSP_check_validity(this_update, next_update, 5 * 60, -1)) {
|
|
|
+ tls_show_errors(MSG_INFO, __func__,
|
|
|
+ "OpenSSL: OCSP status times invalid");
|
|
|
+ OCSP_BASICRESP_free(basic);
|
|
|
+ OCSP_RESPONSE_free(rsp);
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ OCSP_BASICRESP_free(basic);
|
|
|
+ OCSP_RESPONSE_free(rsp);
|
|
|
+
|
|
|
+ wpa_printf(MSG_DEBUG, "OpenSSL: OCSP status for server certificate: %s",
|
|
|
+ OCSP_cert_status_str(status));
|
|
|
+
|
|
|
+ if (status == V_OCSP_CERTSTATUS_GOOD)
|
|
|
+ return 1;
|
|
|
+ if (status == V_OCSP_CERTSTATUS_REVOKED)
|
|
|
+ return 0;
|
|
|
+ if (conn->flags & TLS_CONN_REQUIRE_OCSP) {
|
|
|
+ wpa_printf(MSG_DEBUG, "OpenSSL: OCSP status unknown, but OCSP required");
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+ wpa_printf(MSG_DEBUG, "OpenSSL: OCSP status unknown, but OCSP was not required, so allow connection to continue");
|
|
|
+ return 1;
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+static int ocsp_status_cb(SSL *s, void *arg)
|
|
|
+{
|
|
|
+ char *tmp;
|
|
|
+ char *resp;
|
|
|
+ size_t len;
|
|
|
+
|
|
|
+ if (tls_global->ocsp_stapling_response == NULL) {
|
|
|
+ wpa_printf(MSG_DEBUG, "OpenSSL: OCSP status callback - no response configured");
|
|
|
+ return SSL_TLSEXT_ERR_OK;
|
|
|
+ }
|
|
|
+
|
|
|
+ resp = os_readfile(tls_global->ocsp_stapling_response, &len);
|
|
|
+ if (resp == NULL) {
|
|
|
+ wpa_printf(MSG_DEBUG, "OpenSSL: OCSP status callback - could not read response file");
|
|
|
+ /* TODO: Build OCSPResponse with responseStatus = internalError
|
|
|
+ */
|
|
|
+ return SSL_TLSEXT_ERR_OK;
|
|
|
+ }
|
|
|
+ wpa_printf(MSG_DEBUG, "OpenSSL: OCSP status callback - send cached response");
|
|
|
+ tmp = OPENSSL_malloc(len);
|
|
|
+ if (tmp == NULL) {
|
|
|
+ os_free(resp);
|
|
|
+ return SSL_TLSEXT_ERR_ALERT_FATAL;
|
|
|
+ }
|
|
|
+
|
|
|
+ os_memcpy(tmp, resp, len);
|
|
|
+ os_free(resp);
|
|
|
+ SSL_set_tlsext_status_ocsp_resp(s, tmp, len);
|
|
|
+
|
|
|
+ return SSL_TLSEXT_ERR_OK;
|
|
|
+}
|
|
|
+
|
|
|
+#endif /* HAVE_OCSP */
|
|
|
+
|
|
|
+
|
|
|
int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn,
|
|
|
const struct tls_connection_params *params)
|
|
|
{
|
|
|
int ret;
|
|
|
unsigned long err;
|
|
|
+ SSL_CTX *ssl_ctx = tls_ctx;
|
|
|
|
|
|
if (conn == NULL)
|
|
|
return -1;
|
|
@@ -2827,6 +3022,14 @@ int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn,
|
|
|
SSL_clear_options(conn->ssl, SSL_OP_NO_TICKET);
|
|
|
#endif /* SSL_OP_NO_TICKET */
|
|
|
|
|
|
+#ifdef HAVE_OCSP
|
|
|
+ if (params->flags & TLS_CONN_REQUEST_OCSP) {
|
|
|
+ SSL_set_tlsext_status_type(conn->ssl, TLSEXT_STATUSTYPE_ocsp);
|
|
|
+ SSL_CTX_set_tlsext_status_cb(ssl_ctx, ocsp_resp_cb);
|
|
|
+ SSL_CTX_set_tlsext_status_arg(ssl_ctx, conn);
|
|
|
+ }
|
|
|
+#endif /* HAVE_OCSP */
|
|
|
+
|
|
|
conn->flags = params->flags;
|
|
|
|
|
|
tls_get_errors(tls_ctx);
|
|
@@ -2869,6 +3072,17 @@ int tls_global_set_params(void *tls_ctx,
|
|
|
SSL_CTX_clear_options(ssl_ctx, SSL_OP_NO_TICKET);
|
|
|
#endif /* SSL_OP_NO_TICKET */
|
|
|
|
|
|
+#ifdef HAVE_OCSP
|
|
|
+ SSL_CTX_set_tlsext_status_cb(ssl_ctx, ocsp_status_cb);
|
|
|
+ SSL_CTX_set_tlsext_status_arg(ssl_ctx, ssl_ctx);
|
|
|
+ os_free(tls_global->ocsp_stapling_response);
|
|
|
+ if (params->ocsp_stapling_response)
|
|
|
+ tls_global->ocsp_stapling_response =
|
|
|
+ os_strdup(params->ocsp_stapling_response);
|
|
|
+ else
|
|
|
+ tls_global->ocsp_stapling_response = NULL;
|
|
|
+#endif /* HAVE_OCSP */
|
|
|
+
|
|
|
return 0;
|
|
|
}
|
|
|
|