From 1c8ecbef646d1b02c584f964e7fe2d8a69e34e00 Mon Sep 17 00:00:00 2001 From: Andrzej Kurek Date: Fri, 7 Jul 2023 05:12:52 -0400 Subject: [PATCH 1/7] Add support for x509 SAN RCF822 and DirectoryName for csr generation Unify the code with the x509 crt counterpart. Signed-off-by: Andrzej Kurek --- library/x509write_crt.c | 5 +++ library/x509write_csr.c | 71 ++++++++++++++++++++++++++++++++++------- 2 files changed, 64 insertions(+), 12 deletions(-) diff --git a/library/x509write_crt.c b/library/x509write_crt.c index 59fd58900..a6dc22c5f 100644 --- a/library/x509write_crt.c +++ b/library/x509write_crt.c @@ -288,6 +288,11 @@ int mbedtls_x509write_crt_set_subject_alternative_name(mbedtls_x509write_cert *c buf + buflen - len, len); + /* If we exceeded the allocated buffer it means that maximum size of the SubjectAltName list + * was incorrectly calculated and memory is corrupted. */ + if (p < buf) { + ret = MBEDTLS_ERR_ASN1_LENGTH_MISMATCH; + } cleanup: mbedtls_free(buf); return ret; diff --git a/library/x509write_csr.c b/library/x509write_csr.c index d792d3450..76473c486 100644 --- a/library/x509write_csr.c +++ b/library/x509write_csr.c @@ -48,6 +48,16 @@ #include "mbedtls/platform.h" +#define CHECK_OVERFLOW_ADD(a, b) \ + do \ + { \ + if (a > SIZE_MAX - (b)) \ + { \ + return MBEDTLS_ERR_X509_BAD_INPUT_DATA; \ + } \ + a += b; \ + } while (0) + void mbedtls_x509write_csr_init(mbedtls_x509write_csr *ctx) { memset(ctx, 0, sizeof(mbedtls_x509write_csr)); @@ -103,37 +113,53 @@ int mbedtls_x509write_csr_set_subject_alternative_name(mbedtls_x509write_csr *ct case MBEDTLS_X509_SAN_DNS_NAME: case MBEDTLS_X509_SAN_UNIFORM_RESOURCE_IDENTIFIER: case MBEDTLS_X509_SAN_IP_ADDRESS: + case MBEDTLS_X509_SAN_RFC822_NAME: /* length of value for each name entry, * maximum 4 bytes for the length field, * 1 byte for the tag/type. */ - buflen += cur->node.san.unstructured_name.len + 4 + 1; + CHECK_OVERFLOW_ADD(buflen, cur->node.san.unstructured_name.len); + CHECK_OVERFLOW_ADD(buflen, 4 + 1); break; - + case MBEDTLS_X509_SAN_DIRECTORY_NAME: + { + const mbedtls_asn1_named_data *chunk = &cur->node.san.directory_name; + while (chunk != NULL) { + // Max 4 bytes for length, +1 for tag, + // additional 4 max for length, +1 for tag. + // See x509_write_name for more information. + CHECK_OVERFLOW_ADD(buflen, 4 + 1 + 4 + 1); + CHECK_OVERFLOW_ADD(buflen, chunk->oid.len); + CHECK_OVERFLOW_ADD(buflen, chunk->val.len); + chunk = chunk->next; + } + CHECK_OVERFLOW_ADD(buflen, 4 + 1); + break; + } default: - /* Not supported - skip. */ - break; + /* Not supported - return. */ + return MBEDTLS_ERR_X509_FEATURE_UNAVAILABLE; } } /* Add the extra length field and tag */ - buflen += 4 + 1; + CHECK_OVERFLOW_ADD(buflen, 4 + 1); /* Allocate buffer */ buf = mbedtls_calloc(1, buflen); if (buf == NULL) { return MBEDTLS_ERR_ASN1_ALLOC_FAILED; } - - mbedtls_platform_zeroize(buf, buflen); p = buf + buflen; /* Write ASN.1-based structure */ cur = san_list; len = 0; while (cur != NULL) { + size_t single_san_len = 0; switch (cur->node.type) { case MBEDTLS_X509_SAN_DNS_NAME: + case MBEDTLS_X509_SAN_RFC822_NAME: case MBEDTLS_X509_SAN_UNIFORM_RESOURCE_IDENTIFIER: case MBEDTLS_X509_SAN_IP_ADDRESS: { @@ -141,23 +167,44 @@ int mbedtls_x509write_csr_set_subject_alternative_name(mbedtls_x509write_csr *ct (const unsigned char *) cur->node.san.unstructured_name.p; size_t unstructured_name_len = cur->node.san.unstructured_name.len; - MBEDTLS_ASN1_CHK_CLEANUP_ADD(len, + MBEDTLS_ASN1_CHK_CLEANUP_ADD(single_san_len, mbedtls_asn1_write_raw_buffer( &p, buf, unstructured_name, unstructured_name_len)); - MBEDTLS_ASN1_CHK_CLEANUP_ADD(len, mbedtls_asn1_write_len( + MBEDTLS_ASN1_CHK_CLEANUP_ADD(single_san_len, mbedtls_asn1_write_len( &p, buf, unstructured_name_len)); - MBEDTLS_ASN1_CHK_CLEANUP_ADD(len, + MBEDTLS_ASN1_CHK_CLEANUP_ADD(single_san_len, mbedtls_asn1_write_tag( &p, buf, MBEDTLS_ASN1_CONTEXT_SPECIFIC | cur->node.type)); } break; - default: - /* Skip unsupported names. */ + case MBEDTLS_X509_SAN_DIRECTORY_NAME: + MBEDTLS_ASN1_CHK_CLEANUP_ADD(single_san_len, + mbedtls_x509_write_names(&p, buf, + (mbedtls_asn1_named_data *) & + cur->node + .san.directory_name)); + MBEDTLS_ASN1_CHK_CLEANUP_ADD(single_san_len, + mbedtls_asn1_write_len(&p, buf, single_san_len)); + MBEDTLS_ASN1_CHK_CLEANUP_ADD(single_san_len, + mbedtls_asn1_write_tag(&p, buf, + MBEDTLS_ASN1_CONTEXT_SPECIFIC | + MBEDTLS_ASN1_CONSTRUCTED | + MBEDTLS_X509_SAN_DIRECTORY_NAME)); break; + default: + /* Error out on an unsupported SAN */ + ret = MBEDTLS_ERR_X509_FEATURE_UNAVAILABLE; + goto cleanup; } cur = cur->next; + /* check for overflow */ + if (len > SIZE_MAX - single_san_len) { + ret = MBEDTLS_ERR_X509_BAD_INPUT_DATA; + goto cleanup; + } + len += single_san_len; } MBEDTLS_ASN1_CHK_CLEANUP_ADD(len, mbedtls_asn1_write_len(&p, buf, len)); From 6bc7a386839cdbac631a2b924832009ae4c1bc8b Mon Sep 17 00:00:00 2001 From: Andrzej Kurek Date: Fri, 7 Jul 2023 05:13:13 -0400 Subject: [PATCH 2/7] Support more SAN subtypes in cert_req.c Signed-off-by: Andrzej Kurek --- programs/x509/cert_req.c | 73 +++++++++++++++++++++++++++++----------- 1 file changed, 53 insertions(+), 20 deletions(-) diff --git a/programs/x509/cert_req.c b/programs/x509/cert_req.c index 23e816b33..17de9def7 100644 --- a/programs/x509/cert_req.c +++ b/programs/x509/cert_req.c @@ -66,10 +66,12 @@ int main(void) " output_file=%%s default: cert.req\n" \ " subject_name=%%s default: CN=Cert,O=mbed TLS,C=UK\n" \ " san=%%s default: (none)\n" \ - " Semicolon-separated-list of values:\n" \ - " DNS:value\n" \ - " URI:value\n" \ - " IP:value (Only IPv4 is supported)\n" \ + " Semicolon-separated-list of values:\n" \ + " DNS:value\n" \ + " URI:value\n" \ + " RFC822:value\n" \ + " IP:value (Only IPv4 is supported)\n" \ + " DN:list of comma separated key=value pairs\n" \ " key_usage=%%s default: (empty)\n" \ " Comma-separated-list of values:\n" \ " digital_signature\n" \ @@ -153,12 +155,13 @@ int main(int argc, char *argv[]) mbedtls_pk_context key; char buf[1024]; int i; - char *p, *q, *r, *subtype_value; + char *p, *q, *r; mbedtls_x509write_csr req; mbedtls_entropy_context entropy; mbedtls_ctr_drbg_context ctr_drbg; const char *pers = "csr example app"; mbedtls_x509_san_list *cur, *prev; + mbedtls_asn1_named_data *ext_san_dirname = NULL; #if defined(MBEDTLS_X509_CRT_PARSE_C) uint8_t ip[4] = { 0 }; #endif @@ -218,11 +221,34 @@ usage: } else if (strcmp(p, "subject_name") == 0) { opt.subject_name = q; } else if (strcmp(p, "san") == 0) { + char *subtype_value; prev = NULL; while (q != NULL) { - if ((r = strchr(q, ';')) != NULL) { + char *semicolon; + r = q; + + /* Find the first non-escaped ; occurrence and remove escaped ones */ + do { + if ((semicolon = strchr(r, ';')) != NULL) { + if (*(semicolon-1) != '\\') { + r = semicolon; + break; + } + /* Remove the escape character */ + size_t size_left = strlen(semicolon); + memmove(semicolon-1, semicolon, size_left); + *(semicolon + size_left - 1) = '\0'; + /* r will now point at the character after the semicolon */ + r = semicolon; + } + + } while (semicolon != NULL); + + if (semicolon != NULL) { *r++ = '\0'; + } else { + r = NULL; } cur = mbedtls_calloc(1, sizeof(mbedtls_x509_san_list)); @@ -236,13 +262,13 @@ usage: if ((subtype_value = strchr(q, ':')) != NULL) { *subtype_value++ = '\0'; } - - if (strcmp(q, "URI") == 0) { + if (strcmp(q, "RFC822") == 0) { + cur->node.type = MBEDTLS_X509_SAN_RFC822_NAME; + } else if (strcmp(q, "URI") == 0) { cur->node.type = MBEDTLS_X509_SAN_UNIFORM_RESOURCE_IDENTIFIER; } else if (strcmp(q, "DNS") == 0) { cur->node.type = MBEDTLS_X509_SAN_DNS_NAME; } else if (strcmp(q, "IP") == 0) { -#if defined(MBEDTLS_X509_CRT_PARSE_C) size_t ip_len = 0; cur->node.type = MBEDTLS_X509_SAN_IP_ADDRESS; ip_len = mbedtls_x509_crt_parse_cn_inet_pton(subtype_value, ip); @@ -251,21 +277,28 @@ usage: subtype_value); goto exit; } -#else - mbedtls_printf("IP SAN parsing requires MBEDTLS_X509_CRT_PARSE_C to be defined"); - goto exit; -#endif + cur->node.san.unstructured_name.p = (unsigned char *) ip; + cur->node.san.unstructured_name.len = sizeof(ip); + } else if (strcmp(q, "DN") == 0) { + cur->node.type = MBEDTLS_X509_SAN_DIRECTORY_NAME; + if ((ret = mbedtls_x509_string_to_names(&ext_san_dirname, + subtype_value)) != 0) { + mbedtls_strerror(ret, buf, sizeof(buf)); + mbedtls_printf( + " failed\n ! mbedtls_x509_string_to_names " + "returned -0x%04x - %s\n\n", + (unsigned int) -ret, buf); + goto exit; + } + cur->node.san.directory_name = *ext_san_dirname; } else { mbedtls_free(cur); goto usage; } - if (strcmp(q, "IP") == 0) { -#if defined(MBEDTLS_X509_CRT_PARSE_C) - cur->node.san.unstructured_name.p = (unsigned char *) ip; - cur->node.san.unstructured_name.len = sizeof(ip); -#endif - } else { + if (cur->node.type == MBEDTLS_X509_SAN_RFC822_NAME || + cur->node.type == MBEDTLS_X509_SAN_UNIFORM_RESOURCE_IDENTIFIER || + cur->node.type == MBEDTLS_X509_SAN_DNS_NAME) { q = subtype_value; cur->node.san.unstructured_name.p = (unsigned char *) q; cur->node.san.unstructured_name.len = strlen(q); @@ -280,7 +313,6 @@ usage: prev = cur; q = r; } - } else if (strcmp(p, "md") == 0) { const mbedtls_md_info_t *md_info = mbedtls_md_info_from_string(q); @@ -467,6 +499,7 @@ exit: } mbedtls_x509write_csr_free(&req); + mbedtls_asn1_free_named_data_list(&ext_san_dirname); mbedtls_pk_free(&key); mbedtls_ctr_drbg_free(&ctr_drbg); mbedtls_entropy_free(&entropy); From 34ccd8d0b6b6fd23361378c19ac5bb419884fd0f Mon Sep 17 00:00:00 2001 From: Andrzej Kurek Date: Fri, 7 Jul 2023 06:32:17 -0400 Subject: [PATCH 3/7] Test x509 csr SAN DN and RFC822 generation Signed-off-by: Andrzej Kurek --- tests/data_files/Makefile | 3 +- tests/data_files/server1.req.sha256.conf | 17 +++++++++ tests/data_files/server1.req.sha256.ext | 22 ++++++----- tests/suites/test_suite_x509write.function | 44 ++++++++++++++++------ 4 files changed, 62 insertions(+), 24 deletions(-) create mode 100644 tests/data_files/server1.req.sha256.conf diff --git a/tests/data_files/Makefile b/tests/data_files/Makefile index 2ad5c2af6..92c0f0b1d 100644 --- a/tests/data_files/Makefile +++ b/tests/data_files/Makefile @@ -1303,8 +1303,7 @@ all_final += server1.req.sha256 server1.req.sha256.ext: server1.key # Generating this with OpenSSL as a comparison point to test we're getting the same result - openssl req -new -out $@ -key $< -subj '/C=NL/O=PolarSSL/CN=PolarSSL Server 1' -sha256 -addext "extendedKeyUsage=serverAuth" -addext "subjectAltName=URI:http://pki.example.com/,IP:127.1.1.0,DNS:example.com" -all_final += server1.req.sha256.ext + openssl req -new -out $@ -key $< -subj '/C=NL/O=PolarSSL/CN=PolarSSL Server 1' -sha256 -config server1.req.sha256.conf parse_input/server1.req.sha384 server1.req.sha384: server1.key $(MBEDTLS_CERT_REQ) output_file=$@ filename=$< subject_name="C=NL,O=PolarSSL,CN=PolarSSL Server 1" md=SHA384 diff --git a/tests/data_files/server1.req.sha256.conf b/tests/data_files/server1.req.sha256.conf new file mode 100644 index 000000000..0d35818c1 --- /dev/null +++ b/tests/data_files/server1.req.sha256.conf @@ -0,0 +1,17 @@ +req_extensions = req_ext + +[req_ext] +extendedKeyUsage = serverAuth +subjectAltName = @alt_names + +[alt_names] +email = mail@example.com +DNS = example.com +dirName = dirname_sect +IP = 127.0.0.1 +URI = http://pki.example.com + +[dirname_sect] +C=UK +O=Mbed TLS +CN=Mbed TLS directoryName SAN diff --git a/tests/data_files/server1.req.sha256.ext b/tests/data_files/server1.req.sha256.ext index c5ff5c573..1bb05da96 100644 --- a/tests/data_files/server1.req.sha256.ext +++ b/tests/data_files/server1.req.sha256.ext @@ -1,18 +1,20 @@ -----BEGIN CERTIFICATE REQUEST----- -MIIC3jCCAcYCAQAwPDELMAkGA1UEBhMCTkwxETAPBgNVBAoMCFBvbGFyU1NMMRow +MIIDPzCCAicCAQAwPDELMAkGA1UEBhMCTkwxETAPBgNVBAoMCFBvbGFyU1NMMRow GAYDVQQDDBFQb2xhclNTTCBTZXJ2ZXIgMTCCASIwDQYJKoZIhvcNAQEBBQADggEP ADCCAQoCggEBAKkCHz1AatVVU4v9Nu6CZS4VYV6Jv7joRZDb7ogWUtPxQ1BHlhJZ ZIdr/SvgRvlzvt3PkuGRW+1moG+JKXlFgNCDatVBQ3dfOXwJBEeCsFc5cO2j7BUZ HqgzCEfBBUKp/UzDtN/dBh9NEFFAZ3MTD0D4bYElXwqxU8YwfhU5rPla7n+SnqYF W+cTl4W1I5LZ1CQG1QkliXUH3aYajz8JGb6tZSxk65Wb3P5BXhem2mxbacwCuhQs FiScStzN0PdSZ3PxLaAj/X70McotcMqJCwTbLqZPcG6ezr1YieJTWZ5uWpJl4og/ -DJQZo93l6J2VE+0p26twEtxaymsXq1KCVLECAwEAAaBdMFsGCSqGSIb3DQEJDjFO -MEwwEwYDVR0lBAwwCgYIKwYBBQUHAwEwNQYDVR0RBC4wLIYXaHR0cDovL3BraS5l -eGFtcGxlLmNvbS+HBH8BAQCCC2V4YW1wbGUuY29tMA0GCSqGSIb3DQEBCwUAA4IB -AQCGmTIXEUvTqwChkzRtxPIQDDchrMnCXgUrTSxre5nvUOpjVlcIIPGWAwxRovfe -pW6OaGZ/3xD0dRAcOW08sTD6GRUazFrubPA1eZiNC7vYdWV59qm84N5yRR/s8Hm+ -okwI47m7W9C0pfaNXchgFUQBn16TrZxPXklbCpBJ/TFV+1ODY0sJPHYiCFpYI+Jz -YuJmadP2BHucl8wv2RyVHywOmV1sDc74i9igVrBCAh8wu+kqImMtrnkGZDxrnj/L -5P1eDfdqG2cN+s40RnMQMosh3UfqpNV/bTgAqBPP2uluT9L1KpWcjZeuvisOgVTq -XwFI5s34fen2DUVw6MWNfbDK +DJQZo93l6J2VE+0p26twEtxaymsXq1KCVLECAwEAAaCBvTCBugYJKoZIhvcNAQkO +MYGsMIGpMBMGA1UdJQQMMAoGCCsGAQUFBwMBMIGRBgNVHREEgYkwgYaBEG1haWxA +ZXhhbXBsZS5jb22CC2V4YW1wbGUuY29tpEcwRTELMAkGA1UEBhMCVUsxETAPBgNV +BAoMCE1iZWQgVExTMSMwIQYDVQQDDBpNYmVkIFRMUyBkaXJlY3RvcnlOYW1lIFNB +TocEfwAAAYYWaHR0cDovL3BraS5leGFtcGxlLmNvbTANBgkqhkiG9w0BAQsFAAOC +AQEAclrNmmgAoa4ctwyBwD1T8jbyBeuxTf+ifc+MQ6pE7YuYSlanHE5l/CoVlW14 +lR5gA01pWJJ7T8RBvo25OqXbvMFSafeGXpSHOG69A6p/7YULtbPuS6uvtdR0m3t+ +2IacL0q5FsSmPw07RNfVGDFniKVqD8eAuYnhFztk0+uZVYD4xGezUckb2wTbzFpu +lUA/NhoWfCyV44TDR5fy23qNXywEhatDU/3nMmSJpBVy4y7J6BQVCl/fbyuKIOqu +0OVP+FvANSO46twA9+38hI+/nPuVwtbBvg1aLBMbLZ3Egi2uozokYFYL22JYNGJo +XORQgR66Sdrvfhiug+F5xmldCg== -----END CERTIFICATE REQUEST----- diff --git a/tests/suites/test_suite_x509write.function b/tests/suites/test_suite_x509write.function index ab4a2d0d3..b4073eccb 100644 --- a/tests/suites/test_suite_x509write.function +++ b/tests/suites/test_suite_x509write.function @@ -153,24 +153,44 @@ void x509_csr_check(char *key_file, char *cert_req_check_file, int md_type, mbedtls_x509_san_list san_ip; mbedtls_x509_san_list san_dns; mbedtls_x509_san_list san_uri; + mbedtls_x509_san_list san_mail; + mbedtls_x509_san_list san_dn; mbedtls_x509_san_list *san_list = NULL; - const char san_ip_name[] = { 0x7f, 0x01, 0x01, 0x00 }; // 127.1.1.0 + mbedtls_asn1_named_data *ext_san_dirname = NULL; + + const char san_ip_name[] = { 0x7f, 0x00, 0x00, 0x01 }; // 127.0.0.1 const char *san_dns_name = "example.com"; - const char *san_uri_name = "http://pki.example.com/"; + const char *san_dn_name = "C=UK,O=Mbed TLS,CN=Mbed TLS directoryName SAN"; + const char *san_mail_name = "mail@example.com"; + const char *san_uri_name = "http://pki.example.com"; + + san_mail.node.type = MBEDTLS_X509_SAN_RFC822_NAME; + san_mail.node.san.unstructured_name.p = (unsigned char *) san_mail_name; + san_mail.node.san.unstructured_name.len = strlen(san_mail_name); + san_mail.next = NULL; + + san_dns.node.type = MBEDTLS_X509_SAN_DNS_NAME; + san_dns.node.san.unstructured_name.p = (unsigned char *) san_dns_name; + san_dns.node.san.unstructured_name.len = strlen(san_dns_name); + san_dns.next = &san_mail; + + san_dn.node.type = MBEDTLS_X509_SAN_DIRECTORY_NAME; + TEST_ASSERT(mbedtls_x509_string_to_names(&ext_san_dirname, + san_dn_name) == 0); + san_dn.node.san.directory_name = *ext_san_dirname; + san_dn.next = &san_dns; + + san_ip.node.type = MBEDTLS_X509_SAN_IP_ADDRESS; + san_ip.node.san.unstructured_name.p = (unsigned char *) san_ip_name; + san_ip.node.san.unstructured_name.len = sizeof(san_ip_name); + san_ip.next = &san_dn; san_uri.node.type = MBEDTLS_X509_SAN_UNIFORM_RESOURCE_IDENTIFIER; san_uri.node.san.unstructured_name.p = (unsigned char *) san_uri_name; san_uri.node.san.unstructured_name.len = strlen(san_uri_name); - san_uri.next = NULL; - san_ip.node.type = MBEDTLS_X509_SAN_IP_ADDRESS; - san_ip.node.san.unstructured_name.p = (unsigned char *) san_ip_name; - san_ip.node.san.unstructured_name.len = sizeof(san_ip_name); - san_ip.next = &san_uri; - san_dns.node.type = MBEDTLS_X509_SAN_DNS_NAME; - san_dns.node.san.unstructured_name.p = (unsigned char *) san_dns_name; - san_dns.node.san.unstructured_name.len = strlen(san_dns_name); - san_dns.next = &san_ip; - san_list = &san_dns; + san_uri.next = &san_ip; + + san_list = &san_uri; memset(&rnd_info, 0x2a, sizeof(mbedtls_test_rnd_pseudo_info)); From c508dc29f6610f00359c782d235179021610d1ab Mon Sep 17 00:00:00 2001 From: Andrzej Kurek Date: Fri, 7 Jul 2023 08:20:02 -0400 Subject: [PATCH 4/7] Unify csr and crt san writing functions Signed-off-by: Andrzej Kurek --- include/mbedtls/x509.h | 3 + library/CMakeLists.txt | 1 + library/Makefile | 1 + library/x509write.c | 186 ++++++++++++++++++++++++++++++++++++++++ library/x509write_crt.c | 141 +----------------------------- library/x509write_csr.c | 142 +----------------------------- 6 files changed, 193 insertions(+), 281 deletions(-) create mode 100644 library/x509write.c diff --git a/include/mbedtls/x509.h b/include/mbedtls/x509.h index 7c9a76184..3d94ec043 100644 --- a/include/mbedtls/x509.h +++ b/include/mbedtls/x509.h @@ -525,6 +525,9 @@ int mbedtls_x509_info_cert_type(char **buf, size_t *size, int mbedtls_x509_info_key_usage(char **buf, size_t *size, unsigned int key_usage); +int mbedtls_x509_write_set_san_common(mbedtls_asn1_named_data **extensions, + const mbedtls_x509_san_list *san_list); + /** * \brief This function parses a CN string as an IP address. * diff --git a/library/CMakeLists.txt b/library/CMakeLists.txt index 8e70c4635..895805ff4 100644 --- a/library/CMakeLists.txt +++ b/library/CMakeLists.txt @@ -98,6 +98,7 @@ set(src_x509 x509_crl.c x509_crt.c x509_csr.c + x509write.c x509write_crt.c x509write_csr.c ) diff --git a/library/Makefile b/library/Makefile index fafcdda8f..8bddd7d1b 100644 --- a/library/Makefile +++ b/library/Makefile @@ -166,6 +166,7 @@ OBJS_X509= \ x509_crl.o \ x509_crt.o \ x509_csr.o \ + x509write.o \ x509write_crt.o \ x509write_csr.o \ pkcs7.o \ diff --git a/library/x509write.c b/library/x509write.c new file mode 100644 index 000000000..cd3c7394d --- /dev/null +++ b/library/x509write.c @@ -0,0 +1,186 @@ +/* + * X.509 internal, common functions for writing + * + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "common.h" +#if defined(MBEDTLS_X509_CSR_WRITE_C) || defined(MBEDTLS_X509_CRT_WRITE_C) + +#include "mbedtls/x509_crt.h" +#include "mbedtls/asn1write.h" +#include "mbedtls/error.h" +#include "mbedtls/oid.h" +#include "mbedtls/platform.h" +#include "mbedtls/platform_util.h" +#include "mbedtls/md.h" + +#include +#include + +#if defined(MBEDTLS_PEM_WRITE_C) +#include "mbedtls/pem.h" +#endif /* MBEDTLS_PEM_WRITE_C */ + +#if defined(MBEDTLS_USE_PSA_CRYPTO) +#include "psa/crypto.h" +#include "mbedtls/psa_util.h" +#include "md_psa.h" +#endif /* MBEDTLS_USE_PSA_CRYPTO */ + +#define CHECK_OVERFLOW_ADD(a, b) \ + do \ + { \ + if (a > SIZE_MAX - (b)) \ + { \ + return MBEDTLS_ERR_X509_BAD_INPUT_DATA; \ + } \ + a += b; \ + } while (0) + +int mbedtls_x509_write_set_san_common(mbedtls_asn1_named_data **extensions, + const mbedtls_x509_san_list *san_list) +{ + int ret = 0; + const mbedtls_x509_san_list *cur; + unsigned char *buf; + unsigned char *p; + size_t len; + size_t buflen = 0; + + /* Determine the maximum size of the SubjectAltName list */ + for (cur = san_list; cur != NULL; cur = cur->next) { + /* Calculate size of the required buffer */ + switch (cur->node.type) { + case MBEDTLS_X509_SAN_DNS_NAME: + case MBEDTLS_X509_SAN_UNIFORM_RESOURCE_IDENTIFIER: + case MBEDTLS_X509_SAN_IP_ADDRESS: + case MBEDTLS_X509_SAN_RFC822_NAME: + /* length of value for each name entry, + * maximum 4 bytes for the length field, + * 1 byte for the tag/type. + */ + CHECK_OVERFLOW_ADD(buflen, cur->node.san.unstructured_name.len); + CHECK_OVERFLOW_ADD(buflen, 4 + 1); + break; + case MBEDTLS_X509_SAN_DIRECTORY_NAME: + { + const mbedtls_asn1_named_data *chunk = &cur->node.san.directory_name; + while (chunk != NULL) { + // Max 4 bytes for length, +1 for tag, + // additional 4 max for length, +1 for tag. + // See x509_write_name for more information. + CHECK_OVERFLOW_ADD(buflen, 4 + 1 + 4 + 1); + CHECK_OVERFLOW_ADD(buflen, chunk->oid.len); + CHECK_OVERFLOW_ADD(buflen, chunk->val.len); + chunk = chunk->next; + } + CHECK_OVERFLOW_ADD(buflen, 4 + 1); + break; + } + default: + /* Not supported - return. */ + return MBEDTLS_ERR_X509_FEATURE_UNAVAILABLE; + } + } + + /* Add the extra length field and tag */ + CHECK_OVERFLOW_ADD(buflen, 4 + 1); + + /* Allocate buffer */ + buf = mbedtls_calloc(1, buflen); + if (buf == NULL) { + return MBEDTLS_ERR_ASN1_ALLOC_FAILED; + } + p = buf + buflen; + + /* Write ASN.1-based structure */ + cur = san_list; + len = 0; + while (cur != NULL) { + size_t single_san_len = 0; + switch (cur->node.type) { + case MBEDTLS_X509_SAN_DNS_NAME: + case MBEDTLS_X509_SAN_RFC822_NAME: + case MBEDTLS_X509_SAN_UNIFORM_RESOURCE_IDENTIFIER: + case MBEDTLS_X509_SAN_IP_ADDRESS: + { + const unsigned char *unstructured_name = + (const unsigned char *) cur->node.san.unstructured_name.p; + size_t unstructured_name_len = cur->node.san.unstructured_name.len; + + MBEDTLS_ASN1_CHK_CLEANUP_ADD(single_san_len, + mbedtls_asn1_write_raw_buffer( + &p, buf, + unstructured_name, unstructured_name_len)); + MBEDTLS_ASN1_CHK_CLEANUP_ADD(single_san_len, mbedtls_asn1_write_len( + &p, buf, unstructured_name_len)); + MBEDTLS_ASN1_CHK_CLEANUP_ADD(single_san_len, + mbedtls_asn1_write_tag( + &p, buf, + MBEDTLS_ASN1_CONTEXT_SPECIFIC | cur->node.type)); + } + break; + case MBEDTLS_X509_SAN_DIRECTORY_NAME: + MBEDTLS_ASN1_CHK_CLEANUP_ADD(single_san_len, + mbedtls_x509_write_names(&p, buf, + (mbedtls_asn1_named_data *) & + cur->node + .san.directory_name)); + MBEDTLS_ASN1_CHK_CLEANUP_ADD(single_san_len, + mbedtls_asn1_write_len(&p, buf, single_san_len)); + MBEDTLS_ASN1_CHK_CLEANUP_ADD(single_san_len, + mbedtls_asn1_write_tag(&p, buf, + MBEDTLS_ASN1_CONTEXT_SPECIFIC | + MBEDTLS_ASN1_CONSTRUCTED | + MBEDTLS_X509_SAN_DIRECTORY_NAME)); + break; + default: + /* Error out on an unsupported SAN */ + ret = MBEDTLS_ERR_X509_FEATURE_UNAVAILABLE; + goto cleanup; + } + cur = cur->next; + /* check for overflow */ + if (len > SIZE_MAX - single_san_len) { + ret = MBEDTLS_ERR_X509_BAD_INPUT_DATA; + goto cleanup; + } + len += single_san_len; + } + + MBEDTLS_ASN1_CHK_CLEANUP_ADD(len, mbedtls_asn1_write_len(&p, buf, len)); + MBEDTLS_ASN1_CHK_CLEANUP_ADD(len, + mbedtls_asn1_write_tag(&p, buf, + MBEDTLS_ASN1_CONSTRUCTED | + MBEDTLS_ASN1_SEQUENCE)); + + ret = mbedtls_x509_set_extension(extensions, + MBEDTLS_OID_SUBJECT_ALT_NAME, + MBEDTLS_OID_SIZE(MBEDTLS_OID_SUBJECT_ALT_NAME), + 0, + buf + buflen - len, len); + + /* If we exceeded the allocated buffer it means that maximum size of the SubjectAltName list + * was incorrectly calculated and memory is corrupted. */ + if (p < buf) { + ret = MBEDTLS_ERR_ASN1_LENGTH_MISMATCH; + } +cleanup: + mbedtls_free(buf); + return ret; +} + +#endif /* MBEDTLS_X509_CSR_WRITE_C || MBEDTLS_X509_CRT_WRITE_C */ diff --git a/library/x509write_crt.c b/library/x509write_crt.c index a6dc22c5f..b1d0ca0f9 100644 --- a/library/x509write_crt.c +++ b/library/x509write_crt.c @@ -48,16 +48,6 @@ #include "md_psa.h" #endif /* MBEDTLS_USE_PSA_CRYPTO */ -#define CHECK_OVERFLOW_ADD(a, b) \ - do \ - { \ - if (a > SIZE_MAX - (b)) \ - { \ - return MBEDTLS_ERR_X509_BAD_INPUT_DATA; \ - } \ - a += b; \ - } while (0) - void mbedtls_x509write_crt_init(mbedtls_x509write_cert *ctx) { memset(ctx, 0, sizeof(mbedtls_x509write_cert)); @@ -166,136 +156,7 @@ int mbedtls_x509write_crt_set_validity(mbedtls_x509write_cert *ctx, int mbedtls_x509write_crt_set_subject_alternative_name(mbedtls_x509write_cert *ctx, const mbedtls_x509_san_list *san_list) { - int ret = 0; - const mbedtls_x509_san_list *cur; - unsigned char *buf; - unsigned char *p; - size_t len; - size_t buflen = 0; - - /* Determine the maximum size of the SubjectAltName list */ - for (cur = san_list; cur != NULL; cur = cur->next) { - /* Calculate size of the required buffer */ - switch (cur->node.type) { - case MBEDTLS_X509_SAN_DNS_NAME: - case MBEDTLS_X509_SAN_UNIFORM_RESOURCE_IDENTIFIER: - case MBEDTLS_X509_SAN_IP_ADDRESS: - case MBEDTLS_X509_SAN_RFC822_NAME: - /* length of value for each name entry, - * maximum 4 bytes for the length field, - * 1 byte for the tag/type. - */ - CHECK_OVERFLOW_ADD(buflen, cur->node.san.unstructured_name.len); - CHECK_OVERFLOW_ADD(buflen, 4 + 1); - break; - case MBEDTLS_X509_SAN_DIRECTORY_NAME: - { - const mbedtls_asn1_named_data *chunk = &cur->node.san.directory_name; - while (chunk != NULL) { - // Max 4 bytes for length, +1 for tag, - // additional 4 max for length, +1 for tag. - // See x509_write_name for more information. - CHECK_OVERFLOW_ADD(buflen, 4 + 1 + 4 + 1); - CHECK_OVERFLOW_ADD(buflen, chunk->oid.len); - CHECK_OVERFLOW_ADD(buflen, chunk->val.len); - chunk = chunk->next; - } - CHECK_OVERFLOW_ADD(buflen, 4 + 1); - break; - } - default: - /* Not supported - return. */ - return MBEDTLS_ERR_X509_FEATURE_UNAVAILABLE; - } - } - - /* Add the extra length field and tag */ - CHECK_OVERFLOW_ADD(buflen, 4 + 1); - - /* Allocate buffer */ - buf = mbedtls_calloc(1, buflen); - if (buf == NULL) { - return MBEDTLS_ERR_ASN1_ALLOC_FAILED; - } - p = buf + buflen; - - /* Write ASN.1-based structure */ - cur = san_list; - len = 0; - while (cur != NULL) { - size_t single_san_len = 0; - switch (cur->node.type) { - case MBEDTLS_X509_SAN_DNS_NAME: - case MBEDTLS_X509_SAN_RFC822_NAME: - case MBEDTLS_X509_SAN_UNIFORM_RESOURCE_IDENTIFIER: - case MBEDTLS_X509_SAN_IP_ADDRESS: - { - const unsigned char *unstructured_name = - (const unsigned char *) cur->node.san.unstructured_name.p; - size_t unstructured_name_len = cur->node.san.unstructured_name.len; - - MBEDTLS_ASN1_CHK_CLEANUP_ADD(single_san_len, - mbedtls_asn1_write_raw_buffer( - &p, buf, - unstructured_name, unstructured_name_len)); - MBEDTLS_ASN1_CHK_CLEANUP_ADD(single_san_len, mbedtls_asn1_write_len( - &p, buf, unstructured_name_len)); - MBEDTLS_ASN1_CHK_CLEANUP_ADD(single_san_len, - mbedtls_asn1_write_tag( - &p, buf, - MBEDTLS_ASN1_CONTEXT_SPECIFIC | cur->node.type)); - } - break; - case MBEDTLS_X509_SAN_DIRECTORY_NAME: - MBEDTLS_ASN1_CHK_CLEANUP_ADD(single_san_len, - mbedtls_x509_write_names(&p, buf, - (mbedtls_asn1_named_data *) & - cur->node - .san.directory_name)); - MBEDTLS_ASN1_CHK_CLEANUP_ADD(single_san_len, - mbedtls_asn1_write_len(&p, buf, single_san_len)); - MBEDTLS_ASN1_CHK_CLEANUP_ADD(single_san_len, - mbedtls_asn1_write_tag(&p, buf, - MBEDTLS_ASN1_CONTEXT_SPECIFIC | - MBEDTLS_ASN1_CONSTRUCTED | - MBEDTLS_X509_SAN_DIRECTORY_NAME)); - break; - default: - /* Error out on an unsupported SAN */ - ret = MBEDTLS_ERR_X509_FEATURE_UNAVAILABLE; - goto cleanup; - } - cur = cur->next; - /* check for overflow */ - if (len > SIZE_MAX - single_san_len) { - ret = MBEDTLS_ERR_X509_BAD_INPUT_DATA; - goto cleanup; - } - len += single_san_len; - } - - MBEDTLS_ASN1_CHK_CLEANUP_ADD(len, mbedtls_asn1_write_len(&p, buf, len)); - MBEDTLS_ASN1_CHK_CLEANUP_ADD(len, - mbedtls_asn1_write_tag(&p, buf, - MBEDTLS_ASN1_CONSTRUCTED | - MBEDTLS_ASN1_SEQUENCE)); - - ret = mbedtls_x509write_crt_set_extension( - ctx, - MBEDTLS_OID_SUBJECT_ALT_NAME, - MBEDTLS_OID_SIZE(MBEDTLS_OID_SUBJECT_ALT_NAME), - 0, - buf + buflen - len, - len); - - /* If we exceeded the allocated buffer it means that maximum size of the SubjectAltName list - * was incorrectly calculated and memory is corrupted. */ - if (p < buf) { - ret = MBEDTLS_ERR_ASN1_LENGTH_MISMATCH; - } -cleanup: - mbedtls_free(buf); - return ret; + return mbedtls_x509_write_set_san_common(&ctx->extensions, san_list); } diff --git a/library/x509write_csr.c b/library/x509write_csr.c index 76473c486..f07a822f9 100644 --- a/library/x509write_csr.c +++ b/library/x509write_csr.c @@ -48,16 +48,6 @@ #include "mbedtls/platform.h" -#define CHECK_OVERFLOW_ADD(a, b) \ - do \ - { \ - if (a > SIZE_MAX - (b)) \ - { \ - return MBEDTLS_ERR_X509_BAD_INPUT_DATA; \ - } \ - a += b; \ - } while (0) - void mbedtls_x509write_csr_init(mbedtls_x509write_csr *ctx) { memset(ctx, 0, sizeof(mbedtls_x509write_csr)); @@ -99,137 +89,7 @@ int mbedtls_x509write_csr_set_extension(mbedtls_x509write_csr *ctx, int mbedtls_x509write_csr_set_subject_alternative_name(mbedtls_x509write_csr *ctx, const mbedtls_x509_san_list *san_list) { - int ret = 0; - const mbedtls_x509_san_list *cur; - unsigned char *buf; - unsigned char *p; - size_t len; - size_t buflen = 0; - - /* Determine the maximum size of the SubjectAltName list */ - for (cur = san_list; cur != NULL; cur = cur->next) { - /* Calculate size of the required buffer */ - switch (cur->node.type) { - case MBEDTLS_X509_SAN_DNS_NAME: - case MBEDTLS_X509_SAN_UNIFORM_RESOURCE_IDENTIFIER: - case MBEDTLS_X509_SAN_IP_ADDRESS: - case MBEDTLS_X509_SAN_RFC822_NAME: - /* length of value for each name entry, - * maximum 4 bytes for the length field, - * 1 byte for the tag/type. - */ - CHECK_OVERFLOW_ADD(buflen, cur->node.san.unstructured_name.len); - CHECK_OVERFLOW_ADD(buflen, 4 + 1); - break; - case MBEDTLS_X509_SAN_DIRECTORY_NAME: - { - const mbedtls_asn1_named_data *chunk = &cur->node.san.directory_name; - while (chunk != NULL) { - // Max 4 bytes for length, +1 for tag, - // additional 4 max for length, +1 for tag. - // See x509_write_name for more information. - CHECK_OVERFLOW_ADD(buflen, 4 + 1 + 4 + 1); - CHECK_OVERFLOW_ADD(buflen, chunk->oid.len); - CHECK_OVERFLOW_ADD(buflen, chunk->val.len); - chunk = chunk->next; - } - CHECK_OVERFLOW_ADD(buflen, 4 + 1); - break; - } - default: - /* Not supported - return. */ - return MBEDTLS_ERR_X509_FEATURE_UNAVAILABLE; - } - } - - /* Add the extra length field and tag */ - CHECK_OVERFLOW_ADD(buflen, 4 + 1); - - /* Allocate buffer */ - buf = mbedtls_calloc(1, buflen); - if (buf == NULL) { - return MBEDTLS_ERR_ASN1_ALLOC_FAILED; - } - p = buf + buflen; - - /* Write ASN.1-based structure */ - cur = san_list; - len = 0; - while (cur != NULL) { - size_t single_san_len = 0; - switch (cur->node.type) { - case MBEDTLS_X509_SAN_DNS_NAME: - case MBEDTLS_X509_SAN_RFC822_NAME: - case MBEDTLS_X509_SAN_UNIFORM_RESOURCE_IDENTIFIER: - case MBEDTLS_X509_SAN_IP_ADDRESS: - { - const unsigned char *unstructured_name = - (const unsigned char *) cur->node.san.unstructured_name.p; - size_t unstructured_name_len = cur->node.san.unstructured_name.len; - - MBEDTLS_ASN1_CHK_CLEANUP_ADD(single_san_len, - mbedtls_asn1_write_raw_buffer( - &p, buf, - unstructured_name, unstructured_name_len)); - MBEDTLS_ASN1_CHK_CLEANUP_ADD(single_san_len, mbedtls_asn1_write_len( - &p, buf, unstructured_name_len)); - MBEDTLS_ASN1_CHK_CLEANUP_ADD(single_san_len, - mbedtls_asn1_write_tag( - &p, buf, - MBEDTLS_ASN1_CONTEXT_SPECIFIC | cur->node.type)); - } - break; - case MBEDTLS_X509_SAN_DIRECTORY_NAME: - MBEDTLS_ASN1_CHK_CLEANUP_ADD(single_san_len, - mbedtls_x509_write_names(&p, buf, - (mbedtls_asn1_named_data *) & - cur->node - .san.directory_name)); - MBEDTLS_ASN1_CHK_CLEANUP_ADD(single_san_len, - mbedtls_asn1_write_len(&p, buf, single_san_len)); - MBEDTLS_ASN1_CHK_CLEANUP_ADD(single_san_len, - mbedtls_asn1_write_tag(&p, buf, - MBEDTLS_ASN1_CONTEXT_SPECIFIC | - MBEDTLS_ASN1_CONSTRUCTED | - MBEDTLS_X509_SAN_DIRECTORY_NAME)); - break; - default: - /* Error out on an unsupported SAN */ - ret = MBEDTLS_ERR_X509_FEATURE_UNAVAILABLE; - goto cleanup; - } - cur = cur->next; - /* check for overflow */ - if (len > SIZE_MAX - single_san_len) { - ret = MBEDTLS_ERR_X509_BAD_INPUT_DATA; - goto cleanup; - } - len += single_san_len; - } - - MBEDTLS_ASN1_CHK_CLEANUP_ADD(len, mbedtls_asn1_write_len(&p, buf, len)); - MBEDTLS_ASN1_CHK_CLEANUP_ADD(len, - mbedtls_asn1_write_tag(&p, buf, - MBEDTLS_ASN1_CONSTRUCTED | - MBEDTLS_ASN1_SEQUENCE)); - - ret = mbedtls_x509write_csr_set_extension( - ctx, - MBEDTLS_OID_SUBJECT_ALT_NAME, - MBEDTLS_OID_SIZE(MBEDTLS_OID_SUBJECT_ALT_NAME), - 0, - buf + buflen - len, - len); - - /* If we exceeded the allocated buffer it means that maximum size of the SubjectAltName list - * was incorrectly calculated and memory is corrupted. */ - if (p < buf) { - ret = MBEDTLS_ERR_ASN1_LENGTH_MISMATCH; - } - -cleanup: - mbedtls_free(buf); - return ret; + return mbedtls_x509_write_set_san_common(&ctx->extensions, san_list); } int mbedtls_x509write_csr_set_key_usage(mbedtls_x509write_csr *ctx, unsigned char key_usage) From b8c784cdbacf4ac40e1cc0149356b3e72ec84346 Mon Sep 17 00:00:00 2001 From: Andrzej Kurek Date: Fri, 7 Jul 2023 08:24:46 -0400 Subject: [PATCH 5/7] Changelog entry Signed-off-by: Andrzej Kurek --- ChangeLog.d/add-rfc822-directoryname-csr-gen.txt | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 ChangeLog.d/add-rfc822-directoryname-csr-gen.txt diff --git a/ChangeLog.d/add-rfc822-directoryname-csr-gen.txt b/ChangeLog.d/add-rfc822-directoryname-csr-gen.txt new file mode 100644 index 000000000..ff8693c40 --- /dev/null +++ b/ChangeLog.d/add-rfc822-directoryname-csr-gen.txt @@ -0,0 +1,3 @@ +Features + * Add a possibility to generate CSR's with RCF822 and directoryName subtype + of subjectAltName extension in x509 certificates. From bdb41dd46d87700b3e5fc3e1db7e9923d9fda6e5 Mon Sep 17 00:00:00 2001 From: Andrzej Kurek Date: Mon, 10 Jul 2023 08:09:50 -0400 Subject: [PATCH 6/7] Add missing resource deallocation in tests Signed-off-by: Andrzej Kurek --- tests/suites/test_suite_x509write.function | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/suites/test_suite_x509write.function b/tests/suites/test_suite_x509write.function index b4073eccb..a0b1dc135 100644 --- a/tests/suites/test_suite_x509write.function +++ b/tests/suites/test_suite_x509write.function @@ -264,6 +264,7 @@ void x509_csr_check(char *key_file, char *cert_req_check_file, int md_type, TEST_ASSERT(ret == MBEDTLS_ERR_ASN1_BUF_TOO_SMALL); exit: + mbedtls_asn1_free_named_data_list(&ext_san_dirname); mbedtls_x509write_csr_free(&req); mbedtls_pk_free(&key); MD_OR_USE_PSA_DONE(); From 312b6df38ae874258bb9fe3d02952d5b59a25bb2 Mon Sep 17 00:00:00 2001 From: Andrzej Kurek Date: Mon, 10 Jul 2023 08:45:30 -0400 Subject: [PATCH 7/7] Add a missing guard in cert_req.c IP parsing requires x509_CRT_PARSE_C Signed-off-by: Andrzej Kurek --- programs/x509/cert_req.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/programs/x509/cert_req.c b/programs/x509/cert_req.c index 17de9def7..558d8cc73 100644 --- a/programs/x509/cert_req.c +++ b/programs/x509/cert_req.c @@ -23,10 +23,10 @@ /* md.h is included this early since MD_CAN_XXX macros are defined there. */ #include "mbedtls/md.h" -#if !defined(MBEDTLS_X509_CSR_WRITE_C) || !defined(MBEDTLS_FS_IO) || \ +#if !defined(MBEDTLS_X509_CSR_WRITE_C) || !defined(MBEDTLS_X509_CRT_PARSE_C) || \ !defined(MBEDTLS_PK_PARSE_C) || !defined(MBEDTLS_MD_CAN_SHA256) || \ !defined(MBEDTLS_ENTROPY_C) || !defined(MBEDTLS_CTR_DRBG_C) || \ - !defined(MBEDTLS_PEM_WRITE_C) + !defined(MBEDTLS_PEM_WRITE_C) || !defined(MBEDTLS_FS_IO) int main(void) { mbedtls_printf("MBEDTLS_X509_CSR_WRITE_C and/or MBEDTLS_FS_IO and/or "