2 * Copyright (c) 2005, PADL Software Pty Ltd.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
16 * 3. Neither the name of PADL Software nor the names of its contributors
17 * may be used to endorse or promote products derived from this software
18 * without specific prior written permission.
20 * THIS SOFTWARE IS PROVIDED BY PADL SOFTWARE AND CONTRIBUTORS ``AS IS'' AND
21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 * ARE DISCLAIMED. IN NO EVENT SHALL PADL SOFTWARE OR CONTRIBUTORS BE LIABLE
24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33 #include "krb5_locl.h"
37 * Client library for Kerberos Credentials Manager (KCM) daemon
46 typedef struct krb5_kcmcache
{
48 struct sockaddr_un path
;
52 typedef struct krb5_kcm_cursor
{
59 #define KCMCACHE(X) ((krb5_kcmcache *)(X)->data.data)
60 #define CACHENAME(X) (KCMCACHE(X)->name)
61 #define KCMCURSOR(C) ((krb5_kcm_cursor)(C))
63 #ifdef HAVE_DOOR_CREATE
65 static krb5_error_code
66 try_door(krb5_context context
,
68 krb5_data
*request_data
,
69 krb5_data
*response_data
)
75 memset(&arg
, 0, sizeof(arg
));
77 fd
= open(k
->door_path
, O_RDWR
);
82 arg
.data_ptr
= request_data
->data
;
83 arg
.data_size
= request_data
->length
;
89 ret
= door_call(fd
, &arg
);
94 ret
= krb5_data_copy(response_data
, arg
.rbuf
, arg
.rsize
);
95 munmap(arg
.rbuf
, arg
.rsize
);
101 #endif /* HAVE_DOOR_CREATE */
103 static krb5_error_code
104 try_unix_socket(krb5_context context
,
106 krb5_data
*request_data
,
107 krb5_data
*response_data
)
112 fd
= socket(AF_UNIX
, SOCK_STREAM
| SOCK_CLOEXEC
, 0);
117 if (connect(fd
, rk_UNCONST(&k
->path
), sizeof(k
->path
)) != 0) {
122 ret
= _krb5_send_and_recv_tcp(fd
, context
->kdc_timeout
,
123 request_data
, response_data
);
128 static krb5_error_code
129 kcm_send_request(krb5_context context
,
131 krb5_storage
*request
,
132 krb5_data
*response_data
)
135 krb5_data request_data
;
138 response_data
->data
= NULL
;
139 response_data
->length
= 0;
141 ret
= krb5_storage_to_data(request
, &request_data
);
143 krb5_clear_error_message(context
);
144 return KRB5_CC_NOMEM
;
147 ret
= KRB5_CC_NOSUPP
;
149 for (i
= 0; i
< context
->max_retries
; i
++) {
150 #ifdef HAVE_DOOR_CREATE
151 ret
= try_door(context
, k
, &request_data
, response_data
);
152 if (ret
== 0 && response_data
->length
!= 0)
155 ret
= try_unix_socket(context
, k
, &request_data
, response_data
);
156 if (ret
== 0 && response_data
->length
!= 0)
160 krb5_data_free(&request_data
);
163 krb5_clear_error_message(context
);
164 ret
= KRB5_CC_NOSUPP
;
170 static krb5_error_code
171 kcm_storage_request(krb5_context context
,
172 kcm_operation opcode
,
173 krb5_storage
**storage_p
)
180 sp
= krb5_storage_emem();
182 krb5_set_error_message(context
, KRB5_CC_NOMEM
, N_("malloc: out of memory", ""));
183 return KRB5_CC_NOMEM
;
186 /* Send MAJOR | VERSION | OPCODE */
187 ret
= krb5_store_int8(sp
, KCM_PROTOCOL_VERSION_MAJOR
);
190 ret
= krb5_store_int8(sp
, KCM_PROTOCOL_VERSION_MINOR
);
193 ret
= krb5_store_int16(sp
, opcode
);
200 krb5_set_error_message(context
, ret
,
201 N_("Failed to encode KCM request", ""));
202 krb5_storage_free(sp
);
208 static krb5_error_code
209 kcm_alloc(krb5_context context
, const char *name
, krb5_ccache
*id
)
214 k
= malloc(sizeof(*k
));
216 krb5_set_error_message(context
, KRB5_CC_NOMEM
,
217 N_("malloc: out of memory", ""));
218 return KRB5_CC_NOMEM
;
222 k
->name
= strdup(name
);
223 if (k
->name
== NULL
) {
225 krb5_set_error_message(context
, KRB5_CC_NOMEM
,
226 N_("malloc: out of memory", ""));
227 return KRB5_CC_NOMEM
;
232 path
= krb5_config_get_string_default(context
, NULL
,
238 k
->path
.sun_family
= AF_UNIX
;
239 strlcpy(k
->path
.sun_path
, path
, sizeof(k
->path
.sun_path
));
241 path
= krb5_config_get_string_default(context
, NULL
,
246 k
->door_path
= strdup(path
);
248 (*id
)->data
.data
= k
;
249 (*id
)->data
.length
= sizeof(*k
);
254 static krb5_error_code
255 kcm_call(krb5_context context
,
257 krb5_storage
*request
,
258 krb5_storage
**response_p
,
259 krb5_data
*response_data_p
)
261 krb5_data response_data
;
264 krb5_storage
*response
;
266 if (response_p
!= NULL
)
269 ret
= kcm_send_request(context
, k
, request
, &response_data
);
274 response
= krb5_storage_from_data(&response_data
);
275 if (response
== NULL
) {
276 krb5_data_free(&response_data
);
280 ret
= krb5_ret_int32(response
, &status
);
282 krb5_storage_free(response
);
283 krb5_data_free(&response_data
);
284 return KRB5_CC_FORMAT
;
288 krb5_storage_free(response
);
289 krb5_data_free(&response_data
);
293 if (response_p
!= NULL
) {
294 *response_data_p
= response_data
;
295 *response_p
= response
;
300 krb5_storage_free(response
);
301 krb5_data_free(&response_data
);
307 kcm_free(krb5_context context
, krb5_ccache
*id
)
309 krb5_kcmcache
*k
= KCMCACHE(*id
);
316 memset(k
, 0, sizeof(*k
));
317 krb5_data_free(&(*id
)->data
);
322 kcm_get_name(krb5_context context
,
325 return CACHENAME(id
);
328 static krb5_error_code
329 kcm_resolve(krb5_context context
, krb5_ccache
*id
, const char *res
)
331 return kcm_alloc(context
, res
, id
);
340 static krb5_error_code
341 kcm_gen_new(krb5_context context
, krb5_ccache
*id
)
345 krb5_storage
*request
, *response
;
346 krb5_data response_data
;
348 ret
= kcm_alloc(context
, NULL
, id
);
354 ret
= kcm_storage_request(context
, KCM_OP_GEN_NEW
, &request
);
356 kcm_free(context
, id
);
360 ret
= kcm_call(context
, k
, request
, &response
, &response_data
);
362 krb5_storage_free(request
);
363 kcm_free(context
, id
);
367 ret
= krb5_ret_stringz(response
, &k
->name
);
371 krb5_storage_free(request
);
372 krb5_storage_free(response
);
373 krb5_data_free(&response_data
);
376 kcm_free(context
, id
);
389 static krb5_error_code
390 kcm_initialize(krb5_context context
,
392 krb5_principal primary_principal
)
395 krb5_kcmcache
*k
= KCMCACHE(id
);
396 krb5_storage
*request
;
398 ret
= kcm_storage_request(context
, KCM_OP_INITIALIZE
, &request
);
402 ret
= krb5_store_stringz(request
, k
->name
);
404 krb5_storage_free(request
);
408 ret
= krb5_store_principal(request
, primary_principal
);
410 krb5_storage_free(request
);
414 ret
= kcm_call(context
, k
, request
, NULL
, NULL
);
416 krb5_storage_free(request
);
420 static krb5_error_code
421 kcm_close(krb5_context context
,
424 kcm_free(context
, &id
);
435 static krb5_error_code
436 kcm_destroy(krb5_context context
,
440 krb5_kcmcache
*k
= KCMCACHE(id
);
441 krb5_storage
*request
;
443 ret
= kcm_storage_request(context
, KCM_OP_DESTROY
, &request
);
447 ret
= krb5_store_stringz(request
, k
->name
);
449 krb5_storage_free(request
);
453 ret
= kcm_call(context
, k
, request
, NULL
, NULL
);
455 krb5_storage_free(request
);
467 static krb5_error_code
468 kcm_store_cred(krb5_context context
,
473 krb5_kcmcache
*k
= KCMCACHE(id
);
474 krb5_storage
*request
;
476 ret
= kcm_storage_request(context
, KCM_OP_STORE
, &request
);
480 ret
= krb5_store_stringz(request
, k
->name
);
482 krb5_storage_free(request
);
486 ret
= krb5_store_creds(request
, creds
);
488 krb5_storage_free(request
);
492 ret
= kcm_call(context
, k
, request
, NULL
, NULL
);
494 krb5_storage_free(request
);
508 static krb5_error_code
509 kcm_retrieve(krb5_context context
,
512 const krb5_creds
*mcred
,
516 krb5_kcmcache
*k
= KCMCACHE(id
);
517 krb5_storage
*request
, *response
;
518 krb5_data response_data
;
520 ret
= kcm_storage_request(context
, KCM_OP_RETRIEVE
, &request
);
524 ret
= krb5_store_stringz(request
, k
->name
);
526 krb5_storage_free(request
);
530 ret
= krb5_store_int32(request
, which
);
532 krb5_storage_free(request
);
536 ret
= krb5_store_creds_tag(request
, rk_UNCONST(mcred
));
538 krb5_storage_free(request
);
542 ret
= kcm_call(context
, k
, request
, &response
, &response_data
);
544 krb5_storage_free(request
);
548 ret
= krb5_ret_creds(response
, creds
);
552 krb5_storage_free(request
);
553 krb5_storage_free(response
);
554 krb5_data_free(&response_data
);
566 static krb5_error_code
567 kcm_get_principal(krb5_context context
,
569 krb5_principal
*principal
)
572 krb5_kcmcache
*k
= KCMCACHE(id
);
573 krb5_storage
*request
, *response
;
574 krb5_data response_data
;
576 ret
= kcm_storage_request(context
, KCM_OP_GET_PRINCIPAL
, &request
);
580 ret
= krb5_store_stringz(request
, k
->name
);
582 krb5_storage_free(request
);
586 ret
= kcm_call(context
, k
, request
, &response
, &response_data
);
588 krb5_storage_free(request
);
592 ret
= krb5_ret_principal(response
, principal
);
596 krb5_storage_free(request
);
597 krb5_storage_free(response
);
598 krb5_data_free(&response_data
);
611 static krb5_error_code
612 kcm_get_first (krb5_context context
,
614 krb5_cc_cursor
*cursor
)
618 krb5_kcmcache
*k
= KCMCACHE(id
);
619 krb5_storage
*request
, *response
;
620 krb5_data response_data
;
622 ret
= kcm_storage_request(context
, KCM_OP_GET_FIRST
, &request
);
626 ret
= krb5_store_stringz(request
, k
->name
);
628 krb5_storage_free(request
);
632 ret
= kcm_call(context
, k
, request
, &response
, &response_data
);
633 krb5_storage_free(request
);
637 c
= calloc(1, sizeof(*c
));
640 krb5_set_error_message(context
, ret
,
641 N_("malloc: out of memory", ""));
650 sret
= krb5_storage_read(response
, &uuid
, sizeof(uuid
));
654 } else if (sret
!= sizeof(uuid
)) {
659 ptr
= realloc(c
->uuids
, sizeof(c
->uuids
[0]) * (c
->length
+ 1));
663 krb5_set_error_message(context
, ENOMEM
,
664 N_("malloc: out of memory", ""));
669 memcpy(&c
->uuids
[c
->length
], &uuid
, sizeof(uuid
));
673 krb5_storage_free(response
);
674 krb5_data_free(&response_data
);
695 static krb5_error_code
696 kcm_get_next (krb5_context context
,
698 krb5_cc_cursor
*cursor
,
702 krb5_kcmcache
*k
= KCMCACHE(id
);
703 krb5_kcm_cursor c
= KCMCURSOR(*cursor
);
704 krb5_storage
*request
, *response
;
705 krb5_data response_data
;
710 if (c
->offset
>= c
->length
)
713 ret
= kcm_storage_request(context
, KCM_OP_GET_NEXT
, &request
);
717 ret
= krb5_store_stringz(request
, k
->name
);
719 krb5_storage_free(request
);
723 sret
= krb5_storage_write(request
,
724 &c
->uuids
[c
->offset
],
725 sizeof(c
->uuids
[c
->offset
]));
727 if (sret
!= sizeof(c
->uuids
[c
->offset
])) {
728 krb5_storage_free(request
);
729 krb5_clear_error_message(context
);
733 ret
= kcm_call(context
, k
, request
, &response
, &response_data
);
734 krb5_storage_free(request
);
735 if (ret
== KRB5_CC_END
) {
739 ret
= krb5_ret_creds(response
, creds
);
743 krb5_storage_free(response
);
744 krb5_data_free(&response_data
);
757 static krb5_error_code
758 kcm_end_get (krb5_context context
,
760 krb5_cc_cursor
*cursor
)
763 krb5_kcmcache
*k
= KCMCACHE(id
);
764 krb5_kcm_cursor c
= KCMCURSOR(*cursor
);
765 krb5_storage
*request
;
767 ret
= kcm_storage_request(context
, KCM_OP_END_GET
, &request
);
771 ret
= krb5_store_stringz(request
, k
->name
);
773 krb5_storage_free(request
);
777 ret
= kcm_call(context
, k
, request
, NULL
, NULL
);
778 krb5_storage_free(request
);
799 static krb5_error_code
800 kcm_remove_cred(krb5_context context
,
806 krb5_kcmcache
*k
= KCMCACHE(id
);
807 krb5_storage
*request
;
809 ret
= kcm_storage_request(context
, KCM_OP_REMOVE_CRED
, &request
);
813 ret
= krb5_store_stringz(request
, k
->name
);
815 krb5_storage_free(request
);
819 ret
= krb5_store_int32(request
, which
);
821 krb5_storage_free(request
);
825 ret
= krb5_store_creds_tag(request
, cred
);
827 krb5_storage_free(request
);
831 ret
= kcm_call(context
, k
, request
, NULL
, NULL
);
833 krb5_storage_free(request
);
837 static krb5_error_code
838 kcm_set_flags(krb5_context context
,
843 krb5_kcmcache
*k
= KCMCACHE(id
);
844 krb5_storage
*request
;
846 ret
= kcm_storage_request(context
, KCM_OP_SET_FLAGS
, &request
);
850 ret
= krb5_store_stringz(request
, k
->name
);
852 krb5_storage_free(request
);
856 ret
= krb5_store_int32(request
, flags
);
858 krb5_storage_free(request
);
862 ret
= kcm_call(context
, k
, request
, NULL
, NULL
);
864 krb5_storage_free(request
);
869 kcm_get_version(krb5_context context
,
875 static krb5_error_code
876 kcm_move(krb5_context context
, krb5_ccache from
, krb5_ccache to
)
879 krb5_kcmcache
*oldk
= KCMCACHE(from
);
880 krb5_kcmcache
*newk
= KCMCACHE(to
);
881 krb5_storage
*request
;
883 ret
= kcm_storage_request(context
, KCM_OP_MOVE_CACHE
, &request
);
887 ret
= krb5_store_stringz(request
, oldk
->name
);
889 krb5_storage_free(request
);
893 ret
= krb5_store_stringz(request
, newk
->name
);
895 krb5_storage_free(request
);
898 ret
= kcm_call(context
, oldk
, request
, NULL
, NULL
);
900 krb5_storage_free(request
);
904 static krb5_error_code
905 kcm_default_name(krb5_context context
, char **str
)
907 return _krb5_expand_default_cc_name(context
,
908 KRB5_DEFAULT_CCNAME_KCM
,
912 static krb5_error_code
913 kcm_lastchange(krb5_context context
, krb5_ccache id
, krb5_timestamp
*mtime
)
920 * Variable containing the KCM based credential cache implemention.
922 * @ingroup krb5_ccache
925 KRB5_LIB_VARIABLE
const krb5_cc_ops krb5_kcm_ops
= {
953 _krb5_kcm_is_running(krb5_context context
)
956 krb5_ccache_data ccdata
;
957 krb5_ccache id
= &ccdata
;
958 krb5_boolean running
;
960 ret
= kcm_alloc(context
, NULL
, &id
);
964 running
= (_krb5_kcm_noop(context
, id
) == 0);
966 kcm_free(context
, &id
);
978 _krb5_kcm_noop(krb5_context context
,
982 krb5_kcmcache
*k
= KCMCACHE(id
);
983 krb5_storage
*request
;
985 ret
= kcm_storage_request(context
, KCM_OP_NOOP
, &request
);
989 ret
= kcm_call(context
, k
, request
, NULL
, NULL
);
991 krb5_storage_free(request
);
1005 _krb5_kcm_chmod(krb5_context context
,
1009 krb5_error_code ret
;
1010 krb5_kcmcache
*k
= KCMCACHE(id
);
1011 krb5_storage
*request
;
1013 ret
= kcm_storage_request(context
, KCM_OP_CHMOD
, &request
);
1017 ret
= krb5_store_stringz(request
, k
->name
);
1019 krb5_storage_free(request
);
1023 ret
= krb5_store_int16(request
, mode
);
1025 krb5_storage_free(request
);
1029 ret
= kcm_call(context
, k
, request
, NULL
, NULL
);
1031 krb5_storage_free(request
);
1046 _krb5_kcm_chown(krb5_context context
,
1051 krb5_error_code ret
;
1052 krb5_kcmcache
*k
= KCMCACHE(id
);
1053 krb5_storage
*request
;
1055 ret
= kcm_storage_request(context
, KCM_OP_CHOWN
, &request
);
1059 ret
= krb5_store_stringz(request
, k
->name
);
1061 krb5_storage_free(request
);
1065 ret
= krb5_store_int32(request
, uid
);
1067 krb5_storage_free(request
);
1071 ret
= krb5_store_int32(request
, gid
);
1073 krb5_storage_free(request
);
1077 ret
= kcm_call(context
, k
, request
, NULL
, NULL
);
1079 krb5_storage_free(request
);
1087 * ServerPrincipalPresent
1088 * ServerPrincipal OPTIONAL
1095 _krb5_kcm_get_initial_ticket(krb5_context context
,
1097 krb5_principal server
,
1100 krb5_error_code ret
;
1101 krb5_kcmcache
*k
= KCMCACHE(id
);
1102 krb5_storage
*request
;
1104 ret
= kcm_storage_request(context
, KCM_OP_GET_INITIAL_TICKET
, &request
);
1108 ret
= krb5_store_stringz(request
, k
->name
);
1110 krb5_storage_free(request
);
1114 ret
= krb5_store_int8(request
, (server
== NULL
) ? 0 : 1);
1116 krb5_storage_free(request
);
1120 if (server
!= NULL
) {
1121 ret
= krb5_store_principal(request
, server
);
1123 krb5_storage_free(request
);
1128 ret
= krb5_store_keyblock(request
, *key
);
1130 krb5_storage_free(request
);
1134 ret
= kcm_call(context
, k
, request
, NULL
, NULL
);
1136 krb5_storage_free(request
);
1152 _krb5_kcm_get_ticket(krb5_context context
,
1154 krb5_kdc_flags flags
,
1155 krb5_enctype enctype
,
1156 krb5_principal server
)
1158 krb5_error_code ret
;
1159 krb5_kcmcache
*k
= KCMCACHE(id
);
1160 krb5_storage
*request
;
1162 ret
= kcm_storage_request(context
, KCM_OP_GET_TICKET
, &request
);
1166 ret
= krb5_store_stringz(request
, k
->name
);
1168 krb5_storage_free(request
);
1172 ret
= krb5_store_int32(request
, flags
.i
);
1174 krb5_storage_free(request
);
1178 ret
= krb5_store_int32(request
, enctype
);
1180 krb5_storage_free(request
);
1184 ret
= krb5_store_principal(request
, server
);
1186 krb5_storage_free(request
);
1190 ret
= kcm_call(context
, k
, request
, NULL
, NULL
);
1192 krb5_storage_free(request
);
1196 #endif /* HAVE_KCM */