Skip to content

Commit cac7e81

Browse files
committed
Remove duplicate leaf cert parse in x509 validator
Squashed commit of the following: commit 80e4852 Author: Sam Clark <[email protected]> Date: Wed Aug 30 11:11:11 2023 -0400 clang-format commit 5cb354e Author: Sam Clark <[email protected]> Date: Wed Aug 30 11:04:18 2023 -0400 add test for validating empty cert chain commit 18898dc Author: Sam Clark <[email protected]> Date: Wed Aug 30 10:42:39 2023 -0400 change length validate error to S2N_ERR_DECODE_CERTIFICATE commit ac44a47 Author: Sam Clark <[email protected]> Date: Tue Aug 29 17:04:52 2023 -0400 fixes commit 382077b Author: Sam Clark <[email protected]> Date: Tue Aug 29 16:54:58 2023 -0400 s2n_asn1der_to_public_key_and_type tests commit 3a28208 Author: Sam Clark <[email protected]> Date: Tue Aug 29 15:54:48 2023 -0400 test for checking trailing bytes with multiple certs commit 5a45a57 Author: Sam Clark <[email protected]> Date: Tue Aug 29 11:32:38 2023 -0400 refactor cert parsing functions commit b3bc30a Merge: ff40e48 5d5400a Author: Sam Clark <[email protected]> Date: Tue Aug 29 10:16:34 2023 -0400 Merge branch 'main' into leaf-cert-fix commit ff40e48 Author: Sam Clark <[email protected]> Date: Tue Aug 29 10:15:55 2023 -0400 wip commit 4e6a05a Author: Sam Clark <[email protected]> Date: Fri Aug 25 22:10:22 2023 -0400 expects leaf cert parse even with tls 1.2 commit a6236ba Author: Sam Clark <[email protected]> Date: Thu Aug 24 19:09:42 2023 -0400 wip
1 parent 5d5400a commit cac7e81

File tree

7 files changed

+221
-66
lines changed

7 files changed

+221
-66
lines changed

crypto/s2n_openssl_x509.c

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@
1717

1818
#include "api/s2n.h"
1919

