2 * Copyright (c) 1997 - 2008 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
,
57 ret
= krb5_ret_int16(sp
, &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
);
66 return (ret
< 0)? errno
: KRB5_KT_END
;
70 static krb5_error_code
71 krb5_kt_ret_string(krb5_context context
,
73 heim_general_string
*data
)
77 ret
= krb5_ret_int16(sp
, &size
);
80 *data
= malloc(size
+ 1);
82 return krb5_enomem(context
);
83 ret
= krb5_storage_read(sp
, *data
, size
);
86 return (ret
< 0)? errno
: KRB5_KT_END
;
90 static krb5_error_code
91 krb5_kt_store_data(krb5_context context
,
96 ret
= krb5_store_int16(sp
, data
.length
);
99 ret
= krb5_storage_write(sp
, data
.data
, data
.length
);
100 if(ret
!= (int)data
.length
){
108 static krb5_error_code
109 krb5_kt_store_string(krb5_storage
*sp
,
110 heim_general_string data
)
113 size_t len
= strlen(data
);
114 ret
= krb5_store_int16(sp
, len
);
117 ret
= krb5_storage_write(sp
, data
, len
);
126 static krb5_error_code
127 krb5_kt_ret_keyblock(krb5_context context
,
128 struct fkt_data
*fkt
,
135 ret
= krb5_ret_int16(sp
, &tmp
); /* keytype + etype */
137 krb5_set_error_message(context
, ret
,
138 N_("Cant read keyblock from file %s", ""),
143 ret
= krb5_kt_ret_data(context
, sp
, &p
->keyvalue
);
145 krb5_set_error_message(context
, ret
,
146 N_("Cant read keyblock from file %s", ""),
151 static krb5_error_code
152 krb5_kt_store_keyblock(krb5_context context
,
153 struct fkt_data
*fkt
,
159 ret
= krb5_store_int16(sp
, p
->keytype
); /* keytype + etype */
161 krb5_set_error_message(context
, ret
,
162 N_("Cant store keyblock to file %s", ""),
166 ret
= krb5_kt_store_data(context
, sp
, p
->keyvalue
);
168 krb5_set_error_message(context
, ret
,
169 N_("Cant store keyblock to file %s", ""),
175 static krb5_error_code
176 krb5_kt_ret_principal(krb5_context context
,
177 struct fkt_data
*fkt
,
179 krb5_principal
*princ
)
188 return krb5_enomem(context
);
190 ret
= krb5_ret_int16(sp
, &len
);
192 krb5_set_error_message(context
, ret
,
193 N_("Failed decoding length of "
194 "keytab principal in keytab file %s", ""),
198 if(krb5_storage_is_flags(sp
, KRB5_STORAGE_PRINCIPAL_WRONG_NUM_COMPONENTS
))
202 krb5_set_error_message(context
, ret
,
203 N_("Keytab principal contains "
204 "invalid length in keytab %s", ""),
208 ret
= krb5_kt_ret_string(context
, sp
, &p
->realm
);
210 krb5_set_error_message(context
, ret
,
211 N_("Can't read realm from keytab: %s", ""),
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
);
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
);
224 krb5_set_error_message(context
, ret
,
225 N_("Can't read principal from "
231 if (krb5_storage_is_flags(sp
, KRB5_STORAGE_PRINCIPAL_NO_NAME_TYPE
))
232 p
->name
.name_type
= KRB5_NT_UNKNOWN
;
235 ret
= krb5_ret_int32(sp
, &tmp32
);
236 p
->name
.name_type
= tmp32
;
238 krb5_set_error_message(context
, ret
,
239 N_("Can't read name-type from "
248 krb5_free_principal(context
, p
);
252 static krb5_error_code
253 krb5_kt_store_principal(krb5_context context
,
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);
263 ret
= krb5_store_int16(sp
, p
->name
.name_string
.len
);
265 ret
= krb5_kt_store_string(sp
, p
->realm
);
267 for(i
= 0; i
< p
->name
.name_string
.len
; i
++){
268 ret
= krb5_kt_store_string(sp
, p
->name
.name_string
.val
[i
]);
272 if(!krb5_storage_is_flags(sp
, KRB5_STORAGE_PRINCIPAL_NO_NAME_TYPE
)) {
273 ret
= krb5_store_int32(sp
, p
->name
.name_type
);
281 static krb5_error_code KRB5_CALLCONV
282 fkt_resolve(krb5_context context
, const char *name
, krb5_keytab id
)
286 d
= malloc(sizeof(*d
));
288 return krb5_enomem(context
);
289 d
->filename
= strdup(name
);
290 if(d
->filename
== NULL
) {
292 return krb5_enomem(context
);
299 static krb5_error_code KRB5_CALLCONV
300 fkt_resolve_java14(krb5_context context
, const char *name
, krb5_keytab id
)
304 ret
= fkt_resolve(context
, name
, id
);
306 struct fkt_data
*d
= id
->data
;
307 d
->flags
|= KRB5_KT_FL_JAVA
;
312 static krb5_error_code KRB5_CALLCONV
313 fkt_close(krb5_context context
, krb5_keytab id
)
315 struct fkt_data
*d
= id
->data
;
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
);
329 static krb5_error_code KRB5_CALLCONV
330 fkt_get_name(krb5_context context
,
335 /* This function is XXX */
336 struct fkt_data
*d
= id
->data
;
337 strlcpy(name
, d
->filename
, namesize
);
342 storage_set_flags(krb5_context context
, krb5_storage
*sp
, int vno
)
347 flags
|= KRB5_STORAGE_PRINCIPAL_WRONG_NUM_COMPONENTS
;
348 flags
|= KRB5_STORAGE_PRINCIPAL_NO_NAME_TYPE
;
349 flags
|= KRB5_STORAGE_HOST_BYTEORDER
;
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
,
369 struct fkt_data
*d
= id
->data
;
371 c
->fd
= open (d
->filename
, flags
);
374 krb5_set_error_message(context
, ret
,
375 N_("keytab %s open failed: %s", ""),
376 d
->filename
, strerror(ret
));
380 ret
= _krb5_xlock(context
, c
->fd
, exclusive
, d
->filename
);
385 c
->sp
= krb5_storage_from_fd(c
->fd
);
387 _krb5_xunlock(context
, 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
);
394 krb5_storage_free(c
->sp
);
395 _krb5_xunlock(context
, c
->fd
);
397 krb5_clear_error_message(context
);
401 krb5_storage_free(c
->sp
);
402 _krb5_xunlock(context
, c
->fd
);
404 krb5_clear_error_message (context
);
405 return KRB5_KEYTAB_BADVNO
;
407 ret
= krb5_ret_int8(c
->sp
, &tag
);
409 krb5_storage_free(c
->sp
);
410 _krb5_xunlock(context
, c
->fd
);
412 krb5_clear_error_message(context
);
416 storage_set_flags(context
, c
->sp
, id
->version
);
420 static krb5_error_code KRB5_CALLCONV
421 fkt_start_seq_get(krb5_context context
,
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
,
431 krb5_keytab_entry
*entry
,
432 krb5_kt_cursor
*cursor
,
436 struct fkt_data
*d
= id
->data
;
444 pos
= krb5_storage_seek(cursor
->sp
, 0, SEEK_CUR
);
446 ret
= krb5_ret_int32(cursor
->sp
, &len
);
450 pos
= krb5_storage_seek(cursor
->sp
, -len
, SEEK_CUR
);
453 ret
= krb5_kt_ret_principal (context
, d
, cursor
->sp
, &entry
->principal
);
456 ret
= krb5_ret_uint32(cursor
->sp
, &utmp32
);
457 entry
->timestamp
= utmp32
;
460 ret
= krb5_ret_int8(cursor
->sp
, &tmp8
);
464 ret
= krb5_kt_ret_keyblock (context
, d
, cursor
->sp
, &entry
->keyblock
);
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)
476 /* there might be a flags field here */
477 if(len
+ 4 + pos
- curpos
>= 8) {
478 ret
= krb5_ret_uint32(cursor
->sp
, &utmp32
);
480 entry
->flags
= utmp32
;
484 entry
->aliases
= NULL
;
486 if(start
) *start
= pos
;
487 if(end
) *end
= pos
+ 4 + len
;
489 krb5_storage_seek(cursor
->sp
, pos
+ 4 + len
, SEEK_SET
);
493 static krb5_error_code KRB5_CALLCONV
494 fkt_next_entry(krb5_context context
,
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
,
505 krb5_kt_cursor
*cursor
)
507 krb5_storage_free(cursor
->sp
);
508 _krb5_xunlock(context
, cursor
->fd
);
513 static krb5_error_code KRB5_CALLCONV
514 fkt_setup_keytab(krb5_context context
,
519 ret
= krb5_store_int8(sp
, 5);
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
,
530 krb5_keytab_entry
*entry
)
535 struct fkt_data
*d
= id
->data
;
539 fd
= open (d
->filename
, O_RDWR
| O_BINARY
| O_CLOEXEC
);
541 fd
= open (d
->filename
, O_RDWR
| O_CREAT
| O_EXCL
| O_BINARY
| O_CLOEXEC
, 0600);
544 krb5_set_error_message(context
, ret
,
545 N_("open(%s): %s", ""), d
->filename
,
551 ret
= _krb5_xlock(context
, fd
, 1, d
->filename
);
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
);
562 storage_set_flags(context
, sp
, id
->version
);
568 ret
= _krb5_xlock(context
, fd
, 1, d
->filename
);
573 sp
= krb5_storage_from_fd(fd
);
574 krb5_storage_set_eof_code(sp
, KRB5_KT_END
);
575 ret
= krb5_ret_int8(sp
, &pvno
);
577 /* we probably have a zero byte file, so try to set it up
579 ret
= fkt_setup_keytab(context
, id
, sp
);
581 krb5_set_error_message(context
, ret
,
582 N_("%s: keytab is corrupted: %s", ""),
583 d
->filename
, strerror(ret
));
586 storage_set_flags(context
, sp
, id
->version
);
589 ret
= KRB5_KEYTAB_BADVNO
;
590 krb5_set_error_message(context
, ret
,
591 N_("Bad version in keytab %s", ""),
595 ret
= krb5_ret_int8 (sp
, &tag
);
597 krb5_set_error_message(context
, ret
,
598 N_("failed reading tag from "
604 storage_set_flags(context
, sp
, id
->version
);
610 emem
= krb5_storage_emem();
612 ret
= krb5_enomem(context
);
615 ret
= krb5_kt_store_principal(context
, emem
, entry
->principal
);
617 krb5_set_error_message(context
, ret
,
618 N_("Failed storing principal "
621 krb5_storage_free(emem
);
624 ret
= krb5_store_int32 (emem
, entry
->timestamp
);
626 krb5_set_error_message(context
, ret
,
627 N_("Failed storing timpstamp "
630 krb5_storage_free(emem
);
633 ret
= krb5_store_int8 (emem
, entry
->vno
% 256);
635 krb5_set_error_message(context
, ret
,
636 N_("Failed storing kvno "
639 krb5_storage_free(emem
);
642 ret
= krb5_kt_store_keyblock (context
, d
, emem
, &entry
->keyblock
);
644 krb5_storage_free(emem
);
647 if ((d
->flags
& KRB5_KT_FL_JAVA
) == 0) {
648 ret
= krb5_store_int32 (emem
, entry
->vno
);
650 krb5_set_error_message(context
, ret
,
651 N_("Failed storing extended kvno "
654 krb5_storage_free(emem
);
657 ret
= krb5_store_uint32 (emem
, entry
->flags
);
659 krb5_set_error_message(context
, ret
,
660 N_("Failed storing extended kvno "
663 krb5_storage_free(emem
);
668 ret
= krb5_storage_to_data(emem
, &keytab
);
669 krb5_storage_free(emem
);
671 krb5_set_error_message(context
, ret
,
672 N_("Failed converting keytab entry "
673 "to memory block for keytab %s", ""),
680 ret
= krb5_ret_int32(sp
, &len
);
681 if(ret
== KRB5_KT_END
) {
687 if(len
>= (int)keytab
.length
) {
688 krb5_storage_seek(sp
, -4, SEEK_CUR
);
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) {
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
);
705 krb5_storage_free(sp
);
706 _krb5_xunlock(context
, fd
);
711 static krb5_error_code KRB5_CALLCONV
712 fkt_remove_entry(krb5_context context
,
714 krb5_keytab_entry
*entry
)
717 krb5_kt_cursor cursor
;
718 off_t pos_start
, pos_end
;
722 ret
= fkt_start_seq_get_int(context
, id
, O_RDWR
| O_BINARY
| O_CLOEXEC
, 1, &cursor
);
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
)) {
730 unsigned char buf
[128];
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
));
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
);
747 krb5_clear_error_message (context
);
748 return KRB5_KT_NOTFOUND
;
753 const krb5_kt_ops krb5_fkt_ops
= {
769 const krb5_kt_ops krb5_wrfkt_ops
= {
785 const krb5_kt_ops krb5_javakt_ops
= {