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