]> git.ipfire.org Git - thirdparty/squid.git/commitdiff
Major update from auth_rewrite
authorhno <>
Thu, 1 Feb 2001 05:16:35 +0000 (05:16 +0000)
committerhno <>
Thu, 1 Feb 2001 05:16:35 +0000 (05:16 +0000)
Robert Collins:

Digest (RFC2617) proxy authentication implementation

Chemolli Francesco:

Several bugs in NTLM authentication when dealing with untrusted
domains, wrong passwords, helper arguments and more have been fixed

25 files changed:
configure.in
doc/Programming-Guide/prog-guide.sgml
helpers/digest_auth/Makefile.in [new file with mode: 0644]
helpers/digest_auth/password/Makefile.in [new file with mode: 0644]
helpers/digest_auth/password/digest_pw_auth.c [new file with mode: 0644]
helpers/ntlm_auth/SMB/libntlmssp.c
helpers/ntlm_auth/SMB/ntlm.h
helpers/ntlm_auth/SMB/ntlm_auth.c
include/md5.h
include/rfc2617.h [new file with mode: 0644]
lib/Makefile.in
lib/md5.c
lib/rfc2617.c [new file with mode: 0644]
src/acl.cc
src/auth/basic/auth_basic.cc
src/auth/digest/Makefile.in [new file with mode: 0644]
src/auth/digest/auth_digest.cc [new file with mode: 0644]
src/auth/digest/auth_digest.h [new file with mode: 0644]
src/auth/ntlm/auth_ntlm.cc
src/authenticate.cc
src/cf.data.pre
src/enums.h
src/helper.cc
src/structs.h
src/typedefs.h

index ed9a72c4a206e3c1ea77191ff92c048902f31cbf..6d387f89ac2500be3b40b7e065f22f1c7c2bbac5 100644 (file)
@@ -3,13 +3,13 @@ dnl  Configuration input file for Squid
 dnl
 dnl  Duane Wessels, wessels@nlanr.net, February 1996 (autoconf v2.9)
 dnl
-dnl  $Id: configure.in,v 1.219 2001/01/18 17:57:04 wessels Exp $
+dnl  $Id: configure.in,v 1.220 2001/01/31 22:16:35 hno Exp $
 dnl
 dnl
 dnl
 AC_INIT(src/main.c)
 AC_CONFIG_HEADER(include/autoconf.h)
-AC_REVISION($Revision: 1.219 $)dnl
+AC_REVISION($Revision: 1.220 $)dnl
 AC_PREFIX_DEFAULT(/usr/local/squid)
 AC_CONFIG_AUX_DIR(cfgaux)
 
@@ -785,6 +785,32 @@ if test -n "$NTLM_AUTH_HELPERS"; then
 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 b5e3ce89b89ec352100ddcc5b84ba17a68e09789..be443093edf50df18d8986860f21892bf09bde80 100644 (file)
@@ -2,7 +2,7 @@
 <article>
 <title>Squid Programmers Guide</title>
 <author>Duane Wessels, Squid Developers
-<date>$Id: prog-guide.sgml,v 1.34 2001/01/07 23:36:35 hno Exp $</date>
+<date>$Id: prog-guide.sgml,v 1.35 2001/01/31 22:16:36 hno Exp $</date>
 
 <abstract>
 Squid is a WWW Cache application developed by the National Laboratory
@@ -2063,10 +2063,15 @@ coupling between the storage layer and the replacement policy.
        <P>typedef int   AUTHSACTIVE();
                
        <P>The Active function is used by squid to determine whether the auth
-        module has successfully configured and initialised itself. If Active
-        returns 0 no other module functions except Shutdown/Dump/Parse/FreeConfig
-       will be called by Squid.
-               
+        module has successfully initialised itself with the current configuration.
+
+       <P>typedef int   AUTHSCONFIGURED();
+
+       <P>The configured function is used to see if the auth module has been given 
+       valid parameters and is able to handle authentication requests if initialised.
+       If configured returns 0 no other module functions except 
+       Shutdown/Dump/Parse/FreeConfig will be called by Squid.
+
        <P>typedef void  AUTHSSETUP(authscheme_entry_t *);
                
        <P>functions of type AUTHSSETUP are used to register an auth module with
diff --git a/helpers/digest_auth/Makefile.in b/helpers/digest_auth/Makefile.in
new file mode 100644 (file)
index 0000000..4fb2616
--- /dev/null
@@ -0,0 +1,38 @@
+#  Makefile for digest auth helpers in the Squid Object Cache server
+#
+#  $Id: Makefile.in,v 1.1 2001/01/31 22:16:42 hno Exp $
+#
+
+# 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;
diff --git a/helpers/digest_auth/password/Makefile.in b/helpers/digest_auth/password/Makefile.in
new file mode 100644 (file)
index 0000000..ec9b4b8
--- /dev/null
@@ -0,0 +1,100 @@
+#
+#  Makefile for the Squid Object Cache server
+#
+#  $Id: Makefile.in,v 1.1 2001/01/31 22:16:42 hno Exp $
+#
+#  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 <mike@nemesis.saar.de> 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
diff --git a/helpers/digest_auth/password/digest_pw_auth.c b/helpers/digest_auth/password/digest_pw_auth.c
new file mode 100644 (file)
index 0000000..dc921a1
--- /dev/null
@@ -0,0 +1,158 @@
+/*
+ * digest_pw_auth.c
+ *
+ * AUTHOR: Robert Collins. Based on ncsa_auth.c by Arjan de Vet <Arjan.deVet@adv.iae.nl>
+ *
+ * Example digest authentication program for Squid, based on the original
+ * proxy_auth code from client_side.c, written by
+ * Jon Thackray <jrmt@uk.gdscorp.com>.
+ *
+ * - 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 <stdio.h>
+#endif
+#if HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#if HAVE_STRING_H
+#include <string.h>
+#endif
+#if HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#if HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#if HAVE_CRYPT_H
+#include <crypt.h>
+#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 <passwordfile>\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 6c9ef0859f3006b4767d5451f64198b8875d62ae..f888ebf5bf590ac3f3077352ae58a1ded54664bc 100644 (file)
 #include <unistd.h>
 #endif
 
+/* these are part of rfcnb-priv.h and smblib-priv.h */
+extern int SMB_Get_Error_Msg(int msg, char *msgbuf, int len);
+extern int SMB_Get_Last_Error();
+extern int RFCNB_Get_Last_Errno();
+
 #include "smblib-priv.h"       /* for SMB_Handle_Type */
 
 /* a few forward-declarations. Hackish, but I don't care right now */
-SMB_Handle_Type SMB_Connect_Server(SMB_Handle_Type Con_Handle,
-    char *server, char *NTdomain);
+SMB_Handle_Type SMB_Connect_Server(SMB_Handle_Type Con_Handle, char *server, char *NTdomain);
 
 /* this one is reallllly haackiish. We really should be using anything from smblib-priv.h
  */
