--------------------- PatchSet 1067 Date: 2001/01/04 15:17:47 Author: rbcollins Branch: auth_digest Tag: (none) Log: more rfc compliance things. Tests for user data sensabilities. Guaranteed Unique nonce generation. Nonce expiration. Members: src/cf.data.pre:1.1.1.3.4.1.2.18.2.4.2.9->1.1.1.3.4.1.2.18.2.4.2.10 src/auth/digest/auth_digest.c:1.1.2.8->1.1.2.9 src/auth/digest/auth_digest.h:1.1.2.4->1.1.2.5 Index: squid/src/cf.data.pre =================================================================== RCS file: /cvsroot/squid-sf//squid/src/cf.data.pre,v retrieving revision 1.1.1.3.4.1.2.18.2.4.2.9 retrieving revision 1.1.1.3.4.1.2.18.2.4.2.10 diff -u -r1.1.1.3.4.1.2.18.2.4.2.9 -r1.1.1.3.4.1.2.18.2.4.2.10 --- squid/src/cf.data.pre 4 Jan 2001 12:17:08 -0000 1.1.1.3.4.1.2.18.2.4.2.9 +++ squid/src/cf.data.pre 4 Jan 2001 15:17:47 -0000 1.1.1.3.4.1.2.18.2.4.2.10 @@ -1,6 +1,6 @@ # -# $Id: cf.data.pre,v 1.1.1.3.4.1.2.18.2.4.2.9 2001/01/04 12:17:08 rbcollins Exp $ +# $Id: cf.data.pre,v 1.1.1.3.4.1.2.18.2.4.2.10 2001/01/04 15:17:47 rbcollins Exp $ # # # SQUID Internet Object Cache http://squid.nlanr.net/Squid/ @@ -1213,12 +1213,16 @@ Specifies the interval that nonces that have been issued to client_agent's are checked for validity. + "nonce_max_duration" timeinterval + Specifies the maximum length of time a given nonce will be valid for. + NOCOMMENT_START #Recommended minimum configuration: #auth_param digest program auth_param digest children 5 auth_paran digest realm Squid proxy-caching web server auth_param digest nonce_garbage_interval 5 minutes +auth_param digest nonce_max_duration 30 minutes #auth_param basic program auth_param basic children 5 auth_param basic realm Squid proxy-caching web server Index: squid/src/auth/digest/auth_digest.c =================================================================== RCS file: /cvsroot/squid-sf//squid/src/auth/digest/auth_digest.c,v retrieving revision 1.1.2.8 retrieving revision 1.1.2.9 diff -u -r1.1.2.8 -r1.1.2.9 --- squid/src/auth/digest/auth_digest.c 3 Jan 2001 00:20:28 -0000 1.1.2.8 +++ squid/src/auth/digest/auth_digest.c 4 Jan 2001 15:17:47 -0000 1.1.2.9 @@ -78,6 +78,9 @@ MemPool *digest_request_pool = NULL; MemPool *digest_nonce_pool = NULL; +/* strings */ +#define QOP_AUTH "auth" + /* * * Nonce Functions @@ -86,6 +89,24 @@ static void authenticateDigestNonceCacheCleanup (void *data); static digest_nonce_h *authenticateDigestNonceFindNonce(const char *nonceb64); +digest_nonce_h * authenticateDigestNonceNew(); +void authenticateDigestNonceDelete(digest_nonce_h *nonce); +void authenticateDigestNonceSetup(); +void authenticateDigestNonceShutdown(); +void authenticateDigestNonceReconfigure(); +const char *authenticateDigestNonceNonceb64(digest_nonce_h *nonce); +int authDigestNonceIsValid(digest_nonce_h *nonce, char nc[9]); +void authDigestNonceEncode(digest_nonce_h *nonce); + +void +authDigestNonceEncode(digest_nonce_h *nonce) +{ + if (!nonce) + return; + if (nonce->nonceb64) + xfree(nonce->nonceb64); + nonce->nonceb64=xstrdup(base64_encode_bin((char *)&(nonce->noncedata),sizeof(digest_nonce_data))); +} digest_nonce_h * authenticateDigestNonceNew() @@ -93,21 +114,23 @@ digest_nonce_h *newnonce = memPoolAlloc(digest_nonce_pool); digest_nonce_h *temp; + /* TODO: create from unique data - get rid of random()*10*/ /* create a new nonce */ - newnonce->noncedata.creationtime=squid_curtime+random()*10; - if (newnonce->nonceb64) - xfree(newnonce->nonceb64); - newnonce->nonceb64=xstrdup(base64_encode_bin((char *)&(newnonce->noncedata),sizeof(digest_nonce_data))); + newnonce->nc=0; + newnonce->flags.valid=1; + newnonce->noncedata.self=newnonce; + newnonce->noncedata.creationtime=current_time.tv_sec; + + authDigestNonceEncode(newnonce); /* loop until we get a unique nonce. The nonce creation must have a random factor*/ while ((temp=authenticateDigestNonceFindNonce(newnonce->nonceb64))) { /* create a new nonce */ - newnonce->noncedata.creationtime=squid_curtime; - if (newnonce->nonceb64) - xfree(newnonce->nonceb64); - newnonce->nonceb64=xstrdup(base64_encode_bin((char *)&(newnonce->noncedata),sizeof(digest_nonce_data))); + newnonce->noncedata.creationtime=current_time.tv_sec; + authDigestNonceEncode(newnonce); } hash_join(digest_nonce_cache, (hash_link *) newnonce); + debug(29,6)("authenticateDigestNonceNew: created nonce %0p at %d\n", newnonce, newnonce->noncedata.creationtime); return newnonce; } @@ -202,6 +225,44 @@ return nonce; } +int +authDigestNonceIsValid(digest_nonce_h *nonce,char nc[9]) +{ + int intnc; + /* do we have a nonce ? */ + if (!nonce) + return 0; + /* has it's max duration expired? */ + if (nonce->noncedata.creationtime + digestConfig->noncemaxduration < current_time.tv_sec) + { + debug (29,6)("authDigestNonceIsValid: Nonce is too old. %d %d %d\n", nonce->noncedata.creationtime,digestConfig->noncemaxduration , current_time.tv_sec); + nonce->flags.valid=0; + return 0; + } + intnc=atoi(nc); + if (intnc != nonce->nc+1) + { + debug (29,6)("authDigestNonceIsValid: Nonce count doesn't match\n"); + nonce->flags.valid=0; + return 0; + } + if (nonce->nc>99999997) + { + debug (29,6)("authDigestNonceIsValid: Nonce count about to overflow\n"); + nonce->flags.valid=0; + return 0; + } + /* has it already been invalidated ? */ + if (!nonce->flags.valid) + { + debug (29,6)("authDigestNonceIsValid: Nonce already invalidated\n"); + return 0; + } + /* seems ok */ + return -1; +} + + /* USER related functions */ @@ -506,7 +567,7 @@ if (digestConfig->authenticate){ debug(29, 5) ("authenticateFixHeader: Sending type:%d header: 'Digest realm=\"%s\", nonce=\"%s\"\n",type,digestConfig->digestAuthRealm,authenticateDigestNonceNonceb64(nonce)); /* in the future, for WWW auth we may want to support the domain entry */ - httpHeaderPutStrf(&rep->header, type, "Digest realm=\"%s\", nonce=\"%s\", qop=\"auth\"",digestConfig->digestAuthRealm,authenticateDigestNonceNonceb64(nonce)); + httpHeaderPutStrf(&rep->header, type, "Digest realm=\"%s\", nonce=\"%s\", qop=\"%s\"",digestConfig->digestAuthRealm,authenticateDigestNonceNonceb64(nonce),QOP_AUTH); } } @@ -635,6 +696,10 @@ memset(scheme->scheme_data, 0, sizeof(auth_digest_config)); digestConfig=scheme->scheme_data; digestConfig->authenticateChildren=5; + /* 5 minutes */ + digestConfig->nonceGCInterval=5*60; + /* 30 minutes */ + digestConfig->noncemaxduration=30*60; } digestConfig=scheme->scheme_data; if (strcasecmp(param_str,"program")==0) @@ -654,6 +719,10 @@ { parse_time_t(&digestConfig->nonceGCInterval); } + else if (strcasecmp(param_str,"nonce_max_duration")==0) + { + parse_time_t(&digestConfig->noncemaxduration); + } else { debug(28,0)("unrecognised digest auth scheme parameter '%s'\n",param_str); @@ -678,6 +747,34 @@ return NULL; } +/* setup the necessary info to log the username */ +void +authDigestLogUsername(auth_user_request_t *auth_user_request, char * username) +{ + auth_user_t * auth_user; + digest_user_h *digest_user; + dlink_node *node; + + /* log the username */ + debug(29,6)("authBasicDecodeAuth: Creating new user for logging '%s'\n",username); + /* new auth_user */ + auth_user=authenticateAuthUserNew("digest"); + /* new scheme data */ + digest_user=authDigestUserNew(); + /* save the credentials */ + digest_user->username=username; + /* link the scheme data in */ + auth_user->scheme_data=digest_user; + /* set the auth_user type */ + auth_user->auth_type=AUTH_BROKEN; + /* link the request to the user */ + auth_user_request->auth_user=auth_user; + /* lock for the auth_user_request link */ + authenticateAuthUserLock(auth_user); + node=dlinkNodeNew(); + dlinkAdd(auth_user_request, node, &auth_user->requests); +} + /* * Decode a Digest [Proxy-]Auth string, placing the results in the passed * Auth_user structure. @@ -811,36 +908,115 @@ } } stringClean(&temp); + + /* now we validate the data given to us */ + + /* TODO: on invalid parameters we should return 400, not 407. Find some clean way + * of doing this. perhaps return a valid struct, and set the direction to clientwards + * combined with a change to the clientwards handling code (ie let the clientwards + * call set the error type (but limited to known correct values - 400/401/407 */ + + /* first the NONCE count */ + if (digest_request->cnonce && strlen(digest_request->nc)!=8) + { + debug (29,6)("authenticateDigestDecode: nonce count length invalid\n"); + authDigestLogUsername(auth_user_request, username); + + /* we don't need the scheme specific data anymore*/ + authDigestRequestDelete(digest_request); + auth_user_request->scheme_data=NULL; + return; + } + + /* now the nonce */ nonce=authenticateDigestNonceFindNonce(digest_request->nonceb64); - if (nonce==NULL) + if ((nonce==NULL) || !(authDigestNonceIsValid(nonce, digest_request->nc))) { /* we couldn't find a matching nonce! */ - debug (29,6)("authenticateDigestDecode: Unexpected nonce recieved\n"); - /* log the username */ - debug(29,6)("authBasicDecodeAuth: Creating new user for logging '%s'\n",username); - /* new auth_user */ - auth_user=authenticateAuthUserNew("digest"); - /* new scheme data */ - digest_user=authDigestUserNew(); - /* save the credentials */ - digest_user->username=username; - /* link the scheme data in */ - auth_user->scheme_data=digest_user; - /* set the auth_user type */ - auth_user->auth_type=AUTH_BROKEN; - /* link the request to the user */ - auth_user_request->auth_user=auth_user; - /* lock for the auth_user_request link */ - authenticateAuthUserLock(auth_user); - node=dlinkNodeNew(); - dlinkAdd(auth_user_request, node, &auth_user->requests); - /* we don't need the scheme specific data */ + debug (29,6)("authenticateDigestDecode: Unexpected or invalid nonce recieved\n"); + authDigestLogUsername(auth_user_request, username); + + /* we don't need the scheme specific data anymore*/ authDigestRequestDelete(digest_request); auth_user_request->scheme_data=NULL; return; } digest_request->nonce=nonce; + /* increment the nonce count */ + nonce->nc++; + + /* check the qop is what we expected */ + if (digest_request->qop && strcmp(digest_request->qop, QOP_AUTH)) + { + /* we recieved a qop option we didn't send */ + debug (29,6)("authenticateDigestDecode: Invalid qop option recieved\n"); + authDigestLogUsername(auth_user_request, username); + + /* we don't need the scheme specific data anymore*/ + authDigestRequestDelete(digest_request); + auth_user_request->scheme_data=NULL; + return; + } + + /* we can't check the URI just yet. We'll check it in the authenticate phase */ + + /* is the response the correct length? */ + + if (!digest_request->response || strlen(digest_request->response)!=32) + { + debug (29,6)("authenticateDigestDecode: Response length invalid\n"); + authDigestLogUsername(auth_user_request, username); + + /* we don't need the scheme specific data anymore*/ + authDigestRequestDelete(digest_request); + auth_user_request->scheme_data=NULL; + return; + } + + /* do we have a username ? */ + if (!username || username[0]=='\0') + { + debug (29,6)("authenticateDigestDecode: Empty or not present username\n"); + authDigestLogUsername(auth_user_request, username); + + /* we don't need the scheme specific data anymore*/ + authDigestRequestDelete(digest_request); + auth_user_request->scheme_data=NULL; + return; + } + + /* 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)) + { + debug (29,6)("authenticateDigestDecode: qop without cnonce, or vice versa!\n"); + authDigestLogUsername(auth_user_request, username); + + /* we don't need the scheme specific data anymore*/ + authDigestRequestDelete(digest_request); + auth_user_request->scheme_data=NULL; + return; + } + + /* check the algorithm is present and supported*/ + if (digest_request->algorithm + && strcmp(digest_request->algorithm,"MD5") + && strcmp(digest_request->algorithm,"MD5-sess")) + { + debug (29,6)("authenticateDigestDecode: invalid algorithm specified!\n"); + authDigestLogUsername(auth_user_request, username); + + /* we don't need the scheme specific data anymore*/ + authDigestRequestDelete(digest_request); + auth_user_request->scheme_data=NULL; + return; + } + + /* the method we'll check at the authenticate step as well */ + + + /* we don't send or parse opaques. Ok so we're flexable ... */ /* find the user */ Index: squid/src/auth/digest/auth_digest.h =================================================================== RCS file: /cvsroot/squid-sf//squid/src/auth/digest/auth_digest.h,v retrieving revision 1.1.2.4 retrieving revision 1.1.2.5 diff -u -r1.1.2.4 -r1.1.2.5 --- squid/src/auth/digest/auth_digest.h 3 Jan 2001 00:20:28 -0000 1.1.2.4 +++ squid/src/auth/digest/auth_digest.h 4 Jan 2001 15:17:48 -0000 1.1.2.5 @@ -55,16 +55,25 @@ digest_nonce_h *nonce; }; -/* nonce privates */ +/* data to be encoded into the nonce's b64 representation */ struct _digest_nonce_data { time_t creationtime; + /* in memory address of the nonce struct (similar purpose to an ETag) */ + digest_nonce_h *self; }; +/* the nonce structure we'll pass around */ struct _digest_nonce_h { /* the first two items are (hash_link) */ char *nonceb64; digest_nonce_h *next; digest_nonce_data noncedata; + /* number of uses we've seen of this nonce */ + long nc; + /* has this nonce been invalidated ? */ + struct { + unsigned int valid:1; + } flags; }; /* configuration runtime data */ @@ -73,6 +82,7 @@ char *digestAuthRealm; wordlist *authenticate; time_t nonceGCInterval; + time_t noncemaxduration; }; typedef struct _auth_digest_config auth_digest_config;