/* Copyright 2001-2004 The Apache Software Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "mod_nss.h"
#include "nss_engine_cipher.h"
#include "secerr.h"
static void HandshakeDone(PRFileDesc *fd, void *doneflag);
extern cipher_properties ciphers_def[];
extern int ciphernum;
/*
* Post Read Request Handler
*/
int nss_hook_ReadReq(request_rec *r)
{
SSLConnRec *sslconn = myConnConfig(r->connection);
PRFileDesc *ssl = sslconn ? sslconn->ssl : NULL;
SECItem *hostInfo = NULL;
SSLSrvConfigRec *sc = mySrvConfig(r->server);
if (!sslconn) {
return DECLINED;
}
if (sslconn->non_nss_request) {
const char *errmsg;
char *thisurl;
char *thisport = "";
int port = ap_get_server_port(r);
if (!ap_is_default_port(port, r)) {
thisport = apr_psprintf(r->pool, ":%u", port);
}
thisurl = ap_escape_html(r->pool,
apr_psprintf(r->pool, "https://%s%s/",
ap_get_server_name(r),
thisport));
errmsg = apr_psprintf(r->pool,
"Reason: You're speaking plain HTTP "
"to an SSL-enabled server port.
\n"
"Instead use the HTTPS scheme to access "
"this URL, please.
\n"
"
Hint: "
"%s
",
thisurl, thisurl);
apr_table_setn(r->notes, "error-notes", errmsg);
/* Now that we have caught this error, forget it. we are done
* with using SSL on this request.
*/
sslconn->non_nss_request = 0;
return HTTP_BAD_REQUEST;
}
/* Get the SSL connection structure and perform the
* delayed interlinking from SSL back to request_rec
*/
if (!ssl) {
return DECLINED;
}
/*
* SNI is on by default. You can switch SNI off by setting
* NSSSNI off.
*/
if (sc->sni) {
hostInfo = SSL_GetNegotiatedHostInfo(ssl);
if (hostInfo != NULL) {
if (ap_is_initial_req(r) && (hostInfo->len != 0)) {
char *servername = NULL;
char *host, *scope_id;
apr_port_t port;
apr_status_t rv;
apr_pool_t *s_p;
apr_pool_create(&s_p, NULL);
servername = apr_pstrndup(s_p, (char *) hostInfo->data, hostInfo->len);
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
"SNI request for %s", servername);
if (!r->hostname) {
ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server,
"Hostname %s provided via SNI, but no hostname"
" provided in HTTP request", servername);
apr_pool_destroy(s_p);
SECITEM_FreeItem(hostInfo, PR_TRUE);
return HTTP_BAD_REQUEST;
}
rv = apr_parse_addr_port(&host, &scope_id, &port, r->hostname, r->pool);
if (rv != APR_SUCCESS || scope_id) {
apr_pool_destroy(s_p);
SECITEM_FreeItem(hostInfo, PR_TRUE);
return HTTP_BAD_REQUEST;
}
if (strcasecmp(host, servername)) {
ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server,
"Hostname %s provided via SNI and hostname %s provided"
" via HTTP are different", servername, host);
apr_pool_destroy(s_p);
SECITEM_FreeItem(hostInfo, PR_TRUE);
return HTTP_BAD_REQUEST;
}
apr_pool_destroy(s_p);
SECITEM_FreeItem(hostInfo, PR_TRUE);
}
} else if (((sc->strict_sni_vhost_check)
|| (mySrvConfig(r->server))->strict_sni_vhost_check)
&& r->connection->vhost_lookup_data) {
/*
* 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 whether this strict checking was set
* up either in the server config we used for handshaking or in
* our current server. This should avoid insecure configuration
* by accident.
*/
ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server,
"No hostname was provided via SNI for a name based"
" virtual host");
apr_table_setn(r->notes, "error-notes",
"Reason: The client software did not provide a "
"hostname using Server Name Indication (SNI), "
"which is required to access this server.
\n");
return HTTP_FORBIDDEN;
}
}
/*
* Log information about incoming HTTPS requests
*/
#if AP_SERVER_MINORVERSION_NUMBER <= 2
if (r->server->loglevel >= APLOG_INFO && ap_is_initial_req(r)) {
#else
if (r->server->log.level >= APLOG_INFO && ap_is_initial_req(r)) {
#endif
ap_log_error(APLOG_MARK, APLOG_INFO, 0, r->server,
"%s HTTPS request received for child %ld (server %s)",
(r->connection->keepalives <= 0 ?
"Initial (No.1)" :
apr_psprintf(r->pool, "Subsequent (No.%d)",
r->connection->keepalives+1)),
r->connection->id,
nss_util_vhostid(r->pool, r->server));
}
if (sslconn->client_cert != NULL)
CERT_DestroyCertificate(sslconn->client_cert);
sslconn->client_cert = SSL_PeerCertificate(ssl);
sslconn->client_dn = NULL;
return DECLINED;
}
/*
* Access Handler
*/
int nss_hook_Access(request_rec *r)
{
SSLDirConfigRec *dc = myDirConfig(r);
SSLSrvConfigRec *sc = mySrvConfig(r->server);
SSLConnRec *sslconn = myConnConfig(r->connection);
PRFileDesc *ssl = sslconn ? sslconn->ssl : NULL;
apr_array_header_t *requires;
nss_require_t *nss_requires;
char *cp;
int ok, i;
BOOL renegotiate = FALSE, renegotiate_quick = FALSE;
CERTCertificate *cert;
CERTCertificate *peercert;
int verify_old, verify;
PRBool ciphers_old[ciphernum];
PRBool ciphers_new[ciphernum];
char * cipher = NULL;
char * ciphers = NULL;
PRBool cipher_in_list = PR_FALSE;
/*
* Support for SSLRequireSSL directive
*/
if (dc->bSSLRequired && !ssl) {
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
"access to %s failed, reason: %s",
r->filename, "SSL connection required");
/* remember forbidden access for strict require option */
apr_table_setn(r->notes, "ssl-access-forbidden", "1");
return HTTP_FORBIDDEN;
}
/*
* Check to see if SSL protocol is enabled. If it's not then
* no further access control checks are relevant. The test for
* sc->enabled is probably strictly unnecessary
*/
if (!((sc->enabled == TRUE) || !ssl)) {
return DECLINED;
}
/*
* 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 NSSCipherSuite
*
* 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 ``NSSOptions +OptRenegotiate''. So we do no
* implicit optimizations.
*/
if (dc->szCipherSuite) {
/* remember old state */
for (i=0; i < ciphernum; i++) {
SSL_CipherPrefGet(ssl, ciphers_def[i].num, &ciphers_old[i]);
}
if (dc->nOptions & SSL_OPT_OPTRENEGOTIATE) {
int on, keySize, secretKeySize;
char *issuer, *subject;
SSL_SecurityStatus(ssl, &on, &cipher,
&keySize, &secretKeySize, &issuer,
&subject);
}
/* configure new state */
for (i=0; iszCipherSuite);
if (nss_parse_ciphers(r->server, ciphers, ciphers_new) < 0) {
ap_log_error(APLOG_MARK, APLOG_WARNING, 0,
r->server,
"Unable to reconfigure (per-directory) "
"permitted SSL ciphers");
nss_log_nss_error(APLOG_MARK, APLOG_ERR, r->server);
free(ciphers);
return HTTP_FORBIDDEN;
}
free(ciphers);
/* Disable all ciphers so only the ones we want will be available */
for (i = 0; i < SSL_NumImplementedCiphers; i++)
{
SSL_CipherPrefSet(ssl, SSL_ImplementedCiphers[i], SSL_NOT_ALLOWED);
}
/* Actually enable the selected ciphers. Also check to
see if the existing cipher is in the new list for
a possible optimization later. */
for (i=0; inOptions & SSL_OPT_OPTRENEGOTIATE) {
if (cipher_in_list != PR_TRUE)
renegotiate = TRUE;
}
else {
/* paranoid way */
for (i=0; iserver,
"Reconfigured cipher suite 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) {
PRInt32 on;
/* remember old state */
SSL_OptionGet(ssl, SSL_REQUIRE_CERTIFICATE, &on);
if (on == PR_TRUE) {
verify_old = SSL_CVERIFY_REQUIRE;
} else {
SSL_OptionGet(ssl, SSL_REQUEST_CERTIFICATE, &on);
if (on == PR_TRUE)
verify_old = SSL_CVERIFY_OPTIONAL;
else
verify_old = SSL_CVERIFY_NONE;
}
/* configure new state */
verify = dc->nVerifyClient;
if (verify == SSL_CVERIFY_REQUIRE) {
SSL_OptionSet(ssl, SSL_REQUEST_CERTIFICATE, PR_TRUE);
SSL_OptionSet(ssl, SSL_REQUIRE_CERTIFICATE, SSL_REQUIRE_ALWAYS);
} else if (verify == SSL_CVERIFY_OPTIONAL) {
SSL_OptionSet(ssl, SSL_REQUEST_CERTIFICATE, PR_TRUE);
SSL_OptionSet(ssl, SSL_REQUIRE_CERTIFICATE, SSL_REQUIRE_NEVER);
} else {
SSL_OptionSet(ssl, SSL_REQUEST_CERTIFICATE, PR_FALSE);
SSL_OptionSet(ssl, SSL_REQUIRE_CERTIFICATE, SSL_REQUIRE_NEVER);
}
/* determine whether we've to force a renegotiation */
if (!renegotiate && verify != verify_old) {
if (((verify_old == SSL_CVERIFY_NONE) &&
(verify != SSL_CVERIFY_NONE)) ||
(!(verify_old & SSL_CVERIFY_OPTIONAL) &&
(verify & SSL_CVERIFY_OPTIONAL)) ||
(!(verify_old & SSL_CVERIFY_REQUIRE) &&
(verify & SSL_CVERIFY_REQUIRE)))
{
renegotiate = TRUE;
/* optimization */
if ((dc->nOptions & SSL_OPT_OPTRENEGOTIATE) &&
(verify_old == SSL_CVERIFY_NONE) &&
((peercert = SSL_PeerCertificate(ssl)) != NULL))
{
renegotiate_quick = TRUE;
CERT_DestroyCertificate(peercert);
}
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0,
r->server,
"Changed client verification type will force "
"%srenegotiation",
renegotiate_quick ? "quick " : "");
}
}
}
/* 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.
*/
if (renegotiate && !renegotiate_quick
&& (apr_table_get(r->headers_in, "transfer-encoding")
|| (apr_table_get(r->headers_in, "content-length")
&& strcmp(apr_table_get(r->headers_in, "content-length"), "0")))
&& !r->expecting_100) {
int rv;
apr_size_t rsize;
rsize = dc->nRenegBufferSize == UNSET ? DEFAULT_RENEG_BUFFER_SIZE :
dc->nRenegBufferSize;
if (rsize > 0) {
/* Fill the I/O buffer with the request body if possible. */
rv = nss_io_buffer_fill(r, rsize);
} else {
/* If the reneg buffer size is set to zero, just fail. */
rv = HTTP_REQUEST_ENTITY_TOO_LARGE;
}
if (rv) {
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
"could not buffer message body to allow "
"SSL renegotiation to proceed");
return rv;
}
}
/*
* 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
* 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.
*/
ap_log_error(APLOG_MARK, APLOG_INFO, 0, r->server,
"Requesting connection re-negotiation");
if (renegotiate_quick) {
SECStatus rv;
CERTCertificate *peerCert;
void *pinArg;
/* perform just a manual re-verification of the peer */
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
"Performing quick renegotiation: "
"just re-verifying the peer");
peerCert = SSL_PeerCertificate(sslconn->ssl);
pinArg = SSL_RevealPinArg(sslconn->ssl);
rv = CERT_VerifyCertNow(CERT_GetDefaultCertDB(),
peerCert,
PR_TRUE,
certUsageSSLClient,
pinArg);
CERT_DestroyCertificate(peerCert);
if (rv != SECSuccess) {
ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server,
"Re-negotiation handshake failed: "
"Client verification failed");
return HTTP_FORBIDDEN;
}
/* The cert is ok, fall through to the check SSLRequires */
}
else {
int handshake_done = 0;
int result = 0;
/* do a full renegotiation */
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
"Performing full renegotiation: "
"complete handshake protocol");
/* Do NOT call SSL_ResetHandshake as this will tear down the
* existing connection.
*/
if (SSL_HandshakeCallback(ssl, HandshakeDone, (void *)&handshake_done) || SSL_ReHandshake(ssl, PR_TRUE)) {
int errCode = PR_GetError();
if (errCode == SEC_ERROR_INVALID_ARGS) {
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
"Re-negotation request failed: "
"trying to do client authentication on a non-SSL3 connection");
} else {
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
"Re-negotation request failed: "
"returned error %d", errCode);
}
r->connection->aborted = 1;
return HTTP_FORBIDDEN;
}
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
"Awaiting re-negotiation handshake");
while (!handshake_done) {
apr_bucket_brigade *bb;
conn_rec *c = r->connection;
bb = apr_brigade_create(c->pool, c->bucket_alloc);
result = SSL_ForceHandshake(ssl);
if (result >= 0) {
/* This flag gets set by the NSS callback */
if (handshake_done)
break;
/* We need to force a read so the SSL data can be
* handled
*/
if ((result = ap_get_brigade(c->input_filters, bb,
AP_MODE_GETLINE, APR_BLOCK_READ, HUGE_STRING_LEN) !=
APR_SUCCESS || APR_BRIGADE_EMPTY(bb))) {
apr_brigade_destroy(bb);
break;
}
} else {
ap_log_error(APLOG_MARK, APLOG_INFO, 0, r->server,
"Read error %d", PR_GetError());
break;
}
}
/* Reset the ssl callback */
SSL_HandshakeCallback(ssl, NULL, NULL);
if (result < 0) {
ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server,
"Re-negotiation handshake failed: "
"Not accepted by client!?");
#if 0
r->connection->aborted = 1;
#endif
return HTTP_FORBIDDEN;
}
}
if (cipher || !cipher_in_list) {
int on, keySize, secretKeySize;
char *issuer, *subject;
SSL_SecurityStatus(ssl, &on, &cipher,
&keySize, &secretKeySize, &issuer,
&subject);
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
"Re-negotiated cipher %s", cipher);
}
/*
* Remember the peer certificate's DN
*/
if ((cert = SSL_PeerCertificate(ssl))) {
if (sslconn->client_cert) {
CERT_DestroyCertificate(sslconn->client_cert);
}
sslconn->client_cert = cert;
sslconn->client_dn = NULL;
}
}
/* 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.
*/
if ((dc->nOptions & SSL_OPT_FAKEBASICAUTH) == 0 && dc->szUserName) {
char *val = nss_var_lookup(r->pool, r->server, r->connection,
r, (char *)dc->szUserName);
if (val && val[0]) {
/* RFC2617 denies usage of colon in BasicAuth */
char *colon = strchr(val, ':');
if (colon == NULL) {
r->user = val;
}
else {
cp = apr_psprintf(r->pool,
"FakeBasicAuth is configured and colon "
"(\":\") character exists in the \"%s\" "
"username", val);
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
"access to %s failed, reason: %s",
r->filename, cp);
return HTTP_FORBIDDEN;
}
}
}
/*
* Check SSLRequire boolean expressions
*/
requires = dc->aRequirement;
nss_requires = (nss_require_t *)requires->elts;
for (i = 0; i < requires->nelts; i++) {
nss_require_t *req = &nss_requires[i];
ok = nss_expr_exec(r, req->mpExpr);
if (ok < 0) {
cp = apr_psprintf(r->pool,
"Failed to execute "
"SSL requirement expression: %s",
nss_expr_get_error());
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
"access to %s failed, reason: %s",
r->filename, cp);
/* remember forbidden access for strict require option */
apr_table_setn(r->notes, "ssl-access-forbidden", "1");
return HTTP_FORBIDDEN;
}
if (ok != 1) {
ap_log_error(APLOG_MARK, APLOG_INFO, 0, r->server,
"Access to %s denied for %s "
"(requirement expression not fulfilled)",
#if AP_SERVER_MINORVERSION_NUMBER <= 2
r->filename, r->connection->remote_ip);
#else
r->filename, r->connection->client_ip);
#endif
ap_log_error(APLOG_MARK, APLOG_INFO, 0, r->server,
"Failed expression: %s", req->cpExpr);
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
"access to %s failed, reason: %s",
r->filename,
"SSL requirement expression not fulfilled "
"(see SSL logfile for more details)");
/* remember forbidden access for strict require option */
apr_table_setn(r->notes, "ssl-access-forbidden", "1");
return HTTP_FORBIDDEN;
}
}
/*
* 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
* deny access.
*/
return DECLINED;
}
/*
* 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.
*/
int nss_hook_UserCheck(request_rec *r)
{
SSLConnRec *sslconn = myConnConfig(r->connection);
SSLSrvConfigRec *sc = mySrvConfig(r->server);
SSLDirConfigRec *dc = myDirConfig(r);
char *clientdn;
const char *auth_line, *username, *password;
/*
* Additionally forbid access (again)
* when strict require option is used.
*/
if ((dc->nOptions & SSL_OPT_STRICTREQUIRE) &&
(apr_table_get(r->notes, "ssl-access-forbidden")))
{
return HTTP_FORBIDDEN;
}
/*
* We decline when we are in a subrequest. The Authorization header
* would already be present if it was added in the main request.
*/
if (!ap_is_initial_req(r)) {
return DECLINED;
}
/*
* 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
* password.
*/
if ((auth_line = apr_table_get(r->headers_in, "Authorization"))) {
if (strcEQ(ap_getword(r->pool, &auth_line, ' '), "Basic")) {
while ((*auth_line == ' ') || (*auth_line == '\t')) {
auth_line++;
}
auth_line = ap_pbase64decode(r->pool, auth_line);
username = ap_getword_nulls(r->pool, &auth_line, ':');
password = auth_line;
if ((username[0] == '/') && strEQ(password, "password")) {
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
"Encountered FakeBasicAuth spoof: %s", username);
return HTTP_FORBIDDEN;
}
}
}
/*
* We decline operation in various situations...
* - NSSOptions +FakeBasicAuth not configured
* - r->user already authenticated
* - ssl not enabled
* - client did not present a certificate
*/
if (!((sc->enabled == TRUE) && sslconn && sslconn->ssl && sslconn->client_cert) ||
!(dc->nOptions & SSL_OPT_FAKEBASICAUTH) || r->user)
{
return DECLINED;
}
if (!sslconn->client_dn) {
char * cp = CERT_NameToAscii(&sslconn->client_cert->subject);
sslconn->client_dn = apr_pstrcat(r->connection->pool, "/", cp, NULL);
PORT_Free(cp);
}
clientdn = (char *)sslconn->client_dn;
char *colon = strchr(clientdn, ':');
if (colon != NULL) {
char *cp = apr_psprintf(r->pool,
"FakeBasicAuth is configured and colon "
"(\":\") character exists in the \"%s\" ",
"username", clientdn);
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
"access to %s failed, reason: %s",
r->filename, cp);
return HTTP_FORBIDDEN;
}
/*
* 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" ;-)
*
* The NSS module is retaining this for compatibility purposes.
*/
auth_line = apr_pstrcat(r->pool, "Basic ",
ap_pbase64encode(r->pool,
apr_pstrcat(r->pool, clientdn,
":password", NULL)),
NULL);
apr_table_set(r->headers_in, "Authorization", auth_line);
ap_log_error(APLOG_MARK, APLOG_INFO, 0, r->server,
"Faking HTTP Basic Auth header: \"Authorization: %s\"",
auth_line);
return DECLINED;
}
/* authorization phase */
int nss_hook_Auth(request_rec *r)
{
SSLDirConfigRec *dc = myDirConfig(r);
ap_log_error(APLOG_MARK, APLOG_INFO, 0, r->server, "nss_hook_Auth");
/*
* Additionally forbid access (again)
* when strict require option is used.
*/
if ((dc->nOptions & SSL_OPT_STRICTREQUIRE) &&
(apr_table_get(r->notes, "ssl-access-forbidden")))
{
return HTTP_FORBIDDEN;
}
return DECLINED;
}
/*
* Fixup Handler
*/
static const char *nss_hook_Fixup_vars[] = {
"SSL_VERSION_INTERFACE",
"SSL_VERSION_LIBRARY",
"SSL_PROTOCOL",
"SSL_SECURE_RENEG",
"SSL_CIPHER",
"SSL_CIPHER_NAME",
"SSL_CIPHER_EXPORT",
"SSL_CIPHER_USEKEYSIZE",
"SSL_CIPHER_ALGKEYSIZE",
"SSL_CLIENT_VERIFY",
"SSL_CLIENT_M_VERSION",
"SSL_CLIENT_M_SERIAL",
"SSL_CLIENT_V_START",
"SSL_CLIENT_V_END",
"SSL_CLIENT_V_REMAIN",
"SSL_CLIENT_S_DN",
"SSL_CLIENT_S_DN_C",
"SSL_CLIENT_S_DN_ST",
"SSL_CLIENT_S_DN_L",
"SSL_CLIENT_S_DN_O",
"SSL_CLIENT_S_DN_OU",
"SSL_CLIENT_S_DN_CN",
"SSL_CLIENT_S_DN_T",
"SSL_CLIENT_S_DN_I",
"SSL_CLIENT_S_DN_G",
"SSL_CLIENT_S_DN_S",
"SSL_CLIENT_S_DN_D",
"SSL_CLIENT_S_DN_UID",
"SSL_CLIENT_S_DN_Email",
"SSL_CLIENT_I_DN",
"SSL_CLIENT_I_DN_C",
"SSL_CLIENT_I_DN_ST",
"SSL_CLIENT_I_DN_L",
"SSL_CLIENT_I_DN_O",
"SSL_CLIENT_I_DN_OU",
"SSL_CLIENT_I_DN_CN",
"SSL_CLIENT_I_DN_T",
"SSL_CLIENT_I_DN_I",
"SSL_CLIENT_I_DN_G",
"SSL_CLIENT_I_DN_S",
"SSL_CLIENT_I_DN_D",
"SSL_CLIENT_I_DN_UID",
"SSL_CLIENT_I_DN_Email",
"SSL_CLIENT_A_KEY",
"SSL_CLIENT_A_SIG",
"SSL_SERVER_M_VERSION",
"SSL_SERVER_M_SERIAL",
"SSL_SERVER_V_START",
"SSL_SERVER_V_END",
"SSL_SERVER_S_DN",
"SSL_SERVER_S_DN_C",
"SSL_SERVER_S_DN_ST",
"SSL_SERVER_S_DN_L",
"SSL_SERVER_S_DN_O",
"SSL_SERVER_S_DN_OU",
"SSL_SERVER_S_DN_CN",
"SSL_SERVER_S_DN_T",
"SSL_SERVER_S_DN_I",
"SSL_SERVER_S_DN_G",
"SSL_SERVER_S_DN_S",
"SSL_SERVER_S_DN_D",
"SSL_SERVER_S_DN_UID",
"SSL_SERVER_S_DN_Email",
"SSL_SERVER_I_DN",
"SSL_SERVER_I_DN_C",
"SSL_SERVER_I_DN_ST",
"SSL_SERVER_I_DN_L",
"SSL_SERVER_I_DN_O",
"SSL_SERVER_I_DN_OU",
"SSL_SERVER_I_DN_CN",
"SSL_SERVER_I_DN_T",
"SSL_SERVER_I_DN_I",
"SSL_SERVER_I_DN_G",
"SSL_SERVER_I_DN_S",
"SSL_SERVER_I_DN_D",
"SSL_SERVER_I_DN_UID",
"SSL_SERVER_I_DN_Email",
"SSL_SERVER_A_KEY",
"SSL_SERVER_A_SIG",
"SSL_SESSION_ID",
NULL
};
int nss_hook_Fixup(request_rec *r)
{
SSLConnRec *sslconn = myConnConfig(r->connection);
SSLSrvConfigRec *sc = mySrvConfig(r->server);
SSLDirConfigRec *dc = myDirConfig(r);
PRFileDesc * ssl;
apr_table_t *env = r->subprocess_env;
char *var, *val = "";
int i;
CERTCertificate *cert;
CERTCertificateList *chain = NULL;
SECItem *hostInfo = NULL;
const char *servername;
/*
* Check to see if SSL is on
*/
if (!((sc->enabled == TRUE) && sslconn && (ssl = sslconn->ssl))) {
return DECLINED;
}
/*
* Set r->user if requested
*/
if (dc->szUserName) {
val = nss_var_lookup(r->pool, r->server, r->connection,
r, (char *)dc->szUserName);
if (val && val[0]) {
r->user = val;
}
}
/*
* Annotate the SSI/CGI environment with standard SSL information
*/
/* the always present HTTPS (=HTTP over SSL) flag! */
apr_table_setn(env, "HTTPS", "on");
/* add content of SNI TLS extension (if supplied with ClientHello) */
hostInfo = SSL_GetNegotiatedHostInfo(ssl);
if (hostInfo) {
servername = apr_pstrndup(r->pool, (char *) hostInfo->data, hostInfo->len);
apr_table_set(env, "SSL_TLS_SNI", servername);
SECITEM_FreeItem(hostInfo, PR_TRUE);
}
/* standard SSL environment variables */
if (dc->nOptions & SSL_OPT_STDENVVARS) {
for (i = 0; nss_hook_Fixup_vars[i]; i++) {
var = (char *)nss_hook_Fixup_vars[i];
val = nss_var_lookup(r->pool, r->server, r->connection, r, var);
if (!strIsEmpty(val)) {
apr_table_setn(env, var, val);
}
}
}
/*
* On-demand bloat up the SSI/CGI environment with certificate data
*/
if (dc->nOptions & SSL_OPT_EXPORTCERTDATA) {
val = nss_var_lookup(r->pool, r->server, r->connection,
r, "SSL_SERVER_CERT");
apr_table_setn(env, "SSL_SERVER_CERT", val);
val = nss_var_lookup(r->pool, r->server, r->connection,
r, "SSL_CLIENT_CERT");
apr_table_setn(env, "SSL_CLIENT_CERT", val);
/* Need to fetch the entire SSL cert chain and add it to the
* variable SSL_CLIENT_CERT_CHAIN_[0..n]
*/
cert = SSL_PeerCertificate(ssl);
if (cert)
chain = CERT_CertChainFromCert(cert, certUsageSSLClient, PR_TRUE);
if (cert && chain) {
int n;
n = chain->len;
CERT_DestroyCertificateList(chain);
for (i = 0; i < n; i++) {
var = apr_psprintf(r->pool, "SSL_CLIENT_CERT_CHAIN_%d", i);
val = nss_var_lookup(r->pool, r->server, r->connection,
r, var);
if (val) {
apr_table_setn(env, var, val);
}
}
}
if (cert)
CERT_DestroyCertificate(cert);
}
return DECLINED;
}
static void HandshakeDone(PRFileDesc *fd, void *doneflag)
{
/*
* This routine is called by SSL when the handshake is
* complete. It sets local state so that the loop doing
* reads from the socket can exit.
*/
*((int *)doneflag) = 1;
}