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