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    if (ret)
525	krb5_kt_free_entry(context, entry);
526    krb5_storage_seek(cursor->sp, pos + 4 + len, SEEK_SET);
527    return ret;
528}
529
530static krb5_error_code KRB5_CALLCONV
531fkt_next_entry(krb5_context context,
532	       krb5_keytab id,
533	       krb5_keytab_entry *entry,
534	       krb5_kt_cursor *cursor)
535{
536    return fkt_next_entry_int(context, id, entry, cursor, NULL, NULL);
537}
538
539static krb5_error_code KRB5_CALLCONV
540fkt_end_seq_get(krb5_context context,
541		krb5_keytab id,
542		krb5_kt_cursor *cursor)
543{
544    krb5_storage_free(cursor->sp);
545    _krb5_xunlock(context, cursor->fd);
546    close(cursor->fd);
547    return 0;
548}
549
550static krb5_error_code KRB5_CALLCONV
551fkt_setup_keytab(krb5_context context,
552		 krb5_keytab id,
553		 krb5_storage *sp)
554{
555    krb5_error_code ret;
556    ret = krb5_store_int8(sp, 5);
557    if(ret)
558	return ret;
559    if(id->version == 0)
560	id->version = KRB5_KT_VNO;
561    return krb5_store_int8 (sp, id->version);
562}
563
564static krb5_error_code KRB5_CALLCONV
565fkt_add_entry(krb5_context context,
566	      krb5_keytab id,
567	      krb5_keytab_entry *entry)
568{
569    int ret;
570    int fd;
571    krb5_storage *sp;
572    struct fkt_data *d = id->data;
573    krb5_data keytab;
574    int32_t len;
575
576    fd = open (d->filename, O_RDWR | O_BINARY | O_CLOEXEC);
577    if (fd < 0) {
578	fd = open (d->filename, O_RDWR | O_CREAT | O_EXCL | O_BINARY | O_CLOEXEC, 0600);
579	if (fd < 0) {
580	    ret = errno;
581	    krb5_set_error_message(context, ret,
582				   N_("open(%s): %s", ""), d->filename,
583				   strerror(ret));
584	    return ret;
585	}
586	rk_cloexec(fd);
587
588	ret = _krb5_xlock(context, fd, 1, d->filename);
589	if (ret) {
590	    close(fd);
591	    return ret;
592	}
593	sp = krb5_storage_from_fd(fd);
594	krb5_storage_set_eof_code(sp, KRB5_KT_END);
595	ret = fkt_setup_keytab(context, id, sp);
596	if(ret) {
597	    goto out;
598	}
599	storage_set_flags(context, sp, id->version);
600    } else {
601	int8_t pvno, tag;
602
603	rk_cloexec(fd);
604
605	ret = _krb5_xlock(context, fd, 1, d->filename);
606	if (ret) {
607	    close(fd);
608	    return ret;
609	}
610	sp = krb5_storage_from_fd(fd);
611	if (sp == NULL) {
612	    ret = ENOMEM;
613	    krb5_set_error_message(context, ret, N_("out of memory", ""));
614	    goto out;
615	}
616	krb5_storage_set_eof_code(sp, KRB5_KT_END);
617	ret = krb5_ret_int8(sp, &pvno);
618	if(ret) {
619	    /* we probably have a zero byte file, so try to set it up
620               properly */
621	    ret = fkt_setup_keytab(context, id, sp);
622	    if(ret) {
623		krb5_set_error_message(context, ret,
624				       N_("%s: keytab is corrupted: %s", ""),
625				       d->filename, strerror(ret));
626		goto out;
627	    }
628	    storage_set_flags(context, sp, id->version);
629	} else {
630	    if(pvno != 5) {
631		ret = KRB5_KEYTAB_BADVNO;
632		krb5_set_error_message(context, ret,
633				       N_("Bad version in keytab %s", ""),
634				       d->filename);
635		goto out;
636	    }
637	    ret = krb5_ret_int8 (sp, &tag);
638	    if (ret) {
639		krb5_set_error_message(context, ret,
640				       N_("failed reading tag from "
641					  "keytab %s", ""),
642				       d->filename);
643		goto out;
644	    }
645	    id->version = tag;
646	    storage_set_flags(context, sp, id->version);
647	}
648    }
649
650    {
651	krb5_storage *emem;
652	emem = krb5_storage_emem();
653	if(emem == NULL) {
654	    ret = ENOMEM;
655	    krb5_set_error_message(context, ret,
656				   N_("malloc: out of memory", ""));
657	    goto out;
658	}
659	ret = krb5_kt_store_principal(context, emem, entry->principal);
660	if(ret) {
661	    krb5_set_error_message(context, ret,
662				   N_("Failed storing principal "
663				      "in keytab %s", ""),
664				   d->filename);
665	    krb5_storage_free(emem);
666	    goto out;
667	}
668	ret = krb5_store_int32 (emem, entry->timestamp);
669	if(ret) {
670	    krb5_set_error_message(context, ret,
671				   N_("Failed storing timpstamp "
672				      "in keytab %s", ""),
673				   d->filename);
674	    krb5_storage_free(emem);
675	    goto out;
676	}
677	ret = krb5_store_int8 (emem, entry->vno % 256);
678	if(ret) {
679	    krb5_set_error_message(context, ret,
680				   N_("Failed storing kvno "
681				      "in keytab %s", ""),
682				   d->filename);
683	    krb5_storage_free(emem);
684	    goto out;
685	}
686	ret = krb5_kt_store_keyblock (context, d, emem, &entry->keyblock);
687	if(ret) {
688	    krb5_storage_free(emem);
689	    goto out;
690	}
691	if ((d->flags & KRB5_KT_FL_JAVA) == 0) {
692	    ret = krb5_store_int32 (emem, entry->vno);
693	    if (ret) {
694		krb5_set_error_message(context, ret,
695				       N_("Failed storing extended kvno "
696					  "in keytab %s", ""),
697				       d->filename);
698		krb5_storage_free(emem);
699		goto out;
700	    }
701	    ret = krb5_store_uint32 (emem, entry->flags);
702	    if (ret) {
703		krb5_set_error_message(context, ret,
704				       N_("Failed storing extended kvno "
705					  "in keytab %s", ""),
706				       d->filename);
707		krb5_storage_free(emem);
708		goto out;
709	    }
710	}
711
712	ret = krb5_storage_to_data(emem, &keytab);
713	krb5_storage_free(emem);
714	if(ret) {
715	    krb5_set_error_message(context, ret,
716				   N_("Failed converting keytab entry "
717				      "to memory block for keytab %s", ""),
718				   d->filename);
719	    goto out;
720	}
721    }
722
723    while(1) {
724	ret = krb5_ret_int32(sp, &len);
725	if(ret == KRB5_KT_END) {
726	    len = (uint32_t)keytab.length;
727	    break;
728	}
729	if(len < 0) {
730	    len = -len;
731	    if(len >= (int32_t)keytab.length) {
732		krb5_storage_seek(sp, -4, SEEK_CUR);
733		break;
734	    }
735	}
736	krb5_storage_seek(sp, len, SEEK_CUR);
737    }
738    ret = krb5_store_int32(sp, len);
739    if(krb5_storage_write(sp, keytab.data, keytab.length) < 0) {
740	ret = errno;
741	krb5_set_error_message(context, ret,
742			       N_("Failed writing keytab block "
743				  "in keytab %s: %s", ""),
744			       d->filename, strerror(ret));
745    }
746    memset(keytab.data, 0, keytab.length);
747    krb5_data_free(&keytab);
748  out:
749    krb5_storage_free(sp);
750    _krb5_xunlock(context, fd);
751    close(fd);
752    return ret;
753}
754
755static krb5_error_code KRB5_CALLCONV
756fkt_remove_entry(krb5_context context,
757		 krb5_keytab id,
758		 krb5_keytab_entry *entry)
759{
760    krb5_keytab_entry e;
761    krb5_kt_cursor cursor;
762    off_t pos_start, pos_end;
763    int found = 0;
764    krb5_error_code ret;
765
766    ret = fkt_start_seq_get_int(context, id, O_RDWR | O_BINARY | O_CLOEXEC, 1, &cursor);
767    if(ret != 0)
768	goto out; /* return other error here? */
769    while(fkt_next_entry_int(context, id, &e, &cursor,
770			     &pos_start, &pos_end) == 0) {
771	if(krb5_kt_compare(context, &e, entry->principal,
772			   entry->vno, entry->keyblock.keytype)) {
773	    size_t len;
774	    unsigned char buf[128];
775	    found = 1;
776	    krb5_storage_seek(cursor.sp, pos_start, SEEK_SET);
777	    len = (size_t)(pos_end - pos_start - 4);
778	    krb5_store_int32(cursor.sp, -(int32_t)len);
779	    memset(buf, 0, sizeof(buf));
780	    while(len > 0) {
781		krb5_storage_write(cursor.sp, buf,
782		    min((size_t)len, sizeof(buf)));
783		len -= min(len, sizeof(buf));
784	    }
785	}
786	krb5_kt_free_entry(context, &e);
787    }
788    krb5_kt_end_seq_get(context, id, &cursor);
789  out:
790    if (!found) {
791	krb5_clear_error_message (context);
792	return KRB5_KT_NOTFOUND;
793    }
794    return 0;
795}
796
797const krb5_kt_ops krb5_fkt_ops = {
798    "FILE",
799    fkt_resolve,
800    fkt_get_name,
801    fkt_close,
802    fkt_destroy,
803    NULL, /* get */
804    fkt_start_seq_get,
805    fkt_next_entry,
806    fkt_end_seq_get,
807    fkt_add_entry,
808    fkt_remove_entry
809};
810
811const krb5_kt_ops krb5_wrfkt_ops = {
812    "WRFILE",
813    fkt_resolve,
814    fkt_get_name,
815    fkt_close,
816    fkt_destroy,
817    NULL, /* get */
818    fkt_start_seq_get,
819    fkt_next_entry,
820    fkt_end_seq_get,
821    fkt_add_entry,
822    fkt_remove_entry
823};
824
825const krb5_kt_ops krb5_javakt_ops = {
826    "JAVA14",
827    fkt_resolve_java14,
828    fkt_get_name,
829    fkt_close,
830    fkt_destroy,
831    NULL, /* get */
832    fkt_start_seq_get,
833    fkt_next_entry,
834    fkt_end_seq_get,
835    fkt_add_entry,
836    fkt_remove_entry
837};
838