7fae0ede978874c213d0e53b335b737d71b1e992
[abartlet/samba.git/.git] / source3 / utils / ntlm_auth_diagnostics.c
1 /* 
2    Unix SMB/CIFS implementation.
3
4    Winbind status program.
5
6    Copyright (C) Tim Potter      2000-2003
7    Copyright (C) Andrew Bartlett <abartlet@samba.org> 2003-2004
8    Copyright (C) Francesco Chemolli <kinkie@kame.usr.dsi.unimi.it> 2000 
9
10    This program is free software; you can redistribute it and/or modify
11    it under the terms of the GNU General Public License as published by
12    the Free Software Foundation; either version 2 of the License, or
13    (at your option) any later version.
14    
15    This program is distributed in the hope that it will be useful,
16    but WITHOUT ANY WARRANTY; without even the implied warranty of
17    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18    GNU General Public License for more details.
19    
20    You should have received a copy of the GNU General Public License
21    along with this program; if not, write to the Free Software
22    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
23 */
24
25 #include "includes.h"
26 #include "utils/ntlm_auth.h"
27
28 #undef DBGC_CLASS
29 #define DBGC_CLASS DBGC_WINBIND
30
31 enum ntlm_break {
32         BREAK_NONE,
33         BREAK_LM,
34         BREAK_NT,
35         NO_LM,
36         NO_NT
37 };
38
39 /* 
40    Authenticate a user with a challenge/response, checking session key
41    and valid authentication types
42 */
43
44 /* 
45  * Test the normal 'LM and NTLM' combination
46  */
47
48 static BOOL test_lm_ntlm_broken(enum ntlm_break break_which) 
49 {
50         BOOL pass = True;
51         NTSTATUS nt_status;
52         uint32 flags = 0;
53         DATA_BLOB lm_response = data_blob(NULL, 24);
54         DATA_BLOB nt_response = data_blob(NULL, 24);
55         DATA_BLOB session_key = data_blob(NULL, 16);
56
57         uchar lm_key[8];
58         uchar user_session_key[16];
59         uchar lm_hash[16];
60         uchar nt_hash[16];
61         DATA_BLOB chall = get_challenge();
62         char *error_string;
63         
64         ZERO_STRUCT(lm_key);
65         ZERO_STRUCT(user_session_key);
66
67         flags |= WBFLAG_PAM_LMKEY;
68         flags |= WBFLAG_PAM_USER_SESSION_KEY;
69
70         SMBencrypt(opt_password,chall.data,lm_response.data);
71         E_deshash(opt_password, lm_hash); 
72
73         SMBNTencrypt(opt_password,chall.data,nt_response.data);
74
75         E_md4hash(opt_password, nt_hash);
76         SMBsesskeygen_ntv1(nt_hash, NULL, session_key.data);
77
78         switch (break_which) {
79         case BREAK_NONE:
80                 break;
81         case BREAK_LM:
82                 lm_response.data[0]++;
83                 break;
84         case BREAK_NT:
85                 nt_response.data[0]++;
86                 break;
87         case NO_LM:
88                 data_blob_free(&lm_response);
89                 break;
90         case NO_NT:
91                 data_blob_free(&nt_response);
92                 break;
93         }
94
95         nt_status = contact_winbind_auth_crap(opt_username, opt_domain, 
96                                               opt_workstation,
97                                               &chall,
98                                               &lm_response,
99                                               &nt_response,
100                                               flags,
101                                               lm_key, 
102                                               user_session_key,
103                                               &error_string, NULL);
104         
105         data_blob_free(&lm_response);
106
107         if (!NT_STATUS_IS_OK(nt_status)) {
108                 d_printf("%s (0x%x)\n", 
109                          error_string,
110                          NT_STATUS_V(nt_status));
111                 SAFE_FREE(error_string);
112                 return break_which == BREAK_NT;
113         }
114
115         if (memcmp(lm_hash, lm_key, 
116                    sizeof(lm_key)) != 0) {
117                 DEBUG(1, ("LM Key does not match expectations!\n"));
118                 DEBUG(1, ("lm_key:\n"));
119                 dump_data(1, (const char *)lm_key, 8);
120                 DEBUG(1, ("expected:\n"));
121                 dump_data(1, (const char *)lm_hash, 8);
122                 pass = False;
123         }
124
125         if (break_which == NO_NT) {
126                 if (memcmp(lm_hash, user_session_key, 
127                            8) != 0) {
128                         DEBUG(1, ("NT Session Key does not match expectations (should be LM hash)!\n"));
129                         DEBUG(1, ("user_session_key:\n"));
130                         dump_data(1, (const char *)user_session_key, sizeof(user_session_key));
131                         DEBUG(1, ("expected:\n"));
132                         dump_data(1, (const char *)lm_hash, sizeof(lm_hash));
133                         pass = False;
134                 }
135         } else {                
136                 if (memcmp(session_key.data, user_session_key, 
137                            sizeof(user_session_key)) != 0) {
138                         DEBUG(1, ("NT Session Key does not match expectations!\n"));
139                         DEBUG(1, ("user_session_key:\n"));
140                         dump_data(1, (const char *)user_session_key, 16);
141                         DEBUG(1, ("expected:\n"));
142                         dump_data(1, (const char *)session_key.data, session_key.length);
143                         pass = False;
144                 }
145         }
146         return pass;
147 }
148
149 /* 
150  * Test LM authentication, no NT response supplied
151  */
152
153 static BOOL test_lm(void) 
154 {
155
156         return test_lm_ntlm_broken(NO_NT);
157 }
158
159 /* 
160  * Test the NTLM response only, no LM.
161  */
162
163 static BOOL test_ntlm(void) 
164 {
165         return test_lm_ntlm_broken(NO_LM);
166 }
167
168 /* 
169  * Test the NTLM response only, but in the LM field.
170  */
171
172 static BOOL test_ntlm_in_lm(void) 
173 {
174         BOOL pass = True;
175         NTSTATUS nt_status;
176         uint32 flags = 0;
177         DATA_BLOB nt_response = data_blob(NULL, 24);
178
179         uchar lm_key[8];
180         uchar lm_hash[16];
181         uchar user_session_key[16];
182         DATA_BLOB chall = get_challenge();
183         char *error_string;
184         
185         ZERO_STRUCT(user_session_key);
186
187         flags |= WBFLAG_PAM_LMKEY;
188         flags |= WBFLAG_PAM_USER_SESSION_KEY;
189
190         SMBNTencrypt(opt_password,chall.data,nt_response.data);
191
192         E_deshash(opt_password, lm_hash); 
193
194         nt_status = contact_winbind_auth_crap(opt_username, opt_domain, 
195                                               opt_workstation,
196                                               &chall,
197                                               &nt_response,
198                                               NULL,
199                                               flags,
200                                               lm_key,
201                                               user_session_key,
202                                               &error_string, NULL);
203         
204         data_blob_free(&nt_response);
205
206         if (!NT_STATUS_IS_OK(nt_status)) {
207                 d_printf("%s (0x%x)\n", 
208                          error_string,
209                          NT_STATUS_V(nt_status));
210                 SAFE_FREE(error_string);
211                 return False;
212         }
213
214         if (memcmp(lm_hash, lm_key, 
215                    sizeof(lm_key)) != 0) {
216                 DEBUG(1, ("LM Key does not match expectations!\n"));
217                 DEBUG(1, ("lm_key:\n"));
218                 dump_data(1, (const char *)lm_key, 8);
219                 DEBUG(1, ("expected:\n"));
220                 dump_data(1, (const char *)lm_hash, 8);
221                 pass = False;
222         }
223         if (memcmp(lm_hash, user_session_key, 8) != 0) {
224                 DEBUG(1, ("Session Key (first 8 lm hash) does not match expectations!\n"));
225                 DEBUG(1, ("user_session_key:\n"));
226                 dump_data(1, (const char *)user_session_key, 16);
227                 DEBUG(1, ("expected:\n"));
228                 dump_data(1, (const char *)lm_hash, 8);
229                 pass = False;
230         }
231         return pass;
232 }
233
234 /* 
235  * Test the NTLM response only, but in the both the NT and LM fields.
236  */
237
238 static BOOL test_ntlm_in_both(void) 
239 {
240         BOOL pass = True;
241         NTSTATUS nt_status;
242         uint32 flags = 0;
243         DATA_BLOB nt_response = data_blob(NULL, 24);
244         DATA_BLOB session_key = data_blob(NULL, 16);
245
246         char lm_key[8];
247         char lm_hash[16];
248         char user_session_key[16];
249         char nt_hash[16];
250         DATA_BLOB chall = get_challenge();
251         char *error_string;
252         
253         ZERO_STRUCT(lm_key);
254         ZERO_STRUCT(user_session_key);
255
256         flags |= WBFLAG_PAM_LMKEY;
257         flags |= WBFLAG_PAM_USER_SESSION_KEY;
258
259         SMBNTencrypt(opt_password,chall.data,nt_response.data);
260         E_md4hash(opt_password, (unsigned char *)nt_hash);
261         SMBsesskeygen_ntv1((const unsigned char *)nt_hash, NULL, session_key.data);
262
263         E_deshash(opt_password, (unsigned char *)lm_hash); 
264
265         nt_status = contact_winbind_auth_crap(opt_username, opt_domain, 
266                                               opt_workstation,
267                                               &chall,
268                                               &nt_response,
269                                               &nt_response,
270                                               flags,
271                                               (unsigned char *)lm_key,
272                                               (unsigned char *)user_session_key,
273                                               &error_string, NULL);
274         
275         data_blob_free(&nt_response);
276
277         if (!NT_STATUS_IS_OK(nt_status)) {
278                 d_printf("%s (0x%x)\n", 
279                          error_string,
280                          NT_STATUS_V(nt_status));
281                 SAFE_FREE(error_string);
282                 return False;
283         }
284
285         if (memcmp(lm_hash, lm_key, 
286                    sizeof(lm_key)) != 0) {
287                 DEBUG(1, ("LM Key does not match expectations!\n"));
288                 DEBUG(1, ("lm_key:\n"));
289                 dump_data(1, lm_key, 8);
290                 DEBUG(1, ("expected:\n"));
291                 dump_data(1, lm_hash, 8);
292                 pass = False;
293         }
294         if (memcmp(session_key.data, user_session_key, 
295                    sizeof(user_session_key)) != 0) {
296                 DEBUG(1, ("NT Session Key does not match expectations!\n"));
297                 DEBUG(1, ("user_session_key:\n"));
298                 dump_data(1, user_session_key, 16);
299                 DEBUG(1, ("expected:\n"));
300                 dump_data(1, (const char *)session_key.data, session_key.length);
301                 pass = False;
302         }
303
304
305         return pass;
306 }
307
308 /* 
309  * Test the NTLMv2 and LMv2 responses
310  */
311
312 static BOOL test_lmv2_ntlmv2_broken(enum ntlm_break break_which) 
313 {
314         BOOL pass = True;
315         NTSTATUS nt_status;
316         uint32 flags = 0;
317         DATA_BLOB ntlmv2_response = data_blob(NULL, 0);
318         DATA_BLOB lmv2_response = data_blob(NULL, 0);
319         DATA_BLOB ntlmv2_session_key = data_blob(NULL, 0);
320         DATA_BLOB names_blob = NTLMv2_generate_names_blob(get_winbind_netbios_name(), get_winbind_domain());
321
322         uchar user_session_key[16];
323         DATA_BLOB chall = get_challenge();
324         char *error_string;
325
326         ZERO_STRUCT(user_session_key);
327         
328         flags |= WBFLAG_PAM_USER_SESSION_KEY;
329
330         if (!SMBNTLMv2encrypt(opt_username, opt_domain, opt_password, &chall,
331                               &names_blob,
332                               &lmv2_response, &ntlmv2_response, 
333                               &ntlmv2_session_key)) {
334                 data_blob_free(&names_blob);
335                 return False;
336         }
337         data_blob_free(&names_blob);
338
339         switch (break_which) {
340         case BREAK_NONE:
341                 break;
342         case BREAK_LM:
343                 lmv2_response.data[0]++;
344                 break;
345         case BREAK_NT:
346                 ntlmv2_response.data[0]++;
347                 break;
348         case NO_LM:
349                 data_blob_free(&lmv2_response);
350                 break;
351         case NO_NT:
352                 data_blob_free(&ntlmv2_response);
353                 break;
354         }
355
356         nt_status = contact_winbind_auth_crap(opt_username, opt_domain, 
357                                               opt_workstation,
358                                               &chall,
359                                               &lmv2_response,
360                                               &ntlmv2_response,
361                                               flags,
362                                               NULL, 
363                                               user_session_key,
364                                               &error_string, NULL);
365         
366         data_blob_free(&lmv2_response);
367         data_blob_free(&ntlmv2_response);
368
369         if (!NT_STATUS_IS_OK(nt_status)) {
370                 d_printf("%s (0x%x)\n", 
371                          error_string,
372                          NT_STATUS_V(nt_status));
373                 SAFE_FREE(error_string);
374                 return break_which == BREAK_NT;
375         }
376
377         if (break_which != NO_NT && break_which != BREAK_NT && memcmp(ntlmv2_session_key.data, user_session_key, 
378                    sizeof(user_session_key)) != 0) {
379                 DEBUG(1, ("USER (NTLMv2) Session Key does not match expectations!\n"));
380                 DEBUG(1, ("user_session_key:\n"));
381                 dump_data(1, (const char *)user_session_key, 16);
382                 DEBUG(1, ("expected:\n"));
383                 dump_data(1, (const char *)ntlmv2_session_key.data, ntlmv2_session_key.length);
384                 pass = False;
385         }
386         return pass;
387 }
388
389 /* 
390  * Test the NTLMv2 and LMv2 responses
391  */
392
393 static BOOL test_lmv2_ntlmv2(void) 
394 {
395         return test_lmv2_ntlmv2_broken(BREAK_NONE);
396 }
397
398 /* 
399  * Test the LMv2 response only
400  */
401
402 static BOOL test_lmv2(void) 
403 {
404         return test_lmv2_ntlmv2_broken(NO_NT);
405 }
406
407 /* 
408  * Test the NTLMv2 response only
409  */
410
411 static BOOL test_ntlmv2(void) 
412 {
413         return test_lmv2_ntlmv2_broken(NO_LM);
414 }
415
416 static BOOL test_lm_ntlm(void) 
417 {
418         return test_lm_ntlm_broken(BREAK_NONE);
419 }
420
421 static BOOL test_ntlm_lm_broken(void) 
422 {
423         return test_lm_ntlm_broken(BREAK_LM);
424 }
425
426 static BOOL test_ntlm_ntlm_broken(void) 
427 {
428         return test_lm_ntlm_broken(BREAK_NT);
429 }
430
431 static BOOL test_ntlmv2_lmv2_broken(void) 
432 {
433         return test_lmv2_ntlmv2_broken(BREAK_LM);
434 }
435
436 static BOOL test_ntlmv2_ntlmv2_broken(void) 
437 {
438         return test_lmv2_ntlmv2_broken(BREAK_NT);
439 }
440
441 static BOOL test_plaintext(enum ntlm_break break_which)
442 {
443         NTSTATUS nt_status;
444         uint32 flags = 0;
445         DATA_BLOB nt_response = data_blob(NULL, 0);
446         DATA_BLOB lm_response = data_blob(NULL, 0);
447         char *password;
448
449         uchar user_session_key[16];
450         uchar lm_key[16];
451         static const uchar zeros[8];
452         DATA_BLOB chall = data_blob(zeros, sizeof(zeros));
453         char *error_string;
454
455         ZERO_STRUCT(user_session_key);
456         
457         flags |= WBFLAG_PAM_LMKEY;
458         flags |= WBFLAG_PAM_USER_SESSION_KEY;
459
460         if ((push_ucs2_allocate((smb_ucs2_t **)&nt_response.data, opt_password)) == -1) {
461                 DEBUG(0, ("push_ucs2_allocate failed!\n"));
462                 exit(1);
463         }
464
465         nt_response.length = strlen_w(((void *)nt_response.data))*sizeof(smb_ucs2_t);
466
467         password = strdup_upper(opt_password);
468
469         if ((convert_string_allocate(NULL, CH_UNIX, 
470                                      CH_DOS, password,
471                                      strlen(password)+1, 
472                                      (void**)&lm_response.data,True)) == -1) {
473                 DEBUG(0, ("push_ascii_allocate failed!\n"));
474                 exit(1);
475         }
476
477         SAFE_FREE(password);
478
479         lm_response.length = strlen(lm_response.data);
480
481         switch (break_which) {
482         case BREAK_NONE:
483                 break;
484         case BREAK_LM:
485                 lm_response.data[0]++;
486                 break;
487         case BREAK_NT:
488                 nt_response.data[0]++;
489                 break;
490         case NO_LM:
491                 SAFE_FREE(lm_response.data);
492                 lm_response.length = 0;
493                 break;
494         case NO_NT:
495                 SAFE_FREE(nt_response.data);
496                 nt_response.length = 0;
497                 break;
498         }
499
500         nt_status = contact_winbind_auth_crap(opt_username, opt_domain, 
501                                               opt_workstation,
502                                               &chall,
503                                               &lm_response,
504                                               &nt_response,
505                                               flags,
506                                               lm_key,
507                                               user_session_key,
508                                               &error_string, NULL);
509         
510         SAFE_FREE(nt_response.data);
511         SAFE_FREE(lm_response.data);
512         data_blob_free(&chall);
513
514         if (!NT_STATUS_IS_OK(nt_status)) {
515                 d_printf("%s (0x%x)\n", 
516                          error_string,
517                          NT_STATUS_V(nt_status));
518                 SAFE_FREE(error_string);
519                 return break_which == BREAK_NT;
520         }
521
522         return break_which != BREAK_NT;
523 }
524
525 static BOOL test_plaintext_none_broken(void) {
526         return test_plaintext(BREAK_NONE);
527 }
528
529 static BOOL test_plaintext_lm_broken(void) {
530         return test_plaintext(BREAK_LM);
531 }
532
533 static BOOL test_plaintext_nt_broken(void) {
534         return test_plaintext(BREAK_NT);
535 }
536
537 static BOOL test_plaintext_nt_only(void) {
538         return test_plaintext(NO_LM);
539 }
540
541 static BOOL test_plaintext_lm_only(void) {
542         return test_plaintext(NO_NT);
543 }
544
545 /* 
546    Tests:
547    
548    - LM only
549    - NT and LM             
550    - NT
551    - NT in LM field
552    - NT in both fields
553    - NTLMv2
554    - NTLMv2 and LMv2
555    - LMv2
556    - plaintext tests (in challenge-response feilds)
557   
558    check we get the correct session key in each case
559    check what values we get for the LM session key
560    
561 */
562
563 static const struct ntlm_tests {
564         BOOL (*fn)(void);
565         const char *name;
566 } test_table[] = {
567         {test_lm, "LM"},
568         {test_lm_ntlm, "LM and NTLM"},
569         {test_ntlm, "NTLM"},
570         {test_ntlm_in_lm, "NTLM in LM"},
571         {test_ntlm_in_both, "NTLM in both"},
572         {test_ntlmv2, "NTLMv2"},
573         {test_lmv2_ntlmv2, "NTLMv2 and LMv2"},
574         {test_lmv2, "LMv2"},
575         {test_ntlmv2_lmv2_broken, "NTLMv2 and LMv2, LMv2 broken"},
576         {test_ntlmv2_ntlmv2_broken, "NTLMv2 and LMv2, NTLMv2 broken"},
577         {test_ntlm_lm_broken, "NTLM and LM, LM broken"},
578         {test_ntlm_ntlm_broken, "NTLM and LM, NTLM broken"},
579         {test_plaintext_none_broken, "Plaintext"},
580         {test_plaintext_lm_broken, "Plaintext LM broken"},
581         {test_plaintext_nt_broken, "Plaintext NT broken"},
582         {test_plaintext_nt_only, "Plaintext NT only"},
583         {test_plaintext_lm_only, "Plaintext LM only"},
584         {NULL, NULL}
585 };
586
587 BOOL diagnose_ntlm_auth(void)
588 {
589         unsigned int i;
590         BOOL pass = True;
591
592         for (i=0; test_table[i].fn; i++) {
593                 if (!test_table[i].fn()) {
594                         DEBUG(1, ("Test %s failed!\n", test_table[i].name));
595                         pass = False;
596                 }
597         }
598
599         return pass;
600 }
601