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