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