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