Skip to content

Commit

Permalink
Add cert validation callback (aws#4156)
Browse files Browse the repository at this point in the history
  • Loading branch information
goatgoose committed Aug 22, 2023
1 parent 346bd1a commit e04acc3
Show file tree
Hide file tree
Showing 9 changed files with 545 additions and 0 deletions.
1 change: 1 addition & 0 deletions error/s2n_errno.c
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ static const char *no_such_error = "Internal s2n error";
ERR_ENTRY(S2N_ERR_CERT_TYPE_UNSUPPORTED, "Certificate Type is unsupported") \
ERR_ENTRY(S2N_ERR_CERT_INVALID, "Certificate is invalid") \
ERR_ENTRY(S2N_ERR_CERT_MAX_CHAIN_DEPTH_EXCEEDED, "The maximum certificate chain depth has been exceeded") \
ERR_ENTRY(S2N_ERR_CERT_REJECTED, "Certificate failed custom application validation") \
ERR_ENTRY(S2N_ERR_CRL_LOOKUP_FAILED, "No CRL could be found for the corresponding certificate") \
ERR_ENTRY(S2N_ERR_CRL_SIGNATURE, "The signature of the CRL is invalid") \
ERR_ENTRY(S2N_ERR_CRL_ISSUER, "Unable to get the CRL issuer certificate") \
Expand Down
1 change: 1 addition & 0 deletions error/s2n_errno.h
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,7 @@ typedef enum {
S2N_ERR_CERT_TYPE_UNSUPPORTED,
S2N_ERR_CERT_INVALID,
S2N_ERR_CERT_MAX_CHAIN_DEPTH_EXCEEDED,
S2N_ERR_CERT_REJECTED,
S2N_ERR_CRL_LOOKUP_FAILED,
S2N_ERR_CRL_SIGNATURE,
S2N_ERR_CRL_ISSUER,
Expand Down
450 changes: 450 additions & 0 deletions tests/unit/s2n_cert_validation_callback_test.c

Large diffs are not rendered by default.

32 changes: 32 additions & 0 deletions tests/unit/s2n_config_test.c
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,11 @@ static int s2n_test_crl_lookup_cb(struct s2n_crl_lookup *lookup, void *context)
return S2N_SUCCESS;
}

static int s2n_test_cert_validation_cb(struct s2n_connection *conn, struct s2n_cert_validation_info *info, void *context)
{
return S2N_SUCCESS;
}

static int s2n_test_async_pkey_fn(struct s2n_connection *conn, struct s2n_async_pkey_op *op)
{
return S2N_SUCCESS;
Expand Down Expand Up @@ -599,6 +604,33 @@ int main(int argc, char **argv)
EXPECT_EQUAL(config->crl_lookup_ctx, NULL);
};

/* Test s2n_config_set_cert_validation_cb */
{
uint8_t context = 0;
DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free);
EXPECT_NOT_NULL(config);

/* Unset by default */
EXPECT_EQUAL(config->cert_validation_cb, NULL);
EXPECT_EQUAL(config->cert_validation_ctx, NULL);

/* Safety */
EXPECT_FAILURE_WITH_ERRNO(s2n_config_set_cert_validation_cb(NULL, s2n_test_cert_validation_cb, &context),
S2N_ERR_NULL);
EXPECT_SUCCESS(s2n_config_set_cert_validation_cb(config, NULL, &context));
EXPECT_SUCCESS(s2n_config_set_cert_validation_cb(config, s2n_test_cert_validation_cb, NULL));

/* Set */
EXPECT_SUCCESS(s2n_config_set_cert_validation_cb(config, s2n_test_cert_validation_cb, &context));
EXPECT_EQUAL(config->cert_validation_cb, s2n_test_cert_validation_cb);
EXPECT_EQUAL(config->cert_validation_ctx, &context);

/* Unset */
EXPECT_SUCCESS(s2n_config_set_cert_validation_cb(config, NULL, NULL));
EXPECT_EQUAL(config->cert_validation_cb, NULL);
EXPECT_EQUAL(config->cert_validation_ctx, NULL);
};

