On the ocsp-verification branch: Implement request constructor,
export, import and acccessors.

* serf_bucket_types.h
  (serf_ssl_ocsp_response_parse): Fix return type.

* buckets/ssl_buckets.c
  (free_ocsp_request): New; pool cleanup function.
  (serf_ssl_ocsp_request_t, serf_ssl_ocsp_response_t): Define types.
  (serf_ssl_ocsp_request_create,
   serf_ssl_ocsp_request_body,
   serf_ssl_ocsp_request_body_size,
   serf_ssl_ocsp_request_export,
   serf_ssl_ocsp_request_import): Implement these functions.
  (serf_ssl_ocsp_response_parse,
   serf_ssl_ocsp_response_verify): Add fake implementations.


git-svn-id: https://svn.apache.org/repos/asf/serf/branches/ocsp-verification@1774473 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/buckets/ssl_buckets.c b/buckets/ssl_buckets.c
index fecb23a..916cb50 100644
--- a/buckets/ssl_buckets.c
+++ b/buckets/ssl_buckets.c
@@ -2614,3 +2614,260 @@
     serf_default_get_remaining,
     serf_ssl_set_config,
 };
+
+
+/*
+ * OCSP bits are here because they depend on OpenSSL and private types
+ * defined in this file.
+ */
+
+struct serf_ssl_ocsp_request_t {
+#ifndef OPENSSL_NO_OCSP
+    /* OpenSSL's internal representation of the OCSP request. */
+    OCSP_REQUEST *request;
+
+    /* DER-encoded request and size. */
+    const void *der_request;
+    apr_size_t der_request_size;
+
+    /* Exported server and issuer certificates. */
+    const char *encoded_server_cert;
+    const char *encoded_issuer_cert;
+#endif  /* OPENSSL_NO_OCSP */
+};
+
+
+#ifndef OPENSSL_NO_OCSP
+static apr_status_t free_ocsp_request(void *data)
+{
+    OCSP_REQUEST_free(data);
+    return APR_SUCCESS;
+}
+#endif  /* OPENSSL_NO_OCSP */
+
+
+serf_ssl_ocsp_request_t *serf_ssl_ocsp_request_create(
+    const serf_ssl_certificate_t *server_cert,
+    const serf_ssl_certificate_t *issuer_cert,
+    int generate_nonce,
+    apr_pool_t *result_pool,
+    apr_pool_t *scratch_pool)
+{
+#ifndef OPENSSL_NO_OCSP
+    X509 *const cert = server_cert->ssl_cert;
+    X509 *const issuer = issuer_cert->ssl_cert;
+
+    serf_ssl_ocsp_request_t *req = NULL;
+    OCSP_REQUEST *ocsp_req = NULL;
+    OCSP_CERTID *cert_id = NULL;
+    unsigned char *unused;
+    void *der;
+    int len;
+
+    if (X509_V_OK != X509_check_issued(issuer, cert))
+        goto cleanup;
+
+    /* TODO: Support other hash algorithms besides the default SHA1. */
+    cert_id = OCSP_cert_to_id(NULL, cert, issuer);
+    if (!cert_id)
+        goto cleanup;
+
+    ocsp_req = OCSP_REQUEST_new();
+    if (!ocsp_req)
+        goto cleanup;
+
+    if (!OCSP_request_add0_id(ocsp_req, cert_id))
+        goto cleanup;
+    cert_id = NULL;
+
+    if (generate_nonce) {
+        /* Generates a random nonce, using the internal random generator. */
+        if (!OCSP_request_add1_nonce(ocsp_req, NULL, -1))
+            goto cleanup;
+    }
+
+    /* Generate the DER form of the request. */
+    len = i2d_OCSP_REQUEST(ocsp_req, NULL);
+    if (len < 0)
+        goto cleanup;
+
+    unused = der = apr_palloc(result_pool, len);
+    len = i2d_OCSP_REQUEST(ocsp_req, &unused); /* unused is incremented */
+    if (len < 0)
+        goto cleanup;
+
+    req = apr_palloc(result_pool, sizeof(*req));
+    req->der_request = der;
+    req->der_request_size = len;
+    req->encoded_server_cert =
+        serf_ssl_cert_export2(server_cert, result_pool, scratch_pool);
+    req->encoded_issuer_cert =
+        serf_ssl_cert_export2(issuer_cert, result_pool, scratch_pool);
+
+    /* Now move the unencoded request to the result. */
+    req->request = ocsp_req;
+    apr_pool_cleanup_register(result_pool, ocsp_req,
+                              free_ocsp_request,
+                              apr_pool_cleanup_null);
+    ocsp_req = NULL;
+
+  cleanup:
+    if (ocsp_req)
+        OCSP_REQUEST_free(ocsp_req);
+    if (cert_id)
+        OCSP_CERTID_free(cert_id);
+    return req;
+#else
+    return NULL;
+#endif  /* OPENSSL_NO_OCSP */
+}
+
+const void *serf_ssl_ocsp_request_body(
+    const serf_ssl_ocsp_request_t *ocsp_request)
+{
+#ifndef OPENSSL_NO_OCSP
+    return ocsp_request->der_request;
+#else
+    return NULL;
+#endif  /* OPENSSL_NO_OCSP */
+}
+
+apr_size_t serf_ssl_ocsp_request_body_size(
+    const serf_ssl_ocsp_request_t *ocsp_request)
+{
+#ifndef OPENSSL_NO_OCSP
+    return ocsp_request->der_request_size;
+#else
+    return 0;
+#endif  /* OPENSSL_NO_OCSP */
+}
+
+const char *serf_ssl_ocsp_request_export(
+    const serf_ssl_ocsp_request_t *ocsp_request,
+    apr_pool_t *result_pool,
+    apr_pool_t *scratch_pool)
+{
+#ifndef OPENSSL_NO_OCSP
+
+    /*
+      The structure of the exported request is:
+
+        "Base64-server-cert" "\x1"
+        "Base64-issuer-cert" "\x1"
+        "Base64-DER-formatted-request" "\0"
+    */
+
+    const apr_size_t s_size = strlen(ocsp_request->encoded_server_cert);
+    const apr_size_t i_size = strlen(ocsp_request->encoded_issuer_cert);
+    const apr_size_t all_size = (
+        apr_base64_encode_len(ocsp_request->der_request_size)
+        + s_size + i_size + 3); /* Three terminator bytes */
+
+    char *const buffer = apr_palloc(result_pool, all_size);
+    char *p = buffer;
+
+    memcpy(p, ocsp_request->encoded_server_cert, s_size);
+    p += s_size;
+    *p++ = '\x1';
+
+    memcpy(p, ocsp_request->encoded_issuer_cert, i_size);
+    p += i_size;
+    *p++ = '\x1';
+
+    apr_base64_encode(p, ocsp_request->der_request,
+                      ocsp_request->der_request_size);
+
+    return buffer;
+#else
+    return NULL;
+#endif  /* OPENSSL_NO_OCSP */
+}
+
+serf_ssl_ocsp_request_t *serf_ssl_ocsp_request_import(
+    const char *encoded_ocsp_request,
+    apr_pool_t *result_pool,
+    apr_pool_t *scratch_pool)
+{
+#ifndef OPENSSL_NO_OCSP
+    serf_ssl_ocsp_request_t *req = NULL;
+    const char *encoded_server_cert = encoded_ocsp_request;
+    const char *encoded_issuer_cert;
+    const char *end_server_cert;
+    const char *end_issuer_cert;
+
+    end_server_cert = strchr(encoded_server_cert, '\x1');
+    if (!end_server_cert)
+        return NULL;
+
+    encoded_issuer_cert = end_server_cert + 1;
+    end_issuer_cert = strchr(encoded_issuer_cert, '\x1');
+
+    if (end_issuer_cert) {
+        OCSP_REQUEST *ocsp_req;
+        const char *base64_request = end_issuer_cert + 1;
+        long der_request_size = apr_base64_decode_len(base64_request);
+        /* FIXME: Use scratch pool instead and pmemdup later? */
+        void *der_request = apr_palloc(result_pool, der_request_size);
+        const unsigned char *unused = der_request;
+
+        der_request_size = apr_base64_decode(der_request, base64_request);
+        ocsp_req = d2i_OCSP_REQUEST(NULL, &unused, der_request_size);
+        if (!ocsp_req)
+            return NULL;
+
+        req = apr_palloc(result_pool, sizeof(*req));
+        req->der_request = der_request;
+        req->der_request_size = der_request_size;
+        req->encoded_server_cert =
+            apr_pstrmemdup(result_pool, encoded_server_cert,
+                           end_server_cert - encoded_server_cert);
+        req->encoded_issuer_cert =
+            apr_pstrmemdup(result_pool, encoded_issuer_cert,
+                           end_issuer_cert - encoded_issuer_cert);
+
+        req->request = ocsp_req;
+        apr_pool_cleanup_register(result_pool, ocsp_req,
+                                  free_ocsp_request,
+                                  apr_pool_cleanup_null);
+    }
+
+    return req;
+#else
+    return NULL;
+#endif  /* OPENSSL_NO_OCSP */
+}
+
+struct serf_ssl_ocsp_response_t {
+#ifndef OPENSSL_NO_OCSP
+    /* OpenSSL's internal representation of the OCSP response. */
+    OCSP_BASICRESP *response;
+#endif  /* OPENSSL_NO_OCSP */
+};
+
+serf_ssl_ocsp_response_t *serf_ssl_ocsp_response_parse(
+    const void *ocsp_response_body,
+    apr_size_t ocsp_response_size,
+    apr_pool_t *result_pool,
+    apr_pool_t *scratch_pool)
+{
+#ifndef OPENSSL_NO_OCSP
+    return NULL;
+#else
+    return NULL;
+#endif  /* OPENSSL_NO_OCSP */
+}
+
+apr_status_t serf_ssl_ocsp_response_verify(
+    const serf_ssl_ocsp_response_t *ocsp_response,
+    const serf_ssl_ocsp_request_t *ocsp_request,
+    apr_time_t *this_update,
+    apr_time_t *next_update,
+    apr_time_t *produced_at,
+    apr_pool_t *scratch_pool)
+{
+#ifndef OPENSSL_NO_OCSP
+    return SERF_ERROR_SSL_OCSP_RESPONSE_INVALID;
+#else
+    return SERF_ERROR_SSL_OCSP_RESPONSE_INVALID;
+#endif  /* OPENSSL_NO_OCSP */
+}
diff --git a/serf_bucket_types.h b/serf_bucket_types.h
index 58f4b7e..0e2f93e 100644
--- a/serf_bucket_types.h
+++ b/serf_bucket_types.h
@@ -891,7 +891,7 @@
  *
  * Returns @c NULL if the response body is not well-formed.
  */
