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
, const krb5_kcmcache
*k
,
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
, const krb5_kcmcache
*k
,
99 krb5_data
*request_data
,
100 krb5_data
*response_data
)
105 fd
= socket(AF_UNIX
, SOCK_STREAM
, 0);
109 if (connect(fd
, rk_UNCONST(&k
->path
), sizeof(k
->path
)) != 0) {
114 ret
= _krb5_send_and_recv_tcp(fd
, context
->kdc_timeout
,
115 request_data
, response_data
);
120 static krb5_error_code
121 kcm_send_request(krb5_context context
,
123 krb5_storage
*request
,
124 krb5_data
*response_data
)
127 krb5_data request_data
;
130 response_data
->data
= NULL
;
131 response_data
->length
= 0;
133 ret
= krb5_storage_to_data(request
, &request_data
);
135 krb5_clear_error_string(context
);
136 return KRB5_CC_NOMEM
;
141 for (i
= 0; i
< context
->max_retries
; i
++) {
142 ret
= try_door(context
, k
, &request_data
, response_data
);
143 if (ret
== 0 && response_data
->length
!= 0)
145 ret
= try_unix_socket(context
, k
, &request_data
, response_data
);
146 if (ret
== 0 && response_data
->length
!= 0)
150 krb5_data_free(&request_data
);
153 krb5_clear_error_string(context
);
160 static krb5_error_code
161 kcm_storage_request(krb5_context context
,
162 kcm_operation opcode
,
163 krb5_storage
**storage_p
)
170 sp
= krb5_storage_emem();
172 krb5_set_error_string(context
, "malloc: out of memory");
173 return KRB5_CC_NOMEM
;
176 /* Send MAJOR | VERSION | OPCODE */
177 ret
= krb5_store_int8(sp
, KCM_PROTOCOL_VERSION_MAJOR
);
180 ret
= krb5_store_int8(sp
, KCM_PROTOCOL_VERSION_MINOR
);
183 ret
= krb5_store_int16(sp
, opcode
);
190 krb5_set_error_string(context
, "Failed to encode request");
191 krb5_storage_free(sp
);
197 static krb5_error_code
198 kcm_alloc(krb5_context context
, const char *name
, krb5_ccache
*id
)
203 k
= malloc(sizeof(*k
));
205 krb5_set_error_string(context
, "malloc: out of memory");
206 return KRB5_CC_NOMEM
;
210 k
->name
= strdup(name
);
211 if (k
->name
== NULL
) {
213 krb5_set_error_string(context
, "malloc: out of memory");
214 return KRB5_CC_NOMEM
;
219 path
= krb5_config_get_string_default(context
, NULL
,
225 k
->path
.sun_family
= AF_UNIX
;
226 strlcpy(k
->path
.sun_path
, path
, sizeof(k
->path
.sun_path
));
228 path
= krb5_config_get_string_default(context
, NULL
,
233 k
->door_path
= strdup(path
);
235 (*id
)->data
.data
= k
;
236 (*id
)->data
.length
= sizeof(*k
);
241 static krb5_error_code
242 kcm_call(krb5_context context
,
244 krb5_storage
*request
,
245 krb5_storage
**response_p
,
246 krb5_data
*response_data_p
)
248 krb5_data response_data
;
251 krb5_storage
*response
;
253 if (response_p
!= NULL
)
256 ret
= kcm_send_request(context
, k
, request
, &response_data
);
261 response
= krb5_storage_from_data(&response_data
);
262 if (response
== NULL
) {
263 krb5_data_free(&response_data
);
267 ret
= krb5_ret_int32(response
, &status
);
269 krb5_storage_free(response
);
270 krb5_data_free(&response_data
);
271 return KRB5_CC_FORMAT
;
275 krb5_storage_free(response
);
276 krb5_data_free(&response_data
);
280 if (response_p
!= NULL
) {
281 *response_data_p
= response_data
;
282 *response_p
= response
;
287 krb5_storage_free(response
);
288 krb5_data_free(&response_data
);
294 kcm_free(krb5_context context
, krb5_ccache
*id
)
296 krb5_kcmcache
*k
= KCMCACHE(*id
);
303 memset(k
, 0, sizeof(*k
));
304 krb5_data_free(&(*id
)->data
);
311 kcm_get_name(krb5_context context
,
314 return CACHENAME(id
);
317 static krb5_error_code
318 kcm_resolve(krb5_context context
, krb5_ccache
*id
, const char *res
)
320 return kcm_alloc(context
, res
, id
);
329 static krb5_error_code
330 kcm_gen_new(krb5_context context
, krb5_ccache
*id
)
334 krb5_storage
*request
, *response
;
335 krb5_data response_data
;
337 ret
= kcm_alloc(context
, NULL
, id
);
343 ret
= kcm_storage_request(context
, KCM_OP_GEN_NEW
, &request
);
345 kcm_free(context
, id
);
349 ret
= kcm_call(context
, k
, request
, &response
, &response_data
);
351 krb5_storage_free(request
);
352 kcm_free(context
, id
);
356 ret
= krb5_ret_stringz(response
, &k
->name
);
360 krb5_storage_free(request
);
361 krb5_storage_free(response
);
362 krb5_data_free(&response_data
);
365 kcm_free(context
, id
);
378 static krb5_error_code
379 kcm_initialize(krb5_context context
,
381 krb5_principal primary_principal
)
384 krb5_kcmcache
*k
= KCMCACHE(id
);
385 krb5_storage
*request
;
387 ret
= kcm_storage_request(context
, KCM_OP_INITIALIZE
, &request
);
391 ret
= krb5_store_stringz(request
, k
->name
);
393 krb5_storage_free(request
);
397 ret
= krb5_store_principal(request
, primary_principal
);
399 krb5_storage_free(request
);
403 ret
= kcm_call(context
, k
, request
, NULL
, NULL
);
405 krb5_storage_free(request
);
409 static krb5_error_code
410 kcm_close(krb5_context context
,
413 kcm_free(context
, &id
);
424 static krb5_error_code
425 kcm_destroy(krb5_context context
,
429 krb5_kcmcache
*k
= KCMCACHE(id
);
430 krb5_storage
*request
;
432 ret
= kcm_storage_request(context
, KCM_OP_DESTROY
, &request
);
436 ret
= krb5_store_stringz(request
, k
->name
);
438 krb5_storage_free(request
);
442 ret
= kcm_call(context
, k
, request
, NULL
, NULL
);
444 krb5_storage_free(request
);
456 static krb5_error_code
457 kcm_store_cred(krb5_context context
,
462 krb5_kcmcache
*k
= KCMCACHE(id
);
463 krb5_storage
*request
;
465 ret
= kcm_storage_request(context
, KCM_OP_STORE
, &request
);
469 ret
= krb5_store_stringz(request
, k
->name
);
471 krb5_storage_free(request
);
475 ret
= krb5_store_creds(request
, creds
);
477 krb5_storage_free(request
);
481 ret
= kcm_call(context
, k
, request
, NULL
, NULL
);
483 krb5_storage_free(request
);
497 static krb5_error_code
498 kcm_retrieve(krb5_context context
,
501 const krb5_creds
*mcred
,
505 krb5_kcmcache
*k
= KCMCACHE(id
);
506 krb5_storage
*request
, *response
;
507 krb5_data response_data
;
509 ret
= kcm_storage_request(context
, KCM_OP_RETRIEVE
, &request
);
513 ret
= krb5_store_stringz(request
, k
->name
);
515 krb5_storage_free(request
);
519 ret
= krb5_store_int32(request
, which
);
521 krb5_storage_free(request
);
525 ret
= krb5_store_creds_tag(request
, rk_UNCONST(mcred
));
527 krb5_storage_free(request
);
531 ret
= kcm_call(context
, k
, request
, &response
, &response_data
);
533 krb5_storage_free(request
);
537 ret
= krb5_ret_creds(response
, creds
);
541 krb5_storage_free(request
);
542 krb5_storage_free(response
);
543 krb5_data_free(&response_data
);
555 static krb5_error_code
556 kcm_get_principal(krb5_context context
,
558 krb5_principal
*principal
)
561 krb5_kcmcache
*k
= KCMCACHE(id
);
562 krb5_storage
*request
, *response
;
563 krb5_data response_data
;
565 ret
= kcm_storage_request(context
, KCM_OP_GET_PRINCIPAL
, &request
);
569 ret
= krb5_store_stringz(request
, k
->name
);
571 krb5_storage_free(request
);
575 ret
= kcm_call(context
, k
, request
, &response
, &response_data
);
577 krb5_storage_free(request
);
581 ret
= krb5_ret_principal(response
, principal
);
585 krb5_storage_free(request
);
586 krb5_storage_free(response
);
587 krb5_data_free(&response_data
);
600 static krb5_error_code
601 kcm_get_first (krb5_context context
,
603 krb5_cc_cursor
*cursor
)
606 krb5_kcmcache
*k
= KCMCACHE(id
);
607 krb5_storage
*request
, *response
;
608 krb5_data response_data
;
611 ret
= kcm_storage_request(context
, KCM_OP_GET_FIRST
, &request
);
615 ret
= krb5_store_stringz(request
, k
->name
);
617 krb5_storage_free(request
);
621 ret
= kcm_call(context
, k
, request
, &response
, &response_data
);
623 krb5_storage_free(request
);
627 ret
= krb5_ret_int32(response
, &tmp
);
631 krb5_storage_free(request
);
632 krb5_storage_free(response
);
633 krb5_data_free(&response_data
);
638 *cursor
= malloc(sizeof(tmp
));
640 return KRB5_CC_NOMEM
;
642 KCMCURSOR(*cursor
) = tmp
;
655 static krb5_error_code
656 kcm_get_next (krb5_context context
,
658 krb5_cc_cursor
*cursor
,
662 krb5_kcmcache
*k
= KCMCACHE(id
);
663 krb5_storage
*request
, *response
;
664 krb5_data response_data
;
666 ret
= kcm_storage_request(context
, KCM_OP_GET_NEXT
, &request
);
670 ret
= krb5_store_stringz(request
, k
->name
);
672 krb5_storage_free(request
);
676 ret
= krb5_store_int32(request
, KCMCURSOR(*cursor
));
678 krb5_storage_free(request
);
682 ret
= kcm_call(context
, k
, request
, &response
, &response_data
);
684 krb5_storage_free(request
);
688 ret
= krb5_ret_creds(response
, creds
);
692 krb5_storage_free(request
);
693 krb5_storage_free(response
);
694 krb5_data_free(&response_data
);
707 static krb5_error_code
708 kcm_end_get (krb5_context context
,
710 krb5_cc_cursor
*cursor
)
713 krb5_kcmcache
*k
= KCMCACHE(id
);
714 krb5_storage
*request
;
716 ret
= kcm_storage_request(context
, KCM_OP_END_GET
, &request
);
720 ret
= krb5_store_stringz(request
, k
->name
);
722 krb5_storage_free(request
);
726 ret
= krb5_store_int32(request
, KCMCURSOR(*cursor
));
728 krb5_storage_free(request
);
732 ret
= kcm_call(context
, k
, request
, NULL
, NULL
);
734 krb5_storage_free(request
);
738 krb5_storage_free(request
);
740 KCMCURSOR(*cursor
) = 0;
756 static krb5_error_code
757 kcm_remove_cred(krb5_context context
,
763 krb5_kcmcache
*k
= KCMCACHE(id
);
764 krb5_storage
*request
;
766 ret
= kcm_storage_request(context
, KCM_OP_REMOVE_CRED
, &request
);
770 ret
= krb5_store_stringz(request
, k
->name
);
772 krb5_storage_free(request
);
776 ret
= krb5_store_int32(request
, which
);
778 krb5_storage_free(request
);
782 ret
= krb5_store_creds_tag(request
, cred
);
784 krb5_storage_free(request
);
788 ret
= kcm_call(context
, k
, request
, NULL
, NULL
);
790 krb5_storage_free(request
);
794 static krb5_error_code
795 kcm_set_flags(krb5_context context
,
800 krb5_kcmcache
*k
= KCMCACHE(id
);
801 krb5_storage
*request
;
803 ret
= kcm_storage_request(context
, KCM_OP_SET_FLAGS
, &request
);
807 ret
= krb5_store_stringz(request
, k
->name
);
809 krb5_storage_free(request
);
813 ret
= krb5_store_int32(request
, flags
);
815 krb5_storage_free(request
);
819 ret
= kcm_call(context
, k
, request
, NULL
, NULL
);
821 krb5_storage_free(request
);
825 static krb5_error_code
826 kcm_get_version(krb5_context context
,
832 static krb5_error_code
833 kcm_move(krb5_context context
, krb5_ccache from
, krb5_ccache to
)
835 krb5_set_error_string(context
, "kcm_move not implemented");
839 static krb5_error_code
840 kcm_default_name(krb5_context context
, char **str
)
842 return _krb5_expand_default_cc_name(context
,
843 KRB5_DEFAULT_CCNAME_KCM
,
848 * Variable containing the KCM based credential cache implemention.
850 * @ingroup krb5_ccache
853 const krb5_cc_ops krb5_kcm_ops
= {
878 _krb5_kcm_is_running(krb5_context context
)
881 krb5_ccache_data ccdata
;
882 krb5_ccache id
= &ccdata
;
883 krb5_boolean running
;
885 ret
= kcm_alloc(context
, NULL
, &id
);
889 running
= (_krb5_kcm_noop(context
, id
) == 0);
891 kcm_free(context
, &id
);
903 _krb5_kcm_noop(krb5_context context
,
907 krb5_kcmcache
*k
= KCMCACHE(id
);
908 krb5_storage
*request
;
910 ret
= kcm_storage_request(context
, KCM_OP_NOOP
, &request
);
914 ret
= kcm_call(context
, k
, request
, NULL
, NULL
);
916 krb5_storage_free(request
);
930 _krb5_kcm_chmod(krb5_context context
,
935 krb5_kcmcache
*k
= KCMCACHE(id
);
936 krb5_storage
*request
;
938 ret
= kcm_storage_request(context
, KCM_OP_CHMOD
, &request
);
942 ret
= krb5_store_stringz(request
, k
->name
);
944 krb5_storage_free(request
);
948 ret
= krb5_store_int16(request
, mode
);
950 krb5_storage_free(request
);
954 ret
= kcm_call(context
, k
, request
, NULL
, NULL
);
956 krb5_storage_free(request
);
971 _krb5_kcm_chown(krb5_context context
,
977 krb5_kcmcache
*k
= KCMCACHE(id
);
978 krb5_storage
*request
;
980 ret
= kcm_storage_request(context
, KCM_OP_CHOWN
, &request
);
984 ret
= krb5_store_stringz(request
, k
->name
);
986 krb5_storage_free(request
);
990 ret
= krb5_store_int32(request
, uid
);
992 krb5_storage_free(request
);
996 ret
= krb5_store_int32(request
, gid
);
998 krb5_storage_free(request
);
1002 ret
= kcm_call(context
, k
, request
, NULL
, NULL
);
1004 krb5_storage_free(request
);
1012 * ServerPrincipalPresent
1013 * ServerPrincipal OPTIONAL
1020 _krb5_kcm_get_initial_ticket(krb5_context context
,
1022 krb5_principal server
,
1025 krb5_error_code ret
;
1026 krb5_kcmcache
*k
= KCMCACHE(id
);
1027 krb5_storage
*request
;
1029 ret
= kcm_storage_request(context
, KCM_OP_GET_INITIAL_TICKET
, &request
);
1033 ret
= krb5_store_stringz(request
, k
->name
);
1035 krb5_storage_free(request
);
1039 ret
= krb5_store_int8(request
, (server
== NULL
) ? 0 : 1);
1041 krb5_storage_free(request
);
1045 if (server
!= NULL
) {
1046 ret
= krb5_store_principal(request
, server
);
1048 krb5_storage_free(request
);
1053 ret
= krb5_store_keyblock(request
, *key
);
1055 krb5_storage_free(request
);
1059 ret
= kcm_call(context
, k
, request
, NULL
, NULL
);
1061 krb5_storage_free(request
);
1077 _krb5_kcm_get_ticket(krb5_context context
,
1079 krb5_kdc_flags flags
,
1080 krb5_enctype enctype
,
1081 krb5_principal server
)
1083 krb5_error_code ret
;
1084 krb5_kcmcache
*k
= KCMCACHE(id
);
1085 krb5_storage
*request
;
1087 ret
= kcm_storage_request(context
, KCM_OP_GET_TICKET
, &request
);
1091 ret
= krb5_store_stringz(request
, k
->name
);
1093 krb5_storage_free(request
);
1097 ret
= krb5_store_int32(request
, flags
.i
);
1099 krb5_storage_free(request
);
1103 ret
= krb5_store_int32(request
, enctype
);
1105 krb5_storage_free(request
);
1109 ret
= krb5_store_principal(request
, server
);
1111 krb5_storage_free(request
);
1115 ret
= kcm_call(context
, k
, request
, NULL
, NULL
);
1117 krb5_storage_free(request
);
1122 #endif /* HAVE_KCM */