]> granicus.if.org Git - apache/blob - modules/ssl/ssl_engine_vars.c
Further clarify the naming of the entity that originates the request by
[apache] / modules / ssl / ssl_engine_vars.c
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
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  */
16
17 /*                      _             _
18  *  _ __ ___   ___   __| |    ___ ___| |  mod_ssl
19  * | '_ ` _ \ / _ \ / _` |   / __/ __| |  Apache Interface to OpenSSL
20  * | | | | | | (_) | (_| |   \__ \__ \ |
21  * |_| |_| |_|\___/ \__,_|___|___/___/_|
22  *                      |_____|
23  *  ssl_engine_vars.c
24  *  Variable Lookup Facility
25  */
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"
32 #include "ap_expr.h"
33
34 #include "apr_time.h"
35
36 /*  _________________________________________________________________
37 **
38 **  Variable Lookup
39 **  _________________________________________________________________
40 */
41
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);
55
56 static int ssl_is_https(conn_rec *c)
57 {
58     SSLConnRec *sslconn = myConnConfig(c);
59     return sslconn && sslconn->ssl;
60 }
61
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;
65
66 static apr_array_header_t *expr_peer_ext_list_fn(ap_expr_eval_ctx_t *ctx,
67                                                  const void *dummy,
68                                                  const char *arg)
69 {
70     return ssl_ext_list(ctx->p, ctx->c, 1, arg);
71 }
72
73 static const char *expr_var_fn(ap_expr_eval_ctx_t *ctx, const void *data)
74 {
75     char *var = (char *)data;
76     return ssl_var_lookup_ssl(ctx->p, ctx->c, ctx->r, var);
77 }
78
79 static int ssl_expr_lookup(ap_expr_lookup_parms *parms)
80 {
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
86          */
87         if (strcEQn(parms->name, "SSL_", 4)) {
88             *parms->func = expr_var_fn;
89             *parms->data = parms->name + 4;
90             return OK;
91         }
92         break;
93     case AP_EXPR_FUNC_LIST:
94         if (strcEQ(parms->name, "PeerExtList")) {
95             *parms->func = expr_peer_ext_list_fn;
96             *parms->data = "PeerExtList";
97             return OK;
98         }
99         break;
100     }
101     return DECLINED;
102 }
103
104
105 void ssl_var_register(apr_pool_t *p)
106 {
107     char *cp, *cp2;
108
109     APR_REGISTER_OPTIONAL_FN(ssl_is_https);
110     APR_REGISTER_OPTIONAL_FN(ssl_var_lookup);
111     APR_REGISTER_OPTIONAL_FN(ssl_ext_list);
112
113     /* Perform once-per-process library version determination: */
114     var_library = apr_pstrdup(p, SSL_LIBRARY_DYNTEXT);
115
116     if ((cp = strchr(var_library, ' ')) != NULL) {
117         *cp = '/';
118         if ((cp2 = strchr(cp, ' ')) != NULL)
119             *cp2 = NUL;
120     }
121
122     if ((cp = strchr(var_library_interface, ' ')) != NULL) {
123         *cp = '/';
124         if ((cp2 = strchr(cp, ' ')) != NULL)
125             *cp2 = NUL;
126     }
127
128     ap_hook_expr_lookup(ssl_expr_lookup, NULL, NULL, APR_HOOK_MIDDLE);
129 }
130
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)
133 {
134     SSLModConfigRec *mc = myModConfig(s);
135     const char *result;
136     BOOL resdup;
137     apr_time_exp_t tm;
138
139     result = NULL;
140     resdup = TRUE;
141
142     /*
143      * When no pool is given try to find one
144      */
145     if (p == NULL) {
146         if (r != NULL)
147             p = r->pool;
148         else if (c != NULL)
149             p = c->pool;
150         else
151             p = mc->pPool;
152     }
153
154     /*
155      * Request dependent stuff
156      */
157     if (r != NULL) {
158         switch (var[0]) {
159         case 'H':
160         case 'h':
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);
178             break;
179
180         case 'R':
181         case 'r':
182             if (strcEQ(var, "REQUEST_METHOD"))
183                 result = r->method;
184             else if (strcEQ(var, "REQUEST_SCHEME"))
185                 result = ap_http_scheme(r);
186             else if (strcEQ(var, "REQUEST_URI"))
187                 result = r->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,
194                                             REMOTE_NAME, NULL);
195             else if (strcEQ(var, "REMOTE_IDENT"))
196                 result = ap_get_remote_logname(r);
197             else if (strcEQ(var, "REMOTE_USER"))
198                 result = r->user;
199             break;
200
201         case 'S':
202         case 's':
203             if (strcEQn(var, "SSL", 3)) break; /* shortcut common case */
204
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;
215             break;
216
217         default:
218             if (strcEQ(var, "PATH_INFO"))
219                 result = r->path_info;
220             else if (strcEQ(var, "QUERY_STRING"))
221                 result = r->args;
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);
232                 if (result == NULL)
233                     result = apr_table_get(r->subprocess_env, var+4);
234             }
235             break;
236         }
237     }
238
239     /*
240      * Connection stuff
241      */
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)
249                 result = "on";
250             else
251                 result = "off";
252         }
253     }
254
255     /*
256      * Totally independent stuff
257      */
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);
265             resdup = FALSE;
266         }
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);
271             resdup = FALSE;
272         }
273 #define MKTIMESTR(format, tmfield) \
274             apr_time_exp_lt(&tm, apr_time_now()); \
275             result = apr_psprintf(p, format, tm.tmfield); \
276             resdup = FALSE;
277         else if (strcEQ(var, "TIME_MON")) {
278             MKTIMESTR("%02d", tm_mon+1)
279         }
280         else if (strcEQ(var, "TIME_DAY")) {
281             MKTIMESTR("%02d", tm_mday)
282         }
283         else if (strcEQ(var, "TIME_HOUR")) {
284             MKTIMESTR("%02d", tm_hour)
285         }
286         else if (strcEQ(var, "TIME_MIN")) {
287             MKTIMESTR("%02d", tm_min)
288         }
289         else if (strcEQ(var, "TIME_SEC")) {
290             MKTIMESTR("%02d", tm_sec)
291         }
292         else if (strcEQ(var, "TIME_WDAY")) {
293             MKTIMESTR("%d", tm_wday)
294         }
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);
301             resdup = FALSE;
302         }
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);
306         }
307     }
308
309     if (result != NULL && resdup)
310         result = apr_pstrdup(p, result);
311     if (result == NULL)
312         result = "";
313     return (char *)result;
314 }
315
316 static char *ssl_var_lookup_ssl(apr_pool_t *p, conn_rec *c, request_rec *r,
317                                 char *var)
318 {
319     SSLConnRec *sslconn = myConnConfig(c);
320     char *result;
321     X509 *xs;
322     STACK_OF(X509) *sk;
323     SSL *ssl;
324
325     result = NULL;
326
327     ssl = sslconn->ssl;
328     if (strlen(var) > 8 && strcEQn(var, "VERSION_", 8)) {
329         result = ssl_var_lookup_ssl_version(p, var+8);
330     }
331     else if (ssl != NULL && strcEQ(var, "PROTOCOL")) {
332         result = (char *)SSL_get_version(ssl);
333     }
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);
337         if (pSession) {
338             result = apr_pstrdup(p, SSL_SESSION_id2sz(
339                                      pSession->session_id,
340                                      pSession->session_id_length,
341                                      buf, sizeof(buf)));
342         }
343     }
344     else if(ssl != NULL && strcEQ(var, "SESSION_RESUMED")) {
345         if (SSL_session_reused(ssl) == 1)
346             result = "Resumed";
347         else
348             result = "Initial";
349     }
350     else if (ssl != NULL && strlen(var) >= 6 && strcEQn(var, "CIPHER", 6)) {
351         result = ssl_var_lookup_ssl_cipher(p, c, var+6);
352     }
353     else if (ssl != NULL && strlen(var) > 18 && strcEQn(var, "CLIENT_CERT_CHAIN_", 18)) {
354         sk = SSL_get_peer_cert_chain(ssl);
355         result = ssl_var_lookup_ssl_cert_chain(p, sk, var+18);
356     }
357     else if (ssl != NULL && strcEQ(var, "CLIENT_VERIFY")) {
358         result = ssl_var_lookup_ssl_cert_verify(p, c);
359     }
360     else if (ssl != NULL && strlen(var) > 7 && strcEQn(var, "CLIENT_", 7)) {
361         if ((xs = SSL_get_peer_certificate(ssl)) != NULL) {
362             result = ssl_var_lookup_ssl_cert(p, r, xs, var+7);
363             X509_free(xs);
364         }
365     }
366     else if (ssl != NULL && strlen(var) > 7 && strcEQn(var, "SERVER_", 7)) {
367         if ((xs = SSL_get_certificate(ssl)) != NULL) {
368             result = ssl_var_lookup_ssl_cert(p, r, xs, var+7);
369             /* SSL_get_certificate is different from SSL_get_peer_certificate.
370              * No need to X509_free(xs).
371              */
372         }
373     }
374     else if (ssl != NULL && strcEQ(var, "COMPRESS_METHOD")) {
375         result = ssl_var_lookup_ssl_compress_meth(ssl);
376     }
377 #ifndef OPENSSL_NO_TLSEXT
378     else if (ssl != NULL && strcEQ(var, "TLS_SNI")) {
379         result = apr_pstrdup(p, SSL_get_servername(ssl,
380                                                    TLSEXT_NAMETYPE_host_name));
381     }
382 #endif
383     else if (ssl != NULL && strcEQ(var, "SECURE_RENEG")) {
384         int flag = 0;
385 #ifdef SSL_get_secure_renegotiation_support
386         flag = SSL_get_secure_renegotiation_support(ssl);
387 #endif
388         result = apr_pstrdup(p, flag ? "true" : "false");
389     }
390
391     return result;
392 }
393
394 static char *ssl_var_lookup_ssl_cert_dn_oneline(apr_pool_t *p, request_rec *r,
395                                                 X509_NAME *xsname)
396 {
397     char *result = NULL;
398     SSLDirConfigRec *dc;
399     int legacy_format = 0;
400     if (r) {
401         dc = myDirConfig(r);
402         legacy_format = dc->nOptions & SSL_OPT_LEGACYDNFORMAT;
403     }
404     if (legacy_format) {
405         char *cp = X509_NAME_oneline(xsname, NULL, 0);
406         result = apr_pstrdup(p, cp);
407         OPENSSL_free(cp);
408     }
409     else {
410         BIO* bio;
411         int n;
412         unsigned long flags = XN_FLAG_RFC2253 & ~ASN1_STRFLGS_ESC_MSB;
413         if ((bio = BIO_new(BIO_s_mem())) == NULL)
414             return NULL;
415         X509_NAME_print_ex(bio, xsname, 0, flags);
416         n = BIO_pending(bio);
417         if (n > 0) {
418             result = apr_palloc(p, n+1);
419             n = BIO_read(bio, result, n);
420             result[n] = NUL;
421         }
422         BIO_free(bio);
423     }
424     return result;
425 }
426
427 static char *ssl_var_lookup_ssl_cert(apr_pool_t *p, request_rec *r, X509 *xs,
428                                      char *var)
429 {
430     char *result;
431     BOOL resdup;
432     X509_NAME *xsname;
433     int nid;
434
435     result = NULL;
436     resdup = TRUE;
437
438     if (strcEQ(var, "M_VERSION")) {
439         result = apr_psprintf(p, "%lu", X509_get_version(xs)+1);
440         resdup = FALSE;
441     }
442     else if (strcEQ(var, "M_SERIAL")) {
443         result = ssl_var_lookup_ssl_cert_serial(p, xs);
444     }
445     else if (strcEQ(var, "V_START")) {
446         result = ssl_var_lookup_ssl_cert_valid(p, X509_get_notBefore(xs));
447     }
448     else if (strcEQ(var, "V_END")) {
449         result = ssl_var_lookup_ssl_cert_valid(p, X509_get_notAfter(xs));
450     }
451     else if (strcEQ(var, "V_REMAIN")) {
452         result = ssl_var_lookup_ssl_cert_remain(p, X509_get_notAfter(xs));
453         resdup = FALSE;
454     }
455     else if (*var && strcEQ(var+1, "_DN")) {
456         if (*var == 'S')
457             xsname = X509_get_subject_name(xs);
458         else if (*var == 'I')
459             xsname = X509_get_issuer_name(xs);
460         else
461             return NULL;
462         result = ssl_var_lookup_ssl_cert_dn_oneline(p, r, xsname);
463         resdup = FALSE;
464     }
465     else if (strlen(var) > 5 && strcEQn(var+1, "_DN_", 4)) {
466         if (*var == 'S')
467             xsname = X509_get_subject_name(xs);
468         else if (*var == 'I')
469             xsname = X509_get_issuer_name(xs);
470         else
471             return NULL;
472         result = ssl_var_lookup_ssl_cert_dn(p, xsname, var+5);
473         resdup = FALSE;
474     }
475     else if (strcEQ(var, "A_SIG")) {
476         nid = OBJ_obj2nid((ASN1_OBJECT *)(xs->cert_info->signature->algorithm));
477         result = apr_pstrdup(p,
478                              (nid == NID_undef) ? "UNKNOWN" : OBJ_nid2ln(nid));
479         resdup = FALSE;
480     }
481     else if (strcEQ(var, "A_KEY")) {
482         nid = OBJ_obj2nid((ASN1_OBJECT *)(xs->cert_info->key->algor->algorithm));
483         result = apr_pstrdup(p,
484                              (nid == NID_undef) ? "UNKNOWN" : OBJ_nid2ln(nid));
485         resdup = FALSE;
486     }
487     else if (strcEQ(var, "CERT")) {
488         result = ssl_var_lookup_ssl_cert_PEM(p, xs);
489     }
490
491     if (result != NULL && resdup)
492         result = apr_pstrdup(p, result);
493     return result;
494 }
495
496 /* In this table, .extract is non-zero if RDNs using the NID should be
497  * extracted to for the SSL_{CLIENT,SERVER}_{I,S}_DN_* environment
498  * variables. */
499 static const struct {
500     char *name;
501     int   nid;
502     int   extract;
503 } ssl_var_lookup_ssl_cert_dn_rec[] = {
504     { "C",     NID_countryName,            1 },
505     { "ST",    NID_stateOrProvinceName,    1 }, /* officially    (RFC2156) */
506     { "SP",    NID_stateOrProvinceName,    0 }, /* compatibility (SSLeay)  */
507     { "L",     NID_localityName,           1 },
508     { "O",     NID_organizationName,       1 },
509     { "OU",    NID_organizationalUnitName, 1 },
510     { "CN",    NID_commonName,             1 },
511     { "T",     NID_title,                  1 },
512     { "I",     NID_initials,               1 },
513     { "G",     NID_givenName,              1 },
514     { "S",     NID_surname,                1 },
515     { "D",     NID_description,            1 },
516 #ifdef NID_userId
517     { "UID",   NID_userId,                 1 },
518 #endif
519     { "Email", NID_pkcs9_emailAddress,     1 },
520     { NULL,    0,                          0 }
521 };
522
523 static char *ssl_var_lookup_ssl_cert_dn(apr_pool_t *p, X509_NAME *xsname, char *var)
524 {
525     char *result, *ptr;
526     X509_NAME_ENTRY *xsne;
527     int i, j, n, idx = 0;
528     apr_size_t varlen;
529
530     /* if an _N suffix is used, find the Nth attribute of given name */
531     ptr = strchr(var, '_');
532     if (ptr != NULL && strspn(ptr + 1, "0123456789") == strlen(ptr + 1)) {
533         idx = atoi(ptr + 1);
534         varlen = ptr - var;
535     } else {
536         varlen = strlen(var);
537     }
538
539     result = NULL;
540
541     for (i = 0; ssl_var_lookup_ssl_cert_dn_rec[i].name != NULL; i++) {
542         if (strEQn(var, ssl_var_lookup_ssl_cert_dn_rec[i].name, varlen)
543             && strlen(ssl_var_lookup_ssl_cert_dn_rec[i].name) == varlen) {
544             for (j = 0; j < sk_X509_NAME_ENTRY_num((STACK_OF(X509_NAME_ENTRY) *)
545                                                    xsname->entries);
546                  j++) {
547                 xsne = sk_X509_NAME_ENTRY_value((STACK_OF(X509_NAME_ENTRY) *)
548                                                 xsname->entries, j);
549
550                 n =OBJ_obj2nid((ASN1_OBJECT *)X509_NAME_ENTRY_get_object(xsne));
551
552                 if (n == ssl_var_lookup_ssl_cert_dn_rec[i].nid && idx-- == 0) {
553                     result = SSL_X509_NAME_ENTRY_to_string(p, xsne);
554                     break;
555                 }
556             }
557             break;
558         }
559     }
560     return result;
561 }
562
563 static char *ssl_var_lookup_ssl_cert_valid(apr_pool_t *p, ASN1_TIME *tm)
564 {
565     char *result;
566     BIO* bio;
567     int n;
568
569     if ((bio = BIO_new(BIO_s_mem())) == NULL)
570         return NULL;
571     ASN1_TIME_print(bio, tm);
572     n = BIO_pending(bio);
573     result = apr_pcalloc(p, n+1);
574     n = BIO_read(bio, result, n);
575     result[n] = NUL;
576     BIO_free(bio);
577     return result;
578 }
579
580 #define DIGIT2NUM(x) (((x)[0] - '0') * 10 + (x)[1] - '0')
581
582 /* Return a string giving the number of days remaining until 'tm', or
583  * "0" if this can't be determined. */
584 static char *ssl_var_lookup_ssl_cert_remain(apr_pool_t *p, ASN1_TIME *tm)
585 {
586     apr_time_t then, now = apr_time_now();
587     apr_time_exp_t exp = {0};
588     long diff;
589     unsigned char *dp;
590
591     /* Fail if the time isn't a valid ASN.1 TIME; RFC3280 mandates
592      * that the seconds digits are present even though ASN.1
593      * doesn't. */
594     if ((tm->type == V_ASN1_UTCTIME && tm->length < 11) ||
595         (tm->type == V_ASN1_GENERALIZEDTIME && tm->length < 13) ||
596         !ASN1_TIME_check(tm)) {
597         return apr_pstrdup(p, "0");
598     }
599
600     if (tm->type == V_ASN1_UTCTIME) {
601         exp.tm_year = DIGIT2NUM(tm->data);
602         if (exp.tm_year <= 50) exp.tm_year += 100;
603         dp = tm->data + 2;
604     } else {
605         exp.tm_year = DIGIT2NUM(tm->data) * 100 + DIGIT2NUM(tm->data + 2) - 1900;
606         dp = tm->data + 4;
607     }
608
609     exp.tm_mon = DIGIT2NUM(dp) - 1;
610     exp.tm_mday = DIGIT2NUM(dp + 2) + 1;
611     exp.tm_hour = DIGIT2NUM(dp + 4);
612     exp.tm_min = DIGIT2NUM(dp + 6);
613     exp.tm_sec = DIGIT2NUM(dp + 8);
614
615     if (apr_time_exp_gmt_get(&then, &exp) != APR_SUCCESS) {
616         return apr_pstrdup(p, "0");
617     }
618
619     diff = (long)((apr_time_sec(then) - apr_time_sec(now)) / (60*60*24));
620
621     return diff > 0 ? apr_ltoa(p, diff) : apr_pstrdup(p, "0");
622 }
623
624 static char *ssl_var_lookup_ssl_cert_serial(apr_pool_t *p, X509 *xs)
625 {
626     char *result;
627     BIO *bio;
628     int n;
629
630     if ((bio = BIO_new(BIO_s_mem())) == NULL)
631         return NULL;
632     i2a_ASN1_INTEGER(bio, X509_get_serialNumber(xs));
633     n = BIO_pending(bio);
634     result = apr_pcalloc(p, n+1);
635     n = BIO_read(bio, result, n);
636     result[n] = NUL;
637     BIO_free(bio);
638     return result;
639 }
640
641 static char *ssl_var_lookup_ssl_cert_chain(apr_pool_t *p, STACK_OF(X509) *sk, char *var)
642 {
643     char *result;
644     X509 *xs;
645     int n;
646
647     result = NULL;
648
649     if (strspn(var, "0123456789") == strlen(var)) {
650         n = atoi(var);
651         if (n < sk_X509_num(sk)) {
652             xs = sk_X509_value(sk, n);
653             result = ssl_var_lookup_ssl_cert_PEM(p, xs);
654         }
655     }
656
657     return result;
658 }
659
660 static char *ssl_var_lookup_ssl_cert_PEM(apr_pool_t *p, X509 *xs)
661 {
662     char *result;
663     BIO *bio;
664     int n;
665
666     if ((bio = BIO_new(BIO_s_mem())) == NULL)
667         return NULL;
668     PEM_write_bio_X509(bio, xs);
669     n = BIO_pending(bio);
670     result = apr_pcalloc(p, n+1);
671     n = BIO_read(bio, result, n);
672     result[n] = NUL;
673     BIO_free(bio);
674     return result;
675 }
676
677 static char *ssl_var_lookup_ssl_cert_verify(apr_pool_t *p, conn_rec *c)
678 {
679     SSLConnRec *sslconn = myConnConfig(c);
680     char *result;
681     long vrc;
682     const char *verr;
683     const char *vinfo;
684     SSL *ssl;
685     X509 *xs;
686
687     result = NULL;
688     ssl   = sslconn->ssl;
689     verr  = sslconn->verify_error;
690     vinfo = sslconn->verify_info;
691     vrc   = SSL_get_verify_result(ssl);
692     xs    = SSL_get_peer_certificate(ssl);
693
694     if (vrc == X509_V_OK && verr == NULL && xs == NULL)
695         /* no client verification done at all */
696         result = "NONE";
697     else if (vrc == X509_V_OK && verr == NULL && vinfo == NULL && xs != NULL)
698         /* client verification done successful */
699         result = "SUCCESS";
700     else if (vrc == X509_V_OK && vinfo != NULL && strEQ(vinfo, "GENEROUS"))
701         /* client verification done in generous way */
702         result = "GENEROUS";
703     else
704         /* client verification failed */
705         result = apr_psprintf(p, "FAILED:%s",
706                               verr ? verr : X509_verify_cert_error_string(vrc));
707
708     if (xs)
709         X509_free(xs);
710     return result;
711 }
712
713 static char *ssl_var_lookup_ssl_cipher(apr_pool_t *p, conn_rec *c, char *var)
714 {
715     SSLConnRec *sslconn = myConnConfig(c);
716     char *result;
717     BOOL resdup;
718     int usekeysize, algkeysize;
719     SSL *ssl;
720
721     result = NULL;
722     resdup = TRUE;
723
724     ssl = sslconn->ssl;
725     ssl_var_lookup_ssl_cipher_bits(ssl, &usekeysize, &algkeysize);
726
727     if (ssl && strEQ(var, "")) {
728         MODSSL_SSL_CIPHER_CONST SSL_CIPHER *cipher = SSL_get_current_cipher(ssl);
729         result = (cipher != NULL ? (char *)SSL_CIPHER_get_name(cipher) : NULL);
730     }
731     else if (strcEQ(var, "_EXPORT"))
732         result = (usekeysize < 56 ? "true" : "false");
733     else if (strcEQ(var, "_USEKEYSIZE")) {
734         result = apr_itoa(p, usekeysize);
735         resdup = FALSE;
736     }
737     else if (strcEQ(var, "_ALGKEYSIZE")) {
738         result = apr_itoa(p, algkeysize);
739         resdup = FALSE;
740     }
741
742     if (result != NULL && resdup)
743         result = apr_pstrdup(p, result);
744     return result;
745 }
746
747 static void ssl_var_lookup_ssl_cipher_bits(SSL *ssl, int *usekeysize, int *algkeysize)
748 {
749     MODSSL_SSL_CIPHER_CONST SSL_CIPHER *cipher;
750
751     *usekeysize = 0;
752     *algkeysize = 0;
753     if (ssl != NULL)
754         if ((cipher = SSL_get_current_cipher(ssl)) != NULL)
755             *usekeysize = SSL_CIPHER_get_bits(cipher, algkeysize);
756     return;
757 }
758
759 static char *ssl_var_lookup_ssl_version(apr_pool_t *p, char *var)
760 {
761     if (strEQ(var, "INTERFACE")) {
762         return apr_pstrdup(p, var_interface);
763     }
764     else if (strEQ(var, "LIBRARY_INTERFACE")) {
765         return apr_pstrdup(p, var_library_interface);
766     }
767     else if (strEQ(var, "LIBRARY")) {
768         return apr_pstrdup(p, var_library);
769     }
770     return NULL;
771 }
772
773 /* Add each RDN in 'xn' to the table 't' where the NID is present in
774  * 'nids', using key prefix 'pfx'.  */
775 static void extract_dn(apr_table_t *t, apr_hash_t *nids, const char *pfx,
776                        X509_NAME *xn, apr_pool_t *p)
777 {
778     STACK_OF(X509_NAME_ENTRY) *ents = xn->entries;
779     X509_NAME_ENTRY *xsne;
780     apr_hash_t *count;
781     int i, nid;
782
783     /* Hash of (int) NID -> (int *) counter to count each time an RDN
784      * with the given NID has been seen. */
785     count = apr_hash_make(p);
786
787     /* For each RDN... */
788     for (i = 0; i < sk_X509_NAME_ENTRY_num(ents); i++) {
789          const char *tag;
790
791          xsne = sk_X509_NAME_ENTRY_value(ents, i);
792
793          /* Retrieve the nid, and check whether this is one of the nids
794           * which are to be extracted. */
795          nid = OBJ_obj2nid((ASN1_OBJECT *)X509_NAME_ENTRY_get_object(xsne));
796
797          tag = apr_hash_get(nids, &nid, sizeof nid);
798          if (tag) {
799              const char *key;
800              int *dup;
801              char *value;
802
803              /* Check whether a variable with this nid was already
804               * been used; if so, use the foo_N=bar syntax. */
805              dup = apr_hash_get(count, &nid, sizeof nid);
806              if (dup) {
807                  key = apr_psprintf(p, "%s%s_%d", pfx, tag, ++(*dup));
808              }
809              else {
810                  /* Otherwise, use the plain foo=bar syntax. */
811                  dup = apr_pcalloc(p, sizeof *dup);
812                  apr_hash_set(count, &nid, sizeof nid, dup);
813                  key = apr_pstrcat(p, pfx, tag, NULL);
814              }
815              value = SSL_X509_NAME_ENTRY_to_string(p, xsne);
816              apr_table_setn(t, key, value);
817          }
818     }
819 }
820
821 void modssl_var_extract_dns(apr_table_t *t, SSL *ssl, apr_pool_t *p)
822 {
823     apr_hash_t *nids;
824     unsigned n;
825     X509 *xs;
826
827     /* Build up a hash table of (int *)NID->(char *)short-name for all
828      * the tags which are to be extracted: */
829     nids = apr_hash_make(p);
830     for (n = 0; ssl_var_lookup_ssl_cert_dn_rec[n].name; n++) {
831         if (ssl_var_lookup_ssl_cert_dn_rec[n].extract) {
832             apr_hash_set(nids, &ssl_var_lookup_ssl_cert_dn_rec[n].nid,
833                          sizeof(ssl_var_lookup_ssl_cert_dn_rec[0].nid),
834                          ssl_var_lookup_ssl_cert_dn_rec[n].name);
835         }
836     }
837
838     /* Extract the server cert DNS -- note that the refcount does NOT
839      * increase: */
840     xs = SSL_get_certificate(ssl);
841     if (xs) {
842         extract_dn(t, nids, "SSL_SERVER_S_DN_", X509_get_subject_name(xs), p);
843         extract_dn(t, nids, "SSL_SERVER_I_DN_", X509_get_issuer_name(xs), p);
844     }
845
846     /* Extract the client cert DNs -- note that the refcount DOES
847      * increase: */
848     xs = SSL_get_peer_certificate(ssl);
849     if (xs) {
850         extract_dn(t, nids, "SSL_CLIENT_S_DN_", X509_get_subject_name(xs), p);
851         extract_dn(t, nids, "SSL_CLIENT_I_DN_", X509_get_issuer_name(xs), p);
852         X509_free(xs);
853     }
854 }
855
856 /* For an extension type which OpenSSL does not recognize, attempt to
857  * parse the extension type as a primitive string.  This will fail for
858  * any structured extension type per the docs.  Returns non-zero on
859  * success and writes the string to the given bio. */
860 static int dump_extn_value(BIO *bio, ASN1_OCTET_STRING *str)
861 {
862     MODSSL_D2I_ASN1_type_bytes_CONST unsigned char *pp = str->data;
863     ASN1_STRING *ret = ASN1_STRING_new();
864     int rv = 0;
865
866     /* This allows UTF8String, IA5String, VisibleString, or BMPString;
867      * conversion to UTF-8 is forced. */
868     if (d2i_DISPLAYTEXT(&ret, &pp, str->length)) {
869         ASN1_STRING_print_ex(bio, ret, ASN1_STRFLGS_UTF8_CONVERT);
870         rv = 1;
871     }
872
873     ASN1_STRING_free(ret);
874     return rv;
875 }
876
877 apr_array_header_t *ssl_ext_list(apr_pool_t *p, conn_rec *c, int peer,
878                                  const char *extension)
879 {
880     SSLConnRec *sslconn = myConnConfig(c);
881     SSL *ssl = NULL;
882     apr_array_header_t *array = NULL;
883     X509 *xs = NULL;
884     ASN1_OBJECT *oid = NULL;
885     int count = 0, j;
886
887     if (!sslconn || !sslconn->ssl || !extension) {
888         return NULL;
889     }
890     ssl = sslconn->ssl;
891
892     /* We accept the "extension" string to be converted as
893      * a long name (nsComment), short name (DN) or
894      * numeric OID (1.2.3.4).
895      */
896     oid = OBJ_txt2obj(extension, 0);
897     if (!oid) {
898         ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, c, APLOGNO(01970)
899                       "could not parse OID '%s'", extension);
900         ERR_clear_error();
901         return NULL;
902     }
903
904     xs = peer ? SSL_get_peer_certificate(ssl) : SSL_get_certificate(ssl);
905     if (xs == NULL) {
906         return NULL;
907     }
908
909     count = X509_get_ext_count(xs);
910     /* Create an array large enough to accomodate every extension. This is
911      * likely overkill, but safe.
912      */
913     array = apr_array_make(p, count, sizeof(char *));
914     for (j = 0; j < count; j++) {
915         X509_EXTENSION *ext = X509_get_ext(xs, j);
916
917         if (OBJ_cmp(ext->object, oid) == 0) {
918             BIO *bio = BIO_new(BIO_s_mem());
919
920             /* We want to obtain a string representation of the extensions
921              * value and add it to the array we're building.
922              * X509V3_EXT_print() doesn't know about all the possible
923              * data types, but the value is stored as an ASN1_OCTET_STRING
924              * allowing us a fallback in case of X509V3_EXT_print
925              * not knowing how to handle the data.
926              */
927             if (X509V3_EXT_print(bio, ext, 0, 0) == 1 ||
928                 dump_extn_value(bio, X509_EXTENSION_get_data(ext)) == 1) {
929                 BUF_MEM *buf;
930                 char **ptr = apr_array_push(array);
931                 BIO_get_mem_ptr(bio, &buf);
932                 *ptr = apr_pstrmemdup(p, buf->data, buf->length);
933             } else {
934                 ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, c, APLOGNO(01971)
935                               "Found an extension '%s', but failed to "
936                               "create a string from it", extension);
937             }
938             BIO_vfree(bio);
939         }
940     }
941
942     if (array->nelts == 0)
943         array = NULL;
944
945     if (peer) {
946         /* only SSL_get_peer_certificate raises the refcount */
947         X509_free(xs);
948     }
949
950     ASN1_OBJECT_free(oid);
951     ERR_clear_error();
952     return array;
953 }
954
955 static char *ssl_var_lookup_ssl_compress_meth(SSL *ssl)
956 {
957     char *result = "NULL";
958 #if (OPENSSL_VERSION_NUMBER >= 0x00908000)
959     SSL_SESSION *pSession = SSL_get_session(ssl);
960
961     if (pSession) {
962         switch (pSession->compress_meth) {
963         case 0:
964             /* default "NULL" already set */
965             break;
966
967             /* Defined by RFC 3749, deflate is coded by "1" */
968         case 1:
969             result = "DEFLATE";
970             break;
971
972             /* IANA assigned compression number for LZS */
973         case 0x40:
974             result = "LZS";
975             break;
976
977         default:
978             result = "UNKNOWN";
979             break;
980         }
981     }
982 #endif
983     return result;
984 }
985
986 /*  _________________________________________________________________
987 **
988 **  SSL Extension to mod_log_config
989 **  _________________________________________________________________
990 */
991
992 #include "../../modules/loggers/mod_log_config.h"
993
994 static const char *ssl_var_log_handler_c(request_rec *r, char *a);
995 static const char *ssl_var_log_handler_x(request_rec *r, char *a);
996
997 /*
998  * register us for the mod_log_config function registering phase
999  * to establish %{...}c and to be able to expand %{...}x variables.
1000  */
1001 void ssl_var_log_config_register(apr_pool_t *p)
1002 {
1003     static APR_OPTIONAL_FN_TYPE(ap_register_log_handler) *log_pfn_register;
1004
1005     log_pfn_register = APR_RETRIEVE_OPTIONAL_FN(ap_register_log_handler);
1006
1007     if (log_pfn_register) {
1008         log_pfn_register(p, "c", ssl_var_log_handler_c, 0);
1009         log_pfn_register(p, "x", ssl_var_log_handler_x, 0);
1010     }
1011     return;
1012 }
1013
1014 /*
1015  * implement the %{..}c log function
1016  * (we are the only function)
1017  */
1018 static const char *ssl_var_log_handler_c(request_rec *r, char *a)
1019 {
1020     SSLConnRec *sslconn = myConnConfig(r->connection);
1021     char *result;
1022
1023     if (sslconn == NULL || sslconn->ssl == NULL)
1024         return NULL;
1025     result = NULL;
1026     if (strEQ(a, "version"))
1027         result = ssl_var_lookup(r->pool, r->server, r->connection, r, "SSL_PROTOCOL");
1028     else if (strEQ(a, "cipher"))
1029         result = ssl_var_lookup(r->pool, r->server, r->connection, r, "SSL_CIPHER");
1030     else if (strEQ(a, "subjectdn") || strEQ(a, "clientcert"))
1031         result = ssl_var_lookup(r->pool, r->server, r->connection, r, "SSL_CLIENT_S_DN");
1032     else if (strEQ(a, "issuerdn") || strEQ(a, "cacert"))
1033         result = ssl_var_lookup(r->pool, r->server, r->connection, r, "SSL_CLIENT_I_DN");
1034     else if (strEQ(a, "errcode"))
1035         result = "-";
1036     else if (strEQ(a, "errstr"))
1037         result = (char *)sslconn->verify_error;
1038     if (result != NULL && result[0] == NUL)
1039         result = NULL;
1040     return result;
1041 }
1042
1043 /*
1044  * extend the implementation of the %{..}x log function
1045  * (there can be more functions)
1046  */
1047 static const char *ssl_var_log_handler_x(request_rec *r, char *a)
1048 {
1049     char *result;
1050
1051     result = ssl_var_lookup(r->pool, r->server, r->connection, r, a);
1052     if (result != NULL && result[0] == NUL)
1053         result = NULL;
1054     return result;
1055 }
1056
1057