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