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