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