r1198: Merge the Samba 3.0 ntlm_auth, including the kerberos and SPENGO parts.
[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
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,
38         GSS_SPNEGO_CLIENT,
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);
49
50 static void manage_squid_basic_request (enum stdio_helper_mode stdio_helper_mode, 
51                                         char *buf, int length);
52
53 static void manage_squid_ntlmssp_request (enum stdio_helper_mode stdio_helper_mode, 
54                                           char *buf, int length);
55
56 static void manage_client_ntlmssp_request (enum stdio_helper_mode stdio_helper_mode, 
57                                            char *buf, int length);
58
59 static void manage_gss_spnego_request (enum stdio_helper_mode stdio_helper_mode, 
60                                        char *buf, int length);
61
62 static void manage_gss_spnego_client_request (enum stdio_helper_mode stdio_helper_mode, 
63                                               char *buf, int length);
64
65 static void manage_ntlm_server_1_request (enum stdio_helper_mode stdio_helper_mode, 
66                                           char *buf, int length);
67
68 static const struct {
69         enum stdio_helper_mode mode;
70         const char *name;
71         stdio_helper_function fn;
72 } stdio_helper_protocols[] = {
73         { SQUID_2_4_BASIC, "squid-2.4-basic", manage_squid_basic_request},
74         { SQUID_2_5_BASIC, "squid-2.5-basic", manage_squid_basic_request},
75         { SQUID_2_5_NTLMSSP, "squid-2.5-ntlmssp", manage_squid_ntlmssp_request},
76         { NTLMSSP_CLIENT_1, "ntlmssp-client-1", manage_client_ntlmssp_request},
77         { GSS_SPNEGO, "gss-spnego", manage_gss_spnego_request},
78         { GSS_SPNEGO_CLIENT, "gss-spnego-client", manage_gss_spnego_client_request},
79         { NTLM_SERVER_1, "ntlm-server-1", manage_ntlm_server_1_request},
80         { NUM_HELPER_MODES, NULL, NULL}
81 };
82
83 extern int winbindd_fd;
84
85 const char *opt_username;
86 const char *opt_domain;
87 const char *opt_workstation;
88 const char *opt_password;
89
90
91 /* Copy of parse_domain_user from winbindd_util.c.  Parse a string of the
92    form DOMAIN/user into a domain and a user */
93
94 static BOOL parse_ntlm_auth_domain_user(const char *domuser, fstring domain, 
95                                      fstring user)
96 {
97
98         char *p = strchr(domuser,*lp_winbind_separator());
99
100         if (!p) {
101                 return False;
102         }
103         
104         fstrcpy(user, p+1);
105         fstrcpy(domain, domuser);
106         domain[PTR_DIFF(p, domuser)] = 0;
107         strupper_m(domain);
108
109         return True;
110 }
111
112 /* Authenticate a user with a plaintext password */
113
114 static BOOL check_plaintext_auth(const char *user, const char *pass, 
115                                  BOOL stdout_diagnostics)
116 {
117         return (strcmp(pass, opt_password) == 0);
118 }
119
120 /* authenticate a user with an encrypted username/password */
121
122 static NTSTATUS local_pw_check_specified(const char *username, 
123                                          const char *domain, 
124                                          const char *workstation,
125                                          const DATA_BLOB *challenge, 
126                                          const DATA_BLOB *lm_response, 
127                                          const DATA_BLOB *nt_response, 
128                                          uint32 flags, 
129                                          DATA_BLOB *lm_session_key, 
130                                          DATA_BLOB *user_session_key, 
131                                          char **error_string, 
132                                          char **unix_name) 
133 {
134         NTSTATUS nt_status;
135         uint8_t lm_pw[16], nt_pw[16];
136         uint8_t *lm_pwd, *nt_pwd;
137         TALLOC_CTX *mem_ctx = talloc_init("local_pw_check_specified");
138         if (!mem_ctx) {
139                 nt_status = NT_STATUS_NO_MEMORY;
140         } else {
141                 
142                 E_md4hash(opt_password, nt_pw);
143                 if (E_deshash(opt_password, lm_pw)) {
144                         lm_pwd = lm_pw;
145                 } else {
146                         lm_pwd = NULL;
147                 }
148                 nt_pwd = nt_pw;
149                 
150                 
151                 nt_status = ntlm_password_check(mem_ctx, 
152                                                 challenge,
153                                                 lm_response,
154                                                 nt_response,
155                                                 NULL, NULL,
156                                                 username,
157                                                 username,
158                                                 domain,
159                                                 lm_pwd, nt_pwd, user_session_key, lm_session_key);
160                 
161                 if (NT_STATUS_IS_OK(nt_status)) {
162                         if (unix_name) {
163                                 asprintf(unix_name, 
164                                          "%s%c%s", domain,
165                                          *lp_winbind_separator(), 
166                                          username);
167                         }
168                 } else {
169                         DEBUG(3, ("Login for user [%s]\\[%s]@[%s] failed due to [%s]\n", 
170                                   domain, username, workstation, 
171                                   nt_errstr(nt_status)));
172                 }
173                 talloc_destroy(mem_ctx);
174         }
175         if (error_string) {
176                 *error_string = strdup(nt_errstr(nt_status));
177         }
178         return nt_status;
179         
180         
181 }
182
183 static NTSTATUS local_pw_check(struct ntlmssp_state *ntlmssp_state, DATA_BLOB *user_session_key, DATA_BLOB *lm_session_key) 
184 {
185         NTSTATUS nt_status;
186         uint8 lm_pw[16], nt_pw[16];
187         uint8_t *lm_pwd, *nt_pwd;
188
189         E_md4hash(opt_password, nt_pw);
190         if (E_deshash(opt_password, lm_pw)) {
191                 lm_pwd = lm_pw;
192         } else {
193                         lm_pwd = NULL;
194         }
195         nt_pwd = nt_pw;
196                 
197         nt_status = ntlm_password_check(ntlmssp_state->mem_ctx, 
198                                         &ntlmssp_state->chal,
199                                         &ntlmssp_state->lm_resp,
200                                         &ntlmssp_state->nt_resp, 
201                                         NULL, NULL,
202                                         ntlmssp_state->user, 
203                                         ntlmssp_state->user, 
204                                         ntlmssp_state->domain,
205                                         lm_pwd, nt_pwd, user_session_key, lm_session_key);
206         
207         if (NT_STATUS_IS_OK(nt_status)) {
208                 ntlmssp_state->auth_context = talloc_asprintf(ntlmssp_state->mem_ctx, 
209                                                               "%s%c%s", ntlmssp_state->domain, 
210                                                               *lp_winbind_separator(), 
211                                                               ntlmssp_state->user);
212         } else {
213                 DEBUG(3, ("Login for user [%s]\\[%s]@[%s] failed due to [%s]\n", 
214                           ntlmssp_state->domain, ntlmssp_state->user, ntlmssp_state->workstation, 
215                           nt_errstr(nt_status)));
216                 ntlmssp_state->auth_context = NULL;
217         }
218         return nt_status;
219 }
220
221 static NTSTATUS ntlm_auth_start_ntlmssp_client(struct ntlmssp_state **client_ntlmssp_state) 
222 {
223         NTSTATUS status;
224         if ( (opt_username == NULL) || (opt_domain == NULL) ) {
225                 DEBUG(1, ("Need username and domain for NTLMSSP\n"));
226                 return status;
227         }
228
229         status = ntlmssp_client_start(client_ntlmssp_state);
230
231         if (!NT_STATUS_IS_OK(status)) {
232                 DEBUG(1, ("Could not start NTLMSSP client: %s\n",
233                           nt_errstr(status)));
234                 ntlmssp_end(client_ntlmssp_state);
235                 return status;
236         }
237
238         status = ntlmssp_set_username(*client_ntlmssp_state, opt_username);
239
240         if (!NT_STATUS_IS_OK(status)) {
241                 DEBUG(1, ("Could not set username: %s\n",
242                           nt_errstr(status)));
243                 ntlmssp_end(client_ntlmssp_state);
244                 return status;
245         }
246
247         status = ntlmssp_set_domain(*client_ntlmssp_state, opt_domain);
248
249         if (!NT_STATUS_IS_OK(status)) {
250                 DEBUG(1, ("Could not set domain: %s\n",
251                           nt_errstr(status)));
252                 ntlmssp_end(client_ntlmssp_state);
253                 return status;
254         }
255
256         status = ntlmssp_set_password(*client_ntlmssp_state, opt_password);
257         
258         if (!NT_STATUS_IS_OK(status)) {
259                 DEBUG(1, ("Could not set password: %s\n",
260                           nt_errstr(status)));
261                 ntlmssp_end(client_ntlmssp_state);
262                 return status;
263         }
264         return NT_STATUS_OK;
265 }
266
267 static NTSTATUS ntlm_auth_start_ntlmssp_server(struct ntlmssp_state **ntlmssp_state) 
268 {
269         NTSTATUS status = ntlmssp_server_start(ntlmssp_state);
270         
271         if (!NT_STATUS_IS_OK(status)) {
272                 DEBUG(1, ("Could not start NTLMSSP client: %s\n",
273                           nt_errstr(status)));
274                 return status;
275         }
276
277         /* Have we been given a local password, or should we ask winbind? */
278         if (opt_password) {
279                 (*ntlmssp_state)->check_password = local_pw_check;
280                 (*ntlmssp_state)->get_domain = lp_workgroup;
281                 (*ntlmssp_state)->get_global_myname = global_myname;
282         } else {
283                 DEBUG(0, ("Winbind not supported in Samba4 ntlm_auth yet, specify --password\n"));
284                 exit(1);
285         }
286         return NT_STATUS_OK;
287 }
288
289 static void manage_squid_ntlmssp_request(enum stdio_helper_mode stdio_helper_mode, 
290                                          char *buf, int length) 
291 {
292         static struct ntlmssp_state *ntlmssp_state = NULL;
293         DATA_BLOB request, reply;
294         NTSTATUS nt_status;
295
296         if (strlen(buf) < 2) {
297                 DEBUG(1, ("NTLMSSP query [%s] invalid", buf));
298                 x_fprintf(x_stdout, "BH\n");
299                 return;
300         }
301
302         if (strlen(buf) > 3) {
303                 request = base64_decode_data_blob(buf + 3);
304         } else {
305                 request = data_blob(NULL, 0);
306         }
307
308         if ((strncmp(buf, "PW ", 3) == 0)) {
309                 /* The calling application wants us to use a local password (rather than winbindd) */
310
311                 opt_password = strndup((const char *)request.data, request.length);
312
313                 if (opt_password == NULL) {
314                         DEBUG(1, ("Out of memory\n"));
315                         x_fprintf(x_stdout, "BH\n");
316                         data_blob_free(&request);
317                         return;
318                 }
319
320                 x_fprintf(x_stdout, "OK\n");
321                 data_blob_free(&request);
322                 return;
323         }
324
325         if (strncmp(buf, "YR", 2) == 0) {
326                 if (ntlmssp_state)
327                         ntlmssp_end(&ntlmssp_state);
328         } else if (strncmp(buf, "KK", 2) == 0) {
329                 
330         } else {
331                 DEBUG(1, ("NTLMSSP query [%s] invalid", buf));
332                 x_fprintf(x_stdout, "BH\n");
333                 return;
334         }
335
336         if (!ntlmssp_state) {
337                 if (!NT_STATUS_IS_OK(nt_status = ntlm_auth_start_ntlmssp_server(&ntlmssp_state))) {
338                         x_fprintf(x_stdout, "BH %s\n", nt_errstr(nt_status));
339                         return;
340                 }
341         }
342
343         DEBUG(10, ("got NTLMSSP packet:\n"));
344         dump_data(10, (const char *)request.data, request.length);
345
346         nt_status = ntlmssp_update(ntlmssp_state, NULL, request, &reply);
347         
348         if (NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
349                 char *reply_base64 = base64_encode_data_blob(reply);
350                 x_fprintf(x_stdout, "TT %s\n", reply_base64);
351                 SAFE_FREE(reply_base64);
352                 data_blob_free(&reply);
353                 DEBUG(10, ("NTLMSSP challenge\n"));
354         } else if (NT_STATUS_EQUAL(nt_status, NT_STATUS_ACCESS_DENIED)) {
355                 x_fprintf(x_stdout, "BH %s\n", nt_errstr(nt_status));
356                 DEBUG(0, ("NTLMSSP BH: %s\n", nt_errstr(nt_status)));
357
358                 ntlmssp_end(&ntlmssp_state);
359         } else if (!NT_STATUS_IS_OK(nt_status)) {
360                 x_fprintf(x_stdout, "NA %s\n", nt_errstr(nt_status));
361                 DEBUG(10, ("NTLMSSP %s\n", nt_errstr(nt_status)));
362         } else {
363                 x_fprintf(x_stdout, "AF %s\n", (char *)ntlmssp_state->auth_context);
364                 DEBUG(10, ("NTLMSSP OK!\n"));
365         }
366
367         data_blob_free(&request);
368 }
369
370 static void manage_client_ntlmssp_request(enum stdio_helper_mode stdio_helper_mode, 
371                                          char *buf, int length) 
372 {
373         static struct ntlmssp_state *ntlmssp_state = NULL;
374         DATA_BLOB request, reply;
375         NTSTATUS nt_status;
376         BOOL first = False;
377         
378         if (strlen(buf) < 2) {
379                 DEBUG(1, ("NTLMSSP query [%s] invalid", buf));
380                 x_fprintf(x_stdout, "BH\n");
381                 return;
382         }
383
384         if (strlen(buf) > 3) {
385                 request = base64_decode_data_blob(buf + 3);
386         } else {
387                 request = data_blob(NULL, 0);
388         }
389
390         if (strncmp(buf, "PW ", 3) == 0) {
391                 /* We asked for a password and obviously got it :-) */
392
393                 opt_password = strndup((const char *)request.data, request.length);
394
395                 if (opt_password == NULL) {
396                         DEBUG(1, ("Out of memory\n"));
397                         x_fprintf(x_stdout, "BH\n");
398                         data_blob_free(&request);
399                         return;
400                 }
401
402                 x_fprintf(x_stdout, "OK\n");
403                 data_blob_free(&request);
404                 return;
405         }
406
407         if (opt_password == NULL) {
408                 
409                 /* Request a password from the calling process.  After
410                    sending it, the calling process should retry asking for the negotiate. */
411                 
412                 DEBUG(10, ("Requesting password\n"));
413                 x_fprintf(x_stdout, "PW\n");
414                 return;
415         }
416
417         if (strncmp(buf, "YR", 2) == 0) {
418                 if (ntlmssp_state)
419                         ntlmssp_end(&ntlmssp_state);
420         } else if (strncmp(buf, "TT", 2) == 0) {
421                 
422         } else {
423                 DEBUG(1, ("NTLMSSP query [%s] invalid", buf));
424                 x_fprintf(x_stdout, "BH\n");
425                 return;
426         }
427
428         if (!ntlmssp_state) {
429                 if (!NT_STATUS_IS_OK(nt_status = ntlm_auth_start_ntlmssp_client(&ntlmssp_state))) {
430                         x_fprintf(x_stdout, "BH %s\n", nt_errstr(nt_status));
431                         return;
432                 }
433                 first = True;
434         }
435
436         DEBUG(10, ("got NTLMSSP packet:\n"));
437         dump_data(10, (const char *)request.data, request.length);
438
439         nt_status = ntlmssp_update(ntlmssp_state, NULL, request, &reply);
440         
441         if (NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
442                 char *reply_base64 = base64_encode_data_blob(reply);
443                 if (first) {
444                         x_fprintf(x_stdout, "YR %s\n", reply_base64);
445                 } else { 
446                         x_fprintf(x_stdout, "KK %s\n", reply_base64);
447                 }
448                 SAFE_FREE(reply_base64);
449                 data_blob_free(&reply);
450                 DEBUG(10, ("NTLMSSP challenge\n"));
451         } else if (NT_STATUS_IS_OK(nt_status)) {
452                 x_fprintf(x_stdout, "AF\n");
453                 DEBUG(10, ("NTLMSSP OK!\n"));
454                 if (ntlmssp_state)
455                         ntlmssp_end(&ntlmssp_state);
456         } else {
457                 x_fprintf(x_stdout, "BH %s\n", nt_errstr(nt_status));
458                 DEBUG(0, ("NTLMSSP BH: %s\n", nt_errstr(nt_status)));
459                 if (ntlmssp_state)
460                         ntlmssp_end(&ntlmssp_state);
461         }
462
463         data_blob_free(&request);
464 }
465
466 static void manage_squid_basic_request(enum stdio_helper_mode stdio_helper_mode, 
467                                        char *buf, int length) 
468 {
469         char *user, *pass;      
470         user=buf;
471         
472         pass=memchr(buf,' ',length);
473         if (!pass) {
474                 DEBUG(2, ("Password not found. Denying access\n"));
475                 x_fprintf(x_stdout, "ERR\n");
476                 return;
477         }
478         *pass='\0';
479         pass++;
480         
481         if (stdio_helper_mode == SQUID_2_5_BASIC) {
482                 rfc1738_unescape(user);
483                 rfc1738_unescape(pass);
484         }
485         
486         if (check_plaintext_auth(user, pass, False)) {
487                 x_fprintf(x_stdout, "OK\n");
488         } else {
489                 x_fprintf(x_stdout, "ERR\n");
490         }
491 }
492
493 static void offer_gss_spnego_mechs(void) {
494
495         DATA_BLOB token;
496         struct spnego_data spnego;
497         ssize_t len;
498         char *reply_base64;
499
500         pstring principal;
501         pstring myname_lower;
502
503         ZERO_STRUCT(spnego);
504
505         pstrcpy(myname_lower, global_myname());
506         strlower_m(myname_lower);
507
508         pstr_sprintf(principal, "%s$@%s", myname_lower, lp_realm());
509
510         /* Server negTokenInit (mech offerings) */
511         spnego.type = SPNEGO_NEG_TOKEN_INIT;
512         spnego.negTokenInit.mechTypes = smb_xmalloc(sizeof(char *) * 3);
513 #ifdef HAVE_KRB5
514         spnego.negTokenInit.mechTypes[0] = smb_xstrdup(OID_KERBEROS5_OLD);
515         spnego.negTokenInit.mechTypes[1] = smb_xstrdup(OID_NTLMSSP);
516         spnego.negTokenInit.mechTypes[2] = NULL;
517 #else
518         spnego.negTokenInit.mechTypes[0] = smb_xstrdup(OID_NTLMSSP);
519         spnego.negTokenInit.mechTypes[1] = NULL;
520 #endif
521
522
523         spnego.negTokenInit.mechListMIC = data_blob(principal,
524                                                     strlen(principal));
525
526         len = write_spnego_data(&token, &spnego);
527         free_spnego_data(&spnego);
528
529         if (len == -1) {
530                 DEBUG(1, ("Could not write SPNEGO data blob\n"));
531                 x_fprintf(x_stdout, "BH\n");
532                 return;
533         }
534
535         reply_base64 = base64_encode_data_blob(token);
536         x_fprintf(x_stdout, "TT %s *\n", reply_base64);
537
538         SAFE_FREE(reply_base64);
539         data_blob_free(&token);
540         DEBUG(10, ("sent SPNEGO negTokenInit\n"));
541         return;
542 }
543
544 static void manage_gss_spnego_request(enum stdio_helper_mode stdio_helper_mode, 
545                                       char *buf, int length) 
546 {
547         static struct ntlmssp_state *ntlmssp_state = NULL;
548         struct spnego_data request, response;
549         DATA_BLOB token;
550         NTSTATUS status;
551         ssize_t len;
552
553         char *user = NULL;
554         char *domain = NULL;
555
556         const char *reply_code;
557         char       *reply_base64;
558         pstring     reply_argument;
559
560         if (strlen(buf) < 2) {
561                 DEBUG(1, ("SPENGO query [%s] invalid", buf));
562                 x_fprintf(x_stdout, "BH\n");
563                 return;
564         }
565
566         if (strncmp(buf, "YR", 2) == 0) {
567                 if (ntlmssp_state)
568                         ntlmssp_end(&ntlmssp_state);
569         } else if (strncmp(buf, "KK", 2) == 0) {
570                 
571         } else {
572                 DEBUG(1, ("SPENGO query [%s] invalid", buf));
573                 x_fprintf(x_stdout, "BH\n");
574                 return;
575         }
576
577         if ( (strlen(buf) == 2)) {
578
579                 /* no client data, get the negTokenInit offering
580                    mechanisms */
581
582                 offer_gss_spnego_mechs();
583                 return;
584         }
585
586         /* All subsequent requests have a blob. This might be negTokenInit or negTokenTarg */
587
588         if (strlen(buf) <= 3) {
589                 DEBUG(1, ("GSS-SPNEGO query [%s] invalid\n", buf));
590                 x_fprintf(x_stdout, "BH\n");
591                 return;
592         }
593
594         token = base64_decode_data_blob(buf + 3);
595         len = read_spnego_data(token, &request);
596         data_blob_free(&token);
597
598         if (len == -1) {
599                 DEBUG(1, ("GSS-SPNEGO query [%s] invalid", buf));
600                 x_fprintf(x_stdout, "BH\n");
601                 return;
602         }
603
604         if (request.type == SPNEGO_NEG_TOKEN_INIT) {
605
606                 /* Second request from Client. This is where the
607                    client offers its mechanism to use. */
608
609                 if ( (request.negTokenInit.mechTypes == NULL) ||
610                      (request.negTokenInit.mechTypes[0] == NULL) ) {
611                         DEBUG(1, ("Client did not offer any mechanism"));
612                         x_fprintf(x_stdout, "BH\n");
613                         return;
614                 }
615
616                 if (strcmp(request.negTokenInit.mechTypes[0], OID_NTLMSSP) == 0) {
617
618                         if ( request.negTokenInit.mechToken.data == NULL ) {
619                                 DEBUG(1, ("Client did not provide  NTLMSSP data\n"));
620                                 x_fprintf(x_stdout, "BH\n");
621                                 return;
622                         }
623
624                         if ( ntlmssp_state != NULL ) {
625                                 DEBUG(1, ("Client wants a new NTLMSSP challenge, but "
626                                           "already got one\n"));
627                                 x_fprintf(x_stdout, "BH\n");
628                                 ntlmssp_end(&ntlmssp_state);
629                                 return;
630                         }
631
632                         if (!NT_STATUS_IS_OK(status = ntlm_auth_start_ntlmssp_server(&ntlmssp_state))) {
633                                 x_fprintf(x_stdout, "BH %s\n", nt_errstr(status));
634                                 return;
635                         }
636
637                         DEBUG(10, ("got NTLMSSP packet:\n"));
638                         dump_data(10, (const char *)request.negTokenInit.mechToken.data,
639                                   request.negTokenInit.mechToken.length);
640
641                         response.type = SPNEGO_NEG_TOKEN_TARG;
642                         response.negTokenTarg.supportedMech = strdup(OID_NTLMSSP);
643                         response.negTokenTarg.mechListMIC = data_blob(NULL, 0);
644
645                         status = ntlmssp_update(ntlmssp_state,
646                                                 NULL, 
647                                                 request.negTokenInit.mechToken,
648                                                 &response.negTokenTarg.responseToken);
649                 }
650
651 #ifdef HAVE_KRB5
652                 if (strcmp(request.negTokenInit.mechTypes[0], OID_KERBEROS5_OLD) == 0) {
653
654                         char *principal;
655                         DATA_BLOB auth_data;
656                         DATA_BLOB ap_rep;
657                         DATA_BLOB session_key;
658
659                         if ( request.negTokenInit.mechToken.data == NULL ) {
660                                 DEBUG(1, ("Client did not provide Kerberos data\n"));
661                                 x_fprintf(x_stdout, "BH\n");
662                                 return;
663                         }
664
665                         response.type = SPNEGO_NEG_TOKEN_TARG;
666                         response.negTokenTarg.supportedMech = strdup(OID_KERBEROS5_OLD);
667                         response.negTokenTarg.mechListMIC = data_blob(NULL, 0);
668                         response.negTokenTarg.responseToken = data_blob(NULL, 0);
669
670                         status = ads_verify_ticket(lp_realm(),
671                                                    &request.negTokenInit.mechToken,
672                                                    &principal, &auth_data, &ap_rep,
673                                                    &session_key);
674
675                         /* Now in "principal" we have the name we are
676                            authenticated as. */
677
678                         if (NT_STATUS_IS_OK(status)) {
679
680                                 domain = strchr(principal, '@');
681
682                                 if (domain == NULL) {
683                                         DEBUG(1, ("Did not get a valid principal "
684                                                   "from ads_verify_ticket\n"));
685                                         x_fprintf(x_stdout, "BH\n");
686                                         return;
687                                 }
688
689                                 *domain++ = '\0';
690                                 domain = strdup(domain);
691                                 user = strdup(principal);
692
693                                 data_blob_free(&ap_rep);
694                                 data_blob_free(&auth_data);
695
696                                 SAFE_FREE(principal);
697                         }
698                 }
699 #endif
700
701         } else {
702
703                 if ( (request.negTokenTarg.supportedMech == NULL) ||
704                      ( strcmp(request.negTokenTarg.supportedMech, OID_NTLMSSP) != 0 ) ) {
705                         /* Kerberos should never send a negTokenTarg, OID_NTLMSSP
706                            is the only one we support that sends this stuff */
707                         DEBUG(1, ("Got a negTokenTarg for something non-NTLMSSP: %s\n",
708                                   request.negTokenTarg.supportedMech));
709                         x_fprintf(x_stdout, "BH\n");
710                         return;
711                 }
712
713                 if (request.negTokenTarg.responseToken.data == NULL) {
714                         DEBUG(1, ("Got a negTokenTarg without a responseToken!\n"));
715                         x_fprintf(x_stdout, "BH\n");
716                         return;
717                 }
718
719                 status = ntlmssp_update(ntlmssp_state,
720                                         NULL,
721                                         request.negTokenTarg.responseToken,
722                                         &response.negTokenTarg.responseToken);
723
724                 response.type = SPNEGO_NEG_TOKEN_TARG;
725                 response.negTokenTarg.supportedMech = strdup(OID_NTLMSSP);
726                 response.negTokenTarg.mechListMIC = data_blob(NULL, 0);
727
728                 if (NT_STATUS_IS_OK(status)) {
729                         user = strdup(ntlmssp_state->user);
730                         domain = strdup(ntlmssp_state->domain);
731                         ntlmssp_end(&ntlmssp_state);
732                 }
733         }
734
735         free_spnego_data(&request);
736
737         if (NT_STATUS_IS_OK(status)) {
738                 response.negTokenTarg.negResult = SPNEGO_ACCEPT_COMPLETED;
739                 reply_code = "AF";
740                 pstr_sprintf(reply_argument, "%s\\%s", domain, user);
741         } else if (NT_STATUS_EQUAL(status,
742                                    NT_STATUS_MORE_PROCESSING_REQUIRED)) {
743                 response.negTokenTarg.negResult = SPNEGO_ACCEPT_INCOMPLETE;
744                 reply_code = "TT";
745                 pstr_sprintf(reply_argument, "*");
746         } else {
747                 response.negTokenTarg.negResult = SPNEGO_REJECT;
748                 reply_code = "NA";
749                 pstrcpy(reply_argument, nt_errstr(status));
750         }
751
752         SAFE_FREE(user);
753         SAFE_FREE(domain);
754
755         len = write_spnego_data(&token, &response);
756         free_spnego_data(&response);
757
758         if (len == -1) {
759                 DEBUG(1, ("Could not write SPNEGO data blob\n"));
760                 x_fprintf(x_stdout, "BH\n");
761                 return;
762         }
763
764         reply_base64 = base64_encode_data_blob(token);
765
766         x_fprintf(x_stdout, "%s %s %s\n",
767                   reply_code, reply_base64, reply_argument);
768
769         SAFE_FREE(reply_base64);
770         data_blob_free(&token);
771
772         return;
773 }
774
775 static struct ntlmssp_state *client_ntlmssp_state = NULL;
776
777 static BOOL manage_client_ntlmssp_init(struct spnego_data spnego)
778 {
779         NTSTATUS status;
780         DATA_BLOB null_blob = data_blob(NULL, 0);
781         DATA_BLOB to_server;
782         char *to_server_base64;
783         const char *my_mechs[] = {OID_NTLMSSP, NULL};
784
785         DEBUG(10, ("Got spnego negTokenInit with NTLMSSP\n"));
786
787         if (client_ntlmssp_state != NULL) {
788                 DEBUG(1, ("Request for initial SPNEGO request where "
789                           "we already have a state\n"));
790                 return False;
791         }
792
793         if (!client_ntlmssp_state) {
794                 if (!NT_STATUS_IS_OK(status = ntlm_auth_start_ntlmssp_client(&client_ntlmssp_state))) {
795                         x_fprintf(x_stdout, "BH %s\n", nt_errstr(status));
796                         return False;
797                 }
798         }
799
800
801         if (opt_password == NULL) {
802
803                 /* Request a password from the calling process.  After
804                    sending it, the calling process should retry with
805                    the negTokenInit. */
806
807                 DEBUG(10, ("Requesting password\n"));
808                 x_fprintf(x_stdout, "PW\n");
809                 return True;
810         }
811
812         spnego.type = SPNEGO_NEG_TOKEN_INIT;
813         spnego.negTokenInit.mechTypes = my_mechs;
814         spnego.negTokenInit.reqFlags = 0;
815         spnego.negTokenInit.mechListMIC = null_blob;
816
817         status = ntlmssp_update(client_ntlmssp_state, 
818                                 NULL,
819                                 null_blob,
820                                 &spnego.negTokenInit.mechToken);
821
822         if (!NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
823                 DEBUG(1, ("Expected MORE_PROCESSING_REQUIRED, got: %s\n",
824                           nt_errstr(status)));
825                 ntlmssp_end(&client_ntlmssp_state);
826                 return False;
827         }
828
829         write_spnego_data(&to_server, &spnego);
830         data_blob_free(&spnego.negTokenInit.mechToken);
831
832         to_server_base64 = base64_encode_data_blob(to_server);
833         data_blob_free(&to_server);
834         x_fprintf(x_stdout, "KK %s\n", to_server_base64);
835         SAFE_FREE(to_server_base64);
836         return True;
837 }
838
839 static void manage_client_ntlmssp_targ(struct spnego_data spnego)
840 {
841         NTSTATUS status;
842         DATA_BLOB null_blob = data_blob(NULL, 0);
843         DATA_BLOB request;
844         DATA_BLOB to_server;
845         char *to_server_base64;
846
847         DEBUG(10, ("Got spnego negTokenTarg with NTLMSSP\n"));
848
849         if (client_ntlmssp_state == NULL) {
850                 DEBUG(1, ("Got NTLMSSP tArg without a client state\n"));
851                 x_fprintf(x_stdout, "BH\n");
852                 ntlmssp_end(&client_ntlmssp_state);
853                 return;
854         }
855
856         if (spnego.negTokenTarg.negResult == SPNEGO_REJECT) {
857                 x_fprintf(x_stdout, "NA\n");
858                 ntlmssp_end(&client_ntlmssp_state);
859                 return;
860         }
861
862         if (spnego.negTokenTarg.negResult == SPNEGO_ACCEPT_COMPLETED) {
863                 x_fprintf(x_stdout, "AF\n");
864                 ntlmssp_end(&client_ntlmssp_state);
865                 return;
866         }
867
868         status = ntlmssp_update(client_ntlmssp_state,
869                                 NULL,
870                                 spnego.negTokenTarg.responseToken,
871                                 &request);
872                 
873         if (!NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
874                 DEBUG(1, ("Expected MORE_PROCESSING_REQUIRED from "
875                           "ntlmssp_update, got: %s\n",
876                           nt_errstr(status)));
877                 x_fprintf(x_stdout, "BH\n");
878                 data_blob_free(&request);
879                 ntlmssp_end(&client_ntlmssp_state);
880                 return;
881         }
882
883         spnego.type = SPNEGO_NEG_TOKEN_TARG;
884         spnego.negTokenTarg.negResult = SPNEGO_ACCEPT_INCOMPLETE;
885         spnego.negTokenTarg.supportedMech = OID_NTLMSSP;
886         spnego.negTokenTarg.responseToken = request;
887         spnego.negTokenTarg.mechListMIC = null_blob;
888         
889         write_spnego_data(&to_server, &spnego);
890         data_blob_free(&request);
891
892         to_server_base64 = base64_encode_data_blob(to_server);
893         data_blob_free(&to_server);
894         x_fprintf(x_stdout, "KK %s\n", to_server_base64);
895         SAFE_FREE(to_server_base64);
896         return;
897 }
898
899 #ifdef HAVE_KRB5
900
901 static BOOL manage_client_krb5_init(struct spnego_data spnego)
902 {
903         char *principal;
904         DATA_BLOB tkt, to_server;
905         DATA_BLOB session_key_krb5 = data_blob(NULL, 0);
906         struct spnego_data reply;
907         char *reply_base64;
908         int retval;
909         
910         const char *my_mechs[] = {OID_KERBEROS5_OLD, NULL};
911         ssize_t len;
912
913         if ( (spnego.negTokenInit.mechListMIC.data == NULL) ||
914              (spnego.negTokenInit.mechListMIC.length == 0) ) {
915                 DEBUG(1, ("Did not get a principal for krb5\n"));
916                 return False;
917         }
918
919         principal = malloc(spnego.negTokenInit.mechListMIC.length+1);
920
921         if (principal == NULL) {
922                 DEBUG(1, ("Could not malloc principal\n"));
923                 return False;
924         }
925
926         memcpy(principal, spnego.negTokenInit.mechListMIC.data,
927                spnego.negTokenInit.mechListMIC.length);
928         principal[spnego.negTokenInit.mechListMIC.length] = '\0';
929
930         retval = cli_krb5_get_ticket(principal, 0, &tkt, &session_key_krb5);
931
932         if (retval) {
933
934                 pstring user;
935
936                 /* Let's try to first get the TGT, for that we need a
937                    password. */
938
939                 if (opt_password == NULL) {
940                         DEBUG(10, ("Requesting password\n"));
941                         x_fprintf(x_stdout, "PW\n");
942                         return True;
943                 }
944
945                 pstr_sprintf(user, "%s@%s", opt_username, opt_domain);
946
947                 if ((retval = kerberos_kinit_password(user, opt_password, 
948                                                       0, NULL))) {
949                         DEBUG(10, ("Requesting TGT failed: %s\n", error_message(retval)));
950                         return False;
951                 }
952
953                 retval = cli_krb5_get_ticket(principal, 0, &tkt, &session_key_krb5);
954
955                 if (retval) {
956                         DEBUG(10, ("Kinit suceeded, but getting a ticket failed: %s\n", error_message(retval)));
957                         return False;
958                 }
959         }
960
961         data_blob_free(&session_key_krb5);
962
963         ZERO_STRUCT(reply);
964
965         reply.type = SPNEGO_NEG_TOKEN_INIT;
966         reply.negTokenInit.mechTypes = my_mechs;
967         reply.negTokenInit.reqFlags = 0;
968         reply.negTokenInit.mechToken = tkt;
969         reply.negTokenInit.mechListMIC = data_blob(NULL, 0);
970
971         len = write_spnego_data(&to_server, &reply);
972         data_blob_free(&tkt);
973
974         if (len == -1) {
975                 DEBUG(1, ("Could not write SPNEGO data blob\n"));
976                 return False;
977         }
978
979         reply_base64 = base64_encode_data_blob(to_server);
980         x_fprintf(x_stdout, "KK %s *\n", reply_base64);
981
982         SAFE_FREE(reply_base64);
983         data_blob_free(&to_server);
984         DEBUG(10, ("sent GSS-SPNEGO KERBEROS5 negTokenInit\n"));
985         return True;
986 }
987
988 static void manage_client_krb5_targ(struct spnego_data spnego)
989 {
990         switch (spnego.negTokenTarg.negResult) {
991         case SPNEGO_ACCEPT_INCOMPLETE:
992                 DEBUG(1, ("Got a Kerberos negTokenTarg with ACCEPT_INCOMPLETE\n"));
993                 x_fprintf(x_stdout, "BH\n");
994                 break;
995         case SPNEGO_ACCEPT_COMPLETED:
996                 DEBUG(10, ("Accept completed\n"));
997                 x_fprintf(x_stdout, "AF\n");
998                 break;
999         case SPNEGO_REJECT:
1000                 DEBUG(10, ("Rejected\n"));
1001                 x_fprintf(x_stdout, "NA\n");
1002                 break;
1003         default:
1004                 DEBUG(1, ("Got an invalid negTokenTarg\n"));
1005                 x_fprintf(x_stdout, "AF\n");
1006         }
1007 }
1008
1009 #endif
1010
1011 static void manage_gss_spnego_client_request(enum stdio_helper_mode stdio_helper_mode, 
1012                                              char *buf, int length) 
1013 {
1014         DATA_BLOB request;
1015         struct spnego_data spnego;
1016         ssize_t len;
1017
1018         if (strlen(buf) <= 3) {
1019                 DEBUG(1, ("SPNEGO query [%s] too short\n", buf));
1020                 x_fprintf(x_stdout, "BH\n");
1021                 return;
1022         }
1023
1024         request = base64_decode_data_blob(buf+3);
1025
1026         if (strncmp(buf, "PW ", 3) == 0) {
1027
1028                 /* We asked for a password and obviously got it :-) */
1029
1030                 opt_password = strndup((const char *)request.data, request.length);
1031                 
1032                 if (opt_password == NULL) {
1033                         DEBUG(1, ("Out of memory\n"));
1034                         x_fprintf(x_stdout, "BH\n");
1035                         data_blob_free(&request);
1036                         return;
1037                 }
1038
1039                 x_fprintf(x_stdout, "OK\n");
1040                 data_blob_free(&request);
1041                 return;
1042         }
1043
1044         if ( (strncmp(buf, "TT ", 3) != 0) &&
1045              (strncmp(buf, "AF ", 3) != 0) &&
1046              (strncmp(buf, "NA ", 3) != 0) ) {
1047                 DEBUG(1, ("SPNEGO request [%s] invalid\n", buf));
1048                 x_fprintf(x_stdout, "BH\n");
1049                 data_blob_free(&request);
1050                 return;
1051         }
1052
1053         /* So we got a server challenge to generate a SPNEGO
1054            client-to-server request... */
1055
1056         len = read_spnego_data(request, &spnego);
1057         data_blob_free(&request);
1058
1059         if (len == -1) {
1060                 DEBUG(1, ("Could not read SPNEGO data for [%s]\n", buf));
1061                 x_fprintf(x_stdout, "BH\n");
1062                 return;
1063         }
1064
1065         if (spnego.type == SPNEGO_NEG_TOKEN_INIT) {
1066
1067                 /* The server offers a list of mechanisms */
1068
1069                 char **mechType = spnego.negTokenInit.mechTypes;
1070
1071                 while (*mechType != NULL) {
1072
1073 #ifdef HAVE_KRB5
1074                         if ( (strcmp(*mechType, OID_KERBEROS5_OLD) == 0) ||
1075                              (strcmp(*mechType, OID_KERBEROS5) == 0) ) {
1076                                 if (manage_client_krb5_init(spnego))
1077                                         goto out;
1078                         }
1079 #endif
1080
1081                         if (strcmp(*mechType, OID_NTLMSSP) == 0) {
1082                                 if (manage_client_ntlmssp_init(spnego))
1083                                         goto out;
1084                         }
1085
1086                         mechType++;
1087                 }
1088
1089                 DEBUG(1, ("Server offered no compatible mechanism\n"));
1090                 x_fprintf(x_stdout, "BH\n");
1091                 return;
1092         }
1093
1094         if (spnego.type == SPNEGO_NEG_TOKEN_TARG) {
1095
1096                 if (spnego.negTokenTarg.supportedMech == NULL) {
1097                         /* On accept/reject Windows does not send the
1098                            mechanism anymore. Handle that here and
1099                            shut down the mechanisms. */
1100
1101                         switch (spnego.negTokenTarg.negResult) {
1102                         case SPNEGO_ACCEPT_COMPLETED:
1103                                 x_fprintf(x_stdout, "AF\n");
1104                                 break;
1105                         case SPNEGO_REJECT:
1106                                 x_fprintf(x_stdout, "NA\n");
1107                                 break;
1108                         default:
1109                                 DEBUG(1, ("Got a negTokenTarg with no mech and an "
1110                                           "unknown negResult: %d\n",
1111                                           spnego.negTokenTarg.negResult));
1112                                 x_fprintf(x_stdout, "BH\n");
1113                         }
1114
1115                         ntlmssp_end(&client_ntlmssp_state);
1116                         goto out;
1117                 }
1118
1119                 if (strcmp(spnego.negTokenTarg.supportedMech,
1120                            OID_NTLMSSP) == 0) {
1121                         manage_client_ntlmssp_targ(spnego);
1122                         goto out;
1123                 }
1124
1125 #if HAVE_KRB5
1126                 if (strcmp(spnego.negTokenTarg.supportedMech,
1127                            OID_KERBEROS5_OLD) == 0) {
1128                         manage_client_krb5_targ(spnego);
1129                         goto out;
1130                 }
1131 #endif
1132
1133         }
1134
1135         DEBUG(1, ("Got an SPNEGO token I could not handle [%s]!\n", buf));
1136         x_fprintf(x_stdout, "BH\n");
1137         return;
1138
1139  out:
1140         free_spnego_data(&spnego);
1141         return;
1142 }
1143
1144 static void manage_ntlm_server_1_request(enum stdio_helper_mode stdio_helper_mode, 
1145                                          char *buf, int length) 
1146 {
1147         char *request, *parameter;      
1148         static DATA_BLOB challenge;
1149         static DATA_BLOB lm_response;
1150         static DATA_BLOB nt_response;
1151         static char *full_username;
1152         static char *username;
1153         static char *domain;
1154         static char *plaintext_password;
1155         static BOOL ntlm_server_1_user_session_key;
1156         static BOOL ntlm_server_1_lm_session_key;
1157         
1158         if (strequal(buf, ".")) {
1159                 if (!full_username && !username) {      
1160                         x_fprintf(x_stdout, "Error: No username supplied!\n");
1161                 } else if (plaintext_password) {
1162                         /* handle this request as plaintext */
1163                         if (!full_username) {
1164                                 if (asprintf(&full_username, "%s%c%s", domain, *lp_winbind_separator(), username) == -1) {
1165                                         x_fprintf(x_stdout, "Error: Out of memory in asprintf!\n.\n");
1166                                         return;
1167                                 }
1168                         }
1169                         if (check_plaintext_auth(full_username, plaintext_password, False)) {
1170                                 x_fprintf(x_stdout, "Authenticated: Yes\n");
1171                         } else {
1172                                 x_fprintf(x_stdout, "Authenticated: No\n");
1173                         }
1174                 } else if (!lm_response.data && !nt_response.data) {
1175                         x_fprintf(x_stdout, "Error: No password supplied!\n");
1176                 } else if (!challenge.data) {   
1177                         x_fprintf(x_stdout, "Error: No lanman-challenge supplied!\n");
1178                 } else {
1179                         char *error_string = NULL;
1180                         DATA_BLOB lm_key;
1181                         DATA_BLOB user_session_key;
1182                         uint32 flags = 0;
1183
1184                         if (full_username && !username) {
1185                                 fstring fstr_user;
1186                                 fstring fstr_domain;
1187                                 
1188                                 if (!parse_ntlm_auth_domain_user(full_username, fstr_user, fstr_domain)) {
1189                                         /* username might be 'tainted', don't print into our new-line deleimianted stream */
1190                                         x_fprintf(x_stdout, "Error: Could not parse into domain and username\n");
1191                                 }
1192                                 SAFE_FREE(username);
1193                                 SAFE_FREE(domain);
1194                                 username = smb_xstrdup(fstr_user);
1195                                 domain = smb_xstrdup(fstr_domain);
1196                         }
1197
1198                         if (!domain) {
1199                                 domain = smb_xstrdup(lp_workgroup());
1200                         }
1201
1202                         if (ntlm_server_1_lm_session_key) 
1203                                 flags |= NTLM_AUTH_FLAG_LMKEY;
1204                         
1205                         if (ntlm_server_1_user_session_key) 
1206                                 flags |= NTLM_AUTH_FLAG_USER_SESSION_KEY;
1207
1208                         if (!NT_STATUS_IS_OK(
1209                                     local_pw_check_specified(username, 
1210                                                               domain, 
1211                                                               global_myname(),
1212                                                               &challenge, 
1213                                                               &lm_response, 
1214                                                               &nt_response, 
1215                                                               flags, 
1216                                                               &lm_key, 
1217                                                               &user_session_key,
1218                                                               &error_string,
1219                                                               NULL))) {
1220
1221                                 x_fprintf(x_stdout, "Authenticated: No\n");
1222                                 x_fprintf(x_stdout, "Authentication-Error: %s\n.\n", error_string);
1223                                 SAFE_FREE(error_string);
1224                         } else {
1225                                 static char zeros[16];
1226                                 char *hex_lm_key;
1227                                 char *hex_user_session_key;
1228
1229                                 x_fprintf(x_stdout, "Authenticated: Yes\n");
1230
1231                                 if (ntlm_server_1_lm_session_key 
1232                                     && lm_key.length 
1233                                     && (memcmp(zeros, lm_key.data, 
1234                                                                 lm_key.length) != 0)) {
1235                                         hex_encode(lm_key.data,
1236                                                    lm_key.length,
1237                                                    &hex_lm_key);
1238                                         x_fprintf(x_stdout, "LANMAN-Session-Key: %s\n", hex_lm_key);
1239                                         SAFE_FREE(hex_lm_key);
1240                                 }
1241
1242                                 if (ntlm_server_1_user_session_key 
1243                                     && user_session_key.length 
1244                                     && (memcmp(zeros, user_session_key.data, 
1245                                                user_session_key.length) != 0)) {
1246                                         hex_encode(user_session_key.data, 
1247                                                    user_session_key.length, 
1248                                                    &hex_user_session_key);
1249                                         x_fprintf(x_stdout, "User-Session-Key: %s\n", hex_user_session_key);
1250                                         SAFE_FREE(hex_user_session_key);
1251                                 }
1252                         }
1253                 }
1254                 /* clear out the state */
1255                 challenge = data_blob(NULL, 0);
1256                 nt_response = data_blob(NULL, 0);
1257                 lm_response = data_blob(NULL, 0);
1258                 SAFE_FREE(full_username);
1259                 SAFE_FREE(username);
1260                 SAFE_FREE(domain);
1261                 SAFE_FREE(plaintext_password);
1262                 ntlm_server_1_user_session_key = False;
1263                 ntlm_server_1_lm_session_key = False;
1264                 x_fprintf(x_stdout, ".\n");
1265
1266                 return;
1267         }
1268
1269         request = buf;
1270
1271         /* Indicates a base64 encoded structure */
1272         parameter = strstr(request, ":: ");
1273         if (!parameter) {
1274                 parameter = strstr(request, ": ");
1275                 
1276                 if (!parameter) {
1277                         DEBUG(0, ("Parameter not found!\n"));
1278                         x_fprintf(x_stdout, "Error: Parameter not found!\n.\n");
1279                         return;
1280                 }
1281                 
1282                 parameter[0] ='\0';
1283                 parameter++;
1284                 parameter[0] ='\0';
1285                 parameter++;
1286
1287         } else {
1288                 parameter[0] ='\0';
1289                 parameter++;
1290                 parameter[0] ='\0';
1291                 parameter++;
1292                 parameter[0] ='\0';
1293                 parameter++;
1294
1295                 base64_decode_inplace(parameter);
1296         }
1297
1298         if (strequal(request, "LANMAN-Challenge")) {
1299                 challenge = strhex_to_data_blob(parameter);
1300                 if (challenge.length != 8) {
1301                         x_fprintf(x_stdout, "Error: hex decode of %s failed! (got %d bytes, expected 8)\n.\n", 
1302                                   parameter,
1303                                   (int)challenge.length);
1304                         challenge = data_blob(NULL, 0);
1305                 }
1306         } else if (strequal(request, "NT-Response")) {
1307                 nt_response = strhex_to_data_blob(parameter);
1308                 if (nt_response.length < 24) {
1309                         x_fprintf(x_stdout, "Error: hex decode of %s failed! (only got %d bytes, needed at least 24)\n.\n", 
1310                                   parameter,
1311                                   (int)nt_response.length);
1312                         nt_response = data_blob(NULL, 0);
1313                 }
1314         } else if (strequal(request, "LANMAN-Response")) {
1315                 lm_response = strhex_to_data_blob(parameter);
1316                 if (lm_response.length != 24) {
1317                         x_fprintf(x_stdout, "Error: hex decode of %s failed! (got %d bytes, expected 24)\n.\n", 
1318                                   parameter,
1319                                   (int)lm_response.length);
1320                         lm_response = data_blob(NULL, 0);
1321                 }
1322         } else if (strequal(request, "Password")) {
1323                 plaintext_password = smb_xstrdup(parameter);
1324         } else if (strequal(request, "NT-Domain")) {
1325                 domain = smb_xstrdup(parameter);
1326         } else if (strequal(request, "Username")) {
1327                 username = smb_xstrdup(parameter);
1328         } else if (strequal(request, "Full-Username")) {
1329                 full_username = smb_xstrdup(parameter);
1330         } else if (strequal(request, "Request-User-Session-Key")) {
1331                 ntlm_server_1_user_session_key = strequal(parameter, "Yes");
1332         } else if (strequal(request, "Request-LanMan-Session-Key")) {
1333                 ntlm_server_1_lm_session_key = strequal(parameter, "Yes");
1334         } else {
1335                 x_fprintf(x_stdout, "Error: Unknown request %s\n.\n", request);
1336         }
1337 }
1338
1339 static void manage_squid_request(enum stdio_helper_mode helper_mode, stdio_helper_function fn) 
1340 {
1341         char buf[SQUID_BUFFER_SIZE+1];
1342         int length;
1343         char *c;
1344         static BOOL err;
1345
1346         /* this is not a typo - x_fgets doesn't work too well under squid */
1347         if (fgets(buf, sizeof(buf)-1, stdin) == NULL) {
1348                 if (ferror(stdin)) {
1349                         DEBUG(1, ("fgets() failed! dying..... errno=%d (%s)\n", ferror(stdin),
1350                                   strerror(ferror(stdin))));
1351                         
1352                         exit(1);    /* BIIG buffer */
1353                 }
1354                 exit(0);
1355         }
1356     
1357         c=memchr(buf,'\n',sizeof(buf)-1);
1358         if (c) {
1359                 *c = '\0';
1360                 length = c-buf;
1361         } else {
1362                 err = 1;
1363                 return;
1364         }
1365         if (err) {
1366                 DEBUG(2, ("Oversized message\n"));
1367                 x_fprintf(x_stderr, "ERR\n");
1368                 err = 0;
1369                 return;
1370         }
1371
1372         DEBUG(10, ("Got '%s' from squid (length: %d).\n",buf,length));
1373
1374         if (buf[0] == '\0') {
1375                 DEBUG(2, ("Invalid Request\n"));
1376                 x_fprintf(x_stderr, "ERR\n");
1377                 return;
1378         }
1379         
1380         fn(helper_mode, buf, length);
1381 }
1382
1383
1384 static void squid_stream(enum stdio_helper_mode stdio_mode, stdio_helper_function fn) {
1385         /* initialize FDescs */
1386         x_setbuf(x_stdout, NULL);
1387         x_setbuf(x_stderr, NULL);
1388         while(1) {
1389                 manage_squid_request(stdio_mode, fn);
1390         }
1391 }
1392
1393
1394 /* Main program */
1395
1396 enum {
1397         OPT_USERNAME = 1000,
1398         OPT_DOMAIN,
1399         OPT_WORKSTATION,
1400         OPT_CHALLENGE,
1401         OPT_RESPONSE,
1402         OPT_LM,
1403         OPT_NT,
1404         OPT_PASSWORD,
1405         OPT_LM_KEY,
1406         OPT_USER_SESSION_KEY,
1407         OPT_DIAGNOSTICS,
1408         OPT_REQUIRE_MEMBERSHIP
1409 };
1410
1411  int main(int argc, const char **argv)
1412 {
1413         static const char *helper_protocol;
1414         int opt;
1415
1416         poptContext pc;
1417
1418         /* NOTE: DO NOT change this interface without considering the implications!
1419            This is an external interface, which other programs will use to interact 
1420            with this helper.
1421         */
1422
1423         /* We do not use single-letter command abbreviations, because they harm future 
1424            interface stability. */
1425
1426         struct poptOption long_options[] = {
1427                 POPT_AUTOHELP
1428                 { "helper-protocol", 0, POPT_ARG_STRING, &helper_protocol, OPT_DOMAIN, "operate as a stdio-based helper", "helper protocol to use"},
1429                 { "domain", 0, POPT_ARG_STRING, &opt_domain, OPT_DOMAIN, "domain name"},
1430                 { "workstation", 0, POPT_ARG_STRING, &opt_workstation, OPT_WORKSTATION, "workstation"},
1431                 { "password", 0, POPT_ARG_STRING, &opt_password, OPT_PASSWORD, "User's plaintext password"},            
1432                 POPT_COMMON_SAMBA
1433                 POPT_TABLEEND
1434         };
1435
1436         /* Samba client initialisation */
1437
1438         setup_logging("ntlm_auth", DEBUG_STDOUT);
1439
1440         if (!lp_load(dyn_CONFIGFILE, True, False, False)) {
1441                 d_fprintf(stderr, "wbinfo: error opening config file %s. Error was %s\n",
1442                         dyn_CONFIGFILE, strerror(errno));
1443                 exit(1);
1444         }
1445
1446         /* Parse options */
1447
1448         pc = poptGetContext("ntlm_auth", argc, argv, long_options, 0);
1449
1450         /* Parse command line options */
1451
1452         if (argc == 1) {
1453                 poptPrintHelp(pc, stderr, 0);
1454                 return 1;
1455         }
1456
1457         pc = poptGetContext(NULL, argc, (const char **)argv, long_options, 
1458                             POPT_CONTEXT_KEEP_FIRST);
1459
1460         while((opt = poptGetNextOpt(pc)) != -1) {
1461                 if (opt < -1) {
1462                         break;
1463                 }
1464         }
1465         if (opt < -1) {
1466                 fprintf(stderr, "%s: %s\n",
1467                         poptBadOption(pc, POPT_BADOPTION_NOALIAS),
1468                         poptStrerror(opt));
1469                 return 1;
1470         }
1471
1472         if (helper_protocol) {
1473                 int i;
1474                 for (i=0; i<NUM_HELPER_MODES; i++) {
1475                         if (strcmp(helper_protocol, stdio_helper_protocols[i].name) == 0) {
1476                                 squid_stream(stdio_helper_protocols[i].mode, stdio_helper_protocols[i].fn);
1477                                 exit(0);
1478                         }
1479                 }
1480                 x_fprintf(x_stderr, "unknown helper protocol [%s]\n\nValid helper protools:\n\n", helper_protocol);
1481
1482                 for (i=0; i<NUM_HELPER_MODES; i++) {
1483                         x_fprintf(x_stderr, "%s\n", stdio_helper_protocols[i].name);
1484                 }
1485
1486                 exit(1);
1487         }
1488
1489         if (!opt_username) {
1490                 x_fprintf(x_stderr, "username must be specified!\n\n");
1491                 poptPrintHelp(pc, stderr, 0);
1492                 exit(1);
1493         }
1494
1495         if (opt_domain == NULL) {
1496                 opt_domain = lp_workgroup();
1497         }
1498
1499         if (opt_workstation == NULL) {
1500                 opt_workstation = lp_netbios_name();
1501         }
1502
1503         if (!opt_password) {
1504                 opt_password = getpass("password: ");
1505         }
1506
1507         {
1508                 char *user;
1509
1510                 asprintf(&user, "%s%c%s", opt_domain, *lp_winbind_separator(), opt_username);
1511                 if (!check_plaintext_auth(user, opt_password, True)) {
1512                         return 1;
1513                 }
1514         }
1515
1516         /* Exit code */
1517
1518         poptFreeContext(pc);
1519         return 0;
1520 }