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