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 91 int 92 main (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 142 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 143 krb5_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 168 static const char * 169 keytab_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 210 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 211 krb5_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 262 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 263 krb5_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 284 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 285 krb5_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 321 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 322 krb5_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 343 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 344 krb5_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 = NULL; /* Quiet lint */ 352 krb5_keytab_entry entry; 353 krb5_error_code ret; 354 355 memset(&entry, 0, sizeof(entry)); 356 if (keyprocarg) 357 ret = krb5_kt_resolve (context, keyprocarg, &keytab); 358 else 359 ret = krb5_kt_default (context, &keytab); 360 361 if (ret) 362 return ret; 363 364 ret = krb5_kt_get_entry (context, keytab, principal, vno, enctype, &entry); 365 if (ret == 0) { 366 ret = krb5_copy_keyblock (context, &entry.keyblock, key); 367 krb5_kt_free_entry(context, &entry); 368 } 369 krb5_kt_close (context, keytab); 370 return ret; 371 } 372 373 /** 374 * Return the type of the `keytab' in the string `prefix of length 375 * `prefixsize'. 376 * 377 * @param context a Keberos context. 378 * @param keytab the keytab to get the prefix for 379 * @param prefix prefix buffer 380 * @param prefixsize length of prefix buffer 381 * 382 * @return Return an error code or 0, see krb5_get_error_message(). 383 * 384 * @ingroup krb5_keytab 385 */ 386 387 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 388 krb5_kt_get_type(krb5_context context, 389 krb5_keytab keytab, 390 char *prefix, 391 size_t prefixsize) 392 { 393 strlcpy(prefix, keytab->prefix, prefixsize); 394 return 0; 395 } 396 397 /** 398 * Retrieve the name of the keytab `keytab' into `name', `namesize' 399 * 400 * @param context a Keberos context. 401 * @param keytab the keytab to get the name for. 402 * @param name name buffer. 403 * @param namesize size of name buffer. 404 * 405 * @return Return an error code or 0, see krb5_get_error_message(). 406 * 407 * @ingroup krb5_keytab 408 */ 409 410 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 411 krb5_kt_get_name(krb5_context context, 412 krb5_keytab keytab, 413 char *name, 414 size_t namesize) 415 { 416 return (*keytab->get_name)(context, keytab, name, namesize); 417 } 418 419 /** 420 * Retrieve the full name of the keytab `keytab' and store the name in 421 * `str'. 422 * 423 * @param context a Keberos context. 424 * @param keytab keytab to get name for. 425 * @param str the name of the keytab name, usee krb5_xfree() to free 426 * the string. On error, *str is set to NULL. 427 * 428 * @return Return an error code or 0, see krb5_get_error_message(). 429 * 430 * @ingroup krb5_keytab 431 */ 432 433 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 434 krb5_kt_get_full_name(krb5_context context, 435 krb5_keytab keytab, 436 char **str) 437 { 438 char type[KRB5_KT_PREFIX_MAX_LEN]; 439 char name[MAXPATHLEN]; 440 krb5_error_code ret; 441 442 *str = NULL; 443 444 ret = krb5_kt_get_type(context, keytab, type, sizeof(type)); 445 if (ret) 446 return ret; 447 448 ret = krb5_kt_get_name(context, keytab, name, sizeof(name)); 449 if (ret) 450 return ret; 451 452 if (asprintf(str, "%s:%s", type, name) == -1) { 453 krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", "")); 454 *str = NULL; 455 return ENOMEM; 456 } 457 458 return 0; 459 } 460 461 /** 462 * Finish using the keytab in `id'. All resources will be released, 463 * even on errors. 464 * 465 * @param context a Keberos context. 466 * @param id keytab to close. 467 * 468 * @return Return an error code or 0, see krb5_get_error_message(). 469 * 470 * @ingroup krb5_keytab 471 */ 472 473 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 474 krb5_kt_close(krb5_context context, 475 krb5_keytab id) 476 { 477 krb5_error_code ret = 0; 478 479 if (id) { 480 ret = (id->close)(context, id); 481 memset(id, 0, sizeof(*id)); 482 free(id); 483 } 484 return ret; 485 } 486 487 /** 488 * Destroy (remove) the keytab in `id'. All resources will be released, 489 * even on errors, does the equvalment of krb5_kt_close() on the resources. 490 * 491 * @param context a Keberos context. 492 * @param id keytab to destroy. 493 * 494 * @return Return an error code or 0, see krb5_get_error_message(). 495 * 496 * @ingroup krb5_keytab 497 */ 498 499 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 500 krb5_kt_destroy(krb5_context context, 501 krb5_keytab id) 502 { 503 krb5_error_code ret; 504 505 ret = (*id->destroy)(context, id); 506 krb5_kt_close(context, id); 507 return ret; 508 } 509 510 /* 511 * Match any aliases in keytab `entry' with `principal'. 512 */ 513 514 static krb5_boolean 515 compare_aliseses(krb5_context context, 516 krb5_keytab_entry *entry, 517 krb5_const_principal principal) 518 { 519 unsigned int i; 520 if (entry->aliases == NULL) 521 return FALSE; 522 for (i = 0; i < entry->aliases->len; i++) 523 if (krb5_principal_compare(context, &entry->aliases->val[i], principal)) 524 return TRUE; 525 return FALSE; 526 } 527 528 /** 529 * Compare `entry' against `principal, vno, enctype'. 530 * Any of `principal, vno, enctype' might be 0 which acts as a wildcard. 531 * Return TRUE if they compare the same, FALSE otherwise. 532 * 533 * @param context a Keberos context. 534 * @param entry an entry to match with. 535 * @param principal principal to match, NULL matches all principals. 536 * @param vno key version to match, 0 matches all key version numbers. 537 * @param enctype encryption type to match, 0 matches all encryption types. 538 * 539 * @return Return TRUE or match, FALSE if not matched. 540 * 541 * @ingroup krb5_keytab 542 */ 543 544 KRB5_LIB_FUNCTION krb5_boolean KRB5_LIB_CALL 545 krb5_kt_compare(krb5_context context, 546 krb5_keytab_entry *entry, 547 krb5_const_principal principal, 548 krb5_kvno vno, 549 krb5_enctype enctype) 550 { 551 if(principal != NULL && 552 !(krb5_principal_compare(context, entry->principal, principal) || 553 compare_aliseses(context, entry, principal))) 554 return FALSE; 555 if(vno && vno != entry->vno) 556 return FALSE; 557 if(enctype && enctype != entry->keyblock.keytype) 558 return FALSE; 559 return TRUE; 560 } 561 562 krb5_error_code 563 _krb5_kt_principal_not_found(krb5_context context, 564 krb5_error_code ret, 565 krb5_keytab id, 566 krb5_const_principal principal, 567 krb5_enctype enctype, 568 int kvno) 569 { 570 char princ[256], kvno_str[25], *kt_name; 571 char *enctype_str = NULL; 572 573 krb5_unparse_name_fixed (context, principal, princ, sizeof(princ)); 574 krb5_kt_get_full_name (context, id, &kt_name); 575 krb5_enctype_to_string(context, enctype, &enctype_str); 576 577 if (kvno) 578 snprintf(kvno_str, sizeof(kvno_str), "(kvno %d)", kvno); 579 else 580 kvno_str[0] = '\0'; 581 582 krb5_set_error_message (context, ret, 583 N_("Failed to find %s%s in keytab %s (%s)", 584 "principal, kvno, keytab file, enctype"), 585 princ, 586 kvno_str, 587 kt_name ? kt_name : "unknown keytab", 588 enctype_str ? enctype_str : "unknown enctype"); 589 free(kt_name); 590 free(enctype_str); 591 return ret; 592 } 593 594 595 /** 596 * Retrieve the keytab entry for `principal, kvno, enctype' into `entry' 597 * from the keytab `id'. Matching is done like krb5_kt_compare(). 598 * 599 * @param context a Keberos context. 600 * @param id a keytab. 601 * @param principal principal to match, NULL matches all principals. 602 * @param kvno key version to match, 0 matches all key version numbers. 603 * @param enctype encryption type to match, 0 matches all encryption types. 604 * @param entry the returned entry, free with krb5_kt_free_entry(). 605 * 606 * @return Return an error code or 0, see krb5_get_error_message(). 607 * 608 * @ingroup krb5_keytab 609 */ 610 611 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 612 krb5_kt_get_entry(krb5_context context, 613 krb5_keytab id, 614 krb5_const_principal principal, 615 krb5_kvno kvno, 616 krb5_enctype enctype, 617 krb5_keytab_entry *entry) 618 { 619 krb5_keytab_entry tmp; 620 krb5_error_code ret; 621 krb5_kt_cursor cursor; 622 623 if(id->get) 624 return (*id->get)(context, id, principal, kvno, enctype, entry); 625 626 memset(&tmp, 0, sizeof(tmp)); 627 ret = krb5_kt_start_seq_get (context, id, &cursor); 628 if (ret) { 629 /* This is needed for krb5_verify_init_creds, but keep error 630 * string from previous error for the human. */ 631 context->error_code = KRB5_KT_NOTFOUND; 632 return KRB5_KT_NOTFOUND; 633 } 634 635 entry->vno = 0; 636 while (krb5_kt_next_entry(context, id, &tmp, &cursor) == 0) { 637 if (krb5_kt_compare(context, &tmp, principal, 0, enctype)) { 638 /* the file keytab might only store the lower 8 bits of 639 the kvno, so only compare those bits */ 640 if (kvno == tmp.vno 641 || (tmp.vno < 256 && kvno % 256 == tmp.vno)) { 642 krb5_kt_copy_entry_contents (context, &tmp, entry); 643 krb5_kt_free_entry (context, &tmp); 644 krb5_kt_end_seq_get(context, id, &cursor); 645 return 0; 646 } else if (kvno == 0 && tmp.vno > entry->vno) { 647 if (entry->vno) 648 krb5_kt_free_entry (context, entry); 649 krb5_kt_copy_entry_contents (context, &tmp, entry); 650 } 651 } 652 krb5_kt_free_entry(context, &tmp); 653 } 654 krb5_kt_end_seq_get (context, id, &cursor); 655 if (entry->vno == 0) 656 return _krb5_kt_principal_not_found(context, KRB5_KT_NOTFOUND, 657 id, principal, enctype, kvno); 658 return 0; 659 } 660 661 /** 662 * Copy the contents of `in' into `out'. 663 * 664 * @param context a Keberos context. 665 * @param in the keytab entry to copy. 666 * @param out the copy of the keytab entry, free with krb5_kt_free_entry(). 667 * 668 * @return Return an error code or 0, see krb5_get_error_message(). 669 * 670 * @ingroup krb5_keytab 671 */ 672 673 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 674 krb5_kt_copy_entry_contents(krb5_context context, 675 const krb5_keytab_entry *in, 676 krb5_keytab_entry *out) 677 { 678 krb5_error_code ret; 679 680 memset(out, 0, sizeof(*out)); 681 682 ret = krb5_copy_principal (context, in->principal, &out->principal); 683 if (ret) 684 return ret; 685 ret = krb5_copy_keyblock_contents (context, 686 &in->keyblock, 687 &out->keyblock); 688 if (ret) { 689 krb5_free_principal(context, out->principal); 690 memset(out, 0, sizeof(*out)); 691 return ret; 692 } 693 out->vno = in->vno; 694 out->timestamp = in->timestamp; 695 return 0; 696 } 697 698 /** 699 * Free the contents of `entry'. 700 * 701 * @param context a Keberos context. 702 * @param entry the entry to free 703 * 704 * @return Return an error code or 0, see krb5_get_error_message(). 705 * 706 * @ingroup krb5_keytab 707 */ 708 709 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 710 krb5_kt_free_entry(krb5_context context, 711 krb5_keytab_entry *entry) 712 { 713 krb5_free_principal (context, entry->principal); 714 krb5_free_keyblock_contents (context, &entry->keyblock); 715 memset(entry, 0, sizeof(*entry)); 716 return 0; 717 } 718 719 /** 720 * Set `cursor' to point at the beginning of `id'. 721 * 722 * @param context a Keberos context. 723 * @param id a keytab. 724 * @param cursor a newly allocated cursor, free with krb5_kt_end_seq_get(). 725 * 726 * @return Return an error code or 0, see krb5_get_error_message(). 727 * 728 * @ingroup krb5_keytab 729 */ 730 731 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 732 krb5_kt_start_seq_get(krb5_context context, 733 krb5_keytab id, 734 krb5_kt_cursor *cursor) 735 { 736 if(id->start_seq_get == NULL) { 737 krb5_set_error_message(context, HEIM_ERR_OPNOTSUPP, 738 N_("start_seq_get is not supported " 739 "in the %s keytab type", ""), 740 id->prefix); 741 return HEIM_ERR_OPNOTSUPP; 742 } 743 return (*id->start_seq_get)(context, id, cursor); 744 } 745 746 /** 747 * Get the next entry from keytab, advance the cursor. On last entry 748 * the function will return KRB5_KT_END. 749 * 750 * @param context a Keberos context. 751 * @param id a keytab. 752 * @param entry the returned entry, free with krb5_kt_free_entry(). 753 * @param cursor the cursor of the iteration. 754 * 755 * @return Return an error code or 0, see krb5_get_error_message(). 756 * 757 * @ingroup krb5_keytab 758 */ 759 760 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 761 krb5_kt_next_entry(krb5_context context, 762 krb5_keytab id, 763 krb5_keytab_entry *entry, 764 krb5_kt_cursor *cursor) 765 { 766 if(id->next_entry == NULL) { 767 krb5_set_error_message(context, HEIM_ERR_OPNOTSUPP, 768 N_("next_entry is not supported in the %s " 769 " keytab", ""), 770 id->prefix); 771 return HEIM_ERR_OPNOTSUPP; 772 } 773 return (*id->next_entry)(context, id, entry, cursor); 774 } 775 776 /** 777 * Release all resources associated with `cursor'. 778 * 779 * @param context a Keberos context. 780 * @param id a keytab. 781 * @param cursor the cursor to free. 782 * 783 * @return Return an error code or 0, see krb5_get_error_message(). 784 * 785 * @ingroup krb5_keytab 786 */ 787 788 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 789 krb5_kt_end_seq_get(krb5_context context, 790 krb5_keytab id, 791 krb5_kt_cursor *cursor) 792 { 793 if(id->end_seq_get == NULL) { 794 krb5_set_error_message(context, HEIM_ERR_OPNOTSUPP, 795 "end_seq_get is not supported in the %s " 796 " keytab", id->prefix); 797 return HEIM_ERR_OPNOTSUPP; 798 } 799 return (*id->end_seq_get)(context, id, cursor); 800 } 801 802 /** 803 * Add the entry in `entry' to the keytab `id'. 804 * 805 * @param context a Keberos context. 806 * @param id a keytab. 807 * @param entry the entry to add 808 * 809 * @return Return an error code or 0, see krb5_get_error_message(). 810 * 811 * @ingroup krb5_keytab 812 */ 813 814 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 815 krb5_kt_add_entry(krb5_context context, 816 krb5_keytab id, 817 krb5_keytab_entry *entry) 818 { 819 if(id->add == NULL) { 820 krb5_set_error_message(context, KRB5_KT_NOWRITE, 821 N_("Add is not supported in the %s keytab", ""), 822 id->prefix); 823 return KRB5_KT_NOWRITE; 824 } 825 entry->timestamp = time(NULL); 826 return (*id->add)(context, id,entry); 827 } 828 829 /** 830 * Remove an entry from the keytab, matching is done using 831 * krb5_kt_compare(). 832 833 * @param context a Keberos context. 834 * @param id a keytab. 835 * @param entry the entry to remove 836 * 837 * @return Return an error code or 0, see krb5_get_error_message(). 838 * 839 * @ingroup krb5_keytab 840 */ 841 842 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 843 krb5_kt_remove_entry(krb5_context context, 844 krb5_keytab id, 845 krb5_keytab_entry *entry) 846 { 847 if(id->remove == NULL) { 848 krb5_set_error_message(context, KRB5_KT_NOWRITE, 849 N_("Remove is not supported in the %s keytab", ""), 850 id->prefix); 851 return KRB5_KT_NOWRITE; 852 } 853 return (*id->remove)(context, id, entry); 854 } 855 856 /** 857 * Return true if the keytab exists and have entries 858 * 859 * @param context a Keberos context. 860 * @param id a keytab. 861 * 862 * @return Return an error code or 0, see krb5_get_error_message(). 863 * 864 * @ingroup krb5_keytab 865 */ 866 867 KRB5_LIB_FUNCTION krb5_boolean KRB5_LIB_CALL 868 krb5_kt_have_content(krb5_context context, 869 krb5_keytab id) 870 { 871 krb5_keytab_entry entry; 872 krb5_kt_cursor cursor; 873 krb5_error_code ret; 874 char *name; 875 876 memset(&entry, 0, sizeof(entry)); 877 ret = krb5_kt_start_seq_get(context, id, &cursor); 878 if (ret) 879 goto notfound; 880 881 ret = krb5_kt_next_entry(context, id, &entry, &cursor); 882 krb5_kt_end_seq_get(context, id, &cursor); 883 if (ret) 884 goto notfound; 885 886 krb5_kt_free_entry(context, &entry); 887 888 return 0; 889 890 notfound: 891 ret = krb5_kt_get_full_name(context, id, &name); 892 if (ret == 0) { 893 krb5_set_error_message(context, KRB5_KT_NOTFOUND, 894 N_("No entry in keytab: %s", ""), name); 895 free(name); 896 } 897 return KRB5_KT_NOTFOUND; 898 } 899