1/* $NetBSD: kuserok.c,v 1.4 2023/06/19 21:41:44 christos Exp $ */ 2 3/* 4 * Copyright (c) 1997 - 2005 Kungliga Tekniska H��gskolan 5 * (Royal Institute of Technology, Stockholm, Sweden). 6 * All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 12 * 1. Redistributions of source code must retain the above copyright 13 * notice, this list of conditions and the following disclaimer. 14 * 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 19 * 3. Neither the name of the Institute nor the names of its contributors 20 * may be used to endorse or promote products derived from this software 21 * without specific prior written permission. 22 * 23 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND 24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 26 * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE 27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 33 * SUCH DAMAGE. 34 */ 35 36#include "krb5_locl.h" 37#include "kuserok_plugin.h" 38#include <dirent.h> 39 40#ifndef SYSTEM_K5LOGIN_DIR 41/* 42 * System k5login location. File namess in this directory are expected 43 * to be usernames and to contain a list of principals allowed to login 44 * as the user named the same as the file. 45 */ 46#define SYSTEM_K5LOGIN_DIR SYSCONFDIR "/k5login.d" 47#endif 48 49/* Plugin framework bits */ 50 51struct plctx { 52 const char *rule; 53 const char *k5login_dir; 54 const char *luser; 55 krb5_const_principal principal; 56 unsigned int flags; 57 krb5_boolean result; 58}; 59 60static krb5_error_code KRB5_LIB_CALL 61plcallback(krb5_context context, const void *plug, void *plugctx, void *userctx) 62{ 63 const krb5plugin_kuserok_ftable *locate = plug; 64 struct plctx *plctx = userctx; 65 66 return locate->kuserok(plugctx, context, plctx->rule, plctx->flags, 67 plctx->k5login_dir, plctx->luser, plctx->principal, 68 &plctx->result); 69} 70 71static krb5_error_code plugin_reg_ret; 72static krb5plugin_kuserok_ftable kuserok_simple_plug; 73static krb5plugin_kuserok_ftable kuserok_sys_k5login_plug; 74static krb5plugin_kuserok_ftable kuserok_user_k5login_plug; 75static krb5plugin_kuserok_ftable kuserok_deny_plug; 76 77static void 78reg_def_plugins_once(void *ctx) 79{ 80 krb5_error_code ret; 81 krb5_context context = ctx; 82 83 plugin_reg_ret = krb5_plugin_register(context, PLUGIN_TYPE_DATA, 84 KRB5_PLUGIN_KUSEROK, 85 &kuserok_simple_plug); 86 ret = krb5_plugin_register(context, PLUGIN_TYPE_DATA, 87 KRB5_PLUGIN_KUSEROK, &kuserok_sys_k5login_plug); 88 if (!plugin_reg_ret) 89 plugin_reg_ret = ret; 90 ret = krb5_plugin_register(context, PLUGIN_TYPE_DATA, 91 KRB5_PLUGIN_KUSEROK, &kuserok_user_k5login_plug); 92 if (!plugin_reg_ret) 93 plugin_reg_ret = ret; 94 ret = krb5_plugin_register(context, PLUGIN_TYPE_DATA, 95 KRB5_PLUGIN_KUSEROK, &kuserok_deny_plug); 96 if (!plugin_reg_ret) 97 plugin_reg_ret = ret; 98} 99 100/** 101 * This function is designed to be portable for Win32 and POSIX. The 102 * design does lead to multiple getpwnam_r() calls, but this is probably 103 * not a big deal. 104 * 105 * Inputs: 106 * 107 * @param context A krb5_context 108 * @param filename Name of item to introspection 109 * @param is_system_location TRUE if the dir/file are system locations or 110 * FALSE if they are user home directory locations 111 * @param dir Directory (optional) 112 * @param dirlstat A pointer to struct stat for the directory (optional) 113 * @param file File (optional) 114 * @param owner Name of user that is expected to own the file 115 */ 116 117static krb5_error_code 118check_owner_dir(krb5_context context, 119 const char *filename, 120 krb5_boolean is_system_location, 121 DIR *dir, 122 struct stat *dirlstat, 123 const char *owner) 124{ 125#ifdef _WIN32 126 /* 127 * XXX Implement this! 128 * 129 * The thing to do is to call _get_osfhandle() on fileno(file) and 130 * dirfd(dir) to get HANDLEs to the same, then call 131 * GetSecurityInfo() on those HANDLEs to get the security descriptor 132 * (SD), then check the owner and DACL. Checking the DACL sounds 133 * like a lot of work (what, derive a mode from the ACL the way 134 * NFSv4 servers do?). Checking the owner means doing an LSARPC 135 * lookup at least (to get the user's SID). 136 */ 137 if (is_system_location || owner == NULL) 138 return 0; 139 krb5_set_error_message(context, EACCES, 140 "User k5login files not supported on Windows"); 141 return EACCES; 142#else 143 struct passwd pw, *pwd = NULL; 144 char pwbuf[2048]; 145 struct stat st; 146 147 heim_assert(owner != NULL, "no directory owner ?"); 148 149 if (rk_getpwnam_r(owner, &pw, pwbuf, sizeof(pwbuf), &pwd) != 0) { 150 krb5_set_error_message(context, errno, 151 "User unknown %s (getpwnam_r())", owner); 152 return EACCES; 153 } 154 if (pwd == NULL) { 155 krb5_set_error_message(context, EACCES, "no user %s", owner); 156 return EACCES; 157 } 158 159 if (fstat(dirfd(dir), &st) == -1) { 160 krb5_set_error_message(context, EACCES, 161 "fstat(%s) of k5login.d failed", 162 filename); 163 return EACCES; 164 } 165 if (!S_ISDIR(st.st_mode)) { 166 krb5_set_error_message(context, ENOTDIR, "%s not a directory", 167 filename); 168 return ENOTDIR; 169 } 170 if (st.st_dev != dirlstat->st_dev || st.st_ino != dirlstat->st_ino) { 171 krb5_set_error_message(context, EACCES, 172 "%s was renamed during kuserok " 173 "operation", filename); 174 return EACCES; 175 } 176 if ((st.st_mode & (S_IWGRP | S_IWOTH)) != 0) { 177 krb5_set_error_message(context, EACCES, 178 "%s has world and/or group write " 179 "permissions", filename); 180 return EACCES; 181 } 182 if (pwd->pw_uid != st.st_uid && st.st_uid != 0) { 183 krb5_set_error_message(context, EACCES, 184 "%s not owned by the user (%s) or root", 185 filename, owner); 186 return EACCES; 187 } 188 189 return 0; 190#endif 191} 192 193static krb5_error_code 194check_owner_file(krb5_context context, 195 const char *filename, 196 FILE *file, const char *owner) 197{ 198#ifdef _WIN32 199 /* 200 * XXX Implement this! 201 * 202 * The thing to do is to call _get_osfhandle() on fileno(file) and 203 * dirfd(dir) to get HANDLEs to the same, then call 204 * GetSecurityInfo() on those HANDLEs to get the security descriptor 205 * (SD), then check the owner and DACL. Checking the DACL sounds 206 * like a lot of work (what, derive a mode from the ACL the way 207 * NFSv4 servers do?). Checking the owner means doing an LSARPC 208 * lookup at least (to get the user's SID). 209 */ 210 if (owner == NULL) 211 return 0; 212 213 krb5_set_error_message(context, EACCES, 214 "User k5login files not supported on Windows"); 215 return EACCES; 216#else 217 struct passwd pw, *pwd = NULL; 218 char pwbuf[2048]; 219 struct stat st; 220 221 if (owner == NULL) 222 return 0; 223 224 if (rk_getpwnam_r(owner, &pw, pwbuf, sizeof(pwbuf), &pwd) != 0) { 225 krb5_set_error_message(context, errno, 226 "User unknown %s (getpwnam_r())", owner); 227 return EACCES; 228 } 229 if (pwd == NULL) { 230 krb5_set_error_message(context, EACCES, "no user %s", owner); 231 return EACCES; 232 } 233 234 if (fstat(fileno(file), &st) == -1) { 235 krb5_set_error_message(context, EACCES, "fstat(%s) of k5login failed", 236 filename); 237 return EACCES; 238 } 239 if (S_ISDIR(st.st_mode)) { 240 krb5_set_error_message(context, EISDIR, "k5login: %s is a directory", 241 filename); 242 return EISDIR; 243 } 244 if ((st.st_mode & (S_IWGRP | S_IWOTH)) != 0) { 245 krb5_set_error_message(context, EISDIR, 246 "k5login %s has world and/or group write " 247 "permissions", filename); 248 return EACCES; 249 } 250 if (pwd->pw_uid != st.st_uid && st.st_uid != 0) { 251 krb5_set_error_message(context, EACCES, 252 "k5login %s not owned by the user or root", 253 filename); 254 return EACCES; 255 } 256 257 return 0; 258#endif 259} 260 261 262/* see if principal is mentioned in the filename access file, return 263 TRUE (in result) if so, FALSE otherwise */ 264 265static krb5_error_code 266check_one_file(krb5_context context, 267 const char *filename, 268 const char *owner, 269 krb5_boolean is_system_location, 270 krb5_const_principal principal, 271 krb5_boolean *result) 272{ 273 FILE *f; 274 char buf[BUFSIZ]; 275 krb5_error_code ret; 276 277 *result = FALSE; 278 279 f = fopen(filename, "r"); 280 if (f == NULL) 281 return errno; 282 rk_cloexec_file(f); 283 284 ret = check_owner_file(context, filename, f, owner); 285 if (ret) 286 goto out; 287 288 while (fgets(buf, sizeof(buf), f) != NULL) { 289 krb5_principal tmp; 290 char *newline = buf + strcspn(buf, "\n"); 291 292 if (*newline != '\n') { 293 int c; 294 c = fgetc(f); 295 if (c != EOF) { 296 while (c != EOF && c != '\n') 297 c = fgetc(f); 298 /* line was too long, so ignore it */ 299 continue; 300 } 301 } 302 *newline = '\0'; 303 ret = krb5_parse_name(context, buf, &tmp); 304 if (ret) 305 continue; 306 *result = krb5_principal_compare(context, principal, tmp); 307 krb5_free_principal(context, tmp); 308 if (*result) { 309 fclose (f); 310 return 0; 311 } 312 } 313 314out: 315 fclose(f); 316 return 0; 317} 318 319static krb5_error_code 320check_directory(krb5_context context, 321 const char *dirname, 322 const char *owner, 323 krb5_boolean is_system_location, 324 krb5_const_principal principal, 325 krb5_boolean *result) 326{ 327 DIR *d; 328 struct dirent *dent; 329 char filename[MAXPATHLEN]; 330 size_t len; 331 krb5_error_code ret = 0; 332 struct stat st; 333 334 *result = FALSE; 335 336 if (lstat(dirname, &st) < 0) 337 return errno; 338 339 if (!S_ISDIR(st.st_mode)) { 340 krb5_set_error_message(context, ENOTDIR, "k5login.d not a directory"); 341 return ENOTDIR; 342 } 343 344 if ((d = opendir(dirname)) == NULL) { 345 krb5_set_error_message(context, ENOTDIR, "Could not open k5login.d"); 346 return errno; 347 } 348 349 ret = check_owner_dir(context, dirname, is_system_location, d, &st, owner); 350 if (ret) 351 goto out; 352 353 while ((dent = readdir(d)) != NULL) { 354 /* 355 * XXX: Should we also skip files whose names start with "."? 356 * Vim ".filename.swp" files are also good candidates to skip. 357 * Once we ignore "#*" and "*~", it is not clear what other 358 * heuristics to apply. 359 */ 360 if (strcmp(dent->d_name, ".") == 0 || 361 strcmp(dent->d_name, "..") == 0 || 362 dent->d_name[0] == '#' || /* emacs autosave */ 363 dent->d_name[strlen(dent->d_name) - 1] == '~') /* emacs backup */ 364 continue; 365 len = snprintf(filename, sizeof(filename), "%s/%s", dirname, dent->d_name); 366 /* Skip too-long filenames that got truncated by snprintf() */ 367 if (len < sizeof(filename)) { 368 ret = check_one_file(context, filename, owner, is_system_location, 369 principal, result); 370 if (ret == 0 && *result == TRUE) 371 break; 372 } 373 ret = 0; /* don't propagate errors upstream */ 374 } 375 376out: 377 closedir(d); 378 return ret; 379} 380 381static krb5_error_code 382check_an2ln(krb5_context context, 383 krb5_const_principal principal, 384 const char *luser, 385 krb5_boolean *result) 386{ 387 krb5_error_code ret; 388 char *lname; 389 390#if 0 391 /* XXX Should we make this an option? */ 392 /* multi-component principals can never match */ 393 if (krb5_principal_get_comp_string(context, principal, 1) != NULL) { 394 *result = FALSE; 395 return 0; 396 } 397#endif 398 399 lname = malloc(strlen(luser) + 1); 400 if (lname == NULL) 401 return krb5_enomem(context); 402 ret = krb5_aname_to_localname(context, principal, strlen(luser)+1, lname); 403 if (ret) 404 goto out; 405 if (strcmp(lname, luser) == 0) 406 *result = TRUE; 407 else 408 *result = FALSE; 409 410out: 411 free(lname); 412 return 0; 413 414} 415 416/** 417 * This function takes the name of a local user and checks if 418 * principal is allowed to log in as that user. 419 * 420 * The user may have a ~/.k5login file listing principals that are 421 * allowed to login as that user. If that file does not exist, all 422 * principals with a only one component that is identical to the 423 * username, and a realm considered local, are allowed access. 424 * 425 * The .k5login file must contain one principal per line, be owned by 426 * user and not be writable by group or other (but must be readable by 427 * anyone). 428 * 429 * Note that if the file exists, no implicit access rights are given 430 * to user@@LOCALREALM. 431 * 432 * Optionally, a set of files may be put in ~/.k5login.d (a 433 * directory), in which case they will all be checked in the same 434 * manner as .k5login. The files may be called anything, but files 435 * starting with a hash (#) , or ending with a tilde (~) are 436 * ignored. Subdirectories are not traversed. Note that this directory 437 * may not be checked by other Kerberos implementations. 438 * 439 * If no configuration file exists, match user against local domains, 440 * ie luser@@LOCAL-REALMS-IN-CONFIGURATION-FILES. 441 * 442 * @param context Kerberos 5 context. 443 * @param principal principal to check if allowed to login 444 * @param luser local user id 445 * 446 * @return returns TRUE if access should be granted, FALSE otherwise. 447 * 448 * @ingroup krb5_support 449 */ 450 451KRB5_LIB_FUNCTION krb5_boolean KRB5_LIB_CALL 452krb5_kuserok(krb5_context context, 453 krb5_principal principal, 454 const char *luser) 455{ 456 return _krb5_kuserok(context, principal, luser, TRUE); 457} 458 459 460KRB5_LIB_FUNCTION krb5_boolean KRB5_LIB_CALL 461_krb5_kuserok(krb5_context context, 462 krb5_principal principal, 463 const char *luser, 464 krb5_boolean an2ln_ok) 465{ 466 static heim_base_once_t reg_def_plugins = HEIM_BASE_ONCE_INIT; 467 krb5_error_code ret; 468 struct plctx ctx; 469 char **rules; 470 471 /* 472 * XXX we should have a struct with a krb5_context field and a 473 * krb5_error_code fied and pass the address of that as the ctx 474 * argument of heim_base_once_f(). For now we use a static to 475 * communicate failures. Actually, we ignore failures anyways, 476 * since we can't return them. 477 */ 478 heim_base_once_f(®_def_plugins, context, reg_def_plugins_once); 479 480 ctx.flags = 0; 481 ctx.luser = luser; 482 ctx.principal = principal; 483 ctx.result = FALSE; 484 485 ctx.k5login_dir = krb5_config_get_string(context, NULL, "libdefaults", 486 "k5login_directory", NULL); 487 488 if (an2ln_ok) 489 ctx.flags |= KUSEROK_ANAME_TO_LNAME_OK; 490 491 if (krb5_config_get_bool_default(context, NULL, FALSE, "libdefaults", 492 "k5login_authoritative", NULL)) 493 ctx.flags |= KUSEROK_K5LOGIN_IS_AUTHORITATIVE; 494 495 if ((ctx.flags & KUSEROK_K5LOGIN_IS_AUTHORITATIVE) && plugin_reg_ret) 496 return plugin_reg_ret; /* fail safe */ 497 498 rules = krb5_config_get_strings(context, NULL, "libdefaults", 499 "kuserok", NULL); 500 if (rules == NULL) { 501 /* Default: check ~/.k5login */ 502 ctx.rule = "USER-K5LOGIN"; 503 504 ret = plcallback(context, &kuserok_user_k5login_plug, NULL, &ctx); 505 if (ret == 0) 506 goto out; 507 508 ctx.rule = "SIMPLE"; 509 ret = plcallback(context, &kuserok_simple_plug, NULL, &ctx); 510 if (ret == 0) 511 goto out; 512 513 ctx.result = FALSE; 514 } else { 515 size_t n; 516 517 for (n = 0; rules[n]; n++) { 518 ctx.rule = rules[n]; 519 520 ret = _krb5_plugin_run_f(context, "krb5", KRB5_PLUGIN_KUSEROK, 521 KRB5_PLUGIN_KUSEROK_VERSION_0, 0, 522 &ctx, plcallback); 523 if (ret != KRB5_PLUGIN_NO_HANDLE) 524 goto out; 525 } 526 } 527 528out: 529 krb5_config_free_strings(rules); 530 531 return ctx.result; 532} 533 534/* 535 * Simple kuserok: check that the lname for the aname matches luser. 536 */ 537 538static krb5_error_code KRB5_LIB_CALL 539kuserok_simple_plug_f(void *plug_ctx, krb5_context context, const char *rule, 540 unsigned int flags, const char *k5login_dir, 541 const char *luser, krb5_const_principal principal, 542 krb5_boolean *result) 543{ 544 krb5_error_code ret; 545 546 if (strcmp(rule, "SIMPLE") != 0 || (flags & KUSEROK_ANAME_TO_LNAME_OK) == 0) 547 return KRB5_PLUGIN_NO_HANDLE; 548 549 ret = check_an2ln(context, principal, luser, result); 550 if (ret == 0 && *result == FALSE) 551 return KRB5_PLUGIN_NO_HANDLE; 552 553 return 0; 554} 555 556/* 557 * Check k5login files in a system location, rather than in home 558 * directories. 559 */ 560 561static krb5_error_code KRB5_LIB_CALL 562kuserok_sys_k5login_plug_f(void *plug_ctx, krb5_context context, 563 const char *rule, unsigned int flags, 564 const char *k5login_dir, const char *luser, 565 krb5_const_principal principal, krb5_boolean *result) 566{ 567 char filename[MAXPATHLEN]; 568 size_t len; 569 const char *profile_dir = NULL; 570 krb5_error_code ret; 571 572 *result = FALSE; 573 574 if (strcmp(rule, "SYSTEM-K5LOGIN") != 0 && 575 strncmp(rule, "SYSTEM-K5LOGIN:", strlen("SYSTEM-K5LOGIN:")) != 0) 576 return KRB5_PLUGIN_NO_HANDLE; 577 578 profile_dir = strchr(rule, ':'); 579 if (profile_dir == NULL) 580 profile_dir = k5login_dir ? k5login_dir : SYSTEM_K5LOGIN_DIR; 581 else 582 profile_dir++; 583 584 len = snprintf(filename, sizeof(filename), "%s/%s", profile_dir, luser); 585 if (len < sizeof(filename)) { 586 ret = check_one_file(context, filename, NULL, TRUE, principal, result); 587 588 if (ret == 0 && 589 ((flags & KUSEROK_K5LOGIN_IS_AUTHORITATIVE) || *result == TRUE)) 590 return 0; 591 } 592 593 *result = FALSE; 594 return KRB5_PLUGIN_NO_HANDLE; 595} 596 597/* 598 * Check ~luser/.k5login and/or ~/luser/.k5login.d 599 */ 600 601static krb5_error_code KRB5_LIB_CALL 602kuserok_user_k5login_plug_f(void *plug_ctx, krb5_context context, 603 const char *rule, unsigned int flags, 604 const char *k5login_dir, const char *luser, 605 krb5_const_principal principal, 606 krb5_boolean *result) 607{ 608#ifdef _WIN32 609 return KRB5_PLUGIN_NO_HANDLE; 610#else 611 char *path; 612 char *path_exp; 613 const char *profile_dir = NULL; 614 krb5_error_code ret; 615 krb5_boolean found_file = FALSE; 616 struct passwd pw, *pwd = NULL; 617 char pwbuf[2048]; 618 619 if (strcmp(rule, "USER-K5LOGIN") != 0) 620 return KRB5_PLUGIN_NO_HANDLE; 621 622 profile_dir = k5login_dir; 623 if (profile_dir == NULL) { 624 /* Don't deadlock with gssd or anything of the sort */ 625 if (!_krb5_homedir_access(context)) 626 return KRB5_PLUGIN_NO_HANDLE; 627 628 if (rk_getpwnam_r(luser, &pw, pwbuf, sizeof(pwbuf), &pwd) != 0) { 629 krb5_set_error_message(context, errno, "User unknown (getpwnam_r())"); 630 return KRB5_PLUGIN_NO_HANDLE; 631 } 632 if (pwd == NULL) { 633 krb5_set_error_message(context, errno, "User unknown (getpwnam())"); 634 return KRB5_PLUGIN_NO_HANDLE; 635 } 636 profile_dir = pwd->pw_dir; 637 } 638 639#define KLOGIN "/.k5login" 640 641 if (asprintf(&path, "%s/.k5login.d", profile_dir) == -1) 642 return krb5_enomem(context); 643 644 ret = _krb5_expand_path_tokensv(context, path, 1, &path_exp, 645 "luser", luser, NULL); 646 free(path); 647 if (ret) 648 return ret; 649 path = path_exp; 650 651 /* check user's ~/.k5login */ 652 path[strlen(path) - strlen(".d")] = '\0'; 653 ret = check_one_file(context, path, luser, FALSE, principal, result); 654 655 /* 656 * A match in ~/.k5login is sufficient. A non-match, falls through to the 657 * .k5login.d code below. 658 */ 659 if (ret == 0 && *result == TRUE) { 660 free(path); 661 return 0; 662 } 663 if (ret != ENOENT) 664 found_file = TRUE; 665 666 /* 667 * A match in ~/.k5login.d/somefile is sufficient. A non-match, falls 668 * through to the code below that handles negative results. 669 * 670 * XXX: put back the .d; clever|hackish? you decide 671 */ 672 path[strlen(path)] = '.'; 673 ret = check_directory(context, path, luser, FALSE, principal, result); 674 free(path); 675 if (ret == 0 && *result == TRUE) 676 return 0; 677 if (ret != ENOENT && ret != ENOTDIR) 678 found_file = TRUE; 679 680 /* 681 * When either ~/.k5login or ~/.k5login.d/ exists, but neither matches 682 * and we're authoritative, we're done. Otherwise, give other plugins 683 * a chance. 684 */ 685 *result = FALSE; 686 if (found_file && (flags & KUSEROK_K5LOGIN_IS_AUTHORITATIVE)) 687 return 0; 688 return KRB5_PLUGIN_NO_HANDLE; 689#endif 690} 691 692static krb5_error_code KRB5_LIB_CALL 693kuserok_deny_plug_f(void *plug_ctx, krb5_context context, const char *rule, 694 unsigned int flags, const char *k5login_dir, 695 const char *luser, krb5_const_principal principal, 696 krb5_boolean *result) 697{ 698 if (strcmp(rule, "DENY") != 0) 699 return KRB5_PLUGIN_NO_HANDLE; 700 701 *result = FALSE; 702 return 0; 703} 704 705static krb5_error_code KRB5_LIB_CALL 706kuser_ok_null_plugin_init(krb5_context context, void **ctx) 707{ 708 *ctx = NULL; 709 return 0; 710} 711 712static void KRB5_LIB_CALL 713kuser_ok_null_plugin_fini(void *ctx) 714{ 715 return; 716} 717 718static krb5plugin_kuserok_ftable kuserok_simple_plug = { 719 KRB5_PLUGIN_KUSEROK_VERSION_0, 720 kuser_ok_null_plugin_init, 721 kuser_ok_null_plugin_fini, 722 kuserok_simple_plug_f, 723}; 724 725static krb5plugin_kuserok_ftable kuserok_sys_k5login_plug = { 726 KRB5_PLUGIN_KUSEROK_VERSION_0, 727 kuser_ok_null_plugin_init, 728 kuser_ok_null_plugin_fini, 729 kuserok_sys_k5login_plug_f, 730}; 731 732static krb5plugin_kuserok_ftable kuserok_user_k5login_plug = { 733 KRB5_PLUGIN_KUSEROK_VERSION_0, 734 kuser_ok_null_plugin_init, 735 kuser_ok_null_plugin_fini, 736 kuserok_user_k5login_plug_f, 737}; 738 739static krb5plugin_kuserok_ftable kuserok_deny_plug = { 740 KRB5_PLUGIN_KUSEROK_VERSION_0, 741 kuser_ok_null_plugin_init, 742 kuser_ok_null_plugin_fini, 743 kuserok_deny_plug_f, 744}; 745 746