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