implement client certificate verification; cherry-pick from h2o/h2o #2621 up to 73e2b8a
diff --git a/lib/openssl.c b/lib/openssl.c
index c63ba86..2d077cc 100644
--- a/lib/openssl.c
+++ b/lib/openssl.c
@@ -1219,8 +1219,6 @@
X509_STORE_CTX *verify_ctx;
int ret;
- assert(server_name != NULL && "ptls_set_server_name MUST be called");
-
/* verify certificate chain */
if ((verify_ctx = X509_STORE_CTX_new()) == NULL) {
ret = PTLS_ERROR_NO_MEMORY;
@@ -1239,12 +1237,16 @@
}
X509_VERIFY_PARAM_set_purpose(params, is_server ? X509_PURPOSE_SSL_SERVER : X509_PURPOSE_SSL_CLIENT);
X509_VERIFY_PARAM_set_depth(params, 98); /* use the default of OpenSSL 1.0.2 and above; see `man SSL_CTX_set_verify` */
- if (server_name != NULL) {
- if (ptls_server_name_is_ipaddr(server_name)) {
- X509_VERIFY_PARAM_set1_ip_asc(params, server_name);
- } else {
- X509_VERIFY_PARAM_set1_host(params, server_name, 0);
- X509_VERIFY_PARAM_set_hostflags(params, X509_CHECK_FLAG_NO_PARTIAL_WILDCARDS);
+ X509_VERIFY_PARAM_set_flags(params, X509_V_FLAG_PARTIAL_CHAIN);
+ if (!is_server) {
+ assert(server_name != NULL && "ptls_set_server_name MUST be called");
+ if (server_name != NULL) {
+ if (ptls_server_name_is_ipaddr(server_name)) {
+ X509_VERIFY_PARAM_set1_ip_asc(params, server_name);
+ } else {
+ X509_VERIFY_PARAM_set1_host(params, server_name, 0);
+ X509_VERIFY_PARAM_set_hostflags(params, X509_CHECK_FLAG_NO_PARTIAL_WILDCARDS);
+ }
}
}
X509_STORE_CTX_set0_param(verify_ctx, params); /* params will be freed alongside verify_ctx */
diff --git a/t/cli.c b/t/cli.c
index db6bf82..5c540dc 100644
--- a/t/cli.c
+++ b/t/cli.c
@@ -365,6 +365,7 @@
" argument is ignored.\n"
" -u update the traffic key when handshake is complete\n"
" -v verify peer using the default certificates\n"
+ " -V CA-root-file verify peer using the CA Root File\n"
" -y cipher-suite cipher-suite to be used, e.g., aes128gcmsha256 (default:\n"
" all)\n"
" -h print this help\n"
@@ -422,7 +423,7 @@
int family = 0;
const char *raw_pub_key_file = NULL, *cert_location = NULL;
- while ((ch = getopt(argc, argv, "46abBC:c:i:Ik:nN:es:Sr:E:K:l:y:vh")) != -1) {
+ while ((ch = getopt(argc, argv, "46abBC:c:i:Ik:nN:es:Sr:E:K:l:y:vV:h")) != -1) {
switch (ch) {
case '4':
family = AF_INET;
@@ -503,7 +504,10 @@
setup_log_event(&ctx, optarg);
break;
case 'v':
- setup_verify_certificate(&ctx);
+ setup_verify_certificate(&ctx, NULL);
+ break;
+ case 'V':
+ setup_verify_certificate(&ctx, optarg);
break;
case 'N': {
ptls_key_exchange_algorithm_t *algo = NULL;
diff --git a/t/util.h b/t/util.h
index 3890737..0dbbed7 100644
--- a/t/util.h
+++ b/t/util.h
@@ -124,10 +124,32 @@
}
}
-static inline void setup_verify_certificate(ptls_context_t *ctx)
+static inline X509_STORE* init_cert_store(char const *crt_file)
+{
+ int ret = 0;
+ X509_STORE *store = X509_STORE_new();
+
+ if (store != NULL) {
+ X509_LOOKUP *lookup = X509_STORE_add_lookup(store, X509_LOOKUP_file());
+ ret = X509_LOOKUP_load_file(lookup, crt_file, X509_FILETYPE_PEM);
+ if (ret != 1) {
+ fprintf(stderr, "Cannot load store (%s), ret = %d\n",
+ crt_file, ret);
+ X509_STORE_free(store);
+ exit(1);
+ }
+ } else {
+ fprintf(stderr, "Cannot get a new X509 store\n");
+ exit(1);
+ }
+
+ return store;
+}
+
+static inline void setup_verify_certificate(ptls_context_t *ctx, const char *ca_file)
{
static ptls_openssl_verify_certificate_t vc;
- ptls_openssl_init_verify_certificate(&vc, NULL);
+ ptls_openssl_init_verify_certificate(&vc, ca_file != NULL ? init_cert_store(ca_file) : NULL);
ctx->verify_certificate = &vc.super;
}