s3-ntlm_auth: allow ntlm_auth --diagnostics to pass again
[ira/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    Copyright (C) Robert O'Callahan 2006 (added cached credential code).
10    Copyright (C) Kai Blin <kai@samba.org> 2008
11
12    This program is free software; you can redistribute it and/or modify
13    it under the terms of the GNU General Public License as published by
14    the Free Software Foundation; either version 3 of the License, or
15    (at your option) any later version.
16
17    This program is distributed in the hope that it will be useful,
18    but WITHOUT ANY WARRANTY; without even the implied warranty of
19    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20    GNU General Public License for more details.
21
22    You should have received a copy of the GNU General Public License
23    along with this program.  If not, see <http://www.gnu.org/licenses/>.
24 */
25
26 #include "includes.h"
27 #include "popt_common.h"
28 #include "utils/ntlm_auth.h"
29 #include "../libcli/auth/libcli_auth.h"
30 #include "../libcli/auth/spnego.h"
31 #include "../auth/ntlmssp/ntlmssp.h"
32 #include "smb_krb5.h"
33 #include <iniparser.h>
34 #include "../lib/crypto/arcfour.h"
35 #include "libads/kerberos_proto.h"
36 #include "nsswitch/winbind_client.h"
37 #include "librpc/gen_ndr/krb5pac.h"
38 #include "../lib/util/asn1.h"
39
40 #ifndef PAM_WINBIND_CONFIG_FILE
41 #define PAM_WINBIND_CONFIG_FILE "/etc/security/pam_winbind.conf"
42 #endif
43
44 #define WINBIND_KRB5_AUTH       0x00000080
45
46 #undef DBGC_CLASS
47 #define DBGC_CLASS DBGC_WINBIND
48
49 #define INITIAL_BUFFER_SIZE 300
50 #define MAX_BUFFER_SIZE 630000
51
52 enum stdio_helper_mode {
53         SQUID_2_4_BASIC,
54         SQUID_2_5_BASIC,
55         SQUID_2_5_NTLMSSP,
56         NTLMSSP_CLIENT_1,
57         GSS_SPNEGO,
58         GSS_SPNEGO_CLIENT,
59         NTLM_SERVER_1,
60         NTLM_CHANGE_PASSWORD_1,
61         NUM_HELPER_MODES
62 };
63
64 enum ntlm_auth_cli_state {
65         CLIENT_INITIAL = 0,
66         CLIENT_RESPONSE,
67         CLIENT_FINISHED,
68         CLIENT_ERROR
69 };
70
71 enum ntlm_auth_svr_state {
72         SERVER_INITIAL = 0,
73         SERVER_CHALLENGE,
74         SERVER_FINISHED,
75         SERVER_ERROR
76 };
77
78 struct ntlm_auth_state {
79         TALLOC_CTX *mem_ctx;
80         enum stdio_helper_mode helper_mode;
81         enum ntlm_auth_cli_state cli_state;
82         enum ntlm_auth_svr_state svr_state;
83         struct ntlmssp_state *ntlmssp_state;
84         uint32_t neg_flags;
85         char *want_feature_list;
86         char *spnego_mech;
87         char *spnego_mech_oid;
88         bool have_session_key;
89         DATA_BLOB session_key;
90         DATA_BLOB initial_message;
91 };
92
93 typedef void (*stdio_helper_function)(struct ntlm_auth_state *state, char *buf,
94                                         int length);
95
96 static void manage_squid_basic_request (struct ntlm_auth_state *state,
97                                         char *buf, int length);
98
99 static void manage_squid_ntlmssp_request (struct ntlm_auth_state *state,
100                                         char *buf, int length);
101
102 static void manage_client_ntlmssp_request (struct ntlm_auth_state *state,
103                                         char *buf, int length);
104
105 static void manage_gss_spnego_request (struct ntlm_auth_state *state,
106                                         char *buf, int length);
107
108 static void manage_gss_spnego_client_request (struct ntlm_auth_state *state,
109                                         char *buf, int length);
110
111 static void manage_ntlm_server_1_request (struct ntlm_auth_state *state,
112                                         char *buf, int length);
113
114 static void manage_ntlm_change_password_1_request(struct ntlm_auth_state *state,
115                                         char *buf, int length);
116
117 static const struct {
118         enum stdio_helper_mode mode;
119         const char *name;
120         stdio_helper_function fn;
121 } stdio_helper_protocols[] = {
122         { SQUID_2_4_BASIC, "squid-2.4-basic", manage_squid_basic_request},
123         { SQUID_2_5_BASIC, "squid-2.5-basic", manage_squid_basic_request},
124         { SQUID_2_5_NTLMSSP, "squid-2.5-ntlmssp", manage_squid_ntlmssp_request},
125         { NTLMSSP_CLIENT_1, "ntlmssp-client-1", manage_client_ntlmssp_request},
126         { GSS_SPNEGO, "gss-spnego", manage_gss_spnego_request},
127         { GSS_SPNEGO_CLIENT, "gss-spnego-client", manage_gss_spnego_client_request},
128         { NTLM_SERVER_1, "ntlm-server-1", manage_ntlm_server_1_request},
129         { NTLM_CHANGE_PASSWORD_1, "ntlm-change-password-1", manage_ntlm_change_password_1_request},
130         { NUM_HELPER_MODES, NULL, NULL}
131 };
132
133 const char *opt_username;
134 const char *opt_domain;
135 const char *opt_workstation;
136 const char *opt_password;
137 static DATA_BLOB opt_challenge;
138 static DATA_BLOB opt_lm_response;
139 static DATA_BLOB opt_nt_response;
140 static int request_lm_key;
141 static int request_user_session_key;
142 static int use_cached_creds;
143
144 static const char *require_membership_of;
145 static const char *require_membership_of_sid;
146 static const char *opt_pam_winbind_conf;
147
148 static char winbind_separator(void)
149 {
150         struct winbindd_response response;
151         static bool got_sep;
152         static char sep;
153
154         if (got_sep)
155                 return sep;
156
157         ZERO_STRUCT(response);
158
159         /* Send off request */
160
161         if (winbindd_request_response(WINBINDD_INFO, NULL, &response) !=
162             NSS_STATUS_SUCCESS) {
163                 d_printf("could not obtain winbind separator!\n");
164                 return *lp_winbind_separator();
165         }
166
167         sep = response.data.info.winbind_separator;
168         got_sep = True;
169
170         if (!sep) {
171                 d_printf("winbind separator was NULL!\n");
172                 return *lp_winbind_separator();
173         }
174
175         return sep;
176 }
177
178 const char *get_winbind_domain(void)
179 {
180         struct winbindd_response response;
181
182         static fstring winbind_domain;
183         if (*winbind_domain) {
184                 return winbind_domain;
185         }
186
187         ZERO_STRUCT(response);
188
189         /* Send off request */
190
191         if (winbindd_request_response(WINBINDD_DOMAIN_NAME, NULL, &response) !=
192             NSS_STATUS_SUCCESS) {
193                 DEBUG(0, ("could not obtain winbind domain name!\n"));
194                 return lp_workgroup();
195         }
196
197         fstrcpy(winbind_domain, response.data.domain_name);
198
199         return winbind_domain;
200
201 }
202
203 const char *get_winbind_netbios_name(void)
204 {
205         struct winbindd_response response;
206
207         static fstring winbind_netbios_name;
208
209         if (*winbind_netbios_name) {
210                 return winbind_netbios_name;
211         }
212
213         ZERO_STRUCT(response);
214
215         /* Send off request */
216
217         if (winbindd_request_response(WINBINDD_NETBIOS_NAME, NULL, &response) !=
218             NSS_STATUS_SUCCESS) {
219                 DEBUG(0, ("could not obtain winbind netbios name!\n"));
220                 return lp_netbios_name();
221         }
222
223         fstrcpy(winbind_netbios_name, response.data.netbios_name);
224
225         return winbind_netbios_name;
226
227 }
228
229 DATA_BLOB get_challenge(void) 
230 {
231         static DATA_BLOB chal;
232         if (opt_challenge.length)
233                 return opt_challenge;
234
235         chal = data_blob(NULL, 8);
236
237         generate_random_buffer(chal.data, chal.length);
238         return chal;
239 }
240
241 /* Copy of parse_domain_user from winbindd_util.c.  Parse a string of the
242    form DOMAIN/user into a domain and a user */
243
244 static bool parse_ntlm_auth_domain_user(const char *domuser, fstring domain, 
245                                      fstring user)
246 {
247
248         char *p = strchr(domuser,winbind_separator());
249
250         if (!p) {
251                 return False;
252         }
253
254         fstrcpy(user, p+1);
255         fstrcpy(domain, domuser);
256         domain[PTR_DIFF(p, domuser)] = 0;
257         strupper_m(domain);
258
259         return True;
260 }
261
262 static bool get_require_membership_sid(void) {
263         struct winbindd_request request;
264         struct winbindd_response response;
265
266         if (!require_membership_of) {
267                 return True;
268         }
269
270         if (require_membership_of_sid) {
271                 return True;
272         }
273
274         /* Otherwise, ask winbindd for the name->sid request */
275
276         ZERO_STRUCT(request);
277         ZERO_STRUCT(response);
278
279         if (!parse_ntlm_auth_domain_user(require_membership_of, 
280                                          request.data.name.dom_name, 
281                                          request.data.name.name)) {
282                 DEBUG(0, ("Could not parse %s into seperate domain/name parts!\n", 
283                           require_membership_of));
284                 return False;
285         }
286
287         if (winbindd_request_response(WINBINDD_LOOKUPNAME, &request, &response) !=
288             NSS_STATUS_SUCCESS) {
289                 DEBUG(0, ("Winbindd lookupname failed to resolve %s into a SID!\n", 
290                           require_membership_of));
291                 return False;
292         }
293
294         require_membership_of_sid = SMB_STRDUP(response.data.sid.sid);
295
296         if (require_membership_of_sid)
297                 return True;
298
299         return False;
300 }
301
302 /* 
303  * Get some configuration from pam_winbind.conf to see if we 
304  * need to contact trusted domain
305  */
306
307 int get_pam_winbind_config()
308 {
309         int ctrl = 0;
310         dictionary *d = NULL;
311
312         if (!opt_pam_winbind_conf || !*opt_pam_winbind_conf) {
313                 opt_pam_winbind_conf = PAM_WINBIND_CONFIG_FILE;
314         }
315
316         d = iniparser_load(discard_const_p(char, opt_pam_winbind_conf));
317
318         if (!d) {
319                 return 0;
320         }
321
322         if (iniparser_getboolean(d, discard_const_p(char, "global:krb5_auth"), false)) {
323                 ctrl |= WINBIND_KRB5_AUTH;
324         }
325
326         iniparser_freedict(d);
327
328         return ctrl;
329 }
330
331 /* Authenticate a user with a plaintext password */
332
333 static bool check_plaintext_auth(const char *user, const char *pass,
334                                  bool stdout_diagnostics)
335 {
336         struct winbindd_request request;
337         struct winbindd_response response;
338         NSS_STATUS result;
339
340         if (!get_require_membership_sid()) {
341                 return False;
342         }
343
344         /* Send off request */
345
346         ZERO_STRUCT(request);
347         ZERO_STRUCT(response);
348
349         fstrcpy(request.data.auth.user, user);
350         fstrcpy(request.data.auth.pass, pass);
351         if (require_membership_of_sid) {
352                 strlcpy(request.data.auth.require_membership_of_sid,
353                         require_membership_of_sid,
354                         sizeof(request.data.auth.require_membership_of_sid));
355         }
356
357         result = winbindd_request_response(WINBINDD_PAM_AUTH, &request, &response);
358
359         /* Display response */
360
361         if (stdout_diagnostics) {
362                 if ((result != NSS_STATUS_SUCCESS) && (response.data.auth.nt_status == 0)) {
363                         d_printf("Reading winbind reply failed! (0x01)\n");
364                 }
365
366                 d_printf("%s: %s (0x%x)\n",
367                          response.data.auth.nt_status_string,
368                          response.data.auth.error_string,
369                          response.data.auth.nt_status);
370         } else {
371                 if ((result != NSS_STATUS_SUCCESS) && (response.data.auth.nt_status == 0)) {
372                         DEBUG(1, ("Reading winbind reply failed! (0x01)\n"));
373                 }
374
375                 DEBUG(3, ("%s: %s (0x%x)\n",
376                           response.data.auth.nt_status_string,
377                           response.data.auth.error_string,
378                           response.data.auth.nt_status));
379         }
380
381         return (result == NSS_STATUS_SUCCESS);
382 }
383
384 /* authenticate a user with an encrypted username/password */
385
386 NTSTATUS contact_winbind_auth_crap(const char *username,
387                                    const char *domain,
388                                    const char *workstation,
389                                    const DATA_BLOB *challenge,
390                                    const DATA_BLOB *lm_response,
391                                    const DATA_BLOB *nt_response,
392                                    uint32 flags,
393                                    uint32 extra_logon_parameters,
394                                    uint8 lm_key[8],
395                                    uint8 user_session_key[16],
396                                    char **error_string,
397                                    char **unix_name)
398 {
399         NTSTATUS nt_status;
400         NSS_STATUS result;
401         struct winbindd_request request;
402         struct winbindd_response response;
403
404         if (!get_require_membership_sid()) {
405                 return NT_STATUS_INVALID_PARAMETER;
406         }
407
408         ZERO_STRUCT(request);
409         ZERO_STRUCT(response);
410
411         request.flags = flags;
412
413         request.data.auth_crap.logon_parameters = extra_logon_parameters
414                 | MSV1_0_ALLOW_WORKSTATION_TRUST_ACCOUNT | MSV1_0_ALLOW_SERVER_TRUST_ACCOUNT;
415
416         if (require_membership_of_sid)
417                 fstrcpy(request.data.auth_crap.require_membership_of_sid, require_membership_of_sid);
418
419         fstrcpy(request.data.auth_crap.user, username);
420         fstrcpy(request.data.auth_crap.domain, domain);
421
422         fstrcpy(request.data.auth_crap.workstation, 
423                 workstation);
424
425         memcpy(request.data.auth_crap.chal, challenge->data, MIN(challenge->length, 8));
426
427         if (lm_response && lm_response->length) {
428                 memcpy(request.data.auth_crap.lm_resp, 
429                        lm_response->data, 
430                        MIN(lm_response->length, sizeof(request.data.auth_crap.lm_resp)));
431                 request.data.auth_crap.lm_resp_len = lm_response->length;
432         }
433
434         if (nt_response && nt_response->length) {
435                 if (nt_response->length > sizeof(request.data.auth_crap.nt_resp)) {
436                         request.flags = request.flags | WBFLAG_BIG_NTLMV2_BLOB;
437                         request.extra_len = nt_response->length;
438                         request.extra_data.data = SMB_MALLOC_ARRAY(char, request.extra_len);
439                         if (request.extra_data.data == NULL) {
440                                 return NT_STATUS_NO_MEMORY;
441                         }
442                         memcpy(request.extra_data.data, nt_response->data,
443                                nt_response->length);
444
445                 } else {
446                         memcpy(request.data.auth_crap.nt_resp,
447                                nt_response->data, nt_response->length);
448                 }
449                 request.data.auth_crap.nt_resp_len = nt_response->length;
450         }
451
452         result = winbindd_request_response(WINBINDD_PAM_AUTH_CRAP, &request, &response);
453         SAFE_FREE(request.extra_data.data);
454
455         /* Display response */
456
457         if ((result != NSS_STATUS_SUCCESS) && (response.data.auth.nt_status == 0)) {
458                 nt_status = NT_STATUS_UNSUCCESSFUL;
459                 if (error_string)
460                         *error_string = smb_xstrdup("Reading winbind reply failed!");
461                 winbindd_free_response(&response);
462                 return nt_status;
463         }
464
465         nt_status = (NT_STATUS(response.data.auth.nt_status));
466         if (!NT_STATUS_IS_OK(nt_status)) {
467                 if (error_string) 
468                         *error_string = smb_xstrdup(response.data.auth.error_string);
469                 winbindd_free_response(&response);
470                 return nt_status;
471         }
472
473         if ((flags & WBFLAG_PAM_LMKEY) && lm_key) {
474                 memcpy(lm_key, response.data.auth.first_8_lm_hash, 
475                        sizeof(response.data.auth.first_8_lm_hash));
476         }
477         if ((flags & WBFLAG_PAM_USER_SESSION_KEY) && user_session_key) {
478                 memcpy(user_session_key, response.data.auth.user_session_key, 
479                         sizeof(response.data.auth.user_session_key));
480         }
481
482         if (flags & WBFLAG_PAM_UNIX_NAME) {
483                 *unix_name = SMB_STRDUP(response.data.auth.unix_username);
484                 if (!*unix_name) {
485                         winbindd_free_response(&response);
486                         return NT_STATUS_NO_MEMORY;
487                 }
488         }
489
490         winbindd_free_response(&response);
491         return nt_status;
492 }
493
494 /* contact server to change user password using auth crap */
495 static NTSTATUS contact_winbind_change_pswd_auth_crap(const char *username,
496                                                       const char *domain,
497                                                       const DATA_BLOB new_nt_pswd,
498                                                       const DATA_BLOB old_nt_hash_enc,
499                                                       const DATA_BLOB new_lm_pswd,
500                                                       const DATA_BLOB old_lm_hash_enc,
501                                                       char  **error_string)
502 {
503         NTSTATUS nt_status;
504         NSS_STATUS result;
505         struct winbindd_request request;
506         struct winbindd_response response;
507
508         if (!get_require_membership_sid())
509         {
510                 if(error_string)
511                         *error_string = smb_xstrdup("Can't get membership sid.");
512                 return NT_STATUS_INVALID_PARAMETER;
513         }
514
515         ZERO_STRUCT(request);
516         ZERO_STRUCT(response);
517
518         if(username != NULL)
519                 fstrcpy(request.data.chng_pswd_auth_crap.user, username);
520         if(domain != NULL)
521                 fstrcpy(request.data.chng_pswd_auth_crap.domain,domain);
522
523         if(new_nt_pswd.length)
524         {
525                 memcpy(request.data.chng_pswd_auth_crap.new_nt_pswd, new_nt_pswd.data, sizeof(request.data.chng_pswd_auth_crap.new_nt_pswd));
526                 request.data.chng_pswd_auth_crap.new_nt_pswd_len = new_nt_pswd.length;
527         }
528
529         if(old_nt_hash_enc.length)
530         {
531                 memcpy(request.data.chng_pswd_auth_crap.old_nt_hash_enc, old_nt_hash_enc.data, sizeof(request.data.chng_pswd_auth_crap.old_nt_hash_enc));
532                 request.data.chng_pswd_auth_crap.old_nt_hash_enc_len = old_nt_hash_enc.length;
533         }
534
535         if(new_lm_pswd.length)
536         {
537                 memcpy(request.data.chng_pswd_auth_crap.new_lm_pswd, new_lm_pswd.data, sizeof(request.data.chng_pswd_auth_crap.new_lm_pswd));
538                 request.data.chng_pswd_auth_crap.new_lm_pswd_len = new_lm_pswd.length;
539         }
540
541         if(old_lm_hash_enc.length)
542         {
543                 memcpy(request.data.chng_pswd_auth_crap.old_lm_hash_enc, old_lm_hash_enc.data, sizeof(request.data.chng_pswd_auth_crap.old_lm_hash_enc));
544                 request.data.chng_pswd_auth_crap.old_lm_hash_enc_len = old_lm_hash_enc.length;
545         }
546
547         result = winbindd_request_response(WINBINDD_PAM_CHNG_PSWD_AUTH_CRAP, &request, &response);
548
549         /* Display response */
550
551         if ((result != NSS_STATUS_SUCCESS) && (response.data.auth.nt_status == 0))
552         {
553                 nt_status = NT_STATUS_UNSUCCESSFUL;
554                 if (error_string)
555                         *error_string = smb_xstrdup("Reading winbind reply failed!");
556                 winbindd_free_response(&response);
557                 return nt_status;
558         }
559
560         nt_status = (NT_STATUS(response.data.auth.nt_status));
561         if (!NT_STATUS_IS_OK(nt_status))
562         {
563                 if (error_string) 
564                         *error_string = smb_xstrdup(response.data.auth.error_string);
565                 winbindd_free_response(&response);
566                 return nt_status;
567         }
568
569         winbindd_free_response(&response);
570
571     return nt_status;
572 }
573
574 static NTSTATUS winbind_pw_check(struct ntlmssp_state *ntlmssp_state, TALLOC_CTX *mem_ctx,
575                                  DATA_BLOB *user_session_key, DATA_BLOB *lm_session_key)
576 {
577         static const char zeros[16] = { 0, };
578         NTSTATUS nt_status;
579         char *error_string = NULL;
580         uint8 lm_key[8]; 
581         uint8 user_sess_key[16]; 
582         char *unix_name = NULL;
583
584         nt_status = contact_winbind_auth_crap(ntlmssp_state->user, ntlmssp_state->domain,
585                                               ntlmssp_state->client.netbios_name,
586                                               &ntlmssp_state->chal,
587                                               &ntlmssp_state->lm_resp,
588                                               &ntlmssp_state->nt_resp, 
589                                               WBFLAG_PAM_LMKEY | WBFLAG_PAM_USER_SESSION_KEY | WBFLAG_PAM_UNIX_NAME,
590                                               0,
591                                               lm_key, user_sess_key, 
592                                               &error_string, &unix_name);
593
594         if (NT_STATUS_IS_OK(nt_status)) {
595                 if (memcmp(lm_key, zeros, 8) != 0) {
596                         *lm_session_key = data_blob_talloc(mem_ctx, NULL, 16);
597                         memcpy(lm_session_key->data, lm_key, 8);
598                         memset(lm_session_key->data+8, '\0', 8);
599                 }
600
601                 if (memcmp(user_sess_key, zeros, 16) != 0) {
602                         *user_session_key = data_blob_talloc(mem_ctx, user_sess_key, 16);
603                 }
604                 ntlmssp_state->callback_private = talloc_strdup(ntlmssp_state,
605                                                                 unix_name);
606         } else {
607                 DEBUG(NT_STATUS_EQUAL(nt_status, NT_STATUS_ACCESS_DENIED) ? 0 : 3, 
608                       ("Login for user [%s]\\[%s]@[%s] failed due to [%s]\n", 
609                        ntlmssp_state->domain, ntlmssp_state->user, 
610                        ntlmssp_state->client.netbios_name,
611                        error_string ? error_string : "unknown error (NULL)"));
612                 ntlmssp_state->callback_private = NULL;
613         }
614
615         SAFE_FREE(error_string);
616         SAFE_FREE(unix_name);
617         return nt_status;
618 }
619
620 static NTSTATUS local_pw_check(struct ntlmssp_state *ntlmssp_state, TALLOC_CTX *mem_ctx,
621                                DATA_BLOB *user_session_key, DATA_BLOB *lm_session_key)
622 {
623         NTSTATUS nt_status;
624         struct samr_Password lm_pw, nt_pw;
625
626         nt_lm_owf_gen (opt_password, nt_pw.hash, lm_pw.hash);
627
628         nt_status = ntlm_password_check(mem_ctx,
629                                         true, true, 0,
630                                         &ntlmssp_state->chal,
631                                         &ntlmssp_state->lm_resp,
632                                         &ntlmssp_state->nt_resp, 
633                                         ntlmssp_state->user, 
634                                         ntlmssp_state->user, 
635                                         ntlmssp_state->domain,
636                                         &lm_pw, &nt_pw, user_session_key, lm_session_key);
637
638         if (NT_STATUS_IS_OK(nt_status)) {
639                 ntlmssp_state->callback_private = talloc_asprintf(ntlmssp_state,
640                                                               "%s%c%s", ntlmssp_state->domain, 
641                                                               *lp_winbind_separator(), 
642                                                               ntlmssp_state->user);
643         } else {
644                 DEBUG(3, ("Login for user [%s]\\[%s]@[%s] failed due to [%s]\n", 
645                           ntlmssp_state->domain, ntlmssp_state->user,
646                           ntlmssp_state->client.netbios_name,
647                           nt_errstr(nt_status)));
648                 ntlmssp_state->callback_private = NULL;
649         }
650         return nt_status;
651 }
652
653 static NTSTATUS ntlm_auth_start_ntlmssp_client(struct ntlmssp_state **client_ntlmssp_state)
654 {
655         NTSTATUS status;
656         if ( (opt_username == NULL) || (opt_domain == NULL) ) {
657                 status = NT_STATUS_UNSUCCESSFUL;
658                 DEBUG(1, ("Need username and domain for NTLMSSP\n"));
659                 return NT_STATUS_INVALID_PARAMETER;
660         }
661
662         status = ntlmssp_client_start(NULL,
663                                       lp_netbios_name(),
664                                       lp_workgroup(),
665                                       lp_client_ntlmv2_auth(),
666                                       client_ntlmssp_state);
667
668         if (!NT_STATUS_IS_OK(status)) {
669                 DEBUG(1, ("Could not start NTLMSSP client: %s\n",
670                           nt_errstr(status)));
671                 TALLOC_FREE(*client_ntlmssp_state);
672                 return status;
673         }
674
675         status = ntlmssp_set_username(*client_ntlmssp_state, opt_username);
676
677         if (!NT_STATUS_IS_OK(status)) {
678                 DEBUG(1, ("Could not set username: %s\n",
679                           nt_errstr(status)));
680                 TALLOC_FREE(*client_ntlmssp_state);
681                 return status;
682         }
683
684         status = ntlmssp_set_domain(*client_ntlmssp_state, opt_domain);
685
686         if (!NT_STATUS_IS_OK(status)) {
687                 DEBUG(1, ("Could not set domain: %s\n",
688                           nt_errstr(status)));
689                 TALLOC_FREE(*client_ntlmssp_state);
690                 return status;
691         }
692
693         if (opt_password) {
694                 status = ntlmssp_set_password(*client_ntlmssp_state, opt_password);
695
696                 if (!NT_STATUS_IS_OK(status)) {
697                         DEBUG(1, ("Could not set password: %s\n",
698                                   nt_errstr(status)));
699                         TALLOC_FREE(*client_ntlmssp_state);
700                         return status;
701                 }
702         }
703
704         return NT_STATUS_OK;
705 }
706
707 static NTSTATUS ntlm_auth_start_ntlmssp_server(struct ntlmssp_state **ntlmssp_state)
708 {
709         NTSTATUS status;
710         const char *netbios_name;
711         const char *netbios_domain;
712         const char *dns_name;
713         char *dns_domain;
714         bool is_standalone = false;
715
716         if (opt_password) {
717                 netbios_name = lp_netbios_name();
718                 netbios_domain = lp_workgroup();
719         } else {
720                 netbios_name = get_winbind_netbios_name();
721                 netbios_domain = get_winbind_domain();
722         }
723         /* This should be a 'netbios domain -> DNS domain' mapping */
724         dns_domain = get_mydnsdomname(talloc_tos());
725         if (dns_domain) {
726                 strlower_m(dns_domain);
727         }
728         dns_name = get_mydnsfullname();
729
730         status = ntlmssp_server_start(NULL,
731                                       is_standalone,
732                                       netbios_name,
733                                       netbios_domain,
734                                       dns_name,
735                                       dns_domain,
736                                       ntlmssp_state);
737         if (!NT_STATUS_IS_OK(status)) {
738                 DEBUG(1, ("Could not start NTLMSSP server: %s\n",
739                           nt_errstr(status)));
740                 return status;
741         }
742
743         (*ntlmssp_state)->neg_flags |=
744                 (NTLMSSP_NEGOTIATE_SIGN |
745                  NTLMSSP_NEGOTIATE_SEAL);
746
747         /* Have we been given a local password, or should we ask winbind? */
748         if (opt_password) {
749                 (*ntlmssp_state)->check_password = local_pw_check;
750         } else {
751                 (*ntlmssp_state)->check_password = winbind_pw_check;
752         }
753         return NT_STATUS_OK;
754 }
755
756 /*******************************************************************
757  Used by firefox to drive NTLM auth to IIS servers.
758 *******************************************************************/
759
760 static NTSTATUS do_ccache_ntlm_auth(DATA_BLOB initial_msg, DATA_BLOB challenge_msg,
761                                 DATA_BLOB *reply)
762 {
763         struct winbindd_request wb_request;
764         struct winbindd_response wb_response;
765         int ctrl = 0;
766         NSS_STATUS result;
767
768         /* get winbindd to do the ntlmssp step on our behalf */
769         ZERO_STRUCT(wb_request);
770         ZERO_STRUCT(wb_response);
771
772         /*
773          * This is tricky here. If we set krb5_auth in pam_winbind.conf
774          * creds for users in trusted domain will be stored the winbindd
775          * child of the trusted domain. If we ask the primary domain for
776          * ntlm_ccache_auth, it will fail. So, we have to ask the trusted
777          * domain's child for ccache_ntlm_auth. that is to say, we have to 
778          * set WBFLAG_PAM_CONTACT_TRUSTDOM in request.flags.
779          */
780         ctrl = get_pam_winbind_config();
781
782         if (ctrl & WINBIND_KRB5_AUTH) {
783                 wb_request.flags |= WBFLAG_PAM_CONTACT_TRUSTDOM;
784         }
785
786         fstr_sprintf(wb_request.data.ccache_ntlm_auth.user,
787                 "%s%c%s", opt_domain, winbind_separator(), opt_username);
788         wb_request.data.ccache_ntlm_auth.uid = geteuid();
789         wb_request.data.ccache_ntlm_auth.initial_blob_len = initial_msg.length;
790         wb_request.data.ccache_ntlm_auth.challenge_blob_len = challenge_msg.length;
791         wb_request.extra_len = initial_msg.length + challenge_msg.length;
792
793         if (wb_request.extra_len > 0) {
794                 wb_request.extra_data.data = SMB_MALLOC_ARRAY(char, wb_request.extra_len);
795                 if (wb_request.extra_data.data == NULL) {
796                         return NT_STATUS_NO_MEMORY;
797                 }
798
799                 memcpy(wb_request.extra_data.data, initial_msg.data, initial_msg.length);
800                 memcpy(wb_request.extra_data.data + initial_msg.length,
801                         challenge_msg.data, challenge_msg.length);
802         }
803
804         result = winbindd_request_response(WINBINDD_CCACHE_NTLMAUTH, &wb_request, &wb_response);
805         SAFE_FREE(wb_request.extra_data.data);
806
807         if (result != NSS_STATUS_SUCCESS) {
808                 winbindd_free_response(&wb_response);
809                 return NT_STATUS_UNSUCCESSFUL;
810         }
811
812         if (reply) {
813                 *reply = data_blob(wb_response.extra_data.data,
814                                 wb_response.data.ccache_ntlm_auth.auth_blob_len);
815                 if (wb_response.data.ccache_ntlm_auth.auth_blob_len > 0 &&
816                                 reply->data == NULL) {
817                         winbindd_free_response(&wb_response);
818                         return NT_STATUS_NO_MEMORY;
819                 }
820         }
821
822         winbindd_free_response(&wb_response);
823         return NT_STATUS_MORE_PROCESSING_REQUIRED;
824 }
825
826 static void manage_squid_ntlmssp_request_int(struct ntlm_auth_state *state,
827                                              char *buf, int length,
828                                              TALLOC_CTX *mem_ctx,
829                                              char **response)
830 {
831         DATA_BLOB request, reply;
832         NTSTATUS nt_status;
833
834         if (strlen(buf) < 2) {
835                 DEBUG(1, ("NTLMSSP query [%s] invalid\n", buf));
836                 *response = talloc_strdup(mem_ctx, "BH NTLMSSP query invalid");
837                 return;
838         }
839
840         if (strlen(buf) > 3) {
841                 if(strncmp(buf, "SF ", 3) == 0){
842                         DEBUG(10, ("Setting flags to negotioate\n"));
843                         TALLOC_FREE(state->want_feature_list);
844                         state->want_feature_list = talloc_strdup(state->mem_ctx,
845                                         buf+3);
846                         *response = talloc_strdup(mem_ctx, "OK");
847                         return;
848                 }
849                 request = base64_decode_data_blob(buf + 3);
850         } else {
851                 request = data_blob_null;
852         }
853
854         if ((strncmp(buf, "PW ", 3) == 0)) {
855                 /* The calling application wants us to use a local password
856                  * (rather than winbindd) */
857
858                 opt_password = SMB_STRNDUP((const char *)request.data,
859                                 request.length);
860
861                 if (opt_password == NULL) {
862                         DEBUG(1, ("Out of memory\n"));
863                         *response = talloc_strdup(mem_ctx, "BH Out of memory");
864                         data_blob_free(&request);
865                         return;
866                 }
867
868                 *response = talloc_strdup(mem_ctx, "OK");
869                 data_blob_free(&request);
870                 return;
871         }
872
873         if (strncmp(buf, "YR", 2) == 0) {
874                 TALLOC_FREE(state->ntlmssp_state);
875                 state->svr_state = SERVER_INITIAL;
876         } else if (strncmp(buf, "KK", 2) == 0) {
877                 /* No special preprocessing required */
878         } else if (strncmp(buf, "GF", 2) == 0) {
879                 DEBUG(10, ("Requested negotiated NTLMSSP flags\n"));
880
881                 if (state->svr_state == SERVER_FINISHED) {
882                         *response = talloc_asprintf(mem_ctx, "GF 0x%08x",
883                                                  state->neg_flags);
884                 }
885                 else {
886                         *response = talloc_strdup(mem_ctx, "BH\n");
887                 }
888                 data_blob_free(&request);
889                 return;
890         } else if (strncmp(buf, "GK", 2) == 0) {
891                 DEBUG(10, ("Requested NTLMSSP session key\n"));
892                 if(state->have_session_key) {
893                         char *key64 = base64_encode_data_blob(state->mem_ctx,
894                                         state->session_key);
895                         *response = talloc_asprintf(mem_ctx, "GK %s",
896                                                  key64 ? key64 : "<NULL>");
897                         TALLOC_FREE(key64);
898                 } else {
899                         *response = talloc_strdup(mem_ctx, "BH");
900                 }
901
902                 data_blob_free(&request);
903                 return;
904         } else {
905                 DEBUG(1, ("NTLMSSP query [%s] invalid\n", buf));
906                 *response = talloc_strdup(mem_ctx, "BH NTLMSSP query invalid");
907                 return;
908         }
909
910         if (!state->ntlmssp_state) {
911                 nt_status = ntlm_auth_start_ntlmssp_server(
912                                 &state->ntlmssp_state);
913                 if (!NT_STATUS_IS_OK(nt_status)) {
914                         *response = talloc_asprintf(
915                                 mem_ctx, "BH %s", nt_errstr(nt_status));
916                         return;
917                 }
918                 ntlmssp_want_feature_list(state->ntlmssp_state,
919                                 state->want_feature_list);
920         }
921
922         DEBUG(10, ("got NTLMSSP packet:\n"));
923         dump_data(10, request.data, request.length);
924
925         nt_status = ntlmssp_update(state->ntlmssp_state, request, &reply);
926
927         if (NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
928                 char *reply_base64 = base64_encode_data_blob(state->mem_ctx,
929                                 reply);
930                 *response = talloc_asprintf(mem_ctx, "TT %s", reply_base64);
931                 TALLOC_FREE(reply_base64);
932                 data_blob_free(&reply);
933                 state->svr_state = SERVER_CHALLENGE;
934                 DEBUG(10, ("NTLMSSP challenge\n"));
935         } else if (NT_STATUS_EQUAL(nt_status, NT_STATUS_ACCESS_DENIED)) {
936                 *response = talloc_asprintf(mem_ctx, "BH %s",
937                                          nt_errstr(nt_status));
938                 DEBUG(0, ("NTLMSSP BH: %s\n", nt_errstr(nt_status)));
939
940                 TALLOC_FREE(state->ntlmssp_state);
941         } else if (!NT_STATUS_IS_OK(nt_status)) {
942                 *response = talloc_asprintf(mem_ctx, "NA %s",
943                                          nt_errstr(nt_status));
944                 DEBUG(10, ("NTLMSSP %s\n", nt_errstr(nt_status)));
945         } else {
946                 *response = talloc_asprintf(
947                         mem_ctx, "AF %s",
948                         (char *)state->ntlmssp_state->callback_private);
949                 DEBUG(10, ("NTLMSSP OK!\n"));
950
951                 if(state->have_session_key)
952                         data_blob_free(&state->session_key);
953                 state->session_key = data_blob(
954                                 state->ntlmssp_state->session_key.data,
955                                 state->ntlmssp_state->session_key.length);
956                 state->neg_flags = state->ntlmssp_state->neg_flags;
957                 state->have_session_key = true;
958                 state->svr_state = SERVER_FINISHED;
959         }
960
961         data_blob_free(&request);
962 }
963
964 static void manage_squid_ntlmssp_request(struct ntlm_auth_state *state,
965                                          char *buf, int length)
966 {
967         char *response;
968
969         manage_squid_ntlmssp_request_int(state, buf, length,
970                                          talloc_tos(), &response);
971
972         if (response == NULL) {
973                 x_fprintf(x_stdout, "BH Out of memory\n");
974                 return;
975         }
976         x_fprintf(x_stdout, "%s\n", response);
977         TALLOC_FREE(response);
978 }
979
980 static void manage_client_ntlmssp_request(struct ntlm_auth_state *state,
981                                                 char *buf, int length)
982 {
983         DATA_BLOB request, reply;
984         NTSTATUS nt_status;
985
986         if (!opt_username || !*opt_username) {
987                 x_fprintf(x_stderr, "username must be specified!\n\n");
988                 exit(1);
989         }
990
991         if (strlen(buf) < 2) {
992                 DEBUG(1, ("NTLMSSP query [%s] invalid\n", buf));
993                 x_fprintf(x_stdout, "BH NTLMSSP query invalid\n");
994                 return;
995         }
996
997         if (strlen(buf) > 3) {
998                 if(strncmp(buf, "SF ", 3) == 0) {
999                         DEBUG(10, ("Looking for flags to negotiate\n"));
1000                         talloc_free(state->want_feature_list);
1001                         state->want_feature_list = talloc_strdup(state->mem_ctx,
1002                                         buf+3);
1003                         x_fprintf(x_stdout, "OK\n");
1004                         return;
1005                 }
1006                 request = base64_decode_data_blob(buf + 3);
1007         } else {
1008                 request = data_blob_null;
1009         }
1010
1011         if (strncmp(buf, "PW ", 3) == 0) {
1012                 /* We asked for a password and obviously got it :-) */
1013
1014                 opt_password = SMB_STRNDUP((const char *)request.data,
1015                                 request.length);
1016
1017                 if (opt_password == NULL) {
1018                         DEBUG(1, ("Out of memory\n"));
1019                         x_fprintf(x_stdout, "BH Out of memory\n");
1020                         data_blob_free(&request);
1021                         return;
1022                 }
1023
1024                 x_fprintf(x_stdout, "OK\n");
1025                 data_blob_free(&request);
1026                 return;
1027         }
1028
1029         if (!state->ntlmssp_state && use_cached_creds) {
1030                 /* check whether cached credentials are usable. */
1031                 DATA_BLOB empty_blob = data_blob_null;
1032
1033                 nt_status = do_ccache_ntlm_auth(empty_blob, empty_blob, NULL);
1034                 if (!NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
1035                         /* failed to use cached creds */
1036                         use_cached_creds = False;
1037                 }
1038         }
1039
1040         if (opt_password == NULL && !use_cached_creds) {
1041                 /* Request a password from the calling process.  After
1042                    sending it, the calling process should retry asking for the
1043                    negotiate. */
1044
1045                 DEBUG(10, ("Requesting password\n"));
1046                 x_fprintf(x_stdout, "PW\n");
1047                 return;
1048         }
1049
1050         if (strncmp(buf, "YR", 2) == 0) {
1051                 TALLOC_FREE(state->ntlmssp_state);
1052                 state->cli_state = CLIENT_INITIAL;
1053         } else if (strncmp(buf, "TT", 2) == 0) {
1054                 /* No special preprocessing required */
1055         } else if (strncmp(buf, "GF", 2) == 0) {
1056                 DEBUG(10, ("Requested negotiated NTLMSSP flags\n"));
1057
1058                 if(state->cli_state == CLIENT_FINISHED) {
1059                         x_fprintf(x_stdout, "GF 0x%08x\n", state->neg_flags);
1060                 }
1061                 else {
1062                         x_fprintf(x_stdout, "BH\n");
1063                 }
1064
1065                 data_blob_free(&request);
1066                 return;
1067         } else if (strncmp(buf, "GK", 2) == 0 ) {
1068                 DEBUG(10, ("Requested session key\n"));
1069
1070                 if(state->cli_state == CLIENT_FINISHED) {
1071                         char *key64 = base64_encode_data_blob(state->mem_ctx,
1072                                         state->session_key);
1073                         x_fprintf(x_stdout, "GK %s\n", key64?key64:"<NULL>");
1074                         TALLOC_FREE(key64);
1075                 }
1076                 else {
1077                         x_fprintf(x_stdout, "BH\n");
1078                 }
1079
1080                 data_blob_free(&request);
1081                 return;
1082         } else {
1083                 DEBUG(1, ("NTLMSSP query [%s] invalid\n", buf));
1084                 x_fprintf(x_stdout, "BH NTLMSSP query invalid\n");
1085                 return;
1086         }
1087
1088         if (!state->ntlmssp_state) {
1089                 nt_status = ntlm_auth_start_ntlmssp_client(
1090                                 &state->ntlmssp_state);
1091                 if (!NT_STATUS_IS_OK(nt_status)) {
1092                         x_fprintf(x_stdout, "BH %s\n", nt_errstr(nt_status));
1093                         return;
1094                 }
1095                 ntlmssp_want_feature_list(state->ntlmssp_state,
1096                                 state->want_feature_list);
1097                 state->initial_message = data_blob_null;
1098         }
1099
1100         DEBUG(10, ("got NTLMSSP packet:\n"));
1101         dump_data(10, request.data, request.length);
1102
1103         if (use_cached_creds && !opt_password &&
1104                         (state->cli_state == CLIENT_RESPONSE)) {
1105                 nt_status = do_ccache_ntlm_auth(state->initial_message, request,
1106                                 &reply);
1107         } else {
1108                 nt_status = ntlmssp_update(state->ntlmssp_state, request,
1109                                 &reply);
1110         }
1111
1112         if (NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
1113                 char *reply_base64 = base64_encode_data_blob(state->mem_ctx,
1114                                 reply);
1115                 if (state->cli_state == CLIENT_INITIAL) {
1116                         x_fprintf(x_stdout, "YR %s\n", reply_base64);
1117                         state->initial_message = reply;
1118                         state->cli_state = CLIENT_RESPONSE;
1119                 } else {
1120                         x_fprintf(x_stdout, "KK %s\n", reply_base64);
1121                         data_blob_free(&reply);
1122                 }
1123                 TALLOC_FREE(reply_base64);
1124                 DEBUG(10, ("NTLMSSP challenge\n"));
1125         } else if (NT_STATUS_IS_OK(nt_status)) {
1126                 char *reply_base64 = base64_encode_data_blob(talloc_tos(),
1127                                 reply);
1128                 x_fprintf(x_stdout, "AF %s\n", reply_base64);
1129                 TALLOC_FREE(reply_base64);
1130
1131                 if(state->have_session_key)
1132                         data_blob_free(&state->session_key);
1133
1134                 state->session_key = data_blob(
1135                                 state->ntlmssp_state->session_key.data,
1136                                 state->ntlmssp_state->session_key.length);
1137                 state->neg_flags = state->ntlmssp_state->neg_flags;
1138                 state->have_session_key = true;
1139
1140                 DEBUG(10, ("NTLMSSP OK!\n"));
1141                 state->cli_state = CLIENT_FINISHED;
1142                 TALLOC_FREE(state->ntlmssp_state);
1143         } else {
1144                 x_fprintf(x_stdout, "BH %s\n", nt_errstr(nt_status));
1145                 DEBUG(0, ("NTLMSSP BH: %s\n", nt_errstr(nt_status)));
1146                 state->cli_state = CLIENT_ERROR;
1147                 TALLOC_FREE(state->ntlmssp_state);
1148         }
1149
1150         data_blob_free(&request);
1151 }
1152
1153 static void manage_squid_basic_request(struct ntlm_auth_state *state,
1154                                         char *buf, int length)
1155 {
1156         char *user, *pass;      
1157         user=buf;
1158
1159         pass=(char *)memchr(buf,' ',length);
1160         if (!pass) {
1161                 DEBUG(2, ("Password not found. Denying access\n"));
1162                 x_fprintf(x_stdout, "ERR\n");
1163                 return;
1164         }
1165         *pass='\0';
1166         pass++;
1167
1168         if (state->helper_mode == SQUID_2_5_BASIC) {
1169                 rfc1738_unescape(user);
1170                 rfc1738_unescape(pass);
1171         }
1172
1173         if (check_plaintext_auth(user, pass, False)) {
1174                 x_fprintf(x_stdout, "OK\n");
1175         } else {
1176                 x_fprintf(x_stdout, "ERR\n");
1177         }
1178 }
1179
1180 static void offer_gss_spnego_mechs(void) {
1181
1182         DATA_BLOB token;
1183         struct spnego_data spnego;
1184         ssize_t len;
1185         char *reply_base64;
1186         TALLOC_CTX *ctx = talloc_tos();
1187         char *principal;
1188         char *myname_lower;
1189
1190         ZERO_STRUCT(spnego);
1191
1192         myname_lower = talloc_strdup(ctx, lp_netbios_name());
1193         if (!myname_lower) {
1194                 return;
1195         }
1196         strlower_m(myname_lower);
1197
1198         principal = talloc_asprintf(ctx, "%s$@%s", myname_lower, lp_realm());
1199         if (!principal) {
1200                 return;
1201         }
1202
1203         /* Server negTokenInit (mech offerings) */
1204         spnego.type = SPNEGO_NEG_TOKEN_INIT;
1205         spnego.negTokenInit.mechTypes = talloc_array(ctx, const char *, 4);
1206 #ifdef HAVE_KRB5
1207         spnego.negTokenInit.mechTypes[0] = talloc_strdup(ctx, OID_KERBEROS5_OLD);
1208         spnego.negTokenInit.mechTypes[1] = talloc_strdup(ctx, OID_KERBEROS5);
1209         spnego.negTokenInit.mechTypes[2] = talloc_strdup(ctx, OID_NTLMSSP);
1210         spnego.negTokenInit.mechTypes[3] = NULL;
1211 #else
1212         spnego.negTokenInit.mechTypes[0] = talloc_strdup(ctx, OID_NTLMSSP);
1213         spnego.negTokenInit.mechTypes[1] = NULL;
1214 #endif
1215
1216
1217         spnego.negTokenInit.mechListMIC = data_blob_talloc(ctx, principal,
1218                                                     strlen(principal));
1219
1220         len = spnego_write_data(ctx, &token, &spnego);
1221         spnego_free_data(&spnego);
1222
1223         if (len == -1) {
1224                 DEBUG(1, ("Could not write SPNEGO data blob\n"));
1225                 x_fprintf(x_stdout, "BH Could not write SPNEGO data blob\n");
1226                 return;
1227         }
1228
1229         reply_base64 = base64_encode_data_blob(talloc_tos(), token);
1230         x_fprintf(x_stdout, "TT %s *\n", reply_base64);
1231
1232         TALLOC_FREE(reply_base64);
1233         data_blob_free(&token);
1234         DEBUG(10, ("sent SPNEGO negTokenInit\n"));
1235         return;
1236 }
1237
1238 static void manage_gss_spnego_request(struct ntlm_auth_state *state,
1239                                         char *buf, int length)
1240 {
1241         struct spnego_data request, response;
1242         DATA_BLOB token;
1243         DATA_BLOB raw_in_token = data_blob_null;
1244         DATA_BLOB raw_out_token = data_blob_null;
1245         NTSTATUS status;
1246         ssize_t len;
1247         TALLOC_CTX *ctx = talloc_tos();
1248
1249         char *user = NULL;
1250         char *domain = NULL;
1251
1252         const char *reply_code;
1253         char       *reply_base64;
1254         char *reply_argument = NULL;
1255         char *supportedMech = NULL;
1256
1257         if (strlen(buf) < 2) {
1258                 DEBUG(1, ("SPENGO query [%s] invalid\n", buf));
1259                 x_fprintf(x_stdout, "BH SPENGO query invalid\n");
1260                 return;
1261         }
1262
1263         if (strncmp(buf, "YR", 2) == 0) {
1264                 TALLOC_FREE(state->ntlmssp_state);
1265                 TALLOC_FREE(state->spnego_mech);
1266                 TALLOC_FREE(state->spnego_mech_oid);
1267         } else if (strncmp(buf, "KK", 2) == 0) {
1268                 ;
1269         } else {
1270                 DEBUG(1, ("SPENGO query [%s] invalid\n", buf));
1271                 x_fprintf(x_stdout, "BH SPENGO query invalid\n");
1272                 return;
1273         }
1274
1275         if ( (strlen(buf) == 2)) {
1276
1277                 /* no client data, get the negTokenInit offering
1278                    mechanisms */
1279
1280                 offer_gss_spnego_mechs();
1281                 return;
1282         }
1283
1284         /* All subsequent requests have a blob. This might be negTokenInit or negTokenTarg */
1285
1286         if (strlen(buf) <= 3) {
1287                 DEBUG(1, ("GSS-SPNEGO query [%s] invalid\n", buf));
1288                 x_fprintf(x_stdout, "BH GSS-SPNEGO query invalid\n");
1289                 return;
1290         }
1291
1292         token = base64_decode_data_blob(buf + 3);
1293
1294         if ((token.length >= 7)
1295             && (strncmp((char *)token.data, "NTLMSSP", 7) == 0)) {
1296                 char *reply;
1297
1298                 data_blob_free(&token);
1299
1300                 DEBUG(10, ("Could not parse GSS-SPNEGO, trying raw "
1301                            "ntlmssp\n"));
1302
1303                 manage_squid_ntlmssp_request_int(state, buf, length,
1304                                                  talloc_tos(), &reply);
1305                 if (reply == NULL) {
1306                         x_fprintf(x_stdout, "BH Out of memory\n");
1307                         return;
1308                 }
1309
1310                 if (strncmp(reply, "AF ", 3) == 0) {
1311                         x_fprintf(x_stdout, "AF * %s\n", reply+3);
1312                 } else {
1313                         x_fprintf(x_stdout, "%s *\n", reply);
1314                 }
1315
1316                 TALLOC_FREE(reply);
1317                 return;
1318         }
1319
1320         ZERO_STRUCT(request);
1321         len = spnego_read_data(ctx, token, &request);
1322         data_blob_free(&token);
1323
1324         if (len == -1) {
1325                 DEBUG(1, ("GSS-SPNEGO query [%s] invalid\n", buf));
1326                 x_fprintf(x_stdout, "BH GSS-SPNEGO query invalid\n");
1327                 return;
1328         }
1329
1330         if (request.type == SPNEGO_NEG_TOKEN_INIT) {
1331 #ifdef HAVE_KRB5
1332                 int krb5_idx = -1;
1333 #endif
1334                 int ntlm_idx = -1;
1335                 int used_idx = -1;
1336                 int i;
1337
1338                 if (state->spnego_mech) {
1339                         DEBUG(1, ("Client restarted SPNEGO with NegTokenInit "
1340                                   "while mech[%s] was already negotiated\n",
1341                                   state->spnego_mech));
1342                         x_fprintf(x_stdout, "BH Client send NegTokenInit twice\n");
1343                         return;
1344                 }
1345
1346                 /* Second request from Client. This is where the
1347                    client offers its mechanism to use. */
1348
1349                 if ( (request.negTokenInit.mechTypes == NULL) ||
1350                      (request.negTokenInit.mechTypes[0] == NULL) ) {
1351                         DEBUG(1, ("Client did not offer any mechanism\n"));
1352                         x_fprintf(x_stdout, "BH Client did not offer any "
1353                                             "mechanism\n");
1354                         return;
1355                 }
1356
1357                 status = NT_STATUS_UNSUCCESSFUL;
1358                 for (i = 0; request.negTokenInit.mechTypes[i] != NULL; i++) {
1359                         DEBUG(10,("got mech[%d][%s]\n",
1360                                 i, request.negTokenInit.mechTypes[i]));
1361 #ifdef HAVE_KRB5
1362                         if (strcmp(request.negTokenInit.mechTypes[i], OID_KERBEROS5_OLD) == 0) {
1363                                 krb5_idx = i;
1364                                 break;
1365                         }
1366                         if (strcmp(request.negTokenInit.mechTypes[i], OID_KERBEROS5) == 0) {
1367                                 krb5_idx = i;
1368                                 break;
1369                         }
1370 #endif
1371                         if (strcmp(request.negTokenInit.mechTypes[i], OID_NTLMSSP) == 0) {
1372                                 ntlm_idx = i;
1373                                 break;
1374                         }
1375                 }
1376
1377                 used_idx = ntlm_idx;
1378 #ifdef HAVE_KRB5
1379                 if (krb5_idx != -1) {
1380                         ntlm_idx = -1;
1381                         used_idx = krb5_idx;
1382                 }
1383 #endif
1384                 if (ntlm_idx > -1) {
1385                         state->spnego_mech = talloc_strdup(state, "ntlmssp");
1386                         if (state->spnego_mech == NULL) {
1387                                 x_fprintf(x_stdout, "BH Out of memory\n");
1388                                 return;
1389                         }
1390
1391                         if (state->ntlmssp_state) {
1392                                 DEBUG(1, ("Client wants a new NTLMSSP challenge, but "
1393                                           "already got one\n"));
1394                                 x_fprintf(x_stdout, "BH Client wants a new "
1395                                                     "NTLMSSP challenge, but "
1396                                                     "already got one\n");
1397                                 TALLOC_FREE(state->ntlmssp_state);
1398                                 return;
1399                         }
1400
1401                         status = ntlm_auth_start_ntlmssp_server(&state->ntlmssp_state);
1402                         if (!NT_STATUS_IS_OK(status)) {
1403                                 x_fprintf(x_stdout, "BH %s\n", nt_errstr(status));
1404                                 return;
1405                         }
1406                 }
1407
1408 #ifdef HAVE_KRB5
1409                 if (krb5_idx > -1) {
1410                         state->spnego_mech = talloc_strdup(state, "krb5");
1411                         if (state->spnego_mech == NULL) {
1412                                 x_fprintf(x_stdout, "BH Out of memory\n");
1413                                 return;
1414                         }
1415                 }
1416 #endif
1417                 if (used_idx > -1) {
1418                         state->spnego_mech_oid = talloc_strdup(state,
1419                                 request.negTokenInit.mechTypes[used_idx]);
1420                         if (state->spnego_mech_oid == NULL) {
1421                                 x_fprintf(x_stdout, "BH Out of memory\n");
1422                                 return;
1423                         }
1424                         supportedMech = talloc_strdup(ctx, state->spnego_mech_oid);
1425                         if (supportedMech == NULL) {
1426                                 x_fprintf(x_stdout, "BH Out of memory\n");
1427                                 return;
1428                         }
1429
1430                         status = NT_STATUS_MORE_PROCESSING_REQUIRED;
1431                 } else {
1432                         status = NT_STATUS_NOT_SUPPORTED;
1433                 }
1434                 if (used_idx == 0) {
1435                         status = NT_STATUS_OK;
1436                         raw_in_token = request.negTokenInit.mechToken;
1437                 }
1438         } else {
1439                 if (state->spnego_mech == NULL) {
1440                         DEBUG(1,("Got netTokenTarg without negTokenInit\n"));
1441                         x_fprintf(x_stdout, "BH Got a negTokenTarg without "
1442                                             "negTokenInit\n");
1443                         return;
1444                 }
1445
1446                 if ((request.negTokenTarg.supportedMech != NULL) &&
1447                      (strcmp(request.negTokenTarg.supportedMech, state->spnego_mech_oid) != 0 ) ) {
1448                         DEBUG(1, ("Got a negTokenTarg with mech[%s] while [%s] was already negotiated\n",
1449                                   request.negTokenTarg.supportedMech,
1450                                   state->spnego_mech_oid));
1451                         x_fprintf(x_stdout, "BH Got a negTokenTarg with speficied mech\n");
1452                         return;
1453                 }
1454
1455                 status = NT_STATUS_OK;
1456                 raw_in_token = request.negTokenTarg.responseToken;
1457         }
1458
1459         if (!NT_STATUS_IS_OK(status)) {
1460                 /* error or more processing */
1461         } else if (strcmp(state->spnego_mech, "ntlmssp") == 0) {
1462
1463                 DEBUG(10, ("got NTLMSSP packet:\n"));
1464                 dump_data(10, raw_in_token.data, raw_in_token.length);
1465
1466                 status = ntlmssp_update(state->ntlmssp_state,
1467                                         raw_in_token,
1468                                         &raw_out_token);
1469                 if (NT_STATUS_IS_OK(status)) {
1470                         user = talloc_strdup(ctx, state->ntlmssp_state->user);
1471                         domain = talloc_strdup(ctx, state->ntlmssp_state->domain);
1472                 }
1473                 if (!NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
1474                         TALLOC_FREE(state->ntlmssp_state);
1475                 }
1476 #ifdef HAVE_KRB5
1477         } else if (strcmp(state->spnego_mech, "krb5") == 0) {
1478                 char *principal;
1479                 DATA_BLOB ap_rep;
1480                 DATA_BLOB session_key;
1481                 struct PAC_LOGON_INFO *logon_info = NULL;
1482                 DATA_BLOB ticket;
1483                 uint8_t tok_id[2];
1484
1485                 if (!spnego_parse_krb5_wrap(ctx, raw_in_token,
1486                                             &ticket, tok_id)) {
1487                         DEBUG(1, ("spnego_parse_krb5_wrap failed\n"));
1488                         x_fprintf(x_stdout, "BH spnego_parse_krb5_wrap failed\n");
1489                         return;
1490                 }
1491
1492                 status = ads_verify_ticket(ctx, lp_realm(), 0,
1493                                            &ticket,
1494                                            &principal, &logon_info, &ap_rep,
1495                                            &session_key, True);
1496
1497                 /* Now in "principal" we have the name we are authenticated as. */
1498
1499                 if (NT_STATUS_IS_OK(status)) {
1500
1501                         domain = strchr_m(principal, '@');
1502
1503                         if (domain == NULL) {
1504                                 DEBUG(1, ("Did not get a valid principal "
1505                                           "from ads_verify_ticket\n"));
1506                                 x_fprintf(x_stdout, "BH Did not get a "
1507                                           "valid principal from "
1508                                           "ads_verify_ticket\n");
1509                                 return;
1510                         }
1511
1512                         *domain++ = '\0';
1513                         domain = talloc_strdup(ctx, domain);
1514                         user = talloc_strdup(ctx, principal);
1515
1516                         if (logon_info) {
1517                                 netsamlogon_cache_store(
1518                                         user, &logon_info->info3);
1519                         }
1520
1521                         data_blob_free(&ap_rep);
1522                         data_blob_free(&session_key);
1523                 }
1524                 data_blob_free(&ticket);
1525 #endif
1526         }
1527
1528         spnego_free_data(&request);
1529         ZERO_STRUCT(response);
1530         response.type = SPNEGO_NEG_TOKEN_TARG;
1531
1532         if (NT_STATUS_IS_OK(status)) {
1533                 TALLOC_FREE(state->spnego_mech);
1534                 TALLOC_FREE(state->spnego_mech_oid);
1535                 response.negTokenTarg.negResult = SPNEGO_ACCEPT_COMPLETED;
1536                 response.negTokenTarg.responseToken = raw_out_token;
1537                 reply_code = "AF";
1538                 reply_argument = talloc_asprintf(ctx, "%s\\%s", domain, user);
1539         } else if (NT_STATUS_EQUAL(status,
1540                                    NT_STATUS_MORE_PROCESSING_REQUIRED)) {
1541                 response.negTokenTarg.supportedMech = supportedMech;
1542                 response.negTokenTarg.responseToken = raw_out_token;
1543                 response.negTokenTarg.negResult = SPNEGO_ACCEPT_INCOMPLETE;
1544                 reply_code = "TT";
1545                 reply_argument = talloc_strdup(ctx, "*");
1546         } else {
1547                 TALLOC_FREE(state->spnego_mech);
1548                 TALLOC_FREE(state->spnego_mech_oid);
1549                 data_blob_free(&raw_out_token);
1550                 response.negTokenTarg.negResult = SPNEGO_REJECT;
1551                 reply_code = "NA";
1552                 reply_argument = talloc_strdup(ctx, nt_errstr(status));
1553         }
1554
1555         if (!reply_argument) {
1556                 DEBUG(1, ("Could not write SPNEGO data blob\n"));
1557                 x_fprintf(x_stdout, "BH Could not write SPNEGO data blob\n");
1558                 spnego_free_data(&response);
1559                 return;
1560         }
1561
1562         len = spnego_write_data(ctx, &token, &response);
1563         spnego_free_data(&response);
1564
1565         if (len == -1) {
1566                 DEBUG(1, ("Could not write SPNEGO data blob\n"));
1567                 x_fprintf(x_stdout, "BH Could not write SPNEGO data blob\n");
1568                 return;
1569         }
1570
1571         reply_base64 = base64_encode_data_blob(talloc_tos(), token);
1572
1573         x_fprintf(x_stdout, "%s %s %s\n",
1574                   reply_code, reply_base64, reply_argument);
1575
1576         TALLOC_FREE(reply_base64);
1577         data_blob_free(&token);
1578
1579         return;
1580 }
1581
1582 static struct ntlmssp_state *client_ntlmssp_state = NULL;
1583
1584 static bool manage_client_ntlmssp_init(struct spnego_data spnego)
1585 {
1586         NTSTATUS status;
1587         DATA_BLOB null_blob = data_blob_null;
1588         DATA_BLOB to_server;
1589         char *to_server_base64;
1590         const char *my_mechs[] = {OID_NTLMSSP, NULL};
1591         TALLOC_CTX *ctx = talloc_tos();
1592
1593         DEBUG(10, ("Got spnego negTokenInit with NTLMSSP\n"));
1594
1595         if (client_ntlmssp_state != NULL) {
1596                 DEBUG(1, ("Request for initial SPNEGO request where "
1597                           "we already have a state\n"));
1598                 return False;
1599         }
1600
1601         if (!client_ntlmssp_state) {
1602                 if (!NT_STATUS_IS_OK(status = ntlm_auth_start_ntlmssp_client(&client_ntlmssp_state))) {
1603                         x_fprintf(x_stdout, "BH %s\n", nt_errstr(status));
1604                         return False;
1605                 }
1606         }
1607
1608
1609         if (opt_password == NULL) {
1610
1611                 /* Request a password from the calling process.  After
1612                    sending it, the calling process should retry with
1613                    the negTokenInit. */
1614
1615                 DEBUG(10, ("Requesting password\n"));
1616                 x_fprintf(x_stdout, "PW\n");
1617                 return True;
1618         }
1619
1620         spnego.type = SPNEGO_NEG_TOKEN_INIT;
1621         spnego.negTokenInit.mechTypes = my_mechs;
1622         spnego.negTokenInit.reqFlags = data_blob_null;
1623         spnego.negTokenInit.reqFlagsPadding = 0;
1624         spnego.negTokenInit.mechListMIC = null_blob;
1625
1626         status = ntlmssp_update(client_ntlmssp_state, null_blob,
1627                                        &spnego.negTokenInit.mechToken);
1628
1629         if ( !(NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED) ||
1630                         NT_STATUS_IS_OK(status)) ) {
1631                 DEBUG(1, ("Expected OK or MORE_PROCESSING_REQUIRED, got: %s\n",
1632                           nt_errstr(status)));
1633                 TALLOC_FREE(client_ntlmssp_state);
1634                 return False;
1635         }
1636
1637         spnego_write_data(ctx, &to_server, &spnego);
1638         data_blob_free(&spnego.negTokenInit.mechToken);
1639
1640         to_server_base64 = base64_encode_data_blob(talloc_tos(), to_server);
1641         data_blob_free(&to_server);
1642         x_fprintf(x_stdout, "KK %s\n", to_server_base64);
1643         TALLOC_FREE(to_server_base64);
1644         return True;
1645 }
1646
1647 static void manage_client_ntlmssp_targ(struct spnego_data spnego)
1648 {
1649         NTSTATUS status;
1650         DATA_BLOB null_blob = data_blob_null;
1651         DATA_BLOB request;
1652         DATA_BLOB to_server;
1653         char *to_server_base64;
1654         TALLOC_CTX *ctx = talloc_tos();
1655
1656         DEBUG(10, ("Got spnego negTokenTarg with NTLMSSP\n"));
1657
1658         if (client_ntlmssp_state == NULL) {
1659                 DEBUG(1, ("Got NTLMSSP tArg without a client state\n"));
1660                 x_fprintf(x_stdout, "BH Got NTLMSSP tArg without a client state\n");
1661                 return;
1662         }
1663
1664         if (spnego.negTokenTarg.negResult == SPNEGO_REJECT) {
1665                 x_fprintf(x_stdout, "NA\n");
1666                 TALLOC_FREE(client_ntlmssp_state);
1667                 return;
1668         }
1669
1670         if (spnego.negTokenTarg.negResult == SPNEGO_ACCEPT_COMPLETED) {
1671                 x_fprintf(x_stdout, "AF\n");
1672                 TALLOC_FREE(client_ntlmssp_state);
1673                 return;
1674         }
1675
1676         status = ntlmssp_update(client_ntlmssp_state,
1677                                        spnego.negTokenTarg.responseToken,
1678                                        &request);
1679
1680         if (!NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
1681                 DEBUG(1, ("Expected MORE_PROCESSING_REQUIRED from "
1682                           "ntlmssp_client_update, got: %s\n",
1683                           nt_errstr(status)));
1684                 x_fprintf(x_stdout, "BH Expected MORE_PROCESSING_REQUIRED from "
1685                                     "ntlmssp_client_update\n");
1686                 data_blob_free(&request);
1687                 TALLOC_FREE(client_ntlmssp_state);
1688                 return;
1689         }
1690
1691         spnego.type = SPNEGO_NEG_TOKEN_TARG;
1692         spnego.negTokenTarg.negResult = SPNEGO_ACCEPT_INCOMPLETE;
1693         spnego.negTokenTarg.supportedMech = (const char *)OID_NTLMSSP;
1694         spnego.negTokenTarg.responseToken = request;
1695         spnego.negTokenTarg.mechListMIC = null_blob;
1696
1697         spnego_write_data(ctx, &to_server, &spnego);
1698         data_blob_free(&request);
1699
1700         to_server_base64 = base64_encode_data_blob(talloc_tos(), to_server);
1701         data_blob_free(&to_server);
1702         x_fprintf(x_stdout, "KK %s\n", to_server_base64);
1703         TALLOC_FREE(to_server_base64);
1704         return;
1705 }
1706
1707 #ifdef HAVE_KRB5
1708
1709 static bool manage_client_krb5_init(struct spnego_data spnego)
1710 {
1711         char *principal;
1712         DATA_BLOB tkt, to_server;
1713         DATA_BLOB session_key_krb5 = data_blob_null;
1714         struct spnego_data reply;
1715         char *reply_base64;
1716         int retval;
1717
1718         const char *my_mechs[] = {OID_KERBEROS5_OLD, NULL};
1719         ssize_t len;
1720         TALLOC_CTX *ctx = talloc_tos();
1721
1722         if ( (spnego.negTokenInit.mechListMIC.data == NULL) ||
1723              (spnego.negTokenInit.mechListMIC.length == 0) ) {
1724                 DEBUG(1, ("Did not get a principal for krb5\n"));
1725                 return False;
1726         }
1727
1728         principal = (char *)SMB_MALLOC(
1729                 spnego.negTokenInit.mechListMIC.length+1);
1730
1731         if (principal == NULL) {
1732                 DEBUG(1, ("Could not malloc principal\n"));
1733                 return False;
1734         }
1735
1736         memcpy(principal, spnego.negTokenInit.mechListMIC.data,
1737                spnego.negTokenInit.mechListMIC.length);
1738         principal[spnego.negTokenInit.mechListMIC.length] = '\0';
1739
1740         retval = cli_krb5_get_ticket(ctx, principal, 0,
1741                                           &tkt, &session_key_krb5,
1742                                           0, NULL, NULL, NULL);
1743         if (retval) {
1744                 char *user = NULL;
1745
1746                 /* Let's try to first get the TGT, for that we need a
1747                    password. */
1748
1749                 if (opt_password == NULL) {
1750                         DEBUG(10, ("Requesting password\n"));
1751                         x_fprintf(x_stdout, "PW\n");
1752                         return True;
1753                 }
1754
1755                 user = talloc_asprintf(talloc_tos(), "%s@%s", opt_username, opt_domain);
1756                 if (!user) {
1757                         return false;
1758                 }
1759
1760                 if ((retval = kerberos_kinit_password(user, opt_password, 0, NULL))) {
1761                         DEBUG(10, ("Requesting TGT failed: %s\n", error_message(retval)));
1762                         return False;
1763                 }
1764
1765                 retval = cli_krb5_get_ticket(ctx, principal, 0,
1766                                                   &tkt, &session_key_krb5,
1767                                                   0, NULL, NULL, NULL);
1768                 if (retval) {
1769                         DEBUG(10, ("Kinit suceeded, but getting a ticket failed: %s\n", error_message(retval)));
1770                         return False;
1771                 }
1772         }
1773
1774         data_blob_free(&session_key_krb5);
1775
1776         ZERO_STRUCT(reply);
1777
1778         reply.type = SPNEGO_NEG_TOKEN_INIT;
1779         reply.negTokenInit.mechTypes = my_mechs;
1780         reply.negTokenInit.reqFlags = data_blob_null;
1781         reply.negTokenInit.reqFlagsPadding = 0;
1782         reply.negTokenInit.mechToken = tkt;
1783         reply.negTokenInit.mechListMIC = data_blob_null;
1784
1785         len = spnego_write_data(ctx, &to_server, &reply);
1786         data_blob_free(&tkt);
1787
1788         if (len == -1) {
1789                 DEBUG(1, ("Could not write SPNEGO data blob\n"));
1790                 return False;
1791         }
1792
1793         reply_base64 = base64_encode_data_blob(talloc_tos(), to_server);
1794         x_fprintf(x_stdout, "KK %s *\n", reply_base64);
1795
1796         TALLOC_FREE(reply_base64);
1797         data_blob_free(&to_server);
1798         DEBUG(10, ("sent GSS-SPNEGO KERBEROS5 negTokenInit\n"));
1799         return True;
1800 }
1801
1802 static void manage_client_krb5_targ(struct spnego_data spnego)
1803 {
1804         switch (spnego.negTokenTarg.negResult) {
1805         case SPNEGO_ACCEPT_INCOMPLETE:
1806                 DEBUG(1, ("Got a Kerberos negTokenTarg with ACCEPT_INCOMPLETE\n"));
1807                 x_fprintf(x_stdout, "BH Got a Kerberos negTokenTarg with "
1808                                     "ACCEPT_INCOMPLETE\n");
1809                 break;
1810         case SPNEGO_ACCEPT_COMPLETED:
1811                 DEBUG(10, ("Accept completed\n"));
1812                 x_fprintf(x_stdout, "AF\n");
1813                 break;
1814         case SPNEGO_REJECT:
1815                 DEBUG(10, ("Rejected\n"));
1816                 x_fprintf(x_stdout, "NA\n");
1817                 break;
1818         default:
1819                 DEBUG(1, ("Got an invalid negTokenTarg\n"));
1820                 x_fprintf(x_stdout, "AF\n");
1821         }
1822 }
1823
1824 #endif
1825
1826 static void manage_gss_spnego_client_request(struct ntlm_auth_state *state,
1827                                                 char *buf, int length)
1828 {
1829         DATA_BLOB request;
1830         struct spnego_data spnego;
1831         ssize_t len;
1832         TALLOC_CTX *ctx = talloc_tos();
1833
1834         if (!opt_username || !*opt_username) {
1835                 x_fprintf(x_stderr, "username must be specified!\n\n");
1836                 exit(1);
1837         }
1838
1839         if (strlen(buf) <= 3) {
1840                 DEBUG(1, ("SPNEGO query [%s] too short\n", buf));
1841                 x_fprintf(x_stdout, "BH SPNEGO query too short\n");
1842                 return;
1843         }
1844
1845         request = base64_decode_data_blob(buf+3);
1846
1847         if (strncmp(buf, "PW ", 3) == 0) {
1848
1849                 /* We asked for a password and obviously got it :-) */
1850
1851                 opt_password = SMB_STRNDUP((const char *)request.data, request.length);
1852
1853                 if (opt_password == NULL) {
1854                         DEBUG(1, ("Out of memory\n"));
1855                         x_fprintf(x_stdout, "BH Out of memory\n");
1856                         data_blob_free(&request);
1857                         return;
1858                 }
1859
1860                 x_fprintf(x_stdout, "OK\n");
1861                 data_blob_free(&request);
1862                 return;
1863         }
1864
1865         if ( (strncmp(buf, "TT ", 3) != 0) &&
1866              (strncmp(buf, "AF ", 3) != 0) &&
1867              (strncmp(buf, "NA ", 3) != 0) ) {
1868                 DEBUG(1, ("SPNEGO request [%s] invalid\n", buf));
1869                 x_fprintf(x_stdout, "BH SPNEGO request invalid\n");
1870                 data_blob_free(&request);
1871                 return;
1872         }
1873
1874         /* So we got a server challenge to generate a SPNEGO
1875            client-to-server request... */
1876
1877         len = spnego_read_data(ctx, request, &spnego);
1878         data_blob_free(&request);
1879
1880         if (len == -1) {
1881                 DEBUG(1, ("Could not read SPNEGO data for [%s]\n", buf));
1882                 x_fprintf(x_stdout, "BH Could not read SPNEGO data\n");
1883                 return;
1884         }
1885
1886         if (spnego.type == SPNEGO_NEG_TOKEN_INIT) {
1887
1888                 /* The server offers a list of mechanisms */
1889
1890                 const char **mechType = (const char **)spnego.negTokenInit.mechTypes;
1891
1892                 while (*mechType != NULL) {
1893
1894 #ifdef HAVE_KRB5
1895                         if ( (strcmp(*mechType, OID_KERBEROS5_OLD) == 0) ||
1896                              (strcmp(*mechType, OID_KERBEROS5) == 0) ) {
1897                                 if (manage_client_krb5_init(spnego))
1898                                         goto out;
1899                         }
1900 #endif
1901
1902                         if (strcmp(*mechType, OID_NTLMSSP) == 0) {
1903                                 if (manage_client_ntlmssp_init(spnego))
1904                                         goto out;
1905                         }
1906
1907                         mechType++;
1908                 }
1909
1910                 DEBUG(1, ("Server offered no compatible mechanism\n"));
1911                 x_fprintf(x_stdout, "BH Server offered no compatible mechanism\n");
1912                 return;
1913         }
1914
1915         if (spnego.type == SPNEGO_NEG_TOKEN_TARG) {
1916
1917                 if (spnego.negTokenTarg.supportedMech == NULL) {
1918                         /* On accept/reject Windows does not send the
1919                            mechanism anymore. Handle that here and
1920                            shut down the mechanisms. */
1921
1922                         switch (spnego.negTokenTarg.negResult) {
1923                         case SPNEGO_ACCEPT_COMPLETED:
1924                                 x_fprintf(x_stdout, "AF\n");
1925                                 break;
1926                         case SPNEGO_REJECT:
1927                                 x_fprintf(x_stdout, "NA\n");
1928                                 break;
1929                         default:
1930                                 DEBUG(1, ("Got a negTokenTarg with no mech and an "
1931                                           "unknown negResult: %d\n",
1932                                           spnego.negTokenTarg.negResult));
1933                                 x_fprintf(x_stdout, "BH Got a negTokenTarg with"
1934                                                     " no mech and an unknown "
1935                                                     "negResult\n");
1936                         }
1937
1938                         TALLOC_FREE(client_ntlmssp_state);
1939                         goto out;
1940                 }
1941
1942                 if (strcmp(spnego.negTokenTarg.supportedMech,
1943                            OID_NTLMSSP) == 0) {
1944                         manage_client_ntlmssp_targ(spnego);
1945                         goto out;
1946                 }
1947
1948 #if HAVE_KRB5
1949                 if (strcmp(spnego.negTokenTarg.supportedMech,
1950                            OID_KERBEROS5_OLD) == 0) {
1951                         manage_client_krb5_targ(spnego);
1952                         goto out;
1953                 }
1954 #endif
1955
1956         }
1957
1958         DEBUG(1, ("Got an SPNEGO token I could not handle [%s]!\n", buf));
1959         x_fprintf(x_stdout, "BH Got an SPNEGO token I could not handle\n");
1960         return;
1961
1962  out:
1963         spnego_free_data(&spnego);
1964         return;
1965 }
1966
1967 static void manage_ntlm_server_1_request(struct ntlm_auth_state *state,
1968                                                 char *buf, int length)
1969 {
1970         char *request, *parameter;      
1971         static DATA_BLOB challenge;
1972         static DATA_BLOB lm_response;
1973         static DATA_BLOB nt_response;
1974         static char *full_username;
1975         static char *username;
1976         static char *domain;
1977         static char *plaintext_password;
1978         static bool ntlm_server_1_user_session_key;
1979         static bool ntlm_server_1_lm_session_key;
1980
1981         if (strequal(buf, ".")) {
1982                 if (!full_username && !username) {      
1983                         x_fprintf(x_stdout, "Error: No username supplied!\n");
1984                 } else if (plaintext_password) {
1985                         /* handle this request as plaintext */
1986                         if (!full_username) {
1987                                 if (asprintf(&full_username, "%s%c%s", domain, winbind_separator(), username) == -1) {
1988                                         x_fprintf(x_stdout, "Error: Out of memory in asprintf!\n.\n");
1989                                         return;
1990                                 }
1991                         }
1992                         if (check_plaintext_auth(full_username, plaintext_password, False)) {
1993                                 x_fprintf(x_stdout, "Authenticated: Yes\n");
1994                         } else {
1995                                 x_fprintf(x_stdout, "Authenticated: No\n");
1996                         }
1997                 } else if (!lm_response.data && !nt_response.data) {
1998                         x_fprintf(x_stdout, "Error: No password supplied!\n");
1999                 } else if (!challenge.data) {   
2000                         x_fprintf(x_stdout, "Error: No lanman-challenge supplied!\n");
2001                 } else {
2002                         char *error_string = NULL;
2003                         uchar lm_key[8];
2004                         uchar user_session_key[16];
2005                         uint32 flags = 0;
2006
2007                         if (full_username && !username) {
2008                                 fstring fstr_user;
2009                                 fstring fstr_domain;
2010
2011                                 if (!parse_ntlm_auth_domain_user(full_username, fstr_user, fstr_domain)) {
2012                                         /* username might be 'tainted', don't print into our new-line deleimianted stream */
2013                                         x_fprintf(x_stdout, "Error: Could not parse into domain and username\n");
2014                                 }
2015                                 SAFE_FREE(username);
2016                                 SAFE_FREE(domain);
2017                                 username = smb_xstrdup(fstr_user);
2018                                 domain = smb_xstrdup(fstr_domain);
2019                         }
2020
2021                         if (!domain) {
2022                                 domain = smb_xstrdup(get_winbind_domain());
2023                         }
2024
2025                         if (ntlm_server_1_lm_session_key) 
2026                                 flags |= WBFLAG_PAM_LMKEY;
2027
2028                         if (ntlm_server_1_user_session_key) 
2029                                 flags |= WBFLAG_PAM_USER_SESSION_KEY;
2030
2031                         if (!NT_STATUS_IS_OK(
2032                                     contact_winbind_auth_crap(username, 
2033                                                               domain, 
2034                                                               lp_netbios_name(),
2035                                                               &challenge, 
2036                                                               &lm_response, 
2037                                                               &nt_response, 
2038                                                               flags, 0,
2039                                                               lm_key, 
2040                                                               user_session_key,
2041                                                               &error_string,
2042                                                               NULL))) {
2043
2044                                 x_fprintf(x_stdout, "Authenticated: No\n");
2045                                 x_fprintf(x_stdout, "Authentication-Error: %s\n.\n", error_string);
2046                         } else {
2047                                 static char zeros[16];
2048                                 char *hex_lm_key;
2049                                 char *hex_user_session_key;
2050
2051                                 x_fprintf(x_stdout, "Authenticated: Yes\n");
2052
2053                                 if (ntlm_server_1_lm_session_key 
2054                                     && (memcmp(zeros, lm_key, 
2055                                                sizeof(lm_key)) != 0)) {
2056                                         hex_lm_key = hex_encode_talloc(NULL,
2057                                                                 (const unsigned char *)lm_key,
2058                                                                 sizeof(lm_key));
2059                                         x_fprintf(x_stdout, "LANMAN-Session-Key: %s\n", hex_lm_key);
2060                                         TALLOC_FREE(hex_lm_key);
2061                                 }
2062
2063                                 if (ntlm_server_1_user_session_key 
2064                                     && (memcmp(zeros, user_session_key, 
2065                                                sizeof(user_session_key)) != 0)) {
2066                                         hex_user_session_key = hex_encode_talloc(NULL,
2067                                                                           (const unsigned char *)user_session_key, 
2068                                                                           sizeof(user_session_key));
2069                                         x_fprintf(x_stdout, "User-Session-Key: %s\n", hex_user_session_key);
2070                                         TALLOC_FREE(hex_user_session_key);
2071                                 }
2072                         }
2073                         SAFE_FREE(error_string);
2074                 }
2075                 /* clear out the state */
2076                 challenge = data_blob_null;
2077                 nt_response = data_blob_null;
2078                 lm_response = data_blob_null;
2079                 SAFE_FREE(full_username);
2080                 SAFE_FREE(username);
2081                 SAFE_FREE(domain);
2082                 SAFE_FREE(plaintext_password);
2083                 ntlm_server_1_user_session_key = False;
2084                 ntlm_server_1_lm_session_key = False;
2085                 x_fprintf(x_stdout, ".\n");
2086
2087                 return;
2088         }
2089
2090         request = buf;
2091
2092         /* Indicates a base64 encoded structure */
2093         parameter = strstr_m(request, ":: ");
2094         if (!parameter) {
2095                 parameter = strstr_m(request, ": ");
2096
2097                 if (!parameter) {
2098                         DEBUG(0, ("Parameter not found!\n"));
2099                         x_fprintf(x_stdout, "Error: Parameter not found!\n.\n");
2100                         return;
2101                 }
2102
2103                 parameter[0] ='\0';
2104                 parameter++;
2105                 parameter[0] ='\0';
2106                 parameter++;
2107
2108         } else {
2109                 parameter[0] ='\0';
2110                 parameter++;
2111                 parameter[0] ='\0';
2112                 parameter++;
2113                 parameter[0] ='\0';
2114                 parameter++;
2115
2116                 base64_decode_inplace(parameter);
2117         }
2118
2119         if (strequal(request, "LANMAN-Challenge")) {
2120                 challenge = strhex_to_data_blob(NULL, parameter);
2121                 if (challenge.length != 8) {
2122                         x_fprintf(x_stdout, "Error: hex decode of %s failed! (got %d bytes, expected 8)\n.\n", 
2123                                   parameter,
2124                                   (int)challenge.length);
2125                         challenge = data_blob_null;
2126                 }
2127         } else if (strequal(request, "NT-Response")) {
2128                 nt_response = strhex_to_data_blob(NULL, parameter);
2129                 if (nt_response.length < 24) {
2130                         x_fprintf(x_stdout, "Error: hex decode of %s failed! (only got %d bytes, needed at least 24)\n.\n", 
2131                                   parameter,
2132                                   (int)nt_response.length);
2133                         nt_response = data_blob_null;
2134                 }
2135         } else if (strequal(request, "LANMAN-Response")) {
2136                 lm_response = strhex_to_data_blob(NULL, parameter);
2137                 if (lm_response.length != 24) {
2138                         x_fprintf(x_stdout, "Error: hex decode of %s failed! (got %d bytes, expected 24)\n.\n", 
2139                                   parameter,
2140                                   (int)lm_response.length);
2141                         lm_response = data_blob_null;
2142                 }
2143         } else if (strequal(request, "Password")) {
2144                 plaintext_password = smb_xstrdup(parameter);
2145         } else if (strequal(request, "NT-Domain")) {
2146                 domain = smb_xstrdup(parameter);
2147         } else if (strequal(request, "Username")) {
2148                 username = smb_xstrdup(parameter);
2149         } else if (strequal(request, "Full-Username")) {
2150                 full_username = smb_xstrdup(parameter);
2151         } else if (strequal(request, "Request-User-Session-Key")) {
2152                 ntlm_server_1_user_session_key = strequal(parameter, "Yes");
2153         } else if (strequal(request, "Request-LanMan-Session-Key")) {
2154                 ntlm_server_1_lm_session_key = strequal(parameter, "Yes");
2155         } else {
2156                 x_fprintf(x_stdout, "Error: Unknown request %s\n.\n", request);
2157         }
2158 }
2159
2160 static void manage_ntlm_change_password_1_request(struct ntlm_auth_state *state,
2161                                                         char *buf, int length)
2162 {
2163         char *request, *parameter;      
2164         static DATA_BLOB new_nt_pswd;
2165         static DATA_BLOB old_nt_hash_enc;
2166         static DATA_BLOB new_lm_pswd;
2167         static DATA_BLOB old_lm_hash_enc;
2168         static char *full_username = NULL;
2169         static char *username = NULL;
2170         static char *domain = NULL;
2171         static char *newpswd =  NULL;
2172         static char *oldpswd = NULL;
2173
2174         if (strequal(buf, ".")) {
2175                 if(newpswd && oldpswd) {
2176                         uchar old_nt_hash[16];
2177                         uchar old_lm_hash[16];
2178                         uchar new_nt_hash[16];
2179                         uchar new_lm_hash[16];
2180
2181                         new_nt_pswd = data_blob(NULL, 516);
2182                         old_nt_hash_enc = data_blob(NULL, 16);
2183
2184                         /* Calculate the MD4 hash (NT compatible) of the
2185                          * password */
2186                         E_md4hash(oldpswd, old_nt_hash);
2187                         E_md4hash(newpswd, new_nt_hash);
2188
2189                         /* E_deshash returns false for 'long'
2190                            passwords (> 14 DOS chars).  
2191
2192                            Therefore, don't send a buffer
2193                            encrypted with the truncated hash
2194                            (it could allow an even easier
2195                            attack on the password)
2196
2197                            Likewise, obey the admin's restriction
2198                         */
2199
2200                         if (lp_client_lanman_auth() &&
2201                             E_deshash(newpswd, new_lm_hash) &&
2202                             E_deshash(oldpswd, old_lm_hash)) {
2203                                 new_lm_pswd = data_blob(NULL, 516);
2204                                 old_lm_hash_enc = data_blob(NULL, 16);
2205                                 encode_pw_buffer(new_lm_pswd.data, newpswd,
2206                                                  STR_UNICODE);
2207
2208                                 arcfour_crypt(new_lm_pswd.data, old_nt_hash, 516);
2209                                 E_old_pw_hash(new_nt_hash, old_lm_hash,
2210                                               old_lm_hash_enc.data);
2211                         } else {
2212                                 new_lm_pswd.data = NULL;
2213                                 new_lm_pswd.length = 0;
2214                                 old_lm_hash_enc.data = NULL;
2215                                 old_lm_hash_enc.length = 0;
2216                         }
2217
2218                         encode_pw_buffer(new_nt_pswd.data, newpswd,
2219                                          STR_UNICODE);
2220
2221                         arcfour_crypt(new_nt_pswd.data, old_nt_hash, 516);
2222                         E_old_pw_hash(new_nt_hash, old_nt_hash,
2223                                       old_nt_hash_enc.data);
2224                 }
2225
2226                 if (!full_username && !username) {      
2227                         x_fprintf(x_stdout, "Error: No username supplied!\n");
2228                 } else if ((!new_nt_pswd.data || !old_nt_hash_enc.data) &&
2229                            (!new_lm_pswd.data || old_lm_hash_enc.data) ) {
2230                         x_fprintf(x_stdout, "Error: No NT or LM password "
2231                                   "blobs supplied!\n");
2232                 } else {
2233                         char *error_string = NULL;
2234
2235                         if (full_username && !username) {
2236                                 fstring fstr_user;
2237                                 fstring fstr_domain;
2238
2239                                 if (!parse_ntlm_auth_domain_user(full_username,
2240                                                                  fstr_user,
2241                                                                  fstr_domain)) {
2242                                         /* username might be 'tainted', don't
2243                                          * print into our new-line
2244                                          * deleimianted stream */
2245                                         x_fprintf(x_stdout, "Error: Could not "
2246                                                   "parse into domain and "
2247                                                   "username\n");
2248                                         SAFE_FREE(username);
2249                                         username = smb_xstrdup(full_username);
2250                                 } else {
2251                                         SAFE_FREE(username);
2252                                         SAFE_FREE(domain);
2253                                         username = smb_xstrdup(fstr_user);
2254                                         domain = smb_xstrdup(fstr_domain);
2255                                 }
2256
2257                         }
2258
2259                         if(!NT_STATUS_IS_OK(contact_winbind_change_pswd_auth_crap(
2260                                                     username, domain,
2261                                                     new_nt_pswd,
2262                                                     old_nt_hash_enc,
2263                                                     new_lm_pswd,
2264                                                     old_lm_hash_enc,
2265                                                     &error_string))) {
2266                                 x_fprintf(x_stdout, "Password-Change: No\n");
2267                                 x_fprintf(x_stdout, "Password-Change-Error: "
2268                                           "%s\n.\n", error_string);
2269                         } else {
2270                                 x_fprintf(x_stdout, "Password-Change: Yes\n");
2271                         }
2272
2273                         SAFE_FREE(error_string);
2274                 }
2275                 /* clear out the state */
2276                 new_nt_pswd = data_blob_null;
2277                 old_nt_hash_enc = data_blob_null;
2278                 new_lm_pswd = data_blob_null;
2279                 old_nt_hash_enc = data_blob_null;
2280                 SAFE_FREE(full_username);
2281                 SAFE_FREE(username);
2282                 SAFE_FREE(domain);
2283                 SAFE_FREE(newpswd);
2284                 SAFE_FREE(oldpswd);
2285                 x_fprintf(x_stdout, ".\n");
2286
2287                 return;
2288         }
2289
2290         request = buf;
2291
2292         /* Indicates a base64 encoded structure */
2293         parameter = strstr_m(request, ":: ");
2294         if (!parameter) {
2295                 parameter = strstr_m(request, ": ");
2296
2297                 if (!parameter) {
2298                         DEBUG(0, ("Parameter not found!\n"));
2299                         x_fprintf(x_stdout, "Error: Parameter not found!\n.\n");
2300                         return;
2301                 }
2302
2303                 parameter[0] ='\0';
2304                 parameter++;
2305                 parameter[0] ='\0';
2306                 parameter++;
2307         } else {
2308                 parameter[0] ='\0';
2309                 parameter++;
2310                 parameter[0] ='\0';
2311                 parameter++;
2312                 parameter[0] ='\0';
2313                 parameter++;
2314
2315                 base64_decode_inplace(parameter);
2316         }
2317
2318         if (strequal(request, "new-nt-password-blob")) {
2319                 new_nt_pswd = strhex_to_data_blob(NULL, parameter);
2320                 if (new_nt_pswd.length != 516) {
2321                         x_fprintf(x_stdout, "Error: hex decode of %s failed! "
2322                                   "(got %d bytes, expected 516)\n.\n", 
2323                                   parameter,
2324                                   (int)new_nt_pswd.length);
2325                         new_nt_pswd = data_blob_null;
2326                 }
2327         } else if (strequal(request, "old-nt-hash-blob")) {
2328                 old_nt_hash_enc = strhex_to_data_blob(NULL, parameter);
2329                 if (old_nt_hash_enc.length != 16) {
2330                         x_fprintf(x_stdout, "Error: hex decode of %s failed! "
2331                                   "(got %d bytes, expected 16)\n.\n", 
2332                                   parameter,
2333                                   (int)old_nt_hash_enc.length);
2334                         old_nt_hash_enc = data_blob_null;
2335                 }
2336         } else if (strequal(request, "new-lm-password-blob")) {
2337                 new_lm_pswd = strhex_to_data_blob(NULL, parameter);
2338                 if (new_lm_pswd.length != 516) {
2339                         x_fprintf(x_stdout, "Error: hex decode of %s failed! "
2340                                   "(got %d bytes, expected 516)\n.\n", 
2341                                   parameter,
2342                                   (int)new_lm_pswd.length);
2343                         new_lm_pswd = data_blob_null;
2344                 }
2345         }
2346         else if (strequal(request, "old-lm-hash-blob")) {
2347                 old_lm_hash_enc = strhex_to_data_blob(NULL, parameter);
2348                 if (old_lm_hash_enc.length != 16)
2349                 {
2350                         x_fprintf(x_stdout, "Error: hex decode of %s failed! "
2351                                   "(got %d bytes, expected 16)\n.\n", 
2352                                   parameter,
2353                                   (int)old_lm_hash_enc.length);
2354                         old_lm_hash_enc = data_blob_null;
2355                 }
2356         } else if (strequal(request, "nt-domain")) {
2357                 domain = smb_xstrdup(parameter);
2358         } else if(strequal(request, "username")) {
2359                 username = smb_xstrdup(parameter);
2360         } else if(strequal(request, "full-username")) {
2361                 username = smb_xstrdup(parameter);
2362         } else if(strequal(request, "new-password")) {
2363                 newpswd = smb_xstrdup(parameter);
2364         } else if (strequal(request, "old-password")) {
2365                 oldpswd = smb_xstrdup(parameter);
2366         } else {
2367                 x_fprintf(x_stdout, "Error: Unknown request %s\n.\n", request);
2368         }
2369 }
2370
2371 static void manage_squid_request(struct ntlm_auth_state *state,
2372                 stdio_helper_function fn)
2373 {
2374         char *buf;
2375         char tmp[INITIAL_BUFFER_SIZE+1];
2376         int length, buf_size = 0;
2377         char *c;
2378
2379         buf = talloc_strdup(state->mem_ctx, "");
2380         if (!buf) {
2381                 DEBUG(0, ("Failed to allocate input buffer.\n"));
2382                 x_fprintf(x_stderr, "ERR\n");
2383                 exit(1);
2384         }
2385
2386         do {
2387
2388                 /* this is not a typo - x_fgets doesn't work too well under
2389                  * squid */
2390                 if (fgets(tmp, sizeof(tmp)-1, stdin) == NULL) {
2391                         if (ferror(stdin)) {
2392                                 DEBUG(1, ("fgets() failed! dying..... errno=%d "
2393                                           "(%s)\n", ferror(stdin),
2394                                           strerror(ferror(stdin))));
2395
2396                                 exit(1);
2397                         }
2398                         exit(0);
2399                 }
2400
2401                 buf = talloc_strdup_append_buffer(buf, tmp);
2402                 buf_size += INITIAL_BUFFER_SIZE;
2403
2404                 if (buf_size > MAX_BUFFER_SIZE) {
2405                         DEBUG(2, ("Oversized message\n"));
2406                         x_fprintf(x_stderr, "ERR\n");
2407                         talloc_free(buf);
2408                         return;
2409                 }
2410
2411                 c = strchr(buf, '\n');
2412         } while (c == NULL);
2413
2414         *c = '\0';
2415         length = c-buf;
2416
2417         DEBUG(10, ("Got '%s' from squid (length: %d).\n",buf,length));
2418
2419         if (buf[0] == '\0') {
2420                 DEBUG(2, ("Invalid Request\n"));
2421                 x_fprintf(x_stderr, "ERR\n");
2422                 talloc_free(buf);
2423                 return;
2424         }
2425
2426         fn(state, buf, length);
2427         talloc_free(buf);
2428 }
2429
2430
2431 static void squid_stream(enum stdio_helper_mode stdio_mode, stdio_helper_function fn) {
2432         TALLOC_CTX *mem_ctx;
2433         struct ntlm_auth_state *state;
2434
2435         /* initialize FDescs */
2436         x_setbuf(x_stdout, NULL);
2437         x_setbuf(x_stderr, NULL);
2438
2439         mem_ctx = talloc_init("ntlm_auth");
2440         if (!mem_ctx) {
2441                 DEBUG(0, ("squid_stream: Failed to create talloc context\n"));
2442                 x_fprintf(x_stderr, "ERR\n");
2443                 exit(1);
2444         }
2445
2446         state = talloc_zero(mem_ctx, struct ntlm_auth_state);
2447         if (!state) {
2448                 DEBUG(0, ("squid_stream: Failed to talloc ntlm_auth_state\n"));
2449                 x_fprintf(x_stderr, "ERR\n");
2450                 exit(1);
2451         }
2452
2453         state->mem_ctx = mem_ctx;
2454         state->helper_mode = stdio_mode;
2455
2456         while(1) {
2457                 TALLOC_CTX *frame = talloc_stackframe();
2458                 manage_squid_request(state, fn);
2459                 TALLOC_FREE(frame);
2460         }
2461 }
2462
2463
2464 /* Authenticate a user with a challenge/response */
2465
2466 static bool check_auth_crap(void)
2467 {
2468         NTSTATUS nt_status;
2469         uint32 flags = 0;
2470         char lm_key[8];
2471         char user_session_key[16];
2472         char *hex_lm_key;
2473         char *hex_user_session_key;
2474         char *error_string;
2475         static uint8 zeros[16];
2476
2477         x_setbuf(x_stdout, NULL);
2478
2479         if (request_lm_key) 
2480                 flags |= WBFLAG_PAM_LMKEY;
2481
2482         if (request_user_session_key) 
2483                 flags |= WBFLAG_PAM_USER_SESSION_KEY;
2484
2485         flags |= WBFLAG_PAM_NT_STATUS_SQUASH;
2486
2487         nt_status = contact_winbind_auth_crap(opt_username, opt_domain, 
2488                                               opt_workstation,
2489                                               &opt_challenge, 
2490                                               &opt_lm_response, 
2491                                               &opt_nt_response, 
2492                                               flags, 0,
2493                                               (unsigned char *)lm_key, 
2494                                               (unsigned char *)user_session_key, 
2495                                               &error_string, NULL);
2496
2497         if (!NT_STATUS_IS_OK(nt_status)) {
2498                 x_fprintf(x_stdout, "%s (0x%x)\n", 
2499                           error_string,
2500                           NT_STATUS_V(nt_status));
2501                 SAFE_FREE(error_string);
2502                 return False;
2503         }
2504
2505         if (request_lm_key 
2506             && (memcmp(zeros, lm_key, 
2507                        sizeof(lm_key)) != 0)) {
2508                 hex_lm_key = hex_encode_talloc(talloc_tos(), (const unsigned char *)lm_key,
2509                                         sizeof(lm_key));
2510                 x_fprintf(x_stdout, "LM_KEY: %s\n", hex_lm_key);
2511                 TALLOC_FREE(hex_lm_key);
2512         }
2513         if (request_user_session_key 
2514             && (memcmp(zeros, user_session_key, 
2515                        sizeof(user_session_key)) != 0)) {
2516                 hex_user_session_key = hex_encode_talloc(talloc_tos(), (const unsigned char *)user_session_key, 
2517                                                   sizeof(user_session_key));
2518                 x_fprintf(x_stdout, "NT_KEY: %s\n", hex_user_session_key);
2519                 TALLOC_FREE(hex_user_session_key);
2520         }
2521
2522         return True;
2523 }
2524
2525 /* Main program */
2526
2527 enum {
2528         OPT_USERNAME = 1000,
2529         OPT_DOMAIN,
2530         OPT_WORKSTATION,
2531         OPT_CHALLENGE,
2532         OPT_RESPONSE,
2533         OPT_LM,
2534         OPT_NT,
2535         OPT_PASSWORD,
2536         OPT_LM_KEY,
2537         OPT_USER_SESSION_KEY,
2538         OPT_DIAGNOSTICS,
2539         OPT_REQUIRE_MEMBERSHIP,
2540         OPT_USE_CACHED_CREDS,
2541         OPT_PAM_WINBIND_CONF
2542 };
2543
2544  int main(int argc, const char **argv)
2545 {
2546         TALLOC_CTX *frame = talloc_stackframe();
2547         int opt;
2548         static const char *helper_protocol;
2549         static int diagnostics;
2550
2551         static const char *hex_challenge;
2552         static const char *hex_lm_response;
2553         static const char *hex_nt_response;
2554
2555         poptContext pc;
2556
2557         /* NOTE: DO NOT change this interface without considering the implications!
2558            This is an external interface, which other programs will use to interact 
2559            with this helper.
2560         */
2561
2562         /* We do not use single-letter command abbreviations, because they harm future 
2563            interface stability. */
2564
2565         struct poptOption long_options[] = {
2566                 POPT_AUTOHELP
2567                 { "helper-protocol", 0, POPT_ARG_STRING, &helper_protocol, OPT_DOMAIN, "operate as a stdio-based helper", "helper protocol to use"},
2568                 { "username", 0, POPT_ARG_STRING, &opt_username, OPT_USERNAME, "username"},
2569                 { "domain", 0, POPT_ARG_STRING, &opt_domain, OPT_DOMAIN, "domain name"},
2570                 { "workstation", 0, POPT_ARG_STRING, &opt_workstation, OPT_WORKSTATION, "workstation"},
2571                 { "challenge", 0, POPT_ARG_STRING, &hex_challenge, OPT_CHALLENGE, "challenge (HEX encoded)"},
2572                 { "lm-response", 0, POPT_ARG_STRING, &hex_lm_response, OPT_LM, "LM Response to the challenge (HEX encoded)"},
2573                 { "nt-response", 0, POPT_ARG_STRING, &hex_nt_response, OPT_NT, "NT or NTLMv2 Response to the challenge (HEX encoded)"},
2574                 { "password", 0, POPT_ARG_STRING, &opt_password, OPT_PASSWORD, "User's plaintext password"},            
2575                 { "request-lm-key", 0, POPT_ARG_NONE, &request_lm_key, OPT_LM_KEY, "Retrieve LM session key"},
2576                 { "request-nt-key", 0, POPT_ARG_NONE, &request_user_session_key, OPT_USER_SESSION_KEY, "Retrieve User (NT) session key"},
2577                 { "use-cached-creds", 0, POPT_ARG_NONE, &use_cached_creds, OPT_USE_CACHED_CREDS, "Use cached credentials if no password is given"},
2578                 { "diagnostics", 0, POPT_ARG_NONE, &diagnostics,
2579                   OPT_DIAGNOSTICS,
2580                   "Perform diagnostics on the authentication chain"},
2581                 { "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" },
2582                 { "pam-winbind-conf", 0, POPT_ARG_STRING, &opt_pam_winbind_conf, OPT_PAM_WINBIND_CONF, "Require that request must set WBFLAG_PAM_CONTACT_TRUSTDOM when krb5 auth is required" },
2583                 POPT_COMMON_CONFIGFILE
2584                 POPT_COMMON_VERSION
2585                 POPT_TABLEEND
2586         };
2587
2588         /* Samba client initialisation */
2589         load_case_tables();
2590
2591         setup_logging("ntlm_auth", DEBUG_STDERR);
2592
2593         /* Parse options */
2594
2595         pc = poptGetContext("ntlm_auth", argc, argv, long_options, 0);
2596
2597         /* Parse command line options */
2598
2599         if (argc == 1) {
2600                 poptPrintHelp(pc, stderr, 0);
2601                 return 1;
2602         }
2603
2604         while((opt = poptGetNextOpt(pc)) != -1) {
2605                 /* Get generic config options like --configfile */
2606         }
2607
2608         poptFreeContext(pc);
2609
2610         if (!lp_load_global(get_dyn_CONFIGFILE())) {
2611                 d_fprintf(stderr, "ntlm_auth: error opening config file %s. Error was %s\n",
2612                         get_dyn_CONFIGFILE(), strerror(errno));
2613                 exit(1);
2614         }
2615
2616         pc = poptGetContext(NULL, argc, (const char **)argv, long_options, 
2617                             POPT_CONTEXT_KEEP_FIRST);
2618
2619         while((opt = poptGetNextOpt(pc)) != -1) {
2620                 switch (opt) {
2621                 case OPT_CHALLENGE:
2622                         opt_challenge = strhex_to_data_blob(NULL, hex_challenge);
2623                         if (opt_challenge.length != 8) {
2624                                 x_fprintf(x_stderr, "hex decode of %s failed! (only got %d bytes)\n", 
2625                                           hex_challenge,
2626                                           (int)opt_challenge.length);
2627                                 exit(1);
2628                         }
2629                         break;
2630                 case OPT_LM: 
2631                         opt_lm_response = strhex_to_data_blob(NULL, hex_lm_response);
2632                         if (opt_lm_response.length != 24) {
2633                                 x_fprintf(x_stderr, "hex decode of %s failed! (only got %d bytes)\n", 
2634                                           hex_lm_response,
2635                                           (int)opt_lm_response.length);
2636                                 exit(1);
2637                         }
2638                         break;
2639
2640                 case OPT_NT: 
2641                         opt_nt_response = strhex_to_data_blob(NULL, hex_nt_response);
2642                         if (opt_nt_response.length < 24) {
2643                                 x_fprintf(x_stderr, "hex decode of %s failed! (only got %d bytes)\n", 
2644                                           hex_nt_response,
2645                                           (int)opt_nt_response.length);
2646                                 exit(1);
2647                         }
2648                         break;
2649
2650                 case OPT_REQUIRE_MEMBERSHIP:
2651                         if (strncasecmp_m("S-", require_membership_of, 2) == 0) {
2652                                 require_membership_of_sid = require_membership_of;
2653                         }
2654                         break;
2655                 }
2656         }
2657
2658         if (opt_username) {
2659                 char *domain = SMB_STRDUP(opt_username);
2660                 char *p = strchr_m(domain, *lp_winbind_separator());
2661                 if (p) {
2662                         opt_username = p+1;
2663                         *p = '\0';
2664                         if (opt_domain && !strequal(opt_domain, domain)) {
2665                                 x_fprintf(x_stderr, "Domain specified in username (%s) "
2666                                         "doesn't match specified domain (%s)!\n\n",
2667                                         domain, opt_domain);
2668                                 poptPrintHelp(pc, stderr, 0);
2669                                 exit(1);
2670                         }
2671                         opt_domain = domain;
2672                 } else {
2673                         SAFE_FREE(domain);
2674                 }
2675         }
2676
2677         /* Note: if opt_domain is "" then send no domain */
2678         if (opt_domain == NULL) {
2679                 opt_domain = get_winbind_domain();
2680         }
2681
2682         if (opt_workstation == NULL) {
2683                 opt_workstation = "";
2684         }
2685
2686         if (helper_protocol) {
2687                 int i;
2688                 for (i=0; i<NUM_HELPER_MODES; i++) {
2689                         if (strcmp(helper_protocol, stdio_helper_protocols[i].name) == 0) {
2690                                 squid_stream(stdio_helper_protocols[i].mode, stdio_helper_protocols[i].fn);
2691                                 exit(0);
2692                         }
2693                 }
2694                 x_fprintf(x_stderr, "unknown helper protocol [%s]\n\nValid helper protools:\n\n", helper_protocol);
2695
2696                 for (i=0; i<NUM_HELPER_MODES; i++) {
2697                         x_fprintf(x_stderr, "%s\n", stdio_helper_protocols[i].name);
2698                 }
2699
2700                 exit(1);
2701         }
2702
2703         if (!opt_username || !*opt_username) {
2704                 x_fprintf(x_stderr, "username must be specified!\n\n");
2705                 poptPrintHelp(pc, stderr, 0);
2706                 exit(1);
2707         }
2708
2709         if (opt_challenge.length) {
2710                 if (!check_auth_crap()) {
2711                         exit(1);
2712                 }
2713                 exit(0);
2714         } 
2715
2716         if (!opt_password) {
2717                 opt_password = getpass("password: ");
2718         }
2719
2720         if (diagnostics) {
2721                 if (!diagnose_ntlm_auth()) {
2722                         return 1;
2723                 }
2724         } else {
2725                 fstring user;
2726
2727                 fstr_sprintf(user, "%s%c%s", opt_domain, winbind_separator(), opt_username);
2728                 if (!check_plaintext_auth(user, opt_password, True)) {
2729                         return 1;
2730                 }
2731         }
2732
2733         /* Exit code */
2734
2735         poptFreeContext(pc);
2736         TALLOC_FREE(frame);
2737         return 0;
2738 }