---------------------
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 <uncomment and complete this line>
 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 <uncomment and complete this line>
 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;
