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_message(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_message(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, N_("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,
195 N_("Failed to encode KCM request", ""));
196 krb5_storage_free(sp);
202 static krb5_error_code
203 kcm_alloc(krb5_context context, const char *name, krb5_ccache *id)
208 k = malloc(sizeof(*k));
210 krb5_set_error_message(context, KRB5_CC_NOMEM, N_("malloc: out of memory", ""));
211 return KRB5_CC_NOMEM;
215 k->name = strdup(name);
216 if (k->name == NULL) {
218 krb5_set_error_message(context, KRB5_CC_NOMEM,
219 N_("malloc: out of memory", ""));
220 return KRB5_CC_NOMEM;
225 path = krb5_config_get_string_default(context, NULL,
231 k->path.sun_family = AF_UNIX;
232 strlcpy(k->path.sun_path, path, sizeof(k->path.sun_path));
234 path = krb5_config_get_string_default(context, NULL,
239 k->door_path = strdup(path);
241 (*id)->data.data = k;
242 (*id)->data.length = sizeof(*k);
247 static krb5_error_code
248 kcm_call(krb5_context context,
250 krb5_storage *request,
251 krb5_storage **response_p,
252 krb5_data *response_data_p)
254 krb5_data response_data;
257 krb5_storage *response;
259 if (response_p != NULL)
262 ret = kcm_send_request(context, k, request, &response_data);
267 response = krb5_storage_from_data(&response_data);
268 if (response == NULL) {
269 krb5_data_free(&response_data);
273 ret = krb5_ret_int32(response, &status);
275 krb5_storage_free(response);
276 krb5_data_free(&response_data);
277 return KRB5_CC_FORMAT;
281 krb5_storage_free(response);
282 krb5_data_free(&response_data);
286 if (response_p != NULL) {
287 *response_data_p = response_data;
288 *response_p = response;
293 krb5_storage_free(response);
294 krb5_data_free(&response_data);
300 kcm_free(krb5_context context, krb5_ccache *id)
302 krb5_kcmcache *k = KCMCACHE(*id);
309 memset(k, 0, sizeof(*k));
310 krb5_data_free(&(*id)->data);
317 kcm_get_name(krb5_context context,
320 return CACHENAME(id);
323 static krb5_error_code
324 kcm_resolve(krb5_context context, krb5_ccache *id, const char *res)
326 return kcm_alloc(context, res, id);
335 static krb5_error_code
336 kcm_gen_new(krb5_context context, krb5_ccache *id)
340 krb5_storage *request, *response;
341 krb5_data response_data;
343 ret = kcm_alloc(context, NULL, id);
349 ret = kcm_storage_request(context, KCM_OP_GEN_NEW, &request);
351 kcm_free(context, id);
355 ret = kcm_call(context, k, request, &response, &response_data);
357 krb5_storage_free(request);
358 kcm_free(context, id);
362 ret = krb5_ret_stringz(response, &k->name);
366 krb5_storage_free(request);
367 krb5_storage_free(response);
368 krb5_data_free(&response_data);
371 kcm_free(context, id);
384 static krb5_error_code
385 kcm_initialize(krb5_context context,
387 krb5_principal primary_principal)
390 krb5_kcmcache *k = KCMCACHE(id);
391 krb5_storage *request;
393 ret = kcm_storage_request(context, KCM_OP_INITIALIZE, &request);
397 ret = krb5_store_stringz(request, k->name);
399 krb5_storage_free(request);
403 ret = krb5_store_principal(request, primary_principal);
405 krb5_storage_free(request);
409 ret = kcm_call(context, k, request, NULL, NULL);
411 krb5_storage_free(request);
415 static krb5_error_code
416 kcm_close(krb5_context context,
419 kcm_free(context, &id);
430 static krb5_error_code
431 kcm_destroy(krb5_context context,
435 krb5_kcmcache *k = KCMCACHE(id);
436 krb5_storage *request;
438 ret = kcm_storage_request(context, KCM_OP_DESTROY, &request);
442 ret = krb5_store_stringz(request, k->name);
444 krb5_storage_free(request);
448 ret = kcm_call(context, k, request, NULL, NULL);
450 krb5_storage_free(request);
462 static krb5_error_code
463 kcm_store_cred(krb5_context context,
468 krb5_kcmcache *k = KCMCACHE(id);
469 krb5_storage *request;
471 ret = kcm_storage_request(context, KCM_OP_STORE, &request);
475 ret = krb5_store_stringz(request, k->name);
477 krb5_storage_free(request);
481 ret = krb5_store_creds(request, creds);
483 krb5_storage_free(request);
487 ret = kcm_call(context, k, request, NULL, NULL);
489 krb5_storage_free(request);
503 static krb5_error_code
504 kcm_retrieve(krb5_context context,
507 const krb5_creds *mcred,
511 krb5_kcmcache *k = KCMCACHE(id);
512 krb5_storage *request, *response;
513 krb5_data response_data;
515 ret = kcm_storage_request(context, KCM_OP_RETRIEVE, &request);
519 ret = krb5_store_stringz(request, k->name);
521 krb5_storage_free(request);
525 ret = krb5_store_int32(request, which);
527 krb5_storage_free(request);
531 ret = krb5_store_creds_tag(request, rk_UNCONST(mcred));
533 krb5_storage_free(request);
537 ret = kcm_call(context, k, request, &response, &response_data);
539 krb5_storage_free(request);
543 ret = krb5_ret_creds(response, creds);
547 krb5_storage_free(request);
548 krb5_storage_free(response);
549 krb5_data_free(&response_data);
561 static krb5_error_code
562 kcm_get_principal(krb5_context context,
564 krb5_principal *principal)
567 krb5_kcmcache *k = KCMCACHE(id);
568 krb5_storage *request, *response;
569 krb5_data response_data;
571 ret = kcm_storage_request(context, KCM_OP_GET_PRINCIPAL, &request);
575 ret = krb5_store_stringz(request, k->name);
577 krb5_storage_free(request);
581 ret = kcm_call(context, k, request, &response, &response_data);
583 krb5_storage_free(request);
587 ret = krb5_ret_principal(response, principal);
591 krb5_storage_free(request);
592 krb5_storage_free(response);
593 krb5_data_free(&response_data);
606 static krb5_error_code
607 kcm_get_first (krb5_context context,
609 krb5_cc_cursor *cursor)
612 krb5_kcmcache *k = KCMCACHE(id);
613 krb5_storage *request, *response;
614 krb5_data response_data;
617 ret = kcm_storage_request(context, KCM_OP_GET_FIRST, &request);
621 ret = krb5_store_stringz(request, k->name);
623 krb5_storage_free(request);
627 ret = kcm_call(context, k, request, &response, &response_data);
629 krb5_storage_free(request);
633 ret = krb5_ret_int32(response, &tmp);
637 krb5_storage_free(request);
638 krb5_storage_free(response);
639 krb5_data_free(&response_data);
644 *cursor = malloc(sizeof(tmp));
646 return KRB5_CC_NOMEM;
648 KCMCURSOR(*cursor) = tmp;
661 static krb5_error_code
662 kcm_get_next (krb5_context context,
664 krb5_cc_cursor *cursor,
668 krb5_kcmcache *k = KCMCACHE(id);
669 krb5_storage *request, *response;
670 krb5_data response_data;
672 ret = kcm_storage_request(context, KCM_OP_GET_NEXT, &request);
676 ret = krb5_store_stringz(request, k->name);
678 krb5_storage_free(request);
682 ret = krb5_store_int32(request, KCMCURSOR(*cursor));
684 krb5_storage_free(request);
688 ret = kcm_call(context, k, request, &response, &response_data);
690 krb5_storage_free(request);
694 ret = krb5_ret_creds(response, creds);
698 krb5_storage_free(request);
699 krb5_storage_free(response);
700 krb5_data_free(&response_data);
713 static krb5_error_code
714 kcm_end_get (krb5_context context,
716 krb5_cc_cursor *cursor)
719 krb5_kcmcache *k = KCMCACHE(id);
720 krb5_storage *request;
722 ret = kcm_storage_request(context, KCM_OP_END_GET, &request);
726 ret = krb5_store_stringz(request, k->name);
728 krb5_storage_free(request);
732 ret = krb5_store_int32(request, KCMCURSOR(*cursor));
734 krb5_storage_free(request);
738 ret = kcm_call(context, k, request, NULL, NULL);
740 krb5_storage_free(request);
744 krb5_storage_free(request);
746 KCMCURSOR(*cursor) = 0;
762 static krb5_error_code
763 kcm_remove_cred(krb5_context context,
769 krb5_kcmcache *k = KCMCACHE(id);
770 krb5_storage *request;
772 ret = kcm_storage_request(context, KCM_OP_REMOVE_CRED, &request);
776 ret = krb5_store_stringz(request, k->name);
778 krb5_storage_free(request);
782 ret = krb5_store_int32(request, which);
784 krb5_storage_free(request);
788 ret = krb5_store_creds_tag(request, cred);
790 krb5_storage_free(request);
794 ret = kcm_call(context, k, request, NULL, NULL);
796 krb5_storage_free(request);
800 static krb5_error_code
801 kcm_set_flags(krb5_context context,
806 krb5_kcmcache *k = KCMCACHE(id);
807 krb5_storage *request;
809 ret = kcm_storage_request(context, KCM_OP_SET_FLAGS, &request);
813 ret = krb5_store_stringz(request, k->name);
815 krb5_storage_free(request);
819 ret = krb5_store_int32(request, flags);
821 krb5_storage_free(request);
825 ret = kcm_call(context, k, request, NULL, NULL);
827 krb5_storage_free(request);
832 kcm_get_version(krb5_context context,
838 static krb5_error_code
839 kcm_move(krb5_context context, krb5_ccache from, krb5_ccache to)
842 krb5_kcmcache *oldk = KCMCACHE(from);
843 krb5_kcmcache *newk = KCMCACHE(to);
844 krb5_storage *request;
846 ret = kcm_storage_request(context, KCM_OP_MOVE_CACHE, &request);
850 ret = krb5_store_stringz(request, oldk->name);
852 krb5_storage_free(request);
856 ret = krb5_store_stringz(request, newk->name);
858 krb5_storage_free(request);
861 ret = kcm_call(context, oldk, request, NULL, NULL);
863 krb5_storage_free(request);
867 static krb5_error_code
868 kcm_default_name(krb5_context context, char **str)
870 return _krb5_expand_default_cc_name(context,
871 KRB5_DEFAULT_CCNAME_KCM,
875 static krb5_error_code
876 kcm_lastchange(krb5_context context, krb5_ccache id, krb5_timestamp *mtime)
883 * Variable containing the KCM based credential cache implemention.
885 * @ingroup krb5_ccache
888 KRB5_LIB_VARIABLE const krb5_cc_ops krb5_kcm_ops = {
916 _krb5_kcm_is_running(krb5_context context)
919 krb5_ccache_data ccdata;
920 krb5_ccache id = &ccdata;
921 krb5_boolean running;
923 ret = kcm_alloc(context, NULL, &id);
927 running = (_krb5_kcm_noop(context, id) == 0);
929 kcm_free(context, &id);
941 _krb5_kcm_noop(krb5_context context,
945 krb5_kcmcache *k = KCMCACHE(id);
946 krb5_storage *request;
948 ret = kcm_storage_request(context, KCM_OP_NOOP, &request);
952 ret = kcm_call(context, k, request, NULL, NULL);
954 krb5_storage_free(request);
968 _krb5_kcm_chmod(krb5_context context,
973 krb5_kcmcache *k = KCMCACHE(id);
974 krb5_storage *request;
976 ret = kcm_storage_request(context, KCM_OP_CHMOD, &request);
980 ret = krb5_store_stringz(request, k->name);
982 krb5_storage_free(request);
986 ret = krb5_store_int16(request, mode);
988 krb5_storage_free(request);
992 ret = kcm_call(context, k, request, NULL, NULL);
994 krb5_storage_free(request);
1009 _krb5_kcm_chown(krb5_context context,
1014 krb5_error_code ret;
1015 krb5_kcmcache *k = KCMCACHE(id);
1016 krb5_storage *request;
1018 ret = kcm_storage_request(context, KCM_OP_CHOWN, &request);
1022 ret = krb5_store_stringz(request, k->name);
1024 krb5_storage_free(request);
1028 ret = krb5_store_int32(request, uid);
1030 krb5_storage_free(request);
1034 ret = krb5_store_int32(request, gid);
1036 krb5_storage_free(request);
1040 ret = kcm_call(context, k, request, NULL, NULL);
1042 krb5_storage_free(request);
1050 * ServerPrincipalPresent
1051 * ServerPrincipal OPTIONAL
1058 _krb5_kcm_get_initial_ticket(krb5_context context,
1060 krb5_principal server,
1063 krb5_error_code ret;
1064 krb5_kcmcache *k = KCMCACHE(id);
1065 krb5_storage *request;
1067 ret = kcm_storage_request(context, KCM_OP_GET_INITIAL_TICKET, &request);
1071 ret = krb5_store_stringz(request, k->name);
1073 krb5_storage_free(request);
1077 ret = krb5_store_int8(request, (server == NULL) ? 0 : 1);
1079 krb5_storage_free(request);
1083 if (server != NULL) {
1084 ret = krb5_store_principal(request, server);
1086 krb5_storage_free(request);
1091 ret = krb5_store_keyblock(request, *key);
1093 krb5_storage_free(request);
1097 ret = kcm_call(context, k, request, NULL, NULL);
1099 krb5_storage_free(request);
1115 _krb5_kcm_get_ticket(krb5_context context,
1117 krb5_kdc_flags flags,
1118 krb5_enctype enctype,
1119 krb5_principal server)
1121 krb5_error_code ret;
1122 krb5_kcmcache *k = KCMCACHE(id);
1123 krb5_storage *request;
1125 ret = kcm_storage_request(context, KCM_OP_GET_TICKET, &request);
1129 ret = krb5_store_stringz(request, k->name);
1131 krb5_storage_free(request);
1135 ret = krb5_store_int32(request, flags.i);
1137 krb5_storage_free(request);
1141 ret = krb5_store_int32(request, enctype);
1143 krb5_storage_free(request);
1147 ret = krb5_store_principal(request, server);
1149 krb5_storage_free(request);
1153 ret = kcm_call(context, k, request, NULL, NULL);
1155 krb5_storage_free(request);
1159 #endif /* HAVE_KCM */