1 /* $NetBSD: keytab_file.c,v 1.1.1.2 2014/04/24 12:45:50 pettai Exp $ */
4 * Copyright (c) 1997 - 2008 Kungliga Tekniska Högskolan
5 * (Royal Institute of Technology, Stockholm, Sweden).
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
12 * 1. Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
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.
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.
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
36 #include "krb5_locl.h"
38 #define KRB5_KT_VNO_1 1
39 #define KRB5_KT_VNO_2 2
40 #define KRB5_KT_VNO KRB5_KT_VNO_2
42 #define KRB5_KT_FL_JAVA 1
45 /* file operations -------------------------------------------- */
52 static krb5_error_code
53 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 krb5_set_error_message(context
, ENOMEM
, N_("malloc: out of memory", ""));
68 ret
= krb5_storage_read(sp
, data
->data
, size
);
70 return (ret
< 0)? errno
: KRB5_KT_END
;
74 static krb5_error_code
75 krb5_kt_ret_string(krb5_context context
,
77 heim_general_string
*data
)
81 ret
= krb5_ret_int16(sp
, &size
);
84 *data
= malloc(size
+ 1);
86 krb5_set_error_message(context
, ENOMEM
, N_("malloc: out of memory", ""));
89 ret
= krb5_storage_read(sp
, *data
, size
);
92 return (ret
< 0)? errno
: KRB5_KT_END
;
96 static krb5_error_code
97 krb5_kt_store_data(krb5_context context
,
102 ret
= krb5_store_int16(sp
, data
.length
);
105 ret
= krb5_storage_write(sp
, data
.data
, data
.length
);
106 if(ret
!= (int)data
.length
){
114 static krb5_error_code
115 krb5_kt_store_string(krb5_storage
*sp
,
116 heim_general_string data
)
119 size_t len
= strlen(data
);
120 ret
= krb5_store_int16(sp
, len
);
123 ret
= krb5_storage_write(sp
, data
, len
);
132 static krb5_error_code
133 krb5_kt_ret_keyblock(krb5_context context
,
134 struct fkt_data
*fkt
,
141 ret
= krb5_ret_int16(sp
, &tmp
); /* keytype + etype */
143 krb5_set_error_message(context
, ret
,
144 N_("Cant read keyblock from file %s", ""),
149 ret
= krb5_kt_ret_data(context
, sp
, &p
->keyvalue
);
151 krb5_set_error_message(context
, ret
,
152 N_("Cant read keyblock from file %s", ""),
157 static krb5_error_code
158 krb5_kt_store_keyblock(krb5_context context
,
159 struct fkt_data
*fkt
,
165 ret
= krb5_store_int16(sp
, p
->keytype
); /* keytype + etype */
167 krb5_set_error_message(context
, ret
,
168 N_("Cant store keyblock to file %s", ""),
172 ret
= krb5_kt_store_data(context
, sp
, p
->keyvalue
);
174 krb5_set_error_message(context
, ret
,
175 N_("Cant store keyblock to file %s", ""),
181 static krb5_error_code
182 krb5_kt_ret_principal(krb5_context context
,
183 struct fkt_data
*fkt
,
185 krb5_principal
*princ
)
194 krb5_set_error_message(context
, ENOMEM
,
195 N_("malloc: out of memory", ""));
199 ret
= krb5_ret_int16(sp
, &len
);
201 krb5_set_error_message(context
, ret
,
202 N_("Failed decoding length of "
203 "keytab principal in keytab file %s", ""),
207 if(krb5_storage_is_flags(sp
, KRB5_STORAGE_PRINCIPAL_WRONG_NUM_COMPONENTS
))
211 krb5_set_error_message(context
, ret
,
212 N_("Keytab principal contains "
213 "invalid length in keytab %s", ""),
217 ret
= krb5_kt_ret_string(context
, sp
, &p
->realm
);
219 krb5_set_error_message(context
, ret
,
220 N_("Can't read realm from keytab: %s", ""),
224 p
->name
.name_string
.val
= calloc(len
, sizeof(*p
->name
.name_string
.val
));
225 if(p
->name
.name_string
.val
== NULL
) {
227 krb5_set_error_message(context
, ret
, N_("malloc: out of memory", ""));
230 p
->name
.name_string
.len
= len
;
231 for(i
= 0; i
< p
->name
.name_string
.len
; i
++){
232 ret
= krb5_kt_ret_string(context
, sp
, p
->name
.name_string
.val
+ i
);
234 krb5_set_error_message(context
, ret
,
235 N_("Can't read principal from "
241 if (krb5_storage_is_flags(sp
, KRB5_STORAGE_PRINCIPAL_NO_NAME_TYPE
))
242 p
->name
.name_type
= KRB5_NT_UNKNOWN
;
245 ret
= krb5_ret_int32(sp
, &tmp32
);
246 p
->name
.name_type
= tmp32
;
248 krb5_set_error_message(context
, ret
,
249 N_("Can't read name-type from "
258 krb5_free_principal(context
, p
);
262 static krb5_error_code
263 krb5_kt_store_principal(krb5_context context
,
270 if(krb5_storage_is_flags(sp
, KRB5_STORAGE_PRINCIPAL_WRONG_NUM_COMPONENTS
))
271 ret
= krb5_store_int16(sp
, p
->name
.name_string
.len
+ 1);
273 ret
= krb5_store_int16(sp
, p
->name
.name_string
.len
);
275 ret
= krb5_kt_store_string(sp
, p
->realm
);
277 for(i
= 0; i
< p
->name
.name_string
.len
; i
++){
278 ret
= krb5_kt_store_string(sp
, p
->name
.name_string
.val
[i
]);
282 if(!krb5_storage_is_flags(sp
, KRB5_STORAGE_PRINCIPAL_NO_NAME_TYPE
)) {
283 ret
= krb5_store_int32(sp
, p
->name
.name_type
);
291 static krb5_error_code KRB5_CALLCONV
292 fkt_resolve(krb5_context context
, const char *name
, krb5_keytab id
)
296 d
= malloc(sizeof(*d
));
298 krb5_set_error_message(context
, ENOMEM
, N_("malloc: out of memory", ""));
301 d
->filename
= strdup(name
);
302 if(d
->filename
== NULL
) {
304 krb5_set_error_message(context
, ENOMEM
, N_("malloc: out of memory", ""));
312 static krb5_error_code KRB5_CALLCONV
313 fkt_resolve_java14(krb5_context context
, const char *name
, krb5_keytab id
)
317 ret
= fkt_resolve(context
, name
, id
);
319 struct fkt_data
*d
= id
->data
;
320 d
->flags
|= KRB5_KT_FL_JAVA
;
325 static krb5_error_code KRB5_CALLCONV
326 fkt_close(krb5_context context
, krb5_keytab id
)
328 struct fkt_data
*d
= id
->data
;
334 static krb5_error_code KRB5_CALLCONV
335 fkt_destroy(krb5_context context
, krb5_keytab id
)
337 struct fkt_data
*d
= id
->data
;
338 _krb5_erase_file(context
, d
->filename
);
342 static krb5_error_code KRB5_CALLCONV
343 fkt_get_name(krb5_context context
,
348 /* This function is XXX */
349 struct fkt_data
*d
= id
->data
;
350 strlcpy(name
, d
->filename
, namesize
);
355 storage_set_flags(krb5_context context
, krb5_storage
*sp
, int vno
)
360 flags
|= KRB5_STORAGE_PRINCIPAL_WRONG_NUM_COMPONENTS
;
361 flags
|= KRB5_STORAGE_PRINCIPAL_NO_NAME_TYPE
;
362 flags
|= KRB5_STORAGE_HOST_BYTEORDER
;
368 "storage_set_flags called with bad vno (%d)", vno
);
370 krb5_storage_set_flags(sp
, flags
);
373 static krb5_error_code
374 fkt_start_seq_get_int(krb5_context context
,
382 struct fkt_data
*d
= id
->data
;
384 c
->fd
= open (d
->filename
, flags
);
387 krb5_set_error_message(context
, ret
,
388 N_("keytab %s open failed: %s", ""),
389 d
->filename
, strerror(ret
));
393 ret
= _krb5_xlock(context
, c
->fd
, exclusive
, d
->filename
);
398 c
->sp
= krb5_storage_from_fd(c
->fd
);
400 _krb5_xunlock(context
, c
->fd
);
402 krb5_set_error_message(context
, ENOMEM
,
403 N_("malloc: out of memory", ""));
406 krb5_storage_set_eof_code(c
->sp
, KRB5_KT_END
);
407 ret
= krb5_ret_int8(c
->sp
, &pvno
);
409 krb5_storage_free(c
->sp
);
410 _krb5_xunlock(context
, c
->fd
);
412 krb5_clear_error_message(context
);
416 krb5_storage_free(c
->sp
);
417 _krb5_xunlock(context
, c
->fd
);
419 krb5_clear_error_message (context
);
420 return KRB5_KEYTAB_BADVNO
;
422 ret
= krb5_ret_int8(c
->sp
, &tag
);
424 krb5_storage_free(c
->sp
);
425 _krb5_xunlock(context
, c
->fd
);
427 krb5_clear_error_message(context
);
431 storage_set_flags(context
, c
->sp
, id
->version
);
435 static krb5_error_code KRB5_CALLCONV
436 fkt_start_seq_get(krb5_context context
,
440 return fkt_start_seq_get_int(context
, id
, O_RDONLY
| O_BINARY
| O_CLOEXEC
, 0, c
);
443 static krb5_error_code
444 fkt_next_entry_int(krb5_context context
,
446 krb5_keytab_entry
*entry
,
447 krb5_kt_cursor
*cursor
,
451 struct fkt_data
*d
= id
->data
;
459 pos
= krb5_storage_seek(cursor
->sp
, 0, SEEK_CUR
);
461 ret
= krb5_ret_int32(cursor
->sp
, &len
);
465 pos
= krb5_storage_seek(cursor
->sp
, -len
, SEEK_CUR
);
468 ret
= krb5_kt_ret_principal (context
, d
, cursor
->sp
, &entry
->principal
);
471 ret
= krb5_ret_uint32(cursor
->sp
, &utmp32
);
472 entry
->timestamp
= utmp32
;
475 ret
= krb5_ret_int8(cursor
->sp
, &tmp8
);
479 ret
= krb5_kt_ret_keyblock (context
, d
, cursor
->sp
, &entry
->keyblock
);
482 /* there might be a 32 bit kvno here
483 * if it's zero, assume that the 8bit one was right,
484 * otherwise trust the new value */
485 curpos
= krb5_storage_seek(cursor
->sp
, 0, SEEK_CUR
);
486 if(len
+ 4 + pos
- curpos
>= 4) {
487 ret
= krb5_ret_int32(cursor
->sp
, &tmp32
);
488 if (ret
== 0 && tmp32
!= 0)
491 /* there might be a flags field here */
492 if(len
+ 4 + pos
- curpos
>= 8) {
493 ret
= krb5_ret_uint32(cursor
->sp
, &utmp32
);
495 entry
->flags
= utmp32
;
499 entry
->aliases
= NULL
;
501 if(start
) *start
= pos
;
502 if(end
) *end
= pos
+ 4 + len
;
504 krb5_storage_seek(cursor
->sp
, pos
+ 4 + len
, SEEK_SET
);
508 static krb5_error_code KRB5_CALLCONV
509 fkt_next_entry(krb5_context context
,
511 krb5_keytab_entry
*entry
,
512 krb5_kt_cursor
*cursor
)
514 return fkt_next_entry_int(context
, id
, entry
, cursor
, NULL
, NULL
);
517 static krb5_error_code KRB5_CALLCONV
518 fkt_end_seq_get(krb5_context context
,
520 krb5_kt_cursor
*cursor
)
522 krb5_storage_free(cursor
->sp
);
523 _krb5_xunlock(context
, cursor
->fd
);
528 static krb5_error_code KRB5_CALLCONV
529 fkt_setup_keytab(krb5_context context
,
534 ret
= krb5_store_int8(sp
, 5);
538 id
->version
= KRB5_KT_VNO
;
539 return krb5_store_int8 (sp
, id
->version
);
542 static krb5_error_code KRB5_CALLCONV
543 fkt_add_entry(krb5_context context
,
545 krb5_keytab_entry
*entry
)
550 struct fkt_data
*d
= id
->data
;
554 fd
= open (d
->filename
, O_RDWR
| O_BINARY
| O_CLOEXEC
);
556 fd
= open (d
->filename
, O_RDWR
| O_CREAT
| O_EXCL
| O_BINARY
| O_CLOEXEC
, 0600);
559 krb5_set_error_message(context
, ret
,
560 N_("open(%s): %s", ""), d
->filename
,
566 ret
= _krb5_xlock(context
, fd
, 1, d
->filename
);
571 sp
= krb5_storage_from_fd(fd
);
572 krb5_storage_set_eof_code(sp
, KRB5_KT_END
);
573 ret
= fkt_setup_keytab(context
, id
, sp
);
577 storage_set_flags(context
, sp
, id
->version
);
583 ret
= _krb5_xlock(context
, fd
, 1, d
->filename
);
588 sp
= krb5_storage_from_fd(fd
);
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();
628 krb5_set_error_message(context
, ret
,
629 N_("malloc: out of memory", ""));
632 ret
= krb5_kt_store_principal(context
, emem
, entry
->principal
);
634 krb5_set_error_message(context
, ret
,
635 N_("Failed storing principal "
638 krb5_storage_free(emem
);
641 ret
= krb5_store_int32 (emem
, entry
->timestamp
);
643 krb5_set_error_message(context
, ret
,
644 N_("Failed storing timpstamp "
647 krb5_storage_free(emem
);
650 ret
= krb5_store_int8 (emem
, entry
->vno
% 256);
652 krb5_set_error_message(context
, ret
,
653 N_("Failed storing kvno "
656 krb5_storage_free(emem
);
659 ret
= krb5_kt_store_keyblock (context
, d
, emem
, &entry
->keyblock
);
661 krb5_storage_free(emem
);
664 if ((d
->flags
& KRB5_KT_FL_JAVA
) == 0) {
665 ret
= krb5_store_int32 (emem
, entry
->vno
);
667 krb5_set_error_message(context
, ret
,
668 N_("Failed storing extended kvno "
671 krb5_storage_free(emem
);
674 ret
= krb5_store_uint32 (emem
, entry
->flags
);
676 krb5_set_error_message(context
, ret
,
677 N_("Failed storing extended kvno "
680 krb5_storage_free(emem
);
685 ret
= krb5_storage_to_data(emem
, &keytab
);
686 krb5_storage_free(emem
);
688 krb5_set_error_message(context
, ret
,
689 N_("Failed converting keytab entry "
690 "to memory block for keytab %s", ""),
697 ret
= krb5_ret_int32(sp
, &len
);
698 if(ret
== KRB5_KT_END
) {
704 if(len
>= (int)keytab
.length
) {
705 krb5_storage_seek(sp
, -4, SEEK_CUR
);
709 krb5_storage_seek(sp
, len
, SEEK_CUR
);
711 ret
= krb5_store_int32(sp
, len
);
712 if(krb5_storage_write(sp
, keytab
.data
, keytab
.length
) < 0) {
714 krb5_set_error_message(context
, ret
,
715 N_("Failed writing keytab block "
716 "in keytab %s: %s", ""),
717 d
->filename
, strerror(ret
));
719 memset(keytab
.data
, 0, keytab
.length
);
720 krb5_data_free(&keytab
);
722 krb5_storage_free(sp
);
723 _krb5_xunlock(context
, fd
);
728 static krb5_error_code KRB5_CALLCONV
729 fkt_remove_entry(krb5_context context
,
731 krb5_keytab_entry
*entry
)
734 krb5_kt_cursor cursor
;
735 off_t pos_start
, pos_end
;
739 ret
= fkt_start_seq_get_int(context
, id
, O_RDWR
| O_BINARY
| O_CLOEXEC
, 1, &cursor
);
741 goto out
; /* return other error here? */
742 while(fkt_next_entry_int(context
, id
, &e
, &cursor
,
743 &pos_start
, &pos_end
) == 0) {
744 if(krb5_kt_compare(context
, &e
, entry
->principal
,
745 entry
->vno
, entry
->keyblock
.keytype
)) {
747 unsigned char buf
[128];
749 krb5_storage_seek(cursor
.sp
, pos_start
, SEEK_SET
);
750 len
= pos_end
- pos_start
- 4;
751 krb5_store_int32(cursor
.sp
, -len
);
752 memset(buf
, 0, sizeof(buf
));
754 krb5_storage_write(cursor
.sp
, buf
,
755 min((size_t)len
, sizeof(buf
)));
756 len
-= min((size_t)len
, sizeof(buf
));
759 krb5_kt_free_entry(context
, &e
);
761 krb5_kt_end_seq_get(context
, id
, &cursor
);
764 krb5_clear_error_message (context
);
765 return KRB5_KT_NOTFOUND
;
770 const krb5_kt_ops krb5_fkt_ops
= {
784 const krb5_kt_ops krb5_wrfkt_ops
= {
798 const krb5_kt_ops krb5_javakt_ops
= {