Imported Upstream version 1.15.1
[platform/upstream/krb5.git] / src / lib / krb5 / ccache / ccselect.c
1 /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 /* lib/krb5/ccache/ccselect.c - krb5_cc_select API and module loader */
3 /*
4  * Copyright (C) 2011 by the Massachusetts Institute of Technology.
5  * All rights reserved.
6  *
7  * Export of this software from the United States of America may
8  *   require a specific license from the United States Government.
9  *   It is the responsibility of any person or organization contemplating
10  *   export to obtain such a license before exporting.
11  *
12  * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
13  * distribute this software and its documentation for any purpose and
14  * without fee is hereby granted, provided that the above copyright
15  * notice appear in all copies and that both that copyright notice and
16  * this permission notice appear in supporting documentation, and that
17  * the name of M.I.T. not be used in advertising or publicity pertaining
18  * to distribution of the software without specific, written prior
19  * permission.  Furthermore if you modify this software you must label
20  * your software as modified software and not distribute it in such a
21  * fashion that it might be confused with the original M.I.T. software.
22  * M.I.T. makes no representations about the suitability of
23  * this software for any purpose.  It is provided "as is" without express
24  * or implied warranty.
25  */
26
27 #include "k5-int.h"
28 #include "cc-int.h"
29 #include <krb5/ccselect_plugin.h>
30 #include "../krb/int-proto.h"
31
32 struct ccselect_module_handle {
33     struct krb5_ccselect_vtable_st vt;
34     krb5_ccselect_moddata data;
35     int priority;
36 };
37
38 static void
39 free_handles(krb5_context context, struct ccselect_module_handle **handles)
40 {
41     struct ccselect_module_handle *h, **hp;
42
43     if (handles == NULL)
44         return;
45     for (hp = handles; *hp != NULL; hp++) {
46         h = *hp;
47         if (h->vt.fini)
48             h->vt.fini(context, h->data);
49         free(h);
50     }
51     free(handles);
52 }
53
54 static krb5_error_code
55 load_modules(krb5_context context)
56 {
57     krb5_error_code ret;
58     struct ccselect_module_handle **list = NULL, *handle;
59     krb5_plugin_initvt_fn *modules = NULL, *mod;
60     size_t count;
61
62 #ifndef _WIN32
63     ret = k5_plugin_register(context, PLUGIN_INTERFACE_CCSELECT, "k5identity",
64                              ccselect_k5identity_initvt);
65     if (ret != 0)
66         goto cleanup;
67 #endif
68
69     ret = k5_plugin_register(context, PLUGIN_INTERFACE_CCSELECT, "realm",
70                              ccselect_realm_initvt);
71     if (ret != 0)
72         goto cleanup;
73
74     ret = k5_plugin_load_all(context, PLUGIN_INTERFACE_CCSELECT, &modules);
75     if (ret != 0)
76         goto cleanup;
77
78     /* Allocate a large enough list of handles. */
79     for (count = 0; modules[count] != NULL; count++);
80     list = k5calloc(count + 1, sizeof(*list), &ret);
81     if (list == NULL)
82         goto cleanup;
83
84     /* Initialize each module, ignoring ones that fail. */
85     count = 0;
86     for (mod = modules; *mod != NULL; mod++) {
87         handle = k5alloc(sizeof(*handle), &ret);
88         if (handle == NULL)
89             goto cleanup;
90         ret = (*mod)(context, 1, 1, (krb5_plugin_vtable)&handle->vt);
91         if (ret != 0) {         /* Failed vtable init is non-fatal. */
92             TRACE_CCSELECT_VTINIT_FAIL(context, ret);
93             free(handle);
94             continue;
95         }
96         handle->data = NULL;
97         ret = handle->vt.init(context, &handle->data, &handle->priority);
98         if (ret != 0) {         /* Failed initialization is non-fatal. */
99             TRACE_CCSELECT_INIT_FAIL(context, handle->vt.name, ret);
100             free(handle);
101             continue;
102         }
103         list[count++] = handle;
104         list[count] = NULL;
105     }
106     list[count] = NULL;
107
108     ret = 0;
109     context->ccselect_handles = list;
110     list = NULL;
111
112 cleanup:
113     k5_plugin_free_modules(context, modules);
114     free_handles(context, list);
115     return ret;
116 }
117
118 static krb5_error_code
119 choose(krb5_context context, struct ccselect_module_handle *h,
120        krb5_principal server, krb5_ccache *cache_out,
121        krb5_principal *princ_out)
122 {
123     return h->vt.choose(context, h->data, server, cache_out, princ_out);
124 }
125
126 krb5_error_code KRB5_CALLCONV
127 krb5_cc_select(krb5_context context, krb5_principal server,
128                krb5_ccache *cache_out, krb5_principal *princ_out)
129 {
130     krb5_error_code ret;
131     int priority;
132     struct ccselect_module_handle **hp, *h;
133     krb5_ccache cache;
134     krb5_principal princ;
135
136     *cache_out = NULL;
137     *princ_out = NULL;
138
139     if (context->ccselect_handles == NULL) {
140         ret = load_modules(context);
141         if (ret)
142             return ret;
143     }
144
145     /* Consult authoritative modules first, then heuristic ones. */
146     for (priority = KRB5_CCSELECT_PRIORITY_AUTHORITATIVE;
147          priority >= KRB5_CCSELECT_PRIORITY_HEURISTIC; priority--) {
148         for (hp = context->ccselect_handles; *hp != NULL; hp++) {
149             h = *hp;
150             if (h->priority != priority)
151                 continue;
152             ret = choose(context, h, server, &cache, &princ);
153             if (ret == 0) {
154                 TRACE_CCSELECT_MODCHOICE(context, h->vt.name, server, cache,
155                                          princ);
156                 *cache_out = cache;
157                 *princ_out = princ;
158                 return 0;
159             } else if (ret == KRB5_CC_NOTFOUND) {
160                 TRACE_CCSELECT_MODNOTFOUND(context, h->vt.name, server, princ);
161                 *princ_out = princ;
162                 return ret;
163             } else if (ret != KRB5_PLUGIN_NO_HANDLE) {
164                 TRACE_CCSELECT_MODFAIL(context, h->vt.name, ret, server);
165                 return ret;
166             }
167         }
168     }
169
170     TRACE_CCSELECT_NOTFOUND(context, server);
171     return KRB5_CC_NOTFOUND;
172 }
173
174 void
175 k5_ccselect_free_context(krb5_context context)
176 {
177     free_handles(context, context->ccselect_handles);
178     context->ccselect_handles = NULL;
179 }