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
48 typedef struct krb5_kcmcache
{
50 struct sockaddr_un path
;
54 #define KCMCACHE(X) ((krb5_kcmcache *)(X)->data.data)
55 #define CACHENAME(X) (KCMCACHE(X)->name)
56 #define KCMCURSOR(C) (*(uint32_t *)(C))
58 static krb5_error_code
59 try_door(krb5_context context
,
60 krb5_data
*request_data
,
61 krb5_data
*response_data
)
63 #ifdef HAVE_DOOR_CREATE
68 memset(&arg
, 0, sizeof(arg
));
70 fd
= open(k
->door_path
, O_RDWR
);
74 arg
.data_ptr
= request_data
->data
;
75 arg
.data_size
= request_data
->length
;
81 ret
= door_call(fd
, &arg
);
86 ret
= krb5_data_copy(response_data
, arg
.rbuf
, arg
.rsize
);
87 munmap(arg
.rbuf
, arg
.rsize
);
97 static krb5_error_code
98 try_unix_socket(krb5_context context
,
100 krb5_data
*request_data
,
101 krb5_data
*response_data
)
106 fd
= socket(AF_UNIX
, SOCK_STREAM
, 0);
110 if (connect(fd
, rk_UNCONST(&k
->path
), sizeof(k
->path
)) != 0) {
115 ret
= _krb5_send_and_recv_tcp(fd
, context
->kdc_timeout
,
116 request_data
, response_data
);
121 static krb5_error_code
122 kcm_send_request(krb5_context context
,
124 krb5_storage
*request
,
125 krb5_data
*response_data
)
128 krb5_data request_data
;
131 response_data
->data
= NULL
;
132 response_data
->length
= 0;
134 ret
= krb5_storage_to_data(request
, &request_data
);
136 krb5_clear_error_string(context
);
137 return KRB5_CC_NOMEM
;
140 ret
= KRB5_CC_NOSUPP
;
142 for (i
= 0; i
< context
->max_retries
; i
++) {
143 ret
= try_door(context
, &request_data
, response_data
);
144 if (ret
== 0 && response_data
->length
!= 0)
146 ret
= try_unix_socket(context
, k
, &request_data
, response_data
);
147 if (ret
== 0 && response_data
->length
!= 0)
151 krb5_data_free(&request_data
);
154 krb5_clear_error_string(context
);
155 ret
= KRB5_CC_NOSUPP
;
161 static krb5_error_code
162 kcm_storage_request(krb5_context context
,
163 kcm_operation opcode
,
164 krb5_storage
**storage_p
)
171 sp
= krb5_storage_emem();
173 krb5_set_error_string(context
, "malloc: out of memory");
174 return KRB5_CC_NOMEM
;
177 /* Send MAJOR | VERSION | OPCODE */
178 ret
= krb5_store_int8(sp
, KCM_PROTOCOL_VERSION_MAJOR
);
181 ret
= krb5_store_int8(sp
, KCM_PROTOCOL_VERSION_MINOR
);
184 ret
= krb5_store_int16(sp
, opcode
);
191 krb5_set_error_string(context
, "Failed to encode request");
192 krb5_storage_free(sp
);
198 static krb5_error_code
199 kcm_alloc(krb5_context context
, const char *name
, krb5_ccache
*id
)
204 k
= malloc(sizeof(*k
));
206 krb5_set_error_string(context
, "malloc: out of memory");
207 return KRB5_CC_NOMEM
;
211 k
->name
= strdup(name
);
212 if (k
->name
== NULL
) {
214 krb5_set_error_string(context
, "malloc: out of memory");
215 return KRB5_CC_NOMEM
;
220 path
= krb5_config_get_string_default(context
, NULL
,
226 k
->path
.sun_family
= AF_UNIX
;
227 strlcpy(k
->path
.sun_path
, path
, sizeof(k
->path
.sun_path
));
229 path
= krb5_config_get_string_default(context
, NULL
,
234 k
->door_path
= strdup(path
);
236 (*id
)->data
.data
= k
;
237 (*id
)->data
.length
= sizeof(*k
);
242 static krb5_error_code
243 kcm_call(krb5_context context
,
245 krb5_storage
*request
,
246 krb5_storage
**response_p
,
247 krb5_data
*response_data_p
)
249 krb5_data response_data
;
252 krb5_storage
*response
;
254 if (response_p
!= NULL
)
257 ret
= kcm_send_request(context
, k
, request
, &response_data
);
262 response
= krb5_storage_from_data(&response_data
);
263 if (response
== NULL
) {
264 krb5_data_free(&response_data
);
268 ret
= krb5_ret_int32(response
, &status
);
270 krb5_storage_free(response
);
271 krb5_data_free(&response_data
);
272 return KRB5_CC_FORMAT
;
276 krb5_storage_free(response
);
277 krb5_data_free(&response_data
);
281 if (response_p
!= NULL
) {
282 *response_data_p
= response_data
;
283 *response_p
= response
;
288 krb5_storage_free(response
);
289 krb5_data_free(&response_data
);
295 kcm_free(krb5_context context
, krb5_ccache
*id
)
297 krb5_kcmcache
*k
= KCMCACHE(*id
);
304 memset(k
, 0, sizeof(*k
));
305 krb5_data_free(&(*id
)->data
);
312 kcm_get_name(krb5_context context
,
315 return CACHENAME(id
);
318 static krb5_error_code
319 kcm_resolve(krb5_context context
, krb5_ccache
*id
, const char *res
)
321 return kcm_alloc(context
, res
, id
);
330 static krb5_error_code
331 kcm_gen_new(krb5_context context
, krb5_ccache
*id
)
335 krb5_storage
*request
, *response
;
336 krb5_data response_data
;
338 ret
= kcm_alloc(context
, NULL
, id
);
344 ret
= kcm_storage_request(context
, KCM_OP_GEN_NEW
, &request
);
346 kcm_free(context
, id
);
350 ret
= kcm_call(context
, k
, request
, &response
, &response_data
);
352 krb5_storage_free(request
);
353 kcm_free(context
, id
);
357 ret
= krb5_ret_stringz(response
, &k
->name
);
361 krb5_storage_free(request
);
362 krb5_storage_free(response
);
363 krb5_data_free(&response_data
);
366 kcm_free(context
, id
);
379 static krb5_error_code
380 kcm_initialize(krb5_context context
,
382 krb5_principal primary_principal
)
385 krb5_kcmcache
*k
= KCMCACHE(id
);
386 krb5_storage
*request
;
388 ret
= kcm_storage_request(context
, KCM_OP_INITIALIZE
, &request
);
392 ret
= krb5_store_stringz(request
, k
->name
);
394 krb5_storage_free(request
);
398 ret
= krb5_store_principal(request
, primary_principal
);
400 krb5_storage_free(request
);
404 ret
= kcm_call(context
, k
, request
, NULL
, NULL
);
406 krb5_storage_free(request
);
410 static krb5_error_code
411 kcm_close(krb5_context context
,
414 kcm_free(context
, &id
);
425 static krb5_error_code
426 kcm_destroy(krb5_context context
,
430 krb5_kcmcache
*k
= KCMCACHE(id
);
431 krb5_storage
*request
;
433 ret
= kcm_storage_request(context
, KCM_OP_DESTROY
, &request
);
437 ret
= krb5_store_stringz(request
, k
->name
);
439 krb5_storage_free(request
);
443 ret
= kcm_call(context
, k
, request
, NULL
, NULL
);
445 krb5_storage_free(request
);
457 static krb5_error_code
458 kcm_store_cred(krb5_context context
,
463 krb5_kcmcache
*k
= KCMCACHE(id
);
464 krb5_storage
*request
;
466 ret
= kcm_storage_request(context
, KCM_OP_STORE
, &request
);
470 ret
= krb5_store_stringz(request
, k
->name
);
472 krb5_storage_free(request
);
476 ret
= krb5_store_creds(request
, creds
);
478 krb5_storage_free(request
);
482 ret
= kcm_call(context
, k
, request
, NULL
, NULL
);
484 krb5_storage_free(request
);
498 static krb5_error_code
499 kcm_retrieve(krb5_context context
,
502 const krb5_creds
*mcred
,
506 krb5_kcmcache
*k
= KCMCACHE(id
);
507 krb5_storage
*request
, *response
;
508 krb5_data response_data
;
510 ret
= kcm_storage_request(context
, KCM_OP_RETRIEVE
, &request
);
514 ret
= krb5_store_stringz(request
, k
->name
);
516 krb5_storage_free(request
);
520 ret
= krb5_store_int32(request
, which
);
522 krb5_storage_free(request
);
526 ret
= krb5_store_creds_tag(request
, rk_UNCONST(mcred
));
528 krb5_storage_free(request
);
532 ret
= kcm_call(context
, k
, request
, &response
, &response_data
);
534 krb5_storage_free(request
);
538 ret
= krb5_ret_creds(response
, creds
);
542 krb5_storage_free(request
);
543 krb5_storage_free(response
);
544 krb5_data_free(&response_data
);
556 static krb5_error_code
557 kcm_get_principal(krb5_context context
,
559 krb5_principal
*principal
)
562 krb5_kcmcache
*k
= KCMCACHE(id
);
563 krb5_storage
*request
, *response
;
564 krb5_data response_data
;
566 ret
= kcm_storage_request(context
, KCM_OP_GET_PRINCIPAL
, &request
);
570 ret
= krb5_store_stringz(request
, k
->name
);
572 krb5_storage_free(request
);
576 ret
= kcm_call(context
, k
, request
, &response
, &response_data
);
578 krb5_storage_free(request
);
582 ret
= krb5_ret_principal(response
, principal
);
586 krb5_storage_free(request
);
587 krb5_storage_free(response
);
588 krb5_data_free(&response_data
);
601 static krb5_error_code
602 kcm_get_first (krb5_context context
,
604 krb5_cc_cursor
*cursor
)
607 krb5_kcmcache
*k
= KCMCACHE(id
);
608 krb5_storage
*request
, *response
;
609 krb5_data response_data
;
612 ret
= kcm_storage_request(context
, KCM_OP_GET_FIRST
, &request
);
616 ret
= krb5_store_stringz(request
, k
->name
);
618 krb5_storage_free(request
);
622 ret
= kcm_call(context
, k
, request
, &response
, &response_data
);
624 krb5_storage_free(request
);
628 ret
= krb5_ret_int32(response
, &tmp
);
632 krb5_storage_free(request
);
633 krb5_storage_free(response
);
634 krb5_data_free(&response_data
);
639 *cursor
= malloc(sizeof(tmp
));
641 return KRB5_CC_NOMEM
;
643 KCMCURSOR(*cursor
) = tmp
;
656 static krb5_error_code
657 kcm_get_next (krb5_context context
,
659 krb5_cc_cursor
*cursor
,
663 krb5_kcmcache
*k
= KCMCACHE(id
);
664 krb5_storage
*request
, *response
;
665 krb5_data response_data
;
667 ret
= kcm_storage_request(context
, KCM_OP_GET_NEXT
, &request
);
671 ret
= krb5_store_stringz(request
, k
->name
);
673 krb5_storage_free(request
);
677 ret
= krb5_store_int32(request
, KCMCURSOR(*cursor
));
679 krb5_storage_free(request
);
683 ret
= kcm_call(context
, k
, request
, &response
, &response_data
);
685 krb5_storage_free(request
);
689 ret
= krb5_ret_creds(response
, creds
);
693 krb5_storage_free(request
);
694 krb5_storage_free(response
);
695 krb5_data_free(&response_data
);
708 static krb5_error_code
709 kcm_end_get (krb5_context context
,
711 krb5_cc_cursor
*cursor
)
714 krb5_kcmcache
*k
= KCMCACHE(id
);
715 krb5_storage
*request
;
717 ret
= kcm_storage_request(context
, KCM_OP_END_GET
, &request
);
721 ret
= krb5_store_stringz(request
, k
->name
);
723 krb5_storage_free(request
);
727 ret
= krb5_store_int32(request
, KCMCURSOR(*cursor
));
729 krb5_storage_free(request
);
733 ret
= kcm_call(context
, k
, request
, NULL
, NULL
);
735 krb5_storage_free(request
);
739 krb5_storage_free(request
);
741 KCMCURSOR(*cursor
) = 0;
757 static krb5_error_code
758 kcm_remove_cred(krb5_context context
,
764 krb5_kcmcache
*k
= KCMCACHE(id
);
765 krb5_storage
*request
;
767 ret
= kcm_storage_request(context
, KCM_OP_REMOVE_CRED
, &request
);
771 ret
= krb5_store_stringz(request
, k
->name
);
773 krb5_storage_free(request
);
777 ret
= krb5_store_int32(request
, which
);
779 krb5_storage_free(request
);
783 ret
= krb5_store_creds_tag(request
, cred
);
785 krb5_storage_free(request
);
789 ret
= kcm_call(context
, k
, request
, NULL
, NULL
);
791 krb5_storage_free(request
);
795 static krb5_error_code
796 kcm_set_flags(krb5_context context
,
801 krb5_kcmcache
*k
= KCMCACHE(id
);
802 krb5_storage
*request
;
804 ret
= kcm_storage_request(context
, KCM_OP_SET_FLAGS
, &request
);
808 ret
= krb5_store_stringz(request
, k
->name
);
810 krb5_storage_free(request
);
814 ret
= krb5_store_int32(request
, flags
);
816 krb5_storage_free(request
);
820 ret
= kcm_call(context
, k
, request
, NULL
, NULL
);
822 krb5_storage_free(request
);
826 static krb5_error_code
827 kcm_get_version(krb5_context context
,
833 static krb5_error_code
834 kcm_move(krb5_context context
, krb5_ccache from
, krb5_ccache to
)
837 krb5_kcmcache
*oldk
= KCMCACHE(from
);
838 krb5_kcmcache
*newk
= KCMCACHE(to
);
839 krb5_storage
*request
;
841 ret
= kcm_storage_request(context
, KCM_OP_MOVE_CACHE
, &request
);
845 ret
= krb5_store_stringz(request
, oldk
->name
);
847 krb5_storage_free(request
);
851 ret
= krb5_store_stringz(request
, newk
->name
);
853 krb5_storage_free(request
);
856 ret
= kcm_call(context
, oldk
, request
, NULL
, NULL
);
858 krb5_storage_free(request
);
862 static krb5_error_code
863 kcm_default_name(krb5_context context
, char **str
)
865 return _krb5_expand_default_cc_name(context
,
866 KRB5_DEFAULT_CCNAME_KCM
,
871 * Variable containing the KCM based credential cache implemention.
873 * @ingroup krb5_ccache
876 const krb5_cc_ops krb5_kcm_ops
= {
902 _krb5_kcm_is_running(krb5_context context
)
905 krb5_ccache_data ccdata
;
906 krb5_ccache id
= &ccdata
;
907 krb5_boolean running
;
909 ret
= kcm_alloc(context
, NULL
, &id
);
913 running
= (_krb5_kcm_noop(context
, id
) == 0);
915 kcm_free(context
, &id
);
927 _krb5_kcm_noop(krb5_context context
,
931 krb5_kcmcache
*k
= KCMCACHE(id
);
932 krb5_storage
*request
;
934 ret
= kcm_storage_request(context
, KCM_OP_NOOP
, &request
);
938 ret
= kcm_call(context
, k
, request
, NULL
, NULL
);
940 krb5_storage_free(request
);
954 _krb5_kcm_chmod(krb5_context context
,
959 krb5_kcmcache
*k
= KCMCACHE(id
);
960 krb5_storage
*request
;
962 ret
= kcm_storage_request(context
, KCM_OP_CHMOD
, &request
);
966 ret
= krb5_store_stringz(request
, k
->name
);
968 krb5_storage_free(request
);
972 ret
= krb5_store_int16(request
, mode
);
974 krb5_storage_free(request
);
978 ret
= kcm_call(context
, k
, request
, NULL
, NULL
);
980 krb5_storage_free(request
);
995 _krb5_kcm_chown(krb5_context context
,
1000 krb5_error_code ret
;
1001 krb5_kcmcache
*k
= KCMCACHE(id
);
1002 krb5_storage
*request
;
1004 ret
= kcm_storage_request(context
, KCM_OP_CHOWN
, &request
);
1008 ret
= krb5_store_stringz(request
, k
->name
);
1010 krb5_storage_free(request
);
1014 ret
= krb5_store_int32(request
, uid
);
1016 krb5_storage_free(request
);
1020 ret
= krb5_store_int32(request
, gid
);
1022 krb5_storage_free(request
);
1026 ret
= kcm_call(context
, k
, request
, NULL
, NULL
);
1028 krb5_storage_free(request
);
1036 * ServerPrincipalPresent
1037 * ServerPrincipal OPTIONAL
1044 _krb5_kcm_get_initial_ticket(krb5_context context
,
1046 krb5_principal server
,
1049 krb5_error_code ret
;
1050 krb5_kcmcache
*k
= KCMCACHE(id
);
1051 krb5_storage
*request
;
1053 ret
= kcm_storage_request(context
, KCM_OP_GET_INITIAL_TICKET
, &request
);
1057 ret
= krb5_store_stringz(request
, k
->name
);
1059 krb5_storage_free(request
);
1063 ret
= krb5_store_int8(request
, (server
== NULL
) ? 0 : 1);
1065 krb5_storage_free(request
);
1069 if (server
!= NULL
) {
1070 ret
= krb5_store_principal(request
, server
);
1072 krb5_storage_free(request
);
1077 ret
= krb5_store_keyblock(request
, *key
);
1079 krb5_storage_free(request
);
1083 ret
= kcm_call(context
, k
, request
, NULL
, NULL
);
1085 krb5_storage_free(request
);
1101 _krb5_kcm_get_ticket(krb5_context context
,
1103 krb5_kdc_flags flags
,
1104 krb5_enctype enctype
,
1105 krb5_principal server
)
1107 krb5_error_code ret
;
1108 krb5_kcmcache
*k
= KCMCACHE(id
);
1109 krb5_storage
*request
;
1111 ret
= kcm_storage_request(context
, KCM_OP_GET_TICKET
, &request
);
1115 ret
= krb5_store_stringz(request
, k
->name
);
1117 krb5_storage_free(request
);
1121 ret
= krb5_store_int32(request
, flags
.i
);
1123 krb5_storage_free(request
);
1127 ret
= krb5_store_int32(request
, enctype
);
1129 krb5_storage_free(request
);
1133 ret
= krb5_store_principal(request
, server
);
1135 krb5_storage_free(request
);
1139 ret
= kcm_call(context
, k
, request
, NULL
, NULL
);
1141 krb5_storage_free(request
);
1145 #endif /* HAVE_KCM */