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