diff options
author | rcritten <> | 2005-05-17 14:50:16 +0000 |
---|---|---|
committer | rcritten <> | 2005-05-17 14:50:16 +0000 |
commit | f6ecd9df97f9bb1be9b69ae97a78eb54b6599463 (patch) | |
tree | c4c8e184503e99362268f59dfa8d3ce114c18013 /nss_engine_vars.c | |
download | mod_nss-f6ecd9df97f9bb1be9b69ae97a78eb54b6599463.tar.gz mod_nss-f6ecd9df97f9bb1be9b69ae97a78eb54b6599463.tar.xz mod_nss-f6ecd9df97f9bb1be9b69ae97a78eb54b6599463.zip |
Initial import of mod_nss
Diffstat (limited to 'nss_engine_vars.c')
-rw-r--r-- | nss_engine_vars.c | 662 |
1 files changed, 662 insertions, 0 deletions
diff --git a/nss_engine_vars.c b/nss_engine_vars.c new file mode 100644 index 0000000..a38013e --- /dev/null +++ b/nss_engine_vars.c @@ -0,0 +1,662 @@ +/* Copyright 2001-2004 The Apache Software Foundation + * + * Licensed 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. + */ + +#include "mod_nss.h" +#include "secder.h" /* DER_GetInteger() */ +#include "base64.h" /* BTOA_DataToAscii() */ + +/* _________________________________________________________________ +** +** Variable Lookup +** _________________________________________________________________ +*/ + +#define CERT_NOTBEFORE 0 +#define CERT_NOTAFTER 1 + +static char *ssl_var_lookup_header(apr_pool_t *p, request_rec *r, const char *name); +static char *ssl_var_lookup_ssl(apr_pool_t *p, conn_rec *c, char *var); +static char *ssl_var_lookup_ssl_cert(apr_pool_t *p, CERTCertificate *xs, char *var, conn_rec *c); +static char *ssl_var_lookup_ssl_cert_dn(apr_pool_t *p, CERTCertificate *cert, char *var); +static char *ssl_var_lookup_ssl_cert_valid(apr_pool_t *p, CERTCertificate *xs, int type); +static char *ssl_var_lookup_ssl_cert_PEM(apr_pool_t *p, CERTCertificate *xs); +static char *ssl_var_lookup_ssl_cert_verify(apr_pool_t *p, conn_rec *c); +static char *ssl_var_lookup_ssl_cipher(apr_pool_t *p, conn_rec *c, char *var); +static char *ssl_var_lookup_ssl_version(apr_pool_t *p, char *var); +static char *ssl_var_lookup_protocol_version(apr_pool_t *p, conn_rec *c); + +static int ssl_is_https(conn_rec *c) +{ + SSLConnRec *sslconn = myConnConfig(c); + return sslconn && sslconn->ssl; +} + +void ssl_var_register(void) +{ + APR_REGISTER_OPTIONAL_FN(ssl_is_https); + APR_REGISTER_OPTIONAL_FN(ssl_var_lookup); + return; +} + +/* This function must remain safe to use for a non-SSL connection. */ +char *ssl_var_lookup(apr_pool_t *p, server_rec *s, conn_rec *c, request_rec *r, char *var) +{ + SSLModConfigRec *mc = myModConfig(s); + char *result; + BOOL resdup; + apr_time_exp_t 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 = (char *)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, NULL); + 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 = apr_psprintf(p, "%u", ap_get_server_port(r)); + else if (strcEQ(var, "SERVER_PROTOCOL")) + result = r->protocol; + } + + /* + * Connection stuff + */ + if (result == NULL && c != NULL) { + SSLConnRec *sslconn = myConnConfig(c); + if (strcEQ(var, "REMOTE_ADDR")) + result = c->remote_ip; + else if (strcEQ(var, "REMOTE_USER")) + result = r->user; + else if (strcEQ(var, "AUTH_TYPE")) + result = r->ap_auth_type; + else if (strlen(var) > 4 && strcEQn(var, "SSL_", 4) + && sslconn && sslconn->ssl) + result = ssl_var_lookup_ssl(p, c, var+4); + else if (strcEQ(var, "HTTPS")) { + if (sslconn && sslconn->ssl) + 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 = apr_psprintf(p, "%d", MODULE_MAGIC_NUMBER); + resdup = FALSE; + } + else if (strcEQ(var, "TIME_YEAR")) { + apr_time_exp_lt(&tm, apr_time_now()); + result = apr_psprintf(p, "%02d%02d", + (tm.tm_year / 100) + 19, tm.tm_year % 100); + resdup = FALSE; + } +#define MKTIMESTR(format, tmfield) \ + apr_time_exp_lt(&tm, apr_time_now()); \ + result = apr_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")) { + apr_time_exp_lt(&tm, apr_time_now()); + result = apr_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 *)apr_table_get(r->notes, var+4); + if (result == NULL) + result = (char *)apr_table_get(r->subprocess_env, var+4); + if (result == NULL) + result = getenv(var+4); + } + } + + if (result != NULL && resdup) + result = apr_pstrdup(p, result); + if (result == NULL) + result = ""; + return result; +} + +static char *ssl_var_lookup_header(apr_pool_t *p, request_rec *r, const char *name) +{ + char *hdr = NULL; + + if ((hdr = (char *)apr_table_get(r->headers_in, name)) != NULL) + hdr = apr_pstrdup(p, hdr); + return hdr; +} + +static char *ssl_var_lookup_ssl(apr_pool_t *p, conn_rec *c, char *var) +{ + SSLConnRec *sslconn = myConnConfig(c); + char *result; + CERTCertificate *xs; + PRFileDesc *ssl; + + result = NULL; + + ssl = sslconn->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_var_lookup_protocol_version(p, c); + } + else if (ssl != NULL && strcEQ(var, "SESSION_ID")) { + char *idstr; + SECItem *iditem; + + if ((iditem = SSL_GetSessionID(ssl)) == NULL) + return NULL; + + /* Convert to base64 ASCII encoding */ + idstr = BTOA_DataToAscii(iditem->data, iditem->len); + if (idstr) { + result = apr_pstrdup(p, idstr); + PORT_Free(idstr); + } + + SECITEM_FreeItem(iditem, PR_TRUE); + } + 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 && 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_PeerCertificate(ssl)) != NULL) { + result = ssl_var_lookup_ssl_cert(p, xs, var+7, c); + CERT_DestroyCertificate(xs); + } + } + else if (ssl != NULL && strlen(var) > 7 && strcEQn(var, "SERVER_", 7)) { + if ((xs = SSL_LocalCertificate(ssl)) != NULL) { + result = ssl_var_lookup_ssl_cert(p, xs, var+7, c); + CERT_DestroyCertificate(xs); + } + } + + return result; +} + +static char *ssl_var_lookup_ssl_cert(apr_pool_t *p, CERTCertificate *xs, char *var, conn_rec *c) +{ + char *result; + BOOL resdup; + char *xsname; + + result = NULL; + resdup = TRUE; + + if (strcEQ(var, "M_VERSION")) { + result = apr_psprintf(p, "%lu", DER_GetInteger(&xs->version)+1); + resdup = FALSE; + } + else if (strcEQ(var, "M_SERIAL")) { + result = apr_psprintf(p, "%lu", DER_GetInteger(&xs->serialNumber)); + resdup = FALSE; + } + else if (strcEQ(var, "V_START")) { + result = ssl_var_lookup_ssl_cert_valid(p, xs, CERT_NOTBEFORE); + } + else if (strcEQ(var, "V_END")) { + result = ssl_var_lookup_ssl_cert_valid(p, xs, CERT_NOTAFTER); + } + else if (strcEQ(var, "S_DN")) { + xsname = CERT_NameToAscii(&xs->subject); + result = apr_pstrdup(p, xsname); + PR_Free(xsname); + resdup = FALSE; + } + else if (strlen(var) > 5 && strcEQn(var, "S_DN_", 5)) { + result = ssl_var_lookup_ssl_cert_dn(p, xs, var+5); + resdup = FALSE; + } + else if (strcEQ(var, "I_DN")) { + xsname = CERT_NameToAscii(&xs->issuer); + result = apr_pstrdup(p, xsname); + PR_Free(xsname); + resdup = FALSE; + } + else if (strlen(var) > 5 && strcEQn(var, "I_DN_", 5)) { + result = ssl_var_lookup_ssl_cert_dn(p, xs, var+5); + resdup = FALSE; + } + else if (strcEQ(var, "A_SIG")) { + SSLChannelInfo channel; + SSLCipherSuiteInfo suite; + SSLConnRec *sslconn = myConnConfig(c); + + if (SSL_GetChannelInfo(sslconn->ssl, &channel, sizeof channel) == + SECSuccess && channel.length == sizeof channel && + channel.cipherSuite) + { + if (SSL_GetCipherSuiteInfo(channel.cipherSuite, + &suite, sizeof suite) == SECSuccess) + { + result = apr_psprintf(p, "%s-%s", suite.macAlgorithmName, suite.authAlgorithmName); + } + } else + result = apr_pstrdup(p, "UNKNOWN"); + resdup = FALSE; + } + else if (strcEQ(var, "A_KEY")) { + SSLChannelInfo channel; + SSLCipherSuiteInfo suite; + SSLConnRec *sslconn = myConnConfig(c); + + if (SSL_GetChannelInfo(sslconn->ssl, &channel, sizeof channel) == + SECSuccess && channel.length == sizeof channel && + channel.cipherSuite) + { + if (SSL_GetCipherSuiteInfo(channel.cipherSuite, + &suite, sizeof suite) == SECSuccess) + { + result = apr_psprintf(p, "%s", suite.keaTypeName); + } + } else + result = apr_pstrdup(p, "UNKNOWN"); + + resdup = FALSE; + } + else if (strcEQ(var, "CERT")) { + result = ssl_var_lookup_ssl_cert_PEM(p, xs); + } + + if (result != NULL && resdup) + result = apr_pstrdup(p, result); + return result; +} + +static char *ssl_var_lookup_ssl_cert_dn(apr_pool_t *p, CERTCertificate *cert, char *var) +{ + char *result; + char *rv; + + result = NULL; + rv = NULL; + + if (strcEQ(var, "C")) { + rv = CERT_GetCountryName(&cert->subject); + } else if (strcEQ(var, "ST")) { + rv = CERT_GetStateName(&cert->subject); + } else if (strcEQ(var, "SP")) { // for compatibility + rv = CERT_GetStateName(&cert->subject); + } else if (strcEQ(var, "L")) { + rv = CERT_GetLocalityName(&cert->subject); + } else if (strcEQ(var, "O")) { + rv = CERT_GetOrgName(&cert->subject); + } else if (strcEQ(var, "OU")) { + rv = CERT_GetOrgUnitName(&cert->subject); + } else if (strcEQ(var, "CN")) { + rv = CERT_GetCommonName(&cert->subject); + } else if (strcEQ(var, "UID")) { + rv = CERT_GetCertUid(&cert->subject); + } else if (strcEQ(var, "EMAIL")) { + rv = CERT_GetCertEmailAddress(&cert->subject); + } else { + rv = NULL; // catch any values we don't support + } + + if (rv) { + result = apr_pstrdup(p, rv); + PORT_Free(rv); // so we can free with the right allocator + } + + return result; +} + +static char *ssl_var_lookup_ssl_cert_valid(apr_pool_t *p, CERTCertificate *xs, int type) +{ + char *result; + PRExplodedTime printableTime; + char timeString[256]; + PRTime notBefore, notAfter; + + CERT_GetCertTimes(xs, ¬Before, ¬After); + + /* Converse time to local time and decompose it into components */ + if (type == CERT_NOTBEFORE) { + PR_ExplodeTime(notBefore, PR_GMTParameters, &printableTime); + } else { + PR_ExplodeTime(notAfter, PR_GMTParameters, &printableTime); + } + + PR_FormatTime(timeString, 256, "%b %d %H:%M:%S %Y GMT", &printableTime); + + result = apr_pstrdup(p, timeString); + + return result; +} + +#define CERT_HEADER "-----BEGIN CERTIFICATE-----\n" +#define CERT_TRAILER "\n-----END CERTIFICATE-----\n" +static char *ssl_var_lookup_ssl_cert_PEM(apr_pool_t *p, CERTCertificate *xs) +{ + char * result = NULL; + char * tmp = NULL; + + tmp = BTOA_DataToAscii(xs->derCert.data, + xs->derCert.len); + + /* Allocate the size of the cert + header + footer + 1 */ + result = apr_palloc(p, strlen(tmp) + 29 + 27 + 1); + strcpy(result, CERT_HEADER); + strcat(result, tmp); + strcat(result, CERT_TRAILER); + result[strlen(tmp) + 29 + 27] = '\0'; + + /* Clean up memory. */ + PR_Free(tmp); + + return result; +} + +static char *ssl_var_lookup_ssl_cert_verify(apr_pool_t *p, conn_rec *c) +{ + SSLConnRec *sslconn = myConnConfig(c); + char *result; + PRFileDesc *ssl; + SECStatus rv; + CERTCertificate *xs; + void *pinArg; + + result = NULL; + ssl = sslconn->ssl; + xs = SSL_PeerCertificate(ssl); + pinArg = SSL_RevealPinArg(sslconn->ssl); + + if (xs == NULL) + result = "NONE"; + else { + rv = CERT_VerifyCertNow(CERT_GetDefaultCertDB(), + xs, + PR_TRUE, + certUsageSSLClient, + pinArg); + + if (rv == SECSuccess) + result = "SUCCESS"; + else + result = apr_psprintf(p, "FAILED"); // FIXME, add more info? + } + + if (xs) + CERT_DestroyCertificate(xs); + + return result; +} + +static char *ssl_var_lookup_ssl_cipher(apr_pool_t *p, conn_rec *c, char *var) +{ + SSLConnRec *sslconn = myConnConfig(c); + char *result; + BOOL resdup; + PRFileDesc *ssl; + int on, keySize, secretKeySize; + char *cipher, *issuer, *subject; + SECStatus secstatus = SECFailure; + + result = NULL; + resdup = TRUE; + + on = keySize = secretKeySize = 0; + cipher = issuer = subject = NULL; + + ssl = sslconn->ssl; + + if (ssl) { + secstatus = SSL_SecurityStatus(ssl, &on, &cipher, + &keySize, &secretKeySize, &issuer, + &subject); + } + + if (secstatus != SECSuccess) + return NULL; + + if (ssl && strEQ(var, "")) { + result = cipher; + } + else if (strcEQ(var, "_EXPORT")) + result = (secretKeySize < 56 ? "true" : "false"); + else if (strcEQ(var, "_USEKEYSIZE")) { + result = apr_psprintf(p, "%d", secretKeySize); + resdup = FALSE; + } + else if (strcEQ(var, "_ALGKEYSIZE")) { + result = apr_psprintf(p, "%d", keySize); + resdup = FALSE; + } + + if (result != NULL && resdup) + result = apr_pstrdup(p, result); + + PR_Free(issuer); + PR_Free(subject); + + return result; +} + +static char *ssl_var_lookup_ssl_version(apr_pool_t *p, char *var) +{ + char *result; + + result = NULL; + + if (strEQ(var, "PRODUCT")) { +#if defined(SSL_PRODUCT_NAME) && defined(SSL_PRODUCT_VERSION) + result = apr_psprintf(p, "%s/%s", SSL_PRODUCT_NAME, SSL_PRODUCT_VERSION); +#else + result = NULL; +#endif + } + else if (strEQ(var, "INTERFACE")) { + result = apr_psprintf(p, "mod_nss/%s", MOD_NSS_VERSION); + } + else if (strEQ(var, "LIBRARY")) { + result = apr_psprintf(p, "NSS/%s", NSS_VERSION); + } + return result; +} + +static char *ssl_var_lookup_protocol_version(apr_pool_t *p, conn_rec *c) +{ + char *result; + SSLChannelInfo channel; + SSLCipherSuiteInfo suite; + SSLConnRec *sslconn = myConnConfig(c); + + result = "UNKNOWN"; + + if (SSL_GetChannelInfo(sslconn->ssl, &channel, sizeof channel) == + SECSuccess && channel.length == sizeof channel && + channel.cipherSuite) { + if (SSL_GetCipherSuiteInfo(channel.cipherSuite, + &suite, sizeof suite) == SECSuccess) { + switch (channel.protocolVersion) { + case SSL_LIBRARY_VERSION_2: + result = "SSLv2"; + break; + case SSL_LIBRARY_VERSION_3_0: + result = "SSLv3"; + break; + case SSL_LIBRARY_VERSION_3_1_TLS: + result = "TLSv1"; + break; + } + } + } + + result = apr_pstrdup(p, result); + + return result; +} + +/* _________________________________________________________________ +** +** SSL Extension to mod_log_config +** _________________________________________________________________ +*/ + +#include "mod_log_config.h" + +static const char *ssl_var_log_handler_c(request_rec *r, char *a); +static const char *ssl_var_log_handler_x(request_rec *r, char *a); + +/* + * register us for the mod_log_config function registering phase + * to establish %{...}c and to be able to expand %{...}x variables. + */ +void ssl_var_log_config_register(apr_pool_t *p) +{ + static APR_OPTIONAL_FN_TYPE(ap_register_log_handler) *log_pfn_register; + + log_pfn_register = APR_RETRIEVE_OPTIONAL_FN(ap_register_log_handler); + + if (log_pfn_register) { + log_pfn_register(p, "c", ssl_var_log_handler_c, 0); + log_pfn_register(p, "x", ssl_var_log_handler_x, 0); + } + return; +} + +/* + * implement the %{..}c log function + * (we are the only function) + */ +static const char *ssl_var_log_handler_c(request_rec *r, char *a) +{ + SSLConnRec *sslconn = myConnConfig(r->connection); + char *result; + + if (sslconn == NULL || sslconn->ssl == NULL) + return NULL; + result = NULL; + if (strEQ(a, "version")) + result = ssl_var_lookup(r->pool, r->server, r->connection, r, "SSL_PROTOCOL"); + else if (strEQ(a, "cipher")) + result = ssl_var_lookup(r->pool, r->server, r->connection, r, "SSL_CIPHER"); + else if (strEQ(a, "subjectdn") || strEQ(a, "clientcert")) + result = ssl_var_lookup(r->pool, r->server, r->connection, r, "SSL_CLIENT_S_DN"); + else if (strEQ(a, "issuerdn") || strEQ(a, "cacert")) + result = ssl_var_lookup(r->pool, r->server, r->connection, r, "SSL_CLIENT_I_DN"); + else if (strEQ(a, "errcode")) + result = "-"; + if (result != NULL && result[0] == NUL) + result = NULL; + return result; +} + +/* + * extend the implementation of the %{..}x log function + * (there can be more functions) + */ +static const char *ssl_var_log_handler_x(request_rec *r, char *a) +{ + char *result; + + result = ssl_var_lookup(r->pool, r->server, r->connection, r, a); + if (result != NULL && result[0] == NUL) + result = NULL; + return result; +} + |