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@xxxxxxxxxxx> --- 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 _______________________________________________ Hostap mailing list Hostap@xxxxxxxxxxxxxxxxxxx http://lists.infradead.org/mailman/listinfo/hostap