if_vtnet - No need to use the taskqueue to run vtnet_update_link_status().
[dragonfly.git] / contrib / openpam / lib / openpam_configure.c
blobd85cd8a4b084d9a2180fc08fbcaa5284705941e6
1 /*-
2 * Copyright (c) 2001-2003 Networks Associates Technology, Inc.
3 * Copyright (c) 2004-2014 Dag-Erling Smørgrav
4 * All rights reserved.
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.
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.
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.
35 * $Id: openpam_configure.c 612 2012-05-26 23:02:55Z des $
38 #ifdef HAVE_CONFIG_H
39 # include "config.h"
40 #endif
42 #include <sys/param.h>
44 #include <ctype.h>
45 #include <errno.h>
46 #include <stdio.h>
47 #include <stdlib.h>
48 #include <string.h>
50 #include <security/pam_appl.h>
52 #include "openpam_impl.h"
53 #include "openpam_ctype.h"
54 #include "openpam_strlcat.h"
55 #include "openpam_strlcpy.h"
57 static int openpam_load_chain(pam_handle_t *, const char *, pam_facility_t);
60 * Validate a service name.
62 * Returns a non-zero value if the argument points to a NUL-terminated
63 * string consisting entirely of characters in the POSIX portable filename
64 * character set, excluding the path separator character.
66 static int
67 valid_service_name(const char *name)
69 const char *p;
71 if (OPENPAM_FEATURE(RESTRICT_SERVICE_NAME)) {
72 /* path separator not allowed */
73 for (p = name; *p != '\0'; ++p)
74 if (!is_pfcs(*p))
75 return (0);
76 } else {
77 /* path separator allowed */
78 for (p = name; *p != '\0'; ++p)
79 if (!is_pfcs(*p) && *p != '/')
80 return (0);
82 return (1);
86 * Parse the facility name.
88 * Returns the corresponding pam_facility_t value, or -1 if the argument
89 * is not a valid facility name.
91 static pam_facility_t
92 parse_facility_name(const char *name)
94 int i;
96 for (i = 0; i < PAM_NUM_FACILITIES; ++i)
97 if (strcmp(pam_facility_name[i], name) == 0)
98 return (i);
99 return ((pam_facility_t)-1);
103 * Parse the control flag.
105 * Returns the corresponding pam_control_t value, or -1 if the argument is
106 * not a valid control flag name.
108 static pam_control_t
109 parse_control_flag(const char *name)
111 int i;
113 for (i = 0; i < PAM_NUM_CONTROL_FLAGS; ++i)
114 if (strcmp(pam_control_flag_name[i], name) == 0)
115 return (i);
116 return ((pam_control_t)-1);
120 * Validate a file name.
122 * Returns a non-zero value if the argument points to a NUL-terminated
123 * string consisting entirely of characters in the POSIX portable filename
124 * character set, including the path separator character.
126 static int
127 valid_module_name(const char *name)
129 const char *p;
131 if (OPENPAM_FEATURE(RESTRICT_MODULE_NAME)) {
132 /* path separator not allowed */
133 for (p = name; *p != '\0'; ++p)
134 if (!is_pfcs(*p))
135 return (0);
136 } else {
137 /* path separator allowed */
138 for (p = name; *p != '\0'; ++p)
139 if (!is_pfcs(*p) && *p != '/')
140 return (0);
142 return (1);
145 typedef enum { pam_conf_style, pam_d_style } openpam_style_t;
148 * Extracts given chains from a policy file.
150 * Returns the number of policy entries which were found for the specified
151 * service and facility, or -1 if a system error occurred or a syntax
152 * error was encountered.
154 static int
155 openpam_parse_chain(pam_handle_t *pamh,
156 const char *service,
157 pam_facility_t facility,
158 FILE *f,
159 const char *filename,
160 openpam_style_t style)
162 pam_chain_t *this, **next;
163 pam_facility_t fclt;
164 pam_control_t ctlf;
165 char *name, *servicename, *modulename;
166 int count, lineno, ret, serrno;
167 char **wordv, *word;
168 int i, wordc;
170 count = 0;
171 this = NULL;
172 name = NULL;
173 lineno = 0;
174 wordc = 0;
175 wordv = NULL;
176 while ((wordv = openpam_readlinev(f, &lineno, &wordc)) != NULL) {
177 /* blank line? */
178 if (wordc == 0) {
179 FREEV(wordc, wordv);
180 continue;
182 i = 0;
184 /* check service name if necessary */
185 if (style == pam_conf_style &&
186 strcmp(wordv[i++], service) != 0) {
187 FREEV(wordc, wordv);
188 continue;
191 /* check facility name */
192 if ((word = wordv[i++]) == NULL ||
193 (fclt = parse_facility_name(word)) == (pam_facility_t)-1) {
194 openpam_log(PAM_LOG_ERROR,
195 "%s(%d): missing or invalid facility",
196 filename, lineno);
197 errno = EINVAL;
198 goto fail;
200 if (facility != fclt && facility != PAM_FACILITY_ANY) {
201 FREEV(wordc, wordv);
202 continue;
205 /* check for "include" */
206 if ((word = wordv[i++]) != NULL &&
207 strcmp(word, "include") == 0) {
208 if ((servicename = wordv[i++]) == NULL ||
209 !valid_service_name(servicename)) {
210 openpam_log(PAM_LOG_ERROR,
211 "%s(%d): missing or invalid service name",
212 filename, lineno);
213 errno = EINVAL;
214 goto fail;
216 if (wordv[i] != NULL) {
217 openpam_log(PAM_LOG_ERROR,
218 "%s(%d): garbage at end of line",
219 filename, lineno);
220 errno = EINVAL;
221 goto fail;
223 ret = openpam_load_chain(pamh, servicename, fclt);
224 FREEV(wordc, wordv);
225 if (ret < 0) {
227 * Bogus errno, but this ensures that the
228 * outer loop does not just ignore the
229 * error and keep searching.
231 if (errno == ENOENT)
232 errno = EINVAL;
233 goto fail;
235 continue;
238 /* get control flag */
239 if (word == NULL || /* same word we compared to "include" */
240 (ctlf = parse_control_flag(word)) == (pam_control_t)-1) {
241 openpam_log(PAM_LOG_ERROR,
242 "%s(%d): missing or invalid control flag",
243 filename, lineno);
244 errno = EINVAL;
245 goto fail;
248 /* get module name */
249 if ((modulename = wordv[i++]) == NULL ||
250 !valid_module_name(modulename)) {
251 openpam_log(PAM_LOG_ERROR,
252 "%s(%d): missing or invalid module name",
253 filename, lineno);
254 errno = EINVAL;
255 goto fail;
258 /* allocate new entry */
259 if ((this = calloc(1, sizeof *this)) == NULL)
260 goto syserr;
261 this->flag = ctlf;
263 /* load module */
264 if ((this->module = openpam_load_module(modulename)) == NULL) {
265 if (errno == ENOENT)
266 errno = ENOEXEC;
267 goto fail;
271 * The remaining items in wordv are the module's
272 * arguments. We could set this->optv = wordv + i, but
273 * then free(this->optv) wouldn't work. Instead, we free
274 * the words we've already consumed, shift the rest up,
275 * and clear the tail end of the array.
277 this->optc = wordc - i;
278 for (i = 0; i < wordc - this->optc; ++i) {
279 FREE(wordv[i]);
281 for (i = 0; i < this->optc; ++i) {
282 wordv[i] = wordv[wordc - this->optc + i];
283 wordv[wordc - this->optc + i] = NULL;
285 this->optv = wordv;
286 wordv = NULL;
287 wordc = 0;
289 /* hook it up */
290 for (next = &pamh->chains[fclt]; *next != NULL;
291 next = &(*next)->next)
292 /* nothing */ ;
293 *next = this;
294 this = NULL;
295 ++count;
298 * The loop ended because openpam_readword() returned NULL, which
299 * can happen for four different reasons: an I/O error (ferror(f)
300 * is true), a memory allocation failure (ferror(f) is false,
301 * feof(f) is false, errno is non-zero), the file ended with an
302 * unterminated quote or backslash escape (ferror(f) is false,
303 * feof(f) is true, errno is non-zero), or the end of the file was
304 * reached without error (ferror(f) is false, feof(f) is true,
305 * errno is zero).
307 if (ferror(f) || errno != 0)
308 goto syserr;
309 if (!feof(f))
310 goto fail;
311 fclose(f);
312 return (count);
313 syserr:
314 serrno = errno;
315 openpam_log(PAM_LOG_ERROR, "%s: %m", filename);
316 errno = serrno;
317 /* fall through */
318 fail:
319 serrno = errno;
320 if (this && this->optc && this->optv)
321 FREEV(this->optc, this->optv);
322 FREE(this);
323 FREEV(wordc, wordv);
324 FREE(wordv);
325 FREE(name);
326 fclose(f);
327 errno = serrno;
328 return (-1);
331 static const char *openpam_policy_path[] = {
332 "/etc/pam.d/",
333 "/etc/pam.conf",
334 "/usr/local/etc/pam.d/",
335 "/usr/local/etc/pam.conf",
336 NULL
340 * Read the specified chains from the specified file.
342 * Returns 0 if the file exists but does not contain any matching lines.
344 * Returns -1 and sets errno to ENOENT if the file does not exist.
346 * Returns -1 and sets errno to some other non-zero value if the file
347 * exists but is unsafe or unreadable, or an I/O error occurs.
349 static int
350 openpam_load_file(pam_handle_t *pamh,
351 const char *service,
352 pam_facility_t facility,
353 const char *filename,
354 openpam_style_t style)
356 FILE *f;
357 int ret, serrno;
359 /* attempt to open the file */
360 if ((f = fopen(filename, "r")) == NULL) {
361 serrno = errno;
362 openpam_log(errno == ENOENT ? PAM_LOG_DEBUG : PAM_LOG_ERROR,
363 "%s: %m", filename);
364 errno = serrno;
365 RETURNN(-1);
366 } else {
367 openpam_log(PAM_LOG_DEBUG, "found %s", filename);
370 /* verify type, ownership and permissions */
371 if (OPENPAM_FEATURE(VERIFY_POLICY_FILE) &&
372 openpam_check_desc_owner_perms(filename, fileno(f)) != 0) {
373 /* already logged the cause */
374 serrno = errno;
375 fclose(f);
376 errno = serrno;
377 RETURNN(-1);
380 /* parse the file */
381 ret = openpam_parse_chain(pamh, service, facility,
382 f, filename, style);
383 RETURNN(ret);
387 * Locates the policy file for a given service and reads the given chains
388 * from it.
390 * Returns the number of policy entries which were found for the specified
391 * service and facility, or -1 if a system error occurred or a syntax
392 * error was encountered.
394 static int
395 openpam_load_chain(pam_handle_t *pamh,
396 const char *service,
397 pam_facility_t facility)
399 const char *p, **path;
400 char filename[PATH_MAX];
401 size_t len;
402 openpam_style_t style;
403 int ret;
405 ENTERS(facility < 0 ? "any" : pam_facility_name[facility]);
407 /* either absolute or relative to cwd */
408 if (strchr(service, '/') != NULL) {
409 if ((p = strrchr(service, '.')) != NULL && strcmp(p, ".conf") == 0)
410 style = pam_conf_style;
411 else
412 style = pam_d_style;
413 ret = openpam_load_file(pamh, service, facility,
414 service, style);
415 RETURNN(ret);
418 /* search standard locations */
419 for (path = openpam_policy_path; *path != NULL; ++path) {
420 /* construct filename */
421 len = strlcpy(filename, *path, sizeof filename);
422 if (filename[len - 1] == '/') {
423 len = strlcat(filename, service, sizeof filename);
424 if (len >= sizeof filename) {
425 errno = ENAMETOOLONG;
426 RETURNN(-1);
428 style = pam_d_style;
429 } else {
430 style = pam_conf_style;
432 ret = openpam_load_file(pamh, service, facility,
433 filename, style);
434 /* success */
435 if (ret > 0)
436 RETURNN(ret);
437 /* the file exists, but an error occurred */
438 if (ret == -1 && errno != ENOENT)
439 RETURNN(ret);
440 /* in pam.d style, an empty file counts as a hit */
441 if (ret == 0 && style == pam_d_style)
442 RETURNN(ret);
445 /* no hit */
446 errno = ENOENT;
447 RETURNN(-1);
451 * OpenPAM internal
453 * Configure a service
457 openpam_configure(pam_handle_t *pamh,
458 const char *service)
460 pam_facility_t fclt;
461 int serrno;
463 ENTERS(service);
464 if (!valid_service_name(service)) {
465 openpam_log(PAM_LOG_ERROR, "invalid service name");
466 RETURNC(PAM_SYSTEM_ERR);
468 if (openpam_load_chain(pamh, service, PAM_FACILITY_ANY) < 0) {
469 if (errno != ENOENT)
470 goto load_err;
472 for (fclt = 0; fclt < PAM_NUM_FACILITIES; ++fclt) {
473 if (pamh->chains[fclt] != NULL)
474 continue;
475 if (openpam_load_chain(pamh, PAM_OTHER, fclt) < 0)
476 goto load_err;
478 RETURNC(PAM_SUCCESS);
479 load_err:
480 serrno = errno;
481 openpam_clear_chains(pamh->chains);
482 errno = serrno;
483 RETURNC(PAM_SYSTEM_ERR);
487 * NODOC
489 * Error codes:
490 * PAM_SYSTEM_ERR