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, SSLConnRec *sslconn, 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, const char *var);
45 static char *ssl_var_lookup_ssl_cert_san(apr_pool_t *p, X509 *xs, char *var);
46 static char *ssl_var_lookup_ssl_cert_valid(apr_pool_t *p, ASN1_TIME *tm);
47 static char *ssl_var_lookup_ssl_cert_remain(apr_pool_t *p, ASN1_TIME *tm);
48 static char *ssl_var_lookup_ssl_cert_serial(apr_pool_t *p, X509 *xs);
49 static char *ssl_var_lookup_ssl_cert_chain(apr_pool_t *p, STACK_OF(X509) *sk, char *var);
50 static char *ssl_var_lookup_ssl_cert_rfc4523_cea(apr_pool_t *p, SSL *ssl);
51 static char *ssl_var_lookup_ssl_cert_PEM(apr_pool_t *p, X509 *xs);
52 static char *ssl_var_lookup_ssl_cert_verify(apr_pool_t *p, SSLConnRec *sslconn);
53 static char *ssl_var_lookup_ssl_cipher(apr_pool_t *p, SSLConnRec *sslconn, char *var);
54 static void  ssl_var_lookup_ssl_cipher_bits(SSL *ssl, int *usekeysize, int *algkeysize);
55 static char *ssl_var_lookup_ssl_version(apr_pool_t *p, char *var);
56 static char *ssl_var_lookup_ssl_compress_meth(SSL *ssl);
57 
ssl_get_effective_config(conn_rec * c)58 static SSLConnRec *ssl_get_effective_config(conn_rec *c)
59 {
60     SSLConnRec *sslconn = myConnConfig(c);
61     if (!(sslconn && sslconn->ssl) && c->master) {
62         /* use master connection if no SSL defined here */
63         sslconn = myConnConfig(c->master);
64     }
65     return sslconn;
66 }
67 
ssl_conn_is_ssl(conn_rec * c)68 static int ssl_conn_is_ssl(conn_rec *c)
69 {
70     const SSLConnRec *sslconn = ssl_get_effective_config(c);
71     return (sslconn && sslconn->ssl)? OK : DECLINED;
72 }
73 
74 static const char var_interface[] = "mod_ssl/" AP_SERVER_BASEREVISION;
75 static char var_library_interface[] = MODSSL_LIBRARY_TEXT;
76 static char *var_library = NULL;
77 
expr_peer_ext_list_fn(ap_expr_eval_ctx_t * ctx,const void * dummy,const char * arg)78 static apr_array_header_t *expr_peer_ext_list_fn(ap_expr_eval_ctx_t *ctx,
79                                                  const void *dummy,
80                                                  const char *arg)
81 {
82     return ssl_ext_list(ctx->p, ctx->c, 1, arg);
83 }
84 
expr_var_fn(ap_expr_eval_ctx_t * ctx,const void * data)85 static const char *expr_var_fn(ap_expr_eval_ctx_t *ctx, const void *data)
86 {
87     char *var = (char *)data;
88     SSLConnRec *sslconn = ssl_get_effective_config(ctx->c);
89 
90     return sslconn ? ssl_var_lookup_ssl(ctx->p, sslconn, ctx->r, var) : NULL;
91 }
92 
expr_func_fn(ap_expr_eval_ctx_t * ctx,const void * data,const char * arg)93 static const char *expr_func_fn(ap_expr_eval_ctx_t *ctx, const void *data,
94                                 const char *arg)
95 {
96     char *var = (char *)arg;
97 
98     return var ? ssl_var_lookup(ctx->p, ctx->s, ctx->c, ctx->r, var) : NULL;
99 }
100 
ssl_expr_lookup(ap_expr_lookup_parms * parms)101 static int ssl_expr_lookup(ap_expr_lookup_parms *parms)
102 {
103     switch (parms->type) {
104     case AP_EXPR_FUNC_VAR:
105         /* for now, we just handle everything that starts with SSL_, but
106          * register our hook as APR_HOOK_LAST
107          * XXX: This can be optimized
108          */
109         if (strcEQn(parms->name, "SSL_", 4)) {
110             *parms->func = expr_var_fn;
111             *parms->data = parms->name + 4;
112             return OK;
113         }
114         break;
115     case AP_EXPR_FUNC_STRING:
116         /* Function SSL() is implemented by us.
117          */
118         if (strcEQ(parms->name, "SSL")) {
119             *parms->func = expr_func_fn;
120             *parms->data = NULL;
121             return OK;
122         }
123         break;
124     case AP_EXPR_FUNC_LIST:
125         if (strcEQ(parms->name, "PeerExtList")) {
126             *parms->func = expr_peer_ext_list_fn;
127             *parms->data = "PeerExtList";
128             return OK;
129         }
130         break;
131     }
132     return DECLINED;
133 }
134 
135 
ssl_var_register(apr_pool_t * p)136 void ssl_var_register(apr_pool_t *p)
137 {
138     char *cp, *cp2;
139 
140     ap_hook_ssl_conn_is_ssl(ssl_conn_is_ssl, NULL, NULL, APR_HOOK_MIDDLE);
141     APR_REGISTER_OPTIONAL_FN(ssl_var_lookup);
142     APR_REGISTER_OPTIONAL_FN(ssl_ext_list);
143 
144     /* Perform once-per-process library version determination: */
145     var_library = apr_pstrdup(p, MODSSL_LIBRARY_DYNTEXT);
146 
147     if ((cp = strchr(var_library, ' ')) != NULL) {
148         *cp = '/';
149         if ((cp2 = strchr(cp, ' ')) != NULL)
150             *cp2 = NUL;
151     }
152 
153     if ((cp = strchr(var_library_interface, ' ')) != NULL) {
154         *cp = '/';
155         if ((cp2 = strchr(cp, ' ')) != NULL)
156             *cp2 = NUL;
157     }
158 
159     ap_hook_expr_lookup(ssl_expr_lookup, NULL, NULL, APR_HOOK_MIDDLE);
160 }
161 
162 /* This function must remain safe to use for a non-SSL connection. */
ssl_var_lookup(apr_pool_t * p,server_rec * s,conn_rec * c,request_rec * r,char * var)163 char *ssl_var_lookup(apr_pool_t *p, server_rec *s, conn_rec *c, request_rec *r, char *var)
164 {
165     SSLModConfigRec *mc = myModConfig(s);
166     const char *result;
167     BOOL resdup;
168     apr_time_exp_t tm;
169 
170     result = NULL;
171     resdup = TRUE;
172 
173     /*
174      * When no pool is given try to find one
175      */
176     if (p == NULL) {
177         if (r != NULL)
178             p = r->pool;
179         else if (c != NULL)
180             p = c->pool;
181         else
182             p = mc->pPool;
183     }
184 
185     /*
186      * Request dependent stuff
187      */
188     if (r != NULL) {
189         switch (var[0]) {
190         case 'H':
191         case 'h':
192             if (strcEQ(var, "HTTP_USER_AGENT"))
193                 result = apr_table_get(r->headers_in, "User-Agent");
194             else if (strcEQ(var, "HTTP_REFERER"))
195                 result = apr_table_get(r->headers_in, "Referer");
196             else if (strcEQ(var, "HTTP_COOKIE"))
197                 result = apr_table_get(r->headers_in, "Cookie");
198             else if (strcEQ(var, "HTTP_FORWARDED"))
199                 result = apr_table_get(r->headers_in, "Forwarded");
200             else if (strcEQ(var, "HTTP_HOST"))
201                 result = apr_table_get(r->headers_in, "Host");
202             else if (strcEQ(var, "HTTP_PROXY_CONNECTION"))
203                 result = apr_table_get(r->headers_in, "Proxy-Connection");
204             else if (strcEQ(var, "HTTP_ACCEPT"))
205                 result = apr_table_get(r->headers_in, "Accept");
206             else if (strlen(var) > 5 && strcEQn(var, "HTTP:", 5))
207                 /* all other headers from which we are still not know about */
208                 result = apr_table_get(r->headers_in, var+5);
209             break;
210 
211         case 'R':
212         case 'r':
213             if (strcEQ(var, "REQUEST_METHOD"))
214                 result = r->method;
215             else if (strcEQ(var, "REQUEST_SCHEME"))
216                 result = ap_http_scheme(r);
217             else if (strcEQ(var, "REQUEST_URI"))
218                 result = r->uri;
219             else if (strcEQ(var, "REQUEST_FILENAME"))
220                 result = r->filename;
221             else if (strcEQ(var, "REMOTE_ADDR"))
222                 result = r->useragent_ip;
223             else if (strcEQ(var, "REMOTE_HOST"))
224                 result = ap_get_useragent_host(r, REMOTE_NAME, NULL);
225             else if (strcEQ(var, "REMOTE_IDENT"))
226                 result = ap_get_remote_logname(r);
227             else if (strcEQ(var, "REMOTE_USER"))
228                 result = r->user;
229             break;
230 
231         case 'S':
232         case 's':
233             if (strcEQn(var, "SSL", 3)) break; /* shortcut common case */
234 
235             if (strcEQ(var, "SERVER_ADMIN"))
236                 result = r->server->server_admin;
237             else if (strcEQ(var, "SERVER_NAME"))
238                 result = ap_get_server_name_for_url(r);
239             else if (strcEQ(var, "SERVER_PORT"))
240                 result = apr_psprintf(p, "%u", ap_get_server_port(r));
241             else if (strcEQ(var, "SERVER_PROTOCOL"))
242                 result = r->protocol;
243             else if (strcEQ(var, "SCRIPT_FILENAME"))
244                 result = r->filename;
245             break;
246 
247         default:
248             if (strcEQ(var, "PATH_INFO"))
249                 result = r->path_info;
250             else if (strcEQ(var, "QUERY_STRING"))
251                 result = r->args;
252             else if (strcEQ(var, "IS_SUBREQ"))
253                 result = (r->main != NULL ? "true" : "false");
254             else if (strcEQ(var, "DOCUMENT_ROOT"))
255                 result = ap_document_root(r);
256             else if (strcEQ(var, "AUTH_TYPE"))
257                 result = r->ap_auth_type;
258             else if (strcEQ(var, "THE_REQUEST"))
259                 result = r->the_request;
260             else if (strlen(var) > 4 && strcEQn(var, "ENV:", 4)) {
261                 result = apr_table_get(r->notes, var+4);
262                 if (result == NULL)
263                     result = apr_table_get(r->subprocess_env, var+4);
264             }
265             break;
266         }
267     }
268 
269     /*
270      * Connection stuff
271      */
272     if (result == NULL && c != NULL) {
273         SSLConnRec *sslconn = ssl_get_effective_config(c);
274         if (strlen(var) > 4 && strcEQn(var, "SSL_", 4)
275             && sslconn && sslconn->ssl)
276             result = ssl_var_lookup_ssl(p, sslconn, r, var+4);
277         else if (strcEQ(var, "HTTPS")) {
278             if (sslconn && sslconn->ssl)
279                 result = "on";
280             else
281                 result = "off";
282         }
283     }
284 
285     /*
286      * Totally independent stuff
287      */
288     if (result == NULL) {
289         if (strlen(var) > 12 && strcEQn(var, "SSL_VERSION_", 12))
290             result = ssl_var_lookup_ssl_version(p, var+12);
291         else if (strcEQ(var, "SERVER_SOFTWARE"))
292             result = ap_get_server_banner();
293         else if (strcEQ(var, "API_VERSION")) {
294             result = apr_itoa(p, MODULE_MAGIC_NUMBER_MAJOR);
295             resdup = FALSE;
296         }
297         else if (strcEQ(var, "TIME_YEAR")) {
298             apr_time_exp_lt(&tm, apr_time_now());
299             result = apr_psprintf(p, "%02d%02d",
300                                  (tm.tm_year / 100) + 19, tm.tm_year % 100);
301             resdup = FALSE;
302         }
303 #define MKTIMESTR(format, tmfield) \
304             apr_time_exp_lt(&tm, apr_time_now()); \
305             result = apr_psprintf(p, format, tm.tmfield); \
306             resdup = FALSE;
307         else if (strcEQ(var, "TIME_MON")) {
308             MKTIMESTR("%02d", tm_mon+1)
309         }
310         else if (strcEQ(var, "TIME_DAY")) {
311             MKTIMESTR("%02d", tm_mday)
312         }
313         else if (strcEQ(var, "TIME_HOUR")) {
314             MKTIMESTR("%02d", tm_hour)
315         }
316         else if (strcEQ(var, "TIME_MIN")) {
317             MKTIMESTR("%02d", tm_min)
318         }
319         else if (strcEQ(var, "TIME_SEC")) {
320             MKTIMESTR("%02d", tm_sec)
321         }
322         else if (strcEQ(var, "TIME_WDAY")) {
323             MKTIMESTR("%d", tm_wday)
324         }
325         else if (strcEQ(var, "TIME")) {
326             apr_time_exp_lt(&tm, apr_time_now());
327             result = apr_psprintf(p,
328                         "%02d%02d%02d%02d%02d%02d%02d", (tm.tm_year / 100) + 19,
329                         (tm.tm_year % 100), tm.tm_mon+1, tm.tm_mday,
330                         tm.tm_hour, tm.tm_min, tm.tm_sec);
331             resdup = FALSE;
332         }
333         /* all other env-variables from the parent Apache process */
334         else if (strlen(var) > 4 && strcEQn(var, "ENV:", 4)) {
335             result = getenv(var+4);
336         }
337     }
338 
339     if (result != NULL && resdup)
340         result = apr_pstrdup(p, result);
341     if (result == NULL)
342         result = "";
343     return (char *)result;
344 }
345 
ssl_var_lookup_ssl(apr_pool_t * p,SSLConnRec * sslconn,request_rec * r,char * var)346 static char *ssl_var_lookup_ssl(apr_pool_t *p, SSLConnRec *sslconn,
347                                 request_rec *r, char *var)
348 {
349     char *result;
350     X509 *xs;
351     STACK_OF(X509) *sk;
352     SSL *ssl;
353 
354     result = NULL;
355 
356     ssl = sslconn->ssl;
357     if (strlen(var) > 8 && strcEQn(var, "VERSION_", 8)) {
358         result = ssl_var_lookup_ssl_version(p, var+8);
359     }
360     else if (ssl != NULL && strcEQ(var, "PROTOCOL")) {
361         result = (char *)SSL_get_version(ssl);
362     }
363     else if (ssl != NULL && strcEQ(var, "SESSION_ID")) {
364         char buf[MODSSL_SESSION_ID_STRING_LEN];
365         SSL_SESSION *pSession = SSL_get_session(ssl);
366         if (pSession) {
367             IDCONST unsigned char *id;
368             unsigned int idlen;
369 
370 #ifdef OPENSSL_NO_SSL_INTERN
371             id = (unsigned char *)SSL_SESSION_get_id(pSession, &idlen);
372 #else
373             id = pSession->session_id;
374             idlen = pSession->session_id_length;
375 #endif
376 
377             result = apr_pstrdup(p, modssl_SSL_SESSION_id2sz(id, idlen,
378                                                              buf, sizeof(buf)));
379         }
380     }
381     else if(ssl != NULL && strcEQ(var, "SESSION_RESUMED")) {
382         if (SSL_session_reused(ssl) == 1)
383             result = "Resumed";
384         else
385             result = "Initial";
386     }
387     else if (ssl != NULL && strlen(var) >= 6 && strcEQn(var, "CIPHER", 6)) {
388         result = ssl_var_lookup_ssl_cipher(p, sslconn, var+6);
389     }
390     else if (ssl != NULL && strlen(var) > 18 && strcEQn(var, "CLIENT_CERT_CHAIN_", 18)) {
391         sk = SSL_get_peer_cert_chain(ssl);
392         result = ssl_var_lookup_ssl_cert_chain(p, sk, var+18);
393     }
394     else if (ssl != NULL && strcEQ(var, "CLIENT_CERT_RFC4523_CEA")) {
395         result = ssl_var_lookup_ssl_cert_rfc4523_cea(p, ssl);
396     }
397     else if (ssl != NULL && strcEQ(var, "CLIENT_VERIFY")) {
398         result = ssl_var_lookup_ssl_cert_verify(p, sslconn);
399     }
400     else if (ssl != NULL && strlen(var) > 7 && strcEQn(var, "CLIENT_", 7)) {
401         if ((xs = SSL_get_peer_certificate(ssl)) != NULL) {
402             result = ssl_var_lookup_ssl_cert(p, r, xs, var+7);
403             X509_free(xs);
404         }
405     }
406     else if (ssl != NULL && strlen(var) > 7 && strcEQn(var, "SERVER_", 7)) {
407         if ((xs = SSL_get_certificate(ssl)) != NULL) {
408             result = ssl_var_lookup_ssl_cert(p, r, xs, var+7);
409             /* SSL_get_certificate is different from SSL_get_peer_certificate.
410              * No need to X509_free(xs).
411              */
412         }
413     }
414     else if (ssl != NULL && strcEQ(var, "COMPRESS_METHOD")) {
415         result = ssl_var_lookup_ssl_compress_meth(ssl);
416     }
417 #ifdef HAVE_TLSEXT
418     else if (ssl != NULL && strcEQ(var, "TLS_SNI")) {
419         result = apr_pstrdup(p, SSL_get_servername(ssl,
420                                                    TLSEXT_NAMETYPE_host_name));
421     }
422 #endif
423     else if (ssl != NULL && strcEQ(var, "SECURE_RENEG")) {
424         int flag = 0;
425 #ifdef SSL_get_secure_renegotiation_support
426         flag = SSL_get_secure_renegotiation_support(ssl);
427 #endif
428         result = apr_pstrdup(p, flag ? "true" : "false");
429     }
430 #ifdef HAVE_SRP
431     else if (ssl != NULL && strcEQ(var, "SRP_USER")) {
432         if ((result = SSL_get_srp_username(ssl)) != NULL) {
433             result = apr_pstrdup(p, result);
434         }
435     }
436     else if (ssl != NULL && strcEQ(var, "SRP_USERINFO")) {
437         if ((result = SSL_get_srp_userinfo(ssl)) != NULL) {
438             result = apr_pstrdup(p, result);
439         }
440     }
441 #endif
442 
443     return result;
444 }
445 
ssl_var_lookup_ssl_cert_dn_oneline(apr_pool_t * p,request_rec * r,X509_NAME * xsname)446 static char *ssl_var_lookup_ssl_cert_dn_oneline(apr_pool_t *p, request_rec *r,
447                                                 X509_NAME *xsname)
448 {
449     char *result = NULL;
450     SSLDirConfigRec *dc;
451     int legacy_format = 0;
452     if (r) {
453         dc = myDirConfig(r);
454         legacy_format = dc->nOptions & SSL_OPT_LEGACYDNFORMAT;
455     }
456     if (legacy_format) {
457         char *cp = X509_NAME_oneline(xsname, NULL, 0);
458         result = apr_pstrdup(p, cp);
459         OPENSSL_free(cp);
460     }
461     else {
462         BIO* bio;
463         unsigned long flags = XN_FLAG_RFC2253 & ~ASN1_STRFLGS_ESC_MSB;
464 
465         if ((bio = BIO_new(BIO_s_mem())) == NULL)
466             return NULL;
467         X509_NAME_print_ex(bio, xsname, 0, flags);
468 
469         result = modssl_bio_free_read(p, bio);
470     }
471     return result;
472 }
473 
ssl_var_lookup_ssl_cert(apr_pool_t * p,request_rec * r,X509 * xs,char * var)474 static char *ssl_var_lookup_ssl_cert(apr_pool_t *p, request_rec *r, X509 *xs,
475                                      char *var)
476 {
477     char *result;
478     BOOL resdup;
479     X509_NAME *xsname;
480     int nid;
481 
482     result = NULL;
483     resdup = TRUE;
484 
485     if (strcEQ(var, "M_VERSION")) {
486         result = apr_psprintf(p, "%lu", X509_get_version(xs)+1);
487         resdup = FALSE;
488     }
489     else if (strcEQ(var, "M_SERIAL")) {
490         result = ssl_var_lookup_ssl_cert_serial(p, xs);
491     }
492     else if (strcEQ(var, "V_START")) {
493         result = ssl_var_lookup_ssl_cert_valid(p, X509_get_notBefore(xs));
494     }
495     else if (strcEQ(var, "V_END")) {
496         result = ssl_var_lookup_ssl_cert_valid(p, X509_get_notAfter(xs));
497     }
498     else if (strcEQ(var, "V_REMAIN")) {
499         result = ssl_var_lookup_ssl_cert_remain(p, X509_get_notAfter(xs));
500         resdup = FALSE;
501     }
502     else if (*var && strcEQ(var+1, "_DN")) {
503         if (*var == 'S')
504             xsname = X509_get_subject_name(xs);
505         else if (*var == 'I')
506             xsname = X509_get_issuer_name(xs);
507         else
508             return NULL;
509         result = ssl_var_lookup_ssl_cert_dn_oneline(p, r, xsname);
510         resdup = FALSE;
511     }
512     else if (strlen(var) > 5 && strcEQn(var+1, "_DN_", 4)) {
513         if (*var == 'S')
514             xsname = X509_get_subject_name(xs);
515         else if (*var == 'I')
516             xsname = X509_get_issuer_name(xs);
517         else
518             return NULL;
519         result = ssl_var_lookup_ssl_cert_dn(p, xsname, var+5);
520         resdup = FALSE;
521     }
522     else if (strlen(var) > 4 && strcEQn(var, "SAN_", 4)) {
523         result = ssl_var_lookup_ssl_cert_san(p, xs, var+4);
524         resdup = FALSE;
525     }
526     else if (strcEQ(var, "A_SIG")) {
527 #if MODSSL_USE_OPENSSL_PRE_1_1_API
528         nid = OBJ_obj2nid((ASN1_OBJECT *)(xs->cert_info->signature->algorithm));
529 #else
530         const ASN1_OBJECT *paobj;
531         X509_ALGOR_get0(&paobj, NULL, NULL, X509_get0_tbs_sigalg(xs));
532         nid = OBJ_obj2nid(paobj);
533 #endif
534         result = apr_pstrdup(p,
535                              (nid == NID_undef) ? "UNKNOWN" : OBJ_nid2ln(nid));
536         resdup = FALSE;
537     }
538     else if (strcEQ(var, "A_KEY")) {
539 #if OPENSSL_VERSION_NUMBER < 0x10100000L
540         nid = OBJ_obj2nid((ASN1_OBJECT *)(xs->cert_info->key->algor->algorithm));
541 #else
542         ASN1_OBJECT *paobj;
543         X509_PUBKEY_get0_param(&paobj, NULL, 0, NULL, X509_get_X509_PUBKEY(xs));
544         nid = OBJ_obj2nid(paobj);
545 #endif
546         result = apr_pstrdup(p,
547                              (nid == NID_undef) ? "UNKNOWN" : OBJ_nid2ln(nid));
548         resdup = FALSE;
549     }
550     else if (strcEQ(var, "CERT")) {
551         result = ssl_var_lookup_ssl_cert_PEM(p, xs);
552     }
553 
554     if (resdup)
555         result = apr_pstrdup(p, result);
556     return result;
557 }
558 
559 /* In this table, .extract is non-zero if RDNs using the NID should be
560  * extracted to for the SSL_{CLIENT,SERVER}_{I,S}_DN_* environment
561  * variables. */
562 static const struct {
563     char *name;
564     int   nid;
565     int   extract;
566 } ssl_var_lookup_ssl_cert_dn_rec[] = {
567     { "C",     NID_countryName,            1 },
568     { "ST",    NID_stateOrProvinceName,    1 }, /* officially    (RFC2156) */
569     { "SP",    NID_stateOrProvinceName,    0 }, /* compatibility (SSLeay)  */
570     { "L",     NID_localityName,           1 },
571     { "O",     NID_organizationName,       1 },
572     { "OU",    NID_organizationalUnitName, 1 },
573     { "CN",    NID_commonName,             1 },
574     { "T",     NID_title,                  1 },
575     { "I",     NID_initials,               1 },
576     { "G",     NID_givenName,              1 },
577     { "S",     NID_surname,                1 },
578     { "D",     NID_description,            1 },
579 #ifdef NID_userId
580     { "UID",   NID_userId,                 1 },
581 #endif
582     { "Email", NID_pkcs9_emailAddress,     1 },
583     { NULL,    0,                          0 }
584 };
585 
ssl_var_lookup_ssl_cert_dn(apr_pool_t * p,X509_NAME * xsname,const char * var)586 static char *ssl_var_lookup_ssl_cert_dn(apr_pool_t *p, X509_NAME *xsname,
587                                         const char *var)
588 {
589     const char *ptr;
590     char *result;
591     X509_NAME_ENTRY *xsne;
592     int i, j, n, idx = 0, raw = 0;
593     apr_size_t varlen;
594 
595     ptr = ap_strrchr_c(var, '_');
596     if (ptr && ptr > var && strcmp(ptr + 1, "RAW") == 0) {
597         var = apr_pstrmemdup(p, var, ptr - var);
598         raw = 1;
599     }
600 
601     /* if an _N suffix is used, find the Nth attribute of given name */
602     ptr = ap_strchr_c(var, '_');
603     if (ptr != NULL && strspn(ptr + 1, "0123456789") == strlen(ptr + 1)) {
604         idx = atoi(ptr + 1);
605         varlen = ptr - var;
606     } else {
607         varlen = strlen(var);
608     }
609 
610     result = NULL;
611 
612     for (i = 0; ssl_var_lookup_ssl_cert_dn_rec[i].name != NULL; i++) {
613         if (strEQn(var, ssl_var_lookup_ssl_cert_dn_rec[i].name, varlen)
614             && strlen(ssl_var_lookup_ssl_cert_dn_rec[i].name) == varlen) {
615             for (j = 0; j < X509_NAME_entry_count(xsname); j++) {
616                 xsne = X509_NAME_get_entry(xsname, j);
617 
618                 n =OBJ_obj2nid((ASN1_OBJECT *)X509_NAME_ENTRY_get_object(xsne));
619 
620                 if (n == ssl_var_lookup_ssl_cert_dn_rec[i].nid && idx-- == 0) {
621                     result = modssl_X509_NAME_ENTRY_to_string(p, xsne, raw);
622                     break;
623                 }
624             }
625             break;
626         }
627     }
628     return result;
629 }
630 
ssl_var_lookup_ssl_cert_san(apr_pool_t * p,X509 * xs,char * var)631 static char *ssl_var_lookup_ssl_cert_san(apr_pool_t *p, X509 *xs, char *var)
632 {
633     int type;
634     apr_size_t numlen;
635     const char *onf = NULL;
636     apr_array_header_t *entries;
637 
638     if (strcEQn(var, "Email_", 6)) {
639         type = GEN_EMAIL;
640         var += 6;
641     }
642     else if (strcEQn(var, "DNS_", 4)) {
643         type = GEN_DNS;
644         var += 4;
645     }
646     else if (strcEQn(var, "OTHER_", 6)) {
647         type = GEN_OTHERNAME;
648         var += 6;
649         if (strEQn(var, "msUPN_", 6)) {
650             var += 6;
651             onf = "msUPN";
652         }
653         else if (strEQn(var, "dnsSRV_", 7)) {
654             var += 7;
655             onf = "id-on-dnsSRV";
656         }
657         else
658            return NULL;
659     }
660     else
661         return NULL;
662 
663     /* sanity check: number must be between 1 and 4 digits */
664     numlen = strspn(var, "0123456789");
665     if ((numlen < 1) || (numlen > 4) || (numlen != strlen(var)))
666         return NULL;
667 
668     if (modssl_X509_getSAN(p, xs, type, onf, atoi(var), &entries))
669         /* return the first entry from this 1-element array */
670         return APR_ARRAY_IDX(entries, 0, char *);
671     else
672         return NULL;
673 }
674 
ssl_var_lookup_ssl_cert_valid(apr_pool_t * p,ASN1_TIME * tm)675 static char *ssl_var_lookup_ssl_cert_valid(apr_pool_t *p, ASN1_TIME *tm)
676 {
677     BIO* bio;
678 
679     if ((bio = BIO_new(BIO_s_mem())) == NULL)
680         return NULL;
681     ASN1_TIME_print(bio, tm);
682 
683     return modssl_bio_free_read(p, bio);
684 }
685 
686 #define DIGIT2NUM(x) (((x)[0] - '0') * 10 + (x)[1] - '0')
687 
688 /* Return a string giving the number of days remaining until 'tm', or
689  * "0" if this can't be determined. */
ssl_var_lookup_ssl_cert_remain(apr_pool_t * p,ASN1_TIME * tm)690 static char *ssl_var_lookup_ssl_cert_remain(apr_pool_t *p, ASN1_TIME *tm)
691 {
692     apr_time_t then, now = apr_time_now();
693     apr_time_exp_t exp = {0};
694     long diff;
695     unsigned char *dp;
696 
697     /* Fail if the time isn't a valid ASN.1 TIME; RFC3280 mandates
698      * that the seconds digits are present even though ASN.1
699      * doesn't. */
700     if ((tm->type == V_ASN1_UTCTIME && tm->length < 11) ||
701         (tm->type == V_ASN1_GENERALIZEDTIME && tm->length < 13) ||
702         !ASN1_TIME_check(tm)) {
703         return apr_pstrdup(p, "0");
704     }
705 
706     if (tm->type == V_ASN1_UTCTIME) {
707         exp.tm_year = DIGIT2NUM(tm->data);
708         if (exp.tm_year <= 50) exp.tm_year += 100;
709         dp = tm->data + 2;
710     } else {
711         exp.tm_year = DIGIT2NUM(tm->data) * 100 + DIGIT2NUM(tm->data + 2) - 1900;
712         dp = tm->data + 4;
713     }
714 
715     exp.tm_mon = DIGIT2NUM(dp) - 1;
716     exp.tm_mday = DIGIT2NUM(dp + 2) + 1;
717     exp.tm_hour = DIGIT2NUM(dp + 4);
718     exp.tm_min = DIGIT2NUM(dp + 6);
719     exp.tm_sec = DIGIT2NUM(dp + 8);
720 
721     if (apr_time_exp_gmt_get(&then, &exp) != APR_SUCCESS) {
722         return apr_pstrdup(p, "0");
723     }
724 
725     diff = (long)((apr_time_sec(then) - apr_time_sec(now)) / (60*60*24));
726 
727     return diff > 0 ? apr_ltoa(p, diff) : apr_pstrdup(p, "0");
728 }
729 
ssl_var_lookup_ssl_cert_serial(apr_pool_t * p,X509 * xs)730 static char *ssl_var_lookup_ssl_cert_serial(apr_pool_t *p, X509 *xs)
731 {
732     BIO *bio;
733 
734     if ((bio = BIO_new(BIO_s_mem())) == NULL)
735         return NULL;
736     i2a_ASN1_INTEGER(bio, X509_get_serialNumber(xs));
737 
738     return modssl_bio_free_read(p, bio);
739 }
740 
ssl_var_lookup_ssl_cert_chain(apr_pool_t * p,STACK_OF (X509)* sk,char * var)741 static char *ssl_var_lookup_ssl_cert_chain(apr_pool_t *p, STACK_OF(X509) *sk, char *var)
742 {
743     char *result;
744     X509 *xs;
745     int n;
746 
747     result = NULL;
748 
749     if (strspn(var, "0123456789") == strlen(var)) {
750         n = atoi(var);
751         if (n < sk_X509_num(sk)) {
752             xs = sk_X509_value(sk, n);
753             result = ssl_var_lookup_ssl_cert_PEM(p, xs);
754         }
755     }
756 
757     return result;
758 }
759 
ssl_var_lookup_ssl_cert_rfc4523_cea(apr_pool_t * p,SSL * ssl)760 static char *ssl_var_lookup_ssl_cert_rfc4523_cea(apr_pool_t *p, SSL *ssl)
761 {
762     char *result;
763     X509 *xs;
764 
765     ASN1_INTEGER *serialNumber;
766 
767     if (!(xs = SSL_get_peer_certificate(ssl))) {
768         return NULL;
769     }
770 
771     result = NULL;
772 
773     serialNumber = X509_get_serialNumber(xs);
774     if (serialNumber) {
775         X509_NAME *issuer = X509_get_issuer_name(xs);
776         if (issuer) {
777             BIGNUM *bn = ASN1_INTEGER_to_BN(serialNumber, NULL);
778             char *decimal = BN_bn2dec(bn);
779             result = apr_pstrcat(p, "{ serialNumber ", decimal,
780                     ", issuer rdnSequence:\"",
781                     modssl_X509_NAME_to_string(p, issuer, 0), "\" }", NULL);
782             OPENSSL_free(decimal);
783             BN_free(bn);
784         }
785     }
786 
787     X509_free(xs);
788     return result;
789 }
790 
ssl_var_lookup_ssl_cert_PEM(apr_pool_t * p,X509 * xs)791 static char *ssl_var_lookup_ssl_cert_PEM(apr_pool_t *p, X509 *xs)
792 {
793     BIO *bio;
794 
795     if ((bio = BIO_new(BIO_s_mem())) == NULL)
796         return NULL;
797     PEM_write_bio_X509(bio, xs);
798 
799     return modssl_bio_free_read(p, bio);
800 }
801 
ssl_var_lookup_ssl_cert_verify(apr_pool_t * p,SSLConnRec * sslconn)802 static char *ssl_var_lookup_ssl_cert_verify(apr_pool_t *p, SSLConnRec *sslconn)
803 {
804     char *result;
805     long vrc;
806     const char *verr;
807     const char *vinfo;
808     SSL *ssl;
809     X509 *xs;
810 
811     result = NULL;
812     ssl   = sslconn->ssl;
813     verr  = sslconn->verify_error;
814     vinfo = sslconn->verify_info;
815     vrc   = SSL_get_verify_result(ssl);
816     xs    = SSL_get_peer_certificate(ssl);
817 
818     if (vrc == X509_V_OK && verr == NULL && xs == NULL)
819         /* no client verification done at all */
820         result = "NONE";
821     else if (vrc == X509_V_OK && verr == NULL && vinfo == NULL && xs != NULL)
822         /* client verification done successful */
823         result = "SUCCESS";
824     else if (vrc == X509_V_OK && vinfo != NULL && strEQ(vinfo, "GENEROUS"))
825         /* client verification done in generous way */
826         result = "GENEROUS";
827     else
828         /* client verification failed */
829         result = apr_psprintf(p, "FAILED:%s",
830                               verr ? verr : X509_verify_cert_error_string(vrc));
831 
832     if (xs)
833         X509_free(xs);
834     return result;
835 }
836 
ssl_var_lookup_ssl_cipher(apr_pool_t * p,SSLConnRec * sslconn,char * var)837 static char *ssl_var_lookup_ssl_cipher(apr_pool_t *p, SSLConnRec *sslconn, char *var)
838 {
839     char *result;
840     BOOL resdup;
841     int usekeysize, algkeysize;
842     SSL *ssl;
843 
844     result = NULL;
845     resdup = TRUE;
846 
847     ssl = sslconn->ssl;
848     ssl_var_lookup_ssl_cipher_bits(ssl, &usekeysize, &algkeysize);
849 
850     if (ssl && strEQ(var, "")) {
851         MODSSL_SSL_CIPHER_CONST SSL_CIPHER *cipher = SSL_get_current_cipher(ssl);
852         result = (cipher != NULL ? (char *)SSL_CIPHER_get_name(cipher) : NULL);
853     }
854     else if (strcEQ(var, "_EXPORT"))
855         result = (usekeysize < 56 ? "true" : "false");
856     else if (strcEQ(var, "_USEKEYSIZE")) {
857         result = apr_itoa(p, usekeysize);
858         resdup = FALSE;
859     }
860     else if (strcEQ(var, "_ALGKEYSIZE")) {
861         result = apr_itoa(p, algkeysize);
862         resdup = FALSE;
863     }
864 
865     if (result != NULL && resdup)
866         result = apr_pstrdup(p, result);
867     return result;
868 }
869 
ssl_var_lookup_ssl_cipher_bits(SSL * ssl,int * usekeysize,int * algkeysize)870 static void ssl_var_lookup_ssl_cipher_bits(SSL *ssl, int *usekeysize, int *algkeysize)
871 {
872     MODSSL_SSL_CIPHER_CONST SSL_CIPHER *cipher;
873 
874     *usekeysize = 0;
875     *algkeysize = 0;
876     if (ssl != NULL)
877         if ((cipher = SSL_get_current_cipher(ssl)) != NULL)
878             *usekeysize = SSL_CIPHER_get_bits(cipher, algkeysize);
879     return;
880 }
881 
ssl_var_lookup_ssl_version(apr_pool_t * p,char * var)882 static char *ssl_var_lookup_ssl_version(apr_pool_t *p, char *var)
883 {
884     if (strEQ(var, "INTERFACE")) {
885         return apr_pstrdup(p, var_interface);
886     }
887     else if (strEQ(var, "LIBRARY_INTERFACE")) {
888         return apr_pstrdup(p, var_library_interface);
889     }
890     else if (strEQ(var, "LIBRARY")) {
891         return apr_pstrdup(p, var_library);
892     }
893     return NULL;
894 }
895 
896 /* Add each RDN in 'xn' to the table 't' where the NID is present in
897  * 'nids', using key prefix 'pfx'.  */
extract_dn(apr_table_t * t,apr_hash_t * nids,const char * pfx,X509_NAME * xn,apr_pool_t * p)898 static void extract_dn(apr_table_t *t, apr_hash_t *nids, const char *pfx,
899                        X509_NAME *xn, apr_pool_t *p)
900 {
901     X509_NAME_ENTRY *xsne;
902     apr_hash_t *count;
903     int i, nid;
904 
905     /* Hash of (int) NID -> (int *) counter to count each time an RDN
906      * with the given NID has been seen. */
907     count = apr_hash_make(p);
908 
909     /* For each RDN... */
910     for (i = 0; i < X509_NAME_entry_count(xn); i++) {
911          const char *tag;
912          xsne = X509_NAME_get_entry(xn, i);
913 
914          /* Retrieve the nid, and check whether this is one of the nids
915           * which are to be extracted. */
916          nid = OBJ_obj2nid((ASN1_OBJECT *)X509_NAME_ENTRY_get_object(xsne));
917 
918          tag = apr_hash_get(nids, &nid, sizeof nid);
919          if (tag) {
920              const char *key;
921              int *dup;
922              char *value;
923 
924              /* Check whether a variable with this nid was already
925               * been used; if so, use the foo_N=bar syntax. */
926              dup = apr_hash_get(count, &nid, sizeof nid);
927              if (dup) {
928                  key = apr_psprintf(p, "%s%s_%d", pfx, tag, ++(*dup));
929              }
930              else {
931                  /* Otherwise, use the plain foo=bar syntax. */
932                  dup = apr_pcalloc(p, sizeof *dup);
933                  apr_hash_set(count, &nid, sizeof nid, dup);
934                  key = apr_pstrcat(p, pfx, tag, NULL);
935              }
936              value = modssl_X509_NAME_ENTRY_to_string(p, xsne, 0);
937              apr_table_setn(t, key, value);
938          }
939     }
940 }
941 
modssl_var_extract_dns(apr_table_t * t,SSL * ssl,apr_pool_t * p)942 void modssl_var_extract_dns(apr_table_t *t, SSL *ssl, apr_pool_t *p)
943 {
944     apr_hash_t *nids;
945     unsigned n;
946     X509 *xs;
947 
948     /* Build up a hash table of (int *)NID->(char *)short-name for all
949      * the tags which are to be extracted: */
950     nids = apr_hash_make(p);
951     for (n = 0; ssl_var_lookup_ssl_cert_dn_rec[n].name; n++) {
952         if (ssl_var_lookup_ssl_cert_dn_rec[n].extract) {
953             apr_hash_set(nids, &ssl_var_lookup_ssl_cert_dn_rec[n].nid,
954                          sizeof(ssl_var_lookup_ssl_cert_dn_rec[0].nid),
955                          ssl_var_lookup_ssl_cert_dn_rec[n].name);
956         }
957     }
958 
959     /* Extract the server cert DNS -- note that the refcount does NOT
960      * increase: */
961     xs = SSL_get_certificate(ssl);
962     if (xs) {
963         extract_dn(t, nids, "SSL_SERVER_S_DN_", X509_get_subject_name(xs), p);
964         extract_dn(t, nids, "SSL_SERVER_I_DN_", X509_get_issuer_name(xs), p);
965     }
966 
967     /* Extract the client cert DNs -- note that the refcount DOES
968      * increase: */
969     xs = SSL_get_peer_certificate(ssl);
970     if (xs) {
971         extract_dn(t, nids, "SSL_CLIENT_S_DN_", X509_get_subject_name(xs), p);
972         extract_dn(t, nids, "SSL_CLIENT_I_DN_", X509_get_issuer_name(xs), p);
973         X509_free(xs);
974     }
975 }
976 
extract_san_array(apr_table_t * t,const char * pfx,apr_array_header_t * entries,apr_pool_t * p)977 static void extract_san_array(apr_table_t *t, const char *pfx,
978                               apr_array_header_t *entries, apr_pool_t *p)
979 {
980     int i;
981 
982     for (i = 0; i < entries->nelts; i++) {
983         const char *key = apr_psprintf(p, "%s_%d", pfx, i);
984         apr_table_setn(t, key, APR_ARRAY_IDX(entries, i, const char *));
985     }
986 }
987 
modssl_var_extract_san_entries(apr_table_t * t,SSL * ssl,apr_pool_t * p)988 void modssl_var_extract_san_entries(apr_table_t *t, SSL *ssl, apr_pool_t *p)
989 {
990     X509 *xs;
991     apr_array_header_t *entries;
992 
993     /* subjectAltName entries of the server certificate */
994     xs = SSL_get_certificate(ssl);
995     if (xs) {
996         if (modssl_X509_getSAN(p, xs, GEN_EMAIL, NULL, -1, &entries)) {
997             extract_san_array(t, "SSL_SERVER_SAN_Email", entries, p);
998         }
999         if (modssl_X509_getSAN(p, xs, GEN_DNS, NULL, -1, &entries)) {
1000             extract_san_array(t, "SSL_SERVER_SAN_DNS", entries, p);
1001         }
1002         if (modssl_X509_getSAN(p, xs, GEN_OTHERNAME, "id-on-dnsSRV", -1,
1003                                &entries)) {
1004             extract_san_array(t, "SSL_SERVER_SAN_OTHER_dnsSRV", entries, p);
1005         }
1006         /* no need to free xs (refcount does not increase) */
1007     }
1008 
1009     /* subjectAltName entries of the client certificate */
1010     xs = SSL_get_peer_certificate(ssl);
1011     if (xs) {
1012         if (modssl_X509_getSAN(p, xs, GEN_EMAIL, NULL, -1, &entries)) {
1013             extract_san_array(t, "SSL_CLIENT_SAN_Email", entries, p);
1014         }
1015         if (modssl_X509_getSAN(p, xs, GEN_DNS, NULL, -1, &entries)) {
1016             extract_san_array(t, "SSL_CLIENT_SAN_DNS", entries, p);
1017         }
1018         if (modssl_X509_getSAN(p, xs, GEN_OTHERNAME, "msUPN", -1, &entries)) {
1019             extract_san_array(t, "SSL_CLIENT_SAN_OTHER_msUPN", entries, p);
1020         }
1021         X509_free(xs);
1022     }
1023 }
1024 
1025 /* For an extension type which OpenSSL does not recognize, attempt to
1026  * parse the extension type as a primitive string.  This will fail for
1027  * any structured extension type per the docs.  Returns non-zero on
1028  * success and writes the string to the given bio. */
dump_extn_value(BIO * bio,ASN1_OCTET_STRING * str)1029 static int dump_extn_value(BIO *bio, ASN1_OCTET_STRING *str)
1030 {
1031     const unsigned char *pp = str->data;
1032     ASN1_STRING *ret = ASN1_STRING_new();
1033     int rv = 0;
1034 
1035     /* This allows UTF8String, IA5String, VisibleString, or BMPString;
1036      * conversion to UTF-8 is forced. */
1037     if (d2i_DISPLAYTEXT(&ret, &pp, str->length)) {
1038         ASN1_STRING_print_ex(bio, ret, ASN1_STRFLGS_UTF8_CONVERT);
1039         rv = 1;
1040     }
1041 
1042     ASN1_STRING_free(ret);
1043     return rv;
1044 }
1045 
ssl_ext_list(apr_pool_t * p,conn_rec * c,int peer,const char * extension)1046 apr_array_header_t *ssl_ext_list(apr_pool_t *p, conn_rec *c, int peer,
1047                                  const char *extension)
1048 {
1049     SSLConnRec *sslconn = ssl_get_effective_config(c);
1050     SSL *ssl = NULL;
1051     apr_array_header_t *array = NULL;
1052     X509 *xs = NULL;
1053     ASN1_OBJECT *oid = NULL;
1054     int count = 0, j;
1055 
1056     if (!sslconn || !sslconn->ssl || !extension) {
1057         return NULL;
1058     }
1059     ssl = sslconn->ssl;
1060 
1061     /* We accept the "extension" string to be converted as
1062      * a long name (nsComment), short name (DN) or
1063      * numeric OID (1.2.3.4).
1064      */
1065     oid = OBJ_txt2obj(extension, 0);
1066     if (!oid) {
1067         ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, c, APLOGNO(01970)
1068                       "could not parse OID '%s'", extension);
1069         ERR_clear_error();
1070         return NULL;
1071     }
1072 
1073     xs = peer ? SSL_get_peer_certificate(ssl) : SSL_get_certificate(ssl);
1074     if (xs == NULL) {
1075         return NULL;
1076     }
1077 
1078     count = X509_get_ext_count(xs);
1079     /* Create an array large enough to accommodate every extension. This is
1080      * likely overkill, but safe.
1081      */
1082     array = apr_array_make(p, count, sizeof(char *));
1083     for (j = 0; j < count; j++) {
1084         X509_EXTENSION *ext = X509_get_ext(xs, j);
1085 
1086         if (OBJ_cmp(X509_EXTENSION_get_object(ext), oid) == 0) {
1087             BIO *bio = BIO_new(BIO_s_mem());
1088 
1089             /* We want to obtain a string representation of the extensions
1090              * value and add it to the array we're building.
1091              * X509V3_EXT_print() doesn't know about all the possible
1092              * data types, but the value is stored as an ASN1_OCTET_STRING
1093              * allowing us a fallback in case of X509V3_EXT_print
1094              * not knowing how to handle the data.
1095              */
1096             if (X509V3_EXT_print(bio, ext, 0, 0) == 1 ||
1097                 dump_extn_value(bio, X509_EXTENSION_get_data(ext)) == 1) {
1098                 BUF_MEM *buf;
1099                 char **ptr = apr_array_push(array);
1100                 BIO_get_mem_ptr(bio, &buf);
1101                 *ptr = apr_pstrmemdup(p, buf->data, buf->length);
1102             } else {
1103                 ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, c, APLOGNO(01971)
1104                               "Found an extension '%s', but failed to "
1105                               "create a string from it", extension);
1106             }
1107             BIO_vfree(bio);
1108         }
1109     }
1110 
1111     if (array->nelts == 0)
1112         array = NULL;
1113 
1114     if (peer) {
1115         /* only SSL_get_peer_certificate raises the refcount */
1116         X509_free(xs);
1117     }
1118 
1119     ASN1_OBJECT_free(oid);
1120     ERR_clear_error();
1121     return array;
1122 }
1123 
ssl_var_lookup_ssl_compress_meth(SSL * ssl)1124 static char *ssl_var_lookup_ssl_compress_meth(SSL *ssl)
1125 {
1126     char *result = "NULL";
1127 #ifndef OPENSSL_NO_COMP
1128     SSL_SESSION *pSession = SSL_get_session(ssl);
1129 
1130     if (pSession) {
1131 #ifdef OPENSSL_NO_SSL_INTERN
1132         switch (SSL_SESSION_get_compress_id(pSession)) {
1133 #else
1134         switch (pSession->compress_meth) {
1135 #endif
1136         case 0:
1137             /* default "NULL" already set */
1138             break;
1139 
1140             /* Defined by RFC 3749, deflate is coded by "1" */
1141         case 1:
1142             result = "DEFLATE";
1143             break;
1144 
1145             /* IANA assigned compression number for LZS */
1146         case 0x40:
1147             result = "LZS";
1148             break;
1149 
1150         default:
1151             result = "UNKNOWN";
1152             break;
1153         }
1154     }
1155 #endif
1156     return result;
1157 }
1158 
1159 /*  _________________________________________________________________
1160 **
1161 **  SSL Extension to mod_log_config
1162 **  _________________________________________________________________
1163 */
1164 
1165 #include "../../modules/loggers/mod_log_config.h"
1166 
1167 static const char *ssl_var_log_handler_c(request_rec *r, char *a);
1168 static const char *ssl_var_log_handler_x(request_rec *r, char *a);
1169 
1170 /*
1171  * register us for the mod_log_config function registering phase
1172  * to establish %{...}c and to be able to expand %{...}x variables.
1173  */
1174 void ssl_var_log_config_register(apr_pool_t *p)
1175 {
1176     APR_OPTIONAL_FN_TYPE(ap_register_log_handler) *log_pfn_register;
1177 
1178     log_pfn_register = APR_RETRIEVE_OPTIONAL_FN(ap_register_log_handler);
1179 
1180     if (log_pfn_register) {
1181         log_pfn_register(p, "c", ssl_var_log_handler_c, 0);
1182         log_pfn_register(p, "x", ssl_var_log_handler_x, 0);
1183     }
1184     return;
1185 }
1186 
1187 /*
1188  * implement the %{..}c log function
1189  * (we are the only function)
1190  */
1191 static const char *ssl_var_log_handler_c(request_rec *r, char *a)
1192 {
1193     SSLConnRec *sslconn = ssl_get_effective_config(r->connection);
1194     char *result;
1195 
1196     if (sslconn == NULL || sslconn->ssl == NULL)
1197         return NULL;
1198     result = NULL;
1199     if (strEQ(a, "version"))
1200         result = ssl_var_lookup(r->pool, r->server, r->connection, r, "SSL_PROTOCOL");
1201     else if (strEQ(a, "cipher"))
1202         result = ssl_var_lookup(r->pool, r->server, r->connection, r, "SSL_CIPHER");
1203     else if (strEQ(a, "subjectdn") || strEQ(a, "clientcert"))
1204         result = ssl_var_lookup(r->pool, r->server, r->connection, r, "SSL_CLIENT_S_DN");
1205     else if (strEQ(a, "issuerdn") || strEQ(a, "cacert"))
1206         result = ssl_var_lookup(r->pool, r->server, r->connection, r, "SSL_CLIENT_I_DN");
1207     else if (strEQ(a, "errcode"))
1208         result = "-";
1209     else if (strEQ(a, "errstr"))
1210         result = (char *)sslconn->verify_error;
1211     if (result != NULL && result[0] == NUL)
1212         result = NULL;
1213     return result;
1214 }
1215 
1216 /*
1217  * extend the implementation of the %{..}x log function
1218  * (there can be more functions)
1219  */
1220 static const char *ssl_var_log_handler_x(request_rec *r, char *a)
1221 {
1222     char *result;
1223 
1224     result = ssl_var_lookup(r->pool, r->server, r->connection, r, a);
1225     if (result != NULL && result[0] == NUL)
1226         result = NULL;
1227     return result;
1228 }
1229 
1230 
1231