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