Reverse previous patch from Stefan and me after comments by Andrew Bartlett
[abartlet/samba.git/.git] / source3 / utils / ntlm_auth.c
1 /* 
2    Unix SMB/CIFS implementation.
3
4    Winbind status program.
5
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 
9
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.
14    
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.
19    
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.
23 */
24
25 #include "includes.h"
26
27 #undef DBGC_CLASS
28 #define DBGC_CLASS DBGC_WINBIND
29
30 #define SQUID_BUFFER_SIZE 2010
31
32 enum squid_mode {
33         SQUID_2_4_BASIC,
34         SQUID_2_5_BASIC,
35         SQUID_2_5_NTLMSSP
36 };
37         
38
39 extern int winbindd_fd;
40
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;
50
51
52 static char winbind_separator(void)
53 {
54         struct winbindd_response response;
55         static BOOL got_sep;
56         static char sep;
57
58         if (got_sep)
59                 return sep;
60
61         ZERO_STRUCT(response);
62
63         /* Send off request */
64
65         if (winbindd_request(WINBINDD_INFO, NULL, &response) !=
66             NSS_STATUS_SUCCESS) {
67                 d_printf("could not obtain winbind separator!\n");
68                 return '\\';
69         }
70
71         sep = response.data.info.winbind_separator;
72         got_sep = True;
73
74         if (!sep) {
75                 d_printf("winbind separator was NULL!\n");
76                 return '\\';
77         }
78         
79         return sep;
80 }
81
82 static const char *get_winbind_domain(void)
83 {
84         struct winbindd_response response;
85
86         static fstring winbind_domain;
87         if (*winbind_domain) {
88                 return winbind_domain;
89         }
90
91         ZERO_STRUCT(response);
92
93         /* Send off request */
94
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;
99         }
100
101         fstrcpy(winbind_domain, response.data.domain_name);
102
103         return winbind_domain;
104
105 }
106
107 static const char *get_winbind_netbios_name(void)
108 {
109         struct winbindd_response response;
110
111         static fstring winbind_netbios_name;
112
113         if (*winbind_netbios_name) {
114                 return winbind_netbios_name;
115         }
116
117         ZERO_STRUCT(response);
118
119         /* Send off request */
120
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;
125         }
126
127         fstrcpy(winbind_netbios_name, response.data.netbios_name);
128
129         return winbind_netbios_name;
130
131 }
132
133 /* Authenticate a user with a plaintext password */
134
135 static BOOL check_plaintext_auth(const char *user, const char *pass, BOOL stdout_diagnostics)
136 {
137         struct winbindd_request request;
138         struct winbindd_response response;
139         NSS_STATUS result;
140
141         /* Send off request */
142
143         ZERO_STRUCT(request);
144         ZERO_STRUCT(response);
145
146         fstrcpy(request.data.auth.user, user);
147         fstrcpy(request.data.auth.pass, pass);
148
149         result = winbindd_request(WINBINDD_PAM_AUTH, &request, &response);
150
151         /* Display response */
152         
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");
156                 }
157                 
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"));
165                 }
166                 
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));               
171         }
172                 
173         return (result == NSS_STATUS_SUCCESS);
174 }
175
176 /* authenticate a user with an encrypted username/password */
177
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) 
188 {
189         NTSTATUS nt_status;
190         NSS_STATUS result;
191         struct winbindd_request request;
192         struct winbindd_response response;
193
194         static uint8 zeros[16];
195
196         ZERO_STRUCT(request);
197         ZERO_STRUCT(response);
198
199         request.data.auth_crap.flags = flags;
200
201         fstrcpy(request.data.auth_crap.user, username);
202
203         fstrcpy(request.data.auth_crap.domain, domain);
204         fstrcpy(request.data.auth_crap.workstation, workstation);
205
206         memcpy(request.data.auth_crap.chal, challenge->data, MIN(challenge->length, 8));
207
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;
211         }
212
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;
216         }
217         
218         result = winbindd_request(WINBINDD_PAM_AUTH_CRAP, &request, &response);
219
220         /* Display response */
221
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;
227         }
228         
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;
234         }
235
236         if ((flags & WINBIND_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));
241         }
242         if ((flags & WINBIND_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));
247         }
248         return nt_status;
249 }
250                                    
251 static NTSTATUS winbind_pw_check(struct ntlmssp_state *ntlmssp_state) 
252 {
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, 
258                                          0,
259                                          NULL, 
260                                          NULL, 
261                                          NULL);
262 }
263
264 static void manage_squid_ntlmssp_request(enum squid_mode squid_mode, 
265                                          char *buf, int length) 
266 {
267         static NTLMSSP_STATE *ntlmssp_state = NULL;
268         DATA_BLOB request, reply;
269         NTSTATUS nt_status;
270
271         if (strlen(buf) < 2) {
272                 DEBUG(1, ("NTLMSSP query [%s] invalid", buf));
273                 x_fprintf(x_stdout, "BH\n");
274                 return;
275         }
276
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;
287         }
288
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;
294         }
295
296         DEBUG(10, ("got NTLMSSP packet:\n"));
297         dump_data(10, request.data, request.length);
298
299         nt_status = ntlmssp_server_update(ntlmssp_state, request, &reply);
300         
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"));
313         }
314
315         data_blob_free(&request);
316 }
317
318 static void manage_squid_basic_request(enum squid_mode squid_mode, 
319                                        char *buf, int length) 
320 {
321         char *user, *pass;      
322         user=buf;
323         
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;
329         }
330         *pass='\0';
331         pass++;
332         
333         if (squid_mode == SQUID_2_5_BASIC) {
334                 rfc1738_unescape(user);
335                 rfc1738_unescape(pass);
336         }
337         
338         if (check_plaintext_auth(user, pass, False)) {
339                 x_fprintf(x_stdout, "OK\n");
340         } else {
341                 x_fprintf(x_stdout, "ERR\n");
342         }
343 }
344
345 static void manage_squid_request(enum squid_mode squid_mode) 
346 {
347         char buf[SQUID_BUFFER_SIZE+1];
348         int length;
349         char *c;
350         static BOOL err;
351
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 */
357         }
358     
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;
366         }
367         if (err) {
368                 DEBUG(2, ("Oversized message\n"));
369                 x_fprintf(x_stderr, "ERR\n");
370                 err = 0;
371                 return;
372         }
373
374         DEBUG(10, ("Got '%s' from squid (length: %d).\n",buf,length));
375
376         if (buf[0] == '\0') {
377                 DEBUG(2, ("Invalid Request\n"));
378                 x_fprintf(x_stderr, "ERR\n");
379                 return;
380         }
381         
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);
386         }
387 }
388
389
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);
396         }
397 }
398
399
400 /* Authenticate a user with a challenge/response */
401
402 static BOOL check_auth_crap(void)
403 {
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         
412         static uint8 zeros[16];
413
414         if (request_lm_key) 
415                 flags |= WINBIND_PAM_LMKEY;
416
417         if (request_nt_key) 
418                 flags |= WINBIND_PAM_NTKEY;
419
420         nt_status = contact_winbind_auth_crap(opt_username, opt_domain, 
421                                               opt_workstation,
422                                               &opt_challenge, 
423                                               &opt_lm_response, 
424                                               &opt_nt_response, 
425                                               flags,
426                                               lm_key, 
427                                               nt_key, 
428                                               &error_string);
429
430         if (!NT_STATUS_IS_OK(nt_status)) {
431                 d_printf("%s (0x%x)\n", 
432                          error_string,
433                          NT_STATUS_V(nt_status));
434                 SAFE_FREE(error_string);
435                 return False;
436         }
437
438         if (request_lm_key 
439             && (memcmp(zeros, lm_key, 
440                        sizeof(lm_key)) != 0)) {
441                 hex_encode(lm_key,
442                            sizeof(lm_key),
443                            &hex_lm_key);
444                 d_printf("LM_KEY: %s\n", hex_lm_key);
445                 SAFE_FREE(hex_lm_key);
446         }
447         if (request_nt_key 
448             && (memcmp(zeros, nt_key, 
449                        sizeof(nt_key)) != 0)) {
450                 hex_encode(nt_key, 
451                            sizeof(nt_key), 
452                            &hex_nt_key);
453                 d_printf("NT_KEY: %s\n", hex_nt_key);
454                 SAFE_FREE(hex_nt_key);
455         }
456
457         return True;
458 }
459
460 /* 
461    Authenticate a user with a challenge/response, checking session key
462    and valid authentication types
463 */
464
465 static DATA_BLOB get_challenge(void) 
466 {
467         static DATA_BLOB chal;
468         if (opt_challenge.length)
469                 return opt_challenge;
470         
471         chal = data_blob(NULL, 8);
472
473         generate_random_buffer(chal.data, chal.length, False);
474         return chal;
475 }
476
477 /* 
478  * Test LM authentication, no NT response supplied
479  */
480
481 static BOOL test_lm(void) 
482 {
483         NTSTATUS nt_status;
484         uint32 flags = 0;
485         DATA_BLOB lm_response = data_blob(NULL, 24);
486
487         uchar lm_key[8];
488         uchar nt_key[16];
489         uchar lm_hash[16];
490         DATA_BLOB chall = get_challenge();
491         char *error_string;
492         
493         ZERO_STRUCT(lm_key);
494         ZERO_STRUCT(nt_key);
495
496         flags |= WINBIND_PAM_LMKEY;
497         flags |= WINBIND_PAM_NTKEY;
498
499         SMBencrypt(opt_password, chall.data, lm_response.data);
500         E_deshash(opt_password, lm_hash); 
501
502         nt_status = contact_winbind_auth_crap(opt_username, opt_domain, opt_workstation,
503                                               &chall,
504                                               &lm_response,
505                                               NULL,
506                                               flags,
507                                               lm_key, 
508                                               nt_key,
509                                               &error_string);
510         
511         data_blob_free(&lm_response);
512
513         if (!NT_STATUS_IS_OK(nt_status)) {
514                 d_printf("%s (0x%x)\n", 
515                          error_string,
516                          NT_STATUS_V(nt_status));
517                 return False;
518         }
519
520         if (memcmp(lm_hash, lm_key, 
521                    sizeof(lm_key)) != 0) {
522                 DEBUG(1, ("LM Key does not match expectations!\n"));
523                 DEBUG(1, ("lm_key:\n"));
524                 dump_data(1, lm_key, 8);
525                 DEBUG(1, ("expected:\n"));
526                 dump_data(1, lm_hash, 8);
527         }
528         if (memcmp(lm_hash, nt_key, 8) != 0) {
529                 DEBUG(1, ("Session Key (first 8, lm hash) does not match expectations!\n"));
530                 DEBUG(1, ("nt_key:\n"));
531                 dump_data(1, nt_key, 8);
532                 DEBUG(1, ("expected:\n"));
533                 dump_data(1, lm_hash, 8);
534         }
535         return True;
536 }
537
538 /* 
539  * Test the normal 'LM and NTLM' combination
540  */
541
542 static BOOL test_lm_ntlm(void) 
543 {
544         BOOL pass = True;
545         NTSTATUS nt_status;
546         uint32 flags = 0;
547         DATA_BLOB lm_response = data_blob(NULL, 24);
548         DATA_BLOB nt_response = data_blob(NULL, 24);
549         DATA_BLOB session_key = data_blob(NULL, 16);
550
551         uchar lm_key[8];
552         uchar nt_key[16];
553         uchar lm_hash[16];
554         uchar nt_hash[16];
555         DATA_BLOB chall = get_challenge();
556         char *error_string;
557         
558         ZERO_STRUCT(lm_key);
559         ZERO_STRUCT(nt_key);
560
561         flags |= WINBIND_PAM_LMKEY;
562         flags |= WINBIND_PAM_NTKEY;
563
564         SMBencrypt(opt_password,chall.data,lm_response.data);
565         E_deshash(opt_password, lm_hash); 
566
567         SMBNTencrypt(opt_password,chall.data,nt_response.data);
568
569         E_md4hash(opt_password, nt_hash);
570         SMBsesskeygen_ntv1(nt_hash, NULL, session_key.data);
571
572         nt_status = contact_winbind_auth_crap(opt_username, opt_domain, 
573                                               opt_workstation,
574                                               &chall,
575                                               &lm_response,
576                                               &nt_response,
577                                               flags,
578                                               lm_key, 
579                                               nt_key,
580                                               &error_string);
581         
582         data_blob_free(&lm_response);
583
584         if (!NT_STATUS_IS_OK(nt_status)) {
585                 d_printf("%s (0x%x)\n", 
586                          error_string,
587                          NT_STATUS_V(nt_status));
588                 SAFE_FREE(error_string);
589                 return False;
590         }
591
592         if (memcmp(lm_hash, lm_key, 
593                    sizeof(lm_key)) != 0) {
594                 DEBUG(1, ("LM Key does not match expectations!\n"));
595                 DEBUG(1, ("lm_key:\n"));
596                 dump_data(1, lm_key, 8);
597                 DEBUG(1, ("expected:\n"));
598                 dump_data(1, lm_hash, 8);
599                 pass = False;
600         }
601         if (memcmp(session_key.data, nt_key, 
602                    sizeof(nt_key)) != 0) {
603                 DEBUG(1, ("NT Session Key does not match expectations!\n"));
604                 DEBUG(1, ("nt_key:\n"));
605                 dump_data(1, nt_key, 16);
606                 DEBUG(1, ("expected:\n"));
607                 dump_data(1, session_key.data, session_key.length);
608                 pass = False;
609         }
610         return pass;
611 }
612
613 /* 
614  * Test the NTLM response only, no LM.
615  */
616
617 static BOOL test_ntlm(void) 
618 {
619         BOOL pass = True;
620         NTSTATUS nt_status;
621         uint32 flags = 0;
622         DATA_BLOB nt_response = data_blob(NULL, 24);
623         DATA_BLOB session_key = data_blob(NULL, 16);
624
625         char lm_key[8];
626         char nt_key[16];
627         char lm_hash[16];
628         char nt_hash[16];
629         DATA_BLOB chall = get_challenge();
630         char *error_string;
631         
632         ZERO_STRUCT(lm_key);
633         ZERO_STRUCT(nt_key);
634
635         flags |= WINBIND_PAM_LMKEY;
636         flags |= WINBIND_PAM_NTKEY;
637
638         SMBNTencrypt(opt_password,chall.data,nt_response.data);
639         E_md4hash(opt_password, nt_hash);
640         SMBsesskeygen_ntv1(nt_hash, NULL, session_key.data);
641
642         E_deshash(opt_password, lm_hash); 
643
644         nt_status = contact_winbind_auth_crap(opt_username, opt_domain, 
645                                               opt_workstation,
646                                               &chall,
647                                               NULL,
648                                               &nt_response,
649                                               flags,
650                                               lm_key,
651                                               nt_key,
652                                               &error_string);
653         
654         data_blob_free(&nt_response);
655
656         if (!NT_STATUS_IS_OK(nt_status)) {
657                 d_printf("%s (0x%x)\n", 
658                          error_string,
659                          NT_STATUS_V(nt_status));
660                 SAFE_FREE(error_string);
661                 return False;
662         }
663
664         if (memcmp(lm_hash, lm_key, 
665                    sizeof(lm_key)) != 0) {
666                 DEBUG(1, ("LM Key does not match expectations!\n"));
667                 DEBUG(1, ("lm_key:\n"));
668                 dump_data(1, lm_key, 8);
669                 DEBUG(1, ("expected:\n"));
670                 dump_data(1, lm_hash, 8);
671                 pass = False;
672         }
673         if (memcmp(session_key.data, nt_key, 
674                    sizeof(nt_key)) != 0) {
675                 DEBUG(1, ("NT Session Key does not match expectations!\n"));
676                 DEBUG(1, ("nt_key:\n"));
677                 dump_data(1, nt_key, 16);
678                 DEBUG(1, ("expected:\n"));
679                 dump_data(1, session_key.data, session_key.length);
680                 pass = False;
681         }
682         return pass;
683 }
684
685 /* 
686  * Test the NTLM response only, but in the LM field.
687  */
688
689 static BOOL test_ntlm_in_lm(void) 
690 {
691         BOOL pass = True;
692         NTSTATUS nt_status;
693         uint32 flags = 0;
694         DATA_BLOB nt_response = data_blob(NULL, 24);
695
696         uchar lm_key[8];
697         uchar lm_hash[16];
698         uchar nt_key[16];
699         DATA_BLOB chall = get_challenge();
700         char *error_string;
701         
702         ZERO_STRUCT(nt_key);
703
704         flags |= WINBIND_PAM_LMKEY;
705         flags |= WINBIND_PAM_NTKEY;
706
707         SMBNTencrypt(opt_password,chall.data,nt_response.data);
708
709         E_deshash(opt_password, lm_hash); 
710
711         nt_status = contact_winbind_auth_crap(opt_username, opt_domain, 
712                                               opt_workstation,
713                                               &chall,
714                                               &nt_response,
715                                               NULL,
716                                               flags,
717                                               lm_key,
718                                               nt_key,
719                                               &error_string);
720         
721         data_blob_free(&nt_response);
722
723         if (!NT_STATUS_IS_OK(nt_status)) {
724                 d_printf("%s (0x%x)\n", 
725                          error_string,
726                          NT_STATUS_V(nt_status));
727                 SAFE_FREE(error_string);
728                 return False;
729         }
730
731         if (memcmp(lm_hash, lm_key, 
732                    sizeof(lm_key)) != 0) {
733                 DEBUG(1, ("LM Key does not match expectations!\n"));
734                 DEBUG(1, ("lm_key:\n"));
735                 dump_data(1, lm_key, 8);
736                 DEBUG(1, ("expected:\n"));
737                 dump_data(1, lm_hash, 8);
738                 pass = False;
739         }
740         if (memcmp(lm_hash, nt_key, 8) != 0) {
741                 DEBUG(1, ("Session Key (first 8 lm hash) does not match expectations!\n"));
742                 DEBUG(1, ("nt_key:\n"));
743                 dump_data(1, nt_key, 16);
744                 DEBUG(1, ("expected:\n"));
745                 dump_data(1, lm_hash, 8);
746                 pass = False;
747         }
748         return pass;
749 }
750
751 /* 
752  * Test the NTLM response only, but in the both the NT and LM fields.
753  */
754
755 static BOOL test_ntlm_in_both(void) 
756 {
757         BOOL pass = True;
758         NTSTATUS nt_status;
759         uint32 flags = 0;
760         DATA_BLOB nt_response = data_blob(NULL, 24);
761         DATA_BLOB session_key = data_blob(NULL, 16);
762
763         char lm_key[8];
764         char lm_hash[16];
765         char nt_key[16];
766         char nt_hash[16];
767         DATA_BLOB chall = get_challenge();
768         char *error_string;
769         
770         ZERO_STRUCT(lm_key);
771         ZERO_STRUCT(nt_key);
772
773         flags |= WINBIND_PAM_LMKEY;
774         flags |= WINBIND_PAM_NTKEY;
775
776         SMBNTencrypt(opt_password,chall.data,nt_response.data);
777         E_md4hash(opt_password, nt_hash);
778         SMBsesskeygen_ntv1(nt_hash, NULL, session_key.data);
779
780         E_deshash(opt_password, lm_hash); 
781
782         nt_status = contact_winbind_auth_crap(opt_username, opt_domain, 
783                                               opt_workstation,
784                                               &chall,
785                                               &nt_response,
786                                               &nt_response,
787                                               flags,
788                                               lm_key,
789                                               nt_key,
790                                               &error_string);
791         
792         data_blob_free(&nt_response);
793
794         if (!NT_STATUS_IS_OK(nt_status)) {
795                 d_printf("%s (0x%x)\n", 
796                          error_string,
797                          NT_STATUS_V(nt_status));
798                 SAFE_FREE(error_string);
799                 return False;
800         }
801
802         if (memcmp(lm_hash, lm_key, 
803                    sizeof(lm_key)) != 0) {
804                 DEBUG(1, ("LM Key does not match expectations!\n"));
805                 DEBUG(1, ("lm_key:\n"));
806                 dump_data(1, lm_key, 8);
807                 DEBUG(1, ("expected:\n"));
808                 dump_data(1, lm_hash, 8);
809                 pass = False;
810         }
811         if (memcmp(session_key.data, nt_key, 
812                    sizeof(nt_key)) != 0) {
813                 DEBUG(1, ("NT Session Key does not match expectations!\n"));
814                 DEBUG(1, ("nt_key:\n"));
815                 dump_data(1, nt_key, 16);
816                 DEBUG(1, ("expected:\n"));
817                 dump_data(1, session_key.data, session_key.length);
818                 pass = False;
819         }
820
821
822         return pass;
823 }
824
825 /* 
826  * Test the NTLMv2 response only
827  */
828
829 static BOOL test_ntlmv2(void) 
830 {
831         BOOL pass = True;
832         NTSTATUS nt_status;
833         uint32 flags = 0;
834         DATA_BLOB ntlmv2_response = data_blob(NULL, 0);
835         DATA_BLOB nt_session_key = data_blob(NULL, 0);
836         DATA_BLOB names_blob = NTLMv2_generate_names_blob(get_winbind_netbios_name(), get_winbind_domain());
837
838         uchar nt_key[16];
839         DATA_BLOB chall = get_challenge();
840         char *error_string;
841
842         ZERO_STRUCT(nt_key);
843         
844         flags |= WINBIND_PAM_NTKEY;
845
846         if (!SMBNTLMv2encrypt(opt_username, opt_domain, opt_password, &chall,
847                               &names_blob,
848                               NULL, &ntlmv2_response, 
849                               &nt_session_key)) {
850                 data_blob_free(&names_blob);
851                 return False;
852         }
853         data_blob_free(&names_blob);
854
855         nt_status = contact_winbind_auth_crap(opt_username, opt_domain, 
856                                               opt_workstation,
857                                               &chall,
858                                               NULL, 
859                                               &ntlmv2_response,
860                                               flags,
861                                               NULL, 
862                                               nt_key,
863                                               &error_string);
864         
865         data_blob_free(&ntlmv2_response);
866
867         if (!NT_STATUS_IS_OK(nt_status)) {
868                 d_printf("%s (0x%x)\n", 
869                          error_string,
870                          NT_STATUS_V(nt_status));
871                 SAFE_FREE(error_string);
872                 return False;
873         }
874
875         if (memcmp(nt_session_key.data, nt_key, 
876                    sizeof(nt_key)) != 0) {
877                 DEBUG(1, ("NT Session Key does not match expectations!\n"));
878                 DEBUG(1, ("nt_key:\n"));
879                 dump_data(1, nt_key, 16);
880                 DEBUG(1, ("expected:\n"));
881                 dump_data(1, nt_session_key.data, nt_session_key.length);
882                 pass = False;
883         }
884         return pass;
885 }
886
887 /* 
888  * Test the NTLMv2 and LMv2 responses
889  */
890
891 static BOOL test_lmv2_ntlmv2(void) 
892 {
893         BOOL pass = True;
894         NTSTATUS nt_status;
895         uint32 flags = 0;
896         DATA_BLOB ntlmv2_response = data_blob(NULL, 0);
897         DATA_BLOB lmv2_response = data_blob(NULL, 0);
898         DATA_BLOB nt_session_key = data_blob(NULL, 0);
899         DATA_BLOB names_blob = NTLMv2_generate_names_blob(get_winbind_netbios_name(), get_winbind_domain());
900
901         uchar nt_key[16];
902         DATA_BLOB chall = get_challenge();
903         char *error_string;
904
905         ZERO_STRUCT(nt_key);
906         
907         flags |= WINBIND_PAM_NTKEY;
908
909         if (!SMBNTLMv2encrypt(opt_username, opt_domain, opt_password, &chall,
910                               &names_blob,
911                               &lmv2_response, &ntlmv2_response, 
912                               &nt_session_key)) {
913                 data_blob_free(&names_blob);
914                 return False;
915         }
916         data_blob_free(&names_blob);
917
918         nt_status = contact_winbind_auth_crap(opt_username, opt_domain, 
919                                               opt_workstation,
920                                               &chall,
921                                               &lmv2_response,
922                                               &ntlmv2_response,
923                                               flags,
924                                               NULL, 
925                                               nt_key,
926                                               &error_string);
927         
928         data_blob_free(&lmv2_response);
929         data_blob_free(&ntlmv2_response);
930
931         if (!NT_STATUS_IS_OK(nt_status)) {
932                 d_printf("%s (0x%x)\n", 
933                          error_string,
934                          NT_STATUS_V(nt_status));
935                 SAFE_FREE(error_string);
936                 return False;
937         }
938
939         if (memcmp(nt_session_key.data, nt_key, 
940                    sizeof(nt_key)) != 0) {
941                 DEBUG(1, ("NT Session Key does not match expectations!\n"));
942                 DEBUG(1, ("nt_key:\n"));
943                 dump_data(1, nt_key, 16);
944                 DEBUG(1, ("expected:\n"));
945                 dump_data(1, nt_session_key.data, nt_session_key.length);
946                 pass = False;
947         }
948         return pass;
949 }
950
951 /* 
952  * Test the LMv2 response only
953  */
954
955 static BOOL test_lmv2(void) 
956 {
957         BOOL pass = True;
958         NTSTATUS nt_status;
959         uint32 flags = 0;
960         DATA_BLOB lmv2_response = data_blob(NULL, 0);
961
962         DATA_BLOB chall = get_challenge();
963         char *error_string;
964
965         if (!SMBNTLMv2encrypt(opt_username, opt_domain, opt_password, &chall,
966                               NULL, 
967                               &lmv2_response, NULL,
968                               NULL)) {
969                 return False;
970         }
971
972         nt_status = contact_winbind_auth_crap(opt_username, opt_domain, 
973                                               opt_workstation,
974                                               &chall,
975                                               &lmv2_response,
976                                               NULL, 
977                                               flags,
978                                               NULL, 
979                                               NULL,
980                                               &error_string);
981         
982         data_blob_free(&lmv2_response);
983
984         if (!NT_STATUS_IS_OK(nt_status)) {
985                 d_printf("%s (0x%x)\n", 
986                          error_string,
987                          NT_STATUS_V(nt_status));
988                 SAFE_FREE(error_string);
989                 return False;
990         }
991
992         return pass;
993 }
994
995 /* 
996  * Test the normal 'LM and NTLM' combination but deliberately break one
997  */
998
999 static BOOL test_ntlm_broken(BOOL break_lm) 
1000 {
1001         BOOL pass = True;
1002         NTSTATUS nt_status;
1003         uint32 flags = 0;
1004         DATA_BLOB lm_response = data_blob(NULL, 24);
1005         DATA_BLOB nt_response = data_blob(NULL, 24);
1006         DATA_BLOB session_key = data_blob(NULL, 16);
1007
1008         uchar lm_key[8];
1009         uchar nt_key[16];
1010         uchar lm_hash[16];
1011         uchar nt_hash[16];
1012         DATA_BLOB chall = get_challenge();
1013         char *error_string;
1014         
1015         ZERO_STRUCT(lm_key);
1016         ZERO_STRUCT(nt_key);
1017
1018         flags |= WINBIND_PAM_LMKEY;
1019         flags |= WINBIND_PAM_NTKEY;
1020
1021         SMBencrypt(opt_password,chall.data,lm_response.data);
1022         E_deshash(opt_password, lm_hash); 
1023
1024         SMBNTencrypt(opt_password,chall.data,nt_response.data);
1025
1026         E_md4hash(opt_password, nt_hash);
1027         SMBsesskeygen_ntv1(nt_hash, NULL, session_key.data);
1028
1029         if (break_lm)
1030                 lm_response.data[0]++;
1031         else
1032                 nt_response.data[0]++;
1033
1034         nt_status = contact_winbind_auth_crap(opt_username, opt_domain, 
1035                                               opt_workstation,
1036                                               &chall,
1037                                               &lm_response,
1038                                               &nt_response,
1039                                               flags,
1040                                               lm_key, 
1041                                               nt_key,
1042                                               &error_string);
1043         
1044         data_blob_free(&lm_response);
1045
1046         if (!NT_STATUS_IS_OK(nt_status)) {
1047                 d_printf("%s (0x%x)\n", 
1048                          error_string,
1049                          NT_STATUS_V(nt_status));
1050                 SAFE_FREE(error_string);
1051                 return False;
1052         }
1053
1054         if (memcmp(lm_hash, lm_key, 
1055                    sizeof(lm_key)) != 0) {
1056                 DEBUG(1, ("LM Key does not match expectations!\n"));
1057                 DEBUG(1, ("lm_key:\n"));
1058                 dump_data(1, lm_key, 8);
1059                 DEBUG(1, ("expected:\n"));
1060                 dump_data(1, lm_hash, 8);
1061                 pass = False;
1062         }
1063         if (memcmp(session_key.data, nt_key, 
1064                    sizeof(nt_key)) != 0) {
1065                 DEBUG(1, ("NT Session Key does not match expectations!\n"));
1066                 DEBUG(1, ("nt_key:\n"));
1067                 dump_data(1, nt_key, 16);
1068                 DEBUG(1, ("expected:\n"));
1069                 dump_data(1, session_key.data, session_key.length);
1070                 pass = False;
1071         }
1072         return pass;
1073 }
1074
1075 static BOOL test_ntlm_lm_broken(void) 
1076 {
1077         return test_ntlm_broken(True);
1078 }
1079
1080 static BOOL test_ntlm_ntlm_broken(void) 
1081 {
1082         return test_ntlm_broken(False);
1083 }
1084
1085 static BOOL test_ntlmv2_broken(BOOL break_lmv2)
1086 {
1087         BOOL pass = True;
1088         NTSTATUS nt_status;
1089         uint32 flags = 0;
1090         DATA_BLOB ntlmv2_response = data_blob(NULL, 0);
1091         DATA_BLOB lmv2_response = data_blob(NULL, 0);
1092         DATA_BLOB nt_session_key = data_blob(NULL, 0);
1093         DATA_BLOB names_blob = NTLMv2_generate_names_blob(get_winbind_netbios_name(), get_winbind_domain());
1094
1095         uchar nt_key[16];
1096         DATA_BLOB chall = get_challenge();
1097         char *error_string;
1098
1099         ZERO_STRUCT(nt_key);
1100         
1101         flags |= WINBIND_PAM_NTKEY;
1102          
1103         if (!SMBNTLMv2encrypt(opt_username, opt_domain, opt_password, &chall,
1104                               &names_blob,
1105                               &lmv2_response, &ntlmv2_response, 
1106                               &nt_session_key)) {
1107                 data_blob_free(&names_blob);
1108                 return False;
1109         }
1110         data_blob_free(&names_blob);
1111
1112         /* Heh - this should break the appropriate password hash nicely! */
1113
1114         if (break_lmv2)
1115                 lmv2_response.data[0]++;
1116         else
1117                 ntlmv2_response.data[0]++;
1118
1119         nt_status = contact_winbind_auth_crap(opt_username, opt_domain, 
1120                                               opt_workstation,
1121                                               &chall,
1122                                               &lmv2_response,
1123                                               &ntlmv2_response,
1124                                               flags,
1125                                               NULL,
1126                                               nt_key,
1127                                               &error_string);
1128         
1129         data_blob_free(&lmv2_response);
1130         data_blob_free(&ntlmv2_response);
1131
1132         if (!NT_STATUS_IS_OK(nt_status)) {
1133                 d_printf("%s (0x%x)\n", 
1134                          error_string,
1135                          NT_STATUS_V(nt_status));
1136                 SAFE_FREE(error_string);
1137                 return False;
1138         }
1139
1140         return pass;
1141 }
1142
1143 static BOOL test_ntlmv2_lmv2_broken(void) 
1144 {
1145         return test_ntlmv2_broken(True);
1146 }
1147
1148 static BOOL test_ntlmv2_ntlmv2_broken(void) 
1149 {
1150         return test_ntlmv2_broken(False);
1151 }
1152
1153 /* 
1154    Tests:
1155    
1156    - LM only
1157    - NT and LM             
1158    - NT
1159    - NT in LM field
1160    - NT in both fields
1161    - NTLMv2
1162    - NTLMv2 and LMv2
1163    - LMv2
1164    
1165    check we get the correct session key in each case
1166    check what values we get for the LM session key
1167    
1168 */
1169
1170 struct ntlm_tests {
1171         BOOL (*fn)(void);
1172         const char *name;
1173 } test_table[] = {
1174         {test_lm, "LM"},
1175         {test_lm_ntlm, "LM and NTLM"},
1176         {test_ntlm, "NTLM"},
1177         {test_ntlm_in_lm, "NTLM in LM"},
1178         {test_ntlm_in_both, "NTLM in both"},
1179         {test_ntlmv2, "NTLMv2"},
1180         {test_lmv2_ntlmv2, "NTLMv2 and LMv2"},
1181         {test_lmv2, "LMv2"},
1182         {test_ntlmv2_lmv2_broken, "NTLMv2 and LMv2, LMv2 broken"},
1183         {test_ntlmv2_ntlmv2_broken, "NTLMv2 and LMv2, NTLMv2 broken"},
1184         {test_ntlm_lm_broken, "NTLM and LM, LM broken"},
1185         {test_ntlm_ntlm_broken, "NTLM and LM, NTLM broken"}
1186 };
1187
1188 static BOOL diagnose_ntlm_auth(void)
1189 {
1190         unsigned int i;
1191         BOOL pass = True;
1192
1193         for (i=0; test_table[i].fn; i++) {
1194                 if (!test_table[i].fn()) {
1195                         DEBUG(1, ("Test %s failed!\n", test_table[i].name));
1196                         pass = False;
1197                 }
1198         }
1199
1200         return pass;
1201 }
1202
1203 /* Main program */
1204
1205 enum {
1206         OPT_USERNAME = 1000,
1207         OPT_DOMAIN,
1208         OPT_WORKSTATION,
1209         OPT_CHALLENGE,
1210         OPT_RESPONSE,
1211         OPT_LM,
1212         OPT_NT,
1213         OPT_PASSWORD,
1214         OPT_LM_KEY,
1215         OPT_NT_KEY,
1216         OPT_DIAGNOSTICS
1217 };
1218
1219  int main(int argc, const char **argv)
1220 {
1221         int opt;
1222         static const char *helper_protocol;
1223         static int diagnostics;
1224
1225         static const char *hex_challenge;
1226         static const char *hex_lm_response;
1227         static const char *hex_nt_response;
1228         char *challenge;
1229         char *lm_response;
1230         char *nt_response;
1231         size_t challenge_len;
1232         size_t lm_response_len;
1233         size_t nt_response_len;
1234
1235         poptContext pc;
1236
1237         /* NOTE: DO NOT change this interface without considering the implications!
1238            This is an external interface, which other programs will use to interact 
1239            with this helper.
1240         */
1241
1242         /* We do not use single-letter command abbreviations, because they harm future 
1243            interface stability. */
1244
1245         struct poptOption long_options[] = {
1246                 POPT_AUTOHELP
1247                 { "helper-protocol", 0, POPT_ARG_STRING, &helper_protocol, OPT_DOMAIN, "operate as a stdio-based helper", "helper protocol to use"},
1248                 { "username", 0, POPT_ARG_STRING, &opt_username, OPT_USERNAME, "username"},
1249                 { "domain", 0, POPT_ARG_STRING, &opt_domain, OPT_DOMAIN, "domain name"},
1250                 { "workstation", 0, POPT_ARG_STRING, &opt_workstation, OPT_WORKSTATION, "workstation"},
1251                 { "challenge", 0, POPT_ARG_STRING, &hex_challenge, OPT_CHALLENGE, "challenge (HEX encoded)"},
1252                 { "lm-response", 0, POPT_ARG_STRING, &hex_lm_response, OPT_LM, "LM Response to the challenge (HEX encoded)"},
1253                 { "nt-response", 0, POPT_ARG_STRING, &hex_nt_response, OPT_NT, "NT or NTLMv2 Response to the challenge (HEX encoded)"},
1254                 { "password", 0, POPT_ARG_STRING, &opt_password, OPT_PASSWORD, "User's plaintext password"},            
1255                 { "request-lm-key", 0, POPT_ARG_NONE, &request_lm_key, OPT_LM_KEY, "Retreive LM session key"},
1256                 { "request-nt-key", 0, POPT_ARG_NONE, &request_nt_key, OPT_NT_KEY, "Retreive NT session key"},
1257                 { "diagnostics", 0, POPT_ARG_NONE, &diagnostics, OPT_DIAGNOSTICS, "Perform diagnostics on the authentictaion chain"},
1258                 POPT_COMMON_SAMBA
1259                 POPT_TABLEEND
1260         };
1261
1262         /* Samba client initialisation */
1263
1264         dbf = x_stderr;
1265         
1266         /* Parse options */
1267
1268         pc = poptGetContext("ntlm_auth", argc, argv, long_options, 0);
1269
1270         /* Parse command line options */
1271
1272         if (argc == 1) {
1273                 poptPrintHelp(pc, stderr, 0);
1274                 return 1;
1275         }
1276
1277         pc = poptGetContext(NULL, argc, (const char **)argv, long_options, 
1278                             POPT_CONTEXT_KEEP_FIRST);
1279
1280         while((opt = poptGetNextOpt(pc)) != -1) {
1281                 switch (opt) {
1282                 case OPT_CHALLENGE:
1283                         challenge = smb_xmalloc((strlen(hex_challenge)+1)/2);
1284                         if ((challenge_len = strhex_to_str(challenge, 
1285                                                            strlen(hex_challenge), 
1286                                                            hex_challenge)) != 8) {
1287                                 x_fprintf(x_stderr, "hex decode of %s failed (only got %u bytes)!\n", 
1288                                         hex_challenge, challenge_len);
1289                                 exit(1);
1290                         }
1291                         opt_challenge = data_blob(challenge, challenge_len);
1292                         SAFE_FREE(challenge);
1293                         break;
1294                 case OPT_LM: 
1295                         lm_response = smb_xmalloc((strlen(hex_lm_response)+1)/2);
1296                         lm_response_len = strhex_to_str(lm_response,    
1297                                                         strlen(hex_lm_response), 
1298                                                         hex_lm_response);
1299                         if (lm_response_len != 24) {
1300                                 x_fprintf(x_stderr, "hex decode of %s failed!\n", hex_lm_response);
1301                                 exit(1);
1302                         }
1303                         opt_lm_response = data_blob(lm_response, lm_response_len);
1304                         SAFE_FREE(lm_response);
1305                         break;
1306                 case OPT_NT: 
1307                         nt_response = smb_xmalloc((strlen(hex_nt_response)+1)/2);
1308                         nt_response_len = strhex_to_str(nt_response, 
1309                                                         strlen(hex_nt_response), 
1310                                                         hex_nt_response);
1311                         if (nt_response_len < 24) {
1312                                 x_fprintf(x_stderr, "hex decode of %s failed!\n", hex_nt_response);
1313                                 exit(1);
1314                         }
1315                         opt_nt_response = data_blob(nt_response, nt_response_len);
1316                         SAFE_FREE(nt_response);
1317                         break;
1318                 }
1319         }
1320
1321         if (helper_protocol) {
1322                 if (strcmp(helper_protocol, "squid-2.5-ntlmssp")== 0) {
1323                         squid_stream(SQUID_2_5_NTLMSSP);
1324                 } else if (strcmp(helper_protocol, "squid-2.5-basic")== 0) {
1325                         squid_stream(SQUID_2_5_BASIC);
1326                 } else if (strcmp(helper_protocol, "squid-2.4-basic")== 0) {
1327                         squid_stream(SQUID_2_4_BASIC);
1328                 } else {
1329                         x_fprintf(x_stderr, "unknown helper protocol [%s]\n", helper_protocol);
1330                         exit(1);
1331                 }
1332         }
1333
1334         if (!opt_username) {
1335                 x_fprintf(x_stderr, "username must be specified!\n\n");
1336                 poptPrintHelp(pc, stderr, 0);
1337                 exit(1);
1338         }
1339
1340         if (opt_domain == NULL) {
1341                 opt_domain = get_winbind_domain();
1342         }
1343
1344         if (opt_workstation == NULL) {
1345                 opt_workstation = "";
1346         }
1347
1348         if (opt_challenge.length) {
1349                 if (!check_auth_crap()) {
1350                         exit(1);
1351                 }
1352                 exit(0);
1353         } 
1354
1355         if (!opt_password) {
1356                 opt_password = getpass("password: ");
1357         }
1358
1359         if (diagnostics) {
1360                 if (!diagnose_ntlm_auth()) {
1361                         exit(1);
1362                 }
1363         } else {
1364                 fstring user;
1365
1366                 snprintf(user, sizeof(user)-1, "%s%c%s", opt_domain, winbind_separator(), opt_username);
1367                 if (!check_plaintext_auth(user, opt_password, True)) {
1368                         exit(1);
1369                 }
1370         }
1371
1372         /* Exit code */
1373
1374         poptFreeContext(pc);
1375         return 0;
1376 }