]> 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::done()
83 {
84 authnegotiate_initialised = 0;
85
86 if (negotiateauthenticators) {
87 helperStatefulShutdown(negotiateauthenticators);
88 }
89
90 if (!shutting_down)
91 return;
92
93 delete negotiateauthenticators;
94 negotiateauthenticators = NULL;
95
96 if (authenticate)
97 wordlistDestroy(&authenticate);
98
99 debugs(29, 2, "negotiateScheme::done: Negotiate authentication Shutdown.");
100 }
101
102 void
103 AuthNegotiateConfig::dump(StoreEntry * entry, const char *name, AuthConfig * scheme)
104 {
105 wordlist *list = authenticate;
106 storeAppendPrintf(entry, "%s %s", name, "negotiate");
107
108 while (list != NULL) {
109 storeAppendPrintf(entry, " %s", list->key);
110 list = list->next;
111 }
112
113 storeAppendPrintf(entry, "\n%s negotiate children %d startup=%d idle=%d concurrency=%d\n",
114 name, authenticateChildren.n_max, authenticateChildren.n_startup, authenticateChildren.n_idle, authenticateChildren.concurrency);
115 storeAppendPrintf(entry, "%s %s keep_alive %s\n", name, "negotiate", keep_alive ? "on" : "off");
116
117 }
118
119 AuthNegotiateConfig::AuthNegotiateConfig() : keep_alive(1)
120 { }
121
122 void
123 AuthNegotiateConfig::parse(AuthConfig * scheme, int n_configured, char *param_str)
124 {
125 if (strcasecmp(param_str, "program") == 0) {
126 if (authenticate)
127 wordlistDestroy(&authenticate);
128
129 parse_wordlist(&authenticate);
130
131 requirePathnameExists("auth_param negotiate program", authenticate->key);
132 } else if (strcasecmp(param_str, "children") == 0) {
133 authenticateChildren.parseConfig();
134 } else if (strcasecmp(param_str, "keep_alive") == 0) {
135 parse_onoff(&keep_alive);
136 } else {
137 debugs(29, 0, "AuthNegotiateConfig::parse: unrecognised negotiate auth scheme parameter '" << param_str << "'");
138 }
139
140 /*
141 * disable client side request pipelining. There is a race with
142 * Negotiate when the client sends a second request on an Negotiate
143 * connection before the authenticate challenge is sent. With
144 * this patch, the client may fail to authenticate, but squid's
145 * state will be preserved. Caveats: this should be a post-parse
146 * test, but that can wait for the modular parser to be integrated.
147 */
148 if (authenticate)
149 Config.onoff.pipeline_prefetch = 0;
150 }
151
152 const char *
153 AuthNegotiateConfig::type() const
154 {
155 return negotiateScheme::GetInstance()->type();
156 }
157
158 /**
159 * Initialize helpers and the like for this auth scheme.
160 * Called AFTER parsing the config file
161 */
162 void
163 AuthNegotiateConfig::init(AuthConfig * scheme)
164 {
165 if (authenticate) {
166
167 authnegotiate_initialised = 1;
168
169 if (negotiateauthenticators == NULL)
170 negotiateauthenticators = new statefulhelper("negotiateauthenticator");
171
172 if (!proxy_auth_cache)
173 proxy_auth_cache = hash_create((HASHCMP *) strcmp, 7921, hash_string);
174
175 assert(proxy_auth_cache);
176
177 negotiateauthenticators->cmdline = authenticate;
178
179 negotiateauthenticators->childs = authenticateChildren;
180
181 negotiateauthenticators->ipc_type = IPC_STREAM;
182
183 helperStatefulOpenServers(negotiateauthenticators);
184
185 CBDATA_INIT_TYPE(authenticateStateData);
186 }
187 }
188
189 void
190 AuthNegotiateConfig::registerWithCacheManager(void)
191 {
192 CacheManager::GetInstance()->
193 registerAction("negotiateauthenticator",
194 "Negotiate User Authenticator Stats",
195 authenticateNegotiateStats, 0, 1);
196 }
197
198 bool
199 AuthNegotiateConfig::active() const
200 {
201 return authnegotiate_initialised == 1;
202 }
203
204 bool
205 AuthNegotiateConfig::configured() const
206 {
207 if ((authenticate != NULL) && (authenticateChildren.n_max != 0)) {
208 debugs(29, 9, "AuthNegotiateConfig::configured: returning configured");
209 return true;
210 }
211
212 debugs(29, 9, "AuthNegotiateConfig::configured: returning unconfigured");
213 return false;
214 }
215
216 /* Negotiate Scheme */
217
218 void
219 AuthNegotiateConfig::fixHeader(AuthUserRequest::Pointer auth_user_request, HttpReply *rep, http_hdr_type reqType, HttpRequest * request)
220 {
221 AuthNegotiateUserRequest *negotiate_request;
222
223 if (!authenticate)
224 return;
225
226 /* Need keep-alive */
227 if (!request->flags.proxy_keepalive && request->flags.must_keepalive)
228 return;
229
230 /* New request, no user details */
231 if (auth_user_request == NULL) {
232 debugs(29, 9, "AuthNegotiateConfig::fixHeader: Sending type:" << reqType << " header: 'Negotiate'");
233 httpHeaderPutStrf(&rep->header, reqType, "Negotiate");
234
235 if (!keep_alive) {
236 /* drop the connection */
237 rep->header.delByName("keep-alive");
238 request->flags.proxy_keepalive = 0;
239 }
240 } else {
241 negotiate_request = dynamic_cast<AuthNegotiateUserRequest *>(auth_user_request.getRaw());
242 assert(negotiate_request != NULL);
243
244 switch (negotiate_request->auth_state) {
245
246 case AUTHENTICATE_STATE_FAILED:
247 /* here it makes sense to drop the connection, as auth is
248 * tied to it, even if MAYBE the client could handle it - Kinkie */
249 rep->header.delByName("keep-alive");
250 request->flags.proxy_keepalive = 0;
251 /* fall through */
252
253 case AUTHENTICATE_STATE_DONE:
254 /* Special case: authentication finished OK but disallowed by ACL.
255 * Need to start over to give the client another chance.
256 */
257
258 if (negotiate_request->server_blob) {
259 debugs(29, 9, "authenticateNegotiateFixErrorHeader: Sending type:" << reqType << " header: 'Negotiate " << negotiate_request->server_blob << "'");
260 httpHeaderPutStrf(&rep->header, reqType, "Negotiate %s", negotiate_request->server_blob);
261 safe_free(negotiate_request->server_blob);
262 } else {
263 debugs(29, 9, "authenticateNegotiateFixErrorHeader: Connection authenticated");
264 httpHeaderPutStrf(&rep->header, reqType, "Negotiate");
265 }
266
267 break;
268
269 case AUTHENTICATE_STATE_NONE:
270 /* semantic change: do not drop the connection.
271 * 2.5 implementation used to keep it open - Kinkie */
272 debugs(29, 9, "AuthNegotiateConfig::fixHeader: Sending type:" << reqType << " header: 'Negotiate'");
273 httpHeaderPutStrf(&rep->header, reqType, "Negotiate");
274 break;
275
276 case AUTHENTICATE_STATE_IN_PROGRESS:
277 /* we're waiting for a response from the client. Pass it the blob */
278 debugs(29, 9, "AuthNegotiateConfig::fixHeader: Sending type:" << reqType << " header: 'Negotiate " << negotiate_request->server_blob << "'");
279 httpHeaderPutStrf(&rep->header, reqType, "Negotiate %s", negotiate_request->server_blob);
280 safe_free(negotiate_request->server_blob);
281 break;
282
283
284 default:
285 debugs(29, 0, "AuthNegotiateConfig::fixHeader: state " << negotiate_request->auth_state << ".");
286 fatal("unexpected state in AuthenticateNegotiateFixErrorHeader.\n");
287 }
288 }
289 }
290
291 NegotiateUser::~NegotiateUser()
292 {
293 debugs(29, 5, "NegotiateUser::~NegotiateUser: doing nothing to clearNegotiate scheme data for '" << this << "'");
294 }
295
296 int32_t
297 NegotiateUser::ttl() const
298 {
299 return -1; // Negotiate canot be cached.
300 }
301
302
303 static void
304 authenticateNegotiateStats(StoreEntry * sentry)
305 {
306 helperStatefulStats(sentry, negotiateauthenticators, "Negotiate Authenticator Statistics");
307 }
308
309 /*
310 * Decode a Negotiate [Proxy-]Auth string, placing the results in the passed
311 * Auth_user structure.
312 */
313 AuthUserRequest::Pointer
314 AuthNegotiateConfig::decode(char const *proxy_auth)
315 {
316 NegotiateUser *newUser = new NegotiateUser(&negotiateConfig);
317 AuthUserRequest *auth_user_request = new AuthNegotiateUserRequest();
318 assert(auth_user_request->user() == NULL);
319
320 auth_user_request->user(newUser);
321 auth_user_request->user()->auth_type = AUTH_NEGOTIATE;
322 #if USER_REQUEST_LOOP_DEAD
323 auth_user_request->user()->addRequest(auth_user_request);
324 #endif
325
326 /* all we have to do is identify that it's Negotiate - the helper does the rest */
327 debugs(29, 9, "AuthNegotiateConfig::decode: Negotiate authentication");
328 return auth_user_request;
329 }
330
331 void
332 NegotiateUser::deleteSelf() const
333 {
334 delete this;
335 }
336
337 NegotiateUser::NegotiateUser(AuthConfig *aConfig) : AuthUser (aConfig)
338 {
339 proxy_auth_list.head = proxy_auth_list.tail = NULL;
340 }