114 SquidMD5Update(&Md5Ctx,
reinterpret_cast<const uint8_t *
>(&nonce->noncedata),
sizeof(nonce->noncedata));
118 CvtHex(H,
static_cast<char *
>(nonce->key));
164 static std::uniform_int_distribution<uint32_t> newRandomData;
168 newnonce->flags.valid =
true;
170 newnonce->noncedata.randomdata = newRandomData(mt);
177 newnonce->noncedata.randomdata = newRandomData(mt);
184 newnonce->flags.incache =
true;
185 debugs(29, 5,
"created nonce " << newnonce <<
" at " << newnonce->noncedata.creationtime);
193 assert(nonce->references == 0);
194 assert(!nonce->flags.incache);
221 digest_nonce_h *nonce;
224 debugs(29, 2,
"Shutting down nonce cache");
228 assert(nonce->flags.incache);
233 debugs(29, 2,
"Nonce cache shutdown");
245 digest_nonce_h *nonce;
246 debugs(29, 3,
"Cleaning the nonce cache now");
251 debugs(29, 3,
"nonce entry : " << nonce <<
" '" << (
char *) nonce->key <<
"'");
252 debugs(29, 4,
"Creation time: " << nonce->noncedata.creationtime);
255 debugs(29, 4,
"Removing nonce " << (
char *) nonce->key <<
" from cache due to timeout.");
256 assert(nonce->flags.incache);
258 nonce->flags.valid =
false;
265 debugs(29, 3,
"Finished cleaning the nonce cache.");
276 assert(nonce->references != 0);
277 debugs(29, 9,
"nonce '" << nonce <<
"' now at '" << nonce->references <<
"'.");
285 if (nonce->references > 0) {
286 -- nonce->references;
291 debugs(29, 9,
"nonce '" << nonce <<
"' now at '" << nonce->references <<
"'.");
293 if (nonce->references == 0)
303 return (
char const *) nonce->key;
306static digest_nonce_h *
309 digest_nonce_h *nonce =
nullptr;
311 if (noncehex ==
nullptr)
314 debugs(29, 9,
"looking for noncehex '" << noncehex <<
"' in the nonce cache.");
321 debugs(29, 9,
"Found nonce '" << nonce <<
"'");
335 intnc = strtol(nc,
nullptr, 16);
338 if (!nonce->flags.valid) {
339 debugs(29, 4,
"Nonce already invalidated");
346 intnc = nonce->nc + 1;
349 if ((
static_cast<Auth::Digest::Config*
>(
Auth::SchemeConfig::Find(
"digest"))->NonceStrictness && intnc != nonce->nc + 1) ||
350 intnc < nonce->nc + 1) {
351 debugs(29, 4,
"Nonce count doesn't match");
352 nonce->flags.valid =
false;
373 if (!nonce->flags.valid)
378 debugs(29, 4,
"Nonce is too old. " <<
379 nonce->noncedata.creationtime <<
" " <<
383 nonce->flags.valid =
false;
387 if (nonce->nc > 99999998) {
388 debugs(29, 4,
"Nonce count overflow");
389 nonce->flags.valid =
false;
394 debugs(29, 4,
"Nonce count over user limit");
395 nonce->flags.valid =
false;
413 if (nonce->nc == 99999997) {
414 debugs(29, 4,
"Nonce count about to overflow");
419 debugs(29, 4,
"Nonce count about to hit user limit");
433 if (!nonce->flags.incache)
438 nonce->flags.incache =
false;
445Auth::Digest::Config::rotateHelpers()
461 storeAppendPrintf(entry,
"%s %s nonce_max_count %d\n%s %s nonce_max_duration %d seconds\n%s %s nonce_garbage_interval %d seconds\n",
462 name,
"digest", noncemaxuses,
463 name,
"digest", (
int) noncemaxduration,
464 name,
"digest", (
int) nonceGCInterval);
469Auth::Digest::Config::active()
const
475Auth::Digest::Config::configured()
const
477 return (SchemeConfig::configured() && !realm.isEmpty() && noncemaxduration > -1);
484 if (!authenticateProgram)
488 digest_nonce_h *nonce =
nullptr;
491 if (auth_user_request !=
nullptr) {
492 Auth::Digest::User *digest_user =
dynamic_cast<Auth::Digest::User *
>(auth_user_request->
user().
getRaw());
497 nonce = digest_user->currentNonce();
505 debugs(29, 9,
"Sending type:" << hdrType <<
506 " header: 'Digest realm=\"" << realm <<
"\", nonce=\"" <<
508 "\", stale=" << (stale ?
"true" :
"false"));
520 if (authenticateProgram) {
538Auth::Digest::Config::registerWithCacheManager(
void)
541 "Digest User Authenticator Stats",
547Auth::Digest::Config::done()
561 if (authenticateProgram)
565Auth::Digest::Config::Config() :
566 nonceGCInterval(5*60),
567 noncemaxduration(30*60),
575Auth::Digest::Config::parse(
Auth::SchemeConfig * scheme,
size_t n_configured,
char *param_str)
577 if (strcmp(param_str,
"nonce_garbage_interval") == 0) {
579 }
else if (strcmp(param_str,
"nonce_max_duration") == 0) {
581 }
else if (strcmp(param_str,
"nonce_max_count") == 0) {
583 }
else if (strcmp(param_str,
"nonce_strictness") == 0) {
585 }
else if (strcmp(param_str,
"check_nonce_count") == 0) {
587 }
else if (strcmp(param_str,
"post_workaround") == 0) {
594Auth::Digest::Config::type()
const
596 return Auth::Digest::Scheme::GetInstance()->type();
611 Auth::Digest::User *digest_user;
620 digest_user = nonce->user;
625 link = digest_user->nonces.head;
631 if (tmplink->
data == nonce) {
642 nonce->user =
nullptr;
651 if (!user || !nonce || !nonce->user)
654 Auth::Digest::User *digest_user = user;
656 node = digest_user->nonces.head;
658 while (
node && (
node->data != nonce))
671 assert((nonce->user ==
nullptr) || (nonce->user == user));
683 assert(auth_user_request !=
nullptr);
686 debugs(29, 9,
"Creating new user for logging '" << (username?username:
"[no username]") <<
"'");
689 digest_user->username(username);
693 auth_user_request->
user(digest_user);
694 return auth_user_request;
702Auth::Digest::Config::decode(
char const *proxy_auth,
const HttpRequest *request,
const char *aRequestRealm)
706 const char *pos =
nullptr;
707 char *username =
nullptr;
708 digest_nonce_h *nonce;
711 debugs(29, 9,
"beginning");
713 Auth::Digest::UserRequest *digest_request =
new Auth::Digest::UserRequest();
730 if ((p = (
const char *)memchr(item,
'=', ilen)) && (p - item < ilen)) {
733 vlen = ilen - (p - item);
739 SBuf keyName(item, nlen);
745 if (keyName ==
SBuf(
"domain",6) || keyName ==
SBuf(
"uri",3)) {
748 if (vlen > 1 && *p ==
'"' && *(p + vlen -1) ==
'"') {
749 value.
assign(p+1, vlen-2);
751 }
else if (keyName ==
SBuf(
"qop",3)) {
755 if (vlen > 1 && *p ==
'"' && *(p + vlen -1) ==
'"') {
756 value.
assign(p+1, vlen-2);
760 }
else if (*p ==
'"') {
762 debugs(29, 9,
"Failed to parse attribute '" << item <<
"' in '" << temp <<
"'");
769 debugs(29, 9,
"Failed to parse attribute '" << item <<
"' in '" << temp <<
"'");
779 if (value.
size() != 0) {
787 debugs(29, 9,
"Found Username '" << username <<
"'");
792 if (value.
size() != 0)
794 debugs(29, 9,
"Found realm '" << digest_request->realm <<
"'");
799 if (value.
size() != 0)
801 debugs(29, 9,
"Found qop '" << digest_request->qop <<
"'");
806 if (value.
size() != 0)
808 debugs(29, 9,
"Found algorithm '" << digest_request->algorithm <<
"'");
813 if (value.
size() != 0)
815 debugs(29, 9,
"Found uri '" << digest_request->uri <<
"'");
820 if (value.
size() != 0)
822 debugs(29, 9,
"Found nonce '" << digest_request->noncehex <<
"'");
826 if (value.
size() == 8) {
828 static_assert(
sizeof(digest_request->nc) == 8 + 1);
830 debugs(29, 9,
"Found noncecount '" << digest_request->nc <<
"'");
832 debugs(29, 9,
"Invalid nc '" << value <<
"' in '" << temp <<
"'");
833 digest_request->nc[0] = 0;
839 if (value.
size() != 0)
841 debugs(29, 9,
"Found cnonce '" << digest_request->cnonce <<
"'");
846 if (value.
size() != 0)
848 debugs(29, 9,
"Found response '" << digest_request->response <<
"'");
852 debugs(29, 3,
"Unknown attribute '" << item <<
"' in '" << temp <<
"'");
875 if (!username || username[0] ==
'\0') {
876 debugs(29, 2,
"Empty or not present username");
886 if (strchr(username,
'"')) {
887 debugs(29, 2,
"Unacceptable username '" << username <<
"'");
894 if (!digest_request->realm || digest_request->realm[0] ==
'\0') {
895 debugs(29, 2,
"Empty or not present realm");
902 if (!digest_request->noncehex || digest_request->noncehex[0] ==
'\0') {
903 debugs(29, 2,
"Empty or not present nonce");
911 if (!digest_request->uri || digest_request->uri[0] ==
'\0') {
912 debugs(29, 2,
"Missing URI field");
919 if (!digest_request->response || strlen(digest_request->response) != 32) {
920 debugs(29, 2,
"Response length invalid");
927 if (!digest_request->algorithm)
928 digest_request->algorithm =
xstrndup(
"MD5", 4);
929 else if (strcmp(digest_request->algorithm,
"MD5")
930 && strcmp(digest_request->algorithm,
"MD5-sess")) {
931 debugs(29, 2,
"invalid algorithm specified!");
938 if (digest_request->qop) {
941 if (strcmp(digest_request->qop, QOP_AUTH) != 0) {
943 debugs(29, 2,
"Invalid qop option received");
950 if (!digest_request->cnonce || digest_request->cnonce[0] ==
'\0') {
951 debugs(29, 2,
"Missing cnonce field");
958 if (strlen(digest_request->nc) != 8 || strspn(digest_request->nc,
"0123456789abcdefABCDEF") != 8) {
959 debugs(29, 2,
"invalid nonce count");
974 debugs(29, 2,
"missing qop!");
985 if (nonce && nonce->user && strcmp(username, nonce->user->username())) {
986 debugs(29, 2,
"Username for the nonce does not equal the username for the request");
992 debugs(29, 2,
"Unexpected or invalid nonce received from " << username);
999 digest_request->nonce = nonce;
1003 if (nonce->user && strcmp(username, nonce->user->username())) {
1004 debugs(29, 2,
"Username for the nonce does not equal the username for the request");
1015 Auth::Digest::User *digest_user;
1020 if (key.
isEmpty() || !(auth_user = Auth::Digest::User::Cache()->lookup(key))) {
1022 debugs(29, 9,
"Creating new digest user '" << username <<
"'");
1023 digest_user =
new Auth::Digest::User(
this, aRequestRealm);
1025 auth_user = digest_user;
1027 digest_user->username(username);
1033 digest_user->addToNameCache();
1046 debugs(29, 9,
"Found user '" << username <<
"' in the user cache as '" << auth_user <<
"'");
1047 digest_user =
static_cast<Auth::Digest::User *
>(auth_user.
getRaw());
1053 assert(digest_request !=
nullptr);
1055 digest_request->user(digest_user);
1056 debugs(29, 9,
"username = '" << digest_user->username() <<
"'\nrealm = '" <<
1057 digest_request->realm <<
"'\nqop = '" << digest_request->qop <<
1058 "'\nalgorithm = '" << digest_request->algorithm <<
"'\nuri = '" <<
1059 digest_request->uri <<
"'\nnonce = '" << digest_request->noncehex <<
1060 "'\nnc = '" << digest_request->nc <<
"'\ncnonce = '" <<
1061 digest_request->cnonce <<
"'\nresponse = '" <<
1062 digest_request->response <<
"'\ndigestnonce = '" << nonce <<
"'");
1064 return digest_request;
#define memPoolCreate
Creates a named MemPool of elements with the given size.
#define SQUIDSBUFPRINT(s)
int strListGetItem(const String *str, char del, const char **item, int *ilen, const char **pos)
String SBufToString(const SBuf &s)
void AUTHSSTATS(StoreEntry *)
static void authDigestNonceUserUnlink(digest_nonce_h *nonce)
static Mem::Allocator * digest_nonce_pool
static hash_table * digest_nonce_cache
Helper::ClientPointer digestauthenticators
void authDigestUserLinkNonce(Auth::Digest::User *user, digest_nonce_h *nonce)
static Auth::UserRequest::Pointer authDigestLogUsername(char *username, Auth::UserRequest::Pointer auth_user_request, const char *requestRealm)
void authDigestNonceUnlink(digest_nonce_h *nonce)
static void authenticateDigestNonceDelete(digest_nonce_h *nonce)
static digest_nonce_h * authenticateDigestNonceFindNonce(const char *noncehex)
int authDigestNonceIsStale(digest_nonce_h *nonce)
static int authdigest_initialised
static void authDigestNonceEncode(digest_nonce_h *nonce)
int authDigestNonceIsValid(digest_nonce_h *nonce, char nc[9])
void authenticateDigestNonceShutdown(void)
const char * authenticateDigestNonceNonceHex(const digest_nonce_h *nonce)
int authDigestNonceLastRequest(digest_nonce_h *nonce)
static void authenticateDigestNonceSetup(void)
void authDigestNoncePurge(digest_nonce_h *nonce)
static AUTHSSTATS authenticateDigestStats
digest_nonce_h * authenticateDigestNonceNew(void)
static const auto & digestFieldsLookupTable()
static void authenticateDigestNonceCacheCleanup(void *data)
static void authDigestNonceLink(digest_nonce_h *nonce)
std::mt19937::result_type RandomSeed32()
void parse_time_t(time_t *var)
void parse_onoff(int *var)
virtual void parse(SchemeConfig *, size_t, char *)
virtual bool dump(StoreEntry *, const char *, SchemeConfig *) const
static SchemeConfig * Find(const char *proxy_auth)
virtual User::Pointer user()
static SBuf BuildUserKey(const char *username, const char *realm)
static Pointer Make(const char *name)
void freeOne(void *obj)
return memory reserved by alloc()
void * alloc()
provide (and reserve) memory suitable for storing one object
void assign(const char *str, int len)
char const * rawBuf() const
char const * termedBuf() const
#define debugs(SECTION, LEVEL, CONTENT)
void dlinkDelete(dlink_node *m, dlink_list *list)
void dlinkAddTail(void *data, dlink_node *m, dlink_list *list)
void eventAdd(const char *name, EVH *func, void *arg, double when, int weight, bool cbdata)
hash_link * hash_lookup(hash_table *, const void *)
hash_table * hash_create(HASHCMP *, int, HASHHASH *)
int HASHCMP(const void *, const void *)
hash_link * hash_next(hash_table *)
void hash_first(hash_table *)
void hash_join(hash_table *, hash_link *)
void hash_remove_link(hash_table *, hash_link *)
void helperShutdown(const Helper::Client::Pointer &hlp)
SQUIDCEXTERN void SquidMD5Init(struct SquidMD5Context *context)
SQUIDCEXTERN void SquidMD5Update(struct SquidMD5Context *context, const void *buf, unsigned len)
SQUIDCEXTERN void SquidMD5Final(uint8_t digest[16], struct SquidMD5Context *context)
void RegisterAction(char const *action, char const *desc, OBJH *handler, Protected, Atomic, Format)
void CvtHex(const HASH Bin, HASHHEX Hex)
char HASHHEX[HASHHEXLEN+1]
void storeAppendPrintf(StoreEntry *e, const char *fmt,...)
struct timeval current_time
the current UNIX time in timeval {seconds, microseconds} format
SBuf Cp1251ToUtf8(const char *in)
converts CP1251 to UTF-8
SBuf Latin1ToUtf8(const char *in)
converts ISO-LATIN-1 to UTF-8
bool isValidUtf8String(const char *source, const char *sourceEnd)
returns whether the given input is a valid (or empty) sequence of UTF-8 code points
void wordlistDestroy(wordlist **list)
destroy a wordlist
void * xcalloc(size_t n, size_t sz)
char * xstrncpy(char *dst, const char *src, size_t n)
char * xstrndup(const char *s, size_t n)