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 /* 552 * krb5_ktf_keytab_size() - Determine the size required to externalize 553 * this krb5_keytab variant. 554 */ 555 static krb5_error_code 556 krb5_ktf_keytab_size(krb5_context kcontext, krb5_pointer arg, size_t *sizep) 557 { 558 krb5_error_code kret; 559 krb5_keytab keytab; 560 size_t required; 561 krb5_ktfile_data *ktdata; 562 563 kret = EINVAL; 564 if ((keytab = (krb5_keytab) arg)) { 565 /* 566 * Saving FILE: variants of krb5_keytab requires at minimum: 567 * krb5_int32 for KV5M_KEYTAB 568 * krb5_int32 for length of keytab name. 569 * krb5_int32 for file status. 570 * krb5_int32 for file position. 571 * krb5_int32 for file position. 572 * krb5_int32 for version. 573 * krb5_int32 for KV5M_KEYTAB 574 */ 575 required = sizeof(krb5_int32) * 7; 576 if (keytab->ops && keytab->ops->prefix) 577 required += (strlen(keytab->ops->prefix)+1); 578 579 /* 580 * The keytab name is formed as follows: 581 * <prefix>:<name> 582 * If there's no name, we use a default name so that we have something 583 * to call krb5_keytab_resolve with. 584 */ 585 ktdata = (krb5_ktfile_data *) keytab->data; 586 required += strlen((ktdata && ktdata->name) ? 587 ktdata->name : ktfile_def_name); 588 kret = 0; 589 590 if (!kret) 591 *sizep += required; 592 } 593 return(kret); 594 } 595 596 /* 598 * krb5_ktf_keytab_externalize() - Externalize the krb5_keytab. 599 */ 600 static krb5_error_code 601 krb5_ktf_keytab_externalize(krb5_context kcontext, krb5_pointer arg, krb5_octet **buffer, size_t *lenremain) 602 { 603 krb5_error_code kret; 604 krb5_keytab keytab; 605 size_t required; 606 krb5_octet *bp; 607 size_t remain; 608 krb5_ktfile_data *ktdata; 609 krb5_int32 file_is_open; 610 krb5_int64 file_pos; 611 char *ktname; 612 size_t namelen; 613 const char *fnamep; 614 615 required = 0; 616 bp = *buffer; 617 remain = *lenremain; 618 kret = EINVAL; 619 if ((keytab = (krb5_keytab) arg)) { 620 kret = ENOMEM; 621 if (!krb5_ktf_keytab_size(kcontext, arg, &required) && 622 (required <= remain)) { 623 /* Our identifier */ 624 (void) krb5_ser_pack_int32(KV5M_KEYTAB, &bp, &remain); 625 626 ktdata = (krb5_ktfile_data *) keytab->data; 627 file_is_open = 0; 628 file_pos = 0; 629 630 /* Calculate the length of the name */ 631 namelen = (keytab->ops && keytab->ops->prefix) ? 632 strlen(keytab->ops->prefix)+1 : 0; 633 if (ktdata && ktdata->name) 634 fnamep = ktdata->name; 635 else 636 fnamep = ktfile_def_name; 637 namelen += (strlen(fnamep)+1); 638 639 if ((ktname = (char *) malloc(namelen))) { 640 /* Format the keytab name. */ 641 if (keytab->ops && keytab->ops->prefix) 642 sprintf(ktname, "%s:%s", keytab->ops->prefix, fnamep); 643 644 else 645 strcpy(ktname, fnamep); 646 647 /* Fill in the file-specific keytab information. */ 648 if (ktdata) { 649 if (ktdata->openf) { 650 long fpos; 651 int fflags = 0; 652 653 file_is_open = 1; 654 #if !defined(_WIN32) 655 fflags = fcntl(fileno(ktdata->openf), F_GETFL, 0); 656 if (fflags > 0) 657 file_is_open |= ((fflags & O_ACCMODE) << 1); 658 #else 659 file_is_open = 0; 660 #endif 661 fpos = ftell(ktdata->openf); 662 file_pos = fpos; /* XX range check? */ 663 } 664 } 665 666 /* Put the length of the file name */ 667 (void) krb5_ser_pack_int32((krb5_int32) strlen(ktname), 668 &bp, &remain); 669 670 /* Put the name */ 671 (void) krb5_ser_pack_bytes((krb5_octet *) ktname, 672 strlen(ktname), 673 &bp, &remain); 674 675 /* Put the file open flag */ 676 (void) krb5_ser_pack_int32(file_is_open, &bp, &remain); 677 678 /* Put the file position */ 679 (void) krb5_ser_pack_int64(file_pos, &bp, &remain); 680 681 /* Put the version */ 682 (void) krb5_ser_pack_int32((krb5_int32) ((ktdata) ? 683 ktdata->version : 0), 684 &bp, &remain); 685 686 /* Put the trailer */ 687 (void) krb5_ser_pack_int32(KV5M_KEYTAB, &bp, &remain); 688 kret = 0; 689 *buffer = bp; 690 *lenremain = remain; 691 free(ktname); 692 } 693 } 694 } 695 return(kret); 696 } 697 698 /* 700 * krb5_ktf_keytab_internalize() - Internalize the krb5_ktf_keytab. 701 */ 702 static krb5_error_code 703 krb5_ktf_keytab_internalize(krb5_context kcontext, krb5_pointer *argp, krb5_octet **buffer, size_t *lenremain) 704 { 705 krb5_error_code kret; 706 krb5_keytab keytab; 707 krb5_int32 ibuf; 708 krb5_octet *bp; 709 size_t remain; 710 char *ktname; 711 krb5_ktfile_data *ktdata; 712 krb5_int32 file_is_open; 713 krb5_int64 foff; 714 715 bp = *buffer; 716 remain = *lenremain; 717 kret = EINVAL; 718 /* Read our magic number */ 719 if (krb5_ser_unpack_int32(&ibuf, &bp, &remain)) 720 ibuf = 0; 721 if (ibuf == KV5M_KEYTAB) { 722 kret = ENOMEM; 723 724 /* Get the length of the keytab name */ 725 kret = krb5_ser_unpack_int32(&ibuf, &bp, &remain); 726 727 if (!kret && 728 (ktname = (char *) malloc((size_t) (ibuf+1))) && 729 !(kret = krb5_ser_unpack_bytes((krb5_octet *) ktname, 730 (size_t) ibuf, 731 &bp, &remain))) { 732 ktname[ibuf] = '\0'; 733 kret = krb5_kt_resolve(kcontext, ktname, &keytab); 734 if (!kret) { 735 kret = ENOMEM; 736 ktdata = (krb5_ktfile_data *) keytab->data; 737 if (!ktdata) { 738 /* XXX */ 739 keytab->data = (void *) malloc(sizeof(krb5_ktfile_data)); 740 ktdata = (krb5_ktfile_data *) keytab->data; 741 memset(ktdata, 0, sizeof(krb5_ktfile_data)); 742 if (strchr(ktname, (int) ':')) 743 ktdata->name = strdup(strchr(ktname, (int) ':')+1); 744 else 745 ktdata->name = strdup(ktname); 746 } 747 if (ktdata) { 748 if (remain >= (sizeof(krb5_int32)*5)) { 749 (void) krb5_ser_unpack_int32(&file_is_open, 750 &bp, &remain); 751 (void) krb5_ser_unpack_int64(&foff, &bp, &remain); 752 (void) krb5_ser_unpack_int32(&ibuf, &bp, &remain); 753 ktdata->version = (int) ibuf; 754 755 (void) krb5_ser_unpack_int32(&ibuf, &bp, &remain); 756 if (ibuf == KV5M_KEYTAB) { 757 if (file_is_open) { 758 int fmode; 759 long fpos; 760 761 #if !defined(_WIN32) 762 fmode = (file_is_open >> 1) & O_ACCMODE; 763 #else 764 fmode = 0; 765 #endif 766 if (fmode) 767 kret = krb5_ktfileint_openw(kcontext, 768 keytab); 769 else 770 kret = krb5_ktfileint_openr(kcontext, 771 keytab); 772 if (!kret) { 773 fpos = foff; /* XX range check? */ 774 fseek(KTFILEP(keytab), fpos, SEEK_SET); 775 } 776 } 777 kret = 0; 778 } 779 else 780 kret = EINVAL; 781 } 782 } 783 if (kret) { 784 if (keytab->data) { 785 if (KTFILENAME(keytab)) 786 krb5_xfree(KTFILENAME(keytab)); 787 krb5_xfree(keytab->data); 788 } 789 krb5_xfree(keytab); 790 } 791 else { 792 *buffer = bp; 793 *lenremain = remain; 794 *argp = (krb5_pointer) keytab; 795 } 796 } 797 free(ktname); 798 } 799 } 800 return(kret); 801 } 802 803 /* 804 * This is an implementation specific resolver. It returns a keytab id 805 * initialized with file keytab routines. 806 */ 807 808 krb5_error_code KRB5_CALLCONV 809 krb5_ktfile_wresolve(krb5_context context, const char *name, krb5_keytab *id) 810 { 811 krb5_ktfile_data *data; 812 krb5_error_code err; 813 814 if ((*id = (krb5_keytab) malloc(sizeof(**id))) == NULL) 815 return(ENOMEM); 816 817 (*id)->ops = &krb5_ktf_writable_ops; 818 if ((data = (krb5_ktfile_data *)malloc(sizeof(krb5_ktfile_data))) == NULL) { 819 krb5_xfree(*id); 820 return(ENOMEM); 821 } 822 823 err = k5_mutex_init(&data->lock); 824 if (err) { 825 krb5_xfree(data); 826 krb5_xfree(*id); 827 return err; 828 } 829 830 if ((data->name = (char *)calloc(strlen(name) + 1, sizeof(char))) == NULL) { 831 k5_mutex_destroy(&data->lock); 832 krb5_xfree(data); 833 krb5_xfree(*id); 834 return(ENOMEM); 835 } 836 837 (void) strcpy(data->name, name); 838 data->openf = 0; 839 data->version = 0; 840 841 (*id)->data = (krb5_pointer)data; 842 (*id)->magic = KV5M_KEYTAB; 843 return(0); 844 } 845 846 847 /* 848 * krb5_ktfile_add() 849 */ 850 851 krb5_error_code KRB5_CALLCONV 852 krb5_ktfile_add(krb5_context context, krb5_keytab id, krb5_keytab_entry *entry) 853 { 854 krb5_error_code retval; 855 856 retval = KTLOCK(id); 857 if (retval) 858 return retval; 859 if ((retval = krb5_ktfileint_openw(context, id))) { 860 KTUNLOCK(id); 861 return retval; 862 } 863 if (fseek(KTFILEP(id), 0, 2) == -1) { 864 KTUNLOCK(id); 865 return KRB5_KT_END; 866 } 867 retval = krb5_ktfileint_write_entry(context, id, entry); 868 krb5_ktfileint_close(context, id); 869 KTUNLOCK(id); 870 return retval; 871 } 872 873 /* 874 * krb5_ktfile_remove() 875 */ 876 877 krb5_error_code KRB5_CALLCONV 878 krb5_ktfile_remove(krb5_context context, krb5_keytab id, krb5_keytab_entry *entry) 879 { 880 krb5_keytab_entry cur_entry; 881 krb5_error_code kerror; 882 krb5_int32 delete_point; 883 884 kerror = KTLOCK(id); 885 if (kerror) 886 return kerror; 887 888 if ((kerror = krb5_ktfileint_openw(context, id))) { 889 KTUNLOCK(id); 890 return kerror; 891 } 892 893 /* 894 * For efficiency and simplicity, we'll use a while true that 895 * is exited with a break statement. 896 */ 897 while (TRUE) { 898 if ((kerror = krb5_ktfileint_internal_read_entry(context, id, 899 &cur_entry, 900 &delete_point))) 901 break; 902 903 if ((entry->vno == cur_entry.vno) && 904 (entry->key.enctype == cur_entry.key.enctype) && 905 krb5_principal_compare(context, entry->principal, cur_entry.principal)) { 906 /* found a match */ 907 krb5_kt_free_entry(context, &cur_entry); 908 break; 909 } 910 krb5_kt_free_entry(context, &cur_entry); 911 } 912 913 if (kerror == KRB5_KT_END) 914 kerror = KRB5_KT_NOTFOUND; 915 916 if (kerror) { 917 (void) krb5_ktfileint_close(context, id); 918 KTUNLOCK(id); 919 return kerror; 920 } 921 922 kerror = krb5_ktfileint_delete_entry(context, id, delete_point); 923 924 if (kerror) { 925 (void) krb5_ktfileint_close(context, id); 926 } else { 927 kerror = krb5_ktfileint_close(context, id); 928 } 929 KTUNLOCK(id); 930 return kerror; 931 } 932 933 /* 934 * krb5_ktf_ops 935 */ 936 937 const struct _krb5_kt_ops krb5_ktf_ops = { 938 0, 939 "FILE", /* Prefix -- this string should not appear anywhere else! */ 940 krb5_ktfile_resolve, 941 krb5_ktfile_get_name, 942 krb5_ktfile_close, 943 krb5_ktfile_get_entry, 944 krb5_ktfile_start_seq_get, 945 krb5_ktfile_get_next, 946 krb5_ktfile_end_get, 947 0, 948 0, 949 &krb5_ktfile_ser_entry 950 }; 951 952 /* 953 * krb5_ktf_writable_ops 954 */ 955 956 const struct _krb5_kt_ops krb5_ktf_writable_ops = { 957 0, 958 "WRFILE", /* Prefix -- this string should not appear anywhere else! */ 959 krb5_ktfile_wresolve, 960 krb5_ktfile_get_name, 961 krb5_ktfile_close, 962 krb5_ktfile_get_entry, 963 krb5_ktfile_start_seq_get, 964 krb5_ktfile_get_next, 965 krb5_ktfile_end_get, 966 krb5_ktfile_add, 967 krb5_ktfile_remove, 968 &krb5_ktfile_ser_entry 969 }; 970 971 /* 972 * krb5_kt_dfl_ops 973 */ 974 975 const krb5_kt_ops krb5_kt_dfl_ops = { 976 0, 977 "FILE", /* Prefix -- this string should not appear anywhere else! */ 978 krb5_ktfile_resolve, 979 krb5_ktfile_get_name, 980 krb5_ktfile_close, 981 krb5_ktfile_get_entry, 982 krb5_ktfile_start_seq_get, 983 krb5_ktfile_get_next, 984 krb5_ktfile_end_get, 985 0, 986 0, 987 &krb5_ktfile_ser_entry 988 }; 989 990 /* 991 * lib/krb5/keytab/file/ktf_util.c 992 * 993 * Copyright (c) Hewlett-Packard Company 1991 994 * Released to the Massachusetts Institute of Technology for inclusion 995 * in the Kerberos source code distribution. 996 * 997 * Copyright 1990,1991 by the Massachusetts Institute of Technology. 998 * All Rights Reserved. 999 * 1000 * Export of this software from the United States of America may 1001 * require a specific license from the United States Government. 1002 * It is the responsibility of any person or organization contemplating 1003 * export to obtain such a license before exporting. 1004 * 1005 * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and 1006 * distribute this software and its documentation for any purpose and 1007 * without fee is hereby granted, provided that the above copyright 1008 * notice appear in all copies and that both that copyright notice and 1009 * this permission notice appear in supporting documentation, and that 1010 * the name of M.I.T. not be used in advertising or publicity pertaining 1011 * to distribution of the software without specific, written prior 1012 * permission. Furthermore if you modify this software you must label 1013 * your software as modified software and not distribute it in such a 1014 * fashion that it might be confused with the original M.I.T. software. 1015 * M.I.T. makes no representations about the suitability of 1016 * this software for any purpose. It is provided "as is" without express 1017 * or implied warranty. 1018 * 1019 * 1020 * This function contains utilities for the file based implementation of 1021 * the keytab. There are no public functions in this file. 1022 * 1023 * This file is the only one that has knowledge of the format of a 1024 * keytab file. 1025 * 1026 * The format is as follows: 1027 * 1028 * <file format vno> 1029 * <record length> 1030 * principal timestamp vno key 1031 * <record length> 1032 * principal timestamp vno key 1033 * .... 1034 * 1035 * A length field (sizeof(krb5_int32)) exists between entries. When this 1036 * length is positive it indicates an active entry, when negative a hole. 1037 * The length indicates the size of the block in the file (this may be 1038 * larger than the size of the next record, since we are using a first 1039 * fit algorithm for re-using holes and the first fit may be larger than 1040 * the entry we are writing). Another (compatible) implementation could 1041 * break up holes when allocating them to smaller entries to minimize 1042 * wasted space. (Such an implementation should also coalesce adjacent 1043 * holes to reduce fragmentation). This implementation does neither. 1044 * 1045 * There are no separators between fields of an entry. 1046 * A principal is a length-encoded array of length-encoded strings. The 1047 * length is a krb5_int16 in each case. The specific format, then, is 1048 * multiple entries concatinated with no separators. An entry has this 1049 * exact format: 1050 * 1051 * sizeof(krb5_int16) bytes for number of components in the principal; 1052 * then, each component listed in ordser. 1053 * For each component, sizeof(krb5_int16) bytes for the number of bytes 1054 * in the component, followed by the component. 1055 * sizeof(krb5_int32) for the principal type (for KEYTAB V2 and higher) 1056 * sizeof(krb5_int32) bytes for the timestamp 1057 * sizeof(krb5_octet) bytes for the key version number 1058 * sizeof(krb5_int16) bytes for the enctype 1059 * sizeof(krb5_int32) bytes for the key length, followed by the key 1060 */ 1061 1062 #ifndef SEEK_SET 1063 #define SEEK_SET 0 1064 #define SEEK_CUR 1 1065 #endif 1066 1067 typedef krb5_int16 krb5_kt_vno; 1068 1069 #define krb5_kt_default_vno ((krb5_kt_vno)KRB5_KT_DEFAULT_VNO) 1070 1071 #define xfwrite(a, b, c, d) fwrite((char *)a, b, (unsigned) c, d) 1072 #define xfread(a, b, c, d) fread((char *)a, b, (unsigned) c, d) 1073 1074 #ifdef ANSI_STDIO 1075 /* Solaris Kerberos */ 1076 static char *const fopen_mode_rbplus= "rb+F"; 1077 static char *const fopen_mode_rb = "rbF"; 1078 #else 1079 /* Solaris Kerberos */ 1080 static char *const fopen_mode_rbplus= "r+F"; 1081 static char *const fopen_mode_rb = "rF"; 1082 #endif 1083 1084 static krb5_error_code 1085 krb5_ktfileint_open(krb5_context context, krb5_keytab id, int mode) 1086 { 1087 krb5_error_code kerror; 1088 krb5_kt_vno kt_vno; 1089 int writevno = 0; 1090 1091 KTCHECKLOCK(id); 1092 errno = 0; 1093 KTFILEP(id) = fopen(KTFILENAME(id), 1094 (mode == KRB5_LOCKMODE_EXCLUSIVE) ? 1095 fopen_mode_rbplus : fopen_mode_rb); 1096 if (!KTFILEP(id)) { 1097 if ((mode == KRB5_LOCKMODE_EXCLUSIVE) && (errno == ENOENT)) { 1098 /* try making it first time around */ 1099 krb5_create_secure_file(context, KTFILENAME(id)); 1100 errno = 0; 1101 KTFILEP(id) = fopen(KTFILENAME(id), fopen_mode_rbplus); 1102 if (!KTFILEP(id)) 1103 return errno ? errno : EMFILE; 1104 writevno = 1; 1105 } else /* some other error */ 1106 return errno ? errno : EMFILE; 1107 } 1108 if ((kerror = krb5_lock_file(context, fileno(KTFILEP(id)), mode))) { 1109 (void) fclose(KTFILEP(id)); 1110 KTFILEP(id) = 0; 1111 return kerror; 1112 } 1113 /* assume ANSI or BSD-style stdio */ 1114 setbuf(KTFILEP(id), KTFILEBUFP(id)); 1115 1116 /* get the vno and verify it */ 1117 if (writevno) { 1118 kt_vno = htons(krb5_kt_default_vno); 1119 KTVERSION(id) = krb5_kt_default_vno; 1120 if (!xfwrite(&kt_vno, sizeof(kt_vno), 1, KTFILEP(id))) { 1121 kerror = errno; 1122 (void) krb5_unlock_file(context, fileno(KTFILEP(id))); 1123 (void) fclose(KTFILEP(id)); 1124 return kerror; 1125 } 1126 } else { 1127 /* gotta verify it instead... */ 1128 if (!xfread(&kt_vno, sizeof(kt_vno), 1, KTFILEP(id))) { 1129 if (feof(KTFILEP(id))) 1130 kerror = KRB5_KEYTAB_BADVNO; 1131 else 1132 kerror = errno; 1133 (void) krb5_unlock_file(context, fileno(KTFILEP(id))); 1134 (void) fclose(KTFILEP(id)); 1135 return kerror; 1136 } 1137 kt_vno = KTVERSION(id) = ntohs(kt_vno); 1138 if ((kt_vno != KRB5_KT_VNO) && 1139 (kt_vno != KRB5_KT_VNO_1)) { 1140 (void) krb5_unlock_file(context, fileno(KTFILEP(id))); 1141 (void) fclose(KTFILEP(id)); 1142 return KRB5_KEYTAB_BADVNO; 1143 } 1144 } 1145 return 0; 1146 } 1147 1148 krb5_error_code 1149 krb5_ktfileint_openr(krb5_context context, krb5_keytab id) 1150 { 1151 return krb5_ktfileint_open(context, id, KRB5_LOCKMODE_SHARED); 1152 } 1153 1154 krb5_error_code 1155 krb5_ktfileint_openw(krb5_context context, krb5_keytab id) 1156 { 1157 return krb5_ktfileint_open(context, id, KRB5_LOCKMODE_EXCLUSIVE); 1158 } 1159 1160 krb5_error_code 1161 krb5_ktfileint_close(krb5_context context, krb5_keytab id) 1162 { 1163 krb5_error_code kerror; 1164 1165 KTCHECKLOCK(id); 1166 if (!KTFILEP(id)) 1167 return 0; 1168 kerror = krb5_unlock_file(context, fileno(KTFILEP(id))); 1169 (void) fclose(KTFILEP(id)); 1170 KTFILEP(id) = 0; 1171 return kerror; 1172 } 1173 1174 krb5_error_code 1175 krb5_ktfileint_delete_entry(krb5_context context, krb5_keytab id, krb5_int32 delete_point) 1176 { 1177 krb5_int32 size; 1178 krb5_int32 len; 1179 char iobuf[BUFSIZ]; 1180 1181 KTCHECKLOCK(id); 1182 if (fseek(KTFILEP(id), delete_point, SEEK_SET)) { 1183 return errno; 1184 } 1185 if (!xfread(&size, sizeof(size), 1, KTFILEP(id))) { 1186 return KRB5_KT_END; 1187 } 1188 if (KTVERSION(id) != KRB5_KT_VNO_1) 1189 size = ntohl(size); 1190 1191 if (size > 0) { 1192 krb5_int32 minus_size = -size; 1193 if (KTVERSION(id) != KRB5_KT_VNO_1) 1194 minus_size = htonl(minus_size); 1195 1196 if (fseek(KTFILEP(id), delete_point, SEEK_SET)) { 1197 return errno; 1198 } 1199 1200 if (!xfwrite(&minus_size, sizeof(minus_size), 1, KTFILEP(id))) { 1201 return KRB5_KT_IOERR; 1202 } 1203 1204 if (size < BUFSIZ) { 1205 len = size; 1206 } else { 1207 len = BUFSIZ; 1208 } 1209 1210 memset(iobuf, 0, (size_t) len); 1211 while (size > 0) { 1212 xfwrite(iobuf, 1, (size_t) len, KTFILEP(id)); 1213 size -= len; 1214 if (size < len) { 1215 len = size; 1216 } 1217 } 1218 1219 return krb5_sync_disk_file(context, KTFILEP(id)); 1220 } 1221 1222 return 0; 1223 } 1224 1225 krb5_error_code 1226 krb5_ktfileint_internal_read_entry(krb5_context context, krb5_keytab id, krb5_keytab_entry *ret_entry, krb5_int32 *delete_point) 1227 { 1228 krb5_octet vno; 1229 krb5_int16 count; 1230 unsigned int u_count, u_princ_size; 1231 krb5_int16 enctype; 1232 krb5_int16 princ_size; 1233 register int i; 1234 krb5_int32 size; 1235 krb5_int32 start_pos; 1236 krb5_error_code error; 1237 char *tmpdata; 1238 krb5_data *princ; 1239 1240 KTCHECKLOCK(id); 1241 memset(ret_entry, 0, sizeof(krb5_keytab_entry)); 1242 ret_entry->magic = KV5M_KEYTAB_ENTRY; 1243 1244 /* fseek to synchronise buffered I/O on the key table. */ 1245 1246 if (fseek(KTFILEP(id), 0L, SEEK_CUR) < 0) 1247 { 1248 return errno; 1249 } 1250 1251 do { 1252 *delete_point = ftell(KTFILEP(id)); 1253 if (!xfread(&size, sizeof(size), 1, KTFILEP(id))) { 1254 return KRB5_KT_END; 1255 } 1256 if (KTVERSION(id) != KRB5_KT_VNO_1) 1257 size = ntohl(size); 1258 1259 if (size < 0) { 1260 if (fseek(KTFILEP(id), -size, SEEK_CUR)) { 1261 return errno; 1262 } 1263 } 1264 } while (size < 0); 1265 1266 if (size == 0) { 1267 return KRB5_KT_END; 1268 } 1269 1270 start_pos = ftell(KTFILEP(id)); 1271 1272 /* deal with guts of parsing... */ 1273 1274 /* first, int16 with #princ components */ 1275 if (!xfread(&count, sizeof(count), 1, KTFILEP(id))) 1276 return KRB5_KT_END; 1277 if (KTVERSION(id) == KRB5_KT_VNO_1) { 1278 count -= 1; /* V1 includes the realm in the count */ 1279 } else { 1280 count = ntohs(count); 1281 } 1282 if (!count || (count < 0)) 1283 return KRB5_KT_END; 1284 ret_entry->principal = (krb5_principal)malloc(sizeof(krb5_principal_data)); 1285 if (!ret_entry->principal) 1286 return ENOMEM; 1287 1288 u_count = count; 1289 ret_entry->principal->magic = KV5M_PRINCIPAL; 1290 ret_entry->principal->length = u_count; 1291 ret_entry->principal->data = (krb5_data *) 1292 calloc(u_count, sizeof(krb5_data)); 1293 if (!ret_entry->principal->data) { 1294 free(ret_entry->principal); 1295 ret_entry->principal = 0; 1296 return ENOMEM; 1297 } 1298 1299 /* Now, get the realm data */ 1300 if (!xfread(&princ_size, sizeof(princ_size), 1, KTFILEP(id))) { 1301 error = KRB5_KT_END; 1302 goto fail; 1303 } 1304 if (KTVERSION(id) != KRB5_KT_VNO_1) 1305 princ_size = ntohs(princ_size); 1306 if (!princ_size || (princ_size < 0)) { 1307 error = KRB5_KT_END; 1308 goto fail; 1309 } 1310 u_princ_size = princ_size; 1311 1312 krb5_princ_set_realm_length(context, ret_entry->principal, u_princ_size); 1313 tmpdata = malloc(u_princ_size+1); 1314 if (!tmpdata) { 1315 error = ENOMEM; 1316 goto fail; 1317 } 1318 if (fread(tmpdata, 1, u_princ_size, KTFILEP(id)) != (size_t) princ_size) { 1319 free(tmpdata); 1320 error = KRB5_KT_END; 1321 goto fail; 1322 } 1323 tmpdata[princ_size] = 0; /* Some things might be expecting null */ 1324 /* termination... ``Be conservative in */ 1325 /* what you send out'' */ 1326 krb5_princ_set_realm_data(context, ret_entry->principal, tmpdata); 1327 1328 for (i = 0; i < count; i++) { 1329 princ = krb5_princ_component(context, ret_entry->principal, i); 1330 if (!xfread(&princ_size, sizeof(princ_size), 1, KTFILEP(id))) { 1331 error = KRB5_KT_END; 1332 goto fail; 1333 } 1334 if (KTVERSION(id) != KRB5_KT_VNO_1) 1335 princ_size = ntohs(princ_size); 1336 if (!princ_size || (princ_size < 0)) { 1337 error = KRB5_KT_END; 1338 goto fail; 1339 } 1340 1341 u_princ_size = princ_size; 1342 princ->length = u_princ_size; 1343 princ->data = malloc(u_princ_size+1); 1344 if (!princ->data) { 1345 error = ENOMEM; 1346 goto fail; 1347 } 1348 if (!xfread(princ->data, sizeof(char), u_princ_size, KTFILEP(id))) { 1349 error = KRB5_KT_END; 1350 goto fail; 1351 } 1352 princ->data[princ_size] = 0; /* Null terminate */ 1353 } 1354 1355 /* read in the principal type, if we can get it */ 1356 if (KTVERSION(id) != KRB5_KT_VNO_1) { 1357 if (!xfread(&ret_entry->principal->type, 1358 sizeof(ret_entry->principal->type), 1, KTFILEP(id))) { 1359 error = KRB5_KT_END; 1360 goto fail; 1361 } 1362 ret_entry->principal->type = ntohl(ret_entry->principal->type); 1363 } 1364 1365 /* read in the timestamp */ 1366 if (!xfread(&ret_entry->timestamp, sizeof(ret_entry->timestamp), 1, KTFILEP(id))) { 1367 error = KRB5_KT_END; 1368 goto fail; 1369 } 1370 if (KTVERSION(id) != KRB5_KT_VNO_1) 1371 ret_entry->timestamp = ntohl(ret_entry->timestamp); 1372 1373 /* read in the version number */ 1374 if (!xfread(&vno, sizeof(vno), 1, KTFILEP(id))) { 1375 error = KRB5_KT_END; 1376 goto fail; 1377 } 1378 ret_entry->vno = (krb5_kvno)vno; 1379 1380 /* key type */ 1381 if (!xfread(&enctype, sizeof(enctype), 1, KTFILEP(id))) { 1382 error = KRB5_KT_END; 1383 goto fail; 1384 } 1385 ret_entry->key.enctype = (krb5_enctype)enctype; 1386 1387 if (KTVERSION(id) != KRB5_KT_VNO_1) 1388 ret_entry->key.enctype = ntohs(ret_entry->key.enctype); 1389 1390 /* key contents */ 1391 ret_entry->key.magic = KV5M_KEYBLOCK; 1392 1393 if (!xfread(&count, sizeof(count), 1, KTFILEP(id))) { 1394 error = KRB5_KT_END; 1395 goto fail; 1396 } 1397 if (KTVERSION(id) != KRB5_KT_VNO_1) 1398 count = ntohs(count); 1399 if (!count || (count < 0)) { 1400 error = KRB5_KT_END; 1401 goto fail; 1402 } 1403 1404 u_count = count; 1405 ret_entry->key.length = u_count; 1406 1407 ret_entry->key.contents = (krb5_octet *)malloc(u_count); 1408 if (!ret_entry->key.contents) { 1409 error = ENOMEM; 1410 goto fail; 1411 } 1412 if (!xfread(ret_entry->key.contents, sizeof(krb5_octet), count, 1413 KTFILEP(id))) { 1414 error = KRB5_KT_END; 1415 goto fail; 1416 } 1417 1418 /* 1419 * Reposition file pointer to the next inter-record length field. 1420 */ 1421 fseek(KTFILEP(id), start_pos + size, SEEK_SET); 1422 return 0; 1423 fail: 1424 1425 for (i = 0; i < krb5_princ_size(context, ret_entry->principal); i++) { 1426 princ = krb5_princ_component(context, ret_entry->principal, i); 1427 if (princ->data) 1428 free(princ->data); 1429 } 1430 free(ret_entry->principal->data); 1431 ret_entry->principal->data = 0; 1432 free(ret_entry->principal); 1433 ret_entry->principal = 0; 1434 return error; 1435 } 1436 1437 krb5_error_code 1438 krb5_ktfileint_read_entry(krb5_context context, krb5_keytab id, krb5_keytab_entry *entryp) 1439 { 1440 krb5_int32 delete_point; 1441 1442 return krb5_ktfileint_internal_read_entry(context, id, entryp, &delete_point); 1443 } 1444 1445 krb5_error_code 1446 krb5_ktfileint_write_entry(krb5_context context, krb5_keytab id, krb5_keytab_entry *entry) 1447 { 1448 krb5_octet vno; 1449 krb5_data *princ; 1450 krb5_int16 count, size, enctype; 1451 krb5_error_code retval = 0; 1452 krb5_timestamp timestamp; 1453 krb5_int32 princ_type; 1454 krb5_int32 size_needed; 1455 krb5_int32 commit_point; 1456 int i; 1457 1458 KTCHECKLOCK(id); 1459 retval = krb5_ktfileint_size_entry(context, entry, &size_needed); 1460 if (retval) 1461 return retval; 1462 retval = krb5_ktfileint_find_slot(context, id, &size_needed, &commit_point); 1463 if (retval) 1464 return retval; 1465 1466 /* fseek to synchronise buffered I/O on the key table. */ 1467 /* XXX Without the weird setbuf crock, can we get rid of this now? */ 1468 if (fseek(KTFILEP(id), 0L, SEEK_CUR) < 0) 1469 { 1470 return errno; 1471 } 1472 1473 if (KTVERSION(id) == KRB5_KT_VNO_1) { 1474 count = (krb5_int16) krb5_princ_size(context, entry->principal) + 1; 1475 } else { 1476 count = htons((u_short) krb5_princ_size(context, entry->principal)); 1477 } 1478 1479 if (!xfwrite(&count, sizeof(count), 1, KTFILEP(id))) { 1480 abend: 1481 return KRB5_KT_IOERR; 1482 } 1483 size = krb5_princ_realm(context, entry->principal)->length; 1484 if (KTVERSION(id) != KRB5_KT_VNO_1) 1485 size = htons(size); 1486 if (!xfwrite(&size, sizeof(size), 1, KTFILEP(id))) { 1487 goto abend; 1488 } 1489 if (!xfwrite(krb5_princ_realm(context, entry->principal)->data, sizeof(char), 1490 krb5_princ_realm(context, entry->principal)->length, KTFILEP(id))) { 1491 goto abend; 1492 } 1493 1494 count = (krb5_int16) krb5_princ_size(context, entry->principal); 1495 for (i = 0; i < count; i++) { 1496 princ = krb5_princ_component(context, entry->principal, i); 1497 size = princ->length; 1498 if (KTVERSION(id) != KRB5_KT_VNO_1) 1499 size = htons(size); 1500 if (!xfwrite(&size, sizeof(size), 1, KTFILEP(id))) { 1501 goto abend; 1502 } 1503 if (!xfwrite(princ->data, sizeof(char), princ->length, KTFILEP(id))) { 1504 goto abend; 1505 } 1506 } 1507 1508 /* 1509 * Write out the principal type 1510 */ 1511 if (KTVERSION(id) != KRB5_KT_VNO_1) { 1512 princ_type = htonl(krb5_princ_type(context, entry->principal)); 1513 if (!xfwrite(&princ_type, sizeof(princ_type), 1, KTFILEP(id))) { 1514 goto abend; 1515 } 1516 } 1517 1518 /* 1519 * Fill in the time of day the entry was written to the keytab. 1520 */ 1521 if (krb5_timeofday(context, &entry->timestamp)) { 1522 entry->timestamp = 0; 1523 } 1524 if (KTVERSION(id) == KRB5_KT_VNO_1) 1525 timestamp = entry->timestamp; 1526 else 1527 timestamp = htonl(entry->timestamp); 1528 if (!xfwrite(×tamp, sizeof(timestamp), 1, KTFILEP(id))) { 1529 goto abend; 1530 } 1531 1532 /* key version number */ 1533 vno = (krb5_octet)entry->vno; 1534 if (!xfwrite(&vno, sizeof(vno), 1, KTFILEP(id))) { 1535 goto abend; 1536 } 1537 /* key type */ 1538 if (KTVERSION(id) == KRB5_KT_VNO_1) 1539 enctype = entry->key.enctype; 1540 else 1541 enctype = htons(entry->key.enctype); 1542 if (!xfwrite(&enctype, sizeof(enctype), 1, KTFILEP(id))) { 1543 goto abend; 1544 } 1545 /* key length */ 1546 if (KTVERSION(id) == KRB5_KT_VNO_1) 1547 size = entry->key.length; 1548 else 1549 size = htons(entry->key.length); 1550 if (!xfwrite(&size, sizeof(size), 1, KTFILEP(id))) { 1551 goto abend; 1552 } 1553 if (!xfwrite(entry->key.contents, sizeof(krb5_octet), 1554 entry->key.length, KTFILEP(id))) { 1555 goto abend; 1556 } 1557 1558 if (fflush(KTFILEP(id))) 1559 goto abend; 1560 1561 retval = krb5_sync_disk_file(context, KTFILEP(id)); 1562 1563 if (retval) { 1564 return retval; 1565 } 1566 1567 if (fseek(KTFILEP(id), commit_point, SEEK_SET)) { 1568 return errno; 1569 } 1570 if (KTVERSION(id) != KRB5_KT_VNO_1) 1571 size_needed = htonl(size_needed); 1572 if (!xfwrite(&size_needed, sizeof(size_needed), 1, KTFILEP(id))) { 1573 goto abend; 1574 } 1575 if (fflush(KTFILEP(id))) 1576 goto abend; 1577 retval = krb5_sync_disk_file(context, KTFILEP(id)); 1578 1579 return retval; 1580 } 1581 1582 /* 1583 * Determine the size needed for a file entry for the given 1584 * keytab entry. 1585 */ 1586 krb5_error_code 1587 krb5_ktfileint_size_entry(krb5_context context, krb5_keytab_entry *entry, krb5_int32 *size_needed) 1588 { 1589 krb5_int16 count; 1590 krb5_int32 total_size, i; 1591 krb5_error_code retval = 0; 1592 1593 count = (krb5_int16) krb5_princ_size(context, entry->principal); 1594 1595 total_size = sizeof(count); 1596 total_size += krb5_princ_realm(context, entry->principal)->length + (sizeof(krb5_int16)); 1597 1598 for (i = 0; i < count; i++) { 1599 total_size += krb5_princ_component(context, entry->principal,i)->length 1600 + (sizeof(krb5_int16)); 1601 } 1602 1603 total_size += sizeof(entry->principal->type); 1604 total_size += sizeof(entry->timestamp); 1605 total_size += sizeof(krb5_octet); 1606 total_size += sizeof(krb5_int16); 1607 total_size += sizeof(krb5_int16) + entry->key.length; 1608 1609 *size_needed = total_size; 1610 return retval; 1611 } 1612 1613 /* 1614 * Find and reserve a slot in the file for an entry of the needed size. 1615 * The commit point will be set to the position in the file where the 1616 * the length (sizeof(krb5_int32) bytes) of this node should be written 1617 * when commiting the write. The file position left as a result of this 1618 * call is the position where the actual data should be written. 1619 * 1620 * The size_needed argument may be adjusted if we find a hole that is 1621 * larger than the size needed. (Recall that size_needed will be used 1622 * to commit the write, but that this field must indicate the size of the 1623 * block in the file rather than the size of the actual entry) 1624 */ 1625 krb5_error_code 1626 krb5_ktfileint_find_slot(krb5_context context, krb5_keytab id, krb5_int32 *size_needed, krb5_int32 *commit_point) 1627 { 1628 krb5_int32 size; 1629 krb5_int32 remainder; 1630 krb5_int32 zero_point; 1631 krb5_kt_vno kt_vno; 1632 krb5_boolean found = FALSE; 1633 char iobuf[BUFSIZ]; 1634 1635 KTCHECKLOCK(id); 1636 /* 1637 * Skip over file version number 1638 */ 1639 if (fseek(KTFILEP(id), 0, SEEK_SET)) { 1640 return errno; 1641 } 1642 if (!xfread(&kt_vno, sizeof(kt_vno), 1, KTFILEP(id))) { 1643 return KRB5_KT_IOERR; 1644 } 1645 1646 while (!found) { 1647 *commit_point = ftell(KTFILEP(id)); 1648 if (!xfread(&size, sizeof(size), 1, KTFILEP(id))) { 1649 /* 1650 * Hit the end of file, reserve this slot. 1651 */ 1652 size = 0; 1653 1654 /* fseek to synchronise buffered I/O on the key table. */ 1655 /* XXX Without the weird setbuf hack, can we nuke this now? */ 1656 if (fseek(KTFILEP(id), 0L, SEEK_CUR) < 0) 1657 { 1658 return errno; 1659 } 1660 1661 #ifdef notdef 1662 /* We don't have to do this because htonl(0) == 0 */ 1663 if (KTVERSION(id) != KRB5_KT_VNO_1) 1664 size = htonl(size); 1665 #endif 1666 1667 if (!xfwrite(&size, sizeof(size), 1, KTFILEP(id))) { 1668 return KRB5_KT_IOERR; 1669 } 1670 found = TRUE; 1671 } 1672 1673 if (KTVERSION(id) != KRB5_KT_VNO_1) 1674 size = ntohl(size); 1675 1676 if (size > 0) { 1677 if (fseek(KTFILEP(id), size, SEEK_CUR)) { 1678 return errno; 1679 } 1680 } else if (!found) { 1681 size = -size; 1682 if (size >= *size_needed) { 1683 *size_needed = size; 1684 found = TRUE; 1685 } else if (size > 0) { 1686 /* 1687 * The current hole is not large enough, so skip it 1688 */ 1689 if (fseek(KTFILEP(id), size, SEEK_CUR)) { 1690 return errno; 1691 } 1692 } else { 1693 1694 /* fseek to synchronise buffered I/O on the key table. */ 1695 1696 if (fseek(KTFILEP(id), 0L, SEEK_CUR) < 0) 1697 { 1698 return errno; 1699 } 1700 1701 /* 1702 * Found the end of the file (marked by a 0 length buffer) 1703 * Make sure we zero any trailing data. 1704 */ 1705 zero_point = ftell(KTFILEP(id)); 1706 while ((size = xfread(iobuf, 1, sizeof(iobuf), KTFILEP(id)))) { 1707 if (size != sizeof(iobuf)) { 1708 remainder = size % sizeof(krb5_int32); 1709 if (remainder) { 1710 size += sizeof(krb5_int32) - remainder; 1711 } 1712 } 1713 1714 if (fseek(KTFILEP(id), 0L, SEEK_CUR) < 0) 1715 { 1716 return errno; 1717 } 1718 1719 memset(iobuf, 0, (size_t) size); 1720 xfwrite(iobuf, 1, (size_t) size, KTFILEP(id)); 1721 fflush(KTFILEP(id)); 1722 if (feof(KTFILEP(id))) { 1723 break; 1724 } 1725 1726 if (fseek(KTFILEP(id), 0L, SEEK_CUR) < 0) 1727 { 1728 return errno; 1729 } 1730 1731 } 1732 if (fseek(KTFILEP(id), zero_point, SEEK_SET)) { 1733 return errno; 1734 } 1735 } 1736 } 1737 } 1738 1739 return 0; 1740 } 1741