--------------------- PatchSet 487 Date: 2000/08/02 14:13:10 Author: rbcollins Branch: ntlm Tag: (none) Log: major rework of NTLM auth process. key points: * added stateful helper code (no stats yet) * external helper creates challenge. * new configuration directive(s) for ntlm authenticate program * basic authentication is now optional. configure with --enable-basic-authentication to use it Members: acconfig.h:1.1.1.3.10.5->1.1.1.3.10.6 configure.in:1.1.1.3.10.9->1.1.1.3.10.10 makefile.in:1.1.1.3.10.2->1.1.1.3.10.3 src/acl.c:1.1.1.3.12.8->1.1.1.3.12.9 src/authenticate.c:1.1.1.3.12.5->1.1.1.3.12.6 src/cache_cf.c:1.1.1.3.4.1.2.5->1.1.1.3.4.1.2.6 src/cf.data.pre:1.1.1.3.4.1.2.12->1.1.1.3.4.1.2.13 src/cf_gen_defines:1.1.10.3->1.1.10.4 src/client_side.c:1.1.1.3.4.1.2.16->1.1.1.3.4.1.2.17 src/defines.h:1.1.1.3.12.5->1.1.1.3.12.6 src/enums.h:1.1.1.3.12.5->1.1.1.3.12.6 src/errorpage.c:1.1.1.3.10.5->1.1.1.3.10.6 src/helper.c:1.1.1.3.12.3->1.1.1.3.12.4 src/mem.c:1.1.1.3.12.4->1.1.1.3.12.5 src/protos.h:1.1.1.3.12.8->1.1.1.3.12.9 src/squid.h:1.1.1.3.12.3->1.1.1.3.12.4 src/structs.h:1.1.1.3.4.1.2.12->1.1.1.3.4.1.2.13 src/typedefs.h:1.1.1.3.12.6->1.1.1.3.12.7 Index: squid/acconfig.h =================================================================== RCS file: /cvsroot/squid-sf//squid/Attic/acconfig.h,v retrieving revision 1.1.1.3.10.5 retrieving revision 1.1.1.3.10.6 diff -u -r1.1.1.3.10.5 -r1.1.1.3.10.6 --- squid/acconfig.h 15 Jul 2000 20:52:06 -0000 1.1.1.3.10.5 +++ squid/acconfig.h 2 Aug 2000 14:13:10 -0000 1.1.1.3.10.6 @@ -20,7 +20,7 @@ * */ @ TOP @ -/* $Id: acconfig.h,v 1.1.1.3.10.5 2000/07/15 20:52:06 hno Exp $ */ +/* $Id: acconfig.h,v 1.1.1.3.10.6 2000/08/02 14:13:10 rbcollins Exp $ */ /********************************* * START OF CONFIGURABLE OPTIONS * @@ -244,6 +244,17 @@ #undef USE_NTLM /* + * Compile in support for Basic authentication. + */ +#undef USE_BASIC_AUTH + +/* + * Compile in generic authentication support code. + */ +#undef USE_AUTHENTICATION + + +/* * If your system has statvfs(), and if it actually works! */ #undef HAVE_STATVFS Index: squid/configure.in =================================================================== RCS file: /cvsroot/squid-sf//squid/configure.in,v retrieving revision 1.1.1.3.10.9 retrieving revision 1.1.1.3.10.10 diff -u -r1.1.1.3.10.9 -r1.1.1.3.10.10 --- squid/configure.in 1 Aug 2000 17:18:15 -0000 1.1.1.3.10.9 +++ squid/configure.in 2 Aug 2000 14:13:10 -0000 1.1.1.3.10.10 @@ -3,13 +3,13 @@ dnl dnl Duane Wessels, wessels@nlanr.net, February 1996 (autoconf v2.9) dnl -dnl $Id: configure.in,v 1.1.1.3.10.9 2000/08/01 17:18:15 hno Exp $ +dnl $Id: configure.in,v 1.1.1.3.10.10 2000/08/02 14:13:10 rbcollins Exp $ dnl dnl dnl AC_INIT(src/main.c) AC_CONFIG_HEADER(include/autoconf.h) -AC_REVISION($Revision: 1.1.1.3.10.9 $)dnl +AC_REVISION($Revision: 1.1.1.3.10.10 $)dnl AC_PREFIX_DEFAULT(/usr/local/squid) AC_CONFIG_AUX_DIR(cfgaux) @@ -616,13 +616,26 @@ [ --enable-ntlm-authentication This allows Squid to use the Microsoft NTLM transparent authentication scheme for - authenticating clients. ], + authenticating clients. You must also compile + at least one NTLM authentication module], [ if test "$enableval" = "yes" ; then echo "Enabling NTLM authentication" AC_DEFINE(USE_NTLM) fi ]) +AC_ARG_ENABLE(basic-authentication, +[ --enable-basic-authentication + This prevents Squid from using Basic (cleartext) + authentication for authenticating clients. You + must also compile at least one authentication + module.], +[ if test "$enableval" = "yes" ; then + echo "Enabling Basic authentication" + AC_DEFINE(USE_BASIC_AUTH) + fi +]) + use_dnsserver= AC_ARG_ENABLE(internal-dns, [ --disable-internal-dns This prevents Squid from directly sending and @@ -694,10 +707,38 @@ esac ]) if test -n "$AUTH_MODULES"; then - echo "Auth moules built: $AUTH_MODULES" + echo "Auth modules built: $AUTH_MODULES" fi AC_SUBST(AUTH_MODULES) +dnl Select ntlm auth modules to build +NTLM_AUTH_MODULES= +AC_ARG_ENABLE(ntlm-auth-modules, +[ --enable-ntlm-auth-modules=\"list of modules\" + This option selects which proxy_auth ntlm helper + modules to build and install as part of the normal + build process. For a list of available modules see + the ntlm_auth_modules directory.], +[ case "$enableval" in + yes) + for module in $srcdir/ntlm_auth_modules/*; do + if test -f $module/Makefile.in; then + NTLM_AUTH_MODULES="$NTLM_AUTH_MODULES `basename $module`" + fi + done + ;; + no) + ;; + *) + NTLM_AUTH_MODULES="`echo $enableval| sed -e 's/,/ /g;s/ */ /g'`" + esac +]) +if test -n "$NTLM_AUTH_MODULES"; then + echo "NTLM auth modules built: $NTLM_AUTH_MODULES" +fi +AC_SUBST(NTLM_AUTH_MODULES) + + dnl Disable "unlinkd" code AC_ARG_ENABLE(unlinkd, [ --disable-unlinkd Do not use unlinkd], @@ -1688,6 +1729,13 @@ fi done +NTLM_AUTH_MAKEFILES="" +for module in $srcdir/ntlm_auth_modules/*; do + if test -f $module/Makefile.in; then + NTLM_AUTH_MAKEFILES="$NTLM_AUTH_MAKEFILES ./ntlm_auth_modules/`basename $module`/Makefile" + fi +done + AC_OUTPUT(\ ./makefile \ ./lib/Makefile \ @@ -1706,4 +1754,6 @@ ./errors/Makefile \ ./auth_modules/Makefile \ $AUTH_MAKEFILES \ + ./ntlm_auth_modules/Makefile \ + $NTLM_AUTH_MAKEFILES \ ) Index: squid/makefile.in =================================================================== RCS file: /cvsroot/squid-sf//squid/Attic/makefile.in,v retrieving revision 1.1.1.3.10.2 retrieving revision 1.1.1.3.10.3 diff -u -r1.1.1.3.10.2 -r1.1.1.3.10.3 --- squid/makefile.in 3 May 2000 19:18:10 -0000 1.1.1.3.10.2 +++ squid/makefile.in 2 Aug 2000 14:13:10 -0000 1.1.1.3.10.3 @@ -1,4 +1,4 @@ -# $Id: makefile.in,v 1.1.1.3.10.2 2000/05/03 19:18:10 hno Exp $ +# $Id: makefile.in,v 1.1.1.3.10.3 2000/08/02 14:13:10 rbcollins Exp $ # srcdir = @srcdir@ @@ -14,7 +14,7 @@ prefix = @prefix@ exec_prefix = @exec_prefix@ -SUBDIRS = lib @makesnmplib@ scripts src icons errors auth_modules +SUBDIRS = lib @makesnmplib@ scripts src icons errors auth_modules ntlm_auth_modules noargs: all @@ -38,6 +38,7 @@ rm -f config.log makefile rm -f include/paths.h include/autoconf.h include/config.h rm -f auth_modules/dummy + rm -f ntlm_auth_modules/dummy @for dir in $(SUBDIRS) contrib; do \ echo Making distclean in $$dir; \ (cd $$dir; $(MAKE) $(MFLAGS) prefix="$(prefix)" distclean); \ Index: squid/src/acl.c =================================================================== RCS file: /cvsroot/squid-sf//squid/src/acl.c,v retrieving revision 1.1.1.3.12.8 retrieving revision 1.1.1.3.12.9 diff -u -r1.1.1.3.12.8 -r1.1.1.3.12.9 --- squid/src/acl.c 20 Jul 2000 09:11:56 -0000 1.1.1.3.12.8 +++ squid/src/acl.c 2 Aug 2000 14:13:10 -0000 1.1.1.3.12.9 @@ -1,6 +1,6 @@ /* - * $Id: acl.c,v 1.1.1.3.12.8 2000/07/20 09:11:56 kinkie Exp $ + * $Id: acl.c,v 1.1.1.3.12.9 2000/08/02 14:13:10 rbcollins Exp $ * * DEBUG: section 28 Access Control * AUTHOR: Duane Wessels @@ -35,7 +35,6 @@ #include "squid.h" #include "splay.h" -#include "ntlm.h" static int aclFromFile = 0; static FILE *aclFile; @@ -54,7 +53,9 @@ static void aclDestroyAclList(acl_list * list); static void aclDestroyTimeList(acl_time_data * data); static void aclDestroyIntRange(intrange *); +#ifdef USE_BASIC_AUTH static FREE aclFreeProxyAuthUser; +#endif static struct _acl *aclFindByName(const char *name); static int aclMatchAcl(struct _acl *, aclCheck_t *); static int aclMatchIntegerRange(intrange * data, int i); @@ -79,6 +80,8 @@ static FQDNH aclLookupDstFQDNDone; static void aclLookupProxyAuthStart(aclCheck_t * checklist); static void aclLookupProxyAuthDone(void *data, char *result); +static void aclLookupProxyStatefulAuthStart(aclCheck_t * checklist); +static void aclLookupProxyNTLMAuthDone(void *data, void * lastserver, char *result); static wordlist *aclDumpIpList(void *); static wordlist *aclDumpDomainList(void *data); static wordlist *aclDumpTimeSpecList(acl_time_data *); @@ -632,6 +635,7 @@ wordlistAdd(curlist, t); } +/* each proxy auth acl can be for a specific front-side protocol. */ static void aclParseProxyAuthList(void *curlist) { @@ -1055,17 +1059,21 @@ return 0; } +/* + 0: basic and ok + 1: bad + 2: ntlm + */ static int aclDecodeProxyAuth(const char *proxy_auth, int *type, char **user, char **password, char *buf, size_t bufsize) { char *sent_auth; char *cleartext; -#if USE_NTLM - int rv; -#endif - if (proxy_auth == NULL) +/* if (proxy_auth == NULL) return 0; + *///checked by aclMatchProxy. + assert(proxy_auth!=NULL); debug(28, 6) ("aclDecodeProxyAuth: header = '%s'\n", proxy_auth); if (strncasecmp(proxy_auth, "Basic ", 6) == 0) { @@ -1077,8 +1085,8 @@ *type = AUTHENTICATE_TYPE_NTLM; #endif } else { - debug(28, 1) ("aclDecodeProxyAuth: Unsupported proxy-auth sheme, '%s'\n", proxy_auth); - return 0; + debug(28, 1) ("aclDecodeProxyAuth: Unsupported proxy-auth scheme, '%s'\n", proxy_auth); + return AUTH_UNKNOWN; } /* Trim leading whitespace before decoding */ @@ -1106,32 +1114,26 @@ *(*password)++ = '\0'; if (*password == NULL) { debug(28, 1) ("aclDecodeProxyAuth: no password in proxy authorization header '%s'\n", proxy_auth); - return 0; + return AUTH_BROKEN; } if (**password == '\0') { debug(28, 1) ("aclDecodeProxyAuth: Disallowing empty password," "user is '%s'\n", *user); - return 0; + return AUTH_BROKEN; } + return AUTH_BASIC; break; #if USE_NTLM case AUTHENTICATE_TYPE_NTLM: + /* all we have to do is identify that it's NTLM */ /* If this is a negotiation requst, we return a challenge */ - if (!ntlmCheckHeader((struct ntlmhdr *)cleartext, NTLM_NEGOTIATE)) - return 2; - - /* Assume this is an authentication request */ - rv = ntlmDecodeAuth((struct ntlm_authenticate *)cleartext, buf, bufsize, - user, password); - xfree(cleartext); - - if (rv || *user == NULL || *password == NULL) { - debug(28, 1) ("aclDecodeProxyAuth: NTLM authorization botched\n"); - return 0; - } + debug(28,7)("aclDecodeProxyAuth: NTLM authentication\n"); + return AUTH_NTLM; break; #endif } + /* UNREACHBLE */ + assert(1); return 1; } @@ -1140,6 +1142,8 @@ * 1 : user validated OK * -1 : check the password for this user via an external authenticator * -2 : invalid Proxy-Authorization: header; + * -3 : get something from an external authenticator; + * -4 : we have a header to send to the client; * ask for Proxy-Authorization: header */ @@ -1151,59 +1155,83 @@ char *user, *password; int type, rv; - /* Don't repeatedly ask for authentication on a persistent connection. */ - if (checklist->auth_state == AUTHENTICATE_STATE_DONE) - return 1; - - switch (aclDecodeProxyAuth(proxy_auth, &type, &user, &password, login_buf, sizeof(login_buf))) { -#if USE_NTLM - case 2: /* Send an NTLM challenge */ - checklist->auth_state = AUTHENTICATE_STATE_CHALLENGE; - checklist->auth_type=type; - /* FALLTHROUGH */ -#endif - case 0: /* User denied access */ - return -2; - } - - debug(28, 5) ("aclMatchProxyAuth: checking user '%s'\n", user); - - if (auth_user) { - /* - * This should be optimized to a boolean argument indicating that the - * password is invalid, instead of passing full acl_proxy_auth_user - * structures, and all messing with checklist->proxy_auth should - * be restricted the functions that deal with the authenticator. - */ - assert(auth_user == checklist->auth_user); - checklist->auth_user = NULL; /* get rid of that special reference */ - /* Check result from external validation */ - if (auth_user->passwd_ok != 1) { - /* password was checked but did not match */ - assert(auth_user->passwd_ok == 0); - debug(28, 4) ("aclMatchProxyAuth: authentication failed for user '%s'\n", - user); - aclFreeProxyAuthUser(auth_user); - /* - * copy username to request for logging on client-side - * unless ident is known (do not override ident with - * false proxy auth names) - */ - if (!*checklist->request->user_ident) - xstrncpy(checklist->request->user_ident, user, USER_IDENT_SZ); - return -2; - } else { - /* password was checked and did match */ - debug(28, 4) ("aclMatchProxyAuth: user '%s' validated OK\n", user); - /* store validated user in hash, after filling in expiretime */ - xstrncpy(checklist->request->user_ident, user, USER_IDENT_SZ); - auth_user->expiretime = current_time.tv_sec + Config.authenticateTTL; - auth_user->ip_expiretime = squid_curtime + Config.authenticateIpTTL; - auth_user->ipaddr = checklist->src_addr; - hash_join(proxy_auth_cache, (hash_link *) auth_user); - /* Continue checking below, as normal */ - } - } + /* state machine for ntlm authentication on a connection + should be easily modified to support Digest via the + +state input state output +NONE NTLM NEGOTIATE NEGOTIATE NEGOTIATE TO HELPER +NEGOTIATE CHALLENGE CHALLENGE CHALLENGE TO CLIENT +CHALLENGE RESPONSE RESPONSE RESPONSE TO HELPER +RESPONSE OK DONE PAGE TO CLIENT +RESPONSE ERR NONE ERROR TO CLIENT +NONE NONE NONE OFFER AUTH TYPES + still thinking about basic state process +NONE BASIC HEADER NONE + + +*/ + + if (checklist->conn==NULL){ + debug (28,1)("aclMatchProxyAuth: no connection data, denying access\n"); + /* + * deny access clientreadrequest requires conn data, we should have it too? + */ + return 0; + } + debug(28,6)("aclMatchAcl:auth state State %d.\n",checklist->conn->auth_state); + debug(28,6)("aclMatchAcl:auth state type %d.\n",checklist->conn->auth_type); + debug(28,6)("aclMatchAcl:auth state state %d.\n",checklist->auth_state); + debug(28,6)("aclMatchAcl:auth state type %d.\n",checklist->auth_type); + + switch (checklist->conn->auth_state){ + case AUTHENTICATE_STATE_NONE: /* this is a new connection */ + if (proxy_auth==NULL) /* no header, request one */ + return -2; + debug(28,6)("aclMatchProxyAuth: auth state none with header %s.\n",proxy_auth); + switch (aclDecodeProxyAuth(proxy_auth, &type, &user, &password, login_buf, sizeof(login_buf))) { +#ifdef USE_BASIC_AUTH + case AUTH_BASIC: + /* legacy non-stateful basic authentication code + * Keep the state set to NONE. + */ + debug(28,5)("aclMatchProxyAuth: auth state type basic.\n",proxy_auth); + debug(28, 5) ("aclMatchProxyAuth: checking user '%s'\n", user); + if (auth_user) { + /* + * This should be optimized to a boolean argument indicating that the + * password is invalid, instead of passing full acl_proxy_auth_user + * structures, and all messing with checklist->proxy_auth should + * be restricted the functions that deal with the authenticator. + */ + assert(auth_user == checklist->auth_user); + checklist->auth_user = NULL; /* get rid of that special reference */ + /* Check result from external validation */ + if (auth_user->passwd_ok != 1) { + /* password was checked but did not match */ + assert(auth_user->passwd_ok == 0); + debug(28, 4) ("aclMatchProxyAuth: authentication failed for user '%s'\n", + user); + aclFreeProxyAuthUser(auth_user); + /* + * copy username to request for logging on client-side + * unless ident is known (do not override ident with + * false proxy auth names) + */ + if (!*checklist->request->user_ident) + xstrncpy(checklist->request->user_ident, user, USER_IDENT_SZ); + return -2; + } else { + /* password was checked and did match */ + debug(28, 4) ("aclMatchProxyAuth: user '%s' validated OK\n", user); + /* store validated user in hash, after filling in expiretime */ + xstrncpy(checklist->request->user_ident, user, USER_IDENT_SZ); + auth_user->expiretime = current_time.tv_sec + Config.authenticateTTL; + auth_user->ip_expiretime = squid_curtime + Config.authenticateIpTTL; + auth_user->ipaddr = checklist->src_addr; + hash_join(proxy_auth_cache, (hash_link *) auth_user); + /* Continue checking below, as normal */ + } + } /* see if we already know this user */ auth_user = hash_lookup(proxy_auth_cache, user); if (!auth_user) { @@ -1224,15 +1252,7 @@ xstrncpy(checklist->request->user_ident, user, USER_IDENT_SZ); switch (acltype) { case ACL_PROXY_AUTH: - rv = aclMatchUser(data, user); - if (checklist->conn) { - if (rv) - checklist->auth_state = AUTHENTICATE_STATE_DONE; -#if USE_NTLM - xstrncpy(checklist->conn->ident, user, USER_IDENT_SZ); -#endif - } - return rv; + return aclMatchUser(data, user); break; case ACL_PROXY_AUTH_REGEX: return aclMatchRegex(data, user); @@ -1268,7 +1288,74 @@ /* wrong password will be trapped above so this does not loop */ return -1; } - /* NOTREACHED */ + + /* not reached */ + return 0; + break; +#endif +#ifdef USE_NTLM + case AUTH_NTLM: + /* we've recieved a negotiate request. pass to a helper */ + debug(28,5)("aclMatchProxyAuth: auth state type ntlm.\n",proxy_auth); + checklist->conn->auth_state=AUTHENTICATE_STATE_NEGOTIATE; + checklist->conn->auth_type =AUTH_NTLM; + return -3; + break; + case AUTH_UNKNOWN: + debug(28,1)("aclMatchProxyAuth: auth state no type with header %s.\n",proxy_auth); + break; + } + break; + case AUTHENTICATE_STATE_NEGOTIATE: + debug(28,5)("aclMatchProxyAuth: auth state negotiate with header %s.\n",proxy_auth); + debug(28,6)("aclMatchProxyAuth: auth state negotiate with challenge %s.\n",checklist->conn->authchallenge); + checklist->conn->auth_state=AUTHENTICATE_STATE_CHALLENGE; + return -4; + break; + case AUTHENTICATE_STATE_CHALLENGE: + /* we should have recieved a NTLM challenge. pass it to the same helper process*/ + debug(28,5)("aclMatchProxyAuth: auth state challenge with header %s.\n",proxy_auth); + checklist->conn->auth_state=AUTHENTICATE_STATE_RESPONSE; + return -3; + break; + case AUTHENTICATE_STATE_RESPONSE: + debug(28,5)("aclMatchProxyAuth: auth state response with header %s.\n",proxy_auth); + fatal("aclMatchProxyAuth: this state location should never be reached.\n"); + break; + case AUTHENTICATE_STATE_DONE: + debug(28,5)("aclMatchProxyAuth: not checking due to state Done.\n"); + + /* we have a valid username. Do they match the acl? NTLM only at this point */ + /* store the username in the request for logging*/ + xstrncpy(checklist->request->user_ident, checklist->conn->authuser, USER_IDENT_SZ); + + switch (acltype) { + case ACL_PROXY_AUTH: + rv = aclMatchUser(data, checklist->conn->authuser); + if (!rv) checklist->auth_state = AUTHENTICATE_STATE_NONE; /* not permitted in this ACL */ + else { + xstrncpy(checklist->conn->ident, checklist->conn->authuser, USER_IDENT_SZ);/*using ident hack.. do we need this? */ + } /* auth user was "user" before */ + return rv; + break; + case ACL_PROXY_AUTH_REGEX: + rv = aclMatchRegex(data, checklist->conn->authuser); + if (!rv) checklist->auth_state = AUTHENTICATE_STATE_NONE; /* not permitted in this ACL */ + else { + xstrncpy(checklist->conn->ident, checklist->conn->authuser, USER_IDENT_SZ);/*using ident hack.. do we need this? */ + } + return rv; + default: + fatal("aclMatchProxyAuth: unknown ACL type"); + return 0; /* NOTREACHED */ + } + + fatal("aclMatchProxyAuth:End of NTLM auth checking code... should be Unreachable\n"); + break; + } +#endif + fatal("aclMatchProxyAuth:END Of MatchACL routine... should be unreachable\n"); + return 0; } static void @@ -1309,6 +1396,53 @@ checklist); } +#ifdef USE_NTLM +static void +aclLookupProxyStatefulAuthStart(aclCheck_t * checklist) +{ + LOCAL_ARRAY(char, login_buf, USER_IDENT_SZ); + const char *proxy_auth; + char *sent_string; + char *user, *password; + int ok,auth_type; + + if (!checklist->request->flags.accelerated) { + /* Proxy auth on proxy requests */ + proxy_auth = httpHeaderGetStr(&checklist->request->header, + HDR_PROXY_AUTHORIZATION); + } else { + /* WWW auth on accelerated requests */ + proxy_auth = httpHeaderGetStr(&checklist->request->header, + HDR_AUTHORIZATION); + } + + /* do we need this when it's stateful? it may not make _any_ sense here! */ + + ok = aclDecodeProxyAuth(proxy_auth, &auth_type, &user, &password, login_buf, + sizeof(login_buf)); + /* + * if aclDecodeProxyAuth() fails, the same call should have failed + * in aclMatchProxyAuth, and we should never get this far. + */ + assert(ok==AUTH_NTLM); + /* to add a new stateful auth method. add identifiers in DecodeProxy + * add authenticateAUTHMETODStart and aclLookupProxyAUTHMETHODDone functions, and _if needed_ new storage to the connection structure + * You should be able to reuse the auth* variable in the connection structure as the state machine _should_ avoid collisions. + */ + + debug(28, 5) ("aclLookupProxyAuthStart: going to ask NTLM authenticator on %s\n", proxy_auth); + + /* stateful authentication has no timeouts/ip storage etc */ + sent_string=xstrdup(proxy_auth); + while(!xisspace(*sent_string)) /*trim NTLM/BASIC/whatever*/ + sent_string++; + while(xisspace(*sent_string)) /*trim leading spaces*/ + sent_string++; + + authenticateNTLMStart(sent_string, aclLookupProxyNTLMAuthDone, checklist, checklist->conn->authhelper); +} +#endif /*NTLM*/ + static int aclMatchInteger(intlist * data, int i) { @@ -1583,6 +1717,7 @@ case -2: /* no such user OR we need a proxy authentication header */ checklist->state[ACL_PROXY_AUTH] = ACL_PROXY_AUTH_NEEDED; + debug(28,6)("aclmatchAcl:we need a proxyauth header\n"); /* * XXX This is a bit oddly done.. should perhaps use different * return codes here @@ -1594,6 +1729,19 @@ */ checklist->state[ACL_PROXY_AUTH] = ACL_LOOKUP_NEEDED; return 0; + case -3: + /* + * we need a external program to generate data for the client + */ + checklist->state[ACL_PROXY_AUTH] = ACL_HELPER_START; + return 0; + case -4: + /* + * we have the response, send to the client + */ + checklist->state[ACL_PROXY_AUTH] = ACL_PROXY_AUTH_NEEDED; + debug(28,6)("aclmatchAcl:we need a second proxyauth header\n"); + return 0; } /* NOTREACHED */ #if SQUID_SNMP @@ -1643,7 +1791,7 @@ { while (list) { AclMatchedName = list->acl->name; - debug(28, 3) ("aclMatchAclList: checking %s%s\n", + debug(28, 3) ("aclMatchAclList: checking %s%s\n", list->op ? null_string : "!", list->acl->name); if (aclMatchAcl(list->acl, checklist) != list->op) { debug(28, 3) ("aclMatchAclList: returning 0\n"); @@ -1721,7 +1869,7 @@ checklist); return; } else if (checklist->state[ACL_PROXY_AUTH] == ACL_LOOKUP_NEEDED) { - debug(28, 3) ("aclCheck: checking password via authenticator\n"); + debug(28, 3) ("aclCheck: checking password via basic authenticator\n"); aclLookupProxyAuthStart(checklist); checklist->state[ACL_PROXY_AUTH] = ACL_LOOKUP_PENDING; return; @@ -1729,8 +1877,15 @@ /* Special case. Client is required to resend the request * with authentication. The request is denied. */ + debug(28,6)("aclCheck: requiring Proxy Auth header.\n"); allow = ACCESS_REQ_PROXY_AUTH; match = -1; + } else if (checklist->state[ACL_PROXY_AUTH] == ACL_HELPER_START) { + /* we are using an external helper to do __everything__ in a STATEFUL fashion */ + debug(28,3) ("aclCheck: handing Proxy Auth header to stateful authenticator\n"); + aclLookupProxyStatefulAuthStart(checklist); + checklist->state[ACL_PROXY_AUTH] = ACL_HELPER_PENDING; + return; } #if USE_IDENT else if (checklist->state[ACL_IDENT] == ACL_LOOKUP_NEEDED) { @@ -1871,6 +2026,44 @@ aclCheck(checklist); } +static void +aclLookupProxyNTLMAuthDone(void *data, void * lastserver, char *result) +{ + aclCheck_t *checklist = data; + checklist->state[ACL_PROXY_AUTH] = ACL_LOOKUP_DONE; + debug(28, 4) ("aclLookupProxyStatefulAuthDone: result = %s\n", + result ? result : "NULL"); +/* check format */ +/* state info check */ + assert(checklist->conn!=NULL); + assert((checklist->conn->auth_state==AUTHENTICATE_STATE_NEGOTIATE)||(checklist->conn->auth_state==AUTHENTICATE_STATE_RESPONSE)); + assert(checklist->conn->auth_type ==AUTH_NTLM); + switch(checklist->conn->auth_state){ + case AUTHENTICATE_STATE_NEGOTIATE: + xstrncpy(checklist->conn->authchallenge,result,NTLM_CHALLENGE_SZ); + assert(!checklist->conn->authhelper); + checklist->conn->authhelper=lastserver; /*save the handler*/ +/* if (result && (strncasecmp(result, "OK", 2) == 0)) + checklist->auth_user->passwd_ok = 1; + else + checklist->auth_user->passwd_ok = 0; */ + break; + case AUTHENTICATE_STATE_RESPONSE: + if (result && (strncasecmp(result, "OK ", 3) == 0)){ + checklist->conn->auth_state=AUTHENTICATE_STATE_DONE; /* they are a valid domain user */ + result+=3; + xstrncpy(checklist->conn->authuser,result,MAX_LOGIN_SZ); + } + else + checklist->conn->auth_state=AUTHENTICATE_STATE_NONE; /* something botched.. restart */ + assert(checklist->conn->authhelper); + checklist->conn->authhelper=0; /*clear the handler*/ + break; + } + aclCheck(checklist); +} + + aclCheck_t * aclChecklistCreate(const acl_access * A, request_t * request, @@ -1943,6 +2136,7 @@ } } +#ifdef USE_BASIC_AUTH static void aclFreeProxyAuthUser(void *data) { @@ -1951,7 +2145,7 @@ xfree(u->passwd); memFree(u, MEM_ACL_PROXY_AUTH_USER); } - +#endif static void aclFreeIpData(void *p) { Index: squid/src/authenticate.c =================================================================== RCS file: /cvsroot/squid-sf//squid/src/authenticate.c,v retrieving revision 1.1.1.3.12.5 retrieving revision 1.1.1.3.12.6 diff -u -r1.1.1.3.12.5 -r1.1.1.3.12.6 --- squid/src/authenticate.c 20 Jul 2000 09:11:56 -0000 1.1.1.3.12.5 +++ squid/src/authenticate.c 2 Aug 2000 14:13:11 -0000 1.1.1.3.12.6 @@ -1,6 +1,6 @@ /* - * $Id: authenticate.c,v 1.1.1.3.12.5 2000/07/20 09:11:56 kinkie Exp $ + * $Id: authenticate.c,v 1.1.1.3.12.6 2000/08/02 14:13:11 rbcollins Exp $ * * DEBUG: section 29 Authenticator * AUTHOR: Duane Wessels @@ -34,11 +34,8 @@ */ #include "squid.h" -#ifdef USE_NTLM -#include "ntlm.h" -#endif -#define NTLM_STATIC_CHALLENGE "deadbeef" +#if defined(USE_BASIC_AUTHE) || defined(USE_NTLM) typedef struct { void *data; @@ -46,9 +43,16 @@ RH *handler; } authenticateStateData; +typedef struct { + void *data; + SRH *handler; +} authenticateStatefulStateData; + static HLPCB authenticateHandleReply; static void authenticateStateFree(authenticateStateData * r); +static void authenticateStatefulStateFree(authenticateStatefulStateData * r); static helper *authenticators = NULL; +static statefulhelper *ntlmauthenticators = NULL; static void authenticateHandleReply(void *data, char *reply) @@ -70,6 +74,50 @@ authenticateStateFree(r); } +static int +authenticateNTLMHandleReply(void *data, void * lastserver, char *reply) +{ + authenticateStatefulStateData *r = data; + int valid,morerequests=0; + char *t = NULL; + debug(29, 5) ("authenticateNTLMHandleReply: Helper: '%d' {%s}\n", lastserver, reply ? reply : ""); + if (reply) { + if (strncasecmp(reply, "CH ", 3) == 0) { + reply += 3; + morerequests=1; + debug(29,4)("authenticateNTLMHandleReply: helper '%d'\n",lastserver); + } + else if (strncasecmp(reply, "OK ", 3) == 0) { + /*give aclNTLM the full header - they must know when it's ok, and 'ERR' _could_ collide with valid domain */ + /* we're finished, leave release the helper*/ + morerequests=0; + } + else if (strncasecmp(reply, "RESETOK", 7) == 0) { + /* Helper successfully reset */ + /* note a reset request returning here MUST NOT set morerequests=1*/ + morerequests=0; + } + else if (strncasecmp(reply, "ERR", 3) == 0) { + if ((t = strchr(reply, ' ')))/* strip after a space */ + *t = '\0'; + } else + { + debug(29, 1) ("authenticateNTLMHandleReply: Unsupported helper response, '%s'\n", reply); + // fatal("unknown helper response"); + } + + if (*reply == '\0') + reply = NULL; + } + valid = cbdataValid(r->data); + cbdataUnlock(r->data); + if (valid) + r->handler(r->data, lastserver, reply); + authenticateStatefulStateFree(r); + return morerequests; +} + + static void authenticateStateFree(authenticateStateData * r) { @@ -77,6 +125,13 @@ } static void +authenticateStatefulStateFree(authenticateStatefulStateData * r) +{ + cbdataFree(r); +} + + +static void authenticateStats(StoreEntry * sentry) { storeAppendPrintf(sentry, "Authenticator Statistics:\n"); @@ -85,7 +140,7 @@ /**** PUBLIC FUNCTIONS ****/ - +/* send the initial data to an authenticator module */ void authenticateStart(acl_proxy_auth_user * auth_user, RH * handler, void *data) { @@ -105,306 +160,138 @@ cbdataLock(data); r->data = data; r->auth_user = auth_user; - -#ifdef USE_NTLM - if (Config.authenticate_ntlm_domain != NULL) { - /* These fields can be NULL ... */ - if (auth_user->user == NULL || auth_user->user[0] == '\0') - auth_user->user = "-"; - if (auth_user->domain == NULL || auth_user->domain[0] == '\0') - auth_user->domain = "-"; - if (auth_user->workstation == NULL || auth_user->workstation[0] == '\0') - auth_user->workstation = "-"; - if (auth_user->lmresponse == NULL || auth_user->lmresponse[0] == '\0') - auth_user->lmresponse = "-"; - if (auth_user->ntresponse == NULL || auth_user->ntresponse[0] == '\0') - auth_user->ntresponse = "-"; -#ifdef USE_SANE_AUTHENTICATOR_PROTOCOL - snprintf(buf,8192,"%s %s\n",auth_user->user, auth_user->passwd); -#else /* compatibility */ - snprintf(buf, 8192, "%s %s %s %s %s %s\n", - auth_user->user, auth_user->domain, - auth_user->workstation, NTLM_STATIC_CHALLENGE, - auth_user->lmresponse, auth_user->ntresponse); -#endif /* USE_SANE_AUTHENTICATOR_PROTOCOL */ - } else -#endif /* USE_NTLM */ - snprintf(buf, 8192, "%s %s\n", auth_user->user, auth_user->passwd); + snprintf(buf, 8192, "%s %s\n", auth_user->user, auth_user->passwd); helperSubmit(authenticators, buf, authenticateHandleReply, r); } +/* send the initial data to a stateful authenticator module */ void -authenticateInit(void) +authenticateNTLMStart(const char *proxy_auth, SRH * handler, void *data, helper_stateful_server * lastserver) { - static int init = 0; - if (!Config.Program.authenticate) + authenticateStatefulStateData *r = NULL; + char buf[8192]; +/* assert(auth_user);*/ + assert(handler); + debug(29, 5) ("authenticateNTLMStart: '%s'\n", proxy_auth); + if (Config.Program.ntlmauthenticate == NULL) { + debug(29, 5) ("authenticateNTLMStart: no NTLM program specified:'%s'\n", proxy_auth); + handler(data,0, NULL); return; - if (authenticators == NULL) - authenticators = helperCreate("authenticator"); - authenticators->cmdline = Config.Program.authenticate; - authenticators->n_to_start = Config.authenticateChildren; - authenticators->ipc_type = IPC_TCP_SOCKET; - helperOpenServers(authenticators); - if (!init) { - cachemgrRegister("authenticator", - "User Authenticator Stats", - authenticateStats, 0, 1); - init++; } + r = xcalloc(1, sizeof(authenticateStateData)); + cbdataAdd(r, cbdataXfree, 0); + r->handler = handler; + cbdataLock(data); + r->data = data; + snprintf(buf, 8192, "%s\n", proxy_auth); + helperStatefulSubmit(ntlmauthenticators, buf, authenticateNTLMHandleReply, r, lastserver); + debug(29,9)("authenticateNTLMstart: finished\n"); } void -authenticateShutdown(void) -{ - if (!authenticators) - return; - helperShutdown(authenticators); - if (!shutting_down) - return; - helperFree(authenticators); - authenticators = NULL; -} - -void -authenticateFixErrorHeader(HttpReply * rep, int type, int auth_type, int auth_state) +authenticateInit(void) { - char *fmt="UNKNOWN", *data=NULL; -#ifdef USE_NTLM - struct ntlm_challenge chal; - int len; -#endif - - switch (auth_type) { - case AUTHENTICATE_TYPE_BASIC: - httpHeaderPutStrf(&rep->header, type, "Basic realm=\"%s\"", Config.proxyAuthRealm); - break; -#ifdef USE_NTLM - case AUTHENTICATE_TYPE_NTLM: -/* if (Config.authenticate_ntlm_domain == NULL) */ - - if (auth_state) { - ntlmMakeChallenge(&chal); - fmt = "NTLM %s"; - len = sizeof(chal) - sizeof(chal.pad) + SSWAP(chal.target.maxlen); - debug(29, 5) ("authenticateFixErrorHeader: len: %d\n", len); - data = (char *)base64_encode_bin((char *)&chal, len); - } else { - fmt = "NTLM"; + static int init = 0; +#ifdef USE_BASIC_AUTH + if (Config.Program.authenticate){ + if (authenticators == NULL) + authenticators = helperCreate("authenticator"); + authenticators->cmdline = Config.Program.authenticate; + authenticators->n_to_start = Config.authenticateChildren; + authenticators->ipc_type = IPC_TCP_SOCKET; + helperOpenServers(authenticators); + if (!init) { + cachemgrRegister("authenticator", + "User Authenticator Stats", + authenticateStats, 0, 1); + init++; + } } - httpHeaderPutStrf(&rep->header, type, fmt, data); - break; #endif - case AUTHENTICATE_TYPE_NONE: - /* Not yet known. Send all supported methods to the client */ -#ifdef USE_NTLM - /* Explorer (the only client using NTLM authentication) requires - the NTLM header to be first. */ - httpHeaderPutStrf(&rep->header, type, "NTLM"); -#endif - httpHeaderPutStrf(&rep->header, type, "Basic realm=\"%s\"", Config.proxyAuthRealm); - break; - default: - fatal("Error: Unknown authentication type\n"); - break; - } - -} - #ifdef USE_NTLM -/* NTLM authentication by ad@netbsd.org - 07/1999 */ -/* XXX this is not done cleanly... */ - -/* - * Generates a challenge request. The randomness of the 8 byte - * challenge strings can be guarenteed to be poor at best. - */ -void -ntlmMakeChallenge(struct ntlm_challenge *chal) -{ -#ifndef NTLM_STATIC_CHALLENGE - static unsigned hash; - int r; -#endif - char *d; - int i; - - memset(chal, 0, sizeof(*chal)); - memcpy(chal->hdr.signature, "NTLMSSP", 8); - chal->flags = WSWAP(0x00018206); - chal->hdr.type = WSWAP(NTLM_CHALLENGE); - chal->unknown[6] = SSWAP(0x003a); - - d = (char *)chal + 48; - i = 0; - - if (Config.authenticate_ntlm_domain != NULL) { - while (Config.authenticate_ntlm_domain[i]) - *d++ = toupper(Config.authenticate_ntlm_domain[i++]); - } else { - *d++='\0'; /* Empty domain. Will it work? */ - } - - chal->target.offset = WSWAP(48); - chal->target.maxlen = SSWAP(i); - chal->target.len = chal->target.maxlen; - -#ifdef NTLM_STATIC_CHALLENGE - memcpy(chal->challenge, NTLM_STATIC_CHALLENGE, 8); -#else - r = (int)rand(); - r = (hash ^ r) + r; - - for (i = 0; i < 8; i++) { - chal->challenge[i] = r; - r = (r >> 2) ^ r; + if (Config.Program.ntlmauthenticate){ + if (ntlmauthenticators == NULL) + ntlmauthenticators = helperStatefulCreate("ntlmauthenticator"); + ntlmauthenticators->cmdline = Config.Program.ntlmauthenticate; + ntlmauthenticators->n_to_start = Config.ntlmauthenticateChildren; + ntlmauthenticators->ipc_type = IPC_TCP_SOCKET; + helperStatefulOpenServers(ntlmauthenticators); + if (!init) { + cachemgrRegister("ntlmauthenticator", + "User NTLM Authenticator Stats", + authenticateStats, 0, 1); + init++; + } } - - hash = r; #endif } -/* - * Check the vailidity of a request header. Return -1 on error. - */ -int -ntlmCheckHeader(struct ntlmhdr *hdr, int type) -{ - /* - * Must be the correct security package and request type. The - * 8 bytes compared includes the ASCII 'NUL'. - */ - if (memcmp(hdr->signature, "NTLMSSP", 8) != 0) { - debug(29, 5) ("ntlmCheckHeader: bad header signature\n"); - return (-1); - } - - if (WSWAP(hdr->type) != type) { - debug(29, 5) ("ntlmCheckHeader: type is %d, wanted %d\n", WSWAP(hdr->type), type); - return (-1); - } - - return (0); -} - -/* - * Extract a string from an NTLM request and return as ASCII. - */ -char * -ntlmGetString(ntlmhdr *hdr, strhdr *str, int flags) +void +authenticateShutdown(void) { - static char buf[512]; - u_short *s, c; - char *d, *sc; - int l, o; - - l = SSWAP(str->len); - o = WSWAP(str->offset); - - /* Sanity checks. XXX values arbitrarialy chosen */ - if (l <= 0 || l >= 32 || o >= 256) { - debug(29, 5) ("ntlmGetString: insane: l:%d o:%d\n", l, o); - return (NULL); - } - - if ((flags & 2) == 0) { - /* UNICODE string */ - s = (u_short *)((char *)hdr + o); - d = buf; - - for (l >>= 1; l; s++, l--) { - c = SSWAP(*s); - if (c > 254 || c == '\0' || !isprint(c)) { - debug(29, 5) ("ntlmGetString: bad uni: %04x\n", c); - return (NULL); - } - *d++ = c; - debug(29, 5) ("ntlmGetString: conv: '%c'\n", c); - } - - *d = 0; - } else { - /* ASCII string */ - sc = (char *)hdr + o; - d = buf; - - for (; l; l--) { - if (*sc == '\0' || !isprint(*sc)) { - debug(29, 5) ("ntlmGetString: bad ascii: %04x\n", c); - return (NULL); - } - *d++ = *sc++; - } - - *d = 0; - } - - return (buf); + if (authenticators) + helperShutdown(authenticators); + if (ntlmauthenticators) + helperStatefulShutdown(ntlmauthenticators); + if (!shutting_down) + return; + helperFree(authenticators); + authenticators = NULL; + helperStatefulFree(ntlmauthenticators); + ntlmauthenticators = NULL; } -/* - * Decode the strings in an NTLM authentication request - */ -int ntlmDecodeAuth(struct ntlm_authenticate *auth, char *buf, size_t size, - char **user, char **pass) +void +authenticateFixErrorHeader(HttpReply * rep, int type, auth_t auth_type, int auth_state, char *authchallenge) { - char *p; - int s; - - if (ntlmCheckHeader(&auth->hdr, NTLM_AUTHENTICATE)) { - debug(29, 5) ("ntlmDecodeAuth: header check fails\n"); - return -1; - } + debug(29,9)("authenticateFixErrorHeader: headertype:%d authtype:%d authstate:%d\n",type,auth_type,auth_state); - debug(29, 5) ("ntlmDecodeAuth: size of %d\n", size); - debug(29, 5) ("ntlmDecodeAuth: flg %08x\n", auth->flags); - debug(29, 5) ("ntlmDecodeAuth: usr o(%d) l(%d)\n", auth->user.offset, - auth->user.len); - - if (user) { - *user = buf; - - if ((p = ntlmGetString(&auth->hdr, &auth->domain, 2)) == NULL) - p = Config.authenticate_ntlm_domain; - if ((s = strlen(p) + 1) >= size) - return 0; - strcpy(buf, p); - size -= s; - buf += (s - 1); -#ifdef USE_SANE_AUTHENTICATOR_PROTOCOL - *buf++ = '\\'; /* Using \ is more consistent with MS-proxy */ -#else - *buf++ = '.'; -#endif - - p = ntlmGetString(&auth->hdr, &auth->user, 2); - if ((s = strlen(p) + 1) >= size) - return 0; - while (*p) - *buf++ = tolower(*p++); - *buf++ = '\0'; - size -= s; - - debug(29, 5) ("ntlmDecodeAuth: user: %s\n", *user); + switch (auth_type){ + case AUTH_UNKNOWN: + /* we don't know the auth type, so we cannot know the state */ + assert(auth_state==AUTHENTICATE_STATE_NONE); + /* the header order breaks RFC 2617, __but__ explorer doesn't use NTLM otherwise */ + #ifdef USE_NTLM + debug(29, 5) ("authenticateFixErrorHeader: Sending type:%d header: 'NTLM'\n",type); + httpHeaderPutStrf(&rep->header, type, "NTLM"); + #endif + #ifdef USE_BASIC_AUTH + debug(29, 5) ("authenticateFixErrorHeader: Sending type:%d header: 'Basic realm=\"%s\"'\n",type,Config.proxyAuthRealm); + httpHeaderPutStrf(&rep->header, type, "Basic realm=\"%s\"", Config.proxyAuthRealm); + #endif + break; + #ifdef USE_BASIC_AUTH + case AUTH_BASIC: + assert(auth_state!=AUTHENTICATE_STATE_NONE); + debug(29, 5) ("authenticateFixErrorHeader: Sending type:%d header: 'Basic realm=\"%s\"'\n",type,Config.proxyAuthRealm); + httpHeaderPutStrf(&rep->header, type, "Basic realm=\"%s\"", Config.proxyAuthRealm); + break; + #endif /* BASIC */ + #ifdef USE_NTLM + case AUTH_NTLM: + /* if auth_state is none, the acl causing the denied was an NTLM _only_ + * not currently support in other routines..*/ + switch (auth_state){ + case AUTHENTICATE_STATE_NONE: + debug(29, 5) ("authenticateFixErrorHeader: Sending type:%d header: 'NTLM'\n",type); + httpHeaderPutStrf(&rep->header, type, "NTLM"); + break; + case AUTHENTICATE_STATE_CHALLENGE: /* we are 'waiting' for a response */ + /* pass the challenge from conn->authChallenge to the client */ + debug(29, 5) ("authenticateFixErrorHeader: Sending type:%d header: 'NTLM %s'\n",type,authchallenge); + httpHeaderPutStrf(&rep->header, type, "NTLM %s", authchallenge); + break; + default: + debug(29,1)("authenticateFixErrorHeader: state %d.\n",auth_state); + fatal("unexpected state in AuthenticatefixErrorHeader.\n"); + } + break; + #endif /* NTLM */ + case AUTH_BROKEN: + /* fall thru */ + default: + fatal("Unknown authentication type"); } - - if (pass) { -#ifdef notdef /* XXX */ - p = ntlmGetString(&auth->hdr, &auth->ntresponse, 2); -#else - p = "PASSWORD"; -#endif - if (p==NULL || (s = strlen(p) + 1) >= size) { - p="-"; - return 0; - } - strcpy(buf, p); - *pass = buf; - size -= s; - buf += s; - debug(29, 5) ("ntlmDecodeAuth: ntresponse: %s\n", *pass); - } - - return 0; + return; } - -#endif /* USE_NTLM */ +#endif /*USE BASIC or NTLM */ Index: squid/src/cache_cf.c =================================================================== RCS file: /cvsroot/squid-sf//squid/src/cache_cf.c,v retrieving revision 1.1.1.3.4.1.2.5 retrieving revision 1.1.1.3.4.1.2.6 diff -u -r1.1.1.3.4.1.2.5 -r1.1.1.3.4.1.2.6 --- squid/src/cache_cf.c 31 Jul 2000 21:56:43 -0000 1.1.1.3.4.1.2.5 +++ squid/src/cache_cf.c 2 Aug 2000 14:13:11 -0000 1.1.1.3.4.1.2.6 @@ -1,6 +1,6 @@ /* - * $Id: cache_cf.c,v 1.1.1.3.4.1.2.5 2000/07/31 21:56:43 hno Exp $ + * $Id: cache_cf.c,v 1.1.1.3.4.1.2.6 2000/08/02 14:13:11 rbcollins Exp $ * * DEBUG: section 3 Configuration File Parsing * AUTHOR: Harvest Derived @@ -294,6 +294,20 @@ Config.redirectChildren = DefaultRedirectChildrenMax; } } +#ifdef USE_NTLM + if (Config.Program.ntlmauthenticate) { + if (Config.ntlmauthenticateChildren < 1) { + Config.ntlmauthenticateChildren = 0; + wordlistDestroy(&Config.Program.ntlmauthenticate); + } else if (Config.ntlmauthenticateChildren > DefaultAuthenticateChildrenMax) { + debug(3, 0) ("WARNING: authenticate_children_ntlm was set to a bad value: %d\n", + Config.ntlmauthenticateChildren); + debug(3, 0) ("Setting it to the maximum (%d).\n", DefaultAuthenticateChildrenMax); + Config.ntlmauthenticateChildren = DefaultAuthenticateChildrenMax; + } + } +#endif +#ifdef USE_BASIC_AUTH if (Config.Program.authenticate) { if (Config.authenticateChildren < 1) { Config.authenticateChildren = 0; @@ -305,6 +319,7 @@ Config.authenticateChildren = DefaultAuthenticateChildrenMax; } } +#endif if (Config.Accel.host) { snprintf(buf, BUFSIZ, "http://%s:%d", Config.Accel.host, Config.Accel.port); Config2.Accel.prefix = xstrdup(buf); @@ -360,6 +375,8 @@ #endif if (Config.Program.redirect) requirePathnameExists("redirect_program", Config.Program.redirect->key); + if (Config.Program.ntlmauthenticate) + requirePathnameExists("authenticate_program_ntlm", Config.Program.ntlmauthenticate->key); if (Config.Program.authenticate) requirePathnameExists("authenticate_program", Config.Program.authenticate->key); requirePathnameExists("Icon Directory", Config.icons.directory); Index: squid/src/cf.data.pre =================================================================== RCS file: /cvsroot/squid-sf//squid/src/cf.data.pre,v retrieving revision 1.1.1.3.4.1.2.12 retrieving revision 1.1.1.3.4.1.2.13 diff -u -r1.1.1.3.4.1.2.12 -r1.1.1.3.4.1.2.13 --- squid/src/cf.data.pre 31 Jul 2000 21:56:43 -0000 1.1.1.3.4.1.2.12 +++ squid/src/cf.data.pre 2 Aug 2000 14:13:11 -0000 1.1.1.3.4.1.2.13 @@ -1,6 +1,6 @@ # -# $Id: cf.data.pre,v 1.1.1.3.4.1.2.12 2000/07/31 21:56:43 hno Exp $ +# $Id: cf.data.pre,v 1.1.1.3.4.1.2.13 2000/08/02 14:13:11 rbcollins Exp $ # # # SQUID Internet Object Cache http://squid.nlanr.net/Squid/ @@ -1070,11 +1070,40 @@ are sent. DOC_END +NAME: authenticate_program_ntlm +TYPE: wordlist +LOC: Config.Program.ntlmauthenticate +DEFAULT: none +IFDEF: USE_NTLM +DOC_START + Specify the command for the external ntlm authenticator. Such a + program reads a line containing the uuencoded NEGOTIATE and replies + with the ntlm CHALLENGE, then waits for the response and answers with + "OK" or "ERR" in an endless loop. If you use an ntlm authenticator, + make sure you have 1 acl of type proxy_auth. By default, the + ntlm authenticator_program is not used. + + authenticate_program @DEFAULT_PREFIX@/bin/ntlm_auth +DOC_END + +NAME: authenticate_children_ntlm +TYPE: int +DEFAULT: 5 +IFDEF: USE_NTLM +LOC: Config.ntlmauthenticateChildren +DOC_START + The number of ntlm authenticator processes to spawn (default 5). If you + start too few Squid will have to wait for them to process a backlog + of usercode/password verifications, slowing it down. When password + verifications are done via a (slow) network you are likely to need + lots of ntlm authenticator processes. +DOC_END NAME: authenticate_program TYPE: wordlist LOC: Config.Program.authenticate DEFAULT: none +IFDEF: USE_BASIC_AUTH DOC_START Specify the command for the external authenticator. Such a program reads a line containing "username password" and replies @@ -1096,6 +1125,7 @@ NAME: authenticate_children TYPE: int DEFAULT: 5 +IFDEF: USE_BASIC_AUTH LOC: Config.authenticateChildren DOC_START The number of authenticator processes to spawn (default 5). If you @@ -1108,6 +1138,7 @@ NAME: authenticate_ttl TYPE: time_t DEFAULT: 1 hour +IFDEF: USE_BASIC_AUTH LOC: Config.authenticateTTL DOC_START The time a checked username/password combination remains cached. @@ -1119,6 +1150,7 @@ TYPE: time_t LOC: Config.authenticateIpTTL DEFAULT: 0 seconds +IFDEF: USE_BASIC_AUTH DOC_START With this option you control how long a proxy authentication will be bound to a specific IP address. If a request using @@ -1137,20 +1169,21 @@ See also authenticate_ip_ttl_is_strict DOC_END -NAME: authenticate_ntlm_domain +NAME: authenticate_ntlm_default_domain TYPE: string DEFAULT: none IFDEF: USE_NTLM -LOC: Config.authenticate_ntlm_domain +LOC: Config.authenticate_ntlm_default_domain DOC_START - For NTLM authentication, the NT domain that this server purports to - be part of. This must be set if you wish to use NTLM authentication. + For NTLM authentication, the default NT domain name that squid + should prepend to usernames in proxy-auth acl lists. DOC_END NAME: authenticate_ip_ttl_is_strict TYPE: onoff LOC: Config.onoff.authenticateIpTTLStrict DEFAULT: on +IFDEF: USE_BASIC_AUTH DOC_START This option makes authenticate_ip_ttl a bit stricted. With this enabled authenticate_ip_ttl will deny all access from other IP Index: squid/src/cf_gen_defines =================================================================== RCS file: /cvsroot/squid-sf//squid/src/cf_gen_defines,v retrieving revision 1.1.10.3 retrieving revision 1.1.10.4 diff -u -r1.1.10.3 -r1.1.10.4 --- squid/src/cf_gen_defines 16 Jul 2000 00:34:00 -0000 1.1.10.3 +++ squid/src/cf_gen_defines 2 Aug 2000 14:13:11 -0000 1.1.10.4 @@ -18,6 +18,8 @@ define["USE_WCCP"]="--enable-wccp" define["USE_UNLINKD"]="--enable-unlinkd" define["USE_REFERER_LOG"]="--enable-referer-log" + define["USE_NTLM"]="--enable-ntlm-authentication" + define["USE_BASIC_AUTH"]="--enable-basic-authentication" } /^IFDEF:/ { print "{\"" $2 "\", \"" (define[$2] != "" ? define[$2] : ("-D" $2)) "\", " Index: squid/src/client_side.c =================================================================== RCS file: /cvsroot/squid-sf//squid/src/client_side.c,v retrieving revision 1.1.1.3.4.1.2.16 retrieving revision 1.1.1.3.4.1.2.17 diff -u -r1.1.1.3.4.1.2.16 -r1.1.1.3.4.1.2.17 --- squid/src/client_side.c 31 Jul 2000 21:56:43 -0000 1.1.1.3.4.1.2.16 +++ squid/src/client_side.c 2 Aug 2000 14:13:11 -0000 1.1.1.3.4.1.2.17 @@ -1,6 +1,6 @@ /* - * $Id: client_side.c,v 1.1.1.3.4.1.2.16 2000/07/31 21:56:43 hno Exp $ + * $Id: client_side.c,v 1.1.1.3.4.1.2.17 2000/08/02 14:13:11 rbcollins Exp $ * * DEBUG: section 33 Client-side Routines * AUTHOR: Duane Wessels @@ -151,7 +151,7 @@ ch = aclChecklistCreate(acl, http->request, conn->ident); -#if USE_IDENT +#if defined (USE_IDENT) || defined (USE_NTLM) /* * hack for ident ACL. It needs to get full addresses, and a * place to store the ident result on persistent connections... @@ -176,7 +176,7 @@ /* * returns true if client specified that the object must come from the cache - * witout contacting origin server + * without contacting origin server */ static int clientOnlyIfCached(clientHttpRequest * http) @@ -214,8 +214,6 @@ int page_id = -1; http_status status; ErrorState *err = NULL; - int auth_type = http->acl_checklist->auth_type; - int auth_state = http->acl_checklist->auth_state; debug(33, 2) ("The request %s %s is %s, because it matched '%s'\n", RequestMethodStr[http->request->method], http->uri, @@ -260,8 +258,9 @@ err = errorCon(page_id, status); err->request = requestLink(http->request); err->src_addr = http->conn->peer.sin_addr; - err->auth_type = auth_type; - err->auth_state = auth_state; + err->auth_type = http->conn->auth_type; + err->auth_state = http->conn->auth_state; + err->authchallenge = http->conn->authchallenge; errorAppendEntry(http->entry, err); } } @@ -789,6 +788,19 @@ cbdataFree(http); } +/* This just releases the stateful helper used by a connection */ +static void +connStateFreeHelperDone(void * data, void * lastserver, char * result) +{ + ConnStateData *connState = data; + assert(!data); + if (result && (strncasecmp(result, "OK", 2) != 0)){ + debug(33,1)("connStateFreeHelperDone: error resetting stateful helper, result '%s'.\n",result); + } + connState->authhelper=0; + connStateFree(connState->fd,data); +} + /* This is a handler normally called by comm_close() */ static void connStateFree(int fd, void *data) @@ -797,6 +809,10 @@ clientHttpRequest *http; debug(33, 3) ("connStateFree: FD %d\n", fd); assert(connState != NULL); + if (connState->authhelper){ + debug(33,4)("connStateFree: FD %d releasing helper %d.\n",fd,connState->authhelper); + authenticateNTLMStart("RESET",connStateFreeHelperDone,connState,connState->authhelper); + } clientdbEstablished(connState->peer.sin_addr, -1); /* decrement */ while ((http = connState->chr) != NULL) { assert(http->conn == connState); Index: squid/src/defines.h =================================================================== RCS file: /cvsroot/squid-sf//squid/src/defines.h,v retrieving revision 1.1.1.3.12.5 retrieving revision 1.1.1.3.12.6 diff -u -r1.1.1.3.12.5 -r1.1.1.3.12.6 --- squid/src/defines.h 31 Jul 2000 21:56:43 -0000 1.1.1.3.12.5 +++ squid/src/defines.h 2 Aug 2000 14:13:11 -0000 1.1.1.3.12.6 @@ -1,6 +1,6 @@ /* - * $Id: defines.h,v 1.1.1.3.12.5 2000/07/31 21:56:43 hno Exp $ + * $Id: defines.h,v 1.1.1.3.12.6 2000/08/02 14:13:11 rbcollins Exp $ * * * SQUID Internet Object Cache http://squid.nlanr.net/Squid/ @@ -133,15 +133,27 @@ #define REDIRECT_DONE 2 #define AUTHENTICATE_AV_FACTOR 1000 - +/* AUTHENTICATION */ #define AUTHENTICATE_STATE_NONE 0 -#define AUTHENTICATE_STATE_CHALLENGE 1 -#define AUTHENTICATE_STATE_DONE 2 +#define AUTHENTICATE_STATE_NEGOTIATE 1 +#define AUTHENTICATE_STATE_CHALLENGE 2 +#define AUTHENTICATE_STATE_RESPONSE 3 +#define AUTHENTICATE_STATE_DONE 4 #define AUTHENTICATE_TYPE_NONE 0 #define AUTHENTICATE_TYPE_BASIC 1 #define AUTHENTICATE_TYPE_NTLM 2 +#define NTLM_CHALLENGE_SZ 256 + +/*ACL CHECKS */ +/* 0 and 1 are reserved for DENY and ALLOW in ALL Cases. DO NOT ALTER */ +#define ACLMATCHPROXY_UNKNOWN_TYPE 0 +#define ACLMATCHPROXY_USERDENIED 0 +#define ACLMATCHPROXY_USERALLOWED 1 +#define ACLMATCHPROXY_NEEDHEADER 2 +#define ACLMATCHPROXY_CHECKHEADER 3 + #define CONNECT_PORT 443 #define current_stacksize(stack) ((stack)->top - (stack)->base) Index: squid/src/enums.h =================================================================== RCS file: /cvsroot/squid-sf//squid/src/enums.h,v retrieving revision 1.1.1.3.12.5 retrieving revision 1.1.1.3.12.6 diff -u -r1.1.1.3.12.5 -r1.1.1.3.12.6 --- squid/src/enums.h 31 Jul 2000 21:56:43 -0000 1.1.1.3.12.5 +++ squid/src/enums.h 2 Aug 2000 14:13:11 -0000 1.1.1.3.12.6 @@ -1,6 +1,6 @@ /* - * $Id: enums.h,v 1.1.1.3.12.5 2000/07/31 21:56:43 hno Exp $ + * $Id: enums.h,v 1.1.1.3.12.6 2000/08/02 14:13:11 rbcollins Exp $ * * * SQUID Internet Object Cache http://squid.nlanr.net/Squid/ @@ -130,7 +130,10 @@ ACL_LOOKUP_NEEDED, ACL_LOOKUP_PENDING, ACL_LOOKUP_DONE, - ACL_PROXY_AUTH_NEEDED + ACL_PROXY_AUTH_NEEDED, + ACL_HELPER_START, + ACL_HELPER_PENDING, + ACL_HELPER_DONE } acl_lookup_state; enum { @@ -506,6 +509,13 @@ ACCESS_REQ_PROXY_AUTH } allow_t; +typedef enum { + AUTH_UNKNOWN, /* default */ + AUTH_BASIC, + AUTH_NTLM, + AUTH_BROKEN /* known type, but broken data */ +} auth_t; + #if SQUID_SNMP enum { SNMP_C_VIEW, @@ -562,8 +572,11 @@ MEM_HASH_LINK, MEM_HASH_TABLE, MEM_HELPER, - MEM_HELPER_REQUEST, + MEM_HELPER_STATEFUL, MEM_HELPER_SERVER, + MEM_HELPER_STATEFUL_SERVER, + MEM_HELPER_REQUEST, + MEM_HELPER_STATEFUL_REQUEST, MEM_HIERARCHYLOGENTRY, #if USE_HTCP MEM_HTCP_SPECIFIER, Index: squid/src/errorpage.c =================================================================== RCS file: /cvsroot/squid-sf//squid/src/errorpage.c,v retrieving revision 1.1.1.3.10.5 retrieving revision 1.1.1.3.10.6 diff -u -r1.1.1.3.10.5 -r1.1.1.3.10.6 --- squid/src/errorpage.c 31 Jul 2000 21:56:43 -0000 1.1.1.3.10.5 +++ squid/src/errorpage.c 2 Aug 2000 14:13:11 -0000 1.1.1.3.10.6 @@ -1,6 +1,6 @@ /* - * $Id: errorpage.c,v 1.1.1.3.10.5 2000/07/31 21:56:43 hno Exp $ + * $Id: errorpage.c,v 1.1.1.3.10.6 2000/08/02 14:13:11 rbcollins Exp $ * * DEBUG: section 4 Error Generation * AUTHOR: Duane Wessels @@ -277,18 +277,20 @@ storeBuffer(entry); rep = errorBuildReply(err); /* Add authentication header */ + debug(28,6)("errorpage:state:type %d:%d.\n",err->auth_state,err->auth_type); switch (err->http_status) { case HTTP_PROXY_AUTHENTICATION_REQUIRED: /* Proxy authorisation needed */ - authenticateFixErrorHeader(rep, HDR_PROXY_AUTHENTICATE, err->auth_type, err->auth_state); - break; + authenticateFixErrorHeader(rep, HDR_PROXY_AUTHENTICATE, err->auth_type, err->auth_state, err->authchallenge); + break; case HTTP_UNAUTHORIZED: /* WWW Authorisation needed */ - authenticateFixErrorHeader(rep, HDR_WWW_AUTHENTICATE, err->auth_type, err->auth_state); - break; + authenticateFixErrorHeader(rep, HDR_WWW_AUTHENTICATE, err->auth_type, err->auth_state, err->authchallenge); + break; default: /* Keep GCC happy */ - break; + /* some other HTTP status */ + break; } httpReplySwapOut(rep, entry); httpReplyAbsorb(mem->reply, rep); Index: squid/src/helper.c =================================================================== RCS file: /cvsroot/squid-sf//squid/src/helper.c,v retrieving revision 1.1.1.3.12.3 retrieving revision 1.1.1.3.12.4 diff -u -r1.1.1.3.12.3 -r1.1.1.3.12.4 --- squid/src/helper.c 3 Jul 2000 21:44:31 -0000 1.1.1.3.12.3 +++ squid/src/helper.c 2 Aug 2000 14:13:11 -0000 1.1.1.3.12.4 @@ -1,6 +1,6 @@ /* - * $Id: helper.c,v 1.1.1.3.12.3 2000/07/03 21:44:31 hno Exp $ + * $Id: helper.c,v 1.1.1.3.12.4 2000/08/02 14:13:11 rbcollins Exp $ * * DEBUG: section 29 Helper process maintenance * AUTHOR: Harvest Derived? @@ -38,13 +38,21 @@ #define HELPER_MAX_ARGS 64 static PF helperHandleRead; +static PF helperStatefulHandleRead; static PF helperServerFree; +static PF helperStatefulServerFree; static void Enqueue(helper * hlp, helper_request *); +static void StatefulEnqueue(statefulhelper * hlp, helper_stateful_request *); static helper_request *Dequeue(helper * hlp); +static helper_stateful_request *StatefulDequeue(statefulhelper * hlp); static helper_server *GetFirstAvailable(helper * hlp); +static helper_stateful_server *StatefulGetFirstAvailable(statefulhelper * hlp); static void helperDispatch(helper_server * srv, helper_request * r); +static void helperStatefulDispatch(helper_stateful_server * srv, helper_stateful_request * r); static void helperKickQueue(helper * hlp); +static void helperStatefulKickQueue(statefulhelper * hlp); static void helperRequestFree(helper_request * r); +static void helperStatefulRequestFree(helper_stateful_request * r); void @@ -125,6 +133,84 @@ } void +helperStatefulOpenServers(statefulhelper * hlp) +{ + char *s; + char *progname; + char *shortname; + char *procname; + char *args[HELPER_MAX_ARGS]; + char fd_note_buf[FD_DESC_SZ]; + helper_stateful_server *srv; + int nargs = 0; + int k; + int x; + int rfd; + int wfd; + wordlist *w; + if (hlp->cmdline == NULL) + return; + progname = hlp->cmdline->key; + if ((s = strrchr(progname, '/'))) + shortname = xstrdup(s + 1); + else + shortname = xstrdup(progname); + debug(29, 1) ("helperStatefulOpenServers: Starting %d '%s' processes\n", + hlp->n_to_start, shortname); + procname = xmalloc(strlen(shortname) + 3); + snprintf(procname, strlen(shortname) + 3, "(%s)", shortname); + args[nargs++] = procname; + for (w = hlp->cmdline->next; w && nargs < HELPER_MAX_ARGS; w = w->next) + args[nargs++] = w->key; + args[nargs++] = NULL; + assert(nargs <= HELPER_MAX_ARGS); + for (k = 0; k < hlp->n_to_start; k++) { + getCurrentTime(); + rfd = wfd = -1; + x = ipcCreate(hlp->ipc_type, + progname, + args, + shortname, + &rfd, + &wfd); + if (x < 0) { + debug(29, 1) ("WARNING: Cannot run '%s' process.\n", progname); + continue; + } + hlp->n_running++; + srv = memAllocate(MEM_HELPER_STATEFUL_SERVER); + cbdataAdd(srv, memFree, MEM_HELPER_STATEFUL_SERVER); + srv->flags.alive = 1; + srv->index = k; + srv->rfd = rfd; + srv->wfd = wfd; + srv->buf = memAllocate(MEM_8K_BUF); + srv->buf_sz = 8192; + srv->offset = 0; + srv->parent = hlp; + cbdataLock(hlp); /* lock because of the parent backlink */ + dlinkAddTail(srv, &srv->link, &hlp->servers); + if (rfd == wfd) { + snprintf(fd_note_buf, FD_DESC_SZ, "%s #%d", shortname, k + 1); + fd_note(rfd, fd_note_buf); + } else { + snprintf(fd_note_buf, FD_DESC_SZ, "reading %s #%d", shortname, k + 1); + fd_note(rfd, fd_note_buf); + snprintf(fd_note_buf, FD_DESC_SZ, "writing %s #%d", shortname, k + 1); + fd_note(wfd, fd_note_buf); + } + commSetNonBlocking(rfd); + if (wfd != rfd) + commSetNonBlocking(wfd); + comm_add_close_handler(rfd, helperStatefulServerFree, srv); + } + safe_free(shortname); + safe_free(procname); + helperStatefulKickQueue(hlp); +} + + +void helperSubmit(helper * hlp, const char *buf, HLPCB * callback, void *data) { helper_request *r = memAllocate(MEM_HELPER_REQUEST); @@ -142,9 +228,39 @@ helperDispatch(srv, r); else Enqueue(hlp, r); + debug(29,9) ("helperSubmit: %s\n",buf); } void +helperStatefulSubmit(statefulhelper * hlp, const char *buf, HLPSCB * callback, void *data, helper_stateful_server * lastserver) +{ + helper_stateful_request *r = memAllocate(MEM_HELPER_STATEFUL_REQUEST); + helper_stateful_server *srv; + if (hlp == NULL) { + debug(29, 3) ("helperStatefulSubmit: hlp == NULL\n"); + callback(data,0, NULL); + return; + } + r->callback = callback; + r->data = data; + r->buf = xstrdup(buf); + cbdataLock(r->data); + if (lastserver){ + helperStatefulDispatch(lastserver, r); + } + else + { + if ((srv = StatefulGetFirstAvailable(hlp))){ + helperStatefulDispatch(srv, r); + } + else + StatefulEnqueue(hlp, r); + } + debug(29,9) ("helperStatefulSubmit: %s\n",buf); +} + + +void helperStats(StoreEntry * sentry, helper * hlp) { helper_server *srv; @@ -221,6 +337,37 @@ } } +void +helperStatefulShutdown(statefulhelper * hlp) +{ + dlink_node *link = hlp->servers.head; + helper_server *srv; + while (link) { + srv = link->data; + link = link->next; + if (!srv->flags.alive) { + debug(34, 3) ("helperStatefulShutdown: %s #%d is NOT ALIVE.\n", + hlp->id_name, srv->index + 1); + continue; + } + srv->flags.shutdown = 1; /* request it to shut itself down */ + if (srv->flags.busy) { + debug(34, 3) ("helperStatefulShutdown: %s #%d is BUSY.\n", + hlp->id_name, srv->index + 1); + continue; + } + if (srv->flags.closing) { + debug(34, 3) ("helperStatefulShutdown: %s #%d is CLOSING.\n", + hlp->id_name, srv->index + 1); + continue; + } + srv->flags.closing = 1; + comm_close(srv->wfd); + srv->wfd = -1; + } +} + + helper * helperCreate(const char *name) { @@ -230,6 +377,16 @@ return hlp; } +helper * +helperStatefulCreate(const char *name) +{ + helper *hlp = memAllocate(MEM_HELPER_STATEFUL); + cbdataAdd(hlp, memFree, MEM_HELPER_STATEFUL); + hlp->id_name = name; + return hlp; +} + + void helperFree(helper * hlp) { @@ -240,6 +397,17 @@ cbdataFree(hlp); } +void +helperStatefulFree(helper * hlp) +{ + /* note, don't free hlp->name, it probably points to static memory */ + if (hlp->queue.head) + debug(29, 0) ("WARNING: freeing %s helper with %d requests queued\n", + hlp->id_name, hlp->stats.queue_size); + cbdataFree(hlp); +} + + /* ====================================================================== */ /* LOCAL FUNCTIONS */ /* ====================================================================== */ @@ -277,6 +445,39 @@ } static void +helperStatefulServerFree(int fd, void *data) +{ + helper_stateful_server *srv = data; + statefulhelper *hlp = srv->parent; + helper_stateful_request *r; + assert(srv->rfd == fd); + if (srv->buf) { + memFree(srv->buf, MEM_8K_BUF); + srv->buf = NULL; + } + if ((r = srv->request)) { + if (cbdataValid(r->data)) + r->callback(r->data, srv,srv->buf); + helperStatefulRequestFree(r); + srv->request = NULL; + } + if (srv->wfd != srv->rfd && srv->wfd != -1) + comm_close(srv->wfd); + dlinkDelete(&srv->link, &hlp->servers); + hlp->n_running--; + assert(hlp->n_running >= 0); + if (!srv->flags.shutdown) { + debug(34, 0) ("WARNING: %s #%d (FD %d) exited\n", + hlp->id_name, srv->index + 1, fd); + if (hlp->n_running < hlp->n_to_start / 2) + fatalf("Too few %s processes are running", hlp->id_name); + } + cbdataUnlock(srv->parent); + cbdataFree(srv); +} + + +static void helperHandleRead(int fd, void *data) { int len; @@ -331,6 +532,60 @@ } static void +helperStatefulHandleRead(int fd, void *data) +{ + int len; + char *t = NULL; + helper_stateful_server *srv = data; + helper_stateful_request *r; + helper *hlp = srv->parent; + assert(fd == srv->rfd); + assert(cbdataValid(data)); + statCounter.syscalls.sock.reads++; + len = read(fd, srv->buf + srv->offset, srv->buf_sz - srv->offset); + fd_bytes(fd, len, FD_READ); + debug(29, 5) ("helperStatefulHandleRead: %d bytes from %s #%d.\n", + len, hlp->id_name, srv->index + 1); + if (len <= 0) { + if (len < 0) + debug(50, 1) ("helperStatefulHandleRead: FD %d read: %s\n", fd, xstrerror()); + comm_close(fd); + return; + } + srv->offset += len; + srv->buf[srv->offset] = '\0'; + r = srv->request; + if (r == NULL) { + /* someone spoke without being spoken to */ + debug(29, 1) ("helperStatefulHandleRead: unexpected read from %s #%d, %d bytes\n", + hlp->id_name, srv->index + 1, len); + srv->offset = 0; + } else if ((t = strchr(srv->buf, '\n'))) { + /* end of reply found */ + debug(29, 3) ("helperStatefulHandleRead: end of reply found\n"); + *t = '\0'; + if (cbdataValid(r->data)) + if(!r->callback(r->data,srv, srv->buf)) /*if non-zero reserve helper */ + srv->flags.busy = 0; + srv->offset = 0; + helperStatefulRequestFree(r); + srv->request = NULL; + hlp->stats.replies++; + hlp->stats.avg_svc_time = + intAverage(hlp->stats.avg_svc_time, + tvSubMsec(srv->dispatch_time, current_time), + hlp->stats.replies, REDIRECT_AV_FACTOR); + if (srv->flags.shutdown) { + comm_close(srv->wfd); + srv->wfd = -1; + } else + helperKickQueue(hlp); + } else { + commSetSelect(srv->rfd, COMM_SELECT_READ, helperStatefulHandleRead, srv, 0); + } +} + +static void Enqueue(helper * hlp, helper_request * r) { dlink_node *link = memAllocate(MEM_DLINK_NODE); @@ -350,6 +605,27 @@ debug(14, 1) ("Consider increasing the number of %s processes in your config file.\n", hlp->id_name); } +static void +StatefulEnqueue(helper * hlp, helper_stateful_request * r) +{ + dlink_node *link = memAllocate(MEM_DLINK_NODE); + dlinkAddTail(r, link, &hlp->queue); + 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); +} + + static helper_request * Dequeue(helper * hlp) { @@ -364,6 +640,20 @@ return r; } +static helper_stateful_request * +StatefulDequeue(statefulhelper * hlp) +{ + dlink_node *link; + helper_stateful_request *r = NULL; + if ((link = hlp->queue.head)) { + r = link->data; + dlinkDelete(link, &hlp->queue); + memFree(link, MEM_DLINK_NODE); + hlp->stats.queue_size--; + } + return r; +} + static helper_server * GetFirstAvailable(helper * hlp) { @@ -382,6 +672,26 @@ return NULL; } +static helper_stateful_server * +StatefulGetFirstAvailable(helper * hlp) +{ + dlink_node *n; + helper_stateful_server *srv = NULL; + debug(29,5)("StatefulGetFirstAvailable: Running servers %d.\n",hlp->n_running); + if (hlp->n_running == 0) + return NULL; + for (n = hlp->servers.head; n != NULL; n = n->next) { + srv = n->data; + if (srv->flags.busy) + continue; + if (!srv->flags.alive) + continue; + return srv; + } + return NULL; +} + + static void helperDispatch(helper_server * srv, helper_request * r) { @@ -412,6 +722,37 @@ } static void +helperStatefulDispatch(helper_stateful_server * srv, helper_stateful_request * r) +{ + helper *hlp = srv->parent; + if (!cbdataValid(r->data)) { + debug(29, 1) ("helperStatefulDispatch: invalid callback data\n"); + helperStatefulRequestFree(r); + return; + } +// assert((srv->flags.busy && srv->staterequestid) || (!srv->flags.busy && !srv->staterequestid)); + /* we keep ourselves busy when we're in a state request. */ + srv->flags.busy = 1; + srv->request = r; + srv->dispatch_time = current_time; + comm_write(srv->wfd, + r->buf, + strlen(r->buf), + NULL, /* Handler */ + NULL, /* Handler-data */ + NULL); /* free */ + commSetSelect(srv->rfd, + COMM_SELECT_READ, + helperStatefulHandleRead, + srv, 0); + debug(29, 5) ("helperStatefulDispatch: Request sent to %s #%d, %d bytes\n", + hlp->id_name, srv->index + 1, strlen(r->buf)); + srv->stats.uses++; + hlp->stats.requests++; +} + + +static void helperKickQueue(helper * hlp) { helper_request *r; @@ -421,9 +762,26 @@ } static void +helperStatefulKickQueue(helper * hlp) +{ + helper_stateful_request *r; + helper_stateful_server *srv; + while ((srv = StatefulGetFirstAvailable(hlp)) && (r = StatefulDequeue(hlp))) + helperStatefulDispatch(srv, r); +} + +static void helperRequestFree(helper_request * r) { cbdataUnlock(r->data); xfree(r->buf); memFree(r, MEM_HELPER_REQUEST); } + +static void +helperStatefulRequestFree(helper_stateful_request * r) +{ + cbdataUnlock(r->data); + xfree(r->buf); + memFree(r, MEM_HELPER_STATEFUL_REQUEST); +} Index: squid/src/mem.c =================================================================== RCS file: /cvsroot/squid-sf//squid/src/mem.c,v retrieving revision 1.1.1.3.12.4 retrieving revision 1.1.1.3.12.5 diff -u -r1.1.1.3.12.4 -r1.1.1.3.12.5 --- squid/src/mem.c 9 Jun 2000 19:50:08 -0000 1.1.1.3.12.4 +++ squid/src/mem.c 2 Aug 2000 14:13:11 -0000 1.1.1.3.12.5 @@ -1,6 +1,6 @@ /* - * $Id: mem.c,v 1.1.1.3.12.4 2000/06/09 19:50:08 hno Exp $ + * $Id: mem.c,v 1.1.1.3.12.5 2000/08/02 14:13:11 rbcollins Exp $ * * DEBUG: section 13 High Level Memory Pool Management * AUTHOR: Harvest Derived @@ -288,10 +288,15 @@ memDataInit(MEM_CLIENT_INFO, "ClientInfo", sizeof(ClientInfo), 0); memDataInit(MEM_MD5_DIGEST, "MD5 digest", MD5_DIGEST_CHARS, 0); memDataInit(MEM_HELPER, "helper", sizeof(helper), 0); + memDataInit(MEM_HELPER_STATEFUL, "stateful_helper", sizeof(statefulhelper), 0); memDataInit(MEM_HELPER_REQUEST, "helper_request", sizeof(helper_request), 0); + memDataInit(MEM_HELPER_STATEFUL_REQUEST, "helper_stateful_request", + sizeof(helper_stateful_request), 0); memDataInit(MEM_HELPER_SERVER, "helper_server", sizeof(helper_server), 0); + memDataInit(MEM_HELPER_STATEFUL_SERVER, "helper_stateful_server", + sizeof(helper_stateful_server), 0); memDataInit(MEM_STORE_IO, "storeIOState", sizeof(storeIOState), 0); /* init string pools */ for (i = 0; i < mem_str_pool_count; i++) { Index: squid/src/protos.h =================================================================== RCS file: /cvsroot/squid-sf//squid/src/protos.h,v retrieving revision 1.1.1.3.12.8 retrieving revision 1.1.1.3.12.9 diff -u -r1.1.1.3.12.8 -r1.1.1.3.12.9 --- squid/src/protos.h 15 Jul 2000 20:52:08 -0000 1.1.1.3.12.8 +++ squid/src/protos.h 2 Aug 2000 14:13:11 -0000 1.1.1.3.12.9 @@ -1,6 +1,6 @@ /* - * $Id: protos.h,v 1.1.1.3.12.8 2000/07/15 20:52:08 hno Exp $ + * $Id: protos.h,v 1.1.1.3.12.9 2000/08/02 14:13:11 rbcollins Exp $ * * * SQUID Internet Object Cache http://squid.nlanr.net/Squid/ @@ -695,9 +695,10 @@ extern void redirectShutdown(void); extern void authenticateStart(acl_proxy_auth_user *, RH *, void *); +extern void authenticateNTLMStart(const char *proxy_auth, SRH * handler, void *data, helper_stateful_server * lastserver); extern void authenticateInit(void); extern void authenticateShutdown(void); -extern void authenticateFixErrorHeader(HttpReply *, int, int, int); +extern void authenticateFixErrorHeader(HttpReply *, int, auth_t, int, char *authchallenge); extern void refreshAddToList(const char *, int, time_t, int, time_t); extern int refreshIsCachable(const StoreEntry *); @@ -1163,11 +1164,19 @@ /* helper.c */ extern void helperOpenServers(helper * hlp); +extern void helperStatefulOpenServers(statefulhelper * hlp); extern void helperSubmit(helper * hlp, const char *buf, HLPCB * callback, void *data); +extern void helperStatefulSubmit(statefulhelper * hlp, const char *buf, HLPSCB * callback, void *data, helper_stateful_server * lastserver); extern void helperStats(StoreEntry * sentry, helper * hlp); +extern void helperStatefulStats(StoreEntry * sentry, statefulhelper * hlp); extern void helperShutdown(helper * hlp); +extern void helperStatefulShutdown(statefulhelper * hlp); extern helper *helperCreate(const char *); +extern statefulhelper *helperStatefulCreate(const char *); extern void helperFree(helper *); +extern void helperStatefulFree(statefulhelper *); + + #if USE_LEAKFINDER extern void leakInit(void); Index: squid/src/squid.h =================================================================== RCS file: /cvsroot/squid-sf//squid/src/squid.h,v retrieving revision 1.1.1.3.12.3 retrieving revision 1.1.1.3.12.4 diff -u -r1.1.1.3.12.3 -r1.1.1.3.12.4 --- squid/src/squid.h 9 Jun 2000 19:50:08 -0000 1.1.1.3.12.3 +++ squid/src/squid.h 2 Aug 2000 14:13:12 -0000 1.1.1.3.12.4 @@ -1,6 +1,6 @@ /* - * $Id: squid.h,v 1.1.1.3.12.3 2000/06/09 19:50:08 hno Exp $ + * $Id: squid.h,v 1.1.1.3.12.4 2000/08/02 14:13:12 rbcollins Exp $ * * AUTHOR: Duane Wessels * @@ -426,4 +426,5 @@ #define INDEXSD(i) (&Config.cacheSwap.swapDirs[(i)]) + #endif /* SQUID_H */ Index: squid/src/structs.h =================================================================== RCS file: /cvsroot/squid-sf//squid/src/structs.h,v retrieving revision 1.1.1.3.4.1.2.12 retrieving revision 1.1.1.3.4.1.2.13 diff -u -r1.1.1.3.4.1.2.12 -r1.1.1.3.4.1.2.13 --- squid/src/structs.h 20 Jul 2000 09:11:56 -0000 1.1.1.3.4.1.2.12 +++ squid/src/structs.h 2 Aug 2000 14:13:12 -0000 1.1.1.3.4.1.2.13 @@ -1,6 +1,6 @@ /* - * $Id: structs.h,v 1.1.1.3.4.1.2.12 2000/07/20 09:11:56 kinkie Exp $ + * $Id: structs.h,v 1.1.1.3.4.1.2.13 2000/08/02 14:13:12 rbcollins Exp $ * * * SQUID Internet Object Cache http://squid.nlanr.net/Squid/ @@ -325,6 +325,7 @@ #endif wordlist *redirect; wordlist *authenticate; + wordlist *ntlmauthenticate; #if USE_ICMP char *pinger; #endif @@ -336,11 +337,14 @@ int dnsChildren; #endif int redirectChildren; +#ifdef USE_BASIC_AUTH int authenticateChildren; time_t authenticateTTL; time_t authenticateIpTTL; +#endif #ifdef USE_NTLM - char *authenticate_ntlm_domain; + int ntlmauthenticateChildren; + char *authenticate_ntlm_default_domain; #endif struct { int single_host; @@ -927,6 +931,11 @@ CBCB *callback; void *cbdata; } body; + int auth_state; /* where the user authentication is up to */ + auth_t auth_type; /* what authentication type this connection was made with */ + char authchallenge[NTLM_CHALLENGE_SZ]; /* what challenge did we give the client? */ + char authuser[USER_IDENT_SZ]; /* what challenge did we give the client? */ + helper_stateful_server * authhelper; /*we need to store the NTLM helper between requests*/ clientHttpRequest *chr; struct sockaddr_in peer; struct sockaddr_in me; @@ -1563,6 +1572,7 @@ http_status http_status; int auth_type; int auth_state; + char *authchallenge; request_t *request; char *url; int xerrno; @@ -1853,6 +1863,13 @@ void *data; }; +struct _helper_stateful_request { + char *buf; + HLPSCB *callback; + void *data; +}; + + struct _helper { wordlist *cmdline; dlink_list servers; @@ -1893,6 +1910,25 @@ } stats; }; + +struct _helper_stateful_server { + int index; + int rfd; + int wfd; + char *buf; + size_t buf_sz; + off_t offset; + struct timeval dispatch_time; + struct timeval answer_time; + dlink_node link; + helper *parent; + helper_stateful_request *request; + struct _helper_flags flags; + struct { + int uses; + } stats; +}; + /* * use this when you need to pass callback data to a blocking * operation, but you don't want to add that pointer to cbdata Index: squid/src/typedefs.h =================================================================== RCS file: /cvsroot/squid-sf//squid/src/typedefs.h,v retrieving revision 1.1.1.3.12.6 retrieving revision 1.1.1.3.12.7 diff -u -r1.1.1.3.12.6 -r1.1.1.3.12.7 --- squid/src/typedefs.h 3 Jul 2000 21:44:32 -0000 1.1.1.3.12.6 +++ squid/src/typedefs.h 2 Aug 2000 14:13:12 -0000 1.1.1.3.12.7 @@ -1,6 +1,6 @@ /* - * $Id: typedefs.h,v 1.1.1.3.12.6 2000/07/03 21:44:32 hno Exp $ + * $Id: typedefs.h,v 1.1.1.3.12.7 2000/08/02 14:13:12 rbcollins Exp $ * * * SQUID Internet Object Cache http://squid.nlanr.net/Squid/ @@ -164,8 +164,11 @@ typedef struct _FwdState FwdState; typedef struct _FwdServer FwdServer; typedef struct _helper helper; +typedef struct _helper statefulhelper; typedef struct _helper_server helper_server; +typedef struct _helper_stateful_server helper_stateful_server; typedef struct _helper_request helper_request; +typedef struct _helper_stateful_request helper_stateful_request; typedef struct _generic_cbdata generic_cbdata; typedef struct _storeIOState storeIOState; typedef struct _link_list link_list; @@ -214,6 +217,7 @@ typedef void IRCB(peer *, peer_t, protocol_t, void *, void *data); typedef void PSC(FwdServer *, void *); typedef void RH(void *data, char *); +typedef void SRH(void *data, void * lastserver, char *); typedef void UH(void *data, wordlist *); typedef int DEFER(int fd, void *data); typedef void CBCB(char *buf, size_t size, void *data); @@ -231,6 +235,7 @@ typedef void SIGHDLR(int sig); typedef void STVLDCB(void *, int, int); typedef void HLPCB(void *, char *buf); +typedef int HLPSCB(void *, void * lastserver, char *buf); typedef void HLPCMDOPTS(int *argc, char **argv); typedef void IDNSCB(void *, rfc1035_rr *, int);