import HEAD into svn+ssh://svn.samba.org/home/svn/samba/trunk
[metze/old/v3-2-winbind-ndr.git] / source / utils / ntlm_auth.c
1 /* 
2    Unix SMB/CIFS implementation.
3
4    Winbind status program.
5
6    Copyright (C) Tim Potter      2000-2002
7    Copyright (C) Andrew Bartlett <abartlet@samba.org> 2003
8    Copyright (C) Francesco Chemolli <kinkie@kame.usr.dsi.unimi.it> 2000 
9
10    This program is free software; you can redistribute it and/or modify
11    it under the terms of the GNU General Public License as published by
12    the Free Software Foundation; either version 2 of the License, or
13    (at your option) any later version.
14    
15    This program is distributed in the hope that it will be useful,
16    but WITHOUT ANY WARRANTY; without even the implied warranty of
17    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18    GNU General Public License for more details.
19    
20    You should have received a copy of the GNU General Public License
21    along with this program; if not, write to the Free Software
22    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
23 */
24
25 #include "includes.h"
26
27 #undef DBGC_CLASS
28 #define DBGC_CLASS DBGC_WINBIND
29
30 #define SQUID_BUFFER_SIZE 2010
31
32 enum 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, 
1115                                                       0, NULL))) {
1116                         DEBUG(10, ("Requesting TGT failed: %s\n", error_message(retval)));
1117                         x_fprintf(x_stdout, "NA\n");
1118                         return True;
1119                 }
1120
1121                 retval = cli_krb5_get_ticket(principal, 0, &tkt, &session_key_krb5);
1122
1123                 if (retval) {
1124                         DEBUG(10, ("Kinit suceeded, but getting a ticket failed: %s\n", error_message(retval)));
1125                 }
1126         }
1127
1128         data_blob_free(&session_key_krb5);
1129
1130         ZERO_STRUCT(reply);
1131
1132         reply.type = SPNEGO_NEG_TOKEN_INIT;
1133         reply.negTokenInit.mechTypes = my_mechs;
1134         reply.negTokenInit.reqFlags = 0;
1135         reply.negTokenInit.mechToken = tkt;
1136         reply.negTokenInit.mechListMIC = data_blob(NULL, 0);
1137
1138         len = write_spnego_data(&to_server, &reply);
1139         data_blob_free(&tkt);
1140
1141         if (len == -1) {
1142                 DEBUG(1, ("Could not write SPNEGO data blob\n"));
1143                 return False;
1144         }
1145
1146         reply_base64 = base64_encode_data_blob(to_server);
1147         x_fprintf(x_stdout, "KK %s *\n", reply_base64);
1148
1149         SAFE_FREE(reply_base64);
1150         data_blob_free(&to_server);
1151         DEBUG(10, ("sent GSS-SPNEGO KERBEROS5 negTokenInit\n"));
1152         return True;
1153 }
1154
1155 static void manage_client_krb5_targ(SPNEGO_DATA spnego)
1156 {
1157         switch (spnego.negTokenTarg.negResult) {
1158         case SPNEGO_ACCEPT_INCOMPLETE:
1159                 DEBUG(1, ("Got a Kerberos negTokenTarg with ACCEPT_INCOMPLETE\n"));
1160                 x_fprintf(x_stdout, "BH\n");
1161                 break;
1162         case SPNEGO_ACCEPT_COMPLETED:
1163                 DEBUG(10, ("Accept completed\n"));
1164                 x_fprintf(x_stdout, "AF\n");
1165                 break;
1166         case SPNEGO_REJECT:
1167                 DEBUG(10, ("Rejected\n"));
1168                 x_fprintf(x_stdout, "NA\n");
1169                 break;
1170         default:
1171                 DEBUG(1, ("Got an invalid negTokenTarg\n"));
1172                 x_fprintf(x_stdout, "AF\n");
1173         }
1174 }
1175
1176 #endif
1177
1178 static void manage_gss_spnego_client_request(enum stdio_helper_mode stdio_helper_mode, 
1179                                              char *buf, int length) 
1180 {
1181         DATA_BLOB request;
1182         SPNEGO_DATA spnego;
1183         ssize_t len;
1184
1185         if (strlen(buf) <= 3) {
1186                 DEBUG(1, ("SPNEGO query [%s] too short\n", buf));
1187                 x_fprintf(x_stdout, "BH\n");
1188                 return;
1189         }
1190
1191         request = base64_decode_data_blob(buf+3);
1192
1193         if (strncmp(buf, "PW ", 3) == 0) {
1194
1195                 /* We asked for a password and obviously got it :-) */
1196
1197                 opt_password = strndup((const char *)request.data, request.length);
1198
1199                 if (opt_password == NULL) {
1200                         DEBUG(1, ("Out of memory\n"));
1201                         x_fprintf(x_stdout, "BH\n");
1202                         data_blob_free(&request);
1203                         return;
1204                 }
1205
1206                 x_fprintf(x_stdout, "OK\n");
1207                 data_blob_free(&request);
1208                 return;
1209         }
1210
1211         if ( (strncmp(buf, "TT ", 3) != 0) &&
1212              (strncmp(buf, "AF ", 3) != 0) &&
1213              (strncmp(buf, "NA ", 3) != 0) ) {
1214                 DEBUG(1, ("SPNEGO request [%s] invalid\n", buf));
1215                 x_fprintf(x_stdout, "BH\n");
1216                 data_blob_free(&request);
1217                 return;
1218         }
1219
1220         /* So we got a server challenge to generate a SPNEGO
1221            client-to-server request... */
1222
1223         len = read_spnego_data(request, &spnego);
1224         data_blob_free(&request);
1225
1226         if (len == -1) {
1227                 DEBUG(1, ("Could not read SPNEGO data for [%s]\n", buf));
1228                 x_fprintf(x_stdout, "BH\n");
1229                 return;
1230         }
1231
1232         if (spnego.type == SPNEGO_NEG_TOKEN_INIT) {
1233
1234                 /* The server offers a list of mechanisms */
1235
1236                 const char **mechType = spnego.negTokenInit.mechTypes;
1237
1238                 while (*mechType != NULL) {
1239
1240 #ifdef HAVE_KRB5
1241                         if ( (strcmp(*mechType, OID_KERBEROS5_OLD) == 0) ||
1242                              (strcmp(*mechType, OID_KERBEROS5) == 0) ) {
1243                                 if (manage_client_krb5_init(spnego))
1244                                         goto out;
1245                         }
1246 #endif
1247
1248                         if (strcmp(*mechType, OID_NTLMSSP) == 0) {
1249                                 if (manage_client_ntlmssp_init(spnego))
1250                                         goto out;
1251                         }
1252
1253                         mechType++;
1254                 }
1255
1256                 DEBUG(1, ("Server offered no compatible mechanism\n"));
1257                 x_fprintf(x_stdout, "BH\n");
1258                 return;
1259         }
1260
1261         if (spnego.type == SPNEGO_NEG_TOKEN_TARG) {
1262
1263                 if (spnego.negTokenTarg.supportedMech == NULL) {
1264                         /* On accept/reject Windows does not send the
1265                            mechanism anymore. Handle that here and
1266                            shut down the mechanisms. */
1267
1268                         switch (spnego.negTokenTarg.negResult) {
1269                         case SPNEGO_ACCEPT_COMPLETED:
1270                                 x_fprintf(x_stdout, "AF\n");
1271                                 break;
1272                         case SPNEGO_REJECT:
1273                                 x_fprintf(x_stdout, "NA\n");
1274                                 break;
1275                         default:
1276                                 DEBUG(1, ("Got a negTokenTarg with no mech and an "
1277                                           "unknown negResult: %d\n",
1278                                           spnego.negTokenTarg.negResult));
1279                                 x_fprintf(x_stdout, "BH\n");
1280                         }
1281
1282                         ntlmssp_end(&client_ntlmssp_state);
1283                         goto out;
1284                 }
1285
1286                 if (strcmp(spnego.negTokenTarg.supportedMech,
1287                            OID_NTLMSSP) == 0) {
1288                         manage_client_ntlmssp_targ(spnego);
1289                         goto out;
1290                 }
1291
1292 #if HAVE_KRB5
1293                 if (strcmp(spnego.negTokenTarg.supportedMech,
1294                            OID_KERBEROS5_OLD) == 0) {
1295                         manage_client_krb5_targ(spnego);
1296                         goto out;
1297                 }
1298 #endif
1299
1300         }
1301
1302         DEBUG(1, ("Got an SPNEGO token I could not handle [%s]!\n", buf));
1303         x_fprintf(x_stdout, "BH\n");
1304         return;
1305
1306  out:
1307         free_spnego_data(&spnego);
1308         return;
1309 }
1310
1311 static void manage_squid_request(enum stdio_helper_mode helper_mode, stdio_helper_function fn) 
1312 {
1313         char buf[SQUID_BUFFER_SIZE+1];
1314         int length;
1315         char *c;
1316         static BOOL err;
1317
1318         /* this is not a typo - x_fgets doesn't work too well under squid */
1319         if (fgets(buf, sizeof(buf)-1, stdin) == NULL) {
1320                 DEBUG(1, ("fgets() failed! dying..... errno=%d (%s)\n", ferror(stdin),
1321                           strerror(ferror(stdin))));
1322                 exit(1);    /* BIIG buffer */
1323         }
1324     
1325         c=memchr(buf,'\n',sizeof(buf)-1);
1326         if (c) {
1327                 *c = '\0';
1328                 length = c-buf;
1329         } else {
1330                 err = 1;
1331                 return;
1332         }
1333         if (err) {
1334                 DEBUG(2, ("Oversized message\n"));
1335                 x_fprintf(x_stderr, "ERR\n");
1336                 err = 0;
1337                 return;
1338         }
1339
1340         DEBUG(10, ("Got '%s' from squid (length: %d).\n",buf,length));
1341
1342         if (buf[0] == '\0') {
1343                 DEBUG(2, ("Invalid Request\n"));
1344                 x_fprintf(x_stderr, "ERR\n");
1345                 return;
1346         }
1347         
1348         fn(helper_mode, buf, length);
1349 }
1350
1351
1352 static void squid_stream(enum stdio_helper_mode stdio_mode, stdio_helper_function fn) {
1353         /* initialize FDescs */
1354         x_setbuf(x_stdout, NULL);
1355         x_setbuf(x_stderr, NULL);
1356         while(1) {
1357                 manage_squid_request(stdio_mode, fn);
1358         }
1359 }
1360
1361
1362 /* Authenticate a user with a challenge/response */
1363
1364 static BOOL check_auth_crap(void)
1365 {
1366         NTSTATUS nt_status;
1367         uint32 flags = 0;
1368         char lm_key[8];
1369         char nt_key[16];
1370         char *hex_lm_key;
1371         char *hex_nt_key;
1372         char *error_string;
1373         static uint8 zeros[16];
1374
1375         x_setbuf(x_stdout, NULL);
1376
1377         if (request_lm_key) 
1378                 flags |= WBFLAG_PAM_LMKEY;
1379
1380         if (request_nt_key) 
1381                 flags |= WBFLAG_PAM_NTKEY;
1382
1383         nt_status = contact_winbind_auth_crap(opt_username, opt_domain, 
1384                                               opt_workstation,
1385                                               &opt_challenge, 
1386                                               &opt_lm_response, 
1387                                               &opt_nt_response, 
1388                                               flags,
1389                                               (unsigned char *)lm_key, 
1390                                               (unsigned char *)nt_key, 
1391                                               &error_string, NULL);
1392
1393         if (!NT_STATUS_IS_OK(nt_status)) {
1394                 x_fprintf(x_stdout, "%s (0x%x)\n", 
1395                           error_string,
1396                           NT_STATUS_V(nt_status));
1397                 SAFE_FREE(error_string);
1398                 return False;
1399         }
1400
1401         if (request_lm_key 
1402             && (memcmp(zeros, lm_key, 
1403                        sizeof(lm_key)) != 0)) {
1404                 hex_encode((const unsigned char *)lm_key,
1405                            sizeof(lm_key),
1406                            &hex_lm_key);
1407                 x_fprintf(x_stdout, "LM_KEY: %s\n", hex_lm_key);
1408                 SAFE_FREE(hex_lm_key);
1409         }
1410         if (request_nt_key 
1411             && (memcmp(zeros, nt_key, 
1412                        sizeof(nt_key)) != 0)) {
1413                 hex_encode((const unsigned char *)nt_key, 
1414                            sizeof(nt_key), 
1415                            &hex_nt_key);
1416                 x_fprintf(x_stdout, "NT_KEY: %s\n", hex_nt_key);
1417                 SAFE_FREE(hex_nt_key);
1418         }
1419
1420         return True;
1421 }
1422
1423 /* 
1424    Authenticate a user with a challenge/response, checking session key
1425    and valid authentication types
1426 */
1427
1428 static DATA_BLOB get_challenge(void) 
1429 {
1430         static DATA_BLOB chal;
1431         if (opt_challenge.length)
1432                 return opt_challenge;
1433         
1434         chal = data_blob(NULL, 8);
1435
1436         generate_random_buffer(chal.data, chal.length, False);
1437         return chal;
1438 }
1439
1440 /* 
1441  * Test the normal 'LM and NTLM' combination
1442  */
1443
1444 static BOOL test_lm_ntlm_broken(enum ntlm_break break_which) 
1445 {
1446         BOOL pass = True;
1447         NTSTATUS nt_status;
1448         uint32 flags = 0;
1449         DATA_BLOB lm_response = data_blob(NULL, 24);
1450         DATA_BLOB nt_response = data_blob(NULL, 24);
1451         DATA_BLOB session_key = data_blob(NULL, 16);
1452
1453         uchar lm_key[8];
1454         uchar nt_key[16];
1455         uchar lm_hash[16];
1456         uchar nt_hash[16];
1457         DATA_BLOB chall = get_challenge();
1458         char *error_string;
1459         
1460         ZERO_STRUCT(lm_key);
1461         ZERO_STRUCT(nt_key);
1462
1463         flags |= WBFLAG_PAM_LMKEY;
1464         flags |= WBFLAG_PAM_NTKEY;
1465
1466         SMBencrypt(opt_password,chall.data,lm_response.data);
1467         E_deshash(opt_password, lm_hash); 
1468
1469         SMBNTencrypt(opt_password,chall.data,nt_response.data);
1470
1471         E_md4hash(opt_password, nt_hash);
1472         SMBsesskeygen_ntv1(nt_hash, NULL, session_key.data);
1473
1474         switch (break_which) {
1475         case BREAK_NONE:
1476                 break;
1477         case BREAK_LM:
1478                 lm_response.data[0]++;
1479                 break;
1480         case BREAK_NT:
1481                 nt_response.data[0]++;
1482                 break;
1483         case NO_LM:
1484                 data_blob_free(&lm_response);
1485                 break;
1486         case NO_NT:
1487                 data_blob_free(&nt_response);
1488                 break;
1489         }
1490
1491         nt_status = contact_winbind_auth_crap(opt_username, opt_domain, 
1492                                               opt_workstation,
1493                                               &chall,
1494                                               &lm_response,
1495                                               &nt_response,
1496                                               flags,
1497                                               lm_key, 
1498                                               nt_key,
1499                                               &error_string, NULL);
1500         
1501         data_blob_free(&lm_response);
1502
1503         if (!NT_STATUS_IS_OK(nt_status)) {
1504                 d_printf("%s (0x%x)\n", 
1505                          error_string,
1506                          NT_STATUS_V(nt_status));
1507                 SAFE_FREE(error_string);
1508                 return break_which == BREAK_NT;
1509         }
1510
1511         if (memcmp(lm_hash, lm_key, 
1512                    sizeof(lm_key)) != 0) {
1513                 DEBUG(1, ("LM Key does not match expectations!\n"));
1514                 DEBUG(1, ("lm_key:\n"));
1515                 dump_data(1, (const char *)lm_key, 8);
1516                 DEBUG(1, ("expected:\n"));
1517                 dump_data(1, (const char *)lm_hash, 8);
1518                 pass = False;
1519         }
1520
1521         if (break_which == NO_NT) {
1522                 if (memcmp(lm_hash, nt_key, 
1523                            8) != 0) {
1524                         DEBUG(1, ("NT Session Key does not match expectations (should be LM hash)!\n"));
1525                         DEBUG(1, ("nt_key:\n"));
1526                         dump_data(1, (const char *)nt_key, sizeof(nt_key));
1527                         DEBUG(1, ("expected:\n"));
1528                         dump_data(1, (const char *)lm_hash, sizeof(lm_hash));
1529                         pass = False;
1530                 }
1531         } else {                
1532                 if (memcmp(session_key.data, nt_key, 
1533                            sizeof(nt_key)) != 0) {
1534                         DEBUG(1, ("NT Session Key does not match expectations!\n"));
1535                         DEBUG(1, ("nt_key:\n"));
1536                         dump_data(1, (const char *)nt_key, 16);
1537                         DEBUG(1, ("expected:\n"));
1538                         dump_data(1, (const char *)session_key.data, session_key.length);
1539                         pass = False;
1540                 }
1541         }
1542         return pass;
1543 }
1544
1545 /* 
1546  * Test LM authentication, no NT response supplied
1547  */
1548
1549 static BOOL test_lm(void) 
1550 {
1551
1552         return test_lm_ntlm_broken(NO_NT);
1553 }
1554
1555 /* 
1556  * Test the NTLM response only, no LM.
1557  */
1558
1559 static BOOL test_ntlm(void) 
1560 {
1561         return test_lm_ntlm_broken(NO_LM);
1562 }
1563
1564 /* 
1565  * Test the NTLM response only, but in the LM field.
1566  */
1567
1568 static BOOL test_ntlm_in_lm(void) 
1569 {
1570         BOOL pass = True;
1571         NTSTATUS nt_status;
1572         uint32 flags = 0;
1573         DATA_BLOB nt_response = data_blob(NULL, 24);
1574
1575         uchar lm_key[8];
1576         uchar lm_hash[16];
1577         uchar nt_key[16];
1578         DATA_BLOB chall = get_challenge();
1579         char *error_string;
1580         
1581         ZERO_STRUCT(nt_key);
1582
1583         flags |= WBFLAG_PAM_LMKEY;
1584         flags |= WBFLAG_PAM_NTKEY;
1585
1586         SMBNTencrypt(opt_password,chall.data,nt_response.data);
1587
1588         E_deshash(opt_password, lm_hash); 
1589
1590         nt_status = contact_winbind_auth_crap(opt_username, opt_domain, 
1591                                               opt_workstation,
1592                                               &chall,
1593                                               &nt_response,
1594                                               NULL,
1595                                               flags,
1596                                               lm_key,
1597                                               nt_key,
1598                                               &error_string, NULL);
1599         
1600         data_blob_free(&nt_response);
1601
1602         if (!NT_STATUS_IS_OK(nt_status)) {
1603                 d_printf("%s (0x%x)\n", 
1604                          error_string,
1605                          NT_STATUS_V(nt_status));
1606                 SAFE_FREE(error_string);
1607                 return False;
1608         }
1609
1610         if (memcmp(lm_hash, lm_key, 
1611                    sizeof(lm_key)) != 0) {
1612                 DEBUG(1, ("LM Key does not match expectations!\n"));
1613                 DEBUG(1, ("lm_key:\n"));
1614                 dump_data(1, (const char *)lm_key, 8);
1615                 DEBUG(1, ("expected:\n"));
1616                 dump_data(1, (const char *)lm_hash, 8);
1617                 pass = False;
1618         }
1619         if (memcmp(lm_hash, nt_key, 8) != 0) {
1620                 DEBUG(1, ("Session Key (first 8 lm hash) does not match expectations!\n"));
1621                 DEBUG(1, ("nt_key:\n"));
1622                 dump_data(1, (const char *)nt_key, 16);
1623                 DEBUG(1, ("expected:\n"));
1624                 dump_data(1, (const char *)lm_hash, 8);
1625                 pass = False;
1626         }
1627         return pass;
1628 }
1629
1630 /* 
1631  * Test the NTLM response only, but in the both the NT and LM fields.
1632  */
1633
1634 static BOOL test_ntlm_in_both(void) 
1635 {
1636         BOOL pass = True;
1637         NTSTATUS nt_status;
1638         uint32 flags = 0;
1639         DATA_BLOB nt_response = data_blob(NULL, 24);
1640         DATA_BLOB session_key = data_blob(NULL, 16);
1641
1642         char lm_key[8];
1643         char lm_hash[16];
1644         char nt_key[16];
1645         char nt_hash[16];
1646         DATA_BLOB chall = get_challenge();
1647         char *error_string;
1648         
1649         ZERO_STRUCT(lm_key);
1650         ZERO_STRUCT(nt_key);
1651
1652         flags |= WBFLAG_PAM_LMKEY;
1653         flags |= WBFLAG_PAM_NTKEY;
1654
1655         SMBNTencrypt(opt_password,chall.data,nt_response.data);
1656         E_md4hash(opt_password, (unsigned char *)nt_hash);
1657         SMBsesskeygen_ntv1((const unsigned char *)nt_hash, NULL, session_key.data);
1658
1659         E_deshash(opt_password, (unsigned char *)lm_hash); 
1660
1661         nt_status = contact_winbind_auth_crap(opt_username, opt_domain, 
1662                                               opt_workstation,
1663                                               &chall,
1664                                               &nt_response,
1665                                               &nt_response,
1666                                               flags,
1667                                               (unsigned char *)lm_key,
1668                                               (unsigned char *)nt_key,
1669                                               &error_string, NULL);
1670         
1671         data_blob_free(&nt_response);
1672
1673         if (!NT_STATUS_IS_OK(nt_status)) {
1674                 d_printf("%s (0x%x)\n", 
1675                          error_string,
1676                          NT_STATUS_V(nt_status));
1677                 SAFE_FREE(error_string);
1678                 return False;
1679         }
1680
1681         if (memcmp(lm_hash, lm_key, 
1682                    sizeof(lm_key)) != 0) {
1683                 DEBUG(1, ("LM Key does not match expectations!\n"));
1684                 DEBUG(1, ("lm_key:\n"));
1685                 dump_data(1, lm_key, 8);
1686                 DEBUG(1, ("expected:\n"));
1687                 dump_data(1, lm_hash, 8);
1688                 pass = False;
1689         }
1690         if (memcmp(session_key.data, nt_key, 
1691                    sizeof(nt_key)) != 0) {
1692                 DEBUG(1, ("NT Session Key does not match expectations!\n"));
1693                 DEBUG(1, ("nt_key:\n"));
1694                 dump_data(1, nt_key, 16);
1695                 DEBUG(1, ("expected:\n"));
1696                 dump_data(1, (const char *)session_key.data, session_key.length);
1697                 pass = False;
1698         }
1699
1700
1701         return pass;
1702 }
1703
1704 /* 
1705  * Test the NTLMv2 and LMv2 responses
1706  */
1707
1708 static BOOL test_lmv2_ntlmv2_broken(enum ntlm_break break_which) 
1709 {
1710         BOOL pass = True;
1711         NTSTATUS nt_status;
1712         uint32 flags = 0;
1713         DATA_BLOB ntlmv2_response = data_blob(NULL, 0);
1714         DATA_BLOB lmv2_response = data_blob(NULL, 0);
1715         DATA_BLOB nt_session_key = data_blob(NULL, 0);
1716         DATA_BLOB names_blob = NTLMv2_generate_names_blob(get_winbind_netbios_name(), get_winbind_domain());
1717
1718         uchar nt_key[16];
1719         DATA_BLOB chall = get_challenge();
1720         char *error_string;
1721
1722         ZERO_STRUCT(nt_key);
1723         
1724         flags |= WBFLAG_PAM_NTKEY;
1725
1726         if (!SMBNTLMv2encrypt(opt_username, opt_domain, opt_password, &chall,
1727                               &names_blob,
1728                               &lmv2_response, &ntlmv2_response, 
1729                               &nt_session_key)) {
1730                 data_blob_free(&names_blob);
1731                 return False;
1732         }
1733         data_blob_free(&names_blob);
1734
1735         switch (break_which) {
1736         case BREAK_NONE:
1737                 break;
1738         case BREAK_LM:
1739                 lmv2_response.data[0]++;
1740                 break;
1741         case BREAK_NT:
1742                 ntlmv2_response.data[0]++;
1743                 break;
1744         case NO_LM:
1745                 data_blob_free(&lmv2_response);
1746                 break;
1747         case NO_NT:
1748                 data_blob_free(&ntlmv2_response);
1749                 break;
1750         }
1751
1752         nt_status = contact_winbind_auth_crap(opt_username, opt_domain, 
1753                                               opt_workstation,
1754                                               &chall,
1755                                               &lmv2_response,
1756                                               &ntlmv2_response,
1757                                               flags,
1758                                               NULL, 
1759                                               nt_key,
1760                                               &error_string, NULL);
1761         
1762         data_blob_free(&lmv2_response);
1763         data_blob_free(&ntlmv2_response);
1764
1765         if (!NT_STATUS_IS_OK(nt_status)) {
1766                 d_printf("%s (0x%x)\n", 
1767                          error_string,
1768                          NT_STATUS_V(nt_status));
1769                 SAFE_FREE(error_string);
1770                 return break_which == BREAK_NT;
1771         }
1772
1773         if (break_which != NO_NT && break_which != BREAK_NT && memcmp(nt_session_key.data, nt_key, 
1774                    sizeof(nt_key)) != 0) {
1775                 DEBUG(1, ("NT Session Key does not match expectations!\n"));
1776                 DEBUG(1, ("nt_key:\n"));
1777                 dump_data(1, (const char *)nt_key, 16);
1778                 DEBUG(1, ("expected:\n"));
1779                 dump_data(1, (const char *)nt_session_key.data, nt_session_key.length);
1780                 pass = False;
1781         }
1782         return pass;
1783 }
1784
1785 /* 
1786  * Test the NTLMv2 and LMv2 responses
1787  */
1788
1789 static BOOL test_lmv2_ntlmv2(void) 
1790 {
1791         return test_lmv2_ntlmv2_broken(BREAK_NONE);
1792 }
1793
1794 /* 
1795  * Test the LMv2 response only
1796  */
1797
1798 static BOOL test_lmv2(void) 
1799 {
1800         return test_lmv2_ntlmv2_broken(NO_NT);
1801 }
1802
1803 /* 
1804  * Test the NTLMv2 response only
1805  */
1806
1807 static BOOL test_ntlmv2(void) 
1808 {
1809         return test_lmv2_ntlmv2_broken(NO_LM);
1810 }
1811
1812 static BOOL test_lm_ntlm(void) 
1813 {
1814         return test_lm_ntlm_broken(BREAK_NONE);
1815 }
1816
1817 static BOOL test_ntlm_lm_broken(void) 
1818 {
1819         return test_lm_ntlm_broken(BREAK_LM);
1820 }
1821
1822 static BOOL test_ntlm_ntlm_broken(void) 
1823 {
1824         return test_lm_ntlm_broken(BREAK_NT);
1825 }
1826
1827 static BOOL test_ntlmv2_lmv2_broken(void) 
1828 {
1829         return test_lmv2_ntlmv2_broken(BREAK_LM);
1830 }
1831
1832 static BOOL test_ntlmv2_ntlmv2_broken(void) 
1833 {
1834         return test_lmv2_ntlmv2_broken(BREAK_NT);
1835 }
1836
1837 static BOOL test_plaintext(enum ntlm_break break_which)
1838 {
1839         NTSTATUS nt_status;
1840         uint32 flags = 0;
1841         DATA_BLOB nt_response = data_blob(NULL, 0);
1842         DATA_BLOB lm_response = data_blob(NULL, 0);
1843         char *password;
1844
1845         uchar nt_key[16];
1846         uchar lm_key[16];
1847         static const uchar zeros[8];
1848         DATA_BLOB chall = data_blob(zeros, sizeof(zeros));
1849         char *error_string;
1850
1851         ZERO_STRUCT(nt_key);
1852         
1853         flags |= WBFLAG_PAM_NTKEY;
1854         flags |= WBFLAG_PAM_LMKEY;
1855
1856         if ((push_ucs2_allocate((smb_ucs2_t **)&nt_response.data, opt_password)) == -1) {
1857                 DEBUG(0, ("push_ucs2_allocate failed!\n"));
1858                 exit(1);
1859         }
1860
1861         nt_response.length = strlen_w(((void *)nt_response.data))*sizeof(smb_ucs2_t);
1862
1863         password = strdup_upper(opt_password);
1864
1865         if ((convert_string_allocate(NULL, CH_UNIX, 
1866                                      CH_DOS, password,
1867                                      strlen(password)+1, 
1868                                      (void**)&lm_response.data,True)) == -1) {
1869                 DEBUG(0, ("push_ascii_allocate failed!\n"));
1870                 exit(1);
1871         }
1872
1873         SAFE_FREE(password);
1874
1875         lm_response.length = strlen(lm_response.data);
1876
1877         switch (break_which) {
1878         case BREAK_NONE:
1879                 break;
1880         case BREAK_LM:
1881                 lm_response.data[0]++;
1882                 break;
1883         case BREAK_NT:
1884                 nt_response.data[0]++;
1885                 break;
1886         case NO_LM:
1887                 SAFE_FREE(lm_response.data);
1888                 lm_response.length = 0;
1889                 break;
1890         case NO_NT:
1891                 SAFE_FREE(nt_response.data);
1892                 nt_response.length = 0;
1893                 break;
1894         }
1895
1896         nt_status = contact_winbind_auth_crap(opt_username, opt_domain, 
1897                                               opt_workstation,
1898                                               &chall,
1899                                               &lm_response,
1900                                               &nt_response,
1901                                               flags,
1902                                               lm_key,
1903                                               nt_key,
1904                                               &error_string, NULL);
1905         
1906         SAFE_FREE(nt_response.data);
1907         SAFE_FREE(lm_response.data);
1908         data_blob_free(&chall);
1909
1910         if (!NT_STATUS_IS_OK(nt_status)) {
1911                 d_printf("%s (0x%x)\n", 
1912                          error_string,
1913                          NT_STATUS_V(nt_status));
1914                 SAFE_FREE(error_string);
1915                 return break_which == BREAK_NT;
1916         }
1917
1918         return break_which != BREAK_NT;
1919 }
1920
1921 static BOOL test_plaintext_none_broken(void) {
1922         return test_plaintext(BREAK_NONE);
1923 }
1924
1925 static BOOL test_plaintext_lm_broken(void) {
1926         return test_plaintext(BREAK_LM);
1927 }
1928
1929 static BOOL test_plaintext_nt_broken(void) {
1930         return test_plaintext(BREAK_NT);
1931 }
1932
1933 static BOOL test_plaintext_nt_only(void) {
1934         return test_plaintext(NO_LM);
1935 }
1936
1937 static BOOL test_plaintext_lm_only(void) {
1938         return test_plaintext(NO_NT);
1939 }
1940
1941 /* 
1942    Tests:
1943    
1944    - LM only
1945    - NT and LM             
1946    - NT
1947    - NT in LM field
1948    - NT in both fields
1949    - NTLMv2
1950    - NTLMv2 and LMv2
1951    - LMv2
1952    - plaintext tests (in challenge-response feilds)
1953   
1954    check we get the correct session key in each case
1955    check what values we get for the LM session key
1956    
1957 */
1958
1959 static const struct ntlm_tests {
1960         BOOL (*fn)(void);
1961         const char *name;
1962 } test_table[] = {
1963         {test_lm, "LM"},
1964         {test_lm_ntlm, "LM and NTLM"},
1965         {test_ntlm, "NTLM"},
1966         {test_ntlm_in_lm, "NTLM in LM"},
1967         {test_ntlm_in_both, "NTLM in both"},
1968         {test_ntlmv2, "NTLMv2"},
1969         {test_lmv2_ntlmv2, "NTLMv2 and LMv2"},
1970         {test_lmv2, "LMv2"},
1971         {test_ntlmv2_lmv2_broken, "NTLMv2 and LMv2, LMv2 broken"},
1972         {test_ntlmv2_ntlmv2_broken, "NTLMv2 and LMv2, NTLMv2 broken"},
1973         {test_ntlm_lm_broken, "NTLM and LM, LM broken"},
1974         {test_ntlm_ntlm_broken, "NTLM and LM, NTLM broken"},
1975         {test_plaintext_none_broken, "Plaintext"},
1976         {test_plaintext_lm_broken, "Plaintext LM broken"},
1977         {test_plaintext_nt_broken, "Plaintext NT broken"},
1978         {test_plaintext_nt_only, "Plaintext NT only"},
1979         {test_plaintext_lm_only, "Plaintext LM only"}
1980 };
1981
1982 static BOOL diagnose_ntlm_auth(void)
1983 {
1984         unsigned int i;
1985         BOOL pass = True;
1986
1987         for (i=0; test_table[i].fn; i++) {
1988                 if (!test_table[i].fn()) {
1989                         DEBUG(1, ("Test %s failed!\n", test_table[i].name));
1990                         pass = False;
1991                 }
1992         }
1993
1994         return pass;
1995 }
1996
1997 /* Main program */
1998
1999 enum {
2000         OPT_USERNAME = 1000,
2001         OPT_DOMAIN,
2002         OPT_WORKSTATION,
2003         OPT_CHALLENGE,
2004         OPT_RESPONSE,
2005         OPT_LM,
2006         OPT_NT,
2007         OPT_PASSWORD,
2008         OPT_LM_KEY,
2009         OPT_NT_KEY,
2010         OPT_DIAGNOSTICS
2011 };
2012
2013  int main(int argc, const char **argv)
2014 {
2015         int opt;
2016         static const char *helper_protocol;
2017         static int diagnostics;
2018
2019         static const char *hex_challenge;
2020         static const char *hex_lm_response;
2021         static const char *hex_nt_response;
2022         char *challenge;
2023         char *lm_response;
2024         char *nt_response;
2025         size_t challenge_len;
2026         size_t lm_response_len;
2027         size_t nt_response_len;
2028
2029         poptContext pc;
2030
2031         /* NOTE: DO NOT change this interface without considering the implications!
2032            This is an external interface, which other programs will use to interact 
2033            with this helper.
2034         */
2035
2036         /* We do not use single-letter command abbreviations, because they harm future 
2037            interface stability. */
2038
2039         struct poptOption long_options[] = {
2040                 POPT_AUTOHELP
2041                 { "helper-protocol", 0, POPT_ARG_STRING, &helper_protocol, OPT_DOMAIN, "operate as a stdio-based helper", "helper protocol to use"},
2042                 { "username", 0, POPT_ARG_STRING, &opt_username, OPT_USERNAME, "username"},
2043                 { "domain", 0, POPT_ARG_STRING, &opt_domain, OPT_DOMAIN, "domain name"},
2044                 { "workstation", 0, POPT_ARG_STRING, &opt_workstation, OPT_WORKSTATION, "workstation"},
2045                 { "challenge", 0, POPT_ARG_STRING, &hex_challenge, OPT_CHALLENGE, "challenge (HEX encoded)"},
2046                 { "lm-response", 0, POPT_ARG_STRING, &hex_lm_response, OPT_LM, "LM Response to the challenge (HEX encoded)"},
2047                 { "nt-response", 0, POPT_ARG_STRING, &hex_nt_response, OPT_NT, "NT or NTLMv2 Response to the challenge (HEX encoded)"},
2048                 { "password", 0, POPT_ARG_STRING, &opt_password, OPT_PASSWORD, "User's plaintext password"},            
2049                 { "request-lm-key", 0, POPT_ARG_NONE, &request_lm_key, OPT_LM_KEY, "Retreive LM session key"},
2050                 { "request-nt-key", 0, POPT_ARG_NONE, &request_nt_key, OPT_NT_KEY, "Retreive NT session key"},
2051                 { "diagnostics", 0, POPT_ARG_NONE, &diagnostics, OPT_DIAGNOSTICS, "Perform diagnostics on the authentictaion chain"},
2052                 POPT_COMMON_SAMBA
2053                 POPT_TABLEEND
2054         };
2055
2056         /* Samba client initialisation */
2057
2058         dbf = x_stderr;
2059         
2060         /* Samba client initialisation */
2061
2062         if (!lp_load(dyn_CONFIGFILE, True, False, False)) {
2063                 d_fprintf(stderr, "wbinfo: error opening config file %s. Error was %s\n",
2064                         dyn_CONFIGFILE, strerror(errno));
2065                 exit(1);
2066         }
2067
2068         /* Parse options */
2069
2070         pc = poptGetContext("ntlm_auth", argc, argv, long_options, 0);
2071
2072         /* Parse command line options */
2073
2074         if (argc == 1) {
2075                 poptPrintHelp(pc, stderr, 0);
2076                 return 1;
2077         }
2078
2079         pc = poptGetContext(NULL, argc, (const char **)argv, long_options, 
2080                             POPT_CONTEXT_KEEP_FIRST);
2081
2082         while((opt = poptGetNextOpt(pc)) != -1) {
2083                 switch (opt) {
2084                 case OPT_CHALLENGE:
2085                         challenge = smb_xmalloc((strlen(hex_challenge))/2+1);
2086                         if ((challenge_len = strhex_to_str(challenge, 
2087                                                            strlen(hex_challenge), 
2088                                                            hex_challenge)) != 8) {
2089                                 x_fprintf(x_stderr, "hex decode of %s failed (only got %lu bytes)!\n", 
2090                                         hex_challenge, (unsigned long)challenge_len);
2091                                 exit(1);
2092                         }
2093                         opt_challenge = data_blob(challenge, challenge_len);
2094                         SAFE_FREE(challenge);
2095                         break;
2096                 case OPT_LM: 
2097                         lm_response = smb_xmalloc((strlen(hex_lm_response))/2+1);
2098                         lm_response_len = strhex_to_str(lm_response,    
2099                                                         strlen(hex_lm_response), 
2100                                                         hex_lm_response);
2101                         if (lm_response_len != 24) {
2102                                 x_fprintf(x_stderr, "hex decode of %s failed!\n", hex_lm_response);
2103                                 exit(1);
2104                         }
2105                         opt_lm_response = data_blob(lm_response, lm_response_len);
2106                         SAFE_FREE(lm_response);
2107                         break;
2108                 case OPT_NT: 
2109                         nt_response = smb_xmalloc((strlen(hex_nt_response)+2)/2+1);
2110                         nt_response_len = strhex_to_str(nt_response, 
2111                                                         strlen(hex_nt_response), 
2112                                                         hex_nt_response);
2113                         if (nt_response_len < 24) {
2114                                 x_fprintf(x_stderr, "hex decode of %s failed!\n", hex_nt_response);
2115                                 exit(1);
2116                         }
2117                         opt_nt_response = data_blob(nt_response, nt_response_len);
2118                         SAFE_FREE(nt_response);
2119                         break;
2120                 }
2121         }
2122
2123         if (helper_protocol) {
2124                 int i;
2125                 for (i=0; i<NUM_HELPER_MODES; i++) {
2126                         if (strcmp(helper_protocol, stdio_helper_protocols[i].name) == 0) {
2127                                 squid_stream(stdio_helper_protocols[i].mode, stdio_helper_protocols[i].fn);
2128                                 exit(0);
2129                         }
2130                 }
2131                 x_fprintf(x_stderr, "unknown helper protocol [%s]\n\nValid helper protools:\n\n", helper_protocol);
2132
2133                 for (i=0; i<NUM_HELPER_MODES; i++) {
2134                         x_fprintf(x_stderr, "%s\n", stdio_helper_protocols[i].name);
2135                 }
2136
2137                 exit(1);
2138         }
2139
2140         if (!opt_username) {
2141                 x_fprintf(x_stderr, "username must be specified!\n\n");
2142                 poptPrintHelp(pc, stderr, 0);
2143                 exit(1);
2144         }
2145
2146         if (opt_domain == NULL) {
2147                 opt_domain = get_winbind_domain();
2148         }
2149
2150         if (opt_workstation == NULL) {
2151                 opt_workstation = "";
2152         }
2153
2154         if (opt_challenge.length) {
2155                 if (!check_auth_crap()) {
2156                         exit(1);
2157                 }
2158                 exit(0);
2159         } 
2160
2161         if (!opt_password) {
2162                 opt_password = getpass("password: ");
2163         }
2164
2165         if (diagnostics) {
2166                 if (!diagnose_ntlm_auth()) {
2167                         exit(1);
2168                 }
2169         } else {
2170                 fstring user;
2171
2172                 fstr_sprintf(user, "%s%c%s", opt_domain, winbind_separator(), opt_username);
2173                 if (!check_plaintext_auth(user, opt_password, True)) {
2174                         exit(1);
2175                 }
2176         }
2177
2178         /* Exit code */
2179
2180         poptFreeContext(pc);
2181         return 0;
2182 }