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;
 }