[PATCH 18/24] wolfssl: implement check_cert_subject
Juliusz Sosinowicz
juliusz at wolfssl.com
Thu Apr 4 11:16:24 PDT 2024
Overall design was copied from tls_openssl.c. Multiple same distinguished names in one subject name are not supported.
Signed-off-by: Juliusz Sosinowicz <juliusz at wolfssl.com>
---
src/crypto/tls_wolfssl.c | 253 ++++++++++++++++++++++++++++++++++-----
1 file changed, 220 insertions(+), 33 deletions(-)
diff --git a/src/crypto/tls_wolfssl.c b/src/crypto/tls_wolfssl.c
index 0cdc4c809a..b88e259e40 100644
--- a/src/crypto/tls_wolfssl.c
+++ b/src/crypto/tls_wolfssl.c
@@ -65,13 +65,15 @@ struct tls_context {
int cert_in_cb;
char *ocsp_stapling_response;
unsigned int tls_session_lifetime;
+ /* This is alloc'ed and needs to be free'd */
+ char *check_cert_subject;
};
static struct tls_context *tls_global = NULL;
/* wolfssl tls_connection */
struct tls_connection {
- struct tls_context *context;
+ const struct tls_context *context;
WOLFSSL *ssl;
int read_alerts;
int write_alerts;
@@ -82,6 +84,7 @@ struct tls_connection {
char *alt_subject_match;
char *suffix_match;
char *domain_match;
+ char *check_cert_subject;
u8 srv_cert_hash[32];
@@ -121,6 +124,21 @@ static struct tls_context * tls_context_new(const struct tls_config *conf)
return context;
}
+static void tls_context_free(struct tls_context* context)
+{
+ if (context) {
+ if (context->check_cert_subject)
+ os_free(context->check_cert_subject);
+ }
+ os_free(context);
+}
+
+/* Helper to make sure the context stays const */
+static const struct tls_context* ssl_ctx_get_tls_context(void *ssl_ctx)
+{
+ return wolfSSL_CTX_get_ex_data(ssl_ctx, TLS_SSL_CTX_CTX_EX_IDX);
+}
+
static void wolfssl_reset_in_data(struct tls_in_data *in,
const struct wpabuf *buf)
@@ -373,9 +391,9 @@ void * tls_init(const struct tls_config *conf)
if (!ssl_ctx) {
tls_ref_count--;
if (context != tls_global)
- os_free(context);
+ tls_context_free(context);
if (tls_ref_count == 0) {
- os_free(tls_global);
+ tls_context_free(tls_global);
tls_global = NULL;
}
return NULL;
@@ -413,18 +431,19 @@ void * tls_init(const struct tls_config *conf)
void tls_deinit(void *ssl_ctx)
{
- struct tls_context *context = wolfSSL_CTX_get_ex_data(ssl_ctx,
- TLS_SSL_CTX_CTX_EX_IDX);
+ struct tls_context *context =
+ /* Need to cast the const away */
+ (struct tls_context *)ssl_ctx_get_tls_context(ssl_ctx);
if (context != tls_global)
- os_free(context);
+ tls_context_free(context);
wolfSSL_CTX_free((WOLFSSL_CTX *) ssl_ctx);
tls_ref_count--;
if (tls_ref_count == 0) {
wolfSSL_Cleanup();
- os_free(tls_global);
+ tls_context_free(tls_global);
tls_global = NULL;
}
}
@@ -467,7 +486,7 @@ struct tls_connection * tls_connection_init(void *tls_ctx)
wolfSSL_SetIOReadCtx(conn->ssl, &conn->input);
wolfSSL_SetIOWriteCtx(conn->ssl, &conn->output);
wolfSSL_set_ex_data(conn->ssl, TLS_SSL_CON_EX_IDX, conn);
- conn->context = wolfSSL_CTX_get_ex_data(ssl_ctx, TLS_SSL_CTX_CTX_EX_IDX);
+ conn->context = ssl_ctx_get_tls_context(ssl_ctx);
/* Need randoms post-hanshake for EAP-FAST, export key and deriving
* session ID in EAP methods. */
@@ -493,6 +512,7 @@ void tls_connection_deinit(void *tls_ctx, struct tls_connection *conn)
os_free(conn->suffix_match);
os_free(conn->domain_match);
os_free(conn->peer_subject);
+ os_free(conn->check_cert_subject);
/* self */
os_free(conn);
@@ -542,7 +562,8 @@ static int tls_connection_set_subject_match(struct tls_connection *conn,
const char *subject_match,
const char *alt_subject_match,
const char *suffix_match,
- const char *domain_match)
+ const char *domain_match,
+ const char *check_cert_subject)
{
os_free(conn->subject_match);
conn->subject_match = NULL;
@@ -576,6 +597,14 @@ static int tls_connection_set_subject_match(struct tls_connection *conn,
return -1;
}
+ os_free(conn->check_cert_subject);
+ conn->check_cert_subject = NULL;
+ if (check_cert_subject) {
+ conn->check_cert_subject = os_strdup(check_cert_subject);
+ if (!conn->check_cert_subject)
+ return -1;
+ }
+
return 0;
}
@@ -954,6 +983,138 @@ static const char * wolfssl_tls_err_string(int err, const char *err_str)
}
}
+/**
+ * match_dn_field - Match configuration DN field against Certificate DN field
+ * @cert: Certificate
+ * @nid: NID of DN field
+ * @field: Field name
+ * @value DN field value which is passed from configuration
+ * e.g., if configuration have C=US and this argument will point to US.
+ * Returns: 1 on success and 0 on failure
+ */
+static int match_dn_field(WOLFSSL_X509 *cert, int nid, const char *field,
+ const char *value)
+{
+ int ret = 0;
+ int len = os_strlen(value);
+ char buf[256];
+ /* Fetch value based on NID */
+ int buf_len = wolfSSL_X509_NAME_get_text_by_NID(
+ wolfSSL_X509_get_subject_name((WOLFSSL_X509*)cert), nid,
+ buf, sizeof(buf));
+
+ if (buf_len >= 0) {
+ wpa_printf(MSG_DEBUG, "wolfSSL: Matching fields: '%s' '%s' '%s'", field,
+ value, buf);
+
+ /* Check wildcard at the right end side */
+ /* E.g., if OU=develop* mentioned in configuration, allow 'OU'
+ * of the subject in the client certificate to start with
+ * 'develop' */
+ if (len > 0 && value[len - 1] == '*') {
+ ret = buf_len >= len && os_memcmp(buf, value, len - 1) == 0;
+ } else {
+ ret = os_strcmp(buf, value) == 0;
+ }
+ } else {
+ wpa_printf(MSG_ERROR, "wolfSSL: cert does not contain entry for '%s'",
+ field);
+ }
+
+ return ret;
+}
+
+#define DN_FIELD_LEN 20
+
+/**
+ * get_value_from_field - Get value from DN field
+ * @cert: Certificate
+ * @field_str: DN field string which is passed from configuration file (e.g.,
+ * C=US)
+ * @processedNIDs: List of NIDs already processed
+ * Returns: 1 on success and 0 on failure
+ */
+static int get_value_from_field(WOLFSSL_X509 *cert, char *field_str,
+ int* processedNIDs)
+{
+ int nid, i;
+ char *context = NULL, *name, *value;
+
+ if (os_strcmp(field_str, "*") == 0)
+ return 1; /* wildcard matches everything */
+
+ name = str_token(field_str, "=", &context);
+ if (!name)
+ return 0;
+
+ nid = wolfSSL_OBJ_txt2nid(name);
+ if (nid == NID_undef) {
+ wpa_printf(MSG_ERROR,
+ "wolfSSL: Unknown field '%s' in check_cert_subject", name);
+ return 0;
+ }
+
+ /* Check for duplicates */
+ for (i = 0; processedNIDs[i] != NID_undef && i < DN_FIELD_LEN; i++) {
+ if (processedNIDs[i] == nid) {
+ wpa_printf(MSG_ERROR, "wolfSSL: no support for multiple DN's in "
+ "check_cert_subject");
+ return 0;
+ }
+ }
+ if (i == DN_FIELD_LEN) {
+ wpa_printf(MSG_ERROR, "wolfSSL: only %d DN's are supported in check_cert_subject",
+ DN_FIELD_LEN);
+ return 0;
+ }
+ processedNIDs[i] = nid;
+
+ value = str_token(field_str, "=", &context);
+ if (!value) {
+ wpa_printf(MSG_ERROR, "wolfSSL: Distinguished Name field '%s' value is "
+ "not defined in check_cert_subject", name);
+ return 0;
+ }
+
+ return match_dn_field(cert, nid, name, value);
+}
+
+/**
+ * tls_match_dn_field - Match subject DN field with check_cert_subject
+ * @cert: Certificate
+ * @match: check_cert_subject string
+ * Returns: Return 1 on success and 0 on failure
+*/
+static int tls_match_dn_field(WOLFSSL_X509 *cert, const char *match)
+{
+ const char *token, *last = NULL;
+ /* Maximum length of each DN field is 255 characters */
+ char field[256];
+ int processedNIDs[DN_FIELD_LEN], i;
+
+ for (i = 0; i < DN_FIELD_LEN; i++)
+ processedNIDs[i] = NID_undef;
+
+ /* Process each '/' delimited field */
+ while ((token = cstr_token(match, "/", &last))) {
+ if (last - token >= (int) sizeof(field)) {
+ wpa_printf(MSG_ERROR,
+ "wolfSSL: Too long DN matching field value in '%s'",
+ match);
+ return 0;
+ }
+ os_memcpy(field, token, last - token);
+ field[last - token] = '\0';
+
+ if (!get_value_from_field(cert, field, processedNIDs)) {
+ wpa_printf(MSG_INFO, "wolfSSL: No match for DN '%s'",
+ field);
+ return 0;
+ }
+ }
+
+ return 1;
+}
static struct wpabuf * get_x509_cert(WOLFSSL_X509 *cert)
{
@@ -976,7 +1137,7 @@ static void wolfssl_tls_fail_event(struct tls_connection *conn,
{
union tls_event_data ev;
struct wpabuf *cert = NULL;
- struct tls_context *context = conn->context;
+ const struct tls_context *context = conn->context;
if (!context->event_cb)
return;
@@ -1029,7 +1190,7 @@ static void wolfssl_tls_cert_event(struct tls_connection *conn,
{
struct wpabuf *cert = NULL;
union tls_event_data ev;
- struct tls_context *context = conn->context;
+ const struct tls_context *context = conn->context;
char *alt_subject[TLS_MAX_ALT_SUBJECT];
int alt, num_alt_subject = 0;
WOLFSSL_GENERAL_NAME *gen;
@@ -1126,8 +1287,9 @@ static int tls_verify_cb(int preverify_ok, WOLFSSL_X509_STORE_CTX *x509_ctx)
int err, depth;
WOLFSSL *ssl;
struct tls_connection *conn;
- struct tls_context *context;
+ const struct tls_context *context;
char *match, *altmatch, *suffix_match, *domain_match;
+ const char *check_cert_subject;
const char *err_str;
err_cert = wolfSSL_X509_STORE_CTX_get_current_cert(x509_ctx);
@@ -1231,7 +1393,20 @@ static int tls_verify_cb(int preverify_ok, WOLFSSL_X509_STORE_CTX *x509_ctx)
"TLS: %s - preverify_ok=%d err=%d (%s) ca_cert_verify=%d depth=%d buf='%s'",
__func__, preverify_ok, err, err_str,
conn->ca_cert_verify, depth, buf);
- if (depth == 0 && match && os_strstr(buf, match) == NULL) {
+ check_cert_subject = conn->check_cert_subject;
+ if (!check_cert_subject)
+ check_cert_subject = conn->context->check_cert_subject;
+ if (check_cert_subject && depth == 0 &&
+ !tls_match_dn_field(err_cert, check_cert_subject)) {
+ wpa_printf(MSG_WARNING,
+ "TLS: Subject '%s' did not match with '%s'",
+ buf, check_cert_subject);
+ preverify_ok = 0;
+ wolfssl_tls_fail_event(conn, err_cert, err, depth, buf,
+ "Distinguished Name",
+ TLS_FAIL_DN_MISMATCH);
+ }
+ else if (depth == 0 && match && os_strstr(buf, match) == NULL) {
wpa_printf(MSG_WARNING,
"TLS: Subject '%s' did not match with '%s'",
buf, match);
@@ -1412,8 +1587,9 @@ int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn,
if (tls_connection_set_subject_match(conn, params->subject_match,
params->altsubject_match,
params->suffix_match,
- params->domain_match) < 0) {
- wpa_printf(MSG_INFO, "Error setting subject match");
+ params->domain_match,
+ params->check_cert_subject) < 0) {
+ wpa_printf(MSG_ERROR, "Error setting subject match");
return -1;
}
@@ -1421,14 +1597,14 @@ int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn,
params->ca_cert_blob,
params->ca_cert_blob_len,
params->ca_path) < 0) {
- wpa_printf(MSG_INFO, "Error setting CA cert");
+ wpa_printf(MSG_ERROR, "Error setting CA cert");
return -1;
}
if (tls_connection_client_cert(conn, params->client_cert,
params->client_cert_blob,
params->client_cert_blob_len) < 0) {
- wpa_printf(MSG_INFO, "Error setting client cert");
+ wpa_printf(MSG_ERROR, "Error setting client cert");
return -1;
}
@@ -1436,13 +1612,13 @@ int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn,
params->private_key_passwd,
params->private_key_blob,
params->private_key_blob_len) < 0) {
- wpa_printf(MSG_INFO, "Error setting private key");
+ wpa_printf(MSG_ERROR, "Error setting private key");
return -1;
}
if (handle_ciphersuites(NULL, conn->ssl, params->openssl_ciphers,
params->flags) != 0) {
- wpa_printf(MSG_INFO, "Error setting ciphersuites");
+ wpa_printf(MSG_ERROR, "Error setting ciphersuites");
return -1;
}
@@ -1475,7 +1651,7 @@ int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn,
wolfSSL_CTX_EnableOCSP(ctx, 0);
#else /* HAVE_OCSP */
if (params->flags & TLS_CONN_REQUIRE_OCSP) {
- wpa_printf(MSG_INFO,
+ wpa_printf(MSG_ERROR,
"wolfSSL: No OCSP support included - reject configuration");
return -1;
}
@@ -1636,19 +1812,33 @@ void ocsp_resp_free_cb(void *ocsp_stapling_response, unsigned char *response)
int tls_global_set_params(void *tls_ctx,
const struct tls_connection_params *params)
{
+ struct tls_context *context =
+ /* Need to cast away const as this is one of the only places
+ * where we should modify it */
+ (struct tls_context*)ssl_ctx_get_tls_context(tls_ctx);
+
wpa_printf(MSG_DEBUG, "SSL: global set params");
- if (params->check_cert_subject)
- return -1; /* not yet supported */
+ os_free(context->check_cert_subject);
+ context->check_cert_subject = NULL;
+ if (params->check_cert_subject) {
+ context->check_cert_subject =
+ os_strdup(params->check_cert_subject);
+ if (!context->check_cert_subject) {
+ wpa_printf(MSG_ERROR, "SSL: Failed to copy check_cert_subject '%s'",
+ params->check_cert_subject);
+ return -1;
+ }
+ }
if (tls_global_ca_cert(tls_ctx, params->ca_cert) < 0) {
- wpa_printf(MSG_INFO, "SSL: Failed to load ca cert file '%s'",
+ wpa_printf(MSG_ERROR, "SSL: Failed to load ca cert file '%s'",
params->ca_cert);
return -1;
}
if (tls_global_client_cert(tls_ctx, params->client_cert) < 0) {
- wpa_printf(MSG_INFO,
+ wpa_printf(MSG_ERROR,
"SSL: Failed to load client cert file '%s'",
params->client_cert);
return -1;
@@ -1656,26 +1846,26 @@ int tls_global_set_params(void *tls_ctx,
if (tls_global_private_key(tls_ctx, params->private_key,
params->private_key_passwd) < 0) {
- wpa_printf(MSG_INFO,
+ wpa_printf(MSG_ERROR,
"SSL: Failed to load private key file '%s'",
params->private_key);
return -1;
}
if (tls_global_dh(tls_ctx, params->dh_file) < 0) {
- wpa_printf(MSG_INFO, "SSL: Failed to load DH file '%s'",
+ wpa_printf(MSG_ERROR, "SSL: Failed to load DH file '%s'",
params->dh_file);
return -1;
}
if (handle_ciphersuites(tls_ctx, NULL, params->openssl_ciphers,
params->flags) != 0) {
- wpa_printf(MSG_INFO, "Error setting ciphersuites");
+ wpa_printf(MSG_ERROR, "Error setting ciphersuites");
return -1;
}
if (params->openssl_ecdh_curves) {
- wpa_printf(MSG_INFO,
+ wpa_printf(MSG_ERROR,
"wolfSSL: openssl_ecdh_curves not supported");
return -1;
}
@@ -1717,7 +1907,7 @@ int tls_connection_set_verify(void *ssl_ctx, struct tls_connection *conn,
const u8 *session_ctx, size_t session_ctx_len)
{
static int counter = 0;
- struct tls_context *context;
+ const struct tls_context *context;
if (!conn)
return -1;
@@ -1736,8 +1926,7 @@ int tls_connection_set_verify(void *ssl_ctx, struct tls_connection *conn,
wolfSSL_set_accept_state(conn->ssl);
- context = wolfSSL_CTX_get_ex_data((WOLFSSL_CTX *) ssl_ctx,
- TLS_SSL_CTX_CTX_EX_IDX);
+ context = ssl_ctx_get_tls_context(ssl_ctx);
if (context && context->tls_session_lifetime == 0) {
/*
* Set session id context to a unique value to make sure
@@ -1753,8 +1942,6 @@ int tls_connection_set_verify(void *ssl_ctx, struct tls_connection *conn,
session_ctx_len);
}
- /* TODO: do we need to fake a session like OpenSSL does here? */
-
return 0;
}
--
2.34.1
More information about the Hostap
mailing list