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.''
30 #include "ssl_private.h"
35 /* _________________________________________________________________
38 ** _________________________________________________________________
41 static char *ssl_var_lookup_ssl(apr_pool_t
*p
, conn_rec
*c
, char *var
);
42 static char *ssl_var_lookup_ssl_cert(apr_pool_t
*p
, X509
*xs
, char *var
);
43 static char *ssl_var_lookup_ssl_cert_dn(apr_pool_t
*p
, X509_NAME
*xsname
, char *var
);
44 static char *ssl_var_lookup_ssl_cert_valid(apr_pool_t
*p
, ASN1_UTCTIME
*tm
);
45 static char *ssl_var_lookup_ssl_cert_remain(apr_pool_t
*p
, ASN1_UTCTIME
*tm
);
46 static char *ssl_var_lookup_ssl_cert_serial(apr_pool_t
*p
, X509
*xs
);
47 static char *ssl_var_lookup_ssl_cert_chain(apr_pool_t
*p
, STACK_OF(X509
) *sk
, char *var
);
48 static char *ssl_var_lookup_ssl_cert_PEM(apr_pool_t
*p
, X509
*xs
);
49 static char *ssl_var_lookup_ssl_cert_verify(apr_pool_t
*p
, conn_rec
*c
);
50 static char *ssl_var_lookup_ssl_cipher(apr_pool_t
*p
, conn_rec
*c
, char *var
);
51 static void ssl_var_lookup_ssl_cipher_bits(SSL
*ssl
, int *usekeysize
, int *algkeysize
);
52 static char *ssl_var_lookup_ssl_version(apr_pool_t
*p
, char *var
);
53 static char *ssl_var_lookup_ssl_compress_meth(SSL
*ssl
);
55 static int ssl_is_https(conn_rec
*c
)
57 SSLConnRec
*sslconn
= myConnConfig(c
);
58 return sslconn
&& sslconn
->ssl
;
61 static const char var_interface
[] = "mod_ssl/" MOD_SSL_VERSION
;
62 static char var_library_interface
[] = SSL_LIBRARY_TEXT
;
63 static char *var_library
= NULL
;
65 void ssl_var_register(apr_pool_t
*p
)
69 APR_REGISTER_OPTIONAL_FN(ssl_is_https
);
70 APR_REGISTER_OPTIONAL_FN(ssl_var_lookup
);
71 APR_REGISTER_OPTIONAL_FN(ssl_ext_list
);
73 /* Perform once-per-process library version determination: */
74 var_library
= apr_pstrdup(p
, SSL_LIBRARY_DYNTEXT
);
76 if ((cp
= strchr(var_library
, ' ')) != NULL
) {
78 if ((cp2
= strchr(cp
, ' ')) != NULL
)
82 if ((cp
= strchr(var_library_interface
, ' ')) != NULL
) {
84 if ((cp2
= strchr(cp
, ' ')) != NULL
)
89 /* This function must remain safe to use for a non-SSL connection. */
90 char *ssl_var_lookup(apr_pool_t
*p
, server_rec
*s
, conn_rec
*c
, request_rec
*r
, char *var
)
92 SSLModConfigRec
*mc
= myModConfig(s
);
101 * When no pool is given try to find one
113 * Request dependent stuff
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);
140 if (strcEQ(var
, "REQUEST_METHOD"))
142 else if (strcEQ(var
, "REQUEST_SCHEME"))
143 result
= ap_http_scheme(r
);
144 else if (strcEQ(var
, "REQUEST_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
,
151 else if (strcEQ(var
, "REMOTE_IDENT"))
152 result
= ap_get_remote_logname(r
);
153 else if (strcEQ(var
, "REMOTE_USER"))
159 if (strcEQn(var
, "SSL", 3)) break; /* shortcut common case */
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
;
174 if (strcEQ(var
, "PATH_INFO"))
175 result
= r
->path_info
;
176 else if (strcEQ(var
, "QUERY_STRING"))
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
;
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
)
209 * Totally independent stuff
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
);
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);
226 #define MKTIMESTR(format, tmfield) \
227 apr_time_exp_lt(&tm, apr_time_now()); \
228 result = apr_psprintf(p, format, tm.tmfield); \
230 else if (strcEQ(var
, "TIME_MON")) {
231 MKTIMESTR("%02d", tm_mon
+1)
233 else if (strcEQ(var
, "TIME_DAY")) {
234 MKTIMESTR("%02d", tm_mday
)
236 else if (strcEQ(var
, "TIME_HOUR")) {
237 MKTIMESTR("%02d", tm_hour
)
239 else if (strcEQ(var
, "TIME_MIN")) {
240 MKTIMESTR("%02d", tm_min
)
242 else if (strcEQ(var
, "TIME_SEC")) {
243 MKTIMESTR("%02d", tm_sec
)
245 else if (strcEQ(var
, "TIME_WDAY")) {
246 MKTIMESTR("%d", tm_wday
)
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
);
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);
260 result
= apr_table_get(r
->subprocess_env
, var
+4);
262 result
= getenv(var
+4);
266 if (result
!= NULL
&& resdup
)
267 result
= apr_pstrdup(p
, result
);
270 return (char *)result
;
273 static char *ssl_var_lookup_ssl(apr_pool_t
*p
, conn_rec
*c
, char *var
)
275 SSLConnRec
*sslconn
= myConnConfig(c
);
284 if (strlen(var
) > 8 && strcEQn(var
, "VERSION_", 8)) {
285 result
= ssl_var_lookup_ssl_version(p
, var
+8);
287 else if (ssl
!= NULL
&& strcEQ(var
, "PROTOCOL")) {
288 result
= (char *)SSL_get_version(ssl
);
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
);
294 result
= apr_pstrdup(p
, SSL_SESSION_id2sz(
295 SSL_SESSION_get_session_id(pSession
),
296 SSL_SESSION_get_session_id_length(pSession
),
300 else if (ssl
!= NULL
&& strlen(var
) >= 6 && strcEQn(var
, "CIPHER", 6)) {
301 result
= ssl_var_lookup_ssl_cipher(p
, c
, var
+6);
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);
307 else if (ssl
!= NULL
&& strcEQ(var
, "CLIENT_VERIFY")) {
308 result
= ssl_var_lookup_ssl_cert_verify(p
, c
);
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);
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);
320 else if (ssl
!= NULL
&& strcEQ(var
, "COMPRESS_METHOD")) {
321 result
= ssl_var_lookup_ssl_compress_meth(ssl
);
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
));
332 static char *ssl_var_lookup_ssl_cert(apr_pool_t
*p
, X509
*xs
, char *var
)
343 if (strcEQ(var
, "M_VERSION")) {
344 result
= apr_psprintf(p
, "%lu", X509_get_version(xs
)+1);
347 else if (strcEQ(var
, "M_SERIAL")) {
348 result
= ssl_var_lookup_ssl_cert_serial(p
, xs
);
350 else if (strcEQ(var
, "V_START")) {
351 result
= ssl_var_lookup_ssl_cert_valid(p
, X509_get_notBefore(xs
));
353 else if (strcEQ(var
, "V_END")) {
354 result
= ssl_var_lookup_ssl_cert_valid(p
, X509_get_notAfter(xs
));
356 else if (strcEQ(var
, "V_REMAIN")) {
357 result
= ssl_var_lookup_ssl_cert_remain(p
, X509_get_notAfter(xs
));
360 else if (strcEQ(var
, "S_DN")) {
361 xsname
= X509_get_subject_name(xs
);
362 cp
= X509_NAME_oneline(xsname
, NULL
, 0);
363 result
= apr_pstrdup(p
, cp
);
367 else if (strlen(var
) > 5 && strcEQn(var
, "S_DN_", 5)) {
368 xsname
= X509_get_subject_name(xs
);
369 result
= ssl_var_lookup_ssl_cert_dn(p
, xsname
, var
+5);
372 else if (strcEQ(var
, "I_DN")) {
373 xsname
= X509_get_issuer_name(xs
);
374 cp
= X509_NAME_oneline(xsname
, NULL
, 0);
375 result
= apr_pstrdup(p
, cp
);
379 else if (strlen(var
) > 5 && strcEQn(var
, "I_DN_", 5)) {
380 xsname
= X509_get_issuer_name(xs
);
381 result
= ssl_var_lookup_ssl_cert_dn(p
, xsname
, var
+5);
384 else if (strcEQ(var
, "A_SIG")) {
385 nid
= OBJ_obj2nid((ASN1_OBJECT
*)X509_get_signature_algorithm(xs
));
386 result
= apr_pstrdup(p
,
387 (nid
== NID_undef
) ? "UNKNOWN" : OBJ_nid2ln(nid
));
390 else if (strcEQ(var
, "A_KEY")) {
391 nid
= OBJ_obj2nid((ASN1_OBJECT
*)X509_get_key_algorithm(xs
));
392 result
= apr_pstrdup(p
,
393 (nid
== NID_undef
) ? "UNKNOWN" : OBJ_nid2ln(nid
));
396 else if (strcEQ(var
, "CERT")) {
397 result
= ssl_var_lookup_ssl_cert_PEM(p
, xs
);
400 if (result
!= NULL
&& resdup
)
401 result
= apr_pstrdup(p
, result
);
405 /* In this table, .extract is non-zero if RDNs using the NID should be
406 * extracted to for the SSL_{CLIENT,SERVER}_{I,S}_DN_* environment
408 static const struct {
412 } ssl_var_lookup_ssl_cert_dn_rec
[] = {
413 { "C", NID_countryName
, 1 },
414 { "ST", NID_stateOrProvinceName
, 1 }, /* officially (RFC2156) */
415 { "SP", NID_stateOrProvinceName
, 0 }, /* compatibility (SSLeay) */
416 { "L", NID_localityName
, 1 },
417 { "O", NID_organizationName
, 1 },
418 { "OU", NID_organizationalUnitName
, 1 },
419 { "CN", NID_commonName
, 1 },
420 { "T", NID_title
, 1 },
421 { "I", NID_initials
, 1 },
422 { "G", NID_givenName
, 1 },
423 { "S", NID_surname
, 1 },
424 { "D", NID_description
, 1 },
425 #ifdef NID_x500UniqueIdentifier /* new name as of Openssl 0.9.7 */
426 { "UID", NID_x500UniqueIdentifier
, 1 },
427 #else /* old name, OpenSSL < 0.9.7 */
428 { "UID", NID_uniqueIdentifier
, 1 },
430 { "Email", NID_pkcs9_emailAddress
, 1 },
434 static char *ssl_var_lookup_ssl_cert_dn(apr_pool_t
*p
, X509_NAME
*xsname
, char *var
)
437 X509_NAME_ENTRY
*xsne
;
438 int i
, j
, n
, idx
= 0;
441 /* if an _N suffix is used, find the Nth attribute of given name */
442 ptr
= strchr(var
, '_');
443 if (ptr
!= NULL
&& strspn(ptr
+ 1, "0123456789") == strlen(ptr
+ 1)) {
447 varlen
= strlen(var
);
452 for (i
= 0; ssl_var_lookup_ssl_cert_dn_rec
[i
].name
!= NULL
; i
++) {
453 if (strEQn(var
, ssl_var_lookup_ssl_cert_dn_rec
[i
].name
, varlen
)
454 && strlen(ssl_var_lookup_ssl_cert_dn_rec
[i
].name
) == varlen
) {
455 for (j
= 0; j
< sk_X509_NAME_ENTRY_num((STACK_OF(X509_NAME_ENTRY
) *)
456 X509_NAME_get_entries(xsname
));
458 xsne
= sk_X509_NAME_ENTRY_value((STACK_OF(X509_NAME_ENTRY
) *)
459 X509_NAME_get_entries(xsname
), j
);
461 n
=OBJ_obj2nid((ASN1_OBJECT
*)X509_NAME_ENTRY_get_object(xsne
));
463 if (n
== ssl_var_lookup_ssl_cert_dn_rec
[i
].nid
&& idx
-- == 0) {
464 unsigned char *data
= X509_NAME_ENTRY_get_data_ptr(xsne
);
465 /* cast needed from unsigned char to char */
466 result
= apr_pstrmemdup(p
, (char *)data
,
467 X509_NAME_ENTRY_get_data_len(xsne
));
468 #if APR_CHARSET_EBCDIC
469 ap_xlate_proto_from_ascii(result
, X509_NAME_ENTRY_get_data_len(xsne
));
470 #endif /* APR_CHARSET_EBCDIC */
480 static char *ssl_var_lookup_ssl_cert_valid(apr_pool_t
*p
, ASN1_UTCTIME
*tm
)
486 if ((bio
= BIO_new(BIO_s_mem())) == NULL
)
488 ASN1_UTCTIME_print(bio
, tm
);
489 n
= BIO_pending(bio
);
490 result
= apr_pcalloc(p
, n
+1);
491 n
= BIO_read(bio
, result
, n
);
497 #define DIGIT2NUM(x) (((x)[0] - '0') * 10 + (x)[1] - '0')
499 /* Return a string giving the number of days remaining until 'tm', or
500 * "0" if this can't be determined. */
501 static char *ssl_var_lookup_ssl_cert_remain(apr_pool_t
*p
, ASN1_UTCTIME
*tm
)
503 apr_time_t then
, now
= apr_time_now();
504 apr_time_exp_t exp
= {0};
507 /* Fail if the time isn't a valid ASN.1 UTCTIME; RFC3280 mandates
508 * that the seconds digits are present even though ASN.1
510 if (tm
->length
< 11 || !ASN1_UTCTIME_check(tm
)) {
511 return apr_pstrdup(p
, "0");
514 exp
.tm_year
= DIGIT2NUM(tm
->data
);
515 exp
.tm_mon
= DIGIT2NUM(tm
->data
+ 2) - 1;
516 exp
.tm_mday
= DIGIT2NUM(tm
->data
+ 4) + 1;
517 exp
.tm_hour
= DIGIT2NUM(tm
->data
+ 6);
518 exp
.tm_min
= DIGIT2NUM(tm
->data
+ 8);
519 exp
.tm_sec
= DIGIT2NUM(tm
->data
+ 10);
521 if (exp
.tm_year
<= 50) exp
.tm_year
+= 100;
523 if (apr_time_exp_gmt_get(&then
, &exp
) != APR_SUCCESS
) {
524 return apr_pstrdup(p
, "0");
527 diff
= (long)((apr_time_sec(then
) - apr_time_sec(now
)) / (60*60*24));
529 return diff
> 0 ? apr_ltoa(p
, diff
) : apr_pstrdup(p
, "0");
532 static char *ssl_var_lookup_ssl_cert_serial(apr_pool_t
*p
, X509
*xs
)
538 if ((bio
= BIO_new(BIO_s_mem())) == NULL
)
540 i2a_ASN1_INTEGER(bio
, X509_get_serialNumber(xs
));
541 n
= BIO_pending(bio
);
542 result
= apr_pcalloc(p
, n
+1);
543 n
= BIO_read(bio
, result
, n
);
549 static char *ssl_var_lookup_ssl_cert_chain(apr_pool_t
*p
, STACK_OF(X509
) *sk
, char *var
)
557 if (strspn(var
, "0123456789") == strlen(var
)) {
559 if (n
< sk_X509_num(sk
)) {
560 xs
= sk_X509_value(sk
, n
);
561 result
= ssl_var_lookup_ssl_cert_PEM(p
, xs
);
568 static char *ssl_var_lookup_ssl_cert_PEM(apr_pool_t
*p
, X509
*xs
)
574 if ((bio
= BIO_new(BIO_s_mem())) == NULL
)
576 PEM_write_bio_X509(bio
, xs
);
577 n
= BIO_pending(bio
);
578 result
= apr_pcalloc(p
, n
+1);
579 n
= BIO_read(bio
, result
, n
);
585 static char *ssl_var_lookup_ssl_cert_verify(apr_pool_t
*p
, conn_rec
*c
)
587 SSLConnRec
*sslconn
= myConnConfig(c
);
597 verr
= sslconn
->verify_error
;
598 vinfo
= sslconn
->verify_info
;
599 vrc
= SSL_get_verify_result(ssl
);
600 xs
= SSL_get_peer_certificate(ssl
);
602 if (vrc
== X509_V_OK
&& verr
== NULL
&& vinfo
== NULL
&& xs
== NULL
)
603 /* no client verification done at all */
605 else if (vrc
== X509_V_OK
&& verr
== NULL
&& vinfo
== NULL
&& xs
!= NULL
)
606 /* client verification done successful */
608 else if (vrc
== X509_V_OK
&& vinfo
!= NULL
&& strEQ(vinfo
, "GENEROUS"))
609 /* client verification done in generous way */
612 /* client verification failed */
613 result
= apr_psprintf(p
, "FAILED:%s", verr
);
620 static char *ssl_var_lookup_ssl_cipher(apr_pool_t
*p
, conn_rec
*c
, char *var
)
622 SSLConnRec
*sslconn
= myConnConfig(c
);
625 int usekeysize
, algkeysize
;
632 ssl_var_lookup_ssl_cipher_bits(ssl
, &usekeysize
, &algkeysize
);
634 if (ssl
&& strEQ(var
, "")) {
635 const SSL_CIPHER
*cipher
= SSL_get_current_cipher(ssl
);
636 result
= (cipher
!= NULL
? (char *)SSL_CIPHER_get_name(cipher
) : NULL
);
638 else if (strcEQ(var
, "_EXPORT"))
639 result
= (usekeysize
< 56 ? "true" : "false");
640 else if (strcEQ(var
, "_USEKEYSIZE")) {
641 result
= apr_itoa(p
, usekeysize
);
644 else if (strcEQ(var
, "_ALGKEYSIZE")) {
645 result
= apr_itoa(p
, algkeysize
);
649 if (result
!= NULL
&& resdup
)
650 result
= apr_pstrdup(p
, result
);
654 static void ssl_var_lookup_ssl_cipher_bits(SSL
*ssl
, int *usekeysize
, int *algkeysize
)
656 const SSL_CIPHER
*cipher
;
661 if ((cipher
= SSL_get_current_cipher(ssl
)) != NULL
)
662 *usekeysize
= SSL_CIPHER_get_bits(cipher
, algkeysize
);
666 static char *ssl_var_lookup_ssl_version(apr_pool_t
*p
, char *var
)
668 if (strEQ(var
, "INTERFACE")) {
669 return apr_pstrdup(p
, var_interface
);
671 else if (strEQ(var
, "LIBRARY_INTERFACE")) {
672 return apr_pstrdup(p
, var_library_interface
);
674 else if (strEQ(var
, "LIBRARY")) {
675 return apr_pstrdup(p
, var_library
);
680 /* Add each RDN in 'xn' to the table 't' where the NID is present in
681 * 'nids', using key prefix 'pfx'. */
682 static void extract_dn(apr_table_t
*t
, apr_hash_t
*nids
, const char *pfx
,
683 X509_NAME
*xn
, apr_pool_t
*p
)
685 STACK_OF(X509_NAME_ENTRY
) *ents
= X509_NAME_get_entries(xn
);
686 X509_NAME_ENTRY
*xsne
;
690 /* Hash of (int) NID -> (int *) counter to count each time an RDN
691 * with the given NID has been seen. */
692 count
= apr_hash_make(p
);
694 /* For each RDN... */
695 for (i
= 0; i
< sk_X509_NAME_ENTRY_num(ents
); i
++) {
698 xsne
= sk_X509_NAME_ENTRY_value(ents
, i
);
700 /* Retrieve the nid, and check whether this is one of the nids
701 * which are to be extracted. */
702 nid
= OBJ_obj2nid((ASN1_OBJECT
*)X509_NAME_ENTRY_get_object(xsne
));
704 tag
= apr_hash_get(nids
, &nid
, sizeof nid
);
706 unsigned char *data
= X509_NAME_ENTRY_get_data_ptr(xsne
);
711 /* Check whether a variable with this nid was already
712 * been used; if so, use the foo_N=bar syntax. */
713 dup
= apr_hash_get(count
, &nid
, sizeof nid
);
715 key
= apr_psprintf(p
, "%s%s_%d", pfx
, tag
, ++(*dup
));
718 /* Otherwise, use the plain foo=bar syntax. */
719 dup
= apr_pcalloc(p
, sizeof *dup
);
720 apr_hash_set(count
, &nid
, sizeof nid
, dup
);
721 key
= apr_pstrcat(p
, pfx
, tag
, NULL
);
724 /* cast needed from 'unsigned char *' to 'char *' */
725 value
= apr_pstrmemdup(p
, (char *)data
,
726 X509_NAME_ENTRY_get_data_len(xsne
));
727 #if APR_CHARSET_EBCDIC
728 ap_xlate_proto_from_ascii(value
, X509_NAME_ENTRY_get_data_len(xsne
));
729 #endif /* APR_CHARSET_EBCDIC */
730 apr_table_setn(t
, key
, value
);
735 void modssl_var_extract_dns(apr_table_t
*t
, SSL
*ssl
, apr_pool_t
*p
)
741 /* Build up a hash table of (int *)NID->(char *)short-name for all
742 * the tags which are to be extracted: */
743 nids
= apr_hash_make(p
);
744 for (n
= 0; ssl_var_lookup_ssl_cert_dn_rec
[n
].name
; n
++) {
745 if (ssl_var_lookup_ssl_cert_dn_rec
[n
].extract
) {
746 apr_hash_set(nids
, &ssl_var_lookup_ssl_cert_dn_rec
[n
].nid
,
747 sizeof(ssl_var_lookup_ssl_cert_dn_rec
[0].nid
),
748 ssl_var_lookup_ssl_cert_dn_rec
[n
].name
);
752 /* Extract the server cert DNS -- note that the refcount does NOT
754 xs
= SSL_get_certificate(ssl
);
756 extract_dn(t
, nids
, "SSL_SERVER_S_DN_", X509_get_subject_name(xs
), p
);
757 extract_dn(t
, nids
, "SSL_SERVER_I_DN_", X509_get_issuer_name(xs
), p
);
760 /* Extract the client cert DNs -- note that the refcount DOES
762 xs
= SSL_get_peer_certificate(ssl
);
764 extract_dn(t
, nids
, "SSL_CLIENT_S_DN_", X509_get_subject_name(xs
), p
);
765 extract_dn(t
, nids
, "SSL_CLIENT_I_DN_", X509_get_issuer_name(xs
), p
);
770 apr_array_header_t
*ssl_ext_list(apr_pool_t
*p
, conn_rec
*c
, int peer
,
771 const char *extension
)
773 SSLConnRec
*sslconn
= myConnConfig(c
);
775 apr_array_header_t
*array
= NULL
;
777 ASN1_OBJECT
*oid
= NULL
;
780 if (!sslconn
|| !sslconn
->ssl
|| !extension
) {
785 /* We accept the "extension" string to be converted as
786 * a long name (nsComment), short name (DN) or
787 * numeric OID (1.2.3.4).
789 oid
= OBJ_txt2obj(extension
, 0);
791 ap_log_cerror(APLOG_MARK
, APLOG_DEBUG
, 0, c
,
792 "Failed to create an object for extension '%s'",
798 xs
= peer
? SSL_get_peer_certificate(ssl
) : SSL_get_certificate(ssl
);
803 count
= X509_get_ext_count(xs
);
804 /* Create an array large enough to accomodate every extension. This is
805 * likely overkill, but safe.
807 array
= apr_array_make(p
, count
, sizeof(char *));
808 for (j
= 0; j
< count
; j
++) {
809 X509_EXTENSION
*ext
= X509_get_ext(xs
, j
);
811 if (OBJ_cmp(ext
->object
, oid
) == 0) {
812 BIO
*bio
= BIO_new(BIO_s_mem());
814 /* We want to obtain a string representation of the extensions
815 * value and add it to the array we're building.
816 * X509V3_EXT_print() doesn't know about all the possible
817 * data types, but the value is stored as an ASN1_OCTET_STRING
818 * allowing us a fallback in case of X509V3_EXT_print
819 * not knowing how to handle the data.
821 if (X509V3_EXT_print(bio
, ext
, 0, 0) == 1 ||
822 ASN1_STRING_print(bio
, ext
->value
) == 1) {
824 char **ptr
= apr_array_push(array
);
825 BIO_get_mem_ptr(bio
, &buf
);
826 *ptr
= apr_pstrmemdup(p
, buf
->data
, buf
->length
);
828 ap_log_cerror(APLOG_MARK
, APLOG_DEBUG
, 0, c
,
829 "Found an extension '%s', but failed to "
830 "create a string from it", extension
);
836 if (array
->nelts
== 0)
840 /* only SSL_get_peer_certificate raises the refcount */
848 static char *ssl_var_lookup_ssl_compress_meth(SSL
*ssl
)
850 char *result
= "NULL";
851 #ifdef OPENSSL_VERSION_NUMBER
852 #if (OPENSSL_VERSION_NUMBER >= 0x00908000)
853 SSL_SESSION
*pSession
= SSL_get_session(ssl
);
856 switch (pSession
->compress_meth
) {
858 /* default "NULL" already set */
861 /* Defined by RFC 3749, deflate is coded by "1" */
866 /* IANA assigned compression number for LZS */
881 /* _________________________________________________________________
883 ** SSL Extension to mod_log_config
884 ** _________________________________________________________________
887 #include "../../modules/loggers/mod_log_config.h"
889 static const char *ssl_var_log_handler_c(request_rec
*r
, char *a
);
890 static const char *ssl_var_log_handler_x(request_rec
*r
, char *a
);
893 * register us for the mod_log_config function registering phase
894 * to establish %{...}c and to be able to expand %{...}x variables.
896 void ssl_var_log_config_register(apr_pool_t
*p
)
898 static APR_OPTIONAL_FN_TYPE(ap_register_log_handler
) *log_pfn_register
;
900 log_pfn_register
= APR_RETRIEVE_OPTIONAL_FN(ap_register_log_handler
);
902 if (log_pfn_register
) {
903 log_pfn_register(p
, "c", ssl_var_log_handler_c
, 0);
904 log_pfn_register(p
, "x", ssl_var_log_handler_x
, 0);
910 * implement the %{..}c log function
911 * (we are the only function)
913 static const char *ssl_var_log_handler_c(request_rec
*r
, char *a
)
915 SSLConnRec
*sslconn
= myConnConfig(r
->connection
);
918 if (sslconn
== NULL
|| sslconn
->ssl
== NULL
)
921 if (strEQ(a
, "version"))
922 result
= ssl_var_lookup(r
->pool
, r
->server
, r
->connection
, r
, "SSL_PROTOCOL");
923 else if (strEQ(a
, "cipher"))
924 result
= ssl_var_lookup(r
->pool
, r
->server
, r
->connection
, r
, "SSL_CIPHER");
925 else if (strEQ(a
, "subjectdn") || strEQ(a
, "clientcert"))
926 result
= ssl_var_lookup(r
->pool
, r
->server
, r
->connection
, r
, "SSL_CLIENT_S_DN");
927 else if (strEQ(a
, "issuerdn") || strEQ(a
, "cacert"))
928 result
= ssl_var_lookup(r
->pool
, r
->server
, r
->connection
, r
, "SSL_CLIENT_I_DN");
929 else if (strEQ(a
, "errcode"))
931 else if (strEQ(a
, "errstr"))
932 result
= (char *)sslconn
->verify_error
;
933 if (result
!= NULL
&& result
[0] == NUL
)
939 * extend the implementation of the %{..}x log function
940 * (there can be more functions)
942 static const char *ssl_var_log_handler_x(request_rec
*r
, char *a
)
946 result
= ssl_var_lookup(r
->pool
, r
->server
, r
->connection
, r
, a
);
947 if (result
!= NULL
&& result
[0] == NUL
)