/* ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you 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 "serf.h" #include "test_server.h" #include "serf_private.h" #include #include #include #if defined(OPENSSL_VERSION_NUMBER) && OPENSSL_VERSION_NUMBER >= 0x10100000L #define USE_OPENSSL_1_1_API #endif static int init_done = 0; typedef struct ssl_context_t { int handshake_done; SSL_CTX* ctx; SSL* ssl; BIO *bio; BIO_METHOD *biom; } ssl_context_t; static int pem_passwd_cb(char *buf, int size, int rwflag, void *userdata) { strncpy(buf, "serftest", size); buf[size - 1] = '\0'; return strlen(buf); } static void bio_set_data(BIO *bio, void *data) { #ifdef USE_OPENSSL_1_1_API BIO_set_data(bio, data); #else bio->ptr = data; #endif } static void *bio_get_data(BIO *bio) { #ifdef USE_OPENSSL_1_1_API return BIO_get_data(bio); #else return bio->ptr; #endif } static int bio_apr_socket_create(BIO *bio) { #ifdef USE_OPENSSL_1_1_API BIO_set_shutdown(bio, 1); BIO_set_init(bio, 1); BIO_set_data(bio, NULL); #else bio->shutdown = 1; bio->init = 1; bio->num = -1; bio->ptr = NULL; #endif return 1; } static int bio_apr_socket_destroy(BIO *bio) { /* Did we already free this? */ if (bio == NULL) { return 0; } return 1; } static long bio_apr_socket_ctrl(BIO *bio, int cmd, long num, void *ptr) { long ret = 1; switch (cmd) { default: /* abort(); */ break; case BIO_CTRL_FLUSH: /* At this point we can't force a flush. */ break; case BIO_CTRL_PUSH: case BIO_CTRL_POP: ret = 0; break; } return ret; } /* Returns the amount read. */ static int bio_apr_socket_read(BIO *bio, char *in, int inlen) { apr_size_t len = inlen; serv_ctx_t *serv_ctx = bio_get_data(bio); apr_status_t status; BIO_clear_retry_flags(bio); status = apr_socket_recv(serv_ctx->client_sock, in, &len); serv_ctx->bio_read_status = status; serf__log_skt(TEST_VERBOSE, __FILE__, serv_ctx->client_sock, "Read %d bytes from socket with status %d.\n", len, status); if (status == APR_EAGAIN) { BIO_set_retry_read(bio); if (len == 0) return -1; } if (SERF_BUCKET_READ_ERROR(status)) return -1; return len; } /* Returns the amount written. */ static int bio_apr_socket_write(BIO *bio, const char *in, int inlen) { apr_size_t len = inlen; serv_ctx_t *serv_ctx = bio_get_data(bio); apr_status_t status = apr_socket_send(serv_ctx->client_sock, in, &len); serf__log_skt(TEST_VERBOSE, __FILE__, serv_ctx->client_sock, "Wrote %d of %d bytes to socket with status %d.\n", len, inlen, status); if (SERF_BUCKET_READ_ERROR(status)) return -1; return len; } #ifndef USE_OPENSSL_1_1_API static BIO_METHOD bio_apr_socket_method = { BIO_TYPE_SOCKET, "APR sockets", bio_apr_socket_write, bio_apr_socket_read, NULL, /* Is this called? */ NULL, /* Is this called? */ bio_apr_socket_ctrl, bio_apr_socket_create, bio_apr_socket_destroy, #ifdef OPENSSL_VERSION_NUMBER NULL /* sslc does not have the callback_ctrl field */ #endif }; #endif static BIO_METHOD *bio_meth_apr_socket_new(void) { BIO_METHOD *biom = NULL; #ifdef USE_OPENSSL_1_1_API biom = BIO_meth_new(BIO_TYPE_SOCKET, "APR sockets"); if (biom) { BIO_meth_set_write(biom, bio_apr_socket_write); BIO_meth_set_read(biom, bio_apr_socket_read); BIO_meth_set_ctrl(biom, bio_apr_socket_ctrl); BIO_meth_set_create(biom, bio_apr_socket_create); BIO_meth_set_destroy(biom, bio_apr_socket_destroy); } #else biom = &bio_apr_socket_method; #endif return biom; } static int validate_client_certificate(int preverify_ok, X509_STORE_CTX *ctx) { serf__log(TEST_VERBOSE, __FILE__, "validate_client_certificate called, " "preverify code: %d.\n", preverify_ok); return preverify_ok; } static apr_status_t init_ssl(serv_ctx_t *serv_ctx) { ssl_context_t *ssl_ctx = serv_ctx->ssl_ctx; ssl_ctx->ssl = SSL_new(ssl_ctx->ctx); SSL_set_cipher_list(ssl_ctx->ssl, "ALL"); SSL_set_bio(ssl_ctx->ssl, ssl_ctx->bio, ssl_ctx->bio); return APR_SUCCESS; } static apr_status_t init_ssl_context(serv_ctx_t *serv_ctx, const char *keyfile, const char **certfiles, const char *client_cn) { ssl_context_t *ssl_ctx = apr_pcalloc(serv_ctx->pool, sizeof(*ssl_ctx)); serv_ctx->ssl_ctx = ssl_ctx; serv_ctx->client_cn = client_cn; serv_ctx->bio_read_status = APR_SUCCESS; /* Init OpenSSL globally */ if (!init_done) { #ifdef USE_OPENSSL_1_1_API OPENSSL_malloc_init(); #else CRYPTO_malloc_init(); #endif ERR_load_crypto_strings(); SSL_load_error_strings(); SSL_library_init(); OpenSSL_add_all_algorithms(); init_done = 1; } /* Init this connection */ if (!ssl_ctx->ctx) { X509_STORE *store; const char *certfile; int i; int rv; ssl_ctx->ctx = SSL_CTX_new(SSLv23_server_method()); SSL_CTX_set_default_passwd_cb(ssl_ctx->ctx, pem_passwd_cb); rv = SSL_CTX_use_PrivateKey_file(ssl_ctx->ctx, keyfile, SSL_FILETYPE_PEM); if (rv != 1) { fprintf(stderr, "Cannot load private key from file '%s'\n", keyfile); exit(1); } /* Set server certificate, add ca certificates if provided. */ certfile = certfiles[0]; rv = SSL_CTX_use_certificate_file(ssl_ctx->ctx, certfile, SSL_FILETYPE_PEM); if (rv != 1) { fprintf(stderr, "Cannot load certficate from file '%s'\n", certfile); exit(1); } i = 1; certfile = certfiles[i++]; store = SSL_CTX_get_cert_store(ssl_ctx->ctx); while(certfile) { FILE *fp = fopen(certfile, "r"); if (fp) { X509 *ssl_cert = PEM_read_X509(fp, NULL, NULL, NULL); fclose(fp); SSL_CTX_add_extra_chain_cert(ssl_ctx->ctx, ssl_cert); X509_STORE_add_cert(store, ssl_cert); } certfile = certfiles[i++]; } /* This makes the server send a client certificate request during handshake. The client certificate is optional (most tests don't send one) by default, but mandatory if client_cn was specified. */ SSL_CTX_set_verify(ssl_ctx->ctx, SSL_VERIFY_PEER, validate_client_certificate); SSL_CTX_set_mode(ssl_ctx->ctx, SSL_MODE_ENABLE_PARTIAL_WRITE); ssl_ctx->biom = bio_meth_apr_socket_new(); ssl_ctx->bio = BIO_new(ssl_ctx->biom); bio_set_data(ssl_ctx->bio, serv_ctx); init_ssl(serv_ctx); } return APR_SUCCESS; } static apr_status_t ssl_reset(serv_ctx_t *serv_ctx) { ssl_context_t *ssl_ctx = serv_ctx->ssl_ctx; serf__log(TEST_VERBOSE, __FILE__, "Reset ssl context.\n"); ssl_ctx->handshake_done = 0; if (ssl_ctx) SSL_clear(ssl_ctx->ssl); init_ssl(serv_ctx); return APR_SUCCESS; } static apr_status_t ssl_handshake(serv_ctx_t *serv_ctx) { ssl_context_t *ssl_ctx = serv_ctx->ssl_ctx; int result; if (ssl_ctx->handshake_done) return APR_SUCCESS; /* SSL handshake */ result = SSL_accept(ssl_ctx->ssl); if (result == 1) { X509 *peer; serf__log(TEST_VERBOSE, __FILE__, "Handshake successful.\n"); /* Check client certificate */ peer = SSL_get_peer_certificate(ssl_ctx->ssl); if (peer) { serf__log(TEST_VERBOSE, __FILE__, "Peer cert received.\n"); if (SSL_get_verify_result(ssl_ctx->ssl) == X509_V_OK) { /* The client sent a certificate which verified OK */ char buf[1024]; int ret; X509_NAME *subject = X509_get_subject_name(peer); ret = X509_NAME_get_text_by_NID(subject, NID_commonName, buf, 1024); if (ret != -1 && strcmp(serv_ctx->client_cn, buf) != 0) { serf__log(TEST_VERBOSE, __FILE__, "Client cert common name " "\"%s\" doesn't match expected \"%s\".\n", buf, serv_ctx->client_cn); return SERF_ERROR_ISSUE_IN_TESTSUITE; } } } else { if (serv_ctx->client_cn) { serf__log(TEST_VERBOSE, __FILE__, "Client cert expected but not" " received.\n"); return SERF_ERROR_ISSUE_IN_TESTSUITE; } } ssl_ctx->handshake_done = 1; } else { int ssl_err; ssl_err = SSL_get_error(ssl_ctx->ssl, result); switch (ssl_err) { case SSL_ERROR_WANT_READ: case SSL_ERROR_WANT_WRITE: return APR_EAGAIN; case SSL_ERROR_SYSCALL: return serv_ctx->bio_read_status; /* Usually APR_EAGAIN */ default: serf__log(TEST_VERBOSE, __FILE__, "SSL Error %d: ", ssl_err); ERR_print_errors_fp(stderr); serf__log_nopref(TEST_VERBOSE, "\n"); return SERF_ERROR_ISSUE_IN_TESTSUITE; } } return APR_EAGAIN; } static apr_status_t ssl_socket_write(serv_ctx_t *serv_ctx, const char *data, apr_size_t *len) { ssl_context_t *ssl_ctx = serv_ctx->ssl_ctx; int result = SSL_write(ssl_ctx->ssl, data, *len); if (result > 0) { *len = result; return APR_SUCCESS; } if (result == 0) return APR_EAGAIN; serf__log(TEST_VERBOSE, __FILE__, "ssl_socket_write: ssl error?\n"); return SERF_ERROR_ISSUE_IN_TESTSUITE; } static apr_status_t ssl_socket_read(serv_ctx_t *serv_ctx, char *data, apr_size_t *len) { ssl_context_t *ssl_ctx = serv_ctx->ssl_ctx; int result = SSL_read(ssl_ctx->ssl, data, *len); if (result > 0) { *len = result; return APR_SUCCESS; } else { int ssl_err; ssl_err = SSL_get_error(ssl_ctx->ssl, result); switch (ssl_err) { case SSL_ERROR_SYSCALL: /* error in bio_bucket_read, probably APR_EAGAIN or APR_EOF */ *len = 0; return serv_ctx->bio_read_status; case SSL_ERROR_WANT_READ: *len = 0; return APR_EAGAIN; case SSL_ERROR_SSL: default: *len = 0; serf__log(TEST_VERBOSE, __FILE__, "ssl_socket_read SSL Error %d: ", ssl_err); ERR_print_errors_fp(stderr); serf__log_nopref(TEST_VERBOSE, "\n"); return SERF_ERROR_ISSUE_IN_TESTSUITE; } } /* not reachable */ return SERF_ERROR_ISSUE_IN_TESTSUITE; } static apr_status_t cleanup_https_server(void *baton) { serv_ctx_t *servctx = baton; ssl_context_t *ssl_ctx = servctx->ssl_ctx; if (ssl_ctx) { if (ssl_ctx->ssl) { SSL_clear(ssl_ctx->ssl); #ifdef USE_OPENSSL_1_1_API BIO_meth_free(ssl_ctx->biom); #endif } SSL_CTX_free(ssl_ctx->ctx); } return APR_SUCCESS; } void setup_https_test_server(serv_ctx_t **servctx_p, apr_sockaddr_t *address, test_server_message_t *message_list, apr_size_t message_count, test_server_action_t *action_list, apr_size_t action_count, apr_int32_t options, const char *keyfile, const char **certfiles, const char *client_cn, apr_pool_t *pool) { serv_ctx_t *servctx; setup_test_server(servctx_p, address, message_list, message_count, action_list, action_count, options, pool); servctx = *servctx_p; apr_pool_cleanup_register(pool, servctx, cleanup_https_server, apr_pool_cleanup_null); servctx->handshake = ssl_handshake; servctx->reset = ssl_reset; /* Override with SSL encrypt/decrypt functions */ servctx->read = ssl_socket_read; servctx->send = ssl_socket_write; init_ssl_context(servctx, keyfile, certfiles, client_cn); }