diff --git a/src/common/dpp.c b/src/common/dpp.c index 9a57a7095..dc66ce913 100644 --- a/src/common/dpp.c +++ b/src/common/dpp.c @@ -1694,6 +1694,11 @@ dpp_build_conf_resp(struct dpp_authentication *auth, const u8 *e_nonce, size_t len[1]; enum dpp_status_error status; + if (auth->force_conf_resp_status != DPP_STATUS_OK) { + status = auth->force_conf_resp_status; + goto forced_status; + } + if (netrole == DPP_NETROLE_CONFIGURATOR) { #ifdef CONFIG_DPP2 env_data = dpp_build_enveloped_data(auth); @@ -1715,6 +1720,7 @@ dpp_build_conf_resp(struct dpp_authentication *auth, const u8 *e_nonce, status = DPP_STATUS_CSR_NEEDED; else status = DPP_STATUS_CONFIGURE_FAILURE; +forced_status: auth->conf_resp_status = status; /* { E-nonce, configurationObject[, sendConnStatus]}ke */ @@ -2040,6 +2046,12 @@ dpp_conf_req_rx(struct dpp_authentication *auth, const u8 *attr_start, char *txt; wpa_hexdump_buf(MSG_DEBUG, "DPP: CertificateRequest", cert_req); + if (dpp_validate_csr(auth, cert_req) < 0) { + wpa_printf(MSG_DEBUG, "DPP: CSR is not valid"); + auth->force_conf_resp_status = DPP_STATUS_CSR_BAD; + goto cont; + } + wpa_printf(MSG_DEBUG, "DPP: CSR is valid - forward to CA/RA"); txt = base64_encode_no_lf(wpabuf_head(cert_req), wpabuf_len(cert_req), NULL); if (!txt) @@ -2051,6 +2063,7 @@ dpp_conf_req_rx(struct dpp_authentication *auth, const u8 *attr_start, auth->waiting_cert = true; goto fail; } +cont: #endif /* CONFIG_DPP2 */ resp = dpp_build_conf_resp(auth, e_nonce, e_nonce_len, netrole, diff --git a/src/common/dpp.h b/src/common/dpp.h index a43b85fc2..f4398aa0d 100644 --- a/src/common/dpp.h +++ b/src/common/dpp.h @@ -250,6 +250,7 @@ struct dpp_authentication { enum dpp_connector_key reconfig_connector_key; enum dpp_status_error auth_resp_status; enum dpp_status_error conf_resp_status; + enum dpp_status_error force_conf_resp_status; u8 peer_mac_addr[ETH_ALEN]; u8 i_nonce[DPP_MAX_NONCE_LEN]; u8 r_nonce[DPP_MAX_NONCE_LEN]; @@ -617,6 +618,7 @@ void dpp_pfs_free(struct dpp_pfs *pfs); struct wpabuf * dpp_build_csr(struct dpp_authentication *auth); struct wpabuf * dpp_pkcs7_certs(const struct wpabuf *pkcs7); +int dpp_validate_csr(struct dpp_authentication *auth, const struct wpabuf *csr); struct dpp_bootstrap_info * dpp_add_qr_code(struct dpp_global *dpp, const char *uri); diff --git a/src/common/dpp_crypto.c b/src/common/dpp_crypto.c index 744f099ce..916aeeff8 100644 --- a/src/common/dpp_crypto.c +++ b/src/common/dpp_crypto.c @@ -2844,6 +2844,125 @@ fail: return pem; } + +int dpp_validate_csr(struct dpp_authentication *auth, const struct wpabuf *csr) +{ + X509_REQ *req; + const unsigned char *pos; + EVP_PKEY *pkey; + int res, loc, ret = -1; + X509_ATTRIBUTE *attr; + ASN1_TYPE *type; + ASN1_STRING *str; + unsigned char *utf8 = NULL; + unsigned char *cp = NULL; + size_t cp_len; + u8 exp_cp[DPP_CP_LEN]; + unsigned int hash_len = auth->curve->hash_len; + + pos = wpabuf_head(csr); + req = d2i_X509_REQ(NULL, &pos, wpabuf_len(csr)); + if (!req) { + wpa_printf(MSG_DEBUG, "DPP: Failed to parse CSR"); + return -1; + } + + pkey = X509_REQ_get_pubkey(req); + if (!pkey) { + wpa_printf(MSG_DEBUG, "DPP: Failed to get public key from CSR"); + goto fail; + } + + res = X509_REQ_verify(req, pkey); + EVP_PKEY_free(pkey); + if (res != 1) { + wpa_printf(MSG_DEBUG, + "DPP: CSR does not have a valid signature"); + goto fail; + } + + loc = X509_REQ_get_attr_by_NID(req, NID_pkcs9_challengePassword, -1); + if (loc < 0) { + wpa_printf(MSG_DEBUG, + "DPP: CSR does not include challengePassword"); + goto fail; + } + + attr = X509_REQ_get_attr(req, loc); + if (!attr) { + wpa_printf(MSG_DEBUG, + "DPP: Could not get challengePassword attribute"); + goto fail; + } + + type = X509_ATTRIBUTE_get0_type(attr, 0); + if (!type) { + wpa_printf(MSG_DEBUG, + "DPP: Could not get challengePassword attribute type"); + goto fail; + } + + res = ASN1_TYPE_get(type); + /* This is supposed to be UTF8String, but allow other strings as well + * since challengePassword is using ASCII (base64 encoded). */ + if (res != V_ASN1_UTF8STRING && res != V_ASN1_PRINTABLESTRING && + res != V_ASN1_IA5STRING) { + wpa_printf(MSG_DEBUG, + "DPP: Unexpected challengePassword attribute type %d", + res); + goto fail; + } + + str = X509_ATTRIBUTE_get0_data(attr, 0, res, NULL); + if (!str) { + wpa_printf(MSG_DEBUG, + "DPP: Could not get ASN.1 string for challengePassword"); + goto fail; + } + + res = ASN1_STRING_to_UTF8(&utf8, str); + if (res < 0) { + wpa_printf(MSG_DEBUG, + "DPP: Could not get UTF8 version of challengePassword"); + goto fail; + } + + cp = base64_decode((const char *) utf8, res, &cp_len); + OPENSSL_free(utf8); + if (!cp) { + wpa_printf(MSG_DEBUG, + "DPP: Could not base64 decode challengePassword"); + goto fail; + } + if (cp_len != DPP_CP_LEN) { + wpa_printf(MSG_DEBUG, + "DPP: Unexpected cp length (%zu) in CSR challengePassword", + cp_len); + goto fail; + } + wpa_hexdump_key(MSG_DEBUG, "DPP: cp from CSR challengePassword", + cp, cp_len); + + /* cp = HKDF-Expand(bk, "CSR challengePassword", 64) */ + if (dpp_hkdf_expand(hash_len, auth->bk, hash_len, + "CSR challengePassword", exp_cp, DPP_CP_LEN) < 0) + goto fail; + wpa_hexdump_key(MSG_DEBUG, + "DPP: cp = HKDF-Expand(bk, \"CSR challengePassword\", 64)", + exp_cp, DPP_CP_LEN); + if (os_memcmp_const(cp, exp_cp, DPP_CP_LEN) != 0) { + wpa_printf(MSG_DEBUG, + "DPP: CSR challengePassword does not match calculated cp"); + goto fail; + } + + ret = 0; +fail: + os_free(cp); + X509_REQ_free(req); + return ret; +} + #endif /* CONFIG_DPP2 */