xref: /freebsd/crypto/krb5/src/lib/krb5/ccache/ccselect.c (revision 7f2fe78b9dd5f51c821d771b63d2e096f6fd49e9)
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
free_handles(krb5_context context,struct ccselect_module_handle ** handles)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
load_modules(krb5_context context)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_register(context, PLUGIN_INTERFACE_CCSELECT, "hostname",
75                              ccselect_hostname_initvt);
76     if (ret != 0)
77         goto cleanup;
78 
79     ret = k5_plugin_load_all(context, PLUGIN_INTERFACE_CCSELECT, &modules);
80     if (ret != 0)
81         goto cleanup;
82 
83     /* Allocate a large enough list of handles. */
84     for (count = 0; modules[count] != NULL; count++);
85     list = k5calloc(count + 1, sizeof(*list), &ret);
86     if (list == NULL)
87         goto cleanup;
88 
89     /* Initialize each module, ignoring ones that fail. */
90     count = 0;
91     for (mod = modules; *mod != NULL; mod++) {
92         handle = k5alloc(sizeof(*handle), &ret);
93         if (handle == NULL)
94             goto cleanup;
95         ret = (*mod)(context, 1, 1, (krb5_plugin_vtable)&handle->vt);
96         if (ret != 0) {         /* Failed vtable init is non-fatal. */
97             TRACE_CCSELECT_VTINIT_FAIL(context, ret);
98             free(handle);
99             continue;
100         }
101         handle->data = NULL;
102         ret = handle->vt.init(context, &handle->data, &handle->priority);
103         if (ret != 0) {         /* Failed initialization is non-fatal. */
104             TRACE_CCSELECT_INIT_FAIL(context, handle->vt.name, ret);
105             free(handle);
106             continue;
107         }
108         list[count++] = handle;
109         list[count] = NULL;
110     }
111     list[count] = NULL;
112 
113     ret = 0;
114     context->ccselect_handles = list;
115     list = NULL;
116 
117 cleanup:
118     k5_plugin_free_modules(context, modules);
119     free_handles(context, list);
120     return ret;
121 }
122 
123 krb5_error_code KRB5_CALLCONV
krb5_cc_select(krb5_context context,krb5_principal server,krb5_ccache * cache_out,krb5_principal * princ_out)124 krb5_cc_select(krb5_context context, krb5_principal server,
125                krb5_ccache *cache_out, krb5_principal *princ_out)
126 {
127     krb5_error_code ret;
128     int priority;
129     struct ccselect_module_handle **hp, *h;
130     krb5_ccache cache;
131     krb5_principal princ;
132     krb5_principal srvcp = NULL;
133     char **fbrealms = NULL;
134 
135     *cache_out = NULL;
136     *princ_out = NULL;
137 
138     if (context->ccselect_handles == NULL) {
139         ret = load_modules(context);
140         if (ret)
141             goto cleanup;
142     }
143 
144     /* Try to use the fallback host realm for the server if there is no
145      * authoritative realm. */
146     if (krb5_is_referral_realm(&server->realm) &&
147         server->type == KRB5_NT_SRV_HST && server->length == 2) {
148         ret = krb5_get_fallback_host_realm(context, &server->data[1],
149                                            &fbrealms);
150         /* Continue without realm if we failed due to no default realm. */
151         if (ret && ret != KRB5_CONFIG_NODEFREALM)
152             goto cleanup;
153         if (!ret) {
154             /* Make a copy with the first fallback realm. */
155             ret = krb5_copy_principal(context, server, &srvcp);
156             if (ret)
157                 goto cleanup;
158             ret = krb5_set_principal_realm(context, srvcp, fbrealms[0]);
159             if (ret)
160                 goto cleanup;
161             server = srvcp;
162         }
163     }
164 
165     /* Consult authoritative modules first, then heuristic ones. */
166     for (priority = KRB5_CCSELECT_PRIORITY_AUTHORITATIVE;
167          priority >= KRB5_CCSELECT_PRIORITY_HEURISTIC; priority--) {
168         for (hp = context->ccselect_handles; *hp != NULL; hp++) {
169             h = *hp;
170             if (h->priority != priority)
171                 continue;
172             ret = h->vt.choose(context, h->data, server, &cache, &princ);
173             if (ret == 0) {
174                 TRACE_CCSELECT_MODCHOICE(context, h->vt.name, server, cache,
175                                          princ);
176                 *cache_out = cache;
177                 *princ_out = princ;
178                 goto cleanup;
179             } else if (ret == KRB5_CC_NOTFOUND) {
180                 TRACE_CCSELECT_MODNOTFOUND(context, h->vt.name, server, princ);
181                 *princ_out = princ;
182                 goto cleanup;
183             } else if (ret != KRB5_PLUGIN_NO_HANDLE) {
184                 TRACE_CCSELECT_MODFAIL(context, h->vt.name, ret, server);
185                 goto cleanup;
186             }
187         }
188     }
189 
190     TRACE_CCSELECT_NOTFOUND(context, server);
191     ret = KRB5_CC_NOTFOUND;
192 
193 cleanup:
194     krb5_free_principal(context, srvcp);
195     krb5_free_host_realm(context, fbrealms);
196     return ret;
197 }
198 
199 void
k5_ccselect_free_context(krb5_context context)200 k5_ccselect_free_context(krb5_context context)
201 {
202     free_handles(context, context->ccselect_handles);
203     context->ccselect_handles = NULL;
204 }
205