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