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