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