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