From 2e21539b2b57df9926d165243efb60480f546ba7 Mon Sep 17 00:00:00 2001 From: "Dr. Stephen Henson" Date: Wed, 15 Mar 2017 16:07:07 +0000 Subject: [PATCH] Add ExpectedClientCANames Add ExpectedClientCANames: for client auth this checks to see if the list of certificate authorities supplied by the server matches the expected value. Reviewed-by: Richard Levitte (Merged from https://github.com/openssl/openssl/pull/2969) --- test/README.ssltest.md | 4 +++ test/build.info | 2 +- test/handshake_helper.c | 8 ++++++ test/handshake_helper.h | 2 ++ test/ssl_test.c | 57 +++++++++++++++++++++++++++++++++++++++++ test/ssl_test_ctx.c | 18 +++++++++++++ test/ssl_test_ctx.h | 2 ++ 7 files changed, 92 insertions(+), 1 deletion(-) diff --git a/test/README.ssltest.md b/test/README.ssltest.md index a32696723d..0d6f4660cb 100644 --- a/test/README.ssltest.md +++ b/test/README.ssltest.md @@ -98,6 +98,10 @@ handshake. * ExpectedServerSignType, ExpectedClientSignType - the expected signature type used by server or client when signing messages +* ExpectedClientCANames - for client auth list of CA names the server must + send. If this is "empty" the list is expected to be empty otherwise it + is a file of certificates whose subject names form the list. + ## Configuring the client and server The client and server configurations can be any valid `SSL_CTX` diff --git a/test/build.info b/test/build.info index 104d3a5532..9f2f950aa4 100644 --- a/test/build.info +++ b/test/build.info @@ -236,7 +236,7 @@ IF[{- !$disabled{tests} -}] SOURCE[ssl_test_ctx_test]=ssl_test_ctx_test.c ssl_test_ctx.c testutil.c test_main_custom.c INCLUDE[ssl_test_ctx_test]=.. ../include - DEPEND[ssl_test_ctx_test]=../libcrypto + DEPEND[ssl_test_ctx_test]=../libcrypto ../libssl SOURCE[ssl_test]=ssl_test.c ssl_test_ctx.c testutil.c handshake_helper.c test_main_custom.c INCLUDE[ssl_test]=.. ../include diff --git a/test/handshake_helper.c b/test/handshake_helper.c index 30fd479837..4bccac1d4e 100644 --- a/test/handshake_helper.c +++ b/test/handshake_helper.c @@ -34,6 +34,7 @@ void HANDSHAKE_RESULT_free(HANDSHAKE_RESULT *result) OPENSSL_free(result->server_npn_negotiated); OPENSSL_free(result->client_alpn_negotiated); OPENSSL_free(result->server_alpn_negotiated); + sk_X509_NAME_pop_free(result->client_ca_names, X509_NAME_free); OPENSSL_free(result); } @@ -1122,6 +1123,7 @@ static HANDSHAKE_RESULT *do_handshake_internal( /* API dictates unsigned int rather than size_t. */ unsigned int proto_len = 0; EVP_PKEY *tmp_key; + STACK_OF(X509_NAME) *names; memset(&server_ctx_data, 0, sizeof(server_ctx_data)); memset(&server2_ctx_data, 0, sizeof(server2_ctx_data)); @@ -1295,6 +1297,12 @@ static HANDSHAKE_RESULT *do_handshake_internal( SSL_get_peer_signature_type_nid(client.ssl, &ret->server_sign_type); SSL_get_peer_signature_type_nid(server.ssl, &ret->client_sign_type); + names = SSL_get_client_CA_list(client.ssl); + if (names == NULL) + ret->client_ca_names = NULL; + else + ret->client_ca_names = SSL_dup_CA_list(names); + ret->server_cert_type = peer_pkey_type(client.ssl); ret->client_cert_type = peer_pkey_type(server.ssl); diff --git a/test/handshake_helper.h b/test/handshake_helper.h index 1f079c932b..a7df5845de 100644 --- a/test/handshake_helper.h +++ b/test/handshake_helper.h @@ -58,6 +58,8 @@ typedef struct handshake_result { int client_sign_hash; /* client signature type */ int client_sign_type; + /* Client CA names */ + STACK_OF(X509_NAME) *client_ca_names; } HANDSHAKE_RESULT; HANDSHAKE_RESULT *HANDSHAKE_RESULT_new(void); diff --git a/test/ssl_test.c b/test/ssl_test.c index 948eb17d78..387f3a6557 100644 --- a/test/ssl_test.c +++ b/test/ssl_test.c @@ -255,6 +255,62 @@ static int check_client_sign_type(HANDSHAKE_RESULT *result, result->client_sign_type); } +static void print_ca_names(STACK_OF(X509_NAME) *names) +{ + BIO *err; + int i; + + if (names == NULL || sk_X509_NAME_num(names) == 0) { + fprintf(stderr, " \n"); + return; + } + err = BIO_new_fp(stderr, BIO_NOCLOSE); + for (i = 0; i < sk_X509_NAME_num(names); i++) { + X509_NAME_print_ex(err, sk_X509_NAME_value(names, i), 4, + XN_FLAG_ONELINE); + BIO_puts(err, "\n"); + } + BIO_free(err); +} + +static int check_ca_names(const char *name, + STACK_OF(X509_NAME) *expected_names, + STACK_OF(X509_NAME) *names) +{ + int i; + + if (expected_names == NULL) + return 1; + if (names == NULL || sk_X509_NAME_num(names) == 0) { + if (sk_X509_NAME_num(expected_names) == 0) + return 1; + goto err; + } + if (sk_X509_NAME_num(names) != sk_X509_NAME_num(expected_names)) + goto err; + for (i = 0; i < sk_X509_NAME_num(names); i++) { + if (X509_NAME_cmp(sk_X509_NAME_value(names, i), + sk_X509_NAME_value(expected_names, i)) != 0) { + goto err; + } + } + return 1; + err: + fprintf(stderr, "%s: list mismatch\nExpected Names:\n", name); + print_ca_names(expected_names); + fprintf(stderr, "Received Names:\n"); + print_ca_names(names); + return 0; +} + +static int check_client_ca_names(HANDSHAKE_RESULT *result, + SSL_TEST_CTX *test_ctx) +{ + return check_ca_names("Client CA names", + test_ctx->expected_client_ca_names, + result->client_ca_names); +} + /* * This could be further simplified by constructing an expected * HANDSHAKE_RESULT, and implementing comparison methods for @@ -283,6 +339,7 @@ static int check_test(HANDSHAKE_RESULT *result, SSL_TEST_CTX *test_ctx) ret &= check_client_cert_type(result, test_ctx); ret &= check_client_sign_hash(result, test_ctx); ret &= check_client_sign_type(result, test_ctx); + ret &= check_client_ca_names(result, test_ctx); } return ret; } diff --git a/test/ssl_test_ctx.c b/test/ssl_test_ctx.c index 3e3be9e058..7189777b78 100644 --- a/test/ssl_test_ctx.c +++ b/test/ssl_test_ctx.c @@ -535,6 +535,22 @@ __owur static int parse_expected_client_sign_hash(SSL_TEST_CTX *test_ctx, value); } +__owur static int parse_expected_ca_names(STACK_OF(X509_NAME) **pnames, + const char *value) +{ + if (value == NULL) + return 0; + if (!strcmp(value, "empty")) + *pnames = sk_X509_NAME_new_null(); + else + *pnames = SSL_load_client_CA_file(value); + return *pnames != NULL; +} +__owur static int parse_expected_client_ca_names(SSL_TEST_CTX *test_ctx, + const char *value) +{ + return parse_expected_ca_names(&test_ctx->expected_client_ca_names, value); +} /* Known test options and their corresponding parse methods. */ @@ -567,6 +583,7 @@ static const ssl_test_ctx_option ssl_test_ctx_options[] = { { "ExpectedClientCertType", &parse_expected_client_cert_type }, { "ExpectedClientSignHash", &parse_expected_client_sign_hash }, { "ExpectedClientSignType", &parse_expected_client_sign_type }, + { "ExpectedClientCANames", &parse_expected_client_ca_names }, }; /* Nested client options. */ @@ -644,6 +661,7 @@ void SSL_TEST_CTX_free(SSL_TEST_CTX *ctx) ssl_test_ctx_free_extra_data(ctx); OPENSSL_free(ctx->expected_npn_protocol); OPENSSL_free(ctx->expected_alpn_protocol); + sk_X509_NAME_pop_free(ctx->expected_client_ca_names, X509_NAME_free); OPENSSL_free(ctx); } diff --git a/test/ssl_test_ctx.h b/test/ssl_test_ctx.h index 3d8f72bbe5..0b37b15de9 100644 --- a/test/ssl_test_ctx.h +++ b/test/ssl_test_ctx.h @@ -194,6 +194,8 @@ typedef struct { int expected_client_sign_hash; /* Expected client signature type */ int expected_client_sign_type; + /* Expected CA names for client auth */ + STACK_OF(X509_NAME) *expected_client_ca_names; } SSL_TEST_CTX; const char *ssl_test_result_name(ssl_test_result_t result); -- 2.25.1