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