20+
#define S2N_MAX_ALLOWED_CERT_TRAILING_BYTES 3
21+
2022
S2N_CLEANUP_RESULT s2n_openssl_x509_stack_pop_free(STACK_OF(X509) **cert_chain)
2123
{
2224
RESULT_ENSURE_REF(*cert_chain);
@@ -39,3 +41,37 @@ S2N_CLEANUP_RESULT s2n_openssl_asn1_time_free_pointer(ASN1_GENERALIZEDTIME **tim
3941
*time_ptr = NULL;
4042
return S2N_RESULT_OK;
4143
}
44+
45+
S2N_RESULT s2n_openssl_x509_parse(struct s2n_blob *cert_asn1_der, X509 **cert, uint32_t *cert_len)
46+
{
47+
RESULT_ENSURE_REF(cert_asn1_der);
48+
RESULT_ENSURE_REF(cert);
49+
RESULT_ENSURE_REF(cert_len);
50+
51+
const uint8_t *cert_data_ptr = cert_asn1_der->data;
52+
53+
*cert = d2i_X509(NULL, (const unsigned char **) &cert_data_ptr, cert_asn1_der->size);
54+
RESULT_ENSURE(*cert, S2N_ERR_CERT_INVALID);
55+
56+
/* If cert parsing is successful, d2i_X509 increments *cert_data_ptr to the byte following the
57+
* parsed data.
58+
*/
59+
*cert_len = cert_data_ptr - cert_asn1_der->data;
60+
61+
return S2N_RESULT_OK;
62+
}
63+
64+
S2N_RESULT s2n_openssl_x509_validate_length(struct s2n_blob *cert_asn1_der, uint32_t cert_len)
65+
{
66+
RESULT_ENSURE_REF(cert_asn1_der);
67+
68+
RESULT_ENSURE_GTE(cert_asn1_der->size, cert_len);
69+
70+
/* Some asn1-encoded certificates contain extraneous trailing bytes. s2n-tls permits some
71+
* number of trailing bytes for compatibility with these certificates.
72+
*/
73+
uint32_t trailing_bytes = cert_asn1_der->size - cert_len;
74+
RESULT_ENSURE(trailing_bytes <= S2N_MAX_ALLOWED_CERT_TRAILING_BYTES, S2N_ERR_DECODE_CERTIFICATE);
75+
76+
return S2N_RESULT_OK;
77+
}

crypto/s2n_openssl_x509.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,14 @@
1919
#include <openssl/x509.h>
2020
#include <stdint.h>
2121

22+
#include "utils/s2n_blob.h"
2223
#include "utils/s2n_safety.h"
2324

2425
DEFINE_POINTER_CLEANUP_FUNC(X509 *, X509_free);
2526

2627
S2N_CLEANUP_RESULT s2n_openssl_x509_stack_pop_free(STACK_OF(X509) **cert_chain);
2728

2829
S2N_CLEANUP_RESULT s2n_openssl_asn1_time_free_pointer(ASN1_GENERALIZEDTIME **time);
30+
31+
S2N_RESULT s2n_openssl_x509_parse(struct s2n_blob *cert_asn1_der, X509 **cert, uint32_t *cert_len);
32+
S2N_RESULT s2n_openssl_x509_validate_length(struct s2n_blob *cert_asn1_der, uint32_t cert_len);

crypto/s2n_pkey.c

Lines changed: 25 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,6 @@
2424
#include "utils/s2n_result.h"
2525
#include "utils/s2n_safety.h"
2626

27-
#define S2N_MAX_ALLOWED_CERT_TRAILING_BYTES 3
28-
2927
int s2n_pkey_zero_init(struct s2n_pkey *pkey)
3028
{
3129
pkey->pkey = NULL;
@@ -198,59 +196,54 @@ int s2n_asn1der_to_private_key(struct s2n_pkey *priv_key, struct s2n_blob *asn1d
198196

199197
int s2n_asn1der_to_public_key_and_type(struct s2n_pkey *pub_key, s2n_pkey_type *pkey_type_out, struct s2n_blob *asn1der)
200198
{
201-
uint8_t *cert_to_parse = asn1der->data;
199+
POSIX_ENSURE_REF(pub_key);
200+
POSIX_ENSURE_REF(pkey_type_out);
201+
POSIX_ENSURE_REF(asn1der);
202+
202203
DEFER_CLEANUP(X509 *cert = NULL, X509_free_pointer);
204+
uint32_t cert_len = 0;
205+
POSIX_GUARD_RESULT(s2n_openssl_x509_parse(asn1der, &cert, &cert_len));
206+
POSIX_GUARD_RESULT(s2n_openssl_x509_validate_length(asn1der, cert_len));
203207

204-
cert = d2i_X509(NULL, (const unsigned char **) (void *) &cert_to_parse, asn1der->size);
205-
S2N_ERROR_IF(cert == NULL, S2N_ERR_DECODE_CERTIFICATE);
208+
POSIX_GUARD_RESULT(s2n_pkey_x509_to_public_key_and_type(cert, pub_key, pkey_type_out));
206209

207-
/* If cert parsing is successful, d2i_X509 increments *cert_to_parse to the byte following the parsed data */
208-
uint32_t parsed_len = cert_to_parse - asn1der->data;
210+
return S2N_SUCCESS;
211+
}
209212

210-
/* Some TLS clients in the wild send extra trailing bytes after the Certificate.
211-
* Allow this in s2n for backwards compatibility with existing clients. */
212-
uint32_t trailing_bytes = asn1der->size - parsed_len;
213-
POSIX_ENSURE(trailing_bytes <= S2N_MAX_ALLOWED_CERT_TRAILING_BYTES, S2N_ERR_DECODE_CERTIFICATE);
213+
S2N_RESULT s2n_pkey_x509_to_public_key_and_type(X509 *cert, struct s2n_pkey *pub_key, s2n_pkey_type *pkey_type_out)
214+
{
215+
RESULT_ENSURE_REF(cert);
216+
RESULT_ENSURE_REF(pub_key);
217+
RESULT_ENSURE_REF(pkey_type_out);
214218

215219
DEFER_CLEANUP(EVP_PKEY *evp_public_key = X509_get_pubkey(cert), EVP_PKEY_free_pointer);
216-
S2N_ERROR_IF(evp_public_key == NULL, S2N_ERR_DECODE_CERTIFICATE);
220+
RESULT_ENSURE(evp_public_key, S2N_ERR_DECODE_CERTIFICATE);
217221

218222
/* Check for success in decoding certificate according to type */
219223
int type = EVP_PKEY_base_id(evp_public_key);
220224

221-
int ret;
222225
switch (type) {
223226
case EVP_PKEY_RSA:
224-
ret = s2n_rsa_pkey_init(pub_key);
225-
if (ret != 0) {
226-
break;
227-
}
228-
ret = s2n_evp_pkey_to_rsa_public_key(&pub_key->key.rsa_key, evp_public_key);
227+
RESULT_GUARD_POSIX(s2n_rsa_pkey_init(pub_key));
228+
RESULT_GUARD_POSIX(s2n_evp_pkey_to_rsa_public_key(&pub_key->key.rsa_key, evp_public_key));
229229
*pkey_type_out = S2N_PKEY_TYPE_RSA;
230230
break;
231231
case EVP_PKEY_RSA_PSS:
232-
ret = s2n_rsa_pss_pkey_init(pub_key);
233-
if (ret != 0) {
234-
break;
235-
}
236-
ret = s2n_evp_pkey_to_rsa_pss_public_key(&pub_key->key.rsa_key, evp_public_key);
232+
RESULT_GUARD_POSIX(s2n_rsa_pss_pkey_init(pub_key));
233+
RESULT_GUARD_POSIX(s2n_evp_pkey_to_rsa_pss_public_key(&pub_key->key.rsa_key, evp_public_key));
237234
*pkey_type_out = S2N_PKEY_TYPE_RSA_PSS;
238235
break;
239236
case EVP_PKEY_EC:
240-
ret = s2n_ecdsa_pkey_init(pub_key);
241-
if (ret != 0) {
242-
break;
243-
}
244-
ret = s2n_evp_pkey_to_ecdsa_public_key(&pub_key->key.ecdsa_key, evp_public_key);
237+
RESULT_GUARD_POSIX(s2n_ecdsa_pkey_init(pub_key));
238+
RESULT_GUARD_POSIX(s2n_evp_pkey_to_ecdsa_public_key(&pub_key->key.ecdsa_key, evp_public_key));
245239
*pkey_type_out = S2N_PKEY_TYPE_ECDSA;
246240
break;
247241
default:
248-
POSIX_BAIL(S2N_ERR_DECODE_CERTIFICATE);
242+
RESULT_BAIL(S2N_ERR_DECODE_CERTIFICATE);
249243
}
250244

251245
pub_key->pkey = evp_public_key;
252-
/* Reset to avoid DEFER_CLEANUP freeing our key */
253-
evp_public_key = NULL;
246+
ZERO_TO_DISABLE_DEFER_CLEANUP(evp_public_key);
254247

255-
return ret;
248+
return S2N_RESULT_OK;
256249
}

crypto/s2n_pkey.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,4 +70,5 @@ int s2n_pkey_match(const struct s2n_pkey *pub_key, const struct s2n_pkey *priv_k
7070
int s2n_pkey_free(struct s2n_pkey *pkey);
7171

7272
int s2n_asn1der_to_private_key(struct s2n_pkey *priv_key, struct s2n_blob *asn1der, int type_hint);
73-
int s2n_asn1der_to_public_key_and_type(struct s2n_pkey *pub_key, s2n_pkey_type *pkey_type, struct s2n_blob *asn1der);
73+
int s2n_asn1der_to_public_key_and_type(struct s2n_pkey *pub_key, s2n_pkey_type *pkey_type_out, struct s2n_blob *asn1der);
74+
S2N_RESULT s2n_pkey_x509_to_public_key_and_type(X509 *cert, struct s2n_pkey *pub_key, s2n_pkey_type *pkey_type_out);

tests/unit/s2n_pkey_test.c

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@
1717
#include "s2n_test.h"
1818
#include "testlib/s2n_testlib.h"
1919

20+
S2N_RESULT s2n_x509_validator_read_asn1_cert(struct s2n_stuffer *cert_chain_in_stuffer, struct s2n_blob *asn1_cert);
21+
2022
int main(int argc, char **argv)
2123
{
2224
BEGIN_TEST();
@@ -167,5 +169,53 @@ int main(int argc, char **argv)
167169
}
168170
};
169171

172+
/* s2n_asn1der_to_public_key_and_type tests */
173+
{
174+
/* A certificate with one trailing byte is parsed successfully */
175+
{
176+
uint8_t cert_chain_data[S2N_MAX_TEST_PEM_SIZE] = { 0 };
177+
uint32_t cert_chain_len = 0;
178+
EXPECT_SUCCESS(s2n_read_test_pem_and_len(S2N_ONE_TRAILING_BYTE_CERT_BIN, cert_chain_data, &cert_chain_len,
179+
S2N_MAX_TEST_PEM_SIZE));
180+
181+
struct s2n_blob cert_chain_blob = { 0 };
182+
EXPECT_SUCCESS(s2n_blob_init(&cert_chain_blob, cert_chain_data, cert_chain_len));
183+
DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free);
184+
EXPECT_SUCCESS(s2n_stuffer_init_written(&cert_chain_stuffer, &cert_chain_blob));
185+
186+
struct s2n_blob cert_asn1_der = { 0 };
187+
EXPECT_OK(s2n_x509_validator_read_asn1_cert(&cert_chain_stuffer, &cert_asn1_der));
188+
189+
DEFER_CLEANUP(struct s2n_pkey public_key = { 0 }, s2n_pkey_free);
190+
EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key));
191+
s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN;
192+
193+
EXPECT_SUCCESS(s2n_asn1der_to_public_key_and_type(&public_key, &pkey_type, &cert_asn1_der));
194+
}
195+
196+
/* A certificate with too many trailing bytes errors */
197+
{
198+
uint8_t cert_chain_data[S2N_MAX_TEST_PEM_SIZE] = { 0 };
199+
uint32_t cert_chain_len = 0;
200+
EXPECT_SUCCESS(s2n_read_test_pem_and_len(S2N_FOUR_TRAILING_BYTE_CERT_BIN, cert_chain_data, &cert_chain_len,
201+
S2N_MAX_TEST_PEM_SIZE));
202+
203+
struct s2n_blob cert_chain_blob = { 0 };
204+
EXPECT_SUCCESS(s2n_blob_init(&cert_chain_blob, cert_chain_data, cert_chain_len));
205+
DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free);
206+
EXPECT_SUCCESS(s2n_stuffer_init_written(&cert_chain_stuffer, &cert_chain_blob));
207+
208+
struct s2n_blob cert_asn1_der = { 0 };
209+
EXPECT_OK(s2n_x509_validator_read_asn1_cert(&cert_chain_stuffer, &cert_asn1_der));
210+
211+
DEFER_CLEANUP(struct s2n_pkey public_key = { 0 }, s2n_pkey_free);
212+
EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key));
213+
s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN;
214+
215+
EXPECT_FAILURE_WITH_ERRNO(s2n_asn1der_to_public_key_and_type(&public_key, &pkey_type, &cert_asn1_der),
216+
S2N_ERR_DECODE_CERTIFICATE);
217+
}
218+
}
219+
170220
END_TEST();
171221
}

