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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
18 * _ __ ___ ___ __| | ___ ___| | mod_ssl
19 * | '_ ` _ \ / _ \ / _` | / __/ __| | Apache Interface to OpenSSL
20 * | | | | | | (_) | (_| | \__ \__ \ |
21 * |_| |_| |_|\___/ \__,_|___|___/___/_|
24 * Variable Lookup Facility
26 /* ``Those of you who think they
27 know everything are very annoying
28 to those of us who do.''
32 /* _________________________________________________________________
35 ** _________________________________________________________________
38 static char *ssl_var_lookup_header(apr_pool_t *p, request_rec *r, const char *name);
39 static char *ssl_var_lookup_ssl(apr_pool_t *p, conn_rec *c, char *var);
40 static char *ssl_var_lookup_ssl_cert(apr_pool_t *p, X509 *xs, char *var);
41 static char *ssl_var_lookup_ssl_cert_dn(apr_pool_t *p, X509_NAME *xsname, char *var);
42 static char *ssl_var_lookup_ssl_cert_valid(apr_pool_t *p, ASN1_UTCTIME *tm);
43 static char *ssl_var_lookup_ssl_cert_serial(apr_pool_t *p, X509 *xs);
44 static char *ssl_var_lookup_ssl_cert_chain(apr_pool_t *p, STACK_OF(X509) *sk, char *var);
45 static char *ssl_var_lookup_ssl_cert_PEM(apr_pool_t *p, X509 *xs);
46 static char *ssl_var_lookup_ssl_cert_verify(apr_pool_t *p, conn_rec *c);
47 static char *ssl_var_lookup_ssl_cipher(apr_pool_t *p, conn_rec *c, char *var);
48 static void ssl_var_lookup_ssl_cipher_bits(SSL *ssl, int *usekeysize, int *algkeysize);
49 static char *ssl_var_lookup_ssl_version(apr_pool_t *pp, apr_pool_t *p, char *var);
51 static int ssl_is_https(conn_rec *c)
53 SSLConnRec *sslconn = myConnConfig(c);
54 return sslconn && sslconn->ssl;
57 void ssl_var_register(void)
59 APR_REGISTER_OPTIONAL_FN(ssl_is_https);
60 APR_REGISTER_OPTIONAL_FN(ssl_var_lookup);
64 /* This function must remain safe to use for a non-SSL connection. */
65 char *ssl_var_lookup(apr_pool_t *p, server_rec *s, conn_rec *c, request_rec *r, char *var)
67 SSLModConfigRec *mc = myModConfig(s);
76 * When no pool is given try to find one
88 * Request dependent stuff
91 if (strcEQ(var, "HTTP_USER_AGENT"))
92 result = ssl_var_lookup_header(p, r, "User-Agent");
93 else if (strcEQ(var, "HTTP_REFERER"))
94 result = ssl_var_lookup_header(p, r, "Referer");
95 else if (strcEQ(var, "HTTP_COOKIE"))
96 result = ssl_var_lookup_header(p, r, "Cookie");
97 else if (strcEQ(var, "HTTP_FORWARDED"))
98 result = ssl_var_lookup_header(p, r, "Forwarded");
99 else if (strcEQ(var, "HTTP_HOST"))
100 result = ssl_var_lookup_header(p, r, "Host");
101 else if (strcEQ(var, "HTTP_PROXY_CONNECTION"))
102 result = ssl_var_lookup_header(p, r, "Proxy-Connection");
103 else if (strcEQ(var, "HTTP_ACCEPT"))
104 result = ssl_var_lookup_header(p, r, "Accept");
105 else if (strlen(var) > 5 && strcEQn(var, "HTTP:", 5))
106 /* all other headers from which we are still not know about */
107 result = ssl_var_lookup_header(p, r, var+5);
108 else if (strcEQ(var, "THE_REQUEST"))
109 result = r->the_request;
110 else if (strcEQ(var, "REQUEST_METHOD"))
111 result = (char *)(r->method);
112 else if (strcEQ(var, "REQUEST_SCHEME"))
113 result = (char *)ap_http_method(r);
114 else if (strcEQ(var, "REQUEST_URI"))
116 else if (strcEQ(var, "SCRIPT_FILENAME") ||
117 strcEQ(var, "REQUEST_FILENAME"))
118 result = r->filename;
119 else if (strcEQ(var, "PATH_INFO"))
120 result = r->path_info;
121 else if (strcEQ(var, "QUERY_STRING"))
123 else if (strcEQ(var, "REMOTE_HOST"))
124 result = (char *)ap_get_remote_host(r->connection,
125 r->per_dir_config, REMOTE_NAME, NULL);
126 else if (strcEQ(var, "REMOTE_IDENT"))
127 result = (char *)ap_get_remote_logname(r);
128 else if (strcEQ(var, "IS_SUBREQ"))
129 result = (r->main != NULL ? "true" : "false");
130 else if (strcEQ(var, "DOCUMENT_ROOT"))
131 result = (char *)ap_document_root(r);
132 else if (strcEQ(var, "SERVER_ADMIN"))
133 result = r->server->server_admin;
134 else if (strcEQ(var, "SERVER_NAME"))
135 result = (char *)ap_get_server_name(r);
136 else if (strcEQ(var, "SERVER_PORT"))
137 result = apr_psprintf(p, "%u", ap_get_server_port(r));
138 else if (strcEQ(var, "SERVER_PROTOCOL"))
139 result = r->protocol;
145 if (result == NULL && c != NULL) {
146 SSLConnRec *sslconn = myConnConfig(c);
147 if (strcEQ(var, "REMOTE_ADDR"))
148 result = c->remote_ip;
149 else if (strcEQ(var, "REMOTE_USER"))
151 else if (strcEQ(var, "AUTH_TYPE"))
152 result = r->ap_auth_type;
153 else if (strlen(var) > 4 && strcEQn(var, "SSL_", 4)
154 && sslconn && sslconn->ssl)
155 result = ssl_var_lookup_ssl(p, c, var+4);
156 else if (strcEQ(var, "HTTPS")) {
157 if (sslconn && sslconn->ssl)
165 * Totally independent stuff
167 if (result == NULL) {
168 if (strlen(var) > 12 && strcEQn(var, "SSL_VERSION_", 12))
169 result = ssl_var_lookup_ssl_version(s->process->pool, p, var+12);
170 else if (strcEQ(var, "SERVER_SOFTWARE"))
171 result = (char *)ap_get_server_version();
172 else if (strcEQ(var, "API_VERSION")) {
173 result = apr_psprintf(p, "%d", MODULE_MAGIC_NUMBER);
176 else if (strcEQ(var, "TIME_YEAR")) {
177 apr_time_exp_lt(&tm, apr_time_now());
178 result = apr_psprintf(p, "%02d%02d",
179 (tm.tm_year / 100) + 19, tm.tm_year % 100);
182 #define MKTIMESTR(format, tmfield) \
183 apr_time_exp_lt(&tm, apr_time_now()); \
184 result = apr_psprintf(p, format, tm.tmfield); \
186 else if (strcEQ(var, "TIME_MON")) {
187 MKTIMESTR("%02d", tm_mon+1)
189 else if (strcEQ(var, "TIME_DAY")) {
190 MKTIMESTR("%02d", tm_mday)
192 else if (strcEQ(var, "TIME_HOUR")) {
193 MKTIMESTR("%02d", tm_hour)
195 else if (strcEQ(var, "TIME_MIN")) {
196 MKTIMESTR("%02d", tm_min)
198 else if (strcEQ(var, "TIME_SEC")) {
199 MKTIMESTR("%02d", tm_sec)
201 else if (strcEQ(var, "TIME_WDAY")) {
202 MKTIMESTR("%d", tm_wday)
204 else if (strcEQ(var, "TIME")) {
205 apr_time_exp_lt(&tm, apr_time_now());
206 result = apr_psprintf(p,
207 "%02d%02d%02d%02d%02d%02d%02d", (tm.tm_year / 100) + 19,
208 (tm.tm_year % 100), tm.tm_mon+1, tm.tm_mday,
209 tm.tm_hour, tm.tm_min, tm.tm_sec);
212 /* all other env-variables from the parent Apache process */
213 else if (strlen(var) > 4 && strcEQn(var, "ENV:", 4)) {
214 result = (char *)apr_table_get(r->notes, var+4);
216 result = (char *)apr_table_get(r->subprocess_env, var+4);
218 result = getenv(var+4);
222 if (result != NULL && resdup)
223 result = apr_pstrdup(p, result);
229 static char *ssl_var_lookup_header(apr_pool_t *p, request_rec *r, const char *name)
233 if ((hdr = (char *)apr_table_get(r->headers_in, name)) != NULL)
234 hdr = apr_pstrdup(p, hdr);
238 static char *ssl_var_lookup_ssl(apr_pool_t *p, conn_rec *c, char *var)
240 SSLConnRec *sslconn = myConnConfig(c);
249 if (strlen(var) > 8 && strcEQn(var, "VERSION_", 8)) {
250 result = ssl_var_lookup_ssl_version(c->base_server->process->pool,
253 else if (ssl != NULL && strcEQ(var, "PROTOCOL")) {
254 result = (char *)SSL_get_version(ssl);
256 else if (ssl != NULL && strcEQ(var, "SESSION_ID")) {
257 char buf[SSL_SESSION_ID_STRING_LEN];
258 SSL_SESSION *pSession = SSL_get_session(ssl);
260 result = apr_pstrdup(p, SSL_SESSION_id2sz(
261 SSL_SESSION_get_session_id(pSession),
262 SSL_SESSION_get_session_id_length(pSession),
266 else if (ssl != NULL && strlen(var) >= 6 && strcEQn(var, "CIPHER", 6)) {
267 result = ssl_var_lookup_ssl_cipher(p, c, var+6);
269 else if (ssl != NULL && strlen(var) > 18 && strcEQn(var, "CLIENT_CERT_CHAIN_", 18)) {
270 sk = SSL_get_peer_cert_chain(ssl);
271 result = ssl_var_lookup_ssl_cert_chain(p, sk, var+18);
273 else if (ssl != NULL && strcEQ(var, "CLIENT_VERIFY")) {
274 result = ssl_var_lookup_ssl_cert_verify(p, c);
276 else if (ssl != NULL && strlen(var) > 7 && strcEQn(var, "CLIENT_", 7)) {
277 if ((xs = SSL_get_peer_certificate(ssl)) != NULL) {
278 result = ssl_var_lookup_ssl_cert(p, xs, var+7);
282 else if (ssl != NULL && strlen(var) > 7 && strcEQn(var, "SERVER_", 7)) {
283 if ((xs = SSL_get_certificate(ssl)) != NULL)
284 result = ssl_var_lookup_ssl_cert(p, xs, var+7);
286 else if (ssl != NULL && strcEQ(var, "SECURE_RENEG")) {
288 #ifdef SSL_get_secure_renegotiation_support
289 flag = SSL_get_secure_renegotiation_support(ssl);
291 result = apr_pstrdup(p, flag ? "true" : "false");
297 static char *ssl_var_lookup_ssl_cert(apr_pool_t *p, X509 *xs, char *var)
308 if (strcEQ(var, "M_VERSION")) {
309 result = apr_psprintf(p, "%lu", X509_get_version(xs)+1);
312 else if (strcEQ(var, "M_SERIAL")) {
313 result = ssl_var_lookup_ssl_cert_serial(p, xs);
315 else if (strcEQ(var, "V_START")) {
316 result = ssl_var_lookup_ssl_cert_valid(p, X509_get_notBefore(xs));
318 else if (strcEQ(var, "V_END")) {
319 result = ssl_var_lookup_ssl_cert_valid(p, X509_get_notAfter(xs));
321 else if (strcEQ(var, "S_DN")) {
322 xsname = X509_get_subject_name(xs);
323 cp = X509_NAME_oneline(xsname, NULL, 0);
324 result = apr_pstrdup(p, cp);
328 else if (strlen(var) > 5 && strcEQn(var, "S_DN_", 5)) {
329 xsname = X509_get_subject_name(xs);
330 result = ssl_var_lookup_ssl_cert_dn(p, xsname, var+5);
333 else if (strcEQ(var, "I_DN")) {
334 xsname = X509_get_issuer_name(xs);
335 cp = X509_NAME_oneline(xsname, NULL, 0);
336 result = apr_pstrdup(p, cp);
340 else if (strlen(var) > 5 && strcEQn(var, "I_DN_", 5)) {
341 xsname = X509_get_issuer_name(xs);
342 result = ssl_var_lookup_ssl_cert_dn(p, xsname, var+5);
345 else if (strcEQ(var, "A_SIG")) {
346 nid = OBJ_obj2nid((ASN1_OBJECT *)X509_get_signature_algorithm(xs));
347 result = apr_pstrdup(p,
348 (nid == NID_undef) ? "UNKNOWN" : OBJ_nid2ln(nid));
351 else if (strcEQ(var, "A_KEY")) {
352 nid = OBJ_obj2nid((ASN1_OBJECT *)X509_get_key_algorithm(xs));
353 result = apr_pstrdup(p,
354 (nid == NID_undef) ? "UNKNOWN" : OBJ_nid2ln(nid));
357 else if (strcEQ(var, "CERT")) {
358 result = ssl_var_lookup_ssl_cert_PEM(p, xs);
361 if (result != NULL && resdup)
362 result = apr_pstrdup(p, result);
366 static const struct {
369 } ssl_var_lookup_ssl_cert_dn_rec[] = {
370 { "C", NID_countryName },
371 { "ST", NID_stateOrProvinceName }, /* officially (RFC2156) */
372 { "SP", NID_stateOrProvinceName }, /* compatibility (SSLeay) */
373 { "L", NID_localityName },
374 { "O", NID_organizationName },
375 { "OU", NID_organizationalUnitName },
376 { "CN", NID_commonName },
378 { "I", NID_initials },
379 { "G", NID_givenName },
380 { "S", NID_surname },
381 { "D", NID_description },
382 /* This has been removed in OpenSSL 0.9.8-dev. */
383 #ifdef NID_uniqueIdentifier
384 { "UID", NID_uniqueIdentifier },
386 { "Email", NID_pkcs9_emailAddress },
390 static char *ssl_var_lookup_ssl_cert_dn(apr_pool_t *p, X509_NAME *xsname, char *var)
393 X509_NAME_ENTRY *xsne;
395 unsigned char *data_ptr;
400 for (i = 0; ssl_var_lookup_ssl_cert_dn_rec[i].name != NULL; i++) {
401 if (strEQ(var, ssl_var_lookup_ssl_cert_dn_rec[i].name)) {
402 for (j = 0; j < sk_X509_NAME_ENTRY_num((STACK_OF(X509_NAME_ENTRY) *)
403 X509_NAME_get_entries(xsname));
405 xsne = sk_X509_NAME_ENTRY_value((STACK_OF(X509_NAME_ENTRY) *)
406 X509_NAME_get_entries(xsname), j);
408 n =OBJ_obj2nid((ASN1_OBJECT *)X509_NAME_ENTRY_get_object(xsne));
409 data_ptr = X509_NAME_ENTRY_get_data_ptr(xsne);
410 data_len = X509_NAME_ENTRY_get_data_len(xsne);
412 if (n == ssl_var_lookup_ssl_cert_dn_rec[i].nid) {
413 result = apr_palloc(p, data_len+1);
414 apr_cpystrn(result, (char *)data_ptr, data_len+1);
415 #ifdef CHARSET_EBCDIC
416 ascii2ebcdic(result, result, xsne->value->length);
417 #endif /* CHARSET_EBCDIC */
418 result[data_len] = NUL;
428 static char *ssl_var_lookup_ssl_cert_valid(apr_pool_t *p, ASN1_UTCTIME *tm)
434 if ((bio = BIO_new(BIO_s_mem())) == NULL)
436 ASN1_UTCTIME_print(bio, tm);
437 n = BIO_pending(bio);
438 result = apr_pcalloc(p, n+1);
439 n = BIO_read(bio, result, n);
445 static char *ssl_var_lookup_ssl_cert_serial(apr_pool_t *p, X509 *xs)
451 if ((bio = BIO_new(BIO_s_mem())) == NULL)
453 i2a_ASN1_INTEGER(bio, X509_get_serialNumber(xs));
454 n = BIO_pending(bio);
455 result = apr_pcalloc(p, n+1);
456 n = BIO_read(bio, result, n);
462 static char *ssl_var_lookup_ssl_cert_chain(apr_pool_t *p, STACK_OF(X509) *sk, char *var)
470 if (strspn(var, "0123456789") == strlen(var)) {
472 if (n < sk_X509_num(sk)) {
473 xs = sk_X509_value(sk, n);
474 result = ssl_var_lookup_ssl_cert_PEM(p, xs);
481 static char *ssl_var_lookup_ssl_cert_PEM(apr_pool_t *p, X509 *xs)
487 if ((bio = BIO_new(BIO_s_mem())) == NULL)
489 PEM_write_bio_X509(bio, xs);
490 n = BIO_pending(bio);
491 result = apr_pcalloc(p, n+1);
492 n = BIO_read(bio, result, n);
498 static char *ssl_var_lookup_ssl_cert_verify(apr_pool_t *p, conn_rec *c)
500 SSLConnRec *sslconn = myConnConfig(c);
510 verr = sslconn->verify_error;
511 vinfo = sslconn->verify_info;
512 vrc = SSL_get_verify_result(ssl);
513 xs = SSL_get_peer_certificate(ssl);
515 if (vrc == X509_V_OK && verr == NULL && vinfo == NULL && xs == NULL)
516 /* no client verification done at all */
518 else if (vrc == X509_V_OK && verr == NULL && vinfo == NULL && xs != NULL)
519 /* client verification done successful */
521 else if (vrc == X509_V_OK && vinfo != NULL && strEQ(vinfo, "GENEROUS"))
522 /* client verification done in generous way */
525 /* client verification failed */
526 result = apr_psprintf(p, "FAILED:%s", verr);
533 static char *ssl_var_lookup_ssl_cipher(apr_pool_t *p, conn_rec *c, char *var)
535 SSLConnRec *sslconn = myConnConfig(c);
538 int usekeysize, algkeysize;
545 ssl_var_lookup_ssl_cipher_bits(ssl, &usekeysize, &algkeysize);
547 if (ssl && strEQ(var, "")) {
548 SSL_CIPHER *cipher = SSL_get_current_cipher(ssl);
549 result = (cipher != NULL ? (char *)SSL_CIPHER_get_name(cipher) : NULL);
551 else if (strcEQ(var, "_EXPORT"))
552 result = (usekeysize < 56 ? "true" : "false");
553 else if (strcEQ(var, "_USEKEYSIZE")) {
554 result = apr_psprintf(p, "%d", usekeysize);
557 else if (strcEQ(var, "_ALGKEYSIZE")) {
558 result = apr_psprintf(p, "%d", algkeysize);
562 if (result != NULL && resdup)
563 result = apr_pstrdup(p, result);
567 static void ssl_var_lookup_ssl_cipher_bits(SSL *ssl, int *usekeysize, int *algkeysize)
574 if ((cipher = SSL_get_current_cipher(ssl)) != NULL)
575 *usekeysize = SSL_CIPHER_get_bits(cipher, algkeysize);
579 static char *ssl_var_lookup_ssl_version(apr_pool_t *pp, apr_pool_t *p, char *var)
581 static char interface[] = "mod_ssl/" MOD_SSL_VERSION;
582 static char library_interface[] = SSL_LIBRARY_TEXT;
583 static char *library = NULL;
588 library = apr_pstrdup(pp, SSL_LIBRARY_DYNTEXT);
589 if ((cp = strchr(library, ' ')) != NULL) {
591 if ((cp2 = strchr(cp, ' ')) != NULL)
594 if ((cp = strchr(library_interface, ' ')) != NULL) {
596 if ((cp2 = strchr(cp, ' ')) != NULL)
601 if (strEQ(var, "INTERFACE")) {
602 result = apr_pstrdup(p, interface);
604 else if (strEQ(var, "LIBRARY_INTERFACE")) {
605 result = apr_pstrdup(p, library_interface);
607 else if (strEQ(var, "LIBRARY")) {
608 result = apr_pstrdup(p, library);
617 /* _________________________________________________________________
619 ** SSL Extension to mod_log_config
620 ** _________________________________________________________________
623 #include "../../modules/loggers/mod_log_config.h"
625 static const char *ssl_var_log_handler_c(request_rec *r, char *a);
626 static const char *ssl_var_log_handler_x(request_rec *r, char *a);
629 * register us for the mod_log_config function registering phase
630 * to establish %{...}c and to be able to expand %{...}x variables.
632 void ssl_var_log_config_register(apr_pool_t *p)
634 static APR_OPTIONAL_FN_TYPE(ap_register_log_handler) *log_pfn_register;
636 log_pfn_register = APR_RETRIEVE_OPTIONAL_FN(ap_register_log_handler);
638 if (log_pfn_register) {
639 log_pfn_register(p, "c", ssl_var_log_handler_c, 0);
640 log_pfn_register(p, "x", ssl_var_log_handler_x, 0);
646 * implement the %{..}c log function
647 * (we are the only function)
649 static const char *ssl_var_log_handler_c(request_rec *r, char *a)
651 SSLConnRec *sslconn = myConnConfig(r->connection);
654 if (sslconn == NULL || sslconn->ssl == NULL)
657 if (strEQ(a, "version"))
658 result = ssl_var_lookup(r->pool, r->server, r->connection, r, "SSL_PROTOCOL");
659 else if (strEQ(a, "cipher"))
660 result = ssl_var_lookup(r->pool, r->server, r->connection, r, "SSL_CIPHER");
661 else if (strEQ(a, "subjectdn") || strEQ(a, "clientcert"))
662 result = ssl_var_lookup(r->pool, r->server, r->connection, r, "SSL_CLIENT_S_DN");
663 else if (strEQ(a, "issuerdn") || strEQ(a, "cacert"))
664 result = ssl_var_lookup(r->pool, r->server, r->connection, r, "SSL_CLIENT_I_DN");
665 else if (strEQ(a, "errcode"))
667 else if (strEQ(a, "errstr"))
668 result = (char *)sslconn->verify_error;
669 if (result != NULL && result[0] == NUL)
675 * extend the implementation of the %{..}x log function
676 * (there can be more functions)
678 static const char *ssl_var_log_handler_x(request_rec *r, char *a)
682 result = ssl_var_lookup(r->pool, r->server, r->connection, r, a);
683 if (result != NULL && result[0] == NUL)