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