r1630: - fixed the replacement timegm() function to work correctly for DST changes
[tprouty/samba.git] / source / utils / ntlm_auth.c
1 /* 
2    Unix SMB/CIFS implementation.
3
4    Winbind status program.
5
6    Copyright (C) Tim Potter      2000-2003
7    Copyright (C) Andrew Bartlett <abartlet@samba.org> 2003-2004
8    Copyright (C) Francesco Chemolli <kinkie@kame.usr.dsi.unimi.it> 2000 
9
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
27 #undef DBGC_CLASS
28 #define DBGC_CLASS DBGC_WINBIND
29
30 #define SQUID_BUFFER_SIZE 2010
31
32 enum stdio_helper_mode {
33         SQUID_2_4_BASIC,
34         SQUID_2_5_BASIC,
35         SQUID_2_5_NTLMSSP,
36         NTLMSSP_CLIENT_1,
37         GSS_SPNEGO_CLIENT,
38         GSS_SPNEGO_SERVER,
39         NTLM_SERVER_1,
40         NUM_HELPER_MODES
41 };
42
43 #define NTLM_AUTH_FLAG_USER_SESSION_KEY     0x0004
44 #define NTLM_AUTH_FLAG_LMKEY                0x0008
45
46
47 typedef void (*stdio_helper_function)(enum stdio_helper_mode stdio_helper_mode, 
48                                       char *buf, int length, void **private);
49
50 static void manage_squid_basic_request (enum stdio_helper_mode stdio_helper_mode, 
51                                         char *buf, int length, void **private);
52
53 static void manage_gensec_request (enum stdio_helper_mode stdio_helper_mode, 
54                                    char *buf, int length, void **private);
55
56 static void manage_ntlm_server_1_request (enum stdio_helper_mode stdio_helper_mode, 
57                                           char *buf, int length, void **private);
58
59 static void manage_squid_request(enum stdio_helper_mode helper_mode, 
60                                  stdio_helper_function fn, void *private);
61
62 static const struct {
63         enum stdio_helper_mode mode;
64         const char *name;
65         stdio_helper_function fn;
66 } stdio_helper_protocols[] = {
67         { SQUID_2_4_BASIC, "squid-2.4-basic", manage_squid_basic_request},
68         { SQUID_2_5_BASIC, "squid-2.5-basic", manage_squid_basic_request},
69         { SQUID_2_5_NTLMSSP, "squid-2.5-ntlmssp", manage_gensec_request},
70         { GSS_SPNEGO_CLIENT, "gss-spnego-client", manage_gensec_request},
71         { GSS_SPNEGO_SERVER, "gss-spnego-server", manage_gensec_request},
72         { NTLMSSP_CLIENT_1, "ntlmssp-client-1", manage_gensec_request},
73         { NTLM_SERVER_1, "ntlm-server-1", manage_ntlm_server_1_request},
74         { NUM_HELPER_MODES, NULL, NULL}
75 };
76
77 extern int winbindd_fd;
78
79 const char *opt_username;
80 const char *opt_domain;
81 const char *opt_workstation;
82 const char *opt_password;
83
84
85 /* Copy of parse_domain_user from winbindd_util.c.  Parse a string of the
86    form DOMAIN/user into a domain and a user */
87
88 static BOOL parse_ntlm_auth_domain_user(const char *domuser, fstring domain, 
89                                      fstring user)
90 {
91
92         char *p = strchr(domuser,*lp_winbind_separator());
93
94         if (!p) {
95                 return False;
96         }
97         
98         fstrcpy(user, p+1);
99         fstrcpy(domain, domuser);
100         domain[PTR_DIFF(p, domuser)] = 0;
101         strupper_m(domain);
102
103         return True;
104 }
105
106 /* Authenticate a user with a plaintext password */
107
108 static BOOL check_plaintext_auth(const char *user, const char *pass, 
109                                  BOOL stdout_diagnostics)
110 {
111         return (strcmp(pass, opt_password) == 0);
112 }
113
114 /* authenticate a user with an encrypted username/password */
115
116 static NTSTATUS local_pw_check_specified(const char *username, 
117                                          const char *domain, 
118                                          const char *workstation,
119                                          const DATA_BLOB *challenge, 
120                                          const DATA_BLOB *lm_response, 
121                                          const DATA_BLOB *nt_response, 
122                                          uint32 flags, 
123                                          DATA_BLOB *lm_session_key, 
124                                          DATA_BLOB *user_session_key, 
125                                          char **error_string, 
126                                          char **unix_name) 
127 {
128         NTSTATUS nt_status;
129         uint8_t lm_pw[16], nt_pw[16];
130         uint8_t *lm_pwd, *nt_pwd;
131         TALLOC_CTX *mem_ctx = talloc_init("local_pw_check_specified");
132         if (!mem_ctx) {
133                 nt_status = NT_STATUS_NO_MEMORY;
134         } else {
135                 
136                 E_md4hash(opt_password, nt_pw);
137                 if (E_deshash(opt_password, lm_pw)) {
138                         lm_pwd = lm_pw;
139                 } else {
140                         lm_pwd = NULL;
141                 }
142                 nt_pwd = nt_pw;
143                 
144                 
145                 nt_status = ntlm_password_check(mem_ctx, 
146                                                 challenge,
147                                                 lm_response,
148                                                 nt_response,
149                                                 NULL, NULL,
150                                                 username,
151                                                 username,
152                                                 domain,
153                                                 lm_pwd, nt_pwd, user_session_key, lm_session_key);
154                 
155                 if (NT_STATUS_IS_OK(nt_status)) {
156                         if (unix_name) {
157                                 asprintf(unix_name, 
158                                          "%s%c%s", domain,
159                                          *lp_winbind_separator(), 
160                                          username);
161                         }
162                 } else {
163                         DEBUG(3, ("Login for user [%s]\\[%s]@[%s] failed due to [%s]\n", 
164                                   domain, username, workstation, 
165                                   nt_errstr(nt_status)));
166                 }
167                 talloc_destroy(mem_ctx);
168         }
169         if (error_string) {
170                 *error_string = strdup(nt_errstr(nt_status));
171         }
172         return nt_status;
173         
174         
175 }
176
177 static void manage_squid_basic_request(enum stdio_helper_mode stdio_helper_mode, 
178                                        char *buf, int length, void **private) 
179 {
180         char *user, *pass;      
181         user=buf;
182         
183         pass=memchr(buf,' ',length);
184         if (!pass) {
185                 DEBUG(2, ("Password not found. Denying access\n"));
186                 x_fprintf(x_stdout, "ERR\n");
187                 return;
188         }
189         *pass='\0';
190         pass++;
191         
192         if (stdio_helper_mode == SQUID_2_5_BASIC) {
193                 rfc1738_unescape(user);
194                 rfc1738_unescape(pass);
195         }
196         
197         if (check_plaintext_auth(user, pass, False)) {
198                 x_fprintf(x_stdout, "OK\n");
199         } else {
200                 x_fprintf(x_stdout, "ERR\n");
201         }
202 }
203
204 /* This is a bit hairy, but the basic idea is to do a password callback
205    to the calling application.  The callback comes from within gensec */
206
207 static void manage_gensec_get_pw_request(enum stdio_helper_mode stdio_helper_mode, 
208                                          char *buf, int length, void **private)  
209 {
210         DATA_BLOB in;
211         struct gensec_security **gensec_state = (struct gensec_security **)private;
212         if (strlen(buf) < 2) {
213                 DEBUG(1, ("query [%s] invalid", buf));
214                 x_fprintf(x_stdout, "BH\n");
215                 return;
216         }
217
218         if (strlen(buf) > 3) {
219                 in = base64_decode_data_blob(buf + 3);
220         } else {
221                 in = data_blob(NULL, 0);
222         }
223
224         if (strncmp(buf, "PW ", 3) == 0) {
225
226                 (*gensec_state)->password_callback_private = talloc_strndup((*gensec_state)->mem_ctx, 
227                                                                             (const char *)in.data, in.length);
228                 
229                 if ((*gensec_state)->password_callback_private == NULL) {
230                         DEBUG(1, ("Out of memory\n"));
231                         x_fprintf(x_stdout, "BH\n");
232                         data_blob_free(&in);
233                         return;
234                 }
235
236                 x_fprintf(x_stdout, "OK\n");
237                 data_blob_free(&in);
238                 return;
239         }
240         DEBUG(1, ("Asked for (and expected) a password\n"));
241         x_fprintf(x_stdout, "BH\n");
242         data_blob_free(&in);
243 }
244
245 /* 
246  * Callback for gensec, to ask the calling application for a password.  Uses the above function
247  * for the stdio part of this.
248  */
249
250 static NTSTATUS get_password(struct gensec_security *gensec_security, TALLOC_CTX *mem_ctx, 
251                              char **password) 
252 {
253         *password = NULL;
254         
255         /* Ask for a password */
256         x_fprintf(x_stdout, "PW\n");
257         gensec_security->password_callback_private = NULL;
258
259         manage_squid_request(NUM_HELPER_MODES /* bogus */, manage_gensec_get_pw_request, &gensec_security);
260         *password = (char *)gensec_security->password_callback_private;
261         if (*password) {
262                 return NT_STATUS_OK;
263         } else {
264                 return NT_STATUS_INVALID_PARAMETER;
265         }
266 }
267
268 static void manage_gensec_request(enum stdio_helper_mode stdio_helper_mode, 
269                                   char *buf, int length, void **private) 
270 {
271         DATA_BLOB in;
272         DATA_BLOB out = data_blob(NULL, 0);
273         char *out_base64 = NULL;
274         const char *reply_arg = NULL;
275         struct gensec_security **gensec_state = (struct gensec_security **)private;
276         NTSTATUS nt_status;
277         BOOL first = False;
278         const char *reply_code;
279         
280         if (strlen(buf) < 2) {
281                 DEBUG(1, ("query [%s] invalid", buf));
282                 x_fprintf(x_stdout, "BH\n");
283                 return;
284         }
285
286         if (strlen(buf) > 3) {
287                 in = base64_decode_data_blob(buf + 3);
288         } else {
289                 in = data_blob(NULL, 0);
290         }
291
292         if (strncmp(buf, "YR", 2) == 0) {
293                 if (gensec_state && *gensec_state) {
294                         gensec_end(gensec_state);
295                         *gensec_state = NULL;
296                 }
297         } else if ( (strncmp(buf, "OK", 2) == 0)) {
298                 /* do nothing */
299                 data_blob_free(&in);
300                 return;
301         } else if ( (strncmp(buf, "TT ", 3) != 0) &&
302                     (strncmp(buf, "KK ", 3) != 0) &&
303                     (strncmp(buf, "AF ", 3) != 0) &&
304                     (strncmp(buf, "NA ", 3) != 0) && 
305                     (strncmp(buf, "PW ", 3) != 0)) {
306                 DEBUG(1, ("SPNEGO request [%s] invalid\n", buf));
307                 x_fprintf(x_stdout, "BH\n");
308                 data_blob_free(&in);
309                 return;
310         }
311
312         /* setup gensec */
313         if (!(gensec_state && *gensec_state)) {
314                 switch (stdio_helper_mode) {
315                 case GSS_SPNEGO_CLIENT:
316                 case NTLMSSP_CLIENT_1:
317                         /* setup the client side */
318                         
319                         if (!NT_STATUS_IS_OK(gensec_client_start(gensec_state))) {
320                                 exit(1);
321                         }
322                         gensec_set_username(*gensec_state, opt_username);
323                         gensec_set_domain(*gensec_state, opt_domain);           
324                         if (opt_password) {
325                                 if (!NT_STATUS_IS_OK(gensec_set_password(*gensec_state, opt_password))) {
326                                         DEBUG(1, ("Out of memory\n"));
327                                         x_fprintf(x_stdout, "BH\n");
328                                         data_blob_free(&in);
329                                         return;
330                                 }
331                         } else {
332                                 gensec_set_password_callback(*gensec_state, get_password, NULL);
333                         }
334                         
335                         break;
336                 case GSS_SPNEGO_SERVER:
337                 case SQUID_2_5_NTLMSSP:
338                         if (!NT_STATUS_IS_OK(gensec_server_start(gensec_state))) {
339                                 exit(1);
340                         }
341                         break;
342                 default:
343                         abort();
344                 }
345
346                 switch (stdio_helper_mode) {
347                 case GSS_SPNEGO_CLIENT:
348                 case GSS_SPNEGO_SERVER:
349                         nt_status = gensec_start_mech_by_oid(*gensec_state, OID_SPNEGO);
350                         break;
351                 case NTLMSSP_CLIENT_1:
352                 case SQUID_2_5_NTLMSSP:
353                         nt_status = gensec_start_mech_by_oid(*gensec_state, OID_NTLMSSP);
354                         break;
355                 default:
356                         abort();
357                 }
358
359                 if (!NT_STATUS_IS_OK(nt_status)) {
360                         DEBUG(1, ("SPENGO login failed to initialise: %s\n", nt_errstr(nt_status)));
361                         x_fprintf(x_stdout, "BH\n");
362                         return;
363                 }
364                 if (!in.length) {
365                         first = True;
366                 }
367         }
368         
369         if (strncmp(buf, "PW ", 3) == 0) {
370
371                 if (!NT_STATUS_IS_OK(gensec_set_password(*gensec_state, 
372                                                          talloc_strndup((*gensec_state)->mem_ctx, 
373                                                                         (const char *)in.data, 
374                                                                         in.length)))) {
375                         DEBUG(1, ("Out of memory\n"));
376                         x_fprintf(x_stdout, "BH\n");
377                         data_blob_free(&in);
378                         return;
379                 }
380
381                 x_fprintf(x_stdout, "OK\n");
382                 data_blob_free(&in);
383                 return;
384         }
385
386         /* update */
387
388         nt_status = gensec_update(*gensec_state, NULL, in, &out);
389         
390         /* don't leak 'bad password'/'no such user' info to the network client */
391         nt_status = nt_status_squash(nt_status);
392
393         if (out.length) {
394                 out_base64 = base64_encode_data_blob(out);
395         } else {
396                 out_base64 = NULL;
397         }
398         if (NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
399                 reply_arg = "*";
400                 if (first) {
401                         reply_code = "YR";
402                 } else if ((*gensec_state)->gensec_role == GENSEC_CLIENT) { 
403                         reply_code = "KK";
404                 } else if ((*gensec_state)->gensec_role == GENSEC_SERVER) { 
405                         reply_code = "TT";
406                 } else {
407                         abort();
408                 }
409
410
411         } else if (NT_STATUS_EQUAL(nt_status, NT_STATUS_ACCESS_DENIED)) {
412                 reply_code = "BH";
413                 reply_arg = nt_errstr(nt_status);
414                 DEBUG(1, ("GENSEC login failed: %s\n", nt_errstr(nt_status)));
415         } else if (NT_STATUS_EQUAL(nt_status, NT_STATUS_UNSUCCESSFUL)) {
416                 reply_code = "BH";
417                 reply_arg = nt_errstr(nt_status);
418                 DEBUG(1, ("GENSEC login failed: %s\n", nt_errstr(nt_status)));
419         } else if (!NT_STATUS_IS_OK(nt_status)) {
420                 reply_code = "NA";
421                 reply_arg = nt_errstr(nt_status);
422                 DEBUG(1, ("GENSEC login failed: %s\n", nt_errstr(nt_status)));
423         } else if /* OK */ ((*gensec_state)->gensec_role == GENSEC_SERVER) {
424                 struct auth_session_info *session_info;
425
426                 nt_status = gensec_session_info(*gensec_state, &session_info);
427                 if (!NT_STATUS_IS_OK(nt_status)) {
428                         reply_code = "BH";
429                         reply_arg = nt_errstr(nt_status);
430                         DEBUG(1, ("GENSEC failed to retreive the session info: %s\n", nt_errstr(nt_status)));
431                 } else {
432
433                         reply_code = "AF";
434                         reply_arg = talloc_asprintf((*gensec_state)->mem_ctx, 
435                                                     "%s%s%s", session_info->server_info->domain, 
436                                                     lp_winbind_separator(), session_info->server_info->account_name);
437                         talloc_destroy(session_info->mem_ctx);
438                 }
439         } else if ((*gensec_state)->gensec_role == GENSEC_CLIENT) {
440                 reply_code = "AF";
441                 reply_arg = NULL;
442         } else {
443                 abort();
444         }
445
446         switch (stdio_helper_mode) {
447         case GSS_SPNEGO_SERVER:
448                 if (out_base64) {
449                         x_fprintf(x_stdout, "%s %s %s\n", reply_code, out_base64, reply_arg);
450                 } else if (reply_arg) {
451                         x_fprintf(x_stdout, "%s %s\n", reply_code, reply_arg);
452                 } else {
453                         x_fprintf(x_stdout, "%s\n", reply_code);
454                 }
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_STDOUT);
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 }