From: Amos Jeffries Date: Fri, 29 May 2015 13:50:26 +0000 (-0700) Subject: Convert external_acl_type directive format tokens X-Git-Tag: SQUID_4_0_1~5^2~22 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=4e56d7f6084e46d23cee970286ea57b01b04b4d4;p=thirdparty%2Fsquid.git Convert external_acl_type directive format tokens Use libformat tokenizer and string assembler mechanisms instead of custom code for external helpers. This allows any logformat code and transaction data to be used by the external ACL helper. The old helper format tokens are added to the libformat for backward compatibility. Known issue: all access lists which use an exetrnal_acl_type helper now require setting up the ACL Checklist with an AccessLogEntry referencing any special token data the old format tokens needed. --- diff --git a/src/AccessLogEntry.h b/src/AccessLogEntry.h index 24a59ac9a0..8731a0ea0b 100644 --- a/src/AccessLogEntry.h +++ b/src/AccessLogEntry.h @@ -209,9 +209,16 @@ public: { public: - Private() : method_str(NULL) {} + Private() : method_str(NULL), lastAclName(NULL), lastAclData(NULL) {} + ~Private() { + safe_free(lastAclName); + safe_free(lastAclData); + } const char *method_str; + const char *lastAclName; + const char *lastAclData; + } _private; HierarchyLogEntry hier; HttpReply *reply; diff --git a/src/ExternalACL.h b/src/ExternalACL.h index bea24e3c7b..1299c017ba 100644 --- a/src/ExternalACL.h +++ b/src/ExternalACL.h @@ -51,6 +51,7 @@ public: virtual void parse(); virtual int match(ACLChecklist *checklist); /* This really should be dynamic based on the external class defn */ + virtual bool requiresAleXXX() const {return true;} virtual bool requiresRequest() const {return true;} /* when requiresRequest is made dynamic, review this too */ diff --git a/src/Makefile.am b/src/Makefile.am index b8d1ea2acb..90bc10e2ad 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -3524,6 +3524,7 @@ tests_testURL_SOURCES = \ int.cc \ internal.h \ internal.cc \ + tests/stub_libeui.cc \ tests/stub_libsecurity.cc \ SquidList.h \ SquidList.cc \ diff --git a/src/acl/Acl.cc b/src/acl/Acl.cc index dcbc520889..b81faeabc2 100644 --- a/src/acl/Acl.cc +++ b/src/acl/Acl.cc @@ -144,7 +144,10 @@ ACL::matches(ACLChecklist *checklist) const AclMatchedName = name; int result = 0; - if (!checklist->hasRequest() && requiresRequest()) { + if (!checklist->hasAleXXX() && requiresAleXXX()) { + debugs(28, DBG_IMPORTANT, "WARNING: " << name << " ACL is used in " << + "context without an HTTP request. Assuming mismatch."); + } else if (!checklist->hasRequest() && requiresRequest()) { debugs(28, DBG_IMPORTANT, "WARNING: " << name << " ACL is used in " << "context without an HTTP request. Assuming mismatch."); } else if (!checklist->hasReply() && requiresReply()) { @@ -360,6 +363,12 @@ aclCacheMatchFlush(dlink_list * cache) } } +bool +ACL::requiresAleXXX() const +{ + return false; +} + bool ACL::requiresReply() const { diff --git a/src/acl/Acl.h b/src/acl/Acl.h index 70831062bc..3ad9abb83c 100644 --- a/src/acl/Acl.h +++ b/src/acl/Acl.h @@ -143,6 +143,8 @@ private: /// Matches the actual data in checklist against this ACL. virtual int match(ACLChecklist *checklist) = 0; // XXX: missing const + /// whether our (i.e. shallow) match() requires checklist to have a AccessLogEntry + virtual bool requiresAleXXX() const; /// whether our (i.e. shallow) match() requires checklist to have a request virtual bool requiresRequest() const; /// whether our (i.e. shallow) match() requires checklist to have a reply diff --git a/src/acl/Checklist.h b/src/acl/Checklist.h index 4d6afbaacf..fd1413f521 100644 --- a/src/acl/Checklist.h +++ b/src/acl/Checklist.h @@ -157,6 +157,7 @@ public: // for ACL::checklistMatches to use virtual bool hasRequest() const = 0; virtual bool hasReply() const = 0; + virtual bool hasAleXXX() const = 0; private: /// Calls non-blocking check callback with the answer and destroys self. diff --git a/src/acl/FilledChecklist.h b/src/acl/FilledChecklist.h index 641272513e..67a566f80a 100644 --- a/src/acl/FilledChecklist.h +++ b/src/acl/FilledChecklist.h @@ -62,6 +62,7 @@ public: // ACLChecklist API virtual bool hasRequest() const { return request != NULL; } virtual bool hasReply() const { return reply != NULL; } + virtual bool hasAleXXX() const { return al != NULL; } public: Ip::Address src_addr; @@ -88,7 +89,7 @@ public: Ssl::X509_Pointer serverCert; #endif - AccessLogEntry::Pointer al; ///< info for the future access.log entry + AccessLogEntry::Pointer al; ///< info for the future access.log, and external ACL ExternalACLEntryPointer extacl_entry; diff --git a/src/cf.data.pre b/src/cf.data.pre index 6975ca293d..ed7ca8616d 100644 --- a/src/cf.data.pre +++ b/src/cf.data.pre @@ -4059,7 +4059,7 @@ DOC_START modifiers are usually not needed, but can be specified if an explicit output format is desired. - % ["|[|'|#] [-] [[0]width] [{argument}] formatcode + % ["|[|'|#] [-] [[0]width] [{arg}] formatcode [{arg}] " output in quoted string format [ output in squid text log format as used by log_mime_hdrs @@ -4073,7 +4073,8 @@ DOC_START When minimum starts with 0, the field is zero-padded. String values exceeding maximum width are truncated. - {arg} argument such as header name etc + {arg} argument such as header name etc. This field may be + placed before or after the token, but not both at once. Format codes: diff --git a/src/external_acl.cc b/src/external_acl.cc index 73fa16c08a..c5bec244d1 100644 --- a/src/external_acl.cc +++ b/src/external_acl.cc @@ -13,12 +13,13 @@ #include "acl/FilledChecklist.h" #include "cache_cf.h" #include "client_side.h" +#include "client_side_request.h" #include "comm/Connection.h" #include "ConfigParser.h" #include "ExternalACL.h" #include "ExternalACLEntry.h" #include "fde.h" -#include "format/ByteCode.h" +#include "format/Token.h" #include "helper.h" #include "helper/Reply.h" #include "HttpHeaderTools.h" @@ -66,27 +67,6 @@ static ExternalACLEntryPointer external_acl_cache_add(external_acl * def, const * external_acl directive */ -class external_acl_format : public RefCountable -{ - MEMPROXY_CLASS(external_acl_format); - -public: - typedef RefCount Pointer; - - external_acl_format() : type(Format::LFT_NONE), header(NULL), member(NULL), separator(' '), header_id(HDR_BAD_HDR) {} - ~external_acl_format() { - xfree(header); - xfree(member); - } - - Format::ByteCode_t type; - external_acl_format::Pointer next; - char *header; - char *member; - char separator; - http_hdr_type header_id; -}; - class external_acl { /* FIXME: These are not really cbdata, but it is an easy way @@ -112,7 +92,7 @@ public: char *name; - external_acl_format::Pointer format; + Format::Format format; wordlist *cmdline; @@ -156,6 +136,7 @@ external_acl::external_acl() : negative_ttl(-1), grace(1), name(NULL), + format("external_acl_type"), cmdline(NULL), children(DEFAULT_EXTERNAL_ACL_CHILDREN), theHelper(NULL), @@ -173,7 +154,6 @@ external_acl::external_acl() : external_acl::~external_acl() { xfree(name); - format = NULL; wordlistDestroy(&cmdline); if (theHelper) { @@ -197,56 +177,6 @@ external_acl::~external_acl() } } -/** - * Parse the External ACL format %<{.*} and %>{.*} token(s) to pass a specific - * request or reply header to external helper. - * - \param header - the token being parsed (without the identifying prefix) - \param type - format enum identifier for this element, pulled from identifying prefix - \param format - structure to contain all the info about this format element. - */ -void -parse_header_token(external_acl_format::Pointer format, char *header, const Format::ByteCode_t type) -{ - /* header format */ - char *member, *end; - - /** Cut away the closing brace */ - end = strchr(header, '}'); - if (end && strlen(end) == 1) - *end = '\0'; - else - self_destruct(); - - member = strchr(header, ':'); - - if (member) { - /* Split in header and member */ - *member = '\0'; - ++member; - - if (!xisalnum(*member)) { - format->separator = *member; - ++member; - } else { - format->separator = ','; - } - - format->member = xstrdup(member); - - if (type == Format::LFT_ADAPTED_REQUEST_HEADER) - format->type = Format::LFT_ADAPTED_REQUEST_HEADER_ELEM; - else - format->type = Format::LFT_REPLY_HEADER_ELEM; - - } else { - format->type = type; - } - - format->header = xstrdup(header); - format->header_id = httpHeaderIdByNameDef(header, strlen(header)); -} - void parse_externalAclHelper(external_acl ** list) { @@ -332,115 +262,56 @@ parse_externalAclHelper(external_acl ** list) if (a->children.defaultQueueSize) a->children.queue_size = 2 * a->children.n_max; - /* Parse format */ - external_acl_format::Pointer *p = &a->format; - + /* Legacy external_acl_type format parser. + * Handles a series of %... tokens where any non-% means + * the start of another parameter field (ie the path to binary). + */ + enum Format::Quoting quote = Format::LOG_QUOTE_NONE; + Format::Token **fmt = &a->format.format; while (token) { - /* stop on first non-format token found */ - + /* stop on first non-% token found */ if (*token != '%') break; - external_acl_format::Pointer format = new external_acl_format; - - if (strncmp(token, "%{", 2) == 0) { - // deprecated. but assume the old configs all referred to request headers. - debugs(82, DBG_PARSE_NOTE(DBG_IMPORTANT), "WARNING: external_acl_type format %{...} is being replaced by %>ha{...} for : " << token); - parse_header_token(format, (token+2), Format::LFT_ADAPTED_REQUEST_HEADER); - } else if (strncmp(token, "%>{", 3) == 0) { - debugs(82, DBG_PARSE_NOTE(DBG_IMPORTANT), "WARNING: external_acl_type format %>{...} is being replaced by %>ha{...} for : " << token); - parse_header_token(format, (token+3), Format::LFT_ADAPTED_REQUEST_HEADER); - } else if (strncmp(token, "%>ha{", 5) == 0) { - parse_header_token(format, (token+5), Format::LFT_ADAPTED_REQUEST_HEADER); - } else if (strncmp(token, "%<{", 3) == 0) { - debugs(82, DBG_PARSE_NOTE(DBG_IMPORTANT), "WARNING: external_acl_type format %<{...} is being replaced by %type = Format::LFT_USER_LOGIN; - a->require_auth = true; -#endif - } -#if USE_IDENT - else if (strcmp(token, "%IDENT") == 0 || strcmp(token, "%ui") == 0) - format->type = Format::LFT_USER_IDENT; -#endif - else if (strcmp(token, "%SRC") == 0 || strcmp(token, "%>a") == 0) - format->type = Format::LFT_CLIENT_IP_ADDRESS; - else if (strcmp(token, "%SRCPORT") == 0 || strcmp(token, "%>p") == 0) - format->type = Format::LFT_CLIENT_PORT; -#if USE_SQUID_EUI - else if (strcmp(token, "%SRCEUI48") == 0) - format->type = Format::LFT_EXT_ACL_CLIENT_EUI48; - else if (strcmp(token, "%SRCEUI64") == 0) - format->type = Format::LFT_EXT_ACL_CLIENT_EUI64; -#endif - else if (strcmp(token, "%MYADDR") == 0 || strcmp(token, "%la") == 0) - format->type = Format::LFT_LOCAL_LISTENING_IP; - else if (strcmp(token, "%MYPORT") == 0 || strcmp(token, "%lp") == 0) - format->type = Format::LFT_LOCAL_LISTENING_PORT; - else if (strcmp(token, "%URI") == 0 || strcmp(token, "%>ru") == 0) - format->type = Format::LFT_CLIENT_REQ_URI; - else if (strcmp(token, "%DST") == 0 || strcmp(token, "%>rd") == 0) - format->type = Format::LFT_CLIENT_REQ_URLDOMAIN; - else if (strcmp(token, "%PROTO") == 0 || strcmp(token, "%>rs") == 0) - format->type = Format::LFT_CLIENT_REQ_URLSCHEME; - else if (strcmp(token, "%PORT") == 0) // XXX: add a logformat token - format->type = Format::LFT_CLIENT_REQ_URLPORT; - else if (strcmp(token, "%PATH") == 0 || strcmp(token, "%>rp") == 0) - format->type = Format::LFT_CLIENT_REQ_URLPATH; - else if (strcmp(token, "%METHOD") == 0 || strcmp(token, "%>rm") == 0) - format->type = Format::LFT_CLIENT_REQ_METHOD; -#if USE_OPENSSL - else if (strcmp(token, "%USER_CERT") == 0) - format->type = Format::LFT_EXT_ACL_USER_CERT_RAW; - else if (strcmp(token, "%USER_CERTCHAIN") == 0) - format->type = Format::LFT_EXT_ACL_USER_CERTCHAIN_RAW; - else if (strncmp(token, "%USER_CERT_", 11) == 0) { - format->type = Format::LFT_EXT_ACL_USER_CERT; - format->header = xstrdup(token + 11); + *fmt = new Format::Token; + + // compatibility for old tokens incompatible with Format::Token syntax +#if USE_OPENSSL // dont bother if we dont have to. + if (strncmp(token, "%USER_CERT_", 11) == 0) { + (*fmt)->type = Format::LFT_EXT_ACL_USER_CERT; + (*fmt)->data.string = xstrdup(token + 11); + (*fmt)->data.header.header = (*fmt)->data.string; } else if (strncmp(token, "%USER_CA_CERT_", 14) == 0) { - format->type = Format::LFT_EXT_ACL_USER_CA_CERT; - format->header = xstrdup(token + 14); + (*fmt)->type = Format::LFT_EXT_ACL_USER_CA_CERT; + (*fmt)->data.string = xstrdup(token + 14); + (*fmt)->data.header.header = (*fmt)->data.string; } else if (strncmp(token, "%CA_CERT_", 9) == 0) { debugs(82, DBG_PARSE_NOTE(DBG_IMPORTANT), "WARNING: external_acl_type %CA_CERT_* code is obsolete. Use %USER_CA_CERT_* instead"); - format->type = Format::LFT_EXT_ACL_USER_CA_CERT; - format->header = xstrdup(token + 9); - } else if (strcmp(token, "%ssl::>sni") == 0) - format->type = Format::LFT_SSL_CLIENT_SNI; - else if (strcmp(token, "%ssl::type = Format::LFT_SSL_SERVER_CERT_SUBJECT; - else if (strcmp(token, "%ssl::type = Format::LFT_SSL_SERVER_CERT_ISSUER; + (*fmt)->type = Format::LFT_EXT_ACL_USER_CA_CERT; + (*fmt)->data.string = xstrdup(token + 9); + (*fmt)->data.header.header = (*fmt)->data.string; + } else #endif + { + // we can use the Format::Token::parse() method since it + // only pulls off one token. Since we already checked + // for '%' prefix above this is guaranteed to be a token. + const size_t len = (*fmt)->parse(token, "e); + assert(len == strlen(token)); + } + + // process special token-specific actions (only if necessary) #if USE_AUTH - else if (strcmp(token, "%EXT_USER") == 0 || strcmp(token, "%ue") == 0) - format->type = Format::LFT_USER_EXTERNAL; + if ((*fmt)->type == Format::LFT_USER_LOGIN) + a->require_auth = true; #endif - else if (strcmp(token, "%EXT_LOG") == 0 || strcmp(token, "%ea") == 0) - format->type = Format::LFT_EXT_LOG; - else if (strcmp(token, "%TAG") == 0 || strcmp(token, "%et") == 0) - format->type = Format::LFT_TAG; - else if (strcmp(token, "%ACL") == 0) - format->type = Format::LFT_EXT_ACL_NAME; - else if (strcmp(token, "%DATA") == 0) - format->type = Format::LFT_EXT_ACL_DATA; - else if (strcmp(token, "%%") == 0) - format->type = Format::LFT_PERCENT; - else { - debugs(0, DBG_CRITICAL, "ERROR: Unknown Format token " << token); - self_destruct(); - } - *p = format; - p = &format->next; + fmt = &((*fmt)->next); token = ConfigParser::NextToken(); } /* There must be at least one format token */ - if (!a->format) + if (!a->format.format) self_destruct(); /* helper */ @@ -499,72 +370,7 @@ dump_externalAclHelper(StoreEntry * sentry, const char *name, const external_acl if (node->quote == external_acl::QUOTE_METHOD_SHELL) storeAppendPrintf(sentry, " protocol=2.5"); - for (external_acl_format::Pointer format = node->format; format!= NULL; format = format->next) { - switch (format->type) { - - case Format::LFT_ADAPTED_REQUEST_HEADER: - storeAppendPrintf(sentry, " %%>ha{%s}", format->header); - break; - - case Format::LFT_ADAPTED_REQUEST_HEADER_ELEM: - storeAppendPrintf(sentry, " %%>ha{%s:%s}", format->header, format->member); - break; - - case Format::LFT_REPLY_HEADER: - storeAppendPrintf(sentry, " %%header); - break; - - case Format::LFT_REPLY_HEADER_ELEM: - storeAppendPrintf(sentry, " %%header, format->member); - break; - -#define DUMP_EXT_ACL_TYPE_FMT(a, fmt, ...) \ - case Format::LFT_##a: \ - storeAppendPrintf(sentry, fmt, ##__VA_ARGS__); \ - break -#if USE_AUTH - DUMP_EXT_ACL_TYPE_FMT(USER_LOGIN," %%ul"); -#endif -#if USE_IDENT - - DUMP_EXT_ACL_TYPE_FMT(USER_IDENT," %%ui"); -#endif - DUMP_EXT_ACL_TYPE_FMT(CLIENT_IP_ADDRESS," %%>a"); - DUMP_EXT_ACL_TYPE_FMT(CLIENT_PORT," %%>p"); -#if USE_SQUID_EUI - DUMP_EXT_ACL_TYPE_FMT(EXT_ACL_CLIENT_EUI48," %%SRCEUI48"); - DUMP_EXT_ACL_TYPE_FMT(EXT_ACL_CLIENT_EUI64," %%SRCEUI64"); -#endif - DUMP_EXT_ACL_TYPE_FMT(LOCAL_LISTENING_IP," %%>la"); - DUMP_EXT_ACL_TYPE_FMT(LOCAL_LISTENING_PORT," %%>lp"); - DUMP_EXT_ACL_TYPE_FMT(CLIENT_REQ_URI," %%>ru"); - DUMP_EXT_ACL_TYPE_FMT(CLIENT_REQ_URLDOMAIN," %%>rd"); - DUMP_EXT_ACL_TYPE_FMT(CLIENT_REQ_URLSCHEME," %%>rs"); - DUMP_EXT_ACL_TYPE_FMT(CLIENT_REQ_URLPORT," %%>rP"); - DUMP_EXT_ACL_TYPE_FMT(CLIENT_REQ_URLPATH," %%>rp"); - DUMP_EXT_ACL_TYPE_FMT(CLIENT_REQ_METHOD," %%>rm"); -#if USE_OPENSSL - DUMP_EXT_ACL_TYPE_FMT(EXT_ACL_USER_CERT_RAW, " %%USER_CERT_RAW"); - DUMP_EXT_ACL_TYPE_FMT(EXT_ACL_USER_CERTCHAIN_RAW, " %%USER_CERTCHAIN_RAW"); - DUMP_EXT_ACL_TYPE_FMT(EXT_ACL_USER_CERT, " %%USER_CERT_%s", format->header); - DUMP_EXT_ACL_TYPE_FMT(EXT_ACL_USER_CA_CERT, " %%USER_CA_CERT_%s", format->header); - DUMP_EXT_ACL_TYPE_FMT(SSL_CLIENT_SNI, "%%ssl::>sni"); - DUMP_EXT_ACL_TYPE_FMT(SSL_SERVER_CERT_SUBJECT, "%%ssl::format.dump(sentry, NULL, false); for (word = node->cmdline; word; word = word->next) storeAppendPrintf(sentry, " %s", word->key); @@ -891,230 +697,21 @@ static char * makeExternalAclKey(ACLFilledChecklist * ch, external_acl_data * acl_data) { static MemBuf mb; - char buf[256]; - int first = 1; - wordlist *arg; - HttpRequest *request = ch->request; - HttpReply *reply = ch->reply; - mb.reset(); - bool data_used = false; - - for (external_acl_format::Pointer format = acl_data->def->format; format != NULL; format = format->next) { - const char *str = NULL; - String sb; - - switch (format->type) { -#if USE_AUTH - case Format::LFT_USER_LOGIN: - // if this ACL line was the cause of credentials fetch - // they may not already be in the checklist - if (ch->auth_user_request == NULL && ch->request) - ch->auth_user_request = ch->request->auth_user_request; - - if (ch->auth_user_request != NULL) - str = ch->auth_user_request->username(); - break; -#endif -#if USE_IDENT - case Format::LFT_USER_IDENT: - str = ch->rfc931; - - if (!str || !*str) { - // if we fail to go async, we still return NULL and the caller - // will detect the failure in ACLExternal::match(). - (void)ch->goAsync(IdentLookup::Instance()); - return NULL; - } - - break; -#endif - - case Format::LFT_CLIENT_IP_ADDRESS: - str = ch->src_addr.toStr(buf,sizeof(buf)); - break; - - case Format::LFT_CLIENT_PORT: - snprintf(buf, sizeof(buf), "%d", request->client_addr.port()); - str = buf; - break; - -#if USE_SQUID_EUI - case Format::LFT_EXT_ACL_CLIENT_EUI48: - if (request->clientConnectionManager.valid() && request->clientConnectionManager->clientConnection != NULL && - request->clientConnectionManager->clientConnection->remoteEui48.encode(buf, sizeof(buf))) - str = buf; - break; - case Format::LFT_EXT_ACL_CLIENT_EUI64: - if (request->clientConnectionManager.valid() && request->clientConnectionManager->clientConnection != NULL && - request->clientConnectionManager->clientConnection->remoteEui64.encode(buf, sizeof(buf))) - str = buf; - break; -#endif - - case Format::LFT_LOCAL_LISTENING_IP: - str = request->my_addr.toStr(buf, sizeof(buf)); - break; - - case Format::LFT_LOCAL_LISTENING_PORT: - snprintf(buf, sizeof(buf), "%d", request->my_addr.port()); - str = buf; - break; - - case Format::LFT_CLIENT_REQ_URI: - str = urlCanonical(request); - break; - - case Format::LFT_CLIENT_REQ_URLDOMAIN: - str = request->GetHost(); - break; - - case Format::LFT_CLIENT_REQ_URLSCHEME: - str = request->url.getScheme().c_str(); - break; - - case Format::LFT_CLIENT_REQ_URLPORT: - snprintf(buf, sizeof(buf), "%d", request->port); - str = buf; - break; + // check for special case tokens in the format + for (Format::Token *t = acl_data->def->format.format; t ; t = t->next) { - case Format::LFT_CLIENT_REQ_URLPATH: - str = request->urlpath.termedBuf(); - break; - - case Format::LFT_CLIENT_REQ_METHOD: { - const SBuf &s = request->method.image(); - sb.append(s.rawContent(), s.length()); + if (t->type == Format::LFT_EXT_ACL_NAME) { + // setup for %ACL + safe_free(ch->al->_private.lastAclName); + ch->al->_private.lastAclName = xstrdup(acl_data->name); } - str = sb.termedBuf(); - break; - - case Format::LFT_ADAPTED_REQUEST_HEADER: - if (format->header_id == -1) - sb = request->header.getByName(format->header); - else - sb = request->header.getStrOrList(format->header_id); - str = sb.termedBuf(); - break; - - case Format::LFT_ADAPTED_REQUEST_HEADER_ELEM: - if (format->header_id == -1) - sb = request->header.getByNameListMember(format->header, format->member, format->separator); - else - sb = request->header.getListMember(format->header_id, format->member, format->separator); - str = sb.termedBuf(); - break; - - case Format::LFT_REPLY_HEADER: - if (reply) { - if (format->header_id == -1) - sb = reply->header.getByName(format->header); - else - sb = reply->header.getStrOrList(format->header_id); - str = sb.termedBuf(); - } - break; - case Format::LFT_REPLY_HEADER_ELEM: - if (reply) { - if (format->header_id == -1) - sb = reply->header.getByNameListMember(format->header, format->member, format->separator); - else - sb = reply->header.getListMember(format->header_id, format->member, format->separator); - str = sb.termedBuf(); - } - break; - -#if USE_OPENSSL - - case Format::LFT_EXT_ACL_USER_CERT_RAW: - - if (ch->conn() != NULL && Comm::IsConnOpen(ch->conn()->clientConnection)) { - SSL *ssl = fd_table[ch->conn()->clientConnection->fd].ssl; - - if (ssl) - str = sslGetUserCertificatePEM(ssl); - } - - break; - - case Format::LFT_EXT_ACL_USER_CERTCHAIN_RAW: - - if (ch->conn() != NULL && Comm::IsConnOpen(ch->conn()->clientConnection)) { - SSL *ssl = fd_table[ch->conn()->clientConnection->fd].ssl; - - if (ssl) - str = sslGetUserCertificateChainPEM(ssl); - } - - break; - - case Format::LFT_EXT_ACL_USER_CERT: - - if (ch->conn() != NULL && Comm::IsConnOpen(ch->conn()->clientConnection)) { - SSL *ssl = fd_table[ch->conn()->clientConnection->fd].ssl; - - if (ssl) - str = sslGetUserAttribute(ssl, format->header); - } - - break; - - case Format::LFT_EXT_ACL_USER_CA_CERT: - - if (ch->conn() != NULL && Comm::IsConnOpen(ch->conn()->clientConnection)) { - SSL *ssl = fd_table[ch->conn()->clientConnection->fd].ssl; - - if (ssl) - str = sslGetCAAttribute(ssl, format->header); - } - - break; - - case Format::LFT_SSL_CLIENT_SNI: - if (ch->conn() != NULL) { - if (Ssl::ServerBump * srvBump = ch->conn()->serverBump()) { - if (!srvBump->clientSni.isEmpty()) - str = srvBump->clientSni.c_str(); - } - } - break; - - case Format::LFT_SSL_SERVER_CERT_SUBJECT: - case Format::LFT_SSL_SERVER_CERT_ISSUER: { - X509 *serverCert = NULL; - if (ch->serverCert.get()) - serverCert = ch->serverCert.get(); - else if (ch->conn() && ch->conn()->serverBump()) - serverCert = ch->conn()->serverBump()->serverCert.get(); - - if (serverCert) { - if (format->type == Format::LFT_SSL_SERVER_CERT_SUBJECT) - str = Ssl::GetX509UserAttribute(serverCert, "DN"); - else - str = Ssl::GetX509CAAttribute(serverCert, "DN"); - } - break; - } - -#endif -#if USE_AUTH - case Format::LFT_USER_EXTERNAL: - str = request->extacl_user.termedBuf(); - break; -#endif - case Format::LFT_EXT_LOG: - str = request->extacl_log.termedBuf(); - break; - case Format::LFT_TAG: - str = request->tag.termedBuf(); - break; - case Format::LFT_EXT_ACL_NAME: - str = acl_data->name; - break; - case Format::LFT_EXT_ACL_DATA: - data_used = true; - for (arg = acl_data->arguments; arg; arg = arg->next) { + if (t->type == Format::LFT_EXT_ACL_NAME) { + // setup string for %DATA + SBuf sb; + bool first = true; + for (auto arg = acl_data->arguments; arg; arg = arg->next) { if (!first) sb.append(" ", 1); @@ -1129,58 +726,22 @@ makeExternalAclKey(ACLFilledChecklist * ch, external_acl_data * acl_data) mb2.clean(); } - first = 0; + first = false; } - break; - case Format::LFT_PERCENT: - str = "%"; - break; - - default: - // TODO: replace this function with Format::assemble() - // For now die on unsupported logformat codes. - fatalf("ERROR: unknown external_acl_type format %u", (uint8_t)format->type); - break; } - if (str) - if (!*str) - str = NULL; - - if (!str) - str = "-"; - - if (!first) - mb.append(" ", 1); - - if (acl_data->def->quote == external_acl::QUOTE_METHOD_URL) { - const char *quoted = rfc1738_escape(str); - mb.append(quoted, strlen(quoted)); - } else { - strwordquote(&mb, str); - } - - sb.clean(); - - first = 0; - } - - if (!data_used) { - for (arg = acl_data->arguments; arg; arg = arg->next) { - if (!first) - mb.append(" ", 1); - - if (acl_data->def->quote == external_acl::QUOTE_METHOD_URL) { - const char *quoted = rfc1738_escape(arg->key); - mb.append(quoted, strlen(quoted)); - } else { - strwordquote(&mb, arg->key); + if (t->type == Format::LFT_USER_IDENT) { + if (!ch->rfc931 || !*ch->rfc931) { + // if we fail to go async, we still return NULL and the caller + // will detect the failure in ACLExternal::match(). + (void)ch->goAsync(IdentLookup::Instance()); + return NULL; } - - first = 0; } } + acl_data->def->format.assemble(mb, ch->al, 0); + return mb.buf; } diff --git a/src/format/Format.cc b/src/format/Format.cc index 74a6343340..00f220e816 100644 --- a/src/format/Format.cc +++ b/src/format/Format.cc @@ -88,14 +88,15 @@ Format::Format::parse(const char *def) } void -Format::Format::dump(StoreEntry * entry, const char *directiveName) +Format::Format::dump(StoreEntry * entry, const char *directiveName, bool eol) const { debugs(46, 4, HERE); // loop rather than recursing to conserve stack space. - for (Format *fmt = this; fmt; fmt = fmt->next) { + for (const Format *fmt = this; fmt; fmt = fmt->next) { debugs(46, 3, HERE << "Dumping format definition for " << fmt->name); - storeAppendPrintf(entry, "%s %s ", directiveName, fmt->name); + if (directiveName) + storeAppendPrintf(entry, "%s %s ", directiveName, fmt->name); for (Token *t = fmt->format; t; t = t->next) { if (t->type == LFT_STRING) @@ -251,7 +252,8 @@ Format::Format::dump(StoreEntry * entry, const char *directiveName) } } - entry->append("\n", 1); + if (eol) + entry->append("\n", 1); } } @@ -370,11 +372,29 @@ Format::Format::assemble(MemBuf &mb, const AccessLogEntry::Pointer &al, int logS al->request->clientConnectionManager->clientConnection->remoteEui64.encode(tmp, 1024); out = tmp; } -#else - out = "-"; #endif break; +#if USE_SQUID_EUI + case LFT_EXT_ACL_CLIENT_EUI48: + if (al->request && al->request->clientConnectionManager.valid() && + al->request->clientConnectionManager->clientConnection != NULL && + al->request->clientConnectionManager->clientConnection->remote.isIPv4()) { + al->request->clientConnectionManager->clientConnection->remoteEui48.encode(tmp, 1024); + out = tmp; + } + break; + + case LFT_EXT_ACL_CLIENT_EUI64: + if (al->request && al->request->clientConnectionManager.valid() && + al->request->clientConnectionManager->clientConnection != NULL && + !al->request->clientConnectionManager->clientConnection->remote.isIPv4()) { + al->request->clientConnectionManager->clientConnection->remoteEui64.encode(tmp, 1024); + out = tmp; + } + break; +#endif + case LFT_SERVER_IP_ADDRESS: if (al->hier.tcpServer != NULL) { out = al->hier.tcpServer->remote.toStr(tmp,sizeof(tmp)); @@ -837,7 +857,11 @@ Format::Format::assemble(MemBuf &mb, const AccessLogEntry::Pointer &al, int logS break; case LFT_USER_EXTERNAL: - out = strOrNull(al->cache.extuser); + if (al->request && al->request->extacl_user.size()) + out = al->request->extacl_user.termedBuf(); + + if (!out) + out = strOrNull(al->cache.extuser); break; /* case LFT_USER_REALM: */ @@ -902,8 +926,7 @@ Format::Format::assemble(MemBuf &mb, const AccessLogEntry::Pointer &al, int logS } else #endif if (al->request && al->request->errDetail != ERR_DETAIL_NONE) { - if (al->request->errDetail > ERR_DETAIL_START && - al->request->errDetail < ERR_DETAIL_MAX) + if (al->request->errDetail > ERR_DETAIL_START && al->request->errDetail < ERR_DETAIL_MAX) out = errorDetailName(al->request->errDetail); else { if (al->request->errDetail >= ERR_DETAIL_EXCEPTION_START) @@ -1130,8 +1153,48 @@ Format::Format::assemble(MemBuf &mb, const AccessLogEntry::Pointer &al, int logS const Ssl::BumpMode mode = static_cast(al->ssl.bumpMode); // for Ssl::bumpEnd, Ssl::bumpMode() returns NULL and we log '-' out = Ssl::bumpMode(mode); - break; } + break; + + case LFT_EXT_ACL_USER_CERT_RAW: + if (al->request) { + ConnStateData *conn = al->request->clientConnectionManager.get(); + if (conn != NULL && Comm::IsConnOpen(conn->clientConnection)) { + if (SSL *ssl = fd_table[conn->clientConnection->fd].ssl) + out = sslGetUserCertificatePEM(ssl); + } + } + break; + + case LFT_EXT_ACL_USER_CERTCHAIN_RAW: + if (al->request) { + ConnStateData *conn = al->request->clientConnectionManager.get(); + if (conn != NULL && Comm::IsConnOpen(conn->clientConnection)) { + if (SSL *ssl = fd_table[conn->clientConnection->fd].ssl) + out = sslGetUserCertificatePEM(ssl); + } + } + break; + + case LFT_EXT_ACL_USER_CERT: + if (al->request) { + ConnStateData *conn = al->request->clientConnectionManager.get(); + if (conn != NULL && Comm::IsConnOpen(conn->clientConnection)) { + if (SSL *ssl = fd_table[conn->clientConnection->fd].ssl) + out = sslGetUserAttribute(ssl, format->data.header.header); + } + } + break; + + case LFT_EXT_ACL_USER_CA_CERT: + if (al->request) { + ConnStateData *conn = al->request->clientConnectionManager.get(); + if (conn != NULL && Comm::IsConnOpen(conn->clientConnection)) { + if (SSL *ssl = fd_table[conn->clientConnection->fd].ssl) + out = sslGetCAAttribute(ssl, format->data.header.header); + } + } + break; case LFT_SSL_USER_CERT_SUBJECT: if (X509 *cert = al->cache.sslClientCert.get()) { @@ -1150,6 +1213,7 @@ Format::Format::assemble(MemBuf &mb, const AccessLogEntry::Pointer &al, int logS } } break; + case LFT_SSL_CLIENT_SNI: if (al->request && al->request->clientConnectionManager.valid()) { if (Ssl::ServerBump * srvBump = al->request->clientConnectionManager->serverBump()) { @@ -1217,18 +1281,12 @@ Format::Format::assemble(MemBuf &mb, const AccessLogEntry::Pointer &al, int logS out = "%"; break; - // XXX: external_acl_type format tokens which are not output by logformat. - // They are listed here because the switch requires - // every ByteCode_t to be explicitly enumerated. - // But do not output due to lack of access to the values. - case LFT_EXT_ACL_USER_CERT_RAW: - case LFT_EXT_ACL_USER_CERTCHAIN_RAW: - case LFT_EXT_ACL_USER_CERT: - case LFT_EXT_ACL_USER_CA_CERT: - case LFT_EXT_ACL_CLIENT_EUI48: - case LFT_EXT_ACL_CLIENT_EUI64: case LFT_EXT_ACL_NAME: + out = al->_private.lastAclName; + break; + case LFT_EXT_ACL_DATA: + out = al->_private.lastAclData; break; } diff --git a/src/format/Format.h b/src/format/Format.h index 4ae6e668db..4fd86bf76f 100644 --- a/src/format/Format.h +++ b/src/format/Format.h @@ -51,7 +51,7 @@ public: void assemble(MemBuf &mb, const AccessLogEntryPointer &al, int logSequenceNumber) const; /// dump this whole list of formats into the provided StoreEntry - void dump(StoreEntry * entry, const char *directiveName); + void dump(StoreEntry * entry, const char *directiveName, bool eol = true) const; char *name; Token *format; diff --git a/src/format/Token.cc b/src/format/Token.cc index 8e0b31b49e..85e7cb54ec 100644 --- a/src/format/Token.cc +++ b/src/format/Token.cc @@ -145,6 +145,30 @@ static TokenTableEntry TokenTableMisc[] = { TokenTableEntry("err_detail", LFT_SQUID_ERROR_DETAIL ), TokenTableEntry("note", LFT_NOTE ), TokenTableEntry("credentials", LFT_CREDENTIALS), + /* + * Legacy external_acl_type format tokens + */ + TokenTableEntry("ACL", LFT_EXT_ACL_NAME), + TokenTableEntry("DATA", LFT_EXT_ACL_DATA), + TokenTableEntry("DST", LFT_CLIENT_REQ_URLDOMAIN), + TokenTableEntry("EXT_LOG", LFT_EXT_LOG), + TokenTableEntry("EXT_USER", LFT_USER_EXTERNAL), + TokenTableEntry("IDENT", LFT_USER_IDENT), + TokenTableEntry("LOGIN", LFT_USER_LOGIN), + TokenTableEntry("METHOD", LFT_CLIENT_REQ_METHOD), + TokenTableEntry("MYADDR", LFT_LOCAL_LISTENING_IP), + TokenTableEntry("MYPORT", LFT_LOCAL_LISTENING_PORT), + TokenTableEntry("PATH", LFT_CLIENT_REQ_URLPATH), + TokenTableEntry("PORT", LFT_CLIENT_REQ_URLPORT), + TokenTableEntry("PROTO", LFT_CLIENT_REQ_URLSCHEME), + TokenTableEntry("SRCEUI48", LFT_EXT_ACL_CLIENT_EUI48), + TokenTableEntry("SRCEUI64", LFT_EXT_ACL_CLIENT_EUI64), + TokenTableEntry("SRCPORT", LFT_CLIENT_PORT), + TokenTableEntry("SRC", LFT_CLIENT_IP_ADDRESS), // keep after longer SRC* tokens + TokenTableEntry("TAG", LFT_TAG), + TokenTableEntry("URI", LFT_CLIENT_REQ_URI), + TokenTableEntry("USER_CERTCHAIN", LFT_EXT_ACL_USER_CERTCHAIN_RAW), + TokenTableEntry("USER_CERT", LFT_EXT_ACL_USER_CERT_RAW), TokenTableEntry(NULL, LFT_NONE) /* this must be last */ }; @@ -202,7 +226,6 @@ void Format::Token::Init() { // TODO standard log tokens - // TODO external ACL fmt tokens #if USE_ADAPTATION TheConfig.registerTokens(String("adapt"),::Format::TokenTableAdapt); @@ -335,6 +358,7 @@ Format::Token::parse(const char *def, Quoting *quoting) cur = endp; } + // when {arg} field is before the token (old logformat syntax) if (*cur == '{') { char *cp; ++cur; @@ -395,6 +419,21 @@ Format::Token::parse(const char *def, Quoting *quoting) fatalf("Can't parse configuration token: '%s'\n", def); } + // when {arg} field is after the token (old external_acl_type token syntax) + // but accept only if there was none before the token + if (*cur == '{' && !data.string) { + char *cp; + ++cur; + l = strcspn(cur, "}"); + cp = (char *)xmalloc(l + 1); + xstrncpy(cp, cur, l + 1); + data.string = cp; + cur += l; + + if (*cur == '}') + ++cur; + } + if (*cur == ' ') { space = true; ++cur;