@@ -49,13 +53,13 @@ static char *SMB_Prots[] =
     "Samba",
     "NT LM 0.12",
     "NT LANMAN 1.0",
-    NULL};
+    NULL
+};
 
 #if 0
 int SMB_Discon(SMB_Handle_Type Con_Handle, BOOL KeepHandle);
 int SMB_Negotiate(void *Con_Handle, char *Prots[]);
-int SMB_Logon_Server(SMB_Handle_Type Con_Handle, char *UserName,
-    char *PassWord, char *Domain, int precrypted);
+int SMB_Logon_Server(SMB_Handle_Type Con_Handle, char *UserName, char *PassWord, char *Domain, int precrypted);
 #endif
 
 #ifdef DEBUG
@@ -87,8 +91,7 @@ connectedp()
 
 /* Tries to connect to a DC. Returns 0 on failure, 1 on OK */
 int
-is_dc_ok(char *domain,
-    char *domain_controller)
+is_dc_ok(char *domain, char *domain_controller)
 {
     SMB_Handle_Type h = SMB_Connect_Server(NULL, domain_controller, domain);
     if (h == NULL)
@@ -137,21 +140,46 @@ init_challenge(char *domain, char *domain_controller)
 const char *
 make_challenge(char *domain, char *domain_controller)
 {
-    if (init_challenge(domain, domain_controller) > 0)
+    if (init_challenge(domain, domain_controller) > 0) {
        return NULL;
-    return ntlm_make_challenge(domain, domain_controller, challenge,
-       NONCE_LEN);
+    }
+    return ntlm_make_challenge(domain, domain_controller, challenge, NONCE_LEN);
 }
 
 #define min(A,B) (A<B?A:B)
+
+int ntlm_errno;
+static char credentials[1024]; /* we can afford to waste */
+
+/* Fetches the user's credentials from the challenge.
+ * Returns NULL if domain or user is not defined
+ * No identity control is performed.
+ * WARNING! The result is static storage, shared with ntlm_check_auth
+ */
+char *
+fetch_credentials(ntlm_authenticate * auth, int auth_length)
+{
+    char *p = credentials;
+    lstring tmp;
+    tmp = ntlm_fetch_string((char *) auth, auth_length, &auth->domain);
+    if (tmp.str == NULL)
+       return NULL;
+    memcpy(p, tmp.str, tmp.l);
+    p += tmp.l;
+    *p++ = '\\';
+    tmp = ntlm_fetch_string((char *) auth, auth_length, &auth->user);
+    if (tmp.str == NULL)
+       return NULL;
+    *(p + tmp.l) = '\0';
+    return credentials;
+}
+
 /* returns NULL on failure, or a pointer to
  * the user's credentials (domain\\username)
  * upon success. WARNING. It's pointing to static storage.
  * In case of problem sets as side-effect ntlm_errno to one of the
  * codes defined in ntlm.h
  */
-int ntlm_errno;
-static char credentials[1024]; /* we can afford to waste */
 char *
 ntlm_check_auth(ntlm_authenticate * auth, int auth_length)
 {
@@ -200,21 +228,13 @@ ntlm_check_auth(ntlm_authenticate * auth, int auth_length)
     debug("checking domain: '%s', user: '%s', pass='%s'\n", domain, user, pass);
 
     rv = SMB_Logon_Server(handle, user, pass, domain, 1);
-
-    while ((rv == NTLM_BAD_PROTOCOL || rv == NTLM_SERVER_ERROR)
-       && retries < BAD_DC_RETRIES_NUMBER) {
-       retries++;
-       usleep((unsigned long) 100000);
-       rv = SMB_Logon_Server(handle, user, pass, domain, 1);
-    }
-
-    debug("\tresult is %d\n", rv);
+    debug("Login attempt had result %d\n", rv);
 
     if (rv != NTV_NO_ERROR) {  /* failed */
        ntlm_errno = rv;
        return NULL;
     }
-    *(user - 1) = '\\';
+    *(user - 1) = '\\';                /* hack. Performing, but ugly. */
 
     debug("credentials: %s\n", credentials);
     return credentials;
index 65b65915a58a1d02eb3257170a5a169634a0f721..df48fb2468fbd4a24371d341100a6543fd1cec62 100644 (file)
 /*
  * define this if you want debugging
  */
-#define DEBUG
-
-/*
- * Number of authentication attempts to perform in case of certain errors
- */
-#define BAD_DC_RETRIES_NUMBER 3
+#ifndef DEBUG
+/* #define DEBUG */
+#endif
 
 /************* END CONFIGURATION ***************/
 
@@ -74,12 +71,14 @@ extern int ntlm_errno;
 #define NTLM_SERVER_ERROR 1
 #define NTLM_PROTOCOL_ERROR 2
 #define NTLM_LOGON_ERROR 3
+#define NTLM_UNTRUSTED_DOMAIN 4
 #define NTLM_BAD_PROTOCOL -1
 #define NTLM_NOT_CONNECTED 10
 
 
 const char *make_challenge(char *domain, char *controller);
 extern char *ntlm_check_auth(ntlm_authenticate * auth, int auth_length);
+extern char *fetch_credentials(ntlm_authenticate * auth, int auth_length);
 void dc_disconnect(void);
 int connectedp(void);
 int is_dc_ok(char *domain, char *domain_controller);
index 8656f64a9b27a877800161416a6ad9b8efbab942..774c8eacd7facdb13437d658e4d3ae39e67dcc20 100644 (file)
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
  *
- * Warning! We MIGHT be open to buffer overflows caused by malformed headers
- *
- * DONE list:
- * use hashtable to cache authentications. Yummy performance-boost, security
- *  loss should be negligible for two reasons:
- *   - if they-re using NT, there's no security to speak of anyways
- *   - it can't get worse than basic authentication.
- * cache expiration
- * challenge hash expiry and renewal.
- * PDC disconnect, after X minutes of inactivity
- *
- * TODO list:
- * change syntax from options-driven to args-driven, with args domain
- *   or domain[/\]server, and an arbitrary number of backup Domain Controllers
- * we don't really need the "status" management, it's more for debugging
- *  purposes. Remove it.
- * Maybe we can cache the created challenge, saving more time?
- *
  */
 
 
 #include "ntlm.h"
 #include "util.h"
 
+/* these are part of rfcnb-priv.h and smblib-priv.h */
+extern int SMB_Get_Error_Msg(int msg, char *msgbuf, int len);
+extern int SMB_Get_Last_Error();
+extern int SMB_Get_Last_SMB_Err();
+
+
 #define BUFFER_SIZE 10240
 
 #if HAVE_STDLIB_H
 #include <stdlib.h>
 #endif
-
-
 #if HAVE_GETOPT_H
 #include <getopt.h>
 #endif
-
-
-
 #ifdef HAVE_STRING_H
 #include <string.h>
 #endif
 #include <ctype.h>
 #endif
 
-char load_balance = 0, failover_enabled = 0, protocol_pedantic = 0;
+#ifdef DEBUG
+char error_messages_buffer[BUFFER_SIZE];
+#endif
+
+char load_balance = 0, failover_enabled = 0, protocol_pedantic = 0, last_ditch_enabled = 0;
 
 dc *controllers = NULL;
 int numcontrollers = 0;
 dc *current_dc;
 
+char smb_error_buffer[1000];
+
 /* housekeeping cycle and periodic operations */
 static unsigned char need_dc_resurrection = 0;
 static void
@@ -100,10 +89,28 @@ lc(char *string)
     }
 }
 
