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