2 * Copyright (c) 1997 - 2017 Kungliga Tekniska Högskolan
3 * (Royal Institute of Technology, Stockholm, Sweden).
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
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
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 -------------------------------------------- */
50 static krb5_error_code
51 krb5_kt_ret_data(krb5_context context
,
59 ret
= krb5_ret_int16(sp
, &size
);
63 data
->data
= malloc(size
);
64 if (data
->data
== NULL
)
65 return krb5_enomem(context
);
66 bytes
= krb5_storage_read(sp
, data
->data
, size
);
68 return (bytes
== -1) ? errno
: KRB5_KT_END
;
72 static krb5_error_code
73 krb5_kt_ret_string(krb5_context context
,
75 heim_general_string
*data
)
81 ret
= krb5_ret_int16(sp
, &size
);
84 *data
= malloc(size
+ 1);
86 return krb5_enomem(context
);
87 bytes
= krb5_storage_read(sp
, *data
, size
);
90 return (bytes
== -1) ? errno
: KRB5_KT_END
;
94 static krb5_error_code
95 krb5_kt_store_data(krb5_context context
,
102 ret
= krb5_store_int16(sp
, data
.length
);
105 bytes
= krb5_storage_write(sp
, data
.data
, data
.length
);
106 if (bytes
!= (int)data
.length
)
107 return bytes
== -1 ? errno
: KRB5_KT_END
;
111 static krb5_error_code
112 krb5_kt_store_string(krb5_storage
*sp
,
113 heim_general_string data
)
117 size_t len
= strlen(data
);
119 ret
= krb5_store_int16(sp
, len
);
122 bytes
= krb5_storage_write(sp
, data
, len
);
123 if (bytes
!= (int)len
)
124 return bytes
== -1 ? errno
: KRB5_KT_END
;
128 static krb5_error_code
129 krb5_kt_ret_keyblock(krb5_context context
,
130 struct fkt_data
*fkt
,
137 ret
= krb5_ret_int16(sp
, &tmp
); /* keytype + etype */
139 krb5_set_error_message(context
, ret
,
140 N_("Cant read keyblock from file %s", ""),
145 ret
= krb5_kt_ret_data(context
, sp
, &p
->keyvalue
);
147 krb5_set_error_message(context
, ret
,
148 N_("Cant read keyblock from file %s", ""),
153 static krb5_error_code
154 krb5_kt_store_keyblock(krb5_context context
,
155 struct fkt_data
*fkt
,
161 ret
= krb5_store_int16(sp
, p
->keytype
); /* keytype + etype */
163 krb5_set_error_message(context
, ret
,
164 N_("Cant store keyblock to file %s", ""),
168 ret
= krb5_kt_store_data(context
, sp
, p
->keyvalue
);
170 krb5_set_error_message(context
, ret
,
171 N_("Cant store keyblock to file %s", ""),
177 static krb5_error_code
178 krb5_kt_ret_principal(krb5_context context
,
179 struct fkt_data
*fkt
,
181 krb5_principal
*princ
)
190 return krb5_enomem(context
);
192 ret
= krb5_ret_int16(sp
, &len
);
194 krb5_set_error_message(context
, ret
,
195 N_("Failed decoding length of "
196 "keytab principal in keytab file %s", ""),
200 if(krb5_storage_is_flags(sp
, KRB5_STORAGE_PRINCIPAL_WRONG_NUM_COMPONENTS
))
204 krb5_set_error_message(context
, ret
,
205 N_("Keytab principal contains "
206 "invalid length in keytab %s", ""),
210 ret
= krb5_kt_ret_string(context
, sp
, &p
->realm
);
212 krb5_set_error_message(context
, ret
,
213 N_("Can't read realm from keytab: %s", ""),
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
);
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
);
226 krb5_set_error_message(context
, ret
,
227 N_("Can't read principal from "
233 if (krb5_storage_is_flags(sp
, KRB5_STORAGE_PRINCIPAL_NO_NAME_TYPE
))
234 p
->name
.name_type
= KRB5_NT_UNKNOWN
;
237 ret
= krb5_ret_int32(sp
, &tmp32
);
238 p
->name
.name_type
= tmp32
;
240 krb5_set_error_message(context
, ret
,
241 N_("Can't read name-type from "
250 krb5_free_principal(context
, p
);
254 static krb5_error_code
255 krb5_kt_store_principal(krb5_context context
,
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);
265 ret
= krb5_store_int16(sp
, p
->name
.name_string
.len
);
267 ret
= krb5_kt_store_string(sp
, p
->realm
);
269 for(i
= 0; i
< p
->name
.name_string
.len
; i
++){
270 ret
= krb5_kt_store_string(sp
, p
->name
.name_string
.val
[i
]);
274 if(!krb5_storage_is_flags(sp
, KRB5_STORAGE_PRINCIPAL_NO_NAME_TYPE
)) {
275 ret
= krb5_store_int32(sp
, p
->name
.name_type
);
283 static krb5_error_code KRB5_CALLCONV
284 fkt_resolve(krb5_context context
, const char *name
, krb5_keytab id
)
288 d
= malloc(sizeof(*d
));
290 return krb5_enomem(context
);
291 d
->filename
= strdup(name
);
292 if(d
->filename
== NULL
) {
294 return krb5_enomem(context
);
301 static krb5_error_code KRB5_CALLCONV
302 fkt_resolve_java14(krb5_context context
, const char *name
, krb5_keytab id
)
306 ret
= fkt_resolve(context
, name
, id
);
308 struct fkt_data
*d
= id
->data
;
309 d
->flags
|= KRB5_KT_FL_JAVA
;
314 static krb5_error_code KRB5_CALLCONV
315 fkt_close(krb5_context context
, krb5_keytab id
)
317 struct fkt_data
*d
= id
->data
;
323 static krb5_error_code KRB5_CALLCONV
324 fkt_destroy(krb5_context context
, krb5_keytab id
)
326 struct fkt_data
*d
= id
->data
;
327 _krb5_erase_file(context
, d
->filename
);
331 static krb5_error_code KRB5_CALLCONV
332 fkt_get_name(krb5_context context
,
337 /* This function is XXX */
338 struct fkt_data
*d
= id
->data
;
339 strlcpy(name
, d
->filename
, namesize
);
344 storage_set_flags(krb5_context context
, krb5_storage
*sp
, int vno
)
349 flags
|= KRB5_STORAGE_PRINCIPAL_WRONG_NUM_COMPONENTS
;
350 flags
|= KRB5_STORAGE_PRINCIPAL_NO_NAME_TYPE
;
351 flags
|= KRB5_STORAGE_HOST_BYTEORDER
;
357 "storage_set_flags called with bad vno (%d)", vno
);
359 krb5_storage_set_flags(sp
, flags
);
362 static krb5_error_code
363 fkt_start_seq_get_int(krb5_context context
,
371 struct fkt_data
*d
= id
->data
;
372 const char *stdio_mode
= "rb";
374 memset(c
, 0, sizeof(*c
));
375 c
->fd
= open (d
->filename
, flags
);
378 krb5_set_error_message(context
, ret
,
379 N_("keytab %s open failed: %s", ""),
380 d
->filename
, strerror(ret
));
384 ret
= _krb5_xlock(context
, c
->fd
, exclusive
, d
->filename
);
389 if ((flags
& O_ACCMODE
) == O_RDWR
&& (flags
& O_APPEND
))
391 else if ((flags
& O_ACCMODE
) == O_RDWR
)
393 else if ((flags
& O_ACCMODE
) == O_WRONLY
)
395 c
->sp
= krb5_storage_stdio_from_fd(c
->fd
, stdio_mode
);
398 return krb5_enomem(context
);
400 krb5_storage_set_eof_code(c
->sp
, KRB5_KT_END
);
401 ret
= krb5_ret_int8(c
->sp
, &pvno
);
403 krb5_storage_free(c
->sp
);
405 krb5_clear_error_message(context
);
409 krb5_storage_free(c
->sp
);
411 krb5_clear_error_message (context
);
412 return KRB5_KEYTAB_BADVNO
;
414 ret
= krb5_ret_int8(c
->sp
, &tag
);
416 krb5_storage_free(c
->sp
);
418 krb5_clear_error_message(context
);
422 storage_set_flags(context
, c
->sp
, id
->version
);
426 static krb5_error_code KRB5_CALLCONV
427 fkt_start_seq_get(krb5_context context
,
431 return fkt_start_seq_get_int(context
, id
, O_RDONLY
| O_BINARY
| O_CLOEXEC
, 0, c
);
434 static krb5_error_code
435 fkt_next_entry_int(krb5_context context
,
437 krb5_keytab_entry
*entry
,
438 krb5_kt_cursor
*cursor
,
442 struct fkt_data
*d
= id
->data
;
450 pos
= krb5_storage_seek(cursor
->sp
, 0, SEEK_CUR
);
452 ret
= krb5_ret_int32(cursor
->sp
, &len
);
456 pos
= krb5_storage_seek(cursor
->sp
, -len
, SEEK_CUR
);
459 ret
= krb5_kt_ret_principal (context
, d
, cursor
->sp
, &entry
->principal
);
462 ret
= krb5_ret_uint32(cursor
->sp
, &utmp32
);
463 entry
->timestamp
= utmp32
;
466 ret
= krb5_ret_int8(cursor
->sp
, &tmp8
);
470 ret
= krb5_kt_ret_keyblock (context
, d
, cursor
->sp
, &entry
->keyblock
);
473 /* there might be a 32 bit kvno here
474 * if it's zero, assume that the 8bit one was right,
475 * otherwise trust the new value */
476 curpos
= krb5_storage_seek(cursor
->sp
, 0, SEEK_CUR
);
477 if(len
+ 4 + pos
- curpos
>= 4) {
478 ret
= krb5_ret_int32(cursor
->sp
, &tmp32
);
479 if (ret
== 0 && tmp32
!= 0)
482 /* there might be a flags field here */
483 if(len
+ 4 + pos
- curpos
>= 8) {
484 ret
= krb5_ret_uint32(cursor
->sp
, &utmp32
);
486 entry
->flags
= utmp32
;
490 entry
->aliases
= NULL
;
492 if(start
) *start
= pos
;
493 if(end
) *end
= pos
+ 4 + len
;
496 krb5_kt_free_entry(context
, entry
);
497 krb5_storage_seek(cursor
->sp
, pos
+ 4 + len
, SEEK_SET
);
501 static krb5_error_code KRB5_CALLCONV
502 fkt_next_entry(krb5_context context
,
504 krb5_keytab_entry
*entry
,
505 krb5_kt_cursor
*cursor
)
507 return fkt_next_entry_int(context
, id
, entry
, cursor
, NULL
, NULL
);
510 static krb5_error_code KRB5_CALLCONV
511 fkt_end_seq_get(krb5_context context
,
513 krb5_kt_cursor
*cursor
)
515 krb5_storage_free(cursor
->sp
);
520 static krb5_error_code KRB5_CALLCONV
521 fkt_setup_keytab(krb5_context context
,
526 ret
= krb5_store_int8(sp
, 5);
530 id
->version
= KRB5_KT_VNO
;
531 return krb5_store_int8 (sp
, id
->version
);
534 static krb5_error_code KRB5_CALLCONV
535 fkt_add_entry(krb5_context context
,
537 krb5_keytab_entry
*entry
)
543 struct fkt_data
*d
= id
->data
;
547 fd
= open(d
->filename
, O_RDWR
| O_BINARY
| O_CLOEXEC
);
549 fd
= open(d
->filename
, O_RDWR
| O_CREAT
| O_EXCL
| O_BINARY
| O_CLOEXEC
, 0600);
552 krb5_set_error_message(context
, ret
,
553 N_("open(%s): %s", ""), d
->filename
,
559 ret
= _krb5_xlock(context
, fd
, 1, d
->filename
);
564 sp
= krb5_storage_stdio_from_fd(fd
, "wb+");
567 return krb5_enomem(context
);
569 krb5_storage_set_eof_code(sp
, KRB5_KT_END
);
570 ret
= fkt_setup_keytab(context
, id
, sp
);
574 storage_set_flags(context
, sp
, id
->version
);
580 ret
= _krb5_xlock(context
, fd
, 1, d
->filename
);
585 sp
= krb5_storage_stdio_from_fd(fd
, "wb+");
590 krb5_storage_set_eof_code(sp
, KRB5_KT_END
);
591 ret
= krb5_ret_int8(sp
, &pvno
);
593 /* we probably have a zero byte file, so try to set it up
595 ret
= fkt_setup_keytab(context
, id
, sp
);
597 krb5_set_error_message(context
, ret
,
598 N_("%s: keytab is corrupted: %s", ""),
599 d
->filename
, strerror(ret
));
602 storage_set_flags(context
, sp
, id
->version
);
605 ret
= KRB5_KEYTAB_BADVNO
;
606 krb5_set_error_message(context
, ret
,
607 N_("Bad version in keytab %s", ""),
611 ret
= krb5_ret_int8 (sp
, &tag
);
613 krb5_set_error_message(context
, ret
,
614 N_("failed reading tag from "
620 storage_set_flags(context
, sp
, id
->version
);
626 emem
= krb5_storage_emem();
628 ret
= krb5_enomem(context
);
631 ret
= krb5_kt_store_principal(context
, emem
, entry
->principal
);
633 krb5_set_error_message(context
, ret
,
634 N_("Failed storing principal "
637 krb5_storage_free(emem
);
640 ret
= krb5_store_int32 (emem
, entry
->timestamp
);
642 krb5_set_error_message(context
, ret
,
643 N_("Failed storing timpstamp "
646 krb5_storage_free(emem
);
649 ret
= krb5_store_int8 (emem
, entry
->vno
% 256);
651 krb5_set_error_message(context
, ret
,
652 N_("Failed storing kvno "
655 krb5_storage_free(emem
);
658 ret
= krb5_kt_store_keyblock (context
, d
, emem
, &entry
->keyblock
);
660 krb5_storage_free(emem
);
663 if ((d
->flags
& KRB5_KT_FL_JAVA
) == 0) {
664 ret
= krb5_store_int32 (emem
, entry
->vno
);
666 krb5_set_error_message(context
, ret
,
667 N_("Failed storing extended kvno "
670 krb5_storage_free(emem
);
673 ret
= krb5_store_uint32 (emem
, entry
->flags
);
675 krb5_set_error_message(context
, ret
,
676 N_("Failed storing extended kvno "
679 krb5_storage_free(emem
);
684 ret
= krb5_storage_to_data(emem
, &keytab
);
685 krb5_storage_free(emem
);
687 krb5_set_error_message(context
, ret
,
688 N_("Failed converting keytab entry "
689 "to memory block for keytab %s", ""),
698 here
= krb5_storage_seek(sp
, 0, SEEK_CUR
);
701 krb5_set_error_message(context
, ret
,
702 N_("Failed writing keytab block "
703 "in keytab %s: %s", ""),
704 d
->filename
, strerror(ret
));
707 ret
= krb5_ret_int32(sp
, &len
);
709 /* There could have been a partial length. Recover! */
710 (void) krb5_storage_truncate(sp
, here
);
716 if(len
>= (int)keytab
.length
) {
717 krb5_storage_seek(sp
, -4, SEEK_CUR
);
721 krb5_storage_seek(sp
, len
, SEEK_CUR
);
723 ret
= krb5_store_int32(sp
, len
);
726 bytes
= krb5_storage_write(sp
, keytab
.data
, keytab
.length
);
727 if (bytes
!= keytab
.length
) {
728 ret
= bytes
== -1 ? errno
: KRB5_KT_END
;
729 krb5_set_error_message(context
, ret
,
730 N_("Failed writing keytab block "
731 "in keytab %s: %s", ""),
732 d
->filename
, strerror(ret
));
734 memset(keytab
.data
, 0, keytab
.length
);
735 krb5_data_free(&keytab
);
738 ret
= krb5_storage_fsync(sp
);
739 krb5_storage_free(sp
);
744 static krb5_error_code KRB5_CALLCONV
745 fkt_remove_entry(krb5_context context
,
747 krb5_keytab_entry
*entry
)
749 struct fkt_data
*fkt
= id
->data
;
752 krb5_kt_cursor cursor
;
753 off_t pos_start
, pos_end
;
757 ret
= fkt_start_seq_get_int(context
, id
, O_RDWR
| O_BINARY
| O_CLOEXEC
, 1, &cursor
);
759 const char *emsg
= krb5_get_error_message(context
, ret
);
761 krb5_set_error_message(context
, ret
,
762 N_("Could not open keytab file for write: %s: %s", ""),
765 krb5_free_error_message(context
, emsg
);
769 (ret
= 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
)) {
774 unsigned char buf
[128];
776 krb5_storage_seek(cursor
.sp
, pos_start
, SEEK_SET
);
777 len
= pos_end
- pos_start
- 4;
778 ret
= krb5_store_int32(cursor
.sp
, -len
);
779 memset(buf
, 0, sizeof(buf
));
780 while (ret
== 0 && len
> 0) {
781 bytes
= krb5_storage_write(cursor
.sp
, buf
,
782 min((size_t)len
, sizeof(buf
)));
783 if (bytes
!= min((size_t)len
, sizeof(buf
))) {
784 ret
= bytes
== -1 ? errno
: KRB5_KT_END
;
787 len
-= min((size_t)len
, sizeof(buf
));
790 krb5_kt_free_entry(context
, &e
);
792 (void) krb5_kt_end_seq_get(context
, id
, &cursor
);
793 if (ret
== KRB5_KT_END
)
796 const char *emsg
= krb5_get_error_message(context
, ret
);
798 krb5_set_error_message(context
, ret
,
799 N_("Could not remove keytab entry from %s: %s", ""),
802 krb5_free_error_message(context
, emsg
);
804 krb5_clear_error_message(context
);
805 return KRB5_KT_NOTFOUND
;
810 const krb5_kt_ops krb5_fkt_ops
= {
826 const krb5_kt_ops krb5_wrfkt_ops
= {
842 const krb5_kt_ops krb5_javakt_ops
= {