diff options
Diffstat (limited to 'lib/openpam_configure.c')
-rw-r--r-- | lib/openpam_configure.c | 340 |
1 files changed, 340 insertions, 0 deletions
diff --git a/lib/openpam_configure.c b/lib/openpam_configure.c new file mode 100644 index 0000000..f9197ad --- /dev/null +++ b/lib/openpam_configure.c @@ -0,0 +1,340 @@ +/*- + * Copyright (c) 2001-2003 Networks Associates Technology, Inc. + * Copyright (c) 2004-2007 Dag-Erling Smørgrav + * All rights reserved. + * + * This software was developed for the FreeBSD Project by ThinkSec AS and + * Network Associates Laboratories, the Security Research Division of + * Network Associates, Inc. under DARPA/SPAWAR contract N66001-01-C-8035 + * ("CBOSS"), as part of the DARPA CHATS research program. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $Id: openpam_configure.c 408 2007-12-21 11:36:24Z des $ + */ + +#include <ctype.h> +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include <security/pam_appl.h> + +#include "openpam_impl.h" + +const char *_pam_facility_name[PAM_NUM_FACILITIES] = { + [PAM_ACCOUNT] = "account", + [PAM_AUTH] = "auth", + [PAM_PASSWORD] = "password", + [PAM_SESSION] = "session", +}; + +const char *_pam_control_flag_name[PAM_NUM_CONTROL_FLAGS] = { + [PAM_BINDING] = "binding", + [PAM_OPTIONAL] = "optional", + [PAM_REQUIRED] = "required", + [PAM_REQUISITE] = "requisite", + [PAM_SUFFICIENT] = "sufficient", +}; + +static int openpam_load_chain(pam_handle_t *, const char *, pam_facility_t); + +/* + * Matches a word against the first one in a string. + * Returns non-zero if they match. + */ +static int +match_word(const char *str, const char *word) +{ + + while (*str && tolower(*str) == tolower(*word)) + ++str, ++word; + return (*str == ' ' && *word == '\0'); +} + +/* + * Return a pointer to the next word (or the final NUL) in a string. + */ +static const char * +next_word(const char *str) +{ + + /* skip current word */ + while (*str && *str != ' ') + ++str; + /* skip whitespace */ + while (*str == ' ') + ++str; + return (str); +} + +/* + * Return a malloc()ed copy of the first word in a string. + */ +static char * +dup_word(const char *str) +{ + const char *end; + char *word; + + for (end = str; *end && *end != ' '; ++end) + /* nothing */ ; + if (asprintf(&word, "%.*s", (int)(end - str), str) < 0) + return (NULL); + return (word); +} + +/* + * Return the length of the first word in a string. + */ +static int +wordlen(const char *str) +{ + int i; + + for (i = 0; str[i] && str[i] != ' '; ++i) + /* nothing */ ; + return (i); +} + +typedef enum { pam_conf_style, pam_d_style } openpam_style_t; + +/* + * Extracts given chains from a policy file. + */ +static int +openpam_read_chain(pam_handle_t *pamh, + const char *service, + pam_facility_t facility, + const char *filename, + openpam_style_t style) +{ + pam_chain_t *this, **next; + const char *p, *q; + int count, i, lineno, ret; + pam_facility_t fclt; + pam_control_t ctlf; + char *line, *name; + FILE *f; + + if ((f = fopen(filename, "r")) == NULL) { + openpam_log(errno == ENOENT ? PAM_LOG_DEBUG : PAM_LOG_NOTICE, + "%s: %m", filename); + return (0); + } + this = NULL; + count = lineno = 0; + while ((line = openpam_readline(f, &lineno, NULL)) != NULL) { + p = line; + + /* match service name */ + if (style == pam_conf_style) { + if (!match_word(p, service)) { + FREE(line); + continue; + } + p = next_word(p); + } + + /* match facility name */ + for (fclt = 0; fclt < PAM_NUM_FACILITIES; ++fclt) + if (match_word(p, _pam_facility_name[fclt])) + break; + if (fclt == PAM_NUM_FACILITIES) { + openpam_log(PAM_LOG_NOTICE, + "%s(%d): invalid facility '%.*s' (ignored)", + filename, lineno, wordlen(p), p); + goto fail; + } + if (facility != fclt && facility != PAM_FACILITY_ANY) { + FREE(line); + continue; + } + p = next_word(p); + + /* include other chain */ + if (match_word(p, "include")) { + p = next_word(p); + if (*next_word(p) != '\0') + openpam_log(PAM_LOG_NOTICE, + "%s(%d): garbage at end of 'include' line", + filename, lineno); + if ((name = dup_word(p)) == NULL) + goto syserr; + ret = openpam_load_chain(pamh, name, fclt); + FREE(name); + if (ret < 0) + goto fail; + count += ret; + FREE(line); + continue; + } + + /* allocate new entry */ + if ((this = calloc(1, sizeof *this)) == NULL) + goto syserr; + + /* control flag */ + for (ctlf = 0; ctlf < PAM_NUM_CONTROL_FLAGS; ++ctlf) + if (match_word(p, _pam_control_flag_name[ctlf])) + break; + if (ctlf == PAM_NUM_CONTROL_FLAGS) { + openpam_log(PAM_LOG_ERROR, + "%s(%d): invalid control flag '%.*s'", + filename, lineno, wordlen(p), p); + goto fail; + } + this->flag = ctlf; + + /* module name */ + p = next_word(p); + if (*p == '\0') { + openpam_log(PAM_LOG_ERROR, + "%s(%d): missing module name", + filename, lineno); + goto fail; + } + if ((name = dup_word(p)) == NULL) + goto syserr; + this->module = openpam_load_module(name); + FREE(name); + if (this->module == NULL) + goto fail; + + /* module options */ + p = q = next_word(p); + while (*q != '\0') { + ++this->optc; + q = next_word(q); + } + this->optv = calloc(this->optc + 1, sizeof(char *)); + if (this->optv == NULL) + goto syserr; + for (i = 0; i < this->optc; ++i) { + if ((this->optv[i] = dup_word(p)) == NULL) + goto syserr; + p = next_word(p); + } + + /* hook it up */ + for (next = &pamh->chains[fclt]; *next != NULL; + next = &(*next)->next) + /* nothing */ ; + *next = this; + this = NULL; + ++count; + + /* next please... */ + FREE(line); + } + if (!feof(f)) + goto syserr; + fclose(f); + return (count); + syserr: + openpam_log(PAM_LOG_ERROR, "%s: %m", filename); + fail: + FREE(this); + FREE(line); + fclose(f); + return (-1); +} + +static const char *openpam_policy_path[] = { + "/etc/pam.d/", + "/etc/pam.conf", + "/usr/local/etc/pam.d/", + "/usr/local/etc/pam.conf", + NULL +}; + +/* + * Locates the policy file for a given service and reads the given chains + * from it. + */ +static int +openpam_load_chain(pam_handle_t *pamh, + const char *service, + pam_facility_t facility) +{ + const char **path; + char *filename; + size_t len; + int r; + + for (path = openpam_policy_path; *path != NULL; ++path) { + len = strlen(*path); + if ((*path)[len - 1] == '/') { + if (asprintf(&filename, "%s%s", *path, service) < 0) { + openpam_log(PAM_LOG_ERROR, "asprintf(): %m"); + return (-PAM_BUF_ERR); + } + r = openpam_read_chain(pamh, service, facility, + filename, pam_d_style); + FREE(filename); + } else { + r = openpam_read_chain(pamh, service, facility, + *path, pam_conf_style); + } + if (r != 0) + return (r); + } + return (0); +} + +/* + * OpenPAM internal + * + * Configure a service + */ + +int +openpam_configure(pam_handle_t *pamh, + const char *service) +{ + pam_facility_t fclt; + + if (openpam_load_chain(pamh, service, PAM_FACILITY_ANY) < 0) + goto load_err; + + for (fclt = 0; fclt < PAM_NUM_FACILITIES; ++fclt) { + if (pamh->chains[fclt] != NULL) + continue; + if (openpam_load_chain(pamh, PAM_OTHER, fclt) < 0) + goto load_err; + } + return (PAM_SUCCESS); + load_err: + openpam_clear_chains(pamh->chains); + return (PAM_SYSTEM_ERR); +} + +/* + * NODOC + * + * Error codes: + * PAM_SYSTEM_ERR + */ |