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