| /* 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); | |
| int derlen = i2d_PrivateKey(pkey, NULL); | |
| apr_size_t length; | |
| unsigned char *p; | |
| ap_assert(derlen > 0); /* should never happen for any loaded key */ | |
| length = (apr_size_t)derlen; | |
| /* 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); | |
| } | |
| #if APR_HAS_THREADS && MODSSL_USE_OPENSSL_PRE_1_1_API | |
| /* | |
| * To ensure thread-safetyness in OpenSSL - work in progress | |
| */ | |
| #if (OPENSSL_VERSION_NUMBER >= 0x10000000L) && \ | |
| (_WIN32 || __BEOS__ || AP_OPENSSL_USE_ERRNO_THREADID) | |
| /* Windows and BeOS can use the default THREADID callback shipped with OpenSSL | |
| * 1.0.x, as can any platform with a thread-safe errno. */ | |
| # define DEFAULT_THREADID_IS_SAFE 1 | |
| #endif | |
| 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 DEFAULT_THREADID_IS_SAFE | |
| /* We don't need to set up a threadid callback on this platform. */ | |
| void ssl_util_thread_id_setup(apr_pool_t *p) | |
| { | |
| ap_log_perror(APLOG_MARK, APLOG_NOTICE, 0, p, APLOGNO(10027) | |
| "using builtin threadid callback for OpenSSL"); | |
| } | |
| #else | |
| /** | |
| * Used by both versions of ssl_util_thr_id(). Returns an unsigned long that | |
| * should be unique to the currently executing thread. | |
| */ | |
| static unsigned long ssl_util_thr_id_internal(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]; /* PSATOLD is at offset 540 in the PSA */ | |
| unsigned long PSATOLD; | |
| } *psaptr = 0; /* PSA is at address 0 */ | |
| return psaptr->PSATOLD; | |
| #else | |
| return (unsigned long) apr_os_thread_current(); | |
| #endif | |
| } | |
| #if HAVE_CRYPTO_SET_ID_CALLBACK | |
| static unsigned long ssl_util_thr_id(void) | |
| { | |
| return ssl_util_thr_id_internal(); | |
| } | |
| #else | |
| static void ssl_util_thr_id(CRYPTO_THREADID *id) | |
| { | |
| /* XXX Ideally we would be using the _set_pointer() callback on platforms | |
| * that have a pointer-based thread "identity". But this entire API is | |
| * fraught with problems (see PR60947) and has been removed completely in | |
| * OpenSSL 1.1.0, so I'm not too invested in fixing it right now. */ | |
| CRYPTO_THREADID_set_numeric(id, ssl_util_thr_id_internal()); | |
| } | |
| #endif /* HAVE_CRYPTO_SET_ID_CALLBACK */ | |
| static apr_status_t ssl_util_thr_id_cleanup(void *old) | |
| { | |
| #if HAVE_CRYPTO_SET_ID_CALLBACK | |
| CRYPTO_set_id_callback(old); | |
| #else | |
| /* XXX This does nothing. The new-style THREADID callback is write-once. */ | |
| CRYPTO_THREADID_set_callback(old); | |
| #endif | |
| return APR_SUCCESS; | |
| } | |
| void ssl_util_thread_id_setup(apr_pool_t *p) | |
| { | |
| #if HAVE_CRYPTO_SET_ID_CALLBACK | |
| ap_log_perror(APLOG_MARK, APLOG_NOTICE, 0, p, APLOGNO(10028) | |
| "using deprecated CRYPTO_set_id_callback for OpenSSL"); | |
| /* This API is deprecated, but we prefer it to its replacement since it | |
| * allows us to unset the callback when this module is being unloaded. */ | |
| CRYPTO_set_id_callback(ssl_util_thr_id); | |
| #else | |
| ap_log_perror(APLOG_MARK, APLOG_NOTICE, 0, p, APLOGNO(10029) | |
| "using dangerous CRYPTO_THREADID_set_callback for OpenSSL"); | |
| /* This is a last resort. We can only set this once, which means that we'd | |
| * better not get loaded into a different address during a restart. See | |
| * PR60947. */ | |
| CRYPTO_THREADID_set_callback(ssl_util_thr_id); | |
| if (CRYPTO_THREADID_get_callback() != ssl_util_thr_id) { | |
| /* XXX Unfortunately this doesn't seem to get logged unless you're | |
| * running in one-process mode, due to PR60999. */ | |
| ap_log_perror(APLOG_MARK, APLOG_CRIT, 0, p, APLOGNO(10030) | |
| "OpenSSL's THREADID callback was already set to another address, " | |
| "and the server is probably going to crash. See bug #60947 for " | |
| "more details. You may need to recompile or upgrade either OpenSSL " | |
| "or httpd."); | |
| } | |
| #endif | |
| apr_pool_cleanup_register(p, NULL, ssl_util_thr_id_cleanup, | |
| apr_pool_cleanup_null); | |
| } | |
| #endif /* DEFAULT_THREADID_IS_SAFE */ | |
| 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); | |
| } | |
| #endif /* #if APR_HAS_THREADS && MODSSL_USE_OPENSSL_PRE_1_1_API */ | |
| int modssl_is_engine_id(const char *name) | |
| { | |
| #if MODSSL_HAVE_ENGINE_API || MODSSL_HAVE_OPENSSL_STORE | |
| /* ### Can handle any other special ENGINE key names here? */ | |
| return strncmp(name, "pkcs11:", 7) == 0; | |
| #else | |
| return 0; | |
| #endif | |
| } |