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 key proc argument 333 * @param principal principal to find 334 * @param vno its kvno to search for 335 * @param enctype the encrypt to search for 336 * @param key return key, free with krb5_free_keyblock() 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 } 665 return 0; 666} 667 668/** 669 * Copy the contents of `in' into `out'. 670 * 671 * @param context a Keberos context. 672 * @param in the keytab entry to copy. 673 * @param out the copy of the keytab entry, free with krb5_kt_free_entry(). 674 * 675 * @return Return an error code or 0, see krb5_get_error_message(). 676 * 677 * @ingroup krb5_keytab 678 */ 679 680KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 681krb5_kt_copy_entry_contents(krb5_context context, 682 const krb5_keytab_entry *in, 683 krb5_keytab_entry *out) 684{ 685 krb5_error_code ret; 686 687 memset(out, 0, sizeof(*out)); 688 out->vno = in->vno; 689 690 ret = krb5_copy_principal (context, in->principal, &out->principal); 691 if (ret) 692 goto fail; 693 ret = krb5_copy_keyblock_contents (context, 694 &in->keyblock, 695 &out->keyblock); 696 if (ret) 697 goto fail; 698 out->timestamp = in->timestamp; 699 return 0; 700fail: 701 krb5_kt_free_entry (context, out); 702 return ret; 703} 704 705/** 706 * Free the contents of `entry'. 707 * 708 * @param context a Keberos context. 709 * @param entry the entry to free 710 * 711 * @return Return an error code or 0, see krb5_get_error_message(). 712 * 713 * @ingroup krb5_keytab 714 */ 715 716KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 717krb5_kt_free_entry(krb5_context context, 718 krb5_keytab_entry *entry) 719{ 720 krb5_free_principal (context, entry->principal); 721 krb5_free_keyblock_contents (context, &entry->keyblock); 722 memset(entry, 0, sizeof(*entry)); 723 return 0; 724} 725 726/** 727 * Set `cursor' to point at the beginning of `id'. 728 * 729 * @param context a Keberos context. 730 * @param id a keytab. 731 * @param cursor a newly allocated cursor, free with krb5_kt_end_seq_get(). 732 * 733 * @return Return an error code or 0, see krb5_get_error_message(). 734 * 735 * @ingroup krb5_keytab 736 */ 737 738KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 739krb5_kt_start_seq_get(krb5_context context, 740 krb5_keytab id, 741 krb5_kt_cursor *cursor) 742{ 743 if(id->start_seq_get == NULL) { 744 krb5_set_error_message(context, HEIM_ERR_OPNOTSUPP, 745 N_("start_seq_get is not supported " 746 "in the %s keytab type", ""), 747 id->prefix); 748 return HEIM_ERR_OPNOTSUPP; 749 } 750 return (*id->start_seq_get)(context, id, cursor); 751} 752 753/** 754 * Get the next entry from keytab, advance the cursor. On last entry 755 * the function will return KRB5_KT_END. 756 * 757 * @param context a Keberos context. 758 * @param id a keytab. 759 * @param entry the returned entry, free with krb5_kt_free_entry(). 760 * @param cursor the cursor of the iteration. 761 * 762 * @return Return an error code or 0, see krb5_get_error_message(). 763 * 764 * @ingroup krb5_keytab 765 */ 766 767KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 768krb5_kt_next_entry(krb5_context context, 769 krb5_keytab id, 770 krb5_keytab_entry *entry, 771 krb5_kt_cursor *cursor) 772{ 773 memset(entry, 0, sizeof(*entry)); 774 if(id->next_entry == NULL) { 775 krb5_set_error_message(context, HEIM_ERR_OPNOTSUPP, 776 N_("next_entry is not supported in the %s " 777 " keytab", ""), 778 id->prefix); 779 return HEIM_ERR_OPNOTSUPP; 780 } 781 return (*id->next_entry)(context, id, entry, cursor); 782} 783 784/** 785 * Release all resources associated with `cursor'. 786 * 787 * @param context a Keberos context. 788 * @param id a keytab. 789 * @param cursor the cursor to free. 790 * 791 * @return Return an error code or 0, see krb5_get_error_message(). 792 * 793 * @ingroup krb5_keytab 794 */ 795 796KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 797krb5_kt_end_seq_get(krb5_context context, 798 krb5_keytab id, 799 krb5_kt_cursor *cursor) 800{ 801 if(id->end_seq_get == NULL) { 802 krb5_set_error_message(context, HEIM_ERR_OPNOTSUPP, 803 "end_seq_get is not supported in the %s " 804 " keytab", id->prefix); 805 return HEIM_ERR_OPNOTSUPP; 806 } 807 return (*id->end_seq_get)(context, id, cursor); 808} 809 810/** 811 * Add the entry in `entry' to the keytab `id'. 812 * 813 * @param context a Keberos context. 814 * @param id a keytab. 815 * @param entry the entry to add 816 * 817 * @return Return an error code or 0, see krb5_get_error_message(). 818 * 819 * @ingroup krb5_keytab 820 */ 821 822KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 823krb5_kt_add_entry(krb5_context context, 824 krb5_keytab id, 825 krb5_keytab_entry *entry) 826{ 827 if(id->add == NULL) { 828 krb5_set_error_message(context, KRB5_KT_NOWRITE, 829 N_("Add is not supported in the %s keytab", ""), 830 id->prefix); 831 return KRB5_KT_NOWRITE; 832 } 833 entry->timestamp = (uint32_t)time(NULL); 834 return (*id->add)(context, id,entry); 835} 836 837/** 838 * Remove an entry from the keytab, matching is done using 839 * krb5_kt_compare(). 840 841 * @param context a Keberos context. 842 * @param id a keytab. 843 * @param entry the entry to remove 844 * 845 * @return Return an error code or 0, see krb5_get_error_message(). 846 * 847 * @ingroup krb5_keytab 848 */ 849 850KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 851krb5_kt_remove_entry(krb5_context context, 852 krb5_keytab id, 853 krb5_keytab_entry *entry) 854{ 855 if(id->remove == NULL) { 856 krb5_set_error_message(context, KRB5_KT_NOWRITE, 857 N_("Remove is not supported in the %s keytab", ""), 858 id->prefix); 859 return KRB5_KT_NOWRITE; 860 } 861 return (*id->remove)(context, id, entry); 862} 863 864/** 865 * Return true if the keytab exists and have entries 866 * 867 * @param context a Keberos context. 868 * @param id a keytab. 869 * 870 * @return Return an error code or 0, see krb5_get_error_message(). 871 * 872 * @ingroup krb5_keytab 873 */ 874 875KRB5_LIB_FUNCTION krb5_boolean KRB5_LIB_CALL 876krb5_kt_have_content(krb5_context context, 877 krb5_keytab id) 878{ 879 krb5_keytab_entry entry; 880 krb5_kt_cursor cursor; 881 krb5_error_code ret; 882 char *name; 883 884 ret = krb5_kt_start_seq_get(context, id, &cursor); 885 if (ret) 886 goto notfound; 887 888 ret = krb5_kt_next_entry(context, id, &entry, &cursor); 889 krb5_kt_end_seq_get(context, id, &cursor); 890 if (ret) 891 goto notfound; 892 893 krb5_kt_free_entry(context, &entry); 894 895 return 0; 896 897 notfound: 898 ret = krb5_kt_get_full_name(context, id, &name); 899 if (ret == 0) { 900 krb5_set_error_message(context, KRB5_KT_NOTFOUND, 901 N_("No entry in keytab: %s", ""), name); 902 free(name); 903 } 904 return KRB5_KT_NOTFOUND; 905} 906