trying to get HEAD building again. If you want the code
[Samba.git] / source / utils / ntlm_auth.c
blob3dfa157bdaba0ff6684dc137f2fba107e3638d02
1 /*
2 Unix SMB/CIFS implementation.
4 Winbind status program.
6 Copyright (C) Tim Potter 2000-2002
7 Copyright (C) Andrew Bartlett <abartlet@samba.org> 2003
8 Copyright (C) Francesco Chemolli <kinkie@kame.usr.dsi.unimi.it> 2000
10 This program is free software; you can redistribute it and/or modify
11 it under the terms of the GNU General Public License as published by
12 the Free Software Foundation; either version 2 of the License, or
13 (at your option) any later version.
15 This program is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 GNU General Public License for more details.
20 You should have received a copy of the GNU General Public License
21 along with this program; if not, write to the Free Software
22 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
25 #include "includes.h"
27 #undef DBGC_CLASS
28 #define DBGC_CLASS DBGC_WINBIND
30 #define SQUID_BUFFER_SIZE 2010
32 enum squid_mode {
33 SQUID_2_4_BASIC,
34 SQUID_2_5_BASIC,
35 SQUID_2_5_NTLMSSP
39 extern int winbindd_fd;
41 static const char *opt_username;
42 static const char *opt_domain;
43 static const char *opt_workstation;
44 static const char *opt_password;
45 static DATA_BLOB opt_challenge;
46 static DATA_BLOB opt_lm_response;
47 static DATA_BLOB opt_nt_response;
48 static int request_lm_key;
49 static int request_nt_key;
52 static char winbind_separator(void)
54 struct winbindd_response response;
55 static BOOL got_sep;
56 static char sep;
58 if (got_sep)
59 return sep;
61 ZERO_STRUCT(response);
63 /* Send off request */
65 if (winbindd_request(WINBINDD_INFO, NULL, &response) !=
66 NSS_STATUS_SUCCESS) {
67 d_printf("could not obtain winbind separator!\n");
68 return '\\';
71 sep = response.data.info.winbind_separator;
72 got_sep = True;
74 if (!sep) {
75 d_printf("winbind separator was NULL!\n");
76 return '\\';
79 return sep;
82 static const char *get_winbind_domain(void)
84 struct winbindd_response response;
86 static fstring winbind_domain;
87 if (*winbind_domain) {
88 return winbind_domain;
91 ZERO_STRUCT(response);
93 /* Send off request */
95 if (winbindd_request(WINBINDD_DOMAIN_NAME, NULL, &response) !=
96 NSS_STATUS_SUCCESS) {
97 d_printf("could not obtain winbind domain name!\n");
98 return NULL;
101 fstrcpy(winbind_domain, response.data.domain_name);
103 return winbind_domain;
107 static const char *get_winbind_netbios_name(void)
109 struct winbindd_response response;
111 static fstring winbind_netbios_name;
113 if (*winbind_netbios_name) {
114 return winbind_netbios_name;
117 ZERO_STRUCT(response);
119 /* Send off request */
121 if (winbindd_request(WINBINDD_NETBIOS_NAME, NULL, &response) !=
122 NSS_STATUS_SUCCESS) {
123 d_printf("could not obtain winbind netbios name!\n");
124 return NULL;
127 fstrcpy(winbind_netbios_name, response.data.netbios_name);
129 return winbind_netbios_name;
133 /* Authenticate a user with a plaintext password */
135 static BOOL check_plaintext_auth(const char *user, const char *pass, BOOL stdout_diagnostics)
137 struct winbindd_request request;
138 struct winbindd_response response;
139 NSS_STATUS result;
141 /* Send off request */
143 ZERO_STRUCT(request);
144 ZERO_STRUCT(response);
146 fstrcpy(request.data.auth.user, user);
147 fstrcpy(request.data.auth.pass, pass);
149 result = winbindd_request(WINBINDD_PAM_AUTH, &request, &response);
151 /* Display response */
153 if (stdout_diagnostics) {
154 if ((result != NSS_STATUS_SUCCESS) && (response.data.auth.nt_status == 0)) {
155 d_printf("Reading winbind reply failed! (0x01)\n");
158 d_printf("%s: %s (0x%x)\n",
159 response.data.auth.nt_status_string,
160 response.data.auth.error_string,
161 response.data.auth.nt_status);
162 } else {
163 if ((result != NSS_STATUS_SUCCESS) && (response.data.auth.nt_status == 0)) {
164 DEBUG(1, ("Reading winbind reply failed! (0x01)\n"));
167 DEBUG(3, ("%s: %s (0x%x)\n",
168 response.data.auth.nt_status_string,
169 response.data.auth.error_string,
170 response.data.auth.nt_status));
173 return (result == NSS_STATUS_SUCCESS);
176 /* authenticate a user with an encrypted username/password */
178 static NTSTATUS contact_winbind_auth_crap(const char *username,
179 const char *domain,
180 const char *workstation,
181 const DATA_BLOB *challenge,
182 const DATA_BLOB *lm_response,
183 const DATA_BLOB *nt_response,
184 uint32 flags,
185 uint8 lm_key[8],
186 uint8 nt_key[16],
187 char **error_string)
189 NTSTATUS nt_status;
190 NSS_STATUS result;
191 struct winbindd_request request;
192 struct winbindd_response response;
194 static uint8 zeros[16];
196 ZERO_STRUCT(request);
197 ZERO_STRUCT(response);
199 request.flags = flags;
201 fstrcpy(request.data.auth_crap.user, username);
203 fstrcpy(request.data.auth_crap.domain, domain);
204 fstrcpy(request.data.auth_crap.workstation, workstation);
206 memcpy(request.data.auth_crap.chal, challenge->data, MIN(challenge->length, 8));
208 if (lm_response && lm_response->length) {
209 memcpy(request.data.auth_crap.lm_resp, lm_response->data, MIN(lm_response->length, sizeof(request.data.auth_crap.lm_resp)));
210 request.data.auth_crap.lm_resp_len = lm_response->length;
213 if (nt_response && nt_response->length) {
214 memcpy(request.data.auth_crap.nt_resp, nt_response->data, MIN(nt_response->length, sizeof(request.data.auth_crap.nt_resp)));
215 request.data.auth_crap.nt_resp_len = nt_response->length;
218 result = winbindd_request(WINBINDD_PAM_AUTH_CRAP, &request, &response);
220 /* Display response */
222 if ((result != NSS_STATUS_SUCCESS) && (response.data.auth.nt_status == 0)) {
223 nt_status = NT_STATUS_UNSUCCESSFUL;
224 if (error_string)
225 *error_string = smb_xstrdup("Reading winbind reply failed!");
226 return nt_status;
229 nt_status = (NT_STATUS(response.data.auth.nt_status));
230 if (!NT_STATUS_IS_OK(nt_status)) {
231 if (error_string)
232 *error_string = smb_xstrdup(response.data.auth.error_string);
233 return nt_status;
236 if ((flags & WBFLAG_PAM_LMKEY) && lm_key
237 && (memcmp(zeros, response.data.auth.first_8_lm_hash,
238 sizeof(response.data.auth.first_8_lm_hash)) != 0)) {
239 memcpy(lm_key, response.data.auth.first_8_lm_hash,
240 sizeof(response.data.auth.first_8_lm_hash));
242 if ((flags & WBFLAG_PAM_NTKEY) && nt_key
243 && (memcmp(zeros, response.data.auth.nt_session_key,
244 sizeof(response.data.auth.nt_session_key)) != 0)) {
245 memcpy(nt_key, response.data.auth.nt_session_key,
246 sizeof(response.data.auth.nt_session_key));
248 return nt_status;
251 static NTSTATUS winbind_pw_check(struct ntlmssp_state *ntlmssp_state)
253 return contact_winbind_auth_crap(ntlmssp_state->user, ntlmssp_state->domain,
254 ntlmssp_state->workstation,
255 &ntlmssp_state->chal,
256 &ntlmssp_state->lm_resp,
257 &ntlmssp_state->nt_resp,
259 NULL,
260 NULL,
261 NULL);
264 static void manage_squid_ntlmssp_request(enum squid_mode squid_mode,
265 char *buf, int length)
267 static NTLMSSP_STATE *ntlmssp_state = NULL;
268 DATA_BLOB request, reply;
269 NTSTATUS nt_status;
271 if (strlen(buf) < 2) {
272 DEBUG(1, ("NTLMSSP query [%s] invalid", buf));
273 x_fprintf(x_stdout, "BH\n");
274 return;
277 if (strlen(buf) > 3) {
278 request = base64_decode_data_blob(buf + 3);
279 } else if (strcmp(buf, "YR") == 0) {
280 request = data_blob(NULL, 0);
281 if (ntlmssp_state)
282 ntlmssp_server_end(&ntlmssp_state);
283 } else {
284 DEBUG(1, ("NTLMSSP query [%s] invalid", buf));
285 x_fprintf(x_stdout, "BH\n");
286 return;
289 if (!ntlmssp_state) {
290 ntlmssp_server_start(&ntlmssp_state);
291 ntlmssp_state->check_password = winbind_pw_check;
292 ntlmssp_state->get_domain = get_winbind_domain;
293 ntlmssp_state->get_global_myname = get_winbind_netbios_name;
296 DEBUG(10, ("got NTLMSSP packet:\n"));
297 dump_data(10, request.data, request.length);
299 nt_status = ntlmssp_server_update(ntlmssp_state, request, &reply);
301 if (NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
302 char *reply_base64 = base64_encode_data_blob(reply);
303 x_fprintf(x_stdout, "TT %s\n", reply_base64);
304 SAFE_FREE(reply_base64);
305 data_blob_free(&reply);
306 DEBUG(10, ("NTLMSSP challenge\n"));
307 } else if (!NT_STATUS_IS_OK(nt_status)) {
308 x_fprintf(x_stdout, "NA %s\n", nt_errstr(nt_status));
309 DEBUG(10, ("NTLMSSP %s\n", nt_errstr(nt_status)));
310 } else {
311 x_fprintf(x_stdout, "AF %s\\%s\n", ntlmssp_state->domain, ntlmssp_state->user);
312 DEBUG(10, ("NTLMSSP OK!\n"));
315 data_blob_free(&request);
318 static void manage_squid_basic_request(enum squid_mode squid_mode,
319 char *buf, int length)
321 char *user, *pass;
322 user=buf;
324 pass=memchr(buf,' ',length);
325 if (!pass) {
326 DEBUG(2, ("Password not found. Denying access\n"));
327 x_fprintf(x_stderr, "ERR\n");
328 return;
330 *pass='\0';
331 pass++;
333 if (squid_mode == SQUID_2_5_BASIC) {
334 rfc1738_unescape(user);
335 rfc1738_unescape(pass);
338 if (check_plaintext_auth(user, pass, False)) {
339 x_fprintf(x_stdout, "OK\n");
340 } else {
341 x_fprintf(x_stdout, "ERR\n");
345 static void manage_squid_request(enum squid_mode squid_mode)
347 char buf[SQUID_BUFFER_SIZE+1];
348 int length;
349 char *c;
350 static BOOL err;
352 /* this is not a typo - x_fgets doesn't work too well under squid */
353 if (fgets(buf, sizeof(buf)-1, stdin) == NULL) {
354 DEBUG(1, ("fgets() failed! dying..... errno=%d (%s)\n", ferror(stdin),
355 strerror(ferror(stdin))));
356 exit(1); /* BIIG buffer */
359 c=memchr(buf,'\n',sizeof(buf)-1);
360 if (c) {
361 *c = '\0';
362 length = c-buf;
363 } else {
364 err = 1;
365 return;
367 if (err) {
368 DEBUG(2, ("Oversized message\n"));
369 x_fprintf(x_stderr, "ERR\n");
370 err = 0;
371 return;
374 DEBUG(10, ("Got '%s' from squid (length: %d).\n",buf,length));
376 if (buf[0] == '\0') {
377 DEBUG(2, ("Invalid Request\n"));
378 x_fprintf(x_stderr, "ERR\n");
379 return;
382 if (squid_mode == SQUID_2_5_BASIC || squid_mode == SQUID_2_4_BASIC) {
383 manage_squid_basic_request(squid_mode, buf, length);
384 } else if (squid_mode == SQUID_2_5_NTLMSSP) {
385 manage_squid_ntlmssp_request(squid_mode, buf, length);
390 static void squid_stream(enum squid_mode squid_mode) {
391 /* initialize FDescs */
392 x_setbuf(x_stdout, NULL);
393 x_setbuf(x_stderr, NULL);
394 while(1) {
395 manage_squid_request(squid_mode);
400 /* Authenticate a user with a challenge/response */
402 static BOOL check_auth_crap(void)
404 NTSTATUS nt_status;
405 uint32 flags = 0;
406 char lm_key[8];
407 char nt_key[16];
408 char *hex_lm_key;
409 char *hex_nt_key;
410 char *error_string;
411 static uint8 zeros[16];
413 x_setbuf(x_stdout, NULL);
415 if (request_lm_key)
416 flags |= WBFLAG_PAM_LMKEY;
418 if (request_nt_key)
419 flags |= WBFLAG_PAM_NTKEY;
421 nt_status = contact_winbind_auth_crap(opt_username, opt_domain,
422 opt_workstation,
423 &opt_challenge,
424 &opt_lm_response,
425 &opt_nt_response,
426 flags,
427 lm_key,
428 nt_key,
429 &error_string);
431 if (!NT_STATUS_IS_OK(nt_status)) {
432 x_fprintf(x_stdout, "%s (0x%x)\n",
433 error_string,
434 NT_STATUS_V(nt_status));
435 SAFE_FREE(error_string);
436 return False;
439 if (request_lm_key
440 && (memcmp(zeros, lm_key,
441 sizeof(lm_key)) != 0)) {
442 hex_encode(lm_key,
443 sizeof(lm_key),
444 &hex_lm_key);
445 x_fprintf(x_stdout, "LM_KEY: %s\n", hex_lm_key);
446 SAFE_FREE(hex_lm_key);
448 if (request_nt_key
449 && (memcmp(zeros, nt_key,
450 sizeof(nt_key)) != 0)) {
451 hex_encode(nt_key,
452 sizeof(nt_key),
453 &hex_nt_key);
454 x_fprintf(x_stdout, "NT_KEY: %s\n", hex_nt_key);
455 SAFE_FREE(hex_nt_key);
458 return True;
462 Authenticate a user with a challenge/response, checking session key
463 and valid authentication types
466 static DATA_BLOB get_challenge(void)
468 static DATA_BLOB chal;
469 if (opt_challenge.length)
470 return opt_challenge;
472 chal = data_blob(NULL, 8);
474 generate_random_buffer(chal.data, chal.length, False);
475 return chal;
479 * Test LM authentication, no NT response supplied
482 static BOOL test_lm(void)
484 NTSTATUS nt_status;
485 uint32 flags = 0;
486 DATA_BLOB lm_response = data_blob(NULL, 24);
488 uchar lm_key[8];
489 uchar nt_key[16];
490 uchar lm_hash[16];
491 DATA_BLOB chall = get_challenge();
492 char *error_string;
494 ZERO_STRUCT(lm_key);
495 ZERO_STRUCT(nt_key);
497 flags |= WBFLAG_PAM_LMKEY;
498 flags |= WBFLAG_PAM_NTKEY;
500 SMBencrypt(opt_password, chall.data, lm_response.data);
501 E_deshash(opt_password, lm_hash);
503 nt_status = contact_winbind_auth_crap(opt_username, opt_domain, opt_workstation,
504 &chall,
505 &lm_response,
506 NULL,
507 flags,
508 lm_key,
509 nt_key,
510 &error_string);
512 data_blob_free(&lm_response);
514 if (!NT_STATUS_IS_OK(nt_status)) {
515 d_printf("%s (0x%x)\n",
516 error_string,
517 NT_STATUS_V(nt_status));
518 return False;
521 if (memcmp(lm_hash, lm_key,
522 sizeof(lm_key)) != 0) {
523 DEBUG(1, ("LM Key does not match expectations!\n"));
524 DEBUG(1, ("lm_key:\n"));
525 dump_data(1, lm_key, 8);
526 DEBUG(1, ("expected:\n"));
527 dump_data(1, lm_hash, 8);
529 if (memcmp(lm_hash, nt_key, 8) != 0) {
530 DEBUG(1, ("Session Key (first 8, lm hash) does not match expectations!\n"));
531 DEBUG(1, ("nt_key:\n"));
532 dump_data(1, nt_key, 8);
533 DEBUG(1, ("expected:\n"));
534 dump_data(1, lm_hash, 8);
536 return True;
540 * Test the normal 'LM and NTLM' combination
543 static BOOL test_lm_ntlm(void)
545 BOOL pass = True;
546 NTSTATUS nt_status;
547 uint32 flags = 0;
548 DATA_BLOB lm_response = data_blob(NULL, 24);
549 DATA_BLOB nt_response = data_blob(NULL, 24);
550 DATA_BLOB session_key = data_blob(NULL, 16);
552 uchar lm_key[8];
553 uchar nt_key[16];
554 uchar lm_hash[16];
555 uchar nt_hash[16];
556 DATA_BLOB chall = get_challenge();
557 char *error_string;
559 ZERO_STRUCT(lm_key);
560 ZERO_STRUCT(nt_key);
562 flags |= WBFLAG_PAM_LMKEY;
563 flags |= WBFLAG_PAM_NTKEY;
565 SMBencrypt(opt_password,chall.data,lm_response.data);
566 E_deshash(opt_password, lm_hash);
568 SMBNTencrypt(opt_password,chall.data,nt_response.data);
570 E_md4hash(opt_password, nt_hash);
571 SMBsesskeygen_ntv1(nt_hash, NULL, session_key.data);
573 nt_status = contact_winbind_auth_crap(opt_username, opt_domain,
574 opt_workstation,
575 &chall,
576 &lm_response,
577 &nt_response,
578 flags,
579 lm_key,
580 nt_key,
581 &error_string);
583 data_blob_free(&lm_response);
585 if (!NT_STATUS_IS_OK(nt_status)) {
586 d_printf("%s (0x%x)\n",
587 error_string,
588 NT_STATUS_V(nt_status));
589 SAFE_FREE(error_string);
590 return False;
593 if (memcmp(lm_hash, lm_key,
594 sizeof(lm_key)) != 0) {
595 DEBUG(1, ("LM Key does not match expectations!\n"));
596 DEBUG(1, ("lm_key:\n"));
597 dump_data(1, lm_key, 8);
598 DEBUG(1, ("expected:\n"));
599 dump_data(1, lm_hash, 8);
600 pass = False;
602 if (memcmp(session_key.data, nt_key,
603 sizeof(nt_key)) != 0) {
604 DEBUG(1, ("NT Session Key does not match expectations!\n"));
605 DEBUG(1, ("nt_key:\n"));
606 dump_data(1, nt_key, 16);
607 DEBUG(1, ("expected:\n"));
608 dump_data(1, session_key.data, session_key.length);
609 pass = False;
611 return pass;
615 * Test the NTLM response only, no LM.
618 static BOOL test_ntlm(void)
620 BOOL pass = True;
621 NTSTATUS nt_status;
622 uint32 flags = 0;
623 DATA_BLOB nt_response = data_blob(NULL, 24);
624 DATA_BLOB session_key = data_blob(NULL, 16);
626 char lm_key[8];
627 char nt_key[16];
628 char lm_hash[16];
629 char nt_hash[16];
630 DATA_BLOB chall = get_challenge();
631 char *error_string;
633 ZERO_STRUCT(lm_key);
634 ZERO_STRUCT(nt_key);
636 flags |= WBFLAG_PAM_LMKEY;
637 flags |= WBFLAG_PAM_NTKEY;
639 SMBNTencrypt(opt_password,chall.data,nt_response.data);
640 E_md4hash(opt_password, nt_hash);
641 SMBsesskeygen_ntv1(nt_hash, NULL, session_key.data);
643 E_deshash(opt_password, lm_hash);
645 nt_status = contact_winbind_auth_crap(opt_username, opt_domain,
646 opt_workstation,
647 &chall,
648 NULL,
649 &nt_response,
650 flags,
651 lm_key,
652 nt_key,
653 &error_string);
655 data_blob_free(&nt_response);
657 if (!NT_STATUS_IS_OK(nt_status)) {
658 d_printf("%s (0x%x)\n",
659 error_string,
660 NT_STATUS_V(nt_status));
661 SAFE_FREE(error_string);
662 return False;
665 if (memcmp(lm_hash, lm_key,
666 sizeof(lm_key)) != 0) {
667 DEBUG(1, ("LM Key does not match expectations!\n"));
668 DEBUG(1, ("lm_key:\n"));
669 dump_data(1, lm_key, 8);
670 DEBUG(1, ("expected:\n"));
671 dump_data(1, lm_hash, 8);
672 pass = False;
674 if (memcmp(session_key.data, nt_key,
675 sizeof(nt_key)) != 0) {
676 DEBUG(1, ("NT Session Key does not match expectations!\n"));
677 DEBUG(1, ("nt_key:\n"));
678 dump_data(1, nt_key, 16);
679 DEBUG(1, ("expected:\n"));
680 dump_data(1, session_key.data, session_key.length);
681 pass = False;
683 return pass;
687 * Test the NTLM response only, but in the LM field.
690 static BOOL test_ntlm_in_lm(void)
692 BOOL pass = True;
693 NTSTATUS nt_status;
694 uint32 flags = 0;
695 DATA_BLOB nt_response = data_blob(NULL, 24);
697 uchar lm_key[8];
698 uchar lm_hash[16];
699 uchar nt_key[16];
700 DATA_BLOB chall = get_challenge();
701 char *error_string;
703 ZERO_STRUCT(nt_key);
705 flags |= WBFLAG_PAM_LMKEY;
706 flags |= WBFLAG_PAM_NTKEY;
708 SMBNTencrypt(opt_password,chall.data,nt_response.data);
710 E_deshash(opt_password, lm_hash);
712 nt_status = contact_winbind_auth_crap(opt_username, opt_domain,
713 opt_workstation,
714 &chall,
715 &nt_response,
716 NULL,
717 flags,
718 lm_key,
719 nt_key,
720 &error_string);
722 data_blob_free(&nt_response);
724 if (!NT_STATUS_IS_OK(nt_status)) {
725 d_printf("%s (0x%x)\n",
726 error_string,
727 NT_STATUS_V(nt_status));
728 SAFE_FREE(error_string);
729 return False;
732 if (memcmp(lm_hash, lm_key,
733 sizeof(lm_key)) != 0) {
734 DEBUG(1, ("LM Key does not match expectations!\n"));
735 DEBUG(1, ("lm_key:\n"));
736 dump_data(1, lm_key, 8);
737 DEBUG(1, ("expected:\n"));
738 dump_data(1, lm_hash, 8);
739 pass = False;
741 if (memcmp(lm_hash, nt_key, 8) != 0) {
742 DEBUG(1, ("Session Key (first 8 lm hash) does not match expectations!\n"));
743 DEBUG(1, ("nt_key:\n"));
744 dump_data(1, nt_key, 16);
745 DEBUG(1, ("expected:\n"));
746 dump_data(1, lm_hash, 8);
747 pass = False;
749 return pass;
753 * Test the NTLM response only, but in the both the NT and LM fields.
756 static BOOL test_ntlm_in_both(void)
758 BOOL pass = True;
759 NTSTATUS nt_status;
760 uint32 flags = 0;
761 DATA_BLOB nt_response = data_blob(NULL, 24);
762 DATA_BLOB session_key = data_blob(NULL, 16);
764 char lm_key[8];
765 char lm_hash[16];
766 char nt_key[16];
767 char nt_hash[16];
768 DATA_BLOB chall = get_challenge();
769 char *error_string;
771 ZERO_STRUCT(lm_key);
772 ZERO_STRUCT(nt_key);
774 flags |= WBFLAG_PAM_LMKEY;
775 flags |= WBFLAG_PAM_NTKEY;
777 SMBNTencrypt(opt_password,chall.data,nt_response.data);
778 E_md4hash(opt_password, nt_hash);
779 SMBsesskeygen_ntv1(nt_hash, NULL, session_key.data);
781 E_deshash(opt_password, lm_hash);
783 nt_status = contact_winbind_auth_crap(opt_username, opt_domain,
784 opt_workstation,
785 &chall,
786 &nt_response,
787 &nt_response,
788 flags,
789 lm_key,
790 nt_key,
791 &error_string);
793 data_blob_free(&nt_response);
795 if (!NT_STATUS_IS_OK(nt_status)) {
796 d_printf("%s (0x%x)\n",
797 error_string,
798 NT_STATUS_V(nt_status));
799 SAFE_FREE(error_string);
800 return False;
803 if (memcmp(lm_hash, lm_key,
804 sizeof(lm_key)) != 0) {
805 DEBUG(1, ("LM Key does not match expectations!\n"));
806 DEBUG(1, ("lm_key:\n"));
807 dump_data(1, lm_key, 8);
808 DEBUG(1, ("expected:\n"));
809 dump_data(1, lm_hash, 8);
810 pass = False;
812 if (memcmp(session_key.data, nt_key,
813 sizeof(nt_key)) != 0) {
814 DEBUG(1, ("NT Session Key does not match expectations!\n"));
815 DEBUG(1, ("nt_key:\n"));
816 dump_data(1, nt_key, 16);
817 DEBUG(1, ("expected:\n"));
818 dump_data(1, session_key.data, session_key.length);
819 pass = False;
823 return pass;
827 * Test the NTLMv2 response only
830 static BOOL test_ntlmv2(void)
832 BOOL pass = True;
833 NTSTATUS nt_status;
834 uint32 flags = 0;
835 DATA_BLOB ntlmv2_response = data_blob(NULL, 0);
836 DATA_BLOB nt_session_key = data_blob(NULL, 0);
837 DATA_BLOB names_blob = NTLMv2_generate_names_blob(get_winbind_netbios_name(), get_winbind_domain());
839 uchar nt_key[16];
840 DATA_BLOB chall = get_challenge();
841 char *error_string;
843 ZERO_STRUCT(nt_key);
845 flags |= WBFLAG_PAM_NTKEY;
847 if (!SMBNTLMv2encrypt(opt_username, opt_domain, opt_password, &chall,
848 &names_blob,
849 NULL, &ntlmv2_response,
850 &nt_session_key)) {
851 data_blob_free(&names_blob);
852 return False;
854 data_blob_free(&names_blob);
856 nt_status = contact_winbind_auth_crap(opt_username, opt_domain,
857 opt_workstation,
858 &chall,
859 NULL,
860 &ntlmv2_response,
861 flags,
862 NULL,
863 nt_key,
864 &error_string);
866 data_blob_free(&ntlmv2_response);
868 if (!NT_STATUS_IS_OK(nt_status)) {
869 d_printf("%s (0x%x)\n",
870 error_string,
871 NT_STATUS_V(nt_status));
872 SAFE_FREE(error_string);
873 return False;
876 if (memcmp(nt_session_key.data, nt_key,
877 sizeof(nt_key)) != 0) {
878 DEBUG(1, ("NT Session Key does not match expectations!\n"));
879 DEBUG(1, ("nt_key:\n"));
880 dump_data(1, nt_key, 16);
881 DEBUG(1, ("expected:\n"));
882 dump_data(1, nt_session_key.data, nt_session_key.length);
883 pass = False;
885 return pass;
889 * Test the NTLMv2 and LMv2 responses
892 static BOOL test_lmv2_ntlmv2(void)
894 BOOL pass = True;
895 NTSTATUS nt_status;
896 uint32 flags = 0;
897 DATA_BLOB ntlmv2_response = data_blob(NULL, 0);
898 DATA_BLOB lmv2_response = data_blob(NULL, 0);
899 DATA_BLOB nt_session_key = data_blob(NULL, 0);
900 DATA_BLOB names_blob = NTLMv2_generate_names_blob(get_winbind_netbios_name(), get_winbind_domain());
902 uchar nt_key[16];
903 DATA_BLOB chall = get_challenge();
904 char *error_string;
906 ZERO_STRUCT(nt_key);
908 flags |= WBFLAG_PAM_NTKEY;
910 if (!SMBNTLMv2encrypt(opt_username, opt_domain, opt_password, &chall,
911 &names_blob,
912 &lmv2_response, &ntlmv2_response,
913 &nt_session_key)) {
914 data_blob_free(&names_blob);
915 return False;
917 data_blob_free(&names_blob);
919 nt_status = contact_winbind_auth_crap(opt_username, opt_domain,
920 opt_workstation,
921 &chall,
922 &lmv2_response,
923 &ntlmv2_response,
924 flags,
925 NULL,
926 nt_key,
927 &error_string);
929 data_blob_free(&lmv2_response);
930 data_blob_free(&ntlmv2_response);
932 if (!NT_STATUS_IS_OK(nt_status)) {
933 d_printf("%s (0x%x)\n",
934 error_string,
935 NT_STATUS_V(nt_status));
936 SAFE_FREE(error_string);
937 return False;
940 if (memcmp(nt_session_key.data, nt_key,
941 sizeof(nt_key)) != 0) {
942 DEBUG(1, ("NT Session Key does not match expectations!\n"));
943 DEBUG(1, ("nt_key:\n"));
944 dump_data(1, nt_key, 16);
945 DEBUG(1, ("expected:\n"));
946 dump_data(1, nt_session_key.data, nt_session_key.length);
947 pass = False;
949 return pass;
953 * Test the LMv2 response only
956 static BOOL test_lmv2(void)
958 BOOL pass = True;
959 NTSTATUS nt_status;
960 uint32 flags = 0;
961 DATA_BLOB lmv2_response = data_blob(NULL, 0);
963 DATA_BLOB chall = get_challenge();
964 char *error_string;
966 if (!SMBNTLMv2encrypt(opt_username, opt_domain, opt_password, &chall,
967 NULL,
968 &lmv2_response, NULL,
969 NULL)) {
970 return False;
973 nt_status = contact_winbind_auth_crap(opt_username, opt_domain,
974 opt_workstation,
975 &chall,
976 &lmv2_response,
977 NULL,
978 flags,
979 NULL,
980 NULL,
981 &error_string);
983 data_blob_free(&lmv2_response);
985 if (!NT_STATUS_IS_OK(nt_status)) {
986 d_printf("%s (0x%x)\n",
987 error_string,
988 NT_STATUS_V(nt_status));
989 SAFE_FREE(error_string);
990 return False;
993 return pass;
997 * Test the normal 'LM and NTLM' combination but deliberately break one
1000 static BOOL test_ntlm_broken(BOOL break_lm)
1002 BOOL pass = True;
1003 NTSTATUS nt_status;
1004 uint32 flags = 0;
1005 DATA_BLOB lm_response = data_blob(NULL, 24);
1006 DATA_BLOB nt_response = data_blob(NULL, 24);
1007 DATA_BLOB session_key = data_blob(NULL, 16);
1009 uchar lm_key[8];
1010 uchar nt_key[16];
1011 uchar lm_hash[16];
1012 uchar nt_hash[16];
1013 DATA_BLOB chall = get_challenge();
1014 char *error_string;
1016 ZERO_STRUCT(lm_key);
1017 ZERO_STRUCT(nt_key);
1019 flags |= WBFLAG_PAM_LMKEY;
1020 flags |= WBFLAG_PAM_NTKEY;
1022 SMBencrypt(opt_password,chall.data,lm_response.data);
1023 E_deshash(opt_password, lm_hash);
1025 SMBNTencrypt(opt_password,chall.data,nt_response.data);
1027 E_md4hash(opt_password, nt_hash);
1028 SMBsesskeygen_ntv1(nt_hash, NULL, session_key.data);
1030 if (break_lm)
1031 lm_response.data[0]++;
1032 else
1033 nt_response.data[0]++;
1035 nt_status = contact_winbind_auth_crap(opt_username, opt_domain,
1036 opt_workstation,
1037 &chall,
1038 &lm_response,
1039 &nt_response,
1040 flags,
1041 lm_key,
1042 nt_key,
1043 &error_string);
1045 data_blob_free(&lm_response);
1047 if (!NT_STATUS_IS_OK(nt_status)) {
1048 d_printf("%s (0x%x)\n",
1049 error_string,
1050 NT_STATUS_V(nt_status));
1051 SAFE_FREE(error_string);
1052 return False;
1055 if (memcmp(lm_hash, lm_key,
1056 sizeof(lm_key)) != 0) {
1057 DEBUG(1, ("LM Key does not match expectations!\n"));
1058 DEBUG(1, ("lm_key:\n"));
1059 dump_data(1, lm_key, 8);
1060 DEBUG(1, ("expected:\n"));
1061 dump_data(1, lm_hash, 8);
1062 pass = False;
1064 if (memcmp(session_key.data, nt_key,
1065 sizeof(nt_key)) != 0) {
1066 DEBUG(1, ("NT Session Key does not match expectations!\n"));
1067 DEBUG(1, ("nt_key:\n"));
1068 dump_data(1, nt_key, 16);
1069 DEBUG(1, ("expected:\n"));
1070 dump_data(1, session_key.data, session_key.length);
1071 pass = False;
1073 return pass;
1076 static BOOL test_ntlm_lm_broken(void)
1078 return test_ntlm_broken(True);
1081 static BOOL test_ntlm_ntlm_broken(void)
1083 return test_ntlm_broken(False);
1086 static BOOL test_ntlmv2_broken(BOOL break_lmv2)
1088 BOOL pass = True;
1089 NTSTATUS nt_status;
1090 uint32 flags = 0;
1091 DATA_BLOB ntlmv2_response = data_blob(NULL, 0);
1092 DATA_BLOB lmv2_response = data_blob(NULL, 0);
1093 DATA_BLOB nt_session_key = data_blob(NULL, 0);
1094 DATA_BLOB names_blob = NTLMv2_generate_names_blob(get_winbind_netbios_name(), get_winbind_domain());
1096 uchar nt_key[16];
1097 DATA_BLOB chall = get_challenge();
1098 char *error_string;
1100 ZERO_STRUCT(nt_key);
1102 flags |= WBFLAG_PAM_NTKEY;
1104 if (!SMBNTLMv2encrypt(opt_username, opt_domain, opt_password, &chall,
1105 &names_blob,
1106 &lmv2_response, &ntlmv2_response,
1107 &nt_session_key)) {
1108 data_blob_free(&names_blob);
1109 return False;
1111 data_blob_free(&names_blob);
1113 /* Heh - this should break the appropriate password hash nicely! */
1115 if (break_lmv2)
1116 lmv2_response.data[0]++;
1117 else
1118 ntlmv2_response.data[0]++;
1120 nt_status = contact_winbind_auth_crap(opt_username, opt_domain,
1121 opt_workstation,
1122 &chall,
1123 &lmv2_response,
1124 &ntlmv2_response,
1125 flags,
1126 NULL,
1127 nt_key,
1128 &error_string);
1130 data_blob_free(&lmv2_response);
1131 data_blob_free(&ntlmv2_response);
1133 if (!NT_STATUS_IS_OK(nt_status)) {
1134 d_printf("%s (0x%x)\n",
1135 error_string,
1136 NT_STATUS_V(nt_status));
1137 SAFE_FREE(error_string);
1138 return False;
1141 return pass;
1144 static BOOL test_ntlmv2_lmv2_broken(void)
1146 return test_ntlmv2_broken(True);
1149 static BOOL test_ntlmv2_ntlmv2_broken(void)
1151 return test_ntlmv2_broken(False);
1155 Tests:
1157 - LM only
1158 - NT and LM
1159 - NT
1160 - NT in LM field
1161 - NT in both fields
1162 - NTLMv2
1163 - NTLMv2 and LMv2
1164 - LMv2
1166 check we get the correct session key in each case
1167 check what values we get for the LM session key
1171 struct ntlm_tests {
1172 BOOL (*fn)(void);
1173 const char *name;
1174 } test_table[] = {
1175 {test_lm, "LM"},
1176 {test_lm_ntlm, "LM and NTLM"},
1177 {test_ntlm, "NTLM"},
1178 {test_ntlm_in_lm, "NTLM in LM"},
1179 {test_ntlm_in_both, "NTLM in both"},
1180 {test_ntlmv2, "NTLMv2"},
1181 {test_lmv2_ntlmv2, "NTLMv2 and LMv2"},
1182 {test_lmv2, "LMv2"},
1183 {test_ntlmv2_lmv2_broken, "NTLMv2 and LMv2, LMv2 broken"},
1184 {test_ntlmv2_ntlmv2_broken, "NTLMv2 and LMv2, NTLMv2 broken"},
1185 {test_ntlm_lm_broken, "NTLM and LM, LM broken"},
1186 {test_ntlm_ntlm_broken, "NTLM and LM, NTLM broken"}
1189 static BOOL diagnose_ntlm_auth(void)
1191 unsigned int i;
1192 BOOL pass = True;
1194 for (i=0; test_table[i].fn; i++) {
1195 if (!test_table[i].fn()) {
1196 DEBUG(1, ("Test %s failed!\n", test_table[i].name));
1197 pass = False;
1201 return pass;
1204 /* Main program */
1206 enum {
1207 OPT_USERNAME = 1000,
1208 OPT_DOMAIN,
1209 OPT_WORKSTATION,
1210 OPT_CHALLENGE,
1211 OPT_RESPONSE,
1212 OPT_LM,
1213 OPT_NT,
1214 OPT_PASSWORD,
1215 OPT_LM_KEY,
1216 OPT_NT_KEY,
1217 OPT_DIAGNOSTICS
1220 int main(int argc, const char **argv)
1222 int opt;
1223 static const char *helper_protocol;
1224 static int diagnostics;
1226 static const char *hex_challenge;
1227 static const char *hex_lm_response;
1228 static const char *hex_nt_response;
1229 char *challenge;
1230 char *lm_response;
1231 char *nt_response;
1232 size_t challenge_len;
1233 size_t lm_response_len;
1234 size_t nt_response_len;
1236 poptContext pc;
1238 /* NOTE: DO NOT change this interface without considering the implications!
1239 This is an external interface, which other programs will use to interact
1240 with this helper.
1243 /* We do not use single-letter command abbreviations, because they harm future
1244 interface stability. */
1246 struct poptOption long_options[] = {
1247 POPT_AUTOHELP
1248 { "helper-protocol", 0, POPT_ARG_STRING, &helper_protocol, OPT_DOMAIN, "operate as a stdio-based helper", "helper protocol to use"},
1249 { "username", 0, POPT_ARG_STRING, &opt_username, OPT_USERNAME, "username"},
1250 { "domain", 0, POPT_ARG_STRING, &opt_domain, OPT_DOMAIN, "domain name"},
1251 { "workstation", 0, POPT_ARG_STRING, &opt_workstation, OPT_WORKSTATION, "workstation"},
1252 { "challenge", 0, POPT_ARG_STRING, &hex_challenge, OPT_CHALLENGE, "challenge (HEX encoded)"},
1253 { "lm-response", 0, POPT_ARG_STRING, &hex_lm_response, OPT_LM, "LM Response to the challenge (HEX encoded)"},
1254 { "nt-response", 0, POPT_ARG_STRING, &hex_nt_response, OPT_NT, "NT or NTLMv2 Response to the challenge (HEX encoded)"},
1255 { "password", 0, POPT_ARG_STRING, &opt_password, OPT_PASSWORD, "User's plaintext password"},
1256 { "request-lm-key", 0, POPT_ARG_NONE, &request_lm_key, OPT_LM_KEY, "Retreive LM session key"},
1257 { "request-nt-key", 0, POPT_ARG_NONE, &request_nt_key, OPT_NT_KEY, "Retreive NT session key"},
1258 { "diagnostics", 0, POPT_ARG_NONE, &diagnostics, OPT_DIAGNOSTICS, "Perform diagnostics on the authentictaion chain"},
1259 POPT_COMMON_SAMBA
1260 POPT_TABLEEND
1263 /* Samba client initialisation */
1265 dbf = x_stderr;
1267 /* Samba client initialisation */
1269 if (!lp_load(dyn_CONFIGFILE, True, False, False)) {
1270 d_fprintf(stderr, "wbinfo: error opening config file %s. Error was %s\n",
1271 dyn_CONFIGFILE, strerror(errno));
1272 exit(1);
1275 /* Parse options */
1277 pc = poptGetContext("ntlm_auth", argc, argv, long_options, 0);
1279 /* Parse command line options */
1281 if (argc == 1) {
1282 poptPrintHelp(pc, stderr, 0);
1283 return 1;
1286 pc = poptGetContext(NULL, argc, (const char **)argv, long_options,
1287 POPT_CONTEXT_KEEP_FIRST);
1289 while((opt = poptGetNextOpt(pc)) != -1) {
1290 switch (opt) {
1291 case OPT_CHALLENGE:
1292 challenge = smb_xmalloc((strlen(hex_challenge))/2+1);
1293 if ((challenge_len = strhex_to_str(challenge,
1294 strlen(hex_challenge),
1295 hex_challenge)) != 8) {
1296 x_fprintf(x_stderr, "hex decode of %s failed (only got %u bytes)!\n",
1297 hex_challenge, challenge_len);
1298 exit(1);
1300 opt_challenge = data_blob(challenge, challenge_len);
1301 SAFE_FREE(challenge);
1302 break;
1303 case OPT_LM:
1304 lm_response = smb_xmalloc((strlen(hex_lm_response))/2+1);
1305 lm_response_len = strhex_to_str(lm_response,
1306 strlen(hex_lm_response),
1307 hex_lm_response);
1308 if (lm_response_len != 24) {
1309 x_fprintf(x_stderr, "hex decode of %s failed!\n", hex_lm_response);
1310 exit(1);
1312 opt_lm_response = data_blob(lm_response, lm_response_len);
1313 SAFE_FREE(lm_response);
1314 break;
1315 case OPT_NT:
1316 nt_response = smb_xmalloc((strlen(hex_nt_response)+2)/2+1);
1317 nt_response_len = strhex_to_str(nt_response,
1318 strlen(hex_nt_response),
1319 hex_nt_response);
1320 if (nt_response_len < 24) {
1321 x_fprintf(x_stderr, "hex decode of %s failed!\n", hex_nt_response);
1322 exit(1);
1324 opt_nt_response = data_blob(nt_response, nt_response_len);
1325 SAFE_FREE(nt_response);
1326 break;
1330 if (helper_protocol) {
1331 if (strcmp(helper_protocol, "squid-2.5-ntlmssp")== 0) {
1332 squid_stream(SQUID_2_5_NTLMSSP);
1333 } else if (strcmp(helper_protocol, "squid-2.5-basic")== 0) {
1334 squid_stream(SQUID_2_5_BASIC);
1335 } else if (strcmp(helper_protocol, "squid-2.4-basic")== 0) {
1336 squid_stream(SQUID_2_4_BASIC);
1337 } else {
1338 x_fprintf(x_stderr, "unknown helper protocol [%s]\n", helper_protocol);
1339 exit(1);
1343 if (!opt_username) {
1344 x_fprintf(x_stderr, "username must be specified!\n\n");
1345 poptPrintHelp(pc, stderr, 0);
1346 exit(1);
1349 if (opt_domain == NULL) {
1350 opt_domain = get_winbind_domain();
1353 if (opt_workstation == NULL) {
1354 opt_workstation = "";
1357 if (opt_challenge.length) {
1358 if (!check_auth_crap()) {
1359 exit(1);
1361 exit(0);
1364 if (!opt_password) {
1365 opt_password = getpass("password: ");
1368 if (diagnostics) {
1369 if (!diagnose_ntlm_auth()) {
1370 exit(1);
1372 } else {
1373 fstring user;
1375 snprintf(user, sizeof(user)-1, "%s%c%s", opt_domain, winbind_separator(), opt_username);
1376 if (!check_plaintext_auth(user, opt_password, True)) {
1377 exit(1);
1381 /* Exit code */
1383 poptFreeContext(pc);
1384 return 0;