1 /* 2 * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 3 * Use is subject to license terms. 4 */ 5 6 7 /* 8 * lib/krb5/keytab/kt_file.c 9 * 10 * Copyright 1990,1991,1995 by the Massachusetts Institute of Technology. 11 * All Rights Reserved. 12 * 13 * Export of this software from the United States of America may 14 * require a specific license from the United States Government. 15 * It is the responsibility of any person or organization contemplating 16 * export to obtain such a license before exporting. 17 * 18 * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and 19 * distribute this software and its documentation for any purpose and 20 * without fee is hereby granted, provided that the above copyright 21 * notice appear in all copies and that both that copyright notice and 22 * this permission notice appear in supporting documentation, and that 23 * the name of M.I.T. not be used in advertising or publicity pertaining 24 * to distribution of the software without specific, written prior 25 * permission. Furthermore if you modify this software you must label 26 * your software as modified software and not distribute it in such a 27 * fashion that it might be confused with the original M.I.T. software. 28 * M.I.T. makes no representations about the suitability of 29 * this software for any purpose. It is provided "as is" without express 30 * or implied warranty. 31 * 32 */ 33 34 #include "k5-int.h" 35 #include <stdio.h> 36 37 /* 38 * Information needed by internal routines of the file-based ticket 39 * cache implementation. 40 */ 41 42 43 /* 44 * Constants 45 */ 46 #define IGNORE_VNO 0 47 #define IGNORE_ENCTYPE 0 48 49 #define KRB5_KT_VNO_1 0x0501 /* krb v5, keytab version 1 (DCE compat) */ 50 #define KRB5_KT_VNO 0x0502 /* krb v5, keytab version 2 (standard) */ 51 52 #define KRB5_KT_DEFAULT_VNO KRB5_KT_VNO 53 54 /* 55 * Types 56 */ 57 typedef struct _krb5_ktfile_data { 58 char *name; /* Name of the file */ 59 FILE *openf; /* open file, if any. */ 60 char iobuf[BUFSIZ]; /* so we can zap it later */ 61 int version; /* Version number of keytab */ 62 k5_mutex_t lock; /* Protect openf, version */ 63 } krb5_ktfile_data; 64 65 /* 66 * Macros 67 */ 68 #define KTPRIVATE(id) ((krb5_ktfile_data *)(id)->data) 69 #define KTFILENAME(id) (((krb5_ktfile_data *)(id)->data)->name) 70 #define KTFILEP(id) (((krb5_ktfile_data *)(id)->data)->openf) 71 #define KTFILEBUFP(id) (((krb5_ktfile_data *)(id)->data)->iobuf) 72 #define KTVERSION(id) (((krb5_ktfile_data *)(id)->data)->version) 73 #define KTLOCK(id) k5_mutex_lock(&((krb5_ktfile_data *)(id)->data)->lock) 74 #define KTUNLOCK(id) k5_mutex_unlock(&((krb5_ktfile_data *)(id)->data)->lock) 75 #define KTCHECKLOCK(id) k5_mutex_assert_locked(&((krb5_ktfile_data *)(id)->data)->lock) 76 77 extern const struct _krb5_kt_ops krb5_ktf_ops; 78 extern const struct _krb5_kt_ops krb5_ktf_writable_ops; 79 80 krb5_error_code KRB5_CALLCONV krb5_ktfile_resolve 81 (krb5_context, 82 const char *, 83 krb5_keytab *); 84 85 krb5_error_code KRB5_CALLCONV krb5_ktfile_wresolve 86 (krb5_context, 87 const char *, 88 krb5_keytab *); 89 90 krb5_error_code KRB5_CALLCONV krb5_ktfile_get_name 91 (krb5_context, 92 krb5_keytab, 93 char *, 94 unsigned int); 95 96 krb5_error_code KRB5_CALLCONV krb5_ktfile_close 97 (krb5_context, 98 krb5_keytab); 99 100 krb5_error_code KRB5_CALLCONV krb5_ktfile_get_entry 101 (krb5_context, 102 krb5_keytab, 103 krb5_const_principal, 104 krb5_kvno, 105 krb5_enctype, 106 krb5_keytab_entry *); 107 108 krb5_error_code KRB5_CALLCONV krb5_ktfile_start_seq_get 109 (krb5_context, 110 krb5_keytab, 111 krb5_kt_cursor *); 112 113 krb5_error_code KRB5_CALLCONV krb5_ktfile_get_next 114 (krb5_context, 115 krb5_keytab, 116 krb5_keytab_entry *, 117 krb5_kt_cursor *); 118 119 krb5_error_code KRB5_CALLCONV krb5_ktfile_end_get 120 (krb5_context, 121 krb5_keytab, 122 krb5_kt_cursor *); 123 124 /* routines to be included on extended version (write routines) */ 125 krb5_error_code KRB5_CALLCONV krb5_ktfile_add 126 (krb5_context, 127 krb5_keytab, 128 krb5_keytab_entry *); 129 130 krb5_error_code KRB5_CALLCONV krb5_ktfile_remove 131 (krb5_context, 132 krb5_keytab, 133 krb5_keytab_entry *); 134 135 krb5_error_code krb5_ktfileint_openr 136 (krb5_context, 137 krb5_keytab); 138 139 krb5_error_code krb5_ktfileint_openw 140 (krb5_context, 141 krb5_keytab); 142 143 krb5_error_code krb5_ktfileint_close 144 (krb5_context, 145 krb5_keytab); 146 147 krb5_error_code krb5_ktfileint_read_entry 148 (krb5_context, 149 krb5_keytab, 150 krb5_keytab_entry *); 151 152 krb5_error_code krb5_ktfileint_write_entry 153 (krb5_context, 154 krb5_keytab, 155 krb5_keytab_entry *); 156 157 krb5_error_code krb5_ktfileint_delete_entry 158 (krb5_context, 159 krb5_keytab, 160 krb5_int32); 161 162 krb5_error_code krb5_ktfileint_internal_read_entry 163 (krb5_context, 164 krb5_keytab, 165 krb5_keytab_entry *, 166 krb5_int32 *); 167 168 krb5_error_code krb5_ktfileint_size_entry 169 (krb5_context, 170 krb5_keytab_entry *, 171 krb5_int32 *); 172 173 krb5_error_code krb5_ktfileint_find_slot 174 (krb5_context, 175 krb5_keytab, 176 krb5_int32 *, 177 krb5_int32 *); 178 179 180 /* 181 * This is an implementation specific resolver. It returns a keytab id 182 * initialized with file keytab routines. 183 */ 184 185 krb5_error_code KRB5_CALLCONV 186 krb5_ktfile_resolve(krb5_context context, const char *name, krb5_keytab *id) 187 { 188 krb5_ktfile_data *data; 189 krb5_error_code err; 190 191 if ((*id = (krb5_keytab) malloc(sizeof(**id))) == NULL) 192 return(ENOMEM); 193 194 (*id)->ops = &krb5_ktf_ops; 195 if ((data = (krb5_ktfile_data *)malloc(sizeof(krb5_ktfile_data))) == NULL) { 196 krb5_xfree(*id); 197 return(ENOMEM); 198 } 199 200 err = k5_mutex_init(&data->lock); 201 if (err) { 202 krb5_xfree(data); 203 krb5_xfree(*id); 204 return err; 205 } 206 207 if ((data->name = (char *)calloc(strlen(name) + 1, sizeof(char))) == NULL) { 208 k5_mutex_destroy(&data->lock); 209 krb5_xfree(data); 210 krb5_xfree(*id); 211 return(ENOMEM); 212 } 213 214 (void) strcpy(data->name, name); 215 data->openf = 0; 216 data->version = 0; 217 218 (*id)->data = (krb5_pointer)data; 219 (*id)->magic = KV5M_KEYTAB; 220 return(0); 221 } 222 223 224 /* 225 * "Close" a file-based keytab and invalidate the id. This means 226 * free memory hidden in the structures. 227 */ 228 229 krb5_error_code KRB5_CALLCONV 230 krb5_ktfile_close(krb5_context context, krb5_keytab id) 231 /* 232 * This routine is responsible for freeing all memory allocated 233 * for this keytab. There are no system resources that need 234 * to be freed nor are there any open files. 235 * 236 * This routine should undo anything done by krb5_ktfile_resolve(). 237 */ 238 { 239 krb5_xfree(KTFILENAME(id)); 240 zap(KTFILEBUFP(id), BUFSIZ); 241 k5_mutex_destroy(&((krb5_ktfile_data *)id->data)->lock); 242 krb5_xfree(id->data); 243 id->ops = 0; 244 krb5_xfree(id); 245 return (0); 246 } 247 248 /* 249 * This is the get_entry routine for the file based keytab implementation. 250 * It opens the keytab file, and either retrieves the entry or returns 251 * an error. 252 */ 253 254 krb5_error_code KRB5_CALLCONV 255 krb5_ktfile_get_entry(krb5_context context, krb5_keytab id, 256 krb5_const_principal principal, krb5_kvno kvno, 257 krb5_enctype enctype, krb5_keytab_entry *entry) 258 { 259 krb5_keytab_entry cur_entry, new_entry; 260 krb5_error_code kerror = 0; 261 int found_wrong_kvno = 0; 262 krb5_boolean similar; 263 int kvno_offset = 0; 264 265 kerror = KTLOCK(id); 266 if (kerror) 267 return kerror; 268 269 /* Open the keyfile for reading */ 270 if ((kerror = krb5_ktfileint_openr(context, id))) { 271 KTUNLOCK(id); 272 return(kerror); 273 } 274 275 /* 276 * For efficiency and simplicity, we'll use a while true that 277 * is exited with a break statement. 278 */ 279 cur_entry.principal = 0; 280 cur_entry.vno = 0; 281 cur_entry.key.contents = 0; 282 283 while (TRUE) { 284 if ((kerror = krb5_ktfileint_read_entry(context, id, &new_entry))) 285 break; 286 287 /* by the time this loop exits, it must either free cur_entry, 288 and copy new_entry there, or free new_entry. Otherwise, it 289 leaks. */ 290 291 /* if the principal isn't the one requested, free new_entry 292 and continue to the next. */ 293 294 if (!krb5_principal_compare(context, principal, new_entry.principal)) { 295 krb5_kt_free_entry(context, &new_entry); 296 continue; 297 } 298 299 /* if the enctype is not ignored and doesn't match, free new_entry 300 and continue to the next */ 301 302 if (enctype != IGNORE_ENCTYPE) { 303 if ((kerror = krb5_c_enctype_compare(context, enctype, 304 new_entry.key.enctype, 305 &similar))) { 306 krb5_kt_free_entry(context, &new_entry); 307 break; 308 } 309 310 if (!similar) { 311 krb5_kt_free_entry(context, &new_entry); 312 continue; 313 } 314 /* 315 * Coerce the enctype of the output keyblock in case we 316 * got an inexact match on the enctype. 317 */ 318 new_entry.key.enctype = enctype; 319 320 } 321 322 if (kvno == IGNORE_VNO) { 323 /* if this is the first match, or if the new vno is 324 bigger, free the current and keep the new. Otherwise, 325 free the new. */ 326 /* A 1.2.x keytab contains only the low 8 bits of the key 327 version number. Since it can be much bigger, and thus 328 the 8-bit value can wrap, we need some heuristics to 329 figure out the "highest" numbered key if some numbers 330 close to 255 and some near 0 are used. 331 332 The heuristic here: 333 334 If we have any keys with versions over 240, then assume 335 that all version numbers 0-127 refer to 256+N instead. 336 Not perfect, but maybe good enough? */ 337 338 #define M(VNO) (((VNO) - kvno_offset + 256) % 256) 339 340 if (new_entry.vno > 240) 341 kvno_offset = 128; 342 if (! cur_entry.principal || 343 M(new_entry.vno) > M(cur_entry.vno)) { 344 krb5_kt_free_entry(context, &cur_entry); 345 cur_entry = new_entry; 346 } else { 347 krb5_kt_free_entry(context, &new_entry); 348 } 349 } else { 350 /* if this kvno matches, free the current (will there ever 351 be one?), keep the new, and break out. Otherwise, remember 352 that we were here so we can return the right error, and 353 free the new */ 354 /* Yuck. The krb5-1.2.x keytab format only stores one byte 355 for the kvno, so we're toast if the kvno requested is 356 higher than that. Short-term workaround: only compare 357 the low 8 bits. */ 358 359 if (new_entry.vno == (kvno & 0xff)) { 360 krb5_kt_free_entry(context, &cur_entry); 361 cur_entry = new_entry; 362 break; 363 } else { 364 found_wrong_kvno++; 365 krb5_kt_free_entry(context, &new_entry); 366 } 367 } 368 } 369 370 if (kerror == KRB5_KT_END) { 371 if (cur_entry.principal) 372 kerror = 0; 373 else if (found_wrong_kvno) 374 kerror = KRB5_KT_KVNONOTFOUND; 375 else 376 kerror = KRB5_KT_NOTFOUND; 377 } 378 if (kerror) { 379 (void) krb5_ktfileint_close(context, id); 380 KTUNLOCK(id); 381 krb5_kt_free_entry(context, &cur_entry); 382 return kerror; 383 } 384 if ((kerror = krb5_ktfileint_close(context, id)) != 0) { 385 KTUNLOCK(id); 386 krb5_kt_free_entry(context, &cur_entry); 387 return kerror; 388 } 389 KTUNLOCK(id); 390 *entry = cur_entry; 391 return 0; 392 } 393 394 /* 395 * Get the name of the file containing a file-based keytab. 396 */ 397 398 krb5_error_code KRB5_CALLCONV 399 krb5_ktfile_get_name(krb5_context context, krb5_keytab id, char *name, unsigned int len) 400 /* 401 * This routine returns the name of the name of the file associated with 402 * this file-based keytab. name is zeroed and the filename is truncated 403 * to fit in name if necessary. The name is prefixed with PREFIX:, so that 404 * trt will happen if the name is passed back to resolve. 405 */ 406 { 407 memset(name, 0, len); 408 409 if (len < strlen(id->ops->prefix)+2) 410 return(KRB5_KT_NAME_TOOLONG); 411 strcpy(name, id->ops->prefix); 412 name += strlen(id->ops->prefix); 413 name[0] = ':'; 414 name++; 415 len -= strlen(id->ops->prefix)+1; 416 417 /* Solaris Kerberos */ 418 if (len < strlen(KTFILENAME(id))+1) 419 return(KRB5_KT_NAME_TOOLONG); 420 strcpy(name, KTFILENAME(id)); 421 /* strcpy will NUL-terminate the destination */ 422 423 return(0); 424 } 425 426 /* 427 * krb5_ktfile_start_seq_get() 428 */ 429 430 krb5_error_code KRB5_CALLCONV 431 krb5_ktfile_start_seq_get(krb5_context context, krb5_keytab id, krb5_kt_cursor *cursorp) 432 { 433 krb5_error_code retval; 434 long *fileoff; 435 436 retval = KTLOCK(id); 437 if (retval) 438 return retval; 439 440 if ((retval = krb5_ktfileint_openr(context, id))) { 441 KTUNLOCK(id); 442 return retval; 443 } 444 445 if (!(fileoff = (long *)malloc(sizeof(*fileoff)))) { 446 krb5_ktfileint_close(context, id); 447 KTUNLOCK(id); 448 return ENOMEM; 449 } 450 *fileoff = ftell(KTFILEP(id)); 451 *cursorp = (krb5_kt_cursor)fileoff; 452 KTUNLOCK(id); 453 454 return 0; 455 } 456 457 /* 458 * krb5_ktfile_get_next() 459 */ 460 461 krb5_error_code KRB5_CALLCONV 462 krb5_ktfile_get_next(krb5_context context, krb5_keytab id, krb5_keytab_entry *entry, krb5_kt_cursor *cursor) 463 { 464 long *fileoff = (long *)*cursor; 465 krb5_keytab_entry cur_entry; 466 krb5_error_code kerror; 467 468 kerror = KTLOCK(id); 469 if (kerror) 470 return kerror; 471 if (KTFILEP(id) == NULL) { 472 KTUNLOCK(id); 473 return KRB5_KT_IOERR; 474 } 475 if (fseek(KTFILEP(id), *fileoff, 0) == -1) { 476 KTUNLOCK(id); 477 return KRB5_KT_END; 478 } 479 if ((kerror = krb5_ktfileint_read_entry(context, id, &cur_entry))) { 480 KTUNLOCK(id); 481 return kerror; 482 } 483 *fileoff = ftell(KTFILEP(id)); 484 *entry = cur_entry; 485 KTUNLOCK(id); 486 return 0; 487 } 488 489 /* 490 * krb5_ktfile_end_get() 491 */ 492 493 krb5_error_code KRB5_CALLCONV 494 krb5_ktfile_end_get(krb5_context context, krb5_keytab id, krb5_kt_cursor *cursor) 495 { 496 krb5_error_code kerror; 497 498 krb5_xfree(*cursor); 499 KTLOCK(id); 500 kerror = krb5_ktfileint_close(context, id); 501 KTUNLOCK(id); 502 return kerror; 503 } 504 505 /* 506 * ser_ktf.c - Serialize keytab file context for subsequent reopen. 507 */ 508 509 static const char ktfile_def_name[] = "."; 510 511 /* 512 * Routines to deal with externalizing krb5_keytab for [WR]FILE: variants. 513 * krb5_ktf_keytab_size(); 514 * krb5_ktf_keytab_externalize(); 515 * krb5_ktf_keytab_internalize(); 516 */ 517 static krb5_error_code krb5_ktf_keytab_size 518 (krb5_context, krb5_pointer, size_t *); 519 static krb5_error_code krb5_ktf_keytab_externalize 520 (krb5_context, krb5_pointer, krb5_octet **, size_t *); 521 static krb5_error_code krb5_ktf_keytab_internalize 522 (krb5_context,krb5_pointer *, krb5_octet **, size_t *); 523 524 /* 525 * Serialization entry for this type. 526 */ 527 const krb5_ser_entry krb5_ktfile_ser_entry = { 528 KV5M_KEYTAB, /* Type */ 529 krb5_ktf_keytab_size, /* Sizer routine */ 530 krb5_ktf_keytab_externalize, /* Externalize routine */ 531 krb5_ktf_keytab_internalize /* Internalize routine */ 532 }; 533 534 /* 535 * krb5_ktf_keytab_size() - Determine the size required to externalize 536 * this krb5_keytab variant. 537 */ 538 static krb5_error_code 539 krb5_ktf_keytab_size(krb5_context kcontext, krb5_pointer arg, size_t *sizep) 540 { 541 krb5_error_code kret; 542 krb5_keytab keytab; 543 size_t required; 544 krb5_ktfile_data *ktdata; 545 546 kret = EINVAL; 547 if ((keytab = (krb5_keytab) arg)) { 548 /* 549 * Saving FILE: variants of krb5_keytab requires at minimum: 550 * krb5_int32 for KV5M_KEYTAB 551 * krb5_int32 for length of keytab name. 552 * krb5_int32 for file status. 553 * krb5_int32 for file position. 554 * krb5_int32 for file position. 555 * krb5_int32 for version. 556 * krb5_int32 for KV5M_KEYTAB 557 */ 558 required = sizeof(krb5_int32) * 7; 559 if (keytab->ops && keytab->ops->prefix) 560 required += (strlen(keytab->ops->prefix)+1); 561 562 /* 563 * The keytab name is formed as follows: 564 * <prefix>:<name> 565 * If there's no name, we use a default name so that we have something 566 * to call krb5_keytab_resolve with. 567 */ 568 ktdata = (krb5_ktfile_data *) keytab->data; 569 required += strlen((ktdata && ktdata->name) ? 570 ktdata->name : ktfile_def_name); 571 kret = 0; 572 573 if (!kret) 574 *sizep += required; 575 } 576 return(kret); 577 } 578 579 /* 580 * krb5_ktf_keytab_externalize() - Externalize the krb5_keytab. 581 */ 582 static krb5_error_code 583 krb5_ktf_keytab_externalize(krb5_context kcontext, krb5_pointer arg, krb5_octet **buffer, size_t *lenremain) 584 { 585 krb5_error_code kret; 586 krb5_keytab keytab; 587 size_t required; 588 krb5_octet *bp; 589 size_t remain; 590 krb5_ktfile_data *ktdata; 591 krb5_int32 file_is_open; 592 krb5_int64 file_pos; 593 char *ktname; 594 size_t namelen; 595 const char *fnamep; 596 597 required = 0; 598 bp = *buffer; 599 remain = *lenremain; 600 kret = EINVAL; 601 if ((keytab = (krb5_keytab) arg)) { 602 kret = ENOMEM; 603 if (!krb5_ktf_keytab_size(kcontext, arg, &required) && 604 (required <= remain)) { 605 /* Our identifier */ 606 (void) krb5_ser_pack_int32(KV5M_KEYTAB, &bp, &remain); 607 608 ktdata = (krb5_ktfile_data *) keytab->data; 609 file_is_open = 0; 610 file_pos = 0; 611 612 /* Calculate the length of the name */ 613 namelen = (keytab->ops && keytab->ops->prefix) ? 614 strlen(keytab->ops->prefix)+1 : 0; 615 if (ktdata && ktdata->name) 616 fnamep = ktdata->name; 617 else 618 fnamep = ktfile_def_name; 619 namelen += (strlen(fnamep)+1); 620 621 if ((ktname = (char *) malloc(namelen))) { 622 /* Format the keytab name. */ 623 if (keytab->ops && keytab->ops->prefix) 624 sprintf(ktname, "%s:%s", keytab->ops->prefix, fnamep); 625 626 else 627 strcpy(ktname, fnamep); 628 629 /* Fill in the file-specific keytab information. */ 630 if (ktdata) { 631 if (ktdata->openf) { 632 long fpos; 633 int fflags = 0; 634 635 file_is_open = 1; 636 #if !defined(_WIN32) 637 fflags = fcntl(fileno(ktdata->openf), F_GETFL, 0); 638 if (fflags > 0) 639 file_is_open |= ((fflags & O_ACCMODE) << 1); 640 #else 641 file_is_open = 0; 642 #endif 643 fpos = ftell(ktdata->openf); 644 file_pos = fpos; /* XX range check? */ 645 } 646 } 647 648 /* Put the length of the file name */ 649 (void) krb5_ser_pack_int32((krb5_int32) strlen(ktname), 650 &bp, &remain); 651 652 /* Put the name */ 653 (void) krb5_ser_pack_bytes((krb5_octet *) ktname, 654 strlen(ktname), 655 &bp, &remain); 656 657 /* Put the file open flag */ 658 (void) krb5_ser_pack_int32(file_is_open, &bp, &remain); 659 660 /* Put the file position */ 661 (void) krb5_ser_pack_int64(file_pos, &bp, &remain); 662 663 /* Put the version */ 664 (void) krb5_ser_pack_int32((krb5_int32) ((ktdata) ? 665 ktdata->version : 0), 666 &bp, &remain); 667 668 /* Put the trailer */ 669 (void) krb5_ser_pack_int32(KV5M_KEYTAB, &bp, &remain); 670 kret = 0; 671 *buffer = bp; 672 *lenremain = remain; 673 free(ktname); 674 } 675 } 676 } 677 return(kret); 678 } 679 680 /* 681 * krb5_ktf_keytab_internalize() - Internalize the krb5_ktf_keytab. 682 */ 683 static krb5_error_code 684 krb5_ktf_keytab_internalize(krb5_context kcontext, krb5_pointer *argp, krb5_octet **buffer, size_t *lenremain) 685 { 686 krb5_error_code kret; 687 krb5_keytab keytab; 688 krb5_int32 ibuf; 689 krb5_octet *bp; 690 size_t remain; 691 char *ktname; 692 krb5_ktfile_data *ktdata; 693 krb5_int32 file_is_open; 694 krb5_int64 foff; 695 696 bp = *buffer; 697 remain = *lenremain; 698 kret = EINVAL; 699 /* Read our magic number */ 700 if (krb5_ser_unpack_int32(&ibuf, &bp, &remain)) 701 ibuf = 0; 702 if (ibuf == KV5M_KEYTAB) { 703 kret = ENOMEM; 704 705 /* Get the length of the keytab name */ 706 kret = krb5_ser_unpack_int32(&ibuf, &bp, &remain); 707 708 if (!kret && 709 (ktname = (char *) malloc((size_t) (ibuf+1))) && 710 !(kret = krb5_ser_unpack_bytes((krb5_octet *) ktname, 711 (size_t) ibuf, 712 &bp, &remain))) { 713 ktname[ibuf] = '\0'; 714 kret = krb5_kt_resolve(kcontext, ktname, &keytab); 715 if (!kret) { 716 kret = ENOMEM; 717 ktdata = (krb5_ktfile_data *) keytab->data; 718 if (!ktdata) { 719 /* XXX */ 720 keytab->data = (void *) malloc(sizeof(krb5_ktfile_data)); 721 ktdata = (krb5_ktfile_data *) keytab->data; 722 memset(ktdata, 0, sizeof(krb5_ktfile_data)); 723 if (strchr(ktname, (int) ':')) 724 ktdata->name = strdup(strchr(ktname, (int) ':')+1); 725 else 726 ktdata->name = strdup(ktname); 727 } 728 if (ktdata) { 729 if (remain >= (sizeof(krb5_int32)*5)) { 730 (void) krb5_ser_unpack_int32(&file_is_open, 731 &bp, &remain); 732 (void) krb5_ser_unpack_int64(&foff, &bp, &remain); 733 (void) krb5_ser_unpack_int32(&ibuf, &bp, &remain); 734 ktdata->version = (int) ibuf; 735 736 (void) krb5_ser_unpack_int32(&ibuf, &bp, &remain); 737 if (ibuf == KV5M_KEYTAB) { 738 if (file_is_open) { 739 int fmode; 740 long fpos; 741 742 #if !defined(_WIN32) 743 fmode = (file_is_open >> 1) & O_ACCMODE; 744 #else 745 fmode = 0; 746 #endif 747 if (fmode) 748 kret = krb5_ktfileint_openw(kcontext, 749 keytab); 750 else 751 kret = krb5_ktfileint_openr(kcontext, 752 keytab); 753 if (!kret) { 754 fpos = foff; /* XX range check? */ 755 fseek(KTFILEP(keytab), fpos, SEEK_SET); 756 } 757 } 758 kret = 0; 759 } 760 else 761 kret = EINVAL; 762 } 763 } 764 if (kret) { 765 if (keytab->data) { 766 if (KTFILENAME(keytab)) 767 krb5_xfree(KTFILENAME(keytab)); 768 krb5_xfree(keytab->data); 769 } 770 krb5_xfree(keytab); 771 } 772 else { 773 *buffer = bp; 774 *lenremain = remain; 775 *argp = (krb5_pointer) keytab; 776 } 777 } 778 free(ktname); 779 } 780 } 781 return(kret); 782 } 783 784 /* 785 * This is an implementation specific resolver. It returns a keytab id 786 * initialized with file keytab routines. 787 */ 788 789 krb5_error_code KRB5_CALLCONV 790 krb5_ktfile_wresolve(krb5_context context, const char *name, krb5_keytab *id) 791 { 792 krb5_ktfile_data *data; 793 krb5_error_code err; 794 795 if ((*id = (krb5_keytab) malloc(sizeof(**id))) == NULL) 796 return(ENOMEM); 797 798 (*id)->ops = &krb5_ktf_writable_ops; 799 if ((data = (krb5_ktfile_data *)malloc(sizeof(krb5_ktfile_data))) == NULL) { 800 krb5_xfree(*id); 801 return(ENOMEM); 802 } 803 804 err = k5_mutex_init(&data->lock); 805 if (err) { 806 krb5_xfree(data); 807 krb5_xfree(*id); 808 return err; 809 } 810 811 if ((data->name = (char *)calloc(strlen(name) + 1, sizeof(char))) == NULL) { 812 k5_mutex_destroy(&data->lock); 813 krb5_xfree(data); 814 krb5_xfree(*id); 815 return(ENOMEM); 816 } 817 818 (void) strcpy(data->name, name); 819 data->openf = 0; 820 data->version = 0; 821 822 (*id)->data = (krb5_pointer)data; 823 (*id)->magic = KV5M_KEYTAB; 824 return(0); 825 } 826 827 828 /* 829 * krb5_ktfile_add() 830 */ 831 832 krb5_error_code KRB5_CALLCONV 833 krb5_ktfile_add(krb5_context context, krb5_keytab id, krb5_keytab_entry *entry) 834 { 835 krb5_error_code retval; 836 837 retval = KTLOCK(id); 838 if (retval) 839 return retval; 840 if ((retval = krb5_ktfileint_openw(context, id))) { 841 KTUNLOCK(id); 842 return retval; 843 } 844 if (fseek(KTFILEP(id), 0, 2) == -1) { 845 KTUNLOCK(id); 846 return KRB5_KT_END; 847 } 848 retval = krb5_ktfileint_write_entry(context, id, entry); 849 krb5_ktfileint_close(context, id); 850 KTUNLOCK(id); 851 return retval; 852 } 853 854 /* 855 * krb5_ktfile_remove() 856 */ 857 858 krb5_error_code KRB5_CALLCONV 859 krb5_ktfile_remove(krb5_context context, krb5_keytab id, krb5_keytab_entry *entry) 860 { 861 krb5_keytab_entry cur_entry; 862 krb5_error_code kerror; 863 krb5_int32 delete_point; 864 865 kerror = KTLOCK(id); 866 if (kerror) 867 return kerror; 868 869 if ((kerror = krb5_ktfileint_openw(context, id))) { 870 KTUNLOCK(id); 871 return kerror; 872 } 873 874 /* 875 * For efficiency and simplicity, we'll use a while true that 876 * is exited with a break statement. 877 */ 878 while (TRUE) { 879 if ((kerror = krb5_ktfileint_internal_read_entry(context, id, 880 &cur_entry, 881 &delete_point))) 882 break; 883 884 if ((entry->vno == cur_entry.vno) && 885 (entry->key.enctype == cur_entry.key.enctype) && 886 krb5_principal_compare(context, entry->principal, cur_entry.principal)) { 887 /* found a match */ 888 krb5_kt_free_entry(context, &cur_entry); 889 break; 890 } 891 krb5_kt_free_entry(context, &cur_entry); 892 } 893 894 if (kerror == KRB5_KT_END) 895 kerror = KRB5_KT_NOTFOUND; 896 897 if (kerror) { 898 (void) krb5_ktfileint_close(context, id); 899 KTUNLOCK(id); 900 return kerror; 901 } 902 903 kerror = krb5_ktfileint_delete_entry(context, id, delete_point); 904 905 if (kerror) { 906 (void) krb5_ktfileint_close(context, id); 907 } else { 908 kerror = krb5_ktfileint_close(context, id); 909 } 910 KTUNLOCK(id); 911 return kerror; 912 } 913 914 /* 915 * krb5_ktf_ops 916 */ 917 918 const struct _krb5_kt_ops krb5_ktf_ops = { 919 0, 920 "FILE", /* Prefix -- this string should not appear anywhere else! */ 921 krb5_ktfile_resolve, 922 krb5_ktfile_get_name, 923 krb5_ktfile_close, 924 krb5_ktfile_get_entry, 925 krb5_ktfile_start_seq_get, 926 krb5_ktfile_get_next, 927 krb5_ktfile_end_get, 928 0, 929 0, 930 &krb5_ktfile_ser_entry 931 }; 932 933 /* 934 * krb5_ktf_writable_ops 935 */ 936 937 const struct _krb5_kt_ops krb5_ktf_writable_ops = { 938 0, 939 "WRFILE", /* Prefix -- this string should not appear anywhere else! */ 940 krb5_ktfile_wresolve, 941 krb5_ktfile_get_name, 942 krb5_ktfile_close, 943 krb5_ktfile_get_entry, 944 krb5_ktfile_start_seq_get, 945 krb5_ktfile_get_next, 946 krb5_ktfile_end_get, 947 krb5_ktfile_add, 948 krb5_ktfile_remove, 949 &krb5_ktfile_ser_entry 950 }; 951 952 /* 953 * krb5_kt_dfl_ops 954 */ 955 956 const krb5_kt_ops krb5_kt_dfl_ops = { 957 0, 958 "FILE", /* Prefix -- this string should not appear anywhere else! */ 959 krb5_ktfile_resolve, 960 krb5_ktfile_get_name, 961 krb5_ktfile_close, 962 krb5_ktfile_get_entry, 963 krb5_ktfile_start_seq_get, 964 krb5_ktfile_get_next, 965 krb5_ktfile_end_get, 966 0, 967 0, 968 &krb5_ktfile_ser_entry 969 }; 970 971 /* 972 * lib/krb5/keytab/file/ktf_util.c 973 * 974 * Copyright (c) Hewlett-Packard Company 1991 975 * Released to the Massachusetts Institute of Technology for inclusion 976 * in the Kerberos source code distribution. 977 * 978 * Copyright 1990,1991 by the Massachusetts Institute of Technology. 979 * All Rights Reserved. 980 * 981 * Export of this software from the United States of America may 982 * require a specific license from the United States Government. 983 * It is the responsibility of any person or organization contemplating 984 * export to obtain such a license before exporting. 985 * 986 * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and 987 * distribute this software and its documentation for any purpose and 988 * without fee is hereby granted, provided that the above copyright 989 * notice appear in all copies and that both that copyright notice and 990 * this permission notice appear in supporting documentation, and that 991 * the name of M.I.T. not be used in advertising or publicity pertaining 992 * to distribution of the software without specific, written prior 993 * permission. Furthermore if you modify this software you must label 994 * your software as modified software and not distribute it in such a 995 * fashion that it might be confused with the original M.I.T. software. 996 * M.I.T. makes no representations about the suitability of 997 * this software for any purpose. It is provided "as is" without express 998 * or implied warranty. 999 * 1000 * 1001 * This function contains utilities for the file based implementation of 1002 * the keytab. There are no public functions in this file. 1003 * 1004 * This file is the only one that has knowledge of the format of a 1005 * keytab file. 1006 * 1007 * The format is as follows: 1008 * 1009 * <file format vno> 1010 * <record length> 1011 * principal timestamp vno key 1012 * <record length> 1013 * principal timestamp vno key 1014 * .... 1015 * 1016 * A length field (sizeof(krb5_int32)) exists between entries. When this 1017 * length is positive it indicates an active entry, when negative a hole. 1018 * The length indicates the size of the block in the file (this may be 1019 * larger than the size of the next record, since we are using a first 1020 * fit algorithm for re-using holes and the first fit may be larger than 1021 * the entry we are writing). Another (compatible) implementation could 1022 * break up holes when allocating them to smaller entries to minimize 1023 * wasted space. (Such an implementation should also coalesce adjacent 1024 * holes to reduce fragmentation). This implementation does neither. 1025 * 1026 * There are no separators between fields of an entry. 1027 * A principal is a length-encoded array of length-encoded strings. The 1028 * length is a krb5_int16 in each case. The specific format, then, is 1029 * multiple entries concatinated with no separators. An entry has this 1030 * exact format: 1031 * 1032 * sizeof(krb5_int16) bytes for number of components in the principal; 1033 * then, each component listed in ordser. 1034 * For each component, sizeof(krb5_int16) bytes for the number of bytes 1035 * in the component, followed by the component. 1036 * sizeof(krb5_int32) for the principal type (for KEYTAB V2 and higher) 1037 * sizeof(krb5_int32) bytes for the timestamp 1038 * sizeof(krb5_octet) bytes for the key version number 1039 * sizeof(krb5_int16) bytes for the enctype 1040 * sizeof(krb5_int32) bytes for the key length, followed by the key 1041 */ 1042 1043 #ifndef SEEK_SET 1044 #define SEEK_SET 0 1045 #define SEEK_CUR 1 1046 #endif 1047 1048 typedef krb5_int16 krb5_kt_vno; 1049 1050 #define krb5_kt_default_vno ((krb5_kt_vno)KRB5_KT_DEFAULT_VNO) 1051 1052 #define xfwrite(a, b, c, d) fwrite((char *)a, b, (unsigned) c, d) 1053 #define xfread(a, b, c, d) fread((char *)a, b, (unsigned) c, d) 1054 1055 #ifdef ANSI_STDIO 1056 /* Solaris Kerberos */ 1057 static char *const fopen_mode_rbplus= "rb+F"; 1058 static char *const fopen_mode_rb = "rbF"; 1059 #else 1060 /* Solaris Kerberos */ 1061 static char *const fopen_mode_rbplus= "r+F"; 1062 static char *const fopen_mode_rb = "rF"; 1063 #endif 1064 1065 static krb5_error_code 1066 krb5_ktfileint_open(krb5_context context, krb5_keytab id, int mode) 1067 { 1068 krb5_error_code kerror; 1069 krb5_kt_vno kt_vno; 1070 int writevno = 0; 1071 1072 KTCHECKLOCK(id); 1073 errno = 0; 1074 KTFILEP(id) = fopen(KTFILENAME(id), 1075 (mode == KRB5_LOCKMODE_EXCLUSIVE) ? 1076 fopen_mode_rbplus : fopen_mode_rb); 1077 if (!KTFILEP(id)) { 1078 if ((mode == KRB5_LOCKMODE_EXCLUSIVE) && (errno == ENOENT)) { 1079 /* try making it first time around */ 1080 krb5_create_secure_file(context, KTFILENAME(id)); 1081 errno = 0; 1082 KTFILEP(id) = fopen(KTFILENAME(id), fopen_mode_rbplus); 1083 if (!KTFILEP(id)) 1084 return errno ? errno : EMFILE; 1085 writevno = 1; 1086 } else /* some other error */ 1087 return errno ? errno : EMFILE; 1088 } 1089 if ((kerror = krb5_lock_file(context, fileno(KTFILEP(id)), mode))) { 1090 (void) fclose(KTFILEP(id)); 1091 KTFILEP(id) = 0; 1092 return kerror; 1093 } 1094 /* assume ANSI or BSD-style stdio */ 1095 setbuf(KTFILEP(id), KTFILEBUFP(id)); 1096 1097 /* get the vno and verify it */ 1098 if (writevno) { 1099 kt_vno = htons(krb5_kt_default_vno); 1100 KTVERSION(id) = krb5_kt_default_vno; 1101 if (!xfwrite(&kt_vno, sizeof(kt_vno), 1, KTFILEP(id))) { 1102 kerror = errno; 1103 (void) krb5_unlock_file(context, fileno(KTFILEP(id))); 1104 (void) fclose(KTFILEP(id)); 1105 return kerror; 1106 } 1107 } else { 1108 /* gotta verify it instead... */ 1109 if (!xfread(&kt_vno, sizeof(kt_vno), 1, KTFILEP(id))) { 1110 if (feof(KTFILEP(id))) 1111 kerror = KRB5_KEYTAB_BADVNO; 1112 else 1113 kerror = errno; 1114 (void) krb5_unlock_file(context, fileno(KTFILEP(id))); 1115 (void) fclose(KTFILEP(id)); 1116 return kerror; 1117 } 1118 kt_vno = KTVERSION(id) = ntohs(kt_vno); 1119 if ((kt_vno != KRB5_KT_VNO) && 1120 (kt_vno != KRB5_KT_VNO_1)) { 1121 (void) krb5_unlock_file(context, fileno(KTFILEP(id))); 1122 (void) fclose(KTFILEP(id)); 1123 return KRB5_KEYTAB_BADVNO; 1124 } 1125 } 1126 return 0; 1127 } 1128 1129 krb5_error_code 1130 krb5_ktfileint_openr(krb5_context context, krb5_keytab id) 1131 { 1132 return krb5_ktfileint_open(context, id, KRB5_LOCKMODE_SHARED); 1133 } 1134 1135 krb5_error_code 1136 krb5_ktfileint_openw(krb5_context context, krb5_keytab id) 1137 { 1138 return krb5_ktfileint_open(context, id, KRB5_LOCKMODE_EXCLUSIVE); 1139 } 1140 1141 krb5_error_code 1142 krb5_ktfileint_close(krb5_context context, krb5_keytab id) 1143 { 1144 krb5_error_code kerror; 1145 1146 KTCHECKLOCK(id); 1147 if (!KTFILEP(id)) 1148 return 0; 1149 kerror = krb5_unlock_file(context, fileno(KTFILEP(id))); 1150 (void) fclose(KTFILEP(id)); 1151 KTFILEP(id) = 0; 1152 return kerror; 1153 } 1154 1155 krb5_error_code 1156 krb5_ktfileint_delete_entry(krb5_context context, krb5_keytab id, krb5_int32 delete_point) 1157 { 1158 krb5_int32 size; 1159 krb5_int32 len; 1160 char iobuf[BUFSIZ]; 1161 1162 KTCHECKLOCK(id); 1163 if (fseek(KTFILEP(id), delete_point, SEEK_SET)) { 1164 return errno; 1165 } 1166 if (!xfread(&size, sizeof(size), 1, KTFILEP(id))) { 1167 return KRB5_KT_END; 1168 } 1169 if (KTVERSION(id) != KRB5_KT_VNO_1) 1170 size = ntohl(size); 1171 1172 if (size > 0) { 1173 krb5_int32 minus_size = -size; 1174 if (KTVERSION(id) != KRB5_KT_VNO_1) 1175 minus_size = htonl(minus_size); 1176 1177 if (fseek(KTFILEP(id), delete_point, SEEK_SET)) { 1178 return errno; 1179 } 1180 1181 if (!xfwrite(&minus_size, sizeof(minus_size), 1, KTFILEP(id))) { 1182 return KRB5_KT_IOERR; 1183 } 1184 1185 if (size < BUFSIZ) { 1186 len = size; 1187 } else { 1188 len = BUFSIZ; 1189 } 1190 1191 memset(iobuf, 0, (size_t) len); 1192 while (size > 0) { 1193 xfwrite(iobuf, 1, (size_t) len, KTFILEP(id)); 1194 size -= len; 1195 if (size < len) { 1196 len = size; 1197 } 1198 } 1199 1200 return krb5_sync_disk_file(context, KTFILEP(id)); 1201 } 1202 1203 return 0; 1204 } 1205 1206 krb5_error_code 1207 krb5_ktfileint_internal_read_entry(krb5_context context, krb5_keytab id, krb5_keytab_entry *ret_entry, krb5_int32 *delete_point) 1208 { 1209 krb5_octet vno; 1210 krb5_int16 count; 1211 unsigned int u_count, u_princ_size; 1212 krb5_int16 enctype; 1213 krb5_int16 princ_size; 1214 register int i; 1215 krb5_int32 size; 1216 krb5_int32 start_pos; 1217 krb5_error_code error; 1218 char *tmpdata; 1219 krb5_data *princ; 1220 1221 KTCHECKLOCK(id); 1222 memset(ret_entry, 0, sizeof(krb5_keytab_entry)); 1223 ret_entry->magic = KV5M_KEYTAB_ENTRY; 1224 1225 /* fseek to synchronise buffered I/O on the key table. */ 1226 1227 if (fseek(KTFILEP(id), 0L, SEEK_CUR) < 0) 1228 { 1229 return errno; 1230 } 1231 1232 do { 1233 *delete_point = ftell(KTFILEP(id)); 1234 if (!xfread(&size, sizeof(size), 1, KTFILEP(id))) { 1235 return KRB5_KT_END; 1236 } 1237 if (KTVERSION(id) != KRB5_KT_VNO_1) 1238 size = ntohl(size); 1239 1240 if (size < 0) { 1241 if (fseek(KTFILEP(id), -size, SEEK_CUR)) { 1242 return errno; 1243 } 1244 } 1245 } while (size < 0); 1246 1247 if (size == 0) { 1248 return KRB5_KT_END; 1249 } 1250 1251 start_pos = ftell(KTFILEP(id)); 1252 1253 /* deal with guts of parsing... */ 1254 1255 /* first, int16 with #princ components */ 1256 if (!xfread(&count, sizeof(count), 1, KTFILEP(id))) 1257 return KRB5_KT_END; 1258 if (KTVERSION(id) == KRB5_KT_VNO_1) { 1259 count -= 1; /* V1 includes the realm in the count */ 1260 } else { 1261 count = ntohs(count); 1262 } 1263 if (!count || (count < 0)) 1264 return KRB5_KT_END; 1265 ret_entry->principal = (krb5_principal)malloc(sizeof(krb5_principal_data)); 1266 if (!ret_entry->principal) 1267 return ENOMEM; 1268 1269 u_count = count; 1270 ret_entry->principal->magic = KV5M_PRINCIPAL; 1271 ret_entry->principal->length = u_count; 1272 ret_entry->principal->data = (krb5_data *) 1273 calloc(u_count, sizeof(krb5_data)); 1274 if (!ret_entry->principal->data) { 1275 free(ret_entry->principal); 1276 ret_entry->principal = 0; 1277 return ENOMEM; 1278 } 1279 1280 /* Now, get the realm data */ 1281 if (!xfread(&princ_size, sizeof(princ_size), 1, KTFILEP(id))) { 1282 error = KRB5_KT_END; 1283 goto fail; 1284 } 1285 if (KTVERSION(id) != KRB5_KT_VNO_1) 1286 princ_size = ntohs(princ_size); 1287 if (!princ_size || (princ_size < 0)) { 1288 error = KRB5_KT_END; 1289 goto fail; 1290 } 1291 u_princ_size = princ_size; 1292 1293 krb5_princ_set_realm_length(context, ret_entry->principal, u_princ_size); 1294 tmpdata = malloc(u_princ_size+1); 1295 if (!tmpdata) { 1296 error = ENOMEM; 1297 goto fail; 1298 } 1299 if (fread(tmpdata, 1, u_princ_size, KTFILEP(id)) != (size_t) princ_size) { 1300 free(tmpdata); 1301 error = KRB5_KT_END; 1302 goto fail; 1303 } 1304 tmpdata[princ_size] = 0; /* Some things might be expecting null */ 1305 /* termination... ``Be conservative in */ 1306 /* what you send out'' */ 1307 krb5_princ_set_realm_data(context, ret_entry->principal, tmpdata); 1308 1309 for (i = 0; i < count; i++) { 1310 princ = krb5_princ_component(context, ret_entry->principal, i); 1311 if (!xfread(&princ_size, sizeof(princ_size), 1, KTFILEP(id))) { 1312 error = KRB5_KT_END; 1313 goto fail; 1314 } 1315 if (KTVERSION(id) != KRB5_KT_VNO_1) 1316 princ_size = ntohs(princ_size); 1317 if (!princ_size || (princ_size < 0)) { 1318 error = KRB5_KT_END; 1319 goto fail; 1320 } 1321 1322 u_princ_size = princ_size; 1323 princ->length = u_princ_size; 1324 princ->data = malloc(u_princ_size+1); 1325 if (!princ->data) { 1326 error = ENOMEM; 1327 goto fail; 1328 } 1329 if (!xfread(princ->data, sizeof(char), u_princ_size, KTFILEP(id))) { 1330 error = KRB5_KT_END; 1331 goto fail; 1332 } 1333 princ->data[princ_size] = 0; /* Null terminate */ 1334 } 1335 1336 /* read in the principal type, if we can get it */ 1337 if (KTVERSION(id) != KRB5_KT_VNO_1) { 1338 if (!xfread(&ret_entry->principal->type, 1339 sizeof(ret_entry->principal->type), 1, KTFILEP(id))) { 1340 error = KRB5_KT_END; 1341 goto fail; 1342 } 1343 ret_entry->principal->type = ntohl(ret_entry->principal->type); 1344 } 1345 1346 /* read in the timestamp */ 1347 if (!xfread(&ret_entry->timestamp, sizeof(ret_entry->timestamp), 1, KTFILEP(id))) { 1348 error = KRB5_KT_END; 1349 goto fail; 1350 } 1351 if (KTVERSION(id) != KRB5_KT_VNO_1) 1352 ret_entry->timestamp = ntohl(ret_entry->timestamp); 1353 1354 /* read in the version number */ 1355 if (!xfread(&vno, sizeof(vno), 1, KTFILEP(id))) { 1356 error = KRB5_KT_END; 1357 goto fail; 1358 } 1359 ret_entry->vno = (krb5_kvno)vno; 1360 1361 /* key type */ 1362 if (!xfread(&enctype, sizeof(enctype), 1, KTFILEP(id))) { 1363 error = KRB5_KT_END; 1364 goto fail; 1365 } 1366 ret_entry->key.enctype = (krb5_enctype)enctype; 1367 1368 if (KTVERSION(id) != KRB5_KT_VNO_1) 1369 ret_entry->key.enctype = ntohs(ret_entry->key.enctype); 1370 1371 /* key contents */ 1372 ret_entry->key.magic = KV5M_KEYBLOCK; 1373 1374 if (!xfread(&count, sizeof(count), 1, KTFILEP(id))) { 1375 error = KRB5_KT_END; 1376 goto fail; 1377 } 1378 if (KTVERSION(id) != KRB5_KT_VNO_1) 1379 count = ntohs(count); 1380 if (!count || (count < 0)) { 1381 error = KRB5_KT_END; 1382 goto fail; 1383 } 1384 1385 u_count = count; 1386 ret_entry->key.length = u_count; 1387 1388 ret_entry->key.contents = (krb5_octet *)malloc(u_count); 1389 if (!ret_entry->key.contents) { 1390 error = ENOMEM; 1391 goto fail; 1392 } 1393 if (!xfread(ret_entry->key.contents, sizeof(krb5_octet), count, 1394 KTFILEP(id))) { 1395 error = KRB5_KT_END; 1396 goto fail; 1397 } 1398 1399 /* 1400 * Reposition file pointer to the next inter-record length field. 1401 */ 1402 fseek(KTFILEP(id), start_pos + size, SEEK_SET); 1403 return 0; 1404 fail: 1405 1406 for (i = 0; i < krb5_princ_size(context, ret_entry->principal); i++) { 1407 princ = krb5_princ_component(context, ret_entry->principal, i); 1408 if (princ->data) 1409 free(princ->data); 1410 } 1411 free(ret_entry->principal->data); 1412 ret_entry->principal->data = 0; 1413 free(ret_entry->principal); 1414 ret_entry->principal = 0; 1415 return error; 1416 } 1417 1418 krb5_error_code 1419 krb5_ktfileint_read_entry(krb5_context context, krb5_keytab id, krb5_keytab_entry *entryp) 1420 { 1421 krb5_int32 delete_point; 1422 1423 return krb5_ktfileint_internal_read_entry(context, id, entryp, &delete_point); 1424 } 1425 1426 krb5_error_code 1427 krb5_ktfileint_write_entry(krb5_context context, krb5_keytab id, krb5_keytab_entry *entry) 1428 { 1429 krb5_octet vno; 1430 krb5_data *princ; 1431 krb5_int16 count, size, enctype; 1432 krb5_error_code retval = 0; 1433 krb5_timestamp timestamp; 1434 krb5_int32 princ_type; 1435 krb5_int32 size_needed; 1436 krb5_int32 commit_point; 1437 int i; 1438 1439 KTCHECKLOCK(id); 1440 retval = krb5_ktfileint_size_entry(context, entry, &size_needed); 1441 if (retval) 1442 return retval; 1443 retval = krb5_ktfileint_find_slot(context, id, &size_needed, &commit_point); 1444 if (retval) 1445 return retval; 1446 1447 /* fseek to synchronise buffered I/O on the key table. */ 1448 /* XXX Without the weird setbuf crock, can we get rid of this now? */ 1449 if (fseek(KTFILEP(id), 0L, SEEK_CUR) < 0) 1450 { 1451 return errno; 1452 } 1453 1454 if (KTVERSION(id) == KRB5_KT_VNO_1) { 1455 count = (krb5_int16) krb5_princ_size(context, entry->principal) + 1; 1456 } else { 1457 count = htons((u_short) krb5_princ_size(context, entry->principal)); 1458 } 1459 1460 if (!xfwrite(&count, sizeof(count), 1, KTFILEP(id))) { 1461 abend: 1462 return KRB5_KT_IOERR; 1463 } 1464 size = krb5_princ_realm(context, entry->principal)->length; 1465 if (KTVERSION(id) != KRB5_KT_VNO_1) 1466 size = htons(size); 1467 if (!xfwrite(&size, sizeof(size), 1, KTFILEP(id))) { 1468 goto abend; 1469 } 1470 if (!xfwrite(krb5_princ_realm(context, entry->principal)->data, sizeof(char), 1471 krb5_princ_realm(context, entry->principal)->length, KTFILEP(id))) { 1472 goto abend; 1473 } 1474 1475 count = (krb5_int16) krb5_princ_size(context, entry->principal); 1476 for (i = 0; i < count; i++) { 1477 princ = krb5_princ_component(context, entry->principal, i); 1478 size = princ->length; 1479 if (KTVERSION(id) != KRB5_KT_VNO_1) 1480 size = htons(size); 1481 if (!xfwrite(&size, sizeof(size), 1, KTFILEP(id))) { 1482 goto abend; 1483 } 1484 if (!xfwrite(princ->data, sizeof(char), princ->length, KTFILEP(id))) { 1485 goto abend; 1486 } 1487 } 1488 1489 /* 1490 * Write out the principal type 1491 */ 1492 if (KTVERSION(id) != KRB5_KT_VNO_1) { 1493 princ_type = htonl(krb5_princ_type(context, entry->principal)); 1494 if (!xfwrite(&princ_type, sizeof(princ_type), 1, KTFILEP(id))) { 1495 goto abend; 1496 } 1497 } 1498 1499 /* 1500 * Fill in the time of day the entry was written to the keytab. 1501 */ 1502 if (krb5_timeofday(context, &entry->timestamp)) { 1503 entry->timestamp = 0; 1504 } 1505 if (KTVERSION(id) == KRB5_KT_VNO_1) 1506 timestamp = entry->timestamp; 1507 else 1508 timestamp = htonl(entry->timestamp); 1509 if (!xfwrite(×tamp, sizeof(timestamp), 1, KTFILEP(id))) { 1510 goto abend; 1511 } 1512 1513 /* key version number */ 1514 vno = (krb5_octet)entry->vno; 1515 if (!xfwrite(&vno, sizeof(vno), 1, KTFILEP(id))) { 1516 goto abend; 1517 } 1518 /* key type */ 1519 if (KTVERSION(id) == KRB5_KT_VNO_1) 1520 enctype = entry->key.enctype; 1521 else 1522 enctype = htons(entry->key.enctype); 1523 if (!xfwrite(&enctype, sizeof(enctype), 1, KTFILEP(id))) { 1524 goto abend; 1525 } 1526 /* key length */ 1527 if (KTVERSION(id) == KRB5_KT_VNO_1) 1528 size = entry->key.length; 1529 else 1530 size = htons(entry->key.length); 1531 if (!xfwrite(&size, sizeof(size), 1, KTFILEP(id))) { 1532 goto abend; 1533 } 1534 if (!xfwrite(entry->key.contents, sizeof(krb5_octet), 1535 entry->key.length, KTFILEP(id))) { 1536 goto abend; 1537 } 1538 1539 if (fflush(KTFILEP(id))) 1540 goto abend; 1541 1542 retval = krb5_sync_disk_file(context, KTFILEP(id)); 1543 1544 if (retval) { 1545 return retval; 1546 } 1547 1548 if (fseek(KTFILEP(id), commit_point, SEEK_SET)) { 1549 return errno; 1550 } 1551 if (KTVERSION(id) != KRB5_KT_VNO_1) 1552 size_needed = htonl(size_needed); 1553 if (!xfwrite(&size_needed, sizeof(size_needed), 1, KTFILEP(id))) { 1554 goto abend; 1555 } 1556 if (fflush(KTFILEP(id))) 1557 goto abend; 1558 retval = krb5_sync_disk_file(context, KTFILEP(id)); 1559 1560 return retval; 1561 } 1562 1563 /* 1564 * Determine the size needed for a file entry for the given 1565 * keytab entry. 1566 */ 1567 krb5_error_code 1568 krb5_ktfileint_size_entry(krb5_context context, krb5_keytab_entry *entry, krb5_int32 *size_needed) 1569 { 1570 krb5_int16 count; 1571 krb5_int32 total_size, i; 1572 krb5_error_code retval = 0; 1573 1574 count = (krb5_int16) krb5_princ_size(context, entry->principal); 1575 1576 total_size = sizeof(count); 1577 total_size += krb5_princ_realm(context, entry->principal)->length + (sizeof(krb5_int16)); 1578 1579 for (i = 0; i < count; i++) { 1580 total_size += krb5_princ_component(context, entry->principal,i)->length 1581 + (sizeof(krb5_int16)); 1582 } 1583 1584 total_size += sizeof(entry->principal->type); 1585 total_size += sizeof(entry->timestamp); 1586 total_size += sizeof(krb5_octet); 1587 total_size += sizeof(krb5_int16); 1588 total_size += sizeof(krb5_int16) + entry->key.length; 1589 1590 *size_needed = total_size; 1591 return retval; 1592 } 1593 1594 /* 1595 * Find and reserve a slot in the file for an entry of the needed size. 1596 * The commit point will be set to the position in the file where the 1597 * the length (sizeof(krb5_int32) bytes) of this node should be written 1598 * when commiting the write. The file position left as a result of this 1599 * call is the position where the actual data should be written. 1600 * 1601 * The size_needed argument may be adjusted if we find a hole that is 1602 * larger than the size needed. (Recall that size_needed will be used 1603 * to commit the write, but that this field must indicate the size of the 1604 * block in the file rather than the size of the actual entry) 1605 */ 1606 krb5_error_code 1607 krb5_ktfileint_find_slot(krb5_context context, krb5_keytab id, krb5_int32 *size_needed, krb5_int32 *commit_point) 1608 { 1609 krb5_int32 size; 1610 krb5_int32 remainder; 1611 krb5_int32 zero_point; 1612 krb5_kt_vno kt_vno; 1613 krb5_boolean found = FALSE; 1614 char iobuf[BUFSIZ]; 1615 1616 KTCHECKLOCK(id); 1617 /* 1618 * Skip over file version number 1619 */ 1620 if (fseek(KTFILEP(id), 0, SEEK_SET)) { 1621 return errno; 1622 } 1623 if (!xfread(&kt_vno, sizeof(kt_vno), 1, KTFILEP(id))) { 1624 return KRB5_KT_IOERR; 1625 } 1626 1627 while (!found) { 1628 *commit_point = ftell(KTFILEP(id)); 1629 if (!xfread(&size, sizeof(size), 1, KTFILEP(id))) { 1630 /* 1631 * Hit the end of file, reserve this slot. 1632 */ 1633 size = 0; 1634 1635 /* fseek to synchronise buffered I/O on the key table. */ 1636 /* XXX Without the weird setbuf hack, can we nuke this now? */ 1637 if (fseek(KTFILEP(id), 0L, SEEK_CUR) < 0) 1638 { 1639 return errno; 1640 } 1641 1642 #ifdef notdef 1643 /* We don't have to do this because htonl(0) == 0 */ 1644 if (KTVERSION(id) != KRB5_KT_VNO_1) 1645 size = htonl(size); 1646 #endif 1647 1648 if (!xfwrite(&size, sizeof(size), 1, KTFILEP(id))) { 1649 return KRB5_KT_IOERR; 1650 } 1651 found = TRUE; 1652 } 1653 1654 if (KTVERSION(id) != KRB5_KT_VNO_1) 1655 size = ntohl(size); 1656 1657 if (size > 0) { 1658 if (fseek(KTFILEP(id), size, SEEK_CUR)) { 1659 return errno; 1660 } 1661 } else if (!found) { 1662 size = -size; 1663 if (size >= *size_needed) { 1664 *size_needed = size; 1665 found = TRUE; 1666 } else if (size > 0) { 1667 /* 1668 * The current hole is not large enough, so skip it 1669 */ 1670 if (fseek(KTFILEP(id), size, SEEK_CUR)) { 1671 return errno; 1672 } 1673 } else { 1674 1675 /* fseek to synchronise buffered I/O on the key table. */ 1676 1677 if (fseek(KTFILEP(id), 0L, SEEK_CUR) < 0) 1678 { 1679 return errno; 1680 } 1681 1682 /* 1683 * Found the end of the file (marked by a 0 length buffer) 1684 * Make sure we zero any trailing data. 1685 */ 1686 zero_point = ftell(KTFILEP(id)); 1687 while ((size = xfread(iobuf, 1, sizeof(iobuf), KTFILEP(id)))) { 1688 if (size != sizeof(iobuf)) { 1689 remainder = size % sizeof(krb5_int32); 1690 if (remainder) { 1691 size += sizeof(krb5_int32) - remainder; 1692 } 1693 } 1694 1695 if (fseek(KTFILEP(id), 0L, SEEK_CUR) < 0) 1696 { 1697 return errno; 1698 } 1699 1700 memset(iobuf, 0, (size_t) size); 1701 xfwrite(iobuf, 1, (size_t) size, KTFILEP(id)); 1702 fflush(KTFILEP(id)); 1703 if (feof(KTFILEP(id))) { 1704 break; 1705 } 1706 1707 if (fseek(KTFILEP(id), 0L, SEEK_CUR) < 0) 1708 { 1709 return errno; 1710 } 1711 1712 } 1713 if (fseek(KTFILEP(id), zero_point, SEEK_SET)) { 1714 return errno; 1715 } 1716 } 1717 } 1718 } 1719 1720 return 0; 1721 } 1722