Moved apache code into a folder to help prepare for packaging where we dont want...
[httpd-crcsyncproxy.git] / apache / modules / ssl / ssl_engine_vars.c
blob8d6414722713cbe62865ab8a61680d7480bd5be3
1 /* Licensed to the Apache Software Foundation (ASF) under one or more
2 * contributor license agreements. See the NOTICE file distributed with
3 * this work for additional information regarding copyright ownership.
4 * The ASF licenses this file to You under the Apache License, Version 2.0
5 * (the "License"); you may not use this file except in compliance with
6 * the License. You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
17 /* _ _
18 * _ __ ___ ___ __| | ___ ___| | mod_ssl
19 * | '_ ` _ \ / _ \ / _` | / __/ __| | Apache Interface to OpenSSL
20 * | | | | | | (_) | (_| | \__ \__ \ |
21 * |_| |_| |_|\___/ \__,_|___|___/___/_|
22 * |_____|
23 * ssl_engine_vars.c
24 * Variable Lookup Facility
26 /* ``Those of you who think they
27 know everything are very annoying
28 to those of us who do.''
29 -- Unknown */
30 #include "ssl_private.h"
31 #include "mod_ssl.h"
33 #include "apr_time.h"
35 /* _________________________________________________________________
37 ** Variable Lookup
38 ** _________________________________________________________________
41 static char *ssl_var_lookup_ssl(apr_pool_t *p, conn_rec *c, char *var);
42 static char *ssl_var_lookup_ssl_cert(apr_pool_t *p, X509 *xs, char *var);
43 static char *ssl_var_lookup_ssl_cert_dn(apr_pool_t *p, X509_NAME *xsname, char *var);
44 static char *ssl_var_lookup_ssl_cert_valid(apr_pool_t *p, ASN1_UTCTIME *tm);
45 static char *ssl_var_lookup_ssl_cert_remain(apr_pool_t *p, ASN1_UTCTIME *tm);
46 static char *ssl_var_lookup_ssl_cert_serial(apr_pool_t *p, X509 *xs);
47 static char *ssl_var_lookup_ssl_cert_chain(apr_pool_t *p, STACK_OF(X509) *sk, char *var);
48 static char *ssl_var_lookup_ssl_cert_PEM(apr_pool_t *p, X509 *xs);
49 static char *ssl_var_lookup_ssl_cert_verify(apr_pool_t *p, conn_rec *c);
50 static char *ssl_var_lookup_ssl_cipher(apr_pool_t *p, conn_rec *c, char *var);
51 static void ssl_var_lookup_ssl_cipher_bits(SSL *ssl, int *usekeysize, int *algkeysize);
52 static char *ssl_var_lookup_ssl_version(apr_pool_t *p, char *var);
53 static char *ssl_var_lookup_ssl_compress_meth(SSL *ssl);
55 static int ssl_is_https(conn_rec *c)
57 SSLConnRec *sslconn = myConnConfig(c);
58 return sslconn && sslconn->ssl;
61 static const char var_interface[] = "mod_ssl/" MOD_SSL_VERSION;
62 static char var_library_interface[] = SSL_LIBRARY_TEXT;
63 static char *var_library = NULL;
65 void ssl_var_register(apr_pool_t *p)
67 char *cp, *cp2;
69 APR_REGISTER_OPTIONAL_FN(ssl_is_https);
70 APR_REGISTER_OPTIONAL_FN(ssl_var_lookup);
71 APR_REGISTER_OPTIONAL_FN(ssl_ext_list);
73 /* Perform once-per-process library version determination: */
74 var_library = apr_pstrdup(p, SSL_LIBRARY_DYNTEXT);
76 if ((cp = strchr(var_library, ' ')) != NULL) {
77 *cp = '/';
78 if ((cp2 = strchr(cp, ' ')) != NULL)
79 *cp2 = NUL;
82 if ((cp = strchr(var_library_interface, ' ')) != NULL) {
83 *cp = '/';
84 if ((cp2 = strchr(cp, ' ')) != NULL)
85 *cp2 = NUL;
89 /* This function must remain safe to use for a non-SSL connection. */
90 char *ssl_var_lookup(apr_pool_t *p, server_rec *s, conn_rec *c, request_rec *r, char *var)
92 SSLModConfigRec *mc = myModConfig(s);
93 const char *result;
94 BOOL resdup;
95 apr_time_exp_t tm;
97 result = NULL;
98 resdup = TRUE;
101 * When no pool is given try to find one
103 if (p == NULL) {
104 if (r != NULL)
105 p = r->pool;
106 else if (c != NULL)
107 p = c->pool;
108 else
109 p = mc->pPool;
113 * Request dependent stuff
115 if (r != NULL) {
116 switch (var[0]) {
117 case 'H':
118 case 'h':
119 if (strcEQ(var, "HTTP_USER_AGENT"))
120 result = apr_table_get(r->headers_in, "User-Agent");
121 else if (strcEQ(var, "HTTP_REFERER"))
122 result = apr_table_get(r->headers_in, "Referer");
123 else if (strcEQ(var, "HTTP_COOKIE"))
124 result = apr_table_get(r->headers_in, "Cookie");
125 else if (strcEQ(var, "HTTP_FORWARDED"))
126 result = apr_table_get(r->headers_in, "Forwarded");
127 else if (strcEQ(var, "HTTP_HOST"))
128 result = apr_table_get(r->headers_in, "Host");
129 else if (strcEQ(var, "HTTP_PROXY_CONNECTION"))
130 result = apr_table_get(r->headers_in, "Proxy-Connection");
131 else if (strcEQ(var, "HTTP_ACCEPT"))
132 result = apr_table_get(r->headers_in, "Accept");
133 else if (strlen(var) > 5 && strcEQn(var, "HTTP:", 5))
134 /* all other headers from which we are still not know about */
135 result = apr_table_get(r->headers_in, var+5);
136 break;
138 case 'R':
139 case 'r':
140 if (strcEQ(var, "REQUEST_METHOD"))
141 result = r->method;
142 else if (strcEQ(var, "REQUEST_SCHEME"))
143 result = ap_http_scheme(r);
144 else if (strcEQ(var, "REQUEST_URI"))
145 result = r->uri;
146 else if (strcEQ(var, "REQUEST_FILENAME"))
147 result = r->filename;
148 else if (strcEQ(var, "REMOTE_HOST"))
149 result = ap_get_remote_host(r->connection, r->per_dir_config,
150 REMOTE_NAME, NULL);
151 else if (strcEQ(var, "REMOTE_IDENT"))
152 result = ap_get_remote_logname(r);
153 else if (strcEQ(var, "REMOTE_USER"))
154 result = r->user;
155 break;
157 case 'S':
158 case 's':
159 if (strcEQn(var, "SSL", 3)) break; /* shortcut common case */
161 if (strcEQ(var, "SERVER_ADMIN"))
162 result = r->server->server_admin;
163 else if (strcEQ(var, "SERVER_NAME"))
164 result = ap_get_server_name(r);
165 else if (strcEQ(var, "SERVER_PORT"))
166 result = apr_psprintf(p, "%u", ap_get_server_port(r));
167 else if (strcEQ(var, "SERVER_PROTOCOL"))
168 result = r->protocol;
169 else if (strcEQ(var, "SCRIPT_FILENAME"))
170 result = r->filename;
171 break;
173 default:
174 if (strcEQ(var, "PATH_INFO"))
175 result = r->path_info;
176 else if (strcEQ(var, "QUERY_STRING"))
177 result = r->args;
178 else if (strcEQ(var, "IS_SUBREQ"))
179 result = (r->main != NULL ? "true" : "false");
180 else if (strcEQ(var, "DOCUMENT_ROOT"))
181 result = ap_document_root(r);
182 else if (strcEQ(var, "AUTH_TYPE"))
183 result = r->ap_auth_type;
184 else if (strcEQ(var, "THE_REQUEST"))
185 result = r->the_request;
186 break;
191 * Connection stuff
193 if (result == NULL && c != NULL) {
194 SSLConnRec *sslconn = myConnConfig(c);
195 if (strlen(var) > 4 && strcEQn(var, "SSL_", 4)
196 && sslconn && sslconn->ssl)
197 result = ssl_var_lookup_ssl(p, c, var+4);
198 else if (strcEQ(var, "REMOTE_ADDR"))
199 result = c->remote_ip;
200 else if (strcEQ(var, "HTTPS")) {
201 if (sslconn && sslconn->ssl)
202 result = "on";
203 else
204 result = "off";
209 * Totally independent stuff
211 if (result == NULL) {
212 if (strlen(var) > 12 && strcEQn(var, "SSL_VERSION_", 12))
213 result = ssl_var_lookup_ssl_version(p, var+12);
214 else if (strcEQ(var, "SERVER_SOFTWARE"))
215 result = ap_get_server_banner();
216 else if (strcEQ(var, "API_VERSION")) {
217 result = apr_itoa(p, MODULE_MAGIC_NUMBER);
218 resdup = FALSE;
220 else if (strcEQ(var, "TIME_YEAR")) {
221 apr_time_exp_lt(&tm, apr_time_now());
222 result = apr_psprintf(p, "%02d%02d",
223 (tm.tm_year / 100) + 19, tm.tm_year % 100);
224 resdup = FALSE;
226 #define MKTIMESTR(format, tmfield) \
227 apr_time_exp_lt(&tm, apr_time_now()); \
228 result = apr_psprintf(p, format, tm.tmfield); \
229 resdup = FALSE;
230 else if (strcEQ(var, "TIME_MON")) {
231 MKTIMESTR("%02d", tm_mon+1)
233 else if (strcEQ(var, "TIME_DAY")) {
234 MKTIMESTR("%02d", tm_mday)
236 else if (strcEQ(var, "TIME_HOUR")) {
237 MKTIMESTR("%02d", tm_hour)
239 else if (strcEQ(var, "TIME_MIN")) {
240 MKTIMESTR("%02d", tm_min)
242 else if (strcEQ(var, "TIME_SEC")) {
243 MKTIMESTR("%02d", tm_sec)
245 else if (strcEQ(var, "TIME_WDAY")) {
246 MKTIMESTR("%d", tm_wday)
248 else if (strcEQ(var, "TIME")) {
249 apr_time_exp_lt(&tm, apr_time_now());
250 result = apr_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);
254 resdup = FALSE;
256 /* all other env-variables from the parent Apache process */
257 else if (strlen(var) > 4 && strcEQn(var, "ENV:", 4)) {
258 result = apr_table_get(r->notes, var+4);
259 if (result == NULL)
260 result = apr_table_get(r->subprocess_env, var+4);
261 if (result == NULL)
262 result = getenv(var+4);
266 if (result != NULL && resdup)
267 result = apr_pstrdup(p, result);
268 if (result == NULL)
269 result = "";
270 return (char *)result;
273 static char *ssl_var_lookup_ssl(apr_pool_t *p, conn_rec *c, char *var)
275 SSLConnRec *sslconn = myConnConfig(c);
276 char *result;
277 X509 *xs;
278 STACK_OF(X509) *sk;
279 SSL *ssl;
281 result = NULL;
283 ssl = sslconn->ssl;
284 if (strlen(var) > 8 && strcEQn(var, "VERSION_", 8)) {
285 result = ssl_var_lookup_ssl_version(p, var+8);
287 else if (ssl != NULL && strcEQ(var, "PROTOCOL")) {
288 result = (char *)SSL_get_version(ssl);
290 else if (ssl != NULL && strcEQ(var, "SESSION_ID")) {
291 char buf[SSL_SESSION_ID_STRING_LEN];
292 SSL_SESSION *pSession = SSL_get_session(ssl);
293 if (pSession) {
294 result = apr_pstrdup(p, SSL_SESSION_id2sz(
295 SSL_SESSION_get_session_id(pSession),
296 SSL_SESSION_get_session_id_length(pSession),
297 buf, sizeof(buf)));
300 else if (ssl != NULL && strlen(var) >= 6 && strcEQn(var, "CIPHER", 6)) {
301 result = ssl_var_lookup_ssl_cipher(p, c, var+6);
303 else if (ssl != NULL && strlen(var) > 18 && strcEQn(var, "CLIENT_CERT_CHAIN_", 18)) {
304 sk = SSL_get_peer_cert_chain(ssl);
305 result = ssl_var_lookup_ssl_cert_chain(p, sk, var+18);
307 else if (ssl != NULL && strcEQ(var, "CLIENT_VERIFY")) {
308 result = ssl_var_lookup_ssl_cert_verify(p, c);
310 else if (ssl != NULL && strlen(var) > 7 && strcEQn(var, "CLIENT_", 7)) {
311 if ((xs = SSL_get_peer_certificate(ssl)) != NULL) {
312 result = ssl_var_lookup_ssl_cert(p, xs, var+7);
313 X509_free(xs);
316 else if (ssl != NULL && strlen(var) > 7 && strcEQn(var, "SERVER_", 7)) {
317 if ((xs = SSL_get_certificate(ssl)) != NULL)
318 result = ssl_var_lookup_ssl_cert(p, xs, var+7);
320 else if (ssl != NULL && strcEQ(var, "COMPRESS_METHOD")) {
321 result = ssl_var_lookup_ssl_compress_meth(ssl);
323 #ifndef OPENSSL_NO_TLSEXT
324 else if (ssl != NULL && strcEQ(var, "TLS_SNI")) {
325 result = apr_pstrdup(p, SSL_get_servername(ssl,
326 TLSEXT_NAMETYPE_host_name));
328 #endif
329 return result;
332 static char *ssl_var_lookup_ssl_cert(apr_pool_t *p, X509 *xs, char *var)
334 char *result;
335 BOOL resdup;
336 X509_NAME *xsname;
337 int nid;
338 char *cp;
340 result = NULL;
341 resdup = TRUE;
343 if (strcEQ(var, "M_VERSION")) {
344 result = apr_psprintf(p, "%lu", X509_get_version(xs)+1);
345 resdup = FALSE;
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, "V_REMAIN")) {
357 result = ssl_var_lookup_ssl_cert_remain(p, X509_get_notAfter(xs));
358 resdup = FALSE;
360 else if (strcEQ(var, "S_DN")) {
361 xsname = X509_get_subject_name(xs);
362 cp = X509_NAME_oneline(xsname, NULL, 0);
363 result = apr_pstrdup(p, cp);
364 modssl_free(cp);
365 resdup = FALSE;
367 else if (strlen(var) > 5 && strcEQn(var, "S_DN_", 5)) {
368 xsname = X509_get_subject_name(xs);
369 result = ssl_var_lookup_ssl_cert_dn(p, xsname, var+5);
370 resdup = FALSE;
372 else if (strcEQ(var, "I_DN")) {
373 xsname = X509_get_issuer_name(xs);
374 cp = X509_NAME_oneline(xsname, NULL, 0);
375 result = apr_pstrdup(p, cp);
376 modssl_free(cp);
377 resdup = FALSE;
379 else if (strlen(var) > 5 && strcEQn(var, "I_DN_", 5)) {
380 xsname = X509_get_issuer_name(xs);
381 result = ssl_var_lookup_ssl_cert_dn(p, xsname, var+5);
382 resdup = FALSE;
384 else if (strcEQ(var, "A_SIG")) {
385 nid = OBJ_obj2nid((ASN1_OBJECT *)X509_get_signature_algorithm(xs));
386 result = apr_pstrdup(p,
387 (nid == NID_undef) ? "UNKNOWN" : OBJ_nid2ln(nid));
388 resdup = FALSE;
390 else if (strcEQ(var, "A_KEY")) {
391 nid = OBJ_obj2nid((ASN1_OBJECT *)X509_get_key_algorithm(xs));
392 result = apr_pstrdup(p,
393 (nid == NID_undef) ? "UNKNOWN" : OBJ_nid2ln(nid));
394 resdup = FALSE;
396 else if (strcEQ(var, "CERT")) {
397 result = ssl_var_lookup_ssl_cert_PEM(p, xs);
400 if (result != NULL && resdup)
401 result = apr_pstrdup(p, result);
402 return result;
405 /* In this table, .extract is non-zero if RDNs using the NID should be
406 * extracted to for the SSL_{CLIENT,SERVER}_{I,S}_DN_* environment
407 * variables. */
408 static const struct {
409 char *name;
410 int nid;
411 int extract;
412 } ssl_var_lookup_ssl_cert_dn_rec[] = {
413 { "C", NID_countryName, 1 },
414 { "ST", NID_stateOrProvinceName, 1 }, /* officially (RFC2156) */
415 { "SP", NID_stateOrProvinceName, 0 }, /* compatibility (SSLeay) */
416 { "L", NID_localityName, 1 },
417 { "O", NID_organizationName, 1 },
418 { "OU", NID_organizationalUnitName, 1 },
419 { "CN", NID_commonName, 1 },
420 { "T", NID_title, 1 },
421 { "I", NID_initials, 1 },
422 { "G", NID_givenName, 1 },
423 { "S", NID_surname, 1 },
424 { "D", NID_description, 1 },
425 #ifdef NID_x500UniqueIdentifier /* new name as of Openssl 0.9.7 */
426 { "UID", NID_x500UniqueIdentifier, 1 },
427 #else /* old name, OpenSSL < 0.9.7 */
428 { "UID", NID_uniqueIdentifier, 1 },
429 #endif
430 { "Email", NID_pkcs9_emailAddress, 1 },
431 { NULL, 0 }
434 static char *ssl_var_lookup_ssl_cert_dn(apr_pool_t *p, X509_NAME *xsname, char *var)
436 char *result, *ptr;
437 X509_NAME_ENTRY *xsne;
438 int i, j, n, idx = 0;
439 apr_size_t varlen;
441 /* if an _N suffix is used, find the Nth attribute of given name */
442 ptr = strchr(var, '_');
443 if (ptr != NULL && strspn(ptr + 1, "0123456789") == strlen(ptr + 1)) {
444 idx = atoi(ptr + 1);
445 varlen = ptr - var;
446 } else {
447 varlen = strlen(var);
450 result = NULL;
452 for (i = 0; ssl_var_lookup_ssl_cert_dn_rec[i].name != NULL; i++) {
453 if (strEQn(var, ssl_var_lookup_ssl_cert_dn_rec[i].name, varlen)
454 && strlen(ssl_var_lookup_ssl_cert_dn_rec[i].name) == varlen) {
455 for (j = 0; j < sk_X509_NAME_ENTRY_num((STACK_OF(X509_NAME_ENTRY) *)
456 X509_NAME_get_entries(xsname));
457 j++) {
458 xsne = sk_X509_NAME_ENTRY_value((STACK_OF(X509_NAME_ENTRY) *)
459 X509_NAME_get_entries(xsname), j);
461 n =OBJ_obj2nid((ASN1_OBJECT *)X509_NAME_ENTRY_get_object(xsne));
463 if (n == ssl_var_lookup_ssl_cert_dn_rec[i].nid && idx-- == 0) {
464 unsigned char *data = X509_NAME_ENTRY_get_data_ptr(xsne);
465 /* cast needed from unsigned char to char */
466 result = apr_pstrmemdup(p, (char *)data,
467 X509_NAME_ENTRY_get_data_len(xsne));
468 #if APR_CHARSET_EBCDIC
469 ap_xlate_proto_from_ascii(result, X509_NAME_ENTRY_get_data_len(xsne));
470 #endif /* APR_CHARSET_EBCDIC */
471 break;
474 break;
477 return result;
480 static char *ssl_var_lookup_ssl_cert_valid(apr_pool_t *p, ASN1_UTCTIME *tm)
482 char *result;
483 BIO* bio;
484 int n;
486 if ((bio = BIO_new(BIO_s_mem())) == NULL)
487 return NULL;
488 ASN1_UTCTIME_print(bio, tm);
489 n = BIO_pending(bio);
490 result = apr_pcalloc(p, n+1);
491 n = BIO_read(bio, result, n);
492 result[n] = NUL;
493 BIO_free(bio);
494 return result;
497 #define DIGIT2NUM(x) (((x)[0] - '0') * 10 + (x)[1] - '0')
499 /* Return a string giving the number of days remaining until 'tm', or
500 * "0" if this can't be determined. */
501 static char *ssl_var_lookup_ssl_cert_remain(apr_pool_t *p, ASN1_UTCTIME *tm)
503 apr_time_t then, now = apr_time_now();
504 apr_time_exp_t exp = {0};
505 long diff;
507 /* Fail if the time isn't a valid ASN.1 UTCTIME; RFC3280 mandates
508 * that the seconds digits are present even though ASN.1
509 * doesn't. */
510 if (tm->length < 11 || !ASN1_UTCTIME_check(tm)) {
511 return apr_pstrdup(p, "0");
514 exp.tm_year = DIGIT2NUM(tm->data);
515 exp.tm_mon = DIGIT2NUM(tm->data + 2) - 1;
516 exp.tm_mday = DIGIT2NUM(tm->data + 4) + 1;
517 exp.tm_hour = DIGIT2NUM(tm->data + 6);
518 exp.tm_min = DIGIT2NUM(tm->data + 8);
519 exp.tm_sec = DIGIT2NUM(tm->data + 10);
521 if (exp.tm_year <= 50) exp.tm_year += 100;
523 if (apr_time_exp_gmt_get(&then, &exp) != APR_SUCCESS) {
524 return apr_pstrdup(p, "0");
527 diff = (long)((apr_time_sec(then) - apr_time_sec(now)) / (60*60*24));
529 return diff > 0 ? apr_ltoa(p, diff) : apr_pstrdup(p, "0");
532 static char *ssl_var_lookup_ssl_cert_serial(apr_pool_t *p, X509 *xs)
534 char *result;
535 BIO *bio;
536 int n;
538 if ((bio = BIO_new(BIO_s_mem())) == NULL)
539 return NULL;
540 i2a_ASN1_INTEGER(bio, X509_get_serialNumber(xs));
541 n = BIO_pending(bio);
542 result = apr_pcalloc(p, n+1);
543 n = BIO_read(bio, result, n);
544 result[n] = NUL;
545 BIO_free(bio);
546 return result;
549 static char *ssl_var_lookup_ssl_cert_chain(apr_pool_t *p, STACK_OF(X509) *sk, char *var)
551 char *result;
552 X509 *xs;
553 int n;
555 result = NULL;
557 if (strspn(var, "0123456789") == strlen(var)) {
558 n = atoi(var);
559 if (n < sk_X509_num(sk)) {
560 xs = sk_X509_value(sk, n);
561 result = ssl_var_lookup_ssl_cert_PEM(p, xs);
565 return result;
568 static char *ssl_var_lookup_ssl_cert_PEM(apr_pool_t *p, X509 *xs)
570 char *result;
571 BIO *bio;
572 int n;
574 if ((bio = BIO_new(BIO_s_mem())) == NULL)
575 return NULL;
576 PEM_write_bio_X509(bio, xs);
577 n = BIO_pending(bio);
578 result = apr_pcalloc(p, n+1);
579 n = BIO_read(bio, result, n);
580 result[n] = NUL;
581 BIO_free(bio);
582 return result;
585 static char *ssl_var_lookup_ssl_cert_verify(apr_pool_t *p, conn_rec *c)
587 SSLConnRec *sslconn = myConnConfig(c);
588 char *result;
589 long vrc;
590 const char *verr;
591 const char *vinfo;
592 SSL *ssl;
593 X509 *xs;
595 result = NULL;
596 ssl = sslconn->ssl;
597 verr = sslconn->verify_error;
598 vinfo = sslconn->verify_info;
599 vrc = SSL_get_verify_result(ssl);
600 xs = SSL_get_peer_certificate(ssl);
602 if (vrc == X509_V_OK && verr == NULL && vinfo == NULL && xs == NULL)
603 /* no client verification done at all */
604 result = "NONE";
605 else if (vrc == X509_V_OK && verr == NULL && vinfo == NULL && xs != NULL)
606 /* client verification done successful */
607 result = "SUCCESS";
608 else if (vrc == X509_V_OK && vinfo != NULL && strEQ(vinfo, "GENEROUS"))
609 /* client verification done in generous way */
610 result = "GENEROUS";
611 else
612 /* client verification failed */
613 result = apr_psprintf(p, "FAILED:%s", verr);
615 if (xs)
616 X509_free(xs);
617 return result;
620 static char *ssl_var_lookup_ssl_cipher(apr_pool_t *p, conn_rec *c, char *var)
622 SSLConnRec *sslconn = myConnConfig(c);
623 char *result;
624 BOOL resdup;
625 int usekeysize, algkeysize;
626 SSL *ssl;
628 result = NULL;
629 resdup = TRUE;
631 ssl = sslconn->ssl;
632 ssl_var_lookup_ssl_cipher_bits(ssl, &usekeysize, &algkeysize);
634 if (ssl && strEQ(var, "")) {
635 const SSL_CIPHER *cipher = SSL_get_current_cipher(ssl);
636 result = (cipher != NULL ? (char *)SSL_CIPHER_get_name(cipher) : NULL);
638 else if (strcEQ(var, "_EXPORT"))
639 result = (usekeysize < 56 ? "true" : "false");
640 else if (strcEQ(var, "_USEKEYSIZE")) {
641 result = apr_itoa(p, usekeysize);
642 resdup = FALSE;
644 else if (strcEQ(var, "_ALGKEYSIZE")) {
645 result = apr_itoa(p, algkeysize);
646 resdup = FALSE;
649 if (result != NULL && resdup)
650 result = apr_pstrdup(p, result);
651 return result;
654 static void ssl_var_lookup_ssl_cipher_bits(SSL *ssl, int *usekeysize, int *algkeysize)
656 const SSL_CIPHER *cipher;
658 *usekeysize = 0;
659 *algkeysize = 0;
660 if (ssl != NULL)
661 if ((cipher = SSL_get_current_cipher(ssl)) != NULL)
662 *usekeysize = SSL_CIPHER_get_bits(cipher, algkeysize);
663 return;
666 static char *ssl_var_lookup_ssl_version(apr_pool_t *p, char *var)
668 if (strEQ(var, "INTERFACE")) {
669 return apr_pstrdup(p, var_interface);
671 else if (strEQ(var, "LIBRARY_INTERFACE")) {
672 return apr_pstrdup(p, var_library_interface);
674 else if (strEQ(var, "LIBRARY")) {
675 return apr_pstrdup(p, var_library);
677 return NULL;
680 /* Add each RDN in 'xn' to the table 't' where the NID is present in
681 * 'nids', using key prefix 'pfx'. */
682 static void extract_dn(apr_table_t *t, apr_hash_t *nids, const char *pfx,
683 X509_NAME *xn, apr_pool_t *p)
685 STACK_OF(X509_NAME_ENTRY) *ents = X509_NAME_get_entries(xn);
686 X509_NAME_ENTRY *xsne;
687 apr_hash_t *count;
688 int i, nid;
690 /* Hash of (int) NID -> (int *) counter to count each time an RDN
691 * with the given NID has been seen. */
692 count = apr_hash_make(p);
694 /* For each RDN... */
695 for (i = 0; i < sk_X509_NAME_ENTRY_num(ents); i++) {
696 const char *tag;
698 xsne = sk_X509_NAME_ENTRY_value(ents, i);
700 /* Retrieve the nid, and check whether this is one of the nids
701 * which are to be extracted. */
702 nid = OBJ_obj2nid((ASN1_OBJECT *)X509_NAME_ENTRY_get_object(xsne));
704 tag = apr_hash_get(nids, &nid, sizeof nid);
705 if (tag) {
706 unsigned char *data = X509_NAME_ENTRY_get_data_ptr(xsne);
707 const char *key;
708 int *dup;
709 char *value;
711 /* Check whether a variable with this nid was already
712 * been used; if so, use the foo_N=bar syntax. */
713 dup = apr_hash_get(count, &nid, sizeof nid);
714 if (dup) {
715 key = apr_psprintf(p, "%s%s_%d", pfx, tag, ++(*dup));
717 else {
718 /* Otherwise, use the plain foo=bar syntax. */
719 dup = apr_pcalloc(p, sizeof *dup);
720 apr_hash_set(count, &nid, sizeof nid, dup);
721 key = apr_pstrcat(p, pfx, tag, NULL);
724 /* cast needed from 'unsigned char *' to 'char *' */
725 value = apr_pstrmemdup(p, (char *)data,
726 X509_NAME_ENTRY_get_data_len(xsne));
727 #if APR_CHARSET_EBCDIC
728 ap_xlate_proto_from_ascii(value, X509_NAME_ENTRY_get_data_len(xsne));
729 #endif /* APR_CHARSET_EBCDIC */
730 apr_table_setn(t, key, value);
735 void modssl_var_extract_dns(apr_table_t *t, SSL *ssl, apr_pool_t *p)
737 apr_hash_t *nids;
738 unsigned n;
739 X509 *xs;
741 /* Build up a hash table of (int *)NID->(char *)short-name for all
742 * the tags which are to be extracted: */
743 nids = apr_hash_make(p);
744 for (n = 0; ssl_var_lookup_ssl_cert_dn_rec[n].name; n++) {
745 if (ssl_var_lookup_ssl_cert_dn_rec[n].extract) {
746 apr_hash_set(nids, &ssl_var_lookup_ssl_cert_dn_rec[n].nid,
747 sizeof(ssl_var_lookup_ssl_cert_dn_rec[0].nid),
748 ssl_var_lookup_ssl_cert_dn_rec[n].name);
752 /* Extract the server cert DNS -- note that the refcount does NOT
753 * increase: */
754 xs = SSL_get_certificate(ssl);
755 if (xs) {
756 extract_dn(t, nids, "SSL_SERVER_S_DN_", X509_get_subject_name(xs), p);
757 extract_dn(t, nids, "SSL_SERVER_I_DN_", X509_get_issuer_name(xs), p);
760 /* Extract the client cert DNs -- note that the refcount DOES
761 * increase: */
762 xs = SSL_get_peer_certificate(ssl);
763 if (xs) {
764 extract_dn(t, nids, "SSL_CLIENT_S_DN_", X509_get_subject_name(xs), p);
765 extract_dn(t, nids, "SSL_CLIENT_I_DN_", X509_get_issuer_name(xs), p);
766 X509_free(xs);
770 apr_array_header_t *ssl_ext_list(apr_pool_t *p, conn_rec *c, int peer,
771 const char *extension)
773 SSLConnRec *sslconn = myConnConfig(c);
774 SSL *ssl = NULL;
775 apr_array_header_t *array = NULL;
776 X509 *xs = NULL;
777 ASN1_OBJECT *oid = NULL;
778 int count = 0, j;
780 if (!sslconn || !sslconn->ssl || !extension) {
781 return NULL;
783 ssl = sslconn->ssl;
785 /* We accept the "extension" string to be converted as
786 * a long name (nsComment), short name (DN) or
787 * numeric OID (1.2.3.4).
789 oid = OBJ_txt2obj(extension, 0);
790 if (!oid) {
791 ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, c,
792 "Failed to create an object for extension '%s'",
793 extension);
794 ERR_clear_error();
795 return NULL;
798 xs = peer ? SSL_get_peer_certificate(ssl) : SSL_get_certificate(ssl);
799 if (xs == NULL) {
800 return NULL;
803 count = X509_get_ext_count(xs);
804 /* Create an array large enough to accomodate every extension. This is
805 * likely overkill, but safe.
807 array = apr_array_make(p, count, sizeof(char *));
808 for (j = 0; j < count; j++) {
809 X509_EXTENSION *ext = X509_get_ext(xs, j);
811 if (OBJ_cmp(ext->object, oid) == 0) {
812 BIO *bio = BIO_new(BIO_s_mem());
814 /* We want to obtain a string representation of the extensions
815 * value and add it to the array we're building.
816 * X509V3_EXT_print() doesn't know about all the possible
817 * data types, but the value is stored as an ASN1_OCTET_STRING
818 * allowing us a fallback in case of X509V3_EXT_print
819 * not knowing how to handle the data.
821 if (X509V3_EXT_print(bio, ext, 0, 0) == 1 ||
822 ASN1_STRING_print(bio, ext->value) == 1) {
823 BUF_MEM *buf;
824 char **ptr = apr_array_push(array);
825 BIO_get_mem_ptr(bio, &buf);
826 *ptr = apr_pstrmemdup(p, buf->data, buf->length);
827 } else {
828 ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, c,
829 "Found an extension '%s', but failed to "
830 "create a string from it", extension);
832 BIO_vfree(bio);
836 if (array->nelts == 0)
837 array = NULL;
839 if (peer) {
840 /* only SSL_get_peer_certificate raises the refcount */
841 X509_free(xs);
844 ERR_clear_error();
845 return array;
848 static char *ssl_var_lookup_ssl_compress_meth(SSL *ssl)
850 char *result = "NULL";
851 #ifdef OPENSSL_VERSION_NUMBER
852 #if (OPENSSL_VERSION_NUMBER >= 0x00908000)
853 SSL_SESSION *pSession = SSL_get_session(ssl);
855 if (pSession) {
856 switch (pSession->compress_meth) {
857 case 0:
858 /* default "NULL" already set */
859 break;
861 /* Defined by RFC 3749, deflate is coded by "1" */
862 case 1:
863 result = "DEFLATE";
864 break;
866 /* IANA assigned compression number for LZS */
867 case 0x40:
868 result = "LZS";
869 break;
871 default:
872 result = "UNKNOWN";
873 break;
876 #endif
877 #endif
878 return result;
881 /* _________________________________________________________________
883 ** SSL Extension to mod_log_config
884 ** _________________________________________________________________
887 #include "../../modules/loggers/mod_log_config.h"
889 static const char *ssl_var_log_handler_c(request_rec *r, char *a);
890 static const char *ssl_var_log_handler_x(request_rec *r, char *a);
893 * register us for the mod_log_config function registering phase
894 * to establish %{...}c and to be able to expand %{...}x variables.
896 void ssl_var_log_config_register(apr_pool_t *p)
898 static APR_OPTIONAL_FN_TYPE(ap_register_log_handler) *log_pfn_register;
900 log_pfn_register = APR_RETRIEVE_OPTIONAL_FN(ap_register_log_handler);
902 if (log_pfn_register) {
903 log_pfn_register(p, "c", ssl_var_log_handler_c, 0);
904 log_pfn_register(p, "x", ssl_var_log_handler_x, 0);
906 return;
910 * implement the %{..}c log function
911 * (we are the only function)
913 static const char *ssl_var_log_handler_c(request_rec *r, char *a)
915 SSLConnRec *sslconn = myConnConfig(r->connection);
916 char *result;
918 if (sslconn == NULL || sslconn->ssl == NULL)
919 return NULL;
920 result = NULL;
921 if (strEQ(a, "version"))
922 result = ssl_var_lookup(r->pool, r->server, r->connection, r, "SSL_PROTOCOL");
923 else if (strEQ(a, "cipher"))
924 result = ssl_var_lookup(r->pool, r->server, r->connection, r, "SSL_CIPHER");
925 else if (strEQ(a, "subjectdn") || strEQ(a, "clientcert"))
926 result = ssl_var_lookup(r->pool, r->server, r->connection, r, "SSL_CLIENT_S_DN");
927 else if (strEQ(a, "issuerdn") || strEQ(a, "cacert"))
928 result = ssl_var_lookup(r->pool, r->server, r->connection, r, "SSL_CLIENT_I_DN");
929 else if (strEQ(a, "errcode"))
930 result = "-";
931 else if (strEQ(a, "errstr"))
932 result = (char *)sslconn->verify_error;
933 if (result != NULL && result[0] == NUL)
934 result = NULL;
935 return result;
939 * extend the implementation of the %{..}x log function
940 * (there can be more functions)
942 static const char *ssl_var_log_handler_x(request_rec *r, char *a)
944 char *result;
946 result = ssl_var_lookup(r->pool, r->server, r->connection, r, a);
947 if (result != NULL && result[0] == NUL)
948 result = NULL;
949 return result;