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 H��gskolan
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
47struct fkt_data {
48    char *filename;
49    int flags;
50};
51
52static krb5_error_code
53krb5_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
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	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
92static krb5_error_code
93krb5_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
110static krb5_error_code
111krb5_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
128static krb5_error_code
129krb5_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
153static krb5_error_code
154krb5_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
177static krb5_error_code
178krb5_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;
249out:
250    krb5_free_principal(context, p);
251    return ret;
252}
253
254static krb5_error_code
255krb5_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
283static krb5_error_code KRB5_CALLCONV
284fkt_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
301static krb5_error_code KRB5_CALLCONV
302fkt_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
314static krb5_error_code KRB5_CALLCONV
315fkt_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
323static krb5_error_code KRB5_CALLCONV
324fkt_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
331static krb5_error_code KRB5_CALLCONV
332fkt_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
343static void
344storage_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
362static krb5_error_code
363fkt_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
422static krb5_error_code KRB5_CALLCONV
423fkt_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
430static krb5_error_code
431fkt_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);
447loop:
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
497static krb5_error_code KRB5_CALLCONV
498fkt_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
506static krb5_error_code KRB5_CALLCONV
507fkt_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
517static krb5_error_code KRB5_CALLCONV
518fkt_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
531static krb5_error_code KRB5_CALLCONV
532fkt_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
715static krb5_error_code KRB5_CALLCONV
716fkt_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
757const 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
773const 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
789const 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