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.
18 * _ __ ___ ___ __| | ___ ___| | mod_ssl
19 * | '_ ` _ \ / _ \ / _` | / __/ __| | Apache Interface to OpenSSL
20 * | | | | | | (_) | (_| | \__ \__ \ |
21 * |_| |_| |_|\___/ \__,_|___|___/___/_|
24 * Variable Lookup Facility
26 /* ``Those of you who think they
27 know everything are very annoying
28 to those of us who do.''
30 #include "ssl_private.h"
36 /* _________________________________________________________________
39 ** _________________________________________________________________
42 static char *ssl_var_lookup_ssl(apr_pool_t *p, conn_rec *c, request_rec *r, char *var);
43 static char *ssl_var_lookup_ssl_cert(apr_pool_t *p, request_rec *r, X509 *xs, char *var);
44 static char *ssl_var_lookup_ssl_cert_dn(apr_pool_t *p, X509_NAME *xsname, char *var);
45 static char *ssl_var_lookup_ssl_cert_valid(apr_pool_t *p, ASN1_TIME *tm);
46 static char *ssl_var_lookup_ssl_cert_remain(apr_pool_t *p, ASN1_TIME *tm);
47 static char *ssl_var_lookup_ssl_cert_serial(apr_pool_t *p, X509 *xs);
48 static char *ssl_var_lookup_ssl_cert_chain(apr_pool_t *p, STACK_OF(X509) *sk, char *var);
49 static char *ssl_var_lookup_ssl_cert_PEM(apr_pool_t *p, X509 *xs);
50 static char *ssl_var_lookup_ssl_cert_verify(apr_pool_t *p, conn_rec *c);
51 static char *ssl_var_lookup_ssl_cipher(apr_pool_t *p, conn_rec *c, char *var);
52 static void ssl_var_lookup_ssl_cipher_bits(SSL *ssl, int *usekeysize, int *algkeysize);
53 static char *ssl_var_lookup_ssl_version(apr_pool_t *p, char *var);
54 static char *ssl_var_lookup_ssl_compress_meth(SSL *ssl);
56 static int ssl_is_https(conn_rec *c)
58 SSLConnRec *sslconn = myConnConfig(c);
59 return sslconn && sslconn->ssl;
62 static const char var_interface[] = "mod_ssl/" AP_SERVER_BASEREVISION;
63 static char var_library_interface[] = SSL_LIBRARY_TEXT;
64 static char *var_library = NULL;
66 static apr_array_header_t *expr_peer_ext_list_fn(ap_expr_eval_ctx_t *ctx,
70 return ssl_ext_list(ctx->p, ctx->c, 1, arg);
73 static const char *expr_var_fn(ap_expr_eval_ctx_t *ctx, const void *data)
75 char *var = (char *)data;
76 return ssl_var_lookup_ssl(ctx->p, ctx->c, ctx->r, var);
79 static int ssl_expr_lookup(ap_expr_lookup_parms *parms)
81 switch (parms->type) {
82 case AP_EXPR_FUNC_VAR:
83 /* for now, we just handle everything that starts with SSL_, but
84 * register our hook as APR_HOOK_LAST
85 * XXX: This can be optimized
87 if (strcEQn(parms->name, "SSL_", 4)) {
88 *parms->func = expr_var_fn;
89 *parms->data = parms->name + 4;
93 case AP_EXPR_FUNC_LIST:
94 if (strcEQ(parms->name, "PeerExtList")) {
95 *parms->func = expr_peer_ext_list_fn;
96 *parms->data = "PeerExtList";
105 void ssl_var_register(apr_pool_t *p)
109 APR_REGISTER_OPTIONAL_FN(ssl_is_https);
110 APR_REGISTER_OPTIONAL_FN(ssl_var_lookup);
111 APR_REGISTER_OPTIONAL_FN(ssl_ext_list);
113 /* Perform once-per-process library version determination: */
114 var_library = apr_pstrdup(p, SSL_LIBRARY_DYNTEXT);
116 if ((cp = strchr(var_library, ' ')) != NULL) {
118 if ((cp2 = strchr(cp, ' ')) != NULL)
122 if ((cp = strchr(var_library_interface, ' ')) != NULL) {
124 if ((cp2 = strchr(cp, ' ')) != NULL)
128 ap_hook_expr_lookup(ssl_expr_lookup, NULL, NULL, APR_HOOK_MIDDLE);
131 /* This function must remain safe to use for a non-SSL connection. */
132 char *ssl_var_lookup(apr_pool_t *p, server_rec *s, conn_rec *c, request_rec *r, char *var)
134 SSLModConfigRec *mc = myModConfig(s);
143 * When no pool is given try to find one
155 * Request dependent stuff
161 if (strcEQ(var, "HTTP_USER_AGENT"))
162 result = apr_table_get(r->headers_in, "User-Agent");
163 else if (strcEQ(var, "HTTP_REFERER"))
164 result = apr_table_get(r->headers_in, "Referer");
165 else if (strcEQ(var, "HTTP_COOKIE"))
166 result = apr_table_get(r->headers_in, "Cookie");
167 else if (strcEQ(var, "HTTP_FORWARDED"))
168 result = apr_table_get(r->headers_in, "Forwarded");
169 else if (strcEQ(var, "HTTP_HOST"))
170 result = apr_table_get(r->headers_in, "Host");
171 else if (strcEQ(var, "HTTP_PROXY_CONNECTION"))
172 result = apr_table_get(r->headers_in, "Proxy-Connection");
173 else if (strcEQ(var, "HTTP_ACCEPT"))
174 result = apr_table_get(r->headers_in, "Accept");
175 else if (strlen(var) > 5 && strcEQn(var, "HTTP:", 5))
176 /* all other headers from which we are still not know about */
177 result = apr_table_get(r->headers_in, var+5);
182 if (strcEQ(var, "REQUEST_METHOD"))
184 else if (strcEQ(var, "REQUEST_SCHEME"))
185 result = ap_http_scheme(r);
186 else if (strcEQ(var, "REQUEST_URI"))
188 else if (strcEQ(var, "REQUEST_FILENAME"))
189 result = r->filename;
190 else if (strcEQ(var, "REMOTE_ADDR"))
191 result = r->useragent_ip;
192 else if (strcEQ(var, "REMOTE_HOST"))
193 result = ap_get_remote_host(r->connection, r->per_dir_config,
195 else if (strcEQ(var, "REMOTE_IDENT"))
196 result = ap_get_remote_logname(r);
197 else if (strcEQ(var, "REMOTE_USER"))
203 if (strcEQn(var, "SSL", 3)) break; /* shortcut common case */
205 if (strcEQ(var, "SERVER_ADMIN"))
206 result = r->server->server_admin;
207 else if (strcEQ(var, "SERVER_NAME"))
208 result = ap_get_server_name_for_url(r);
209 else if (strcEQ(var, "SERVER_PORT"))
210 result = apr_psprintf(p, "%u", ap_get_server_port(r));
211 else if (strcEQ(var, "SERVER_PROTOCOL"))
212 result = r->protocol;
213 else if (strcEQ(var, "SCRIPT_FILENAME"))
214 result = r->filename;
218 if (strcEQ(var, "PATH_INFO"))
219 result = r->path_info;
220 else if (strcEQ(var, "QUERY_STRING"))
222 else if (strcEQ(var, "IS_SUBREQ"))
223 result = (r->main != NULL ? "true" : "false");
224 else if (strcEQ(var, "DOCUMENT_ROOT"))
225 result = ap_document_root(r);
226 else if (strcEQ(var, "AUTH_TYPE"))
227 result = r->ap_auth_type;
228 else if (strcEQ(var, "THE_REQUEST"))
229 result = r->the_request;
230 else if (strlen(var) > 4 && strcEQn(var, "ENV:", 4)) {
231 result = apr_table_get(r->notes, var+4);
233 result = apr_table_get(r->subprocess_env, var+4);
242 if (result == NULL && c != NULL) {
243 SSLConnRec *sslconn = myConnConfig(c);
244 if (strlen(var) > 4 && strcEQn(var, "SSL_", 4)
245 && sslconn && sslconn->ssl)
246 result = ssl_var_lookup_ssl(p, c, r, var+4);
247 else if (strcEQ(var, "HTTPS")) {
248 if (sslconn && sslconn->ssl)
256 * Totally independent stuff
258 if (result == NULL) {
259 if (strlen(var) > 12 && strcEQn(var, "SSL_VERSION_", 12))
260 result = ssl_var_lookup_ssl_version(p, var+12);
261 else if (strcEQ(var, "SERVER_SOFTWARE"))
262 result = ap_get_server_banner();
263 else if (strcEQ(var, "API_VERSION")) {
264 result = apr_itoa(p, MODULE_MAGIC_NUMBER);
267 else if (strcEQ(var, "TIME_YEAR")) {
268 apr_time_exp_lt(&tm, apr_time_now());
269 result = apr_psprintf(p, "%02d%02d",
270 (tm.tm_year / 100) + 19, tm.tm_year % 100);
273 #define MKTIMESTR(format, tmfield) \
274 apr_time_exp_lt(&tm, apr_time_now()); \
275 result = apr_psprintf(p, format, tm.tmfield); \
277 else if (strcEQ(var, "TIME_MON")) {
278 MKTIMESTR("%02d", tm_mon+1)
280 else if (strcEQ(var, "TIME_DAY")) {
281 MKTIMESTR("%02d", tm_mday)
283 else if (strcEQ(var, "TIME_HOUR")) {
284 MKTIMESTR("%02d", tm_hour)
286 else if (strcEQ(var, "TIME_MIN")) {
287 MKTIMESTR("%02d", tm_min)
289 else if (strcEQ(var, "TIME_SEC")) {
290 MKTIMESTR("%02d", tm_sec)
292 else if (strcEQ(var, "TIME_WDAY")) {
293 MKTIMESTR("%d", tm_wday)
295 else if (strcEQ(var, "TIME")) {
296 apr_time_exp_lt(&tm, apr_time_now());
297 result = apr_psprintf(p,
298 "%02d%02d%02d%02d%02d%02d%02d", (tm.tm_year / 100) + 19,
299 (tm.tm_year % 100), tm.tm_mon+1, tm.tm_mday,
300 tm.tm_hour, tm.tm_min, tm.tm_sec);
303 /* all other env-variables from the parent Apache process */
304 else if (strlen(var) > 4 && strcEQn(var, "ENV:", 4)) {
305 result = getenv(var+4);
309 if (result != NULL && resdup)
310 result = apr_pstrdup(p, result);
313 return (char *)result;
316 static char *ssl_var_lookup_ssl(apr_pool_t *p, conn_rec *c, request_rec *r,
319 SSLConnRec *sslconn = myConnConfig(c);
328 if (strlen(var) > 8 && strcEQn(var, "VERSION_", 8)) {
329 result = ssl_var_lookup_ssl_version(p, var+8);
331 else if (ssl != NULL && strcEQ(var, "PROTOCOL")) {
332 result = (char *)SSL_get_version(ssl);
334 else if (ssl != NULL && strcEQ(var, "SESSION_ID")) {
335 char buf[SSL_SESSION_ID_STRING_LEN];
336 SSL_SESSION *pSession = SSL_get_session(ssl);
341 #ifdef OPENSSL_NO_SSL_INTERN
342 id = (unsigned char *)SSL_SESSION_get_id(pSession, &idlen);
344 id = pSession->session_id;
345 idlen = pSession->session_id_length;
348 result = apr_pstrdup(p, SSL_SESSION_id2sz(id, idlen,
352 else if(ssl != NULL && strcEQ(var, "SESSION_RESUMED")) {
353 if (SSL_session_reused(ssl) == 1)
358 else if (ssl != NULL && strlen(var) >= 6 && strcEQn(var, "CIPHER", 6)) {
359 result = ssl_var_lookup_ssl_cipher(p, c, var+6);
361 else if (ssl != NULL && strlen(var) > 18 && strcEQn(var, "CLIENT_CERT_CHAIN_", 18)) {
362 sk = SSL_get_peer_cert_chain(ssl);
363 result = ssl_var_lookup_ssl_cert_chain(p, sk, var+18);
365 else if (ssl != NULL && strcEQ(var, "CLIENT_VERIFY")) {
366 result = ssl_var_lookup_ssl_cert_verify(p, c);
368 else if (ssl != NULL && strlen(var) > 7 && strcEQn(var, "CLIENT_", 7)) {
369 if ((xs = SSL_get_peer_certificate(ssl)) != NULL) {
370 result = ssl_var_lookup_ssl_cert(p, r, xs, var+7);
374 else if (ssl != NULL && strlen(var) > 7 && strcEQn(var, "SERVER_", 7)) {
375 if ((xs = SSL_get_certificate(ssl)) != NULL) {
376 result = ssl_var_lookup_ssl_cert(p, r, xs, var+7);
377 /* SSL_get_certificate is different from SSL_get_peer_certificate.
378 * No need to X509_free(xs).
382 else if (ssl != NULL && strcEQ(var, "COMPRESS_METHOD")) {
383 result = ssl_var_lookup_ssl_compress_meth(ssl);
386 else if (ssl != NULL && strcEQ(var, "TLS_SNI")) {
387 result = apr_pstrdup(p, SSL_get_servername(ssl,
388 TLSEXT_NAMETYPE_host_name));
391 else if (ssl != NULL && strcEQ(var, "SECURE_RENEG")) {
393 #ifdef SSL_get_secure_renegotiation_support
394 flag = SSL_get_secure_renegotiation_support(ssl);
396 result = apr_pstrdup(p, flag ? "true" : "false");
399 else if (ssl != NULL && strcEQ(var, "SRP_USER")) {
400 if ((result = SSL_get_srp_username(ssl)) != NULL) {
401 result = apr_pstrdup(p, result);
404 else if (ssl != NULL && strcEQ(var, "SRP_USERINFO")) {
405 if ((result = SSL_get_srp_userinfo(ssl)) != NULL) {
406 result = apr_pstrdup(p, result);
414 static char *ssl_var_lookup_ssl_cert_dn_oneline(apr_pool_t *p, request_rec *r,
419 int legacy_format = 0;
422 legacy_format = dc->nOptions & SSL_OPT_LEGACYDNFORMAT;
425 char *cp = X509_NAME_oneline(xsname, NULL, 0);
426 result = apr_pstrdup(p, cp);
432 unsigned long flags = XN_FLAG_RFC2253 & ~ASN1_STRFLGS_ESC_MSB;
433 if ((bio = BIO_new(BIO_s_mem())) == NULL)
435 X509_NAME_print_ex(bio, xsname, 0, flags);
436 n = BIO_pending(bio);
438 result = apr_palloc(p, n+1);
439 n = BIO_read(bio, result, n);
447 static char *ssl_var_lookup_ssl_cert(apr_pool_t *p, request_rec *r, X509 *xs,
458 if (strcEQ(var, "M_VERSION")) {
459 result = apr_psprintf(p, "%lu", X509_get_version(xs)+1);
462 else if (strcEQ(var, "M_SERIAL")) {
463 result = ssl_var_lookup_ssl_cert_serial(p, xs);
465 else if (strcEQ(var, "V_START")) {
466 result = ssl_var_lookup_ssl_cert_valid(p, X509_get_notBefore(xs));
468 else if (strcEQ(var, "V_END")) {
469 result = ssl_var_lookup_ssl_cert_valid(p, X509_get_notAfter(xs));
471 else if (strcEQ(var, "V_REMAIN")) {
472 result = ssl_var_lookup_ssl_cert_remain(p, X509_get_notAfter(xs));
475 else if (*var && strcEQ(var+1, "_DN")) {
477 xsname = X509_get_subject_name(xs);
478 else if (*var == 'I')
479 xsname = X509_get_issuer_name(xs);
482 result = ssl_var_lookup_ssl_cert_dn_oneline(p, r, xsname);
485 else if (strlen(var) > 5 && strcEQn(var+1, "_DN_", 4)) {
487 xsname = X509_get_subject_name(xs);
488 else if (*var == 'I')
489 xsname = X509_get_issuer_name(xs);
492 result = ssl_var_lookup_ssl_cert_dn(p, xsname, var+5);
495 else if (strcEQ(var, "A_SIG")) {
496 nid = OBJ_obj2nid((ASN1_OBJECT *)(xs->cert_info->signature->algorithm));
497 result = apr_pstrdup(p,
498 (nid == NID_undef) ? "UNKNOWN" : OBJ_nid2ln(nid));
501 else if (strcEQ(var, "A_KEY")) {
502 nid = OBJ_obj2nid((ASN1_OBJECT *)(xs->cert_info->key->algor->algorithm));
503 result = apr_pstrdup(p,
504 (nid == NID_undef) ? "UNKNOWN" : OBJ_nid2ln(nid));
507 else if (strcEQ(var, "CERT")) {
508 result = ssl_var_lookup_ssl_cert_PEM(p, xs);
512 result = apr_pstrdup(p, result);
516 /* In this table, .extract is non-zero if RDNs using the NID should be
517 * extracted to for the SSL_{CLIENT,SERVER}_{I,S}_DN_* environment
519 static const struct {
523 } ssl_var_lookup_ssl_cert_dn_rec[] = {
524 { "C", NID_countryName, 1 },
525 { "ST", NID_stateOrProvinceName, 1 }, /* officially (RFC2156) */
526 { "SP", NID_stateOrProvinceName, 0 }, /* compatibility (SSLeay) */
527 { "L", NID_localityName, 1 },
528 { "O", NID_organizationName, 1 },
529 { "OU", NID_organizationalUnitName, 1 },
530 { "CN", NID_commonName, 1 },
531 { "T", NID_title, 1 },
532 { "I", NID_initials, 1 },
533 { "G", NID_givenName, 1 },
534 { "S", NID_surname, 1 },
535 { "D", NID_description, 1 },
537 { "UID", NID_userId, 1 },
539 { "Email", NID_pkcs9_emailAddress, 1 },
543 static char *ssl_var_lookup_ssl_cert_dn(apr_pool_t *p, X509_NAME *xsname, char *var)
546 X509_NAME_ENTRY *xsne;
547 int i, j, n, idx = 0;
550 /* if an _N suffix is used, find the Nth attribute of given name */
551 ptr = strchr(var, '_');
552 if (ptr != NULL && strspn(ptr + 1, "0123456789") == strlen(ptr + 1)) {
556 varlen = strlen(var);
561 for (i = 0; ssl_var_lookup_ssl_cert_dn_rec[i].name != NULL; i++) {
562 if (strEQn(var, ssl_var_lookup_ssl_cert_dn_rec[i].name, varlen)
563 && strlen(ssl_var_lookup_ssl_cert_dn_rec[i].name) == varlen) {
564 for (j = 0; j < sk_X509_NAME_ENTRY_num((STACK_OF(X509_NAME_ENTRY) *)
567 xsne = sk_X509_NAME_ENTRY_value((STACK_OF(X509_NAME_ENTRY) *)
570 n =OBJ_obj2nid((ASN1_OBJECT *)X509_NAME_ENTRY_get_object(xsne));
572 if (n == ssl_var_lookup_ssl_cert_dn_rec[i].nid && idx-- == 0) {
573 result = SSL_X509_NAME_ENTRY_to_string(p, xsne);
583 static char *ssl_var_lookup_ssl_cert_valid(apr_pool_t *p, ASN1_TIME *tm)
589 if ((bio = BIO_new(BIO_s_mem())) == NULL)
591 ASN1_TIME_print(bio, tm);
592 n = BIO_pending(bio);
593 result = apr_pcalloc(p, n+1);
594 n = BIO_read(bio, result, n);
600 #define DIGIT2NUM(x) (((x)[0] - '0') * 10 + (x)[1] - '0')
602 /* Return a string giving the number of days remaining until 'tm', or
603 * "0" if this can't be determined. */
604 static char *ssl_var_lookup_ssl_cert_remain(apr_pool_t *p, ASN1_TIME *tm)
606 apr_time_t then, now = apr_time_now();
607 apr_time_exp_t exp = {0};
611 /* Fail if the time isn't a valid ASN.1 TIME; RFC3280 mandates
612 * that the seconds digits are present even though ASN.1
614 if ((tm->type == V_ASN1_UTCTIME && tm->length < 11) ||
615 (tm->type == V_ASN1_GENERALIZEDTIME && tm->length < 13) ||
616 !ASN1_TIME_check(tm)) {
617 return apr_pstrdup(p, "0");
620 if (tm->type == V_ASN1_UTCTIME) {
621 exp.tm_year = DIGIT2NUM(tm->data);
622 if (exp.tm_year <= 50) exp.tm_year += 100;
625 exp.tm_year = DIGIT2NUM(tm->data) * 100 + DIGIT2NUM(tm->data + 2) - 1900;
629 exp.tm_mon = DIGIT2NUM(dp) - 1;
630 exp.tm_mday = DIGIT2NUM(dp + 2) + 1;
631 exp.tm_hour = DIGIT2NUM(dp + 4);
632 exp.tm_min = DIGIT2NUM(dp + 6);
633 exp.tm_sec = DIGIT2NUM(dp + 8);
635 if (apr_time_exp_gmt_get(&then, &exp) != APR_SUCCESS) {
636 return apr_pstrdup(p, "0");
639 diff = (long)((apr_time_sec(then) - apr_time_sec(now)) / (60*60*24));
641 return diff > 0 ? apr_ltoa(p, diff) : apr_pstrdup(p, "0");
644 static char *ssl_var_lookup_ssl_cert_serial(apr_pool_t *p, X509 *xs)
650 if ((bio = BIO_new(BIO_s_mem())) == NULL)
652 i2a_ASN1_INTEGER(bio, X509_get_serialNumber(xs));
653 n = BIO_pending(bio);
654 result = apr_pcalloc(p, n+1);
655 n = BIO_read(bio, result, n);
661 static char *ssl_var_lookup_ssl_cert_chain(apr_pool_t *p, STACK_OF(X509) *sk, char *var)
669 if (strspn(var, "0123456789") == strlen(var)) {
671 if (n < sk_X509_num(sk)) {
672 xs = sk_X509_value(sk, n);
673 result = ssl_var_lookup_ssl_cert_PEM(p, xs);
680 static char *ssl_var_lookup_ssl_cert_PEM(apr_pool_t *p, X509 *xs)
686 if ((bio = BIO_new(BIO_s_mem())) == NULL)
688 PEM_write_bio_X509(bio, xs);
689 n = BIO_pending(bio);
690 result = apr_pcalloc(p, n+1);
691 n = BIO_read(bio, result, n);
697 static char *ssl_var_lookup_ssl_cert_verify(apr_pool_t *p, conn_rec *c)
699 SSLConnRec *sslconn = myConnConfig(c);
709 verr = sslconn->verify_error;
710 vinfo = sslconn->verify_info;
711 vrc = SSL_get_verify_result(ssl);
712 xs = SSL_get_peer_certificate(ssl);
714 if (vrc == X509_V_OK && verr == NULL && xs == NULL)
715 /* no client verification done at all */
717 else if (vrc == X509_V_OK && verr == NULL && vinfo == NULL && xs != NULL)
718 /* client verification done successful */
720 else if (vrc == X509_V_OK && vinfo != NULL && strEQ(vinfo, "GENEROUS"))
721 /* client verification done in generous way */
724 /* client verification failed */
725 result = apr_psprintf(p, "FAILED:%s",
726 verr ? verr : X509_verify_cert_error_string(vrc));
733 static char *ssl_var_lookup_ssl_cipher(apr_pool_t *p, conn_rec *c, char *var)
735 SSLConnRec *sslconn = myConnConfig(c);
738 int usekeysize, algkeysize;
745 ssl_var_lookup_ssl_cipher_bits(ssl, &usekeysize, &algkeysize);
747 if (ssl && strEQ(var, "")) {
748 MODSSL_SSL_CIPHER_CONST SSL_CIPHER *cipher = SSL_get_current_cipher(ssl);
749 result = (cipher != NULL ? (char *)SSL_CIPHER_get_name(cipher) : NULL);
751 else if (strcEQ(var, "_EXPORT"))
752 result = (usekeysize < 56 ? "true" : "false");
753 else if (strcEQ(var, "_USEKEYSIZE")) {
754 result = apr_itoa(p, usekeysize);
757 else if (strcEQ(var, "_ALGKEYSIZE")) {
758 result = apr_itoa(p, algkeysize);
762 if (result != NULL && resdup)
763 result = apr_pstrdup(p, result);
767 static void ssl_var_lookup_ssl_cipher_bits(SSL *ssl, int *usekeysize, int *algkeysize)
769 MODSSL_SSL_CIPHER_CONST SSL_CIPHER *cipher;
774 if ((cipher = SSL_get_current_cipher(ssl)) != NULL)
775 *usekeysize = SSL_CIPHER_get_bits(cipher, algkeysize);
779 static char *ssl_var_lookup_ssl_version(apr_pool_t *p, char *var)
781 if (strEQ(var, "INTERFACE")) {
782 return apr_pstrdup(p, var_interface);
784 else if (strEQ(var, "LIBRARY_INTERFACE")) {
785 return apr_pstrdup(p, var_library_interface);
787 else if (strEQ(var, "LIBRARY")) {
788 return apr_pstrdup(p, var_library);
793 /* Add each RDN in 'xn' to the table 't' where the NID is present in
794 * 'nids', using key prefix 'pfx'. */
795 static void extract_dn(apr_table_t *t, apr_hash_t *nids, const char *pfx,
796 X509_NAME *xn, apr_pool_t *p)
798 STACK_OF(X509_NAME_ENTRY) *ents = xn->entries;
799 X509_NAME_ENTRY *xsne;
803 /* Hash of (int) NID -> (int *) counter to count each time an RDN
804 * with the given NID has been seen. */
805 count = apr_hash_make(p);
807 /* For each RDN... */
808 for (i = 0; i < sk_X509_NAME_ENTRY_num(ents); i++) {
811 xsne = sk_X509_NAME_ENTRY_value(ents, i);
813 /* Retrieve the nid, and check whether this is one of the nids
814 * which are to be extracted. */
815 nid = OBJ_obj2nid((ASN1_OBJECT *)X509_NAME_ENTRY_get_object(xsne));
817 tag = apr_hash_get(nids, &nid, sizeof nid);
823 /* Check whether a variable with this nid was already
824 * been used; if so, use the foo_N=bar syntax. */
825 dup = apr_hash_get(count, &nid, sizeof nid);
827 key = apr_psprintf(p, "%s%s_%d", pfx, tag, ++(*dup));
830 /* Otherwise, use the plain foo=bar syntax. */
831 dup = apr_pcalloc(p, sizeof *dup);
832 apr_hash_set(count, &nid, sizeof nid, dup);
833 key = apr_pstrcat(p, pfx, tag, NULL);
835 value = SSL_X509_NAME_ENTRY_to_string(p, xsne);
836 apr_table_setn(t, key, value);
841 void modssl_var_extract_dns(apr_table_t *t, SSL *ssl, apr_pool_t *p)
847 /* Build up a hash table of (int *)NID->(char *)short-name for all
848 * the tags which are to be extracted: */
849 nids = apr_hash_make(p);
850 for (n = 0; ssl_var_lookup_ssl_cert_dn_rec[n].name; n++) {
851 if (ssl_var_lookup_ssl_cert_dn_rec[n].extract) {
852 apr_hash_set(nids, &ssl_var_lookup_ssl_cert_dn_rec[n].nid,
853 sizeof(ssl_var_lookup_ssl_cert_dn_rec[0].nid),
854 ssl_var_lookup_ssl_cert_dn_rec[n].name);
858 /* Extract the server cert DNS -- note that the refcount does NOT
860 xs = SSL_get_certificate(ssl);
862 extract_dn(t, nids, "SSL_SERVER_S_DN_", X509_get_subject_name(xs), p);
863 extract_dn(t, nids, "SSL_SERVER_I_DN_", X509_get_issuer_name(xs), p);
866 /* Extract the client cert DNs -- note that the refcount DOES
868 xs = SSL_get_peer_certificate(ssl);
870 extract_dn(t, nids, "SSL_CLIENT_S_DN_", X509_get_subject_name(xs), p);
871 extract_dn(t, nids, "SSL_CLIENT_I_DN_", X509_get_issuer_name(xs), p);
876 /* For an extension type which OpenSSL does not recognize, attempt to
877 * parse the extension type as a primitive string. This will fail for
878 * any structured extension type per the docs. Returns non-zero on
879 * success and writes the string to the given bio. */
880 static int dump_extn_value(BIO *bio, ASN1_OCTET_STRING *str)
882 const unsigned char *pp = str->data;
883 ASN1_STRING *ret = ASN1_STRING_new();
886 /* This allows UTF8String, IA5String, VisibleString, or BMPString;
887 * conversion to UTF-8 is forced. */
888 if (d2i_DISPLAYTEXT(&ret, &pp, str->length)) {
889 ASN1_STRING_print_ex(bio, ret, ASN1_STRFLGS_UTF8_CONVERT);
893 ASN1_STRING_free(ret);
897 apr_array_header_t *ssl_ext_list(apr_pool_t *p, conn_rec *c, int peer,
898 const char *extension)
900 SSLConnRec *sslconn = myConnConfig(c);
902 apr_array_header_t *array = NULL;
904 ASN1_OBJECT *oid = NULL;
907 if (!sslconn || !sslconn->ssl || !extension) {
912 /* We accept the "extension" string to be converted as
913 * a long name (nsComment), short name (DN) or
914 * numeric OID (1.2.3.4).
916 oid = OBJ_txt2obj(extension, 0);
918 ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, c, APLOGNO(01970)
919 "could not parse OID '%s'", extension);
924 xs = peer ? SSL_get_peer_certificate(ssl) : SSL_get_certificate(ssl);
929 count = X509_get_ext_count(xs);
930 /* Create an array large enough to accomodate every extension. This is
931 * likely overkill, but safe.
933 array = apr_array_make(p, count, sizeof(char *));
934 for (j = 0; j < count; j++) {
935 X509_EXTENSION *ext = X509_get_ext(xs, j);
937 if (OBJ_cmp(ext->object, oid) == 0) {
938 BIO *bio = BIO_new(BIO_s_mem());
940 /* We want to obtain a string representation of the extensions
941 * value and add it to the array we're building.
942 * X509V3_EXT_print() doesn't know about all the possible
943 * data types, but the value is stored as an ASN1_OCTET_STRING
944 * allowing us a fallback in case of X509V3_EXT_print
945 * not knowing how to handle the data.
947 if (X509V3_EXT_print(bio, ext, 0, 0) == 1 ||
948 dump_extn_value(bio, X509_EXTENSION_get_data(ext)) == 1) {
950 char **ptr = apr_array_push(array);
951 BIO_get_mem_ptr(bio, &buf);
952 *ptr = apr_pstrmemdup(p, buf->data, buf->length);
954 ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, c, APLOGNO(01971)
955 "Found an extension '%s', but failed to "
956 "create a string from it", extension);
962 if (array->nelts == 0)
966 /* only SSL_get_peer_certificate raises the refcount */
970 ASN1_OBJECT_free(oid);
975 static char *ssl_var_lookup_ssl_compress_meth(SSL *ssl)
977 char *result = "NULL";
978 #ifndef OPENSSL_NO_COMP
979 SSL_SESSION *pSession = SSL_get_session(ssl);
982 #ifdef OPENSSL_NO_SSL_INTERN
983 switch (SSL_SESSION_get_compress_id(pSession)) {
985 switch (pSession->compress_meth) {
988 /* default "NULL" already set */
991 /* Defined by RFC 3749, deflate is coded by "1" */
996 /* IANA assigned compression number for LZS */
1010 /* _________________________________________________________________
1012 ** SSL Extension to mod_log_config
1013 ** _________________________________________________________________
1016 #include "../../modules/loggers/mod_log_config.h"
1018 static const char *ssl_var_log_handler_c(request_rec *r, char *a);
1019 static const char *ssl_var_log_handler_x(request_rec *r, char *a);
1022 * register us for the mod_log_config function registering phase
1023 * to establish %{...}c and to be able to expand %{...}x variables.
1025 void ssl_var_log_config_register(apr_pool_t *p)
1027 static APR_OPTIONAL_FN_TYPE(ap_register_log_handler) *log_pfn_register;
1029 log_pfn_register = APR_RETRIEVE_OPTIONAL_FN(ap_register_log_handler);
1031 if (log_pfn_register) {
1032 log_pfn_register(p, "c", ssl_var_log_handler_c, 0);
1033 log_pfn_register(p, "x", ssl_var_log_handler_x, 0);
1039 * implement the %{..}c log function
1040 * (we are the only function)
1042 static const char *ssl_var_log_handler_c(request_rec *r, char *a)
1044 SSLConnRec *sslconn = myConnConfig(r->connection);
1047 if (sslconn == NULL || sslconn->ssl == NULL)
1050 if (strEQ(a, "version"))
1051 result = ssl_var_lookup(r->pool, r->server, r->connection, r, "SSL_PROTOCOL");
1052 else if (strEQ(a, "cipher"))
1053 result = ssl_var_lookup(r->pool, r->server, r->connection, r, "SSL_CIPHER");
1054 else if (strEQ(a, "subjectdn") || strEQ(a, "clientcert"))
1055 result = ssl_var_lookup(r->pool, r->server, r->connection, r, "SSL_CLIENT_S_DN");
1056 else if (strEQ(a, "issuerdn") || strEQ(a, "cacert"))
1057 result = ssl_var_lookup(r->pool, r->server, r->connection, r, "SSL_CLIENT_I_DN");
1058 else if (strEQ(a, "errcode"))
1060 else if (strEQ(a, "errstr"))
1061 result = (char *)sslconn->verify_error;
1062 if (result != NULL && result[0] == NUL)
1068 * extend the implementation of the %{..}x log function
1069 * (there can be more functions)
1071 static const char *ssl_var_log_handler_x(request_rec *r, char *a)
1075 result = ssl_var_lookup(r->pool, r->server, r->connection, r, a);
1076 if (result != NULL && result[0] == NUL)