9f0c839186c5044611ec22b0ac04365a4bbd26e9
[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                 DEBUG(10, ("Could not parse GSS-SPNEGO, trying raw "
1323                            "ntlmssp\n"));
1324
1325                 manage_squid_ntlmssp_request_int(state, buf, length,
1326                                                  talloc_tos(), &reply);
1327                 if (reply == NULL) {
1328                         x_fprintf(x_stdout, "BH Out of memory\n");
1329                         return;
1330                 }
1331
1332                 if (strncmp(reply, "AF ", 3) == 0) {
1333                         x_fprintf(x_stdout, "AF * %s\n", reply+3);
1334                 } else {
1335                         x_fprintf(x_stdout, "%s *\n", reply);
1336                 }
1337
1338                 TALLOC_FREE(reply);
1339                 return;
1340         }
1341
1342         len = spnego_read_data(ctx, token, &request);
1343         data_blob_free(&token);
1344
1345         if (len == -1) {
1346                 DEBUG(1, ("GSS-SPNEGO query [%s] invalid\n", buf));
1347                 x_fprintf(x_stdout, "BH GSS-SPNEGO query invalid\n");
1348                 return;
1349         }
1350
1351         if (request.type == SPNEGO_NEG_TOKEN_INIT) {
1352
1353                 /* Second request from Client. This is where the
1354                    client offers its mechanism to use. */
1355
1356                 if ( (request.negTokenInit.mechTypes == NULL) ||
1357                      (request.negTokenInit.mechTypes[0] == NULL) ) {
1358                         DEBUG(1, ("Client did not offer any mechanism\n"));
1359                         x_fprintf(x_stdout, "BH Client did not offer any "
1360                                             "mechanism\n");
1361                         return;
1362                 }
1363
1364                 status = NT_STATUS_UNSUCCESSFUL;
1365                 if (strcmp(request.negTokenInit.mechTypes[0], OID_NTLMSSP) == 0) {
1366
1367                         if ( request.negTokenInit.mechToken.data == NULL ) {
1368                                 DEBUG(1, ("Client did not provide NTLMSSP data\n"));
1369                                 x_fprintf(x_stdout, "BH Client did not provide "
1370                                                     "NTLMSSP data\n");
1371                                 return;
1372                         }
1373
1374                         if ( ntlmssp_state != NULL ) {
1375                                 DEBUG(1, ("Client wants a new NTLMSSP challenge, but "
1376                                           "already got one\n"));
1377                                 x_fprintf(x_stdout, "BH Client wants a new "
1378                                                     "NTLMSSP challenge, but "
1379                                                     "already got one\n");
1380                                 TALLOC_FREE(ntlmssp_state);
1381                                 return;
1382                         }
1383
1384                         if (!NT_STATUS_IS_OK(status = ntlm_auth_start_ntlmssp_server(&ntlmssp_state))) {
1385                                 x_fprintf(x_stdout, "BH %s\n", nt_errstr(status));
1386                                 return;
1387                         }
1388
1389                         DEBUG(10, ("got NTLMSSP packet:\n"));
1390                         dump_data(10, request.negTokenInit.mechToken.data,
1391                                   request.negTokenInit.mechToken.length);
1392
1393                         response.type = SPNEGO_NEG_TOKEN_TARG;
1394                         response.negTokenTarg.supportedMech = talloc_strdup(ctx, OID_NTLMSSP);
1395                         response.negTokenTarg.mechListMIC = data_blob_talloc(ctx, NULL, 0);
1396
1397                         status = ntlmssp_update(ntlmssp_state,
1398                                                        request.negTokenInit.mechToken,
1399                                                        &response.negTokenTarg.responseToken);
1400                 }
1401
1402 #ifdef HAVE_KRB5
1403                 if (strcmp(request.negTokenInit.mechTypes[0], OID_KERBEROS5_OLD) == 0) {
1404
1405                         TALLOC_CTX *mem_ctx = talloc_init("manage_gss_spnego_request");
1406                         char *principal;
1407                         DATA_BLOB ap_rep;
1408                         DATA_BLOB session_key;
1409                         struct PAC_LOGON_INFO *logon_info = NULL;
1410                         DATA_BLOB ticket;
1411                         uint8_t tok_id[2];
1412
1413                         if ( request.negTokenInit.mechToken.data == NULL ) {
1414                                 DEBUG(1, ("Client did not provide Kerberos data\n"));
1415                                 x_fprintf(x_stdout, "BH Client did not provide "
1416                                                     "Kerberos data\n");
1417                                 return;
1418                         }
1419
1420                         dump_data(10, request.negTokenInit.mechToken.data,
1421                                   request.negTokenInit.mechToken.length);
1422
1423                         if (!spnego_parse_krb5_wrap(ctx, request.negTokenInit.mechToken,
1424                                                     &ticket, tok_id)) {
1425                                 DEBUG(1, ("spnego_parse_krb5_wrap failed\n"));
1426                                 x_fprintf(x_stdout, "BH spnego_parse_krb5_wrap failed\n");
1427                                 return;
1428                         }
1429
1430                         response.type = SPNEGO_NEG_TOKEN_TARG;
1431                         response.negTokenTarg.supportedMech = talloc_strdup(ctx, OID_KERBEROS5_OLD);
1432                         response.negTokenTarg.mechListMIC = data_blob_talloc(ctx, NULL, 0);
1433                         response.negTokenTarg.responseToken = data_blob_talloc(ctx, NULL, 0);
1434
1435                         status = ads_verify_ticket(mem_ctx, lp_realm(), 0,
1436                                                    &ticket,
1437                                                    &principal, &logon_info, &ap_rep,
1438                                                    &session_key, True);
1439
1440                         /* Now in "principal" we have the name we are
1441                            authenticated as. */
1442
1443                         if (NT_STATUS_IS_OK(status)) {
1444
1445                                 domain = strchr_m(principal, '@');
1446
1447                                 if (domain == NULL) {
1448                                         DEBUG(1, ("Did not get a valid principal "
1449                                                   "from ads_verify_ticket\n"));
1450                                         x_fprintf(x_stdout, "BH Did not get a "
1451                                                   "valid principal from "
1452                                                   "ads_verify_ticket\n");
1453                                         return;
1454                                 }
1455
1456                                 *domain++ = '\0';
1457                                 domain = SMB_STRDUP(domain);
1458                                 user = SMB_STRDUP(principal);
1459
1460                                 netsamlogon_cache_store(
1461                                         user, &logon_info->info3);
1462
1463                                 data_blob_free(&ap_rep);
1464                         }
1465
1466                         TALLOC_FREE(mem_ctx);
1467                 }
1468 #endif
1469
1470         } else {
1471
1472                 if ( (request.negTokenTarg.supportedMech == NULL) ||
1473                      ( strcmp(request.negTokenTarg.supportedMech, OID_NTLMSSP) != 0 ) ) {
1474                         /* Kerberos should never send a negTokenTarg, OID_NTLMSSP
1475                            is the only one we support that sends this stuff */
1476                         DEBUG(1, ("Got a negTokenTarg for something non-NTLMSSP: %s\n",
1477                                   request.negTokenTarg.supportedMech));
1478                         x_fprintf(x_stdout, "BH Got a negTokenTarg for "
1479                                             "something non-NTLMSSP\n");
1480                         return;
1481                 }
1482
1483                 if (request.negTokenTarg.responseToken.data == NULL) {
1484                         DEBUG(1, ("Got a negTokenTarg without a responseToken!\n"));
1485                         x_fprintf(x_stdout, "BH Got a negTokenTarg without a "
1486                                             "responseToken!\n");
1487                         return;
1488                 }
1489
1490                 status = ntlmssp_update(ntlmssp_state,
1491                                                request.negTokenTarg.responseToken,
1492                                                &response.negTokenTarg.responseToken);
1493
1494                 response.type = SPNEGO_NEG_TOKEN_TARG;
1495                 response.negTokenTarg.supportedMech = talloc_strdup(ctx, OID_NTLMSSP);
1496                 response.negTokenTarg.mechListMIC = data_blob_talloc(ctx, NULL, 0);
1497
1498                 if (NT_STATUS_IS_OK(status)) {
1499                         user = SMB_STRDUP(ntlmssp_state->user);
1500                         domain = SMB_STRDUP(ntlmssp_state->domain);
1501                         TALLOC_FREE(ntlmssp_state);
1502                 }
1503         }
1504
1505         spnego_free_data(&request);
1506
1507         if (NT_STATUS_IS_OK(status)) {
1508                 response.negTokenTarg.negResult = SPNEGO_ACCEPT_COMPLETED;
1509                 reply_code = "AF";
1510                 reply_argument = talloc_asprintf(ctx, "%s\\%s", domain, user);
1511         } else if (NT_STATUS_EQUAL(status,
1512                                    NT_STATUS_MORE_PROCESSING_REQUIRED)) {
1513                 response.negTokenTarg.negResult = SPNEGO_ACCEPT_INCOMPLETE;
1514                 reply_code = "TT";
1515                 reply_argument = talloc_strdup(ctx, "*");
1516         } else {
1517                 response.negTokenTarg.negResult = SPNEGO_REJECT;
1518                 reply_code = "NA";
1519                 reply_argument = talloc_strdup(ctx, nt_errstr(status));
1520         }
1521
1522         if (!reply_argument) {
1523                 DEBUG(1, ("Could not write SPNEGO data blob\n"));
1524                 x_fprintf(x_stdout, "BH Could not write SPNEGO data blob\n");
1525                 return;
1526         }
1527
1528         SAFE_FREE(user);
1529         SAFE_FREE(domain);
1530
1531         len = spnego_write_data(ctx, &token, &response);
1532         spnego_free_data(&response);
1533
1534         if (len == -1) {
1535                 DEBUG(1, ("Could not write SPNEGO data blob\n"));
1536                 x_fprintf(x_stdout, "BH Could not write SPNEGO data blob\n");
1537                 return;
1538         }
1539
1540         reply_base64 = base64_encode_data_blob(talloc_tos(), token);
1541
1542         x_fprintf(x_stdout, "%s %s %s\n",
1543                   reply_code, reply_base64, reply_argument);
1544
1545         TALLOC_FREE(reply_base64);
1546         data_blob_free(&token);
1547
1548         return;
1549 }
1550
1551 static struct ntlmssp_state *client_ntlmssp_state = NULL;
1552
1553 static bool manage_client_ntlmssp_init(struct spnego_data spnego)
1554 {
1555         NTSTATUS status;
1556         DATA_BLOB null_blob = data_blob_null;
1557         DATA_BLOB to_server;
1558         char *to_server_base64;
1559         const char *my_mechs[] = {OID_NTLMSSP, NULL};
1560         TALLOC_CTX *ctx = talloc_tos();
1561
1562         DEBUG(10, ("Got spnego negTokenInit with NTLMSSP\n"));
1563
1564         if (client_ntlmssp_state != NULL) {
1565                 DEBUG(1, ("Request for initial SPNEGO request where "
1566                           "we already have a state\n"));
1567                 return False;
1568         }
1569
1570         if (!client_ntlmssp_state) {
1571                 if (!NT_STATUS_IS_OK(status = ntlm_auth_start_ntlmssp_client(&client_ntlmssp_state))) {
1572                         x_fprintf(x_stdout, "BH %s\n", nt_errstr(status));
1573                         return False;
1574                 }
1575         }
1576
1577
1578         if (opt_password == NULL) {
1579
1580                 /* Request a password from the calling process.  After
1581                    sending it, the calling process should retry with
1582                    the negTokenInit. */
1583
1584                 DEBUG(10, ("Requesting password\n"));
1585                 x_fprintf(x_stdout, "PW\n");
1586                 return True;
1587         }
1588
1589         spnego.type = SPNEGO_NEG_TOKEN_INIT;
1590         spnego.negTokenInit.mechTypes = my_mechs;
1591         spnego.negTokenInit.reqFlags = data_blob_null;
1592         spnego.negTokenInit.reqFlagsPadding = 0;
1593         spnego.negTokenInit.mechListMIC = null_blob;
1594
1595         status = ntlmssp_update(client_ntlmssp_state, null_blob,
1596                                        &spnego.negTokenInit.mechToken);
1597
1598         if ( !(NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED) ||
1599                         NT_STATUS_IS_OK(status)) ) {
1600                 DEBUG(1, ("Expected OK or MORE_PROCESSING_REQUIRED, got: %s\n",
1601                           nt_errstr(status)));
1602                 TALLOC_FREE(client_ntlmssp_state);
1603                 return False;
1604         }
1605
1606         spnego_write_data(ctx, &to_server, &spnego);
1607         data_blob_free(&spnego.negTokenInit.mechToken);
1608
1609         to_server_base64 = base64_encode_data_blob(talloc_tos(), to_server);
1610         data_blob_free(&to_server);
1611         x_fprintf(x_stdout, "KK %s\n", to_server_base64);
1612         TALLOC_FREE(to_server_base64);
1613         return True;
1614 }
1615
1616 static void manage_client_ntlmssp_targ(struct spnego_data spnego)
1617 {
1618         NTSTATUS status;
1619         DATA_BLOB null_blob = data_blob_null;
1620         DATA_BLOB request;
1621         DATA_BLOB to_server;
1622         char *to_server_base64;
1623         TALLOC_CTX *ctx = talloc_tos();
1624
1625         DEBUG(10, ("Got spnego negTokenTarg with NTLMSSP\n"));
1626
1627         if (client_ntlmssp_state == NULL) {
1628                 DEBUG(1, ("Got NTLMSSP tArg without a client state\n"));
1629                 x_fprintf(x_stdout, "BH Got NTLMSSP tArg without a client state\n");
1630                 return;
1631         }
1632
1633         if (spnego.negTokenTarg.negResult == SPNEGO_REJECT) {
1634                 x_fprintf(x_stdout, "NA\n");
1635                 TALLOC_FREE(client_ntlmssp_state);
1636                 return;
1637         }
1638
1639         if (spnego.negTokenTarg.negResult == SPNEGO_ACCEPT_COMPLETED) {
1640                 x_fprintf(x_stdout, "AF\n");
1641                 TALLOC_FREE(client_ntlmssp_state);
1642                 return;
1643         }
1644
1645         status = ntlmssp_update(client_ntlmssp_state,
1646                                        spnego.negTokenTarg.responseToken,
1647                                        &request);
1648
1649         if (!NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
1650                 DEBUG(1, ("Expected MORE_PROCESSING_REQUIRED from "
1651                           "ntlmssp_client_update, got: %s\n",
1652                           nt_errstr(status)));
1653                 x_fprintf(x_stdout, "BH Expected MORE_PROCESSING_REQUIRED from "
1654                                     "ntlmssp_client_update\n");
1655                 data_blob_free(&request);
1656                 TALLOC_FREE(client_ntlmssp_state);
1657                 return;
1658         }
1659
1660         spnego.type = SPNEGO_NEG_TOKEN_TARG;
1661         spnego.negTokenTarg.negResult = SPNEGO_ACCEPT_INCOMPLETE;
1662         spnego.negTokenTarg.supportedMech = (char *)OID_NTLMSSP;
1663         spnego.negTokenTarg.responseToken = request;
1664         spnego.negTokenTarg.mechListMIC = null_blob;
1665
1666         spnego_write_data(ctx, &to_server, &spnego);
1667         data_blob_free(&request);
1668
1669         to_server_base64 = base64_encode_data_blob(talloc_tos(), to_server);
1670         data_blob_free(&to_server);
1671         x_fprintf(x_stdout, "KK %s\n", to_server_base64);
1672         TALLOC_FREE(to_server_base64);
1673         return;
1674 }
1675
1676 #ifdef HAVE_KRB5
1677
1678 static bool manage_client_krb5_init(struct spnego_data spnego)
1679 {
1680         char *principal;
1681         DATA_BLOB tkt, to_server;
1682         DATA_BLOB session_key_krb5 = data_blob_null;
1683         struct spnego_data reply;
1684         char *reply_base64;
1685         int retval;
1686
1687         const char *my_mechs[] = {OID_KERBEROS5_OLD, NULL};
1688         ssize_t len;
1689         TALLOC_CTX *ctx = talloc_tos();
1690
1691         if ( (spnego.negTokenInit.mechListMIC.data == NULL) ||
1692              (spnego.negTokenInit.mechListMIC.length == 0) ) {
1693                 DEBUG(1, ("Did not get a principal for krb5\n"));
1694                 return False;
1695         }
1696
1697         principal = (char *)SMB_MALLOC(
1698                 spnego.negTokenInit.mechListMIC.length+1);
1699
1700         if (principal == NULL) {
1701                 DEBUG(1, ("Could not malloc principal\n"));
1702                 return False;
1703         }
1704
1705         memcpy(principal, spnego.negTokenInit.mechListMIC.data,
1706                spnego.negTokenInit.mechListMIC.length);
1707         principal[spnego.negTokenInit.mechListMIC.length] = '\0';
1708
1709         retval = cli_krb5_get_ticket(ctx, principal, 0,
1710                                           &tkt, &session_key_krb5,
1711                                           0, NULL, NULL, NULL);
1712         if (retval) {
1713                 char *user = NULL;
1714
1715                 /* Let's try to first get the TGT, for that we need a
1716                    password. */
1717
1718                 if (opt_password == NULL) {
1719                         DEBUG(10, ("Requesting password\n"));
1720                         x_fprintf(x_stdout, "PW\n");
1721                         return True;
1722                 }
1723
1724                 user = talloc_asprintf(talloc_tos(), "%s@%s", opt_username, opt_domain);
1725                 if (!user) {
1726                         return false;
1727                 }
1728
1729                 if ((retval = kerberos_kinit_password(user, opt_password, 0, NULL))) {
1730                         DEBUG(10, ("Requesting TGT failed: %s\n", error_message(retval)));
1731                         return False;
1732                 }
1733
1734                 retval = cli_krb5_get_ticket(ctx, principal, 0,
1735                                                   &tkt, &session_key_krb5,
1736                                                   0, NULL, NULL, NULL);
1737                 if (retval) {
1738                         DEBUG(10, ("Kinit suceeded, but getting a ticket failed: %s\n", error_message(retval)));
1739                         return False;
1740                 }
1741         }
1742
1743         data_blob_free(&session_key_krb5);
1744
1745         ZERO_STRUCT(reply);
1746
1747         reply.type = SPNEGO_NEG_TOKEN_INIT;
1748         reply.negTokenInit.mechTypes = my_mechs;
1749         reply.negTokenInit.reqFlags = data_blob_null;
1750         reply.negTokenInit.reqFlagsPadding = 0;
1751         reply.negTokenInit.mechToken = tkt;
1752         reply.negTokenInit.mechListMIC = data_blob_null;
1753
1754         len = spnego_write_data(ctx, &to_server, &reply);
1755         data_blob_free(&tkt);
1756
1757         if (len == -1) {
1758                 DEBUG(1, ("Could not write SPNEGO data blob\n"));
1759                 return False;
1760         }
1761
1762         reply_base64 = base64_encode_data_blob(talloc_tos(), to_server);
1763         x_fprintf(x_stdout, "KK %s *\n", reply_base64);
1764
1765         TALLOC_FREE(reply_base64);
1766         data_blob_free(&to_server);
1767         DEBUG(10, ("sent GSS-SPNEGO KERBEROS5 negTokenInit\n"));
1768         return True;
1769 }
1770
1771 static void manage_client_krb5_targ(struct spnego_data spnego)
1772 {
1773         switch (spnego.negTokenTarg.negResult) {
1774         case SPNEGO_ACCEPT_INCOMPLETE:
1775                 DEBUG(1, ("Got a Kerberos negTokenTarg with ACCEPT_INCOMPLETE\n"));
1776                 x_fprintf(x_stdout, "BH Got a Kerberos negTokenTarg with "
1777                                     "ACCEPT_INCOMPLETE\n");
1778                 break;
1779         case SPNEGO_ACCEPT_COMPLETED:
1780                 DEBUG(10, ("Accept completed\n"));
1781                 x_fprintf(x_stdout, "AF\n");
1782                 break;
1783         case SPNEGO_REJECT:
1784                 DEBUG(10, ("Rejected\n"));
1785                 x_fprintf(x_stdout, "NA\n");
1786                 break;
1787         default:
1788                 DEBUG(1, ("Got an invalid negTokenTarg\n"));
1789                 x_fprintf(x_stdout, "AF\n");
1790         }
1791 }
1792
1793 #endif
1794
1795 static void manage_gss_spnego_client_request(struct ntlm_auth_state *state,
1796                                                 char *buf, int length)
1797 {
1798         DATA_BLOB request;
1799         struct spnego_data spnego;
1800         ssize_t len;
1801         TALLOC_CTX *ctx = talloc_tos();
1802
1803         if (!opt_username || !*opt_username) {
1804                 x_fprintf(x_stderr, "username must be specified!\n\n");
1805                 exit(1);
1806         }
1807
1808         if (strlen(buf) <= 3) {
1809                 DEBUG(1, ("SPNEGO query [%s] too short\n", buf));
1810                 x_fprintf(x_stdout, "BH SPNEGO query too short\n");
1811                 return;
1812         }
1813
1814         request = base64_decode_data_blob(buf+3);
1815
1816         if (strncmp(buf, "PW ", 3) == 0) {
1817
1818                 /* We asked for a password and obviously got it :-) */
1819
1820                 opt_password = SMB_STRNDUP((const char *)request.data, request.length);
1821
1822                 if (opt_password == NULL) {
1823                         DEBUG(1, ("Out of memory\n"));
1824                         x_fprintf(x_stdout, "BH Out of memory\n");
1825                         data_blob_free(&request);
1826                         return;
1827                 }
1828
1829                 x_fprintf(x_stdout, "OK\n");
1830                 data_blob_free(&request);
1831                 return;
1832         }
1833
1834         if ( (strncmp(buf, "TT ", 3) != 0) &&
1835              (strncmp(buf, "AF ", 3) != 0) &&
1836              (strncmp(buf, "NA ", 3) != 0) ) {
1837                 DEBUG(1, ("SPNEGO request [%s] invalid\n", buf));
1838                 x_fprintf(x_stdout, "BH SPNEGO request invalid\n");
1839                 data_blob_free(&request);
1840                 return;
1841         }
1842
1843         /* So we got a server challenge to generate a SPNEGO
1844            client-to-server request... */
1845
1846         len = spnego_read_data(ctx, request, &spnego);
1847         data_blob_free(&request);
1848
1849         if (len == -1) {
1850                 DEBUG(1, ("Could not read SPNEGO data for [%s]\n", buf));
1851                 x_fprintf(x_stdout, "BH Could not read SPNEGO data\n");
1852                 return;
1853         }
1854
1855         if (spnego.type == SPNEGO_NEG_TOKEN_INIT) {
1856
1857                 /* The server offers a list of mechanisms */
1858
1859                 const char **mechType = (const char **)spnego.negTokenInit.mechTypes;
1860
1861                 while (*mechType != NULL) {
1862
1863 #ifdef HAVE_KRB5
1864                         if ( (strcmp(*mechType, OID_KERBEROS5_OLD) == 0) ||
1865                              (strcmp(*mechType, OID_KERBEROS5) == 0) ) {
1866                                 if (manage_client_krb5_init(spnego))
1867                                         goto out;
1868                         }
1869 #endif
1870
1871                         if (strcmp(*mechType, OID_NTLMSSP) == 0) {
1872                                 if (manage_client_ntlmssp_init(spnego))
1873                                         goto out;
1874                         }
1875
1876                         mechType++;
1877                 }
1878
1879                 DEBUG(1, ("Server offered no compatible mechanism\n"));
1880                 x_fprintf(x_stdout, "BH Server offered no compatible mechanism\n");
1881                 return;
1882         }
1883
1884         if (spnego.type == SPNEGO_NEG_TOKEN_TARG) {
1885
1886                 if (spnego.negTokenTarg.supportedMech == NULL) {
1887                         /* On accept/reject Windows does not send the
1888                            mechanism anymore. Handle that here and
1889                            shut down the mechanisms. */
1890
1891                         switch (spnego.negTokenTarg.negResult) {
1892                         case SPNEGO_ACCEPT_COMPLETED:
1893                                 x_fprintf(x_stdout, "AF\n");
1894                                 break;
1895                         case SPNEGO_REJECT:
1896                                 x_fprintf(x_stdout, "NA\n");
1897                                 break;
1898                         default:
1899                                 DEBUG(1, ("Got a negTokenTarg with no mech and an "
1900                                           "unknown negResult: %d\n",
1901                                           spnego.negTokenTarg.negResult));
1902                                 x_fprintf(x_stdout, "BH Got a negTokenTarg with"
1903                                                     " no mech and an unknown "
1904                                                     "negResult\n");
1905                         }
1906
1907                         TALLOC_FREE(client_ntlmssp_state);
1908                         goto out;
1909                 }
1910
1911                 if (strcmp(spnego.negTokenTarg.supportedMech,
1912                            OID_NTLMSSP) == 0) {
1913                         manage_client_ntlmssp_targ(spnego);
1914                         goto out;
1915                 }
1916
1917 #if HAVE_KRB5
1918                 if (strcmp(spnego.negTokenTarg.supportedMech,
1919                            OID_KERBEROS5_OLD) == 0) {
1920                         manage_client_krb5_targ(spnego);
1921                         goto out;
1922                 }
1923 #endif
1924
1925         }
1926
1927         DEBUG(1, ("Got an SPNEGO token I could not handle [%s]!\n", buf));
1928         x_fprintf(x_stdout, "BH Got an SPNEGO token I could not handle\n");
1929         return;
1930
1931  out:
1932         spnego_free_data(&spnego);
1933         return;
1934 }
1935
1936 static void manage_ntlm_server_1_request(struct ntlm_auth_state *state,
1937                                                 char *buf, int length)
1938 {
1939         char *request, *parameter;      
1940         static DATA_BLOB challenge;
1941         static DATA_BLOB lm_response;
1942         static DATA_BLOB nt_response;
1943         static char *full_username;
1944         static char *username;
1945         static char *domain;
1946         static char *plaintext_password;
1947         static bool ntlm_server_1_user_session_key;
1948         static bool ntlm_server_1_lm_session_key;
1949
1950         if (strequal(buf, ".")) {
1951                 if (!full_username && !username) {      
1952                         x_fprintf(x_stdout, "Error: No username supplied!\n");
1953                 } else if (plaintext_password) {
1954                         /* handle this request as plaintext */
1955                         if (!full_username) {
1956                                 if (asprintf(&full_username, "%s%c%s", domain, winbind_separator(), username) == -1) {
1957                                         x_fprintf(x_stdout, "Error: Out of memory in asprintf!\n.\n");
1958                                         return;
1959                                 }
1960                         }
1961                         if (check_plaintext_auth(full_username, plaintext_password, False)) {
1962                                 x_fprintf(x_stdout, "Authenticated: Yes\n");
1963                         } else {
1964                                 x_fprintf(x_stdout, "Authenticated: No\n");
1965                         }
1966                 } else if (!lm_response.data && !nt_response.data) {
1967                         x_fprintf(x_stdout, "Error: No password supplied!\n");
1968                 } else if (!challenge.data) {   
1969                         x_fprintf(x_stdout, "Error: No lanman-challenge supplied!\n");
1970                 } else {
1971                         char *error_string = NULL;
1972                         uchar lm_key[8];
1973                         uchar user_session_key[16];
1974                         uint32 flags = 0;
1975
1976                         if (full_username && !username) {
1977                                 fstring fstr_user;
1978                                 fstring fstr_domain;
1979
1980                                 if (!parse_ntlm_auth_domain_user(full_username, fstr_user, fstr_domain)) {
1981                                         /* username might be 'tainted', don't print into our new-line deleimianted stream */
1982                                         x_fprintf(x_stdout, "Error: Could not parse into domain and username\n");
1983                                 }
1984                                 SAFE_FREE(username);
1985                                 SAFE_FREE(domain);
1986                                 username = smb_xstrdup(fstr_user);
1987                                 domain = smb_xstrdup(fstr_domain);
1988                         }
1989
1990                         if (!domain) {
1991                                 domain = smb_xstrdup(get_winbind_domain());
1992                         }
1993
1994                         if (ntlm_server_1_lm_session_key) 
1995                                 flags |= WBFLAG_PAM_LMKEY;
1996
1997                         if (ntlm_server_1_user_session_key) 
1998                                 flags |= WBFLAG_PAM_USER_SESSION_KEY;
1999
2000                         if (!NT_STATUS_IS_OK(
2001                                     contact_winbind_auth_crap(username, 
2002                                                               domain, 
2003                                                               global_myname(),
2004                                                               &challenge, 
2005                                                               &lm_response, 
2006                                                               &nt_response, 
2007                                                               flags, 
2008                                                               lm_key, 
2009                                                               user_session_key,
2010                                                               &error_string,
2011                                                               NULL))) {
2012
2013                                 x_fprintf(x_stdout, "Authenticated: No\n");
2014                                 x_fprintf(x_stdout, "Authentication-Error: %s\n.\n", error_string);
2015                         } else {
2016                                 static char zeros[16];
2017                                 char *hex_lm_key;
2018                                 char *hex_user_session_key;
2019
2020                                 x_fprintf(x_stdout, "Authenticated: Yes\n");
2021
2022                                 if (ntlm_server_1_lm_session_key 
2023                                     && (memcmp(zeros, lm_key, 
2024                                                sizeof(lm_key)) != 0)) {
2025                                         hex_lm_key = hex_encode_talloc(NULL,
2026                                                                 (const unsigned char *)lm_key,
2027                                                                 sizeof(lm_key));
2028                                         x_fprintf(x_stdout, "LANMAN-Session-Key: %s\n", hex_lm_key);
2029                                         TALLOC_FREE(hex_lm_key);
2030                                 }
2031
2032                                 if (ntlm_server_1_user_session_key 
2033                                     && (memcmp(zeros, user_session_key, 
2034                                                sizeof(user_session_key)) != 0)) {
2035                                         hex_user_session_key = hex_encode_talloc(NULL,
2036                                                                           (const unsigned char *)user_session_key, 
2037                                                                           sizeof(user_session_key));
2038                                         x_fprintf(x_stdout, "User-Session-Key: %s\n", hex_user_session_key);
2039                                         TALLOC_FREE(hex_user_session_key);
2040                                 }
2041                         }
2042                         SAFE_FREE(error_string);
2043                 }
2044                 /* clear out the state */
2045                 challenge = data_blob_null;
2046                 nt_response = data_blob_null;
2047                 lm_response = data_blob_null;
2048                 SAFE_FREE(full_username);
2049                 SAFE_FREE(username);
2050                 SAFE_FREE(domain);
2051                 SAFE_FREE(plaintext_password);
2052                 ntlm_server_1_user_session_key = False;
2053                 ntlm_server_1_lm_session_key = False;
2054                 x_fprintf(x_stdout, ".\n");
2055
2056                 return;
2057         }
2058
2059         request = buf;
2060
2061         /* Indicates a base64 encoded structure */
2062         parameter = strstr_m(request, ":: ");
2063         if (!parameter) {
2064                 parameter = strstr_m(request, ": ");
2065
2066                 if (!parameter) {
2067                         DEBUG(0, ("Parameter not found!\n"));
2068                         x_fprintf(x_stdout, "Error: Parameter not found!\n.\n");
2069                         return;
2070                 }
2071
2072                 parameter[0] ='\0';
2073                 parameter++;
2074                 parameter[0] ='\0';
2075                 parameter++;
2076
2077         } else {
2078                 parameter[0] ='\0';
2079                 parameter++;
2080                 parameter[0] ='\0';
2081                 parameter++;
2082                 parameter[0] ='\0';
2083                 parameter++;
2084
2085                 base64_decode_inplace(parameter);
2086         }
2087
2088         if (strequal(request, "LANMAN-Challenge")) {
2089                 challenge = strhex_to_data_blob(NULL, parameter);
2090                 if (challenge.length != 8) {
2091                         x_fprintf(x_stdout, "Error: hex decode of %s failed! (got %d bytes, expected 8)\n.\n", 
2092                                   parameter,
2093                                   (int)challenge.length);
2094                         challenge = data_blob_null;
2095                 }
2096         } else if (strequal(request, "NT-Response")) {
2097                 nt_response = strhex_to_data_blob(NULL, parameter);
2098                 if (nt_response.length < 24) {
2099                         x_fprintf(x_stdout, "Error: hex decode of %s failed! (only got %d bytes, needed at least 24)\n.\n", 
2100                                   parameter,
2101                                   (int)nt_response.length);
2102                         nt_response = data_blob_null;
2103                 }
2104         } else if (strequal(request, "LANMAN-Response")) {
2105                 lm_response = strhex_to_data_blob(NULL, parameter);
2106                 if (lm_response.length != 24) {
2107                         x_fprintf(x_stdout, "Error: hex decode of %s failed! (got %d bytes, expected 24)\n.\n", 
2108                                   parameter,
2109                                   (int)lm_response.length);
2110                         lm_response = data_blob_null;
2111                 }
2112         } else if (strequal(request, "Password")) {
2113                 plaintext_password = smb_xstrdup(parameter);
2114         } else if (strequal(request, "NT-Domain")) {
2115                 domain = smb_xstrdup(parameter);
2116         } else if (strequal(request, "Username")) {
2117                 username = smb_xstrdup(parameter);
2118         } else if (strequal(request, "Full-Username")) {
2119                 full_username = smb_xstrdup(parameter);
2120         } else if (strequal(request, "Request-User-Session-Key")) {
2121                 ntlm_server_1_user_session_key = strequal(parameter, "Yes");
2122         } else if (strequal(request, "Request-LanMan-Session-Key")) {
2123                 ntlm_server_1_lm_session_key = strequal(parameter, "Yes");
2124         } else {
2125                 x_fprintf(x_stdout, "Error: Unknown request %s\n.\n", request);
2126         }
2127 }
2128
2129 static void manage_ntlm_change_password_1_request(struct ntlm_auth_state *state,
2130                                                         char *buf, int length)
2131 {
2132         char *request, *parameter;      
2133         static DATA_BLOB new_nt_pswd;
2134         static DATA_BLOB old_nt_hash_enc;
2135         static DATA_BLOB new_lm_pswd;
2136         static DATA_BLOB old_lm_hash_enc;
2137         static char *full_username = NULL;
2138         static char *username = NULL;
2139         static char *domain = NULL;
2140         static char *newpswd =  NULL;
2141         static char *oldpswd = NULL;
2142
2143         if (strequal(buf, ".")) {
2144                 if(newpswd && oldpswd) {
2145                         uchar old_nt_hash[16];
2146                         uchar old_lm_hash[16];
2147                         uchar new_nt_hash[16];
2148                         uchar new_lm_hash[16];
2149
2150                         new_nt_pswd = data_blob(NULL, 516);
2151                         old_nt_hash_enc = data_blob(NULL, 16);
2152
2153                         /* Calculate the MD4 hash (NT compatible) of the
2154                          * password */
2155                         E_md4hash(oldpswd, old_nt_hash);
2156                         E_md4hash(newpswd, new_nt_hash);
2157
2158                         /* E_deshash returns false for 'long'
2159                            passwords (> 14 DOS chars).  
2160
2161                            Therefore, don't send a buffer
2162                            encrypted with the truncated hash
2163                            (it could allow an even easier
2164                            attack on the password)
2165
2166                            Likewise, obey the admin's restriction
2167                         */
2168
2169                         if (lp_client_lanman_auth() &&
2170                             E_deshash(newpswd, new_lm_hash) &&
2171                             E_deshash(oldpswd, old_lm_hash)) {
2172                                 new_lm_pswd = data_blob(NULL, 516);
2173                                 old_lm_hash_enc = data_blob(NULL, 16);
2174                                 encode_pw_buffer(new_lm_pswd.data, newpswd,
2175                                                  STR_UNICODE);
2176
2177                                 arcfour_crypt(new_lm_pswd.data, old_nt_hash, 516);
2178                                 E_old_pw_hash(new_nt_hash, old_lm_hash,
2179                                               old_lm_hash_enc.data);
2180                         } else {
2181                                 new_lm_pswd.data = NULL;
2182                                 new_lm_pswd.length = 0;
2183                                 old_lm_hash_enc.data = NULL;
2184                                 old_lm_hash_enc.length = 0;
2185                         }
2186
2187                         encode_pw_buffer(new_nt_pswd.data, newpswd,
2188                                          STR_UNICODE);
2189
2190                         arcfour_crypt(new_nt_pswd.data, old_nt_hash, 516);
2191                         E_old_pw_hash(new_nt_hash, old_nt_hash,
2192                                       old_nt_hash_enc.data);
2193                 }
2194
2195                 if (!full_username && !username) {      
2196                         x_fprintf(x_stdout, "Error: No username supplied!\n");
2197                 } else if ((!new_nt_pswd.data || !old_nt_hash_enc.data) &&
2198                            (!new_lm_pswd.data || old_lm_hash_enc.data) ) {
2199                         x_fprintf(x_stdout, "Error: No NT or LM password "
2200                                   "blobs supplied!\n");
2201                 } else {
2202                         char *error_string = NULL;
2203
2204                         if (full_username && !username) {
2205                                 fstring fstr_user;
2206                                 fstring fstr_domain;
2207
2208                                 if (!parse_ntlm_auth_domain_user(full_username,
2209                                                                  fstr_user,
2210                                                                  fstr_domain)) {
2211                                         /* username might be 'tainted', don't
2212                                          * print into our new-line
2213                                          * deleimianted stream */
2214                                         x_fprintf(x_stdout, "Error: Could not "
2215                                                   "parse into domain and "
2216                                                   "username\n");
2217                                         SAFE_FREE(username);
2218                                         username = smb_xstrdup(full_username);
2219                                 } else {
2220                                         SAFE_FREE(username);
2221                                         SAFE_FREE(domain);
2222                                         username = smb_xstrdup(fstr_user);
2223                                         domain = smb_xstrdup(fstr_domain);
2224                                 }
2225
2226                         }
2227
2228                         if(!NT_STATUS_IS_OK(contact_winbind_change_pswd_auth_crap(
2229                                                     username, domain,
2230                                                     new_nt_pswd,
2231                                                     old_nt_hash_enc,
2232                                                     new_lm_pswd,
2233                                                     old_lm_hash_enc,
2234                                                     &error_string))) {
2235                                 x_fprintf(x_stdout, "Password-Change: No\n");
2236                                 x_fprintf(x_stdout, "Password-Change-Error: "
2237                                           "%s\n.\n", error_string);
2238                         } else {
2239                                 x_fprintf(x_stdout, "Password-Change: Yes\n");
2240                         }
2241
2242                         SAFE_FREE(error_string);
2243                 }
2244                 /* clear out the state */
2245                 new_nt_pswd = data_blob_null;
2246                 old_nt_hash_enc = data_blob_null;
2247                 new_lm_pswd = data_blob_null;
2248                 old_nt_hash_enc = data_blob_null;
2249                 SAFE_FREE(full_username);
2250                 SAFE_FREE(username);
2251                 SAFE_FREE(domain);
2252                 SAFE_FREE(newpswd);
2253                 SAFE_FREE(oldpswd);
2254                 x_fprintf(x_stdout, ".\n");
2255
2256                 return;
2257         }
2258
2259         request = buf;
2260
2261         /* Indicates a base64 encoded structure */
2262         parameter = strstr_m(request, ":: ");
2263         if (!parameter) {
2264                 parameter = strstr_m(request, ": ");
2265
2266                 if (!parameter) {
2267                         DEBUG(0, ("Parameter not found!\n"));
2268                         x_fprintf(x_stdout, "Error: Parameter not found!\n.\n");
2269                         return;
2270                 }
2271
2272                 parameter[0] ='\0';
2273                 parameter++;
2274                 parameter[0] ='\0';
2275                 parameter++;
2276         } else {
2277                 parameter[0] ='\0';
2278                 parameter++;
2279                 parameter[0] ='\0';
2280                 parameter++;
2281                 parameter[0] ='\0';
2282                 parameter++;
2283
2284                 base64_decode_inplace(parameter);
2285         }
2286
2287         if (strequal(request, "new-nt-password-blob")) {
2288                 new_nt_pswd = strhex_to_data_blob(NULL, parameter);
2289                 if (new_nt_pswd.length != 516) {
2290                         x_fprintf(x_stdout, "Error: hex decode of %s failed! "
2291                                   "(got %d bytes, expected 516)\n.\n", 
2292                                   parameter,
2293                                   (int)new_nt_pswd.length);
2294                         new_nt_pswd = data_blob_null;
2295                 }
2296         } else if (strequal(request, "old-nt-hash-blob")) {
2297                 old_nt_hash_enc = strhex_to_data_blob(NULL, parameter);
2298                 if (old_nt_hash_enc.length != 16) {
2299                         x_fprintf(x_stdout, "Error: hex decode of %s failed! "
2300                                   "(got %d bytes, expected 16)\n.\n", 
2301                                   parameter,
2302                                   (int)old_nt_hash_enc.length);
2303                         old_nt_hash_enc = data_blob_null;
2304                 }
2305         } else if (strequal(request, "new-lm-password-blob")) {
2306                 new_lm_pswd = strhex_to_data_blob(NULL, parameter);
2307                 if (new_lm_pswd.length != 516) {
2308                         x_fprintf(x_stdout, "Error: hex decode of %s failed! "
2309                                   "(got %d bytes, expected 516)\n.\n", 
2310                                   parameter,
2311                                   (int)new_lm_pswd.length);
2312                         new_lm_pswd = data_blob_null;
2313                 }
2314         }
2315         else if (strequal(request, "old-lm-hash-blob")) {
2316                 old_lm_hash_enc = strhex_to_data_blob(NULL, parameter);
2317                 if (old_lm_hash_enc.length != 16)
2318                 {
2319                         x_fprintf(x_stdout, "Error: hex decode of %s failed! "
2320                                   "(got %d bytes, expected 16)\n.\n", 
2321                                   parameter,
2322                                   (int)old_lm_hash_enc.length);
2323                         old_lm_hash_enc = data_blob_null;
2324                 }
2325         } else if (strequal(request, "nt-domain")) {
2326                 domain = smb_xstrdup(parameter);
2327         } else if(strequal(request, "username")) {
2328                 username = smb_xstrdup(parameter);
2329         } else if(strequal(request, "full-username")) {
2330                 username = smb_xstrdup(parameter);
2331         } else if(strequal(request, "new-password")) {
2332                 newpswd = smb_xstrdup(parameter);
2333         } else if (strequal(request, "old-password")) {
2334                 oldpswd = smb_xstrdup(parameter);
2335         } else {
2336                 x_fprintf(x_stdout, "Error: Unknown request %s\n.\n", request);
2337         }
2338 }
2339
2340 static void manage_squid_request(struct ntlm_auth_state *state,
2341                 stdio_helper_function fn)
2342 {
2343         char *buf;
2344         char tmp[INITIAL_BUFFER_SIZE+1];
2345         int length, buf_size = 0;
2346         char *c;
2347
2348         buf = talloc_strdup(state->mem_ctx, "");
2349         if (!buf) {
2350                 DEBUG(0, ("Failed to allocate input buffer.\n"));
2351                 x_fprintf(x_stderr, "ERR\n");
2352                 exit(1);
2353         }
2354
2355         do {
2356
2357                 /* this is not a typo - x_fgets doesn't work too well under
2358                  * squid */
2359                 if (fgets(tmp, sizeof(tmp)-1, stdin) == NULL) {
2360                         if (ferror(stdin)) {
2361                                 DEBUG(1, ("fgets() failed! dying..... errno=%d "
2362                                           "(%s)\n", ferror(stdin),
2363                                           strerror(ferror(stdin))));
2364
2365                                 exit(1);
2366                         }
2367                         exit(0);
2368                 }
2369
2370                 buf = talloc_strdup_append_buffer(buf, tmp);
2371                 buf_size += INITIAL_BUFFER_SIZE;
2372
2373                 if (buf_size > MAX_BUFFER_SIZE) {
2374                         DEBUG(2, ("Oversized message\n"));
2375                         x_fprintf(x_stderr, "ERR\n");
2376                         talloc_free(buf);
2377                         return;
2378                 }
2379
2380                 c = strchr(buf, '\n');
2381         } while (c == NULL);
2382
2383         *c = '\0';
2384         length = c-buf;
2385
2386         DEBUG(10, ("Got '%s' from squid (length: %d).\n",buf,length));
2387
2388         if (buf[0] == '\0') {
2389                 DEBUG(2, ("Invalid Request\n"));
2390                 x_fprintf(x_stderr, "ERR\n");
2391                 talloc_free(buf);
2392                 return;
2393         }
2394
2395         fn(state, buf, length);
2396         talloc_free(buf);
2397 }
2398
2399
2400 static void squid_stream(enum stdio_helper_mode stdio_mode, stdio_helper_function fn) {
2401         TALLOC_CTX *mem_ctx;
2402         struct ntlm_auth_state *state;
2403
2404         /* initialize FDescs */
2405         x_setbuf(x_stdout, NULL);
2406         x_setbuf(x_stderr, NULL);
2407
2408         mem_ctx = talloc_init("ntlm_auth");
2409         if (!mem_ctx) {
2410                 DEBUG(0, ("squid_stream: Failed to create talloc context\n"));
2411                 x_fprintf(x_stderr, "ERR\n");
2412                 exit(1);
2413         }
2414
2415         state = talloc_zero(mem_ctx, struct ntlm_auth_state);
2416         if (!state) {
2417                 DEBUG(0, ("squid_stream: Failed to talloc ntlm_auth_state\n"));
2418                 x_fprintf(x_stderr, "ERR\n");
2419                 exit(1);
2420         }
2421
2422         state->mem_ctx = mem_ctx;
2423         state->helper_mode = stdio_mode;
2424
2425         while(1) {
2426                 TALLOC_CTX *frame = talloc_stackframe();
2427                 manage_squid_request(state, fn);
2428                 TALLOC_FREE(frame);
2429         }
2430 }
2431
2432
2433 /* Authenticate a user with a challenge/response */
2434
2435 static bool check_auth_crap(void)
2436 {
2437         NTSTATUS nt_status;
2438         uint32 flags = 0;
2439         char lm_key[8];
2440         char user_session_key[16];
2441         char *hex_lm_key;
2442         char *hex_user_session_key;
2443         char *error_string;
2444         static uint8 zeros[16];
2445
2446         x_setbuf(x_stdout, NULL);
2447
2448         if (request_lm_key) 
2449                 flags |= WBFLAG_PAM_LMKEY;
2450
2451         if (request_user_session_key) 
2452                 flags |= WBFLAG_PAM_USER_SESSION_KEY;
2453
2454         flags |= WBFLAG_PAM_NT_STATUS_SQUASH;
2455
2456         nt_status = contact_winbind_auth_crap(opt_username, opt_domain, 
2457                                               opt_workstation,
2458                                               &opt_challenge, 
2459                                               &opt_lm_response, 
2460                                               &opt_nt_response, 
2461                                               flags,
2462                                               (unsigned char *)lm_key, 
2463                                               (unsigned char *)user_session_key, 
2464                                               &error_string, NULL);
2465
2466         if (!NT_STATUS_IS_OK(nt_status)) {
2467                 x_fprintf(x_stdout, "%s (0x%x)\n", 
2468                           error_string,
2469                           NT_STATUS_V(nt_status));
2470                 SAFE_FREE(error_string);
2471                 return False;
2472         }
2473
2474         if (request_lm_key 
2475             && (memcmp(zeros, lm_key, 
2476                        sizeof(lm_key)) != 0)) {
2477                 hex_lm_key = hex_encode_talloc(talloc_tos(), (const unsigned char *)lm_key,
2478                                         sizeof(lm_key));
2479                 x_fprintf(x_stdout, "LM_KEY: %s\n", hex_lm_key);
2480                 TALLOC_FREE(hex_lm_key);
2481         }
2482         if (request_user_session_key 
2483             && (memcmp(zeros, user_session_key, 
2484                        sizeof(user_session_key)) != 0)) {
2485                 hex_user_session_key = hex_encode_talloc(talloc_tos(), (const unsigned char *)user_session_key, 
2486                                                   sizeof(user_session_key));
2487                 x_fprintf(x_stdout, "NT_KEY: %s\n", hex_user_session_key);
2488                 TALLOC_FREE(hex_user_session_key);
2489         }
2490
2491         return True;
2492 }
2493
2494 /* Main program */
2495
2496 enum {
2497         OPT_USERNAME = 1000,
2498         OPT_DOMAIN,
2499         OPT_WORKSTATION,
2500         OPT_CHALLENGE,
2501         OPT_RESPONSE,
2502         OPT_LM,
2503         OPT_NT,
2504         OPT_PASSWORD,
2505         OPT_LM_KEY,
2506         OPT_USER_SESSION_KEY,
2507         OPT_DIAGNOSTICS,
2508         OPT_REQUIRE_MEMBERSHIP,
2509         OPT_USE_CACHED_CREDS,
2510         OPT_PAM_WINBIND_CONF
2511 };
2512
2513  int main(int argc, const char **argv)
2514 {
2515         TALLOC_CTX *frame = talloc_stackframe();
2516         int opt;
2517         static const char *helper_protocol;
2518         static int diagnostics;
2519
2520         static const char *hex_challenge;
2521         static const char *hex_lm_response;
2522         static const char *hex_nt_response;
2523
2524         poptContext pc;
2525
2526         /* NOTE: DO NOT change this interface without considering the implications!
2527            This is an external interface, which other programs will use to interact 
2528            with this helper.
2529         */
2530
2531         /* We do not use single-letter command abbreviations, because they harm future 
2532            interface stability. */
2533
2534         struct poptOption long_options[] = {
2535                 POPT_AUTOHELP
2536                 { "helper-protocol", 0, POPT_ARG_STRING, &helper_protocol, OPT_DOMAIN, "operate as a stdio-based helper", "helper protocol to use"},
2537                 { "username", 0, POPT_ARG_STRING, &opt_username, OPT_USERNAME, "username"},
2538                 { "domain", 0, POPT_ARG_STRING, &opt_domain, OPT_DOMAIN, "domain name"},
2539                 { "workstation", 0, POPT_ARG_STRING, &opt_workstation, OPT_WORKSTATION, "workstation"},
2540                 { "challenge", 0, POPT_ARG_STRING, &hex_challenge, OPT_CHALLENGE, "challenge (HEX encoded)"},
2541                 { "lm-response", 0, POPT_ARG_STRING, &hex_lm_response, OPT_LM, "LM Response to the challenge (HEX encoded)"},
2542                 { "nt-response", 0, POPT_ARG_STRING, &hex_nt_response, OPT_NT, "NT or NTLMv2 Response to the challenge (HEX encoded)"},
2543                 { "password", 0, POPT_ARG_STRING, &opt_password, OPT_PASSWORD, "User's plaintext password"},            
2544                 { "request-lm-key", 0, POPT_ARG_NONE, &request_lm_key, OPT_LM_KEY, "Retrieve LM session key"},
2545                 { "request-nt-key", 0, POPT_ARG_NONE, &request_user_session_key, OPT_USER_SESSION_KEY, "Retrieve User (NT) session key"},
2546                 { "use-cached-creds", 0, POPT_ARG_NONE, &use_cached_creds, OPT_USE_CACHED_CREDS, "Use cached credentials if no password is given"},
2547                 { "diagnostics", 0, POPT_ARG_NONE, &diagnostics,
2548                   OPT_DIAGNOSTICS,
2549                   "Perform diagnostics on the authentication chain"},
2550                 { "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" },
2551                 { "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" },
2552                 POPT_COMMON_CONFIGFILE
2553                 POPT_COMMON_VERSION
2554                 POPT_TABLEEND
2555         };
2556
2557         /* Samba client initialisation */
2558         load_case_tables();
2559
2560         setup_logging("ntlm_auth", DEBUG_STDERR);
2561
2562         /* Parse options */
2563
2564         pc = poptGetContext("ntlm_auth", argc, argv, long_options, 0);
2565
2566         /* Parse command line options */
2567
2568         if (argc == 1) {
2569                 poptPrintHelp(pc, stderr, 0);
2570                 return 1;
2571         }
2572
2573         while((opt = poptGetNextOpt(pc)) != -1) {
2574                 /* Get generic config options like --configfile */
2575         }
2576
2577         poptFreeContext(pc);
2578
2579         if (!lp_load(get_dyn_CONFIGFILE(), True, False, False, True)) {
2580                 d_fprintf(stderr, "ntlm_auth: error opening config file %s. Error was %s\n",
2581                         get_dyn_CONFIGFILE(), strerror(errno));
2582                 exit(1);
2583         }
2584
2585         pc = poptGetContext(NULL, argc, (const char **)argv, long_options, 
2586                             POPT_CONTEXT_KEEP_FIRST);
2587
2588         while((opt = poptGetNextOpt(pc)) != -1) {
2589                 switch (opt) {
2590                 case OPT_CHALLENGE:
2591                         opt_challenge = strhex_to_data_blob(NULL, hex_challenge);
2592                         if (opt_challenge.length != 8) {
2593                                 x_fprintf(x_stderr, "hex decode of %s failed! (only got %d bytes)\n", 
2594                                           hex_challenge,
2595                                           (int)opt_challenge.length);
2596                                 exit(1);
2597                         }
2598                         break;
2599                 case OPT_LM: 
2600                         opt_lm_response = strhex_to_data_blob(NULL, hex_lm_response);
2601                         if (opt_lm_response.length != 24) {
2602                                 x_fprintf(x_stderr, "hex decode of %s failed! (only got %d bytes)\n", 
2603                                           hex_lm_response,
2604                                           (int)opt_lm_response.length);
2605                                 exit(1);
2606                         }
2607                         break;
2608
2609                 case OPT_NT: 
2610                         opt_nt_response = strhex_to_data_blob(NULL, hex_nt_response);
2611                         if (opt_nt_response.length < 24) {
2612                                 x_fprintf(x_stderr, "hex decode of %s failed! (only got %d bytes)\n", 
2613                                           hex_nt_response,
2614                                           (int)opt_nt_response.length);
2615                                 exit(1);
2616                         }
2617                         break;
2618
2619                 case OPT_REQUIRE_MEMBERSHIP:
2620                         if (StrnCaseCmp("S-", require_membership_of, 2) == 0) {
2621                                 require_membership_of_sid = require_membership_of;
2622                         }
2623                         break;
2624                 }
2625         }
2626
2627         if (opt_username) {
2628                 char *domain = SMB_STRDUP(opt_username);
2629                 char *p = strchr_m(domain, *lp_winbind_separator());
2630                 if (p) {
2631                         opt_username = p+1;
2632                         *p = '\0';
2633                         if (opt_domain && !strequal(opt_domain, domain)) {
2634                                 x_fprintf(x_stderr, "Domain specified in username (%s) "
2635                                         "doesn't match specified domain (%s)!\n\n",
2636                                         domain, opt_domain);
2637                                 poptPrintHelp(pc, stderr, 0);
2638                                 exit(1);
2639                         }
2640                         opt_domain = domain;
2641                 } else {
2642                         SAFE_FREE(domain);
2643                 }
2644         }
2645
2646         /* Note: if opt_domain is "" then send no domain */
2647         if (opt_domain == NULL) {
2648                 opt_domain = get_winbind_domain();
2649         }
2650
2651         if (opt_workstation == NULL) {
2652                 opt_workstation = "";
2653         }
2654
2655         if (helper_protocol) {
2656                 int i;
2657                 for (i=0; i<NUM_HELPER_MODES; i++) {
2658                         if (strcmp(helper_protocol, stdio_helper_protocols[i].name) == 0) {
2659                                 squid_stream(stdio_helper_protocols[i].mode, stdio_helper_protocols[i].fn);
2660                                 exit(0);
2661                         }
2662                 }
2663                 x_fprintf(x_stderr, "unknown helper protocol [%s]\n\nValid helper protools:\n\n", helper_protocol);
2664
2665                 for (i=0; i<NUM_HELPER_MODES; i++) {
2666                         x_fprintf(x_stderr, "%s\n", stdio_helper_protocols[i].name);
2667                 }
2668
2669                 exit(1);
2670         }
2671
2672         if (!opt_username || !*opt_username) {
2673                 x_fprintf(x_stderr, "username must be specified!\n\n");
2674                 poptPrintHelp(pc, stderr, 0);
2675                 exit(1);
2676         }
2677
2678         if (opt_challenge.length) {
2679                 if (!check_auth_crap()) {
2680                         exit(1);
2681                 }
2682                 exit(0);
2683         } 
2684
2685         if (!opt_password) {
2686                 opt_password = getpass("password: ");
2687         }
2688
2689         if (diagnostics) {
2690                 if (!diagnose_ntlm_auth()) {
2691                         return 1;
2692                 }
2693         } else {
2694                 fstring user;
2695
2696                 fstr_sprintf(user, "%s%c%s", opt_domain, winbind_separator(), opt_username);
2697                 if (!check_plaintext_auth(user, opt_password, True)) {
2698                         return 1;
2699                 }
2700         }
2701
2702         /* Exit code */
2703
2704         poptFreeContext(pc);
2705         TALLOC_FREE(frame);
2706         return 0;
2707 }