blob: a8b79073c2bbaeb4328969893f801216398056f4 [file] [log] [blame]
/* 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"
APU_DECLARE(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.
*/
APU_DECLARE(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 should 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;
}
APU_DECLARE(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) {
return APR_ENOMEM;
}
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;
}
APU_DECLARE(apr_status_t) apu_ssl_socket_close(apr_ssl_socket_t * sock)
{
int sslRv;
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;
}
APU_DECLARE(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;
}
APU_DECLARE(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? */
}
APU_DECLARE(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 ? */
}
APU_DECLARE(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) {
return APR_ENOMEM;
}
if (!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;
}
APU_DECLARE(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;
}
APU_DECLARE(apr_status_t) apr_evp_crypt_cleanup(apr_evp_crypt_t * e)
{
#if HAVE_DECL_EVP_PKEY_CTX_NEW
if (e->pkeyCtx) {
EVP_PKEY_CTX_free(e->pkeyCtx);
e->pkeyCtx = NULL;
}
#endif
if (e->cipherCtx) {
EVP_CIPHER_CTX_cleanup(e->cipherCtx);
e->cipherCtx = NULL;
}
return APR_SUCCESS;
}
apr_status_t apr_evp_crypt_cleanup_helper(void *data)
{
apr_evp_crypt_t *f = (apr_evp_crypt_t *) data;
return apr_evp_crypt_cleanup(f);
}
APU_DECLARE(apr_status_t) apr_evp_factory_cleanup(apr_evp_factory_t * f)
{
apu_evp_data_t *evpData = f->evpData;
int i;
for (i = 0; i < EVP_MAX_KEY_LENGTH; evpData->key[i++] = 0);
for (i = 0; i < EVP_MAX_IV_LENGTH; evpData->iv[i++] = 0);
#if HAVE_DECL_EVP_PKEY_CTX_NEW
if (evpData->ssl) {
SSL_free(evpData->ssl);
evpData->ssl = NULL;
}
if (evpData->sslCtx) {
SSL_CTX_free(evpData->sslCtx);
evpData->sslCtx = NULL;
}
#endif
return APR_SUCCESS;
}
apr_status_t apr_evp_factory_cleanup_helper(void *data)
{
apr_evp_factory_t *f = (apr_evp_factory_t *) data;
return apr_evp_factory_cleanup(f);
}
APU_DECLARE(apr_status_t) apr_evp_init(void)
{
return apr_ssl_init();
}
APU_DECLARE(apr_status_t) apr_evp_factory_create(apr_evp_factory_t ** newFactory,
const char *privateKeyFn,
const char *certFn,
const char *cipherName,
const char *passphrase,
const char *engine,
const char *digest,
apr_evp_factory_type_e purpose,
apr_pool_t * pool)
{
apr_evp_factory_t *f = apr_pcalloc(pool, sizeof(apr_evp_factory_t));
apu_evp_data_t *data;
if (!f) {
return APR_ENOMEM;
}
*newFactory = f;
f->pool = pool;
f->purpose = purpose;
data = apr_pcalloc(pool, sizeof(apu_evp_data_t));
if (!data) {
return APR_ENOMEM;
}
f->evpData = data;
apr_pool_cleanup_register(pool, f,
apr_evp_factory_cleanup_helper,
apr_pool_cleanup_null);
switch (purpose) {
case APR_EVP_FACTORY_ASYM:{
#if HAVE_DECL_EVP_PKEY_CTX_NEW
/* load certs */
data->sslCtx = SSL_CTX_new(SSLv23_server_method());
if (data->sslCtx) {
if (!SSL_CTX_use_PrivateKey_file(data->sslCtx, privateKeyFn,
SSL_FILETYPE_PEM) ||
!SSL_CTX_use_certificate_file(data->sslCtx, certFn,
SSL_FILETYPE_PEM) ||
!SSL_CTX_check_private_key(data->sslCtx)) {
SSL_CTX_free(data->sslCtx);
return APR_ENOCERT;
}
data->ssl = SSL_new(data->sslCtx);
if (data->ssl) {
X509 *cert;
data->privkey = SSL_get_privatekey(data->ssl);
cert = SSL_get_certificate(data->ssl);
if (cert) {
data->pubkey = X509_get_pubkey(cert);
}
}
}
#else
return APR_ENOTIMPL;
#endif
}
case APR_EVP_FACTORY_SYM:{
data->cipher = EVP_get_cipherbyname(cipherName);
if (!data->cipher) {
return APR_ENOCIPHER;
}
data->md = EVP_get_digestbyname(digest);
if (!data->md) {
return APR_ENODIGEST;
}
EVP_BytesToKey(data->cipher, data->md,
data->salt,
(const unsigned char *) passphrase, strlen(passphrase), 1,
data->key, data->iv);
}
}
return APR_SUCCESS;
}
APU_DECLARE(apr_status_t) apr_evp_crypt_init(apr_evp_factory_t * f,
apr_evp_crypt_t ** e,
apr_evp_crypt_type_e type,
apr_evp_crypt_key_e key,
apr_pool_t * p)
{
apu_evp_data_t *data = f->evpData;
if (!*e) {
*e = apr_pcalloc(p, sizeof(apr_evp_crypt_t));
}
if (!*e) {
return APR_ENOMEM;
}
(*e)->pool = p;
(*e)->purpose = f->purpose;
(*e)->type = type;
(*e)->key = key;
switch (f->purpose) {
case APR_EVP_FACTORY_ASYM:{
#if HAVE_DECL_EVP_PKEY_CTX_NEW
/* todo: add ENGINE support */
if (APR_EVP_KEY_PUBLIC == type) {
(*e)->pkeyCtx = EVP_PKEY_CTX_new(data->pubkey, NULL);
}
else if (APR_EVP_KEY_PRIVATE == type) {
(*e)->pkeyCtx = EVP_PKEY_CTX_new(data->privkey, NULL);
}
apr_pool_cleanup_register(p, *e, apr_evp_crypt_cleanup_helper,
apr_pool_cleanup_null);
if (APR_EVP_ENCRYPT == type) {
if (EVP_PKEY_encrypt_init((*e)->pkeyCtx) <= 0) {
return APR_EINIT;
}
}
else if (APR_EVP_DECRYPT == type) {
if (EVP_PKEY_decrypt_init((*e)->pkeyCtx) <= 0) {
return APR_EINIT;
}
}
return APR_SUCCESS;
#else
return APR_ENOTIMPL;
#endif
}
case APR_EVP_FACTORY_SYM:{
if (!(*e)->cipherCtx) {
(*e)->cipherCtx = apr_pcalloc(p, sizeof(EVP_CIPHER_CTX));
if (!(*e)->cipherCtx) {
return APR_ENOMEM;
}
}
EVP_CIPHER_CTX_init((*e)->cipherCtx);
EVP_CipherInit_ex((*e)->cipherCtx, data->cipher, NULL, data->key, data->iv, type);
return APR_SUCCESS;
}
}
return APR_EINIT;
}
APU_DECLARE(apr_status_t) apr_evp_crypt(apr_evp_crypt_t * e,
unsigned char **out,
apr_size_t * outlen,
const unsigned char *in,
apr_size_t inlen)
{
unsigned char *buffer;
switch (e->purpose) {
case APR_EVP_FACTORY_ASYM:{
#if HAVE_DECL_EVP_PKEY_CTX_NEW
if (!out || !*out) {
if (APR_EVP_ENCRYPT == e->type &&
EVP_PKEY_encrypt(e->pkeyCtx, NULL, outlen,
in, inlen) <= 0) {
return APR_EGENERAL;
}
if (APR_EVP_DECRYPT == e->type &&
EVP_PKEY_decrypt(e->pkeyCtx, NULL, outlen,
in, inlen) <= 0) {
return APR_EGENERAL;
}
if (!out) {
return APR_SUCCESS;
}
buffer = apr_palloc(e->pool, *outlen + 1);
if (!buffer) {
return APR_ENOMEM;
}
*out = buffer;
buffer[*outlen] = 0;
}
if (APR_EVP_ENCRYPT == e->type &&
EVP_PKEY_encrypt(e->pkeyCtx, *out, outlen,
in, inlen) <= 0) {
return APR_EGENERAL;
}
if (APR_EVP_DECRYPT == e->type &&
EVP_PKEY_decrypt(e->pkeyCtx, *out, outlen,
in, inlen) <= 0) {
return APR_EGENERAL;
}
return APR_SUCCESS;
#else
return APR_ENOTIMPL;
#endif
}
case APR_EVP_FACTORY_SYM:{
int len = (int) *outlen;
if (!out) {
*outlen = inlen + EVP_MAX_BLOCK_LENGTH;
return APR_SUCCESS;
}
if (!*out) {
buffer = apr_palloc(e->pool, inlen + EVP_MAX_BLOCK_LENGTH);
if (!buffer) {
return APR_ENOMEM;
}
*out = buffer;
}
if (!EVP_CipherUpdate(e->cipherCtx, *out, &len, in, inlen)) {
return APR_EGENERAL;
}
*outlen = (apr_size_t) len;
return APR_SUCCESS;
}
}
return APR_EGENERAL;
}
APU_DECLARE(apr_status_t) apr_evp_crypt_finish(apr_evp_crypt_t * e,
unsigned char *out,
apr_size_t * outlen)
{
switch (e->purpose) {
case APR_EVP_FACTORY_ASYM:{
#if HAVE_DECL_EVP_PKEY_CTX_NEW
break;
#else
return APR_ENOTIMPL;
#endif
}
case APR_EVP_FACTORY_SYM:{
int tlen;
if (!EVP_CipherFinal_ex(e->cipherCtx, out, &tlen)) {
return APR_EGENERAL;
}
*outlen = tlen;
break;
}
}
apr_evp_crypt_cleanup(e);
return APR_SUCCESS;
}
#endif