]> git.ipfire.org Git - thirdparty/hostap.git/commitdiff
DPP2: Validate CSR on Configurator before forwarding to CA/RA
authorJouni Malinen <jouni@codeaurora.org>
Wed, 17 Jun 2020 17:33:07 +0000 (20:33 +0300)
committerJouni Malinen <j@w1.fi>
Wed, 17 Jun 2020 17:33:07 +0000 (20:33 +0300)
Parse the received CSR, verify that it has been signed correctly, and
verify that the challengePassword is present and matches the derived cp.

Signed-off-by: Jouni Malinen <jouni@codeaurora.org>
src/common/dpp.c
src/common/dpp.h
src/common/dpp_crypto.c

index 9a57a7095cc1cf3ecf3857bd333600e91a14919f..dc66ce913c9da36ccd9b7e0382a2962edaf1c7ab 100644 (file)
@@ -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,
index a43b85fc2c054afaa468fe6a71c3c92966a71160..f4398aa0ddcdfd41e2812cd21ae11fbbdb32c592 100644 (file)
@@ -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);
index 744f099ce8528676fcb0aa2f2487f26f039c082c..916aeeff8aa3aeff94705e3b0f3c75504e1d31bf 100644 (file)
@@ -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 */