]>
Commit | Line | Data |
---|---|---|
ca02e0ec | 1 | /* |
4ac4a490 | 2 | * Copyright (C) 1996-2017 The Squid Software Foundation and contributors |
ca02e0ec AJ |
3 | * |
4 | * Squid software is distributed under GPLv2+ license and includes | |
5 | * contributions from numerous individuals and organizations. | |
6 | * Please see the COPYING and CONTRIBUTORS files for details. | |
7 | */ | |
8 | ||
94439e4e | 9 | /* |
10 | * (C) 2000 Francesco Chemolli <kinkie@kame.usr.dsi.unimi.it> | |
11 | * Distributed freely under the terms of the GNU General Public License, | |
6c066355 | 12 | * version 2 or later. See the file COPYING for licensing details |
94439e4e | 13 | * |
14 | * This program is distributed in the hope that it will be useful, | |
15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
17 | * GNU General Public License for more details. | |
26ac0430 | 18 | |
94439e4e | 19 | * You should have received a copy of the GNU General Public License |
20 | * along with this program; if not, write to the Free Software | |
21 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA. | |
94439e4e | 22 | */ |
ca02e0ec | 23 | |
f7f3304a | 24 | #include "squid.h" |
25f98340 | 25 | #include "base64.h" |
59a09b98 | 26 | #include "compat/debug.h" |
079b1d0f | 27 | #include "helper/protocol_defines.h" |
7c16470c AJ |
28 | #include "ntlmauth/ntlmauth.h" |
29 | #include "ntlmauth/support_bits.cci" | |
30 | #include "rfcnb/rfcnb.h" | |
31 | #include "smblib/smblib.h" | |
dc47f531 | 32 | |
074d6a40 AJ |
33 | #include <cassert> |
34 | #include <cctype> | |
35 | #include <cerrno> | |
36 | #include <csignal> | |
37 | #include <cstdlib> | |
38 | #include <cstring> | |
39 | #include <ctime> | |
1dcf61eb AJ |
40 | #if HAVE_UNISTD_H |
41 | #include <unistd.h> | |
42 | #endif | |
94439e4e | 43 | #if HAVE_GETOPT_H |
44 | #include <getopt.h> | |
45 | #endif | |
32d002cb | 46 | #if HAVE_UNISTD_H |
45b635fa | 47 | #include <unistd.h> |
48 | #endif | |
1dcf61eb | 49 | |
1dcf61eb AJ |
50 | /************* CONFIGURATION ***************/ |
51 | ||
52 | #define DEAD_DC_RETRY_INTERVAL 30 | |
53 | ||
54 | /************* END CONFIGURATION ***************/ | |
55 | ||
56 | /* A couple of harmless helper macros */ | |
57 | #define SEND(X) debug("sending '%s' to squid\n",X); printf(X "\n"); | |
58 | #ifdef __GNUC__ | |
59 | #define SEND2(X,Y...) debug("sending '" X "' to squid\n",Y); printf(X "\n",Y); | |
60 | #define SEND3(X,Y...) debug("sending '" X "' to squid\n",Y); printf(X "\n",Y); | |
61 | #else | |
62 | /* no gcc, no debugging. varargs macros are a gcc extension */ | |
63 | #define SEND2 printf | |
64 | #define SEND3 printf | |
65 | #endif | |
66 | ||
67 | const char *make_challenge(char *domain, char *controller); | |
68 | char *ntlm_check_auth(ntlm_authenticate * auth, int auth_length); | |
69 | void dc_disconnect(void); | |
70 | int connectedp(void); | |
71 | int is_dc_ok(char *domain, char *domain_controller); | |
72 | ||
73 | typedef struct _dc dc; | |
74 | struct _dc { | |
75 | char *domain; | |
76 | char *controller; | |
f53969cc | 77 | time_t dead; /* 0 if it's alive, otherwise time of death */ |
1dcf61eb AJ |
78 | dc *next; |
79 | }; | |
94439e4e | 80 | |
a19a836d | 81 | /* local functions */ |
a19a836d AJ |
82 | void usage(void); |
83 | void process_options(int argc, char *argv[]); | |
84 | const char * obtain_challenge(void); | |
85 | void manage_request(void); | |
86 | ||
1dcf61eb AJ |
87 | #define ENCODED_PASS_LEN 24 |
88 | #define MAX_USERNAME_LEN 255 | |
89 | #define MAX_DOMAIN_LEN 255 | |
90 | #define MAX_PASSWD_LEN 31 | |
91 | ||
92 | static unsigned char challenge[NTLM_NONCE_LEN]; | |
93 | static unsigned char lmencoded_empty_pass[ENCODED_PASS_LEN], | |
f53969cc | 94 | ntencoded_empty_pass[ENCODED_PASS_LEN]; |
1dcf61eb AJ |
95 | SMB_Handle_Type handle = NULL; |
96 | int ntlm_errno; | |
f53969cc | 97 | static char credentials[MAX_USERNAME_LEN+MAX_DOMAIN_LEN+2]; /* we can afford to waste */ |
1dcf61eb AJ |
98 | static char my_domain[100], my_domain_controller[100]; |
99 | static char errstr[1001]; | |
32d002cb | 100 | #if DEBUG |
1dcf61eb | 101 | char error_messages_buffer[NTLM_BLOB_BUFFER_SIZE]; |
2d70df72 | 102 | #endif |
5d146f7d | 103 | char load_balance = 0, protocol_pedantic = 0; |
94439e4e | 104 | dc *controllers = NULL; |
105 | int numcontrollers = 0; | |
106 | dc *current_dc; | |
2d70df72 | 107 | char smb_error_buffer[1000]; |
108 | ||
1dcf61eb AJ |
109 | /* Disconnects from the DC. A reconnection will be done upon the next request |
110 | */ | |
111 | void | |
112 | dc_disconnect() | |
9bea1d5b | 113 | { |
1dcf61eb AJ |
114 | if (handle != NULL) |
115 | SMB_Discon(handle, 0); | |
116 | handle = NULL; | |
94439e4e | 117 | } |
118 | ||
1dcf61eb AJ |
119 | int |
120 | connectedp() | |
94439e4e | 121 | { |
1dcf61eb AJ |
122 | return (handle != NULL); |
123 | } | |
124 | ||
125 | /* Tries to connect to a DC. Returns 0 on failure, 1 on OK */ | |
126 | int | |
127 | is_dc_ok(char *domain, char *domain_controller) | |
128 | { | |
129 | SMB_Handle_Type h = SMB_Connect_Server(NULL, domain_controller, domain); | |
130 | if (h == NULL) | |
131 | return 0; | |
132 | SMB_Discon(h, 0); | |
133 | return 1; | |
134 | } | |
135 | ||
136 | /* returns 0 on success, > 0 on failure */ | |
137 | static int | |
138 | init_challenge(char *domain, char *domain_controller) | |
139 | { | |
140 | int smberr; | |
141 | ||
142 | if (handle != NULL) { | |
143 | return 0; | |
94439e4e | 144 | } |
1dcf61eb AJ |
145 | debug("Connecting to server %s domain %s\n", domain_controller, domain); |
146 | handle = SMB_Connect_Server(NULL, domain_controller, domain); | |
147 | smberr = SMB_Get_Last_Error(); | |
148 | SMB_Get_Error_Msg(smberr, errstr, 1000); | |
149 | ||
f53969cc | 150 | if (handle == NULL) { /* couldn't connect */ |
1dcf61eb AJ |
151 | debug("Couldn't connect to SMB Server. Error:%s\n", errstr); |
152 | return 1; | |
153 | } | |
f53969cc | 154 | if (SMB_Negotiate(handle, SMB_Prots) < 0) { /* An error */ |
1dcf61eb AJ |
155 | debug("Error negotiating protocol with SMB Server\n"); |
156 | SMB_Discon(handle, 0); | |
157 | handle = NULL; | |
158 | return 2; | |
159 | } | |
f53969cc | 160 | if (handle->Security == 0) { /* share-level security, unuseable */ |
1dcf61eb AJ |
161 | debug("SMB Server uses share-level security .. we need user security.\n"); |
162 | SMB_Discon(handle, 0); | |
163 | handle = NULL; | |
164 | return 3; | |
165 | } | |
166 | memcpy(challenge, handle->Encrypt_Key, NTLM_NONCE_LEN); | |
167 | SMBencrypt((unsigned char *)"",challenge,lmencoded_empty_pass); | |
168 | SMBNTencrypt((unsigned char *)"",challenge,ntencoded_empty_pass); | |
169 | return 0; | |
94439e4e | 170 | } |
171 | ||
1dcf61eb AJ |
172 | const char * |
173 | make_challenge(char *domain, char *domain_controller) | |
94439e4e | 174 | { |
1dcf61eb AJ |
175 | /* trying to circumvent some strange problem wih pointers in SMBLib */ |
176 | /* Ugly as hell, but the lib is going to be dropped... */ | |
34107f7c AJ |
177 | strncpy(my_domain, domain, sizeof(my_domain)-1); |
178 | my_domain[sizeof(my_domain)-1] = '\0'; | |
179 | strncpy(my_domain_controller, domain_controller, sizeof(my_domain_controller)-1); | |
180 | my_domain_controller[sizeof(my_domain_controller)-1] = '\0'; | |
181 | ||
1dcf61eb AJ |
182 | if (init_challenge(my_domain, my_domain_controller) > 0) { |
183 | return NULL; | |
184 | } | |
aadbbd7d | 185 | |
1dcf61eb | 186 | ntlm_challenge chal; |
09aabd84 FC |
187 | uint32_t flags = NTLM_REQUEST_NON_NT_SESSION_KEY | |
188 | NTLM_CHALLENGE_TARGET_IS_DOMAIN | | |
189 | NTLM_NEGOTIATE_ALWAYS_SIGN | | |
190 | NTLM_NEGOTIATE_USE_NTLM | | |
191 | NTLM_NEGOTIATE_USE_LM | | |
192 | NTLM_NEGOTIATE_ASCII; | |
1dcf61eb | 193 | ntlm_make_challenge(&chal, my_domain, my_domain_controller, (char *)challenge, NTLM_NONCE_LEN, flags); |
aadbbd7d AJ |
194 | |
195 | size_t len = sizeof(chal) - sizeof(chal.payload) + le16toh(chal.target.maxlen); | |
196 | // for lack of a good NTLM token size limit, allow up to what the helper input can be | |
197 | // validations later will expect to be limited to that size. | |
198 | static uint8_t b64buf[HELPER_INPUT_BUFFER-10]; /* 10 for other line fields, delimiters and terminator */ | |
199 | if (base64_encode_len(len) < sizeof(b64buf)-1) { | |
54835620 | 200 | debug("base64 encoding of the token challenge will exceed %" PRIuSIZE " bytes", sizeof(b64buf)); |
aadbbd7d AJ |
201 | return NULL; |
202 | } | |
203 | ||
204 | struct base64_encode_ctx ctx; | |
205 | base64_encode_init(&ctx); | |
206 | size_t blen = base64_encode_update(&ctx, b64buf, len, reinterpret_cast<const uint8_t *>(&chal)); | |
207 | blen += base64_encode_final(&ctx, b64buf+blen); | |
208 | b64buf[blen] = '\0'; | |
209 | return reinterpret_cast<const char*>(b64buf); | |
1dcf61eb AJ |
210 | } |
211 | ||
212 | /* returns NULL on failure, or a pointer to | |
213 | * the user's credentials (domain\\username) | |
214 | * upon success. WARNING. It's pointing to static storage. | |
215 | * In case of problem sets as side-effect ntlm_errno to one of the | |
216 | * codes defined in ntlm.h | |
217 | */ | |
218 | char * | |
219 | ntlm_check_auth(ntlm_authenticate * auth, int auth_length) | |
220 | { | |
221 | int rv; | |
222 | char pass[MAX_PASSWD_LEN+1]; | |
223 | char *domain = credentials; | |
224 | char *user; | |
225 | lstring tmp; | |
226 | ||
f53969cc | 227 | if (handle == NULL) { /*if null we aren't connected, but it shouldn't happen */ |
1dcf61eb AJ |
228 | debug("Weird, we've been disconnected\n"); |
229 | ntlm_errno = NTLM_ERR_NOT_CONNECTED; | |
230 | return NULL; | |
231 | } | |
232 | ||
233 | /* debug("fetching domain\n"); */ | |
234 | tmp = ntlm_fetch_string(&(auth->hdr), auth_length, &auth->domain, auth->flags); | |
235 | if (tmp.str == NULL || tmp.l == 0) { | |
236 | debug("No domain supplied. Returning no-auth\n"); | |
237 | ntlm_errno = NTLM_ERR_LOGON; | |
238 | return NULL; | |
239 | } | |
240 | if (tmp.l > MAX_DOMAIN_LEN) { | |
241 | debug("Domain string exceeds %d bytes, rejecting\n", MAX_DOMAIN_LEN); | |
242 | ntlm_errno = NTLM_ERR_LOGON; | |
243 | return NULL; | |
244 | } | |
245 | memcpy(domain, tmp.str, tmp.l); | |
246 | user = domain + tmp.l; | |
f207fe64 FC |
247 | *user = '\0'; |
248 | ++user; | |
1dcf61eb AJ |
249 | |
250 | /* debug("fetching user name\n"); */ | |
251 | tmp = ntlm_fetch_string(&(auth->hdr), auth_length, &auth->user, auth->flags); | |
252 | if (tmp.str == NULL || tmp.l == 0) { | |
253 | debug("No username supplied. Returning no-auth\n"); | |
254 | ntlm_errno = NTLM_ERR_LOGON; | |
255 | return NULL; | |
256 | } | |
257 | if (tmp.l > MAX_USERNAME_LEN) { | |
258 | debug("Username string exceeds %d bytes, rejecting\n", MAX_USERNAME_LEN); | |
259 | ntlm_errno = NTLM_ERR_LOGON; | |
260 | return NULL; | |
261 | } | |
262 | memcpy(user, tmp.str, tmp.l); | |
263 | *(user + tmp.l) = '\0'; | |
264 | ||
47567b73 AJ |
265 | // grab the *response blobs. these are fixed length 24 bytes of binary |
266 | const ntlmhdr *packet = &(auth->hdr); | |
267 | { | |
268 | const strhdr * str = &auth->lmresponse; | |
269 | ||
270 | int16_t len = le16toh(str->len); | |
271 | int32_t offset = le32toh(str->offset); | |
272 | ||
273 | if (len != ENCODED_PASS_LEN || offset + len > auth_length || offset == 0) { | |
274 | debug("LM response: insane data (pkt-sz: %d, fetch len: %d, offset: %d)\n", auth_length, len, offset); | |
275 | ntlm_errno = NTLM_ERR_LOGON; | |
276 | return NULL; | |
277 | } | |
278 | tmp.str = (char *)packet + offset; | |
279 | tmp.l = len; | |
94439e4e | 280 | } |
1dcf61eb AJ |
281 | if (tmp.l > MAX_PASSWD_LEN) { |
282 | debug("Password string exceeds %d bytes, rejecting\n", MAX_PASSWD_LEN); | |
283 | ntlm_errno = NTLM_ERR_LOGON; | |
284 | return NULL; | |
285 | } | |
286 | ||
47567b73 | 287 | /* Authenticating against the NT response doesn't seem to work... in SMB LM helper. */ |
1dcf61eb AJ |
288 | memcpy(pass, tmp.str, tmp.l); |
289 | pass[min(MAX_PASSWD_LEN,tmp.l)] = '\0'; | |
290 | ||
1dcf61eb AJ |
291 | debug("Empty LM pass detection: user: '%s', ours:'%s', his: '%s' (length: %d)\n", |
292 | user,lmencoded_empty_pass,tmp.str,tmp.l); | |
293 | if (memcmp(tmp.str,lmencoded_empty_pass,ENCODED_PASS_LEN)==0) { | |
294 | fprintf(stderr,"Empty LM password supplied for user %s\\%s. " | |
295 | "No-auth\n",domain,user); | |
296 | ntlm_errno=NTLM_ERR_LOGON; | |
297 | return NULL; | |
298 | } | |
299 | ||
47567b73 AJ |
300 | /* still fetch the NT response and check validity against empty password */ |
301 | { | |
302 | const strhdr * str = &auth->ntresponse; | |
303 | int16_t len = le16toh(str->len); | |
53ae090f AJ |
304 | // NT response field may be absent. that is okay. |
305 | if (len != 0) { | |
306 | int32_t offset = le32toh(str->offset); | |
307 | ||
308 | if (len != ENCODED_PASS_LEN || offset + len > auth_length || offset == 0) { | |
309 | debug("NT response: insane data (pkt-sz: %d, fetch len: %d, offset: %d)\n", auth_length, len, offset); | |
310 | ntlm_errno = NTLM_ERR_LOGON; | |
311 | return NULL; | |
312 | } | |
313 | tmp.str = (char *)packet + offset; | |
314 | tmp.l = len; | |
315 | ||
316 | debug("Empty NT pass detection: user: '%s', ours:'%s', his: '%s' (length: %d)\n", | |
317 | user,ntencoded_empty_pass,tmp.str,tmp.l); | |
318 | if (memcmp(tmp.str,lmencoded_empty_pass,ENCODED_PASS_LEN)==0) { | |
319 | fprintf(stderr,"ERROR: Empty NT password supplied for user %s\\%s. No-auth\n", domain, user); | |
320 | ntlm_errno = NTLM_ERR_LOGON; | |
321 | return NULL; | |
322 | } | |
1dcf61eb AJ |
323 | } |
324 | } | |
1dcf61eb | 325 | |
1dcf61eb AJ |
326 | debug("checking domain: '%s', user: '%s', pass='%s'\n", domain, user, pass); |
327 | ||
328 | rv = SMB_Logon_Server(handle, user, pass, domain, 1); | |
329 | debug("Login attempt had result %d\n", rv); | |
330 | ||
f53969cc | 331 | if (rv != NTLM_ERR_NONE) { /* failed */ |
1dcf61eb AJ |
332 | ntlm_errno = rv; |
333 | return NULL; | |
334 | } | |
f53969cc | 335 | *(user - 1) = '\\'; /* hack. Performing, but ugly. */ |
1dcf61eb AJ |
336 | |
337 | debug("credentials: %s\n", credentials); | |
338 | return credentials; | |
94439e4e | 339 | } |
340 | ||
daacd51f AJ |
341 | extern "C" void timeout_during_auth(int signum); |
342 | ||
1dcf61eb | 343 | static char got_timeout = 0; |
daacd51f AJ |
344 | /** signal handler to be invoked when the authentication operation |
345 | * times out */ | |
346 | void | |
ced8def3 | 347 | timeout_during_auth(int) |
1dcf61eb AJ |
348 | { |
349 | dc_disconnect(); | |
350 | } | |
2d70df72 | 351 | |
94439e4e | 352 | /* |
353 | * options: | |
354 | * -b try load-balancing the domain-controllers | |
355 | * -f fail-over to another DC if DC connection fails. | |
5d146f7d | 356 | * DEPRECATED and VERBOSELY IGNORED. This is on by default now. |
2d70df72 | 357 | * -l last-ditch-mode |
94439e4e | 358 | * domain\controller ... |
359 | */ | |
a88de9ce | 360 | char *my_program_name = NULL; |
361 | ||
362 | void | |
363 | usage() | |
364 | { | |
365 | fprintf(stderr, | |
26ac0430 AJ |
366 | "%s usage:\n%s [-b] [-f] [-d] [-l] domain\\controller [domain\\controller ...]\n" |
367 | "-b enables load-balancing among controllers\n" | |
368 | "-f enables failover among controllers (DEPRECATED and always active)\n" | |
26ac0430 AJ |
369 | "-d enables debugging statements if DEBUG was defined at build-time.\n\n" |
370 | "You MUST specify at least one Domain Controller.\n" | |
371 | "You can use either \\ or / as separator between the domain name \n" | |
372 | "and the controller name\n", | |
373 | my_program_name, my_program_name); | |
a88de9ce | 374 | } |
375 | ||
59a09b98 | 376 | /* int debug_enabled=0; defined in libcompat */ |
a88de9ce | 377 | |
94439e4e | 378 | void |
379 | process_options(int argc, char *argv[]) | |
380 | { | |
381 | int opt, j, had_error = 0; | |
382 | dc *new_dc = NULL, *last_dc = NULL; | |
3c641669 | 383 | while (-1 != (opt = getopt(argc, argv, "bfld"))) { |
26ac0430 AJ |
384 | switch (opt) { |
385 | case 'b': | |
386 | load_balance = 1; | |
387 | break; | |
388 | case 'f': | |
389 | fprintf(stderr, | |
390 | "WARNING. The -f flag is DEPRECATED and always active.\n"); | |
391 | break; | |
26ac0430 AJ |
392 | case 'd': |
393 | debug_enabled=1; | |
394 | break; | |
395 | default: | |
396 | fprintf(stderr, "unknown option: -%c. Exiting\n", opt); | |
397 | usage(); | |
398 | had_error = 1; | |
399 | } | |
94439e4e | 400 | } |
401 | if (had_error) | |
26ac0430 | 402 | exit(1); |
94439e4e | 403 | /* Okay, now begin filling controllers up */ |
404 | /* we can avoid memcpy-ing, and just reuse argv[] */ | |
755494da | 405 | for (j = optind; j < argc; ++j) { |
26ac0430 AJ |
406 | char *d, *c; |
407 | /* d will not be freed in case of non-error. Since we don't reconfigure, | |
408 | * it's going to live as long as the process anyways */ | |
c14fb378 | 409 | d = static_cast<char*>(xmalloc(strlen(argv[j]) + 1)); |
26ac0430 | 410 | strcpy(d, argv[j]); |
75aa769b | 411 | debug("Adding domain-controller %s\n", d); |
26ac0430 AJ |
412 | if (NULL == (c = strchr(d, '\\')) && NULL == (c = strchr(d, '/'))) { |
413 | fprintf(stderr, "Couldn't grok domain-controller %s\n", d); | |
414 | free(d); | |
415 | continue; | |
416 | } | |
417 | /* more than one delimiter is not allowed */ | |
418 | if (NULL != strchr(c + 1, '\\') || NULL != strchr(c + 1, '/')) { | |
419 | fprintf(stderr, "Broken domain-controller %s\n", d); | |
420 | free(d); | |
421 | continue; | |
422 | } | |
f207fe64 FC |
423 | *c= '\0'; |
424 | ++c; | |
c14fb378 | 425 | new_dc = static_cast<dc *>(xmalloc(sizeof(dc))); |
26ac0430 AJ |
426 | if (!new_dc) { |
427 | fprintf(stderr, "Malloc error while parsing DC options\n"); | |
428 | free(d); | |
429 | continue; | |
430 | } | |
431 | /* capitalize */ | |
432 | uc(c); | |
433 | uc(d); | |
755494da | 434 | ++numcontrollers; |
26ac0430 AJ |
435 | new_dc->domain = d; |
436 | new_dc->controller = c; | |
437 | new_dc->dead = 0; | |
f53969cc | 438 | if (controllers == NULL) { /* first controller */ |
26ac0430 AJ |
439 | controllers = new_dc; |
440 | last_dc = new_dc; | |
441 | } else { | |
f53969cc | 442 | last_dc->next = new_dc; /* can't be null */ |
26ac0430 AJ |
443 | last_dc = new_dc; |
444 | } | |
94439e4e | 445 | } |
446 | if (numcontrollers == 0) { | |
26ac0430 AJ |
447 | fprintf(stderr, "You must specify at least one domain-controller!\n"); |
448 | usage(); | |
449 | exit(1); | |
94439e4e | 450 | } |
f53969cc | 451 | last_dc->next = controllers; /* close the queue, now it's circular */ |
94439e4e | 452 | } |
453 | ||
a19a836d AJ |
454 | /** |
455 | * tries connecting to the domain controllers in the "controllers" ring, | |
94439e4e | 456 | * with failover if the adequate option is specified. |
457 | */ | |
458 | const char * | |
459 | obtain_challenge() | |
460 | { | |
461 | int j = 0; | |
60d096f4 | 462 | const char *ch = NULL; |
755494da | 463 | for (j = 0; j < numcontrollers; ++j) { |
75aa769b | 464 | debug("obtain_challenge: selecting %s\\%s (attempt #%d)\n", |
632d8053 | 465 | current_dc->domain, current_dc->controller, j + 1); |
26ac0430 AJ |
466 | if (current_dc->dead != 0) { |
467 | if (time(NULL) - current_dc->dead >= DEAD_DC_RETRY_INTERVAL) { | |
468 | /* mark helper as retry-worthy if it's so. */ | |
75aa769b | 469 | debug("Reviving DC\n"); |
26ac0430 | 470 | current_dc->dead = 0; |
f53969cc | 471 | } else { /* skip it */ |
75aa769b | 472 | debug("Skipping it\n"); |
26ac0430 AJ |
473 | continue; |
474 | } | |
475 | } | |
476 | /* else branch. Here we KNOW that the DC is fine */ | |
75aa769b | 477 | debug("attempting challenge retrieval\n"); |
26ac0430 | 478 | ch = make_challenge(current_dc->domain, current_dc->controller); |
75aa769b | 479 | debug("make_challenge retuned %p\n", ch); |
26ac0430 | 480 | if (ch) { |
75aa769b | 481 | debug("Got it\n"); |
f53969cc | 482 | return ch; /* All went OK, returning */ |
26ac0430 AJ |
483 | } |
484 | /* Huston, we've got a problem. Take this DC out of the loop */ | |
75aa769b | 485 | debug("Marking DC as DEAD\n"); |
26ac0430 AJ |
486 | current_dc->dead = time(NULL); |
487 | /* Try with the next */ | |
75aa769b | 488 | debug("moving on to next controller\n"); |
26ac0430 | 489 | current_dc = current_dc->next; |
94439e4e | 490 | } |
5d146f7d | 491 | /* all DCs failed. */ |
94439e4e | 492 | return NULL; |
493 | } | |
494 | ||
495 | void | |
496 | manage_request() | |
497 | { | |
498 | ntlmhdr *fast_header; | |
1dcf61eb | 499 | char buf[NTLM_BLOB_BUFFER_SIZE]; |
8bdd0cec | 500 | char decoded[NTLM_BLOB_BUFFER_SIZE]; |
8bdd0cec | 501 | char *ch2, *cred = NULL; |
94439e4e | 502 | |
1dcf61eb | 503 | if (fgets(buf, NTLM_BLOB_BUFFER_SIZE, stdin) == NULL) { |
26ac0430 AJ |
504 | fprintf(stderr, "fgets() failed! dying..... errno=%d (%s)\n", errno, |
505 | strerror(errno)); | |
f53969cc | 506 | exit(1); /* BIIG buffer */ |
6437ac71 | 507 | } |
75aa769b | 508 | debug("managing request\n"); |
f53969cc | 509 | ch2 = (char*)memchr(buf, '\n', NTLM_BLOB_BUFFER_SIZE); /* safer against overrun than strchr */ |
94439e4e | 510 | if (ch2) { |
f53969cc | 511 | *ch2 = '\0'; /* terminate the string at newline. */ |
94439e4e | 512 | } |
75aa769b | 513 | debug("ntlm authenticator. Got '%s' from Squid\n", buf); |
94439e4e | 514 | |
f53969cc | 515 | if (memcmp(buf, "KK ", 3) == 0) { /* authenticate-request */ |
26ac0430 | 516 | /* figure out what we got */ |
aadbbd7d AJ |
517 | struct base64_decode_ctx ctx; |
518 | base64_decode_init(&ctx); | |
519 | size_t dstLen = 0; | |
520 | int decodedLen = 0; | |
521 | if (!base64_decode_update(&ctx, &dstLen, reinterpret_cast<uint8_t*>(decoded), strlen(buf)-3, reinterpret_cast<const uint8_t*>(buf+3)) || | |
522 | !base64_decode_final(&ctx)) { | |
523 | SEND("NA Packet format error, couldn't base64-decode"); | |
524 | return; | |
525 | } | |
526 | decodedLen = dstLen; | |
26ac0430 | 527 | |
f53969cc | 528 | if ((size_t)decodedLen < sizeof(ntlmhdr)) { /* decoding failure, return error */ |
aadbbd7d | 529 | SEND("NA Packet format error, truncated packet header."); |
26ac0430 AJ |
530 | return; |
531 | } | |
532 | /* fast-track-decode request type. */ | |
1dcf61eb | 533 | fast_header = (ntlmhdr *) decoded; |
26ac0430 AJ |
534 | |
535 | /* sanity-check: it IS a NTLMSSP packet, isn't it? */ | |
1dcf61eb | 536 | if (ntlm_validate_packet(fast_header, NTLM_ANY) < 0) { |
26ac0430 AJ |
537 | SEND("NA Broken authentication packet"); |
538 | return; | |
539 | } | |
540 | switch (le32toh(fast_header->type)) { | |
541 | case NTLM_NEGOTIATE: | |
542 | SEND("NA Invalid negotiation request received"); | |
543 | return; | |
f53969cc | 544 | /* notreached */ |
26ac0430 | 545 | case NTLM_CHALLENGE: |
83ae34bc | 546 | SEND("NA Got a challenge. We refuse to have our authority disputed"); |
26ac0430 | 547 | return; |
f53969cc | 548 | /* notreached */ |
26ac0430 AJ |
549 | case NTLM_AUTHENTICATE: |
550 | /* check against the DC */ | |
26ac0430 AJ |
551 | signal(SIGALRM, timeout_during_auth); |
552 | alarm(30); | |
8bdd0cec | 553 | cred = ntlm_check_auth((ntlm_authenticate *) decoded, decodedLen); |
26ac0430 AJ |
554 | alarm(0); |
555 | signal(SIGALRM, SIG_DFL); | |
556 | if (got_timeout != 0) { | |
557 | fprintf(stderr, "ntlm-auth[%ld]: Timeout during authentication.\n", (long)getpid()); | |
558 | SEND("BH Timeout during authentication"); | |
559 | got_timeout = 0; | |
560 | return; | |
561 | } | |
562 | if (cred == NULL) { | |
563 | int smblib_err, smb_errorclass, smb_errorcode, nb_error; | |
f53969cc | 564 | if (ntlm_errno == NTLM_ERR_LOGON) { /* hackish */ |
26ac0430 AJ |
565 | SEND("NA Logon Failure"); |
566 | return; | |
567 | } | |
568 | /* there was an error. We have two errno's to look at. | |
569 | * libntlmssp's erno is insufficient, we'll have to look at | |
570 | * the actual SMB library error codes, to acually figure | |
571 | * out what's happening. The thing has braindamaged interfacess..*/ | |
572 | smblib_err = SMB_Get_Last_Error(); | |
573 | smb_errorclass = SMBlib_Error_Class(SMB_Get_Last_SMB_Err()); | |
574 | smb_errorcode = SMBlib_Error_Code(SMB_Get_Last_SMB_Err()); | |
575 | nb_error = RFCNB_Get_Last_Error(); | |
75aa769b AJ |
576 | debug("No creds. SMBlib error %d, SMB error class %d, SMB error code %d, NB error %d\n", |
577 | smblib_err, smb_errorclass, smb_errorcode, nb_error); | |
26ac0430 AJ |
578 | /* Should I use smblib_err? Actually it seems I can do as well |
579 | * without it.. */ | |
f53969cc | 580 | if (nb_error != 0) { /* netbios-level error */ |
e490d2a3 | 581 | SEND("BH NetBios error!"); |
26ac0430 AJ |
582 | fprintf(stderr, "NetBios error code %d (%s)\n", nb_error, |
583 | RFCNB_Error_Strings[abs(nb_error)]); | |
584 | return; | |
585 | } | |
586 | switch (smb_errorclass) { | |
587 | case SMBC_SUCCESS: | |
75aa769b | 588 | debug("Huh? Got a SMB success code but could check auth.."); |
26ac0430 | 589 | SEND("NA Authentication failed"); |
26ac0430 AJ |
590 | return; |
591 | case SMBC_ERRDOS: | |
592 | /*this is the most important one for errors */ | |
75aa769b | 593 | debug("DOS error\n"); |
26ac0430 | 594 | switch (smb_errorcode) { |
f53969cc SM |
595 | /* two categories matter to us: those which could be |
596 | * server errors, and those which are auth errors */ | |
597 | case SMBD_noaccess: /* 5 */ | |
26ac0430 AJ |
598 | SEND("NA Access denied"); |
599 | return; | |
600 | case SMBD_badformat: | |
601 | SEND("NA bad format in authentication packet"); | |
602 | return; | |
603 | case SMBD_badaccess: | |
604 | SEND("NA Bad access request"); | |
605 | return; | |
606 | case SMBD_baddata: | |
607 | SEND("NA Bad Data"); | |
608 | return; | |
609 | default: | |
e490d2a3 | 610 | SEND("BH DOS Error"); |
26ac0430 AJ |
611 | return; |
612 | } | |
f53969cc | 613 | case SMBC_ERRSRV: /* server errors */ |
75aa769b | 614 | debug("Server error"); |
26ac0430 | 615 | switch (smb_errorcode) { |
f53969cc | 616 | /* mostly same as above */ |
26ac0430 AJ |
617 | case SMBV_badpw: |
618 | SEND("NA Bad password"); | |
619 | return; | |
620 | case SMBV_access: | |
621 | SEND("NA Server access error"); | |
622 | return; | |
623 | default: | |
e490d2a3 | 624 | SEND("BH Server Error"); |
26ac0430 AJ |
625 | return; |
626 | } | |
f53969cc | 627 | case SMBC_ERRHRD: /* hardware errors don't really matter */ |
e490d2a3 | 628 | SEND("BH Domain Controller Hardware error"); |
26ac0430 AJ |
629 | return; |
630 | case SMBC_ERRCMD: | |
e490d2a3 | 631 | SEND("BH Domain Controller Command Error"); |
26ac0430 AJ |
632 | return; |
633 | } | |
83ae34bc AJ |
634 | SEND("BH unknown internal error."); |
635 | return; | |
26ac0430 | 636 | } |
83ae34bc | 637 | |
f53969cc | 638 | lc(cred); /* let's lowercase them for our convenience */ |
26ac0430 AJ |
639 | SEND2("AF %s", cred); |
640 | return; | |
641 | default: | |
642 | SEND("BH unknown authentication packet type"); | |
643 | return; | |
644 | } | |
7c572fcd | 645 | /* notreached */ |
26ac0430 | 646 | return; |
94439e4e | 647 | } |
f53969cc | 648 | if (memcmp(buf, "YR", 2) == 0) { /* refresh-request */ |
26ac0430 | 649 | dc_disconnect(); |
78a474a9 | 650 | const char *ch = obtain_challenge(); |
26ac0430 AJ |
651 | /* Robert says we can afford to wait forever. I'll trust him on this |
652 | * one */ | |
653 | while (ch == NULL) { | |
654 | sleep(30); | |
655 | ch = obtain_challenge(); | |
656 | } | |
657 | SEND2("TT %s", ch); | |
658 | return; | |
94439e4e | 659 | } |
660 | SEND("BH Helper detected protocol error"); | |
661 | return; | |
26ac0430 | 662 | /********* END ********/ |
94439e4e | 663 | |
94439e4e | 664 | } |
665 | ||
666 | int | |
667 | main(int argc, char *argv[]) | |
668 | { | |
a20277e1 | 669 | debug("%s " VERSION " " SQUID_BUILD_INFO " starting up...\n", argv[0]); |
94439e4e | 670 | |
a88de9ce | 671 | my_program_name = argv[0]; |
94439e4e | 672 | process_options(argc, argv); |
673 | ||
75aa769b | 674 | debug("options processed OK\n"); |
94439e4e | 675 | |
676 | /* initialize FDescs */ | |
677 | setbuf(stdout, NULL); | |
678 | setbuf(stderr, NULL); | |
679 | ||
680 | /* select the first domain controller we're going to use */ | |
681 | current_dc = controllers; | |
682 | if (load_balance != 0 && numcontrollers > 1) { | |
26ac0430 AJ |
683 | int n; |
684 | pid_t pid = getpid(); | |
685 | n = pid % numcontrollers; | |
75aa769b | 686 | debug("load balancing. Selected controller #%d\n", n); |
26ac0430 AJ |
687 | while (n > 0) { |
688 | current_dc = current_dc->next; | |
755494da | 689 | --n; |
26ac0430 | 690 | } |
94439e4e | 691 | } |
692 | while (1) { | |
26ac0430 | 693 | manage_request(); |
94439e4e | 694 | } |
7c572fcd | 695 | /* notreached */ |
94439e4e | 696 | return 0; |
697 | } | |
f53969cc | 698 |