1 1.8 riastrad /* $NetBSD: openpam_configure.c,v 1.8 2025/09/06 12:23:17 riastradh Exp $ */ 2 1.2 christos 3 1.1 christos /*- 4 1.1 christos * Copyright (c) 2001-2003 Networks Associates Technology, Inc. 5 1.6 christos * Copyright (c) 2004-2025 Dag-Erling Smrgrav 6 1.1 christos * All rights reserved. 7 1.1 christos * 8 1.1 christos * This software was developed for the FreeBSD Project by ThinkSec AS and 9 1.1 christos * Network Associates Laboratories, the Security Research Division of 10 1.1 christos * Network Associates, Inc. under DARPA/SPAWAR contract N66001-01-C-8035 11 1.1 christos * ("CBOSS"), as part of the DARPA CHATS research program. 12 1.1 christos * 13 1.1 christos * Redistribution and use in source and binary forms, with or without 14 1.1 christos * modification, are permitted provided that the following conditions 15 1.1 christos * are met: 16 1.1 christos * 1. Redistributions of source code must retain the above copyright 17 1.1 christos * notice, this list of conditions and the following disclaimer. 18 1.1 christos * 2. Redistributions in binary form must reproduce the above copyright 19 1.1 christos * notice, this list of conditions and the following disclaimer in the 20 1.1 christos * documentation and/or other materials provided with the distribution. 21 1.1 christos * 3. The name of the author may not be used to endorse or promote 22 1.1 christos * products derived from this software without specific prior written 23 1.1 christos * permission. 24 1.1 christos * 25 1.1 christos * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 26 1.1 christos * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 27 1.1 christos * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 28 1.1 christos * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 29 1.1 christos * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 30 1.1 christos * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 31 1.1 christos * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 32 1.1 christos * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 33 1.1 christos * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 34 1.1 christos * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 35 1.1 christos * SUCH DAMAGE. 36 1.1 christos */ 37 1.1 christos 38 1.1 christos #ifdef HAVE_CONFIG_H 39 1.1 christos # include "config.h" 40 1.1 christos #endif 41 1.1 christos 42 1.2 christos #include <sys/cdefs.h> 43 1.8 riastrad __RCSID("$NetBSD: openpam_configure.c,v 1.8 2025/09/06 12:23:17 riastradh Exp $"); 44 1.2 christos 45 1.1 christos #include <sys/param.h> 46 1.1 christos 47 1.1 christos #include <errno.h> 48 1.1 christos #include <stdio.h> 49 1.1 christos #include <stdlib.h> 50 1.1 christos #include <string.h> 51 1.1 christos 52 1.1 christos #include <security/pam_appl.h> 53 1.1 christos 54 1.1 christos #include "openpam_impl.h" 55 1.1 christos #include "openpam_ctype.h" 56 1.1 christos #include "openpam_strlcat.h" 57 1.1 christos #include "openpam_strlcpy.h" 58 1.1 christos 59 1.1 christos static int openpam_load_chain(pam_handle_t *, const char *, pam_facility_t); 60 1.1 christos 61 1.1 christos /* 62 1.1 christos * Validate a service name. 63 1.1 christos * 64 1.1 christos * Returns a non-zero value if the argument points to a NUL-terminated 65 1.1 christos * string consisting entirely of characters in the POSIX portable filename 66 1.1 christos * character set, excluding the path separator character. 67 1.1 christos */ 68 1.1 christos static int 69 1.1 christos valid_service_name(const char *name) 70 1.1 christos { 71 1.1 christos const char *p; 72 1.1 christos 73 1.1 christos if (OPENPAM_FEATURE(RESTRICT_SERVICE_NAME)) { 74 1.1 christos /* path separator not allowed */ 75 1.1 christos for (p = name; *p != '\0'; ++p) 76 1.1 christos if (!is_pfcs(*p)) 77 1.1 christos return (0); 78 1.1 christos } else { 79 1.1 christos /* path separator allowed */ 80 1.1 christos for (p = name; *p != '\0'; ++p) 81 1.1 christos if (!is_pfcs(*p) && *p != '/') 82 1.1 christos return (0); 83 1.1 christos } 84 1.1 christos return (1); 85 1.1 christos } 86 1.1 christos 87 1.1 christos /* 88 1.1 christos * Parse the facility name. 89 1.1 christos * 90 1.1 christos * Returns the corresponding pam_facility_t value, or -1 if the argument 91 1.1 christos * is not a valid facility name. 92 1.1 christos */ 93 1.1 christos static pam_facility_t 94 1.1 christos parse_facility_name(const char *name) 95 1.1 christos { 96 1.1 christos int i; 97 1.1 christos 98 1.1 christos for (i = 0; i < PAM_NUM_FACILITIES; ++i) 99 1.1 christos if (strcmp(pam_facility_name[i], name) == 0) 100 1.1 christos return (i); 101 1.1 christos return ((pam_facility_t)-1); 102 1.1 christos } 103 1.1 christos 104 1.1 christos /* 105 1.1 christos * Parse the control flag. 106 1.1 christos * 107 1.1 christos * Returns the corresponding pam_control_t value, or -1 if the argument is 108 1.1 christos * not a valid control flag name. 109 1.1 christos */ 110 1.1 christos static pam_control_t 111 1.1 christos parse_control_flag(const char *name) 112 1.1 christos { 113 1.2 christos pam_control_t i; 114 1.1 christos 115 1.2 christos for (i = PAM_BINDING; i < PAM_NUM_CONTROL_FLAGS; ++i) 116 1.1 christos if (strcmp(pam_control_flag_name[i], name) == 0) 117 1.1 christos return (i); 118 1.1 christos return ((pam_control_t)-1); 119 1.1 christos } 120 1.1 christos 121 1.1 christos /* 122 1.1 christos * Validate a file name. 123 1.1 christos * 124 1.1 christos * Returns a non-zero value if the argument points to a NUL-terminated 125 1.1 christos * string consisting entirely of characters in the POSIX portable filename 126 1.1 christos * character set, including the path separator character. 127 1.1 christos */ 128 1.1 christos static int 129 1.1 christos valid_module_name(const char *name) 130 1.1 christos { 131 1.1 christos const char *p; 132 1.1 christos 133 1.1 christos if (OPENPAM_FEATURE(RESTRICT_MODULE_NAME)) { 134 1.1 christos /* path separator not allowed */ 135 1.1 christos for (p = name; *p != '\0'; ++p) 136 1.1 christos if (!is_pfcs(*p)) 137 1.1 christos return (0); 138 1.1 christos } else { 139 1.1 christos /* path separator allowed */ 140 1.1 christos for (p = name; *p != '\0'; ++p) 141 1.1 christos if (!is_pfcs(*p) && *p != '/') 142 1.1 christos return (0); 143 1.1 christos } 144 1.1 christos return (1); 145 1.1 christos } 146 1.1 christos 147 1.1 christos typedef enum { pam_conf_style, pam_d_style } openpam_style_t; 148 1.1 christos 149 1.1 christos /* 150 1.1 christos * Extracts given chains from a policy file. 151 1.1 christos * 152 1.1 christos * Returns the number of policy entries which were found for the specified 153 1.1 christos * service and facility, or -1 if a system error occurred or a syntax 154 1.1 christos * error was encountered. 155 1.1 christos */ 156 1.1 christos static int 157 1.1 christos openpam_parse_chain(pam_handle_t *pamh, 158 1.1 christos const char *service, 159 1.1 christos pam_facility_t facility, 160 1.1 christos FILE *f, 161 1.1 christos const char *filename, 162 1.1 christos openpam_style_t style) 163 1.1 christos { 164 1.1 christos pam_chain_t *this, **next; 165 1.6 christos pam_module_t *module; 166 1.1 christos pam_facility_t fclt; 167 1.1 christos pam_control_t ctlf; 168 1.1 christos char *name, *servicename, *modulename; 169 1.6 christos int count, lineno, nonfatal, ret, serrno; 170 1.1 christos char **wordv, *word; 171 1.1 christos int i, wordc; 172 1.1 christos 173 1.1 christos count = 0; 174 1.1 christos this = NULL; 175 1.1 christos name = NULL; 176 1.1 christos lineno = 0; 177 1.1 christos wordc = 0; 178 1.1 christos wordv = NULL; 179 1.1 christos while ((wordv = openpam_readlinev(f, &lineno, &wordc)) != NULL) { 180 1.1 christos /* blank line? */ 181 1.1 christos if (wordc == 0) { 182 1.1 christos FREEV(wordc, wordv); 183 1.1 christos continue; 184 1.1 christos } 185 1.1 christos i = 0; 186 1.1 christos 187 1.1 christos /* check service name if necessary */ 188 1.1 christos if (style == pam_conf_style && 189 1.1 christos strcmp(wordv[i++], service) != 0) { 190 1.1 christos FREEV(wordc, wordv); 191 1.1 christos continue; 192 1.1 christos } 193 1.1 christos 194 1.1 christos /* check facility name */ 195 1.6 christos if ((word = wordv[i++]) == NULL) { 196 1.1 christos openpam_log(PAM_LOG_ERROR, 197 1.6 christos "%s(%d): missing facility", 198 1.6 christos filename, lineno); 199 1.6 christos errno = EINVAL; 200 1.6 christos goto fail; 201 1.6 christos } 202 1.6 christos if (*word == '-') { 203 1.6 christos nonfatal = 1; 204 1.6 christos word++; 205 1.6 christos } else { 206 1.6 christos nonfatal = 0; 207 1.6 christos } 208 1.6 christos if ((fclt = parse_facility_name(word)) == (pam_facility_t)-1) { 209 1.6 christos openpam_log(PAM_LOG_ERROR, 210 1.6 christos "%s(%d): invalid facility", 211 1.1 christos filename, lineno); 212 1.1 christos errno = EINVAL; 213 1.1 christos goto fail; 214 1.1 christos } 215 1.1 christos if (facility != fclt && facility != PAM_FACILITY_ANY) { 216 1.1 christos FREEV(wordc, wordv); 217 1.1 christos continue; 218 1.1 christos } 219 1.1 christos 220 1.6 christos /* control flag or "include" */ 221 1.6 christos if ((word = wordv[i++]) == NULL) { 222 1.6 christos openpam_log(PAM_LOG_ERROR, 223 1.6 christos "%s(%d): missing control flag", 224 1.6 christos filename, lineno); 225 1.6 christos errno = EINVAL; 226 1.6 christos goto fail; 227 1.6 christos } 228 1.6 christos if (strcmp(word, "include") == 0) { 229 1.6 christos if ((servicename = wordv[i++]) == NULL) { 230 1.6 christos openpam_log(PAM_LOG_ERROR, 231 1.6 christos "%s(%d): missing service name", 232 1.6 christos filename, lineno); 233 1.6 christos errno = EINVAL; 234 1.6 christos goto fail; 235 1.6 christos } 236 1.6 christos if (!valid_service_name(servicename)) { 237 1.1 christos openpam_log(PAM_LOG_ERROR, 238 1.6 christos "%s(%d): invalid service name", 239 1.1 christos filename, lineno); 240 1.1 christos errno = EINVAL; 241 1.1 christos goto fail; 242 1.1 christos } 243 1.1 christos if (wordv[i] != NULL) { 244 1.1 christos openpam_log(PAM_LOG_ERROR, 245 1.1 christos "%s(%d): garbage at end of line", 246 1.1 christos filename, lineno); 247 1.1 christos errno = EINVAL; 248 1.1 christos goto fail; 249 1.1 christos } 250 1.1 christos ret = openpam_load_chain(pamh, servicename, fclt); 251 1.1 christos FREEV(wordc, wordv); 252 1.1 christos if (ret < 0) { 253 1.1 christos /* 254 1.1 christos * Bogus errno, but this ensures that the 255 1.1 christos * outer loop does not just ignore the 256 1.1 christos * error and keep searching. 257 1.1 christos */ 258 1.6 christos if (errno == ENOENT) { 259 1.6 christos if (nonfatal) 260 1.6 christos continue; 261 1.1 christos errno = EINVAL; 262 1.6 christos } 263 1.1 christos goto fail; 264 1.1 christos } 265 1.1 christos continue; 266 1.1 christos } 267 1.6 christos if ((ctlf = parse_control_flag(word)) == (pam_control_t)-1) { 268 1.1 christos openpam_log(PAM_LOG_ERROR, 269 1.6 christos "%s(%d): invalid control flag", 270 1.1 christos filename, lineno); 271 1.1 christos errno = EINVAL; 272 1.1 christos goto fail; 273 1.1 christos } 274 1.1 christos 275 1.1 christos /* get module name */ 276 1.6 christos if ((modulename = wordv[i++]) == NULL) { 277 1.6 christos openpam_log(PAM_LOG_ERROR, 278 1.6 christos "%s(%d): missing module name", 279 1.6 christos filename, lineno); 280 1.6 christos errno = EINVAL; 281 1.6 christos goto fail; 282 1.6 christos } 283 1.6 christos if (!valid_module_name(modulename)) { 284 1.1 christos openpam_log(PAM_LOG_ERROR, 285 1.6 christos "%s(%d): invalid module name", 286 1.1 christos filename, lineno); 287 1.1 christos errno = EINVAL; 288 1.1 christos goto fail; 289 1.1 christos } 290 1.1 christos 291 1.1 christos /* load module */ 292 1.6 christos if ((module = openpam_load_module(modulename)) == NULL) { 293 1.6 christos if (errno == ENOENT) { 294 1.6 christos if (nonfatal) { 295 1.6 christos FREEV(wordc, wordv); 296 1.6 christos continue; 297 1.6 christos } 298 1.1 christos errno = ENOEXEC; 299 1.6 christos } 300 1.1 christos goto fail; 301 1.1 christos } 302 1.1 christos 303 1.6 christos /* allocate new entry */ 304 1.8 riastrad if ((this = calloc(1, sizeof *this)) == NULL) 305 1.6 christos goto syserr; 306 1.7 riastrad this->flag = (int)ctlf; 307 1.6 christos this->module = module; 308 1.6 christos 309 1.1 christos /* 310 1.1 christos * The remaining items in wordv are the module's 311 1.1 christos * arguments. We could set this->optv = wordv + i, but 312 1.1 christos * then free(this->optv) wouldn't work. Instead, we free 313 1.1 christos * the words we've already consumed, shift the rest up, 314 1.1 christos * and clear the tail end of the array. 315 1.1 christos */ 316 1.1 christos this->optc = wordc - i; 317 1.1 christos for (i = 0; i < wordc - this->optc; ++i) { 318 1.1 christos FREE(wordv[i]); 319 1.1 christos } 320 1.1 christos for (i = 0; i < this->optc; ++i) { 321 1.1 christos wordv[i] = wordv[wordc - this->optc + i]; 322 1.1 christos wordv[wordc - this->optc + i] = NULL; 323 1.1 christos } 324 1.1 christos this->optv = wordv; 325 1.1 christos wordv = NULL; 326 1.1 christos wordc = 0; 327 1.1 christos 328 1.1 christos /* hook it up */ 329 1.1 christos for (next = &pamh->chains[fclt]; *next != NULL; 330 1.1 christos next = &(*next)->next) 331 1.1 christos /* nothing */ ; 332 1.1 christos *next = this; 333 1.1 christos this = NULL; 334 1.1 christos ++count; 335 1.1 christos } 336 1.1 christos /* 337 1.1 christos * The loop ended because openpam_readword() returned NULL, which 338 1.1 christos * can happen for four different reasons: an I/O error (ferror(f) 339 1.1 christos * is true), a memory allocation failure (ferror(f) is false, 340 1.1 christos * feof(f) is false, errno is non-zero), the file ended with an 341 1.1 christos * unterminated quote or backslash escape (ferror(f) is false, 342 1.1 christos * feof(f) is true, errno is non-zero), or the end of the file was 343 1.1 christos * reached without error (ferror(f) is false, feof(f) is true, 344 1.1 christos * errno is zero). 345 1.1 christos */ 346 1.1 christos if (ferror(f) || errno != 0) 347 1.1 christos goto syserr; 348 1.1 christos if (!feof(f)) 349 1.1 christos goto fail; 350 1.1 christos fclose(f); 351 1.1 christos return (count); 352 1.1 christos syserr: 353 1.1 christos serrno = errno; 354 1.1 christos openpam_log(PAM_LOG_ERROR, "%s: %m", filename); 355 1.1 christos errno = serrno; 356 1.1 christos /* fall through */ 357 1.1 christos fail: 358 1.1 christos serrno = errno; 359 1.1 christos if (this && this->optc && this->optv) 360 1.1 christos FREEV(this->optc, this->optv); 361 1.1 christos FREE(this); 362 1.1 christos FREEV(wordc, wordv); 363 1.1 christos FREE(wordv); 364 1.1 christos FREE(name); 365 1.1 christos fclose(f); 366 1.1 christos errno = serrno; 367 1.1 christos return (-1); 368 1.1 christos } 369 1.1 christos 370 1.1 christos /* 371 1.1 christos * Read the specified chains from the specified file. 372 1.1 christos * 373 1.1 christos * Returns 0 if the file exists but does not contain any matching lines. 374 1.1 christos * 375 1.1 christos * Returns -1 and sets errno to ENOENT if the file does not exist. 376 1.1 christos * 377 1.1 christos * Returns -1 and sets errno to some other non-zero value if the file 378 1.1 christos * exists but is unsafe or unreadable, or an I/O error occurs. 379 1.1 christos */ 380 1.1 christos static int 381 1.1 christos openpam_load_file(pam_handle_t *pamh, 382 1.1 christos const char *service, 383 1.1 christos pam_facility_t facility, 384 1.1 christos const char *filename, 385 1.1 christos openpam_style_t style) 386 1.1 christos { 387 1.1 christos FILE *f; 388 1.1 christos int ret, serrno; 389 1.1 christos 390 1.1 christos /* attempt to open the file */ 391 1.1 christos if ((f = fopen(filename, "r")) == NULL) { 392 1.1 christos serrno = errno; 393 1.1 christos openpam_log(errno == ENOENT ? PAM_LOG_DEBUG : PAM_LOG_ERROR, 394 1.1 christos "%s: %m", filename); 395 1.1 christos errno = serrno; 396 1.1 christos RETURNN(-1); 397 1.1 christos } else { 398 1.1 christos openpam_log(PAM_LOG_DEBUG, "found %s", filename); 399 1.1 christos } 400 1.1 christos 401 1.1 christos /* verify type, ownership and permissions */ 402 1.1 christos if (OPENPAM_FEATURE(VERIFY_POLICY_FILE) && 403 1.1 christos openpam_check_desc_owner_perms(filename, fileno(f)) != 0) { 404 1.1 christos /* already logged the cause */ 405 1.1 christos serrno = errno; 406 1.1 christos fclose(f); 407 1.1 christos errno = serrno; 408 1.1 christos RETURNN(-1); 409 1.1 christos } 410 1.1 christos 411 1.1 christos /* parse the file */ 412 1.1 christos ret = openpam_parse_chain(pamh, service, facility, 413 1.1 christos f, filename, style); 414 1.1 christos RETURNN(ret); 415 1.1 christos } 416 1.1 christos 417 1.1 christos /* 418 1.1 christos * Locates the policy file for a given service and reads the given chains 419 1.1 christos * from it. 420 1.1 christos * 421 1.1 christos * Returns the number of policy entries which were found for the specified 422 1.1 christos * service and facility, or -1 if a system error occurred or a syntax 423 1.1 christos * error was encountered. 424 1.1 christos */ 425 1.1 christos static int 426 1.1 christos openpam_load_chain(pam_handle_t *pamh, 427 1.1 christos const char *service, 428 1.1 christos pam_facility_t facility) 429 1.1 christos { 430 1.1 christos const char *p, **path; 431 1.1 christos char filename[PATH_MAX]; 432 1.1 christos size_t len; 433 1.1 christos openpam_style_t style; 434 1.1 christos int ret; 435 1.1 christos 436 1.1 christos ENTERS(facility < 0 ? "any" : pam_facility_name[facility]); 437 1.1 christos 438 1.1 christos /* either absolute or relative to cwd */ 439 1.1 christos if (strchr(service, '/') != NULL) { 440 1.1 christos if ((p = strrchr(service, '.')) != NULL && strcmp(p, ".conf") == 0) 441 1.1 christos style = pam_conf_style; 442 1.1 christos else 443 1.1 christos style = pam_d_style; 444 1.1 christos ret = openpam_load_file(pamh, service, facility, 445 1.1 christos service, style); 446 1.1 christos RETURNN(ret); 447 1.1 christos } 448 1.1 christos 449 1.1 christos /* search standard locations */ 450 1.1 christos for (path = openpam_policy_path; *path != NULL; ++path) { 451 1.1 christos /* construct filename */ 452 1.1 christos len = strlcpy(filename, *path, sizeof filename); 453 1.3 christos if (len >= sizeof filename) { 454 1.3 christos errno = ENAMETOOLONG; 455 1.3 christos RETURNN(-1); 456 1.3 christos } 457 1.1 christos if (filename[len - 1] == '/') { 458 1.1 christos len = strlcat(filename, service, sizeof filename); 459 1.1 christos if (len >= sizeof filename) { 460 1.1 christos errno = ENAMETOOLONG; 461 1.1 christos RETURNN(-1); 462 1.1 christos } 463 1.1 christos style = pam_d_style; 464 1.1 christos } else { 465 1.1 christos style = pam_conf_style; 466 1.1 christos } 467 1.1 christos ret = openpam_load_file(pamh, service, facility, 468 1.1 christos filename, style); 469 1.1 christos /* success */ 470 1.1 christos if (ret > 0) 471 1.1 christos RETURNN(ret); 472 1.1 christos /* the file exists, but an error occurred */ 473 1.1 christos if (ret == -1 && errno != ENOENT) 474 1.1 christos RETURNN(ret); 475 1.1 christos /* in pam.d style, an empty file counts as a hit */ 476 1.1 christos if (ret == 0 && style == pam_d_style) 477 1.1 christos RETURNN(ret); 478 1.1 christos } 479 1.1 christos 480 1.1 christos /* no hit */ 481 1.1 christos errno = ENOENT; 482 1.1 christos RETURNN(-1); 483 1.1 christos } 484 1.1 christos 485 1.1 christos /* 486 1.1 christos * OpenPAM internal 487 1.1 christos * 488 1.1 christos * Configure a service 489 1.1 christos */ 490 1.1 christos 491 1.1 christos int 492 1.1 christos openpam_configure(pam_handle_t *pamh, 493 1.1 christos const char *service) 494 1.1 christos { 495 1.1 christos pam_facility_t fclt; 496 1.1 christos int serrno; 497 1.1 christos 498 1.1 christos ENTERS(service); 499 1.1 christos if (!valid_service_name(service)) { 500 1.1 christos openpam_log(PAM_LOG_ERROR, "invalid service name"); 501 1.1 christos RETURNC(PAM_SYSTEM_ERR); 502 1.1 christos } 503 1.1 christos if (openpam_load_chain(pamh, service, PAM_FACILITY_ANY) < 0) { 504 1.1 christos if (errno != ENOENT) 505 1.1 christos goto load_err; 506 1.1 christos } 507 1.1 christos for (fclt = 0; fclt < PAM_NUM_FACILITIES; ++fclt) { 508 1.1 christos if (pamh->chains[fclt] != NULL) 509 1.1 christos continue; 510 1.3 christos if (OPENPAM_FEATURE(FALLBACK_TO_OTHER)) { 511 1.3 christos if (openpam_load_chain(pamh, PAM_OTHER, fclt) < 0) 512 1.3 christos goto load_err; 513 1.3 christos } 514 1.1 christos } 515 1.2 christos #ifdef __NetBSD__ 516 1.2 christos /* 517 1.2 christos * On NetBSD we require the AUTH chain to have a binding, 518 1.2 christos * a required, or requisite module. 519 1.2 christos */ 520 1.2 christos { 521 1.2 christos pam_chain_t *this = pamh->chains[PAM_AUTH]; 522 1.2 christos for (; this != NULL; this = this->next) 523 1.2 christos if (this->flag == PAM_BINDING || 524 1.2 christos this->flag == PAM_REQUIRED || 525 1.2 christos this->flag == PAM_REQUISITE) 526 1.2 christos break; 527 1.2 christos if (this == NULL) { 528 1.2 christos openpam_log(PAM_LOG_ERROR, 529 1.2 christos "No required, requisite, or binding component " 530 1.2 christos "in service %s, facility %s", 531 1.2 christos service, pam_facility_name[PAM_AUTH]); 532 1.2 christos goto load_err; 533 1.2 christos } 534 1.2 christos } 535 1.2 christos #endif 536 1.1 christos RETURNC(PAM_SUCCESS); 537 1.1 christos load_err: 538 1.1 christos serrno = errno; 539 1.1 christos openpam_clear_chains(pamh->chains); 540 1.1 christos errno = serrno; 541 1.1 christos RETURNC(PAM_SYSTEM_ERR); 542 1.1 christos } 543 1.1 christos 544 1.1 christos /* 545 1.1 christos * NODOC 546 1.1 christos * 547 1.1 christos * Error codes: 548 1.1 christos * PAM_SYSTEM_ERR 549 1.1 christos */ 550