1 /* 2 * Copyright (c) 2006 - 2007 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 the Institute nor the names of its contributors 18 * may be used to endorse or promote products derived from this software 19 * without specific prior written permission. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND 22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24 * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE 25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31 * SUCH DAMAGE. 32 */ 33 34 #define HC_DEPRECATED_CRYPTO 35 36 #include "kuser_locl.h" 37 38 #include <kdigest-commands.h> 39 #include <hex.h> 40 #include <base64.h> 41 #include <heimntlm.h> 42 #include "crypto-headers.h" 43 44 static int version_flag = 0; 45 static int help_flag = 0; 46 static char *ccache_string; 47 static krb5_ccache id; 48 49 static struct getargs args[] = { 50 {"ccache", 0, arg_string, &ccache_string, "credential cache", NULL }, 51 {"version", 0, arg_flag, &version_flag, "print version", NULL }, 52 {"help", 0, arg_flag, &help_flag, NULL, NULL } 53 }; 54 55 static void 56 usage (int ret) 57 { 58 arg_printusage (args, sizeof(args)/sizeof(*args), 59 NULL, ""); 60 exit (ret); 61 } 62 63 static krb5_context context; 64 65 int 66 digest_probe(struct digest_probe_options *opt, 67 int argc, char ** argv) 68 { 69 krb5_error_code ret; 70 krb5_realm realm; 71 unsigned flags; 72 73 realm = opt->realm_string; 74 75 if (realm == NULL) 76 errx(1, "realm missing"); 77 78 ret = krb5_digest_probe(context, realm, id, &flags); 79 if (ret) 80 krb5_err(context, 1, ret, "digest_probe"); 81 82 printf("flags: %u\n", flags); 83 84 return 0; 85 } 86 87 int 88 digest_server_init(struct digest_server_init_options *opt, 89 int argc, char ** argv) 90 { 91 krb5_error_code ret; 92 krb5_digest digest; 93 94 ret = krb5_digest_alloc(context, &digest); 95 if (ret) 96 krb5_err(context, 1, ret, "digest_alloc"); 97 98 ret = krb5_digest_set_type(context, digest, opt->type_string); 99 if (ret) 100 krb5_err(context, 1, ret, "krb5_digest_set_type"); 101 102 if (opt->cb_type_string && opt->cb_value_string) { 103 ret = krb5_digest_set_server_cb(context, digest, 104 opt->cb_type_string, 105 opt->cb_value_string); 106 if (ret) 107 krb5_err(context, 1, ret, "krb5_digest_set_server_cb"); 108 } 109 ret = krb5_digest_init_request(context, 110 digest, 111 opt->kerberos_realm_string, 112 id); 113 if (ret) 114 krb5_err(context, 1, ret, "krb5_digest_init_request"); 115 116 printf("type=%s\n", opt->type_string); 117 printf("server-nonce=%s\n", 118 krb5_digest_get_server_nonce(context, digest)); 119 { 120 const char *s = krb5_digest_get_identifier(context, digest); 121 if (s) 122 printf("identifier=%s\n", s); 123 } 124 printf("opaque=%s\n", krb5_digest_get_opaque(context, digest)); 125 126 krb5_digest_free(digest); 127 128 return 0; 129 } 130 131 int 132 digest_server_request(struct digest_server_request_options *opt, 133 int argc, char **argv) 134 { 135 krb5_error_code ret; 136 krb5_digest digest; 137 const char *status, *rsp; 138 krb5_data session_key; 139 140 if (opt->server_nonce_string == NULL) 141 errx(1, "server nonce missing"); 142 if (opt->type_string == NULL) 143 errx(1, "type missing"); 144 if (opt->opaque_string == NULL) 145 errx(1, "opaque missing"); 146 if (opt->client_response_string == NULL) 147 errx(1, "client response missing"); 148 149 ret = krb5_digest_alloc(context, &digest); 150 if (ret) 151 krb5_err(context, 1, ret, "digest_alloc"); 152 153 if (strcasecmp(opt->type_string, "CHAP") == 0) { 154 if (opt->server_identifier_string == NULL) 155 errx(1, "server identifier missing"); 156 157 ret = krb5_digest_set_identifier(context, digest, 158 opt->server_identifier_string); 159 if (ret) 160 krb5_err(context, 1, ret, "krb5_digest_set_type"); 161 } 162 163 ret = krb5_digest_set_type(context, digest, opt->type_string); 164 if (ret) 165 krb5_err(context, 1, ret, "krb5_digest_set_type"); 166 167 ret = krb5_digest_set_username(context, digest, opt->username_string); 168 if (ret) 169 krb5_err(context, 1, ret, "krb5_digest_set_username"); 170 171 ret = krb5_digest_set_server_nonce(context, digest, 172 opt->server_nonce_string); 173 if (ret) 174 krb5_err(context, 1, ret, "krb5_digest_set_server_nonce"); 175 176 if(opt->client_nonce_string) { 177 ret = krb5_digest_set_client_nonce(context, digest, 178 opt->client_nonce_string); 179 if (ret) 180 krb5_err(context, 1, ret, "krb5_digest_set_client_nonce"); 181 } 182 183 184 ret = krb5_digest_set_opaque(context, digest, opt->opaque_string); 185 if (ret) 186 krb5_err(context, 1, ret, "krb5_digest_set_opaque"); 187 188 ret = krb5_digest_set_responseData(context, digest, 189 opt->client_response_string); 190 if (ret) 191 krb5_err(context, 1, ret, "krb5_digest_set_responseData"); 192 193 ret = krb5_digest_request(context, digest, 194 opt->kerberos_realm_string, id); 195 if (ret) 196 krb5_err(context, 1, ret, "krb5_digest_request"); 197 198 status = krb5_digest_rep_get_status(context, digest) ? "ok" : "failed"; 199 rsp = krb5_digest_get_rsp(context, digest); 200 201 printf("status=%s\n", status); 202 if (rsp) 203 printf("rsp=%s\n", rsp); 204 printf("tickets=no\n"); 205 206 ret = krb5_digest_get_session_key(context, digest, &session_key); 207 if (ret) 208 krb5_err(context, 1, ret, "krb5_digest_get_session_key"); 209 210 if (session_key.length) { 211 char *key; 212 hex_encode(session_key.data, session_key.length, &key); 213 if (key == NULL) 214 krb5_errx(context, 1, "hex_encode"); 215 krb5_data_free(&session_key); 216 printf("session-key=%s\n", key); 217 free(key); 218 } 219 220 krb5_digest_free(digest); 221 222 return 0; 223 } 224 225 static void 226 client_chap(const void *server_nonce, size_t snoncelen, 227 unsigned char server_identifier, 228 const char *password) 229 { 230 EVP_MD_CTX *ctx; 231 unsigned char md[MD5_DIGEST_LENGTH]; 232 char *h; 233 234 ctx = EVP_MD_CTX_create(); 235 EVP_DigestInit_ex(ctx, EVP_md5(), NULL); 236 237 EVP_DigestUpdate(ctx, &server_identifier, 1); 238 EVP_DigestUpdate(ctx, password, strlen(password)); 239 EVP_DigestUpdate(ctx, server_nonce, snoncelen); 240 EVP_DigestFinal_ex(ctx, md, NULL); 241 242 EVP_MD_CTX_destroy(ctx); 243 244 hex_encode(md, 16, &h); 245 246 printf("responseData=%s\n", h); 247 free(h); 248 } 249 250 static const unsigned char ms_chap_v2_magic1[39] = { 251 0x4D, 0x61, 0x67, 0x69, 0x63, 0x20, 0x73, 0x65, 0x72, 0x76, 252 0x65, 0x72, 0x20, 0x74, 0x6F, 0x20, 0x63, 0x6C, 0x69, 0x65, 253 0x6E, 0x74, 0x20, 0x73, 0x69, 0x67, 0x6E, 0x69, 0x6E, 0x67, 254 0x20, 0x63, 0x6F, 0x6E, 0x73, 0x74, 0x61, 0x6E, 0x74 255 }; 256 static const unsigned char ms_chap_v2_magic2[41] = { 257 0x50, 0x61, 0x64, 0x20, 0x74, 0x6F, 0x20, 0x6D, 0x61, 0x6B, 258 0x65, 0x20, 0x69, 0x74, 0x20, 0x64, 0x6F, 0x20, 0x6D, 0x6F, 259 0x72, 0x65, 0x20, 0x74, 0x68, 0x61, 0x6E, 0x20, 0x6F, 0x6E, 260 0x65, 0x20, 0x69, 0x74, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6F, 261 0x6E 262 }; 263 static const unsigned char ms_rfc3079_magic1[27] = { 264 0x54, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x74, 265 0x68, 0x65, 0x20, 0x4d, 0x50, 0x50, 0x45, 0x20, 0x4d, 266 0x61, 0x73, 0x74, 0x65, 0x72, 0x20, 0x4b, 0x65, 0x79 267 }; 268 269 static void 270 client_mschapv2(const void *server_nonce, size_t snoncelen, 271 const void *client_nonce, size_t cnoncelen, 272 const char *username, 273 const char *password) 274 { 275 EVP_MD_CTX *hctx, *ctx; 276 unsigned char md[SHA_DIGEST_LENGTH], challenge[SHA_DIGEST_LENGTH]; 277 unsigned char hmd[MD4_DIGEST_LENGTH]; 278 struct ntlm_buf answer; 279 int i, len, ret; 280 char *h; 281 282 ctx = EVP_MD_CTX_create(); 283 EVP_DigestInit_ex(ctx, EVP_sha1(), NULL); 284 285 EVP_DigestUpdate(ctx, client_nonce, cnoncelen); 286 EVP_DigestUpdate(ctx, server_nonce, snoncelen); 287 EVP_DigestUpdate(ctx, username, strlen(username)); 288 EVP_DigestFinal_ex(ctx, md, NULL); 289 290 291 hctx = EVP_MD_CTX_create(); 292 EVP_DigestInit_ex(hctx, EVP_md4(), NULL); 293 len = strlen(password); 294 for (i = 0; i < len; i++) { 295 EVP_DigestUpdate(hctx, &password[i], 1); 296 EVP_DigestUpdate(hctx, &password[len], 1); 297 } 298 EVP_DigestFinal_ex(hctx, hmd, NULL); 299 300 301 /* ChallengeResponse */ 302 ret = heim_ntlm_calculate_ntlm1(hmd, sizeof(hmd), md, &answer); 303 if (ret) 304 errx(1, "heim_ntlm_calculate_ntlm1"); 305 306 hex_encode(answer.data, answer.length, &h); 307 printf("responseData=%s\n", h); 308 free(h); 309 310 /* PasswordHash */ 311 EVP_DigestInit_ex(hctx, EVP_md4(), NULL); 312 EVP_DigestUpdate(hctx, hmd, sizeof(hmd)); 313 EVP_DigestFinal_ex(hctx, hmd, NULL); 314 315 316 /* GenerateAuthenticatorResponse */ 317 EVP_DigestInit_ex(ctx, EVP_sha1(), NULL); 318 EVP_DigestUpdate(ctx, hmd, sizeof(hmd)); 319 EVP_DigestUpdate(ctx, answer.data, answer.length); 320 EVP_DigestUpdate(ctx, ms_chap_v2_magic1, sizeof(ms_chap_v2_magic1)); 321 EVP_DigestFinal_ex(ctx, md, NULL); 322 323 /* ChallengeHash */ 324 EVP_DigestInit_ex(ctx, EVP_sha1(), NULL); 325 EVP_DigestUpdate(ctx, client_nonce, cnoncelen); 326 EVP_DigestUpdate(ctx, server_nonce, snoncelen); 327 EVP_DigestUpdate(ctx, username, strlen(username)); 328 EVP_DigestFinal_ex(ctx, challenge, NULL); 329 330 EVP_DigestInit_ex(ctx, EVP_sha1(), NULL); 331 EVP_DigestUpdate(ctx, md, sizeof(md)); 332 EVP_DigestUpdate(ctx, challenge, 8); 333 EVP_DigestUpdate(ctx, ms_chap_v2_magic2, sizeof(ms_chap_v2_magic2)); 334 EVP_DigestFinal_ex(ctx, md, NULL); 335 336 hex_encode(md, sizeof(md), &h); 337 printf("AuthenticatorResponse=%s\n", h); 338 free(h); 339 340 /* get_master, rfc 3079 3.4 */ 341 EVP_DigestInit_ex(ctx, EVP_sha1(), NULL); 342 EVP_DigestUpdate(ctx, hmd, sizeof(hmd)); 343 EVP_DigestUpdate(ctx, answer.data, answer.length); 344 EVP_DigestUpdate(ctx, ms_rfc3079_magic1, sizeof(ms_rfc3079_magic1)); 345 EVP_DigestFinal_ex(ctx, md, NULL); 346 347 free(answer.data); 348 349 hex_encode(md, 16, &h); 350 printf("session-key=%s\n", h); 351 free(h); 352 353 EVP_MD_CTX_destroy(hctx); 354 EVP_MD_CTX_destroy(ctx); 355 } 356 357 358 int 359 digest_client_request(struct digest_client_request_options *opt, 360 int argc, char **argv) 361 { 362 char *server_nonce, *client_nonce = NULL, server_identifier; 363 ssize_t snoncelen, cnoncelen = 0; 364 365 if (opt->server_nonce_string == NULL) 366 errx(1, "server nonce missing"); 367 if (opt->password_string == NULL) 368 errx(1, "password missing"); 369 370 if (opt->opaque_string == NULL) 371 errx(1, "opaque missing"); 372 373 snoncelen = strlen(opt->server_nonce_string); 374 server_nonce = malloc(snoncelen); 375 if (server_nonce == NULL) 376 errx(1, "server_nonce"); 377 378 snoncelen = hex_decode(opt->server_nonce_string, server_nonce, snoncelen); 379 if (snoncelen <= 0) 380 errx(1, "server nonce wrong"); 381 382 if (opt->client_nonce_string) { 383 cnoncelen = strlen(opt->client_nonce_string); 384 client_nonce = malloc(cnoncelen); 385 if (client_nonce == NULL) 386 errx(1, "client_nonce"); 387 388 cnoncelen = hex_decode(opt->client_nonce_string, 389 client_nonce, cnoncelen); 390 if (cnoncelen <= 0) 391 errx(1, "client nonce wrong"); 392 } 393 394 if (opt->server_identifier_string) { 395 int ret; 396 397 ret = hex_decode(opt->server_identifier_string, &server_identifier, 1); 398 if (ret != 1) 399 errx(1, "server identifier wrong length"); 400 } 401 402 if (strcasecmp(opt->type_string, "CHAP") == 0) { 403 if (opt->server_identifier_string == NULL) 404 errx(1, "server identifier missing"); 405 406 client_chap(server_nonce, snoncelen, server_identifier, 407 opt->password_string); 408 409 } else if (strcasecmp(opt->type_string, "MS-CHAP-V2") == 0) { 410 if (opt->client_nonce_string == NULL) 411 errx(1, "client nonce missing"); 412 if (opt->username_string == NULL) 413 errx(1, "client nonce missing"); 414 415 client_mschapv2(server_nonce, snoncelen, 416 client_nonce, cnoncelen, 417 opt->username_string, 418 opt->password_string); 419 } 420 if (client_nonce) 421 free(client_nonce); 422 free(server_nonce); 423 424 return 0; 425 } 426 427 #include <heimntlm.h> 428 429 int 430 ntlm_server_init(struct ntlm_server_init_options *opt, 431 int argc, char ** argv) 432 { 433 krb5_error_code ret; 434 krb5_ntlm ntlm; 435 struct ntlm_type2 type2; 436 krb5_data challenge, opaque; 437 struct ntlm_buf data; 438 char *s; 439 static char zero2[] = "\x00\x00"; 440 441 memset(&type2, 0, sizeof(type2)); 442 443 ret = krb5_ntlm_alloc(context, &ntlm); 444 if (ret) 445 krb5_err(context, 1, ret, "krb5_ntlm_alloc"); 446 447 ret = krb5_ntlm_init_request(context, 448 ntlm, 449 opt->kerberos_realm_string, 450 id, 451 NTLM_NEG_UNICODE|NTLM_NEG_NTLM, 452 "NUTCRACKER", 453 "L"); 454 if (ret) 455 krb5_err(context, 1, ret, "krb5_ntlm_init_request"); 456 457 /* 458 * 459 */ 460 461 ret = krb5_ntlm_init_get_challange(context, ntlm, &challenge); 462 if (ret) 463 krb5_err(context, 1, ret, "krb5_ntlm_init_get_challange"); 464 465 if (challenge.length != sizeof(type2.challenge)) 466 krb5_errx(context, 1, "ntlm challenge have wrong length"); 467 memcpy(type2.challenge, challenge.data, sizeof(type2.challenge)); 468 krb5_data_free(&challenge); 469 470 ret = krb5_ntlm_init_get_flags(context, ntlm, &type2.flags); 471 if (ret) 472 krb5_err(context, 1, ret, "krb5_ntlm_init_get_flags"); 473 474 krb5_ntlm_init_get_targetname(context, ntlm, &type2.targetname); 475 type2.targetinfo.data = zero2; 476 type2.targetinfo.length = 2; 477 478 ret = heim_ntlm_encode_type2(&type2, &data); 479 if (ret) 480 krb5_errx(context, 1, "heim_ntlm_encode_type2"); 481 482 free(type2.targetname); 483 484 /* 485 * 486 */ 487 488 base64_encode(data.data, data.length, &s); 489 free(data.data); 490 printf("type2=%s\n", s); 491 free(s); 492 493 /* 494 * 495 */ 496 497 ret = krb5_ntlm_init_get_opaque(context, ntlm, &opaque); 498 if (ret) 499 krb5_err(context, 1, ret, "krb5_ntlm_init_get_opaque"); 500 501 base64_encode(opaque.data, opaque.length, &s); 502 krb5_data_free(&opaque); 503 printf("opaque=%s\n", s); 504 free(s); 505 506 /* 507 * 508 */ 509 510 krb5_ntlm_free(context, ntlm); 511 512 return 0; 513 } 514 515 516 /* 517 * 518 */ 519 520 int 521 help(void *opt, int argc, char **argv) 522 { 523 sl_slc_help(commands, argc, argv); 524 return 0; 525 } 526 527 int 528 main(int argc, char **argv) 529 { 530 krb5_error_code ret; 531 int optidx = 0; 532 533 setprogname(argv[0]); 534 535 ret = krb5_init_context (&context); 536 if (ret == KRB5_CONFIG_BADFORMAT) 537 errx (1, "krb5_init_context failed to parse configuration file"); 538 else if (ret) 539 errx(1, "krb5_init_context failed: %d", ret); 540 541 if(getarg(args, sizeof(args) / sizeof(args[0]), argc, argv, &optidx)) 542 usage(1); 543 544 if (help_flag) 545 usage (0); 546 547 if(version_flag){ 548 print_version(NULL); 549 exit(0); 550 } 551 552 argc -= optidx; 553 argv += optidx; 554 555 if (argc == 0) { 556 help(NULL, argc, argv); 557 return 1; 558 } 559 560 if (ccache_string) { 561 ret = krb5_cc_resolve(context, ccache_string, &id); 562 if (ret) 563 krb5_err(context, 1, ret, "krb5_cc_resolve"); 564 } 565 566 ret = sl_command (commands, argc, argv); 567 if (ret == -1) { 568 help(NULL, argc, argv); 569 return 1; 570 } 571 return ret; 572 } 573