diff --git a/src/pk.c b/src/pk.c index 7526b4924d9..1dc8c83936a 100644 --- a/src/pk.c +++ b/src/pk.c @@ -6838,7 +6838,7 @@ static void pem_find_pattern(char* pem, int pemLen, int idx, const char* prefix, *start = *len = 0; /* Find prefix part. */ - for (; idx < pemLen - prefixLen; idx++) { + for (; idx <= pemLen - prefixLen; idx++) { if ((pem[idx] == prefix[0]) && (XMEMCMP(pem + idx, prefix, (size_t)prefixLen) == 0)) { idx += prefixLen; @@ -6847,7 +6847,7 @@ static void pem_find_pattern(char* pem, int pemLen, int idx, const char* prefix, } } /* Find postfix part. */ - for (; idx < pemLen - postfixLen; idx++) { + for (; idx <= pemLen - postfixLen; idx++) { if ((pem[idx] == postfix[0]) && (XMEMCMP(pem + idx, postfix, (size_t)postfixLen) == 0)) { *len = idx - *start; @@ -6900,9 +6900,21 @@ static int pem_read_data(char* pem, int pemLen, char **name, char **header, } } if (ret == 0) { - /* Find encryption headers after header. */ + /* Find footer. */ start += nameLen + PEM_HDR_FIN_SZ; - pem_find_pattern(pem, pemLen, start, "\n", "\n\n", &startHdr, &hdrLen); + pem_find_pattern(pem, pemLen, start, PEM_END, PEM_HDR_FIN, &startEnd, + &endLen); + /* Validate header name and footer name are the same. */ + if ((endLen != nameLen) || + (XMEMCMP(*name, pem + startEnd, (size_t)nameLen) != 0)) { + ret = ASN_NO_PEM_HEADER; + } + } + if (ret == 0) { + /* Find encryption headers - bounded by the footer so that a blank line + * after it isn't matched. */ + pem_find_pattern(pem, startEnd - PEM_END_SZ, start, "\n", "\n\n", + &startHdr, &hdrLen); if (hdrLen > 0) { /* Include first of two '\n' characters. */ hdrLen++; @@ -6921,15 +6933,6 @@ static int pem_read_data(char* pem, int pemLen, char **name, char **header, XMEMCPY(*header, pem + startHdr, (size_t)hdrLen); start = startHdr + hdrLen + 1; } - - /* Find footer. */ - pem_find_pattern(pem, pemLen, start, PEM_END, PEM_HDR_FIN, &startEnd, - &endLen); - /* Validate header name and footer name are the same. */ - if ((endLen != nameLen) || - (XMEMCMP(*name, pem + startEnd, (size_t)nameLen) != 0)) { - ret = ASN_NO_PEM_HEADER; - } } if (ret == 0) { unsigned char* der = (unsigned char*)pem; diff --git a/tests/api.c b/tests/api.c index 0b957a57a97..eb48e662335 100644 --- a/tests/api.c +++ b/tests/api.c @@ -29182,6 +29182,17 @@ static int test_wolfSSL_PEM_read(void) EXPECT_DECLS; #if defined(OPENSSL_EXTRA) && !defined(NO_FILESYSTEM) && !defined(NO_BIO) const char* filename = "./certs/server-keyEnc.pem"; + /* Footer's final "-----" is the last byte of the input - no trailing EOL. */ + const char* pemNoEol = + "-----BEGIN TEST-----\n" + "AAECAwQ=\n" + "-----END TEST-----"; + /* Blank line after the footer - must not be taken as an encryption + * header terminator. */ + const char* pemBlankEol = + "-----BEGIN TEST-----\n" + "AAECAwQ=\n" + "-----END TEST-----\n\n"; XFILE fp = XBADFILE; char* name = NULL; char* header = NULL; @@ -29330,10 +29341,39 @@ static int test_wolfSSL_PEM_read(void) ExpectIntEQ(XMEMCMP(out, fileData, fileDataSz), 0); BIO_free(bio); + bio = NULL; XFREE(fileData, NULL, DYNAMIC_TYPE_TMP_BUFFER); XFREE(name, NULL, DYNAMIC_TYPE_TMP_BUFFER); XFREE(header, NULL, DYNAMIC_TYPE_TMP_BUFFER); XFREE(data, NULL, DYNAMIC_TYPE_TMP_BUFFER); + + /* Valid PEM that ends at the last dash of the footer must still parse. */ + name = NULL; + header = NULL; + data = NULL; + ExpectNotNull(bio = BIO_new_mem_buf(pemNoEol, (int)XSTRLEN(pemNoEol))); + ExpectIntEQ(PEM_read_bio(bio, &name, &header, &data, &len), 1); + ExpectIntEQ(XSTRNCMP(name, "TEST", 4), 0); + ExpectIntEQ(len, 5); + BIO_free(bio); + bio = NULL; + XFREE(name, NULL, DYNAMIC_TYPE_TMP_BUFFER); + XFREE(header, NULL, DYNAMIC_TYPE_TMP_BUFFER); + XFREE(data, NULL, DYNAMIC_TYPE_TMP_BUFFER); + + /* Valid PEM ending with a blank line must still parse. */ + name = NULL; + header = NULL; + data = NULL; + ExpectNotNull(bio = BIO_new_mem_buf(pemBlankEol, (int)XSTRLEN(pemBlankEol))); + ExpectIntEQ(PEM_read_bio(bio, &name, &header, &data, &len), 1); + ExpectIntEQ(XSTRNCMP(name, "TEST", 4), 0); + ExpectIntEQ(XSTRLEN(header), 0); + ExpectIntEQ(len, 5); + BIO_free(bio); + XFREE(name, NULL, DYNAMIC_TYPE_TMP_BUFFER); + XFREE(header, NULL, DYNAMIC_TYPE_TMP_BUFFER); + XFREE(data, NULL, DYNAMIC_TYPE_TMP_BUFFER); #endif return EXPECT_RESULT(); }