--------------------- PatchSet 7079 Date: 2005/10/08 09:24:12 Author: serassio Branch: negotiate-nt-2_5 Tag: (none) Log: Imported negotiate-2_5.patch Members: src/auth/negotiate/.cvsignore:1.1->1.1.2.1 src/auth/negotiate/Makefile.am:1.1.6.1->1.1.6.2 src/auth/negotiate/auth_negotiate.c:1.1.6.1->1.1.6.2 src/auth/negotiate/auth_negotiate.h:1.1.6.1->1.1.6.2 --- /dev/null Wed Feb 14 01:13:16 2007 +++ squid/src/auth/negotiate/.cvsignore Wed Feb 14 01:13:26 2007 @@ -0,0 +1,2 @@ +.cvsignore +Makefile.in --- /dev/null Wed Feb 14 01:13:16 2007 +++ squid/src/auth/negotiate/Makefile.am Wed Feb 14 01:13:26 2007 @@ -0,0 +1 @@ +SUBDIRS = --- /dev/null Wed Feb 14 01:13:16 2007 +++ squid/src/auth/negotiate/auth_negotiate.c Wed Feb 14 01:13:26 2007 @@ -0,0 +1,758 @@ + +/* + * $Id: auth_negotiate.c,v 1.1.6.2 2005/10/08 09:24:12 serassio Exp $ + * + * DEBUG: section 29 Negotiate Authenticator + * AUTHOR: Robert Collins + * + * SQUID Web Proxy Cache http://www.squid-cache.org/ + * ---------------------------------------------------------- + * + * Squid is the result of efforts by numerous individuals from + * the Internet community; see the CONTRIBUTORS file for full + * details. Many organizations have provided support for Squid's + * development; see the SPONSORS file for full details. Squid is + * Copyrighted (C) 2001 by the Regents of the University of + * California; see the COPYRIGHT file for full details. Squid + * incorporates software developed and/or copyrighted by other + * sources; see the CREDITS file for full details. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA. + * + */ + +/* The functions in this file handle authentication. + * They DO NOT perform access control or auditing. + * See acl.c for access control and client_side.c for auditing */ + + +#include "squid.h" +#include "auth_negotiate.h" + +extern AUTHSSETUP authSchemeSetup_negotiate; + +static void +authenticateStateFree(authenticateStateData * r) +{ + authenticateAuthUserRequestUnlock(r->auth_user_request); + r->auth_user_request = NULL; + cbdataFree(r); +} + +/* Negotiate Scheme */ +static HLPSCB authenticateNegotiateHandleReply; +static AUTHSACTIVE authenticateNegotiateActive; +static AUTHSAUTHED authNegotiateAuthenticated; +static AUTHSAUTHUSER authenticateNegotiateAuthenticateUser; +static AUTHSCONFIGURED authNegotiateConfigured; +static AUTHSFIXERR authenticateNegotiateFixErrorHeader; +static AUTHSADDHEADER authNegotiateAddHeader; +static AUTHSFREE authenticateNegotiateFreeUser; +static AUTHSDIRECTION authenticateNegotiateDirection; +static AUTHSDECODE authenticateDecodeNegotiateAuth; +static AUTHSDUMP authNegotiateCfgDump; +static AUTHSFREECONFIG authNegotiateFreeConfig; +static AUTHSINIT authNegotiateInit; +static AUTHSONCLOSEC authenticateNegotiateOnCloseConnection; +static AUTHSUSERNAME authenticateNegotiateUsername; +static AUTHSREQFREE authNegotiateAURequestFree; +static AUTHSPARSE authNegotiateParse; +static AUTHSSTART authenticateNegotiateStart; +static AUTHSSTATS authenticateNegotiateStats; +static AUTHSSHUTDOWN authNegotiateDone; + +static statefulhelper *negotiateauthenticators = NULL; + +CBDATA_TYPE(authenticateStateData); + +static int authnegotiate_initialised = 0; + +static MemPool *negotiate_user_pool = NULL; +static MemPool *negotiate_request_pool = NULL; +static auth_negotiate_config *negotiateConfig = NULL; + +static void authenticateNegotiateReleaseServer(negotiate_request_t * negotiate_request); +/* + * + * Private Functions + * + */ + +static void +authNegotiateDone(void) +{ + debug(29, 2) ("authNegotiateDone: shutting down Negotiate authentication.\n"); + if (negotiateauthenticators) + helperStatefulShutdown(negotiateauthenticators); + authnegotiate_initialised = 0; + if (!shutting_down) + return; + if (negotiateauthenticators) + helperStatefulFree(negotiateauthenticators); + negotiateauthenticators = NULL; + if (negotiate_request_pool) { + assert(memPoolInUseCount(negotiate_request_pool) == 0); + memPoolDestroy(negotiate_request_pool); + negotiate_request_pool = NULL; + } + if (negotiate_user_pool) { + assert(memPoolInUseCount(negotiate_user_pool) == 0); + memPoolDestroy(negotiate_user_pool); + negotiate_user_pool = NULL; + } + debug(29, 2) ("authNegotiateDone: Negotiate authentication Shutdown.\n"); +} + +/* free any allocated configuration details */ +static void +authNegotiateFreeConfig(authScheme * scheme) +{ + if (negotiateConfig == NULL) + return; + assert(negotiateConfig == scheme->scheme_data); + if (negotiateConfig->authenticate) + wordlistDestroy(&negotiateConfig->authenticate); + safe_free(negotiateConfig); + scheme->scheme_data = NULL; +} + +static void +authNegotiateCfgDump(StoreEntry * entry, const char *name, authScheme * scheme) +{ + auth_negotiate_config *config = scheme->scheme_data; + wordlist *list = config->authenticate; + storeAppendPrintf(entry, "%s %s program", name, "negotiate"); + while (list != NULL) { + storeAppendPrintf(entry, " %s", list->key); + list = list->next; + } + storeAppendPrintf(entry, "\n"); + storeAppendPrintf(entry, "%s %s children %d\n", name, "negotiate", config->authenticateChildren); + storeAppendPrintf(entry, "%s %s keep_alive %d\n", name, "negotiate", config->keep_alive); +} + +static void +authNegotiateParse(authScheme * scheme, int n_configured, char *param_str) +{ + if (scheme->scheme_data == NULL) { + assert(negotiateConfig == NULL); + /* this is the first param to be found */ + scheme->scheme_data = xmalloc(sizeof(auth_negotiate_config)); + memset(scheme->scheme_data, 0, sizeof(auth_negotiate_config)); + negotiateConfig = scheme->scheme_data; + negotiateConfig->authenticateChildren = 5; + negotiateConfig->keep_alive = 1; + } + negotiateConfig = scheme->scheme_data; + if (strcasecmp(param_str, "program") == 0) { + if (negotiateConfig->authenticate) + wordlistDestroy(&negotiateConfig->authenticate); + parse_wordlist(&negotiateConfig->authenticate); + requirePathnameExists("authparam negotiate program", negotiateConfig->authenticate->key); + } else if (strcasecmp(param_str, "children") == 0) { + parse_int(&negotiateConfig->authenticateChildren); + } else if (strcasecmp(param_str, "keep_alive") == 0) { + parse_onoff(&negotiateConfig->keep_alive); + } else { + debug(28, 0) ("unrecognised negotiate auth scheme parameter '%s'\n", param_str); + } +} + + +void +authSchemeSetup_negotiate(authscheme_entry_t * authscheme) +{ + assert(!authnegotiate_initialised); + authscheme->Active = authenticateNegotiateActive; + authscheme->configured = authNegotiateConfigured; + authscheme->parse = authNegotiateParse; + authscheme->dump = authNegotiateCfgDump; + authscheme->requestFree = authNegotiateAURequestFree; + authscheme->freeconfig = authNegotiateFreeConfig; + authscheme->init = authNegotiateInit; + authscheme->authAuthenticate = authenticateNegotiateAuthenticateUser; + authscheme->authenticated = authNegotiateAuthenticated; + authscheme->authFixHeader = authenticateNegotiateFixErrorHeader; + authscheme->AddHeader = authNegotiateAddHeader; + authscheme->FreeUser = authenticateNegotiateFreeUser; + authscheme->authStart = authenticateNegotiateStart; + authscheme->authStats = authenticateNegotiateStats; + authscheme->authUserUsername = authenticateNegotiateUsername; + authscheme->getdirection = authenticateNegotiateDirection; + authscheme->decodeauth = authenticateDecodeNegotiateAuth; + authscheme->donefunc = authNegotiateDone; + authscheme->oncloseconnection = authenticateNegotiateOnCloseConnection; +} + +/* Initialize helpers and the like for this auth scheme. Called AFTER parsing the + * config file */ +static void +authNegotiateInit(authScheme * scheme) +{ + static int negotiateinit = 0; + if (negotiateConfig->authenticate) { + /* + * disable client side request pipelining. There is a race with + * Negotiate when the client sends a second request on an Negotiate + * connection before the authenticate challenge is sent. With + * this patch, the client may fail to authenticate, but squid's + * state will be preserved. Caveats: this should be a post-parse + * test, but that can wait for the modular parser to be integrated. + */ + if (negotiateConfig->authenticate && Config.onoff.pipeline_prefetch != 0) { + debug(28, 1) ("pipeline prefetching incompatile with Negotiate authentication. Disabling pipeline_prefetch\n"); + Config.onoff.pipeline_prefetch = 0; + } + if (!negotiate_user_pool) + negotiate_user_pool = memPoolCreate("Negotiate Scheme User Data", sizeof(negotiate_user_t)); + if (!negotiate_request_pool) + negotiate_request_pool = memPoolCreate("Negotiate Scheme Request Data", sizeof(negotiate_request_t)); + authnegotiate_initialised = 1; + if (negotiateauthenticators == NULL) + negotiateauthenticators = helperStatefulCreate("negotiateauthenticator"); + negotiateauthenticators->cmdline = negotiateConfig->authenticate; + negotiateauthenticators->n_to_start = negotiateConfig->authenticateChildren; + negotiateauthenticators->ipc_type = IPC_TCP_SOCKET; + helperStatefulOpenServers(negotiateauthenticators); + if (!negotiateinit) { + cachemgrRegister("negotiateauthenticator", + "Negotiate User Authenticator Stats", + authenticateNegotiateStats, 0, 1); + negotiateinit++; + } + CBDATA_INIT_TYPE(authenticateStateData); + } +} + +static int +authenticateNegotiateActive() +{ + return (authnegotiate_initialised == 1) ? 1 : 0; +} + + +static int +authNegotiateConfigured() +{ + if (negotiateConfig == NULL) { + debug(29, 9) ("authNegotiateConfigured: not configured\n"); + return 0; + } + if (negotiateConfig->authenticate == NULL) { + debug(29, 9) ("authNegotiateConfigured: no helper\n"); + return 0; + } + if (negotiateConfig->authenticateChildren == 0) { + debug(29, 9) ("authNegotiateConfigured: no helper children\n"); + return 0; + } + debug(29, 9) ("authNegotiateConfigured: returning configured\n"); + return 1; +} + +/* Negotiate Scheme */ + +static int +authenticateNegotiateDirection(auth_user_request_t * auth_user_request) +{ + negotiate_request_t *negotiate_request = auth_user_request->scheme_data; + /* null auth_user is checked for by authenticateDirection */ + if (negotiate_request->waiting || negotiate_request->client_blob) + return -1; /* Need helper response to continue */ + switch (negotiate_request->auth_state) { + case AUTHENTICATE_STATE_NONE: /* no progress at all. */ + debug(29, 1) ("authenticateNegotiateDirection: called before Negotiate Authenticate!. Report a bug to squid-dev. au %p\n", auth_user_request); + return -2; + case AUTHENTICATE_STATE_NEGOTIATE: /* send to client */ + assert(negotiate_request->server_blob); + return 1; + case AUTHENTICATE_STATE_FAILED: + return -2; + case AUTHENTICATE_STATE_FINISHED: /* do nothing.. */ + case AUTHENTICATE_STATE_DONE: /* do nothing.. */ + return 0; + case AUTHENTICATE_STATE_INITIAL: + debug(29, 1) ("authenticateNegotiateDirection: Unexpected AUTHENTICATE_STATE_INITIAL\n"); + return -2; + } + return -2; +} + +/* + * Send the authenticate error header(s). Note: IE has a bug and the Negotiate header + * must be first. To ensure that, the configure use --enable-auth=negotiate, anything + * else. + */ +static void +authenticateNegotiateFixErrorHeader(auth_user_request_t * auth_user_request, HttpReply * rep, http_hdr_type type, request_t * request) +{ + negotiate_request_t *negotiate_request; + if (!request->flags.proxy_keepalive) + return; + if (!negotiateConfig->authenticate) + return; + /* New request, no user details */ + if (auth_user_request == NULL) { + debug(29, 9) ("authenticateNegotiateFixErrorHeader: Sending type:%d header: 'Negotiate'\n", type); + httpHeaderPutStrf(&rep->header, type, "Negotiate"); + if (!negotiateConfig->keep_alive) { + /* drop the connection */ + httpHeaderDelByName(&rep->header, "keep-alive"); + request->flags.proxy_keepalive = 0; + } + return; + } + negotiate_request = auth_user_request->scheme_data; + switch (negotiate_request->auth_state) { + case AUTHENTICATE_STATE_NONE: + case AUTHENTICATE_STATE_FAILED: + debug(29, 9) ("authenticateNegotiateFixErrorHeader: Sending type:%d header: 'Negotiate'\n", type); + httpHeaderPutStrf(&rep->header, type, "Negotiate"); + /* drop the connection */ + httpHeaderDelByName(&rep->header, "keep-alive"); + request->flags.proxy_keepalive = 0; + break; + case AUTHENTICATE_STATE_NEGOTIATE: + /* we are 'waiting' for a response from the client */ + /* pass the blob to the client */ + debug(29, 9) ("authenticateNegotiateFixErrorHeader: Sending type:%d header: 'Negotiate %s'\n", type, negotiate_request->server_blob); + httpHeaderPutStrf(&rep->header, type, "Negotiate %s", negotiate_request->server_blob); + safe_free(negotiate_request->server_blob); + request->flags.must_keepalive = 1; + break; + case AUTHENTICATE_STATE_FINISHED: + /* Special case when authentication finished, but not allowed by ACL */ + if (negotiate_request->server_blob) { + debug(29, 9) ("authenticateNegotiateFixErrorHeader: Sending type:%d header: 'Negotiate %s'\n", type, negotiate_request->server_blob); + httpHeaderPutStrf(&rep->header, type, "Negotiate %s", negotiate_request->server_blob); + safe_free(negotiate_request->server_blob); + } else { + debug(29, 9) ("authenticateNegotiateFixErrorHeader: Connection authenticated\n"); + } + break; + default: + debug(29, 0) ("authenticateNegotiateFixErrorHeader: state %d.\n", negotiate_request->auth_state); + fatal("unexpected state in AuthenticateNegotiateFixErrorHeader.\n"); + } +} + +/* add the [proxy]authorisation header */ +static void +authNegotiateAddHeader(auth_user_request_t * auth_user_request, HttpReply * rep, int accel) +{ + int type; + negotiate_request_t *negotiate_request; + if (!auth_user_request) + return; + negotiate_request = auth_user_request->scheme_data; + if (!negotiate_request->server_blob) + return; + + type = accel ? HDR_WWW_AUTHENTICATE : HDR_PROXY_AUTHENTICATE; + + debug(29, 9) ("authenticateNegotiateFixErrorHeader: Sending type:%d header: 'Negotiate %s'\n", type, negotiate_request->server_blob); + httpHeaderPutStrf(&rep->header, type, "Negotiate %s", negotiate_request->server_blob); + safe_free(negotiate_request->server_blob); +} + +static void +authNegotiateRequestFree(negotiate_request_t * negotiate_request) +{ + if (!negotiate_request) + return; + safe_free(negotiate_request->server_blob); + safe_free(negotiate_request->client_blob); + if (negotiate_request->authserver != NULL) { + debug(29, 9) ("authenticateNegotiateRequestFree: releasing server '%p'\n", negotiate_request->authserver); + authenticateNegotiateReleaseServer(negotiate_request); + } + memPoolFree(negotiate_request_pool, negotiate_request); +} + +static void +authNegotiateAURequestFree(auth_user_request_t * auth_user_request) +{ + if (auth_user_request->scheme_data) + authNegotiateRequestFree((negotiate_request_t *) auth_user_request->scheme_data); + auth_user_request->scheme_data = NULL; +} + +static void +authenticateNegotiateFreeUser(auth_user_t * auth_user) +{ + negotiate_user_t *negotiate_user = auth_user->scheme_data; + + debug(29, 5) ("authenticateNegotiateFreeUser: Clearing Negotiate scheme data\n"); + safe_free(negotiate_user->username); + memPoolFree(negotiate_user_pool, negotiate_user); + auth_user->scheme_data = NULL; +} + +/* clear the Negotiate helper of being reserved for future requests */ +static void +authenticateNegotiateReleaseServer(negotiate_request_t * negotiate_request) +{ + helper_stateful_server *server = negotiate_request->authserver; + if (!server) + return; + debug(29, 9) ("authenticateNegotiateReleaseServer: releasing server '%p'\n", server); + negotiate_request->authserver = NULL; + helperStatefulReleaseServer(server); +} + +static void +authenticateNegotiateHandleReply(void *data, void *srv, char *reply) +{ + authenticateStateData *r = data; + int valid; + auth_user_request_t *auth_user_request; + auth_user_t *auth_user; + negotiate_user_t *negotiate_user; + negotiate_request_t *negotiate_request; + char *blob, *arg = NULL; + debug(29, 9) ("authenticateNegotiateHandleReply: Helper: '%p' {%s}\n", srv, reply ? reply : ""); + valid = cbdataValid(r->data); + if (!valid) { + debug(29, 2) ("AuthenticateNegotiateHandleReply: invalid callback data. Releasing helper '%p'.\n", srv); + cbdataUnlock(r->data); + authenticateStateFree(r); + return; + } + if (!reply) { + /* + * TODO: this occurs when a helper crashes. We should clean + * up that helpers resources and queued requests. + */ + fatal("authenticateNegotiateHandleReply: called with no result string\n"); + } + + auth_user_request = r->auth_user_request; + + negotiate_request = auth_user_request->scheme_data; + assert(negotiate_request != NULL); + + assert(negotiate_request->waiting); + negotiate_request->waiting = 0; + safe_free(negotiate_request->client_blob); + + auth_user = auth_user_request->auth_user; + assert(auth_user != NULL); + assert(auth_user->auth_type == AUTH_NEGOTIATE); + negotiate_user = auth_user_request->auth_user->scheme_data; + + if (negotiate_request->authserver == NULL) + negotiate_request->authserver = srv; + else + assert(negotiate_request->authserver == srv); + + blob = strchr(reply, ' '); + if (blob) { + blob++; + arg = strchr(blob + 1, ' '); + } + + /* seperate out the useful data */ + if (strncasecmp(reply, "TT ", 3) == 0 && blob != NULL) { + /* we have been given a blob to send to the client */ + if (arg) + *arg++ = '\0'; + safe_free(negotiate_request->server_blob); + negotiate_request->server_blob = xstrdup(blob); + negotiate_request->auth_state = AUTHENTICATE_STATE_NEGOTIATE; + safe_free(auth_user_request->message); + auth_user_request->message = xstrdup("Authenication in progress"); + debug(29, 4) ("authenticateNegotiateHandleReply: Need to challenge the client with a server blob '%s'\n", blob); + } else if (strncasecmp(reply, "AF ", 3) == 0 && arg != NULL) { + /* we're finished, release the helper */ + if (arg) + *arg++ = '\0'; + safe_free(negotiate_user->username); + negotiate_user->username = xstrdup(arg); + safe_free(auth_user_request->message); + auth_user_request->message = xstrdup("Login successful"); + safe_free(negotiate_request->server_blob); + negotiate_request->server_blob = xstrdup(blob); + debug(29, 4) ("authenticateNegotiateHandleReply: Successfully validated user via Negotiate. Username '%s'\n", arg); + authenticateNegotiateReleaseServer(negotiate_request); + negotiate_request->auth_state = AUTHENTICATE_STATE_FINISHED; + } else if (strncasecmp(reply, "NA ", 3) == 0 && arg != NULL) { + if (arg) + *arg++ = '\0'; + safe_free(auth_user_request->message); + auth_user_request->message = xstrdup(arg); + negotiate_request->auth_state = AUTHENTICATE_STATE_FAILED; + safe_free(negotiate_request->server_blob); + negotiate_request->server_blob = xstrdup(blob); + authenticateNegotiateReleaseServer(negotiate_request); + debug(29, 4) ("authenticateNegotiateHandleReply: Failed validating user via Negotiate. Error returned '%s'\n", arg); + } else if (strncasecmp(reply, "BH ", 3) == 0) { + /* TODO kick off a refresh process. This can occur after a YR or after + * a KK. If after a YR release the helper and resubmit the request via + * Authenticate Negotiate start. + * If after a KK deny the user's request w/ 407 and mark the helper as + * Needing YR. */ + auth_user_request->message = xstrdup(reply); + negotiate_request->auth_state = AUTHENTICATE_STATE_FAILED; + safe_free(negotiate_request->server_blob); + authenticateNegotiateReleaseServer(negotiate_request); + debug(29, 1) ("authenticateNegotiateHandleReply: Error validating user via Negotiate. Error returned '%s'\n", reply); + } else { + fatalf("authenticateNegotiateHandleReply: *** Unsupported helper response ***, '%s'\n", reply); + } + r->handler(r->data, NULL); + cbdataUnlock(r->data); + authenticateStateFree(r); +} + +static void +authenticateNegotiateStats(StoreEntry * sentry) +{ + storeAppendPrintf(sentry, "Negotiate Authenticator Statistics:\n"); + helperStatefulStats(sentry, negotiateauthenticators); +} + +/* send the initial data to a stateful negotiate authenticator module */ +static void +authenticateNegotiateStart(auth_user_request_t * auth_user_request, RH * handler, void *data) +{ + authenticateStateData *r = NULL; + char buf[8192]; + char *sent_string = NULL; + negotiate_user_t *negotiate_user; + negotiate_request_t *negotiate_request; + auth_user_t *auth_user; + + assert(auth_user_request); + auth_user = auth_user_request->auth_user; + negotiate_user = auth_user->scheme_data; + negotiate_request = auth_user_request->scheme_data; + assert(negotiate_user); + assert(negotiate_request); + assert(handler); + assert(data); + assert(auth_user->auth_type == AUTH_NEGOTIATE); + debug(29, 9) ("authenticateNegotiateStart: auth state '%d'\n", negotiate_request->auth_state); + sent_string = negotiate_request->client_blob; + + debug(29, 9) ("authenticateNegotiateStart: state '%d'\n", negotiate_request->auth_state); + debug(29, 9) ("authenticateNegotiateStart: '%s'\n", sent_string); + if (negotiateConfig->authenticate == NULL) { + debug(29, 0) ("authenticateNegotiateStart: no Negotiate program specified:'%s'\n", sent_string); + handler(data, NULL); + return; + } + + /* Send blob to helper */ + r = cbdataAlloc(authenticateStateData); + r->handler = handler; + cbdataLock(data); + r->data = data; + r->auth_user_request = auth_user_request; + authenticateAuthUserRequestLock(r->auth_user_request); + if (negotiate_request->auth_state == AUTHENTICATE_STATE_INITIAL) + snprintf(buf, 8192, "YR %s\n", sent_string); + else + snprintf(buf, 8192, "KK %s\n", sent_string); + negotiate_request->waiting = 1; + safe_free(negotiate_request->client_blob); + helperStatefulSubmit(negotiateauthenticators, buf, authenticateNegotiateHandleReply, r, negotiate_request->authserver); +} + +/* clear any connection related authentication details */ +static void +authenticateNegotiateOnCloseConnection(ConnStateData * conn) +{ + negotiate_request_t *negotiate_request; + assert(conn != NULL); + if (conn->auth_user_request != NULL) { + assert(conn->auth_user_request->scheme_data != NULL); + negotiate_request = conn->auth_user_request->scheme_data; + assert(negotiate_request->conn == conn); + if (negotiate_request->authserver != NULL) + authenticateNegotiateReleaseServer(negotiate_request); + /* unlock the connection based lock */ + debug(29, 9) ("authenticateNegotiateOnCloseConnection: Unlocking auth_user from the connection.\n"); + /* minor abstraction break here: FIXME */ + /* Ensure that the auth user request will be getting closed */ + /* IFF we start persisting the struct after the conn closes - say for logging + * then this test may become invalid + */ + assert(conn->auth_user_request->references == 1); + authenticateAuthUserRequestUnlock(conn->auth_user_request); + conn->auth_user_request = NULL; + } +} + +/* authenticateUserUsername: return a pointer to the username in the */ +static char * +authenticateNegotiateUsername(auth_user_t * auth_user) +{ + negotiate_user_t *negotiate_user = auth_user->scheme_data; + if (negotiate_user) + return negotiate_user->username; + return NULL; +} + +/* + * Called on the initial request only, to set things up for later processing + */ + +static void +authenticateDecodeNegotiateAuth(auth_user_request_t * auth_user_request, const char *proxy_auth) +{ + dlink_node *node; + assert(auth_user_request->auth_user == NULL); + auth_user_request->auth_user = authenticateAuthUserNew("negotiate"); + auth_user_request->auth_user->auth_type = AUTH_NEGOTIATE; + auth_user_request->auth_user->scheme_data = memPoolAlloc(negotiate_user_pool); + auth_user_request->scheme_data = memPoolAlloc(negotiate_request_pool); + memset(auth_user_request->scheme_data, '\0', sizeof(negotiate_request_t)); + /* lock for the auth_user_request link */ + authenticateAuthUserLock(auth_user_request->auth_user); + node = dlinkNodeNew(); + dlinkAdd(auth_user_request, node, &auth_user_request->auth_user->requests); + + /* the helper does the rest, with data collected in + * authenticateNegotiateAuthenticateUser */ + debug(29, 9) ("authenticateDecodeNegotiateAuth: Negotiate authentication\n"); + return; +} + +static int +authenticateNegotiatecmpUsername(negotiate_user_t * u1, negotiate_user_t * u2) +{ + return strcmp(u1->username, u2->username); +} + + +static int +authNegotiateAuthenticated(auth_user_request_t * auth_user_request) +{ + negotiate_request_t *negotiate_request = auth_user_request->scheme_data; + if (negotiate_request->auth_state == AUTHENTICATE_STATE_FINISHED) + return 1; + debug(29, 9) ("User not fully authenticated.\n"); + return 0; +} + +static void +authenticateNegotiateAuthenticateUser(auth_user_request_t * auth_user_request, request_t * request, ConnStateData * conn, http_hdr_type type) +{ + const char *proxy_auth, *blob; + auth_user_hash_pointer *usernamehash; + auth_user_t *auth_user; + negotiate_request_t *negotiate_request; + negotiate_user_t *negotiate_user; + + auth_user = auth_user_request->auth_user; + assert(auth_user); + assert(auth_user->auth_type == AUTH_NEGOTIATE); + assert(auth_user->scheme_data != NULL); + assert(auth_user_request->scheme_data != NULL); + negotiate_user = auth_user->scheme_data; + negotiate_request = auth_user_request->scheme_data; + /* Check that we are in the client side, where we can generate + * auth challenges */ + if (!conn) { + negotiate_request->auth_state = AUTHENTICATE_STATE_FAILED; + debug(29, 1) ("authenticateNegotiateAuthenticateUser: attempt to perform authentication without a connection!\n"); + return; + } + if (negotiate_request->waiting) { + debug(29, 1) ("authenticateNegotiateAuthenticateUser: waiting for helper reply!\n"); + return; + } + if (negotiate_request->server_blob) { + debug(29, 2) ("authenticateNegotiateAuthenticateUser: need to challenge client '%s'!\n", negotiate_request->server_blob); + return; + } + + /* get header */ + proxy_auth = httpHeaderGetStr(&request->header, type); + if (strncasecmp("Negotiate ", proxy_auth, 10) != 0) { + fatal("Incorrect scheme in auth header\n"); + /* TODO: more fault tolerance.. reset the auth scheme here */ + } + blob = proxy_auth + 10; + while (xisspace(*blob)) /*trim leading spaces */ + blob++; + + switch (negotiate_request->auth_state) { + case AUTHENTICATE_STATE_NONE: + /* we've recieved a negotiate request. pass to a helper */ + debug(29, 9) ("authenticateNegotiateAuthenticateUser: auth state negotiate none. %s\n", proxy_auth); + negotiate_request->auth_state = AUTHENTICATE_STATE_INITIAL; + safe_free(negotiate_request->client_blob); + negotiate_request->client_blob = xstrdup(blob); + conn->auth_type = AUTH_NEGOTIATE; + conn->auth_user_request = auth_user_request; + negotiate_request->conn = conn; + /* and lock for the connection duration */ + debug(29, 9) ("authenticateNegotiateAuthenticateUser: Locking auth_user from the connection.\n"); + authenticateAuthUserRequestLock(auth_user_request); + return; + break; + case AUTHENTICATE_STATE_INITIAL: + debug(29, 1) ("authenticateNegotiateAuthenticateUser: need to ask helper!\n"); + return; + break; + case AUTHENTICATE_STATE_NEGOTIATE: + /* we should have recieved a blob from the clien. pass it to the same + * helper process */ + debug(29, 9) ("authenticateNegotiateAuthenticateUser: auth state challenge with header %s.\n", proxy_auth); + /* do a cache lookup here. If it matches it's a successful negotiate + * challenge - release the helper and use the existing auth_user + * details. */ + safe_free(negotiate_request->client_blob); + negotiate_request->client_blob = xstrdup(blob); + return; + break; + case AUTHENTICATE_STATE_FINISHED: + /* this connection is authenticated */ + debug(29, 4) ("authenticated user %s\n", negotiate_user->username); + /* see if this is an existing user with a different proxy_auth + * string */ + usernamehash = hash_lookup(proxy_auth_username_cache, negotiate_user->username); + if (usernamehash) { + while (usernamehash && (usernamehash->auth_user->auth_type != auth_user->auth_type || authenticateNegotiatecmpUsername(usernamehash->auth_user->scheme_data, negotiate_user) != 0)) + usernamehash = usernamehash->next; + } + if (usernamehash) { + /* we can't seamlessly recheck the username due to the + * challenge nature of the protocol. Just free the + * temporary auth_user */ + authenticateAuthUserMerge(auth_user, usernamehash->auth_user); + auth_user = usernamehash->auth_user; + auth_user_request->auth_user = auth_user; + } else { + /* store user in hash's */ + authenticateUserNameCacheAdd(auth_user); + } + /* set these to now because this is either a new login from an + * existing user or a new user */ + auth_user->expiretime = current_time.tv_sec; + authenticateNegotiateReleaseServer(negotiate_request); + negotiate_request->auth_state = AUTHENTICATE_STATE_DONE; + return; + case AUTHENTICATE_STATE_DONE: + fatal("authenticateNegotiateAuthenticateUser: unexpect auth state DONE! Report a bug to the squid developers.\n"); + break; + case AUTHENTICATE_STATE_FAILED: + /* we've failed somewhere in authentication */ + debug(29, 9) ("authenticateNegotiateAuthenticateUser: auth state negotiate failed. %s\n", proxy_auth); + return; + } + return; +} --- /dev/null Wed Feb 14 01:13:16 2007 +++ squid/src/auth/negotiate/auth_negotiate.h Wed Feb 14 01:13:26 2007 @@ -0,0 +1,58 @@ +/* + * auth_negotiate.h + * Internal declarations for the negotiate auth module + */ + +#ifndef __AUTH_Negotiate_H__ +#define __AUTH_Negotiate_H__ + +#define DefaultAuthenticateChildrenMax 32 /* 32 processes */ + +typedef enum { + AUTHENTICATE_STATE_NONE, + AUTHENTICATE_STATE_INITIAL, + AUTHENTICATE_STATE_NEGOTIATE, + AUTHENTICATE_STATE_FINISHED, + AUTHENTICATE_STATE_DONE, + AUTHENTICATE_STATE_FAILED +} auth_state_t; /* connection level auth state */ + +/* Generic */ +typedef struct { + void *data; + auth_user_request_t *auth_user_request; + RH *handler; +} authenticateStateData; + +struct _negotiate_user { + /* what username did this connection get? */ + char *username; +}; + +struct _negotiate_request { + /*we need to store the helper server between requests */ + helper_stateful_server *authserver; + /* how far through the authentication process are we? */ + auth_state_t auth_state; + /* currently waiting for helper response */ + int waiting; + /* what connection is this associated with */ + ConnStateData *conn; + /* our current blob to pass to the client */ + char *server_blob; + /* our current blob to pass to the server */ + char *client_blob; +}; + +/* configuration runtime data */ +struct _auth_negotiate_config { + int authenticateChildren; + int keep_alive; + wordlist *authenticate; +}; + +typedef struct _negotiate_user negotiate_user_t; +typedef struct _negotiate_request negotiate_request_t; +typedef struct _auth_negotiate_config auth_negotiate_config; + +#endif