4 * DEBUG: section 29 Negotiate Authenticator
5 * AUTHOR: Robert Collins, Henrik Nordstrom, Francesco Chemolli
7 * SQUID Web Proxy Cache http://www.squid-cache.org/
8 * ----------------------------------------------------------
10 * Squid is the result of efforts by numerous individuals from
11 * the Internet community; see the CONTRIBUTORS file for full
12 * details. Many organizations have provided support for Squid's
13 * development; see the SPONSORS file for full details. Squid is
14 * Copyrighted (C) 2001 by the Regents of the University of
15 * California; see the COPYRIGHT file for full details. Squid
16 * incorporates software developed and/or copyrighted by other
17 * sources; see the CREDITS file for full details.
19 * This program is free software; you can redistribute it and/or modify
20 * it under the terms of the GNU General Public License as published by
21 * the Free Software Foundation; either version 2 of the License, or
22 * (at your option) any later version.
24 * This program is distributed in the hope that it will be useful,
25 * but WITHOUT ANY WARRANTY; without even the implied warranty of
26 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
27 * GNU General Public License for more details.
29 * You should have received a copy of the GNU General Public License
30 * along with this program; if not, write to the Free Software
31 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
35 /* The functions in this file handle authentication.
36 * They DO NOT perform access control or auditing.
37 * See acl.c for access control and client_side.c for auditing */
41 #include "auth/negotiate/auth_negotiate.h"
42 #include "auth/Gadgets.h"
43 #include "auth/State.h"
44 #include "CacheManager.h"
46 #include "client_side.h"
47 #include "HttpReply.h"
48 #include "HttpRequest.h"
49 #include "SquidTime.h"
50 /** \todo remove this include */
51 #include "auth/negotiate/negotiateScheme.h"
52 #include "auth/negotiate/negotiateUserRequest.h"
56 \defgroup AuthNegotiateInternal Negotiate Authenticator Internals
57 \ingroup AuthNegotiateAPI
60 /* Negotiate Scheme */
61 static AUTHSSTATS authenticateNegotiateStats
;
63 /// \ingroup AuthNegotiateInternal
64 statefulhelper
*negotiateauthenticators
= NULL
;
66 /// \ingroup AuthNegotiateInternal
67 static int authnegotiate_initialised
= 0;
69 /// \ingroup AuthNegotiateInternal
70 AuthNegotiateConfig negotiateConfig
;
72 /// \ingroup AuthNegotiateInternal
73 static hash_table
*proxy_auth_cache
= NULL
;
82 AuthNegotiateConfig::rotateHelpers()
84 /* schedule closure of existing helpers */
85 if (negotiateauthenticators
) {
86 helperStatefulShutdown(negotiateauthenticators
);
89 /* NP: dynamic helper restart will ensure they start up again as needed. */
93 AuthNegotiateConfig::done()
95 authnegotiate_initialised
= 0;
97 if (negotiateauthenticators
) {
98 helperStatefulShutdown(negotiateauthenticators
);
104 delete negotiateauthenticators
;
105 negotiateauthenticators
= NULL
;
108 wordlistDestroy(&authenticate
);
110 debugs(29, 2, "negotiateScheme::done: Negotiate authentication Shutdown.");
114 AuthNegotiateConfig::dump(StoreEntry
* entry
, const char *name
, AuthConfig
* scheme
)
116 wordlist
*list
= authenticate
;
117 storeAppendPrintf(entry
, "%s %s", name
, "negotiate");
119 while (list
!= NULL
) {
120 storeAppendPrintf(entry
, " %s", list
->key
);
124 storeAppendPrintf(entry
, "\n%s negotiate children %d startup=%d idle=%d concurrency=%d\n",
125 name
, authenticateChildren
.n_max
, authenticateChildren
.n_startup
, authenticateChildren
.n_idle
, authenticateChildren
.concurrency
);
126 storeAppendPrintf(entry
, "%s %s keep_alive %s\n", name
, "negotiate", keep_alive
? "on" : "off");
130 AuthNegotiateConfig::AuthNegotiateConfig() : keep_alive(1)
134 AuthNegotiateConfig::parse(AuthConfig
* scheme
, int n_configured
, char *param_str
)
136 if (strcasecmp(param_str
, "program") == 0) {
138 wordlistDestroy(&authenticate
);
140 parse_wordlist(&authenticate
);
142 requirePathnameExists("auth_param negotiate program", authenticate
->key
);
143 } else if (strcasecmp(param_str
, "children") == 0) {
144 authenticateChildren
.parseConfig();
145 } else if (strcasecmp(param_str
, "keep_alive") == 0) {
146 parse_onoff(&keep_alive
);
148 debugs(29, 0, "AuthNegotiateConfig::parse: unrecognised negotiate auth scheme parameter '" << param_str
<< "'");
152 * disable client side request pipelining. There is a race with
153 * Negotiate when the client sends a second request on an Negotiate
154 * connection before the authenticate challenge is sent. With
155 * this patch, the client may fail to authenticate, but squid's
156 * state will be preserved. Caveats: this should be a post-parse
157 * test, but that can wait for the modular parser to be integrated.
160 Config
.onoff
.pipeline_prefetch
= 0;
164 AuthNegotiateConfig::type() const
166 return negotiateScheme::GetInstance()->type();
170 * Initialize helpers and the like for this auth scheme.
171 * Called AFTER parsing the config file
174 AuthNegotiateConfig::init(AuthConfig
* scheme
)
178 authnegotiate_initialised
= 1;
180 if (negotiateauthenticators
== NULL
)
181 negotiateauthenticators
= new statefulhelper("negotiateauthenticator");
183 if (!proxy_auth_cache
)
184 proxy_auth_cache
= hash_create((HASHCMP
*) strcmp
, 7921, hash_string
);
186 assert(proxy_auth_cache
);
188 negotiateauthenticators
->cmdline
= authenticate
;
190 negotiateauthenticators
->childs
= authenticateChildren
;
192 negotiateauthenticators
->ipc_type
= IPC_STREAM
;
194 helperStatefulOpenServers(negotiateauthenticators
);
196 CBDATA_INIT_TYPE(authenticateStateData
);
201 AuthNegotiateConfig::registerWithCacheManager(void)
203 CacheManager::GetInstance()->
204 registerAction("negotiateauthenticator",
205 "Negotiate User Authenticator Stats",
206 authenticateNegotiateStats
, 0, 1);
210 AuthNegotiateConfig::active() const
212 return authnegotiate_initialised
== 1;
216 AuthNegotiateConfig::configured() const
218 if ((authenticate
!= NULL
) && (authenticateChildren
.n_max
!= 0)) {
219 debugs(29, 9, "AuthNegotiateConfig::configured: returning configured");
223 debugs(29, 9, "AuthNegotiateConfig::configured: returning unconfigured");
227 /* Negotiate Scheme */
230 AuthNegotiateConfig::fixHeader(AuthUserRequest::Pointer auth_user_request
, HttpReply
*rep
, http_hdr_type reqType
, HttpRequest
* request
)
232 AuthNegotiateUserRequest
*negotiate_request
;
237 /* Need keep-alive */
238 if (!request
->flags
.proxy_keepalive
&& request
->flags
.must_keepalive
)
241 /* New request, no user details */
242 if (auth_user_request
== NULL
) {
243 debugs(29, 9, "AuthNegotiateConfig::fixHeader: Sending type:" << reqType
<< " header: 'Negotiate'");
244 httpHeaderPutStrf(&rep
->header
, reqType
, "Negotiate");
247 /* drop the connection */
248 rep
->header
.delByName("keep-alive");
249 request
->flags
.proxy_keepalive
= 0;
252 negotiate_request
= dynamic_cast<AuthNegotiateUserRequest
*>(auth_user_request
.getRaw());
253 assert(negotiate_request
!= NULL
);
255 switch (negotiate_request
->user()->credentials()) {
257 case AuthUser::Failed
:
258 /* here it makes sense to drop the connection, as auth is
259 * tied to it, even if MAYBE the client could handle it - Kinkie */
260 rep
->header
.delByName("keep-alive");
261 request
->flags
.proxy_keepalive
= 0;
265 /* Special case: authentication finished OK but disallowed by ACL.
266 * Need to start over to give the client another chance.
268 if (negotiate_request
->server_blob
) {
269 debugs(29, 9, "authenticateNegotiateFixErrorHeader: Sending type:" << reqType
<< " header: 'Negotiate " << negotiate_request
->server_blob
<< "'");
270 httpHeaderPutStrf(&rep
->header
, reqType
, "Negotiate %s", negotiate_request
->server_blob
);
271 safe_free(negotiate_request
->server_blob
);
273 debugs(29, 9, "authenticateNegotiateFixErrorHeader: Connection authenticated");
274 httpHeaderPutStrf(&rep
->header
, reqType
, "Negotiate");
278 case AuthUser::Unchecked
:
279 /* semantic change: do not drop the connection.
280 * 2.5 implementation used to keep it open - Kinkie */
281 debugs(29, 9, "AuthNegotiateConfig::fixHeader: Sending type:" << reqType
<< " header: 'Negotiate'");
282 httpHeaderPutStrf(&rep
->header
, reqType
, "Negotiate");
285 case AuthUser::Handshake
:
286 /* we're waiting for a response from the client. Pass it the blob */
287 debugs(29, 9, "AuthNegotiateConfig::fixHeader: Sending type:" << reqType
<< " header: 'Negotiate " << negotiate_request
->server_blob
<< "'");
288 httpHeaderPutStrf(&rep
->header
, reqType
, "Negotiate %s", negotiate_request
->server_blob
);
289 safe_free(negotiate_request
->server_blob
);
293 debugs(29, DBG_CRITICAL
, "AuthNegotiateConfig::fixHeader: state " << negotiate_request
->user()->credentials() << ".");
294 fatal("unexpected state in AuthenticateNegotiateFixErrorHeader.\n");
299 NegotiateUser::~NegotiateUser()
301 debugs(29, 5, "NegotiateUser::~NegotiateUser: doing nothing to clearNegotiate scheme data for '" << this << "'");
305 NegotiateUser::ttl() const
307 return -1; // Negotiate cannot be cached.
311 authenticateNegotiateStats(StoreEntry
* sentry
)
313 helperStatefulStats(sentry
, negotiateauthenticators
, "Negotiate Authenticator Statistics");
317 * Decode a Negotiate [Proxy-]Auth string, placing the results in the passed
318 * Auth_user structure.
320 AuthUserRequest::Pointer
321 AuthNegotiateConfig::decode(char const *proxy_auth
)
323 NegotiateUser
*newUser
= new NegotiateUser(&negotiateConfig
);
324 AuthUserRequest
*auth_user_request
= new AuthNegotiateUserRequest();
325 assert(auth_user_request
->user() == NULL
);
327 auth_user_request
->user(newUser
);
328 auth_user_request
->user()->auth_type
= AUTH_NEGOTIATE
;
330 /* all we have to do is identify that it's Negotiate - the helper does the rest */
331 debugs(29, 9, "AuthNegotiateConfig::decode: Negotiate authentication");
332 return auth_user_request
;
336 NegotiateUser::deleteSelf() const
341 NegotiateUser::NegotiateUser(AuthConfig
*aConfig
) : AuthUser (aConfig
)
343 proxy_auth_list
.head
= proxy_auth_list
.tail
= NULL
;