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