diff -urwN squid-1.2.beta23pl3-orig/src/Makefile.in squid-1.2.beta23pl3/src/Makefile.in --- squid-1.2.beta23pl3-orig/src/Makefile.in Thu Jul 30 22:59:59 1998 +++ squid-1.2.beta23pl3/src/Makefile.in Thu Jul 30 22:47:10 1998 @@ -26,6 +26,7 @@ UNLINKD_EXE = unlinkd$(exec_suffix) PINGER_EXE = pinger$(exec_suffix) CACHEMGR_EXE = cachemgr$(cgi_suffix) +NCSA_AUTH_EXE = ncsa_auth$(exec_suffix) DEFAULT_CONFIG_FILE = $(sysconfdir)/squid.conf DEFAULT_MIME_TABLE = $(sysconfdir)/mime.conf @@ -40,6 +41,8 @@ DEFAULT_ICON_DIR = $(sysconfdir)/icons DEFAULT_ERROR_DIR = $(sysconfdir)/errors DEFAULT_MIB_PATH = $(sysconfdir)/mib.txt +DEFAULT_AUTH_PROGRAM = $(bindir)/$(NCSA_AUTH_EXE) +DEFAULT_PASSWD_FILE = $(sysconfdir)/passwd CC = @CC@ MAKEDEPEND = @MAKEDEPEND@ @@ -72,9 +75,10 @@ DNSSERVER_LIBS = -L../lib -lmiscutil $(XTRA_LIBS) PINGER_LIBS = -L../lib -lmiscutil $(XTRA_LIBS) STD_APP_LIBS = -L../lib -lmiscutil $(XTRA_LIBS) +AUTH_LIBS = -L../lib -lmiscutil $(CRYPTLIB) $(XTRA_LIBS) PROGS = $(SQUID_EXE) $(CLIENT_EXE) -UTILS = $(DNSSERVER_EXE) $(UNLINKD_EXE) +UTILS = $(DNSSERVER_EXE) $(UNLINKD_EXE) $(NCSA_AUTH_EXE) SUID_UTILS = $(PINGER_EXE) CGIPROGS = $(CACHEMGR_EXE) OBJS = \ @@ -82,6 +86,7 @@ acl.o \ asn.o \ @ASYNC_OBJS@ \ + authenticate.o \ cache_cf.o \ CacheDigest.o \ cache_manager.o \ @@ -217,6 +222,9 @@ $(UNLINKD_EXE): unlinkd-daemon.o $(CC) $(LDFLAGS) unlinkd-daemon.o -o $@ +$(NCSA_AUTH_EXE): ncsa_auth.o hash.o debug.o globals.o + $(CC) $(LDFLAGS) ncsa_auth.o hash.o debug.o globals.o -o $@ $(AUTH_LIBS) + unlinkd-daemon.o: unlinkd.c $(CC) -c $(CFLAGS) -DUNLINK_DAEMON $(srcdir)/unlinkd.c -o $@ @@ -247,7 +255,9 @@ s%@DEFAULT_SWAP_DIR@%$(DEFAULT_SWAP_DIR)%g;\ s%@DEFAULT_ICON_DIR@%$(DEFAULT_ICON_DIR)%g;\ s%@DEFAULT_MIB_PATH@%$(DEFAULT_MIB_PATH)%g;\ - s%@DEFAULT_ERROR_DIR@%$(DEFAULT_ERROR_DIR)%g" < $(srcdir)/cf.data.pre >$@ + s%@DEFAULT_ERROR_DIR@%$(DEFAULT_ERROR_DIR)%g;\ + s%@DEFAULT_AUTH_PROGRAM@%$(DEFAULT_AUTH_PROGRAM)%g;\ + s%@DEFAULT_PASSWD_FILE@%$(DEFAULT_PASSWD_FILE)%g" < $(srcdir)/cf.data.pre >$@ install-mkdirs: -@if test ! -d $(prefix); then \ diff -urwN squid-1.2.beta23pl3-orig/src/acl.c squid-1.2.beta23pl3/src/acl.c --- squid-1.2.beta23pl3-orig/src/acl.c Thu Jul 30 22:59:29 1998 +++ squid-1.2.beta23pl3/src/acl.c Mon Aug 3 00:02:08 1998 @@ -60,7 +60,7 @@ static IPH aclLookupDstIPforASNDone; static FQDNH aclLookupSrcFQDNDone; static FQDNH aclLookupDstFQDNDone; -static int aclReadProxyAuth(acl_proxy_auth * p); +static void aclProxyAuthDone(void * data, char * result); static wordlist *aclDumpIpList(acl_ip_data * ip); static wordlist *aclDumpDomainList(void *data); static wordlist *aclDumpTimeSpecList(acl_time_data *); @@ -647,39 +647,36 @@ #endif /* USE_SPLAY_TREE */ -/* check for change password file each 300 seconds */ -#define CHECK_PROXY_FILE_TIME 300 +/* default proxy_auth timeout is 3600 seconds */ +#define PROXY_AUTH_TIMEOUT 3600 + static void aclParseProxyAuth(void *data) { acl_proxy_auth *p; acl_proxy_auth **q = data; char *t; - t = strtok(NULL, w_space); - if (t) { + p = xcalloc(1, sizeof(acl_proxy_auth)); - p->filename = xstrdup(t); - p->last_time = 0; - p->change_time = 0; + + /* read timeout value (if any) */ t = strtok(NULL, w_space); if (t == NULL) { - p->check_interval = CHECK_PROXY_FILE_TIME; + p->timeout = PROXY_AUTH_TIMEOUT; } else { - p->check_interval = atoi(t); + p->timeout = atoi(t); } - if (p->check_interval < 1) - p->check_interval = 1; - p->hash = 0; /* force creation of a new hash table */ - if (aclReadProxyAuth(p)) { - *q = p; + /* the minimum timeout is 10 seconds */ + if (p->timeout < 10) + p->timeout = 10; + + /* First time around, 7921 should be big enough */ + if ((p->hash = hash_create((HASHCMP *) strcmp, 7921, hash_string)) < 0) { + debug(28, 0) ("aclParseProxyAuth: cannot create hash table, turning proxy_auth off\n"); + *q = NULL; return; - } else { - debug(28, 0) ("cannot read proxy_auth %s, ignoring\n", p->filename); - } - } else { - debug(28, 0) ("no filename in acl proxy_auth, ignoring\n"); } - *q = NULL; + *q = p; return; } @@ -1117,6 +1114,13 @@ return 0; } +/* aclMatchProxyAuth can return three exit codes: + 0 : No such user; invalid Proxy-authorization: header; + ask for Proxy-Authorization: header + 1 : user validated OK + -1 : check the password for this user via an external authenticator +*/ + static int aclMatchProxyAuth(acl_proxy_auth * p, aclCheck_t * checklist) { @@ -1137,51 +1141,67 @@ strtok(sent_auth, "\n"); cleartext = uudecode(sent_auth); xfree(sent_auth); - debug(28, 3) ("aclMatchProxyAuth: cleartext = '%s'\n", cleartext); + debug(28, 6) ("aclMatchProxyAuth: cleartext = '%s'\n", cleartext); xstrncpy(sent_user, cleartext, USER_IDENT_SZ); xfree(cleartext); if ((passwd = strchr(sent_user, ':')) != NULL) *passwd++ = '\0'; if (passwd == NULL) { - debug(28, 3) ("aclMatchProxyAuth: No passwd in auth blob\n"); + debug(28, 1) ("aclMatchProxyAuth: no passwd in proxy authorization header\n"); return 0; } - debug(28, 5) ("aclMatchProxyAuth: checking user %s\n", sent_user); + debug(28, 5) ("aclMatchProxyAuth: checking user '%s'\n", sent_user); /* copy username to checklist for logging on client-side */ xstrncpy(checklist->request->user_ident, sent_user, USER_IDENT_SZ); - /* reread password file if necessary */ - aclReadProxyAuth(p); + + /* see if we already know this user */ u = hash_lookup(p->hash, sent_user); if (NULL == u) { - /* User doesn't exist; deny them */ - debug(28, 4) ("aclMatchProxyAuth: user %s does not exist\n", sent_user); - return 0; - } - /* See if we've already validated them */ - *passwd |= 0x80; - if (0 == strcmp(u->passwd, passwd)) { - debug(28, 5) ("aclMatchProxyAuth: user %s previously validated\n", + /* user not yet known, ask external authenticator */ + debug(28, 4) ("aclMatchProxyAuth: user '%s' not yet known\n", sent_user); + } else { + /* user already known, check password with the cached one */ + if ((0 == strcmp(u->passwd, passwd)) && + (u->expiretime > current_time.tv_sec)) { + debug(28, 5) ("aclMatchProxyAuth: user '%s' previously validated\n", sent_user); return 1; } - *passwd &= (~0x80); -#if HAVE_CRYPT - if (strcmp(u->passwd, crypt(passwd, u->passwd))) { -#else - if (strcmp(u->passwd, passwd)) { -#endif - /* Passwords differ, deny access */ - p->last_time = 0; /* Trigger a check of the password file */ - debug(28, 4) ("aclMatchProxyAuth: authentication failed: user %s: " - "passwords differ\n", sent_user); - return 0; - } - *passwd |= 0x80; - debug(28, 5) ("aclMatchProxyAuth: user %s validated OK\n", sent_user); + /* password mismatch/timeout */ + debug(28, 4) ("aclMatchProxyAuth: user '%s' password mismatch/timeout\n", + sent_user); + /* remove this user from the hash, making him unknown */ hash_remove_link(p->hash, (hash_link *) u); - safe_free(u->passwd); + aclFreeProxyAuthUser(u); + } + + /* we've got an unknown user now */ + if (checklist->auth_user == NULL) { + /* we must still check this user's password */ + u = memAllocate(MEM_ACL_PROXY_AUTH_USER); + u->user = xstrdup(sent_user); u->passwd = xstrdup(passwd); - hash_join(p->hash, (hash_link *) u); + u->passwd_ok = 0; + u->expiretime = 0; + checklist->auth_user = u; + debug(28, 4) ("aclMatchProxyAuth: going to ask authenticator\n"); + return -1; + } + + /* checklist->auth_user has just been checked, check result */ + if (checklist->auth_user->passwd_ok == -1) { + /* password was checked but did not match */ + debug(28, 4) ("aclMatchProxyAuth: authentication failed for user '%s'\n", + sent_user); + return 0; + } + + /* checklist->auth_user->passwd_ok == 1, passwd check OK */ + debug(28, 4) ("aclMatchProxyAuth: user '%s' validated OK\n", sent_user); + /* store validated user in hash, after filling in expiretime */ + checklist->auth_user->expiretime = current_time.tv_sec + p->timeout; + hash_join(p->hash, (hash_link *) checklist->auth_user); + return 1; } @@ -1345,15 +1365,20 @@ return aclMatchRegex(acl->data, checklist->browser); /* NOTREACHED */ case ACL_PROXY_AUTH: - if (!aclMatchProxyAuth(acl->data, checklist)) { + k = aclMatchProxyAuth(acl->data, checklist); + if (k == 0) { /* no such user OR we need a proxy authentication header */ - checklist->state[ACL_PROXY_AUTH] = ACL_LOOKUP_NEEDED; + checklist->state[ACL_PROXY_AUTH] = ACL_PROXY_AUTH_NEEDED; return 0; - } else { + } else if (k == 1) { /* register that we used the proxy authentication header */ - checklist->state[ACL_PROXY_AUTH] = ACL_LOOKUP_DONE; + checklist->state[ACL_PROXY_AUTH] = ACL_PROXY_AUTH_USED; EBIT_SET(r->flags, REQ_USED_PROXY_AUTH); return 1; + } else if (k == -1) { + /* register that we need to check the password */ + checklist->state[ACL_PROXY_AUTH] = ACL_PROXY_AUTH_CHECK; + return 0; } /* NOTREACHED */ case ACL_SNMP_COMM: @@ -1473,17 +1498,25 @@ checklist); return; } + /* extra case for proxy_auth */ + if (checklist->state[ACL_PROXY_AUTH] == ACL_PROXY_AUTH_CHECK) { + debug(28, 3) ("aclCheck: checking password via authenticator\n"); + authenticateStart(checklist->auth_user, aclProxyAuthDone, + checklist); + return; + } /* * We are done with this _acl_access entry. Either the request * is allowed, denied, or we move on to the next entry. */ cbdataUnlock(A); - if (checklist->state[ACL_PROXY_AUTH] == ACL_LOOKUP_NEEDED) { + if (checklist->state[ACL_PROXY_AUTH] == ACL_PROXY_AUTH_NEEDED) { allow = ACCESS_REQ_PROXY_AUTH; debug(28, 3) ("aclCheck: match pending, returning %d\n", allow); aclCheckCallback(checklist, allow); return; } + /* checklist->state[ACL_PROXY_AUTH] == ACL_PROXY_AUTH_USED */ if (match) { debug(28, 3) ("aclCheck: match found, returning %d\n", allow); aclCheckCallback(checklist, allow); @@ -1534,6 +1567,7 @@ checklist->state[ACL_DST_IP] = ACL_LOOKUP_DONE; aclCheck(checklist); } + static void aclLookupDstIPforASNDone(const ipcache_addrs * ia, void *data) { @@ -1558,6 +1592,19 @@ aclCheck(checklist); } +static void +aclProxyAuthDone(void *data, char *result) +{ + aclCheck_t *checklist = data; + checklist->state[ACL_PROXY_AUTH] = ACL_LOOKUP_DONE; + debug(28, 4) ("aclProxyAuthDone: result = %s\n", result); + if (result && (strncasecmp(result, "OK", 2) == 0)) + checklist->auth_user->passwd_ok = 1; + else + checklist->auth_user->passwd_ok = -1; + aclCheck(checklist); +} + aclCheck_t * aclChecklistCreate(const acl_access * A, request_t * request, @@ -1566,7 +1613,7 @@ const char *ident) { int i; - aclCheck_t *checklist = xcalloc(1, sizeof(aclCheck_t));; + aclCheck_t *checklist = xcalloc(1, sizeof(aclCheck_t)); cbdataAdd(checklist, MEM_NONE); checklist->access_list = A; /* @@ -1583,6 +1630,7 @@ xstrncpy(checklist->browser, user_agent, BROWSERNAMELEN); if (ident) xstrncpy(checklist->ident, ident, USER_IDENT_SZ); + checklist->auth_user = NULL; /* init to NULL */ return checklist; } @@ -1659,7 +1707,6 @@ hashFreeItems(p->hash, aclFreeProxyAuthUser); hashFreeMemory(p->hash); p->hash = NULL; - safe_free(p->filename); safe_free(p); } @@ -1796,62 +1843,6 @@ #endif /* SPLAY_TREE */ -/* Original ProxyAuth code by Jon Thackray */ -/* Generalized to ACL's by Arjan.deVet */ -static int -aclReadProxyAuth(acl_proxy_auth * p) -{ - struct stat buf; - static char *passwords = NULL; - char *user = NULL; - char *passwd = NULL; - FILE *f = NULL; - if ((squid_curtime - p->last_time) < p->check_interval) - return 1; - if (0 != stat(p->filename, &buf)) { - debug(28, 0) ("aclReadProxyAuth: can't access proxy_auth file %s, turning authentication off\n", p->filename); - return 0; - } - if (buf.st_mtime == p->change_time) { - debug(28, 5) ("aclReadProxyAuth: %s not changed (old=%d,new=%d)\n", - p->filename, (int) p->change_time, (int) buf.st_mtime); - p->last_time = squid_curtime; - return 1; - } - debug(28, 1) ("aclReadProxyAuth: reloading changed proxy authentication file %s\n", p->filename); - p->change_time = buf.st_mtime; - if (NULL != p->hash) { - hashFreeItems(p->hash, aclFreeProxyAuthUser); - hashFreeMemory(p->hash); - } - p->hash = hash_create((HASHCMP*) strcmp, 7921, hash_string); - assert(NULL != p->hash); - passwords = xmalloc((size_t) buf.st_size + 2); - f = fopen(p->filename, "r"); - fread(passwords, (size_t) buf.st_size, 1, f); - *(passwords + buf.st_size) = '\0'; - strcat(passwords, "\n"); - fclose(f); - user = strtok(passwords, ":"); - passwd = strtok(NULL, "\n"); - debug(28, 5) ("aclReadProxyAuth: adding new passwords to hash table\n"); - while (user != NULL) { - if ((int) strlen(user) > 1 && passwd && (int) strlen(passwd) > 1) { - acl_proxy_auth_user *u; - u = memAllocate(MEM_ACL_PROXY_AUTH_USER); - u->user = xstrdup(user); - u->passwd = xstrdup(passwd); - debug(28, 6) ("aclReadProxyAuth: adding %s, %s to hash table\n", user, passwd); - hash_join(p->hash, (hash_link *) u); - } - user = strtok(NULL, ":"); - passwd = strtok(NULL, "\n"); - } - xfree(passwords); - return 1; -} - - /* compare a host and a domain */ #if defined(USE_SPLAY_TREE) @@ -2088,7 +2079,7 @@ char buf[MAXPATHLEN]; wordlist *w = xcalloc(1, sizeof(wordlist)); assert(data != NULL); - snprintf(buf, sizeof(buf), "%s %d\n", data->filename, data->check_interval); + snprintf(buf, sizeof(buf), "%d\n", data->timeout); w->key = xstrdup(buf); *T = w; T = &w->next; diff -urwN squid-1.2.beta23pl3-orig/src/authenticate.c squid-1.2.beta23pl3/src/authenticate.c --- squid-1.2.beta23pl3-orig/src/authenticate.c Thu Jan 1 01:00:00 1970 +++ squid-1.2.beta23pl3/src/authenticate.c Sat Aug 1 14:18:30 1998 @@ -0,0 +1,455 @@ + +/* + * $Id: authenticate.c,v 1.60 1998/05/08 22:00:23 wessels Exp $ + * + * DEBUG: section 29 Authenticator + * AUTHOR: Duane Wessels + * + * 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 + * Duane Wessels and the University of California San Diego. 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 "squid.h" + +typedef struct { + void *data; + acl_proxy_auth_user *auth_user; + RH *handler; +} authenticateStateData; + +typedef struct _authenticator { + int index; + int flags; + int fd; + char *inbuf; + unsigned int size; + unsigned int offset; + struct timeval dispatch_time; + authenticateStateData *authenticateState; +} authenticator_t; + +static struct { + int requests; + int replies; + int errors; + int avg_svc_time; + int queue_size; + int use_hist[DefaultAuthenticateChildrenMax]; + int rewrites[DefaultAuthenticateChildrenMax]; +} AuthenticateStats; + + +struct authenticateQueueData { + struct authenticateQueueData *next; + authenticateStateData *authenticateState; +}; + +static authenticator_t *GetFirstAvailable(void); +static PF authenticateHandleRead; +static authenticateStateData *Dequeue(void); +static void Enqueue(authenticateStateData *); +static void authenticateDispatch(authenticator_t *, authenticateStateData *); +static void authenticateStateFree(authenticateStateData * r); + +static authenticator_t **authenticate_child_table = NULL; +static int NAuthenticators = 0; +static int NAuthenticatorsOpen = 0; +static struct authenticateQueueData *authenticateQueueHead = NULL; +static struct authenticateQueueData **authenticateQueueTailP = &authenticateQueueHead; + +static void +authenticateHandleRead(int fd, void *data) +{ + authenticator_t *authenticator = data; + int len; + authenticateStateData *r = authenticator->authenticateState; + char *t = NULL; + int n; + + len = read(fd, + authenticator->inbuf + authenticator->offset, + authenticator->size - authenticator->offset); + fd_bytes(fd, len, FD_READ); + debug(29, 5) ("authenticateHandleRead: %d bytes from Authenticator #%d.\n", + len, authenticator->index + 1); + if (len <= 0) { + if (len < 0) + debug(50, 1) ("authenticateHandleRead: FD %d read: %s\n", fd, xstrerror()); + debug(29, EBIT_TEST(authenticator->flags, HELPER_CLOSING) ? 5 : 1) + ("FD %d: Connection from Authenticator #%d is closed, disabling\n", + fd, authenticator->index + 1); + authenticator->flags = 0; + memFree(MEM_8K_BUF, authenticator->inbuf); + authenticator->inbuf = NULL; + comm_close(fd); + if (--NAuthenticatorsOpen == 0 && !shutting_down) + fatal_dump("All authenticators have exited!"); + return; + } + if (len != 1) + AuthenticateStats.rewrites[authenticator->index]++; + authenticator->offset += len; + authenticator->inbuf[authenticator->offset] = '\0'; + /* reschedule */ + commSetSelect(authenticator->fd, + COMM_SELECT_READ, + authenticateHandleRead, + authenticator, 0); + if ((t = strchr(authenticator->inbuf, '\n'))) { + /* end of record found */ + *t = '\0'; + if ((t = strchr(authenticator->inbuf, ' '))) + *t = '\0'; /* terminate at space */ + if (r == NULL) { + /* A naughty authenticator has spoken without being spoken to */ + /* B.R.Foster@massey.ac.nz, SQUID/1.1.3 */ + debug(29, 0) ("authenticateHandleRead: unexpected reply: '%s'\n", + authenticator->inbuf); + authenticator->offset = 0; + } else { + debug(29, 5) ("authenticateHandleRead: reply: '%s'\n", + authenticator->inbuf); + /* careful here. r->data might point to something which + * has recently been freed. If so, we require that r->handler + * be NULL */ + if (r->handler) { + r->handler(r->data, + t == authenticator->inbuf ? NULL : authenticator->inbuf); + } + authenticateStateFree(r); + authenticator->authenticateState = NULL; + EBIT_CLR(authenticator->flags, HELPER_BUSY); + authenticator->offset = 0; + n = ++AuthenticateStats.replies; + AuthenticateStats.avg_svc_time = + intAverage(AuthenticateStats.avg_svc_time, + tvSubMsec(authenticator->dispatch_time, current_time), + n, AUTHENTICATE_AV_FACTOR); + } + } + while ((authenticator = GetFirstAvailable()) && (r = Dequeue())) + authenticateDispatch(authenticator, r); +} + +static void +Enqueue(authenticateStateData * r) +{ + struct authenticateQueueData *new = xcalloc(1, sizeof(struct authenticateQueueData)); + new->authenticateState = r; + *authenticateQueueTailP = new; + authenticateQueueTailP = &new->next; + AuthenticateStats.queue_size++; +} + +static authenticateStateData * +Dequeue(void) +{ + struct authenticateQueueData *old = NULL; + authenticateStateData *r = NULL; + if (authenticateQueueHead) { + r = authenticateQueueHead->authenticateState; + old = authenticateQueueHead; + authenticateQueueHead = authenticateQueueHead->next; + if (authenticateQueueHead == NULL) + authenticateQueueTailP = &authenticateQueueHead; + safe_free(old); + AuthenticateStats.queue_size--; + } + return r; +} + +static authenticator_t * +GetFirstAvailable(void) +{ + int k; + authenticator_t *authenticate = NULL; + for (k = 0; k < NAuthenticators; k++) { + authenticate = *(authenticate_child_table + k); + if (EBIT_TEST(authenticate->flags, HELPER_BUSY)) + continue; + if (!EBIT_TEST(authenticate->flags, HELPER_ALIVE)) + continue; + return authenticate; + } + return NULL; +} + +static void +authenticateStateFree(authenticateStateData * r) +{ + safe_free(r); +} + + +static void +authenticateDispatch(authenticator_t * authenticate, authenticateStateData * r) +{ + char *buf = NULL; + int len; + if (r->handler == NULL) { + debug(29, 1) ("authenticateDispatch: skipping '%s' because no handler\n", + r->auth_user->user); + authenticateStateFree(r); + return; + } + EBIT_SET(authenticate->flags, HELPER_BUSY); + authenticate->authenticateState = r; + authenticate->dispatch_time = current_time; + buf = memAllocate(MEM_8K_BUF); + snprintf(buf, 8192, "%s %s\n", + r->auth_user->user, + r->auth_user->passwd); + len = strlen(buf); + comm_write(authenticate->fd, + buf, + len, + NULL, /* Handler */ + NULL, /* Handler-data */ + memFree8K); + debug(29, 5) ("authenticateDispatch: Request sent to Authenticator #%d, %d bytes\n", + authenticate->index + 1, len); + AuthenticateStats.use_hist[authenticate->index]++; + AuthenticateStats.requests++; +} + + +/**** PUBLIC FUNCTIONS ****/ + + +void +authenticateStart(acl_proxy_auth_user * auth_user, RH * handler, void *data) +{ + authenticateStateData *r = NULL; + authenticator_t *authenticator = NULL; + if (!auth_user) + fatal_dump("authenticateStart: NULL auth_user"); + if (!handler) + fatal_dump("authenticateStart: NULL handler"); + debug(29, 5) ("authenticateStart: '%s:%s'\n", auth_user->user, + auth_user->passwd); + if (Config.Program.authenticate == NULL) { + handler(data, NULL); + return; + } + r = xcalloc(1, sizeof(authenticateStateData)); + r->handler = handler; + r->data = data; + r->auth_user = auth_user; + if ((authenticator = GetFirstAvailable())) + authenticateDispatch(authenticator, r); + else + Enqueue(r); +} + +void +authenticateFreeMemory(void) +{ + int k; + /* free old structures if present */ + if (authenticate_child_table) { + for (k = 0; k < NAuthenticators; k++) { + if (authenticate_child_table[k]->inbuf) + memFree(MEM_8K_BUF, authenticate_child_table[k]->inbuf); + safe_free(authenticate_child_table[k]); + } + safe_free(authenticate_child_table); + } +} + +void +authenticateOpenServers(void) +{ + char *prg = Config.Program.authenticate; + wordlist *auth_opts; + char *short_prg; + char *short_prg2; + int k; + int authenticatesocket; + LOCAL_ARRAY(char, fd_note_buf, FD_DESC_SZ); + static int first_time = 0; + char *s; + char *args[32]; + int i, x; + + authenticateFreeMemory(); + if (Config.Program.authenticate == NULL) + return; + NAuthenticators = NAuthenticatorsOpen = Config.authenticateChildren; + authenticate_child_table = xcalloc(NAuthenticators, sizeof(authenticator_t *)); + debug(29, 1) ("authenticateOpenServers: Starting %d '%s' processes\n", + NAuthenticators, prg); + if ((s = strrchr(prg, '/'))) + short_prg = xstrdup(s + 1); + else + short_prg = xstrdup(prg); + short_prg2 = xmalloc(strlen(s) + 3); + snprintf(short_prg2, strlen(s) + 3, "(%s)", short_prg); + for (k = 0; k < NAuthenticators; k++) { + authenticate_child_table[k] = xcalloc(1, sizeof(authenticator_t)); + args[0] = short_prg2; + i = 1; + auth_opts = Config.Program.authenticate_options; + while ((auth_opts != NULL) && (i < 31)) { + args[i++] = auth_opts->key; + auth_opts = auth_opts->next; + } + if (auth_opts != NULL) { + debug(29, 0) ("WARNING: too many authenticate_options\n"); + } + args[i] = NULL; + x = ipcCreate(IPC_TCP_SOCKET, + prg, + args, + "authenticator", + &authenticatesocket, + &authenticatesocket); + if (x < 0) { + debug(29, 1) ("WARNING: Cannot run '%s' process.\n", prg); + EBIT_CLR(authenticate_child_table[k]->flags, HELPER_ALIVE); + } else { + EBIT_SET(authenticate_child_table[k]->flags, HELPER_ALIVE); + authenticate_child_table[k]->index = k; + authenticate_child_table[k]->fd = authenticatesocket; + authenticate_child_table[k]->inbuf = memAllocate(MEM_8K_BUF); + authenticate_child_table[k]->size = 8192; + authenticate_child_table[k]->offset = 0; + snprintf(fd_note_buf, FD_DESC_SZ, "%s #%d", + short_prg, + authenticate_child_table[k]->index + 1); + fd_note(authenticate_child_table[k]->fd, fd_note_buf); + commSetNonBlocking(authenticate_child_table[k]->fd); + /* set handler for incoming result */ + commSetSelect(authenticate_child_table[k]->fd, + COMM_SELECT_READ, + authenticateHandleRead, + authenticate_child_table[k], 0); + debug(29, 3) ("authenticateOpenServers: 'authenticate_server' %d started\n", + k); + } + } + if (first_time == 0) { + first_time++; + memset(&AuthenticateStats, '\0', sizeof(AuthenticateStats)); + cachemgrRegister("authenticator", + "Authenticator Stats", + authenticateStats, 0, 1); + } + safe_free(short_prg); + safe_free(short_prg2); +} + +void +authenticateShutdownServers(void *unused) +{ + authenticator_t *authenticate = NULL; + authenticateStateData *r = NULL; + int k; + int na = 0; + if (Config.Program.authenticate == NULL) + return; + if (authenticateQueueHead) { + while ((authenticate = GetFirstAvailable()) && (r = Dequeue())) + authenticateDispatch(authenticate, r); + return; + } + for (k = 0; k < NAuthenticators; k++) { + authenticate = *(authenticate_child_table + k); + if (!EBIT_TEST(authenticate->flags, HELPER_ALIVE)) + continue; + if (EBIT_TEST(authenticate->flags, HELPER_CLOSING)) + continue; + if (EBIT_TEST(authenticate->flags, HELPER_BUSY)) { + na++; + continue; + } + debug(29, 3) ("authenticateShutdownServers: closing authenticator #%d, FD %d\n", + authenticate->index + 1, authenticate->fd); + comm_close(authenticate->fd); + EBIT_SET(authenticate->flags, HELPER_CLOSING); + EBIT_SET(authenticate->flags, HELPER_BUSY); + } + if (na) + eventAdd("authenticateShutdownServers", authenticateShutdownServers, NULL, 1.0, 1); +} + +int +authenticateUnregister(const char *url, void *data) +{ + authenticator_t *authenticate = NULL; + authenticateStateData *r = NULL; + struct authenticateQueueData *rq = NULL; + int k; + int n = 0; + if (Config.Program.authenticate == NULL) + return 0; + debug(29, 3) ("authenticateUnregister: '%s'\n", url); + for (k = 0; k < NAuthenticators; k++) { + authenticate = *(authenticate_child_table + k); + if ((r = authenticate->authenticateState) == NULL) + continue; + if (r->data != data) + continue; + debug(29, 3) ("authenticateUnregister: Found match\n"); + r->handler = NULL; + n++; + } + for (rq = authenticateQueueHead; rq; rq = rq->next) { + if ((r = rq->authenticateState) == NULL) + continue; + if (r->data != data) + continue; + debug(29, 3) ("authenticateUnregister: Found match.\n"); + r->handler = NULL; + n++; + } + debug(29, 3) ("authenticateUnregister: Unregistered %d handlers\n", n); + return n; +} + +void +authenticateStats(StoreEntry * sentry) +{ + int k; + storeAppendPrintf(sentry, "Authenticator Statistics:\n"); + storeAppendPrintf(sentry, "requests: %d\n", + AuthenticateStats.requests); + storeAppendPrintf(sentry, "replies: %d\n", + AuthenticateStats.replies); + storeAppendPrintf(sentry, "queue length: %d\n", + AuthenticateStats.queue_size); + storeAppendPrintf(sentry, "avg service time: %d msec\n", + AuthenticateStats.avg_svc_time); + storeAppendPrintf(sentry, "number of authenticators: %d\n", + NAuthenticators); + storeAppendPrintf(sentry, "use histogram:\n"); + for (k = 0; k < NAuthenticators; k++) { + storeAppendPrintf(sentry, " authenticator #%d: %d (%d requests)\n", + k + 1, + AuthenticateStats.use_hist[k], + AuthenticateStats.rewrites[k]); + } +} diff -urwN squid-1.2.beta23pl3-orig/src/cache_cf.c squid-1.2.beta23pl3/src/cache_cf.c --- squid-1.2.beta23pl3-orig/src/cache_cf.c Wed Jul 22 22:37:01 1998 +++ squid-1.2.beta23pl3/src/cache_cf.c Sun Jul 26 10:13:57 1998 @@ -266,6 +266,17 @@ Config.redirectChildren = DefaultRedirectChildrenMax; } } + if (Config.Program.authenticate) { + if (Config.authenticateChildren < 1) { + Config.authenticateChildren = 0; + safe_free(Config.Program.authenticate); + } else if (Config.authenticateChildren > DefaultAuthenticateChildrenMax) { + debug(3, 0) ("WARNING: authenticate_children was set to a bad value: %d\n", + Config.authenticateChildren); + debug(3, 0) ("Setting it to the maximum (%d).\n", DefaultAuthenticateChildrenMax); + Config.authenticateChildren = DefaultAuthenticateChildrenMax; + } + } if (Config.Accel.host) { snprintf(buf, BUFSIZ, "http://%s:%d", Config.Accel.host, Config.Accel.port); Config2.Accel.prefix = xstrdup(buf); @@ -310,6 +321,8 @@ requirePathnameExists("unlinkd_program", Config.Program.unlinkd); if (Config.Program.redirect) requirePathnameExists("redirect_program", Config.Program.redirect); + if (Config.Program.authenticate) + requirePathnameExists("authenticate_program", Config.Program.authenticate); requirePathnameExists("Icon Directory", Config.icons.directory); requirePathnameExists("Error Directory", Config.errorDirectory); } diff -urwN squid-1.2.beta23pl3-orig/src/cf.data.pre squid-1.2.beta23pl3/src/cf.data.pre --- squid-1.2.beta23pl3-orig/src/cf.data.pre Wed Jul 22 22:53:53 1998 +++ squid-1.2.beta23pl3/src/cf.data.pre Mon Aug 3 00:07:52 1998 @@ -917,6 +917,47 @@ redirect_children 5 DOC_END +NAME: authenticate_program +TYPE: string +LOC: Config.Program.authenticate +DEFAULT: none +DOC_START + Specify the location of the executable for the authenticator. + Such a program reads a line containing "username password" + and replies "OK" or "ERR" in an endless loop. + If you use an authenticator, make sure you have 1 acl of type + proxy_auth. + By default, the authenticator_program is not used. + +authenticate_program @DEFAULT_AUTH_PROGRAM@ +DOC_END + + +NAME: authenticate_options +TYPE: wordlist +LOC: Config.Program.authenticate_options +DEFAULT: none +DOC_START + Command line options for the authenticate program. + +authenticate_options @DEFAULT_PASSWD_FILE@ +DOC_END + + +NAME: authenticate_children +TYPE: int +DEFAULT: 5 +LOC: Config.authenticateChildren +DOC_START + The number of 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 authenticator processes. + +authenticate_children 5 +DOC_END + COMMENT_START OPTIONS FOR TUNING THE CACHE ----------------------------------------------------------------------------- @@ -1283,17 +1324,22 @@ # cache_peer_acl mycache.mydomain.net asexample # cache_peer_acl mycache_mydomain.net !all - acl aclname proxy_auth passwd_file [ refresh ] - # 'passwd_file' is an Apache-style file of passwords for - # authenticated proxy access. Looks like user:password, with - # the password being standard crypt() format. 'refresh' is - # the time in seconds to check for a changes in the file - # (default = 300 secs). When using a proxy_auth ACL in an - # ACL list, make sure it is the *last* in the list and the - # only proxy_auth ACL in the list. NOTE: when a - # Proxy-Authentication header is sent but it is not needed - # during ACL checking the username is NOT logged in - # access.log. + acl aclname proxy_auth [ refresh ] + # Use an EXTERNAL authentication program to check username/password + # combinations (see authenticate_program). + # + # 'timeout' is the time a checked username/password combination + # remains cached (default = 3600 secs). If a wrong password + # is given for a cached user, the user gets removed from the + # username/password cache forcing a revalidation. + # + # When using a proxy_auth ACL in an http_access rule, make sure + # it is the *last* in the list and the only proxy_auth ACL in + # the list. + # + # NOTE: when a Proxy-Authentication header is sent but it is not + # needed during ACL checking the username is NOT logged + # in access.log. acl manager proto cache_object acl localhost src 127.0.0.1/255.255.255.255 @@ -1302,6 +1348,7 @@ acl SSL_ports port 443 563 acl Dangerous_ports port 7 9 19 acl CONNECT method CONNECT +acl password proxy_auth 300 DOC_END NAME: http_access diff -urwN squid-1.2.beta23pl3-orig/src/defines.h squid-1.2.beta23pl3/src/defines.h --- squid-1.2.beta23pl3-orig/src/defines.h Wed Jul 22 22:53:54 1998 +++ squid-1.2.beta23pl3/src/defines.h Sun Jul 26 10:13:57 1998 @@ -53,6 +53,7 @@ #define DefaultDnsChildrenMax 32 /* 32 processes */ #define DefaultRedirectChildrenMax 32 /* 32 processes */ +#define DefaultAuthenticateChildrenMax 32 /* 32 processes */ #define MAXHTTPPORTS 12 #define COMM_OK (0) @@ -149,6 +150,12 @@ #define REDIRECT_NONE 0 #define REDIRECT_PENDING 1 #define REDIRECT_DONE 2 + +#define AUTHENTICATE_AV_FACTOR 1000 + +#define AUTHENTICATE_NONE 0 +#define AUTHENTICATE_PENDING 1 +#define AUTHENTICATE_DONE 2 #define CONNECT_PORT 443 diff -urwN squid-1.2.beta23pl3-orig/src/enums.h squid-1.2.beta23pl3/src/enums.h --- squid-1.2.beta23pl3-orig/src/enums.h Wed Jul 22 22:53:55 1998 +++ squid-1.2.beta23pl3/src/enums.h Sun Jul 26 10:13:58 1998 @@ -114,7 +114,10 @@ ACL_LOOKUP_NONE, ACL_LOOKUP_NEEDED, ACL_LOOKUP_PENDING, - ACL_LOOKUP_DONE + ACL_LOOKUP_DONE, + ACL_PROXY_AUTH_NEEDED, + ACL_PROXY_AUTH_USED, + ACL_PROXY_AUTH_CHECK } acl_lookup_state; enum { diff -urwN squid-1.2.beta23pl3-orig/src/main.c squid-1.2.beta23pl3/src/main.c --- squid-1.2.beta23pl3-orig/src/main.c Thu Jul 30 23:00:01 1998 +++ squid-1.2.beta23pl3/src/main.c Thu Jul 30 22:53:56 1998 @@ -316,6 +316,7 @@ #endif dnsShutdownServers(NULL); redirectShutdownServers(NULL); + authenticateShutdownServers(NULL); storeDirCloseSwapLogs(); errorFree(); parseConfigFile(ConfigFile); @@ -325,6 +326,7 @@ errorInitialize(); /* reload error pages */ dnsOpenServers(); redirectOpenServers(); + authenticateOpenServers(); serverConnectionsOpen(); if (theOutIcpConnection >= 0 && (!Config2.Accel.on || Config.onoff.accel_with_proxy)) neighbors_open(theOutIcpConnection); @@ -400,6 +402,7 @@ fqdncache_init(); dnsOpenServers(); redirectOpenServers(); + authenticateOpenServers(); useragentOpenLog(); httpHeaderInitModule(); /* must go before any header processing (e.g. the one in errorInitialize) */ httpAnonInitModule(); /* must go before accepting requests */ @@ -564,6 +567,7 @@ serverConnectionsClose(); eventAdd("dnsShutdownServers", dnsShutdownServers, NULL, 0.0, 1); eventAdd("redirectShutdownServers", redirectShutdownServers, NULL, 0.0, 1); + eventAdd("authenticateShutdownServers", authenticateShutdownServers, NULL, 0.0, 1); eventAdd("SquidShutdown", SquidShutdown, NULL, (double) (wait + 1), 1); } eventRun(); diff -urwN squid-1.2.beta23pl3-orig/src/ncsa_auth.c squid-1.2.beta23pl3/src/ncsa_auth.c --- squid-1.2.beta23pl3-orig/src/ncsa_auth.c Thu Jan 1 01:00:00 1970 +++ squid-1.2.beta23pl3/src/ncsa_auth.c Sun Aug 2 21:05:51 1998 @@ -0,0 +1,119 @@ +/* + * ncsa_auth.c + * + * AUTHOR: Arjan de Vet + * + * Example authentication program for Squid, based on the original + * proxy_auth code from client_side.c, written by + * Jon Thackray . + * + * Uses a NCSA httpd style password file for authentication with the + * following improvements suggested by various people: + * + * - comment lines are possible and should start with a '#'; + * - empty or blank lines are possible; + * - extra fields in the password file are ignored; this makes it + * possible to use a Unix password file but I do not recommend that. + * + */ + +#include "squid.h" + +static hash_table *hash = NULL; + +static void +read_passwd_file(const char *passwdfile) +{ + FILE *f; + hash_link *hashr; + char buf[8192]; + acl_proxy_auth_user *u; + char *user; + char *passwd; + + if (hash != NULL) { + /* clear hash first */ + hash_first(hash); + while ((hashr = hash_next(hash)) != NULL) { + hash_remove_link(hash, hashr); + xfree(((acl_proxy_auth_user *) hashr)->user); + xfree(((acl_proxy_auth_user *) hashr)->passwd); + } + } else { + /* initial setup */ + if ((hash = hash_create((HASHCMP *)strcmp, 7921, hash_string)) < 0) { + fprintf(stderr, "ncsa_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(acl_proxy_auth_user)); + u->user = xstrdup(user); + u->passwd = xstrdup(passwd); + hash_join(hash, (hash_link *) u); + } + } + fclose(f); +} + +/* this is only needed for hash.c */ +void +fatal(const char *message) +{ + fprintf(stderr, "ncsa_auth: fatal: %s\n", message); + exit(1); +} + +int +main(int argc, char **argv) +{ + struct stat sb; + time_t change_time = 0; + char buf[256]; + char *user, *passwd, *p; + acl_proxy_auth_user *u; + + if (argc != 2) { + fprintf(stderr, "Usage: ncsa_auth \n"); + exit(1); + } + if (stat(argv[1], &sb) != 0) { + fprintf(stderr, "cannot stat %s\n", argv[1]); + exit(1); + } + + while (fgets(buf, 256, stdin) != NULL) { + if (stat(argv[1], &sb) == 0) { + if (sb.st_mtime != change_time) { + read_passwd_file(argv[1]); + change_time = sb.st_mtime; + } + } + user = buf; + if ((passwd = strchr(buf, ' ')) == NULL) { + printf("ERR\n"); + fflush(stdout); + continue; + } + *passwd++ = '\0'; + if ((p = strchr(passwd, '\n')) != NULL) + *p = '\0'; /* strip \n */ + u = hash_lookup(hash, user); + if (u && + strcmp(u->passwd, (char *) crypt(passwd, u->passwd)) == 0) { + printf("OK\n"); + } else { + printf("ERR\n"); + } + fflush(stdout); + } + + exit(0); +} diff -urwN squid-1.2.beta23pl3-orig/src/protos.h squid-1.2.beta23pl3/src/protos.h --- squid-1.2.beta23pl3-orig/src/protos.h Thu Jul 30 23:00:01 1998 +++ squid-1.2.beta23pl3/src/protos.h Thu Jul 30 22:54:54 1998 @@ -642,6 +642,13 @@ extern int redirectUnregister(const char *url, void *); extern void redirectFreeMemory(void); +extern void authenticateStart(acl_proxy_auth_user *, RH *, void *); +extern void authenticateOpenServers(void); +extern void authenticateShutdownServers(void *); +extern void authenticateStats(StoreEntry *); +extern int authenticateUnregister(const char *url, void *); +extern void authenticateFreeMemory(void); + extern void refreshAddToList(const char *, int, time_t, int, time_t); extern int refreshCheck(const StoreEntry *, request_t *, time_t delta); extern time_t refreshWhen(const StoreEntry * entry); diff -urwN squid-1.2.beta23pl3-orig/src/structs.h squid-1.2.beta23pl3/src/structs.h --- squid-1.2.beta23pl3-orig/src/structs.h Thu Jul 30 22:59:30 1998 +++ squid-1.2.beta23pl3/src/structs.h Sat Aug 1 14:40:05 1998 @@ -57,10 +57,7 @@ }; struct _acl_proxy_auth { - char *filename; - time_t last_time; - time_t change_time; - int check_interval; + int timeout; /* timeout value for cached usercode:password entries */ hash_table *hash; }; @@ -68,7 +65,10 @@ /* first two items must be same as hash_link */ char *user; acl_proxy_auth_user *next; + /* extra fields for proxy_auth */ char *passwd; + int passwd_ok; /* 1 = passwd checked OK */ + long expiretime; }; struct _acl_deny_info_list { @@ -170,6 +170,7 @@ request_t *request; char ident[USER_IDENT_SZ]; char browser[BROWSERNAMELEN]; + acl_proxy_auth_user *auth_user; acl_lookup_state state[ACL_ENUM_MAX]; PF *callback; void *callback_data; @@ -274,11 +275,14 @@ struct { char *dnsserver; char *redirect; + char *authenticate; + wordlist *authenticate_options; char *pinger; char *unlinkd; } Program; int dnsChildren; int redirectChildren; + int authenticateChildren; struct { char *host; u_short port;