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