ssl_engine_kernel.c revision c6cf638d68b4cfff4f74ebc360abca97ad38cd71
165N/A/* Licensed to the Apache Software Foundation (ASF) under one or more 165N/A * contributor license agreements. See the NOTICE file distributed with 165N/A * this work for additional information regarding copyright ownership. 165N/A * The ASF licenses this file to You under the Apache License, Version 2.0 165N/A * (the "License"); you may not use this file except in compliance with 165N/A * the License. You may obtain a copy of the License at 165N/A * Unless required by applicable law or agreed to in writing, software 165N/A * distributed under the License is distributed on an "AS IS" BASIS, 165N/A * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 165N/A * See the License for the specific language governing permissions and 165N/A * limitations under the License. 165N/A * _ __ ___ ___ __| | ___ ___| | mod_ssl 165N/A * | '_ ` _ \ / _ \ / _` | / __/ __| | Apache Interface to OpenSSL 165N/A * | | | | | | (_) | (_| | \__ \__ \ | 165N/A * |_| |_| |_|\___/ \__,_|___|___/___/_| 165N/A * The SSL engine kernel 165N/A /* ``It took me fifteen years to discover 165N/A I had no talent for programming, but 165N/A I couldn't give it up because by that 165N/A time I was too famous.'' 165N/A/* Perform an upgrade-to-TLS for the given request, per RFC 2817. */ 165N/A "upgrading connection to TLS");
165N/A "failed to send 101 interim response for connection " /* Perform initial SSL handshake. */ "TLS upgrade handshake failed: not accepted by client!?");
/* Perform a speculative (and non-blocking) read from the connection * filters for the given request, to determine whether there is any * pending data to read. Return non-zero if there is, else zero. */ * Post Read Request Handler /* Perform TLS upgrade here if "SSLEngine optional" is configured, * SSL is not already set up for this connection, and the client * has sent a suitable Upgrade header. */ "Reason: You're speaking plain HTTP " "to an SSL-enabled server port.<br />\n" "Instead use the HTTPS scheme to access " "this URL, please.<br />\n" "<a href=\"%s\"><b>%s</b></a></blockquote>",
/* Now that we have caught this error, forget it. we are done * with using SSL on this request. * Get the SSL connection structure and perform the * delayed interlinking from SSL back to request_rec * The SNI extension supplied a hostname. So don't accept requests * with either no hostname or a different hostname. "Hostname %s provided via SNI, but no hostname" "Hostname %s provided via SNI and hostname %s provided" * We are using a name based configuration here, but no hostname was * provided via SNI. Don't allow that if are requested to do strict * checking. Check wether this strict checking was setup either in the * server config we used for handshaking or in our current server. * This should avoid insecure configuration by accident. "No hostname was provided via SNI for a name based" * Log information about incoming HTTPS requests "%s HTTPS request received for child %ld (server %s)",
/* SetEnvIf ssl-*-shutdown flags can only be per-server, * so they won't change across keepalive requests * Move SetEnvIf information from request_rec to conn_rec/BUFF * to allow the close connection handler to use them. /* being case-sensitive here. * and not checking for the -shutdown since these are the only * SetEnvIf "flags" we support return;
/* should only ever be one ssl-*-shutdown */ * Support for SSLRequireSSL directive /* This vhost was configured for optional SSL, just tell the * client that we need to upgrade. "access to %s failed, reason: %s",
r->
filename,
"SSL connection required");
/* remember forbidden access for strict require option */ * Check to see whether SSL is in use; if it's not, then no * further access control checks are relevant. (the test for * sc->enabled is probably strictly unnecessary) * 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 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. * 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 * o The optimized and still secure way where we force a renegotiation * only if the currently active cipher is no longer contained in the * 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. /* configure new state */ "Unable to reconfigure (per-directory) " "permitted SSL ciphers");
/* determine whether a renegotiation has to be forced */ "Reconfigured cipher suite will force renegotiation");
* 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 * SSLConnRec attached to the SSL* of OpenSSL. We've to force the * restriction on the certificate chain). /* determine the new depth */ "Reduced client verification depth will force " * override of SSLVerifyClient * 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 /* configure new state */ /* determine whether we've to force a renegotiation */ "Changed client verification type will force " /* If we're handling a request for a vhost other than the default one, * then we need to make sure that client authentication is properly * enforced. For clients supplying an SNI extension, the peer * certificate verification has happened in the handshake already * (and r->server == handshakeserver). For non-SNI requests, * an additional check is needed here. If client authentication * is configured as mandatory, then we can only proceed if the * CA list doesn't have to be changed (OpenSSL doesn't provide * an option to change the list for an existing session). "Non-default virtual host with SSLVerify set to " "'require' and VirtualHost-specific CA certificate " "list is only available to clients with TLS server " "name indication (SNI) support");
/* let it pass, possibly with an "incorrect" peer cert, * so make sure the SSL_CLIENT_VERIFY environment variable * will indicate partial success only, later on. /* If a renegotiation is now required for this location, and the * request includes a message body (and the client has not * requested a "100 Continue" response), then the client will be * streaming the request body over the wire already. In that * case, it is not possible to stop and perform a new SSL * handshake immediately; once the SSL library moves to the * "accept" state, it will reject the SSL packets which the client * is sending for the request body. * To allow authentication to complete in this auth hook, the * solution used here is to fill a (bounded) buffer with the * request body, and then to reinject that request body later. /* Fill the I/O buffer with the request body if possible. */ /* If the reneg buffer size is set to zero, just fail. */ "could not buffer message body to allow " "SSL renegotiation to proceed");
* now do the renegotiation if anything was actually reconfigured * Now we force the SSL renegotation by sending the Hello Request * message to the client. Here we have to do a workaround: Actually * 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 * ACCEPT state manually. Using SSL_set_accept_state() doesn't work * here because it resets too much of the connection. So we set the * state explicitly and continue the handshake manually. "Requesting connection re-negotiation");
/* perform just a manual re-verification of the peer */ "Performing quick renegotiation: " "just re-verifying the peer");
/* client cert is in the session cache, but there is * no chain, since ssl3_get_client_certificate() * sk_X509_shift-ed the peer cert out of the chain. * we put it back here for the purpose of quick_renegotiation. "Cannot find peer certificate chain");
"Cannot find certificate storage");
"Re-negotiation verification step failed");
/* we created this ourselves, so free it */ /* Additional mitigation for CVE-2009-3555: At this point, * before renegotiating, an (entire) request has been read * from the connection. An attacker may have sent further * data to "prefix" any subsequent request by the victim's * client after the renegotiation; this data may already * have been read and buffered. Forcing a connection * closure after the response ensures such data will be * discarded. Legimately pipelined HTTP requests will be * retried anyway with this approach. */ "insecure SSL re-negotiation required, but " "a pipelined request is present; keepalive " /* Perform a full renegotiation. */ "Performing full renegotiation: complete handshake " "protocol (%s support secure renegotiation)",
"client does" :
"client does not" /* Toggle the renegotiation state to allow the new * handshake to proceed. */ "Re-negotiation request failed");
"Awaiting re-negotiation handshake");
/* XXX: Should replace SSL_set_state with SSL_renegotiate(ssl); * However, this causes failures in perl-framework currently, * perhaps pre-test if we have already negotiated? "Re-negotiation handshake failed: " "Not accepted by client!?");
* Remember the peer certificate's DN * Finally check for acceptable renegotiation results "Re-negotiation handshake failed: " "Client verification failed");
"Re-negotiation handshake failed: " "Client certificate missing");
* Also check that SSLCipherSuite has been enforced as expected. "SSL cipher suite not renegotiated: " "access to %s denied using cipher %s",
/* If we're trying to have the user name set from a client * certificate then we need to set it here. This should be safe as * the user name probably isn't important from an auth checking point * of view as the certificate supplied acts in that capacity. * However, if FakeAuth is being used then this isn't the case so * we need to postpone setting the username until later. * Check SSLRequire boolean expressions "SSL requirement expression: %s",
"access to %s failed, reason: %s",
/* remember forbidden access for strict require option */ "Access to %s denied for %s " "(requirement expression not fulfilled)",
"access to %s failed, reason: %s",
"SSL requirement expression not fulfilled");
/* remember forbidden access for strict require option */ * Else access is granted from our point of view (except vendor * handlers override). But we have to return DECLINED here instead * of OK, because mod_auth and other modules still might want to * Authentication 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. * Additionally forbid access (again) * when strict require option is used. * We decline when we are in a subrequest. The Authorization header * would already be present if it was added in the main request. * Make sure the user is not able to fake the client certificate * based authentication by just entering an X.509 Subject DN * ("/XX=YYY/XX=YYY/..") as the username and "password" as the "Encountered FakeBasicAuth spoof: %s",
username);
* We decline operation in various situations... * - SSLOptions +FakeBasicAuth not configured * - r->user already authenticated * - client did not present a certificate * 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" ;-) "Faking HTTP Basic Auth header: \"Authorization: %s\"",
/* authorization phase */ * Additionally forbid access (again) * when strict require option is used. /* If "SSLEngine optional" is configured, this is not an SSL * connection, and this isn't a subrequest, send an Upgrade * Check to see if SSL is on * Annotate the SSI/CGI environment with standard SSL information /* the always present HTTPS (=HTTP over SSL) flag! */ /* add content of SNI TLS extension (if supplied with ClientHello) */ /* standard SSL environment variables */ * On-demand bloat up the SSI/CGI environment with certificate data /* _________________________________________________________________ ** OpenSSL Callback Functions ** _________________________________________________________________ * 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. * XXX: base on comment above, if thread support is enabled, * we should spawn a low-priority thread to generate new keys * So we generated 512 and 1024 bit temporary keys on startup * which we now just hand out on demand.... "handing out temporary %d bit RSA key",
keylen);
/* doesn't matter if export flag is on, * we won't be asked for keylen > 512 in that case. * if we are asked for a keylen > 1024, it is too expensive * to generate on the fly. * XXX: any reason not to generate 2048 bit keys at startup? * Hand out the already generated DH parameters... "handing out temporary %d bit DH key",
keylen);
/* XXX Uses 256-bit key for now. TODO: support other sizes. */ "handing out temporary 256 bit ECC key");
/* ecdh->group = EC_GROUP_new_by_nid(NID_secp160r2); */ * This OpenSSL callback function is called when OpenSSL * does client authentication and verifies the certificate chain. /* Get Apache context back through OpenSSL context */ /* Get verify ingredients */ * Log verification information "Certificate Verification, depth %d",
* Check for optionally acceptable non-verifiable issuer situation * SSLProxyVerify is either not configured or set to "none". * (this callback doesn't happen in the server context if SSLVerify * is not configured or set to "none") "Certificate Verification: Verifiable Issuer is " "configured as optional, therefore we're accepting " /* If there was an optional verification error, it's not * possible to perform OCSP validation since the issuer may be "cannot perform OCSP validation for cert " "if issuer has not been verified " "(optional_no_ca configured)");
* If we already know it's not ok, log the real reason "Certificate Verification: Error (%d): %s",
* Finally check the depth of the certificate verification "Certificate Verification: Certificate Chain too long " "(chain has %d certificates, but maximum allowed are " * And finally signal OpenSSL the (perhaps changed) state * Unless a revocation store for CRLs was created we * cannot do any CRL-based verification, of course. * Determine certificate ingredients in advance * 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 * 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 * Try to retrieve a CRL corresponding to the _subject_ of * the current certificate in order to verify it's integrity. * Log information about CRL * (A little bit complicated because of ASN.1 and BIOs...) char buff[
512];
/* should be plenty */ * Verify the signature on this CRL /* Only refcounted in OpenSSL */ "Invalid signature on CRL");
* Check date of CRL to make sure it's not expired "Found CRL has invalid nextUpdate field");
"Found CRL is expired - " "revoking all certificates until you get updated CRL");
* Try to retrieve a CRL corresponding to the _issuer_ of * the current certificate in order to check for revocation. * Check if the current certificate is revoked by this CRL for (i = 0; i < n; i++) {
"Certificate with serial %ld (0x%lX) " "revoked per CRL from issuer %s",
"Proxy client certificate callback: (%s) " * caller will decrement the cert and key reference * so we need to increment here to prevent them from "downstream server wanted client certificate " * downstream server didn't send us a list of acceptable CA certs, * so we send the first client cert in the list. "Inter-Process Session Cache: " "request=%s status=%s id=%s %s(session %s)",
* 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. /* Get Apache context back through OpenSSL context */ * Set the timeout also for the internal OpenSSL cache, because this way * our inter-process cache is consulted only when it's really necessary. * Store the SSL_SESSION in the inter-process cache with the * same expire time, so it expires automatically there, too. * return 0 which means to OpenSSL that the session is still * valid and was not freed by us with SSL_SESSION_free(). * 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. /* Get Apache context back through OpenSSL context */ * Try to retrieve the SSL_SESSION from the inter-process cache * Return NULL or the retrieved SSL_SESSION. But indicate (by * setting do_copy to 0) that the reference count on the * SSL_SESSION should not be incremented by the SSL library, * because we will no longer hold a reference to it ourself. * 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 * Get Apache context back through OpenSSL context return;
/* on server shutdown Apache is already gone */ * Remove the SSL_SESSION from the inter-process cache /* TODO: Do we need a temp pool here, or are we always shutting down? */ /* Dump debugginfo trace to the log file. */ * create the various trace messages "%s: Exit: failed in %s",
* Because SSL renegotations can happen at any time (not only after * SSL_accept()), the best way to log the current connection details is * right after a finished handshake. "Connection: Client IP: %s, Protocol: %s, " "Cipher: %s (%s/%s bits)",
* This callback function is executed while OpenSSL processes the SSL * handshake and does SSL record layer stuff. It's used to trap * client-initiated renegotiations, and for dumping everything to the /* Retrieve the conn_rec and the associated SSLConnRec. */ /* If the reneg state is to reject renegotiations, check the SSL * state machine and move to ABORT if a Client Hello is being "rejecting client initiated renegotiation");
/* If the first handshake is complete, change state to reject any * subsequent client-initated renegotiation. */ * This callback function is executed when OpenSSL encounters an extended * client hello with a server name indication extension ("SNI", cf. RFC 4366). "SSL virtual host for servername %s found",
"No matching SSL virtual host for servername " * Find a (name-based) SSL virtual host where either the ServerName * or one of the ServerAliases matches the supplied name (to be used * with ap_vhost_iterate_given_conn()) * if not matched yet, check ServerAlias entries * (adapted from vhost.c:matches_aliases()) /* if still no match, check ServerAlias entries with wildcards */ /* set SSL_CTX (if matched) */ * SSL_set_SSL_CTX() only deals with the server cert, * so we need to duplicate a few additional settings * Only initialize the verification settings from the ctx * if they are not yet set, or if we're called when a new * SSL connection is set up (num_renegotiations == 0). * Otherwise, we would possibly reset a per-directory * configuration which was put into effect by ssl_hook_Access. * Save the found server into our SSLConnRec for later * There is one special filter callback, which is set * very early depending on the base_server's log level. * If this is not the first vhost we're now selecting * (and the first vhost doesn't use APLOG_DEBUG), then * we need to set that callback here.