summaryrefslogtreecommitdiffstats
path: root/nss_engine_vars.c
diff options
context:
space:
mode:
Diffstat (limited to 'nss_engine_vars.c')
-rw-r--r--nss_engine_vars.c662
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, &notBefore, &notAfter);
+
+ /* 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;
+}
+