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