Add some comments.
[kai/samba.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 nt_key[16];
626         char nt_hash[16];
627         DATA_BLOB chall = get_challenge();
628         char *error_string;
629         
630         ZERO_STRUCT(nt_key);
631
632         flags |= WINBIND_PAM_NTKEY;
633
634         SMBNTencrypt(opt_password,chall.data,nt_response.data);
635         E_md4hash(opt_password, nt_hash);
636         SMBsesskeygen_ntv1(nt_hash, NULL, session_key.data);
637
638         nt_status = contact_winbind_auth_crap(opt_username, opt_domain, 
639                                               opt_workstation,
640                                               &chall,
641                                               NULL,
642                                               &nt_response,
643                                               flags,
644                                               NULL,
645                                               nt_key,
646                                               &error_string);
647         
648         data_blob_free(&nt_response);
649
650         if (!NT_STATUS_IS_OK(nt_status)) {
651                 d_printf("%s (0x%x)\n", 
652                          error_string,
653                          NT_STATUS_V(nt_status));
654                 SAFE_FREE(error_string);
655                 return False;
656         }
657
658         if (memcmp(session_key.data, nt_key, 
659                    sizeof(nt_key)) != 0) {
660                 DEBUG(1, ("NT Session Key does not match expectations!\n"));
661                 DEBUG(1, ("nt_key:\n"));
662                 dump_data(1, nt_key, 16);
663                 DEBUG(1, ("expected:\n"));
664                 dump_data(1, session_key.data, session_key.length);
665                 pass = False;
666         }
667         return pass;
668 }
669
670 /* 
671  * Test the NTLM response only, but in the LM feild.
672  */
673
674 static BOOL test_ntlm_in_lm(void) 
675 {
676         BOOL pass = True;
677         NTSTATUS nt_status;
678         uint32 flags = 0;
679         DATA_BLOB nt_response = data_blob(NULL, 24);
680
681         uchar lm_key[8];
682         uchar lm_hash[16];
683         uchar nt_key[16];
684         DATA_BLOB chall = get_challenge();
685         char *error_string;
686         
687         ZERO_STRUCT(nt_key);
688
689         flags |= WINBIND_PAM_LMKEY;
690         flags |= WINBIND_PAM_NTKEY;
691
692         SMBNTencrypt(opt_password,chall.data,nt_response.data);
693
694         E_deshash(opt_password, lm_hash); 
695
696         nt_status = contact_winbind_auth_crap(opt_username, opt_domain, 
697                                               opt_workstation,
698                                               &chall,
699                                               &nt_response,
700                                               NULL,
701                                               flags,
702                                               lm_key,
703                                               nt_key,
704                                               &error_string);
705         
706         data_blob_free(&nt_response);
707
708         if (!NT_STATUS_IS_OK(nt_status)) {
709                 d_printf("%s (0x%x)\n", 
710                          error_string,
711                          NT_STATUS_V(nt_status));
712                 SAFE_FREE(error_string);
713                 return False;
714         }
715
716         if (memcmp(lm_hash, lm_key, 
717                    sizeof(lm_key)) != 0) {
718                 DEBUG(1, ("LM Key does not match expectations!\n"));
719                 DEBUG(1, ("lm_key:\n"));
720                 dump_data(1, lm_key, 8);
721                 DEBUG(1, ("expected:\n"));
722                 dump_data(1, lm_hash, 8);
723                 pass = False;
724         }
725         if (memcmp(lm_hash, nt_key, 8) != 0) {
726                 DEBUG(1, ("Session Key (first 8 lm hash) does not match expectations!\n"));
727                 DEBUG(1, ("nt_key:\n"));
728                 dump_data(1, nt_key, 16);
729                 DEBUG(1, ("expected:\n"));
730                 dump_data(1, lm_hash, 8);
731                 pass = False;
732         }
733         return pass;
734 }
735
736 /* 
737  * Test the NTLM response only, but in the both the NT and LM feilds.
738  */
739
740 static BOOL test_ntlm_in_both(void) 
741 {
742         BOOL pass = True;
743         NTSTATUS nt_status;
744         uint32 flags = 0;
745         DATA_BLOB nt_response = data_blob(NULL, 24);
746         DATA_BLOB session_key = data_blob(NULL, 16);
747
748         char lm_key[8];
749         char lm_hash[16];
750         char nt_key[16];
751         char nt_hash[16];
752         DATA_BLOB chall = get_challenge();
753         char *error_string;
754         
755         ZERO_STRUCT(lm_key);
756         ZERO_STRUCT(nt_key);
757
758         flags |= WINBIND_PAM_LMKEY;
759         flags |= WINBIND_PAM_NTKEY;
760
761         SMBNTencrypt(opt_password,chall.data,nt_response.data);
762         E_md4hash(opt_password, nt_hash);
763         SMBsesskeygen_ntv1(nt_hash, NULL, session_key.data);
764
765         E_deshash(opt_password, lm_hash); 
766
767         nt_status = contact_winbind_auth_crap(opt_username, opt_domain, 
768                                               opt_workstation,
769                                               &chall,
770                                               &nt_response,
771                                               &nt_response,
772                                               flags,
773                                               lm_key,
774                                               nt_key,
775                                               &error_string);
776         
777         data_blob_free(&nt_response);
778
779         if (!NT_STATUS_IS_OK(nt_status)) {
780                 d_printf("%s (0x%x)\n", 
781                          error_string,
782                          NT_STATUS_V(nt_status));
783                 SAFE_FREE(error_string);
784                 return False;
785         }
786
787         if (memcmp(lm_hash, lm_key, 
788                    sizeof(lm_key)) != 0) {
789                 DEBUG(1, ("LM Key does not match expectations!\n"));
790                 DEBUG(1, ("lm_key:\n"));
791                 dump_data(1, lm_key, 8);
792                 DEBUG(1, ("expected:\n"));
793                 dump_data(1, lm_hash, 8);
794                 pass = False;
795         }
796         if (memcmp(session_key.data, nt_key, 
797                    sizeof(nt_key)) != 0) {
798                 DEBUG(1, ("NT Session Key does not match expectations!\n"));
799                 DEBUG(1, ("nt_key:\n"));
800                 dump_data(1, nt_key, 16);
801                 DEBUG(1, ("expected:\n"));
802                 dump_data(1, session_key.data, session_key.length);
803                 pass = False;
804         }
805
806
807         return pass;
808 }
809
810 /* 
811  * Test the NTLMv2 response only
812  */
813
814 static BOOL test_ntlmv2(void) 
815 {
816         BOOL pass = True;
817         NTSTATUS nt_status;
818         uint32 flags = 0;
819         DATA_BLOB ntlmv2_response = data_blob(NULL, 0);
820         DATA_BLOB lmv2_response = data_blob(NULL, 0);
821         DATA_BLOB nt_session_key = data_blob(NULL, 0);
822         DATA_BLOB lm_session_key = data_blob(NULL, 0);
823
824         uchar lm_key[16];
825         uchar nt_key[16];
826         DATA_BLOB chall = get_challenge();
827         char *error_string;
828
829         ZERO_STRUCT(lm_key);
830         ZERO_STRUCT(nt_key);
831         
832         flags |= WINBIND_PAM_LMKEY;
833         flags |= WINBIND_PAM_NTKEY;
834
835         if (!SMBNTLMv2encrypt(opt_username, opt_domain, opt_password, chall,
836                               &lmv2_response, &ntlmv2_response, 
837                               &lm_session_key, &nt_session_key)) {
838                 return False;
839         }
840
841         nt_status = contact_winbind_auth_crap(opt_username, opt_domain, 
842                                               opt_workstation,
843                                               &chall,
844                                               NULL, 
845                                               &ntlmv2_response,
846                                               flags,
847                                               lm_key,
848                                               nt_key,
849                                               &error_string);
850         
851         data_blob_free(&lmv2_response);
852         data_blob_free(&ntlmv2_response);
853
854         if (!NT_STATUS_IS_OK(nt_status)) {
855                 d_printf("%s (0x%x)\n", 
856                          error_string,
857                          NT_STATUS_V(nt_status));
858                 SAFE_FREE(error_string);
859                 return False;
860         }
861
862 #if 0
863         if (memcmp(lm_session_key.data, lm_key, 
864                    sizeof(lm_key)) != 0) {
865                 DEBUG(1, ("LM Session Key does not match expectations!\n"));
866                 DEBUG(1, ("lm_key:\n"));
867                 dump_data(1, lm_key, 16);
868                 DEBUG(1, ("expected:\n"));
869                 dump_data(1, lm_session_key.data, lm_session_key.length);
870                 pass = False;
871         }
872         if (memcmp(nt_session_key.data, nt_key, 
873                    sizeof(nt_key)) != 0) {
874                 DEBUG(1, ("NT Session Key does not match expectations!\n"));
875                 DEBUG(1, ("nt_key:\n"));
876                 dump_data(1, nt_key, 16);
877                 DEBUG(1, ("expected:\n"));
878                 dump_data(1, nt_session_key.data, nt_session_key.length);
879                 pass = False;
880         }
881 #endif
882         return pass;
883 }
884
885 /* 
886  * Test the NTLMv2 and LMv2 responses
887  */
888
889 static BOOL test_lmv2_ntlmv2(void) 
890 {
891         BOOL pass = True;
892         NTSTATUS nt_status;
893         uint32 flags = 0;
894         DATA_BLOB ntlmv2_response = data_blob(NULL, 0);
895         DATA_BLOB lmv2_response = data_blob(NULL, 0);
896         DATA_BLOB nt_session_key = data_blob(NULL, 0);
897         DATA_BLOB lm_session_key = data_blob(NULL, 0);
898
899         uchar lm_key[16];
900         uchar nt_key[16];
901         DATA_BLOB chall = get_challenge();
902         char *error_string;
903
904         ZERO_STRUCT(nt_key);
905         ZERO_STRUCT(lm_key);
906         
907         flags |= WINBIND_PAM_LMKEY;
908         flags |= WINBIND_PAM_NTKEY;
909
910         if (!SMBNTLMv2encrypt(opt_username, opt_domain, opt_password, chall,
911                               &lmv2_response, &ntlmv2_response, 
912                               &lm_session_key, &nt_session_key)) {
913                 return False;
914         }
915
916         nt_status = contact_winbind_auth_crap(opt_username, opt_domain, 
917                                               opt_workstation,
918                                               &chall,
919                                               &lmv2_response,
920                                               &ntlmv2_response,
921                                               flags,
922                                               lm_key,
923                                               nt_key,
924                                               &error_string);
925         
926         data_blob_free(&lmv2_response);
927         data_blob_free(&ntlmv2_response);
928
929         if (!NT_STATUS_IS_OK(nt_status)) {
930                 d_printf("%s (0x%x)\n", 
931                          error_string,
932                          NT_STATUS_V(nt_status));
933                 SAFE_FREE(error_string);
934                 return False;
935         }
936
937 #if 0
938         if (memcmp(lm_session_key.data, lm_key, 
939                    sizeof(lm_key)) != 0) {
940                 DEBUG(1, ("LM Session Key does not match expectations!\n"));
941                 DEBUG(1, ("lm_key:\n"));
942                 dump_data(1, lm_key, 16);
943                 DEBUG(1, ("expected:\n"));
944                 dump_data(1, lm_session_key.data, lm_session_key.length);
945                 pass = False;
946         }
947         if (memcmp(nt_session_key.data, nt_key, 
948                    sizeof(nt_key)) != 0) {
949                 DEBUG(1, ("NT Session Key does not match expectations!\n"));
950                 DEBUG(1, ("nt_key:\n"));
951                 dump_data(1, nt_key, 16);
952                 DEBUG(1, ("expected:\n"));
953                 dump_data(1, nt_session_key.data, nt_session_key.length);
954                 pass = False;
955         }
956 #endif
957         return pass;
958 }
959
960 /* 
961  * Test the LMv2 response only
962  */
963
964 static BOOL test_lmv2(void) 
965 {
966         BOOL pass = True;
967         NTSTATUS nt_status;
968         uint32 flags = 0;
969         DATA_BLOB ntlmv2_response = data_blob(NULL, 0);
970         DATA_BLOB lmv2_response = data_blob(NULL, 0);
971         DATA_BLOB nt_session_key = data_blob(NULL, 0);
972         DATA_BLOB lm_session_key = data_blob(NULL, 0);
973
974         uchar lm_key[16];
975         uchar nt_key[16];
976         DATA_BLOB chall = get_challenge();
977         char *error_string;
978
979         ZERO_STRUCT(nt_key);
980         ZERO_STRUCT(lm_key);
981         
982         flags |= WINBIND_PAM_LMKEY;
983         flags |= WINBIND_PAM_NTKEY;
984
985         if (!SMBNTLMv2encrypt(opt_username, opt_domain, opt_password, chall,
986                               &lmv2_response, &ntlmv2_response, 
987                               &lm_session_key, &nt_session_key)) {
988                 return False;
989         }
990
991         nt_status = contact_winbind_auth_crap(opt_username, opt_domain, 
992                                               opt_workstation,
993                                               &chall,
994                                               &lmv2_response,
995                                               NULL, 
996                                               flags,
997                                               lm_key,
998                                               nt_key,
999                                               &error_string);
1000         
1001         data_blob_free(&lmv2_response);
1002         data_blob_free(&ntlmv2_response);
1003
1004         if (!NT_STATUS_IS_OK(nt_status)) {
1005                 d_printf("%s (0x%x)\n", 
1006                          error_string,
1007                          NT_STATUS_V(nt_status));
1008                 SAFE_FREE(error_string);
1009                 return False;
1010         }
1011
1012 #if 0   
1013         if (memcmp(lm_session_key.data, lm_key, 
1014                    sizeof(lm_key)) != 0) {
1015                 DEBUG(1, ("LM Session Key does not match expectations!\n"));
1016                 DEBUG(1, ("lm_key:\n"));
1017                 dump_data(1, lm_key, 16);
1018                 DEBUG(1, ("expected:\n"));
1019                 dump_data(1, lm_session_key.data, lm_session_key.length);
1020                 pass = False;
1021         }
1022         if (memcmp(nt_session_key.data, nt_key, 
1023                    sizeof(nt_key)) != 0) {
1024                 DEBUG(1, ("NT Session Key does not match expectations!\n"));
1025                 DEBUG(1, ("nt_key:\n"));
1026                 dump_data(1, nt_key, 16);
1027                 DEBUG(1, ("expected:\n"));
1028                 dump_data(1, nt_session_key.data, nt_session_key.length);
1029                 pass = False;
1030         }
1031 #endif
1032         return pass;
1033 }
1034
1035 /* 
1036    Tests:
1037    
1038    - LM only
1039    - NT and LM             
1040    - NT
1041    - NT in LM feild
1042    - NT in both feilds
1043    - NTLMv2
1044    - NTLMv2 and LMv2
1045    - LMv2
1046    
1047    check we get the correct session key in each case
1048    check what values we get for the LM session key
1049    
1050 */
1051
1052 struct ntlm_tests {
1053         BOOL (*fn)(void);
1054         const char *name;
1055 } test_table[] = {
1056         {test_lm, "test LM"},
1057         {test_lm_ntlm, "test LM and NTLM"},
1058         {test_ntlm, "test NTLM"},
1059         {test_ntlm_in_lm, "test NTLM in LM"},
1060         {test_ntlm_in_both, "test NTLM in both"},
1061         {test_ntlmv2, "test NTLMv2"},
1062         {test_lmv2_ntlmv2, "test NTLMv2 and LMv2"},
1063         {test_lmv2, "test LMv2"}
1064 };
1065
1066 static BOOL diagnose_ntlm_auth(void)
1067 {
1068         unsigned int i;
1069         BOOL pass = True;
1070
1071         for (i=0; test_table[i].fn; i++) {
1072                 if (!test_table[i].fn()) {
1073                         DEBUG(1, ("Test %s failed!\n", test_table[i].name));
1074                         pass = False;
1075                 }
1076         }
1077
1078         return pass;
1079 }
1080
1081 /* Main program */
1082
1083 enum {
1084         OPT_USERNAME = 1000,
1085         OPT_DOMAIN,
1086         OPT_WORKSTATION,
1087         OPT_CHALLENGE,
1088         OPT_RESPONSE,
1089         OPT_LM,
1090         OPT_NT,
1091         OPT_PASSWORD,
1092         OPT_LM_KEY,
1093         OPT_NT_KEY,
1094         OPT_DIAGNOSTICS
1095 };
1096
1097  int main(int argc, const char **argv)
1098 {
1099         int opt;
1100         static const char *helper_protocol;
1101         static int diagnostics;
1102
1103         static const char *hex_challenge;
1104         static const char *hex_lm_response;
1105         static const char *hex_nt_response;
1106         char *challenge;
1107         char *lm_response;
1108         char *nt_response;
1109         size_t challenge_len;
1110         size_t lm_response_len;
1111         size_t nt_response_len;
1112
1113         poptContext pc;
1114
1115         /* NOTE: DO NOT change this interface without considering the implications!
1116            This is an external interface, which other programs will use to interact 
1117            with this helper.
1118         */
1119
1120         /* We do not use single-letter command abbreviations, because they harm future 
1121            interface stability. */
1122
1123         struct poptOption long_options[] = {
1124                 POPT_AUTOHELP
1125                 { "helper-protocol", 0, POPT_ARG_STRING, &helper_protocol, OPT_DOMAIN, "operate as a stdio-based helper", "helper protocol to use"},
1126                 { "username", 0, POPT_ARG_STRING, &opt_username, OPT_USERNAME, "username"},
1127                 { "domain", 0, POPT_ARG_STRING, &opt_domain, OPT_DOMAIN, "domain name"},
1128                 { "workstation", 0, POPT_ARG_STRING, &opt_workstation, OPT_WORKSTATION, "workstation"},
1129                 { "challenge", 0, POPT_ARG_STRING, &hex_challenge, OPT_CHALLENGE, "challenge (HEX encoded)"},
1130                 { "lm-response", 0, POPT_ARG_STRING, &hex_lm_response, OPT_LM, "LM Response to the challenge (HEX encoded)"},
1131                 { "nt-response", 0, POPT_ARG_STRING, &hex_nt_response, OPT_NT, "NT or NTLMv2 Response to the challenge (HEX encoded)"},
1132                 { "password", 0, POPT_ARG_STRING, &opt_password, OPT_PASSWORD, "User's plaintext password"},            
1133                 { "request-lm-key", 0, POPT_ARG_NONE, &request_lm_key, OPT_LM_KEY, "Retreive LM session key"},
1134                 { "request-nt-key", 0, POPT_ARG_NONE, &request_nt_key, OPT_NT_KEY, "Retreive NT session key"},
1135                 { "diagnostics", 0, POPT_ARG_NONE, &diagnostics, OPT_DIAGNOSTICS, "Perform diagnostics on the authentictaion chain"},
1136                 POPT_COMMON_SAMBA
1137                 POPT_TABLEEND
1138         };
1139
1140         /* Samba client initialisation */
1141
1142         dbf = x_stderr;
1143         
1144         /* Parse options */
1145
1146         pc = poptGetContext("ntlm_auth", argc, argv, long_options, 0);
1147
1148         /* Parse command line options */
1149
1150         if (argc == 1) {
1151                 poptPrintHelp(pc, stderr, 0);
1152                 return 1;
1153         }
1154
1155         pc = poptGetContext(NULL, argc, (const char **)argv, long_options, 
1156                             POPT_CONTEXT_KEEP_FIRST);
1157
1158         while((opt = poptGetNextOpt(pc)) != -1) {
1159                 switch (opt) {
1160                 case OPT_CHALLENGE:
1161                         challenge = smb_xmalloc((strlen(hex_challenge)+1)/2);
1162                         if ((challenge_len = strhex_to_str(challenge, 
1163                                                            strlen(hex_challenge), 
1164                                                            hex_challenge)) != 8) {
1165                                 x_fprintf(x_stderr, "hex decode of %s failed (only got %u bytes)!\n", 
1166                                         hex_challenge, challenge_len);
1167                                 exit(1);
1168                         }
1169                         opt_challenge = data_blob(challenge, challenge_len);
1170                         SAFE_FREE(challenge);
1171                         break;
1172                 case OPT_LM: 
1173                         lm_response = smb_xmalloc((strlen(hex_lm_response)+1)/2);
1174                         lm_response_len = strhex_to_str(lm_response,    
1175                                                         strlen(hex_lm_response), 
1176                                                         hex_lm_response);
1177                         if (lm_response_len != 24) {
1178                                 x_fprintf(x_stderr, "hex decode of %s failed!\n", hex_lm_response);
1179                                 exit(1);
1180                         }
1181                         opt_lm_response = data_blob(lm_response, lm_response_len);
1182                         SAFE_FREE(lm_response);
1183                         break;
1184                 case OPT_NT: 
1185                         nt_response = smb_xmalloc((strlen(hex_nt_response)+1)/2);
1186                         nt_response_len = strhex_to_str(nt_response, 
1187                                                         strlen(hex_nt_response), 
1188                                                         hex_nt_response);
1189                         if (nt_response_len < 24) {
1190                                 x_fprintf(x_stderr, "hex decode of %s failed!\n", hex_nt_response);
1191                                 exit(1);
1192                         }
1193                         opt_nt_response = data_blob(nt_response, nt_response_len);
1194                         SAFE_FREE(nt_response);
1195                         break;
1196                 }
1197         }
1198
1199         if (helper_protocol) {
1200                 if (strcmp(helper_protocol, "squid-2.5-ntlmssp")== 0) {
1201                         squid_stream(SQUID_2_5_NTLMSSP);
1202                 } else if (strcmp(helper_protocol, "squid-2.5-basic")== 0) {
1203                         squid_stream(SQUID_2_5_BASIC);
1204                 } else if (strcmp(helper_protocol, "squid-2.4-basic")== 0) {
1205                         squid_stream(SQUID_2_4_BASIC);
1206                 } else {
1207                         x_fprintf(x_stderr, "unknown helper protocol [%s]\n", helper_protocol);
1208                         exit(1);
1209                 }
1210         }
1211
1212         if (!opt_username) {
1213                 x_fprintf(x_stderr, "username must be specified!\n\n");
1214                 poptPrintHelp(pc, stderr, 0);
1215                 exit(1);
1216         }
1217
1218         if (opt_domain == NULL) {
1219                 opt_domain = get_winbind_domain();
1220         }
1221
1222         if (opt_workstation == NULL) {
1223                 opt_workstation = "";
1224         }
1225
1226         if (opt_challenge.length) {
1227                 if (!check_auth_crap()) {
1228                         exit(1);
1229                 }
1230                 exit(0);
1231         } 
1232
1233         if (!opt_password) {
1234                 opt_password = getpass("password: ");
1235         }
1236
1237         if (diagnostics) {
1238                 if (!diagnose_ntlm_auth()) {
1239                         exit(1);
1240                 }
1241         } else {
1242                 fstring user;
1243
1244                 snprintf(user, sizeof(user)-1, "%s%c%s", opt_domain, winbind_separator(), opt_username);
1245                 if (!check_plaintext_auth(user, opt_password, True)) {
1246                         exit(1);
1247                 }
1248         }
1249
1250         /* Exit code */
1251
1252         poptFreeContext(pc);
1253         return 0;
1254 }