#include <openssl/ssl.h>
#include <openssl/err.h>
+#include <openssl/opensslv.h>
#include <openssl/pkcs12.h>
#include <openssl/x509v3.h>
#ifndef OPENSSL_NO_ENGINE
#include <openssl/dh.h>
#endif
-#ifdef OPENSSL_IS_BORINGSSL
-#include <openssl/asn1.h>
-#include <openssl/asn1t.h>
-#endif /* OPENSSL_IS_BORINGSSL */
-
#include "common.h"
#include "crypto.h"
#include "sha1.h"
#include "sha256.h"
#include "tls.h"
+#include "tls_openssl.h"
-#if OPENSSL_VERSION_NUMBER < 0x10000000L
-/* ERR_remove_thread_state replaces ERR_remove_state and the latter is
- * deprecated. However, OpenSSL 0.9.8 doesn't include
- * ERR_remove_thread_state. */
-#define ERR_remove_thread_state(tid) ERR_remove_state(0)
+#if !defined(CONFIG_FIPS) && \
+ (defined(EAP_FAST) || defined(EAP_FAST_DYNAMIC) || \
+ defined(EAP_SERVER_FAST))
+#define OPENSSL_NEED_EAP_FAST_PRF
#endif
#if defined(OPENSSL_IS_BORINGSSL)
#endif /* OPENSSL_NO_TLSEXT */
#endif /* SSL_set_tlsext_status_type */
+#if (OPENSSL_VERSION_NUMBER < 0x10100000L || \
+ defined(LIBRESSL_VERSION_NUMBER)) && \
+ !defined(BORINGSSL_API_VERSION)
+/*
+ * SSL_get_client_random() and SSL_get_server_random() were added in OpenSSL
+ * 1.1.0 and newer BoringSSL revisions. Provide compatibility wrappers for
+ * older versions.
+ */
+
+static size_t SSL_get_client_random(const SSL *ssl, unsigned char *out,
+ size_t outlen)
+{
+ if (!ssl->s3 || outlen < SSL3_RANDOM_SIZE)
+ return 0;
+ os_memcpy(out, ssl->s3->client_random, SSL3_RANDOM_SIZE);
+ return SSL3_RANDOM_SIZE;
+}
+
+
+static size_t SSL_get_server_random(const SSL *ssl, unsigned char *out,
+ size_t outlen)
+{
+ if (!ssl->s3 || outlen < SSL3_RANDOM_SIZE)
+ return 0;
+ os_memcpy(out, ssl->s3->server_random, SSL3_RANDOM_SIZE);
+ return SSL3_RANDOM_SIZE;
+}
+
+
+#ifdef OPENSSL_NEED_EAP_FAST_PRF
+static size_t SSL_SESSION_get_master_key(const SSL_SESSION *session,
+ unsigned char *out, size_t outlen)
+{
+ if (!session || session->master_key_length < 0 ||
+ (size_t) session->master_key_length > outlen)
+ return 0;
+ if ((size_t) session->master_key_length < outlen)
+ outlen = session->master_key_length;
+ os_memcpy(out, session->master_key, outlen);
+ return outlen;
+}
+#endif /* OPENSSL_NEED_EAP_FAST_PRF */
+
+#endif
+
#ifdef ANDROID
#include <openssl/pem.h>
#include <keystore/keystore_get.h>
free(value);
return bio;
}
+
+
+static int tls_add_ca_from_keystore(X509_STORE *ctx, const char *key_alias)
+{
+ BIO *bio = BIO_from_keystore(key_alias);
+ STACK_OF(X509_INFO) *stack = NULL;
+ stack_index_t i;
+
+ if (bio) {
+ stack = PEM_X509_INFO_read_bio(bio, NULL, NULL, NULL);
+ BIO_free(bio);
+ }
+
+ if (!stack) {
+ wpa_printf(MSG_WARNING, "TLS: Failed to parse certificate: %s",
+ key_alias);
+ return -1;
+ }
+
+ for (i = 0; i < sk_X509_INFO_num(stack); ++i) {
+ X509_INFO *info = sk_X509_INFO_value(stack, i);
+
+ if (info->x509)
+ X509_STORE_add_cert(ctx, info->x509);
+ if (info->crl)
+ X509_STORE_add_crl(ctx, info->crl);
+ }
+
+ sk_X509_INFO_pop_free(stack, X509_INFO_free);
+
+ return 0;
+}
+
+
+static int tls_add_ca_from_keystore_encoded(X509_STORE *ctx,
+ const char *encoded_key_alias)
+{
+ int rc = -1;
+ int len = os_strlen(encoded_key_alias);
+ unsigned char *decoded_alias;
+
+ if (len & 1) {
+ wpa_printf(MSG_WARNING, "Invalid hex-encoded alias: %s",
+ encoded_key_alias);
+ return rc;
+ }
+
+ decoded_alias = os_malloc(len / 2 + 1);
+ if (decoded_alias) {
+ if (!hexstr2bin(encoded_key_alias, decoded_alias, len / 2)) {
+ decoded_alias[len / 2] = '\0';
+ rc = tls_add_ca_from_keystore(
+ ctx, (const char *) decoded_alias);
+ }
+ os_free(decoded_alias);
+ }
+
+ return rc;
+}
+
#endif /* ANDROID */
static int tls_openssl_ref_count = 0;
X509 *peer_issuer;
X509 *peer_issuer_issuer;
-#if OPENSSL_VERSION_NUMBER >= 0x10100000L
unsigned char client_random[SSL3_RANDOM_SIZE];
unsigned char server_random[SSL3_RANDOM_SIZE];
-#endif
};
wpa_printf(MSG_DEBUG, "OpenSSL: Loaded CA certificate for "
"system certificate store: subject='%s'", buf);
- if (!X509_STORE_add_cert(ssl_ctx->cert_store, cert)) {
+ if (!X509_STORE_add_cert(SSL_CTX_get_cert_store(ssl_ctx),
+ cert)) {
tls_show_errors(MSG_WARNING, __func__,
"Failed to add ca_cert to OpenSSL "
"certificate store");
engine = ENGINE_by_id(id);
if (engine) {
- ENGINE_free(engine);
wpa_printf(MSG_DEBUG, "ENGINE: engine '%s' is already "
"available", id);
- return 0;
+ /*
+ * If it was auto-loaded by ENGINE_by_id() we might still
+ * need to tell it which PKCS#11 module to use in legacy
+ * (non-p11-kit) environments. Do so now; even if it was
+ * properly initialised before, setting it again will be
+ * harmless.
+ */
+ goto found;
}
ERR_clear_error();
id, ERR_error_string(ERR_get_error(), NULL));
return -1;
}
-
+ found:
while (post && post[0]) {
wpa_printf(MSG_DEBUG, "ENGINE: '%s' '%s'", post[0], post[1]);
if (ENGINE_ctrl_cmd_string(engine, post[0], post[1], 0) == 0) {
}
#endif /* OPENSSL_FIPS */
#endif /* CONFIG_FIPS */
+#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER)
SSL_load_error_strings();
SSL_library_init();
#ifndef OPENSSL_NO_SHA256
#endif /* OPENSSL_NO_RC2 */
PKCS12_PBE_add();
#endif /* PKCS12_FUNCS */
+#endif /* < 1.1.0 */
} else {
context = tls_context_new(conf);
if (context == NULL)
os_free(tls_global);
tls_global = NULL;
}
+ os_free(data);
return NULL;
}
data->ssl = ssl;
SSL_CTX_set_options(ssl, SSL_OP_NO_SSLv2);
SSL_CTX_set_options(ssl, SSL_OP_NO_SSLv3);
+#ifdef SSL_MODE_NO_AUTO_CHAIN
+ /* Number of deployed use cases assume the default OpenSSL behavior of
+ * auto chaining the local certificate is in use. BoringSSL removed this
+ * functionality by default, so we need to restore it here to avoid
+ * breaking existing use cases. */
+ SSL_CTX_clear_mode(ssl, SSL_MODE_NO_AUTO_CHAIN);
+#endif /* SSL_MODE_NO_AUTO_CHAIN */
+
SSL_CTX_set_info_callback(ssl, ssl_info_cb);
SSL_CTX_set_app_data(ssl, context);
if (data->tls_session_lifetime > 0) {
tls_openssl_ref_count--;
if (tls_openssl_ref_count == 0) {
+#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER)
#ifndef OPENSSL_NO_ENGINE
ENGINE_cleanup();
#endif /* OPENSSL_NO_ENGINE */
ERR_remove_thread_state(NULL);
ERR_free_strings();
EVP_cleanup();
+#endif /* < 1.1.0 */
os_free(tls_global->ocsp_stapling_response);
tls_global->ocsp_stapling_response = NULL;
os_free(tls_global);
found++;
}
+ sk_GENERAL_NAME_pop_free(ext, GENERAL_NAME_free);
+
return found;
}
1) {
wpa_printf(MSG_DEBUG, "TLS: %s in dNSName found",
full ? "Match" : "Suffix match");
+ sk_GENERAL_NAME_pop_free(ext, GENERAL_NAME_free);
return 1;
}
}
+ sk_GENERAL_NAME_pop_free(ext, GENERAL_NAME_free);
if (dns_name) {
wpa_printf(MSG_DEBUG, "TLS: None of the dNSName(s) matched");
}
-static struct wpabuf * get_x509_cert(X509 *cert)
-{
- struct wpabuf *buf;
- u8 *tmp;
-
- int cert_len = i2d_X509(cert, NULL);
- if (cert_len <= 0)
- return NULL;
-
- buf = wpabuf_alloc(cert_len);
- if (buf == NULL)
- return NULL;
-
- tmp = wpabuf_put(buf, cert_len);
- i2d_X509(cert, &tmp);
- return buf;
-}
-
-
-static void openssl_tls_fail_event(struct tls_connection *conn,
- X509 *err_cert, int err, int depth,
- const char *subject, const char *err_str,
- enum tls_fail_reason reason)
-{
- union tls_event_data ev;
- struct wpabuf *cert = NULL;
- struct tls_context *context = conn->context;
-
- if (context->event_cb == NULL)
- return;
-
- cert = get_x509_cert(err_cert);
- os_memset(&ev, 0, sizeof(ev));
- ev.cert_fail.reason = reason != TLS_FAIL_UNSPECIFIED ?
- reason : openssl_tls_fail_reason(err);
- ev.cert_fail.depth = depth;
- ev.cert_fail.subject = subject;
- ev.cert_fail.reason_txt = err_str;
- ev.cert_fail.cert = cert;
- context->event_cb(context->cb_ctx, TLS_CERT_CHAIN_FAILURE, &ev);
- wpabuf_free(cert);
-}
-
-
-static void openssl_tls_cert_event(struct tls_connection *conn,
- X509 *err_cert, int depth,
- const char *subject)
-{
- struct wpabuf *cert = NULL;
- union tls_event_data ev;
- struct tls_context *context = conn->context;
- char *altsubject[TLS_MAX_ALT_SUBJECT];
- int alt, num_altsubject = 0;
- GENERAL_NAME *gen;
- void *ext;
- stack_index_t i;
-#ifdef CONFIG_SHA256
- u8 hash[32];
-#endif /* CONFIG_SHA256 */
-
- if (context->event_cb == NULL)
- return;
-
- os_memset(&ev, 0, sizeof(ev));
- if (conn->cert_probe || context->cert_in_cb) {
- cert = get_x509_cert(err_cert);
- ev.peer_cert.cert = cert;
- }
-#ifdef CONFIG_SHA256
- if (cert) {
- const u8 *addr[1];
- size_t len[1];
- addr[0] = wpabuf_head(cert);
- len[0] = wpabuf_len(cert);
- if (sha256_vector(1, addr, len, hash) == 0) {
- ev.peer_cert.hash = hash;
- ev.peer_cert.hash_len = sizeof(hash);
- }
- }
-#endif /* CONFIG_SHA256 */
- ev.peer_cert.depth = depth;
- ev.peer_cert.subject = subject;
-
- ext = X509_get_ext_d2i(err_cert, NID_subject_alt_name, NULL, NULL);
- for (i = 0; ext && i < sk_GENERAL_NAME_num(ext); i++) {
- char *pos;
-
- if (num_altsubject == TLS_MAX_ALT_SUBJECT)
- break;
- gen = sk_GENERAL_NAME_value(ext, i);
- if (gen->type != GEN_EMAIL &&
- gen->type != GEN_DNS &&
- gen->type != GEN_URI)
- continue;
-
- pos = os_malloc(10 + gen->d.ia5->length + 1);
- if (pos == NULL)
- break;
- altsubject[num_altsubject++] = pos;
-
- switch (gen->type) {
- case GEN_EMAIL:
- os_memcpy(pos, "EMAIL:", 6);
- pos += 6;
- break;
- case GEN_DNS:
- os_memcpy(pos, "DNS:", 4);
- pos += 4;
- break;
- case GEN_URI:
- os_memcpy(pos, "URI:", 4);
- pos += 4;
- break;
- }
-
- os_memcpy(pos, gen->d.ia5->data, gen->d.ia5->length);
- pos += gen->d.ia5->length;
- *pos = '\0';
- }
-
- for (alt = 0; alt < num_altsubject; alt++)
- ev.peer_cert.altsubject[alt] = altsubject[alt];
- ev.peer_cert.num_altsubject = num_altsubject;
-
- context->event_cb(context->cb_ctx, TLS_PEER_CERTIFICATE, &ev);
- wpabuf_free(cert);
- for (alt = 0; alt < num_altsubject; alt++)
- os_free(altsubject[alt]);
-}
-
-
-#ifdef OPENSSL_IS_BORINGSSL
-
-/*
- * CertID ::= SEQUENCE {
- * hashAlgorithm AlgorithmIdentifier,
- * issuerNameHash OCTET STRING, -- Hash of Issuer's DN
- * issuerKeyHash OCTET STRING, -- Hash of Issuer's public key
- * serialNumber CertificateSerialNumber }
- */
-typedef struct {
- X509_ALGOR *hashAlgorithm;
- ASN1_OCTET_STRING *issuerNameHash;
- ASN1_OCTET_STRING *issuerKeyHash;
- ASN1_INTEGER *serialNumber;
-} CertID;
-
-/*
- * ResponseBytes ::= SEQUENCE {
- * responseType OBJECT IDENTIFIER,
- * response OCTET STRING }
- */
-typedef struct {
- ASN1_OBJECT *responseType;
- ASN1_OCTET_STRING *response;
-} ResponseBytes;
-
-/*
- * OCSPResponse ::= SEQUENCE {
- * responseStatus OCSPResponseStatus,
- * responseBytes [0] EXPLICIT ResponseBytes OPTIONAL }
- */
-typedef struct {
- ASN1_ENUMERATED *responseStatus;
- ResponseBytes *responseBytes;
-} OCSPResponse;
-
-ASN1_SEQUENCE(ResponseBytes) = {
- ASN1_SIMPLE(ResponseBytes, responseType, ASN1_OBJECT),
- ASN1_SIMPLE(ResponseBytes, response, ASN1_OCTET_STRING)
-} ASN1_SEQUENCE_END(ResponseBytes);
-
-ASN1_SEQUENCE(OCSPResponse) = {
- ASN1_SIMPLE(OCSPResponse, responseStatus, ASN1_ENUMERATED),
- ASN1_EXP_OPT(OCSPResponse, responseBytes, ResponseBytes, 0)
-} ASN1_SEQUENCE_END(OCSPResponse);
-
-IMPLEMENT_ASN1_FUNCTIONS(OCSPResponse);
-
-/*
- * ResponderID ::= CHOICE {
- * byName [1] Name,
- * byKey [2] KeyHash }
- */
-typedef struct {
- int type;
- union {
- X509_NAME *byName;
- ASN1_OCTET_STRING *byKey;
- } value;
-} ResponderID;
-
-/*
- * RevokedInfo ::= SEQUENCE {
- * revocationTime GeneralizedTime,
- * revocationReason [0] EXPLICIT CRLReason OPTIONAL }
- */
-typedef struct {
- ASN1_GENERALIZEDTIME *revocationTime;
- ASN1_ENUMERATED *revocationReason;
-} RevokedInfo;
-
-/*
- * CertStatus ::= CHOICE {
- * good [0] IMPLICIT NULL,
- * revoked [1] IMPLICIT RevokedInfo,
- * unknown [2] IMPLICIT UnknownInfo }
- */
-typedef struct {
- int type;
- union {
- ASN1_NULL *good;
- RevokedInfo *revoked;
- ASN1_NULL *unknown;
- } value;
-} CertStatus;
-
-/*
- * SingleResponse ::= SEQUENCE {
- * certID CertID,
- * certStatus CertStatus,
- * thisUpdate GeneralizedTime,
- * nextUpdate [0] EXPLICIT GeneralizedTime OPTIONAL,
- * singleExtensions [1] EXPLICIT Extensions OPTIONAL }
- */
-typedef struct {
- CertID *certID;
- CertStatus *certStatus;
- ASN1_GENERALIZEDTIME *thisUpdate;
- ASN1_GENERALIZEDTIME *nextUpdate;
- STACK_OF(X509_EXTENSION) *singleExtensions;
-} SingleResponse;
-
-/*
- * ResponseData ::= SEQUENCE {
- * version [0] EXPLICIT Version DEFAULT v1,
- * responderID ResponderID,
- * producedAt GeneralizedTime,
- * responses SEQUENCE OF SingleResponse,
- * responseExtensions [1] EXPLICIT Extensions OPTIONAL }
- */
-typedef struct {
- ASN1_INTEGER *version;
- ResponderID *responderID;
- ASN1_GENERALIZEDTIME *producedAt;
- STACK_OF(SingleResponse) *responses;
- STACK_OF(X509_EXTENSION) *responseExtensions;
-} ResponseData;
-
-/*
- * BasicOCSPResponse ::= SEQUENCE {
- * tbsResponseData ResponseData,
- * signatureAlgorithm AlgorithmIdentifier,
- * signature BIT STRING,
- * certs [0] EXPLICIT SEQUENCE OF Certificate OPTIONAL }
- */
-typedef struct {
- ResponseData *tbsResponseData;
- X509_ALGOR *signatureAlgorithm;
- ASN1_BIT_STRING *signature;
- STACK_OF(X509) *certs;
-} BasicOCSPResponse;
-
-ASN1_SEQUENCE(CertID) = {
- ASN1_SIMPLE(CertID, hashAlgorithm, X509_ALGOR),
- ASN1_SIMPLE(CertID, issuerNameHash, ASN1_OCTET_STRING),
- ASN1_SIMPLE(CertID, issuerKeyHash, ASN1_OCTET_STRING),
- ASN1_SIMPLE(CertID, serialNumber, ASN1_INTEGER)
-} ASN1_SEQUENCE_END(CertID);
-
-ASN1_CHOICE(ResponderID) = {
- ASN1_EXP(ResponderID, value.byName, X509_NAME, 1),
- ASN1_EXP(ResponderID, value.byKey, ASN1_OCTET_STRING, 2)
-} ASN1_CHOICE_END(ResponderID);
-
-ASN1_SEQUENCE(RevokedInfo) = {
- ASN1_SIMPLE(RevokedInfo, revocationTime, ASN1_GENERALIZEDTIME),
- ASN1_EXP_OPT(RevokedInfo, revocationReason, ASN1_ENUMERATED, 0)
-} ASN1_SEQUENCE_END(RevokedInfo);
-
-ASN1_CHOICE(CertStatus) = {
- ASN1_IMP(CertStatus, value.good, ASN1_NULL, 0),
- ASN1_IMP(CertStatus, value.revoked, RevokedInfo, 1),
- ASN1_IMP(CertStatus, value.unknown, ASN1_NULL, 2)
-} ASN1_CHOICE_END(CertStatus);
-
-ASN1_SEQUENCE(SingleResponse) = {
- ASN1_SIMPLE(SingleResponse, certID, CertID),
- ASN1_SIMPLE(SingleResponse, certStatus, CertStatus),
- ASN1_SIMPLE(SingleResponse, thisUpdate, ASN1_GENERALIZEDTIME),
- ASN1_EXP_OPT(SingleResponse, nextUpdate, ASN1_GENERALIZEDTIME, 0),
- ASN1_EXP_SEQUENCE_OF_OPT(SingleResponse, singleExtensions,
- X509_EXTENSION, 1)
-} ASN1_SEQUENCE_END(SingleResponse);
-
-ASN1_SEQUENCE(ResponseData) = {
- ASN1_EXP_OPT(ResponseData, version, ASN1_INTEGER, 0),
- ASN1_SIMPLE(ResponseData, responderID, ResponderID),
- ASN1_SIMPLE(ResponseData, producedAt, ASN1_GENERALIZEDTIME),
- ASN1_SEQUENCE_OF(ResponseData, responses, SingleResponse),
- ASN1_EXP_SEQUENCE_OF_OPT(ResponseData, responseExtensions,
- X509_EXTENSION, 1)
-} ASN1_SEQUENCE_END(ResponseData);
-
-ASN1_SEQUENCE(BasicOCSPResponse) = {
- ASN1_SIMPLE(BasicOCSPResponse, tbsResponseData, ResponseData),
- ASN1_SIMPLE(BasicOCSPResponse, signatureAlgorithm, X509_ALGOR),
- ASN1_SIMPLE(BasicOCSPResponse, signature, ASN1_BIT_STRING),
- ASN1_EXP_SEQUENCE_OF_OPT(BasicOCSPResponse, certs, X509, 0)
-} ASN1_SEQUENCE_END(BasicOCSPResponse);
-
-IMPLEMENT_ASN1_FUNCTIONS(BasicOCSPResponse);
-
-#define sk_SingleResponse_num(sk) \
-sk_num(CHECKED_CAST(_STACK *, STACK_OF(SingleResponse) *, sk))
-
-#define sk_SingleResponse_value(sk, i) \
- ((SingleResponse *) \
- sk_value(CHECKED_CAST(_STACK *, STACK_OF(SingleResponse) *, sk), (i)))
-
-
-static char * mem_bio_to_str(BIO *out)
-{
- char *txt;
- size_t rlen;
- int res;
-
- rlen = BIO_ctrl_pending(out);
- txt = os_malloc(rlen + 1);
- if (!txt) {
- BIO_free(out);
- return NULL;
- }
-
- res = BIO_read(out, txt, rlen);
- BIO_free(out);
- if (res < 0) {
- os_free(txt);
- return NULL;
- }
-
- txt[res] = '\0';
- return txt;
-}
-
-
-static char * generalizedtime_str(ASN1_GENERALIZEDTIME *t)
-{
- BIO *out;
-
- out = BIO_new(BIO_s_mem());
- if (!out)
- return NULL;
-
- if (!ASN1_GENERALIZEDTIME_print(out, t)) {
- BIO_free(out);
- return NULL;
- }
-
- return mem_bio_to_str(out);
-}
-
-
-static char * responderid_str(ResponderID *rid)
-{
- BIO *out;
-
- out = BIO_new(BIO_s_mem());
- if (!out)
- return NULL;
-
- switch (rid->type) {
- case 0:
- X509_NAME_print_ex(out, rid->value.byName, 0, XN_FLAG_ONELINE);
- break;
- case 1:
- i2a_ASN1_STRING(out, rid->value.byKey, V_ASN1_OCTET_STRING);
- break;
- default:
- BIO_free(out);
- return NULL;
- }
-
- return mem_bio_to_str(out);
-}
-
-
-static char * octet_string_str(ASN1_OCTET_STRING *o)
-{
- BIO *out;
-
- out = BIO_new(BIO_s_mem());
- if (!out)
- return NULL;
-
- i2a_ASN1_STRING(out, o, V_ASN1_OCTET_STRING);
- return mem_bio_to_str(out);
-}
-
-
-static char * integer_str(ASN1_INTEGER *i)
-{
- BIO *out;
-
- out = BIO_new(BIO_s_mem());
- if (!out)
- return NULL;
-
- i2a_ASN1_INTEGER(out, i);
- return mem_bio_to_str(out);
-}
-
-
-static char * algor_str(X509_ALGOR *alg)
-{
- BIO *out;
-
- out = BIO_new(BIO_s_mem());
- if (!out)
- return NULL;
-
- i2a_ASN1_OBJECT(out, alg->algorithm);
- return mem_bio_to_str(out);
-}
-
-
-static char * extensions_str(const char *title, STACK_OF(X509_EXTENSION) *ext)
-{
- BIO *out;
-
- if (!ext)
- return NULL;
-
- out = BIO_new(BIO_s_mem());
- if (!out)
- return NULL;
-
- if (!X509V3_extensions_print(out, title, ext, 0, 0)) {
- BIO_free(out);
- return NULL;
- }
- return mem_bio_to_str(out);
-}
-
-
-static int ocsp_resp_valid(ASN1_GENERALIZEDTIME *thisupd,
- ASN1_GENERALIZEDTIME *nextupd)
-{
- time_t now, tmp;
-
- if (!ASN1_GENERALIZEDTIME_check(thisupd)) {
- wpa_printf(MSG_DEBUG,
- "OpenSSL: Invalid OCSP response thisUpdate");
- return 0;
- }
-
- time(&now);
- tmp = now + 5 * 60; /* allow five minute clock difference */
- if (X509_cmp_time(thisupd, &tmp) > 0) {
- wpa_printf(MSG_DEBUG, "OpenSSL: OCSP response not yet valid");
- return 0;
- }
-
- if (!nextupd)
- return 1; /* OK - no limit on response age */
-
- if (!ASN1_GENERALIZEDTIME_check(nextupd)) {
- wpa_printf(MSG_DEBUG,
- "OpenSSL: Invalid OCSP response nextUpdate");
- return 0;
- }
-
- tmp = now - 5 * 60; /* allow five minute clock difference */
- if (X509_cmp_time(nextupd, &tmp) < 0) {
- wpa_printf(MSG_DEBUG, "OpenSSL: OCSP response expired");
- return 0;
- }
-
- if (ASN1_STRING_cmp(nextupd, thisupd) < 0) {
- wpa_printf(MSG_DEBUG,
- "OpenSSL: OCSP response nextUpdate before thisUpdate");
- return 0;
- }
-
- /* Both thisUpdate and nextUpdate are valid */
- return -1;
-}
-
-
-static int issuer_match(X509 *cert, X509 *issuer, CertID *certid)
-{
- X509_NAME *iname;
- ASN1_BIT_STRING *ikey;
- const EVP_MD *dgst;
- unsigned int len;
- unsigned char md[EVP_MAX_MD_SIZE];
- ASN1_OCTET_STRING *hash;
- char *txt;
-
- dgst = EVP_get_digestbyobj(certid->hashAlgorithm->algorithm);
- if (!dgst) {
- wpa_printf(MSG_DEBUG,
- "OpenSSL: Could not find matching hash algorithm for OCSP");
- return -1;
- }
-
- iname = X509_get_issuer_name(cert);
- if (!X509_NAME_digest(iname, dgst, md, &len))
- return -1;
- hash = ASN1_OCTET_STRING_new();
- if (!hash)
- return -1;
- if (!ASN1_OCTET_STRING_set(hash, md, len)) {
- ASN1_OCTET_STRING_free(hash);
- return -1;
- }
-
- txt = octet_string_str(hash);
- if (txt) {
- wpa_printf(MSG_DEBUG, "OpenSSL: calculated issuerNameHash: %s",
- txt);
- os_free(txt);
- }
-
- if (ASN1_OCTET_STRING_cmp(certid->issuerNameHash, hash)) {
- ASN1_OCTET_STRING_free(hash);
- return -1;
- }
-
- ikey = X509_get0_pubkey_bitstr(issuer);
- if (!EVP_Digest(ikey->data, ikey->length, md, &len, dgst, NULL) ||
- !ASN1_OCTET_STRING_set(hash, md, len)) {
- ASN1_OCTET_STRING_free(hash);
- return -1;
- }
-
- txt = octet_string_str(hash);
- if (txt) {
- wpa_printf(MSG_DEBUG, "OpenSSL: calculated issuerKeyHash: %s",
- txt);
- os_free(txt);
- }
-
- if (ASN1_OCTET_STRING_cmp(certid->issuerKeyHash, hash)) {
- ASN1_OCTET_STRING_free(hash);
- return -1;
- }
-
- ASN1_OCTET_STRING_free(hash);
- return 0;
-}
-
-
-static X509 * ocsp_find_signer(STACK_OF(X509) *certs, ResponderID *rid)
-{
- unsigned int i;
- unsigned char hash[SHA_DIGEST_LENGTH];
-
- if (rid->type == 0) {
- /* byName */
- return X509_find_by_subject(certs, rid->value.byName);
- }
-
- /* byKey */
- if (rid->value.byKey->length != SHA_DIGEST_LENGTH)
- return NULL;
- for (i = 0; i < sk_X509_num(certs); i++) {
- X509 *x = sk_X509_value(certs, i);
-
- X509_pubkey_digest(x, EVP_sha1(), hash, NULL);
- if (os_memcmp(rid->value.byKey->data, hash,
- SHA_DIGEST_LENGTH) == 0)
- return x;
- }
-
- return NULL;
-}
-
-
-enum ocsp_result {
- OCSP_GOOD, OCSP_REVOKED, OCSP_NO_RESPONSE, OCSP_INVALID
-};
-
-static enum ocsp_result check_ocsp_resp(struct tls_connection *conn,
- X509 *cert, X509 *issuer)
-{
- const uint8_t *resp_data;
- size_t resp_len;
- OCSPResponse *resp;
- int status;
- ResponseBytes *bytes;
- const u8 *basic_data;
- size_t basic_len;
- BasicOCSPResponse *basic;
- ResponseData *rd;
- char *txt;
- int i, num;
- unsigned int j, num_resp;
- SingleResponse *matching_resp = NULL, *cmp_sresp;
- enum ocsp_result result = OCSP_INVALID;
- X509_STORE *store;
- STACK_OF(X509) *untrusted = NULL, *certs = NULL, *chain = NULL;
- X509_STORE_CTX ctx;
- X509 *signer, *tmp_cert;
- int signer_trusted = 0;
- EVP_PKEY *skey;
- int ret;
- char buf[256];
-
- txt = integer_str(X509_get_serialNumber(cert));
- if (txt) {
- wpa_printf(MSG_DEBUG,
- "OpenSSL: Searching OCSP response for peer certificate serialNumber: %s", txt);
- os_free(txt);
- }
-
- SSL_get0_ocsp_response(conn->ssl, &resp_data, &resp_len);
- if (resp_data == NULL || resp_len == 0) {
- wpa_printf(MSG_DEBUG, "OpenSSL: No OCSP response received");
- return OCSP_NO_RESPONSE;
- }
-
- wpa_hexdump(MSG_DEBUG, "OpenSSL: OCSP response", resp_data, resp_len);
-
- resp = d2i_OCSPResponse(NULL, &resp_data, resp_len);
- if (!resp) {
- wpa_printf(MSG_INFO, "OpenSSL: Failed to parse OCSPResponse");
- return OCSP_INVALID;
- }
-
- status = ASN1_ENUMERATED_get(resp->responseStatus);
- if (status != 0) {
- wpa_printf(MSG_INFO, "OpenSSL: OCSP responder error %d",
- status);
- return OCSP_INVALID;
- }
-
- bytes = resp->responseBytes;
-
- if (!bytes ||
- OBJ_obj2nid(bytes->responseType) != NID_id_pkix_OCSP_basic) {
- wpa_printf(MSG_INFO,
- "OpenSSL: Could not find BasicOCSPResponse");
- return OCSP_INVALID;
- }
-
- basic_data = ASN1_STRING_data(bytes->response);
- basic_len = ASN1_STRING_length(bytes->response);
- wpa_hexdump(MSG_DEBUG, "OpenSSL: BasicOCSPResponse",
- basic_data, basic_len);
-
- basic = d2i_BasicOCSPResponse(NULL, &basic_data, basic_len);
- if (!basic) {
- wpa_printf(MSG_INFO,
- "OpenSSL: Could not parse BasicOCSPResponse");
- OCSPResponse_free(resp);
- return OCSP_INVALID;
- }
-
- rd = basic->tbsResponseData;
-
- if (basic->certs) {
- untrusted = sk_X509_dup(basic->certs);
-
- num = sk_X509_num(basic->certs);
- for (i = 0; i < num; i++) {
- X509 *extra_cert;
-
- extra_cert = sk_X509_value(basic->certs, i);
- X509_NAME_oneline(X509_get_subject_name(extra_cert),
- buf, sizeof(buf));
- wpa_printf(MSG_DEBUG,
- "OpenSSL: BasicOCSPResponse cert %s", buf);
-
- if (!sk_X509_push(untrusted, extra_cert)) {
- wpa_printf(MSG_DEBUG,
- "OpenSSL: Could not add certificate to the untrusted stack");
- }
- }
- }
-
- store = SSL_CTX_get_cert_store(conn->ssl_ctx);
- if (conn->peer_issuer) {
- if (X509_STORE_add_cert(store, conn->peer_issuer) != 1) {
- tls_show_errors(MSG_INFO, __func__,
- "OpenSSL: Could not add issuer to certificate store");
- }
- certs = sk_X509_new_null();
- if (certs) {
- tmp_cert = X509_dup(conn->peer_issuer);
- if (tmp_cert && !sk_X509_push(certs, tmp_cert)) {
- tls_show_errors(
- MSG_INFO, __func__,
- "OpenSSL: Could not add issuer to OCSP responder trust store");
- X509_free(tmp_cert);
- sk_X509_free(certs);
- certs = NULL;
- }
- if (certs && conn->peer_issuer_issuer) {
- tmp_cert = X509_dup(conn->peer_issuer_issuer);
- if (tmp_cert &&
- !sk_X509_push(certs, tmp_cert)) {
- tls_show_errors(
- MSG_INFO, __func__,
- "OpenSSL: Could not add issuer's issuer to OCSP responder trust store");
- X509_free(tmp_cert);
- }
- }
- }
- }
-
- signer = ocsp_find_signer(certs, rd->responderID);
- if (!signer)
- signer = ocsp_find_signer(untrusted, rd->responderID);
- else
- signer_trusted = 1;
- if (!signer) {
- wpa_printf(MSG_DEBUG,
- "OpenSSL: Could not find OCSP signer certificate");
- goto fail;
- }
-
- skey = X509_get_pubkey(signer);
- if (!skey) {
- wpa_printf(MSG_DEBUG,
- "OpenSSL: Could not get OCSP signer public key");
- goto fail;
- }
- if (ASN1_item_verify(ASN1_ITEM_rptr(ResponseData),
- basic->signatureAlgorithm, basic->signature,
- basic->tbsResponseData, skey) <= 0) {
- wpa_printf(MSG_DEBUG,
- "OpenSSL: BasicOCSPResponse signature is invalid");
- goto fail;
- }
-
- X509_NAME_oneline(X509_get_subject_name(signer), buf, sizeof(buf));
- wpa_printf(MSG_DEBUG,
- "OpenSSL: Found OCSP signer certificate %s and verified BasicOCSPResponse signature",
- buf);
-
- if (!X509_STORE_CTX_init(&ctx, store, signer, untrusted))
- goto fail;
- X509_STORE_CTX_set_purpose(&ctx, X509_PURPOSE_OCSP_HELPER);
- ret = X509_verify_cert(&ctx);
- chain = X509_STORE_CTX_get1_chain(&ctx);
- X509_STORE_CTX_cleanup(&ctx);
- if (ret <= 0) {
- wpa_printf(MSG_DEBUG,
- "OpenSSL: Could not validate OCSP signer certificate");
- goto fail;
- }
-
- if (!chain || sk_X509_num(chain) <= 0) {
- wpa_printf(MSG_DEBUG, "OpenSSL: No OCSP signer chain found");
- goto fail;
- }
-
- if (!signer_trusted) {
- X509_check_purpose(signer, -1, 0);
- if ((signer->ex_flags & EXFLAG_XKUSAGE) &&
- (signer->ex_xkusage & XKU_OCSP_SIGN)) {
- wpa_printf(MSG_DEBUG,
- "OpenSSL: OCSP signer certificate delegation OK");
- } else {
- tmp_cert = sk_X509_value(chain, sk_X509_num(chain) - 1);
- if (X509_check_trust(tmp_cert, NID_OCSP_sign, 0) !=
- X509_TRUST_TRUSTED) {
- wpa_printf(MSG_DEBUG,
- "OpenSSL: OCSP signer certificate not trusted");
- result = OCSP_NO_RESPONSE;
- goto fail;
- }
- }
- }
-
- wpa_printf(MSG_DEBUG, "OpenSSL: OCSP version: %lu",
- ASN1_INTEGER_get(rd->version));
-
- txt = responderid_str(rd->responderID);
- if (txt) {
- wpa_printf(MSG_DEBUG, "OpenSSL: OCSP responderID: %s",
- txt);
- os_free(txt);
- }
-
- txt = generalizedtime_str(rd->producedAt);
- if (txt) {
- wpa_printf(MSG_DEBUG, "OpenSSL: OCSP producedAt: %s",
- txt);
- os_free(txt);
- }
-
- num_resp = sk_SingleResponse_num(rd->responses);
- if (num_resp == 0) {
- wpa_printf(MSG_DEBUG,
- "OpenSSL: No OCSP SingleResponse within BasicOCSPResponse");
- result = OCSP_NO_RESPONSE;
- goto fail;
- }
- cmp_sresp = sk_SingleResponse_value(rd->responses, 0);
- for (j = 0; j < num_resp; j++) {
- SingleResponse *sresp;
- CertID *cid1, *cid2;
-
- sresp = sk_SingleResponse_value(rd->responses, j);
- wpa_printf(MSG_DEBUG, "OpenSSL: OCSP SingleResponse %u/%u",
- j + 1, num_resp);
-
- txt = algor_str(sresp->certID->hashAlgorithm);
- if (txt) {
- wpa_printf(MSG_DEBUG,
- "OpenSSL: certID hashAlgorithm: %s", txt);
- os_free(txt);
- }
+static struct wpabuf * get_x509_cert(X509 *cert)
+{
+ struct wpabuf *buf;
+ u8 *tmp;
- txt = octet_string_str(sresp->certID->issuerNameHash);
- if (txt) {
- wpa_printf(MSG_DEBUG,
- "OpenSSL: certID issuerNameHash: %s", txt);
- os_free(txt);
- }
+ int cert_len = i2d_X509(cert, NULL);
+ if (cert_len <= 0)
+ return NULL;
- txt = octet_string_str(sresp->certID->issuerKeyHash);
- if (txt) {
- wpa_printf(MSG_DEBUG,
- "OpenSSL: certID issuerKeyHash: %s", txt);
- os_free(txt);
- }
+ buf = wpabuf_alloc(cert_len);
+ if (buf == NULL)
+ return NULL;
- txt = integer_str(sresp->certID->serialNumber);
- if (txt) {
- wpa_printf(MSG_DEBUG,
- "OpenSSL: certID serialNumber: %s", txt);
- os_free(txt);
- }
+ tmp = wpabuf_put(buf, cert_len);
+ i2d_X509(cert, &tmp);
+ return buf;
+}
- switch (sresp->certStatus->type) {
- case 0:
- wpa_printf(MSG_DEBUG, "OpenSSL: certStatus: good");
- break;
- case 1:
- wpa_printf(MSG_DEBUG, "OpenSSL: certStatus: revoked");
- break;
- default:
- wpa_printf(MSG_DEBUG, "OpenSSL: certStatus: unknown");
- break;
- }
- txt = generalizedtime_str(sresp->thisUpdate);
- if (txt) {
- wpa_printf(MSG_DEBUG, "OpenSSL: thisUpdate: %s", txt);
- os_free(txt);
- }
+static void openssl_tls_fail_event(struct tls_connection *conn,
+ X509 *err_cert, int err, int depth,
+ const char *subject, const char *err_str,
+ enum tls_fail_reason reason)
+{
+ union tls_event_data ev;
+ struct wpabuf *cert = NULL;
+ struct tls_context *context = conn->context;
- if (sresp->nextUpdate) {
- txt = generalizedtime_str(sresp->nextUpdate);
- if (txt) {
- wpa_printf(MSG_DEBUG, "OpenSSL: nextUpdate: %s",
- txt);
- os_free(txt);
- }
- }
+ if (context->event_cb == NULL)
+ return;
- txt = extensions_str("singleExtensions",
- sresp->singleExtensions);
- if (txt) {
- wpa_printf(MSG_DEBUG, "OpenSSL: %s", txt);
- os_free(txt);
- }
+ cert = get_x509_cert(err_cert);
+ os_memset(&ev, 0, sizeof(ev));
+ ev.cert_fail.reason = reason != TLS_FAIL_UNSPECIFIED ?
+ reason : openssl_tls_fail_reason(err);
+ ev.cert_fail.depth = depth;
+ ev.cert_fail.subject = subject;
+ ev.cert_fail.reason_txt = err_str;
+ ev.cert_fail.cert = cert;
+ context->event_cb(context->cb_ctx, TLS_CERT_CHAIN_FAILURE, &ev);
+ wpabuf_free(cert);
+}
- cid1 = cmp_sresp->certID;
- cid2 = sresp->certID;
- if (j > 0 &&
- (OBJ_cmp(cid1->hashAlgorithm->algorithm,
- cid2->hashAlgorithm->algorithm) != 0 ||
- ASN1_OCTET_STRING_cmp(cid1->issuerNameHash,
- cid2->issuerNameHash) != 0 ||
- ASN1_OCTET_STRING_cmp(cid1->issuerKeyHash,
- cid2->issuerKeyHash) != 0)) {
- wpa_printf(MSG_DEBUG,
- "OpenSSL: Different OCSP response issuer information between SingleResponse values within BasicOCSPResponse");
- goto fail;
- }
- if (!matching_resp && issuer &&
- ASN1_INTEGER_cmp(sresp->certID->serialNumber,
- X509_get_serialNumber(cert)) == 0 &&
- issuer_match(cert, issuer, sresp->certID) == 0) {
- wpa_printf(MSG_DEBUG,
- "OpenSSL: This response matches peer certificate");
- matching_resp = sresp;
+static void openssl_tls_cert_event(struct tls_connection *conn,
+ X509 *err_cert, int depth,
+ const char *subject)
+{
+ struct wpabuf *cert = NULL;
+ union tls_event_data ev;
+ struct tls_context *context = conn->context;
+ char *altsubject[TLS_MAX_ALT_SUBJECT];
+ int alt, num_altsubject = 0;
+ GENERAL_NAME *gen;
+ void *ext;
+ stack_index_t i;
+#ifdef CONFIG_SHA256
+ u8 hash[32];
+#endif /* CONFIG_SHA256 */
+
+ if (context->event_cb == NULL)
+ return;
+
+ os_memset(&ev, 0, sizeof(ev));
+ if (conn->cert_probe || (conn->flags & TLS_CONN_EXT_CERT_CHECK) ||
+ context->cert_in_cb) {
+ cert = get_x509_cert(err_cert);
+ ev.peer_cert.cert = cert;
+ }
+#ifdef CONFIG_SHA256
+ if (cert) {
+ const u8 *addr[1];
+ size_t len[1];
+ addr[0] = wpabuf_head(cert);
+ len[0] = wpabuf_len(cert);
+ if (sha256_vector(1, addr, len, hash) == 0) {
+ ev.peer_cert.hash = hash;
+ ev.peer_cert.hash_len = sizeof(hash);
}
}
+#endif /* CONFIG_SHA256 */
+ ev.peer_cert.depth = depth;
+ ev.peer_cert.subject = subject;
- txt = extensions_str("responseExtensions", rd->responseExtensions);
- if (txt) {
- wpa_printf(MSG_DEBUG, "OpenSSL: %s", txt);
- os_free(txt);
- }
+ ext = X509_get_ext_d2i(err_cert, NID_subject_alt_name, NULL, NULL);
+ for (i = 0; ext && i < sk_GENERAL_NAME_num(ext); i++) {
+ char *pos;
- if (!matching_resp) {
- wpa_printf(MSG_DEBUG,
- "OpenSSL: Could not find OCSP response that matches the peer certificate");
- result = OCSP_NO_RESPONSE;
- goto fail;
- }
+ if (num_altsubject == TLS_MAX_ALT_SUBJECT)
+ break;
+ gen = sk_GENERAL_NAME_value(ext, i);
+ if (gen->type != GEN_EMAIL &&
+ gen->type != GEN_DNS &&
+ gen->type != GEN_URI)
+ continue;
- if (!ocsp_resp_valid(matching_resp->thisUpdate,
- matching_resp->nextUpdate)) {
- wpa_printf(MSG_DEBUG,
- "OpenSSL: OCSP response not valid at this time");
- goto fail;
- }
+ pos = os_malloc(10 + gen->d.ia5->length + 1);
+ if (pos == NULL)
+ break;
+ altsubject[num_altsubject++] = pos;
- if (matching_resp->certStatus->type == 1) {
- wpa_printf(MSG_DEBUG,
- "OpenSSL: OCSP response indicated that the peer certificate has been revoked");
- result = OCSP_REVOKED;
- goto fail;
- }
+ switch (gen->type) {
+ case GEN_EMAIL:
+ os_memcpy(pos, "EMAIL:", 6);
+ pos += 6;
+ break;
+ case GEN_DNS:
+ os_memcpy(pos, "DNS:", 4);
+ pos += 4;
+ break;
+ case GEN_URI:
+ os_memcpy(pos, "URI:", 4);
+ pos += 4;
+ break;
+ }
- if (matching_resp->certStatus->type != 0) {
- wpa_printf(MSG_DEBUG,
- "OpenSSL: OCSP response did not indicate good status");
- result = OCSP_NO_RESPONSE;
- goto fail;
+ os_memcpy(pos, gen->d.ia5->data, gen->d.ia5->length);
+ pos += gen->d.ia5->length;
+ *pos = '\0';
}
+ sk_GENERAL_NAME_pop_free(ext, GENERAL_NAME_free);
- /* OCSP response indicated the certificate is good. */
- result = OCSP_GOOD;
-fail:
- sk_X509_pop_free(chain, X509_free);
- sk_X509_free(untrusted);
- sk_X509_pop_free(certs, X509_free);
- BasicOCSPResponse_free(basic);
- OCSPResponse_free(resp);
+ for (alt = 0; alt < num_altsubject; alt++)
+ ev.peer_cert.altsubject[alt] = altsubject[alt];
+ ev.peer_cert.num_altsubject = num_altsubject;
- return result;
+ context->event_cb(context->cb_ctx, TLS_PEER_CERTIFICATE, &ev);
+ wpabuf_free(cert);
+ for (alt = 0; alt < num_altsubject; alt++)
+ os_free(altsubject[alt]);
}
-#endif /* OPENSSL_IS_BORINGSSL */
-
static int tls_verify_cb(int preverify_ok, X509_STORE_CTX *x509_ctx)
{
}
#ifdef OPENSSL_IS_BORINGSSL
- if (depth == 0 && (conn->flags & TLS_CONN_REQUEST_OCSP)) {
+ if (depth == 0 && (conn->flags & TLS_CONN_REQUEST_OCSP) &&
+ preverify_ok) {
enum ocsp_result res;
- res = check_ocsp_resp(conn, err_cert, conn->peer_issuer);
+ res = check_ocsp_resp(conn->ssl_ctx, conn->ssl, err_cert,
+ conn->peer_issuer,
+ conn->peer_issuer_issuer);
if (res == OCSP_REVOKED) {
preverify_ok = 0;
openssl_tls_fail_event(conn, err_cert, err, depth, buf,
}
#endif /* OPENSSL_IS_BORINGSSL */
- if (preverify_ok && context->event_cb != NULL)
+ if (depth == 0 && preverify_ok && context->event_cb != NULL)
context->event_cb(context->cb_ctx,
TLS_CERT_CHAIN_SUCCESS, NULL);
}
#ifdef ANDROID
+ /* Single alias */
if (ca_cert && os_strncmp("keystore://", ca_cert, 11) == 0) {
- BIO *bio = BIO_from_keystore(&ca_cert[11]);
- STACK_OF(X509_INFO) *stack = NULL;
- stack_index_t i;
-
- if (bio) {
- stack = PEM_X509_INFO_read_bio(bio, NULL, NULL, NULL);
- BIO_free(bio);
- }
- if (!stack)
+ if (tls_add_ca_from_keystore(SSL_CTX_get_cert_store(ssl_ctx),
+ &ca_cert[11]) < 0)
return -1;
+ SSL_set_verify(conn->ssl, SSL_VERIFY_PEER, tls_verify_cb);
+ return 0;
+ }
- for (i = 0; i < sk_X509_INFO_num(stack); ++i) {
- X509_INFO *info = sk_X509_INFO_value(stack, i);
- if (info->x509) {
- X509_STORE_add_cert(ssl_ctx->cert_store,
- info->x509);
- }
- if (info->crl) {
- X509_STORE_add_crl(ssl_ctx->cert_store,
- info->crl);
+ /* Multiple aliases separated by space */
+ if (ca_cert && os_strncmp("keystores://", ca_cert, 12) == 0) {
+ char *aliases = os_strdup(&ca_cert[12]);
+ const char *delim = " ";
+ int rc = 0;
+ char *savedptr;
+ char *alias;
+
+ if (!aliases)
+ return -1;
+ alias = strtok_r(aliases, delim, &savedptr);
+ for (; alias; alias = strtok_r(NULL, delim, &savedptr)) {
+ if (tls_add_ca_from_keystore_encoded(
+ SSL_CTX_get_cert_store(ssl_ctx), alias)) {
+ wpa_printf(MSG_WARNING,
+ "OpenSSL: %s - Failed to add ca_cert %s from keystore",
+ __func__, alias);
+ rc = -1;
+ break;
}
}
- sk_X509_INFO_pop_free(stack, X509_INFO_free);
+ os_free(aliases);
+ if (rc)
+ return rc;
+
SSL_set_verify(conn->ssl, SSL_VERIFY_PEER, tls_verify_cb);
return 0;
}
#ifdef SSL_OP_NO_TICKET
if (flags & TLS_CONN_DISABLE_SESSION_TICKET)
SSL_set_options(ssl, SSL_OP_NO_TICKET);
-#ifdef SSL_clear_options
else
SSL_clear_options(ssl, SSL_OP_NO_TICKET);
-#endif /* SSL_clear_options */
#endif /* SSL_OP_NO_TICKET */
#ifdef SSL_OP_NO_TLSv1
if (client_cert == NULL && client_cert_blob == NULL)
return 0;
+#ifdef PKCS12_FUNCS
+#if OPENSSL_VERSION_NUMBER < 0x10002000L || defined(LIBRESSL_VERSION_NUMBER)
+ /*
+ * Clear previously set extra chain certificates, if any, from PKCS#12
+ * processing in tls_parse_pkcs12() to allow OpenSSL to build a new
+ * chain properly.
+ */
+ SSL_CTX_clear_extra_chain_certs(conn->ssl_ctx);
+#endif /* OPENSSL_VERSION_NUMBER < 0x10002000L */
+#endif /* PKCS12_FUNCS */
+
if (client_cert_blob &&
SSL_use_certificate_ASN1(conn->ssl, (u8 *) client_cert_blob,
client_cert_blob_len) == 1) {
int ret = -1;
if (bio) {
x509 = PEM_read_bio_X509(bio, NULL, NULL, NULL);
- BIO_free(bio);
}
if (x509) {
if (SSL_use_certificate(conn->ssl, x509) == 1)
ret = 0;
X509_free(x509);
}
+
+ /* Read additional certificates into the chain. */
+ while (bio) {
+ x509 = PEM_read_bio_X509(bio, NULL, NULL, NULL);
+ if (x509) {
+ /* Takes ownership of x509 */
+ SSL_add0_chain_cert(conn->ssl, x509);
+ } else {
+ BIO_free(bio);
+ bio = NULL;
+ }
+ }
return ret;
}
#endif /* ANDROID */
}
if (certs) {
-#if OPENSSL_VERSION_NUMBER >= 0x10002000L
- SSL_clear_chain_certs(ssl);
+#if OPENSSL_VERSION_NUMBER >= 0x10002000L && !defined(LIBRESSL_VERSION_NUMBER)
+ if (ssl)
+ SSL_clear_chain_certs(ssl);
+ else
+ SSL_CTX_clear_chain_certs(data->ssl);
while ((cert = sk_X509_pop(certs)) != NULL) {
X509_NAME_oneline(X509_get_subject_name(cert), buf,
sizeof(buf));
wpa_printf(MSG_DEBUG, "TLS: additional certificate"
" from PKCS12: subject='%s'", buf);
- if (SSL_add1_chain_cert(ssl, cert) != 1) {
+ if ((ssl && SSL_add1_chain_cert(ssl, cert) != 1) ||
+ (!ssl && SSL_CTX_add1_chain_cert(data->ssl,
+ cert) != 1)) {
tls_show_errors(MSG_DEBUG, __func__,
"Failed to add additional certificate");
res = -1;
+ X509_free(cert);
break;
}
+ X509_free(cert);
}
if (!res) {
/* Try to continue anyway */
}
- sk_X509_free(certs);
+ sk_X509_pop_free(certs, X509_free);
#ifndef OPENSSL_IS_BORINGSSL
- res = SSL_build_cert_chain(ssl,
- SSL_BUILD_CHAIN_FLAG_CHECK |
- SSL_BUILD_CHAIN_FLAG_IGNORE_ERROR);
+ if (ssl)
+ res = SSL_build_cert_chain(
+ ssl,
+ SSL_BUILD_CHAIN_FLAG_CHECK |
+ SSL_BUILD_CHAIN_FLAG_IGNORE_ERROR);
+ else
+ res = SSL_CTX_build_cert_chain(
+ data->ssl,
+ SSL_BUILD_CHAIN_FLAG_CHECK |
+ SSL_BUILD_CHAIN_FLAG_IGNORE_ERROR);
if (!res) {
tls_show_errors(MSG_DEBUG, __func__,
"Failed to build certificate chain");
*/
res = 0;
#else /* OPENSSL_VERSION_NUMBER >= 0x10002000L */
-#if OPENSSL_VERSION_NUMBER >= 0x10001000L
SSL_CTX_clear_extra_chain_certs(data->ssl);
-#endif /* OPENSSL_VERSION_NUMBER >= 0x10001000L */
while ((cert = sk_X509_pop(certs)) != NULL) {
X509_NAME_oneline(X509_get_subject_name(cert), buf,
sizeof(buf));
*/
if (SSL_CTX_add_extra_chain_cert(data->ssl, cert) != 1)
{
+ X509_free(cert);
res = -1;
break;
}
}
- sk_X509_free(certs);
+ sk_X509_pop_free(certs, X509_free);
#endif /* OPENSSL_VERSION_NUMBER >= 0x10002000L */
}
if (conn == NULL || keys == NULL)
return -1;
ssl = conn->ssl;
-#if OPENSSL_VERSION_NUMBER < 0x10100000L
- if (ssl == NULL || ssl->s3 == NULL || ssl->session == NULL)
- return -1;
-
- os_memset(keys, 0, sizeof(*keys));
- keys->client_random = ssl->s3->client_random;
- keys->client_random_len = SSL3_RANDOM_SIZE;
- keys->server_random = ssl->s3->server_random;
- keys->server_random_len = SSL3_RANDOM_SIZE;
-#else
if (ssl == NULL)
return -1;
keys->server_random = conn->server_random;
keys->server_random_len = SSL_get_server_random(
ssl, conn->server_random, sizeof(conn->server_random));
-#endif
return 0;
}
-#ifndef CONFIG_FIPS
+#ifdef OPENSSL_NEED_EAP_FAST_PRF
static int openssl_get_keyblock_size(SSL *ssl)
{
-#if OPENSSL_VERSION_NUMBER < 0x10100000L
+#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER)
const EVP_CIPHER *c;
const EVP_MD *h;
int md_size;
return -1;
c = ssl->enc_read_ctx->cipher;
-#if OPENSSL_VERSION_NUMBER >= 0x00909000L
h = EVP_MD_CTX_md(ssl->read_hash);
-#else
- h = ssl->read_hash;
-#endif
if (h)
md_size = EVP_MD_size(h);
-#if OPENSSL_VERSION_NUMBER >= 0x10000000L
else if (ssl->s3)
md_size = ssl->s3->tmp.new_mac_secret_size;
-#endif
else
return -1;
EVP_CIPHER_iv_length(c));
#endif
}
-#endif /* CONFIG_FIPS */
+#endif /* OPENSSL_NEED_EAP_FAST_PRF */
-static int openssl_tls_prf(struct tls_connection *conn,
- const char *label, int server_random_first,
- int skip_keyblock, u8 *out, size_t out_len)
+int tls_connection_export_key(void *tls_ctx, struct tls_connection *conn,
+ const char *label, u8 *out, size_t out_len)
{
-#ifdef CONFIG_FIPS
- wpa_printf(MSG_ERROR, "OpenSSL: TLS keys cannot be exported in FIPS "
- "mode");
- return -1;
-#else /* CONFIG_FIPS */
-#if OPENSSL_VERSION_NUMBER < 0x10100000L
- SSL *ssl;
- u8 *rnd;
- int ret = -1;
- int skip = 0;
- u8 *tmp_out = NULL;
- u8 *_out = out;
- const char *ver;
-
- /*
- * TLS library did not support key generation, so get the needed TLS
- * session parameters and use an internal implementation of TLS PRF to
- * derive the key.
- */
-
- if (conn == NULL)
+ if (!conn ||
+ SSL_export_keying_material(conn->ssl, out, out_len, label,
+ os_strlen(label), NULL, 0, 0) != 1)
return -1;
- ssl = conn->ssl;
- if (ssl == NULL || ssl->s3 == NULL || ssl->session == NULL ||
- ssl->session->master_key_length <= 0)
- return -1;
- ver = SSL_get_version(ssl);
-
- if (skip_keyblock) {
- skip = openssl_get_keyblock_size(ssl);
- if (skip < 0)
- return -1;
- tmp_out = os_malloc(skip + out_len);
- if (!tmp_out)
- return -1;
- _out = tmp_out;
- }
-
- rnd = os_malloc(2 * SSL3_RANDOM_SIZE);
- if (!rnd) {
- os_free(tmp_out);
- return -1;
- }
-
- if (server_random_first) {
- os_memcpy(rnd, ssl->s3->server_random, SSL3_RANDOM_SIZE);
- os_memcpy(rnd + SSL3_RANDOM_SIZE, ssl->s3->client_random,
- SSL3_RANDOM_SIZE);
- } else {
- os_memcpy(rnd, ssl->s3->client_random, SSL3_RANDOM_SIZE);
- os_memcpy(rnd + SSL3_RANDOM_SIZE, ssl->s3->server_random,
- SSL3_RANDOM_SIZE);
- }
+ return 0;
+}
- if (os_strcmp(ver, "TLSv1.2") == 0) {
- tls_prf_sha256(ssl->session->master_key,
- ssl->session->master_key_length,
- label, rnd, 2 * SSL3_RANDOM_SIZE,
- _out, skip + out_len);
- ret = 0;
- } else if (tls_prf_sha1_md5(ssl->session->master_key,
- ssl->session->master_key_length,
- label, rnd, 2 * SSL3_RANDOM_SIZE,
- _out, skip + out_len) == 0) {
- ret = 0;
- }
- os_free(rnd);
- if (ret == 0 && skip_keyblock)
- os_memcpy(out, _out + skip, out_len);
- bin_clear_free(tmp_out, skip);
- return ret;
-#else
+int tls_connection_get_eap_fast_key(void *tls_ctx, struct tls_connection *conn,
+ u8 *out, size_t out_len)
+{
+#ifdef OPENSSL_NEED_EAP_FAST_PRF
SSL *ssl;
SSL_SESSION *sess;
u8 *rnd;
const char *ver;
/*
- * TLS library did not support key generation, so get the needed TLS
- * session parameters and use an internal implementation of TLS PRF to
- * derive the key.
+ * TLS library did not support EAP-FAST key generation, so get the
+ * needed TLS session parameters and use an internal implementation of
+ * TLS PRF to derive the key.
*/
if (conn == NULL)
if (!ver || !sess)
return -1;
- if (skip_keyblock) {
- skip = openssl_get_keyblock_size(ssl);
- if (skip < 0)
- return -1;
- tmp_out = os_malloc(skip + out_len);
- if (!tmp_out)
- return -1;
- _out = tmp_out;
- }
+ skip = openssl_get_keyblock_size(ssl);
+ if (skip < 0)
+ return -1;
+ tmp_out = os_malloc(skip + out_len);
+ if (!tmp_out)
+ return -1;
+ _out = tmp_out;
rnd = os_malloc(2 * SSL3_RANDOM_SIZE);
if (!rnd) {
master_key_len = SSL_SESSION_get_master_key(sess, master_key,
sizeof(master_key));
- if (server_random_first) {
- os_memcpy(rnd, server_random, SSL3_RANDOM_SIZE);
- os_memcpy(rnd + SSL3_RANDOM_SIZE, client_random,
- SSL3_RANDOM_SIZE);
- } else {
- os_memcpy(rnd, client_random, SSL3_RANDOM_SIZE);
- os_memcpy(rnd + SSL3_RANDOM_SIZE, server_random,
- SSL3_RANDOM_SIZE);
- }
+ os_memcpy(rnd, server_random, SSL3_RANDOM_SIZE);
+ os_memcpy(rnd + SSL3_RANDOM_SIZE, client_random, SSL3_RANDOM_SIZE);
if (os_strcmp(ver, "TLSv1.2") == 0) {
tls_prf_sha256(master_key, master_key_len,
- label, rnd, 2 * SSL3_RANDOM_SIZE,
+ "key expansion", rnd, 2 * SSL3_RANDOM_SIZE,
_out, skip + out_len);
ret = 0;
} else if (tls_prf_sha1_md5(master_key, master_key_len,
- label, rnd, 2 * SSL3_RANDOM_SIZE,
+ "key expansion", rnd, 2 * SSL3_RANDOM_SIZE,
_out, skip + out_len) == 0) {
ret = 0;
}
os_memset(master_key, 0, sizeof(master_key));
os_free(rnd);
- if (ret == 0 && skip_keyblock)
+ if (ret == 0)
os_memcpy(out, _out + skip, out_len);
bin_clear_free(tmp_out, skip);
return ret;
-#endif
-#endif /* CONFIG_FIPS */
-}
-
-
-int tls_connection_prf(void *tls_ctx, struct tls_connection *conn,
- const char *label, int server_random_first,
- int skip_keyblock, u8 *out, size_t out_len)
-{
-#if OPENSSL_VERSION_NUMBER >= 0x10001000L
- SSL *ssl;
- if (conn == NULL)
- return -1;
- if (server_random_first || skip_keyblock)
- return openssl_tls_prf(conn, label,
- server_random_first, skip_keyblock,
- out, out_len);
- ssl = conn->ssl;
- if (SSL_export_keying_material(ssl, out, out_len, label,
- os_strlen(label), NULL, 0, 0) == 1) {
- wpa_printf(MSG_DEBUG, "OpenSSL: Using internal PRF");
- return 0;
- }
-#endif
- return openssl_tls_prf(conn, label, server_random_first,
- skip_keyblock, out, out_len);
+#else /* OPENSSL_NEED_EAP_FAST_PRF */
+ wpa_printf(MSG_ERROR,
+ "OpenSSL: EAP-FAST keys cannot be exported in FIPS mode");
+ return -1;
+#endif /* OPENSSL_NEED_EAP_FAST_PRF */
}
int tls_connection_resumed(void *ssl_ctx, struct tls_connection *conn)
{
-#if OPENSSL_VERSION_NUMBER >= 0x10001000L
return conn ? SSL_cache_hit(conn->ssl) : 0;
-#else
- return conn ? conn->ssl->hit : 0;
-#endif
}
int tls_connection_set_cipher_list(void *tls_ctx, struct tls_connection *conn,
u8 *ciphers)
{
- char buf[100], *pos, *end;
+ char buf[500], *pos, *end;
u8 *c;
int ret;
case TLS_CIPHER_ANON_DH_AES128_SHA:
suite = "ADH-AES128-SHA";
break;
+ case TLS_CIPHER_RSA_DHE_AES256_SHA:
+ suite = "DHE-RSA-AES256-SHA";
+ break;
+ case TLS_CIPHER_AES256_SHA:
+ suite = "AES256-SHA";
+ break;
default:
wpa_printf(MSG_DEBUG, "TLS: Unsupported "
"cipher selection: %d", *c);
wpa_printf(MSG_DEBUG, "OpenSSL: cipher suites: %s", buf + 1);
-#if OPENSSL_VERSION_NUMBER >= 0x10100000L
+#if OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(LIBRESSL_VERSION_NUMBER)
#if defined(EAP_FAST) || defined(EAP_FAST_DYNAMIC) || defined(EAP_SERVER_FAST)
if (os_strstr(buf, ":ADH-")) {
/*
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_CERTID_free(id);
OCSP_BASICRESP_free(basic);
OCSP_RESPONSE_free(rsp);
return (conn->flags & TLS_CONN_REQUIRE_OCSP) ? 0 : 1;
}
+ OCSP_CERTID_free(id);
if (!OCSP_check_validity(this_update, next_update, 5 * 60, -1)) {
tls_show_errors(MSG_INFO, __func__,
if (conn == NULL)
return -1;
+ if (params->flags & TLS_CONN_REQUIRE_OCSP_ALL) {
+ wpa_printf(MSG_INFO,
+ "OpenSSL: ocsp=3 not supported");
+ return -1;
+ }
+
/*
* If the engine isn't explicitly configured, and any of the
* cert/key fields are actually PKCS#11 URIs, then automatically
engine_id = "pkcs11";
#if defined(EAP_FAST) || defined(EAP_FAST_DYNAMIC) || defined(EAP_SERVER_FAST)
-#if OPENSSL_VERSION_NUMBER < 0x10100000L
+#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER)
if (params->flags & TLS_CONN_EAP_FAST) {
wpa_printf(MSG_DEBUG,
"OpenSSL: Use TLSv1_method() for EAP-FAST");
#ifdef SSL_OP_NO_TICKET
if (params->flags & TLS_CONN_DISABLE_SESSION_TICKET)
SSL_CTX_set_options(ssl_ctx, SSL_OP_NO_TICKET);
-#ifdef SSL_CTX_clear_options
else
SSL_CTX_clear_options(ssl_ctx, SSL_OP_NO_TICKET);
-#endif /* SSL_clear_options */
#endif /* SSL_OP_NO_TICKET */
#ifdef HAVE_OCSP
* commented out unless explicitly needed for EAP-FAST in order to be able to
* build this file with unmodified openssl. */
-#ifdef OPENSSL_IS_BORINGSSL
+#if (defined(OPENSSL_IS_BORINGSSL) || OPENSSL_VERSION_NUMBER >= 0x10100000L) && !defined(LIBRESSL_VERSION_NUMBER)
static int tls_sess_sec_cb(SSL *s, void *secret, int *secret_len,
STACK_OF(SSL_CIPHER) *peer_ciphers,
const SSL_CIPHER **cipher, void *arg)
struct tls_connection *conn = arg;
int ret;
-#if OPENSSL_VERSION_NUMBER < 0x10100000L
+#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER)
if (conn == NULL || conn->session_ticket_cb == NULL)
return 0;
wpa_hexdump(MSG_DEBUG, "OpenSSL: ClientHello SessionTicket "
"extension", data, len);
- conn->session_ticket = os_malloc(len);
+ conn->session_ticket = os_memdup(data, len);
if (conn->session_ticket == NULL)
return 0;
- os_memcpy(conn->session_ticket, data, len);
conn->session_ticket_len = len;
return 1;
int tls_get_library_version(char *buf, size_t buf_len)
{
+#if OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(LIBRESSL_VERSION_NUMBER)
+ return os_snprintf(buf, buf_len, "OpenSSL build=%s run=%s",
+ OPENSSL_VERSION_TEXT,
+ OpenSSL_version(OPENSSL_VERSION));
+#else
return os_snprintf(buf, buf_len, "OpenSSL build=%s run=%s",
OPENSSL_VERSION_TEXT,
SSLeay_version(SSLEAY_VERSION));
+#endif
}