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 RCSID("$Id: kcm.c 22108 2007-12-03 17:23:53Z lha $");
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 */