2 * Copyright (c) 2001-2003 Networks Associates Technology, Inc.
3 * Copyright (c) 2004-2011 Dag-Erling Smørgrav
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
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
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
35 * $Id: openpam_configure.c 500 2011-11-22 12:07:03Z des $
48 #include <security/pam_appl.h>
50 #include "openpam_impl.h"
51 #include "openpam_strlcmp.h"
53 static int openpam_load_chain(pam_handle_t *, const char *, pam_facility_t);
56 * Evaluates to non-zero if the argument is a linear whitespace character.
59 (ch == ' ' || ch == '\t')
62 * Evaluates to non-zero if the argument is a printable ASCII character.
63 * Assumes that the execution character set is a superset of ASCII.
66 (ch >= '!' && ch <= '~')
69 * Returns non-zero if the argument belongs to the POSIX Portable Filename
70 * Character Set. Assumes that the execution character set is a superset
74 ((ch >= '0' && ch <= '9') || \
75 (ch >= 'A' && ch <= 'Z') || \
76 (ch >= 'a' && ch <= 'z') || \
77 ch == '.' || ch == '_' || ch == '-')
80 * Parse the service name.
82 * Returns the length of the service name, or 0 if the end of the string
83 * was reached or a disallowed non-whitespace character was encountered.
85 * If parse_service_name() is successful, it updates *service to point to
86 * the first character of the service name and *line to point one
87 * character past the end. If it reaches the end of the string, it
88 * updates *line to point to the terminating NUL character and leaves
89 * *service unmodified. In all other cases, it leaves both *line and
90 * *service unmodified.
92 * Allowed characters are all characters in the POSIX portable filename
96 parse_service_name(char **line, char **service)
100 for (b = *line; *b && is_lws(*b); ++b)
106 for (e = b; *e && !is_lws(*e); ++e)
117 * Parse the facility name.
119 * Returns the corresponding pam_facility_t value, or -1 if the end of the
120 * string was reached, a disallowed non-whitespace character was
121 * encountered, or the first word was not a recognized facility name.
123 * If parse_facility_name() is successful, it updates *line to point one
124 * character past the end of the facility name. If it reaches the end of
125 * the string, it updates *line to point to the terminating NUL character.
126 * In all other cases, it leaves *line unmodified.
128 static pam_facility_t
129 parse_facility_name(char **line)
134 for (b = *line; *b && is_lws(*b); ++b)
138 return ((pam_facility_t)-1);
140 for (e = b; *e && !is_lws(*e); ++e)
143 return ((pam_facility_t)-1);
144 for (i = 0; i < PAM_NUM_FACILITIES; ++i)
145 if (strlcmp(pam_facility_name[i], b, e - b) == 0)
147 if (i == PAM_NUM_FACILITIES)
148 return ((pam_facility_t)-1);
154 * Parse the word "include".
156 * If the next word on the line is "include", parse_include() updates
157 * *line to point one character past "include" and returns 1. Otherwise,
158 * it leaves *line unmodified and returns 0.
161 parse_include(char **line)
165 for (b = *line; *b && is_lws(*b); ++b)
171 for (e = b; *e && !is_lws(*e); ++e)
175 if (strlcmp("include", b, e - b) != 0)
182 * Parse the control flag.
184 * Returns the corresponding pam_control_t value, or -1 if the end of the
185 * string was reached, a disallowed non-whitespace character was
186 * encountered, or the first word was not a recognized control flag.
188 * If parse_control_flag() is successful, it updates *line to point one
189 * character past the end of the control flag. If it reaches the end of
190 * the string, it updates *line to point to the terminating NUL character.
191 * In all other cases, it leaves *line unmodified.
194 parse_control_flag(char **line)
199 for (b = *line; *b && is_lws(*b); ++b)
203 return ((pam_control_t)-1);
205 for (e = b; *e && !is_lws(*e); ++e)
208 return ((pam_control_t)-1);
209 for (i = 0; i < PAM_NUM_CONTROL_FLAGS; ++i)
210 if (strlcmp(pam_control_flag_name[i], b, e - b) == 0)
212 if (i == PAM_NUM_CONTROL_FLAGS)
213 return ((pam_control_t)-1);
221 * Returns the length of the file name, or 0 if the end of the string was
222 * reached or a disallowed non-whitespace character was encountered.
224 * If parse_filename() is successful, it updates *filename to point to the
225 * first character of the filename and *line to point one character past
226 * the end. If it reaches the end of the string, it updates *line to
227 * point to the terminating NUL character and leaves *filename unmodified.
228 * In all other cases, it leaves both *line and *filename unmodified.
230 * Allowed characters are all characters in the POSIX portable filename
231 * character set, plus the path separator (forward slash).
234 parse_filename(char **line, char **filename)
238 for (b = *line; *b && is_lws(*b); ++b)
244 for (e = b; *e && !is_lws(*e); ++e)
245 if (!is_pfcs(*e) && *e != '/')
257 * Returns a dynamically allocated string containing the next module
258 * option, or NULL if the end of the string was reached or a disallowed
259 * non-whitespace character was encountered.
261 * If parse_option() is successful, it updates *line to point one
262 * character past the end of the option. If it reaches the end of the
263 * string, it updates *line to point to the terminating NUL character. In
264 * all other cases, it leaves *line unmodified.
266 * If parse_option() fails to allocate memory, it will return NULL and set
267 * errno to a non-zero value.
269 * Allowed characters for option names are all characters in the POSIX
270 * portable filename character set. Allowed characters for option values
271 * are any printable non-whitespace characters. The option value may be
272 * quoted in either single or double quotes, in which case space
273 * characters and whichever quote character was not used are allowed.
274 * Note that the entire value must be quoted, not just part of it.
277 parse_option(char **line)
279 char *nb, *ne, *vb, *ve;
285 for (nb = *line; *nb && is_lws(*nb); ++nb)
291 for (ne = nb; *ne && !is_lws(*ne) && *ne != '='; ++ne)
298 if (*vb == '"' || *vb == '\'')
301 *ve && *ve != q && (is_p(*ve) || (q && is_lws(*ve)));
305 /* non-printable character or missing endquote */
307 if (q && *(ve + 1) && !is_lws(*(ve + 1)))
308 /* garbage after value */
313 size = (ne - nb) + 1;
315 size += (ve - vb) + 1;
316 if ((option = malloc(size)) == NULL)
318 strncpy(option, nb, ne - nb);
320 option[ne - nb] = '=';
321 strncpy(option + (ne - nb) + 1, vb, ve - vb);
323 option[size - 1] = '\0';
324 *line = q ? ve + 1 : ve;
329 * Consume trailing whitespace.
331 * If there are no non-whitespace characters left on the line, parse_eol()
332 * updates *line to point at the terminating NUL character and returns 0.
333 * Otherwise, it leaves *line unmodified and returns a non-zero value.
336 parse_eol(char **line)
340 for (p = *line; *p && is_lws(*p); ++p)
343 return ((unsigned char)*p);
348 typedef enum { pam_conf_style, pam_d_style } openpam_style_t;
351 * Extracts given chains from a policy file.
354 openpam_parse_chain(pam_handle_t *pamh,
356 pam_facility_t facility,
357 const char *filename,
358 openpam_style_t style)
360 pam_chain_t *this, **next;
363 char *line, *str, *name;
364 char *option, **optv;
365 int len, lineno, ret;
368 if ((f = fopen(filename, "r")) == NULL) {
369 openpam_log(errno == ENOENT ? PAM_LOG_DEBUG : PAM_LOG_NOTICE,
371 return (PAM_SUCCESS);
373 if (openpam_check_desc_owner_perms(filename, fileno(f)) != 0) {
375 return (PAM_SYSTEM_ERR);
380 while ((line = openpam_readline(f, &lineno, NULL)) != NULL) {
381 /* get service name if necessary */
382 if (style == pam_conf_style) {
383 if ((len = parse_service_name(&line, &str)) == 0) {
384 openpam_log(PAM_LOG_NOTICE,
385 "%s(%d): invalid service name (ignored)",
390 if (strlcmp(service, str, len) != 0) {
396 /* get facility name */
397 if ((fclt = parse_facility_name(&line)) == (pam_facility_t)-1) {
398 openpam_log(PAM_LOG_ERROR,
399 "%s(%d): missing or invalid facility",
403 if (facility != fclt && facility != PAM_FACILITY_ANY) {
408 /* check for "include" */
409 if (parse_include(&line)) {
410 if ((len = parse_service_name(&line, &str)) == 0) {
411 openpam_log(PAM_LOG_ERROR,
412 "%s(%d): missing or invalid filename",
416 if ((name = strndup(str, len)) == NULL)
418 if (parse_eol(&line) != 0) {
419 openpam_log(PAM_LOG_ERROR,
420 "%s(%d): garbage at end of line",
424 ret = openpam_load_chain(pamh, name, fclt);
426 if (ret != PAM_SUCCESS)
432 /* get control flag */
433 if ((ctlf = parse_control_flag(&line)) == (pam_control_t)-1) {
434 openpam_log(PAM_LOG_ERROR,
435 "%s(%d): missing or invalid control flag",
440 /* get module name */
441 if ((len = parse_filename(&line, &str)) == 0) {
442 openpam_log(PAM_LOG_ERROR,
443 "%s(%d): missing or invalid module name",
447 if ((name = strndup(str, len)) == NULL)
450 /* allocate new entry */
451 if ((this = calloc(1, sizeof *this)) == NULL)
455 /* get module options */
456 if ((this->optv = malloc(sizeof *optv)) == NULL)
459 while ((option = parse_option(&line)) != NULL) {
460 optv = realloc(this->optv,
461 (this->optc + 2) * sizeof *optv);
465 this->optv[this->optc++] = option;
467 this->optv[this->optc] = NULL;
469 openpam_log(PAM_LOG_ERROR,
470 "%s(%d): syntax error in module options",
476 this->module = openpam_load_module(name);
478 if (this->module == NULL)
482 for (next = &pamh->chains[fclt]; *next != NULL;
483 next = &(*next)->next)
494 return (PAM_SUCCESS);
496 openpam_log(PAM_LOG_ERROR, "%s: %m", filename);
498 if (this && this->optc) {
500 FREE(this->optv[this->optc]);
507 return (PAM_SYSTEM_ERR);
510 static const char *openpam_policy_path[] = {
513 "/usr/local/etc/pam.d/",
514 "/usr/local/etc/pam.conf",
519 * Locates the policy file for a given service and reads the given chains
523 openpam_load_chain(pam_handle_t *pamh,
525 pam_facility_t facility)
532 for (path = openpam_policy_path; *path != NULL; ++path) {
534 if ((*path)[len - 1] == '/') {
535 if (asprintf(&filename, "%s%s", *path, service) < 0) {
536 openpam_log(PAM_LOG_ERROR, "asprintf(): %m");
537 return (PAM_BUF_ERR);
539 ret = openpam_parse_chain(pamh, service, facility,
540 filename, pam_d_style);
543 ret = openpam_parse_chain(pamh, service, facility,
544 *path, pam_conf_style);
546 if (ret != PAM_SUCCESS)
549 return (PAM_SUCCESS);
555 * Configure a service
559 openpam_configure(pam_handle_t *pamh,
565 for (p = service; *p; ++p)
567 return (PAM_SYSTEM_ERR);
569 if (openpam_load_chain(pamh, service, PAM_FACILITY_ANY) != PAM_SUCCESS)
572 for (fclt = 0; fclt < PAM_NUM_FACILITIES; ++fclt) {
573 if (pamh->chains[fclt] != NULL)
575 if (openpam_load_chain(pamh, PAM_OTHER, fclt) != PAM_SUCCESS)
578 return (PAM_SUCCESS);
580 openpam_clear_chains(pamh->chains);
581 return (PAM_SYSTEM_ERR);