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