]>
Commit | Line | Data |
---|---|---|
6bf4f823 | 1 | /* |
6bf4f823 | 2 | * DEBUG: section 29 Negotiate Authenticator |
3 | * AUTHOR: Robert Collins, Henrik Nordstrom, Francesco Chemolli | |
4 | * | |
5 | * SQUID Web Proxy Cache http://www.squid-cache.org/ | |
6 | * ---------------------------------------------------------- | |
7 | * | |
8 | * Squid is the result of efforts by numerous individuals from | |
9 | * the Internet community; see the CONTRIBUTORS file for full | |
10 | * details. Many organizations have provided support for Squid's | |
11 | * development; see the SPONSORS file for full details. Squid is | |
12 | * Copyrighted (C) 2001 by the Regents of the University of | |
13 | * California; see the COPYRIGHT file for full details. Squid | |
14 | * incorporates software developed and/or copyrighted by other | |
15 | * sources; see the CREDITS file for full details. | |
16 | * | |
17 | * This program is free software; you can redistribute it and/or modify | |
18 | * it under the terms of the GNU General Public License as published by | |
19 | * the Free Software Foundation; either version 2 of the License, or | |
20 | * (at your option) any later version. | |
26ac0430 | 21 | * |
6bf4f823 | 22 | * This program is distributed in the hope that it will be useful, |
23 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
24 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
25 | * GNU General Public License for more details. | |
26ac0430 | 26 | * |
6bf4f823 | 27 | * You should have received a copy of the GNU General Public License |
28 | * along with this program; if not, write to the Free Software | |
29 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA. | |
30 | * | |
31 | */ | |
32 | ||
33 | /* The functions in this file handle authentication. | |
34 | * They DO NOT perform access control or auditing. | |
35 | * See acl.c for access control and client_side.c for auditing */ | |
36 | ||
582c2af2 | 37 | #include "squid.h" |
5817ee13 | 38 | #include "auth/negotiate/auth_negotiate.h" |
3ad63615 | 39 | #include "auth/Gadgets.h" |
928f3421 | 40 | #include "auth/State.h" |
8a01b99e | 41 | #include "cache_cf.h" |
8822ebee | 42 | #include "mgr/Registration.h" |
6bf4f823 | 43 | #include "Store.h" |
44 | #include "client_side.h" | |
a5bac1d2 | 45 | #include "HttpHeaderTools.h" |
6bf4f823 | 46 | #include "HttpReply.h" |
47 | #include "HttpRequest.h" | |
cc192b50 | 48 | #include "SquidTime.h" |
616cfc4c | 49 | #include "auth/negotiate/Scheme.h" |
aa110616 | 50 | #include "auth/negotiate/User.h" |
616cfc4c | 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 |
6bf4f823 | 69 | static hash_table *proxy_auth_cache = NULL; |
70 | ||
71 | /* | |
72 | * | |
73 | * Private Functions | |
74 | * | |
75 | */ | |
76 | ||
0bcb6908 | 77 | void |
372fccd6 | 78 | Auth::Negotiate::Config::rotateHelpers() |
0bcb6908 AJ |
79 | { |
80 | /* schedule closure of existing helpers */ | |
81 | if (negotiateauthenticators) { | |
82 | helperStatefulShutdown(negotiateauthenticators); | |
83 | } | |
84 | ||
85 | /* NP: dynamic helper restart will ensure they start up again as needed. */ | |
86 | } | |
87 | ||
6bf4f823 | 88 | void |
372fccd6 | 89 | Auth::Negotiate::Config::done() |
6bf4f823 | 90 | { |
6bf4f823 | 91 | authnegotiate_initialised = 0; |
92 | ||
5817ee13 AJ |
93 | if (negotiateauthenticators) { |
94 | helperStatefulShutdown(negotiateauthenticators); | |
5817ee13 | 95 | } |
6bf4f823 | 96 | |
6bf4f823 | 97 | if (!shutting_down) |
98 | return; | |
99 | ||
48d54e4d | 100 | delete negotiateauthenticators; |
6bf4f823 | 101 | negotiateauthenticators = NULL; |
102 | ||
58ee2093 AJ |
103 | if (authenticateProgram) |
104 | wordlistDestroy(&authenticateProgram); | |
cdabe87d | 105 | |
372fccd6 | 106 | debugs(29, DBG_IMPORTANT, "Reconfigure: Negotiate authentication configuration cleared."); |
6bf4f823 | 107 | } |
108 | ||
109 | void | |
372fccd6 | 110 | Auth::Negotiate::Config::dump(StoreEntry * entry, const char *name, Auth::Config * scheme) |
6bf4f823 | 111 | { |
58ee2093 | 112 | wordlist *list = authenticateProgram; |
6bf4f823 | 113 | storeAppendPrintf(entry, "%s %s", name, "negotiate"); |
114 | ||
115 | while (list != NULL) { | |
116 | storeAppendPrintf(entry, " %s", list->key); | |
117 | list = list->next; | |
118 | } | |
119 | ||
404cfda1 AJ |
120 | storeAppendPrintf(entry, "\n%s negotiate children %d startup=%d idle=%d concurrency=%d\n", |
121 | name, authenticateChildren.n_max, authenticateChildren.n_startup, authenticateChildren.n_idle, authenticateChildren.concurrency); | |
6bf4f823 | 122 | storeAppendPrintf(entry, "%s %s keep_alive %s\n", name, "negotiate", keep_alive ? "on" : "off"); |
123 | ||
124 | } | |
125 | ||
372fccd6 | 126 | Auth::Negotiate::Config::Config() : keep_alive(1) |
6bf4f823 | 127 | { } |
128 | ||
129 | void | |
372fccd6 | 130 | Auth::Negotiate::Config::parse(Auth::Config * scheme, int n_configured, char *param_str) |
6bf4f823 | 131 | { |
132 | if (strcasecmp(param_str, "program") == 0) { | |
58ee2093 AJ |
133 | if (authenticateProgram) |
134 | wordlistDestroy(&authenticateProgram); | |
6bf4f823 | 135 | |
58ee2093 | 136 | parse_wordlist(&authenticateProgram); |
6bf4f823 | 137 | |
58ee2093 | 138 | requirePathnameExists("auth_param negotiate program", authenticateProgram->key); |
6bf4f823 | 139 | } else if (strcasecmp(param_str, "children") == 0) { |
48d54e4d | 140 | authenticateChildren.parseConfig(); |
6bf4f823 | 141 | } else if (strcasecmp(param_str, "keep_alive") == 0) { |
142 | parse_onoff(&keep_alive); | |
143 | } else { | |
372fccd6 | 144 | debugs(29, DBG_CRITICAL, "ERROR: unrecognised Negotiate auth scheme parameter '" << param_str << "'"); |
6bf4f823 | 145 | } |
6bf4f823 | 146 | } |
147 | ||
148 | const char * | |
372fccd6 | 149 | Auth::Negotiate::Config::type() const |
6bf4f823 | 150 | { |
d6374be6 | 151 | return Auth::Negotiate::Scheme::GetInstance()->type(); |
6bf4f823 | 152 | } |
153 | ||
63be0a78 | 154 | /** |
155 | * Initialize helpers and the like for this auth scheme. | |
156 | * Called AFTER parsing the config file | |
157 | */ | |
6bf4f823 | 158 | void |
372fccd6 | 159 | Auth::Negotiate::Config::init(Auth::Config * scheme) |
6bf4f823 | 160 | { |
58ee2093 | 161 | if (authenticateProgram) { |
81425fb6 | 162 | |
6bf4f823 | 163 | authnegotiate_initialised = 1; |
164 | ||
165 | if (negotiateauthenticators == NULL) | |
48d54e4d | 166 | negotiateauthenticators = new statefulhelper("negotiateauthenticator"); |
6bf4f823 | 167 | |
168 | if (!proxy_auth_cache) | |
30abd221 | 169 | proxy_auth_cache = hash_create((HASHCMP *) strcmp, 7921, hash_string); |
6bf4f823 | 170 | |
171 | assert(proxy_auth_cache); | |
172 | ||
58ee2093 | 173 | negotiateauthenticators->cmdline = authenticateProgram; |
6bf4f823 | 174 | |
1af735c7 | 175 | negotiateauthenticators->childs.updateLimits(authenticateChildren); |
6bf4f823 | 176 | |
177 | negotiateauthenticators->ipc_type = IPC_STREAM; | |
178 | ||
179 | helperStatefulOpenServers(negotiateauthenticators); | |
6bf4f823 | 180 | } |
181 | } | |
182 | ||
62ee09ca | 183 | void |
372fccd6 | 184 | Auth::Negotiate::Config::registerWithCacheManager(void) |
62ee09ca | 185 | { |
8822ebee | 186 | Mgr::RegisterAction("negotiateauthenticator", |
d9fc6862 A |
187 | "Negotiate User Authenticator Stats", |
188 | authenticateNegotiateStats, 0, 1); | |
62ee09ca | 189 | } |
190 | ||
6bf4f823 | 191 | bool |
372fccd6 | 192 | Auth::Negotiate::Config::active() const |
6bf4f823 | 193 | { |
194 | return authnegotiate_initialised == 1; | |
195 | } | |
196 | ||
197 | bool | |
372fccd6 | 198 | Auth::Negotiate::Config::configured() const |
6bf4f823 | 199 | { |
58ee2093 | 200 | if (authenticateProgram && (authenticateChildren.n_max != 0)) { |
372fccd6 | 201 | debugs(29, 9, HERE << "returning configured"); |
6bf4f823 | 202 | return true; |
203 | } | |
204 | ||
372fccd6 | 205 | debugs(29, 9, HERE << "returning unconfigured"); |
6bf4f823 | 206 | return false; |
207 | } | |
208 | ||
209 | /* Negotiate Scheme */ | |
6bf4f823 | 210 | |
211 | void | |
c7baff40 | 212 | Auth::Negotiate::Config::fixHeader(Auth::UserRequest::Pointer auth_user_request, HttpReply *rep, http_hdr_type reqType, HttpRequest * request) |
6bf4f823 | 213 | { |
58ee2093 | 214 | if (!authenticateProgram) |
6bf4f823 | 215 | return; |
216 | ||
81425fb6 | 217 | /* Need keep-alive */ |
450fe1cb | 218 | if (!request->flags.proxyKeepalive && request->flags.mustKeepalive) |
26ac0430 | 219 | return; |
81425fb6 | 220 | |
6bf4f823 | 221 | /* New request, no user details */ |
222 | if (auth_user_request == NULL) { | |
372fccd6 | 223 | debugs(29, 9, HERE << "Sending type:" << reqType << " header: 'Negotiate'"); |
076df709 | 224 | httpHeaderPutStrf(&rep->header, reqType, "Negotiate"); |
6bf4f823 | 225 | |
226 | if (!keep_alive) { | |
227 | /* drop the connection */ | |
a9925b40 | 228 | rep->header.delByName("keep-alive"); |
450fe1cb | 229 | request->flags.proxyKeepalive = 0; |
6bf4f823 | 230 | } |
231 | } else { | |
c7baff40 | 232 | Auth::Negotiate::UserRequest *negotiate_request = dynamic_cast<Auth::Negotiate::UserRequest *>(auth_user_request.getRaw()); |
27da7c21 | 233 | assert(negotiate_request != NULL); |
234 | ||
d232141d | 235 | switch (negotiate_request->user()->credentials()) { |
6bf4f823 | 236 | |
d87154ee | 237 | case Auth::Failed: |
6bf4f823 | 238 | /* here it makes sense to drop the connection, as auth is |
239 | * tied to it, even if MAYBE the client could handle it - Kinkie */ | |
a9925b40 | 240 | rep->header.delByName("keep-alive"); |
450fe1cb | 241 | request->flags.proxyKeepalive = 0; |
6bf4f823 | 242 | /* fall through */ |
243 | ||
d87154ee | 244 | case Auth::Ok: |
6bf4f823 | 245 | /* Special case: authentication finished OK but disallowed by ACL. |
246 | * Need to start over to give the client another chance. | |
247 | */ | |
6bf4f823 | 248 | if (negotiate_request->server_blob) { |
372fccd6 | 249 | debugs(29, 9, HERE << "Sending type:" << reqType << " header: 'Negotiate " << negotiate_request->server_blob << "'"); |
076df709 | 250 | httpHeaderPutStrf(&rep->header, reqType, "Negotiate %s", negotiate_request->server_blob); |
6bf4f823 | 251 | safe_free(negotiate_request->server_blob); |
252 | } else { | |
372fccd6 | 253 | debugs(29, 9, HERE << "Connection authenticated"); |
076df709 | 254 | httpHeaderPutStrf(&rep->header, reqType, "Negotiate"); |
6bf4f823 | 255 | } |
6bf4f823 | 256 | break; |
257 | ||
d87154ee | 258 | case Auth::Unchecked: |
6bf4f823 | 259 | /* semantic change: do not drop the connection. |
260 | * 2.5 implementation used to keep it open - Kinkie */ | |
372fccd6 | 261 | debugs(29, 9, HERE << "Sending type:" << reqType << " header: 'Negotiate'"); |
076df709 | 262 | httpHeaderPutStrf(&rep->header, reqType, "Negotiate"); |
6bf4f823 | 263 | break; |
264 | ||
d87154ee | 265 | case Auth::Handshake: |
6bf4f823 | 266 | /* we're waiting for a response from the client. Pass it the blob */ |
372fccd6 | 267 | debugs(29, 9, HERE << "Sending type:" << reqType << " header: 'Negotiate " << negotiate_request->server_blob << "'"); |
076df709 | 268 | httpHeaderPutStrf(&rep->header, reqType, "Negotiate %s", negotiate_request->server_blob); |
6bf4f823 | 269 | safe_free(negotiate_request->server_blob); |
270 | break; | |
271 | ||
6bf4f823 | 272 | default: |
372fccd6 | 273 | debugs(29, DBG_CRITICAL, "ERROR: Negotiate auth fixHeader: state " << negotiate_request->user()->credentials() << "."); |
6bf4f823 | 274 | fatal("unexpected state in AuthenticateNegotiateFixErrorHeader.\n"); |
275 | } | |
276 | } | |
277 | } | |
278 | ||
6bf4f823 | 279 | static void |
280 | authenticateNegotiateStats(StoreEntry * sentry) | |
281 | { | |
81425fb6 | 282 | helperStatefulStats(sentry, negotiateauthenticators, "Negotiate Authenticator Statistics"); |
6bf4f823 | 283 | } |
284 | ||
6bf4f823 | 285 | /* |
81425fb6 | 286 | * Decode a Negotiate [Proxy-]Auth string, placing the results in the passed |
6bf4f823 | 287 | * Auth_user structure. |
288 | */ | |
c7baff40 | 289 | Auth::UserRequest::Pointer |
372fccd6 | 290 | Auth::Negotiate::Config::decode(char const *proxy_auth) |
6bf4f823 | 291 | { |
df2eb1a3 | 292 | Auth::Negotiate::User *newUser = new Auth::Negotiate::User(Auth::Config::Find("negotiate")); |
c7baff40 | 293 | Auth::UserRequest *auth_user_request = new Auth::Negotiate::UserRequest(); |
6bf4f823 | 294 | assert(auth_user_request->user() == NULL); |
a33a428a | 295 | |
6bf4f823 | 296 | auth_user_request->user(newUser); |
616cfc4c | 297 | auth_user_request->user()->auth_type = Auth::AUTH_NEGOTIATE; |
6bf4f823 | 298 | |
81425fb6 | 299 | /* all we have to do is identify that it's Negotiate - the helper does the rest */ |
372fccd6 | 300 | debugs(29, 9, HERE << "decode Negotiate authentication"); |
6bf4f823 | 301 | return auth_user_request; |
302 | } |