error_string, not error_message...
[ira/wip.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 *helper_protocol;
42 static const char *opt_username;
43 static const char *opt_domain;
44 static const char *opt_workstation;
45 static const char *opt_password;
46 static DATA_BLOB opt_challenge;
47 static DATA_BLOB opt_lm_response;
48 static DATA_BLOB opt_nt_response;
49 static int request_lm_key;
50 static int request_nt_key;
51 static int diagnostics;
52
53
54 static char winbind_separator(void)
55 {
56         struct winbindd_response response;
57         static BOOL got_sep;
58         static char sep;
59
60         if (got_sep)
61                 return sep;
62
63         ZERO_STRUCT(response);
64
65         /* Send off request */
66
67         if (winbindd_request(WINBINDD_INFO, NULL, &response) !=
68             NSS_STATUS_SUCCESS) {
69                 d_printf("could not obtain winbind separator!\n");
70                 return '\\';
71         }
72
73         sep = response.data.info.winbind_separator;
74         got_sep = True;
75
76         if (!sep) {
77                 d_printf("winbind separator was NULL!\n");
78                 return '\\';
79         }
80         
81         return sep;
82 }
83
84 static const char *get_winbind_domain(void)
85 {
86         struct winbindd_response response;
87
88         static fstring winbind_domain;
89         if (*winbind_domain) {
90                 return winbind_domain;
91         }
92
93         ZERO_STRUCT(response);
94
95         /* Send off request */
96
97         if (winbindd_request(WINBINDD_DOMAIN_NAME, NULL, &response) !=
98             NSS_STATUS_SUCCESS) {
99                 d_printf("could not obtain winbind domain name!\n");
100                 return NULL;
101         }
102
103         fstrcpy(winbind_domain, response.data.domain_name);
104
105         return winbind_domain;
106
107 }
108
109 static const char *get_winbind_netbios_name(void)
110 {
111         struct winbindd_response response;
112
113         static fstring winbind_netbios_name;
114
115         if (*winbind_netbios_name) {
116                 return winbind_netbios_name;
117         }
118
119         ZERO_STRUCT(response);
120
121         /* Send off request */
122
123         if (winbindd_request(WINBINDD_NETBIOS_NAME, NULL, &response) !=
124             NSS_STATUS_SUCCESS) {
125                 d_printf("could not obtain winbind netbios name!\n");
126                 return NULL;
127         }
128
129         fstrcpy(winbind_netbios_name, response.data.netbios_name);
130
131         return winbind_netbios_name;
132
133 }
134
135 /* Authenticate a user with a plaintext password */
136
137 static BOOL check_plaintext_auth(const char *user, const char *pass, BOOL stdout_diagnostics)
138 {
139         struct winbindd_request request;
140         struct winbindd_response response;
141         NSS_STATUS result;
142
143         /* Send off request */
144
145         ZERO_STRUCT(request);
146         ZERO_STRUCT(response);
147
148         fstrcpy(request.data.auth.user, user);
149         fstrcpy(request.data.auth.pass, pass);
150
151         result = winbindd_request(WINBINDD_PAM_AUTH, &request, &response);
152
153         /* Display response */
154         
155         if (stdout_diagnostics) {
156                 if ((result != NSS_STATUS_SUCCESS) && (response.data.auth.nt_status == 0)) {
157                         d_printf("Reading winbind reply failed! (0x01)\n");
158                 }
159                 
160                 d_printf("%s (0x%x)\n", 
161                          response.data.auth.nt_status_string, 
162                          response.data.auth.nt_status);
163         } else {
164                 if ((result != NSS_STATUS_SUCCESS) && (response.data.auth.nt_status == 0)) {
165                         DEBUG(1, ("Reading winbind reply failed! (0x01)\n"));
166                 }
167                 
168                 DEBUG(3, ("%s (0x%x)\n", 
169                          response.data.auth.nt_status_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[16], 
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 const DATA_BLOB get_challenge(void) 
466 {
467         static DATA_BLOB chal;
468         if (opt_challenge.length)
469                 return opt_challenge;
470         
471         chal = data_blob(NULL, 8);
472
473         generate_random_buffer(chal.data, chal.length, False);
474         return chal;
475 }
476
477 static BOOL test_lm(void) 
478 {
479         NTSTATUS nt_status;
480         uint32 flags = 0;
481         DATA_BLOB lm_response = data_blob(NULL, 24);
482
483         uchar lm_key[8];
484         uchar lm_hash[16];
485         DATA_BLOB chall = get_challenge();
486         char *error_string;
487         
488         flags |= WINBIND_PAM_LMKEY;
489
490         SMBencrypt(opt_password,chall.data,lm_response.data);
491         E_deshash(opt_password, lm_hash); 
492
493         nt_status = contact_winbind_auth_crap(opt_username, opt_domain, opt_workstation,
494                                               &chall,
495                                               &lm_response,
496                                               NULL,
497                                               flags,
498                                               lm_key, 
499                                               NULL,
500                                               &error_string);
501         
502         data_blob_free(&lm_response);
503
504         if (!NT_STATUS_IS_OK(nt_status)) {
505                 d_printf("%s (0x%x)\n", 
506                          error_string,
507                          NT_STATUS_V(nt_status));
508                 return False;
509         }
510
511         if (memcmp(lm_hash, lm_key, 
512                    sizeof(lm_key)) != 0) {
513                 DEBUG(1, ("LM Key does not match expectations!\n"));
514                 DEBUG(1, ("lm_key:\n"));
515                 dump_data(1, lm_key, 8);
516                 DEBUG(1, ("expected:\n"));
517                 dump_data(1, lm_hash, 8);
518         }
519         return True;
520 }
521
522 static BOOL test_lm_ntlm(void) 
523 {
524         BOOL pass = True;
525         NTSTATUS nt_status;
526         uint32 flags = 0;
527         DATA_BLOB lm_response = data_blob(NULL, 24);
528         DATA_BLOB nt_response = data_blob(NULL, 24);
529         DATA_BLOB session_key = data_blob(NULL, 16);
530
531         uchar lm_key[8];
532         uchar nt_key[16];
533         uchar lm_hash[16];
534         uchar nt_hash[16];
535         DATA_BLOB chall = get_challenge();
536         char *error_string;
537         
538         flags |= WINBIND_PAM_LMKEY;
539         flags |= WINBIND_PAM_NTKEY;
540
541         SMBencrypt(opt_password,chall.data,lm_response.data);
542         E_deshash(opt_password, lm_hash); 
543
544         SMBNTencrypt(opt_password,chall.data,nt_response.data);
545
546         E_md4hash(opt_password, nt_hash);
547         SMBsesskeygen_ntv1(nt_hash, NULL, session_key.data);
548
549         nt_status = contact_winbind_auth_crap(opt_username, opt_domain, 
550                                               opt_workstation,
551                                               &chall,
552                                               &lm_response,
553                                               &nt_response,
554                                               flags,
555                                               lm_key, 
556                                               nt_key,
557                                               &error_string);
558         
559         data_blob_free(&lm_response);
560
561         if (!NT_STATUS_IS_OK(nt_status)) {
562                 d_printf("%s (0x%x)\n", 
563                          error_string,
564                          NT_STATUS_V(nt_status));
565                 SAFE_FREE(error_string);
566                 return False;
567         }
568
569         if (memcmp(lm_hash, lm_key, 
570                    sizeof(lm_key)) != 0) {
571                 DEBUG(1, ("LM Key does not match expectations!\n"));
572                 DEBUG(1, ("lm_key:\n"));
573                 dump_data(1, lm_key, 8);
574                 DEBUG(1, ("expected:\n"));
575                 dump_data(1, lm_hash, 8);
576                 pass = False;
577         }
578         if (memcmp(session_key.data, nt_key, 
579                    sizeof(nt_key)) != 0) {
580                 DEBUG(1, ("NT Session Key does not match expectations!\n"));
581                 DEBUG(1, ("nt_key:\n"));
582                 dump_data(1, nt_key, 16);
583                 DEBUG(1, ("expected:\n"));
584                 dump_data(1, session_key.data, session_key.length);
585                 pass = False;
586         }
587         return pass;
588 }
589
590 static BOOL test_ntlm(void) 
591 {
592         BOOL pass = True;
593         NTSTATUS nt_status;
594         uint32 flags = 0;
595         DATA_BLOB nt_response = data_blob(NULL, 24);
596         DATA_BLOB session_key = data_blob(NULL, 16);
597
598         char nt_key[16];
599         char nt_hash[16];
600         DATA_BLOB chall = get_challenge();
601         char *error_string;
602         
603         flags |= WINBIND_PAM_NTKEY;
604
605         SMBNTencrypt(opt_password,chall.data,nt_response.data);
606         E_md4hash(opt_password, nt_hash);
607         SMBsesskeygen_ntv1(nt_hash, NULL, session_key.data);
608
609         nt_status = contact_winbind_auth_crap(opt_username, opt_domain, 
610                                               opt_workstation,
611                                               &chall,
612                                               NULL,
613                                               &nt_response,
614                                               flags,
615                                               NULL,
616                                               nt_key,
617                                               &error_string);
618         
619         data_blob_free(&nt_response);
620
621         if (!NT_STATUS_IS_OK(nt_status)) {
622                 d_printf("%s (0x%x)\n", 
623                          error_string,
624                          NT_STATUS_V(nt_status));
625                 SAFE_FREE(error_string);
626                 return False;
627         }
628
629         if (memcmp(session_key.data, nt_key, 
630                    sizeof(nt_key)) != 0) {
631                 DEBUG(1, ("NT Session Key does not match expectations!\n"));
632                 DEBUG(1, ("nt_key:\n"));
633                 dump_data(1, nt_key, 16);
634                 DEBUG(1, ("expected:\n"));
635                 dump_data(1, session_key.data, session_key.length);
636                 pass = False;
637         }
638         return pass;
639 }
640
641 /* 
642    Tests:
643    
644    - LM only
645    - NT and LM             
646    - NT
647    - NTLMv2
648    - NTLMv2 and LMv2
649    - LMv2
650    
651    check we get the correct session key in each case
652    check what values we get for the LM session key
653    
654 */
655
656 struct ntlm_tests {
657         BOOL (*fn)();
658         const char *name;
659 } test_table[] = {
660         {test_lm, "test LM"},
661         {test_lm_ntlm, "test LM and NTLM"},
662         {test_ntlm, "test NTLM"}
663 /*      {test_lm_ntlmv2, "test NTLMv2"}, */
664 /*      {test_lm_ntlmv2, "test NTLMv2 and LMv2"}, */
665 /*      {test_lm_ntlmv2, "test LMv2"} */
666 };
667
668 static BOOL diagnose_ntlm_auth(void)
669 {
670         unsigned int i;
671         BOOL pass = True;
672
673         for (i=0; test_table[i].fn; i++) {
674                 if (!test_table[i].fn()) {
675                         DEBUG(1, ("Test %s failed!\n", test_table[i].name));
676                         pass = False;
677                 }
678         }
679
680         return pass;
681 }
682
683 /* Main program */
684
685 enum {
686         OPT_USERNAME = 1000,
687         OPT_DOMAIN,
688         OPT_WORKSTATION,
689         OPT_CHALLENGE,
690         OPT_RESPONSE,
691         OPT_LM,
692         OPT_NT,
693         OPT_PASSWORD,
694         OPT_LM_KEY,
695         OPT_NT_KEY,
696         OPT_DIAGNOSTICS
697 };
698
699  int main(int argc, const char **argv)
700 {
701         int opt;
702
703         static const char *hex_challenge;
704         static const char *hex_lm_response;
705         static const char *hex_nt_response;
706         char *challenge;
707         char *lm_response;
708         char *nt_response;
709         size_t challenge_len;
710         size_t lm_response_len;
711         size_t nt_response_len;
712
713         poptContext pc;
714
715         /* NOTE: DO NOT change this interface without considering the implications!
716            This is an external interface, which other programs will use to interact 
717            with this helper.
718         */
719
720         /* We do not use single-letter command abbreviations, because they harm future 
721            interface stability. */
722
723         struct poptOption long_options[] = {
724                 POPT_AUTOHELP
725                 { "helper-protocol", 0, POPT_ARG_STRING, &helper_protocol, OPT_DOMAIN, "operate as a stdio-based helper", "helper protocol to use"},
726                 { "username", 0, POPT_ARG_STRING, &opt_username, OPT_USERNAME, "username"},
727                 { "domain", 0, POPT_ARG_STRING, &opt_domain, OPT_DOMAIN, "domain name"},
728                 { "workstation", 0, POPT_ARG_STRING, &opt_workstation, OPT_WORKSTATION, "workstation"},
729                 { "challenge", 0, POPT_ARG_STRING, &hex_challenge, OPT_CHALLENGE, "challenge (HEX encoded)"},
730                 { "lm-response", 0, POPT_ARG_STRING, &hex_lm_response, OPT_LM, "LM Response to the challenge (HEX encoded)"},
731                 { "nt-response", 0, POPT_ARG_STRING, &hex_nt_response, OPT_NT, "NT or NTLMv2 Response to the challenge (HEX encoded)"},
732                 { "password", 0, POPT_ARG_STRING, &opt_password, OPT_PASSWORD, "User's plaintext password"},            
733                 { "request-lm-key", 0, POPT_ARG_NONE, &request_lm_key, OPT_LM_KEY, "Retreive LM session key"},
734                 { "request-nt-key", 0, POPT_ARG_NONE, &request_nt_key, OPT_NT_KEY, "Retreive NT session key"},
735                 { "diagnostics", 0, POPT_ARG_NONE, &diagnostics, OPT_DIAGNOSTICS, "Perform diagnostics on the authentictaion chain"},
736                 POPT_COMMON_SAMBA
737                 POPT_TABLEEND
738         };
739
740         /* Samba client initialisation */
741
742         dbf = x_stderr;
743         
744         /* Parse options */
745
746         pc = poptGetContext("ntlm_auth", argc, argv, long_options, 0);
747
748         /* Parse command line options */
749
750         if (argc == 1) {
751                 poptPrintHelp(pc, stderr, 0);
752                 return 1;
753         }
754
755         pc = poptGetContext(NULL, argc, (const char **)argv, long_options, 
756                             POPT_CONTEXT_KEEP_FIRST);
757
758         while((opt = poptGetNextOpt(pc)) != -1) {
759                 switch (opt) {
760                 case OPT_CHALLENGE:
761                         challenge = smb_xmalloc((strlen(hex_challenge)+1)/2);
762                         if ((challenge_len = strhex_to_str(challenge, 
763                                                            strlen(hex_challenge), 
764                                                            hex_challenge)) != 8) {
765                                 x_fprintf(x_stderr, "hex decode of %s failed (only got %u bytes)!\n", 
766                                         hex_challenge, challenge_len);
767                                 exit(1);
768                         }
769                         opt_challenge = data_blob(challenge, challenge_len);
770                         SAFE_FREE(challenge);
771                         break;
772                 case OPT_LM: 
773                         lm_response = smb_xmalloc((strlen(hex_lm_response)+1)/2);
774                         lm_response_len = strhex_to_str(lm_response,    
775                                                         strlen(hex_lm_response), 
776                                                         hex_lm_response);
777                         if (lm_response_len != 24) {
778                                 x_fprintf(x_stderr, "hex decode of %s failed!\n", hex_lm_response);
779                                 exit(1);
780                         }
781                         opt_lm_response = data_blob(lm_response, lm_response_len);
782                         SAFE_FREE(lm_response);
783                         break;
784                 case OPT_NT: 
785                         nt_response = smb_xmalloc((strlen(hex_nt_response)+1)/2);
786                         nt_response_len = strhex_to_str(nt_response, 
787                                                         strlen(hex_nt_response), 
788                                                         hex_nt_response);
789                         if (nt_response_len < 24) {
790                                 x_fprintf(x_stderr, "hex decode of %s failed!\n", hex_nt_response);
791                                 exit(1);
792                         }
793                         opt_nt_response = data_blob(nt_response, nt_response_len);
794                         SAFE_FREE(nt_response);
795                         break;
796                 }
797         }
798
799         if (helper_protocol) {
800                 if (strcmp(helper_protocol, "squid-2.5-ntlmssp")== 0) {
801                         squid_stream(SQUID_2_5_NTLMSSP);
802                 } else if (strcmp(helper_protocol, "squid-2.5-basic")== 0) {
803                         squid_stream(SQUID_2_5_BASIC);
804                 } else if (strcmp(helper_protocol, "squid-2.4-basic")== 0) {
805                         squid_stream(SQUID_2_4_BASIC);
806                 } else {
807                         x_fprintf(x_stderr, "unknown helper protocol [%s]\n", helper_protocol);
808                         exit(1);
809                 }
810         }
811
812         if (!opt_username) {
813                 x_fprintf(x_stderr, "username must be specified!\n\n");
814                 poptPrintHelp(pc, stderr, 0);
815                 exit(1);
816         }
817
818         if (opt_domain == NULL) {
819                 opt_domain = get_winbind_domain();
820         }
821
822         if (opt_workstation == NULL) {
823                 opt_workstation = "";
824         }
825
826         if (opt_challenge.length) {
827                 if (!check_auth_crap()) {
828                         exit(1);
829                 }
830                 exit(0);
831         } 
832
833         if (!opt_password) {
834                 opt_password = getpass("password: ");
835         }
836
837         if (diagnostics) {
838                 if (!diagnose_ntlm_auth()) {
839                         exit(1);
840                 }
841         } else {
842                 fstring user;
843
844                 snprintf(user, sizeof(user)-1, "%s%c%s", opt_domain, winbind_separator(), opt_username);
845                 if (!check_plaintext_auth(user, opt_password, True)) {
846                         exit(1);
847                 }
848         }
849
850         /* Exit code */
851
852         poptFreeContext(pc);
853         return 0;
854 }