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 "krb5_locl.h" 35 36 RCSID("$Id: pac.c 21934 2007-08-27 14:21:04Z lha $"); 37 38 struct PAC_INFO_BUFFER { 39 uint32_t type; 40 uint32_t buffersize; 41 uint32_t offset_hi; 42 uint32_t offset_lo; 43 }; 44 45 struct PACTYPE { 46 uint32_t numbuffers; 47 uint32_t version; 48 struct PAC_INFO_BUFFER buffers[1]; 49 }; 50 51 struct krb5_pac_data { 52 struct PACTYPE *pac; 53 krb5_data data; 54 struct PAC_INFO_BUFFER *server_checksum; 55 struct PAC_INFO_BUFFER *privsvr_checksum; 56 struct PAC_INFO_BUFFER *logon_name; 57 }; 58 59 #define PAC_ALIGNMENT 8 60 61 #define PACTYPE_SIZE 8 62 #define PAC_INFO_BUFFER_SIZE 16 63 64 #define PAC_SERVER_CHECKSUM 6 65 #define PAC_PRIVSVR_CHECKSUM 7 66 #define PAC_LOGON_NAME 10 67 #define PAC_CONSTRAINED_DELEGATION 11 68 69 #define CHECK(r,f,l) \ 70 do { \ 71 if (((r) = f ) != 0) { \ 72 krb5_clear_error_string(context); \ 73 goto l; \ 74 } \ 75 } while(0) 76 77 static const char zeros[PAC_ALIGNMENT] = { 0 }; 78 79 /* 80 * 81 */ 82 83 krb5_error_code 84 krb5_pac_parse(krb5_context context, const void *ptr, size_t len, 85 krb5_pac *pac) 86 { 87 krb5_error_code ret; 88 krb5_pac p; 89 krb5_storage *sp = NULL; 90 uint32_t i, tmp, tmp2, header_end; 91 92 p = calloc(1, sizeof(*p)); 93 if (p == NULL) { 94 ret = ENOMEM; 95 krb5_set_error_string(context, "out of memory"); 96 goto out; 97 } 98 99 sp = krb5_storage_from_readonly_mem(ptr, len); 100 if (sp == NULL) { 101 ret = ENOMEM; 102 krb5_set_error_string(context, "out of memory"); 103 goto out; 104 } 105 krb5_storage_set_flags(sp, KRB5_STORAGE_BYTEORDER_LE); 106 107 CHECK(ret, krb5_ret_uint32(sp, &tmp), out); 108 CHECK(ret, krb5_ret_uint32(sp, &tmp2), out); 109 if (tmp < 1) { 110 krb5_set_error_string(context, "PAC have too few buffer"); 111 ret = EINVAL; /* Too few buffers */ 112 goto out; 113 } 114 if (tmp2 != 0) { 115 krb5_set_error_string(context, "PAC have wrong version"); 116 ret = EINVAL; /* Wrong version */ 117 goto out; 118 } 119 120 p->pac = calloc(1, 121 sizeof(*p->pac) + (sizeof(p->pac->buffers[0]) * (tmp - 1))); 122 if (p->pac == NULL) { 123 krb5_set_error_string(context, "out of memory"); 124 ret = ENOMEM; 125 goto out; 126 } 127 128 p->pac->numbuffers = tmp; 129 p->pac->version = tmp2; 130 131 header_end = PACTYPE_SIZE + (PAC_INFO_BUFFER_SIZE * p->pac->numbuffers); 132 if (header_end > len) { 133 ret = EINVAL; 134 goto out; 135 } 136 137 for (i = 0; i < p->pac->numbuffers; i++) { 138 CHECK(ret, krb5_ret_uint32(sp, &p->pac->buffers[i].type), out); 139 CHECK(ret, krb5_ret_uint32(sp, &p->pac->buffers[i].buffersize), out); 140 CHECK(ret, krb5_ret_uint32(sp, &p->pac->buffers[i].offset_lo), out); 141 CHECK(ret, krb5_ret_uint32(sp, &p->pac->buffers[i].offset_hi), out); 142 143 /* consistency checks */ 144 if (p->pac->buffers[i].offset_lo & (PAC_ALIGNMENT - 1)) { 145 krb5_set_error_string(context, "PAC out of allignment"); 146 ret = EINVAL; 147 goto out; 148 } 149 if (p->pac->buffers[i].offset_hi) { 150 krb5_set_error_string(context, "PAC high offset set"); 151 ret = EINVAL; 152 goto out; 153 } 154 if (p->pac->buffers[i].offset_lo > len) { 155 krb5_set_error_string(context, "PAC offset off end"); 156 ret = EINVAL; 157 goto out; 158 } 159 if (p->pac->buffers[i].offset_lo < header_end) { 160 krb5_set_error_string(context, "PAC offset inside header: %d %d", 161 p->pac->buffers[i].offset_lo, header_end); 162 ret = EINVAL; 163 goto out; 164 } 165 if (p->pac->buffers[i].buffersize > len - p->pac->buffers[i].offset_lo){ 166 krb5_set_error_string(context, "PAC length off end"); 167 ret = EINVAL; 168 goto out; 169 } 170 171 /* let save pointer to data we need later */ 172 if (p->pac->buffers[i].type == PAC_SERVER_CHECKSUM) { 173 if (p->server_checksum) { 174 krb5_set_error_string(context, "PAC have two server checksums"); 175 ret = EINVAL; 176 goto out; 177 } 178 p->server_checksum = &p->pac->buffers[i]; 179 } else if (p->pac->buffers[i].type == PAC_PRIVSVR_CHECKSUM) { 180 if (p->privsvr_checksum) { 181 krb5_set_error_string(context, "PAC have two KDC checksums"); 182 ret = EINVAL; 183 goto out; 184 } 185 p->privsvr_checksum = &p->pac->buffers[i]; 186 } else if (p->pac->buffers[i].type == PAC_LOGON_NAME) { 187 if (p->logon_name) { 188 krb5_set_error_string(context, "PAC have two logon names"); 189 ret = EINVAL; 190 goto out; 191 } 192 p->logon_name = &p->pac->buffers[i]; 193 } 194 } 195 196 ret = krb5_data_copy(&p->data, ptr, len); 197 if (ret) 198 goto out; 199 200 krb5_storage_free(sp); 201 202 *pac = p; 203 return 0; 204 205 out: 206 if (sp) 207 krb5_storage_free(sp); 208 if (p) { 209 if (p->pac) 210 free(p->pac); 211 free(p); 212 } 213 *pac = NULL; 214 215 return ret; 216 } 217 218 krb5_error_code 219 krb5_pac_init(krb5_context context, krb5_pac *pac) 220 { 221 krb5_error_code ret; 222 krb5_pac p; 223 224 p = calloc(1, sizeof(*p)); 225 if (p == NULL) { 226 krb5_set_error_string(context, "out of memory"); 227 return ENOMEM; 228 } 229 230 p->pac = calloc(1, sizeof(*p->pac)); 231 if (p->pac == NULL) { 232 free(p); 233 krb5_set_error_string(context, "out of memory"); 234 return ENOMEM; 235 } 236 237 ret = krb5_data_alloc(&p->data, PACTYPE_SIZE); 238 if (ret) { 239 free (p->pac); 240 free(p); 241 krb5_set_error_string(context, "out of memory"); 242 return ret; 243 } 244 245 246 *pac = p; 247 return 0; 248 } 249 250 krb5_error_code 251 krb5_pac_add_buffer(krb5_context context, krb5_pac p, 252 uint32_t type, const krb5_data *data) 253 { 254 krb5_error_code ret; 255 void *ptr; 256 size_t len, offset, header_end, old_end; 257 uint32_t i; 258 259 len = p->pac->numbuffers; 260 261 ptr = realloc(p->pac, 262 sizeof(*p->pac) + (sizeof(p->pac->buffers[0]) * len)); 263 if (ptr == NULL) { 264 krb5_set_error_string(context, "out of memory"); 265 return ENOMEM; 266 } 267 p->pac = ptr; 268 269 for (i = 0; i < len; i++) 270 p->pac->buffers[i].offset_lo += PAC_INFO_BUFFER_SIZE; 271 272 offset = p->data.length + PAC_INFO_BUFFER_SIZE; 273 274 p->pac->buffers[len].type = type; 275 p->pac->buffers[len].buffersize = data->length; 276 p->pac->buffers[len].offset_lo = offset; 277 p->pac->buffers[len].offset_hi = 0; 278 279 old_end = p->data.length; 280 len = p->data.length + data->length + PAC_INFO_BUFFER_SIZE; 281 if (len < p->data.length) { 282 krb5_set_error_string(context, "integer overrun"); 283 return EINVAL; 284 } 285 286 /* align to PAC_ALIGNMENT */ 287 len = ((len + PAC_ALIGNMENT - 1) / PAC_ALIGNMENT) * PAC_ALIGNMENT; 288 289 ret = krb5_data_realloc(&p->data, len); 290 if (ret) { 291 krb5_set_error_string(context, "out of memory"); 292 return ret; 293 } 294 295 /* 296 * make place for new PAC INFO BUFFER header 297 */ 298 header_end = PACTYPE_SIZE + (PAC_INFO_BUFFER_SIZE * p->pac->numbuffers); 299 memmove((unsigned char *)p->data.data + header_end + PAC_INFO_BUFFER_SIZE, 300 (unsigned char *)p->data.data + header_end , 301 old_end - header_end); 302 memset((unsigned char *)p->data.data + header_end, 0, PAC_INFO_BUFFER_SIZE); 303 304 /* 305 * copy in new data part 306 */ 307 308 memcpy((unsigned char *)p->data.data + offset, 309 data->data, data->length); 310 memset((unsigned char *)p->data.data + offset + data->length, 311 0, p->data.length - offset - data->length); 312 313 p->pac->numbuffers += 1; 314 315 return 0; 316 } 317 318 krb5_error_code 319 krb5_pac_get_buffer(krb5_context context, krb5_pac p, 320 uint32_t type, krb5_data *data) 321 { 322 krb5_error_code ret; 323 uint32_t i; 324 325 /* 326 * Hide the checksums from external consumers 327 */ 328 329 if (type == PAC_PRIVSVR_CHECKSUM || type == PAC_SERVER_CHECKSUM) { 330 ret = krb5_data_alloc(data, 16); 331 if (ret) { 332 krb5_set_error_string(context, "out of memory"); 333 return ret; 334 } 335 memset(data->data, 0, data->length); 336 return 0; 337 } 338 339 for (i = 0; i < p->pac->numbuffers; i++) { 340 size_t len = p->pac->buffers[i].buffersize; 341 size_t offset = p->pac->buffers[i].offset_lo; 342 343 if (p->pac->buffers[i].type != type) 344 continue; 345 346 ret = krb5_data_copy(data, (unsigned char *)p->data.data + offset, len); 347 if (ret) { 348 krb5_set_error_string(context, "Out of memory"); 349 return ret; 350 } 351 return 0; 352 } 353 krb5_set_error_string(context, "No PAC buffer of type %lu was found", 354 (unsigned long)type); 355 return ENOENT; 356 } 357 358 /* 359 * 360 */ 361 362 krb5_error_code 363 krb5_pac_get_types(krb5_context context, 364 krb5_pac p, 365 size_t *len, 366 uint32_t **types) 367 { 368 size_t i; 369 370 *types = calloc(p->pac->numbuffers, sizeof(*types)); 371 if (*types == NULL) { 372 *len = 0; 373 krb5_set_error_string(context, "out of memory"); 374 return ENOMEM; 375 } 376 for (i = 0; i < p->pac->numbuffers; i++) 377 (*types)[i] = p->pac->buffers[i].type; 378 *len = p->pac->numbuffers; 379 380 return 0; 381 } 382 383 /* 384 * 385 */ 386 387 void 388 krb5_pac_free(krb5_context context, krb5_pac pac) 389 { 390 krb5_data_free(&pac->data); 391 free(pac->pac); 392 free(pac); 393 } 394 395 /* 396 * 397 */ 398 399 static krb5_error_code 400 verify_checksum(krb5_context context, 401 const struct PAC_INFO_BUFFER *sig, 402 const krb5_data *data, 403 void *ptr, size_t len, 404 const krb5_keyblock *key) 405 { 406 krb5_crypto crypto = NULL; 407 krb5_storage *sp = NULL; 408 uint32_t type; 409 krb5_error_code ret; 410 Checksum cksum; 411 412 memset(&cksum, 0, sizeof(cksum)); 413 414 sp = krb5_storage_from_mem((char *)data->data + sig->offset_lo, 415 sig->buffersize); 416 if (sp == NULL) { 417 krb5_set_error_string(context, "out of memory"); 418 return ENOMEM; 419 } 420 krb5_storage_set_flags(sp, KRB5_STORAGE_BYTEORDER_LE); 421 422 CHECK(ret, krb5_ret_uint32(sp, &type), out); 423 cksum.cksumtype = type; 424 cksum.checksum.length = 425 sig->buffersize - krb5_storage_seek(sp, 0, SEEK_CUR); 426 cksum.checksum.data = malloc(cksum.checksum.length); 427 if (cksum.checksum.data == NULL) { 428 krb5_set_error_string(context, "out of memory"); 429 ret = ENOMEM; 430 goto out; 431 } 432 ret = krb5_storage_read(sp, cksum.checksum.data, cksum.checksum.length); 433 if (ret != cksum.checksum.length) { 434 krb5_set_error_string(context, "PAC checksum missing checksum"); 435 ret = EINVAL; 436 goto out; 437 } 438 439 if (!krb5_checksum_is_keyed(context, cksum.cksumtype)) { 440 krb5_set_error_string (context, "Checksum type %d not keyed", 441 cksum.cksumtype); 442 ret = EINVAL; 443 goto out; 444 } 445 446 ret = krb5_crypto_init(context, key, 0, &crypto); 447 if (ret) 448 goto out; 449 450 ret = krb5_verify_checksum(context, crypto, KRB5_KU_OTHER_CKSUM, 451 ptr, len, &cksum); 452 free(cksum.checksum.data); 453 krb5_crypto_destroy(context, crypto); 454 krb5_storage_free(sp); 455 456 return ret; 457 458 out: 459 if (cksum.checksum.data) 460 free(cksum.checksum.data); 461 if (sp) 462 krb5_storage_free(sp); 463 if (crypto) 464 krb5_crypto_destroy(context, crypto); 465 return ret; 466 } 467 468 static krb5_error_code 469 create_checksum(krb5_context context, 470 const krb5_keyblock *key, 471 void *data, size_t datalen, 472 void *sig, size_t siglen) 473 { 474 krb5_crypto crypto = NULL; 475 krb5_error_code ret; 476 Checksum cksum; 477 478 ret = krb5_crypto_init(context, key, 0, &crypto); 479 if (ret) 480 return ret; 481 482 ret = krb5_create_checksum(context, crypto, KRB5_KU_OTHER_CKSUM, 0, 483 data, datalen, &cksum); 484 krb5_crypto_destroy(context, crypto); 485 if (ret) 486 return ret; 487 488 if (cksum.checksum.length != siglen) { 489 krb5_set_error_string(context, "pac checksum wrong length"); 490 free_Checksum(&cksum); 491 return EINVAL; 492 } 493 494 memcpy(sig, cksum.checksum.data, siglen); 495 free_Checksum(&cksum); 496 497 return 0; 498 } 499 500 501 /* 502 * 503 */ 504 505 #define NTTIME_EPOCH 0x019DB1DED53E8000LL 506 507 static uint64_t 508 unix2nttime(time_t unix_time) 509 { 510 long long wt; 511 wt = unix_time * (uint64_t)10000000 + (uint64_t)NTTIME_EPOCH; 512 return wt; 513 } 514 515 static krb5_error_code 516 verify_logonname(krb5_context context, 517 const struct PAC_INFO_BUFFER *logon_name, 518 const krb5_data *data, 519 time_t authtime, 520 krb5_const_principal principal) 521 { 522 krb5_error_code ret; 523 krb5_principal p2; 524 uint32_t time1, time2; 525 krb5_storage *sp; 526 uint16_t len; 527 char *s; 528 529 sp = krb5_storage_from_readonly_mem((const char *)data->data + logon_name->offset_lo, 530 logon_name->buffersize); 531 if (sp == NULL) { 532 krb5_set_error_string(context, "Out of memory"); 533 return ENOMEM; 534 } 535 536 krb5_storage_set_flags(sp, KRB5_STORAGE_BYTEORDER_LE); 537 538 CHECK(ret, krb5_ret_uint32(sp, &time1), out); 539 CHECK(ret, krb5_ret_uint32(sp, &time2), out); 540 541 { 542 uint64_t t1, t2; 543 t1 = unix2nttime(authtime); 544 t2 = ((uint64_t)time2 << 32) | time1; 545 if (t1 != t2) { 546 krb5_storage_free(sp); 547 krb5_set_error_string(context, "PAC timestamp mismatch"); 548 return EINVAL; 549 } 550 } 551 CHECK(ret, krb5_ret_uint16(sp, &len), out); 552 if (len == 0) { 553 krb5_storage_free(sp); 554 krb5_set_error_string(context, "PAC logon name length missing"); 555 return EINVAL; 556 } 557 558 s = malloc(len); 559 if (s == NULL) { 560 krb5_storage_free(sp); 561 krb5_set_error_string(context, "Out of memory"); 562 return ENOMEM; 563 } 564 ret = krb5_storage_read(sp, s, len); 565 if (ret != len) { 566 krb5_storage_free(sp); 567 krb5_set_error_string(context, "Failed to read pac logon name"); 568 return EINVAL; 569 } 570 krb5_storage_free(sp); 571 #if 1 /* cheat for now */ 572 { 573 size_t i; 574 575 if (len & 1) { 576 krb5_set_error_string(context, "PAC logon name malformed"); 577 return EINVAL; 578 } 579 580 for (i = 0; i < len / 2; i++) { 581 if (s[(i * 2) + 1]) { 582 krb5_set_error_string(context, "PAC logon name not ASCII"); 583 return EINVAL; 584 } 585 s[i] = s[i * 2]; 586 } 587 s[i] = '\0'; 588 } 589 #else 590 { 591 uint16_t *ucs2; 592 ssize_t ucs2len; 593 size_t u8len; 594 595 ucs2 = malloc(sizeof(ucs2[0]) * len / 2); 596 if (ucs2) 597 abort(); 598 ucs2len = wind_ucs2read(s, len / 2, ucs2); 599 free(s); 600 if (len < 0) 601 return -1; 602 ret = wind_ucs2toutf8(ucs2, ucs2len, NULL, &u8len); 603 if (ret < 0) 604 abort(); 605 s = malloc(u8len + 1); 606 if (s == NULL) 607 abort(); 608 wind_ucs2toutf8(ucs2, ucs2len, s, &u8len); 609 free(ucs2); 610 } 611 #endif 612 ret = krb5_parse_name_flags(context, s, KRB5_PRINCIPAL_PARSE_NO_REALM, &p2); 613 free(s); 614 if (ret) 615 return ret; 616 617 if (krb5_principal_compare_any_realm(context, principal, p2) != TRUE) { 618 krb5_set_error_string(context, "PAC logon name mismatch"); 619 ret = EINVAL; 620 } 621 krb5_free_principal(context, p2); 622 return ret; 623 out: 624 return ret; 625 } 626 627 /* 628 * 629 */ 630 631 static krb5_error_code 632 build_logon_name(krb5_context context, 633 time_t authtime, 634 krb5_const_principal principal, 635 krb5_data *logon) 636 { 637 krb5_error_code ret; 638 krb5_storage *sp; 639 uint64_t t; 640 char *s, *s2; 641 size_t i, len; 642 643 t = unix2nttime(authtime); 644 645 krb5_data_zero(logon); 646 647 sp = krb5_storage_emem(); 648 if (sp == NULL) { 649 krb5_set_error_string(context, "out of memory"); 650 return ENOMEM; 651 } 652 krb5_storage_set_flags(sp, KRB5_STORAGE_BYTEORDER_LE); 653 654 CHECK(ret, krb5_store_uint32(sp, t & 0xffffffff), out); 655 CHECK(ret, krb5_store_uint32(sp, t >> 32), out); 656 657 ret = krb5_unparse_name_flags(context, principal, 658 KRB5_PRINCIPAL_UNPARSE_NO_REALM, &s); 659 if (ret) 660 goto out; 661 662 len = strlen(s); 663 664 CHECK(ret, krb5_store_uint16(sp, len * 2), out); 665 666 #if 1 /* cheat for now */ 667 s2 = malloc(len * 2); 668 if (s2 == NULL) { 669 ret = ENOMEM; 670 free(s); 671 goto out; 672 } 673 for (i = 0; i < len; i++) { 674 s2[i * 2] = s[i]; 675 s2[i * 2 + 1] = 0; 676 } 677 free(s); 678 #else 679 /* write libwind code here */ 680 #endif 681 682 ret = krb5_storage_write(sp, s2, len * 2); 683 free(s2); 684 if (ret != len * 2) { 685 ret = ENOMEM; 686 goto out; 687 } 688 ret = krb5_storage_to_data(sp, logon); 689 if (ret) 690 goto out; 691 krb5_storage_free(sp); 692 693 return 0; 694 out: 695 krb5_storage_free(sp); 696 return ret; 697 } 698 699 700 /* 701 * 702 */ 703 704 krb5_error_code 705 krb5_pac_verify(krb5_context context, 706 const krb5_pac pac, 707 time_t authtime, 708 krb5_const_principal principal, 709 const krb5_keyblock *server, 710 const krb5_keyblock *privsvr) 711 { 712 krb5_error_code ret; 713 714 if (pac->server_checksum == NULL) { 715 krb5_set_error_string(context, "PAC missing server checksum"); 716 return EINVAL; 717 } 718 if (pac->privsvr_checksum == NULL) { 719 krb5_set_error_string(context, "PAC missing kdc checksum"); 720 return EINVAL; 721 } 722 if (pac->logon_name == NULL) { 723 krb5_set_error_string(context, "PAC missing logon name"); 724 return EINVAL; 725 } 726 727 ret = verify_logonname(context, 728 pac->logon_name, 729 &pac->data, 730 authtime, 731 principal); 732 if (ret) 733 return ret; 734 735 /* 736 * in the service case, clean out data option of the privsvr and 737 * server checksum before checking the checksum. 738 */ 739 { 740 krb5_data *copy; 741 742 ret = krb5_copy_data(context, &pac->data, ©); 743 if (ret) 744 return ret; 745 746 if (pac->server_checksum->buffersize < 4) 747 return EINVAL; 748 if (pac->privsvr_checksum->buffersize < 4) 749 return EINVAL; 750 751 memset((char *)copy->data + pac->server_checksum->offset_lo + 4, 752 0, 753 pac->server_checksum->buffersize - 4); 754 755 memset((char *)copy->data + pac->privsvr_checksum->offset_lo + 4, 756 0, 757 pac->privsvr_checksum->buffersize - 4); 758 759 ret = verify_checksum(context, 760 pac->server_checksum, 761 &pac->data, 762 copy->data, 763 copy->length, 764 server); 765 krb5_free_data(context, copy); 766 if (ret) 767 return ret; 768 } 769 if (privsvr) { 770 ret = verify_checksum(context, 771 pac->privsvr_checksum, 772 &pac->data, 773 (char *)pac->data.data 774 + pac->server_checksum->offset_lo + 4, 775 pac->server_checksum->buffersize - 4, 776 privsvr); 777 if (ret) 778 return ret; 779 } 780 781 return 0; 782 } 783 784 /* 785 * 786 */ 787 788 static krb5_error_code 789 fill_zeros(krb5_context context, krb5_storage *sp, size_t len) 790 { 791 ssize_t sret; 792 size_t l; 793 794 while (len) { 795 l = len; 796 if (l > sizeof(zeros)) 797 l = sizeof(zeros); 798 sret = krb5_storage_write(sp, zeros, l); 799 if (sret <= 0) { 800 krb5_set_error_string(context, "out of memory"); 801 return ENOMEM; 802 } 803 len -= sret; 804 } 805 return 0; 806 } 807 808 static krb5_error_code 809 pac_checksum(krb5_context context, 810 const krb5_keyblock *key, 811 uint32_t *cksumtype, 812 size_t *cksumsize) 813 { 814 krb5_cksumtype cktype; 815 krb5_error_code ret; 816 krb5_crypto crypto = NULL; 817 818 ret = krb5_crypto_init(context, key, 0, &crypto); 819 if (ret) 820 return ret; 821 822 ret = krb5_crypto_get_checksum_type(context, crypto, &cktype); 823 ret = krb5_crypto_destroy(context, crypto); 824 if (ret) 825 return ret; 826 827 if (krb5_checksum_is_keyed(context, cktype) == FALSE) { 828 krb5_set_error_string(context, "PAC checksum type is not keyed"); 829 return EINVAL; 830 } 831 832 ret = krb5_checksumsize(context, cktype, cksumsize); 833 if (ret) 834 return ret; 835 836 *cksumtype = (uint32_t)cktype; 837 838 return 0; 839 } 840 841 krb5_error_code 842 _krb5_pac_sign(krb5_context context, 843 krb5_pac p, 844 time_t authtime, 845 krb5_principal principal, 846 const krb5_keyblock *server_key, 847 const krb5_keyblock *priv_key, 848 krb5_data *data) 849 { 850 krb5_error_code ret; 851 krb5_storage *sp = NULL, *spdata = NULL; 852 uint32_t end; 853 size_t server_size, priv_size; 854 uint32_t server_offset = 0, priv_offset = 0; 855 uint32_t server_cksumtype = 0, priv_cksumtype = 0; 856 int i, num = 0; 857 krb5_data logon, d; 858 859 krb5_data_zero(&logon); 860 861 if (p->logon_name == NULL) 862 num++; 863 if (p->server_checksum == NULL) 864 num++; 865 if (p->privsvr_checksum == NULL) 866 num++; 867 868 if (num) { 869 void *ptr; 870 871 ptr = realloc(p->pac, sizeof(*p->pac) + (sizeof(p->pac->buffers[0]) * (p->pac->numbuffers + num - 1))); 872 if (ptr == NULL) { 873 krb5_set_error_string(context, "out of memory"); 874 return ENOMEM; 875 } 876 p->pac = ptr; 877 878 if (p->logon_name == NULL) { 879 p->logon_name = &p->pac->buffers[p->pac->numbuffers++]; 880 memset(p->logon_name, 0, sizeof(*p->logon_name)); 881 p->logon_name->type = PAC_LOGON_NAME; 882 } 883 if (p->server_checksum == NULL) { 884 p->server_checksum = &p->pac->buffers[p->pac->numbuffers++]; 885 memset(p->server_checksum, 0, sizeof(*p->server_checksum)); 886 p->server_checksum->type = PAC_SERVER_CHECKSUM; 887 } 888 if (p->privsvr_checksum == NULL) { 889 p->privsvr_checksum = &p->pac->buffers[p->pac->numbuffers++]; 890 memset(p->privsvr_checksum, 0, sizeof(*p->privsvr_checksum)); 891 p->privsvr_checksum->type = PAC_PRIVSVR_CHECKSUM; 892 } 893 } 894 895 /* Calculate LOGON NAME */ 896 ret = build_logon_name(context, authtime, principal, &logon); 897 if (ret) 898 goto out; 899 900 /* Set lengths for checksum */ 901 ret = pac_checksum(context, server_key, &server_cksumtype, &server_size); 902 if (ret) 903 goto out; 904 ret = pac_checksum(context, priv_key, &priv_cksumtype, &priv_size); 905 if (ret) 906 goto out; 907 908 /* Encode PAC */ 909 sp = krb5_storage_emem(); 910 if (sp == NULL) { 911 krb5_set_error_string(context, "out of memory"); 912 return ENOMEM; 913 } 914 krb5_storage_set_flags(sp, KRB5_STORAGE_BYTEORDER_LE); 915 916 spdata = krb5_storage_emem(); 917 if (spdata == NULL) { 918 krb5_storage_free(sp); 919 krb5_set_error_string(context, "out of memory"); 920 return ENOMEM; 921 } 922 krb5_storage_set_flags(spdata, KRB5_STORAGE_BYTEORDER_LE); 923 924 CHECK(ret, krb5_store_uint32(sp, p->pac->numbuffers), out); 925 CHECK(ret, krb5_store_uint32(sp, p->pac->version), out); 926 927 end = PACTYPE_SIZE + (PAC_INFO_BUFFER_SIZE * p->pac->numbuffers); 928 929 for (i = 0; i < p->pac->numbuffers; i++) { 930 uint32_t len; 931 size_t sret; 932 void *ptr = NULL; 933 934 /* store data */ 935 936 if (p->pac->buffers[i].type == PAC_SERVER_CHECKSUM) { 937 len = server_size + 4; 938 server_offset = end + 4; 939 CHECK(ret, krb5_store_uint32(spdata, server_cksumtype), out); 940 CHECK(ret, fill_zeros(context, spdata, server_size), out); 941 } else if (p->pac->buffers[i].type == PAC_PRIVSVR_CHECKSUM) { 942 len = priv_size + 4; 943 priv_offset = end + 4; 944 CHECK(ret, krb5_store_uint32(spdata, priv_cksumtype), out); 945 CHECK(ret, fill_zeros(context, spdata, priv_size), out); 946 } else if (p->pac->buffers[i].type == PAC_LOGON_NAME) { 947 len = krb5_storage_write(spdata, logon.data, logon.length); 948 if (logon.length != len) { 949 ret = EINVAL; 950 goto out; 951 } 952 } else { 953 len = p->pac->buffers[i].buffersize; 954 ptr = (char *)p->data.data + p->pac->buffers[i].offset_lo; 955 956 sret = krb5_storage_write(spdata, ptr, len); 957 if (sret != len) { 958 krb5_set_error_string(context, "out of memory"); 959 ret = ENOMEM; 960 goto out; 961 } 962 /* XXX if not aligned, fill_zeros */ 963 } 964 965 /* write header */ 966 CHECK(ret, krb5_store_uint32(sp, p->pac->buffers[i].type), out); 967 CHECK(ret, krb5_store_uint32(sp, len), out); 968 CHECK(ret, krb5_store_uint32(sp, end), out); 969 CHECK(ret, krb5_store_uint32(sp, 0), out); 970 971 /* advance data endpointer and align */ 972 { 973 int32_t e; 974 975 end += len; 976 e = ((end + PAC_ALIGNMENT - 1) / PAC_ALIGNMENT) * PAC_ALIGNMENT; 977 if (end != e) { 978 CHECK(ret, fill_zeros(context, spdata, e - end), out); 979 } 980 end = e; 981 } 982 983 } 984 985 /* assert (server_offset != 0 && priv_offset != 0); */ 986 987 /* export PAC */ 988 ret = krb5_storage_to_data(spdata, &d); 989 if (ret) { 990 krb5_set_error_string(context, "out of memory"); 991 goto out; 992 } 993 ret = krb5_storage_write(sp, d.data, d.length); 994 if (ret != d.length) { 995 krb5_data_free(&d); 996 krb5_set_error_string(context, "out of memory"); 997 ret = ENOMEM; 998 goto out; 999 } 1000 krb5_data_free(&d); 1001 1002 ret = krb5_storage_to_data(sp, &d); 1003 if (ret) { 1004 krb5_set_error_string(context, "out of memory"); 1005 goto out; 1006 } 1007 1008 /* sign */ 1009 1010 ret = create_checksum(context, server_key, 1011 d.data, d.length, 1012 (char *)d.data + server_offset, server_size); 1013 if (ret) { 1014 krb5_data_free(&d); 1015 goto out; 1016 } 1017 1018 ret = create_checksum(context, priv_key, 1019 (char *)d.data + server_offset, server_size, 1020 (char *)d.data + priv_offset, priv_size); 1021 if (ret) { 1022 krb5_data_free(&d); 1023 goto out; 1024 } 1025 1026 /* done */ 1027 *data = d; 1028 1029 krb5_data_free(&logon); 1030 krb5_storage_free(sp); 1031 krb5_storage_free(spdata); 1032 1033 return 0; 1034 out: 1035 krb5_data_free(&logon); 1036 if (sp) 1037 krb5_storage_free(sp); 1038 if (spdata) 1039 krb5_storage_free(spdata); 1040 return ret; 1041 } 1042