Try to gain a bit more consistancy in the output of usernames from ntlm_auth:
[abartlet/samba.git/.git] / source3 / utils / ntlm_auth.c
1 /* 
2    Unix SMB/CIFS implementation.
3
4    Winbind status program.
5
6    Copyright (C) Tim Potter      2000-2002
7    Copyright (C) Andrew Bartlett <abartlet@samba.org> 2003
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         NUM_HELPER_MODES
40 };
41
42 enum ntlm_break {
43         BREAK_NONE,
44         BREAK_LM,
45         BREAK_NT,
46         NO_LM,
47         NO_NT
48 };
49
50 typedef void (*stdio_helper_function)(enum stdio_helper_mode stdio_helper_mode, 
51                                      char *buf, int length);
52
53 static void manage_squid_basic_request (enum stdio_helper_mode stdio_helper_mode, 
54                                         char *buf, int length);
55
56 static void manage_squid_ntlmssp_request (enum stdio_helper_mode stdio_helper_mode, 
57                                           char *buf, int length);
58
59 static void manage_client_ntlmssp_request (enum stdio_helper_mode stdio_helper_mode, 
60                                            char *buf, int length);
61
62 static void manage_gss_spnego_request (enum stdio_helper_mode stdio_helper_mode, 
63                                        char *buf, int length);
64
65 static void manage_gss_spnego_client_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         { NUM_HELPER_MODES, NULL, NULL}
80 };
81
82 extern int winbindd_fd;
83
84 static const char *opt_username;
85 static const char *opt_domain;
86 static const char *opt_workstation;
87 static const char *opt_password;
88 static DATA_BLOB opt_challenge;
89 static DATA_BLOB opt_lm_response;
90 static DATA_BLOB opt_nt_response;
91 static int request_lm_key;
92 static int request_nt_key;
93
94
95 static char winbind_separator(void)
96 {
97         struct winbindd_response response;
98         static BOOL got_sep;
99         static char sep;
100
101         if (got_sep)
102                 return sep;
103
104         ZERO_STRUCT(response);
105
106         /* Send off request */
107
108         if (winbindd_request(WINBINDD_INFO, NULL, &response) !=
109             NSS_STATUS_SUCCESS) {
110                 d_printf("could not obtain winbind separator!\n");
111                 return '\\';
112         }
113
114         sep = response.data.info.winbind_separator;
115         got_sep = True;
116
117         if (!sep) {
118                 d_printf("winbind separator was NULL!\n");
119                 return '\\';
120         }
121         
122         return sep;
123 }
124
125 static const char *get_winbind_domain(void)
126 {
127         struct winbindd_response response;
128
129         static fstring winbind_domain;
130         if (*winbind_domain) {
131                 return winbind_domain;
132         }
133
134         ZERO_STRUCT(response);
135
136         /* Send off request */
137
138         if (winbindd_request(WINBINDD_DOMAIN_NAME, NULL, &response) !=
139             NSS_STATUS_SUCCESS) {
140                 DEBUG(0, ("could not obtain winbind domain name!\n"));
141                 return NULL;
142         }
143
144         fstrcpy(winbind_domain, response.data.domain_name);
145
146         return winbind_domain;
147
148 }
149
150 static const char *get_winbind_netbios_name(void)
151 {
152         struct winbindd_response response;
153
154         static fstring winbind_netbios_name;
155
156         if (*winbind_netbios_name) {
157                 return winbind_netbios_name;
158         }
159
160         ZERO_STRUCT(response);
161
162         /* Send off request */
163
164         if (winbindd_request(WINBINDD_NETBIOS_NAME, NULL, &response) !=
165             NSS_STATUS_SUCCESS) {
166                 DEBUG(0, ("could not obtain winbind netbios name!\n"));
167                 return NULL;
168         }
169
170         fstrcpy(winbind_netbios_name, response.data.netbios_name);
171
172         return winbind_netbios_name;
173
174 }
175
176 /* Authenticate a user with a plaintext password */
177
178 static BOOL check_plaintext_auth(const char *user, const char *pass, BOOL stdout_diagnostics)
179 {
180         struct winbindd_request request;
181         struct winbindd_response response;
182         NSS_STATUS result;
183
184         /* Send off request */
185
186         ZERO_STRUCT(request);
187         ZERO_STRUCT(response);
188
189         fstrcpy(request.data.auth.user, user);
190         fstrcpy(request.data.auth.pass, pass);
191
192         result = winbindd_request(WINBINDD_PAM_AUTH, &request, &response);
193
194         /* Display response */
195         
196         if (stdout_diagnostics) {
197                 if ((result != NSS_STATUS_SUCCESS) && (response.data.auth.nt_status == 0)) {
198                         d_printf("Reading winbind reply failed! (0x01)\n");
199                 }
200                 
201                 d_printf("%s: %s (0x%x)\n", 
202                          response.data.auth.nt_status_string, 
203                          response.data.auth.error_string, 
204                          response.data.auth.nt_status);
205         } else {
206                 if ((result != NSS_STATUS_SUCCESS) && (response.data.auth.nt_status == 0)) {
207                         DEBUG(1, ("Reading winbind reply failed! (0x01)\n"));
208                 }
209                 
210                 DEBUG(3, ("%s: %s (0x%x)\n", 
211                           response.data.auth.nt_status_string, 
212                           response.data.auth.error_string,
213                           response.data.auth.nt_status));               
214         }
215                 
216         return (result == NSS_STATUS_SUCCESS);
217 }
218
219 /* authenticate a user with an encrypted username/password */
220
221 static NTSTATUS contact_winbind_auth_crap(const char *username, 
222                                           const char *domain, 
223                                           const char *workstation,
224                                           const DATA_BLOB *challenge, 
225                                           const DATA_BLOB *lm_response, 
226                                           const DATA_BLOB *nt_response, 
227                                           uint32 flags, 
228                                           uint8 lm_key[8], 
229                                           uint8 nt_key[16], 
230                                           char **error_string, 
231                                           char **unix_name) 
232 {
233         NTSTATUS nt_status;
234         NSS_STATUS result;
235         struct winbindd_request request;
236         struct winbindd_response response;
237
238         static uint8 zeros[16];
239
240         ZERO_STRUCT(request);
241         ZERO_STRUCT(response);
242
243         request.flags = flags;
244
245         if (push_utf8_fstring(request.data.auth_crap.user, username) == -1) {
246                 *error_string = smb_xstrdup(
247                         "unable to create utf8 string for username");
248                 return NT_STATUS_UNSUCCESSFUL;
249         }
250
251         if (push_utf8_fstring(request.data.auth_crap.domain, domain) == -1) {
252                 *error_string = smb_xstrdup(
253                         "unable to create utf8 string for domain");
254                 return NT_STATUS_UNSUCCESSFUL;
255         }
256
257         if (push_utf8_fstring(request.data.auth_crap.workstation, 
258                               workstation) == -1) {
259                 *error_string = smb_xstrdup(
260                         "unable to create utf8 string for workstation");
261                 return NT_STATUS_UNSUCCESSFUL;
262         }
263
264         memcpy(request.data.auth_crap.chal, challenge->data, MIN(challenge->length, 8));
265
266         if (lm_response && lm_response->length) {
267                 memcpy(request.data.auth_crap.lm_resp, lm_response->data, MIN(lm_response->length, sizeof(request.data.auth_crap.lm_resp)));
268                 request.data.auth_crap.lm_resp_len = lm_response->length;
269         }
270
271         if (nt_response && nt_response->length) {
272                 memcpy(request.data.auth_crap.nt_resp, nt_response->data, MIN(nt_response->length, sizeof(request.data.auth_crap.nt_resp)));
273                 request.data.auth_crap.nt_resp_len = nt_response->length;
274         }
275         
276         result = winbindd_request(WINBINDD_PAM_AUTH_CRAP, &request, &response);
277
278         /* Display response */
279
280         if ((result != NSS_STATUS_SUCCESS) && (response.data.auth.nt_status == 0)) {
281                 nt_status = NT_STATUS_UNSUCCESSFUL;
282                 if (error_string)
283                         *error_string = smb_xstrdup("Reading winbind reply failed!");
284                 return nt_status;
285         }
286         
287         nt_status = (NT_STATUS(response.data.auth.nt_status));
288         if (!NT_STATUS_IS_OK(nt_status)) {
289                 if (error_string) 
290                         *error_string = smb_xstrdup(response.data.auth.error_string);
291                 return nt_status;
292         }
293
294         if ((flags & WBFLAG_PAM_LMKEY) && lm_key 
295             && (memcmp(zeros, response.data.auth.first_8_lm_hash, 
296                        sizeof(response.data.auth.first_8_lm_hash)) != 0)) {
297                 memcpy(lm_key, response.data.auth.first_8_lm_hash, 
298                         sizeof(response.data.auth.first_8_lm_hash));
299         }
300         if ((flags & WBFLAG_PAM_NTKEY) && nt_key
301                     && (memcmp(zeros, response.data.auth.nt_session_key, 
302                                sizeof(response.data.auth.nt_session_key)) != 0)) {
303                 memcpy(nt_key, response.data.auth.nt_session_key, 
304                         sizeof(response.data.auth.nt_session_key));
305         }
306
307         if (flags & WBFLAG_PAM_UNIX_NAME) {
308                 *unix_name = response.extra_data;
309         }
310
311         return nt_status;
312 }
313                                    
314 static NTSTATUS winbind_pw_check(struct ntlmssp_state *ntlmssp_state, DATA_BLOB *nt_session_key, DATA_BLOB *lm_session_key) 
315 {
316         static const char zeros[16];
317         NTSTATUS nt_status;
318         char *error_string;
319         uint8 lm_key[8]; 
320         uint8 nt_key[16]; 
321         char *unix_name;
322
323         nt_status = contact_winbind_auth_crap(ntlmssp_state->user, ntlmssp_state->domain,
324                                               ntlmssp_state->workstation,
325                                               &ntlmssp_state->chal,
326                                               &ntlmssp_state->lm_resp,
327                                               &ntlmssp_state->nt_resp, 
328                                               WBFLAG_PAM_LMKEY | WBFLAG_PAM_NTKEY | WBFLAG_PAM_UNIX_NAME,
329                                               lm_key, nt_key, 
330                                               &error_string, &unix_name);
331
332         if (NT_STATUS_IS_OK(nt_status)) {
333                 if (memcmp(lm_key, zeros, 8) != 0) {
334                         *lm_session_key = data_blob(NULL, 16);
335                         memcpy(lm_session_key->data, lm_key, 8);
336                         memset(lm_session_key->data+8, '\0', 8);
337                 }
338                 
339                 if (memcmp(nt_key, zeros, 16) != 0) {
340                         *nt_session_key = data_blob(nt_key, 16);
341                 }
342                 ntlmssp_state->auth_context = talloc_strdup(ntlmssp_state->mem_ctx, unix_name);
343                 SAFE_FREE(unix_name);
344         } else {
345                 DEBUG(NT_STATUS_EQUAL(nt_status, NT_STATUS_ACCESS_DENIED) ? 0 : 3, 
346                       ("Login for user [%s]\\[%s]@[%s] failed due to [%s]\n", 
347                        ntlmssp_state->domain, ntlmssp_state->user, ntlmssp_state->workstation, error_string ? error_string : "unknown error (NULL)"));
348                 ntlmssp_state->auth_context = NULL;
349         }
350         return nt_status;
351 }
352
353 static NTSTATUS local_pw_check(struct ntlmssp_state *ntlmssp_state, DATA_BLOB *nt_session_key, DATA_BLOB *lm_session_key) 
354 {
355         static const char zeros[16];
356         NTSTATUS nt_status;
357         uint8 lm_key[8]; 
358         uint8 nt_key[16]; 
359         uint8 lm_pw[16], nt_pw[16];
360
361         nt_lm_owf_gen (opt_password, nt_pw, lm_pw);
362         
363         nt_status = ntlm_password_check(ntlmssp_state->mem_ctx, 
364                                         &ntlmssp_state->chal,
365                                         &ntlmssp_state->lm_resp,
366                                         &ntlmssp_state->nt_resp, 
367                                         ntlmssp_state->user, 
368                                         ntlmssp_state->user, 
369                                         ntlmssp_state->domain,
370                                         lm_pw, nt_pw, nt_session_key, lm_session_key);
371         
372         if (NT_STATUS_IS_OK(nt_status)) {
373                 if (memcmp(lm_key, zeros, 8) != 0) {
374                         *lm_session_key = data_blob(NULL, 16);
375                         memcpy(lm_session_key->data, lm_key, 8);
376                         memset(lm_session_key->data+8, '\0', 8);
377                 }
378                 
379                 if (memcmp(nt_key, zeros, 16) != 0) {
380                         *nt_session_key = data_blob(nt_key, 16);
381                 }
382                 ntlmssp_state->auth_context = talloc_asprintf(ntlmssp_state->mem_ctx, "%s%c%s", ntlmssp_state->domain, *lp_winbind_separator(), ntlmssp_state->user);
383         } else {
384                 DEBUG(3, ("Login for user [%s]\\[%s]@[%s] failed due to [%s]\n", 
385                           ntlmssp_state->domain, ntlmssp_state->user, ntlmssp_state->workstation, 
386                           nt_errstr(nt_status)));
387                 ntlmssp_state->auth_context = NULL;
388         }
389         return nt_status;
390 }
391
392 static NTSTATUS ntlm_auth_start_ntlmssp_client(NTLMSSP_STATE **client_ntlmssp_state) 
393 {
394         NTSTATUS status;
395         if ( (opt_username == NULL) || (opt_domain == NULL) ) {
396                 DEBUG(1, ("Need username and domain for NTLMSSP\n"));
397                 return status;
398         }
399
400         status = ntlmssp_client_start(client_ntlmssp_state);
401
402         if (!NT_STATUS_IS_OK(status)) {
403                 DEBUG(1, ("Could not start NTLMSSP client: %s\n",
404                           nt_errstr(status)));
405                 ntlmssp_end(client_ntlmssp_state);
406                 return status;
407         }
408
409         status = ntlmssp_set_username(*client_ntlmssp_state, opt_username);
410
411         if (!NT_STATUS_IS_OK(status)) {
412                 DEBUG(1, ("Could not set username: %s\n",
413                           nt_errstr(status)));
414                 ntlmssp_end(client_ntlmssp_state);
415                 return status;
416         }
417
418         status = ntlmssp_set_domain(*client_ntlmssp_state, opt_domain);
419
420         if (!NT_STATUS_IS_OK(status)) {
421                 DEBUG(1, ("Could not set domain: %s\n",
422                           nt_errstr(status)));
423                 ntlmssp_end(client_ntlmssp_state);
424                 return status;
425         }
426
427         status = ntlmssp_set_password(*client_ntlmssp_state, opt_password);
428         
429         if (!NT_STATUS_IS_OK(status)) {
430                 DEBUG(1, ("Could not set password: %s\n",
431                           nt_errstr(status)));
432                 ntlmssp_end(client_ntlmssp_state);
433                 return status;
434         }
435         return NT_STATUS_OK;
436 }
437
438 static NTSTATUS ntlm_auth_start_ntlmssp_server(NTLMSSP_STATE **ntlmssp_state) 
439 {
440         NTSTATUS status = ntlmssp_server_start(ntlmssp_state);
441         
442         if (!NT_STATUS_IS_OK(status)) {
443                 DEBUG(1, ("Could not start NTLMSSP client: %s\n",
444                           nt_errstr(status)));
445                 return status;
446         }
447
448         /* Have we been given a local password, or should we ask winbind? */
449         if (opt_password) {
450                 (*ntlmssp_state)->check_password = local_pw_check;
451                 (*ntlmssp_state)->get_domain = lp_workgroup;
452                 (*ntlmssp_state)->get_global_myname = global_myname;
453         } else {
454                 (*ntlmssp_state)->check_password = winbind_pw_check;
455                 (*ntlmssp_state)->get_domain = get_winbind_domain;
456                 (*ntlmssp_state)->get_global_myname = get_winbind_netbios_name;
457         }
458         return NT_STATUS_OK;
459 }
460
461 static void manage_squid_ntlmssp_request(enum stdio_helper_mode stdio_helper_mode, 
462                                          char *buf, int length) 
463 {
464         static NTLMSSP_STATE *ntlmssp_state = NULL;
465         DATA_BLOB request, reply;
466         NTSTATUS nt_status;
467
468         if (strlen(buf) < 2) {
469                 DEBUG(1, ("NTLMSSP query [%s] invalid", buf));
470                 x_fprintf(x_stdout, "BH\n");
471                 return;
472         }
473
474         if (strlen(buf) > 3) {
475                 request = base64_decode_data_blob(buf + 3);
476         } else {
477                 request = data_blob(NULL, 0);
478         }
479
480         if ((strncmp(buf, "PW ", 3) == 0)) {
481                 /* The calling application wants us to use a local password (rather than winbindd) */
482
483                 opt_password = strndup((const char *)request.data, request.length);
484
485                 if (opt_password == NULL) {
486                         DEBUG(1, ("Out of memory\n"));
487                         x_fprintf(x_stdout, "BH\n");
488                         data_blob_free(&request);
489                         return;
490                 }
491
492                 x_fprintf(x_stdout, "OK\n");
493                 data_blob_free(&request);
494                 return;
495         }
496
497         if (strncmp(buf, "YR", 2) == 0) {
498                 if (ntlmssp_state)
499                         ntlmssp_end(&ntlmssp_state);
500         } else if (strncmp(buf, "KK", 2) == 0) {
501                 
502         } else {
503                 DEBUG(1, ("NTLMSSP query [%s] invalid", buf));
504                 x_fprintf(x_stdout, "BH\n");
505                 return;
506         }
507
508         if (!ntlmssp_state) {
509                 if (!NT_STATUS_IS_OK(nt_status = ntlm_auth_start_ntlmssp_server(&ntlmssp_state))) {
510                         x_fprintf(x_stdout, "BH %s\n", nt_errstr(nt_status));
511                         return;
512                 }
513         }
514
515         DEBUG(10, ("got NTLMSSP packet:\n"));
516         dump_data(10, (const char *)request.data, request.length);
517
518         nt_status = ntlmssp_update(ntlmssp_state, request, &reply);
519         
520         if (NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
521                 char *reply_base64 = base64_encode_data_blob(reply);
522                 x_fprintf(x_stdout, "TT %s\n", reply_base64);
523                 SAFE_FREE(reply_base64);
524                 data_blob_free(&reply);
525                 DEBUG(10, ("NTLMSSP challenge\n"));
526         } else if (NT_STATUS_EQUAL(nt_status, NT_STATUS_ACCESS_DENIED)) {
527                 x_fprintf(x_stdout, "BH %s\n", nt_errstr(nt_status));
528                 DEBUG(0, ("NTLMSSP BH: %s\n", nt_errstr(nt_status)));
529
530                 ntlmssp_end(&ntlmssp_state);
531         } else if (!NT_STATUS_IS_OK(nt_status)) {
532                 x_fprintf(x_stdout, "NA %s\n", nt_errstr(nt_status));
533                 DEBUG(10, ("NTLMSSP %s\n", nt_errstr(nt_status)));
534         } else {
535                 x_fprintf(x_stdout, "AF %s\n", (char *)ntlmssp_state->auth_context);
536                 DEBUG(10, ("NTLMSSP OK!\n"));
537         }
538
539         data_blob_free(&request);
540 }
541
542 static void manage_client_ntlmssp_request(enum stdio_helper_mode stdio_helper_mode, 
543                                          char *buf, int length) 
544 {
545         static NTLMSSP_STATE *ntlmssp_state = NULL;
546         DATA_BLOB request, reply;
547         NTSTATUS nt_status;
548         BOOL first = False;
549         
550         if (strlen(buf) < 2) {
551                 DEBUG(1, ("NTLMSSP query [%s] invalid", buf));
552                 x_fprintf(x_stdout, "BH\n");
553                 return;
554         }
555
556         if (strlen(buf) > 3) {
557                 request = base64_decode_data_blob(buf + 3);
558         } else {
559                 request = data_blob(NULL, 0);
560         }
561
562         if (strncmp(buf, "PW ", 3) == 0) {
563                 /* We asked for a password and obviously got it :-) */
564
565                 opt_password = strndup((const char *)request.data, request.length);
566
567                 if (opt_password == NULL) {
568                         DEBUG(1, ("Out of memory\n"));
569                         x_fprintf(x_stdout, "BH\n");
570                         data_blob_free(&request);
571                         return;
572                 }
573
574                 x_fprintf(x_stdout, "OK\n");
575                 data_blob_free(&request);
576                 return;
577         }
578
579         if (opt_password == NULL) {
580                 
581                 /* Request a password from the calling process.  After
582                    sending it, the calling process should retry asking for the negotiate. */
583                 
584                 DEBUG(10, ("Requesting password\n"));
585                 x_fprintf(x_stdout, "PW\n");
586                 return;
587         }
588
589         if (strncmp(buf, "YR", 2) == 0) {
590                 if (ntlmssp_state)
591                         ntlmssp_end(&ntlmssp_state);
592         } else if (strncmp(buf, "TT", 2) == 0) {
593                 
594         } else {
595                 DEBUG(1, ("NTLMSSP query [%s] invalid", buf));
596                 x_fprintf(x_stdout, "BH\n");
597                 return;
598         }
599
600         if (!ntlmssp_state) {
601                 if (!NT_STATUS_IS_OK(nt_status = ntlm_auth_start_ntlmssp_client(&ntlmssp_state))) {
602                         x_fprintf(x_stdout, "BH %s\n", nt_errstr(nt_status));
603                         return;
604                 }
605                 first = True;
606         }
607
608         DEBUG(10, ("got NTLMSSP packet:\n"));
609         dump_data(10, (const char *)request.data, request.length);
610
611         nt_status = ntlmssp_update(ntlmssp_state, request, &reply);
612         
613         if (NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
614                 char *reply_base64 = base64_encode_data_blob(reply);
615                 if (first) {
616                         x_fprintf(x_stdout, "YR %s\n", reply_base64);
617                 } else { 
618                         x_fprintf(x_stdout, "KK %s\n", reply_base64);
619                 }
620                 SAFE_FREE(reply_base64);
621                 data_blob_free(&reply);
622                 DEBUG(10, ("NTLMSSP challenge\n"));
623         } else if (NT_STATUS_IS_OK(nt_status)) {
624                 x_fprintf(x_stdout, "AF\n");
625                 DEBUG(10, ("NTLMSSP OK!\n"));
626                 if (ntlmssp_state)
627                         ntlmssp_end(&ntlmssp_state);
628         } else {
629                 x_fprintf(x_stdout, "BH %s\n", nt_errstr(nt_status));
630                 DEBUG(0, ("NTLMSSP BH: %s\n", nt_errstr(nt_status)));
631                 if (ntlmssp_state)
632                         ntlmssp_end(&ntlmssp_state);
633         }
634
635         data_blob_free(&request);
636 }
637
638 static void manage_squid_basic_request(enum stdio_helper_mode stdio_helper_mode, 
639                                        char *buf, int length) 
640 {
641         char *user, *pass;      
642         user=buf;
643         
644         pass=memchr(buf,' ',length);
645         if (!pass) {
646                 DEBUG(2, ("Password not found. Denying access\n"));
647                 x_fprintf(x_stderr, "ERR\n");
648                 return;
649         }
650         *pass='\0';
651         pass++;
652         
653         if (stdio_helper_mode == SQUID_2_5_BASIC) {
654                 rfc1738_unescape(user);
655                 rfc1738_unescape(pass);
656         }
657         
658         if (check_plaintext_auth(user, pass, False)) {
659                 x_fprintf(x_stdout, "OK\n");
660         } else {
661                 x_fprintf(x_stdout, "ERR\n");
662         }
663 }
664
665 static void offer_gss_spnego_mechs(void) {
666
667         DATA_BLOB token;
668         SPNEGO_DATA spnego;
669         ssize_t len;
670         char *reply_base64;
671
672         pstring principal;
673         pstring myname_lower;
674
675         ZERO_STRUCT(spnego);
676
677         pstrcpy(myname_lower, global_myname());
678         strlower_m(myname_lower);
679
680         pstr_sprintf(principal, "%s$@%s", myname_lower, lp_realm());
681
682         /* Server negTokenInit (mech offerings) */
683         spnego.type = SPNEGO_NEG_TOKEN_INIT;
684         spnego.negTokenInit.mechTypes = smb_xmalloc(sizeof(char *) * 3);
685 #ifdef HAVE_KRB5
686         spnego.negTokenInit.mechTypes[0] = smb_xstrdup(OID_KERBEROS5_OLD);
687         spnego.negTokenInit.mechTypes[1] = smb_xstrdup(OID_NTLMSSP);
688         spnego.negTokenInit.mechTypes[2] = NULL;
689 #else
690         spnego.negTokenInit.mechTypes[0] = smb_xstrdup(OID_NTLMSSP);
691         spnego.negTokenInit.mechTypes[1] = NULL;
692 #endif
693
694
695         spnego.negTokenInit.mechListMIC = data_blob(principal,
696                                                     strlen(principal));
697
698         len = write_spnego_data(&token, &spnego);
699         free_spnego_data(&spnego);
700
701         if (len == -1) {
702                 DEBUG(1, ("Could not write SPNEGO data blob\n"));
703                 x_fprintf(x_stdout, "BH\n");
704                 return;
705         }
706
707         reply_base64 = base64_encode_data_blob(token);
708         x_fprintf(x_stdout, "TT %s *\n", reply_base64);
709
710         SAFE_FREE(reply_base64);
711         data_blob_free(&token);
712         DEBUG(10, ("sent SPNEGO negTokenInit\n"));
713         return;
714 }
715
716 static void manage_gss_spnego_request(enum stdio_helper_mode stdio_helper_mode, 
717                                       char *buf, int length) 
718 {
719         static NTLMSSP_STATE *ntlmssp_state = NULL;
720         SPNEGO_DATA request, response;
721         DATA_BLOB token;
722         NTSTATUS status;
723         ssize_t len;
724
725         char *user = NULL;
726         char *domain = NULL;
727
728         const char *reply_code;
729         char       *reply_base64;
730         pstring     reply_argument;
731
732         if (strlen(buf) < 2) {
733
734                 if (ntlmssp_state != NULL) {
735                         DEBUG(1, ("Request for initial SPNEGO request where "
736                                   "we already have a state\n"));
737                         x_fprintf(x_stdout, "BH\n");
738                         return;
739                 }
740
741                 DEBUG(1, ("NTLMSSP query [%s] invalid", buf));
742                 x_fprintf(x_stdout, "BH\n");
743                 return;
744         }
745
746         if ( (strlen(buf) == 2) && (strcmp(buf, "YR") == 0) ) {
747
748                 /* Initial request, get the negTokenInit offering
749                    mechanisms */
750
751                 offer_gss_spnego_mechs();
752                 return;
753         }
754
755         /* All subsequent requests are "KK" (Knock, Knock ;)) and have
756            a blob. This might be negTokenInit or negTokenTarg */
757
758         if ( (strlen(buf) <= 3) || (strncmp(buf, "KK", 2) != 0) ) {
759                 DEBUG(1, ("GSS-SPNEGO query [%s] invalid\n", buf));
760                 x_fprintf(x_stdout, "BH\n");
761                 return;
762         }
763
764         token = base64_decode_data_blob(buf + 3);
765         len = read_spnego_data(token, &request);
766         data_blob_free(&token);
767
768         if (len == -1) {
769                 DEBUG(1, ("GSS-SPNEGO query [%s] invalid", buf));
770                 x_fprintf(x_stdout, "BH\n");
771                 return;
772         }
773
774         if (request.type == SPNEGO_NEG_TOKEN_INIT) {
775
776                 /* Second request from Client. This is where the
777                    client offers its mechanism to use. */
778
779                 if ( (request.negTokenInit.mechTypes == NULL) ||
780                      (request.negTokenInit.mechTypes[0] == NULL) ) {
781                         DEBUG(1, ("Client did not offer any mechanism"));
782                         x_fprintf(x_stdout, "BH\n");
783                         return;
784                 }
785
786                 if (strcmp(request.negTokenInit.mechTypes[0], OID_NTLMSSP) == 0) {
787
788                         if ( request.negTokenInit.mechToken.data == NULL ) {
789                                 DEBUG(1, ("Client did not provide  NTLMSSP data\n"));
790                                 x_fprintf(x_stdout, "BH\n");
791                                 return;
792                         }
793
794                         if ( ntlmssp_state != NULL ) {
795                                 DEBUG(1, ("Client wants a new NTLMSSP challenge, but "
796                                           "already got one\n"));
797                                 x_fprintf(x_stdout, "BH\n");
798                                 ntlmssp_end(&ntlmssp_state);
799                                 return;
800                         }
801
802                         if (!NT_STATUS_IS_OK(status = ntlm_auth_start_ntlmssp_server(&ntlmssp_state))) {
803                                 x_fprintf(x_stdout, "BH %s\n", nt_errstr(status));
804                                 return;
805                         }
806
807                         DEBUG(10, ("got NTLMSSP packet:\n"));
808                         dump_data(10, (const char *)request.negTokenInit.mechToken.data,
809                                   request.negTokenInit.mechToken.length);
810
811                         response.type = SPNEGO_NEG_TOKEN_TARG;
812                         response.negTokenTarg.supportedMech = strdup(OID_NTLMSSP);
813                         response.negTokenTarg.mechListMIC = data_blob(NULL, 0);
814
815                         status = ntlmssp_update(ntlmssp_state,
816                                                        request.negTokenInit.mechToken,
817                                                        &response.negTokenTarg.responseToken);
818                 }
819
820 #ifdef HAVE_KRB5
821                 if (strcmp(request.negTokenInit.mechTypes[0], OID_KERBEROS5_OLD) == 0) {
822
823                         char *principal;
824                         DATA_BLOB auth_data;
825                         DATA_BLOB ap_rep;
826                         DATA_BLOB session_key;
827
828                         if ( request.negTokenInit.mechToken.data == NULL ) {
829                                 DEBUG(1, ("Client did not provide Kerberos data\n"));
830                                 x_fprintf(x_stdout, "BH\n");
831                                 return;
832                         }
833
834                         response.type = SPNEGO_NEG_TOKEN_TARG;
835                         response.negTokenTarg.supportedMech = strdup(OID_KERBEROS5_OLD);
836                         response.negTokenTarg.mechListMIC = data_blob(NULL, 0);
837                         response.negTokenTarg.responseToken = data_blob(NULL, 0);
838
839                         status = ads_verify_ticket(lp_realm(),
840                                                    &request.negTokenInit.mechToken,
841                                                    &principal, &auth_data, &ap_rep,
842                                                    &session_key);
843
844                         /* Now in "principal" we have the name we are
845                            authenticated as. */
846
847                         if (NT_STATUS_IS_OK(status)) {
848
849                                 domain = strchr(principal, '@');
850
851                                 if (domain == NULL) {
852                                         DEBUG(1, ("Did not get a valid principal "
853                                                   "from ads_verify_ticket\n"));
854                                         x_fprintf(x_stdout, "BH\n");
855                                         return;
856                                 }
857
858                                 *domain++ = '\0';
859                                 domain = strdup(domain);
860                                 user = strdup(principal);
861
862                                 data_blob_free(&ap_rep);
863                                 data_blob_free(&auth_data);
864
865                                 SAFE_FREE(principal);
866                         }
867                 }
868 #endif
869
870         } else {
871
872                 if ( (request.negTokenTarg.supportedMech == NULL) ||
873                      ( strcmp(request.negTokenTarg.supportedMech, OID_NTLMSSP) != 0 ) ) {
874                         /* Kerberos should never send a negTokenTarg, OID_NTLMSSP
875                            is the only one we support that sends this stuff */
876                         DEBUG(1, ("Got a negTokenTarg for something non-NTLMSSP: %s\n",
877                                   request.negTokenTarg.supportedMech));
878                         x_fprintf(x_stdout, "BH\n");
879                         return;
880                 }
881
882                 if (request.negTokenTarg.responseToken.data == NULL) {
883                         DEBUG(1, ("Got a negTokenTarg without a responseToken!\n"));
884                         x_fprintf(x_stdout, "BH\n");
885                         return;
886                 }
887
888                 status = ntlmssp_update(ntlmssp_state,
889                                                request.negTokenTarg.responseToken,
890                                                &response.negTokenTarg.responseToken);
891
892                 response.type = SPNEGO_NEG_TOKEN_TARG;
893                 response.negTokenTarg.supportedMech = strdup(OID_NTLMSSP);
894                 response.negTokenTarg.mechListMIC = data_blob(NULL, 0);
895
896                 if (NT_STATUS_IS_OK(status)) {
897                         user = strdup(ntlmssp_state->user);
898                         domain = strdup(ntlmssp_state->domain);
899                         ntlmssp_end(&ntlmssp_state);
900                 }
901         }
902
903         free_spnego_data(&request);
904
905         if (NT_STATUS_IS_OK(status)) {
906                 response.negTokenTarg.negResult = SPNEGO_ACCEPT_COMPLETED;
907                 reply_code = "AF";
908                 pstr_sprintf(reply_argument, "%s\\%s", domain, user);
909         } else if (NT_STATUS_EQUAL(status,
910                                    NT_STATUS_MORE_PROCESSING_REQUIRED)) {
911                 response.negTokenTarg.negResult = SPNEGO_ACCEPT_INCOMPLETE;
912                 reply_code = "TT";
913                 pstr_sprintf(reply_argument, "*");
914         } else {
915                 response.negTokenTarg.negResult = SPNEGO_REJECT;
916                 reply_code = "NA";
917                 pstrcpy(reply_argument, nt_errstr(status));
918         }
919
920         SAFE_FREE(user);
921         SAFE_FREE(domain);
922
923         len = write_spnego_data(&token, &response);
924         free_spnego_data(&response);
925
926         if (len == -1) {
927                 DEBUG(1, ("Could not write SPNEGO data blob\n"));
928                 x_fprintf(x_stdout, "BH\n");
929                 return;
930         }
931
932         reply_base64 = base64_encode_data_blob(token);
933
934         x_fprintf(x_stdout, "%s %s %s\n",
935                   reply_code, reply_base64, reply_argument);
936
937         SAFE_FREE(reply_base64);
938         data_blob_free(&token);
939
940         return;
941 }
942
943 static NTLMSSP_STATE *client_ntlmssp_state = NULL;
944
945 static BOOL manage_client_ntlmssp_init(SPNEGO_DATA spnego)
946 {
947         NTSTATUS status;
948         DATA_BLOB null_blob = data_blob(NULL, 0);
949         DATA_BLOB to_server;
950         char *to_server_base64;
951         const char *my_mechs[] = {OID_NTLMSSP, NULL};
952
953         DEBUG(10, ("Got spnego negTokenInit with NTLMSSP\n"));
954
955         if (client_ntlmssp_state != NULL) {
956                 DEBUG(1, ("Request for initial SPNEGO request where "
957                           "we already have a state\n"));
958                 return False;
959         }
960
961         if (!client_ntlmssp_state) {
962                 if (!NT_STATUS_IS_OK(status = ntlm_auth_start_ntlmssp_client(&client_ntlmssp_state))) {
963                         x_fprintf(x_stdout, "BH %s\n", nt_errstr(status));
964                         return False;
965                 }
966         }
967
968
969         if (opt_password == NULL) {
970
971                 /* Request a password from the calling process.  After
972                    sending it, the calling process should retry with
973                    the negTokenInit. */
974
975                 DEBUG(10, ("Requesting password\n"));
976                 x_fprintf(x_stdout, "PW\n");
977                 return True;
978         }
979
980         spnego.type = SPNEGO_NEG_TOKEN_INIT;
981         spnego.negTokenInit.mechTypes = my_mechs;
982         spnego.negTokenInit.reqFlags = 0;
983         spnego.negTokenInit.mechListMIC = null_blob;
984
985         status = ntlmssp_update(client_ntlmssp_state, null_blob,
986                                        &spnego.negTokenInit.mechToken);
987
988         if (!NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
989                 DEBUG(1, ("Expected MORE_PROCESSING_REQUIRED, got: %s\n",
990                           nt_errstr(status)));
991                 ntlmssp_end(&client_ntlmssp_state);
992                 return False;
993         }
994
995         write_spnego_data(&to_server, &spnego);
996         data_blob_free(&spnego.negTokenInit.mechToken);
997
998         to_server_base64 = base64_encode_data_blob(to_server);
999         data_blob_free(&to_server);
1000         x_fprintf(x_stdout, "KK %s\n", to_server_base64);
1001         SAFE_FREE(to_server_base64);
1002         return True;
1003 }
1004
1005 static void manage_client_ntlmssp_targ(SPNEGO_DATA spnego)
1006 {
1007         NTSTATUS status;
1008         DATA_BLOB null_blob = data_blob(NULL, 0);
1009         DATA_BLOB request;
1010         DATA_BLOB to_server;
1011         char *to_server_base64;
1012
1013         DEBUG(10, ("Got spnego negTokenTarg with NTLMSSP\n"));
1014
1015         if (client_ntlmssp_state == NULL) {
1016                 DEBUG(1, ("Got NTLMSSP tArg without a client state\n"));
1017                 x_fprintf(x_stdout, "BH\n");
1018                 ntlmssp_end(&client_ntlmssp_state);
1019                 return;
1020         }
1021
1022         if (spnego.negTokenTarg.negResult == SPNEGO_REJECT) {
1023                 x_fprintf(x_stdout, "NA\n");
1024                 ntlmssp_end(&client_ntlmssp_state);
1025                 return;
1026         }
1027
1028         if (spnego.negTokenTarg.negResult == SPNEGO_ACCEPT_COMPLETED) {
1029                 x_fprintf(x_stdout, "AF\n");
1030                 ntlmssp_end(&client_ntlmssp_state);
1031                 return;
1032         }
1033
1034         status = ntlmssp_update(client_ntlmssp_state,
1035                                        spnego.negTokenTarg.responseToken,
1036                                        &request);
1037                 
1038         if (!NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
1039                 DEBUG(1, ("Expected MORE_PROCESSING_REQUIRED from "
1040                           "ntlmssp_client_update, got: %s\n",
1041                           nt_errstr(status)));
1042                 x_fprintf(x_stdout, "BH\n");
1043                 data_blob_free(&request);
1044                 ntlmssp_end(&client_ntlmssp_state);
1045                 return;
1046         }
1047
1048         spnego.type = SPNEGO_NEG_TOKEN_TARG;
1049         spnego.negTokenTarg.negResult = SPNEGO_ACCEPT_INCOMPLETE;
1050         spnego.negTokenTarg.supportedMech = OID_NTLMSSP;
1051         spnego.negTokenTarg.responseToken = request;
1052         spnego.negTokenTarg.mechListMIC = null_blob;
1053         
1054         write_spnego_data(&to_server, &spnego);
1055         data_blob_free(&request);
1056
1057         to_server_base64 = base64_encode_data_blob(to_server);
1058         data_blob_free(&to_server);
1059         x_fprintf(x_stdout, "KK %s\n", to_server_base64);
1060         SAFE_FREE(to_server_base64);
1061         return;
1062 }
1063
1064 #ifdef HAVE_KRB5
1065
1066 static BOOL manage_client_krb5_init(SPNEGO_DATA spnego)
1067 {
1068         char *principal;
1069         DATA_BLOB tkt, to_server;
1070         DATA_BLOB session_key_krb5;
1071         SPNEGO_DATA reply;
1072         char *reply_base64;
1073         
1074         const char *my_mechs[] = {OID_KERBEROS5_OLD, NULL};
1075         ssize_t len;
1076
1077         if ( (spnego.negTokenInit.mechListMIC.data == NULL) ||
1078              (spnego.negTokenInit.mechListMIC.length == 0) ) {
1079                 DEBUG(1, ("Did not get a principal for krb5\n"));
1080                 return False;
1081         }
1082
1083         principal = malloc(spnego.negTokenInit.mechListMIC.length+1);
1084
1085         if (principal == NULL) {
1086                 DEBUG(1, ("Could not malloc principal\n"));
1087                 return False;
1088         }
1089
1090         memcpy(principal, spnego.negTokenInit.mechListMIC.data,
1091                spnego.negTokenInit.mechListMIC.length);
1092         principal[spnego.negTokenInit.mechListMIC.length] = '\0';
1093
1094         tkt = cli_krb5_get_ticket(principal, 0, &session_key_krb5);
1095
1096         if (tkt.data == NULL) {
1097
1098                 pstring user;
1099
1100                 /* Let's try to first get the TGT, for that we need a
1101                    password. */
1102
1103                 if (opt_password == NULL) {
1104                         DEBUG(10, ("Requesting password\n"));
1105                         x_fprintf(x_stdout, "PW\n");
1106                         return True;
1107                 }
1108
1109                 pstr_sprintf(user, "%s@%s", opt_username, opt_domain);
1110
1111                 if (kerberos_kinit_password(user, opt_password, 0) != 0) {
1112                         DEBUG(10, ("Requesting TGT failed\n"));
1113                         x_fprintf(x_stdout, "NA\n");
1114                         return True;
1115                 }
1116
1117                 tkt = cli_krb5_get_ticket(principal, 0, &session_key_krb5);
1118         }
1119
1120         data_blob_free(&session_key_krb5);
1121
1122         ZERO_STRUCT(reply);
1123
1124         reply.type = SPNEGO_NEG_TOKEN_INIT;
1125         reply.negTokenInit.mechTypes = my_mechs;
1126         reply.negTokenInit.reqFlags = 0;
1127         reply.negTokenInit.mechToken = tkt;
1128         reply.negTokenInit.mechListMIC = data_blob(NULL, 0);
1129
1130         len = write_spnego_data(&to_server, &reply);
1131         data_blob_free(&tkt);
1132
1133         if (len == -1) {
1134                 DEBUG(1, ("Could not write SPNEGO data blob\n"));
1135                 return False;
1136         }
1137
1138         reply_base64 = base64_encode_data_blob(to_server);
1139         x_fprintf(x_stdout, "KK %s *\n", reply_base64);
1140
1141         SAFE_FREE(reply_base64);
1142         data_blob_free(&to_server);
1143         DEBUG(10, ("sent GSS-SPNEGO KERBEROS5 negTokenInit\n"));
1144         return True;
1145 }
1146
1147 static void manage_client_krb5_targ(SPNEGO_DATA spnego)
1148 {
1149         switch (spnego.negTokenTarg.negResult) {
1150         case SPNEGO_ACCEPT_INCOMPLETE:
1151                 DEBUG(1, ("Got a Kerberos negTokenTarg with ACCEPT_INCOMPLETE\n"));
1152                 x_fprintf(x_stdout, "BH\n");
1153                 break;
1154         case SPNEGO_ACCEPT_COMPLETED:
1155                 DEBUG(10, ("Accept completed\n"));
1156                 x_fprintf(x_stdout, "AF\n");
1157                 break;
1158         case SPNEGO_REJECT:
1159                 DEBUG(10, ("Rejected\n"));
1160                 x_fprintf(x_stdout, "NA\n");
1161                 break;
1162         default:
1163                 DEBUG(1, ("Got an invalid negTokenTarg\n"));
1164                 x_fprintf(x_stdout, "AF\n");
1165         }
1166 }
1167
1168 #endif
1169
1170 static void manage_gss_spnego_client_request(enum stdio_helper_mode stdio_helper_mode, 
1171                                              char *buf, int length) 
1172 {
1173         DATA_BLOB request;
1174         SPNEGO_DATA spnego;
1175         ssize_t len;
1176
1177         if (strlen(buf) <= 3) {
1178                 DEBUG(1, ("SPNEGO query [%s] too short\n", buf));
1179                 x_fprintf(x_stdout, "BH\n");
1180                 return;
1181         }
1182
1183         request = base64_decode_data_blob(buf+3);
1184
1185         if (strncmp(buf, "PW ", 3) == 0) {
1186
1187                 /* We asked for a password and obviously got it :-) */
1188
1189                 opt_password = strndup((const char *)request.data, request.length);
1190
1191                 if (opt_password == NULL) {
1192                         DEBUG(1, ("Out of memory\n"));
1193                         x_fprintf(x_stdout, "BH\n");
1194                         data_blob_free(&request);
1195                         return;
1196                 }
1197
1198                 x_fprintf(x_stdout, "OK\n");
1199                 data_blob_free(&request);
1200                 return;
1201         }
1202
1203         if ( (strncmp(buf, "TT ", 3) != 0) &&
1204              (strncmp(buf, "AF ", 3) != 0) &&
1205              (strncmp(buf, "NA ", 3) != 0) ) {
1206                 DEBUG(1, ("SPNEGO request [%s] invalid\n", buf));
1207                 x_fprintf(x_stdout, "BH\n");
1208                 data_blob_free(&request);
1209                 return;
1210         }
1211
1212         /* So we got a server challenge to generate a SPNEGO
1213            client-to-server request... */
1214
1215         len = read_spnego_data(request, &spnego);
1216         data_blob_free(&request);
1217
1218         if (len == -1) {
1219                 DEBUG(1, ("Could not read SPNEGO data for [%s]\n", buf));
1220                 x_fprintf(x_stdout, "BH\n");
1221                 return;
1222         }
1223
1224         if (spnego.type == SPNEGO_NEG_TOKEN_INIT) {
1225
1226                 /* The server offers a list of mechanisms */
1227
1228                 const char **mechType = spnego.negTokenInit.mechTypes;
1229
1230                 while (*mechType != NULL) {
1231
1232 #ifdef HAVE_KRB5
1233                         if ( (strcmp(*mechType, OID_KERBEROS5_OLD) == 0) ||
1234                              (strcmp(*mechType, OID_KERBEROS5) == 0) ) {
1235                                 if (manage_client_krb5_init(spnego))
1236                                         goto out;
1237                         }
1238 #endif
1239
1240                         if (strcmp(*mechType, OID_NTLMSSP) == 0) {
1241                                 if (manage_client_ntlmssp_init(spnego))
1242                                         goto out;
1243                         }
1244
1245                         mechType++;
1246                 }
1247
1248                 DEBUG(1, ("Server offered no compatible mechanism\n"));
1249                 x_fprintf(x_stdout, "BH\n");
1250                 return;
1251         }
1252
1253         if (spnego.type == SPNEGO_NEG_TOKEN_TARG) {
1254
1255                 if (spnego.negTokenTarg.supportedMech == NULL) {
1256                         /* On accept/reject Windows does not send the
1257                            mechanism anymore. Handle that here and
1258                            shut down the mechanisms. */
1259
1260                         switch (spnego.negTokenTarg.negResult) {
1261                         case SPNEGO_ACCEPT_COMPLETED:
1262                                 x_fprintf(x_stdout, "AF\n");
1263                                 break;
1264                         case SPNEGO_REJECT:
1265                                 x_fprintf(x_stdout, "NA\n");
1266                                 break;
1267                         default:
1268                                 DEBUG(1, ("Got a negTokenTarg with no mech and an "
1269                                           "unknown negResult: %d\n",
1270                                           spnego.negTokenTarg.negResult));
1271                                 x_fprintf(x_stdout, "BH\n");
1272                         }
1273
1274                         ntlmssp_end(&client_ntlmssp_state);
1275                         goto out;
1276                 }
1277
1278                 if (strcmp(spnego.negTokenTarg.supportedMech,
1279                            OID_NTLMSSP) == 0) {
1280                         manage_client_ntlmssp_targ(spnego);
1281                         goto out;
1282                 }
1283
1284 #if HAVE_KRB5
1285                 if (strcmp(spnego.negTokenTarg.supportedMech,
1286                            OID_KERBEROS5_OLD) == 0) {
1287                         manage_client_krb5_targ(spnego);
1288                         goto out;
1289                 }
1290 #endif
1291
1292         }
1293
1294         DEBUG(1, ("Got an SPNEGO token I could not handle [%s]!\n", buf));
1295         x_fprintf(x_stdout, "BH\n");
1296         return;
1297
1298  out:
1299         free_spnego_data(&spnego);
1300         return;
1301 }
1302
1303 static void manage_squid_request(enum stdio_helper_mode helper_mode, stdio_helper_function fn) 
1304 {
1305         char buf[SQUID_BUFFER_SIZE+1];
1306         int length;
1307         char *c;
1308         static BOOL err;
1309
1310         /* this is not a typo - x_fgets doesn't work too well under squid */
1311         if (fgets(buf, sizeof(buf)-1, stdin) == NULL) {
1312                 DEBUG(1, ("fgets() failed! dying..... errno=%d (%s)\n", ferror(stdin),
1313                           strerror(ferror(stdin))));
1314                 exit(1);    /* BIIG buffer */
1315         }
1316     
1317         c=memchr(buf,'\n',sizeof(buf)-1);
1318         if (c) {
1319                 *c = '\0';
1320                 length = c-buf;
1321         } else {
1322                 err = 1;
1323                 return;
1324         }
1325         if (err) {
1326                 DEBUG(2, ("Oversized message\n"));
1327                 x_fprintf(x_stderr, "ERR\n");
1328                 err = 0;
1329                 return;
1330         }
1331
1332         DEBUG(10, ("Got '%s' from squid (length: %d).\n",buf,length));
1333
1334         if (buf[0] == '\0') {
1335                 DEBUG(2, ("Invalid Request\n"));
1336                 x_fprintf(x_stderr, "ERR\n");
1337                 return;
1338         }
1339         
1340         fn(helper_mode, buf, length);
1341 }
1342
1343
1344 static void squid_stream(enum stdio_helper_mode stdio_mode, stdio_helper_function fn) {
1345         /* initialize FDescs */
1346         x_setbuf(x_stdout, NULL);
1347         x_setbuf(x_stderr, NULL);
1348         while(1) {
1349                 manage_squid_request(stdio_mode, fn);
1350         }
1351 }
1352
1353
1354 /* Authenticate a user with a challenge/response */
1355
1356 static BOOL check_auth_crap(void)
1357 {
1358         NTSTATUS nt_status;
1359         uint32 flags = 0;
1360         char lm_key[8];
1361         char nt_key[16];
1362         char *hex_lm_key;
1363         char *hex_nt_key;
1364         char *error_string;
1365         static uint8 zeros[16];
1366
1367         x_setbuf(x_stdout, NULL);
1368
1369         if (request_lm_key) 
1370                 flags |= WBFLAG_PAM_LMKEY;
1371
1372         if (request_nt_key) 
1373                 flags |= WBFLAG_PAM_NTKEY;
1374
1375         nt_status = contact_winbind_auth_crap(opt_username, opt_domain, 
1376                                               opt_workstation,
1377                                               &opt_challenge, 
1378                                               &opt_lm_response, 
1379                                               &opt_nt_response, 
1380                                               flags,
1381                                               (unsigned char *)lm_key, 
1382                                               (unsigned char *)nt_key, 
1383                                               &error_string, NULL);
1384
1385         if (!NT_STATUS_IS_OK(nt_status)) {
1386                 x_fprintf(x_stdout, "%s (0x%x)\n", 
1387                           error_string,
1388                           NT_STATUS_V(nt_status));
1389                 SAFE_FREE(error_string);
1390                 return False;
1391         }
1392
1393         if (request_lm_key 
1394             && (memcmp(zeros, lm_key, 
1395                        sizeof(lm_key)) != 0)) {
1396                 hex_encode((const unsigned char *)lm_key,
1397                            sizeof(lm_key),
1398                            &hex_lm_key);
1399                 x_fprintf(x_stdout, "LM_KEY: %s\n", hex_lm_key);
1400                 SAFE_FREE(hex_lm_key);
1401         }
1402         if (request_nt_key 
1403             && (memcmp(zeros, nt_key, 
1404                        sizeof(nt_key)) != 0)) {
1405                 hex_encode((const unsigned char *)nt_key, 
1406                            sizeof(nt_key), 
1407                            &hex_nt_key);
1408                 x_fprintf(x_stdout, "NT_KEY: %s\n", hex_nt_key);
1409                 SAFE_FREE(hex_nt_key);
1410         }
1411
1412         return True;
1413 }
1414
1415 /* 
1416    Authenticate a user with a challenge/response, checking session key
1417    and valid authentication types
1418 */
1419
1420 static DATA_BLOB get_challenge(void) 
1421 {
1422         static DATA_BLOB chal;
1423         if (opt_challenge.length)
1424                 return opt_challenge;
1425         
1426         chal = data_blob(NULL, 8);
1427
1428         generate_random_buffer(chal.data, chal.length, False);
1429         return chal;
1430 }
1431
1432 /* 
1433  * Test the normal 'LM and NTLM' combination
1434  */
1435
1436 static BOOL test_lm_ntlm_broken(enum ntlm_break break_which) 
1437 {
1438         BOOL pass = True;
1439         NTSTATUS nt_status;
1440         uint32 flags = 0;
1441         DATA_BLOB lm_response = data_blob(NULL, 24);
1442         DATA_BLOB nt_response = data_blob(NULL, 24);
1443         DATA_BLOB session_key = data_blob(NULL, 16);
1444
1445         uchar lm_key[8];
1446         uchar nt_key[16];
1447         uchar lm_hash[16];
1448         uchar nt_hash[16];
1449         DATA_BLOB chall = get_challenge();
1450         char *error_string;
1451         
1452         ZERO_STRUCT(lm_key);
1453         ZERO_STRUCT(nt_key);
1454
1455         flags |= WBFLAG_PAM_LMKEY;
1456         flags |= WBFLAG_PAM_NTKEY;
1457
1458         SMBencrypt(opt_password,chall.data,lm_response.data);
1459         E_deshash(opt_password, lm_hash); 
1460
1461         SMBNTencrypt(opt_password,chall.data,nt_response.data);
1462
1463         E_md4hash(opt_password, nt_hash);
1464         SMBsesskeygen_ntv1(nt_hash, NULL, session_key.data);
1465
1466         switch (break_which) {
1467         case BREAK_NONE:
1468                 break;
1469         case BREAK_LM:
1470                 lm_response.data[0]++;
1471                 break;
1472         case BREAK_NT:
1473                 nt_response.data[0]++;
1474                 break;
1475         case NO_LM:
1476                 data_blob_free(&lm_response);
1477                 break;
1478         case NO_NT:
1479                 data_blob_free(&nt_response);
1480                 break;
1481         }
1482
1483         nt_status = contact_winbind_auth_crap(opt_username, opt_domain, 
1484                                               opt_workstation,
1485                                               &chall,
1486                                               &lm_response,
1487                                               &nt_response,
1488                                               flags,
1489                                               lm_key, 
1490                                               nt_key,
1491                                               &error_string, NULL);
1492         
1493         data_blob_free(&lm_response);
1494
1495         if (!NT_STATUS_IS_OK(nt_status)) {
1496                 d_printf("%s (0x%x)\n", 
1497                          error_string,
1498                          NT_STATUS_V(nt_status));
1499                 SAFE_FREE(error_string);
1500                 return break_which == BREAK_NT;
1501         }
1502
1503         if (memcmp(lm_hash, lm_key, 
1504                    sizeof(lm_key)) != 0) {
1505                 DEBUG(1, ("LM Key does not match expectations!\n"));
1506                 DEBUG(1, ("lm_key:\n"));
1507                 dump_data(1, (const char *)lm_key, 8);
1508                 DEBUG(1, ("expected:\n"));
1509                 dump_data(1, (const char *)lm_hash, 8);
1510                 pass = False;
1511         }
1512
1513         if (break_which == NO_NT) {
1514                 if (memcmp(lm_hash, nt_key, 
1515                            8) != 0) {
1516                         DEBUG(1, ("NT Session Key does not match expectations (should be LM hash)!\n"));
1517                         DEBUG(1, ("nt_key:\n"));
1518                         dump_data(1, (const char *)nt_key, sizeof(nt_key));
1519                         DEBUG(1, ("expected:\n"));
1520                         dump_data(1, (const char *)lm_hash, sizeof(lm_hash));
1521                         pass = False;
1522                 }
1523         } else {                
1524                 if (memcmp(session_key.data, nt_key, 
1525                            sizeof(nt_key)) != 0) {
1526                         DEBUG(1, ("NT Session Key does not match expectations!\n"));
1527                         DEBUG(1, ("nt_key:\n"));
1528                         dump_data(1, (const char *)nt_key, 16);
1529                         DEBUG(1, ("expected:\n"));
1530                         dump_data(1, (const char *)session_key.data, session_key.length);
1531                         pass = False;
1532                 }
1533         }
1534         return pass;
1535 }
1536
1537 /* 
1538  * Test LM authentication, no NT response supplied
1539  */
1540
1541 static BOOL test_lm(void) 
1542 {
1543
1544         return test_lm_ntlm_broken(NO_NT);
1545 }
1546
1547 /* 
1548  * Test the NTLM response only, no LM.
1549  */
1550
1551 static BOOL test_ntlm(void) 
1552 {
1553         return test_lm_ntlm_broken(NO_LM);
1554 }
1555
1556 /* 
1557  * Test the NTLM response only, but in the LM field.
1558  */
1559
1560 static BOOL test_ntlm_in_lm(void) 
1561 {
1562         BOOL pass = True;
1563         NTSTATUS nt_status;
1564         uint32 flags = 0;
1565         DATA_BLOB nt_response = data_blob(NULL, 24);
1566
1567         uchar lm_key[8];
1568         uchar lm_hash[16];
1569         uchar nt_key[16];
1570         DATA_BLOB chall = get_challenge();
1571         char *error_string;
1572         
1573         ZERO_STRUCT(nt_key);
1574
1575         flags |= WBFLAG_PAM_LMKEY;
1576         flags |= WBFLAG_PAM_NTKEY;
1577
1578         SMBNTencrypt(opt_password,chall.data,nt_response.data);
1579
1580         E_deshash(opt_password, lm_hash); 
1581
1582         nt_status = contact_winbind_auth_crap(opt_username, opt_domain, 
1583                                               opt_workstation,
1584                                               &chall,
1585                                               &nt_response,
1586                                               NULL,
1587                                               flags,
1588                                               lm_key,
1589                                               nt_key,
1590                                               &error_string, NULL);
1591         
1592         data_blob_free(&nt_response);
1593
1594         if (!NT_STATUS_IS_OK(nt_status)) {
1595                 d_printf("%s (0x%x)\n", 
1596                          error_string,
1597                          NT_STATUS_V(nt_status));
1598                 SAFE_FREE(error_string);
1599                 return False;
1600         }
1601
1602         if (memcmp(lm_hash, lm_key, 
1603                    sizeof(lm_key)) != 0) {
1604                 DEBUG(1, ("LM Key does not match expectations!\n"));
1605                 DEBUG(1, ("lm_key:\n"));
1606                 dump_data(1, (const char *)lm_key, 8);
1607                 DEBUG(1, ("expected:\n"));
1608                 dump_data(1, (const char *)lm_hash, 8);
1609                 pass = False;
1610         }
1611         if (memcmp(lm_hash, nt_key, 8) != 0) {
1612                 DEBUG(1, ("Session Key (first 8 lm hash) does not match expectations!\n"));
1613                 DEBUG(1, ("nt_key:\n"));
1614                 dump_data(1, (const char *)nt_key, 16);
1615                 DEBUG(1, ("expected:\n"));
1616                 dump_data(1, (const char *)lm_hash, 8);
1617                 pass = False;
1618         }
1619         return pass;
1620 }
1621
1622 /* 
1623  * Test the NTLM response only, but in the both the NT and LM fields.
1624  */
1625
1626 static BOOL test_ntlm_in_both(void) 
1627 {
1628         BOOL pass = True;
1629         NTSTATUS nt_status;
1630         uint32 flags = 0;
1631         DATA_BLOB nt_response = data_blob(NULL, 24);
1632         DATA_BLOB session_key = data_blob(NULL, 16);
1633
1634         char lm_key[8];
1635         char lm_hash[16];
1636         char nt_key[16];
1637         char nt_hash[16];
1638         DATA_BLOB chall = get_challenge();
1639         char *error_string;
1640         
1641         ZERO_STRUCT(lm_key);
1642         ZERO_STRUCT(nt_key);
1643
1644         flags |= WBFLAG_PAM_LMKEY;
1645         flags |= WBFLAG_PAM_NTKEY;
1646
1647         SMBNTencrypt(opt_password,chall.data,nt_response.data);
1648         E_md4hash(opt_password, (unsigned char *)nt_hash);
1649         SMBsesskeygen_ntv1((const unsigned char *)nt_hash, NULL, session_key.data);
1650
1651         E_deshash(opt_password, (unsigned char *)lm_hash); 
1652
1653         nt_status = contact_winbind_auth_crap(opt_username, opt_domain, 
1654                                               opt_workstation,
1655                                               &chall,
1656                                               &nt_response,
1657                                               &nt_response,
1658                                               flags,
1659                                               (unsigned char *)lm_key,
1660                                               (unsigned char *)nt_key,
1661                                               &error_string, NULL);
1662         
1663         data_blob_free(&nt_response);
1664
1665         if (!NT_STATUS_IS_OK(nt_status)) {
1666                 d_printf("%s (0x%x)\n", 
1667                          error_string,
1668                          NT_STATUS_V(nt_status));
1669                 SAFE_FREE(error_string);
1670                 return False;
1671         }
1672
1673         if (memcmp(lm_hash, lm_key, 
1674                    sizeof(lm_key)) != 0) {
1675                 DEBUG(1, ("LM Key does not match expectations!\n"));
1676                 DEBUG(1, ("lm_key:\n"));
1677                 dump_data(1, lm_key, 8);
1678                 DEBUG(1, ("expected:\n"));
1679                 dump_data(1, lm_hash, 8);
1680                 pass = False;
1681         }
1682         if (memcmp(session_key.data, nt_key, 
1683                    sizeof(nt_key)) != 0) {
1684                 DEBUG(1, ("NT Session Key does not match expectations!\n"));
1685                 DEBUG(1, ("nt_key:\n"));
1686                 dump_data(1, nt_key, 16);
1687                 DEBUG(1, ("expected:\n"));
1688                 dump_data(1, (const char *)session_key.data, session_key.length);
1689                 pass = False;
1690         }
1691
1692
1693         return pass;
1694 }
1695
1696 /* 
1697  * Test the NTLMv2 and LMv2 responses
1698  */
1699
1700 static BOOL test_lmv2_ntlmv2_broken(enum ntlm_break break_which) 
1701 {
1702         BOOL pass = True;
1703         NTSTATUS nt_status;
1704         uint32 flags = 0;
1705         DATA_BLOB ntlmv2_response = data_blob(NULL, 0);
1706         DATA_BLOB lmv2_response = data_blob(NULL, 0);
1707         DATA_BLOB nt_session_key = data_blob(NULL, 0);
1708         DATA_BLOB names_blob = NTLMv2_generate_names_blob(get_winbind_netbios_name(), get_winbind_domain());
1709
1710         uchar nt_key[16];
1711         DATA_BLOB chall = get_challenge();
1712         char *error_string;
1713
1714         ZERO_STRUCT(nt_key);
1715         
1716         flags |= WBFLAG_PAM_NTKEY;
1717
1718         if (!SMBNTLMv2encrypt(opt_username, opt_domain, opt_password, &chall,
1719                               &names_blob,
1720                               &lmv2_response, &ntlmv2_response, 
1721                               &nt_session_key)) {
1722                 data_blob_free(&names_blob);
1723                 return False;
1724         }
1725         data_blob_free(&names_blob);
1726
1727         switch (break_which) {
1728         case BREAK_NONE:
1729                 break;
1730         case BREAK_LM:
1731                 lmv2_response.data[0]++;
1732                 break;
1733         case BREAK_NT:
1734                 ntlmv2_response.data[0]++;
1735                 break;
1736         case NO_LM:
1737                 data_blob_free(&lmv2_response);
1738                 break;
1739         case NO_NT:
1740                 data_blob_free(&ntlmv2_response);
1741                 break;
1742         }
1743
1744         nt_status = contact_winbind_auth_crap(opt_username, opt_domain, 
1745                                               opt_workstation,
1746                                               &chall,
1747                                               &lmv2_response,
1748                                               &ntlmv2_response,
1749                                               flags,
1750                                               NULL, 
1751                                               nt_key,
1752                                               &error_string, NULL);
1753         
1754         data_blob_free(&lmv2_response);
1755         data_blob_free(&ntlmv2_response);
1756
1757         if (!NT_STATUS_IS_OK(nt_status)) {
1758                 d_printf("%s (0x%x)\n", 
1759                          error_string,
1760                          NT_STATUS_V(nt_status));
1761                 SAFE_FREE(error_string);
1762                 return break_which == BREAK_NT;
1763         }
1764
1765         if (break_which != NO_NT && break_which != BREAK_NT && memcmp(nt_session_key.data, nt_key, 
1766                    sizeof(nt_key)) != 0) {
1767                 DEBUG(1, ("NT Session Key does not match expectations!\n"));
1768                 DEBUG(1, ("nt_key:\n"));
1769                 dump_data(1, (const char *)nt_key, 16);
1770                 DEBUG(1, ("expected:\n"));
1771                 dump_data(1, (const char *)nt_session_key.data, nt_session_key.length);
1772                 pass = False;
1773         }
1774         return pass;
1775 }
1776
1777 /* 
1778  * Test the NTLMv2 and LMv2 responses
1779  */
1780
1781 static BOOL test_lmv2_ntlmv2(void) 
1782 {
1783         return test_lmv2_ntlmv2_broken(BREAK_NONE);
1784 }
1785
1786 /* 
1787  * Test the LMv2 response only
1788  */
1789
1790 static BOOL test_lmv2(void) 
1791 {
1792         return test_lmv2_ntlmv2_broken(NO_NT);
1793 }
1794
1795 /* 
1796  * Test the NTLMv2 response only
1797  */
1798
1799 static BOOL test_ntlmv2(void) 
1800 {
1801         return test_lmv2_ntlmv2_broken(NO_LM);
1802 }
1803
1804 static BOOL test_lm_ntlm(void) 
1805 {
1806         return test_lm_ntlm_broken(BREAK_NONE);
1807 }
1808
1809 static BOOL test_ntlm_lm_broken(void) 
1810 {
1811         return test_lm_ntlm_broken(BREAK_LM);
1812 }
1813
1814 static BOOL test_ntlm_ntlm_broken(void) 
1815 {
1816         return test_lm_ntlm_broken(BREAK_NT);
1817 }
1818
1819 static BOOL test_ntlmv2_lmv2_broken(void) 
1820 {
1821         return test_lmv2_ntlmv2_broken(BREAK_LM);
1822 }
1823
1824 static BOOL test_ntlmv2_ntlmv2_broken(void) 
1825 {
1826         return test_lmv2_ntlmv2_broken(BREAK_NT);
1827 }
1828
1829 static BOOL test_plaintext(enum ntlm_break break_which)
1830 {
1831         NTSTATUS nt_status;
1832         uint32 flags = 0;
1833         DATA_BLOB nt_response = data_blob(NULL, 0);
1834         DATA_BLOB lm_response = data_blob(NULL, 0);
1835         char *password;
1836
1837         uchar nt_key[16];
1838         uchar lm_key[16];
1839         static const uchar zeros[8];
1840         DATA_BLOB chall = data_blob(zeros, sizeof(zeros));
1841         char *error_string;
1842
1843         ZERO_STRUCT(nt_key);
1844         
1845         flags |= WBFLAG_PAM_NTKEY;
1846         flags |= WBFLAG_PAM_LMKEY;
1847
1848         if ((push_ucs2_allocate((smb_ucs2_t **)&nt_response.data, opt_password)) == -1) {
1849                 DEBUG(0, ("push_ucs2_allocate failed!\n"));
1850                 exit(1);
1851         }
1852
1853         nt_response.length = strlen_w(((void *)nt_response.data))*sizeof(smb_ucs2_t);
1854
1855         password = strdup_upper(opt_password);
1856
1857         if ((convert_string_allocate(NULL, CH_UNIX, 
1858                                      CH_DOS, password,
1859                                      strlen(password)+1, 
1860                                      (void**)&lm_response.data)) == -1) {
1861                 DEBUG(0, ("push_ascii_allocate failed!\n"));
1862                 exit(1);
1863         }
1864
1865         SAFE_FREE(password);
1866
1867         lm_response.length = strlen(lm_response.data);
1868
1869         switch (break_which) {
1870         case BREAK_NONE:
1871                 break;
1872         case BREAK_LM:
1873                 lm_response.data[0]++;
1874                 break;
1875         case BREAK_NT:
1876                 nt_response.data[0]++;
1877                 break;
1878         case NO_LM:
1879                 SAFE_FREE(lm_response.data);
1880                 lm_response.length = 0;
1881                 break;
1882         case NO_NT:
1883                 SAFE_FREE(nt_response.data);
1884                 nt_response.length = 0;
1885                 break;
1886         }
1887
1888         nt_status = contact_winbind_auth_crap(opt_username, opt_domain, 
1889                                               opt_workstation,
1890                                               &chall,
1891                                               &lm_response,
1892                                               &nt_response,
1893                                               flags,
1894                                               lm_key,
1895                                               nt_key,
1896                                               &error_string, NULL);
1897         
1898         SAFE_FREE(nt_response.data);
1899         SAFE_FREE(lm_response.data);
1900         data_blob_free(&chall);
1901
1902         if (!NT_STATUS_IS_OK(nt_status)) {
1903                 d_printf("%s (0x%x)\n", 
1904                          error_string,
1905                          NT_STATUS_V(nt_status));
1906                 SAFE_FREE(error_string);
1907                 return break_which == BREAK_NT;
1908         }
1909
1910         return break_which != BREAK_NT;
1911 }
1912
1913 static BOOL test_plaintext_none_broken(void) {
1914         return test_plaintext(BREAK_NONE);
1915 }
1916
1917 static BOOL test_plaintext_lm_broken(void) {
1918         return test_plaintext(BREAK_LM);
1919 }
1920
1921 static BOOL test_plaintext_nt_broken(void) {
1922         return test_plaintext(BREAK_NT);
1923 }
1924
1925 static BOOL test_plaintext_nt_only(void) {
1926         return test_plaintext(NO_LM);
1927 }
1928
1929 static BOOL test_plaintext_lm_only(void) {
1930         return test_plaintext(NO_NT);
1931 }
1932
1933 /* 
1934    Tests:
1935    
1936    - LM only
1937    - NT and LM             
1938    - NT
1939    - NT in LM field
1940    - NT in both fields
1941    - NTLMv2
1942    - NTLMv2 and LMv2
1943    - LMv2
1944    - plaintext tests (in challenge-response feilds)
1945   
1946    check we get the correct session key in each case
1947    check what values we get for the LM session key
1948    
1949 */
1950
1951 struct ntlm_tests {
1952         BOOL (*fn)(void);
1953         const char *name;
1954 } test_table[] = {
1955         {test_lm, "LM"},
1956         {test_lm_ntlm, "LM and NTLM"},
1957         {test_ntlm, "NTLM"},
1958         {test_ntlm_in_lm, "NTLM in LM"},
1959         {test_ntlm_in_both, "NTLM in both"},
1960         {test_ntlmv2, "NTLMv2"},
1961         {test_lmv2_ntlmv2, "NTLMv2 and LMv2"},
1962         {test_lmv2, "LMv2"},
1963         {test_ntlmv2_lmv2_broken, "NTLMv2 and LMv2, LMv2 broken"},
1964         {test_ntlmv2_ntlmv2_broken, "NTLMv2 and LMv2, NTLMv2 broken"},
1965         {test_ntlm_lm_broken, "NTLM and LM, LM broken"},
1966         {test_ntlm_ntlm_broken, "NTLM and LM, NTLM broken"},
1967         {test_plaintext_none_broken, "Plaintext"},
1968         {test_plaintext_lm_broken, "Plaintext LM broken"},
1969         {test_plaintext_nt_broken, "Plaintext NT broken"},
1970         {test_plaintext_nt_only, "Plaintext NT only"},
1971         {test_plaintext_lm_only, "Plaintext LM only"}
1972 };
1973
1974 static BOOL diagnose_ntlm_auth(void)
1975 {
1976         unsigned int i;
1977         BOOL pass = True;
1978
1979         for (i=0; test_table[i].fn; i++) {
1980                 if (!test_table[i].fn()) {
1981                         DEBUG(1, ("Test %s failed!\n", test_table[i].name));
1982                         pass = False;
1983                 }
1984         }
1985
1986         return pass;
1987 }
1988
1989 /* Main program */
1990
1991 enum {
1992         OPT_USERNAME = 1000,
1993         OPT_DOMAIN,
1994         OPT_WORKSTATION,
1995         OPT_CHALLENGE,
1996         OPT_RESPONSE,
1997         OPT_LM,
1998         OPT_NT,
1999         OPT_PASSWORD,
2000         OPT_LM_KEY,
2001         OPT_NT_KEY,
2002         OPT_DIAGNOSTICS
2003 };
2004
2005  int main(int argc, const char **argv)
2006 {
2007         int opt;
2008         static const char *helper_protocol;
2009         static int diagnostics;
2010
2011         static const char *hex_challenge;
2012         static const char *hex_lm_response;
2013         static const char *hex_nt_response;
2014         char *challenge;
2015         char *lm_response;
2016         char *nt_response;
2017         size_t challenge_len;
2018         size_t lm_response_len;
2019         size_t nt_response_len;
2020
2021         poptContext pc;
2022
2023         /* NOTE: DO NOT change this interface without considering the implications!
2024            This is an external interface, which other programs will use to interact 
2025            with this helper.
2026         */
2027
2028         /* We do not use single-letter command abbreviations, because they harm future 
2029            interface stability. */
2030
2031         struct poptOption long_options[] = {
2032                 POPT_AUTOHELP
2033                 { "helper-protocol", 0, POPT_ARG_STRING, &helper_protocol, OPT_DOMAIN, "operate as a stdio-based helper", "helper protocol to use"},
2034                 { "username", 0, POPT_ARG_STRING, &opt_username, OPT_USERNAME, "username"},
2035                 { "domain", 0, POPT_ARG_STRING, &opt_domain, OPT_DOMAIN, "domain name"},
2036                 { "workstation", 0, POPT_ARG_STRING, &opt_workstation, OPT_WORKSTATION, "workstation"},
2037                 { "challenge", 0, POPT_ARG_STRING, &hex_challenge, OPT_CHALLENGE, "challenge (HEX encoded)"},
2038                 { "lm-response", 0, POPT_ARG_STRING, &hex_lm_response, OPT_LM, "LM Response to the challenge (HEX encoded)"},
2039                 { "nt-response", 0, POPT_ARG_STRING, &hex_nt_response, OPT_NT, "NT or NTLMv2 Response to the challenge (HEX encoded)"},
2040                 { "password", 0, POPT_ARG_STRING, &opt_password, OPT_PASSWORD, "User's plaintext password"},            
2041                 { "request-lm-key", 0, POPT_ARG_NONE, &request_lm_key, OPT_LM_KEY, "Retreive LM session key"},
2042                 { "request-nt-key", 0, POPT_ARG_NONE, &request_nt_key, OPT_NT_KEY, "Retreive NT session key"},
2043                 { "diagnostics", 0, POPT_ARG_NONE, &diagnostics, OPT_DIAGNOSTICS, "Perform diagnostics on the authentictaion chain"},
2044                 POPT_COMMON_SAMBA
2045                 POPT_TABLEEND
2046         };
2047
2048         /* Samba client initialisation */
2049
2050         dbf = x_stderr;
2051         
2052         /* Samba client initialisation */
2053
2054         if (!lp_load(dyn_CONFIGFILE, True, False, False)) {
2055                 d_fprintf(stderr, "wbinfo: error opening config file %s. Error was %s\n",
2056                         dyn_CONFIGFILE, strerror(errno));
2057                 exit(1);
2058         }
2059
2060         /* Parse options */
2061
2062         pc = poptGetContext("ntlm_auth", argc, argv, long_options, 0);
2063
2064         /* Parse command line options */
2065
2066         if (argc == 1) {
2067                 poptPrintHelp(pc, stderr, 0);
2068                 return 1;
2069         }
2070
2071         pc = poptGetContext(NULL, argc, (const char **)argv, long_options, 
2072                             POPT_CONTEXT_KEEP_FIRST);
2073
2074         while((opt = poptGetNextOpt(pc)) != -1) {
2075                 switch (opt) {
2076                 case OPT_CHALLENGE:
2077                         challenge = smb_xmalloc((strlen(hex_challenge))/2+1);
2078                         if ((challenge_len = strhex_to_str(challenge, 
2079                                                            strlen(hex_challenge), 
2080                                                            hex_challenge)) != 8) {
2081                                 x_fprintf(x_stderr, "hex decode of %s failed (only got %lu bytes)!\n", 
2082                                         hex_challenge, (unsigned long)challenge_len);
2083                                 exit(1);
2084                         }
2085                         opt_challenge = data_blob(challenge, challenge_len);
2086                         SAFE_FREE(challenge);
2087                         break;
2088                 case OPT_LM: 
2089                         lm_response = smb_xmalloc((strlen(hex_lm_response))/2+1);
2090                         lm_response_len = strhex_to_str(lm_response,    
2091                                                         strlen(hex_lm_response), 
2092                                                         hex_lm_response);
2093                         if (lm_response_len != 24) {
2094                                 x_fprintf(x_stderr, "hex decode of %s failed!\n", hex_lm_response);
2095                                 exit(1);
2096                         }
2097                         opt_lm_response = data_blob(lm_response, lm_response_len);
2098                         SAFE_FREE(lm_response);
2099                         break;
2100                 case OPT_NT: 
2101                         nt_response = smb_xmalloc((strlen(hex_nt_response)+2)/2+1);
2102                         nt_response_len = strhex_to_str(nt_response, 
2103                                                         strlen(hex_nt_response), 
2104                                                         hex_nt_response);
2105                         if (nt_response_len < 24) {
2106                                 x_fprintf(x_stderr, "hex decode of %s failed!\n", hex_nt_response);
2107                                 exit(1);
2108                         }
2109                         opt_nt_response = data_blob(nt_response, nt_response_len);
2110                         SAFE_FREE(nt_response);
2111                         break;
2112                 }
2113         }
2114
2115         if (helper_protocol) {
2116                 int i;
2117                 for (i=0; i<NUM_HELPER_MODES; i++) {
2118                         if (strcmp(helper_protocol, stdio_helper_protocols[i].name) == 0) {
2119                                 squid_stream(stdio_helper_protocols[i].mode, stdio_helper_protocols[i].fn);
2120                                 exit(0);
2121                         }
2122                 }
2123                 x_fprintf(x_stderr, "unknown helper protocol [%s]\n\nValid helper protools:\n\n", helper_protocol);
2124
2125                 for (i=0; i<NUM_HELPER_MODES; i++) {
2126                         x_fprintf(x_stderr, "%s\n", stdio_helper_protocols[i].name);
2127                 }
2128
2129                 exit(1);
2130         }
2131
2132         if (!opt_username) {
2133                 x_fprintf(x_stderr, "username must be specified!\n\n");
2134                 poptPrintHelp(pc, stderr, 0);
2135                 exit(1);
2136         }
2137
2138         if (opt_domain == NULL) {
2139                 opt_domain = get_winbind_domain();
2140         }
2141
2142         if (opt_workstation == NULL) {
2143                 opt_workstation = "";
2144         }
2145
2146         if (opt_challenge.length) {
2147                 if (!check_auth_crap()) {
2148                         exit(1);
2149                 }
2150                 exit(0);
2151         } 
2152
2153         if (!opt_password) {
2154                 opt_password = getpass("password: ");
2155         }
2156
2157         if (diagnostics) {
2158                 if (!diagnose_ntlm_auth()) {
2159                         exit(1);
2160                 }
2161         } else {
2162                 fstring user;
2163
2164                 fstr_sprintf(user, "%s%c%s", opt_domain, winbind_separator(), opt_username);
2165                 if (!check_plaintext_auth(user, opt_password, True)) {
2166                         exit(1);
2167                 }
2168         }
2169
2170         /* Exit code */
2171
2172         poptFreeContext(pc);
2173         return 0;
2174 }