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