]> git.ipfire.org Git - thirdparty/squid.git/blob - src/auth/negotiate/auth_negotiate.cc
Merge from trunk
[thirdparty/squid.git] / src / auth / negotiate / auth_negotiate.cc
1 /*
2 * $Id$
3 *
4 * DEBUG: section 29 Negotiate Authenticator
5 * AUTHOR: Robert Collins, Henrik Nordstrom, Francesco Chemolli
6 *
7 * SQUID Web Proxy Cache http://www.squid-cache.org/
8 * ----------------------------------------------------------
9 *
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.
18 *
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.
23 *
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.
28 *
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.
32 *
33 */
34
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 */
38
39
40 #include "squid.h"
41 #include "auth/negotiate/auth_negotiate.h"
42 #include "auth/Gadgets.h"
43 #include "auth/State.h"
44 #include "CacheManager.h"
45 #include "Store.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"
53 #include "wordlist.h"
54
55 /**
56 \defgroup AuthNegotiateInternal Negotiate Authenticator Internals
57 \ingroup AuthNegotiateAPI
58 */
59
60 /* Negotiate Scheme */
61 static AUTHSSTATS authenticateNegotiateStats;
62
63 /// \ingroup AuthNegotiateInternal
64 statefulhelper *negotiateauthenticators = NULL;
65
66 /// \ingroup AuthNegotiateInternal
67 static int authnegotiate_initialised = 0;
68
69 /// \ingroup AuthNegotiateInternal
70 AuthNegotiateConfig negotiateConfig;
71
72 /// \ingroup AuthNegotiateInternal
73 static hash_table *proxy_auth_cache = NULL;
74
75 /*
76 *
77 * Private Functions
78 *
79 */
80
81 void
82 AuthNegotiateConfig::rotateHelpers()
83 {
84 /* schedule closure of existing helpers */
85 if (negotiateauthenticators) {
86 helperStatefulShutdown(negotiateauthenticators);
87 }
88
89 /* NP: dynamic helper restart will ensure they start up again as needed. */
90 }
91
92 void
93 AuthNegotiateConfig::done()
94 {
95 authnegotiate_initialised = 0;
96
97 if (negotiateauthenticators) {
98 helperStatefulShutdown(negotiateauthenticators);
99 }
100
101 if (!shutting_down)
102 return;
103
104 delete negotiateauthenticators;
105 negotiateauthenticators = NULL;
106
107 if (authenticate)
108 wordlistDestroy(&authenticate);
109
110 debugs(29, 2, "negotiateScheme::done: Negotiate authentication Shutdown.");
111 }
112
113 void
114 AuthNegotiateConfig::dump(StoreEntry * entry, const char *name, AuthConfig * scheme)
115 {
116 wordlist *list = authenticate;
117 storeAppendPrintf(entry, "%s %s", name, "negotiate");
118
119 while (list != NULL) {
120 storeAppendPrintf(entry, " %s", list->key);
121 list = list->next;
122 }
123
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");
127
128 }
129
130 AuthNegotiateConfig::AuthNegotiateConfig() : keep_alive(1)
131 { }
132
133 void
134 AuthNegotiateConfig::parse(AuthConfig * scheme, int n_configured, char *param_str)
135 {
136 if (strcasecmp(param_str, "program") == 0) {
137 if (authenticate)
138 wordlistDestroy(&authenticate);
139
140 parse_wordlist(&authenticate);
141
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);
147 } else {
148 debugs(29, 0, "AuthNegotiateConfig::parse: unrecognised negotiate auth scheme parameter '" << param_str << "'");
149 }
150
151 /*
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.
158 */
159 if (authenticate)
160 Config.onoff.pipeline_prefetch = 0;
161 }
162
163 const char *
164 AuthNegotiateConfig::type() const
165 {
166 return negotiateScheme::GetInstance()->type();
167 }
168
169 /**
170 * Initialize helpers and the like for this auth scheme.
171 * Called AFTER parsing the config file
172 */
173 void
174 AuthNegotiateConfig::init(AuthConfig * scheme)
175 {
176 if (authenticate) {
177
178 authnegotiate_initialised = 1;
179
180 if (negotiateauthenticators == NULL)
181 negotiateauthenticators = new statefulhelper("negotiateauthenticator");
182
183 if (!proxy_auth_cache)
184 proxy_auth_cache = hash_create((HASHCMP *) strcmp, 7921, hash_string);
185
186 assert(proxy_auth_cache);
187
188 negotiateauthenticators->cmdline = authenticate;
189
190 negotiateauthenticators->childs = authenticateChildren;
191
192 negotiateauthenticators->ipc_type = IPC_STREAM;
193
194 helperStatefulOpenServers(negotiateauthenticators);
195
196 CBDATA_INIT_TYPE(authenticateStateData);
197 }
198 }
199
200 void
201 AuthNegotiateConfig::registerWithCacheManager(void)
202 {
203 CacheManager::GetInstance()->
204 registerAction("negotiateauthenticator",
205 "Negotiate User Authenticator Stats",
206 authenticateNegotiateStats, 0, 1);
207 }
208
209 bool
210 AuthNegotiateConfig::active() const
211 {
212 return authnegotiate_initialised == 1;
213 }
214
215 bool
216 AuthNegotiateConfig::configured() const
217 {
218 if ((authenticate != NULL) && (authenticateChildren.n_max != 0)) {
219 debugs(29, 9, "AuthNegotiateConfig::configured: returning configured");
220 return true;
221 }
222
223 debugs(29, 9, "AuthNegotiateConfig::configured: returning unconfigured");
224 return false;
225 }
226
227 /* Negotiate Scheme */
228
229 void
230 AuthNegotiateConfig::fixHeader(AuthUserRequest::Pointer auth_user_request, HttpReply *rep, http_hdr_type reqType, HttpRequest * request)
231 {
232 AuthNegotiateUserRequest *negotiate_request;
233
234 if (!authenticate)
235 return;
236
237 /* Need keep-alive */
238 if (!request->flags.proxy_keepalive && request->flags.must_keepalive)
239 return;
240
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");
245
246 if (!keep_alive) {
247 /* drop the connection */
248 rep->header.delByName("keep-alive");
249 request->flags.proxy_keepalive = 0;
250 }
251 } else {
252 negotiate_request = dynamic_cast<AuthNegotiateUserRequest *>(auth_user_request.getRaw());
253 assert(negotiate_request != NULL);
254
255 switch (negotiate_request->auth_state) {
256
257 case AUTHENTICATE_STATE_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;
262 /* fall through */
263
264 case AUTHENTICATE_STATE_DONE:
265 /* Special case: authentication finished OK but disallowed by ACL.
266 * Need to start over to give the client another chance.
267 */
268
269 if (negotiate_request->server_blob) {
270 debugs(29, 9, "authenticateNegotiateFixErrorHeader: Sending type:" << reqType << " header: 'Negotiate " << negotiate_request->server_blob << "'");
271 httpHeaderPutStrf(&rep->header, reqType, "Negotiate %s", negotiate_request->server_blob);
272 safe_free(negotiate_request->server_blob);
273 } else {
274 debugs(29, 9, "authenticateNegotiateFixErrorHeader: Connection authenticated");
275 httpHeaderPutStrf(&rep->header, reqType, "Negotiate");
276 }
277
278 break;
279
280 case AUTHENTICATE_STATE_NONE:
281 /* semantic change: do not drop the connection.
282 * 2.5 implementation used to keep it open - Kinkie */
283 debugs(29, 9, "AuthNegotiateConfig::fixHeader: Sending type:" << reqType << " header: 'Negotiate'");
284 httpHeaderPutStrf(&rep->header, reqType, "Negotiate");
285 break;
286
287 case AUTHENTICATE_STATE_IN_PROGRESS:
288 /* we're waiting for a response from the client. Pass it the blob */
289 debugs(29, 9, "AuthNegotiateConfig::fixHeader: Sending type:" << reqType << " header: 'Negotiate " << negotiate_request->server_blob << "'");
290 httpHeaderPutStrf(&rep->header, reqType, "Negotiate %s", negotiate_request->server_blob);
291 safe_free(negotiate_request->server_blob);
292 break;
293
294
295 default:
296 debugs(29, 0, "AuthNegotiateConfig::fixHeader: state " << negotiate_request->auth_state << ".");
297 fatal("unexpected state in AuthenticateNegotiateFixErrorHeader.\n");
298 }
299 }
300 }
301
302 NegotiateUser::~NegotiateUser()
303 {
304 debugs(29, 5, "NegotiateUser::~NegotiateUser: doing nothing to clearNegotiate scheme data for '" << this << "'");
305 }
306
307 int32_t
308 NegotiateUser::ttl() const
309 {
310 return -1; // Negotiate canot be cached.
311 }
312
313
314 static void
315 authenticateNegotiateStats(StoreEntry * sentry)
316 {
317 helperStatefulStats(sentry, negotiateauthenticators, "Negotiate Authenticator Statistics");
318 }
319
320 /*
321 * Decode a Negotiate [Proxy-]Auth string, placing the results in the passed
322 * Auth_user structure.
323 */
324 AuthUserRequest::Pointer
325 AuthNegotiateConfig::decode(char const *proxy_auth)
326 {
327 NegotiateUser *newUser = new NegotiateUser(&negotiateConfig);
328 AuthUserRequest *auth_user_request = new AuthNegotiateUserRequest();
329 assert(auth_user_request->user() == NULL);
330
331 auth_user_request->user(newUser);
332 auth_user_request->user()->auth_type = AUTH_NEGOTIATE;
333
334 /* all we have to do is identify that it's Negotiate - the helper does the rest */
335 debugs(29, 9, "AuthNegotiateConfig::decode: Negotiate authentication");
336 return auth_user_request;
337 }
338
339 void
340 NegotiateUser::deleteSelf() const
341 {
342 delete this;
343 }
344
345 NegotiateUser::NegotiateUser(AuthConfig *aConfig) : AuthUser (aConfig)
346 {
347 proxy_auth_list.head = proxy_auth_list.tail = NULL;
348 }