Line data Source code
1 : /*
2 : * Copyright (c) 1997 - 1999, 2002 - 2003 Kungliga Tekniska Högskolan
3 : * (Royal Institute of Technology, Stockholm, Sweden).
4 : * All rights reserved.
5 : *
6 : * Redistribution and use in source and binary forms, with or without
7 : * modification, are permitted provided that the following conditions
8 : * are met:
9 : *
10 : * 1. Redistributions of source code must retain the above copyright
11 : * notice, this list of conditions and the following disclaimer.
12 : *
13 : * 2. Redistributions in binary form must reproduce the above copyright
14 : * notice, this list of conditions and the following disclaimer in the
15 : * documentation and/or other materials provided with the distribution.
16 : *
17 : * 3. Neither the name of the Institute nor the names of its contributors
18 : * may be used to endorse or promote products derived from this software
19 : * without specific prior written permission.
20 : *
21 : * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
22 : * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 : * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 : * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
25 : * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 : * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 : * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 : * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 : * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 : * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 : * SUCH DAMAGE.
32 : */
33 :
34 : #include "krb5_locl.h"
35 : #include "an2ln_plugin.h"
36 : #include "db_plugin.h"
37 :
38 : #include <string.h>
39 :
40 : /* Default plugin (DB using binary search of sorted text file) follows */
41 : static krb5_error_code KRB5_LIB_CALL an2ln_def_plug_init(krb5_context, void **);
42 : static void KRB5_LIB_CALL an2ln_def_plug_fini(void *);
43 : static krb5_error_code KRB5_LIB_CALL an2ln_def_plug_an2ln(void *, krb5_context, const char *,
44 : krb5_const_principal, set_result_f,
45 : void *);
46 :
47 : static const krb5plugin_an2ln_ftable an2ln_def_plug = {
48 : 0,
49 : an2ln_def_plug_init,
50 : an2ln_def_plug_fini,
51 : an2ln_def_plug_an2ln,
52 : };
53 :
54 : /* Plugin engine code follows */
55 : struct plctx {
56 : krb5_const_principal aname;
57 : heim_string_t luser;
58 : const char *rule;
59 : };
60 :
61 : static krb5_error_code KRB5_LIB_CALL
62 0 : set_res(void *userctx, const char *res)
63 : {
64 0 : struct plctx *plctx = userctx;
65 0 : plctx->luser = heim_string_create(res);
66 0 : if (plctx->luser == NULL)
67 0 : return ENOMEM;
68 0 : return 0;
69 : }
70 :
71 : static krb5_error_code KRB5_LIB_CALL
72 0 : plcallback(krb5_context context,
73 : const void *plug, void *plugctx, void *userctx)
74 : {
75 0 : const krb5plugin_an2ln_ftable *locate = plug;
76 0 : struct plctx *plctx = userctx;
77 :
78 0 : if (plctx->luser)
79 0 : return 0;
80 :
81 0 : return locate->an2ln(plugctx, context, plctx->rule, plctx->aname, set_res, plctx);
82 : }
83 :
84 : static const char *const an2ln_plugin_deps[] = { "krb5", NULL };
85 :
86 : static const struct heim_plugin_data
87 : an2ln_plugin_data = {
88 : "krb5",
89 : KRB5_PLUGIN_AN2LN,
90 : KRB5_PLUGIN_AN2LN_VERSION_0,
91 : an2ln_plugin_deps,
92 : krb5_get_instance
93 : };
94 :
95 : static krb5_error_code
96 0 : an2ln_plugin(krb5_context context, const char *rule, krb5_const_principal aname,
97 : size_t lnsize, char *lname)
98 : {
99 0 : krb5_error_code ret;
100 0 : struct plctx ctx;
101 :
102 0 : ctx.rule = rule;
103 0 : ctx.aname = aname;
104 0 : ctx.luser = NULL;
105 :
106 : /*
107 : * Order of plugin invocation is non-deterministic, but there should
108 : * really be no more than one plugin that can handle any given kind
109 : * rule, so the effect should be deterministic anyways.
110 : */
111 0 : ret = _krb5_plugin_run_f(context, &an2ln_plugin_data,
112 : 0, &ctx, plcallback);
113 0 : if (ret != 0) {
114 0 : heim_release(ctx.luser);
115 0 : return ret;
116 : }
117 :
118 0 : if (ctx.luser == NULL)
119 0 : return KRB5_PLUGIN_NO_HANDLE;
120 :
121 0 : if (strlcpy(lname, heim_string_get_utf8(ctx.luser), lnsize) >= lnsize)
122 0 : ret = KRB5_CONFIG_NOTENUFSPACE;
123 :
124 0 : heim_release(ctx.luser);
125 0 : return ret;
126 : }
127 :
128 : static void
129 0 : reg_def_plugins_once(void *ctx)
130 : {
131 0 : krb5_context context = ctx;
132 :
133 0 : krb5_plugin_register(context, PLUGIN_TYPE_DATA, KRB5_PLUGIN_AN2LN,
134 : &an2ln_def_plug);
135 0 : }
136 :
137 : static int
138 0 : princ_realm_is_default(krb5_context context,
139 : krb5_const_principal aname)
140 : {
141 0 : krb5_error_code ret;
142 0 : krb5_realm *lrealms = NULL;
143 0 : krb5_realm *r;
144 0 : int valid;
145 :
146 0 : ret = krb5_get_default_realms(context, &lrealms);
147 0 : if (ret)
148 0 : return 0;
149 :
150 0 : valid = 0;
151 0 : for (r = lrealms; *r != NULL; ++r) {
152 0 : if (strcmp (*r, aname->realm) == 0) {
153 0 : valid = 1;
154 0 : break;
155 : }
156 : }
157 0 : krb5_free_host_realm (context, lrealms);
158 0 : return valid;
159 : }
160 :
161 : /*
162 : * This function implements MIT's auth_to_local_names configuration for
163 : * configuration compatibility. Specifically:
164 : *
165 : * [realms]
166 : * <realm-name> = {
167 : * auth_to_local_names = {
168 : * <unparsed-principal-name> = <username>
169 : * }
170 : * }
171 : *
172 : * If multiple usernames are configured then the last one is taken.
173 : *
174 : * The configuration can only be expected to hold a relatively small
175 : * number of mappings. For lots of mappings use a DB.
176 : */
177 : static krb5_error_code
178 0 : an2ln_local_names(krb5_context context,
179 : krb5_const_principal aname,
180 : size_t lnsize,
181 : char *lname)
182 : {
183 0 : krb5_error_code ret;
184 0 : char *unparsed;
185 0 : char **values;
186 0 : char *res;
187 0 : size_t i;
188 :
189 0 : if (!princ_realm_is_default(context, aname))
190 0 : return KRB5_PLUGIN_NO_HANDLE;
191 :
192 0 : ret = krb5_unparse_name_flags(context, aname,
193 : KRB5_PRINCIPAL_UNPARSE_NO_REALM,
194 : &unparsed);
195 0 : if (ret)
196 0 : return ret;
197 :
198 0 : ret = KRB5_PLUGIN_NO_HANDLE;
199 0 : values = krb5_config_get_strings(context, NULL, "realms", aname->realm,
200 : "auth_to_local_names", unparsed, NULL);
201 0 : free(unparsed);
202 0 : if (!values)
203 0 : return ret;
204 : /* Take the last value, just like MIT */
205 0 : for (res = NULL, i = 0; values[i]; i++)
206 0 : res = values[i];
207 0 : if (res) {
208 0 : ret = 0;
209 0 : if (strlcpy(lname, res, lnsize) >= lnsize)
210 0 : ret = KRB5_CONFIG_NOTENUFSPACE;
211 :
212 0 : if (!*res || strcmp(res, ":") == 0)
213 0 : ret = KRB5_NO_LOCALNAME;
214 : }
215 :
216 0 : krb5_config_free_strings(values);
217 0 : return ret;
218 : }
219 :
220 : /*
221 : * Heimdal's default aname2lname mapping.
222 : */
223 : static krb5_error_code
224 0 : an2ln_default(krb5_context context,
225 : char *rule,
226 : krb5_const_principal aname,
227 : size_t lnsize, char *lname)
228 : {
229 0 : krb5_error_code ret;
230 0 : const char *res;
231 0 : int root_princs_ok;
232 :
233 0 : if (strcmp(rule, "NONE") == 0)
234 0 : return KRB5_NO_LOCALNAME;
235 :
236 0 : if (strcmp(rule, "DEFAULT") == 0)
237 0 : root_princs_ok = 0;
238 0 : else if (strcmp(rule, "HEIMDAL_DEFAULT") == 0)
239 0 : root_princs_ok = 1;
240 : else
241 0 : return KRB5_PLUGIN_NO_HANDLE;
242 :
243 0 : if (!princ_realm_is_default(context, aname))
244 0 : return KRB5_PLUGIN_NO_HANDLE;
245 :
246 0 : if (aname->name.name_string.len == 1) {
247 : /*
248 : * One component principal names in default realm -> the one
249 : * component is the username.
250 : */
251 0 : res = aname->name.name_string.val[0];
252 0 : } else if (root_princs_ok && aname->name.name_string.len == 2 &&
253 0 : strcmp (aname->name.name_string.val[1], "root") == 0) {
254 : /*
255 : * Two-component principal names in default realm where the
256 : * first component is "root" -> root IFF the principal is in
257 : * root's .k5login (or whatever krb5_kuserok() does).
258 : */
259 0 : krb5_principal rootprinc;
260 0 : krb5_boolean userok;
261 :
262 0 : res = "root";
263 :
264 0 : ret = krb5_copy_principal(context, aname, &rootprinc);
265 0 : if (ret)
266 0 : return ret;
267 :
268 0 : userok = _krb5_kuserok(context, rootprinc, res, FALSE);
269 0 : krb5_free_principal(context, rootprinc);
270 0 : if (!userok)
271 0 : return KRB5_NO_LOCALNAME;
272 : } else {
273 0 : return KRB5_PLUGIN_NO_HANDLE;
274 : }
275 :
276 0 : if (strlcpy(lname, res, lnsize) >= lnsize)
277 0 : return KRB5_CONFIG_NOTENUFSPACE;
278 :
279 0 : return 0;
280 : }
281 :
282 : /**
283 : * Map a principal name to a local username.
284 : *
285 : * Returns 0 on success, KRB5_NO_LOCALNAME if no mapping was found, or
286 : * some Kerberos or system error.
287 : *
288 : * Inputs:
289 : *
290 : * @param context A krb5_context
291 : * @param aname A principal name
292 : * @param lnsize The size of the buffer into which the username will be written
293 : * @param lname The buffer into which the username will be written
294 : *
295 : * @ingroup krb5_support
296 : */
297 : KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
298 0 : krb5_aname_to_localname(krb5_context context,
299 : krb5_const_principal aname,
300 : size_t lnsize,
301 : char *lname)
302 : {
303 0 : static heim_base_once_t reg_def_plugins = HEIM_BASE_ONCE_INIT;
304 0 : krb5_error_code ret;
305 0 : krb5_realm realm;
306 0 : size_t i;
307 0 : char **rules = NULL;
308 0 : char *rule;
309 :
310 0 : if (lnsize)
311 0 : lname[0] = '\0';
312 :
313 0 : heim_base_once_f(®_def_plugins, context, reg_def_plugins_once);
314 :
315 : /* Try MIT's auth_to_local_names config first */
316 0 : ret = an2ln_local_names(context, aname, lnsize, lname);
317 0 : if (ret != KRB5_PLUGIN_NO_HANDLE)
318 0 : return ret;
319 :
320 0 : ret = krb5_get_default_realm(context, &realm);
321 0 : if (ret)
322 0 : return ret;
323 :
324 0 : rules = krb5_config_get_strings(context, NULL, "realms", realm,
325 : "auth_to_local", NULL);
326 0 : krb5_xfree(realm);
327 0 : if (!rules) {
328 : /* Heimdal's default rule */
329 0 : ret = an2ln_default(context, "HEIMDAL_DEFAULT", aname, lnsize, lname);
330 0 : if (ret == KRB5_PLUGIN_NO_HANDLE)
331 0 : return KRB5_NO_LOCALNAME;
332 0 : return ret;
333 : }
334 :
335 : /*
336 : * MIT rules.
337 : *
338 : * Note that RULEs and DBs only have white-list functionality,
339 : * thus RULEs and DBs that we don't understand we simply ignore.
340 : *
341 : * This means that plugins that implement black-lists are
342 : * dangerous: if a black-list plugin isn't found, the black-list
343 : * won't be enforced. But black-lists are dangerous anyways.
344 : */
345 0 : for (ret = KRB5_PLUGIN_NO_HANDLE, i = 0; rules[i]; i++) {
346 0 : rule = rules[i];
347 :
348 : /* Try NONE, DEFAULT, and HEIMDAL_DEFAULT rules */
349 0 : ret = an2ln_default(context, rule, aname, lnsize, lname);
350 0 : if (ret == KRB5_PLUGIN_NO_HANDLE)
351 : /* Try DB, RULE, ... plugins */
352 0 : ret = an2ln_plugin(context, rule, aname, lnsize, lname);
353 :
354 0 : if (ret == 0 && lnsize && !lname[0])
355 0 : continue; /* Success but no lname?! lies! */
356 0 : else if (ret != KRB5_PLUGIN_NO_HANDLE)
357 0 : break;
358 : }
359 :
360 0 : if (ret == KRB5_PLUGIN_NO_HANDLE) {
361 0 : if (lnsize)
362 0 : lname[0] = '\0';
363 0 : ret = KRB5_NO_LOCALNAME;
364 : }
365 :
366 0 : krb5_config_free_strings(rules);
367 0 : return ret;
368 : }
369 :
370 : static krb5_error_code KRB5_LIB_CALL
371 0 : an2ln_def_plug_init(krb5_context context, void **ctx)
372 : {
373 0 : *ctx = NULL;
374 0 : return 0;
375 : }
376 :
377 : static void KRB5_LIB_CALL
378 0 : an2ln_def_plug_fini(void *ctx)
379 : {
380 0 : }
381 :
382 : static heim_base_once_t sorted_text_db_init_once = HEIM_BASE_ONCE_INIT;
383 :
384 : static void
385 0 : sorted_text_db_init_f(void *arg)
386 : {
387 0 : (void) heim_db_register("sorted-text", NULL, &heim_sorted_text_file_dbtype);
388 0 : }
389 :
390 : static krb5_error_code KRB5_LIB_CALL
391 0 : an2ln_def_plug_an2ln(void *plug_ctx, krb5_context context,
392 : const char *rule,
393 : krb5_const_principal aname,
394 : set_result_f set_res_f, void *set_res_ctx)
395 : {
396 0 : krb5_error_code ret;
397 0 : const char *an2ln_db_fname;
398 0 : heim_db_t dbh = NULL;
399 0 : heim_dict_t db_options;
400 0 : heim_data_t k, v;
401 0 : heim_error_t error;
402 0 : char *unparsed = NULL;
403 0 : char *value = NULL;
404 :
405 0 : _krb5_load_db_plugins(context);
406 0 : heim_base_once_f(&sorted_text_db_init_once, NULL, sorted_text_db_init_f);
407 :
408 0 : if (strncmp(rule, "DB:", strlen("DB:")) != 0)
409 0 : return KRB5_PLUGIN_NO_HANDLE;
410 :
411 0 : an2ln_db_fname = &rule[strlen("DB:")];
412 0 : if (!*an2ln_db_fname)
413 0 : return KRB5_PLUGIN_NO_HANDLE;
414 :
415 0 : ret = krb5_unparse_name(context, aname, &unparsed);
416 0 : if (ret)
417 0 : return ret;
418 :
419 0 : db_options = heim_dict_create(11);
420 0 : if (db_options != NULL)
421 0 : heim_dict_set_value(db_options, HSTR("read-only"),
422 0 : heim_number_create(1));
423 0 : dbh = heim_db_create(NULL, an2ln_db_fname, db_options, &error);
424 0 : heim_release(db_options);
425 0 : if (dbh == NULL) {
426 0 : krb5_set_error_message(context, heim_error_get_code(error),
427 0 : N_("Couldn't open aname2lname-text-db", ""));
428 0 : ret = KRB5_PLUGIN_NO_HANDLE;
429 0 : goto cleanup;
430 : }
431 :
432 : /* Binary search; file should be sorted (in C locale) */
433 0 : k = heim_data_ref_create(unparsed, strlen(unparsed), NULL);
434 0 : if (k == NULL) {
435 0 : ret = krb5_enomem(context);
436 0 : goto cleanup;
437 : }
438 0 : v = heim_db_copy_value(dbh, NULL, k, &error);
439 0 : heim_release(k);
440 0 : if (v == NULL && error != NULL) {
441 0 : krb5_set_error_message(context, heim_error_get_code(error),
442 0 : N_("Lookup in aname2lname-text-db failed", ""));
443 0 : ret = heim_error_get_code(error);
444 0 : goto cleanup;
445 0 : } else if (v == NULL) {
446 0 : ret = KRB5_PLUGIN_NO_HANDLE;
447 0 : goto cleanup;
448 : } else {
449 : /* found */
450 0 : if (heim_data_get_length(v) == 0) {
451 0 : krb5_set_error_message(context, ret,
452 0 : N_("Principal mapped to empty username", ""));
453 0 : ret = KRB5_NO_LOCALNAME;
454 0 : goto cleanup;
455 : }
456 0 : value = strndup(heim_data_get_ptr(v), heim_data_get_length(v));
457 0 : heim_release(v);
458 0 : if (value == NULL) {
459 0 : ret = krb5_enomem(context);
460 0 : goto cleanup;
461 : }
462 0 : ret = set_res_f(set_res_ctx, value);
463 : }
464 :
465 0 : cleanup:
466 0 : heim_release(dbh);
467 0 : free(unparsed);
468 0 : free(value);
469 0 : return ret;
470 : }
471 :
|