keytab.c revision 102644
155682Smarkm/* 2102644Snectar * Copyright (c) 1997 - 2002 Kungliga Tekniska H�gskolan 355682Smarkm * (Royal Institute of Technology, Stockholm, Sweden). 455682Smarkm * All rights reserved. 555682Smarkm * 655682Smarkm * Redistribution and use in source and binary forms, with or without 755682Smarkm * modification, are permitted provided that the following conditions 855682Smarkm * are met: 955682Smarkm * 1055682Smarkm * 1. Redistributions of source code must retain the above copyright 1155682Smarkm * notice, this list of conditions and the following disclaimer. 1255682Smarkm * 1355682Smarkm * 2. Redistributions in binary form must reproduce the above copyright 1455682Smarkm * notice, this list of conditions and the following disclaimer in the 1555682Smarkm * documentation and/or other materials provided with the distribution. 1655682Smarkm * 1755682Smarkm * 3. Neither the name of the Institute nor the names of its contributors 1855682Smarkm * may be used to endorse or promote products derived from this software 1955682Smarkm * without specific prior written permission. 2055682Smarkm * 2155682Smarkm * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND 2255682Smarkm * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 2355682Smarkm * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2455682Smarkm * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE 2555682Smarkm * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2655682Smarkm * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2755682Smarkm * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2855682Smarkm * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2955682Smarkm * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 3055682Smarkm * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 3155682Smarkm * SUCH DAMAGE. 3255682Smarkm */ 3355682Smarkm 3455682Smarkm#include "krb5_locl.h" 3555682Smarkm 36102644SnectarRCSID("$Id: keytab.c,v 1.53 2002/03/10 23:14:12 assar Exp $"); 3755682Smarkm 3855682Smarkm/* 3955682Smarkm * Register a new keytab in `ops' 4055682Smarkm * Return 0 or an error. 4155682Smarkm */ 4255682Smarkm 4355682Smarkmkrb5_error_code 4455682Smarkmkrb5_kt_register(krb5_context context, 4555682Smarkm const krb5_kt_ops *ops) 4655682Smarkm{ 4755682Smarkm struct krb5_keytab_data *tmp; 4855682Smarkm 4955682Smarkm tmp = realloc(context->kt_types, 5055682Smarkm (context->num_kt_types + 1) * sizeof(*context->kt_types)); 5178527Sassar if(tmp == NULL) { 5278527Sassar krb5_set_error_string(context, "malloc: out of memory"); 5355682Smarkm return ENOMEM; 5478527Sassar } 5555682Smarkm memcpy(&tmp[context->num_kt_types], ops, 5655682Smarkm sizeof(tmp[context->num_kt_types])); 5755682Smarkm context->kt_types = tmp; 5855682Smarkm context->num_kt_types++; 5955682Smarkm return 0; 6055682Smarkm} 6155682Smarkm 6255682Smarkm/* 6355682Smarkm * Resolve the keytab name (of the form `type:residual') in `name' 6455682Smarkm * into a keytab in `id'. 6555682Smarkm * Return 0 or an error 6655682Smarkm */ 6755682Smarkm 6855682Smarkmkrb5_error_code 6955682Smarkmkrb5_kt_resolve(krb5_context context, 7055682Smarkm const char *name, 7155682Smarkm krb5_keytab *id) 7255682Smarkm{ 7355682Smarkm krb5_keytab k; 7455682Smarkm int i; 7555682Smarkm const char *type, *residual; 7655682Smarkm size_t type_len; 7755682Smarkm krb5_error_code ret; 7855682Smarkm 7955682Smarkm residual = strchr(name, ':'); 8055682Smarkm if(residual == NULL) { 8155682Smarkm type = "FILE"; 8255682Smarkm type_len = strlen(type); 8355682Smarkm residual = name; 8455682Smarkm } else { 8555682Smarkm type = name; 8655682Smarkm type_len = residual - name; 8755682Smarkm residual++; 8855682Smarkm } 8955682Smarkm 9055682Smarkm for(i = 0; i < context->num_kt_types; i++) { 9190926Snectar if(strncasecmp(type, context->kt_types[i].prefix, type_len) == 0) 9255682Smarkm break; 9355682Smarkm } 9478527Sassar if(i == context->num_kt_types) { 9578527Sassar krb5_set_error_string(context, "unknown keytab type %.*s", 9678527Sassar (int)type_len, type); 9755682Smarkm return KRB5_KT_UNKNOWN_TYPE; 9878527Sassar } 9955682Smarkm 10055682Smarkm k = malloc (sizeof(*k)); 10178527Sassar if (k == NULL) { 10278527Sassar krb5_set_error_string(context, "malloc: out of memory"); 10355682Smarkm return ENOMEM; 10478527Sassar } 10555682Smarkm memcpy(k, &context->kt_types[i], sizeof(*k)); 10655682Smarkm k->data = NULL; 10755682Smarkm ret = (*k->resolve)(context, residual, k); 10855682Smarkm if(ret) { 10955682Smarkm free(k); 11055682Smarkm k = NULL; 11155682Smarkm } 11255682Smarkm *id = k; 11355682Smarkm return ret; 11455682Smarkm} 11555682Smarkm 11655682Smarkm/* 11755682Smarkm * copy the name of the default keytab into `name'. 11855682Smarkm * Return 0 or KRB5_CONFIG_NOTENUFSPACE if `namesize' is too short. 11955682Smarkm */ 12055682Smarkm 12155682Smarkmkrb5_error_code 12255682Smarkmkrb5_kt_default_name(krb5_context context, char *name, size_t namesize) 12355682Smarkm{ 12478527Sassar if (strlcpy (name, context->default_keytab, namesize) >= namesize) { 12578527Sassar krb5_clear_error_string (context); 12655682Smarkm return KRB5_CONFIG_NOTENUFSPACE; 12778527Sassar } 12855682Smarkm return 0; 12955682Smarkm} 13055682Smarkm 13155682Smarkm/* 13278527Sassar * copy the name of the default modify keytab into `name'. 13378527Sassar * Return 0 or KRB5_CONFIG_NOTENUFSPACE if `namesize' is too short. 13478527Sassar */ 13578527Sassar 13678527Sassarkrb5_error_code 13778527Sassarkrb5_kt_default_modify_name(krb5_context context, char *name, size_t namesize) 13878527Sassar{ 13990926Snectar const char *kt = NULL; 14090926Snectar if(context->default_keytab_modify == NULL) { 14190926Snectar if(strncasecmp(context->default_keytab, "ANY:", 4) != 0) 14290926Snectar kt = context->default_keytab; 14390926Snectar else { 14490926Snectar size_t len = strcspn(context->default_keytab + 4, ","); 14590926Snectar if(len >= namesize) { 14690926Snectar krb5_clear_error_string(context); 14790926Snectar return KRB5_CONFIG_NOTENUFSPACE; 14890926Snectar } 14990926Snectar strlcpy(name, context->default_keytab + 4, namesize); 15090926Snectar name[len] = '\0'; 15190926Snectar return 0; 15290926Snectar } 15390926Snectar } else 15490926Snectar kt = context->default_keytab_modify; 15590926Snectar if (strlcpy (name, kt, namesize) >= namesize) { 15678527Sassar krb5_clear_error_string (context); 15778527Sassar return KRB5_CONFIG_NOTENUFSPACE; 15878527Sassar } 15978527Sassar return 0; 16078527Sassar} 16178527Sassar 16278527Sassar/* 16355682Smarkm * Set `id' to the default keytab. 16455682Smarkm * Return 0 or an error. 16555682Smarkm */ 16655682Smarkm 16755682Smarkmkrb5_error_code 16855682Smarkmkrb5_kt_default(krb5_context context, krb5_keytab *id) 16955682Smarkm{ 17055682Smarkm return krb5_kt_resolve (context, context->default_keytab, id); 17155682Smarkm} 17255682Smarkm 17355682Smarkm/* 17455682Smarkm * Read the key identified by `(principal, vno, enctype)' from the 17555682Smarkm * keytab in `keyprocarg' (the default if == NULL) into `*key'. 17655682Smarkm * Return 0 or an error. 17755682Smarkm */ 17855682Smarkm 17955682Smarkmkrb5_error_code 18055682Smarkmkrb5_kt_read_service_key(krb5_context context, 18155682Smarkm krb5_pointer keyprocarg, 18255682Smarkm krb5_principal principal, 18355682Smarkm krb5_kvno vno, 18455682Smarkm krb5_enctype enctype, 18555682Smarkm krb5_keyblock **key) 18655682Smarkm{ 18755682Smarkm krb5_keytab keytab; 18855682Smarkm krb5_keytab_entry entry; 18955682Smarkm krb5_error_code ret; 19055682Smarkm 19155682Smarkm if (keyprocarg) 19255682Smarkm ret = krb5_kt_resolve (context, keyprocarg, &keytab); 19355682Smarkm else 19455682Smarkm ret = krb5_kt_default (context, &keytab); 19555682Smarkm 19655682Smarkm if (ret) 19755682Smarkm return ret; 19855682Smarkm 19955682Smarkm ret = krb5_kt_get_entry (context, keytab, principal, vno, enctype, &entry); 20055682Smarkm krb5_kt_close (context, keytab); 20155682Smarkm if (ret) 20255682Smarkm return ret; 20355682Smarkm ret = krb5_copy_keyblock (context, &entry.keyblock, key); 20455682Smarkm krb5_kt_free_entry(context, &entry); 20555682Smarkm return ret; 20655682Smarkm} 20755682Smarkm 20855682Smarkm/* 20955682Smarkm * Retrieve the name of the keytab `keytab' into `name', `namesize' 21055682Smarkm * Return 0 or an error. 21155682Smarkm */ 21255682Smarkm 21355682Smarkmkrb5_error_code 21455682Smarkmkrb5_kt_get_name(krb5_context context, 21555682Smarkm krb5_keytab keytab, 21655682Smarkm char *name, 21755682Smarkm size_t namesize) 21855682Smarkm{ 21955682Smarkm return (*keytab->get_name)(context, keytab, name, namesize); 22055682Smarkm} 22155682Smarkm 22255682Smarkm/* 22355682Smarkm * Finish using the keytab in `id'. All resources will be released. 22455682Smarkm * Return 0 or an error. 22555682Smarkm */ 22655682Smarkm 22755682Smarkmkrb5_error_code 22855682Smarkmkrb5_kt_close(krb5_context context, 22955682Smarkm krb5_keytab id) 23055682Smarkm{ 23155682Smarkm krb5_error_code ret; 23255682Smarkm 23355682Smarkm ret = (*id->close)(context, id); 23455682Smarkm if(ret == 0) 23555682Smarkm free(id); 23655682Smarkm return ret; 23755682Smarkm} 23855682Smarkm 23955682Smarkm/* 24055682Smarkm * Compare `entry' against `principal, vno, enctype'. 24155682Smarkm * Any of `principal, vno, enctype' might be 0 which acts as a wildcard. 24255682Smarkm * Return TRUE if they compare the same, FALSE otherwise. 24355682Smarkm */ 24455682Smarkm 24555682Smarkmkrb5_boolean 24655682Smarkmkrb5_kt_compare(krb5_context context, 24755682Smarkm krb5_keytab_entry *entry, 24855682Smarkm krb5_const_principal principal, 24955682Smarkm krb5_kvno vno, 25055682Smarkm krb5_enctype enctype) 25155682Smarkm{ 25255682Smarkm if(principal != NULL && 25355682Smarkm !krb5_principal_compare(context, entry->principal, principal)) 25455682Smarkm return FALSE; 25555682Smarkm if(vno && vno != entry->vno) 25655682Smarkm return FALSE; 25755682Smarkm if(enctype && enctype != entry->keyblock.keytype) 25855682Smarkm return FALSE; 25955682Smarkm return TRUE; 26055682Smarkm} 26155682Smarkm 26255682Smarkm/* 26355682Smarkm * Retrieve the keytab entry for `principal, kvno, enctype' into `entry' 26455682Smarkm * from the keytab `id'. 265102644Snectar * kvno == 0 is a wildcard and gives the keytab with the highest vno. 26655682Smarkm * Return 0 or an error. 26755682Smarkm */ 26855682Smarkm 26955682Smarkmkrb5_error_code 27055682Smarkmkrb5_kt_get_entry(krb5_context context, 27155682Smarkm krb5_keytab id, 27255682Smarkm krb5_const_principal principal, 27355682Smarkm krb5_kvno kvno, 27455682Smarkm krb5_enctype enctype, 27555682Smarkm krb5_keytab_entry *entry) 27655682Smarkm{ 27755682Smarkm krb5_keytab_entry tmp; 27855682Smarkm krb5_error_code ret; 27955682Smarkm krb5_kt_cursor cursor; 28055682Smarkm 28155682Smarkm if(id->get) 28255682Smarkm return (*id->get)(context, id, principal, kvno, enctype, entry); 28355682Smarkm 28455682Smarkm ret = krb5_kt_start_seq_get (context, id, &cursor); 28555682Smarkm if (ret) 28655682Smarkm return KRB5_KT_NOTFOUND; /* XXX i.e. file not found */ 28755682Smarkm 28855682Smarkm entry->vno = 0; 28955682Smarkm while (krb5_kt_next_entry(context, id, &tmp, &cursor) == 0) { 29055682Smarkm if (krb5_kt_compare(context, &tmp, principal, 0, enctype)) { 291102644Snectar /* the file keytab might only store the lower 8 bits of 292102644Snectar the kvno, so only compare those bits */ 293102644Snectar if (kvno == tmp.vno 294102644Snectar || (tmp.vno < 256 && kvno % 256 == tmp.vno)) { 29555682Smarkm krb5_kt_copy_entry_contents (context, &tmp, entry); 29655682Smarkm krb5_kt_free_entry (context, &tmp); 29755682Smarkm krb5_kt_end_seq_get(context, id, &cursor); 29855682Smarkm return 0; 29955682Smarkm } else if (kvno == 0 && tmp.vno > entry->vno) { 30055682Smarkm if (entry->vno) 30155682Smarkm krb5_kt_free_entry (context, entry); 30255682Smarkm krb5_kt_copy_entry_contents (context, &tmp, entry); 30355682Smarkm } 30455682Smarkm } 30555682Smarkm krb5_kt_free_entry(context, &tmp); 30655682Smarkm } 30755682Smarkm krb5_kt_end_seq_get (context, id, &cursor); 30878527Sassar if (entry->vno) { 30955682Smarkm return 0; 31078527Sassar } else { 31178527Sassar char princ[256], kt_name[256]; 31278527Sassar 31378527Sassar krb5_unparse_name_fixed (context, principal, princ, sizeof(princ)); 31478527Sassar krb5_kt_get_name (context, id, kt_name, sizeof(kt_name)); 31578527Sassar 31678527Sassar krb5_set_error_string (context, 317102644Snectar "failed to find %s%s%d%s in keytab %s", 318102644Snectar princ, 319102644Snectar kvno ? "(" : "", 320102644Snectar kvno, 321102644Snectar kvno ? ")" : "", 322102644Snectar kt_name); 32355682Smarkm return KRB5_KT_NOTFOUND; 32478527Sassar } 32555682Smarkm} 32655682Smarkm 32755682Smarkm/* 32855682Smarkm * Copy the contents of `in' into `out'. 329102644Snectar * Return 0 or an error. */ 33055682Smarkm 33155682Smarkmkrb5_error_code 33255682Smarkmkrb5_kt_copy_entry_contents(krb5_context context, 33355682Smarkm const krb5_keytab_entry *in, 33455682Smarkm krb5_keytab_entry *out) 33555682Smarkm{ 33655682Smarkm krb5_error_code ret; 33755682Smarkm 33855682Smarkm memset(out, 0, sizeof(*out)); 33955682Smarkm out->vno = in->vno; 34055682Smarkm 34155682Smarkm ret = krb5_copy_principal (context, in->principal, &out->principal); 34255682Smarkm if (ret) 34355682Smarkm goto fail; 34455682Smarkm ret = krb5_copy_keyblock_contents (context, 34555682Smarkm &in->keyblock, 34655682Smarkm &out->keyblock); 34755682Smarkm if (ret) 34855682Smarkm goto fail; 34955682Smarkm out->timestamp = in->timestamp; 35055682Smarkm return 0; 35155682Smarkmfail: 35255682Smarkm krb5_kt_free_entry (context, out); 35355682Smarkm return ret; 35455682Smarkm} 35555682Smarkm 35655682Smarkm/* 35755682Smarkm * Free the contents of `entry'. 35855682Smarkm */ 35955682Smarkm 36055682Smarkmkrb5_error_code 36155682Smarkmkrb5_kt_free_entry(krb5_context context, 36255682Smarkm krb5_keytab_entry *entry) 36355682Smarkm{ 36455682Smarkm krb5_free_principal (context, entry->principal); 36555682Smarkm krb5_free_keyblock_contents (context, &entry->keyblock); 36655682Smarkm return 0; 36755682Smarkm} 36855682Smarkm 36955682Smarkm#if 0 37055682Smarkmstatic int 37155682Smarkmxxxlock(int fd, int write) 37255682Smarkm{ 37355682Smarkm if(flock(fd, (write ? LOCK_EX : LOCK_SH) | LOCK_NB) < 0) { 37455682Smarkm sleep(1); 37555682Smarkm if(flock(fd, (write ? LOCK_EX : LOCK_SH) | LOCK_NB) < 0) 37655682Smarkm return -1; 37755682Smarkm } 37855682Smarkm return 0; 37955682Smarkm} 38055682Smarkm 38155682Smarkmstatic void 38255682Smarkmxxxunlock(int fd) 38355682Smarkm{ 38455682Smarkm flock(fd, LOCK_UN); 38555682Smarkm} 38655682Smarkm#endif 38755682Smarkm 38855682Smarkm/* 38955682Smarkm * Set `cursor' to point at the beginning of `id'. 39055682Smarkm * Return 0 or an error. 39155682Smarkm */ 39255682Smarkm 39355682Smarkmkrb5_error_code 39455682Smarkmkrb5_kt_start_seq_get(krb5_context context, 39555682Smarkm krb5_keytab id, 39655682Smarkm krb5_kt_cursor *cursor) 39755682Smarkm{ 39878527Sassar if(id->start_seq_get == NULL) { 39978527Sassar krb5_set_error_string(context, 40078527Sassar "start_seq_get is not supported in the %s " 40178527Sassar " keytab", id->prefix); 40255682Smarkm return HEIM_ERR_OPNOTSUPP; 40378527Sassar } 40455682Smarkm return (*id->start_seq_get)(context, id, cursor); 40555682Smarkm} 40655682Smarkm 40755682Smarkm/* 40855682Smarkm * Get the next entry from `id' pointed to by `cursor' and advance the 40955682Smarkm * `cursor'. 41055682Smarkm * Return 0 or an error. 41155682Smarkm */ 41255682Smarkm 41355682Smarkmkrb5_error_code 41455682Smarkmkrb5_kt_next_entry(krb5_context context, 41555682Smarkm krb5_keytab id, 41655682Smarkm krb5_keytab_entry *entry, 41755682Smarkm krb5_kt_cursor *cursor) 41855682Smarkm{ 41978527Sassar if(id->next_entry == NULL) { 42078527Sassar krb5_set_error_string(context, 42178527Sassar "next_entry is not supported in the %s " 42278527Sassar " keytab", id->prefix); 42355682Smarkm return HEIM_ERR_OPNOTSUPP; 42478527Sassar } 42555682Smarkm return (*id->next_entry)(context, id, entry, cursor); 42655682Smarkm} 42755682Smarkm 42855682Smarkm/* 42955682Smarkm * Release all resources associated with `cursor'. 43055682Smarkm */ 43155682Smarkm 43255682Smarkmkrb5_error_code 43355682Smarkmkrb5_kt_end_seq_get(krb5_context context, 43455682Smarkm krb5_keytab id, 43555682Smarkm krb5_kt_cursor *cursor) 43655682Smarkm{ 43778527Sassar if(id->end_seq_get == NULL) { 43878527Sassar krb5_set_error_string(context, 43978527Sassar "end_seq_get is not supported in the %s " 44078527Sassar " keytab", id->prefix); 44155682Smarkm return HEIM_ERR_OPNOTSUPP; 44278527Sassar } 44355682Smarkm return (*id->end_seq_get)(context, id, cursor); 44455682Smarkm} 44555682Smarkm 44655682Smarkm/* 44755682Smarkm * Add the entry in `entry' to the keytab `id'. 44855682Smarkm * Return 0 or an error. 44955682Smarkm */ 45055682Smarkm 45155682Smarkmkrb5_error_code 45255682Smarkmkrb5_kt_add_entry(krb5_context context, 45355682Smarkm krb5_keytab id, 45455682Smarkm krb5_keytab_entry *entry) 45555682Smarkm{ 45678527Sassar if(id->add == NULL) { 45778527Sassar krb5_set_error_string(context, "Add is not supported in the %s keytab", 45878527Sassar id->prefix); 45955682Smarkm return KRB5_KT_NOWRITE; 46078527Sassar } 46157416Smarkm entry->timestamp = time(NULL); 46255682Smarkm return (*id->add)(context, id,entry); 46355682Smarkm} 46455682Smarkm 46555682Smarkm/* 46655682Smarkm * Remove the entry `entry' from the keytab `id'. 46755682Smarkm * Return 0 or an error. 46855682Smarkm */ 46955682Smarkm 47055682Smarkmkrb5_error_code 47155682Smarkmkrb5_kt_remove_entry(krb5_context context, 47255682Smarkm krb5_keytab id, 47355682Smarkm krb5_keytab_entry *entry) 47455682Smarkm{ 47578527Sassar if(id->remove == NULL) { 47678527Sassar krb5_set_error_string(context, 47778527Sassar "Remove is not supported in the %s keytab", 47878527Sassar id->prefix); 47955682Smarkm return KRB5_KT_NOWRITE; 48078527Sassar } 48155682Smarkm return (*id->remove)(context, id, entry); 48255682Smarkm} 483