]> granicus.if.org Git - apache/blob - modules/ssl/ssl_engine_vars.c
mod_cache: Don't add cached/revalidated entity headers to a 304 response.
[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             unsigned char *id;
339             unsigned int idlen;
340
341 #ifdef OPENSSL_NO_SSL_INTERN
342             id = (unsigned char *)SSL_SESSION_get_id(pSession, &idlen);
343 #else
344             id = pSession->session_id;
345             idlen = pSession->session_id_length;
346 #endif
347
348             result = apr_pstrdup(p, SSL_SESSION_id2sz(id, idlen,
349                                                       buf, sizeof(buf)));
350         }
351     }
352     else if(ssl != NULL && strcEQ(var, "SESSION_RESUMED")) {
353         if (SSL_session_reused(ssl) == 1)
354             result = "Resumed";
355         else
356             result = "Initial";
357     }
358     else if (ssl != NULL && strlen(var) >= 6 && strcEQn(var, "CIPHER", 6)) {
359         result = ssl_var_lookup_ssl_cipher(p, c, var+6);
360     }
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);
364     }
365     else if (ssl != NULL && strcEQ(var, "CLIENT_VERIFY")) {
366         result = ssl_var_lookup_ssl_cert_verify(p, c);
367     }
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);
371             X509_free(xs);
372         }
373     }
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).
379              */
380         }
381     }
382     else if (ssl != NULL && strcEQ(var, "COMPRESS_METHOD")) {
383         result = ssl_var_lookup_ssl_compress_meth(ssl);
384     }
385 #ifdef HAVE_TLSEXT
386     else if (ssl != NULL && strcEQ(var, "TLS_SNI")) {
387         result = apr_pstrdup(p, SSL_get_servername(ssl,
388                                                    TLSEXT_NAMETYPE_host_name));
389     }
390 #endif
391     else if (ssl != NULL && strcEQ(var, "SECURE_RENEG")) {
392         int flag = 0;
393 #ifdef SSL_get_secure_renegotiation_support
394         flag = SSL_get_secure_renegotiation_support(ssl);
395 #endif
396         result = apr_pstrdup(p, flag ? "true" : "false");
397     }
398 #ifdef HAVE_SRP
399     else if (ssl != NULL && strcEQ(var, "SRP_USER")) {
400         if ((result = SSL_get_srp_username(ssl)) != NULL) {
401             result = apr_pstrdup(p, result);
402         }
403     }
404     else if (ssl != NULL && strcEQ(var, "SRP_USERINFO")) {
405         if ((result = SSL_get_srp_userinfo(ssl)) != NULL) {
406             result = apr_pstrdup(p, result);
407         }
408     }
409 #endif
410
411     return result;
412 }
413
414 static char *ssl_var_lookup_ssl_cert_dn_oneline(apr_pool_t *p, request_rec *r,
415                                                 X509_NAME *xsname)
416 {
417     char *result = NULL;
418     SSLDirConfigRec *dc;
419     int legacy_format = 0;
420     if (r) {
421         dc = myDirConfig(r);
422         legacy_format = dc->nOptions & SSL_OPT_LEGACYDNFORMAT;
423     }
424     if (legacy_format) {
425         char *cp = X509_NAME_oneline(xsname, NULL, 0);
426         result = apr_pstrdup(p, cp);
427         OPENSSL_free(cp);
428     }
429     else {
430         BIO* bio;
431         int n;
432         unsigned long flags = XN_FLAG_RFC2253 & ~ASN1_STRFLGS_ESC_MSB;
433         if ((bio = BIO_new(BIO_s_mem())) == NULL)
434             return NULL;
435         X509_NAME_print_ex(bio, xsname, 0, flags);
436         n = BIO_pending(bio);
437         if (n > 0) {
438             result = apr_palloc(p, n+1);
439             n = BIO_read(bio, result, n);
440             result[n] = NUL;
441         }
442         BIO_free(bio);
443     }
444     return result;
445 }
446
447 static char *ssl_var_lookup_ssl_cert(apr_pool_t *p, request_rec *r, X509 *xs,
448                                      char *var)
449 {
450     char *result;
451     BOOL resdup;
452     X509_NAME *xsname;
453     int nid;
454
455     result = NULL;
456     resdup = TRUE;
457
458     if (strcEQ(var, "M_VERSION")) {
459         result = apr_psprintf(p, "%lu", X509_get_version(xs)+1);
460         resdup = FALSE;
461     }
462     else if (strcEQ(var, "M_SERIAL")) {
463         result = ssl_var_lookup_ssl_cert_serial(p, xs);
464     }
465     else if (strcEQ(var, "V_START")) {
466         result = ssl_var_lookup_ssl_cert_valid(p, X509_get_notBefore(xs));
467     }
468     else if (strcEQ(var, "V_END")) {
469         result = ssl_var_lookup_ssl_cert_valid(p, X509_get_notAfter(xs));
470     }
471     else if (strcEQ(var, "V_REMAIN")) {
472         result = ssl_var_lookup_ssl_cert_remain(p, X509_get_notAfter(xs));
473         resdup = FALSE;
474     }
475     else if (*var && strcEQ(var+1, "_DN")) {
476         if (*var == 'S')
477             xsname = X509_get_subject_name(xs);
478         else if (*var == 'I')
479             xsname = X509_get_issuer_name(xs);
480         else
481             return NULL;
482         result = ssl_var_lookup_ssl_cert_dn_oneline(p, r, xsname);
483         resdup = FALSE;
484     }
485     else if (strlen(var) > 5 && strcEQn(var+1, "_DN_", 4)) {
486         if (*var == 'S')
487             xsname = X509_get_subject_name(xs);
488         else if (*var == 'I')
489             xsname = X509_get_issuer_name(xs);
490         else
491             return NULL;
492         result = ssl_var_lookup_ssl_cert_dn(p, xsname, var+5);
493         resdup = FALSE;
494     }
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));
499         resdup = FALSE;
500     }
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));
505         resdup = FALSE;
506     }
507     else if (strcEQ(var, "CERT")) {
508         result = ssl_var_lookup_ssl_cert_PEM(p, xs);
509     }
510
511     if (resdup)
512         result = apr_pstrdup(p, result);
513     return result;
514 }
515
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
518  * variables. */
519 static const struct {
520     char *name;
521     int   nid;
522     int   extract;
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 },
536 #ifdef NID_userId
537     { "UID",   NID_userId,                 1 },
538 #endif
539     { "Email", NID_pkcs9_emailAddress,     1 },
540     { NULL,    0,                          0 }
541 };
542
543 static char *ssl_var_lookup_ssl_cert_dn(apr_pool_t *p, X509_NAME *xsname, char *var)
544 {
545     char *result, *ptr;
546     X509_NAME_ENTRY *xsne;
547     int i, j, n, idx = 0;
548     apr_size_t varlen;
549
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)) {
553         idx = atoi(ptr + 1);
554         varlen = ptr - var;
555     } else {
556         varlen = strlen(var);
557     }
558
559     result = NULL;
560
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) *)
565                                                    xsname->entries);
566                  j++) {
567                 xsne = sk_X509_NAME_ENTRY_value((STACK_OF(X509_NAME_ENTRY) *)
568                                                 xsname->entries, j);
569
570                 n =OBJ_obj2nid((ASN1_OBJECT *)X509_NAME_ENTRY_get_object(xsne));
571
572                 if (n == ssl_var_lookup_ssl_cert_dn_rec[i].nid && idx-- == 0) {
573                     result = SSL_X509_NAME_ENTRY_to_string(p, xsne);
574                     break;
575                 }
576             }
577             break;
578         }
579     }
580     return result;
581 }
582
583 static char *ssl_var_lookup_ssl_cert_valid(apr_pool_t *p, ASN1_TIME *tm)
584 {
585     char *result;
586     BIO* bio;
587     int n;
588
589     if ((bio = BIO_new(BIO_s_mem())) == NULL)
590         return 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);
595     result[n] = NUL;
596     BIO_free(bio);
597     return result;
598 }
599
600 #define DIGIT2NUM(x) (((x)[0] - '0') * 10 + (x)[1] - '0')
601
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)
605 {
606     apr_time_t then, now = apr_time_now();
607     apr_time_exp_t exp = {0};
608     long diff;
609     unsigned char *dp;
610
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
613      * doesn't. */
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");
618     }
619
620     if (tm->type == V_ASN1_UTCTIME) {
621         exp.tm_year = DIGIT2NUM(tm->data);
622         if (exp.tm_year <= 50) exp.tm_year += 100;
623         dp = tm->data + 2;
624     } else {
625         exp.tm_year = DIGIT2NUM(tm->data) * 100 + DIGIT2NUM(tm->data + 2) - 1900;
626         dp = tm->data + 4;
627     }
628
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);
634
635     if (apr_time_exp_gmt_get(&then, &exp) != APR_SUCCESS) {
636         return apr_pstrdup(p, "0");
637     }
638
639     diff = (long)((apr_time_sec(then) - apr_time_sec(now)) / (60*60*24));
640
641     return diff > 0 ? apr_ltoa(p, diff) : apr_pstrdup(p, "0");
642 }
643
644 static char *ssl_var_lookup_ssl_cert_serial(apr_pool_t *p, X509 *xs)
645 {
646     char *result;
647     BIO *bio;
648     int n;
649
650     if ((bio = BIO_new(BIO_s_mem())) == NULL)
651         return 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);
656     result[n] = NUL;
657     BIO_free(bio);
658     return result;
659 }
660
661 static char *ssl_var_lookup_ssl_cert_chain(apr_pool_t *p, STACK_OF(X509) *sk, char *var)
662 {
663     char *result;
664     X509 *xs;
665     int n;
666
667     result = NULL;
668
669     if (strspn(var, "0123456789") == strlen(var)) {
670         n = atoi(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);
674         }
675     }
676
677     return result;
678 }
679
680 static char *ssl_var_lookup_ssl_cert_PEM(apr_pool_t *p, X509 *xs)
681 {
682     char *result;
683     BIO *bio;
684     int n;
685
686     if ((bio = BIO_new(BIO_s_mem())) == NULL)
687         return 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);
692     result[n] = NUL;
693     BIO_free(bio);
694     return result;
695 }
696
697 static char *ssl_var_lookup_ssl_cert_verify(apr_pool_t *p, conn_rec *c)
698 {
699     SSLConnRec *sslconn = myConnConfig(c);
700     char *result;
701     long vrc;
702     const char *verr;
703     const char *vinfo;
704     SSL *ssl;
705     X509 *xs;
706
707     result = NULL;
708     ssl   = sslconn->ssl;
709     verr  = sslconn->verify_error;
710     vinfo = sslconn->verify_info;
711     vrc   = SSL_get_verify_result(ssl);
712     xs    = SSL_get_peer_certificate(ssl);
713
714     if (vrc == X509_V_OK && verr == NULL && xs == NULL)
715         /* no client verification done at all */
716         result = "NONE";
717     else if (vrc == X509_V_OK && verr == NULL && vinfo == NULL && xs != NULL)
718         /* client verification done successful */
719         result = "SUCCESS";
720     else if (vrc == X509_V_OK && vinfo != NULL && strEQ(vinfo, "GENEROUS"))
721         /* client verification done in generous way */
722         result = "GENEROUS";
723     else
724         /* client verification failed */
725         result = apr_psprintf(p, "FAILED:%s",
726                               verr ? verr : X509_verify_cert_error_string(vrc));
727
728     if (xs)
729         X509_free(xs);
730     return result;
731 }
732
733 static char *ssl_var_lookup_ssl_cipher(apr_pool_t *p, conn_rec *c, char *var)
734 {
735     SSLConnRec *sslconn = myConnConfig(c);
736     char *result;
737     BOOL resdup;
738     int usekeysize, algkeysize;
739     SSL *ssl;
740
741     result = NULL;
742     resdup = TRUE;
743
744     ssl = sslconn->ssl;
745     ssl_var_lookup_ssl_cipher_bits(ssl, &usekeysize, &algkeysize);
746
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);
750     }
751     else if (strcEQ(var, "_EXPORT"))
752         result = (usekeysize < 56 ? "true" : "false");
753     else if (strcEQ(var, "_USEKEYSIZE")) {
754         result = apr_itoa(p, usekeysize);
755         resdup = FALSE;
756     }
757     else if (strcEQ(var, "_ALGKEYSIZE")) {
758         result = apr_itoa(p, algkeysize);
759         resdup = FALSE;
760     }
761
762     if (result != NULL && resdup)
763         result = apr_pstrdup(p, result);
764     return result;
765 }
766
767 static void ssl_var_lookup_ssl_cipher_bits(SSL *ssl, int *usekeysize, int *algkeysize)
768 {
769     MODSSL_SSL_CIPHER_CONST SSL_CIPHER *cipher;
770
771     *usekeysize = 0;
772     *algkeysize = 0;
773     if (ssl != NULL)
774         if ((cipher = SSL_get_current_cipher(ssl)) != NULL)
775             *usekeysize = SSL_CIPHER_get_bits(cipher, algkeysize);
776     return;
777 }
778
779 static char *ssl_var_lookup_ssl_version(apr_pool_t *p, char *var)
780 {
781     if (strEQ(var, "INTERFACE")) {
782         return apr_pstrdup(p, var_interface);
783     }
784     else if (strEQ(var, "LIBRARY_INTERFACE")) {
785         return apr_pstrdup(p, var_library_interface);
786     }
787     else if (strEQ(var, "LIBRARY")) {
788         return apr_pstrdup(p, var_library);
789     }
790     return NULL;
791 }
792
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)
797 {
798     STACK_OF(X509_NAME_ENTRY) *ents = xn->entries;
799     X509_NAME_ENTRY *xsne;
800     apr_hash_t *count;
801     int i, nid;
802
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);
806
807     /* For each RDN... */
808     for (i = 0; i < sk_X509_NAME_ENTRY_num(ents); i++) {
809          const char *tag;
810
811          xsne = sk_X509_NAME_ENTRY_value(ents, i);
812
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));
816
817          tag = apr_hash_get(nids, &nid, sizeof nid);
818          if (tag) {
819              const char *key;
820              int *dup;
821              char *value;
822
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);
826              if (dup) {
827                  key = apr_psprintf(p, "%s%s_%d", pfx, tag, ++(*dup));
828              }
829              else {
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);
834              }
835              value = SSL_X509_NAME_ENTRY_to_string(p, xsne);
836              apr_table_setn(t, key, value);
837          }
838     }
839 }
840
841 void modssl_var_extract_dns(apr_table_t *t, SSL *ssl, apr_pool_t *p)
842 {
843     apr_hash_t *nids;
844     unsigned n;
845     X509 *xs;
846
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);
855         }
856     }
857
858     /* Extract the server cert DNS -- note that the refcount does NOT
859      * increase: */
860     xs = SSL_get_certificate(ssl);
861     if (xs) {
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);
864     }
865
866     /* Extract the client cert DNs -- note that the refcount DOES
867      * increase: */
868     xs = SSL_get_peer_certificate(ssl);
869     if (xs) {
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);
872         X509_free(xs);
873     }
874 }
875
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)
881 {
882     const unsigned char *pp = str->data;
883     ASN1_STRING *ret = ASN1_STRING_new();
884     int rv = 0;
885
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);
890         rv = 1;
891     }
892
893     ASN1_STRING_free(ret);
894     return rv;
895 }
896
897 apr_array_header_t *ssl_ext_list(apr_pool_t *p, conn_rec *c, int peer,
898                                  const char *extension)
899 {
900     SSLConnRec *sslconn = myConnConfig(c);
901     SSL *ssl = NULL;
902     apr_array_header_t *array = NULL;
903     X509 *xs = NULL;
904     ASN1_OBJECT *oid = NULL;
905     int count = 0, j;
906
907     if (!sslconn || !sslconn->ssl || !extension) {
908         return NULL;
909     }
910     ssl = sslconn->ssl;
911
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).
915      */
916     oid = OBJ_txt2obj(extension, 0);
917     if (!oid) {
918         ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, c, APLOGNO(01970)
919                       "could not parse OID '%s'", extension);
920         ERR_clear_error();
921         return NULL;
922     }
923
924     xs = peer ? SSL_get_peer_certificate(ssl) : SSL_get_certificate(ssl);
925     if (xs == NULL) {
926         return NULL;
927     }
928
929     count = X509_get_ext_count(xs);
930     /* Create an array large enough to accomodate every extension. This is
931      * likely overkill, but safe.
932      */
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);
936
937         if (OBJ_cmp(ext->object, oid) == 0) {
938             BIO *bio = BIO_new(BIO_s_mem());
939
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.
946              */
947             if (X509V3_EXT_print(bio, ext, 0, 0) == 1 ||
948                 dump_extn_value(bio, X509_EXTENSION_get_data(ext)) == 1) {
949                 BUF_MEM *buf;
950                 char **ptr = apr_array_push(array);
951                 BIO_get_mem_ptr(bio, &buf);
952                 *ptr = apr_pstrmemdup(p, buf->data, buf->length);
953             } else {
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);
957             }
958             BIO_vfree(bio);
959         }
960     }
961
962     if (array->nelts == 0)
963         array = NULL;
964
965     if (peer) {
966         /* only SSL_get_peer_certificate raises the refcount */
967         X509_free(xs);
968     }
969
970     ASN1_OBJECT_free(oid);
971     ERR_clear_error();
972     return array;
973 }
974
975 static char *ssl_var_lookup_ssl_compress_meth(SSL *ssl)
976 {
977     char *result = "NULL";
978 #ifndef OPENSSL_NO_COMP
979     SSL_SESSION *pSession = SSL_get_session(ssl);
980
981     if (pSession) {
982 #ifdef OPENSSL_NO_SSL_INTERN
983         switch (SSL_SESSION_get_compress_id(pSession)) {
984 #else
985         switch (pSession->compress_meth) {
986 #endif
987         case 0:
988             /* default "NULL" already set */
989             break;
990
991             /* Defined by RFC 3749, deflate is coded by "1" */
992         case 1:
993             result = "DEFLATE";
994             break;
995
996             /* IANA assigned compression number for LZS */
997         case 0x40:
998             result = "LZS";
999             break;
1000
1001         default:
1002             result = "UNKNOWN";
1003             break;
1004         }
1005     }
1006 #endif
1007     return result;
1008 }
1009
1010 /*  _________________________________________________________________
1011 **
1012 **  SSL Extension to mod_log_config
1013 **  _________________________________________________________________
1014 */
1015
1016 #include "../../modules/loggers/mod_log_config.h"
1017
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);
1020
1021 /*
1022  * register us for the mod_log_config function registering phase
1023  * to establish %{...}c and to be able to expand %{...}x variables.
1024  */
1025 void ssl_var_log_config_register(apr_pool_t *p)
1026 {
1027     static APR_OPTIONAL_FN_TYPE(ap_register_log_handler) *log_pfn_register;
1028
1029     log_pfn_register = APR_RETRIEVE_OPTIONAL_FN(ap_register_log_handler);
1030
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);
1034     }
1035     return;
1036 }
1037
1038 /*
1039  * implement the %{..}c log function
1040  * (we are the only function)
1041  */
1042 static const char *ssl_var_log_handler_c(request_rec *r, char *a)
1043 {
1044     SSLConnRec *sslconn = myConnConfig(r->connection);
1045     char *result;
1046
1047     if (sslconn == NULL || sslconn->ssl == NULL)
1048         return NULL;
1049     result = 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"))
1059         result = "-";
1060     else if (strEQ(a, "errstr"))
1061         result = (char *)sslconn->verify_error;
1062     if (result != NULL && result[0] == NUL)
1063         result = NULL;
1064     return result;
1065 }
1066
1067 /*
1068  * extend the implementation of the %{..}x log function
1069  * (there can be more functions)
1070  */
1071 static const char *ssl_var_log_handler_x(request_rec *r, char *a)
1072 {
1073     char *result;
1074
1075     result = ssl_var_lookup(r->pool, r->server, r->connection, r, a);
1076     if (result != NULL && result[0] == NUL)
1077         result = NULL;
1078     return result;
1079 }
1080
1081