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