-serf_ssl_ocsp_request_t *serf_ssl_ocsp_response_parse(
+serf_ssl_ocsp_response_t *serf_ssl_ocsp_response_parse(
     const void *ocsp_response_body,
     apr_size_t ocsp_response_size,
     apr_pool_t *result_pool,
diff --git a/test/test_ssl.c b/test/test_ssl.c
index 105f0dd..776501f 100644
--- a/test/test_ssl.c
+++ b/test/test_ssl.c
@@ -2303,6 +2303,103 @@
                                                 handler_ctx, tb->pool);
 }
 
+
+#ifndef OPENSSL_NO_OCSP
+static void load_ocsp_test_certs(CuTest *tc,
+                                 serf_ssl_certificate_t **cert,
+                                 serf_ssl_certificate_t **issuer)
+{
+    test_baton_t *tb = tc->testBaton;
+    apr_status_t status;
+
+    status = serf_ssl_load_cert_file(
+        cert,
+        get_srcdir_file(tb->pool, "test/certs/serfserver_san_ocsp_cert.pem"),
+        tb->pool);
+    CuAssertIntEquals(tc, APR_SUCCESS, status);
+    CuAssertPtrNotNull(tc, *cert);
+
+    status = serf_ssl_load_cert_file(
+        issuer,
+        get_srcdir_file(tb->pool, "test/certs/serfcacert.pem"),
+        tb->pool);
+    CuAssertIntEquals(tc, APR_SUCCESS, status);
+    CuAssertPtrNotNull(tc, *issuer);
+}
+#endif  /* OPENSSL_NO_OCSP */
+
+static void test_ssl_ocsp_request_create(CuTest *tc)
+{
+#ifndef OPENSSL_NO_OCSP
+    test_baton_t *tb = tc->testBaton;
+    serf_ssl_certificate_t *cert = NULL;
+    serf_ssl_certificate_t *issuer = NULL;
+    serf_ssl_ocsp_request_t *req = NULL;
+
+    load_ocsp_test_certs(tc, &cert, &issuer);
+
+    /* no nonce */
+    req = serf_ssl_ocsp_request_create(cert, issuer, 0, tb->pool, tb->pool);
+    CuAssertPtrNotNull(tc, req);
+
+    /* add nonce */
+    req = serf_ssl_ocsp_request_create(cert, issuer, 1, tb->pool, tb->pool);
+    CuAssertPtrNotNull(tc, req);
+
+    /* certs switched */
+    req = serf_ssl_ocsp_request_create(issuer, cert, 0, tb->pool, tb->pool);
+    CuAssertPtrEquals(tc, NULL, req);
+#else
+    CuTestAssertTrue(tc, 1);
+#endif  /* OPENSSL_NO_OCSP */
+}
+
+
+static void test_ssl_ocsp_request_export_import(CuTest *tc)
+{
+#ifndef OPENSSL_NO_OCSP
+    test_baton_t *tb = tc->testBaton;
+    serf_ssl_certificate_t *cert = NULL;
+    serf_ssl_certificate_t *issuer = NULL;
+    serf_ssl_ocsp_request_t *req = NULL;
+    serf_ssl_ocsp_request_t *impreq = NULL;
+    const char *expreq = NULL;
+
+    load_ocsp_test_certs(tc, &cert, &issuer);
+
+    impreq = serf_ssl_ocsp_request_import("foo", tb->pool, tb->pool);
+    CuAssertPtrEquals(tc, NULL, impreq);
+
+    impreq = serf_ssl_ocsp_request_import("foo" "\x1" "bar", tb->pool, tb->pool);
+    CuAssertPtrEquals(tc, NULL, impreq);
+
+    impreq = serf_ssl_ocsp_request_import("foo" "\x1" "bar" "\x1" "baz", tb->pool, tb->pool);
+    CuAssertPtrEquals(tc, NULL, impreq);
+
+    req = serf_ssl_ocsp_request_create(cert, issuer, 0, tb->pool, tb->pool);
+    CuAssertPtrNotNull(tc, req);
+    CuAssertPtrNotNull(tc, serf_ssl_ocsp_request_body(req));
+    CuAssertTrue(tc, 0 < serf_ssl_ocsp_request_body_size(req));
+
+    expreq = serf_ssl_ocsp_request_export(req, tb->pool, tb->pool);
+    CuAssertPtrNotNull(tc, expreq);
+
+    impreq = serf_ssl_ocsp_request_import(expreq, tb->pool, tb->pool);
+    CuAssertPtrNotNull(tc, impreq);
+
+    CuAssertIntEquals(tc,
+                      serf_ssl_ocsp_request_body_size(req),
+                      serf_ssl_ocsp_request_body_size(impreq));
+    CuAssertTrue(tc,
+                 0 == memcmp(serf_ssl_ocsp_request_body(req),
+                             serf_ssl_ocsp_request_body(impreq),
+                             serf_ssl_ocsp_request_body_size(req)));
+#else
+    CuTestAssertTrue(tc, 1);
+#endif  /* OPENSSL_NO_OCSP */
+}
+
+
 CuSuite *test_ssl(void)
 {
     CuSuite *suite = CuSuiteNew();
@@ -2349,6 +2446,7 @@
     SUITE_ADD_TEST(suite, test_ssl_server_cert_with_san_and_empty_cb);
     SUITE_ADD_TEST(suite, test_ssl_renegotiate);
     SUITE_ADD_TEST(suite, test_ssl_alpn_negotiate);
-
+    SUITE_ADD_TEST(suite, test_ssl_ocsp_request_create);
+    SUITE_ADD_TEST(suite, test_ssl_ocsp_request_export_import);
     return suite;
 }