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