r3463: separated out some more headers (asn_1.h, messages.h, dlinklist.h and ioctl.h)
[samba.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 2 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, write to the Free Software
22    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
23 */
24
25 #include "includes.h"
26 #include "system/passwd.h"
27 #include "lib/cmdline/popt_common.h"
28 #include "auth/auth.h"
29 #include "asn_1.h"
30
31 #undef DBGC_CLASS
32 #define DBGC_CLASS DBGC_WINBIND
33
34 #define SQUID_BUFFER_SIZE 2010
35
36 enum stdio_helper_mode {
37         SQUID_2_4_BASIC,
38         SQUID_2_5_BASIC,
39         SQUID_2_5_NTLMSSP,
40         NTLMSSP_CLIENT_1,
41         GSS_SPNEGO_CLIENT,
42         GSS_SPNEGO_SERVER,
43         NTLM_SERVER_1,
44         NUM_HELPER_MODES
45 };
46
47 #define NTLM_AUTH_FLAG_USER_SESSION_KEY     0x0004
48 #define NTLM_AUTH_FLAG_LMKEY                0x0008
49
50
51 typedef void (*stdio_helper_function)(enum stdio_helper_mode stdio_helper_mode, 
52                                       char *buf, int length, void **private);
53
54 static void manage_squid_basic_request (enum stdio_helper_mode stdio_helper_mode, 
55                                         char *buf, int length, void **private);
56
57 static void manage_gensec_request (enum stdio_helper_mode stdio_helper_mode, 
58                                    char *buf, int length, void **private);
59
60 static void manage_ntlm_server_1_request (enum stdio_helper_mode stdio_helper_mode, 
61                                           char *buf, int length, void **private);
62
63 static void manage_squid_request(enum stdio_helper_mode helper_mode, 
64                                  stdio_helper_function fn, void *private);
65
66 static const struct {
67         enum stdio_helper_mode mode;
68         const char *name;
69         stdio_helper_function fn;
70 } stdio_helper_protocols[] = {
71         { SQUID_2_4_BASIC, "squid-2.4-basic", manage_squid_basic_request},
72         { SQUID_2_5_BASIC, "squid-2.5-basic", manage_squid_basic_request},
73         { SQUID_2_5_NTLMSSP, "squid-2.5-ntlmssp", manage_gensec_request},
74         { GSS_SPNEGO_CLIENT, "gss-spnego-client", manage_gensec_request},
75         { GSS_SPNEGO_SERVER, "gss-spnego", manage_gensec_request},
76         { NTLMSSP_CLIENT_1, "ntlmssp-client-1", manage_gensec_request},
77         { NTLM_SERVER_1, "ntlm-server-1", manage_ntlm_server_1_request},
78         { NUM_HELPER_MODES, NULL, NULL}
79 };
80
81 extern int winbindd_fd;
82
83 const char *opt_username;
84 const char *opt_domain;
85 const char *opt_workstation;
86 const char *opt_password;
87
88
89 /* Copy of parse_domain_user from winbindd_util.c.  Parse a string of the
90    form DOMAIN/user into a domain and a user */
91
92 static BOOL parse_ntlm_auth_domain_user(const char *domuser, fstring domain, 
93                                      fstring user)
94 {
95
96         char *p = strchr(domuser,*lp_winbind_separator());
97
98         if (!p) {
99                 return False;
100         }
101         
102         fstrcpy(user, p+1);
103         fstrcpy(domain, domuser);
104         domain[PTR_DIFF(p, domuser)] = 0;
105
106         return True;
107 }
108
109 /* Authenticate a user with a plaintext password */
110
111 static BOOL check_plaintext_auth(const char *user, const char *pass, 
112                                  BOOL stdout_diagnostics)
113 {
114         return (strcmp(pass, opt_password) == 0);
115 }
116
117 /* authenticate a user with an encrypted username/password */
118
119 static NTSTATUS local_pw_check_specified(const char *username, 
120                                          const char *domain, 
121                                          const char *workstation,
122                                          const DATA_BLOB *challenge, 
123                                          const DATA_BLOB *lm_response, 
124                                          const DATA_BLOB *nt_response, 
125                                          uint32 flags, 
126                                          DATA_BLOB *lm_session_key, 
127                                          DATA_BLOB *user_session_key, 
128                                          char **error_string, 
129                                          char **unix_name) 
130 {
131         NTSTATUS nt_status;
132         uint8_t lm_pw[16], nt_pw[16];
133         uint8_t *lm_pwd, *nt_pwd;
134         TALLOC_CTX *mem_ctx = talloc_init("local_pw_check_specified");
135         if (!mem_ctx) {
136                 nt_status = NT_STATUS_NO_MEMORY;
137         } else {
138                 
139                 E_md4hash(opt_password, nt_pw);
140                 if (E_deshash(opt_password, lm_pw)) {
141                         lm_pwd = lm_pw;
142                 } else {
143                         lm_pwd = NULL;
144                 }
145                 nt_pwd = nt_pw;
146                 
147                 
148                 nt_status = ntlm_password_check(mem_ctx, 
149                                                 challenge,
150                                                 lm_response,
151                                                 nt_response,
152                                                 NULL, NULL,
153                                                 username,
154                                                 username,
155                                                 domain,
156                                                 lm_pwd, nt_pwd, user_session_key, lm_session_key);
157                 
158                 if (NT_STATUS_IS_OK(nt_status)) {
159                         if (unix_name) {
160                                 asprintf(unix_name, 
161                                          "%s%c%s", domain,
162                                          *lp_winbind_separator(), 
163                                          username);
164                         }
165                 } else {
166                         DEBUG(3, ("Login for user [%s]\\[%s]@[%s] failed due to [%s]\n", 
167                                   domain, username, workstation, 
168                                   nt_errstr(nt_status)));
169                 }
170                 talloc_destroy(mem_ctx);
171         }
172         if (error_string) {
173                 *error_string = strdup(nt_errstr(nt_status));
174         }
175         return nt_status;
176         
177         
178 }
179
180 static void manage_squid_basic_request(enum stdio_helper_mode stdio_helper_mode, 
181                                        char *buf, int length, void **private) 
182 {
183         char *user, *pass;      
184         user=buf;
185         
186         pass=memchr(buf,' ',length);
187         if (!pass) {
188                 DEBUG(2, ("Password not found. Denying access\n"));
189                 x_fprintf(x_stdout, "ERR\n");
190                 return;
191         }
192         *pass='\0';
193         pass++;
194         
195         if (stdio_helper_mode == SQUID_2_5_BASIC) {
196                 rfc1738_unescape(user);
197                 rfc1738_unescape(pass);
198         }
199         
200         if (check_plaintext_auth(user, pass, False)) {
201                 x_fprintf(x_stdout, "OK\n");
202         } else {
203                 x_fprintf(x_stdout, "ERR\n");
204         }
205 }
206
207 /* This is a bit hairy, but the basic idea is to do a password callback
208    to the calling application.  The callback comes from within gensec */
209
210 static void manage_gensec_get_pw_request(enum stdio_helper_mode stdio_helper_mode, 
211                                          char *buf, int length, void **private)  
212 {
213         DATA_BLOB in;
214         struct gensec_security **gensec_state = (struct gensec_security **)private;
215         if (strlen(buf) < 2) {
216                 DEBUG(1, ("query [%s] invalid", buf));
217                 x_fprintf(x_stdout, "BH\n");
218                 return;
219         }
220
221         if (strlen(buf) > 3) {
222                 in = base64_decode_data_blob(buf + 3);
223         } else {
224                 in = data_blob(NULL, 0);
225         }
226
227         if (strncmp(buf, "PW ", 3) == 0) {
228
229                 (*gensec_state)->password_callback_private = talloc_strndup((*gensec_state), 
230                                                                             (const char *)in.data, in.length);
231                 
232                 if ((*gensec_state)->password_callback_private == NULL) {
233                         DEBUG(1, ("Out of memory\n"));
234                         x_fprintf(x_stdout, "BH\n");
235                         data_blob_free(&in);
236                         return;
237                 }
238
239                 x_fprintf(x_stdout, "OK\n");
240                 data_blob_free(&in);
241                 return;
242         }
243         DEBUG(1, ("Asked for (and expected) a password\n"));
244         x_fprintf(x_stdout, "BH\n");
245         data_blob_free(&in);
246 }
247
248 /* 
249  * Callback for gensec, to ask the calling application for a password.  Uses the above function
250  * for the stdio part of this.
251  */
252
253 static NTSTATUS get_password(struct gensec_security *gensec_security, TALLOC_CTX *mem_ctx, 
254                              char **password) 
255 {
256         *password = NULL;
257         
258         /* Ask for a password */
259         x_fprintf(x_stdout, "PW\n");
260         gensec_security->password_callback_private = NULL;
261
262         manage_squid_request(NUM_HELPER_MODES /* bogus */, manage_gensec_get_pw_request, &gensec_security);
263         *password = (char *)gensec_security->password_callback_private;
264         if (*password) {
265                 return NT_STATUS_OK;
266         } else {
267                 return NT_STATUS_INVALID_PARAMETER;
268         }
269 }
270
271 static void manage_gensec_request(enum stdio_helper_mode stdio_helper_mode, 
272                                   char *buf, int length, void **private) 
273 {
274         DATA_BLOB in;
275         DATA_BLOB out = data_blob(NULL, 0);
276         char *out_base64 = NULL;
277         const char *reply_arg = NULL;
278         struct gensec_security **gensec_state = (struct gensec_security **)private;
279         NTSTATUS nt_status;
280         BOOL first = False;
281         const char *reply_code;
282         
283         if (strlen(buf) < 2) {
284                 DEBUG(1, ("query [%s] invalid", buf));
285                 x_fprintf(x_stdout, "BH\n");
286                 return;
287         }
288
289         if (strlen(buf) > 3) {
290                 in = base64_decode_data_blob(buf + 3);
291         } else {
292                 in = data_blob(NULL, 0);
293         }
294
295         if (strncmp(buf, "YR", 2) == 0) {
296                 if (gensec_state && *gensec_state) {
297                         gensec_end(gensec_state);
298                         *gensec_state = NULL;
299                 }
300         } else if ( (strncmp(buf, "OK", 2) == 0)) {
301                 /* do nothing */
302                 data_blob_free(&in);
303                 return;
304         } else if ( (strncmp(buf, "TT ", 3) != 0) &&
305                     (strncmp(buf, "KK ", 3) != 0) &&
306                     (strncmp(buf, "AF ", 3) != 0) &&
307                     (strncmp(buf, "NA ", 3) != 0) && 
308                     (strncmp(buf, "PW ", 3) != 0)) {
309                 DEBUG(1, ("SPNEGO request [%s] invalid\n", buf));
310                 x_fprintf(x_stdout, "BH\n");
311                 data_blob_free(&in);
312                 return;
313         }
314
315         /* setup gensec */
316         if (!(gensec_state && *gensec_state)) {
317                 switch (stdio_helper_mode) {
318                 case GSS_SPNEGO_CLIENT:
319                 case NTLMSSP_CLIENT_1:
320                         /* setup the client side */
321                         
322                         if (!NT_STATUS_IS_OK(gensec_client_start(NULL, gensec_state))) {
323                                 exit(1);
324                         }
325                         gensec_set_username(*gensec_state, opt_username);
326                         gensec_set_domain(*gensec_state, opt_domain);           
327                         if (opt_password) {
328                                 if (!NT_STATUS_IS_OK(gensec_set_password(*gensec_state, opt_password))) {
329                                         DEBUG(1, ("Out of memory\n"));
330                                         x_fprintf(x_stdout, "BH\n");
331                                         data_blob_free(&in);
332                                         return;
333                                 }
334                         } else {
335                                 gensec_set_password_callback(*gensec_state, get_password, NULL);
336                         }
337                         
338                         break;
339                 case GSS_SPNEGO_SERVER:
340                 case SQUID_2_5_NTLMSSP:
341                         if (!NT_STATUS_IS_OK(gensec_server_start(NULL, gensec_state))) {
342                                 exit(1);
343                         }
344                         break;
345                 default:
346                         abort();
347                 }
348
349                 switch (stdio_helper_mode) {
350                 case GSS_SPNEGO_CLIENT:
351                 case GSS_SPNEGO_SERVER:
352                         nt_status = gensec_start_mech_by_oid(*gensec_state, OID_SPNEGO);
353                         break;
354                 case NTLMSSP_CLIENT_1:
355                 case SQUID_2_5_NTLMSSP:
356                         nt_status = gensec_start_mech_by_oid(*gensec_state, OID_NTLMSSP);
357                         break;
358                 default:
359                         abort();
360                 }
361
362                 if (!NT_STATUS_IS_OK(nt_status)) {
363                         DEBUG(1, ("SPENGO login failed to initialise: %s\n", nt_errstr(nt_status)));
364                         x_fprintf(x_stdout, "BH\n");
365                         return;
366                 }
367                 if (!in.length) {
368                         first = True;
369                 }
370         }
371         
372         if (strncmp(buf, "PW ", 3) == 0) {
373
374                 if (!NT_STATUS_IS_OK(gensec_set_password(*gensec_state, 
375                                                          talloc_strndup((*gensec_state), 
376                                                                         (const char *)in.data, 
377                                                                         in.length)))) {
378                         DEBUG(1, ("Out of memory\n"));
379                         x_fprintf(x_stdout, "BH\n");
380                         data_blob_free(&in);
381                         return;
382                 }
383
384                 x_fprintf(x_stdout, "OK\n");
385                 data_blob_free(&in);
386                 return;
387         }
388
389         /* update */
390
391         nt_status = gensec_update(*gensec_state, NULL, in, &out);
392         
393         /* don't leak 'bad password'/'no such user' info to the network client */
394         nt_status = nt_status_squash(nt_status);
395
396         if (out.length) {
397                 out_base64 = base64_encode_data_blob(out);
398         } else {
399                 out_base64 = NULL;
400         }
401         if (NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
402                 reply_arg = "*";
403                 if (first) {
404                         reply_code = "YR";
405                 } else if ((*gensec_state)->gensec_role == GENSEC_CLIENT) { 
406                         reply_code = "KK";
407                 } else if ((*gensec_state)->gensec_role == GENSEC_SERVER) { 
408                         reply_code = "TT";
409                 } else {
410                         abort();
411                 }
412
413
414         } else if (NT_STATUS_EQUAL(nt_status, NT_STATUS_ACCESS_DENIED)) {
415                 reply_code = "BH";
416                 reply_arg = nt_errstr(nt_status);
417                 DEBUG(1, ("GENSEC login failed: %s\n", nt_errstr(nt_status)));
418         } else if (NT_STATUS_EQUAL(nt_status, NT_STATUS_UNSUCCESSFUL)) {
419                 reply_code = "BH";
420                 reply_arg = nt_errstr(nt_status);
421                 DEBUG(1, ("GENSEC login failed: %s\n", nt_errstr(nt_status)));
422         } else if (!NT_STATUS_IS_OK(nt_status)) {
423                 reply_code = "NA";
424                 reply_arg = nt_errstr(nt_status);
425                 DEBUG(1, ("GENSEC login failed: %s\n", nt_errstr(nt_status)));
426         } else if /* OK */ ((*gensec_state)->gensec_role == GENSEC_SERVER) {
427                 struct auth_session_info *session_info;
428
429                 nt_status = gensec_session_info(*gensec_state, &session_info);
430                 if (!NT_STATUS_IS_OK(nt_status)) {
431                         reply_code = "BH";
432                         reply_arg = nt_errstr(nt_status);
433                         DEBUG(1, ("GENSEC failed to retreive the session info: %s\n", nt_errstr(nt_status)));
434                 } else {
435
436                         reply_code = "AF";
437                         reply_arg = talloc_asprintf(*gensec_state, 
438                                                     "%s%s%s", session_info->server_info->domain, 
439                                                     lp_winbind_separator(), session_info->server_info->account_name);
440                         talloc_free(session_info);
441                 }
442         } else if ((*gensec_state)->gensec_role == GENSEC_CLIENT) {
443                 reply_code = "AF";
444                 reply_arg = NULL;
445         } else {
446                 abort();
447         }
448
449         switch (stdio_helper_mode) {
450         case GSS_SPNEGO_SERVER:
451                 x_fprintf(x_stdout, "%s %s %s\n", reply_code, 
452                           out_base64 ? out_base64 : "*", 
453                           reply_arg ? reply_arg : "*");
454                 break;
455         default:
456                 if (out_base64) {
457                         x_fprintf(x_stdout, "%s %s\n", reply_code, out_base64);
458                 } else if (reply_arg) {
459                         x_fprintf(x_stdout, "%s %s\n", reply_code, reply_arg);
460                 } else {
461                         x_fprintf(x_stdout, "%s\n", reply_code);
462                 }
463         }
464
465         SAFE_FREE(out_base64);
466         return;
467 }
468
469 static void manage_ntlm_server_1_request(enum stdio_helper_mode stdio_helper_mode, 
470                                          char *buf, int length, void **private) 
471 {
472         char *request, *parameter;      
473         static DATA_BLOB challenge;
474         static DATA_BLOB lm_response;
475         static DATA_BLOB nt_response;
476         static char *full_username;
477         static char *username;
478         static char *domain;
479         static char *plaintext_password;
480         static BOOL ntlm_server_1_user_session_key;
481         static BOOL ntlm_server_1_lm_session_key;
482         
483         if (strequal(buf, ".")) {
484                 if (!full_username && !username) {      
485                         x_fprintf(x_stdout, "Error: No username supplied!\n");
486                 } else if (plaintext_password) {
487                         /* handle this request as plaintext */
488                         if (!full_username) {
489                                 if (asprintf(&full_username, "%s%c%s", domain, *lp_winbind_separator(), username) == -1) {
490                                         x_fprintf(x_stdout, "Error: Out of memory in asprintf!\n.\n");
491                                         return;
492                                 }
493                         }
494                         if (check_plaintext_auth(full_username, plaintext_password, False)) {
495                                 x_fprintf(x_stdout, "Authenticated: Yes\n");
496                         } else {
497                                 x_fprintf(x_stdout, "Authenticated: No\n");
498                         }
499                 } else if (!lm_response.data && !nt_response.data) {
500                         x_fprintf(x_stdout, "Error: No password supplied!\n");
501                 } else if (!challenge.data) {   
502                         x_fprintf(x_stdout, "Error: No lanman-challenge supplied!\n");
503                 } else {
504                         char *error_string = NULL;
505                         DATA_BLOB lm_key;
506                         DATA_BLOB user_session_key;
507                         uint32 flags = 0;
508
509                         if (full_username && !username) {
510                                 fstring fstr_user;
511                                 fstring fstr_domain;
512                                 
513                                 if (!parse_ntlm_auth_domain_user(full_username, fstr_user, fstr_domain)) {
514                                         /* username might be 'tainted', don't print into our new-line deleimianted stream */
515                                         x_fprintf(x_stdout, "Error: Could not parse into domain and username\n");
516                                 }
517                                 SAFE_FREE(username);
518                                 SAFE_FREE(domain);
519                                 username = smb_xstrdup(fstr_user);
520                                 domain = smb_xstrdup(fstr_domain);
521                         }
522
523                         if (!domain) {
524                                 domain = smb_xstrdup(lp_workgroup());
525                         }
526
527                         if (ntlm_server_1_lm_session_key) 
528                                 flags |= NTLM_AUTH_FLAG_LMKEY;
529                         
530                         if (ntlm_server_1_user_session_key) 
531                                 flags |= NTLM_AUTH_FLAG_USER_SESSION_KEY;
532
533                         if (!NT_STATUS_IS_OK(
534                                     local_pw_check_specified(username, 
535                                                               domain, 
536                                                               lp_netbios_name(),
537                                                               &challenge, 
538                                                               &lm_response, 
539                                                               &nt_response, 
540                                                               flags, 
541                                                               &lm_key, 
542                                                               &user_session_key,
543                                                               &error_string,
544                                                               NULL))) {
545
546                                 x_fprintf(x_stdout, "Authenticated: No\n");
547                                 x_fprintf(x_stdout, "Authentication-Error: %s\n.\n", error_string);
548                                 SAFE_FREE(error_string);
549                         } else {
550                                 static char zeros[16];
551                                 char *hex_lm_key;
552                                 char *hex_user_session_key;
553
554                                 x_fprintf(x_stdout, "Authenticated: Yes\n");
555
556                                 if (ntlm_server_1_lm_session_key 
557                                     && lm_key.length 
558                                     && (memcmp(zeros, lm_key.data, 
559                                                                 lm_key.length) != 0)) {
560                                         hex_encode(lm_key.data,
561                                                    lm_key.length,
562                                                    &hex_lm_key);
563                                         x_fprintf(x_stdout, "LANMAN-Session-Key: %s\n", hex_lm_key);
564                                         SAFE_FREE(hex_lm_key);
565                                 }
566
567                                 if (ntlm_server_1_user_session_key 
568                                     && user_session_key.length 
569                                     && (memcmp(zeros, user_session_key.data, 
570                                                user_session_key.length) != 0)) {
571                                         hex_encode(user_session_key.data, 
572                                                    user_session_key.length, 
573                                                    &hex_user_session_key);
574                                         x_fprintf(x_stdout, "User-Session-Key: %s\n", hex_user_session_key);
575                                         SAFE_FREE(hex_user_session_key);
576                                 }
577                         }
578                 }
579                 /* clear out the state */
580                 challenge = data_blob(NULL, 0);
581                 nt_response = data_blob(NULL, 0);
582                 lm_response = data_blob(NULL, 0);
583                 SAFE_FREE(full_username);
584                 SAFE_FREE(username);
585                 SAFE_FREE(domain);
586                 SAFE_FREE(plaintext_password);
587                 ntlm_server_1_user_session_key = False;
588                 ntlm_server_1_lm_session_key = False;
589                 x_fprintf(x_stdout, ".\n");
590
591                 return;
592         }
593
594         request = buf;
595
596         /* Indicates a base64 encoded structure */
597         parameter = strstr(request, ":: ");
598         if (!parameter) {
599                 parameter = strstr(request, ": ");
600                 
601                 if (!parameter) {
602                         DEBUG(0, ("Parameter not found!\n"));
603                         x_fprintf(x_stdout, "Error: Parameter not found!\n.\n");
604                         return;
605                 }
606                 
607                 parameter[0] ='\0';
608                 parameter++;
609                 parameter[0] ='\0';
610                 parameter++;
611
612         } else {
613                 parameter[0] ='\0';
614                 parameter++;
615                 parameter[0] ='\0';
616                 parameter++;
617                 parameter[0] ='\0';
618                 parameter++;
619
620                 base64_decode_inplace(parameter);
621         }
622
623         if (strequal(request, "LANMAN-Challenge")) {
624                 challenge = strhex_to_data_blob(parameter);
625                 if (challenge.length != 8) {
626                         x_fprintf(x_stdout, "Error: hex decode of %s failed! (got %d bytes, expected 8)\n.\n", 
627                                   parameter,
628                                   (int)challenge.length);
629                         challenge = data_blob(NULL, 0);
630                 }
631         } else if (strequal(request, "NT-Response")) {
632                 nt_response = strhex_to_data_blob(parameter);
633                 if (nt_response.length < 24) {
634                         x_fprintf(x_stdout, "Error: hex decode of %s failed! (only got %d bytes, needed at least 24)\n.\n", 
635                                   parameter,
636                                   (int)nt_response.length);
637                         nt_response = data_blob(NULL, 0);
638                 }
639         } else if (strequal(request, "LANMAN-Response")) {
640                 lm_response = strhex_to_data_blob(parameter);
641                 if (lm_response.length != 24) {
642                         x_fprintf(x_stdout, "Error: hex decode of %s failed! (got %d bytes, expected 24)\n.\n", 
643                                   parameter,
644                                   (int)lm_response.length);
645                         lm_response = data_blob(NULL, 0);
646                 }
647         } else if (strequal(request, "Password")) {
648                 plaintext_password = smb_xstrdup(parameter);
649         } else if (strequal(request, "NT-Domain")) {
650                 domain = smb_xstrdup(parameter);
651         } else if (strequal(request, "Username")) {
652                 username = smb_xstrdup(parameter);
653         } else if (strequal(request, "Full-Username")) {
654                 full_username = smb_xstrdup(parameter);
655         } else if (strequal(request, "Request-User-Session-Key")) {
656                 ntlm_server_1_user_session_key = strequal(parameter, "Yes");
657         } else if (strequal(request, "Request-LanMan-Session-Key")) {
658                 ntlm_server_1_lm_session_key = strequal(parameter, "Yes");
659         } else {
660                 x_fprintf(x_stdout, "Error: Unknown request %s\n.\n", request);
661         }
662 }
663
664 static void manage_squid_request(enum stdio_helper_mode helper_mode, stdio_helper_function fn, void *private) 
665 {
666         char buf[SQUID_BUFFER_SIZE+1];
667         int length;
668         char *c;
669         static BOOL err;
670
671         /* this is not a typo - x_fgets doesn't work too well under squid */
672         if (fgets(buf, sizeof(buf)-1, stdin) == NULL) {
673                 if (ferror(stdin)) {
674                         DEBUG(1, ("fgets() failed! dying..... errno=%d (%s)\n", ferror(stdin),
675                                   strerror(ferror(stdin))));
676                         
677                         exit(1);    /* BIIG buffer */
678                 }
679                 exit(0);
680         }
681     
682         c=memchr(buf,'\n',sizeof(buf)-1);
683         if (c) {
684                 *c = '\0';
685                 length = c-buf;
686         } else {
687                 err = 1;
688                 return;
689         }
690         if (err) {
691                 DEBUG(2, ("Oversized message\n"));
692                 x_fprintf(x_stderr, "ERR\n");
693                 err = 0;
694                 return;
695         }
696
697         DEBUG(10, ("Got '%s' from squid (length: %d).\n",buf,length));
698
699         if (buf[0] == '\0') {
700                 DEBUG(2, ("Invalid Request\n"));
701                 x_fprintf(x_stderr, "ERR\n");
702                 return;
703         }
704         
705         fn(helper_mode, buf, length, private);
706 }
707
708 static void squid_stream(enum stdio_helper_mode stdio_mode, stdio_helper_function fn) {
709         void *private = NULL;
710         /* initialize FDescs */
711         x_setbuf(x_stdout, NULL);
712         x_setbuf(x_stderr, NULL);
713         while(1) {
714                 manage_squid_request(stdio_mode, fn, &private);
715         }
716 }
717
718
719 /* Main program */
720
721 enum {
722         OPT_USERNAME = 1000,
723         OPT_DOMAIN,
724         OPT_WORKSTATION,
725         OPT_CHALLENGE,
726         OPT_RESPONSE,
727         OPT_LM,
728         OPT_NT,
729         OPT_PASSWORD,
730         OPT_LM_KEY,
731         OPT_USER_SESSION_KEY,
732         OPT_DIAGNOSTICS,
733         OPT_REQUIRE_MEMBERSHIP
734 };
735
736  int main(int argc, const char **argv)
737 {
738         static const char *helper_protocol;
739         int opt;
740
741         poptContext pc;
742
743         /* NOTE: DO NOT change this interface without considering the implications!
744            This is an external interface, which other programs will use to interact 
745            with this helper.
746         */
747
748         /* We do not use single-letter command abbreviations, because they harm future 
749            interface stability. */
750
751         struct poptOption long_options[] = {
752                 POPT_AUTOHELP
753                 { "helper-protocol", 0, POPT_ARG_STRING, &helper_protocol, OPT_DOMAIN, "operate as a stdio-based helper", "helper protocol to use"},
754                 { "domain", 0, POPT_ARG_STRING, &opt_domain, OPT_DOMAIN, "domain name"},
755                 { "workstation", 0, POPT_ARG_STRING, &opt_workstation, OPT_WORKSTATION, "workstation"},
756                 { "username", 0, POPT_ARG_STRING, &opt_username, OPT_PASSWORD, "Username"},             
757                 { "password", 0, POPT_ARG_STRING, &opt_password, OPT_PASSWORD, "User's plaintext password"},            
758                 POPT_COMMON_SAMBA
759                 POPT_TABLEEND
760         };
761
762         /* Samba client initialisation */
763
764         setup_logging("ntlm_auth", DEBUG_STDERR);
765
766         if (!lp_load(dyn_CONFIGFILE, True, False, False)) {
767                 d_fprintf(stderr, "wbinfo: error opening config file %s. Error was %s\n",
768                         dyn_CONFIGFILE, strerror(errno));
769                 exit(1);
770         }
771
772         /* Parse options */
773
774         pc = poptGetContext("ntlm_auth", argc, argv, long_options, 0);
775
776         /* Parse command line options */
777
778         if (argc == 1) {
779                 poptPrintHelp(pc, stderr, 0);
780                 return 1;
781         }
782
783         pc = poptGetContext(NULL, argc, (const char **)argv, long_options, 
784                             POPT_CONTEXT_KEEP_FIRST);
785
786         while((opt = poptGetNextOpt(pc)) != -1) {
787                 if (opt < -1) {
788                         break;
789                 }
790         }
791         if (opt < -1) {
792                 fprintf(stderr, "%s: %s\n",
793                         poptBadOption(pc, POPT_BADOPTION_NOALIAS),
794                         poptStrerror(opt));
795                 return 1;
796         }
797
798         if (opt_domain == NULL) {
799                 opt_domain = lp_workgroup();
800         }
801
802         if (helper_protocol) {
803                 int i;
804                 for (i=0; i<NUM_HELPER_MODES; i++) {
805                         if (strcmp(helper_protocol, stdio_helper_protocols[i].name) == 0) {
806                                 squid_stream(stdio_helper_protocols[i].mode, stdio_helper_protocols[i].fn);
807                                 exit(0);
808                         }
809                 }
810                 x_fprintf(x_stderr, "unknown helper protocol [%s]\n\nValid helper protools:\n\n", helper_protocol);
811
812                 for (i=0; i<NUM_HELPER_MODES; i++) {
813                         x_fprintf(x_stderr, "%s\n", stdio_helper_protocols[i].name);
814                 }
815
816                 exit(1);
817         }
818
819         if (!opt_username) {
820                 x_fprintf(x_stderr, "username must be specified!\n\n");
821                 poptPrintHelp(pc, stderr, 0);
822                 exit(1);
823         }
824
825         if (opt_workstation == NULL) {
826                 opt_workstation = lp_netbios_name();
827         }
828
829         if (!opt_password) {
830                 opt_password = getpass("password: ");
831         }
832
833         {
834                 char *user;
835
836                 asprintf(&user, "%s%c%s", opt_domain, *lp_winbind_separator(), opt_username);
837                 if (!check_plaintext_auth(user, opt_password, True)) {
838                         return 1;
839                 }
840         }
841
842         /* Exit code */
843
844         poptFreeContext(pc);
845         return 0;
846 }