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
42static char *ssl_var_lookup_ssl(apr_pool_t *p, conn_rec *c, request_rec *r, char *var);
43static char *ssl_var_lookup_ssl_cert(apr_pool_t *p, request_rec *r, X509 *xs, char *var);
44static char *ssl_var_lookup_ssl_cert_dn(apr_pool_t *p, X509_NAME *xsname, char *var);
45static char *ssl_var_lookup_ssl_cert_valid(apr_pool_t *p, ASN1_TIME *tm);
46static char *ssl_var_lookup_ssl_cert_remain(apr_pool_t *p, ASN1_TIME *tm);
47static char *ssl_var_lookup_ssl_cert_serial(apr_pool_t *p, X509 *xs);
48static char *ssl_var_lookup_ssl_cert_chain(apr_pool_t *p, STACK_OF(X509) *sk, char *var);
49static char *ssl_var_lookup_ssl_cert_PEM(apr_pool_t *p, X509 *xs);
50static char *ssl_var_lookup_ssl_cert_verify(apr_pool_t *p, conn_rec *c);
51static char *ssl_var_lookup_ssl_cipher(apr_pool_t *p, conn_rec *c, char *var);
52static void  ssl_var_lookup_ssl_cipher_bits(SSL *ssl, int *usekeysize, int *algkeysize);
53static char *ssl_var_lookup_ssl_version(apr_pool_t *p, char *var);
54static char *ssl_var_lookup_ssl_compress_meth(SSL *ssl);
55
56static int ssl_is_https(conn_rec *c)
57{
58    SSLConnRec *sslconn = myConnConfig(c);
59    return sslconn && sslconn->ssl;
60}
61
62static const char var_interface[] = "mod_ssl/" AP_SERVER_BASEREVISION;
63static char var_library_interface[] = SSL_LIBRARY_TEXT;
64static char *var_library = NULL;
65
66static 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
73static 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
79static 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
105void 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. */
132char *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
316static 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
414static 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
447static 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. */
519static 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
543static 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
583static 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. */
604static 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
644static 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
661static 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
680static 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
697static 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
733static 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
767static 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
779static 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'.  */
795static 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
841void 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. */
880static 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
897apr_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
975static 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
1018static const char *ssl_var_log_handler_c(request_rec *r, char *a);
1019static 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 */
1025void 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 */
1042static 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 */
1071static 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
1082