summaryrefslogtreecommitdiffstats
path: root/usr.sbin/httpd/src/modules/ssl/ssl_engine_kernel.c
diff options
context:
space:
mode:
authorbeck <beck@openbsd.org>1999-09-29 06:28:49 +0000
committerbeck <beck@openbsd.org>1999-09-29 06:28:49 +0000
commita5311d6855861e61375991d1275235e931299ca4 (patch)
treef44f3e07a9dd26d791b37aaf78429c2019699036 /usr.sbin/httpd/src/modules/ssl/ssl_engine_kernel.c
parentadd -V option (diff)
downloadwireguard-openbsd-a5311d6855861e61375991d1275235e931299ca4.tar.xz
wireguard-openbsd-a5311d6855861e61375991d1275235e931299ca4.zip
Apache 1.3.9 + Mod_ssl 2.4.2 - now builds with apaci nastiness.
Diffstat (limited to 'usr.sbin/httpd/src/modules/ssl/ssl_engine_kernel.c')
-rw-r--r--usr.sbin/httpd/src/modules/ssl/ssl_engine_kernel.c1056
1 files changed, 806 insertions, 250 deletions
diff --git a/usr.sbin/httpd/src/modules/ssl/ssl_engine_kernel.c b/usr.sbin/httpd/src/modules/ssl/ssl_engine_kernel.c
index 99b19843c07..3addfce1275 100644
--- a/usr.sbin/httpd/src/modules/ssl/ssl_engine_kernel.c
+++ b/usr.sbin/httpd/src/modules/ssl/ssl_engine_kernel.c
@@ -1,8 +1,8 @@
/* _ _
-** _ __ ___ ___ __| | ___ ___| |
-** | '_ ` _ \ / _ \ / _` | / __/ __| |
-** | | | | | | (_) | (_| | \__ \__ \ | mod_ssl - Apache Interface to SSLeay
-** |_| |_| |_|\___/ \__,_|___|___/___/_| http://www.engelschall.com/sw/mod_ssl/
+** _ __ ___ ___ __| | ___ ___| | mod_ssl
+** | '_ ` _ \ / _ \ / _` | / __/ __| | Apache Interface to OpenSSL
+** | | | | | | (_) | (_| | \__ \__ \ | www.modssl.org
+** |_| |_| |_|\___/ \__,_|___|___/___/_| ftp.modssl.org
** |_____|
** ssl_engine_kernel.c
** The SSL engine kernel
@@ -27,7 +27,7 @@
* software must display the following acknowledgment:
* "This product includes software developed by
* Ralf S. Engelschall <rse@engelschall.com> for use in the
- * mod_ssl project (http://www.engelschall.com/sw/mod_ssl/)."
+ * mod_ssl project (http://www.modssl.org/)."
*
* 4. The names "mod_ssl" must not be used to endorse or promote
* products derived from this software without prior written
@@ -42,7 +42,7 @@
* acknowledgment:
* "This product includes software developed by
* Ralf S. Engelschall <rse@engelschall.com> for use in the
- * mod_ssl project (http://www.engelschall.com/sw/mod_ssl/)."
+ * mod_ssl project (http://www.modssl.org/)."
*
* THIS SOFTWARE IS PROVIDED BY RALF S. ENGELSCHALL ``AS IS'' AND ANY
* EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
@@ -130,8 +130,11 @@ void ssl_hook_NewConnection(conn_rec *conn)
server_rec *srvr;
BUFF *fb;
SSLSrvConfigRec *sc;
+ ap_ctx *apctx;
SSL *ssl;
char *cp;
+ char *cpVHostID;
+ X509 *xs;
int rc;
int n;
@@ -158,8 +161,9 @@ void ssl_hook_NewConnection(conn_rec *conn)
* Remember the connection information for
* later access inside callback functions
*/
+ cpVHostID = ssl_util_vhostid(conn->pool, srvr);
ssl_log(srvr, SSL_LOG_INFO, "Connection to child %d established (server %s)",
- conn->child_num, ssl_util_vhostid(conn->pool, srvr));
+ conn->child_num, cpVHostID);
/*
* Seed the Pseudo Random Number Generator (PRNG)
@@ -172,54 +176,41 @@ void ssl_hook_NewConnection(conn_rec *conn)
* attach this to the socket. Additionally we register this attachment
* so we can detach later.
*/
- ssl = SSL_new(sc->pSSLCtx);
- SSL_set_app_data(ssl, conn); /* conn_rec (available now) */
- SSL_set_app_data2(ssl, NULL); /* request_rec (available later) */
+ if ((ssl = SSL_new(sc->pSSLCtx)) == NULL) {
+ ssl_log(conn->server, SSL_LOG_ERROR|SSL_ADD_SSLERR,
+ "Unable to create a new SSL connection from the SSL context");
+ ap_ctx_set(fb->ctx, "ssl", NULL);
+ ap_bsetflag(fb, B_EOF|B_EOUT, 1);
+ conn->aborted = 1;
+ return;
+ }
+ SSL_clear(ssl);
+ SSL_set_session_id_context(ssl, (unsigned char *)cpVHostID, strlen(cpVHostID));
+ SSL_set_app_data(ssl, conn);
+ apctx = ap_ctx_new(conn->pool);
+ ap_ctx_set(apctx, "ssl::request_rec", NULL);
+ ap_ctx_set(apctx, "ssl::verify::depth", AP_CTX_NUM2PTR(0));
+ SSL_set_app_data2(ssl, apctx);
SSL_set_fd(ssl, fb->fd);
ap_ctx_set(fb->ctx, "ssl", ssl);
- ap_register_cleanup(conn->pool, (void *)conn,
- ssl_hook_CloseConnection, ssl_hook_CloseConnection);
/*
- * Configure SSLeay BIO Data Logging
+ * Configure callbacks for SSL connection
*/
+ SSL_set_tmp_rsa_callback(ssl, ssl_callback_TmpRSA);
+ SSL_set_tmp_dh_callback(ssl, ssl_callback_TmpDH);
if (sc->nLogLevel >= SSL_LOG_DEBUG) {
BIO_set_callback(SSL_get_rbio(ssl), ssl_io_data_cb);
BIO_set_callback_arg(SSL_get_rbio(ssl), ssl);
}
/*
- * Configure the server certificate and private key
- * which should be used for this connection.
- */
- if (sc->szCertificateFile != NULL) {
- if (SSL_use_certificate(ssl, sc->px509Certificate) <= 0) {
- ssl_log(conn->server, SSL_LOG_ERROR|SSL_ADD_SSLERR,
- "Unable to configure server certificate for connection");
- SSL_free(ssl);
- ap_ctx_set(fb->ctx, "ssl", NULL);
- ap_bsetflag(fb, B_EOF|B_EOUT, 1);
- conn->aborted = 1;
- return;
- }
- if (SSL_use_RSAPrivateKey(ssl, sc->prsaKey) <= 0) {
- ssl_log(conn->server, SSL_LOG_ERROR|SSL_ADD_SSLERR,
- "Unable to configure server private key for connection");
- SSL_free(ssl);
- ap_ctx_set(fb->ctx, "ssl", NULL);
- ap_bsetflag(fb, B_EOF|B_EOUT, 1);
- conn->aborted = 1;
- return;
- }
- }
-
- /*
* Predefine some client verification results
*/
ap_ctx_set(fb->ctx, "ssl::client::dn", NULL);
ap_ctx_set(fb->ctx, "ssl::verify::error", NULL);
- /*
+ /*
* We have to manage a I/O timeout ourself, because Apache
* does it the first time when reading the request, but we're
* working some time before this happens.
@@ -243,7 +234,7 @@ void ssl_hook_NewConnection(conn_rec *conn)
ssl_log(srvr, SSL_LOG_INFO,
"SSL handshake stopped: connection was closed");
SSL_set_shutdown(ssl, SSL_RECEIVED_SHUTDOWN);
- while (!SSL_shutdown(ssl));
+ SSL_smart_shutdown(ssl);
SSL_free(ssl);
ap_ctx_set(fb->ctx, "ssl", NULL);
ap_bsetflag(fb, B_EOF|B_EOUT, 1);
@@ -252,16 +243,15 @@ void ssl_hook_NewConnection(conn_rec *conn)
}
else if (ERR_GET_REASON(ERR_peek_error()) == SSL_R_HTTP_REQUEST) {
/*
- * The case where SSLeay has recognized a HTTP request:
+ * The case where OpenSSL has recognized a HTTP request:
* This means the client speaks plain HTTP on our HTTPS
* port. Hmmmm... At least for this error we can be more friendly
* and try to provide him with a HTML error page. We have only one
- * problem: SSLeay has already read some bytes from the HTTP
+ * problem: OpenSSL has already read some bytes from the HTTP
* request. So we have to skip the request line manually and
* instead provide a faked one in order to continue the internal
* Apache processing.
*
- * (This feature is only available for SSLeay 0.9.0 and higher)
*/
char ca[2];
int rv;
@@ -287,7 +277,7 @@ void ssl_hook_NewConnection(conn_rec *conn)
/* third: kick away the SSL stuff */
SSL_set_shutdown(ssl, SSL_SENT_SHUTDOWN|SSL_RECEIVED_SHUTDOWN);
- while (!SSL_shutdown(ssl));
+ SSL_smart_shutdown(ssl);
SSL_free(ssl);
ap_ctx_set(fb->ctx, "ssl", NULL);
@@ -295,11 +285,11 @@ void ssl_hook_NewConnection(conn_rec *conn)
return;
}
else if (ap_ctx_get(ap_global_ctx, "ssl::handshake::timeout") == (void *)TRUE) {
- ssl_log(srvr, SSL_LOG_ERROR,
+ ssl_log(srvr, SSL_LOG_ERROR,
"SSL handshake timed out (client %s, server %s)",
- conn->remote_ip, ssl_util_vhostid(conn->pool, srvr));
+ conn->remote_ip, cpVHostID);
SSL_set_shutdown(ssl, SSL_RECEIVED_SHUTDOWN);
- while (!SSL_shutdown(ssl));
+ SSL_smart_shutdown(ssl);
SSL_free(ssl);
ap_ctx_set(fb->ctx, "ssl", NULL);
ap_bsetflag(fb, B_EOF|B_EOUT, 1);
@@ -309,10 +299,16 @@ void ssl_hook_NewConnection(conn_rec *conn)
else if (SSL_get_error(ssl, rc) == SSL_ERROR_SYSCALL) {
if (errno == EINTR)
continue;
- ssl_log(srvr, SSL_LOG_ERROR|SSL_ADD_ERRNO,
- "SSL handshake interrupted by system");
+ if (errno > 0)
+ ssl_log(srvr, SSL_LOG_ERROR|SSL_ADD_SSLERR|SSL_ADD_ERRNO,
+ "SSL handshake interrupted by system "
+ "[Hint: Stop button pressed in browser?!]");
+ else
+ ssl_log(srvr, SSL_LOG_INFO|SSL_ADD_SSLERR|SSL_ADD_ERRNO,
+ "Spurious SSL handshake interrupt"
+ "[Hint: Usually just one of those OpenSSL confusions!?]");
SSL_set_shutdown(ssl, SSL_RECEIVED_SHUTDOWN);
- while (!SSL_shutdown(ssl));
+ SSL_smart_shutdown(ssl);
SSL_free(ssl);
ap_ctx_set(fb->ctx, "ssl", NULL);
ap_bsetflag(fb, B_EOF|B_EOUT, 1);
@@ -325,7 +321,7 @@ void ssl_hook_NewConnection(conn_rec *conn)
*/
ssl_log(srvr, SSL_LOG_ERROR|SSL_ADD_SSLERR|SSL_ADD_ERRNO,
"SSL handshake failed (client %s, server %s)",
- conn->remote_ip, ssl_util_vhostid(conn->pool, srvr));
+ conn->remote_ip, cpVHostID);
/*
* try to gracefully shutdown the connection:
@@ -335,7 +331,7 @@ void ssl_hook_NewConnection(conn_rec *conn)
* - block the socket, so Apache cannot operate any more
*/
SSL_set_shutdown(ssl, SSL_RECEIVED_SHUTDOWN);
- while (!SSL_shutdown(ssl));
+ SSL_smart_shutdown(ssl);
SSL_free(ssl);
ap_ctx_set(fb->ctx, "ssl", NULL);
ap_bsetflag(fb, B_EOF|B_EOUT, 1);
@@ -351,7 +347,7 @@ void ssl_hook_NewConnection(conn_rec *conn)
ssl_log(srvr, SSL_LOG_ERROR|SSL_ADD_SSLERR,
"SSL client authentication failed: %s", cp);
SSL_set_shutdown(ssl, SSL_RECEIVED_SHUTDOWN);
- while (!SSL_shutdown(ssl));
+ SSL_smart_shutdown(ssl);
SSL_free(ssl);
ap_ctx_set(fb->ctx, "ssl", NULL);
ap_bsetflag(fb, B_EOF|B_EOUT, 1);
@@ -360,17 +356,12 @@ void ssl_hook_NewConnection(conn_rec *conn)
}
/*
- * Remember the peer certificate when
- * client authentication was done
+ * Remember the peer certificate's DN
*/
- if (sc->nVerifyClient != SSL_CVERIFY_NONE) {
- char *s;
- X509 *xs;
- if ((xs = SSL_get_peer_certificate(ssl)) != NULL) {
- s = X509_NAME_oneline(X509_get_subject_name(xs), NULL, 0);
- ap_ctx_set(fb->ctx, "ssl::client::dn", ap_pstrdup(conn->pool, s));
- free(s);
- }
+ if ((xs = SSL_get_peer_certificate(ssl)) != NULL) {
+ cp = X509_NAME_oneline(X509_get_subject_name(xs), NULL, 0);
+ ap_ctx_set(fb->ctx, "ssl::client::dn", ap_pstrdup(conn->pool, cp));
+ free(cp);
}
/*
@@ -382,7 +373,7 @@ void ssl_hook_NewConnection(conn_rec *conn)
ssl_log(srvr, SSL_LOG_ERROR,
"No acceptable peer certificate available");
SSL_set_shutdown(ssl, SSL_RECEIVED_SHUTDOWN);
- while (!SSL_shutdown(ssl));
+ SSL_smart_shutdown(ssl);
SSL_free(ssl);
ap_ctx_set(fb->ctx, "ssl", NULL);
ap_bsetflag(fb, B_EOF|B_EOUT, 1);
@@ -399,7 +390,7 @@ void ssl_hook_NewConnection(conn_rec *conn)
/*
* Improve I/O throughput by using
- * SSLeay's read-ahead functionality
+ * OpenSSL's read-ahead functionality
* (don't used under Win32, because
* there we use select())
*/
@@ -407,6 +398,12 @@ void ssl_hook_NewConnection(conn_rec *conn)
SSL_set_read_ahead(ssl, TRUE);
#endif
+#ifdef SSL_VENDOR
+ /* Allow vendors to do more things on connection time... */
+ ap_hook_use("ap::mod_ssl::vendor::new_connection",
+ AP_HOOK_SIG2(void,ptr), AP_HOOK_ALL, conn);
+#endif
+
return;
}
@@ -422,29 +419,89 @@ void ssl_hook_TimeoutConnection(int sig)
/*
* Close the SSL part of the socket connection
+ * (called immediately _before_ the socket is closed)
*/
-void ssl_hook_CloseConnection(void *_conn)
+void ssl_hook_CloseConnection(conn_rec *conn)
{
- conn_rec *conn = _conn;
SSL *ssl;
+ char *cpType;
+
+ ssl = ap_ctx_get(conn->client->ctx, "ssl");
+ if (ssl == NULL)
+ return;
/*
- * Optionally shutdown the SSL session
+ * First make sure that no more data is pending in Apache's BUFF,
+ * because when it's (implicitly) flushed later by the ap_bclose()
+ * calls of Apache it would lead to an I/O error in the browser due
+ * to the fact that the SSL layer was already removed by us.
+ */
+ ap_bflush(conn->client);
+
+ /*
+ * Now close the SSL layer of the connection. We've to take
+ * the TLSv1 standard into account here:
+ *
+ * | 7.2.1. Closure alerts
+ * |
+ * | The client and the server must share knowledge that the connection is
+ * | ending in order to avoid a truncation attack. Either party may
+ * | initiate the exchange of closing messages.
+ * |
+ * | close_notify
+ * | This message notifies the recipient that the sender will not send
+ * | any more messages on this connection. The session becomes
+ * | unresumable if any connection is terminated without proper
+ * | close_notify messages with level equal to warning.
+ * |
+ * | Either party may initiate a close by sending a close_notify alert.
+ * | Any data received after a closure alert is ignored.
+ * |
+ * | Each party is required to send a close_notify alert before closing
+ * | the write side of the connection. It is required that the other party
+ * | respond with a close_notify alert of its own and close down the
+ * | connection immediately, discarding any pending writes. It is not
+ * | required for the initiator of the close to wait for the responding
+ * | close_notify alert before closing the read side of the connection.
+ *
+ * This means we've to send a close notify message, but haven't to wait
+ * for the close notify of the client. Actually we cannot wait for the
+ * close notify of the client because some clients (including Netscape
+ * 4.x) don't send one, so we would hang.
*/
- ssl = ap_ctx_get(conn->client->ctx, "ssl");
- if (ssl != NULL) {
- SSL_set_shutdown(ssl, SSL_SENT_SHUTDOWN|SSL_RECEIVED_SHUTDOWN);
- while (!SSL_shutdown(ssl));
- SSL_free(ssl);
- ap_ctx_set(conn->client->ctx, "ssl", NULL);
- }
/*
- * And finally log the connection close
+ * exchange close notify messages, but allow the user
+ * to force the type of handshake via SetEnvIf directive
*/
- ssl_log(conn->server, SSL_LOG_INFO, "Connection to child %d closed (server %s)",
- conn->child_num, ssl_util_vhostid(conn->pool, conn->server));
+ if (ap_ctx_get(conn->client->ctx, "ssl::flag::unclean-shutdown") == PTRUE) {
+ /* perform no close notify handshake at all
+ (violates the SSL/TLS standard!) */
+ SSL_set_shutdown(ssl, SSL_SENT_SHUTDOWN|SSL_RECEIVED_SHUTDOWN);
+ cpType = "unclean";
+ }
+ else if (ap_ctx_get(conn->client->ctx, "ssl::flag::accurate-shutdown") == PTRUE) {
+ /* send close notify and wait for clients close notify
+ (standard compliant, but usually causes connection hangs) */
+ SSL_set_shutdown(ssl, 0);
+ cpType = "accurate";
+ }
+ else {
+ /* send close notify, but don't wait for clients close notify
+ (standard compliant and safe, so it's the DEFAULT!) */
+ SSL_set_shutdown(ssl, SSL_RECEIVED_SHUTDOWN);
+ cpType = "standard";
+ }
+ SSL_smart_shutdown(ssl);
+
+ /* deallocate the SSL connection */
+ SSL_free(ssl);
+ ap_ctx_set(conn->client->ctx, "ssl", NULL);
+ /* and finally log the fact that we've closed the connection */
+ ssl_log(conn->server, SSL_LOG_INFO,
+ "Connection to child %d closed with %s shutdown (server %s)",
+ conn->child_num, cpType, ssl_util_vhostid(conn->pool, conn->server));
return;
}
@@ -454,14 +511,17 @@ void ssl_hook_CloseConnection(void *_conn)
int ssl_hook_ReadReq(request_rec *r)
{
SSL *ssl;
+ ap_ctx *apctx;
/*
- * Get the SSL connection structure and perform the
+ * Get the SSL connection structure and perform the
* delayed interlinking from SSL back to request_rec
*/
ssl = ap_ctx_get(r->connection->client->ctx, "ssl");
- if (ssl != NULL)
- SSL_set_app_data2(ssl, r);
+ if (ssl != NULL) {
+ apctx = SSL_get_app_data2(ssl);
+ ap_ctx_set(apctx, "ssl::request_rec", r);
+ }
/*
* Force the mod_ssl content handler when URL indicates this
@@ -480,6 +540,43 @@ int ssl_hook_ReadReq(request_rec *r)
}
/*
+ * URL Translation Handler
+ */
+int ssl_hook_Translate(request_rec *r)
+{
+ if (ap_ctx_get(r->connection->client->ctx, "ssl") == NULL)
+ return DECLINED;
+
+ /*
+ * Log information about incoming HTTPS requests
+ */
+ if (ap_is_initial_req(r))
+ ssl_log(r->server, SSL_LOG_INFO,
+ "%s HTTPS request received for child %d (server %s)",
+ r->connection->keepalives <= 0 ?
+ "Initial (No.1)" :
+ ap_psprintf(r->pool, "Subsequent (No.%d)",
+ r->connection->keepalives+1),
+ r->connection->child_num,
+ ssl_util_vhostid(r->pool, r->server));
+
+ /*
+ * Move SetEnvIf information from request_rec to conn_rec/BUFF
+ * to allow the close connection handler to use them.
+ */
+ if (ap_table_get(r->subprocess_env, "ssl-unclean-shutdown") != NULL)
+ ap_ctx_set(r->connection->client->ctx, "ssl::flag::unclean-shutdown", PTRUE);
+ else
+ ap_ctx_set(r->connection->client->ctx, "ssl::flag::unclean-shutdown", PFALSE);
+ if (ap_table_get(r->subprocess_env, "ssl-accurate-shutdown") != NULL)
+ ap_ctx_set(r->connection->client->ctx, "ssl::flag::accurate-shutdown", PTRUE);
+ else
+ ap_ctx_set(r->connection->client->ctx, "ssl::flag::accurate-shutdown", PFALSE);
+
+ return DECLINED;
+}
+
+/*
* Content Handler
*/
int ssl_hook_Handler(request_rec *r)
@@ -510,55 +607,6 @@ int ssl_hook_Handler(request_rec *r)
}
/*
- * Auth Handler:
- * Fake a Basic authentication from the X509 client certificate.
- *
- * This must be run fairly early on to prevent a real authentication from
- * occuring, in particular it must be run before anything else that
- * authenticates a user. This means that the Module statement for this
- * module should be LAST in the Configuration file.
- */
-int ssl_hook_Auth(request_rec *r)
-{
- SSLSrvConfigRec *sc = mySrvConfig(r->server);
- SSLDirConfigRec *dc = myDirConfig(r);
- char b1[MAX_STRING_LEN], b2[MAX_STRING_LEN];
- char *clientdn;
-
- /*
- * We decline operation in various situations..
- */
- if (!sc->bEnabled)
- return DECLINED;
- if (ap_ctx_get(r->connection->client->ctx, "ssl") == NULL)
- return DECLINED;
- if (!(dc->nOptions & SSL_OPT_FAKEBASICAUTH))
- return DECLINED;
- if (r->connection->user)
- return DECLINED;
- if ((clientdn = (char *)ap_ctx_get(r->connection->client->ctx, "ssl::client::dn")) == NULL)
- return DECLINED;
-
- /*
- * Fake a password - which one would be immaterial, as, it seems, an empty
- * password in the users file would match ALL incoming passwords, if only
- * we were using the standard crypt library routine. Unfortunately, SSLeay
- * "fixes" a "bug" in crypt and thus prevents blank passwords from
- * working. (IMHO what they really fix is a bug in the users of the code
- * - failing to program correctly for shadow passwords). We need,
- * therefore, to provide a password. This password can be matched by
- * adding the string "xxj31ZMTZzkVA" as the password in the user file.
- * This is just the crypted variant of the word "password" ;-)
- */
- ap_snprintf(b1, sizeof(b1), "%s:password", clientdn);
- ssl_util_uuencode(b2, b1, FALSE);
- ap_snprintf(b1, sizeof(b1), "Basic %s", b2);
- ap_table_set(r->headers_in, "Authorization", b1);
-
- return DECLINED;
-}
-
-/*
* Access Handler
*/
int ssl_hook_Access(request_rec *r)
@@ -566,25 +614,35 @@ int ssl_hook_Access(request_rec *r)
SSLDirConfigRec *dc;
SSLSrvConfigRec *sc;
SSL *ssl;
- SSL_CTX *ctx;
+ SSL_CTX *ctx = NULL;
array_header *apRequirement;
ssl_require_t *pRequirements;
ssl_require_t *pRequirement;
char *cp;
int ok;
int i;
- BOOL renegotiate;
+ BOOL renegotiate;
+ BOOL renegotiate_quick;
#ifdef SSL_EXPERIMENTAL
BOOL reconfigured_locations;
- STACK *skCAList;
+ STACK_OF(X509_NAME) *skCAList;
char *cpCAPath;
char *cpCAFile;
#endif
- STACK *skCipherOld;
- STACK *skCipher;
+ X509 *cert;
+ STACK_OF(X509) *certstack;
+ X509_STORE *certstore;
+ X509_STORE_CTX certstorectx;
+ int depth;
+ STACK_OF(SSL_CIPHER) *skCipherOld;
+ STACK_OF(SSL_CIPHER) *skCipher;
+ SSL_CIPHER *pCipher;
+ ap_ctx *apctx;
int nVerifyOld;
int nVerify;
int n;
+ void *vp;
+ int rc;
dc = myDirConfig(r);
sc = mySrvConfig(r->server);
@@ -597,6 +655,8 @@ int ssl_hook_Access(request_rec *r)
*/
if (dc->bSSLRequired && ssl == NULL) {
ap_log_reason("SSL connection required", r->filename, r);
+ /* remember forbidden access for strict require option */
+ ap_table_setn(r->notes, "ssl-access-forbidden", (void *)1);
return FORBIDDEN;
}
@@ -609,83 +669,154 @@ int ssl_hook_Access(request_rec *r)
return DECLINED;
/*
- * Support for per-directory SSL connection parameters.
- *
+ * Support for per-directory reconfigured SSL connection parameters.
+ *
* This is implemented by forcing an SSL renegotiation with the
* reconfigured parameter suite. But Apache's internal API processing
- * makes our life very hard, because when internal sub-requests occur we
- * nevertheless should avoid multiple unnecessary SSL handshakes (they
- * need network I/O and time to perform). But the optimization for
- * filtering out the unnecessary handshakes isn't such obvious.
- * Especially because while Apache is in its sub-request processing the
- * client could force additional handshakes, too. And these take place
- * perhaps without our notice. So the only possibility is to ask
- * SSLeay/OpenSSL whether the renegotiation has to be performed or not. It
- * has to performed when some parameters which were previously known (by
- * us) are not those we've now reconfigured (as known by SSLeay/OpenSSL).
- */
- renegotiate = FALSE;
+ * makes our life very hard here, because when internal sub-requests occur
+ * we nevertheless should avoid multiple unnecessary SSL handshakes (they
+ * require extra network I/O and especially time to perform).
+ *
+ * But the optimization for filtering out the unnecessary handshakes isn't
+ * obvious and trivial. Especially because while Apache is in its
+ * sub-request processing the client could force additional handshakes,
+ * too. And these take place perhaps without our notice. So the only
+ * possibility is to explicitly _ask_ OpenSSL whether the renegotiation
+ * has to be performed or not. It has to performed when some parameters
+ * which were previously known (by us) are not those we've now
+ * reconfigured (as known by OpenSSL) or (in optimized way) at least when
+ * the reconfigured parameter suite is stronger (more restrictions) than
+ * the currently active one.
+ */
+ renegotiate = FALSE;
+ renegotiate_quick = FALSE;
#ifdef SSL_EXPERIMENTAL
reconfigured_locations = FALSE;
#endif
- /*
- * override of SSLCipherSuite
+ /*
+ * Override of SSLCipherSuite
+ *
+ * We provide two options here:
+ *
+ * o The paranoid and default approach where we force a renegotiation when
+ * the cipher suite changed in _any_ way (which is straight-forward but
+ * often forces renegotiations too often and is perhaps not what the
+ * user actually wanted).
+ *
+ * o The optimized and still secure way where we force a renegotiation
+ * only if the currently active cipher is no longer contained in the
+ * reconfigured/new cipher suite. Any other changes are not important
+ * because it's the servers choice to select a cipher from the ones the
+ * client supports. So as long as the current cipher is still in the new
+ * cipher suite we're happy. Because we can assume we would have
+ * selected it again even when other (better) ciphers exists now in the
+ * new cipher suite. This approach is fine because the user explicitly
+ * has to enable this via ``SSLOptions +OptRenegotiate''. So we do no
+ * implicit optimizations.
*/
if (dc->szCipherSuite != NULL) {
- /* remember old cipher suite for comparison */
- if ((skCipherOld = SSL_get_ciphers(ssl)) != NULL)
- skCipherOld = sk_dup(skCipherOld);
- /* configure new cipher suite */
+ /* remember old state */
+ pCipher = NULL;
+ skCipherOld = NULL;
+ if (dc->nOptions & SSL_OPT_OPTRENEGOTIATE)
+ pCipher = SSL_get_current_cipher(ssl);
+ else {
+ skCipherOld = SSL_get_ciphers(ssl);
+ if (skCipherOld != NULL)
+ skCipherOld = sk_SSL_CIPHER_dup(skCipherOld);
+ }
+ /* configure new state */
if (!SSL_set_cipher_list(ssl, dc->szCipherSuite)) {
ssl_log(r->server, SSL_LOG_WARN|SSL_ADD_SSLERR,
"Unable to reconfigure (per-directory) permitted SSL ciphers");
+ if (skCipherOld != NULL)
+ sk_SSL_CIPHER_free(skCipherOld);
return FORBIDDEN;
}
- /* determine whether the cipher suite was actually changed */
+ /* determine whether a renegotiation has to be forced */
skCipher = SSL_get_ciphers(ssl);
- if ((skCipherOld == NULL && skCipher != NULL) ||
- (skCipherOld != NULL && skCipher == NULL) )
- renegotiate = TRUE;
- else if (skCipherOld != NULL && skCipher != NULL) {
- for (n = 0; n < sk_num(skCipher); n++) {
- if (sk_find(skCipherOld, sk_value(skCipher, n)) < 0) {
- renegotiate = TRUE;
- break;
- }
+ if (dc->nOptions & SSL_OPT_OPTRENEGOTIATE) {
+ /* optimized way */
+ if ((pCipher == NULL && skCipher != NULL) ||
+ (pCipher != NULL && skCipher == NULL) )
+ renegotiate = TRUE;
+ else if (pCipher != NULL && skCipher != NULL
+ && sk_SSL_CIPHER_find(skCipher, pCipher) < 0) {
+ renegotiate = TRUE;
}
- for (n = 0; n < sk_num(skCipherOld); n++) {
- if (sk_find(skCipher, sk_value(skCipherOld, n)) < 0) {
- renegotiate = TRUE;
- break;
+ }
+ else {
+ /* paranoid way */
+ if ((skCipherOld == NULL && skCipher != NULL) ||
+ (skCipherOld != NULL && skCipher == NULL) )
+ renegotiate = TRUE;
+ else if (skCipherOld != NULL && skCipher != NULL) {
+ for (n = 0; !renegotiate && n < sk_SSL_CIPHER_num(skCipher); n++) {
+ if (sk_SSL_CIPHER_find(skCipherOld, sk_SSL_CIPHER_value(skCipher, n)) < 0)
+ renegotiate = TRUE;
+ }
+ for (n = 0; !renegotiate && n < sk_SSL_CIPHER_num(skCipherOld); n++) {
+ if (sk_SSL_CIPHER_find(skCipher, sk_SSL_CIPHER_value(skCipherOld, n)) < 0)
+ renegotiate = TRUE;
}
}
}
- /* free old cipher suite */
+ /* cleanup */
if (skCipherOld != NULL)
- sk_free(skCipherOld);
+ sk_SSL_CIPHER_free(skCipherOld);
+ /* tracing */
+ if (renegotiate)
+ ssl_log(r->server, SSL_LOG_TRACE,
+ "Reconfigured cipher suite will force renegotiation");
}
/*
- * override of SSLVerifyDepth:
- * This is handled by us manually inside the verify callback
- * function and not by SSLeay internally. And our function is
- * aware of both the per-server and per-directory contexts.
- * All we have to do is to force the renegotiation when the
- * maximum allowed depth is changed.
+ * override of SSLVerifyDepth
+ *
+ * The depth checks are handled by us manually inside the verify callback
+ * function and not by OpenSSL internally (and our function is aware of
+ * both the per-server and per-directory contexts). So we cannot ask
+ * OpenSSL about the currently verify depth. Instead we remember it in our
+ * ap_ctx attached to the SSL* of OpenSSL. We've to force the
+ * renegotiation if the reconfigured/new verify depth is less than the
+ * currently active/remembered verify depth (because this means more
+ * restriction on the certificate chain).
*/
if (dc->nVerifyDepth != UNSET) {
- if (dc->nVerifyDepth != sc->nVerifyDepth)
+ apctx = SSL_get_app_data2(ssl);
+ if ((vp = ap_ctx_get(apctx, "ssl::verify::depth")) != NULL)
+ n = AP_CTX_PTR2NUM(vp);
+ else
+ n = sc->nVerifyDepth;
+ ap_ctx_set(apctx, "ssl::verify::depth",
+ AP_CTX_NUM2PTR(dc->nVerifyDepth));
+ /* determine whether a renegotiation has to be forced */
+ if (dc->nVerifyDepth < n) {
renegotiate = TRUE;
+ ssl_log(r->server, SSL_LOG_TRACE,
+ "Reduced client verification depth will force renegotiation");
+ }
}
- /*
+ /*
* override of SSLVerifyClient
+ *
+ * We force a renegotiation if the reconfigured/new verify type is
+ * stronger than the currently active verify type.
+ *
+ * The order is: none << optional_no_ca << optional << require
+ *
+ * Additionally the following optimization is possible here: When the
+ * currently active verify type is "none" but a client certificate is
+ * already known/present, it's enough to manually force a client
+ * verification but at least skip the I/O-intensive renegotation
+ * handshake.
*/
if (dc->nVerifyClient != SSL_CVERIFY_UNSET) {
- /* remember old verify mode */
+ /* remember old state */
nVerifyOld = SSL_get_verify_mode(ssl);
- /* configure new verify mode */
+ /* configure new state */
nVerify = SSL_VERIFY_NONE;
if (dc->nVerifyClient == SSL_CVERIFY_REQUIRE)
nVerify |= SSL_VERIFY_PEER|SSL_VERIFY_FAIL_IF_NO_PEER_CERT;
@@ -694,9 +825,25 @@ int ssl_hook_Access(request_rec *r)
nVerify |= SSL_VERIFY_PEER;
SSL_set_verify(ssl, nVerify, ssl_callback_SSLVerify);
SSL_set_verify_result(ssl, X509_V_OK);
- /* determine whether the verify mode was actually changed */
- if (nVerify != nVerifyOld)
- renegotiate = TRUE;
+ /* determine whether we've to force a renegotiation */
+ if (nVerify != nVerifyOld) {
+ if ( ( (nVerifyOld == SSL_VERIFY_NONE)
+ && (nVerify != SSL_VERIFY_NONE))
+ || ( !(nVerifyOld & SSL_VERIFY_PEER)
+ && (nVerify & SSL_VERIFY_PEER))
+ || ( !(nVerifyOld & (SSL_VERIFY_PEER|SSL_VERIFY_FAIL_IF_NO_PEER_CERT))
+ && (nVerify & (SSL_VERIFY_PEER|SSL_VERIFY_FAIL_IF_NO_PEER_CERT)))) {
+ renegotiate = TRUE;
+ /* optimization */
+ if ( dc->nOptions & SSL_OPT_OPTRENEGOTIATE
+ && nVerifyOld == SSL_VERIFY_NONE
+ && SSL_get_peer_certificate(ssl) != NULL)
+ renegotiate_quick = TRUE;
+ ssl_log(r->server, SSL_LOG_TRACE,
+ "Changed client verification type will force %srenegotiation",
+ renegotiate_quick ? "quick " : "");
+ }
+ }
}
/*
@@ -710,13 +857,13 @@ int ssl_hook_Access(request_rec *r)
* chance to provide this functionality...
*/
#ifdef SSL_EXPERIMENTAL
- if ( ( dc->szCACertificateFile != NULL
- && ( sc->szCACertificateFile == NULL
- || ( sc->szCACertificateFile != NULL
+ if ( ( dc->szCACertificateFile != NULL
+ && ( sc->szCACertificateFile == NULL
+ || ( sc->szCACertificateFile != NULL
&& strNE(dc->szCACertificateFile, sc->szCACertificateFile))))
- || ( dc->szCACertificatePath != NULL
- && ( sc->szCACertificatePath == NULL
- || ( sc->szCACertificatePath != NULL
+ || ( dc->szCACertificatePath != NULL
+ && ( sc->szCACertificatePath == NULL
+ || ( sc->szCACertificatePath != NULL
&& strNE(dc->szCACertificatePath, sc->szCACertificatePath)))) ) {
cpCAFile = dc->szCACertificateFile != NULL ?
dc->szCACertificateFile : sc->szCACertificateFile;
@@ -724,8 +871,8 @@ int ssl_hook_Access(request_rec *r)
dc->szCACertificatePath : sc->szCACertificatePath;
/*
FIXME: This should be...
- if (!SSL_load_verify_locations(ssl, cpCAFile, cpCAPath)) {
- ...but SSLeay/OpenSSL still doesn't provide this!
+ if (!SSL_load_verify_locations(ssl, cpCAFile, cpCAPath)) {
+ ...but OpenSSL still doesn't provide this!
*/
if (!SSL_CTX_load_verify_locations(ctx, cpCAFile, cpCAPath)) {
ssl_log(r->server, SSL_LOG_ERROR|SSL_ADD_SSLERR,
@@ -733,7 +880,7 @@ int ssl_hook_Access(request_rec *r)
"for client authentication");
return FORBIDDEN;
}
- if ((skCAList = ssl_init_FindCAList(r->server, r->pool,
+ if ((skCAList = ssl_init_FindCAList(r->server, r->pool,
cpCAFile, cpCAPath)) == NULL) {
ssl_log(r->server, SSL_LOG_ERROR,
"Unable to determine list of available "
@@ -743,17 +890,33 @@ int ssl_hook_Access(request_rec *r)
SSL_set_client_CA_list(ssl, skCAList);
renegotiate = TRUE;
reconfigured_locations = TRUE;
+ ssl_log(r->server, SSL_LOG_TRACE,
+ "Changed client verification locations will force renegotiation");
}
#endif /* SSL_EXPERIMENTAL */
+#ifndef SSL_EXPERIMENTAL
/*
+ * SSL renegotiations in conjunction with HTTP
+ * requests using the POST method are not supported.
+ */
+ if (renegotiate && r->method_number == M_POST) {
+ ssl_log(r->server, SSL_LOG_ERROR,
+ "SSL Re-negotiation in conjunction with POST method not supported!");
+ ssl_log(r->server, SSL_LOG_INFO,
+ "There is only experimental support which has to be enabled first");
+ return METHOD_NOT_ALLOWED;
+ }
+#endif /* not SSL_EXPERIMENTAL */
+
+ /*
* now do the renegotiation if anything was actually reconfigured
*/
if (renegotiate) {
- /*
+ /*
* Now we force the SSL renegotation by sending the Hello Request
* message to the client. Here we have to do a workaround: Actually
- * SSLeay returns immediately after sending the Hello Request (the
+ * OpenSSL returns immediately after sending the Hello Request (the
* intent AFAIK is because the SSL/TLS protocol says it's not a must
* that the client replies to a Hello Request). But because we insist
* on a reply (anything else is an error for us) we have to go to the
@@ -762,33 +925,83 @@ int ssl_hook_Access(request_rec *r)
* state explicitly and continue the handshake manually.
*/
ssl_log(r->server, SSL_LOG_INFO, "Requesting connection re-negotiation");
- SSL_renegotiate(ssl);
- SSL_do_handshake(ssl);
- if (SSL_get_state(ssl) != SSL_ST_OK) {
- ssl_log(r->server, SSL_LOG_ERROR, "Re-negotation request failed");
- return FORBIDDEN;
+ if (renegotiate_quick) {
+ /* perform just a manual re-verification of the peer */
+ ssl_log(r->server, SSL_LOG_TRACE,
+ "Performing quick renegotiation: just re-verifying the peer");
+ certstore = SSL_CTX_get_cert_store(ctx);
+ if (certstore == NULL) {
+ ssl_log(r->server, SSL_LOG_ERROR, "Cannot find certificate storage");
+ return FORBIDDEN;
+ }
+ certstack = SSL_get_peer_cert_chain(ssl);
+ if (certstack == NULL || sk_X509_num(certstack) == 0) {
+ ssl_log(r->server, SSL_LOG_ERROR, "Cannot find peer certificate chain");
+ return FORBIDDEN;
+ }
+ cert = sk_X509_value(certstack, 0);
+ X509_STORE_CTX_init(&certstorectx, certstore, cert, certstack);
+ depth = SSL_get_verify_depth(ssl);
+ if (depth >= 0)
+ X509_STORE_CTX_set_depth(&certstorectx, depth);
+ X509_STORE_CTX_set_ex_data(&certstorectx,
+ SSL_get_ex_data_X509_STORE_CTX_idx(), (char *)ssl);
+ if (!X509_verify_cert(&certstorectx))
+ ssl_log(r->server, SSL_LOG_ERROR|SSL_ADD_SSLERR,
+ "Re-negotiation verification step failed");
+ SSL_set_verify_result(ssl, certstorectx.error);
+ X509_STORE_CTX_cleanup(&certstorectx);
}
- ssl_log(r->server, SSL_LOG_INFO, "Awaiting re-negotiation handshake");
- SSL_set_state(ssl, SSL_ST_ACCEPT);
- SSL_do_handshake(ssl);
- if (SSL_get_state(ssl) != SSL_ST_OK) {
- ssl_log(r->server, SSL_LOG_ERROR,
- "Re-negotiation handshake failed: Not accepted by client!?");
- return FORBIDDEN;
+ else {
+ /* do a full renegotiation */
+ ssl_log(r->server, SSL_LOG_TRACE,
+ "Performing full renegotiation: complete handshake protocol");
+ if (r->main != NULL)
+ SSL_set_session_id_context(ssl, (unsigned char *)&(r->main), sizeof(r->main));
+ else
+ SSL_set_session_id_context(ssl, (unsigned char *)&r, sizeof(r));
+#ifdef SSL_EXPERIMENTAL
+ ssl_io_suck(r, ssl);
+#endif
+ SSL_renegotiate(ssl);
+ SSL_do_handshake(ssl);
+ if (SSL_get_state(ssl) != SSL_ST_OK) {
+ ssl_log(r->server, SSL_LOG_ERROR, "Re-negotiation request failed");
+ return FORBIDDEN;
+ }
+ ssl_log(r->server, SSL_LOG_INFO, "Awaiting re-negotiation handshake");
+ SSL_set_state(ssl, SSL_ST_ACCEPT);
+ SSL_do_handshake(ssl);
+ if (SSL_get_state(ssl) != SSL_ST_OK) {
+ ssl_log(r->server, SSL_LOG_ERROR,
+ "Re-negotiation handshake failed: Not accepted by client!?");
+ return FORBIDDEN;
+ }
+ }
+
+ /*
+ * Remember the peer certificate's DN
+ */
+ if ((cert = SSL_get_peer_certificate(ssl)) != NULL) {
+ cp = X509_NAME_oneline(X509_get_subject_name(cert), NULL, 0);
+ ap_ctx_set(r->connection->client->ctx, "ssl::client::dn",
+ ap_pstrdup(r->connection->pool, cp));
+ free(cp);
}
/*
* Finally check for acceptable renegotiation results
*/
if (dc->nVerifyClient != SSL_CVERIFY_NONE) {
- if (SSL_get_verify_result(ssl) != X509_V_OK) {
- ssl_log(r->server, SSL_LOG_ERROR,
+ if ( dc->nVerifyClient == SSL_CVERIFY_REQUIRE
+ && SSL_get_verify_result(ssl) != X509_V_OK ) {
+ ssl_log(r->server, SSL_LOG_ERROR,
"Re-negotiation handshake failed: Client verification failed");
return FORBIDDEN;
}
- if ( dc->nVerifyClient == SSL_CVERIFY_REQUIRE
+ if ( dc->nVerifyClient == SSL_CVERIFY_REQUIRE
&& SSL_get_peer_certificate(ssl) == NULL ) {
- ssl_log(r->server, SSL_LOG_ERROR,
+ ssl_log(r->server, SSL_LOG_ERROR,
"Re-negotiation handshake failed: Client certificate missing");
return FORBIDDEN;
}
@@ -796,10 +1009,10 @@ int ssl_hook_Access(request_rec *r)
}
/*
- * Under old SSLeay we had to change the X509_STORE inside the SSL_CTX
- * instead inside the SSL structure, so we have to reconfigure it to the
- * old values. This should be changed with forthcoming OpenSSL version
- * when better functionality is avaiable.
+ * Under old OpenSSL we had to change the X509_STORE inside the
+ * SSL_CTX instead inside the SSL structure, so we have to reconfigure it
+ * to the old values. This should be changed with forthcoming OpenSSL
+ * versions when better functionality is avaiable.
*/
#ifdef SSL_EXPERIMENTAL
if (renegotiate && reconfigured_locations) {
@@ -817,7 +1030,7 @@ int ssl_hook_Access(request_rec *r)
* Check SSLRequire boolean expressions
*/
apRequirement = dc->aRequirement;
- pRequirements = (ssl_require_t *)apRequirement->elts;
+ pRequirements = (ssl_require_t *)apRequirement->elts;
for (i = 0; i < apRequirement->nelts; i++) {
pRequirement = &pRequirements[i];
ok = ssl_expr_exec(r, pRequirement->mpExpr);
@@ -825,6 +1038,8 @@ int ssl_hook_Access(request_rec *r)
cp = ap_psprintf(r->pool, "Failed to execute SSL requirement expression: %s",
ssl_expr_get_error());
ap_log_reason(cp, r->filename, r);
+ /* remember forbidden access for strict require option */
+ ap_table_setn(r->notes, "ssl-access-forbidden", (void *)1);
return FORBIDDEN;
}
if (ok != 1) {
@@ -835,14 +1050,97 @@ int ssl_hook_Access(request_rec *r)
"Failed expression: %s", pRequirement->cpExpr);
ap_log_reason("SSL requirement expression not fulfilled "
"(see SSL logfile for more details)", r->filename, r);
+ /* remember forbidden access for strict require option */
+ ap_table_setn(r->notes, "ssl-access-forbidden", (void *)1);
return FORBIDDEN;
}
}
/*
* Else access is granted...
+ * (except vendor handlers override)
*/
- return OK;
+ rc = OK;
+#ifdef SSL_VENDOR
+ ap_hook_use("ap::mod_ssl::vendor::access_handler",
+ AP_HOOK_SIG2(int,ptr), AP_HOOK_DECLINE(DECLINED),
+ &rc, r);
+#endif
+ return rc;
+}
+
+/*
+ * Auth Handler:
+ * Fake a Basic authentication from the X509 client certificate.
+ *
+ * This must be run fairly early on to prevent a real authentication from
+ * occuring, in particular it must be run before anything else that
+ * authenticates a user. This means that the Module statement for this
+ * module should be LAST in the Configuration file.
+ */
+int ssl_hook_Auth(request_rec *r)
+{
+ SSLSrvConfigRec *sc = mySrvConfig(r->server);
+ SSLDirConfigRec *dc = myDirConfig(r);
+ char b1[MAX_STRING_LEN], b2[MAX_STRING_LEN];
+ char *clientdn;
+
+ /*
+ * Additionally forbid access (again)
+ * when strict require option is used.
+ */
+ if ( (dc->nOptions & SSL_OPT_STRICTREQUIRE)
+ && (ap_table_get(r->notes, "ssl-access-forbidden") != NULL))
+ return FORBIDDEN;
+
+ /*
+ * We decline operation in various situations...
+ */
+ if (!sc->bEnabled)
+ return DECLINED;
+ if (ap_ctx_get(r->connection->client->ctx, "ssl") == NULL)
+ return DECLINED;
+ if (!(dc->nOptions & SSL_OPT_FAKEBASICAUTH))
+ return DECLINED;
+ if (r->connection->user)
+ return DECLINED;
+ if ((clientdn = (char *)ap_ctx_get(r->connection->client->ctx, "ssl::client::dn")) == NULL)
+ return DECLINED;
+
+ /*
+ * Fake a password - which one would be immaterial, as, it seems, an empty
+ * password in the users file would match ALL incoming passwords, if only
+ * we were using the standard crypt library routine. Unfortunately, OpenSSL
+ * "fixes" a "bug" in crypt and thus prevents blank passwords from
+ * working. (IMHO what they really fix is a bug in the users of the code
+ * - failing to program correctly for shadow passwords). We need,
+ * therefore, to provide a password. This password can be matched by
+ * adding the string "xxj31ZMTZzkVA" as the password in the user file.
+ * This is just the crypted variant of the word "password" ;-)
+ */
+ ap_snprintf(b1, sizeof(b1), "%s:password", clientdn);
+ ssl_util_uuencode(b2, b1, FALSE);
+ ap_snprintf(b1, sizeof(b1), "Basic %s", b2);
+ ap_table_set(r->headers_in, "Authorization", b1);
+ ssl_log(r->server, SSL_LOG_INFO,
+ "Faking HTTP Basic Auth header: \"Authorization: %s\"", b1);
+
+ return DECLINED;
+}
+
+int ssl_hook_UserCheck(request_rec *r)
+{
+ SSLDirConfigRec *dc = myDirConfig(r);
+
+ /*
+ * Additionally forbid access (again)
+ * when strict require option is used.
+ */
+ if ( (dc->nOptions & SSL_OPT_STRICTREQUIRE)
+ && (ap_table_get(r->notes, "ssl-access-forbidden") != NULL))
+ return FORBIDDEN;
+
+ return DECLINED;
}
/*
@@ -901,6 +1199,7 @@ static const char *ssl_hook_Fixup_vars[] = {
"SSL_SERVER_I_DN_Email",
"SSL_SERVER_A_KEY",
"SSL_SERVER_A_SIG",
+ "SSL_SESSION_ID",
NULL
};
@@ -955,22 +1254,95 @@ int ssl_hook_Fixup(request_rec *r)
/* _________________________________________________________________
**
-** SSLeay Callback Functions
+** OpenSSL Callback Functions
** _________________________________________________________________
*/
/*
- * Handle out the already generated RSA key...
+ * Handle out temporary RSA private keys on demand
+ *
+ * The background of this as the TLSv1 standard explains it:
+ *
+ * | D.1. Temporary RSA keys
+ * |
+ * | US Export restrictions limit RSA keys used for encryption to 512
+ * | bits, but do not place any limit on lengths of RSA keys used for
+ * | signing operations. Certificates often need to be larger than 512
+ * | bits, since 512-bit RSA keys are not secure enough for high-value
+ * | transactions or for applications requiring long-term security. Some
+ * | certificates are also designated signing-only, in which case they
+ * | cannot be used for key exchange.
+ * |
+ * | When the public key in the certificate cannot be used for encryption,
+ * | the server signs a temporary RSA key, which is then exchanged. In
+ * | exportable applications, the temporary RSA key should be the maximum
+ * | allowable length (i.e., 512 bits). Because 512-bit RSA keys are
+ * | relatively insecure, they should be changed often. For typical
+ * | electronic commerce applications, it is suggested that keys be
+ * | changed daily or every 500 transactions, and more often if possible.
+ * | Note that while it is acceptable to use the same temporary key for
+ * | multiple transactions, it must be signed each time it is used.
+ * |
+ * | RSA key generation is a time-consuming process. In many cases, a
+ * | low-priority process can be assigned the task of key generation.
+ * | Whenever a new key is completed, the existing temporary key can be
+ * | replaced with the new one.
+ *
+ * So we generated 512 and 1024 bit temporary keys on startup
+ * which we now just handle out on demand....
+ *
*/
-RSA *ssl_callback_TmpRSA(SSL *pSSL, int nExport)
+RSA *ssl_callback_TmpRSA(SSL *pSSL, int nExport, int nKeyLen)
{
SSLModConfigRec *mc = myModConfig();
+ RSA *rsa;
+
+ rsa = NULL;
+ if (nExport) {
+ /* It's because an export cipher is used */
+ if (nKeyLen == 512)
+ rsa = mc->pRSATmpKey512;
+ else if (nKeyLen == 1024)
+ rsa = mc->pRSATmpKey1024;
+ else
+ /* it's too expensive to generate on-the-fly, so keep 1024bit */
+ rsa = mc->pRSATmpKey1024;
+ }
+ else {
+ /* It's because a sign-only certificate situation exists */
+ rsa = mc->pRSATmpKey1024;
+ }
+ return rsa;
+}
- return mc->pRSATmpKey;
+/*
+ * Handle out the already generated DH parameters...
+ */
+DH *ssl_callback_TmpDH(SSL *pSSL, int nExport, int nKeyLen)
+{
+ SSLModConfigRec *mc = myModConfig();
+ DH *dh;
+
+ dh = NULL;
+ if (nExport) {
+ /* It's because an export cipher is used */
+ if (nKeyLen == 512)
+ dh = mc->pDHTmpParam512;
+ else if (nKeyLen == 1024)
+ dh = mc->pDHTmpParam1024;
+ else
+ /* it's too expensive to generate on-the-fly, so keep 1024bit */
+ dh = mc->pDHTmpParam1024;
+ }
+ else {
+ /* It's because a sign-only certificate situation exists */
+ dh = mc->pDHTmpParam1024;
+ }
+ return dh;
}
/*
- * This SSLeay callback function is called when SSLeay
+ * This OpenSSL callback function is called when OpenSSL
* does client authentication and verifies the certificate chain.
*/
int ssl_callback_SSLVerify(int ok, X509_STORE_CTX *ctx)
@@ -981,19 +1353,22 @@ int ssl_callback_SSLVerify(int ok, X509_STORE_CTX *ctx)
request_rec *r;
SSLSrvConfigRec *sc;
SSLDirConfigRec *dc;
+ ap_ctx *actx;
X509 *xs;
int errnum;
int errdepth;
char *cp;
char *cp2;
int depth;
+ int verify;
/*
- * Get Apache context back through SSLeay context
+ * Get Apache context back through OpenSSL context
*/
ssl = (SSL *)X509_STORE_CTX_get_app_data(ctx);
conn = (conn_rec *)SSL_get_app_data(ssl);
- r = (request_rec *)SSL_get_app_data2(ssl);
+ actx = (ap_ctx *)SSL_get_app_data2(ssl);
+ r = (request_rec *)ap_ctx_get(actx, "ssl::request_rec");
s = conn->server;
sc = mySrvConfig(s);
dc = (r != NULL ? myDirConfig(r) : NULL);
@@ -1022,11 +1397,15 @@ int ssl_callback_SSLVerify(int ok, X509_STORE_CTX *ctx)
/*
* Check for optionally acceptable non-verifiable issuer situation
*/
+ if (dc != NULL && dc->nVerifyClient != SSL_CVERIFY_UNSET)
+ verify = dc->nVerifyClient;
+ else
+ verify = sc->nVerifyClient;
if ( ( errnum == X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT
|| errnum == X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN
|| errnum == X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY
|| errnum == X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE )
- && sc->nVerifyClient == SSL_CVERIFY_OPTIONAL_NO_CA ) {
+ && verify == SSL_CVERIFY_OPTIONAL_NO_CA ) {
ssl_log(s, SSL_LOG_TRACE,
"Certificate Verification: Verifiable Issuer is configured as "
"optional, therefore we're accepting the certificate");
@@ -1034,6 +1413,14 @@ int ssl_callback_SSLVerify(int ok, X509_STORE_CTX *ctx)
}
/*
+ * Additionally perform CRL-based revocation checks
+ */
+ if (ok) {
+ ok = ssl_callback_SSLVerify_CRL(ok, ctx, s);
+ errnum = X509_STORE_CTX_get_error(ctx);
+ }
+
+ /*
* If we already know it's not ok, log the real reason
*/
if (!ok) {
@@ -1041,7 +1428,7 @@ int ssl_callback_SSLVerify(int ok, X509_STORE_CTX *ctx)
errnum, X509_verify_cert_error_string(errnum));
ap_ctx_set(conn->client->ctx, "ssl::client::dn", NULL);
ap_ctx_set(conn->client->ctx, "ssl::verify::error",
- X509_verify_cert_error_string(errnum));
+ (void *)X509_verify_cert_error_string(errnum));
}
/*
@@ -1049,27 +1436,191 @@ int ssl_callback_SSLVerify(int ok, X509_STORE_CTX *ctx)
*/
if (dc != NULL && dc->nVerifyDepth != UNSET)
depth = dc->nVerifyDepth;
- else
+ else
depth = sc->nVerifyDepth;
if (errdepth > depth) {
ssl_log(s, SSL_LOG_ERROR,
"Certificate Verification: Certificate Chain too long "
- "(chain has %d certificates, but maximum allowed are only %d)",
+ "(chain has %d certificates, but maximum allowed are only %d)",
errdepth, depth);
ap_ctx_set(conn->client->ctx, "ssl::verify::error",
- X509_verify_cert_error_string(X509_V_ERR_CERT_CHAIN_TOO_LONG));
+ (void *)X509_verify_cert_error_string(X509_V_ERR_CERT_CHAIN_TOO_LONG));
ok = FALSE;
}
/*
- * And finally signal SSLeay the (perhaps changed) state
+ * And finally signal OpenSSL the (perhaps changed) state
*/
return (ok);
}
+int ssl_callback_SSLVerify_CRL(
+ int ok, X509_STORE_CTX *ctx, server_rec *s)
+{
+ SSLSrvConfigRec *sc;
+ X509_OBJECT obj;
+ X509_NAME *subject;
+ X509_NAME *issuer;
+ X509 *xs;
+ X509_CRL *crl;
+ X509_REVOKED *revoked;
+ long serial;
+ BIO *bio;
+ int i, n, rc;
+ char *cp;
+ char *cp2;
+
+ /*
+ * Unless a revocation store for CRLs was created we
+ * cannot do any CRL-based verification, of course.
+ */
+ sc = mySrvConfig(s);
+ if (sc->pRevocationStore == NULL)
+ return ok;
+
+ /*
+ * Determine certificate ingredients in advance
+ */
+ xs = X509_STORE_CTX_get_current_cert(ctx);
+ subject = X509_get_subject_name(xs);
+ issuer = X509_get_issuer_name(xs);
+
+ /*
+ * OpenSSL provides the general mechanism to deal with CRLs but does not
+ * use them automatically when verifying certificates, so we do it
+ * explicitly here. We will check the CRL for the currently checked
+ * certificate, if there is such a CRL in the store.
+ *
+ * We come through this procedure for each certificate in the certificate
+ * chain, starting with the root-CA's certificate. At each step we've to
+ * both verify the signature on the CRL (to make sure it's a valid CRL)
+ * and it's revocation list (to make sure the current certificate isn't
+ * revoked). But because to check the signature on the CRL we need the
+ * public key of the issuing CA certificate (which was already processed
+ * one round before), we've a little problem. But we can both solve it and
+ * at the same time optimize the processing by using the following
+ * verification scheme (idea and code snippets borrowed from the GLOBUS
+ * project):
+ *
+ * 1. We'll check the signature of a CRL in each step when we find a CRL
+ * through the _subject_ name of the current certificate. This CRL
+ * itself will be needed the first time in the next round, of course.
+ * But we do the signature processing one round before this where the
+ * public key of the CA is available.
+ *
+ * 2. We'll check the revocation list of a CRL in each step when
+ * we find a CRL through the _issuer_ name of the current certificate.
+ * This CRLs signature was then already verified one round before.
+ *
+ * This verification scheme allows a CA to revoke its own certificate as
+ * well, of course.
+ */
+
+ /*
+ * Try to retrieve a CRL corresponding to the _subject_ of
+ * the current certificate in order to verify it's integrity.
+ */
+ memset((char *)&obj, 0, sizeof(obj));
+ rc = SSL_X509_STORE_lookup(sc->pRevocationStore, X509_LU_CRL, subject, &obj);
+ crl = obj.data.crl;
+ if (rc > 0 && crl != NULL) {
+ /*
+ * Log information about CRL
+ * (A little bit complicated because of ASN.1 and BIOs...)
+ */
+ if (ssl_log_applies(s, SSL_LOG_TRACE)) {
+ bio = BIO_new(BIO_s_mem());
+ BIO_printf(bio, "lastUpdate: ");
+ ASN1_UTCTIME_print(bio, X509_CRL_get_lastUpdate(crl));
+ BIO_printf(bio, ", nextUpdate: ");
+ ASN1_UTCTIME_print(bio, X509_CRL_get_nextUpdate(crl));
+ n = BIO_pending(bio);
+ cp = malloc(n+1);
+ n = BIO_read(bio, cp, n);
+ cp[n] = NUL;
+ BIO_free(bio);
+ cp2 = X509_NAME_oneline(subject, NULL, 0);
+ ssl_log(s, SSL_LOG_TRACE, "CA CRL: Issuer: %s, %s", cp2, cp);
+ free(cp2);
+ free(cp);
+ }
+
+ /*
+ * Verify the signature on this CRL
+ */
+ if (X509_CRL_verify(crl, X509_get_pubkey(xs)) <= 0) {
+ ssl_log(s, SSL_LOG_WARN, "Invalid signature on CRL");
+ X509_STORE_CTX_set_error(ctx, X509_V_ERR_CRL_SIGNATURE_FAILURE);
+ X509_OBJECT_free_contents(&obj);
+ return FALSE;
+ }
+
+ /*
+ * Check date of CRL to make sure it's not expired
+ */
+ i = X509_cmp_current_time(X509_CRL_get_nextUpdate(crl));
+ if (i == 0) {
+ ssl_log(s, SSL_LOG_WARN, "Found CRL has invalid nextUpdate field");
+ X509_STORE_CTX_set_error(ctx, X509_V_ERR_ERROR_IN_CRL_NEXT_UPDATE_FIELD);
+ X509_OBJECT_free_contents(&obj);
+ return FALSE;
+ }
+ if (i < 0) {
+ ssl_log(s, SSL_LOG_WARN,
+ "Found CRL is expired - "
+ "revoking all certificates until you get updated CRL");
+ X509_STORE_CTX_set_error(ctx, X509_V_ERR_CRL_HAS_EXPIRED);
+ X509_OBJECT_free_contents(&obj);
+ return FALSE;
+ }
+ X509_OBJECT_free_contents(&obj);
+ }
+
+ /*
+ * Try to retrieve a CRL corresponding to the _issuer_ of
+ * the current certificate in order to check for revocation.
+ */
+ memset((char *)&obj, 0, sizeof(obj));
+ rc = SSL_X509_STORE_lookup(sc->pRevocationStore, X509_LU_CRL, issuer, &obj);
+ crl = obj.data.crl;
+ if (rc > 0 && crl != NULL) {
+ /*
+ * Check if the current certificate is revoked by this CRL
+ */
+#if SSL_LIBRARY_VERSION < 0x00904000
+ n = sk_num(X509_CRL_get_REVOKED(crl));
+#else
+ n = sk_X509_REVOKED_num(X509_CRL_get_REVOKED(crl));
+#endif
+ for (i = 0; i < n; i++) {
+#if SSL_LIBRARY_VERSION < 0x00904000
+ revoked = (X509_REVOKED *)sk_value(X509_CRL_get_REVOKED(crl), i);
+#else
+ revoked = sk_X509_REVOKED_value(X509_CRL_get_REVOKED(crl), i);
+#endif
+ if (ASN1_INTEGER_cmp(revoked->serialNumber, X509_get_serialNumber(xs)) == 0) {
+
+ serial = ASN1_INTEGER_get(revoked->serialNumber);
+ cp = X509_NAME_oneline(issuer, NULL, 0);
+ ssl_log(s, SSL_LOG_INFO,
+ "Certificate with serial %ld (0x%lX) "
+ "revoked per CRL from issuer %s",
+ serial, serial, cp);
+ free(cp);
+
+ X509_STORE_CTX_set_error(ctx, X509_V_ERR_CERT_REVOKED);
+ X509_OBJECT_free_contents(&obj);
+ return FALSE;
+ }
+ }
+ X509_OBJECT_free_contents(&obj);
+ }
+ return ok;
+}
+
/*
- * This callback function is executed by SSLeay whenever a new SSL_SESSION is
- * added to the internal SSLeay session cache. We use this hook to spread the
+ * This callback function is executed by OpenSSL whenever a new SSL_SESSION is
+ * added to the internal OpenSSL session cache. We use this hook to spread the
* SSL_SESSION also to the inter-process disk-cache to make share it with our
* other Apache pre-forked server processes.
*/
@@ -1079,16 +1630,17 @@ int ssl_callback_NewSessionCacheEntry(SSL *ssl, SSL_SESSION *pNew)
server_rec *s;
SSLSrvConfigRec *sc;
long t;
+ BOOL rc;
/*
- * Get Apache context back through SSLeay context
+ * Get Apache context back through OpenSSL context
*/
conn = (conn_rec *)SSL_get_app_data(ssl);
s = conn->server;
sc = mySrvConfig(s);
/*
- * Set the timeout also for the internal SSLeay cache, because this way
+ * Set the timeout also for the internal OpenSSL cache, because this way
* our inter-process cache is consulted only when it's really necessary.
*/
t = (SSL_get_time(pNew) + sc->nSessionCacheTimeout);
@@ -1098,26 +1650,27 @@ int ssl_callback_NewSessionCacheEntry(SSL *ssl, SSL_SESSION *pNew)
* Store the SSL_SESSION in the inter-process cache with the
* same expire time, so it expires automatically there, too.
*/
- ssl_scache_store(s, pNew, t);
+ rc = ssl_scache_store(s, pNew, t);
/*
* Log this cache operation
*/
ssl_log(s, SSL_LOG_TRACE, "Inter-Process Session Cache: "
- "request=SET id=%s timeout=%ds (session caching)",
+ "request=SET status=%s id=%s timeout=%ds (session caching)",
+ rc == TRUE ? "OK" : "BAD",
ssl_scache_id2sz(pNew->session_id, pNew->session_id_length),
t-time(NULL));
/*
- * return 0 which means to SSLeay that the pNew is still
+ * return 0 which means to OpenSSL that the pNew is still
* valid and was not freed by us with SSL_SESSION_free().
*/
return 0;
}
/*
- * This callback function is executed by SSLeay whenever a
- * SSL_SESSION is looked up in the internal SSLeay cache and it
+ * This callback function is executed by OpenSSL whenever a
+ * SSL_SESSION is looked up in the internal OpenSSL cache and it
* was not found. We use this to lookup the SSL_SESSION in the
* inter-process disk-cache where it was perhaps stored by one
* of our other Apache pre-forked server processes.
@@ -1130,7 +1683,7 @@ SSL_SESSION *ssl_callback_GetSessionCacheEntry(
SSL_SESSION *pSession;
/*
- * Get Apache context back through SSLeay context
+ * Get Apache context back through OpenSSL context
*/
conn = (conn_rec *)SSL_get_app_data(ssl);
s = conn->server;
@@ -1163,8 +1716,8 @@ SSL_SESSION *ssl_callback_GetSessionCacheEntry(
}
/*
- * This callback function is executed by SSLeay whenever a
- * SSL_SESSION is removed from the the internal SSLeay cache.
+ * This callback function is executed by OpenSSL whenever a
+ * SSL_SESSION is removed from the the internal OpenSSL cache.
* We use this to remove the SSL_SESSION in the inter-process
* disk-cache, too.
*/
@@ -1174,9 +1727,11 @@ void ssl_callback_DelSessionCacheEntry(
server_rec *s;
/*
- * Get Apache context back through SSLeay context
+ * Get Apache context back through OpenSSL context
*/
s = (server_rec *)SSL_CTX_get_app_data(ctx);
+ if (s == NULL) /* on server shutdown Apache is already gone */
+ return;
/*
* Remove the SSL_SESSION from the inter-process cache
@@ -1195,9 +1750,9 @@ void ssl_callback_DelSessionCacheEntry(
}
/*
- * This callback function is executed while SSLeay processes the
+ * This callback function is executed while OpenSSL processes the
* SSL handshake and does SSL record layer stuff. We use it to
- * trace SSLeay's processing in out SSL logfile.
+ * trace OpenSSL's processing in out SSL logfile.
*/
void ssl_callback_LogTracingState(SSL *ssl, int where, int rc)
{
@@ -1224,26 +1779,27 @@ void ssl_callback_LogTracingState(SSL *ssl, int where, int rc)
else if (where & SSL_CB_HANDSHAKE_DONE)
ssl_log(s, SSL_LOG_TRACE, "%s: Handshake: done", SSL_LIBRARY_NAME);
else if (where & SSL_CB_LOOP)
- ssl_log(s, SSL_LOG_TRACE, "%s: Loop: %s",
+ ssl_log(s, SSL_LOG_TRACE, "%s: Loop: %s",
SSL_LIBRARY_NAME, SSL_state_string_long(ssl));
else if (where & SSL_CB_READ)
ssl_log(s, SSL_LOG_TRACE, "%s: Read: %s",
SSL_LIBRARY_NAME, SSL_state_string_long(ssl));
else if (where & SSL_CB_WRITE)
- ssl_log(s, SSL_LOG_TRACE, "%s: Write: %s",
+ ssl_log(s, SSL_LOG_TRACE, "%s: Write: %s",
SSL_LIBRARY_NAME, SSL_state_string_long(ssl));
else if (where & SSL_CB_ALERT) {
str = (where & SSL_CB_READ) ? "read" : "write";
- ssl_log(s, SSL_LOG_TRACE, "%s: Alert: %s:%s\n", SSL_LIBRARY_NAME,
+ ssl_log(s, SSL_LOG_TRACE, "%s: Alert: %s:%s:%s\n",
+ SSL_LIBRARY_NAME, str,
SSL_alert_type_string_long(rc),
SSL_alert_desc_string_long(rc));
}
else if (where & SSL_CB_EXIT) {
if (rc == 0)
- ssl_log(s, SSL_LOG_TRACE, "%s: Exit: failed in %s",
+ ssl_log(s, SSL_LOG_TRACE, "%s: Exit: failed in %s",
SSL_LIBRARY_NAME, SSL_state_string_long(ssl));
else if (rc < 0)
- ssl_log(s, SSL_LOG_TRACE, "%s: Exit: error in %s",
+ ssl_log(s, SSL_LOG_TRACE, "%s: Exit: error in %s",
SSL_LIBRARY_NAME, SSL_state_string_long(ssl));
}
}