Look for auth_to_local in the default realm's realm section...
[heimdal.git] / lib / krb5 / aname_to_localname.c
blob49c9554a64c8eedec63dd6842ba82a89722d7658
1 /*
2 * Copyright (c) 1997 - 1999, 2002 - 2003 Kungliga Tekniska Högskolan
3 * (Royal Institute of Technology, Stockholm, Sweden).
4 * All rights reserved.
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
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.
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.
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.
34 #include "krb5_locl.h"
35 #include "an2ln_plugin.h"
36 #include "db_plugin.h"
38 /* Default plugin (DB using binary search of sorted text file) follows */
39 static krb5_error_code KRB5_LIB_CALL an2ln_def_plug_init(krb5_context, void **);
40 static void KRB5_LIB_CALL an2ln_def_plug_fini(void *);
41 static krb5_error_code KRB5_LIB_CALL an2ln_def_plug_an2ln(void *, krb5_context, const char *,
42 krb5_const_principal, set_result_f,
43 void *);
45 static krb5plugin_an2ln_ftable an2ln_def_plug = {
47 an2ln_def_plug_init,
48 an2ln_def_plug_fini,
49 an2ln_def_plug_an2ln,
52 /* Plugin engine code follows */
53 struct plctx {
54 krb5_const_principal aname;
55 heim_string_t luser;
56 const char *rule;
59 static krb5_error_code KRB5_LIB_CALL
60 set_res(void *userctx, const char *res)
62 struct plctx *plctx = userctx;
63 plctx->luser = heim_string_create(res);
64 if (plctx->luser == NULL)
65 return ENOMEM;
66 return 0;
69 static krb5_error_code KRB5_LIB_CALL
70 plcallback(krb5_context context,
71 const void *plug, void *plugctx, void *userctx)
73 const krb5plugin_an2ln_ftable *locate = plug;
74 struct plctx *plctx = userctx;
76 if (plctx->luser)
77 return 0;
79 return locate->an2ln(plugctx, context, plctx->rule, plctx->aname, set_res, plctx);
82 static krb5_error_code
83 an2ln_plugin(krb5_context context, const char *rule, krb5_const_principal aname,
84 size_t lnsize, char *lname)
86 krb5_error_code ret;
87 struct plctx ctx;
89 ctx.rule = rule;
90 ctx.aname = aname;
91 ctx.luser = NULL;
94 * Order of plugin invocation is non-deterministic, but there should
95 * really be no more than one plugin that can handle any given kind
96 * rule, so the effect should be deterministic anyways.
98 ret = _krb5_plugin_run_f(context, "krb5", KRB5_PLUGIN_AN2LN,
99 KRB5_PLUGIN_AN2LN_VERSION_0, 0, &ctx, plcallback);
100 if (ret != 0) {
101 heim_release(ctx.luser);
102 return ret;
105 if (ctx.luser == NULL)
106 return KRB5_PLUGIN_NO_HANDLE;
108 if (strlcpy(lname, heim_string_get_utf8(ctx.luser), lnsize) >= lnsize)
109 ret = KRB5_CONFIG_NOTENUFSPACE;
111 heim_release(ctx.luser);
112 return ret;
115 static void
116 reg_def_plugins_once(void *ctx)
118 krb5_error_code ret;
119 krb5_context context = ctx;
121 ret = krb5_plugin_register(context, PLUGIN_TYPE_DATA,
122 KRB5_PLUGIN_AN2LN, &an2ln_def_plug);
125 static int
126 princ_realm_is_default(krb5_context context,
127 krb5_const_principal aname)
129 krb5_error_code ret;
130 krb5_realm *lrealms = NULL;
131 krb5_realm *r;
132 int valid;
134 ret = krb5_get_default_realms(context, &lrealms);
135 if (ret)
136 return 0;
138 valid = 0;
139 for (r = lrealms; *r != NULL; ++r) {
140 if (strcmp (*r, aname->realm) == 0) {
141 valid = 1;
142 break;
145 krb5_free_host_realm (context, lrealms);
146 return valid;
150 * This function implements MIT's auth_to_local_names configuration for
151 * configuration compatibility. Specifically:
153 * [realms]
154 * <realm-name> = {
155 * auth_to_local_names = {
156 * <unparsed-principal-name> = <username>
160 * If multiple usernames are configured then the last one is taken.
162 * The configuration can only be expected to hold a relatively small
163 * number of mappings. For lots of mappings use a DB.
165 static krb5_error_code
166 an2ln_local_names(krb5_context context,
167 krb5_const_principal aname,
168 size_t lnsize,
169 char *lname)
171 krb5_error_code ret;
172 char *unparsed;
173 char **values;
174 char *res;
175 size_t i;
177 if (!princ_realm_is_default(context, aname))
178 return KRB5_PLUGIN_NO_HANDLE;
180 ret = krb5_unparse_name_flags(context, aname,
181 KRB5_PRINCIPAL_UNPARSE_NO_REALM,
182 &unparsed);
183 if (ret)
184 return ret;
186 ret = KRB5_PLUGIN_NO_HANDLE;
187 values = krb5_config_get_strings(context, NULL, "realms", aname->realm,
188 "auth_to_local_names", unparsed, NULL);
189 free(unparsed);
190 if (!values)
191 return ret;
192 /* Take the last value, just like MIT */
193 for (res = NULL, i = 0; values[i]; i++)
194 res = values[i];
195 if (res) {
196 ret = 0;
197 if (strlcpy(lname, res, lnsize) >= lnsize)
198 ret = KRB5_CONFIG_NOTENUFSPACE;
200 if (!*res || strcmp(res, ":") == 0)
201 ret = KRB5_NO_LOCALNAME;
204 krb5_config_free_strings(values);
205 return ret;
209 * Heimdal's default aname2lname mapping.
211 static krb5_error_code
212 an2ln_default(krb5_context context,
213 char *rule,
214 krb5_const_principal aname,
215 size_t lnsize, char *lname)
217 krb5_error_code ret;
218 const char *res;
219 int root_princs_ok;
221 if (strcmp(rule, "NONE") == 0)
222 return KRB5_NO_LOCALNAME;
224 if (strcmp(rule, "DEFAULT") == 0)
225 root_princs_ok = 0;
226 else if (strcmp(rule, "HEIMDAL_DEFAULT") == 0)
227 root_princs_ok = 1;
228 else
229 return KRB5_PLUGIN_NO_HANDLE;
231 if (!princ_realm_is_default(context, aname))
232 return KRB5_PLUGIN_NO_HANDLE;
234 if (aname->name.name_string.len == 1) {
236 * One component principal names in default realm -> the one
237 * component is the username.
239 res = aname->name.name_string.val[0];
240 } else if (root_princs_ok && aname->name.name_string.len == 2 &&
241 strcmp (aname->name.name_string.val[1], "root") == 0) {
243 * Two-component principal names in default realm where the
244 * first component is "root" -> root IFF the principal is in
245 * root's .k5login (or whatever krb5_kuserok() does).
247 krb5_principal rootprinc;
248 krb5_boolean userok;
250 res = "root";
252 ret = krb5_copy_principal(context, aname, &rootprinc);
253 if (ret)
254 return ret;
256 userok = _krb5_kuserok(context, rootprinc, res, FALSE);
257 krb5_free_principal(context, rootprinc);
258 if (!userok)
259 return KRB5_NO_LOCALNAME;
260 } else {
261 return KRB5_PLUGIN_NO_HANDLE;
264 if (strlcpy(lname, res, lnsize) >= lnsize)
265 return KRB5_CONFIG_NOTENUFSPACE;
267 return 0;
271 * Map a principal name to a local username.
273 * Returns 0 on success, KRB5_NO_LOCALNAME if no mapping was found, or
274 * some Kerberos or system error.
276 * Inputs:
278 * @param context A krb5_context
279 * @param aname A principal name
280 * @param lnsize The size of the buffer into which the username will be written
281 * @param lname The buffer into which the username will be written
283 * @ingroup krb5_support
285 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
286 krb5_aname_to_localname(krb5_context context,
287 krb5_const_principal aname,
288 size_t lnsize,
289 char *lname)
291 static heim_base_once_t reg_def_plugins = HEIM_BASE_ONCE_INIT;
292 krb5_error_code ret;
293 krb5_realm realm;
294 size_t i;
295 char **rules = NULL;
296 char *rule;
298 if (lnsize)
299 lname[0] = '\0';
301 heim_base_once_f(&reg_def_plugins, context, reg_def_plugins_once);
303 /* Try MIT's auth_to_local_names config first */
304 ret = an2ln_local_names(context, aname, lnsize, lname);
305 if (ret != KRB5_PLUGIN_NO_HANDLE)
306 return ret;
308 ret = krb5_get_default_realm(context, &realm);
309 if (ret)
310 return ret;
312 rules = krb5_config_get_strings(context, NULL, "realms", realm,
313 "auth_to_local", NULL);
314 krb5_xfree(realm);
315 if (!rules) {
316 /* Heimdal's default rule */
317 ret = an2ln_default(context, "HEIMDAL_DEFAULT", aname, lnsize, lname);
318 if (ret == KRB5_PLUGIN_NO_HANDLE)
319 return KRB5_NO_LOCALNAME;
320 return ret;
324 * MIT rules.
326 * Note that RULEs and DBs only have white-list functionality,
327 * thus RULEs and DBs that we don't understand we simply ignore.
329 * This means that plugins that implement black-lists are
330 * dangerous: if a black-list plugin isn't found, the black-list
331 * won't be enforced. But black-lists are dangerous anyways.
333 for (ret = KRB5_PLUGIN_NO_HANDLE, i = 0; rules[i]; i++) {
334 rule = rules[i];
336 /* Try NONE, DEFAULT, and HEIMDAL_DEFAULT rules */
337 ret = an2ln_default(context, rule, aname, lnsize, lname);
338 if (ret == KRB5_PLUGIN_NO_HANDLE)
339 /* Try DB, RULE, ... plugins */
340 ret = an2ln_plugin(context, rule, aname, lnsize, lname);
342 if (ret == 0 && lnsize && !lname[0])
343 continue; /* Success but no lname?! lies! */
344 else if (ret != KRB5_PLUGIN_NO_HANDLE)
345 break;
348 if (ret == KRB5_PLUGIN_NO_HANDLE) {
349 if (lnsize)
350 lname[0] = '\0';
351 ret = KRB5_NO_LOCALNAME;
354 krb5_config_free_strings(rules);
355 return ret;
358 static krb5_error_code KRB5_LIB_CALL
359 an2ln_def_plug_init(krb5_context context, void **ctx)
361 *ctx = NULL;
362 return 0;
365 static void KRB5_LIB_CALL
366 an2ln_def_plug_fini(void *ctx)
370 static heim_base_once_t sorted_text_db_init_once = HEIM_BASE_ONCE_INIT;
372 static void
373 sorted_text_db_init_f(void *arg)
375 (void) heim_db_register("sorted-text", NULL, &heim_sorted_text_file_dbtype);
378 static krb5_error_code KRB5_LIB_CALL
379 an2ln_def_plug_an2ln(void *plug_ctx, krb5_context context,
380 const char *rule,
381 krb5_const_principal aname,
382 set_result_f set_res_f, void *set_res_ctx)
384 krb5_error_code ret;
385 const char *an2ln_db_fname;
386 heim_db_t dbh = NULL;
387 heim_dict_t db_options;
388 heim_data_t k, v;
389 heim_error_t error;
390 char *unparsed = NULL;
391 char *value = NULL;
393 _krb5_load_db_plugins(context);
394 heim_base_once_f(&sorted_text_db_init_once, NULL, sorted_text_db_init_f);
396 if (strncmp(rule, "DB:", strlen("DB:") != 0))
397 return KRB5_PLUGIN_NO_HANDLE;
399 an2ln_db_fname = &rule[strlen("DB:")];
400 if (!*an2ln_db_fname)
401 return KRB5_PLUGIN_NO_HANDLE;
403 ret = krb5_unparse_name(context, aname, &unparsed);
404 if (ret)
405 return ret;
407 db_options = heim_dict_create(11);
408 if (db_options != NULL)
409 heim_dict_set_value(db_options, HSTR("read-only"),
410 heim_number_create(1));
411 dbh = heim_db_create(NULL, an2ln_db_fname, db_options, &error);
412 if (dbh == NULL) {
413 krb5_set_error_message(context, heim_error_get_code(error),
414 N_("Couldn't open aname2lname-text-db", ""));
415 ret = KRB5_PLUGIN_NO_HANDLE;
416 goto cleanup;
419 /* Binary search; file should be sorted (in C locale) */
420 k = heim_data_ref_create(unparsed, strlen(unparsed), NULL);
421 if (k == NULL)
422 return krb5_enomem(context);
423 v = heim_db_copy_value(dbh, NULL, k, &error);
424 heim_release(k);
425 if (v == NULL && error != NULL) {
426 krb5_set_error_message(context, heim_error_get_code(error),
427 N_("Lookup in aname2lname-text-db failed", ""));
428 ret = heim_error_get_code(error);
429 goto cleanup;
430 } else if (v == NULL) {
431 ret = KRB5_PLUGIN_NO_HANDLE;
432 goto cleanup;
433 } else {
434 /* found */
435 if (heim_data_get_length(v) == 0) {
436 krb5_set_error_message(context, ret,
437 N_("Principal mapped to empty username", ""));
438 ret = KRB5_NO_LOCALNAME;
439 goto cleanup;
441 ret = set_res_f(set_res_ctx, heim_data_get_ptr(v));
442 heim_release(v);
445 cleanup:
446 heim_release(dbh);
447 free(unparsed);
448 free(value);
449 return ret;