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