1 /* 2 * Copyright (c) 1997 - 2005 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: keytab_file.c 17457 2006-05-05 12:36:57Z lha $"); 37 38 #define KRB5_KT_VNO_1 1 39 #define KRB5_KT_VNO_2 2 40 #define KRB5_KT_VNO KRB5_KT_VNO_2 41 42 #define KRB5_KT_FL_JAVA 1 43 44 45 /* file operations -------------------------------------------- */ 46 47 struct fkt_data { 48 char *filename; 49 int flags; 50 }; 51 52 static krb5_error_code 53 krb5_kt_ret_data(krb5_context context, 54 krb5_storage *sp, 55 krb5_data *data) 56 { 57 int ret; 58 int16_t size; 59 ret = krb5_ret_int16(sp, &size); 60 if(ret) 61 return ret; 62 data->length = size; 63 data->data = malloc(size); 64 if (data->data == NULL) { 65 krb5_set_error_string (context, "malloc: out of memory"); 66 return ENOMEM; 67 } 68 ret = krb5_storage_read(sp, data->data, size); 69 if(ret != size) 70 return (ret < 0)? errno : KRB5_KT_END; 71 return 0; 72 } 73 74 static krb5_error_code 75 krb5_kt_ret_string(krb5_context context, 76 krb5_storage *sp, 77 heim_general_string *data) 78 { 79 int ret; 80 int16_t size; 81 ret = krb5_ret_int16(sp, &size); 82 if(ret) 83 return ret; 84 *data = malloc(size + 1); 85 if (*data == NULL) { 86 krb5_set_error_string (context, "malloc: out of memory"); 87 return ENOMEM; 88 } 89 ret = krb5_storage_read(sp, *data, size); 90 (*data)[size] = '\0'; 91 if(ret != size) 92 return (ret < 0)? errno : KRB5_KT_END; 93 return 0; 94 } 95 96 static krb5_error_code 97 krb5_kt_store_data(krb5_context context, 98 krb5_storage *sp, 99 krb5_data data) 100 { 101 int ret; 102 ret = krb5_store_int16(sp, data.length); 103 if(ret < 0) 104 return ret; 105 ret = krb5_storage_write(sp, data.data, data.length); 106 if(ret != data.length){ 107 if(ret < 0) 108 return errno; 109 return KRB5_KT_END; 110 } 111 return 0; 112 } 113 114 static krb5_error_code 115 krb5_kt_store_string(krb5_storage *sp, 116 heim_general_string data) 117 { 118 int ret; 119 size_t len = strlen(data); 120 ret = krb5_store_int16(sp, len); 121 if(ret < 0) 122 return ret; 123 ret = krb5_storage_write(sp, data, len); 124 if(ret != len){ 125 if(ret < 0) 126 return errno; 127 return KRB5_KT_END; 128 } 129 return 0; 130 } 131 132 static krb5_error_code 133 krb5_kt_ret_keyblock(krb5_context context, krb5_storage *sp, krb5_keyblock *p) 134 { 135 int ret; 136 int16_t tmp; 137 138 ret = krb5_ret_int16(sp, &tmp); /* keytype + etype */ 139 if(ret) return ret; 140 p->keytype = tmp; 141 ret = krb5_kt_ret_data(context, sp, &p->keyvalue); 142 return ret; 143 } 144 145 static krb5_error_code 146 krb5_kt_store_keyblock(krb5_context context, 147 krb5_storage *sp, 148 krb5_keyblock *p) 149 { 150 int ret; 151 152 ret = krb5_store_int16(sp, p->keytype); /* keytype + etype */ 153 if(ret) return ret; 154 ret = krb5_kt_store_data(context, sp, p->keyvalue); 155 return ret; 156 } 157 158 159 static krb5_error_code 160 krb5_kt_ret_principal(krb5_context context, 161 krb5_storage *sp, 162 krb5_principal *princ) 163 { 164 int i; 165 int ret; 166 krb5_principal p; 167 int16_t len; 168 169 ALLOC(p, 1); 170 if(p == NULL) { 171 krb5_set_error_string (context, "malloc: out of memory"); 172 return ENOMEM; 173 } 174 175 ret = krb5_ret_int16(sp, &len); 176 if(ret) { 177 krb5_set_error_string(context, 178 "Failed decoding length of keytab principal"); 179 goto out; 180 } 181 if(krb5_storage_is_flags(sp, KRB5_STORAGE_PRINCIPAL_WRONG_NUM_COMPONENTS)) 182 len--; 183 if (len < 0) { 184 krb5_set_error_string(context, 185 "Keytab principal contains invalid length"); 186 ret = KRB5_KT_END; 187 goto out; 188 } 189 ret = krb5_kt_ret_string(context, sp, &p->realm); 190 if(ret) 191 goto out; 192 p->name.name_string.val = calloc(len, sizeof(*p->name.name_string.val)); 193 if(p->name.name_string.val == NULL) { 194 krb5_set_error_string (context, "malloc: out of memory"); 195 ret = ENOMEM; 196 goto out; 197 } 198 p->name.name_string.len = len; 199 for(i = 0; i < p->name.name_string.len; i++){ 200 ret = krb5_kt_ret_string(context, sp, p->name.name_string.val + i); 201 if(ret) 202 goto out; 203 } 204 if (krb5_storage_is_flags(sp, KRB5_STORAGE_PRINCIPAL_NO_NAME_TYPE)) 205 p->name.name_type = KRB5_NT_UNKNOWN; 206 else { 207 int32_t tmp32; 208 ret = krb5_ret_int32(sp, &tmp32); 209 p->name.name_type = tmp32; 210 if (ret) 211 goto out; 212 } 213 *princ = p; 214 return 0; 215 out: 216 krb5_free_principal(context, p); 217 return ret; 218 } 219 220 static krb5_error_code 221 krb5_kt_store_principal(krb5_context context, 222 krb5_storage *sp, 223 krb5_principal p) 224 { 225 int i; 226 int ret; 227 228 if(krb5_storage_is_flags(sp, KRB5_STORAGE_PRINCIPAL_WRONG_NUM_COMPONENTS)) 229 ret = krb5_store_int16(sp, p->name.name_string.len + 1); 230 else 231 ret = krb5_store_int16(sp, p->name.name_string.len); 232 if(ret) return ret; 233 ret = krb5_kt_store_string(sp, p->realm); 234 if(ret) return ret; 235 for(i = 0; i < p->name.name_string.len; i++){ 236 ret = krb5_kt_store_string(sp, p->name.name_string.val[i]); 237 if(ret) 238 return ret; 239 } 240 if(!krb5_storage_is_flags(sp, KRB5_STORAGE_PRINCIPAL_NO_NAME_TYPE)) { 241 ret = krb5_store_int32(sp, p->name.name_type); 242 if(ret) 243 return ret; 244 } 245 246 return 0; 247 } 248 249 static krb5_error_code 250 fkt_resolve(krb5_context context, const char *name, krb5_keytab id) 251 { 252 struct fkt_data *d; 253 254 d = malloc(sizeof(*d)); 255 if(d == NULL) { 256 krb5_set_error_string (context, "malloc: out of memory"); 257 return ENOMEM; 258 } 259 d->filename = strdup(name); 260 if(d->filename == NULL) { 261 free(d); 262 krb5_set_error_string (context, "malloc: out of memory"); 263 return ENOMEM; 264 } 265 d->flags = 0; 266 id->data = d; 267 return 0; 268 } 269 270 static krb5_error_code 271 fkt_resolve_java14(krb5_context context, const char *name, krb5_keytab id) 272 { 273 krb5_error_code ret; 274 275 ret = fkt_resolve(context, name, id); 276 if (ret == 0) { 277 struct fkt_data *d = id->data; 278 d->flags |= KRB5_KT_FL_JAVA; 279 } 280 return ret; 281 } 282 283 static krb5_error_code 284 fkt_close(krb5_context context, krb5_keytab id) 285 { 286 struct fkt_data *d = id->data; 287 free(d->filename); 288 free(d); 289 return 0; 290 } 291 292 static krb5_error_code 293 fkt_get_name(krb5_context context, 294 krb5_keytab id, 295 char *name, 296 size_t namesize) 297 { 298 /* This function is XXX */ 299 struct fkt_data *d = id->data; 300 strlcpy(name, d->filename, namesize); 301 return 0; 302 } 303 304 static void 305 storage_set_flags(krb5_context context, krb5_storage *sp, int vno) 306 { 307 int flags = 0; 308 switch(vno) { 309 case KRB5_KT_VNO_1: 310 flags |= KRB5_STORAGE_PRINCIPAL_WRONG_NUM_COMPONENTS; 311 flags |= KRB5_STORAGE_PRINCIPAL_NO_NAME_TYPE; 312 flags |= KRB5_STORAGE_HOST_BYTEORDER; 313 break; 314 case KRB5_KT_VNO_2: 315 break; 316 default: 317 krb5_warnx(context, 318 "storage_set_flags called with bad vno (%d)", vno); 319 } 320 krb5_storage_set_flags(sp, flags); 321 } 322 323 static krb5_error_code 324 fkt_start_seq_get_int(krb5_context context, 325 krb5_keytab id, 326 int flags, 327 int exclusive, 328 krb5_kt_cursor *c) 329 { 330 int8_t pvno, tag; 331 krb5_error_code ret; 332 struct fkt_data *d = id->data; 333 334 c->fd = open (d->filename, flags); 335 if (c->fd < 0) { 336 ret = errno; 337 krb5_set_error_string(context, "%s: %s", d->filename, 338 strerror(ret)); 339 return ret; 340 } 341 ret = _krb5_xlock(context, c->fd, exclusive, d->filename); 342 if (ret) { 343 close(c->fd); 344 return ret; 345 } 346 c->sp = krb5_storage_from_fd(c->fd); 347 if (c->sp == NULL) { 348 _krb5_xunlock(context, c->fd); 349 close(c->fd); 350 krb5_set_error_string (context, "malloc: out of memory"); 351 return ENOMEM; 352 } 353 krb5_storage_set_eof_code(c->sp, KRB5_KT_END); 354 ret = krb5_ret_int8(c->sp, &pvno); 355 if(ret) { 356 krb5_storage_free(c->sp); 357 _krb5_xunlock(context, c->fd); 358 close(c->fd); 359 krb5_clear_error_string(context); 360 return ret; 361 } 362 if(pvno != 5) { 363 krb5_storage_free(c->sp); 364 _krb5_xunlock(context, c->fd); 365 close(c->fd); 366 krb5_clear_error_string (context); 367 return KRB5_KEYTAB_BADVNO; 368 } 369 ret = krb5_ret_int8(c->sp, &tag); 370 if (ret) { 371 krb5_storage_free(c->sp); 372 _krb5_xunlock(context, c->fd); 373 close(c->fd); 374 krb5_clear_error_string(context); 375 return ret; 376 } 377 id->version = tag; 378 storage_set_flags(context, c->sp, id->version); 379 return 0; 380 } 381 382 static krb5_error_code 383 fkt_start_seq_get(krb5_context context, 384 krb5_keytab id, 385 krb5_kt_cursor *c) 386 { 387 return fkt_start_seq_get_int(context, id, O_RDONLY | O_BINARY, 0, c); 388 } 389 390 static krb5_error_code 391 fkt_next_entry_int(krb5_context context, 392 krb5_keytab id, 393 krb5_keytab_entry *entry, 394 krb5_kt_cursor *cursor, 395 off_t *start, 396 off_t *end) 397 { 398 int32_t len; 399 int ret; 400 int8_t tmp8; 401 int32_t tmp32; 402 off_t pos, curpos; 403 404 pos = krb5_storage_seek(cursor->sp, 0, SEEK_CUR); 405 loop: 406 ret = krb5_ret_int32(cursor->sp, &len); 407 if (ret) 408 return ret; 409 if(len < 0) { 410 pos = krb5_storage_seek(cursor->sp, -len, SEEK_CUR); 411 goto loop; 412 } 413 ret = krb5_kt_ret_principal (context, cursor->sp, &entry->principal); 414 if (ret) 415 goto out; 416 ret = krb5_ret_int32(cursor->sp, &tmp32); 417 entry->timestamp = tmp32; 418 if (ret) 419 goto out; 420 ret = krb5_ret_int8(cursor->sp, &tmp8); 421 if (ret) 422 goto out; 423 entry->vno = tmp8; 424 ret = krb5_kt_ret_keyblock (context, cursor->sp, &entry->keyblock); 425 if (ret) 426 goto out; 427 /* there might be a 32 bit kvno here 428 * if it's zero, assume that the 8bit one was right, 429 * otherwise trust the new value */ 430 curpos = krb5_storage_seek(cursor->sp, 0, SEEK_CUR); 431 if(len + 4 + pos - curpos >= 4) { 432 ret = krb5_ret_int32(cursor->sp, &tmp32); 433 if (ret == 0 && tmp32 != 0) { 434 entry->vno = tmp32; 435 } 436 } 437 if(start) *start = pos; 438 if(end) *end = pos + 4 + len; 439 out: 440 krb5_storage_seek(cursor->sp, pos + 4 + len, SEEK_SET); 441 return ret; 442 } 443 444 static krb5_error_code 445 fkt_next_entry(krb5_context context, 446 krb5_keytab id, 447 krb5_keytab_entry *entry, 448 krb5_kt_cursor *cursor) 449 { 450 return fkt_next_entry_int(context, id, entry, cursor, NULL, NULL); 451 } 452 453 static krb5_error_code 454 fkt_end_seq_get(krb5_context context, 455 krb5_keytab id, 456 krb5_kt_cursor *cursor) 457 { 458 krb5_storage_free(cursor->sp); 459 _krb5_xunlock(context, cursor->fd); 460 close(cursor->fd); 461 return 0; 462 } 463 464 static krb5_error_code 465 fkt_setup_keytab(krb5_context context, 466 krb5_keytab id, 467 krb5_storage *sp) 468 { 469 krb5_error_code ret; 470 ret = krb5_store_int8(sp, 5); 471 if(ret) 472 return ret; 473 if(id->version == 0) 474 id->version = KRB5_KT_VNO; 475 return krb5_store_int8 (sp, id->version); 476 } 477 478 static krb5_error_code 479 fkt_add_entry(krb5_context context, 480 krb5_keytab id, 481 krb5_keytab_entry *entry) 482 { 483 int ret; 484 int fd; 485 krb5_storage *sp; 486 struct fkt_data *d = id->data; 487 krb5_data keytab; 488 int32_t len; 489 490 fd = open (d->filename, O_RDWR | O_BINARY); 491 if (fd < 0) { 492 fd = open (d->filename, O_RDWR | O_CREAT | O_EXCL | O_BINARY, 0600); 493 if (fd < 0) { 494 ret = errno; 495 krb5_set_error_string(context, "open(%s): %s", d->filename, 496 strerror(ret)); 497 return ret; 498 } 499 ret = _krb5_xlock(context, fd, 1, d->filename); 500 if (ret) { 501 close(fd); 502 return ret; 503 } 504 sp = krb5_storage_from_fd(fd); 505 krb5_storage_set_eof_code(sp, KRB5_KT_END); 506 ret = fkt_setup_keytab(context, id, sp); 507 if(ret) { 508 goto out; 509 } 510 storage_set_flags(context, sp, id->version); 511 } else { 512 int8_t pvno, tag; 513 ret = _krb5_xlock(context, fd, 1, d->filename); 514 if (ret) { 515 close(fd); 516 return ret; 517 } 518 sp = krb5_storage_from_fd(fd); 519 krb5_storage_set_eof_code(sp, KRB5_KT_END); 520 ret = krb5_ret_int8(sp, &pvno); 521 if(ret) { 522 /* we probably have a zero byte file, so try to set it up 523 properly */ 524 ret = fkt_setup_keytab(context, id, sp); 525 if(ret) { 526 krb5_set_error_string(context, "%s: keytab is corrupted: %s", 527 d->filename, strerror(ret)); 528 goto out; 529 } 530 storage_set_flags(context, sp, id->version); 531 } else { 532 if(pvno != 5) { 533 ret = KRB5_KEYTAB_BADVNO; 534 krb5_set_error_string(context, "%s: %s", 535 d->filename, strerror(ret)); 536 goto out; 537 } 538 ret = krb5_ret_int8 (sp, &tag); 539 if (ret) { 540 krb5_set_error_string(context, "%s: reading tag: %s", 541 d->filename, strerror(ret)); 542 goto out; 543 } 544 id->version = tag; 545 storage_set_flags(context, sp, id->version); 546 } 547 } 548 549 { 550 krb5_storage *emem; 551 emem = krb5_storage_emem(); 552 if(emem == NULL) { 553 ret = ENOMEM; 554 krb5_set_error_string (context, "malloc: out of memory"); 555 goto out; 556 } 557 ret = krb5_kt_store_principal(context, emem, entry->principal); 558 if(ret) { 559 krb5_storage_free(emem); 560 goto out; 561 } 562 ret = krb5_store_int32 (emem, entry->timestamp); 563 if(ret) { 564 krb5_storage_free(emem); 565 goto out; 566 } 567 ret = krb5_store_int8 (emem, entry->vno % 256); 568 if(ret) { 569 krb5_storage_free(emem); 570 goto out; 571 } 572 ret = krb5_kt_store_keyblock (context, emem, &entry->keyblock); 573 if(ret) { 574 krb5_storage_free(emem); 575 goto out; 576 } 577 if ((d->flags & KRB5_KT_FL_JAVA) == 0) { 578 ret = krb5_store_int32 (emem, entry->vno); 579 if (ret) { 580 krb5_storage_free(emem); 581 goto out; 582 } 583 } 584 585 ret = krb5_storage_to_data(emem, &keytab); 586 krb5_storage_free(emem); 587 if(ret) 588 goto out; 589 } 590 591 while(1) { 592 ret = krb5_ret_int32(sp, &len); 593 if(ret == KRB5_KT_END) { 594 len = keytab.length; 595 break; 596 } 597 if(len < 0) { 598 len = -len; 599 if(len >= keytab.length) { 600 krb5_storage_seek(sp, -4, SEEK_CUR); 601 break; 602 } 603 } 604 krb5_storage_seek(sp, len, SEEK_CUR); 605 } 606 ret = krb5_store_int32(sp, len); 607 if(krb5_storage_write(sp, keytab.data, keytab.length) < 0) 608 ret = errno; 609 memset(keytab.data, 0, keytab.length); 610 krb5_data_free(&keytab); 611 out: 612 krb5_storage_free(sp); 613 _krb5_xunlock(context, fd); 614 close(fd); 615 return ret; 616 } 617 618 static krb5_error_code 619 fkt_remove_entry(krb5_context context, 620 krb5_keytab id, 621 krb5_keytab_entry *entry) 622 { 623 krb5_keytab_entry e; 624 krb5_kt_cursor cursor; 625 off_t pos_start, pos_end; 626 int found = 0; 627 krb5_error_code ret; 628 629 ret = fkt_start_seq_get_int(context, id, O_RDWR | O_BINARY, 1, &cursor); 630 if(ret != 0) 631 goto out; /* return other error here? */ 632 while(fkt_next_entry_int(context, id, &e, &cursor, 633 &pos_start, &pos_end) == 0) { 634 if(krb5_kt_compare(context, &e, entry->principal, 635 entry->vno, entry->keyblock.keytype)) { 636 int32_t len; 637 unsigned char buf[128]; 638 found = 1; 639 krb5_storage_seek(cursor.sp, pos_start, SEEK_SET); 640 len = pos_end - pos_start - 4; 641 krb5_store_int32(cursor.sp, -len); 642 memset(buf, 0, sizeof(buf)); 643 while(len > 0) { 644 krb5_storage_write(cursor.sp, buf, min(len, sizeof(buf))); 645 len -= min(len, sizeof(buf)); 646 } 647 } 648 krb5_kt_free_entry(context, &e); 649 } 650 krb5_kt_end_seq_get(context, id, &cursor); 651 out: 652 if (!found) { 653 krb5_clear_error_string (context); 654 return KRB5_KT_NOTFOUND; 655 } 656 return 0; 657 } 658 659 const krb5_kt_ops krb5_fkt_ops = { 660 "FILE", 661 fkt_resolve, 662 fkt_get_name, 663 fkt_close, 664 NULL, /* get */ 665 fkt_start_seq_get, 666 fkt_next_entry, 667 fkt_end_seq_get, 668 fkt_add_entry, 669 fkt_remove_entry 670 }; 671 672 const krb5_kt_ops krb5_wrfkt_ops = { 673 "WRFILE", 674 fkt_resolve, 675 fkt_get_name, 676 fkt_close, 677 NULL, /* get */ 678 fkt_start_seq_get, 679 fkt_next_entry, 680 fkt_end_seq_get, 681 fkt_add_entry, 682 fkt_remove_entry 683 }; 684 685 const krb5_kt_ops krb5_javakt_ops = { 686 "JAVA14", 687 fkt_resolve_java14, 688 fkt_get_name, 689 fkt_close, 690 NULL, /* get */ 691 fkt_start_seq_get, 692 fkt_next_entry, 693 fkt_end_seq_get, 694 fkt_add_entry, 695 fkt_remove_entry 696 }; 697