From 2203b5137d154b08e890fea8cf568b927bb042fc Mon Sep 17 00:00:00 2001 From: Amos Jeffries Date: Tue, 16 Sep 2025 16:43:14 +1200 Subject: [PATCH 1/4] Add flag MaskSensitiveInfo to allow for explicit caller controlled redaction of Authentication credentials in generated outputs. --- src/HttpHeader.cc | 7 +++---- src/HttpHeader.h | 6 +++++- src/HttpReply.cc | 2 +- src/HttpRequest.cc | 8 ++++---- src/HttpRequest.h | 4 +++- src/base/Makefile.am | 1 + src/base/MaskSensitiveInfo.h | 17 +++++++++++++++++ src/client_side.cc | 8 ++++---- src/client_side_reply.cc | 2 +- src/clients/HttpTunneler.cc | 2 +- src/errorpage.cc | 4 ++-- src/htcp.cc | 10 +++++----- src/http.cc | 2 +- src/http/Message.cc | 2 +- src/servers/FtpServer.cc | 2 +- src/tests/stub_HttpHeader.cc | 2 +- src/tests/stub_HttpRequest.cc | 2 +- 17 files changed, 52 insertions(+), 29 deletions(-) create mode 100644 src/base/MaskSensitiveInfo.h diff --git a/src/HttpHeader.cc b/src/HttpHeader.cc index 31d06fcb602..50ea9d62707 100644 --- a/src/HttpHeader.cc +++ b/src/HttpHeader.cc @@ -669,16 +669,15 @@ HttpHeader::parse(const char *header_start, size_t hdrLen, Http::ContentLengthIn /* packs all the entries using supplied packer */ void -HttpHeader::packInto(Packable * p, bool mask_sensitive_info) const +HttpHeader::packInto(Packable * p, MaskSensitiveInfo masking) const { HttpHeaderPos pos = HttpHeaderInitPos; const HttpHeaderEntry *e; assert(p); - debugs(55, 7, this << " into " << p << - (mask_sensitive_info ? " while masking" : "")); + debugs(55, 7, this << " into " << p << (masking == MaskSensitiveInfo::on ? " while masking" : "")); /* pack all entries one by one */ while ((e = getEntry(&pos))) { - if (!mask_sensitive_info) { + if (masking == MaskSensitiveInfo::off) { e->packInto(p); continue; } diff --git a/src/HttpHeader.h b/src/HttpHeader.h index c0e2deadf77..89e929b8504 100644 --- a/src/HttpHeader.h +++ b/src/HttpHeader.h @@ -11,11 +11,13 @@ #include "anyp/ProtocolVersion.h" #include "base/LookupTable.h" +#include "base/MaskSensitiveInfo.h" #include "http/RegisteredHeaders.h" /* because we pass a spec by value */ #include "HttpHeaderMask.h" #include "mem/PoolingAllocator.h" #include "sbuf/forward.h" +#include "security/forward.h" #include "SquidString.h" #include @@ -96,7 +98,9 @@ class HttpHeader /// \returns 0 when needs more data /// \returns -1 on error int parse(const char *buf, size_t buf_len, bool atEnd, size_t &hdr_sz, Http::ContentLengthInterpreter &interpreter); - void packInto(Packable * p, bool mask_sensitive_info=false) const; + /// Serialize HTTP Fields using HTTP/1.1 syntax in RFC 9112 section 5. + /// Optionally redact credentials in HTTP Authentication headers. + void packInto(Packable *, MaskSensitiveInfo) const; HttpHeaderEntry *getEntry(HttpHeaderPos * pos) const; HttpHeaderEntry *findEntry(Http::HdrType id) const; /// deletes all fields with a given name, if any. diff --git a/src/HttpReply.cc b/src/HttpReply.cc index 796be42f1ef..8ce934a514d 100644 --- a/src/HttpReply.cc +++ b/src/HttpReply.cc @@ -87,7 +87,7 @@ void HttpReply::packHeadersUsingFastPacker(Packable &p) const { sline.packInto(&p); - header.packInto(&p); + header.packInto(&p, MaskSensitiveInfo::off); p.append("\r\n", 2); } diff --git a/src/HttpRequest.cc b/src/HttpRequest.cc index c8810f1d58a..834af6011d3 100644 --- a/src/HttpRequest.cc +++ b/src/HttpRequest.cc @@ -334,19 +334,19 @@ HttpRequest::swapOut(StoreEntry * e) { assert(e); e->buffer(); - pack(e); + pack(e, MaskSensitiveInfo::off); e->flush(); } /* packs request-line and headers, appends terminator */ void -HttpRequest::pack(Packable * const p, const bool maskSensitiveInfo) const +HttpRequest::pack(Packable * const p, MaskSensitiveInfo mask) const { assert(p); /* pack request-line */ packFirstLineInto(p, false /* origin-form */); /* headers */ - header.packInto(p, maskSensitiveInfo); + header.packInto(p, mask); /* indicate the end of the header section */ p->append("\r\n", 2); } @@ -358,7 +358,7 @@ void httpRequestPack(void *obj, Packable *p) { HttpRequest *request = static_cast(obj); - request->pack(p); + request->pack(p, MaskSensitiveInfo::off); } /* returns the length of request line + headers + crlf */ diff --git a/src/HttpRequest.h b/src/HttpRequest.h index 3088efbd1a7..4ab7d4e056f 100644 --- a/src/HttpRequest.h +++ b/src/HttpRequest.h @@ -204,7 +204,9 @@ class HttpRequest: public Http::Message void swapOut(StoreEntry * e); - void pack(Packable * p, bool maskSensitiveInfo = false) const; + /// Serialize HTTP Request using HTTP/1.1 origin-form syntax in RFC 9112 section 3. + /// \copydoc HttpHeader::packInto() + void pack(Packable * const, MaskSensitiveInfo) const; static void httpRequestPack(void *obj, Packable *p); diff --git a/src/base/Makefile.am b/src/base/Makefile.am index 39710ad8d2a..2fdc98887ac 100644 --- a/src/base/Makefile.am +++ b/src/base/Makefile.am @@ -48,6 +48,7 @@ libbase_la_SOURCES = \ JobWait.h \ Lock.h \ LookupTable.h \ + MaskSensitiveInfo.h \ OnOff.h \ Packable.h \ PackableStream.h \ diff --git a/src/base/MaskSensitiveInfo.h b/src/base/MaskSensitiveInfo.h new file mode 100644 index 00000000000..31ca8366a65 --- /dev/null +++ b/src/base/MaskSensitiveInfo.h @@ -0,0 +1,17 @@ +/* + * Copyright (C) 1996-2025 The Squid Software Foundation and contributors + * + * Squid software is distributed under GPLv2+ license and includes + * contributions from numerous individuals and organizations. + * Please see the COPYING and CONTRIBUTORS files for details. + */ + +#ifndef SQUID_SRC_BASE_MASKSENSITIVEINFO_H +#define SQUID_SRC_BASE_MASKSENSITIVEINFO_H + +#include "base/OnOff.h" + +/// Flags for explicit decisions on handling of sensitive information. +using MaskSensitiveInfo = OnOff; + +#endif /* SQUID_SRC_BASE_MASKSENSITIVEINFO_H */ diff --git a/src/client_side.cc b/src/client_side.cc index 443153e705a..b2cb50df195 100644 --- a/src/client_side.cc +++ b/src/client_side.cc @@ -327,14 +327,14 @@ prepareLogWithRequestDetails(HttpRequest *request, const AccessLogEntryPointer & if (Config.onoff.log_mime_hdrs) { MemBuf mb; mb.init(); - request->header.packInto(&mb); + request->header.packInto(&mb, MaskSensitiveInfo::off); //This is the request after adaptation or redirection aLogEntry->headers.adapted_request = xstrdup(mb.buf); // the virgin request is saved to aLogEntry->request if (aLogEntry->request) { mb.reset(); - aLogEntry->request->header.packInto(&mb); + aLogEntry->request->header.packInto(&mb, MaskSensitiveInfo::off); aLogEntry->headers.request = xstrdup(mb.buf); } @@ -342,7 +342,7 @@ prepareLogWithRequestDetails(HttpRequest *request, const AccessLogEntryPointer & const Adaptation::History::Pointer ah = request->adaptLogHistory(); if (ah != nullptr) { mb.reset(); - ah->lastMeta.packInto(&mb); + ah->lastMeta.packInto(&mb, MaskSensitiveInfo::off); aLogEntry->adapt.last_meta = xstrdup(mb.buf); } #endif @@ -724,7 +724,7 @@ clientPackRangeHdr(const HttpReplyPointer &rep, const HttpHdrRangeSpec * spec, S httpHeaderAddContRange(&hdr, *spec, rep->content_length); - hdr.packInto(mb); + hdr.packInto(mb, MaskSensitiveInfo::off); hdr.clean(); /* append (we packed a header, not a reply) */ diff --git a/src/client_side_reply.cc b/src/client_side_reply.cc index 3fb52ac9ce3..3ce0c95ad6a 100644 --- a/src/client_side_reply.cc +++ b/src/client_side_reply.cc @@ -1004,7 +1004,7 @@ clientReplyContext::traceReply() http->storeEntry()->buffer(); MemBuf content; content.init(); - http->request->pack(&content, true /* hide authorization data */); + http->request->pack(&content, MaskSensitiveInfo::on); const HttpReplyPointer rep(new HttpReply); rep->setHeaders(Http::scOkay, nullptr, "message/http", content.contentSize(), 0, squid_curtime); rep->body.set(SBuf(content.buf, content.size)); diff --git a/src/clients/HttpTunneler.cc b/src/clients/HttpTunneler.cc index 3af7a19d5ac..e570c4c8269 100644 --- a/src/clients/HttpTunneler.cc +++ b/src/clients/HttpTunneler.cc @@ -152,7 +152,7 @@ Http::Tunneler::writeRequest() &hdr_out, connection->getPeer(), flags); - hdr_out.packInto(&mb); + hdr_out.packInto(&mb, MaskSensitiveInfo::off); hdr_out.clean(); mb.append("\r\n", 2); diff --git a/src/errorpage.cc b/src/errorpage.cc index 87698fc52a2..d0c34886991 100644 --- a/src/errorpage.cc +++ b/src/errorpage.cc @@ -886,7 +886,7 @@ ErrorState::Dump(MemBuf * mb) body << "HTTP Request:\r\n"; MemBuf r; r.init(); - request->pack(&r, true /* hide authorization data */); + request->pack(&r, MaskSensitiveInfo::on); body << r.content(); } @@ -1149,7 +1149,7 @@ ErrorState::compileLegacyCode(Build &build) break; } else if (request) - request->pack(&mb, true /* hide authorization data */); + request->pack(&mb, MaskSensitiveInfo::on); else p = "[no request]"; break; diff --git a/src/htcp.cc b/src/htcp.cc index da1e616b35e..2ec3d341404 100644 --- a/src/htcp.cc +++ b/src/htcp.cc @@ -865,7 +865,7 @@ htcpTstReply(htcpDataHeader * dhdr, StoreEntry * e, htcpSpecifier * spec, Ip::Ad hdr.putInt(Http::HdrType::AGE, 0); MemBuf mb; mb.init(); - hdr.packInto(&mb); + hdr.packInto(&mb, MaskSensitiveInfo::off); stuff.D.resp_hdrs = xstrdup(mb.buf); stuff.D.respHdrsSz = mb.contentSize(); debugs(31, 3, "htcpTstReply: resp_hdrs = {" << stuff.D.resp_hdrs << "}"); @@ -878,7 +878,7 @@ htcpTstReply(htcpDataHeader * dhdr, StoreEntry * e, htcpSpecifier * spec, Ip::Ad if (e && e->lastModified() > -1) hdr.putTime(Http::HdrType::LAST_MODIFIED, e->lastModified()); - hdr.packInto(&mb); + hdr.packInto(&mb, MaskSensitiveInfo::off); stuff.D.entity_hdrs = xstrdup(mb.buf); stuff.D.entityHdrsSz = mb.contentSize(); @@ -904,7 +904,7 @@ htcpTstReply(htcpDataHeader * dhdr, StoreEntry * e, htcpSpecifier * spec, Ip::Ad } #endif /* USE_ICMP */ - hdr.packInto(&mb); + hdr.packInto(&mb, MaskSensitiveInfo::off); stuff.D.cache_hdrs = xstrdup(mb.buf); stuff.D.cacheHdrsSz = mb.contentSize(); debugs(31, 3, "htcpTstReply: cache_hdrs = {" << stuff.D.cache_hdrs << "}"); @@ -1579,7 +1579,7 @@ htcpQuery(StoreEntry * e, HttpRequest * req, CachePeer * p) HttpStateData::httpBuildRequestHeader(req, e, nullptr, &hdr, p, flags); MemBuf mb; mb.init(); - hdr.packInto(&mb); + hdr.packInto(&mb, MaskSensitiveInfo::off); hdr.clean(); stuff.S.req_hdrs = mb.buf; pktlen = htcpBuildPacket(pkt, sizeof(pkt), &stuff); @@ -1633,7 +1633,7 @@ htcpClear(StoreEntry * e, HttpRequest * req, const HttpRequestMethod &, CachePee if (reason != HTCP_CLR_INVALIDATION) { HttpStateData::httpBuildRequestHeader(req, e, nullptr, &hdr, p, flags); mb.init(); - hdr.packInto(&mb); + hdr.packInto(&mb, MaskSensitiveInfo::off); hdr.clean(); stuff.S.req_hdrs = mb.buf; } else { diff --git a/src/http.cc b/src/http.cc index 42ee7ec24a0..d4f1a36b277 100644 --- a/src/http.cc +++ b/src/http.cc @@ -2399,7 +2399,7 @@ HttpStateData::buildRequestPrefix(MemBuf * mb) upgradeHeaderOut = new String(hdr.getList(Http::HdrType::UPGRADE)); } - hdr.packInto(mb); + hdr.packInto(mb, MaskSensitiveInfo::off); hdr.clean(); } /* append header terminator */ diff --git a/src/http/Message.cc b/src/http/Message.cc index bd40c894978..74f35b339e2 100644 --- a/src/http/Message.cc +++ b/src/http/Message.cc @@ -253,7 +253,7 @@ void Http::Message::packInto(Packable *p, bool full_uri) const { packFirstLineInto(p, full_uri); - header.packInto(p); + header.packInto(p, MaskSensitiveInfo::off); p->append("\r\n", 2); } diff --git a/src/servers/FtpServer.cc b/src/servers/FtpServer.cc index 6ee7db90b69..a8e7847b62a 100644 --- a/src/servers/FtpServer.cc +++ b/src/servers/FtpServer.cc @@ -1310,7 +1310,7 @@ Ftp::Server::handleRequest(HttpRequest *request) if (Debug::Enabled(9, 2)) { MemBuf mb; mb.init(); - request->pack(&mb); + request->pack(&mb, MaskSensitiveInfo::off); debugs(9, 2, "FTP Client " << clientConnection); debugs(9, 2, "FTP Client REQUEST:\n---------\n" << mb.buf << diff --git a/src/tests/stub_HttpHeader.cc b/src/tests/stub_HttpHeader.cc index 2771105ecab..7c9c82435ac 100644 --- a/src/tests/stub_HttpHeader.cc +++ b/src/tests/stub_HttpHeader.cc @@ -31,7 +31,7 @@ void HttpHeader::update(const HttpHeader *) STUB void HttpHeader::compact() STUB int HttpHeader::parse(const char *, size_t, Http::ContentLengthInterpreter &) STUB_RETVAL(-1) int HttpHeader::parse(const char *, size_t, bool, size_t &, Http::ContentLengthInterpreter &) STUB_RETVAL(-1) -void HttpHeader::packInto(Packable *, bool) const STUB +void HttpHeader::packInto(Packable *, MaskSensitiveInfo) const STUB HttpHeaderEntry *HttpHeader::getEntry(HttpHeaderPos *) const STUB_RETVAL(nullptr) HttpHeaderEntry *HttpHeader::findEntry(Http::HdrType) const STUB_RETVAL(nullptr) int HttpHeader::delByName(const SBuf &) STUB_RETVAL(0) diff --git a/src/tests/stub_HttpRequest.cc b/src/tests/stub_HttpRequest.cc index 48a0f1ce03e..75f74ae91f9 100644 --- a/src/tests/stub_HttpRequest.cc +++ b/src/tests/stub_HttpRequest.cc @@ -45,7 +45,7 @@ bool HttpRequest::expectingBody(const HttpRequestMethod &, int64_t &) const STUB bool HttpRequest::bodyNibbled() const STUB_RETVAL(false) int HttpRequest::prefixLen() const STUB_RETVAL(0) void HttpRequest::swapOut(StoreEntry *) STUB -void HttpRequest::pack(Packable *, bool) const STUB +void HttpRequest::pack(Packable *, MaskSensitiveInfo) const STUB void HttpRequest::httpRequestPack(void *, Packable *) STUB HttpRequest * HttpRequest::FromUrl(const SBuf &, const MasterXaction::Pointer &, const HttpRequestMethod &) STUB_RETVAL(nullptr) HttpRequest * HttpRequest::FromUrlXXX(const char *, const MasterXaction::Pointer &, const HttpRequestMethod &) STUB_RETVAL(nullptr) From c4714516dba83c3cea52dfb4f0d87d7862b15ece Mon Sep 17 00:00:00 2001 From: Amos Jeffries Date: Tue, 14 Oct 2025 15:34:29 +1300 Subject: [PATCH 2/4] drop unnecessary include --- src/HttpHeader.h | 1 - 1 file changed, 1 deletion(-) diff --git a/src/HttpHeader.h b/src/HttpHeader.h index 89e929b8504..e62f01025eb 100644 --- a/src/HttpHeader.h +++ b/src/HttpHeader.h @@ -17,7 +17,6 @@ #include "HttpHeaderMask.h" #include "mem/PoolingAllocator.h" #include "sbuf/forward.h" -#include "security/forward.h" #include "SquidString.h" #include From 57f41fc89e460f8411db3a53ac7ad69962db3f9a Mon Sep 17 00:00:00 2001 From: Amos Jeffries Date: Mon, 27 Oct 2025 23:42:53 +1300 Subject: [PATCH 3/4] Expand flags into ICAP --- src/adaptation/icap/ModXact.cc | 8 ++++---- src/adaptation/icap/ModXact.h | 2 +- src/http/Message.cc | 4 ++-- src/http/Message.h | 2 +- src/tests/stub_libhttp.cc | 2 +- 5 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/adaptation/icap/ModXact.cc b/src/adaptation/icap/ModXact.cc index 9f8783fe52b..9e4793b66e9 100644 --- a/src/adaptation/icap/ModXact.cc +++ b/src/adaptation/icap/ModXact.cc @@ -961,7 +961,7 @@ void Adaptation::Icap::ModXact::prepEchoing() // write the virgin message into a memory buffer httpBuf.init(); - packHead(httpBuf, oldHead); + packHead(httpBuf, oldHead, MaskSensitiveInfo::off); // allocate the adapted message and copy metainfo Must(!adapted.header); @@ -1615,15 +1615,15 @@ Adaptation::Icap::ModXact::encapsulateHead(MemBuf &icapBuf, const char *section, } // pack polished HTTP header - packHead(httpBuf, headClone.getRaw()); + packHead(httpBuf, headClone.getRaw(), MaskSensitiveInfo::off); // headClone unlocks and, hence, deletes the message we packed } void -Adaptation::Icap::ModXact::packHead(MemBuf &httpBuf, const Http::Message *head) +Adaptation::Icap::ModXact::packHead(MemBuf &httpBuf, const Http::Message *head, const MaskSensitiveInfo masking) { - head->packInto(&httpBuf, true); + head->packInto(&httpBuf, true, masking); } // decides whether to offer a preview and calculates its size diff --git a/src/adaptation/icap/ModXact.h b/src/adaptation/icap/ModXact.h index ba4d73b8e0e..d7898d58a06 100644 --- a/src/adaptation/icap/ModXact.h +++ b/src/adaptation/icap/ModXact.h @@ -281,7 +281,7 @@ class ModXact: public Xaction, public BodyProducer, public BodyConsumer template bool parsePart(Part *part, const char *description); - void packHead(MemBuf &httpBuf, const Http::Message *head); + void packHead(MemBuf &httpBuf, const Http::Message *head, const MaskSensitiveInfo); void encapsulateHead(MemBuf &icapBuf, const char *section, MemBuf &httpBuf, const Http::Message *head); bool gotEncapsulated(const char *section) const; /// whether ICAP response header indicates HTTP header presence diff --git a/src/http/Message.cc b/src/http/Message.cc index 74f35b339e2..e5613c949e1 100644 --- a/src/http/Message.cc +++ b/src/http/Message.cc @@ -250,10 +250,10 @@ Http::Message::persistent() const } void -Http::Message::packInto(Packable *p, bool full_uri) const +Http::Message::packInto(Packable *p, bool full_uri, const MaskSensitiveInfo mask) const { packFirstLineInto(p, full_uri); - header.packInto(p, MaskSensitiveInfo::off); + header.packInto(p, mask); p->append("\r\n", 2); } diff --git a/src/http/Message.h b/src/http/Message.h index 1f1b6ac91e8..ae0ef76fd23 100644 --- a/src/http/Message.h +++ b/src/http/Message.h @@ -50,7 +50,7 @@ class Message : public RefCountable virtual void reset() = 0; // will have body when http*Clean()s are gone - void packInto(Packable *, bool full_uri) const; + void packInto(Packable *, bool full_uri, const MaskSensitiveInfo) const; ///< produce a message copy, except for a few connection-specific settings virtual Http::Message *clone() const = 0; // TODO rename: not a true copy? diff --git a/src/tests/stub_libhttp.cc b/src/tests/stub_libhttp.cc index 09f5fa9bc6d..62b16676e35 100644 --- a/src/tests/stub_libhttp.cc +++ b/src/tests/stub_libhttp.cc @@ -38,7 +38,7 @@ namespace Http { Message::Message(const http_hdr_owner_type owner): header(owner) {STUB} Message::~Message() {STUB} -void Message::packInto(Packable *, bool) const STUB +void Message::packInto(Packable *, bool, const MaskSensitiveInfo) const STUB void Message::setContentLength(int64_t) STUB bool Message::persistent() const STUB_RETVAL(false) void Message::putCc(const HttpHdrCc &) STUB From 23b091139adde4c61f8dd247c1afda68eda95028 Mon Sep 17 00:00:00 2001 From: Amos Jeffries Date: Wed, 29 Oct 2025 02:33:44 +1300 Subject: [PATCH 4/4] ecap caller --- src/adaptation/ecap/MessageRep.cc | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/adaptation/ecap/MessageRep.cc b/src/adaptation/ecap/MessageRep.cc index bbb0501ad6b..acd84f010af 100644 --- a/src/adaptation/ecap/MessageRep.cc +++ b/src/adaptation/ecap/MessageRep.cc @@ -94,7 +94,9 @@ Adaptation::Ecap::HeaderRep::image() const { MemBuf mb; mb.init(); - theMessage.packInto(&mb, true); + // XXX: libecap does not provide for header masking + // we are forced to trust the library does not leak + theMessage.packInto(&mb, true, MaskSensitiveInfo::off); return Area::FromTempBuffer(mb.content(), mb.contentSize()); }