#include "squid.h"
#include "AccessLogEntry.h"
#include "HttpRequest.h"
- AccessLogEntry::Ssl::Ssl(): user(NULL), bumpMode(::Ssl::bumpEnd)
+#include "ssl/support.h"
+
+#if USE_SSL
++AccessLogEntry::SslDetails::SslDetails(): user(NULL), bumpMode(::Ssl::bumpEnd)
+{
+}
+#endif /* USE_SSL */
+
void
AccessLogEntry::getLogClientIp(char *buf, size_t bufsz) const
const char *opcode;
} htcp;
- class Ssl {
+#if USE_SSL
+ /// logging information specific to the SSL protocol
- Ssl();
++ class SslDetails {
+ public:
++ SslDetails();
+
+ const char *user; ///< emailAddress from the SSL client certificate
+ int bumpMode; ///< whether and how the request was SslBumped
+ } ssl;
+#endif
+
/** \brief This subclass holds log info for Squid internal stats
* \todo Inner class declarations should be moved outside
* \todo some details relevant to particular protocols need shuffling to other sub-classes
cfg->oldest_service_failure = 0;
cfg->service_failure_limit = 0;
}
+#endif
+
+#if USE_SSL
+static void parse_sslproxy_cert_adapt(sslproxy_cert_adapt **cert_adapt)
+{
+ char *al;
+ sslproxy_cert_adapt *ca = (sslproxy_cert_adapt *) xcalloc(1, sizeof(sslproxy_cert_adapt));
+ if ((al = strtok(NULL, w_space)) == NULL) {
+ self_destruct();
+ return;
+ }
+
+ const char *param;
+ if ( char *s = strchr(al, '{')) {
+ *s = '\0'; // terminate the al string
+ s++;
+ param = s;
+ s = strchr(s, '}');
+ if (!s) {
+ self_destruct();
+ return;
+ }
+ *s = '\0';
+ }
+ else
+ param = NULL;
+
+ if (strcmp(al, Ssl::CertAdaptAlgorithmStr[Ssl::algSetValidAfter]) == 0) {
+ ca->alg = Ssl::algSetValidAfter;
+ ca->param = strdup("on");
+ }
+ else if (strcmp(al, Ssl::CertAdaptAlgorithmStr[Ssl::algSetValidBefore]) == 0) {
+ ca->alg = Ssl::algSetValidBefore;
+ ca->param = strdup("on");
+ }
+ else if (strcmp(al, Ssl::CertAdaptAlgorithmStr[Ssl::algSetCommonName]) == 0) {
+ ca->alg = Ssl::algSetCommonName;
+ if (param) {
+ if (strlen(param) > 64) {
+ debugs(3, DBG_CRITICAL, "FATAL: sslproxy_cert_adapt: setCommonName{" <<param << "} : using common name longer than 64 bytes is not supported");
+ self_destruct();
+ return;
+ }
+ ca->param = strdup(param);
+ }
+ } else {
+ debugs(3, DBG_CRITICAL, "FATAL: sslproxy_cert_adapt: unknown cert adaptation algorithm: " << al);
+ self_destruct();
+ return;
+ }
+
+ aclParseAclList(LegacyParser, &ca->aclList);
+
+ while(*cert_adapt)
+ cert_adapt = &(*cert_adapt)->next;
+
+ *cert_adapt = ca;
+}
+
+static void dump_sslproxy_cert_adapt(StoreEntry *entry, const char *name, sslproxy_cert_adapt *cert_adapt)
+{
+ for (sslproxy_cert_adapt *ca = cert_adapt; ca != NULL; ca = ca->next) {
+ storeAppendPrintf(entry, "%s ", name);
+ storeAppendPrintf(entry, "%s{%s} ", Ssl::sslCertAdaptAlgoritm(ca->alg), ca->param);
+ if (ca->aclList)
+ dump_acl_list(entry, ca->aclList);
+ storeAppendPrintf(entry, "\n");
+ }
+}
+
+static void free_sslproxy_cert_adapt(sslproxy_cert_adapt **cert_adapt)
+{
+ while(*cert_adapt) {
+ sslproxy_cert_adapt *ca = *cert_adapt;
+ *cert_adapt = ca->next;
+ safe_free(ca->param);
+
+ if (ca->aclList)
+ aclDestroyAclList(&ca->aclList);
+
+ safe_free(ca);
+ }
+}
+
+static void parse_sslproxy_cert_sign(sslproxy_cert_sign **cert_sign)
+{
+ char *al;
+ sslproxy_cert_sign *cs = (sslproxy_cert_sign *) xcalloc(1, sizeof(sslproxy_cert_sign));
+ if ((al = strtok(NULL, w_space)) == NULL) {
+ self_destruct();
+ return;
+ }
+
+ if (strcmp(al, Ssl::CertSignAlgorithmStr[Ssl::algSignTrusted]) == 0)
+ cs->alg = Ssl::algSignTrusted;
+ else if (strcmp(al, Ssl::CertSignAlgorithmStr[Ssl::algSignUntrusted]) == 0)
+ cs->alg = Ssl::algSignUntrusted;
+ else if (strcmp(al, Ssl::CertSignAlgorithmStr[Ssl::algSignSelf]) == 0)
+ cs->alg = Ssl::algSignSelf;
+ else {
+ debugs(3, DBG_CRITICAL, "FATAL: sslproxy_cert_sign: unknown cert signing algorithm: " << al);
+ self_destruct();
+ return;
+ }
+
+ aclParseAclList(LegacyParser, &cs->aclList);
+
+ while(*cert_sign)
+ cert_sign = &(*cert_sign)->next;
+
+ *cert_sign = cs;
+}
+
+static void dump_sslproxy_cert_sign(StoreEntry *entry, const char *name, sslproxy_cert_sign *cert_sign)
+{
+ sslproxy_cert_sign *cs;
+ for (cs = cert_sign; cs != NULL; cs = cs->next) {
+ storeAppendPrintf(entry, "%s ", name);
+ storeAppendPrintf(entry, "%s ", Ssl::certSignAlgorithm(cs->alg));
+ if (cs->aclList)
+ dump_acl_list(entry, cs->aclList);
+ storeAppendPrintf(entry, "\n");
+ }
+}
+
+static void free_sslproxy_cert_sign(sslproxy_cert_sign **cert_sign)
+{
+ while(*cert_sign) {
+ sslproxy_cert_sign *cs = *cert_sign;
+ *cert_sign = cs->next;
+
+ if (cs->aclList)
+ aclDestroyAclList(&cs->aclList);
+
+ safe_free(cs);
+ }
+}
+
+class sslBumpCfgRr: public ::RegisteredRunner
+{
+public:
+ static Ssl::BumpMode lastDeprecatedRule;
+ /* RegisteredRunner API */
+ virtual void run(const RunnerRegistry &);
+};
+
+Ssl::BumpMode sslBumpCfgRr::lastDeprecatedRule = Ssl::bumpEnd;
+
+RunnerRegistrationEntry(rrFinalizeConfig, sslBumpCfgRr);
+
+void sslBumpCfgRr::run(const RunnerRegistry &r)
+{
+ if (lastDeprecatedRule != Ssl::bumpEnd) {
+ assert( lastDeprecatedRule == Ssl::bumpClientFirst || lastDeprecatedRule == Ssl::bumpNone);
+ static char buf[1024];
+ if (lastDeprecatedRule == Ssl::bumpClientFirst) {
+ strcpy(buf, "ssl_bump deny all");
+ debugs(3, DBG_CRITICAL, "WARNING: auto-converting deprecated implicit "
+ "\"ssl_bump deny all\" to \"ssl_bump none all\". New ssl_bump configurations "
+ "must not use implicit rules. Update your ssl_bump rules.");
+ } else {
+ strcpy(buf, "ssl_bump allow all");
+ debugs(3, DBG_CRITICAL, "SECURITY NOTICE: auto-converting deprecated implicit "
+ "\"ssl_bump allow all\" to \"ssl_bump client-first all\" which is usually "
+ "inferior to the newer server-first bumping mode. New ssl_bump"
+ " configurations must not use implicit rules. Update your ssl_bump rules.");
+ }
+ parse_line(buf);
+ }
+}
+
+static void parse_sslproxy_ssl_bump(acl_access **ssl_bump)
+{
+ typedef const char *BumpCfgStyle;
+ BumpCfgStyle bcsNone = NULL;
+ BumpCfgStyle bcsNew = "new client/server-first/none";
+ BumpCfgStyle bcsOld = "deprecated allow/deny";
+ static BumpCfgStyle bumpCfgStyleLast = bcsNone;
+ BumpCfgStyle bumpCfgStyleNow = bcsNone;
+ char *bm;
+ if ((bm = strtok(NULL, w_space)) == NULL) {
+ self_destruct();
+ return;
+ }
+
+ // if this is the first rule proccessed
+ if (*ssl_bump == NULL) {
+ bumpCfgStyleLast = bcsNone;
+ sslBumpCfgRr::lastDeprecatedRule = Ssl::bumpEnd;
+ }
+
+ acl_access *A = new acl_access;
+ A->allow = allow_t(ACCESS_ALLOWED);
+
+ if (strcmp(bm, Ssl::BumpModeStr[Ssl::bumpClientFirst]) == 0) {
+ A->allow.kind = Ssl::bumpClientFirst;
+ bumpCfgStyleNow = bcsNew;
+ } else if (strcmp(bm, Ssl::BumpModeStr[Ssl::bumpServerFirst]) == 0) {
+ A->allow.kind = Ssl::bumpServerFirst;
+ bumpCfgStyleNow = bcsNew;
+ } else if (strcmp(bm, Ssl::BumpModeStr[Ssl::bumpNone]) == 0) {
+ A->allow.kind = Ssl::bumpNone;
+ bumpCfgStyleNow = bcsNew;
+ } else if (strcmp(bm, "allow") == 0) {
+ debugs(3, DBG_CRITICAL, "SECURITY NOTICE: auto-converting deprecated "
+ "\"ssl_bump allow <acl>\" to \"ssl_bump client-first <acl>\" which "
+ "is usually inferior to the newer server-first "
+ "bumping mode. Update your ssl_bump rules.");
+ A->allow.kind = Ssl::bumpClientFirst;
+ bumpCfgStyleNow = bcsOld;
+ sslBumpCfgRr::lastDeprecatedRule = Ssl::bumpClientFirst;
+ } else if (strcmp(bm, "deny") == 0) {
+ debugs(3, DBG_CRITICAL, "WARNING: auto-converting deprecated "
+ "\"ssl_bump deny <acl>\" to \"ssl_bump none <acl>\". Update "
+ "your ssl_bump rules.");
+ A->allow.kind = Ssl::bumpNone;
+ bumpCfgStyleNow = bcsOld;
+ sslBumpCfgRr::lastDeprecatedRule = Ssl::bumpNone;
+ } else {
+ debugs(3, DBG_CRITICAL, "FATAL: unknown ssl_bump mode: " << bm);
+ self_destruct();
+ return;
+ }
+
+ if (bumpCfgStyleLast != bcsNone && bumpCfgStyleNow != bumpCfgStyleLast) {
+ debugs(3, DBG_CRITICAL, "FATAL: do not mix " << bumpCfgStyleNow << " actions with " <<
+ bumpCfgStyleLast << " actions. Update your ssl_bump rules.");
+ self_destruct();
+ return;
+ }
+
+ bumpCfgStyleLast = bumpCfgStyleNow;
+
+ aclParseAclList(LegacyParser, &A->aclList);
+
+ acl_access *B, **T;
+ for (B = *ssl_bump, T = ssl_bump; B; T = &B->next, B = B->next);
+ *T = A;
+}
+
+static void dump_sslproxy_ssl_bump(StoreEntry *entry, const char *name, acl_access *ssl_bump)
+{
+ acl_access *sb;
+ for (sb = ssl_bump; sb != NULL; sb = sb->next) {
+ storeAppendPrintf(entry, "%s ", name);
+ storeAppendPrintf(entry, "%s ", Ssl::bumpMode(sb->allow.kind));
+ if (sb->aclList)
+ dump_acl_list(entry, sb->aclList);
+ storeAppendPrintf(entry, "\n");
+ }
+}
+
+static void free_sslproxy_ssl_bump(acl_access **ssl_bump)
+{
+ free_acl_access(ssl_bump);
+}
#endif
+
+ static void dump_HeaderWithAclList(StoreEntry * entry, const char *name, HeaderWithAclList *headers)
+ {
+ if (!headers)
+ return;
+
+ for (HeaderWithAclList::iterator hwa = headers->begin(); hwa != headers->end(); ++hwa) {
+ storeAppendPrintf(entry, "%s ", hwa->fieldName.c_str());
+ storeAppendPrintf(entry, "%s ", hwa->fieldValue.c_str());
+ if (hwa->aclList)
+ dump_acl_list(entry, hwa->aclList);
+ storeAppendPrintf(entry, "\n");
+ }
+ }
+
+ static void parse_HeaderWithAclList(HeaderWithAclList **headers)
+ {
+ char *fn;
+ if (!*headers) {
+ *headers = new HeaderWithAclList;
+ }
+ if ((fn = strtok(NULL, w_space)) == NULL) {
+ self_destruct();
+ return;
+ }
+ HeaderWithAcl hwa;
+ hwa.fieldName = fn;
+ hwa.fieldId = httpHeaderIdByNameDef(fn, strlen(fn));
+ if (hwa.fieldId == HDR_BAD_HDR)
+ hwa.fieldId = HDR_OTHER;
+
+ String buf;
+ bool wasQuoted;
+ ConfigParser::ParseQuotedString(&buf, &wasQuoted);
+ hwa.fieldValue = buf.termedBuf();
+ hwa.quoted = wasQuoted;
+ if (hwa.quoted) {
+ Format::Format *nlf = new ::Format::Format("hdrWithAcl");
+ if (!nlf->parse(hwa.fieldValue.c_str())) {
+ self_destruct();
+ return;
+ }
+ hwa.valueFormat = nlf;
+ }
+ aclParseAclList(LegacyParser, &hwa.aclList);
+ (*headers)->push_back(hwa);
+ }
+
+ static void free_HeaderWithAclList(HeaderWithAclList **header)
+ {
+ if (!(*header))
+ return;
+
+ for (HeaderWithAclList::iterator hwa = (*header)->begin(); hwa != (*header)->end(); ++hwa) {
+ if (hwa->aclList)
+ aclDestroyAclList(&hwa->aclList);
+
+ if (hwa->valueFormat) {
+ delete hwa->valueFormat;
+ hwa->valueFormat = NULL;
+ }
+ }
+ delete *header;
+ *header = NULL;
+ }
readSomeData();
}
- if (!context->http->al.request)
- context->http->al.request = HTTPMSGLOCK(http->request);
+void
+ConnStateData::quitAfterError(HttpRequest *request)
+{
+ // From HTTP p.o.v., we do not have to close after every error detected
+ // at the client-side, but many such errors do require closure and the
+ // client-side code is bad at handling errors so we play it safe.
+ if (request)
+ request->flags.proxy_keepalive = 0;
+ flags.readMore = false;
+ debugs(33,4, HERE << "Will close after error: " << clientConnection);
+}
+
+#if USE_SSL
+bool ConnStateData::serveDelayedError(ClientSocketContext *context)
+{
+ ClientHttpRequest *http = context->http;
+
+ if (!sslServerBump)
+ return false;
+
+ assert(sslServerBump->entry);
+ // Did we create an error entry while processing CONNECT?
+ if (!sslServerBump->entry->isEmpty()) {
+ quitAfterError(http->request);
+
+ // Get the saved error entry and send it to the client by replacing the
+ // ClientHttpRequest store entry with it.
+ clientStreamNode *node = context->getClientReplyContext();
+ clientReplyContext *repContext = dynamic_cast<clientReplyContext *>(node->data.getRaw());
+ assert(repContext);
+ debugs(33, 5, "Responding with delated error for " << http->uri);
+ repContext->setReplyToStoreEntry(sslServerBump->entry);
+
+ // save the original request for logging purposes
- if (!context->http->al.request)
- context->http->al.request = HTTPMSGLOCK(request);
++ if (!context->http->al->request)
++ context->http->al->request = HTTPMSGLOCK(http->request);
+
+ // Get error details from the fake certificate-peeking request.
+ http->request->detailError(sslServerBump->request->errType, sslServerBump->request->errDetail);
+ context->pullData();
+ return true;
+ }
+
+ // In bump-server-first mode, we have not necessarily seen the intended
+ // server name at certificate-peeking time. Check for domain mismatch now,
+ // when we can extract the intended name from the bumped HTTP request.
+ if (sslServerBump->serverCert.get()) {
+ HttpRequest *request = http->request;
+ if (!Ssl::checkX509ServerValidity(sslServerBump->serverCert.get(), request->GetHost())) {
+ debugs(33, 2, "SQUID_X509_V_ERR_DOMAIN_MISMATCH: Certificate " <<
+ "does not match domainname " << request->GetHost());
+
+ ACLFilledChecklist check(Config.ssl_client.cert_error, request, dash_str);
+ check.sslErrors = new Ssl::Errors(SQUID_X509_V_ERR_DOMAIN_MISMATCH);
+ if (Comm::IsConnOpen(pinning.serverConnection))
+ check.fd(pinning.serverConnection->fd);
+ const bool allowDomainMismatch =
+ check.fastCheck() == ACCESS_ALLOWED;
+ delete check.sslErrors;
+ check.sslErrors = NULL;
+
+ if (!allowDomainMismatch) {
+ quitAfterError(request);
+
+ clientStreamNode *node = context->getClientReplyContext();
+ clientReplyContext *repContext = dynamic_cast<clientReplyContext *>(node->data.getRaw());
+ assert (repContext);
+
+ // Fill the server IP and hostname for error page generation.
+ HttpRequest::Pointer const & peekerRequest = sslServerBump->request;
+ request->hier.note(peekerRequest->hier.tcpServer, request->GetHost());
+
+ // Create an error object and fill it
+ ErrorState *err = new ErrorState(ERR_SECURE_CONNECT_FAIL, HTTP_SERVICE_UNAVAILABLE, request);
+ err->src_addr = clientConnection->remote;
+ Ssl::ErrorDetail *errDetail = new Ssl::ErrorDetail(
+ SQUID_X509_V_ERR_DOMAIN_MISMATCH,
+ sslServerBump->serverCert.get(), NULL);
+ err->detail = errDetail;
+ // Save the original request for logging purposes.
++ if (!context->http->al->request)
++ context->http->al->request = HTTPMSGLOCK(request);
+ repContext->setReplyToError(request->method, err);
+ assert(context->http->out.offset == 0);
+ context->pullData();
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
+#endif // USE_SSL
+
static void
clientProcessRequest(ConnStateData *conn, HttpParser *hp, ClientSocketContext *context, const HttpRequestMethod& method, HttpVersion http_ver)
{
/* prevent confusion over whether we default to persistent or not */
http->request->flags.proxy_keepalive = 0;
- http->al.http.code = errstate->httpStatus;
+ http->al->http.code = errstate->httpStatus;
createStoreEntry(method, request_flags());
-#if USE_AUTH
- errstate->auth_user_request = auth_user_request;
-#endif
assert(errstate->callback_data == NULL);
errorAppendEntry(http->storeEntry(), errstate);
/* Now the caller reads to get this */
bool
ClientRequestContext::sslBumpAccessCheck()
{
- if (http->request->method == METHOD_CONNECT &&
- Config.accessList.ssl_bump && http->getConn()->port->sslBump) {
- debugs(85, 5, HERE << "SslBump possible, checking ACL");
+ // If SSL connection tunneling or bumping decision has been made, obey it.
+ const Ssl::BumpMode bumpMode = http->getConn()->sslBumpMode;
+ if (bumpMode != Ssl::bumpEnd) {
+ debugs(85, 5, HERE << "SslBump already decided (" << bumpMode <<
+ "), " << "ignoring ssl_bump for " << http->getConn());
- http->al.ssl.bumpMode = bumpMode; // inherited from bumped connection
++ http->al->ssl.bumpMode = bumpMode; // inherited from bumped connection
+ return false;
+ }
- ACLFilledChecklist *acl_checklist = clientAclChecklistCreate(Config.accessList.ssl_bump, http);
- acl_checklist->nonBlockingCheck(sslBumpAccessCheckDoneWrapper, this);
- return true;
- } else {
- http->sslBumpNeeded(false);
+ // If we have not decided yet, decide whether to bump now.
+
+ // Bumping here can only start with a CONNECT request on a bumping port
+ // (bumping of intercepted SSL conns is decided before we get 1st request).
+ // We also do not bump redirected CONNECT requests.
+ if (http->request->method != METHOD_CONNECT || http->redirect.status ||
+ !Config.accessList.ssl_bump || !http->getConn()->port->sslBump) {
- http->al.ssl.bumpMode = Ssl::bumpEnd; // SslBump does not apply; log -
++ http->al->ssl.bumpMode = Ssl::bumpEnd; // SslBump does not apply; log -
+ debugs(85, 5, HERE << "cannot SslBump this request");
return false;
}
- http->al.ssl.bumpMode = Ssl::bumpEnd; // SslBump does not apply; log -
+
+ // Do not bump during authentication: clients would not proxy-authenticate
+ // if we delay a 407 response and respond with 200 OK to CONNECT.
+ if (error && error->httpStatus == HTTP_PROXY_AUTHENTICATION_REQUIRED) {
++ http->al->ssl.bumpMode = Ssl::bumpEnd; // SslBump does not apply; log -
+ debugs(85, 5, HERE << "no SslBump during proxy authentication");
+ return false;
+ }
+
+ debugs(85, 5, HERE << "SslBump possible, checking ACL");
+
+ ACLFilledChecklist *acl_checklist = clientAclChecklistCreate(Config.accessList.ssl_bump, http);
+ acl_checklist->nonBlockingCheck(sslBumpAccessCheckDoneWrapper, this);
+ return true;
}
/**
}
void
-ClientRequestContext::sslBumpAccessCheckDone(bool doSslBump)
+ClientRequestContext::sslBumpAccessCheckDone(const allow_t &answer)
{
- http->sslBumpNeeded(doSslBump);
+ if (!httpStateIsValid())
+ return;
+
+ const Ssl::BumpMode bumpMode = answer == ACCESS_ALLOWED ?
+ static_cast<Ssl::BumpMode>(answer.kind) : Ssl::bumpNone;
+ http->sslBumpNeed(bumpMode); // for processRequest() to bump if needed
- http->al.ssl.bumpMode = bumpMode; // for logging
++ http->al->ssl.bumpMode = bumpMode; // for logging
+
http->doCallouts();
}
#endif
return;
}
- al.http.code = 200;
+ // We lack HttpReply which logRequest() uses to log the status code.
+ // TODO: Use HttpReply instead of the "200 Connection established" string.
++ al->http.code = 200;
+
#if USE_AUTH
// Preserve authentication info for the ssl-bumped request
if (request->auth_user_request != NULL)
assert(calloutContext);
/*Save the original request for logging purposes*/
- if (!calloutContext->http->al.request)
- calloutContext->http->al.request = HTTPMSGLOCK(request);
+ if (!calloutContext->http->al->request)
+ calloutContext->http->al->request = HTTPMSGLOCK(request);
+ if (!calloutContext->error) {
// CVE-2009-0801: verify the Host: header is consistent with other known details.
- if (!calloutContext->host_header_verify_done) {
- debugs(83, 3, HERE << "Doing calloutContext->hostHeaderVerify()");
- calloutContext->host_header_verify_done = true;
- calloutContext->hostHeaderVerify();
- return;
- }
+ if (!calloutContext->host_header_verify_done) {
+ debugs(83, 3, HERE << "Doing calloutContext->hostHeaderVerify()");
+ calloutContext->host_header_verify_done = true;
+ calloutContext->hostHeaderVerify();
+ return;
+ }
- if (!calloutContext->http_access_done) {
- debugs(83, 3, HERE << "Doing calloutContext->clientAccessCheck()");
- calloutContext->http_access_done = true;
- calloutContext->clientAccessCheck();
- return;
- }
+ if (!calloutContext->http_access_done) {
+ debugs(83, 3, HERE << "Doing calloutContext->clientAccessCheck()");
+ calloutContext->http_access_done = true;
+ calloutContext->clientAccessCheck();
+ return;
+ }
#if USE_ADAPTATION
- if (!calloutContext->adaptation_acl_check_done) {
- calloutContext->adaptation_acl_check_done = true;
- if (Adaptation::AccessCheck::Start(
+ if (!calloutContext->adaptation_acl_check_done) {
+ calloutContext->adaptation_acl_check_done = true;
+ if (Adaptation::AccessCheck::Start(
Adaptation::methodReqmod, Adaptation::pointPreCache,
request, NULL, this))
- return; // will call callback
- }
+ return; // will call callback
+ }
#endif
- if (!calloutContext->redirect_done) {
- calloutContext->redirect_done = true;
- assert(calloutContext->redirect_state == REDIRECT_NONE);
+ if (!calloutContext->redirect_done) {
+ calloutContext->redirect_done = true;
+ assert(calloutContext->redirect_state == REDIRECT_NONE);
- if (Config.Program.redirect) {
- debugs(83, 3, HERE << "Doing calloutContext->clientRedirectStart()");
- calloutContext->redirect_state = REDIRECT_PENDING;
- calloutContext->clientRedirectStart();
- return;
+ if (Config.Program.redirect) {
+ debugs(83, 3, HERE << "Doing calloutContext->clientRedirectStart()");
+ calloutContext->redirect_state = REDIRECT_PENDING;
+ calloutContext->clientRedirectStart();
+ return;
+ }
}
- }
- if (!calloutContext->adapted_http_access_done) {
- debugs(83, 3, HERE << "Doing calloutContext->clientAccessCheck2()");
- calloutContext->adapted_http_access_done = true;
- calloutContext->clientAccessCheck2();
- return;
- }
+ if (!calloutContext->adapted_http_access_done) {
+ debugs(83, 3, HERE << "Doing calloutContext->clientAccessCheck2()");
+ calloutContext->adapted_http_access_done = true;
+ calloutContext->clientAccessCheck2();
+ return;
+ }
- if (!calloutContext->interpreted_req_hdrs) {
- debugs(83, 3, HERE << "Doing clientInterpretRequestHeaders()");
- calloutContext->interpreted_req_hdrs = 1;
- clientInterpretRequestHeaders(this);
- }
+ if (!calloutContext->interpreted_req_hdrs) {
+ debugs(83, 3, HERE << "Doing clientInterpretRequestHeaders()");
+ calloutContext->interpreted_req_hdrs = 1;
+ clientInterpretRequestHeaders(this);
+ }
- if (!calloutContext->no_cache_done) {
- calloutContext->no_cache_done = true;
+ if (!calloutContext->no_cache_done) {
+ calloutContext->no_cache_done = true;
- if (Config.accessList.noCache && request->flags.cachable) {
- debugs(83, 3, HERE << "Doing calloutContext->checkNoCache()");
- calloutContext->checkNoCache();
- return;
+ if (Config.accessList.noCache && request->flags.cachable) {
+ debugs(83, 3, HERE << "Doing calloutContext->checkNoCache()");
+ calloutContext->checkNoCache();
+ return;
+ }
}
- }
+ } // if !calloutContext->error
if (!calloutContext->tosToClientDone) {
calloutContext->tosToClientDone = true;
#endif
#if USE_SSL
+ LFT_SSL_BUMP_MODE,
+ LFT_SSL_USER_CERT_SUBJECT,
+ LFT_SSL_USER_CERT_ISSUER,
#endif
LFT_PERCENT /* special string cases for escaped chars */
break;
#if USE_SSL
+ case LFT_SSL_BUMP_MODE: {
+ const Ssl::BumpMode mode = static_cast<Ssl::BumpMode>(al->ssl.bumpMode);
+ // for Ssl::bumpEnd, Ssl::bumpMode() returns NULL and we log '-'
+ out = Ssl::bumpMode(mode);
+ break;
+ }
++
+ case LFT_SSL_USER_CERT_SUBJECT:
+ if (X509 *cert = al->cache.sslClientCert.get()) {
+ if (X509_NAME *subject = X509_get_subject_name(cert)) {
+ X509_NAME_oneline(subject, tmp, sizeof(tmp));
+ out = tmp;
+ }
+ }
+ break;
+
+ case LFT_SSL_USER_CERT_ISSUER:
+ if (X509 *cert = al->cache.sslClientCert.get()) {
+ if (X509_NAME *issuer = X509_get_issuer_name(cert)) {
+ X509_NAME_oneline(issuer, tmp, sizeof(tmp));
+ out = tmp;
+ }
+ }
+ break;
#endif
case LFT_PERCENT:
#if USE_SSL
// SSL (ssl::) tokens
static TokenTableEntry TokenTableSsl[] = {
+ {"bump_mode", LFT_SSL_BUMP_MODE},
+ {">cert_subject", LFT_SSL_USER_CERT_SUBJECT},
+ {">cert_issuer", LFT_SSL_USER_CERT_ISSUER},
{NULL, LFT_NONE}
};
#endif