diff --git a/src/x509.c b/src/x509.c index 41163b4809e..d7f8f43eee9 100644 --- a/src/x509.c +++ b/src/x509.c @@ -6228,6 +6228,44 @@ int wolfSSL_NAME_CONSTRAINTS_check_name(WOLFSSL_NAME_CONSTRAINTS* nc, } #endif /* !IGNORE_NAME_CONSTRAINTS */ +#ifndef NO_BIO +static int wolfssl_x509_name_esc_value(const char* in, int inSz, + unsigned long flags, char* out); + +/* Write pfx then val to bio with control characters escaped. + * + * Returns WOLFSSL_SUCCESS on success, WOLFSSL_FAILURE on failure. + */ +static int X509PrintEscStr(WOLFSSL_BIO* bio, const char* pfx, + const char* val, int valSz) +{ + char buf[96]; + int ret = WOLFSSL_SUCCESS; + int pfxSz = (int)XSTRLEN(pfx); + int inSz = 0; + int escSz; + int i; + + if ((pfxSz > 0) && (wolfSSL_BIO_write(bio, pfx, pfxSz) != pfxSz)) { + ret = WOLFSSL_FAILURE; + } + /* ESC_CTRL ignores position and at most triples a byte, so chunk it. */ + for (i = 0; (ret == WOLFSSL_SUCCESS) && (i < valSz); i += inSz) { + inSz = valSz - i; + if (inSz > (int)sizeof(buf) / 3) { + inSz = (int)sizeof(buf) / 3; + } + escSz = wolfssl_x509_name_esc_value(val + i, inSz, + WOLFSSL_ASN1_STRFLGS_ESC_CTRL, buf); + if (wolfSSL_BIO_write(bio, buf, escSz) != escSz) { + ret = WOLFSSL_FAILURE; + } + } + + return ret; +} +#endif /* !NO_BIO */ + #if defined(OPENSSL_ALL) && !defined(NO_BIO) /* Outputs name string of the given WOLFSSL_GENERAL_NAME_OBJECT to WOLFSSL_BIO. * Can handle following GENERAL_NAME_OBJECT types: @@ -6272,18 +6310,16 @@ int wolfSSL_GENERAL_NAME_print(WOLFSSL_BIO* out, WOLFSSL_GENERAL_NAME* gen) break; case GEN_EMAIL: - ret = wolfSSL_BIO_printf(out, "email:"); - ret = (ret > 0) ? WOLFSSL_SUCCESS : WOLFSSL_FAILURE; - if (ret == WOLFSSL_SUCCESS) { - ret = wolfSSL_ASN1_STRING_print(out, gen->d.rfc822Name); + if (gen->d.rfc822Name != NULL) { + ret = X509PrintEscStr(out, "email:", gen->d.rfc822Name->data, + gen->d.rfc822Name->length); } break; case GEN_DNS: - ret = wolfSSL_BIO_printf(out, "DNS:"); - ret = (ret > 0) ? WOLFSSL_SUCCESS : WOLFSSL_FAILURE; - if (ret == WOLFSSL_SUCCESS) { - ret = wolfSSL_ASN1_STRING_print(out, gen->d.dNSName); + if (gen->d.dNSName != NULL) { + ret = X509PrintEscStr(out, "DNS:", gen->d.dNSName->data, + gen->d.dNSName->length); } break; @@ -6307,11 +6343,10 @@ int wolfSSL_GENERAL_NAME_print(WOLFSSL_BIO* out, WOLFSSL_GENERAL_NAME* gen) break; case GEN_URI: - ret = wolfSSL_BIO_printf(out, "URI:"); - ret = (ret > 0) ? WOLFSSL_SUCCESS : WOLFSSL_FAILURE; - if (ret == WOLFSSL_SUCCESS) { - ret = wolfSSL_ASN1_STRING_print(out, - gen->d.uniformResourceIdentifier); + if (gen->d.uniformResourceIdentifier != NULL) { + ret = X509PrintEscStr(out, "URI:", + gen->d.uniformResourceIdentifier->data, + gen->d.uniformResourceIdentifier->length); } break; @@ -7252,24 +7287,21 @@ static struct acert_dir_print_t acert_dir_print[ACERT_NUM_DIR_TAGS] = { "CN=", {0x55, 0x04, ASN_COMMON_NAME} }, }; -/* Print an entry of ASN_DIR_TYPE into dst of length max_len. +/* Print an entry of ASN_DIR_TYPE to bio. * - * Returns total_len of str on success. - * Returns < 0 on failure. + * Returns WOLFSSL_SUCCESS on success. + * Returns WOLFSSL_FAILURE on failure. * */ -static int X509PrintDirType(char * dst, int max_len, const DNS_entry * entry) +static int X509PrintDirType(WOLFSSL_BIO* bio, const DNS_entry * entry) { word32 k = 0; word32 i = 0; const char * src = entry->name; word32 src_len = 0; - int total_len = 0; - int bytes_left = max_len; int fld_len = 0; + int ret = WOLFSSL_SUCCESS; int match_found = 0; - XMEMSET(dst, 0, max_len); - /* The entry holds raw DER which may contain zero bytes, and under * WC_ASN_NO_HEAP it is not NUL terminated, so use the stored length. */ if (entry->len > 0) { @@ -7277,7 +7309,7 @@ static int X509PrintDirType(char * dst, int max_len, const DNS_entry * entry) } /* loop over printable DIR tags. */ - for (k = 0; k < ACERT_NUM_DIR_TAGS; ++k) { + for (k = 0; (ret == WOLFSSL_SUCCESS) && (k < ACERT_NUM_DIR_TAGS); ++k) { const char * pfx = acert_dir_print[k].pfx; const byte * tag = acert_dir_print[k].tag; byte asn_tag; @@ -7289,19 +7321,6 @@ static int X509PrintDirType(char * dst, int max_len, const DNS_entry * entry) * underflowing the bound. */ for (i = 0; i + 5 <= src_len; ++i) { if (XMEMCMP(tag, &src[i], 3) == 0) { - if (bytes_left < 5) { - /* Not enough space left for name oid + tag + len. */ - break; - } - - if (match_found) { - /* append a {',', ' '} before doing anything else. */ - *dst++ = ','; - *dst++ = ' '; - total_len += 2; - bytes_left -= 2; - } - i += 3; /* Get the ASN Tag. */ @@ -7324,35 +7343,32 @@ static int X509PrintDirType(char * dst, int max_len, const DNS_entry * entry) break; } - /* Make sure we have space to fit it. */ - if ((int) XSTRLEN(pfx) > bytes_left) { - /* Not enough space left. */ - break; + if (match_found && (wolfSSL_BIO_puts(bio, ", ") <= 0)) { + ret = WOLFSSL_FAILURE; } - - /* Copy it in, decrement available space. */ - XSTRNCPY(dst, pfx, bytes_left); - dst += XSTRLEN(pfx); - total_len += (int)XSTRLEN(pfx); - bytes_left -= (int)XSTRLEN(pfx); - - if (fld_len > bytes_left) { - /* Not enough space left. */ + if (ret == WOLFSSL_SUCCESS) { + ret = X509PrintEscStr(bio, pfx, &src[i], fld_len); + } + if (ret != WOLFSSL_SUCCESS) { break; } - - XMEMCPY(dst, &src[i], fld_len); i += fld_len; - dst += fld_len; - total_len += fld_len; - bytes_left -= fld_len; match_found = 1; } } } - return total_len; + if ((ret == WOLFSSL_SUCCESS) && !match_found) { + /* Nothing in the encoding was printable. Emit a placeholder, as the + * other unsupported entry types do, rather than failing the print of + * the whole certificate. */ + if (wolfSSL_BIO_puts(bio, "DirName:") <= 0) { + ret = WOLFSSL_FAILURE; + } + } + + return ret; } static int X509_print_name_entry(WOLFSSL_BIO* bio, const DNS_entry* entry, int indent) @@ -7384,8 +7400,10 @@ static int X509_print_name_entry(WOLFSSL_BIO* bio, } } + /* Escaped values go straight to bio, the rest through scratch. */ + len = 0; if (entry->type == ASN_DNS_TYPE) { - len = XSNPRINTF(scratch, MAX_WIDTH, "DNS:%s", entry->name); + ret = X509PrintEscStr(bio, "DNS:", entry->name, entry->len); } #if defined(OPENSSL_ALL) || defined(WOLFSSL_IP_ALT_NAME) else if (entry->type == ASN_IP_TYPE) { @@ -7402,21 +7420,13 @@ static int X509_print_name_entry(WOLFSSL_BIO* bio, } #endif /* OPENSSL_ALL || WOLFSSL_IP_ALT_NAME */ else if (entry->type == ASN_RFC822_TYPE) { - len = XSNPRINTF(scratch, MAX_WIDTH, "email:%s", - entry->name); + ret = X509PrintEscStr(bio, "email:", entry->name, entry->len); } else if (entry->type == ASN_DIR_TYPE) { - len = X509PrintDirType(scratch, MAX_WIDTH, entry); - if (len == 0) { - /* Nothing in the encoding was printable. Emit a placeholder, - * as the other unsupported entry types do, rather than - * failing the print of the whole certificate. */ - len = XSNPRINTF(scratch, MAX_WIDTH, "DirName:"); - } + ret = X509PrintDirType(bio, entry); } else if (entry->type == ASN_URI_TYPE) { - len = XSNPRINTF(scratch, MAX_WIDTH, "URI:%s", - entry->name); + ret = X509PrintEscStr(bio, "URI:", entry->name, entry->len); } #ifdef WOLFSSL_RID_ALT_NAME else if (entry->type == ASN_RID_TYPE) { @@ -7441,12 +7451,11 @@ static int X509_print_name_entry(WOLFSSL_BIO* bio, ret = WOLFSSL_FAILURE; break; } - if (len >= MAX_WIDTH) { + if ((ret != WOLFSSL_SUCCESS) || (len < 0) || (len >= MAX_WIDTH)) { ret = WOLFSSL_FAILURE; break; } - if (wolfSSL_BIO_write(bio, scratch, (int)XSTRLEN(scratch)) - <= 0) { + if ((len > 0) && (wolfSSL_BIO_write(bio, scratch, len) <= 0)) { ret = WOLFSSL_FAILURE; break; } @@ -8256,7 +8265,8 @@ static int X509PrintName(WOLFSSL_BIO* bio, WOLFSSL_X509_NAME* name, if (wolfSSL_BIO_write(bio, scratch, scratchLen) <= 0) { return WOLFSSL_FAILURE; } - if (wolfSSL_X509_NAME_print_ex(bio, name, 1, 0) <= 0) { + if (wolfSSL_X509_NAME_print_ex(bio, name, 1, + WOLFSSL_ASN1_STRFLGS_ESC_CTRL) <= 0) { return WOLFSSL_FAILURE; } if (wolfSSL_BIO_write(bio, "\n", (int)XSTRLEN("\n")) <= 0) { @@ -8342,14 +8352,17 @@ static int X509PrintReqAttributes(WOLFSSL_BIO* bio, WOLFSSL_X509* x509, WOLFSSL_MSG("No REQ attribute found when expected"); return WOLFSSL_FAILURE; } - if ((scratchLen = XSNPRINTF(scratch, MAX_WIDTH, - "%*s%s%*s:%s\n", indent+4, "", - lName, (NAME_SZ/4)-lNameSz, "", data)) + if (XSNPRINTF(scratch, MAX_WIDTH, + "%*s%s%*s:", indent+4, "", + lName, (NAME_SZ/4)-lNameSz, "") >= MAX_WIDTH) { return WOLFSSL_FAILURE; } - if (wolfSSL_BIO_write(bio, scratch, scratchLen) <= 0) { + if ((X509PrintEscStr(bio, scratch, (const char*)data, + wolfSSL_ASN1_STRING_length( + attr->value->value.asn1_string)) != WOLFSSL_SUCCESS) || + (wolfSSL_BIO_write(bio, "\n", 1) <= 0)) { WOLFSSL_MSG("Error writing REQ attribute"); return WOLFSSL_FAILURE; } @@ -11159,6 +11172,10 @@ int wolfSSL_X509_VERIFY_PARAM_inherit(WOLFSSL_X509_VERIFY_PARAM *to, if (!(ret = wolfSSL_X509_VERIFY_PARAM_set1_host(to, from->hostName, (unsigned int)XSTRLEN(from->hostName)))) return ret; + } + /* host flags */ + if (isOverWrite || + (from->hostFlags != 0 && (to->hostFlags == 0 || isDefault))) { to->hostFlags = from->hostFlags; } /* ip ascii */ diff --git a/tests/api.c b/tests/api.c index f0f30b70181..7a35a5b0f8f 100644 --- a/tests/api.c +++ b/tests/api.c @@ -23794,6 +23794,25 @@ static int test_wolfSSL_GENERAL_NAME_print(void) const char* dirNameStr = "DirName:"; const char* ridStr = "Registered ID:1.2.3.4.5"; + static const struct { + int type; + const char* expect; + } ctrlCases[] = { + { GEN_DNS, "DNS:a\\0D\\0Ab" }, + { GEN_EMAIL, "email:a\\0D\\0Ab" }, + { GEN_URI, "URI:a\\0D\\0Ab" }, + }; + static const struct { + size_t bufSz; + const char* val; + } shortCases[] = { + { 2, "" }, /* "DNS:" cut off */ + { 8, "a\r\nb" }, /* "a\0D\0Ab" cut off */ + }; + BIO* pairW = NULL; + BIO* pairR = NULL; + size_t i; + /* BIO to output */ ExpectNotNull(out = BIO_new(BIO_s_mem())); @@ -24032,6 +24051,8 @@ static int test_wolfSSL_GENERAL_NAME_print(void) ExpectNotNull(dirName = X509_NAME_new()); ExpectIntEQ(X509_NAME_add_entry_by_NID(dirName, NID_commonName, MBSTRING_UTF8, (unsigned char*)"wolfSSLDirNameTest", -1, -1, 0), 1); + ExpectIntEQ(X509_NAME_add_entry_by_NID(dirName, NID_organizationalUnitName, + MBSTRING_UTF8, (unsigned char*)"a\r\nb", -1, -1, 0), 1); if (gn != NULL) { /* Replace the default IA5 string allocated by GENERAL_NAME_new with * the directoryName and take ownership of it. */ @@ -24047,6 +24068,9 @@ static int test_wolfSSL_GENERAL_NAME_print(void) ExpectIntEQ(XSTRNCMP((const char*)outbuf, dirNameStr, XSTRLEN(dirNameStr)), 0); ExpectNotNull(XSTRSTR((const char*)outbuf, "wolfSSLDirNameTest")); + /* Control characters are escaped, not written raw. */ + ExpectNotNull(XSTRSTR((const char*)outbuf, "a\\0D\\0Ab")); + ExpectNull(XSTRSTR((const char*)outbuf, "\n")); /* Duplicating GEN_DIRNAME not supported. */ ExpectNull(dup_gn = GENERAL_NAME_dup(gn)); GENERAL_NAME_free(gn); @@ -24077,6 +24101,39 @@ static int test_wolfSSL_GENERAL_NAME_print(void) GENERAL_NAME_free(gn); gn = NULL; + /* Control characters in DNS, email and URI names are escaped. */ + for (i = 0; i < sizeof(ctrlCases) / sizeof(ctrlCases[0]); i++) { + ExpectNotNull(gn = GENERAL_NAME_new()); + if (gn != NULL) { + gn->type = ctrlCases[i].type; + ExpectIntEQ(ASN1_STRING_set(gn->d.ia5, "a\r\nb", -1), 1); + } + ExpectIntEQ(GENERAL_NAME_print(out, gn), 1); + XMEMSET(outbuf, 0, sizeof(outbuf)); + ExpectIntGT(BIO_read(out, outbuf, sizeof(outbuf) - 1), 0); + ExpectStrEQ((const char*)outbuf, ctrlCases[i].expect); + GENERAL_NAME_free(gn); + gn = NULL; + } + + /* A partial write to the BIO is a failure. */ + for (i = 0; i < sizeof(shortCases) / sizeof(shortCases[0]); i++) { + ExpectIntEQ(BIO_new_bio_pair(&pairW, shortCases[i].bufSz, &pairR, + shortCases[i].bufSz), WOLFSSL_SUCCESS); + ExpectNotNull(gn = GENERAL_NAME_new()); + if (gn != NULL) { + gn->type = GEN_DNS; + ExpectIntEQ(ASN1_STRING_set(gn->d.ia5, shortCases[i].val, -1), 1); + } + ExpectIntEQ(GENERAL_NAME_print(pairW, gn), 0); + GENERAL_NAME_free(gn); + gn = NULL; + BIO_free(pairW); + pairW = NULL; + BIO_free(pairR); + pairR = NULL; + } + BIO_free(out); #endif /* OPENSSL_ALL */ return EXPECT_RESULT(); @@ -31421,6 +31478,8 @@ static int test_wolfSSL_X509_REQ_print(void) const char* csrFileName = "certs/csr.attr.der"; const char* csrExtFileName = "certs/csr.ext.der"; BIO* bio = NULL; + X509_NAME* name = NULL; + char buf[8192]; ExpectTrue((fp = XFOPEN(csrFileName, "rb")) != XBADFILE); ExpectNotNull(req = d2i_X509_REQ_fp(fp, NULL)); @@ -31447,7 +31506,29 @@ static int test_wolfSSL_X509_REQ_print(void) ExpectNotNull(bio = BIO_new(BIO_s_mem())); ExpectIntEQ(wolfSSL_X509_REQ_print(bio, req), WOLFSSL_SUCCESS); ExpectIntEQ(BIO_get_mem_data(bio, NULL), 1889); + BIO_free(bio); + bio = NULL; + /* Control characters in the subject and attributes are escaped. */ + ExpectNotNull(name = X509_NAME_new()); + ExpectIntEQ(X509_NAME_add_entry_by_NID(name, NID_commonName, + MBSTRING_UTF8, (unsigned char*)"a\r\nb", -1, -1, 0), 1); + ExpectIntEQ(X509_REQ_set_subject_name(req, name), WOLFSSL_SUCCESS); + ExpectIntEQ(X509_REQ_add1_attr_by_NID(req, WC_NID_pkcs9_challengePassword, + WOLFSSL_MBSTRING_ASC, (byte*)"c\r\nd", -1), WOLFSSL_SUCCESS); + ExpectIntEQ(X509_REQ_add1_attr_by_NID(req, WC_NID_pkcs9_unstructuredName, + WOLFSSL_MBSTRING_ASC, (byte*)TEST_CTRL_LONG_RAW, -1), + WOLFSSL_SUCCESS); + ExpectNotNull(bio = BIO_new(BIO_s_mem())); + ExpectIntEQ(wolfSSL_X509_REQ_print(bio, req), WOLFSSL_SUCCESS); + ExpectIntGT(test_bio_mem_to_str(bio, buf, (int)sizeof(buf)), 0); + ExpectNotNull(XSTRSTR(buf, "CN=a\\0D\\0Ab")); + ExpectNull(XSTRSTR(buf, "a\r\nb")); + ExpectNotNull(XSTRSTR(buf, ":c\\0D\\0Ad\n")); + ExpectNull(XSTRSTR(buf, "c\r\nd")); + ExpectNotNull(XSTRSTR(buf, ":" TEST_CTRL_LONG_ESC "\n")); + + X509_NAME_free(name); BIO_free(bio); wolfSSL_X509_REQ_free(req); #endif @@ -32543,20 +32624,32 @@ static int test_wolfSSL_X509_print_dir_altname(void) * byte early in the encoding. */ 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x00, /* commonName "Test", which sits after that zero byte. */ - 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x04, 'T', 'e', 's', 't' + 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x04, 'T', 'e', 's', 't', + /* organizationalUnitName with control characters. */ + 0x06, 0x03, 0x55, 0x04, 0x0b, 0x0c, 0x04, 'a', '\r', '\n', 'b' }; /* Shorter than the five bytes the tag scan needs. */ static const char shortDirName[] = { 0x30, 0x00 }; + /* organizationName header for a value too long once escaped. */ + static const char longDirHdr[] = { 0x06, 0x03, 0x55, 0x04, 0x0a, 0x0c, + (char)(sizeof(TEST_CTRL_LONG_RAW) - 1) }; + char longDir[sizeof(longDirHdr) + sizeof(TEST_CTRL_LONG_RAW) - 1]; X509* x509 = NULL; BIO* bio = NULL; char* data = NULL; int len = 0; char buf[8192]; + XMEMCPY(longDir, longDirHdr, sizeof(longDirHdr)); + XMEMCPY(longDir + sizeof(longDirHdr), TEST_CTRL_LONG_RAW, + sizeof(TEST_CTRL_LONG_RAW) - 1); + ExpectNotNull(x509 = X509_load_certificate_file(svrCertFile, WOLFSSL_FILETYPE_PEM)); ExpectIntEQ(wolfSSL_X509_add_altname_ex(x509, dirName, (word32)sizeof( dirName), ASN_DIR_TYPE), WOLFSSL_SUCCESS); + ExpectIntEQ(wolfSSL_X509_add_altname_ex(x509, longDir, (word32)sizeof( + longDir), ASN_DIR_TYPE), WOLFSSL_SUCCESS); ExpectNotNull(bio = BIO_new(BIO_s_mem())); ExpectIntEQ(X509_print(bio, x509), SSL_SUCCESS); @@ -32567,6 +32660,9 @@ static int test_wolfSSL_X509_print_dir_altname(void) XMEMCPY(buf, data, (size_t)len); buf[len] = '\0'; ExpectNotNull(XSTRSTR(buf, "CN=Test")); + ExpectNotNull(XSTRSTR(buf, "OU=a\\0D\\0Ab")); + ExpectNull(XSTRSTR(buf, "a\r\nb")); + ExpectNotNull(XSTRSTR(buf, "O=" TEST_CTRL_LONG_ESC)); } BIO_free(bio); bio = NULL; @@ -32596,14 +32692,91 @@ static int test_wolfSSL_X509_print_dir_altname(void) return EXPECT_RESULT(); } +static int test_wolfSSL_X509_print_altname_ctrl(void) +{ + EXPECT_DECLS; +#if defined(OPENSSL_EXTRA) && !defined(NO_FILESYSTEM) && \ + !defined(NO_RSA) && defined(XSNPRINTF) && !defined(WC_DISABLE_RADIX_ZERO_PAD) + static const struct { + int type; + const char* expect; + const char* expectLong; + } cases[] = { + { ASN_DNS_TYPE, "DNS:a\\0D\\0Ab", "DNS:" TEST_CTRL_LONG_ESC }, + { ASN_RFC822_TYPE, "email:a\\0D\\0Ab", "email:" TEST_CTRL_LONG_ESC }, + { ASN_URI_TYPE, "URI:a\\0D\\0Ab", "URI:" TEST_CTRL_LONG_ESC }, + }; + X509* x509 = NULL; + BIO* bio = NULL; + char buf[8192]; + size_t i; + + ExpectNotNull(x509 = X509_load_certificate_file(svrCertFile, + WOLFSSL_FILETYPE_PEM)); + for (i = 0; i < sizeof(cases) / sizeof(cases[0]); i++) { + ExpectIntEQ(wolfSSL_X509_add_altname(x509, "a\r\nb", cases[i].type), + WOLFSSL_SUCCESS); + ExpectIntEQ(wolfSSL_X509_add_altname(x509, TEST_CTRL_LONG_RAW, + cases[i].type), WOLFSSL_SUCCESS); + } + + ExpectNotNull(bio = BIO_new(BIO_s_mem())); + ExpectIntEQ(X509_print(bio, x509), SSL_SUCCESS); + ExpectIntGT(test_bio_mem_to_str(bio, buf, (int)sizeof(buf)), 0); + for (i = 0; i < sizeof(cases) / sizeof(cases[0]); i++) { + ExpectNotNull(XSTRSTR(buf, cases[i].expect)); + ExpectNotNull(XSTRSTR(buf, cases[i].expectLong)); + } + ExpectNull(XSTRSTR(buf, "a\r\nb")); + + BIO_free(bio); + X509_free(x509); +#endif + return EXPECT_RESULT(); +} + +static int test_wolfSSL_X509_print_name_ctrl(void) +{ + EXPECT_DECLS; +#if defined(OPENSSL_EXTRA) && !defined(NO_FILESYSTEM) && \ + !defined(NO_RSA) && defined(XSNPRINTF) && !defined(WC_DISABLE_RADIX_ZERO_PAD) + X509* x509 = NULL; + X509_NAME* name = NULL; + BIO* bio = NULL; + char buf[8192]; + + ExpectNotNull(x509 = X509_load_certificate_file(svrCertFile, + WOLFSSL_FILETYPE_PEM)); + ExpectNotNull(name = X509_NAME_new()); + ExpectIntEQ(X509_NAME_add_entry_by_NID(name, NID_commonName, + MBSTRING_UTF8, (unsigned char*)"a\r\nb", -1, -1, 0), 1); + ExpectIntEQ(X509_set_subject_name(x509, name), WOLFSSL_SUCCESS); + ExpectIntEQ(X509_set_issuer_name(x509, name), WOLFSSL_SUCCESS); + + ExpectNotNull(bio = BIO_new(BIO_s_mem())); + ExpectIntEQ(X509_print(bio, x509), SSL_SUCCESS); + ExpectIntGT(test_bio_mem_to_str(bio, buf, (int)sizeof(buf)), 0); + ExpectNotNull(XSTRSTR(buf, "Issuer: CN=a\\0D\\0Ab")); + ExpectNotNull(XSTRSTR(buf, "Subject: CN=a\\0D\\0Ab")); + ExpectNull(XSTRSTR(buf, "a\r\nb")); + + BIO_free(bio); + X509_NAME_free(name); + X509_free(x509); +#endif + return EXPECT_RESULT(); +} + static int test_wolfSSL_X509_CRL_print(void) { EXPECT_DECLS; #if defined(OPENSSL_EXTRA) && !defined(NO_CERTS) && defined(HAVE_CRL) && \ !defined(NO_RSA) && !defined(NO_FILESYSTEM) && defined(XSNPRINTF) X509_CRL* crl = NULL; + X509_NAME* name = NULL; BIO *bio = NULL; XFILE fp = XBADFILE; + char buf[8192]; ExpectTrue((fp = XFOPEN("./certs/crl/crl.pem", "rb")) != XBADFILE); ExpectNotNull(crl = (X509_CRL*)PEM_read_X509_CRL(fp, (X509_CRL **)NULL, @@ -32613,7 +32786,21 @@ static int test_wolfSSL_X509_CRL_print(void) ExpectNotNull(bio = BIO_new(BIO_s_mem())); ExpectIntEQ(X509_CRL_print(bio, crl), SSL_SUCCESS); + BIO_free(bio); + bio = NULL; + + /* Control characters in the issuer are escaped. */ + ExpectNotNull(name = X509_NAME_new()); + ExpectIntEQ(X509_NAME_add_entry_by_NID(name, NID_commonName, + MBSTRING_UTF8, (unsigned char*)"a\r\nb", -1, -1, 0), 1); + ExpectIntEQ(X509_CRL_set_issuer_name(crl, name), WOLFSSL_SUCCESS); + ExpectNotNull(bio = BIO_new(BIO_s_mem())); + ExpectIntEQ(X509_CRL_print(bio, crl), SSL_SUCCESS); + ExpectIntGT(test_bio_mem_to_str(bio, buf, (int)sizeof(buf)), 0); + ExpectNotNull(XSTRSTR(buf, "CN=a\\0D\\0Ab")); + ExpectNull(XSTRSTR(buf, "a\r\nb")); + X509_NAME_free(name); X509_CRL_free(crl); BIO_free(bio); #endif @@ -44570,6 +44757,8 @@ TEST_CASE testCases[] = { TEST_DECL(test_wolfSSL_X509_print_basic_constraints), TEST_DECL(test_wolfSSL_X509_print_ext_key_usage), TEST_DECL(test_wolfSSL_X509_print_dir_altname), + TEST_DECL(test_wolfSSL_X509_print_altname_ctrl), + TEST_DECL(test_wolfSSL_X509_print_name_ctrl), TEST_DECL(test_wolfSSL_X509_CRL_print), #endif diff --git a/tests/api/test_ossl_x509_acert.c b/tests/api/test_ossl_x509_acert.c index d1c133055ce..da2b96c391e 100644 --- a/tests/api/test_ossl_x509_acert.c +++ b/tests/api/test_ossl_x509_acert.c @@ -32,6 +32,7 @@ #ifdef OPENSSL_EXTRA #include #endif +#include #include #include @@ -329,6 +330,52 @@ int test_wolfSSL_X509_ACERT_misc_api(void) return EXPECT_RESULT(); } +int test_wolfSSL_X509_ACERT_print_ctrl(void) +{ + EXPECT_DECLS; +#if defined(WOLFSSL_ACERT) && !defined(NO_CERTS) && !defined(NO_RSA) && \ + !defined(NO_FILESYSTEM) && defined(OPENSSL_EXTRA) + /* Holder entityName CN, replaced with one of the same length. */ + static const char holderCn[] = "server.example"; + static const char ctrlCn[] = "server\r\nxample"; + X509_ACERT* x509 = NULL; + BIO* bp = NULL; + DerBuffer* der = NULL; + byte* pem = NULL; + size_t pemSz = 0; + char buf[2048]; + word32 i = 0; + + ExpectIntEQ(load_file("certs/acert/acert_ietf.pem", &pem, &pemSz), 0); + ExpectIntEQ(wc_PemToDer(pem, (long)pemSz, ACERT_TYPE, &der, HEAP_HINT, + NULL, NULL), 0); + if (der != NULL) { + for (i = 0; i + sizeof(holderCn) - 1 <= der->length; i++) { + if (XMEMCMP(der->buffer + i, holderCn, sizeof(holderCn) - 1) == 0) + break; + } + ExpectIntLE(i + sizeof(holderCn) - 1, der->length); + if (EXPECT_SUCCESS()) { + XMEMCPY(der->buffer + i, ctrlCn, sizeof(ctrlCn) - 1); + } + ExpectNotNull(x509 = wolfSSL_X509_ACERT_load_certificate_buffer( + der->buffer, (int)der->length, WOLFSSL_FILETYPE_ASN1)); + } + + ExpectNotNull(bp = BIO_new(BIO_s_mem())); + ExpectIntEQ(X509_ACERT_print(bp, x509), SSL_SUCCESS); + ExpectIntGT(test_bio_mem_to_str(bp, buf, (int)sizeof(buf)), 0); + ExpectNotNull(XSTRSTR(buf, "CN=server\\0D\\0Axample")); + ExpectNull(XSTRSTR(buf, ctrlCn)); + + BIO_free(bp); + X509_ACERT_free(x509); + wc_FreeDer(&der); + XFREE(pem, NULL, DYNAMIC_TYPE_TMP_BUFFER); +#endif + return EXPECT_RESULT(); +} + int test_wolfSSL_X509_ACERT_buffer(void) { EXPECT_DECLS; diff --git a/tests/api/test_ossl_x509_acert.h b/tests/api/test_ossl_x509_acert.h index 6a89f1ccba2..4011fdca91a 100644 --- a/tests/api/test_ossl_x509_acert.h +++ b/tests/api/test_ossl_x509_acert.h @@ -26,6 +26,7 @@ int test_wolfSSL_X509_ACERT_verify(void); int test_wolfSSL_X509_ACERT_misc_api(void); +int test_wolfSSL_X509_ACERT_print_ctrl(void); int test_wolfSSL_X509_ACERT_buffer(void); int test_wolfSSL_X509_ACERT_new_and_sign(void); int test_wolfSSL_X509_ACERT_asn(void); @@ -33,6 +34,7 @@ int test_wolfSSL_X509_ACERT_asn(void); #define TEST_OSSL_X509_ACERT_DECLS \ TEST_DECL_GROUP("ossl_x509_acert", test_wolfSSL_X509_ACERT_verify), \ TEST_DECL_GROUP("ossl_x509_acert", test_wolfSSL_X509_ACERT_misc_api), \ + TEST_DECL_GROUP("ossl_x509_acert", test_wolfSSL_X509_ACERT_print_ctrl), \ TEST_DECL_GROUP("ossl_x509_acert", test_wolfSSL_X509_ACERT_buffer), \ TEST_DECL_GROUP("ossl_x509_acert", test_wolfSSL_X509_ACERT_new_and_sign), \ TEST_DECL_GROUP("ossl_x509_acert", test_wolfSSL_X509_ACERT_new_and_sign) diff --git a/tests/api/test_ossl_x509_vp.c b/tests/api/test_ossl_x509_vp.c index 77ebff00481..6285e897381 100644 --- a/tests/api/test_ossl_x509_vp.c +++ b/tests/api/test_ossl_x509_vp.c @@ -185,6 +185,26 @@ int test_wolfSSL_X509_VERIFY_PARAM(void) ExpectTrue(paramTo->check_time == 22); ExpectIntEQ(paramTo->flags & WOLFSSL_USE_CHECK_TIME, 0); + /* hostFlags are inherited even when the host name is not */ + if ((paramTo != NULL) && (paramFrom != NULL)) { + XMEMSET(paramTo, 0, sizeof(X509_VERIFY_PARAM)); + XMEMSET(paramFrom, 0, sizeof(X509_VERIFY_PARAM)); + } + ExpectIntEQ(X509_VERIFY_PARAM_set1_host(paramTo, testhostName2, + (int)XSTRLEN(testhostName2)), 1); + X509_VERIFY_PARAM_set_hostflags(paramFrom, 0x01); + ExpectIntEQ(X509_VERIFY_PARAM_inherit(paramTo, paramFrom), 1); + ExpectIntEQ(0x01, paramTo->hostFlags); + ExpectIntEQ(0, XSTRNCMP(paramTo->hostName, testhostName2, + (int)XSTRLEN(testhostName2))); + + /* Set hostFlags are only replaced with default or overwrite */ + X509_VERIFY_PARAM_set_hostflags(paramFrom, 0x04); + ExpectIntEQ(X509_VERIFY_PARAM_inherit(paramTo, paramFrom), 1); + ExpectIntEQ(0x01, paramTo->hostFlags); + ExpectIntEQ(X509_VERIFY_PARAM_set1(paramTo, paramFrom), 1); + ExpectIntEQ(0x04, paramTo->hostFlags); + /* test for incorrect parameters */ ExpectIntEQ(X509_VERIFY_PARAM_set_flags(NULL, X509_V_FLAG_CRL_CHECK_ALL), 0); diff --git a/tests/utils.c b/tests/utils.c index a63fb54e975..88dba4c1120 100644 --- a/tests/utils.c +++ b/tests/utils.c @@ -871,6 +871,24 @@ int test_memio_setup(struct test_memio_ctx *ctx, #endif /* HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES_BUILD */ +#if defined(OPENSSL_EXTRA) && !defined(NO_BIO) +/* Copy a memory BIO into buf as a string. Returns length, or -1. */ +int test_bio_mem_to_str(WOLFSSL_BIO* bio, char* buf, int bufSz) +{ + char* data = NULL; + int len = wolfSSL_BIO_get_mem_data(bio, &data); + + buf[0] = '\0'; + if ((data == NULL) || (len <= 0) || (len >= bufSz)) { + return -1; + } + XMEMCPY(buf, data, (size_t)len); + buf[len] = '\0'; + + return len; +} +#endif + #if !defined(NO_FILESYSTEM) && defined(OPENSSL_EXTRA) && \ defined(DEBUG_UNIT_TEST_CERTS) /* Used when debugging name constraint tests. Not static to allow use in diff --git a/tests/utils.h b/tests/utils.h index 3a8d2e6bfb9..4207f581e2a 100644 --- a/tests/utils.h +++ b/tests/utils.h @@ -134,6 +134,17 @@ int test_memio_remove_from_buffer(struct test_memio_ctx *ctx, int client, int of THREAD_RETURN WOLFSSL_THREAD run_wolfssl_server(void* args); void run_wolfssl_client(void* args); +#if defined(OPENSSL_EXTRA) && !defined(NO_BIO) +int test_bio_mem_to_str(WOLFSSL_BIO* bio, char* buf, int bufSz); +#endif + +/* Under 80 bytes as is, over 80 once control characters are escaped. */ +#define TEST_CTRL_LONG_RAW "0123456789\r\n0123456789\r\n0123456789\r\n" \ + "0123456789\r\n0123456789\r\n0123456789" +#define TEST_CTRL_LONG_ESC "0123456789\\0D\\0A0123456789\\0D\\0A" \ + "0123456789\\0D\\0A0123456789\\0D\\0A" \ + "0123456789\\0D\\0A0123456789" + #if !defined(NO_FILESYSTEM) && defined(OPENSSL_EXTRA) && \ defined(DEBUG_UNIT_TEST_CERTS) void DEBUG_WRITE_CERT_X509(WOLFSSL_X509* x509, const char* fileName); diff --git a/wolfssl/openssl/x509.h b/wolfssl/openssl/x509.h index a22a46b4ad6..caed5dddee4 100644 --- a/wolfssl/openssl/x509.h +++ b/wolfssl/openssl/x509.h @@ -77,7 +77,10 @@ WOLFSSL_XN_FLAG_SPC_EQ | \ WOLFSSL_XN_FLAG_FN_LN | \ WOLFSSL_XN_FLAG_FN_ALIGN) -#define WOLFSSL_XN_FLAG_ONELINE (WOLFSSL_XN_FLAG_SEP_CPLUS_SPC | WOLFSSL_XN_FLAG_SPC_EQ | WOLFSSL_XN_FLAG_FN_SN) +#define WOLFSSL_XN_FLAG_ONELINE (WOLFSSL_ASN1_STRFLGS_ESC_CTRL | \ + WOLFSSL_XN_FLAG_SEP_CPLUS_SPC | \ + WOLFSSL_XN_FLAG_SPC_EQ | \ + WOLFSSL_XN_FLAG_FN_SN) #ifndef OPENSSL_COEXIST