diff -urwN squid-1.1.20-orig/src/Makefile.in squid-1.1.20/src/Makefile.in --- squid-1.1.20-orig/src/Makefile.in Fri Oct 24 19:57:11 1997 +++ squid-1.1.20/src/Makefile.in Sat Mar 7 18:01:23 1998 @@ -73,13 +73,15 @@ DNSSERVER_LIBS = -L../lib -lmiscutil $(XTRA_LIBS) FTPGET_LIBS = -L../lib -lmiscutil $(XTRA_LIBS) PINGER_LIBS = -L../lib -lmiscutil $(XTRA_LIBS) +AUTH_LIBS = -L../lib -lmiscutil $(CRYPTLIB) $(XTRA_LIBS) PROGS = squid client -UTILS = dnsserver ftpget unlinkd +UTILS = dnsserver ftpget unlinkd ncsa_auth SUID_UTILS = pinger CGIPROGS = cachemgr.cgi OBJS = \ acl.o \ + authenticate.o \ cache_cf.o \ client_db.o \ client_side.o \ @@ -164,6 +166,9 @@ unlinkd: unlinkd.c $(CC) $(CFLAGS) -DUNLINK_DAEMON $(srcdir)/unlinkd.c -o $@ $(LDFLAGS) + +ncsa_auth: ncsa_auth.o hash.o + $(CC) -o $@ $(LDFLAGS) $@.o hash.o $(AUTH_LIBS) squid.conf: squid.conf.pre Makefile sed "\ diff -urwN squid-1.1.20-orig/src/acl.c squid-1.1.20/src/acl.c --- squid-1.1.20-orig/src/acl.c Mon Nov 3 20:27:08 1997 +++ squid-1.1.20/src/acl.c Thu Feb 12 22:13:39 1998 @@ -171,6 +171,10 @@ return ACL_METHOD; if (!strcmp(s, "browser")) return ACL_BROWSER; +#ifdef USE_PROXY_AUTH + if (!strcmp(s, "proxy_auth")) + return ACL_PROXY_AUTH; +#endif /* USE_PROXY_AUTH */ return ACL_NONE; } @@ -572,6 +576,42 @@ #endif /* USE_SPLAY_TREE */ +#ifdef USE_PROXY_AUTH + +/* default proxy_auth timeout is 3600 seconds */ +#define PROXY_AUTH_TIMEOUT 3600 + +static void +aclParseProxyAuth(void *data) +{ + struct _acl_proxy_auth *p; + struct _acl_proxy_auth **q = data; + char *t; + + p = xcalloc(1, sizeof(struct _acl_proxy_auth)); + + /* read timeout value (if any) */ + t = strtok(NULL, w_space); + if (t == NULL) { + p->timeout = PROXY_AUTH_TIMEOUT; + } else { + p->timeout = atoi(t); + } + /* 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(urlcmp, 7921, hash_string)) < 0) { + debug(28, 0, "aclParseProxyAuth: can't create hash table, turning auth off\n"); + *q = NULL; + return; + } + *q = p; + return; +} +#endif /* USE_PROXY_AUTH */ + void aclParseAclLine(void) { @@ -650,6 +690,11 @@ case ACL_BROWSER: aclParseRegexList(&A->data, 0); break; +#ifdef USE_PROXY_AUTH + case ACL_PROXY_AUTH: + aclParseProxyAuth(&A->data); + break; +#endif /* USE_PROXY_AUTH */ case ACL_NONE: default: debug_trap("Bad ACL type"); @@ -998,6 +1043,78 @@ return 0; } +#ifdef USE_PROXY_AUTH +static int +aclMatchProxyAuth(struct _acl_proxy_auth *p, struct _auth_r *auth) +{ + LOCAL_ARRAY(char, user, ICP_IDENT_SZ); + char *passwd = NULL; + hash_link *hashr = NULL; + struct _passwd_r *pwd; + + /* do some basic checking on the basic authentication string */ + if ((auth->basic == NULL) || (*auth->basic == '\0') || + (strstr(auth->basic, ":") == NULL)) + return 0; + + /* separate user and password */ + xstrncpy(user, auth->basic, ICP_IDENT_SZ); + passwd = strstr(user, ":"); + *passwd = '\0'; + passwd++; + debug(28, 5, "aclMatchProxyAuth: checking user %s\n", user); + + hashr = hash_lookup(p->hash, user); + if (hashr == NULL) { + /* user not yet in hash, ask authenticator */ + debug(28, 4, "aclMatchProxyAuth: user %s not yet in hash\n", user); + } else { + /* See if we've already validated this user before */ + pwd = (struct _passwd_r *) hashr->item; + passwd[0] |= 0x80; /* check mutated password */ + if ((strcmp(pwd->passwd, passwd) == 0) && + (pwd->expiretime > current_time.tv_sec)) { + debug(28, 5, "aclMatchProxyAuth: user %s previously validated\n", + user); + return 1; + } + /* password mismatch/timeout, lookup password again for user */ + passwd[0] &= ~(0x80); /* remove mutation */ + debug(28, 4, "aclMatchProxyAuth: user %s passwd mismatch/timeout\n", + user); + xfree(pwd->passwd); + xfree(pwd); + hash_delete(p->hash, user); + } + + if (auth->checked == -1) { + /* password was checked but didn't match */ + debug(28, 4, "aclMatchProxyAuth: authenticate failed for user %s\n", + user); + return 0; + } + + if (auth->checked == 0) { + /* password was not yet checked, force password check */ + debug(28, 4, "aclMatchProxyAuth: user %s not yet checked\n", user); + return -1; + } + + /* auth->checked == 1 */ + + debug(28, 5, "aclMatchProxyAuth: user %s validated OK, stored in hash\n", + user); + passwd[0] |= 0x80; /* store mutated password away */ + + pwd = xmalloc(sizeof(struct _passwd_r)); + pwd->expiretime = current_time.tv_sec + p->timeout; + pwd->passwd = xstrdup(passwd); + hash_insert(p->hash, xstrdup(user), (void *) pwd); + + return 1; +} +#endif /* USE_PROXY_AUTH */ + static int aclMatchInteger(intlist * data, int i) { @@ -1114,8 +1231,6 @@ return aclMatchInteger(acl->data, r->port); /* NOTREACHED */ case ACL_USER: - /* debug(28, 0, "aclMatchAcl: ACL_USER unimplemented\n"); */ - /* return 0; */ return aclMatchIdent(acl->data, checklist->ident); /* NOTREACHED */ case ACL_PROTO: @@ -1127,6 +1242,29 @@ case ACL_BROWSER: return aclMatchRegex(acl->data, checklist->browser); /* NOTREACHED */ +#ifdef USE_PROXY_AUTH + case ACL_PROXY_AUTH: + /* checklist->auth containts user:passwd */ + k = aclMatchProxyAuth(acl->data, &checklist->auth); + if (k == 0) { + /* No such user OR we need proxy authentication info; + This means that a 407 HTTP return code should be sent + to the client */ + checklist->state[ACL_PROXY_AUTH] = ACL_PROXY_AUTH_NEEDED; + return 0; + } else if (k == 1) { + /* user validated OK */ + /* register that we used the proxy authentication header + so that it will be logged in access.log */ + checklist->state[ACL_PROXY_AUTH] = ACL_PROXY_AUTH_USED; + 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; /* XXX */ + } + /* NOTREACHED */ +#endif /* USE_PROXY_AUTH */ case ACL_NONE: default: debug(28, 0, "aclMatchAcl: '%s' has bad type %d\n", @@ -1223,6 +1361,27 @@ } } +#ifdef USE_PROXY_AUTH +void +aclDestroyProxyAuth(struct _acl_proxy_auth *p) +{ + int i; + hash_link *hashr = NULL; + struct _passwd_r *pwd; + + /* destroy hash list contents */ + for (i = 0, hashr = hash_first(p->hash); hashr; hashr = hash_next(p->hash)) { + pwd = (struct _passwd_r *) hashr->item; + safe_free(pwd->passwd); + safe_free(pwd); + hash_delete(p->hash, hashr->key); + } + /* destroy and free the hash table itself */ + hash_destroy(p->hash); + safe_free(p); +} +#endif + void aclDestroyAcls(void) { @@ -1268,6 +1427,11 @@ case ACL_METHOD: intlistDestroy((intlist **) & a->data); break; +#ifdef USE_PROXY_AUTH + case ACL_PROXY_AUTH: + aclDestroyProxyAuth(a->data); + break; +#endif /* USE_PROXY_AUTH */ case ACL_NONE: default: fatal_dump("aclDestroyAcls: Found ACL_NONE?"); diff -urwN squid-1.1.20-orig/src/acl.h squid-1.1.20/src/acl.h --- squid-1.1.20-orig/src/acl.h Thu Feb 20 22:03:10 1997 +++ squid-1.1.20/src/acl.h Thu Feb 12 21:54:54 1998 @@ -44,6 +44,9 @@ ACL_PROTO, ACL_METHOD, ACL_BROWSER, +#ifdef USE_PROXY_AUTH + ACL_PROXY_AUTH, +#endif /* USE_PROXY_AUTH */ ACL_ENUM_MAX } squid_acl; @@ -78,6 +81,20 @@ struct _acl_name_list *next; }; +#ifdef USE_PROXY_AUTH +struct _acl_proxy_auth { + int timeout; /* timeout value for cached usercode:password entries */ + HashID hash; +}; + +/* a password record to be stored in the hash table; each password has + it's own expiretime */ +struct _passwd_r { + long expiretime; + char *passwd; +}; +#endif /* USE_PROXY_AUTH */ + struct _acl_deny_info_list { char url[MAX_URL]; struct _acl_name_list *acl_list; @@ -116,15 +133,28 @@ ACL_LOOKUP_NONE, ACL_LOOKUP_NEED, ACL_LOOKUP_PENDING, - ACL_LOOKUP_DONE + ACL_LOOKUP_DONE, + ACL_PROXY_AUTH_NEEDED, + ACL_PROXY_AUTH_USED, + ACL_PROXY_AUTH_CHECK } acl_lookup_state; +#ifdef USE_PROXY_AUTH +struct _auth_r { + char basic[ICP_IDENT_SZ]; /* usercode:password */ + int checked; /* 0 = not yet, 1 = OK, -1 = NOT OK */ +}; +#endif + struct _aclCheck_t { struct in_addr src_addr; struct in_addr dst_addr; char src_fqdn[SQUIDHOSTNAMELEN]; request_t *request; char ident[ICP_IDENT_SZ]; +#ifdef USE_PROXY_AUTH + struct _auth_r auth; +#endif char browser[BROWSERNAMELEN]; acl_lookup_state state[ACL_ENUM_MAX]; }; diff -urwN squid-1.1.20-orig/src/authenticate.c squid-1.1.20/src/authenticate.c --- squid-1.1.20-orig/src/authenticate.c Thu Jan 1 01:00:00 1970 +++ squid-1.1.20/src/authenticate.c Sat Mar 7 16:31:44 1998 @@ -0,0 +1,531 @@ +/* + * $Id: authenticate.c,v 1.34.2.1 1997/04/15 23:26:02 wessels Exp $ + * + * created from redirect.c + * + * DEBUG: section 44 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. + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifdef USE_PROXY_AUTH + +#include "squid.h" + +#define AUTHENTICATE_FLAG_ALIVE 0x01 +#define AUTHENTICATE_FLAG_BUSY 0x02 +#define AUTHENTICATE_FLAG_CLOSING 0x04 + +typedef struct { + int fd; + void *data; + const char *client_ident; + const char *client_passwd; + AH 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 _PARAMS((void)); +static int authenticateCreateAuthenticator _PARAMS((const char *command)); +static int authenticateHandleRead _PARAMS((int, authenticator_t *)); +static authenticateStateData *Dequeue _PARAMS((void)); +static void Enqueue _PARAMS((authenticateStateData *)); +static void authenticateDispatch _PARAMS((authenticator_t *, authenticateStateData *)); + +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 char *authenticator_args[32]; + +static int +authenticateCreateAuthenticator(const char *command) +{ + pid_t pid; + struct sockaddr_in S; + static int n_authenticator = 0; + int cfd; + int sfd; + int len; + int fd; + struct timeval slp; + cfd = comm_open(SOCK_STREAM, + 0, + local_addr, + 0, + COMM_NOCLOEXEC, + "socket to authenticator"); + if (cfd == COMM_ERROR) { + debug(44, 0, "authenticate_create_authenticator: Failed to create authenticator\n"); + return -1; + } + len = sizeof(S); + memset(&S, '\0', len); + if (getsockname(cfd, (struct sockaddr *) &S, &len) < 0) { + debug(50, 0, "authenticate_create_authenticator: getsockname: %s\n", xstrerror()); + comm_close(cfd); + return -1; + } + listen(cfd, 1); + if ((pid = fork()) < 0) { + debug(50, 0, "authenticate_create_authenticator: fork: %s\n", xstrerror()); + comm_close(cfd); + return -1; + } + if (pid > 0) { /* parent */ + comm_close(cfd); /* close shared socket with child */ + /* open new socket for parent process */ + sfd = comm_open(SOCK_STREAM, + 0, + local_addr, + 0, + 0, + NULL); /* blocking! */ + if (sfd == COMM_ERROR) + return -1; + if (comm_connect_addr(sfd, &S) == COMM_ERROR) { + comm_close(sfd); + return -1; + } + comm_set_fd_lifetime(sfd, -1); + debug(44, 4, "authenticate_create_authenticator: FD %d connected to %s #%d.\n", + sfd, command, ++n_authenticator); + slp.tv_sec = 0; + slp.tv_usec = 250000; + select(0, NULL, NULL, NULL, &slp); + return sfd; + } + /* child */ + no_suid(); /* give up extra priviliges */ + if ((fd = accept(cfd, NULL, NULL)) < 0) { + debug(50, 0, "authenticate_create_authenticator: FD %d accept: %s\n", + cfd, xstrerror()); + _exit(1); + } + dup2(fd, 0); + dup2(fd, 1); + dup2(fileno(debug_log), 2); + fclose(debug_log); + close(fd); + close(cfd); + + execvp(command, authenticator_args); + debug(50, 0, "authenticate_create_authenticator: %s: %s\n", command, xstrerror()); + _exit(1); + return 0; +} + +static int +authenticateHandleRead(int fd, authenticator_t * authenticator) +{ + int len; + authenticateStateData *r = authenticator->authenticateState; + char *t = NULL; + int n; + int svc_time; + + len = read(fd, + authenticator->inbuf + authenticator->offset, + authenticator->size - authenticator->offset); + debug(44, 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(44, authenticator->flags & AUTHENTICATE_FLAG_CLOSING ? 5 : 1, + "FD %d: Connection from Authenticator #%d is closed, disabling\n", + fd, authenticator->index + 1); + authenticator->flags = 0; + put_free_8k_page(authenticator->inbuf); + authenticator->inbuf = NULL; + comm_close(fd); + if (--NAuthenticatorsOpen == 0 && !shutdown_pending && !reread_pending) + fatal_dump("All authenticators have exited!"); + return 0; + } + if (len != 1) + AuthenticateStats.rewrites[authenticator->index]++; + authenticator->offset += len; + authenticator->inbuf[authenticator->offset] = '\0'; + /* reschedule */ + commSetSelect(authenticator->fd, + COMM_SELECT_READ, + (PF) 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(44, 0, "authenticateHandleRead: unexpected reply: '%s'\n", + authenticator->inbuf); + authenticator->offset = 0; + } else { + debug(44, 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); + } + safe_free(r); + authenticator->authenticateState = NULL; + authenticator->flags &= ~AUTHENTICATE_FLAG_BUSY; + authenticator->offset = 0; + n = ++AuthenticateStats.replies; + svc_time = tvSubMsec(authenticator->dispatch_time, current_time); + if (n > AUTHENTICATE_AV_FACTOR) + n = AUTHENTICATE_AV_FACTOR; + AuthenticateStats.avg_svc_time + = (AuthenticateStats.avg_svc_time * (n - 1) + svc_time) / n; + } + } + while ((authenticator = GetFirstAvailable()) && (r = Dequeue())) + authenticateDispatch(authenticator, r); + return 0; +} + +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 (BIT_TEST(authenticate->flags, AUTHENTICATE_FLAG_BUSY)) + continue; + if (!BIT_TEST(authenticate->flags, AUTHENTICATE_FLAG_ALIVE)) + continue; + return authenticate; + } + return NULL; +} + + +static void +authenticateDispatch(authenticator_t * authenticate, authenticateStateData * r) +{ + char *buf = NULL; + int len; + if (r->handler == NULL) { + debug(44, 1, "authenticateDispatch: skipping '%s' because no handler\n", + r->client_ident); + safe_free(r); + return; + } + authenticate->flags |= AUTHENTICATE_FLAG_BUSY; + authenticate->authenticateState = r; + authenticate->dispatch_time = current_time; + buf = get_free_8k_page(); + sprintf(buf, "%s %s\n", + r->client_ident, + r->client_passwd); + len = strlen(buf); + comm_write(authenticate->fd, + buf, + len, + 0, /* timeout */ + NULL, /* Handler */ + NULL, /* Handler-data */ + put_free_8k_page); + debug(44, 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(int cfd, icpStateData * icpState, AH handler, void *data) +{ + authenticateStateData *r = NULL; + authenticator_t *authenticator = NULL; + debug(44, 5, "authenticateStart: '%s'\n", icpState->url); + if (!handler) + fatal_dump("authenticateStart: NULL handler"); + if (!icpState) + fatal_dump("authenticateStart: NULL icpState"); + if (Config.Program.authenticate == NULL) { + handler(data, NULL); + return; + } + r = xcalloc(1, sizeof(authenticateStateData)); + r->fd = cfd; + if (icpState->ident.ident == NULL || *icpState->ident.ident == '\0') { + r->client_ident = dash_str; + } else { + r->client_ident = icpState->ident.ident; + } + if (icpState->ident.passwd == NULL || *icpState->ident.passwd == '\0') { + r->client_passwd = dash_str; + } else { + r->client_passwd = icpState->ident.passwd; + } + r->handler = handler; + r->data = data; + 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) + put_free_8k_page(authenticate_child_table[k]->inbuf); + safe_free(authenticate_child_table[k]); + } + safe_free(authenticate_child_table); + } +} + +void +authenticateOpenServers(void) +{ + char *prg = Config.Program.authenticate; + int k; + int authenticatesocket; + LOCAL_ARRAY(char, fd_note_buf, FD_ASCII_NOTE_SZ); + static int first_time = 0; + char **av, *p; + + authenticateFreeMemory(); + if (Config.Program.authenticate == NULL) + return; + NAuthenticators = NAuthenticatorsOpen = Config.authenticateChildren; + authenticate_child_table = xcalloc(NAuthenticators, sizeof(authenticator_t *)); + + av = authenticator_args; + *av = strdup("(authenticator)"); + av++; k = 1; + p = strtok(Config.Program.authenticate_opts, " \t"); + while ((p != NULL) && (k < 32)) { + *av = p; + av++; k++; + p = strtok(NULL, " \t"); + } + *av = NULL; + + debug(44, 1, "authenticateOpenServers: Starting %d '%s' processes\n", + NAuthenticators, prg); + for (k = 0; k < NAuthenticators; k++) { + authenticate_child_table[k] = xcalloc(1, sizeof(authenticator_t)); + if ((authenticatesocket = authenticateCreateAuthenticator(prg)) < 0) { + debug(44, 1, "WARNING: Cannot run '%s' process.\n", prg); + authenticate_child_table[k]->flags &= ~AUTHENTICATE_FLAG_ALIVE; + } else { + authenticate_child_table[k]->flags |= AUTHENTICATE_FLAG_ALIVE; + authenticate_child_table[k]->index = k; + authenticate_child_table[k]->fd = authenticatesocket; + authenticate_child_table[k]->inbuf = get_free_8k_page(); + authenticate_child_table[k]->size = 8192; + authenticate_child_table[k]->offset = 0; + sprintf(fd_note_buf, "%s #%d", + 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, + (PF) authenticateHandleRead, + (void *) authenticate_child_table[k], 0); + debug(44, 3, "authenticateOpenServers: 'authenticate_server' %d started\n", + k); + } + } + if (first_time == 0) { + first_time++; + memset(&AuthenticateStats, '\0', sizeof(AuthenticateStats)); + } +} + +void +authenticateShutdownServers(void) +{ + authenticator_t *authenticate = NULL; + authenticateStateData *r = NULL; + int k; + 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 (!(authenticate->flags & AUTHENTICATE_FLAG_ALIVE)) + continue; + if (authenticate->flags & AUTHENTICATE_FLAG_BUSY) + continue; + if (authenticate->flags & AUTHENTICATE_FLAG_CLOSING) + continue; + debug(44, 3, "authenticateShutdownServers: closing authenticator #%d, FD %d\n", + authenticate->index + 1, authenticate->fd); + comm_close(authenticate->fd); + authenticate->flags |= AUTHENTICATE_FLAG_CLOSING; + authenticate->flags |= AUTHENTICATE_FLAG_BUSY; + } +} + + +int +authenticateUnregister(const char *url, int fd) +{ + authenticator_t *authenticate = NULL; + authenticateStateData *r = NULL; + struct authenticateQueueData *rq = NULL; + int k; + int n = 0; + if (Config.Program.authenticate == NULL) + return 0; + debug(44, 3, "authenticateUnregister: FD %d '%s'\n", fd, url); + for (k = 0; k < NAuthenticators; k++) { + authenticate = *(authenticate_child_table + k); + if ((r = authenticate->authenticateState) == NULL) + continue; + if (r->fd != fd) + continue; +#if 0 + if (strcmp(r->orig_url, url)) + continue; +#endif + debug(44, 3, "authenticateUnregister: Found match\n"); + r->handler = NULL; + n++; + } + for (rq = authenticateQueueHead; rq; rq = rq->next) { + if ((r = rq->authenticateState) == NULL) + continue; + if (r->fd != fd) + continue; +#if 0 + if (strcmp(r->orig_url, url)) + continue; +#endif + debug(44, 3, "authenticateUnregister: Found match.\n"); + r->handler = NULL; + n++; + } + debug(44, 3, "authenticateUnregister: Unregistered %d handlers\n", n); + return n; +} + +void +authenticateStats(StoreEntry * sentry) +{ + int k; + storeAppendPrintf(sentry, open_bracket); + 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 rewrites)}\n", + k + 1, + AuthenticateStats.use_hist[k], + AuthenticateStats.rewrites[k]); + } + storeAppendPrintf(sentry, close_bracket); +} + +#endif diff -urwN squid-1.1.20-orig/src/authenticate.h squid-1.1.20/src/authenticate.h --- squid-1.1.20-orig/src/authenticate.h Thu Jan 1 01:00:00 1970 +++ squid-1.1.20/src/authenticate.h Sun Feb 8 20:52:50 1998 @@ -0,0 +1,48 @@ +/* + * $Id: authenticate.h,v 1.8.4.1 1997/04/15 23:26:03 wessels Exp $ + * + * 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. + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifndef AUTHENTICATE_H +#define AUTHENTICATE_H + +#define AUTHENTICATE_AV_FACTOR 1000 + +typedef void (*AH) (void *data, char *result); + +extern void authenticateStart _PARAMS((int cfd, icpStateData *, AH, void *)); +extern void authenticateOpenServers _PARAMS((void)); +extern void authenticateShutdownServers _PARAMS((void)); +extern void authenticateStats _PARAMS((StoreEntry *)); +extern int authenticateUnregister _PARAMS((const char *url, int fd)); +extern void authenticateFreeMemory _PARAMS((void)); + +#define AUTHENTICATE_NONE 0 +#define AUTHENTICATE_PENDING 1 +#define AUTHENTICATE_DONE 2 + +#endif /* AUTHENTICATE_H */ diff -urwN squid-1.1.20-orig/src/cache_cf.c squid-1.1.20/src/cache_cf.c --- squid-1.1.20-orig/src/cache_cf.c Wed Dec 31 22:36:58 1997 +++ squid-1.1.20/src/cache_cf.c Sat Mar 7 16:25:38 1998 @@ -134,6 +134,9 @@ #define DefaultOptionsAnonymizer 0 /* default off */ #define DefaultOptionsIcpHitStale 0 /* default off */ #define DefaultRedirectChildren 5 /* 5 processes */ +#ifdef USE_PROXY_AUTH +#define DefaultAuthenticateChildren 5 /* 5 processes */ +#endif #define DefaultMaxRequestSize (100 << 10) /* 100Kb */ #define DefaultHttpPortNum CACHE_HTTP_PORT @@ -145,9 +148,6 @@ #define DefaultUseragentLogFile (char *)NULL /* default NONE */ #define DefaultStoreLogFile DEFAULT_STORE_LOG #define DefaultSwapLogFile (char *)NULL /* default swappath(0) */ -#if USE_PROXY_AUTH -#define DefaultProxyAuthFile (char *)NULL /* default NONE */ -#endif /* USE_PROXY_AUTH */ #define DefaultLogRotateNumber 10 #define DefaultAdminEmail "webmaster" #define DefaultFtpgetProgram DEFAULT_FTPGET @@ -156,6 +156,10 @@ #define DefaultPingerProgram DEFAULT_PINGER #define DefaultUnlinkdProgram DEFAULT_UNLINKD #define DefaultRedirectProgram (char *)NULL /* default NONE */ +#ifdef USE_PROXY_AUTH +#define DefaultAuthenticateProgram (char *)NULL /* default NONE */ +#define DefaultAuthenticateOptions "" +#endif #define DefaultEffectiveUser (char *)NULL /* default NONE */ #define DefaultEffectiveGroup (char *)NULL /* default NONE */ #define DefaultAppendDomain (char *)NULL /* default NONE */ @@ -654,22 +658,6 @@ Config.adminEmail = xstrdup(token); } -#if USE_PROXY_AUTH -static void -parseProxyAuthLine(void) -{ - char *token; - token = strtok(NULL, w_space); - if (token == NULL) - self_destruct(); - safe_free(Config.proxyAuth.File); - aclDestroyRegexList(Config.proxyAuth.IgnoreDomains); - Config.proxyAuth.IgnoreDomains = NULL; - Config.proxyAuth.File = xstrdup(token); - aclParseRegexList(&Config.proxyAuth.IgnoreDomains, 1); -} -#endif /* USE_PROXY_AUTH */ - static void parseHttpdAccelLine(void) { @@ -1226,19 +1214,23 @@ else if (!strcmp(token, "redirect_children")) parseIntegerValue(&Config.redirectChildren); +#ifdef USE_PROXY_AUTH + else if (!strcmp(token, "authenticate_program")) + parsePathname(&Config.Program.authenticate, 1); + + else if (!strcmp(token, "authenticate_options")) + parseToEndOfLine(&Config.Program.authenticate_opts); + + else if (!strcmp(token, "authenticate_children")) + parseIntegerValue(&Config.authenticateChildren); +#endif + else if (!strcmp(token, "pinger_program")) parsePathname(&Config.Program.pinger, 1); else if (!strcmp(token, "unlinkd_program")) parsePathname(&Config.Program.unlinkd, 1); -#if USE_PROXY_AUTH - else if (!strcmp(token, "proxy_auth")) - parseProxyAuthLine(); - else if (!strcmp(token, "proxy_auth_ignore")) - aclParseRegexList(&Config.proxyAuth.IgnoreDomains, 1); -#endif /* USE_PROXY_AUTH */ - else if (!strcmp(token, "source_ping")) parseOnOff(&Config.sourcePing); @@ -1477,6 +1469,10 @@ safe_free(Config.Program.ftpget_opts); safe_free(Config.Program.dnsserver); safe_free(Config.Program.redirect); +#ifdef USE_PROXY_AUTH + safe_free(Config.Program.authenticate); + safe_free(Config.Program.authenticate_opts); +#endif safe_free(Config.Program.unlinkd); safe_free(Config.Program.pinger); safe_free(Config.Accel.host); @@ -1486,11 +1482,6 @@ safe_free(Config.pidFilename); safe_free(Config.visibleHostname); safe_free(Config.ftpUser); -#if USE_PROXY_AUTH - safe_free(Config.proxyAuth.File); - aclDestroyRegexList(Config.proxyAuth.IgnoreDomains); - Config.proxyAuth.IgnoreDomains = NULL; -#endif /* USE_PROXY_AUTH */ safe_free(Config.Announce.host); safe_free(Config.Announce.file); safe_free(Config.errHtmlText); @@ -1539,6 +1530,9 @@ Config.cleanRate = DefaultCleanRate; Config.dnsChildren = DefaultDnsChildren; Config.redirectChildren = DefaultRedirectChildren; +#ifdef USE_PROXY_AUTH + Config.authenticateChildren = DefaultAuthenticateChildren; +#endif Config.sourcePing = DefaultSourcePing; Config.quickAbort.min = DefaultQuickAbortMin; Config.quickAbort.pct = DefaultQuickAbortPct; @@ -1572,6 +1566,10 @@ Config.Program.ftpget_opts = safe_xstrdup(DefaultFtpgetOptions); Config.Program.dnsserver = safe_xstrdup(DefaultDnsserverProgram); Config.Program.redirect = safe_xstrdup(DefaultRedirectProgram); +#ifdef USE_PROXY_AUTH + Config.Program.authenticate = safe_xstrdup(DefaultAuthenticateProgram); + Config.Program.authenticate_opts = safe_xstrdup(DefaultAuthenticateOptions); +#endif Config.Program.pinger = safe_xstrdup(DefaultPingerProgram); Config.Program.unlinkd = safe_xstrdup(DefaultUnlinkdProgram); Config.Accel.host = safe_xstrdup(DefaultAccelHost); @@ -1580,10 +1578,6 @@ Config.Accel.withProxy = DefaultAccelWithProxy; Config.pidFilename = safe_xstrdup(DefaultPidFilename); Config.visibleHostname = safe_xstrdup(DefaultVisibleHostname); -#if USE_PROXY_AUTH - Config.proxyAuth.File = safe_xstrdup(DefaultProxyAuthFile); -/* Config.proxyAuth.IgnoreDomains = safe_xstrdup(DefaultproxyAuthIgnoreDomains); */ -#endif /* USE_PROXY_AUTH */ Config.ftpUser = safe_xstrdup(DefaultFtpUser); Config.Announce.host = safe_xstrdup(DefaultAnnounceHost); Config.Announce.port = DefaultAnnouncePort; diff -urwN squid-1.1.20-orig/src/cache_cf.h squid-1.1.20/src/cache_cf.h --- squid-1.1.20-orig/src/cache_cf.h Mon Dec 1 08:07:57 1997 +++ squid-1.1.20/src/cache_cf.h Sat Mar 7 15:27:19 1998 @@ -108,6 +108,9 @@ #define DefaultDnsChildrenMax 32 /* 32 processes */ #define DefaultRedirectChildrenMax 32 /* 32 processes */ +#ifdef USE_PROXY_AUTH +#define DefaultAuthenticateChildrenMax 32 /* 32 processes */ +#endif typedef struct _wordlist { char *key; @@ -177,12 +180,6 @@ int rotateNumber; int log_fqdn; } Log; -#if USE_PROXY_AUTH - struct { - char *File; - relist *IgnoreDomains; - } proxyAuth; -#endif /* USE_PROXY_AUTH */ char *adminEmail; char *effectiveUser; char *effectiveGroup; @@ -191,11 +188,18 @@ char *ftpget_opts; char *dnsserver; char *redirect; +#ifdef USE_PROXY_AUTH + char *authenticate; + char *authenticate_opts; +#endif char *pinger; char *unlinkd; } Program; int dnsChildren; int redirectChildren; +#ifdef USE_PROXY_AUTH + int authenticateChildren; +#endif int sourcePing; int commonLogFormat; #if LOG_FULL_HEADERS diff -urwN squid-1.1.20-orig/src/cachemgr.c squid-1.1.20/src/cachemgr.c --- squid-1.1.20-orig/src/cachemgr.c Wed Jan 7 23:59:04 1998 +++ squid-1.1.20/src/cachemgr.c Mon Feb 9 22:00:38 1998 @@ -225,6 +225,7 @@ STATS_F, STATS_D, STATS_R, + STATS_A, STATS_O, STATS_VM, STATS_U, @@ -252,6 +253,7 @@ "stats/fqdncache", "stats/dns", "stats/redirector", + "stats/authenticator", "stats/objects", "stats/vm_objects", "stats/utilization", @@ -279,6 +281,7 @@ "FQDN Cache Contents", "DNS Server Statistics", "Redirector Statistics", + "Authenticator Statistics", "Objects", "VM Objects", "Utilization", @@ -374,6 +377,7 @@ print_option(op, STATS_F); print_option(op, STATS_D); print_option(op, STATS_R); + print_option(op, STATS_A); print_option(op, SHUTDOWN); print_option(op, REFRESH); #ifdef REMOVE_OBJECT @@ -730,6 +734,7 @@ case STATS_F: case STATS_D: case STATS_R: + case STATS_A: case STATS_O: case STATS_VM: case STATS_U: @@ -784,6 +789,7 @@ print_option(op, STATS_F); print_option(op, STATS_D); print_option(op, STATS_R); + print_option(op, STATS_A); printf("\n"); printf("\n", hostname); printf("\n", portnum); @@ -826,6 +832,7 @@ case STATS_F: case STATS_D: case STATS_R: + case STATS_A: case STATS_O: case STATS_VM: case STATS_IO: @@ -914,6 +921,7 @@ case STATS_F: case STATS_D: case STATS_R: + case STATS_A: case STATS_IO: case STATS_HDRS: case STATS_FDS: diff -urwN squid-1.1.20-orig/src/client_side.c squid-1.1.20/src/client_side.c --- squid-1.1.20-orig/src/client_side.c Wed Nov 19 17:44:51 1997 +++ squid-1.1.20/src/client_side.c Sun Jun 7 22:14:25 1998 @@ -32,6 +32,9 @@ #include "squid.h" static void clientRedirectDone _PARAMS((void *data, char *result)); +#ifdef USE_PROXY_AUTH +static void clientAuthenticateDone _PARAMS((void *data, char *result)); +#endif static void icpHandleIMSReply _PARAMS((int fd, StoreEntry * entry, void *data)); static void clientLookupDstIPDone _PARAMS((int fd, const ipcache_addrs *, void *data)); static void clientLookupSrcFQDNDone _PARAMS((int fd, const char *fqdn, void *data)); @@ -89,39 +92,6 @@ clientAccessCheck(icpState, icpState->aclHandler); } -#if USE_PROXY_AUTH -/* ProxyAuth code by Jon Thackray */ -/* return 1 if allowed, 0 if denied */ -static int -clientProxyAuthCheck(icpStateData * icpState) -{ - const char *proxy_user; - - /* Check that the user is allowed to access via this proxy-cache - * don't restrict if they're accessing a local domain or - * an object of type cacheobj:// */ - if (Config.proxyAuth.File == NULL) - return 1; - if (urlParseProtocol(icpState->url) == PROTO_CACHEOBJ) - return 1; - - if (Config.proxyAuth.IgnoreDomains) { - if (aclMatchRegex(Config.proxyAuth.IgnoreDomains, icpState->request->host)) { - debug(33, 2, "clientProxyAuthCheck: host \"%s\" matched proxyAuthIgnoreDomains\n", icpState->request->host); - return 1; - } - } - proxy_user = proxyAuthenticate(icpState->request_hdr); - xstrncpy(icpState->ident.ident, proxy_user, ICP_IDENT_SZ); - xstrncpy(icpState->aclChecklist->ident, proxy_user, ICP_IDENT_SZ); - debug(33, 6, "clientProxyAuthCheck: user = %s\n", icpState->ident.ident); - - if (strcmp(icpState->ident.ident, dash_str) == 0) - return 0; - return 1; -} -#endif /* USE_PROXY_AUTH */ - static int checkAccelOnly(icpStateData * icpState) { @@ -145,6 +115,9 @@ aclCheck_t *ch = NULL; char *browser = NULL; const ipcache_addrs *ia = NULL; +#ifdef USE_PROXY_AUTH + int proxy_auth_hdr = 0; +#endif if (Config.identLookup && icpState->ident.state == IDENT_NONE) { icpState->aclHandler = handler; @@ -164,11 +137,90 @@ xstrncpy(icpState->aclChecklist->ident, icpState->ident.ident, ICP_IDENT_SZ); +#ifdef USE_PROXY_AUTH + /* Initialize auth.checked on 0 because we don't have any + usercode/password validation done yet */ + icpState->aclChecklist->auth.checked = 0; +#endif } /* This so we can have SRC ACLs for cache_host_acl. */ icpState->request->client_addr = icpState->peer.sin_addr; -#if USE_PROXY_AUTH - if (clientProxyAuthCheck(icpState) == 0) { + +#ifdef USE_PROXY_AUTH + if (urlParseProtocol(icpState->url) != PROTO_CACHEOBJ) { + char *s = NULL; + /* Look for Proxy-authorization: Basic in the + * headers sent by the client + */ + if ((s = mime_get_header(icpState->request_hdr, + "Proxy-authorization:")) == NULL) { + debug(33, 5, "clientAccessCheck: Can't find authorization header\n"); + xstrncpy(icpState->aclChecklist->auth.basic, "-:-", ICP_IDENT_SZ); + } else { + LOCAL_ARRAY(char, sent_user, ICP_IDENT_SZ); + char *sent_userandpw = NULL; + char *clear_userandpw = NULL; + /* Skip the 'Basic' part */ + s += strlen(" Basic"); + sent_userandpw = xstrdup(s); + /* Trim trailing \n before decoding */ + strtok(sent_userandpw, "\n"); + clear_userandpw = uudecode(sent_userandpw); + xfree(sent_userandpw); + xstrncpy(icpState->aclChecklist->auth.basic, clear_userandpw, + ICP_IDENT_SZ); + xstrncpy(sent_user, clear_userandpw, ICP_IDENT_SZ); + xfree(clear_userandpw); + if ((s = strstr(sent_user, ":")) != NULL) { + *s = '\0'; s++; + xstrncpy(icpState->ident.ident, sent_user, ICP_IDENT_SZ); + /* we need this in authenticate.c */ + xstrncpy(icpState->ident.passwd, s, ICP_IDENT_SZ); + proxy_auth_hdr = 1; + } else { + /* invalid Basic proxy authorization */ + s = mime_get_header(icpState->request_hdr, + "Proxy-authorization:"); + debug(33, 0, "clientAccessCheck: invalid proxy authorization header: %s\n", + s); + xstrncpy(icpState->aclChecklist->auth.basic, "-:-", ICP_IDENT_SZ); + } + } + } +#endif /* USE_PROXY_AUTH */ + + ch = icpState->aclChecklist; + icpState->aclHandler = handler; + if (checkAccelOnly(icpState)) { + answer = 0; + } else { + answer = aclCheck(HTTPAccessList, ch); + +#ifdef USE_PROXY_AUTH + if (ch->state[ACL_PROXY_AUTH] == ACL_PROXY_AUTH_CHECK) { + /* we need to check the usercode/password combination + stored in icpState->ident */ + debug(33, 5, "clientAccessCheck: check the password\n"); + if (icpState->authenticate_state != AUTHENTICATE_NONE) + fatal_dump("clientAccessCheck: wrong authenticate_state"); + icpState->authenticate_state = AUTHENTICATE_PENDING; + authenticateStart(icpState->fd, icpState, + clientAuthenticateDone, icpState); + return; + } + /* We can have received a Proxy-authentication: header + * but there may be no proxy authentication needed. + * In that case we erase the username from icpState->ident. + */ + if (proxy_auth_hdr && + (ch->state[ACL_PROXY_AUTH] != ACL_PROXY_AUTH_USED)) { + debug(33, 5, "clientAccessCheck: proxy_auth not used, resetting ident\n"); + xstrncpy(icpState->ident.ident, "-", ICP_IDENT_SZ); + xstrncpy(icpState->ident.passwd, "-", ICP_IDENT_SZ); + } + /* check whether we need a proxy authentication header */ + if (ch->state[ACL_PROXY_AUTH] == ACL_PROXY_AUTH_NEEDED) { + /* We need proxy authentication, send 407 error code */ char *wbuf = NULL; int fd = icpState->fd; debug(33, 4, "Proxy Denied: %s\n", icpState->url); @@ -184,12 +236,6 @@ } #endif /* USE_PROXY_AUTH */ - ch = icpState->aclChecklist; - icpState->aclHandler = handler; - if (checkAccelOnly(icpState)) { - answer = 0; - } else { - answer = aclCheck(HTTPAccessList, ch); if (ch->state[ACL_DST_IP] == ACL_LOOKUP_NEED) { ch->state[ACL_DST_IP] = ACL_LOOKUP_PENDING; /* first */ icpState->ip_lookup_pending = 1; @@ -317,143 +363,32 @@ icpProcessRequest(fd, icpState); } -#if USE_PROXY_AUTH -/* Check the modification time on the file that holds the proxy - * passwords every 'n' seconds, and if it has changed, reload it - */ -#define CHECK_PROXY_FILE_TIME 300 - -const char * -proxyAuthenticate(const char *headers) +#ifdef USE_PROXY_AUTH +static void +clientAuthenticateDone(void *data, char *result) { - /* Keep the time measurements and the hash - * table of users and passwords handy */ - static time_t last_time = 0; - static time_t change_time = 0; - static HashID validated = 0; - static char *passwords = NULL; - LOCAL_ARRAY(char, sent_user, ICP_IDENT_SZ); - - char *s = NULL; - char *sent_userandpw = NULL; - char *user = NULL; - char *passwd = NULL; - char *clear_userandpw = NULL; - struct stat buf; - int i; - hash_link *hashr = NULL; - FILE *f = NULL; - - /* Look for Proxy-authorization: Basic in the - * headers sent by the client - */ - if ((s = mime_get_header(headers, "Proxy-authorization:")) == NULL) { - debug(33, 5, "proxyAuthenticate: Can't find authorization header\n"); - return (dash_str); - } - /* Skip the 'Basic' part */ - s += strlen(" Basic"); - sent_userandpw = xstrdup(s); - strtok(sent_userandpw, "\n"); /* Trim trailing \n before decoding */ - clear_userandpw = uudecode(sent_userandpw); - xfree(sent_userandpw); - - xstrncpy(sent_user, clear_userandpw, ICP_IDENT_SZ); - strtok(sent_user, ":"); /* Remove :password */ - debug(33, 5, "proxyAuthenticate: user = %s\n", sent_user); - - /* Look at the Last-modified time of the proxy.passwords - * file every five minutes, to see if it's been changed via - * a cgi-bin script, etc. If so, reload a fresh copy into memory - */ - - if ((squid_curtime - last_time) > CHECK_PROXY_FILE_TIME) { - debug(33, 5, "proxyAuthenticate: checking password file %s hasn't changed\n", Config.proxyAuth.File); + icpStateData *icpState = data; - if (stat(Config.proxyAuth.File, &buf) == 0) { - if (buf.st_mtime != change_time) { - debug(33, 0, "proxyAuthenticate: reloading changed proxy authentication password file %s \n", Config.proxyAuth.File); - change_time = buf.st_mtime; - - if (validated != 0) { - debug(33, 5, "proxyAuthenticate: invalidating old entries\n"); - for (i = 0, hashr = hash_first(validated); hashr; hashr = hash_next(validated)) { - debug(33, 6, "proxyAuthenticate: deleting %s\n", hashr->key); - hash_delete(validated, hashr->key); - } + debug(33, 5, "clientAuthenticateDone: '%s' result=%s\n", icpState->url, + result ? result : "NULL"); + if (icpState->authenticate_state != AUTHENTICATE_PENDING) + fatal_dump("clientAuthenticateDone: wrong authenticate_state"); + icpState->authenticate_state = AUTHENTICATE_DONE; + if (result) { + if (strcmp(result, "OK") == 0) { + icpState->aclChecklist->auth.checked = 1; } else { - /* First time around, 7921 should be big enough */ - if ((validated = hash_create(urlcmp, 7921, hash_string)) < 0) { - debug(33, 1, "ERK: can't create hash table. Turning auth off"); - xfree(Config.proxyAuth.File); - Config.proxyAuth.File = NULL; - return (dash_str); - } - } - - passwords = xmalloc((size_t) buf.st_size + 2); - f = fopen(Config.proxyAuth.File, "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(33, 5, "proxyAuthenticate: adding new passwords to hash table\n"); - while (user != NULL) { - if (strlen(user) > 1 && passwd && strlen(passwd) > 1) { - debug(33, 6, "proxyAuthenticate: adding %s, %s to hash table\n", user, passwd); - hash_insert(validated, xstrdup(user), (void *) xstrdup(passwd)); - } - user = strtok(NULL, ":"); - passwd = strtok(NULL, "\n"); - } - - xfree(passwords); + icpState->aclChecklist->auth.checked = -1; } } else { - debug(33, 1, "ERK: can't access proxy_auth file %s. Turning authentication off", Config.proxyAuth.File); - xfree(Config.proxyAuth.File); - Config.proxyAuth.File = NULL; - return (dash_str); - } - last_time = squid_curtime; - } - hashr = hash_lookup(validated, sent_user); - if (hashr == NULL) { - /* User doesn't exist; deny them */ - debug(33, 4, "proxyAuthenticate: user %s doesn't exist\n", sent_user); - xfree(clear_userandpw); - return (dash_str); - } - passwd = strstr(clear_userandpw, ":"); - passwd++; - - /* See if we've already validated them */ - passwd[0] |= 0x80; /* check mutated password */ - if (strcmp(hashr->item, passwd) == 0) { - debug(33, 5, "proxyAuthenticate: user %s previously validated\n", sent_user); - xfree(clear_userandpw); - return sent_user; - } - passwd[0] &= ~(0x80); /* check vs crypt */ - if (strcmp(hashr->item, (char *) crypt(passwd, hashr->item))) { - /* Passwords differ, deny access */ - debug(33, 4, "proxyAuthenticate: authentication failed: user %s passwords differ\n", sent_user); - xfree(clear_userandpw); - return (dash_str); + /* no result */ + debug(33, 1, "clientAuthenticateDone: no result from authenticator!\n"); + icpState->aclChecklist->auth.checked = -1; } - passwd[0] |= 0x80; /* store mutated password away */ - debug(33, 5, "proxyAuthenticate: user %s validated\n", sent_user); - hash_delete(validated, sent_user); - hash_insert(validated, xstrdup(sent_user), (void *) xstrdup(passwd)); - - xfree(clear_userandpw); - return (sent_user); + clientAccessCheck(icpState, icpState->aclHandler); + return; } -#endif /* USE_PROXY_AUTH */ +#endif void icpProcessExpired(int fd, void *data) diff -urwN squid-1.1.20-orig/src/client_side.h squid-1.1.20/src/client_side.h --- squid-1.1.20-orig/src/client_side.h Tue Feb 4 00:03:06 1997 +++ squid-1.1.20/src/client_side.h Sat Feb 7 13:06:31 1998 @@ -38,8 +38,4 @@ extern char *clientConstructTraceEcho _PARAMS((icpStateData *)); extern void clientPurgeRequest _PARAMS((icpStateData *)); -#if USE_PROXY_AUTH -const char *proxyAuthenticate(const char *headers); -#endif /* USE_PROXY_AUTH */ - #endif /* CLIENT_SIDE_H */ diff -urwN squid-1.1.20-orig/src/comm.c squid-1.1.20/src/comm.c --- squid-1.1.20-orig/src/comm.c Mon Dec 1 08:09:41 1997 +++ squid-1.1.20/src/comm.c Sun Feb 8 23:26:31 1998 @@ -809,6 +809,9 @@ ftpServerClose(); dnsShutdownServers(); redirectShutdownServers(); +#ifdef USE_PROXY_AUTH + authenticateShutdownServers(); +#endif /* shutdown_pending will be set to * +1 for SIGTERM * -1 for SIGINT */ @@ -994,6 +997,9 @@ ftpServerClose(); dnsShutdownServers(); redirectShutdownServers(); +#ifdef USE_PROXY_AUTH + authenticateShutdownServers(); +#endif /* shutdown_pending will be set to * +1 for SIGTERM * -1 for SIGINT */ diff -urwN squid-1.1.20-orig/src/hash.c squid-1.1.20/src/hash.c --- squid-1.1.20-orig/src/hash.c Thu Nov 28 08:08:48 1996 +++ squid-1.1.20/src/hash.c Sat Feb 7 13:06:31 1998 @@ -369,6 +369,21 @@ } /* + * hash_destroy - destroy hash list + */ +void +hash_destroy(HashID hid) +{ + if (hid >= MAX_HTABLE) + return; + if (htbl[hid].valid == 0) + return; + safe_free(htbl[hid].buckets); + htbl[hid].valid = 0; + return; +} + +/* * hash_insert - inserts the given item 'item' under the given key 'k' * into the hash table 'hid'. Returns non-zero on error; otherwise, * returns 0 and inserts the item. diff -urwN squid-1.1.20-orig/src/hash.h squid-1.1.20/src/hash.h --- squid-1.1.20-orig/src/hash.h Thu Nov 7 00:14:38 1996 +++ squid-1.1.20/src/hash.h Sat Feb 7 13:06:31 1998 @@ -144,6 +144,7 @@ extern HashID hash_create _PARAMS((int (*)_PARAMS((const char *, const char *)), int, unsigned int (*)_PARAMS((const char *, HashID)))); +extern void hash_destroy _PARAMS((HashID)); /* insert/delete */ extern int hash_insert _PARAMS((HashID, const char *, void *)); diff -urwN squid-1.1.20-orig/src/http.c squid-1.1.20/src/http.c --- squid-1.1.20-orig/src/http.c Tue Oct 28 19:07:29 1997 +++ squid-1.1.20/src/http.c Sat Feb 7 13:06:31 1998 @@ -764,7 +764,7 @@ continue; #if USE_PROXY_AUTH if (strncasecmp(xbuf, "Proxy-authorization:", 20) == 0) - if (Config.proxyAuth.File) + /* if (Config.proxyAuth.File) */ continue; #endif if (strncasecmp(xbuf, "Connection:", 11) == 0) diff -urwN squid-1.1.20-orig/src/icp.h squid-1.1.20/src/icp.h --- squid-1.1.20-orig/src/icp.h Mon Dec 15 09:43:21 1997 +++ squid-1.1.20/src/icp.h Sun Feb 8 23:27:11 1998 @@ -196,11 +196,17 @@ struct { int fd; char ident[ICP_IDENT_SZ]; +#ifdef USE_PROXY_AUTH + char passwd[ICP_IDENT_SZ]; +#endif void (*callback) _PARAMS((void *)); int state; } ident; int ip_lookup_pending; int redirect_state; +#ifdef USE_PROXY_AUTH + int authenticate_state; +#endif } icpStateData; extern void *icpCreateMessage _PARAMS((icp_opcode opcode, diff -urwN squid-1.1.20-orig/src/main.c squid-1.1.20/src/main.c --- squid-1.1.20-orig/src/main.c Mon Dec 15 09:43:21 1997 +++ squid-1.1.20/src/main.c Sun Feb 8 23:28:13 1998 @@ -512,6 +512,9 @@ fqdncache_restart(); /* sigh, fqdncache too */ dnsOpenServers(); redirectOpenServers(); +#ifdef USE_PROXY_AUTH + authenticateOpenServers(); +#endif serverConnectionsOpen(); ftpInitialize(); if (theOutIcpConnection >= 0 && (!httpd_accel_mode || Config.Accel.withProxy)) @@ -569,6 +572,9 @@ fqdncache_init(); dnsOpenServers(); redirectOpenServers(); +#ifdef USE_PROXY_AUTH + authenticateOpenServers(); +#endif useragentOpenLog(); ftpInitialize(); diff -urwN squid-1.1.20-orig/src/ncsa_auth.c squid-1.1.20/src/ncsa_auth.c --- squid-1.1.20-orig/src/ncsa_auth.c Thu Jan 1 01:00:00 1970 +++ squid-1.1.20/src/ncsa_auth.c Sat Mar 7 18:17:53 1998 @@ -0,0 +1,114 @@ +/* + * 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. + * + */ + +#include "squid.h" + +#define CHECK_INTERVAL 10 + +static HashID hash = NULL; +static time_t last_time = 0; +static time_t change_time = 0; + +static void +read_passwd_file(const char *passwdfile) +{ + struct stat buf; + FILE *f; + int i; + hash_link *hashr; + static char *passwords = NULL; + char *user; + char *passwd; + + if (stat(passwdfile, &buf) == 0) { + if (buf.st_mtime != change_time) { + /* file has changed */ + change_time = buf.st_mtime; + if (hash != NULL) { + for (i = 0, hashr = hash_first(hash); hashr; hashr = hash_next(hash)) { + hash_delete(hash, hashr->key); + } + } else { + /* initial setup */ + if ((hash = hash_create(strcmp, 7921, hash_string)) < 0) { + fprintf(stderr, "ncsa_auth: cannot create hash table\n"); + exit(1); + } + } + passwords = xmalloc((size_t) buf.st_size + 2); + f = fopen(passwdfile, "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"); + while (user != NULL) { + if (strlen(user) > 0 && passwd && strlen(passwd) > 0) { + hash_insert(hash, xstrdup(user), (void *) xstrdup(passwd)); + } + user = strtok(NULL, ":"); + passwd = strtok(NULL, "\n"); + } + xfree(passwords); + } + } else { + fprintf(stderr, "ncsa_auth: cannot stat %s\n", passwdfile); + exit(1); + } +} + +/* this is only needed for hash.c */ +void +fatal_dump(const char *message) +{ + fprintf(stderr, "ncsa_auth: fatal_dump: %s\n", message); + exit(1); +} + +int +main(int argc, char **argv) +{ + char buf[256]; + time_t now = 0; + char *user, *passwd; + hash_link *hashr; + + if (argc != 2) { + fprintf(stderr, "Usage: ncsa_auth \n"); + exit(1); + } + read_passwd_file(argv[1]); + last_time = time(NULL); + + while (fgets(buf, 256, stdin) != NULL) { + now = time(NULL); + if ((now - last_time) >= CHECK_INTERVAL) { + last_time = now; + read_passwd_file(argv[1]); + } + user = strtok(buf, " "); + passwd = strtok(NULL, " \n"); + hashr = hash_lookup(hash, user); + if (hashr && + strcmp(hashr->item, (char *) crypt(passwd, hashr->item)) == 0) { + printf("OK\n"); + } else { + printf("ERR\n"); + } + fflush(stdout); + } + + exit(0); +} diff -urwN squid-1.1.20-orig/src/objcache.c squid-1.1.20/src/objcache.c --- squid-1.1.20-orig/src/objcache.c Wed Mar 26 08:29:52 1997 +++ squid-1.1.20/src/objcache.c Mon Feb 9 22:00:38 1998 @@ -163,6 +163,8 @@ op = MGR_DNSSERVERS; else if (!strcmp(buf, "stats/redirector")) op = MGR_REDIRECTORS; + else if (!strcmp(buf, "stats/authenticator")) + op = MGR_AUTHENTICATORS; else if (!strcmp(buf, "stats/io")) op = MGR_IO; else if (!strcmp(buf, "stats/reply_headers")) @@ -288,6 +290,9 @@ break; case MGR_REDIRECTORS: HTTPCacheInfo->stat_get(HTTPCacheInfo, "redirector", entry); + break; + case MGR_AUTHENTICATORS: + HTTPCacheInfo->stat_get(HTTPCacheInfo, "authenticator", entry); break; case MGR_IO: HTTPCacheInfo->stat_get(HTTPCacheInfo, "io", entry); diff -urwN squid-1.1.20-orig/src/objcache_opcodes.h squid-1.1.20/src/objcache_opcodes.h --- squid-1.1.20-orig/src/objcache_opcodes.h Fri Dec 13 23:26:38 1996 +++ squid-1.1.20/src/objcache_opcodes.h Mon Feb 9 22:00:38 1998 @@ -47,6 +47,7 @@ MGR_NETDB, MGR_OBJECTS, MGR_REDIRECTORS, + MGR_AUTHENTICATORS, MGR_REFRESH, MGR_REMOVE, MGR_REPLY_HDRS, @@ -77,6 +78,7 @@ "netdb", "objects", "redirectors", + "authenticators", "refresh", "remove", "reply_headers", diff -urwN squid-1.1.20-orig/src/squid.conf.pre.in squid-1.1.20/src/squid.conf.pre.in --- squid-1.1.20-orig/src/squid.conf.pre.in Wed Dec 31 22:36:59 1997 +++ squid-1.1.20/src/squid.conf.pre.in Sat Mar 7 16:37:21 1998 @@ -587,6 +587,27 @@ # #redirect_children 5 +# TAG: authenticate_program +# 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. You must +# define USE_PROXY_AUTH during compilation. +# +#authenticate_program /bin/false + +# TAG: authenticate_options +# Options for the authenticate program. +# +#authenticate_options + +# TAG: authenticate_children +# The number of authenticate programs to spawn. +# +#authenticate_children 5 + # OPTIONS FOR TUNING THE CACHE #----------------------------------------------------------------------------- @@ -778,6 +799,22 @@ # acl aclname user username ... # string match on ident output. # # use REQUIRED to accept any # # non-null ident. +# acl aclname proxy_auth [ timeout ] +# Use an EXTERNAL authenticate program to check username/password +# combinations (see authenticate_program). +# +# 'timeout' is the time a cached username/password entry remains +# cached (default = 3600 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. +# +# NOTE: proxy_auth support is not compiled into Squid by default. +# To use this feature you must enable the USE_PROXY_AUTH option +# near the top of src/Makefile. acl manager proto cache_object acl localhost src 127.0.0.1/255.255.255.255 @@ -997,23 +1034,6 @@ # passthrough_proxy host:port # #passthrough_proxy - -# TAG: proxy_auth -# Usage: proxy_auth passwd_file [ ignore-domain ] -# -# 'passwd_file' is an apache-style file of passwords for -# authenticated proxy access Looks like user:password, with the -# password being standard crypt() format. Proxy authentication -# is disabled by default. -# -# 'ignore-domain' is a domain name for which authorization will -# *not* be required. -# -# NOTE, proxy_auth support is not compiled into Squid by default. -# To use this feature you must enable the USE_PROXY_AUTH option -# near the top of src/Makefile. -# -#proxy_auth /dev/null # TAG: err_html_text # HTML text to include in error messages. Make this a "mailto" diff -urwN squid-1.1.20-orig/src/squid.h squid-1.1.20/src/squid.h --- squid-1.1.20-orig/src/squid.h Tue Oct 21 18:08:53 1997 +++ squid-1.1.20/src/squid.h Thu Feb 12 21:55:35 1998 @@ -302,6 +302,9 @@ #include "acl.h" #include "async_io.h" #include "redirect.h" +#ifdef USE_PROXY_AUTH +#include "authenticate.h" +#endif #include "client_side.h" #include "useragent.h" #include "icmp.h" diff -urwN squid-1.1.20-orig/src/stat.c squid-1.1.20/src/stat.c --- squid-1.1.20-orig/src/stat.c Thu Aug 21 21:29:01 1997 +++ squid-1.1.20/src/stat.c Mon Feb 9 22:32:21 1998 @@ -423,6 +423,10 @@ dnsStats(sentry); } else if (strcmp(req, "redirector") == 0) { redirectStats(sentry); +#ifdef USE_PROXY_AUTH + } else if (strcmp(req, "authenticator") == 0) { + authenticateStats(sentry); +#endif } else if (strcmp(req, "utilization") == 0) { stat_utilization_get(HTTPCacheInfo, sentry, "HTTP"); stat_utilization_get(ICPCacheInfo, sentry, "ICP");