diff options
Diffstat (limited to 'modules/ssl/ssl_engine_vars.c')
-rw-r--r-- | modules/ssl/ssl_engine_vars.c | 615 |
1 files changed, 615 insertions, 0 deletions
diff --git a/modules/ssl/ssl_engine_vars.c b/modules/ssl/ssl_engine_vars.c new file mode 100644 index 0000000000..c0755a5ec8 --- /dev/null +++ b/modules/ssl/ssl_engine_vars.c @@ -0,0 +1,615 @@ +/* _ _ +** _ __ ___ ___ __| | ___ ___| | mod_ssl +** | '_ ` _ \ / _ \ / _` | / __/ __| | Apache Interface to OpenSSL +** | | | | | | (_) | (_| | \__ \__ \ | www.modssl.org +** |_| |_| |_|\___/ \__,_|___|___/___/_| ftp.modssl.org +** |_____| +** ssl_engine_vars.c +** Variable Lookup Facility +*/ + +/* ==================================================================== + * Copyright (c) 1998-2001 Ralf S. Engelschall. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. All advertising materials mentioning features or use of this + * software must display the following acknowledgment: + * "This product includes software developed by + * Ralf S. Engelschall <rse@engelschall.com> for use in the + * mod_ssl project (http://www.modssl.org/)." + * + * 4. The names "mod_ssl" must not be used to endorse or promote + * products derived from this software without prior written + * permission. For written permission, please contact + * rse@engelschall.com. + * + * 5. Products derived from this software may not be called "mod_ssl" + * nor may "mod_ssl" appear in their names without prior + * written permission of Ralf S. Engelschall. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by + * Ralf S. Engelschall <rse@engelschall.com> for use in the + * mod_ssl project (http://www.modssl.org/)." + * + * THIS SOFTWARE IS PROVIDED BY RALF S. ENGELSCHALL ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL RALF S. ENGELSCHALL OR + * HIS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * ==================================================================== + */ + /* ``Those of you who think they + know everything are very annoying + to those of us who do.'' + -- Unknown */ +#include "mod_ssl.h" + + +/* _________________________________________________________________ +** +** Variable Lookup +** _________________________________________________________________ +*/ + +static char *ssl_var_lookup_header(pool *p, request_rec *r, const char *name); +static char *ssl_var_lookup_ssl(pool *p, conn_rec *c, char *var); +static char *ssl_var_lookup_ssl_cert(pool *p, X509 *xs, char *var); +static char *ssl_var_lookup_ssl_cert_dn(pool *p, X509_NAME *xsname, char *var); +static char *ssl_var_lookup_ssl_cert_valid(pool *p, ASN1_UTCTIME *tm); +static char *ssl_var_lookup_ssl_cert_serial(pool *p, X509 *xs); +static char *ssl_var_lookup_ssl_cert_chain(pool *p, STACK_OF(X509) *sk, char *var); +static char *ssl_var_lookup_ssl_cert_PEM(pool *p, X509 *xs); +static char *ssl_var_lookup_ssl_cert_verify(pool *p, conn_rec *c); +static char *ssl_var_lookup_ssl_cipher(pool *p, conn_rec *c, char *var); +static void ssl_var_lookup_ssl_cipher_bits(SSL *ssl, int *usekeysize, int *algkeysize); +static char *ssl_var_lookup_ssl_version(pool *p, char *var); + +void ssl_var_register(void) +{ + ap_hook_configure("ap::mod_ssl::var_lookup", + AP_HOOK_SIG6(ptr,ptr,ptr,ptr,ptr,ptr), AP_HOOK_DECLINE(NULL)); + ap_hook_register("ap::mod_ssl::var_lookup", + ssl_var_lookup, AP_HOOK_NOCTX); + return; +} + +void ssl_var_unregister(void) +{ + ap_hook_unregister("ap::mod_ssl::var_lookup", ssl_var_lookup); + return; +} + +char *ssl_var_lookup(pool *p, server_rec *s, conn_rec *c, request_rec *r, char *var) +{ + SSLModConfigRec *mc = myModConfig(); + char *result; + BOOL resdup; + time_t tc; + struct tm *tm; + + result = NULL; + resdup = TRUE; + + /* + * When no pool is given try to find one + */ + if (p == NULL) { + if (r != NULL) + p = r->pool; + else if (c != NULL) + p = c->pool; + else + p = mc->pPool; + } + + /* + * Request dependent stuff + */ + if (r != NULL) { + if (strcEQ(var, "HTTP_USER_AGENT")) + result = ssl_var_lookup_header(p, r, "User-Agent"); + else if (strcEQ(var, "HTTP_REFERER")) + result = ssl_var_lookup_header(p, r, "Referer"); + else if (strcEQ(var, "HTTP_COOKIE")) + result = ssl_var_lookup_header(p, r, "Cookie"); + else if (strcEQ(var, "HTTP_FORWARDED")) + result = ssl_var_lookup_header(p, r, "Forwarded"); + else if (strcEQ(var, "HTTP_HOST")) + result = ssl_var_lookup_header(p, r, "Host"); + else if (strcEQ(var, "HTTP_PROXY_CONNECTION")) + result = ssl_var_lookup_header(p, r, "Proxy-Connection"); + else if (strcEQ(var, "HTTP_ACCEPT")) + result = ssl_var_lookup_header(p, r, "Accept"); + else if (strlen(var) > 5 && strcEQn(var, "HTTP:", 5)) + /* all other headers from which we are still not know about */ + result = ssl_var_lookup_header(p, r, var+5); + else if (strcEQ(var, "THE_REQUEST")) + result = r->the_request; + else if (strcEQ(var, "REQUEST_METHOD")) + result = (char *)(r->method); + else if (strcEQ(var, "REQUEST_SCHEME")) + result = ap_http_method(r); + else if (strcEQ(var, "REQUEST_URI")) + result = r->uri; + else if (strcEQ(var, "SCRIPT_FILENAME") || + strcEQ(var, "REQUEST_FILENAME")) + result = r->filename; + else if (strcEQ(var, "PATH_INFO")) + result = r->path_info; + else if (strcEQ(var, "QUERY_STRING")) + result = r->args; + else if (strcEQ(var, "REMOTE_HOST")) + result = (char *)ap_get_remote_host(r->connection, + r->per_dir_config, REMOTE_NAME); + else if (strcEQ(var, "REMOTE_IDENT")) + result = (char *)ap_get_remote_logname(r); + else if (strcEQ(var, "IS_SUBREQ")) + result = (r->main != NULL ? "true" : "false"); + else if (strcEQ(var, "DOCUMENT_ROOT")) + result = (char *)ap_document_root(r); + else if (strcEQ(var, "SERVER_ADMIN")) + result = r->server->server_admin; + else if (strcEQ(var, "SERVER_NAME")) + result = (char *)ap_get_server_name(r); + else if (strcEQ(var, "SERVER_PORT")) + result = ap_psprintf(p, "%u", ap_get_server_port(r)); + else if (strcEQ(var, "SERVER_PROTOCOL")) + result = r->protocol; + } + + /* + * Connection stuff + */ + if (result == NULL && c != NULL) { + if (strcEQ(var, "REMOTE_ADDR")) + result = c->remote_ip; + else if (strcEQ(var, "REMOTE_USER")) + result = c->user; + else if (strcEQ(var, "AUTH_TYPE")) + result = c->ap_auth_type; + else if (strlen(var) > 4 && strcEQn(var, "SSL_", 4)) + result = ssl_var_lookup_ssl(p, c, var+4); + else if (strcEQ(var, "HTTPS")) { + if (ap_ctx_get(c->client->ctx, "ssl") != NULL) + result = "on"; + else + result = "off"; + } + } + + /* + * Totally independent stuff + */ + if (result == NULL) { + if (strlen(var) > 12 && strcEQn(var, "SSL_VERSION_", 12)) + result = ssl_var_lookup_ssl_version(p, var+12); + else if (strcEQ(var, "SERVER_SOFTWARE")) + result = (char *)ap_get_server_version(); + else if (strcEQ(var, "API_VERSION")) { + result = ap_psprintf(p, "%d", MODULE_MAGIC_NUMBER); + resdup = FALSE; + } + else if (strcEQ(var, "TIME_YEAR")) { + tc = time(NULL); + tm = localtime(&tc); + result = ap_psprintf(p, "%02d%02d", + (tm->tm_year / 100) + 19, tm->tm_year % 100); + resdup = FALSE; + } +#define MKTIMESTR(format, tmfield) \ + tc = time(NULL); \ + tm = localtime(&tc); \ + result = ap_psprintf(p, format, tm->tmfield); \ + resdup = FALSE; + else if (strcEQ(var, "TIME_MON")) { + MKTIMESTR("%02d", tm_mon+1) + } + else if (strcEQ(var, "TIME_DAY")) { + MKTIMESTR("%02d", tm_mday) + } + else if (strcEQ(var, "TIME_HOUR")) { + MKTIMESTR("%02d", tm_hour) + } + else if (strcEQ(var, "TIME_MIN")) { + MKTIMESTR("%02d", tm_min) + } + else if (strcEQ(var, "TIME_SEC")) { + MKTIMESTR("%02d", tm_sec) + } + else if (strcEQ(var, "TIME_WDAY")) { + MKTIMESTR("%d", tm_wday) + } + else if (strcEQ(var, "TIME")) { + tc = time(NULL); + tm = localtime(&tc); + result = ap_psprintf(p, + "%02d%02d%02d%02d%02d%02d%02d", (tm->tm_year / 100) + 19, + (tm->tm_year % 100), tm->tm_mon+1, tm->tm_mday, + tm->tm_hour, tm->tm_min, tm->tm_sec); + resdup = FALSE; + } + /* all other env-variables from the parent Apache process */ + else if (strlen(var) > 4 && strcEQn(var, "ENV:", 4)) { + result = (char *)ap_table_get(r->notes, var+4); + if (result == NULL) + result = (char *)ap_table_get(r->subprocess_env, var+4); + if (result == NULL) + result = getenv(var+4); + } + } + + if (result != NULL && resdup) + result = ap_pstrdup(p, result); + if (result == NULL) + result = ""; + return result; +} + +static char *ssl_var_lookup_header(pool *p, request_rec *r, const char *name) +{ + array_header *hdrs_arr; + table_entry *hdrs; + int i; + + hdrs_arr = ap_table_elts(r->headers_in); + hdrs = (table_entry *)hdrs_arr->elts; + for (i = 0; i < hdrs_arr->nelts; ++i) { + if (hdrs[i].key == NULL) + continue; + if (strcEQ(hdrs[i].key, name)) + return ap_pstrdup(p, hdrs[i].val); + } + return NULL; +} + +static char *ssl_var_lookup_ssl(pool *p, conn_rec *c, char *var) +{ + char *result; + X509 *xs; + STACK_OF(X509) *sk; + SSL *ssl; + + result = NULL; + + ssl = ap_ctx_get(c->client->ctx, "ssl"); + if (strlen(var) > 8 && strcEQn(var, "VERSION_", 8)) { + result = ssl_var_lookup_ssl_version(p, var+8); + } + else if (ssl != NULL && strcEQ(var, "PROTOCOL")) { + result = (char *)SSL_get_version(ssl); + } + else if (ssl != NULL && strcEQ(var, "SESSION_ID")) { + SSL_SESSION *pSession = SSL_get_session(ssl); + result = ap_pstrdup(p, SSL_SESSION_id2sz(pSession->session_id, + pSession->session_id_length)); + } + else if (ssl != NULL && strlen(var) >= 6 && strcEQn(var, "CIPHER", 6)) { + result = ssl_var_lookup_ssl_cipher(p, c, var+6); + } + else if (ssl != NULL && strlen(var) > 18 && strcEQn(var, "CLIENT_CERT_CHAIN_", 18)) { + sk = SSL_get_peer_cert_chain(ssl); + result = ssl_var_lookup_ssl_cert_chain(p, sk, var+17); + } + else if (ssl != NULL && strcEQ(var, "CLIENT_VERIFY")) { + result = ssl_var_lookup_ssl_cert_verify(p, c); + } + else if (ssl != NULL && strlen(var) > 7 && strcEQn(var, "CLIENT_", 7)) { + if ((xs = SSL_get_peer_certificate(ssl)) != NULL) + result = ssl_var_lookup_ssl_cert(p, xs, var+7); + } + else if (ssl != NULL && strlen(var) > 7 && strcEQn(var, "SERVER_", 7)) { + if ((xs = SSL_get_certificate(ssl)) != NULL) + result = ssl_var_lookup_ssl_cert(p, xs, var+7); + } + return result; +} + +static char *ssl_var_lookup_ssl_cert(pool *p, X509 *xs, char *var) +{ + char *result; + BOOL resdup; + X509_NAME *xsname; + int nid; + char *cp; + + result = NULL; + resdup = TRUE; + + if (strcEQ(var, "M_VERSION")) { + result = ap_psprintf(p, "%lu", X509_get_version(xs)+1); + resdup = FALSE; + } + else if (strcEQ(var, "M_SERIAL")) { + result = ssl_var_lookup_ssl_cert_serial(p, xs); + } + else if (strcEQ(var, "V_START")) { + result = ssl_var_lookup_ssl_cert_valid(p, X509_get_notBefore(xs)); + } + else if (strcEQ(var, "V_END")) { + result = ssl_var_lookup_ssl_cert_valid(p, X509_get_notAfter(xs)); + } + else if (strcEQ(var, "S_DN")) { + xsname = X509_get_subject_name(xs); + cp = X509_NAME_oneline(xsname, NULL, 0); + result = ap_pstrdup(p, cp); + free(cp); + resdup = FALSE; + } + else if (strlen(var) > 5 && strcEQn(var, "S_DN_", 5)) { + xsname = X509_get_subject_name(xs); + result = ssl_var_lookup_ssl_cert_dn(p, xsname, var+5); + resdup = FALSE; + } + else if (strcEQ(var, "I_DN")) { + xsname = X509_get_issuer_name(xs); + cp = X509_NAME_oneline(xsname, NULL, 0); + result = ap_pstrdup(p, cp); + free(cp); + resdup = FALSE; + } + else if (strlen(var) > 5 && strcEQn(var, "I_DN_", 5)) { + xsname = X509_get_issuer_name(xs); + result = ssl_var_lookup_ssl_cert_dn(p, xsname, var+5); + resdup = FALSE; + } + else if (strcEQ(var, "A_SIG")) { + nid = OBJ_obj2nid(xs->cert_info->signature->algorithm); + result = ap_pstrdup(p, (nid == NID_undef) ? "UNKNOWN" : OBJ_nid2ln(nid)); + resdup = FALSE; + } + else if (strcEQ(var, "A_KEY")) { + nid = OBJ_obj2nid(xs->cert_info->key->algor->algorithm); + result = ap_pstrdup(p, (nid == NID_undef) ? "UNKNOWN" : OBJ_nid2ln(nid)); + resdup = FALSE; + } + else if (strcEQ(var, "CERT")) { + result = ssl_var_lookup_ssl_cert_PEM(p, xs); + } + + if (result != NULL && resdup) + result = ap_pstrdup(p, result); + return result; +} + +static const struct { + char *name; + int nid; +} ssl_var_lookup_ssl_cert_dn_rec[] = { + { "C", NID_countryName }, + { "ST", NID_stateOrProvinceName }, /* officially (RFC2156) */ + { "SP", NID_stateOrProvinceName }, /* compatibility (SSLeay) */ + { "L", NID_localityName }, + { "O", NID_organizationName }, + { "OU", NID_organizationalUnitName }, + { "CN", NID_commonName }, + { "T", NID_title }, + { "I", NID_initials }, + { "G", NID_givenName }, + { "S", NID_surname }, + { "D", NID_description }, + { "UID", NID_uniqueIdentifier }, + { "Email", NID_pkcs9_emailAddress }, + { NULL, 0 } +}; + +static char *ssl_var_lookup_ssl_cert_dn(pool *p, X509_NAME *xsname, char *var) +{ + char *result; + X509_NAME_ENTRY *xsne; + int i, j, n; + + result = NULL; + + for (i = 0; ssl_var_lookup_ssl_cert_dn_rec[i].name != NULL; i++) { + if (strEQ(var, ssl_var_lookup_ssl_cert_dn_rec[i].name)) { + for (j = 0; j < sk_X509_NAME_ENTRY_num(xsname->entries); j++) { + xsne = sk_X509_NAME_ENTRY_value(xsname->entries, j); + n = OBJ_obj2nid(xsne->object); + if (n == ssl_var_lookup_ssl_cert_dn_rec[i].nid) { + result = ap_palloc(p, xsne->value->length+1); + ap_cpystrn(result, (char *)xsne->value->data, xsne->value->length+1); +#ifdef CHARSET_EBCDIC + ascii2ebcdic(result, result, xsne->value->length); +#endif /* CHARSET_EBCDIC */ + result[xsne->value->length] = NUL; + break; + } + } + break; + } + } + return result; +} + +static char *ssl_var_lookup_ssl_cert_valid(pool *p, ASN1_UTCTIME *tm) +{ + char *result; + BIO* bio; + int n; + + if ((bio = BIO_new(BIO_s_mem())) == NULL) + return NULL; + ASN1_UTCTIME_print(bio, tm); + n = BIO_pending(bio); + result = ap_pcalloc(p, n+1); + n = BIO_read(bio, result, n); + result[n] = NUL; + BIO_free(bio); + return result; +} + +static char *ssl_var_lookup_ssl_cert_serial(pool *p, X509 *xs) +{ + char *result; + BIO *bio; + int n; + + if ((bio = BIO_new(BIO_s_mem())) == NULL) + return NULL; + i2a_ASN1_INTEGER(bio, X509_get_serialNumber(xs)); + n = BIO_pending(bio); + result = ap_pcalloc(p, n+1); + n = BIO_read(bio, result, n); + result[n] = NUL; + BIO_free(bio); + return result; +} + +static char *ssl_var_lookup_ssl_cert_chain(pool *p, STACK_OF(X509) *sk, char *var) +{ + char *result; + X509 *xs; + int n; + + result = NULL; + + if (strspn(var, "0123456789") == strlen(var)) { + n = atoi(var); + if (n < sk_X509_num(sk)) { + xs = sk_X509_value(sk, n); + result = ssl_var_lookup_ssl_cert_PEM(p, xs); + } + } + + return result; +} + +static char *ssl_var_lookup_ssl_cert_PEM(pool *p, X509 *xs) +{ + char *result; + BIO *bio; + int n; + + if ((bio = BIO_new(BIO_s_mem())) == NULL) + return NULL; + PEM_write_bio_X509(bio, xs); + n = BIO_pending(bio); + result = ap_pcalloc(p, n+1); + n = BIO_read(bio, result, n); + result[n] = NUL; + BIO_free(bio); + return result; +} + +static char *ssl_var_lookup_ssl_cert_verify(pool *p, conn_rec *c) +{ + char *result; + long vrc; + char *verr; + char *vinfo; + SSL *ssl; + X509 *xs; + + result = NULL; + ssl = ap_ctx_get(c->client->ctx, "ssl"); + verr = ap_ctx_get(c->client->ctx, "ssl::verify::error"); + vinfo = ap_ctx_get(c->client->ctx, "ssl::verify::info"); + vrc = SSL_get_verify_result(ssl); + xs = SSL_get_peer_certificate(ssl); + + if (vrc == X509_V_OK && verr == NULL && vinfo == NULL && xs == NULL) + /* no client verification done at all */ + result = "NONE"; + else if (vrc == X509_V_OK && verr == NULL && vinfo == NULL && xs != NULL) + /* client verification done successful */ + result = "SUCCESS"; + else if (vrc == X509_V_OK && vinfo != NULL && strEQ(vinfo, "GENEROUS")) + /* client verification done in generous way */ + result = "GENEROUS"; + else + /* client verification failed */ + result = ap_psprintf(p, "FAILED:%s", verr); + return result; +} + +static char *ssl_var_lookup_ssl_cipher(pool *p, conn_rec *c, char *var) +{ + char *result; + BOOL resdup; + int usekeysize, algkeysize; + SSL *ssl; + + result = NULL; + resdup = TRUE; + + ssl = ap_ctx_get(c->client->ctx, "ssl"); + ssl_var_lookup_ssl_cipher_bits(ssl, &usekeysize, &algkeysize); + + if (strEQ(var, "")) + result = (ssl != NULL ? (char *)SSL_get_cipher_name(ssl) : NULL); + else if (strcEQ(var, "_EXPORT")) + result = (usekeysize < 56 ? "true" : "false"); + else if (strcEQ(var, "_USEKEYSIZE")) { + result = ap_psprintf(p, "%d", usekeysize); + resdup = FALSE; + } + else if (strcEQ(var, "_ALGKEYSIZE")) { + result = ap_psprintf(p, "%d", algkeysize); + resdup = FALSE; + } + + if (result != NULL && resdup) + result = ap_pstrdup(p, result); + return result; +} + +static void ssl_var_lookup_ssl_cipher_bits(SSL *ssl, int *usekeysize, int *algkeysize) +{ + SSL_CIPHER *cipher; + + *usekeysize = 0; + *algkeysize = 0; + if (ssl != NULL) + if ((cipher = SSL_get_current_cipher(ssl)) != NULL) + *usekeysize = SSL_CIPHER_get_bits(cipher, algkeysize); + return; +} + +static char *ssl_var_lookup_ssl_version(pool *p, char *var) +{ + char *result; + char *cp, *cp2; + + result = NULL; + + if (strEQ(var, "PRODUCT")) { +#if defined(SSL_PRODUCT_NAME) && defined(SSL_PRODUCT_VERSION) + result = ap_psprintf(p, "%s/%s", SSL_PRODUCT_NAME, SSL_PRODUCT_VERSION); +#else + result = NULL; +#endif + } + else if (strEQ(var, "INTERFACE")) { + result = ap_psprintf(p, "mod_ssl/%s", MOD_SSL_VERSION); + } + else if (strEQ(var, "LIBRARY")) { + result = ap_pstrdup(p, SSL_LIBRARY_TEXT); + if ((cp = strchr(result, ' ')) != NULL) { + *cp = '/'; + if ((cp2 = strchr(cp, ' ')) != NULL) + *cp2 = NUL; + } + } + return result; +} + |