Config.cc
Go to the documentation of this file.
1 /*
2  * Copyright (C) 1996-2025 The Squid Software Foundation and contributors
3  *
4  * Squid software is distributed under GPLv2+ license and includes
5  * contributions from numerous individuals and organizations.
6  * Please see the COPYING and CONTRIBUTORS files for details.
7  */
8 
9 /* DEBUG: section 29 Authenticator */
10 
11 /* The functions in this file handle authentication.
12  * They DO NOT perform access control or auditing.
13  * See acl.c for access control and client_side.c for auditing */
14 
15 #include "squid.h"
16 #include "auth/basic/Config.h"
17 #include "auth/basic/Scheme.h"
18 #include "auth/basic/User.h"
19 #include "auth/basic/UserRequest.h"
20 #include "auth/CredentialsCache.h"
21 #include "auth/Gadgets.h"
22 #include "auth/State.h"
23 #include "auth/toUtf.h"
24 #include "base64.h"
25 #include "cache_cf.h"
26 #include "helper.h"
27 #include "HttpHeaderTools.h"
28 #include "HttpReply.h"
29 #include "mgr/Registration.h"
30 #include "rfc1738.h"
31 #include "sbuf/SBuf.h"
32 #include "Store.h"
33 #include "util.h"
34 #include "wordlist.h"
35 
36 /* Basic Scheme */
38 
40 
41 static int authbasic_initialised = 0;
42 
43 /*
44  *
45  * Public Functions
46  *
47  */
48 
49 /* internal functions */
50 
51 bool
52 Auth::Basic::Config::active() const
53 {
54  return authbasic_initialised == 1;
55 }
56 
57 bool
58 Auth::Basic::Config::configured() const
59 {
60  return (SchemeConfig::configured() && !realm.isEmpty());
61 }
62 
63 const char *
64 Auth::Basic::Config::type() const
65 {
66  return Auth::Basic::Scheme::GetInstance()->type();
67 }
68 
69 void
70 Auth::Basic::Config::fixHeader(Auth::UserRequest::Pointer, HttpReply *rep, Http::HdrType hdrType, HttpRequest *)
71 {
72  if (authenticateProgram) {
73  if (utf8) {
74  debugs(29, 9, "Sending type:" << hdrType << " header: 'Basic realm=\"" << realm << "\", charset=\"UTF-8\"'");
75  httpHeaderPutStrf(&rep->header, hdrType, "Basic realm=\"" SQUIDSBUFPH "\", charset=\"UTF-8\"", SQUIDSBUFPRINT(realm));
76  } else {
77  debugs(29, 9, "Sending type:" << hdrType << " header: 'Basic realm=\"" << realm << "\"'");
78  httpHeaderPutStrf(&rep->header, hdrType, "Basic realm=\"" SQUIDSBUFPH "\"", SQUIDSBUFPRINT(realm));
79  }
80  }
81 }
82 
83 void
84 Auth::Basic::Config::rotateHelpers()
85 {
86  /* schedule closure of existing helpers */
87  if (basicauthenticators) {
89  }
90 
91  /* NP: dynamic helper restart will ensure they start up again as needed. */
92 }
93 
95 void
96 Auth::Basic::Config::done()
97 {
99 
101 
102  if (basicauthenticators) {
104  }
105 
106  basicauthenticators = nullptr;
107 
108  if (authenticateProgram)
109  wordlistDestroy(&authenticateProgram);
110 }
111 
112 bool
113 Auth::Basic::Config::dump(StoreEntry * entry, const char *name, Auth::SchemeConfig * scheme) const
114 {
115  if (!Auth::SchemeConfig::dump(entry, name, scheme))
116  return false; // not configured
117 
118  storeAppendPrintf(entry, "%s basic credentialsttl %d seconds\n", name, (int) credentialsTTL);
119  storeAppendPrintf(entry, "%s basic casesensitive %s\n", name, casesensitive ? "on" : "off");
120  return true;
121 }
122 
124  credentialsTTL( 2*60*60 ),
125  casesensitive(0)
126 {
127  static const SBuf defaultRealm("Squid proxy-caching web server");
128  realm = defaultRealm;
129 }
130 
131 void
132 Auth::Basic::Config::parse(Auth::SchemeConfig * scheme, size_t n_configured, char *param_str)
133 {
134  if (strcmp(param_str, "credentialsttl") == 0) {
135  parse_time_t(&credentialsTTL);
136  } else if (strcmp(param_str, "casesensitive") == 0) {
137  parse_onoff(&casesensitive);
138  } else
139  Auth::SchemeConfig::parse(scheme, n_configured, param_str);
140 }
141 
142 static void
144 {
146  basicauthenticators->packStatsInto(sentry, "Basic Authenticator Statistics");
147 }
148 
149 char *
150 Auth::Basic::Config::decodeCleartext(const char *httpAuthHeader, const HttpRequest *request)
151 {
152  const char *proxy_auth = httpAuthHeader;
153 
154  /* trim BASIC from string */
155  while (xisgraph(*proxy_auth))
156  ++proxy_auth;
157 
158  /* Trim leading whitespace before decoding */
159  while (xisspace(*proxy_auth))
160  ++proxy_auth;
161 
162  /* Trim trailing \n before decoding */
163  // XXX: really? is the \n actually still there? does the header parse not drop it?
164  char *eek = xstrdup(proxy_auth);
165  strtok(eek, "\n");
166 
167  const size_t srcLen = strlen(eek);
168  char *cleartext = static_cast<char*>(xmalloc(BASE64_DECODE_LENGTH(srcLen)+1));
169 
170  struct base64_decode_ctx ctx;
171  base64_decode_init(&ctx);
172 
173  size_t dstLen = 0;
174  if (base64_decode_update(&ctx, &dstLen, reinterpret_cast<uint8_t*>(cleartext), srcLen, eek) && base64_decode_final(&ctx)) {
175  cleartext[dstLen] = '\0';
176 
177  if (utf8 && !isValidUtf8String(cleartext, cleartext + dstLen)) {
178  auto str = isCP1251EncodingAllowed(request) ?
179  Cp1251ToUtf8(cleartext) : Latin1ToUtf8(cleartext);
180  safe_free(cleartext);
181  cleartext = xstrdup(str.c_str());
182  }
183 
184  /*
185  * Don't allow NL or CR in the credentials.
186  * Oezguer Kesim <oec@codeblau.de>
187  */
188  debugs(29, 9, "'" << cleartext << "'");
189 
190  if (strcspn(cleartext, "\r\n") != strlen(cleartext)) {
191  debugs(29, DBG_IMPORTANT, "WARNING: Bad characters in authorization header '" << httpAuthHeader << "'");
192  safe_free(cleartext);
193  }
194  } else {
195  debugs(29, 2, "WARNING: Invalid Base64 character in authorization header '" << httpAuthHeader << "'");
196  safe_free(cleartext);
197  }
198 
199  safe_free(eek);
200  return cleartext;
201 }
202 
211 Auth::Basic::Config::decode(char const *proxy_auth, const HttpRequest *request, const char *aRequestRealm)
212 {
213  Auth::UserRequest::Pointer auth_user_request = dynamic_cast<Auth::UserRequest*>(new Auth::Basic::UserRequest);
214  /* decode the username */
215 
216  // retrieve the cleartext (in a dynamically allocated char*)
217  const auto cleartext = decodeCleartext(proxy_auth, request);
218 
219  // empty header? no auth details produced...
220  if (!cleartext)
221  return auth_user_request;
222 
224  /* permitted because local_basic is purely local function scope. */
225  Auth::Basic::User *local_basic = nullptr;
226 
227  char *separator = strchr(cleartext, ':');
228 
229  lb = local_basic = new Auth::Basic::User(this, aRequestRealm);
230 
231  if (separator) {
232  /* terminate the username */
233  *separator = '\0';
234  local_basic->passwd = xstrdup(separator+1);
235  }
236 
237  if (!casesensitive)
238  Tolower(cleartext);
239  local_basic->username(cleartext);
240 
241  if (local_basic->passwd == nullptr) {
242  debugs(29, 4, "no password in proxy authorization header '" << proxy_auth << "'");
243  auth_user_request->setDenyMessage("no password was present in the HTTP [proxy-]authorization header. This is most likely a browser bug");
244  } else {
245  if (local_basic->passwd[0] == '\0') {
246  debugs(29, 4, "Disallowing empty password. User is '" << local_basic->username() << "'");
247  safe_free(local_basic->passwd);
248  auth_user_request->setDenyMessage("Request denied because you provided an empty password. Users MUST have a password.");
249  }
250  }
251 
252  xfree(cleartext);
253 
254  if (!local_basic->valid()) {
255  lb->auth_type = Auth::AUTH_BROKEN;
256  auth_user_request->user(lb);
257  return auth_user_request;
258  }
259 
260  /* now lookup and see if we have a matching auth_user structure in memory. */
261  Auth::User::Pointer auth_user;
262 
263  if (!(auth_user = Auth::Basic::User::Cache()->lookup(lb->userKey()))) {
264  /* the user doesn't exist in the username cache yet */
265  /* save the credentials */
266  debugs(29, 9, "Creating new user '" << lb->username() << "'");
267  /* set the auth_user type */
268  lb->auth_type = Auth::AUTH_BASIC;
269  /* current time for timeouts */
270  lb->expiretime = current_time.tv_sec;
271 
272  /* this basic_user struct is the 'lucky one' to get added to the username cache */
273  /* the requests after this link to the basic_user */
274  /* store user in hash */
275  lb->addToNameCache();
276 
277  auth_user = lb;
278  assert(auth_user != nullptr);
279  } else {
280  /* replace the current cached password with the new one */
281  Auth::Basic::User *basic_auth = dynamic_cast<Auth::Basic::User *>(auth_user.getRaw());
282  assert(basic_auth);
283  basic_auth->updateCached(local_basic);
284  auth_user = basic_auth;
285  }
286 
287  /* link the request to the in-cache user */
288  auth_user_request->user(auth_user);
289  return auth_user_request;
290 }
291 
294 void
295 Auth::Basic::Config::init(Auth::SchemeConfig *)
296 {
297  if (authenticateProgram) {
299 
300  if (basicauthenticators == nullptr)
301  basicauthenticators = Helper::Client::Make("basicauthenticator");
302 
303  basicauthenticators->cmdline = authenticateProgram;
304 
305  basicauthenticators->childs.updateLimits(authenticateChildren);
306 
307  basicauthenticators->ipc_type = IPC_STREAM;
308 
309  basicauthenticators->openSessions();
310  }
311 }
312 
313 void
314 Auth::Basic::Config::registerWithCacheManager(void)
315 {
316  Mgr::RegisterAction("basicauthenticator",
317  "Basic User Authenticator Stats",
318  authenticateBasicStats, 0, 1);
319 }
320 
void wordlistDestroy(wordlist **list)
destroy a wordlist
Definition: wordlist.cc:16
#define xmalloc
HttpHeader header
Definition: Message.h:74
void base64_decode_init(struct base64_decode_ctx *ctx)
Definition: base64.c:54
void storeAppendPrintf(StoreEntry *e, const char *fmt,...)
Definition: store.cc:855
Definition: SBuf.h:93
#define xstrdup
C * getRaw() const
Definition: RefCount.h:89
static struct node * parse(FILE *fp)
Definition: parse.c:965
void setDenyMessage(char const *)
Definition: UserRequest.cc:114
virtual User::Pointer user()
Definition: UserRequest.h:143
static Pointer Make(const char *name)
Definition: helper.cc:757
void httpHeaderPutStrf(HttpHeader *hdr, Http::HdrType id, const char *fmt,...)
void helperShutdown(const Helper::Client::Pointer &hlp)
Definition: helper.cc:769
void Tolower(char *)
Definition: util.cc:28
SBuf Latin1ToUtf8(const char *in)
converts ISO-LATIN-1 to UTF-8
Definition: toUtf.cc:16
int base64_decode_final(struct base64_decode_ctx *ctx)
Definition: base64.c:159
struct timeval current_time
the current UNIX time in timeval {seconds, microseconds} format
Definition: gadgets.cc:18
void parse_time_t(time_t *var)
Definition: cache_cf.cc:2952
#define SQUIDSBUFPRINT(s)
Definition: SBuf.h:32
static int authbasic_initialised
Definition: Config.cc:41
struct _Cache Cache
#define xisgraph(x)
Definition: xis.h:28
#define safe_free(x)
Definition: xalloc.h:73
#define assert(EX)
Definition: assert.h:17
int base64_decode_update(struct base64_decode_ctx *ctx, size_t *dst_length, uint8_t *dst, size_t src_length, const char *src)
Definition: base64.c:129
static AUTHSSTATS authenticateBasicStats
Definition: Config.cc:37
bool isValidUtf8String(const char *source, const char *sourceEnd)
returns whether the given input is a valid (or empty) sequence of UTF-8 code points
Definition: toUtf.cc:172
#define xfree
void AUTHSSTATS(StoreEntry *)
Definition: Gadgets.h:21
@ AUTH_BASIC
Definition: Type.h:19
void RegisterAction(char const *action, char const *desc, OBJH *handler, Protected, Atomic, Format)
Definition: Registration.cc:54
#define DBG_IMPORTANT
Definition: Stream.h:38
void parse_onoff(int *var)
Definition: cache_cf.cc:2580
virtual void done()
virtual void parse(SchemeConfig *, size_t, char *)
Definition: SchemeConfig.cc:84
virtual bool dump(StoreEntry *, const char *, SchemeConfig *) const
@ AUTH_BROKEN
Definition: Type.h:23
#define xisspace(x)
Definition: xis.h:15
#define IPC_STREAM
Definition: defines.h:104
#define debugs(SECTION, LEVEL, CONTENT)
Definition: Stream.h:192
SBuf Cp1251ToUtf8(const char *in)
converts CP1251 to UTF-8
Definition: toUtf.cc:37
#define SQUIDSBUFPH
Definition: SBuf.h:31
#define BASE64_DECODE_LENGTH(length)
Definition: base64.h:120
class SquidConfig Config
Definition: SquidConfig.cc:12
Helper::ClientPointer basicauthenticators
Definition: Config.cc:39

 

Introduction

Documentation

Support

Miscellaneous