+
+void
+send_bh_or_ld(char *bhmessage, ntlm_authenticate * failedauth, int authlen)
+{
+    char *creds = NULL;
+    if (last_ditch_enabled) {
+       creds = fetch_credentials(failedauth, authlen);
+       if (creds) {
+           SEND2("LD %s", creds);
+       } else {
+           SEND("NA last-ditch on, but no credentials");
+       }
+    } else {
+       SEND(bhmessage);
+    }
+}
+
 /*
  * options:
  * -b try load-balancing the domain-controllers
  * -f fail-over to another DC if DC connection fails.
+ * -l last-ditch-mode
  * domain\controller ...
  */
 char *my_program_name = NULL;
@@ -115,12 +122,12 @@ usage()
        "%s usage:\n"
        "%s [-b] [-f] domain\\controller [domain\\controller ...]\n"
        "-b, if specified, enables load-balancing among controllers\n"
-       "-f, if specified, enables failover among controllers\n\n"
-       "You MUST specify at least one Domain Controller.\n"
+       "-f, if specified, enables failover among controllers\n"
+       "-l, if specified, changes behavior on domain controller failyures to"
+       "\tlast-ditch\n\n" "You MUST specify at least one Domain Controller.\n"
        "You can use either \\ or / as separator between the domain name \n"
-       "\tand the controller name\n"
-       ,my_program_name
-       ,my_program_name);
+       "\tand the controller name\n",
+       my_program_name, my_program_name);
 }
 
 
