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,v 1.7 2005/06/17 04:20:11 lha Exp $");
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) (*(u_int32_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;
249 krb5_error_code ret, status;
250 krb5_storage *response;
252 if (response_p != NULL)
255 ret = kcm_send_request(context, k, request, &response_data);
260 response = krb5_storage_from_data(&response_data);
261 if (response == NULL) {
262 krb5_data_free(&response_data);
266 ret = krb5_ret_int32(response, &status);
268 krb5_storage_free(response);
269 krb5_data_free(&response_data);
270 return KRB5_CC_FORMAT;
274 krb5_storage_free(response);
275 krb5_data_free(&response_data);
279 if (response_p != NULL) {
280 *response_data_p = response_data;
281 *response_p = response;
286 krb5_storage_free(response);
287 krb5_data_free(&response_data);
293 kcm_free(krb5_context context, krb5_ccache *id)
295 krb5_kcmcache *k = KCMCACHE(*id);
302 memset(k, 0, sizeof(*k));
303 krb5_data_free(&(*id)->data);
310 kcm_get_name(krb5_context context,
313 return CACHENAME(id);
316 static krb5_error_code
317 kcm_resolve(krb5_context context, krb5_ccache *id, const char *res)
319 return kcm_alloc(context, res, id);
328 static krb5_error_code
329 kcm_gen_new(krb5_context context, krb5_ccache *id)
333 krb5_storage *request, *response;
334 krb5_data response_data;
336 ret = kcm_alloc(context, NULL, id);
342 ret = kcm_storage_request(context, KCM_OP_GEN_NEW, &request);
344 kcm_free(context, id);
348 ret = kcm_call(context, k, request, &response, &response_data);
350 krb5_storage_free(request);
351 kcm_free(context, id);
355 ret = krb5_ret_stringz(response, &k->name);
359 krb5_storage_free(request);
360 krb5_storage_free(response);
361 krb5_data_free(&response_data);
364 kcm_free(context, id);
377 static krb5_error_code
378 kcm_initialize(krb5_context context,
380 krb5_principal primary_principal)
383 krb5_kcmcache *k = KCMCACHE(id);
384 krb5_storage *request;
386 ret = kcm_storage_request(context, KCM_OP_INITIALIZE, &request);
390 ret = krb5_store_stringz(request, k->name);
392 krb5_storage_free(request);
396 ret = krb5_store_principal(request, primary_principal);
398 krb5_storage_free(request);
402 ret = kcm_call(context, k, request, NULL, NULL);
404 krb5_storage_free(request);
408 static krb5_error_code
409 kcm_close(krb5_context context,
412 kcm_free(context, &id);
423 static krb5_error_code
424 kcm_destroy(krb5_context context,
428 krb5_kcmcache *k = KCMCACHE(id);
429 krb5_storage *request;
431 ret = kcm_storage_request(context, KCM_OP_DESTROY, &request);
435 ret = krb5_store_stringz(request, k->name);
437 krb5_storage_free(request);
441 ret = kcm_call(context, k, request, NULL, NULL);
443 krb5_storage_free(request);
455 static krb5_error_code
456 kcm_store_cred(krb5_context context,
461 krb5_kcmcache *k = KCMCACHE(id);
462 krb5_storage *request;
464 ret = kcm_storage_request(context, KCM_OP_STORE, &request);
468 ret = krb5_store_stringz(request, k->name);
470 krb5_storage_free(request);
474 ret = krb5_store_creds(request, creds);
476 krb5_storage_free(request);
480 ret = kcm_call(context, k, request, NULL, NULL);
482 krb5_storage_free(request);
496 static krb5_error_code
497 kcm_retrieve(krb5_context context,
500 const krb5_creds *mcred,
504 krb5_kcmcache *k = KCMCACHE(id);
505 krb5_storage *request, *response;
506 krb5_data response_data;
508 ret = kcm_storage_request(context, KCM_OP_RETRIEVE, &request);
512 ret = krb5_store_stringz(request, k->name);
514 krb5_storage_free(request);
518 ret = krb5_store_int32(request, which);
520 krb5_storage_free(request);
524 ret = krb5_store_creds_tag(request, rk_UNCONST(mcred));
526 krb5_storage_free(request);
530 ret = kcm_call(context, k, request, &response, &response_data);
532 krb5_storage_free(request);
536 ret = krb5_ret_creds(response, creds);
540 krb5_storage_free(request);
541 krb5_storage_free(response);
542 krb5_data_free(&response_data);
554 static krb5_error_code
555 kcm_get_principal(krb5_context context,
557 krb5_principal *principal)
560 krb5_kcmcache *k = KCMCACHE(id);
561 krb5_storage *request, *response;
562 krb5_data response_data;
564 ret = kcm_storage_request(context, KCM_OP_GET_PRINCIPAL, &request);
568 ret = krb5_store_stringz(request, k->name);
570 krb5_storage_free(request);
574 ret = kcm_call(context, k, request, &response, &response_data);
576 krb5_storage_free(request);
580 ret = krb5_ret_principal(response, principal);
584 krb5_storage_free(request);
585 krb5_storage_free(response);
586 krb5_data_free(&response_data);
599 static krb5_error_code
600 kcm_get_first (krb5_context context,
602 krb5_cc_cursor *cursor)
605 krb5_kcmcache *k = KCMCACHE(id);
606 krb5_storage *request, *response;
607 krb5_data response_data;
610 ret = kcm_storage_request(context, KCM_OP_GET_FIRST, &request);
614 ret = krb5_store_stringz(request, k->name);
616 krb5_storage_free(request);
620 ret = kcm_call(context, k, request, &response, &response_data);
622 krb5_storage_free(request);
626 ret = krb5_ret_int32(response, &tmp);
630 krb5_storage_free(request);
631 krb5_storage_free(response);
632 krb5_data_free(&response_data);
637 *cursor = malloc(sizeof(tmp));
639 return KRB5_CC_NOMEM;
641 KCMCURSOR(*cursor) = tmp;
654 static krb5_error_code
655 kcm_get_next (krb5_context context,
657 krb5_cc_cursor *cursor,
661 krb5_kcmcache *k = KCMCACHE(id);
662 krb5_storage *request, *response;
663 krb5_data response_data;
665 ret = kcm_storage_request(context, KCM_OP_GET_NEXT, &request);
669 ret = krb5_store_stringz(request, k->name);
671 krb5_storage_free(request);
675 ret = krb5_store_int32(request, KCMCURSOR(*cursor));
677 krb5_storage_free(request);
681 ret = kcm_call(context, k, request, &response, &response_data);
683 krb5_storage_free(request);
687 ret = krb5_ret_creds(response, creds);
691 krb5_storage_free(request);
692 krb5_storage_free(response);
693 krb5_data_free(&response_data);
706 static krb5_error_code
707 kcm_end_get (krb5_context context,
709 krb5_cc_cursor *cursor)
712 krb5_kcmcache *k = KCMCACHE(id);
713 krb5_storage *request;
715 ret = kcm_storage_request(context, KCM_OP_END_GET, &request);
719 ret = krb5_store_stringz(request, k->name);
721 krb5_storage_free(request);
725 ret = krb5_store_int32(request, KCMCURSOR(*cursor));
727 krb5_storage_free(request);
731 ret = kcm_call(context, k, request, NULL, NULL);
733 krb5_storage_free(request);
737 krb5_storage_free(request);
739 KCMCURSOR(*cursor) = 0;
755 static krb5_error_code
756 kcm_remove_cred(krb5_context context,
762 krb5_kcmcache *k = KCMCACHE(id);
763 krb5_storage *request;
765 ret = kcm_storage_request(context, KCM_OP_REMOVE_CRED, &request);
769 ret = krb5_store_stringz(request, k->name);
771 krb5_storage_free(request);
775 ret = krb5_store_int32(request, which);
777 krb5_storage_free(request);
781 ret = krb5_store_creds_tag(request, cred);
783 krb5_storage_free(request);
787 ret = kcm_call(context, k, request, NULL, NULL);
789 krb5_storage_free(request);
793 static krb5_error_code
794 kcm_set_flags(krb5_context context,
799 krb5_kcmcache *k = KCMCACHE(id);
800 krb5_storage *request;
802 ret = kcm_storage_request(context, KCM_OP_SET_FLAGS, &request);
806 ret = krb5_store_stringz(request, k->name);
808 krb5_storage_free(request);
812 ret = krb5_store_int32(request, flags);
814 krb5_storage_free(request);
818 ret = kcm_call(context, k, request, NULL, NULL);
820 krb5_storage_free(request);
824 static krb5_error_code
825 kcm_get_version(krb5_context context,
831 const krb5_cc_ops krb5_kcm_ops = {
851 _krb5_kcm_is_running(krb5_context context)
854 krb5_ccache_data ccdata;
855 krb5_ccache id = &ccdata;
856 krb5_boolean running;
858 ret = kcm_alloc(context, NULL, &id);
862 running = (_krb5_kcm_noop(context, id) == 0);
864 kcm_free(context, &id);
876 _krb5_kcm_noop(krb5_context context,
880 krb5_kcmcache *k = KCMCACHE(id);
881 krb5_storage *request;
883 ret = kcm_storage_request(context, KCM_OP_NOOP, &request);
887 ret = kcm_call(context, k, request, NULL, NULL);
889 krb5_storage_free(request);
903 _krb5_kcm_chmod(krb5_context context,
908 krb5_kcmcache *k = KCMCACHE(id);
909 krb5_storage *request;
911 ret = kcm_storage_request(context, KCM_OP_CHMOD, &request);
915 ret = krb5_store_stringz(request, k->name);
917 krb5_storage_free(request);
921 ret = krb5_store_int16(request, mode);
923 krb5_storage_free(request);
927 ret = kcm_call(context, k, request, NULL, NULL);
929 krb5_storage_free(request);
944 _krb5_kcm_chown(krb5_context context,
950 krb5_kcmcache *k = KCMCACHE(id);
951 krb5_storage *request;
953 ret = kcm_storage_request(context, KCM_OP_CHOWN, &request);
957 ret = krb5_store_stringz(request, k->name);
959 krb5_storage_free(request);
963 ret = krb5_store_int32(request, uid);
965 krb5_storage_free(request);
969 ret = krb5_store_int32(request, gid);
971 krb5_storage_free(request);
975 ret = kcm_call(context, k, request, NULL, NULL);
977 krb5_storage_free(request);
985 * ServerPrincipalPresent
986 * ServerPrincipal OPTIONAL
993 _krb5_kcm_get_initial_ticket(krb5_context context,
995 krb5_principal server,
999 krb5_kcmcache *k = KCMCACHE(id);
1000 krb5_storage *request;
1002 ret = kcm_storage_request(context, KCM_OP_GET_INITIAL_TICKET, &request);
1006 ret = krb5_store_stringz(request, k->name);
1008 krb5_storage_free(request);
1012 ret = krb5_store_int8(request, (server == NULL) ? 0 : 1);
1014 krb5_storage_free(request);
1018 if (server != NULL) {
1019 ret = krb5_store_principal(request, server);
1021 krb5_storage_free(request);
1026 ret = krb5_store_keyblock(request, *key);
1028 krb5_storage_free(request);
1032 ret = kcm_call(context, k, request, NULL, NULL);
1034 krb5_storage_free(request);
1050 _krb5_kcm_get_ticket(krb5_context context,
1052 krb5_kdc_flags flags,
1053 krb5_enctype enctype,
1054 krb5_principal server)
1056 krb5_error_code ret;
1057 krb5_kcmcache *k = KCMCACHE(id);
1058 krb5_storage *request;
1060 ret = kcm_storage_request(context, KCM_OP_GET_TICKET, &request);
1064 ret = krb5_store_stringz(request, k->name);
1066 krb5_storage_free(request);
1070 ret = krb5_store_int32(request, flags.i);
1072 krb5_storage_free(request);
1076 ret = krb5_store_int32(request, enctype);
1078 krb5_storage_free(request);
1082 ret = krb5_store_principal(request, server);
1084 krb5_storage_free(request);
1088 ret = kcm_call(context, k, request, NULL, NULL);
1090 krb5_storage_free(request);
1095 #endif /* HAVE_KCM */