1/*
2 * Copyright (c) 2006 - 2008 Kungliga Tekniska Högskolan
3 * (Royal Institute of Technology, Stockholm, Sweden).
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 *
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice, this list of conditions and the following disclaimer.
12 *
13 * 2. Redistributions in binary form must reproduce the above copyright
14 *    notice, this list of conditions and the following disclaimer in the
15 *    documentation and/or other materials provided with the distribution.
16 *
17 * 3. Neither the name of KTH nor the names of its contributors may be
18 *    used to endorse or promote products derived from this software without
19 *    specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY KTH AND ITS CONTRIBUTORS ``AS IS'' AND ANY
22 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
24 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL KTH OR ITS CONTRIBUTORS BE
25 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
26 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
27 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
28 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
29 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
30 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
31 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32 */
33
34#include "config.h"
35
36#include <stdio.h>
37#include <gssapi.h>
38#include <gssapi_ntlm.h>
39#include <err.h>
40#include <roken.h>
41#include <hex.h>
42#include <getarg.h>
43#include "test_common.h"
44
45static int use_server_domain = 0;
46static int verbose_flag = 0;
47static int broken_session_key_flag = 0;
48
49#ifdef ENABLE_NTLM
50
51#include <krb5.h>
52#include <heimntlm.h>
53
54#define HC_DEPRECATED_CRYPTO
55
56#include "crypto-headers.h"
57
58static void
59dump_packet(const char *name, const void *data, size_t len)
60{
61    char *p;
62
63    printf("%s\n", name);
64    hex_encode(data, len, &p);
65    printf("%s\n", p);
66    free(p);
67}
68
69
70static void
71dump_pac(gss_ctx_id_t ctx)
72{
73    OM_uint32 min_stat;
74    gss_buffer_set_t pac = GSS_C_NO_BUFFER_SET;
75
76    if (gss_inquire_sec_context_by_oid(&min_stat,
77				       ctx,
78				       GSS_C_INQ_WIN2K_PAC_X,
79				       &pac) == GSS_S_COMPLETE &&
80	pac->elements != NULL) {
81
82	dump_packet("Win2K PAC", pac->elements[0].value, pac->elements[0].length);
83	gss_release_buffer_set(&min_stat, &pac);
84    }
85}
86
87static void
88verify_session_key(gss_ctx_id_t ctx,
89		   struct ntlm_buf *sessionkey,
90		   const char *version)
91{
92    OM_uint32 maj_stat, min_stat;
93    gss_buffer_set_t key;
94
95    maj_stat = gss_inquire_sec_context_by_oid(&min_stat,
96					      ctx,
97					      GSS_NTLM_GET_SESSION_KEY_X,
98					      &key);
99    if (maj_stat != GSS_S_COMPLETE || key->count != 1)
100	errx(1, "GSS_NTLM_GET_SESSION_KEY_X: %s", version);
101
102    if (key->elements[0].length == 0) {
103	warnx("no session not negotiated");
104	goto out;
105    }
106
107    if (key->elements[0].length != sessionkey->length)
108	errx(1, "key length wrong: %d version: %s",
109	     (int)key->elements[0].length, version);
110
111    if(memcmp(key->elements[0].value,
112	      sessionkey->data, sessionkey->length) != 0) {
113	dump_packet("AD    session key", key->elements[0].value, key->elements[0].length);
114	dump_packet("local session key", sessionkey->data, sessionkey->length);
115	if (!broken_session_key_flag)
116	    errx(1, "session key wrong: version: %s", version);
117    }
118
119 out:
120    gss_release_buffer_set(&min_stat, &key);
121}
122
123static int
124test_libntlm_v1(const char *test_name, int flags,
125		const char *user, const char *domain, const char *password)
126{
127    OM_uint32 maj_stat, min_stat;
128    gss_ctx_id_t ctx = GSS_C_NO_CONTEXT;
129    gss_buffer_desc input, output;
130    struct ntlm_type1 type1;
131    struct ntlm_type2 type2;
132    struct ntlm_type3 type3;
133    struct ntlm_buf data;
134    krb5_error_code ret;
135    gss_name_t src_name = GSS_C_NO_NAME;
136    struct ntlm_buf sessionkey;
137
138    memset(&type1, 0, sizeof(type1));
139    memset(&type2, 0, sizeof(type2));
140    memset(&type3, 0, sizeof(type3));
141
142    type1.flags =
143	NTLM_NEG_UNICODE|NTLM_NEG_TARGET|
144	NTLM_NEG_NTLM|NTLM_NEG_VERSION|
145	flags;
146    type1.domain = strdup(domain);
147    type1.hostname = NULL;
148    type1.os[0] = 0;
149    type1.os[1] = 0;
150
151    ret = heim_ntlm_encode_type1(&type1, &data);
152    if (ret)
153	errx(1, "heim_ntlm_encode_type1");
154
155    if (verbose_flag)
156	dump_packet("type1", data.data, data.length);
157
158    input.value = data.data;
159    input.length = data.length;
160
161    output.length = 0;
162    output.value = NULL;
163
164    maj_stat = gss_accept_sec_context(&min_stat,
165				      &ctx,
166				      GSS_C_NO_CREDENTIAL,
167				      &input,
168				      GSS_C_NO_CHANNEL_BINDINGS,
169				      NULL,
170				      NULL,
171				      &output,
172				      NULL,
173				      NULL,
174				      NULL);
175    free(data.data);
176    if (GSS_ERROR(maj_stat)) {
177	warnx("accept_sec_context 1 %s: %s",
178	      test_name, gssapi_err(maj_stat, min_stat, GSS_C_NO_OID));
179	return 0;
180    }
181
182    if (output.length == 0)
183	errx(1, "output.length == 0");
184
185    data.data = output.value;
186    data.length = output.length;
187
188    if (verbose_flag)
189	dump_packet("type2", data.data, data.length);
190
191    ret = heim_ntlm_decode_type2(&data, &type2);
192    if (ret)
193	errx(1, "heim_ntlm_decode_type2");
194
195    gss_release_buffer(&min_stat, &output);
196
197    type3.flags = type1.flags & type2.flags;
198    type3.username = rk_UNCONST(user);
199    if (use_server_domain)
200	type3.targetname = type2.targetname;
201    else
202	type3.targetname = rk_UNCONST(domain);
203    type3.ws = rk_UNCONST("workstation");
204
205    {
206	struct ntlm_buf key, tempsession;
207
208	heim_ntlm_nt_key(password, &key);
209
210	heim_ntlm_calculate_ntlm1(key.data, key.length,
211				  type2.challenge,
212				  &type3.ntlm);
213
214	heim_ntlm_v1_base_session(key.data, key.length, &tempsession);
215	heim_ntlm_free_buf(&key);
216
217	if (type3.flags & NTLM_NEG_KEYEX) {
218	    heim_ntlm_keyex_wrap(&tempsession, &sessionkey, &type3.sessionkey);
219	    heim_ntlm_free_buf(&tempsession);
220	} else {
221	    sessionkey = tempsession;
222	}
223    }
224
225    ret = heim_ntlm_encode_type3(&type3, &data, NULL);
226    if (ret)
227	errx(1, "heim_ntlm_encode_type3");
228
229    if (verbose_flag)
230	dump_packet("type3", data.data, data.length);
231
232    input.length = data.length;
233    input.value = data.data;
234
235    maj_stat = gss_accept_sec_context(&min_stat,
236				      &ctx,
237				      GSS_C_NO_CREDENTIAL,
238				      &input,
239				      GSS_C_NO_CHANNEL_BINDINGS,
240				      &src_name,
241				      NULL,
242				      &output,
243				      NULL,
244				      NULL,
245				      NULL);
246    free(input.value);
247    if (maj_stat != GSS_S_COMPLETE) {
248	warnx("accept_sec_context 2 %s: %s",
249	      test_name, gssapi_err(maj_stat, min_stat, GSS_C_NO_OID));
250	return 1;
251    }
252
253    gss_release_buffer(&min_stat, &output);
254
255    verify_session_key(ctx, &sessionkey,
256		       (flags & NTLM_NEG_KEYEX) ? "v1-keyex" : "v1");
257
258    heim_ntlm_free_buf(&sessionkey);
259
260    if (verbose_flag)
261	dump_pac(ctx);
262
263    /* check that we have a source name */
264
265    if (src_name == GSS_C_NO_NAME)
266	errx(1, "no source name!");
267
268    gss_display_name(&min_stat, src_name, &output, NULL);
269
270    if (verbose_flag)
271      printf("src_name: %.*s\n", (int)output.length, (char*)output.value);
272
273    gss_release_name(&min_stat, &src_name);
274    gss_release_buffer(&min_stat, &output);
275
276    gss_delete_sec_context(&min_stat, &ctx, NULL);
277
278    printf("done: %s\n", test_name);
279
280    return 0;
281}
282
283static int
284test_libntlm_v2(const char *test_name, int flags,
285		const char *user, const char *domain, const char *password)
286{
287    OM_uint32 maj_stat, min_stat;
288    gss_ctx_id_t ctx = GSS_C_NO_CONTEXT;
289    gss_name_t src_name = GSS_C_NO_NAME;
290    gss_buffer_desc input, output;
291    struct ntlm_type1 type1;
292    struct ntlm_type2 type2;
293    struct ntlm_type3 type3;
294    struct ntlm_buf data;
295    krb5_error_code ret;
296    struct ntlm_buf sessionkey;
297
298    memset(&type1, 0, sizeof(type1));
299    memset(&type2, 0, sizeof(type2));
300    memset(&type3, 0, sizeof(type3));
301
302    type1.flags = NTLM_NEG_UNICODE|NTLM_NEG_NTLM|flags;
303    type1.domain = strdup(domain);
304    type1.hostname = NULL;
305    type1.os[0] = 0;
306    type1.os[1] = 0;
307
308    ret = heim_ntlm_encode_type1(&type1, &data);
309    if (ret)
310	errx(1, "heim_ntlm_encode_type1");
311
312    if (verbose_flag)
313	dump_packet("type1", data.data, data.length);
314
315    input.value = data.data;
316    input.length = data.length;
317
318    output.length = 0;
319    output.value = NULL;
320
321    maj_stat = gss_accept_sec_context(&min_stat,
322				      &ctx,
323				      GSS_C_NO_CREDENTIAL,
324				      &input,
325				      GSS_C_NO_CHANNEL_BINDINGS,
326				      NULL,
327				      NULL,
328				      &output,
329				      NULL,
330				      NULL,
331				      NULL);
332    free(data.data);
333    if (GSS_ERROR(maj_stat)) {
334	warnx("accept_sec_context 1 %s: %s",
335	      test_name, gssapi_err(maj_stat, min_stat, GSS_C_NO_OID));
336	return 1;
337    }
338
339    if (output.length == 0)
340	errx(1, "output.length == 0");
341
342    data.data = output.value;
343    data.length = output.length;
344
345    if (verbose_flag)
346	dump_packet("type2", data.data, data.length);
347
348    ret = heim_ntlm_decode_type2(&data, &type2);
349    if (ret)
350	errx(1, "heim_ntlm_decode_type2: %d", ret);
351
352    if (type2.targetinfo.length) {
353	struct ntlm_targetinfo ti;
354
355	ret = heim_ntlm_decode_targetinfo(&type2.targetinfo, 1, &ti);
356	if (ret)
357	    errx(1, "heim_ntlm_decode_targetinfo: %d", ret);
358
359	if (ti.domainname == NULL)
360	    errx(1, "no domain name, windows clients hates this");
361	if (ti.servername == NULL)
362	    errx(1, "no servername name, windows clients hates this");
363
364	heim_ntlm_free_targetinfo(&ti);
365    } else {
366	warnx("no targetinfo");
367    }
368
369    type3.flags = type1.flags & type2.flags;
370    type3.username = rk_UNCONST(user);
371    if (use_server_domain)
372	type3.targetname = type2.targetname;
373    else
374	type3.targetname = rk_UNCONST(domain);
375    type3.ws = rk_UNCONST("workstation");
376
377    {
378	struct ntlm_buf key, tempsession, chal;
379	unsigned char ntlmv2[16];
380
381	heim_ntlm_nt_key(password, &key);
382
383	if (verbose_flag)
384	    dump_packet("user key", key.data, key.length);
385
386        heim_ntlm_calculate_lm2(key.data, key.length,
387                                user,
388                                type3.targetname,
389                                type2.challenge,
390                                ntlmv2,
391                                &type3.lm);
392
393        chal.length = 8;
394        chal.data = type2.challenge;
395
396	if (verbose_flag)
397	    dump_packet("lm", type3.lm.data, type3.lm.length);
398
399	heim_ntlm_calculate_ntlm2(key.data, key.length,
400				  user,
401				  type3.targetname,
402				  type2.challenge,
403				  &type2.targetinfo,
404				  ntlmv2,
405				  &type3.ntlm);
406
407	if (verbose_flag)
408	    dump_packet("ntlm", type3.ntlm.data, type3.ntlm.length);
409
410	heim_ntlm_v2_base_session(ntlmv2, sizeof(ntlmv2),
411				  &type3.ntlm,
412				  &tempsession);
413	if (verbose_flag)
414	    dump_packet("base session key", tempsession.data, tempsession.length);
415
416	heim_ntlm_free_buf(&key);
417
418	if (type3.flags & NTLM_NEG_KEYEX) {
419	    heim_ntlm_keyex_wrap(&tempsession, &sessionkey, &type3.sessionkey);
420	    heim_ntlm_free_buf(&tempsession);
421	} else {
422	    sessionkey = tempsession;
423	}
424	memset(ntlmv2, 0, sizeof(ntlmv2));
425
426	if (verbose_flag)
427	    dump_packet("session key", sessionkey.data, sessionkey.length);
428    }
429
430    ret = heim_ntlm_encode_type3(&type3, &data, NULL);
431    if (ret)
432	errx(1, "heim_ntlm_encode_type3");
433
434    if (verbose_flag)
435	dump_packet("type3", data.data, data.length);
436
437    input.length = data.length;
438    input.value = data.data;
439
440    maj_stat = gss_accept_sec_context(&min_stat,
441				      &ctx,
442				      GSS_C_NO_CREDENTIAL,
443				      &input,
444				      GSS_C_NO_CHANNEL_BINDINGS,
445				      &src_name,
446				      NULL,
447				      &output,
448				      NULL,
449				      NULL,
450				      NULL);
451    free(input.value);
452    if (maj_stat != GSS_S_COMPLETE) {
453	warnx("accept_sec_context 2 %s: %s",
454	      test_name, gssapi_err(maj_stat, min_stat, GSS_C_NO_OID));
455	return 1;
456    }
457
458    gss_release_buffer(&min_stat, &output);
459
460    verify_session_key(ctx, &sessionkey,
461		       (flags & NTLM_NEG_KEYEX) ? "v2-keyex" : "v2");
462
463    heim_ntlm_free_buf(&sessionkey);
464
465    if (verbose_flag)
466	dump_pac(ctx);
467
468    /* check that we have a source name */
469
470    if (src_name == GSS_C_NO_NAME)
471	errx(1, "no source name!");
472
473    gss_display_name(&min_stat, src_name, &output, NULL);
474
475    if (verbose_flag)
476      printf("src_name: %.*s\n", (int)output.length, (char*)output.value);
477
478    gss_release_name(&min_stat, &src_name);
479    gss_release_buffer(&min_stat, &output);
480
481    gss_delete_sec_context(&min_stat, &ctx, NULL);
482
483    printf("done: %s\n", test_name);
484
485    return 0;
486}
487#endif
488
489static char *user_string = NULL;
490static char *domain_string = NULL;
491static char *password_string = NULL;
492static int version_flag = 0;
493static int help_flag	= 0;
494
495static int ntlmv1 = 1;
496static int ntlmv2 = 1;
497
498static struct getargs args[] = {
499    {"user",	0,	arg_string,	&user_string, "user name", "user" },
500    {"domain",	0,	arg_string,	&domain_string, "domain", "domain" },
501    {"use-server-domain",0,arg_flag,	&use_server_domain, "use server domain" },
502    {"password",0,	arg_string,	&password_string, "password", "password" },
503    {"ntlm1", 0,	arg_negative_flag, &ntlmv1, "don't test NTLMv1", NULL},
504    {"ntlm2", 0,	arg_negative_flag, &ntlmv2, "don't test NTLMv2", NULL},
505    {"session-key-broken",0,arg_flag,	&broken_session_key_flag, "session key is broken, we know", NULL },
506    {"verbose",	0,	arg_flag,	&verbose_flag, "verbose debug output", NULL },
507    {"version",	0,	arg_flag,	&version_flag, "print version", NULL },
508    {"help",	0,	arg_flag,	&help_flag,  NULL, NULL }
509};
510
511static void
512usage (int ret)
513{
514    arg_printusage (args, sizeof(args)/sizeof(*args),
515		    NULL, "");
516    exit (ret);
517}
518
519int
520main(int argc, char **argv)
521{
522    int ret = 0, optidx = 0;
523
524    setprogname(argv[0]);
525
526    if(getarg(args, sizeof(args) / sizeof(args[0]), argc, argv, &optidx))
527	usage(1);
528
529    if (help_flag)
530	usage (0);
531
532    if(version_flag){
533	print_version(NULL);
534	exit(0);
535    }
536
537#ifdef ENABLE_NTLM
538    if (user_string == NULL)
539	errx(1, "no username");
540    if (domain_string == NULL)
541	domain_string = "";
542    if (password_string == NULL)
543	errx(1, "no password");
544
545    if (ntlmv1) {
546	ret += test_libntlm_v1("v1", 0, user_string, domain_string, password_string);
547	ret += test_libntlm_v1("v1 kex", NTLM_NEG_KEYEX, user_string, domain_string, password_string);
548    }
549
550    if (ntlmv2) {
551	ret += test_libntlm_v2("v2", 0, user_string, domain_string, password_string);
552	ret += test_libntlm_v2("v2 kex", NTLM_NEG_KEYEX, user_string, domain_string, password_string);
553    }
554
555#endif
556    return (ret != 0) ? 1 : 0;
557}
558