keytab.c revision 120945
155682Smarkm/* 2120945Snectar * Copyright (c) 1997 - 2003 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 36120945SnectarRCSID("$Id: keytab.c,v 1.55 2003/03/27 03:45:01 lha 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 49120945Snectar if (strlen(ops->prefix) > KRB5_KT_PREFIX_MAX_LEN - 1) { 50120945Snectar krb5_set_error_string(context, "krb5_kt_register; prefix too long"); 51120945Snectar return KRB5_KT_NAME_TOOLONG; 52120945Snectar } 53120945Snectar 5455682Smarkm tmp = realloc(context->kt_types, 5555682Smarkm (context->num_kt_types + 1) * sizeof(*context->kt_types)); 5678527Sassar if(tmp == NULL) { 5778527Sassar krb5_set_error_string(context, "malloc: out of memory"); 5855682Smarkm return ENOMEM; 5978527Sassar } 6055682Smarkm memcpy(&tmp[context->num_kt_types], ops, 6155682Smarkm sizeof(tmp[context->num_kt_types])); 6255682Smarkm context->kt_types = tmp; 6355682Smarkm context->num_kt_types++; 6455682Smarkm return 0; 6555682Smarkm} 6655682Smarkm 6755682Smarkm/* 6855682Smarkm * Resolve the keytab name (of the form `type:residual') in `name' 6955682Smarkm * into a keytab in `id'. 7055682Smarkm * Return 0 or an error 7155682Smarkm */ 7255682Smarkm 7355682Smarkmkrb5_error_code 7455682Smarkmkrb5_kt_resolve(krb5_context context, 7555682Smarkm const char *name, 7655682Smarkm krb5_keytab *id) 7755682Smarkm{ 7855682Smarkm krb5_keytab k; 7955682Smarkm int i; 8055682Smarkm const char *type, *residual; 8155682Smarkm size_t type_len; 8255682Smarkm krb5_error_code ret; 8355682Smarkm 8455682Smarkm residual = strchr(name, ':'); 8555682Smarkm if(residual == NULL) { 8655682Smarkm type = "FILE"; 8755682Smarkm type_len = strlen(type); 8855682Smarkm residual = name; 8955682Smarkm } else { 9055682Smarkm type = name; 9155682Smarkm type_len = residual - name; 9255682Smarkm residual++; 9355682Smarkm } 9455682Smarkm 9555682Smarkm for(i = 0; i < context->num_kt_types; i++) { 9690926Snectar if(strncasecmp(type, context->kt_types[i].prefix, type_len) == 0) 9755682Smarkm break; 9855682Smarkm } 9978527Sassar if(i == context->num_kt_types) { 10078527Sassar krb5_set_error_string(context, "unknown keytab type %.*s", 10178527Sassar (int)type_len, type); 10255682Smarkm return KRB5_KT_UNKNOWN_TYPE; 10378527Sassar } 10455682Smarkm 10555682Smarkm k = malloc (sizeof(*k)); 10678527Sassar if (k == NULL) { 10778527Sassar krb5_set_error_string(context, "malloc: out of memory"); 10855682Smarkm return ENOMEM; 10978527Sassar } 11055682Smarkm memcpy(k, &context->kt_types[i], sizeof(*k)); 11155682Smarkm k->data = NULL; 11255682Smarkm ret = (*k->resolve)(context, residual, k); 11355682Smarkm if(ret) { 11455682Smarkm free(k); 11555682Smarkm k = NULL; 11655682Smarkm } 11755682Smarkm *id = k; 11855682Smarkm return ret; 11955682Smarkm} 12055682Smarkm 12155682Smarkm/* 12255682Smarkm * copy the name of the default keytab into `name'. 12355682Smarkm * Return 0 or KRB5_CONFIG_NOTENUFSPACE if `namesize' is too short. 12455682Smarkm */ 12555682Smarkm 12655682Smarkmkrb5_error_code 12755682Smarkmkrb5_kt_default_name(krb5_context context, char *name, size_t namesize) 12855682Smarkm{ 12978527Sassar if (strlcpy (name, context->default_keytab, namesize) >= namesize) { 13078527Sassar krb5_clear_error_string (context); 13155682Smarkm return KRB5_CONFIG_NOTENUFSPACE; 13278527Sassar } 13355682Smarkm return 0; 13455682Smarkm} 13555682Smarkm 13655682Smarkm/* 13778527Sassar * copy the name of the default modify keytab into `name'. 13878527Sassar * Return 0 or KRB5_CONFIG_NOTENUFSPACE if `namesize' is too short. 13978527Sassar */ 14078527Sassar 14178527Sassarkrb5_error_code 14278527Sassarkrb5_kt_default_modify_name(krb5_context context, char *name, size_t namesize) 14378527Sassar{ 14490926Snectar const char *kt = NULL; 14590926Snectar if(context->default_keytab_modify == NULL) { 14690926Snectar if(strncasecmp(context->default_keytab, "ANY:", 4) != 0) 14790926Snectar kt = context->default_keytab; 14890926Snectar else { 14990926Snectar size_t len = strcspn(context->default_keytab + 4, ","); 15090926Snectar if(len >= namesize) { 15190926Snectar krb5_clear_error_string(context); 15290926Snectar return KRB5_CONFIG_NOTENUFSPACE; 15390926Snectar } 15490926Snectar strlcpy(name, context->default_keytab + 4, namesize); 15590926Snectar name[len] = '\0'; 15690926Snectar return 0; 15790926Snectar } 15890926Snectar } else 15990926Snectar kt = context->default_keytab_modify; 16090926Snectar if (strlcpy (name, kt, namesize) >= namesize) { 16178527Sassar krb5_clear_error_string (context); 16278527Sassar return KRB5_CONFIG_NOTENUFSPACE; 16378527Sassar } 16478527Sassar return 0; 16578527Sassar} 16678527Sassar 16778527Sassar/* 16855682Smarkm * Set `id' to the default keytab. 16955682Smarkm * Return 0 or an error. 17055682Smarkm */ 17155682Smarkm 17255682Smarkmkrb5_error_code 17355682Smarkmkrb5_kt_default(krb5_context context, krb5_keytab *id) 17455682Smarkm{ 17555682Smarkm return krb5_kt_resolve (context, context->default_keytab, id); 17655682Smarkm} 17755682Smarkm 17855682Smarkm/* 17955682Smarkm * Read the key identified by `(principal, vno, enctype)' from the 18055682Smarkm * keytab in `keyprocarg' (the default if == NULL) into `*key'. 18155682Smarkm * Return 0 or an error. 18255682Smarkm */ 18355682Smarkm 18455682Smarkmkrb5_error_code 18555682Smarkmkrb5_kt_read_service_key(krb5_context context, 18655682Smarkm krb5_pointer keyprocarg, 18755682Smarkm krb5_principal principal, 18855682Smarkm krb5_kvno vno, 18955682Smarkm krb5_enctype enctype, 19055682Smarkm krb5_keyblock **key) 19155682Smarkm{ 19255682Smarkm krb5_keytab keytab; 19355682Smarkm krb5_keytab_entry entry; 19455682Smarkm krb5_error_code ret; 19555682Smarkm 19655682Smarkm if (keyprocarg) 19755682Smarkm ret = krb5_kt_resolve (context, keyprocarg, &keytab); 19855682Smarkm else 19955682Smarkm ret = krb5_kt_default (context, &keytab); 20055682Smarkm 20155682Smarkm if (ret) 20255682Smarkm return ret; 20355682Smarkm 20455682Smarkm ret = krb5_kt_get_entry (context, keytab, principal, vno, enctype, &entry); 20555682Smarkm krb5_kt_close (context, keytab); 20655682Smarkm if (ret) 20755682Smarkm return ret; 20855682Smarkm ret = krb5_copy_keyblock (context, &entry.keyblock, key); 20955682Smarkm krb5_kt_free_entry(context, &entry); 21055682Smarkm return ret; 21155682Smarkm} 21255682Smarkm 21355682Smarkm/* 214120945Snectar * Return the type of the `keytab' in the string `prefix of length 215120945Snectar * `prefixsize'. 216120945Snectar */ 217120945Snectar 218120945Snectarkrb5_error_code 219120945Snectarkrb5_kt_get_type(krb5_context context, 220120945Snectar krb5_keytab keytab, 221120945Snectar char *prefix, 222120945Snectar size_t prefixsize) 223120945Snectar{ 224120945Snectar strlcpy(prefix, keytab->prefix, prefixsize); 225120945Snectar return 0; 226120945Snectar} 227120945Snectar 228120945Snectar/* 22955682Smarkm * Retrieve the name of the keytab `keytab' into `name', `namesize' 23055682Smarkm * Return 0 or an error. 23155682Smarkm */ 23255682Smarkm 23355682Smarkmkrb5_error_code 23455682Smarkmkrb5_kt_get_name(krb5_context context, 23555682Smarkm krb5_keytab keytab, 23655682Smarkm char *name, 23755682Smarkm size_t namesize) 23855682Smarkm{ 23955682Smarkm return (*keytab->get_name)(context, keytab, name, namesize); 24055682Smarkm} 24155682Smarkm 24255682Smarkm/* 24355682Smarkm * Finish using the keytab in `id'. All resources will be released. 24455682Smarkm * Return 0 or an error. 24555682Smarkm */ 24655682Smarkm 24755682Smarkmkrb5_error_code 24855682Smarkmkrb5_kt_close(krb5_context context, 24955682Smarkm krb5_keytab id) 25055682Smarkm{ 25155682Smarkm krb5_error_code ret; 25255682Smarkm 25355682Smarkm ret = (*id->close)(context, id); 25455682Smarkm if(ret == 0) 25555682Smarkm free(id); 25655682Smarkm return ret; 25755682Smarkm} 25855682Smarkm 25955682Smarkm/* 26055682Smarkm * Compare `entry' against `principal, vno, enctype'. 26155682Smarkm * Any of `principal, vno, enctype' might be 0 which acts as a wildcard. 26255682Smarkm * Return TRUE if they compare the same, FALSE otherwise. 26355682Smarkm */ 26455682Smarkm 26555682Smarkmkrb5_boolean 26655682Smarkmkrb5_kt_compare(krb5_context context, 26755682Smarkm krb5_keytab_entry *entry, 26855682Smarkm krb5_const_principal principal, 26955682Smarkm krb5_kvno vno, 27055682Smarkm krb5_enctype enctype) 27155682Smarkm{ 27255682Smarkm if(principal != NULL && 27355682Smarkm !krb5_principal_compare(context, entry->principal, principal)) 27455682Smarkm return FALSE; 27555682Smarkm if(vno && vno != entry->vno) 27655682Smarkm return FALSE; 27755682Smarkm if(enctype && enctype != entry->keyblock.keytype) 27855682Smarkm return FALSE; 27955682Smarkm return TRUE; 28055682Smarkm} 28155682Smarkm 28255682Smarkm/* 28355682Smarkm * Retrieve the keytab entry for `principal, kvno, enctype' into `entry' 28455682Smarkm * from the keytab `id'. 285102644Snectar * kvno == 0 is a wildcard and gives the keytab with the highest vno. 28655682Smarkm * Return 0 or an error. 28755682Smarkm */ 28855682Smarkm 28955682Smarkmkrb5_error_code 29055682Smarkmkrb5_kt_get_entry(krb5_context context, 29155682Smarkm krb5_keytab id, 29255682Smarkm krb5_const_principal principal, 29355682Smarkm krb5_kvno kvno, 29455682Smarkm krb5_enctype enctype, 29555682Smarkm krb5_keytab_entry *entry) 29655682Smarkm{ 29755682Smarkm krb5_keytab_entry tmp; 29855682Smarkm krb5_error_code ret; 29955682Smarkm krb5_kt_cursor cursor; 30055682Smarkm 30155682Smarkm if(id->get) 30255682Smarkm return (*id->get)(context, id, principal, kvno, enctype, entry); 30355682Smarkm 30455682Smarkm ret = krb5_kt_start_seq_get (context, id, &cursor); 30555682Smarkm if (ret) 30655682Smarkm return KRB5_KT_NOTFOUND; /* XXX i.e. file not found */ 30755682Smarkm 30855682Smarkm entry->vno = 0; 30955682Smarkm while (krb5_kt_next_entry(context, id, &tmp, &cursor) == 0) { 31055682Smarkm if (krb5_kt_compare(context, &tmp, principal, 0, enctype)) { 311102644Snectar /* the file keytab might only store the lower 8 bits of 312102644Snectar the kvno, so only compare those bits */ 313102644Snectar if (kvno == tmp.vno 314102644Snectar || (tmp.vno < 256 && kvno % 256 == tmp.vno)) { 31555682Smarkm krb5_kt_copy_entry_contents (context, &tmp, entry); 31655682Smarkm krb5_kt_free_entry (context, &tmp); 31755682Smarkm krb5_kt_end_seq_get(context, id, &cursor); 31855682Smarkm return 0; 31955682Smarkm } else if (kvno == 0 && tmp.vno > entry->vno) { 32055682Smarkm if (entry->vno) 32155682Smarkm krb5_kt_free_entry (context, entry); 32255682Smarkm krb5_kt_copy_entry_contents (context, &tmp, entry); 32355682Smarkm } 32455682Smarkm } 32555682Smarkm krb5_kt_free_entry(context, &tmp); 32655682Smarkm } 32755682Smarkm krb5_kt_end_seq_get (context, id, &cursor); 32878527Sassar if (entry->vno) { 32955682Smarkm return 0; 33078527Sassar } else { 331120945Snectar char princ[256], kt_name[256], kvno_str[25]; 33278527Sassar 33378527Sassar krb5_unparse_name_fixed (context, principal, princ, sizeof(princ)); 33478527Sassar krb5_kt_get_name (context, id, kt_name, sizeof(kt_name)); 33578527Sassar 336120945Snectar if (kvno) 337120945Snectar snprintf(kvno_str, sizeof(kvno_str), "(kvno %d)", kvno); 338120945Snectar else 339120945Snectar kvno_str[0] = '\0'; 340120945Snectar 34178527Sassar krb5_set_error_string (context, 342120945Snectar "failed to find %s%s in keytab %s", 343102644Snectar princ, 344120945Snectar kvno_str, 345102644Snectar kt_name); 34655682Smarkm return KRB5_KT_NOTFOUND; 34778527Sassar } 34855682Smarkm} 34955682Smarkm 35055682Smarkm/* 35155682Smarkm * Copy the contents of `in' into `out'. 352102644Snectar * Return 0 or an error. */ 35355682Smarkm 35455682Smarkmkrb5_error_code 35555682Smarkmkrb5_kt_copy_entry_contents(krb5_context context, 35655682Smarkm const krb5_keytab_entry *in, 35755682Smarkm krb5_keytab_entry *out) 35855682Smarkm{ 35955682Smarkm krb5_error_code ret; 36055682Smarkm 36155682Smarkm memset(out, 0, sizeof(*out)); 36255682Smarkm out->vno = in->vno; 36355682Smarkm 36455682Smarkm ret = krb5_copy_principal (context, in->principal, &out->principal); 36555682Smarkm if (ret) 36655682Smarkm goto fail; 36755682Smarkm ret = krb5_copy_keyblock_contents (context, 36855682Smarkm &in->keyblock, 36955682Smarkm &out->keyblock); 37055682Smarkm if (ret) 37155682Smarkm goto fail; 37255682Smarkm out->timestamp = in->timestamp; 37355682Smarkm return 0; 37455682Smarkmfail: 37555682Smarkm krb5_kt_free_entry (context, out); 37655682Smarkm return ret; 37755682Smarkm} 37855682Smarkm 37955682Smarkm/* 38055682Smarkm * Free the contents of `entry'. 38155682Smarkm */ 38255682Smarkm 38355682Smarkmkrb5_error_code 38455682Smarkmkrb5_kt_free_entry(krb5_context context, 38555682Smarkm krb5_keytab_entry *entry) 38655682Smarkm{ 38755682Smarkm krb5_free_principal (context, entry->principal); 38855682Smarkm krb5_free_keyblock_contents (context, &entry->keyblock); 38955682Smarkm return 0; 39055682Smarkm} 39155682Smarkm 39255682Smarkm#if 0 39355682Smarkmstatic int 39455682Smarkmxxxlock(int fd, int write) 39555682Smarkm{ 39655682Smarkm if(flock(fd, (write ? LOCK_EX : LOCK_SH) | LOCK_NB) < 0) { 39755682Smarkm sleep(1); 39855682Smarkm if(flock(fd, (write ? LOCK_EX : LOCK_SH) | LOCK_NB) < 0) 39955682Smarkm return -1; 40055682Smarkm } 40155682Smarkm return 0; 40255682Smarkm} 40355682Smarkm 40455682Smarkmstatic void 40555682Smarkmxxxunlock(int fd) 40655682Smarkm{ 40755682Smarkm flock(fd, LOCK_UN); 40855682Smarkm} 40955682Smarkm#endif 41055682Smarkm 41155682Smarkm/* 41255682Smarkm * Set `cursor' to point at the beginning of `id'. 41355682Smarkm * Return 0 or an error. 41455682Smarkm */ 41555682Smarkm 41655682Smarkmkrb5_error_code 41755682Smarkmkrb5_kt_start_seq_get(krb5_context context, 41855682Smarkm krb5_keytab id, 41955682Smarkm krb5_kt_cursor *cursor) 42055682Smarkm{ 42178527Sassar if(id->start_seq_get == NULL) { 42278527Sassar krb5_set_error_string(context, 42378527Sassar "start_seq_get is not supported in the %s " 42478527Sassar " keytab", id->prefix); 42555682Smarkm return HEIM_ERR_OPNOTSUPP; 42678527Sassar } 42755682Smarkm return (*id->start_seq_get)(context, id, cursor); 42855682Smarkm} 42955682Smarkm 43055682Smarkm/* 43155682Smarkm * Get the next entry from `id' pointed to by `cursor' and advance the 43255682Smarkm * `cursor'. 43355682Smarkm * Return 0 or an error. 43455682Smarkm */ 43555682Smarkm 43655682Smarkmkrb5_error_code 43755682Smarkmkrb5_kt_next_entry(krb5_context context, 43855682Smarkm krb5_keytab id, 43955682Smarkm krb5_keytab_entry *entry, 44055682Smarkm krb5_kt_cursor *cursor) 44155682Smarkm{ 44278527Sassar if(id->next_entry == NULL) { 44378527Sassar krb5_set_error_string(context, 44478527Sassar "next_entry is not supported in the %s " 44578527Sassar " keytab", id->prefix); 44655682Smarkm return HEIM_ERR_OPNOTSUPP; 44778527Sassar } 44855682Smarkm return (*id->next_entry)(context, id, entry, cursor); 44955682Smarkm} 45055682Smarkm 45155682Smarkm/* 45255682Smarkm * Release all resources associated with `cursor'. 45355682Smarkm */ 45455682Smarkm 45555682Smarkmkrb5_error_code 45655682Smarkmkrb5_kt_end_seq_get(krb5_context context, 45755682Smarkm krb5_keytab id, 45855682Smarkm krb5_kt_cursor *cursor) 45955682Smarkm{ 46078527Sassar if(id->end_seq_get == NULL) { 46178527Sassar krb5_set_error_string(context, 46278527Sassar "end_seq_get is not supported in the %s " 46378527Sassar " keytab", id->prefix); 46455682Smarkm return HEIM_ERR_OPNOTSUPP; 46578527Sassar } 46655682Smarkm return (*id->end_seq_get)(context, id, cursor); 46755682Smarkm} 46855682Smarkm 46955682Smarkm/* 47055682Smarkm * Add the entry in `entry' to the keytab `id'. 47155682Smarkm * Return 0 or an error. 47255682Smarkm */ 47355682Smarkm 47455682Smarkmkrb5_error_code 47555682Smarkmkrb5_kt_add_entry(krb5_context context, 47655682Smarkm krb5_keytab id, 47755682Smarkm krb5_keytab_entry *entry) 47855682Smarkm{ 47978527Sassar if(id->add == NULL) { 48078527Sassar krb5_set_error_string(context, "Add is not supported in the %s keytab", 48178527Sassar id->prefix); 48255682Smarkm return KRB5_KT_NOWRITE; 48378527Sassar } 48457416Smarkm entry->timestamp = time(NULL); 48555682Smarkm return (*id->add)(context, id,entry); 48655682Smarkm} 48755682Smarkm 48855682Smarkm/* 48955682Smarkm * Remove the entry `entry' from the keytab `id'. 49055682Smarkm * Return 0 or an error. 49155682Smarkm */ 49255682Smarkm 49355682Smarkmkrb5_error_code 49455682Smarkmkrb5_kt_remove_entry(krb5_context context, 49555682Smarkm krb5_keytab id, 49655682Smarkm krb5_keytab_entry *entry) 49755682Smarkm{ 49878527Sassar if(id->remove == NULL) { 49978527Sassar krb5_set_error_string(context, 50078527Sassar "Remove is not supported in the %s keytab", 50178527Sassar id->prefix); 50255682Smarkm return KRB5_KT_NOWRITE; 50378527Sassar } 50455682Smarkm return (*id->remove)(context, id, entry); 50555682Smarkm} 506