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