diff options
author | minfrin <minfrin@13f79535-47bb-0310-9956-ffa450edef68> | 2007-11-21 21:01:50 +0000 |
---|---|---|
committer | minfrin <minfrin@13f79535-47bb-0310-9956-ffa450edef68> | 2007-11-21 21:01:50 +0000 |
commit | 5139ddb3433c90e1f7a653dafc921356b8712c25 (patch) | |
tree | 3ceec32543d53a040ef01794f5a93a13f98b1f6c | |
parent | 7b9eadc1fe7ed8c256c2ecbd5b47cab0444ac12b (diff) | |
download | libapr-util-5139ddb3433c90e1f7a653dafc921356b8712c25.tar.gz |
Expose the SSL EVP interface to encrypt and decrypt arbitrary
blocks of data, using symmetrical keys. Experimental support for
asymmetrical public/private keys as supported by OpenSSL v0.9.9.
git-svn-id: http://svn.apache.org/repos/asf/apr/apr-util/trunk@597209 13f79535-47bb-0310-9956-ffa450edef68
-rw-r--r-- | CHANGES | 7 | ||||
-rw-r--r-- | build/ssl.m4 | 4 | ||||
-rw-r--r-- | include/apr_buckets.h | 18 | ||||
-rw-r--r-- | include/apr_ssl.h | 157 | ||||
-rw-r--r-- | include/private/apr_ssl_openssl_private.h | 41 | ||||
-rw-r--r-- | ssl/apr_ssl_openssl.c | 294 | ||||
-rw-r--r-- | ssl/apr_ssl_winsock.c | 49 |
7 files changed, 566 insertions, 4 deletions
@@ -1,6 +1,11 @@ -*- coding: utf-8 -*- Changes with APR-util 1.3.0 - + + *) Expose the SSL EVP interface to encrypt and decrypt arbitrary + blocks of data, using symmetrical keys. Experimental support for + asymmetrical public/private keys as supported by OpenSSL v0.9.9. + [Graham Leggett] + *) Introduce apr_dbd_open_ex() [Bojan Smojver] *) Make md5 hash files portable between EBCDIC and ASCII platforms diff --git a/build/ssl.m4 b/build/ssl.m4 index 13d9bc9b..d51602e3 100644 --- a/build/ssl.m4 +++ b/build/ssl.m4 @@ -86,6 +86,9 @@ AC_DEFUN([APU_CHECK_OPENSSL], [ fi fi + AC_CHECK_DECLS([EVP_PKEY_CTX_new], [], [], + [#include <openssl/evp.h>]) + CPPFLAGS="$old_cppflags" LDFLAGS="$old_ldflags" fi @@ -97,7 +100,6 @@ AC_DEFUN([APU_CHECK_OPENSSL], [ fi ]) - AC_SUBST(apu_have_openssl) dnl Add the libraries we will need now that we have set apu_have_openssl correctly diff --git a/include/apr_buckets.h b/include/apr_buckets.h index 829c635c..2fe85195 100644 --- a/include/apr_buckets.h +++ b/include/apr_buckets.h @@ -971,7 +971,16 @@ APU_DECLARE_NONSTD(void) apr_bucket_free(void *block); } while (0) /** - * read the data from the bucket + * Read the data from the bucket. + * + * If it is not practical to return all + * the data in the bucket, the current bucket is split and replaced by + * two buckets, the first representing the data returned in this call, + * and the second representing the rest of the data as yet unread. The + * original bucket will become the first bucket after this call. + * + * (It is assumed that the bucket is a member of a brigade when this + * function is called). * @param e The bucket to read from * @param str The location to store the data in * @param len The amount of data read @@ -988,7 +997,12 @@ APU_DECLARE_NONSTD(void) apr_bucket_free(void *block); #define apr_bucket_setaside(e,p) (e)->type->setaside(e,p) /** - * Split one bucket in two. + * Split one bucket in two at the point provided. + * + * Once split, the original bucket becomes the first of the two new buckets. + * + * (It is assumed that the bucket is a member of a brigade when this + * function is called). * @param e The bucket to split * @param point The offset to split the bucket at */ diff --git a/include/apr_ssl.h b/include/apr_ssl.h index 45fe498a..99ab9bde 100644 --- a/include/apr_ssl.h +++ b/include/apr_ssl.h @@ -254,6 +254,163 @@ APU_DECLARE(apr_status_t) apr_pollset_remove_ssl_socket(apr_ssl_socket_t *); APU_DECLARE(apr_status_t) apr_ssl_socket_set_poll_events(apr_ssl_socket_t *, apr_int16_t); + + +/** + * Values that determine how a created factory will be used. + */ +typedef enum { + APR_EVP_FACTORY_SYM, /**< Factory is for symmetrical operations */ + APR_EVP_FACTORY_ASYM /**< Factory is for asymmetrical operations */ +} apr_evp_factory_type_e; + +/** + * Values that determine whether we need to encrypt or decrypt. + */ +typedef enum { + APR_EVP_DECRYPT=0, + APR_EVP_ENCRYPT=1 +} apr_evp_crypt_type_e; + +/** + * Values that determine which key to use during encrypt or decrypt. + */ +typedef enum { + APR_EVP_KEY_SYM=0, /* Use a passphrase / symmetrical */ + APR_EVP_KEY_PUBLIC=1, /* Use the public key / asymmetrical */ + APR_EVP_KEY_PRIVATE=2 /* Use the private key / asymetrical */ +} apr_evp_crypt_key_e; + +/** + * Structure for referencing an evp "factory" + */ +typedef struct apu_evp_factory apr_evp_factory_t; + +/** + * Structure for referencing an EVP PKEY context. + */ +typedef struct apu_evp_crypt apr_evp_crypt_t; + +/** + * @fn apr_status_t apr_evp_factory_create(apr_evp_factory_t **newFactory, + const char *privateKeyFilename, + const char *certificateFilename, + const char *cipherName, + const char *passphrase, + const char *engine, + const char *digest, + apr_evp_factory_type_e purpose, + apr_pool_t *pool) + * @brief Attempts to create an EVP "factory". The "factory" is then + * used to create contexts to keep track of encryption. + * @param newFactory The newly created factory + * @param privateKeyFilename Private key filename to use for assetrical encryption + * @param certificateFilename X509 certificate file to use for assymetrical encryption + * @param cipherName Name of cipher to use for symmetrical encryption + * @param passphrase Passphrase to use for assymetrical encryption + * @param purpose Constant that determines how the created factory will be used + * @param pool The pool to use for memory allocations + * @return an APR_ status code. APR_ENOCERT will be returned if the certificates + * cannot be loaded, APR_ENOCIPHER if the cipher cannot be found. + * APR_ENODIGEST if the digest cannot be found. APR_ENOTIMPL will + * be returned if not supported. + */ +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); + +/** + * @fn apr_status_t apr_evp_crypt_init(apr_evp_factory_t *, + * apr_evp_crypt_t **e, + * apr_evp_crypt_type_e type, + * apr_evp_crypt_key_e key, + * apr_pool_t *p) + * @brief Initialise a context for encrypting arbitrary data. + * @note If *e is NULL, a apr_evp_crypt_t will be created from a pool. If + * *e is not NULL, *e must point at a previously created structure. + * @param factory The EVP factory containing keys to use. + * @param evp The evp context returned, see note. + * @param type Whether to encrypt or decrypt. + * @param key Which key to use. + * @param p The pool to use. + * @return APR_EINIT if initialisation unsuccessful. Returns + * APR_ENOTIMPL if not supported. + */ +APR_DECLARE(apr_status_t) apr_evp_crypt_init(apr_evp_factory_t *, + apr_evp_crypt_t **e, + apr_evp_crypt_type_e type, + apr_evp_crypt_key_e key, + apr_pool_t *p); + +/** + * @fn apr_status_t apr_evp_crypt(apr_evp_crypt_t *evp, + * unsigned char *out, + * apr_size_t *outlen, + * const unsigned char *in, + * apr_size_t inlen) + * @brief Encrypt/decrypt data provided by in, write it to out. + * @note The number of bytes written will be written to outlen. If + * out is NULL, outlen will contain the maximum size of the + * buffer needed to hold the data. + * @param evp The evp context to use. + * @param out Address of a buffer to which data will be written, + * see note. + * @param outlen Length of the output will be written here. + * @param in Address of the buffer to read. + * @param inlen Length of the buffer to read. + * @return APR_EGENERAL if an error occurred. Returns APR_ENOTIMPL if + * not supported. + */ +APR_DECLARE(apr_status_t) apr_evp_crypt(apr_evp_crypt_t *, + unsigned char **out, + apr_size_t *outlen, + const unsigned char *in, + apr_size_t inlen); + +/** + * @fn apr_status_t apr_evp_crypt_finish(apr_evp_crypt_t *, + * unsigned char *out, + * apr_size_t *outlen) + * @brief Encrypt final data block, write it to out. + * @note If necessary the final block will be written out after being + * padded. After this call, the context is cleaned and can be + * reused by apr_env_encrypt_init() or apr_env_decrypt_init(). + * @param evp The evp context to use. + * @param out Address of a buffer to which data will be written. + * @param outlen Length of the output will be written here. + * @return APR_EGENERAL if an error occurred. Returns APR_ENOTIMPL if + * not supported. + */ +APR_DECLARE(apr_status_t) apr_evp_crypt_finish(apr_evp_crypt_t *e, + unsigned char *out, + apr_size_t *outlen); + + +/** + * @fn apr_status_t apr_evp_crypt_cleanup(apr_evp_crypt_t *e) + * @brief Clean encryption / decryption context. + * @note After cleanup, a context is free to be reused if necessary. + * @param evp The evp context to use. + * @return Returns APR_ENOTIMPL if not supported. + */ +APR_DECLARE(apr_status_t) apr_evp_crypt_cleanup(apr_evp_crypt_t *e); + +/** + * @fn apr_status_t apr_evp_factory_cleanup(apr_evp_factory_t *f) + * @brief Clean encryption / decryption factory. + * @note After cleanup, a factory is free to be reused if necessary. + * @param f The factory to use. + * @return Returns APR_ENOTIMPL if not supported. + */ +APR_DECLARE(apr_status_t) apr_evp_factory_cleanup(apr_evp_factory_t *f); + + /** @} */ #ifdef __cplusplus } diff --git a/include/private/apr_ssl_openssl_private.h b/include/private/apr_ssl_openssl_private.h index af968d40..3ef3a982 100644 --- a/include/private/apr_ssl_openssl_private.h +++ b/include/private/apr_ssl_openssl_private.h @@ -32,6 +32,47 @@ struct apu_ssl_socket_data { int sslErr; /** SSL_get_error() code */ }; +typedef struct apu_evp_data apu_evp_data_t; + +/** + * EVP factory structure + */ +struct apu_evp_factory { + apr_pool_t *pool; /**< pool to use for memory allocations */ + apr_evp_factory_type_e purpose; /**< Purpose of the factory */ + apu_evp_data_t *evpData; /**< Pointer to implementation specific data */ +}; + +/** + * Define the cipher context structure used as a handle by + * the generic apu_evp_* functions. + */ +struct apu_evp_data { + const EVP_CIPHER *cipher; + const EVP_MD *md; + unsigned char salt[8]; + unsigned char key[EVP_MAX_KEY_LENGTH]; + unsigned char iv[EVP_MAX_IV_LENGTH]; + const char *privateKeyFilename; + const char *certificateFilename; +#if HAVE_DECL_EVP_PKEY_CTX_NEW + SSL_CTX *sslCtx; + SSL *ssl; + EVP_PKEY *pubkey; + EVP_PKEY *privkey; +#endif +}; + +struct apu_evp_crypt { + apr_pool_t *pool; + EVP_CIPHER_CTX *cipherCtx; +#if HAVE_DECL_EVP_PKEY_CTX_NEW + EVP_PKEY_CTX *pkeyCtx; +#endif + apr_evp_factory_type_e purpose; + apr_evp_crypt_type_e type; + apr_evp_crypt_key_e key; +}; #endif /* APU_HAVE_OPENSSL */ diff --git a/ssl/apr_ssl_openssl.c b/ssl/apr_ssl_openssl.c index 9e1258fb..480a3bd8 100644 --- a/ssl/apr_ssl_openssl.c +++ b/ssl/apr_ssl_openssl.c @@ -231,4 +231,298 @@ apr_status_t apu_ssl_raw_error(apr_ssl_socket_t *sock) return APR_SUCCESS; } +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); +} + +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); +} + +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_evp_factory_t *)apr_pcalloc(pool, sizeof(apr_evp_factory_t)); + if (!f) { + return APR_ENOMEM; + } + *newFactory = f; + f->pool = pool; + f->purpose = purpose; + + apu_evp_data_t *data = (apu_evp_data_t *)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) { + data->privkey = SSL_get_privatekey(data->ssl); + X509 *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; + } + OpenSSL_add_all_digests(); + 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; + +} + +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_evp_crypt_t *)apr_pcalloc(p, sizeof(apr_evp_crypt_t)); + } + (*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 = (EVP_CIPHER_CTX *)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; + +} + +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; + +} + +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 + diff --git a/ssl/apr_ssl_winsock.c b/ssl/apr_ssl_winsock.c index c43d9140..9a80bfe3 100644 --- a/ssl/apr_ssl_winsock.c +++ b/ssl/apr_ssl_winsock.c @@ -245,4 +245,53 @@ apr_status_t apu_ssl_raw_error(apr_ssl_socket_t *sock) return APR_SUCCESS; } +apr_status_t apr_evp_crypt_cleanup(apr_evp_crypt_t *e) +{ + return APR_ENOTIMPL; +} + +apr_status_t apr_evp_factory_cleanup(apr_evp_factory_t *f) +{ + return APR_ENOTIMPL; +} + +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) +{ + return APR_ENOTIMPL; +} + +apr_status_t 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) +{ + return APR_ENOTIMPL; +} + +apr_status_t apr_evp_crypt(apr_evp_crypt_t *, + unsigned char **out, + apr_size_t *outlen, + const unsigned char *in, + apr_size_t inlen) +{ + return APR_ENOTIMPL; +} + +apr_status_t apr_evp_crypt_finish(apr_evp_crypt_t *e, + unsigned char *out, + apr_size_t *outlen); +{ + return APR_ENOTIMPL; +} + #endif + |