pam/pam_env_econf.patch
Thorsten Kukuk a7257563cb Accepting request 1010118 from home:schubi2
- pam_env: Using libeconf for reading configuration and environment
  files.

OBS-URL: https://build.opensuse.org/request/show/1010118
OBS-URL: https://build.opensuse.org/package/show/Linux-PAM/pam?expand=0&rev=264
2022-10-12 09:48:57 +00:00

1020 lines
33 KiB
Diff

diff -Naur org/configure.ac patch/configure.ac
--- org/configure.ac 2022-10-11 12:35:53.558193223 +0200
+++ patch/configure.ac 2022-10-11 12:36:32.502192985 +0200
@@ -511,7 +511,11 @@
[Directory for distribution provided configuration files])
AC_DEFINE_UNQUOTED([VENDOR_SCONFIGDIR], ["$enable_vendordir/security"],
[Directory for PAM modules distribution provided configuration files])
- STRINGPARAM_VENDORDIR="--stringparam vendordir '$enable_vendordir' --stringparam profile.condition 'with_vendordir'"
+ if test "$WITH_ECONF" = "yes" ; then
+ STRINGPARAM_VENDORDIR="--stringparam vendordir '$enable_vendordir' --stringparam profile.condition 'with_vendordir;with_vendordir_and_with_econf'"
+ else
+ STRINGPARAM_VENDORDIR="--stringparam vendordir '$enable_vendordir' --stringparam profile.condition 'with_vendordir;with_vendordir_and_without_econf"
+ fi
else
STRINGPARAM_VENDORDIR="--stringparam profile.condition 'without_vendordir'"
fi
diff -Naur org/modules/pam_env/Makefile.am patch/modules/pam_env/Makefile.am
--- org/modules/pam_env/Makefile.am 2022-10-11 12:35:53.574193223 +0200
+++ patch/modules/pam_env/Makefile.am 2022-10-11 12:36:32.518192985 +0200
@@ -12,20 +12,23 @@
endif
XMLS = README.xml pam_env.conf.5.xml pam_env.8.xml
dist_check_SCRIPTS = tst-pam_env
-TESTS = $(dist_check_SCRIPTS)
+TESTS = $(dist_check_SCRIPTS) $(check_PROGRAMS)
securelibdir = $(SECUREDIR)
secureconfdir = $(SCONFIGDIR)
AM_CFLAGS = -I$(top_srcdir)/libpam/include -I$(top_srcdir)/libpamc/include \
- $(WARN_CFLAGS)
+ $(WARN_CFLAGS) -DSYSCONFDIR=\"$(sysconfdir)\" $(ECONF_CFLAGS)
AM_LDFLAGS = -no-undefined -avoid-version -module
if HAVE_VERSIONING
AM_LDFLAGS += -Wl,--version-script=$(srcdir)/../modules.map
endif
securelib_LTLIBRARIES = pam_env.la
-pam_env_la_LIBADD = $(top_builddir)/libpam/libpam.la
+pam_env_la_LIBADD = $(top_builddir)/libpam/libpam.la $(ECONF_LIBS)
+
+check_PROGRAMS = tst-pam_env-retval
+tst_pam_env_retval_LDADD = $(top_builddir)/libpam/libpam.la
dist_secureconf_DATA = pam_env.conf
dist_sysconf_DATA = environment
diff -Naur org/modules/pam_env/pam_env.8.xml patch/modules/pam_env/pam_env.8.xml
--- org/modules/pam_env/pam_env.8.xml 2022-10-11 12:35:53.574193223 +0200
+++ patch/modules/pam_env/pam_env.8.xml 2022-10-11 12:36:32.518192985 +0200
@@ -52,13 +52,55 @@
variables as well as <emphasis>PAM_ITEM</emphasis>s such as
<emphasis>PAM_RHOST</emphasis>.
</para>
- <para>
+ <para condition="with_vendordir_and_with_econf">
+ Rules for (un)setting of variables can be defined in an own config
+ file. The path to this file can be specified with the
+ <emphasis>conffile</emphasis> option.
+ If this file does not exist, the default rules are taken from the
+ config files <filename>/etc/security/pam_env.conf</filename> and
+ <filename>/etc/security/pam_env.conf.d/*.conf</filename>.
+ If the file <filename>/etc/security/pam_env.conf</filename> does not
+ exist, the rules are taken from the files
+ <filename>%vendordir%/security/pam_env.conf</filename>,
+ <filename>%vendordir%/security/pam_env.conf.d/*.conf</filename> and
+ <filename>/etc/security/pam_env.conf.d/*.conf</filename> in that order.
+ </para>
+ <para condition="with_vendordir_and_without_econf">
+ By default rules for (un)setting of variables are taken from the
+ config file <filename>/etc/security/pam_env.conf</filename>.
+ If this file does not exist <filename>%vendordir%/security/pam_env.conf</filename> is used.
+ An alternate file can be specified with the <emphasis>conffile</emphasis>
+ option, which overrules all other files.
+ </para>
+ <para condition="without_vendordir">
By default rules for (un)setting of variables are taken from the
config file <filename>/etc/security/pam_env.conf</filename>. An
alternate file can be specified with the <emphasis>conffile</emphasis>
option.
</para>
- <para>
+ <para condition="with_vendordir_and_with_econf">
+ Environment variables can be defined in a file with simple <emphasis>KEY=VAL</emphasis>
+ pairs on separate lines. The path to this file can be specified with the
+ <emphasis>envfile</emphasis> option.
+ If this file has not been defined, the settings are read from the
+ files <filename>/etc/security/environment</filename> and
+ <filename>/etc/security/environment.d/*</filename>.
+ If the file <filename>/etc/environment</filename> does not exist, the
+ settings are read from the files <filename>%vendordir%/environment</filename>,
+ <filename>%vendordir%/environment.d/*</filename> and
+ <filename>/etc/environment.d/*</filename> in that order.
+ And last but not least, with the <emphasis>readenv</emphasis> option this mechanism can
+ be completely disabled.
+ </para>
+ <para condition="with_vendordir_and_without_econf">
+ Second a file (<filename>/etc/environment</filename> by default) with simple
+ <emphasis>KEY=VAL</emphasis> pairs on separate lines will be read.
+ If this file does not exist, <filename>%vendordir%/etc/environment</filename> is used.
+ With the <emphasis>envfile</emphasis> option an alternate file can be specified,
+ which overrules all other files.
+ And with the <emphasis>readenv</emphasis> option this can be completely disabled.
+ </para>
+ <para condition="without_vendordir">
Second a file (<filename>/etc/environment</filename> by default) with simple
<emphasis>KEY=VAL</emphasis> pairs on separate lines will be read.
With the <emphasis>envfile</emphasis> option an alternate file can be specified.
@@ -224,12 +266,14 @@
<title>FILES</title>
<variablelist>
<varlistentry>
+ <term condition="with_vendordir"><filename>/usr/etc/security/pam_env.conf</filename></term>
<term><filename>/etc/security/pam_env.conf</filename></term>
<listitem>
<para>Default configuration file</para>
</listitem>
</varlistentry>
<varlistentry>
+ <term condition="with_vendordir"><filename>/usr/etc/environment</filename></term>
<term><filename>/etc/environment</filename></term>
<listitem>
<para>Default environment file</para>
diff -Naur org/modules/pam_env/pam_env.c patch/modules/pam_env/pam_env.c
--- org/modules/pam_env/pam_env.c 2022-10-11 12:35:53.574193223 +0200
+++ patch/modules/pam_env/pam_env.c 2022-10-11 12:36:32.518192985 +0200
@@ -7,6 +7,9 @@
*/
#define DEFAULT_ETC_ENVFILE "/etc/environment"
+#ifdef VENDORDIR
+#define VENDOR_DEFAULT_ETC_ENVFILE (VENDORDIR "/etc/environment")
+#endif
#define DEFAULT_READ_ENVFILE 1
#define DEFAULT_USER_ENVFILE ".pam_environment"
@@ -25,6 +28,9 @@
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
+#ifdef USE_ECONF
+#include <libeconf.h>
+#endif
#include <security/pam_modules.h>
#include <security/pam_modutil.h>
@@ -42,6 +48,9 @@
} VAR;
#define DEFAULT_CONF_FILE (SCONFIGDIR "/pam_env.conf")
+#ifdef VENDOR_SCONFIGDIR
+#define VENDOR_DEFAULT_CONF_FILE (VENDOR_SCONFIGDIR "/pam_env.conf")
+#endif
#define BUF_SIZE 8192
#define MAX_ENV 8192
@@ -53,18 +62,19 @@
#define UNDEFINE_VAR 102
#define ILLEGAL_VAR 103
-static int _assemble_line(FILE *, char *, int);
-static int _parse_line(const pam_handle_t *, const char *, VAR *);
-static int _check_var(pam_handle_t *, VAR *); /* This is the real meat */
-static void _clean_var(VAR *);
-static int _expand_arg(pam_handle_t *, char **);
-static const char * _pam_get_item_byname(pam_handle_t *, const char *);
-static int _define_var(pam_handle_t *, int, VAR *);
-static int _undefine_var(pam_handle_t *, int, VAR *);
-
/* This is a special value used to designate an empty string */
static char quote='\0';
+static void free_string_array(char **array)
+{
+ if (array == NULL)
+ return;
+ for (char **entry = array; *entry != NULL; ++entry) {
+ free(*entry);
+ }
+ free(array);
+}
+
/* argument parsing */
#define PAM_DEBUG_ARG 0x01
@@ -77,10 +87,10 @@
int ctrl=0;
*user_envfile = DEFAULT_USER_ENVFILE;
- *envfile = DEFAULT_ETC_ENVFILE;
+ *envfile = NULL;
*readenv = DEFAULT_READ_ENVFILE;
*user_readenv = DEFAULT_USER_READ_ENVFILE;
- *conffile = DEFAULT_CONF_FILE;
+ *conffile = NULL;
/* step through arguments */
for (; argc-- > 0; ++argv) {
@@ -128,166 +138,145 @@
return ctrl;
}
-static int
-_parse_config_file(pam_handle_t *pamh, int ctrl, const char *file)
-{
- int retval;
- char buffer[BUF_SIZE];
- FILE *conf;
- VAR Var, *var=&Var;
-
- D(("Called."));
-
- var->name=NULL; var->defval=NULL; var->override=NULL;
+#ifdef USE_ECONF
- D(("Config file name is: %s", file));
+#define ENVIRONMENT "environment"
+#define PAM_ENV "pam_env"
- /*
- * Lets try to open the config file, parse it and process
- * any variables found.
- */
+static int
+isDirectory(const char *path) {
+ struct stat statbuf;
+ if (stat(path, &statbuf) != 0)
+ return 0;
+ return S_ISDIR(statbuf.st_mode);
+}
- if ((conf = fopen(file,"r")) == NULL) {
- pam_syslog(pamh, LOG_ERR, "Unable to open config file: %s: %m", file);
- return PAM_IGNORE;
+static int
+econf_read_file(const pam_handle_t *pamh, const char *filename, const char *delim,
+ const char *name, const char *suffix, const char *subpath,
+ char ***lines)
+{
+ econf_file *key_file = NULL;
+ econf_err error;
+ size_t key_number = 0;
+ char **keys = NULL;
+ const char *base_dir = "";
+
+ if (filename != NULL) {
+ if (isDirectory(filename)) {
+ /* Set base directory which can be different from root */
+ D(("filename argument is a directory: %s", filename));
+ base_dir = filename;
+ } else {
+ /* Read only one file */
+ error = econf_readFile (&key_file, filename, delim, "#");
+ D(("File name is: %s", filename));
+ if (error != ECONF_SUCCESS) {
+ pam_syslog(pamh, LOG_ERR, "Unable to open env file: %s: %s", filename,
+ econf_errString(error));
+ if (error == ECONF_NOFILE)
+ return PAM_IGNORE;
+ else
+ return PAM_ABORT;
+ }
+ }
}
-
- /* _pam_assemble_line will provide a complete line from the config file,
- * with all comments removed and any escaped newlines fixed up
- */
-
- while (( retval = _assemble_line(conf, buffer, BUF_SIZE)) > 0) {
- D(("Read line: %s", buffer));
-
- if ((retval = _parse_line(pamh, buffer, var)) == GOOD_LINE) {
- retval = _check_var(pamh, var);
-
- if (DEFINE_VAR == retval) {
- retval = _define_var(pamh, ctrl, var);
-
- } else if (UNDEFINE_VAR == retval) {
- retval = _undefine_var(pamh, ctrl, var);
+ if (filename == NULL || base_dir[0] != '\0') {
+ /* Read and merge all setting in e.g. /usr/etc and /etc */
+ char *vendor_dir = NULL, *sysconf_dir;
+ if (subpath != NULL && subpath[0] != '\0') {
+#ifdef VENDORDIR
+ if (asprintf(&vendor_dir, "%s%s/%s/", base_dir, VENDORDIR, subpath) < 0) {
+ pam_syslog(pamh, LOG_ERR, "Cannot allocate memory.");
+ return PAM_BUF_ERR;
+ }
+#endif
+ if (asprintf(&sysconf_dir, "%s%s/%s/", base_dir, SYSCONFDIR, subpath) < 0) {
+ pam_syslog(pamh, LOG_ERR, "Cannot allocate memory.");
+ free(vendor_dir);
+ return PAM_BUF_ERR;
+ }
+ } else {
+#ifdef VENDORDIR
+ if (asprintf(&vendor_dir, "%s%s/", base_dir, VENDORDIR) < 0) {
+ pam_syslog(pamh, LOG_ERR, "Cannot allocate memory.");
+ return PAM_BUF_ERR;
+ }
+#endif
+ if (asprintf(&sysconf_dir, "%s%s/", base_dir, SYSCONFDIR) < 0) {
+ pam_syslog(pamh, LOG_ERR, "Cannot allocate memory.");
+ free(vendor_dir);
+ return PAM_BUF_ERR;
}
}
- if (PAM_SUCCESS != retval && ILLEGAL_VAR != retval
- && BAD_LINE != retval && PAM_BAD_ITEM != retval) break;
-
- _clean_var(var);
-
- } /* while */
-
- (void) fclose(conf);
-
- /* tidy up */
- _clean_var(var); /* We could have got here prematurely,
- * this is safe though */
- D(("Exit."));
- return (retval != 0 ? PAM_ABORT : PAM_SUCCESS);
-}
-static int
-_parse_env_file(pam_handle_t *pamh, int ctrl, const char *file)
-{
- int retval=PAM_SUCCESS, i, t;
- char buffer[BUF_SIZE], *key, *mark;
- FILE *conf;
-
- D(("Env file name is: %s", file));
-
- if ((conf = fopen(file,"r")) == NULL) {
- pam_syslog(pamh, LOG_ERR, "Unable to open env file: %s: %m", file);
- return PAM_IGNORE;
+ D(("Read configuration from directory %s and %s", vendor_dir, sysconf_dir));
+ error = econf_readDirs (&key_file, vendor_dir, sysconf_dir, name, suffix,
+ delim, "#");
+ free(vendor_dir);
+ free(sysconf_dir);
+ if (error != ECONF_SUCCESS) {
+ pam_syslog(pamh, LOG_ERR, "Unable to read configuration in different directories: %s",
+ econf_errString(error));
+ if (error == ECONF_NOFILE)
+ return PAM_IGNORE;
+ else
+ return PAM_ABORT;
+ }
}
- while (_assemble_line(conf, buffer, BUF_SIZE) > 0) {
- D(("Read line: %s", buffer));
- key = buffer;
-
- /* skip leading white space */
- key += strspn(key, " \n\t");
-
- /* skip blanks lines and comments */
- if (key[0] == '#')
- continue;
-
- /* skip over "export " if present so we can be compat with
- bash type declarations */
- if (strncmp(key, "export ", (size_t) 7) == 0)
- key += 7;
-
- /* now find the end of value */
- mark = key;
- while(mark[0] != '\n' && mark[0] != '#' && mark[0] != '\0')
- mark++;
- if (mark[0] != '\0')
- mark[0] = '\0';
-
- /*
- * sanity check, the key must be alphanumeric
- */
-
- if (key[0] == '=') {
- pam_syslog(pamh, LOG_ERR,
- "missing key name '%s' in %s', ignoring",
- key, file);
- continue;
- }
-
- for ( i = 0 ; key[i] != '=' && key[i] != '\0' ; i++ )
- if (!isalnum(key[i]) && key[i] != '_') {
- pam_syslog(pamh, LOG_ERR,
- "non-alphanumeric key '%s' in %s', ignoring",
- key, file);
- break;
- }
- /* non-alphanumeric key, ignore this line */
- if (key[i] != '=' && key[i] != '\0')
- continue;
+ error = econf_getKeys(key_file, NULL, &key_number, &keys);
+ if (error != ECONF_SUCCESS && error != ECONF_NOKEY) {
+ pam_syslog(pamh, LOG_ERR, "Unable to read keys: %s",
+ econf_errString(error));
+ econf_freeFile(key_file);
+ return PAM_ABORT;
+ }
- /* now we try to be smart about quotes around the value,
- but not too smart, we can't get all fancy with escaped
- values like bash */
- if (key[i] == '=' && (key[++i] == '\"' || key[i] == '\'')) {
- for ( t = i+1 ; key[t] != '\0' ; t++)
- if (key[t] != '\"' && key[t] != '\'')
- key[i++] = key[t];
- else if (key[t+1] != '\0')
- key[i++] = key[t];
- key[i] = '\0';
- }
+ *lines = malloc((key_number +1)* sizeof(char**));
+ if (*lines == NULL) {
+ pam_syslog(pamh, LOG_ERR, "Cannot allocate memory.");
+ econf_free(keys);
+ econf_freeFile(key_file);
+ return PAM_BUF_ERR;
+ }
- /* if this is a request to delete a variable, check that it's
- actually set first, so we don't get a vague error back from
- pam_putenv() */
- for (i = 0; key[i] != '=' && key[i] != '\0'; i++);
+ (*lines)[key_number] = 0;
- if (key[i] == '\0' && !pam_getenv(pamh,key))
- continue;
+ for (size_t i = 0; i < key_number; i++) {
+ char *val;
- /* set the env var, if it fails, we break out of the loop */
- retval = pam_putenv(pamh, key);
- if (retval != PAM_SUCCESS) {
- D(("error setting env \"%s\"", key));
- break;
- } else if (ctrl & PAM_DEBUG_ARG) {
- pam_syslog(pamh, LOG_DEBUG,
- "pam_putenv(\"%s\")", key);
+ error = econf_getStringValue (key_file, NULL, keys[i], &val);
+ if (error != ECONF_SUCCESS) {
+ pam_syslog(pamh, LOG_ERR, "Unable to get string from key %s: %s",
+ keys[i],
+ econf_errString(error));
+ } else {
+ if (asprintf(&(*lines)[i],"%s%c%s", keys[i], delim[0], val) < 0) {
+ pam_syslog(pamh, LOG_ERR, "Cannot allocate memory.");
+ econf_free(keys);
+ econf_freeFile(key_file);
+ free_string_array(*lines);
+ free (val);
+ return PAM_BUF_ERR;
}
+ free (val);
+ }
}
- (void) fclose(conf);
-
- /* tidy up */
- D(("Exit."));
- return retval;
+ econf_free(keys);
+ econf_free(key_file);
+ return PAM_SUCCESS;
}
+#else
+
/*
* This is where we read a line of the PAM config file. The line may be
* preceded by lines of comments and also extended with "\\\n"
*/
-
-static int _assemble_line(FILE *f, char *buffer, int buf_len)
+static int
+_assemble_line(FILE *f, char *buffer, int buf_len)
{
char *p = buffer;
char *s, *os;
@@ -375,8 +364,54 @@
return used;
}
+static int read_file(const pam_handle_t *pamh, const char*filename, char ***lines)
+{
+ FILE *conf;
+ char buffer[BUF_SIZE];
+
+ D(("Parsed file name is: %s", filename));
+
+ if ((conf = fopen(filename,"r")) == NULL) {
+ pam_syslog(pamh, LOG_ERR, "Unable to open env file: %s", filename);
+ return PAM_IGNORE;
+ }
+
+ size_t i = 0;
+ *lines = malloc((i + 1)* sizeof(char**));
+ if (*lines == NULL) {
+ pam_syslog(pamh, LOG_ERR, "Cannot allocate memory.");
+ (void) fclose(conf);
+ return PAM_BUF_ERR;
+ }
+ (*lines)[i] = 0;
+ while (_assemble_line(conf, buffer, BUF_SIZE) > 0) {
+ char **tmp = NULL;
+ D(("Read line: %s", buffer));
+ tmp = realloc(*lines, (++i + 1) * sizeof(char**));
+ if (tmp == NULL) {
+ pam_syslog(pamh, LOG_ERR, "Cannot allocate memory.");
+ (void) fclose(conf);
+ free_string_array(*lines);
+ return PAM_BUF_ERR;
+ }
+ *lines = tmp;
+ (*lines)[i-1] = strdup(buffer);
+ if ((*lines)[i-1] == NULL) {
+ pam_syslog(pamh, LOG_ERR, "Cannot allocate memory.");
+ (void) fclose(conf);
+ free_string_array(*lines);
+ return PAM_BUF_ERR;
+ }
+ (*lines)[i] = 0;
+ }
+
+ (void) fclose(conf);
+ return PAM_SUCCESS;
+}
+#endif
+
static int
-_parse_line (const pam_handle_t *pamh, const char *buffer, VAR *var)
+_parse_line(const pam_handle_t *pamh, const char *buffer, VAR *var)
{
/*
* parse buffer into var, legal syntax is
@@ -471,75 +506,57 @@
return GOOD_LINE;
}
-static int _check_var(pam_handle_t *pamh, VAR *var)
+static const char *
+_pam_get_item_byname(pam_handle_t *pamh, const char *name)
{
/*
- * Examine the variable and determine what action to take.
- * Returns DEFINE_VAR, UNDEFINE_VAR depending on action to take
- * or a PAM_* error code if passed back from other routines
- *
- * if no DEFAULT provided, the empty string is assumed
- * if no OVERRIDE provided, the empty string is assumed
- * if DEFAULT= and OVERRIDE evaluates to the empty string,
- * this variable should be undefined
- * if DEFAULT="" and OVERRIDE evaluates to the empty string,
- * this variable should be defined with no value
- * if OVERRIDE=value and value turns into the empty string, DEFAULT is used
- *
- * If DEFINE_VAR is to be returned, the correct value to define will
- * be pointed to by var->value
+ * This function just allows me to use names as given in the config
+ * file and translate them into the appropriate PAM_ITEM macro
*/
- int retval;
+ int item;
+ const void *itemval;
D(("Called."));
-
- /*
- * First thing to do is to expand any arguments, but only
- * if they are not the special quote values (cause expand_arg
- * changes memory).
- */
-
- if (var->defval && (&quote != var->defval) &&
- ((retval = _expand_arg(pamh, &(var->defval))) != PAM_SUCCESS)) {
- return retval;
- }
- if (var->override && (&quote != var->override) &&
- ((retval = _expand_arg(pamh, &(var->override))) != PAM_SUCCESS)) {
- return retval;
+ if (strcmp(name, "PAM_USER") == 0 || strcmp(name, "HOME") == 0 || strcmp(name, "SHELL") == 0) {
+ item = PAM_USER;
+ } else if (strcmp(name, "PAM_USER_PROMPT") == 0) {
+ item = PAM_USER_PROMPT;
+ } else if (strcmp(name, "PAM_TTY") == 0) {
+ item = PAM_TTY;
+ } else if (strcmp(name, "PAM_RUSER") == 0) {
+ item = PAM_RUSER;
+ } else if (strcmp(name, "PAM_RHOST") == 0) {
+ item = PAM_RHOST;
+ } else {
+ D(("Unknown PAM_ITEM: <%s>", name));
+ pam_syslog (pamh, LOG_ERR, "Unknown PAM_ITEM: <%s>", name);
+ return NULL;
}
- /* Now its easy */
-
- if (var->override && *(var->override)) {
- /* if there is a non-empty string in var->override, we use it */
- D(("OVERRIDE variable <%s> being used: <%s>", var->name, var->override));
- var->value = var->override;
- retval = DEFINE_VAR;
- } else {
+ if (pam_get_item(pamh, item, &itemval) != PAM_SUCCESS) {
+ D(("pam_get_item failed"));
+ return NULL; /* let pam_get_item() log the error */
+ }
- var->value = var->defval;
- if (&quote == var->defval) {
- /*
- * This means that the empty string was given for defval value
- * which indicates that a variable should be defined with no value
- */
- D(("An empty variable: <%s>", var->name));
- retval = DEFINE_VAR;
- } else if (var->defval) {
- D(("DEFAULT variable <%s> being used: <%s>", var->name, var->defval));
- retval = DEFINE_VAR;
- } else {
- D(("UNDEFINE variable <%s>", var->name));
- retval = UNDEFINE_VAR;
+ if (itemval && (strcmp(name, "HOME") == 0 || strcmp(name, "SHELL") == 0)) {
+ struct passwd *user_entry;
+ user_entry = pam_modutil_getpwnam (pamh, itemval);
+ if (!user_entry) {
+ pam_syslog(pamh, LOG_ERR, "No such user!?");
+ return NULL;
}
+ return (strcmp(name, "SHELL") == 0) ?
+ user_entry->pw_shell :
+ user_entry->pw_dir;
}
D(("Exit."));
- return retval;
+ return itemval;
}
-static int _expand_arg(pam_handle_t *pamh, char **value)
+static int
+_expand_arg(pam_handle_t *pamh, char **value)
{
const char *orig=*value, *tmpptr=NULL;
char *ptr; /*
@@ -679,55 +696,96 @@
return PAM_SUCCESS;
}
-static const char * _pam_get_item_byname(pam_handle_t *pamh, const char *name)
+static int
+_check_var(pam_handle_t *pamh, VAR *var)
{
/*
- * This function just allows me to use names as given in the config
- * file and translate them into the appropriate PAM_ITEM macro
+ * Examine the variable and determine what action to take.
+ * Returns DEFINE_VAR, UNDEFINE_VAR depending on action to take
+ * or a PAM_* error code if passed back from other routines
+ *
+ * if no DEFAULT provided, the empty string is assumed
+ * if no OVERRIDE provided, the empty string is assumed
+ * if DEFAULT= and OVERRIDE evaluates to the empty string,
+ * this variable should be undefined
+ * if DEFAULT="" and OVERRIDE evaluates to the empty string,
+ * this variable should be defined with no value
+ * if OVERRIDE=value and value turns into the empty string, DEFAULT is used
+ *
+ * If DEFINE_VAR is to be returned, the correct value to define will
+ * be pointed to by var->value
*/
- int item;
- const void *itemval;
+ int retval;
D(("Called."));
- if (strcmp(name, "PAM_USER") == 0 || strcmp(name, "HOME") == 0 || strcmp(name, "SHELL") == 0) {
- item = PAM_USER;
- } else if (strcmp(name, "PAM_USER_PROMPT") == 0) {
- item = PAM_USER_PROMPT;
- } else if (strcmp(name, "PAM_TTY") == 0) {
- item = PAM_TTY;
- } else if (strcmp(name, "PAM_RUSER") == 0) {
- item = PAM_RUSER;
- } else if (strcmp(name, "PAM_RHOST") == 0) {
- item = PAM_RHOST;
- } else {
- D(("Unknown PAM_ITEM: <%s>", name));
- pam_syslog (pamh, LOG_ERR, "Unknown PAM_ITEM: <%s>", name);
- return NULL;
- }
- if (pam_get_item(pamh, item, &itemval) != PAM_SUCCESS) {
- D(("pam_get_item failed"));
- return NULL; /* let pam_get_item() log the error */
+ /*
+ * First thing to do is to expand any arguments, but only
+ * if they are not the special quote values (cause expand_arg
+ * changes memory).
+ */
+
+ if (var->defval && (&quote != var->defval) &&
+ ((retval = _expand_arg(pamh, &(var->defval))) != PAM_SUCCESS)) {
+ return retval;
+ }
+ if (var->override && (&quote != var->override) &&
+ ((retval = _expand_arg(pamh, &(var->override))) != PAM_SUCCESS)) {
+ return retval;
}
- if (itemval && (strcmp(name, "HOME") == 0 || strcmp(name, "SHELL") == 0)) {
- struct passwd *user_entry;
- user_entry = pam_modutil_getpwnam (pamh, itemval);
- if (!user_entry) {
- pam_syslog(pamh, LOG_ERR, "No such user!?");
- return NULL;
+ /* Now its easy */
+
+ if (var->override && *(var->override)) {
+ /* if there is a non-empty string in var->override, we use it */
+ D(("OVERRIDE variable <%s> being used: <%s>", var->name, var->override));
+ var->value = var->override;
+ retval = DEFINE_VAR;
+ } else {
+
+ var->value = var->defval;
+ if (&quote == var->defval) {
+ /*
+ * This means that the empty string was given for defval value
+ * which indicates that a variable should be defined with no value
+ */
+ D(("An empty variable: <%s>", var->name));
+ retval = DEFINE_VAR;
+ } else if (var->defval) {
+ D(("DEFAULT variable <%s> being used: <%s>", var->name, var->defval));
+ retval = DEFINE_VAR;
+ } else {
+ D(("UNDEFINE variable <%s>", var->name));
+ retval = UNDEFINE_VAR;
}
- return (strcmp(name, "SHELL") == 0) ?
- user_entry->pw_shell :
- user_entry->pw_dir;
}
D(("Exit."));
- return itemval;
+ return retval;
}
-static int _define_var(pam_handle_t *pamh, int ctrl, VAR *var)
+static void
+_clean_var(VAR *var)
+{
+ if (var->name) {
+ free(var->name);
+ }
+ if (var->defval && (&quote != var->defval)) {
+ free(var->defval);
+ }
+ if (var->override && (&quote != var->override)) {
+ free(var->override);
+ }
+ var->name = NULL;
+ var->value = NULL; /* never has memory specific to it */
+ var->defval = NULL;
+ var->override = NULL;
+ return;
+}
+
+static int
+_define_var(pam_handle_t *pamh, int ctrl, VAR *var)
{
/* We have a variable to define, this is a simple function */
@@ -749,7 +807,8 @@
return retval;
}
-static int _undefine_var(pam_handle_t *pamh, int ctrl, VAR *var)
+static int
+_undefine_var(pam_handle_t *pamh, int ctrl, VAR *var)
{
/* We have a variable to undefine, this is a simple function */
@@ -760,25 +819,176 @@
return pam_putenv(pamh, var->name);
}
-static void _clean_var(VAR *var)
+static int
+_parse_config_file(pam_handle_t *pamh, int ctrl, const char *file)
{
- if (var->name) {
- free(var->name);
- }
- if (var->defval && (&quote != var->defval)) {
- free(var->defval);
- }
- if (var->override && (&quote != var->override)) {
- free(var->override);
- }
- var->name = NULL;
- var->value = NULL; /* never has memory specific to it */
- var->defval = NULL;
- var->override = NULL;
- return;
+ int retval;
+ VAR Var, *var=&Var;
+ char **conf_list = NULL;
+
+ var->name=NULL; var->defval=NULL; var->override=NULL;
+
+ D(("Called."));
+
+#ifdef USE_ECONF
+ /* If "file" is not NULL, only this file will be parsed. */
+ retval = econf_read_file(pamh, file, " \t", PAM_ENV, ".conf", "security", &conf_list);
+#else
+ /* Only one file will be parsed. So, file has to be set. */
+ if (file == NULL) /* No filename has been set via argv. */
+ file = DEFAULT_CONF_FILE;
+#ifdef VENDOR_DEFAULT_CONF_FILE
+ /*
+ * Check whether file is available.
+ * If it does not exist, fall back to VENDOR_DEFAULT_CONF_FILE file.
+ */
+ struct stat stat_buffer;
+ if (stat(file, &stat_buffer) != 0 && errno == ENOENT) {
+ file = VENDOR_DEFAULT_CONF_FILE;
+ }
+#endif
+ retval = read_file(pamh, file, &conf_list);
+#endif
+
+ if (retval != PAM_SUCCESS)
+ return retval;
+
+ for (char **conf = conf_list; *conf != NULL; ++conf) {
+ if ((retval = _parse_line(pamh, *conf, var)) == GOOD_LINE) {
+ retval = _check_var(pamh, var);
+
+ if (DEFINE_VAR == retval) {
+ retval = _define_var(pamh, ctrl, var);
+
+ } else if (UNDEFINE_VAR == retval) {
+ retval = _undefine_var(pamh, ctrl, var);
+ }
+ }
+ if (PAM_SUCCESS != retval && ILLEGAL_VAR != retval
+ && BAD_LINE != retval && PAM_BAD_ITEM != retval) break;
+
+ _clean_var(var);
+
+ } /* for */
+
+ /* tidy up */
+ free_string_array(conf_list);
+ _clean_var(var); /* We could have got here prematurely,
+ * this is safe though */
+ D(("Exit."));
+ return (retval != 0 ? PAM_ABORT : PAM_SUCCESS);
}
+static int
+_parse_env_file(pam_handle_t *pamh, int ctrl, const char *file)
+{
+ int retval=PAM_SUCCESS, i, t;
+ char *key, *mark;
+ char **env_list = NULL;
+
+#ifdef USE_ECONF
+ retval = econf_read_file(pamh, file, "=", ENVIRONMENT, "", "", &env_list);
+#else
+ /* Only one file will be parsed. So, file has to be set. */
+ if (file == NULL) /* No filename has been set via argv. */
+ file = DEFAULT_ETC_ENVFILE;
+#ifdef VENDOR_DEFAULT_ETC_ENVFILE
+ /*
+ * Check whether file is available.
+ * If it does not exist, fall back to VENDOR_DEFAULT_ETC_ENVFILE; file.
+ */
+ struct stat stat_buffer;
+ if (stat(file, &stat_buffer) != 0 && errno == ENOENT) {
+ file = VENDOR_DEFAULT_ETC_ENVFILE;
+ }
+#endif
+ retval = read_file(pamh, file, &env_list);
+#endif
+
+ if (retval != PAM_SUCCESS)
+ return retval == PAM_IGNORE ? PAM_SUCCESS : retval;
+
+ for (char **env = env_list; *env != NULL; ++env) {
+ key = *env;
+
+ /* skip leading white space */
+ key += strspn(key, " \n\t");
+
+ /* skip blanks lines and comments */
+ if (key[0] == '#')
+ continue;
+
+ /* skip over "export " if present so we can be compat with
+ bash type declarations */
+ if (strncmp(key, "export ", (size_t) 7) == 0)
+ key += 7;
+
+ /* now find the end of value */
+ mark = key;
+ while(mark[0] != '\n' && mark[0] != '#' && mark[0] != '\0')
+ mark++;
+ if (mark[0] != '\0')
+ mark[0] = '\0';
+
+ /*
+ * sanity check, the key must be alphanumeric
+ */
+
+ if (key[0] == '=') {
+ pam_syslog(pamh, LOG_ERR,
+ "missing key name '%s' in %s', ignoring",
+ key, file);
+ continue;
+ }
+
+ for ( i = 0 ; key[i] != '=' && key[i] != '\0' ; i++ )
+ if (!isalnum(key[i]) && key[i] != '_') {
+ pam_syslog(pamh, LOG_ERR,
+ "non-alphanumeric key '%s' in %s', ignoring",
+ key, file);
+ break;
+ }
+ /* non-alphanumeric key, ignore this line */
+ if (key[i] != '=' && key[i] != '\0')
+ continue;
+
+ /* now we try to be smart about quotes around the value,
+ but not too smart, we can't get all fancy with escaped
+ values like bash */
+ if (key[i] == '=' && (key[++i] == '\"' || key[i] == '\'')) {
+ for ( t = i+1 ; key[t] != '\0' ; t++)
+ if (key[t] != '\"' && key[t] != '\'')
+ key[i++] = key[t];
+ else if (key[t+1] != '\0')
+ key[i++] = key[t];
+ key[i] = '\0';
+ }
+
+ /* if this is a request to delete a variable, check that it's
+ actually set first, so we don't get a vague error back from
+ pam_putenv() */
+ for (i = 0; key[i] != '=' && key[i] != '\0'; i++);
+
+ if (key[i] == '\0' && !pam_getenv(pamh,key))
+ continue;
+
+ /* set the env var, if it fails, we break out of the loop */
+ retval = pam_putenv(pamh, key);
+ if (retval != PAM_SUCCESS) {
+ D(("error setting env \"%s\"", key));
+ break;
+ } else if (ctrl & PAM_DEBUG_ARG) {
+ pam_syslog(pamh, LOG_DEBUG,
+ "pam_putenv(\"%s\")", key);
+ }
+ free(*env);
+ }
+ /* tidy up */
+ free(env_list);
+ D(("Exit."));
+ return retval;
+}
/* --- authentication management functions (only) --- */
diff -Naur org/modules/pam_env/pam_env.conf.5.xml patch/modules/pam_env/pam_env.conf.5.xml
--- org/modules/pam_env/pam_env.conf.5.xml 2022-10-11 12:35:53.574193223 +0200
+++ patch/modules/pam_env/pam_env.conf.5.xml 2022-10-11 12:36:32.518192985 +0200
@@ -20,7 +20,15 @@
<refsect1 id='pam_env.conf-description'>
<title>DESCRIPTION</title>
- <para>
+ <para condition="with_vendordir">
+ The <filename>/usr/etc/security/pam_env.conf</filename> and
+ <filename>/etc/security/pam_env.conf</filename> files specify
+ the environment variables to be set, unset or modified by
+ <citerefentry><refentrytitle>pam_env</refentrytitle><manvolnum>8</manvolnum></citerefentry>.
+ When someone logs in, these files are read and the environment
+ variables are set according.
+ </para>
+ <para condition="without_vendordir">
The <filename>/etc/security/pam_env.conf</filename> file specifies
the environment variables to be set, unset or modified by
<citerefentry><refentrytitle>pam_env</refentrytitle><manvolnum>8</manvolnum></citerefentry>.
@@ -61,7 +69,15 @@
at front) can be used to mark this line as a comment line.
</para>
- <para>
+ <para condition="with_vendordir">
+ The <filename>/usr/etc/environment</filename> and <filename>/etc/environment</filename> files specify
+ the environment variables to be set. These files must consist of simple
+ <emphasis>NAME=VALUE</emphasis> pairs on separate lines.
+ The <citerefentry><refentrytitle>pam_env</refentrytitle><manvolnum>8</manvolnum></citerefentry>
+ module will read these files after the <filename>pam_env.conf</filename>
+ file.
+ </para>
+ <para condition="without_vendordir">
The <filename>/etc/environment</filename> file specifies
the environment variables to be set. The file must consist of simple
<emphasis>NAME=VALUE</emphasis> pairs on separate lines.