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