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