Merge 2610c05b5b95cc7036b3d6dfb894c6cfbdb68483 as Samba-4.0alpha16
[sfrench/samba-autobuild/.git] / source4 / 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
10    This program is free software; you can redistribute it and/or modify
11    it under the terms of the GNU General Public License as published by
12    the Free Software Foundation; either version 3 of the License, or
13    (at your option) any later version.
14    
15    This program is distributed in the hope that it will be useful,
16    but WITHOUT ANY WARRANTY; without even the implied warranty of
17    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18    GNU General Public License for more details.
19    
20    You should have received a copy of the GNU General Public License
21    along with this program.  If not, see <http://www.gnu.org/licenses/>.
22 */
23
24 #include "includes.h"
25 #include "system/filesys.h"
26 #include "lib/cmdline/popt_common.h"
27 #include <ldb.h>
28 #include "auth/credentials/credentials.h"
29 #include "auth/gensec/gensec.h"
30 #include "auth/auth.h"
31 #include "librpc/gen_ndr/ndr_netlogon.h"
32 #include "auth/auth_sam.h"
33 #include "libcli/auth/libcli_auth.h"
34 #include "libcli/security/security.h"
35 #include "lib/events/events.h"
36 #include "lib/messaging/messaging.h"
37 #include "lib/messaging/irpc.h"
38 #include "auth/ntlmssp/ntlmssp.h"
39 #include "param/param.h"
40
41 #define INITIAL_BUFFER_SIZE 300
42 #define MAX_BUFFER_SIZE 63000
43
44 enum stdio_helper_mode {
45         SQUID_2_4_BASIC,
46         SQUID_2_5_BASIC,
47         SQUID_2_5_NTLMSSP,
48         NTLMSSP_CLIENT_1,
49         GSS_SPNEGO_CLIENT,
50         GSS_SPNEGO_SERVER,
51         NTLM_SERVER_1,
52         NUM_HELPER_MODES
53 };
54
55 #define NTLM_AUTH_FLAG_USER_SESSION_KEY     0x0004
56 #define NTLM_AUTH_FLAG_LMKEY                0x0008
57
58
59 typedef void (*stdio_helper_function)(enum stdio_helper_mode stdio_helper_mode, 
60                                       struct loadparm_context *lp_ctx,
61                                       char *buf, int length, void **private1,
62                                       unsigned int mux_id, void **private2);
63
64 static void manage_squid_basic_request (enum stdio_helper_mode stdio_helper_mode, 
65                                         struct loadparm_context *lp_ctx,
66                                         char *buf, int length, void **private1,
67                                         unsigned int mux_id, void **private2);
68
69 static void manage_gensec_request (enum stdio_helper_mode stdio_helper_mode, 
70                                    struct loadparm_context *lp_ctx,
71                                    char *buf, int length, void **private1,
72                                    unsigned int mux_id, void **private2);
73
74 static void manage_ntlm_server_1_request (enum stdio_helper_mode stdio_helper_mode, 
75                                           struct loadparm_context *lp_ctx,
76                                           char *buf, int length, void **private1,
77                                           unsigned int mux_id, void **private2);
78
79 static void manage_squid_request(struct loadparm_context *lp_ctx,
80                                  enum stdio_helper_mode helper_mode, 
81                                  stdio_helper_function fn, void **private2);
82
83 static const struct {
84         enum stdio_helper_mode mode;
85         const char *name;
86         stdio_helper_function fn;
87 } stdio_helper_protocols[] = {
88         { SQUID_2_4_BASIC, "squid-2.4-basic", manage_squid_basic_request},
89         { SQUID_2_5_BASIC, "squid-2.5-basic", manage_squid_basic_request},
90         { SQUID_2_5_NTLMSSP, "squid-2.5-ntlmssp", manage_gensec_request},
91         { GSS_SPNEGO_CLIENT, "gss-spnego-client", manage_gensec_request},
92         { GSS_SPNEGO_SERVER, "gss-spnego", manage_gensec_request},
93         { NTLMSSP_CLIENT_1, "ntlmssp-client-1", manage_gensec_request},
94         { NTLM_SERVER_1, "ntlm-server-1", manage_ntlm_server_1_request},
95         { NUM_HELPER_MODES, NULL, NULL}
96 };
97
98 extern int winbindd_fd;
99
100 static const char *opt_username;
101 static const char *opt_domain;
102 static const char *opt_workstation;
103 static const char *opt_password;
104 static int opt_multiplex;
105 static int use_cached_creds;
106
107
108 static void mux_printf(unsigned int mux_id, const char *format, ...) PRINTF_ATTRIBUTE(2, 3);
109
110 static void mux_printf(unsigned int mux_id, const char *format, ...)
111 {
112         va_list ap;
113
114         if (opt_multiplex) {
115                 x_fprintf(x_stdout, "%d ", mux_id);
116         }
117
118         va_start(ap, format);
119         x_vfprintf(x_stdout, format, ap);
120         va_end(ap);
121 }
122
123
124
125 /* Copy of parse_domain_user from winbindd_util.c.  Parse a string of the
126    form DOMAIN/user into a domain and a user */
127
128 static bool parse_ntlm_auth_domain_user(const char *domuser, char **domain, 
129                                                                                 char **user, char winbind_separator)
130 {
131
132         char *p = strchr(domuser, winbind_separator);
133
134         if (!p) {
135                 return false;
136         }
137         
138         *user = smb_xstrdup(p+1);
139         *domain = smb_xstrdup(domuser);
140         (*domain)[PTR_DIFF(p, domuser)] = 0;
141
142         return true;
143 }
144
145
146 /* Authenticate a user with a plaintext password */
147
148 static bool check_plaintext_auth(const char *user, const char *pass, 
149                                  bool stdout_diagnostics)
150 {
151         return (strcmp(pass, opt_password) == 0);
152 }
153
154 /* authenticate a user with an encrypted username/password */
155
156 static NTSTATUS local_pw_check_specified(struct loadparm_context *lp_ctx,
157                                          const char *username, 
158                                          const char *domain, 
159                                          const char *workstation,
160                                          const DATA_BLOB *challenge, 
161                                          const DATA_BLOB *lm_response, 
162                                          const DATA_BLOB *nt_response, 
163                                          uint32_t flags, 
164                                          DATA_BLOB *lm_session_key, 
165                                          DATA_BLOB *user_session_key, 
166                                          char **error_string, 
167                                          char **unix_name) 
168 {
169         NTSTATUS nt_status;
170         struct samr_Password lm_pw, nt_pw;
171         struct samr_Password *lm_pwd, *nt_pwd;
172         TALLOC_CTX *mem_ctx = talloc_init("local_pw_check_specified");
173         if (!mem_ctx) {
174                 nt_status = NT_STATUS_NO_MEMORY;
175         } else {
176                 
177                 E_md4hash(opt_password, nt_pw.hash);
178                 if (E_deshash(opt_password, lm_pw.hash)) {
179                         lm_pwd = &lm_pw;
180                 } else {
181                         lm_pwd = NULL;
182                 }
183                 nt_pwd = &nt_pw;
184                 
185                 
186                 nt_status = ntlm_password_check(mem_ctx, 
187                                                 lpcfg_lanman_auth(lp_ctx),
188                                                 lpcfg_ntlm_auth(lp_ctx),
189                                                 MSV1_0_ALLOW_SERVER_TRUST_ACCOUNT |
190                                                 MSV1_0_ALLOW_WORKSTATION_TRUST_ACCOUNT,
191                                                 challenge,
192                                                 lm_response,
193                                                 nt_response,
194                                                 username,
195                                                 username,
196                                                 domain,
197                                                 lm_pwd, nt_pwd, user_session_key, lm_session_key);
198                 
199                 if (NT_STATUS_IS_OK(nt_status)) {
200                         if (unix_name) {
201                                 if (asprintf(unix_name, "%s%c%s", domain,
202                                              *lpcfg_winbind_separator(lp_ctx),
203                                              username) < 0) {
204                                         nt_status = NT_STATUS_NO_MEMORY;
205                                 }
206                         }
207                 } else {
208                         DEBUG(3, ("Login for user [%s]\\[%s]@[%s] failed due to [%s]\n", 
209                                   domain, username, workstation, 
210                                   nt_errstr(nt_status)));
211                 }
212                 talloc_free(mem_ctx);
213         }
214         if (error_string) {
215                 *error_string = strdup(nt_errstr(nt_status));
216         }
217         return nt_status;
218         
219         
220 }
221
222 static void manage_squid_basic_request(enum stdio_helper_mode stdio_helper_mode, 
223                                        struct loadparm_context *lp_ctx,
224                                        char *buf, int length, void **private1,
225                                        unsigned int mux_id, void **private2) 
226 {
227         char *user, *pass;      
228         user=buf;
229         
230         pass = memchr(buf, ' ', length);
231         if (!pass) {
232                 DEBUG(2, ("Password not found. Denying access\n"));
233                 mux_printf(mux_id, "ERR\n");
234                 return;
235         }
236         *pass='\0';
237         pass++;
238         
239         if (stdio_helper_mode == SQUID_2_5_BASIC) {
240                 rfc1738_unescape(user);
241                 rfc1738_unescape(pass);
242         }
243         
244         if (check_plaintext_auth(user, pass, false)) {
245                 mux_printf(mux_id, "OK\n");
246         } else {
247                 mux_printf(mux_id, "ERR\n");
248         }
249 }
250
251 /* This is a bit hairy, but the basic idea is to do a password callback
252    to the calling application.  The callback comes from within gensec */
253
254 static void manage_gensec_get_pw_request(enum stdio_helper_mode stdio_helper_mode, 
255                                          struct loadparm_context *lp_ctx,
256                                          char *buf, int length, void **private1,
257                                          unsigned int mux_id, void **password)  
258 {
259         DATA_BLOB in;
260         if (strlen(buf) < 2) {
261                 DEBUG(1, ("query [%s] invalid", buf));
262                 mux_printf(mux_id, "BH Query invalid\n");
263                 return;
264         }
265
266         if (strlen(buf) > 3) {
267                 in = base64_decode_data_blob(buf + 3);
268         } else {
269                 in = data_blob(NULL, 0);
270         }
271
272         if (strncmp(buf, "PW ", 3) == 0) {
273
274                 *password = talloc_strndup(*private1 /* hopefully the right gensec context, useful to use for talloc */,
275                                            (const char *)in.data, in.length);
276                 
277                 if (*password == NULL) {
278                         DEBUG(1, ("Out of memory\n"));
279                         mux_printf(mux_id, "BH Out of memory\n");
280                         data_blob_free(&in);
281                         return;
282                 }
283
284                 mux_printf(mux_id, "OK\n");
285                 data_blob_free(&in);
286                 return;
287         }
288         DEBUG(1, ("Asked for (and expected) a password\n"));
289         mux_printf(mux_id, "BH Expected a password\n");
290         data_blob_free(&in);
291 }
292
293 /** 
294  * Callback for password credentials.  This is not async, and when
295  * GENSEC and the credentials code is made async, it will look rather
296  * different.
297  */
298
299 static const char *get_password(struct cli_credentials *credentials) 
300 {
301         char *password = NULL;
302         
303         /* Ask for a password */
304         mux_printf((unsigned int)(uintptr_t)credentials->priv_data, "PW\n");
305         credentials->priv_data = NULL;
306
307         manage_squid_request(cmdline_lp_ctx, NUM_HELPER_MODES /* bogus */, manage_gensec_get_pw_request, (void **)&password);
308         return password;
309 }
310
311 /**
312  Check if a string is part of a list.
313 **/
314 static bool in_list(const char *s, const char *list, bool casesensitive)
315 {
316         char *tok;
317         size_t tok_len = 1024;
318         const char *p=list;
319
320         if (!list)
321                 return false;
322
323         tok = (char *)malloc(tok_len);
324         if (!tok) {
325                 return false;
326         }
327
328         while (next_token(&p, tok, LIST_SEP, tok_len)) {
329                 if ((casesensitive?strcmp:strcasecmp_m)(tok,s) == 0) {
330                         free(tok);
331                         return true;
332                 }
333         }
334         free(tok);
335         return false;
336 }
337
338 static void gensec_want_feature_list(struct gensec_security *state, char* feature_list)
339 {
340         if (in_list("NTLMSSP_FEATURE_SESSION_KEY", feature_list, true)) {
341                 DEBUG(10, ("want GENSEC_FEATURE_SESSION_KEY\n"));
342                 gensec_want_feature(state, GENSEC_FEATURE_SESSION_KEY);
343         }
344         if (in_list("NTLMSSP_FEATURE_SIGN", feature_list, true)) {
345                 DEBUG(10, ("want GENSEC_FEATURE_SIGN\n"));
346                 gensec_want_feature(state, GENSEC_FEATURE_SIGN);
347         }
348         if (in_list("NTLMSSP_FEATURE_SEAL", feature_list, true)) {
349                 DEBUG(10, ("want GENSEC_FEATURE_SEAL\n"));
350                 gensec_want_feature(state, GENSEC_FEATURE_SEAL);
351         }
352 }
353
354 static void manage_gensec_request(enum stdio_helper_mode stdio_helper_mode, 
355                                   struct loadparm_context *lp_ctx,
356                                   char *buf, int length, void **private1,
357                                   unsigned int mux_id, void **private2) 
358 {
359         DATA_BLOB in;
360         DATA_BLOB out = data_blob(NULL, 0);
361         char *out_base64 = NULL;
362         const char *reply_arg = NULL;
363         struct gensec_ntlm_state {
364                 struct gensec_security *gensec_state;
365                 const char *set_password;
366         };
367         struct gensec_ntlm_state *state;
368         struct tevent_context *ev;
369         struct imessaging_context *msg;
370
371         NTSTATUS nt_status;
372         bool first = false;
373         const char *reply_code;
374         struct cli_credentials *creds;
375
376         static char *want_feature_list = NULL;
377         static DATA_BLOB session_key;
378
379         TALLOC_CTX *mem_ctx;
380
381         if (*private1) {
382                 state = (struct gensec_ntlm_state *)*private1;
383         } else {
384                 state = talloc_zero(NULL, struct gensec_ntlm_state);
385                 if (!state) {
386                         mux_printf(mux_id, "BH No Memory\n");
387                         exit(1);
388                 }
389                 *private1 = state;
390                 if (opt_password) {
391                         state->set_password = opt_password;
392                 }
393         }
394         
395         if (strlen(buf) < 2) {
396                 DEBUG(1, ("query [%s] invalid", buf));
397                 mux_printf(mux_id, "BH Query invalid\n");
398                 return;
399         }
400
401         if (strlen(buf) > 3) {
402                 if(strncmp(buf, "SF ", 3) == 0) {
403                         DEBUG(10, ("Setting flags to negotiate\n"));
404                         talloc_free(want_feature_list);
405                         want_feature_list = talloc_strndup(state, buf+3, strlen(buf)-3);
406                         mux_printf(mux_id, "OK\n");
407                         return;
408                 }
409                 in = base64_decode_data_blob(buf + 3);
410         } else {
411                 in = data_blob(NULL, 0);
412         }
413
414         if (strncmp(buf, "YR", 2) == 0) {
415                 if (state->gensec_state) {
416                         talloc_free(state->gensec_state);
417                         state->gensec_state = NULL;
418                 }
419         } else if ( (strncmp(buf, "OK", 2) == 0)) {
420                 /* Just return BH, like ntlm_auth from Samba 3 does. */
421                 mux_printf(mux_id, "BH Command expected\n");
422                 data_blob_free(&in);
423                 return;
424         } else if ( (strncmp(buf, "TT ", 3) != 0) &&
425                     (strncmp(buf, "KK ", 3) != 0) &&
426                     (strncmp(buf, "AF ", 3) != 0) &&
427                     (strncmp(buf, "NA ", 3) != 0) && 
428                     (strncmp(buf, "UG", 2) != 0) && 
429                     (strncmp(buf, "PW ", 3) != 0) &&
430                     (strncmp(buf, "GK", 2) != 0) &&
431                     (strncmp(buf, "GF", 2) != 0)) {
432                 DEBUG(1, ("SPNEGO request [%s] invalid\n", buf));
433                 mux_printf(mux_id, "BH SPNEGO request invalid\n");
434                 data_blob_free(&in);
435                 return;
436         }
437
438         ev = s4_event_context_init(state);
439         if (!ev) {
440                 exit(1);
441         }
442
443         mem_ctx = talloc_named(NULL, 0, "manage_gensec_request internal mem_ctx");
444
445         /* setup gensec */
446         if (!(state->gensec_state)) {
447                 switch (stdio_helper_mode) {
448                 case GSS_SPNEGO_CLIENT:
449                 case NTLMSSP_CLIENT_1:
450                         /* setup the client side */
451
452                         nt_status = gensec_client_start(NULL, &state->gensec_state, ev, 
453                                                         lpcfg_gensec_settings(NULL, lp_ctx));
454                         if (!NT_STATUS_IS_OK(nt_status)) {
455                                 talloc_free(mem_ctx);
456                                 exit(1);
457                         }
458
459                         break;
460                 case GSS_SPNEGO_SERVER:
461                 case SQUID_2_5_NTLMSSP:
462                 {
463                         const char *winbind_method[] = { "winbind", NULL };
464                         struct auth4_context *auth_context;
465
466                         msg = imessaging_client_init(state, lpcfg_imessaging_path(state, lp_ctx), ev);
467                         if (!msg) {
468                                 talloc_free(mem_ctx);
469                                 exit(1);
470                         }
471                         nt_status = auth_context_create_methods(mem_ctx, 
472                                                                 winbind_method,
473                                                                 ev, 
474                                                                 msg, 
475                                                                 lp_ctx,
476                                                                 NULL,
477                                                                 &auth_context);
478         
479                         if (!NT_STATUS_IS_OK(nt_status)) {
480                                 talloc_free(mem_ctx);
481                                 exit(1);
482                         }
483                         
484                         if (!NT_STATUS_IS_OK(gensec_server_start(state, ev, 
485                                                                  lpcfg_gensec_settings(state, lp_ctx),
486                                                                  auth_context, &state->gensec_state))) {
487                                 talloc_free(mem_ctx);
488                                 exit(1);
489                         }
490                         break;
491                 }
492                 default:
493                         talloc_free(mem_ctx);
494                         abort();
495                 }
496
497                 creds = cli_credentials_init(state->gensec_state);
498                 cli_credentials_set_conf(creds, lp_ctx);
499                 if (opt_username) {
500                         cli_credentials_set_username(creds, opt_username, CRED_SPECIFIED);
501                 }
502                 if (opt_domain) {
503                         cli_credentials_set_domain(creds, opt_domain, CRED_SPECIFIED);
504                 }
505                 if (state->set_password) {
506                         cli_credentials_set_password(creds, state->set_password, CRED_SPECIFIED);
507                 } else {
508                         cli_credentials_set_password_callback(creds, get_password);
509                         creds->priv_data = (void*)(uintptr_t)mux_id;
510                 }
511                 if (opt_workstation) {
512                         cli_credentials_set_workstation(creds, opt_workstation, CRED_SPECIFIED);
513                 }
514                 
515                 switch (stdio_helper_mode) {
516                 case GSS_SPNEGO_SERVER:
517                 case SQUID_2_5_NTLMSSP:
518                         cli_credentials_set_machine_account(creds, lp_ctx);
519                         break;
520                 default:
521                         break;
522                 }
523
524                 gensec_set_credentials(state->gensec_state, creds);
525                 gensec_want_feature_list(state->gensec_state, want_feature_list);
526
527                 switch (stdio_helper_mode) {
528                 case GSS_SPNEGO_CLIENT:
529                 case GSS_SPNEGO_SERVER:
530                         nt_status = gensec_start_mech_by_oid(state->gensec_state, GENSEC_OID_SPNEGO);
531                         if (!in.length) {
532                                 first = true;
533                         }
534                         break;
535                 case NTLMSSP_CLIENT_1:
536                         if (!in.length) {
537                                 first = true;
538                         }
539                         /* fall through */
540                 case SQUID_2_5_NTLMSSP:
541                         nt_status = gensec_start_mech_by_oid(state->gensec_state, GENSEC_OID_NTLMSSP);
542                         break;
543                 default:
544                         talloc_free(mem_ctx);
545                         abort();
546                 }
547
548                 if (!NT_STATUS_IS_OK(nt_status)) {
549                         DEBUG(1, ("GENSEC mech failed to start: %s\n", nt_errstr(nt_status)));
550                         mux_printf(mux_id, "BH GENSEC mech failed to start\n");
551                         talloc_free(mem_ctx);
552                         return;
553                 }
554
555         }
556
557         /* update */
558
559         if (strncmp(buf, "PW ", 3) == 0) {
560                 state->set_password = talloc_strndup(state,
561                                                      (const char *)in.data, 
562                                                      in.length);
563                 
564                 cli_credentials_set_password(gensec_get_credentials(state->gensec_state),
565                                              state->set_password,
566                                              CRED_SPECIFIED);
567                 mux_printf(mux_id, "OK\n");
568                 data_blob_free(&in);
569                 talloc_free(mem_ctx);
570                 return;
571         }
572
573         if (strncmp(buf, "UG", 2) == 0) {
574                 int i;
575                 char *grouplist = NULL;
576                 struct auth_session_info *session_info;
577
578                 nt_status = gensec_session_info(state->gensec_state, &session_info); 
579                 if (!NT_STATUS_IS_OK(nt_status)) {
580                         DEBUG(1, ("gensec_session_info failed: %s\n", nt_errstr(nt_status)));
581                         mux_printf(mux_id, "BH %s\n", nt_errstr(nt_status));
582                         data_blob_free(&in);
583                         talloc_free(mem_ctx);
584                         return;
585                 }
586                 
587                 /* get the string onto the context */
588                 grouplist = talloc_strdup(mem_ctx, "");
589                 
590                 for (i=0; i<session_info->security_token->num_sids; i++) {
591                         struct security_token *token = session_info->security_token; 
592                         const char *sidstr = dom_sid_string(session_info, 
593                                                             &token->sids[i]);
594                         grouplist = talloc_asprintf_append_buffer(grouplist, "%s,", sidstr);
595                 }
596
597                 mux_printf(mux_id, "GL %s\n", grouplist);
598                 talloc_free(session_info);
599                 data_blob_free(&in);
600                 talloc_free(mem_ctx);
601                 return;
602         }
603
604         if (strncmp(buf, "GK", 2) == 0) {
605                 char *base64_key;
606                 DEBUG(10, ("Requested session key\n"));
607                 nt_status = gensec_session_key(state->gensec_state, &session_key);
608                 if(!NT_STATUS_IS_OK(nt_status)) {
609                         DEBUG(1, ("gensec_session_key failed: %s\n", nt_errstr(nt_status)));
610                         mux_printf(mux_id, "BH No session key\n");
611                         talloc_free(mem_ctx);
612                         return;
613                 } else {
614                         base64_key = base64_encode_data_blob(state, session_key);
615                         mux_printf(mux_id, "GK %s\n", base64_key);
616                         talloc_free(base64_key);
617                 }
618                 talloc_free(mem_ctx);
619                 return;
620         }
621
622         if (strncmp(buf, "GF", 2) == 0) {
623                 struct ntlmssp_state *ntlmssp_state;
624                 uint32_t neg_flags;
625
626                 ntlmssp_state = talloc_get_type(state->gensec_state->private_data,
627                                 struct ntlmssp_state);
628                 neg_flags = ntlmssp_state->neg_flags;
629
630                 DEBUG(10, ("Requested negotiated feature flags\n"));
631                 mux_printf(mux_id, "GF 0x%08x\n", neg_flags);
632                 return;
633         }
634
635         nt_status = gensec_update(state->gensec_state, mem_ctx, in, &out);
636         
637         /* don't leak 'bad password'/'no such user' info to the network client */
638         nt_status = nt_status_squash(nt_status);
639
640         if (out.length) {
641                 out_base64 = base64_encode_data_blob(mem_ctx, out);
642         } else {
643                 out_base64 = NULL;
644         }
645
646         if (NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
647                 reply_arg = "*";
648                 if (first) {
649                         reply_code = "YR";
650                 } else if (state->gensec_state->gensec_role == GENSEC_CLIENT) { 
651                         reply_code = "KK";
652                 } else if (state->gensec_state->gensec_role == GENSEC_SERVER) { 
653                         reply_code = "TT";
654                 } else {
655                         abort();
656                 }
657
658
659         } else if (NT_STATUS_EQUAL(nt_status, NT_STATUS_ACCESS_DENIED)) {
660                 reply_code = "BH NT_STATUS_ACCESS_DENIED";
661                 reply_arg = nt_errstr(nt_status);
662                 DEBUG(1, ("GENSEC login failed: %s\n", nt_errstr(nt_status)));
663         } else if (NT_STATUS_EQUAL(nt_status, NT_STATUS_UNSUCCESSFUL)) {
664                 reply_code = "BH NT_STATUS_UNSUCCESSFUL";
665                 reply_arg = nt_errstr(nt_status);
666                 DEBUG(1, ("GENSEC login failed: %s\n", nt_errstr(nt_status)));
667         } else if (!NT_STATUS_IS_OK(nt_status)) {
668                 reply_code = "NA";
669                 reply_arg = nt_errstr(nt_status);
670                 DEBUG(1, ("GENSEC login failed: %s\n", nt_errstr(nt_status)));
671         } else if /* OK */ (state->gensec_state->gensec_role == GENSEC_SERVER) {
672                 struct auth_session_info *session_info;
673
674                 nt_status = gensec_session_info(state->gensec_state, &session_info);
675                 if (!NT_STATUS_IS_OK(nt_status)) {
676                         reply_code = "BH Failed to retrive session info";
677                         reply_arg = nt_errstr(nt_status);
678                         DEBUG(1, ("GENSEC failed to retrieve the session info: %s\n", nt_errstr(nt_status)));
679                 } else {
680
681                         reply_code = "AF";
682                         reply_arg = talloc_asprintf(state->gensec_state, 
683                                                     "%s%s%s", session_info->info->domain_name,
684                                                     lpcfg_winbind_separator(lp_ctx), session_info->info->account_name);
685                         talloc_free(session_info);
686                 }
687         } else if (state->gensec_state->gensec_role == GENSEC_CLIENT) {
688                 reply_code = "AF";
689                 reply_arg = out_base64;
690         } else {
691                 abort();
692         }
693
694         switch (stdio_helper_mode) {
695         case GSS_SPNEGO_SERVER:
696                 mux_printf(mux_id, "%s %s %s\n", reply_code, 
697                           out_base64 ? out_base64 : "*", 
698                           reply_arg ? reply_arg : "*");
699                 break;
700         default:
701                 if (out_base64) {
702                         mux_printf(mux_id, "%s %s\n", reply_code, out_base64);
703                 } else if (reply_arg) {
704                         mux_printf(mux_id, "%s %s\n", reply_code, reply_arg);
705                 } else {
706                         mux_printf(mux_id, "%s\n", reply_code);
707                 }
708         }
709
710         talloc_free(mem_ctx);
711         return;
712 }
713
714 static void manage_ntlm_server_1_request(enum stdio_helper_mode stdio_helper_mode, 
715                                          struct loadparm_context *lp_ctx,
716                                          char *buf, int length, void **private1,
717                                          unsigned int mux_id, void **private2) 
718 {
719         char *request, *parameter;      
720         static DATA_BLOB challenge;
721         static DATA_BLOB lm_response;
722         static DATA_BLOB nt_response;
723         static char *full_username;
724         static char *username;
725         static char *domain;
726         static char *plaintext_password;
727         static bool ntlm_server_1_user_session_key;
728         static bool ntlm_server_1_lm_session_key;
729         
730         if (strequal(buf, ".")) {
731                 if (!full_username && !username) {      
732                         mux_printf(mux_id, "Error: No username supplied!\n");
733                 } else if (plaintext_password) {
734                         /* handle this request as plaintext */
735                         if (!full_username) {
736                                 if (asprintf(&full_username, "%s%c%s", domain, *lpcfg_winbind_separator(lp_ctx), username) < 0) {
737                                         mux_printf(mux_id, "Error: Out of memory in asprintf!\n.\n");
738                                         return;
739                                 }
740                         }
741                         if (check_plaintext_auth(full_username, plaintext_password, false)) {
742                                 mux_printf(mux_id, "Authenticated: Yes\n");
743                         } else {
744                                 mux_printf(mux_id, "Authenticated: No\n");
745                         }
746                 } else if (!lm_response.data && !nt_response.data) {
747                         mux_printf(mux_id, "Error: No password supplied!\n");
748                 } else if (!challenge.data) {   
749                         mux_printf(mux_id, "Error: No lanman-challenge supplied!\n");
750                 } else {
751                         char *error_string = NULL;
752                         DATA_BLOB lm_key;
753                         DATA_BLOB user_session_key;
754                         uint32_t flags = 0;
755
756                         if (full_username && !username) {
757                                 SAFE_FREE(username);
758                                 SAFE_FREE(domain);
759                                 if (!parse_ntlm_auth_domain_user(full_username, &username, 
760                                                                                                  &domain, 
761                                                                                                  *lpcfg_winbind_separator(lp_ctx))) {
762                                         /* username might be 'tainted', don't print into our new-line deleimianted stream */
763                                         mux_printf(mux_id, "Error: Could not parse into domain and username\n");
764                                 }
765                         }
766
767                         if (!domain) {
768                                 domain = smb_xstrdup(lpcfg_workgroup(lp_ctx));
769                         }
770
771                         if (ntlm_server_1_lm_session_key) 
772                                 flags |= NTLM_AUTH_FLAG_LMKEY;
773                         
774                         if (ntlm_server_1_user_session_key) 
775                                 flags |= NTLM_AUTH_FLAG_USER_SESSION_KEY;
776
777                         if (!NT_STATUS_IS_OK(
778                                     local_pw_check_specified(lp_ctx,
779                                                              username, 
780                                                               domain, 
781                                                               lpcfg_netbios_name(lp_ctx),
782                                                               &challenge, 
783                                                               &lm_response, 
784                                                               &nt_response, 
785                                                               flags, 
786                                                               &lm_key, 
787                                                               &user_session_key,
788                                                               &error_string,
789                                                               NULL))) {
790
791                                 mux_printf(mux_id, "Authenticated: No\n");
792                                 mux_printf(mux_id, "Authentication-Error: %s\n.\n", error_string);
793                                 SAFE_FREE(error_string);
794                         } else {
795                                 static char zeros[16];
796                                 char *hex_lm_key;
797                                 char *hex_user_session_key;
798
799                                 mux_printf(mux_id, "Authenticated: Yes\n");
800
801                                 if (ntlm_server_1_lm_session_key 
802                                     && lm_key.length 
803                                     && (memcmp(zeros, lm_key.data, 
804                                                                 lm_key.length) != 0)) {
805                                         hex_encode(lm_key.data,
806                                                    lm_key.length,
807                                                    &hex_lm_key);
808                                         mux_printf(mux_id, "LANMAN-Session-Key: %s\n", hex_lm_key);
809                                         SAFE_FREE(hex_lm_key);
810                                 }
811
812                                 if (ntlm_server_1_user_session_key 
813                                     && user_session_key.length 
814                                     && (memcmp(zeros, user_session_key.data, 
815                                                user_session_key.length) != 0)) {
816                                         hex_encode(user_session_key.data, 
817                                                    user_session_key.length, 
818                                                    &hex_user_session_key);
819                                         mux_printf(mux_id, "User-Session-Key: %s\n", hex_user_session_key);
820                                         SAFE_FREE(hex_user_session_key);
821                                 }
822                         }
823                 }
824                 /* clear out the state */
825                 challenge = data_blob(NULL, 0);
826                 nt_response = data_blob(NULL, 0);
827                 lm_response = data_blob(NULL, 0);
828                 SAFE_FREE(full_username);
829                 SAFE_FREE(username);
830                 SAFE_FREE(domain);
831                 SAFE_FREE(plaintext_password);
832                 ntlm_server_1_user_session_key = false;
833                 ntlm_server_1_lm_session_key = false;
834                 mux_printf(mux_id, ".\n");
835
836                 return;
837         }
838
839         request = buf;
840
841         /* Indicates a base64 encoded structure */
842         parameter = strstr(request, ":: ");
843         if (!parameter) {
844                 parameter = strstr(request, ": ");
845                 
846                 if (!parameter) {
847                         DEBUG(0, ("Parameter not found!\n"));
848                         mux_printf(mux_id, "Error: Parameter not found!\n.\n");
849                         return;
850                 }
851                 
852                 parameter[0] ='\0';
853                 parameter++;
854                 parameter[0] ='\0';
855                 parameter++;
856
857         } else {
858                 parameter[0] ='\0';
859                 parameter++;
860                 parameter[0] ='\0';
861                 parameter++;
862                 parameter[0] ='\0';
863                 parameter++;
864
865                 base64_decode_inplace(parameter);
866         }
867
868         if (strequal(request, "LANMAN-Challenge")) {
869                 challenge = strhex_to_data_blob(NULL, parameter);
870                 if (challenge.length != 8) {
871                         mux_printf(mux_id, "Error: hex decode of %s failed! (got %d bytes, expected 8)\n.\n", 
872                                   parameter,
873                                   (int)challenge.length);
874                         challenge = data_blob(NULL, 0);
875                 }
876         } else if (strequal(request, "NT-Response")) {
877                 nt_response = strhex_to_data_blob(NULL, parameter);
878                 if (nt_response.length < 24) {
879                         mux_printf(mux_id, "Error: hex decode of %s failed! (only got %d bytes, needed at least 24)\n.\n", 
880                                   parameter,
881                                   (int)nt_response.length);
882                         nt_response = data_blob(NULL, 0);
883                 }
884         } else if (strequal(request, "LANMAN-Response")) {
885                 lm_response = strhex_to_data_blob(NULL, parameter);
886                 if (lm_response.length != 24) {
887                         mux_printf(mux_id, "Error: hex decode of %s failed! (got %d bytes, expected 24)\n.\n", 
888                                   parameter,
889                                   (int)lm_response.length);
890                         lm_response = data_blob(NULL, 0);
891                 }
892         } else if (strequal(request, "Password")) {
893                 plaintext_password = smb_xstrdup(parameter);
894         } else if (strequal(request, "NT-Domain")) {
895                 domain = smb_xstrdup(parameter);
896         } else if (strequal(request, "Username")) {
897                 username = smb_xstrdup(parameter);
898         } else if (strequal(request, "Full-Username")) {
899                 full_username = smb_xstrdup(parameter);
900         } else if (strequal(request, "Request-User-Session-Key")) {
901                 ntlm_server_1_user_session_key = strequal(parameter, "Yes");
902         } else if (strequal(request, "Request-LanMan-Session-Key")) {
903                 ntlm_server_1_lm_session_key = strequal(parameter, "Yes");
904         } else {
905                 mux_printf(mux_id, "Error: Unknown request %s\n.\n", request);
906         }
907 }
908
909 static void manage_squid_request(struct loadparm_context *lp_ctx, enum stdio_helper_mode helper_mode,
910                                  stdio_helper_function fn, void **private2) 
911 {
912         char *buf;
913         char tmp[INITIAL_BUFFER_SIZE+1];
914         unsigned int mux_id = 0;
915         int length, buf_size = 0;
916         char *c;
917         struct mux_private {
918                 unsigned int max_mux;
919                 void **private_pointers;
920         };
921
922         static struct mux_private *mux_private;
923         static void *normal_private;
924         void **private1;
925
926         buf = talloc_strdup(NULL, "");
927
928         if (buf == NULL) {
929                 DEBUG(0, ("Failed to allocate memory for reading the input "
930                           "buffer.\n"));
931                 x_fprintf(x_stdout, "ERR\n");
932                 return;
933         }
934
935         do {
936                 /* this is not a typo - x_fgets doesn't work too well under
937                  * squid */
938                 if (fgets(tmp, INITIAL_BUFFER_SIZE, stdin) == NULL) {
939                         if (ferror(stdin)) {
940                                 DEBUG(1, ("fgets() failed! dying..... errno=%d "
941                                           "(%s)\n", ferror(stdin),
942                                           strerror(ferror(stdin))));
943
944                                 exit(1);    /* BIIG buffer */
945                         }
946                         exit(0);
947                 }
948
949                 buf = talloc_strdup_append_buffer(buf, tmp);
950                 buf_size += INITIAL_BUFFER_SIZE;
951
952                 if (buf_size > MAX_BUFFER_SIZE) {
953                         DEBUG(0, ("Invalid Request (too large)\n"));
954                         x_fprintf(x_stdout, "ERR\n");
955                         talloc_free(buf);
956                         return;
957                 }
958
959                 c = strchr(buf, '\n');
960         } while (c == NULL);
961
962         *c = '\0';
963         length = c-buf;
964
965         DEBUG(10, ("Got '%s' from squid (length: %d).\n",buf,length));
966
967         if (buf[0] == '\0') {
968                 DEBUG(0, ("Invalid Request (empty)\n"));
969                 x_fprintf(x_stdout, "ERR\n");
970                 talloc_free(buf);
971                 return;
972         }
973
974         if (opt_multiplex) {
975                 if (sscanf(buf, "%u ", &mux_id) != 1) {
976                         DEBUG(0, ("Invalid Request - no multiplex id\n"));
977                         x_fprintf(x_stdout, "ERR\n");
978                         talloc_free(buf);
979                         return;
980                 }
981                 if (!mux_private) {
982                         mux_private = talloc(NULL, struct mux_private);
983                         mux_private->max_mux = 0;
984                         mux_private->private_pointers = NULL;
985                 }
986                 
987                 c=strchr(buf,' ');
988                 if (!c) {
989                         DEBUG(0, ("Invalid Request - no data after multiplex id\n"));
990                         x_fprintf(x_stdout, "ERR\n");
991                         talloc_free(buf);
992                         return;
993                 }
994                 c++;
995                 if (mux_id >= mux_private->max_mux) {
996                         unsigned int prev_max = mux_private->max_mux;
997                         mux_private->max_mux = mux_id + 1;
998                         mux_private->private_pointers
999                                 = talloc_realloc(mux_private, 
1000                                                    mux_private->private_pointers, 
1001                                                    void *, mux_private->max_mux);
1002                         memset(&mux_private->private_pointers[prev_max], '\0',  
1003                                (sizeof(*mux_private->private_pointers) * (mux_private->max_mux - prev_max))); 
1004                 };
1005
1006                 private1 = &mux_private->private_pointers[mux_id];
1007         } else {
1008                 c = buf;
1009                 private1 = &normal_private;
1010         }
1011
1012         fn(helper_mode, lp_ctx, c, length, private1, mux_id, private2);
1013         talloc_free(buf);
1014 }
1015
1016 static void squid_stream(struct loadparm_context *lp_ctx,
1017                          enum stdio_helper_mode stdio_mode, 
1018                          stdio_helper_function fn) {
1019         /* initialize FDescs */
1020         x_setbuf(x_stdout, NULL);
1021         x_setbuf(x_stderr, NULL);
1022         while(1) {
1023                 manage_squid_request(lp_ctx, stdio_mode, fn, NULL);
1024         }
1025 }
1026
1027
1028 /* Main program */
1029
1030 enum {
1031         OPT_USERNAME = 1000,
1032         OPT_DOMAIN,
1033         OPT_WORKSTATION,
1034         OPT_CHALLENGE,
1035         OPT_RESPONSE,
1036         OPT_LM,
1037         OPT_NT,
1038         OPT_PASSWORD,
1039         OPT_LM_KEY,
1040         OPT_USER_SESSION_KEY,
1041         OPT_DIAGNOSTICS,
1042         OPT_REQUIRE_MEMBERSHIP,
1043         OPT_MULTIPLEX,
1044         OPT_USE_CACHED_CREDS,
1045 };
1046
1047 int main(int argc, const char **argv)
1048 {
1049         static const char *helper_protocol;
1050         int opt;
1051
1052         poptContext pc;
1053
1054         /* NOTE: DO NOT change this interface without considering the implications!
1055            This is an external interface, which other programs will use to interact 
1056            with this helper.
1057         */
1058
1059         /* We do not use single-letter command abbreviations, because they harm future 
1060            interface stability. */
1061
1062         struct poptOption long_options[] = {
1063                 POPT_AUTOHELP
1064                 { "helper-protocol", 0, POPT_ARG_STRING, &helper_protocol, OPT_DOMAIN, "operate as a stdio-based helper", "helper protocol to use"},
1065                 { "domain", 0, POPT_ARG_STRING, &opt_domain, OPT_DOMAIN, "domain name"},
1066                 { "workstation", 0, POPT_ARG_STRING, &opt_workstation, OPT_WORKSTATION, "workstation"},
1067                 { "username", 0, POPT_ARG_STRING, &opt_username, OPT_PASSWORD, "Username"},             
1068                 { "password", 0, POPT_ARG_STRING, &opt_password, OPT_PASSWORD, "User's plaintext password"},            
1069                 { "multiplex", 0, POPT_ARG_NONE, &opt_multiplex, OPT_MULTIPLEX, "Multiplex Mode"},
1070                 { "use-cached-creds", 0, POPT_ARG_NONE, &use_cached_creds, OPT_USE_CACHED_CREDS, "silently ignored for compatibility reasons"},
1071                 POPT_COMMON_SAMBA
1072                 POPT_COMMON_VERSION
1073                 { NULL }
1074         };
1075
1076         /* Samba client initialisation */
1077
1078         setup_logging(NULL, DEBUG_STDERR);
1079
1080         /* Parse options */
1081
1082         pc = poptGetContext("ntlm_auth", argc, argv, long_options, 0);
1083
1084         /* Parse command line options */
1085
1086         if (argc == 1) {
1087                 poptPrintHelp(pc, stderr, 0);
1088                 return 1;
1089         }
1090
1091         pc = poptGetContext(NULL, argc, (const char **)argv, long_options, 
1092                             POPT_CONTEXT_KEEP_FIRST);
1093
1094         while((opt = poptGetNextOpt(pc)) != -1) {
1095                 if (opt < -1) {
1096                         break;
1097                 }
1098         }
1099         if (opt < -1) {
1100                 fprintf(stderr, "%s: %s\n",
1101                         poptBadOption(pc, POPT_BADOPTION_NOALIAS),
1102                         poptStrerror(opt));
1103                 return 1;
1104         }
1105
1106         gensec_init();
1107
1108         if (opt_domain == NULL) {
1109                 opt_domain = lpcfg_workgroup(cmdline_lp_ctx);
1110         }
1111
1112         if (helper_protocol) {
1113                 int i;
1114                 for (i=0; i<NUM_HELPER_MODES; i++) {
1115                         if (strcmp(helper_protocol, stdio_helper_protocols[i].name) == 0) {
1116                                 squid_stream(cmdline_lp_ctx, stdio_helper_protocols[i].mode, stdio_helper_protocols[i].fn);
1117                                 exit(0);
1118                         }
1119                 }
1120                 x_fprintf(x_stderr, "unknown helper protocol [%s]\n\nValid helper protools:\n\n", helper_protocol);
1121
1122                 for (i=0; i<NUM_HELPER_MODES; i++) {
1123                         x_fprintf(x_stderr, "%s\n", stdio_helper_protocols[i].name);
1124                 }
1125
1126                 exit(1);
1127         }
1128
1129         if (!opt_username) {
1130                 x_fprintf(x_stderr, "username must be specified!\n\n");
1131                 poptPrintHelp(pc, stderr, 0);
1132                 exit(1);
1133         }
1134
1135         if (opt_workstation == NULL) {
1136                 opt_workstation = lpcfg_netbios_name(cmdline_lp_ctx);
1137         }
1138
1139         if (!opt_password) {
1140                 opt_password = getpass("password: ");
1141         }
1142
1143         {
1144                 char *user;
1145
1146                 if (asprintf(&user, "%s%c%s", opt_domain,
1147                              *lpcfg_winbind_separator(cmdline_lp_ctx),
1148                              opt_username) < 0) {
1149                         return 1;
1150                 }
1151                 if (!check_plaintext_auth(user, opt_password, true)) {
1152                         return 1;
1153                 }
1154         }
1155
1156         /* Exit code */
1157
1158         poptFreeContext(pc);
1159         return 0;
1160 }