Home | History | Annotate | Line # | Download | only in libpam
      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