155682Smarkm/* 2233294Sstas * Copyright (c) 1997 - 2005 Kungliga Tekniska H��gskolan 3233294Sstas * (Royal Institute of Technology, Stockholm, Sweden). 4233294Sstas * All rights reserved. 555682Smarkm * 6233294Sstas * Redistribution and use in source and binary forms, with or without 7233294Sstas * modification, are permitted provided that the following conditions 8233294Sstas * are met: 955682Smarkm * 10233294Sstas * 1. Redistributions of source code must retain the above copyright 11233294Sstas * notice, this list of conditions and the following disclaimer. 1255682Smarkm * 13233294Sstas * 2. Redistributions in binary form must reproduce the above copyright 14233294Sstas * notice, this list of conditions and the following disclaimer in the 15233294Sstas * documentation and/or other materials provided with the distribution. 1655682Smarkm * 17233294Sstas * 3. Neither the name of the Institute nor the names of its contributors 18233294Sstas * may be used to endorse or promote products derived from this software 19233294Sstas * without specific prior written permission. 2055682Smarkm * 21233294Sstas * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND 22233294Sstas * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23233294Sstas * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24233294Sstas * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE 25233294Sstas * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26233294Sstas * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 27233294Sstas * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28233294Sstas * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29233294Sstas * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 30233294Sstas * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31233294Sstas * SUCH DAMAGE. 3255682Smarkm */ 3355682Smarkm 3455682Smarkm#include "krb5_locl.h" 3555682Smarkm 36233294Sstas/** 37233294Sstas * @page krb5_keytab_intro The keytab handing functions 38233294Sstas * @section section_krb5_keytab Kerberos Keytabs 39233294Sstas * 40233294Sstas * See the library functions here: @ref krb5_keytab 41233294Sstas * 42233294Sstas * Keytabs are long term key storage for servers, their equvalment of 43233294Sstas * password files. 44233294Sstas * 45233294Sstas * Normally the only function that useful for server are to specify 46233294Sstas * what keytab to use to other core functions like krb5_rd_req() 47233294Sstas * krb5_kt_resolve(), and krb5_kt_close(). 48233294Sstas * 49233294Sstas * @subsection krb5_keytab_names Keytab names 50233294Sstas * 51233294Sstas * A keytab name is on the form type:residual. The residual part is 52233294Sstas * specific to each keytab-type. 53233294Sstas * 54233294Sstas * When a keytab-name is resolved, the type is matched with an internal 55233294Sstas * list of keytab types. If there is no matching keytab type, 56233294Sstas * the default keytab is used. The current default type is FILE. 57233294Sstas * 58233294Sstas * The default value can be changed in the configuration file 59233294Sstas * /etc/krb5.conf by setting the variable 60233294Sstas * [defaults]default_keytab_name. 61233294Sstas * 62233294Sstas * The keytab types that are implemented in Heimdal are: 63233294Sstas * - file 64233294Sstas * store the keytab in a file, the type's name is FILE . The 65233294Sstas * residual part is a filename. For compatibility with other 66233294Sstas * Kerberos implemtation WRFILE and JAVA14 is also accepted. WRFILE 67233294Sstas * has the same format as FILE. JAVA14 have a format that is 68233294Sstas * compatible with older versions of MIT kerberos and SUN's Java 69233294Sstas * based installation. They store a truncted kvno, so when the knvo 70233294Sstas * excess 255, they are truncted in this format. 71233294Sstas * 72233294Sstas * - keytab 73233294Sstas * store the keytab in a AFS keyfile (usually /usr/afs/etc/KeyFile ), 74233294Sstas * the type's name is AFSKEYFILE. The residual part is a filename. 75233294Sstas * 76233294Sstas * - memory 77233294Sstas * The keytab is stored in a memory segment. This allows sensitive 78233294Sstas * and/or temporary data not to be stored on disk. The type's name 79233294Sstas * is MEMORY. Each MEMORY keytab is referenced counted by and 80233294Sstas * opened by the residual name, so two handles can point to the 81233294Sstas * same memory area. When the last user closes using krb5_kt_close() 82233294Sstas * the keytab, the keys in they keytab is memset() to zero and freed 83233294Sstas * and can no longer be looked up by name. 84233294Sstas * 85233294Sstas * 86233294Sstas * @subsection krb5_keytab_example Keytab example 87233294Sstas * 88233294Sstas * This is a minimalistic version of ktutil. 89233294Sstas * 90233294Sstas * @code 91233294Sstasint 92233294Sstasmain (int argc, char **argv) 93233294Sstas{ 94233294Sstas krb5_context context; 95233294Sstas krb5_keytab keytab; 96233294Sstas krb5_kt_cursor cursor; 97233294Sstas krb5_keytab_entry entry; 98233294Sstas krb5_error_code ret; 99233294Sstas char *principal; 10055682Smarkm 101233294Sstas if (krb5_init_context (&context) != 0) 102233294Sstas errx(1, "krb5_context"); 103233294Sstas 104233294Sstas ret = krb5_kt_default (context, &keytab); 105233294Sstas if (ret) 106233294Sstas krb5_err(context, 1, ret, "krb5_kt_default"); 107233294Sstas 108233294Sstas ret = krb5_kt_start_seq_get(context, keytab, &cursor); 109233294Sstas if (ret) 110233294Sstas krb5_err(context, 1, ret, "krb5_kt_start_seq_get"); 111233294Sstas while((ret = krb5_kt_next_entry(context, keytab, &entry, &cursor)) == 0){ 112233294Sstas krb5_unparse_name(context, entry.principal, &principal); 113233294Sstas printf("principal: %s\n", principal); 114233294Sstas free(principal); 115233294Sstas krb5_kt_free_entry(context, &entry); 116233294Sstas } 117233294Sstas ret = krb5_kt_end_seq_get(context, keytab, &cursor); 118233294Sstas if (ret) 119233294Sstas krb5_err(context, 1, ret, "krb5_kt_end_seq_get"); 120233294Sstas ret = krb5_kt_close(context, keytab); 121233294Sstas if (ret) 122233294Sstas krb5_err(context, 1, ret, "krb5_kt_close"); 123233294Sstas krb5_free_context(context); 124233294Sstas return 0; 125233294Sstas} 126233294Sstas * @endcode 127233294Sstas * 12855682Smarkm */ 12955682Smarkm 130233294Sstas 131233294Sstas/** 132233294Sstas * Register a new keytab backend. 133233294Sstas * 134233294Sstas * @param context a Keberos context. 135233294Sstas * @param ops a backend to register. 136233294Sstas * 137233294Sstas * @return Return an error code or 0, see krb5_get_error_message(). 138233294Sstas * 139233294Sstas * @ingroup krb5_keytab 140233294Sstas */ 141233294Sstas 142233294SstasKRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 14355682Smarkmkrb5_kt_register(krb5_context context, 14455682Smarkm const krb5_kt_ops *ops) 14555682Smarkm{ 14655682Smarkm struct krb5_keytab_data *tmp; 14755682Smarkm 148120945Snectar if (strlen(ops->prefix) > KRB5_KT_PREFIX_MAX_LEN - 1) { 149233294Sstas krb5_set_error_message(context, KRB5_KT_BADNAME, 150233294Sstas N_("can't register cache type, prefix too long", "")); 151178825Sdfr return KRB5_KT_BADNAME; 152120945Snectar } 153120945Snectar 15455682Smarkm tmp = realloc(context->kt_types, 15555682Smarkm (context->num_kt_types + 1) * sizeof(*context->kt_types)); 15678527Sassar if(tmp == NULL) { 157233294Sstas krb5_set_error_message(context, ENOMEM, 158233294Sstas N_("malloc: out of memory", "")); 15955682Smarkm return ENOMEM; 16078527Sassar } 16155682Smarkm memcpy(&tmp[context->num_kt_types], ops, 16255682Smarkm sizeof(tmp[context->num_kt_types])); 16355682Smarkm context->kt_types = tmp; 16455682Smarkm context->num_kt_types++; 16555682Smarkm return 0; 16655682Smarkm} 16755682Smarkm 168233294Sstasstatic const char * 169233294Sstaskeytab_name(const char *name, const char **type, size_t *type_len) 170233294Sstas{ 171233294Sstas const char *residual; 172233294Sstas 173233294Sstas residual = strchr(name, ':'); 174233294Sstas 175233294Sstas if (residual == NULL || 176233294Sstas name[0] == '/' 177233294Sstas#ifdef _WIN32 178233294Sstas /* Avoid treating <drive>:<path> as a keytab type 179233294Sstas * specification */ 180233294Sstas || name + 1 == residual 181233294Sstas#endif 182233294Sstas ) { 183233294Sstas 184233294Sstas *type = "FILE"; 185233294Sstas *type_len = strlen(*type); 186233294Sstas residual = name; 187233294Sstas } else { 188233294Sstas *type = name; 189233294Sstas *type_len = residual - name; 190233294Sstas residual++; 191233294Sstas } 192233294Sstas 193233294Sstas return residual; 194233294Sstas} 195233294Sstas 196233294Sstas/** 19755682Smarkm * Resolve the keytab name (of the form `type:residual') in `name' 19855682Smarkm * into a keytab in `id'. 199233294Sstas * 200233294Sstas * @param context a Keberos context. 201233294Sstas * @param name name to resolve 202233294Sstas * @param id resulting keytab, free with krb5_kt_close(). 203233294Sstas * 204233294Sstas * @return Return an error code or 0, see krb5_get_error_message(). 205233294Sstas * 206233294Sstas * @ingroup krb5_keytab 20755682Smarkm */ 20855682Smarkm 209233294Sstas 210233294SstasKRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 21155682Smarkmkrb5_kt_resolve(krb5_context context, 21255682Smarkm const char *name, 21355682Smarkm krb5_keytab *id) 21455682Smarkm{ 21555682Smarkm krb5_keytab k; 21655682Smarkm int i; 21755682Smarkm const char *type, *residual; 21855682Smarkm size_t type_len; 21955682Smarkm krb5_error_code ret; 22055682Smarkm 221233294Sstas residual = keytab_name(name, &type, &type_len); 222233294Sstas 22355682Smarkm for(i = 0; i < context->num_kt_types; i++) { 22490926Snectar if(strncasecmp(type, context->kt_types[i].prefix, type_len) == 0) 22555682Smarkm break; 22655682Smarkm } 22778527Sassar if(i == context->num_kt_types) { 228233294Sstas krb5_set_error_message(context, KRB5_KT_UNKNOWN_TYPE, 229233294Sstas N_("unknown keytab type %.*s", "type"), 230233294Sstas (int)type_len, type); 23155682Smarkm return KRB5_KT_UNKNOWN_TYPE; 23278527Sassar } 233233294Sstas 23455682Smarkm k = malloc (sizeof(*k)); 23578527Sassar if (k == NULL) { 236233294Sstas krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", "")); 23755682Smarkm return ENOMEM; 23878527Sassar } 23955682Smarkm memcpy(k, &context->kt_types[i], sizeof(*k)); 24055682Smarkm k->data = NULL; 24155682Smarkm ret = (*k->resolve)(context, residual, k); 24255682Smarkm if(ret) { 24355682Smarkm free(k); 24455682Smarkm k = NULL; 24555682Smarkm } 24655682Smarkm *id = k; 24755682Smarkm return ret; 24855682Smarkm} 24955682Smarkm 250233294Sstas/** 25155682Smarkm * copy the name of the default keytab into `name'. 252233294Sstas * 253233294Sstas * @param context a Keberos context. 254233294Sstas * @param name buffer where the name will be written 255233294Sstas * @param namesize length of name 256233294Sstas * 257233294Sstas * @return Return an error code or 0, see krb5_get_error_message(). 258233294Sstas * 259233294Sstas * @ingroup krb5_keytab 26055682Smarkm */ 26155682Smarkm 262233294SstasKRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 26355682Smarkmkrb5_kt_default_name(krb5_context context, char *name, size_t namesize) 26455682Smarkm{ 26578527Sassar if (strlcpy (name, context->default_keytab, namesize) >= namesize) { 266233294Sstas krb5_clear_error_message (context); 26755682Smarkm return KRB5_CONFIG_NOTENUFSPACE; 26878527Sassar } 26955682Smarkm return 0; 27055682Smarkm} 27155682Smarkm 272233294Sstas/** 273233294Sstas * Copy the name of the default modify keytab into `name'. 274233294Sstas * 275233294Sstas * @param context a Keberos context. 276233294Sstas * @param name buffer where the name will be written 277233294Sstas * @param namesize length of name 278233294Sstas * 279233294Sstas * @return Return an error code or 0, see krb5_get_error_message(). 280233294Sstas * 281233294Sstas * @ingroup krb5_keytab 28278527Sassar */ 28378527Sassar 284233294SstasKRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 28578527Sassarkrb5_kt_default_modify_name(krb5_context context, char *name, size_t namesize) 28678527Sassar{ 28790926Snectar const char *kt = NULL; 28890926Snectar if(context->default_keytab_modify == NULL) { 28990926Snectar if(strncasecmp(context->default_keytab, "ANY:", 4) != 0) 29090926Snectar kt = context->default_keytab; 29190926Snectar else { 29290926Snectar size_t len = strcspn(context->default_keytab + 4, ","); 29390926Snectar if(len >= namesize) { 294233294Sstas krb5_clear_error_message(context); 29590926Snectar return KRB5_CONFIG_NOTENUFSPACE; 29690926Snectar } 29790926Snectar strlcpy(name, context->default_keytab + 4, namesize); 29890926Snectar name[len] = '\0'; 29990926Snectar return 0; 300233294Sstas } 30190926Snectar } else 30290926Snectar kt = context->default_keytab_modify; 30390926Snectar if (strlcpy (name, kt, namesize) >= namesize) { 304233294Sstas krb5_clear_error_message (context); 30578527Sassar return KRB5_CONFIG_NOTENUFSPACE; 30678527Sassar } 30778527Sassar return 0; 30878527Sassar} 30978527Sassar 310233294Sstas/** 31155682Smarkm * Set `id' to the default keytab. 312233294Sstas * 313233294Sstas * @param context a Keberos context. 314233294Sstas * @param id the new default keytab. 315233294Sstas * 316233294Sstas * @return Return an error code or 0, see krb5_get_error_message(). 317233294Sstas * 318233294Sstas * @ingroup krb5_keytab 31955682Smarkm */ 32055682Smarkm 321233294SstasKRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 32255682Smarkmkrb5_kt_default(krb5_context context, krb5_keytab *id) 32355682Smarkm{ 32455682Smarkm return krb5_kt_resolve (context, context->default_keytab, id); 32555682Smarkm} 32655682Smarkm 327233294Sstas/** 32855682Smarkm * Read the key identified by `(principal, vno, enctype)' from the 32955682Smarkm * keytab in `keyprocarg' (the default if == NULL) into `*key'. 330233294Sstas * 331233294Sstas * @param context a Keberos context. 332233294Sstas * @param keyprocarg 333233294Sstas * @param principal 334233294Sstas * @param vno 335233294Sstas * @param enctype 336233294Sstas * @param key 337233294Sstas * 338233294Sstas * @return Return an error code or 0, see krb5_get_error_message(). 339233294Sstas * 340233294Sstas * @ingroup krb5_keytab 34155682Smarkm */ 34255682Smarkm 343233294SstasKRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 34455682Smarkmkrb5_kt_read_service_key(krb5_context context, 34555682Smarkm krb5_pointer keyprocarg, 34655682Smarkm krb5_principal principal, 34755682Smarkm krb5_kvno vno, 34855682Smarkm krb5_enctype enctype, 34955682Smarkm krb5_keyblock **key) 35055682Smarkm{ 35155682Smarkm krb5_keytab keytab; 35255682Smarkm krb5_keytab_entry entry; 35355682Smarkm krb5_error_code ret; 35455682Smarkm 35555682Smarkm if (keyprocarg) 35655682Smarkm ret = krb5_kt_resolve (context, keyprocarg, &keytab); 35755682Smarkm else 35855682Smarkm ret = krb5_kt_default (context, &keytab); 35955682Smarkm 36055682Smarkm if (ret) 36155682Smarkm return ret; 36255682Smarkm 36355682Smarkm ret = krb5_kt_get_entry (context, keytab, principal, vno, enctype, &entry); 36455682Smarkm krb5_kt_close (context, keytab); 36555682Smarkm if (ret) 36655682Smarkm return ret; 36755682Smarkm ret = krb5_copy_keyblock (context, &entry.keyblock, key); 36855682Smarkm krb5_kt_free_entry(context, &entry); 36955682Smarkm return ret; 37055682Smarkm} 37155682Smarkm 372233294Sstas/** 373120945Snectar * Return the type of the `keytab' in the string `prefix of length 374120945Snectar * `prefixsize'. 375233294Sstas * 376233294Sstas * @param context a Keberos context. 377233294Sstas * @param keytab the keytab to get the prefix for 378233294Sstas * @param prefix prefix buffer 379233294Sstas * @param prefixsize length of prefix buffer 380233294Sstas * 381233294Sstas * @return Return an error code or 0, see krb5_get_error_message(). 382233294Sstas * 383233294Sstas * @ingroup krb5_keytab 384120945Snectar */ 385120945Snectar 386233294SstasKRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 387120945Snectarkrb5_kt_get_type(krb5_context context, 388120945Snectar krb5_keytab keytab, 389120945Snectar char *prefix, 390120945Snectar size_t prefixsize) 391120945Snectar{ 392120945Snectar strlcpy(prefix, keytab->prefix, prefixsize); 393120945Snectar return 0; 394120945Snectar} 395120945Snectar 396233294Sstas/** 39755682Smarkm * Retrieve the name of the keytab `keytab' into `name', `namesize' 398233294Sstas * 399233294Sstas * @param context a Keberos context. 400233294Sstas * @param keytab the keytab to get the name for. 401233294Sstas * @param name name buffer. 402233294Sstas * @param namesize size of name buffer. 403233294Sstas * 404233294Sstas * @return Return an error code or 0, see krb5_get_error_message(). 405233294Sstas * 406233294Sstas * @ingroup krb5_keytab 40755682Smarkm */ 40855682Smarkm 409233294SstasKRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 410233294Sstaskrb5_kt_get_name(krb5_context context, 41155682Smarkm krb5_keytab keytab, 41255682Smarkm char *name, 41355682Smarkm size_t namesize) 41455682Smarkm{ 41555682Smarkm return (*keytab->get_name)(context, keytab, name, namesize); 41655682Smarkm} 41755682Smarkm 418233294Sstas/** 419178825Sdfr * Retrieve the full name of the keytab `keytab' and store the name in 420233294Sstas * `str'. 421233294Sstas * 422233294Sstas * @param context a Keberos context. 423233294Sstas * @param keytab keytab to get name for. 424233294Sstas * @param str the name of the keytab name, usee krb5_xfree() to free 425233294Sstas * the string. On error, *str is set to NULL. 426233294Sstas * 427233294Sstas * @return Return an error code or 0, see krb5_get_error_message(). 428233294Sstas * 429233294Sstas * @ingroup krb5_keytab 43055682Smarkm */ 43155682Smarkm 432233294SstasKRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 433233294Sstaskrb5_kt_get_full_name(krb5_context context, 434178825Sdfr krb5_keytab keytab, 435178825Sdfr char **str) 436178825Sdfr{ 437178825Sdfr char type[KRB5_KT_PREFIX_MAX_LEN]; 438178825Sdfr char name[MAXPATHLEN]; 439178825Sdfr krb5_error_code ret; 440233294Sstas 441178825Sdfr *str = NULL; 442178825Sdfr 443178825Sdfr ret = krb5_kt_get_type(context, keytab, type, sizeof(type)); 444178825Sdfr if (ret) 445178825Sdfr return ret; 446178825Sdfr 447178825Sdfr ret = krb5_kt_get_name(context, keytab, name, sizeof(name)); 448178825Sdfr if (ret) 449178825Sdfr return ret; 450178825Sdfr 451178825Sdfr if (asprintf(str, "%s:%s", type, name) == -1) { 452233294Sstas krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", "")); 453178825Sdfr *str = NULL; 454178825Sdfr return ENOMEM; 455178825Sdfr } 456178825Sdfr 457178825Sdfr return 0; 458178825Sdfr} 459178825Sdfr 460233294Sstas/** 461178825Sdfr * Finish using the keytab in `id'. All resources will be released, 462233294Sstas * even on errors. 463233294Sstas * 464233294Sstas * @param context a Keberos context. 465233294Sstas * @param id keytab to close. 466233294Sstas * 467233294Sstas * @return Return an error code or 0, see krb5_get_error_message(). 468233294Sstas * 469233294Sstas * @ingroup krb5_keytab 470178825Sdfr */ 471178825Sdfr 472233294SstasKRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 473233294Sstaskrb5_kt_close(krb5_context context, 47455682Smarkm krb5_keytab id) 47555682Smarkm{ 47655682Smarkm krb5_error_code ret; 47755682Smarkm 47855682Smarkm ret = (*id->close)(context, id); 479178825Sdfr memset(id, 0, sizeof(*id)); 480178825Sdfr free(id); 48155682Smarkm return ret; 48255682Smarkm} 48355682Smarkm 484233294Sstas/** 485233294Sstas * Destroy (remove) the keytab in `id'. All resources will be released, 486233294Sstas * even on errors, does the equvalment of krb5_kt_close() on the resources. 487233294Sstas * 488233294Sstas * @param context a Keberos context. 489233294Sstas * @param id keytab to destroy. 490233294Sstas * 491233294Sstas * @return Return an error code or 0, see krb5_get_error_message(). 492233294Sstas * 493233294Sstas * @ingroup krb5_keytab 494233294Sstas */ 495233294Sstas 496233294SstasKRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 497233294Sstaskrb5_kt_destroy(krb5_context context, 498233294Sstas krb5_keytab id) 499233294Sstas{ 500233294Sstas krb5_error_code ret; 501233294Sstas 502233294Sstas ret = (*id->destroy)(context, id); 503233294Sstas krb5_kt_close(context, id); 504233294Sstas return ret; 505233294Sstas} 506233294Sstas 50755682Smarkm/* 508233294Sstas * Match any aliases in keytab `entry' with `principal'. 509233294Sstas */ 510233294Sstas 511233294Sstasstatic krb5_boolean 512233294Sstascompare_aliseses(krb5_context context, 513233294Sstas krb5_keytab_entry *entry, 514233294Sstas krb5_const_principal principal) 515233294Sstas{ 516233294Sstas unsigned int i; 517233294Sstas if (entry->aliases == NULL) 518233294Sstas return FALSE; 519233294Sstas for (i = 0; i < entry->aliases->len; i++) 520233294Sstas if (krb5_principal_compare(context, &entry->aliases->val[i], principal)) 521233294Sstas return TRUE; 522233294Sstas return FALSE; 523233294Sstas} 524233294Sstas 525233294Sstas/** 52655682Smarkm * Compare `entry' against `principal, vno, enctype'. 52755682Smarkm * Any of `principal, vno, enctype' might be 0 which acts as a wildcard. 52855682Smarkm * Return TRUE if they compare the same, FALSE otherwise. 529233294Sstas * 530233294Sstas * @param context a Keberos context. 531233294Sstas * @param entry an entry to match with. 532233294Sstas * @param principal principal to match, NULL matches all principals. 533233294Sstas * @param vno key version to match, 0 matches all key version numbers. 534233294Sstas * @param enctype encryption type to match, 0 matches all encryption types. 535233294Sstas * 536233294Sstas * @return Return TRUE or match, FALSE if not matched. 537233294Sstas * 538233294Sstas * @ingroup krb5_keytab 53955682Smarkm */ 54055682Smarkm 541233294SstasKRB5_LIB_FUNCTION krb5_boolean KRB5_LIB_CALL 54255682Smarkmkrb5_kt_compare(krb5_context context, 543233294Sstas krb5_keytab_entry *entry, 54455682Smarkm krb5_const_principal principal, 54555682Smarkm krb5_kvno vno, 54655682Smarkm krb5_enctype enctype) 54755682Smarkm{ 548233294Sstas if(principal != NULL && 549233294Sstas !(krb5_principal_compare(context, entry->principal, principal) || 550233294Sstas compare_aliseses(context, entry, principal))) 55155682Smarkm return FALSE; 55255682Smarkm if(vno && vno != entry->vno) 55355682Smarkm return FALSE; 55455682Smarkm if(enctype && enctype != entry->keyblock.keytype) 55555682Smarkm return FALSE; 55655682Smarkm return TRUE; 55755682Smarkm} 55855682Smarkm 559233294Sstaskrb5_error_code 560233294Sstas_krb5_kt_principal_not_found(krb5_context context, 561233294Sstas krb5_error_code ret, 562233294Sstas krb5_keytab id, 563233294Sstas krb5_const_principal principal, 564233294Sstas krb5_enctype enctype, 565233294Sstas int kvno) 566233294Sstas{ 567233294Sstas char princ[256], kvno_str[25], *kt_name; 568233294Sstas char *enctype_str = NULL; 569233294Sstas 570233294Sstas krb5_unparse_name_fixed (context, principal, princ, sizeof(princ)); 571233294Sstas krb5_kt_get_full_name (context, id, &kt_name); 572233294Sstas krb5_enctype_to_string(context, enctype, &enctype_str); 573233294Sstas 574233294Sstas if (kvno) 575233294Sstas snprintf(kvno_str, sizeof(kvno_str), "(kvno %d)", kvno); 576233294Sstas else 577233294Sstas kvno_str[0] = '\0'; 578233294Sstas 579233294Sstas krb5_set_error_message (context, ret, 580233294Sstas N_("Failed to find %s%s in keytab %s (%s)", 581233294Sstas "principal, kvno, keytab file, enctype"), 582233294Sstas princ, 583233294Sstas kvno_str, 584233294Sstas kt_name ? kt_name : "unknown keytab", 585233294Sstas enctype_str ? enctype_str : "unknown enctype"); 586233294Sstas free(kt_name); 587233294Sstas free(enctype_str); 588233294Sstas return ret; 589233294Sstas} 590233294Sstas 591233294Sstas 592233294Sstas/** 59355682Smarkm * Retrieve the keytab entry for `principal, kvno, enctype' into `entry' 594233294Sstas * from the keytab `id'. Matching is done like krb5_kt_compare(). 595233294Sstas * 596233294Sstas * @param context a Keberos context. 597233294Sstas * @param id a keytab. 598233294Sstas * @param principal principal to match, NULL matches all principals. 599233294Sstas * @param kvno key version to match, 0 matches all key version numbers. 600233294Sstas * @param enctype encryption type to match, 0 matches all encryption types. 601233294Sstas * @param entry the returned entry, free with krb5_kt_free_entry(). 602233294Sstas * 603233294Sstas * @return Return an error code or 0, see krb5_get_error_message(). 604233294Sstas * 605233294Sstas * @ingroup krb5_keytab 60655682Smarkm */ 60755682Smarkm 608233294SstasKRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 60955682Smarkmkrb5_kt_get_entry(krb5_context context, 61055682Smarkm krb5_keytab id, 61155682Smarkm krb5_const_principal principal, 61255682Smarkm krb5_kvno kvno, 61355682Smarkm krb5_enctype enctype, 61455682Smarkm krb5_keytab_entry *entry) 61555682Smarkm{ 61655682Smarkm krb5_keytab_entry tmp; 61755682Smarkm krb5_error_code ret; 61855682Smarkm krb5_kt_cursor cursor; 61955682Smarkm 62055682Smarkm if(id->get) 62155682Smarkm return (*id->get)(context, id, principal, kvno, enctype, entry); 62255682Smarkm 62355682Smarkm ret = krb5_kt_start_seq_get (context, id, &cursor); 624178825Sdfr if (ret) { 625233294Sstas /* This is needed for krb5_verify_init_creds, but keep error 626233294Sstas * string from previous error for the human. */ 627233294Sstas context->error_code = KRB5_KT_NOTFOUND; 628233294Sstas return KRB5_KT_NOTFOUND; 629178825Sdfr } 63055682Smarkm 63155682Smarkm entry->vno = 0; 63255682Smarkm while (krb5_kt_next_entry(context, id, &tmp, &cursor) == 0) { 63355682Smarkm if (krb5_kt_compare(context, &tmp, principal, 0, enctype)) { 634102644Snectar /* the file keytab might only store the lower 8 bits of 635102644Snectar the kvno, so only compare those bits */ 636102644Snectar if (kvno == tmp.vno 637102644Snectar || (tmp.vno < 256 && kvno % 256 == tmp.vno)) { 63855682Smarkm krb5_kt_copy_entry_contents (context, &tmp, entry); 63955682Smarkm krb5_kt_free_entry (context, &tmp); 64055682Smarkm krb5_kt_end_seq_get(context, id, &cursor); 64155682Smarkm return 0; 64255682Smarkm } else if (kvno == 0 && tmp.vno > entry->vno) { 64355682Smarkm if (entry->vno) 64455682Smarkm krb5_kt_free_entry (context, entry); 64555682Smarkm krb5_kt_copy_entry_contents (context, &tmp, entry); 64655682Smarkm } 64755682Smarkm } 64855682Smarkm krb5_kt_free_entry(context, &tmp); 64955682Smarkm } 65055682Smarkm krb5_kt_end_seq_get (context, id, &cursor); 651233294Sstas if (entry->vno == 0) 652233294Sstas return _krb5_kt_principal_not_found(context, KRB5_KT_NOTFOUND, 653233294Sstas id, principal, enctype, kvno); 654233294Sstas return 0; 65555682Smarkm} 65655682Smarkm 657233294Sstas/** 65855682Smarkm * Copy the contents of `in' into `out'. 659233294Sstas * 660233294Sstas * @param context a Keberos context. 661233294Sstas * @param in the keytab entry to copy. 662233294Sstas * @param out the copy of the keytab entry, free with krb5_kt_free_entry(). 663233294Sstas * 664233294Sstas * @return Return an error code or 0, see krb5_get_error_message(). 665233294Sstas * 666233294Sstas * @ingroup krb5_keytab 667233294Sstas */ 66855682Smarkm 669233294SstasKRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 67055682Smarkmkrb5_kt_copy_entry_contents(krb5_context context, 67155682Smarkm const krb5_keytab_entry *in, 67255682Smarkm krb5_keytab_entry *out) 67355682Smarkm{ 67455682Smarkm krb5_error_code ret; 67555682Smarkm 67655682Smarkm memset(out, 0, sizeof(*out)); 67755682Smarkm out->vno = in->vno; 67855682Smarkm 67955682Smarkm ret = krb5_copy_principal (context, in->principal, &out->principal); 68055682Smarkm if (ret) 68155682Smarkm goto fail; 68255682Smarkm ret = krb5_copy_keyblock_contents (context, 68355682Smarkm &in->keyblock, 68455682Smarkm &out->keyblock); 68555682Smarkm if (ret) 68655682Smarkm goto fail; 68755682Smarkm out->timestamp = in->timestamp; 68855682Smarkm return 0; 68955682Smarkmfail: 69055682Smarkm krb5_kt_free_entry (context, out); 69155682Smarkm return ret; 69255682Smarkm} 69355682Smarkm 694233294Sstas/** 69555682Smarkm * Free the contents of `entry'. 696233294Sstas * 697233294Sstas * @param context a Keberos context. 698233294Sstas * @param entry the entry to free 699233294Sstas * 700233294Sstas * @return Return an error code or 0, see krb5_get_error_message(). 701233294Sstas * 702233294Sstas * @ingroup krb5_keytab 70355682Smarkm */ 70455682Smarkm 705233294SstasKRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 70655682Smarkmkrb5_kt_free_entry(krb5_context context, 70755682Smarkm krb5_keytab_entry *entry) 70855682Smarkm{ 709178825Sdfr krb5_free_principal (context, entry->principal); 710178825Sdfr krb5_free_keyblock_contents (context, &entry->keyblock); 711178825Sdfr memset(entry, 0, sizeof(*entry)); 71255682Smarkm return 0; 71355682Smarkm} 71455682Smarkm 715233294Sstas/** 71655682Smarkm * Set `cursor' to point at the beginning of `id'. 717233294Sstas * 718233294Sstas * @param context a Keberos context. 719233294Sstas * @param id a keytab. 720233294Sstas * @param cursor a newly allocated cursor, free with krb5_kt_end_seq_get(). 721233294Sstas * 722233294Sstas * @return Return an error code or 0, see krb5_get_error_message(). 723233294Sstas * 724233294Sstas * @ingroup krb5_keytab 72555682Smarkm */ 72655682Smarkm 727233294SstasKRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 72855682Smarkmkrb5_kt_start_seq_get(krb5_context context, 72955682Smarkm krb5_keytab id, 73055682Smarkm krb5_kt_cursor *cursor) 73155682Smarkm{ 73278527Sassar if(id->start_seq_get == NULL) { 733233294Sstas krb5_set_error_message(context, HEIM_ERR_OPNOTSUPP, 734233294Sstas N_("start_seq_get is not supported " 735233294Sstas "in the %s keytab type", ""), 736233294Sstas id->prefix); 73755682Smarkm return HEIM_ERR_OPNOTSUPP; 73878527Sassar } 73955682Smarkm return (*id->start_seq_get)(context, id, cursor); 74055682Smarkm} 74155682Smarkm 742233294Sstas/** 743233294Sstas * Get the next entry from keytab, advance the cursor. On last entry 744233294Sstas * the function will return KRB5_KT_END. 745233294Sstas * 746233294Sstas * @param context a Keberos context. 747233294Sstas * @param id a keytab. 748233294Sstas * @param entry the returned entry, free with krb5_kt_free_entry(). 749233294Sstas * @param cursor the cursor of the iteration. 750233294Sstas * 751233294Sstas * @return Return an error code or 0, see krb5_get_error_message(). 752233294Sstas * 753233294Sstas * @ingroup krb5_keytab 75455682Smarkm */ 75555682Smarkm 756233294SstasKRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 75755682Smarkmkrb5_kt_next_entry(krb5_context context, 75855682Smarkm krb5_keytab id, 75955682Smarkm krb5_keytab_entry *entry, 76055682Smarkm krb5_kt_cursor *cursor) 76155682Smarkm{ 76278527Sassar if(id->next_entry == NULL) { 763233294Sstas krb5_set_error_message(context, HEIM_ERR_OPNOTSUPP, 764233294Sstas N_("next_entry is not supported in the %s " 765233294Sstas " keytab", ""), 766233294Sstas id->prefix); 76755682Smarkm return HEIM_ERR_OPNOTSUPP; 76878527Sassar } 76955682Smarkm return (*id->next_entry)(context, id, entry, cursor); 77055682Smarkm} 77155682Smarkm 772233294Sstas/** 77355682Smarkm * Release all resources associated with `cursor'. 774233294Sstas * 775233294Sstas * @param context a Keberos context. 776233294Sstas * @param id a keytab. 777233294Sstas * @param cursor the cursor to free. 778233294Sstas * 779233294Sstas * @return Return an error code or 0, see krb5_get_error_message(). 780233294Sstas * 781233294Sstas * @ingroup krb5_keytab 78255682Smarkm */ 78355682Smarkm 784233294SstasKRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 78555682Smarkmkrb5_kt_end_seq_get(krb5_context context, 78655682Smarkm krb5_keytab id, 78755682Smarkm krb5_kt_cursor *cursor) 78855682Smarkm{ 78978527Sassar if(id->end_seq_get == NULL) { 790233294Sstas krb5_set_error_message(context, HEIM_ERR_OPNOTSUPP, 791233294Sstas "end_seq_get is not supported in the %s " 792233294Sstas " keytab", id->prefix); 79355682Smarkm return HEIM_ERR_OPNOTSUPP; 79478527Sassar } 79555682Smarkm return (*id->end_seq_get)(context, id, cursor); 79655682Smarkm} 79755682Smarkm 798233294Sstas/** 79955682Smarkm * Add the entry in `entry' to the keytab `id'. 800233294Sstas * 801233294Sstas * @param context a Keberos context. 802233294Sstas * @param id a keytab. 803233294Sstas * @param entry the entry to add 804233294Sstas * 805233294Sstas * @return Return an error code or 0, see krb5_get_error_message(). 806233294Sstas * 807233294Sstas * @ingroup krb5_keytab 80855682Smarkm */ 80955682Smarkm 810233294SstasKRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 81155682Smarkmkrb5_kt_add_entry(krb5_context context, 81255682Smarkm krb5_keytab id, 81355682Smarkm krb5_keytab_entry *entry) 81455682Smarkm{ 81578527Sassar if(id->add == NULL) { 816233294Sstas krb5_set_error_message(context, KRB5_KT_NOWRITE, 817233294Sstas N_("Add is not supported in the %s keytab", ""), 818233294Sstas id->prefix); 81955682Smarkm return KRB5_KT_NOWRITE; 82078527Sassar } 82157416Smarkm entry->timestamp = time(NULL); 82255682Smarkm return (*id->add)(context, id,entry); 82355682Smarkm} 82455682Smarkm 825233294Sstas/** 826233294Sstas * Remove an entry from the keytab, matching is done using 827233294Sstas * krb5_kt_compare(). 828233294Sstas 829233294Sstas * @param context a Keberos context. 830233294Sstas * @param id a keytab. 831233294Sstas * @param entry the entry to remove 832233294Sstas * 833233294Sstas * @return Return an error code or 0, see krb5_get_error_message(). 834233294Sstas * 835233294Sstas * @ingroup krb5_keytab 83655682Smarkm */ 83755682Smarkm 838233294SstasKRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 83955682Smarkmkrb5_kt_remove_entry(krb5_context context, 84055682Smarkm krb5_keytab id, 84155682Smarkm krb5_keytab_entry *entry) 84255682Smarkm{ 84378527Sassar if(id->remove == NULL) { 844233294Sstas krb5_set_error_message(context, KRB5_KT_NOWRITE, 845233294Sstas N_("Remove is not supported in the %s keytab", ""), 846233294Sstas id->prefix); 84755682Smarkm return KRB5_KT_NOWRITE; 84878527Sassar } 84955682Smarkm return (*id->remove)(context, id, entry); 85055682Smarkm} 851233294Sstas 852233294Sstas/** 853233294Sstas * Return true if the keytab exists and have entries 854233294Sstas * 855233294Sstas * @param context a Keberos context. 856233294Sstas * @param id a keytab. 857233294Sstas * 858233294Sstas * @return Return an error code or 0, see krb5_get_error_message(). 859233294Sstas * 860233294Sstas * @ingroup krb5_keytab 861233294Sstas */ 862233294Sstas 863233294SstasKRB5_LIB_FUNCTION krb5_boolean KRB5_LIB_CALL 864233294Sstaskrb5_kt_have_content(krb5_context context, 865233294Sstas krb5_keytab id) 866233294Sstas{ 867233294Sstas krb5_keytab_entry entry; 868233294Sstas krb5_kt_cursor cursor; 869233294Sstas krb5_error_code ret; 870233294Sstas char *name; 871233294Sstas 872233294Sstas ret = krb5_kt_start_seq_get(context, id, &cursor); 873233294Sstas if (ret) 874233294Sstas goto notfound; 875233294Sstas 876233294Sstas ret = krb5_kt_next_entry(context, id, &entry, &cursor); 877233294Sstas krb5_kt_end_seq_get(context, id, &cursor); 878233294Sstas if (ret) 879233294Sstas goto notfound; 880233294Sstas 881233294Sstas krb5_kt_free_entry(context, &entry); 882233294Sstas 883233294Sstas return 0; 884233294Sstas 885233294Sstas notfound: 886233294Sstas ret = krb5_kt_get_full_name(context, id, &name); 887233294Sstas if (ret == 0) { 888233294Sstas krb5_set_error_message(context, KRB5_KT_NOTFOUND, 889233294Sstas N_("No entry in keytab: %s", ""), name); 890233294Sstas free(name); 891233294Sstas } 892233294Sstas return KRB5_KT_NOTFOUND; 893233294Sstas} 894