1/* 2 * Copyright (c) 1997 - 2005 Kungliga Tekniska Högskolan 3 * (Royal Institute of Technology, Stockholm, Sweden). 4 * All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 17 * 3. Neither the name of the Institute nor the names of its contributors 18 * may be used to endorse or promote products derived from this software 19 * without specific prior written permission. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND 22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24 * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE 25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31 * SUCH DAMAGE. 32 */ 33 34#include "krb5_locl.h" 35 36/** 37 * @page krb5_keytab_intro The keytab handing functions 38 * @section section_krb5_keytab Kerberos Keytabs 39 * 40 * See the library functions here: @ref krb5_keytab 41 * 42 * Keytabs are long term key storage for servers, their equvalment of 43 * password files. 44 * 45 * Normally the only function that useful for server are to specify 46 * what keytab to use to other core functions like krb5_rd_req() 47 * krb5_kt_resolve(), and krb5_kt_close(). 48 * 49 * @subsection krb5_keytab_names Keytab names 50 * 51 * A keytab name is on the form type:residual. The residual part is 52 * specific to each keytab-type. 53 * 54 * When a keytab-name is resolved, the type is matched with an internal 55 * list of keytab types. If there is no matching keytab type, 56 * the default keytab is used. The current default type is FILE. 57 * 58 * The default value can be changed in the configuration file 59 * /etc/krb5.conf by setting the variable 60 * [defaults]default_keytab_name. 61 * 62 * The keytab types that are implemented in Heimdal are: 63 * - file 64 * store the keytab in a file, the type's name is FILE . The 65 * residual part is a filename. For compatibility with other 66 * Kerberos implemtation WRFILE and JAVA14 is also accepted. WRFILE 67 * has the same format as FILE. JAVA14 have a format that is 68 * compatible with older versions of MIT kerberos and SUN's Java 69 * based installation. They store a truncted kvno, so when the knvo 70 * excess 255, they are truncted in this format. 71 * 72 * - keytab 73 * store the keytab in a AFS keyfile (usually /usr/afs/etc/KeyFile ), 74 * the type's name is AFSKEYFILE. The residual part is a filename. 75 * 76 * - memory 77 * The keytab is stored in a memory segment. This allows sensitive 78 * and/or temporary data not to be stored on disk. The type's name 79 * is MEMORY. Each MEMORY keytab is referenced counted by and 80 * opened by the residual name, so two handles can point to the 81 * same memory area. When the last user closes using krb5_kt_close() 82 * the keytab, the keys in they keytab is memset() to zero and freed 83 * and can no longer be looked up by name. 84 * 85 * 86 * @subsection krb5_keytab_example Keytab example 87 * 88 * This is a minimalistic version of ktutil. 89 * 90 * @code 91int 92main (int argc, char **argv) 93{ 94 krb5_context context; 95 krb5_keytab keytab; 96 krb5_kt_cursor cursor; 97 krb5_keytab_entry entry; 98 krb5_error_code ret; 99 char *principal; 100 101 if (krb5_init_context (&context) != 0) 102 errx(1, "krb5_context"); 103 104 ret = krb5_kt_default (context, &keytab); 105 if (ret) 106 krb5_err(context, 1, ret, "krb5_kt_default"); 107 108 ret = krb5_kt_start_seq_get(context, keytab, &cursor); 109 if (ret) 110 krb5_err(context, 1, ret, "krb5_kt_start_seq_get"); 111 while((ret = krb5_kt_next_entry(context, keytab, &entry, &cursor)) == 0){ 112 krb5_unparse_name(context, entry.principal, &principal); 113 printf("principal: %s\n", principal); 114 free(principal); 115 krb5_kt_free_entry(context, &entry); 116 } 117 ret = krb5_kt_end_seq_get(context, keytab, &cursor); 118 if (ret) 119 krb5_err(context, 1, ret, "krb5_kt_end_seq_get"); 120 ret = krb5_kt_close(context, keytab); 121 if (ret) 122 krb5_err(context, 1, ret, "krb5_kt_close"); 123 krb5_free_context(context); 124 return 0; 125} 126 * @endcode 127 * 128 */ 129 130 131/** 132 * Register a new keytab backend. 133 * 134 * @param context a Keberos context. 135 * @param ops a backend to register. 136 * 137 * @return Return an error code or 0, see krb5_get_error_message(). 138 * 139 * @ingroup krb5_keytab 140 */ 141 142KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 143krb5_kt_register(krb5_context context, 144 const krb5_kt_ops *ops) 145{ 146 struct krb5_keytab_data *tmp; 147 148 if (strlen(ops->prefix) > KRB5_KT_PREFIX_MAX_LEN - 1) { 149 krb5_set_error_message(context, KRB5_KT_BADNAME, 150 N_("can't register cache type, prefix too long", "")); 151 return KRB5_KT_BADNAME; 152 } 153 154 tmp = realloc(context->kt_types, 155 (context->num_kt_types + 1) * sizeof(*context->kt_types)); 156 if(tmp == NULL) { 157 krb5_set_error_message(context, ENOMEM, 158 N_("malloc: out of memory", "")); 159 return ENOMEM; 160 } 161 memcpy(&tmp[context->num_kt_types], ops, 162 sizeof(tmp[context->num_kt_types])); 163 context->kt_types = tmp; 164 context->num_kt_types++; 165 return 0; 166} 167 168static const char * 169keytab_name(const char *name, const char **type, size_t *type_len) 170{ 171 const char *residual; 172 173 residual = strchr(name, ':'); 174 175 if (residual == NULL || 176 name[0] == '/' 177#ifdef _WIN32 178 /* Avoid treating <drive>:<path> as a keytab type 179 * specification */ 180 || name + 1 == residual 181#endif 182 ) { 183 184 *type = "FILE"; 185 *type_len = strlen(*type); 186 residual = name; 187 } else { 188 *type = name; 189 *type_len = residual - name; 190 residual++; 191 } 192 193 return residual; 194} 195 196/** 197 * Resolve the keytab name (of the form `type:residual') in `name' 198 * into a keytab in `id'. 199 * 200 * @param context a Keberos context. 201 * @param name name to resolve 202 * @param id resulting keytab, free with krb5_kt_close(). 203 * 204 * @return Return an error code or 0, see krb5_get_error_message(). 205 * 206 * @ingroup krb5_keytab 207 */ 208 209 210KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 211krb5_kt_resolve(krb5_context context, 212 const char *name, 213 krb5_keytab *id) 214{ 215 krb5_keytab k; 216 int i; 217 const char *type, *residual; 218 size_t type_len; 219 krb5_error_code ret; 220 221 residual = keytab_name(name, &type, &type_len); 222 223 for(i = 0; i < context->num_kt_types; i++) { 224 if(strncasecmp(type, context->kt_types[i].prefix, type_len) == 0) 225 break; 226 } 227 if(i == context->num_kt_types) { 228 krb5_set_error_message(context, KRB5_KT_UNKNOWN_TYPE, 229 N_("unknown keytab type %.*s", "type"), 230 (int)type_len, type); 231 return KRB5_KT_UNKNOWN_TYPE; 232 } 233 234 k = malloc (sizeof(*k)); 235 if (k == NULL) { 236 krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", "")); 237 return ENOMEM; 238 } 239 memcpy(k, &context->kt_types[i], sizeof(*k)); 240 k->data = NULL; 241 ret = (*k->resolve)(context, residual, k); 242 if(ret) { 243 free(k); 244 k = NULL; 245 } 246 *id = k; 247 return ret; 248} 249 250/** 251 * copy the name of the default keytab into `name'. 252 * 253 * @param context a Keberos context. 254 * @param name buffer where the name will be written 255 * @param namesize length of name 256 * 257 * @return Return an error code or 0, see krb5_get_error_message(). 258 * 259 * @ingroup krb5_keytab 260 */ 261 262KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 263krb5_kt_default_name(krb5_context context, char *name, size_t namesize) 264{ 265 if (strlcpy (name, context->default_keytab, namesize) >= namesize) { 266 krb5_clear_error_message (context); 267 return KRB5_CONFIG_NOTENUFSPACE; 268 } 269 return 0; 270} 271 272/** 273 * Copy the name of the default modify keytab into `name'. 274 * 275 * @param context a Keberos context. 276 * @param name buffer where the name will be written 277 * @param namesize length of name 278 * 279 * @return Return an error code or 0, see krb5_get_error_message(). 280 * 281 * @ingroup krb5_keytab 282 */ 283 284KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 285krb5_kt_default_modify_name(krb5_context context, char *name, size_t namesize) 286{ 287 const char *kt = NULL; 288 if(context->default_keytab_modify == NULL) { 289 if(strncasecmp(context->default_keytab, "ANY:", 4) != 0) 290 kt = context->default_keytab; 291 else { 292 size_t len = strcspn(context->default_keytab + 4, ","); 293 if(len >= namesize) { 294 krb5_clear_error_message(context); 295 return KRB5_CONFIG_NOTENUFSPACE; 296 } 297 strlcpy(name, context->default_keytab + 4, namesize); 298 name[len] = '\0'; 299 return 0; 300 } 301 } else 302 kt = context->default_keytab_modify; 303 if (strlcpy (name, kt, namesize) >= namesize) { 304 krb5_clear_error_message (context); 305 return KRB5_CONFIG_NOTENUFSPACE; 306 } 307 return 0; 308} 309 310/** 311 * Set `id' to the default keytab. 312 * 313 * @param context a Keberos context. 314 * @param id the new default keytab. 315 * 316 * @return Return an error code or 0, see krb5_get_error_message(). 317 * 318 * @ingroup krb5_keytab 319 */ 320 321KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 322krb5_kt_default(krb5_context context, krb5_keytab *id) 323{ 324 return krb5_kt_resolve (context, context->default_keytab, id); 325} 326 327/** 328 * Read the key identified by `(principal, vno, enctype)' from the 329 * keytab in `keyprocarg' (the default if == NULL) into `*key'. 330 * 331 * @param context a Keberos context. 332 * @param keyprocarg 333 * @param principal 334 * @param vno 335 * @param enctype 336 * @param key 337 * 338 * @return Return an error code or 0, see krb5_get_error_message(). 339 * 340 * @ingroup krb5_keytab 341 */ 342 343KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 344krb5_kt_read_service_key(krb5_context context, 345 krb5_pointer keyprocarg, 346 krb5_principal principal, 347 krb5_kvno vno, 348 krb5_enctype enctype, 349 krb5_keyblock **key) 350{ 351 krb5_keytab keytab; 352 krb5_keytab_entry entry; 353 krb5_error_code ret; 354 355 if (keyprocarg) 356 ret = krb5_kt_resolve (context, keyprocarg, &keytab); 357 else 358 ret = krb5_kt_default (context, &keytab); 359 360 if (ret) 361 return ret; 362 363 ret = krb5_kt_get_entry (context, keytab, principal, vno, enctype, &entry); 364 krb5_kt_close (context, keytab); 365 if (ret) 366 return ret; 367 ret = krb5_copy_keyblock (context, &entry.keyblock, key); 368 krb5_kt_free_entry(context, &entry); 369 return ret; 370} 371 372/** 373 * Return the type of the `keytab' in the string `prefix of length 374 * `prefixsize'. 375 * 376 * @param context a Keberos context. 377 * @param keytab the keytab to get the prefix for 378 * @param prefix prefix buffer 379 * @param prefixsize length of prefix buffer 380 * 381 * @return Return an error code or 0, see krb5_get_error_message(). 382 * 383 * @ingroup krb5_keytab 384 */ 385 386KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 387krb5_kt_get_type(krb5_context context, 388 krb5_keytab keytab, 389 char *prefix, 390 size_t prefixsize) 391{ 392 strlcpy(prefix, keytab->prefix, prefixsize); 393 return 0; 394} 395 396/** 397 * Retrieve the name of the keytab `keytab' into `name', `namesize' 398 * 399 * @param context a Keberos context. 400 * @param keytab the keytab to get the name for. 401 * @param name name buffer. 402 * @param namesize size of name buffer. 403 * 404 * @return Return an error code or 0, see krb5_get_error_message(). 405 * 406 * @ingroup krb5_keytab 407 */ 408 409KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 410krb5_kt_get_name(krb5_context context, 411 krb5_keytab keytab, 412 char *name, 413 size_t namesize) 414{ 415 return (*keytab->get_name)(context, keytab, name, namesize); 416} 417 418/** 419 * Retrieve the full name of the keytab `keytab' and store the name in 420 * `str'. 421 * 422 * @param context a Keberos context. 423 * @param keytab keytab to get name for. 424 * @param str the name of the keytab name, usee krb5_xfree() to free 425 * the string. On error, *str is set to NULL. 426 * 427 * @return Return an error code or 0, see krb5_get_error_message(). 428 * 429 * @ingroup krb5_keytab 430 */ 431 432KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 433krb5_kt_get_full_name(krb5_context context, 434 krb5_keytab keytab, 435 char **str) 436{ 437 char type[KRB5_KT_PREFIX_MAX_LEN]; 438 char name[MAXPATHLEN]; 439 krb5_error_code ret; 440 441 *str = NULL; 442 443 ret = krb5_kt_get_type(context, keytab, type, sizeof(type)); 444 if (ret) 445 return ret; 446 447 ret = krb5_kt_get_name(context, keytab, name, sizeof(name)); 448 if (ret) 449 return ret; 450 451 if (asprintf(str, "%s:%s", type, name) == -1) { 452 krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", "")); 453 *str = NULL; 454 return ENOMEM; 455 } 456 457 return 0; 458} 459 460/** 461 * Finish using the keytab in `id'. All resources will be released, 462 * even on errors. 463 * 464 * @param context a Keberos context. 465 * @param id keytab to close. 466 * 467 * @return Return an error code or 0, see krb5_get_error_message(). 468 * 469 * @ingroup krb5_keytab 470 */ 471 472KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 473krb5_kt_close(krb5_context context, 474 krb5_keytab id) 475{ 476 krb5_error_code ret; 477 478 ret = (*id->close)(context, id); 479 memset(id, 0, sizeof(*id)); 480 free(id); 481 return ret; 482} 483 484/** 485 * Destroy (remove) the keytab in `id'. All resources will be released, 486 * even on errors, does the equvalment of krb5_kt_close() on the resources. 487 * 488 * @param context a Keberos context. 489 * @param id keytab to destroy. 490 * 491 * @return Return an error code or 0, see krb5_get_error_message(). 492 * 493 * @ingroup krb5_keytab 494 */ 495 496KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 497krb5_kt_destroy(krb5_context context, 498 krb5_keytab id) 499{ 500 krb5_error_code ret; 501 502 ret = (*id->destroy)(context, id); 503 krb5_kt_close(context, id); 504 return ret; 505} 506 507/* 508 * Match any aliases in keytab `entry' with `principal'. 509 */ 510 511static krb5_boolean 512compare_aliseses(krb5_context context, 513 krb5_keytab_entry *entry, 514 krb5_error_code (*cmp)(krb5_context, krb5_const_principal, krb5_const_principal), 515 krb5_const_principal principal) 516{ 517 unsigned int i; 518 if (entry->aliases == NULL) 519 return FALSE; 520 for (i = 0; i < entry->aliases->len; i++) 521 if (cmp(context, &entry->aliases->val[i], principal)) 522 return TRUE; 523 return FALSE; 524} 525 526/** 527 * Compare `entry' against `principal, vno, enctype'. 528 * Any of `principal, vno, enctype' might be 0 which acts as a wildcard. 529 * Return TRUE if they compare the same, FALSE otherwise. 530 * 531 * @param context a Keberos context. 532 * @param entry an entry to match with. 533 * @param principal principal to match, NULL matches all principals. 534 * @param vno key version to match, 0 matches all key version numbers. 535 * @param enctype encryption type to match, 0 matches all encryption types. 536 * 537 * @return Return TRUE or match, FALSE if not matched. 538 * 539 * @ingroup krb5_keytab 540 */ 541 542KRB5_LIB_FUNCTION krb5_boolean KRB5_LIB_CALL 543krb5_kt_compare(krb5_context context, 544 krb5_keytab_entry *entry, 545 krb5_const_principal principal, 546 krb5_kvno vno, 547 krb5_enctype enctype) 548{ 549 if(principal != NULL) { 550 krb5_error_code (*cmp)(krb5_context, krb5_const_principal, krb5_const_principal) = 551 krb5_principal_compare; 552 553 if (principal->name.name_type == KRB5_NT_GSS_HOSTBASED_SERVICE) 554 cmp = krb5_principal_compare_any_realm; 555 556 if (!cmp(context, entry->principal, principal) || 557 compare_aliseses(context, entry, cmp, principal)) 558 return FALSE; 559 } 560 if(vno && vno != entry->vno) 561 return FALSE; 562 if(enctype && enctype != entry->keyblock.keytype) 563 return FALSE; 564 return TRUE; 565} 566 567krb5_error_code 568_krb5_kt_principal_not_found(krb5_context context, 569 krb5_error_code ret, 570 krb5_keytab id, 571 krb5_const_principal principal, 572 krb5_enctype enctype, 573 int kvno) 574{ 575 char princ[256], kvno_str[25], *kt_name; 576 char *enctype_str = NULL; 577 578 krb5_unparse_name_fixed (context, principal, princ, sizeof(princ)); 579 krb5_kt_get_full_name (context, id, &kt_name); 580 if (enctype) 581 krb5_enctype_to_string(context, enctype, &enctype_str); 582 583 if (kvno) 584 snprintf(kvno_str, sizeof(kvno_str), "(kvno %d)", kvno); 585 else 586 kvno_str[0] = '\0'; 587 588 krb5_set_error_message (context, ret, 589 N_("Failed to find %s%s in keytab %s (%s)", 590 "principal, kvno, keytab file, enctype"), 591 princ, 592 kvno_str, 593 kt_name ? kt_name : "unknown keytab", 594 enctype_str ? enctype_str : "unknown enctype"); 595 free(kt_name); 596 if (enctype_str) 597 free(enctype_str); 598 return ret; 599} 600 601 602/** 603 * Retrieve the keytab entry for `principal, kvno, enctype' into `entry' 604 * from the keytab `id'. Matching is done like krb5_kt_compare(). 605 * 606 * @param context a Keberos context. 607 * @param id a keytab. 608 * @param principal principal to match, NULL matches all principals. 609 * @param kvno key version to match, 0 matches all key version numbers. 610 * @param enctype encryption type to match, 0 matches all encryption types. 611 * @param entry the returned entry, free with krb5_kt_free_entry(). 612 * 613 * @return Return an error code or 0, see krb5_get_error_message(). 614 * 615 * @ingroup krb5_keytab 616 */ 617 618KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 619krb5_kt_get_entry(krb5_context context, 620 krb5_keytab id, 621 krb5_const_principal principal, 622 krb5_kvno kvno, 623 krb5_enctype enctype, 624 krb5_keytab_entry *entry) 625{ 626 krb5_keytab_entry tmp; 627 krb5_error_code ret; 628 krb5_kt_cursor cursor; 629 630 if(id->get) 631 return (*id->get)(context, id, principal, kvno, enctype, entry); 632 633 ret = krb5_kt_start_seq_get (context, id, &cursor); 634 if (ret) { 635 /* This is needed for krb5_verify_init_creds, but keep error 636 * string from previous error for the human. */ 637 context->error_code = KRB5_KT_NOTFOUND; 638 return KRB5_KT_NOTFOUND; 639 } 640 641 entry->vno = 0; 642 while (krb5_kt_next_entry(context, id, &tmp, &cursor) == 0) { 643 if (krb5_kt_compare(context, &tmp, principal, 0, enctype)) { 644 /* the file keytab might only store the lower 8 bits of 645 the kvno, so only compare those bits */ 646 if (kvno == tmp.vno 647 || (tmp.vno < 256 && kvno % 256 == tmp.vno)) { 648 krb5_kt_copy_entry_contents (context, &tmp, entry); 649 krb5_kt_free_entry (context, &tmp); 650 krb5_kt_end_seq_get(context, id, &cursor); 651 return 0; 652 } else if (kvno == 0 && tmp.vno > entry->vno) { 653 if (entry->vno) 654 krb5_kt_free_entry (context, entry); 655 krb5_kt_copy_entry_contents (context, &tmp, entry); 656 } 657 } 658 krb5_kt_free_entry(context, &tmp); 659 } 660 krb5_kt_end_seq_get (context, id, &cursor); 661 if (entry->vno == 0) 662 return _krb5_kt_principal_not_found(context, KRB5_KT_NOTFOUND, 663 id, principal, enctype, kvno); 664 return 0; 665} 666 667/** 668 * Copy the contents of `in' into `out'. 669 * 670 * @param context a Keberos context. 671 * @param in the keytab entry to copy. 672 * @param out the copy of the keytab entry, free with krb5_kt_free_entry(). 673 * 674 * @return Return an error code or 0, see krb5_get_error_message(). 675 * 676 * @ingroup krb5_keytab 677 */ 678 679KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 680krb5_kt_copy_entry_contents(krb5_context context, 681 const krb5_keytab_entry *in, 682 krb5_keytab_entry *out) 683{ 684 krb5_error_code ret; 685 686 memset(out, 0, sizeof(*out)); 687 out->vno = in->vno; 688 689 ret = krb5_copy_principal (context, in->principal, &out->principal); 690 if (ret) 691 goto fail; 692 ret = krb5_copy_keyblock_contents (context, 693 &in->keyblock, 694 &out->keyblock); 695 if (ret) 696 goto fail; 697 out->timestamp = in->timestamp; 698 return 0; 699fail: 700 krb5_kt_free_entry (context, out); 701 return ret; 702} 703 704/** 705 * Free the contents of `entry'. 706 * 707 * @param context a Keberos context. 708 * @param entry the entry to free 709 * 710 * @return Return an error code or 0, see krb5_get_error_message(). 711 * 712 * @ingroup krb5_keytab 713 */ 714 715KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 716krb5_kt_free_entry(krb5_context context, 717 krb5_keytab_entry *entry) 718{ 719 krb5_free_principal (context, entry->principal); 720 krb5_free_keyblock_contents (context, &entry->keyblock); 721 memset(entry, 0, sizeof(*entry)); 722 return 0; 723} 724 725/** 726 * Set `cursor' to point at the beginning of `id'. 727 * 728 * @param context a Keberos context. 729 * @param id a keytab. 730 * @param cursor a newly allocated cursor, free with krb5_kt_end_seq_get(). 731 * 732 * @return Return an error code or 0, see krb5_get_error_message(). 733 * 734 * @ingroup krb5_keytab 735 */ 736 737KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 738krb5_kt_start_seq_get(krb5_context context, 739 krb5_keytab id, 740 krb5_kt_cursor *cursor) 741{ 742 if(id->start_seq_get == NULL) { 743 krb5_set_error_message(context, HEIM_ERR_OPNOTSUPP, 744 N_("start_seq_get is not supported " 745 "in the %s keytab type", ""), 746 id->prefix); 747 return HEIM_ERR_OPNOTSUPP; 748 } 749 return (*id->start_seq_get)(context, id, cursor); 750} 751 752/** 753 * Get the next entry from keytab, advance the cursor. On last entry 754 * the function will return KRB5_KT_END. 755 * 756 * @param context a Keberos context. 757 * @param id a keytab. 758 * @param entry the returned entry, free with krb5_kt_free_entry(). 759 * @param cursor the cursor of the iteration. 760 * 761 * @return Return an error code or 0, see krb5_get_error_message(). 762 * 763 * @ingroup krb5_keytab 764 */ 765 766KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 767krb5_kt_next_entry(krb5_context context, 768 krb5_keytab id, 769 krb5_keytab_entry *entry, 770 krb5_kt_cursor *cursor) 771{ 772 if(id->next_entry == NULL) { 773 krb5_set_error_message(context, HEIM_ERR_OPNOTSUPP, 774 N_("next_entry is not supported in the %s " 775 " keytab", ""), 776 id->prefix); 777 return HEIM_ERR_OPNOTSUPP; 778 } 779 return (*id->next_entry)(context, id, entry, cursor); 780} 781 782/** 783 * Release all resources associated with `cursor'. 784 * 785 * @param context a Keberos context. 786 * @param id a keytab. 787 * @param cursor the cursor to free. 788 * 789 * @return Return an error code or 0, see krb5_get_error_message(). 790 * 791 * @ingroup krb5_keytab 792 */ 793 794KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 795krb5_kt_end_seq_get(krb5_context context, 796 krb5_keytab id, 797 krb5_kt_cursor *cursor) 798{ 799 if(id->end_seq_get == NULL) { 800 krb5_set_error_message(context, HEIM_ERR_OPNOTSUPP, 801 "end_seq_get is not supported in the %s " 802 " keytab", id->prefix); 803 return HEIM_ERR_OPNOTSUPP; 804 } 805 return (*id->end_seq_get)(context, id, cursor); 806} 807 808/** 809 * Add the entry in `entry' to the keytab `id'. 810 * 811 * @param context a Keberos context. 812 * @param id a keytab. 813 * @param entry the entry to add 814 * 815 * @return Return an error code or 0, see krb5_get_error_message(). 816 * 817 * @ingroup krb5_keytab 818 */ 819 820KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 821krb5_kt_add_entry(krb5_context context, 822 krb5_keytab id, 823 krb5_keytab_entry *entry) 824{ 825 if(id->add == NULL) { 826 krb5_set_error_message(context, KRB5_KT_NOWRITE, 827 N_("Add is not supported in the %s keytab", ""), 828 id->prefix); 829 return KRB5_KT_NOWRITE; 830 } 831 entry->timestamp = (uint32_t)time(NULL); 832 return (*id->add)(context, id,entry); 833} 834 835/** 836 * Remove an entry from the keytab, matching is done using 837 * krb5_kt_compare(). 838 839 * @param context a Keberos context. 840 * @param id a keytab. 841 * @param entry the entry to remove 842 * 843 * @return Return an error code or 0, see krb5_get_error_message(). 844 * 845 * @ingroup krb5_keytab 846 */ 847 848KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 849krb5_kt_remove_entry(krb5_context context, 850 krb5_keytab id, 851 krb5_keytab_entry *entry) 852{ 853 if(id->remove == NULL) { 854 krb5_set_error_message(context, KRB5_KT_NOWRITE, 855 N_("Remove is not supported in the %s keytab", ""), 856 id->prefix); 857 return KRB5_KT_NOWRITE; 858 } 859 return (*id->remove)(context, id, entry); 860} 861 862/** 863 * Return true if the keytab exists and have entries 864 * 865 * @param context a Keberos context. 866 * @param id a keytab. 867 * 868 * @return Return an error code or 0, see krb5_get_error_message(). 869 * 870 * @ingroup krb5_keytab 871 */ 872 873KRB5_LIB_FUNCTION krb5_boolean KRB5_LIB_CALL 874krb5_kt_have_content(krb5_context context, 875 krb5_keytab id) 876{ 877 krb5_keytab_entry entry; 878 krb5_kt_cursor cursor; 879 krb5_error_code ret; 880 char *name; 881 882 ret = krb5_kt_start_seq_get(context, id, &cursor); 883 if (ret) 884 goto notfound; 885 886 ret = krb5_kt_next_entry(context, id, &entry, &cursor); 887 krb5_kt_end_seq_get(context, id, &cursor); 888 if (ret) 889 goto notfound; 890 891 krb5_kt_free_entry(context, &entry); 892 893 return 0; 894 895 notfound: 896 ret = krb5_kt_get_full_name(context, id, &name); 897 if (ret == 0) { 898 krb5_set_error_message(context, KRB5_KT_NOTFOUND, 899 N_("No entry in keytab: %s", ""), name); 900 free(name); 901 } 902 return KRB5_KT_NOTFOUND; 903} 904