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)
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,
<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
<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
--- /dev/null
+# 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;
--- /dev/null
+#
+# 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
--- /dev/null
+/*
+ * 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);
+}
#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
*/
"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
/* 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)
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)
{
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;
/*
* 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 ***************/
#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);
* 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
}
}
+
+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;
"%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);
}
{
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;
case 'f':
failover_enabled = 1;
break;
+ case 'l':
+ last_ditch_enabled = 1;
+ break;
default:
fprintf(stderr, "unknown option: -%c. Exiting\n", opt);
usage();
/* 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 */
{
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;
}
/* 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;
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;
}
}
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();
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);
/*
- * $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
} 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
--- /dev/null
+/* 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 */
#
-# $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@
UTILOBJS = rfc1123.o \
rfc1738.o \
rfc1035.o \
+ rfc2617.o \
util.o \
getfullhostname.o \
base64.o \
/*
- * $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 */
#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)
* 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;
* 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];
* 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;
--- /dev/null
+/* 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);
+};
/*
- * $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
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;
/*
- * $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
static AUTHSACTIVE authenticateBasicActive;
static AUTHSAUTHED authenticateBasicAuthenticated;
static AUTHSAUTHUSER authenticateBasicAuthenticateUser;
+static AUTHSCONFIGURED authBasicConfigured;
static AUTHSDIRECTION authenticateBasicDirection;
static AUTHSDECODE authenticateBasicDecodeAuth;
static AUTHSDUMP authBasicCfgDump;
authscheme->init = authBasicInit;
authscheme->authAuthenticate = authenticateBasicAuthenticateUser;
authscheme->authenticated = authenticateBasicAuthenticated;
+ authscheme->configured = authBasicConfigured;
authscheme->authFixHeader = authenticateBasicFixErrorHeader;
authscheme->FreeUser = authenticateBasicFreeUser;
authscheme->freeconfig = authBasicFreeConfig;
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;
}
}
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) {
--- /dev/null
+#
+# 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
--- /dev/null
+
+/*
+ * $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);
+}
--- /dev/null
+/*
+ * 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
/*
- * $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
static AUTHSACTIVE authenticateNTLMActive;
static AUTHSAUTHED authNTLMAuthenticated;
static AUTHSAUTHUSER authenticateNTLMAuthenticateUser;
+static AUTHSCONFIGURED authNTLMConfigured;
static AUTHSFIXERR authenticateNTLMFixErrorHeader;
static AUTHSFREE authenticateNTLMFreeUser;
static AUTHSDIRECTION authenticateNTLMDirection;
void
authNTLMDone(void)
{
+ debug(29, 2) ("authNTLMDone: shutting down NTLM authentication.\n");
if (ntlmauthenticators)
helperStatefulShutdown(ntlmauthenticators);
authntlm_initialised = 0;
}
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) {
{
assert(!authntlm_initialised);
authscheme->Active = authenticateNTLMActive;
+ authscheme->configured = authNTLMConfigured;
authscheme->parse = authNTLMParse;
authscheme->dump = authNTLMCfgDump;
authscheme->requestFree = authNTLMAURequestFree;
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;
}
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';
/* 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
/* 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';
/* 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;
/*
- * $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
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;
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
{
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;
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);
}
}
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;
}
}
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())
#
-# $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/
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
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
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
/*
- * $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/
AUTH_UNKNOWN, /* default */
AUTH_BASIC,
AUTH_NTLM,
+ AUTH_DIGEST,
AUTH_BROKEN /* known type, but broken data */
} auth_type_t;
/*
- * $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?
{
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); */
}
/*
- * $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/
AUTHSADDTRAILER *AddTrailer;
AUTHSAUTHED *authenticated;
AUTHSAUTHUSER *authAuthenticate;
+ AUTHSCONFIGURED *configured;
AUTHSDUMP *dump;
AUTHSFIXERR *authFixHeader;
AUTHSFREE *FreeUser;
/*
- * $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/
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 *);