]> git.ipfire.org Git - thirdparty/squid.git/blame - helpers/ntlm_auth/smb_lm/ntlm_smb_lm_auth.c
Updated release notes
[thirdparty/squid.git] / helpers / ntlm_auth / smb_lm / ntlm_smb_lm_auth.c
CommitLineData
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 */
dc47f531 16#define SQUID_NO_ALLOC_PROTECT 1
94439e4e 17#include "config.h"
dc47f531 18
94439e4e 19#include "ntlmauth.h"
30b4b6fc 20#include "ntlm_smb_lm_auth.h"
94439e4e 21#include "util.h"
6437ac71 22#include "smbval/smblib-common.h"
23#include "smbval/rfcnb-error.h"
94439e4e 24
5d146f7d 25#include <signal.h>
5d146f7d 26
2d70df72 27/* these are part of rfcnb-priv.h and smblib-priv.h */
28extern int SMB_Get_Error_Msg(int msg, char *msgbuf, int len);
a19a836d
AJ
29extern int SMB_Get_Last_Error(void);
30extern int SMB_Get_Last_SMB_Err(void);
31extern int RFCNB_Get_Last_Error(void);
32
2d70df72 33
6437ac71 34#include <errno.h>
2d70df72 35
94439e4e 36#define BUFFER_SIZE 10240
37
38#if HAVE_STDLIB_H
39#include <stdlib.h>
40#endif
94439e4e 41#if HAVE_GETOPT_H
42#include <getopt.h>
43#endif
32d002cb 44#if HAVE_STRING_H
94439e4e 45#include <string.h>
46#endif
32d002cb 47#if HAVE_CTYPE_H
94439e4e 48#include <ctype.h>
49#endif
32d002cb 50#if HAVE_UNISTD_H
45b635fa 51#include <unistd.h>
52#endif
32d002cb 53#if HAVE_ASSERT_H
2c22dde8 54#include <assert.h>
55#endif
94439e4e 56
a19a836d
AJ
57/* local functions */
58void send_bh_or_ld(char const *bhmessage, ntlm_authenticate * failedauth, int authlen);
59void usage(void);
60void process_options(int argc, char *argv[]);
61const char * obtain_challenge(void);
62void manage_request(void);
63
64
32d002cb 65#if DEBUG
2d70df72 66char error_messages_buffer[BUFFER_SIZE];
67#endif
68
5d146f7d 69char load_balance = 0, protocol_pedantic = 0;
32d002cb 70#if NTLM_FAIL_OPEN
5d146f7d 71char last_ditch_enabled = 0;
72#endif
94439e4e 73
74dc *controllers = NULL;
75int numcontrollers = 0;
76dc *current_dc;
77
2d70df72 78char smb_error_buffer[1000];
79
5d146f7d 80/* signal handler to be invoked when the authentication operation
9bea1d5b 81 * times out */
82static char got_timeout = 0;
94439e4e 83static void
9bea1d5b 84timeout_during_auth(int signum)
85{
86 dc_disconnect();
94439e4e 87}
88
89/* makes a null-terminated string upper-case. Changes CONTENTS! */
90static void
91uc(char *string)
92{
93 char *p = string, c;
94 while ((c = *p)) {
26ac0430
AJ
95 *p = xtoupper(c);
96 p++;
94439e4e 97 }
98}
99
100/* makes a null-terminated string lower-case. Changes CONTENTS! */
101static void
102lc(char *string)
103{
104 char *p = string, c;
105 while ((c = *p)) {
26ac0430
AJ
106 *p = xtolower(c);
107 p++;
94439e4e 108 }
109}
110
2d70df72 111
112void
a19a836d 113send_bh_or_ld(char const *bhmessage, ntlm_authenticate * failedauth, int authlen)
2d70df72 114{
32d002cb 115#if NTLM_FAIL_OPEN
13bf0a40 116 char *creds = NULL;
2d70df72 117 if (last_ditch_enabled) {
26ac0430
AJ
118 creds = fetch_credentials(failedauth, authlen);
119 if (creds) {
120 lc(creds);
121 SEND2("LD %s", creds);
122 } else {
123 SEND("NA last-ditch on, but no credentials");
124 }
2d70df72 125 } else {
5d146f7d 126#endif
26ac0430 127 SEND2("BH %s", bhmessage);
32d002cb 128#if NTLM_FAIL_OPEN
2d70df72 129 }
5d146f7d 130#endif
2d70df72 131}
132
94439e4e 133/*
134 * options:
135 * -b try load-balancing the domain-controllers
136 * -f fail-over to another DC if DC connection fails.
5d146f7d 137 * DEPRECATED and VERBOSELY IGNORED. This is on by default now.
2d70df72 138 * -l last-ditch-mode
94439e4e 139 * domain\controller ...
140 */
a88de9ce 141char *my_program_name = NULL;
142
143void
144usage()
145{
146 fprintf(stderr,
26ac0430
AJ
147 "%s usage:\n%s [-b] [-f] [-d] [-l] domain\\controller [domain\\controller ...]\n"
148 "-b enables load-balancing among controllers\n"
149 "-f enables failover among controllers (DEPRECATED and always active)\n"
150 "-l changes behavior on domain controller failyures to last-ditch.\n"
151 "-d enables debugging statements if DEBUG was defined at build-time.\n\n"
152 "You MUST specify at least one Domain Controller.\n"
153 "You can use either \\ or / as separator between the domain name \n"
154 "and the controller name\n",
155 my_program_name, my_program_name);
a88de9ce 156}
157
fdbb3b19 158int debug_enabled=0;
a88de9ce 159
94439e4e 160void
161process_options(int argc, char *argv[])
162{
163 int opt, j, had_error = 0;
164 dc *new_dc = NULL, *last_dc = NULL;
3c641669 165 while (-1 != (opt = getopt(argc, argv, "bfld"))) {
26ac0430
AJ
166 switch (opt) {
167 case 'b':
168 load_balance = 1;
169 break;
170 case 'f':
171 fprintf(stderr,
172 "WARNING. The -f flag is DEPRECATED and always active.\n");
173 break;
32d002cb 174#if NTLM_FAIL_OPEN
26ac0430
AJ
175 case 'l':
176 last_ditch_enabled = 1;
177 break;
5d146f7d 178#endif
26ac0430
AJ
179 case 'd':
180 debug_enabled=1;
181 break;
182 default:
183 fprintf(stderr, "unknown option: -%c. Exiting\n", opt);
184 usage();
185 had_error = 1;
186 }
94439e4e 187 }
188 if (had_error)
26ac0430 189 exit(1);
94439e4e 190 /* Okay, now begin filling controllers up */
191 /* we can avoid memcpy-ing, and just reuse argv[] */
192 for (j = optind; j < argc; j++) {
26ac0430
AJ
193 char *d, *c;
194 /* d will not be freed in case of non-error. Since we don't reconfigure,
195 * it's going to live as long as the process anyways */
196 d = malloc(strlen(argv[j]) + 1);
197 strcpy(d, argv[j]);
75aa769b 198 debug("Adding domain-controller %s\n", d);
26ac0430
AJ
199 if (NULL == (c = strchr(d, '\\')) && NULL == (c = strchr(d, '/'))) {
200 fprintf(stderr, "Couldn't grok domain-controller %s\n", d);
201 free(d);
202 continue;
203 }
204 /* more than one delimiter is not allowed */
205 if (NULL != strchr(c + 1, '\\') || NULL != strchr(c + 1, '/')) {
206 fprintf(stderr, "Broken domain-controller %s\n", d);
207 free(d);
208 continue;
209 }
210 *c++ = '\0';
211 new_dc = (dc *) malloc(sizeof(dc));
212 if (!new_dc) {
213 fprintf(stderr, "Malloc error while parsing DC options\n");
214 free(d);
215 continue;
216 }
217 /* capitalize */
218 uc(c);
219 uc(d);
220 numcontrollers++;
221 new_dc->domain = d;
222 new_dc->controller = c;
223 new_dc->dead = 0;
224 if (controllers == NULL) { /* first controller */
225 controllers = new_dc;
226 last_dc = new_dc;
227 } else {
228 last_dc->next = new_dc; /* can't be null */
229 last_dc = new_dc;
230 }
94439e4e 231 }
232 if (numcontrollers == 0) {
26ac0430
AJ
233 fprintf(stderr, "You must specify at least one domain-controller!\n");
234 usage();
235 exit(1);
94439e4e 236 }
237 last_dc->next = controllers; /* close the queue, now it's circular */
238}
239
a19a836d
AJ
240/**
241 * tries connecting to the domain controllers in the "controllers" ring,
94439e4e 242 * with failover if the adequate option is specified.
243 */
244const char *
245obtain_challenge()
246{
247 int j = 0;
60d096f4 248 const char *ch = NULL;
94439e4e 249 for (j = 0; j < numcontrollers; j++) {
75aa769b 250 debug("obtain_challenge: selecting %s\\%s (attempt #%d)\n",
7bfa4b00 251 current_dc->domain, current_dc->controller, j + 1);
26ac0430
AJ
252 if (current_dc->dead != 0) {
253 if (time(NULL) - current_dc->dead >= DEAD_DC_RETRY_INTERVAL) {
254 /* mark helper as retry-worthy if it's so. */
75aa769b 255 debug("Reviving DC\n");
26ac0430
AJ
256 current_dc->dead = 0;
257 } else { /* skip it */
75aa769b 258 debug("Skipping it\n");
26ac0430
AJ
259 continue;
260 }
261 }
262 /* else branch. Here we KNOW that the DC is fine */
75aa769b 263 debug("attempting challenge retrieval\n");
26ac0430 264 ch = make_challenge(current_dc->domain, current_dc->controller);
75aa769b 265 debug("make_challenge retuned %p\n", ch);
26ac0430 266 if (ch) {
75aa769b 267 debug("Got it\n");
26ac0430
AJ
268 return ch; /* All went OK, returning */
269 }
270 /* Huston, we've got a problem. Take this DC out of the loop */
75aa769b 271 debug("Marking DC as DEAD\n");
26ac0430
AJ
272 current_dc->dead = time(NULL);
273 /* Try with the next */
75aa769b 274 debug("moving on to next controller\n");
26ac0430 275 current_dc = current_dc->next;
94439e4e 276 }
5d146f7d 277 /* all DCs failed. */
94439e4e 278 return NULL;
279}
280
2d70df72 281
94439e4e 282void
283manage_request()
284{
285 ntlmhdr *fast_header;
2d70df72 286 char buf[BUFFER_SIZE];
94439e4e 287 const char *ch;
db03754a 288 char *ch2, *decoded, *cred = NULL;
94439e4e 289 int plen;
290
6437ac71 291 if (fgets(buf, BUFFER_SIZE, stdin) == NULL) {
26ac0430
AJ
292 fprintf(stderr, "fgets() failed! dying..... errno=%d (%s)\n", errno,
293 strerror(errno));
294 exit(1); /* BIIG buffer */
6437ac71 295 }
75aa769b 296 debug("managing request\n");
94439e4e 297 ch2 = memchr(buf, '\n', BUFFER_SIZE); /* safer against overrun than strchr */
298 if (ch2) {
26ac0430
AJ
299 *ch2 = '\0'; /* terminate the string at newline. */
300 ch = ch2;
94439e4e 301 }
75aa769b 302 debug("ntlm authenticator. Got '%s' from Squid\n", buf);
94439e4e 303
304 if (memcmp(buf, "KK ", 3) == 0) { /* authenticate-request */
26ac0430
AJ
305 /* figure out what we got */
306 decoded = base64_decode(buf + 3);
307 /* Note: we don't need to manage memory at this point, since
308 * base64_decode returns a pointer to static storage.
309 */
310
311 if (!decoded) { /* decoding failure, return error */
312 SEND("NA Packet format error, couldn't base64-decode");
313 return;
314 }
315 /* fast-track-decode request type. */
316 fast_header = (struct _ntlmhdr *) decoded;
317
318 /* sanity-check: it IS a NTLMSSP packet, isn't it? */
319 if (memcmp(fast_header->signature, "NTLMSSP", 8) != 0) {
320 SEND("NA Broken authentication packet");
321 return;
322 }
323 switch (le32toh(fast_header->type)) {
324 case NTLM_NEGOTIATE:
325 SEND("NA Invalid negotiation request received");
326 return;
327 /* notreached */
328 case NTLM_CHALLENGE:
83ae34bc 329 SEND("NA Got a challenge. We refuse to have our authority disputed");
26ac0430
AJ
330 return;
331 /* notreached */
332 case NTLM_AUTHENTICATE:
333 /* check against the DC */
334 plen = strlen(buf) * 3 / 4; /* we only need it here. Optimization */
335 signal(SIGALRM, timeout_during_auth);
336 alarm(30);
337 cred = ntlm_check_auth((ntlm_authenticate *) decoded, plen);
338 alarm(0);
339 signal(SIGALRM, SIG_DFL);
340 if (got_timeout != 0) {
341 fprintf(stderr, "ntlm-auth[%ld]: Timeout during authentication.\n", (long)getpid());
342 SEND("BH Timeout during authentication");
343 got_timeout = 0;
344 return;
345 }
346 if (cred == NULL) {
347 int smblib_err, smb_errorclass, smb_errorcode, nb_error;
348 if (ntlm_errno == NTLM_LOGON_ERROR) { /* hackish */
349 SEND("NA Logon Failure");
350 return;
351 }
352 /* there was an error. We have two errno's to look at.
353 * libntlmssp's erno is insufficient, we'll have to look at
354 * the actual SMB library error codes, to acually figure
355 * out what's happening. The thing has braindamaged interfacess..*/
356 smblib_err = SMB_Get_Last_Error();
357 smb_errorclass = SMBlib_Error_Class(SMB_Get_Last_SMB_Err());
358 smb_errorcode = SMBlib_Error_Code(SMB_Get_Last_SMB_Err());
359 nb_error = RFCNB_Get_Last_Error();
75aa769b
AJ
360 debug("No creds. SMBlib error %d, SMB error class %d, SMB error code %d, NB error %d\n",
361 smblib_err, smb_errorclass, smb_errorcode, nb_error);
26ac0430
AJ
362 /* Should I use smblib_err? Actually it seems I can do as well
363 * without it.. */
364 if (nb_error != 0) { /* netbios-level error */
365 send_bh_or_ld("NetBios error!",
366 (ntlm_authenticate *) decoded, plen);
367 fprintf(stderr, "NetBios error code %d (%s)\n", nb_error,
368 RFCNB_Error_Strings[abs(nb_error)]);
369 return;
370 }
371 switch (smb_errorclass) {
372 case SMBC_SUCCESS:
75aa769b 373 debug("Huh? Got a SMB success code but could check auth..");
26ac0430
AJ
374 SEND("NA Authentication failed");
375 /*
376 * send_bh_or_ld("SMB success, but no creds. Internal error?",
377 * (ntlm_authenticate *) decoded, plen);
378 */
379 return;
380 case SMBC_ERRDOS:
381 /*this is the most important one for errors */
75aa769b 382 debug("DOS error\n");
26ac0430
AJ
383 switch (smb_errorcode) {
384 /* two categories matter to us: those which could be
385 * server errors, and those which are auth errors */
386 case SMBD_noaccess: /* 5 */
387 SEND("NA Access denied");
388 return;
389 case SMBD_badformat:
390 SEND("NA bad format in authentication packet");
391 return;
392 case SMBD_badaccess:
393 SEND("NA Bad access request");
394 return;
395 case SMBD_baddata:
396 SEND("NA Bad Data");
397 return;
398 default:
399 send_bh_or_ld("DOS Error",
400 (ntlm_authenticate *) decoded, plen);
401 return;
402 }
403 case SMBC_ERRSRV: /* server errors */
75aa769b 404 debug("Server error");
26ac0430
AJ
405 switch (smb_errorcode) {
406 /* mostly same as above */
407 case SMBV_badpw:
408 SEND("NA Bad password");
409 return;
410 case SMBV_access:
411 SEND("NA Server access error");
412 return;
413 default:
414 send_bh_or_ld("Server Error",
415 (ntlm_authenticate *) decoded, plen);
416 return;
417 }
418 case SMBC_ERRHRD: /* hardware errors don't really matter */
419 send_bh_or_ld("Domain Controller Hardware error",
420 (ntlm_authenticate *) decoded, plen);
421 return;
422 case SMBC_ERRCMD:
423 send_bh_or_ld("Domain Controller Command Error",
424 (ntlm_authenticate *) decoded, plen);
425 return;
426 }
83ae34bc
AJ
427 SEND("BH unknown internal error.");
428 return;
26ac0430 429 }
83ae34bc 430
26ac0430
AJ
431 lc(cred); /* let's lowercase them for our convenience */
432 SEND2("AF %s", cred);
433 return;
434 default:
435 SEND("BH unknown authentication packet type");
436 return;
437 }
7c572fcd 438 /* notreached */
26ac0430 439 return;
94439e4e 440 }
441 if (memcmp(buf, "YR", 2) == 0) { /* refresh-request */
26ac0430
AJ
442 dc_disconnect();
443 ch = obtain_challenge();
444 /* Robert says we can afford to wait forever. I'll trust him on this
445 * one */
446 while (ch == NULL) {
447 sleep(30);
448 ch = obtain_challenge();
449 }
450 SEND2("TT %s", ch);
451 return;
94439e4e 452 }
453 SEND("BH Helper detected protocol error");
454 return;
26ac0430 455 /********* END ********/
94439e4e 456
457
458}
459
460int
461main(int argc, char *argv[])
462{
75aa769b 463 debug("ntlm_auth build " __DATE__ ", " __TIME__ " starting up...\n");
32d002cb 464#if DEBUG
75aa769b 465 debug("changing dir to /tmp\n");
bdcd85c3 466 if (chdir("/tmp") != 0) {
75aa769b 467 debug("ERROR: (%d) failed.\n",errno);
bdcd85c3
AJ
468 return 2;
469 }
6437ac71 470#endif
94439e4e 471
a88de9ce 472 my_program_name = argv[0];
94439e4e 473 process_options(argc, argv);
474
75aa769b 475 debug("options processed OK\n");
94439e4e 476
477 /* initialize FDescs */
478 setbuf(stdout, NULL);
479 setbuf(stderr, NULL);
480
481 /* select the first domain controller we're going to use */
482 current_dc = controllers;
483 if (load_balance != 0 && numcontrollers > 1) {
26ac0430
AJ
484 int n;
485 pid_t pid = getpid();
486 n = pid % numcontrollers;
75aa769b 487 debug("load balancing. Selected controller #%d\n", n);
26ac0430
AJ
488 while (n > 0) {
489 current_dc = current_dc->next;
490 n--;
491 }
94439e4e 492 }
493 while (1) {
26ac0430 494 manage_request();
94439e4e 495 }
7c572fcd 496 /* notreached */
94439e4e 497 return 0;
498}