Skip to content

Commit da67413

Browse files
authored
Merge pull request #10747 from gasbytes/tls-1-2-ocsp-multi-stapl-check
Reject status_request_v2 ocsp-multi staples that bundle multiple SingleResponses
2 parents 0913436 + e8653a8 commit da67413

7 files changed

Lines changed: 228 additions & 0 deletions

File tree

doc/dox_comments/header_files/ssl.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5681,6 +5681,11 @@ long wolfSSL_set_tlsext_status_ocsp_resp(WOLFSSL *ssl, unsigned char *resp, int
56815681
wolfSSL. The application must not free the buffer after calling this
56825682
function.
56835683

5684+
Each stapled response (one per certificate) must carry exactly one
5685+
SingleResponse. A BasicOCSPResponse bundling more than one SingleResponse
5686+
is rejected by the peer, so responders must supply a separate response per
5687+
certificate rather than combining statuses into a single response.
5688+
56845689
\param ssl The WOLFSSL session.
56855690
\param resp Pointer to the response buffer.
56865691
\param len Length of the response buffer.

src/internal.c

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18467,6 +18467,11 @@ static int DoCertificateStatus(WOLFSSL* ssl, byte* input, word32* inOutIdx,
1846718467
|| (response->single->status->status != CERT_GOOD))
1846818468
ret = BAD_CERTIFICATE_STATUS_ERROR;
1846918469

18470+
/* Bundling more than one SingleResponse inside a single
18471+
* stapled BasicOCSPResponse is not supported. */
18472+
if (ret == 0 && response->single->next != NULL)
18473+
ret = BAD_CERTIFICATE_STATUS_ERROR;
18474+
1847018475
if (ret == 0) {
1847118476
request = (OcspRequest*)TLSX_CSR2_GetRequest(
1847218477
ssl->extensions, status_type, idx);

tests/api.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37085,6 +37085,7 @@ TEST_CASE testCases[] = {
3708537085
TEST_DECL(test_ocsp_certid_dup),
3708637086
TEST_DECL(test_ocsp_resp_find_status_serial_prefix),
3708737087
TEST_DECL(test_ocsp_tls_cert_cb),
37088+
TEST_DECL(test_ocsp_status_request_v2_multi_revoked_single),
3708837089
TEST_DECL(test_ocsp_cert_unknown_crl_fallback),
3708937090
TEST_DECL(test_ocsp_cert_unknown_crl_fallback_nonleaf),
3709037091
TEST_DECL(test_tls13_nonblock_ocsp_low_mfl),

tests/api/create_ocsp_test_blobs.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -436,6 +436,32 @@ def create_bad_response(rd: dict) -> bytes:
436436
'responder_key': WOLFSSL_OCSP_CERT_PATH + 'intermediate1-ca-key.pem',
437437
'name': 'resp_server1_cert'
438438
},
439+
{
440+
# A single, validly-signed BasicOCSPResponse that bundles two
441+
# SingleResponses: the first (wire order) is a benign CERT_GOOD entry
442+
# for an unrelated serial, the second is the server1 leaf cert marked
443+
# CERT_REVOKED.
444+
# Signed by intermediate1, the legitimate issuer and
445+
# authorized responder for server1.
446+
'response_status': 0,
447+
'signature_algorithm': signature_algorithm(),
448+
'responder_by_name': True,
449+
'responder_cert': WOLFSSL_OCSP_CERT_PATH + 'intermediate1-ca-cert.pem',
450+
'responses': [
451+
{
452+
'issuer_cert': WOLFSSL_OCSP_CERT_PATH + 'intermediate1-ca-cert.pem',
453+
'serial': 0x01,
454+
'status': CERT_GOOD
455+
},
456+
{
457+
'issuer_cert': WOLFSSL_OCSP_CERT_PATH + 'intermediate1-ca-cert.pem',
458+
'serial': 0x05,
459+
'status': CERT_REVOKED
460+
}
461+
],
462+
'responder_key': WOLFSSL_OCSP_CERT_PATH + 'intermediate1-ca-key.pem',
463+
'name': 'resp_server1_revoked_good_first'
464+
},
439465
{
440466
# Ancestor-issued responder; rejected by RFC 6960 4.2.2.2 enforcement
441467
'response_status': 0,

tests/api/test_ocsp.c

Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1346,11 +1346,141 @@ int test_ocsp_tls_cert_cb(void)
13461346
return EXPECT_RESULT();
13471347
}
13481348

1349+
#if defined(HAVE_CERTIFICATE_STATUS_REQUEST_V2) && !defined(WOLFSSL_NO_TLS12)
1350+
/* Stapling for a 3-cert chain. The intermediate and root staples are the
1351+
* normal single-SingleResponse CERT_GOOD responses, but the leaf (idx 0)
1352+
* staple is resp_server1_revoked_good_first: one validly-signed
1353+
* BasicOCSPResponse that bundles a benign CERT_GOOD single FIRST and the
1354+
* server1 leaf cert's CERT_REVOKED single SECOND. */
1355+
static int test_ocsp_revoked_multi_single_status_cb(WOLFSSL* ssl, void* ioCtx)
1356+
{
1357+
byte* leaf_resp = NULL;
1358+
byte* int_resp = NULL;
1359+
byte* root_resp = NULL;
1360+
int ret = WOLFSSL_OCSP_STATUS_CB_ALERT_FATAL;
1361+
(void)ioCtx;
1362+
leaf_resp = (byte*)XMALLOC(sizeof(resp_server1_revoked_good_first), NULL,
1363+
DYNAMIC_TYPE_TMP_BUFFER);
1364+
int_resp = (byte*)XMALLOC(sizeof(resp_intermediate1_cert), NULL,
1365+
DYNAMIC_TYPE_TMP_BUFFER);
1366+
root_resp = (byte*)XMALLOC(sizeof(resp_root_ca_cert), NULL,
1367+
DYNAMIC_TYPE_TMP_BUFFER);
1368+
if (leaf_resp != NULL && int_resp != NULL && root_resp != NULL) {
1369+
XMEMCPY(leaf_resp, resp_server1_revoked_good_first,
1370+
sizeof(resp_server1_revoked_good_first));
1371+
XMEMCPY(int_resp, resp_intermediate1_cert,
1372+
sizeof(resp_intermediate1_cert));
1373+
XMEMCPY(root_resp, resp_root_ca_cert, sizeof(resp_root_ca_cert));
1374+
if (wolfSSL_set_tlsext_status_ocsp_resp_multi(ssl, leaf_resp,
1375+
sizeof(resp_server1_revoked_good_first), 0) == WOLFSSL_SUCCESS)
1376+
leaf_resp = NULL;
1377+
if (wolfSSL_set_tlsext_status_ocsp_resp_multi(ssl, int_resp,
1378+
sizeof(resp_intermediate1_cert), 1) == WOLFSSL_SUCCESS)
1379+
int_resp = NULL;
1380+
if (wolfSSL_set_tlsext_status_ocsp_resp_multi(ssl, root_resp,
1381+
sizeof(resp_root_ca_cert), 2) == WOLFSSL_SUCCESS)
1382+
root_resp = NULL;
1383+
if (leaf_resp == NULL && int_resp == NULL && root_resp == NULL)
1384+
ret = WOLFSSL_OCSP_STATUS_CB_OK;
1385+
}
1386+
XFREE(leaf_resp, NULL, DYNAMIC_TYPE_TMP_BUFFER);
1387+
XFREE(int_resp, NULL, DYNAMIC_TYPE_TMP_BUFFER);
1388+
XFREE(root_resp, NULL, DYNAMIC_TYPE_TMP_BUFFER);
1389+
return ret;
1390+
}
1391+
1392+
/*
1393+
* status_request_v2 OCSP-multi must reject a stapled leaf response
1394+
* whose MATCHING SingleResponse is CERT_REVOKED, even when an unrelated
1395+
* CERT_GOOD SingleResponse appears first in the same BasicOCSPResponse.
1396+
*/
1397+
int test_ocsp_status_request_v2_multi_revoked_single(void)
1398+
{
1399+
EXPECT_DECLS;
1400+
size_t i;
1401+
struct {
1402+
method_provider client_meth;
1403+
method_provider server_meth;
1404+
const char* tls_version;
1405+
} params[] = {
1406+
{ wolfTLSv1_2_client_method, wolfTLSv1_2_server_method, "TLSv1_2" },
1407+
#ifdef WOLFSSL_DTLS
1408+
{ wolfDTLSv1_2_client_method, wolfDTLSv1_2_server_method, "DTLSv1_2" },
1409+
#endif
1410+
};
1411+
1412+
for (i = 0; i < XELEM_CNT(params) && !EXPECT_FAIL(); i++) {
1413+
struct test_ssl_memio_ctx test_ctx;
1414+
WOLFSSL_ALERT_HISTORY h;
1415+
1416+
printf("\nTesting %s\n", params[i].tls_version);
1417+
1418+
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
1419+
test_ctx.c_cb.caPemFile = "";
1420+
/* Server cert/key come only from the per-connection cert callback. */
1421+
test_ctx.s_cb.certPemFile = "";
1422+
test_ctx.s_cb.keyPemFile = "";
1423+
test_ctx.c_cb.method = params[i].client_meth;
1424+
test_ctx.s_cb.method = params[i].server_meth;
1425+
1426+
/* Full server1 -> intermediate1 -> root chain so the leaf staple is at
1427+
* idx 0. */
1428+
test_ocsp_tls_cert_cb_opts.chainLen = 3;
1429+
test_ocsp_tls_cert_cb_opts.failStaple = 0;
1430+
test_ctx.s_cb.ctx_ready = test_ocsp_tls_cert_cb_ctx_ready;
1431+
1432+
ExpectIntEQ(test_ssl_memio_setup(&test_ctx), TEST_SUCCESS);
1433+
ExpectIntEQ(wolfSSL_UnloadCertsKeys(test_ctx.s_ssl), WOLFSSL_SUCCESS);
1434+
1435+
/* server: enable stapling and supply the multi-single leaf */
1436+
ExpectIntEQ(wolfSSL_CTX_EnableOCSPStapling(test_ctx.s_ctx),
1437+
WOLFSSL_SUCCESS);
1438+
ExpectIntEQ(wolfSSL_CTX_set_tlsext_status_cb(test_ctx.s_ctx,
1439+
test_ocsp_revoked_multi_single_status_cb), WOLFSSL_SUCCESS);
1440+
1441+
/* client: verify chain via callback, request status_request_v2 multi.
1442+
* Deliberately no ocsp_status_verify_cb: exercise DoCertificateStatus
1443+
* in isolation. */
1444+
wolfSSL_set_verify(test_ctx.c_ssl, WOLFSSL_VERIFY_DEFAULT,
1445+
test_ocsp_tls_cert_cb_verify_cb);
1446+
wolfSSL_SetCertCbCtx(test_ctx.c_ssl, test_ctx.c_ssl);
1447+
ExpectIntEQ(wolfSSL_CTX_EnableOCSPStapling(test_ctx.c_ctx),
1448+
WOLFSSL_SUCCESS);
1449+
ExpectIntEQ(wolfSSL_CTX_EnableOCSPMustStaple(test_ctx.c_ctx),
1450+
WOLFSSL_SUCCESS);
1451+
ExpectIntEQ(wolfSSL_UseOCSPStaplingV2(test_ctx.c_ssl,
1452+
WOLFSSL_CSR2_OCSP_MULTI, WOLFSSL_CSR2_OCSP_USE_NONCE),
1453+
WOLFSSL_SUCCESS);
1454+
1455+
/* Revoked leaf must abort the handshake. */
1456+
ExpectIntEQ(test_ssl_memio_do_handshake(&test_ctx, 10, NULL),
1457+
TEST_FAIL);
1458+
XMEMSET(&h, 0, sizeof(h));
1459+
ExpectIntEQ(wolfSSL_get_alert_history(test_ctx.s_ssl, &h),
1460+
WOLFSSL_SUCCESS);
1461+
ExpectIntEQ(h.last_rx.level, alert_fatal);
1462+
ExpectIntEQ(h.last_rx.code, bad_certificate_status_response);
1463+
1464+
test_ssl_memio_cleanup(&test_ctx);
1465+
}
1466+
return EXPECT_RESULT();
1467+
}
1468+
#else
1469+
int test_ocsp_status_request_v2_multi_revoked_single(void)
1470+
{
1471+
return TEST_SKIPPED;
1472+
}
1473+
#endif /* HAVE_CERTIFICATE_STATUS_REQUEST_V2 && !WOLFSSL_NO_TLS12 */
1474+
13491475
#else /* feature guards */
13501476
int test_ocsp_tls_cert_cb(void)
13511477
{
13521478
return TEST_SKIPPED;
13531479
}
1480+
int test_ocsp_status_request_v2_multi_revoked_single(void)
1481+
{
1482+
return TEST_SKIPPED;
1483+
}
13541484
#endif
13551485

13561486
/*

tests/api/test_ocsp.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ int test_ocsp_basic_verify(void);
3030
int test_ocsp_responder_keyhash_binding(void);
3131
int test_ocsp_response_parsing(void);
3232
int test_ocsp_tls_cert_cb(void);
33+
int test_ocsp_status_request_v2_multi_revoked_single(void);
3334
int test_ocsp_cert_unknown_crl_fallback(void);
3435
int test_ocsp_cert_unknown_crl_fallback_nonleaf(void);
3536
int test_tls13_nonblock_ocsp_low_mfl(void);

tests/api/test_ocsp_test_blobs.h

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2425,4 +2425,64 @@ unsigned char resp_bad[] = {
24252425
0x26, 0x65, 0x60, 0x49, 0xa5, 0x75, 0x2f, 0x02, 0x08,
24262426
};
24272427

2428+
unsigned char resp_server1_revoked_good_first[] = {
2429+
0x30, 0x82, 0x02, 0x9e, 0x0a, 0x01, 0x00, 0xa0, 0x82, 0x02, 0x97, 0x30,
2430+
0x82, 0x02, 0x93, 0x06, 0x09, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x30,
2431+
0x01, 0x01, 0x04, 0x82, 0x02, 0x84, 0x30, 0x82, 0x02, 0x80, 0x30, 0x82,
2432+
0x01, 0x6a, 0xa1, 0x81, 0xa4, 0x30, 0x81, 0xa1, 0x31, 0x0b, 0x30, 0x09,
2433+
0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, 0x31, 0x13, 0x30,
2434+
0x11, 0x06, 0x03, 0x55, 0x04, 0x08, 0x0c, 0x0a, 0x57, 0x61, 0x73, 0x68,
2435+
0x69, 0x6e, 0x67, 0x74, 0x6f, 0x6e, 0x31, 0x10, 0x30, 0x0e, 0x06, 0x03,
2436+
0x55, 0x04, 0x07, 0x0c, 0x07, 0x53, 0x65, 0x61, 0x74, 0x74, 0x6c, 0x65,
2437+
0x31, 0x10, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x0c, 0x07, 0x77,
2438+
0x6f, 0x6c, 0x66, 0x53, 0x53, 0x4c, 0x31, 0x14, 0x30, 0x12, 0x06, 0x03,
2439+
0x55, 0x04, 0x0b, 0x0c, 0x0b, 0x45, 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x65,
2440+
0x72, 0x69, 0x6e, 0x67, 0x31, 0x22, 0x30, 0x20, 0x06, 0x03, 0x55, 0x04,
2441+
0x03, 0x0c, 0x19, 0x77, 0x6f, 0x6c, 0x66, 0x53, 0x53, 0x4c, 0x20, 0x69,
2442+
0x6e, 0x74, 0x65, 0x72, 0x6d, 0x65, 0x64, 0x69, 0x61, 0x74, 0x65, 0x20,
2443+
0x43, 0x41, 0x20, 0x31, 0x31, 0x1f, 0x30, 0x1d, 0x06, 0x09, 0x2a, 0x86,
2444+
0x48, 0x86, 0xf7, 0x0d, 0x01, 0x09, 0x01, 0x16, 0x10, 0x69, 0x6e, 0x66,
2445+
0x6f, 0x40, 0x77, 0x6f, 0x6c, 0x66, 0x73, 0x73, 0x6c, 0x2e, 0x63, 0x6f,
2446+
0x6d, 0x18, 0x0f, 0x32, 0x30, 0x32, 0x36, 0x30, 0x36, 0x32, 0x31, 0x31,
2447+
0x36, 0x34, 0x32, 0x33, 0x36, 0x5a, 0x30, 0x81, 0xaf, 0x30, 0x4d, 0x30,
2448+
0x38, 0x30, 0x07, 0x06, 0x05, 0x2b, 0x0e, 0x03, 0x02, 0x1a, 0x04, 0x14,
2449+
0x71, 0x4d, 0x82, 0x23, 0x40, 0x59, 0xc0, 0x96, 0xa1, 0x37, 0x43, 0xfa,
2450+
0x31, 0xdb, 0xba, 0xb1, 0x43, 0x18, 0xda, 0x04, 0x04, 0x14, 0x83, 0xc6,
2451+
0x3a, 0x89, 0x2c, 0x81, 0xf4, 0x02, 0xd7, 0x9d, 0x4c, 0xe2, 0x2a, 0xc0,
2452+
0x71, 0x82, 0x64, 0x44, 0xda, 0x0e, 0x02, 0x01, 0x01, 0x80, 0x00, 0x18,
2453+
0x0f, 0x32, 0x30, 0x32, 0x36, 0x30, 0x36, 0x32, 0x31, 0x31, 0x36, 0x34,
2454+
0x32, 0x33, 0x36, 0x5a, 0x30, 0x5e, 0x30, 0x38, 0x30, 0x07, 0x06, 0x05,
2455+
0x2b, 0x0e, 0x03, 0x02, 0x1a, 0x04, 0x14, 0x71, 0x4d, 0x82, 0x23, 0x40,
2456+
0x59, 0xc0, 0x96, 0xa1, 0x37, 0x43, 0xfa, 0x31, 0xdb, 0xba, 0xb1, 0x43,
2457+
0x18, 0xda, 0x04, 0x04, 0x14, 0x83, 0xc6, 0x3a, 0x89, 0x2c, 0x81, 0xf4,
2458+
0x02, 0xd7, 0x9d, 0x4c, 0xe2, 0x2a, 0xc0, 0x71, 0x82, 0x64, 0x44, 0xda,
2459+
0x0e, 0x02, 0x01, 0x05, 0xa1, 0x11, 0x18, 0x0f, 0x32, 0x30, 0x32, 0x36,
2460+
0x30, 0x36, 0x32, 0x31, 0x31, 0x36, 0x34, 0x32, 0x33, 0x36, 0x5a, 0x18,
2461+
0x0f, 0x32, 0x30, 0x32, 0x36, 0x30, 0x36, 0x32, 0x31, 0x31, 0x36, 0x34,
2462+
0x32, 0x33, 0x36, 0x5a, 0x30, 0x0b, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86,
2463+
0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x03, 0x82, 0x01, 0x01, 0x00, 0xc4, 0x95,
2464+
0xdb, 0x71, 0xca, 0x09, 0x4b, 0x37, 0xf1, 0xe8, 0x25, 0xc4, 0x75, 0x6f,
2465+
0x87, 0x22, 0x95, 0x8b, 0x7f, 0x7f, 0x00, 0xf0, 0x75, 0x35, 0x66, 0xea,
2466+
0x56, 0x26, 0xc4, 0x9a, 0xe1, 0x40, 0xe9, 0xd2, 0x7c, 0x8c, 0x83, 0x0f,
2467+
0x33, 0x33, 0xac, 0x1e, 0xcc, 0x8e, 0x0e, 0x99, 0x43, 0xb8, 0xd6, 0x13,
2468+
0xfa, 0xdb, 0xbe, 0xaf, 0x64, 0x6a, 0xbd, 0x92, 0x35, 0xb2, 0x7e, 0xaf,
2469+
0xda, 0x49, 0x89, 0xa2, 0x37, 0xcc, 0x50, 0x88, 0xa8, 0x0b, 0xb4, 0xe1,
2470+
0x6a, 0xb7, 0x94, 0x06, 0x92, 0xb8, 0x6a, 0xd2, 0x55, 0xfe, 0x7e, 0xb4,
2471+
0x5e, 0x12, 0xed, 0x9d, 0xf6, 0x04, 0x49, 0x2c, 0x68, 0xae, 0xc4, 0x21,
2472+
0x3c, 0xd4, 0xe4, 0x97, 0x76, 0x2c, 0xdb, 0x78, 0xfb, 0x22, 0xdd, 0x3b,
2473+
0xed, 0x03, 0x68, 0xaa, 0x7b, 0x9d, 0xd2, 0x66, 0x9f, 0x53, 0xc8, 0x45,
2474+
0xe7, 0x8d, 0xeb, 0x39, 0x05, 0x90, 0xbe, 0x9e, 0xe3, 0x3f, 0xcd, 0x4c,
2475+
0x0a, 0x37, 0x3c, 0x18, 0xbb, 0xef, 0x2e, 0xb6, 0x53, 0xda, 0x65, 0xfb,
2476+
0xd6, 0xd4, 0xfa, 0x04, 0xcb, 0x93, 0x23, 0x60, 0x5e, 0x62, 0x8b, 0xd2,
2477+
0x14, 0xe4, 0x14, 0xd3, 0x3f, 0xdd, 0x8b, 0x65, 0x55, 0x95, 0x27, 0x45,
2478+
0xdb, 0x7c, 0x74, 0x88, 0x33, 0x7b, 0x2d, 0x6e, 0x11, 0x41, 0x19, 0xc6,
2479+
0xe2, 0x79, 0x3d, 0x27, 0xc1, 0x5e, 0xb1, 0xcc, 0xf9, 0x6a, 0x9c, 0xaf,
2480+
0x73, 0x78, 0xc5, 0xeb, 0x1b, 0xfa, 0x6f, 0x92, 0x6b, 0x75, 0xf0, 0x70,
2481+
0xc9, 0x44, 0xd2, 0xa8, 0xbe, 0x2e, 0xee, 0x0e, 0x0b, 0xac, 0x7f, 0x99,
2482+
0x6d, 0x67, 0x1e, 0x2d, 0x7d, 0x19, 0x3a, 0x66, 0x1b, 0xb2, 0x0f, 0xfe,
2483+
0x49, 0x89, 0x24, 0xb2, 0x15, 0xa6, 0xcc, 0xd2, 0xdd, 0xc4, 0x45, 0xfb,
2484+
0xd2, 0xe5, 0xeb, 0xe8, 0xd7, 0x09, 0xf1, 0xc0, 0xb8, 0x0d, 0xc8, 0x34,
2485+
0xc2, 0x6c,
2486+
};
2487+
24282488
#endif /* OCSP_TEST_BLOBS_H */

0 commit comments

Comments
 (0)