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 }