]> git.ipfire.org Git - thirdparty/squid.git/blob - src/auth/negotiate/auth_negotiate.cc
SourceLayout: namespace for Auth::User
[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 "mgr/Registration.h"
45 #include "Store.h"
46 #include "client_side.h"
47 #include "HttpReply.h"
48 #include "HttpRequest.h"
49 #include "SquidTime.h"
50 #include "auth/negotiate/Scheme.h"
51 #include "auth/negotiate/UserRequest.h"
52 #include "wordlist.h"
53
54 /**
55 \defgroup AuthNegotiateInternal Negotiate Authenticator Internals
56 \ingroup AuthNegotiateAPI
57 */
58
59 /* Negotiate Scheme */
60 static AUTHSSTATS authenticateNegotiateStats;
61
62 /// \ingroup AuthNegotiateInternal
63 statefulhelper *negotiateauthenticators = NULL;
64
65 /// \ingroup AuthNegotiateInternal
66 static int authnegotiate_initialised = 0;
67
68 /// \ingroup AuthNegotiateInternal
69 Auth::Negotiate::Config negotiateConfig;
70
71 /// \ingroup AuthNegotiateInternal
72 static hash_table *proxy_auth_cache = NULL;
73
74 /*
75 *
76 * Private Functions
77 *
78 */
79
80 void
81 Auth::Negotiate::Config::rotateHelpers()
82 {
83 /* schedule closure of existing helpers */
84 if (negotiateauthenticators) {
85 helperStatefulShutdown(negotiateauthenticators);
86 }
87
88 /* NP: dynamic helper restart will ensure they start up again as needed. */
89 }
90
91 void
92 Auth::Negotiate::Config::done()
93 {
94 authnegotiate_initialised = 0;
95
96 if (negotiateauthenticators) {
97 helperStatefulShutdown(negotiateauthenticators);
98 }
99
100 if (!shutting_down)
101 return;
102
103 delete negotiateauthenticators;
104 negotiateauthenticators = NULL;
105
106 if (authenticateProgram)
107 wordlistDestroy(&authenticateProgram);
108
109 debugs(29, DBG_IMPORTANT, "Reconfigure: Negotiate authentication configuration cleared.");
110 }
111
112 void
113 Auth::Negotiate::Config::dump(StoreEntry * entry, const char *name, Auth::Config * scheme)
114 {
115 wordlist *list = authenticateProgram;
116 storeAppendPrintf(entry, "%s %s", name, "negotiate");
117
118 while (list != NULL) {
119 storeAppendPrintf(entry, " %s", list->key);
120 list = list->next;
121 }
122
123 storeAppendPrintf(entry, "\n%s negotiate children %d startup=%d idle=%d concurrency=%d\n",
124 name, authenticateChildren.n_max, authenticateChildren.n_startup, authenticateChildren.n_idle, authenticateChildren.concurrency);
125 storeAppendPrintf(entry, "%s %s keep_alive %s\n", name, "negotiate", keep_alive ? "on" : "off");
126
127 }
128
129 Auth::Negotiate::Config::Config() : keep_alive(1)
130 { }
131
132 void
133 Auth::Negotiate::Config::parse(Auth::Config * scheme, int n_configured, char *param_str)
134 {
135 if (strcasecmp(param_str, "program") == 0) {
136 if (authenticateProgram)
137 wordlistDestroy(&authenticateProgram);
138
139 parse_wordlist(&authenticateProgram);
140
141 requirePathnameExists("auth_param negotiate program", authenticateProgram->key);
142 } else if (strcasecmp(param_str, "children") == 0) {
143 authenticateChildren.parseConfig();
144 } else if (strcasecmp(param_str, "keep_alive") == 0) {
145 parse_onoff(&keep_alive);
146 } else {
147 debugs(29, DBG_CRITICAL, "ERROR: unrecognised Negotiate auth scheme parameter '" << param_str << "'");
148 }
149 }
150
151 const char *
152 Auth::Negotiate::Config::type() const
153 {
154 return Auth::Negotiate::Scheme::GetInstance()->type();
155 }
156
157 /**
158 * Initialize helpers and the like for this auth scheme.
159 * Called AFTER parsing the config file
160 */
161 void
162 Auth::Negotiate::Config::init(Auth::Config * scheme)
163 {
164 if (authenticateProgram) {
165
166 authnegotiate_initialised = 1;
167
168 if (negotiateauthenticators == NULL)
169 negotiateauthenticators = new statefulhelper("negotiateauthenticator");
170
171 if (!proxy_auth_cache)
172 proxy_auth_cache = hash_create((HASHCMP *) strcmp, 7921, hash_string);
173
174 assert(proxy_auth_cache);
175
176 negotiateauthenticators->cmdline = authenticateProgram;
177
178 negotiateauthenticators->childs = authenticateChildren;
179
180 negotiateauthenticators->ipc_type = IPC_STREAM;
181
182 helperStatefulOpenServers(negotiateauthenticators);
183
184 CBDATA_INIT_TYPE(authenticateStateData);
185 }
186 }
187
188 void
189 Auth::Negotiate::Config::registerWithCacheManager(void)
190 {
191 Mgr::RegisterAction("negotiateauthenticator",
192 "Negotiate User Authenticator Stats",
193 authenticateNegotiateStats, 0, 1);
194 }
195
196 bool
197 Auth::Negotiate::Config::active() const
198 {
199 return authnegotiate_initialised == 1;
200 }
201
202 bool
203 Auth::Negotiate::Config::configured() const
204 {
205 if (authenticateProgram && (authenticateChildren.n_max != 0)) {
206 debugs(29, 9, HERE << "returning configured");
207 return true;
208 }
209
210 debugs(29, 9, HERE << "returning unconfigured");
211 return false;
212 }
213
214 /* Negotiate Scheme */
215
216 void
217 Auth::Negotiate::Config::fixHeader(AuthUserRequest::Pointer auth_user_request, HttpReply *rep, http_hdr_type reqType, HttpRequest * request)
218 {
219 AuthNegotiateUserRequest *negotiate_request;
220
221 if (!authenticateProgram)
222 return;
223
224 /* Need keep-alive */
225 if (!request->flags.proxy_keepalive && request->flags.must_keepalive)
226 return;
227
228 /* New request, no user details */
229 if (auth_user_request == NULL) {
230 debugs(29, 9, HERE << "Sending type:" << reqType << " header: 'Negotiate'");
231 httpHeaderPutStrf(&rep->header, reqType, "Negotiate");
232
233 if (!keep_alive) {
234 /* drop the connection */
235 rep->header.delByName("keep-alive");
236 request->flags.proxy_keepalive = 0;
237 }
238 } else {
239 negotiate_request = dynamic_cast<AuthNegotiateUserRequest *>(auth_user_request.getRaw());
240 assert(negotiate_request != NULL);
241
242 switch (negotiate_request->user()->credentials()) {
243
244 case Auth::Failed:
245 /* here it makes sense to drop the connection, as auth is
246 * tied to it, even if MAYBE the client could handle it - Kinkie */
247 rep->header.delByName("keep-alive");
248 request->flags.proxy_keepalive = 0;
249 /* fall through */
250
251 case Auth::Ok:
252 /* Special case: authentication finished OK but disallowed by ACL.
253 * Need to start over to give the client another chance.
254 */
255 if (negotiate_request->server_blob) {
256 debugs(29, 9, HERE << "Sending type:" << reqType << " header: 'Negotiate " << negotiate_request->server_blob << "'");
257 httpHeaderPutStrf(&rep->header, reqType, "Negotiate %s", negotiate_request->server_blob);
258 safe_free(negotiate_request->server_blob);
259 } else {
260 debugs(29, 9, HERE << "Connection authenticated");
261 httpHeaderPutStrf(&rep->header, reqType, "Negotiate");
262 }
263 break;
264
265 case Auth::Unchecked:
266 /* semantic change: do not drop the connection.
267 * 2.5 implementation used to keep it open - Kinkie */
268 debugs(29, 9, HERE << "Sending type:" << reqType << " header: 'Negotiate'");
269 httpHeaderPutStrf(&rep->header, reqType, "Negotiate");
270 break;
271
272 case Auth::Handshake:
273 /* we're waiting for a response from the client. Pass it the blob */
274 debugs(29, 9, HERE << "Sending type:" << reqType << " header: 'Negotiate " << negotiate_request->server_blob << "'");
275 httpHeaderPutStrf(&rep->header, reqType, "Negotiate %s", negotiate_request->server_blob);
276 safe_free(negotiate_request->server_blob);
277 break;
278
279 default:
280 debugs(29, DBG_CRITICAL, "ERROR: Negotiate auth fixHeader: state " << negotiate_request->user()->credentials() << ".");
281 fatal("unexpected state in AuthenticateNegotiateFixErrorHeader.\n");
282 }
283 }
284 }
285
286 NegotiateUser::~NegotiateUser()
287 {
288 debugs(29, 5, HERE << "doing nothing to clearNegotiate scheme data for '" << this << "'");
289 }
290
291 int32_t
292 NegotiateUser::ttl() const
293 {
294 return -1; // Negotiate cannot be cached.
295 }
296
297 static void
298 authenticateNegotiateStats(StoreEntry * sentry)
299 {
300 helperStatefulStats(sentry, negotiateauthenticators, "Negotiate Authenticator Statistics");
301 }
302
303 /*
304 * Decode a Negotiate [Proxy-]Auth string, placing the results in the passed
305 * Auth_user structure.
306 */
307 AuthUserRequest::Pointer
308 Auth::Negotiate::Config::decode(char const *proxy_auth)
309 {
310 NegotiateUser *newUser = new NegotiateUser(&negotiateConfig);
311 AuthUserRequest *auth_user_request = new AuthNegotiateUserRequest();
312 assert(auth_user_request->user() == NULL);
313
314 auth_user_request->user(newUser);
315 auth_user_request->user()->auth_type = Auth::AUTH_NEGOTIATE;
316
317 /* all we have to do is identify that it's Negotiate - the helper does the rest */
318 debugs(29, 9, HERE << "decode Negotiate authentication");
319 return auth_user_request;
320 }
321
322 NegotiateUser::NegotiateUser(Auth::Config *aConfig) : Auth::User(aConfig)
323 {
324 proxy_auth_list.head = proxy_auth_list.tail = NULL;
325 }