summaryrefslogtreecommitdiffstats
path: root/modules/ssl/ssl_util.c
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--modules/ssl/ssl_util.c485
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
+}