mirror of
https://github.com/apache/httpd.git
synced 2025-06-01 23:21:45 +03:00
git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@424823 13f79535-47bb-0310-9956-ffa450edef68
401 lines
10 KiB
C
401 lines
10 KiB
C
/* 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)
|
|
{
|
|
char *id;
|
|
SSLSrvConfigRec *sc;
|
|
char *host;
|
|
apr_port_t port;
|
|
|
|
host = s->server_hostname;
|
|
if (s->port != 0)
|
|
port = s->port;
|
|
else {
|
|
sc = mySrvConfig(s);
|
|
if (sc->enabled == TRUE)
|
|
port = DEFAULT_HTTPS_PORT;
|
|
else
|
|
port = DEFAULT_HTTP_PORT;
|
|
}
|
|
id = apr_psprintf(p, "%s:%lu", host, (unsigned long)port);
|
|
return id;
|
|
}
|
|
|
|
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;
|
|
if ((proc = (apr_proc_t *)apr_pcalloc(p, sizeof(apr_proc_t))) == NULL)
|
|
return NULL;
|
|
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;
|
|
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;
|
|
}
|
|
|
|
ssl_algo_t ssl_util_algotypeof(X509 *pCert, EVP_PKEY *pKey)
|
|
{
|
|
ssl_algo_t t;
|
|
EVP_PKEY *pFreeKey = NULL;
|
|
|
|
t = SSL_ALGO_UNKNOWN;
|
|
if (pCert != NULL)
|
|
pFreeKey = pKey = X509_get_pubkey(pCert);
|
|
if (pKey != NULL) {
|
|
switch (EVP_PKEY_key_type(pKey)) {
|
|
case EVP_PKEY_RSA:
|
|
t = SSL_ALGO_RSA;
|
|
break;
|
|
case EVP_PKEY_DSA:
|
|
t = SSL_ALGO_DSA;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
#ifdef OPENSSL_VERSION_NUMBER
|
|
/* Only refcounted in OpenSSL */
|
|
if (pFreeKey != NULL)
|
|
EVP_PKEY_free(pFreeKey);
|
|
#endif
|
|
return t;
|
|
}
|
|
|
|
char *ssl_util_algotypestr(ssl_algo_t t)
|
|
{
|
|
char *cp;
|
|
|
|
cp = "UNKNOWN";
|
|
switch (t) {
|
|
case SSL_ALGO_RSA:
|
|
cp = "RSA";
|
|
break;
|
|
case SSL_ALGO_DSA:
|
|
cp = "DSA";
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
return cp;
|
|
}
|
|
|
|
/*
|
|
* certain key and cert data needs to survive restarts,
|
|
* which are stored in the user data table of s->process->pool.
|
|
* to prevent "leaking" of this data, we use malloc/free
|
|
* rather than apr_palloc and these wrappers to help make sure
|
|
* we do not leak the malloc-ed data.
|
|
*/
|
|
unsigned char *ssl_asn1_table_set(apr_hash_t *table,
|
|
const char *key,
|
|
long int length)
|
|
{
|
|
apr_ssize_t klen = strlen(key);
|
|
ssl_asn1_t *asn1 = apr_hash_get(table, key, klen);
|
|
|
|
/*
|
|
* if a value for this key already exists,
|
|
* reuse as much of the already malloc-ed data
|
|
* as possible.
|
|
*/
|
|
if (asn1) {
|
|
if (asn1->nData != length) {
|
|
free(asn1->cpData); /* XXX: realloc? */
|
|
asn1->cpData = NULL;
|
|
}
|
|
}
|
|
else {
|
|
asn1 = malloc(sizeof(*asn1));
|
|
asn1->source_mtime = 0; /* used as a note for encrypted private keys */
|
|
asn1->cpData = NULL;
|
|
}
|
|
|
|
asn1->nData = length;
|
|
if (!asn1->cpData) {
|
|
asn1->cpData = malloc(length);
|
|
}
|
|
|
|
apr_hash_set(table, key, klen, asn1);
|
|
|
|
return asn1->cpData; /* caller will assign a value to this */
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
static const char *ssl_asn1_key_types[] = {"RSA", "DSA"};
|
|
|
|
const char *ssl_asn1_keystr(int keytype)
|
|
{
|
|
if (keytype >= SSL_AIDX_MAX) {
|
|
return NULL;
|
|
}
|
|
|
|
return ssl_asn1_key_types[keytype];
|
|
}
|
|
|
|
const char *ssl_asn1_table_keyfmt(apr_pool_t *p,
|
|
const char *id,
|
|
int keytype)
|
|
{
|
|
const char *keystr = ssl_asn1_keystr(keytype);
|
|
|
|
return apr_pstrcat(p, id, ":", keystr, NULL);
|
|
}
|
|
|
|
STACK_OF(X509) *ssl_read_pkcs7(server_rec *s, const char *pkcs7)
|
|
{
|
|
PKCS7 *p7;
|
|
STACK_OF(X509) *certs = NULL;
|
|
FILE *f;
|
|
|
|
f = fopen(pkcs7, "r");
|
|
if (!f) {
|
|
ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, "Can't open %s", pkcs7);
|
|
ssl_die();
|
|
}
|
|
|
|
p7 = PEM_read_PKCS7(f, NULL, NULL, NULL);
|
|
if (!p7) {
|
|
ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, s,
|
|
"Can't read PKCS7 object %s", pkcs7);
|
|
ssl_log_ssl_error(APLOG_MARK, APLOG_CRIT, s);
|
|
exit(1);
|
|
}
|
|
|
|
switch (OBJ_obj2nid(p7->type)) {
|
|
case NID_pkcs7_signed:
|
|
certs = p7->d.sign->cert;
|
|
break;
|
|
|
|
case NID_pkcs7_signedAndEnveloped:
|
|
certs = p7->d.signed_and_enveloped->cert;
|
|
break;
|
|
|
|
default:
|
|
ap_log_error(APLOG_MARK, APLOG_CRIT|APLOG_NOERRNO, 0, s,
|
|
"Don't understand PKCS7 file %s", pkcs7);
|
|
ssl_die();
|
|
}
|
|
|
|
if (!certs) {
|
|
ap_log_error(APLOG_MARK, APLOG_CRIT|APLOG_NOERRNO, 0, s,
|
|
"No certificates in %s", pkcs7);
|
|
ssl_die();
|
|
}
|
|
|
|
fclose(f);
|
|
|
|
return certs;
|
|
}
|
|
|
|
|
|
#if APR_HAS_THREADS
|
|
/*
|
|
* To ensure thread-safetyness in OpenSSL - work in progress
|
|
*/
|
|
|
|
static apr_thread_mutex_t **lock_cs;
|
|
static int lock_num_locks;
|
|
|
|
#ifdef HAVE_SSLC
|
|
#if SSLC_VERSION_NUMBER >= 0x2000
|
|
static int ssl_util_thr_lock(int mode, int type,
|
|
char *file, int line)
|
|
#else
|
|
static void ssl_util_thr_lock(int mode, int type,
|
|
char *file, int line)
|
|
#endif
|
|
#else
|
|
static void ssl_util_thr_lock(int mode, int type,
|
|
const char *file, int line)
|
|
#endif
|
|
{
|
|
if (type < lock_num_locks) {
|
|
if (mode & CRYPTO_LOCK) {
|
|
apr_thread_mutex_lock(lock_cs[type]);
|
|
}
|
|
else {
|
|
apr_thread_mutex_unlock(lock_cs[type]);
|
|
}
|
|
#ifdef HAVE_SSLC
|
|
#if SSLC_VERSION_NUMBER >= 0x2000
|
|
return 1;
|
|
}
|
|
else {
|
|
return -1;
|
|
#endif
|
|
#endif
|
|
}
|
|
}
|
|
|
|
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_thread_cleanup(void *data)
|
|
{
|
|
CRYPTO_set_locking_callback(NULL);
|
|
CRYPTO_set_id_callback(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_id_callback(ssl_util_thr_id);
|
|
|
|
CRYPTO_set_locking_callback(ssl_util_thr_lock);
|
|
|
|
apr_pool_cleanup_register(p, NULL, ssl_util_thread_cleanup,
|
|
apr_pool_cleanup_null);
|
|
}
|
|
#endif
|