diff options
author | des <des@FreeBSD.org> | 2012-05-26 14:23:18 +0000 |
---|---|---|
committer | des <des@FreeBSD.org> | 2012-05-26 14:23:18 +0000 |
commit | 4d990f74cc8d1f6788d9e35ce8ada48cc0b5f41f (patch) | |
tree | 64307889a4ba20119258d81c0ac0b3060d7a10f0 /lib | |
parent | 8944658d904917b82ba31f5fbee46b5aebac9182 (diff) | |
download | FreeBSD-src-4d990f74cc8d1f6788d9e35ce8ada48cc0b5f41f.zip FreeBSD-src-4d990f74cc8d1f6788d9e35ce8ada48cc0b5f41f.tar.gz |
Vendor import of OpenPAM Micrampelis.
Diffstat (limited to 'lib')
-rw-r--r-- | lib/Makefile.am | 11 | ||||
-rw-r--r-- | lib/Makefile.in | 29 | ||||
-rw-r--r-- | lib/openpam_check_owner_perms.c | 23 | ||||
-rw-r--r-- | lib/openpam_configure.c | 592 | ||||
-rw-r--r-- | lib/openpam_constants.h | 9 | ||||
-rw-r--r-- | lib/openpam_ctype.h | 68 | ||||
-rw-r--r-- | lib/openpam_debug.h | 45 | ||||
-rw-r--r-- | lib/openpam_dynamic.c | 81 | ||||
-rw-r--r-- | lib/openpam_features.c | 69 | ||||
-rw-r--r-- | lib/openpam_features.h | 48 | ||||
-rw-r--r-- | lib/openpam_get_feature.c | 99 | ||||
-rw-r--r-- | lib/openpam_get_option.c | 3 | ||||
-rw-r--r-- | lib/openpam_impl.h | 18 | ||||
-rw-r--r-- | lib/openpam_load.c | 6 | ||||
-rw-r--r-- | lib/openpam_log.c | 14 | ||||
-rw-r--r-- | lib/openpam_readline.c | 52 | ||||
-rw-r--r-- | lib/openpam_readlinev.c | 156 | ||||
-rw-r--r-- | lib/openpam_readword.c | 207 | ||||
-rw-r--r-- | lib/openpam_set_feature.c | 75 | ||||
-rw-r--r-- | lib/openpam_set_option.c | 3 | ||||
-rw-r--r-- | lib/openpam_straddch.c | 111 | ||||
-rw-r--r-- | lib/openpam_strlcat.h | 54 | ||||
-rw-r--r-- | lib/openpam_strlcmp.h | 5 | ||||
-rw-r--r-- | lib/openpam_strlcpy.h | 7 | ||||
-rw-r--r-- | lib/openpam_subst.c | 5 | ||||
-rw-r--r-- | lib/openpam_ttyconv.c | 12 | ||||
-rw-r--r-- | lib/pam_get_authtok.c | 12 | ||||
-rw-r--r-- | lib/pam_putenv.c | 4 | ||||
-rw-r--r-- | lib/pam_setenv.c | 4 |
29 files changed, 1357 insertions, 465 deletions
diff --git a/lib/Makefile.am b/lib/Makefile.am index 3a2e60e..9ce2d2f 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -1,4 +1,4 @@ -# $Id: Makefile.am 499 2011-11-22 11:51:50Z des $ +# $Id: Makefile.am 602 2012-04-15 17:31:15Z des $ NULL = @@ -8,8 +8,11 @@ lib_LTLIBRARIES = libpam.la noinst_HEADERS = \ openpam_constants.h \ + openpam_ctype.h \ openpam_debug.h \ + openpam_features.h \ openpam_impl.h \ + openpam_strlcat.h \ openpam_strlcmp.h \ openpam_strlcpy.h @@ -20,17 +23,23 @@ libpam_la_SOURCES = \ openpam_constants.c \ openpam_dispatch.c \ openpam_dynamic.c \ + openpam_features.c \ openpam_findenv.c \ openpam_free_data.c \ openpam_free_envlist.c \ + openpam_get_feature.c \ openpam_get_option.c \ openpam_load.c \ openpam_log.c \ openpam_nullconv.c \ openpam_readline.c \ + openpam_readlinev.c \ + openpam_readword.c \ openpam_restore_cred.c \ openpam_set_option.c \ + openpam_set_feature.c \ openpam_static.c \ + openpam_straddch.c \ openpam_subst.c \ openpam_ttyconv.c \ pam_acct_mgmt.c \ diff --git a/lib/Makefile.in b/lib/Makefile.in index 0052ce2..353fbab 100644 --- a/lib/Makefile.in +++ b/lib/Makefile.in @@ -15,7 +15,7 @@ @SET_MAKE@ -# $Id: Makefile.am 499 2011-11-22 11:51:50Z des $ +# $Id: Makefile.am 602 2012-04-15 17:31:15Z des $ VPATH = @srcdir@ @@ -76,11 +76,13 @@ am__objects_1 = am_libpam_la_OBJECTS = openpam_borrow_cred.lo \ openpam_check_owner_perms.lo openpam_configure.lo \ openpam_constants.lo openpam_dispatch.lo openpam_dynamic.lo \ - openpam_findenv.lo openpam_free_data.lo \ - openpam_free_envlist.lo openpam_get_option.lo openpam_load.lo \ - openpam_log.lo openpam_nullconv.lo openpam_readline.lo \ - openpam_restore_cred.lo openpam_set_option.lo \ - openpam_static.lo openpam_subst.lo openpam_ttyconv.lo \ + openpam_features.lo openpam_findenv.lo openpam_free_data.lo \ + openpam_free_envlist.lo openpam_get_feature.lo \ + openpam_get_option.lo openpam_load.lo openpam_log.lo \ + openpam_nullconv.lo openpam_readline.lo openpam_readlinev.lo \ + openpam_readword.lo openpam_restore_cred.lo \ + openpam_set_option.lo openpam_set_feature.lo openpam_static.lo \ + openpam_straddch.lo openpam_subst.lo openpam_ttyconv.lo \ pam_acct_mgmt.lo pam_authenticate.lo pam_chauthtok.lo \ pam_close_session.lo pam_end.lo pam_error.lo \ pam_get_authtok.lo pam_get_data.lo pam_get_item.lo \ @@ -234,8 +236,11 @@ INCLUDES = -I$(top_srcdir)/include lib_LTLIBRARIES = libpam.la noinst_HEADERS = \ openpam_constants.h \ + openpam_ctype.h \ openpam_debug.h \ + openpam_features.h \ openpam_impl.h \ + openpam_strlcat.h \ openpam_strlcmp.h \ openpam_strlcpy.h @@ -246,17 +251,23 @@ libpam_la_SOURCES = \ openpam_constants.c \ openpam_dispatch.c \ openpam_dynamic.c \ + openpam_features.c \ openpam_findenv.c \ openpam_free_data.c \ openpam_free_envlist.c \ + openpam_get_feature.c \ openpam_get_option.c \ openpam_load.c \ openpam_log.c \ openpam_nullconv.c \ openpam_readline.c \ + openpam_readlinev.c \ + openpam_readword.c \ openpam_restore_cred.c \ openpam_set_option.c \ + openpam_set_feature.c \ openpam_static.c \ + openpam_straddch.c \ openpam_subst.c \ openpam_ttyconv.c \ pam_acct_mgmt.c \ @@ -387,17 +398,23 @@ distclean-compile: @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/openpam_constants.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/openpam_dispatch.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/openpam_dynamic.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/openpam_features.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/openpam_findenv.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/openpam_free_data.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/openpam_free_envlist.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/openpam_get_feature.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/openpam_get_option.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/openpam_load.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/openpam_log.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/openpam_nullconv.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/openpam_readline.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/openpam_readlinev.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/openpam_readword.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/openpam_restore_cred.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/openpam_set_feature.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/openpam_set_option.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/openpam_static.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/openpam_straddch.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/openpam_subst.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/openpam_ttyconv.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pam_acct_mgmt.Plo@am__quote@ diff --git a/lib/openpam_check_owner_perms.c b/lib/openpam_check_owner_perms.c index 9d64ed6..d3b2ca9 100644 --- a/lib/openpam_check_owner_perms.c +++ b/lib/openpam_check_owner_perms.c @@ -11,6 +11,9 @@ * 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 @@ -24,7 +27,7 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $Id: openpam_check_owner_perms.c 499 2011-11-22 11:51:50Z des $ + * $Id: openpam_check_owner_perms.c 543 2012-03-31 22:11:34Z des $ */ #ifdef HAVE_CONFIG_H @@ -67,6 +70,12 @@ openpam_check_desc_owner_perms(const char *name, int fd) errno = serrno; return (-1); } + if (!S_ISREG(sb.st_mode)) { + openpam_log(PAM_LOG_ERROR, + "%s: not a regular file", name); + errno = EINVAL; + return (-1); + } if ((sb.st_uid != root && sb.st_uid != arbitrator) || (sb.st_mode & (S_IWGRP|S_IWOTH)) != 0) { openpam_log(PAM_LOG_ERROR, @@ -84,7 +93,7 @@ openpam_check_desc_owner_perms(const char *name, int fd) * up to it are owned by either root or the arbitrator and that they are * not writable by group or other. * - * Note that openpam_check_file_owner_perms() should be used instead if + * Note that openpam_check_desc_owner_perms() should be used instead if * possible to avoid a race between the ownership / permission check and * the actual open(). */ @@ -95,8 +104,9 @@ openpam_check_path_owner_perms(const char *path) uid_t root, arbitrator; char pathbuf[PATH_MAX]; struct stat sb; - int len, serrno; + int len, serrno, tip; + tip = 1; root = 0; arbitrator = geteuid(); if (realpath(path, pathbuf) == NULL) @@ -111,6 +121,12 @@ openpam_check_path_owner_perms(const char *path) } return (-1); } + if (tip && !S_ISREG(sb.st_mode)) { + openpam_log(PAM_LOG_ERROR, + "%s: not a regular file", pathbuf); + errno = EINVAL; + return (-1); + } if ((sb.st_uid != root && sb.st_uid != arbitrator) || (sb.st_mode & (S_IWGRP|S_IWOTH)) != 0) { openpam_log(PAM_LOG_ERROR, @@ -120,6 +136,7 @@ openpam_check_path_owner_perms(const char *path) } while (--len > 0 && pathbuf[len] != '/') pathbuf[len] = '\0'; + tip = 0; } return (0); } diff --git a/lib/openpam_configure.c b/lib/openpam_configure.c index d395565..778bec8 100644 --- a/lib/openpam_configure.c +++ b/lib/openpam_configure.c @@ -1,6 +1,6 @@ /*- * Copyright (c) 2001-2003 Networks Associates Technology, Inc. - * Copyright (c) 2004-2011 Dag-Erling Smørgrav + * Copyright (c) 2004-2012 Dag-Erling Smørgrav * All rights reserved. * * This software was developed for the FreeBSD Project by ThinkSec AS and @@ -32,13 +32,15 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $Id: openpam_configure.c 500 2011-11-22 12:07:03Z des $ + * $Id: openpam_configure.c 601 2012-04-14 20:37:45Z des $ */ #ifdef HAVE_CONFIG_H # include "config.h" #endif +#include <sys/param.h> + #include <ctype.h> #include <errno.h> #include <stdio.h> @@ -48,389 +50,183 @@ #include <security/pam_appl.h> #include "openpam_impl.h" -#include "openpam_strlcmp.h" +#include "openpam_ctype.h" +#include "openpam_strlcat.h" +#include "openpam_strlcpy.h" static int openpam_load_chain(pam_handle_t *, const char *, pam_facility_t); /* - * Evaluates to non-zero if the argument is a linear whitespace character. - */ -#define is_lws(ch) \ - (ch == ' ' || ch == '\t') - -/* - * Evaluates to non-zero if the argument is a printable ASCII character. - * Assumes that the execution character set is a superset of ASCII. - */ -#define is_p(ch) \ - (ch >= '!' && ch <= '~') - -/* - * Returns non-zero if the argument belongs to the POSIX Portable Filename - * Character Set. Assumes that the execution character set is a superset - * of ASCII. - */ -#define is_pfcs(ch) \ - ((ch >= '0' && ch <= '9') || \ - (ch >= 'A' && ch <= 'Z') || \ - (ch >= 'a' && ch <= 'z') || \ - ch == '.' || ch == '_' || ch == '-') - -/* - * Parse the service name. - * - * Returns the length of the service name, or 0 if the end of the string - * was reached or a disallowed non-whitespace character was encountered. + * Validate a service name. * - * If parse_service_name() is successful, it updates *service to point to - * the first character of the service name and *line to point one - * character past the end. If it reaches the end of the string, it - * updates *line to point to the terminating NUL character and leaves - * *service unmodified. In all other cases, it leaves both *line and - * *service unmodified. - * - * Allowed characters are all characters in the POSIX portable filename - * character set. + * Returns a non-zero value if the argument points to a NUL-terminated + * string consisting entirely of characters in the POSIX portable filename + * character set, excluding the path separator character. */ static int -parse_service_name(char **line, char **service) +valid_service_name(const char *name) { - char *b, *e; + const char *p; - for (b = *line; *b && is_lws(*b); ++b) - /* nothing */ ; - if (!*b) { - *line = b; - return (0); + if (OPENPAM_FEATURE(RESTRICT_SERVICE_NAME)) { + /* path separator not allowed */ + for (p = name; *p != '\0'; ++p) + if (!is_pfcs(*p)) + return (0); + } else { + /* path separator allowed */ + for (p = name; *p != '\0'; ++p) + if (!is_pfcs(*p) && *p != '/') + return (0); } - for (e = b; *e && !is_lws(*e); ++e) - if (!is_pfcs(*e)) - return (0); - if (e == b) - return (0); - *line = e; - *service = b; - return (e - b); + return (1); } /* * Parse the facility name. * - * Returns the corresponding pam_facility_t value, or -1 if the end of the - * string was reached, a disallowed non-whitespace character was - * encountered, or the first word was not a recognized facility name. - * - * If parse_facility_name() is successful, it updates *line to point one - * character past the end of the facility name. If it reaches the end of - * the string, it updates *line to point to the terminating NUL character. - * In all other cases, it leaves *line unmodified. + * Returns the corresponding pam_facility_t value, or -1 if the argument + * is not a valid facility name. */ static pam_facility_t -parse_facility_name(char **line) +parse_facility_name(const char *name) { - char *b, *e; int i; - for (b = *line; *b && is_lws(*b); ++b) - /* nothing */ ; - if (!*b) { - *line = b; - return ((pam_facility_t)-1); - } - for (e = b; *e && !is_lws(*e); ++e) - /* nothing */ ; - if (e == b) - return ((pam_facility_t)-1); for (i = 0; i < PAM_NUM_FACILITIES; ++i) - if (strlcmp(pam_facility_name[i], b, e - b) == 0) - break; - if (i == PAM_NUM_FACILITIES) - return ((pam_facility_t)-1); - *line = e; - return (i); -} - -/* - * Parse the word "include". - * - * If the next word on the line is "include", parse_include() updates - * *line to point one character past "include" and returns 1. Otherwise, - * it leaves *line unmodified and returns 0. - */ -static int -parse_include(char **line) -{ - char *b, *e; - - for (b = *line; *b && is_lws(*b); ++b) - /* nothing */ ; - if (!*b) { - *line = b; - return (-1); - } - for (e = b; *e && !is_lws(*e); ++e) - /* nothing */ ; - if (e == b) - return (0); - if (strlcmp("include", b, e - b) != 0) - return (0); - *line = e; - return (1); + if (strcmp(pam_facility_name[i], name) == 0) + return (i); + return ((pam_facility_t)-1); } /* * Parse the control flag. * - * Returns the corresponding pam_control_t value, or -1 if the end of the - * string was reached, a disallowed non-whitespace character was - * encountered, or the first word was not a recognized control flag. - * - * If parse_control_flag() is successful, it updates *line to point one - * character past the end of the control flag. If it reaches the end of - * the string, it updates *line to point to the terminating NUL character. - * In all other cases, it leaves *line unmodified. + * Returns the corresponding pam_control_t value, or -1 if the argument is + * not a valid control flag name. */ static pam_control_t -parse_control_flag(char **line) +parse_control_flag(const char *name) { - char *b, *e; int i; - for (b = *line; *b && is_lws(*b); ++b) - /* nothing */ ; - if (!*b) { - *line = b; - return ((pam_control_t)-1); - } - for (e = b; *e && !is_lws(*e); ++e) - /* nothing */ ; - if (e == b) - return ((pam_control_t)-1); for (i = 0; i < PAM_NUM_CONTROL_FLAGS; ++i) - if (strlcmp(pam_control_flag_name[i], b, e - b) == 0) - break; - if (i == PAM_NUM_CONTROL_FLAGS) - return ((pam_control_t)-1); - *line = e; - return (i); + if (strcmp(pam_control_flag_name[i], name) == 0) + return (i); + return ((pam_control_t)-1); } /* - * Parse a file name. - * - * Returns the length of the file name, or 0 if the end of the string was - * reached or a disallowed non-whitespace character was encountered. + * Validate a file name. * - * If parse_filename() is successful, it updates *filename to point to the - * first character of the filename and *line to point one character past - * the end. If it reaches the end of the string, it updates *line to - * point to the terminating NUL character and leaves *filename unmodified. - * In all other cases, it leaves both *line and *filename unmodified. - * - * Allowed characters are all characters in the POSIX portable filename - * character set, plus the path separator (forward slash). + * Returns a non-zero value if the argument points to a NUL-terminated + * string consisting entirely of characters in the POSIX portable filename + * character set, including the path separator character. */ static int -parse_filename(char **line, char **filename) +valid_module_name(const char *name) { - char *b, *e; - - for (b = *line; *b && is_lws(*b); ++b) - /* nothing */ ; - if (!*b) { - *line = b; - return (0); - } - for (e = b; *e && !is_lws(*e); ++e) - if (!is_pfcs(*e) && *e != '/') - return (0); - if (e == b) - return (0); - *line = e; - *filename = b; - return (e - b); -} + const char *p; -/* - * Parse an option. - * - * Returns a dynamically allocated string containing the next module - * option, or NULL if the end of the string was reached or a disallowed - * non-whitespace character was encountered. - * - * If parse_option() is successful, it updates *line to point one - * character past the end of the option. If it reaches the end of the - * string, it updates *line to point to the terminating NUL character. In - * all other cases, it leaves *line unmodified. - * - * If parse_option() fails to allocate memory, it will return NULL and set - * errno to a non-zero value. - * - * Allowed characters for option names are all characters in the POSIX - * portable filename character set. Allowed characters for option values - * are any printable non-whitespace characters. The option value may be - * quoted in either single or double quotes, in which case space - * characters and whichever quote character was not used are allowed. - * Note that the entire value must be quoted, not just part of it. - */ -static char * -parse_option(char **line) -{ - char *nb, *ne, *vb, *ve; - unsigned char q = 0; - char *option; - size_t size; - - errno = 0; - for (nb = *line; *nb && is_lws(*nb); ++nb) - /* nothing */ ; - if (!*nb) { - *line = nb; - return (NULL); - } - for (ne = nb; *ne && !is_lws(*ne) && *ne != '='; ++ne) - if (!is_pfcs(*ne)) - return (NULL); - if (ne == nb) - return (NULL); - if (*ne == '=') { - vb = ne + 1; - if (*vb == '"' || *vb == '\'') - q = *vb++; - for (ve = vb; - *ve && *ve != q && (is_p(*ve) || (q && is_lws(*ve))); - ++ve) - /* nothing */ ; - if (q && *ve != q) - /* non-printable character or missing endquote */ - return (NULL); - if (q && *(ve + 1) && !is_lws(*(ve + 1))) - /* garbage after value */ - return (NULL); + if (OPENPAM_FEATURE(RESTRICT_MODULE_NAME)) { + /* path separator not allowed */ + for (p = name; *p != '\0'; ++p) + if (!is_pfcs(*p)) + return (0); } else { - vb = ve = ne; + /* path separator allowed */ + for (p = name; *p != '\0'; ++p) + if (!is_pfcs(*p) && *p != '/') + return (0); } - size = (ne - nb) + 1; - if (ve > vb) - size += (ve - vb) + 1; - if ((option = malloc(size)) == NULL) - return (NULL); - strncpy(option, nb, ne - nb); - if (ve > vb) { - option[ne - nb] = '='; - strncpy(option + (ne - nb) + 1, vb, ve - vb); - } - option[size - 1] = '\0'; - *line = q ? ve + 1 : ve; - return (option); -} - -/* - * Consume trailing whitespace. - * - * If there are no non-whitespace characters left on the line, parse_eol() - * updates *line to point at the terminating NUL character and returns 0. - * Otherwise, it leaves *line unmodified and returns a non-zero value. - */ -static int -parse_eol(char **line) -{ - char *p; - - for (p = *line; *p && is_lws(*p); ++p) - /* nothing */ ; - if (*p) - return ((unsigned char)*p); - *line = p; - return (0); + return (1); } typedef enum { pam_conf_style, pam_d_style } openpam_style_t; /* * Extracts given chains from a policy file. + * + * Returns the number of policy entries which were found for the specified + * service and facility, or -1 if a system error occurred or a syntax + * error was encountered. */ static int openpam_parse_chain(pam_handle_t *pamh, const char *service, pam_facility_t facility, + FILE *f, const char *filename, openpam_style_t style) { pam_chain_t *this, **next; pam_facility_t fclt; pam_control_t ctlf; - char *line, *str, *name; - char *option, **optv; - int len, lineno, ret; - FILE *f; + char *name, *servicename, *modulename; + int count, lineno, ret, serrno; + char **wordv, *word; + int i, wordc; - if ((f = fopen(filename, "r")) == NULL) { - openpam_log(errno == ENOENT ? PAM_LOG_DEBUG : PAM_LOG_NOTICE, - "%s: %m", filename); - return (PAM_SUCCESS); - } - if (openpam_check_desc_owner_perms(filename, fileno(f)) != 0) { - fclose(f); - return (PAM_SYSTEM_ERR); - } + count = 0; this = NULL; name = NULL; lineno = 0; - while ((line = openpam_readline(f, &lineno, NULL)) != NULL) { - /* get service name if necessary */ - if (style == pam_conf_style) { - if ((len = parse_service_name(&line, &str)) == 0) { - openpam_log(PAM_LOG_NOTICE, - "%s(%d): invalid service name (ignored)", - filename, lineno); - FREE(line); - continue; - } - if (strlcmp(service, str, len) != 0) { - FREE(line); - continue; - } + wordc = 0; + wordv = NULL; + while ((wordv = openpam_readlinev(f, &lineno, &wordc)) != NULL) { + /* blank line? */ + if (wordc == 0) { + FREEV(wordc, wordv); + continue; + } + i = 0; + + /* check service name if necessary */ + if (style == pam_conf_style && + strcmp(wordv[i++], service) != 0) { + FREEV(wordc, wordv); + continue; } - /* get facility name */ - if ((fclt = parse_facility_name(&line)) == (pam_facility_t)-1) { + /* check facility name */ + if ((word = wordv[i++]) == NULL || + (fclt = parse_facility_name(word)) == (pam_facility_t)-1) { openpam_log(PAM_LOG_ERROR, "%s(%d): missing or invalid facility", filename, lineno); goto fail; } if (facility != fclt && facility != PAM_FACILITY_ANY) { - FREE(line); + FREEV(wordc, wordv); continue; } /* check for "include" */ - if (parse_include(&line)) { - if ((len = parse_service_name(&line, &str)) == 0) { + if ((word = wordv[i++]) != NULL && + strcmp(word, "include") == 0) { + if ((servicename = wordv[i++]) == NULL || + !valid_service_name(servicename)) { openpam_log(PAM_LOG_ERROR, - "%s(%d): missing or invalid filename", + "%s(%d): missing or invalid service name", filename, lineno); goto fail; } - if ((name = strndup(str, len)) == NULL) - goto syserr; - if (parse_eol(&line) != 0) { + if (wordv[i] != NULL) { openpam_log(PAM_LOG_ERROR, "%s(%d): garbage at end of line", filename, lineno); goto fail; } - ret = openpam_load_chain(pamh, name, fclt); - FREE(name); - if (ret != PAM_SUCCESS) + ret = openpam_load_chain(pamh, servicename, fclt); + FREEV(wordc, wordv); + if (ret < 0) goto fail; - FREE(line); continue; } /* get control flag */ - if ((ctlf = parse_control_flag(&line)) == (pam_control_t)-1) { + if (word == NULL || /* same word we compared to "include" */ + (ctlf = parse_control_flag(word)) == (pam_control_t)-1) { openpam_log(PAM_LOG_ERROR, "%s(%d): missing or invalid control flag", filename, lineno); @@ -438,73 +234,76 @@ openpam_parse_chain(pam_handle_t *pamh, } /* get module name */ - if ((len = parse_filename(&line, &str)) == 0) { + if ((modulename = wordv[i++]) == NULL || + !valid_module_name(modulename)) { openpam_log(PAM_LOG_ERROR, "%s(%d): missing or invalid module name", filename, lineno); goto fail; } - if ((name = strndup(str, len)) == NULL) - goto syserr; /* allocate new entry */ if ((this = calloc(1, sizeof *this)) == NULL) goto syserr; this->flag = ctlf; - /* get module options */ - if ((this->optv = malloc(sizeof *optv)) == NULL) - goto syserr; - this->optc = 0; - while ((option = parse_option(&line)) != NULL) { - optv = realloc(this->optv, - (this->optc + 2) * sizeof *optv); - if (optv == NULL) - goto syserr; - this->optv = optv; - this->optv[this->optc++] = option; - } - this->optv[this->optc] = NULL; - if (*line != '\0') { - openpam_log(PAM_LOG_ERROR, - "%s(%d): syntax error in module options", - filename, lineno); - goto fail; - } - /* load module */ - this->module = openpam_load_module(name); - FREE(name); - if (this->module == NULL) + if ((this->module = openpam_load_module(modulename)) == NULL) goto fail; + /* + * The remaining items in wordv are the module's + * arguments. We could set this->optv = wordv + i, but + * then free(this->optv) wouldn't work. Instead, we free + * the words we've already consumed, shift the rest up, + * and clear the tail end of the array. + */ + this->optc = wordc - i; + for (i = 0; i < wordc - this->optc; ++i) { + FREE(wordv[i]); + wordv[i] = wordv[wordc - this->optc + i]; + wordv[wordc - this->optc + i] = NULL; + } + this->optv = wordv; + wordv = NULL; + wordc = 0; + /* hook it up */ for (next = &pamh->chains[fclt]; *next != NULL; next = &(*next)->next) /* nothing */ ; *next = this; this = NULL; - - /* next please... */ - FREE(line); + ++count; } - if (!feof(f)) + /* + * The loop ended because openpam_readword() returned NULL, which + * can happen for four different reasons: an I/O error (ferror(f) + * is true), a memory allocation failure (ferror(f) is false, + * errno is non-zero) + */ + if (ferror(f) || errno != 0) goto syserr; + if (!feof(f)) + goto fail; fclose(f); - return (PAM_SUCCESS); + return (count); syserr: + serrno = errno; openpam_log(PAM_LOG_ERROR, "%s: %m", filename); + errno = serrno; + /* fall through */ fail: - if (this && this->optc) { - while (this->optc--) - FREE(this->optv[this->optc]); - FREE(this->optv); - } + serrno = errno; + if (this && this->optc && this->optv) + FREEV(this->optc, this->optv); FREE(this); - FREE(line); + FREEV(wordc, wordv); + FREE(wordv); FREE(name); fclose(f); - return (PAM_SYSTEM_ERR); + errno = serrno; + return (-1); } static const char *openpam_policy_path[] = { @@ -516,37 +315,110 @@ static const char *openpam_policy_path[] = { }; /* + * Read the specified chains from the specified file. + * + * Returns 0 if the file exists but does not contain any matching lines. + * + * Returns -1 and sets errno to ENOENT if the file does not exist. + * + * Returns -1 and sets errno to some other non-zero value if the file + * exists but is unsafe or unreadable, or an I/O error occurs. + */ +static int +openpam_load_file(pam_handle_t *pamh, + const char *service, + pam_facility_t facility, + const char *filename, + openpam_style_t style) +{ + FILE *f; + int ret, serrno; + + /* attempt to open the file */ + if ((f = fopen(filename, "r")) == NULL) { + serrno = errno; + openpam_log(errno == ENOENT ? PAM_LOG_DEBUG : PAM_LOG_ERROR, + "%s: %m", filename); + errno = serrno; + RETURNN(-1); + } else { + openpam_log(PAM_LOG_DEBUG, "found %s", filename); + } + + /* verify type, ownership and permissions */ + if (OPENPAM_FEATURE(VERIFY_POLICY_FILE) && + openpam_check_desc_owner_perms(filename, fileno(f)) != 0) { + /* already logged the cause */ + serrno = errno; + fclose(f); + errno = serrno; + RETURNN(-1); + } + + /* parse the file */ + ret = openpam_parse_chain(pamh, service, facility, + f, filename, style); + RETURNN(ret); +} + +/* * Locates the policy file for a given service and reads the given chains * from it. + * + * Returns the number of policy entries which were found for the specified + * service and facility, or -1 if a system error occurred or a syntax + * error was encountered. */ static int openpam_load_chain(pam_handle_t *pamh, const char *service, pam_facility_t facility) { - const char **path; - char *filename; + const char *p, **path; + char filename[PATH_MAX]; size_t len; + openpam_style_t style; int ret; + ENTERS(facility < 0 ? "any" : pam_facility_name[facility]); + + /* either absolute or relative to cwd */ + if (strchr(service, '/') != NULL) { + if ((p = strrchr(service, '.')) != NULL && strcmp(p, ".conf") == 0) + style = pam_conf_style; + else + style = pam_d_style; + ret = openpam_load_file(pamh, service, facility, + service, style); + RETURNN(ret); + } + + /* search standard locations */ 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); + /* construct filename */ + len = strlcpy(filename, *path, sizeof filename); + if (filename[len - 1] == '/') { + len = strlcat(filename, service, sizeof filename); + if (len >= sizeof filename) { + errno = ENAMETOOLONG; + RETURNN(-1); } - ret = openpam_parse_chain(pamh, service, facility, - filename, pam_d_style); - FREE(filename); + style = pam_d_style; } else { - ret = openpam_parse_chain(pamh, service, facility, - *path, pam_conf_style); + style = pam_conf_style; } - if (ret != PAM_SUCCESS) - return (ret); + ret = openpam_load_file(pamh, service, facility, + filename, style); + /* the file exists, but an error occurred */ + if (ret == -1 && errno != ENOENT) + RETURNN(ret); + /* in pam.d style, an empty file counts as a hit */ + if (ret == 0 && style == pam_d_style) + RETURNN(ret); } - return (PAM_SUCCESS); + + /* no hit */ + RETURNN(0); } /* @@ -560,25 +432,27 @@ openpam_configure(pam_handle_t *pamh, const char *service) { pam_facility_t fclt; - const char *p; + int serrno; - for (p = service; *p; ++p) - if (!is_pfcs(*p)) - return (PAM_SYSTEM_ERR); - - if (openpam_load_chain(pamh, service, PAM_FACILITY_ANY) != PAM_SUCCESS) + ENTERS(service); + if (!valid_service_name(service)) { + openpam_log(PAM_LOG_ERROR, "invalid service name"); + RETURNC(PAM_SYSTEM_ERR); + } + 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) != PAM_SUCCESS) + if (openpam_load_chain(pamh, PAM_OTHER, fclt) < 0) goto load_err; } - return (PAM_SUCCESS); + RETURNC(PAM_SUCCESS); load_err: + serrno = errno; openpam_clear_chains(pamh->chains); - return (PAM_SYSTEM_ERR); + errno = serrno; + RETURNC(PAM_SYSTEM_ERR); } /* diff --git a/lib/openpam_constants.h b/lib/openpam_constants.h index b923179..a7d6ce8 100644 --- a/lib/openpam_constants.h +++ b/lib/openpam_constants.h @@ -11,6 +11,9 @@ * 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 @@ -24,11 +27,11 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $Id: openpam_constants.h 491 2011-11-12 00:12:32Z des $ + * $Id: openpam_constants.h 606 2012-04-20 11:06:38Z des $ */ -#ifndef OPENPAM_CONSTANTS_INCLUDED -#define OPENPAM_CONSTANTS_INCLUDED +#ifndef OPENPAM_CONSTANTS_H_INCLUDED +#define OPENPAM_CONSTANTS_H_INCLUDED extern const char *pam_err_name[PAM_NUM_ERRORS]; extern const char *pam_item_name[PAM_NUM_ITEMS]; diff --git a/lib/openpam_ctype.h b/lib/openpam_ctype.h new file mode 100644 index 0000000..b3ec846 --- /dev/null +++ b/lib/openpam_ctype.h @@ -0,0 +1,68 @@ +/*- + * Copyright (c) 2012 Dag-Erling Smørgrav + * All rights reserved. + * + * 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 + * in this position and unchanged. + * 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_ctype.h 578 2012-04-06 00:45:59Z des $ + */ + +#ifndef OPENPAM_CTYPE_H_INCLUDED +#define OPENPAM_CTYPE_H_INCLUDED + +/* + * Evaluates to non-zero if the argument is a linear whitespace character. + * For the purposes of this macro, the definition of linear whitespace is + * extended to include the form feed and carraige return characters. + */ +#define is_lws(ch) \ + (ch == ' ' || ch == '\t' || ch == '\f' || ch == '\r') + +/* + * Evaluates to non-zero if the argument is a whitespace character. + */ +#define is_ws(ch) \ + (is_lws(ch) || ch == '\n') + +/* + * Evaluates to non-zero if the argument is a printable ASCII character. + * Assumes that the execution character set is a superset of ASCII. + */ +#define is_p(ch) \ + (ch >= '!' && ch <= '~') + +/* + * Returns non-zero if the argument belongs to the POSIX Portable Filename + * Character Set. Assumes that the execution character set is a superset + * of ASCII. + */ +#define is_pfcs(ch) \ + ((ch >= '0' && ch <= '9') || \ + (ch >= 'A' && ch <= 'Z') || \ + (ch >= 'a' && ch <= 'z') || \ + ch == '.' || ch == '_' || ch == '-') + +#endif diff --git a/lib/openpam_debug.h b/lib/openpam_debug.h index ef2884d..050783e 100644 --- a/lib/openpam_debug.h +++ b/lib/openpam_debug.h @@ -32,60 +32,68 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $Id: openpam_debug.h 491 2011-11-12 00:12:32Z des $ + * $Id: openpam_debug.h 606 2012-04-20 11:06:38Z des $ */ -#ifndef OPENPAM_DEBUG_INCLUDED -#define OPENPAM_DEBUG_INCLUDED +#ifndef OPENPAM_DEBUG_H_INCLUDED +#define OPENPAM_DEBUG_H_INCLUDED #ifdef OPENPAM_DEBUG -#define ENTER() openpam_log(PAM_LOG_DEBUG, "entering") +#define ENTER() openpam_log(PAM_LOG_LIBDEBUG, "entering") #define ENTERI(i) do { \ int i_ = (i); \ if (i_ > 0 && i_ < PAM_NUM_ITEMS) \ - openpam_log(PAM_LOG_DEBUG, "entering: %s", pam_item_name[i_]); \ + openpam_log(PAM_LOG_LIBDEBUG, "entering: %s", pam_item_name[i_]); \ else \ - openpam_log(PAM_LOG_DEBUG, "entering: %d", i_); \ + openpam_log(PAM_LOG_LIBDEBUG, "entering: %d", i_); \ } while (0) #define ENTERN(n) do { \ int n_ = (n); \ - openpam_log(PAM_LOG_DEBUG, "entering: %d", n_); \ + openpam_log(PAM_LOG_LIBDEBUG, "entering: %d", n_); \ } while (0) #define ENTERS(s) do { \ const char *s_ = (s); \ if (s_ == NULL) \ - openpam_log(PAM_LOG_DEBUG, "entering: NULL"); \ + openpam_log(PAM_LOG_LIBDEBUG, "entering: NULL"); \ else \ - openpam_log(PAM_LOG_DEBUG, "entering: '%s'", s_); \ + openpam_log(PAM_LOG_LIBDEBUG, "entering: '%s'", s_); \ } while (0) -#define RETURNV() openpam_log(PAM_LOG_DEBUG, "returning") +#define ENTERF(f) do { \ + int f_ = (f); \ + if (f_ >= 0 && f_ <= OPENPAM_NUM_FEATURES) \ + openpam_log(PAM_LOG_LIBDEBUG, "entering: %s", \ + openpam_features[f_].name); \ + else \ + openpam_log(PAM_LOG_LIBDEBUG, "entering: %d", f_); \ +} while (0) +#define RETURNV() openpam_log(PAM_LOG_LIBDEBUG, "returning") #define RETURNC(c) do { \ int c_ = (c); \ if (c_ >= 0 && c_ < PAM_NUM_ERRORS) \ - openpam_log(PAM_LOG_DEBUG, "returning %s", pam_err_name[c_]); \ + openpam_log(PAM_LOG_LIBDEBUG, "returning %s", pam_err_name[c_]); \ else \ - openpam_log(PAM_LOG_DEBUG, "returning %d!", c_); \ + openpam_log(PAM_LOG_LIBDEBUG, "returning %d!", c_); \ return (c_); \ } while (0) #define RETURNN(n) do { \ int n_ = (n); \ - openpam_log(PAM_LOG_DEBUG, "returning %d", n_); \ + openpam_log(PAM_LOG_LIBDEBUG, "returning %d", n_); \ return (n_); \ } while (0) #define RETURNP(p) do { \ - const void *p_ = (p); \ + void *p_ = (p); \ if (p_ == NULL) \ - openpam_log(PAM_LOG_DEBUG, "returning NULL"); \ + openpam_log(PAM_LOG_LIBDEBUG, "returning NULL"); \ else \ - openpam_log(PAM_LOG_DEBUG, "returning %p", p_); \ + openpam_log(PAM_LOG_LIBDEBUG, "returning %p", p_); \ return (p_); \ } while (0) #define RETURNS(s) do { \ const char *s_ = (s); \ if (s_ == NULL) \ - openpam_log(PAM_LOG_DEBUG, "returning NULL"); \ + openpam_log(PAM_LOG_LIBDEBUG, "returning NULL"); \ else \ - openpam_log(PAM_LOG_DEBUG, "returning '%s'", s_); \ + openpam_log(PAM_LOG_LIBDEBUG, "returning '%s'", s_); \ return (s_); \ } while (0) #else @@ -93,6 +101,7 @@ #define ENTERI(i) #define ENTERN(n) #define ENTERS(s) +#define ENTERF(f) #define RETURNV() return #define RETURNC(c) return (c) #define RETURNN(n) return (n) diff --git a/lib/openpam_dynamic.c b/lib/openpam_dynamic.c index d44174f..1dfc1ac 100644 --- a/lib/openpam_dynamic.c +++ b/lib/openpam_dynamic.c @@ -32,7 +32,7 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $Id: openpam_dynamic.c 502 2011-12-18 13:59:22Z des $ + * $Id: openpam_dynamic.c 607 2012-04-20 11:09:37Z des $ */ #ifdef HAVE_CONFIG_H @@ -40,6 +40,7 @@ #endif #include <dlfcn.h> +#include <fcntl.h> #include <errno.h> #include <stdio.h> #include <stdlib.h> @@ -60,15 +61,50 @@ * Perform sanity checks and attempt to load a module */ +#ifdef HAVE_FDLOPEN static void * try_dlopen(const char *modfn) { + void *dlh; + int fd; - if (openpam_check_path_owner_perms(modfn) != 0) + if ((fd = open(modfn, O_RDONLY)) < 0) + return (NULL); + if (OPENPAM_FEATURE(VERIFY_MODULE_FILE) && + openpam_check_desc_owner_perms(modfn, fd) != 0) { + close(fd); + return (NULL); + } + if ((dlh = fdlopen(fd, RTLD_NOW)) == NULL) { + openpam_log(PAM_LOG_ERROR, "%s: %s", modfn, dlerror()); + close(fd); + errno = 0; + return (NULL); + } + close(fd); + return (dlh); +} +#else +static void * +try_dlopen(const char *modfn) +{ + int check_module_file; + void *dlh; + + openpam_get_feature(OPENPAM_VERIFY_MODULE_FILE, + &check_module_file); + if (check_module_file && + openpam_check_path_owner_perms(modfn) != 0) + return (NULL); + if ((dlh = dlopen(modfn, RTLD_NOW)) == NULL) { + openpam_log(PAM_LOG_ERROR, "%s: %s", modfn, dlerror()); + errno = 0; return (NULL); - return (dlopen(modfn, RTLD_NOW)); + } + return (dlh); } - +#endif + /* * OpenPAM internal * @@ -100,9 +136,6 @@ openpam_dynamic(const char *path) *strrchr(vpath, '.') = '\0'; dlh = try_dlopen(vpath); } - serrno = errno; - FREE(vpath); - errno = serrno; if (dlh == NULL) goto err; if ((module = calloc(1, sizeof *module)) == NULL) @@ -112,19 +145,41 @@ openpam_dynamic(const char *path) module->dlh = dlh; dlmodule = dlsym(dlh, "_pam_module"); for (i = 0; i < PAM_NUM_PRIMITIVES; ++i) { - module->func[i] = dlmodule ? dlmodule->func[i] : - (pam_func_t)dlsym(dlh, pam_sm_func_name[i]); - if (module->func[i] == NULL) - openpam_log(PAM_LOG_DEBUG, "%s: %s(): %s", - path, pam_sm_func_name[i], dlerror()); + if (dlmodule) { + module->func[i] = dlmodule->func[i]; + } else { + module->func[i] = + (pam_func_t)dlsym(dlh, pam_sm_func_name[i]); + /* + * This openpam_log() call is a major source of + * log spam, and the cases that matter are caught + * and logged in openpam_dispatch(). This would + * be less problematic if dlerror() returned an + * error code so we could log an error only when + * dlsym() failed for a reason other than "no such + * symbol". + */ +#if 0 + if (module->func[i] == NULL) + openpam_log(PAM_LOG_DEBUG, "%s: %s(): %s", + path, pam_sm_func_name[i], dlerror()); +#endif + } } + FREE(vpath); return (module); buf_err: + serrno = errno; if (dlh != NULL) dlclose(dlh); FREE(module); + errno = serrno; err: - openpam_log(PAM_LOG_ERROR, "%m"); + serrno = errno; + if (errno != 0) + openpam_log(PAM_LOG_ERROR, "%s: %m", vpath); + FREE(vpath); + errno = serrno; return (NULL); } diff --git a/lib/openpam_features.c b/lib/openpam_features.c new file mode 100644 index 0000000..586fc2a --- /dev/null +++ b/lib/openpam_features.c @@ -0,0 +1,69 @@ +/*- + * Copyright (c) 2012 Dag-Erling Smørgrav + * All rights reserved. + * + * 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 + * in this position and unchanged. + * 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_features.c 608 2012-05-17 16:00:13Z des $ + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include <security/pam_appl.h> + +#include "openpam_impl.h" + +#define STRUCT_OPENPAM_FEATURE(name, descr, dflt) \ + [OPENPAM_##name] = { \ + "OPENPAM_" #name, \ + descr, \ + dflt \ + } + +struct openpam_feature openpam_features[OPENPAM_NUM_FEATURES] = { + STRUCT_OPENPAM_FEATURE( + RESTRICT_SERVICE_NAME, + "Disallow path separators in service names", + 1 + ), + STRUCT_OPENPAM_FEATURE( + VERIFY_POLICY_FILE, + "Verify ownership and permissions of policy files", + 1 + ), + STRUCT_OPENPAM_FEATURE( + RESTRICT_MODULE_NAME, + "Disallow path separators in module names", + 0 + ), + STRUCT_OPENPAM_FEATURE( + VERIFY_MODULE_FILE, + "Verify ownership and permissions of module files", + 1 + ), +}; diff --git a/lib/openpam_features.h b/lib/openpam_features.h new file mode 100644 index 0000000..227b1a9 --- /dev/null +++ b/lib/openpam_features.h @@ -0,0 +1,48 @@ +/*- + * Copyright (c) 2012 Dag-Erling Smørgrav + * All rights reserved. + * + * 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 + * in this position and unchanged. + * 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$ + */ + +#ifndef OPENPAM_FEATURES_H_INCLUDED +#define OPENPAM_FEATURES_H_INCLUDED + +struct openpam_feature { + const char *name; + const char *desc; + int onoff; +}; + +extern struct openpam_feature openpam_features[OPENPAM_NUM_FEATURES]; + +/* shortcut for internal use */ +#define OPENPAM_FEATURE(f) \ + openpam_features[OPENPAM_##f].onoff + +#endif diff --git a/lib/openpam_get_feature.c b/lib/openpam_get_feature.c new file mode 100644 index 0000000..b552357 --- /dev/null +++ b/lib/openpam_get_feature.c @@ -0,0 +1,99 @@ +/*- + * Copyright (c) 2012 Dag-Erling Smørgrav + * All rights reserved. + * + * 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 + * in this position and unchanged. + * 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_get_feature.c 608 2012-05-17 16:00:13Z des $ + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include <security/pam_appl.h> +#include <security/openpam.h> + +#include "openpam_impl.h" + +/* + * OpenPAM extension + * + * Query the state of an optional feature. + */ + +int +openpam_get_feature(int feature, int *onoff) +{ + + ENTERF(feature); + if (feature < 0 || feature >= OPENPAM_NUM_FEATURES) + RETURNC(PAM_SYMBOL_ERR); + *onoff = openpam_features[feature].onoff; + RETURNC(PAM_SUCCESS); +} + +/* + * Error codes: + * + * PAM_SYMBOL_ERR + */ + +/** + * EXPERIMENTAL + * + * The =openpam_get_feature function stores the current state of the + * specified feature in the variable pointed to by its =onoff argument. + * + * The following features are recognized: + * + * =OPENPAM_RESTRICT_SERVICE_NAME: + * Disallow path separators in service names. + * This feature is enabled by default. + * Disabling it allows the application to specify the path to + * the desired policy file directly. + * + * =OPENPAM_VERIFY_POLICY_FILE: + * Verify the ownership and permissions of the policy file + * and the path leading up to it. + * This feature is enabled by default. + * + * =OPENPAM_RESTRICT_MODULE_NAME: + * Disallow path separators in module names. + * This feature is disabled by default. + * Enabling it prevents the use of modules in non-standard + * locations. + * + * =OPENPAM_VERIFY_MODULE_FILE: + * Verify the ownership and permissions of each loadable + * module and the path leading up to it. + * This feature is enabled by default. + * + * + * >openpam_set_feature + * + * AUTHOR DES + */ diff --git a/lib/openpam_get_option.c b/lib/openpam_get_option.c index b5faa87..1f62d21 100644 --- a/lib/openpam_get_option.c +++ b/lib/openpam_get_option.c @@ -32,7 +32,7 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $Id: openpam_get_option.c 482 2011-11-03 16:33:02Z des $ + * $Id: openpam_get_option.c 531 2012-03-31 14:24:37Z des $ */ #ifdef HAVE_CONFIG_H @@ -44,7 +44,6 @@ #include <string.h> #include <security/pam_appl.h> -#include <security/openpam.h> #include "openpam_impl.h" diff --git a/lib/openpam_impl.h b/lib/openpam_impl.h index ba4d455..9e8b45f 100644 --- a/lib/openpam_impl.h +++ b/lib/openpam_impl.h @@ -32,7 +32,7 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $Id: openpam_impl.h 499 2011-11-22 11:51:50Z des $ + * $Id: openpam_impl.h 594 2012-04-14 14:18:41Z des $ */ #ifndef OPENPAM_IMPL_H_INCLUDED @@ -157,9 +157,23 @@ pam_module_t *openpam_static(const char *); #endif pam_module_t *openpam_dynamic(const char *); -#define FREE(p) do { free((p)); (p) = NULL; } while (0) +#define FREE(p) \ + do { \ + free(p); \ + (p) = NULL; \ + } while (0) + +#define FREEV(c, v) \ + do { \ + while (c) { \ + --(c); \ + FREE((v)[(c)]); \ + } \ + FREE(v); \ + } while (0) #include "openpam_constants.h" #include "openpam_debug.h" +#include "openpam_features.h" #endif diff --git a/lib/openpam_load.c b/lib/openpam_load.c index 0eb8ea7..871d1a8 100644 --- a/lib/openpam_load.c +++ b/lib/openpam_load.c @@ -32,7 +32,7 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $Id: openpam_load.c 491 2011-11-12 00:12:32Z des $ + * $Id: openpam_load.c 547 2012-04-01 15:01:21Z des $ */ #ifdef HAVE_CONFIG_H @@ -108,9 +108,7 @@ openpam_destroy_chain(pam_chain_t *chain) return; openpam_destroy_chain(chain->next); chain->next = NULL; - while (chain->optc--) - FREE(chain->optv[chain->optc]); - FREE(chain->optv); + FREEV(chain->optc, chain->optv); openpam_release_module(chain->module); chain->module = NULL; FREE(chain); diff --git a/lib/openpam_log.c b/lib/openpam_log.c index 9e3d28b..2b89f6c 100644 --- a/lib/openpam_log.c +++ b/lib/openpam_log.c @@ -32,18 +32,17 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $Id: openpam_log.c 437 2011-09-13 12:00:13Z des $ + * $Id: openpam_log.c 544 2012-03-31 22:47:15Z des $ */ #ifdef HAVE_CONFIG_H # include "config.h" #endif -#include <ctype.h> +#include <errno.h> #include <stdarg.h> #include <stdio.h> #include <stdlib.h> -#include <string.h> #include <syslog.h> #include <security/pam_appl.h> @@ -71,6 +70,7 @@ openpam_log(int level, const char *fmt, ...) int priority; switch (level) { + case PAM_LOG_LIBDEBUG: case PAM_LOG_DEBUG: if (!openpam_debug) return; @@ -100,8 +100,10 @@ _openpam_log(int level, const char *func, const char *fmt, ...) va_list ap; char *format; int priority; + int serrno; switch (level) { + case PAM_LOG_LIBDEBUG: case PAM_LOG_DEBUG: if (!openpam_debug) return; @@ -119,10 +121,13 @@ _openpam_log(int level, const char *func, const char *fmt, ...) break; } va_start(ap, fmt); + serrno = errno; if (asprintf(&format, "in %s(): %s", func, fmt) > 0) { + errno = serrno; vsyslog(priority, format, ap); FREE(format); } else { + errno = serrno; vsyslog(priority, fmt, ap); } va_end(ap); @@ -137,6 +142,9 @@ _openpam_log(int level, const char *func, const char *fmt, ...) * The =level argument indicates the importance of the message. * The following levels are defined: * + * =PAM_LOG_LIBDEBUG: + * Debugging messages. + * For internal use only. * =PAM_LOG_DEBUG: * Debugging messages. * These messages are normally not logged unless the global diff --git a/lib/openpam_readline.c b/lib/openpam_readline.c index 9cc8cc1..014acfb 100644 --- a/lib/openpam_readline.c +++ b/lib/openpam_readline.c @@ -32,7 +32,7 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $Id: openpam_readline.c 473 2011-11-03 10:48:25Z des $ + * $Id: openpam_readline.c 596 2012-04-14 14:52:40Z des $ */ #ifdef HAVE_CONFIG_H @@ -44,6 +44,7 @@ #include <stdlib.h> #include <security/pam_appl.h> + #include "openpam_impl.h" #define MIN_LINE_LENGTH 128 @@ -61,22 +62,11 @@ openpam_readline(FILE *f, int *lineno, size_t *lenp) size_t len, size; int ch; - if ((line = malloc(MIN_LINE_LENGTH)) == NULL) + if ((line = malloc(size = MIN_LINE_LENGTH)) == NULL) { + openpam_log(PAM_LOG_ERROR, "malloc(): %m"); return (NULL); - size = MIN_LINE_LENGTH; + } len = 0; - -#define line_putch(ch) do { \ - if (len >= size - 1) { \ - char *tmp = realloc(line, size *= 2); \ - if (tmp == NULL) \ - goto fail; \ - line = tmp; \ - } \ - line[len++] = ch; \ - line[len] = '\0'; \ -} while (0) - for (;;) { ch = fgetc(f); /* strip comment */ @@ -105,26 +95,15 @@ openpam_readline(FILE *f, int *lineno, size_t *lenp) /* done */ break; } - /* whitespace */ - if (isspace(ch)) { - /* ignore leading whitespace */ - /* collapse linear whitespace */ - if (len > 0 && line[len - 1] != ' ') - line_putch(' '); - continue; - } /* anything else */ - line_putch(ch); + if (openpam_straddch(&line, &size, &len, ch) != 0) + goto fail; } - - /* remove trailing whitespace */ - while (len > 0 && isspace((unsigned char)line[len - 1])) - --len; - line[len] = '\0'; if (len == 0) goto fail; if (lenp != NULL) *lenp = len; + openpam_log(PAM_LOG_LIBDEBUG, "returning '%s'", line); return (line); fail: FREE(line); @@ -132,16 +111,18 @@ fail: } /** + * DEPRECATED openpam_readlinev + * * The =openpam_readline function reads a line from a file, and returns it - * in a NUL-terminated buffer allocated with =malloc. + * in a NUL-terminated buffer allocated with =!malloc. * * The =openpam_readline function performs a certain amount of processing * on the data it reads: * - * - Comments (introduced by a hash sign) are stripped, as is leading and - * trailing whitespace. - * - Any amount of linear whitespace is collapsed to a single space. + * - Comments (introduced by a hash sign) are stripped. + * * - Blank lines are ignored. + * * - If a line ends in a backslash, the backslash is stripped and the * next line is appended. * @@ -152,5 +133,8 @@ fail: * terminating NUL character) is stored in the variable it points to. * * The caller is responsible for releasing the returned buffer by passing - * it to =free. + * it to =!free. + * + * >openpam_readlinev + * >openpam_readword */ diff --git a/lib/openpam_readlinev.c b/lib/openpam_readlinev.c new file mode 100644 index 0000000..5a43b61 --- /dev/null +++ b/lib/openpam_readlinev.c @@ -0,0 +1,156 @@ +/*- + * Copyright (c) 2012 Dag-Erling Smørgrav + * All rights reserved. + * + * 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 + * in this position and unchanged. + * 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_readlinev.c 588 2012-04-08 11:52:25Z des $ + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> + +#include <security/pam_appl.h> + +#include "openpam_impl.h" + +#define MIN_WORDV_SIZE 32 + +/* + * OpenPAM extension + * + * Read a line from a file and split it into words. + */ + +char ** +openpam_readlinev(FILE *f, int *lineno, int *lenp) +{ + char *word, **wordv, **tmp; + size_t wordlen, wordvsize; + int ch, serrno, wordvlen; + + wordvsize = MIN_WORDV_SIZE; + wordvlen = 0; + if ((wordv = malloc(wordvsize * sizeof *wordv)) == NULL) { + openpam_log(PAM_LOG_ERROR, "malloc(): %m"); + errno = ENOMEM; + return (NULL); + } + wordv[wordvlen] = NULL; + while ((word = openpam_readword(f, lineno, &wordlen)) != NULL) { + if ((unsigned int)wordvlen + 1 >= wordvsize) { + /* need to expand the array */ + wordvsize *= 2; + tmp = realloc(wordv, wordvsize * sizeof *wordv); + if (tmp == NULL) { + openpam_log(PAM_LOG_ERROR, "malloc(): %m"); + errno = ENOMEM; + break; + } + wordv = tmp; + } + /* insert our word */ + wordv[wordvlen++] = word; + wordv[wordvlen] = NULL; + } + if (errno != 0) { + /* I/O error or out of memory */ + serrno = errno; + while (wordvlen--) + free(wordv[wordvlen]); + free(wordv); + errno = serrno; + return (NULL); + } + /* assert(!ferror(f)) */ + ch = fgetc(f); + /* assert(ch == EOF || ch == '\n') */ + if (ch == EOF && wordvlen == 0) { + free(wordv); + return (NULL); + } + if (ch == '\n' && lineno != NULL) + ++*lineno; + if (lenp != NULL) + *lenp = wordvlen; + return (wordv); +} + +/** + * The =openpam_readlinev function reads a line from a file, splits it + * into words according to the rules described in the =openpam_readword + * manual page, and returns a list of those words. + * + * If =lineno is not =NULL, the integer variable it points to is + * incremented every time a newline character is read. + * This includes quoted or escaped newline characters and the newline + * character at the end of the line. + * + * If =lenp is not =NULL, the number of words on the line is stored in the + * variable to which it points. + * + * RETURN VALUES + * + * If successful, the =openpam_readlinev function returns a pointer to a + * dynamically allocated array of pointers to individual dynamically + * allocated NUL-terminated strings, each containing a single word, in the + * order in which they were encountered on the line. + * The array is terminated by a =NULL pointer. + * + * The caller is responsible for freeing both the array and the individual + * strings by passing each of them to =!free. + * + * If the end of the line was reached before any words were read, + * =openpam_readlinev returns a pointer to a dynamically allocated array + * containing a single =NULL pointer. + * + * The =openpam_readlinev function can fail and return =NULL for one of + * four reasons: + * + * - The end of the file was reached before any words were read; :errno is + * zero, =!ferror returns zero, and =!feof returns a non-zero value. + * + * - The end of the file was reached while a quote or backslash escape + * was in effect; :errno is set to =EINVAL, =!ferror returns zero, and + * =!feof returns a non-zero value. + * + * - An error occurred while reading from the file; :errno is non-zero, + * =!ferror returns a non-zero value and =!feof returns zero. + * + * - A =!malloc or =!realloc call failed; :errno is set to =ENOMEM, + * =!ferror returns a non-zero value, and =!feof may or may not return + * a non-zero value. + * + * >openpam_readline + * >openpam_readword + * + * AUTHOR DES + */ diff --git a/lib/openpam_readword.c b/lib/openpam_readword.c new file mode 100644 index 0000000..74a4d46 --- /dev/null +++ b/lib/openpam_readword.c @@ -0,0 +1,207 @@ +/*- + * Copyright (c) 2012 Dag-Erling Smørgrav + * All rights reserved. + * + * 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 + * in this position and unchanged. + * 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_readword.c 588 2012-04-08 11:52:25Z des $ + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> + +#include <security/pam_appl.h> + +#include "openpam_impl.h" +#include "openpam_ctype.h" + +#define MIN_WORD_SIZE 32 + +/* + * OpenPAM extension + * + * Read a word from a file, respecting shell quoting rules. + */ + +char * +openpam_readword(FILE *f, int *lineno, size_t *lenp) +{ + char *word; + size_t size, len; + int ch, comment, escape, quote; + int serrno; + + errno = 0; + + /* skip initial whitespace */ + comment = 0; + while ((ch = getc(f)) != EOF && ch != '\n') { + if (ch == '#') + comment = 1; + if (!is_lws(ch) && !comment) + break; + } + if (ch == EOF) + return (NULL); + ungetc(ch, f); + if (ch == '\n') + return (NULL); + + word = NULL; + size = len = 0; + escape = quote = 0; + while ((ch = fgetc(f)) != EOF && (!is_ws(ch) || quote || escape)) { + if (ch == '\\' && !escape && quote != '\'') { + /* escape next character */ + escape = ch; + } else if ((ch == '\'' || ch == '"') && !quote && !escape) { + /* begin quote */ + quote = ch; + /* edge case: empty quoted string */ + if (word == NULL && (word = malloc(1)) == NULL) { + openpam_log(PAM_LOG_ERROR, "malloc(): %m"); + errno = ENOMEM; + return (NULL); + } + *word = '\0'; + size = 1; + } else if (ch == quote && !escape) { + /* end quote */ + quote = 0; + } else if (ch == '\n' && escape && quote != '\'') { + /* line continuation */ + escape = 0; + } else { + if (escape && quote && ch != '\\' && ch != quote && + openpam_straddch(&word, &size, &len, '\\') != 0) { + free(word); + errno = ENOMEM; + return (NULL); + } + if (openpam_straddch(&word, &size, &len, ch) != 0) { + free(word); + errno = ENOMEM; + return (NULL); + } + escape = 0; + } + if (lineno != NULL && ch == '\n') + ++*lineno; + } + if (ch == EOF && ferror(f)) { + serrno = errno; + free(word); + errno = serrno; + return (NULL); + } + if (ch == EOF && (escape || quote)) { + /* Missing escaped character or closing quote. */ + openpam_log(PAM_LOG_ERROR, "unexpected end of file"); + free(word); + errno = EINVAL; + return (NULL); + } + ungetc(ch, f); + if (lenp != NULL) + *lenp = len; + return (word); +} + +/** + * The =openpam_readword function reads the next word from a file, and + * returns it in a NUL-terminated buffer allocated with =!malloc. + * + * A word is a sequence of non-whitespace characters. + * However, whitespace characters can be included in a word if quoted or + * escaped according to the following rules: + * + * - An unescaped single or double quote introduces a quoted string, + * which ends when the same quote character is encountered a second + * time. + * The quotes themselves are stripped. + * + * - Within a single- or double-quoted string, all whitespace characters, + * including the newline character, are preserved as-is. + * + * - Outside a quoted string, a backslash escapes the next character, + * which is preserved as-is, unless that character is a newline, in + * which case it is discarded and reading continues at the beginning of + * the next line as if the backslash and newline had not been there. + * In all cases, the backslash itself is discarded. + * + * - Within a single-quoted string, double quotes and backslashes are + * preserved as-is. + * + * - Within a double-quoted string, a single quote is preserved as-is, + * and a backslash is preserved as-is unless used to escape a double + * quote. + * + * In addition, if the first non-whitespace character on the line is a + * hash character (#), the rest of the line is discarded. + * If a hash character occurs within a word, however, it is preserved + * as-is. + * A backslash at the end of a comment does cause line continuation. + * + * If =lineno is not =NULL, the integer variable it points to is + * incremented every time a quoted or escaped newline character is read. + * + * If =lenp is not =NULL, the length of the word (after quotes and + * backslashes have been removed) is stored in the variable it points to. + * + * RETURN VALUES + * + * If successful, the =openpam_readword function returns a pointer to a + * dynamically allocated NUL-terminated string containing the first word + * encountered on the line. + * + * The caller is responsible for releasing the returned buffer by passing + * it to =!free. + * + * If =openpam_readword reaches the end of the line or file before any + * characters are copied to the word, it returns =NULL. In the former + * case, the newline is pushed back to the file. + * + * If =openpam_readword reaches the end of the file while a quote or + * backslash escape is in effect, it sets :errno to =EINVAL and returns + * =NULL. + * + * IMPLEMENTATION NOTES + * + * The parsing rules are intended to be equivalent to the normal POSIX + * shell quoting rules. + * Any discrepancy is a bug and should be reported to the author along + * with sample input that can be used to reproduce the error. + * + * >openpam_readline + * >openpam_readlinev + * + * AUTHOR DES + */ diff --git a/lib/openpam_set_feature.c b/lib/openpam_set_feature.c new file mode 100644 index 0000000..4f6a4a5 --- /dev/null +++ b/lib/openpam_set_feature.c @@ -0,0 +1,75 @@ +/*- + * Copyright (c) 2012 Dag-Erling Smørgrav + * All rights reserved. + * + * 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 + * in this position and unchanged. + * 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_set_feature.c 608 2012-05-17 16:00:13Z des $ + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include <security/pam_appl.h> +#include <security/openpam.h> + +#include "openpam_impl.h" + +/* + * OpenPAM extension + * + * Enable or disable an optional feature. + */ + +int +openpam_set_feature(int feature, int onoff) +{ + + ENTERF(feature); + if (feature < 0 || feature >= OPENPAM_NUM_FEATURES) + RETURNC(PAM_SYMBOL_ERR); + openpam_features[feature].onoff = onoff; + RETURNC(PAM_SUCCESS); +} + +/* + * Error codes: + * + * PAM_SYMBOL_ERR + */ + +/** + * EXPERIMENTAL + * + * The =openpam_set_feature function sets the state of the specified + * feature to the value specified by the =onoff argument. + * See =openpam_get_feature for a list of recognized features. + * + * >openpam_get_feature + * + * AUTHOR DES + */ diff --git a/lib/openpam_set_option.c b/lib/openpam_set_option.c index c7cb1c7..1712a71 100644 --- a/lib/openpam_set_option.c +++ b/lib/openpam_set_option.c @@ -32,7 +32,7 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $Id: openpam_set_option.c 482 2011-11-03 16:33:02Z des $ + * $Id: openpam_set_option.c 532 2012-03-31 14:24:53Z des $ */ #ifdef HAVE_CONFIG_H @@ -46,7 +46,6 @@ #include <string.h> #include <security/pam_appl.h> -#include <security/openpam.h> #include "openpam_impl.h" diff --git a/lib/openpam_straddch.c b/lib/openpam_straddch.c new file mode 100644 index 0000000..9845cc6 --- /dev/null +++ b/lib/openpam_straddch.c @@ -0,0 +1,111 @@ +/*- + * Copyright (c) 2012 Dag-Erling Smørgrav + * All rights reserved. + * + * 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 + * in this position and unchanged. + * 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_straddch.c 568 2012-04-05 14:35:53Z des $ + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include <errno.h> +#include <stdlib.h> + +#include <security/pam_appl.h> + +#include "openpam_impl.h" + +#define MIN_STR_SIZE 32 + +/* + * OpenPAM extension + * + * Add a character to a string, expanding the buffer if needed. + */ + +int +openpam_straddch(char **str, size_t *size, size_t *len, int ch) +{ + size_t tmpsize; + char *tmpstr; + + if (*str == NULL) { + /* initial allocation */ + tmpsize = MIN_STR_SIZE; + if ((tmpstr = malloc(tmpsize)) == NULL) { + openpam_log(PAM_LOG_ERROR, "malloc(): %m"); + errno = ENOMEM; + return (-1); + } + *str = tmpstr; + *size = tmpsize; + *len = 0; + } else if (*len + 1 >= *size) { + /* additional space required */ + tmpsize = *size * 2; + if ((tmpstr = realloc(*str, tmpsize)) == NULL) { + openpam_log(PAM_LOG_ERROR, "realloc(): %m"); + errno = ENOMEM; + return (-1); + } + *size = tmpsize; + *str = tmpstr; + } + (*str)[*len] = ch; + ++*len; + (*str)[*len] = '\0'; + return (0); +} + +/** + * The =openpam_straddch function appends a character to a dynamically + * allocated NUL-terminated buffer, reallocating the buffer as needed. + * + * The =str argument points to a variable containing either a pointer to + * an existing buffer or =NULL. + * If the value of the variable pointed to by =str is =NULL, a new buffer + * is allocated. + * + * The =size and =len argument point to variables used to hold the size + * of the buffer and the length of the string it contains, respectively. + * + * If a new buffer is allocated or an existing buffer is reallocated to + * make room for the additional character, =str and =size are updated + * accordingly. + * + * The =openpam_straddch function ensures that the buffer is always + * NUL-terminated. + * + * If the =openpam_straddch function is successful, it increments the + * integer variable pointed to by =len and returns 0. + * Otherwise, it leaves the variables pointed to by =str, =size and =len + * unmodified, sets :errno to =ENOMEM and returns -1. + * + * AUTHOR DES + */ diff --git a/lib/openpam_strlcat.h b/lib/openpam_strlcat.h new file mode 100644 index 0000000..1f26693 --- /dev/null +++ b/lib/openpam_strlcat.h @@ -0,0 +1,54 @@ +/*- + * Copyright (c) 2011 Dag-Erling Smørgrav + * All rights reserved. + * + * 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 + * in this position and unchanged. + * 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_strlcat.h 578 2012-04-06 00:45:59Z des $ + */ + +#ifndef OPENPAM_STRLCAT_H_INCLUDED +#define OPENPAM_STRLCAT_H_INCLUDED + +#ifndef HAVE_STRLCAT +/* like strcat(3), but always NUL-terminates; returns strlen(src) */ +static size_t +strlcat(char *dst, const char *src, size_t size) +{ + size_t len; + + for (len = 0; *dst && size > 1; ++len, --size) + dst++; + for (; *src && size > 1; ++len, --size) + *dst++ = *src++; + *dst = '\0'; + while (*src) + ++len, ++src; + return (len); +} +#endif + +#endif diff --git a/lib/openpam_strlcmp.h b/lib/openpam_strlcmp.h index c692225..2a78e0f 100644 --- a/lib/openpam_strlcmp.h +++ b/lib/openpam_strlcmp.h @@ -11,6 +11,9 @@ * 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 @@ -24,7 +27,7 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $Id: openpam_strlcmp.h 475 2011-11-03 15:29:24Z des $ + * $Id: openpam_strlcmp.h 578 2012-04-06 00:45:59Z des $ */ #ifndef OPENPAM_STRLCMP_H_INCLUDED diff --git a/lib/openpam_strlcpy.h b/lib/openpam_strlcpy.h index 921653b..9c65548 100644 --- a/lib/openpam_strlcpy.h +++ b/lib/openpam_strlcpy.h @@ -11,6 +11,9 @@ * 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 @@ -24,7 +27,7 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $Id: openpam_strlcpy.h 492 2011-11-20 02:04:17Z des $ + * $Id: openpam_strlcpy.h 578 2012-04-06 00:45:59Z des $ */ #ifndef OPENPAM_STRLCPY_H_INCLUDED @@ -32,7 +35,7 @@ #ifndef HAVE_STRLCPY /* like strcpy(3), but always NUL-terminates; returns strlen(src) */ -size_t +static size_t strlcpy(char *dst, const char *src, size_t size) { size_t len; diff --git a/lib/openpam_subst.c b/lib/openpam_subst.c index d54b827..bab7a78 100644 --- a/lib/openpam_subst.c +++ b/lib/openpam_subst.c @@ -11,6 +11,9 @@ * 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 @@ -24,7 +27,7 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $Id: openpam_subst.c 461 2011-11-02 14:00:38Z des $ + * $Id: openpam_subst.c 543 2012-03-31 22:11:34Z des $ */ #ifdef HAVE_CONFIG_H diff --git a/lib/openpam_ttyconv.c b/lib/openpam_ttyconv.c index ec078f4..14a324d 100644 --- a/lib/openpam_ttyconv.c +++ b/lib/openpam_ttyconv.c @@ -32,7 +32,7 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $Id: openpam_ttyconv.c 437 2011-09-13 12:00:13Z des $ + * $Id: openpam_ttyconv.c 527 2012-02-26 03:23:59Z des $ */ #ifdef HAVE_CONFIG_H @@ -69,17 +69,17 @@ prompt(const char *msg) { char buf[PAM_MAX_RESP_SIZE]; struct sigaction action, saved_action; - sigset_t saved_sigset, sigset; + sigset_t saved_sigset, the_sigset; unsigned int saved_alarm; int eof, error, fd; size_t len; char *retval; char ch; - sigemptyset(&sigset); - sigaddset(&sigset, SIGINT); - sigaddset(&sigset, SIGTSTP); - sigprocmask(SIG_SETMASK, &sigset, &saved_sigset); + sigemptyset(&the_sigset); + sigaddset(&the_sigset, SIGINT); + sigaddset(&the_sigset, SIGTSTP); + sigprocmask(SIG_SETMASK, &the_sigset, &saved_sigset); action.sa_handler = &timeout; action.sa_flags = 0; sigemptyset(&action.sa_mask); diff --git a/lib/pam_get_authtok.c b/lib/pam_get_authtok.c index a0613ef..1a3aebc 100644 --- a/lib/pam_get_authtok.c +++ b/lib/pam_get_authtok.c @@ -32,7 +32,7 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $Id: pam_get_authtok.c 455 2011-10-29 18:31:11Z des $ + * $Id: pam_get_authtok.c 510 2011-12-31 13:14:23Z des $ */ #ifdef HAVE_CONFIG_H @@ -50,6 +50,7 @@ #include "openpam_impl.h" static const char authtok_prompt[] = "Password:"; +static const char authtok_prompt_remote[] = "Password for %u@%h:"; static const char oldauthtok_prompt[] = "Old Password:"; static const char newauthtok_prompt[] = "New Password:"; @@ -69,6 +70,7 @@ pam_get_authtok(pam_handle_t *pamh, size_t prompt_size; const void *oldauthtok, *prevauthtok, *promptp; const char *prompt_option, *default_prompt; + const void *lhost, *rhost; char *resp, *resp2; int pitem, r, style, twice; @@ -82,6 +84,14 @@ pam_get_authtok(pam_handle_t *pamh, pitem = PAM_AUTHTOK_PROMPT; prompt_option = "authtok_prompt"; default_prompt = authtok_prompt; + r = pam_get_item(pamh, PAM_RHOST, &rhost); + if (r == PAM_SUCCESS && rhost != NULL) { + r = pam_get_item(pamh, PAM_HOST, &lhost); + if (r == PAM_SUCCESS && lhost != NULL) { + if (strcmp(rhost, lhost) != 0) + default_prompt = authtok_prompt_remote; + } + } r = pam_get_item(pamh, PAM_OLDAUTHTOK, &oldauthtok); if (r == PAM_SUCCESS && oldauthtok != NULL) { default_prompt = newauthtok_prompt; diff --git a/lib/pam_putenv.c b/lib/pam_putenv.c index 369066d..e1f0bc3 100644 --- a/lib/pam_putenv.c +++ b/lib/pam_putenv.c @@ -32,7 +32,7 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $Id: pam_putenv.c 437 2011-09-13 12:00:13Z des $ + * $Id: pam_putenv.c 539 2012-03-31 20:53:22Z des $ */ #ifdef HAVE_CONFIG_H @@ -102,7 +102,7 @@ pam_putenv(pam_handle_t *pamh, */ /** - * The =pam_putenv function sets a environment variable. + * The =pam_putenv function sets an environment variable. * Its semantics are similar to those of =putenv, but it modifies the PAM * context's environment list instead of the application's. * diff --git a/lib/pam_setenv.c b/lib/pam_setenv.c index fbe6a8f..6fd4c10 100644 --- a/lib/pam_setenv.c +++ b/lib/pam_setenv.c @@ -32,7 +32,7 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $Id: pam_setenv.c 437 2011-09-13 12:00:13Z des $ + * $Id: pam_setenv.c 539 2012-03-31 20:53:22Z des $ */ #ifdef HAVE_CONFIG_H @@ -92,7 +92,7 @@ pam_setenv(pam_handle_t *pamh, */ /** - * The =pam_setenv function sets a environment variable. + * The =pam_setenv function sets an environment variable. * Its semantics are similar to those of =setenv, but it modifies the PAM * context's environment list instead of the application's. * |