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