| /* Copyright 2008 Justin Erenkrantz and Greg Stein |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| #include <apr.h> |
| #include <apr_pools.h> |
| #include <apr_strings.h> |
| #include <apr_env.h> |
| |
| #include "serf.h" |
| #include "serf_bucket_types.h" |
| |
| #include "test_serf.h" |
| |
| #if defined(WIN32) && defined(_DEBUG) |
| /* Include this file to allow running a Debug build of serf with a Release |
| build of OpenSSL. */ |
| #include <openssl/applink.c> |
| #endif |
| |
| /* Test setting up the openssl library. */ |
| static void test_ssl_init(CuTest *tc) |
| { |
| serf_bucket_t *bkt, *stream; |
| serf_ssl_context_t *ssl_context; |
| apr_status_t status; |
| |
| apr_pool_t *test_pool = tc->testBaton; |
| serf_bucket_alloc_t *alloc = serf_bucket_allocator_create(test_pool, NULL, |
| NULL); |
| |
| stream = SERF_BUCKET_SIMPLE_STRING("", alloc); |
| |
| bkt = serf_bucket_ssl_decrypt_create(stream, NULL, |
| alloc); |
| ssl_context = serf_bucket_ssl_decrypt_context_get(bkt); |
| |
| bkt = serf_bucket_ssl_encrypt_create(stream, ssl_context, |
| alloc); |
| |
| status = serf_ssl_use_default_certificates(ssl_context); |
| |
| CuAssertIntEquals(tc, APR_SUCCESS, status); |
| } |
| |
| |
| static const char * get_ca_file(apr_pool_t *pool, const char * file) |
| { |
| char *srcdir = ""; |
| |
| if (apr_env_get(&srcdir, "srcdir", pool) == APR_SUCCESS) { |
| return apr_pstrcat(pool, srcdir, "/", file, NULL); |
| } |
| else { |
| return file; |
| } |
| } |
| |
| |
| /* Test that loading a custom CA certificate file works. */ |
| static void test_ssl_load_cert_file(CuTest *tc) |
| { |
| serf_ssl_certificate_t *cert = NULL; |
| |
| apr_pool_t *test_pool = tc->testBaton; |
| apr_status_t status = serf_ssl_load_cert_file( |
| &cert, get_ca_file(test_pool, "test/serftestca.pem"), test_pool); |
| |
| CuAssertIntEquals(tc, APR_SUCCESS, status); |
| CuAssertPtrNotNull(tc, cert); |
| } |
| |
| /* Test that reading the subject from a custom CA certificate file works. */ |
| static void test_ssl_cert_subject(CuTest *tc) |
| { |
| apr_hash_t *subject; |
| serf_ssl_certificate_t *cert = NULL; |
| apr_status_t status; |
| |
| apr_pool_t *test_pool = tc->testBaton; |
| |
| status = serf_ssl_load_cert_file(&cert, get_ca_file(test_pool, |
| "test/serftestca.pem"), |
| test_pool); |
| |
| CuAssertIntEquals(tc, APR_SUCCESS, status); |
| CuAssertPtrNotNull(tc, cert); |
| |
| subject = serf_ssl_cert_subject(cert, test_pool); |
| CuAssertPtrNotNull(tc, subject); |
| |
| CuAssertStrEquals(tc, "Serf", |
| apr_hash_get(subject, "CN", APR_HASH_KEY_STRING)); |
| CuAssertStrEquals(tc, "Test Suite", |
| apr_hash_get(subject, "OU", APR_HASH_KEY_STRING)); |
| CuAssertStrEquals(tc, "In Serf we trust, Inc.", |
| apr_hash_get(subject, "O", APR_HASH_KEY_STRING)); |
| CuAssertStrEquals(tc, "Mechelen", |
| apr_hash_get(subject, "L", APR_HASH_KEY_STRING)); |
| CuAssertStrEquals(tc, "Antwerp", |
| apr_hash_get(subject, "ST", APR_HASH_KEY_STRING)); |
| CuAssertStrEquals(tc, "BE", |
| apr_hash_get(subject, "C", APR_HASH_KEY_STRING)); |
| CuAssertStrEquals(tc, "serf@example.com", |
| apr_hash_get(subject, "E", APR_HASH_KEY_STRING)); |
| } |
| |
| /* Test that reading the issuer from a custom CA certificate file works. */ |
| static void test_ssl_cert_issuer(CuTest *tc) |
| { |
| apr_hash_t *issuer; |
| serf_ssl_certificate_t *cert = NULL; |
| apr_status_t status; |
| |
| apr_pool_t *test_pool = tc->testBaton; |
| |
| status = serf_ssl_load_cert_file(&cert, get_ca_file(test_pool, |
| "test/serftestca.pem"), |
| test_pool); |
| |
| CuAssertIntEquals(tc, APR_SUCCESS, status); |
| CuAssertPtrNotNull(tc, cert); |
| |
| issuer = serf_ssl_cert_issuer(cert, test_pool); |
| CuAssertPtrNotNull(tc, issuer); |
| |
| /* TODO: create a new test certificate with different issuer and subject. */ |
| CuAssertStrEquals(tc, "Serf", |
| apr_hash_get(issuer, "CN", APR_HASH_KEY_STRING)); |
| CuAssertStrEquals(tc, "Test Suite", |
| apr_hash_get(issuer, "OU", APR_HASH_KEY_STRING)); |
| CuAssertStrEquals(tc, "In Serf we trust, Inc.", |
| apr_hash_get(issuer, "O", APR_HASH_KEY_STRING)); |
| CuAssertStrEquals(tc, "Mechelen", |
| apr_hash_get(issuer, "L", APR_HASH_KEY_STRING)); |
| CuAssertStrEquals(tc, "Antwerp", |
| apr_hash_get(issuer, "ST", APR_HASH_KEY_STRING)); |
| CuAssertStrEquals(tc, "BE", |
| apr_hash_get(issuer, "C", APR_HASH_KEY_STRING)); |
| CuAssertStrEquals(tc, "serf@example.com", |
| apr_hash_get(issuer, "E", APR_HASH_KEY_STRING)); |
| } |
| |
| /* Test that reading the notBefore,notAfter,sha1 fingerprint and subjectAltNames |
| from a custom CA certificate file works. */ |
| static void test_ssl_cert_certificate(CuTest *tc) |
| { |
| apr_hash_t *kv; |
| serf_ssl_certificate_t *cert = NULL; |
| apr_array_header_t *san_arr; |
| apr_status_t status; |
| |
| apr_pool_t *test_pool = tc->testBaton; |
| |
| status = serf_ssl_load_cert_file(&cert, get_ca_file(test_pool, |
| "test/serftestca.pem"), |
| test_pool); |
| CuAssertIntEquals(tc, APR_SUCCESS, status); |
| CuAssertPtrNotNull(tc, cert); |
| |
| kv = serf_ssl_cert_certificate(cert, test_pool); |
| CuAssertPtrNotNull(tc, kv); |
| |
| CuAssertStrEquals(tc, "8A:4C:19:D5:F2:52:4E:35:49:5E:7A:14:80:B2:02:BD:B4:4D:22:18", |
| apr_hash_get(kv, "sha1", APR_HASH_KEY_STRING)); |
| CuAssertStrEquals(tc, "Mar 21 13:18:17 2008 GMT", |
| apr_hash_get(kv, "notBefore", APR_HASH_KEY_STRING)); |
| CuAssertStrEquals(tc, "Mar 21 13:18:17 2011 GMT", |
| apr_hash_get(kv, "notAfter", APR_HASH_KEY_STRING)); |
| |
| /* TODO: create a new test certificate with a/some sAN's. */ |
| san_arr = apr_hash_get(kv, "subjectAltName", APR_HASH_KEY_STRING); |
| CuAssertTrue(tc, san_arr == NULL); |
| } |
| |
| static const char *extract_cert_from_pem(const char *pemdata, |
| apr_pool_t *pool) |
| { |
| enum { INIT, CERT_BEGIN, CERT_FOUND } state; |
| serf_bucket_t *pembkt; |
| const char *begincert = "-----BEGIN CERTIFICATE-----"; |
| const char *endcert = "-----END CERTIFICATE-----"; |
| char *certdata = ""; |
| apr_size_t certlen = 0; |
| apr_status_t status = APR_SUCCESS; |
| |
| serf_bucket_alloc_t *alloc = serf_bucket_allocator_create(pool, |
| NULL, NULL); |
| |
| /* Extract the certificate from the .pem file, also remove newlines. */ |
| pembkt = SERF_BUCKET_SIMPLE_STRING(pemdata, alloc); |
| state = INIT; |
| while (state != CERT_FOUND && status != APR_EOF) { |
| const char *data; |
| apr_size_t len; |
| int found; |
| |
| status = serf_bucket_readline(pembkt, SERF_NEWLINE_ANY, &found, |
| &data, &len); |
| if (SERF_BUCKET_READ_ERROR(status)) |
| return NULL; |
| |
| if (state == INIT) { |
| if (strncmp(begincert, data, strlen(begincert)) == 0) |
| state = CERT_BEGIN; |
| } else if (state == CERT_BEGIN) { |
| if (strncmp(endcert, data, strlen(endcert)) == 0) |
| state = CERT_FOUND; |
| else { |
| certdata = apr_pstrcat(pool, certdata, data, NULL); |
| certlen += len; |
| switch (found) { |
| case SERF_NEWLINE_CR: |
| case SERF_NEWLINE_LF: |
| certdata[certlen-1] = '\0'; |
| certlen --; |
| break; |
| case SERF_NEWLINE_CRLF: |
| certdata[certlen-2] = '\0'; |
| certlen-=2; |
| break; |
| } |
| } |
| } |
| } |
| |
| if (state == CERT_FOUND) |
| return certdata; |
| else |
| return NULL; |
| } |
| |
| static void test_ssl_cert_export(CuTest *tc) |
| { |
| serf_ssl_certificate_t *cert = NULL; |
| apr_file_t *fp; |
| apr_finfo_t file_info; |
| const char *base64derbuf; |
| char *pembuf; |
| apr_size_t pemlen; |
| apr_status_t status; |
| |
| apr_pool_t *test_pool = tc->testBaton; |
| |
| status = serf_ssl_load_cert_file(&cert, get_ca_file(test_pool, |
| "test/serftestca.pem"), |
| test_pool); |
| CuAssertIntEquals(tc, APR_SUCCESS, status); |
| CuAssertPtrNotNull(tc, cert); |
| |
| /* A .pem file contains a Base64 encoded DER certificate, which is exactly |
| what serf_ssl_cert_export is supposed to be returning. */ |
| status = apr_file_open(&fp, "test/serftestca.pem", |
| APR_FOPEN_READ | APR_FOPEN_BINARY, |
| APR_FPROT_OS_DEFAULT, test_pool); |
| CuAssertIntEquals(tc, APR_SUCCESS, status); |
| |
| apr_file_info_get(&file_info, APR_FINFO_SIZE, fp); |
| pembuf = apr_palloc(test_pool, file_info.size); |
| |
| status = apr_file_read_full(fp, pembuf, file_info.size, &pemlen); |
| CuAssertIntEquals(tc, APR_SUCCESS, status); |
| |
| base64derbuf = serf_ssl_cert_export(cert, test_pool); |
| |
| CuAssertStrEquals(tc, |
| extract_cert_from_pem(pembuf, test_pool), |
| base64derbuf); |
| } |
| |
| CuSuite *test_ssl(void) |
| { |
| CuSuite *suite = CuSuiteNew(); |
| |
| CuSuiteSetSetupTeardownCallbacks(suite, test_setup, test_teardown); |
| |
| SUITE_ADD_TEST(suite, test_ssl_init); |
| SUITE_ADD_TEST(suite, test_ssl_load_cert_file); |
| SUITE_ADD_TEST(suite, test_ssl_cert_subject); |
| SUITE_ADD_TEST(suite, test_ssl_cert_issuer); |
| SUITE_ADD_TEST(suite, test_ssl_cert_certificate); |
| SUITE_ADD_TEST(suite, test_ssl_cert_export); |
| |
| return suite; |
| } |