1/* $NetBSD: kdigest.c,v 1.1.1.1 2011/04/13 18:14:38 elric 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 46static int version_flag = 0; 47static int help_flag = 0; 48static char *ccache_string; 49static krb5_ccache id; 50 51static 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 57static void 58usage (int ret) 59{ 60 arg_printusage (args, sizeof(args)/sizeof(*args), 61 NULL, ""); 62 exit (ret); 63} 64 65static krb5_context context; 66 67int 68digest_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 89int 90digest_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 133int 134digest_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 227static void 228client_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 252static 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}; 258static 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}; 265static 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 271static void 272client_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 360int 361digest_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 431int 432ntlm_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 442 memset(&type2, 0, sizeof(type2)); 443 444 ret = krb5_ntlm_alloc(context, &ntlm); 445 if (ret) 446 krb5_err(context, 1, ret, "krb5_ntlm_alloc"); 447 448 ret = krb5_ntlm_init_request(context, 449 ntlm, 450 opt->kerberos_realm_string, 451 id, 452 NTLM_NEG_UNICODE|NTLM_NEG_NTLM, 453 "NUTCRACKER", 454 "L"); 455 if (ret) 456 krb5_err(context, 1, ret, "krb5_ntlm_init_request"); 457 458 /* 459 * 460 */ 461 462 ret = krb5_ntlm_init_get_challange(context, ntlm, &challenge); 463 if (ret) 464 krb5_err(context, 1, ret, "krb5_ntlm_init_get_challange"); 465 466 if (challenge.length != sizeof(type2.challenge)) 467 krb5_errx(context, 1, "ntlm challenge have wrong length"); 468 memcpy(type2.challenge, challenge.data, sizeof(type2.challenge)); 469 krb5_data_free(&challenge); 470 471 ret = krb5_ntlm_init_get_flags(context, ntlm, &type2.flags); 472 if (ret) 473 krb5_err(context, 1, ret, "krb5_ntlm_init_get_flags"); 474 475 krb5_ntlm_init_get_targetname(context, ntlm, &type2.targetname); 476 type2.targetinfo.data = "\x00\x00"; 477 type2.targetinfo.length = 2; 478 479 ret = heim_ntlm_encode_type2(&type2, &data); 480 if (ret) 481 krb5_errx(context, 1, "heim_ntlm_encode_type2"); 482 483 free(type2.targetname); 484 485 /* 486 * 487 */ 488 489 base64_encode(data.data, data.length, &s); 490 free(data.data); 491 printf("type2=%s\n", s); 492 free(s); 493 494 /* 495 * 496 */ 497 498 ret = krb5_ntlm_init_get_opaque(context, ntlm, &opaque); 499 if (ret) 500 krb5_err(context, 1, ret, "krb5_ntlm_init_get_opaque"); 501 502 base64_encode(opaque.data, opaque.length, &s); 503 krb5_data_free(&opaque); 504 printf("opaque=%s\n", s); 505 free(s); 506 507 /* 508 * 509 */ 510 511 krb5_ntlm_free(context, ntlm); 512 513 return 0; 514} 515 516 517/* 518 * 519 */ 520 521int 522help(void *opt, int argc, char **argv) 523{ 524 sl_slc_help(commands, argc, argv); 525 return 0; 526} 527 528int 529main(int argc, char **argv) 530{ 531 krb5_error_code ret; 532 int optidx = 0; 533 534 setprogname(argv[0]); 535 536 ret = krb5_init_context (&context); 537 if (ret == KRB5_CONFIG_BADFORMAT) 538 errx (1, "krb5_init_context failed to parse configuration file"); 539 else if (ret) 540 errx(1, "krb5_init_context failed: %d", ret); 541 542 if(getarg(args, sizeof(args) / sizeof(args[0]), argc, argv, &optidx)) 543 usage(1); 544 545 if (help_flag) 546 usage (0); 547 548 if(version_flag){ 549 print_version(NULL); 550 exit(0); 551 } 552 553 argc -= optidx; 554 argv += optidx; 555 556 if (argc == 0) { 557 help(NULL, argc, argv); 558 return 1; 559 } 560 561 if (ccache_string) { 562 ret = krb5_cc_resolve(context, ccache_string, &id); 563 if (ret) 564 krb5_err(context, 1, ret, "krb5_cc_resolve"); 565 } 566 567 ret = sl_command (commands, argc, argv); 568 if (ret == -1) { 569 help(NULL, argc, argv); 570 return 1; 571 } 572 return ret; 573} 574