1/* Licensed to the Apache Software Foundation (ASF) under one or more
2 * contributor license agreements.  See the NOTICE file distributed with
3 * this work for additional information regarding copyright ownership.
4 * The ASF licenses this file to You under the Apache License, Version 2.0
5 * (the "License"); you may not use this file except in compliance with
6 * the License.  You may obtain a copy of the License at
7 *
8 *     http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17/*                      _             _
18 *  _ __ ___   ___   __| |    ___ ___| |  mod_ssl
19 * | '_ ` _ \ / _ \ / _` |   / __/ __| |  Apache Interface to OpenSSL
20 * | | | | | | (_) | (_| |   \__ \__ \ |
21 * |_| |_| |_|\___/ \__,_|___|___/___/_|
22 *                      |_____|
23 *  ssl_util.c
24 *  Utility Functions
25 */
26                             /* ``Every day of my life
27                                  I am forced to add another
28                                  name to the list of people
29                                  who piss me off!''
30                                            -- Calvin          */
31
32#include "ssl_private.h"
33#include "ap_mpm.h"
34#include "apr_thread_mutex.h"
35
36/*  _________________________________________________________________
37**
38**  Utility Functions
39**  _________________________________________________________________
40*/
41
42char *ssl_util_vhostid(apr_pool_t *p, server_rec *s)
43{
44    char *id;
45    SSLSrvConfigRec *sc;
46    char *host;
47    apr_port_t port;
48
49    host = s->server_hostname;
50    if (s->port != 0)
51        port = s->port;
52    else {
53        sc = mySrvConfig(s);
54        if (sc->enabled == TRUE)
55            port = DEFAULT_HTTPS_PORT;
56        else
57            port = DEFAULT_HTTP_PORT;
58    }
59    id = apr_psprintf(p, "%s:%lu", host, (unsigned long)port);
60    return id;
61}
62
63apr_file_t *ssl_util_ppopen(server_rec *s, apr_pool_t *p, const char *cmd,
64                            const char * const *argv)
65{
66    apr_procattr_t *procattr;
67    apr_proc_t *proc;
68
69    if (apr_procattr_create(&procattr, p) != APR_SUCCESS)
70        return NULL;
71    if (apr_procattr_io_set(procattr, APR_FULL_BLOCK, APR_FULL_BLOCK,
72                            APR_FULL_BLOCK) != APR_SUCCESS)
73        return NULL;
74    if (apr_procattr_dir_set(procattr,
75                             ap_make_dirstr_parent(p, cmd)) != APR_SUCCESS)
76        return NULL;
77    if (apr_procattr_cmdtype_set(procattr, APR_PROGRAM) != APR_SUCCESS)
78        return NULL;
79    if ((proc = (apr_proc_t *)apr_pcalloc(p, sizeof(apr_proc_t))) == NULL)
80        return NULL;
81    if (apr_proc_create(proc, cmd, argv, NULL, procattr, p) != APR_SUCCESS)
82        return NULL;
83    return proc->out;
84}
85
86void ssl_util_ppclose(server_rec *s, apr_pool_t *p, apr_file_t *fp)
87{
88    apr_file_close(fp);
89    return;
90}
91
92/*
93 * Run a filter program and read the first line of its stdout output
94 */
95char *ssl_util_readfilter(server_rec *s, apr_pool_t *p, const char *cmd,
96                          const char * const *argv)
97{
98    static char buf[MAX_STRING_LEN];
99    apr_file_t *fp;
100    apr_size_t nbytes = 1;
101    char c;
102    int k;
103
104    if ((fp = ssl_util_ppopen(s, p, cmd, argv)) == NULL)
105        return NULL;
106    /* XXX: we are reading 1 byte at a time here */
107    for (k = 0; apr_file_read(fp, &c, &nbytes) == APR_SUCCESS
108                && nbytes == 1 && (k < MAX_STRING_LEN-1)     ; ) {
109        if (c == '\n' || c == '\r')
110            break;
111        buf[k++] = c;
112    }
113    buf[k] = NUL;
114    ssl_util_ppclose(s, p, fp);
115
116    return buf;
117}
118
119BOOL ssl_util_path_check(ssl_pathcheck_t pcm, const char *path, apr_pool_t *p)
120{
121    apr_finfo_t finfo;
122
123    if (path == NULL)
124        return FALSE;
125    if (pcm & SSL_PCM_EXISTS && apr_stat(&finfo, path,
126                                APR_FINFO_TYPE|APR_FINFO_SIZE, p) != 0)
127        return FALSE;
128    if (pcm & SSL_PCM_ISREG && finfo.filetype != APR_REG)
129        return FALSE;
130    if (pcm & SSL_PCM_ISDIR && finfo.filetype != APR_DIR)
131        return FALSE;
132    if (pcm & SSL_PCM_ISNONZERO && finfo.size <= 0)
133        return FALSE;
134    return TRUE;
135}
136
137ssl_algo_t ssl_util_algotypeof(X509 *pCert, EVP_PKEY *pKey)
138{
139    ssl_algo_t t;
140    EVP_PKEY *pFreeKey = NULL;
141
142    t = SSL_ALGO_UNKNOWN;
143    if (pCert != NULL)
144        pFreeKey = pKey = X509_get_pubkey(pCert);
145    if (pKey != NULL) {
146        switch (EVP_PKEY_key_type(pKey)) {
147            case EVP_PKEY_RSA:
148                t = SSL_ALGO_RSA;
149                break;
150            case EVP_PKEY_DSA:
151                t = SSL_ALGO_DSA;
152                break;
153#ifndef OPENSSL_NO_EC
154            case EVP_PKEY_EC:
155                t = SSL_ALGO_ECC;
156                break;
157#endif
158            default:
159                break;
160        }
161    }
162#ifdef OPENSSL_VERSION_NUMBER
163    /* Only refcounted in OpenSSL */
164    if (pFreeKey != NULL)
165        EVP_PKEY_free(pFreeKey);
166#endif
167    return t;
168}
169
170char *ssl_util_algotypestr(ssl_algo_t t)
171{
172    char *cp;
173
174    cp = "UNKNOWN";
175    switch (t) {
176        case SSL_ALGO_RSA:
177            cp = "RSA";
178            break;
179        case SSL_ALGO_DSA:
180            cp = "DSA";
181            break;
182#ifndef OPENSSL_NO_EC
183        case SSL_ALGO_ECC:
184            cp = "ECC";
185            break;
186#endif
187        default:
188            break;
189    }
190    return cp;
191}
192
193/*
194 * certain key and cert data needs to survive restarts,
195 * which are stored in the user data table of s->process->pool.
196 * to prevent "leaking" of this data, we use malloc/free
197 * rather than apr_palloc and these wrappers to help make sure
198 * we do not leak the malloc-ed data.
199 */
200unsigned char *ssl_asn1_table_set(apr_hash_t *table,
201                                  const char *key,
202                                  long int length)
203{
204    apr_ssize_t klen = strlen(key);
205    ssl_asn1_t *asn1 = apr_hash_get(table, key, klen);
206
207    /*
208     * if a value for this key already exists,
209     * reuse as much of the already malloc-ed data
210     * as possible.
211     */
212    if (asn1) {
213        if (asn1->nData != length) {
214            free(asn1->cpData); /* XXX: realloc? */
215            asn1->cpData = NULL;
216        }
217    }
218    else {
219        asn1 = malloc(sizeof(*asn1));
220        asn1->source_mtime = 0; /* used as a note for encrypted private keys */
221        asn1->cpData = NULL;
222    }
223
224    asn1->nData = length;
225    if (!asn1->cpData) {
226        asn1->cpData = malloc(length);
227    }
228
229    apr_hash_set(table, key, klen, asn1);
230
231    return asn1->cpData; /* caller will assign a value to this */
232}
233
234ssl_asn1_t *ssl_asn1_table_get(apr_hash_t *table,
235                               const char *key)
236{
237    return (ssl_asn1_t *)apr_hash_get(table, key, APR_HASH_KEY_STRING);
238}
239
240void ssl_asn1_table_unset(apr_hash_t *table,
241                          const char *key)
242{
243    apr_ssize_t klen = strlen(key);
244    ssl_asn1_t *asn1 = apr_hash_get(table, key, klen);
245
246    if (!asn1) {
247        return;
248    }
249
250    if (asn1->cpData) {
251        free(asn1->cpData);
252    }
253    free(asn1);
254
255    apr_hash_set(table, key, klen, NULL);
256}
257
258#ifndef OPENSSL_NO_EC
259static const char *ssl_asn1_key_types[] = {"RSA", "DSA", "ECC"};
260#else
261static const char *ssl_asn1_key_types[] = {"RSA", "DSA"};
262#endif
263
264const char *ssl_asn1_keystr(int keytype)
265{
266    if (keytype >= SSL_AIDX_MAX) {
267        return NULL;
268    }
269
270    return ssl_asn1_key_types[keytype];
271}
272
273const char *ssl_asn1_table_keyfmt(apr_pool_t *p,
274                                  const char *id,
275                                  int keytype)
276{
277    const char *keystr = ssl_asn1_keystr(keytype);
278
279    return apr_pstrcat(p, id, ":", keystr, NULL);
280}
281
282
283#if APR_HAS_THREADS
284/*
285 * To ensure thread-safetyness in OpenSSL - work in progress
286 */
287
288static apr_thread_mutex_t **lock_cs;
289static int                  lock_num_locks;
290
291#ifdef HAVE_SSLC
292#if SSLC_VERSION_NUMBER >= 0x2000
293static int ssl_util_thr_lock(int mode, int type,
294                             char *file, int line)
295#else
296static void ssl_util_thr_lock(int mode, int type,
297                              char *file, int line)
298#endif
299#else
300static void ssl_util_thr_lock(int mode, int type,
301                              const char *file, int line)
302#endif
303{
304    if (type < lock_num_locks) {
305        if (mode & CRYPTO_LOCK) {
306            apr_thread_mutex_lock(lock_cs[type]);
307        }
308        else {
309            apr_thread_mutex_unlock(lock_cs[type]);
310        }
311#ifdef HAVE_SSLC
312#if SSLC_VERSION_NUMBER >= 0x2000
313        return 1;
314    }
315    else {
316        return -1;
317#endif
318#endif
319    }
320}
321
322/* Dynamic lock structure */
323struct CRYPTO_dynlock_value {
324    apr_pool_t *pool;
325    const char* file;
326    int line;
327    apr_thread_mutex_t *mutex;
328};
329
330/* Global reference to the pool passed into ssl_util_thread_setup() */
331apr_pool_t *dynlockpool = NULL;
332
333/*
334 * Dynamic lock creation callback
335 */
336static struct CRYPTO_dynlock_value *ssl_dyn_create_function(const char *file,
337                                                     int line)
338{
339    struct CRYPTO_dynlock_value *value;
340    apr_pool_t *p;
341    apr_status_t rv;
342
343    /*
344     * We need a pool to allocate our mutex.  Since we can't clear
345     * allocated memory from a pool, create a subpool that we can blow
346     * away in the destruction callback.
347     */
348    rv = apr_pool_create(&p, dynlockpool);
349    if (rv != APR_SUCCESS) {
350        ap_log_perror(file, line, APLOG_ERR, rv, dynlockpool,
351                       "Failed to create subpool for dynamic lock");
352        return NULL;
353    }
354
355    ap_log_perror(file, line, APLOG_DEBUG, 0, p,
356                  "Creating dynamic lock");
357
358    value = (struct CRYPTO_dynlock_value *)apr_palloc(p,
359                                                      sizeof(struct CRYPTO_dynlock_value));
360    if (!value) {
361        ap_log_perror(file, line, APLOG_ERR, 0, p,
362                      "Failed to allocate dynamic lock structure");
363        return NULL;
364    }
365
366    value->pool = p;
367    /* Keep our own copy of the place from which we were created,
368       using our own pool. */
369    value->file = apr_pstrdup(p, file);
370    value->line = line;
371    rv = apr_thread_mutex_create(&(value->mutex), APR_THREAD_MUTEX_DEFAULT,
372                                p);
373    if (rv != APR_SUCCESS) {
374        ap_log_perror(file, line, APLOG_ERR, rv, p,
375                      "Failed to create thread mutex for dynamic lock");
376        apr_pool_destroy(p);
377        return NULL;
378    }
379    return value;
380}
381
382/*
383 * Dynamic locking and unlocking function
384 */
385
386static void ssl_dyn_lock_function(int mode, struct CRYPTO_dynlock_value *l,
387                           const char *file, int line)
388{
389    apr_status_t rv;
390
391    if (mode & CRYPTO_LOCK) {
392        ap_log_perror(file, line, APLOG_DEBUG, 0, l->pool,
393                      "Acquiring mutex %s:%d", l->file, l->line);
394        rv = apr_thread_mutex_lock(l->mutex);
395        ap_log_perror(file, line, APLOG_DEBUG, rv, l->pool,
396                      "Mutex %s:%d acquired!", l->file, l->line);
397    }
398    else {
399        ap_log_perror(file, line, APLOG_DEBUG, 0, l->pool,
400                      "Releasing mutex %s:%d", l->file, l->line);
401        rv = apr_thread_mutex_unlock(l->mutex);
402        ap_log_perror(file, line, APLOG_DEBUG, rv, l->pool,
403                      "Mutex %s:%d released!", l->file, l->line);
404    }
405}
406
407/*
408 * Dynamic lock destruction callback
409 */
410static void ssl_dyn_destroy_function(struct CRYPTO_dynlock_value *l,
411                          const char *file, int line)
412{
413    apr_status_t rv;
414
415    ap_log_perror(file, line, APLOG_DEBUG, 0, l->pool,
416                  "Destroying dynamic lock %s:%d", l->file, l->line);
417    rv = apr_thread_mutex_destroy(l->mutex);
418    if (rv != APR_SUCCESS) {
419        ap_log_perror(file, line, APLOG_ERR, rv, l->pool,
420                      "Failed to destroy mutex for dynamic lock %s:%d",
421                      l->file, l->line);
422    }
423
424    /* Trust that whomever owned the CRYPTO_dynlock_value we were
425     * passed has no future use for it...
426     */
427    apr_pool_destroy(l->pool);
428}
429
430static unsigned long ssl_util_thr_id(void)
431{
432    /* OpenSSL needs this to return an unsigned long.  On OS/390, the pthread
433     * id is a structure twice that big.  Use the TCB pointer instead as a
434     * unique unsigned long.
435     */
436#ifdef __MVS__
437    struct PSA {
438        char unmapped[540];
439        unsigned long PSATOLD;
440    } *psaptr = 0;
441
442    return psaptr->PSATOLD;
443#else
444    return (unsigned long) apr_os_thread_current();
445#endif
446}
447
448static apr_status_t ssl_util_thread_cleanup(void *data)
449{
450    CRYPTO_set_locking_callback(NULL);
451    CRYPTO_set_id_callback(NULL);
452
453    CRYPTO_set_dynlock_create_callback(NULL);
454    CRYPTO_set_dynlock_lock_callback(NULL);
455    CRYPTO_set_dynlock_destroy_callback(NULL);
456
457    dynlockpool = NULL;
458
459    /* Let the registered mutex cleanups do their own thing
460     */
461    return APR_SUCCESS;
462}
463
464void ssl_util_thread_setup(apr_pool_t *p)
465{
466    int i;
467
468    lock_num_locks = CRYPTO_num_locks();
469    lock_cs = apr_palloc(p, lock_num_locks * sizeof(*lock_cs));
470
471    for (i = 0; i < lock_num_locks; i++) {
472        apr_thread_mutex_create(&(lock_cs[i]), APR_THREAD_MUTEX_DEFAULT, p);
473    }
474
475    CRYPTO_set_id_callback(ssl_util_thr_id);
476
477    CRYPTO_set_locking_callback(ssl_util_thr_lock);
478
479    /* Set up dynamic locking scaffolding for OpenSSL to use at its
480     * convenience.
481     */
482    dynlockpool = p;
483    CRYPTO_set_dynlock_create_callback(ssl_dyn_create_function);
484    CRYPTO_set_dynlock_lock_callback(ssl_dyn_lock_function);
485    CRYPTO_set_dynlock_destroy_callback(ssl_dyn_destroy_function);
486
487    apr_pool_cleanup_register(p, NULL, ssl_util_thread_cleanup,
488                                       apr_pool_cleanup_null);
489}
490#endif
491