1 /*                      _             _
2 **  _ __ ___   ___   __| |    ___ ___| |  mod_ssl
3 ** | '_ ` _ \ / _ \ / _` |   / __/ __| |  Apache Interface to OpenSSL
4 ** | | | | | | (_) | (_| |   \__ \__ \ |  www.modssl.org
5 ** |_| |_| |_|\___/ \__,_|___|___/___/_|  ftp.modssl.org
6 **                      |_____|
7 **  ssl_engine_ext.c
8 **  Extensions to other Apache parts
9 */
10 
11 /* ====================================================================
12  * Copyright (c) 1998-2003 Ralf S. Engelschall. All rights reserved.
13  *
14  * Redistribution and use in source and binary forms, with or without
15  * modification, are permitted provided that the following conditions
16  * are met:
17  *
18  * 1. Redistributions of source code must retain the above copyright
19  *    notice, this list of conditions and the following disclaimer.
20  *
21  * 2. Redistributions in binary form must reproduce the above copyright
22  *    notice, this list of conditions and the following
23  *    disclaimer in the documentation and/or other materials
24  *    provided with the distribution.
25  *
26  * 3. All advertising materials mentioning features or use of this
27  *    software must display the following acknowledgment:
28  *    "This product includes software developed by
29  *     Ralf S. Engelschall <rse@engelschall.com> for use in the
30  *     mod_ssl project (http://www.modssl.org/)."
31  *
32  * 4. The names "mod_ssl" must not be used to endorse or promote
33  *    products derived from this software without prior written
34  *    permission. For written permission, please contact
35  *    rse@engelschall.com.
36  *
37  * 5. Products derived from this software may not be called "mod_ssl"
38  *    nor may "mod_ssl" appear in their names without prior
39  *    written permission of Ralf S. Engelschall.
40  *
41  * 6. Redistributions of any form whatsoever must retain the following
42  *    acknowledgment:
43  *    "This product includes software developed by
44  *     Ralf S. Engelschall <rse@engelschall.com> for use in the
45  *     mod_ssl project (http://www.modssl.org/)."
46  *
47  * THIS SOFTWARE IS PROVIDED BY RALF S. ENGELSCHALL ``AS IS'' AND ANY
48  * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
49  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
50  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL RALF S. ENGELSCHALL OR
51  * HIS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
52  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
53  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
54  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
55  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
56  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
57  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
58  * OF THE POSSIBILITY OF SUCH DAMAGE.
59  * ====================================================================
60  */
61                              /* ``Only those who attempt the absurd
62                                   can achieve the impossible.''
63                                            -- Unknown             */
64 #include "mod_ssl.h"
65 
66 
67 /*  _________________________________________________________________
68 **
69 **  SSL Extensions
70 **  _________________________________________________________________
71 */
72 
73 static void  ssl_ext_mlc_register(void);
74 static void  ssl_ext_mlc_unregister(void);
75 static void  ssl_ext_mr_register(void);
76 static void  ssl_ext_mr_unregister(void);
77 static void  ssl_ext_mp_register(void);
78 static void  ssl_ext_mp_unregister(void);
79 static void  ssl_ext_ms_register(void);
80 static void  ssl_ext_ms_unregister(void);
81 
ssl_ext_register(void)82 void ssl_ext_register(void)
83 {
84     ssl_ext_mlc_register();
85     ssl_ext_mr_register();
86     ssl_ext_mp_register();
87     ssl_ext_ms_register();
88     return;
89 }
90 
ssl_ext_unregister(void)91 void ssl_ext_unregister(void)
92 {
93     ssl_ext_mlc_unregister();
94     ssl_ext_mr_unregister();
95     ssl_ext_mp_unregister();
96     ssl_ext_ms_unregister();
97     return;
98 }
99 
100 /*  _________________________________________________________________
101 **
102 **  SSL Extension to mod_log_config
103 **  _________________________________________________________________
104 */
105 
106 static char *ssl_ext_mlc_log_c(request_rec *r, char *a);
107 static char *ssl_ext_mlc_log_x(request_rec *r, char *a);
108 
109 /*
110  * register us for the mod_log_config function registering phase
111  * to establish %{...}c and to be able to expand %{...}x variables.
112  */
ssl_ext_mlc_register(void)113 static void ssl_ext_mlc_register(void)
114 {
115     ap_hook_register("ap::mod_log_config::log_c",
116                      ssl_ext_mlc_log_c, AP_HOOK_NOCTX);
117     ap_hook_register("ap::mod_log_config::log_x",
118                      ssl_ext_mlc_log_x, AP_HOOK_NOCTX);
119     return;
120 }
121 
ssl_ext_mlc_unregister(void)122 static void ssl_ext_mlc_unregister(void)
123 {
124     ap_hook_unregister("ap::mod_log_config::log_c",
125                        ssl_ext_mlc_log_c);
126     ap_hook_unregister("ap::mod_log_config::log_x",
127                        ssl_ext_mlc_log_x);
128     return;
129 }
130 
131 /*
132  * implement the %{..}c log function
133  * (we are the only function)
134  */
ssl_ext_mlc_log_c(request_rec * r,char * a)135 static char *ssl_ext_mlc_log_c(request_rec *r, char *a)
136 {
137     char *result;
138 
139     if (ap_ctx_get(r->connection->client->ctx, "ssl") == NULL)
140         return NULL;
141     result = NULL;
142     if (strEQ(a, "version"))
143         result = ssl_var_lookup(r->pool, r->server, r->connection, r, "SSL_PROTOCOL");
144     else if (strEQ(a, "cipher"))
145         result = ssl_var_lookup(r->pool, r->server, r->connection, r, "SSL_CIPHER");
146     else if (strEQ(a, "subjectdn") || strEQ(a, "clientcert"))
147         result = ssl_var_lookup(r->pool, r->server, r->connection, r, "SSL_CLIENT_S_DN");
148     else if (strEQ(a, "issuerdn") || strEQ(a, "cacert"))
149         result = ssl_var_lookup(r->pool, r->server, r->connection, r, "SSL_CLIENT_I_DN");
150     else if (strEQ(a, "errcode"))
151         result = "-";
152     else if (strEQ(a, "errstr"))
153         result = ap_ctx_get(r->connection->client->ctx, "ssl::verify::error");
154     if (result != NULL && result[0] == NUL)
155         result = NULL;
156     return result;
157 }
158 
159 /*
160  * extend the implementation of the %{..}x log function
161  * (there can be more functions)
162  */
ssl_ext_mlc_log_x(request_rec * r,char * a)163 static char *ssl_ext_mlc_log_x(request_rec *r, char *a)
164 {
165     char *result;
166 
167     result = NULL;
168     if (ap_ctx_get(r->connection->client->ctx, "ssl") != NULL)
169         result = ssl_var_lookup(r->pool, r->server, r->connection, r, a);
170     if (result != NULL && result[0] == NUL)
171         result = NULL;
172     return result;
173 }
174 
175 /*  _________________________________________________________________
176 **
177 **  SSL Extension to mod_rewrite
178 **  _________________________________________________________________
179 */
180 
181 static char *ssl_ext_mr_lookup_variable(request_rec *r, char *var);
182 
183 /*
184  * register us for the mod_rewrite lookup_variable() function
185  */
ssl_ext_mr_register(void)186 static void ssl_ext_mr_register(void)
187 {
188     ap_hook_register("ap::mod_rewrite::lookup_variable",
189                      ssl_ext_mr_lookup_variable, AP_HOOK_NOCTX);
190     return;
191 }
192 
ssl_ext_mr_unregister(void)193 static void ssl_ext_mr_unregister(void)
194 {
195     ap_hook_unregister("ap::mod_rewrite::lookup_variable",
196                        ssl_ext_mr_lookup_variable);
197     return;
198 }
199 
ssl_ext_mr_lookup_variable(request_rec * r,char * var)200 static char *ssl_ext_mr_lookup_variable(request_rec *r, char *var)
201 {
202     char *val;
203 
204     val = ssl_var_lookup(r->pool, r->server, r->connection, r, var);
205     if (val[0] == NUL)
206         val = NULL;
207     return val;
208 }
209 
210 /*  _________________________________________________________________
211 **
212 **  SSL Extension to mod_proxy
213 **  _________________________________________________________________
214 */
215 
216 static int   ssl_ext_mp_canon(request_rec *, char *);
217 static int   ssl_ext_mp_handler(request_rec *, void *, char *, char *, int, char *);
218 static int   ssl_ext_mp_set_destport(request_rec *);
219 static char *ssl_ext_mp_new_connection(request_rec *, BUFF *, char *);
220 static void  ssl_ext_mp_close_connection(void *);
221 static int   ssl_ext_mp_write_host_header(request_rec *, BUFF *, char *, int, char *);
222 #ifdef SSL_EXPERIMENTAL_PROXY
223 static void  ssl_ext_mp_init(server_rec *, pool *);
224 static int   ssl_ext_mp_verify_cb(int, X509_STORE_CTX *);
225 static int   ssl_ext_mp_clientcert_cb(SSL *, X509 **, EVP_PKEY **);
226 #endif
227 
228 /*
229  * register us ...
230  */
ssl_ext_mp_register(void)231 static void ssl_ext_mp_register(void)
232 {
233 #ifdef SSL_EXPERIMENTAL_PROXY
234     ap_hook_register("ap::mod_proxy::init",
235                      ssl_ext_mp_init, AP_HOOK_NOCTX);
236 #endif
237     ap_hook_register("ap::mod_proxy::canon",
238                      ssl_ext_mp_canon, AP_HOOK_NOCTX);
239     ap_hook_register("ap::mod_proxy::handler",
240                      ssl_ext_mp_handler, AP_HOOK_NOCTX);
241     ap_hook_register("ap::mod_proxy::http::handler::set_destport",
242                      ssl_ext_mp_set_destport, AP_HOOK_NOCTX);
243     ap_hook_register("ap::mod_proxy::http::handler::new_connection",
244                      ssl_ext_mp_new_connection, AP_HOOK_NOCTX);
245     ap_hook_register("ap::mod_proxy::http::handler::write_host_header",
246                      ssl_ext_mp_write_host_header, AP_HOOK_NOCTX);
247     return;
248 }
249 
ssl_ext_mp_unregister(void)250 static void ssl_ext_mp_unregister(void)
251 {
252 #ifdef SSL_EXPERIMENTAL_PROXY
253     ap_hook_unregister("ap::mod_proxy::init", ssl_ext_mp_init);
254 #endif
255     ap_hook_unregister("ap::mod_proxy::canon", ssl_ext_mp_canon);
256     ap_hook_unregister("ap::mod_proxy::handler", ssl_ext_mp_handler);
257     ap_hook_unregister("ap::mod_proxy::http::handler::set_destport",
258                        ssl_ext_mp_set_destport);
259     ap_hook_unregister("ap::mod_proxy::http::handler::new_connection",
260                        ssl_ext_mp_new_connection);
261     ap_hook_unregister("ap::mod_proxy::http::handler::write_host_header",
262                        ssl_ext_mp_write_host_header);
263     return;
264 }
265 
266 /*
267  * SSL proxy initialization
268  */
269 #ifdef SSL_EXPERIMENTAL_PROXY
ssl_ext_mp_init(server_rec * s,pool * p)270 static void ssl_ext_mp_init(server_rec *s, pool *p)
271 {
272     SSLSrvConfigRec *sc;
273     char *cpVHostID;
274     int nVerify;
275     SSL_CTX *ctx;
276     char *cp;
277     STACK_OF(X509_INFO) *sk;
278 
279     /*
280      * Initialize each virtual server
281      */
282     ERR_clear_error();
283     for (; s != NULL; s = s->next) {
284         sc = mySrvConfig(s);
285         cpVHostID = ssl_util_vhostid(p, s);
286 
287         if (sc->bProxyVerify == UNSET)
288             sc->bProxyVerify = FALSE;
289 
290         /*
291          *  Create new SSL context and configure callbacks
292          */
293         if (sc->nProxyProtocol == SSL_PROTOCOL_NONE) {
294             ssl_log(s, SSL_LOG_ERROR,
295                     "Init: (%s) No Proxy SSL protocols available [hint: SSLProxyProtocol]",
296                     cpVHostID);
297             ssl_die();
298         }
299         cp = ap_pstrcat(p, (sc->nProxyProtocol & SSL_PROTOCOL_SSLV2 ? "SSLv2, " : ""),
300                            (sc->nProxyProtocol & SSL_PROTOCOL_SSLV3 ? "SSLv3, " : ""),
301                            (sc->nProxyProtocol & SSL_PROTOCOL_TLSV1 ? "TLSv1, " : ""), NULL);
302         cp[strlen(cp)-2] = NUL;
303         ssl_log(s, SSL_LOG_TRACE,
304                 "Init: (%s) Creating new proxy SSL context (protocols: %s)",
305                 cpVHostID, cp);
306         if (sc->nProxyProtocol == SSL_PROTOCOL_SSLV2)
307             ctx = SSL_CTX_new(SSLv2_client_method());  /* only SSLv2 is left */
308         else
309             ctx = SSL_CTX_new(SSLv23_client_method()); /* be more flexible */
310         if (ctx == NULL) {
311             ssl_log(s, SSL_LOG_ERROR|SSL_ADD_SSLERR,
312                     "Init: (%s) Unable to create SSL Proxy context", cpVHostID);
313             ssl_die();
314         }
315         sc->pSSLProxyCtx = ctx;
316         SSL_CTX_set_options(ctx, SSL_OP_ALL);
317         if (!(sc->nProxyProtocol & SSL_PROTOCOL_SSLV2))
318             SSL_CTX_set_options(ctx, SSL_OP_NO_SSLv2);
319         if (!(sc->nProxyProtocol & SSL_PROTOCOL_SSLV3))
320             SSL_CTX_set_options(ctx, SSL_OP_NO_SSLv3);
321         if (!(sc->nProxyProtocol & SSL_PROTOCOL_TLSV1))
322             SSL_CTX_set_options(ctx, SSL_OP_NO_TLSv1);
323 
324         if (sc->szProxyClientCertificateFile || sc->szProxyClientCertificatePath) {
325             sk = sk_X509_INFO_new_null();
326             if (sc->szProxyClientCertificateFile)
327                 SSL_load_CrtAndKeyInfo_file(p, sk, sc->szProxyClientCertificateFile);
328             if (sc->szProxyClientCertificatePath)
329                 SSL_load_CrtAndKeyInfo_path(p, sk, sc->szProxyClientCertificatePath);
330             ssl_log(s, SSL_LOG_TRACE, "Init: (%s) loaded %d client certs for SSL proxy",
331                     cpVHostID, sk_X509_INFO_num(sk));
332             if (sk_X509_INFO_num(sk) > 0) {
333                 SSL_CTX_set_client_cert_cb(ctx, ssl_ext_mp_clientcert_cb);
334                 sc->skProxyClientCerts = sk;
335             }
336         }
337 
338         /*
339          * Calculate OpenSSL verify type for verifying the remote server
340          * certificate. We either verify it against our list of CA's, or don't
341          * bother at all.
342          */
343         nVerify = SSL_VERIFY_NONE;
344         if (sc->bProxyVerify)
345             nVerify |= SSL_VERIFY_PEER;
346         if (   nVerify & SSL_VERIFY_PEER
347             && sc->szProxyCACertificateFile == NULL
348             && sc->szProxyCACertificatePath == NULL) {
349             ssl_log(s, SSL_LOG_ERROR,
350                     "Init: (%s) SSLProxyVerify set to On but no CA certificates configured",
351                     cpVHostID);
352             ssl_die();
353         }
354         if (   nVerify & SSL_VERIFY_NONE
355             && (   sc->szProxyCACertificateFile != NULL
356                 || sc->szProxyCACertificatePath != NULL)) {
357             ssl_log(s, SSL_LOG_WARN,
358                     "init: (%s) CA certificates configured but ignored because SSLProxyVerify is Off",
359                     cpVHostID);
360         }
361         SSL_CTX_set_verify(ctx, nVerify, ssl_ext_mp_verify_cb);
362 
363         /*
364          * Enable session caching. We can safely use the same cache
365          * as used for communicating with the other clients.
366          */
367         SSL_CTX_sess_set_new_cb(sc->pSSLProxyCtx,    ssl_callback_NewSessionCacheEntry);
368         SSL_CTX_sess_set_get_cb(sc->pSSLProxyCtx,    ssl_callback_GetSessionCacheEntry);
369         SSL_CTX_sess_set_remove_cb(sc->pSSLProxyCtx, ssl_callback_DelSessionCacheEntry);
370 
371         /*
372          *  Configure SSL Cipher Suite
373          */
374         ssl_log(s, SSL_LOG_TRACE,
375                 "Init: (%s) Configuring permitted SSL ciphers for SSL proxy", cpVHostID);
376         if (sc->szProxyCipherSuite != NULL) {
377             if (!SSL_CTX_set_cipher_list(sc->pSSLProxyCtx, sc->szProxyCipherSuite)) {
378                 ssl_log(s, SSL_LOG_ERROR|SSL_ADD_SSLERR,
379                         "Init: (%s) Unable to configure permitted SSL ciphers for SSL Proxy",
380                         cpVHostID);
381                 ssl_die();
382             }
383         }
384 
385         /*
386          * Configure Client Authentication details
387          */
388         if (sc->szProxyCACertificateFile != NULL || sc->szProxyCACertificatePath != NULL) {
389              ssl_log(s, SSL_LOG_DEBUG,
390                      "Init: (%s) Configuring client verification locations for SSL Proxy",
391                      cpVHostID);
392              if (!SSL_CTX_load_verify_locations(sc->pSSLProxyCtx,
393                                                 sc->szProxyCACertificateFile,
394                                                 sc->szProxyCACertificatePath)) {
395                  ssl_log(s, SSL_LOG_ERROR|SSL_ADD_SSLERR,
396                          "Init: (%s) Unable to configure SSL verify locations for SSL proxy",
397                          cpVHostID);
398                  ssl_die();
399              }
400         }
401     }
402     return;
403 }
404 #endif /* SSL_EXPERIMENTAL_PROXY */
405 
ssl_ext_mp_canon(request_rec * r,char * url)406 static int ssl_ext_mp_canon(request_rec *r, char *url)
407 {
408     int rc;
409 
410     if (strcEQn(url, "https:", 6)) {
411         rc = OK;
412         ap_hook_call("ap::mod_proxy::http::canon",
413                      &rc, r, url+6, "https", DEFAULT_HTTPS_PORT);
414         return rc;
415     }
416     return DECLINED;
417 }
418 
ssl_ext_mp_handler(request_rec * r,void * cr,char * url,char * proxyhost,int proxyport,char * protocol)419 static int ssl_ext_mp_handler(
420     request_rec *r, void *cr, char *url, char *proxyhost, int proxyport, char *protocol)
421 {
422     int rc;
423 
424     if (strcEQ(protocol, "https")) {
425         ap_ctx_set(r->ctx, "ssl::proxy::enabled", PTRUE);
426         ap_hook_call("ap::mod_proxy::http::handler",
427                      &rc, r, cr, url, proxyhost, proxyport);
428         return rc;
429     }
430     else {
431         ap_ctx_set(r->ctx, "ssl::proxy::enabled", PFALSE);
432     }
433     return DECLINED;
434 }
435 
ssl_ext_mp_set_destport(request_rec * r)436 static int ssl_ext_mp_set_destport(request_rec *r)
437 {
438     if (ap_ctx_get(r->ctx, "ssl::proxy::enabled") == PTRUE)
439         return DEFAULT_HTTPS_PORT;
440     else
441         return DEFAULT_HTTP_PORT;
442 }
443 
ssl_ext_mp_new_connection(request_rec * r,BUFF * fb,char * peer)444 static char *ssl_ext_mp_new_connection(request_rec *r, BUFF *fb, char *peer)
445 {
446 #ifndef SSL_EXPERIMENTAL_PROXY
447     SSL_CTX *ssl_ctx;
448 #endif
449     SSL *ssl;
450     char *errmsg;
451     int rc;
452     char *cpVHostID;
453     char *cpVHostMD5;
454 #ifdef SSL_EXPERIMENTAL_PROXY
455     SSLSrvConfigRec *sc;
456     char *cp;
457 #endif
458 
459     if (ap_ctx_get(r->ctx, "ssl::proxy::enabled") == PFALSE)
460         return NULL;
461 
462     /*
463      * Find context
464      */
465 #ifdef SSL_EXPERIMENTAL_PROXY
466     sc = mySrvConfig(r->server);
467 #endif
468     cpVHostID = ssl_util_vhostid(r->pool, r->server);
469 
470     /*
471      * Create a SSL context and handle
472      */
473 #ifdef SSL_EXPERIMENTAL_PROXY
474     ssl = SSL_new(sc->pSSLProxyCtx);
475 #else
476     ssl_ctx = SSL_CTX_new(SSLv23_client_method());
477     ssl = SSL_new(ssl_ctx);
478 #endif
479     if (ssl == NULL) {
480         errmsg = ap_psprintf(r->pool, "SSL proxy new failed (%s): peer %s: %s",
481                              cpVHostID, peer, ERR_reason_error_string(ERR_get_error()));
482         ap_ctx_set(fb->ctx, "ssl", NULL);
483         return errmsg;
484     }
485     SSL_clear(ssl);
486     cpVHostMD5 = ap_md5(r->pool, (unsigned char *)cpVHostID);
487     if (!SSL_set_session_id_context(ssl, (unsigned char *)cpVHostMD5, strlen(cpVHostMD5))) {
488         errmsg = ap_psprintf(r->pool, "Unable to set session id context to `%s': peer %s: %s",
489                              cpVHostMD5, peer, ERR_reason_error_string(ERR_get_error()));
490         ap_ctx_set(fb->ctx, "ssl", NULL);
491         return errmsg;
492     }
493     SSL_set_fd(ssl, fb->fd);
494 #ifdef SSL_EXPERIMENTAL_PROXY
495     SSL_set_app_data(ssl, fb->ctx);
496 #endif
497     ap_ctx_set(fb->ctx, "ssl", ssl);
498 #ifdef SSL_EXPERIMENTAL_PROXY
499     ap_ctx_set(fb->ctx, "ssl::proxy::server_rec", r->server);
500     ap_ctx_set(fb->ctx, "ssl::proxy::peer", peer);
501     ap_ctx_set(fb->ctx, "ssl::proxy::servername", cpVHostID);
502     ap_ctx_set(fb->ctx, "ssl::proxy::verifyerror", NULL);
503 #endif
504 
505     /*
506      * Give us a chance to gracefully close the connection
507      */
508     ap_register_cleanup(r->pool, (void *)fb,
509                         ssl_ext_mp_close_connection, ssl_ext_mp_close_connection);
510 
511     /*
512      * Establish the SSL connection
513      */
514     if ((rc = SSL_connect(ssl)) <= 0) {
515 #ifdef SSL_EXPERIMENTAL_PROXY
516         if ((cp = (char *)ap_ctx_get(fb->ctx, "ssl::proxy::verifyerror")) != NULL) {
517             SSL_set_shutdown(ssl, SSL_RECEIVED_SHUTDOWN);
518             SSL_smart_shutdown(ssl);
519             SSL_free(ssl);
520             ap_ctx_set(fb->ctx, "ssl", NULL);
521             ap_bsetflag(fb, B_EOF|B_EOUT, 1);
522             return NULL;
523         }
524 #endif
525         errmsg = ap_psprintf(r->pool, "SSL proxy connect failed (%s): peer %s: %s",
526                              cpVHostID, peer, ERR_reason_error_string(ERR_get_error()));
527         ssl_log(r->server, SSL_LOG_ERROR, "%s", errmsg);
528         SSL_free(ssl);
529         ap_ctx_set(fb->ctx, "ssl", NULL);
530         return errmsg;
531     }
532 
533     return NULL;
534 }
535 
ssl_ext_mp_close_connection(void * _fb)536 static void ssl_ext_mp_close_connection(void *_fb)
537 {
538     BUFF *fb = _fb;
539     SSL *ssl;
540 #ifndef SSL_EXPERIMENTAL_PROXY
541     SSL_CTX *ctx;
542 #endif
543 
544     ssl = ap_ctx_get(fb->ctx, "ssl");
545     if (ssl != NULL) {
546 #ifndef SSL_EXPERIMENTAL_PROXY
547         ctx = SSL_get_SSL_CTX(ssl);
548 #endif
549         SSL_set_shutdown(ssl, SSL_SENT_SHUTDOWN|SSL_RECEIVED_SHUTDOWN);
550         SSL_smart_shutdown(ssl);
551         SSL_free(ssl);
552         ap_ctx_set(fb->ctx, "ssl", NULL);
553 #ifndef SSL_EXPERIMENTAL_PROXY
554         if (ctx != NULL)
555             SSL_CTX_free(ctx);
556 #endif
557     }
558     return;
559 }
560 
ssl_ext_mp_write_host_header(request_rec * r,BUFF * fb,char * host,int port,char * portstr)561 static int ssl_ext_mp_write_host_header(
562     request_rec *r, BUFF *fb, char *host, int port, char *portstr)
563 {
564     if (ap_ctx_get(r->ctx, "ssl::proxy::enabled") == PFALSE)
565         return DECLINED;
566 
567     if (portstr != NULL && port != DEFAULT_HTTPS_PORT) {
568         ap_bvputs(fb, "Host: ", host, ":", portstr, "\r\n", NULL);
569         return OK;
570     }
571     return DECLINED;
572 }
573 
574 #ifdef SSL_EXPERIMENTAL_PROXY
575 
576 /*
577  * Callback for client certificate stuff.
578  * If the remote site sent us a SSLv3 list of acceptable CA's then trawl the
579  * table of client certs and send the first one that matches.
580  */
ssl_ext_mp_clientcert_cb(SSL * ssl,X509 ** x509,EVP_PKEY ** pkey)581 static int ssl_ext_mp_clientcert_cb(SSL *ssl, X509 **x509, EVP_PKEY **pkey)
582 {
583     SSLSrvConfigRec *sc;
584     X509_NAME *xnx;
585     X509_NAME *issuer;
586     X509_INFO *xi;
587     char *peer;
588     char *servername;
589     server_rec *s;
590     ap_ctx *pCtx;
591     STACK_OF(X509_NAME) *sk;
592     STACK_OF(X509_INFO) *pcerts;
593     char *cp;
594     int i, j;
595 
596     pCtx       = (ap_ctx *)SSL_get_app_data(ssl);
597     s          = ap_ctx_get(pCtx, "ssl::proxy::server_rec");
598     peer       = ap_ctx_get(pCtx, "ssl::proxy::peer");
599     servername = ap_ctx_get(pCtx, "ssl::proxy::servername");
600 
601     sc         = mySrvConfig(s);
602     pcerts     = sc->skProxyClientCerts;
603 
604     ssl_log(s, SSL_LOG_DEBUG,
605             "Proxy client certificate callback: (%s) entered", servername);
606 
607     if ((pcerts == NULL) || (sk_X509_INFO_num(pcerts) <= 0)) {
608         ssl_log(s, SSL_LOG_TRACE,
609                 "Proxy client certificate callback: (%s) "
610                 "site wanted client certificate but none available",
611                 servername);
612         return 0;
613     }
614 
615     sk = SSL_get_client_CA_list(ssl);
616 
617     if ((sk == NULL) || (sk_X509_NAME_num(sk) <= 0)) {
618         /*
619          * remote site didn't send us a list of acceptable CA certs,
620          * so lets send the first one we came across
621          */
622         xi = sk_X509_INFO_value(pcerts, 0);
623         cp = X509_NAME_oneline(X509_get_subject_name(xi->x509), NULL, 0);
624         ssl_log(s, SSL_LOG_DEBUG,
625                 "SSL Proxy: (%s) no acceptable CA list, sending %s",
626                 servername, cp != NULL ? cp : "-unknown-");
627         OPENSSL_free(cp);
628         /* export structures to the caller */
629         *x509 = xi->x509;
630         *pkey = xi->x_pkey->dec_pkey;
631         /* prevent OpenSSL freeing these structures */
632         CRYPTO_add(&((*x509)->references), +1, CRYPTO_LOCK_X509_PKEY);
633         CRYPTO_add(&((*pkey)->references), +1, CRYPTO_LOCK_X509_PKEY);
634         return 1;
635     }
636 
637     for (i = 0; i < sk_X509_NAME_num(sk); i++) {
638         xnx = sk_X509_NAME_value(sk, i);
639         for (j = 0; j < sk_X509_INFO_num(pcerts); j++) {
640             xi = sk_X509_INFO_value(pcerts,j);
641             issuer = X509_get_issuer_name(xi->x509);
642             if (X509_NAME_cmp(issuer, xnx) == 0) {
643                 cp = X509_NAME_oneline(X509_get_subject_name(xi->x509), NULL, 0);
644                 ssl_log(s, SSL_LOG_DEBUG, "SSL Proxy: (%s) sending %s",
645                         servername, cp != NULL ? cp : "-unknown-");
646                 OPENSSL_free(cp);
647                 /* export structures to the caller */
648                 *x509 = xi->x509;
649                 *pkey = xi->x_pkey->dec_pkey;
650                 /* prevent OpenSSL freeing these structures */
651                 CRYPTO_add(&((*x509)->references), +1, CRYPTO_LOCK_X509_PKEY);
652                 CRYPTO_add(&((*pkey)->references), +1, CRYPTO_LOCK_X509_PKEY);
653                 return 1;
654             }
655         }
656     }
657     ssl_log(s, SSL_LOG_TRACE,
658             "Proxy client certificate callback: (%s) "
659             "no client certificate found!?", servername);
660     return 0;
661 }
662 
663 /*
664  * This is the verify callback when we are connecting to a remote SSL server
665  * from the proxy. Information is passed in via the SSL "ctx" app_data
666  * mechanism. We pass in an Apache context in this field, which contains
667  * server_rec of the server making the proxy connection from the
668  * "ssl::proxy::server_rec" context.
669  *
670  * The result of the verification is passed back out to SSLERR via the return
671  * value. We also store the error message in the "proxyverifyfailed" context,
672  * so the caller of SSL_connect() can log a detailed error message.
673  */
ssl_ext_mp_verify_cb(int ok,X509_STORE_CTX * ctx)674 static int ssl_ext_mp_verify_cb(int ok, X509_STORE_CTX *ctx)
675 {
676     SSLSrvConfigRec *sc;
677     X509 *xs;
678     int errnum;
679     int errdepth;
680     char *cp, *cp2;
681     ap_ctx *pCtx;
682     server_rec *s;
683     SSL *ssl;
684     char *peer;
685     char *servername;
686 
687     ssl        = (SSL *)X509_STORE_CTX_get_app_data(ctx);
688     pCtx       = (ap_ctx *)SSL_get_app_data(ssl);
689     s          = ap_ctx_get(pCtx, "ssl::proxy::server_rec");
690     peer       = ap_ctx_get(pCtx, "ssl::proxy::peer");
691     servername = ap_ctx_get(pCtx, "ssl::proxy::servername");
692     sc         = mySrvConfig(s);
693 
694     /*
695      * Unless stated otherwise by the configuration, we really don't
696      * care if the verification was okay or not, so lets return now
697      * before we do anything involving memory or time.
698      */
699     if (sc->bProxyVerify == FALSE)
700         return ok;
701 
702     /*
703      * Get verify ingredients
704      */
705     xs       = X509_STORE_CTX_get_current_cert(ctx);
706     errnum   = X509_STORE_CTX_get_error(ctx);
707     errdepth = X509_STORE_CTX_get_error_depth(ctx);
708 
709     /*
710      * Log verification information
711      */
712     cp  = X509_NAME_oneline(X509_get_subject_name(xs), NULL, 0);
713     cp2 = X509_NAME_oneline(X509_get_issuer_name(xs),  NULL, 0);
714     ssl_log(s, SSL_LOG_DEBUG,
715             "SSL Proxy: (%s) Certificate Verification for remote server %s: "
716             "depth: %d, subject: %s, issuer: %s",
717             servername, peer != NULL ? peer : "-unknown-",
718             errdepth, cp != NULL ? cp : "-unknown-",
719             cp2 != NULL ? cp2 : "-unknown");
720     OPENSSL_free(cp);
721     OPENSSL_free(cp2);
722 
723     /*
724      * If we already know it's not ok, log the real reason
725      */
726     if (!ok) {
727         ssl_log(s, SSL_LOG_ERROR,
728                 "SSL Proxy: (%s) Certificate Verification failed for %s: "
729                 "Error (%d): %s", servername,
730                 peer != NULL ? peer : "-unknown-",
731                 errnum, X509_verify_cert_error_string(errnum));
732         ap_ctx_set(pCtx, "ssl::proxy::verifyerror",
733                    (void *)X509_verify_cert_error_string(errnum));
734         return ok;
735     }
736 
737     /*
738      * Check the depth of the certificate chain
739      */
740     if (sc->nProxyVerifyDepth > 0) {
741         if (errdepth > sc->nProxyVerifyDepth) {
742             ssl_log(s, SSL_LOG_ERROR,
743                 "SSL Proxy: (%s) Certificate Verification failed for %s: "
744                 "Certificate Chain too long "
745                 "(chain has %d certificates, but maximum allowed are only %d)",
746                 servername, peer, errdepth, sc->nProxyVerifyDepth);
747             ap_ctx_set(pCtx, "ssl::proxy::verifyerror",
748                        (void *)X509_verify_cert_error_string(X509_V_ERR_CERT_CHAIN_TOO_LONG));
749             ok = FALSE;
750         }
751     }
752 
753     /*
754      * And finally signal OpenSSL the (perhaps changed) state
755      */
756     return (ok);
757 }
758 
759 #endif /* SSL_EXPERIMENTAL_PROXY */
760 
761 /*  _________________________________________________________________
762 **
763 **  SSL Extension to mod_status
764 **  _________________________________________________________________
765 */
766 
767 static void ssl_ext_ms_display(request_rec *, int, int);
768 
ssl_ext_ms_register(void)769 static void ssl_ext_ms_register(void)
770 {
771     ap_hook_register("ap::mod_status::display", ssl_ext_ms_display, AP_HOOK_NOCTX);
772     return;
773 }
774 
ssl_ext_ms_unregister(void)775 static void ssl_ext_ms_unregister(void)
776 {
777     ap_hook_unregister("ap::mod_status::display", ssl_ext_ms_display);
778     return;
779 }
780 
ssl_ext_ms_display_cb(char * str,void * _r)781 static void ssl_ext_ms_display_cb(char *str, void *_r)
782 {
783     request_rec *r = (request_rec *)_r;
784     if (str != NULL)
785         ap_rputs(str, r);
786     return;
787 }
788 
ssl_ext_ms_display(request_rec * r,int no_table_report,int short_report)789 static void ssl_ext_ms_display(request_rec *r, int no_table_report, int short_report)
790 {
791     SSLSrvConfigRec *sc = mySrvConfig(r->server);
792 
793     if (sc == NULL)
794         return;
795     if (short_report)
796         return;
797     ap_rputs("<hr>\n", r);
798     ap_rputs("<table cellspacing=0 cellpadding=0>\n", r);
799     ap_rputs("<tr><td bgcolor=\"#000000\">\n", r);
800     ap_rputs("<b><font color=\"#ffffff\" face=\"Arial,Helvetica\">SSL/TLS Session Cache Status:</font></b>\r", r);
801     ap_rputs("</td></tr>\n", r);
802     ap_rputs("<tr><td bgcolor=\"#ffffff\">\n", r);
803     ssl_scache_status(r->server, r->pool, ssl_ext_ms_display_cb, r);
804     ap_rputs("</td></tr>\n", r);
805     ap_rputs("</table>\n", r);
806     return;
807 }
808 
809