445d2921121b81ba71ebeedc5bd9116f7fb23c23
[samba.git] / source3 / utils / ntlm_auth.c
1 /* 
2    Unix SMB/CIFS implementation.
3
4    Winbind status program.
5
6    Copyright (C) Tim Potter      2000-2002
7    Copyright (C) Andrew Bartlett <abartlet@samba.org> 2003
8    Copyright (C) Francesco Chemolli <kinkie@kame.usr.dsi.unimi.it> 2000 
9
10    This program is free software; you can redistribute it and/or modify
11    it under the terms of the GNU General Public License as published by
12    the Free Software Foundation; either version 2 of the License, or
13    (at your option) any later version.
14    
15    This program is distributed in the hope that it will be useful,
16    but WITHOUT ANY WARRANTY; without even the implied warranty of
17    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18    GNU General Public License for more details.
19    
20    You should have received a copy of the GNU General Public License
21    along with this program; if not, write to the Free Software
22    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
23 */
24
25 #include "includes.h"
26
27 #undef DBGC_CLASS
28 #define DBGC_CLASS DBGC_WINBIND
29
30 #define SQUID_BUFFER_SIZE 2010
31
32 enum squid_mode {
33         SQUID_2_4_BASIC,
34         SQUID_2_5_BASIC,
35         SQUID_2_5_NTLMSSP
36 };
37         
38
39 extern int winbindd_fd;
40
41 static const char *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: %s (0x%x)\n", 
161                          response.data.auth.nt_status_string, 
162                          response.data.auth.error_string, 
163                          response.data.auth.nt_status);
164         } else {
165                 if ((result != NSS_STATUS_SUCCESS) && (response.data.auth.nt_status == 0)) {
166                         DEBUG(1, ("Reading winbind reply failed! (0x01)\n"));
167                 }
168                 
169                 DEBUG(3, ("%s: %s (0x%x)\n", 
170                           response.data.auth.nt_status_string, 
171                           response.data.auth.error_string,
172                           response.data.auth.nt_status));               
173         }
174                 
175         return (result == NSS_STATUS_SUCCESS);
176 }
177
178 /* authenticate a user with an encrypted username/password */
179
180 static NTSTATUS contact_winbind_auth_crap(const char *username, 
181                                           const char *domain, 
182                                           const char *workstation,
183                                           const DATA_BLOB *challenge, 
184                                           const DATA_BLOB *lm_response, 
185                                           const DATA_BLOB *nt_response, 
186                                           uint32 flags, 
187                                           uint8 lm_key[16], 
188                                           uint8 nt_key[16], 
189                                           char **error_string) 
190 {
191         NTSTATUS nt_status;
192         NSS_STATUS result;
193         struct winbindd_request request;
194         struct winbindd_response response;
195
196         static uint8 zeros[16];
197
198         ZERO_STRUCT(request);
199         ZERO_STRUCT(response);
200
201         request.data.auth_crap.flags = flags;
202
203         fstrcpy(request.data.auth_crap.user, username);
204
205         fstrcpy(request.data.auth_crap.domain, domain);
206         fstrcpy(request.data.auth_crap.workstation, workstation);
207
208         memcpy(request.data.auth_crap.chal, challenge->data, MIN(challenge->length, 8));
209
210         if (lm_response && lm_response->length) {
211                 memcpy(request.data.auth_crap.lm_resp, lm_response->data, MIN(lm_response->length, sizeof(request.data.auth_crap.lm_resp)));
212                 request.data.auth_crap.lm_resp_len = lm_response->length;
213         }
214
215         if (nt_response && nt_response->length) {
216                 memcpy(request.data.auth_crap.nt_resp, nt_response->data, MIN(nt_response->length, sizeof(request.data.auth_crap.nt_resp)));
217                 request.data.auth_crap.nt_resp_len = nt_response->length;
218         }
219         
220         result = winbindd_request(WINBINDD_PAM_AUTH_CRAP, &request, &response);
221
222         /* Display response */
223
224         if ((result != NSS_STATUS_SUCCESS) && (response.data.auth.nt_status == 0)) {
225                 nt_status = NT_STATUS_UNSUCCESSFUL;
226                 if (error_string)
227                         *error_string = smb_xstrdup("Reading winbind reply failed!");
228                 return nt_status;
229         }
230         
231         nt_status = (NT_STATUS(response.data.auth.nt_status));
232         if (!NT_STATUS_IS_OK(nt_status)) {
233                 if (error_string) 
234                         *error_string = smb_xstrdup(response.data.auth.error_string);
235                 return nt_status;
236         }
237
238         if ((flags & WINBIND_PAM_LMKEY) && lm_key 
239             && (memcmp(zeros, response.data.auth.first_8_lm_hash, 
240                        sizeof(response.data.auth.first_8_lm_hash)) != 0)) {
241                 memcpy(lm_key, response.data.auth.first_8_lm_hash, 
242                         sizeof(response.data.auth.first_8_lm_hash));
243         }
244         if ((flags & WINBIND_PAM_NTKEY) && nt_key
245                     && (memcmp(zeros, response.data.auth.nt_session_key, 
246                                sizeof(response.data.auth.nt_session_key)) != 0)) {
247                 memcpy(nt_key, response.data.auth.nt_session_key, 
248                         sizeof(response.data.auth.nt_session_key));
249         }
250         return nt_status;
251 }
252                                    
253 static NTSTATUS winbind_pw_check(struct ntlmssp_state *ntlmssp_state) 
254 {
255         return contact_winbind_auth_crap(ntlmssp_state->user, ntlmssp_state->domain,
256                                          ntlmssp_state->workstation,
257                                          &ntlmssp_state->chal,
258                                          &ntlmssp_state->lm_resp,
259                                          &ntlmssp_state->nt_resp, 
260                                          0,
261                                          NULL, 
262                                          NULL, 
263                                          NULL);
264 }
265
266 static void manage_squid_ntlmssp_request(enum squid_mode squid_mode, 
267                                          char *buf, int length) 
268 {
269         static NTLMSSP_STATE *ntlmssp_state = NULL;
270         DATA_BLOB request, reply;
271         NTSTATUS nt_status;
272
273         if (strlen(buf) < 2) {
274                 DEBUG(1, ("NTLMSSP query [%s] invalid", buf));
275                 x_fprintf(x_stdout, "BH\n");
276                 return;
277         }
278
279         if (strlen(buf) > 3) {
280                 request = base64_decode_data_blob(buf + 3);
281         } else if (strcmp(buf, "YR") == 0) {
282                 request = data_blob(NULL, 0);
283                 if (ntlmssp_state)
284                         ntlmssp_server_end(&ntlmssp_state);
285         } else {
286                 DEBUG(1, ("NTLMSSP query [%s] invalid", buf));
287                 x_fprintf(x_stdout, "BH\n");
288                 return;
289         }
290
291         if (!ntlmssp_state) {
292                 ntlmssp_server_start(&ntlmssp_state);
293                 ntlmssp_state->check_password = winbind_pw_check;
294                 ntlmssp_state->get_domain = get_winbind_domain;
295                 ntlmssp_state->get_global_myname = get_winbind_netbios_name;
296         }
297
298         DEBUG(10, ("got NTLMSSP packet:\n"));
299         dump_data(10, request.data, request.length);
300
301         nt_status = ntlmssp_server_update(ntlmssp_state, request, &reply);
302         
303         if (NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
304                 char *reply_base64 = base64_encode_data_blob(reply);
305                 x_fprintf(x_stdout, "TT %s\n", reply_base64);
306                 SAFE_FREE(reply_base64);
307                 data_blob_free(&reply);
308                 DEBUG(10, ("NTLMSSP challenge\n"));
309         } else if (!NT_STATUS_IS_OK(nt_status)) {
310                 x_fprintf(x_stdout, "NA %s\n", nt_errstr(nt_status));
311                 DEBUG(10, ("NTLMSSP %s\n", nt_errstr(nt_status)));
312         } else {
313                 x_fprintf(x_stdout, "AF %s\\%s\n", ntlmssp_state->domain, ntlmssp_state->user);
314                 DEBUG(10, ("NTLMSSP OK!\n"));
315         }
316
317         data_blob_free(&request);
318 }
319
320 static void manage_squid_basic_request(enum squid_mode squid_mode, 
321                                        char *buf, int length) 
322 {
323         char *user, *pass;      
324         user=buf;
325         
326         pass=memchr(buf,' ',length);
327         if (!pass) {
328                 DEBUG(2, ("Password not found. Denying access\n"));
329                 x_fprintf(x_stderr, "ERR\n");
330                 return;
331         }
332         *pass='\0';
333         pass++;
334         
335         if (squid_mode == SQUID_2_5_BASIC) {
336                 rfc1738_unescape(user);
337                 rfc1738_unescape(pass);
338         }
339         
340         if (check_plaintext_auth(user, pass, False)) {
341                 x_fprintf(x_stdout, "OK\n");
342         } else {
343                 x_fprintf(x_stdout, "ERR\n");
344         }
345 }
346
347 static void manage_squid_request(enum squid_mode squid_mode) 
348 {
349         char buf[SQUID_BUFFER_SIZE+1];
350         int length;
351         char *c;
352         static BOOL err;
353
354         /* this is not a typo - x_fgets doesn't work too well under squid */
355         if (fgets(buf, sizeof(buf)-1, stdin) == NULL) {
356                 DEBUG(1, ("fgets() failed! dying..... errno=%d (%s)\n", ferror(stdin),
357                           strerror(ferror(stdin))));
358                 exit(1);    /* BIIG buffer */
359         }
360     
361         c=memchr(buf,'\n',sizeof(buf)-1);
362         if (c) {
363                 *c = '\0';
364                 length = c-buf;
365         } else {
366                 err = 1;
367                 return;
368         }
369         if (err) {
370                 DEBUG(2, ("Oversized message\n"));
371                 x_fprintf(x_stderr, "ERR\n");
372                 err = 0;
373                 return;
374         }
375
376         DEBUG(10, ("Got '%s' from squid (length: %d).\n",buf,length));
377
378         if (buf[0] == '\0') {
379                 DEBUG(2, ("Invalid Request\n"));
380                 x_fprintf(x_stderr, "ERR\n");
381                 return;
382         }
383         
384         if (squid_mode == SQUID_2_5_BASIC || squid_mode == SQUID_2_4_BASIC) {
385                 manage_squid_basic_request(squid_mode, buf, length);
386         } else if (squid_mode == SQUID_2_5_NTLMSSP) {
387                 manage_squid_ntlmssp_request(squid_mode, buf, length);
388         }
389 }
390
391
392 static void squid_stream(enum squid_mode squid_mode) {
393         /* initialize FDescs */
394         x_setbuf(x_stdout, NULL);
395         x_setbuf(x_stderr, NULL);
396         while(1) {
397                 manage_squid_request(squid_mode);
398         }
399 }
400
401
402 /* Authenticate a user with a challenge/response */
403
404 static BOOL check_auth_crap(void)
405 {
406         NTSTATUS nt_status;
407         uint32 flags = 0;
408         char lm_key[8];
409         char nt_key[16];
410         char *hex_lm_key;
411         char *hex_nt_key;
412         char *error_string;
413         
414         static uint8 zeros[16];
415
416         if (request_lm_key) 
417                 flags |= WINBIND_PAM_LMKEY;
418
419         if (request_nt_key) 
420                 flags |= WINBIND_PAM_NTKEY;
421
422         nt_status = contact_winbind_auth_crap(opt_username, opt_domain, 
423                                               opt_workstation,
424                                               &opt_challenge, 
425                                               &opt_lm_response, 
426                                               &opt_nt_response, 
427                                               flags,
428                                               lm_key, 
429                                               nt_key, 
430                                               &error_string);
431
432         if (!NT_STATUS_IS_OK(nt_status)) {
433                 d_printf("%s (0x%x)\n", 
434                          error_string,
435                          NT_STATUS_V(nt_status));
436                 SAFE_FREE(error_string);
437                 return False;
438         }
439
440         if (request_lm_key 
441             && (memcmp(zeros, lm_key, 
442                        sizeof(lm_key)) != 0)) {
443                 hex_encode(lm_key,
444                            sizeof(lm_key),
445                            &hex_lm_key);
446                 d_printf("LM_KEY: %s\n", hex_lm_key);
447                 SAFE_FREE(hex_lm_key);
448         }
449         if (request_nt_key 
450             && (memcmp(zeros, nt_key, 
451                        sizeof(nt_key)) != 0)) {
452                 hex_encode(nt_key, 
453                            sizeof(nt_key), 
454                            &hex_nt_key);
455                 d_printf("NT_KEY: %s\n", hex_nt_key);
456                 SAFE_FREE(hex_nt_key);
457         }
458
459         return True;
460 }
461
462 /* 
463    Authenticate a user with a challenge/response, checking session key
464    and valid authentication types
465 */
466
467 static DATA_BLOB get_challenge(void) 
468 {
469         static DATA_BLOB chal;
470         if (opt_challenge.length)
471                 return opt_challenge;
472         
473         chal = data_blob(NULL, 8);
474
475         generate_random_buffer(chal.data, chal.length, False);
476         return chal;
477 }
478
479 static BOOL test_lm(void) 
480 {
481         NTSTATUS nt_status;
482         uint32 flags = 0;
483         DATA_BLOB lm_response = data_blob(NULL, 24);
484
485         uchar lm_key[8];
486         uchar lm_hash[16];
487         DATA_BLOB chall = get_challenge();
488         char *error_string;
489         
490         flags |= WINBIND_PAM_LMKEY;
491
492         SMBencrypt(opt_password,chall.data,lm_response.data);
493         E_deshash(opt_password, lm_hash); 
494
495         nt_status = contact_winbind_auth_crap(opt_username, opt_domain, opt_workstation,
496                                               &chall,
497                                               &lm_response,
498                                               NULL,
499                                               flags,
500                                               lm_key, 
501                                               NULL,
502                                               &error_string);
503         
504         data_blob_free(&lm_response);
505
506         if (!NT_STATUS_IS_OK(nt_status)) {
507                 d_printf("%s (0x%x)\n", 
508                          error_string,
509                          NT_STATUS_V(nt_status));
510                 return False;
511         }
512
513         if (memcmp(lm_hash, lm_key, 
514                    sizeof(lm_key)) != 0) {
515                 DEBUG(1, ("LM Key does not match expectations!\n"));
516                 DEBUG(1, ("lm_key:\n"));
517                 dump_data(1, lm_key, 8);
518                 DEBUG(1, ("expected:\n"));
519                 dump_data(1, lm_hash, 8);
520         }
521         return True;
522 }
523
524 static BOOL test_lm_ntlm(void) 
525 {
526         BOOL pass = True;
527         NTSTATUS nt_status;
528         uint32 flags = 0;
529         DATA_BLOB lm_response = data_blob(NULL, 24);
530         DATA_BLOB nt_response = data_blob(NULL, 24);
531         DATA_BLOB session_key = data_blob(NULL, 16);
532
533         uchar lm_key[8];
534         uchar nt_key[16];
535         uchar lm_hash[16];
536         uchar nt_hash[16];
537         DATA_BLOB chall = get_challenge();
538         char *error_string;
539         
540         flags |= WINBIND_PAM_LMKEY;
541         flags |= WINBIND_PAM_NTKEY;
542
543         SMBencrypt(opt_password,chall.data,lm_response.data);
544         E_deshash(opt_password, lm_hash); 
545
546         SMBNTencrypt(opt_password,chall.data,nt_response.data);
547
548         E_md4hash(opt_password, nt_hash);
549         SMBsesskeygen_ntv1(nt_hash, NULL, session_key.data);
550
551         nt_status = contact_winbind_auth_crap(opt_username, opt_domain, 
552                                               opt_workstation,
553                                               &chall,
554                                               &lm_response,
555                                               &nt_response,
556                                               flags,
557                                               lm_key, 
558                                               nt_key,
559                                               &error_string);
560         
561         data_blob_free(&lm_response);
562
563         if (!NT_STATUS_IS_OK(nt_status)) {
564                 d_printf("%s (0x%x)\n", 
565                          error_string,
566                          NT_STATUS_V(nt_status));
567                 SAFE_FREE(error_string);
568                 return False;
569         }
570
571         if (memcmp(lm_hash, lm_key, 
572                    sizeof(lm_key)) != 0) {
573                 DEBUG(1, ("LM Key does not match expectations!\n"));
574                 DEBUG(1, ("lm_key:\n"));
575                 dump_data(1, lm_key, 8);
576                 DEBUG(1, ("expected:\n"));
577                 dump_data(1, lm_hash, 8);
578                 pass = False;
579         }
580         if (memcmp(session_key.data, nt_key, 
581                    sizeof(nt_key)) != 0) {
582                 DEBUG(1, ("NT Session Key does not match expectations!\n"));
583                 DEBUG(1, ("nt_key:\n"));
584                 dump_data(1, nt_key, 16);
585                 DEBUG(1, ("expected:\n"));
586                 dump_data(1, session_key.data, session_key.length);
587                 pass = False;
588         }
589         return pass;
590 }
591
592 static BOOL test_ntlm(void) 
593 {
594         BOOL pass = True;
595         NTSTATUS nt_status;
596         uint32 flags = 0;
597         DATA_BLOB nt_response = data_blob(NULL, 24);
598         DATA_BLOB session_key = data_blob(NULL, 16);
599
600         char nt_key[16];
601         char nt_hash[16];
602         DATA_BLOB chall = get_challenge();
603         char *error_string;
604         
605         flags |= WINBIND_PAM_NTKEY;
606
607         SMBNTencrypt(opt_password,chall.data,nt_response.data);
608         E_md4hash(opt_password, nt_hash);
609         SMBsesskeygen_ntv1(nt_hash, NULL, session_key.data);
610
611         nt_status = contact_winbind_auth_crap(opt_username, opt_domain, 
612                                               opt_workstation,
613                                               &chall,
614                                               NULL,
615                                               &nt_response,
616                                               flags,
617                                               NULL,
618                                               nt_key,
619                                               &error_string);
620         
621         data_blob_free(&nt_response);
622
623         if (!NT_STATUS_IS_OK(nt_status)) {
624                 d_printf("%s (0x%x)\n", 
625                          error_string,
626                          NT_STATUS_V(nt_status));
627                 SAFE_FREE(error_string);
628                 return False;
629         }
630
631         if (memcmp(session_key.data, nt_key, 
632                    sizeof(nt_key)) != 0) {
633                 DEBUG(1, ("NT Session Key does not match expectations!\n"));
634                 DEBUG(1, ("nt_key:\n"));
635                 dump_data(1, nt_key, 16);
636                 DEBUG(1, ("expected:\n"));
637                 dump_data(1, session_key.data, session_key.length);
638                 pass = False;
639         }
640         return pass;
641 }
642
643 /* 
644    Tests:
645    
646    - LM only
647    - NT and LM             
648    - NT
649    - NTLMv2
650    - NTLMv2 and LMv2
651    - LMv2
652    
653    check we get the correct session key in each case
654    check what values we get for the LM session key
655    
656 */
657
658 struct ntlm_tests {
659         BOOL (*fn)();
660         const char *name;
661 } test_table[] = {
662         {test_lm, "test LM"},
663         {test_lm_ntlm, "test LM and NTLM"},
664         {test_ntlm, "test NTLM"}
665 /*      {test_lm_ntlmv2, "test NTLMv2"}, */
666 /*      {test_lm_ntlmv2, "test NTLMv2 and LMv2"}, */
667 /*      {test_lm_ntlmv2, "test LMv2"} */
668 };
669
670 static BOOL diagnose_ntlm_auth(void)
671 {
672         unsigned int i;
673         BOOL pass = True;
674
675         for (i=0; test_table[i].fn; i++) {
676                 if (!test_table[i].fn()) {
677                         DEBUG(1, ("Test %s failed!\n", test_table[i].name));
678                         pass = False;
679                 }
680         }
681
682         return pass;
683 }
684
685 /* Main program */
686
687 enum {
688         OPT_USERNAME = 1000,
689         OPT_DOMAIN,
690         OPT_WORKSTATION,
691         OPT_CHALLENGE,
692         OPT_RESPONSE,
693         OPT_LM,
694         OPT_NT,
695         OPT_PASSWORD,
696         OPT_LM_KEY,
697         OPT_NT_KEY,
698         OPT_DIAGNOSTICS
699 };
700
701  int main(int argc, const char **argv)
702 {
703         int opt;
704
705         static const char *hex_challenge;
706         static const char *hex_lm_response;
707         static const char *hex_nt_response;
708         char *challenge;
709         char *lm_response;
710         char *nt_response;
711         size_t challenge_len;
712         size_t lm_response_len;
713         size_t nt_response_len;
714
715         poptContext pc;
716
717         /* NOTE: DO NOT change this interface without considering the implications!
718            This is an external interface, which other programs will use to interact 
719            with this helper.
720         */
721
722         /* We do not use single-letter command abbreviations, because they harm future 
723            interface stability. */
724
725         struct poptOption long_options[] = {
726                 POPT_AUTOHELP
727                 { "helper-protocol", 0, POPT_ARG_STRING, &helper_protocol, OPT_DOMAIN, "operate as a stdio-based helper", "helper protocol to use"},
728                 { "username", 0, POPT_ARG_STRING, &opt_username, OPT_USERNAME, "username"},
729                 { "domain", 0, POPT_ARG_STRING, &opt_domain, OPT_DOMAIN, "domain name"},
730                 { "workstation", 0, POPT_ARG_STRING, &opt_workstation, OPT_WORKSTATION, "workstation"},
731                 { "challenge", 0, POPT_ARG_STRING, &hex_challenge, OPT_CHALLENGE, "challenge (HEX encoded)"},
732                 { "lm-response", 0, POPT_ARG_STRING, &hex_lm_response, OPT_LM, "LM Response to the challenge (HEX encoded)"},
733                 { "nt-response", 0, POPT_ARG_STRING, &hex_nt_response, OPT_NT, "NT or NTLMv2 Response to the challenge (HEX encoded)"},
734                 { "password", 0, POPT_ARG_STRING, &opt_password, OPT_PASSWORD, "User's plaintext password"},            
735                 { "request-lm-key", 0, POPT_ARG_NONE, &request_lm_key, OPT_LM_KEY, "Retreive LM session key"},
736                 { "request-nt-key", 0, POPT_ARG_NONE, &request_nt_key, OPT_NT_KEY, "Retreive NT session key"},
737                 { "diagnostics", 0, POPT_ARG_NONE, &diagnostics, OPT_DIAGNOSTICS, "Perform diagnostics on the authentictaion chain"},
738                 POPT_COMMON_SAMBA
739                 POPT_TABLEEND
740         };
741
742         /* Samba client initialisation */
743
744         dbf = x_stderr;
745         
746         /* Parse options */
747
748         pc = poptGetContext("ntlm_auth", argc, argv, long_options, 0);
749
750         /* Parse command line options */
751
752         if (argc == 1) {
753                 poptPrintHelp(pc, stderr, 0);
754                 return 1;
755         }
756
757         pc = poptGetContext(NULL, argc, (const char **)argv, long_options, 
758                             POPT_CONTEXT_KEEP_FIRST);
759
760         while((opt = poptGetNextOpt(pc)) != -1) {
761                 switch (opt) {
762                 case OPT_CHALLENGE:
763                         challenge = smb_xmalloc((strlen(hex_challenge)+1)/2);
764                         if ((challenge_len = strhex_to_str(challenge, 
765                                                            strlen(hex_challenge), 
766                                                            hex_challenge)) != 8) {
767                                 x_fprintf(x_stderr, "hex decode of %s failed (only got %u bytes)!\n", 
768                                         hex_challenge, challenge_len);
769                                 exit(1);
770                         }
771                         opt_challenge = data_blob(challenge, challenge_len);
772                         SAFE_FREE(challenge);
773                         break;
774                 case OPT_LM: 
775                         lm_response = smb_xmalloc((strlen(hex_lm_response)+1)/2);
776                         lm_response_len = strhex_to_str(lm_response,    
777                                                         strlen(hex_lm_response), 
778                                                         hex_lm_response);
779                         if (lm_response_len != 24) {
780                                 x_fprintf(x_stderr, "hex decode of %s failed!\n", hex_lm_response);
781                                 exit(1);
782                         }
783                         opt_lm_response = data_blob(lm_response, lm_response_len);
784                         SAFE_FREE(lm_response);
785                         break;
786                 case OPT_NT: 
787                         nt_response = smb_xmalloc((strlen(hex_nt_response)+1)/2);
788                         nt_response_len = strhex_to_str(nt_response, 
789                                                         strlen(hex_nt_response), 
790                                                         hex_nt_response);
791                         if (nt_response_len < 24) {
792                                 x_fprintf(x_stderr, "hex decode of %s failed!\n", hex_nt_response);
793                                 exit(1);
794                         }
795                         opt_nt_response = data_blob(nt_response, nt_response_len);
796                         SAFE_FREE(nt_response);
797                         break;
798                 }
799         }
800
801         if (helper_protocol) {
802                 if (strcmp(helper_protocol, "squid-2.5-ntlmssp")== 0) {
803                         squid_stream(SQUID_2_5_NTLMSSP);
804                 } else if (strcmp(helper_protocol, "squid-2.5-basic")== 0) {
805                         squid_stream(SQUID_2_5_BASIC);
806                 } else if (strcmp(helper_protocol, "squid-2.4-basic")== 0) {
807                         squid_stream(SQUID_2_4_BASIC);
808                 } else {
809                         x_fprintf(x_stderr, "unknown helper protocol [%s]\n", helper_protocol);
810                         exit(1);
811                 }
812         }
813
814         if (!opt_username) {
815                 x_fprintf(x_stderr, "username must be specified!\n\n");
816                 poptPrintHelp(pc, stderr, 0);
817                 exit(1);
818         }
819
820         if (opt_domain == NULL) {
821                 opt_domain = get_winbind_domain();
822         }
823
824         if (opt_workstation == NULL) {
825                 opt_workstation = "";
826         }
827
828         if (opt_challenge.length) {
829                 if (!check_auth_crap()) {
830                         exit(1);
831                 }
832                 exit(0);
833         } 
834
835         if (!opt_password) {
836                 opt_password = getpass("password: ");
837         }
838
839         if (diagnostics) {
840                 if (!diagnose_ntlm_auth()) {
841                         exit(1);
842                 }
843         } else {
844                 fstring user;
845
846                 snprintf(user, sizeof(user)-1, "%s%c%s", opt_domain, winbind_separator(), opt_username);
847                 if (!check_plaintext_auth(user, opt_password, True)) {
848                         exit(1);
849                 }
850         }
851
852         /* Exit code */
853
854         poptFreeContext(pc);
855         return 0;
856 }