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 krb5_set_error_message(context
, ENOMEM
, N_("malloc: out of memory", ""));
66 ret
= krb5_storage_read(sp
, data
->data
, size
);
68 return (ret
< 0)? errno
: KRB5_KT_END
;
72 static krb5_error_code
73 krb5_kt_ret_string(krb5_context context
,
75 heim_general_string
*data
)
79 ret
= krb5_ret_int16(sp
, &size
);
82 *data
= malloc(size
+ 1);
84 krb5_set_error_message(context
, ENOMEM
, N_("malloc: out of memory", ""));
87 ret
= krb5_storage_read(sp
, *data
, size
);
90 return (ret
< 0)? errno
: KRB5_KT_END
;
94 static krb5_error_code
95 krb5_kt_store_data(krb5_context context
,
100 ret
= krb5_store_int16(sp
, data
.length
);
103 ret
= krb5_storage_write(sp
, data
.data
, data
.length
);
104 if(ret
!= data
.length
){
112 static krb5_error_code
113 krb5_kt_store_string(krb5_storage
*sp
,
114 heim_general_string data
)
117 size_t len
= strlen(data
);
118 ret
= krb5_store_int16(sp
, len
);
121 ret
= krb5_storage_write(sp
, data
, len
);
130 static krb5_error_code
131 krb5_kt_ret_keyblock(krb5_context context
,
132 struct fkt_data
*fkt
,
139 ret
= krb5_ret_int16(sp
, &tmp
); /* keytype + etype */
141 krb5_set_error_message(context
, ret
,
142 N_("Cant read keyblock from file %s", ""),
147 ret
= krb5_kt_ret_data(context
, sp
, &p
->keyvalue
);
149 krb5_set_error_message(context
, ret
,
150 N_("Cant read keyblock from file %s", ""),
155 static krb5_error_code
156 krb5_kt_store_keyblock(krb5_context context
,
157 struct fkt_data
*fkt
,
163 ret
= krb5_store_int16(sp
, p
->keytype
); /* keytype + etype */
165 krb5_set_error_message(context
, ret
,
166 N_("Cant store keyblock to file %s", ""),
170 ret
= krb5_kt_store_data(context
, sp
, p
->keyvalue
);
172 krb5_set_error_message(context
, ret
,
173 N_("Cant store keyblock to file %s", ""),
179 static krb5_error_code
180 krb5_kt_ret_principal(krb5_context context
,
181 struct fkt_data
*fkt
,
183 krb5_principal
*princ
)
192 krb5_set_error_message(context
, ENOMEM
,
193 N_("malloc: out of memory", ""));
197 ret
= krb5_ret_int16(sp
, &len
);
199 krb5_set_error_message(context
, ret
,
200 N_("Failed decoding length of "
201 "keytab principal in keytab file %s", ""),
205 if(krb5_storage_is_flags(sp
, KRB5_STORAGE_PRINCIPAL_WRONG_NUM_COMPONENTS
))
209 krb5_set_error_message(context
, ret
,
210 N_("Keytab principal contains "
211 "invalid length in keytab %s", ""),
215 ret
= krb5_kt_ret_string(context
, sp
, &p
->realm
);
217 krb5_set_error_message(context
, ret
,
218 N_("Can't read realm from keytab: %s", ""),
222 p
->name
.name_string
.val
= calloc(len
, sizeof(*p
->name
.name_string
.val
));
223 if(p
->name
.name_string
.val
== NULL
) {
225 krb5_set_error_message(context
, ret
, N_("malloc: out of memory", ""));
228 p
->name
.name_string
.len
= len
;
229 for(i
= 0; i
< p
->name
.name_string
.len
; i
++){
230 ret
= krb5_kt_ret_string(context
, sp
, p
->name
.name_string
.val
+ i
);
232 krb5_set_error_message(context
, ret
,
233 N_("Can't read principal from "
239 if (krb5_storage_is_flags(sp
, KRB5_STORAGE_PRINCIPAL_NO_NAME_TYPE
))
240 p
->name
.name_type
= KRB5_NT_UNKNOWN
;
243 ret
= krb5_ret_int32(sp
, &tmp32
);
244 p
->name
.name_type
= tmp32
;
246 krb5_set_error_message(context
, ret
,
247 N_("Can't read name-type from "
256 krb5_free_principal(context
, p
);
260 static krb5_error_code
261 krb5_kt_store_principal(krb5_context context
,
268 if(krb5_storage_is_flags(sp
, KRB5_STORAGE_PRINCIPAL_WRONG_NUM_COMPONENTS
))
269 ret
= krb5_store_int16(sp
, p
->name
.name_string
.len
+ 1);
271 ret
= krb5_store_int16(sp
, p
->name
.name_string
.len
);
273 ret
= krb5_kt_store_string(sp
, p
->realm
);
275 for(i
= 0; i
< p
->name
.name_string
.len
; i
++){
276 ret
= krb5_kt_store_string(sp
, p
->name
.name_string
.val
[i
]);
280 if(!krb5_storage_is_flags(sp
, KRB5_STORAGE_PRINCIPAL_NO_NAME_TYPE
)) {
281 ret
= krb5_store_int32(sp
, p
->name
.name_type
);
289 static krb5_error_code
290 fkt_resolve(krb5_context context
, const char *name
, krb5_keytab id
)
294 d
= malloc(sizeof(*d
));
296 krb5_set_error_message(context
, ENOMEM
, N_("malloc: out of memory", ""));
299 d
->filename
= strdup(name
);
300 if(d
->filename
== NULL
) {
302 krb5_set_error_message(context
, ENOMEM
, N_("malloc: out of memory", ""));
310 static krb5_error_code
311 fkt_resolve_java14(krb5_context context
, const char *name
, krb5_keytab id
)
315 ret
= fkt_resolve(context
, name
, id
);
317 struct fkt_data
*d
= id
->data
;
318 d
->flags
|= KRB5_KT_FL_JAVA
;
323 static krb5_error_code
324 fkt_close(krb5_context context
, krb5_keytab id
)
326 struct fkt_data
*d
= id
->data
;
332 static krb5_error_code
333 fkt_destroy(krb5_context context
, krb5_keytab id
)
335 struct fkt_data
*d
= id
->data
;
336 _krb5_erase_file(context
, d
->filename
);
340 static krb5_error_code
341 fkt_get_name(krb5_context context
,
346 /* This function is XXX */
347 struct fkt_data
*d
= id
->data
;
348 strlcpy(name
, d
->filename
, namesize
);
353 storage_set_flags(krb5_context context
, krb5_storage
*sp
, int vno
)
358 flags
|= KRB5_STORAGE_PRINCIPAL_WRONG_NUM_COMPONENTS
;
359 flags
|= KRB5_STORAGE_PRINCIPAL_NO_NAME_TYPE
;
360 flags
|= KRB5_STORAGE_HOST_BYTEORDER
;
366 "storage_set_flags called with bad vno (%d)", vno
);
368 krb5_storage_set_flags(sp
, flags
);
371 static krb5_error_code
372 fkt_start_seq_get_int(krb5_context context
,
380 struct fkt_data
*d
= id
->data
;
382 c
->fd
= open (d
->filename
, flags
);
385 krb5_set_error_message(context
, ret
,
386 N_("keytab %s open failed: %s", ""),
387 d
->filename
, strerror(ret
));
391 ret
= _krb5_xlock(context
, c
->fd
, exclusive
, d
->filename
);
396 c
->sp
= krb5_storage_from_fd(c
->fd
);
398 _krb5_xunlock(context
, c
->fd
);
400 krb5_set_error_message(context
, ENOMEM
,
401 N_("malloc: out of memory", ""));
404 krb5_storage_set_eof_code(c
->sp
, KRB5_KT_END
);
405 ret
= krb5_ret_int8(c
->sp
, &pvno
);
407 krb5_storage_free(c
->sp
);
408 _krb5_xunlock(context
, c
->fd
);
410 krb5_clear_error_message(context
);
414 krb5_storage_free(c
->sp
);
415 _krb5_xunlock(context
, c
->fd
);
417 krb5_clear_error_message (context
);
418 return KRB5_KEYTAB_BADVNO
;
420 ret
= krb5_ret_int8(c
->sp
, &tag
);
422 krb5_storage_free(c
->sp
);
423 _krb5_xunlock(context
, c
->fd
);
425 krb5_clear_error_message(context
);
429 storage_set_flags(context
, c
->sp
, id
->version
);
433 static krb5_error_code
434 fkt_start_seq_get(krb5_context context
,
438 return fkt_start_seq_get_int(context
, id
, O_RDONLY
| O_BINARY
| O_CLOEXEC
, 0, c
);
441 static krb5_error_code
442 fkt_next_entry_int(krb5_context context
,
444 krb5_keytab_entry
*entry
,
445 krb5_kt_cursor
*cursor
,
449 struct fkt_data
*d
= id
->data
;
457 pos
= krb5_storage_seek(cursor
->sp
, 0, SEEK_CUR
);
459 ret
= krb5_ret_int32(cursor
->sp
, &len
);
463 pos
= krb5_storage_seek(cursor
->sp
, -len
, SEEK_CUR
);
466 ret
= krb5_kt_ret_principal (context
, d
, cursor
->sp
, &entry
->principal
);
469 ret
= krb5_ret_uint32(cursor
->sp
, &utmp32
);
470 entry
->timestamp
= utmp32
;
473 ret
= krb5_ret_int8(cursor
->sp
, &tmp8
);
477 ret
= krb5_kt_ret_keyblock (context
, d
, cursor
->sp
, &entry
->keyblock
);
480 /* there might be a 32 bit kvno here
481 * if it's zero, assume that the 8bit one was right,
482 * otherwise trust the new value */
483 curpos
= krb5_storage_seek(cursor
->sp
, 0, SEEK_CUR
);
484 if(len
+ 4 + pos
- curpos
>= 4) {
485 ret
= krb5_ret_int32(cursor
->sp
, &tmp32
);
486 if (ret
== 0 && tmp32
!= 0)
489 /* there might be a flags field here */
490 if(len
+ 4 + pos
- curpos
>= 8) {
491 ret
= krb5_ret_uint32(cursor
->sp
, &utmp32
);
493 entry
->flags
= utmp32
;
497 entry
->aliases
= NULL
;
499 if(start
) *start
= pos
;
500 if(end
) *end
= pos
+ 4 + len
;
502 krb5_storage_seek(cursor
->sp
, pos
+ 4 + len
, SEEK_SET
);
506 static krb5_error_code
507 fkt_next_entry(krb5_context context
,
509 krb5_keytab_entry
*entry
,
510 krb5_kt_cursor
*cursor
)
512 return fkt_next_entry_int(context
, id
, entry
, cursor
, NULL
, NULL
);
515 static krb5_error_code
516 fkt_end_seq_get(krb5_context context
,
518 krb5_kt_cursor
*cursor
)
520 krb5_storage_free(cursor
->sp
);
521 _krb5_xunlock(context
, cursor
->fd
);
526 static krb5_error_code
527 fkt_setup_keytab(krb5_context context
,
532 ret
= krb5_store_int8(sp
, 5);
536 id
->version
= KRB5_KT_VNO
;
537 return krb5_store_int8 (sp
, id
->version
);
540 static krb5_error_code
541 fkt_add_entry(krb5_context context
,
543 krb5_keytab_entry
*entry
)
548 struct fkt_data
*d
= id
->data
;
552 fd
= open (d
->filename
, O_RDWR
| O_BINARY
| O_CLOEXEC
);
554 fd
= open (d
->filename
, O_RDWR
| O_CREAT
| O_EXCL
| O_BINARY
| O_CLOEXEC
, 0600);
557 krb5_set_error_message(context
, ret
,
558 N_("open(%s): %s", ""), d
->filename
,
564 ret
= _krb5_xlock(context
, fd
, 1, d
->filename
);
569 sp
= krb5_storage_from_fd(fd
);
570 krb5_storage_set_eof_code(sp
, KRB5_KT_END
);
571 ret
= fkt_setup_keytab(context
, id
, sp
);
575 storage_set_flags(context
, sp
, id
->version
);
581 ret
= _krb5_xlock(context
, fd
, 1, d
->filename
);
586 sp
= krb5_storage_from_fd(fd
);
587 krb5_storage_set_eof_code(sp
, KRB5_KT_END
);
588 ret
= krb5_ret_int8(sp
, &pvno
);
590 /* we probably have a zero byte file, so try to set it up
592 ret
= fkt_setup_keytab(context
, id
, sp
);
594 krb5_set_error_message(context
, ret
,
595 N_("%s: keytab is corrupted: %s", ""),
596 d
->filename
, strerror(ret
));
599 storage_set_flags(context
, sp
, id
->version
);
602 ret
= KRB5_KEYTAB_BADVNO
;
603 krb5_set_error_message(context
, ret
,
604 N_("Bad version in keytab %s", ""),
608 ret
= krb5_ret_int8 (sp
, &tag
);
610 krb5_set_error_message(context
, ret
,
611 N_("failed reading tag from "
617 storage_set_flags(context
, sp
, id
->version
);
623 emem
= krb5_storage_emem();
626 krb5_set_error_message(context
, ret
,
627 N_("malloc: out of memory", ""));
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", ""),
695 ret
= krb5_ret_int32(sp
, &len
);
696 if(ret
== KRB5_KT_END
) {
702 if(len
>= keytab
.length
) {
703 krb5_storage_seek(sp
, -4, SEEK_CUR
);
707 krb5_storage_seek(sp
, len
, SEEK_CUR
);
709 ret
= krb5_store_int32(sp
, len
);
710 if(krb5_storage_write(sp
, keytab
.data
, keytab
.length
) < 0) {
712 krb5_set_error_message(context
, ret
,
713 N_("Failed writing keytab block "
714 "in keytab %s: %s", ""),
715 d
->filename
, strerror(ret
));
717 memset(keytab
.data
, 0, keytab
.length
);
718 krb5_data_free(&keytab
);
720 krb5_storage_free(sp
);
721 _krb5_xunlock(context
, fd
);
726 static krb5_error_code
727 fkt_remove_entry(krb5_context context
,
729 krb5_keytab_entry
*entry
)
732 krb5_kt_cursor cursor
;
733 off_t pos_start
, pos_end
;
737 ret
= fkt_start_seq_get_int(context
, id
, O_RDWR
| O_BINARY
| O_CLOEXEC
, 1, &cursor
);
739 goto out
; /* return other error here? */
740 while(fkt_next_entry_int(context
, id
, &e
, &cursor
,
741 &pos_start
, &pos_end
) == 0) {
742 if(krb5_kt_compare(context
, &e
, entry
->principal
,
743 entry
->vno
, entry
->keyblock
.keytype
)) {
745 unsigned char buf
[128];
747 krb5_storage_seek(cursor
.sp
, pos_start
, SEEK_SET
);
748 len
= pos_end
- pos_start
- 4;
749 krb5_store_int32(cursor
.sp
, -len
);
750 memset(buf
, 0, sizeof(buf
));
752 krb5_storage_write(cursor
.sp
, buf
, min(len
, sizeof(buf
)));
753 len
-= min(len
, sizeof(buf
));
756 krb5_kt_free_entry(context
, &e
);
758 krb5_kt_end_seq_get(context
, id
, &cursor
);
761 krb5_clear_error_message (context
);
762 return KRB5_KT_NOTFOUND
;
767 const krb5_kt_ops krb5_fkt_ops
= {
781 const krb5_kt_ops krb5_wrfkt_ops
= {
795 const krb5_kt_ops krb5_javakt_ops
= {