diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 17:20:00 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 17:20:00 +0000 |
commit | 8daa83a594a2e98f39d764422bfbdbc62c9efd44 (patch) | |
tree | 4099e8021376c7d8c05bdf8503093d80e9c7bad0 /third_party/heimdal/lib/krb5/aname_to_localname.c | |
parent | Initial commit. (diff) | |
download | samba-8daa83a594a2e98f39d764422bfbdbc62c9efd44.tar.xz samba-8daa83a594a2e98f39d764422bfbdbc62c9efd44.zip |
Adding upstream version 2:4.20.0+dfsg.upstream/2%4.20.0+dfsg
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'third_party/heimdal/lib/krb5/aname_to_localname.c')
-rw-r--r-- | third_party/heimdal/lib/krb5/aname_to_localname.c | 471 |
1 files changed, 471 insertions, 0 deletions
diff --git a/third_party/heimdal/lib/krb5/aname_to_localname.c b/third_party/heimdal/lib/krb5/aname_to_localname.c new file mode 100644 index 0000000..7c546fb --- /dev/null +++ b/third_party/heimdal/lib/krb5/aname_to_localname.c @@ -0,0 +1,471 @@ +/* + * Copyright (c) 1997 - 1999, 2002 - 2003 Kungliga Tekniska Högskolan + * (Royal Institute of Technology, Stockholm, Sweden). + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "krb5_locl.h" +#include "an2ln_plugin.h" +#include "db_plugin.h" + +#include <string.h> + +/* Default plugin (DB using binary search of sorted text file) follows */ +static krb5_error_code KRB5_LIB_CALL an2ln_def_plug_init(krb5_context, void **); +static void KRB5_LIB_CALL an2ln_def_plug_fini(void *); +static krb5_error_code KRB5_LIB_CALL an2ln_def_plug_an2ln(void *, krb5_context, const char *, + krb5_const_principal, set_result_f, + void *); + +static const krb5plugin_an2ln_ftable an2ln_def_plug = { + 0, + an2ln_def_plug_init, + an2ln_def_plug_fini, + an2ln_def_plug_an2ln, +}; + +/* Plugin engine code follows */ +struct plctx { + krb5_const_principal aname; + heim_string_t luser; + const char *rule; +}; + +static krb5_error_code KRB5_LIB_CALL +set_res(void *userctx, const char *res) +{ + struct plctx *plctx = userctx; + plctx->luser = heim_string_create(res); + if (plctx->luser == NULL) + return ENOMEM; + return 0; +} + +static krb5_error_code KRB5_LIB_CALL +plcallback(krb5_context context, + const void *plug, void *plugctx, void *userctx) +{ + const krb5plugin_an2ln_ftable *locate = plug; + struct plctx *plctx = userctx; + + if (plctx->luser) + return 0; + + return locate->an2ln(plugctx, context, plctx->rule, plctx->aname, set_res, plctx); +} + +static const char *const an2ln_plugin_deps[] = { "krb5", NULL }; + +static const struct heim_plugin_data +an2ln_plugin_data = { + "krb5", + KRB5_PLUGIN_AN2LN, + KRB5_PLUGIN_AN2LN_VERSION_0, + an2ln_plugin_deps, + krb5_get_instance +}; + +static krb5_error_code +an2ln_plugin(krb5_context context, const char *rule, krb5_const_principal aname, + size_t lnsize, char *lname) +{ + krb5_error_code ret; + struct plctx ctx; + + ctx.rule = rule; + ctx.aname = aname; + ctx.luser = NULL; + + /* + * Order of plugin invocation is non-deterministic, but there should + * really be no more than one plugin that can handle any given kind + * rule, so the effect should be deterministic anyways. + */ + ret = _krb5_plugin_run_f(context, &an2ln_plugin_data, + 0, &ctx, plcallback); + if (ret != 0) { + heim_release(ctx.luser); + return ret; + } + + if (ctx.luser == NULL) + return KRB5_PLUGIN_NO_HANDLE; + + if (strlcpy(lname, heim_string_get_utf8(ctx.luser), lnsize) >= lnsize) + ret = KRB5_CONFIG_NOTENUFSPACE; + + heim_release(ctx.luser); + return ret; +} + +static void +reg_def_plugins_once(void *ctx) +{ + krb5_context context = ctx; + + krb5_plugin_register(context, PLUGIN_TYPE_DATA, KRB5_PLUGIN_AN2LN, + &an2ln_def_plug); +} + +static int +princ_realm_is_default(krb5_context context, + krb5_const_principal aname) +{ + krb5_error_code ret; + krb5_realm *lrealms = NULL; + krb5_realm *r; + int valid; + + ret = krb5_get_default_realms(context, &lrealms); + if (ret) + return 0; + + valid = 0; + for (r = lrealms; *r != NULL; ++r) { + if (strcmp (*r, aname->realm) == 0) { + valid = 1; + break; + } + } + krb5_free_host_realm (context, lrealms); + return valid; +} + +/* + * This function implements MIT's auth_to_local_names configuration for + * configuration compatibility. Specifically: + * + * [realms] + * <realm-name> = { + * auth_to_local_names = { + * <unparsed-principal-name> = <username> + * } + * } + * + * If multiple usernames are configured then the last one is taken. + * + * The configuration can only be expected to hold a relatively small + * number of mappings. For lots of mappings use a DB. + */ +static krb5_error_code +an2ln_local_names(krb5_context context, + krb5_const_principal aname, + size_t lnsize, + char *lname) +{ + krb5_error_code ret; + char *unparsed; + char **values; + char *res; + size_t i; + + if (!princ_realm_is_default(context, aname)) + return KRB5_PLUGIN_NO_HANDLE; + + ret = krb5_unparse_name_flags(context, aname, + KRB5_PRINCIPAL_UNPARSE_NO_REALM, + &unparsed); + if (ret) + return ret; + + ret = KRB5_PLUGIN_NO_HANDLE; + values = krb5_config_get_strings(context, NULL, "realms", aname->realm, + "auth_to_local_names", unparsed, NULL); + free(unparsed); + if (!values) + return ret; + /* Take the last value, just like MIT */ + for (res = NULL, i = 0; values[i]; i++) + res = values[i]; + if (res) { + ret = 0; + if (strlcpy(lname, res, lnsize) >= lnsize) + ret = KRB5_CONFIG_NOTENUFSPACE; + + if (!*res || strcmp(res, ":") == 0) + ret = KRB5_NO_LOCALNAME; + } + + krb5_config_free_strings(values); + return ret; +} + +/* + * Heimdal's default aname2lname mapping. + */ +static krb5_error_code +an2ln_default(krb5_context context, + char *rule, + krb5_const_principal aname, + size_t lnsize, char *lname) +{ + krb5_error_code ret; + const char *res; + int root_princs_ok; + + if (strcmp(rule, "NONE") == 0) + return KRB5_NO_LOCALNAME; + + if (strcmp(rule, "DEFAULT") == 0) + root_princs_ok = 0; + else if (strcmp(rule, "HEIMDAL_DEFAULT") == 0) + root_princs_ok = 1; + else + return KRB5_PLUGIN_NO_HANDLE; + + if (!princ_realm_is_default(context, aname)) + return KRB5_PLUGIN_NO_HANDLE; + + if (aname->name.name_string.len == 1) { + /* + * One component principal names in default realm -> the one + * component is the username. + */ + res = aname->name.name_string.val[0]; + } else if (root_princs_ok && aname->name.name_string.len == 2 && + strcmp (aname->name.name_string.val[1], "root") == 0) { + /* + * Two-component principal names in default realm where the + * first component is "root" -> root IFF the principal is in + * root's .k5login (or whatever krb5_kuserok() does). + */ + krb5_principal rootprinc; + krb5_boolean userok; + + res = "root"; + + ret = krb5_copy_principal(context, aname, &rootprinc); + if (ret) + return ret; + + userok = _krb5_kuserok(context, rootprinc, res, FALSE); + krb5_free_principal(context, rootprinc); + if (!userok) + return KRB5_NO_LOCALNAME; + } else { + return KRB5_PLUGIN_NO_HANDLE; + } + + if (strlcpy(lname, res, lnsize) >= lnsize) + return KRB5_CONFIG_NOTENUFSPACE; + + return 0; +} + +/** + * Map a principal name to a local username. + * + * Returns 0 on success, KRB5_NO_LOCALNAME if no mapping was found, or + * some Kerberos or system error. + * + * Inputs: + * + * @param context A krb5_context + * @param aname A principal name + * @param lnsize The size of the buffer into which the username will be written + * @param lname The buffer into which the username will be written + * + * @ingroup krb5_support + */ +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_aname_to_localname(krb5_context context, + krb5_const_principal aname, + size_t lnsize, + char *lname) +{ + static heim_base_once_t reg_def_plugins = HEIM_BASE_ONCE_INIT; + krb5_error_code ret; + krb5_realm realm; + size_t i; + char **rules = NULL; + char *rule; + + if (lnsize) + lname[0] = '\0'; + + heim_base_once_f(®_def_plugins, context, reg_def_plugins_once); + + /* Try MIT's auth_to_local_names config first */ + ret = an2ln_local_names(context, aname, lnsize, lname); + if (ret != KRB5_PLUGIN_NO_HANDLE) + return ret; + + ret = krb5_get_default_realm(context, &realm); + if (ret) + return ret; + + rules = krb5_config_get_strings(context, NULL, "realms", realm, + "auth_to_local", NULL); + krb5_xfree(realm); + if (!rules) { + /* Heimdal's default rule */ + ret = an2ln_default(context, "HEIMDAL_DEFAULT", aname, lnsize, lname); + if (ret == KRB5_PLUGIN_NO_HANDLE) + return KRB5_NO_LOCALNAME; + return ret; + } + + /* + * MIT rules. + * + * Note that RULEs and DBs only have white-list functionality, + * thus RULEs and DBs that we don't understand we simply ignore. + * + * This means that plugins that implement black-lists are + * dangerous: if a black-list plugin isn't found, the black-list + * won't be enforced. But black-lists are dangerous anyways. + */ + for (ret = KRB5_PLUGIN_NO_HANDLE, i = 0; rules[i]; i++) { + rule = rules[i]; + + /* Try NONE, DEFAULT, and HEIMDAL_DEFAULT rules */ + ret = an2ln_default(context, rule, aname, lnsize, lname); + if (ret == KRB5_PLUGIN_NO_HANDLE) + /* Try DB, RULE, ... plugins */ + ret = an2ln_plugin(context, rule, aname, lnsize, lname); + + if (ret == 0 && lnsize && !lname[0]) + continue; /* Success but no lname?! lies! */ + else if (ret != KRB5_PLUGIN_NO_HANDLE) + break; + } + + if (ret == KRB5_PLUGIN_NO_HANDLE) { + if (lnsize) + lname[0] = '\0'; + ret = KRB5_NO_LOCALNAME; + } + + krb5_config_free_strings(rules); + return ret; +} + +static krb5_error_code KRB5_LIB_CALL +an2ln_def_plug_init(krb5_context context, void **ctx) +{ + *ctx = NULL; + return 0; +} + +static void KRB5_LIB_CALL +an2ln_def_plug_fini(void *ctx) +{ +} + +static heim_base_once_t sorted_text_db_init_once = HEIM_BASE_ONCE_INIT; + +static void +sorted_text_db_init_f(void *arg) +{ + (void) heim_db_register("sorted-text", NULL, &heim_sorted_text_file_dbtype); +} + +static krb5_error_code KRB5_LIB_CALL +an2ln_def_plug_an2ln(void *plug_ctx, krb5_context context, + const char *rule, + krb5_const_principal aname, + set_result_f set_res_f, void *set_res_ctx) +{ + krb5_error_code ret; + const char *an2ln_db_fname; + heim_db_t dbh = NULL; + heim_dict_t db_options; + heim_data_t k, v; + heim_error_t error; + char *unparsed = NULL; + char *value = NULL; + + _krb5_load_db_plugins(context); + heim_base_once_f(&sorted_text_db_init_once, NULL, sorted_text_db_init_f); + + if (strncmp(rule, "DB:", strlen("DB:")) != 0) + return KRB5_PLUGIN_NO_HANDLE; + + an2ln_db_fname = &rule[strlen("DB:")]; + if (!*an2ln_db_fname) + return KRB5_PLUGIN_NO_HANDLE; + + ret = krb5_unparse_name(context, aname, &unparsed); + if (ret) + return ret; + + db_options = heim_dict_create(11); + if (db_options != NULL) + heim_dict_set_value(db_options, HSTR("read-only"), + heim_number_create(1)); + dbh = heim_db_create(NULL, an2ln_db_fname, db_options, &error); + heim_release(db_options); + if (dbh == NULL) { + krb5_set_error_message(context, heim_error_get_code(error), + N_("Couldn't open aname2lname-text-db", "")); + ret = KRB5_PLUGIN_NO_HANDLE; + goto cleanup; + } + + /* Binary search; file should be sorted (in C locale) */ + k = heim_data_ref_create(unparsed, strlen(unparsed), NULL); + if (k == NULL) { + ret = krb5_enomem(context); + goto cleanup; + } + v = heim_db_copy_value(dbh, NULL, k, &error); + heim_release(k); + if (v == NULL && error != NULL) { + krb5_set_error_message(context, heim_error_get_code(error), + N_("Lookup in aname2lname-text-db failed", "")); + ret = heim_error_get_code(error); + goto cleanup; + } else if (v == NULL) { + ret = KRB5_PLUGIN_NO_HANDLE; + goto cleanup; + } else { + /* found */ + if (heim_data_get_length(v) == 0) { + krb5_set_error_message(context, ret, + N_("Principal mapped to empty username", "")); + ret = KRB5_NO_LOCALNAME; + goto cleanup; + } + value = strndup(heim_data_get_ptr(v), heim_data_get_length(v)); + heim_release(v); + if (value == NULL) { + ret = krb5_enomem(context); + goto cleanup; + } + ret = set_res_f(set_res_ctx, value); + } + +cleanup: + heim_release(dbh); + free(unparsed); + free(value); + return ret; +} + |