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