Added parameters to add callback function to handle unsupported extensions. Similar to how the callback functions work when parsing certificates. Also added new test cases.
Signed-off-by: Matthias Schulz <mschulz@hilscher.com>
diff --git a/include/mbedtls/x509_csr.h b/include/mbedtls/x509_csr.h
index 513a83e..a4bdc94 100644
--- a/include/mbedtls/x509_csr.h
+++ b/include/mbedtls/x509_csr.h
@@ -103,6 +103,64 @@
const unsigned char *buf, size_t buflen);
/**
+ * \brief The type of certificate extension callbacks.
+ *
+ * Callbacks of this type are passed to and used by the
+ * mbedtls_x509_csr_parse_der_with_ext_cb() routine when
+ * it encounters either an unsupported extension.
+ * Future versions of the library may invoke the callback
+ * in other cases, if and when the need arises.
+ *
+ * \param p_ctx An opaque context passed to the callback.
+ * \param csr The CSR being parsed.
+ * \param oid The OID of the extension.
+ * \param critical Whether the extension is critical.
+ * \param p Pointer to the start of the extension value
+ * (the content of the OCTET STRING).
+ * \param end End of extension value.
+ *
+ * \note The callback must fail and return a negative error code
+ * if it can not parse or does not support the extension.
+ * When the callback fails to parse a critical extension
+ * mbedtls_x509_csr_parse_der_with_ext_cb() also fails.
+ * When the callback fails to parse a non critical extension
+ * mbedtls_x509_csr_parse_der_with_ext_cb() simply skips
+ * the extension and continues parsing.
+ *
+ * \return \c 0 on success.
+ * \return A negative error code on failure.
+ */
+typedef int (*mbedtls_x509_csr_ext_cb_t)(void *p_ctx,
+ mbedtls_x509_csr const *csr,
+ mbedtls_x509_buf const *oid,
+ int critical,
+ const unsigned char *p,
+ const unsigned char *end);
+
+/**
+ * \brief Load a Certificate Signing Request (CSR) in DER format
+ *
+ * \note CSR attributes (if any) are currently silently ignored.
+ *
+ * \note If #MBEDTLS_USE_PSA_CRYPTO is enabled, the PSA crypto
+ * subsystem must have been initialized by calling
+ * psa_crypto_init() before calling this function.
+ *
+ * \param csr CSR context to fill
+ * \param buf buffer holding the CRL data
+ * \param buflen size of the buffer
+ * \param cb A callback invoked for every unsupported certificate
+ * extension.
+ * \param p_ctx An opaque context passed to the callback.
+ *
+ * \return 0 if successful, or a specific X509 error code
+ */
+int mbedtls_x509_csr_parse_der_with_ext_cb(mbedtls_x509_csr *csr,
+ const unsigned char *buf, size_t buflen,
+ mbedtls_x509_csr_ext_cb_t cb,
+ void *p_ctx);
+
+/**
* \brief Load a Certificate Signing Request (CSR), DER or PEM format
*
* \note See notes for \c mbedtls_x509_csr_parse_der()
diff --git a/library/x509_csr.c b/library/x509_csr.c
index baf2606..9e4a01a 100644
--- a/library/x509_csr.c
+++ b/library/x509_csr.c
@@ -73,11 +73,13 @@
* Parse CSR extension requests in DER format
*/
static int x509_csr_parse_extensions(mbedtls_x509_csr *csr,
- unsigned char **p, const unsigned char *end)
+ unsigned char **p, const unsigned char *end,
+ mbedtls_x509_csr_ext_cb_t cb,
+ void *p_ctx)
{
int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
size_t len;
- unsigned char *end_ext_data;
+ unsigned char *end_ext_data, *end_ext_octet;
while (*p < end) {
mbedtls_x509_buf extn_oid = { 0, 0, NULL };
@@ -114,7 +116,9 @@
return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_X509_INVALID_EXTENSIONS, ret);
}
- if (*p + len != end_ext_data) {
+ end_ext_octet = *p + len;
+
+ if (end_ext_octet != end_ext_data) {
return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_X509_INVALID_EXTENSIONS,
MBEDTLS_ERR_ASN1_LENGTH_MISMATCH);
}
@@ -124,50 +128,72 @@
*/
ret = mbedtls_oid_get_x509_ext_type(&extn_oid, &ext_type);
- if (ret == 0) {
- /* Forbid repeated extensions */
- if ((csr->ext_types & ext_type) != 0) {
- return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_X509_INVALID_EXTENSIONS,
- MBEDTLS_ERR_ASN1_INVALID_DATA);
+ if (ret != 0) {
+ /* Give the callback (if any) a chance to handle the extension */
+ if (cb != NULL) {
+ ret = cb(p_ctx, csr, &extn_oid, is_critical, *p, end_ext_octet);
+ if (ret != 0 && is_critical) {
+ return ret;
+ }
+ *p = end_ext_octet;
+ continue;
}
- csr->ext_types |= ext_type;
+ /* No parser found, skip extension */
+ *p = end_ext_octet;
- switch (ext_type) {
- case MBEDTLS_X509_EXT_KEY_USAGE:
- /* Parse key usage */
- if ((ret = mbedtls_x509_get_key_usage(p, end_ext_data,
- &csr->key_usage)) != 0) {
- return ret;
- }
- break;
-
- case MBEDTLS_X509_EXT_SUBJECT_ALT_NAME:
- /* Parse subject alt name */
- if ((ret = mbedtls_x509_get_subject_alt_name(p, end_ext_data,
- &csr->subject_alt_names)) != 0) {
- return ret;
- }
- break;
-
- case MBEDTLS_X509_EXT_NS_CERT_TYPE:
- /* Parse netscape certificate type */
- if ((ret = mbedtls_x509_get_ns_cert_type(p, end_ext_data,
- &csr->ns_cert_type)) != 0) {
- return ret;
- }
- break;
- default:
- break;
- }
- } else {
if (is_critical) {
/* Data is marked as critical: fail */
return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_X509_INVALID_EXTENSIONS,
MBEDTLS_ERR_ASN1_UNEXPECTED_TAG);
}
+ continue;
}
- *p = end_ext_data;
+
+ /* Forbid repeated extensions */
+ if ((csr->ext_types & ext_type) != 0) {
+ return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_X509_INVALID_EXTENSIONS,
+ MBEDTLS_ERR_ASN1_INVALID_DATA);
+ }
+
+ csr->ext_types |= ext_type;
+
+ switch (ext_type) {
+ case MBEDTLS_X509_EXT_KEY_USAGE:
+ /* Parse key usage */
+ if ((ret = mbedtls_x509_get_key_usage(p, end_ext_data,
+ &csr->key_usage)) != 0) {
+ return ret;
+ }
+ break;
+
+ case MBEDTLS_X509_EXT_SUBJECT_ALT_NAME:
+ /* Parse subject alt name */
+ if ((ret = mbedtls_x509_get_subject_alt_name(p, end_ext_data,
+ &csr->subject_alt_names)) != 0) {
+ return ret;
+ }
+ break;
+
+ case MBEDTLS_X509_EXT_NS_CERT_TYPE:
+ /* Parse netscape certificate type */
+ if ((ret = mbedtls_x509_get_ns_cert_type(p, end_ext_data,
+ &csr->ns_cert_type)) != 0) {
+ return ret;
+ }
+ break;
+ default:
+ /*
+ * If this is a non-critical extension, which the oid layer
+ * supports, but there isn't an x509 parser for it,
+ * skip the extension.
+ */
+ if (is_critical) {
+ return MBEDTLS_ERR_X509_FEATURE_UNAVAILABLE;
+ } else {
+ *p = end_ext_octet;
+ }
+ }
}
if (*p != end) {
@@ -182,7 +208,9 @@
* Parse CSR attributes in DER format
*/
static int x509_csr_parse_attributes(mbedtls_x509_csr *csr,
- const unsigned char *start, const unsigned char *end)
+ const unsigned char *start, const unsigned char *end,
+ mbedtls_x509_csr_ext_cb_t cb,
+ void *p_ctx)
{
int ret;
size_t len;
@@ -221,7 +249,7 @@
return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_X509_INVALID_EXTENSIONS, ret);
}
- if ((ret = x509_csr_parse_extensions(csr, p, *p + len)) != 0) {
+ if ((ret = x509_csr_parse_extensions(csr, p, *p + len, cb, p_ctx)) != 0) {
return ret;
}
@@ -245,8 +273,10 @@
/*
* Parse a CSR in DER format
*/
-int mbedtls_x509_csr_parse_der(mbedtls_x509_csr *csr,
- const unsigned char *buf, size_t buflen)
+static int mbedtls_x509_csr_parse_der_internal(mbedtls_x509_csr *csr,
+ const unsigned char *buf, size_t buflen,
+ mbedtls_x509_csr_ext_cb_t cb,
+ void *p_ctx)
{
int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
size_t len;
@@ -370,7 +400,7 @@
return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_X509_INVALID_FORMAT, ret);
}
- if ((ret = x509_csr_parse_attributes(csr, p, p + len)) != 0) {
+ if ((ret = x509_csr_parse_attributes(csr, p, p + len, cb, p_ctx)) != 0) {
mbedtls_x509_csr_free(csr);
return ret;
}
@@ -410,6 +440,26 @@
}
/*
+ * Parse a CSR in DER format
+ */
+int mbedtls_x509_csr_parse_der(mbedtls_x509_csr *csr,
+ const unsigned char *buf, size_t buflen)
+{
+ return mbedtls_x509_csr_parse_der_internal(csr, buf, buflen, NULL, NULL);
+}
+
+/*
+ * Parse a CSR in DER format with callback for unknown extensions
+ */
+int mbedtls_x509_csr_parse_der_with_ext_cb(mbedtls_x509_csr *csr,
+ const unsigned char *buf, size_t buflen,
+ mbedtls_x509_csr_ext_cb_t cb,
+ void *p_ctx)
+{
+ return mbedtls_x509_csr_parse_der_internal(csr, buf, buflen, cb, p_ctx);
+}
+
+/*
* Parse a CSR, allowing for PEM or raw DER encoding
*/
int mbedtls_x509_csr_parse(mbedtls_x509_csr *csr, const unsigned char *buf, size_t buflen)
diff --git a/tests/suites/test_suite_x509parse.data b/tests/suites/test_suite_x509parse.data
index a38a3ff..9817536 100644
--- a/tests/suites/test_suite_x509parse.data
+++ b/tests/suites/test_suite_x509parse.data
@@ -2940,9 +2940,17 @@
depends_on:MBEDTLS_PK_CAN_ECDSA_SOME:MBEDTLS_ECP_HAVE_SECP256R1:MBEDTLS_MD_CAN_SHA1:!MBEDTLS_X509_REMOVE_INFO
mbedtls_x509_csr_parse:"308201183081bf0201003034310b3009060355040613024e4c3111300f060355040a1308506f6c617253534c31123010060355040313096c6f63616c686f73743059301306072a8648ce3d020106082a8648ce3d0301070342000437cc56d976091e5a723ec7592dff206eee7cf9069174d0ad14b5f768225962924ee500d82311ffea2fd2345d5d16bd8a88c26b770d55cd8a2a0efa01c8b4edffa029302706092a864886f70d01090e311a301830090603551d1304023000300b0603551d0f0404030205e0300906072a8648ce3d04010349003046022100b49fd8c8f77abfa871908dfbe684a08a793d0f490a43d86fcf2086e4f24bb0c2022100f829d5ccd3742369299e6294394717c4b723a0f68b44e831b6e6c3bcabf97243":"CSR version \: 1\nsubject name \: C=NL, O=PolarSSL, CN=localhost\nsigned using \: ECDSA with SHA1\nEC key size \: 256 bits\n\nkey usage \: Digital Signature, Non Repudiation, Key Encipherment\n":0
-X509 CSR ASN.1 (critical extensions)
+X509 CSR ASN.1 (Unsupported critical extension)
depends_on:MBEDTLS_PK_CAN_ECDSA_SOME:MBEDTLS_ECP_HAVE_SECP256R1:MBEDTLS_MD_CAN_SHA256:!MBEDTLS_X509_REMOVE_INFO
-mbedtls_x509_csr_parse:"308201233081cb02010030413119301706035504030c1053656c66207369676e65642074657374310b300906035504061302444531173015060355040a0c0e41757468437274444220546573743059301306072a8648ce3d020106082a8648ce3d03010703420004c11ebb9951848a436ca2c8a73382f24bbb6c28a92e401d4889b0c361f377b92a8b0497ff2f5a5f6057ae85f704ab1850bef075914f68ed3aeb15a1ff1ebc0dc6a028302606092a864886f70d01090e311930173015060b2b0601040183890c8622020101ff0403010101300a06082a8648ce3d040302034700304402200c4108fd098525993d3fd5b113f0a1ead8750852baf55a2f8e670a22cabc0ba1022034db93a0fcb993912adcf2ea8cb4b66389af30e264d43c0daea03255e45d2ccc":"CSR version \: 1\nsubject name \: CN=Self signed test, C=DE, O=AuthCrtDB Test\nsigned using \: ECDSA with SHA256\nEC key size \: 256 bits\n":0
+mbedtls_x509_csr_parse:"308201233081cb02010030413119301706035504030c1053656c66207369676e65642074657374310b300906035504061302444531173015060355040a0c0e41757468437274444220546573743059301306072a8648ce3d020106082a8648ce3d03010703420004c11ebb9951848a436ca2c8a73382f24bbb6c28a92e401d4889b0c361f377b92a8b0497ff2f5a5f6057ae85f704ab1850bef075914f68ed3aeb15a1ff1ebc0dc6a028302606092a864886f70d01090e311930173015060b2b0601040183890c8622020101ff0403010101300a06082a8648ce3d040302034700304402200c4108fd098525993d3fd5b113f0a1ead8750852baf55a2f8e670a22cabc0ba1022034db93a0fcb993912adcf2ea8cb4b66389af30e264d43c0daea03255e45d2ccc":"":MBEDTLS_ERR_X509_INVALID_EXTENSIONS+MBEDTLS_ERR_ASN1_UNEXPECTED_TAG
+
+X509 CSR ASN.1 (Unsupported critical extension accepted by callback)
+depends_on:MBEDTLS_PK_CAN_ECDSA_SOME:MBEDTLS_ECP_HAVE_SECP256R1:MBEDTLS_MD_CAN_SHA256:!MBEDTLS_X509_REMOVE_INFO
+mbedtls_x509_csr_parse_with_ext_cb:"308201233081cb02010030413119301706035504030c1053656c66207369676e65642074657374310b300906035504061302444531173015060355040a0c0e41757468437274444220546573743059301306072a8648ce3d020106082a8648ce3d03010703420004c11ebb9951848a436ca2c8a73382f24bbb6c28a92e401d4889b0c361f377b92a8b0497ff2f5a5f6057ae85f704ab1850bef075914f68ed3aeb15a1ff1ebc0dc6a028302606092a864886f70d01090e311930173015060b2b0601040183890c8622020101ff0403010101300a06082a8648ce3d040302034700304402200c4108fd098525993d3fd5b113f0a1ead8750852baf55a2f8e670a22cabc0ba1022034db93a0fcb993912adcf2ea8cb4b66389af30e264d43c0daea03255e45d2ccc":"CSR version \: 1\nsubject name \: CN=Self signed test, C=DE, O=AuthCrtDB Test\nsigned using \: ECDSA with SHA256\nEC key size \: 256 bits\n":0:1
+
+X509 CSR ASN.1 (Unsupported critical extension rejected by callback)
+depends_on:MBEDTLS_PK_CAN_ECDSA_SOME:MBEDTLS_ECP_HAVE_SECP256R1:MBEDTLS_MD_CAN_SHA256:!MBEDTLS_X509_REMOVE_INFO
+mbedtls_x509_csr_parse_with_ext_cb:"308201233081cb02010030413119301706035504030c1053656c66207369676e65642074657374310b300906035504061302444531173015060355040a0c0e41757468437274444220546573743059301306072a8648ce3d020106082a8648ce3d03010703420004c11ebb9951848a436ca2c8a73382f24bbb6c28a92e401d4889b0c361f377b92a8b0497ff2f5a5f6057ae85f704ab1850bef075914f68ed3aeb15a1ff1ebc0dc6a028302606092a864886f70d01090e311930173015060b2b0601040183890c8622020101ff0403010101300a06082a8648ce3d040302034700304402200c4108fd098525993d3fd5b113f0a1ead8750852baf55a2f8e670a22cabc0ba1022034db93a0fcb993912adcf2ea8cb4b66389af30e264d43c0daea03255e45d2ccc":"":MBEDTLS_ERR_X509_INVALID_EXTENSIONS+MBEDTLS_ERR_ASN1_UNEXPECTED_TAG:0
X509 CSR ASN.1 (bad first tag)
mbedtls_x509_csr_parse:"3100":"":MBEDTLS_ERR_X509_INVALID_FORMAT
diff --git a/tests/suites/test_suite_x509parse.function b/tests/suites/test_suite_x509parse.function
index 114bd52..8cdca82 100644
--- a/tests/suites/test_suite_x509parse.function
+++ b/tests/suites/test_suite_x509parse.function
@@ -412,6 +412,33 @@
MBEDTLS_ERR_ASN1_UNEXPECTED_TAG);
}
}
+
+int parse_csr_ext_accept_cb(void *p_ctx, mbedtls_x509_csr const *csr, mbedtls_x509_buf const *oid,
+ int critical, const unsigned char *cp, const unsigned char *end)
+{
+ (void) p_ctx;
+ (void) csr;
+ (void) oid;
+ (void) critical;
+ (void) cp;
+ (void) end;
+
+ return 0;
+}
+
+int parse_csr_ext_reject_cb(void *p_ctx, mbedtls_x509_csr const *csr, mbedtls_x509_buf const *oid,
+ int critical, const unsigned char *cp, const unsigned char *end)
+{
+ (void) p_ctx;
+ (void) csr;
+ (void) oid;
+ (void) critical;
+ (void) cp;
+ (void) end;
+
+ return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_X509_INVALID_EXTENSIONS,
+ MBEDTLS_ERR_ASN1_UNEXPECTED_TAG);
+}
#endif /* MBEDTLS_X509_CRT_PARSE_C */
/* END_HEADER */
@@ -1245,6 +1272,36 @@
}
/* END_CASE */
+/* BEGIN_CASE depends_on:MBEDTLS_X509_CSR_PARSE_C:!MBEDTLS_X509_REMOVE_INFO */
+void mbedtls_x509_csr_parse_with_ext_cb(data_t *csr_der, char *ref_out, int ref_ret, int accept)
+{
+ mbedtls_x509_csr csr;
+ char my_out[1000];
+ int my_ret;
+
+ mbedtls_x509_csr_init(&csr);
+ USE_PSA_INIT();
+
+ memset(my_out, 0, sizeof(my_out));
+
+ my_ret = mbedtls_x509_csr_parse_der_with_ext_cb(&csr, csr_der->x, csr_der->len,
+ accept ? parse_csr_ext_accept_cb :
+ parse_csr_ext_reject_cb,
+ NULL);
+ TEST_EQUAL(my_ret, ref_ret);
+
+ if (ref_ret == 0) {
+ size_t my_out_len = mbedtls_x509_csr_info(my_out, sizeof(my_out), "", &csr);
+ TEST_EQUAL(my_out_len, strlen(ref_out));
+ TEST_EQUAL(strcmp(my_out, ref_out), 0);
+ }
+
+exit:
+ mbedtls_x509_csr_free(&csr);
+ USE_PSA_DONE();
+}
+/* END_CASE */
+
/* BEGIN_CASE depends_on:MBEDTLS_FS_IO:MBEDTLS_X509_CSR_PARSE_C:!MBEDTLS_X509_REMOVE_INFO */
void mbedtls_x509_csr_parse_file(char *csr_file, char *ref_out, int ref_ret)
{