2 * Copyright (c) 1997 - 1999, 2002 - 2003 Kungliga Tekniska Högskolan
3 * (Royal Institute of Technology, Stockholm, Sweden).
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
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
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
,
45 static krb5plugin_an2ln_ftable an2ln_def_plug
= {
52 /* Plugin engine code follows */
54 krb5_const_principal aname
;
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
)
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
;
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
)
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
);
101 heim_release(ctx
.luser
);
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
);
116 reg_def_plugins_once(void *ctx
)
119 krb5_context context
= ctx
;
121 ret
= krb5_plugin_register(context
, PLUGIN_TYPE_DATA
,
122 KRB5_PLUGIN_AN2LN
, &an2ln_def_plug
);
126 princ_realm_is_default(krb5_context context
,
127 krb5_const_principal aname
)
130 krb5_realm
*lrealms
= NULL
;
134 ret
= krb5_get_default_realms(context
, &lrealms
);
139 for (r
= lrealms
; *r
!= NULL
; ++r
) {
140 if (strcmp (*r
, aname
->realm
) == 0) {
145 krb5_free_host_realm (context
, lrealms
);
150 * This function implements MIT's auth_to_local_names configuration for
151 * configuration compatibility. Specifically:
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
,
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
,
186 ret
= KRB5_PLUGIN_NO_HANDLE
;
187 values
= krb5_config_get_strings(context
, NULL
, "realms", aname
->realm
,
188 "auth_to_local_names", unparsed
, NULL
);
192 /* Take the last value, just like MIT */
193 for (res
= NULL
, i
= 0; values
[i
]; i
++)
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
);
209 * Heimdal's default aname2lname mapping.
211 static krb5_error_code
212 an2ln_default(krb5_context context
,
214 krb5_const_principal aname
,
215 size_t lnsize
, char *lname
)
221 if (strcmp(rule
, "NONE") == 0)
222 return KRB5_NO_LOCALNAME
;
224 if (strcmp(rule
, "DEFAULT") == 0)
226 else if (strcmp(rule
, "HEIMDAL_DEFAULT") == 0)
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
;
252 ret
= krb5_copy_principal(context
, aname
, &rootprinc
);
256 userok
= _krb5_kuserok(context
, rootprinc
, res
, FALSE
);
257 krb5_free_principal(context
, rootprinc
);
259 return KRB5_NO_LOCALNAME
;
261 return KRB5_PLUGIN_NO_HANDLE
;
264 if (strlcpy(lname
, res
, lnsize
) >= lnsize
)
265 return KRB5_CONFIG_NOTENUFSPACE
;
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.
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
,
291 static heim_base_once_t reg_def_plugins
= HEIM_BASE_ONCE_INIT
;
301 heim_base_once_f(®_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
)
308 ret
= krb5_get_default_realm(context
, &realm
);
312 rules
= krb5_config_get_strings(context
, NULL
, "realms", realm
,
313 "auth_to_local", NULL
);
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
;
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
++) {
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
)
348 if (ret
== KRB5_PLUGIN_NO_HANDLE
) {
351 ret
= KRB5_NO_LOCALNAME
;
354 krb5_config_free_strings(rules
);
358 static krb5_error_code KRB5_LIB_CALL
359 an2ln_def_plug_init(krb5_context context
, void **ctx
)
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
;
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
,
381 krb5_const_principal aname
,
382 set_result_f set_res_f
, void *set_res_ctx
)
385 const char *an2ln_db_fname
;
386 heim_db_t dbh
= NULL
;
387 heim_dict_t db_options
;
390 char *unparsed
= 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
);
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
);
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
;
419 /* Binary search; file should be sorted (in C locale) */
420 k
= heim_data_ref_create(unparsed
, strlen(unparsed
), NULL
);
422 return krb5_enomem(context
);
423 v
= heim_db_copy_value(dbh
, NULL
, k
, &error
);
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
);
430 } else if (v
== NULL
) {
431 ret
= KRB5_PLUGIN_NO_HANDLE
;
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
;
441 ret
= set_res_f(set_res_ctx
, heim_data_get_ptr(v
));