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