openpam_configure.c revision 1.7 1 1.7 riastrad /* $NetBSD: openpam_configure.c,v 1.7 2025/09/06 12:17:09 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.7 riastrad __RCSID("$NetBSD: openpam_configure.c,v 1.7 2025/09/06 12:17:09 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.6 christos if ((this = calloc((size_t)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