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