155682Smarkm/* 290926Snectar * 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 36178825SdfrRCSID("$Id: keytab_krb4.c 17046 2006-04-10 17:10:53Z lha $"); 3755682Smarkm 3855682Smarkmstruct krb4_kt_data { 3955682Smarkm char *filename; 4055682Smarkm}; 4155682Smarkm 4255682Smarkmstatic krb5_error_code 4355682Smarkmkrb4_kt_resolve(krb5_context context, const char *name, krb5_keytab id) 4455682Smarkm{ 4555682Smarkm struct krb4_kt_data *d; 4655682Smarkm 4755682Smarkm d = malloc (sizeof(*d)); 4878527Sassar if (d == NULL) { 4978527Sassar krb5_set_error_string (context, "malloc: out of memory"); 5055682Smarkm return ENOMEM; 5178527Sassar } 5255682Smarkm d->filename = strdup (name); 5355682Smarkm if (d->filename == NULL) { 5455682Smarkm free(d); 5578527Sassar krb5_set_error_string (context, "malloc: out of memory"); 5655682Smarkm return ENOMEM; 5755682Smarkm } 5855682Smarkm id->data = d; 5955682Smarkm return 0; 6055682Smarkm} 6155682Smarkm 6255682Smarkmstatic krb5_error_code 6355682Smarkmkrb4_kt_get_name (krb5_context context, 6455682Smarkm krb5_keytab id, 6555682Smarkm char *name, 6655682Smarkm size_t name_sz) 6755682Smarkm{ 6855682Smarkm struct krb4_kt_data *d = id->data; 6955682Smarkm 7055682Smarkm strlcpy (name, d->filename, name_sz); 7155682Smarkm return 0; 7255682Smarkm} 7355682Smarkm 7455682Smarkmstatic krb5_error_code 7555682Smarkmkrb4_kt_close (krb5_context context, 7655682Smarkm krb5_keytab id) 7755682Smarkm{ 7855682Smarkm struct krb4_kt_data *d = id->data; 7955682Smarkm 8055682Smarkm free (d->filename); 8155682Smarkm free (d); 8255682Smarkm return 0; 8355682Smarkm} 8455682Smarkm 8555682Smarkmstruct krb4_cursor_extra_data { 8655682Smarkm krb5_keytab_entry entry; 8755682Smarkm int num; 8855682Smarkm}; 8955682Smarkm 9090926Snectarstatic int 9190926Snectaropen_flock(const char *filename, int flags, int mode) 9290926Snectar{ 9390926Snectar int lock_mode; 9490926Snectar int tries = 0; 9590926Snectar int fd = open(filename, flags, mode); 9690926Snectar if(fd < 0) 9790926Snectar return fd; 9890926Snectar if((flags & O_ACCMODE) == O_RDONLY) 9990926Snectar lock_mode = LOCK_SH | LOCK_NB; 10090926Snectar else 10190926Snectar lock_mode = LOCK_EX | LOCK_NB; 10290926Snectar while(flock(fd, lock_mode) < 0) { 10390926Snectar if(++tries < 5) { 10490926Snectar sleep(1); 10590926Snectar } else { 10690926Snectar close(fd); 10790926Snectar return -1; 10890926Snectar } 10990926Snectar } 11090926Snectar return fd; 11190926Snectar} 11290926Snectar 11390926Snectar 11490926Snectar 11555682Smarkmstatic krb5_error_code 11655682Smarkmkrb4_kt_start_seq_get_int (krb5_context context, 11755682Smarkm krb5_keytab id, 11855682Smarkm int flags, 11955682Smarkm krb5_kt_cursor *c) 12055682Smarkm{ 12155682Smarkm struct krb4_kt_data *d = id->data; 12255682Smarkm struct krb4_cursor_extra_data *ed; 12378527Sassar int ret; 12455682Smarkm 12555682Smarkm ed = malloc (sizeof(*ed)); 12678527Sassar if (ed == NULL) { 12778527Sassar krb5_set_error_string (context, "malloc: out of memory"); 12855682Smarkm return ENOMEM; 12978527Sassar } 13055682Smarkm ed->entry.principal = NULL; 13155682Smarkm ed->num = -1; 13255682Smarkm c->data = ed; 13390926Snectar c->fd = open_flock (d->filename, flags, 0); 13455682Smarkm if (c->fd < 0) { 13578527Sassar ret = errno; 13655682Smarkm free (ed); 13778527Sassar krb5_set_error_string(context, "open(%s): %s", d->filename, 13878527Sassar strerror(ret)); 13978527Sassar return ret; 14055682Smarkm } 14155682Smarkm c->sp = krb5_storage_from_fd(c->fd); 142178825Sdfr if(c->sp == NULL) { 143178825Sdfr close(c->fd); 144178825Sdfr free(ed); 145178825Sdfr return ENOMEM; 146178825Sdfr } 147102644Snectar krb5_storage_set_eof_code(c->sp, KRB5_KT_END); 14855682Smarkm return 0; 14955682Smarkm} 15055682Smarkm 15155682Smarkmstatic krb5_error_code 15255682Smarkmkrb4_kt_start_seq_get (krb5_context context, 15355682Smarkm krb5_keytab id, 15455682Smarkm krb5_kt_cursor *c) 15555682Smarkm{ 15655682Smarkm return krb4_kt_start_seq_get_int (context, id, O_BINARY | O_RDONLY, c); 15755682Smarkm} 15855682Smarkm 15955682Smarkmstatic krb5_error_code 16055682Smarkmread_v4_entry (krb5_context context, 16155682Smarkm struct krb4_kt_data *d, 16255682Smarkm krb5_kt_cursor *c, 16355682Smarkm struct krb4_cursor_extra_data *ed) 16455682Smarkm{ 165178825Sdfr unsigned char des_key[8]; 16655682Smarkm krb5_error_code ret; 16755682Smarkm char *service, *instance, *realm; 16855682Smarkm int8_t kvno; 16955682Smarkm 17055682Smarkm ret = krb5_ret_stringz(c->sp, &service); 17155682Smarkm if (ret) 17255682Smarkm return ret; 17355682Smarkm ret = krb5_ret_stringz(c->sp, &instance); 17455682Smarkm if (ret) { 17555682Smarkm free (service); 17655682Smarkm return ret; 17755682Smarkm } 17855682Smarkm ret = krb5_ret_stringz(c->sp, &realm); 17955682Smarkm if (ret) { 18055682Smarkm free (service); 18155682Smarkm free (instance); 18255682Smarkm return ret; 18355682Smarkm } 18455682Smarkm ret = krb5_425_conv_principal (context, service, instance, realm, 18555682Smarkm &ed->entry.principal); 18655682Smarkm free (service); 18755682Smarkm free (instance); 18855682Smarkm free (realm); 18955682Smarkm if (ret) 19055682Smarkm return ret; 19155682Smarkm ret = krb5_ret_int8(c->sp, &kvno); 19255682Smarkm if (ret) { 19355682Smarkm krb5_free_principal (context, ed->entry.principal); 19455682Smarkm return ret; 19555682Smarkm } 196178825Sdfr ret = krb5_storage_read(c->sp, des_key, sizeof(des_key)); 19755682Smarkm if (ret < 0) { 19855682Smarkm krb5_free_principal(context, ed->entry.principal); 19955682Smarkm return ret; 20055682Smarkm } 20155682Smarkm if (ret < 8) { 20255682Smarkm krb5_free_principal(context, ed->entry.principal); 20355682Smarkm return EINVAL; 20455682Smarkm } 20555682Smarkm ed->entry.vno = kvno; 20655682Smarkm ret = krb5_data_copy (&ed->entry.keyblock.keyvalue, 207178825Sdfr des_key, sizeof(des_key)); 20855682Smarkm if (ret) 20955682Smarkm return ret; 21055682Smarkm ed->entry.timestamp = time(NULL); 21155682Smarkm ed->num = 0; 21255682Smarkm return 0; 21355682Smarkm} 21455682Smarkm 21555682Smarkmstatic krb5_error_code 21655682Smarkmkrb4_kt_next_entry (krb5_context context, 21755682Smarkm krb5_keytab id, 21855682Smarkm krb5_keytab_entry *entry, 21955682Smarkm krb5_kt_cursor *c) 22055682Smarkm{ 22155682Smarkm krb5_error_code ret; 22255682Smarkm struct krb4_kt_data *d = id->data; 22355682Smarkm struct krb4_cursor_extra_data *ed = c->data; 22455682Smarkm const krb5_enctype keytypes[] = {ETYPE_DES_CBC_MD5, 22555682Smarkm ETYPE_DES_CBC_MD4, 22655682Smarkm ETYPE_DES_CBC_CRC}; 22755682Smarkm 22855682Smarkm if (ed->num == -1) { 22955682Smarkm ret = read_v4_entry (context, d, c, ed); 23055682Smarkm if (ret) 23155682Smarkm return ret; 23255682Smarkm } 23355682Smarkm ret = krb5_kt_copy_entry_contents (context, 23455682Smarkm &ed->entry, 23555682Smarkm entry); 23655682Smarkm if (ret) 23755682Smarkm return ret; 23855682Smarkm entry->keyblock.keytype = keytypes[ed->num]; 23955682Smarkm if (++ed->num == 3) { 24055682Smarkm krb5_kt_free_entry (context, &ed->entry); 24155682Smarkm ed->num = -1; 24255682Smarkm } 24355682Smarkm return 0; 24455682Smarkm} 24555682Smarkm 24655682Smarkmstatic krb5_error_code 24755682Smarkmkrb4_kt_end_seq_get (krb5_context context, 24855682Smarkm krb5_keytab id, 24955682Smarkm krb5_kt_cursor *c) 25055682Smarkm{ 25155682Smarkm struct krb4_cursor_extra_data *ed = c->data; 25255682Smarkm 25355682Smarkm krb5_storage_free (c->sp); 25455682Smarkm if (ed->num != -1) 25555682Smarkm krb5_kt_free_entry (context, &ed->entry); 25655682Smarkm free (c->data); 25755682Smarkm close (c->fd); 25855682Smarkm return 0; 25955682Smarkm} 26055682Smarkm 26155682Smarkmstatic krb5_error_code 26290926Snectarkrb4_store_keytab_entry(krb5_context context, 26390926Snectar krb5_keytab_entry *entry, 26490926Snectar krb5_storage *sp) 26555682Smarkm{ 26655682Smarkm krb5_error_code ret; 26772445Sassar#define ANAME_SZ 40 26872445Sassar#define INST_SZ 40 26972445Sassar#define REALM_SZ 40 27055682Smarkm char service[ANAME_SZ]; 27155682Smarkm char instance[INST_SZ]; 27255682Smarkm char realm[REALM_SZ]; 27390926Snectar ret = krb5_524_conv_principal (context, entry->principal, 27490926Snectar service, instance, realm); 27590926Snectar if (ret) 27690926Snectar return ret; 27790926Snectar if (entry->keyblock.keyvalue.length == 8 27890926Snectar && entry->keyblock.keytype == ETYPE_DES_CBC_MD5) { 27990926Snectar ret = krb5_store_stringz(sp, service); 28090926Snectar ret = krb5_store_stringz(sp, instance); 28190926Snectar ret = krb5_store_stringz(sp, realm); 28290926Snectar ret = krb5_store_int8(sp, entry->vno); 283102644Snectar ret = krb5_storage_write(sp, entry->keyblock.keyvalue.data, 8); 28490926Snectar } 28590926Snectar return 0; 28690926Snectar} 28755682Smarkm 28890926Snectarstatic krb5_error_code 28990926Snectarkrb4_kt_add_entry (krb5_context context, 29090926Snectar krb5_keytab id, 29190926Snectar krb5_keytab_entry *entry) 29290926Snectar{ 29390926Snectar struct krb4_kt_data *d = id->data; 29490926Snectar krb5_storage *sp; 29590926Snectar krb5_error_code ret; 29690926Snectar int fd; 29790926Snectar 29890926Snectar fd = open_flock (d->filename, O_WRONLY | O_APPEND | O_BINARY, 0); 29955682Smarkm if (fd < 0) { 30090926Snectar fd = open_flock (d->filename, 30155682Smarkm O_WRONLY | O_APPEND | O_BINARY | O_CREAT, 0600); 30278527Sassar if (fd < 0) { 30378527Sassar ret = errno; 30478527Sassar krb5_set_error_string(context, "open(%s): %s", d->filename, 30578527Sassar strerror(ret)); 30678527Sassar return ret; 30778527Sassar } 30855682Smarkm } 30990926Snectar sp = krb5_storage_from_fd(fd); 31090926Snectar if(sp == NULL) { 31190926Snectar close(fd); 31290926Snectar return ENOMEM; 31355682Smarkm } 314178825Sdfr krb5_storage_set_eof_code(sp, KRB5_KT_END); 31590926Snectar ret = krb4_store_keytab_entry(context, entry, sp); 31690926Snectar krb5_storage_free(sp); 31790926Snectar if(close (fd) < 0) 31890926Snectar return errno; 31990926Snectar return ret; 32090926Snectar} 32190926Snectar 32290926Snectarstatic krb5_error_code 32390926Snectarkrb4_kt_remove_entry(krb5_context context, 324178825Sdfr krb5_keytab id, 325178825Sdfr krb5_keytab_entry *entry) 32690926Snectar{ 32790926Snectar struct krb4_kt_data *d = id->data; 32890926Snectar krb5_error_code ret; 32990926Snectar krb5_keytab_entry e; 33090926Snectar krb5_kt_cursor cursor; 33190926Snectar krb5_storage *sp; 33290926Snectar int remove_flag = 0; 33390926Snectar 33490926Snectar sp = krb5_storage_emem(); 335178825Sdfr if (sp == NULL) { 336178825Sdfr krb5_set_error_string(context, "malloc: out of memory"); 337178825Sdfr return ENOMEM; 338178825Sdfr } 33990926Snectar ret = krb5_kt_start_seq_get(context, id, &cursor); 340178825Sdfr if (ret) { 341178825Sdfr krb5_storage_free(sp); 342178825Sdfr return ret; 343178825Sdfr } 34490926Snectar while(krb5_kt_next_entry(context, id, &e, &cursor) == 0) { 34590926Snectar if(!krb5_kt_compare(context, &e, entry->principal, 34690926Snectar entry->vno, entry->keyblock.keytype)) { 34790926Snectar ret = krb4_store_keytab_entry(context, &e, sp); 34890926Snectar if(ret) { 349178825Sdfr krb5_kt_free_entry(context, &e); 35090926Snectar krb5_storage_free(sp); 35190926Snectar return ret; 35290926Snectar } 35390926Snectar } else 35490926Snectar remove_flag = 1; 355178825Sdfr krb5_kt_free_entry(context, &e); 35655682Smarkm } 35790926Snectar krb5_kt_end_seq_get(context, id, &cursor); 35890926Snectar if(remove_flag) { 35990926Snectar int fd; 36090926Snectar unsigned char buf[1024]; 36190926Snectar ssize_t n; 36290926Snectar krb5_data data; 36390926Snectar struct stat st; 36490926Snectar 36590926Snectar krb5_storage_to_data(sp, &data); 36690926Snectar krb5_storage_free(sp); 36790926Snectar 36890926Snectar fd = open_flock (d->filename, O_RDWR | O_BINARY, 0); 36990926Snectar if(fd < 0) { 37090926Snectar memset(data.data, 0, data.length); 37190926Snectar krb5_data_free(&data); 37290926Snectar if(errno == EACCES || errno == EROFS) 37390926Snectar return KRB5_KT_NOWRITE; 37490926Snectar return errno; 37590926Snectar } 37690926Snectar 37790926Snectar if(write(fd, data.data, data.length) != data.length) { 37890926Snectar memset(data.data, 0, data.length); 379178825Sdfr krb5_data_free(&data); 38090926Snectar close(fd); 38190926Snectar krb5_set_error_string(context, "failed writing to \"%s\"", d->filename); 38290926Snectar return errno; 38390926Snectar } 38490926Snectar memset(data.data, 0, data.length); 38590926Snectar if(fstat(fd, &st) < 0) { 386178825Sdfr krb5_data_free(&data); 38790926Snectar close(fd); 38890926Snectar krb5_set_error_string(context, "failed getting size of \"%s\"", d->filename); 38990926Snectar return errno; 39090926Snectar } 39190926Snectar st.st_size -= data.length; 39290926Snectar memset(buf, 0, sizeof(buf)); 39390926Snectar while(st.st_size > 0) { 39490926Snectar n = min(st.st_size, sizeof(buf)); 39590926Snectar n = write(fd, buf, n); 39690926Snectar if(n <= 0) { 397178825Sdfr krb5_data_free(&data); 39890926Snectar close(fd); 39990926Snectar krb5_set_error_string(context, "failed writing to \"%s\"", d->filename); 40090926Snectar return errno; 40190926Snectar 40290926Snectar } 40390926Snectar st.st_size -= n; 40490926Snectar } 40590926Snectar if(ftruncate(fd, data.length) < 0) { 406178825Sdfr krb5_data_free(&data); 40790926Snectar close(fd); 40890926Snectar krb5_set_error_string(context, "failed truncating \"%s\"", d->filename); 40990926Snectar return errno; 41090926Snectar } 41190926Snectar krb5_data_free(&data); 41290926Snectar if(close(fd) < 0) { 41390926Snectar krb5_set_error_string(context, "error closing \"%s\"", d->filename); 41490926Snectar return errno; 41590926Snectar } 41690926Snectar return 0; 417178825Sdfr } else { 418178825Sdfr krb5_storage_free(sp); 41990926Snectar return KRB5_KT_NOTFOUND; 420178825Sdfr } 42155682Smarkm} 42255682Smarkm 42390926Snectar 42472445Sassarconst krb5_kt_ops krb4_fkt_ops = { 42555682Smarkm "krb4", 42655682Smarkm krb4_kt_resolve, 42755682Smarkm krb4_kt_get_name, 42855682Smarkm krb4_kt_close, 42955682Smarkm NULL, /* get */ 43055682Smarkm krb4_kt_start_seq_get, 43155682Smarkm krb4_kt_next_entry, 43255682Smarkm krb4_kt_end_seq_get, 43355682Smarkm krb4_kt_add_entry, /* add_entry */ 43490926Snectar krb4_kt_remove_entry /* remove_entry */ 43555682Smarkm}; 43678527Sassar 43778527Sassarconst krb5_kt_ops krb5_srvtab_fkt_ops = { 43878527Sassar "SRVTAB", 43978527Sassar krb4_kt_resolve, 44078527Sassar krb4_kt_get_name, 44178527Sassar krb4_kt_close, 44278527Sassar NULL, /* get */ 44378527Sassar krb4_kt_start_seq_get, 44478527Sassar krb4_kt_next_entry, 44578527Sassar krb4_kt_end_seq_get, 44678527Sassar krb4_kt_add_entry, /* add_entry */ 44790926Snectar krb4_kt_remove_entry /* remove_entry */ 44878527Sassar}; 449