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; 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 386 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 387 krb5_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 409 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 410 krb5_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 432 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 433 krb5_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 472 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 473 krb5_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 496 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 497 krb5_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 511 static krb5_boolean 512 compare_aliseses(krb5_context context, 513 krb5_keytab_entry *entry, 514 krb5_const_principal principal) 515 { 516 unsigned int i; 517 if (entry->aliases == NULL) 518 return FALSE; 519 for (i = 0; i < entry->aliases->len; i++) 520 if (krb5_principal_compare(context, &entry->aliases->val[i], principal)) 521 return TRUE; 522 return FALSE; 523 } 524 525 /** 526 * Compare `entry' against `principal, vno, enctype'. 527 * Any of `principal, vno, enctype' might be 0 which acts as a wildcard. 528 * Return TRUE if they compare the same, FALSE otherwise. 529 * 530 * @param context a Keberos context. 531 * @param entry an entry to match with. 532 * @param principal principal to match, NULL matches all principals. 533 * @param vno key version to match, 0 matches all key version numbers. 534 * @param enctype encryption type to match, 0 matches all encryption types. 535 * 536 * @return Return TRUE or match, FALSE if not matched. 537 * 538 * @ingroup krb5_keytab 539 */ 540 541 KRB5_LIB_FUNCTION krb5_boolean KRB5_LIB_CALL 542 krb5_kt_compare(krb5_context context, 543 krb5_keytab_entry *entry, 544 krb5_const_principal principal, 545 krb5_kvno vno, 546 krb5_enctype enctype) 547 { 548 if(principal != NULL && 549 !(krb5_principal_compare(context, entry->principal, principal) || 550 compare_aliseses(context, entry, principal))) 551 return FALSE; 552 if(vno && vno != entry->vno) 553 return FALSE; 554 if(enctype && enctype != entry->keyblock.keytype) 555 return FALSE; 556 return TRUE; 557 } 558 559 krb5_error_code 560 _krb5_kt_principal_not_found(krb5_context context, 561 krb5_error_code ret, 562 krb5_keytab id, 563 krb5_const_principal principal, 564 krb5_enctype enctype, 565 int kvno) 566 { 567 char princ[256], kvno_str[25], *kt_name; 568 char *enctype_str = NULL; 569 570 krb5_unparse_name_fixed (context, principal, princ, sizeof(princ)); 571 krb5_kt_get_full_name (context, id, &kt_name); 572 krb5_enctype_to_string(context, enctype, &enctype_str); 573 574 if (kvno) 575 snprintf(kvno_str, sizeof(kvno_str), "(kvno %d)", kvno); 576 else 577 kvno_str[0] = '\0'; 578 579 krb5_set_error_message (context, ret, 580 N_("Failed to find %s%s in keytab %s (%s)", 581 "principal, kvno, keytab file, enctype"), 582 princ, 583 kvno_str, 584 kt_name ? kt_name : "unknown keytab", 585 enctype_str ? enctype_str : "unknown enctype"); 586 free(kt_name); 587 free(enctype_str); 588 return ret; 589 } 590 591 592 /** 593 * Retrieve the keytab entry for `principal, kvno, enctype' into `entry' 594 * from the keytab `id'. Matching is done like krb5_kt_compare(). 595 * 596 * @param context a Keberos context. 597 * @param id a keytab. 598 * @param principal principal to match, NULL matches all principals. 599 * @param kvno key version to match, 0 matches all key version numbers. 600 * @param enctype encryption type to match, 0 matches all encryption types. 601 * @param entry the returned entry, free with krb5_kt_free_entry(). 602 * 603 * @return Return an error code or 0, see krb5_get_error_message(). 604 * 605 * @ingroup krb5_keytab 606 */ 607 608 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 609 krb5_kt_get_entry(krb5_context context, 610 krb5_keytab id, 611 krb5_const_principal principal, 612 krb5_kvno kvno, 613 krb5_enctype enctype, 614 krb5_keytab_entry *entry) 615 { 616 krb5_keytab_entry tmp; 617 krb5_error_code ret; 618 krb5_kt_cursor cursor; 619 620 if(id->get) 621 return (*id->get)(context, id, principal, kvno, enctype, entry); 622 623 ret = krb5_kt_start_seq_get (context, id, &cursor); 624 if (ret) { 625 /* This is needed for krb5_verify_init_creds, but keep error 626 * string from previous error for the human. */ 627 context->error_code = KRB5_KT_NOTFOUND; 628 return KRB5_KT_NOTFOUND; 629 } 630 631 entry->vno = 0; 632 while (krb5_kt_next_entry(context, id, &tmp, &cursor) == 0) { 633 if (krb5_kt_compare(context, &tmp, principal, 0, enctype)) { 634 /* the file keytab might only store the lower 8 bits of 635 the kvno, so only compare those bits */ 636 if (kvno == tmp.vno 637 || (tmp.vno < 256 && kvno % 256 == tmp.vno)) { 638 krb5_kt_copy_entry_contents (context, &tmp, entry); 639 krb5_kt_free_entry (context, &tmp); 640 krb5_kt_end_seq_get(context, id, &cursor); 641 return 0; 642 } else if (kvno == 0 && tmp.vno > entry->vno) { 643 if (entry->vno) 644 krb5_kt_free_entry (context, entry); 645 krb5_kt_copy_entry_contents (context, &tmp, entry); 646 } 647 } 648 krb5_kt_free_entry(context, &tmp); 649 } 650 krb5_kt_end_seq_get (context, id, &cursor); 651 if (entry->vno == 0) 652 return _krb5_kt_principal_not_found(context, KRB5_KT_NOTFOUND, 653 id, principal, enctype, kvno); 654 return 0; 655 } 656 657 /** 658 * Copy the contents of `in' into `out'. 659 * 660 * @param context a Keberos context. 661 * @param in the keytab entry to copy. 662 * @param out the copy of the keytab entry, free with krb5_kt_free_entry(). 663 * 664 * @return Return an error code or 0, see krb5_get_error_message(). 665 * 666 * @ingroup krb5_keytab 667 */ 668 669 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 670 krb5_kt_copy_entry_contents(krb5_context context, 671 const krb5_keytab_entry *in, 672 krb5_keytab_entry *out) 673 { 674 krb5_error_code ret; 675 676 memset(out, 0, sizeof(*out)); 677 out->vno = in->vno; 678 679 ret = krb5_copy_principal (context, in->principal, &out->principal); 680 if (ret) 681 goto fail; 682 ret = krb5_copy_keyblock_contents (context, 683 &in->keyblock, 684 &out->keyblock); 685 if (ret) 686 goto fail; 687 out->timestamp = in->timestamp; 688 return 0; 689 fail: 690 krb5_kt_free_entry (context, out); 691 return ret; 692 } 693 694 /** 695 * Free the contents of `entry'. 696 * 697 * @param context a Keberos context. 698 * @param entry the entry to free 699 * 700 * @return Return an error code or 0, see krb5_get_error_message(). 701 * 702 * @ingroup krb5_keytab 703 */ 704 705 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 706 krb5_kt_free_entry(krb5_context context, 707 krb5_keytab_entry *entry) 708 { 709 krb5_free_principal (context, entry->principal); 710 krb5_free_keyblock_contents (context, &entry->keyblock); 711 memset(entry, 0, sizeof(*entry)); 712 return 0; 713 } 714 715 /** 716 * Set `cursor' to point at the beginning of `id'. 717 * 718 * @param context a Keberos context. 719 * @param id a keytab. 720 * @param cursor a newly allocated cursor, free with krb5_kt_end_seq_get(). 721 * 722 * @return Return an error code or 0, see krb5_get_error_message(). 723 * 724 * @ingroup krb5_keytab 725 */ 726 727 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 728 krb5_kt_start_seq_get(krb5_context context, 729 krb5_keytab id, 730 krb5_kt_cursor *cursor) 731 { 732 if(id->start_seq_get == NULL) { 733 krb5_set_error_message(context, HEIM_ERR_OPNOTSUPP, 734 N_("start_seq_get is not supported " 735 "in the %s keytab type", ""), 736 id->prefix); 737 return HEIM_ERR_OPNOTSUPP; 738 } 739 return (*id->start_seq_get)(context, id, cursor); 740 } 741 742 /** 743 * Get the next entry from keytab, advance the cursor. On last entry 744 * the function will return KRB5_KT_END. 745 * 746 * @param context a Keberos context. 747 * @param id a keytab. 748 * @param entry the returned entry, free with krb5_kt_free_entry(). 749 * @param cursor the cursor of the iteration. 750 * 751 * @return Return an error code or 0, see krb5_get_error_message(). 752 * 753 * @ingroup krb5_keytab 754 */ 755 756 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 757 krb5_kt_next_entry(krb5_context context, 758 krb5_keytab id, 759 krb5_keytab_entry *entry, 760 krb5_kt_cursor *cursor) 761 { 762 if(id->next_entry == NULL) { 763 krb5_set_error_message(context, HEIM_ERR_OPNOTSUPP, 764 N_("next_entry is not supported in the %s " 765 " keytab", ""), 766 id->prefix); 767 return HEIM_ERR_OPNOTSUPP; 768 } 769 return (*id->next_entry)(context, id, entry, cursor); 770 } 771 772 /** 773 * Release all resources associated with `cursor'. 774 * 775 * @param context a Keberos context. 776 * @param id a keytab. 777 * @param cursor the cursor to free. 778 * 779 * @return Return an error code or 0, see krb5_get_error_message(). 780 * 781 * @ingroup krb5_keytab 782 */ 783 784 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 785 krb5_kt_end_seq_get(krb5_context context, 786 krb5_keytab id, 787 krb5_kt_cursor *cursor) 788 { 789 if(id->end_seq_get == NULL) { 790 krb5_set_error_message(context, HEIM_ERR_OPNOTSUPP, 791 "end_seq_get is not supported in the %s " 792 " keytab", id->prefix); 793 return HEIM_ERR_OPNOTSUPP; 794 } 795 return (*id->end_seq_get)(context, id, cursor); 796 } 797 798 /** 799 * Add the entry in `entry' to the keytab `id'. 800 * 801 * @param context a Keberos context. 802 * @param id a keytab. 803 * @param entry the entry to add 804 * 805 * @return Return an error code or 0, see krb5_get_error_message(). 806 * 807 * @ingroup krb5_keytab 808 */ 809 810 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 811 krb5_kt_add_entry(krb5_context context, 812 krb5_keytab id, 813 krb5_keytab_entry *entry) 814 { 815 if(id->add == NULL) { 816 krb5_set_error_message(context, KRB5_KT_NOWRITE, 817 N_("Add is not supported in the %s keytab", ""), 818 id->prefix); 819 return KRB5_KT_NOWRITE; 820 } 821 entry->timestamp = time(NULL); 822 return (*id->add)(context, id,entry); 823 } 824 825 /** 826 * Remove an entry from the keytab, matching is done using 827 * krb5_kt_compare(). 828 829 * @param context a Keberos context. 830 * @param id a keytab. 831 * @param entry the entry to remove 832 * 833 * @return Return an error code or 0, see krb5_get_error_message(). 834 * 835 * @ingroup krb5_keytab 836 */ 837 838 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 839 krb5_kt_remove_entry(krb5_context context, 840 krb5_keytab id, 841 krb5_keytab_entry *entry) 842 { 843 if(id->remove == NULL) { 844 krb5_set_error_message(context, KRB5_KT_NOWRITE, 845 N_("Remove is not supported in the %s keytab", ""), 846 id->prefix); 847 return KRB5_KT_NOWRITE; 848 } 849 return (*id->remove)(context, id, entry); 850 } 851 852 /** 853 * Return true if the keytab exists and have entries 854 * 855 * @param context a Keberos context. 856 * @param id a keytab. 857 * 858 * @return Return an error code or 0, see krb5_get_error_message(). 859 * 860 * @ingroup krb5_keytab 861 */ 862 863 KRB5_LIB_FUNCTION krb5_boolean KRB5_LIB_CALL 864 krb5_kt_have_content(krb5_context context, 865 krb5_keytab id) 866 { 867 krb5_keytab_entry entry; 868 krb5_kt_cursor cursor; 869 krb5_error_code ret; 870 char *name; 871 872 ret = krb5_kt_start_seq_get(context, id, &cursor); 873 if (ret) 874 goto notfound; 875 876 ret = krb5_kt_next_entry(context, id, &entry, &cursor); 877 krb5_kt_end_seq_get(context, id, &cursor); 878 if (ret) 879 goto notfound; 880 881 krb5_kt_free_entry(context, &entry); 882 883 return 0; 884 885 notfound: 886 ret = krb5_kt_get_full_name(context, id, &name); 887 if (ret == 0) { 888 krb5_set_error_message(context, KRB5_KT_NOTFOUND, 889 N_("No entry in keytab: %s", ""), name); 890 free(name); 891 } 892 return KRB5_KT_NOTFOUND; 893 } 894