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 */