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