No functionality changes expected.
Added allow_t API to avoid direct comparisons with ACCESS_ALLOWED and
ACCESS_DENIED. Developers using direct comparisons eventually mishandle
exceptional ACCESS_DUNNO and ACCESS_AUTH_REQUIRED cases where neither
"allow" nor "deny" rule matched. The new API cannot fully prevent such
bugs, but should either led the developer to the right choice (usually
.allowed()) or alert the reviewer about an unusual choice (i.e.,
denied()).
The vast majority of checks use allowed(), but we could not eliminate
the remaining denied() cases ("miss_access" and "cache" directives) for
backward compatibility reasons -- previously "working" deployments may
suddenly start blocking cache misses and/or stop caching:
http://lists.squid-cache.org/pipermail/squid-dev/2017-May/008576.html
if (http->getConn() != NULL)
ch.conn(http->getConn());
- if (DelayPools::delay_data[pool].theComposite().getRaw() && ch.fastCheck() == ACCESS_ALLOWED) {
+ if (DelayPools::delay_data[pool].theComposite().getRaw() && ch.fastCheck().allowed()) {
DelayId result (pool + 1);
CompositePoolNode::CompositeSelectionDetails details;
*/
ACLFilledChecklist ch(Config.accessList.miss, request, NULL);
ch.src_addr = request->client_addr;
- if (ch.fastCheck() == ACCESS_DENIED) {
+ if (ch.fastCheck().denied()) {
err_type page_id;
page_id = aclGetDenyInfoPage(&Config.denyInfoList, AclMatchedName, 1);
bool retriable = checkRetriable();
if (!retriable && Config.accessList.serverPconnForNonretriable) {
ACLFilledChecklist ch(Config.accessList.serverPconnForNonretriable, request, NULL);
- retriable = (ch.fastCheck() == ACCESS_ALLOWED);
+ retriable = ch.fastCheck().allowed();
}
// always call shared pool first because we need to close an idle
// connection there if we have to use a standby connection.
aclMapTOS(acl_tos * head, ACLChecklist * ch)
{
for (acl_tos *l = head; l; l = l->next) {
- if (!l->aclList || ch->fastCheck(l->aclList) == ACCESS_ALLOWED)
+ if (!l->aclList || ch->fastCheck(l->aclList).allowed())
return l->tos;
}
aclMapNfmark(acl_nfmark * head, ACLChecklist * ch)
{
for (acl_nfmark *l = head; l; l = l->next) {
- if (!l->aclList || ch->fastCheck(l->aclList) == ACCESS_ALLOWED)
+ if (!l->aclList || ch->fastCheck(l->aclList).allowed())
return l->nfmark;
}
if (conn->remote.isIPv4() != l->addr.isIPv4()) continue;
/* check ACLs for this outgoing address */
- if (!l->aclList || ch.fastCheck(l->aclList) == ACCESS_ALLOWED) {
+ if (!l->aclList || ch.fastCheck(l->aclList).allowed()) {
conn->local = l->addr;
return;
}
ACLFilledChecklist checklist(hm->access_list, request, NULL);
- if (checklist.fastCheck() == ACCESS_ALLOWED) {
+ if (checklist.fastCheck().allowed()) {
/* aclCheckFast returns true for allow. */
debugs(66, 7, "checklist for mangler is positive. Mangle");
retval = 1;
ACLFilledChecklist checklist(NULL, request, NULL);
for (HeaderWithAclList::const_iterator hwa = headersAdd.begin(); hwa != headersAdd.end(); ++hwa) {
- if (!hwa->aclList || checklist.fastCheck(hwa->aclList) == ACCESS_ALLOWED) {
+ if (!hwa->aclList || checklist.fastCheck(hwa->aclList).allowed()) {
const char *fieldValue = NULL;
MemBuf mb;
if (hwa->quoted) {
HTTPMSGLOCK(ch.reply);
for (AclSizeLimit *l = Config.ReplyBodySize; l; l = l -> next) {
/* if there is no ACL list or if the ACLs listed match use this size value */
- if (!l->aclList || ch.fastCheck(l->aclList) == ACCESS_ALLOWED) {
+ if (!l->aclList || ch.fastCheck(l->aclList).allowed()) {
debugs(58, 4, HERE << "bodySizeMax=" << bodySizeMax);
bodySizeMax = l->size; // may be -1
break;
for (AclSizeLimit *l = Config.rangeOffsetLimit; l; l = l -> next) {
/* if there is no ACL list or if the ACLs listed match use this limit value */
- if (!l->aclList || ch.fastCheck(l->aclList) == ACCESS_ALLOWED) {
+ if (!l->aclList || ch.fastCheck(l->aclList).allowed()) {
debugs(58, 4, HERE << "rangeOffsetLimit=" << rangeOffsetLimit);
rangeOffsetLimit = l->size; // may be -1
break;
if (Config.accessList.spoof_client_ip) {
ACLFilledChecklist *checklist = new ACLFilledChecklist(Config.accessList.spoof_client_ip, this, clientConnection->rfc931);
checklist->al = al;
- flags.spoofClientIp = (checklist->fastCheck() == ACCESS_ALLOWED);
+ flags.spoofClientIp = checklist->fastCheck().allowed();
delete checklist;
} else
flags.spoofClientIp = true;
for (auto v: values) {
assert(v->aclList);
- const int ret = ch.fastCheck(v->aclList);
+ const auto ret = ch.fastCheck(v->aclList);
debugs(93, 5, "Check for header name: " << theKey << ": " << v->value() <<
", HttpRequest: " << request << " HttpReply: " << reply << " matched: " << ret);
- if (ret == ACCESS_ALLOWED) {
+ if (ret.allowed()) {
matched = v->format(al);
return true;
}
#include "dlink.h"
#include "sbuf/forward.h"
+#include <algorithm>
#include <ostream>
class ConfigParser;
return code;
}
+ /// Whether an "allow" rule matched. If in doubt, use this popular method.
+ /// Also use this method to treat exceptional ACCESS_DUNNO and
+ /// ACCESS_AUTH_REQUIRED outcomes as if a "deny" rule matched.
+ /// See also: denied().
+ bool allowed() const { return code == ACCESS_ALLOWED; }
+
+ /// Whether a "deny" rule matched. Avoid this rarely used method.
+ /// Use this method (only) to treat exceptional ACCESS_DUNNO and
+ /// ACCESS_AUTH_REQUIRED outcomes as if an "allow" rule matched.
+ /// See also: allowed().
+ bool denied() const { return code == ACCESS_DENIED; }
+
+ /// whether there was either a default rule, a rule without any ACLs, or a
+ /// a rule with ACLs that all matched
+ bool someRuleMatched() const { return allowed() || denied(); }
+
aclMatchCode code; ///< ACCESS_* code
int kind; ///< which custom access list verb matched
};
inline const char *
AllowOrDeny(const allow_t &action)
{
- return action == ACCESS_ALLOWED ? "allow" : "deny";
+ return action.allowed() ? "allow" : "deny";
}
template <class ActionToStringConverter>
Must(!candidates.empty()); // the candidate we were checking must be there
debugs(93,5, HERE << topCandidate() << " answer=" << answer);
- if (answer == ACCESS_ALLOWED) { // the rule matched
+ if (answer.allowed()) { // the rule matched
ServiceGroupPointer g = topGroup();
if (g != NULL) { // the corresponding group found
callBack(g);
cl->reply = info.icapReply;
HTTPMSGLOCK(cl->reply);
- bool result = cl->fastCheck() == ACCESS_ALLOWED;
+ bool result = cl->fastCheck().allowed();
delete cl;
return result;
}
ch.reply = rep;
HTTPMSGLOCK(ch.reply);
const allow_t answer = ch.fastCheck(Auth::TheConfig.schemeAccess);
- if (answer == ACCESS_ALLOWED)
+ if (answer.allowed())
return Auth::TheConfig.schemeLists.at(answer.kind).authConfigs;
}
return Auth::TheConfig.schemes;
statsCheck.reply = al->reply;
HTTPMSGLOCK(statsCheck.reply);
}
- updatePerformanceCounters = (statsCheck.fastCheck() == ACCESS_ALLOWED);
+ updatePerformanceCounters = statsCheck.fastCheck().allowed();
}
if (updatePerformanceCounters) {
if (Config.ssl_client.cert_error) {
ACLFilledChecklist check(Config.ssl_client.cert_error, request, dash_str);
check.sslErrors = new Security::CertErrors(Security::CertError(SQUID_X509_V_ERR_DOMAIN_MISMATCH, srvCert));
- allowDomainMismatch = (check.fastCheck() == ACCESS_ALLOWED);
+ allowDomainMismatch = check.fastCheck().allowed();
delete check.sslErrors;
check.sslErrors = NULL;
}
checklist.my_addr = conn->clientConnection->local;
checklist.conn(conn);
allow_t answer = checklist.fastCheck();
- if (answer == ACCESS_ALLOWED && answer.kind == 1) {
+ if (answer.allowed() && answer.kind == 1) {
debugs(33, 3, "Request will be tunneled to server");
if (context) {
assert(conn->pipeline.front() == context); // XXX: still assumes HTTP/1 semantics
ch.my_addr = clientConnection->local;
ch.conn(this);
- if (ch.fastCheck() != ACCESS_ALLOWED)
+ if (!ch.fastCheck().allowed())
return proxyProtocolError("PROXY client not permitted by ACLs");
return true;
ACLFilledChecklist identChecklist(Ident::TheConfig.identLookup, NULL, NULL);
identChecklist.src_addr = clientConnection->remote;
identChecklist.my_addr = clientConnection->local;
- if (identChecklist.fastCheck() == ACCESS_ALLOWED)
+ if (identChecklist.fastCheck().allowed())
Ident::Start(clientConnection, clientIdentDone, this);
}
#endif
if (pools[pool]->access) {
ch.changeAcl(pools[pool]->access);
allow_t answer = ch.fastCheck();
- if (answer == ACCESS_ALLOWED) {
+ if (answer.allowed()) {
/* request client information from db after we did all checks
this will save hash lookup if client failed checks */
if (!connState->isOpen())
return;
- if (answer == ACCESS_ALLOWED) {
+ if (answer.allowed()) {
debugs(33, 2, "sslBump action " << Ssl::bumpMode(answer.kind) << "needed for " << connState->clientConnection);
connState->sslBumpMode = static_cast<Ssl::BumpMode>(answer.kind);
} else {
(ca->alg == Ssl::algSetValidBefore && certProperties.setValidBefore) )
continue;
- if (ca->aclList && checklist.fastCheck(ca->aclList) == ACCESS_ALLOWED) {
+ if (ca->aclList && checklist.fastCheck(ca->aclList).allowed()) {
const char *alg = Ssl::CertAdaptAlgorithmStr[ca->alg];
const char *param = ca->param;
certProperties.signAlgorithm = Ssl::algSignEnd;
for (sslproxy_cert_sign *sg = Config.ssl_client.cert_sign; sg != NULL; sg = sg->next) {
- if (sg->aclList && checklist.fastCheck(sg->aclList) == ACCESS_ALLOWED) {
+ if (sg->aclList && checklist.fastCheck(sg->aclList).allowed()) {
certProperties.signAlgorithm = (Ssl::CertSignAlgorithm)sg->alg;
break;
}
debugs(33, 5, "Answer: " << answer << " kind:" << answer.kind);
assert(connState->serverBump());
Ssl::BumpMode bumpAction;
- if (answer == ACCESS_ALLOWED) {
+ if (answer.allowed()) {
bumpAction = (Ssl::BumpMode)answer.kind;
} else
bumpAction = Ssl::bumpSplice;
std::unique_ptr<ACLFilledChecklist> chl(clientAclChecklistCreate(Config.accessList.sendHit, http));
chl->reply = const_cast<HttpReply*>(rep); // ACLChecklist API bug
HTTPMSGLOCK(chl->reply);
- return chl->fastCheck() != ACCESS_ALLOWED; // when in doubt, block
+ return !chl->fastCheck().allowed(); // when in doubt, block
}
// This does not happen, I hope, because we are called from CacheHit, which
<< ' ' << http->uri << " is " << accessAllowed << ", because it matched "
<< (AclMatchedName ? AclMatchedName : "NO ACL's"));
- if (accessAllowed != ACCESS_ALLOWED) {
+ if (!accessAllowed.allowed()) {
ErrorState *err;
err_type page_id;
page_id = aclGetDenyInfoPage(&Config.denyInfoList, AclMatchedName, 1);
ClientHttpRequest *http = calloutContext->http;
HttpRequest *request = http->request;
- /*
- * answer should be be ACCESS_ALLOWED or ACCESS_DENIED if we are
- * called as a result of ACL checks, or -1 if we are called when
- * there's nothing left to do.
- */
- if (answer == ACCESS_ALLOWED &&
- request->x_forwarded_for_iterator.size () != 0) {
+ if (answer.allowed() && request->x_forwarded_for_iterator.size() != 0) {
/*
* Remove the last comma-delimited element from the
calloutContext->acl_checklist->nonBlockingCheck(clientFollowXForwardedForCheck, data);
return;
}
- } /*if (answer == ACCESS_ALLOWED &&
- request->x_forwarded_for_iterator.size () != 0)*/
+ }
/* clean up, and pass control to clientAccessCheck */
if (Config.onoff.log_uses_indirect_client) {
request->x_forwarded_for_iterator.clean();
request->flags.done_follow_x_forwarded_for = true;
- if (answer != ACCESS_ALLOWED && answer != ACCESS_DENIED) {
+ if (!answer.someRuleMatched()) {
debugs(28, DBG_CRITICAL, "ERROR: Processing X-Forwarded-For. Stopping at IP address: " << request->indirect_client_addr );
}
proxy_auth_msg = http->request->auth_user_request->denyMessage("<null>");
#endif
- if (answer != ACCESS_ALLOWED) {
+ if (!answer.allowed()) {
// auth has a grace period where credentials can be expired but okay not to challenge.
/* Send an auth challenge or error */
ClientHttpRequest *http = context->http;
context->acl_checklist = NULL;
- if (answer == ACCESS_ALLOWED)
+ if (answer.allowed())
redirectStart(http, clientRedirectDoneWrapper, context);
else {
Helper::Reply const nilReply(Helper::Error);
ClientHttpRequest *http = context->http;
context->acl_checklist = NULL;
- if (answer == ACCESS_ALLOWED)
+ if (answer.allowed())
storeIdStart(http, clientStoreIdDoneWrapper, context);
else {
debugs(85, 3, "access denied expected ERR reply handling: " << answer);
ClientRequestContext::checkNoCacheDone(const allow_t &answer)
{
acl_checklist = NULL;
- if (answer == ACCESS_DENIED) {
+ if (answer.denied()) {
http->request->flags.noCache = true; // dont read reply from cache
http->request->flags.cachable = false; // dont store reply into cache
}
if (!httpStateIsValid())
return;
- const Ssl::BumpMode bumpMode = answer == ACCESS_ALLOWED ?
+ const Ssl::BumpMode bumpMode = answer.allowed() ?
static_cast<Ssl::BumpMode>(answer.kind) : Ssl::bumpSplice;
http->sslBumpNeed(bumpMode); // for processRequest() to bump if needed
http->al->ssl.bumpMode = bumpMode; // for logging
ACLFilledChecklist ch(acl, originalRequest().getRaw());
ch.reply = const_cast<HttpReply*>(entry->getReply()); // ACLFilledChecklist API bug
HTTPMSGLOCK(ch.reply);
- if (ch.fastCheck() != ACCESS_ALLOWED) { // when in doubt, block
+ if (!ch.fastCheck().allowed()) { // when in doubt, block
debugs(20, 3, "store_miss prohibits caching");
return true;
}
bool doEpsv = true;
if (Config.accessList.ftp_epsv) {
ACLFilledChecklist checklist(Config.accessList.ftp_epsv, fwd->request, NULL);
- doEpsv = (checklist.fastCheck() == ACCESS_ALLOWED);
+ doEpsv = checklist.fastCheck().allowed();
}
if (!doEpsv) {
debugs(9, 5, "EPSV support manually disabled. Sending PASV for FTP Channel (" << ctrl.conn->remote <<")");
if (result == ACCESS_DUNNO)
return false; // non-cacheable response
- if ((result == ACCESS_ALLOWED ? ttl : negative_ttl) <= 0)
+ if ((result.allowed() ? ttl : negative_ttl) <= 0)
return false; // not caching this type of response
return true;
/* Make sure the user is authenticated */
debugs(82, 3, HERE << acl->def->name << " check user authenticated.");
const allow_t ti = AuthenticateAcl(ch);
- if (ti != ACCESS_ALLOWED) {
+ if (!ti.allowed()) {
debugs(82, 2, HERE << acl->def->name << " user not authenticated (" << ti << ")");
return ti;
}
if (def->cache_size <= 0 || entry->result == ACCESS_DUNNO)
return 1;
- if (entry->date + (entry->result == ACCESS_ALLOWED ? def->ttl : def->negative_ttl) < squid_curtime)
+ if (entry->date + (entry->result.allowed() ? def->ttl : def->negative_ttl) < squid_curtime)
return 1;
else
return 0;
return 1;
int ttl;
- ttl = entry->result == ACCESS_ALLOWED ? def->ttl : def->negative_ttl;
+ ttl = entry->result.allowed() ? def->ttl : def->negative_ttl;
ttl = (ttl * (100 - def->grace)) / 100;
if (entry->date + ttl <= squid_curtime)
ACLFilledChecklist checklist(acl, s->request.getRaw(), nullptr);
checklist.src_addr = from;
checklist.my_addr.setNoAddr();
- return (checklist.fastCheck() == ACCESS_ALLOWED);
+ return checklist.fastCheck().allowed();
}
static void
ACLFilledChecklist ch(Config.accessList.reply, originalRequest().getRaw());
ch.reply = reply;
HTTPMSGLOCK(ch.reply);
- if (ch.fastCheck() != ACCESS_ALLOWED) { // TODO: support slow lookups?
+ if (!ch.fastCheck().allowed()) { // TODO: support slow lookups?
debugs(11, 3, HERE << "ignoring denied 1xx");
proceedAfter1xx();
return;
}
ACLFilledChecklist ch(Config.accessList.brokenPosts, originalRequest().getRaw());
- if (ch.fastCheck() != ACCESS_ALLOWED) {
+ if (!ch.fastCheck().allowed()) {
debugs(11, 5, HERE << "didn't match brokenPosts");
return false;
}
chl->reply = rep;
HTTPMSGLOCK(chl->reply);
const allow_t answer = chl->fastCheck();
- if (answer == ACCESS_ALLOWED) {
+ if (answer.allowed()) {
writeQuotaHandler = pool->createBucket();
fd_table[clientConnection->fd].writeQuotaHandler = writeQuotaHandler;
break;
ACLFilledChecklist checklist(Config.accessList.icp, icp_request, NULL);
checklist.src_addr = from;
checklist.my_addr.setNoAddr();
- return (checklist.fastCheck() == ACCESS_ALLOWED);
+ return checklist.fastCheck().allowed();
}
char const *
xstrncpy(al->hier.host, dash_str, SQUIDHOSTNAMELEN);
for (; log; log = log->next) {
- if (log->aclList && checklist && checklist->fastCheck(log->aclList) != ACCESS_ALLOWED)
+ if (log->aclList && checklist && !checklist->fastCheck(log->aclList).allowed())
continue;
// The special-case "none" type has no logfile object set
ACLFilledChecklist checklist(p->access, request, NULL);
- return (checklist.fastCheck() == ACCESS_ALLOWED);
+ return checklist.fastCheck().allowed();
}
/* Return TRUE if it is okay to send an ICP request to this CachePeer. */
bool allowed = false;
if (check) {
check->sslErrors = new Security::CertErrors(Security::CertError(i->error_no, i->cert, i->error_depth));
- if (check->fastCheck() == ACCESS_ALLOWED)
+ if (check->fastCheck().allowed())
allowed = true;
}
// else the Config.ssl_client.cert_error access list is not defined
ClientHttpRequest *http = pipeline.front()->http;
HttpRequest *request = http->request;
ACLFilledChecklist bodyContinuationCheck(Config.accessList.forceRequestBodyContinuation, request, NULL);
- if (bodyContinuationCheck.fastCheck() == ACCESS_ALLOWED) {
+ if (bodyContinuationCheck.fastCheck().allowed()) {
request->forcedBodyContinuation = true;
if (checkDataConnPost()) {
// Write control Msg
if (Config.accessList.forceRequestBodyContinuation) {
ACLFilledChecklist bodyContinuationCheck(Config.accessList.forceRequestBodyContinuation, request.getRaw(), NULL);
- if (bodyContinuationCheck.fastCheck() == ACCESS_ALLOWED) {
+ if (bodyContinuationCheck.fastCheck().allowed()) {
debugs(33, 5, "Body Continuation forced");
request->forcedBodyContinuation = true;
//sendControlMsg
u_char *Community;
u_char *buf = rq->buf;
int len = rq->len;
- allow_t allow = ACCESS_DENIED;
if (!Config.accessList.snmp) {
debugs(49, DBG_IMPORTANT, "WARNING: snmp_access not configured. agent query DENIED from : " << rq->from);
ACLFilledChecklist checklist(Config.accessList.snmp, NULL, NULL);
checklist.src_addr = rq->from;
checklist.snmp_community = (char *) Community;
- allow = checklist.fastCheck();
- if (allow == ACCESS_ALLOWED && (snmp_coexist_V2toV1(PDU))) {
+ if (checklist.fastCheck().allowed() && (snmp_coexist_V2toV1(PDU))) {
rq->community = Community;
rq->PDU = PDU;
debugs(49, 5, "snmpAgentParse: reqid=[" << PDU->reqid << "]");
void
Ssl::PeekingPeerConnector::checkForPeekAndSpliceDone(allow_t answer)
{
- const Ssl::BumpMode finalAction = (answer.code == ACCESS_ALLOWED) ?
+ const Ssl::BumpMode finalAction = answer.allowed() ?
static_cast<Ssl::BumpMode>(answer.kind):
checkForPeekAndSpliceGuess();
checkForPeekAndSpliceMatched(finalAction);
assert(!filledCheck->sslErrors);
filledCheck->sslErrors = new Security::CertErrors(Security::CertError(error_no, broken_cert));
filledCheck->serverCert = peer_cert;
- if (check->fastCheck() == ACCESS_ALLOWED) {
+ if (check->fastCheck().allowed()) {
debugs(83, 3, "bypassing SSL error " << error_no << " in " << buffer);
ok = 1;
} else {
ACLFilledChecklist ch(Config.accessList.miss, request, NULL);
ch.src_addr = request->client_addr;
ch.my_addr = request->my_addr;
- if (ch.fastCheck() == ACCESS_DENIED) {
+ if (ch.fastCheck().denied()) {
debugs(26, 4, HERE << "MISS access forbidden.");
err = new ErrorState(ERR_FORWARDING_DENIED, Http::scForbidden, request);
http->al->http.code = Http::scForbidden;