Move kadmin and ktutil to /usr/bin.
[heimdal.git] / lib / krb5 / keytab_file.c
blob8b596790adf9dc49933d2f0e37a1afcbe64aad29
1 /*
2 * Copyright (c) 1997 - 2008 Kungliga Tekniska Högskolan
3 * (Royal Institute of Technology, Stockholm, Sweden).
4 * All rights reserved.
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
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.
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.
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.
34 #include "krb5_locl.h"
36 #define KRB5_KT_VNO_1 1
37 #define KRB5_KT_VNO_2 2
38 #define KRB5_KT_VNO KRB5_KT_VNO_2
40 #define KRB5_KT_FL_JAVA 1
43 /* file operations -------------------------------------------- */
45 struct fkt_data {
46 char *filename;
47 int flags;
50 static krb5_error_code
51 krb5_kt_ret_data(krb5_context context,
52 krb5_storage *sp,
53 krb5_data *data)
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 return krb5_enomem(context);
64 ret = krb5_storage_read(sp, data->data, size);
65 if(ret != size)
66 return (ret < 0)? errno : KRB5_KT_END;
67 return 0;
70 static krb5_error_code
71 krb5_kt_ret_string(krb5_context context,
72 krb5_storage *sp,
73 heim_general_string *data)
75 int ret;
76 int16_t size;
77 ret = krb5_ret_int16(sp, &size);
78 if(ret)
79 return ret;
80 *data = malloc(size + 1);
81 if (*data == NULL)
82 return krb5_enomem(context);
83 ret = krb5_storage_read(sp, *data, size);
84 (*data)[size] = '\0';
85 if(ret != size)
86 return (ret < 0)? errno : KRB5_KT_END;
87 return 0;
90 static krb5_error_code
91 krb5_kt_store_data(krb5_context context,
92 krb5_storage *sp,
93 krb5_data data)
95 int ret;
96 ret = krb5_store_int16(sp, data.length);
97 if(ret < 0)
98 return ret;
99 ret = krb5_storage_write(sp, data.data, data.length);
100 if(ret != (int)data.length){
101 if(ret < 0)
102 return errno;
103 return KRB5_KT_END;
105 return 0;
108 static krb5_error_code
109 krb5_kt_store_string(krb5_storage *sp,
110 heim_general_string data)
112 int ret;
113 size_t len = strlen(data);
114 ret = krb5_store_int16(sp, len);
115 if(ret < 0)
116 return ret;
117 ret = krb5_storage_write(sp, data, len);
118 if(ret != (int)len){
119 if(ret < 0)
120 return errno;
121 return KRB5_KT_END;
123 return 0;
126 static krb5_error_code
127 krb5_kt_ret_keyblock(krb5_context context,
128 struct fkt_data *fkt,
129 krb5_storage *sp,
130 krb5_keyblock *p)
132 int ret;
133 int16_t tmp;
135 ret = krb5_ret_int16(sp, &tmp); /* keytype + etype */
136 if(ret) {
137 krb5_set_error_message(context, ret,
138 N_("Cant read keyblock from file %s", ""),
139 fkt->filename);
140 return ret;
142 p->keytype = tmp;
143 ret = krb5_kt_ret_data(context, sp, &p->keyvalue);
144 if (ret)
145 krb5_set_error_message(context, ret,
146 N_("Cant read keyblock from file %s", ""),
147 fkt->filename);
148 return ret;
151 static krb5_error_code
152 krb5_kt_store_keyblock(krb5_context context,
153 struct fkt_data *fkt,
154 krb5_storage *sp,
155 krb5_keyblock *p)
157 int ret;
159 ret = krb5_store_int16(sp, p->keytype); /* keytype + etype */
160 if(ret) {
161 krb5_set_error_message(context, ret,
162 N_("Cant store keyblock to file %s", ""),
163 fkt->filename);
164 return ret;
166 ret = krb5_kt_store_data(context, sp, p->keyvalue);
167 if (ret)
168 krb5_set_error_message(context, ret,
169 N_("Cant store keyblock to file %s", ""),
170 fkt->filename);
171 return ret;
175 static krb5_error_code
176 krb5_kt_ret_principal(krb5_context context,
177 struct fkt_data *fkt,
178 krb5_storage *sp,
179 krb5_principal *princ)
181 size_t i;
182 int ret;
183 krb5_principal p;
184 int16_t len;
186 ALLOC(p, 1);
187 if(p == NULL)
188 return krb5_enomem(context);
190 ret = krb5_ret_int16(sp, &len);
191 if(ret) {
192 krb5_set_error_message(context, ret,
193 N_("Failed decoding length of "
194 "keytab principal in keytab file %s", ""),
195 fkt->filename);
196 goto out;
198 if(krb5_storage_is_flags(sp, KRB5_STORAGE_PRINCIPAL_WRONG_NUM_COMPONENTS))
199 len--;
200 if (len < 0) {
201 ret = KRB5_KT_END;
202 krb5_set_error_message(context, ret,
203 N_("Keytab principal contains "
204 "invalid length in keytab %s", ""),
205 fkt->filename);
206 goto out;
208 ret = krb5_kt_ret_string(context, sp, &p->realm);
209 if(ret) {
210 krb5_set_error_message(context, ret,
211 N_("Can't read realm from keytab: %s", ""),
212 fkt->filename);
213 goto out;
215 p->name.name_string.val = calloc(len, sizeof(*p->name.name_string.val));
216 if(p->name.name_string.val == NULL) {
217 ret = krb5_enomem(context);
218 goto out;
220 p->name.name_string.len = len;
221 for(i = 0; i < p->name.name_string.len; i++){
222 ret = krb5_kt_ret_string(context, sp, p->name.name_string.val + i);
223 if(ret) {
224 krb5_set_error_message(context, ret,
225 N_("Can't read principal from "
226 "keytab: %s", ""),
227 fkt->filename);
228 goto out;
231 if (krb5_storage_is_flags(sp, KRB5_STORAGE_PRINCIPAL_NO_NAME_TYPE))
232 p->name.name_type = KRB5_NT_UNKNOWN;
233 else {
234 int32_t tmp32;
235 ret = krb5_ret_int32(sp, &tmp32);
236 p->name.name_type = tmp32;
237 if (ret) {
238 krb5_set_error_message(context, ret,
239 N_("Can't read name-type from "
240 "keytab: %s", ""),
241 fkt->filename);
242 goto out;
245 *princ = p;
246 return 0;
247 out:
248 krb5_free_principal(context, p);
249 return ret;
252 static krb5_error_code
253 krb5_kt_store_principal(krb5_context context,
254 krb5_storage *sp,
255 krb5_principal p)
257 size_t i;
258 int ret;
260 if(krb5_storage_is_flags(sp, KRB5_STORAGE_PRINCIPAL_WRONG_NUM_COMPONENTS))
261 ret = krb5_store_int16(sp, p->name.name_string.len + 1);
262 else
263 ret = krb5_store_int16(sp, p->name.name_string.len);
264 if(ret) return ret;
265 ret = krb5_kt_store_string(sp, p->realm);
266 if(ret) return ret;
267 for(i = 0; i < p->name.name_string.len; i++){
268 ret = krb5_kt_store_string(sp, p->name.name_string.val[i]);
269 if(ret)
270 return ret;
272 if(!krb5_storage_is_flags(sp, KRB5_STORAGE_PRINCIPAL_NO_NAME_TYPE)) {
273 ret = krb5_store_int32(sp, p->name.name_type);
274 if(ret)
275 return ret;
278 return 0;
281 static krb5_error_code KRB5_CALLCONV
282 fkt_resolve(krb5_context context, const char *name, krb5_keytab id)
284 struct fkt_data *d;
286 d = malloc(sizeof(*d));
287 if(d == NULL)
288 return krb5_enomem(context);
289 d->filename = strdup(name);
290 if(d->filename == NULL) {
291 free(d);
292 return krb5_enomem(context);
294 d->flags = 0;
295 id->data = d;
296 return 0;
299 static krb5_error_code KRB5_CALLCONV
300 fkt_resolve_java14(krb5_context context, const char *name, krb5_keytab id)
302 krb5_error_code ret;
304 ret = fkt_resolve(context, name, id);
305 if (ret == 0) {
306 struct fkt_data *d = id->data;
307 d->flags |= KRB5_KT_FL_JAVA;
309 return ret;
312 static krb5_error_code KRB5_CALLCONV
313 fkt_close(krb5_context context, krb5_keytab id)
315 struct fkt_data *d = id->data;
316 free(d->filename);
317 free(d);
318 return 0;
321 static krb5_error_code KRB5_CALLCONV
322 fkt_destroy(krb5_context context, krb5_keytab id)
324 struct fkt_data *d = id->data;
325 _krb5_erase_file(context, d->filename);
326 return 0;
329 static krb5_error_code KRB5_CALLCONV
330 fkt_get_name(krb5_context context,
331 krb5_keytab id,
332 char *name,
333 size_t namesize)
335 /* This function is XXX */
336 struct fkt_data *d = id->data;
337 strlcpy(name, d->filename, namesize);
338 return 0;
341 static void
342 storage_set_flags(krb5_context context, krb5_storage *sp, int vno)
344 int flags = 0;
345 switch(vno) {
346 case KRB5_KT_VNO_1:
347 flags |= KRB5_STORAGE_PRINCIPAL_WRONG_NUM_COMPONENTS;
348 flags |= KRB5_STORAGE_PRINCIPAL_NO_NAME_TYPE;
349 flags |= KRB5_STORAGE_HOST_BYTEORDER;
350 break;
351 case KRB5_KT_VNO_2:
352 break;
353 default:
354 krb5_warnx(context,
355 "storage_set_flags called with bad vno (%d)", vno);
357 krb5_storage_set_flags(sp, flags);
360 static krb5_error_code
361 fkt_start_seq_get_int(krb5_context context,
362 krb5_keytab id,
363 int flags,
364 int exclusive,
365 krb5_kt_cursor *c)
367 int8_t pvno, tag;
368 krb5_error_code ret;
369 struct fkt_data *d = id->data;
371 c->fd = open (d->filename, flags);
372 if (c->fd < 0) {
373 ret = errno;
374 krb5_set_error_message(context, ret,
375 N_("keytab %s open failed: %s", ""),
376 d->filename, strerror(ret));
377 return ret;
379 rk_cloexec(c->fd);
380 ret = _krb5_xlock(context, c->fd, exclusive, d->filename);
381 if (ret) {
382 close(c->fd);
383 return ret;
385 c->sp = krb5_storage_from_fd(c->fd);
386 if (c->sp == NULL) {
387 _krb5_xunlock(context, c->fd);
388 close(c->fd);
389 return krb5_enomem(context);
391 krb5_storage_set_eof_code(c->sp, KRB5_KT_END);
392 ret = krb5_ret_int8(c->sp, &pvno);
393 if(ret) {
394 krb5_storage_free(c->sp);
395 _krb5_xunlock(context, c->fd);
396 close(c->fd);
397 krb5_clear_error_message(context);
398 return ret;
400 if(pvno != 5) {
401 krb5_storage_free(c->sp);
402 _krb5_xunlock(context, c->fd);
403 close(c->fd);
404 krb5_clear_error_message (context);
405 return KRB5_KEYTAB_BADVNO;
407 ret = krb5_ret_int8(c->sp, &tag);
408 if (ret) {
409 krb5_storage_free(c->sp);
410 _krb5_xunlock(context, c->fd);
411 close(c->fd);
412 krb5_clear_error_message(context);
413 return ret;
415 id->version = tag;
416 storage_set_flags(context, c->sp, id->version);
417 return 0;
420 static krb5_error_code KRB5_CALLCONV
421 fkt_start_seq_get(krb5_context context,
422 krb5_keytab id,
423 krb5_kt_cursor *c)
425 return fkt_start_seq_get_int(context, id, O_RDONLY | O_BINARY | O_CLOEXEC, 0, c);
428 static krb5_error_code
429 fkt_next_entry_int(krb5_context context,
430 krb5_keytab id,
431 krb5_keytab_entry *entry,
432 krb5_kt_cursor *cursor,
433 off_t *start,
434 off_t *end)
436 struct fkt_data *d = id->data;
437 int32_t len;
438 int ret;
439 int8_t tmp8;
440 int32_t tmp32;
441 uint32_t utmp32;
442 off_t pos, curpos;
444 pos = krb5_storage_seek(cursor->sp, 0, SEEK_CUR);
445 loop:
446 ret = krb5_ret_int32(cursor->sp, &len);
447 if (ret)
448 return ret;
449 if(len < 0) {
450 pos = krb5_storage_seek(cursor->sp, -len, SEEK_CUR);
451 goto loop;
453 ret = krb5_kt_ret_principal (context, d, cursor->sp, &entry->principal);
454 if (ret)
455 goto out;
456 ret = krb5_ret_uint32(cursor->sp, &utmp32);
457 entry->timestamp = utmp32;
458 if (ret)
459 goto out;
460 ret = krb5_ret_int8(cursor->sp, &tmp8);
461 if (ret)
462 goto out;
463 entry->vno = tmp8;
464 ret = krb5_kt_ret_keyblock (context, d, cursor->sp, &entry->keyblock);
465 if (ret)
466 goto out;
467 /* there might be a 32 bit kvno here
468 * if it's zero, assume that the 8bit one was right,
469 * otherwise trust the new value */
470 curpos = krb5_storage_seek(cursor->sp, 0, SEEK_CUR);
471 if(len + 4 + pos - curpos >= 4) {
472 ret = krb5_ret_int32(cursor->sp, &tmp32);
473 if (ret == 0 && tmp32 != 0)
474 entry->vno = tmp32;
476 /* there might be a flags field here */
477 if(len + 4 + pos - curpos >= 8) {
478 ret = krb5_ret_uint32(cursor->sp, &utmp32);
479 if (ret == 0)
480 entry->flags = utmp32;
481 } else
482 entry->flags = 0;
484 entry->aliases = NULL;
486 if(start) *start = pos;
487 if(end) *end = pos + 4 + len;
488 out:
489 krb5_storage_seek(cursor->sp, pos + 4 + len, SEEK_SET);
490 return ret;
493 static krb5_error_code KRB5_CALLCONV
494 fkt_next_entry(krb5_context context,
495 krb5_keytab id,
496 krb5_keytab_entry *entry,
497 krb5_kt_cursor *cursor)
499 return fkt_next_entry_int(context, id, entry, cursor, NULL, NULL);
502 static krb5_error_code KRB5_CALLCONV
503 fkt_end_seq_get(krb5_context context,
504 krb5_keytab id,
505 krb5_kt_cursor *cursor)
507 krb5_storage_free(cursor->sp);
508 _krb5_xunlock(context, cursor->fd);
509 close(cursor->fd);
510 return 0;
513 static krb5_error_code KRB5_CALLCONV
514 fkt_setup_keytab(krb5_context context,
515 krb5_keytab id,
516 krb5_storage *sp)
518 krb5_error_code ret;
519 ret = krb5_store_int8(sp, 5);
520 if(ret)
521 return ret;
522 if(id->version == 0)
523 id->version = KRB5_KT_VNO;
524 return krb5_store_int8 (sp, id->version);
527 static krb5_error_code KRB5_CALLCONV
528 fkt_add_entry(krb5_context context,
529 krb5_keytab id,
530 krb5_keytab_entry *entry)
532 int ret;
533 int fd;
534 krb5_storage *sp;
535 struct fkt_data *d = id->data;
536 krb5_data keytab;
537 int32_t len;
539 fd = open (d->filename, O_RDWR | O_BINARY | O_CLOEXEC);
540 if (fd < 0) {
541 fd = open (d->filename, O_RDWR | O_CREAT | O_EXCL | O_BINARY | O_CLOEXEC, 0600);
542 if (fd < 0) {
543 ret = errno;
544 krb5_set_error_message(context, ret,
545 N_("open(%s): %s", ""), d->filename,
546 strerror(ret));
547 return ret;
549 rk_cloexec(fd);
551 ret = _krb5_xlock(context, fd, 1, d->filename);
552 if (ret) {
553 close(fd);
554 return ret;
556 sp = krb5_storage_from_fd(fd);
557 krb5_storage_set_eof_code(sp, KRB5_KT_END);
558 ret = fkt_setup_keytab(context, id, sp);
559 if(ret) {
560 goto out;
562 storage_set_flags(context, sp, id->version);
563 } else {
564 int8_t pvno, tag;
566 rk_cloexec(fd);
568 ret = _krb5_xlock(context, fd, 1, d->filename);
569 if (ret) {
570 close(fd);
571 return ret;
573 sp = krb5_storage_from_fd(fd);
574 krb5_storage_set_eof_code(sp, KRB5_KT_END);
575 ret = krb5_ret_int8(sp, &pvno);
576 if(ret) {
577 /* we probably have a zero byte file, so try to set it up
578 properly */
579 ret = fkt_setup_keytab(context, id, sp);
580 if(ret) {
581 krb5_set_error_message(context, ret,
582 N_("%s: keytab is corrupted: %s", ""),
583 d->filename, strerror(ret));
584 goto out;
586 storage_set_flags(context, sp, id->version);
587 } else {
588 if(pvno != 5) {
589 ret = KRB5_KEYTAB_BADVNO;
590 krb5_set_error_message(context, ret,
591 N_("Bad version in keytab %s", ""),
592 d->filename);
593 goto out;
595 ret = krb5_ret_int8 (sp, &tag);
596 if (ret) {
597 krb5_set_error_message(context, ret,
598 N_("failed reading tag from "
599 "keytab %s", ""),
600 d->filename);
601 goto out;
603 id->version = tag;
604 storage_set_flags(context, sp, id->version);
609 krb5_storage *emem;
610 emem = krb5_storage_emem();
611 if(emem == NULL) {
612 ret = krb5_enomem(context);
613 goto out;
615 ret = krb5_kt_store_principal(context, emem, entry->principal);
616 if(ret) {
617 krb5_set_error_message(context, ret,
618 N_("Failed storing principal "
619 "in keytab %s", ""),
620 d->filename);
621 krb5_storage_free(emem);
622 goto out;
624 ret = krb5_store_int32 (emem, entry->timestamp);
625 if(ret) {
626 krb5_set_error_message(context, ret,
627 N_("Failed storing timpstamp "
628 "in keytab %s", ""),
629 d->filename);
630 krb5_storage_free(emem);
631 goto out;
633 ret = krb5_store_int8 (emem, entry->vno % 256);
634 if(ret) {
635 krb5_set_error_message(context, ret,
636 N_("Failed storing kvno "
637 "in keytab %s", ""),
638 d->filename);
639 krb5_storage_free(emem);
640 goto out;
642 ret = krb5_kt_store_keyblock (context, d, emem, &entry->keyblock);
643 if(ret) {
644 krb5_storage_free(emem);
645 goto out;
647 if ((d->flags & KRB5_KT_FL_JAVA) == 0) {
648 ret = krb5_store_int32 (emem, entry->vno);
649 if (ret) {
650 krb5_set_error_message(context, ret,
651 N_("Failed storing extended kvno "
652 "in keytab %s", ""),
653 d->filename);
654 krb5_storage_free(emem);
655 goto out;
657 ret = krb5_store_uint32 (emem, entry->flags);
658 if (ret) {
659 krb5_set_error_message(context, ret,
660 N_("Failed storing extended kvno "
661 "in keytab %s", ""),
662 d->filename);
663 krb5_storage_free(emem);
664 goto out;
668 ret = krb5_storage_to_data(emem, &keytab);
669 krb5_storage_free(emem);
670 if(ret) {
671 krb5_set_error_message(context, ret,
672 N_("Failed converting keytab entry "
673 "to memory block for keytab %s", ""),
674 d->filename);
675 goto out;
679 while(1) {
680 ret = krb5_ret_int32(sp, &len);
681 if(ret == KRB5_KT_END) {
682 len = keytab.length;
683 break;
685 if(len < 0) {
686 len = -len;
687 if(len >= (int)keytab.length) {
688 krb5_storage_seek(sp, -4, SEEK_CUR);
689 break;
692 krb5_storage_seek(sp, len, SEEK_CUR);
694 ret = krb5_store_int32(sp, len);
695 if(krb5_storage_write(sp, keytab.data, keytab.length) < 0) {
696 ret = errno;
697 krb5_set_error_message(context, ret,
698 N_("Failed writing keytab block "
699 "in keytab %s: %s", ""),
700 d->filename, strerror(ret));
702 memset(keytab.data, 0, keytab.length);
703 krb5_data_free(&keytab);
704 out:
705 krb5_storage_free(sp);
706 _krb5_xunlock(context, fd);
707 close(fd);
708 return ret;
711 static krb5_error_code KRB5_CALLCONV
712 fkt_remove_entry(krb5_context context,
713 krb5_keytab id,
714 krb5_keytab_entry *entry)
716 krb5_keytab_entry e;
717 krb5_kt_cursor cursor;
718 off_t pos_start, pos_end;
719 int found = 0;
720 krb5_error_code ret;
722 ret = fkt_start_seq_get_int(context, id, O_RDWR | O_BINARY | O_CLOEXEC, 1, &cursor);
723 if(ret != 0)
724 goto out; /* return other error here? */
725 while(fkt_next_entry_int(context, id, &e, &cursor,
726 &pos_start, &pos_end) == 0) {
727 if(krb5_kt_compare(context, &e, entry->principal,
728 entry->vno, entry->keyblock.keytype)) {
729 int32_t len;
730 unsigned char buf[128];
731 found = 1;
732 krb5_storage_seek(cursor.sp, pos_start, SEEK_SET);
733 len = pos_end - pos_start - 4;
734 krb5_store_int32(cursor.sp, -len);
735 memset(buf, 0, sizeof(buf));
736 while(len > 0) {
737 krb5_storage_write(cursor.sp, buf,
738 min((size_t)len, sizeof(buf)));
739 len -= min((size_t)len, sizeof(buf));
742 krb5_kt_free_entry(context, &e);
744 krb5_kt_end_seq_get(context, id, &cursor);
745 out:
746 if (!found) {
747 krb5_clear_error_message (context);
748 return KRB5_KT_NOTFOUND;
750 return 0;
753 const krb5_kt_ops krb5_fkt_ops = {
754 "FILE",
755 fkt_resolve,
756 fkt_get_name,
757 fkt_close,
758 fkt_destroy,
759 NULL, /* get */
760 fkt_start_seq_get,
761 fkt_next_entry,
762 fkt_end_seq_get,
763 fkt_add_entry,
764 fkt_remove_entry,
765 NULL,
769 const krb5_kt_ops krb5_wrfkt_ops = {
770 "WRFILE",
771 fkt_resolve,
772 fkt_get_name,
773 fkt_close,
774 fkt_destroy,
775 NULL, /* get */
776 fkt_start_seq_get,
777 fkt_next_entry,
778 fkt_end_seq_get,
779 fkt_add_entry,
780 fkt_remove_entry,
781 NULL,
785 const krb5_kt_ops krb5_javakt_ops = {
786 "JAVA14",
787 fkt_resolve_java14,
788 fkt_get_name,
789 fkt_close,
790 fkt_destroy,
791 NULL, /* get */
792 fkt_start_seq_get,
793 fkt_next_entry,
794 fkt_end_seq_get,
795 fkt_add_entry,
796 fkt_remove_entry,
797 NULL,