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 ** _________________________________________________________________
72 static char *ssl_var_lookup_header(pool *p, request_rec *r, const char *name);
73 static char *ssl_var_lookup_ssl(pool *p, conn_rec *c, char *var);
74 static char *ssl_var_lookup_ssl_cert(pool *p, X509 *xs, char *var);
75 static char *ssl_var_lookup_ssl_cert_dn(pool *p, X509_NAME *xsname, char *var);
76 static char *ssl_var_lookup_ssl_cert_valid(pool *p, ASN1_UTCTIME *tm);
77 static char *ssl_var_lookup_ssl_cert_serial(pool *p, X509 *xs);
78 static char *ssl_var_lookup_ssl_cert_chain(pool *p, STACK_OF(X509) *sk, char *var);
79 static char *ssl_var_lookup_ssl_cert_PEM(pool *p, X509 *xs);
80 static char *ssl_var_lookup_ssl_cert_verify(pool *p, conn_rec *c);
81 static char *ssl_var_lookup_ssl_cipher(pool *p, conn_rec *c, char *var);
82 static void ssl_var_lookup_ssl_cipher_bits(SSL *ssl, int *usekeysize, int *algkeysize);
83 static char *ssl_var_lookup_ssl_version(pool *p, char *var);
86 void ssl_var_register(void)
89 ap_hook_configure("ap::mod_ssl::var_lookup",
90 AP_HOOK_SIG6(ptr,ptr,ptr,ptr,ptr,ptr), AP_HOOK_DECLINE(NULL));
91 ap_hook_register("ap::mod_ssl::var_lookup",
92 ssl_var_lookup, AP_HOOK_NOCTX);
97 void ssl_var_unregister(void)
100 ap_hook_unregister("ap::mod_ssl::var_lookup", ssl_var_lookup);
107 char *ssl_var_lookup(pool *p, server_rec *s, conn_rec *c, request_rec *r, char *var)
109 SSLModConfigRec *mc = myModConfig();
119 * When no pool is given try to find one
131 * Request dependent stuff
134 if (strcEQ(var, "HTTP_USER_AGENT"))
135 result = ssl_var_lookup_header(p, r, "User-Agent");
136 else if (strcEQ(var, "HTTP_REFERER"))
137 result = ssl_var_lookup_header(p, r, "Referer");
138 else if (strcEQ(var, "HTTP_COOKIE"))
139 result = ssl_var_lookup_header(p, r, "Cookie");
140 else if (strcEQ(var, "HTTP_FORWARDED"))
141 result = ssl_var_lookup_header(p, r, "Forwarded");
142 else if (strcEQ(var, "HTTP_HOST"))
143 result = ssl_var_lookup_header(p, r, "Host");
144 else if (strcEQ(var, "HTTP_PROXY_CONNECTION"))
145 result = ssl_var_lookup_header(p, r, "Proxy-Connection");
146 else if (strcEQ(var, "HTTP_ACCEPT"))
147 result = ssl_var_lookup_header(p, r, "Accept");
148 else if (strlen(var) > 5 && strcEQn(var, "HTTP:", 5))
149 /* all other headers from which we are still not know about */
150 result = ssl_var_lookup_header(p, r, var+5);
151 else if (strcEQ(var, "THE_REQUEST"))
152 result = r->the_request;
153 else if (strcEQ(var, "REQUEST_METHOD"))
154 result = (char *)(r->method);
155 else if (strcEQ(var, "REQUEST_SCHEME"))
156 result = ap_http_method(r);
157 else if (strcEQ(var, "REQUEST_URI"))
159 else if (strcEQ(var, "SCRIPT_FILENAME") ||
160 strcEQ(var, "REQUEST_FILENAME"))
161 result = r->filename;
162 else if (strcEQ(var, "PATH_INFO"))
163 result = r->path_info;
164 else if (strcEQ(var, "QUERY_STRING"))
166 else if (strcEQ(var, "REMOTE_HOST"))
167 result = (char *)ap_get_remote_host(r->connection,
168 r->per_dir_config, REMOTE_NAME);
169 else if (strcEQ(var, "REMOTE_IDENT"))
170 result = (char *)ap_get_remote_logname(r);
171 else if (strcEQ(var, "IS_SUBREQ"))
172 result = (r->main != NULL ? "true" : "false");
173 else if (strcEQ(var, "DOCUMENT_ROOT"))
174 result = (char *)ap_document_root(r);
175 else if (strcEQ(var, "SERVER_ADMIN"))
176 result = r->server->server_admin;
177 else if (strcEQ(var, "SERVER_NAME"))
178 result = (char *)ap_get_server_name(r);
179 else if (strcEQ(var, "SERVER_PORT"))
180 result = ap_psprintf(p, "%u", ap_get_server_port(r));
181 else if (strcEQ(var, "SERVER_PROTOCOL"))
182 result = r->protocol;
188 if (result == NULL && c != NULL) {
189 if (strcEQ(var, "REMOTE_ADDR"))
190 result = c->remote_ip;
191 else if (strcEQ(var, "REMOTE_USER"))
193 else if (strcEQ(var, "AUTH_TYPE"))
194 result = c->ap_auth_type;
195 else if (strlen(var) > 4 && strcEQn(var, "SSL_", 4))
196 result = ssl_var_lookup_ssl(p, c, var+4);
197 else if (strcEQ(var, "HTTPS")) {
198 if (ap_ctx_get(c->client->ctx, "ssl") != NULL)
206 * Totally independent stuff
208 if (result == NULL) {
209 if (strlen(var) > 12 && strcEQn(var, "SSL_VERSION_", 12))
210 result = ssl_var_lookup_ssl_version(p, var+12);
211 else if (strcEQ(var, "SERVER_SOFTWARE"))
212 result = (char *)ap_get_server_version();
213 else if (strcEQ(var, "API_VERSION")) {
214 result = ap_psprintf(p, "%d", MODULE_MAGIC_NUMBER);
217 else if (strcEQ(var, "TIME_YEAR")) {
220 result = ap_psprintf(p, "%02d%02d",
221 (tm->tm_year / 100) + 19, tm->tm_year % 100);
224 #define MKTIMESTR(format, tmfield) \
226 tm = localtime(&tc); \
227 result = ap_psprintf(p, format, tm->tmfield); \
229 else if (strcEQ(var, "TIME_MON")) {
230 MKTIMESTR("%02d", tm_mon+1)
232 else if (strcEQ(var, "TIME_DAY")) {
233 MKTIMESTR("%02d", tm_mday)
235 else if (strcEQ(var, "TIME_HOUR")) {
236 MKTIMESTR("%02d", tm_hour)
238 else if (strcEQ(var, "TIME_MIN")) {
239 MKTIMESTR("%02d", tm_min)
241 else if (strcEQ(var, "TIME_SEC")) {
242 MKTIMESTR("%02d", tm_sec)
244 else if (strcEQ(var, "TIME_WDAY")) {
245 MKTIMESTR("%d", tm_wday)
247 else if (strcEQ(var, "TIME")) {
250 result = ap_psprintf(p,
251 "%02d%02d%02d%02d%02d%02d%02d", (tm->tm_year / 100) + 19,
252 (tm->tm_year % 100), tm->tm_mon+1, tm->tm_mday,
253 tm->tm_hour, tm->tm_min, tm->tm_sec);
256 /* all other env-variables from the parent Apache process */
257 else if (strlen(var) > 4 && strcEQn(var, "ENV:", 4)) {
258 result = (char *)ap_table_get(r->notes, var+4);
260 result = (char *)ap_table_get(r->subprocess_env, var+4);
262 result = getenv(var+4);
266 if (result != NULL && resdup)
267 result = ap_pstrdup(p, result);
273 static char *ssl_var_lookup_header(pool *p, request_rec *r, const char *name)
275 array_header *hdrs_arr;
279 hdrs_arr = ap_table_elts(r->headers_in);
280 hdrs = (table_entry *)hdrs_arr->elts;
281 for (i = 0; i < hdrs_arr->nelts; ++i) {
282 if (hdrs[i].key == NULL)
284 if (strcEQ(hdrs[i].key, name))
285 return ap_pstrdup(p, hdrs[i].val);
290 static char *ssl_var_lookup_ssl(pool *p, conn_rec *c, char *var)
299 ssl = ap_ctx_get(c->client->ctx, "ssl");
300 if (strlen(var) > 8 && strcEQn(var, "VERSION_", 8)) {
301 result = ssl_var_lookup_ssl_version(p, var+8);
303 else if (ssl != NULL && strcEQ(var, "PROTOCOL")) {
304 result = (char *)SSL_get_version(ssl);
306 else if (ssl != NULL && strcEQ(var, "SESSION_ID")) {
307 SSL_SESSION *pSession = SSL_get_session(ssl);
308 result = ap_pstrdup(p, SSL_SESSION_id2sz(pSession->session_id,
309 pSession->session_id_length));
311 else if (ssl != NULL && strlen(var) >= 6 && strcEQn(var, "CIPHER", 6)) {
312 result = ssl_var_lookup_ssl_cipher(p, c, var+6);
314 else if (ssl != NULL && strlen(var) > 18 && strcEQn(var, "CLIENT_CERT_CHAIN_", 18)) {
315 sk = SSL_get_peer_cert_chain(ssl);
316 result = ssl_var_lookup_ssl_cert_chain(p, sk, var+17);
318 else if (ssl != NULL && strcEQ(var, "CLIENT_VERIFY")) {
319 result = ssl_var_lookup_ssl_cert_verify(p, c);
321 else if (ssl != NULL && strlen(var) > 7 && strcEQn(var, "CLIENT_", 7)) {
322 if ((xs = SSL_get_peer_certificate(ssl)) != NULL)
323 result = ssl_var_lookup_ssl_cert(p, xs, var+7);
325 else if (ssl != NULL && strlen(var) > 7 && strcEQn(var, "SERVER_", 7)) {
326 if ((xs = SSL_get_certificate(ssl)) != NULL)
327 result = ssl_var_lookup_ssl_cert(p, xs, var+7);
332 static char *ssl_var_lookup_ssl_cert(pool *p, X509 *xs, char *var)
343 if (strcEQ(var, "M_VERSION")) {
344 result = ap_psprintf(p, "%lu", X509_get_version(xs)+1);
347 else if (strcEQ(var, "M_SERIAL")) {
348 result = ssl_var_lookup_ssl_cert_serial(p, xs);
350 else if (strcEQ(var, "V_START")) {
351 result = ssl_var_lookup_ssl_cert_valid(p, X509_get_notBefore(xs));
353 else if (strcEQ(var, "V_END")) {
354 result = ssl_var_lookup_ssl_cert_valid(p, X509_get_notAfter(xs));
356 else if (strcEQ(var, "S_DN")) {
357 xsname = X509_get_subject_name(xs);
358 cp = X509_NAME_oneline(xsname, NULL, 0);
359 result = ap_pstrdup(p, cp);
363 else if (strlen(var) > 5 && strcEQn(var, "S_DN_", 5)) {
364 xsname = X509_get_subject_name(xs);
365 result = ssl_var_lookup_ssl_cert_dn(p, xsname, var+5);
368 else if (strcEQ(var, "I_DN")) {
369 xsname = X509_get_issuer_name(xs);
370 cp = X509_NAME_oneline(xsname, NULL, 0);
371 result = ap_pstrdup(p, cp);
375 else if (strlen(var) > 5 && strcEQn(var, "I_DN_", 5)) {
376 xsname = X509_get_issuer_name(xs);
377 result = ssl_var_lookup_ssl_cert_dn(p, xsname, var+5);
380 else if (strcEQ(var, "A_SIG")) {
381 nid = OBJ_obj2nid(xs->cert_info->signature->algorithm);
382 result = ap_pstrdup(p, (nid == NID_undef) ? "UNKNOWN" : OBJ_nid2ln(nid));
385 else if (strcEQ(var, "A_KEY")) {
386 nid = OBJ_obj2nid(xs->cert_info->key->algor->algorithm);
387 result = ap_pstrdup(p, (nid == NID_undef) ? "UNKNOWN" : OBJ_nid2ln(nid));
390 else if (strcEQ(var, "CERT")) {
391 result = ssl_var_lookup_ssl_cert_PEM(p, xs);
394 if (result != NULL && resdup)
395 result = ap_pstrdup(p, result);
399 static const struct {
402 } ssl_var_lookup_ssl_cert_dn_rec[] = {
403 { "C", NID_countryName },
404 { "ST", NID_stateOrProvinceName }, /* officially (RFC2156) */
405 { "SP", NID_stateOrProvinceName }, /* compatibility (SSLeay) */
406 { "L", NID_localityName },
407 { "O", NID_organizationName },
408 { "OU", NID_organizationalUnitName },
409 { "CN", NID_commonName },
411 { "I", NID_initials },
412 { "G", NID_givenName },
413 { "S", NID_surname },
414 { "D", NID_description },
415 { "UID", NID_uniqueIdentifier },
416 { "Email", NID_pkcs9_emailAddress },
420 static char *ssl_var_lookup_ssl_cert_dn(pool *p, X509_NAME *xsname, char *var)
423 X509_NAME_ENTRY *xsne;
428 for (i = 0; ssl_var_lookup_ssl_cert_dn_rec[i].name != NULL; i++) {
429 if (strEQ(var, ssl_var_lookup_ssl_cert_dn_rec[i].name)) {
430 for (j = 0; j < sk_X509_NAME_ENTRY_num(xsname->entries); j++) {
431 xsne = sk_X509_NAME_ENTRY_value(xsname->entries, j);
432 n = OBJ_obj2nid(xsne->object);
433 if (n == ssl_var_lookup_ssl_cert_dn_rec[i].nid) {
434 result = ap_palloc(p, xsne->value->length+1);
435 ap_cpystrn(result, (char *)xsne->value->data, xsne->value->length+1);
436 #ifdef CHARSET_EBCDIC
437 ascii2ebcdic(result, result, xsne->value->length);
438 #endif /* CHARSET_EBCDIC */
439 result[xsne->value->length] = NUL;
449 static char *ssl_var_lookup_ssl_cert_valid(pool *p, ASN1_UTCTIME *tm)
455 if ((bio = BIO_new(BIO_s_mem())) == NULL)
457 ASN1_UTCTIME_print(bio, tm);
458 n = BIO_pending(bio);
459 result = ap_pcalloc(p, n+1);
460 n = BIO_read(bio, result, n);
466 static char *ssl_var_lookup_ssl_cert_serial(pool *p, X509 *xs)
472 if ((bio = BIO_new(BIO_s_mem())) == NULL)
474 i2a_ASN1_INTEGER(bio, X509_get_serialNumber(xs));
475 n = BIO_pending(bio);
476 result = ap_pcalloc(p, n+1);
477 n = BIO_read(bio, result, n);
483 static char *ssl_var_lookup_ssl_cert_chain(pool *p, STACK_OF(X509) *sk, char *var)
491 if (strspn(var, "0123456789") == strlen(var)) {
493 if (n < sk_X509_num(sk)) {
494 xs = sk_X509_value(sk, n);
495 result = ssl_var_lookup_ssl_cert_PEM(p, xs);
502 static char *ssl_var_lookup_ssl_cert_PEM(pool *p, X509 *xs)
508 if ((bio = BIO_new(BIO_s_mem())) == NULL)
510 PEM_write_bio_X509(bio, xs);
511 n = BIO_pending(bio);
512 result = ap_pcalloc(p, n+1);
513 n = BIO_read(bio, result, n);
519 static char *ssl_var_lookup_ssl_cert_verify(pool *p, conn_rec *c)
529 ssl = ap_ctx_get(c->client->ctx, "ssl");
530 verr = ap_ctx_get(c->client->ctx, "ssl::verify::error");
531 vinfo = ap_ctx_get(c->client->ctx, "ssl::verify::info");
532 vrc = SSL_get_verify_result(ssl);
533 xs = SSL_get_peer_certificate(ssl);
535 if (vrc == X509_V_OK && verr == NULL && vinfo == NULL && xs == NULL)
536 /* no client verification done at all */
538 else if (vrc == X509_V_OK && verr == NULL && vinfo == NULL && xs != NULL)
539 /* client verification done successful */
541 else if (vrc == X509_V_OK && vinfo != NULL && strEQ(vinfo, "GENEROUS"))
542 /* client verification done in generous way */
545 /* client verification failed */
546 result = ap_psprintf(p, "FAILED:%s", verr);
550 static char *ssl_var_lookup_ssl_cipher(pool *p, conn_rec *c, char *var)
554 int usekeysize, algkeysize;
560 ssl = ap_ctx_get(c->client->ctx, "ssl");
561 ssl_var_lookup_ssl_cipher_bits(ssl, &usekeysize, &algkeysize);
564 result = (ssl != NULL ? (char *)SSL_get_cipher_name(ssl) : NULL);
565 else if (strcEQ(var, "_EXPORT"))
566 result = (usekeysize < 56 ? "true" : "false");
567 else if (strcEQ(var, "_USEKEYSIZE")) {
568 result = ap_psprintf(p, "%d", usekeysize);
571 else if (strcEQ(var, "_ALGKEYSIZE")) {
572 result = ap_psprintf(p, "%d", algkeysize);
576 if (result != NULL && resdup)
577 result = ap_pstrdup(p, result);
581 static void ssl_var_lookup_ssl_cipher_bits(SSL *ssl, int *usekeysize, int *algkeysize)
588 if ((cipher = SSL_get_current_cipher(ssl)) != NULL)
589 *usekeysize = SSL_CIPHER_get_bits(cipher, algkeysize);
593 static char *ssl_var_lookup_ssl_version(pool *p, char *var)
600 if (strEQ(var, "PRODUCT")) {
601 #if defined(SSL_PRODUCT_NAME) && defined(SSL_PRODUCT_VERSION)
602 result = ap_psprintf(p, "%s/%s", SSL_PRODUCT_NAME, SSL_PRODUCT_VERSION);
607 else if (strEQ(var, "INTERFACE")) {
608 result = ap_psprintf(p, "mod_ssl/%s", MOD_SSL_VERSION);
610 else if (strEQ(var, "LIBRARY")) {
611 result = ap_pstrdup(p, SSL_LIBRARY_TEXT);
612 if ((cp = strchr(result, ' ')) != NULL) {
614 if ((cp2 = strchr(cp, ' ')) != NULL)