@@ -129,7 +136,7 @@ process_options(int argc, char *argv[])
 {
     int opt, j, had_error = 0;
     dc *new_dc = NULL, *last_dc = NULL;
-    while (-1 != (opt = getopt(argc, argv, "bf"))) {
+    while (-1 != (opt = getopt(argc, argv, "bfl"))) {
        switch (opt) {
        case 'b':
            load_balance = 1;
@@ -137,6 +144,9 @@ process_options(int argc, char *argv[])
        case 'f':
            failover_enabled = 1;
            break;
+       case 'l':
+           last_ditch_enabled = 1;
+           break;
        default:
            fprintf(stderr, "unknown option: -%c. Exiting\n", opt);
            usage();
@@ -149,15 +159,27 @@ process_options(int argc, char *argv[])
     /* we can avoid memcpy-ing, and just reuse argv[] */
     for (j = optind; j < argc; j++) {
        char *d, *c;
-       d = argv[j];
+       /* d will not be freed in case of non-error. Since we don't reconfigure,
+        * it's going to live as long as the process anyways */
+       d = malloc(strlen(argv[j]) + 1);
+       strcpy(d, argv[j]);
+       debug("Adding domain-controller %s\n", d);
        if (NULL == (c = strchr(d, '\\')) && NULL == (c = strchr(d, '/'))) {
            fprintf(stderr, "Couldn't grok domain-controller %s\n", d);
+           free(d);
+           continue;
+       }
+       /* more than one delimiter is not allowed */
+       if (NULL != strchr(c + 1, '\\') || NULL != strchr(c + 1, '/')) {
+           fprintf(stderr, "Broken domain-controller %s\n", d);
+           free(d);
            continue;
        }
        *c++ = '\0';
        new_dc = (dc *) malloc(sizeof(dc));
        if (!new_dc) {
            fprintf(stderr, "Malloc error while parsing DC options\n");
+           free(d);
            continue;
        }
        /* capitalize */
@@ -191,12 +213,15 @@ obtain_challenge()
 {
     int j = 0;
     const char *ch;
+    debug("obtain_challenge: getting new challenge\n");
     for (j = 0; j < numcontrollers; j++) {
        if (current_dc->status == DC_OK) {
+           debug("getting challenge from %s\%s\n", current_dc->domain, current_dc->controller);
            ch = make_challenge(current_dc->domain, current_dc->controller);
            if (ch)
                return ch;      /* All went OK, returning */
            /* Huston, we've got a problem. Take this DC out of the loop */
+           debug("Marking DC as DEAD\n");
            current_dc->status = DC_DEAD;
            need_dc_resurrection = 1;
        }
@@ -205,14 +230,16 @@ obtain_challenge()
        /* Try with the next */
        current_dc = current_dc->next;
     }
+    /* DC (all DCs if failover is enabled) failed. */
     return NULL;
 }
 
+
 void
 manage_request()
 {
     ntlmhdr *fast_header;
-    char buf[10240];
+    char buf[BUFFER_SIZE];
     const char *ch;
     char *ch2, *decoded, *cred;
     int plen;
@@ -259,36 +286,79 @@ manage_request()
            plen = strlen(buf) * 3 / 4;         /* we only need it here. Optimization */
            cred = ntlm_check_auth((ntlm_authenticate *) decoded, plen);
            if (cred == NULL) {
+               int errorclass, errorcode;
+#ifdef DEBUG
+               SMB_Get_Error_Msg(SMB_Get_Last_SMB_Err(),
+                   error_messages_buffer, BUFFER_SIZE);
+               debug("Authentication failure. SMB error: %d: %s\n. Class=%d, "
+                   "Code=%d\n",
+                   SMB_Get_Last_SMB_Err(), error_messages_buffer,
+                   SMB_Get_Last_SMB_Err() & 0xff, SMB_Get_Last_SMB_Err() >> 16);
+#endif
+               /* This is kind of a special case, which happens when the
+                * client sends credentials in a domain which is not trusted
+                * by the domain we're using when authenticating. Unfortunately,
+                * it can't currently be accommodated in the current framework so
+                * I'll leave it hanging here, waiting for the general framework
+                * to be expanded to better accommodate the generale case. */
+               errorclass = SMB_Get_Last_SMB_Err() & 0xff;
+               errorcode = SMB_Get_Last_SMB_Err() >> 16;
+               if (errorclass == 1 && errorcode == 5) {
+                   SEND("NA Wrong password or untrusted domain");
+                   return;
+               }
                switch (ntlm_errno) {
                case NTLM_LOGON_ERROR:
                    SEND("NA authentication failure");
-                   dc_disconnect();
-                   current_dc = current_dc->next;
+                   /* I must have been drugged when I wrote the following two lines */
+                   /* dc_disconnect();
+                    * current_dc = current_dc->next; */
                    return;
                case NTLM_SERVER_ERROR:
-                   SEND("BH Domain Controller Error");
-                   dc_disconnect();
-                   current_dc = current_dc->next;
+                   send_bh_or_ld("BH Domain Controller Error", (ntlm_authenticate *) decoded, plen);
+                   /* SEND("BH Domain Controller Error"); */
+                   /* we don't really need to disconnect NOW.
+                    * Besides, we asked squid to force a reconnect. This way, if we
+                    * have authentications in flight, we might even succeed.
+                    */
+                   /* dc_disconnect(); */
+
+                   SMB_Get_Error_Msg(SMB_Get_Last_Error(), smb_error_buffer, 1000);
+                   debug("Last error was: %s, RFC errno=%d\n", smb_error_buffer,
+                       RFCNB_Get_Last_Errno());
+                   if (failover_enabled)
+                       current_dc = current_dc->next;
                    return;
                case NTLM_PROTOCOL_ERROR:
-                   SEND("BH Domain Controller communication error");
-                   dc_disconnect();
-                   current_dc = current_dc->next;
+                   send_bh_or_ld("BH Domain Controller communication error", (ntlm_authenticate *) decoded, plen);
+                   /* SEND("BH Domain Controller communication error"); */
+                   /* dc_disconnect(); */
+                   if (failover_enabled)
+                       current_dc = current_dc->next;
                    return;
                case NTLM_NOT_CONNECTED:
-                   SEND("BH Domain Controller (or network) died on us");
-                   dc_disconnect();
-                   current_dc = current_dc->next;
+                   send_bh_or_ld("BH Domain Controller (or network) died on us", (ntlm_authenticate *) decoded, plen);
+                   /* SEND("BH Domain Controller (or network) died on us"); */
+                   /* dc_disconnect(); */
+                   if (failover_enabled)
+                       current_dc = current_dc->next;
                    return;
                case NTLM_BAD_PROTOCOL:
-                   SEND("BH Domain controller failure");
-                   dc_disconnect();
-                   current_dc = current_dc->next;
+                   send_bh_or_ld("BH Domain controller failure", (ntlm_authenticate *) decoded, plen);
+                   /* SEND("BH Domain controller failure"); */
+                   /* dc_disconnect(); *//* maybe we're overreacting? */
+                   SMB_Get_Error_Msg(SMB_Get_Last_Error(), smb_error_buffer, 1000);
+                   debug("Last error was: %s. RFCNB errno=%d\n", smb_error_buffer,
+                       RFCNB_Get_Last_Errno());
+                   if (failover_enabled)
+                       current_dc = current_dc->next;
                    return;
                default:
-                   SEND("BH Unhandled error while talking to Domain Controller");
-                   dc_disconnect();
-                   current_dc = current_dc->next;
+                   send_bh_or_ld("BH Unhandled error while talking to Domain Controller", (ntlm_authenticate *) decoded, plen);
+                   /* SEND("BH Unhandled error while talking to Domain Controller"); */
+                   /* dc_disconnect(); *//* maybe we're overreacting? */
+                   if (failover_enabled)
+                       current_dc = current_dc->next;
                    return;
                }
            }
@@ -306,6 +376,8 @@ manage_request()
     if (memcmp(buf, "YR", 2) == 0) {   /* refresh-request */
        dc_disconnect();
        ch = obtain_challenge();
+       /* Robert says we can afford to wait forever. I'll trust him on this
+        * one */
        while (ch == NULL) {
            sleep(30);
            ch = obtain_challenge();
@@ -326,7 +398,7 @@ int
 main(int argc, char *argv[])
 {
 
-    debug("starting up...\n");
+    debug("ntlm_auth build " __DATE__ ", " __TIME__ " starting up...\n");
 
     my_program_name = argv[0];
     process_options(argc, argv);
index 001bf9bfe4bd135e7be31e7d2bacf29275bb31e2..cfbfa3297e1f1e859282ee13fd550bf49ae88834 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * $Id: md5.h,v 1.6 1998/09/23 17:19:59 wessels Exp $
+ * $Id: md5.h,v 1.7 2001/01/31 22:16:36 hno Exp $
  */
 
 #ifndef MD5_H
@@ -37,7 +37,7 @@ typedef struct {
 } 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
diff --git a/include/rfc2617.h b/include/rfc2617.h
new file mode 100644 (file)
index 0000000..189ec88
--- /dev/null
@@ -0,0 +1,88 @@
+/* The source in this file is derived from the reference implementation
+ * in RFC 2617.
+ * RFC 2617 is Copyright (C) The Internet Society (1999).  All Rights Reserved.
+ *
+ * The following copyright and licence statement covers all changes made to the
+ * reference implementation.
+ *
+ * Key changes to the reference implementation were: 
+ * alteration to a plain C layout.
+ * Create CvtBin function
+ * Allow CalcHA1 to make use of precaculated username:password:realm hash's
+ * to prevent squid knowing the users password (idea suggested in RFC 2617).
+ */
+
+
+/*
+ * $Id: rfc2617.h,v 1.1 2001/01/31 22:16:36 hno Exp $
+ *
+ * DEBUG:
+ * AUTHOR: RFC 2617 & 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.
+ *
+ */
+
+#ifndef RFC2617_H
+#define RFC2617_H
+#include "md5.h"
+
+#define HASHLEN 16
+typedef char HASH[HASHLEN];
+#define HASHHEXLEN 32
+typedef char HASHHEX[HASHHEXLEN + 1];
+
+/* calculate H(A1) as per HTTP Digest spec */
+void DigestCalcHA1(
+    const char *pszAlg,
+    const char *pszUserName,
+    const char *pszRealm,
+    const char *pszPassword,
+    const char *pszNonce,
+    const char *pszCNonce,
+    HASH HA1,
+    HASHHEX SessionKey
+);
+
+/* calculate request-digest/response-digest as per HTTP Digest spec */
+void DigestCalcResponse(
+    const HASHHEX HA1,         /* H(A1) */
+    const char *pszNonce,      /* nonce from server */
+    const char *pszNonceCount, /* 8 hex digits */
+    const char *pszCNonce,     /* client nonce */
+    const char *pszQop,                /* qop-value: "", "auth", "auth-int" */
+    const char *pszMethod,     /* method from the request */
+    const char *pszDigestUri,  /* requested URL */
+    const HASHHEX HEntity,     /* H(entity body) if qop="auth-int" */
+    HASHHEX Response           /* request-digest or response-digest */
+);
+
+void CvtHex(const HASH Bin, HASHHEX Hex);
+
+void CvtBin(const HASHHEX Hex, HASH Bin);
+
+#endif /* RFC 2617 */
index 5ae7ab5cd0a668a0d95e536814e159aada4c6a2d..1969455acec1d7ea10a58cb22f328e5119ba7a83 100644 (file)
@@ -1,5 +1,5 @@
 #
-#  $Id: Makefile.in,v 1.48 2001/01/07 23:36:36 hno Exp $
+#  $Id: Makefile.in,v 1.49 2001/01/31 22:16:37 hno Exp $
 #
 prefix         = @prefix@
 top_srcdir     = @top_srcdir@
@@ -24,6 +24,7 @@ INCLUDE               = -I../include -I$(top_srcdir)/include
 UTILOBJS       = rfc1123.o \
                  rfc1738.o \
                  rfc1035.o \
+                 rfc2617.o \
                  util.o \
                  getfullhostname.o \
                  base64.o \
index ef95a437fc361ad9721a7a4a948ac175be757766..0775fbf4e53165decadc0b159da0ba07d4ed1aed 100644 (file)
--- a/lib/md5.c
+++ b/lib/md5.c
@@ -1,5 +1,5 @@
 /*
- * $Id: md5.c,v 1.9 1999/05/04 21:20:39 wessels Exp $
+ * $Id: md5.c,v 1.10 2001/01/31 22:16:37 hno 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 @@ MD5Init(MD5_CTX * context)
  * 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 @@ MD5Final(unsigned char digest[16], MD5_CTX * context)
  * 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 @@ Encode(unsigned char *output, u_num32 * input, unsigned int len)
  * 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;
 
diff --git a/lib/rfc2617.c b/lib/rfc2617.c
new file mode 100644 (file)
index 0000000..01942fb
--- /dev/null
@@ -0,0 +1,177 @@
+/* The source in this file is derived from the reference implementation 
+ * in RFC 2617. 
+ * RFC 2617 is Copyright (C) The Internet Society (1999).  All Rights Reserved.
+ *
+ * The following copyright and licence statement covers all changes made to the 
+ * reference implementation.
+ * 
+ * Key changes were: alteration to a plain C layout.
+ * Create CvtBin function
+ * Allow CalcHA1 to make use of precaculated username:password:realm hash's
+ * to prevent squid knowing the users password (idea suggested in RFC 2617).
+ */
+
+
+/*
+ * $Id: rfc2617.c,v 1.1 2001/01/31 22:16:37 hno Exp $
+ *
+ * DEBUG:
+ * AUTHOR: RFC 2617 & 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.
+ *
+ */
+
+#include "config.h"
+#include <string.h>
+#include "rfc2617.h"
+
+void 
+CvtHex(const HASH Bin, HASHHEX Hex)
+{
+    unsigned short i;
+    unsigned char j;
+
+    for (i = 0; i < HASHLEN; i++) {
+       j = (Bin[i] >> 4) & 0xf;
+       if (j <= 9)
+           Hex[i * 2] = (j + '0');
+       else
+           Hex[i * 2] = (j + 'a' - 10);
+       j = Bin[i] & 0xf;
+       if (j <= 9)
+           Hex[i * 2 + 1] = (j + '0');
+       else
+           Hex[i * 2 + 1] = (j + 'a' - 10);
+    };
+    Hex[HASHHEXLEN] = '\0';
+};
+
+void 
+CvtBin(const HASHHEX Hex, HASH Bin)
+{
+    unsigned short i;
+    unsigned char j;
+
+    for (i = 0; i < HASHHEXLEN; i++) {
+       j = Hex[i];
+       if (('0' <= j) && (j <= '9'))
+           Bin[i / 2] |= ((j - '0') << ((i % 2 == 0) ? 4 : 0));
+       else
+           Bin[i / 2] |= ((j - 'a' + 10) << ((i % 2 == 0) ? 4 : 0));
+    };
+    Bin[HASHLEN] = '\0';
+};
+
+
+/* calculate H(A1) as per spec */
+void 
+DigestCalcHA1(
+    const char *pszAlg,
+    const char *pszUserName,
+    const char *pszRealm,
+    const char *pszPassword,
+    const char *pszNonce,
+    const char *pszCNonce,
+    HASH HA1,
+    HASHHEX SessionKey
+)
+{
+    MD5_CTX Md5Ctx;
+
+    if (pszUserName) {
+       MD5Init(&Md5Ctx);
+       MD5Update(&Md5Ctx, pszUserName, strlen(pszUserName));
+       MD5Update(&Md5Ctx, ":", 1);
+       MD5Update(&Md5Ctx, pszRealm, strlen(pszRealm));
+       MD5Update(&Md5Ctx, ":", 1);
+       MD5Update(&Md5Ctx, pszPassword, strlen(pszPassword));
+       MD5Final(HA1, &Md5Ctx);
+    }
+    if (stricmp(pszAlg, "md5-sess") == 0) {
+       MD5Init(&Md5Ctx);
+       MD5Update(&Md5Ctx, HA1, HASHLEN);
+       MD5Update(&Md5Ctx, ":", 1);
+       MD5Update(&Md5Ctx, pszNonce, strlen(pszNonce));
+       MD5Update(&Md5Ctx, ":", 1);
+       MD5Update(&Md5Ctx, pszCNonce, strlen(pszCNonce));
+       MD5Final(HA1, &Md5Ctx);
+    };
+    CvtHex(HA1, SessionKey);
+};
+
+/* calculate request-digest/response-digest as per HTTP Digest spec */
+void 
+DigestCalcResponse(
+    const HASHHEX HA1,         /* H(A1) */
+    const char *pszNonce,      /* nonce from server */
+    const char *pszNonceCount, /* 8 hex digits */
+    const char *pszCNonce,     /* client nonce */
+    const char *pszQop,                /* qop-value: "", "auth", "auth-int" */
+    const char *pszMethod,     /* method from the request */
+    const char *pszDigestUri,  /* requested URL */
+    const HASHHEX HEntity,     /* H(entity body) if qop="auth-int" */
+    HASHHEX Response           /* request-digest or response-digest */
+)
+{
+    MD5_CTX Md5Ctx;
+    HASH HA2;
+    HASH RespHash;
+    HASHHEX HA2Hex;
+
+    /*  calculate H(A2)
+     */
+    MD5Init(&Md5Ctx);
+    MD5Update(&Md5Ctx, pszMethod, strlen(pszMethod));
+    MD5Update(&Md5Ctx, ":", 1);
+    MD5Update(&Md5Ctx, pszDigestUri, strlen(pszDigestUri));
+    if (stricmp(pszQop, "auth-int") == 0) {
+       MD5Update(&Md5Ctx, ":", 1);
+       MD5Update(&Md5Ctx, HEntity, HASHHEXLEN);
+    };
+    MD5Final(HA2, &Md5Ctx);
+    CvtHex(HA2, HA2Hex);
+
+    /* calculate response
+     */
+    MD5Init(&Md5Ctx);
+    MD5Update(&Md5Ctx, HA1, HASHHEXLEN);
+    MD5Update(&Md5Ctx, ":", 1);
+    MD5Update(&Md5Ctx, pszNonce, strlen(pszNonce));
+    MD5Update(&Md5Ctx, ":", 1);
+    if (*pszQop) {
+       MD5Update(&Md5Ctx, pszNonceCount, strlen(pszNonceCount));
+       MD5Update(&Md5Ctx, ":", 1);
+       MD5Update(&Md5Ctx, pszCNonce, strlen(pszCNonce));
+       MD5Update(&Md5Ctx, ":", 1);
+       MD5Update(&Md5Ctx, pszQop, strlen(pszQop));
+       MD5Update(&Md5Ctx, ":", 1);
+    };
+    MD5Update(&Md5Ctx, HA2Hex, HASHHEXLEN);
+    MD5Final(RespHash, &Md5Ctx);
+    CvtHex(RespHash, Response);
+};
index 63f29b0da21da1ef0540d47431c50591a6494508..8bfe738b6b49591591e98d37ef445877ca01bfdb 100644 (file)
@@ -1,6 +1,6 @@
 
 /*
- * $Id: acl.cc,v 1.243 2001/01/31 21:48:24 hno Exp $
+ * $Id: acl.cc,v 1.244 2001/01/31 22:16:38 hno Exp $
  *
  * DEBUG: section 28    Access Control
  * AUTHOR: Duane Wessels
@@ -1217,7 +1217,7 @@ aclMatchProxyAuth(void *data, http_hdr_type headertype,
            if (!authenticateValidateUser(auth_user_request = authenticateGetAuthUser(proxy_auth))) {
                /* the decode might have left a username for logging, or a message to
                 * the user */
-               if (auth_user_request) {
+               if (authenticateUserRequestUsername(auth_user_request)) {
                    /* lock the user for the request structure link */
                    authenticateAuthUserRequestLock(auth_user_request);
                    checklist->request->auth_user_request = auth_user_request;
index 13e4fcaebb1a81e0e1507c4af645d038e82db778..82ed246c3997925319f312fa26e8906a0f4b6149 100644 (file)
@@ -1,6 +1,6 @@
 
 /*
- * $Id: auth_basic.cc,v 1.4 2001/01/12 00:37:28 wessels Exp $
+ * $Id: auth_basic.cc,v 1.5 2001/01/31 22:16:40 hno Exp $
  *
  * DEBUG: section 29    Authenticator
  * AUTHOR: Duane Wessels
@@ -53,6 +53,7 @@ static HLPCB authenticateBasicHandleReply;
 static AUTHSACTIVE authenticateBasicActive;
 static AUTHSAUTHED authenticateBasicAuthenticated;
 static AUTHSAUTHUSER authenticateBasicAuthenticateUser;
+static AUTHSCONFIGURED authBasicConfigured;
 static AUTHSDIRECTION authenticateBasicDirection;
 static AUTHSDECODE authenticateBasicDecodeAuth;
 static AUTHSDUMP authBasicCfgDump;
@@ -107,6 +108,7 @@ authSchemeSetup_basic(authscheme_entry_t * authscheme)
     authscheme->init = authBasicInit;
     authscheme->authAuthenticate = authenticateBasicAuthenticateUser;
     authscheme->authenticated = authenticateBasicAuthenticated;
+    authscheme->configured = authBasicConfigured;
     authscheme->authFixHeader = authenticateBasicFixErrorHeader;
     authscheme->FreeUser = authenticateBasicFreeUser;
     authscheme->freeconfig = authBasicFreeConfig;
@@ -121,10 +123,20 @@ authSchemeSetup_basic(authscheme_entry_t * authscheme)
 
 int
 authenticateBasicActive()
+{
+    return (authbasic_initialised == 1) ? 1 : 0;
+}
+
+int
+authBasicConfigured()
 {
     if ((basicConfig != NULL) && (basicConfig->authenticate != NULL) &&
-       (basicConfig->authenticateChildren != 0) && (basicConfig->basicAuthRealm != NULL))
+       (basicConfig->authenticateChildren != 0) &&
+       (basicConfig->basicAuthRealm != NULL)) {
+       debug(29, 9) ("authBasicConfigured: returning configured\n");
        return 1;
+    }
+    debug(29, 9) ("authBasicConfigured: returning unconfigured\n");
     return 0;
 }
 
@@ -308,6 +320,8 @@ authBasicParse(authScheme * scheme, int n_configured, char *param_str)
     }
     basicConfig = scheme->scheme_data;
     if (strcasecmp(param_str, "program") == 0) {
+       if (basicConfig->authenticate)
+           wordlistDestroy(&basicConfig->authenticate);
        parse_wordlist(&basicConfig->authenticate);
        requirePathnameExists("authparam basic program", basicConfig->authenticate->key);
     } else if (strcasecmp(param_str, "children") == 0) {
diff --git a/src/auth/digest/Makefile.in b/src/auth/digest/Makefile.in
new file mode 100644 (file)
index 0000000..d71dc59
--- /dev/null
@@ -0,0 +1,70 @@
+#
+#  Makefile for the Digest authentication scheme modulefor the Squid Object Cache server
+#
+#  $Id: Makefile.in,v 1.1 2001/01/31 22:16:41 hno Exp $
+#
+
+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
diff --git a/src/auth/digest/auth_digest.cc b/src/auth/digest/auth_digest.cc
new file mode 100644 (file)
index 0000000..ae909dc
--- /dev/null
@@ -0,0 +1,1323 @@
+
+/*
+ * $Id: auth_digest.cc,v 1.1 2001/01/31 22:16:41 hno Exp $
+ *
+ * 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 : "<NULL>");
+    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) {
+       if (digestConfig->authenticate)
+           wordlistDestroy(&digestConfig->authenticate);
+       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 - item < ilen))
+           ilen = p++ - item;
+       if (!strncmp(item, "username", ilen)) {
+           /* white space */
+           while (xisspace(*p))
+               p++;
+           /* quote mark */
+           p++;
+           username = xstrndup(p, strchr(p, '"') + 1 - p);
+           debug(29, 9) ("authDigestDecodeAuth: Found Username '%s'\n", username);
+       } else if (!strncmp(item, "realm", ilen)) {
+           /* white space */
+           while (xisspace(*p))
+               p++;
+           /* quote mark */
+           p++;
+           digest_request->realm = 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);
+}
diff --git a/src/auth/digest/auth_digest.h b/src/auth/digest/auth_digest.h
new file mode 100644 (file)
index 0000000..d500c16
--- /dev/null
@@ -0,0 +1,100 @@
+/*
+ * 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
index 9c7e775e0ea0d86cf757953b65e88fb8c74ebc78..31079c1e074d0bca9227a6d9c20c5976c482ec2e 100644 (file)
@@ -1,6 +1,6 @@
 
 /*
- * $Id: auth_ntlm.cc,v 1.5 2001/01/12 00:37:30 wessels Exp $
+ * $Id: auth_ntlm.cc,v 1.6 2001/01/31 22:16:43 hno Exp $
  *
  * DEBUG: section 29    NTLM Authenticator
  * AUTHOR: Robert Collins
@@ -53,6 +53,7 @@ static HLPSCB authenticateNTLMHandleplaceholder;
 static AUTHSACTIVE authenticateNTLMActive;
 static AUTHSAUTHED authNTLMAuthenticated;
 static AUTHSAUTHUSER authenticateNTLMAuthenticateUser;
+static AUTHSCONFIGURED authNTLMConfigured;
 static AUTHSFIXERR authenticateNTLMFixErrorHeader;
 static AUTHSFREE authenticateNTLMFreeUser;
 static AUTHSDIRECTION authenticateNTLMDirection;
@@ -94,6 +95,7 @@ static hash_table *proxy_auth_cache = NULL;
 void
 authNTLMDone(void)
 {
+    debug(29, 2) ("authNTLMDone: shutting down NTLM authentication.\n");
     if (ntlmauthenticators)
        helperStatefulShutdown(ntlmauthenticators);
     authntlm_initialised = 0;
@@ -165,6 +167,8 @@ authNTLMParse(authScheme * scheme, int n_configured, char *param_str)
     }
     ntlmConfig = scheme->scheme_data;
     if (strcasecmp(param_str, "program") == 0) {
+       if (ntlmConfig->authenticate)
+           wordlistDestroy(&ntlmConfig->authenticate);
        parse_wordlist(&ntlmConfig->authenticate);
        requirePathnameExists("authparam ntlm program", ntlmConfig->authenticate->key);
     } else if (strcasecmp(param_str, "children") == 0) {
@@ -184,6 +188,7 @@ authSchemeSetup_ntlm(authscheme_entry_t * authscheme)
 {
     assert(!authntlm_initialised);
     authscheme->Active = authenticateNTLMActive;
+    authscheme->configured = authNTLMConfigured;
     authscheme->parse = authNTLMParse;
     authscheme->dump = authNTLMCfgDump;
     authscheme->requestFree = authNTLMAURequestFree;
@@ -242,11 +247,21 @@ authNTLMInit(authScheme * scheme)
 
 int
 authenticateNTLMActive()
+{
+    return (authntlm_initialised == 1) ? 1 : 0;
+}
+
+
+int
+authNTLMConfigured()
 {
     if ((ntlmConfig != NULL) && (ntlmConfig->authenticate != NULL) &&
        (ntlmConfig->authenticateChildren != 0) && (ntlmConfig->challengeuses > -1)
-       && (ntlmConfig->challengelifetime > -1))
+       && (ntlmConfig->challengelifetime > -1)) {
+       debug(29, 9) ("authNTLMConfigured: returning configured\n");
        return 1;
+    }
+    debug(29, 9) ("authNTLMConfigured: returning unconfigured\n");
     return 0;
 }
 
@@ -456,7 +471,7 @@ authenticateNTLMHandleReply(void *data, void *lastserver, char *reply)
                result = S_HELPER_RELEASE;      /*some error has occured. no more requests */
                ntlm_request->authhelper = NULL;
                auth_user->flags.credentials_ok = 2;    /* Login/Usercode failed */
-               debug(29, 4) ("authenticateNTLMHandleReply: Error validating user via NTLM.\n");
+               debug(29, 4) ("authenticateNTLMHandleReply: Error validating user via NTLM. Error returned '%s'\n", reply);
                ntlm_request->auth_state = AUTHENTICATE_STATE_NONE;
                if ((t = strchr(reply, ' ')))   /* strip after a space */
                    *t = '\0';
@@ -482,7 +497,7 @@ authenticateNTLMHandleReply(void *data, void *lastserver, char *reply)
                    /* The helper broke on YR. It automatically
                     * resets */
                    auth_user->flags.credentials_ok = 3;        /* cannot process */
-                   debug(29, 1) ("authenticateNTLMHandleReply: Error obtaining challenge from helper: %d.\n", lastserver);
+                   debug(29, 1) ("authenticateNTLMHandleReply: Error obtaining challenge from helper: %d. Error returned '%s'\n", lastserver, reply);
                    /* mark it for starving */
                    helperstate->starve = 1;
                    /* resubmit the request. This helper is currently busy, so we will get
@@ -492,7 +507,7 @@ authenticateNTLMHandleReply(void *data, void *lastserver, char *reply)
                    /* the helper broke on a KK */
                    /* first the standard KK stuff */
                    auth_user->flags.credentials_ok = 2;        /* Login/Usercode failed */
-                   debug(29, 4) ("authenticateNTLMHandleReply: Error validating user via NTLM.\n");
+                   debug(29, 4) ("authenticateNTLMHandleReply: Error validating user via NTLM. Error returned '%s'\n", reply);
                    ntlm_request->auth_state = AUTHENTICATE_STATE_NONE;
                    if ((t = strchr(reply, ' ')))       /* strip after a space */
                        *t = '\0';
@@ -845,6 +860,10 @@ authenticateNTLMAuthenticateUser(auth_user_request_t * auth_user_request, reques
        /* we've recieved a negotiate request. pass to a helper */
        debug(29, 9) ("authenticateNTLMAuthenticateUser: auth state ntlm none. %s\n",
            proxy_auth);
+       if (auth_user->flags.credentials_ok == 2) {
+           /* the authentication fialed badly... */
+           return;
+       }
        ntlm_request->auth_state = AUTHENTICATE_STATE_NEGOTIATE;
        ntlm_request->ntlmnegotiate = xstrndup(proxy_auth, NTLM_CHALLENGE_SZ + 5);
        conn->auth_type = AUTH_NTLM;
index 2c0d6f205b9b88ef3a0cb09d34f23e582e22c049..ebd57c9ed39aa5babc45acac6a894d87f9e69568 100644 (file)
@@ -1,6 +1,6 @@
 
 /*
- * $Id: authenticate.cc,v 1.19 2001/01/12 00:37:14 wessels Exp $
+ * $Id: authenticate.cc,v 1.20 2001/01/31 22:16:38 hno Exp $
  *
  * DEBUG: section 29    Authenticator
  * AUTHOR: Duane Wessels
@@ -61,7 +61,8 @@ authenticateAuthSchemeConfigured(const char *proxy_auth)
     int i;
     for (i = 0; i < Config.authConfig.n_configured; i++) {
        scheme = Config.authConfig.schemes + i;
-       if (strncasecmp(proxy_auth, scheme->typestr, strlen(scheme->typestr)) == 0)
+       if ((strncasecmp(proxy_auth, scheme->typestr, strlen(scheme->typestr)) == 0) &&
+           (authscheme_list[scheme->Id].Active()))
            return 1;
     }
     return 0;
@@ -296,7 +297,10 @@ char *
 authenticateUserRequestUsername(auth_user_request_t * auth_user_request)
 {
     assert(auth_user_request != NULL);
-    return authenticateUserUsername(auth_user_request->auth_user);
+    if (auth_user_request->auth_user)
+       return authenticateUserUsername(auth_user_request->auth_user);
+    else
+       return NULL;
 }
 
 /* returns
@@ -322,7 +326,7 @@ authenticateActiveSchemeCount()
 {
     int i = 0, rv = 0;
     for (i = 0; authscheme_list && authscheme_list[i].typestr; i++)
-       if (authscheme_list[i].Active())
+       if (authscheme_list[i].configured())
            rv++;
     debug(29, 9) ("authenticateActiveSchemeCount: %d active.\n", rv);
     return rv;
@@ -351,7 +355,7 @@ authenticateInit(authConfig * config)
     authScheme *scheme;
     for (i = 0; i < config->n_configured; i++) {
        scheme = config->schemes + i;
-       if (authscheme_list[scheme->Id].init) {
+       if (authscheme_list[scheme->Id].init && authscheme_list[scheme->Id].configured()) {
            authscheme_list[scheme->Id].init(scheme);
        }
     }
@@ -370,7 +374,7 @@ authenticateShutdown(void)
            authscheme_list[i].donefunc();
        else
            debug(29, 2) ("authenticateShutdown: scheme %s has not registered a shutdown function.\n", authscheme_list[i].typestr);
-       if (!reconfiguring)
+       if (shutting_down)
            authscheme_list[i].typestr = NULL;
     }
 }
@@ -409,7 +413,7 @@ authenticateFixHeader(HttpReply * rep, auth_user_request_t * auth_user_request,
        else {
            int i;
            authScheme *scheme;
-           /* call each configured authscheme */
+           /* call each configured & running authscheme */
            for (i = 0; i < Config.authConfig.n_configured; i++) {
                scheme = Config.authConfig.schemes + i;
                if (authscheme_list[scheme->Id].Active())
index c967e4247ae7555553ef6fd64fe51ee4330be485..b477dbdca63614b3026166b93598b865f167334b 100644 (file)
@@ -1,6 +1,6 @@
 
 #
-# $Id: cf.data.pre,v 1.211 2001/01/30 21:31:05 hno Exp $
+# $Id: cf.data.pre,v 1.212 2001/01/31 22:16:38 hno Exp $
 #
 #
 # SQUID Web Proxy Cache          http://www.squid-cache.org/
@@ -1141,6 +1141,11 @@ DOC_START
        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 @@ DOC_START
        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 @@ DOC_START
 
 NOCOMMENT_START
 #Recommended minimum configuration:
+#auth_param digest program <uncomment and complete this line>
+#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 <uncomment and complete this line to activate>
-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 <uncomment and complete this line>
 auth_param basic children 5
 auth_param basic realm Squid proxy-caching web server
index f6ac60feed27c300d4cc6294c628d47a41c5ef1f..71c424cdec974a55c8bccb66fa5e8bf7ca6708c6 100644 (file)
@@ -1,6 +1,6 @@
 
 /*
- * $Id: enums.h,v 1.183 2001/01/12 00:37:17 wessels Exp $
+ * $Id: enums.h,v 1.184 2001/01/31 22:16:38 hno Exp $
  *
  *
  * SQUID Web Proxy Cache          http://www.squid-cache.org/
@@ -496,6 +496,7 @@ typedef enum {
     AUTH_UNKNOWN,              /* default */
     AUTH_BASIC,
     AUTH_NTLM,
+    AUTH_DIGEST,
     AUTH_BROKEN                        /* known type, but broken data */
 } auth_type_t;
 
index fde66cd3e790615111f544b7a566f42d8889d392..97aea6701296309b3c758b6edcc99d5f42c9c214 100644 (file)
@@ -1,6 +1,6 @@
 
 /*
- * $Id: helper.cc,v 1.24 2001/01/12 00:37:18 wessels Exp $
+ * $Id: helper.cc,v 1.25 2001/01/31 22:16:38 hno Exp $
  *
  * DEBUG: section 29    Helper process maintenance
  * AUTHOR: Harvest Derived?
@@ -842,7 +842,23 @@ StatefulServerEnqueue(helper_stateful_server * srv, helper_stateful_request * r)
 {
     dlink_node *link = memAllocate(MEM_DLINK_NODE);
     dlinkAddTail(r, link, &srv->queue);
-    /* XXX No queue length check here? */
+/* TODO: warning if the queue on this server is more than X
+ * We don't check the queue size at the moment, because
+ * requests hitting here are deferrable 
+ */
+/*    hlp->stats.queue_size++;
+ * if (hlp->stats.queue_size < hlp->n_running)
+ * return;
+ * if (squid_curtime - hlp->last_queue_warn < 600)
+ * return;
+ * if (shutting_down || reconfiguring)
+ * return;
+ * hlp->last_queue_warn = squid_curtime;
+ * debug(14, 0) ("WARNING: All %s processes are busy.\n", hlp->id_name);
+ * debug(14, 0) ("WARNING: %d pending requests queued\n", hlp->stats.queue_size);
+ * if (hlp->stats.queue_size > hlp->n_running * 2)
+ * fatalf("Too many queued %s requests", hlp->id_name);
+ * debug(14, 1) ("Consider increasing the number of %s processes in your config file.\n", hlp->id_name);  */
 }
 
 
index 1eded00a68ce5221af92d9bdbfbf2475906643dc..670b9b93721d749bfc03b085336f35b3e14ea6b9 100644 (file)
@@ -1,6 +1,6 @@
 
 /*
- * $Id: structs.h,v 1.379 2001/01/12 00:37:22 wessels Exp $
+ * $Id: structs.h,v 1.380 2001/01/31 22:16:38 hno Exp $
  *
  *
  * SQUID Web Proxy Cache          http://www.squid-cache.org/
@@ -139,6 +139,7 @@ struct _authscheme_entry {
     AUTHSADDTRAILER *AddTrailer;
     AUTHSAUTHED *authenticated;
     AUTHSAUTHUSER *authAuthenticate;
+    AUTHSCONFIGURED *configured;
     AUTHSDUMP *dump;
     AUTHSFIXERR *authFixHeader;
     AUTHSFREE *FreeUser;
index e1fbc0d7f67151d3264edf54c870dbd3f4c59e10..ae4fca5eebde39aba3b16b1fe014fea4b25b61d7 100644 (file)
@@ -1,6 +1,6 @@
 
 /*
- * $Id: typedefs.h,v 1.120 2001/01/12 00:37:23 wessels Exp $
+ * $Id: typedefs.h,v 1.121 2001/01/31 22:16:39 hno Exp $
  *
  *
  * SQUID Web Proxy Cache          http://www.squid-cache.org/
@@ -293,6 +293,7 @@ typedef void StatHistBinDumper(StoreEntry *, int idx, double val, double size, i
 typedef int AUTHSACTIVE();
 typedef int AUTHSAUTHED(auth_user_request_t *);
 typedef void AUTHSAUTHUSER(auth_user_request_t *, request_t *, ConnStateData *, http_hdr_type);
+typedef int AUTHSCONFIGURED();
 typedef void AUTHSDECODE(auth_user_request_t *, const char *);
 typedef int AUTHSDIRECTION(auth_user_request_t *);
 typedef void AUTHSDUMP(StoreEntry *, const char *, authScheme *);