155682Smarkm/* 2178825Sdfr * Copyright (c) 1997 - 2007 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 36178825SdfrRCSID("$Id: keytab_keyfile.c 20695 2007-05-30 14:09:09Z lha $"); 3755682Smarkm 3855682Smarkm/* afs keyfile operations --------------------------------------- */ 3955682Smarkm 4055682Smarkm/* 4155682Smarkm * Minimum tools to handle the AFS KeyFile. 4255682Smarkm * 4355682Smarkm * Format of the KeyFile is: 4455682Smarkm * <int32_t numkeys> {[<int32_t kvno> <char[8] deskey>] * numkeys} 4555682Smarkm * 4655682Smarkm * It just adds to the end of the keyfile, deleting isn't implemented. 4755682Smarkm * Use your favorite text/hex editor to delete keys. 4855682Smarkm * 4955682Smarkm */ 5055682Smarkm 5155682Smarkm#define AFS_SERVERTHISCELL "/usr/afs/etc/ThisCell" 5255682Smarkm#define AFS_SERVERMAGICKRBCONF "/usr/afs/etc/krb.conf" 5355682Smarkm 5455682Smarkmstruct akf_data { 5555682Smarkm int num_entries; 5655682Smarkm char *filename; 5755682Smarkm char *cell; 5855682Smarkm char *realm; 5955682Smarkm}; 6055682Smarkm 6155682Smarkm/* 6255682Smarkm * set `d->cell' and `d->realm' 6355682Smarkm */ 6455682Smarkm 6555682Smarkmstatic int 66178825Sdfrget_cell_and_realm (krb5_context context, struct akf_data *d) 6755682Smarkm{ 6855682Smarkm FILE *f; 6955682Smarkm char buf[BUFSIZ], *cp; 7078527Sassar int ret; 7155682Smarkm 7255682Smarkm f = fopen (AFS_SERVERTHISCELL, "r"); 7378527Sassar if (f == NULL) { 7478527Sassar ret = errno; 7578527Sassar krb5_set_error_string (context, "open %s: %s", AFS_SERVERTHISCELL, 7678527Sassar strerror(ret)); 7778527Sassar return ret; 7878527Sassar } 7955682Smarkm if (fgets (buf, sizeof(buf), f) == NULL) { 8055682Smarkm fclose (f); 8178527Sassar krb5_set_error_string (context, "no cell in %s", AFS_SERVERTHISCELL); 8255682Smarkm return EINVAL; 8355682Smarkm } 84107207Snectar buf[strcspn(buf, "\n")] = '\0'; 8555682Smarkm fclose(f); 8655682Smarkm 8755682Smarkm d->cell = strdup (buf); 8878527Sassar if (d->cell == NULL) { 8978527Sassar krb5_set_error_string (context, "malloc: out of memory"); 9078527Sassar return ENOMEM; 9178527Sassar } 9255682Smarkm 9355682Smarkm f = fopen (AFS_SERVERMAGICKRBCONF, "r"); 9455682Smarkm if (f != NULL) { 9555682Smarkm if (fgets (buf, sizeof(buf), f) == NULL) { 96178825Sdfr free (d->cell); 97178825Sdfr d->cell = NULL; 9855682Smarkm fclose (f); 9978527Sassar krb5_set_error_string (context, "no realm in %s", 10078527Sassar AFS_SERVERMAGICKRBCONF); 10155682Smarkm return EINVAL; 10255682Smarkm } 103107207Snectar buf[strcspn(buf, "\n")] = '\0'; 10455682Smarkm fclose(f); 10555682Smarkm } 10655682Smarkm /* uppercase */ 10755682Smarkm for (cp = buf; *cp != '\0'; cp++) 108178825Sdfr *cp = toupper((unsigned char)*cp); 10955682Smarkm 11055682Smarkm d->realm = strdup (buf); 11155682Smarkm if (d->realm == NULL) { 11255682Smarkm free (d->cell); 113178825Sdfr d->cell = NULL; 11478527Sassar krb5_set_error_string (context, "malloc: out of memory"); 11578527Sassar return ENOMEM; 11655682Smarkm } 11755682Smarkm return 0; 11855682Smarkm} 11955682Smarkm 12055682Smarkm/* 12155682Smarkm * init and get filename 12255682Smarkm */ 12355682Smarkm 12455682Smarkmstatic krb5_error_code 12555682Smarkmakf_resolve(krb5_context context, const char *name, krb5_keytab id) 12655682Smarkm{ 12755682Smarkm int ret; 12855682Smarkm struct akf_data *d = malloc(sizeof (struct akf_data)); 12955682Smarkm 13078527Sassar if (d == NULL) { 13178527Sassar krb5_set_error_string (context, "malloc: out of memory"); 13278527Sassar return ENOMEM; 13378527Sassar } 13455682Smarkm 13555682Smarkm d->num_entries = 0; 13678527Sassar ret = get_cell_and_realm (context, d); 13755682Smarkm if (ret) { 13855682Smarkm free (d); 13955682Smarkm return ret; 14055682Smarkm } 14155682Smarkm d->filename = strdup (name); 14255682Smarkm if (d->filename == NULL) { 14355682Smarkm free (d->cell); 14455682Smarkm free (d->realm); 14555682Smarkm free (d); 14678527Sassar krb5_set_error_string (context, "malloc: out of memory"); 14755682Smarkm return ENOMEM; 14855682Smarkm } 14955682Smarkm id->data = d; 15055682Smarkm 15155682Smarkm return 0; 15255682Smarkm} 15355682Smarkm 15455682Smarkm/* 15555682Smarkm * cleanup 15655682Smarkm */ 15755682Smarkm 15855682Smarkmstatic krb5_error_code 15955682Smarkmakf_close(krb5_context context, krb5_keytab id) 16055682Smarkm{ 16155682Smarkm struct akf_data *d = id->data; 16255682Smarkm 16355682Smarkm free (d->filename); 16455682Smarkm free (d->cell); 16555682Smarkm free (d); 16655682Smarkm return 0; 16755682Smarkm} 16855682Smarkm 16955682Smarkm/* 17055682Smarkm * Return filename 17155682Smarkm */ 17255682Smarkm 17355682Smarkmstatic krb5_error_code 17455682Smarkmakf_get_name(krb5_context context, 17555682Smarkm krb5_keytab id, 17655682Smarkm char *name, 17755682Smarkm size_t name_sz) 17855682Smarkm{ 17955682Smarkm struct akf_data *d = id->data; 18055682Smarkm 18155682Smarkm strlcpy (name, d->filename, name_sz); 18255682Smarkm return 0; 18355682Smarkm} 18455682Smarkm 18555682Smarkm/* 18655682Smarkm * Init 18755682Smarkm */ 18855682Smarkm 18955682Smarkmstatic krb5_error_code 19055682Smarkmakf_start_seq_get(krb5_context context, 19155682Smarkm krb5_keytab id, 19255682Smarkm krb5_kt_cursor *c) 19355682Smarkm{ 19455682Smarkm int32_t ret; 19555682Smarkm struct akf_data *d = id->data; 19655682Smarkm 19755682Smarkm c->fd = open (d->filename, O_RDONLY|O_BINARY, 0600); 19878527Sassar if (c->fd < 0) { 19978527Sassar ret = errno; 20078527Sassar krb5_set_error_string(context, "open(%s): %s", d->filename, 20178527Sassar strerror(ret)); 20278527Sassar return ret; 20378527Sassar } 20455682Smarkm 20555682Smarkm c->sp = krb5_storage_from_fd(c->fd); 20655682Smarkm ret = krb5_ret_int32(c->sp, &d->num_entries); 20755682Smarkm if(ret) { 20855682Smarkm krb5_storage_free(c->sp); 20955682Smarkm close(c->fd); 21078527Sassar krb5_clear_error_string (context); 211102644Snectar if(ret == KRB5_KT_END) 21278527Sassar return KRB5_KT_NOTFOUND; 21355682Smarkm return ret; 21455682Smarkm } 21555682Smarkm 21655682Smarkm return 0; 21755682Smarkm} 21855682Smarkm 21955682Smarkmstatic krb5_error_code 22055682Smarkmakf_next_entry(krb5_context context, 22155682Smarkm krb5_keytab id, 22255682Smarkm krb5_keytab_entry *entry, 22355682Smarkm krb5_kt_cursor *cursor) 22455682Smarkm{ 22555682Smarkm struct akf_data *d = id->data; 22655682Smarkm int32_t kvno; 22755682Smarkm off_t pos; 22855682Smarkm int ret; 22955682Smarkm 230102644Snectar pos = krb5_storage_seek(cursor->sp, 0, SEEK_CUR); 23155682Smarkm 23255682Smarkm if ((pos - 4) / (4 + 8) >= d->num_entries) 23355682Smarkm return KRB5_KT_END; 23455682Smarkm 23555682Smarkm ret = krb5_make_principal (context, &entry->principal, 23655682Smarkm d->realm, "afs", d->cell, NULL); 23755682Smarkm if (ret) 23855682Smarkm goto out; 23955682Smarkm 24055682Smarkm ret = krb5_ret_int32(cursor->sp, &kvno); 24155682Smarkm if (ret) { 24255682Smarkm krb5_free_principal (context, entry->principal); 24355682Smarkm goto out; 24455682Smarkm } 24555682Smarkm 24672445Sassar entry->vno = kvno; 24755682Smarkm 24855682Smarkm entry->keyblock.keytype = ETYPE_DES_CBC_MD5; 24955682Smarkm entry->keyblock.keyvalue.length = 8; 25055682Smarkm entry->keyblock.keyvalue.data = malloc (8); 25155682Smarkm if (entry->keyblock.keyvalue.data == NULL) { 25255682Smarkm krb5_free_principal (context, entry->principal); 25378527Sassar krb5_set_error_string (context, "malloc: out of memory"); 25455682Smarkm ret = ENOMEM; 25555682Smarkm goto out; 25655682Smarkm } 25755682Smarkm 258102644Snectar ret = krb5_storage_read(cursor->sp, entry->keyblock.keyvalue.data, 8); 25955682Smarkm if(ret != 8) 26055682Smarkm ret = (ret < 0) ? errno : KRB5_KT_END; 26172445Sassar else 26272445Sassar ret = 0; 26355682Smarkm 26455682Smarkm entry->timestamp = time(NULL); 26555682Smarkm 26655682Smarkm out: 267102644Snectar krb5_storage_seek(cursor->sp, pos + 4 + 8, SEEK_SET); 26855682Smarkm return ret; 26955682Smarkm} 27055682Smarkm 27155682Smarkmstatic krb5_error_code 27255682Smarkmakf_end_seq_get(krb5_context context, 27355682Smarkm krb5_keytab id, 27455682Smarkm krb5_kt_cursor *cursor) 27555682Smarkm{ 27655682Smarkm krb5_storage_free(cursor->sp); 27755682Smarkm close(cursor->fd); 27855682Smarkm return 0; 27955682Smarkm} 28055682Smarkm 28155682Smarkmstatic krb5_error_code 28255682Smarkmakf_add_entry(krb5_context context, 28355682Smarkm krb5_keytab id, 28455682Smarkm krb5_keytab_entry *entry) 28555682Smarkm{ 28655682Smarkm struct akf_data *d = id->data; 28755682Smarkm int fd, created = 0; 28872445Sassar krb5_error_code ret; 28990926Snectar int32_t len; 29090926Snectar krb5_storage *sp; 29155682Smarkm 29290926Snectar 293178825Sdfr if (entry->keyblock.keyvalue.length != 8) 29490926Snectar return 0; 295178825Sdfr switch(entry->keyblock.keytype) { 296178825Sdfr case ETYPE_DES_CBC_CRC: 297178825Sdfr case ETYPE_DES_CBC_MD4: 298178825Sdfr case ETYPE_DES_CBC_MD5: 299178825Sdfr break; 300178825Sdfr default: 301178825Sdfr return 0; 302178825Sdfr } 30390926Snectar 30455682Smarkm fd = open (d->filename, O_RDWR | O_BINARY); 30555682Smarkm if (fd < 0) { 30655682Smarkm fd = open (d->filename, 307103423Snectar O_RDWR | O_BINARY | O_CREAT | O_EXCL, 0600); 30878527Sassar if (fd < 0) { 30978527Sassar ret = errno; 31078527Sassar krb5_set_error_string(context, "open(%s): %s", d->filename, 31178527Sassar strerror(ret)); 31278527Sassar return ret; 31378527Sassar } 31455682Smarkm created = 1; 31555682Smarkm } 31655682Smarkm 31790926Snectar sp = krb5_storage_from_fd(fd); 31890926Snectar if(sp == NULL) { 31990926Snectar close(fd); 32090926Snectar krb5_set_error_string (context, "malloc: out of memory"); 32190926Snectar return ENOMEM; 32290926Snectar } 32390926Snectar if (created) 32490926Snectar len = 0; 32590926Snectar else { 326102644Snectar if(krb5_storage_seek(sp, 0, SEEK_SET) < 0) { 32778527Sassar ret = errno; 32872445Sassar krb5_storage_free(sp); 32972445Sassar close(fd); 33078527Sassar krb5_set_error_string (context, "seek: %s", strerror(ret)); 33178527Sassar return ret; 33272445Sassar } 33390926Snectar 33490926Snectar ret = krb5_ret_int32(sp, &len); 33572445Sassar if(ret) { 33672445Sassar krb5_storage_free(sp); 33772445Sassar close(fd); 33872445Sassar return ret; 33972445Sassar } 34090926Snectar } 341178825Sdfr 342178825Sdfr /* 343178825Sdfr * Make sure we don't add the entry twice, assumes the DES 344178825Sdfr * encryption types are all the same key. 345178825Sdfr */ 346178825Sdfr if (len > 0) { 347178825Sdfr int32_t kvno; 348178825Sdfr int i; 349178825Sdfr 350178825Sdfr for (i = 0; i < len; i++) { 351178825Sdfr ret = krb5_ret_int32(sp, &kvno); 352178825Sdfr if (ret) { 353178825Sdfr krb5_set_error_string (context, "Failed to get kvno "); 354178825Sdfr goto out; 355178825Sdfr } 356178825Sdfr if(krb5_storage_seek(sp, 8, SEEK_CUR) < 0) { 357178825Sdfr krb5_set_error_string (context, "seek: %s", strerror(ret)); 358178825Sdfr goto out; 359178825Sdfr } 360178825Sdfr if (kvno == entry->vno) { 361178825Sdfr ret = 0; 362178825Sdfr goto out; 363178825Sdfr } 364178825Sdfr } 365178825Sdfr } 366178825Sdfr 36790926Snectar len++; 36890926Snectar 369102644Snectar if(krb5_storage_seek(sp, 0, SEEK_SET) < 0) { 37090926Snectar ret = errno; 37190926Snectar krb5_set_error_string (context, "seek: %s", strerror(ret)); 372178825Sdfr goto out; 37390926Snectar } 37490926Snectar 37590926Snectar ret = krb5_store_int32(sp, len); 37690926Snectar if(ret) { 377178825Sdfr krb5_set_error_string(context, "keytab keyfile failed new length"); 37890926Snectar return ret; 37990926Snectar } 38055682Smarkm 381102644Snectar if(krb5_storage_seek(sp, (len - 1) * (8 + 4), SEEK_CUR) < 0) { 38290926Snectar ret = errno; 383178825Sdfr krb5_set_error_string (context, "seek to end: %s", strerror(ret)); 384178825Sdfr goto out; 38590926Snectar } 38672445Sassar 38790926Snectar ret = krb5_store_int32(sp, entry->vno); 38890926Snectar if(ret) { 389178825Sdfr krb5_set_error_string(context, "keytab keyfile failed store kvno"); 390178825Sdfr goto out; 39155682Smarkm } 392102644Snectar ret = krb5_storage_write(sp, entry->keyblock.keyvalue.data, 393102644Snectar entry->keyblock.keyvalue.length); 39490926Snectar if(ret != entry->keyblock.keyvalue.length) { 395178825Sdfr if (ret < 0) 396178825Sdfr ret = errno; 397178825Sdfr else 398178825Sdfr ret = ENOTTY; 399178825Sdfr krb5_set_error_string(context, "keytab keyfile failed to add key"); 400178825Sdfr goto out; 40190926Snectar } 402178825Sdfr ret = 0; 403178825Sdfrout: 40490926Snectar krb5_storage_free(sp); 40555682Smarkm close (fd); 406178825Sdfr return ret; 40755682Smarkm} 40855682Smarkm 40955682Smarkmconst krb5_kt_ops krb5_akf_ops = { 41055682Smarkm "AFSKEYFILE", 41155682Smarkm akf_resolve, 41255682Smarkm akf_get_name, 41355682Smarkm akf_close, 41455682Smarkm NULL, /* get */ 41555682Smarkm akf_start_seq_get, 41655682Smarkm akf_next_entry, 41755682Smarkm akf_end_seq_get, 41855682Smarkm akf_add_entry, 41955682Smarkm NULL /* remove */ 42055682Smarkm}; 421