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