r17610: Added the ability for firefox to drive the winbindd
[samba.git] / source3 / utils / ntlm_auth.c
1 /* 
2    Unix SMB/CIFS implementation.
3
4    Winbind status program.
5
6    Copyright (C) Tim Potter      2000-2003
7    Copyright (C) Andrew Bartlett <abartlet@samba.org> 2003-2004
8    Copyright (C) Francesco Chemolli <kinkie@kame.usr.dsi.unimi.it> 2000 
9    Copyright (C) Robert O'Callahan 2006 (added cached credential code).
10
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                 ntlmssp_end(&client_ntlmssp_state);
1348                 return;
1349         }
1350
1351         if (spnego.negTokenTarg.negResult == SPNEGO_REJECT) {
1352                 x_fprintf(x_stdout, "NA\n");
1353                 ntlmssp_end(&client_ntlmssp_state);
1354                 return;
1355         }
1356
1357         if (spnego.negTokenTarg.negResult == SPNEGO_ACCEPT_COMPLETED) {
1358                 x_fprintf(x_stdout, "AF\n");
1359                 ntlmssp_end(&client_ntlmssp_state);
1360                 return;
1361         }
1362
1363         status = ntlmssp_update(client_ntlmssp_state,
1364                                        spnego.negTokenTarg.responseToken,
1365                                        &request);
1366                 
1367         if (!NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
1368                 DEBUG(1, ("Expected MORE_PROCESSING_REQUIRED from "
1369                           "ntlmssp_client_update, got: %s\n",
1370                           nt_errstr(status)));
1371                 x_fprintf(x_stdout, "BH\n");
1372                 data_blob_free(&request);
1373                 ntlmssp_end(&client_ntlmssp_state);
1374                 return;
1375         }
1376
1377         spnego.type = SPNEGO_NEG_TOKEN_TARG;
1378         spnego.negTokenTarg.negResult = SPNEGO_ACCEPT_INCOMPLETE;
1379         spnego.negTokenTarg.supportedMech = (char *)OID_NTLMSSP;
1380         spnego.negTokenTarg.responseToken = request;
1381         spnego.negTokenTarg.mechListMIC = null_blob;
1382         
1383         write_spnego_data(&to_server, &spnego);
1384         data_blob_free(&request);
1385
1386         to_server_base64 = base64_encode_data_blob(to_server);
1387         data_blob_free(&to_server);
1388         x_fprintf(x_stdout, "KK %s\n", to_server_base64);
1389         SAFE_FREE(to_server_base64);
1390         return;
1391 }
1392
1393 #ifdef HAVE_KRB5
1394
1395 static BOOL manage_client_krb5_init(SPNEGO_DATA spnego)
1396 {
1397         char *principal;
1398         DATA_BLOB tkt, to_server;
1399         DATA_BLOB session_key_krb5 = data_blob(NULL, 0);
1400         SPNEGO_DATA reply;
1401         char *reply_base64;
1402         int retval;
1403         
1404         const char *my_mechs[] = {OID_KERBEROS5_OLD, NULL};
1405         ssize_t len;
1406
1407         if ( (spnego.negTokenInit.mechListMIC.data == NULL) ||
1408              (spnego.negTokenInit.mechListMIC.length == 0) ) {
1409                 DEBUG(1, ("Did not get a principal for krb5\n"));
1410                 return False;
1411         }
1412
1413         principal = (char *)SMB_MALLOC(
1414                 spnego.negTokenInit.mechListMIC.length+1);
1415
1416         if (principal == NULL) {
1417                 DEBUG(1, ("Could not malloc principal\n"));
1418                 return False;
1419         }
1420
1421         memcpy(principal, spnego.negTokenInit.mechListMIC.data,
1422                spnego.negTokenInit.mechListMIC.length);
1423         principal[spnego.negTokenInit.mechListMIC.length] = '\0';
1424
1425         retval = cli_krb5_get_ticket(principal, 0, &tkt, &session_key_krb5, 0, NULL);
1426
1427         if (retval) {
1428
1429                 pstring user;
1430
1431                 /* Let's try to first get the TGT, for that we need a
1432                    password. */
1433
1434                 if (opt_password == NULL) {
1435                         DEBUG(10, ("Requesting password\n"));
1436                         x_fprintf(x_stdout, "PW\n");
1437                         return True;
1438                 }
1439
1440                 pstr_sprintf(user, "%s@%s", opt_username, opt_domain);
1441
1442                 if ((retval = kerberos_kinit_password(user, opt_password, 0, NULL))) {
1443                         DEBUG(10, ("Requesting TGT failed: %s\n", error_message(retval)));
1444                         return False;
1445                 }
1446
1447                 retval = cli_krb5_get_ticket(principal, 0, &tkt, &session_key_krb5, 0, NULL);
1448
1449                 if (retval) {
1450                         DEBUG(10, ("Kinit suceeded, but getting a ticket failed: %s\n", error_message(retval)));
1451                         return False;
1452                 }
1453         }
1454
1455         data_blob_free(&session_key_krb5);
1456
1457         ZERO_STRUCT(reply);
1458
1459         reply.type = SPNEGO_NEG_TOKEN_INIT;
1460         reply.negTokenInit.mechTypes = my_mechs;
1461         reply.negTokenInit.reqFlags = 0;
1462         reply.negTokenInit.mechToken = tkt;
1463         reply.negTokenInit.mechListMIC = data_blob(NULL, 0);
1464
1465         len = write_spnego_data(&to_server, &reply);
1466         data_blob_free(&tkt);
1467
1468         if (len == -1) {
1469                 DEBUG(1, ("Could not write SPNEGO data blob\n"));
1470                 return False;
1471         }
1472
1473         reply_base64 = base64_encode_data_blob(to_server);
1474         x_fprintf(x_stdout, "KK %s *\n", reply_base64);
1475
1476         SAFE_FREE(reply_base64);
1477         data_blob_free(&to_server);
1478         DEBUG(10, ("sent GSS-SPNEGO KERBEROS5 negTokenInit\n"));
1479         return True;
1480 }
1481
1482 static void manage_client_krb5_targ(SPNEGO_DATA spnego)
1483 {
1484         switch (spnego.negTokenTarg.negResult) {
1485         case SPNEGO_ACCEPT_INCOMPLETE:
1486                 DEBUG(1, ("Got a Kerberos negTokenTarg with ACCEPT_INCOMPLETE\n"));
1487                 x_fprintf(x_stdout, "BH\n");
1488                 break;
1489         case SPNEGO_ACCEPT_COMPLETED:
1490                 DEBUG(10, ("Accept completed\n"));
1491                 x_fprintf(x_stdout, "AF\n");
1492                 break;
1493         case SPNEGO_REJECT:
1494                 DEBUG(10, ("Rejected\n"));
1495                 x_fprintf(x_stdout, "NA\n");
1496                 break;
1497         default:
1498                 DEBUG(1, ("Got an invalid negTokenTarg\n"));
1499                 x_fprintf(x_stdout, "AF\n");
1500         }
1501 }
1502
1503 #endif
1504
1505 static void manage_gss_spnego_client_request(enum stdio_helper_mode stdio_helper_mode, 
1506                                              char *buf, int length) 
1507 {
1508         DATA_BLOB request;
1509         SPNEGO_DATA spnego;
1510         ssize_t len;
1511
1512         if (strlen(buf) <= 3) {
1513                 DEBUG(1, ("SPNEGO query [%s] too short\n", buf));
1514                 x_fprintf(x_stdout, "BH\n");
1515                 return;
1516         }
1517
1518         request = base64_decode_data_blob(buf+3);
1519
1520         if (strncmp(buf, "PW ", 3) == 0) {
1521
1522                 /* We asked for a password and obviously got it :-) */
1523
1524                 opt_password = SMB_STRNDUP((const char *)request.data, request.length);
1525                 
1526                 if (opt_password == NULL) {
1527                         DEBUG(1, ("Out of memory\n"));
1528                         x_fprintf(x_stdout, "BH\n");
1529                         data_blob_free(&request);
1530                         return;
1531                 }
1532
1533                 x_fprintf(x_stdout, "OK\n");
1534                 data_blob_free(&request);
1535                 return;
1536         }
1537
1538         if ( (strncmp(buf, "TT ", 3) != 0) &&
1539              (strncmp(buf, "AF ", 3) != 0) &&
1540              (strncmp(buf, "NA ", 3) != 0) ) {
1541                 DEBUG(1, ("SPNEGO request [%s] invalid\n", buf));
1542                 x_fprintf(x_stdout, "BH\n");
1543                 data_blob_free(&request);
1544                 return;
1545         }
1546
1547         /* So we got a server challenge to generate a SPNEGO
1548            client-to-server request... */
1549
1550         len = read_spnego_data(request, &spnego);
1551         data_blob_free(&request);
1552
1553         if (len == -1) {
1554                 DEBUG(1, ("Could not read SPNEGO data for [%s]\n", buf));
1555                 x_fprintf(x_stdout, "BH\n");
1556                 return;
1557         }
1558
1559         if (spnego.type == SPNEGO_NEG_TOKEN_INIT) {
1560
1561                 /* The server offers a list of mechanisms */
1562
1563                 const char **mechType = (const char **)spnego.negTokenInit.mechTypes;
1564
1565                 while (*mechType != NULL) {
1566
1567 #ifdef HAVE_KRB5
1568                         if ( (strcmp(*mechType, OID_KERBEROS5_OLD) == 0) ||
1569                              (strcmp(*mechType, OID_KERBEROS5) == 0) ) {
1570                                 if (manage_client_krb5_init(spnego))
1571                                         goto out;
1572                         }
1573 #endif
1574
1575                         if (strcmp(*mechType, OID_NTLMSSP) == 0) {
1576                                 if (manage_client_ntlmssp_init(spnego))
1577                                         goto out;
1578                         }
1579
1580                         mechType++;
1581                 }
1582
1583                 DEBUG(1, ("Server offered no compatible mechanism\n"));
1584                 x_fprintf(x_stdout, "BH\n");
1585                 return;
1586         }
1587
1588         if (spnego.type == SPNEGO_NEG_TOKEN_TARG) {
1589
1590                 if (spnego.negTokenTarg.supportedMech == NULL) {
1591                         /* On accept/reject Windows does not send the
1592                            mechanism anymore. Handle that here and
1593                            shut down the mechanisms. */
1594
1595                         switch (spnego.negTokenTarg.negResult) {
1596                         case SPNEGO_ACCEPT_COMPLETED:
1597                                 x_fprintf(x_stdout, "AF\n");
1598                                 break;
1599                         case SPNEGO_REJECT:
1600                                 x_fprintf(x_stdout, "NA\n");
1601                                 break;
1602                         default:
1603                                 DEBUG(1, ("Got a negTokenTarg with no mech and an "
1604                                           "unknown negResult: %d\n",
1605                                           spnego.negTokenTarg.negResult));
1606                                 x_fprintf(x_stdout, "BH\n");
1607                         }
1608
1609                         ntlmssp_end(&client_ntlmssp_state);
1610                         goto out;
1611                 }
1612
1613                 if (strcmp(spnego.negTokenTarg.supportedMech,
1614                            OID_NTLMSSP) == 0) {
1615                         manage_client_ntlmssp_targ(spnego);
1616                         goto out;
1617                 }
1618
1619 #if HAVE_KRB5
1620                 if (strcmp(spnego.negTokenTarg.supportedMech,
1621                            OID_KERBEROS5_OLD) == 0) {
1622                         manage_client_krb5_targ(spnego);
1623                         goto out;
1624                 }
1625 #endif
1626
1627         }
1628
1629         DEBUG(1, ("Got an SPNEGO token I could not handle [%s]!\n", buf));
1630         x_fprintf(x_stdout, "BH\n");
1631         return;
1632
1633  out:
1634         free_spnego_data(&spnego);
1635         return;
1636 }
1637
1638 static void manage_ntlm_server_1_request(enum stdio_helper_mode stdio_helper_mode, 
1639                                          char *buf, int length) 
1640 {
1641         char *request, *parameter;      
1642         static DATA_BLOB challenge;
1643         static DATA_BLOB lm_response;
1644         static DATA_BLOB nt_response;
1645         static char *full_username;
1646         static char *username;
1647         static char *domain;
1648         static char *plaintext_password;
1649         static BOOL ntlm_server_1_user_session_key;
1650         static BOOL ntlm_server_1_lm_session_key;
1651         
1652         if (strequal(buf, ".")) {
1653                 if (!full_username && !username) {      
1654                         x_fprintf(x_stdout, "Error: No username supplied!\n");
1655                 } else if (plaintext_password) {
1656                         /* handle this request as plaintext */
1657                         if (!full_username) {
1658                                 if (asprintf(&full_username, "%s%c%s", domain, winbind_separator(), username) == -1) {
1659                                         x_fprintf(x_stdout, "Error: Out of memory in asprintf!\n.\n");
1660                                         return;
1661                                 }
1662                         }
1663                         if (check_plaintext_auth(full_username, plaintext_password, False)) {
1664                                 x_fprintf(x_stdout, "Authenticated: Yes\n");
1665                         } else {
1666                                 x_fprintf(x_stdout, "Authenticated: No\n");
1667                         }
1668                 } else if (!lm_response.data && !nt_response.data) {
1669                         x_fprintf(x_stdout, "Error: No password supplied!\n");
1670                 } else if (!challenge.data) {   
1671                         x_fprintf(x_stdout, "Error: No lanman-challenge supplied!\n");
1672                 } else {
1673                         char *error_string = NULL;
1674                         uchar lm_key[8];
1675                         uchar user_session_key[16];
1676                         uint32 flags = 0;
1677
1678                         if (full_username && !username) {
1679                                 fstring fstr_user;
1680                                 fstring fstr_domain;
1681                                 
1682                                 if (!parse_ntlm_auth_domain_user(full_username, fstr_user, fstr_domain)) {
1683                                         /* username might be 'tainted', don't print into our new-line deleimianted stream */
1684                                         x_fprintf(x_stdout, "Error: Could not parse into domain and username\n");
1685                                 }
1686                                 SAFE_FREE(username);
1687                                 SAFE_FREE(domain);
1688                                 username = smb_xstrdup(fstr_user);
1689                                 domain = smb_xstrdup(fstr_domain);
1690                         }
1691
1692                         if (!domain) {
1693                                 domain = smb_xstrdup(get_winbind_domain());
1694                         }
1695
1696                         if (ntlm_server_1_lm_session_key) 
1697                                 flags |= WBFLAG_PAM_LMKEY;
1698                         
1699                         if (ntlm_server_1_user_session_key) 
1700                                 flags |= WBFLAG_PAM_USER_SESSION_KEY;
1701
1702                         if (!NT_STATUS_IS_OK(
1703                                     contact_winbind_auth_crap(username, 
1704                                                               domain, 
1705                                                               global_myname(),
1706                                                               &challenge, 
1707                                                               &lm_response, 
1708                                                               &nt_response, 
1709                                                               flags, 
1710                                                               lm_key, 
1711                                                               user_session_key,
1712                                                               &error_string,
1713                                                               NULL))) {
1714
1715                                 x_fprintf(x_stdout, "Authenticated: No\n");
1716                                 x_fprintf(x_stdout, "Authentication-Error: %s\n.\n", error_string);
1717                                 SAFE_FREE(error_string);
1718                         } else {
1719                                 static char zeros[16];
1720                                 char *hex_lm_key;
1721                                 char *hex_user_session_key;
1722
1723                                 x_fprintf(x_stdout, "Authenticated: Yes\n");
1724
1725                                 if (ntlm_server_1_lm_session_key 
1726                                     && (memcmp(zeros, lm_key, 
1727                                                sizeof(lm_key)) != 0)) {
1728                                         hex_lm_key = hex_encode(NULL,
1729                                                                 (const unsigned char *)lm_key,
1730                                                                 sizeof(lm_key));
1731                                         x_fprintf(x_stdout, "LANMAN-Session-Key: %s\n", hex_lm_key);
1732                                         TALLOC_FREE(hex_lm_key);
1733                                 }
1734
1735                                 if (ntlm_server_1_user_session_key 
1736                                     && (memcmp(zeros, user_session_key, 
1737                                                sizeof(user_session_key)) != 0)) {
1738                                         hex_user_session_key = hex_encode(NULL,
1739                                                                           (const unsigned char *)user_session_key, 
1740                                                                           sizeof(user_session_key));
1741                                         x_fprintf(x_stdout, "User-Session-Key: %s\n", hex_user_session_key);
1742                                         TALLOC_FREE(hex_user_session_key);
1743                                 }
1744                         }
1745                 }
1746                 /* clear out the state */
1747                 challenge = data_blob(NULL, 0);
1748                 nt_response = data_blob(NULL, 0);
1749                 lm_response = data_blob(NULL, 0);
1750                 SAFE_FREE(full_username);
1751                 SAFE_FREE(username);
1752                 SAFE_FREE(domain);
1753                 SAFE_FREE(plaintext_password);
1754                 ntlm_server_1_user_session_key = False;
1755                 ntlm_server_1_lm_session_key = False;
1756                 x_fprintf(x_stdout, ".\n");
1757
1758                 return;
1759         }
1760
1761         request = buf;
1762
1763         /* Indicates a base64 encoded structure */
1764         parameter = strstr_m(request, ":: ");
1765         if (!parameter) {
1766                 parameter = strstr_m(request, ": ");
1767                 
1768                 if (!parameter) {
1769                         DEBUG(0, ("Parameter not found!\n"));
1770                         x_fprintf(x_stdout, "Error: Parameter not found!\n.\n");
1771                         return;
1772                 }
1773                 
1774                 parameter[0] ='\0';
1775                 parameter++;
1776                 parameter[0] ='\0';
1777                 parameter++;
1778
1779         } else {
1780                 parameter[0] ='\0';
1781                 parameter++;
1782                 parameter[0] ='\0';
1783                 parameter++;
1784                 parameter[0] ='\0';
1785                 parameter++;
1786
1787                 base64_decode_inplace(parameter);
1788         }
1789
1790         if (strequal(request, "LANMAN-Challenge")) {
1791                 challenge = strhex_to_data_blob(NULL, parameter);
1792                 if (challenge.length != 8) {
1793                         x_fprintf(x_stdout, "Error: hex decode of %s failed! (got %d bytes, expected 8)\n.\n", 
1794                                   parameter,
1795                                   (int)challenge.length);
1796                         challenge = data_blob(NULL, 0);
1797                 }
1798         } else if (strequal(request, "NT-Response")) {
1799                 nt_response = strhex_to_data_blob(NULL, parameter);
1800                 if (nt_response.length < 24) {
1801                         x_fprintf(x_stdout, "Error: hex decode of %s failed! (only got %d bytes, needed at least 24)\n.\n", 
1802                                   parameter,
1803                                   (int)nt_response.length);
1804                         nt_response = data_blob(NULL, 0);
1805                 }
1806         } else if (strequal(request, "LANMAN-Response")) {
1807                 lm_response = strhex_to_data_blob(NULL, parameter);
1808                 if (lm_response.length != 24) {
1809                         x_fprintf(x_stdout, "Error: hex decode of %s failed! (got %d bytes, expected 24)\n.\n", 
1810                                   parameter,
1811                                   (int)lm_response.length);
1812                         lm_response = data_blob(NULL, 0);
1813                 }
1814         } else if (strequal(request, "Password")) {
1815                 plaintext_password = smb_xstrdup(parameter);
1816         } else if (strequal(request, "NT-Domain")) {
1817                 domain = smb_xstrdup(parameter);
1818         } else if (strequal(request, "Username")) {
1819                 username = smb_xstrdup(parameter);
1820         } else if (strequal(request, "Full-Username")) {
1821                 full_username = smb_xstrdup(parameter);
1822         } else if (strequal(request, "Request-User-Session-Key")) {
1823                 ntlm_server_1_user_session_key = strequal(parameter, "Yes");
1824         } else if (strequal(request, "Request-LanMan-Session-Key")) {
1825                 ntlm_server_1_lm_session_key = strequal(parameter, "Yes");
1826         } else {
1827                 x_fprintf(x_stdout, "Error: Unknown request %s\n.\n", request);
1828         }
1829 }
1830
1831 static void manage_ntlm_change_password_1_request(enum stdio_helper_mode helper_mode, char *buf, int length)
1832 {
1833         char *request, *parameter;      
1834         static DATA_BLOB new_nt_pswd;
1835         static DATA_BLOB old_nt_hash_enc;
1836         static DATA_BLOB new_lm_pswd;
1837         static DATA_BLOB old_lm_hash_enc;
1838         static char *full_username = NULL;
1839         static char *username = NULL;
1840         static char *domain = NULL;
1841         static char *newpswd =  NULL;
1842         static char *oldpswd = NULL;
1843
1844         if (strequal(buf, ".")) {
1845                 if(newpswd && oldpswd) {
1846                         uchar old_nt_hash[16];
1847                         uchar old_lm_hash[16];
1848                         uchar new_nt_hash[16];
1849                         uchar new_lm_hash[16];
1850
1851                         new_nt_pswd = data_blob(NULL, 516);
1852                         old_nt_hash_enc = data_blob(NULL, 16);
1853                         
1854                         /* Calculate the MD4 hash (NT compatible) of the
1855                          * password */
1856                         E_md4hash(oldpswd, old_nt_hash);
1857                         E_md4hash(newpswd, new_nt_hash);
1858
1859                         /* E_deshash returns false for 'long'
1860                            passwords (> 14 DOS chars).  
1861                            
1862                            Therefore, don't send a buffer
1863                            encrypted with the truncated hash
1864                            (it could allow an even easier
1865                            attack on the password)
1866
1867                            Likewise, obey the admin's restriction
1868                         */
1869
1870                         if (lp_client_lanman_auth() &&
1871                             E_deshash(newpswd, new_lm_hash) &&
1872                             E_deshash(oldpswd, old_lm_hash)) {
1873                                 new_lm_pswd = data_blob(NULL, 516);
1874                                 old_lm_hash_enc = data_blob(NULL, 16);
1875                                 encode_pw_buffer(new_lm_pswd.data, newpswd,
1876                                                  STR_UNICODE);
1877
1878                                 SamOEMhash(new_lm_pswd.data, old_nt_hash, 516);
1879                                 E_old_pw_hash(new_nt_hash, old_lm_hash,
1880                                               old_lm_hash_enc.data);
1881                         } else {
1882                                 new_lm_pswd.data = NULL;
1883                                 new_lm_pswd.length = 0;
1884                                 old_lm_hash_enc.data = NULL;
1885                                 old_lm_hash_enc.length = 0;
1886                         }
1887
1888                         encode_pw_buffer(new_nt_pswd.data, newpswd,
1889                                          STR_UNICODE);
1890         
1891                         SamOEMhash(new_nt_pswd.data, old_nt_hash, 516);
1892                         E_old_pw_hash(new_nt_hash, old_nt_hash,
1893                                       old_nt_hash_enc.data);
1894                 }
1895                 
1896                 if (!full_username && !username) {      
1897                         x_fprintf(x_stdout, "Error: No username supplied!\n");
1898                 } else if ((!new_nt_pswd.data || !old_nt_hash_enc.data) &&
1899                            (!new_lm_pswd.data || old_lm_hash_enc.data) ) {
1900                         x_fprintf(x_stdout, "Error: No NT or LM password "
1901                                   "blobs supplied!\n");
1902                 } else {
1903                         char *error_string = NULL;
1904                         
1905                         if (full_username && !username) {
1906                                 fstring fstr_user;
1907                                 fstring fstr_domain;
1908                                 
1909                                 if (!parse_ntlm_auth_domain_user(full_username,
1910                                                                  fstr_user,
1911                                                                  fstr_domain)) {
1912                                         /* username might be 'tainted', don't
1913                                          * print into our new-line
1914                                          * deleimianted stream */
1915                                         x_fprintf(x_stdout, "Error: Could not "
1916                                                   "parse into domain and "
1917                                                   "username\n");
1918                                         SAFE_FREE(username);
1919                                         username = smb_xstrdup(full_username);
1920                                 } else {
1921                                         SAFE_FREE(username);
1922                                         SAFE_FREE(domain);
1923                                         username = smb_xstrdup(fstr_user);
1924                                         domain = smb_xstrdup(fstr_domain);
1925                                 }
1926                                 
1927                         }
1928
1929                         if(!NT_STATUS_IS_OK(contact_winbind_change_pswd_auth_crap(
1930                                                     username, domain,
1931                                                     new_nt_pswd,
1932                                                     old_nt_hash_enc,
1933                                                     new_lm_pswd,
1934                                                     old_lm_hash_enc,
1935                                                     &error_string))) {
1936                                 x_fprintf(x_stdout, "Password-Change: No\n");
1937                                 x_fprintf(x_stdout, "Password-Change-Error: "
1938                                           "%s\n.\n", error_string);
1939                         } else {
1940                                 x_fprintf(x_stdout, "Password-Change: Yes\n");
1941                         }
1942
1943                         SAFE_FREE(error_string);
1944                 }
1945                 /* clear out the state */
1946                 new_nt_pswd = data_blob(NULL, 0);
1947                 old_nt_hash_enc = data_blob(NULL, 0);
1948                 new_lm_pswd = data_blob(NULL, 0);
1949                 old_nt_hash_enc = data_blob(NULL, 0);
1950                 SAFE_FREE(full_username);
1951                 SAFE_FREE(username);
1952                 SAFE_FREE(domain);
1953                 SAFE_FREE(newpswd);
1954                 SAFE_FREE(oldpswd);
1955                 x_fprintf(x_stdout, ".\n");
1956
1957                 return;
1958         }
1959
1960         request = buf;
1961
1962         /* Indicates a base64 encoded structure */
1963         parameter = strstr_m(request, ":: ");
1964         if (!parameter) {
1965                 parameter = strstr_m(request, ": ");
1966                 
1967                 if (!parameter) {
1968                         DEBUG(0, ("Parameter not found!\n"));
1969                         x_fprintf(x_stdout, "Error: Parameter not found!\n.\n");
1970                         return;
1971                 }
1972                 
1973                 parameter[0] ='\0';
1974                 parameter++;
1975                 parameter[0] ='\0';
1976                 parameter++;
1977         } else {
1978                 parameter[0] ='\0';
1979                 parameter++;
1980                 parameter[0] ='\0';
1981                 parameter++;
1982                 parameter[0] ='\0';
1983                 parameter++;
1984
1985                 base64_decode_inplace(parameter);
1986         }
1987
1988         if (strequal(request, "new-nt-password-blob")) {
1989                 new_nt_pswd = strhex_to_data_blob(NULL, parameter);
1990                 if (new_nt_pswd.length != 516) {
1991                         x_fprintf(x_stdout, "Error: hex decode of %s failed! "
1992                                   "(got %d bytes, expected 516)\n.\n", 
1993                                   parameter,
1994                                   (int)new_nt_pswd.length);
1995                         new_nt_pswd = data_blob(NULL, 0);
1996                 }
1997         } else if (strequal(request, "old-nt-hash-blob")) {
1998                 old_nt_hash_enc = strhex_to_data_blob(NULL, parameter);
1999                 if (old_nt_hash_enc.length != 16) {
2000                         x_fprintf(x_stdout, "Error: hex decode of %s failed! "
2001                                   "(got %d bytes, expected 16)\n.\n", 
2002                                   parameter,
2003                                   (int)old_nt_hash_enc.length);
2004                         old_nt_hash_enc = data_blob(NULL, 0);
2005                 }
2006         } else if (strequal(request, "new-lm-password-blob")) {
2007                 new_lm_pswd = strhex_to_data_blob(NULL, parameter);
2008                 if (new_lm_pswd.length != 516) {
2009                         x_fprintf(x_stdout, "Error: hex decode of %s failed! "
2010                                   "(got %d bytes, expected 516)\n.\n", 
2011                                   parameter,
2012                                   (int)new_lm_pswd.length);
2013                         new_lm_pswd = data_blob(NULL, 0);
2014                 }
2015         }
2016         else if (strequal(request, "old-lm-hash-blob")) {
2017                 old_lm_hash_enc = strhex_to_data_blob(NULL, parameter);
2018                 if (old_lm_hash_enc.length != 16)
2019                 {
2020                         x_fprintf(x_stdout, "Error: hex decode of %s failed! "
2021                                   "(got %d bytes, expected 16)\n.\n", 
2022                                   parameter,
2023                                   (int)old_lm_hash_enc.length);
2024                         old_lm_hash_enc = data_blob(NULL, 0);
2025                 }
2026         } else if (strequal(request, "nt-domain")) {
2027                 domain = smb_xstrdup(parameter);
2028         } else if(strequal(request, "username")) {
2029                 username = smb_xstrdup(parameter);
2030         } else if(strequal(request, "full-username")) {
2031                 username = smb_xstrdup(parameter);
2032         } else if(strequal(request, "new-password")) {
2033                 newpswd = smb_xstrdup(parameter);
2034         } else if (strequal(request, "old-password")) {
2035                 oldpswd = smb_xstrdup(parameter);
2036         } else {
2037                 x_fprintf(x_stdout, "Error: Unknown request %s\n.\n", request);
2038         }
2039 }
2040
2041 static void manage_squid_request(enum stdio_helper_mode helper_mode, stdio_helper_function fn) 
2042 {
2043         char buf[SQUID_BUFFER_SIZE+1];
2044         int length;
2045         char *c;
2046         static BOOL err;
2047
2048         /* this is not a typo - x_fgets doesn't work too well under squid */
2049         if (fgets(buf, sizeof(buf)-1, stdin) == NULL) {
2050                 if (ferror(stdin)) {
2051                         DEBUG(1, ("fgets() failed! dying..... errno=%d (%s)\n", ferror(stdin),
2052                                   strerror(ferror(stdin))));
2053                         
2054                         exit(1);    /* BIIG buffer */
2055                 }
2056                 exit(0);
2057         }
2058     
2059         c=(char *)memchr(buf,'\n',sizeof(buf)-1);
2060         if (c) {
2061                 *c = '\0';
2062                 length = c-buf;
2063         } else {
2064                 err = 1;
2065                 return;
2066         }
2067         if (err) {
2068                 DEBUG(2, ("Oversized message\n"));
2069                 x_fprintf(x_stderr, "ERR\n");
2070                 err = 0;
2071                 return;
2072         }
2073
2074         DEBUG(10, ("Got '%s' from squid (length: %d).\n",buf,length));
2075
2076         if (buf[0] == '\0') {
2077                 DEBUG(2, ("Invalid Request\n"));
2078                 x_fprintf(x_stderr, "ERR\n");
2079                 return;
2080         }
2081         
2082         fn(helper_mode, buf, length);
2083 }
2084
2085
2086 static void squid_stream(enum stdio_helper_mode stdio_mode, stdio_helper_function fn) {
2087         /* initialize FDescs */
2088         x_setbuf(x_stdout, NULL);
2089         x_setbuf(x_stderr, NULL);
2090         while(1) {
2091                 manage_squid_request(stdio_mode, fn);
2092         }
2093 }
2094
2095
2096 /* Authenticate a user with a challenge/response */
2097
2098 static BOOL check_auth_crap(void)
2099 {
2100         NTSTATUS nt_status;
2101         uint32 flags = 0;
2102         char lm_key[8];
2103         char user_session_key[16];
2104         char *hex_lm_key;
2105         char *hex_user_session_key;
2106         char *error_string;
2107         static uint8 zeros[16];
2108
2109         x_setbuf(x_stdout, NULL);
2110
2111         if (request_lm_key) 
2112                 flags |= WBFLAG_PAM_LMKEY;
2113
2114         if (request_user_session_key) 
2115                 flags |= WBFLAG_PAM_USER_SESSION_KEY;
2116
2117         flags |= WBFLAG_PAM_NT_STATUS_SQUASH;
2118
2119         nt_status = contact_winbind_auth_crap(opt_username, opt_domain, 
2120                                               opt_workstation,
2121                                               &opt_challenge, 
2122                                               &opt_lm_response, 
2123                                               &opt_nt_response, 
2124                                               flags,
2125                                               (unsigned char *)lm_key, 
2126                                               (unsigned char *)user_session_key, 
2127                                               &error_string, NULL);
2128
2129         if (!NT_STATUS_IS_OK(nt_status)) {
2130                 x_fprintf(x_stdout, "%s (0x%x)\n", 
2131                           error_string,
2132                           NT_STATUS_V(nt_status));
2133                 SAFE_FREE(error_string);
2134                 return False;
2135         }
2136
2137         if (request_lm_key 
2138             && (memcmp(zeros, lm_key, 
2139                        sizeof(lm_key)) != 0)) {
2140                 hex_lm_key = hex_encode(NULL, (const unsigned char *)lm_key,
2141                                         sizeof(lm_key));
2142                 x_fprintf(x_stdout, "LM_KEY: %s\n", hex_lm_key);
2143                 TALLOC_FREE(hex_lm_key);
2144         }
2145         if (request_user_session_key 
2146             && (memcmp(zeros, user_session_key, 
2147                        sizeof(user_session_key)) != 0)) {
2148                 hex_user_session_key = hex_encode(NULL, (const unsigned char *)user_session_key, 
2149                                                   sizeof(user_session_key));
2150                 x_fprintf(x_stdout, "NT_KEY: %s\n", hex_user_session_key);
2151                 TALLOC_FREE(hex_user_session_key);
2152         }
2153
2154         return True;
2155 }
2156
2157 /* Main program */
2158
2159 enum {
2160         OPT_USERNAME = 1000,
2161         OPT_DOMAIN,
2162         OPT_WORKSTATION,
2163         OPT_CHALLENGE,
2164         OPT_RESPONSE,
2165         OPT_LM,
2166         OPT_NT,
2167         OPT_PASSWORD,
2168         OPT_LM_KEY,
2169         OPT_USER_SESSION_KEY,
2170         OPT_DIAGNOSTICS,
2171         OPT_REQUIRE_MEMBERSHIP,
2172         OPT_USE_CACHED_CREDS
2173 };
2174
2175  int main(int argc, const char **argv)
2176 {
2177         int opt;
2178         static const char *helper_protocol;
2179         static int diagnostics;
2180
2181         static const char *hex_challenge;
2182         static const char *hex_lm_response;
2183         static const char *hex_nt_response;
2184
2185         poptContext pc;
2186
2187         /* NOTE: DO NOT change this interface without considering the implications!
2188            This is an external interface, which other programs will use to interact 
2189            with this helper.
2190         */
2191
2192         /* We do not use single-letter command abbreviations, because they harm future 
2193            interface stability. */
2194
2195         struct poptOption long_options[] = {
2196                 POPT_AUTOHELP
2197                 { "helper-protocol", 0, POPT_ARG_STRING, &helper_protocol, OPT_DOMAIN, "operate as a stdio-based helper", "helper protocol to use"},
2198                 { "username", 0, POPT_ARG_STRING, &opt_username, OPT_USERNAME, "username"},
2199                 { "domain", 0, POPT_ARG_STRING, &opt_domain, OPT_DOMAIN, "domain name"},
2200                 { "workstation", 0, POPT_ARG_STRING, &opt_workstation, OPT_WORKSTATION, "workstation"},
2201                 { "challenge", 0, POPT_ARG_STRING, &hex_challenge, OPT_CHALLENGE, "challenge (HEX encoded)"},
2202                 { "lm-response", 0, POPT_ARG_STRING, &hex_lm_response, OPT_LM, "LM Response to the challenge (HEX encoded)"},
2203                 { "nt-response", 0, POPT_ARG_STRING, &hex_nt_response, OPT_NT, "NT or NTLMv2 Response to the challenge (HEX encoded)"},
2204                 { "password", 0, POPT_ARG_STRING, &opt_password, OPT_PASSWORD, "User's plaintext password"},            
2205                 { "request-lm-key", 0, POPT_ARG_NONE, &request_lm_key, OPT_LM_KEY, "Retrieve LM session key"},
2206                 { "request-nt-key", 0, POPT_ARG_NONE, &request_user_session_key, OPT_USER_SESSION_KEY, "Retrieve User (NT) session key"},
2207                 { "use-cached-creds", 0, POPT_ARG_NONE, &use_cached_creds, OPT_USE_CACHED_CREDS, "Use cached credentials if no password is given"},
2208                 { "diagnostics", 0, POPT_ARG_NONE, &diagnostics, OPT_DIAGNOSTICS, "Perform diagnostics on the authentictaion chain"},
2209                 { "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" },
2210                 POPT_COMMON_SAMBA
2211                 POPT_TABLEEND
2212         };
2213
2214         /* Samba client initialisation */
2215         load_case_tables();
2216
2217         dbf = x_stderr;
2218         
2219         /* Samba client initialisation */
2220
2221         if (!lp_load(dyn_CONFIGFILE, True, False, False, True)) {
2222                 d_fprintf(stderr, "ntlm_auth: error opening config file %s. Error was %s\n",
2223                         dyn_CONFIGFILE, strerror(errno));
2224                 exit(1);
2225         }
2226
2227         /* Parse options */
2228
2229         pc = poptGetContext("ntlm_auth", argc, argv, long_options, 0);
2230
2231         /* Parse command line options */
2232
2233         if (argc == 1) {
2234                 poptPrintHelp(pc, stderr, 0);
2235                 return 1;
2236         }
2237
2238         pc = poptGetContext(NULL, argc, (const char **)argv, long_options, 
2239                             POPT_CONTEXT_KEEP_FIRST);
2240
2241         while((opt = poptGetNextOpt(pc)) != -1) {
2242                 switch (opt) {
2243                 case OPT_CHALLENGE:
2244                         opt_challenge = strhex_to_data_blob(NULL, hex_challenge);
2245                         if (opt_challenge.length != 8) {
2246                                 x_fprintf(x_stderr, "hex decode of %s failed! (only got %d bytes)\n", 
2247                                           hex_challenge,
2248                                           (int)opt_challenge.length);
2249                                 exit(1);
2250                         }
2251                         break;
2252                 case OPT_LM: 
2253                         opt_lm_response = strhex_to_data_blob(NULL, hex_lm_response);
2254                         if (opt_lm_response.length != 24) {
2255                                 x_fprintf(x_stderr, "hex decode of %s failed! (only got %d bytes)\n", 
2256                                           hex_lm_response,
2257                                           (int)opt_lm_response.length);
2258                                 exit(1);
2259                         }
2260                         break;
2261
2262                 case OPT_NT: 
2263                         opt_nt_response = strhex_to_data_blob(NULL, hex_nt_response);
2264                         if (opt_nt_response.length < 24) {
2265                                 x_fprintf(x_stderr, "hex decode of %s failed! (only got %d bytes)\n", 
2266                                           hex_nt_response,
2267                                           (int)opt_nt_response.length);
2268                                 exit(1);
2269                         }
2270                         break;
2271
2272                 case OPT_REQUIRE_MEMBERSHIP:
2273                         if (StrnCaseCmp("S-", require_membership_of, 2) == 0) {
2274                                 require_membership_of_sid = require_membership_of;
2275                         }
2276                         break;
2277                 }
2278         }
2279
2280         if (helper_protocol) {
2281                 int i;
2282                 for (i=0; i<NUM_HELPER_MODES; i++) {
2283                         if (strcmp(helper_protocol, stdio_helper_protocols[i].name) == 0) {
2284                                 squid_stream(stdio_helper_protocols[i].mode, stdio_helper_protocols[i].fn);
2285                                 exit(0);
2286                         }
2287                 }
2288                 x_fprintf(x_stderr, "unknown helper protocol [%s]\n\nValid helper protools:\n\n", helper_protocol);
2289
2290                 for (i=0; i<NUM_HELPER_MODES; i++) {
2291                         x_fprintf(x_stderr, "%s\n", stdio_helper_protocols[i].name);
2292                 }
2293
2294                 exit(1);
2295         }
2296
2297         if (!opt_username) {
2298                 x_fprintf(x_stderr, "username must be specified!\n\n");
2299                 poptPrintHelp(pc, stderr, 0);
2300                 exit(1);
2301         }
2302
2303         if (opt_domain == NULL) {
2304                 opt_domain = get_winbind_domain();
2305         }
2306
2307         if (opt_workstation == NULL) {
2308                 opt_workstation = "";
2309         }
2310
2311         if (opt_challenge.length) {
2312                 if (!check_auth_crap()) {
2313                         exit(1);
2314                 }
2315                 exit(0);
2316         } 
2317
2318         if (!opt_password) {
2319                 opt_password = getpass("password: ");
2320         }
2321
2322         if (diagnostics) {
2323                 if (!diagnose_ntlm_auth()) {
2324                         return 1;
2325                 }
2326         } else {
2327                 fstring user;
2328
2329                 fstr_sprintf(user, "%s%c%s", opt_domain, winbind_separator(), opt_username);
2330                 if (!check_plaintext_auth(user, opt_password, True)) {
2331                         return 1;
2332                 }
2333         }
2334
2335         /* Exit code */
2336
2337         poptFreeContext(pc);
2338         return 0;
2339 }