On the '1.3.x-r1901040' branch: Merge changes from the 1.3.x branch.


git-svn-id: https://svn.apache.org/repos/asf/serf/branches/1.3.x-r1901040@1909312 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/test/test_context.c b/test/test_context.c
index bbdb245..4e44bbe 100644
--- a/test/test_context.c
+++ b/test/test_context.c
@@ -25,6 +25,8 @@
 #include <apr_strings.h>
 #include <apr_version.h>
 
+#include <openssl/opensslv.h>
+
 #include "serf.h"
 #include "serf_private.h"
 
@@ -1127,34 +1129,90 @@
     return APR_SUCCESS;
 }
 
-static apr_status_t
-ssl_server_cert_cb_expect_failures(void *baton, int failures,
-                                   const serf_ssl_certificate_t *cert)
+static const char *format_cert_failures(int failures, apr_pool_t *pool)
 {
-    test_baton_t *tb = baton;
-    int expected_failures = *(int *)tb->user_baton;
+    const char *str = "";
 
-    tb->result_flags |= TEST_RESULT_SERVERCERTCB_CALLED;
+    if (failures & SERF_SSL_CERT_NOTYETVALID) {
+        str = apr_pstrcat(pool, str, *str ? "|" : "", "CERT_NOTYETVALID", NULL);
+        failures &= ~SERF_SSL_CERT_NOTYETVALID;
+    }
 
-    /* We expect an error from the certificate validation function. */
-    if (failures & expected_failures)
-        return APR_SUCCESS;
+    if (failures & SERF_SSL_CERT_EXPIRED) {
+        str = apr_pstrcat(pool, str, *str ? "|" : "", "CERT_EXPIRED", NULL);
+        failures &= ~SERF_SSL_CERT_EXPIRED;
+    }
+
+    if (failures & SERF_SSL_CERT_UNKNOWNCA) {
+        str = apr_pstrcat(pool, str, *str ? "|" : "", "CERT_UNKNOWNCA", NULL);
+        failures &= ~SERF_SSL_CERT_UNKNOWNCA;
+    }
+
+    if (failures & SERF_SSL_CERT_SELF_SIGNED) {
+        str = apr_pstrcat(pool, str, *str ? "|" : "", "CERT_SELF_SIGNED", NULL);
+        failures &= ~SERF_SSL_CERT_SELF_SIGNED;
+    }
+
+    if (failures & SERF_SSL_CERT_UNKNOWN_FAILURE) {
+        str = apr_pstrcat(pool, str, *str ? "|" : "", "CERT_UNKNOWN_FAILURE", NULL);
+        failures &= ~SERF_SSL_CERT_UNKNOWN_FAILURE;
+    }
+
+    if (failures & SERF_SSL_CERT_REVOKED) {
+        str = apr_pstrcat(pool, str, *str ? "|" : "", "CERT_REVOKED", NULL);
+        failures &= ~SERF_SSL_CERT_REVOKED;
+    }
+
+    if (failures) {
+        serf__log(TEST_VERBOSE, __FILE__, "Unexpected or unknown cert failure\n");
+        abort();
+    }
+
+    if (*str)
+        return str;
     else
-        return SERF_ERROR_ISSUE_IN_TESTSUITE;
+        return "NONE";
 }
 
-static apr_status_t
-ssl_server_cert_cb_expect_allok(void *baton, int failures,
-                                const serf_ssl_certificate_t *cert)
+/* Logs failures in tb->user_baton, for later validation. */
+static apr_status_t ssl_server_cert_cb_log(void *baton, int failures,
+                                           const serf_ssl_certificate_t *cert)
 {
     test_baton_t *tb = baton;
+    const char *cert_str;
+
     tb->result_flags |= TEST_RESULT_SERVERCERTCB_CALLED;
 
-    /* No error expected, certificate is valid. */
-    if (failures)
-        return SERF_ERROR_ISSUE_IN_TESTSUITE;
-    else
-        return APR_SUCCESS;
+    if (cert) {
+        apr_hash_t *subject;
+        const char *common_name;
+        int depth;
+
+        subject = serf_ssl_cert_subject(cert, tb->pool);
+        if (!subject)
+            return SERF_ERROR_ISSUE_IN_TESTSUITE;
+
+        common_name = apr_hash_get(subject, "CN", APR_HASH_KEY_STRING);
+        depth = serf_ssl_cert_depth(cert);
+
+        cert_str = apr_psprintf(tb->pool, "(CN=%s, depth=%d)", common_name, depth);
+    } else {
+        cert_str = "(null)";
+    }
+
+    if (!tb->user_baton)
+        tb->user_baton = "";
+
+    tb->user_baton = apr_pstrcat(
+        tb->pool,
+        tb->user_baton,
+        "cert_cb: "
+        "failures = ", format_cert_failures(failures, tb->pool),
+        ", cert = ", cert_str,
+        "\n",
+        NULL);
+
+    return APR_SUCCESS;
 }
 
 static apr_status_t
