/*- * 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,v 1.5 2008/01/27 01:22:59 christos Exp $ */ #include #include #include #include #include #include #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((unsigned char)*str) == tolower((unsigned char)*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((size_t)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((size_t)(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", #ifndef __NetBSD__ "/usr/local/etc/pam.d/", "/usr/local/etc/pam.conf", #else /* Possibly /usr/pkg? */ #endif 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; } #ifdef __NetBSD__ /* * On NetBSD we require the AUTH chain to have a binding * or a required module. */ { pam_chain_t *this = pamh->chains[PAM_AUTH]; for (; this != NULL; this = this->next) if (this->flag == PAM_BINDING || this->flag == PAM_REQUIRED) break; if (this == NULL) { openpam_log(PAM_LOG_ERROR, "No required or binding component " "in service %s, facility %s", service, _pam_facility_name[PAM_AUTH]); goto load_err; } } #endif return (PAM_SUCCESS); load_err: openpam_clear_chains(pamh->chains); return (PAM_SYSTEM_ERR); } /* * NODOC * * Error codes: * PAM_SYSTEM_ERR */