/ lib / gssapi / test_gssntlm.c
test_gssntlm.c
  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  
 45  static int use_server_domain = 0;
 46  static int verbose_flag = 0;
 47  static 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  
 58  static void
 59  dump_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  
 70  static void
 71  dump_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  
 87  static void
 88  verify_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  
123  static int
124  test_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      if (!GSSCheckNTLMReflection(type2.challenge))
198  	errx(1, "reflection not detected");
199  
200      type3.flags = type1.flags & type2.flags;
201      type3.username = rk_UNCONST(user);
202      if (use_server_domain)
203  	type3.targetname = type2.targetname;
204      else
205  	type3.targetname = rk_UNCONST(domain);
206      type3.ws = rk_UNCONST("workstation");
207  
208      {
209  	struct ntlm_buf key, tempsession;
210  
211  	heim_ntlm_nt_key(password, &key);
212  
213  	heim_ntlm_calculate_ntlm1(key.data, key.length,
214  				  type2.challenge,
215  				  &type3.ntlm);
216  
217  	heim_ntlm_v1_base_session(key.data, key.length, &tempsession);
218  	heim_ntlm_free_buf(&key);
219  
220  	if (type3.flags & NTLM_NEG_KEYEX) {
221  	    heim_ntlm_keyex_wrap(&tempsession, &sessionkey, &type3.sessionkey);
222  	    heim_ntlm_free_buf(&tempsession);
223  	} else {
224  	    sessionkey = tempsession;
225  	}
226      }
227  
228      ret = heim_ntlm_encode_type3(&type3, &data, NULL);
229      if (ret)
230  	errx(1, "heim_ntlm_encode_type3");
231  
232      if (verbose_flag)
233  	dump_packet("type3", data.data, data.length);
234  
235      input.length = data.length;
236      input.value = data.data;
237  
238      maj_stat = gss_accept_sec_context(&min_stat,
239  				      &ctx,
240  				      GSS_C_NO_CREDENTIAL,
241  				      &input,
242  				      GSS_C_NO_CHANNEL_BINDINGS,
243  				      &src_name,
244  				      NULL,
245  				      &output,
246  				      NULL,
247  				      NULL,
248  				      NULL);
249      free(input.value);
250      if (maj_stat != GSS_S_COMPLETE) {
251  	warnx("accept_sec_context 2 %s: %s",
252  	      test_name, gssapi_err(maj_stat, min_stat, GSS_C_NO_OID));
253  	return 1;
254      }
255  
256      gss_release_buffer(&min_stat, &output);
257  
258      verify_session_key(ctx, &sessionkey, 
259  		       (flags & NTLM_NEG_KEYEX) ? "v1-keyex" : "v1");
260  
261      heim_ntlm_free_buf(&sessionkey);
262  
263      if (verbose_flag)
264  	dump_pac(ctx);
265  
266      /* check that we have a source name */
267  
268      if (src_name == GSS_C_NO_NAME)
269  	errx(1, "no source name!");
270  
271      gss_display_name(&min_stat, src_name, &output, NULL);
272  
273      if (verbose_flag)
274        printf("src_name: %.*s\n", (int)output.length, (char*)output.value);
275  
276      gss_release_name(&min_stat, &src_name);
277      gss_release_buffer(&min_stat, &output);
278  
279      gss_delete_sec_context(&min_stat, &ctx, NULL);
280  
281      printf("done: %s\n", test_name);
282  
283      return 0;
284  }
285  
286  static int
287  test_libntlm_v2(const char *test_name, int flags,
288  		const char *user, const char *domain, const char *password)
289  {
290      OM_uint32 maj_stat, min_stat;
291      gss_ctx_id_t ctx = GSS_C_NO_CONTEXT;
292      gss_name_t src_name = GSS_C_NO_NAME;
293      gss_buffer_desc input, output;
294      struct ntlm_type1 type1;
295      struct ntlm_type2 type2;
296      struct ntlm_type3 type3;
297      struct ntlm_buf data;
298      krb5_error_code ret;
299      struct ntlm_buf sessionkey;
300  
301      memset(&type1, 0, sizeof(type1));
302      memset(&type2, 0, sizeof(type2));
303      memset(&type3, 0, sizeof(type3));
304  
305      type1.flags = NTLM_NEG_UNICODE|NTLM_NEG_NTLM|flags;
306      type1.domain = strdup(domain);
307      type1.hostname = NULL;
308      type1.os[0] = 0;
309      type1.os[1] = 0;
310  
311      ret = heim_ntlm_encode_type1(&type1, &data);
312      if (ret)
313  	errx(1, "heim_ntlm_encode_type1");
314  
315      if (verbose_flag)
316  	dump_packet("type1", data.data, data.length);
317  
318      input.value = data.data;
319      input.length = data.length;
320  
321      output.length = 0;
322      output.value = NULL;
323  
324      maj_stat = gss_accept_sec_context(&min_stat,
325  				      &ctx,
326  				      GSS_C_NO_CREDENTIAL,
327  				      &input,
328  				      GSS_C_NO_CHANNEL_BINDINGS,
329  				      NULL,
330  				      NULL,
331  				      &output,
332  				      NULL,
333  				      NULL,
334  				      NULL);
335      free(data.data);
336      if (GSS_ERROR(maj_stat)) {
337  	warnx("accept_sec_context 1 %s: %s",
338  	      test_name, gssapi_err(maj_stat, min_stat, GSS_C_NO_OID));
339  	return 1;
340      }
341  
342      if (output.length == 0)
343  	errx(1, "output.length == 0");
344  
345      data.data = output.value;
346      data.length = output.length;
347  
348      if (verbose_flag)
349  	dump_packet("type2", data.data, data.length);
350  
351      ret = heim_ntlm_decode_type2(&data, &type2);
352      if (ret)
353  	errx(1, "heim_ntlm_decode_type2: %d", ret);
354  
355      if (!GSSCheckNTLMReflection(type2.challenge))
356  	errx(1, "reflection not detected");
357  
358      if (type2.targetinfo.length) {
359  	struct ntlm_targetinfo ti;
360  
361  	ret = heim_ntlm_decode_targetinfo(&type2.targetinfo, 1, &ti);
362  	if (ret)
363  	    errx(1, "heim_ntlm_decode_targetinfo: %d", ret);
364  	
365  	if (ti.domainname == NULL)
366  	    errx(1, "no domain name, windows clients hates this");
367  	if (ti.servername == NULL)
368  	    errx(1, "no servername name, windows clients hates this");
369  	
370  	heim_ntlm_free_targetinfo(&ti);
371      } else {
372  	warnx("no targetinfo");
373      }
374  
375      type3.flags = type1.flags & type2.flags;
376      type3.username = rk_UNCONST(user);
377      if (use_server_domain)
378  	type3.targetname = type2.targetname;
379      else
380  	type3.targetname = rk_UNCONST(domain);
381      type3.ws = rk_UNCONST("workstation");
382  
383      {
384  	struct ntlm_buf key, tempsession, chal;
385  	unsigned char ntlmv2[16];
386  
387  	heim_ntlm_nt_key(password, &key);
388  
389  	if (verbose_flag)
390  	    dump_packet("user key", key.data, key.length);
391  
392          heim_ntlm_calculate_lm2(key.data, key.length,
393                                  user,
394                                  type3.targetname,
395                                  type2.challenge,
396                                  ntlmv2,
397                                  &type3.lm);
398  
399          chal.length = 8;
400          chal.data = type2.challenge;
401  
402  	if (verbose_flag)
403  	    dump_packet("lm", type3.lm.data, type3.lm.length);
404  
405  	heim_ntlm_calculate_ntlm2(key.data, key.length,
406  				  user,
407  				  type3.targetname,
408  				  type2.challenge,
409  				  &type2.targetinfo,
410  				  ntlmv2,
411  				  &type3.ntlm);
412  
413  	if (verbose_flag)
414  	    dump_packet("ntlm", type3.ntlm.data, type3.ntlm.length);
415  
416  	heim_ntlm_v2_base_session(ntlmv2, sizeof(ntlmv2),
417  				  &type3.ntlm,
418  				  &tempsession);
419  	if (verbose_flag)
420  	    dump_packet("base session key", tempsession.data, tempsession.length);
421  
422  	heim_ntlm_free_buf(&key);
423  
424  	if (type3.flags & NTLM_NEG_KEYEX) {
425  	    heim_ntlm_keyex_wrap(&tempsession, &sessionkey, &type3.sessionkey);
426  	    heim_ntlm_free_buf(&tempsession);
427  	} else {
428  	    sessionkey = tempsession;
429  	}
430  	memset(ntlmv2, 0, sizeof(ntlmv2));
431  
432  	if (verbose_flag)
433  	    dump_packet("session key", sessionkey.data, sessionkey.length);
434      }
435  
436      ret = heim_ntlm_encode_type3(&type3, &data, NULL);
437      if (ret)
438  	errx(1, "heim_ntlm_encode_type3");
439  
440      if (verbose_flag)
441  	dump_packet("type3", data.data, data.length);
442  
443      input.length = data.length;
444      input.value = data.data;
445  
446      maj_stat = gss_accept_sec_context(&min_stat,
447  				      &ctx,
448  				      GSS_C_NO_CREDENTIAL,
449  				      &input,
450  				      GSS_C_NO_CHANNEL_BINDINGS,
451  				      &src_name,
452  				      NULL,
453  				      &output,
454  				      NULL,
455  				      NULL,
456  				      NULL);
457      free(input.value);
458      if (maj_stat != GSS_S_COMPLETE) {
459  	warnx("accept_sec_context 2 %s: %s",
460  	      test_name, gssapi_err(maj_stat, min_stat, GSS_C_NO_OID));
461  	return 1;
462      }
463  
464      gss_release_buffer(&min_stat, &output);
465  
466      verify_session_key(ctx, &sessionkey,
467  		       (flags & NTLM_NEG_KEYEX) ? "v2-keyex" : "v2");
468  
469      heim_ntlm_free_buf(&sessionkey);
470  
471      if (verbose_flag)
472  	dump_pac(ctx);
473  
474      /* check that we have a source name */
475  
476      if (src_name == GSS_C_NO_NAME)
477  	errx(1, "no source name!");
478  
479      gss_display_name(&min_stat, src_name, &output, NULL);
480  
481      if (verbose_flag)
482        printf("src_name: %.*s\n", (int)output.length, (char*)output.value);
483  
484      gss_release_name(&min_stat, &src_name);
485      gss_release_buffer(&min_stat, &output);
486  
487      gss_delete_sec_context(&min_stat, &ctx, NULL);
488  
489      printf("done: %s\n", test_name);
490  
491      return 0;
492  }
493  #endif
494  
495  static char *user_string = NULL;
496  static char *domain_string = NULL;
497  static char *password_string = NULL;
498  static int version_flag = 0;
499  static int help_flag	= 0;
500  
501  static int ntlmv1 = 0;
502  static int ntlmv2 = 1;
503  
504  static struct getargs args[] = {
505      {"user",	0,	arg_string,	&user_string, "user name", "user" },
506      {"domain",	0,	arg_string,	&domain_string, "domain", "domain" },
507      {"use-server-domain",0,arg_flag,	&use_server_domain, "use server domain" },
508      {"password",0,	arg_string,	&password_string, "password", "password" },
509      {"ntlm1", 0,	arg_flag,	&ntlmv1, "do test NTLMv1", NULL},
510      {"ntlm2", 0,	arg_negative_flag, &ntlmv2, "don't test NTLMv2", NULL},
511      {"session-key-broken",0,arg_flag,	&broken_session_key_flag, "session key is broken, we know", NULL },
512      {"verbose",	0,	arg_flag,	&verbose_flag, "verbose debug output", NULL },
513      {"version",	0,	arg_flag,	&version_flag, "print version", NULL },
514      {"help",	0,	arg_flag,	&help_flag,  NULL, NULL }
515  };
516  
517  static void
518  usage (int ret)
519  {
520      arg_printusage (args, sizeof(args)/sizeof(*args),
521  		    NULL, "");
522      exit (ret);
523  }
524  
525  int
526  main(int argc, char **argv)
527  {
528      int ret = 0, optidx = 0;
529  
530      setprogname(argv[0]);
531  
532      if(getarg(args, sizeof(args) / sizeof(args[0]), argc, argv, &optidx))
533  	usage(1);
534  
535      if (help_flag)
536  	usage (0);
537  
538      if(version_flag){
539  	print_version(NULL);
540  	exit(0);
541      }
542  
543  #ifdef ENABLE_NTLM
544      if (user_string == NULL)
545  	errx(1, "no username");
546      if (domain_string == NULL)
547  	domain_string = "";
548      if (password_string == NULL)
549  	errx(1, "no password");
550  
551      if (ntlmv1) {
552  	ret += test_libntlm_v1("v1", 0, user_string, domain_string, password_string);
553  	ret += test_libntlm_v1("v1 kex", NTLM_NEG_KEYEX, user_string, domain_string, password_string);
554      }
555  
556      if (ntlmv2) {
557  	ret += test_libntlm_v2("v2", 0, user_string, domain_string, password_string);
558  	ret += test_libntlm_v2("v2 kex", NTLM_NEG_KEYEX, user_string, domain_string, password_string);
559      }
560  
561  #endif
562      return (ret != 0) ? 1 : 0;
563  }