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.''
67 /* _________________________________________________________________
70 ** _________________________________________________________________
73 static char *ssl_var_lookup_header(pool *p, request_rec *r, const char *name);
74 static char *ssl_var_lookup_ssl(pool *p, conn_rec *c, char *var);
75 static char *ssl_var_lookup_ssl_cert(pool *p, X509 *xs, char *var);
76 static char *ssl_var_lookup_ssl_cert_dn(pool *p, X509_NAME *xsname, char *var);
77 static char *ssl_var_lookup_ssl_cert_valid(pool *p, ASN1_UTCTIME *tm);
78 static char *ssl_var_lookup_ssl_cert_serial(pool *p, X509 *xs);
79 static char *ssl_var_lookup_ssl_cert_chain(pool *p, STACK_OF(X509) *sk, char *var);
80 static char *ssl_var_lookup_ssl_cert_PEM(pool *p, X509 *xs);
81 static char *ssl_var_lookup_ssl_cert_verify(pool *p, conn_rec *c);
82 static char *ssl_var_lookup_ssl_cipher(pool *p, conn_rec *c, char *var);
83 static void ssl_var_lookup_ssl_cipher_bits(SSL *ssl, int *usekeysize, int *algkeysize);
84 static char *ssl_var_lookup_ssl_version(pool *p, char *var);
86 void ssl_var_register(void)
88 ap_hook_configure("ap::mod_ssl::var_lookup",
89 AP_HOOK_SIG6(ptr,ptr,ptr,ptr,ptr,ptr), AP_HOOK_DECLINE(NULL));
90 ap_hook_register("ap::mod_ssl::var_lookup",
91 ssl_var_lookup, AP_HOOK_NOCTX);
95 void ssl_var_unregister(void)
97 ap_hook_unregister("ap::mod_ssl::var_lookup", ssl_var_lookup);
101 char *ssl_var_lookup(pool *p, server_rec *s, conn_rec *c, request_rec *r, char *var)
103 SSLModConfigRec *mc = myModConfig();
113 * When no pool is given try to find one
125 * Request dependent stuff
128 if (strcEQ(var, "HTTP_USER_AGENT"))
129 result = ssl_var_lookup_header(p, r, "User-Agent");
130 else if (strcEQ(var, "HTTP_REFERER"))
131 result = ssl_var_lookup_header(p, r, "Referer");
132 else if (strcEQ(var, "HTTP_COOKIE"))
133 result = ssl_var_lookup_header(p, r, "Cookie");
134 else if (strcEQ(var, "HTTP_FORWARDED"))
135 result = ssl_var_lookup_header(p, r, "Forwarded");
136 else if (strcEQ(var, "HTTP_HOST"))
137 result = ssl_var_lookup_header(p, r, "Host");
138 else if (strcEQ(var, "HTTP_PROXY_CONNECTION"))
139 result = ssl_var_lookup_header(p, r, "Proxy-Connection");
140 else if (strcEQ(var, "HTTP_ACCEPT"))
141 result = ssl_var_lookup_header(p, r, "Accept");
142 else if (strlen(var) > 5 && strcEQn(var, "HTTP:", 5))
143 /* all other headers from which we are still not know about */
144 result = ssl_var_lookup_header(p, r, var+5);
145 else if (strcEQ(var, "THE_REQUEST"))
146 result = r->the_request;
147 else if (strcEQ(var, "REQUEST_METHOD"))
148 result = (char *)(r->method);
149 else if (strcEQ(var, "REQUEST_SCHEME"))
150 result = ap_http_method(r);
151 else if (strcEQ(var, "REQUEST_URI"))
153 else if (strcEQ(var, "SCRIPT_FILENAME") ||
154 strcEQ(var, "REQUEST_FILENAME"))
155 result = r->filename;
156 else if (strcEQ(var, "PATH_INFO"))
157 result = r->path_info;
158 else if (strcEQ(var, "QUERY_STRING"))
160 else if (strcEQ(var, "REMOTE_HOST"))
161 result = (char *)ap_get_remote_host(r->connection,
162 r->per_dir_config, REMOTE_NAME);
163 else if (strcEQ(var, "REMOTE_IDENT"))
164 result = (char *)ap_get_remote_logname(r);
165 else if (strcEQ(var, "IS_SUBREQ"))
166 result = (r->main != NULL ? "true" : "false");
167 else if (strcEQ(var, "DOCUMENT_ROOT"))
168 result = (char *)ap_document_root(r);
169 else if (strcEQ(var, "SERVER_ADMIN"))
170 result = r->server->server_admin;
171 else if (strcEQ(var, "SERVER_NAME"))
172 result = (char *)ap_get_server_name(r);
173 else if (strcEQ(var, "SERVER_PORT"))
174 result = ap_psprintf(p, "%u", ap_get_server_port(r));
175 else if (strcEQ(var, "SERVER_PROTOCOL"))
176 result = r->protocol;
182 if (result == NULL && c != NULL) {
183 if (strcEQ(var, "REMOTE_ADDR"))
184 result = c->remote_ip;
185 else if (strcEQ(var, "REMOTE_USER"))
187 else if (strcEQ(var, "AUTH_TYPE"))
188 result = c->ap_auth_type;
189 else if (strlen(var) > 4 && strcEQn(var, "SSL_", 4))
190 result = ssl_var_lookup_ssl(p, c, var+4);
191 else if (strcEQ(var, "HTTPS")) {
192 if (ap_ctx_get(c->client->ctx, "ssl") != NULL)
200 * Totally independent stuff
202 if (result == NULL) {
203 if (strlen(var) > 12 && strcEQn(var, "SSL_VERSION_", 12))
204 result = ssl_var_lookup_ssl_version(p, var+12);
205 else if (strcEQ(var, "SERVER_SOFTWARE"))
206 result = (char *)ap_get_server_version();
207 else if (strcEQ(var, "API_VERSION")) {
208 result = ap_psprintf(p, "%d", MODULE_MAGIC_NUMBER);
211 else if (strcEQ(var, "TIME_YEAR")) {
214 result = ap_psprintf(p, "%02d%02d",
215 (tm->tm_year / 100) + 19, tm->tm_year % 100);
218 #define MKTIMESTR(format, tmfield) \
220 tm = localtime(&tc); \
221 result = ap_psprintf(p, format, tm->tmfield); \
223 else if (strcEQ(var, "TIME_MON")) {
224 MKTIMESTR("%02d", tm_mon+1)
226 else if (strcEQ(var, "TIME_DAY")) {
227 MKTIMESTR("%02d", tm_mday)
229 else if (strcEQ(var, "TIME_HOUR")) {
230 MKTIMESTR("%02d", tm_hour)
232 else if (strcEQ(var, "TIME_MIN")) {
233 MKTIMESTR("%02d", tm_min)
235 else if (strcEQ(var, "TIME_SEC")) {
236 MKTIMESTR("%02d", tm_sec)
238 else if (strcEQ(var, "TIME_WDAY")) {
239 MKTIMESTR("%d", tm_wday)
241 else if (strcEQ(var, "TIME")) {
244 result = ap_psprintf(p,
245 "%02d%02d%02d%02d%02d%02d%02d", (tm->tm_year / 100) + 19,
246 (tm->tm_year % 100), tm->tm_mon+1, tm->tm_mday,
247 tm->tm_hour, tm->tm_min, tm->tm_sec);
250 /* all other env-variables from the parent Apache process */
251 else if (strlen(var) > 4 && strcEQn(var, "ENV:", 4)) {
252 result = (char *)ap_table_get(r->notes, var+4);
254 result = (char *)ap_table_get(r->subprocess_env, var+4);
256 result = getenv(var+4);
260 if (result != NULL && resdup)
261 result = ap_pstrdup(p, result);
267 static char *ssl_var_lookup_header(pool *p, request_rec *r, const char *name)
269 array_header *hdrs_arr;
273 hdrs_arr = ap_table_elts(r->headers_in);
274 hdrs = (table_entry *)hdrs_arr->elts;
275 for (i = 0; i < hdrs_arr->nelts; ++i) {
276 if (hdrs[i].key == NULL)
278 if (strcEQ(hdrs[i].key, name))
279 return ap_pstrdup(p, hdrs[i].val);
284 static char *ssl_var_lookup_ssl(pool *p, conn_rec *c, char *var)
293 ssl = ap_ctx_get(c->client->ctx, "ssl");
294 if (strlen(var) > 8 && strcEQn(var, "VERSION_", 8)) {
295 result = ssl_var_lookup_ssl_version(p, var+8);
297 else if (ssl != NULL && strcEQ(var, "PROTOCOL")) {
298 result = (char *)SSL_get_version(ssl);
300 else if (ssl != NULL && strcEQ(var, "SESSION_ID")) {
301 SSL_SESSION *pSession = SSL_get_session(ssl);
302 result = ap_pstrdup(p, SSL_SESSION_id2sz(pSession->session_id,
303 pSession->session_id_length));
305 else if (ssl != NULL && strlen(var) >= 6 && strcEQn(var, "CIPHER", 6)) {
306 result = ssl_var_lookup_ssl_cipher(p, c, var+6);
308 else if (ssl != NULL && strlen(var) > 18 && strcEQn(var, "CLIENT_CERT_CHAIN_", 18)) {
309 sk = SSL_get_peer_cert_chain(ssl);
310 result = ssl_var_lookup_ssl_cert_chain(p, sk, var+17);
312 else if (ssl != NULL && strcEQ(var, "CLIENT_VERIFY")) {
313 result = ssl_var_lookup_ssl_cert_verify(p, c);
315 else if (ssl != NULL && strlen(var) > 7 && strcEQn(var, "CLIENT_", 7)) {
316 if ((xs = SSL_get_peer_certificate(ssl)) != NULL)
317 result = ssl_var_lookup_ssl_cert(p, xs, var+7);
319 else if (ssl != NULL && strlen(var) > 7 && strcEQn(var, "SERVER_", 7)) {
320 if ((xs = SSL_get_certificate(ssl)) != NULL)
321 result = ssl_var_lookup_ssl_cert(p, xs, var+7);
326 static char *ssl_var_lookup_ssl_cert(pool *p, X509 *xs, char *var)
337 if (strcEQ(var, "M_VERSION")) {
338 result = ap_psprintf(p, "%lu", X509_get_version(xs)+1);
341 else if (strcEQ(var, "M_SERIAL")) {
342 result = ssl_var_lookup_ssl_cert_serial(p, xs);
344 else if (strcEQ(var, "V_START")) {
345 result = ssl_var_lookup_ssl_cert_valid(p, X509_get_notBefore(xs));
347 else if (strcEQ(var, "V_END")) {
348 result = ssl_var_lookup_ssl_cert_valid(p, X509_get_notAfter(xs));
350 else if (strcEQ(var, "S_DN")) {
351 xsname = X509_get_subject_name(xs);
352 cp = X509_NAME_oneline(xsname, NULL, 0);
353 result = ap_pstrdup(p, cp);
357 else if (strlen(var) > 5 && strcEQn(var, "S_DN_", 5)) {
358 xsname = X509_get_subject_name(xs);
359 result = ssl_var_lookup_ssl_cert_dn(p, xsname, var+5);
362 else if (strcEQ(var, "I_DN")) {
363 xsname = X509_get_issuer_name(xs);
364 cp = X509_NAME_oneline(xsname, NULL, 0);
365 result = ap_pstrdup(p, cp);
369 else if (strlen(var) > 5 && strcEQn(var, "I_DN_", 5)) {
370 xsname = X509_get_issuer_name(xs);
371 result = ssl_var_lookup_ssl_cert_dn(p, xsname, var+5);
374 else if (strcEQ(var, "A_SIG")) {
375 nid = OBJ_obj2nid(xs->cert_info->signature->algorithm);
376 result = ap_pstrdup(p, (nid == NID_undef) ? "UNKNOWN" : OBJ_nid2ln(nid));
379 else if (strcEQ(var, "A_KEY")) {
380 nid = OBJ_obj2nid(xs->cert_info->key->algor->algorithm);
381 result = ap_pstrdup(p, (nid == NID_undef) ? "UNKNOWN" : OBJ_nid2ln(nid));
384 else if (strcEQ(var, "CERT")) {
385 result = ssl_var_lookup_ssl_cert_PEM(p, xs);
388 if (result != NULL && resdup)
389 result = ap_pstrdup(p, result);
393 static const struct {
396 } ssl_var_lookup_ssl_cert_dn_rec[] = {
397 { "C", NID_countryName },
398 { "ST", NID_stateOrProvinceName }, /* officially (RFC2156) */
399 { "SP", NID_stateOrProvinceName }, /* compatibility (SSLeay) */
400 { "L", NID_localityName },
401 { "O", NID_organizationName },
402 { "OU", NID_organizationalUnitName },
403 { "CN", NID_commonName },
405 { "I", NID_initials },
406 { "G", NID_givenName },
407 { "S", NID_surname },
408 { "D", NID_description },
409 { "UID", NID_uniqueIdentifier },
410 { "Email", NID_pkcs9_emailAddress },
414 static char *ssl_var_lookup_ssl_cert_dn(pool *p, X509_NAME *xsname, char *var)
417 X509_NAME_ENTRY *xsne;
422 for (i = 0; ssl_var_lookup_ssl_cert_dn_rec[i].name != NULL; i++) {
423 if (strEQ(var, ssl_var_lookup_ssl_cert_dn_rec[i].name)) {
424 for (j = 0; j < sk_X509_NAME_ENTRY_num(xsname->entries); j++) {
425 xsne = sk_X509_NAME_ENTRY_value(xsname->entries, j);
426 n = OBJ_obj2nid(xsne->object);
427 if (n == ssl_var_lookup_ssl_cert_dn_rec[i].nid) {
428 result = ap_palloc(p, xsne->value->length+1);
429 ap_cpystrn(result, (char *)xsne->value->data, xsne->value->length+1);
430 #ifdef CHARSET_EBCDIC
431 ascii2ebcdic(result, result, xsne->value->length);
432 #endif /* CHARSET_EBCDIC */
433 result[xsne->value->length] = NUL;
443 static char *ssl_var_lookup_ssl_cert_valid(pool *p, ASN1_UTCTIME *tm)
449 if ((bio = BIO_new(BIO_s_mem())) == NULL)
451 ASN1_UTCTIME_print(bio, tm);
452 n = BIO_pending(bio);
453 result = ap_pcalloc(p, n+1);
454 n = BIO_read(bio, result, n);
460 static char *ssl_var_lookup_ssl_cert_serial(pool *p, X509 *xs)
466 if ((bio = BIO_new(BIO_s_mem())) == NULL)
468 i2a_ASN1_INTEGER(bio, X509_get_serialNumber(xs));
469 n = BIO_pending(bio);
470 result = ap_pcalloc(p, n+1);
471 n = BIO_read(bio, result, n);
477 static char *ssl_var_lookup_ssl_cert_chain(pool *p, STACK_OF(X509) *sk, char *var)
485 if (strspn(var, "0123456789") == strlen(var)) {
487 if (n < sk_X509_num(sk)) {
488 xs = sk_X509_value(sk, n);
489 result = ssl_var_lookup_ssl_cert_PEM(p, xs);
496 static char *ssl_var_lookup_ssl_cert_PEM(pool *p, X509 *xs)
502 if ((bio = BIO_new(BIO_s_mem())) == NULL)
504 PEM_write_bio_X509(bio, xs);
505 n = BIO_pending(bio);
506 result = ap_pcalloc(p, n+1);
507 n = BIO_read(bio, result, n);
513 static char *ssl_var_lookup_ssl_cert_verify(pool *p, conn_rec *c)
523 ssl = ap_ctx_get(c->client->ctx, "ssl");
524 verr = ap_ctx_get(c->client->ctx, "ssl::verify::error");
525 vinfo = ap_ctx_get(c->client->ctx, "ssl::verify::info");
526 vrc = SSL_get_verify_result(ssl);
527 xs = SSL_get_peer_certificate(ssl);
529 if (vrc == X509_V_OK && verr == NULL && vinfo == NULL && xs == NULL)
530 /* no client verification done at all */
532 else if (vrc == X509_V_OK && verr == NULL && vinfo == NULL && xs != NULL)
533 /* client verification done successful */
535 else if (vrc == X509_V_OK && vinfo != NULL && strEQ(vinfo, "GENEROUS"))
536 /* client verification done in generous way */
539 /* client verification failed */
540 result = ap_psprintf(p, "FAILED:%s", verr);
544 static char *ssl_var_lookup_ssl_cipher(pool *p, conn_rec *c, char *var)
548 int usekeysize, algkeysize;
554 ssl = ap_ctx_get(c->client->ctx, "ssl");
555 ssl_var_lookup_ssl_cipher_bits(ssl, &usekeysize, &algkeysize);
558 result = (ssl != NULL ? (char *)SSL_get_cipher_name(ssl) : NULL);
559 else if (strcEQ(var, "_EXPORT"))
560 result = (usekeysize < 56 ? "true" : "false");
561 else if (strcEQ(var, "_USEKEYSIZE")) {
562 result = ap_psprintf(p, "%d", usekeysize);
565 else if (strcEQ(var, "_ALGKEYSIZE")) {
566 result = ap_psprintf(p, "%d", algkeysize);
570 if (result != NULL && resdup)
571 result = ap_pstrdup(p, result);
575 static void ssl_var_lookup_ssl_cipher_bits(SSL *ssl, int *usekeysize, int *algkeysize)
582 if ((cipher = SSL_get_current_cipher(ssl)) != NULL)
583 *usekeysize = SSL_CIPHER_get_bits(cipher, algkeysize);
587 static char *ssl_var_lookup_ssl_version(pool *p, char *var)
594 if (strEQ(var, "PRODUCT")) {
595 #if defined(SSL_PRODUCT_NAME) && defined(SSL_PRODUCT_VERSION)
596 result = ap_psprintf(p, "%s/%s", SSL_PRODUCT_NAME, SSL_PRODUCT_VERSION);
601 else if (strEQ(var, "INTERFACE")) {
602 result = ap_psprintf(p, "mod_ssl/%s", MOD_SSL_VERSION);
604 else if (strEQ(var, "LIBRARY")) {
605 result = ap_pstrdup(p, SSL_LIBRARY_TEXT);
606 if ((cp = strchr(result, ' ')) != NULL) {
608 if ((cp2 = strchr(cp, ' ')) != NULL)