@@ -1171,7 +1229,6 @@
     test_baton_t *tb;
     handler_baton_t handler_ctx[1];
     const int num_requests = sizeof(handler_ctx)/sizeof(handler_ctx[0]);
-    int expected_failures;
     apr_status_t status;
     test_server_message_t message_list[] = {
         {CHUNKED_REQUEST(1, "1")},
@@ -1194,20 +1251,34 @@
                                      get_srcdir_file(test_pool, "test/server/serfserverkey.pem"),
                                      server_certs_srcdir(server_cert, test_pool),
                                      NULL, /* no client cert */
-                                     ssl_server_cert_cb_expect_failures,
+                                     ssl_server_cert_cb_log,
                                      test_pool);
     CuAssertIntEquals(tc, APR_SUCCESS, status);
 
-    /* This unknown failures is X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE, 
-       meaning the chain has only the server cert. A good candidate for its
-       own failure code. */
-    expected_failures = SERF_SSL_CERT_UNKNOWNCA;
-    tb->user_baton = &expected_failures;
-
     create_new_request(tb, &handler_ctx[0], "GET", "/", 1);
 
     test_helper_run_requests_expect_ok(tc, tb, num_requests, handler_ctx,
                                        test_pool);
+
+    /* OpenSSL 1.1.1i allows to continue verification for certificates with an
+       unknown CA. See https://github.com/openssl/openssl/issues/11297.
+
+       These unknown failures are X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY
+       and X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE. The second one means that
+       the chain has only the server cert. A good candidate for its own failure
+       code. */
+#if OPENSSL_VERSION_NUMBER >= 0x1010109fL /* >= 1.1.1i */
+    CuAssertStrEquals(tc,
+        "cert_cb: failures = CERT_UNKNOWNCA, cert = (CN=localhost, depth=0)\n"
+        "cert_cb: failures = CERT_UNKNOWNCA, cert = (CN=localhost, depth=0)\n"
+        "cert_cb: failures = NONE, cert = (CN=localhost, depth=0)\n",
+        tb->user_baton);
+#else
+    CuAssertStrEquals(tc,
+        "cert_cb: failures = CERT_UNKNOWNCA, cert = (CN=localhost, depth=0)\n"
+        "cert_cb: failures = CERT_UNKNOWNCA, cert = (CN=localhost, depth=0)\n",
+        tb->user_baton);
+#endif
 }
 
 /* Set up the ssl context with the CA and root CA certificates needed for
@@ -1266,7 +1337,7 @@
                                      get_srcdir_file(test_pool, "test/server/serfserverkey.pem"),
                                      server_certs_srcdir(server_certs, test_pool),
                                      NULL, /* no client cert */
-                                     ssl_server_cert_cb_expect_allok,
+                                     ssl_server_cert_cb_log,
                                      test_pool);
     CuAssertIntEquals(tc, APR_SUCCESS, status);
 
@@ -1274,6 +1345,9 @@
 
     test_helper_run_requests_expect_ok(tc, tb, num_requests, handler_ctx,
                                        test_pool);
+    CuAssertStrEquals(tc,
+        "cert_cb: failures = NONE, cert = (CN=localhost, depth=0)\n",
+        tb->user_baton);
 }
 
 /* Validate that when the application rejects the cert, the context loop
@@ -1366,7 +1440,7 @@
         return status;
 
     serf_ssl_server_cert_chain_callback_set(tb->ssl_context,
-                                            ssl_server_cert_cb_expect_allok,
+                                            ssl_server_cert_cb_log,
                                             cert_chain_cb,
                                             tb);
 
@@ -1400,7 +1474,7 @@
                                      get_srcdir_file(test_pool, "test/server/serfserverkey.pem"),
                                      server_certs_srcdir(server_certs, test_pool),
                                      NULL, /* no client cert */
-                                     ssl_server_cert_cb_expect_allok,
+                                     ssl_server_cert_cb_log,
                                      test_pool);
     CuAssertIntEquals(tc, APR_SUCCESS, status);
 
@@ -1408,8 +1482,9 @@
 
     test_helper_run_requests_expect_ok(tc, tb, num_requests,
                                        handler_ctx, test_pool);
-
-    CuAssertTrue(tc, tb->result_flags & TEST_RESULT_SERVERCERTCB_CALLED);
+    CuAssertStrEquals(tc,
+        "cert_cb: failures = NONE, cert = (CN=localhost, depth=0)\n",
+        tb->user_baton);
     CuAssertTrue(tc, tb->result_flags & TEST_RESULT_SERVERCERTCHAINCB_CALLED);
 }
 
