1 /* 2 * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 3 * Use is subject to license terms. 4 */ 5 /* 6 * lib/krb5/krb/pac.c 7 * 8 * Copyright 2008 by the Massachusetts Institute of Technology. 9 * All Rights Reserved. 10 * 11 * Export of this software from the United States of America may 12 * require a specific license from the United States Government. 13 * It is the responsibility of any person or organization contemplating 14 * export to obtain such a license before exporting. 15 * 16 * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and 17 * distribute this software and its documentation for any purpose and 18 * without fee is hereby granted, provided that the above copyright 19 * notice appear in all copies and that both that copyright notice and 20 * this permission notice appear in supporting documentation, and that 21 * the name of M.I.T. not be used in advertising or publicity pertaining 22 * to distribution of the software without specific, written prior 23 * permission. Furthermore if you modify this software you must label 24 * your software as modified software and not distribute it in such a 25 * fashion that it might be confused with the original M.I.T. software. 26 * M.I.T. makes no representations about the suitability of 27 * this software for any purpose. It is provided "as is" without express 28 * or implied warranty. 29 * 30 */ 31 32 #include "k5-int.h" 33 #include "k5-utf8.h" 34 35 /* draft-brezak-win2k-krb-authz-00 */ 36 37 /* 38 * A PAC consists of a sequence of PAC_INFO_BUFFERs, preceeded by 39 * a PACTYPE header. Decoding the contents of the buffers is left 40 * to the application (notwithstanding signature verification). 41 */ 42 43 /* 44 * SUNW17PACresync 45 * These should eventually go to k5-platform.h or equiv. 46 */ 47 static inline unsigned short 48 load_16_le (const void *cvp) 49 { 50 const unsigned char *p = cvp; 51 #if defined(__GNUC__) && defined(K5_LE) 52 return GET(16,p); 53 #elif defined(__GNUC__) && defined(K5_BE) && defined(SWAP16) 54 return GETSWAPPED(16,p); 55 #else 56 return (p[0] | (p[1] << 8)); 57 #endif 58 } 59 60 static inline unsigned int 61 load_32_le (const void *cvp) 62 { 63 const unsigned char *p = cvp; 64 #if defined(__GNUC__) && defined(K5_LE) 65 return GET(32,p); 66 #elif defined(__GNUC__) && defined(K5_BE) && defined(SWAP32) 67 return GETSWAPPED(32,p); 68 #else 69 return (p[0] | (p[1] << 8) | (p[2] << 16) | (p[3] << 24)); 70 #endif 71 } 72 static inline UINT64_TYPE 73 load_64_le (const void *cvp) 74 { 75 const unsigned char *p = cvp; 76 #if defined(__GNUC__) && defined(K5_LE) 77 return GET(64,p); 78 #elif defined(__GNUC__) && defined(K5_BE) && defined(SWAP64) 79 return GETSWAPPED(64,p); 80 #else 81 return ((UINT64_TYPE)load_32_le(p+4) << 32) | load_32_le(p); 82 #endif 83 } 84 85 static inline void 86 store_16_le (unsigned int val, void *vp) 87 { 88 unsigned char *p = vp; 89 #if defined(__GNUC__) && defined(K5_LE) 90 PUT(16,p,val); 91 #elif defined(__GNUC__) && defined(K5_BE) && defined(SWAP16) 92 PUTSWAPPED(16,p,val); 93 #else 94 p[1] = (val >> 8) & 0xff; 95 p[0] = (val ) & 0xff; 96 #endif 97 } 98 99 static inline void 100 store_32_le (unsigned int val, void *vp) 101 { 102 unsigned char *p = vp; 103 #if defined(__GNUC__) && defined(K5_LE) 104 PUT(32,p,val); 105 #elif defined(__GNUC__) && defined(K5_BE) && defined(SWAP32) 106 PUTSWAPPED(32,p,val); 107 #else 108 p[3] = (val >> 24) & 0xff; 109 p[2] = (val >> 16) & 0xff; 110 p[1] = (val >> 8) & 0xff; 111 p[0] = (val ) & 0xff; 112 #endif 113 } 114 static inline void 115 store_64_le (UINT64_TYPE val, void *vp) 116 { 117 unsigned char *p = vp; 118 #if defined(__GNUC__) && defined(K5_LE) 119 PUT(64,p,val); 120 #elif defined(__GNUC__) && defined(K5_BE) && defined(SWAP64) 121 PUTSWAPPED(64,p,val); 122 #else 123 p[7] = (unsigned char)((val >> 56) & 0xff); 124 p[6] = (unsigned char)((val >> 48) & 0xff); 125 p[5] = (unsigned char)((val >> 40) & 0xff); 126 p[4] = (unsigned char)((val >> 32) & 0xff); 127 p[3] = (unsigned char)((val >> 24) & 0xff); 128 p[2] = (unsigned char)((val >> 16) & 0xff); 129 p[1] = (unsigned char)((val >> 8) & 0xff); 130 p[0] = (unsigned char)((val ) & 0xff); 131 #endif 132 } 133 134 135 typedef struct _PAC_INFO_BUFFER { 136 krb5_ui_4 ulType; 137 krb5_ui_4 cbBufferSize; 138 krb5_ui_8 Offset; 139 } PAC_INFO_BUFFER; 140 141 #define PAC_INFO_BUFFER_LENGTH 16 142 143 /* ulType */ 144 #define PAC_LOGON_INFO 1 145 #define PAC_SERVER_CHECKSUM 6 146 #define PAC_PRIVSVR_CHECKSUM 7 147 #define PAC_CLIENT_INFO 10 148 149 typedef struct _PACTYPE { 150 krb5_ui_4 cBuffers; 151 krb5_ui_4 Version; 152 PAC_INFO_BUFFER Buffers[1]; 153 } PACTYPE; 154 155 #define PAC_ALIGNMENT 8 156 #define PACTYPE_LENGTH 8U 157 #define PAC_SIGNATURE_DATA_LENGTH 4U 158 #define PAC_CLIENT_INFO_LENGTH 10U 159 160 #define NT_TIME_EPOCH 11644473600LL 161 162 struct krb5_pac_data { 163 PACTYPE *pac; /* PAC header + info buffer array */ 164 krb5_data data; /* PAC data (including uninitialised header) */ 165 }; 166 167 static krb5_error_code 168 k5_pac_locate_buffer(krb5_context context, 169 const krb5_pac pac, 170 krb5_ui_4 type, 171 krb5_data *data); 172 173 /* 174 * Add a buffer to the provided PAC and update header. 175 */ 176 static krb5_error_code 177 k5_pac_add_buffer(krb5_context context, 178 krb5_pac pac, 179 krb5_ui_4 type, 180 const krb5_data *data, 181 krb5_boolean zerofill, 182 krb5_data *out_data) 183 { 184 PACTYPE *header; 185 size_t header_len, i, pad = 0; 186 char *pac_data; 187 188 assert((data->data == NULL) == zerofill); 189 190 /* Check there isn't already a buffer of this type */ 191 if (k5_pac_locate_buffer(context, pac, type, NULL) == 0) { 192 return EINVAL; 193 } 194 195 header = (PACTYPE *)realloc(pac->pac, 196 sizeof(PACTYPE) + 197 (pac->pac->cBuffers * sizeof(PAC_INFO_BUFFER))); 198 if (header == NULL) { 199 return ENOMEM; 200 } 201 pac->pac = header; 202 203 header_len = PACTYPE_LENGTH + (pac->pac->cBuffers * PAC_INFO_BUFFER_LENGTH); 204 205 if (data->length % PAC_ALIGNMENT) 206 pad = PAC_ALIGNMENT - (data->length % PAC_ALIGNMENT); 207 208 pac_data = realloc(pac->data.data, 209 pac->data.length + PAC_INFO_BUFFER_LENGTH + data->length + pad); 210 if (pac_data == NULL) { 211 return ENOMEM; 212 } 213 pac->data.data = pac_data; 214 215 /* Update offsets of existing buffers */ 216 for (i = 0; i < pac->pac->cBuffers; i++) 217 pac->pac->Buffers[i].Offset += PAC_INFO_BUFFER_LENGTH; 218 219 /* Make room for new PAC_INFO_BUFFER */ 220 memmove(pac->data.data + header_len + PAC_INFO_BUFFER_LENGTH, 221 pac->data.data + header_len, 222 pac->data.length - header_len); 223 memset(pac->data.data + header_len, 0, PAC_INFO_BUFFER_LENGTH); 224 225 /* Initialise new PAC_INFO_BUFFER */ 226 pac->pac->Buffers[i].ulType = type; 227 pac->pac->Buffers[i].cbBufferSize = data->length; 228 pac->pac->Buffers[i].Offset = pac->data.length + PAC_INFO_BUFFER_LENGTH; 229 assert((pac->pac->Buffers[i].Offset % PAC_ALIGNMENT) == 0); 230 231 /* Copy in new PAC data and zero padding bytes */ 232 if (zerofill) 233 memset(pac->data.data + pac->pac->Buffers[i].Offset, 0, data->length); 234 else 235 memcpy(pac->data.data + pac->pac->Buffers[i].Offset, data->data, data->length); 236 237 memset(pac->data.data + pac->pac->Buffers[i].Offset + data->length, 0, pad); 238 239 pac->pac->cBuffers++; 240 pac->data.length += PAC_INFO_BUFFER_LENGTH + data->length + pad; 241 242 if (out_data != NULL) { 243 out_data->data = pac->data.data + pac->pac->Buffers[i].Offset; 244 out_data->length = data->length; 245 } 246 247 return 0; 248 } 249 250 krb5_error_code KRB5_CALLCONV 251 krb5_pac_add_buffer(krb5_context context, 252 krb5_pac pac, 253 krb5_ui_4 type, 254 const krb5_data *data) 255 { 256 return k5_pac_add_buffer(context, pac, type, data, FALSE, NULL); 257 } 258 259 /* 260 * Free a PAC 261 */ 262 void KRB5_CALLCONV 263 krb5_pac_free(krb5_context context, 264 krb5_pac pac) 265 { 266 if (pac != NULL) { 267 if (pac->data.data != NULL) { 268 memset(pac->data.data, 0, pac->data.length); 269 free(pac->data.data); 270 } 271 if (pac->pac != NULL) 272 free(pac->pac); 273 memset(pac, 0, sizeof(*pac)); 274 free(pac); 275 } 276 } 277 278 static krb5_error_code 279 k5_pac_locate_buffer(krb5_context context, 280 const krb5_pac pac, 281 krb5_ui_4 type, 282 krb5_data *data) 283 { 284 PAC_INFO_BUFFER *buffer = NULL; 285 size_t i; 286 287 if (pac == NULL) 288 return EINVAL; 289 290 for (i = 0; i < pac->pac->cBuffers; i++) { 291 if (pac->pac->Buffers[i].ulType == type) { 292 if (buffer == NULL) 293 buffer = &pac->pac->Buffers[i]; 294 else 295 return EINVAL; 296 } 297 } 298 299 if (buffer == NULL) 300 return ENOENT; 301 302 assert(buffer->Offset + buffer->cbBufferSize <= pac->data.length); 303 304 if (data != NULL) { 305 data->length = buffer->cbBufferSize; 306 data->data = pac->data.data + buffer->Offset; 307 } 308 309 return 0; 310 } 311 312 /* 313 * Find a buffer and copy data into output 314 */ 315 krb5_error_code KRB5_CALLCONV 316 krb5_pac_get_buffer(krb5_context context, 317 krb5_pac pac, 318 krb5_ui_4 type, 319 krb5_data *data) 320 { 321 krb5_data d; 322 krb5_error_code ret; 323 324 ret = k5_pac_locate_buffer(context, pac, type, &d); 325 if (ret != 0) 326 return ret; 327 328 data->data = malloc(d.length); 329 if (data->data == NULL) 330 return ENOMEM; 331 332 data->length = d.length; 333 memcpy(data->data, d.data, d.length); 334 335 return 0; 336 } 337 338 /* 339 * Return an array of the types of data in the PAC 340 */ 341 krb5_error_code KRB5_CALLCONV 342 krb5_pac_get_types(krb5_context context, 343 krb5_pac pac, 344 size_t *len, 345 krb5_ui_4 **types) 346 { 347 size_t i; 348 349 *types = (krb5_ui_4 *)malloc(pac->pac->cBuffers * sizeof(krb5_ui_4)); 350 if (*types == NULL) 351 return ENOMEM; 352 353 *len = pac->pac->cBuffers; 354 355 for (i = 0; i < pac->pac->cBuffers; i++) 356 (*types)[i] = pac->pac->Buffers[i].ulType; 357 358 return 0; 359 } 360 361 /* 362 * Initialize PAC 363 */ 364 krb5_error_code KRB5_CALLCONV 365 krb5_pac_init(krb5_context context, 366 krb5_pac *ppac) 367 { 368 krb5_pac pac; 369 370 pac = (krb5_pac)malloc(sizeof(*pac)); 371 if (pac == NULL) 372 return ENOMEM; 373 374 pac->pac = (PACTYPE *)malloc(sizeof(PACTYPE)); 375 if (pac->pac == NULL) { 376 free( pac); 377 return ENOMEM; 378 } 379 380 pac->pac->cBuffers = 0; 381 pac->pac->Version = 0; 382 383 pac->data.length = PACTYPE_LENGTH; 384 pac->data.data = calloc(1, pac->data.length); 385 if (pac->data.data == NULL) { 386 krb5_pac_free(context, pac); 387 return ENOMEM; 388 } 389 390 *ppac = pac; 391 392 return 0; 393 } 394 395 /* 396 * Parse the supplied data into the PAC allocated by this function 397 */ 398 krb5_error_code KRB5_CALLCONV 399 krb5_pac_parse(krb5_context context, 400 const void *ptr, 401 size_t len, 402 krb5_pac *ppac) 403 { 404 krb5_error_code ret; 405 size_t i; 406 const unsigned char *p = (const unsigned char *)ptr; 407 krb5_pac pac; 408 size_t header_len; 409 krb5_ui_4 cbuffers, version; 410 411 *ppac = NULL; 412 413 if (len < PACTYPE_LENGTH) 414 return ERANGE; 415 416 cbuffers = load_32_le(p); 417 p += 4; 418 version = load_32_le(p); 419 p += 4; 420 421 if (version != 0) 422 return EINVAL; 423 424 header_len = PACTYPE_LENGTH + (cbuffers * PAC_INFO_BUFFER_LENGTH); 425 if (len < header_len) 426 return ERANGE; 427 428 ret = krb5_pac_init(context, &pac); 429 if (ret != 0) 430 return ret; 431 432 pac->pac = (PACTYPE *)realloc(pac->pac, 433 sizeof(PACTYPE) + ((cbuffers - 1) * sizeof(PAC_INFO_BUFFER))); 434 if (pac->pac == NULL) { 435 krb5_pac_free(context, pac); 436 return ENOMEM; 437 } 438 439 pac->pac->cBuffers = cbuffers; 440 pac->pac->Version = version; 441 442 for (i = 0; i < pac->pac->cBuffers; i++) { 443 PAC_INFO_BUFFER *buffer = &pac->pac->Buffers[i]; 444 445 buffer->ulType = load_32_le(p); 446 p += 4; 447 buffer->cbBufferSize = load_32_le(p); 448 p += 4; 449 buffer->Offset = load_64_le(p); 450 p += 8; 451 452 if (buffer->Offset % PAC_ALIGNMENT) { 453 krb5_pac_free(context, pac); 454 return EINVAL; 455 } 456 if (buffer->Offset < header_len || 457 buffer->Offset + buffer->cbBufferSize > len) { 458 krb5_pac_free(context, pac); 459 return ERANGE; 460 } 461 } 462 463 pac->data.data = realloc(pac->data.data, len); 464 if (pac->data.data == NULL) { 465 krb5_pac_free(context, pac); 466 return ENOMEM; 467 } 468 memcpy(pac->data.data, ptr, len); 469 470 pac->data.length = len; 471 472 *ppac = pac; 473 474 return 0; 475 } 476 477 static krb5_error_code 478 k5_time_to_seconds_since_1970(krb5_int64 ntTime, krb5_timestamp *elapsedSeconds) 479 { 480 krb5_ui_8 abstime; 481 482 ntTime /= 10000000; 483 484 abstime = ntTime > 0 ? ntTime - NT_TIME_EPOCH : -ntTime; 485 486 if (abstime > KRB5_INT32_MAX) 487 return ERANGE; 488 489 *elapsedSeconds = abstime; 490 491 return 0; 492 } 493 494 static krb5_error_code 495 k5_seconds_since_1970_to_time(krb5_timestamp elapsedSeconds, krb5_ui_8 *ntTime) 496 { 497 *ntTime = elapsedSeconds; 498 499 if (elapsedSeconds > 0) 500 *ntTime += NT_TIME_EPOCH; 501 502 *ntTime *= 10000000; 503 504 return 0; 505 } 506 507 static krb5_error_code 508 k5_pac_validate_client(krb5_context context, 509 const krb5_pac pac, 510 krb5_timestamp authtime, 511 krb5_const_principal principal) 512 { 513 krb5_error_code ret; 514 krb5_data client_info; 515 char *pac_princname; 516 unsigned char *p; 517 krb5_timestamp pac_authtime; 518 krb5_ui_2 pac_princname_length; 519 krb5_int64 pac_nt_authtime; 520 krb5_principal pac_principal; 521 522 ret = k5_pac_locate_buffer(context, pac, PAC_CLIENT_INFO, &client_info); 523 if (ret != 0) 524 return ret; 525 526 if (client_info.length < PAC_CLIENT_INFO_LENGTH) 527 return ERANGE; 528 529 p = (unsigned char *)client_info.data; 530 pac_nt_authtime = load_64_le(p); 531 p += 8; 532 pac_princname_length = load_16_le(p); 533 p += 2; 534 535 ret = k5_time_to_seconds_since_1970(pac_nt_authtime, &pac_authtime); 536 if (ret != 0) 537 return ret; 538 539 if (client_info.length < PAC_CLIENT_INFO_LENGTH + pac_princname_length || 540 pac_princname_length % 2) 541 return ERANGE; 542 543 ret = krb5int_ucs2lecs_to_utf8s(p, (size_t)pac_princname_length / 2, &pac_princname, NULL); 544 if (ret != 0) 545 return ret; 546 547 ret = krb5_parse_name_flags(context, pac_princname, 0, &pac_principal); 548 if (ret != 0) { 549 free(pac_princname); 550 return ret; 551 } 552 553 free(pac_princname); 554 555 if (pac_authtime != authtime || 556 krb5_principal_compare(context, pac_principal, principal) == FALSE) 557 ret = KRB5KRB_AP_WRONG_PRINC; 558 559 krb5_free_principal(context, pac_principal); 560 561 return ret; 562 } 563 564 static krb5_error_code 565 k5_pac_zero_signature(krb5_context context, 566 const krb5_pac pac, 567 krb5_ui_4 type, 568 krb5_data *data) 569 { 570 PAC_INFO_BUFFER *buffer = NULL; 571 size_t i; 572 573 assert(type == PAC_SERVER_CHECKSUM || type == PAC_PRIVSVR_CHECKSUM); 574 assert(data->length >= pac->data.length); 575 576 for (i = 0; i < pac->pac->cBuffers; i++) { 577 if (pac->pac->Buffers[i].ulType == type) { 578 buffer = &pac->pac->Buffers[i]; 579 break; 580 } 581 } 582 583 if (buffer == NULL) 584 return ENOENT; 585 586 if (buffer->Offset + buffer->cbBufferSize > pac->data.length) 587 return ERANGE; 588 589 if (buffer->cbBufferSize < PAC_SIGNATURE_DATA_LENGTH) 590 return KRB5_BAD_MSIZE; 591 592 /* Zero out the data portion of the checksum only */ 593 memset(data->data + buffer->Offset + PAC_SIGNATURE_DATA_LENGTH, 594 0, 595 buffer->cbBufferSize - PAC_SIGNATURE_DATA_LENGTH); 596 597 return 0; 598 } 599 600 static krb5_error_code 601 k5_pac_verify_server_checksum(krb5_context context, 602 const krb5_pac pac, 603 const krb5_keyblock *server) 604 { 605 krb5_error_code ret; 606 krb5_data pac_data; /* PAC with zeroed checksums */ 607 krb5_checksum checksum; 608 krb5_data checksum_data; 609 krb5_boolean valid; 610 krb5_octet *p; 611 612 ret = k5_pac_locate_buffer(context, pac, PAC_SERVER_CHECKSUM, &checksum_data); 613 if (ret != 0) 614 return ret; 615 616 if (checksum_data.length < PAC_SIGNATURE_DATA_LENGTH) 617 return KRB5_BAD_MSIZE; 618 619 p = (krb5_octet *)checksum_data.data; 620 checksum.checksum_type = load_32_le(p); 621 checksum.length = checksum_data.length - PAC_SIGNATURE_DATA_LENGTH; 622 checksum.contents = p + PAC_SIGNATURE_DATA_LENGTH; 623 624 pac_data.length = pac->data.length; 625 pac_data.data = malloc(pac->data.length); 626 if (pac_data.data == NULL) 627 return ENOMEM; 628 629 memcpy(pac_data.data, pac->data.data, pac->data.length); 630 631 /* Zero out both checksum buffers */ 632 ret = k5_pac_zero_signature(context, pac, PAC_SERVER_CHECKSUM, &pac_data); 633 if (ret != 0) { 634 free(pac_data.data); 635 return ret; 636 } 637 638 ret = k5_pac_zero_signature(context, pac, PAC_PRIVSVR_CHECKSUM, &pac_data); 639 if (ret != 0) { 640 free(pac_data.data); 641 return ret; 642 } 643 644 ret = krb5_c_verify_checksum(context, server, KRB5_KEYUSAGE_APP_DATA_CKSUM, 645 &pac_data, &checksum, &valid); 646 if (ret != 0) { 647 free(pac_data.data); 648 return ret; 649 } 650 651 if (valid == FALSE) 652 ret = KRB5KRB_AP_ERR_BAD_INTEGRITY; 653 654 free(pac_data.data); /* SUNW17PACresync - mem leak fix */ 655 return ret; 656 } 657 658 static krb5_error_code 659 k5_pac_verify_kdc_checksum(krb5_context context, 660 const krb5_pac pac, 661 const krb5_keyblock *privsvr) 662 { 663 krb5_error_code ret; 664 krb5_data server_checksum, privsvr_checksum; 665 krb5_checksum checksum; 666 krb5_boolean valid; 667 krb5_octet *p; 668 669 ret = k5_pac_locate_buffer(context, pac, PAC_PRIVSVR_CHECKSUM, &privsvr_checksum); 670 if (ret != 0) 671 return ret; 672 673 if (privsvr_checksum.length < PAC_SIGNATURE_DATA_LENGTH) 674 return KRB5_BAD_MSIZE; 675 676 ret = k5_pac_locate_buffer(context, pac, PAC_SERVER_CHECKSUM, &server_checksum); 677 if (ret != 0) 678 return ret; 679 680 if (server_checksum.length < PAC_SIGNATURE_DATA_LENGTH) 681 return KRB5_BAD_MSIZE; 682 683 p = (krb5_octet *)privsvr_checksum.data; 684 checksum.checksum_type = load_32_le(p); 685 checksum.length = privsvr_checksum.length - PAC_SIGNATURE_DATA_LENGTH; 686 checksum.contents = p + PAC_SIGNATURE_DATA_LENGTH; 687 688 server_checksum.data += PAC_SIGNATURE_DATA_LENGTH; 689 server_checksum.length -= PAC_SIGNATURE_DATA_LENGTH; 690 691 ret = krb5_c_verify_checksum(context, privsvr, KRB5_KEYUSAGE_APP_DATA_CKSUM, 692 &server_checksum, &checksum, &valid); 693 if (ret != 0) 694 return ret; 695 696 if (valid == FALSE) 697 ret = KRB5KRB_AP_ERR_BAD_INTEGRITY; 698 699 return ret; 700 } 701 702 krb5_error_code KRB5_CALLCONV 703 krb5_pac_verify(krb5_context context, 704 const krb5_pac pac, 705 krb5_timestamp authtime, 706 krb5_const_principal principal, 707 const krb5_keyblock *server, 708 const krb5_keyblock *privsvr) 709 { 710 krb5_error_code ret; 711 712 if (server == NULL) 713 return EINVAL; 714 715 ret = k5_pac_verify_server_checksum(context, pac, server); 716 if (ret != 0) 717 return ret; 718 719 if (privsvr != NULL) { 720 ret = k5_pac_verify_kdc_checksum(context, pac, privsvr); 721 if (ret != 0) 722 return ret; 723 } 724 725 if (principal != NULL) { 726 ret = k5_pac_validate_client(context, pac, authtime, principal); 727 if (ret != 0) 728 return ret; 729 } 730 731 return 0; 732 } 733 734 static krb5_error_code 735 k5_insert_client_info(krb5_context context, 736 krb5_pac pac, 737 krb5_timestamp authtime, 738 krb5_const_principal principal) 739 { 740 krb5_error_code ret; 741 krb5_data client_info; 742 char *princ_name_utf8 = NULL; 743 unsigned char *princ_name_ucs2 = NULL, *p; 744 size_t princ_name_ucs2_len = 0; 745 krb5_ui_8 nt_authtime; 746 747 /* If we already have a CLIENT_INFO buffer, then just validate it */ 748 if (k5_pac_locate_buffer(context, pac, PAC_CLIENT_INFO, &client_info) == 0) { 749 return k5_pac_validate_client(context, pac, authtime, principal); 750 } 751 752 ret = krb5_unparse_name_flags(context, principal, 753 KRB5_PRINCIPAL_UNPARSE_NO_REALM, &princ_name_utf8); 754 if (ret != 0) 755 goto cleanup; 756 757 ret = krb5int_utf8s_to_ucs2les(princ_name_utf8, 758 &princ_name_ucs2, 759 &princ_name_ucs2_len); 760 if (ret != 0) 761 goto cleanup; 762 763 client_info.length = PAC_CLIENT_INFO_LENGTH + princ_name_ucs2_len; 764 client_info.data = NULL; 765 766 ret = k5_pac_add_buffer(context, pac, PAC_CLIENT_INFO, &client_info, TRUE, &client_info); 767 if (ret != 0) 768 goto cleanup; 769 770 p = (unsigned char *)client_info.data; 771 772 /* copy in authtime converted to a 64-bit NT time */ 773 k5_seconds_since_1970_to_time(authtime, &nt_authtime); 774 store_64_le(nt_authtime, p); 775 p += 8; 776 777 /* copy in number of UCS-2 characters in principal name */ 778 store_16_le(princ_name_ucs2_len, p); 779 p += 2; 780 781 /* copy in principal name */ 782 memcpy(p, princ_name_ucs2, princ_name_ucs2_len); 783 784 cleanup: 785 if (princ_name_utf8 != NULL) 786 free(princ_name_utf8); 787 if (princ_name_ucs2 != NULL) 788 free(princ_name_ucs2); 789 790 return ret; 791 } 792 793 static krb5_error_code 794 k5_insert_checksum(krb5_context context, 795 krb5_pac pac, 796 krb5_ui_4 type, 797 const krb5_keyblock *key, 798 krb5_cksumtype *cksumtype) 799 { 800 krb5_error_code ret; 801 size_t len; 802 krb5_data cksumdata; 803 804 ret = krb5int_c_mandatory_cksumtype(context, key->enctype, cksumtype); 805 if (ret != 0) 806 return ret; 807 808 ret = krb5_c_checksum_length(context, *cksumtype, &len); 809 if (ret != 0) 810 return ret; 811 812 ret = k5_pac_locate_buffer(context, pac, type, &cksumdata); 813 if (ret == 0) { 814 /* If we're resigning PAC, make sure we can fit checksum into existing buffer */ 815 if (cksumdata.length != PAC_SIGNATURE_DATA_LENGTH + len) 816 return ERANGE; 817 818 memset(cksumdata.data, 0, cksumdata.length); 819 } else { 820 /* Add a zero filled buffer */ 821 cksumdata.length = PAC_SIGNATURE_DATA_LENGTH + len; 822 cksumdata.data = NULL; 823 824 ret = k5_pac_add_buffer(context, pac, type, &cksumdata, TRUE, &cksumdata); 825 if (ret != 0) 826 return ret; 827 } 828 829 /* Encode checksum type into buffer */ 830 store_32_le((krb5_ui_4)*cksumtype, cksumdata.data); 831 832 return 0; 833 } 834 835 /* in-place encoding of PAC header */ 836 static krb5_error_code 837 k5_pac_encode_header(krb5_context context, krb5_pac pac) 838 { 839 size_t i; 840 unsigned char *p; 841 size_t header_len; 842 843 header_len = PACTYPE_LENGTH + (pac->pac->cBuffers * PAC_INFO_BUFFER_LENGTH); 844 assert(pac->data.length >= header_len); 845 846 p = (unsigned char *)pac->data.data; 847 848 store_32_le(pac->pac->cBuffers, p); 849 p += 4; 850 store_32_le(pac->pac->Version, p); 851 p += 4; 852 853 for (i = 0; i < pac->pac->cBuffers; i++) { 854 PAC_INFO_BUFFER *buffer = &pac->pac->Buffers[i]; 855 856 store_32_le(buffer->ulType, p); 857 p += 4; 858 store_32_le(buffer->cbBufferSize, p); 859 p += 4; 860 store_64_le(buffer->Offset, p); 861 p += 8; 862 863 assert((buffer->Offset % PAC_ALIGNMENT) == 0); 864 assert(buffer->Offset + buffer->cbBufferSize <= pac->data.length); 865 assert(buffer->Offset >= header_len); 866 867 if (buffer->Offset % PAC_ALIGNMENT || 868 buffer->Offset + buffer->cbBufferSize > pac->data.length || 869 buffer->Offset < header_len) 870 return ERANGE; 871 } 872 873 return 0; 874 } 875 876 877 #if 0 878 /* 879 * SUNW17PACresync 880 * We don't have the new MIT iov interfaces yet and don't need them yet. 881 * We'll need this for full 1.7 resync. 882 */ 883 krb5_error_code KRB5_CALLCONV 884 krb5int_pac_sign(krb5_context context, 885 krb5_pac pac, 886 krb5_timestamp authtime, 887 krb5_const_principal principal, 888 const krb5_keyblock *server_key, 889 const krb5_keyblock *privsvr_key, 890 krb5_data *data) 891 { 892 krb5_error_code ret; 893 krb5_data server_cksum, privsvr_cksum; 894 krb5_cksumtype server_cksumtype, privsvr_cksumtype; 895 krb5_crypto_iov iov[2]; 896 897 data->length = 0; 898 data->data = NULL; 899 900 if (principal != NULL) { 901 ret = k5_insert_client_info(context, pac, authtime, principal); 902 if (ret != 0) 903 return ret; 904 } 905 906 /* Create zeroed buffers for both checksums */ 907 ret = k5_insert_checksum(context, pac, PAC_SERVER_CHECKSUM, 908 server_key, &server_cksumtype); 909 if (ret != 0) 910 return ret; 911 912 ret = k5_insert_checksum(context, pac, PAC_PRIVSVR_CHECKSUM, 913 privsvr_key, &privsvr_cksumtype); 914 if (ret != 0) 915 return ret; 916 917 /* Now, encode the PAC header so that the checksums will include it */ 918 ret = k5_pac_encode_header(context, pac); 919 if (ret != 0) 920 return ret; 921 922 /* Generate the server checksum over the entire PAC */ 923 ret = k5_pac_locate_buffer(context, pac, PAC_SERVER_CHECKSUM, &server_cksum); 924 if (ret != 0) 925 return ret; 926 927 assert(server_cksum.length > PAC_SIGNATURE_DATA_LENGTH); 928 929 iov[0].flags = KRB5_CRYPTO_TYPE_DATA; 930 iov[0].data = pac->data; 931 932 iov[1].flags = KRB5_CRYPTO_TYPE_CHECKSUM; 933 iov[1].data.data = server_cksum.data + PAC_SIGNATURE_DATA_LENGTH; 934 iov[1].data.length = server_cksum.length - PAC_SIGNATURE_DATA_LENGTH; 935 936 ret = krb5_c_make_checksum_iov(context, server_cksumtype, 937 server_key, KRB5_KEYUSAGE_APP_DATA_CKSUM, 938 iov, sizeof(iov)/sizeof(iov[0])); 939 if (ret != 0) 940 return ret; 941 942 /* Generate the privsvr checksum over the server checksum buffer */ 943 ret = k5_pac_locate_buffer(context, pac, PAC_PRIVSVR_CHECKSUM, &privsvr_cksum); 944 if (ret != 0) 945 return ret; 946 947 assert(privsvr_cksum.length > PAC_SIGNATURE_DATA_LENGTH); 948 949 iov[0].flags = KRB5_CRYPTO_TYPE_DATA; 950 iov[0].data.data = server_cksum.data + PAC_SIGNATURE_DATA_LENGTH; 951 iov[0].data.length = server_cksum.length - PAC_SIGNATURE_DATA_LENGTH; 952 953 iov[1].flags = KRB5_CRYPTO_TYPE_CHECKSUM; 954 iov[1].data.data = privsvr_cksum.data + PAC_SIGNATURE_DATA_LENGTH; 955 iov[1].data.length = privsvr_cksum.length - PAC_SIGNATURE_DATA_LENGTH; 956 957 ret = krb5_c_make_checksum_iov(context, privsvr_cksumtype, 958 privsvr_key, KRB5_KEYUSAGE_APP_DATA_CKSUM, 959 iov, sizeof(iov)/sizeof(iov[0])); 960 if (ret != 0) 961 return ret; 962 963 data->data = malloc(pac->data.length); 964 if (data->data == NULL) 965 return ENOMEM; 966 967 data->length = pac->data.length; 968 969 memcpy(data->data, pac->data.data, pac->data.length); 970 memset(pac->data.data, 0, PACTYPE_LENGTH + (pac->pac->cBuffers * PAC_INFO_BUFFER_LENGTH)); 971 972 return 0; 973 } 974 #endif 975 976