* See acl.c for access control and client_side.c for auditing */
-#include "squid.h"
+#include "squid-old.h"
#include "rfc2617.h"
#include "auth/digest/auth_digest.h"
+#include "auth/digest/Scheme.h"
+#include "auth/digest/User.h"
+#include "auth/digest/UserRequest.h"
#include "auth/Gadgets.h"
+#include "auth/State.h"
+#include "base64.h"
+#include "base/StringArea.h"
#include "event.h"
-#include "CacheManager.h"
+#include "mgr/Registration.h"
#include "Store.h"
#include "HttpRequest.h"
#include "HttpReply.h"
#include "wordlist.h"
#include "SquidTime.h"
-/* TODO don't include this */
-#include "auth/digest/digestScheme.h"
/* Digest Scheme */
-static HLPCB authenticateDigestHandleReply;
static AUTHSSTATS authenticateDigestStats;
-static helper *digestauthenticators = NULL;
+helper *digestauthenticators = NULL;
static hash_table *digest_nonce_cache;
static int authdigest_initialised = 0;
static MemAllocator *digest_nonce_pool = NULL;
-CBDATA_TYPE(DigestAuthenticateStateData);
+enum http_digest_attr_type {
+ DIGEST_USERNAME,
+ DIGEST_REALM,
+ DIGEST_QOP,
+ DIGEST_ALGORITHM,
+ DIGEST_URI,
+ DIGEST_NONCE,
+ DIGEST_NC,
+ DIGEST_CNONCE,
+ DIGEST_RESPONSE,
+ DIGEST_ENUM_END
+};
+
+static const HttpHeaderFieldAttrs DigestAttrs[DIGEST_ENUM_END] = {
+ {"username", (http_hdr_type)DIGEST_USERNAME},
+ {"realm", (http_hdr_type)DIGEST_REALM},
+ {"qop", (http_hdr_type)DIGEST_QOP},
+ {"algorithm", (http_hdr_type)DIGEST_ALGORITHM},
+ {"uri", (http_hdr_type)DIGEST_URI},
+ {"nonce", (http_hdr_type)DIGEST_NONCE},
+ {"nc", (http_hdr_type)DIGEST_NC},
+ {"cnonce", (http_hdr_type)DIGEST_CNONCE},
+ {"response", (http_hdr_type)DIGEST_RESPONSE},
+};
+
+static HttpHeaderFieldInfo *DigestFieldsInfo = NULL;
/*
*
static digest_nonce_h *authenticateDigestNonceNew(void);
static void authenticateDigestNonceDelete(digest_nonce_h * nonce);
static void authenticateDigestNonceSetup(void);
-static void authenticateDigestNonceShutdown(void);
-static void authenticateDigestNonceReconfigure(void);
-static const char *authenticateDigestNonceNonceb64(digest_nonce_h * nonce);
-static int authDigestNonceIsValid(digest_nonce_h * nonce, char nc[9]);
static int authDigestNonceIsStale(digest_nonce_h * nonce);
static void authDigestNonceEncode(digest_nonce_h * nonce);
-static int authDigestNonceLastRequest(digest_nonce_h * nonce);
static void authDigestNonceLink(digest_nonce_h * nonce);
-static void authDigestNonceUnlink(digest_nonce_h * nonce);
#if NOT_USED
static int authDigestNonceLinks(digest_nonce_h * nonce);
#endif
static void authDigestNonceUserUnlink(digest_nonce_h * nonce);
-static void authDigestNoncePurge(digest_nonce_h * nonce);
static void
authDigestNonceEncode(digest_nonce_h * nonce)
safe_free(nonce->key);
- digest_nonce_pool->free(nonce);
+ digest_nonce_pool->freeOne(nonce);
}
}
if (!digest_nonce_cache) {
digest_nonce_cache = hash_create((HASHCMP *) strcmp, 7921, hash_string);
assert(digest_nonce_cache);
- eventAdd("Digest none cache maintenance", authenticateDigestNonceCacheCleanup, NULL, static_cast<AuthDigestConfig*>(AuthConfig::Find("digest"))->nonceGCInterval, 1);
+ eventAdd("Digest none cache maintenance", authenticateDigestNonceCacheCleanup, NULL, static_cast<Auth::Digest::Config*>(Auth::Config::Find("digest"))->nonceGCInterval, 1);
}
}
-static void
+void
authenticateDigestNonceShutdown(void)
{
/*
debugs(29, 2, "authenticateDigestNonceShutdown: Nonce cache shutdown");
}
-static void
-authenticateDigestNonceReconfigure(void)
-{}
-
static void
authenticateDigestNonceCacheCleanup(void *data)
{
debugs(29, 3, "authenticateDigestNonceCacheCleanup: Finished cleaning the nonce cache.");
- if (static_cast<AuthDigestConfig*>(AuthConfig::Find("digest"))->active())
- eventAdd("Digest none cache maintenance", authenticateDigestNonceCacheCleanup, NULL, static_cast<AuthDigestConfig*>(AuthConfig::Find("digest"))->nonceGCInterval, 1);
+ if (static_cast<Auth::Digest::Config*>(Auth::Config::Find("digest"))->active())
+ eventAdd("Digest none cache maintenance", authenticateDigestNonceCacheCleanup, NULL, static_cast<Auth::Digest::Config*>(Auth::Config::Find("digest"))->nonceGCInterval, 1);
}
static void
#endif
-static void
+void
authDigestNonceUnlink(digest_nonce_h * nonce)
{
assert(nonce != NULL);
authenticateDigestNonceDelete(nonce);
}
-static const char *
-authenticateDigestNonceNonceb64(digest_nonce_h * nonce)
+const char *
+authenticateDigestNonceNonceb64(const digest_nonce_h * nonce)
{
if (!nonce)
return NULL;
return nonce;
}
-static int
+int
authDigestNonceIsValid(digest_nonce_h * nonce, char nc[9])
{
unsigned long intnc;
}
/* is the nonce-count ok ? */
- if (!static_cast<AuthDigestConfig*>(AuthConfig::Find("digest"))->CheckNonceCount) {
+ if (!static_cast<Auth::Digest::Config*>(Auth::Config::Find("digest"))->CheckNonceCount) {
nonce->nc++;
return -1; /* forced OK by configuration */
}
- if ((static_cast<AuthDigestConfig*>(AuthConfig::Find("digest"))->NonceStrictness && intnc != nonce->nc + 1) ||
+ if ((static_cast<Auth::Digest::Config*>(Auth::Config::Find("digest"))->NonceStrictness && intnc != nonce->nc + 1) ||
intnc < nonce->nc + 1) {
debugs(29, 4, "authDigestNonceIsValid: Nonce count doesn't match");
nonce->flags.valid = 0;
return -1;
/* has it's max duration expired? */
- if (nonce->noncedata.creationtime + static_cast<AuthDigestConfig*>(AuthConfig::Find("digest"))->noncemaxduration < current_time.tv_sec) {
+ if (nonce->noncedata.creationtime + static_cast<Auth::Digest::Config*>(Auth::Config::Find("digest"))->noncemaxduration < current_time.tv_sec) {
debugs(29, 4, "authDigestNonceIsStale: Nonce is too old. " <<
nonce->noncedata.creationtime << " " <<
- static_cast<AuthDigestConfig*>(AuthConfig::Find("digest"))->noncemaxduration << " " <<
+ static_cast<Auth::Digest::Config*>(Auth::Config::Find("digest"))->noncemaxduration << " " <<
current_time.tv_sec);
nonce->flags.valid = 0;
return -1;
}
- if (nonce->nc > static_cast<AuthDigestConfig*>(AuthConfig::Find("digest"))->noncemaxuses) {
+ if (nonce->nc > static_cast<Auth::Digest::Config*>(Auth::Config::Find("digest"))->noncemaxuses) {
debugs(29, 4, "authDigestNoncelastRequest: Nonce count over user limit");
nonce->flags.valid = 0;
return -1;
return 0;
}
-/* return -1 if the digest will be stale on the next request */
-static int
+/**
+ * \retval 0 the digest is not stale yet
+ * \retval -1 the digest will be stale on the next request
+ */
+int
authDigestNonceLastRequest(digest_nonce_h * nonce)
{
if (!nonce)
return -1;
}
- if (nonce->nc >= static_cast<AuthDigestConfig*>(AuthConfig::Find("digest"))->noncemaxuses - 1) {
+ if (nonce->nc >= static_cast<Auth::Digest::Config*>(Auth::Config::Find("digest"))->noncemaxuses - 1) {
debugs(29, 4, "authDigestNoncelastRequest: Nonce count about to hit user limit");
return -1;
}
return 0;
}
-static void
+void
authDigestNoncePurge(digest_nonce_h * nonce)
{
if (!nonce)
}
/* USER related functions */
-static AuthUser *
+static Auth::User::Pointer
authDigestUserFindUsername(const char *username)
{
AuthUserHashPointer *usernamehash;
- AuthUser *auth_user;
debugs(29, 9, HERE << "Looking for user '" << username << "'");
- if (username && (usernamehash = static_cast < auth_user_hash_pointer * >(hash_lookup(proxy_auth_username_cache, username)))) {
- while ((usernamehash->user()->auth_type != AUTH_DIGEST) &&
- (usernamehash->next))
- usernamehash = static_cast < auth_user_hash_pointer * >(usernamehash->next);
-
- auth_user = NULL;
+ if (username && (usernamehash = static_cast < AuthUserHashPointer * >(hash_lookup(proxy_auth_username_cache, username)))) {
+ while ((usernamehash->user()->auth_type != Auth::AUTH_DIGEST) && (usernamehash->next))
+ usernamehash = static_cast<AuthUserHashPointer *>(usernamehash->next);
- if (usernamehash->user()->auth_type == AUTH_DIGEST) {
- auth_user = usernamehash->user();
+ if (usernamehash->user()->auth_type == Auth::AUTH_DIGEST) {
+ return usernamehash->user();
}
-
- return auth_user;
}
return NULL;
}
-static void
-authDigestUserShutdown(void)
-{
- /** \todo Future work: the auth framework could flush it's cache */
- AuthUserHashPointer *usernamehash;
- AuthUser *auth_user;
- hash_first(proxy_auth_username_cache);
-
- while ((usernamehash = ((auth_user_hash_pointer *) hash_next(proxy_auth_username_cache)))) {
- auth_user = usernamehash->user();
-
- if (strcmp(auth_user->config->type(), "digest") == 0)
- auth_user->unlock();
- }
-}
-
-/** delete the digest request structure. Does NOT delete related structures */
void
-digestScheme::done()
+Auth::Digest::Config::rotateHelpers()
{
- /** \todo this should be a Config call. */
-
- if (digestauthenticators)
+ /* schedule closure of existing helpers */
+ if (digestauthenticators) {
helperShutdown(digestauthenticators);
-
- authdigest_initialised = 0;
-
- if (!shutting_down) {
- authenticateDigestNonceReconfigure();
- return;
}
- delete digestauthenticators;
- digestauthenticators = NULL;
-
- authDigestUserShutdown();
- authenticateDigestNonceShutdown();
- debugs(29, 2, "authenticateDigestDone: Digest authentication shut down.");
-
- /* clear the global handle to this scheme. */
- _instance = NULL;
+ /* NP: dynamic helper restart will ensure they start up again as needed. */
}
void
-AuthDigestConfig::dump(StoreEntry * entry, const char *name, AuthConfig * scheme)
+Auth::Digest::Config::dump(StoreEntry * entry, const char *name, Auth::Config * scheme)
{
- wordlist *list = authenticate;
+ wordlist *list = authenticateProgram;
debugs(29, 9, "authDigestCfgDump: Dumping configuration");
storeAppendPrintf(entry, "%s %s", name, "digest");
}
bool
-AuthDigestConfig::active() const
+Auth::Digest::Config::active() const
{
return authdigest_initialised == 1;
}
bool
-AuthDigestConfig::configured() const
+Auth::Digest::Config::configured() const
{
- if ((authenticate != NULL) &&
+ if ((authenticateProgram != NULL) &&
(authenticateChildren.n_max != 0) &&
(digestAuthRealm != NULL) && (noncemaxduration > -1))
return true;
return false;
}
-int
-AuthDigestUserRequest::authenticated() const
-{
- if (credentials() == Ok)
- return 1;
-
- return 0;
-}
-
-/** log a digest user in
- */
-void
-AuthDigestUserRequest::authenticate(HttpRequest * request, ConnStateData * conn, http_hdr_type type)
-{
- AuthDigestUserRequest *digest_request;
- digest_user_h *digest_user;
-
- HASHHEX SESSIONKEY;
- HASHHEX HA2 = "";
- HASHHEX Response;
-
- assert(user() != NULL);
- AuthUser *auth_user = user();
-
- digest_user = dynamic_cast<digest_user_h*>(auth_user);
- assert(digest_user != NULL);
-
- /* if the check has corrupted the user, just return */
-
- if (credentials() == Failed) {
- return;
- }
-
- digest_request = this;
-
- /* do we have the HA1 */
-
- if (!digest_user->HA1created) {
- credentials(Pending);
- return;
- }
-
- if (digest_request->nonce == NULL) {
- /* this isn't a nonce we issued */
- credentials(Failed);
- return;
- }
-
- DigestCalcHA1(digest_request->algorithm, NULL, NULL, NULL,
- authenticateDigestNonceNonceb64(digest_request->nonce),
- digest_request->cnonce,
- digest_user->HA1, SESSIONKEY);
- DigestCalcResponse(SESSIONKEY, authenticateDigestNonceNonceb64(digest_request->nonce),
- digest_request->nc, digest_request->cnonce, digest_request->qop,
- RequestMethodStr(request->method), digest_request->uri, HA2, Response);
-
- debugs(29, 9, "\nResponse = '" << digest_request->response << "'\nsquid is = '" << Response << "'");
-
- if (strcasecmp(digest_request->response, Response) != 0) {
- if (!digest_request->flags.helper_queried) {
- /* Query the helper in case the password has changed */
- digest_request->flags.helper_queried = 1;
- digest_request->credentials_ok = Pending;
- return;
- }
-
- if (static_cast<AuthDigestConfig*>(AuthConfig::Find("digest"))->PostWorkaround && request->method != METHOD_GET) {
- /* Ugly workaround for certain very broken browsers using the
- * wrong method to calculate the request-digest on POST request.
- * This should be deleted once Digest authentication becomes more
- * widespread and such broken browsers no longer are commonly
- * used.
- */
- DigestCalcResponse(SESSIONKEY, authenticateDigestNonceNonceb64(digest_request->nonce),
- digest_request->nc, digest_request->cnonce, digest_request->qop,
- RequestMethodStr(METHOD_GET), digest_request->uri, HA2, Response);
-
- if (strcasecmp(digest_request->response, Response)) {
- credentials(Failed);
- digest_request->setDenyMessage("Incorrect password");
- return;
- } else {
- const char *useragent = request->header.getStr(HDR_USER_AGENT);
-
- static IpAddress last_broken_addr;
- static int seen_broken_client = 0;
-
- if (!seen_broken_client) {
- last_broken_addr.SetNoAddr();
- seen_broken_client = 1;
- }
-
- if (last_broken_addr != request->client_addr) {
- debugs(29, 1, "\nDigest POST bug detected from " <<
- request->client_addr << " using '" <<
- (useragent ? useragent : "-") <<
- "'. Please upgrade browser. See Bug #630 for details.");
-
- last_broken_addr = request->client_addr;
- }
- }
- } else {
- credentials(Failed);
- digest_request->flags.invalid_password = 1;
- digest_request->setDenyMessage("Incorrect password");
- return;
- }
-
- /* check for stale nonce */
- if (!authDigestNonceIsValid(digest_request->nonce, digest_request->nc)) {
- debugs(29, 3, "authenticateDigestAuthenticateuser: user '" << digest_user->username() << "' validated OK but nonce stale");
- credentials(Failed);
- digest_request->setDenyMessage("Stale nonce");
- return;
- }
- }
-
- credentials(Ok);
-
- /* password was checked and did match */
- debugs(29, 4, "authenticateDigestAuthenticateuser: user '" << digest_user->username() << "' validated OK");
-
- /* auth_user is now linked, we reset these values
- * after external auth occurs anyway */
- auth_user->expiretime = current_time.tv_sec;
- return;
-}
-
-int
-AuthDigestUserRequest::module_direction()
-{
- switch (credentials()) {
-
- case Unchecked:
- return -1;
-
- case Ok:
-
- return 0;
-
- case Pending:
- return -1;
-
- case Failed:
-
- /* send new challenge */
- return 1;
- }
-
- return -2;
-}
-
-/* add the [proxy]authorisation header */
-void
-AuthDigestUserRequest::addHeader(HttpReply * rep, int accel)
-{
- http_hdr_type type;
-
- /* don't add to authentication error pages */
-
- if ((!accel && rep->sline.status == HTTP_PROXY_AUTHENTICATION_REQUIRED)
- || (accel && rep->sline.status == HTTP_UNAUTHORIZED))
- return;
-
- type = accel ? HDR_AUTHENTICATION_INFO : HDR_PROXY_AUTHENTICATION_INFO;
-
-#if WAITING_FOR_TE
- /* test for http/1.1 transfer chunked encoding */
- if (chunkedtest)
- return;
-
-#endif
-
- if ((static_cast<AuthDigestConfig*>(AuthConfig::Find("digest"))->authenticate) && authDigestNonceLastRequest(nonce)) {
- flags.authinfo_sent = 1;
- debugs(29, 9, "authDigestAddHead: Sending type:" << type << " header: 'nextnonce=\"" << authenticateDigestNonceNonceb64(nonce) << "\"");
- httpHeaderPutStrf(&rep->header, type, "nextnonce=\"%s\"", authenticateDigestNonceNonceb64(nonce));
- }
-}
-
-#if WAITING_FOR_TE
-/* add the [proxy]authorisation header */
-void
-AuthDigestUserRequest::addTrailer(HttpReply * rep, int accel)
-{
- int type;
-
- if (!auth_user_request)
- return;
-
-
- /* has the header already been send? */
- if (flags.authinfo_sent)
- return;
-
- /* don't add to authentication error pages */
- if ((!accel && rep->sline.status == HTTP_PROXY_AUTHENTICATION_REQUIRED)
- || (accel && rep->sline.status == HTTP_UNAUTHORIZED))
- return;
-
- type = accel ? HDR_AUTHENTICATION_INFO : HDR_PROXY_AUTHENTICATION_INFO;
-
- if ((static_cast<AuthDigestConfig*>(digestScheme::GetInstance()->getConfig())->authenticate) && authDigestNonceLastRequest(nonce)) {
- debugs(29, 9, "authDigestAddTrailer: Sending type:" << type << " header: 'nextnonce=\"" << authenticateDigestNonceNonceb64(nonce) << "\"");
- httpTrailerPutStrf(&rep->header, type, "nextnonce=\"%s\"", authenticateDigestNonceNonceb64(nonce));
- }
-}
-
-#endif
-
/* add the [www-|Proxy-]authenticate header on a 407 or 401 reply */
void
-AuthDigestConfig::fixHeader(AuthUserRequest::Pointer auth_user_request, HttpReply *rep, http_hdr_type hdrType, HttpRequest * request)
+Auth::Digest::Config::fixHeader(Auth::UserRequest::Pointer auth_user_request, HttpReply *rep, http_hdr_type hdrType, HttpRequest * request)
{
- if (!authenticate)
+ if (!authenticateProgram)
return;
int stale = 0;
if (auth_user_request != NULL) {
- AuthDigestUserRequest *digest_request;
- digest_request = dynamic_cast<AuthDigestUserRequest*>(auth_user_request.getRaw());
+ Auth::Digest::UserRequest *digest_request = dynamic_cast<Auth::Digest::UserRequest*>(auth_user_request.getRaw());
assert (digest_request != NULL);
stale = !digest_request->flags.invalid_password;
/* on a 407 or 401 we always use a new nonce */
digest_nonce_h *nonce = authenticateDigestNonceNew();
- debugs(29, 9, "authenticateFixHeader: Sending type:" << hdrType <<
+ debugs(29, 9, HERE << "Sending type:" << hdrType <<
" header: 'Digest realm=\"" << digestAuthRealm << "\", nonce=\"" <<
authenticateDigestNonceNonceb64(nonce) << "\", qop=\"" << QOP_AUTH <<
"\", stale=" << (stale ? "true" : "false"));
httpHeaderPutStrf(&rep->header, hdrType, "Digest realm=\"%s\", nonce=\"%s\", qop=\"%s\", stale=%s", digestAuthRealm, authenticateDigestNonceNonceb64(nonce), QOP_AUTH, stale ? "true" : "false");
}
-DigestUser::~DigestUser()
-{
-
- dlink_node *link, *tmplink;
- link = nonces.head;
-
- while (link) {
- tmplink = link;
- link = link->next;
- dlinkDelete(tmplink, &nonces);
- authDigestNoncePurge(static_cast < digest_nonce_h * >(tmplink->data));
- authDigestNonceUnlink(static_cast < digest_nonce_h * >(tmplink->data));
- dlinkNodeDelete(tmplink);
- }
-}
-
-static void
-authenticateDigestHandleReply(void *data, char *reply)
-{
- DigestAuthenticateStateData *replyData = static_cast < DigestAuthenticateStateData * >(data);
- char *t = NULL;
- void *cbdata;
- debugs(29, 9, "authenticateDigestHandleReply: {" << (reply ? reply : "<NULL>") << "}");
-
- if (reply) {
- if ((t = strchr(reply, ' ')))
- *t++ = '\0';
-
- if (*reply == '\0' || *reply == '\n')
- reply = NULL;
- }
-
- assert(replyData->auth_user_request != NULL);
- AuthUserRequest::Pointer auth_user_request = replyData->auth_user_request;
-
- /* AYJ: 2009-12-12: allow this because the digest_request pointer is purely local */
- AuthDigestUserRequest *digest_request = dynamic_cast < AuthDigestUserRequest * >(auth_user_request.getRaw());
- assert(digest_request);
-
- digest_user_h *digest_user = dynamic_cast < digest_user_h * >(auth_user_request->user());
- assert(digest_user != NULL);
-
- if (reply && (strncasecmp(reply, "ERR", 3) == 0)) {
- digest_request->credentials(AuthDigestUserRequest::Failed);
- digest_request->flags.invalid_password = 1;
-
- if (t && *t)
- digest_request->setDenyMessage(t);
- } else if (reply) {
- CvtBin(reply, digest_user->HA1);
- digest_user->HA1created = 1;
- }
-
- if (cbdataReferenceValidDone(replyData->data, &cbdata))
- replyData->handler(cbdata, NULL);
-
- replyData->auth_user_request = NULL;
-
- cbdataFree(replyData);
-}
-
/* Initialize helpers and the like for this auth scheme. Called AFTER parsing the
* config file */
void
-AuthDigestConfig::init(AuthConfig * scheme)
+Auth::Digest::Config::init(Auth::Config * scheme)
{
- if (authenticate) {
+ if (authenticateProgram) {
+ DigestFieldsInfo = httpHeaderBuildFieldsInfo(DigestAttrs, DIGEST_ENUM_END);
authenticateDigestNonceSetup();
authdigest_initialised = 1;
if (digestauthenticators == NULL)
digestauthenticators = new helper("digestauthenticator");
- digestauthenticators->cmdline = authenticate;
+ digestauthenticators->cmdline = authenticateProgram;
- digestauthenticators->childs = authenticateChildren;
+ digestauthenticators->childs.updateLimits(authenticateChildren);
digestauthenticators->ipc_type = IPC_STREAM;
helperOpenServers(digestauthenticators);
-
- CBDATA_INIT_TYPE(DigestAuthenticateStateData);
}
}
void
-AuthDigestConfig::registerWithCacheManager(void)
+Auth::Digest::Config::registerWithCacheManager(void)
{
- CacheManager::GetInstance()->
- registerAction("digestauthenticator",
- "Digest User Authenticator Stats",
- authenticateDigestStats, 0, 1);
+ Mgr::RegisterAction("digestauthenticator",
+ "Digest User Authenticator Stats",
+ authenticateDigestStats, 0, 1);
}
/* free any allocated configuration details */
void
-AuthDigestConfig::done()
+Auth::Digest::Config::done()
{
- if (authenticate)
- wordlistDestroy(&authenticate);
+ authdigest_initialised = 0;
+
+ if (digestauthenticators)
+ helperShutdown(digestauthenticators);
+
+ if (DigestFieldsInfo) {
+ httpHeaderDestroyFieldsInfo(DigestFieldsInfo, DIGEST_ENUM_END);
+ DigestFieldsInfo = NULL;
+ }
+
+ if (!shutting_down)
+ return;
+
+ delete digestauthenticators;
+ digestauthenticators = NULL;
+
+ if (authenticateProgram)
+ wordlistDestroy(&authenticateProgram);
safe_free(digestAuthRealm);
}
-AuthDigestConfig::AuthDigestConfig() : authenticateChildren(20,0,1,1)
-{
- /* TODO: move into initialisation list */
- authenticate = NULL;
- /* 5 minutes */
- nonceGCInterval = 5 * 60;
- /* 30 minutes */
- noncemaxduration = 30 * 60;
- /* 50 requests */
- noncemaxuses = 50;
- /* Not strict nonce count behaviour */
- NonceStrictness = 0;
- /* Verify nonce count */
- CheckNonceCount = 1;
-}
+Auth::Digest::Config::Config() :
+ digestAuthRealm(NULL),
+ nonceGCInterval(5*60),
+ noncemaxduration(30*60),
+ noncemaxuses(50),
+ NonceStrictness(0),
+ CheckNonceCount(1),
+ PostWorkaround(0),
+ utf8(0)
+{}
void
-AuthDigestConfig::parse(AuthConfig * scheme, int n_configured, char *param_str)
+Auth::Digest::Config::parse(Auth::Config * scheme, int n_configured, char *param_str)
{
if (strcasecmp(param_str, "program") == 0) {
- if (authenticate)
- wordlistDestroy(&authenticate);
+ if (authenticateProgram)
+ wordlistDestroy(&authenticateProgram);
- parse_wordlist(&authenticate);
+ parse_wordlist(&authenticateProgram);
- requirePathnameExists("auth_param digest program", authenticate->key);
+ requirePathnameExists("auth_param digest program", authenticateProgram->key);
} else if (strcasecmp(param_str, "children") == 0) {
authenticateChildren.parseConfig();
} else if (strcasecmp(param_str, "realm") == 0) {
}
const char *
-AuthDigestConfig::type() const
+Auth::Digest::Config::type() const
{
- return digestScheme::GetInstance()->type();
+ return Auth::Digest::Scheme::GetInstance()->type();
}
static void
authDigestNonceUserUnlink(digest_nonce_h * nonce)
{
- digest_user_h *digest_user;
+ Auth::Digest::User *digest_user;
dlink_node *link, *tmplink;
if (!nonce)
}
/* authDigestUserLinkNonce: add a nonce to a given user's struct */
-
static void
-authDigestUserLinkNonce(DigestUser * user, digest_nonce_h * nonce)
+authDigestUserLinkNonce(Auth::Digest::User * user, digest_nonce_h * nonce)
{
dlink_node *node;
- digest_user_h *digest_user;
if (!user || !nonce)
return;
- digest_user = user;
+ Auth::Digest::User *digest_user = user;
node = digest_user->nonces.head;
}
/* setup the necessary info to log the username */
-static AuthUserRequest::Pointer
-authDigestLogUsername(char *username, AuthUserRequest::Pointer auth_user_request)
+static Auth::UserRequest::Pointer
+authDigestLogUsername(char *username, Auth::UserRequest::Pointer auth_user_request)
{
assert(auth_user_request != NULL);
/* log the username */
debugs(29, 9, "authDigestLogUsername: Creating new user for logging '" << username << "'");
- digest_user_h *digest_user = new DigestUser(static_cast<AuthDigestConfig*>(AuthConfig::Find("digest")));
+ Auth::User::Pointer digest_user = new Auth::Digest::User(static_cast<Auth::Digest::Config*>(Auth::Config::Find("digest")));
/* save the credentials */
digest_user->username(username);
/* set the auth_user type */
- digest_user->auth_type = AUTH_BROKEN;
+ digest_user->auth_type = Auth::AUTH_BROKEN;
/* link the request to the user */
auth_user_request->user(digest_user);
- digest_user->lock();
- digest_user->addRequest(auth_user_request);
return auth_user_request;
}
* Decode a Digest [Proxy-]Auth string, placing the results in the passed
* Auth_user structure.
*/
-AuthUserRequest::Pointer
-AuthDigestConfig::decode(char const *proxy_auth)
+Auth::UserRequest::Pointer
+Auth::Digest::Config::decode(char const *proxy_auth)
{
const char *item;
const char *p;
debugs(29, 9, "authenticateDigestDecodeAuth: beginning");
- AuthDigestUserRequest *digest_request = new AuthDigestUserRequest();
+ Auth::Digest::UserRequest *digest_request = new Auth::Digest::UserRequest();
/* trim DIGEST from string */
String temp(proxy_auth);
while (strListGetItem(&temp, ',', &item, &ilen, &pos)) {
- if ((p = strchr(item, '=')) && (p - item < ilen))
- ilen = p++ - item;
-
- if (!strncmp(item, "username", ilen)) {
- /* white space */
-
- while (xisspace(*p))
- p++;
+ /* isolate directive name & value */
+ size_t nlen;
+ size_t vlen;
+ if ((p = (const char *)memchr(item, '=', ilen)) && (p - item < ilen)) {
+ nlen = p++ - item;
+ vlen = ilen - (p - item);
+ } else {
+ nlen = ilen;
+ vlen = 0;
+ }
- /* quote mark */
- p++;
+ StringArea keyName(item, nlen-1);
+ String value;
- safe_free(username);
- username = xstrndup(p, strchr(p, '"') + 1 - p);
+ if (vlen > 0) {
+ // see RFC 2617 section 3.2.1 and 3.2.2 for details on the BNF
- debugs(29, 9, "authDigestDecodeAuth: Found Username '" << username << "'");
- } else if (!strncmp(item, "realm", ilen)) {
- /* white space */
+ if (keyName == StringArea("domain",6) || keyName == StringArea("uri",3)) {
+ // domain is Special. Not a quoted-string, must not be de-quoted. But is wrapped in '"'
+ // BUG 3077: uri= can also be sent to us in a mangled (invalid!) form like domain
+ if (*p == '"' && *(p + vlen -1) == '"') {
+ value.limitInit(p+1, vlen-2);
+ }
+ } else if (keyName == StringArea("qop",3)) {
+ // qop is more special.
+ // On request this must not be quoted-string de-quoted. But is several values wrapped in '"'
+ // On response this is a single un-quoted token.
+ if (*p == '"' && *(p + vlen -1) == '"') {
+ value.limitInit(p+1, vlen-2);
+ } else {
+ value.limitInit(p, vlen);
+ }
+ } else if (*p == '"') {
+ if (!httpHeaderParseQuotedString(p, vlen, &value)) {
+ debugs(29, 9, HERE << "Failed to parse attribute '" << item << "' in '" << temp << "'");
+ continue;
+ }
+ } else {
+ value.limitInit(p, vlen);
+ }
+ } else {
+ debugs(29, 9, HERE << "Failed to parse attribute '" << item << "' in '" << temp << "'");
+ continue;
+ }
- while (xisspace(*p))
- p++;
+ /* find type */
+ http_digest_attr_type type = (http_digest_attr_type)httpHeaderIdByName(item, nlen, DigestFieldsInfo, DIGEST_ENUM_END);
- /* quote mark */
- p++;
+ switch (type) {
+ case DIGEST_USERNAME:
+ safe_free(username);
+ username = xstrndup(value.rawBuf(), value.size() + 1);
+ debugs(29, 9, HERE << "Found Username '" << username << "'");
+ break;
+ case DIGEST_REALM:
safe_free(digest_request->realm);
- digest_request->realm = xstrndup(p, strchr(p, '"') + 1 - p);
-
- debugs(29, 9, "authDigestDecodeAuth: Found realm '" << digest_request->realm << "'");
- } else if (!strncmp(item, "qop", ilen)) {
- /* white space */
-
- while (xisspace(*p))
- p++;
-
- if (*p == '\"')
- /* quote mark */
- p++;
+ digest_request->realm = xstrndup(value.rawBuf(), value.size() + 1);
+ debugs(29, 9, HERE << "Found realm '" << digest_request->realm << "'");
+ break;
+ case DIGEST_QOP:
safe_free(digest_request->qop);
- digest_request->qop = xstrndup(p, strcspn(p, "\" \t\r\n()<>@,;:\\/[]?={}") + 1);
-
- debugs(29, 9, "authDigestDecodeAuth: Found qop '" << digest_request->qop << "'");
- } else if (!strncmp(item, "algorithm", ilen)) {
- /* white space */
-
- while (xisspace(*p))
- p++;
-
- if (*p == '\"')
- /* quote mark */
- p++;
+ digest_request->qop = xstrndup(value.rawBuf(), value.size() + 1);
+ debugs(29, 9, HERE << "Found qop '" << digest_request->qop << "'");
+ break;
+ case DIGEST_ALGORITHM:
safe_free(digest_request->algorithm);
- digest_request->algorithm = xstrndup(p, strcspn(p, "\" \t\r\n()<>@,;:\\/[]?={}") + 1);
-
- debugs(29, 9, "authDigestDecodeAuth: Found algorithm '" << digest_request->algorithm << "'");
- } else if (!strncmp(item, "uri", ilen)) {
- /* white space */
-
- while (xisspace(*p))
- p++;
-
- /* quote mark */
- p++;
+ digest_request->algorithm = xstrndup(value.rawBuf(), value.size() + 1);
+ debugs(29, 9, HERE << "Found algorithm '" << digest_request->algorithm << "'");
+ break;
+ case DIGEST_URI:
safe_free(digest_request->uri);
- digest_request->uri = xstrndup(p, strchr(p, '"') + 1 - p);
-
- debugs(29, 9, "authDigestDecodeAuth: Found uri '" << digest_request->uri << "'");
- } else if (!strncmp(item, "nonce", ilen)) {
- /* white space */
-
- while (xisspace(*p))
- p++;
-
- /* quote mark */
- p++;
+ digest_request->uri = xstrndup(value.rawBuf(), value.size() + 1);
+ debugs(29, 9, HERE << "Found uri '" << digest_request->uri << "'");
+ break;
+ case DIGEST_NONCE:
safe_free(digest_request->nonceb64);
- digest_request->nonceb64 = xstrndup(p, strchr(p, '"') + 1 - p);
-
- debugs(29, 9, "authDigestDecodeAuth: Found nonce '" << digest_request->nonceb64 << "'");
- } else if (!strncmp(item, "nc", ilen)) {
- /* white space */
-
- while (xisspace(*p))
- p++;
-
- xstrncpy(digest_request->nc, p, 9);
+ digest_request->nonceb64 = xstrndup(value.rawBuf(), value.size() + 1);
+ debugs(29, 9, HERE << "Found nonce '" << digest_request->nonceb64 << "'");
+ break;
- debugs(29, 9, "authDigestDecodeAuth: Found noncecount '" << digest_request->nc << "'");
- } else if (!strncmp(item, "cnonce", ilen)) {
- /* white space */
-
- while (xisspace(*p))
- p++;
-
- /* quote mark */
- p++;
+ case DIGEST_NC:
+ if (value.size() != 8) {
+ debugs(29, 9, HERE << "Invalid nc '" << value << "' in '" << temp << "'");
+ }
+ xstrncpy(digest_request->nc, value.rawBuf(), value.size() + 1);
+ debugs(29, 9, HERE << "Found noncecount '" << digest_request->nc << "'");
+ break;
+ case DIGEST_CNONCE:
safe_free(digest_request->cnonce);
- digest_request->cnonce = xstrndup(p, strchr(p, '"') + 1 - p);
-
- debugs(29, 9, "authDigestDecodeAuth: Found cnonce '" << digest_request->cnonce << "'");
- } else if (!strncmp(item, "response", ilen)) {
- /* white space */
-
- while (xisspace(*p))
- p++;
-
- /* quote mark */
- p++;
+ digest_request->cnonce = xstrndup(value.rawBuf(), value.size() + 1);
+ debugs(29, 9, HERE << "Found cnonce '" << digest_request->cnonce << "'");
+ break;
+ case DIGEST_RESPONSE:
safe_free(digest_request->response);
- digest_request->response = xstrndup(p, strchr(p, '"') + 1 - p);
+ digest_request->response = xstrndup(value.rawBuf(), value.size() + 1);
+ debugs(29, 9, HERE << "Found response '" << digest_request->response << "'");
+ break;
- debugs(29, 9, "authDigestDecodeAuth: Found response '" << digest_request->response << "'");
+ default:
+ debugs(29, 3, HERE << "Unknown attribute '" << item << "' in '" << temp << "'");
+ break;
}
}
* correct values - 400/401/407
*/
- /* first the NONCE count */
+ /* 2069 requirements */
- if (digest_request->cnonce && strlen(digest_request->nc) != 8) {
- debugs(29, 4, "authenticateDigestDecode: nonce count length invalid");
+ /* do we have a username ? */
+ if (!username || username[0] == '\0') {
+ debugs(29, 2, HERE << "Empty or not present username");
return authDigestLogUsername(username, digest_request);
}
- /* now the nonce */
- nonce = authenticateDigestNonceFindNonce(digest_request->nonceb64);
-
- if (!nonce) {
- /* we couldn't find a matching nonce! */
- debugs(29, 4, "authenticateDigestDecode: Unexpected or invalid nonce received");
+ /* Sanity check of the username.
+ * " can not be allowed in usernames until * the digest helper protocol
+ * have been redone
+ */
+ if (strchr(username, '"')) {
+ debugs(29, 2, HERE << "Unacceptable username '" << username << "'");
return authDigestLogUsername(username, digest_request);
}
- digest_request->nonce = nonce;
- authDigestNonceLink(nonce);
-
- /* check the qop is what we expected. Note that for compatability with
- * RFC 2069 we should support a missing qop. Tough. */
+ /* do we have a realm ? */
+ if (!digest_request->realm || digest_request->realm[0] == '\0') {
+ debugs(29, 2, HERE << "Empty or not present realm");
+ return authDigestLogUsername(username, digest_request);
+ }
- if (digest_request->qop && strcmp(digest_request->qop, QOP_AUTH) != 0) {
- /* we received a qop option we didn't send */
- debugs(29, 4, "authenticateDigestDecode: Invalid qop option received");
+ /* and a nonce? */
+ if (!digest_request->nonceb64 || digest_request->nonceb64[0] == '\0') {
+ debugs(29, 2, HERE << "Empty or not present nonce");
return authDigestLogUsername(username, digest_request);
}
/* we can't check the URI just yet. We'll check it in the
- * authenticate phase */
+ * authenticate phase, but needs to be given */
+ if (!digest_request->uri || digest_request->uri[0] == '\0') {
+ debugs(29, 2, HERE << "Missing URI field");
+ return authDigestLogUsername(username, digest_request);
+ }
/* is the response the correct length? */
-
if (!digest_request->response || strlen(digest_request->response) != 32) {
- debugs(29, 4, "authenticateDigestDecode: Response length invalid");
+ debugs(29, 2, HERE << "Response length invalid");
return authDigestLogUsername(username, digest_request);
}
- /* do we have a username ? */
- if (!username || username[0] == '\0') {
- debugs(29, 4, "authenticateDigestDecode: Empty or not present username");
+ /* check the algorithm is present and supported */
+ if (!digest_request->algorithm)
+ digest_request->algorithm = xstrndup("MD5", 4);
+ else if (strcmp(digest_request->algorithm, "MD5")
+ && strcmp(digest_request->algorithm, "MD5-sess")) {
+ debugs(29, 2, HERE << "invalid algorithm specified!");
return authDigestLogUsername(username, digest_request);
}
- /* check that we're not being hacked / the username hasn't changed */
- if (nonce->user && strcmp(username, nonce->user->username())) {
- debugs(29, 4, "authenticateDigestDecode: Username for the nonce does not equal the username for the request");
- return authDigestLogUsername(username, digest_request);
+ /* 2617 requirements, indicated by qop */
+ if (digest_request->qop) {
+
+ /* check the qop is what we expected. */
+ if (strcmp(digest_request->qop, QOP_AUTH) != 0) {
+ /* we received a qop option we didn't send */
+ debugs(29, 2, HERE << "Invalid qop option received");
+ return authDigestLogUsername(username, digest_request);
+ }
+
+ /* check cnonce */
+ if (!digest_request->cnonce || digest_request->cnonce[0] == '\0') {
+ debugs(29, 2, HERE << "Missing cnonce field");
+ return authDigestLogUsername(username, digest_request);
+ }
+
+ /* check nc */
+ if (strlen(digest_request->nc) != 8 || strspn(digest_request->nc, "0123456789abcdefABCDEF") != 8) {
+ debugs(29, 2, HERE << "invalid nonce count");
+ return authDigestLogUsername(username, digest_request);
+ }
+ } else {
+ /* cnonce and nc both require qop */
+ if (digest_request->cnonce || digest_request->nc) {
+ debugs(29, 2, HERE << "missing qop!");
+ return authDigestLogUsername(username, digest_request);
+ }
}
- /* if we got a qop, did we get a cnonce or did we get a cnonce wihtout a qop? */
- if ((digest_request->qop && !digest_request->cnonce)
- || (!digest_request->qop && digest_request->cnonce)) {
- debugs(29, 4, "authenticateDigestDecode: qop without cnonce, or vice versa!");
+ /** below nonce state dependent **/
+
+ /* now the nonce */
+ nonce = authenticateDigestNonceFindNonce(digest_request->nonceb64);
+ if (!nonce) {
+ /* we couldn't find a matching nonce! */
+ debugs(29, 2, HERE << "Unexpected or invalid nonce received");
+ if (digest_request->user() != NULL)
+ digest_request->user()->credentials(Auth::Failed);
return authDigestLogUsername(username, digest_request);
}
- /* check the algorithm is present and supported */
- if (!digest_request->algorithm)
- digest_request->algorithm = xstrndup("MD5", 4);
- else if (strcmp(digest_request->algorithm, "MD5")
- && strcmp(digest_request->algorithm, "MD5-sess")) {
- debugs(29, 4, "authenticateDigestDecode: invalid algorithm specified!");
+ digest_request->nonce = nonce;
+ authDigestNonceLink(nonce);
+
+ /* check that we're not being hacked / the username hasn't changed */
+ if (nonce->user && strcmp(username, nonce->user->username())) {
+ debugs(29, 2, HERE << "Username for the nonce does not equal the username for the request");
return authDigestLogUsername(username, digest_request);
}
/* we don't send or parse opaques. Ok so we're flexable ... */
/* find the user */
- digest_user_h *digest_user;
+ Auth::Digest::User *digest_user;
- AuthUser *auth_user;
+ Auth::User::Pointer auth_user;
if ((auth_user = authDigestUserFindUsername(username)) == NULL) {
/* the user doesn't exist in the username cache yet */
- debugs(29, 9, "authDigestDecodeAuth: Creating new digest user '" << username << "'");
- digest_user = new DigestUser(this);
+ debugs(29, 9, HERE << "Creating new digest user '" << username << "'");
+ digest_user = new Auth::Digest::User(this);
/* auth_user is a parent */
auth_user = digest_user;
/* save the username */
digest_user->username(username);
/* set the user type */
- digest_user->auth_type = AUTH_DIGEST;
+ digest_user->auth_type = Auth::AUTH_DIGEST;
/* this auth_user struct is the one to get added to the
* username cache */
/* store user in hash's */
*/
authDigestUserLinkNonce(digest_user, nonce);
} else {
- debugs(29, 9, "authDigestDecodeAuth: Found user '" << username << "' in the user cache as '" << auth_user << "'");
- digest_user = static_cast < digest_user_h * >(auth_user);
+ debugs(29, 9, HERE << "Found user '" << username << "' in the user cache as '" << auth_user << "'");
+ digest_user = static_cast<Auth::Digest::User *>(auth_user.getRaw());
xfree(username);
}
/*link the request and the user */
assert(digest_request != NULL);
- digest_user->lock();
digest_request->user(digest_user);
-
- digest_user->addRequest(digest_request);
-
- debugs(29, 9, "username = '" << digest_user->username() << "'\nrealm = '" <<
+ debugs(29, 9, HERE << "username = '" << digest_user->username() << "'\nrealm = '" <<
digest_request->realm << "'\nqop = '" << digest_request->qop <<
"'\nalgorithm = '" << digest_request->algorithm << "'\nuri = '" <<
digest_request->uri << "'\nnonce = '" << digest_request->nonceb64 <<
return digest_request;
}
-
-/* send the initial data to a digest authenticator module */
-void
-AuthDigestUserRequest::module_start(RH * handler, void *data)
-{
- DigestAuthenticateStateData *r = NULL;
- char buf[8192];
- digest_user_h *digest_user;
- assert(user()->auth_type == AUTH_DIGEST);
- digest_user = dynamic_cast<digest_user_h*>(user());
- assert(digest_user != NULL);
- debugs(29, 9, "authenticateStart: '\"" << digest_user->username() << "\":\"" << realm << "\"'");
-
- if (static_cast<AuthDigestConfig*>(AuthConfig::Find("digest"))->authenticate == NULL) {
- handler(data, NULL);
- return;
- }
-
- r = cbdataAlloc(DigestAuthenticateStateData);
- r->handler = handler;
- r->data = cbdataReference(data);
- r->auth_user_request = static_cast<AuthUserRequest*>(this);
- if (static_cast<AuthDigestConfig*>(AuthConfig::Find("digest"))->utf8) {
- char userstr[1024];
- latin1_to_utf8(userstr, sizeof(userstr), digest_user->username());
- snprintf(buf, 8192, "\"%s\":\"%s\"\n", userstr, realm);
- } else {
- snprintf(buf, 8192, "\"%s\":\"%s\"\n", digest_user->username(), realm);
- }
-
- helperSubmit(digestauthenticators, buf, authenticateDigestHandleReply, r);
-}
-
-DigestUser::DigestUser (AuthConfig *aConfig) : AuthUser (aConfig), HA1created (0)
-{}
-
-AuthDigestUserRequest::CredentialsState
-AuthDigestUserRequest::credentials() const
-{
- return credentials_ok;
-}
-
-void
-AuthDigestUserRequest::credentials(CredentialsState newCreds)
-{
- credentials_ok = newCreds;
-}
-
-AuthDigestUserRequest::AuthDigestUserRequest() : nonceb64(NULL) ,cnonce(NULL) ,realm(NULL),
- pszPass(NULL) ,algorithm(NULL) ,pszMethod(NULL),
- qop(NULL) ,uri(NULL) ,response(NULL),
- nonce(NULL),
- credentials_ok (Unchecked)
-{}
-
-/** delete the digest request structure. Does NOT delete related structures */
-AuthDigestUserRequest::~AuthDigestUserRequest()
-{
- safe_free (nonceb64);
- safe_free (cnonce);
- safe_free (realm);
- safe_free (pszPass);
- safe_free (algorithm);
- safe_free (pszMethod);
- safe_free (qop);
- safe_free (uri);
- safe_free (response);
-
- if (nonce)
- authDigestNonceUnlink(nonce);
-}