/* Test s2n_config_set_status_request_type */
for (size_t mode_i = 0; mode_i < s2n_array_len(modes); mode_i++) {
s2n_mode mode = modes[mode_i];
Expand Down
1 change: 1 addition & 0 deletions tls/s2n_alerts.c
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@ static S2N_RESULT s2n_translate_protocol_error_to_alert(int error_code, uint8_t
S2N_NO_ALERT(S2N_ERR_CERT_TYPE_UNSUPPORTED);
S2N_NO_ALERT(S2N_ERR_CERT_INVALID);
S2N_NO_ALERT(S2N_ERR_CERT_MAX_CHAIN_DEPTH_EXCEEDED);
S2N_NO_ALERT(S2N_ERR_CERT_REJECTED);
S2N_NO_ALERT(S2N_ERR_CRL_LOOKUP_FAILED);
S2N_NO_ALERT(S2N_ERR_CRL_SIGNATURE);
S2N_NO_ALERT(S2N_ERR_CRL_ISSUER);
Expand Down
10 changes: 10 additions & 0 deletions tls/s2n_config.c
Original file line number Diff line number Diff line change
Expand Up @@ -1121,3 +1121,13 @@ int s2n_config_set_recv_multi_record(struct s2n_config *config, bool enabled)

return S2N_SUCCESS;
}

int s2n_config_set_cert_validation_cb(struct s2n_config *config, s2n_cert_validation_callback cb, void *ctx)
{
POSIX_ENSURE_REF(config);

config->cert_validation_cb = cb;
config->cert_validation_ctx = ctx;

return S2N_SUCCESS;
}
3 changes: 3 additions & 0 deletions tls/s2n_config.h
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,9 @@ struct s2n_config {
s2n_crl_lookup_callback crl_lookup_cb;
void *crl_lookup_ctx;

s2n_cert_validation_callback cert_validation_cb;
void *cert_validation_ctx;

/* Application supplied callback to resolve domain name conflicts when loading certs. */
s2n_cert_tiebreak_callback cert_tiebreak_cb;

Expand Down
33 changes: 33 additions & 0 deletions tls/s2n_x509_validator.c
Original file line number Diff line number Diff line change
Expand Up @@ -581,6 +581,9 @@ static S2N_RESULT s2n_x509_validator_read_leaf_info(struct s2n_connection *conn,
S2N_RESULT s2n_x509_validator_validate_cert_chain(struct s2n_x509_validator *validator, struct s2n_connection *conn,
uint8_t *cert_chain_in, uint32_t cert_chain_len, s2n_pkey_type *pkey_type, struct s2n_pkey *public_key_out)
{
RESULT_ENSURE_REF(conn);
RESULT_ENSURE_REF(conn->config);

switch (validator->state) {
case INIT:
break;
Expand Down Expand Up @@ -616,6 +619,14 @@ S2N_RESULT s2n_x509_validator_validate_cert_chain(struct s2n_x509_validator *val
RESULT_GUARD_POSIX(s2n_extension_list_process(S2N_EXTENSION_LIST_CERTIFICATE, conn, &first_certificate_extensions));
}

if (conn->config->cert_validation_cb) {
struct s2n_cert_validation_info info = { 0 };
RESULT_ENSURE(conn->config->cert_validation_cb(conn, &info, conn->config->cert_validation_ctx) >= S2N_SUCCESS,
S2N_ERR_CANCELLED);
RESULT_ENSURE(info.finished, S2N_ERR_INVALID_STATE);
RESULT_ENSURE(info.accepted, S2N_ERR_CERT_REJECTED);
}

*public_key_out = public_key;

/* Reset the old struct, so we don't clean up public_key_out */
Expand Down Expand Up @@ -851,3 +862,25 @@ bool s2n_x509_validator_is_cert_chain_validated(const struct s2n_x509_validator
{
return validator && (validator->state == VALIDATED || validator->state == OCSP_VALIDATED);
}

int s2n_cert_validation_accept(struct s2n_cert_validation_info *info)
{
POSIX_ENSURE_REF(info);
POSIX_ENSURE(!info->finished, S2N_ERR_INVALID_STATE);

info->finished = true;
info->accepted = true;

return S2N_SUCCESS;
}

int s2n_cert_validation_reject(struct s2n_cert_validation_info *info)
{
POSIX_ENSURE_REF(info);
POSIX_ENSURE(!info->finished, S2N_ERR_INVALID_STATE);

info->finished = true;
info->accepted = false;

return S2N_SUCCESS;
}
14 changes: 14 additions & 0 deletions tls/s2n_x509_validator.h
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,11 @@ struct s2n_x509_validator {
struct s2n_array *crl_lookup_list;
};

struct s2n_cert_validation_info {
unsigned finished : 1;
unsigned accepted : 1;
};

/** Some libcrypto implementations do not support OCSP validation. Returns 1 if supported, 0 otherwise. */
uint8_t s2n_x509_ocsp_stapling_supported(void);

Expand Down Expand Up @@ -138,3 +143,12 @@ S2N_RESULT s2n_validate_certificate_signature(struct s2n_connection *conn, X509
/* Checks to see if a certificate has a signature algorithm that's in our certificate_signature_preferences list */
S2N_RESULT s2n_validate_sig_scheme_supported(struct s2n_connection *conn, X509 *x509_cert,
const struct s2n_signature_preferences *cert_sig_preferences);

typedef int (*s2n_cert_validation_callback)(struct s2n_connection *conn, struct s2n_cert_validation_info *info,
void *context);

int s2n_config_set_cert_validation_cb(struct s2n_config *config,
s2n_cert_validation_callback cert_validation_cb, void *context);

int s2n_cert_validation_accept(struct s2n_cert_validation_info *info);
int s2n_cert_validation_reject(struct s2n_cert_validation_info *info);

0 comments on commit e04acc3

Please sign in to comment.