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
33#include "apr_time.h"
34
35/*  _________________________________________________________________
36**
37**  Variable Lookup
38**  _________________________________________________________________
39*/
40
41static char *ssl_var_lookup_ssl(apr_pool_t *p, conn_rec *c, char *var);
42static char *ssl_var_lookup_ssl_cert(apr_pool_t *p, X509 *xs, char *var);
43static char *ssl_var_lookup_ssl_cert_dn(apr_pool_t *p, X509_NAME *xsname, char *var);
44static char *ssl_var_lookup_ssl_cert_valid(apr_pool_t *p, ASN1_UTCTIME *tm);
45static char *ssl_var_lookup_ssl_cert_remain(apr_pool_t *p, ASN1_UTCTIME *tm);
46static char *ssl_var_lookup_ssl_cert_serial(apr_pool_t *p, X509 *xs);
47static char *ssl_var_lookup_ssl_cert_chain(apr_pool_t *p, STACK_OF(X509) *sk, char *var);
48static char *ssl_var_lookup_ssl_cert_PEM(apr_pool_t *p, X509 *xs);
49static char *ssl_var_lookup_ssl_cert_verify(apr_pool_t *p, conn_rec *c);
50static char *ssl_var_lookup_ssl_cipher(apr_pool_t *p, conn_rec *c, char *var);
51static void  ssl_var_lookup_ssl_cipher_bits(SSL *ssl, int *usekeysize, int *algkeysize);
52static char *ssl_var_lookup_ssl_version(apr_pool_t *p, char *var);
53static char *ssl_var_lookup_ssl_compress_meth(SSL *ssl);
54
55static int ssl_is_https(conn_rec *c)
56{
57    SSLConnRec *sslconn = myConnConfig(c);
58    return sslconn && sslconn->ssl;
59}
60
61static const char var_interface[] = "mod_ssl/" MOD_SSL_VERSION;
62static char var_library_interface[] = SSL_LIBRARY_TEXT;
63static char *var_library = NULL;
64
65void ssl_var_register(apr_pool_t *p)
66{
67    char *cp, *cp2;
68
69    APR_REGISTER_OPTIONAL_FN(ssl_is_https);
70    APR_REGISTER_OPTIONAL_FN(ssl_var_lookup);
71    APR_REGISTER_OPTIONAL_FN(ssl_ext_lookup);
72
73    /* Perform once-per-process library version determination: */
74    var_library = apr_pstrdup(p, SSL_LIBRARY_DYNTEXT);
75
76    if ((cp = strchr(var_library, ' ')) != NULL) {
77        *cp = '/';
78        if ((cp2 = strchr(cp, ' ')) != NULL)
79            *cp2 = NUL;
80    }
81
82    if ((cp = strchr(var_library_interface, ' ')) != NULL) {
83        *cp = '/';
84        if ((cp2 = strchr(cp, ' ')) != NULL)
85            *cp2 = NUL;
86    }
87}
88
89/* This function must remain safe to use for a non-SSL connection. */
90char *ssl_var_lookup(apr_pool_t *p, server_rec *s, conn_rec *c, request_rec *r, char *var)
91{
92    SSLModConfigRec *mc = myModConfig(s);
93    const char *result;
94    BOOL resdup;
95    apr_time_exp_t tm;
96
97    result = NULL;
98    resdup = TRUE;
99
100    /*
101     * When no pool is given try to find one
102     */
103    if (p == NULL) {
104        if (r != NULL)
105            p = r->pool;
106        else if (c != NULL)
107            p = c->pool;
108        else
109            p = mc->pPool;
110    }
111
112    /*
113     * Request dependent stuff
114     */
115    if (r != NULL) {
116        switch (var[0]) {
117        case 'H':
118        case 'h':
119            if (strcEQ(var, "HTTP_USER_AGENT"))
120                result = apr_table_get(r->headers_in, "User-Agent");
121            else if (strcEQ(var, "HTTP_REFERER"))
122                result = apr_table_get(r->headers_in, "Referer");
123            else if (strcEQ(var, "HTTP_COOKIE"))
124                result = apr_table_get(r->headers_in, "Cookie");
125            else if (strcEQ(var, "HTTP_FORWARDED"))
126                result = apr_table_get(r->headers_in, "Forwarded");
127            else if (strcEQ(var, "HTTP_HOST"))
128                result = apr_table_get(r->headers_in, "Host");
129            else if (strcEQ(var, "HTTP_PROXY_CONNECTION"))
130                result = apr_table_get(r->headers_in, "Proxy-Connection");
131            else if (strcEQ(var, "HTTP_ACCEPT"))
132                result = apr_table_get(r->headers_in, "Accept");
133            else if (strlen(var) > 5 && strcEQn(var, "HTTP:", 5))
134                /* all other headers from which we are still not know about */
135                result = apr_table_get(r->headers_in, var+5);
136            break;
137
138        case 'R':
139        case 'r':
140            if (strcEQ(var, "REQUEST_METHOD"))
141                result = r->method;
142            else if (strcEQ(var, "REQUEST_SCHEME"))
143                result = ap_http_scheme(r);
144            else if (strcEQ(var, "REQUEST_URI"))
145                result = r->uri;
146            else if (strcEQ(var, "REQUEST_FILENAME"))
147                result = r->filename;
148            else if (strcEQ(var, "REMOTE_HOST"))
149                result = ap_get_remote_host(r->connection, r->per_dir_config,
150                                            REMOTE_NAME, NULL);
151            else if (strcEQ(var, "REMOTE_IDENT"))
152                result = ap_get_remote_logname(r);
153            else if (strcEQ(var, "REMOTE_USER"))
154                result = r->user;
155            break;
156
157        case 'S':
158        case 's':
159            if (strcEQn(var, "SSL", 3)) break; /* shortcut common case */
160
161            if (strcEQ(var, "SERVER_ADMIN"))
162                result = r->server->server_admin;
163            else if (strcEQ(var, "SERVER_NAME"))
164                result = ap_get_server_name(r);
165            else if (strcEQ(var, "SERVER_PORT"))
166                result = apr_psprintf(p, "%u", ap_get_server_port(r));
167            else if (strcEQ(var, "SERVER_PROTOCOL"))
168                result = r->protocol;
169            else if (strcEQ(var, "SCRIPT_FILENAME"))
170                result = r->filename;
171            break;
172
173        default:
174            if (strcEQ(var, "PATH_INFO"))
175                result = r->path_info;
176            else if (strcEQ(var, "QUERY_STRING"))
177                result = r->args;
178            else if (strcEQ(var, "IS_SUBREQ"))
179                result = (r->main != NULL ? "true" : "false");
180            else if (strcEQ(var, "DOCUMENT_ROOT"))
181                result = ap_document_root(r);
182            else if (strcEQ(var, "AUTH_TYPE"))
183                result = r->ap_auth_type;
184            else if (strcEQ(var, "THE_REQUEST"))
185                result = r->the_request;
186            break;
187        }
188    }
189
190    /*
191     * Connection stuff
192     */
193    if (result == NULL && c != NULL) {
194        SSLConnRec *sslconn = myConnConfig(c);
195        if (strlen(var) > 4 && strcEQn(var, "SSL_", 4)
196            && sslconn && sslconn->ssl)
197            result = ssl_var_lookup_ssl(p, c, var+4);
198        else if (strcEQ(var, "REMOTE_ADDR"))
199            result = c->remote_ip;
200        else if (strcEQ(var, "HTTPS")) {
201            if (sslconn && sslconn->ssl)
202                result = "on";
203            else
204                result = "off";
205        }
206    }
207
208    /*
209     * Totally independent stuff
210     */
211    if (result == NULL) {
212        if (strlen(var) > 12 && strcEQn(var, "SSL_VERSION_", 12))
213            result = ssl_var_lookup_ssl_version(p, var+12);
214        else if (strcEQ(var, "SERVER_SOFTWARE"))
215            result = ap_get_server_banner();
216        else if (strcEQ(var, "API_VERSION")) {
217            result = apr_itoa(p, MODULE_MAGIC_NUMBER);
218            resdup = FALSE;
219        }
220        else if (strcEQ(var, "TIME_YEAR")) {
221            apr_time_exp_lt(&tm, apr_time_now());
222            result = apr_psprintf(p, "%02d%02d",
223                                 (tm.tm_year / 100) + 19, tm.tm_year % 100);
224            resdup = FALSE;
225        }
226#define MKTIMESTR(format, tmfield) \
227            apr_time_exp_lt(&tm, apr_time_now()); \
228            result = apr_psprintf(p, format, tm.tmfield); \
229            resdup = FALSE;
230        else if (strcEQ(var, "TIME_MON")) {
231            MKTIMESTR("%02d", tm_mon+1)
232        }
233        else if (strcEQ(var, "TIME_DAY")) {
234            MKTIMESTR("%02d", tm_mday)
235        }
236        else if (strcEQ(var, "TIME_HOUR")) {
237            MKTIMESTR("%02d", tm_hour)
238        }
239        else if (strcEQ(var, "TIME_MIN")) {
240            MKTIMESTR("%02d", tm_min)
241        }
242        else if (strcEQ(var, "TIME_SEC")) {
243            MKTIMESTR("%02d", tm_sec)
244        }
245        else if (strcEQ(var, "TIME_WDAY")) {
246            MKTIMESTR("%d", tm_wday)
247        }
248        else if (strcEQ(var, "TIME")) {
249            apr_time_exp_lt(&tm, apr_time_now());
250            result = apr_psprintf(p,
251                        "%02d%02d%02d%02d%02d%02d%02d", (tm.tm_year / 100) + 19,
252                        (tm.tm_year % 100), tm.tm_mon+1, tm.tm_mday,
253                        tm.tm_hour, tm.tm_min, tm.tm_sec);
254            resdup = FALSE;
255        }
256        /* all other env-variables from the parent Apache process */
257        else if (strlen(var) > 4 && strcEQn(var, "ENV:", 4)) {
258            result = apr_table_get(r->notes, var+4);
259            if (result == NULL)
260                result = apr_table_get(r->subprocess_env, var+4);
261            if (result == NULL)
262                result = getenv(var+4);
263        }
264    }
265
266    if (result != NULL && resdup)
267        result = apr_pstrdup(p, result);
268    if (result == NULL)
269        result = "";
270    return (char *)result;
271}
272
273static char *ssl_var_lookup_ssl(apr_pool_t *p, conn_rec *c, char *var)
274{
275    SSLConnRec *sslconn = myConnConfig(c);
276    char *result;
277    X509 *xs;
278    STACK_OF(X509) *sk;
279    SSL *ssl;
280
281    result = NULL;
282
283    ssl = sslconn->ssl;
284    if (strlen(var) > 8 && strcEQn(var, "VERSION_", 8)) {
285        result = ssl_var_lookup_ssl_version(p, var+8);
286    }
287    else if (ssl != NULL && strcEQ(var, "PROTOCOL")) {
288        result = (char *)SSL_get_version(ssl);
289    }
290    else if (ssl != NULL && strcEQ(var, "SESSION_ID")) {
291        char buf[SSL_SESSION_ID_STRING_LEN];
292        SSL_SESSION *pSession = SSL_get_session(ssl);
293        if (pSession) {
294            result = apr_pstrdup(p, SSL_SESSION_id2sz(
295                                     SSL_SESSION_get_session_id(pSession),
296                                     SSL_SESSION_get_session_id_length(pSession),
297                                     buf, sizeof(buf)));
298        }
299    }
300    else if (ssl != NULL && strlen(var) >= 6 && strcEQn(var, "CIPHER", 6)) {
301        result = ssl_var_lookup_ssl_cipher(p, c, var+6);
302    }
303    else if (ssl != NULL && strlen(var) > 18 && strcEQn(var, "CLIENT_CERT_CHAIN_", 18)) {
304        sk = SSL_get_peer_cert_chain(ssl);
305        result = ssl_var_lookup_ssl_cert_chain(p, sk, var+18);
306    }
307    else if (ssl != NULL && strcEQ(var, "CLIENT_VERIFY")) {
308        result = ssl_var_lookup_ssl_cert_verify(p, c);
309    }
310    else if (ssl != NULL && strlen(var) > 7 && strcEQn(var, "CLIENT_", 7)) {
311        if ((xs = SSL_get_peer_certificate(ssl)) != NULL) {
312            result = ssl_var_lookup_ssl_cert(p, xs, var+7);
313            X509_free(xs);
314        }
315    }
316    else if (ssl != NULL && strlen(var) > 7 && strcEQn(var, "SERVER_", 7)) {
317        if ((xs = SSL_get_certificate(ssl)) != NULL)
318            result = ssl_var_lookup_ssl_cert(p, xs, var+7);
319    }
320    else if (ssl != NULL && strcEQ(var, "COMPRESS_METHOD")) {
321        result = ssl_var_lookup_ssl_compress_meth(ssl);
322    }
323#ifndef OPENSSL_NO_TLSEXT
324    else if (ssl != NULL && strcEQ(var, "TLS_SNI")) {
325        result = apr_pstrdup(p, SSL_get_servername(ssl,
326                                                   TLSEXT_NAMETYPE_host_name));
327    }
328#endif
329    else if (ssl != NULL && strcEQ(var, "SECURE_RENEG")) {
330        int flag = 0;
331#ifdef SSL_get_secure_renegotiation_support
332        flag = SSL_get_secure_renegotiation_support(ssl);
333#endif
334        result = apr_pstrdup(p, flag ? "true" : "false");
335    }
336
337    return result;
338}
339
340static char *ssl_var_lookup_ssl_cert(apr_pool_t *p, X509 *xs, char *var)
341{
342    char *result;
343    BOOL resdup;
344    X509_NAME *xsname;
345    int nid;
346    char *cp;
347
348    result = NULL;
349    resdup = TRUE;
350
351    if (strcEQ(var, "M_VERSION")) {
352        result = apr_psprintf(p, "%lu", X509_get_version(xs)+1);
353        resdup = FALSE;
354    }
355    else if (strcEQ(var, "M_SERIAL")) {
356        result = ssl_var_lookup_ssl_cert_serial(p, xs);
357    }
358    else if (strcEQ(var, "V_START")) {
359        result = ssl_var_lookup_ssl_cert_valid(p, X509_get_notBefore(xs));
360    }
361    else if (strcEQ(var, "V_END")) {
362        result = ssl_var_lookup_ssl_cert_valid(p, X509_get_notAfter(xs));
363    }
364    else if (strcEQ(var, "V_REMAIN")) {
365        result = ssl_var_lookup_ssl_cert_remain(p, X509_get_notAfter(xs));
366        resdup = FALSE;
367    }
368    else if (strcEQ(var, "S_DN")) {
369        xsname = X509_get_subject_name(xs);
370        cp = X509_NAME_oneline(xsname, NULL, 0);
371        result = apr_pstrdup(p, cp);
372        modssl_free(cp);
373        resdup = FALSE;
374    }
375    else if (strlen(var) > 5 && strcEQn(var, "S_DN_", 5)) {
376        xsname = X509_get_subject_name(xs);
377        result = ssl_var_lookup_ssl_cert_dn(p, xsname, var+5);
378        resdup = FALSE;
379    }
380    else if (strcEQ(var, "I_DN")) {
381        xsname = X509_get_issuer_name(xs);
382        cp = X509_NAME_oneline(xsname, NULL, 0);
383        result = apr_pstrdup(p, cp);
384        modssl_free(cp);
385        resdup = FALSE;
386    }
387    else if (strlen(var) > 5 && strcEQn(var, "I_DN_", 5)) {
388        xsname = X509_get_issuer_name(xs);
389        result = ssl_var_lookup_ssl_cert_dn(p, xsname, var+5);
390        resdup = FALSE;
391    }
392    else if (strcEQ(var, "A_SIG")) {
393        nid = OBJ_obj2nid((ASN1_OBJECT *)X509_get_signature_algorithm(xs));
394        result = apr_pstrdup(p,
395                             (nid == NID_undef) ? "UNKNOWN" : OBJ_nid2ln(nid));
396        resdup = FALSE;
397    }
398    else if (strcEQ(var, "A_KEY")) {
399        nid = OBJ_obj2nid((ASN1_OBJECT *)X509_get_key_algorithm(xs));
400        result = apr_pstrdup(p,
401                             (nid == NID_undef) ? "UNKNOWN" : OBJ_nid2ln(nid));
402        resdup = FALSE;
403    }
404    else if (strcEQ(var, "CERT")) {
405        result = ssl_var_lookup_ssl_cert_PEM(p, xs);
406    }
407
408    if (result != NULL && resdup)
409        result = apr_pstrdup(p, result);
410    return result;
411}
412
413/* In this table, .extract is non-zero if RDNs using the NID should be
414 * extracted to for the SSL_{CLIENT,SERVER}_{I,S}_DN_* environment
415 * variables. */
416static const struct {
417    char *name;
418    int   nid;
419    int   extract;
420} ssl_var_lookup_ssl_cert_dn_rec[] = {
421    { "C",     NID_countryName,            1 },
422    { "ST",    NID_stateOrProvinceName,    1 }, /* officially    (RFC2156) */
423    { "SP",    NID_stateOrProvinceName,    0 }, /* compatibility (SSLeay)  */
424    { "L",     NID_localityName,           1 },
425    { "O",     NID_organizationName,       1 },
426    { "OU",    NID_organizationalUnitName, 1 },
427    { "CN",    NID_commonName,             1 },
428    { "T",     NID_title,                  1 },
429    { "I",     NID_initials,               1 },
430    { "G",     NID_givenName,              1 },
431    { "S",     NID_surname,                1 },
432    { "D",     NID_description,            1 },
433#ifdef NID_userId
434    { "UID",   NID_userId,                 1 },
435#endif
436    { "Email", NID_pkcs9_emailAddress,     1 },
437    { NULL,    0,                          0 }
438};
439
440static char *ssl_var_lookup_ssl_cert_dn(apr_pool_t *p, X509_NAME *xsname, char *var)
441{
442    char *result, *ptr;
443    X509_NAME_ENTRY *xsne;
444    int i, j, n, idx = 0;
445    apr_size_t varlen;
446
447    /* if an _N suffix is used, find the Nth attribute of given name */
448    ptr = strchr(var, '_');
449    if (ptr != NULL && strspn(ptr + 1, "0123456789") == strlen(ptr + 1)) {
450        idx = atoi(ptr + 1);
451        varlen = ptr - var;
452    } else {
453        varlen = strlen(var);
454    }
455
456    result = NULL;
457
458    for (i = 0; ssl_var_lookup_ssl_cert_dn_rec[i].name != NULL; i++) {
459        if (strEQn(var, ssl_var_lookup_ssl_cert_dn_rec[i].name, varlen)
460            && strlen(ssl_var_lookup_ssl_cert_dn_rec[i].name) == varlen) {
461            for (j = 0; j < sk_X509_NAME_ENTRY_num((STACK_OF(X509_NAME_ENTRY) *)
462                                                 X509_NAME_get_entries(xsname));
463                 j++) {
464                xsne = sk_X509_NAME_ENTRY_value((STACK_OF(X509_NAME_ENTRY) *)
465                                             X509_NAME_get_entries(xsname), j);
466
467                n =OBJ_obj2nid((ASN1_OBJECT *)X509_NAME_ENTRY_get_object(xsne));
468
469                if (n == ssl_var_lookup_ssl_cert_dn_rec[i].nid && idx-- == 0) {
470                    unsigned char *data = X509_NAME_ENTRY_get_data_ptr(xsne);
471                    /* cast needed from unsigned char to char */
472                    result = apr_pstrmemdup(p, (char *)data,
473                                            X509_NAME_ENTRY_get_data_len(xsne));
474#if APR_CHARSET_EBCDIC
475                    ap_xlate_proto_from_ascii(result, X509_NAME_ENTRY_get_data_len(xsne));
476#endif /* APR_CHARSET_EBCDIC */
477                    break;
478                }
479            }
480            break;
481        }
482    }
483    return result;
484}
485
486static char *ssl_var_lookup_ssl_cert_valid(apr_pool_t *p, ASN1_UTCTIME *tm)
487{
488    char *result;
489    BIO* bio;
490    int n;
491
492    if ((bio = BIO_new(BIO_s_mem())) == NULL)
493        return NULL;
494    ASN1_UTCTIME_print(bio, tm);
495    n = BIO_pending(bio);
496    result = apr_pcalloc(p, n+1);
497    n = BIO_read(bio, result, n);
498    result[n] = NUL;
499    BIO_free(bio);
500    return result;
501}
502
503#define DIGIT2NUM(x) (((x)[0] - '0') * 10 + (x)[1] - '0')
504
505/* Return a string giving the number of days remaining until 'tm', or
506 * "0" if this can't be determined. */
507static char *ssl_var_lookup_ssl_cert_remain(apr_pool_t *p, ASN1_UTCTIME *tm)
508{
509    apr_time_t then, now = apr_time_now();
510    apr_time_exp_t exp = {0};
511    long diff;
512
513    /* Fail if the time isn't a valid ASN.1 UTCTIME; RFC3280 mandates
514     * that the seconds digits are present even though ASN.1
515     * doesn't. */
516    if (tm->length < 11 || !ASN1_UTCTIME_check(tm)) {
517        return apr_pstrdup(p, "0");
518    }
519
520    exp.tm_year = DIGIT2NUM(tm->data);
521    exp.tm_mon = DIGIT2NUM(tm->data + 2) - 1;
522    exp.tm_mday = DIGIT2NUM(tm->data + 4) + 1;
523    exp.tm_hour = DIGIT2NUM(tm->data + 6);
524    exp.tm_min = DIGIT2NUM(tm->data + 8);
525    exp.tm_sec = DIGIT2NUM(tm->data + 10);
526
527    if (exp.tm_year <= 50) exp.tm_year += 100;
528
529    if (apr_time_exp_gmt_get(&then, &exp) != APR_SUCCESS) {
530        return apr_pstrdup(p, "0");
531    }
532
533    diff = (long)((apr_time_sec(then) - apr_time_sec(now)) / (60*60*24));
534
535    return diff > 0 ? apr_ltoa(p, diff) : apr_pstrdup(p, "0");
536}
537
538static char *ssl_var_lookup_ssl_cert_serial(apr_pool_t *p, X509 *xs)
539{
540    char *result;
541    BIO *bio;
542    int n;
543
544    if ((bio = BIO_new(BIO_s_mem())) == NULL)
545        return NULL;
546    i2a_ASN1_INTEGER(bio, X509_get_serialNumber(xs));
547    n = BIO_pending(bio);
548    result = apr_pcalloc(p, n+1);
549    n = BIO_read(bio, result, n);
550    result[n] = NUL;
551    BIO_free(bio);
552    return result;
553}
554
555static char *ssl_var_lookup_ssl_cert_chain(apr_pool_t *p, STACK_OF(X509) *sk, char *var)
556{
557    char *result;
558    X509 *xs;
559    int n;
560
561    result = NULL;
562
563    if (strspn(var, "0123456789") == strlen(var)) {
564        n = atoi(var);
565        if (n < sk_X509_num(sk)) {
566            xs = sk_X509_value(sk, n);
567            result = ssl_var_lookup_ssl_cert_PEM(p, xs);
568        }
569    }
570
571    return result;
572}
573
574static char *ssl_var_lookup_ssl_cert_PEM(apr_pool_t *p, X509 *xs)
575{
576    char *result;
577    BIO *bio;
578    int n;
579
580    if ((bio = BIO_new(BIO_s_mem())) == NULL)
581        return NULL;
582    PEM_write_bio_X509(bio, xs);
583    n = BIO_pending(bio);
584    result = apr_pcalloc(p, n+1);
585    n = BIO_read(bio, result, n);
586    result[n] = NUL;
587    BIO_free(bio);
588    return result;
589}
590
591static char *ssl_var_lookup_ssl_cert_verify(apr_pool_t *p, conn_rec *c)
592{
593    SSLConnRec *sslconn = myConnConfig(c);
594    char *result;
595    long vrc;
596    const char *verr;
597    const char *vinfo;
598    SSL *ssl;
599    X509 *xs;
600
601    result = NULL;
602    ssl   = sslconn->ssl;
603    verr  = sslconn->verify_error;
604    vinfo = sslconn->verify_info;
605    vrc   = SSL_get_verify_result(ssl);
606    xs    = SSL_get_peer_certificate(ssl);
607
608    if (vrc == X509_V_OK && verr == NULL && xs == NULL)
609        /* no client verification done at all */
610        result = "NONE";
611    else if (vrc == X509_V_OK && verr == NULL && vinfo == NULL && xs != NULL)
612        /* client verification done successful */
613        result = "SUCCESS";
614    else if (vrc == X509_V_OK && vinfo != NULL && strEQ(vinfo, "GENEROUS"))
615        /* client verification done in generous way */
616        result = "GENEROUS";
617    else
618        /* client verification failed */
619        result = apr_psprintf(p, "FAILED:%s", verr);
620
621    if (xs)
622        X509_free(xs);
623    return result;
624}
625
626static char *ssl_var_lookup_ssl_cipher(apr_pool_t *p, conn_rec *c, char *var)
627{
628    SSLConnRec *sslconn = myConnConfig(c);
629    char *result;
630    BOOL resdup;
631    int usekeysize, algkeysize;
632    SSL *ssl;
633
634    result = NULL;
635    resdup = TRUE;
636
637    ssl = sslconn->ssl;
638    ssl_var_lookup_ssl_cipher_bits(ssl, &usekeysize, &algkeysize);
639
640    if (ssl && strEQ(var, "")) {
641        MODSSL_SSL_CIPHER_CONST SSL_CIPHER *cipher = SSL_get_current_cipher(ssl);
642        result = (cipher != NULL ? (char *)SSL_CIPHER_get_name(cipher) : NULL);
643    }
644    else if (strcEQ(var, "_EXPORT"))
645        result = (usekeysize < 56 ? "true" : "false");
646    else if (strcEQ(var, "_USEKEYSIZE")) {
647        result = apr_itoa(p, usekeysize);
648        resdup = FALSE;
649    }
650    else if (strcEQ(var, "_ALGKEYSIZE")) {
651        result = apr_itoa(p, algkeysize);
652        resdup = FALSE;
653    }
654
655    if (result != NULL && resdup)
656        result = apr_pstrdup(p, result);
657    return result;
658}
659
660static void ssl_var_lookup_ssl_cipher_bits(SSL *ssl, int *usekeysize, int *algkeysize)
661{
662    MODSSL_SSL_CIPHER_CONST SSL_CIPHER *cipher;
663
664    *usekeysize = 0;
665    *algkeysize = 0;
666    if (ssl != NULL)
667        if ((cipher = SSL_get_current_cipher(ssl)) != NULL)
668            *usekeysize = SSL_CIPHER_get_bits(cipher, algkeysize);
669    return;
670}
671
672static char *ssl_var_lookup_ssl_version(apr_pool_t *p, char *var)
673{
674    if (strEQ(var, "INTERFACE")) {
675        return apr_pstrdup(p, var_interface);
676    }
677    else if (strEQ(var, "LIBRARY_INTERFACE")) {
678        return apr_pstrdup(p, var_library_interface);
679    }
680    else if (strEQ(var, "LIBRARY")) {
681        return apr_pstrdup(p, var_library);
682    }
683    return NULL;
684}
685
686/* Add each RDN in 'xn' to the table 't' where the NID is present in
687 * 'nids', using key prefix 'pfx'.  */
688static void extract_dn(apr_table_t *t, apr_hash_t *nids, const char *pfx,
689                       X509_NAME *xn, apr_pool_t *p)
690{
691    STACK_OF(X509_NAME_ENTRY) *ents = X509_NAME_get_entries(xn);
692    X509_NAME_ENTRY *xsne;
693    apr_hash_t *count;
694    int i, nid;
695
696    /* Hash of (int) NID -> (int *) counter to count each time an RDN
697     * with the given NID has been seen. */
698    count = apr_hash_make(p);
699
700    /* For each RDN... */
701    for (i = 0; i < sk_X509_NAME_ENTRY_num(ents); i++) {
702         const char *tag;
703
704         xsne = sk_X509_NAME_ENTRY_value(ents, i);
705
706         /* Retrieve the nid, and check whether this is one of the nids
707          * which are to be extracted. */
708         nid = OBJ_obj2nid((ASN1_OBJECT *)X509_NAME_ENTRY_get_object(xsne));
709
710         tag = apr_hash_get(nids, &nid, sizeof nid);
711         if (tag) {
712             unsigned char *data = X509_NAME_ENTRY_get_data_ptr(xsne);
713             const char *key;
714             int *dup;
715             char *value;
716
717             /* Check whether a variable with this nid was already
718              * been used; if so, use the foo_N=bar syntax. */
719             dup = apr_hash_get(count, &nid, sizeof nid);
720             if (dup) {
721                 key = apr_psprintf(p, "%s%s_%d", pfx, tag, ++(*dup));
722             }
723             else {
724                 /* Otherwise, use the plain foo=bar syntax. */
725                 dup = apr_pcalloc(p, sizeof *dup);
726                 apr_hash_set(count, &nid, sizeof nid, dup);
727                 key = apr_pstrcat(p, pfx, tag, NULL);
728             }
729
730             /* cast needed from 'unsigned char *' to 'char *' */
731             value = apr_pstrmemdup(p, (char *)data,
732                                    X509_NAME_ENTRY_get_data_len(xsne));
733#if APR_CHARSET_EBCDIC
734             ap_xlate_proto_from_ascii(value, X509_NAME_ENTRY_get_data_len(xsne));
735#endif /* APR_CHARSET_EBCDIC */
736             apr_table_setn(t, key, value);
737         }
738    }
739}
740
741void modssl_var_extract_dns(apr_table_t *t, SSL *ssl, apr_pool_t *p)
742{
743    apr_hash_t *nids;
744    unsigned n;
745    X509 *xs;
746
747    /* Build up a hash table of (int *)NID->(char *)short-name for all
748     * the tags which are to be extracted: */
749    nids = apr_hash_make(p);
750    for (n = 0; ssl_var_lookup_ssl_cert_dn_rec[n].name; n++) {
751        if (ssl_var_lookup_ssl_cert_dn_rec[n].extract) {
752            apr_hash_set(nids, &ssl_var_lookup_ssl_cert_dn_rec[n].nid,
753                         sizeof(ssl_var_lookup_ssl_cert_dn_rec[0].nid),
754                         ssl_var_lookup_ssl_cert_dn_rec[n].name);
755        }
756    }
757
758    /* Extract the server cert DNS -- note that the refcount does NOT
759     * increase: */
760    xs = SSL_get_certificate(ssl);
761    if (xs) {
762        extract_dn(t, nids, "SSL_SERVER_S_DN_", X509_get_subject_name(xs), p);
763        extract_dn(t, nids, "SSL_SERVER_I_DN_", X509_get_issuer_name(xs), p);
764    }
765
766    /* Extract the client cert DNs -- note that the refcount DOES
767     * increase: */
768    xs = SSL_get_peer_certificate(ssl);
769    if (xs) {
770        extract_dn(t, nids, "SSL_CLIENT_S_DN_", X509_get_subject_name(xs), p);
771        extract_dn(t, nids, "SSL_CLIENT_I_DN_", X509_get_issuer_name(xs), p);
772        X509_free(xs);
773    }
774}
775
776const char *ssl_ext_lookup(apr_pool_t *p, conn_rec *c, int peer,
777                           const char *oidnum)
778{
779    SSLConnRec *sslconn = myConnConfig(c);
780    SSL *ssl;
781    X509 *xs = NULL;
782    ASN1_OBJECT *oid;
783    int count = 0, j;
784    char *result = NULL;
785
786    if (!sslconn || !sslconn->ssl) {
787        return NULL;
788    }
789    ssl = sslconn->ssl;
790
791    oid = OBJ_txt2obj(oidnum, 1);
792    if (!oid) {
793        ERR_clear_error();
794        return NULL;
795    }
796
797    xs = peer ? SSL_get_peer_certificate(ssl) : SSL_get_certificate(ssl);
798    if (xs == NULL) {
799        return NULL;
800    }
801
802    count = X509_get_ext_count(xs);
803
804    for (j = 0; j < count; j++) {
805        X509_EXTENSION *ext = X509_get_ext(xs, j);
806
807        if (OBJ_cmp(ext->object, oid) == 0) {
808            BIO *bio = BIO_new(BIO_s_mem());
809
810            if (X509V3_EXT_print(bio, ext, 0, 0) == 1) {
811                BUF_MEM *buf;
812
813                BIO_get_mem_ptr(bio, &buf);
814                result = apr_pstrmemdup(p, buf->data, buf->length);
815            }
816
817            BIO_vfree(bio);
818            break;
819        }
820    }
821
822    if (peer) {
823        /* only SSL_get_peer_certificate raises the refcount */
824        X509_free(xs);
825    }
826
827    ERR_clear_error();
828    return result;
829}
830
831static char *ssl_var_lookup_ssl_compress_meth(SSL *ssl)
832{
833    char *result = "NULL";
834#ifdef OPENSSL_VERSION_NUMBER
835#if (OPENSSL_VERSION_NUMBER >= 0x00908000)
836    SSL_SESSION *pSession = SSL_get_session(ssl);
837
838    if (pSession) {
839        switch (pSession->compress_meth) {
840        case 0:
841            /* default "NULL" already set */
842            break;
843
844            /* Defined by RFC 3749, deflate is coded by "1" */
845        case 1:
846            result = "DEFLATE";
847            break;
848
849            /* IANA assigned compression number for LZS */
850        case 0x40:
851            result = "LZS";
852            break;
853
854        default:
855            result = "UNKNOWN";
856            break;
857        }
858    }
859#endif
860#endif
861    return result;
862}
863
864/*  _________________________________________________________________
865**
866**  SSL Extension to mod_log_config
867**  _________________________________________________________________
868*/
869
870#include "../../modules/loggers/mod_log_config.h"
871
872static const char *ssl_var_log_handler_c(request_rec *r, char *a);
873static const char *ssl_var_log_handler_x(request_rec *r, char *a);
874
875/*
876 * register us for the mod_log_config function registering phase
877 * to establish %{...}c and to be able to expand %{...}x variables.
878 */
879void ssl_var_log_config_register(apr_pool_t *p)
880{
881    static APR_OPTIONAL_FN_TYPE(ap_register_log_handler) *log_pfn_register;
882
883    log_pfn_register = APR_RETRIEVE_OPTIONAL_FN(ap_register_log_handler);
884
885    if (log_pfn_register) {
886        log_pfn_register(p, "c", ssl_var_log_handler_c, 0);
887        log_pfn_register(p, "x", ssl_var_log_handler_x, 0);
888    }
889    return;
890}
891
892/*
893 * implement the %{..}c log function
894 * (we are the only function)
895 */
896static const char *ssl_var_log_handler_c(request_rec *r, char *a)
897{
898    SSLConnRec *sslconn = myConnConfig(r->connection);
899    char *result;
900
901    if (sslconn == NULL || sslconn->ssl == NULL)
902        return NULL;
903    result = NULL;
904    if (strEQ(a, "version"))
905        result = ssl_var_lookup(r->pool, r->server, r->connection, r, "SSL_PROTOCOL");
906    else if (strEQ(a, "cipher"))
907        result = ssl_var_lookup(r->pool, r->server, r->connection, r, "SSL_CIPHER");
908    else if (strEQ(a, "subjectdn") || strEQ(a, "clientcert"))
909        result = ssl_var_lookup(r->pool, r->server, r->connection, r, "SSL_CLIENT_S_DN");
910    else if (strEQ(a, "issuerdn") || strEQ(a, "cacert"))
911        result = ssl_var_lookup(r->pool, r->server, r->connection, r, "SSL_CLIENT_I_DN");
912    else if (strEQ(a, "errcode"))
913        result = "-";
914    else if (strEQ(a, "errstr"))
915        result = (char *)sslconn->verify_error;
916    if (result != NULL && result[0] == NUL)
917        result = NULL;
918    return result;
919}
920
921/*
922 * extend the implementation of the %{..}x log function
923 * (there can be more functions)
924 */
925static const char *ssl_var_log_handler_x(request_rec *r, char *a)
926{
927    char *result;
928
929    result = ssl_var_lookup(r->pool, r->server, r->connection, r, a);
930    if (result != NULL && result[0] == NUL)
931        result = NULL;
932    return result;
933}
934
935