]>
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 | ||
582c2af2 | 40 | #include "squid.h" |
5817ee13 | 41 | #include "auth/negotiate/auth_negotiate.h" |
3ad63615 | 42 | #include "auth/Gadgets.h" |
928f3421 | 43 | #include "auth/State.h" |
8a01b99e | 44 | #include "cache_cf.h" |
8822ebee | 45 | #include "mgr/Registration.h" |
6bf4f823 | 46 | #include "Store.h" |
47 | #include "client_side.h" | |
a5bac1d2 | 48 | #include "HttpHeaderTools.h" |
6bf4f823 | 49 | #include "HttpReply.h" |
50 | #include "HttpRequest.h" | |
582c2af2 | 51 | #include "protos.h" |
cc192b50 | 52 | #include "SquidTime.h" |
616cfc4c | 53 | #include "auth/negotiate/Scheme.h" |
aa110616 | 54 | #include "auth/negotiate/User.h" |
616cfc4c | 55 | #include "auth/negotiate/UserRequest.h" |
d295d770 | 56 | #include "wordlist.h" |
6bf4f823 | 57 | |
63be0a78 | 58 | /** |
59 | \defgroup AuthNegotiateInternal Negotiate Authenticator Internals | |
60 | \ingroup AuthNegotiateAPI | |
61 | */ | |
62 | ||
6bf4f823 | 63 | /* Negotiate Scheme */ |
6bf4f823 | 64 | static AUTHSSTATS authenticateNegotiateStats; |
65 | ||
63be0a78 | 66 | /// \ingroup AuthNegotiateInternal |
928f3421 | 67 | statefulhelper *negotiateauthenticators = NULL; |
6bf4f823 | 68 | |
63be0a78 | 69 | /// \ingroup AuthNegotiateInternal |
6bf4f823 | 70 | static int authnegotiate_initialised = 0; |
71 | ||
63be0a78 | 72 | /// \ingroup AuthNegotiateInternal |
372fccd6 | 73 | Auth::Negotiate::Config negotiateConfig; |
6bf4f823 | 74 | |
63be0a78 | 75 | /// \ingroup AuthNegotiateInternal |
6bf4f823 | 76 | static hash_table *proxy_auth_cache = NULL; |
77 | ||
78 | /* | |
79 | * | |
80 | * Private Functions | |
81 | * | |
82 | */ | |
83 | ||
0bcb6908 | 84 | void |
372fccd6 | 85 | Auth::Negotiate::Config::rotateHelpers() |
0bcb6908 AJ |
86 | { |
87 | /* schedule closure of existing helpers */ | |
88 | if (negotiateauthenticators) { | |
89 | helperStatefulShutdown(negotiateauthenticators); | |
90 | } | |
91 | ||
92 | /* NP: dynamic helper restart will ensure they start up again as needed. */ | |
93 | } | |
94 | ||
6bf4f823 | 95 | void |
372fccd6 | 96 | Auth::Negotiate::Config::done() |
6bf4f823 | 97 | { |
6bf4f823 | 98 | authnegotiate_initialised = 0; |
99 | ||
5817ee13 AJ |
100 | if (negotiateauthenticators) { |
101 | helperStatefulShutdown(negotiateauthenticators); | |
5817ee13 | 102 | } |
6bf4f823 | 103 | |
6bf4f823 | 104 | if (!shutting_down) |
105 | return; | |
106 | ||
48d54e4d | 107 | delete negotiateauthenticators; |
6bf4f823 | 108 | negotiateauthenticators = NULL; |
109 | ||
58ee2093 AJ |
110 | if (authenticateProgram) |
111 | wordlistDestroy(&authenticateProgram); | |
cdabe87d | 112 | |
372fccd6 | 113 | debugs(29, DBG_IMPORTANT, "Reconfigure: Negotiate authentication configuration cleared."); |
6bf4f823 | 114 | } |
115 | ||
116 | void | |
372fccd6 | 117 | Auth::Negotiate::Config::dump(StoreEntry * entry, const char *name, Auth::Config * scheme) |
6bf4f823 | 118 | { |
58ee2093 | 119 | wordlist *list = authenticateProgram; |
6bf4f823 | 120 | storeAppendPrintf(entry, "%s %s", name, "negotiate"); |
121 | ||
122 | while (list != NULL) { | |
123 | storeAppendPrintf(entry, " %s", list->key); | |
124 | list = list->next; | |
125 | } | |
126 | ||
404cfda1 AJ |
127 | storeAppendPrintf(entry, "\n%s negotiate children %d startup=%d idle=%d concurrency=%d\n", |
128 | name, authenticateChildren.n_max, authenticateChildren.n_startup, authenticateChildren.n_idle, authenticateChildren.concurrency); | |
6bf4f823 | 129 | storeAppendPrintf(entry, "%s %s keep_alive %s\n", name, "negotiate", keep_alive ? "on" : "off"); |
130 | ||
131 | } | |
132 | ||
372fccd6 | 133 | Auth::Negotiate::Config::Config() : keep_alive(1) |
6bf4f823 | 134 | { } |
135 | ||
136 | void | |
372fccd6 | 137 | Auth::Negotiate::Config::parse(Auth::Config * scheme, int n_configured, char *param_str) |
6bf4f823 | 138 | { |
139 | if (strcasecmp(param_str, "program") == 0) { | |
58ee2093 AJ |
140 | if (authenticateProgram) |
141 | wordlistDestroy(&authenticateProgram); | |
6bf4f823 | 142 | |
58ee2093 | 143 | parse_wordlist(&authenticateProgram); |
6bf4f823 | 144 | |
58ee2093 | 145 | requirePathnameExists("auth_param negotiate program", authenticateProgram->key); |
6bf4f823 | 146 | } else if (strcasecmp(param_str, "children") == 0) { |
48d54e4d | 147 | authenticateChildren.parseConfig(); |
6bf4f823 | 148 | } else if (strcasecmp(param_str, "keep_alive") == 0) { |
149 | parse_onoff(&keep_alive); | |
150 | } else { | |
372fccd6 | 151 | debugs(29, DBG_CRITICAL, "ERROR: unrecognised Negotiate auth scheme parameter '" << param_str << "'"); |
6bf4f823 | 152 | } |
6bf4f823 | 153 | } |
154 | ||
155 | const char * | |
372fccd6 | 156 | Auth::Negotiate::Config::type() const |
6bf4f823 | 157 | { |
d6374be6 | 158 | return Auth::Negotiate::Scheme::GetInstance()->type(); |
6bf4f823 | 159 | } |
160 | ||
63be0a78 | 161 | /** |
162 | * Initialize helpers and the like for this auth scheme. | |
163 | * Called AFTER parsing the config file | |
164 | */ | |
6bf4f823 | 165 | void |
372fccd6 | 166 | Auth::Negotiate::Config::init(Auth::Config * scheme) |
6bf4f823 | 167 | { |
58ee2093 | 168 | if (authenticateProgram) { |
81425fb6 | 169 | |
6bf4f823 | 170 | authnegotiate_initialised = 1; |
171 | ||
172 | if (negotiateauthenticators == NULL) | |
48d54e4d | 173 | negotiateauthenticators = new statefulhelper("negotiateauthenticator"); |
6bf4f823 | 174 | |
175 | if (!proxy_auth_cache) | |
30abd221 | 176 | proxy_auth_cache = hash_create((HASHCMP *) strcmp, 7921, hash_string); |
6bf4f823 | 177 | |
178 | assert(proxy_auth_cache); | |
179 | ||
58ee2093 | 180 | negotiateauthenticators->cmdline = authenticateProgram; |
6bf4f823 | 181 | |
1af735c7 | 182 | negotiateauthenticators->childs.updateLimits(authenticateChildren); |
6bf4f823 | 183 | |
184 | negotiateauthenticators->ipc_type = IPC_STREAM; | |
185 | ||
186 | helperStatefulOpenServers(negotiateauthenticators); | |
6bf4f823 | 187 | } |
188 | } | |
189 | ||
62ee09ca | 190 | void |
372fccd6 | 191 | Auth::Negotiate::Config::registerWithCacheManager(void) |
62ee09ca | 192 | { |
8822ebee | 193 | Mgr::RegisterAction("negotiateauthenticator", |
d9fc6862 A |
194 | "Negotiate User Authenticator Stats", |
195 | authenticateNegotiateStats, 0, 1); | |
62ee09ca | 196 | } |
197 | ||
6bf4f823 | 198 | bool |
372fccd6 | 199 | Auth::Negotiate::Config::active() const |
6bf4f823 | 200 | { |
201 | return authnegotiate_initialised == 1; | |
202 | } | |
203 | ||
204 | bool | |
372fccd6 | 205 | Auth::Negotiate::Config::configured() const |
6bf4f823 | 206 | { |
58ee2093 | 207 | if (authenticateProgram && (authenticateChildren.n_max != 0)) { |
372fccd6 | 208 | debugs(29, 9, HERE << "returning configured"); |
6bf4f823 | 209 | return true; |
210 | } | |
211 | ||
372fccd6 | 212 | debugs(29, 9, HERE << "returning unconfigured"); |
6bf4f823 | 213 | return false; |
214 | } | |
215 | ||
216 | /* Negotiate Scheme */ | |
6bf4f823 | 217 | |
218 | void | |
c7baff40 | 219 | Auth::Negotiate::Config::fixHeader(Auth::UserRequest::Pointer auth_user_request, HttpReply *rep, http_hdr_type reqType, HttpRequest * request) |
6bf4f823 | 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 { | |
c7baff40 | 239 | Auth::Negotiate::UserRequest *negotiate_request = dynamic_cast<Auth::Negotiate::UserRequest *>(auth_user_request.getRaw()); |
27da7c21 | 240 | assert(negotiate_request != NULL); |
241 | ||
d232141d | 242 | switch (negotiate_request->user()->credentials()) { |
6bf4f823 | 243 | |
d87154ee | 244 | case Auth::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 | ||
d87154ee | 251 | case Auth::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 | ||
d87154ee | 265 | case Auth::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 | ||
d87154ee | 272 | case Auth::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 | ||
6bf4f823 | 286 | static void |
287 | authenticateNegotiateStats(StoreEntry * sentry) | |
288 | { | |
81425fb6 | 289 | helperStatefulStats(sentry, negotiateauthenticators, "Negotiate Authenticator Statistics"); |
6bf4f823 | 290 | } |
291 | ||
6bf4f823 | 292 | /* |
81425fb6 | 293 | * Decode a Negotiate [Proxy-]Auth string, placing the results in the passed |
6bf4f823 | 294 | * Auth_user structure. |
295 | */ | |
c7baff40 | 296 | Auth::UserRequest::Pointer |
372fccd6 | 297 | Auth::Negotiate::Config::decode(char const *proxy_auth) |
6bf4f823 | 298 | { |
aa110616 | 299 | Auth::Negotiate::User *newUser = new Auth::Negotiate::User(&negotiateConfig); |
c7baff40 | 300 | Auth::UserRequest *auth_user_request = new Auth::Negotiate::UserRequest(); |
6bf4f823 | 301 | assert(auth_user_request->user() == NULL); |
a33a428a | 302 | |
6bf4f823 | 303 | auth_user_request->user(newUser); |
616cfc4c | 304 | auth_user_request->user()->auth_type = Auth::AUTH_NEGOTIATE; |
6bf4f823 | 305 | |
81425fb6 | 306 | /* all we have to do is identify that it's Negotiate - the helper does the rest */ |
372fccd6 | 307 | debugs(29, 9, HERE << "decode Negotiate authentication"); |
6bf4f823 | 308 | return auth_user_request; |
309 | } |