sync 3.0 into HEAD for the last time
[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         GSS_SPNEGO,
37         GSS_SPNEGO_CLIENT
38 };
39         
40
41 extern int winbindd_fd;
42
43 static const char *opt_username;
44 static const char *opt_domain;
45 static const char *opt_workstation;
46 static const char *opt_password;
47 static DATA_BLOB opt_challenge;
48 static DATA_BLOB opt_lm_response;
49 static DATA_BLOB opt_nt_response;
50 static int request_lm_key;
51 static int request_nt_key;
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[8], 
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.flags = flags;
202
203         if (push_utf8_fstring(request.data.auth_crap.user, username) == -1) {
204                 *error_string = smb_xstrdup(
205                         "unable to create utf8 string for username");
206                 return NT_STATUS_UNSUCCESSFUL;
207         }
208
209         if (push_utf8_fstring(request.data.auth_crap.domain, domain) == -1) {
210                 *error_string = smb_xstrdup(
211                         "unable to create utf8 string for domain");
212                 return NT_STATUS_UNSUCCESSFUL;
213         }
214
215         if (push_utf8_fstring(request.data.auth_crap.workstation, 
216                               workstation) == -1) {
217                 *error_string = smb_xstrdup(
218                         "unable to create utf8 string for workstation");
219                 return NT_STATUS_UNSUCCESSFUL;
220         }
221
222         memcpy(request.data.auth_crap.chal, challenge->data, MIN(challenge->length, 8));
223
224         if (lm_response && lm_response->length) {
225                 memcpy(request.data.auth_crap.lm_resp, lm_response->data, MIN(lm_response->length, sizeof(request.data.auth_crap.lm_resp)));
226                 request.data.auth_crap.lm_resp_len = lm_response->length;
227         }
228
229         if (nt_response && nt_response->length) {
230                 memcpy(request.data.auth_crap.nt_resp, nt_response->data, MIN(nt_response->length, sizeof(request.data.auth_crap.nt_resp)));
231                 request.data.auth_crap.nt_resp_len = nt_response->length;
232         }
233         
234         result = winbindd_request(WINBINDD_PAM_AUTH_CRAP, &request, &response);
235
236         /* Display response */
237
238         if ((result != NSS_STATUS_SUCCESS) && (response.data.auth.nt_status == 0)) {
239                 nt_status = NT_STATUS_UNSUCCESSFUL;
240                 if (error_string)
241                         *error_string = smb_xstrdup("Reading winbind reply failed!");
242                 return nt_status;
243         }
244         
245         nt_status = (NT_STATUS(response.data.auth.nt_status));
246         if (!NT_STATUS_IS_OK(nt_status)) {
247                 if (error_string) 
248                         *error_string = smb_xstrdup(response.data.auth.error_string);
249                 return nt_status;
250         }
251
252         if ((flags & WBFLAG_PAM_LMKEY) && lm_key 
253             && (memcmp(zeros, response.data.auth.first_8_lm_hash, 
254                        sizeof(response.data.auth.first_8_lm_hash)) != 0)) {
255                 memcpy(lm_key, response.data.auth.first_8_lm_hash, 
256                         sizeof(response.data.auth.first_8_lm_hash));
257         }
258         if ((flags & WBFLAG_PAM_NTKEY) && nt_key
259                     && (memcmp(zeros, response.data.auth.nt_session_key, 
260                                sizeof(response.data.auth.nt_session_key)) != 0)) {
261                 memcpy(nt_key, response.data.auth.nt_session_key, 
262                         sizeof(response.data.auth.nt_session_key));
263         }
264         return nt_status;
265 }
266                                    
267 static NTSTATUS winbind_pw_check(struct ntlmssp_state *ntlmssp_state) 
268 {
269         return contact_winbind_auth_crap(ntlmssp_state->user, ntlmssp_state->domain,
270                                          ntlmssp_state->workstation,
271                                          &ntlmssp_state->chal,
272                                          &ntlmssp_state->lm_resp,
273                                          &ntlmssp_state->nt_resp, 
274                                          0,
275                                          NULL, 
276                                          NULL, 
277                                          NULL);
278 }
279
280 static void manage_squid_ntlmssp_request(enum squid_mode squid_mode, 
281                                          char *buf, int length) 
282 {
283         static NTLMSSP_STATE *ntlmssp_state = NULL;
284         DATA_BLOB request, reply;
285         NTSTATUS nt_status;
286
287         if (strlen(buf) < 2) {
288                 DEBUG(1, ("NTLMSSP query [%s] invalid", buf));
289                 x_fprintf(x_stdout, "BH\n");
290                 return;
291         }
292
293         if (strlen(buf) > 3) {
294                 request = base64_decode_data_blob(buf + 3);
295         } else if (strcmp(buf, "YR") == 0) {
296                 request = data_blob(NULL, 0);
297                 if (ntlmssp_state)
298                         ntlmssp_server_end(&ntlmssp_state);
299         } else {
300                 DEBUG(1, ("NTLMSSP query [%s] invalid", buf));
301                 x_fprintf(x_stdout, "BH\n");
302                 return;
303         }
304
305         if (!ntlmssp_state) {
306                 ntlmssp_server_start(&ntlmssp_state);
307                 ntlmssp_state->check_password = winbind_pw_check;
308                 ntlmssp_state->get_domain = get_winbind_domain;
309                 ntlmssp_state->get_global_myname = get_winbind_netbios_name;
310         }
311
312         DEBUG(10, ("got NTLMSSP packet:\n"));
313         dump_data(10, (const char *)request.data, request.length);
314
315         nt_status = ntlmssp_server_update(ntlmssp_state, request, &reply);
316         
317         if (NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
318                 char *reply_base64 = base64_encode_data_blob(reply);
319                 x_fprintf(x_stdout, "TT %s\n", reply_base64);
320                 SAFE_FREE(reply_base64);
321                 data_blob_free(&reply);
322                 DEBUG(10, ("NTLMSSP challenge\n"));
323         } else if (!NT_STATUS_IS_OK(nt_status)) {
324                 x_fprintf(x_stdout, "NA %s\n", nt_errstr(nt_status));
325                 DEBUG(10, ("NTLMSSP %s\n", nt_errstr(nt_status)));
326         } else {
327                 x_fprintf(x_stdout, "AF %s\\%s\n", ntlmssp_state->domain, ntlmssp_state->user);
328                 DEBUG(10, ("NTLMSSP OK!\n"));
329         }
330
331         data_blob_free(&request);
332 }
333
334 static void manage_squid_basic_request(enum squid_mode squid_mode, 
335                                        char *buf, int length) 
336 {
337         char *user, *pass;      
338         user=buf;
339         
340         pass=memchr(buf,' ',length);
341         if (!pass) {
342                 DEBUG(2, ("Password not found. Denying access\n"));
343                 x_fprintf(x_stderr, "ERR\n");
344                 return;
345         }
346         *pass='\0';
347         pass++;
348         
349         if (squid_mode == SQUID_2_5_BASIC) {
350                 rfc1738_unescape(user);
351                 rfc1738_unescape(pass);
352         }
353         
354         if (check_plaintext_auth(user, pass, False)) {
355                 x_fprintf(x_stdout, "OK\n");
356         } else {
357                 x_fprintf(x_stdout, "ERR\n");
358         }
359 }
360
361 static void offer_gss_spnego_mechs(void) {
362
363         DATA_BLOB token;
364         SPNEGO_DATA spnego;
365         ssize_t len;
366         char *reply_base64;
367
368         pstring principal;
369         pstring myname_lower;
370
371         ZERO_STRUCT(spnego);
372
373         pstrcpy(myname_lower, global_myname());
374         strlower_m(myname_lower);
375
376         pstr_sprintf(principal, "%s$@%s", myname_lower, lp_realm());
377
378         /* Server negTokenInit (mech offerings) */
379         spnego.type = SPNEGO_NEG_TOKEN_INIT;
380         spnego.negTokenInit.mechTypes = smb_xmalloc(sizeof(char *) * 3);
381 #ifdef HAVE_KRB5
382         spnego.negTokenInit.mechTypes[0] = smb_xstrdup(OID_KERBEROS5_OLD);
383         spnego.negTokenInit.mechTypes[1] = smb_xstrdup(OID_NTLMSSP);
384         spnego.negTokenInit.mechTypes[2] = NULL;
385 #else
386         spnego.negTokenInit.mechTypes[0] = smb_xstrdup(OID_NTLMSSP);
387         spnego.negTokenInit.mechTypes[1] = NULL;
388 #endif
389
390
391         spnego.negTokenInit.mechListMIC = data_blob(principal,
392                                                     strlen(principal));
393
394         len = write_spnego_data(&token, &spnego);
395         free_spnego_data(&spnego);
396
397         if (len == -1) {
398                 DEBUG(1, ("Could not write SPNEGO data blob\n"));
399                 x_fprintf(x_stdout, "BH\n");
400                 return;
401         }
402
403         reply_base64 = base64_encode_data_blob(token);
404         x_fprintf(x_stdout, "TT %s *\n", reply_base64);
405
406         SAFE_FREE(reply_base64);
407         data_blob_free(&token);
408         DEBUG(10, ("sent SPNEGO negTokenInit\n"));
409         return;
410 }
411
412 static void manage_gss_spnego_request(enum squid_mode squid_mode,
413                                       char *buf, int length) 
414 {
415         static NTLMSSP_STATE *ntlmssp_state = NULL;
416         SPNEGO_DATA request, response;
417         DATA_BLOB token;
418         NTSTATUS status;
419         ssize_t len;
420
421         char *user = NULL;
422         char *domain = NULL;
423
424         const char *reply_code;
425         char       *reply_base64;
426         pstring     reply_argument;
427
428         if (strlen(buf) < 2) {
429
430                 if (ntlmssp_state != NULL) {
431                         DEBUG(1, ("Request for initial SPNEGO request where "
432                                   "we already have a state\n"));
433                         x_fprintf(x_stdout, "BH\n");
434                         return;
435                 }
436
437                 DEBUG(1, ("NTLMSSP query [%s] invalid", buf));
438                 x_fprintf(x_stdout, "BH\n");
439                 return;
440         }
441
442         if ( (strlen(buf) == 2) && (strcmp(buf, "YR") == 0) ) {
443
444                 /* Initial request, get the negTokenInit offering
445                    mechanisms */
446
447                 offer_gss_spnego_mechs();
448                 return;
449         }
450
451         /* All subsequent requests are "KK" (Knock, Knock ;)) and have
452            a blob. This might be negTokenInit or negTokenTarg */
453
454         if ( (strlen(buf) <= 3) || (strncmp(buf, "KK", 2) != 0) ) {
455                 DEBUG(1, ("GSS-SPNEGO query [%s] invalid\n", buf));
456                 x_fprintf(x_stdout, "BH\n");
457                 return;
458         }
459
460         token = base64_decode_data_blob(buf + 3);
461         len = read_spnego_data(token, &request);
462         data_blob_free(&token);
463
464         if (len == -1) {
465                 DEBUG(1, ("GSS-SPNEGO query [%s] invalid", buf));
466                 x_fprintf(x_stdout, "BH\n");
467                 return;
468         }
469
470         if (request.type == SPNEGO_NEG_TOKEN_INIT) {
471
472                 /* Second request from Client. This is where the
473                    client offers its mechanism to use. We currently
474                    only support NTLMSSP, the decision for Kerberos
475                    would be taken here. */
476
477                 if ( (request.negTokenInit.mechTypes == NULL) ||
478                      (request.negTokenInit.mechTypes[0] == NULL) ) {
479                         DEBUG(1, ("Client did not offer any mechanism"));
480                         x_fprintf(x_stdout, "BH\n");
481                         return;
482                 }
483
484                 if (strcmp(request.negTokenInit.mechTypes[0], OID_NTLMSSP) == 0) {
485
486                         if ( request.negTokenInit.mechToken.data == NULL ) {
487                                 DEBUG(1, ("Client did not provide  NTLMSSP data\n"));
488                                 x_fprintf(x_stdout, "BH\n");
489                                 return;
490                         }
491
492                         if ( ntlmssp_state != NULL ) {
493                                 DEBUG(1, ("Client wants a new NTLMSSP challenge, but "
494                                           "already got one\n"));
495                                 x_fprintf(x_stdout, "BH\n");
496                                 ntlmssp_server_end(&ntlmssp_state);
497                                 return;
498                         }
499
500                         ntlmssp_server_start(&ntlmssp_state);
501                         ntlmssp_state->check_password = winbind_pw_check;
502                         ntlmssp_state->get_domain = get_winbind_domain;
503                         ntlmssp_state->get_global_myname = get_winbind_netbios_name;
504
505                         DEBUG(10, ("got NTLMSSP packet:\n"));
506                         dump_data(10, (const char *)request.negTokenInit.mechToken.data,
507                                   request.negTokenInit.mechToken.length);
508
509                         response.type = SPNEGO_NEG_TOKEN_TARG;
510                         response.negTokenTarg.supportedMech = strdup(OID_NTLMSSP);
511                         response.negTokenTarg.mechListMIC = data_blob(NULL, 0);
512
513                         status = ntlmssp_server_update(ntlmssp_state,
514                                                        request.negTokenInit.mechToken,
515                                                        &response.negTokenTarg.responseToken);
516                 }
517
518 #ifdef HAVE_KRB5
519                 if (strcmp(request.negTokenInit.mechTypes[0], OID_KERBEROS5_OLD) == 0) {
520
521                         char *principal;
522                         DATA_BLOB auth_data;
523                         DATA_BLOB ap_rep;
524                         uint8 session_key[16];
525
526                         if ( request.negTokenInit.mechToken.data == NULL ) {
527                                 DEBUG(1, ("Client did not provide Kerberos data\n"));
528                                 x_fprintf(x_stdout, "BH\n");
529                                 return;
530                         }
531
532                         response.type = SPNEGO_NEG_TOKEN_TARG;
533                         response.negTokenTarg.supportedMech = strdup(OID_KERBEROS5_OLD);
534                         response.negTokenTarg.mechListMIC = data_blob(NULL, 0);
535                         response.negTokenTarg.responseToken = data_blob(NULL, 0);
536
537                         status = ads_verify_ticket(lp_realm(),
538                                                    &request.negTokenInit.mechToken,
539                                                    &principal, &auth_data, &ap_rep,
540                                                    session_key);
541
542                         /* Now in "principal" we have the name we are
543                            authenticated as. */
544
545                         if (NT_STATUS_IS_OK(status)) {
546
547                                 domain = strchr(principal, '@');
548
549                                 if (domain == NULL) {
550                                         DEBUG(1, ("Did not get a valid principal "
551                                                   "from ads_verify_ticket\n"));
552                                         x_fprintf(x_stdout, "BH\n");
553                                         return;
554                                 }
555
556                                 *domain++ = '\0';
557                                 domain = strdup(domain);
558                                 user = strdup(principal);
559
560                                 data_blob_free(&ap_rep);
561                                 data_blob_free(&auth_data);
562
563                                 SAFE_FREE(principal);
564                         }
565                 }
566 #endif
567
568         } else {
569
570                 if ( (request.negTokenTarg.supportedMech == NULL) ||
571                      ( strcmp(request.negTokenTarg.supportedMech, OID_NTLMSSP) != 0 ) ) {
572                         /* Kerberos should never send a negTokenTarg, OID_NTLMSSP
573                            is the only one we support that sends this stuff */
574                         DEBUG(1, ("Got a negTokenTarg for something non-NTLMSSP: %s\n",
575                                   request.negTokenTarg.supportedMech));
576                         x_fprintf(x_stdout, "BH\n");
577                         return;
578                 }
579
580                 if (request.negTokenTarg.responseToken.data == NULL) {
581                         DEBUG(1, ("Got a negTokenTarg without a responseToken!\n"));
582                         x_fprintf(x_stdout, "BH\n");
583                         return;
584                 }
585
586                 status = ntlmssp_server_update(ntlmssp_state,
587                                                request.negTokenTarg.responseToken,
588                                                &response.negTokenTarg.responseToken);
589
590                 response.type = SPNEGO_NEG_TOKEN_TARG;
591                 response.negTokenTarg.supportedMech = strdup(OID_NTLMSSP);
592                 response.negTokenTarg.mechListMIC = data_blob(NULL, 0);
593
594                 if (NT_STATUS_IS_OK(status)) {
595                         user = strdup(ntlmssp_state->user);
596                         domain = strdup(ntlmssp_state->domain);
597                         ntlmssp_server_end(&ntlmssp_state);
598                 }
599         }
600
601         free_spnego_data(&request);
602
603         if (NT_STATUS_IS_OK(status)) {
604                 response.negTokenTarg.negResult = SPNEGO_ACCEPT_COMPLETED;
605                 reply_code = "AF";
606                 pstr_sprintf(reply_argument, "%s\\%s", domain, user);
607         } else if (NT_STATUS_EQUAL(status,
608                                    NT_STATUS_MORE_PROCESSING_REQUIRED)) {
609                 response.negTokenTarg.negResult = SPNEGO_ACCEPT_INCOMPLETE;
610                 reply_code = "TT";
611                 pstr_sprintf(reply_argument, "*");
612         } else {
613                 response.negTokenTarg.negResult = SPNEGO_REJECT;
614                 reply_code = "NA";
615                 pstrcpy(reply_argument, nt_errstr(status));
616         }
617
618         SAFE_FREE(user);
619         SAFE_FREE(domain);
620
621         len = write_spnego_data(&token, &response);
622         free_spnego_data(&response);
623
624         if (len == -1) {
625                 DEBUG(1, ("Could not write SPNEGO data blob\n"));
626                 x_fprintf(x_stdout, "BH\n");
627                 return;
628         }
629
630         reply_base64 = base64_encode_data_blob(token);
631
632         x_fprintf(x_stdout, "%s %s %s\n",
633                   reply_code, reply_base64, reply_argument);
634
635         SAFE_FREE(reply_base64);
636         data_blob_free(&token);
637
638         return;
639 }
640
641 static NTLMSSP_CLIENT_STATE *client_ntlmssp_state = NULL;
642
643 static BOOL manage_client_ntlmssp_init(SPNEGO_DATA spnego)
644 {
645         NTSTATUS status;
646         DATA_BLOB null_blob = data_blob(NULL, 0);
647         DATA_BLOB to_server;
648         char *to_server_base64;
649         const char *my_mechs[] = {OID_NTLMSSP, NULL};
650
651         DEBUG(10, ("Got spnego negTokenInit with NTLMSSP\n"));
652
653         if (client_ntlmssp_state != NULL) {
654                 DEBUG(1, ("Request for initial SPNEGO request where "
655                           "we already have a state\n"));
656                 return False;
657         }
658
659         if ( (opt_username == NULL) || (opt_domain == NULL) ) {
660                 DEBUG(1, ("Need username and domain for NTLMSSP\n"));
661                 return False;
662         }
663
664         if (opt_password == NULL) {
665
666                 /* Request a password from the calling process.  After
667                    sending it, the calling process should retry with
668                    the negTokenInit. */
669
670                 DEBUG(10, ("Requesting password\n"));
671                 x_fprintf(x_stdout, "PW\n");
672                 return True;
673         }
674
675         status = ntlmssp_client_start(&client_ntlmssp_state);
676
677         if (!NT_STATUS_IS_OK(status)) {
678                 DEBUG(1, ("Could not start NTLMSSP client: %s\n",
679                           nt_errstr(status)));
680                 ntlmssp_client_end(&client_ntlmssp_state);
681                 return False;
682         }
683
684         status = ntlmssp_set_username(client_ntlmssp_state, opt_username);
685
686         if (!NT_STATUS_IS_OK(status)) {
687                 DEBUG(1, ("Could not set username: %s\n",
688                           nt_errstr(status)));
689                 ntlmssp_client_end(&client_ntlmssp_state);
690                 return False;
691         }
692
693         status = ntlmssp_set_domain(client_ntlmssp_state, opt_domain);
694
695         if (!NT_STATUS_IS_OK(status)) {
696                 DEBUG(1, ("Could not set domain: %s\n",
697                           nt_errstr(status)));
698                 ntlmssp_client_end(&client_ntlmssp_state);
699                 return False;
700         }
701
702         status = ntlmssp_set_password(client_ntlmssp_state, opt_password);
703         
704         if (!NT_STATUS_IS_OK(status)) {
705                 DEBUG(1, ("Could not set password: %s\n",
706                           nt_errstr(status)));
707                 ntlmssp_client_end(&client_ntlmssp_state);
708                 return False;
709         }
710
711         spnego.type = SPNEGO_NEG_TOKEN_INIT;
712         spnego.negTokenInit.mechTypes = my_mechs;
713         spnego.negTokenInit.reqFlags = 0;
714         spnego.negTokenInit.mechListMIC = null_blob;
715
716         status = ntlmssp_client_update(client_ntlmssp_state, null_blob,
717                                        &spnego.negTokenInit.mechToken);
718
719         if (!NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
720                 DEBUG(1, ("Expected MORE_PROCESSING_REQUIRED, got: %s\n",
721                           nt_errstr(status)));
722                 ntlmssp_client_end(&client_ntlmssp_state);
723                 return False;
724         }
725
726         write_spnego_data(&to_server, &spnego);
727         data_blob_free(&spnego.negTokenInit.mechToken);
728
729         to_server_base64 = base64_encode_data_blob(to_server);
730         data_blob_free(&to_server);
731         x_fprintf(x_stdout, "KK %s\n", to_server_base64);
732         SAFE_FREE(to_server_base64);
733         return True;
734 }
735
736 static void manage_client_ntlmssp_targ(SPNEGO_DATA spnego)
737 {
738         NTSTATUS status;
739         DATA_BLOB null_blob = data_blob(NULL, 0);
740         DATA_BLOB request;
741         DATA_BLOB to_server;
742         char *to_server_base64;
743
744         DEBUG(10, ("Got spnego negTokenTarg with NTLMSSP\n"));
745
746         if (client_ntlmssp_state == NULL) {
747                 DEBUG(1, ("Got NTLMSSP tArg without a client state\n"));
748                 x_fprintf(x_stdout, "BH\n");
749                 ntlmssp_client_end(&client_ntlmssp_state);
750                 return;
751         }
752
753         if (spnego.negTokenTarg.negResult == SPNEGO_REJECT) {
754                 x_fprintf(x_stdout, "NA\n");
755                 ntlmssp_client_end(&client_ntlmssp_state);
756                 return;
757         }
758
759         if (spnego.negTokenTarg.negResult == SPNEGO_ACCEPT_COMPLETED) {
760                 x_fprintf(x_stdout, "AF\n");
761                 ntlmssp_client_end(&client_ntlmssp_state);
762                 return;
763         }
764
765         status = ntlmssp_client_update(client_ntlmssp_state,
766                                        spnego.negTokenTarg.responseToken,
767                                        &request);
768                 
769         if (!NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
770                 DEBUG(1, ("Expected MORE_PROCESSING_REQUIRED from "
771                           "ntlmssp_client_update, got: %s\n",
772                           nt_errstr(status)));
773                 x_fprintf(x_stdout, "BH\n");
774                 data_blob_free(&request);
775                 ntlmssp_client_end(&client_ntlmssp_state);
776                 return;
777         }
778
779         spnego.type = SPNEGO_NEG_TOKEN_TARG;
780         spnego.negTokenTarg.negResult = SPNEGO_ACCEPT_INCOMPLETE;
781         spnego.negTokenTarg.supportedMech = OID_NTLMSSP;
782         spnego.negTokenTarg.responseToken = request;
783         spnego.negTokenTarg.mechListMIC = null_blob;
784         
785         write_spnego_data(&to_server, &spnego);
786         data_blob_free(&request);
787
788         to_server_base64 = base64_encode_data_blob(to_server);
789         data_blob_free(&to_server);
790         x_fprintf(x_stdout, "KK %s\n", to_server_base64);
791         SAFE_FREE(to_server_base64);
792         return;
793 }
794
795 #ifdef HAVE_KRB5
796
797 static BOOL manage_client_krb5_init(SPNEGO_DATA spnego)
798 {
799         char *principal;
800         DATA_BLOB tkt, to_server;
801         unsigned char session_key_krb5[16];
802         SPNEGO_DATA reply;
803         char *reply_base64;
804         
805         const char *my_mechs[] = {OID_KERBEROS5_OLD, NULL};
806         ssize_t len;
807
808         if ( (spnego.negTokenInit.mechListMIC.data == NULL) ||
809              (spnego.negTokenInit.mechListMIC.length == 0) ) {
810                 DEBUG(1, ("Did not get a principal for krb5\n"));
811                 return False;
812         }
813
814         principal = malloc(spnego.negTokenInit.mechListMIC.length+1);
815
816         if (principal == NULL) {
817                 DEBUG(1, ("Could not malloc principal\n"));
818                 return False;
819         }
820
821         memcpy(principal, spnego.negTokenInit.mechListMIC.data,
822                spnego.negTokenInit.mechListMIC.length);
823         principal[spnego.negTokenInit.mechListMIC.length] = '\0';
824
825         tkt = cli_krb5_get_ticket(principal, 0, session_key_krb5);
826
827         if (tkt.data == NULL) {
828
829                 pstring user;
830
831                 /* Let's try to first get the TGT, for that we need a
832                    password. */
833
834                 if (opt_password == NULL) {
835                         DEBUG(10, ("Requesting password\n"));
836                         x_fprintf(x_stdout, "PW\n");
837                         return True;
838                 }
839
840                 pstr_sprintf(user, "%s@%s", opt_username, opt_domain);
841
842                 if (kerberos_kinit_password(user, opt_password, 0) != 0) {
843                         DEBUG(10, ("Requesting TGT failed\n"));
844                         x_fprintf(x_stdout, "NA\n");
845                         return True;
846                 }
847
848                 tkt = cli_krb5_get_ticket(principal, 0, session_key_krb5);
849         }
850
851         ZERO_STRUCT(reply);
852
853         reply.type = SPNEGO_NEG_TOKEN_INIT;
854         reply.negTokenInit.mechTypes = my_mechs;
855         reply.negTokenInit.reqFlags = 0;
856         reply.negTokenInit.mechToken = tkt;
857         reply.negTokenInit.mechListMIC = data_blob(NULL, 0);
858
859         len = write_spnego_data(&to_server, &reply);
860         data_blob_free(&tkt);
861
862         if (len == -1) {
863                 DEBUG(1, ("Could not write SPNEGO data blob\n"));
864                 return False;
865         }
866
867         reply_base64 = base64_encode_data_blob(to_server);
868         x_fprintf(x_stdout, "KK %s *\n", reply_base64);
869
870         SAFE_FREE(reply_base64);
871         data_blob_free(&to_server);
872         DEBUG(10, ("sent GSS-SPNEGO KERBEROS5 negTokenInit\n"));
873         return True;
874 }
875
876 static void manage_client_krb5_targ(SPNEGO_DATA spnego)
877 {
878         switch (spnego.negTokenTarg.negResult) {
879         case SPNEGO_ACCEPT_INCOMPLETE:
880                 DEBUG(1, ("Got a Kerberos negTokenTarg with ACCEPT_INCOMPLETE\n"));
881                 x_fprintf(x_stdout, "BH\n");
882                 break;
883         case SPNEGO_ACCEPT_COMPLETED:
884                 DEBUG(10, ("Accept completed\n"));
885                 x_fprintf(x_stdout, "AF\n");
886                 break;
887         case SPNEGO_REJECT:
888                 DEBUG(10, ("Rejected\n"));
889                 x_fprintf(x_stdout, "NA\n");
890                 break;
891         default:
892                 DEBUG(1, ("Got an invalid negTokenTarg\n"));
893                 x_fprintf(x_stdout, "AF\n");
894         }
895 }
896
897 #endif
898
899 static void manage_gss_spnego_client_request(enum squid_mode squid_mode,
900                                              char *buf, int length) 
901 {
902         DATA_BLOB request;
903         SPNEGO_DATA spnego;
904         ssize_t len;
905
906         if (strlen(buf) <= 3) {
907                 DEBUG(1, ("SPNEGO query [%s] too short\n", buf));
908                 x_fprintf(x_stdout, "BH\n");
909                 return;
910         }
911
912         request = base64_decode_data_blob(buf+3);
913
914         if (strncmp(buf, "PW ", 3) == 0) {
915
916                 /* We asked for a password and obviously got it :-) */
917
918                 opt_password = strndup((const char *)request.data, request.length);
919
920                 if (opt_password == NULL) {
921                         DEBUG(1, ("Out of memory\n"));
922                         x_fprintf(x_stdout, "BH\n");
923                         data_blob_free(&request);
924                         return;
925                 }
926
927                 x_fprintf(x_stdout, "OK\n");
928                 data_blob_free(&request);
929                 return;
930         }
931
932         if ( (strncmp(buf, "TT ", 3) != 0) &&
933              (strncmp(buf, "AF ", 3) != 0) &&
934              (strncmp(buf, "NA ", 3) != 0) ) {
935                 DEBUG(1, ("SPNEGO request [%s] invalid\n", buf));
936                 x_fprintf(x_stdout, "BH\n");
937                 data_blob_free(&request);
938                 return;
939         }
940
941         /* So we got a server challenge to generate a SPNEGO
942            client-to-server request... */
943
944         len = read_spnego_data(request, &spnego);
945         data_blob_free(&request);
946
947         if (len == -1) {
948                 DEBUG(1, ("Could not read SPNEGO data for [%s]\n", buf));
949                 x_fprintf(x_stdout, "BH\n");
950                 return;
951         }
952
953         if (spnego.type == SPNEGO_NEG_TOKEN_INIT) {
954
955                 /* The server offers a list of mechanisms */
956
957                 const char **mechType = spnego.negTokenInit.mechTypes;
958
959                 while (*mechType != NULL) {
960
961 #ifdef HAVE_KRB5
962                         if ( (strcmp(*mechType, OID_KERBEROS5_OLD) == 0) ||
963                              (strcmp(*mechType, OID_KERBEROS5) == 0) ) {
964                                 if (manage_client_krb5_init(spnego))
965                                         goto out;
966                         }
967 #endif
968
969                         if (strcmp(*mechType, OID_NTLMSSP) == 0) {
970                                 if (manage_client_ntlmssp_init(spnego))
971                                         goto out;
972                         }
973
974                         mechType++;
975                 }
976
977                 DEBUG(1, ("Server offered no compatible mechanism\n"));
978                 x_fprintf(x_stdout, "BH\n");
979                 return;
980         }
981
982         if (spnego.type == SPNEGO_NEG_TOKEN_TARG) {
983
984                 if (spnego.negTokenTarg.supportedMech == NULL) {
985                         /* On accept/reject Windows does not send the
986                            mechanism anymore. Handle that here and
987                            shut down the mechanisms. */
988
989                         switch (spnego.negTokenTarg.negResult) {
990                         case SPNEGO_ACCEPT_COMPLETED:
991                                 x_fprintf(x_stdout, "AF\n");
992                                 break;
993                         case SPNEGO_REJECT:
994                                 x_fprintf(x_stdout, "NA\n");
995                                 break;
996                         default:
997                                 DEBUG(1, ("Got a negTokenTarg with no mech and an "
998                                           "unknown negResult: %d\n",
999                                           spnego.negTokenTarg.negResult));
1000                                 x_fprintf(x_stdout, "BH\n");
1001                         }
1002
1003                         ntlmssp_client_end(&client_ntlmssp_state);
1004                         goto out;
1005                 }
1006
1007                 if (strcmp(spnego.negTokenTarg.supportedMech,
1008                            OID_NTLMSSP) == 0) {
1009                         manage_client_ntlmssp_targ(spnego);
1010                         goto out;
1011                 }
1012
1013 #if HAVE_KRB5
1014                 if (strcmp(spnego.negTokenTarg.supportedMech,
1015                            OID_KERBEROS5_OLD) == 0) {
1016                         manage_client_krb5_targ(spnego);
1017                         goto out;
1018                 }
1019 #endif
1020
1021         }
1022
1023         DEBUG(1, ("Got an SPNEGO token I could not handle [%s]!\n", buf));
1024         x_fprintf(x_stdout, "BH\n");
1025         return;
1026
1027  out:
1028         free_spnego_data(&spnego);
1029         return;
1030 }
1031
1032 static void manage_squid_request(enum squid_mode squid_mode) 
1033 {
1034         char buf[SQUID_BUFFER_SIZE+1];
1035         int length;
1036         char *c;
1037         static BOOL err;
1038
1039         /* this is not a typo - x_fgets doesn't work too well under squid */
1040         if (fgets(buf, sizeof(buf)-1, stdin) == NULL) {
1041                 DEBUG(1, ("fgets() failed! dying..... errno=%d (%s)\n", ferror(stdin),
1042                           strerror(ferror(stdin))));
1043                 exit(1);    /* BIIG buffer */
1044         }
1045     
1046         c=memchr(buf,'\n',sizeof(buf)-1);
1047         if (c) {
1048                 *c = '\0';
1049                 length = c-buf;
1050         } else {
1051                 err = 1;
1052                 return;
1053         }
1054         if (err) {
1055                 DEBUG(2, ("Oversized message\n"));
1056                 x_fprintf(x_stderr, "ERR\n");
1057                 err = 0;
1058                 return;
1059         }
1060
1061         DEBUG(10, ("Got '%s' from squid (length: %d).\n",buf,length));
1062
1063         if (buf[0] == '\0') {
1064                 DEBUG(2, ("Invalid Request\n"));
1065                 x_fprintf(x_stderr, "ERR\n");
1066                 return;
1067         }
1068         
1069         if (squid_mode == SQUID_2_5_BASIC || squid_mode == SQUID_2_4_BASIC) {
1070                 manage_squid_basic_request(squid_mode, buf, length);
1071         } else if (squid_mode == SQUID_2_5_NTLMSSP) {
1072                 manage_squid_ntlmssp_request(squid_mode, buf, length);
1073         } else if (squid_mode == GSS_SPNEGO) {
1074                 manage_gss_spnego_request(squid_mode, buf, length);
1075         } else if (squid_mode == GSS_SPNEGO_CLIENT) {
1076                 manage_gss_spnego_client_request(squid_mode, buf, length);
1077         }
1078 }
1079
1080
1081 static void squid_stream(enum squid_mode squid_mode) {
1082         /* initialize FDescs */
1083         x_setbuf(x_stdout, NULL);
1084         x_setbuf(x_stderr, NULL);
1085         while(1) {
1086                 manage_squid_request(squid_mode);
1087         }
1088 }
1089
1090
1091 /* Authenticate a user with a challenge/response */
1092
1093 static BOOL check_auth_crap(void)
1094 {
1095         NTSTATUS nt_status;
1096         uint32 flags = 0;
1097         char lm_key[8];
1098         char nt_key[16];
1099         char *hex_lm_key;
1100         char *hex_nt_key;
1101         char *error_string;
1102         static uint8 zeros[16];
1103
1104         x_setbuf(x_stdout, NULL);
1105
1106         if (request_lm_key) 
1107                 flags |= WBFLAG_PAM_LMKEY;
1108
1109         if (request_nt_key) 
1110                 flags |= WBFLAG_PAM_NTKEY;
1111
1112         nt_status = contact_winbind_auth_crap(opt_username, opt_domain, 
1113                                               opt_workstation,
1114                                               &opt_challenge, 
1115                                               &opt_lm_response, 
1116                                               &opt_nt_response, 
1117                                               flags,
1118                                               (unsigned char *)lm_key, 
1119                                               (unsigned char *)nt_key, 
1120                                               &error_string);
1121
1122         if (!NT_STATUS_IS_OK(nt_status)) {
1123                 x_fprintf(x_stdout, "%s (0x%x)\n", 
1124                           error_string,
1125                           NT_STATUS_V(nt_status));
1126                 SAFE_FREE(error_string);
1127                 return False;
1128         }
1129
1130         if (request_lm_key 
1131             && (memcmp(zeros, lm_key, 
1132                        sizeof(lm_key)) != 0)) {
1133                 hex_encode((const unsigned char *)lm_key,
1134                            sizeof(lm_key),
1135                            &hex_lm_key);
1136                 x_fprintf(x_stdout, "LM_KEY: %s\n", hex_lm_key);
1137                 SAFE_FREE(hex_lm_key);
1138         }
1139         if (request_nt_key 
1140             && (memcmp(zeros, nt_key, 
1141                        sizeof(nt_key)) != 0)) {
1142                 hex_encode((const unsigned char *)nt_key, 
1143                            sizeof(nt_key), 
1144                            &hex_nt_key);
1145                 x_fprintf(x_stdout, "NT_KEY: %s\n", hex_nt_key);
1146                 SAFE_FREE(hex_nt_key);
1147         }
1148
1149         return True;
1150 }
1151
1152 /* 
1153    Authenticate a user with a challenge/response, checking session key
1154    and valid authentication types
1155 */
1156
1157 static DATA_BLOB get_challenge(void) 
1158 {
1159         static DATA_BLOB chal;
1160         if (opt_challenge.length)
1161                 return opt_challenge;
1162         
1163         chal = data_blob(NULL, 8);
1164
1165         generate_random_buffer(chal.data, chal.length, False);
1166         return chal;
1167 }
1168
1169 /* 
1170  * Test LM authentication, no NT response supplied
1171  */
1172
1173 static BOOL test_lm(void) 
1174 {
1175         NTSTATUS nt_status;
1176         uint32 flags = 0;
1177         DATA_BLOB lm_response = data_blob(NULL, 24);
1178
1179         uchar lm_key[8];
1180         uchar nt_key[16];
1181         uchar lm_hash[16];
1182         DATA_BLOB chall = get_challenge();
1183         char *error_string;
1184         
1185         ZERO_STRUCT(lm_key);
1186         ZERO_STRUCT(nt_key);
1187
1188         flags |= WBFLAG_PAM_LMKEY;
1189         flags |= WBFLAG_PAM_NTKEY;
1190
1191         SMBencrypt(opt_password, chall.data, lm_response.data);
1192         E_deshash(opt_password, lm_hash); 
1193
1194         nt_status = contact_winbind_auth_crap(opt_username, opt_domain, opt_workstation,
1195                                               &chall,
1196                                               &lm_response,
1197                                               NULL,
1198                                               flags,
1199                                               lm_key, 
1200                                               nt_key,
1201                                               &error_string);
1202         
1203         data_blob_free(&lm_response);
1204
1205         if (!NT_STATUS_IS_OK(nt_status)) {
1206                 d_printf("%s (0x%x)\n", 
1207                          error_string,
1208                          NT_STATUS_V(nt_status));
1209                 return False;
1210         }
1211
1212         if (memcmp(lm_hash, lm_key, 
1213                    sizeof(lm_key)) != 0) {
1214                 DEBUG(1, ("LM Key does not match expectations!\n"));
1215                 DEBUG(1, ("lm_key:\n"));
1216                 dump_data(1, (const char *)lm_key, 8);
1217                 DEBUG(1, ("expected:\n"));
1218                 dump_data(1, (const char *)lm_hash, 8);
1219         }
1220         if (memcmp(lm_hash, nt_key, 8) != 0) {
1221                 DEBUG(1, ("Session Key (first 8, lm hash) does not match expectations!\n"));
1222                 DEBUG(1, ("nt_key:\n"));
1223                 dump_data(1, (const char *)nt_key, 8);
1224                 DEBUG(1, ("expected:\n"));
1225                 dump_data(1, (const char *)lm_hash, 8);
1226         }
1227         return True;
1228 }
1229
1230 /* 
1231  * Test the normal 'LM and NTLM' combination
1232  */
1233
1234 static BOOL test_lm_ntlm(void) 
1235 {
1236         BOOL pass = True;
1237         NTSTATUS nt_status;
1238         uint32 flags = 0;
1239         DATA_BLOB lm_response = data_blob(NULL, 24);
1240         DATA_BLOB nt_response = data_blob(NULL, 24);
1241         DATA_BLOB session_key = data_blob(NULL, 16);
1242
1243         uchar lm_key[8];
1244         uchar nt_key[16];
1245         uchar lm_hash[16];
1246         uchar nt_hash[16];
1247         DATA_BLOB chall = get_challenge();
1248         char *error_string;
1249         
1250         ZERO_STRUCT(lm_key);
1251         ZERO_STRUCT(nt_key);
1252
1253         flags |= WBFLAG_PAM_LMKEY;
1254         flags |= WBFLAG_PAM_NTKEY;
1255
1256         SMBencrypt(opt_password,chall.data,lm_response.data);
1257         E_deshash(opt_password, lm_hash); 
1258
1259         SMBNTencrypt(opt_password,chall.data,nt_response.data);
1260
1261         E_md4hash(opt_password, nt_hash);
1262         SMBsesskeygen_ntv1(nt_hash, NULL, session_key.data);
1263
1264         nt_status = contact_winbind_auth_crap(opt_username, opt_domain, 
1265                                               opt_workstation,
1266                                               &chall,
1267                                               &lm_response,
1268                                               &nt_response,
1269                                               flags,
1270                                               lm_key, 
1271                                               nt_key,
1272                                               &error_string);
1273         
1274         data_blob_free(&lm_response);
1275
1276         if (!NT_STATUS_IS_OK(nt_status)) {
1277                 d_printf("%s (0x%x)\n", 
1278                          error_string,
1279                          NT_STATUS_V(nt_status));
1280                 SAFE_FREE(error_string);
1281                 return False;
1282         }
1283
1284         if (memcmp(lm_hash, lm_key, 
1285                    sizeof(lm_key)) != 0) {
1286                 DEBUG(1, ("LM Key does not match expectations!\n"));
1287                 DEBUG(1, ("lm_key:\n"));
1288                 dump_data(1, (const char *)lm_key, 8);
1289                 DEBUG(1, ("expected:\n"));
1290                 dump_data(1, (const char *)lm_hash, 8);
1291                 pass = False;
1292         }
1293         if (memcmp(session_key.data, nt_key, 
1294                    sizeof(nt_key)) != 0) {
1295                 DEBUG(1, ("NT Session Key does not match expectations!\n"));
1296                 DEBUG(1, ("nt_key:\n"));
1297                 dump_data(1, (const char *)nt_key, 16);
1298                 DEBUG(1, ("expected:\n"));
1299                 dump_data(1, (const char *)session_key.data, session_key.length);
1300                 pass = False;
1301         }
1302         return pass;
1303 }
1304
1305 /* 
1306  * Test the NTLM response only, no LM.
1307  */
1308
1309 static BOOL test_ntlm(void) 
1310 {
1311         BOOL pass = True;
1312         NTSTATUS nt_status;
1313         uint32 flags = 0;
1314         DATA_BLOB nt_response = data_blob(NULL, 24);
1315         DATA_BLOB session_key = data_blob(NULL, 16);
1316
1317         char lm_key[8];
1318         char nt_key[16];
1319         char lm_hash[16];
1320         char nt_hash[16];
1321         DATA_BLOB chall = get_challenge();
1322         char *error_string;
1323         
1324         ZERO_STRUCT(lm_key);
1325         ZERO_STRUCT(nt_key);
1326
1327         flags |= WBFLAG_PAM_LMKEY;
1328         flags |= WBFLAG_PAM_NTKEY;
1329
1330         SMBNTencrypt(opt_password,chall.data,nt_response.data);
1331         E_md4hash(opt_password, (unsigned char *)nt_hash);
1332         SMBsesskeygen_ntv1((const unsigned char *)nt_hash, NULL, session_key.data);
1333
1334         E_deshash(opt_password, (unsigned char *)lm_hash); 
1335
1336         nt_status = contact_winbind_auth_crap(opt_username, opt_domain, 
1337                                               opt_workstation,
1338                                               &chall,
1339                                               NULL,
1340                                               &nt_response,
1341                                               flags,
1342                                               (unsigned char *)lm_key,
1343                                               (unsigned char *)nt_key,
1344                                               &error_string);
1345         
1346         data_blob_free(&nt_response);
1347
1348         if (!NT_STATUS_IS_OK(nt_status)) {
1349                 d_printf("%s (0x%x)\n", 
1350                          error_string,
1351                          NT_STATUS_V(nt_status));
1352                 SAFE_FREE(error_string);
1353                 return False;
1354         }
1355
1356         if (memcmp(lm_hash, lm_key, 
1357                    sizeof(lm_key)) != 0) {
1358                 DEBUG(1, ("LM Key does not match expectations!\n"));
1359                 DEBUG(1, ("lm_key:\n"));
1360                 dump_data(1, lm_key, 8);
1361                 DEBUG(1, ("expected:\n"));
1362                 dump_data(1, lm_hash, 8);
1363                 pass = False;
1364         }
1365         if (memcmp(session_key.data, nt_key, 
1366                    sizeof(nt_key)) != 0) {
1367                 DEBUG(1, ("NT Session Key does not match expectations!\n"));
1368                 DEBUG(1, ("nt_key:\n"));
1369                 dump_data(1, nt_key, 16);
1370                 DEBUG(1, ("expected:\n"));
1371                 dump_data(1, (const char *)session_key.data, session_key.length);
1372                 pass = False;
1373         }
1374         return pass;
1375 }
1376
1377 /* 
1378  * Test the NTLM response only, but in the LM field.
1379  */
1380
1381 static BOOL test_ntlm_in_lm(void) 
1382 {
1383         BOOL pass = True;
1384         NTSTATUS nt_status;
1385         uint32 flags = 0;
1386         DATA_BLOB nt_response = data_blob(NULL, 24);
1387
1388         uchar lm_key[8];
1389         uchar lm_hash[16];
1390         uchar nt_key[16];
1391         DATA_BLOB chall = get_challenge();
1392         char *error_string;
1393         
1394         ZERO_STRUCT(nt_key);
1395
1396         flags |= WBFLAG_PAM_LMKEY;
1397         flags |= WBFLAG_PAM_NTKEY;
1398
1399         SMBNTencrypt(opt_password,chall.data,nt_response.data);
1400
1401         E_deshash(opt_password, lm_hash); 
1402
1403         nt_status = contact_winbind_auth_crap(opt_username, opt_domain, 
1404                                               opt_workstation,
1405                                               &chall,
1406                                               &nt_response,
1407                                               NULL,
1408                                               flags,
1409                                               lm_key,
1410                                               nt_key,
1411                                               &error_string);
1412         
1413         data_blob_free(&nt_response);
1414
1415         if (!NT_STATUS_IS_OK(nt_status)) {
1416                 d_printf("%s (0x%x)\n", 
1417                          error_string,
1418                          NT_STATUS_V(nt_status));
1419                 SAFE_FREE(error_string);
1420                 return False;
1421         }
1422
1423         if (memcmp(lm_hash, lm_key, 
1424                    sizeof(lm_key)) != 0) {
1425                 DEBUG(1, ("LM Key does not match expectations!\n"));
1426                 DEBUG(1, ("lm_key:\n"));
1427                 dump_data(1, (const char *)lm_key, 8);
1428                 DEBUG(1, ("expected:\n"));
1429                 dump_data(1, (const char *)lm_hash, 8);
1430                 pass = False;
1431         }
1432         if (memcmp(lm_hash, nt_key, 8) != 0) {
1433                 DEBUG(1, ("Session Key (first 8 lm hash) does not match expectations!\n"));
1434                 DEBUG(1, ("nt_key:\n"));
1435                 dump_data(1, (const char *)nt_key, 16);
1436                 DEBUG(1, ("expected:\n"));
1437                 dump_data(1, (const char *)lm_hash, 8);
1438                 pass = False;
1439         }
1440         return pass;
1441 }
1442
1443 /* 
1444  * Test the NTLM response only, but in the both the NT and LM fields.
1445  */
1446
1447 static BOOL test_ntlm_in_both(void) 
1448 {
1449         BOOL pass = True;
1450         NTSTATUS nt_status;
1451         uint32 flags = 0;
1452         DATA_BLOB nt_response = data_blob(NULL, 24);
1453         DATA_BLOB session_key = data_blob(NULL, 16);
1454
1455         char lm_key[8];
1456         char lm_hash[16];
1457         char nt_key[16];
1458         char nt_hash[16];
1459         DATA_BLOB chall = get_challenge();
1460         char *error_string;
1461         
1462         ZERO_STRUCT(lm_key);
1463         ZERO_STRUCT(nt_key);
1464
1465         flags |= WBFLAG_PAM_LMKEY;
1466         flags |= WBFLAG_PAM_NTKEY;
1467
1468         SMBNTencrypt(opt_password,chall.data,nt_response.data);
1469         E_md4hash(opt_password, (unsigned char *)nt_hash);
1470         SMBsesskeygen_ntv1((const unsigned char *)nt_hash, NULL, session_key.data);
1471
1472         E_deshash(opt_password, (unsigned char *)lm_hash); 
1473
1474         nt_status = contact_winbind_auth_crap(opt_username, opt_domain, 
1475                                               opt_workstation,
1476                                               &chall,
1477                                               &nt_response,
1478                                               &nt_response,
1479                                               flags,
1480                                               (unsigned char *)lm_key,
1481                                               (unsigned char *)nt_key,
1482                                               &error_string);
1483         
1484         data_blob_free(&nt_response);
1485
1486         if (!NT_STATUS_IS_OK(nt_status)) {
1487                 d_printf("%s (0x%x)\n", 
1488                          error_string,
1489                          NT_STATUS_V(nt_status));
1490                 SAFE_FREE(error_string);
1491                 return False;
1492         }
1493
1494         if (memcmp(lm_hash, lm_key, 
1495                    sizeof(lm_key)) != 0) {
1496                 DEBUG(1, ("LM Key does not match expectations!\n"));
1497                 DEBUG(1, ("lm_key:\n"));
1498                 dump_data(1, lm_key, 8);
1499                 DEBUG(1, ("expected:\n"));
1500                 dump_data(1, lm_hash, 8);
1501                 pass = False;
1502         }
1503         if (memcmp(session_key.data, nt_key, 
1504                    sizeof(nt_key)) != 0) {
1505                 DEBUG(1, ("NT Session Key does not match expectations!\n"));
1506                 DEBUG(1, ("nt_key:\n"));
1507                 dump_data(1, nt_key, 16);
1508                 DEBUG(1, ("expected:\n"));
1509                 dump_data(1, (const char *)session_key.data, session_key.length);
1510                 pass = False;
1511         }
1512
1513
1514         return pass;
1515 }
1516
1517 /* 
1518  * Test the NTLMv2 response only
1519  */
1520
1521 static BOOL test_ntlmv2(void) 
1522 {
1523         BOOL pass = True;
1524         NTSTATUS nt_status;
1525         uint32 flags = 0;
1526         DATA_BLOB ntlmv2_response = data_blob(NULL, 0);
1527         DATA_BLOB nt_session_key = data_blob(NULL, 0);
1528         DATA_BLOB names_blob = NTLMv2_generate_names_blob(get_winbind_netbios_name(), get_winbind_domain());
1529
1530         uchar nt_key[16];
1531         DATA_BLOB chall = get_challenge();
1532         char *error_string;
1533
1534         ZERO_STRUCT(nt_key);
1535         
1536         flags |= WBFLAG_PAM_NTKEY;
1537
1538         if (!SMBNTLMv2encrypt(opt_username, opt_domain, opt_password, &chall,
1539                               &names_blob,
1540                               NULL, &ntlmv2_response, 
1541                               &nt_session_key)) {
1542                 data_blob_free(&names_blob);
1543                 return False;
1544         }
1545         data_blob_free(&names_blob);
1546
1547         nt_status = contact_winbind_auth_crap(opt_username, opt_domain, 
1548                                               opt_workstation,
1549                                               &chall,
1550                                               NULL, 
1551                                               &ntlmv2_response,
1552                                               flags,
1553                                               NULL, 
1554                                               nt_key,
1555                                               &error_string);
1556         
1557         data_blob_free(&ntlmv2_response);
1558
1559         if (!NT_STATUS_IS_OK(nt_status)) {
1560                 d_printf("%s (0x%x)\n", 
1561                          error_string,
1562                          NT_STATUS_V(nt_status));
1563                 SAFE_FREE(error_string);
1564                 return False;
1565         }
1566
1567         if (memcmp(nt_session_key.data, nt_key, 
1568                    sizeof(nt_key)) != 0) {
1569                 DEBUG(1, ("NT Session Key does not match expectations!\n"));
1570                 DEBUG(1, ("nt_key:\n"));
1571                 dump_data(1, (const char *)nt_key, 16);
1572                 DEBUG(1, ("expected:\n"));
1573                 dump_data(1, (const char *)nt_session_key.data, nt_session_key.length);
1574                 pass = False;
1575         }
1576         return pass;
1577 }
1578
1579 /* 
1580  * Test the NTLMv2 and LMv2 responses
1581  */
1582
1583 static BOOL test_lmv2_ntlmv2(void) 
1584 {
1585         BOOL pass = True;
1586         NTSTATUS nt_status;
1587         uint32 flags = 0;
1588         DATA_BLOB ntlmv2_response = data_blob(NULL, 0);
1589         DATA_BLOB lmv2_response = data_blob(NULL, 0);
1590         DATA_BLOB nt_session_key = data_blob(NULL, 0);
1591         DATA_BLOB names_blob = NTLMv2_generate_names_blob(get_winbind_netbios_name(), get_winbind_domain());
1592
1593         uchar nt_key[16];
1594         DATA_BLOB chall = get_challenge();
1595         char *error_string;
1596
1597         ZERO_STRUCT(nt_key);
1598         
1599         flags |= WBFLAG_PAM_NTKEY;
1600
1601         if (!SMBNTLMv2encrypt(opt_username, opt_domain, opt_password, &chall,
1602                               &names_blob,
1603                               &lmv2_response, &ntlmv2_response, 
1604                               &nt_session_key)) {
1605                 data_blob_free(&names_blob);
1606                 return False;
1607         }
1608         data_blob_free(&names_blob);
1609
1610         nt_status = contact_winbind_auth_crap(opt_username, opt_domain, 
1611                                               opt_workstation,
1612                                               &chall,
1613                                               &lmv2_response,
1614                                               &ntlmv2_response,
1615                                               flags,
1616                                               NULL, 
1617                                               nt_key,
1618                                               &error_string);
1619         
1620         data_blob_free(&lmv2_response);
1621         data_blob_free(&ntlmv2_response);
1622
1623         if (!NT_STATUS_IS_OK(nt_status)) {
1624                 d_printf("%s (0x%x)\n", 
1625                          error_string,
1626                          NT_STATUS_V(nt_status));
1627                 SAFE_FREE(error_string);
1628                 return False;
1629         }
1630
1631         if (memcmp(nt_session_key.data, nt_key, 
1632                    sizeof(nt_key)) != 0) {
1633                 DEBUG(1, ("NT Session Key does not match expectations!\n"));
1634                 DEBUG(1, ("nt_key:\n"));
1635                 dump_data(1, (const char *)nt_key, 16);
1636                 DEBUG(1, ("expected:\n"));
1637                 dump_data(1, (const char *)nt_session_key.data, nt_session_key.length);
1638                 pass = False;
1639         }
1640         return pass;
1641 }
1642
1643 /* 
1644  * Test the LMv2 response only
1645  */
1646
1647 static BOOL test_lmv2(void) 
1648 {
1649         BOOL pass = True;
1650         NTSTATUS nt_status;
1651         uint32 flags = 0;
1652         DATA_BLOB lmv2_response = data_blob(NULL, 0);
1653
1654         DATA_BLOB chall = get_challenge();
1655         char *error_string;
1656
1657         if (!SMBNTLMv2encrypt(opt_username, opt_domain, opt_password, &chall,
1658                               NULL, 
1659                               &lmv2_response, NULL,
1660                               NULL)) {
1661                 return False;
1662         }
1663
1664         nt_status = contact_winbind_auth_crap(opt_username, opt_domain, 
1665                                               opt_workstation,
1666                                               &chall,
1667                                               &lmv2_response,
1668                                               NULL, 
1669                                               flags,
1670                                               NULL, 
1671                                               NULL,
1672                                               &error_string);
1673         
1674         data_blob_free(&lmv2_response);
1675
1676         if (!NT_STATUS_IS_OK(nt_status)) {
1677                 d_printf("%s (0x%x)\n", 
1678                          error_string,
1679                          NT_STATUS_V(nt_status));
1680                 SAFE_FREE(error_string);
1681                 return False;
1682         }
1683
1684         return pass;
1685 }
1686
1687 /* 
1688  * Test the normal 'LM and NTLM' combination but deliberately break one
1689  */
1690
1691 static BOOL test_ntlm_broken(BOOL break_lm) 
1692 {
1693         BOOL pass = True;
1694         NTSTATUS nt_status;
1695         uint32 flags = 0;
1696         DATA_BLOB lm_response = data_blob(NULL, 24);
1697         DATA_BLOB nt_response = data_blob(NULL, 24);
1698         DATA_BLOB session_key = data_blob(NULL, 16);
1699
1700         uchar lm_key[8];
1701         uchar nt_key[16];
1702         uchar lm_hash[16];
1703         uchar nt_hash[16];
1704         DATA_BLOB chall = get_challenge();
1705         char *error_string;
1706         
1707         ZERO_STRUCT(lm_key);
1708         ZERO_STRUCT(nt_key);
1709
1710         flags |= WBFLAG_PAM_LMKEY;
1711         flags |= WBFLAG_PAM_NTKEY;
1712
1713         SMBencrypt(opt_password,chall.data,lm_response.data);
1714         E_deshash(opt_password, lm_hash); 
1715
1716         SMBNTencrypt(opt_password,chall.data,nt_response.data);
1717
1718         E_md4hash(opt_password, nt_hash);
1719         SMBsesskeygen_ntv1(nt_hash, NULL, session_key.data);
1720
1721         if (break_lm)
1722                 lm_response.data[0]++;
1723         else
1724                 nt_response.data[0]++;
1725
1726         nt_status = contact_winbind_auth_crap(opt_username, opt_domain, 
1727                                               opt_workstation,
1728                                               &chall,
1729                                               &lm_response,
1730                                               &nt_response,
1731                                               flags,
1732                                               lm_key, 
1733                                               nt_key,
1734                                               &error_string);
1735         
1736         data_blob_free(&lm_response);
1737
1738         if (!NT_STATUS_IS_OK(nt_status)) {
1739                 d_printf("%s (0x%x)\n", 
1740                          error_string,
1741                          NT_STATUS_V(nt_status));
1742                 SAFE_FREE(error_string);
1743                 return False;
1744         }
1745
1746         if (memcmp(lm_hash, lm_key, 
1747                    sizeof(lm_key)) != 0) {
1748                 DEBUG(1, ("LM Key does not match expectations!\n"));
1749                 DEBUG(1, ("lm_key:\n"));
1750                 dump_data(1, (const char *)lm_key, 8);
1751                 DEBUG(1, ("expected:\n"));
1752                 dump_data(1, (const char *)lm_hash, 8);
1753                 pass = False;
1754         }
1755         if (memcmp(session_key.data, nt_key, 
1756                    sizeof(nt_key)) != 0) {
1757                 DEBUG(1, ("NT Session Key does not match expectations!\n"));
1758                 DEBUG(1, ("nt_key:\n"));
1759                 dump_data(1, (const char *)nt_key, 16);
1760                 DEBUG(1, ("expected:\n"));
1761                 dump_data(1, (const char *)session_key.data, session_key.length);
1762                 pass = False;
1763         }
1764         return pass;
1765 }
1766
1767 static BOOL test_ntlm_lm_broken(void) 
1768 {
1769         return test_ntlm_broken(True);
1770 }
1771
1772 static BOOL test_ntlm_ntlm_broken(void) 
1773 {
1774         return test_ntlm_broken(False);
1775 }
1776
1777 static BOOL test_ntlmv2_broken(BOOL break_lmv2)
1778 {
1779         BOOL pass = True;
1780         NTSTATUS nt_status;
1781         uint32 flags = 0;
1782         DATA_BLOB ntlmv2_response = data_blob(NULL, 0);
1783         DATA_BLOB lmv2_response = data_blob(NULL, 0);
1784         DATA_BLOB nt_session_key = data_blob(NULL, 0);
1785         DATA_BLOB names_blob = NTLMv2_generate_names_blob(get_winbind_netbios_name(), get_winbind_domain());
1786
1787         uchar nt_key[16];
1788         DATA_BLOB chall = get_challenge();
1789         char *error_string;
1790
1791         ZERO_STRUCT(nt_key);
1792         
1793         flags |= WBFLAG_PAM_NTKEY;
1794          
1795         if (!SMBNTLMv2encrypt(opt_username, opt_domain, opt_password, &chall,
1796                               &names_blob,
1797                               &lmv2_response, &ntlmv2_response, 
1798                               &nt_session_key)) {
1799                 data_blob_free(&names_blob);
1800                 return False;
1801         }
1802         data_blob_free(&names_blob);
1803
1804         /* Heh - this should break the appropriate password hash nicely! */
1805
1806         if (break_lmv2)
1807                 lmv2_response.data[0]++;
1808         else
1809                 ntlmv2_response.data[0]++;
1810
1811         nt_status = contact_winbind_auth_crap(opt_username, opt_domain, 
1812                                               opt_workstation,
1813                                               &chall,
1814                                               &lmv2_response,
1815                                               &ntlmv2_response,
1816                                               flags,
1817                                               NULL,
1818                                               nt_key,
1819                                               &error_string);
1820         
1821         data_blob_free(&lmv2_response);
1822         data_blob_free(&ntlmv2_response);
1823
1824         if (!NT_STATUS_IS_OK(nt_status)) {
1825                 d_printf("%s (0x%x)\n", 
1826                          error_string,
1827                          NT_STATUS_V(nt_status));
1828                 SAFE_FREE(error_string);
1829                 return False;
1830         }
1831
1832         return pass;
1833 }
1834
1835 static BOOL test_ntlmv2_lmv2_broken(void) 
1836 {
1837         return test_ntlmv2_broken(True);
1838 }
1839
1840 static BOOL test_ntlmv2_ntlmv2_broken(void) 
1841 {
1842         return test_ntlmv2_broken(False);
1843 }
1844
1845 /* 
1846    Tests:
1847    
1848    - LM only
1849    - NT and LM             
1850    - NT
1851    - NT in LM field
1852    - NT in both fields
1853    - NTLMv2
1854    - NTLMv2 and LMv2
1855    - LMv2
1856    
1857    check we get the correct session key in each case
1858    check what values we get for the LM session key
1859    
1860 */
1861
1862 struct ntlm_tests {
1863         BOOL (*fn)(void);
1864         const char *name;
1865 } test_table[] = {
1866         {test_lm, "LM"},
1867         {test_lm_ntlm, "LM and NTLM"},
1868         {test_ntlm, "NTLM"},
1869         {test_ntlm_in_lm, "NTLM in LM"},
1870         {test_ntlm_in_both, "NTLM in both"},
1871         {test_ntlmv2, "NTLMv2"},
1872         {test_lmv2_ntlmv2, "NTLMv2 and LMv2"},
1873         {test_lmv2, "LMv2"},
1874         {test_ntlmv2_lmv2_broken, "NTLMv2 and LMv2, LMv2 broken"},
1875         {test_ntlmv2_ntlmv2_broken, "NTLMv2 and LMv2, NTLMv2 broken"},
1876         {test_ntlm_lm_broken, "NTLM and LM, LM broken"},
1877         {test_ntlm_ntlm_broken, "NTLM and LM, NTLM broken"}
1878 };
1879
1880 static BOOL diagnose_ntlm_auth(void)
1881 {
1882         unsigned int i;
1883         BOOL pass = True;
1884
1885         for (i=0; test_table[i].fn; i++) {
1886                 if (!test_table[i].fn()) {
1887                         DEBUG(1, ("Test %s failed!\n", test_table[i].name));
1888                         pass = False;
1889                 }
1890         }
1891
1892         return pass;
1893 }
1894
1895 /* Main program */
1896
1897 enum {
1898         OPT_USERNAME = 1000,
1899         OPT_DOMAIN,
1900         OPT_WORKSTATION,
1901         OPT_CHALLENGE,
1902         OPT_RESPONSE,
1903         OPT_LM,
1904         OPT_NT,
1905         OPT_PASSWORD,
1906         OPT_LM_KEY,
1907         OPT_NT_KEY,
1908         OPT_DIAGNOSTICS
1909 };
1910
1911  int main(int argc, const char **argv)
1912 {
1913         int opt;
1914         static const char *helper_protocol;
1915         static int diagnostics;
1916
1917         static const char *hex_challenge;
1918         static const char *hex_lm_response;
1919         static const char *hex_nt_response;
1920         char *challenge;
1921         char *lm_response;
1922         char *nt_response;
1923         size_t challenge_len;
1924         size_t lm_response_len;
1925         size_t nt_response_len;
1926
1927         poptContext pc;
1928
1929         /* NOTE: DO NOT change this interface without considering the implications!
1930            This is an external interface, which other programs will use to interact 
1931            with this helper.
1932         */
1933
1934         /* We do not use single-letter command abbreviations, because they harm future 
1935            interface stability. */
1936
1937         struct poptOption long_options[] = {
1938                 POPT_AUTOHELP
1939                 { "helper-protocol", 0, POPT_ARG_STRING, &helper_protocol, OPT_DOMAIN, "operate as a stdio-based helper", "helper protocol to use"},
1940                 { "username", 0, POPT_ARG_STRING, &opt_username, OPT_USERNAME, "username"},
1941                 { "domain", 0, POPT_ARG_STRING, &opt_domain, OPT_DOMAIN, "domain name"},
1942                 { "workstation", 0, POPT_ARG_STRING, &opt_workstation, OPT_WORKSTATION, "workstation"},
1943                 { "challenge", 0, POPT_ARG_STRING, &hex_challenge, OPT_CHALLENGE, "challenge (HEX encoded)"},
1944                 { "lm-response", 0, POPT_ARG_STRING, &hex_lm_response, OPT_LM, "LM Response to the challenge (HEX encoded)"},
1945                 { "nt-response", 0, POPT_ARG_STRING, &hex_nt_response, OPT_NT, "NT or NTLMv2 Response to the challenge (HEX encoded)"},
1946                 { "password", 0, POPT_ARG_STRING, &opt_password, OPT_PASSWORD, "User's plaintext password"},            
1947                 { "request-lm-key", 0, POPT_ARG_NONE, &request_lm_key, OPT_LM_KEY, "Retreive LM session key"},
1948                 { "request-nt-key", 0, POPT_ARG_NONE, &request_nt_key, OPT_NT_KEY, "Retreive NT session key"},
1949                 { "diagnostics", 0, POPT_ARG_NONE, &diagnostics, OPT_DIAGNOSTICS, "Perform diagnostics on the authentictaion chain"},
1950                 POPT_COMMON_SAMBA
1951                 POPT_TABLEEND
1952         };
1953
1954         /* Samba client initialisation */
1955
1956         dbf = x_stderr;
1957         
1958         /* Samba client initialisation */
1959
1960         if (!lp_load(dyn_CONFIGFILE, True, False, False)) {
1961                 d_fprintf(stderr, "wbinfo: error opening config file %s. Error was %s\n",
1962                         dyn_CONFIGFILE, strerror(errno));
1963                 exit(1);
1964         }
1965
1966         /* Parse options */
1967
1968         pc = poptGetContext("ntlm_auth", argc, argv, long_options, 0);
1969
1970         /* Parse command line options */
1971
1972         if (argc == 1) {
1973                 poptPrintHelp(pc, stderr, 0);
1974                 return 1;
1975         }
1976
1977         pc = poptGetContext(NULL, argc, (const char **)argv, long_options, 
1978                             POPT_CONTEXT_KEEP_FIRST);
1979
1980         while((opt = poptGetNextOpt(pc)) != -1) {
1981                 switch (opt) {
1982                 case OPT_CHALLENGE:
1983                         challenge = smb_xmalloc((strlen(hex_challenge))/2+1);
1984                         if ((challenge_len = strhex_to_str(challenge, 
1985                                                            strlen(hex_challenge), 
1986                                                            hex_challenge)) != 8) {
1987                                 x_fprintf(x_stderr, "hex decode of %s failed (only got %u bytes)!\n", 
1988                                         hex_challenge, challenge_len);
1989                                 exit(1);
1990                         }
1991                         opt_challenge = data_blob(challenge, challenge_len);
1992                         SAFE_FREE(challenge);
1993                         break;
1994                 case OPT_LM: 
1995                         lm_response = smb_xmalloc((strlen(hex_lm_response))/2+1);
1996                         lm_response_len = strhex_to_str(lm_response,    
1997                                                         strlen(hex_lm_response), 
1998                                                         hex_lm_response);
1999                         if (lm_response_len != 24) {
2000                                 x_fprintf(x_stderr, "hex decode of %s failed!\n", hex_lm_response);
2001                                 exit(1);
2002                         }
2003                         opt_lm_response = data_blob(lm_response, lm_response_len);
2004                         SAFE_FREE(lm_response);
2005                         break;
2006                 case OPT_NT: 
2007                         nt_response = smb_xmalloc((strlen(hex_nt_response)+2)/2+1);
2008                         nt_response_len = strhex_to_str(nt_response, 
2009                                                         strlen(hex_nt_response), 
2010                                                         hex_nt_response);
2011                         if (nt_response_len < 24) {
2012                                 x_fprintf(x_stderr, "hex decode of %s failed!\n", hex_nt_response);
2013                                 exit(1);
2014                         }
2015                         opt_nt_response = data_blob(nt_response, nt_response_len);
2016                         SAFE_FREE(nt_response);
2017                         break;
2018                 }
2019         }
2020
2021         if (helper_protocol) {
2022                 if (strcmp(helper_protocol, "squid-2.5-ntlmssp")== 0) {
2023                         squid_stream(SQUID_2_5_NTLMSSP);
2024                 } else if (strcmp(helper_protocol, "squid-2.5-basic")== 0) {
2025                         squid_stream(SQUID_2_5_BASIC);
2026                 } else if (strcmp(helper_protocol, "squid-2.4-basic")== 0) {
2027                         squid_stream(SQUID_2_4_BASIC);
2028                 } else if (strcmp(helper_protocol, "gss-spnego")== 0) {
2029                         squid_stream(GSS_SPNEGO);
2030                 } else if (strcmp(helper_protocol, "gss-spnego-client") == 0) {
2031                         squid_stream(GSS_SPNEGO_CLIENT);
2032                 } else {
2033                         x_fprintf(x_stderr, "unknown helper protocol [%s]\n", helper_protocol);
2034                         exit(1);
2035                 }
2036         }
2037
2038         if (!opt_username) {
2039                 x_fprintf(x_stderr, "username must be specified!\n\n");
2040                 poptPrintHelp(pc, stderr, 0);
2041                 exit(1);
2042         }
2043
2044         if (opt_domain == NULL) {
2045                 opt_domain = get_winbind_domain();
2046         }
2047
2048         if (opt_workstation == NULL) {
2049                 opt_workstation = "";
2050         }
2051
2052         if (opt_challenge.length) {
2053                 if (!check_auth_crap()) {
2054                         exit(1);
2055                 }
2056                 exit(0);
2057         } 
2058
2059         if (!opt_password) {
2060                 opt_password = getpass("password: ");
2061         }
2062
2063         if (diagnostics) {
2064                 if (!diagnose_ntlm_auth()) {
2065                         exit(1);
2066                 }
2067         } else {
2068                 fstring user;
2069
2070                 fstr_sprintf(user, "%s%c%s", opt_domain, winbind_separator(), opt_username);
2071                 if (!check_plaintext_auth(user, opt_password, True)) {
2072                         exit(1);
2073                 }
2074         }
2075
2076         /* Exit code */
2077
2078         poptFreeContext(pc);
2079         return 0;
2080 }