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
,
61 krb5_data
*request_data
,
62 krb5_data
*response_data
)
64 #ifdef HAVE_DOOR_CREATE
69 memset(&arg
, 0, sizeof(arg
));
71 fd
= open(k
->door_path
, O_RDWR
);
76 arg
.data_ptr
= request_data
->data
;
77 arg
.data_size
= request_data
->length
;
83 ret
= door_call(fd
, &arg
);
88 ret
= krb5_data_copy(response_data
, arg
.rbuf
, arg
.rsize
);
89 munmap(arg
.rbuf
, arg
.rsize
);
99 static krb5_error_code
100 try_unix_socket(krb5_context context
,
102 krb5_data
*request_data
,
103 krb5_data
*response_data
)
108 fd
= socket(AF_UNIX
, SOCK_STREAM
| SOCK_CLOEXEC
, 0);
113 if (connect(fd
, rk_UNCONST(&k
->path
), sizeof(k
->path
)) != 0) {
118 ret
= _krb5_send_and_recv_tcp(fd
, context
->kdc_timeout
,
119 request_data
, response_data
);
124 static krb5_error_code
125 kcm_send_request(krb5_context context
,
127 krb5_storage
*request
,
128 krb5_data
*response_data
)
131 krb5_data request_data
;
134 response_data
->data
= NULL
;
135 response_data
->length
= 0;
137 ret
= krb5_storage_to_data(request
, &request_data
);
139 krb5_clear_error_string(context
);
140 return KRB5_CC_NOMEM
;
143 ret
= KRB5_CC_NOSUPP
;
145 for (i
= 0; i
< context
->max_retries
; i
++) {
146 ret
= try_door(context
, k
, &request_data
, response_data
);
147 if (ret
== 0 && response_data
->length
!= 0)
149 ret
= try_unix_socket(context
, k
, &request_data
, response_data
);
150 if (ret
== 0 && response_data
->length
!= 0)
154 krb5_data_free(&request_data
);
157 krb5_clear_error_string(context
);
158 ret
= KRB5_CC_NOSUPP
;
164 static krb5_error_code
165 kcm_storage_request(krb5_context context
,
166 kcm_operation opcode
,
167 krb5_storage
**storage_p
)
174 sp
= krb5_storage_emem();
176 krb5_set_error_message(context
, KRB5_CC_NOMEM
, "malloc: out of memory");
177 return KRB5_CC_NOMEM
;
180 /* Send MAJOR | VERSION | OPCODE */
181 ret
= krb5_store_int8(sp
, KCM_PROTOCOL_VERSION_MAJOR
);
184 ret
= krb5_store_int8(sp
, KCM_PROTOCOL_VERSION_MINOR
);
187 ret
= krb5_store_int16(sp
, opcode
);
194 krb5_set_error_message(context
, ret
, "Failed to encode request");
195 krb5_storage_free(sp
);
201 static krb5_error_code
202 kcm_alloc(krb5_context context
, const char *name
, krb5_ccache
*id
)
207 k
= malloc(sizeof(*k
));
209 krb5_set_error_message(context
, KRB5_CC_NOMEM
, "malloc: out of memory");
210 return KRB5_CC_NOMEM
;
214 k
->name
= strdup(name
);
215 if (k
->name
== NULL
) {
217 krb5_set_error_message(context
, KRB5_CC_NOMEM
, "malloc: out of memory");
218 return KRB5_CC_NOMEM
;
223 path
= krb5_config_get_string_default(context
, NULL
,
229 k
->path
.sun_family
= AF_UNIX
;
230 strlcpy(k
->path
.sun_path
, path
, sizeof(k
->path
.sun_path
));
232 path
= krb5_config_get_string_default(context
, NULL
,
237 k
->door_path
= strdup(path
);
239 (*id
)->data
.data
= k
;
240 (*id
)->data
.length
= sizeof(*k
);
245 static krb5_error_code
246 kcm_call(krb5_context context
,
248 krb5_storage
*request
,
249 krb5_storage
**response_p
,
250 krb5_data
*response_data_p
)
252 krb5_data response_data
;
255 krb5_storage
*response
;
257 if (response_p
!= NULL
)
260 ret
= kcm_send_request(context
, k
, request
, &response_data
);
265 response
= krb5_storage_from_data(&response_data
);
266 if (response
== NULL
) {
267 krb5_data_free(&response_data
);
271 ret
= krb5_ret_int32(response
, &status
);
273 krb5_storage_free(response
);
274 krb5_data_free(&response_data
);
275 return KRB5_CC_FORMAT
;
279 krb5_storage_free(response
);
280 krb5_data_free(&response_data
);
284 if (response_p
!= NULL
) {
285 *response_data_p
= response_data
;
286 *response_p
= response
;
291 krb5_storage_free(response
);
292 krb5_data_free(&response_data
);
298 kcm_free(krb5_context context
, krb5_ccache
*id
)
300 krb5_kcmcache
*k
= KCMCACHE(*id
);
307 memset(k
, 0, sizeof(*k
));
308 krb5_data_free(&(*id
)->data
);
315 kcm_get_name(krb5_context context
,
318 return CACHENAME(id
);
321 static krb5_error_code
322 kcm_resolve(krb5_context context
, krb5_ccache
*id
, const char *res
)
324 return kcm_alloc(context
, res
, id
);
333 static krb5_error_code
334 kcm_gen_new(krb5_context context
, krb5_ccache
*id
)
338 krb5_storage
*request
, *response
;
339 krb5_data response_data
;
341 ret
= kcm_alloc(context
, NULL
, id
);
347 ret
= kcm_storage_request(context
, KCM_OP_GEN_NEW
, &request
);
349 kcm_free(context
, id
);
353 ret
= kcm_call(context
, k
, request
, &response
, &response_data
);
355 krb5_storage_free(request
);
356 kcm_free(context
, id
);
360 ret
= krb5_ret_stringz(response
, &k
->name
);
364 krb5_storage_free(request
);
365 krb5_storage_free(response
);
366 krb5_data_free(&response_data
);
369 kcm_free(context
, id
);
382 static krb5_error_code
383 kcm_initialize(krb5_context context
,
385 krb5_principal primary_principal
)
388 krb5_kcmcache
*k
= KCMCACHE(id
);
389 krb5_storage
*request
;
391 ret
= kcm_storage_request(context
, KCM_OP_INITIALIZE
, &request
);
395 ret
= krb5_store_stringz(request
, k
->name
);
397 krb5_storage_free(request
);
401 ret
= krb5_store_principal(request
, primary_principal
);
403 krb5_storage_free(request
);
407 ret
= kcm_call(context
, k
, request
, NULL
, NULL
);
409 krb5_storage_free(request
);
413 static krb5_error_code
414 kcm_close(krb5_context context
,
417 kcm_free(context
, &id
);
428 static krb5_error_code
429 kcm_destroy(krb5_context context
,
433 krb5_kcmcache
*k
= KCMCACHE(id
);
434 krb5_storage
*request
;
436 ret
= kcm_storage_request(context
, KCM_OP_DESTROY
, &request
);
440 ret
= krb5_store_stringz(request
, k
->name
);
442 krb5_storage_free(request
);
446 ret
= kcm_call(context
, k
, request
, NULL
, NULL
);
448 krb5_storage_free(request
);
460 static krb5_error_code
461 kcm_store_cred(krb5_context context
,
466 krb5_kcmcache
*k
= KCMCACHE(id
);
467 krb5_storage
*request
;
469 ret
= kcm_storage_request(context
, KCM_OP_STORE
, &request
);
473 ret
= krb5_store_stringz(request
, k
->name
);
475 krb5_storage_free(request
);
479 ret
= krb5_store_creds(request
, creds
);
481 krb5_storage_free(request
);
485 ret
= kcm_call(context
, k
, request
, NULL
, NULL
);
487 krb5_storage_free(request
);
501 static krb5_error_code
502 kcm_retrieve(krb5_context context
,
505 const krb5_creds
*mcred
,
509 krb5_kcmcache
*k
= KCMCACHE(id
);
510 krb5_storage
*request
, *response
;
511 krb5_data response_data
;
513 ret
= kcm_storage_request(context
, KCM_OP_RETRIEVE
, &request
);
517 ret
= krb5_store_stringz(request
, k
->name
);
519 krb5_storage_free(request
);
523 ret
= krb5_store_int32(request
, which
);
525 krb5_storage_free(request
);
529 ret
= krb5_store_creds_tag(request
, rk_UNCONST(mcred
));
531 krb5_storage_free(request
);
535 ret
= kcm_call(context
, k
, request
, &response
, &response_data
);
537 krb5_storage_free(request
);
541 ret
= krb5_ret_creds(response
, creds
);
545 krb5_storage_free(request
);
546 krb5_storage_free(response
);
547 krb5_data_free(&response_data
);
559 static krb5_error_code
560 kcm_get_principal(krb5_context context
,
562 krb5_principal
*principal
)
565 krb5_kcmcache
*k
= KCMCACHE(id
);
566 krb5_storage
*request
, *response
;
567 krb5_data response_data
;
569 ret
= kcm_storage_request(context
, KCM_OP_GET_PRINCIPAL
, &request
);
573 ret
= krb5_store_stringz(request
, k
->name
);
575 krb5_storage_free(request
);
579 ret
= kcm_call(context
, k
, request
, &response
, &response_data
);
581 krb5_storage_free(request
);
585 ret
= krb5_ret_principal(response
, principal
);
589 krb5_storage_free(request
);
590 krb5_storage_free(response
);
591 krb5_data_free(&response_data
);
604 static krb5_error_code
605 kcm_get_first (krb5_context context
,
607 krb5_cc_cursor
*cursor
)
610 krb5_kcmcache
*k
= KCMCACHE(id
);
611 krb5_storage
*request
, *response
;
612 krb5_data response_data
;
615 ret
= kcm_storage_request(context
, KCM_OP_GET_FIRST
, &request
);
619 ret
= krb5_store_stringz(request
, k
->name
);
621 krb5_storage_free(request
);
625 ret
= kcm_call(context
, k
, request
, &response
, &response_data
);
627 krb5_storage_free(request
);
631 ret
= krb5_ret_int32(response
, &tmp
);
635 krb5_storage_free(request
);
636 krb5_storage_free(response
);
637 krb5_data_free(&response_data
);
642 *cursor
= malloc(sizeof(tmp
));
644 return KRB5_CC_NOMEM
;
646 KCMCURSOR(*cursor
) = tmp
;
659 static krb5_error_code
660 kcm_get_next (krb5_context context
,
662 krb5_cc_cursor
*cursor
,
666 krb5_kcmcache
*k
= KCMCACHE(id
);
667 krb5_storage
*request
, *response
;
668 krb5_data response_data
;
670 ret
= kcm_storage_request(context
, KCM_OP_GET_NEXT
, &request
);
674 ret
= krb5_store_stringz(request
, k
->name
);
676 krb5_storage_free(request
);
680 ret
= krb5_store_int32(request
, KCMCURSOR(*cursor
));
682 krb5_storage_free(request
);
686 ret
= kcm_call(context
, k
, request
, &response
, &response_data
);
688 krb5_storage_free(request
);
692 ret
= krb5_ret_creds(response
, creds
);
696 krb5_storage_free(request
);
697 krb5_storage_free(response
);
698 krb5_data_free(&response_data
);
711 static krb5_error_code
712 kcm_end_get (krb5_context context
,
714 krb5_cc_cursor
*cursor
)
717 krb5_kcmcache
*k
= KCMCACHE(id
);
718 krb5_storage
*request
;
720 ret
= kcm_storage_request(context
, KCM_OP_END_GET
, &request
);
724 ret
= krb5_store_stringz(request
, k
->name
);
726 krb5_storage_free(request
);
730 ret
= krb5_store_int32(request
, KCMCURSOR(*cursor
));
732 krb5_storage_free(request
);
736 ret
= kcm_call(context
, k
, request
, NULL
, NULL
);
738 krb5_storage_free(request
);
742 krb5_storage_free(request
);
744 KCMCURSOR(*cursor
) = 0;
760 static krb5_error_code
761 kcm_remove_cred(krb5_context context
,
767 krb5_kcmcache
*k
= KCMCACHE(id
);
768 krb5_storage
*request
;
770 ret
= kcm_storage_request(context
, KCM_OP_REMOVE_CRED
, &request
);
774 ret
= krb5_store_stringz(request
, k
->name
);
776 krb5_storage_free(request
);
780 ret
= krb5_store_int32(request
, which
);
782 krb5_storage_free(request
);
786 ret
= krb5_store_creds_tag(request
, cred
);
788 krb5_storage_free(request
);
792 ret
= kcm_call(context
, k
, request
, NULL
, NULL
);
794 krb5_storage_free(request
);
798 static krb5_error_code
799 kcm_set_flags(krb5_context context
,
804 krb5_kcmcache
*k
= KCMCACHE(id
);
805 krb5_storage
*request
;
807 ret
= kcm_storage_request(context
, KCM_OP_SET_FLAGS
, &request
);
811 ret
= krb5_store_stringz(request
, k
->name
);
813 krb5_storage_free(request
);
817 ret
= krb5_store_int32(request
, flags
);
819 krb5_storage_free(request
);
823 ret
= kcm_call(context
, k
, request
, NULL
, NULL
);
825 krb5_storage_free(request
);
830 kcm_get_version(krb5_context context
,
836 static krb5_error_code
837 kcm_move(krb5_context context
, krb5_ccache from
, krb5_ccache to
)
840 krb5_kcmcache
*oldk
= KCMCACHE(from
);
841 krb5_kcmcache
*newk
= KCMCACHE(to
);
842 krb5_storage
*request
;
844 ret
= kcm_storage_request(context
, KCM_OP_MOVE_CACHE
, &request
);
848 ret
= krb5_store_stringz(request
, oldk
->name
);
850 krb5_storage_free(request
);
854 ret
= krb5_store_stringz(request
, newk
->name
);
856 krb5_storage_free(request
);
859 ret
= kcm_call(context
, oldk
, request
, NULL
, NULL
);
861 krb5_storage_free(request
);
865 static krb5_error_code
866 kcm_default_name(krb5_context context
, char **str
)
868 return _krb5_expand_default_cc_name(context
,
869 KRB5_DEFAULT_CCNAME_KCM
,
874 * Variable containing the KCM based credential cache implemention.
876 * @ingroup krb5_ccache
879 KRB5_LIB_VARIABLE
const krb5_cc_ops krb5_kcm_ops
= {
905 _krb5_kcm_is_running(krb5_context context
)
908 krb5_ccache_data ccdata
;
909 krb5_ccache id
= &ccdata
;
910 krb5_boolean running
;
912 ret
= kcm_alloc(context
, NULL
, &id
);
916 running
= (_krb5_kcm_noop(context
, id
) == 0);
918 kcm_free(context
, &id
);
930 _krb5_kcm_noop(krb5_context context
,
934 krb5_kcmcache
*k
= KCMCACHE(id
);
935 krb5_storage
*request
;
937 ret
= kcm_storage_request(context
, KCM_OP_NOOP
, &request
);
941 ret
= kcm_call(context
, k
, request
, NULL
, NULL
);
943 krb5_storage_free(request
);
957 _krb5_kcm_chmod(krb5_context context
,
962 krb5_kcmcache
*k
= KCMCACHE(id
);
963 krb5_storage
*request
;
965 ret
= kcm_storage_request(context
, KCM_OP_CHMOD
, &request
);
969 ret
= krb5_store_stringz(request
, k
->name
);
971 krb5_storage_free(request
);
975 ret
= krb5_store_int16(request
, mode
);
977 krb5_storage_free(request
);
981 ret
= kcm_call(context
, k
, request
, NULL
, NULL
);
983 krb5_storage_free(request
);
998 _krb5_kcm_chown(krb5_context context
,
1003 krb5_error_code ret
;
1004 krb5_kcmcache
*k
= KCMCACHE(id
);
1005 krb5_storage
*request
;
1007 ret
= kcm_storage_request(context
, KCM_OP_CHOWN
, &request
);
1011 ret
= krb5_store_stringz(request
, k
->name
);
1013 krb5_storage_free(request
);
1017 ret
= krb5_store_int32(request
, uid
);
1019 krb5_storage_free(request
);
1023 ret
= krb5_store_int32(request
, gid
);
1025 krb5_storage_free(request
);
1029 ret
= kcm_call(context
, k
, request
, NULL
, NULL
);
1031 krb5_storage_free(request
);
1039 * ServerPrincipalPresent
1040 * ServerPrincipal OPTIONAL
1047 _krb5_kcm_get_initial_ticket(krb5_context context
,
1049 krb5_principal server
,
1052 krb5_error_code ret
;
1053 krb5_kcmcache
*k
= KCMCACHE(id
);
1054 krb5_storage
*request
;
1056 ret
= kcm_storage_request(context
, KCM_OP_GET_INITIAL_TICKET
, &request
);
1060 ret
= krb5_store_stringz(request
, k
->name
);
1062 krb5_storage_free(request
);
1066 ret
= krb5_store_int8(request
, (server
== NULL
) ? 0 : 1);
1068 krb5_storage_free(request
);
1072 if (server
!= NULL
) {
1073 ret
= krb5_store_principal(request
, server
);
1075 krb5_storage_free(request
);
1080 ret
= krb5_store_keyblock(request
, *key
);
1082 krb5_storage_free(request
);
1086 ret
= kcm_call(context
, k
, request
, NULL
, NULL
);
1088 krb5_storage_free(request
);
1104 _krb5_kcm_get_ticket(krb5_context context
,
1106 krb5_kdc_flags flags
,
1107 krb5_enctype enctype
,
1108 krb5_principal server
)
1110 krb5_error_code ret
;
1111 krb5_kcmcache
*k
= KCMCACHE(id
);
1112 krb5_storage
*request
;
1114 ret
= kcm_storage_request(context
, KCM_OP_GET_TICKET
, &request
);
1118 ret
= krb5_store_stringz(request
, k
->name
);
1120 krb5_storage_free(request
);
1124 ret
= krb5_store_int32(request
, flags
.i
);
1126 krb5_storage_free(request
);
1130 ret
= krb5_store_int32(request
, enctype
);
1132 krb5_storage_free(request
);
1136 ret
= krb5_store_principal(request
, server
);
1138 krb5_storage_free(request
);
1142 ret
= kcm_call(context
, k
, request
, NULL
, NULL
);
1144 krb5_storage_free(request
);
1148 #endif /* HAVE_KCM */