Home | History | Annotate | Line # | Download | only in krb5
      1 /*	$NetBSD: keytab_file.c,v 1.2 2017/01/28 21:31:49 christos Exp $	*/
      2 
      3 /*
      4  * Copyright (c) 1997 - 2008 Kungliga Tekniska Hgskolan
      5  * (Royal Institute of Technology, Stockholm, Sweden).
      6  * All rights reserved.
      7  *
      8  * Redistribution and use in source and binary forms, with or without
      9  * modification, are permitted provided that the following conditions
     10  * are met:
     11  *
     12  * 1. Redistributions of source code must retain the above copyright
     13  *    notice, this list of conditions and the following disclaimer.
     14  *
     15  * 2. Redistributions in binary form must reproduce the above copyright
     16  *    notice, this list of conditions and the following disclaimer in the
     17  *    documentation and/or other materials provided with the distribution.
     18  *
     19  * 3. Neither the name of the Institute nor the names of its contributors
     20  *    may be used to endorse or promote products derived from this software
     21  *    without specific prior written permission.
     22  *
     23  * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
     24  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     25  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     26  * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
     27  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     28  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
     29  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     30  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     31  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     32  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     33  * SUCH DAMAGE.
     34  */
     35 
     36 #include "krb5_locl.h"
     37 
     38 #define KRB5_KT_VNO_1 1
     39 #define KRB5_KT_VNO_2 2
     40 #define KRB5_KT_VNO   KRB5_KT_VNO_2
     41 
     42 #define KRB5_KT_FL_JAVA 1
     43 
     44 
     45 /* file operations -------------------------------------------- */
     46 
     47 struct fkt_data {
     48     char *filename;
     49     int flags;
     50 };
     51 
     52 static krb5_error_code
     53 krb5_kt_ret_data(krb5_context context,
     54 		 krb5_storage *sp,
     55 		 krb5_data *data)
     56 {
     57     int ret;
     58     int16_t size;
     59     ret = krb5_ret_int16(sp, &size);
     60     if(ret)
     61 	return ret;
     62     data->length = size;
     63     data->data = malloc(size);
     64     if (data->data == NULL)
     65 	return krb5_enomem(context);
     66     ret = krb5_storage_read(sp, data->data, size);
     67     if(ret != size)
     68 	return (ret < 0)? errno : KRB5_KT_END;
     69     return 0;
     70 }
     71 
     72 static krb5_error_code
     73 krb5_kt_ret_string(krb5_context context,
     74 		   krb5_storage *sp,
     75 		   heim_general_string *data)
     76 {
     77     int ret;
     78     int16_t size;
     79     ret = krb5_ret_int16(sp, &size);
     80     if(ret)
     81 	return ret;
     82     *data = malloc(size + 1);
     83     if (*data == NULL)
     84 	return krb5_enomem(context);
     85     ret = krb5_storage_read(sp, *data, size);
     86     (*data)[size] = '\0';
     87     if(ret != size)
     88 	return (ret < 0)? errno : KRB5_KT_END;
     89     return 0;
     90 }
     91 
     92 static krb5_error_code
     93 krb5_kt_store_data(krb5_context context,
     94 		   krb5_storage *sp,
     95 		   krb5_data data)
     96 {
     97     int ret;
     98     ret = krb5_store_int16(sp, data.length);
     99     if(ret < 0)
    100 	return ret;
    101     ret = krb5_storage_write(sp, data.data, data.length);
    102     if(ret != (int)data.length){
    103 	if(ret < 0)
    104 	    return errno;
    105 	return KRB5_KT_END;
    106     }
    107     return 0;
    108 }
    109 
    110 static krb5_error_code
    111 krb5_kt_store_string(krb5_storage *sp,
    112 		     heim_general_string data)
    113 {
    114     int ret;
    115     size_t len = strlen(data);
    116     ret = krb5_store_int16(sp, len);
    117     if(ret < 0)
    118 	return ret;
    119     ret = krb5_storage_write(sp, data, len);
    120     if(ret != (int)len){
    121 	if(ret < 0)
    122 	    return errno;
    123 	return KRB5_KT_END;
    124     }
    125     return 0;
    126 }
    127 
    128 static krb5_error_code
    129 krb5_kt_ret_keyblock(krb5_context context,
    130 		     struct fkt_data *fkt,
    131 		     krb5_storage *sp,
    132 		     krb5_keyblock *p)
    133 {
    134     int ret;
    135     int16_t tmp;
    136 
    137     ret = krb5_ret_int16(sp, &tmp); /* keytype + etype */
    138     if(ret)  {
    139 	krb5_set_error_message(context, ret,
    140 			       N_("Cant read keyblock from file %s", ""),
    141 			       fkt->filename);
    142 	return ret;
    143     }
    144     p->keytype = tmp;
    145     ret = krb5_kt_ret_data(context, sp, &p->keyvalue);
    146     if (ret)
    147 	krb5_set_error_message(context, ret,
    148 			       N_("Cant read keyblock from file %s", ""),
    149 			       fkt->filename);
    150     return ret;
    151 }
    152 
    153 static krb5_error_code
    154 krb5_kt_store_keyblock(krb5_context context,
    155 		       struct fkt_data *fkt,
    156 		       krb5_storage *sp,
    157 		       krb5_keyblock *p)
    158 {
    159     int ret;
    160 
    161     ret = krb5_store_int16(sp, p->keytype); /* keytype + etype */
    162     if(ret) {
    163 	krb5_set_error_message(context, ret,
    164 			       N_("Cant store keyblock to file %s", ""),
    165 			       fkt->filename);
    166 	return ret;
    167     }
    168     ret = krb5_kt_store_data(context, sp, p->keyvalue);
    169     if (ret)
    170 	krb5_set_error_message(context, ret,
    171 			       N_("Cant store keyblock to file %s", ""),
    172 			       fkt->filename);
    173     return ret;
    174 }
    175 
    176 
    177 static krb5_error_code
    178 krb5_kt_ret_principal(krb5_context context,
    179 		      struct fkt_data *fkt,
    180 		      krb5_storage *sp,
    181 		      krb5_principal *princ)
    182 {
    183     size_t i;
    184     int ret;
    185     krb5_principal p;
    186     int16_t len;
    187 
    188     ALLOC(p, 1);
    189     if(p == NULL)
    190 	return krb5_enomem(context);
    191 
    192     ret = krb5_ret_int16(sp, &len);
    193     if(ret) {
    194 	krb5_set_error_message(context, ret,
    195 			       N_("Failed decoding length of "
    196 				  "keytab principal in keytab file %s", ""),
    197 			       fkt->filename);
    198 	goto out;
    199     }
    200     if(krb5_storage_is_flags(sp, KRB5_STORAGE_PRINCIPAL_WRONG_NUM_COMPONENTS))
    201 	len--;
    202     if (len < 0) {
    203 	ret = KRB5_KT_END;
    204 	krb5_set_error_message(context, ret,
    205 			       N_("Keytab principal contains "
    206 				  "invalid length in keytab %s", ""),
    207 			       fkt->filename);
    208 	goto out;
    209     }
    210     ret = krb5_kt_ret_string(context, sp, &p->realm);
    211     if(ret) {
    212 	krb5_set_error_message(context, ret,
    213 			       N_("Can't read realm from keytab: %s", ""),
    214 			       fkt->filename);
    215 	goto out;
    216     }
    217     p->name.name_string.val = calloc(len, sizeof(*p->name.name_string.val));
    218     if(p->name.name_string.val == NULL) {
    219 	ret = krb5_enomem(context);
    220 	goto out;
    221     }
    222     p->name.name_string.len = len;
    223     for(i = 0; i < p->name.name_string.len; i++){
    224 	ret = krb5_kt_ret_string(context, sp, p->name.name_string.val + i);
    225 	if(ret) {
    226 	    krb5_set_error_message(context, ret,
    227 				   N_("Can't read principal from "
    228 				      "keytab: %s", ""),
    229 				   fkt->filename);
    230 	    goto out;
    231 	}
    232     }
    233     if (krb5_storage_is_flags(sp, KRB5_STORAGE_PRINCIPAL_NO_NAME_TYPE))
    234 	p->name.name_type = KRB5_NT_UNKNOWN;
    235     else {
    236 	int32_t tmp32;
    237 	ret = krb5_ret_int32(sp, &tmp32);
    238 	p->name.name_type = tmp32;
    239 	if (ret) {
    240 	    krb5_set_error_message(context, ret,
    241 				   N_("Can't read name-type from "
    242 				      "keytab: %s", ""),
    243 				   fkt->filename);
    244 	    goto out;
    245 	}
    246     }
    247     *princ = p;
    248     return 0;
    249 out:
    250     krb5_free_principal(context, p);
    251     return ret;
    252 }
    253 
    254 static krb5_error_code
    255 krb5_kt_store_principal(krb5_context context,
    256 			krb5_storage *sp,
    257 			krb5_principal p)
    258 {
    259     size_t i;
    260     int ret;
    261 
    262     if(krb5_storage_is_flags(sp, KRB5_STORAGE_PRINCIPAL_WRONG_NUM_COMPONENTS))
    263 	ret = krb5_store_int16(sp, p->name.name_string.len + 1);
    264     else
    265 	ret = krb5_store_int16(sp, p->name.name_string.len);
    266     if(ret) return ret;
    267     ret = krb5_kt_store_string(sp, p->realm);
    268     if(ret) return ret;
    269     for(i = 0; i < p->name.name_string.len; i++){
    270 	ret = krb5_kt_store_string(sp, p->name.name_string.val[i]);
    271 	if(ret)
    272 	    return ret;
    273     }
    274     if(!krb5_storage_is_flags(sp, KRB5_STORAGE_PRINCIPAL_NO_NAME_TYPE)) {
    275 	ret = krb5_store_int32(sp, p->name.name_type);
    276 	if(ret)
    277 	    return ret;
    278     }
    279 
    280     return 0;
    281 }
    282 
    283 static krb5_error_code KRB5_CALLCONV
    284 fkt_resolve(krb5_context context, const char *name, krb5_keytab id)
    285 {
    286     struct fkt_data *d;
    287 
    288     d = malloc(sizeof(*d));
    289     if(d == NULL)
    290 	return krb5_enomem(context);
    291     d->filename = strdup(name);
    292     if(d->filename == NULL) {
    293 	free(d);
    294 	return krb5_enomem(context);
    295     }
    296     d->flags = 0;
    297     id->data = d;
    298     return 0;
    299 }
    300 
    301 static krb5_error_code KRB5_CALLCONV
    302 fkt_resolve_java14(krb5_context context, const char *name, krb5_keytab id)
    303 {
    304     krb5_error_code ret;
    305 
    306     ret = fkt_resolve(context, name, id);
    307     if (ret == 0) {
    308 	struct fkt_data *d = id->data;
    309 	d->flags |= KRB5_KT_FL_JAVA;
    310     }
    311     return ret;
    312 }
    313 
    314 static krb5_error_code KRB5_CALLCONV
    315 fkt_close(krb5_context context, krb5_keytab id)
    316 {
    317     struct fkt_data *d = id->data;
    318     free(d->filename);
    319     free(d);
    320     return 0;
    321 }
    322 
    323 static krb5_error_code KRB5_CALLCONV
    324 fkt_destroy(krb5_context context, krb5_keytab id)
    325 {
    326     struct fkt_data *d = id->data;
    327     _krb5_erase_file(context, d->filename);
    328     return 0;
    329 }
    330 
    331 static krb5_error_code KRB5_CALLCONV
    332 fkt_get_name(krb5_context context,
    333 	     krb5_keytab id,
    334 	     char *name,
    335 	     size_t namesize)
    336 {
    337     /* This function is XXX */
    338     struct fkt_data *d = id->data;
    339     strlcpy(name, d->filename, namesize);
    340     return 0;
    341 }
    342 
    343 static void
    344 storage_set_flags(krb5_context context, krb5_storage *sp, int vno)
    345 {
    346     int flags = 0;
    347     switch(vno) {
    348     case KRB5_KT_VNO_1:
    349 	flags |= KRB5_STORAGE_PRINCIPAL_WRONG_NUM_COMPONENTS;
    350 	flags |= KRB5_STORAGE_PRINCIPAL_NO_NAME_TYPE;
    351 	flags |= KRB5_STORAGE_HOST_BYTEORDER;
    352 	break;
    353     case KRB5_KT_VNO_2:
    354 	break;
    355     default:
    356 	krb5_warnx(context,
    357 		   "storage_set_flags called with bad vno (%d)", vno);
    358     }
    359     krb5_storage_set_flags(sp, flags);
    360 }
    361 
    362 static krb5_error_code
    363 fkt_start_seq_get_int(krb5_context context,
    364 		      krb5_keytab id,
    365 		      int flags,
    366 		      int exclusive,
    367 		      krb5_kt_cursor *c)
    368 {
    369     int8_t pvno, tag;
    370     krb5_error_code ret;
    371     struct fkt_data *d = id->data;
    372 
    373     c->fd = open (d->filename, flags);
    374     if (c->fd < 0) {
    375 	ret = errno;
    376 	krb5_set_error_message(context, ret,
    377 			       N_("keytab %s open failed: %s", ""),
    378 			       d->filename, strerror(ret));
    379 	return ret;
    380     }
    381     rk_cloexec(c->fd);
    382     ret = _krb5_xlock(context, c->fd, exclusive, d->filename);
    383     if (ret) {
    384 	close(c->fd);
    385 	return ret;
    386     }
    387     c->sp = krb5_storage_from_fd(c->fd);
    388     if (c->sp == NULL) {
    389 	_krb5_xunlock(context, c->fd);
    390 	close(c->fd);
    391 	return krb5_enomem(context);
    392     }
    393     krb5_storage_set_eof_code(c->sp, KRB5_KT_END);
    394     ret = krb5_ret_int8(c->sp, &pvno);
    395     if(ret) {
    396 	krb5_storage_free(c->sp);
    397 	_krb5_xunlock(context, c->fd);
    398 	close(c->fd);
    399 	krb5_clear_error_message(context);
    400 	return ret;
    401     }
    402     if(pvno != 5) {
    403 	krb5_storage_free(c->sp);
    404 	_krb5_xunlock(context, c->fd);
    405 	close(c->fd);
    406 	krb5_clear_error_message (context);
    407 	return KRB5_KEYTAB_BADVNO;
    408     }
    409     ret = krb5_ret_int8(c->sp, &tag);
    410     if (ret) {
    411 	krb5_storage_free(c->sp);
    412 	_krb5_xunlock(context, c->fd);
    413 	close(c->fd);
    414 	krb5_clear_error_message(context);
    415 	return ret;
    416     }
    417     id->version = tag;
    418     storage_set_flags(context, c->sp, id->version);
    419     return 0;
    420 }
    421 
    422 static krb5_error_code KRB5_CALLCONV
    423 fkt_start_seq_get(krb5_context context,
    424 		  krb5_keytab id,
    425 		  krb5_kt_cursor *c)
    426 {
    427     return fkt_start_seq_get_int(context, id, O_RDONLY | O_BINARY | O_CLOEXEC, 0, c);
    428 }
    429 
    430 static krb5_error_code
    431 fkt_next_entry_int(krb5_context context,
    432 		   krb5_keytab id,
    433 		   krb5_keytab_entry *entry,
    434 		   krb5_kt_cursor *cursor,
    435 		   off_t *start,
    436 		   off_t *end)
    437 {
    438     struct fkt_data *d = id->data;
    439     int32_t len;
    440     int ret;
    441     int8_t tmp8;
    442     int32_t tmp32;
    443     uint32_t utmp32;
    444     off_t pos, curpos;
    445 
    446     pos = krb5_storage_seek(cursor->sp, 0, SEEK_CUR);
    447 loop:
    448     ret = krb5_ret_int32(cursor->sp, &len);
    449     if (ret)
    450 	return ret;
    451     if(len < 0) {
    452 	pos = krb5_storage_seek(cursor->sp, -len, SEEK_CUR);
    453 	goto loop;
    454     }
    455     ret = krb5_kt_ret_principal (context, d, cursor->sp, &entry->principal);
    456     if (ret)
    457 	goto out;
    458     ret = krb5_ret_uint32(cursor->sp, &utmp32);
    459     entry->timestamp = utmp32;
    460     if (ret)
    461 	goto out;
    462     ret = krb5_ret_int8(cursor->sp, &tmp8);
    463     if (ret)
    464 	goto out;
    465     entry->vno = tmp8;
    466     ret = krb5_kt_ret_keyblock (context, d, cursor->sp, &entry->keyblock);
    467     if (ret)
    468 	goto out;
    469     /* there might be a 32 bit kvno here
    470      * if it's zero, assume that the 8bit one was right,
    471      * otherwise trust the new value */
    472     curpos = krb5_storage_seek(cursor->sp, 0, SEEK_CUR);
    473     if(len + 4 + pos - curpos >= 4) {
    474 	ret = krb5_ret_int32(cursor->sp, &tmp32);
    475 	if (ret == 0 && tmp32 != 0)
    476 	    entry->vno = tmp32;
    477     }
    478     /* there might be a flags field here */
    479     if(len + 4 + pos - curpos >= 8) {
    480 	ret = krb5_ret_uint32(cursor->sp, &utmp32);
    481 	if (ret == 0)
    482 	    entry->flags = utmp32;
    483     } else
    484 	entry->flags = 0;
    485 
    486     entry->aliases = NULL;
    487 
    488     if(start) *start = pos;
    489     if(end) *end = pos + 4 + len;
    490  out:
    491     if (ret)
    492         krb5_kt_free_entry(context, entry);
    493     krb5_storage_seek(cursor->sp, pos + 4 + len, SEEK_SET);
    494     return ret;
    495 }
    496 
    497 static krb5_error_code KRB5_CALLCONV
    498 fkt_next_entry(krb5_context context,
    499 	       krb5_keytab id,
    500 	       krb5_keytab_entry *entry,
    501 	       krb5_kt_cursor *cursor)
    502 {
    503     return fkt_next_entry_int(context, id, entry, cursor, NULL, NULL);
    504 }
    505 
    506 static krb5_error_code KRB5_CALLCONV
    507 fkt_end_seq_get(krb5_context context,
    508 		krb5_keytab id,
    509 		krb5_kt_cursor *cursor)
    510 {
    511     krb5_storage_free(cursor->sp);
    512     _krb5_xunlock(context, cursor->fd);
    513     close(cursor->fd);
    514     return 0;
    515 }
    516 
    517 static krb5_error_code KRB5_CALLCONV
    518 fkt_setup_keytab(krb5_context context,
    519 		 krb5_keytab id,
    520 		 krb5_storage *sp)
    521 {
    522     krb5_error_code ret;
    523     ret = krb5_store_int8(sp, 5);
    524     if(ret)
    525 	return ret;
    526     if(id->version == 0)
    527 	id->version = KRB5_KT_VNO;
    528     return krb5_store_int8 (sp, id->version);
    529 }
    530 
    531 static krb5_error_code KRB5_CALLCONV
    532 fkt_add_entry(krb5_context context,
    533 	      krb5_keytab id,
    534 	      krb5_keytab_entry *entry)
    535 {
    536     int ret;
    537     int fd;
    538     krb5_storage *sp;
    539     struct fkt_data *d = id->data;
    540     krb5_data keytab;
    541     int32_t len;
    542 
    543     fd = open (d->filename, O_RDWR | O_BINARY | O_CLOEXEC);
    544     if (fd < 0) {
    545 	fd = open (d->filename, O_RDWR | O_CREAT | O_EXCL | O_BINARY | O_CLOEXEC, 0600);
    546 	if (fd < 0) {
    547 	    ret = errno;
    548 	    krb5_set_error_message(context, ret,
    549 				   N_("open(%s): %s", ""), d->filename,
    550 				   strerror(ret));
    551 	    return ret;
    552 	}
    553 	rk_cloexec(fd);
    554 
    555 	ret = _krb5_xlock(context, fd, 1, d->filename);
    556 	if (ret) {
    557 	    close(fd);
    558 	    return ret;
    559 	}
    560 	sp = krb5_storage_from_fd(fd);
    561 	krb5_storage_set_eof_code(sp, KRB5_KT_END);
    562 	ret = fkt_setup_keytab(context, id, sp);
    563 	if(ret) {
    564 	    goto out;
    565 	}
    566 	storage_set_flags(context, sp, id->version);
    567     } else {
    568 	int8_t pvno, tag;
    569 
    570 	rk_cloexec(fd);
    571 
    572 	ret = _krb5_xlock(context, fd, 1, d->filename);
    573 	if (ret) {
    574 	    close(fd);
    575 	    return ret;
    576 	}
    577 	sp = krb5_storage_from_fd(fd);
    578 	krb5_storage_set_eof_code(sp, KRB5_KT_END);
    579 	ret = krb5_ret_int8(sp, &pvno);
    580 	if(ret) {
    581 	    /* we probably have a zero byte file, so try to set it up
    582                properly */
    583 	    ret = fkt_setup_keytab(context, id, sp);
    584 	    if(ret) {
    585 		krb5_set_error_message(context, ret,
    586 				       N_("%s: keytab is corrupted: %s", ""),
    587 				       d->filename, strerror(ret));
    588 		goto out;
    589 	    }
    590 	    storage_set_flags(context, sp, id->version);
    591 	} else {
    592 	    if(pvno != 5) {
    593 		ret = KRB5_KEYTAB_BADVNO;
    594 		krb5_set_error_message(context, ret,
    595 				       N_("Bad version in keytab %s", ""),
    596 				       d->filename);
    597 		goto out;
    598 	    }
    599 	    ret = krb5_ret_int8 (sp, &tag);
    600 	    if (ret) {
    601 		krb5_set_error_message(context, ret,
    602 				       N_("failed reading tag from "
    603 					  "keytab %s", ""),
    604 				       d->filename);
    605 		goto out;
    606 	    }
    607 	    id->version = tag;
    608 	    storage_set_flags(context, sp, id->version);
    609 	}
    610     }
    611 
    612     {
    613 	krb5_storage *emem;
    614 	emem = krb5_storage_emem();
    615 	if(emem == NULL) {
    616 	    ret = krb5_enomem(context);
    617 	    goto out;
    618 	}
    619 	ret = krb5_kt_store_principal(context, emem, entry->principal);
    620 	if(ret) {
    621 	    krb5_set_error_message(context, ret,
    622 				   N_("Failed storing principal "
    623 				      "in keytab %s", ""),
    624 				   d->filename);
    625 	    krb5_storage_free(emem);
    626 	    goto out;
    627 	}
    628 	ret = krb5_store_int32 (emem, entry->timestamp);
    629 	if(ret) {
    630 	    krb5_set_error_message(context, ret,
    631 				   N_("Failed storing timpstamp "
    632 				      "in keytab %s", ""),
    633 				   d->filename);
    634 	    krb5_storage_free(emem);
    635 	    goto out;
    636 	}
    637 	ret = krb5_store_int8 (emem, entry->vno % 256);
    638 	if(ret) {
    639 	    krb5_set_error_message(context, ret,
    640 				   N_("Failed storing kvno "
    641 				      "in keytab %s", ""),
    642 				   d->filename);
    643 	    krb5_storage_free(emem);
    644 	    goto out;
    645 	}
    646 	ret = krb5_kt_store_keyblock (context, d, emem, &entry->keyblock);
    647 	if(ret) {
    648 	    krb5_storage_free(emem);
    649 	    goto out;
    650 	}
    651 	if ((d->flags & KRB5_KT_FL_JAVA) == 0) {
    652 	    ret = krb5_store_int32 (emem, entry->vno);
    653 	    if (ret) {
    654 		krb5_set_error_message(context, ret,
    655 				       N_("Failed storing extended kvno "
    656 					  "in keytab %s", ""),
    657 				       d->filename);
    658 		krb5_storage_free(emem);
    659 		goto out;
    660 	    }
    661 	    ret = krb5_store_uint32 (emem, entry->flags);
    662 	    if (ret) {
    663 		krb5_set_error_message(context, ret,
    664 				       N_("Failed storing extended kvno "
    665 					  "in keytab %s", ""),
    666 				       d->filename);
    667 		krb5_storage_free(emem);
    668 		goto out;
    669 	    }
    670 	}
    671 
    672 	ret = krb5_storage_to_data(emem, &keytab);
    673 	krb5_storage_free(emem);
    674 	if(ret) {
    675 	    krb5_set_error_message(context, ret,
    676 				   N_("Failed converting keytab entry "
    677 				      "to memory block for keytab %s", ""),
    678 				   d->filename);
    679 	    goto out;
    680 	}
    681     }
    682 
    683     while(1) {
    684 	ret = krb5_ret_int32(sp, &len);
    685 	if(ret == KRB5_KT_END) {
    686 	    len = keytab.length;
    687 	    break;
    688 	}
    689 	if(len < 0) {
    690 	    len = -len;
    691 	    if(len >= (int)keytab.length) {
    692 		krb5_storage_seek(sp, -4, SEEK_CUR);
    693 		break;
    694 	    }
    695 	}
    696 	krb5_storage_seek(sp, len, SEEK_CUR);
    697     }
    698     ret = krb5_store_int32(sp, len);
    699     if(krb5_storage_write(sp, keytab.data, keytab.length) < 0) {
    700 	ret = errno;
    701 	krb5_set_error_message(context, ret,
    702 			       N_("Failed writing keytab block "
    703 				  "in keytab %s: %s", ""),
    704 			       d->filename, strerror(ret));
    705     }
    706     memset(keytab.data, 0, keytab.length);
    707     krb5_data_free(&keytab);
    708   out:
    709     krb5_storage_free(sp);
    710     _krb5_xunlock(context, fd);
    711     close(fd);
    712     return ret;
    713 }
    714 
    715 static krb5_error_code KRB5_CALLCONV
    716 fkt_remove_entry(krb5_context context,
    717 		 krb5_keytab id,
    718 		 krb5_keytab_entry *entry)
    719 {
    720     krb5_keytab_entry e;
    721     krb5_kt_cursor cursor;
    722     off_t pos_start, pos_end;
    723     int found = 0;
    724     krb5_error_code ret;
    725 
    726     ret = fkt_start_seq_get_int(context, id, O_RDWR | O_BINARY | O_CLOEXEC, 1, &cursor);
    727     if(ret != 0)
    728 	goto out; /* return other error here? */
    729     while(fkt_next_entry_int(context, id, &e, &cursor,
    730 			     &pos_start, &pos_end) == 0) {
    731 	if(krb5_kt_compare(context, &e, entry->principal,
    732 			   entry->vno, entry->keyblock.keytype)) {
    733 	    int32_t len;
    734 	    unsigned char buf[128];
    735 	    found = 1;
    736 	    krb5_storage_seek(cursor.sp, pos_start, SEEK_SET);
    737 	    len = pos_end - pos_start - 4;
    738 	    krb5_store_int32(cursor.sp, -len);
    739 	    memset(buf, 0, sizeof(buf));
    740 	    while(len > 0) {
    741 		krb5_storage_write(cursor.sp, buf,
    742 		    min((size_t)len, sizeof(buf)));
    743 		len -= min((size_t)len, sizeof(buf));
    744 	    }
    745 	}
    746 	krb5_kt_free_entry(context, &e);
    747     }
    748     krb5_kt_end_seq_get(context, id, &cursor);
    749   out:
    750     if (!found) {
    751 	krb5_clear_error_message (context);
    752 	return KRB5_KT_NOTFOUND;
    753     }
    754     return 0;
    755 }
    756 
    757 const krb5_kt_ops krb5_fkt_ops = {
    758     "FILE",
    759     fkt_resolve,
    760     fkt_get_name,
    761     fkt_close,
    762     fkt_destroy,
    763     NULL, /* get */
    764     fkt_start_seq_get,
    765     fkt_next_entry,
    766     fkt_end_seq_get,
    767     fkt_add_entry,
    768     fkt_remove_entry,
    769     NULL,
    770     0
    771 };
    772 
    773 const krb5_kt_ops krb5_wrfkt_ops = {
    774     "WRFILE",
    775     fkt_resolve,
    776     fkt_get_name,
    777     fkt_close,
    778     fkt_destroy,
    779     NULL, /* get */
    780     fkt_start_seq_get,
    781     fkt_next_entry,
    782     fkt_end_seq_get,
    783     fkt_add_entry,
    784     fkt_remove_entry,
    785     NULL,
    786     0
    787 };
    788 
    789 const krb5_kt_ops krb5_javakt_ops = {
    790     "JAVA14",
    791     fkt_resolve_java14,
    792     fkt_get_name,
    793     fkt_close,
    794     fkt_destroy,
    795     NULL, /* get */
    796     fkt_start_seq_get,
    797     fkt_next_entry,
    798     fkt_end_seq_get,
    799     fkt_add_entry,
    800     fkt_remove_entry,
    801     NULL,
    802     0
    803 };
    804