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_kcmcache *k = KCMCACHE(id);
1101 krb5_error_code ret;
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 */