diff options
Diffstat (limited to '')
-rw-r--r-- | modules/ssl/ssl_util.c | 485 |
1 files changed, 485 insertions, 0 deletions
diff --git a/modules/ssl/ssl_util.c b/modules/ssl/ssl_util.c new file mode 100644 index 0000000..c889295 --- /dev/null +++ b/modules/ssl/ssl_util.c @@ -0,0 +1,485 @@ +/* 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. + */ + +/* _ _ + * _ __ ___ ___ __| | ___ ___| | mod_ssl + * | '_ ` _ \ / _ \ / _` | / __/ __| | Apache Interface to OpenSSL + * | | | | | | (_) | (_| | \__ \__ \ | + * |_| |_| |_|\___/ \__,_|___|___/___/_| + * |_____| + * ssl_util.c + * Utility Functions + */ + /* ``Every day of my life + I am forced to add another + name to the list of people + who piss me off!'' + -- Calvin */ + +#include "ssl_private.h" +#include "ap_mpm.h" +#include "apr_thread_mutex.h" + +/* _________________________________________________________________ +** +** Utility Functions +** _________________________________________________________________ +*/ + +char *ssl_util_vhostid(apr_pool_t *p, server_rec *s) +{ + SSLSrvConfigRec *sc; + apr_port_t port; + + if (s->port != 0) + port = s->port; + else { + sc = mySrvConfig(s); + port = sc->enabled == TRUE ? DEFAULT_HTTPS_PORT : DEFAULT_HTTP_PORT; + } + + return apr_psprintf(p, "%s:%lu", s->server_hostname, (unsigned long)port); +} + +/* + * Return TRUE iff the given servername matches the server record when + * selecting virtual hosts. + */ +BOOL ssl_util_vhost_matches(const char *servername, server_rec *s) +{ + apr_array_header_t *names; + int i; + + /* check ServerName */ + if (!strcasecmp(servername, s->server_hostname)) { + return TRUE; + } + + /* + * if not matched yet, check ServerAlias entries + * (adapted from vhost.c:matches_aliases()) + */ + names = s->names; + if (names) { + char **name = (char **)names->elts; + for (i = 0; i < names->nelts; ++i) { + if (!name[i]) + continue; + if (!strcasecmp(servername, name[i])) { + return TRUE; + } + } + } + + /* if still no match, check ServerAlias entries with wildcards */ + names = s->wild_names; + if (names) { + char **name = (char **)names->elts; + for (i = 0; i < names->nelts; ++i) { + if (!name[i]) + continue; + if (!ap_strcasecmp_match(servername, name[i])) { + return TRUE; + } + } + } + + return FALSE; +} + +int modssl_request_is_tls(const request_rec *r, SSLConnRec **scout) +{ + SSLConnRec *sslconn = myConnConfig(r->connection); + SSLSrvConfigRec *sc = mySrvConfig(r->server); + + if (!(sslconn && sslconn->ssl) && r->connection->master) { + sslconn = myConnConfig(r->connection->master); + } + + if (sc->enabled == SSL_ENABLED_FALSE || !sslconn || !sslconn->ssl) + return 0; + + if (scout) *scout = sslconn; + + return 1; +} + +apr_file_t *ssl_util_ppopen(server_rec *s, apr_pool_t *p, const char *cmd, + const char * const *argv) +{ + apr_procattr_t *procattr; + apr_proc_t *proc; + + if (apr_procattr_create(&procattr, p) != APR_SUCCESS) + return NULL; + if (apr_procattr_io_set(procattr, APR_FULL_BLOCK, APR_FULL_BLOCK, + APR_FULL_BLOCK) != APR_SUCCESS) + return NULL; + if (apr_procattr_dir_set(procattr, + ap_make_dirstr_parent(p, cmd)) != APR_SUCCESS) + return NULL; + if (apr_procattr_cmdtype_set(procattr, APR_PROGRAM) != APR_SUCCESS) + return NULL; + proc = apr_pcalloc(p, sizeof(apr_proc_t)); + if (apr_proc_create(proc, cmd, argv, NULL, procattr, p) != APR_SUCCESS) + return NULL; + return proc->out; +} + +void ssl_util_ppclose(server_rec *s, apr_pool_t *p, apr_file_t *fp) +{ + apr_file_close(fp); + return; +} + +/* + * Run a filter program and read the first line of its stdout output + */ +char *ssl_util_readfilter(server_rec *s, apr_pool_t *p, const char *cmd, + const char * const *argv) +{ + static char buf[MAX_STRING_LEN]; + apr_file_t *fp; + apr_size_t nbytes = 1; + char c; + int k; + + if ((fp = ssl_util_ppopen(s, p, cmd, argv)) == NULL) + return NULL; + /* XXX: we are reading 1 byte at a time here */ + for (k = 0; apr_file_read(fp, &c, &nbytes) == APR_SUCCESS + && nbytes == 1 && (k < MAX_STRING_LEN-1) ; ) { + if (c == '\n' || c == '\r') + break; + buf[k++] = c; + } + buf[k] = NUL; + ssl_util_ppclose(s, p, fp); + + return buf; +} + +BOOL ssl_util_path_check(ssl_pathcheck_t pcm, const char *path, apr_pool_t *p) +{ + apr_finfo_t finfo; + + if (path == NULL) + return FALSE; + if (pcm & SSL_PCM_EXISTS && apr_stat(&finfo, path, + APR_FINFO_TYPE|APR_FINFO_SIZE, p) != 0) + return FALSE; + AP_DEBUG_ASSERT((pcm & SSL_PCM_EXISTS) || + !(pcm & (SSL_PCM_ISREG|SSL_PCM_ISDIR|SSL_PCM_ISNONZERO))); + if (pcm & SSL_PCM_ISREG && finfo.filetype != APR_REG) + return FALSE; + if (pcm & SSL_PCM_ISDIR && finfo.filetype != APR_DIR) + return FALSE; + if (pcm & SSL_PCM_ISNONZERO && finfo.size <= 0) + return FALSE; + return TRUE; +} + +/* Decrypted private keys are cached to survive restarts. The cached + * data must have lifetime of the process (hence malloc/free rather + * than pools), and uses raw DER since the EVP_PKEY structure + * internals may not survive across a module reload. */ +ssl_asn1_t *ssl_asn1_table_set(apr_hash_t *table, const char *key, + EVP_PKEY *pkey) +{ + apr_ssize_t klen = strlen(key); + ssl_asn1_t *asn1 = apr_hash_get(table, key, klen); + apr_size_t length = i2d_PrivateKey(pkey, NULL); + unsigned char *p; + + /* Re-use structure if cached previously. */ + if (asn1) { + if (asn1->nData != length) { + asn1->cpData = ap_realloc(asn1->cpData, length); + } + } + else { + asn1 = ap_malloc(sizeof(*asn1)); + asn1->source_mtime = 0; /* used as a note for encrypted private keys */ + asn1->cpData = ap_malloc(length); + + apr_hash_set(table, key, klen, asn1); + } + + asn1->nData = length; + p = asn1->cpData; + i2d_PrivateKey(pkey, &p); /* increases p by length */ + + return asn1; +} + +ssl_asn1_t *ssl_asn1_table_get(apr_hash_t *table, + const char *key) +{ + return (ssl_asn1_t *)apr_hash_get(table, key, APR_HASH_KEY_STRING); +} + +void ssl_asn1_table_unset(apr_hash_t *table, + const char *key) +{ + apr_ssize_t klen = strlen(key); + ssl_asn1_t *asn1 = apr_hash_get(table, key, klen); + + if (!asn1) { + return; + } + + if (asn1->cpData) { + free(asn1->cpData); + } + free(asn1); + + apr_hash_set(table, key, klen, NULL); +} + +#if APR_HAS_THREADS && MODSSL_USE_OPENSSL_PRE_1_1_API + +/* + * To ensure thread-safetyness in OpenSSL - work in progress + */ + +static apr_thread_mutex_t **lock_cs; +static int lock_num_locks; + +static void ssl_util_thr_lock(int mode, int type, + const char *file, int line) +{ + if (type < lock_num_locks) { + if (mode & CRYPTO_LOCK) { + apr_thread_mutex_lock(lock_cs[type]); + } + else { + apr_thread_mutex_unlock(lock_cs[type]); + } + } +} + +/* Dynamic lock structure */ +struct CRYPTO_dynlock_value { + apr_pool_t *pool; + const char* file; + int line; + apr_thread_mutex_t *mutex; +}; + +/* Global reference to the pool passed into ssl_util_thread_setup() */ +apr_pool_t *dynlockpool = NULL; + +/* + * Dynamic lock creation callback + */ +static struct CRYPTO_dynlock_value *ssl_dyn_create_function(const char *file, + int line) +{ + struct CRYPTO_dynlock_value *value; + apr_pool_t *p; + apr_status_t rv; + + /* + * We need a pool to allocate our mutex. Since we can't clear + * allocated memory from a pool, create a subpool that we can blow + * away in the destruction callback. + */ + apr_pool_create(&p, dynlockpool); + apr_pool_tag(p, "modssl_dynlock_value"); + ap_log_perror(file, line, APLOG_MODULE_INDEX, APLOG_TRACE1, 0, p, + "Creating dynamic lock"); + + value = apr_palloc(p, sizeof(struct CRYPTO_dynlock_value)); + value->pool = p; + /* Keep our own copy of the place from which we were created, + using our own pool. */ + value->file = apr_pstrdup(p, file); + value->line = line; + rv = apr_thread_mutex_create(&(value->mutex), APR_THREAD_MUTEX_DEFAULT, + p); + if (rv != APR_SUCCESS) { + ap_log_perror(file, line, APLOG_MODULE_INDEX, APLOG_ERR, rv, p, APLOGNO(02186) + "Failed to create thread mutex for dynamic lock"); + apr_pool_destroy(p); + return NULL; + } + return value; +} + +/* + * Dynamic locking and unlocking function + */ + +static void ssl_dyn_lock_function(int mode, struct CRYPTO_dynlock_value *l, + const char *file, int line) +{ + apr_status_t rv; + + if (mode & CRYPTO_LOCK) { + ap_log_perror(file, line, APLOG_MODULE_INDEX, APLOG_TRACE3, 0, l->pool, + "Acquiring mutex %s:%d", l->file, l->line); + rv = apr_thread_mutex_lock(l->mutex); + ap_log_perror(file, line, APLOG_MODULE_INDEX, APLOG_TRACE3, rv, l->pool, + "Mutex %s:%d acquired!", l->file, l->line); + } + else { + ap_log_perror(file, line, APLOG_MODULE_INDEX, APLOG_TRACE3, 0, l->pool, + "Releasing mutex %s:%d", l->file, l->line); + rv = apr_thread_mutex_unlock(l->mutex); + ap_log_perror(file, line, APLOG_MODULE_INDEX, APLOG_TRACE3, rv, l->pool, + "Mutex %s:%d released!", l->file, l->line); + } +} + +/* + * Dynamic lock destruction callback + */ +static void ssl_dyn_destroy_function(struct CRYPTO_dynlock_value *l, + const char *file, int line) +{ + apr_status_t rv; + + ap_log_perror(file, line, APLOG_MODULE_INDEX, APLOG_TRACE1, 0, l->pool, + "Destroying dynamic lock %s:%d", l->file, l->line); + rv = apr_thread_mutex_destroy(l->mutex); + if (rv != APR_SUCCESS) { + ap_log_perror(file, line, APLOG_MODULE_INDEX, APLOG_ERR, rv, l->pool, + APLOGNO(02192) "Failed to destroy mutex for dynamic " + "lock %s:%d", l->file, l->line); + } + + /* Trust that whomever owned the CRYPTO_dynlock_value we were + * passed has no future use for it... + */ + apr_pool_destroy(l->pool); +} + +#if OPENSSL_VERSION_NUMBER >= 0x10000000L + +static void ssl_util_thr_id(CRYPTO_THREADID *id) +{ + /* OpenSSL needs this to return an unsigned long. On OS/390, the pthread + * id is a structure twice that big. Use the TCB pointer instead as a + * unique unsigned long. + */ +#ifdef __MVS__ + struct PSA { + char unmapped[540]; /* PSATOLD is at offset 540 in the PSA */ + unsigned long PSATOLD; + } *psaptr = 0; /* PSA is at address 0 */ + + CRYPTO_THREADID_set_numeric(id, psaptr->PSATOLD); +#else + CRYPTO_THREADID_set_numeric(id, (unsigned long) apr_os_thread_current()); +#endif +} + +static apr_status_t ssl_util_thr_id_cleanup(void *old) +{ + CRYPTO_THREADID_set_callback(old); + return APR_SUCCESS; +} + +#else + +static unsigned long ssl_util_thr_id(void) +{ + /* OpenSSL needs this to return an unsigned long. On OS/390, the pthread + * id is a structure twice that big. Use the TCB pointer instead as a + * unique unsigned long. + */ +#ifdef __MVS__ + struct PSA { + char unmapped[540]; + unsigned long PSATOLD; + } *psaptr = 0; + + return psaptr->PSATOLD; +#else + return (unsigned long) apr_os_thread_current(); +#endif +} + +static apr_status_t ssl_util_thr_id_cleanup(void *old) +{ + CRYPTO_set_id_callback(old); + return APR_SUCCESS; +} + +#endif + +static apr_status_t ssl_util_thread_cleanup(void *data) +{ + CRYPTO_set_locking_callback(NULL); + + CRYPTO_set_dynlock_create_callback(NULL); + CRYPTO_set_dynlock_lock_callback(NULL); + CRYPTO_set_dynlock_destroy_callback(NULL); + + dynlockpool = NULL; + + /* Let the registered mutex cleanups do their own thing + */ + return APR_SUCCESS; +} + +void ssl_util_thread_setup(apr_pool_t *p) +{ + int i; + + lock_num_locks = CRYPTO_num_locks(); + lock_cs = apr_palloc(p, lock_num_locks * sizeof(*lock_cs)); + + for (i = 0; i < lock_num_locks; i++) { + apr_thread_mutex_create(&(lock_cs[i]), APR_THREAD_MUTEX_DEFAULT, p); + } + + CRYPTO_set_locking_callback(ssl_util_thr_lock); + + /* Set up dynamic locking scaffolding for OpenSSL to use at its + * convenience. + */ + dynlockpool = p; + CRYPTO_set_dynlock_create_callback(ssl_dyn_create_function); + CRYPTO_set_dynlock_lock_callback(ssl_dyn_lock_function); + CRYPTO_set_dynlock_destroy_callback(ssl_dyn_destroy_function); + + apr_pool_cleanup_register(p, NULL, ssl_util_thread_cleanup, + apr_pool_cleanup_null); +} + +void ssl_util_thread_id_setup(apr_pool_t *p) +{ +#if OPENSSL_VERSION_NUMBER >= 0x10000000L + CRYPTO_THREADID_set_callback(ssl_util_thr_id); +#else + CRYPTO_set_id_callback(ssl_util_thr_id); +#endif + apr_pool_cleanup_register(p, NULL, ssl_util_thr_id_cleanup, + apr_pool_cleanup_null); +} + +#endif /* #if APR_HAS_THREADS && MODSSL_USE_OPENSSL_PRE_1_1_API */ + +int modssl_is_engine_id(const char *name) +{ +#if defined(HAVE_OPENSSL_ENGINE_H) && defined(HAVE_ENGINE_INIT) + /* ### Can handle any other special ENGINE key names here? */ + return strncmp(name, "pkcs11:", 7) == 0; +#else + return 0; +#endif +} |