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