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