2 ** _ __ ___ ___ __| | ___ ___| | mod_ssl
3 ** | '_ ` _ \ / _ \ / _` | / __/ __| | Apache Interface to OpenSSL
4 ** | | | | | | (_) | (_| | \__ \__ \ | www.modssl.org
5 ** |_| |_| |_|\___/ \__,_|___|___/___/_| ftp.modssl.org
8 ** Variable Lookup Facility
11 /* ====================================================================
12 * The Apache Software License, Version 1.1
14 * Copyright (c) 2000-2001 The Apache Software Foundation. All rights
17 * Redistribution and use in source and binary forms, with or without
18 * modification, are permitted provided that the following conditions
21 * 1. Redistributions of source code must retain the above copyright
22 * notice, this list of conditions and the following disclaimer.
24 * 2. Redistributions in binary form must reproduce the above copyright
25 * notice, this list of conditions and the following disclaimer in
26 * the documentation and/or other materials provided with the
29 * 3. The end-user documentation included with the redistribution,
30 * if any, must include the following acknowledgment:
31 * "This product includes software developed by the
32 * Apache Software Foundation (http://www.apache.org/)."
33 * Alternately, this acknowledgment may appear in the software itself,
34 * if and wherever such third-party acknowledgments normally appear.
36 * 4. The names "Apache" and "Apache Software Foundation" must
37 * not be used to endorse or promote products derived from this
38 * software without prior written permission. For written
39 * permission, please contact apache@apache.org.
41 * 5. Products derived from this software may not be called "Apache",
42 * nor may "Apache" appear in their name, without prior written
43 * permission of the Apache Software Foundation.
45 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
46 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
47 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
48 * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
49 * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
50 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
51 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
52 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
53 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
54 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
55 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
57 * ====================================================================
59 /* ``Those of you who think they
60 know everything are very annoying
61 to those of us who do.''
65 /* _________________________________________________________________
68 ** _________________________________________________________________
71 static char *ssl_var_lookup_header(apr_pool_t *p, request_rec *r, const char *name);
72 static char *ssl_var_lookup_ssl(apr_pool_t *p, conn_rec *c, char *var);
73 static char *ssl_var_lookup_ssl_cert(apr_pool_t *p, X509 *xs, char *var);
74 static char *ssl_var_lookup_ssl_cert_dn(apr_pool_t *p, X509_NAME *xsname, char *var);
75 static char *ssl_var_lookup_ssl_cert_valid(apr_pool_t *p, ASN1_UTCTIME *tm);
76 static char *ssl_var_lookup_ssl_cert_serial(apr_pool_t *p, X509 *xs);
77 static char *ssl_var_lookup_ssl_cert_chain(apr_pool_t *p, STACK_OF(X509) *sk, char *var);
78 static char *ssl_var_lookup_ssl_cert_PEM(apr_pool_t *p, X509 *xs);
79 static char *ssl_var_lookup_ssl_cert_verify(apr_pool_t *p, conn_rec *c);
80 static char *ssl_var_lookup_ssl_cipher(apr_pool_t *p, conn_rec *c, char *var);
81 static void ssl_var_lookup_ssl_cipher_bits(SSL *ssl, int *usekeysize, int *algkeysize);
82 static char *ssl_var_lookup_ssl_version(apr_pool_t *p, char *var);
84 void ssl_var_register(void)
86 APR_REGISTER_OPTIONAL_FN(ssl_var_lookup);
90 char *ssl_var_lookup(apr_pool_t *p, server_rec *s, conn_rec *c, request_rec *r, char *var)
93 SSLModConfigRec *mc = myModConfig(s);
103 * When no pool is given try to find one
115 * Request dependent stuff
118 if (strcEQ(var, "HTTP_USER_AGENT"))
119 result = ssl_var_lookup_header(p, r, "User-Agent");
120 else if (strcEQ(var, "HTTP_REFERER"))
121 result = ssl_var_lookup_header(p, r, "Referer");
122 else if (strcEQ(var, "HTTP_COOKIE"))
123 result = ssl_var_lookup_header(p, r, "Cookie");
124 else if (strcEQ(var, "HTTP_FORWARDED"))
125 result = ssl_var_lookup_header(p, r, "Forwarded");
126 else if (strcEQ(var, "HTTP_HOST"))
127 result = ssl_var_lookup_header(p, r, "Host");
128 else if (strcEQ(var, "HTTP_PROXY_CONNECTION"))
129 result = ssl_var_lookup_header(p, r, "Proxy-Connection");
130 else if (strcEQ(var, "HTTP_ACCEPT"))
131 result = ssl_var_lookup_header(p, r, "Accept");
132 else if (strlen(var) > 5 && strcEQn(var, "HTTP:", 5))
133 /* all other headers from which we are still not know about */
134 result = ssl_var_lookup_header(p, r, var+5);
135 else if (strcEQ(var, "THE_REQUEST"))
136 result = r->the_request;
137 else if (strcEQ(var, "REQUEST_METHOD"))
138 result = (char *)(r->method);
139 else if (strcEQ(var, "REQUEST_SCHEME"))
140 result = (char *)ap_http_method(r);
141 else if (strcEQ(var, "REQUEST_URI"))
143 else if (strcEQ(var, "SCRIPT_FILENAME") ||
144 strcEQ(var, "REQUEST_FILENAME"))
145 result = r->filename;
146 else if (strcEQ(var, "PATH_INFO"))
147 result = r->path_info;
148 else if (strcEQ(var, "QUERY_STRING"))
150 else if (strcEQ(var, "REMOTE_HOST"))
151 result = (char *)ap_get_remote_host(r->connection,
152 r->per_dir_config, REMOTE_NAME, NULL);
153 else if (strcEQ(var, "REMOTE_IDENT"))
154 result = (char *)ap_get_remote_logname(r);
155 else if (strcEQ(var, "IS_SUBREQ"))
156 result = (r->main != NULL ? "true" : "false");
157 else if (strcEQ(var, "DOCUMENT_ROOT"))
158 result = (char *)ap_document_root(r);
159 else if (strcEQ(var, "SERVER_ADMIN"))
160 result = r->server->server_admin;
161 else if (strcEQ(var, "SERVER_NAME"))
162 result = (char *)ap_get_server_name(r);
163 else if (strcEQ(var, "SERVER_PORT"))
164 result = apr_psprintf(p, "%u", ap_get_server_port(r));
165 else if (strcEQ(var, "SERVER_PROTOCOL"))
166 result = r->protocol;
172 if (result == NULL && c != NULL) {
173 sslconn = myConnConfig(c);
174 if (strcEQ(var, "REMOTE_ADDR"))
175 result = c->remote_ip;
176 else if (strcEQ(var, "REMOTE_USER"))
178 else if (strcEQ(var, "AUTH_TYPE"))
179 result = r->ap_auth_type;
180 else if (strlen(var) > 4 && strcEQn(var, "SSL_", 4))
181 result = ssl_var_lookup_ssl(p, c, var+4);
182 else if (strcEQ(var, "HTTPS")) {
183 if (sslconn->ssl != NULL)
191 * Totally independent stuff
193 if (result == NULL) {
194 if (strlen(var) > 12 && strcEQn(var, "SSL_VERSION_", 12))
195 result = ssl_var_lookup_ssl_version(p, var+12);
196 else if (strcEQ(var, "SERVER_SOFTWARE"))
197 result = (char *)ap_get_server_version();
198 else if (strcEQ(var, "API_VERSION")) {
199 result = apr_psprintf(p, "%d", MODULE_MAGIC_NUMBER);
202 else if (strcEQ(var, "TIME_YEAR")) {
205 result = apr_psprintf(p, "%02d%02d",
206 (tm->tm_year / 100) + 19, tm->tm_year % 100);
209 #define MKTIMESTR(format, tmfield) \
211 tm = localtime(&tc); \
212 result = apr_psprintf(p, format, tm->tmfield); \
214 else if (strcEQ(var, "TIME_MON")) {
215 MKTIMESTR("%02d", tm_mon+1)
217 else if (strcEQ(var, "TIME_DAY")) {
218 MKTIMESTR("%02d", tm_mday)
220 else if (strcEQ(var, "TIME_HOUR")) {
221 MKTIMESTR("%02d", tm_hour)
223 else if (strcEQ(var, "TIME_MIN")) {
224 MKTIMESTR("%02d", tm_min)
226 else if (strcEQ(var, "TIME_SEC")) {
227 MKTIMESTR("%02d", tm_sec)
229 else if (strcEQ(var, "TIME_WDAY")) {
230 MKTIMESTR("%d", tm_wday)
232 else if (strcEQ(var, "TIME")) {
235 result = apr_psprintf(p,
236 "%02d%02d%02d%02d%02d%02d%02d", (tm->tm_year / 100) + 19,
237 (tm->tm_year % 100), tm->tm_mon+1, tm->tm_mday,
238 tm->tm_hour, tm->tm_min, tm->tm_sec);
241 /* all other env-variables from the parent Apache process */
242 else if (strlen(var) > 4 && strcEQn(var, "ENV:", 4)) {
243 result = (char *)apr_table_get(r->notes, var+4);
245 result = (char *)apr_table_get(r->subprocess_env, var+4);
247 result = getenv(var+4);
251 if (result != NULL && resdup)
252 result = apr_pstrdup(p, result);
258 static char *ssl_var_lookup_header(apr_pool_t *p, request_rec *r, const char *name)
262 if ((hdr = (char *)apr_table_get(r->headers_in, name)) != NULL)
263 hdr = apr_pstrdup(p, hdr);
267 static char *ssl_var_lookup_ssl(apr_pool_t *p, conn_rec *c, char *var)
269 SSLConnRec *sslconn = myConnConfig(c);
278 if (strlen(var) > 8 && strcEQn(var, "VERSION_", 8)) {
279 result = ssl_var_lookup_ssl_version(p, var+8);
281 else if (ssl != NULL && strcEQ(var, "PROTOCOL")) {
282 result = (char *)SSL_get_version(ssl);
284 else if (ssl != NULL && strcEQ(var, "SESSION_ID")) {
285 SSL_SESSION *pSession = SSL_get_session(ssl);
286 result = apr_pstrdup(p, SSL_SESSION_id2sz(pSession->session_id,
287 pSession->session_id_length));
289 else if (ssl != NULL && strlen(var) >= 6 && strcEQn(var, "CIPHER", 6)) {
290 result = ssl_var_lookup_ssl_cipher(p, c, var+6);
292 else if (ssl != NULL && strlen(var) > 18 && strcEQn(var, "CLIENT_CERT_CHAIN_", 18)) {
293 sk = SSL_get_peer_cert_chain(ssl);
294 result = ssl_var_lookup_ssl_cert_chain(p, sk, var+17);
296 else if (ssl != NULL && strcEQ(var, "CLIENT_VERIFY")) {
297 result = ssl_var_lookup_ssl_cert_verify(p, c);
299 else if (ssl != NULL && strlen(var) > 7 && strcEQn(var, "CLIENT_", 7)) {
300 if ((xs = SSL_get_peer_certificate(ssl)) != NULL)
301 result = ssl_var_lookup_ssl_cert(p, xs, var+7);
303 else if (ssl != NULL && strlen(var) > 7 && strcEQn(var, "SERVER_", 7)) {
304 if ((xs = SSL_get_certificate(ssl)) != NULL)
305 result = ssl_var_lookup_ssl_cert(p, xs, var+7);
310 static char *ssl_var_lookup_ssl_cert(apr_pool_t *p, X509 *xs, char *var)
321 if (strcEQ(var, "M_VERSION")) {
322 result = apr_psprintf(p, "%lu", X509_get_version(xs)+1);
325 else if (strcEQ(var, "M_SERIAL")) {
326 result = ssl_var_lookup_ssl_cert_serial(p, xs);
328 else if (strcEQ(var, "V_START")) {
329 result = ssl_var_lookup_ssl_cert_valid(p, X509_get_notBefore(xs));
331 else if (strcEQ(var, "V_END")) {
332 result = ssl_var_lookup_ssl_cert_valid(p, X509_get_notAfter(xs));
334 else if (strcEQ(var, "S_DN")) {
335 xsname = X509_get_subject_name(xs);
336 cp = X509_NAME_oneline(xsname, NULL, 0);
337 result = apr_pstrdup(p, cp);
341 else if (strlen(var) > 5 && strcEQn(var, "S_DN_", 5)) {
342 xsname = X509_get_subject_name(xs);
343 result = ssl_var_lookup_ssl_cert_dn(p, xsname, var+5);
346 else if (strcEQ(var, "I_DN")) {
347 xsname = X509_get_issuer_name(xs);
348 cp = X509_NAME_oneline(xsname, NULL, 0);
349 result = apr_pstrdup(p, cp);
353 else if (strlen(var) > 5 && strcEQn(var, "I_DN_", 5)) {
354 xsname = X509_get_issuer_name(xs);
355 result = ssl_var_lookup_ssl_cert_dn(p, xsname, var+5);
358 else if (strcEQ(var, "A_SIG")) {
359 nid = OBJ_obj2nid(xs->cert_info->signature->algorithm);
360 result = apr_pstrdup(p, (nid == NID_undef) ? "UNKNOWN" : OBJ_nid2ln(nid));
363 else if (strcEQ(var, "A_KEY")) {
364 nid = OBJ_obj2nid(xs->cert_info->key->algor->algorithm);
365 result = apr_pstrdup(p, (nid == NID_undef) ? "UNKNOWN" : OBJ_nid2ln(nid));
368 else if (strcEQ(var, "CERT")) {
369 result = ssl_var_lookup_ssl_cert_PEM(p, xs);
372 if (result != NULL && resdup)
373 result = apr_pstrdup(p, result);
377 static const struct {
380 } ssl_var_lookup_ssl_cert_dn_rec[] = {
381 { "C", NID_countryName },
382 { "ST", NID_stateOrProvinceName }, /* officially (RFC2156) */
383 { "SP", NID_stateOrProvinceName }, /* compatibility (SSLeay) */
384 { "L", NID_localityName },
385 { "O", NID_organizationName },
386 { "OU", NID_organizationalUnitName },
387 { "CN", NID_commonName },
389 { "I", NID_initials },
390 { "G", NID_givenName },
391 { "S", NID_surname },
392 { "D", NID_description },
393 { "UID", NID_uniqueIdentifier },
394 { "Email", NID_pkcs9_emailAddress },
398 static char *ssl_var_lookup_ssl_cert_dn(apr_pool_t *p, X509_NAME *xsname, char *var)
401 X509_NAME_ENTRY *xsne;
406 for (i = 0; ssl_var_lookup_ssl_cert_dn_rec[i].name != NULL; i++) {
407 if (strEQ(var, ssl_var_lookup_ssl_cert_dn_rec[i].name)) {
408 for (j = 0; j < sk_X509_NAME_ENTRY_num(xsname->entries); j++) {
409 xsne = sk_X509_NAME_ENTRY_value(xsname->entries, j);
410 n = OBJ_obj2nid(xsne->object);
411 if (n == ssl_var_lookup_ssl_cert_dn_rec[i].nid) {
412 result = apr_palloc(p, xsne->value->length+1);
413 apr_cpystrn(result, (char *)xsne->value->data, xsne->value->length+1);
414 #ifdef CHARSET_EBCDIC
415 ascii2ebcdic(result, result, xsne->value->length);
416 #endif /* CHARSET_EBCDIC */
417 result[xsne->value->length] = NUL;
427 static char *ssl_var_lookup_ssl_cert_valid(apr_pool_t *p, ASN1_UTCTIME *tm)
433 if ((bio = BIO_new(BIO_s_mem())) == NULL)
435 ASN1_UTCTIME_print(bio, tm);
436 n = BIO_pending(bio);
437 result = apr_pcalloc(p, n+1);
438 n = BIO_read(bio, result, n);
444 static char *ssl_var_lookup_ssl_cert_serial(apr_pool_t *p, X509 *xs)
450 if ((bio = BIO_new(BIO_s_mem())) == NULL)
452 i2a_ASN1_INTEGER(bio, X509_get_serialNumber(xs));
453 n = BIO_pending(bio);
454 result = apr_pcalloc(p, n+1);
455 n = BIO_read(bio, result, n);
461 static char *ssl_var_lookup_ssl_cert_chain(apr_pool_t *p, STACK_OF(X509) *sk, char *var)
469 if (strspn(var, "0123456789") == strlen(var)) {
471 if (n < sk_X509_num(sk)) {
472 xs = sk_X509_value(sk, n);
473 result = ssl_var_lookup_ssl_cert_PEM(p, xs);
480 static char *ssl_var_lookup_ssl_cert_PEM(apr_pool_t *p, X509 *xs)
486 if ((bio = BIO_new(BIO_s_mem())) == NULL)
488 PEM_write_bio_X509(bio, xs);
489 n = BIO_pending(bio);
490 result = apr_pcalloc(p, n+1);
491 n = BIO_read(bio, result, n);
497 static char *ssl_var_lookup_ssl_cert_verify(apr_pool_t *p, conn_rec *c)
499 SSLConnRec *sslconn = myConnConfig(c);
509 verr = (char *)apr_table_get(c->notes, "ssl::verify::error");
510 vinfo = (char *)apr_table_get(c->notes, "ssl::verify::info");
511 vrc = SSL_get_verify_result(ssl);
512 xs = SSL_get_peer_certificate(ssl);
514 if (vrc == X509_V_OK && verr == NULL && vinfo == NULL && xs == NULL)
515 /* no client verification done at all */
517 else if (vrc == X509_V_OK && verr == NULL && vinfo == NULL && xs != NULL)
518 /* client verification done successful */
520 else if (vrc == X509_V_OK && vinfo != NULL && strEQ(vinfo, "GENEROUS"))
521 /* client verification done in generous way */
524 /* client verification failed */
525 result = apr_psprintf(p, "FAILED:%s", verr);
529 static char *ssl_var_lookup_ssl_cipher(apr_pool_t *p, conn_rec *c, char *var)
531 SSLConnRec *sslconn = myConnConfig(c);
534 int usekeysize, algkeysize;
541 ssl_var_lookup_ssl_cipher_bits(ssl, &usekeysize, &algkeysize);
544 result = (ssl != NULL ? (char *)SSL_get_cipher_name(ssl) : NULL);
545 else if (strcEQ(var, "_EXPORT"))
546 result = (usekeysize < 56 ? "true" : "false");
547 else if (strcEQ(var, "_USEKEYSIZE")) {
548 result = apr_psprintf(p, "%d", usekeysize);
551 else if (strcEQ(var, "_ALGKEYSIZE")) {
552 result = apr_psprintf(p, "%d", algkeysize);
556 if (result != NULL && resdup)
557 result = apr_pstrdup(p, result);
561 static void ssl_var_lookup_ssl_cipher_bits(SSL *ssl, int *usekeysize, int *algkeysize)
568 if ((cipher = SSL_get_current_cipher(ssl)) != NULL)
569 *usekeysize = SSL_CIPHER_get_bits(cipher, algkeysize);
573 static char *ssl_var_lookup_ssl_version(apr_pool_t *p, char *var)
580 if (strEQ(var, "PRODUCT")) {
581 #if defined(SSL_PRODUCT_NAME) && defined(SSL_PRODUCT_VERSION)
582 result = apr_psprintf(p, "%s/%s", SSL_PRODUCT_NAME, SSL_PRODUCT_VERSION);
587 else if (strEQ(var, "INTERFACE")) {
588 result = apr_psprintf(p, "mod_ssl/%s", MOD_SSL_VERSION);
590 else if (strEQ(var, "LIBRARY")) {
591 result = apr_pstrdup(p, SSL_LIBRARY_TEXT);
592 if ((cp = strchr(result, ' ')) != NULL) {
594 if ((cp2 = strchr(cp, ' ')) != NULL)
601 /* _________________________________________________________________
603 ** SSL Extension to mod_log_config
604 ** _________________________________________________________________
607 #include "../../modules/loggers/mod_log_config.h"
609 static const char *ssl_var_log_handler_c(request_rec *r, char *a);
610 static const char *ssl_var_log_handler_x(request_rec *r, char *a);
613 * register us for the mod_log_config function registering phase
614 * to establish %{...}c and to be able to expand %{...}x variables.
616 void ssl_var_log_config_register(apr_pool_t *p)
618 static APR_OPTIONAL_FN_TYPE(ap_register_log_handler) *log_pfn_register;
620 log_pfn_register = APR_RETRIEVE_OPTIONAL_FN(ap_register_log_handler);
622 if (log_pfn_register) {
623 log_pfn_register(p, "c", ssl_var_log_handler_c, 0);
624 log_pfn_register(p, "x", ssl_var_log_handler_x, 0);
630 * implement the %{..}c log function
631 * (we are the only function)
633 static const char *ssl_var_log_handler_c(request_rec *r, char *a)
635 SSLConnRec *sslconn = myConnConfig(r->connection);
638 if (sslconn->ssl == NULL)
641 if (strEQ(a, "version"))
642 result = ssl_var_lookup(r->pool, r->server, r->connection, r, "SSL_PROTOCOL");
643 else if (strEQ(a, "cipher"))
644 result = ssl_var_lookup(r->pool, r->server, r->connection, r, "SSL_CIPHER");
645 else if (strEQ(a, "subjectdn") || strEQ(a, "clientcert"))
646 result = ssl_var_lookup(r->pool, r->server, r->connection, r, "SSL_CLIENT_S_DN");
647 else if (strEQ(a, "issuerdn") || strEQ(a, "cacert"))
648 result = ssl_var_lookup(r->pool, r->server, r->connection, r, "SSL_CLIENT_I_DN");
649 else if (strEQ(a, "errcode"))
651 else if (strEQ(a, "errstr"))
652 result = (char *)apr_table_get(r->connection->notes, "ssl::verify::error");
653 if (result != NULL && result[0] == NUL)
659 * extend the implementation of the %{..}x log function
660 * (there can be more functions)
662 static const char *ssl_var_log_handler_x(request_rec *r, char *a)
664 SSLConnRec *sslconn = myConnConfig(r->connection);
668 if (sslconn->ssl != NULL)
669 result = ssl_var_lookup(r->pool, r->server, r->connection, r, a);
670 if (result != NULL && result[0] == NUL)