--------------------- PatchSet 1369 Date: 2001/01/23 22:54:53 Author: rbcollins Branch: auth_rewrite Tag: (none) Log: merged in digest Members: configure.in:1.1.1.3.10.17.2.16->1.1.1.3.10.17.2.17 include/md5.h:1.1.1.1->1.1.1.1.26.1 lib/Makefile.in:1.1.1.2.10.2.2.4->1.1.1.2.10.2.2.5 lib/md5.c:1.1.1.2->1.1.1.2.22.1 src/cf.data.pre:1.1.1.3.4.1.2.18.2.18->1.1.1.3.4.1.2.18.2.19 src/enums.h:1.1.1.3.12.15.2.11->1.1.1.3.12.15.2.12 src/auth/basic/auth_basic.c:1.1.2.29->1.1.2.30 src/auth/digest/Makefile.in:1.1->1.1.20.1 src/auth/digest/auth_digest.c:1.1->1.1.20.1 src/auth/digest/auth_digest.h:1.1->1.1.20.1 src/auth/digest/helpers/Makefile.in:1.1->1.1.8.1 src/auth/digest/helpers/password/Makefile.in:1.1->1.1.8.1 src/auth/digest/helpers/password/digest_pw_auth.c:1.1->1.1.8.1 src/auth/ntlm/auth_ntlm.c:1.1.2.29->1.1.2.30 Index: squid/configure.in =================================================================== RCS file: /cvsroot/squid-sf//squid/configure.in,v retrieving revision 1.1.1.3.10.17.2.16 retrieving revision 1.1.1.3.10.17.2.17 diff -u -r1.1.1.3.10.17.2.16 -r1.1.1.3.10.17.2.17 --- squid/configure.in 23 Jan 2001 10:14:14 -0000 1.1.1.3.10.17.2.16 +++ squid/configure.in 23 Jan 2001 22:54:53 -0000 1.1.1.3.10.17.2.17 @@ -3,13 +3,13 @@ dnl dnl Duane Wessels, wessels@nlanr.net, February 1996 (autoconf v2.9) dnl -dnl $Id: configure.in,v 1.1.1.3.10.17.2.16 2001/01/23 10:14:14 rbcollins Exp $ +dnl $Id: configure.in,v 1.1.1.3.10.17.2.17 2001/01/23 22:54:53 rbcollins Exp $ dnl dnl dnl AC_INIT(src/main.c) AC_CONFIG_HEADER(include/autoconf.h) -AC_REVISION($Revision: 1.1.1.3.10.17.2.16 $)dnl +AC_REVISION($Revision: 1.1.1.3.10.17.2.17 $)dnl AC_PREFIX_DEFAULT(/usr/local/squid) AC_CONFIG_AUX_DIR(cfgaux) @@ -785,6 +785,32 @@ fi AC_SUBST(NTLM_AUTH_HELPERS) +dnl Select digest auth scheme helpers to build +DIGEST_AUTH_HELPERS= +AC_ARG_ENABLE(digest-auth-helpers, +[ --enable-digest-auth-helpers=\"list of helpers\" + This option selects which digest scheme authentication + helpers to build and install as part of the normal build + process. For a list of available modules see the + src/auth/digest/helpers directory.], +[ case "$enableval" in + yes) + for helper in $srcdir/src/auth/digest/helpers/*; do + if test -f $helper/Makefile.in; then + DIGEST_AUTH_HELPERS="$DIGEST_AUTH_HELPERS `basename $helper`" + fi + done + ;; + no) + ;; + *) + DIGEST_AUTH_HELPERS="`echo $enableval| sed -e 's/,/ /g;s/ */ /g'`" + esac +]) +if test -n "$DIGEST_AUTH_HELPERS"; then + echo "Digest auth helpers built: $DIGEST_AUTH_HELPERS" +fi +AC_SUBST(DIGEST_AUTH_HELPERS) dnl Disable "unlinkd" code AC_ARG_ENABLE(unlinkd, Index: squid/include/md5.h =================================================================== RCS file: /cvsroot/squid-sf//squid/include/md5.h,v retrieving revision 1.1.1.1 retrieving revision 1.1.1.1.26.1 diff -u -r1.1.1.1 -r1.1.1.1.26.1 --- squid/include/md5.h 26 Jan 2000 03:21:47 -0000 1.1.1.1 +++ squid/include/md5.h 23 Jan 2001 22:54:53 -0000 1.1.1.1.26.1 @@ -1,5 +1,5 @@ /* - * $Id: md5.h,v 1.1.1.1 2000/01/26 03:21:47 hno Exp $ + * $Id: md5.h,v 1.1.1.1.26.1 2001/01/23 22:54:53 rbcollins Exp $ */ #ifndef MD5_H @@ -37,7 +37,7 @@ } MD5_CTX; void MD5Init(MD5_CTX *); -void MD5Update(MD5_CTX *, unsigned char *, unsigned int); +void MD5Update(MD5_CTX *, const unsigned char *, unsigned int); void MD5Final(unsigned char[16], MD5_CTX *); #define MD5_DIGEST_CHARS 16 Index: squid/lib/Makefile.in =================================================================== RCS file: /cvsroot/squid-sf//squid/lib/Attic/Makefile.in,v retrieving revision 1.1.1.2.10.2.2.4 retrieving revision 1.1.1.2.10.2.2.5 diff -u -r1.1.1.2.10.2.2.4 -r1.1.1.2.10.2.2.5 --- squid/lib/Makefile.in 7 Jan 2001 02:44:55 -0000 1.1.1.2.10.2.2.4 +++ squid/lib/Makefile.in 23 Jan 2001 22:54:53 -0000 1.1.1.2.10.2.2.5 @@ -1,5 +1,5 @@ # -# $Id: Makefile.in,v 1.1.1.2.10.2.2.4 2001/01/07 02:44:55 rbcollins Exp $ +# $Id: Makefile.in,v 1.1.1.2.10.2.2.5 2001/01/23 22:54:53 rbcollins Exp $ # prefix = @prefix@ top_srcdir = @top_srcdir@ @@ -24,6 +24,7 @@ UTILOBJS = rfc1123.o \ rfc1738.o \ rfc1035.o \ + rfc2617.o \ util.o \ getfullhostname.o \ base64.o \ Index: squid/lib/md5.c =================================================================== RCS file: /cvsroot/squid-sf//squid/lib/md5.c,v retrieving revision 1.1.1.2 retrieving revision 1.1.1.2.22.1 diff -u -r1.1.1.2 -r1.1.1.2.22.1 --- squid/lib/md5.c 26 Jan 2000 03:25:00 -0000 1.1.1.2 +++ squid/lib/md5.c 23 Jan 2001 22:54:53 -0000 1.1.1.2.22.1 @@ -1,5 +1,5 @@ /* - * $Id: md5.c,v 1.1.1.2 2000/01/26 03:25:00 hno Exp $ + * $Id: md5.c,v 1.1.1.2.22.1 2001/01/23 22:54:53 rbcollins Exp $ */ /* taken from RFC-1321/Appendix A.3 */ @@ -63,9 +63,9 @@ #define S43 15 #define S44 21 -static void MD5Transform(u_num32[4], unsigned char[64]); +static void MD5Transform(u_num32[4], const unsigned char[64]); static void Encode(unsigned char *, u_num32 *, unsigned int); -static void Decode(u_num32 *, unsigned char *, unsigned int); +static void Decode(u_num32 *, const unsigned char *, unsigned int); #if HAVE_MEMCPY #define MD5_memcpy(to,from,count) memcpy(to,from,count) @@ -145,7 +145,7 @@ * processing another message block, and updating the context. */ void -MD5Update(MD5_CTX * context, unsigned char *input, unsigned int inputLen) +MD5Update(MD5_CTX * context, const unsigned char *input, unsigned int inputLen) { unsigned int i, index, partLen; @@ -213,7 +213,7 @@ * MD5 basic transformation. Transforms state based on block. */ static void -MD5Transform(u_num32 state[4], unsigned char block[64]) +MD5Transform(u_num32 state[4], const unsigned char block[64]) { u_num32 a = state[0], b = state[1], c = state[2], d = state[3], x[16]; @@ -324,7 +324,7 @@ * multiple of 4. */ static void -Decode(u_num32 * output, unsigned char *input, unsigned int len) +Decode(u_num32 * output, const unsigned char *input, unsigned int len) { unsigned int i, j; 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.18 retrieving revision 1.1.1.3.4.1.2.18.2.19 diff -u -r1.1.1.3.4.1.2.18.2.18 -r1.1.1.3.4.1.2.18.2.19 --- squid/src/cf.data.pre 23 Jan 2001 10:14:20 -0000 1.1.1.3.4.1.2.18.2.18 +++ squid/src/cf.data.pre 23 Jan 2001 22:54:53 -0000 1.1.1.3.4.1.2.18.2.19 @@ -1,6 +1,6 @@ # -# $Id: cf.data.pre,v 1.1.1.3.4.1.2.18.2.18 2001/01/23 10:14:20 rbcollins Exp $ +# $Id: cf.data.pre,v 1.1.1.3.4.1.2.18.2.19 2001/01/23 22:54:53 rbcollins Exp $ # # # SQUID Web Proxy Cache http://www.squid-cache.org/ @@ -1141,6 +1141,11 @@ basic) then either put basic first, or disable the other schemes (by commenting out their program entry). + Once an authentication scheme is fully configured, it can only be shutdown + by shutting squid down and restarting. Changes can be made on the fly and + activated with a reconfigure. I.E. You can change to a different helper, + but not unconfigure the helper completely. + === Parameters for the basic scheme follow. === "program" cmdline @@ -1183,6 +1188,51 @@ If you are using such a system, you will be vulnerable to replay attacks unless you also enable the IP ttl is strict option. + === Parameters for the digest scheme follow === + + "program" cmdline + Specify the command for the external authenticator. Such a + program reads a line containing "username":"realm" and replies + with the appropriate H(A1) value base64 encoded. See rfc 2616 for + the definition of H(A1). If you use an authenticator, + make sure you have 1 acl of type proxy_auth. By default, + authentication is not used. + + If you want to use build a authenticator, + jump over to the ../digest_auth_modules directory and choose the + authenticator to use. It it's directory type + % make + % make install + + Then, set this line to something like + + auth_param digest program @DEFAULT_PREFIX@/bin/digest_auth_pw @DEFAULT_PREFIX@/etc/digpass + + + "children" numberofchildren + The number of authenticator processes to spawn (no default). If you + start too few Squid will have to wait for them to process a backlog + of H(A1) calculations, slowing it down. When the H(A1) calculations + are done via a (slow) network you are likely to need lots of + authenticator processes. + auth_param digest children 5 + + "realm" realmstring + Specifies the realm name which is to be reported to the client for + the digest proxy authentication scheme (part of the text the user will + see when prompted their username and password). There is no default. + auth_param digest realm Squid proxy-caching web server + + "nonce_garbage_interval" timeinterval + 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. + + "nonce_max_count" number + Specifies the maximum number of times a given nonce can be used. + === NTLM scheme options follow === "program" cmdline @@ -1219,10 +1269,16 @@ NOCOMMENT_START #Recommended minimum configuration: +#auth_param digest program +#auth_param digest children 5 +#auth_param 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 digest nonce_max_count 50 #auth_param ntlm program -auth_param ntlm children 5 -auth_param ntlm max_challenge_reuses 0 -auth_param ntlm max_challenge_lifetime 2 minutes +#auth_param ntlm children 5 +#auth_param ntlm max_challenge_reuses 0 +#auth_param ntlm max_challenge_lifetime 2 minutes #auth_param basic program auth_param basic children 5 auth_param basic realm Squid proxy-caching web server Index: squid/src/enums.h =================================================================== RCS file: /cvsroot/squid-sf//squid/src/enums.h,v retrieving revision 1.1.1.3.12.15.2.11 retrieving revision 1.1.1.3.12.15.2.12 diff -u -r1.1.1.3.12.15.2.11 -r1.1.1.3.12.15.2.12 --- squid/src/enums.h 23 Jan 2001 10:14:20 -0000 1.1.1.3.12.15.2.11 +++ squid/src/enums.h 23 Jan 2001 22:54:53 -0000 1.1.1.3.12.15.2.12 @@ -1,6 +1,6 @@ /* - * $Id: enums.h,v 1.1.1.3.12.15.2.11 2001/01/23 10:14:20 rbcollins Exp $ + * $Id: enums.h,v 1.1.1.3.12.15.2.12 2001/01/23 22:54:53 rbcollins Exp $ * * * SQUID Web Proxy Cache http://www.squid-cache.org/ @@ -496,6 +496,7 @@ AUTH_UNKNOWN, /* default */ AUTH_BASIC, AUTH_NTLM, + AUTH_DIGEST, AUTH_BROKEN /* known type, but broken data */ } auth_type_t; Index: squid/src/auth/basic/auth_basic.c =================================================================== RCS file: /cvsroot/squid-sf//squid/src/auth/basic/auth_basic.c,v retrieving revision 1.1.2.29 retrieving revision 1.1.2.30 diff -u -r1.1.2.29 -r1.1.2.30 --- squid/src/auth/basic/auth_basic.c 23 Jan 2001 13:12:59 -0000 1.1.2.29 +++ squid/src/auth/basic/auth_basic.c 23 Jan 2001 22:54:53 -0000 1.1.2.30 @@ -131,8 +131,12 @@ authBasicConfigured() { if ((basicConfig != NULL) && (basicConfig->authenticate != NULL) && - (basicConfig->authenticateChildren != 0) && (basicConfig->basicAuthRealm != NULL)) - return 1; + (basicConfig->authenticateChildren != 0) && + (basicConfig->basicAuthRealm != NULL)) { + debug(29,9)("authBasicConfigured: returning configured\n"); + return 1; + } + debug(29,9)("authBasicConfigured: returning unconfigured\n"); return 0; } --- /dev/null Wed Feb 14 00:48:56 2007 +++ squid/src/auth/digest/Makefile.in Wed Feb 14 00:49:23 2007 @@ -0,0 +1,70 @@ +# +# Makefile for the Digest authentication scheme modulefor the Squid Object Cache server +# +# $Id$ +# + +AUTH = digest + +SUBDIRS = helpers + +top_srcdir = @top_srcdir@ +VPATH = @srcdir@ + +CC = @CC@ +MAKEDEPEND = @MAKEDEPEND@ +AR_R = @AR_R@ +RANLIB = @RANLIB@ +AC_CFLAGS = @CFLAGS@ +SHELL = /bin/sh + +INCLUDE = -I../../../include -I$(top_srcdir)/include -I$(top_srcdir)/src/ +CFLAGS = $(AC_CFLAGS) $(INCLUDE) $(DEFINES) + +OUT = ../$(AUTH).a + +OBJS = \ + auth_digest.o + + +all install: $(OUT) + @for dir in $(SUBDIRS); do \ + if [ -f $$dir/Makefile ]; then \ + sh -c "cd $$dir && $(MAKE) $@" || exit 1; \ + fi; \ + done; + +$(OUT): $(OBJS) + @rm -f ../stamp + $(AR_R) $(OUT) $(OBJS) + $(RANLIB) $(OUT) + +$(OBJS): $(top_srcdir)/include/version.h ../../../include/autoconf.h + +.c.o: + @rm -f ../stamp + $(CC) $(CFLAGS) -c $< + +clean: + -rm -rf *.o *pure_* core ../$(AUTH).a + -for dir in *; do \ + if [ -f $$dir/Makefile ]; then \ + sh -c "cd $$dir && $(MAKE) clean"; \ + fi; \ + done + +distclean: clean + -rm -f Makefile + -rm -f Makefile.bak + -rm -f tags + -for dir in *; do \ + if [ -f $$dir/Makefile ]; then \ + sh -c "cd $$dir && $(MAKE) distclean"; \ + fi; \ + done + +tags: + ctags *.[ch] $(top_srcdir)/src/*.[ch] $(top_srcdir)/include/*.h $(top_srcdir)/lib/*.[ch] + +depend: + $(MAKEDEPEND) $(INCLUDE) -fMakefile *.c --- /dev/null Wed Feb 14 00:48:56 2007 +++ squid/src/auth/digest/auth_digest.c Wed Feb 14 00:49:23 2007 @@ -0,0 +1,1394 @@ + +/* + * $Id$ + * + * DEBUG: section 29 Authenticator + * AUTHOR: Robert Collins + * + * SQUID Internet Object Cache http://squid.nlanr.net/Squid/ + * ---------------------------------------------------------- + * + * Squid is the result of efforts by numerous individuals from the + * Internet community. Development is led by Duane Wessels of the + * National Laboratory for Applied Network Research and funded by the + * National Science Foundation. Squid is Copyrighted (C) 1998 by + * the Regents of the University of California. Please see the + * COPYRIGHT file for full details. Squid incorporates software + * developed and/or copyrighted by other sources. Please see the + * CREDITS file for full details. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA. + * + */ + +/* The functions in this file handle authentication. + They DO NOT perform access control or auditing. + See acl.c for access control and client_side.c for auditing */ + + +#include "squid.h" +#include "rfc2617.h" +#include "auth_digest.h" + +static void +authenticateStateFree(authenticateStateData * r) +{ + cbdataFree(r); +} + +/* Digest Scheme */ + +static HLPCB authenticateDigestHandleReply; +static AUTHSACTIVE authenticateDigestActive; +static AUTHSADDHEADER authDigestAddHeader; +#if WAITING_FOR_TE +static AUTHSADDTRAILER authDigestAddTrailer; +#endif +static AUTHSAUTHED authDigestAuthenticated; +static AUTHSAUTHUSER authenticateDigestAuthenticateUser; +static AUTHSCONFIGURED authDigestConfigured; +static AUTHSDIRECTION authenticateDigestDirection; +static AUTHSDECODE authenticateDigestDecodeAuth; +static AUTHSDUMP authDigestCfgDump; +static AUTHSFIXERR authenticateDigestFixHeader; +static AUTHSFREE authenticateDigestUserFree; +static AUTHSFREECONFIG authDigestFreeConfig; +static AUTHSINIT authDigestInit; +static AUTHSPARSE authDigestParse; +static AUTHSREQFREE authDigestAURequestFree; +static AUTHSSTART authenticateDigestStart; +static AUTHSSTATS authenticateDigestStats; +static AUTHSUSERNAME authenticateDigestUsername; +static AUTHSSHUTDOWN authDigestDone; + +static helper *digestauthenticators = NULL; + +static hash_table *digest_nonce_cache; + +static auth_digest_config *digestConfig = NULL; + +static int authdigest_initialised = 0; +MemPool *digest_user_pool = NULL; +MemPool *digest_request_pool = NULL; +MemPool *digest_nonce_pool = NULL; + +CBDATA_TYPE(authenticateStateData); + +/* + * + * Nonce Functions + * + */ + +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]); +int authDigestNonceIsStale(digest_nonce_h *nonce); +void authDigestNonceEncode(digest_nonce_h *nonce); +int authDigestNonceLastRequest(digest_nonce_h *nonce); +void authDigestNonceLink(digest_nonce_h *nonce); +void authDigestNonceUnlink(digest_nonce_h *nonce); +int authDigestNonceLinks(digest_nonce_h *nonce); +void authDigestNonceUserUnlink(digest_nonce_h *nonce); +void authDigestNoncePurge(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() +{ + digest_nonce_h *newnonce = memPoolAlloc(digest_nonce_pool); + digest_nonce_h *temp; + +/* NONCE CREATION - NOTES AND REASONING. RBC 20010108 + === EXCERPT FROM RFC 2617 === + The contents of the nonce are implementation dependent. The quality + of the implementation depends on a good choice. A nonce might, for + example, be constructed as the base 64 encoding of + + time-stamp H(time-stamp ":" ETag ":" private-key) + + where time-stamp is a server-generated time or other non-repeating + value, ETag is the value of the HTTP ETag header associated with + the requested entity, and private-key is data known only to the + server. With a nonce of this form a server would recalculate the + hash portion after receiving the client authentication header and + reject the request if it did not match the nonce from that header + or if the time-stamp value is not recent enough. In this way the + server can limit the time of the nonce's validity. The inclusion of + the ETag prevents a replay request for an updated version of the + resource. (Note: including the IP address of the client in the + nonce would appear to offer the server the ability to limit the + reuse of the nonce to the same client that originally got it. + However, that would break proxy farms, where requests from a single + user often go through different proxies in the farm. Also, IP + address spoofing is not that hard.) + ==== + + Now for my reasoning: + We will not accept a unrecognised nonce->we have all recognisable nonces stored + If we send out unique base64 encodings we guarantee that a given nonce applies + to only one user (barring attacks or really bad timing with expiry and creation). + Using a random component in the nonce allows us to loop to find a unique nonce. + We use H(nonce_data) so the nonce is meaningless to the reciever. + So our nonce looks like base64(H(timestamp,pointertohash,randomdata)) + And even if our randomness is not very random (probably due to bad coding on my + part) we don't really care - the timestamp and memory pointer should provide + enough protection for the users authentication. +*/ + + /* create a new nonce */ + newnonce->nc=0; + newnonce->flags.valid=1; + newnonce->noncedata.self=newnonce; + newnonce->noncedata.creationtime=current_time.tv_sec; + newnonce->noncedata.randomdata=random(); + + 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.randomdata=random(); + authDigestNonceEncode(newnonce); + } + hash_join(digest_nonce_cache, (hash_link *) newnonce); + /* the cache's link */ + authDigestNonceLink(newnonce); + newnonce->flags.incache=1; + debug(29,5)("authenticateDigestNonceNew: created nonce %0p at %d\n", newnonce, newnonce->noncedata.creationtime); + return newnonce; +} + +void +authenticateDigestNonceDelete(digest_nonce_h *nonce) +{ + if (nonce) + { + assert(nonce->references == 0); +#if UNREACHABLECODE + if (nonce->flags.incache) + hash_remove_link(digest_nonce_cache, (hash_link *)nonce); +#endif + assert (nonce->flags.incache==0); + safe_free(nonce->nonceb64); + memPoolFree(digest_nonce_pool, nonce); + } +} + +void authenticateDigestNonceSetup() +{ + if (!digest_nonce_pool) + digest_nonce_pool = memPoolCreate("Digest Scheme nonce's", sizeof(digest_nonce_h)); + 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, digestConfig->nonceGCInterval,1); + } +} + +void authenticateDigestNonceShutdown() +{ + /* + * We empty the cache of any nonces left in there. + */ + digest_nonce_h *nonce; + if (digest_nonce_cache) + { + debug(29,2)("authenticateDigestNonceShutdown: Shutting down nonce cache \n"); + hash_first(digest_nonce_cache); + while ((nonce=((digest_nonce_h *)hash_next(digest_nonce_cache)))) { + assert (nonce->flags.incache); + authDigestNoncePurge(nonce); + } + } + if (digest_nonce_pool) + { + assert(memPoolInUseCount(digest_nonce_pool)==0); + memPoolDestroy(digest_nonce_pool); + digest_nonce_pool=NULL; + } + debug(29,2)("authenticateDigestNonceShutdown: Nonce cache shutdown\n"); +} + +void authenticateDigestNonceReconfigure() +{ +} + +void +authenticateDigestNonceCacheCleanup(void *data) +{ + /* + * We walk the hash by nonceb64 as that is the unique key we use. + * For big hashs tables we could consider stepping through the cache, 100/200 + * entries at a time. Lets see how it flys first. + */ + digest_nonce_h *nonce; + debug(29,3) ("authenticateDigestNonceCacheCleanup: Cleaning the nonce cache now\n"); + debug(29,3) ("authenticateDigestNonceCacheCleanup: Current time: %d\n", + current_time.tv_sec); + hash_first(digest_nonce_cache); + while ((nonce=((digest_nonce_h *)hash_next(digest_nonce_cache)))) { + debug(29,3) ("authenticateDigestNonceCacheCleanup: nonce entry : '%s'\n", nonce->nonceb64); + debug(29,4) ("authenticateDigestNonceCacheCleanup: Creation time: %d\n", nonce->noncedata.creationtime); + if (authDigestNonceIsStale(nonce)) { + debug(29,4)("authenticateDigestNonceCacheCleanup: Removing nonce %s from cache due to timeout.\n",nonce->nonceb64); + assert (nonce->flags.incache); + /* invalidate nonce so future requests fail */ + nonce->flags.valid=0; + /* if it is tied to a auth_user, remove the tie */ + authDigestNonceUserUnlink(nonce); + authDigestNoncePurge(nonce); + } + } + debug(29,3) ("authenticateDigestNonceCacheCleanup: Finished cleaning the nonce cache.\n"); + if (authenticateDigestActive()) + eventAdd("Digest none cache maintenance",authenticateDigestNonceCacheCleanup, NULL, digestConfig->nonceGCInterval,1); +} + +void +authDigestNonceLink(digest_nonce_h *nonce) +{ + assert(nonce != NULL); + nonce->references++; + debug(29, 9) ("authDigestNonceLink: nonce '%d' now at '%d'.\n", nonce,nonce->references); +} + +int +authDigestNonceLinks(digest_nonce_h *nonce) +{ + if (!nonce) + return -1; + return nonce->references; +} + +void +authDigestNonceUnlink(digest_nonce_h *nonce) +{ + assert(nonce != NULL); + if (nonce->references > 0) { + nonce->references--; + } else { + debug(29, 1) ("authDigestNonceUnlink; Attempt to lower nonce %d refcount below 0!\n", nonce); + } + debug(29, 9) ("authDigestNonceUnlink: nonce '%d' now at '%d'.\n", nonce, nonce->references); + if (nonce->references == 0) + authenticateDigestNonceDelete(nonce); +} + +const char * +authenticateDigestNonceNonceb64(digest_nonce_h *nonce) +{ + if (!nonce) + return NULL; + return nonce->nonceb64; +} + +digest_nonce_h * +authenticateDigestNonceFindNonce(const char *nonceb64) +{ + digest_nonce_h *nonce=NULL; + if (nonceb64==NULL) + return NULL; + debug(29,9)("authDigestNonceFindNonce:looking for nonceb64 '%s' in the nonce cache.\n",nonceb64); + if ((nonce = hash_lookup(digest_nonce_cache, nonceb64))) + while ((strcmp(nonce->nonceb64,nonceb64)) && (nonce->next)) + nonce=nonce->next; + if ((nonce == NULL) || (strcmp(nonce->nonceb64,nonceb64))) + return NULL; + debug(29,9)("authDigestNonceFindNonce: Found nonce '%d'\n",nonce); + return nonce; +} + +int +authDigestNonceIsValid(digest_nonce_h *nonce,char nc[9]) +{ + int intnc; + /* do we have a nonce ? */ + if (!nonce) + return 0; + intnc=atoi(nc); + if (intnc != nonce->nc+1) + { + debug (29,4)("authDigestNonceIsValid: Nonce count doesn't match\n"); + nonce->flags.valid=0; + return 0; + } + /* has it already been invalidated ? */ + if (!nonce->flags.valid) + { + debug (29,4)("authDigestNonceIsValid: Nonce already invalidated\n"); + return 0; + } + /* seems ok */ + return -1; +} + +int +authDigestNonceIsStale(digest_nonce_h *nonce) +{ + /* do we have a nonce ? */ + if (!nonce) + return -1; + /* has it's max duration expired? */ + if (nonce->noncedata.creationtime + digestConfig->noncemaxduration < current_time.tv_sec) + { + debug (29,4)("authDigestNonceIsStale: Nonce is too old. %d %d %d\n", nonce->noncedata.creationtime,digestConfig->noncemaxduration , current_time.tv_sec); + nonce->flags.valid=0; + return -1; + } + if (nonce->nc>99999998) + { + debug (29,4)("authDigestNonceIsStale: Nonce count overflow\n"); + nonce->flags.valid=0; + return -1; + } + if (nonce->nc>digestConfig->noncemaxuses) + { + debug (29,4)("authDigestNoncelastRequest: Nonce count over user limit\n"); + nonce->flags.valid=0; + return -1; + } + /* seems ok */ + return 0; +} + +/* return -1 if the digest will be stale on the next request */ +int +authDigestNonceLastRequest(digest_nonce_h *nonce) +{ + if (!nonce) + return -1; + if (nonce->nc==99999997) + { + debug (29,4)("authDigestNoncelastRequest: Nonce count about to overflow\n"); + return -1; + } + if (nonce->nc==digestConfig->noncemaxuses-1) + { + debug (29,4)("authDigestNoncelastRequest: Nonce count about to hit user limit\n"); + return -1; + } + /* and other tests are possible. */ + return 0; +} + +void +authDigestNoncePurge(digest_nonce_h *nonce) +{ + if (!nonce) + return; + if (!nonce->flags.incache) + return; + hash_remove_link(digest_nonce_cache, (hash_link *)nonce); + nonce->flags.incache=0; + /* the cache's link */ + authDigestNonceUnlink(nonce); +} + +/* USER related functions */ + + +int +authDigestUsercmpname(digest_user_h * u1, digest_user_h *u2) +{ + return strcmp(u1->username,u2->username); +} + +auth_user_t * +authDigestUserFindUsername(const char * username) +{ + auth_user_hash_pointer *usernamehash; + auth_user_t *auth_user; + debug(29,9)("authDigestUserFindUsername: Looking for user '%s'\n",username); + if (username && (usernamehash = hash_lookup(proxy_auth_username_cache, username))) + { + while ((usernamehash->auth_user->auth_type != AUTH_DIGEST) && + (usernamehash->next)) + usernamehash=usernamehash->next; + auth_user=NULL; + if (usernamehash->auth_user->auth_type==AUTH_DIGEST) + { + auth_user=usernamehash->auth_user; + } + return auth_user; + } + return NULL; +} + +digest_user_h * +authDigestUserNew() +{ + return memPoolAlloc(digest_user_pool); +} + +void +authDigestUserSetup() +{ + if (!digest_user_pool) + digest_user_pool = memPoolCreate("Digest Scheme User Data", sizeof(digest_user_h)); +} + +void +authDigestUserShutdown() +{ + /* + * Future work: the auth framework could flush it's cache + */ + auth_user_hash_pointer *usernamehash; + auth_user_t *auth_user; + hash_first(proxy_auth_username_cache); + while ((usernamehash = ((auth_user_hash_pointer *) hash_next(proxy_auth_username_cache)))) { + auth_user = usernamehash->auth_user; + if (strcmp(authscheme_list[auth_user->auth_module-1].typestr,"digest")==0) + /* it's digest */ + authenticateAuthUserUnlock(auth_user); + } + if (digest_user_pool) + { + assert(memPoolInUseCount(digest_user_pool)==0); + memPoolDestroy(digest_user_pool); + digest_user_pool = NULL; + } +} + + +/* request related functions */ + +/* delete the digest reuqest structure. Does NOT delete related structures */ +void +authDigestRequestDelete(digest_request_h *digest_request) +{ + if (digest_request->nonceb64) + xfree(digest_request->nonceb64); + if (digest_request->cnonce) + xfree(digest_request->cnonce); + if (digest_request->realm) + xfree(digest_request->realm); + if (digest_request->pszPass) + xfree(digest_request->pszPass); + if (digest_request->algorithm) + xfree(digest_request->algorithm); + if (digest_request->pszMethod) + xfree(digest_request->pszMethod); + if (digest_request->qop) + xfree(digest_request->qop); + if (digest_request->uri) + xfree(digest_request->uri); + if (digest_request->response) + xfree(digest_request->response); + if (digest_request->nonce) + authDigestNonceUnlink(digest_request->nonce); + memPoolFree(digest_request_pool, digest_request); +} + +void +authDigestAURequestFree(auth_user_request_t *auth_user_request) +{ + if (auth_user_request->scheme_data != NULL) + authDigestRequestDelete((digest_request_h *)auth_user_request->scheme_data); +} + +digest_request_h * +authDigestRequestNew() +{ + digest_request_h *tmp; + tmp=memPoolAlloc(digest_request_pool); + assert (tmp !=NULL); + return tmp; +} + +void +authDigestRequestSetup() +{ + if (!digest_request_pool) + digest_request_pool = memPoolCreate("Digest Scheme Request Data", sizeof(digest_request_h)); +} + +void +authDigestRequestShutdown() +{ + /* No requests should be in progress when we get here */ + if (digest_request_pool) + { + assert(memPoolInUseCount(digest_request_pool)==0); + memPoolDestroy(digest_request_pool); + digest_request_pool = NULL; + } +} + + +void +authDigestDone(void) +{ + if (digestauthenticators) + helperShutdown(digestauthenticators); + authdigest_initialised = 0; + if (!shutting_down) + { + authenticateDigestNonceReconfigure(); + return; + } + if (digestauthenticators) + { + helperFree(digestauthenticators); + digestauthenticators = NULL; + } + authDigestRequestShutdown(); + authDigestUserShutdown(); + authenticateDigestNonceShutdown(); + debug(29,2)("authenticateDigestDone: Digest authentication shut down.\n"); +} + +static void +authDigestCfgDump(StoreEntry * entry, const char *name, authScheme * scheme) +{ + auth_digest_config *config = scheme->scheme_data; + wordlist *list = config->authenticate; + debug(29,9)("authDigestCfgDump: Dumping configuration\n"); + storeAppendPrintf(entry, "%s %s", name, "digest"); + while (list != NULL) { + storeAppendPrintf(entry, " %s", list->key); + list = list->next; + } + storeAppendPrintf(entry, "\n%s %s realm %s\n%s %s children %d\n%s %s nonce_max_count %d\n%s %s nonce_max_duration %d seconds\n%s %s nonce_garbage_interval %d seconds\n", + name, "digest", config->digestAuthRealm, + name, "digest", config->authenticateChildren, + name, "digest", config->noncemaxuses, + name, "digest", config->noncemaxduration, + name, "digest", config->nonceGCInterval); +} + +void +authSchemeSetup_digest(authscheme_entry_t *authscheme) { + assert(!authdigest_initialised); + authscheme->Active =authenticateDigestActive; + authscheme->configured =authDigestConfigured; + authscheme->parse =authDigestParse; + authscheme->freeconfig =authDigestFreeConfig; + authscheme->dump =authDigestCfgDump; + authscheme->init =authDigestInit; + authscheme->authAuthenticate = authenticateDigestAuthenticateUser; + authscheme->authenticated= authDigestAuthenticated; + authscheme->authFixHeader=authenticateDigestFixHeader; + authscheme->FreeUser =authenticateDigestUserFree; + authscheme->AddHeader =authDigestAddHeader; +#if WAITING_FOR_TE + authscheme->AddTrailer =authDigestAddTrailer; +#endif + authscheme->authStart =authenticateDigestStart; + authscheme->authStats =authenticateDigestStats; + authscheme->authUserUsername = authenticateDigestUsername; + authscheme->getdirection=authenticateDigestDirection; + authscheme->oncloseconnection=NULL; + authscheme->decodeauth =authenticateDigestDecodeAuth; + authscheme->donefunc = authDigestDone; + authscheme->requestFree = authDigestAURequestFree; +} + +int +authenticateDigestActive() +{ + return (authdigest_initialised==1) ? 1:0; +} +int +authDigestConfigured() +{ + if ((digestConfig != NULL) && (digestConfig->authenticate != NULL) && + (digestConfig->authenticateChildren != 0) && + (digestConfig->digestAuthRealm != NULL) && (digestConfig->noncemaxduration > -1)) + return 1; + return 0; +} + +int +authDigestAuthenticated(auth_user_request_t * auth_user_request) +{ + if (auth_user_request->auth_user->flags.credentials_ok==1) + return 1; + else + return 0; +} + +/* log a digest user in + */ +static void +authenticateDigestAuthenticateUser(auth_user_request_t *auth_user_request, request_t *request, ConnStateData *conn, http_hdr_type type) { + auth_user_t *auth_user; + digest_request_h * digest_request; + digest_user_h * digest_user; + + HASHHEX SESSIONKEY; + HASHHEX HA2 = ""; + HASHHEX Response; + + assert (auth_user_request->auth_user != NULL); + auth_user=auth_user_request->auth_user; + + /* if the check has corrupted the user, just return */ + if (auth_user_request->auth_user->flags.credentials_ok==3) + { + return; + } + + assert(auth_user->scheme_data != NULL); + digest_user = auth_user->scheme_data; + + assert(auth_user_request->scheme_data != NULL); + digest_request=auth_user_request->scheme_data; + + /* do we have the HA1 */ + if (!digest_user->HA1created) + { + auth_user_request->auth_user->flags.credentials_ok=2; + return; + } + if (digest_request->nonce==NULL) + { + /* this isn't a nonce we issued */ + /* TODO: record breaks in authentication at the request level + * This is probably best done with support changes at the auth_rewrite level -RBC + * and can wait for auth_rewrite V2. + */ + auth_user->flags.credentials_ok=3; + 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); + + debug(29,9)("\nResponse = '%s'\n" + "squid is = '%s'\n" , digest_request->response,Response); + + if (strcasecmp(digest_request->response,Response)) + { + auth_user->flags.credentials_ok=3; + return; + } + + auth_user->flags.credentials_ok=1; + /* password was checked and did match */ + debug(29, 4) ("authenticateDigestAuthenticateuser: user '%s' validated OK\n", + digest_user->username); + + /* auth_user is now linked, we reset these values + * after external auth occurs anyway */ + auth_user->expiretime = current_time.tv_sec; + auth_user->ip_expiretime = squid_curtime; + return; +} + +int authenticateDigestDirection(auth_user_request_t *auth_user_request) { + digest_request_h * digest_request; +/* null auth_user is checked for by authenticateDirection */ + switch (auth_user_request->auth_user->flags.credentials_ok) { + case 0: /* not checked */ + return -1; + case 1: /* checked & ok */ + digest_request=auth_user_request->scheme_data; + if (authDigestNonceIsStale(digest_request->nonce)) + /* send stale response to the client agent */ + return -2; + return 0; + case 2: /* partway through checking. */ + return -1; + case 3: /* authentication process failed. */ + return -2; + } + return -2; +} + +/* add the [proxy]authorisation header */ +void +authDigestAddHeader(auth_user_request_t *auth_user_request, HttpReply *rep, int accel) +{ + int type; + digest_request_h *digest_request; + if (!auth_user_request) + return; + digest_request=auth_user_request->scheme_data; + /* 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 ((digestConfig->authenticate) && authDigestNonceLastRequest(digest_request->nonce)) + { + digest_request->flags.authinfo_sent=1; + debug(29, 9) ("authDigestAddHead: Sending type:%d header: 'nextnonce=\"%s\"",type,authenticateDigestNonceNonceb64(digest_request->nonce)); + httpHeaderPutStrf(&rep->header, type, "nextnonce=\"%s\"",authenticateDigestNonceNonceb64(digest_request->nonce)); + } + +} + +#if WAITING_FOR_TE +/* add the [proxy]authorisation header */ +void +authDigestAddTrailer(auth_user_request_t *auth_user_request, HttpReply *rep, int accel) +{ + int type; + digest_request_h *digest_request; + if (!auth_user_request) + return; + digest_request=auth_user_request->scheme_data; + /* has the header already been send? */ + if (digest_request->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 ((digestConfig->authenticate) && authDigestNonceLastRequest(digest_request->nonce)) + { + debug(29, 9) ("authDigestAddTrailer: Sending type:%d header: 'nextnonce=\"%s\"",type, authenticateDigestNonceNonceb64(digest_request->nonce)); + httpTrailerPutStrf(&rep->header, type, "nextnonce=\"%s\"",authenticateDigestNonceNonceb64(digest_request->nonce)); + } + +} +#endif + +/* add the [www-|Proxy-]authenticate header on a 407 or 401 reply */ +void +authenticateDigestFixHeader(auth_user_request_t *auth_user_request, HttpReply *rep, http_hdr_type type, request_t * request){ + digest_request_h *digest_request; + int stale=0; + digest_nonce_h *nonce = authenticateDigestNonceNew(); + if (auth_user_request && authDigestAuthenticated(auth_user_request) && auth_user_request->scheme_data) + { + digest_request=auth_user_request->scheme_data; + stale=authDigestNonceIsStale(digest_request->nonce); + } + if (digestConfig->authenticate){ + debug(29, 9) ("authenticateFixHeader: Sending type:%d header: 'Digest realm=\"%s\", nonce=\"%s\", qop=\"%s\", stale=%s\n",type,digestConfig->digestAuthRealm,authenticateDigestNonceNonceb64(nonce),QOP_AUTH, stale ? "true" : "false"); + /* in the future, for WWW auth we may want to support the domain entry */ + httpHeaderPutStrf(&rep->header, type, "Digest realm=\"%s\", nonce=\"%s\", qop=\"%s\", stale=%s",digestConfig->digestAuthRealm,authenticateDigestNonceNonceb64(nonce),QOP_AUTH, stale ? "true" : "false"); + } +} + +void +authenticateDigestUserFree(auth_user_t *auth_user) { + digest_user_h * digest_user = auth_user->scheme_data; + dlink_node *link,*tmplink; + debug(29,9) ("authenticateDigestFreeUser: Clearing Digest scheme data\n"); + if (!digest_user) + return; + safe_free(digest_user->username); + + link=digest_user->nonces.head; + while (link) + { + tmplink=link; + link=link->next; + dlinkDelete(tmplink, &digest_user->nonces); + authDigestNoncePurge(tmplink->data); + authDigestNonceUnlink(tmplink->data); + dlinkNodeDelete(tmplink); + } + + memPoolFree(digest_user_pool, auth_user->scheme_data); + auth_user->scheme_data = NULL; +} + +static void +authenticateDigestHandleReply(void *data, char *reply) +{ + authenticateStateData *r = data; + auth_user_request_t *auth_user_request; + digest_request_h *digest_request; + digest_user_h *digest_user; + int valid; + char *t = NULL; + debug(29, 9) ("authenticateDigestHandleReply: {%s}\n", reply ? reply : ""); + if (reply) { + if ((t = strchr(reply, ' '))) + *t = '\0'; + if (*reply == '\0') + reply = NULL; + } + assert(r->auth_user_request != NULL); + auth_user_request=r->auth_user_request; + assert(auth_user_request->scheme_data != NULL); + digest_request = auth_user_request->scheme_data; + digest_user = auth_user_request->auth_user->scheme_data; + if (reply && (strncasecmp(reply, "ERR", 3) == 0)) + auth_user_request->auth_user->flags.credentials_ok = 3; + else + { + CvtBin(reply,digest_user->HA1); + digest_user->HA1created=1; + } + valid = cbdataValid(r->data); + if (valid) + r->handler(r->data, NULL); + cbdataUnlock(r->data); + authenticateStateFree(r); +} + +/* Initialize helpers and the like for this auth scheme. Called AFTER parsing the + * config file */ +static void +authDigestInit(authScheme *scheme) +{ + static int init = 0; + if (digestConfig->authenticate){ + authDigestUserSetup(); + authDigestRequestSetup(); + authenticateDigestNonceSetup(); + authdigest_initialised = 1; + if (digestauthenticators == NULL) + digestauthenticators = helperCreate("digestauthenticator"); + digestauthenticators->cmdline = digestConfig->authenticate; + digestauthenticators->n_to_start = digestConfig->authenticateChildren; + digestauthenticators->ipc_type = IPC_TCP_SOCKET; + helperOpenServers(digestauthenticators); + if (!init) + { + cachemgrRegister("digestauthenticator", "User Authenticator Stats", + authenticateDigestStats, 0, 1); + init++; + } + CBDATA_INIT_TYPE(authenticateStateData); + } +} + + +/* free any allocated configuration details */ +void +authDigestFreeConfig(authScheme *scheme) +{ + if (digestConfig==NULL) + return; + assert(digestConfig==scheme->scheme_data); + if (digestConfig->authenticate) + wordlistDestroy(&digestConfig->authenticate); + if (digestConfig->digestAuthRealm) + safe_free(digestConfig->digestAuthRealm); + xfree(digestConfig); + digestConfig=NULL; +} + +static void +authDigestParse(authScheme *scheme, int n_configured, char *param_str) +{ + if (scheme->scheme_data==NULL) + { + assert (digestConfig==NULL); + /* this is the first param to be found */ + scheme->scheme_data=xmalloc(sizeof(auth_digest_config)); + 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; + /* 50 requests */ + digestConfig->noncemaxuses=50; + } + digestConfig=scheme->scheme_data; + if (strcasecmp(param_str,"program")==0) + { + parse_wordlist(&digestConfig->authenticate); + requirePathnameExists("authparam digest program",digestConfig->authenticate->key); + } + else if (strcasecmp(param_str,"children")==0) + { + parse_int(&digestConfig->authenticateChildren); + } + else if (strcasecmp(param_str,"realm")==0) + { + parse_eol(&digestConfig->digestAuthRealm); + } + else if (strcasecmp(param_str,"nonce_garbage_interval")==0) + { + parse_time_t(&digestConfig->nonceGCInterval); + } + else if (strcasecmp(param_str,"nonce_max_duration")==0) + { + parse_time_t(&digestConfig->noncemaxduration); + } + else if (strcasecmp(param_str,"nonce_max_count")==0) + { + parse_int(&digestConfig->noncemaxuses); + } else + { + debug(28,0)("unrecognised digest auth scheme parameter '%s'\n",param_str); + } +} + + +static void +authenticateDigestStats(StoreEntry * sentry) +{ + storeAppendPrintf(sentry, "Digest Authenticator Statistics:\n"); + helperStats(sentry, digestauthenticators); +} + +/* NonceUserUnlink: remove the reference to auth_user and unlink the node from the list*/ + +void +authDigestNonceUserUnlink(digest_nonce_h *nonce) +{ + digest_user_h * digest_user; + dlink_node *link,*tmplink; + if (!nonce) + return; + if (!nonce->auth_user) + return; + digest_user = nonce->auth_user->scheme_data; + /* unlink from the user list. Yes we're crossing structures but this is the only + * time this code is needed + */ + link=digest_user->nonces.head; + while (link) + { + tmplink=link; + link=link->next; + if (tmplink->data==nonce) + { + dlinkDelete(tmplink, &digest_user->nonces); + authDigestNonceUnlink(tmplink->data); + dlinkNodeDelete(tmplink); + link=NULL; + } + } + /* this reference to auth_user was not locked because freeeing the auth_user frees + * the nonce too. + */ + nonce->auth_user=NULL; +} + +/* authDigestUserLinkNonce: add a nonce to a given user's struct */ + +void +authDigestUserLinkNonce(auth_user_t *auth_user, digest_nonce_h *nonce) +{ + dlink_node *node; + digest_user_h * digest_user; + if (!auth_user || !nonce) + return; + if (!auth_user->scheme_data) + return; + digest_user=auth_user->scheme_data; + node=digest_user->nonces.head; + while (node && (node->data != nonce)) + node=node->next; + if (node) + return; + node=dlinkNodeNew(); + dlinkAddTail(nonce,node,&digest_user->nonces); + authDigestNonceLink(nonce); + /* ping this nonce to this auth user */ + assert ((nonce->auth_user==NULL) || (nonce->auth_user=auth_user)); + /* we don't lock this reference because removing the auth_user removes the + * hash too. Of course if that changes we're stuffed so read the code huh? + */ + nonce->auth_user=auth_user; +} + +/* authenticateDigestUsername: return a pointer to the username in the */ +char * +authenticateDigestUsername(auth_user_t *auth_user) { + digest_user_h * digest_user = auth_user->scheme_data; + if (digest_user) + return digest_user->username; + 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,9)("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. + */ + +static void +authenticateDigestDecodeAuth(auth_user_request_t *auth_user_request, const char * proxy_auth) { + String temp; + const char *item; + const char *p; + const char *pos = NULL; + char * username=NULL; + digest_nonce_h *nonce; + int ilen; + digest_request_h * digest_request; + digest_user_h * digest_user; + auth_user_t * auth_user; + dlink_node *node; + + debug(29,9)("authenticateDigestDecodeAuth: beginning\n"); + assert(auth_user_request != NULL); + + digest_request = authDigestRequestNew(); + + /* trim DIGEST from string */ + while (!xisspace(*proxy_auth)) + proxy_auth++; + + /* Trim leading whitespace before decoding */ + while (xisspace(*proxy_auth)) + proxy_auth++; + + stringInit(&temp, proxy_auth); + while (strListGetItem(&temp, ',', &item,&ilen,&pos)) + { + if ((p = strchr(item,'=')) && (p-itemrealm=xstrndup(p,strchr(p,'"')+1-p); + debug(29,9)("authDigestDecodeAuth: Found realm '%s'\n",digest_request->realm); + } + else if (!strncmp(item, "qop", ilen)) + { + /* white space */ + while (xisspace(*p)) + p++; + /* quote mark */ + p++; + digest_request->qop=xstrndup(p,strchr(p,'"')+1-p); + debug(29,9)("authDigestDecodeAuth: Found qop '%s'\n",digest_request->qop); + } + else if (!strncmp(item, "algorithm", ilen)) + { + /* white space */ + while (xisspace(*p)) + p++; + /* quote mark */ + p++; + digest_request->algorithm=xstrndup(p,strchr(p,'"')+1-p); + debug(29,9)("authDigestDecodeAuth: Found algorithm '%s'\n",digest_request->algorithm); + } + else if (!strncmp(item, "uri", ilen)) + { + /* white space */ + while (xisspace(*p)) + p++; + /* quote mark */ + p++; + digest_request->uri=xstrndup(p,strchr(p,'"')+1-p); + debug(29,9)("authDigestDecodeAuth: Found uri '%s'\n",digest_request->uri); + } + else if (!strncmp(item, "nonce", ilen)) + { + /* white space */ + while (xisspace(*p)) + p++; + /* quote mark */ + p++; + digest_request->nonceb64=xstrndup(p,strchr(p,'"')+1-p); + debug(29,9)("authDigestDecodeAuth: Found nonce '%s'\n",digest_request->nonceb64); + } + else if (!strncmp(item, "nc", ilen)) + { + /* white space */ + while (xisspace(*p)) + p++; + xstrncpy(digest_request->nc,p,9); + debug(29,9)("authDigestDecodeAuth: Found noncecount '%s'\n",digest_request->nc); + } + else if (!strncmp(item, "cnonce", ilen)) + { + /* white space */ + while (xisspace(*p)) + p++; + /* quote mark */ + p++; + digest_request->cnonce=xstrndup(p,strchr(p,'"')+1-p); + debug(29,9)("authDigestDecodeAuth: Found cnonce '%s'\n",digest_request->cnonce); + } + else if (!strncmp(item, "response", ilen)) + { + /* white space */ + while (xisspace(*p)) + p++; + /* quote mark */ + p++; + digest_request->response=xstrndup(p,strchr(p,'"')+1-p); + debug(29,9)("authDigestDecodeAuth: Found response '%s'\n",digest_request->response); + } + } + 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,4)("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) || !(authDigestNonceIsValid(nonce, digest_request->nc))) + { + /* we couldn't find a matching nonce! */ + debug (29,4)("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++; + authDigestNonceLink(nonce); + + /* 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,4)("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,4)("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,4)("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; + } + + /* check that we're not being hacked / the username hasn't changed */ + if (nonce->auth_user && strcmp(username, authenticateUserUsername(nonce->auth_user))) + { + debug (29,4)("authenticateDigestDecode: Username for the nonce does not equal the username for the request\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,4)("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,4)("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 */ + + if ((auth_user=authDigestUserFindUsername(username))== NULL) + { + /* the user doesn't exist in the username cache yet */ + debug(29,9)("authDigestDecodeAuth: Creating new digest user '%s'\n",username); + /* new auth_user */ + auth_user=authenticateAuthUserNew("digest"); + /* new scheme user data */ + digest_user=authDigestUserNew(); + /* save the username */ + digest_user->username=username; + /* link the primary struct in */ + auth_user->scheme_data=digest_user; + /* set the user type */ + auth_user->auth_type=AUTH_DIGEST; + /* this auth_user struct is the one to get added to the username cache */ + /* store user in hash's */ + authenticateUserNameCacheAdd(auth_user); + /* + * Add the digest to the user so we can tell if a hacking or spoofing attack + * is taking place. We do this by assuming the user agent won't change user + * name without warning. + */ + authDigestUserLinkNonce(auth_user,nonce); + } + else + { + debug(29,9)("authDigestDecodeAuth: Found user '%s' in the user cache as '%d'\n",username,auth_user); + digest_user=auth_user->scheme_data; + xfree(username); + } + /*link the request and the user */ + auth_user_request->auth_user=auth_user; + auth_user_request->scheme_data=digest_request; + /* lock for the request link */ + authenticateAuthUserLock(auth_user); + node=dlinkNodeNew(); + dlinkAdd(auth_user_request, node, &auth_user->requests); + + debug(29,9)("username = '%s'\nrealm = '%s'\nqop = '%s'\nalgorithm = '%s'\nuri = '%s'\nnonce = '%s'\nnc = '%s'\ncnonce = '%s'\nresponse = '%s'\ndigestnonce = '%d'\n", + digest_user->username, digest_request->realm, + digest_request->qop, digest_request->algorithm, + digest_request->uri, digest_request->nonceb64, + digest_request->nc, digest_request->cnonce, digest_request->response,nonce); + + return; +} + +/* send the initial data to a digest authenticator module */ +static void +authenticateDigestStart(auth_user_request_t * auth_user_request, RH * handler, void *data) +{ + authenticateStateData *r = NULL; + char buf[8192]; + digest_request_h * digest_request; + digest_user_h * digest_user; + assert(auth_user_request); + assert(handler); + assert(auth_user_request->auth_user->auth_type==AUTH_DIGEST); + assert(auth_user_request->auth_user->scheme_data != NULL); + assert(auth_user_request->scheme_data != NULL); + digest_request = auth_user_request->scheme_data; + digest_user = auth_user_request->auth_user->scheme_data; + debug(29, 9) ("authenticateStart: '\"%s\":\"%s\"'\n", digest_user->username, + digest_request->realm); + if (digestConfig->authenticate == NULL) { + handler(data, NULL); + return; + } + r = CBDATA_ALLOC(authenticateStateData, NULL); + r->handler = handler; + cbdataLock(data); + r->data = data; + r->auth_user_request = auth_user_request; + snprintf(buf, 8192, "\"%s\":\"%s\"\n", digest_user->username, digest_request->realm); + helperSubmit(digestauthenticators, buf, authenticateDigestHandleReply, r); +} --- /dev/null Wed Feb 14 00:48:56 2007 +++ squid/src/auth/digest/auth_digest.h Wed Feb 14 00:49:23 2007 @@ -0,0 +1,91 @@ +/* + * auth_digest.h + * Internal declarations for the digest auth module + */ + +#ifndef __AUTH_DIGEST_H__ +#define __AUTH_DIGEST_H__ +#include "rfc2617.h" + +/* Generic */ +typedef struct { + void *data; + auth_user_request_t *auth_user_request; + RH *handler; +} authenticateStateData; + +typedef struct _digest_request_h digest_request_h; +typedef struct _digest_user_h digest_user_h; +typedef struct _digest_nonce_data digest_nonce_data; + +typedef struct _digest_nonce_h digest_nonce_h; + +struct _digest_user_h { + char *username; + HASH HA1; + int HA1created; + /* what nonces have been allocated to this user*/ + dlink_list nonces; +}; + +/* the digest_request structure is what follows the http_request around */ +struct _digest_request_h { + char * nonceb64;// = "dcd98b7102dd2f0e8b11d0f600bfb0c093"; + char * cnonce;// = "0a4f113b"; + char * realm;// = "testrealm@host.com"; + char * pszPass;// = "Circle Of Life"; + char * algorithm;// = "md5"; + char nc[9];// = "00000001"; + char * pszMethod;// = "GET"; + char * qop;// = "auth"; + char *uri;// = "/dir/index.html"; + char *response; + struct { + unsigned int authinfo_sent:1; + } flags; + digest_nonce_h *nonce; +}; + +/* 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; + long randomdata; +}; + +/* 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; + /* reference count */ + short references; + /* the auth_user this nonce has been tied to */ + auth_user_t * auth_user; + /* has this nonce been invalidated ? */ + struct { + unsigned int valid:1; + unsigned int incache:1; + } flags; +}; + +/* configuration runtime data */ +struct _auth_digest_config { + int authenticateChildren; + char *digestAuthRealm; + wordlist *authenticate; + time_t nonceGCInterval; + time_t noncemaxduration; + int noncemaxuses; +}; + +typedef struct _auth_digest_config auth_digest_config; + +/* strings */ +#define QOP_AUTH "auth" + +#endif --- /dev/null Wed Feb 14 00:48:56 2007 +++ squid/src/auth/digest/helpers/Makefile.in Wed Feb 14 00:49:23 2007 @@ -0,0 +1,38 @@ +# Makefile for digest auth helpers in the Squid Object Cache server +# +# $Id$ +# + +# The 'nop' is in the SUBDIRS list because some Unixes that can't +# handle empty for lists. + +SUBDIRS = @DIGEST_AUTH_HELPERS@ nop + +all: + @for dir in $(SUBDIRS); do \ + if [ -f $$dir/Makefile ]; then \ + sh -c "cd $$dir && $(MAKE) all" || exit 1; \ + fi; \ + done; + +clean: + -for dir in *; do \ + if [ -f $$dir/Makefile ]; then \ + sh -c "cd $$dir && $(MAKE) clean"; \ + fi; \ + done + +distclean: + -rm -f Makefile + -for dir in *; do \ + if [ -f $$dir/Makefile ]; then \ + sh -c "cd $$dir && $(MAKE) distclean"; \ + fi; \ + done + +.DEFAULT: + @for dir in $(SUBDIRS); do \ + if [ -f $$dir/Makefile ]; then \ + sh -c "cd $$dir && $(MAKE) $@" || exit 1; \ + fi; \ + done; --- /dev/null Wed Feb 14 00:48:56 2007 +++ squid/src/auth/digest/helpers/password/Makefile.in Wed Feb 14 00:49:23 2007 @@ -0,0 +1,100 @@ +# +# Makefile for the Squid Object Cache server +# +# $Id$ +# +# Uncomment and customize the following to suit your needs: +# + +prefix = @prefix@ +exec_prefix = @exec_prefix@ +exec_suffix = @exec_suffix@ +cgi_suffix = @cgi_suffix@ +top_srcdir = @top_srcdir@ +bindir = @bindir@ +libexecdir = @libexecdir@ +sysconfdir = @sysconfdir@ +localstatedir = @localstatedir@ +srcdir = @srcdir@ +VPATH = @srcdir@ + +# Gotta love the DOS legacy +# +DIGEST_PW_AUTH_EXE = digest_pw_auth$(exec_suffix) + +DEFAULT_PASSWD_FILE = $(sysconfdir)/digest_passwd + +CC = @CC@ +MAKEDEPEND = @MAKEDEPEND@ +INSTALL = @INSTALL@ +INSTALL_BIN = @INSTALL_PROGRAM@ +INSTALL_FILE = @INSTALL_DATA@ +INSTALL_SUID = @INSTALL_PROGRAM@ -o root -m 4755 +RANLIB = @RANLIB@ +LN_S = @LN_S@ +PERL = @PERL@ +CRYPTLIB = @CRYPTLIB@ +REGEXLIB = @REGEXLIB@ +PTHREADLIB = @PTHREADLIB@ +SNMPLIB = @SNMPLIB@ +MALLOCLIB = @LIB_MALLOC@ +AC_CFLAGS = @CFLAGS@ +LDFLAGS = @LDFLAGS@ +XTRA_LIBS = @XTRA_LIBS@ +XTRA_OBJS = @XTRA_OBJS@ +MV = @MV@ +RM = @RM@ +SHELL = /bin/sh + + +INCLUDE = -I. -I../../../../../include -I$(top_srcdir)/include +CFLAGS = $(AC_CFLAGS) $(INCLUDE) $(DEFINES) +AUTH_LIBS = -L../../../../../lib -lmiscutil $(CRYPTLIB) $(XTRA_LIBS) + +PROGS = $(DIGEST_PW_AUTH_EXE) +OBJS = digest_pw_auth.o + +all: $(DIGEST_PW_AUTH_EXE) + +$(OBJS): $(top_srcdir)/include/version.h + +$(DIGEST_PW_AUTH_EXE): digest_pw_auth.o + $(CC) $(LDFLAGS) digest_pw_auth.o -o $@ $(AUTH_LIBS) + +install-mkdirs: + -@if test ! -d $(prefix); then \ + echo "mkdir $(prefix)"; \ + mkdir $(prefix); \ + fi + -@if test ! -d $(bindir); then \ + echo "mkdir $(bindir)"; \ + mkdir $(bindir); \ + fi + +# Michael Lupp wants to know about additions +# to the install target. +install: all install-mkdirs + @for f in $(PROGS); do \ + if test -f $(bindir)/$$f; then \ + echo $(MV) $(bindir)/$$f $(bindir)/-$$f; \ + $(MV) $(bindir)/$$f $(bindir)/-$$f; \ + fi; \ + echo $(INSTALL_BIN) $$f $(bindir); \ + $(INSTALL_BIN) $$f $(bindir); \ + if test -f $(bindir)/-$$f; then \ + echo $(RM) -f $(bindir)/-$$f; \ + $(RM) -f $(bindir)/-$$f; \ + fi; \ + done + +clean: + -rm -rf *.o *pure_* core $(PROGS) + +distclean: clean + -rm -f Makefile + +tags: + ctags *.[ch] ../include/*.h ../lib/*.[ch] + +depend: + $(MAKEDEPEND) -I../include -I. -fMakefile *.c --- /dev/null Wed Feb 14 00:48:56 2007 +++ squid/src/auth/digest/helpers/password/digest_pw_auth.c Wed Feb 14 00:49:23 2007 @@ -0,0 +1,158 @@ +/* + * digest_pw_auth.c + * + * AUTHOR: Robert Collins. Based on ncsa_auth.c by Arjan de Vet + * + * Example digest authentication program for Squid, based on the original + * proxy_auth code from client_side.c, written by + * Jon Thackray . + * + * - comment lines are possible and should start with a '#'; + * - empty or blank lines are possible; + * - file format is username:password + * + * To build a directory integrated backend, you need to be able to + * calculate the HA1 returned to squid. To avoid storing a plaintext + * password you can calculate MD5(username:realm:password) when the user changes their + * password, and store the tuple username:realm:HA1. then find the matching + * username:realm when squid asks for the HA1. + * + * This implementation could be improved by using such a triple for the file format. + * However storing such a triple does little to improve security: If compromised the + * username:realm:HA1 combination is "plaintext equivalent" - for the purposes of + * digest authentication they allow the user access. Password syncronisation + * is not tackled by digest - just preventing on the wire compromise. + * + */ + +#include "config.h" +#if HAVE_STDIO_H +#include +#endif +#if HAVE_STDLIB_H +#include +#endif +#if HAVE_UNISTD_H +#include +#endif +#if HAVE_STRING_H +#include +#endif +#if HAVE_SYS_TYPES_H +#include +#endif +#if HAVE_SYS_STAT_H +#include +#endif +#if HAVE_CRYPT_H +#include +#endif + +#include "util.h" +#include "hash.h" +#include "rfc2617.h" + +static hash_table *hash = NULL; +static HASHFREE my_free; + +typedef struct _user_data { + /* first two items must be same as hash_link */ + char *user; + struct _user_data *next; + char *passwd; + char *realm; +} user_data; + +static void +my_free(void *p) +{ + user_data *u = p; + xfree(u->user); + xfree(u->passwd); + xfree(u); +} + +static void +read_passwd_file(const char *passwdfile) +{ + FILE *f; + char buf[8192]; + user_data *u; + char *user; + char *passwd; + if (hash != NULL) { + hashFreeItems(hash, my_free); + } + /* initial setup */ + hash = hash_create((HASHCMP *) strcmp, 7921, hash_string); + if (NULL == hash) { + fprintf(stderr, "digest_pw_auth: cannot create hash table\n"); + exit(1); + } + f = fopen(passwdfile, "r"); + while (fgets(buf, 8192, f) != NULL) { + if ((buf[0] == '#') || (buf[0] == ' ') || (buf[0] == '\t') || + (buf[0] == '\n')) + continue; + user = strtok(buf, ":\n"); + passwd = strtok(NULL, ":\n"); + if ((strlen(user) > 0) && passwd) { + u = xmalloc(sizeof(*u)); + u->user = xstrdup(user); + u->passwd = xstrdup(passwd); + hash_join(hash, (hash_link *) u); + } + } + fclose(f); +} + +int +main(int argc, char **argv) +{ + struct stat sb; + time_t change_time = 0; + char buf[256]; + char *user, *realm, *p; + user_data *u; + HASH HA1; + HASHHEX HHA1; + setbuf(stdout, NULL); + if (argc != 2) { + fprintf(stderr, "Usage: digest_pw_auth \n"); + exit(1); + } + if (stat(argv[1], &sb) != 0) { + fprintf(stderr, "cannot stat %s\n", argv[1]); + exit(1); + } + while (fgets(buf, 256, stdin) != NULL) { + if ((p = strchr(buf, '\n')) != NULL) + *p = '\0'; /* strip \n */ + if (stat(argv[1], &sb) == 0) { + if (sb.st_mtime != change_time) { + read_passwd_file(argv[1]); + change_time = sb.st_mtime; + } + } + if ((user = strtok(buf, "\"")) == NULL) { + printf("ERR\n"); + continue; + } + if ((realm = strtok(NULL, "\"")) == NULL) { + printf("ERR\n"); + continue; + } + if ((realm = strtok(NULL, "\"")) == NULL) { + printf("ERR\n"); + continue; + } + u = hash_lookup(hash, user); + if (u == NULL) { + printf("ERR\n"); + } else { + DigestCalcHA1("md5",user,realm,u->passwd, NULL, NULL, HA1, HHA1); + printf("%s\n",HHA1); + } + } + exit(0); +} Index: squid/src/auth/ntlm/auth_ntlm.c =================================================================== RCS file: /cvsroot/squid-sf//squid/src/auth/ntlm/auth_ntlm.c,v retrieving revision 1.1.2.29 retrieving revision 1.1.2.30 diff -u -r1.1.2.29 -r1.1.2.30 --- squid/src/auth/ntlm/auth_ntlm.c 23 Jan 2001 13:12:59 -0000 1.1.2.29 +++ squid/src/auth/ntlm/auth_ntlm.c 23 Jan 2001 22:54:54 -0000 1.1.2.30 @@ -255,8 +255,11 @@ { if ((ntlmConfig != NULL) && (ntlmConfig->authenticate != NULL) && (ntlmConfig->authenticateChildren != 0) && (ntlmConfig->challengeuses > -1) - && (ntlmConfig->challengelifetime > -1)) - return 1; + && (ntlmConfig->challengelifetime > -1)) { + debug(29,9)("authNTLMConfigured: returning configured\n"); + return 1; + } + debug(29,9)("authNTLMConfigured: returning unconfigured\n"); return 0; }