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 c
->fd
= open (d
->filename
, flags
);
377 krb5_set_error_message(context
, ret
,
378 N_("keytab %s open failed: %s", ""),
379 d
->filename
, strerror(ret
));
383 ret
= _krb5_xlock(context
, c
->fd
, exclusive
, d
->filename
);
388 if ((flags
& O_ACCMODE
) == O_RDWR
&& (flags
& O_APPEND
))
390 else if ((flags
& O_ACCMODE
) == O_RDWR
)
392 else if ((flags
& O_ACCMODE
) == O_WRONLY
)
394 c
->sp
= krb5_storage_stdio_from_fd(c
->fd
, stdio_mode
);
397 return krb5_enomem(context
);
399 krb5_storage_set_eof_code(c
->sp
, KRB5_KT_END
);
400 ret
= krb5_ret_int8(c
->sp
, &pvno
);
402 krb5_storage_free(c
->sp
);
404 krb5_clear_error_message(context
);
408 krb5_storage_free(c
->sp
);
410 krb5_clear_error_message (context
);
411 return KRB5_KEYTAB_BADVNO
;
413 ret
= krb5_ret_int8(c
->sp
, &tag
);
415 krb5_storage_free(c
->sp
);
417 krb5_clear_error_message(context
);
421 storage_set_flags(context
, c
->sp
, id
->version
);
425 static krb5_error_code KRB5_CALLCONV
426 fkt_start_seq_get(krb5_context context
,
430 return fkt_start_seq_get_int(context
, id
, O_RDONLY
| O_BINARY
| O_CLOEXEC
, 0, c
);
433 static krb5_error_code
434 fkt_next_entry_int(krb5_context context
,
436 krb5_keytab_entry
*entry
,
437 krb5_kt_cursor
*cursor
,
441 struct fkt_data
*d
= id
->data
;
449 pos
= krb5_storage_seek(cursor
->sp
, 0, SEEK_CUR
);
451 ret
= krb5_ret_int32(cursor
->sp
, &len
);
455 pos
= krb5_storage_seek(cursor
->sp
, -len
, SEEK_CUR
);
458 ret
= krb5_kt_ret_principal (context
, d
, cursor
->sp
, &entry
->principal
);
461 ret
= krb5_ret_uint32(cursor
->sp
, &utmp32
);
462 entry
->timestamp
= utmp32
;
465 ret
= krb5_ret_int8(cursor
->sp
, &tmp8
);
469 ret
= krb5_kt_ret_keyblock (context
, d
, cursor
->sp
, &entry
->keyblock
);
472 /* there might be a 32 bit kvno here
473 * if it's zero, assume that the 8bit one was right,
474 * otherwise trust the new value */
475 curpos
= krb5_storage_seek(cursor
->sp
, 0, SEEK_CUR
);
476 if(len
+ 4 + pos
- curpos
>= 4) {
477 ret
= krb5_ret_int32(cursor
->sp
, &tmp32
);
478 if (ret
== 0 && tmp32
!= 0)
481 /* there might be a flags field here */
482 if(len
+ 4 + pos
- curpos
>= 8) {
483 ret
= krb5_ret_uint32(cursor
->sp
, &utmp32
);
485 entry
->flags
= utmp32
;
489 entry
->aliases
= NULL
;
491 if(start
) *start
= pos
;
492 if(end
) *end
= pos
+ 4 + len
;
495 krb5_kt_free_entry(context
, entry
);
496 krb5_storage_seek(cursor
->sp
, pos
+ 4 + len
, SEEK_SET
);
500 static krb5_error_code KRB5_CALLCONV
501 fkt_next_entry(krb5_context context
,
503 krb5_keytab_entry
*entry
,
504 krb5_kt_cursor
*cursor
)
506 return fkt_next_entry_int(context
, id
, entry
, cursor
, NULL
, NULL
);
509 static krb5_error_code KRB5_CALLCONV
510 fkt_end_seq_get(krb5_context context
,
512 krb5_kt_cursor
*cursor
)
514 krb5_storage_free(cursor
->sp
);
519 static krb5_error_code KRB5_CALLCONV
520 fkt_setup_keytab(krb5_context context
,
525 ret
= krb5_store_int8(sp
, 5);
529 id
->version
= KRB5_KT_VNO
;
530 return krb5_store_int8 (sp
, id
->version
);
533 static krb5_error_code KRB5_CALLCONV
534 fkt_add_entry(krb5_context context
,
536 krb5_keytab_entry
*entry
)
542 struct fkt_data
*d
= id
->data
;
546 fd
= open(d
->filename
, O_RDWR
| O_BINARY
| O_CLOEXEC
);
548 fd
= open(d
->filename
, O_RDWR
| O_CREAT
| O_EXCL
| O_BINARY
| O_CLOEXEC
, 0600);
551 krb5_set_error_message(context
, ret
,
552 N_("open(%s): %s", ""), d
->filename
,
558 ret
= _krb5_xlock(context
, fd
, 1, d
->filename
);
563 sp
= krb5_storage_stdio_from_fd(fd
, "wb+");
566 return krb5_enomem(context
);
568 krb5_storage_set_eof_code(sp
, KRB5_KT_END
);
569 ret
= fkt_setup_keytab(context
, id
, sp
);
573 storage_set_flags(context
, sp
, id
->version
);
579 ret
= _krb5_xlock(context
, fd
, 1, d
->filename
);
584 sp
= krb5_storage_stdio_from_fd(fd
, "wb+");
589 krb5_storage_set_eof_code(sp
, KRB5_KT_END
);
590 ret
= krb5_ret_int8(sp
, &pvno
);
592 /* we probably have a zero byte file, so try to set it up
594 ret
= fkt_setup_keytab(context
, id
, sp
);
596 krb5_set_error_message(context
, ret
,
597 N_("%s: keytab is corrupted: %s", ""),
598 d
->filename
, strerror(ret
));
601 storage_set_flags(context
, sp
, id
->version
);
604 ret
= KRB5_KEYTAB_BADVNO
;
605 krb5_set_error_message(context
, ret
,
606 N_("Bad version in keytab %s", ""),
610 ret
= krb5_ret_int8 (sp
, &tag
);
612 krb5_set_error_message(context
, ret
,
613 N_("failed reading tag from "
619 storage_set_flags(context
, sp
, id
->version
);
625 emem
= krb5_storage_emem();
627 ret
= krb5_enomem(context
);
630 ret
= krb5_kt_store_principal(context
, emem
, entry
->principal
);
632 krb5_set_error_message(context
, ret
,
633 N_("Failed storing principal "
636 krb5_storage_free(emem
);
639 ret
= krb5_store_int32 (emem
, entry
->timestamp
);
641 krb5_set_error_message(context
, ret
,
642 N_("Failed storing timpstamp "
645 krb5_storage_free(emem
);
648 ret
= krb5_store_int8 (emem
, entry
->vno
% 256);
650 krb5_set_error_message(context
, ret
,
651 N_("Failed storing kvno "
654 krb5_storage_free(emem
);
657 ret
= krb5_kt_store_keyblock (context
, d
, emem
, &entry
->keyblock
);
659 krb5_storage_free(emem
);
662 if ((d
->flags
& KRB5_KT_FL_JAVA
) == 0) {
663 ret
= krb5_store_int32 (emem
, entry
->vno
);
665 krb5_set_error_message(context
, ret
,
666 N_("Failed storing extended kvno "
669 krb5_storage_free(emem
);
672 ret
= krb5_store_uint32 (emem
, entry
->flags
);
674 krb5_set_error_message(context
, ret
,
675 N_("Failed storing extended kvno "
678 krb5_storage_free(emem
);
683 ret
= krb5_storage_to_data(emem
, &keytab
);
684 krb5_storage_free(emem
);
686 krb5_set_error_message(context
, ret
,
687 N_("Failed converting keytab entry "
688 "to memory block for keytab %s", ""),
697 here
= krb5_storage_seek(sp
, 0, SEEK_CUR
);
700 krb5_set_error_message(context
, ret
,
701 N_("Failed writing keytab block "
702 "in keytab %s: %s", ""),
703 d
->filename
, strerror(ret
));
706 ret
= krb5_ret_int32(sp
, &len
);
708 /* There could have been a partial length. Recover! */
709 (void) krb5_storage_truncate(sp
, here
);
715 if(len
>= (int)keytab
.length
) {
716 krb5_storage_seek(sp
, -4, SEEK_CUR
);
720 krb5_storage_seek(sp
, len
, SEEK_CUR
);
722 ret
= krb5_store_int32(sp
, len
);
725 bytes
= krb5_storage_write(sp
, keytab
.data
, keytab
.length
);
726 if (bytes
!= keytab
.length
) {
727 ret
= bytes
== -1 ? errno
: KRB5_KT_END
;
728 krb5_set_error_message(context
, ret
,
729 N_("Failed writing keytab block "
730 "in keytab %s: %s", ""),
731 d
->filename
, strerror(ret
));
733 memset(keytab
.data
, 0, keytab
.length
);
734 krb5_data_free(&keytab
);
737 ret
= krb5_storage_fsync(sp
);
738 krb5_storage_free(sp
);
743 static krb5_error_code KRB5_CALLCONV
744 fkt_remove_entry(krb5_context context
,
746 krb5_keytab_entry
*entry
)
748 struct fkt_data
*fkt
= id
->data
;
751 krb5_kt_cursor cursor
;
752 off_t pos_start
, pos_end
;
756 ret
= fkt_start_seq_get_int(context
, id
, O_RDWR
| O_BINARY
| O_CLOEXEC
, 1, &cursor
);
758 const char *emsg
= krb5_get_error_message(context
, ret
);
760 krb5_set_error_message(context
, ret
,
761 N_("Could not open keytab file for write: %s: %s", ""),
764 krb5_free_error_message(context
, emsg
);
768 (ret
= fkt_next_entry_int(context
, id
, &e
, &cursor
,
769 &pos_start
, &pos_end
)) == 0) {
770 if (krb5_kt_compare(context
, &e
, entry
->principal
,
771 entry
->vno
, entry
->keyblock
.keytype
)) {
773 unsigned char buf
[128];
775 krb5_storage_seek(cursor
.sp
, pos_start
, SEEK_SET
);
776 len
= pos_end
- pos_start
- 4;
777 ret
= krb5_store_int32(cursor
.sp
, -len
);
778 memset(buf
, 0, sizeof(buf
));
779 while (ret
== 0 && len
> 0) {
780 bytes
= krb5_storage_write(cursor
.sp
, buf
,
781 min((size_t)len
, sizeof(buf
)));
782 if (bytes
!= min((size_t)len
, sizeof(buf
))) {
783 ret
= bytes
== -1 ? errno
: KRB5_KT_END
;
786 len
-= min((size_t)len
, sizeof(buf
));
789 krb5_kt_free_entry(context
, &e
);
791 (void) krb5_kt_end_seq_get(context
, id
, &cursor
);
792 if (ret
== KRB5_KT_END
)
795 const char *emsg
= krb5_get_error_message(context
, ret
);
797 krb5_set_error_message(context
, ret
,
798 N_("Could not remove keytab entry from %s: %s", ""),
800 krb5_get_error_message(context
, ret
));
801 krb5_free_error_message(context
, emsg
);
803 krb5_clear_error_message(context
);
804 return KRB5_KT_NOTFOUND
;
809 const krb5_kt_ops krb5_fkt_ops
= {
825 const krb5_kt_ops krb5_wrfkt_ops
= {
841 const krb5_kt_ops krb5_javakt_ops
= {