Add _krb5_kcm_is_running/_krb5_kcm_noop API
[heimdal.git] / lib / krb5 / kcm.c
blob21b770be8574dcd8196ef759ef4a075939df4e2c
1 /*
2 * Copyright (c) 2005, PADL Software Pty Ltd.
3 * All rights reserved.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
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
30 * SUCH DAMAGE.
33 #include "krb5_locl.h"
35 #ifdef HAVE_KCM
37 * Client library for Kerberos Credentials Manager (KCM) daemon
40 #ifdef HAVE_SYS_UN_H
41 #include <sys/un.h>
42 #endif
44 #include "kcm.h"
46 RCSID("$Id$");
48 typedef struct krb5_kcmcache {
49 char *name;
50 struct sockaddr_un path;
51 } krb5_kcmcache;
53 #define KCMCACHE(X) ((krb5_kcmcache *)(X)->data.data)
54 #define CACHENAME(X) (KCMCACHE(X)->name)
55 #define KCMCURSOR(C) (*(u_int32_t *)(C))
57 static krb5_error_code
58 kcm_send_request(krb5_context context,
59 krb5_kcmcache *k,
60 krb5_storage *request,
61 krb5_data *response_data)
63 krb5_error_code ret;
64 krb5_data request_data;
65 int i;
67 response_data->data = NULL;
68 response_data->length = 0;
70 ret = krb5_storage_to_data(request, &request_data);
71 if (ret) {
72 return KRB5_CC_NOMEM;
75 ret = KRB5_CC_IO;
77 for (i = 0; i < context->max_retries; i++) {
78 int fd;
80 fd = socket(AF_UNIX, SOCK_STREAM, 0);
81 if (fd < 0)
82 continue;
84 if (connect(fd, (struct sockaddr *)&k->path, sizeof(k->path)) != 0) {
85 close(fd);
86 continue;
89 ret = _krb5_send_and_recv_tcp(fd, context->kdc_timeout,
90 &request_data, response_data);
91 close(fd);
92 if (ret == 0 && response_data->length != 0) {
93 break;
97 krb5_data_free(&request_data);
99 if (ret)
100 ret = KRB5_CC_IO;
102 return ret;
105 static krb5_error_code
106 kcm_storage_request(krb5_context context,
107 kcm_operation opcode,
108 krb5_storage **storage_p)
110 krb5_storage *sp;
111 krb5_error_code ret;
113 *storage_p = NULL;
115 sp = krb5_storage_emem();
116 if (sp == NULL) {
117 krb5_set_error_string(context, "malloc: out of memory");
118 return KRB5_CC_NOMEM;
121 /* Send MAJOR | VERSION | OPCODE */
122 ret = krb5_store_int8(sp, KCM_PROTOCOL_VERSION_MAJOR);
123 if (ret)
124 goto fail;
125 ret = krb5_store_int8(sp, KCM_PROTOCOL_VERSION_MINOR);
126 if (ret)
127 goto fail;
128 ret = krb5_store_int16(sp, opcode);
129 if (ret)
130 goto fail;
132 *storage_p = sp;
133 fail:
134 if (ret) {
135 krb5_set_error_string(context, "Failed to encode request");
136 krb5_storage_free(sp);
139 return ret;
142 static krb5_error_code
143 kcm_alloc(krb5_context context, const char *name, krb5_ccache *id)
145 krb5_kcmcache *k;
147 k = malloc(sizeof(*k));
148 if (k == NULL) {
149 krb5_set_error_string(context, "malloc: out of memory");
150 return KRB5_CC_NOMEM;
153 if (name != NULL) {
154 k->name = strdup(name);
155 if (k->name == NULL) {
156 free(k);
157 krb5_set_error_string(context, "malloc: out of memory");
158 return KRB5_CC_NOMEM;
160 } else
161 k->name = NULL;
163 k->path.sun_family = AF_UNIX;
164 strlcpy(k->path.sun_path, _PATH_KCM_SOCKET, sizeof(k->path.sun_path));
166 (*id)->data.data = k;
167 (*id)->data.length = sizeof(*k);
169 return 0;
172 static krb5_error_code
173 kcm_call(krb5_context context,
174 krb5_kcmcache *k,
175 krb5_storage *request,
176 krb5_storage **response_p,
177 krb5_data *response_data_p)
179 krb5_data response_data;
180 krb5_error_code ret, status;
181 krb5_storage *response;
183 if (response_p != NULL)
184 *response_p = NULL;
186 ret = kcm_send_request(context, k, request, &response_data);
187 if (ret) {
188 return ret;
191 response = krb5_storage_from_data(&response_data);
192 if (response == NULL) {
193 krb5_data_free(&response_data);
194 return KRB5_CC_IO;
197 ret = krb5_ret_int32(response, &status);
198 if (ret) {
199 krb5_storage_free(response);
200 krb5_data_free(&response_data);
201 return KRB5_CC_FORMAT;
204 if (status) {
205 krb5_storage_free(response);
206 krb5_data_free(&response_data);
207 return status;
210 if (response_p != NULL) {
211 *response_data_p = response_data;
212 *response_p = response;
214 return 0;
217 krb5_storage_free(response);
218 krb5_data_free(&response_data);
220 return 0;
223 static void
224 kcm_free(krb5_context context, krb5_ccache *id)
226 krb5_kcmcache *k = KCMCACHE(*id);
228 if (k != NULL) {
229 if (k->name != NULL) {
230 free(k->name);
231 k->name = NULL;
233 memset(k, 0, sizeof(*k));
234 krb5_data_free(&(*id)->data);
237 *id = NULL;
240 static const char *
241 kcm_get_name(krb5_context context,
242 krb5_ccache id)
244 return CACHENAME(id);
247 static krb5_error_code
248 kcm_resolve(krb5_context context, krb5_ccache *id, const char *res)
250 return kcm_alloc(context, res, id);
254 * Request:
256 * Response:
257 * NameZ
259 static krb5_error_code
260 kcm_gen_new(krb5_context context, krb5_ccache *id)
262 krb5_kcmcache *k;
263 krb5_error_code ret;
264 krb5_storage *request, *response;
265 krb5_data response_data;
267 ret = kcm_alloc(context, NULL, id);
268 if (ret)
269 return ret;
271 k = KCMCACHE(*id);
273 ret = kcm_storage_request(context, KCM_OP_GEN_NEW, &request);
274 if (ret) {
275 kcm_free(context, id);
276 return ret;
279 ret = kcm_call(context, k, request, &response, &response_data);
280 if (ret) {
281 krb5_storage_free(request);
282 kcm_free(context, id);
283 return ret;
286 ret = krb5_ret_stringz(response, &k->name);
287 if (ret)
288 ret = KRB5_CC_IO;
290 krb5_storage_free(request);
291 krb5_storage_free(response);
292 krb5_data_free(&response_data);
294 if (ret)
295 kcm_free(context, id);
297 return ret;
301 * Request:
302 * NameZ
303 * Principal
305 * Response:
308 static krb5_error_code
309 kcm_initialize(krb5_context context,
310 krb5_ccache id,
311 krb5_principal primary_principal)
313 krb5_error_code ret;
314 krb5_kcmcache *k = KCMCACHE(id);
315 krb5_storage *request;
317 ret = kcm_storage_request(context, KCM_OP_INITIALIZE, &request);
318 if (ret)
319 return ret;
321 ret = krb5_store_stringz(request, k->name);
322 if (ret) {
323 krb5_storage_free(request);
324 return ret;
327 ret = krb5_store_principal(request, primary_principal);
328 if (ret) {
329 krb5_storage_free(request);
330 return ret;
333 ret = kcm_call(context, k, request, NULL, NULL);
335 krb5_storage_free(request);
336 return ret;
339 static krb5_error_code
340 kcm_close(krb5_context context,
341 krb5_ccache id)
343 kcm_free(context, &id);
344 return 0;
348 * Request:
349 * NameZ
351 * Response:
354 static krb5_error_code
355 kcm_destroy(krb5_context context,
356 krb5_ccache id)
358 krb5_error_code ret;
359 krb5_kcmcache *k = KCMCACHE(id);
360 krb5_storage *request;
362 ret = kcm_storage_request(context, KCM_OP_DESTROY, &request);
363 if (ret)
364 return ret;
366 ret = krb5_store_stringz(request, k->name);
367 if (ret) {
368 krb5_storage_free(request);
369 return ret;
372 ret = kcm_call(context, k, request, NULL, NULL);
374 krb5_storage_free(request);
375 return ret;
379 * Request:
380 * NameZ
381 * Creds
383 * Response:
386 static krb5_error_code
387 kcm_store_cred(krb5_context context,
388 krb5_ccache id,
389 krb5_creds *creds)
391 krb5_error_code ret;
392 krb5_kcmcache *k = KCMCACHE(id);
393 krb5_storage *request;
395 ret = kcm_storage_request(context, KCM_OP_STORE, &request);
396 if (ret)
397 return ret;
399 ret = krb5_store_stringz(request, k->name);
400 if (ret) {
401 krb5_storage_free(request);
402 return ret;
405 ret = krb5_store_creds(request, creds);
406 if (ret) {
407 krb5_storage_free(request);
408 return ret;
411 ret = kcm_call(context, k, request, NULL, NULL);
413 krb5_storage_free(request);
414 return ret;
418 * Request:
419 * NameZ
420 * WhichFields
421 * MatchCreds
423 * Response:
424 * Creds
427 static krb5_error_code
428 kcm_retrieve(krb5_context context,
429 krb5_ccache id,
430 krb5_flags which,
431 const krb5_creds *mcred,
432 krb5_creds *creds)
434 krb5_error_code ret;
435 krb5_kcmcache *k = KCMCACHE(id);
436 krb5_storage *request, *response;
437 krb5_data response_data;
439 ret = kcm_storage_request(context, KCM_OP_RETRIEVE, &request);
440 if (ret)
441 return ret;
443 ret = krb5_store_stringz(request, k->name);
444 if (ret) {
445 krb5_storage_free(request);
446 return ret;
449 ret = krb5_store_int32(request, which);
450 if (ret) {
451 krb5_storage_free(request);
452 return ret;
455 ret = krb5_store_creds_tag(request, (krb5_creds *)mcred);
456 if (ret) {
457 krb5_storage_free(request);
458 return ret;
461 ret = kcm_call(context, k, request, &response, &response_data);
462 if (ret) {
463 krb5_storage_free(request);
464 return ret;
467 ret = krb5_ret_creds(response, creds);
468 if (ret)
469 ret = KRB5_CC_IO;
471 krb5_storage_free(request);
472 krb5_storage_free(response);
473 krb5_data_free(&response_data);
475 return ret;
479 * Request:
480 * NameZ
482 * Response:
483 * Principal
485 static krb5_error_code
486 kcm_get_principal(krb5_context context,
487 krb5_ccache id,
488 krb5_principal *principal)
490 krb5_error_code ret;
491 krb5_kcmcache *k = KCMCACHE(id);
492 krb5_storage *request, *response;
493 krb5_data response_data;
495 ret = kcm_storage_request(context, KCM_OP_GET_PRINCIPAL, &request);
496 if (ret)
497 return ret;
499 ret = krb5_store_stringz(request, k->name);
500 if (ret) {
501 krb5_storage_free(request);
502 return ret;
505 ret = kcm_call(context, k, request, &response, &response_data);
506 if (ret) {
507 krb5_storage_free(request);
508 return ret;
511 ret = krb5_ret_principal(response, principal);
512 if (ret)
513 ret = KRB5_CC_IO;
515 krb5_storage_free(request);
516 krb5_storage_free(response);
517 krb5_data_free(&response_data);
519 return ret;
523 * Request:
524 * NameZ
526 * Response:
527 * Cursor
530 static krb5_error_code
531 kcm_get_first (krb5_context context,
532 krb5_ccache id,
533 krb5_cc_cursor *cursor)
535 krb5_error_code ret;
536 krb5_kcmcache *k = KCMCACHE(id);
537 krb5_storage *request, *response;
538 krb5_data response_data;
539 u_int32_t tmp;
541 ret = kcm_storage_request(context, KCM_OP_GET_FIRST, &request);
542 if (ret)
543 return ret;
545 ret = krb5_store_stringz(request, k->name);
546 if (ret) {
547 krb5_storage_free(request);
548 return ret;
551 ret = kcm_call(context, k, request, &response, &response_data);
552 if (ret) {
553 krb5_storage_free(request);
554 return ret;
557 ret = krb5_ret_int32(response, &tmp);
558 if (ret)
559 ret = KRB5_CC_IO;
561 krb5_storage_free(request);
562 krb5_storage_free(response);
563 krb5_data_free(&response_data);
565 if (ret)
566 return ret;
568 *cursor = malloc(sizeof(tmp));
569 if (*cursor == NULL)
570 return KRB5_CC_NOMEM;
572 KCMCURSOR(*cursor) = tmp;
574 return 0;
578 * Request:
579 * NameZ
580 * Cursor
582 * Response:
583 * Creds
585 static krb5_error_code
586 kcm_get_next (krb5_context context,
587 krb5_ccache id,
588 krb5_cc_cursor *cursor,
589 krb5_creds *creds)
591 krb5_error_code ret;
592 krb5_kcmcache *k = KCMCACHE(id);
593 krb5_storage *request, *response;
594 krb5_data response_data;
596 ret = kcm_storage_request(context, KCM_OP_GET_NEXT, &request);
597 if (ret)
598 return ret;
600 ret = krb5_store_stringz(request, k->name);
601 if (ret) {
602 krb5_storage_free(request);
603 return ret;
606 ret = krb5_store_int32(request, KCMCURSOR(*cursor));
607 if (ret) {
608 krb5_storage_free(request);
609 return ret;
612 ret = kcm_call(context, k, request, &response, &response_data);
613 if (ret) {
614 krb5_storage_free(request);
615 return ret;
618 ret = krb5_ret_creds(response, creds);
619 if (ret)
620 ret = KRB5_CC_IO;
622 krb5_storage_free(request);
623 krb5_storage_free(response);
624 krb5_data_free(&response_data);
626 return ret;
630 * Request:
631 * NameZ
632 * Cursor
634 * Response:
637 static krb5_error_code
638 kcm_end_get (krb5_context context,
639 krb5_ccache id,
640 krb5_cc_cursor *cursor)
642 krb5_error_code ret;
643 krb5_kcmcache *k = KCMCACHE(id);
644 krb5_storage *request;
646 ret = kcm_storage_request(context, KCM_OP_END_GET, &request);
647 if (ret)
648 return ret;
650 ret = krb5_store_stringz(request, k->name);
651 if (ret) {
652 krb5_storage_free(request);
653 return ret;
656 ret = krb5_store_int32(request, KCMCURSOR(*cursor));
657 if (ret) {
658 krb5_storage_free(request);
659 return ret;
662 ret = kcm_call(context, k, request, NULL, NULL);
663 if (ret) {
664 krb5_storage_free(request);
665 return ret;
668 krb5_storage_free(request);
670 KCMCURSOR(*cursor) = 0;
671 free(*cursor);
672 *cursor = NULL;
674 return ret;
678 * Request:
679 * NameZ
680 * WhichFields
681 * MatchCreds
683 * Response:
686 static krb5_error_code
687 kcm_remove_cred(krb5_context context,
688 krb5_ccache id,
689 krb5_flags which,
690 krb5_creds *cred)
692 krb5_error_code ret;
693 krb5_kcmcache *k = KCMCACHE(id);
694 krb5_storage *request;
696 ret = kcm_storage_request(context, KCM_OP_REMOVE_CRED, &request);
697 if (ret)
698 return ret;
700 ret = krb5_store_stringz(request, k->name);
701 if (ret) {
702 krb5_storage_free(request);
703 return ret;
706 ret = krb5_store_int32(request, which);
707 if (ret) {
708 krb5_storage_free(request);
709 return ret;
712 ret = krb5_store_creds_tag(request, cred);
713 if (ret) {
714 krb5_storage_free(request);
715 return ret;
718 ret = kcm_call(context, k, request, NULL, NULL);
720 krb5_storage_free(request);
721 return ret;
724 static krb5_error_code
725 kcm_set_flags(krb5_context context,
726 krb5_ccache id,
727 krb5_flags flags)
729 krb5_error_code ret;
730 krb5_kcmcache *k = KCMCACHE(id);
731 krb5_storage *request;
733 ret = kcm_storage_request(context, KCM_OP_SET_FLAGS, &request);
734 if (ret)
735 return ret;
737 ret = krb5_store_stringz(request, k->name);
738 if (ret) {
739 krb5_storage_free(request);
740 return ret;
743 ret = krb5_store_int32(request, flags);
744 if (ret) {
745 krb5_storage_free(request);
746 return ret;
749 ret = kcm_call(context, k, request, NULL, NULL);
751 krb5_storage_free(request);
752 return ret;
755 static krb5_error_code
756 kcm_get_version(krb5_context context,
757 krb5_ccache id)
759 return 0;
762 const krb5_cc_ops krb5_kcm_ops = {
763 "KCM",
764 kcm_get_name,
765 kcm_resolve,
766 kcm_gen_new,
767 kcm_initialize,
768 kcm_destroy,
769 kcm_close,
770 kcm_store_cred,
771 kcm_retrieve,
772 kcm_get_principal,
773 kcm_get_first,
774 kcm_get_next,
775 kcm_end_get,
776 kcm_remove_cred,
777 kcm_set_flags,
778 kcm_get_version
781 krb5_boolean
782 _krb5_kcm_is_running(krb5_context context)
784 krb5_error_code ret;
785 krb5_ccache_data ccdata;
786 krb5_ccache id = &ccdata;
787 krb5_boolean running;
789 ret = kcm_alloc(context, NULL, &id);
790 if (ret)
791 return 0;
793 running = (_krb5_kcm_noop(context, id) == 0);
795 kcm_free(context, &id);
797 return running;
801 * Request:
803 * Response:
806 krb5_error_code
807 _krb5_kcm_noop(krb5_context context,
808 krb5_ccache id)
810 krb5_error_code ret;
811 krb5_kcmcache *k = KCMCACHE(id);
812 krb5_storage *request;
814 ret = kcm_storage_request(context, KCM_OP_NOOP, &request);
815 if (ret)
816 return ret;
818 ret = kcm_call(context, k, request, NULL, NULL);
820 krb5_storage_free(request);
821 return ret;
826 * Request:
827 * NameZ
828 * Mode
830 * Response:
833 krb5_error_code
834 _krb5_kcm_chmod(krb5_context context,
835 krb5_ccache id,
836 u_int16_t mode)
838 krb5_error_code ret;
839 krb5_kcmcache *k = KCMCACHE(id);
840 krb5_storage *request;
842 ret = kcm_storage_request(context, KCM_OP_CHMOD, &request);
843 if (ret)
844 return ret;
846 ret = krb5_store_stringz(request, k->name);
847 if (ret) {
848 krb5_storage_free(request);
849 return ret;
852 ret = krb5_store_int16(request, mode);
853 if (ret) {
854 krb5_storage_free(request);
855 return ret;
858 ret = kcm_call(context, k, request, NULL, NULL);
860 krb5_storage_free(request);
861 return ret;
866 * Request:
867 * NameZ
868 * UID
869 * GID
871 * Response:
874 krb5_error_code
875 _krb5_kcm_chown(krb5_context context,
876 krb5_ccache id,
877 u_int32_t uid,
878 u_int32_t gid)
880 krb5_error_code ret;
881 krb5_kcmcache *k = KCMCACHE(id);
882 krb5_storage *request;
884 ret = kcm_storage_request(context, KCM_OP_CHOWN, &request);
885 if (ret)
886 return ret;
888 ret = krb5_store_stringz(request, k->name);
889 if (ret) {
890 krb5_storage_free(request);
891 return ret;
894 ret = krb5_store_int32(request, uid);
895 if (ret) {
896 krb5_storage_free(request);
897 return ret;
900 ret = krb5_store_int32(request, gid);
901 if (ret) {
902 krb5_storage_free(request);
903 return ret;
906 ret = kcm_call(context, k, request, NULL, NULL);
908 krb5_storage_free(request);
909 return ret;
914 * Request:
915 * NameZ
916 * ServerPrincipalPresent
917 * ServerPrincipal OPTIONAL
918 * Key
920 * Repsonse:
923 krb5_error_code
924 _krb5_kcm_get_initial_ticket(krb5_context context,
925 krb5_ccache id,
926 krb5_principal server,
927 krb5_keyblock *key)
929 krb5_error_code ret;
930 krb5_kcmcache *k = KCMCACHE(id);
931 krb5_storage *request;
933 ret = kcm_storage_request(context, KCM_OP_GET_INITIAL_TICKET, &request);
934 if (ret)
935 return ret;
937 ret = krb5_store_stringz(request, k->name);
938 if (ret) {
939 krb5_storage_free(request);
940 return ret;
943 ret = krb5_store_int8(request, (server == NULL) ? 0 : 1);
944 if (ret) {
945 krb5_storage_free(request);
946 return ret;
949 if (server != NULL) {
950 ret = krb5_store_principal(request, server);
951 if (ret) {
952 krb5_storage_free(request);
953 return ret;
957 ret = krb5_store_keyblock(request, *key);
958 if (ret) {
959 krb5_storage_free(request);
960 return ret;
963 ret = kcm_call(context, k, request, NULL, NULL);
965 krb5_storage_free(request);
966 return ret;
971 * Request:
972 * NameZ
973 * KDCFlags
974 * EncryptionType
975 * ServerPrincipal
977 * Repsonse:
980 krb5_error_code
981 _krb5_kcm_get_ticket(krb5_context context,
982 krb5_ccache id,
983 krb5_kdc_flags flags,
984 krb5_enctype enctype,
985 krb5_principal server)
987 krb5_error_code ret;
988 krb5_kcmcache *k = KCMCACHE(id);
989 krb5_storage *request;
991 ret = kcm_storage_request(context, KCM_OP_GET_TICKET, &request);
992 if (ret)
993 return ret;
995 ret = krb5_store_stringz(request, k->name);
996 if (ret) {
997 krb5_storage_free(request);
998 return ret;
1001 ret = krb5_store_int32(request, flags.i);
1002 if (ret) {
1003 krb5_storage_free(request);
1004 return ret;
1007 ret = krb5_store_int32(request, enctype);
1008 if (ret) {
1009 krb5_storage_free(request);
1010 return ret;
1013 ret = krb5_store_principal(request, server);
1014 if (ret) {
1015 krb5_storage_free(request);
1016 return ret;
1019 ret = kcm_call(context, k, request, NULL, NULL);
1021 krb5_storage_free(request);
1022 return ret;
1026 #endif /* HAVE_KCM */