tests/unit/s2n_x509_validator_test.c

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1812,6 +1812,47 @@ int main(int argc, char **argv)
18121812
s2n_x509_validator_wipe(&validator);
18131813
};
18141814

1815+
/* Ensure that certs after the leaf cert can have an arbitrary number of trailing bytes */
1816+
{
1817+
DEFER_CLEANUP(struct s2n_x509_validator validator = { 0 }, s2n_x509_validator_wipe);
1818+
EXPECT_SUCCESS(s2n_x509_validator_init_no_x509_validation(&validator));
1819+
1820+
DEFER_CLEANUP(struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free);
1821+
EXPECT_NOT_NULL(connection);
1822+
1823+
DEFER_CLEANUP(struct s2n_stuffer one_trailing_byte_chain = { 0 }, s2n_stuffer_free);
1824+
EXPECT_SUCCESS(read_file(&one_trailing_byte_chain, S2N_ONE_TRAILING_BYTE_CERT_BIN, S2N_MAX_TEST_PEM_SIZE));
1825+
uint32_t one_trailing_byte_chain_len = s2n_stuffer_data_available(&one_trailing_byte_chain);
1826+
uint8_t *one_trailing_byte_chain_data = s2n_stuffer_raw_read(&one_trailing_byte_chain,
1827+
one_trailing_byte_chain_len);
1828+
1829+
DEFER_CLEANUP(struct s2n_stuffer four_trailing_bytes_chain = { 0 }, s2n_stuffer_free);
1830+
EXPECT_SUCCESS(read_file(&four_trailing_bytes_chain, S2N_FOUR_TRAILING_BYTE_CERT_BIN, S2N_MAX_TEST_PEM_SIZE));
1831+
uint32_t four_trailing_bytes_chain_len = s2n_stuffer_data_available(&four_trailing_bytes_chain);
1832+
uint8_t *four_trailing_bytes_chain_data = s2n_stuffer_raw_read(&four_trailing_bytes_chain,
1833+
four_trailing_bytes_chain_len);
1834+
1835+
DEFER_CLEANUP(struct s2n_stuffer chain_stuffer = { 0 }, s2n_stuffer_free);
1836+
EXPECT_SUCCESS(s2n_stuffer_alloc(&chain_stuffer, S2N_MAX_TEST_PEM_SIZE * 2));
1837+
EXPECT_SUCCESS(s2n_stuffer_write_bytes(&chain_stuffer, one_trailing_byte_chain_data,
1838+
one_trailing_byte_chain_len));
1839+
EXPECT_SUCCESS(s2n_stuffer_write_bytes(&chain_stuffer, four_trailing_bytes_chain_data,
1840+
four_trailing_bytes_chain_len));
1841+
1842+
uint32_t chain_len = s2n_stuffer_data_available(&chain_stuffer);
1843+
EXPECT_TRUE(chain_len > 0);
1844+
uint8_t *chain_data = s2n_stuffer_raw_read(&chain_stuffer, chain_len);
1845+
1846+
DEFER_CLEANUP(struct s2n_pkey public_key = { 0 }, s2n_pkey_free);
1847+
EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key));
1848+
s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN;
1849+
1850+
EXPECT_OK(s2n_x509_validator_validate_cert_chain(&validator, connection, chain_data, chain_len, &pkey_type,
1851+
&public_key));
1852+
1853+
EXPECT_EQUAL(sk_X509_num(validator.cert_chain_from_wire), 2);
1854+
};
1855+
18151856
/* Test validator trusts a SHA-1 signature in a certificate chain if certificate validation is off */
18161857
{
18171858
struct s2n_x509_trust_store trust_store;
@@ -1898,6 +1939,23 @@ int main(int argc, char **argv)
18981939
s2n_x509_trust_store_wipe(&trust_store);
18991940
};
19001941

1942+
/* Validator fails if cert chain is empty */
1943+
{
1944+
DEFER_CLEANUP(struct s2n_x509_validator validator = { 0 }, s2n_x509_validator_wipe);
1945+
EXPECT_SUCCESS(s2n_x509_validator_init_no_x509_validation(&validator));
1946+
1947+
DEFER_CLEANUP(struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free);
1948+
EXPECT_NOT_NULL(connection);
1949+
1950+
struct s2n_pkey public_key = { 0 };
1951+
EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key));
1952+
s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN;
1953+
1954+
EXPECT_ERROR_WITH_ERRNO(s2n_x509_validator_validate_cert_chain(&validator, connection, NULL, 0, &pkey_type,
1955+
&public_key),
1956+
S2N_ERR_NO_CERT_FOUND);
1957+
}
1958+
19011959
/* Test trust store can be wiped */
19021960
{
19031961
/* Wipe new s2n_config, which is initialized with certs from the system default locations. */

0 commit comments

Comments
 (0)