@@ -1442,7 +1517,7 @@
         return status;
 
     serf_ssl_server_cert_chain_callback_set(tb->ssl_context,
-                                            ssl_server_cert_cb_expect_allok,
+                                            ssl_server_cert_cb_log,
                                             cert_chain_all_certs_cb,
                                             tb);
 
@@ -1475,7 +1550,7 @@
                                      get_srcdir_file(test_pool, "test/server/serfserverkey.pem"),
                                      server_certs_srcdir(all_server_certs, test_pool),
                                      NULL, /* no client cert */
-                                     ssl_server_cert_cb_expect_allok,
+                                     ssl_server_cert_cb_log,
                                      test_pool);
     CuAssertIntEquals(tc, APR_SUCCESS, status);
 
@@ -1484,7 +1559,10 @@
     test_helper_run_requests_expect_ok(tc, tb, num_requests,
                                        handler_ctx, test_pool);
 
-    CuAssertTrue(tc, tb->result_flags & TEST_RESULT_SERVERCERTCB_CALLED);
+    CuAssertStrEquals(tc,
+        "cert_cb: failures = CERT_SELF_SIGNED, cert = (CN=Serf Root CA, depth=2)\n"
+        "cert_cb: failures = NONE, cert = (CN=localhost, depth=0)\n",
+        tb->user_baton);
     CuAssertTrue(tc, tb->result_flags & TEST_RESULT_SERVERCERTCHAINCB_CALLED);
 }
 
@@ -1739,7 +1817,6 @@
     test_baton_t *tb;
     handler_baton_t handler_ctx[1];
     const int num_requests = sizeof(handler_ctx)/sizeof(handler_ctx[0]);
-    int expected_failures;
     apr_status_t status;
     test_server_message_t message_list[] = {
         {CHUNKED_REQUEST(1, "1")},
@@ -1764,18 +1841,19 @@
                                      get_srcdir_file(test_pool, "test/server/serfserverkey.pem"),
                                      server_certs_srcdir(expired_server_certs, test_pool),
                                      NULL, /* no client cert */
-                                     ssl_server_cert_cb_expect_failures,
+                                     ssl_server_cert_cb_log,
                                      test_pool);
     CuAssertIntEquals(tc, APR_SUCCESS, status);
 
-    expected_failures = SERF_SSL_CERT_SELF_SIGNED |
-                        SERF_SSL_CERT_EXPIRED;
-    tb->user_baton = &expected_failures;
-
     create_new_request(tb, &handler_ctx[0], "GET", "/", 1);
 
     test_helper_run_requests_expect_ok(tc, tb, num_requests, handler_ctx,
                                        test_pool);
+    CuAssertStrEquals(tc,
+        "cert_cb: failures = CERT_SELF_SIGNED, cert = (CN=Serf Root CA, depth=2)\n"
+        "cert_cb: failures = CERT_EXPIRED, cert = (CN=localhost, depth=0)\n"
+        "cert_cb: failures = CERT_EXPIRED, cert = (CN=localhost, depth=0)\n",
+        tb->user_baton);
 }
 
 /* Validate that the expired certificate is reported as failure in the
@@ -1785,7 +1863,6 @@
     test_baton_t *tb;
     handler_baton_t handler_ctx[1];
     const int num_requests = sizeof(handler_ctx)/sizeof(handler_ctx[0]);
-    int expected_failures;
     apr_status_t status;
     test_server_message_t message_list[] = {
         {CHUNKED_REQUEST(1, "1")},
@@ -1810,18 +1887,19 @@
                                      get_srcdir_file(test_pool, "test/server/serfserverkey.pem"),
                                      server_certs_srcdir(future_server_certs, test_pool),
                                      NULL, /* no client cert */
-                                     ssl_server_cert_cb_expect_failures,
+                                     ssl_server_cert_cb_log,
                                      test_pool);
     CuAssertIntEquals(tc, APR_SUCCESS, status);
 
-    expected_failures = SERF_SSL_CERT_SELF_SIGNED |
-                        SERF_SSL_CERT_NOTYETVALID;
-    tb->user_baton = &expected_failures;
-
     create_new_request(tb, &handler_ctx[0], "GET", "/", 1);
 
     test_helper_run_requests_expect_ok(tc, tb, num_requests, handler_ctx,
                                        test_pool);
+    CuAssertStrEquals(tc,
+        "cert_cb: failures = CERT_SELF_SIGNED, cert = (CN=Serf Root CA, depth=2)\n"
+        "cert_cb: failures = CERT_NOTYETVALID, cert = (CN=localhost, depth=0)\n"
+        "cert_cb: failures = CERT_NOTYETVALID, cert = (CN=localhost, depth=0)\n",
+        tb->user_baton);
 }