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