/* 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 "apr.h" #include "apr_errno.h" #include "apr_pools.h" #include "apr_strings.h" #define APR_WANT_MEMFUNC #define APR_WANT_STRFUNC #include "apr_want.h" #include "apr_general.h" #include "apu_config.h" #ifdef APU_HAVE_OPENSSL #include "apu.h" #include "apr_portable.h" #include "apr_ssl.h" #include "apr_ssl_private.h" #include "apr_ssl_openssl_private.h" apr_status_t apu_ssl_init(void) { CRYPTO_malloc_init(); SSL_load_error_strings(); SSL_library_init(); OpenSSL_add_all_algorithms(); return APR_SUCCESS; } /* SSL_get_error() docs say that this MUST be called in the same * thread as the operation that failed, and that no other * SSL_ operations should be called between the error being reported * and the call to get the error code made, hence this function should * be called within the function that generates the error. * TODO - this should be expanded to generate the correct APR_ errors * when we have created the mappings :-) */ static void openssl_get_error(apr_ssl_socket_t *sock, int fncode) { sock->sslData->err = fncode; sock->sslData->sslErr = SSL_get_error(sock->sslData->ssl, fncode); } /* The apr_ssl_factory_t structure will have the pool and purpose * fields set only. */ apr_status_t apu_ssl_factory_create(apr_ssl_factory_t *asf, const char *privateKeyFn, const char *certFn, const char *digestType) { apu_ssl_data_t *sslData = apr_pcalloc(asf->pool, sizeof(*sslData)); if (!sslData) { return APR_ENOMEM; } if (asf->purpose == APR_SSL_FACTORY_SERVER) { sslData->ctx = SSL_CTX_new(SSLv23_server_method()); if (sslData->ctx) { if (!SSL_CTX_use_PrivateKey_file(sslData->ctx, privateKeyFn, SSL_FILETYPE_PEM) || !SSL_CTX_use_certificate_file(sslData->ctx, certFn, SSL_FILETYPE_PEM) || !SSL_CTX_check_private_key(sslData->ctx)) { SSL_CTX_free(sslData->ctx); return APR_ENOENT; /* what code shoudl we return? */ } } } else { sslData->ctx = SSL_CTX_new(SSLv23_client_method()); } if (digestType) { sslData->md = EVP_get_digestbyname(digestType); /* we don't care if this fails... */ } if (!sslData->ctx) return APR_EGENERAL; /* what error code? */ asf->sslData = sslData; return APR_SUCCESS; } apr_status_t apu_ssl_socket_create(apr_ssl_socket_t *sslSock, apr_ssl_factory_t *asf) { apu_ssl_socket_data_t *sslData = apr_pcalloc(sslSock->pool, sizeof(*sslData)); apr_os_sock_t fd; if (!sslData || !asf->sslData) return APR_EINVAL; sslData->ssl = SSL_new(asf->sslData->ctx); if (!sslData->ssl) return APR_EINVALSOCK; /* Hmm, better error code? */ /* Joe Orton points out this is actually wrong and assumes that * that we're on an "fd" system. We need some better way of handling * this for systems that don't use fd's for sockets. Will? */ if (apr_os_sock_get(&fd, sslSock->plain) != APR_SUCCESS) return APR_EINVALSOCK; SSL_set_fd(sslData->ssl, fd); sslSock->sslData = sslData; return APR_SUCCESS; } apr_status_t apu_ssl_socket_close(apr_ssl_socket_t *sock) { int sslRv; apr_status_t rv; if (!sock->sslData->ssl) return APR_SUCCESS; if (sock->connected) { if ((sslRv = SSL_shutdown(sock->sslData->ssl)) == 0) sslRv = SSL_shutdown(sock->sslData->ssl); if (sslRv == -1) return APR_EINVALSOCK; /* Better error code to return? */ } SSL_free(sock->sslData->ssl); sock->sslData->ssl = NULL; return APR_SUCCESS; } apr_status_t apu_ssl_connect(apr_ssl_socket_t *sock) { int sslOp; if (!sock->sslData->ssl) return APR_EINVAL; if ((sslOp = SSL_connect(sock->sslData->ssl)) == 1) { sock->connected = 1; return APR_SUCCESS; } openssl_get_error(sock, sslOp); return APR_EGENERAL; } apr_status_t apu_ssl_send(apr_ssl_socket_t *sock, const char *buf, apr_size_t *len) { int sslOp; sslOp = SSL_write(sock->sslData->ssl, buf, *len); if (sslOp > 0) { *len = sslOp; return APR_SUCCESS; } openssl_get_error(sock, sslOp); return APR_EGENERAL; /* SSL error? */ } apr_status_t apu_ssl_recv(apr_ssl_socket_t * sock, char *buf, apr_size_t *len) { int sslOp; if (!sock->sslData) return APR_EINVAL; sslOp = SSL_read(sock->sslData->ssl, buf, *len); if (sslOp > 0) { *len = sslOp; return APR_SUCCESS; } openssl_get_error(sock, sslOp); return APR_EGENERAL; /* SSL error ? */ } apr_status_t apu_ssl_accept(apr_ssl_socket_t *newSock, apr_ssl_socket_t *oldSock, apr_pool_t *pool) { apu_ssl_socket_data_t *sslData = apr_pcalloc(pool, sizeof(*sslData)); apr_os_sock_t fd; int sslOp; if (!sslData || !oldSock->factory) return APR_EINVAL; sslData->ssl = SSL_new(oldSock->factory->sslData->ctx); if (!sslData->ssl) return APR_EINVAL; if (apr_os_sock_get(&fd, newSock->plain) != APR_SUCCESS) return APR_EINVALSOCK; SSL_set_fd(sslData->ssl, fd); newSock->pool = pool; newSock->sslData = sslData; newSock->factory = oldSock->factory; if ((sslOp = SSL_accept(sslData->ssl)) != 1) { openssl_get_error(newSock, sslOp); return APR_EGENERAL; } return APR_SUCCESS; } apr_status_t apu_ssl_raw_error(apr_ssl_socket_t *sock) { if (!sock->sslData) return APR_EINVAL; if (sock->sslData->sslErr) return sock->sslData->sslErr; return APR_SUCCESS; } #endif