summaryrefslogtreecommitdiff
path: root/modules
diff options
context:
space:
mode:
Diffstat (limited to 'modules')
-rw-r--r--modules/pam_access/Makefile.am4
-rw-r--r--modules/pam_access/pam_access.c68
-rw-r--r--modules/pam_cracklib/pam_cracklib.c38
-rw-r--r--modules/pam_echo/pam_echo.c5
-rw-r--r--modules/pam_env/pam_env.c45
-rw-r--r--modules/pam_filter/pam_filter.c4
-rw-r--r--modules/pam_group/pam_group.c281
-rw-r--r--modules/pam_lastlog/pam_lastlog.c6
-rw-r--r--modules/pam_limits/limits.conf.5.xml42
-rw-r--r--modules/pam_limits/pam_limits.8.xml25
-rw-r--r--modules/pam_limits/pam_limits.c367
-rw-r--r--modules/pam_listfile/pam_listfile.c13
-rw-r--r--modules/pam_loginuid/pam_loginuid.c3
-rw-r--r--modules/pam_namespace/argv_parse.c29
-rw-r--r--modules/pam_namespace/md5.c2
-rw-r--r--modules/pam_namespace/pam_namespace.8.xml21
-rw-r--r--modules/pam_namespace/pam_namespace.c105
-rw-r--r--modules/pam_namespace/pam_namespace.h9
-rw-r--r--modules/pam_nologin/pam_nologin.8.xml2
-rw-r--r--modules/pam_pwhistory/opasswd.c57
-rw-r--r--modules/pam_pwhistory/pam_pwhistory.8.xml4
-rw-r--r--modules/pam_pwhistory/pam_pwhistory.c21
-rw-r--r--modules/pam_securetty/pam_securetty.8.xml18
-rw-r--r--modules/pam_securetty/pam_securetty.c70
-rw-r--r--modules/pam_selinux/pam_selinux.c52
-rw-r--r--modules/pam_sepermit/pam_sepermit.c12
-rw-r--r--modules/pam_stress/pam_stress.c11
-rw-r--r--modules/pam_tally2/pam_tally2.8.xml12
-rw-r--r--modules/pam_time/pam_time.c273
-rw-r--r--modules/pam_timestamp/pam_timestamp.c6
-rw-r--r--modules/pam_unix/Makefile.am9
-rw-r--r--modules/pam_unix/md5.c2
-rw-r--r--modules/pam_unix/pam_unix_passwd.c48
-rw-r--r--modules/pam_unix/support.c37
-rw-r--r--modules/pam_unix/yppasswd_xdr.c4
-rw-r--r--modules/pam_xauth/pam_xauth.c45
36 files changed, 1191 insertions, 559 deletions
diff --git a/modules/pam_access/Makefile.am b/modules/pam_access/Makefile.am
index b4fea7df..89222b56 100644
--- a/modules/pam_access/Makefile.am
+++ b/modules/pam_access/Makefile.am
@@ -15,14 +15,14 @@ securelibdir = $(SECUREDIR)
secureconfdir = $(SCONFIGDIR)
AM_CFLAGS = -I$(top_srcdir)/libpam/include -I$(top_srcdir)/libpamc/include \
- -DPAM_ACCESS_CONFIG=\"$(SCONFIGDIR)/access.conf\"
+ -DPAM_ACCESS_CONFIG=\"$(SCONFIGDIR)/access.conf\" $(NIS_CFLAGS)
AM_LDFLAGS = -no-undefined -avoid-version -module
if HAVE_VERSIONING
AM_LDFLAGS += -Wl,--version-script=$(srcdir)/../modules.map
endif
securelib_LTLIBRARIES = pam_access.la
-pam_access_la_LIBADD = -L$(top_builddir)/libpam -lpam @LIBNSL@
+pam_access_la_LIBADD = -L$(top_builddir)/libpam -lpam $(NIS_LIBS)
secureconf_DATA = access.conf
diff --git a/modules/pam_access/pam_access.c b/modules/pam_access/pam_access.c
index daee47da..2669a5ec 100644
--- a/modules/pam_access/pam_access.c
+++ b/modules/pam_access/pam_access.c
@@ -106,6 +106,8 @@ struct login_info {
const char *fs; /* field separator */
const char *sep; /* list-element separator */
int from_remote_host; /* If PAM_RHOST was used for from */
+ struct addrinfo *res; /* Cached DNS resolution of from */
+ int gai_rv; /* Cached retval of getaddrinfo */
};
/* Parse module config arguments */
@@ -168,7 +170,7 @@ static int user_match (pam_handle_t *, char *, struct login_info *);
static int group_match (pam_handle_t *, const char *, const char *, int);
static int from_match (pam_handle_t *, char *, struct login_info *);
static int string_match (pam_handle_t *, const char *, const char *, int);
-static int network_netmask_match (pam_handle_t *, const char *, const char *, int);
+static int network_netmask_match (pam_handle_t *, const char *, const char *, struct login_info *);
/* isipaddr - find out if string provided is an IP address or not */
@@ -476,12 +478,10 @@ netgroup_match (pam_handle_t *pamh, const char *netgroup,
if (getdomainname (domainname_res, sizeof (domainname_res)) == 0)
{
- if (strcmp (domainname_res, "(none)") == 0)
+ if (domainname_res[0] != '\0' && strcmp (domainname_res, "(none)") != 0)
{
- /* If domainname is not set, some systems will return "(none)" */
- domainname_res[0] = '\0';
- }
- mydomain = domainname_res;
+ mydomain = domainname_res;
+ }
}
#endif
@@ -521,14 +521,25 @@ user_match (pam_handle_t *pamh, char *tok, struct login_info *item)
* name of the user's primary group.
*/
- if (tok[0] != '@' && (at = strchr(tok + 1, '@')) != 0) {
+ /* Try to split on a pattern (@*[^@]+)(@+.*) */
+ for (at = tok; *at == '@'; ++at);
+
+ if ((at = strchr(at, '@')) != NULL) {
/* split user@host pattern */
if (item->hostname == NULL)
return NO;
+ memcpy (&fake_item, item, sizeof(fake_item));
fake_item.from = item->hostname;
+ fake_item.gai_rv = 0;
+ fake_item.res = NULL;
+ fake_item.from_remote_host = 1; /* hostname should be resolvable */
*at = 0;
- return (user_match (pamh, tok, item) &&
- from_match (pamh, at + 1, &fake_item));
+ if (!user_match (pamh, tok, item))
+ return NO;
+ rv = from_match (pamh, at + 1, &fake_item);
+ if (fake_item.gai_rv == 0 && fake_item.res)
+ freeaddrinfo(fake_item.res);
+ return rv;
} else if (tok[0] == '@') { /* netgroup */
const char *hostname = NULL;
if (tok[1] == '@') { /* add hostname to netgroup match */
@@ -612,22 +623,24 @@ from_match (pam_handle_t *pamh UNUSED, char *tok, struct login_info *item)
if ((str_len = strlen(string)) > (tok_len = strlen(tok))
&& strcasecmp(tok, string + str_len - tok_len) == 0)
return (YES);
- } else if (strcasecmp(tok, "LOCAL") == 0) { /* local: no PAM_RHOSTS */
- if (item->from_remote_host == 0)
+ } else if (item->from_remote_host == 0) { /* local: no PAM_RHOSTS */
+ if (strcasecmp(tok, "LOCAL") == 0)
return (YES);
} else if (tok[(tok_len = strlen(tok)) - 1] == '.') {
- struct addrinfo *res;
struct addrinfo hint;
memset (&hint, '\0', sizeof (hint));
hint.ai_flags = AI_CANONNAME;
hint.ai_family = AF_INET;
- if (getaddrinfo (string, NULL, &hint, &res) != 0)
+ if (item->gai_rv != 0)
+ return NO;
+ else if (!item->res &&
+ (item->gai_rv = getaddrinfo (string, NULL, &hint, &item->res)) != 0)
return NO;
else
{
- struct addrinfo *runp = res;
+ struct addrinfo *runp = item->res;
while (runp != NULL)
{
@@ -643,17 +656,15 @@ from_match (pam_handle_t *pamh UNUSED, char *tok, struct login_info *item)
if (strncmp(tok, buf, tok_len) == 0)
{
- freeaddrinfo (res);
return YES;
}
}
runp = runp->ai_next;
}
- freeaddrinfo (res);
}
} else {
/* Assume network/netmask with a IP of a host. */
- if (network_netmask_match(pamh, tok, string, item->debug))
+ if (network_netmask_match(pamh, tok, string, item))
return YES;
}
@@ -696,13 +707,13 @@ string_match (pam_handle_t *pamh, const char *tok, const char *string,
*/
static int
network_netmask_match (pam_handle_t *pamh,
- const char *tok, const char *string, int debug)
+ const char *tok, const char *string, struct login_info *item)
{
char *netmask_ptr;
char netmask_string[MAXHOSTNAMELEN + 1];
int addr_type;
- if (debug)
+ if (item->debug)
pam_syslog (pamh, LOG_DEBUG,
"network_netmask_match: tok=%s, item=%s", tok, string);
/* OK, check if tok is of type addr/mask */
@@ -747,18 +758,20 @@ network_netmask_match (pam_handle_t *pamh,
if (isipaddr(string, NULL, NULL) != YES)
{
/* Assume network/netmask with a name of a host. */
- struct addrinfo *res;
struct addrinfo hint;
memset (&hint, '\0', sizeof (hint));
hint.ai_flags = AI_CANONNAME;
hint.ai_family = AF_UNSPEC;
- if (getaddrinfo (string, NULL, &hint, &res) != 0)
+ if (item->gai_rv != 0)
+ return NO;
+ else if (!item->res &&
+ (item->gai_rv = getaddrinfo (string, NULL, &hint, &item->res)) != 0)
return NO;
else
{
- struct addrinfo *runp = res;
+ struct addrinfo *runp = item->res;
while (runp != NULL)
{
@@ -772,12 +785,10 @@ network_netmask_match (pam_handle_t *pamh,
if (are_addresses_equal(buf, tok, netmask_ptr))
{
- freeaddrinfo (res);
return YES;
}
runp = runp->ai_next;
}
- freeaddrinfo (res);
}
}
else
@@ -799,6 +810,7 @@ pam_sm_authenticate (pam_handle_t *pamh, int flags UNUSED,
const char *from;
struct passwd *user_pw;
char hostname[MAXHOSTNAMELEN + 1];
+ int rv;
/* set username */
@@ -815,6 +827,7 @@ pam_sm_authenticate (pam_handle_t *pamh, int flags UNUSED,
/*
* Bundle up the arguments to avoid unnecessary clumsiness later on.
*/
+ memset(&loginfo, '\0', sizeof(loginfo));
loginfo.user = user_pw;
loginfo.config_file = PAM_ACCESS_CONFIG;
@@ -885,7 +898,12 @@ pam_sm_authenticate (pam_handle_t *pamh, int flags UNUSED,
loginfo.hostname = NULL;
}
- if (login_access(pamh, &loginfo)) {
+ rv = login_access(pamh, &loginfo);
+
+ if (loginfo.gai_rv == 0 && loginfo.res)
+ freeaddrinfo(loginfo.res);
+
+ if (rv) {
return (PAM_SUCCESS);
} else {
pam_syslog(pamh, LOG_ERR,
diff --git a/modules/pam_cracklib/pam_cracklib.c b/modules/pam_cracklib/pam_cracklib.c
index 2e911261..1955b83f 100644
--- a/modules/pam_cracklib/pam_cracklib.c
+++ b/modules/pam_cracklib/pam_cracklib.c
@@ -473,6 +473,9 @@ static char * str_lower(char *string)
{
char *cp;
+ if (!string)
+ return NULL;
+
for (cp = string; *cp; cp++)
*cp = tolower(*cp);
return string;
@@ -492,15 +495,26 @@ static const char *password_check(struct cracklib_options *opt,
}
newmono = str_lower(x_strdup(new));
+ if (!newmono)
+ msg = _("memory allocation error");
+
usermono = str_lower(x_strdup(user));
- if (old) {
- oldmono = str_lower(x_strdup(old));
- wrapped = malloc(strlen(oldmono) * 2 + 1);
- strcpy (wrapped, oldmono);
- strcat (wrapped, oldmono);
+ if (!usermono)
+ msg = _("memory allocation error");
+
+ if (!msg && old) {
+ oldmono = str_lower(x_strdup(old));
+ if (oldmono)
+ wrapped = malloc(strlen(oldmono) * 2 + 1);
+ if (wrapped) {
+ strcpy (wrapped, oldmono);
+ strcat (wrapped, oldmono);
+ } else {
+ msg = _("memory allocation error");
+ }
}
- if (palindrome(newmono))
+ if (!msg && palindrome(newmono))
msg = _("is a palindrome");
if (!msg && oldmono && strcmp(oldmono, newmono) == 0)
@@ -524,13 +538,17 @@ static const char *password_check(struct cracklib_options *opt,
if (!msg && usercheck(opt, newmono, usermono))
msg = _("contains the user name in some form");
- memset(newmono, 0, strlen(newmono));
- free(newmono);
free(usermono);
- if (old) {
+ if (newmono) {
+ memset(newmono, 0, strlen(newmono));
+ free(newmono);
+ }
+ if (oldmono) {
memset(oldmono, 0, strlen(oldmono));
- memset(wrapped, 0, strlen(wrapped));
free(oldmono);
+ }
+ if (wrapped) {
+ memset(wrapped, 0, strlen(wrapped));
free(wrapped);
}
diff --git a/modules/pam_echo/pam_echo.c b/modules/pam_echo/pam_echo.c
index 31ebca22..043ff703 100644
--- a/modules/pam_echo/pam_echo.c
+++ b/modules/pam_echo/pam_echo.c
@@ -119,7 +119,10 @@ replace_and_print (pam_handle_t *pamh, const char *mesg)
str = &myhostname;
}
else
- pam_get_item (pamh, item, &str);
+ {
+ if (pam_get_item (pamh, item, &str) != PAM_SUCCESS)
+ str = NULL;
+ }
if (str == NULL)
str = "(null)";
for (q = str; *q != '\0' && len < length - 1; ++q)
diff --git a/modules/pam_env/pam_env.c b/modules/pam_env/pam_env.c
index 8ac8ed33..e04f5b53 100644
--- a/modules/pam_env/pam_env.c
+++ b/modules/pam_env/pam_env.c
@@ -68,8 +68,8 @@ static int _check_var(pam_handle_t *, VAR *); /* This is the real mea
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 *, VAR *);
-static int _undefine_var(pam_handle_t *, VAR *);
+static int _define_var(pam_handle_t *, int, VAR *);
+static int _undefine_var(pam_handle_t *, int, VAR *);
/* This is a flag used to designate an empty string */
static char quote='Z';
@@ -99,7 +99,7 @@ _pam_parse (const pam_handle_t *pamh, int argc, const char **argv,
if (!strcmp(*argv,"debug"))
ctrl |= PAM_DEBUG_ARG;
else if (!strncmp(*argv,"conffile=",9)) {
- if (*argv+9 == '\0') {
+ if ((*argv)[9] == '\0') {
pam_syslog(pamh, LOG_ERR,
"conffile= specification missing argument - ignored");
} else {
@@ -107,7 +107,7 @@ _pam_parse (const pam_handle_t *pamh, int argc, const char **argv,
D(("new Configuration File: %s", *conffile));
}
} else if (!strncmp(*argv,"envfile=",8)) {
- if (*argv+8 == '\0') {
+ if ((*argv)[8] == '\0') {
pam_syslog (pamh, LOG_ERR,
"envfile= specification missing argument - ignored");
} else {
@@ -115,7 +115,7 @@ _pam_parse (const pam_handle_t *pamh, int argc, const char **argv,
D(("new Env File: %s", *envfile));
}
} else if (!strncmp(*argv,"user_envfile=",13)) {
- if (*argv+13 == '\0') {
+ if ((*argv)[13] == '\0') {
pam_syslog (pamh, LOG_ERR,
"user_envfile= specification missing argument - ignored");
} else {
@@ -134,7 +134,7 @@ _pam_parse (const pam_handle_t *pamh, int argc, const char **argv,
}
static int
-_parse_config_file(pam_handle_t *pamh, const char *file)
+_parse_config_file(pam_handle_t *pamh, int ctrl, const char *file)
{
int retval;
char buffer[BUF_SIZE];
@@ -168,10 +168,10 @@ _parse_config_file(pam_handle_t *pamh, const char *file)
retval = _check_var(pamh, var);
if (DEFINE_VAR == retval) {
- retval = _define_var(pamh, var);
+ retval = _define_var(pamh, ctrl, var);
} else if (UNDEFINE_VAR == retval) {
- retval = _undefine_var(pamh, var);
+ retval = _undefine_var(pamh, ctrl, var);
}
}
if (PAM_SUCCESS != retval && ILLEGAL_VAR != retval
@@ -191,7 +191,7 @@ _parse_config_file(pam_handle_t *pamh, const char *file)
}
static int
-_parse_env_file(pam_handle_t *pamh, const char *file)
+_parse_env_file(pam_handle_t *pamh, int ctrl, const char *file)
{
int retval=PAM_SUCCESS, i, t;
char buffer[BUF_SIZE], *key, *mark;
@@ -267,6 +267,9 @@ _parse_env_file(pam_handle_t *pamh, const char *file)
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);
}
}
@@ -287,6 +290,7 @@ static int _assemble_line(FILE *f, char *buffer, int buf_len)
char *p = buffer;
char *s, *os;
int used = 0;
+ int whitespace;
/* loop broken with a 'break' when a non-'\\n' ended line is read */
@@ -309,8 +313,10 @@ static int _assemble_line(FILE *f, char *buffer, int buf_len)
/* skip leading spaces --- line may be blank */
- s = p + strspn(p, " \n\t");
+ whitespace = strspn(p, " \n\t");
+ s = p + whitespace;
if (*s && (*s != '#')) {
+ used += whitespace;
os = s;
/*
@@ -564,6 +570,7 @@ static int _expand_arg(pam_handle_t *pamh, char **value)
D(("Variable buffer overflow: <%s> + <%s>", tmp, tmpptr));
pam_syslog (pamh, LOG_ERR, "Variable buffer overflow: <%s> + <%s>",
tmp, tmpptr);
+ return PAM_BUF_ERR;
}
continue;
}
@@ -625,6 +632,7 @@ static int _expand_arg(pam_handle_t *pamh, char **value)
D(("Variable buffer overflow: <%s> + <%s>", tmp, tmpptr));
pam_syslog (pamh, LOG_ERR,
"Variable buffer overflow: <%s> + <%s>", tmp, tmpptr);
+ return PAM_BUF_ERR;
}
}
} /* if ('{' != *orig++) */
@@ -636,6 +644,7 @@ static int _expand_arg(pam_handle_t *pamh, char **value)
D(("Variable buffer overflow: <%s> + <%s>", tmp, tmpptr));
pam_syslog(pamh, LOG_ERR,
"Variable buffer overflow: <%s> + <%s>", tmp, tmpptr);
+ return PAM_BUF_ERR;
}
}
} /* for (;*orig;) */
@@ -691,7 +700,7 @@ static const char * _pam_get_item_byname(pam_handle_t *pamh, const char *name)
return itemval;
}
-static int _define_var(pam_handle_t *pamh, VAR *var)
+static int _define_var(pam_handle_t *pamh, int ctrl, VAR *var)
{
/* We have a variable to define, this is a simple function */
@@ -705,16 +714,22 @@ static int _define_var(pam_handle_t *pamh, VAR *var)
}
retval = pam_putenv(pamh, envvar);
+ if (ctrl & PAM_DEBUG_ARG) {
+ pam_syslog(pamh, LOG_DEBUG, "pam_putenv(\"%s\")", envvar);
+ }
_pam_drop(envvar);
D(("Exit."));
return retval;
}
-static int _undefine_var(pam_handle_t *pamh, 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 */
D(("Called and exit."));
+ if (ctrl & PAM_DEBUG_ARG) {
+ pam_syslog(pamh, LOG_DEBUG, "remove variable \"%s\"", var->name);
+ }
return pam_putenv(pamh, var->name);
}
@@ -762,10 +777,10 @@ handle_env (pam_handle_t *pamh, int argc, const char **argv)
ctrl = _pam_parse(pamh, argc, argv, &conf_file, &env_file,
&readenv, &user_env_file, &user_readenv);
- retval = _parse_config_file(pamh, conf_file);
+ retval = _parse_config_file(pamh, ctrl, conf_file);
if(readenv && retval == PAM_SUCCESS) {
- retval = _parse_env_file(pamh, env_file);
+ retval = _parse_env_file(pamh, ctrl, env_file);
if (retval == PAM_IGNORE)
retval = PAM_SUCCESS;
}
@@ -795,7 +810,7 @@ handle_env (pam_handle_t *pamh, int argc, const char **argv)
if (pam_modutil_drop_priv(pamh, &privs, user_entry)) {
retval = PAM_SESSION_ERR;
} else {
- retval = _parse_config_file(pamh, envpath);
+ retval = _parse_config_file(pamh, ctrl, envpath);
if (pam_modutil_regain_priv(pamh, &privs))
retval = PAM_SESSION_ERR;
}
diff --git a/modules/pam_filter/pam_filter.c b/modules/pam_filter/pam_filter.c
index 41028902..57453b82 100644
--- a/modules/pam_filter/pam_filter.c
+++ b/modules/pam_filter/pam_filter.c
@@ -177,8 +177,8 @@ static int process_args(pam_handle_t *pamh
#define USER_OFFSET 5 /* strlen('USER='); */
#define USER_NAME "USER="
- pam_get_user(pamh, &user, NULL);
- if (user == NULL) {
+ if (pam_get_user(pamh, &user, NULL) != PAM_SUCCESS ||
+ user == NULL) {
user = "<unknown>";
}
size = USER_OFFSET+strlen(user);
diff --git a/modules/pam_group/pam_group.c b/modules/pam_group/pam_group.c
index 310b2622..be5f20f3 100644
--- a/modules/pam_group/pam_group.c
+++ b/modules/pam_group/pam_group.c
@@ -2,6 +2,7 @@
/*
* Written by Andrew Morgan <morgan@linux.kernel.org> 1996/7/6
+ * Field parsing rewritten by Tomas Mraz <tm@t8m.info>
*/
#include "config.h"
@@ -50,166 +51,156 @@ typedef enum { AND, OR } operator;
/* --- static functions for checking whether the user should be let in --- */
-static void shift_bytes(char *mem, int from, int by)
+static char *
+shift_buf(char *mem, int from)
{
- while (by-- > 0) {
- *mem = mem[from];
+ char *start = mem;
+ while ((*mem = mem[from]) != '\0')
++mem;
+ memset(mem, '\0', PAM_GROUP_BUFLEN - (mem - start));
+ return mem;
+}
+
+static void
+trim_spaces(char *buf, char *from)
+{
+ while (from > buf) {
+ --from;
+ if (*from == ' ')
+ *from = '\0';
+ else
+ break;
}
}
-/* This function should initially be called with buf = NULL. If
- * an error occurs, the file descriptor is closed. Subsequent
- * calls with a closed descriptor will cause buf to be deallocated.
- * Therefore, always check buf after calling this to see if an error
- * occurred.
- */
+#define STATE_NL 0 /* new line starting */
+#define STATE_COMMENT 1 /* inside comment */
+#define STATE_FIELD 2 /* field following */
+#define STATE_EOF 3 /* end of file or error */
+
static int
-read_field (const pam_handle_t *pamh, int fd, char **buf, int *from, int *to)
+read_field(const pam_handle_t *pamh, int fd, char **buf, int *from, int *state)
{
- /* is buf set ? */
+ char *to;
+ char *src;
+ int i;
+ char c;
+ int onspace;
+ /* is buf set ? */
if (! *buf) {
- *buf = (char *) malloc(PAM_GROUP_BUFLEN);
+ *buf = (char *) calloc(1, PAM_GROUP_BUFLEN+1);
if (! *buf) {
pam_syslog(pamh, LOG_ERR, "out of memory");
+ D(("no memory"));
+ *state = STATE_EOF;
return -1;
}
- *from = *to = 0;
+ *from = 0;
+ *state = STATE_NL;
fd = open(PAM_GROUP_CONF, O_RDONLY);
+ if (fd < 0) {
+ pam_syslog(pamh, LOG_ERR, "error opening %s: %m", PAM_GROUP_CONF);
+ _pam_drop(*buf);
+ *state = STATE_EOF;
+ return -1;
+ }
}
- /* do we have a file open ? return error */
-
- if (fd < 0 && *to <= 0) {
- pam_syslog(pamh, LOG_ERR, "%s not opened", PAM_GROUP_CONF);
- memset(*buf, 0, PAM_GROUP_BUFLEN);
- _pam_drop(*buf);
- return -1;
- }
-
- /* check if there was a newline last time */
-
- if ((*to > *from) && (*to > 0)
- && ((*buf)[*from] == '\0')) { /* previous line ended */
- (*from)++;
- (*buf)[0] = '\0';
- return fd;
- }
-
- /* ready for more data: first shift the buffer's remaining data */
-
- *to -= *from;
- shift_bytes(*buf, *from, *to);
- *from = 0;
- (*buf)[*to] = '\0';
-
- while (fd >= 0 && *to < PAM_GROUP_BUFLEN) {
- int i;
-
- /* now try to fill the remainder of the buffer */
+ if (*from > 0)
+ to = shift_buf(*buf, *from);
+ else
+ to = *buf;
- i = read(fd, *to + *buf, PAM_GROUP_BUFLEN - *to);
+ while (fd != -1 && to - *buf < PAM_GROUP_BUFLEN) {
+ i = pam_modutil_read(fd, to, PAM_GROUP_BUFLEN - (to - *buf));
if (i < 0) {
pam_syslog(pamh, LOG_ERR, "error reading %s: %m", PAM_GROUP_CONF);
close(fd);
+ memset(*buf, 0, PAM_GROUP_BUFLEN);
+ _pam_drop(*buf);
+ *state = STATE_EOF;
return -1;
} else if (!i) {
close(fd);
fd = -1; /* end of file reached */
- } else
- *to += i;
+ }
- /*
- * contract the buffer. Delete any comments, and replace all
- * multiple spaces with single commas
- */
+ to += i;
+ }
- i = 0;
-#ifdef DEBUG_DUMP
- D(("buffer=<%s>",*buf));
-#endif
- while (i < *to) {
- if ((*buf)[i] == ',') {
- int j;
-
- for (j=++i; j<*to && (*buf)[j] == ','; ++j);
- if (j!=i) {
- shift_bytes(i + (*buf), j-i, (*to) - j);
- *to -= j-i;
+ if (to == *buf) {
+ /* nothing previously in buf, nothing read */
+ _pam_drop(*buf);
+ *state = STATE_EOF;
+ return -1;
+ }
+
+ memset(to, '\0', PAM_GROUP_BUFLEN - (to - *buf));
+
+ to = *buf;
+ onspace = 1; /* delete any leading spaces */
+
+ for (src = to; (c=*src) != '\0'; ++src) {
+ if (*state == STATE_COMMENT && c != '\n') {
+ continue;
+ }
+
+ switch (c) {
+ case '\n':
+ *state = STATE_NL;
+ *to = '\0';
+ *from = (src - *buf) + 1;
+ trim_spaces(*buf, to);
+ return fd;
+
+ case '\t':
+ case ' ':
+ if (!onspace) {
+ onspace = 1;
+ *to++ = ' ';
}
- }
- switch ((*buf)[i]) {
- int j, c;
+ break;
+
+ case '!':
+ onspace = 1; /* ignore following spaces */
+ *to++ = '!';
+ break;
+
case '#':
- c = 0;
- for (j=i; j < *to && (c = (*buf)[j]) != '\n'; ++j);
- if (j >= *to) {
- (*buf)[*to = ++i] = '\0';
- } else if (c == '\n') {
- shift_bytes(i + (*buf), j-i, (*to) - j);
- *to -= j-i;
- ++i;
- } else {
- pam_syslog(pamh, LOG_CRIT,
- "internal error in file %s at line %d",
- __FILE__, __LINE__);
- close(fd);
- return -1;
- }
+ *state = STATE_COMMENT;
break;
+
+ case FIELD_SEPARATOR:
+ *state = STATE_FIELD;
+ *to = '\0';
+ *from = (src - *buf) + 1;
+ trim_spaces(*buf, to);
+ return fd;
+
case '\\':
- if ((*buf)[i+1] == '\n') {
- shift_bytes(i + *buf, 2, *to - (i+2));
- *to -= 2;
- } else {
- ++i; /* we don't escape non-newline characters */
+ if (src[1] == '\n') {
+ ++src; /* skip it */
+ break;
}
- break;
- case '!':
- case ' ':
- case '\t':
- if ((*buf)[i] != '!')
- (*buf)[i] = ',';
- /* delete any trailing spaces */
- for (j=++i; j < *to && ( (c = (*buf)[j]) == ' '
- || c == '\t' ); ++j);
- shift_bytes(i + *buf, j-i, (*to)-j );
- *to -= j-i;
- break;
default:
- ++i;
- }
+ *to++ = c;
+ onspace = 0;
}
+ if (src > to)
+ *src = '\0'; /* clearing */
}
- (*buf)[*to] = '\0';
-
- /* now return the next field (set the from/to markers) */
- {
- int i;
-
- for (i=0; i<*to; ++i) {
- switch ((*buf)[i]) {
- case '#':
- case '\n': /* end of the line/file */
- (*buf)[i] = '\0';
- *from = i;
- return fd;
- case FIELD_SEPARATOR: /* end of the field */
- (*buf)[i] = '\0';
- *from = ++i;
- return fd;
- }
- }
- *from = i;
- (*buf)[*from] = '\0';
+ if (*state != STATE_COMMENT) {
+ *state = STATE_COMMENT;
+ pam_syslog(pamh, LOG_ERR, "field too long - ignored");
+ **buf = '\0';
+ } else {
+ *to = '\0';
+ trim_spaces(*buf, to);
}
- if (*to <= 0) {
- D(("[end of text]"));
- *buf = NULL;
- }
+ *from = 0;
return fd;
}
@@ -582,7 +573,7 @@ static int mkgrplist(pam_handle_t *pamh, char *buf, gid_t **list, int len)
static int check_account(pam_handle_t *pamh, const char *service,
const char *tty, const char *user)
{
- int from=0,to=0,fd=-1;
+ int from=0, state=STATE_NL, fd=-1;
char *buffer=NULL;
int count=0;
TIME here_and_now;
@@ -627,7 +618,7 @@ static int check_account(pam_handle_t *pamh, const char *service,
/* here we get the service name field */
- fd = read_field(pamh,fd,&buffer,&from,&to);
+ fd = read_field(pamh, fd, &buffer, &from, &state);
if (!buffer || !buffer[0]) {
/* empty line .. ? */
continue;
@@ -635,15 +626,21 @@ static int check_account(pam_handle_t *pamh, const char *service,
++count;
D(("working on rule #%d",count));
+ if (state != STATE_FIELD) {
+ pam_syslog(pamh, LOG_ERR,
+ "%s: malformed rule #%d", PAM_GROUP_CONF, count);
+ continue;
+ }
+
good = logic_field(pamh,service, buffer, count, is_same);
D(("with service: %s", good ? "passes":"fails" ));
/* here we get the terminal name field */
- fd = read_field(pamh,fd,&buffer,&from,&to);
- if (!buffer || !buffer[0]) {
+ fd = read_field(pamh, fd, &buffer, &from, &state);
+ if (state != STATE_FIELD) {
pam_syslog(pamh, LOG_ERR,
- "%s: no tty entry #%d", PAM_GROUP_CONF, count);
+ "%s: malformed rule #%d", PAM_GROUP_CONF, count);
continue;
}
good &= logic_field(pamh,tty, buffer, count, is_same);
@@ -651,10 +648,10 @@ static int check_account(pam_handle_t *pamh, const char *service,
/* here we get the username field */
- fd = read_field(pamh,fd,&buffer,&from,&to);
- if (!buffer || !buffer[0]) {
+ fd = read_field(pamh, fd, &buffer, &from, &state);
+ if (state != STATE_FIELD) {
pam_syslog(pamh, LOG_ERR,
- "%s: no user entry #%d", PAM_GROUP_CONF, count);
+ "%s: malformed rule #%d", PAM_GROUP_CONF, count);
continue;
}
/* If buffer starts with @, we are using netgroups */
@@ -669,20 +666,20 @@ static int check_account(pam_handle_t *pamh, const char *service,
/* here we get the time field */
- fd = read_field(pamh,fd,&buffer,&from,&to);
- if (!buffer || !buffer[0]) {
+ fd = read_field(pamh, fd, &buffer, &from, &state);
+ if (state != STATE_FIELD) {
pam_syslog(pamh, LOG_ERR,
- "%s: no time entry #%d", PAM_GROUP_CONF, count);
+ "%s: malformed rule #%d", PAM_GROUP_CONF, count);
continue;
}
good &= logic_field(pamh,&here_and_now, buffer, count, check_time);
D(("with time: %s", good ? "passes":"fails" ));
- fd = read_field(pamh,fd,&buffer,&from,&to);
- if (!buffer || !buffer[0]) {
+ fd = read_field(pamh, fd, &buffer, &from, &state);
+ if (state == STATE_FIELD) {
pam_syslog(pamh, LOG_ERR,
- "%s: no listed groups for rule #%d", PAM_GROUP_CONF, count);
+ "%s: poorly terminated rule #%d", PAM_GROUP_CONF, count);
continue;
}
@@ -701,14 +698,6 @@ static int check_account(pam_handle_t *pamh, const char *service,
}
}
- /* check the line is terminated correctly */
-
- fd = read_field(pamh,fd,&buffer,&from,&to);
- if (buffer && buffer[0]) {
- pam_syslog(pamh, LOG_ERR,
- "%s: poorly terminated rule #%d", PAM_GROUP_CONF, count);
- }
-
if (good > 0) {
D(("rule #%d passed, added %d groups", count, good));
} else if (good < 0) {
@@ -717,7 +706,7 @@ static int check_account(pam_handle_t *pamh, const char *service,
D(("rule #%d failed", count));
}
- } while (buffer);
+ } while (state != STATE_EOF);
/* now set the groups for the user */
diff --git a/modules/pam_lastlog/pam_lastlog.c b/modules/pam_lastlog/pam_lastlog.c
index b44c1755..9e8da7d2 100644
--- a/modules/pam_lastlog/pam_lastlog.c
+++ b/modules/pam_lastlog/pam_lastlog.c
@@ -403,9 +403,13 @@ last_login_failed(pam_handle_t *pamh, int announce, const char *user, time_t llt
/* obtain the failed login attempt records from btmp */
fd = open(_PATH_BTMP, O_RDONLY);
if (fd < 0) {
+ int save_errno = errno;
pam_syslog(pamh, LOG_ERR, "unable to open %s: %m", _PATH_BTMP);
D(("unable to open %s file", _PATH_BTMP));
- return PAM_SERVICE_ERR;
+ if (save_errno == ENOENT)
+ return PAM_SUCCESS;
+ else
+ return PAM_SERVICE_ERR;
}
while ((retval=pam_modutil_read(fd, (void *)&ut,
diff --git a/modules/pam_limits/limits.conf.5.xml b/modules/pam_limits/limits.conf.5.xml
index a9757a7f..939fa0fe 100644
--- a/modules/pam_limits/limits.conf.5.xml
+++ b/modules/pam_limits/limits.conf.5.xml
@@ -53,7 +53,38 @@
<listitem>
<para>
the wildcard <emphasis remap='B'>%</emphasis>, for maxlogins limit only,
- can also be used with <emphasis remap='b'>%group</emphasis> syntax.
+ can also be used with <emphasis remap='B'>%group</emphasis> syntax. If the
+ <emphasis remap='B'>%</emphasis> wildcard is used alone it is identical
+ to using <emphasis remap='B'>*</emphasis> with maxsyslogins limit. With
+ a group specified after <emphasis remap='B'>%</emphasis> it limits the total
+ number of logins of all users that are member of the group.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ an uid range specified as <replaceable>&lt;min_uid&gt;</replaceable><emphasis
+ remap='B'>:</emphasis><replaceable>&lt;max_uid&gt;</replaceable>. If min_uid
+ is omitted, the match is exact for the max_uid. If max_uid is omitted, all
+ uids greater than or equal min_uid match.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ a gid range specified as <emphasis
+ remap='B'>@</emphasis><replaceable>&lt;min_gid&gt;</replaceable><emphasis
+ remap='B'>:</emphasis><replaceable>&lt;max_gid&gt;</replaceable>. If min_gid
+ is omitted, the match is exact for the max_gid. If max_gid is omitted, all
+ gids greater than or equal min_gid match. For the exact match all groups including
+ the user's supplementary groups are examined. For the range matches only
+ the user's primary group is examined.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ a gid specified as <emphasis
+ remap='B'>%:</emphasis><replaceable>&lt;gid&gt;</replaceable> applicable
+ to maxlogins limit only. It limits the total number of logins of all users
+ that are member of the group with the specified gid.
</para>
</listitem>
</itemizedlist>
@@ -182,7 +213,7 @@
<varlistentry>
<term><option>maxsyslogins</option></term>
<listitem>
- <para>maximum number of logins on system</para>
+ <para>maximum number of all logins on system</para>
</listitem>
</varlistentry>
<varlistentry>
@@ -205,7 +236,7 @@
</listitem>
</varlistentry>
<varlistentry>
- <term><option>msqqueue</option></term>
+ <term><option>msgqueue</option></term>
<listitem>
<para>maximum memory used by POSIX message queues (bytes)
(Linux 2.6 and higher)</para>
@@ -272,12 +303,15 @@
</para>
<programlisting>
* soft core 0
-* hard rss 10000
+* hard nofile 512
@student hard nproc 20
@faculty soft nproc 20
@faculty hard nproc 50
ftp hard nproc 0
@student - maxlogins 4
+:123 hard cpu 5000
+@500: soft cpu 10000
+600:700 hard locks 10
</programlisting>
</refsect1>
diff --git a/modules/pam_limits/pam_limits.8.xml b/modules/pam_limits/pam_limits.8.xml
index 0be7ef4d..663c0e7b 100644
--- a/modules/pam_limits/pam_limits.8.xml
+++ b/modules/pam_limits/pam_limits.8.xml
@@ -23,15 +23,15 @@
<cmdsynopsis id="pam_limits-cmdsynopsis">
<command>pam_limits.so</command>
<arg choice="opt">
- change_uid
- </arg>
- <arg choice="opt">
conf=<replaceable>/path/to/limits.conf</replaceable>
</arg>
<arg choice="opt">
debug
</arg>
<arg choice="opt">
+ set_all
+ </arg>
+ <arg choice="opt">
utmp_early
</arg>
<arg choice="opt">
@@ -72,35 +72,34 @@
<variablelist>
<varlistentry>
<term>
- <option>change_uid</option>
+ <option>conf=<replaceable>/path/to/limits.conf</replaceable></option>
</term>
<listitem>
<para>
- Change real uid to the user for who the limits are set up. Use this
- option if you have problems like login not forking a shell for user
- who has no processes. Be warned that something else may break when
- you do this.
+ Indicate an alternative limits.conf style configuration file to
+ override the default.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>
- <option>conf=<replaceable>/path/to/limits.conf</replaceable></option>
+ <option>debug</option>
</term>
<listitem>
<para>
- Indicate an alternative limits.conf style configuration file to
- override the default.
+ Print debug information.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>
- <option>debug</option>
+ <option>set_all</option>
</term>
<listitem>
<para>
- Print debug information.
+ Set the limits for which no value is specified in the
+ configuration file to the one from the process with the
+ PID 1.
</para>
</listitem>
</varlistentry>
diff --git a/modules/pam_limits/pam_limits.c b/modules/pam_limits/pam_limits.c
index f446f9e3..c1810e07 100644
--- a/modules/pam_limits/pam_limits.c
+++ b/modules/pam_limits/pam_limits.c
@@ -51,9 +51,16 @@
#define LIMITS_DEF_USER 0 /* limit was set by an user entry */
#define LIMITS_DEF_GROUP 1 /* limit was set by a group entry */
#define LIMITS_DEF_ALLGROUP 2 /* limit was set by a group entry */
-#define LIMITS_DEF_ALL 3 /* limit was set by an default entry */
-#define LIMITS_DEF_DEFAULT 4 /* limit was set by an default entry */
-#define LIMITS_DEF_NONE 5 /* this limit was not set yet */
+#define LIMITS_DEF_ALL 3 /* limit was set by an all entry */
+#define LIMITS_DEF_DEFAULT 4 /* limit was set by a default entry */
+#define LIMITS_DEF_KERNEL 5 /* limit was set from /proc/1/limits */
+#define LIMITS_DEF_NONE 6 /* this limit was not set yet */
+
+#define LIMIT_RANGE_ERR -1 /* error in specified uid/gid range */
+#define LIMIT_RANGE_NONE 0 /* no range specified */
+#define LIMIT_RANGE_ONE 1 /* exact uid/gid specified (:max_uid)*/
+#define LIMIT_RANGE_MIN 2 /* only minimum uid/gid specified (min_uid:) */
+#define LIMIT_RANGE_MM 3 /* both min and max uid/gid specified (min_uid:max_uid) */
static const char *limits_def_names[] = {
"USER",
@@ -61,6 +68,7 @@ static const char *limits_def_names[] = {
"ALLGROUP",
"ALL",
"DEFAULT",
+ "KERNEL",
"NONE",
NULL
};
@@ -103,9 +111,9 @@ struct pam_limit_s {
/* argument parsing */
#define PAM_DEBUG_ARG 0x0001
-#define PAM_DO_SETREUID 0x0002
#define PAM_UTMP_EARLY 0x0004
#define PAM_NO_AUDIT 0x0008
+#define PAM_SET_ALL 0x0010
/* Limits from globbed files. */
#define LIMITS_CONF_GLOB LIMITS_FILE_DIR
@@ -127,12 +135,12 @@ _pam_parse (const pam_handle_t *pamh, int argc, const char **argv,
ctrl |= PAM_DEBUG_ARG;
} else if (!strncmp(*argv,"conf=",5)) {
pl->conf_file = *argv+5;
- } else if (!strncmp(*argv,"change_uid",10)) {
- ctrl |= PAM_DO_SETREUID;
} else if (!strcmp(*argv,"utmp_early")) {
ctrl |= PAM_UTMP_EARLY;
} else if (!strcmp(*argv,"noaudit")) {
ctrl |= PAM_NO_AUDIT;
+ } else if (!strcmp(*argv,"set_all")) {
+ ctrl |= PAM_SET_ALL;
} else {
pam_syslog(pamh, LOG_ERR, "unknown option: %s", *argv);
}
@@ -289,7 +297,140 @@ check_logins (pam_handle_t *pamh, const char *name, int limit, int ctrl,
return 0;
}
-static int init_limits(struct pam_limit_s *pl)
+static const char *lnames[RLIM_NLIMITS] = {
+ [RLIMIT_CPU] = "Max cpu time",
+ [RLIMIT_FSIZE] = "Max file size",
+ [RLIMIT_DATA] = "Max data size",
+ [RLIMIT_STACK] = "Max stack size",
+ [RLIMIT_CORE] = "Max core file size",
+ [RLIMIT_RSS] = "Max resident set",
+ [RLIMIT_NPROC] = "Max processes",
+ [RLIMIT_NOFILE] = "Max open files",
+ [RLIMIT_MEMLOCK] = "Max locked memory",
+#ifdef RLIMIT_AS
+ [RLIMIT_AS] = "Max address space",
+#endif
+#ifdef RLIMIT_LOCKS
+ [RLIMIT_LOCKS] = "Max file locks",
+#endif
+#ifdef RLIMIT_SIGPENDING
+ [RLIMIT_SIGPENDING] = "Max pending signals",
+#endif
+#ifdef RLIMIT_MSGQUEUE
+ [RLIMIT_MSGQUEUE] = "Max msgqueue size",
+#endif
+#ifdef RLIMIT_NICE
+ [RLIMIT_NICE] = "Max nice priority",
+#endif
+#ifdef RLIMIT_RTPRIO
+ [RLIMIT_RTPRIO] = "Max realtime priority",
+#endif
+#ifdef RLIMIT_RTTIME
+ [RLIMIT_RTTIME] = "Max realtime timeout",
+#endif
+};
+
+static int str2rlimit(char *name) {
+ int i;
+ if (!name || *name == '\0')
+ return -1;
+ for(i = 0; i < RLIM_NLIMITS; i++) {
+ if (strcmp(name, lnames[i]) == 0) return i;
+ }
+ return -1;
+}
+
+static rlim_t str2rlim_t(char *value) {
+ unsigned long long rlimit = 0;
+
+ if (!value) return (rlim_t)rlimit;
+ if (strcmp(value, "unlimited") == 0) {
+ return RLIM_INFINITY;
+ }
+ rlimit = strtoull(value, NULL, 10);
+ return (rlim_t)rlimit;
+}
+
+#define LIMITS_SKIP_WHITESPACE { \
+ /* step backwards over spaces */ \
+ pos--; \
+ while (pos && line[pos] == ' ') pos--; \
+ if (!pos) continue; \
+ line[pos+1] = '\0'; \
+}
+#define LIMITS_MARK_ITEM(item) { \
+ /* step backwards over non-spaces */ \
+ pos--; \
+ while (pos && line[pos] != ' ') pos--; \
+ if (!pos) continue; \
+ item = line + pos + 1; \
+}
+
+static void parse_kernel_limits(pam_handle_t *pamh, struct pam_limit_s *pl, int ctrl)
+{
+ int i, maxlen = 0;
+ FILE *limitsfile;
+ const char *proclimits = "/proc/1/limits";
+ char line[256];
+ char *units, *hard, *soft, *name;
+
+ if (!(limitsfile = fopen(proclimits, "r"))) {
+ pam_syslog(pamh, LOG_WARNING, "Could not read %s (%s), using PAM defaults", proclimits, strerror(errno));
+ return;
+ }
+
+ while (fgets(line, 256, limitsfile)) {
+ int pos = strlen(line);
+ if (pos < 2) continue;
+
+ /* drop trailing newline */
+ if (line[pos-1] == '\n') {
+ pos--;
+ line[pos] = '\0';
+ }
+
+ /* determine formatting boundry of limits report */
+ if (!maxlen && strncmp(line, "Limit", 5) == 0) {
+ maxlen = pos;
+ continue;
+ }
+
+ if (pos == maxlen) {
+ /* step backwards over "Units" name */
+ LIMITS_SKIP_WHITESPACE;
+ LIMITS_MARK_ITEM(units);
+ }
+ else {
+ units = "";
+ }
+
+ /* step backwards over "Hard Limit" value */
+ LIMITS_SKIP_WHITESPACE;
+ LIMITS_MARK_ITEM(hard);
+
+ /* step backwards over "Soft Limit" value */
+ LIMITS_SKIP_WHITESPACE;
+ LIMITS_MARK_ITEM(soft);
+
+ /* step backwards over name of limit */
+ LIMITS_SKIP_WHITESPACE;
+ name = line;
+
+ i = str2rlimit(name);
+ if (i < 0 || i >= RLIM_NLIMITS) {
+ if (ctrl & PAM_DEBUG_ARG)
+ pam_syslog(pamh, LOG_DEBUG, "Unknown kernel rlimit '%s' ignored", name);
+ continue;
+ }
+ pl->limits[i].limit.rlim_cur = str2rlim_t(soft);
+ pl->limits[i].limit.rlim_max = str2rlim_t(hard);
+ pl->limits[i].src_soft = LIMITS_DEF_KERNEL;
+ pl->limits[i].src_hard = LIMITS_DEF_KERNEL;
+ }
+ fclose(limitsfile);
+}
+
+static int init_limits(pam_handle_t *pamh, struct pam_limit_s *pl, int ctrl)
{
int i;
int retval = PAM_SUCCESS;
@@ -310,6 +451,20 @@ static int init_limits(struct pam_limit_s *pl)
}
}
+#ifdef __linux__
+ if (ctrl & PAM_SET_ALL) {
+ parse_kernel_limits(pamh, pl, ctrl);
+
+ for(i = 0; i < RLIM_NLIMITS; i++) {
+ if (pl->limits[i].supported &&
+ (pl->limits[i].src_soft == LIMITS_DEF_NONE ||
+ pl->limits[i].src_hard == LIMITS_DEF_NONE)) {
+ pam_syslog(pamh, LOG_WARNING, "Did not find kernel RLIMIT for %s, using PAM default", rlimit2str(i));
+ }
+ }
+ }
+#endif
+
errno = 0;
pl->priority = getpriority (PRIO_PROCESS, 0);
if (pl->priority == -1 && errno != 0)
@@ -523,8 +678,57 @@ process_limit (const pam_handle_t *pamh, int source, const char *lim_type,
return;
}
-static int parse_config_file(pam_handle_t *pamh, const char *uname, int ctrl,
- struct pam_limit_s *pl)
+static int
+parse_uid_range(pam_handle_t *pamh, const char *domain,
+ uid_t *min_uid, uid_t *max_uid)
+{
+ const char *range = domain;
+ char *pmax;
+ char *endptr;
+ int rv = LIMIT_RANGE_MM;
+
+ if ((pmax=strchr(range, ':')) == NULL)
+ return LIMIT_RANGE_NONE;
+ ++pmax;
+
+ if (range[0] == '@' || range[0] == '%')
+ ++range;
+
+ if (range[0] == ':')
+ rv = LIMIT_RANGE_ONE;
+ else {
+ errno = 0;
+ *min_uid = strtoul (range, &endptr, 10);
+ if (errno != 0 || (range == endptr) || *endptr != ':') {
+ pam_syslog(pamh, LOG_DEBUG,
+ "wrong min_uid/gid value in '%s'", domain);
+ return LIMIT_RANGE_ERR;
+ }
+ }
+
+ if (*pmax == '\0') {
+ if (rv == LIMIT_RANGE_ONE)
+ return LIMIT_RANGE_ERR;
+ else
+ return LIMIT_RANGE_MIN;
+ }
+
+ errno = 0;
+ *max_uid = strtoul (pmax, &endptr, 10);
+ if (errno != 0 || (pmax == endptr) || *endptr != '\0') {
+ pam_syslog(pamh, LOG_DEBUG,
+ "wrong max_uid/gid value in '%s'", domain);
+ return LIMIT_RANGE_ERR;
+ }
+
+ if (rv == LIMIT_RANGE_ONE)
+ *min_uid = *max_uid;
+ return rv;
+}
+
+static int
+parse_config_file(pam_handle_t *pamh, const char *uname, uid_t uid, gid_t gid,
+ int ctrl, struct pam_limit_s *pl)
{
FILE *fil;
char buf[LINE_LENGTH];
@@ -546,8 +750,10 @@ static int parse_config_file(pam_handle_t *pamh, const char *uname, int ctrl,
char item[LINE_LENGTH];
char value[LINE_LENGTH];
int i;
+ int rngtype;
size_t j;
char *tptr,*line;
+ uid_t min_uid = (uid_t)-1, max_uid = (uid_t)-1;
line = buf;
/* skip the leading white space */
@@ -575,6 +781,11 @@ static int parse_config_file(pam_handle_t *pamh, const char *uname, int ctrl,
for(j=0; j < strlen(ltype); j++)
ltype[j]=tolower(ltype[j]);
+ if ((rngtype=parse_uid_range(pamh, domain, &min_uid, &max_uid)) < 0) {
+ pam_syslog(pamh, LOG_WARNING, "invalid uid range '%s' - skipped", domain);
+ continue;
+ }
+
if (i == 4) { /* a complete line */
for(j=0; j < strlen(item); j++)
item[j]=tolower(item[j]);
@@ -584,47 +795,133 @@ static int parse_config_file(pam_handle_t *pamh, const char *uname, int ctrl,
if (strcmp(uname, domain) == 0) /* this user have a limit */
process_limit(pamh, LIMITS_DEF_USER, ltype, item, value, ctrl, pl);
else if (domain[0]=='@') {
- if (ctrl & PAM_DEBUG_ARG) {
+ if (ctrl & PAM_DEBUG_ARG) {
pam_syslog(pamh, LOG_DEBUG,
"checking if %s is in group %s",
uname, domain + 1);
- }
- if (pam_modutil_user_in_group_nam_nam(pamh, uname, domain+1))
- process_limit(pamh, LIMITS_DEF_GROUP, ltype, item, value, ctrl,
+ }
+ switch(rngtype) {
+ case LIMIT_RANGE_NONE:
+ if (pam_modutil_user_in_group_nam_nam(pamh, uname, domain+1))
+ process_limit(pamh, LIMITS_DEF_GROUP, ltype, item, value, ctrl,
+ pl);
+ break;
+ case LIMIT_RANGE_ONE:
+ if (pam_modutil_user_in_group_nam_gid(pamh, uname, (gid_t)max_uid))
+ process_limit(pamh, LIMITS_DEF_GROUP, ltype, item, value, ctrl,
pl);
+ break;
+ case LIMIT_RANGE_MM:
+ if (gid > (gid_t)max_uid)
+ break;
+ /* fallthrough */
+ case LIMIT_RANGE_MIN:
+ if (gid >= (gid_t)min_uid)
+ process_limit(pamh, LIMITS_DEF_GROUP, ltype, item, value, ctrl,
+ pl);
+ }
} else if (domain[0]=='%') {
- if (ctrl & PAM_DEBUG_ARG) {
+ if (ctrl & PAM_DEBUG_ARG) {
pam_syslog(pamh, LOG_DEBUG,
"checking if %s is in group %s",
uname, domain + 1);
- }
- if (strcmp(domain,"%") == 0)
- process_limit(pamh, LIMITS_DEF_ALL, ltype, item, value, ctrl,
- pl);
- else if (pam_modutil_user_in_group_nam_nam(pamh, uname, domain+1)) {
- strcpy(pl->login_group, domain+1);
- process_limit(pamh, LIMITS_DEF_ALLGROUP, ltype, item, value, ctrl,
- pl);
}
- } else if (strcmp(domain, "*") == 0)
- process_limit(pamh, LIMITS_DEF_DEFAULT, ltype, item, value, ctrl,
- pl);
+ switch(rngtype) {
+ case LIMIT_RANGE_NONE:
+ if (strcmp(domain,"%") == 0)
+ process_limit(pamh, LIMITS_DEF_ALL, ltype, item, value, ctrl,
+ pl);
+ else if (pam_modutil_user_in_group_nam_nam(pamh, uname, domain+1)) {
+ strcpy(pl->login_group, domain+1);
+ process_limit(pamh, LIMITS_DEF_ALLGROUP, ltype, item, value, ctrl,
+ pl);
+ }
+ break;
+ case LIMIT_RANGE_ONE:
+ if (pam_modutil_user_in_group_nam_gid(pamh, uname, (gid_t)max_uid)) {
+ struct group *grp;
+ grp = pam_modutil_getgrgid(pamh, (gid_t)max_uid);
+ strncpy(pl->login_group, grp->gr_name, sizeof(pl->login_group));
+ pl->login_group[sizeof(pl->login_group)-1] = '\0';
+ process_limit(pamh, LIMITS_DEF_ALLGROUP, ltype, item, value, ctrl,
+ pl);
+ }
+ break;
+ case LIMIT_RANGE_MIN:
+ case LIMIT_RANGE_MM:
+ pam_syslog(pamh, LOG_WARNING, "range unsupported for %%group matching - ignored");
+ }
+ } else {
+ switch(rngtype) {
+ case LIMIT_RANGE_NONE:
+ if (strcmp(domain, "*") == 0)
+ process_limit(pamh, LIMITS_DEF_DEFAULT, ltype, item, value, ctrl,
+ pl);
+ break;
+ case LIMIT_RANGE_ONE:
+ if (uid != max_uid)
+ break;
+ /* fallthrough */
+ case LIMIT_RANGE_MM:
+ if (uid > max_uid)
+ break;
+ /* fallthrough */
+ case LIMIT_RANGE_MIN:
+ if (uid >= min_uid)
+ process_limit(pamh, LIMITS_DEF_USER, ltype, item, value, ctrl, pl);
+ }
+ }
} else if (i == 2 && ltype[0] == '-') { /* Probably a no-limit line */
if (strcmp(uname, domain) == 0) {
if (ctrl & PAM_DEBUG_ARG) {
pam_syslog(pamh, LOG_DEBUG, "no limits for '%s'", uname);
}
- fclose(fil);
- return PAM_IGNORE;
- } else if (domain[0] == '@' && pam_modutil_user_in_group_nam_nam(pamh, uname, domain+1)) {
+ } else if (domain[0] == '@') {
+ switch(rngtype) {
+ case LIMIT_RANGE_NONE:
+ if (!pam_modutil_user_in_group_nam_nam(pamh, uname, domain+1))
+ continue; /* next line */
+ break;
+ case LIMIT_RANGE_ONE:
+ if (!pam_modutil_user_in_group_nam_gid(pamh, uname, (gid_t)max_uid))
+ continue; /* next line */
+ break;
+ case LIMIT_RANGE_MM:
+ if (gid > (gid_t)max_uid)
+ continue; /* next line */
+ /* fallthrough */
+ case LIMIT_RANGE_MIN:
+ if (gid < (gid_t)min_uid)
+ continue; /* next line */
+ }
if (ctrl & PAM_DEBUG_ARG) {
pam_syslog(pamh, LOG_DEBUG,
"no limits for '%s' in group '%s'",
uname, domain+1);
}
- fclose(fil);
- return PAM_IGNORE;
+ } else {
+ switch(rngtype) {
+ case LIMIT_RANGE_NONE:
+ continue; /* next line */
+ case LIMIT_RANGE_ONE:
+ if (uid != max_uid)
+ continue; /* next line */
+ break;
+ case LIMIT_RANGE_MM:
+ if (uid > max_uid)
+ continue; /* next line */
+ /* fallthrough */
+ case LIMIT_RANGE_MIN:
+ if (uid >= min_uid)
+ break;
+ continue; /* next line */
+ }
+ if (ctrl & PAM_DEBUG_ARG) {
+ pam_syslog(pamh, LOG_DEBUG, "no limits for '%s'", uname);
+ }
}
+ fclose(fil);
+ return PAM_IGNORE;
} else {
pam_syslog(pamh, LOG_WARNING, "invalid line '%s' - skipped", line);
}
@@ -728,13 +1025,13 @@ pam_sm_open_session (pam_handle_t *pamh, int flags UNUSED,
return PAM_USER_UNKNOWN;
}
- retval = init_limits(pl);
+ retval = init_limits(pamh, pl, ctrl);
if (retval != PAM_SUCCESS) {
pam_syslog(pamh, LOG_WARNING, "cannot initialize");
return PAM_ABORT;
}
- retval = parse_config_file(pamh, pwd->pw_name, ctrl, pl);
+ retval = parse_config_file(pamh, pwd->pw_name, pwd->pw_uid, pwd->pw_gid, ctrl, pl);
if (retval == PAM_IGNORE) {
D(("the configuration file ('%s') has an applicable '<domain> -' entry", CONF_FILE));
return PAM_SUCCESS;
@@ -758,7 +1055,7 @@ pam_sm_open_session (pam_handle_t *pamh, int flags UNUSED,
/* Parse the *.conf files. */
for (i = 0; globbuf.gl_pathv[i] != NULL; i++) {
pl->conf_file = globbuf.gl_pathv[i];
- retval = parse_config_file(pamh, pwd->pw_name, ctrl, pl);
+ retval = parse_config_file(pamh, pwd->pw_name, pwd->pw_uid, pwd->pw_gid, ctrl, pl);
if (retval == PAM_IGNORE) {
D(("the configuration file ('%s') has an applicable '<domain> -' entry", pl->conf_file));
globfree(&globbuf);
@@ -777,10 +1074,6 @@ out:
return retval;
}
- if (ctrl & PAM_DO_SETREUID) {
- setreuid(pwd->pw_uid, -1);
- }
-
retval = setup_limits(pamh, pwd->pw_name, pwd->pw_uid, ctrl, pl);
if (retval & LOGIN_ERR)
pam_error(pamh, _("Too many logins for '%s'."), pwd->pw_name);
diff --git a/modules/pam_listfile/pam_listfile.c b/modules/pam_listfile/pam_listfile.c
index 616d2201..2af2afd8 100644
--- a/modules/pam_listfile/pam_listfile.c
+++ b/modules/pam_listfile/pam_listfile.c
@@ -78,8 +78,14 @@ pam_sm_authenticate (pam_handle_t *pamh, int flags UNUSED,
{
const char *junk;
+ /* option quiet has no value */
+ if(!strcmp(argv[i],"quiet")) {
+ quiet = 1;
+ continue;
+ }
+
memset(mybuf,'\0',sizeof(mybuf));
- memset(myval,'\0',sizeof(mybuf));
+ memset(myval,'\0',sizeof(myval));
junk = strchr(argv[i], '=');
if((junk == NULL) || (junk - argv[i]) >= (int) sizeof(mybuf)) {
pam_syslog(pamh,LOG_ERR, "Bad option: \"%s\"",
@@ -142,8 +148,6 @@ pam_sm_authenticate (pam_handle_t *pamh, int flags UNUSED,
apply_type=APPLY_TYPE_USER;
strncpy(apply_val,myval,sizeof(apply_val)-1);
}
- } else if (!strcmp(mybuf,"quiet")) {
- quiet = 1;
} else {
free(ifname);
pam_syslog(pamh,LOG_ERR, "Unknown option: %s",mybuf);
@@ -283,7 +287,8 @@ pam_sm_authenticate (pam_handle_t *pamh, int flags UNUSED,
ifname, citem, citemp, sense);
#endif
if(lstat(ifname,&fileinfo)) {
- pam_syslog(pamh,LOG_ERR, "Couldn't open %s",ifname);
+ if(!quiet)
+ pam_syslog(pamh,LOG_ERR, "Couldn't open %s",ifname);
free(ifname);
return onerr;
}
diff --git a/modules/pam_loginuid/pam_loginuid.c b/modules/pam_loginuid/pam_loginuid.c
index 4fa486c7..06479973 100644
--- a/modules/pam_loginuid/pam_loginuid.c
+++ b/modules/pam_loginuid/pam_loginuid.c
@@ -14,7 +14,8 @@
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * Foundation, Inc., 51 Franklin Street, Suite 500
+ * Boston, MA 02110-1335 USA
*
* Authors:
* Steve Grubb <sgrubb@redhat.com>
diff --git a/modules/pam_namespace/argv_parse.c b/modules/pam_namespace/argv_parse.c
index acc76d74..40510542 100644
--- a/modules/pam_namespace/argv_parse.c
+++ b/modules/pam_namespace/argv_parse.c
@@ -44,15 +44,15 @@ int argv_parse(const char *in_buf, int *ret_argc, char ***ret_argv)
{
int argc = 0, max_argc = 0;
char **argv, **new_argv, *buf, ch;
- const char *cp = 0;
- char *outcp = 0;
+ const char *cp = NULL;
+ char *outcp = NULL;
int state = STATE_WHITESPACE;
buf = malloc(strlen(in_buf)+1);
if (!buf)
return -1;
- max_argc = 0; argc = 0; argv = 0;
+ argv = NULL;
outcp = buf;
for (cp = in_buf; (ch = *cp); cp++) {
if (state == STATE_WHITESPACE) {
@@ -111,23 +111,30 @@ int argv_parse(const char *in_buf, int *ret_argc, char ***ret_argv)
}
if (state != STATE_WHITESPACE)
*outcp++ = '\0';
- if (argv == 0) {
- argv = malloc(sizeof(char *));
+ if (ret_argv) {
+ if (argv == NULL) {
+ free(buf);
+ if ((argv=malloc(sizeof(char *))) == NULL)
+ return -1;
+ }
+ argv[argc] = NULL;
+ *ret_argv = argv;
+ } else {
free(buf);
+ free(argv);
}
- argv[argc] = 0;
if (ret_argc)
*ret_argc = argc;
- if (ret_argv)
- *ret_argv = argv;
return 0;
}
void argv_free(char **argv)
{
- if (*argv)
- free(*argv);
- free(argv);
+ if (argv) {
+ if (*argv)
+ free(*argv);
+ free(argv);
+ }
}
#ifdef DEBUG_ARGV_PARSE
diff --git a/modules/pam_namespace/md5.c b/modules/pam_namespace/md5.c
index 9a060d27..b23d1607 100644
--- a/modules/pam_namespace/md5.c
+++ b/modules/pam_namespace/md5.c
@@ -148,7 +148,7 @@ void MD5Name(MD5Final)(unsigned char digest[16], struct MD5Context *ctx)
MD5Name(MD5Transform)(ctx->buf, (uint32 *) ctx->in);
byteReverse((unsigned char *) ctx->buf, 4);
memcpy(digest, ctx->buf, 16);
- memset(ctx, 0, sizeof(ctx)); /* In case it's sensitive */
+ memset(ctx, 0, sizeof(*ctx)); /* In case it's sensitive */
}
/* The four core functions - F1 is optimized somewhat */
diff --git a/modules/pam_namespace/pam_namespace.8.xml b/modules/pam_namespace/pam_namespace.8.xml
index 0433f0fd..48021c80 100644
--- a/modules/pam_namespace/pam_namespace.8.xml
+++ b/modules/pam_namespace/pam_namespace.8.xml
@@ -52,6 +52,9 @@
<arg choice="opt">
use_default_context
</arg>
+ <arg choice="opt">
+ mount_private
+ </arg>
</cmdsynopsis>
</refsynopsisdiv>
@@ -234,6 +237,24 @@
</listitem>
</varlistentry>
+ <varlistentry>
+ <term>
+ <option>mount_private</option>
+ </term>
+ <listitem>
+ <para>
+ This option can be used on systems where the / mount point or
+ its submounts are made shared (for example with a
+ <command>mount --make-rshared /</command> command).
+ The module will make the polyinstantiated directory mount points
+ private. Normally the pam_namespace will try to detect the
+ shared / mount point and make the polyinstantiated directories
+ private automatically. This option has to be used just when
+ only a subtree is shared and / is not.
+ </para>
+ </listitem>
+ </varlistentry>
+
</variablelist>
</refsect1>
diff --git a/modules/pam_namespace/pam_namespace.c b/modules/pam_namespace/pam_namespace.c
index a13f9599..4a99184a 100644
--- a/modules/pam_namespace/pam_namespace.c
+++ b/modules/pam_namespace/pam_namespace.c
@@ -61,9 +61,11 @@ static void add_polydir_entry(struct instance_data *idata,
static void del_polydir(struct polydir_s *poly)
{
- free(poly->uid);
- free(poly->init_script);
- free(poly);
+ if (poly) {
+ free(poly->uid);
+ free(poly->init_script);
+ free(poly);
+ }
}
/*
@@ -307,10 +309,6 @@ static int process_line(char *line, const char *home, const char *rhome,
const char *rvar_values[] = {rhome, idata->ruser};
int len;
- poly = calloc(1, sizeof(*poly));
- if (poly == NULL)
- goto erralloc;
-
/*
* skip the leading white space
*/
@@ -337,6 +335,10 @@ static int process_line(char *line, const char *home, const char *rhome,
if (line[0] == 0)
return 0;
+ poly = calloc(1, sizeof(*poly));
+ if (poly == NULL)
+ goto erralloc;
+
/*
* Initialize and scan the five strings from the line from the
* namespace configuration file.
@@ -1001,7 +1003,7 @@ static int protect_mount(int dfd, const char *path, struct instance_data *idata)
return 0;
}
-static int protect_dir(const char *path, mode_t mode, int do_mkdir,
+static int protect_dir(const char *path, mode_t mode, int do_mkdir, int always,
struct instance_data *idata)
{
char *p = strdup(path);
@@ -1080,7 +1082,7 @@ static int protect_dir(const char *path, mode_t mode, int do_mkdir,
}
}
- if (flags & O_NOFOLLOW) {
+ if ((flags & O_NOFOLLOW) || always) {
/* we are inside user-owned dir - protect */
if (protect_mount(rv, p, idata) == -1) {
save_errno = errno;
@@ -1093,7 +1095,7 @@ static int protect_dir(const char *path, mode_t mode, int do_mkdir,
error:
save_errno = errno;
free(p);
- if (dfd != AT_FDCWD)
+ if (dfd != AT_FDCWD && dfd >= 0)
close(dfd);
errno = save_errno;
@@ -1122,7 +1124,7 @@ static int check_inst_parent(char *ipath, struct instance_data *idata)
if (trailing_slash)
*trailing_slash = '\0';
- dfd = protect_dir(inst_parent, 0, 1, idata);
+ dfd = protect_dir(inst_parent, 0, 1, 0, idata);
if (dfd == -1 || fstat(dfd, &instpbuf) < 0) {
pam_syslog(idata->pamh, LOG_ERR,
@@ -1257,7 +1259,7 @@ static int create_polydir(struct polydir_s *polyptr,
}
#endif
- rc = protect_dir(dir, mode, 1, idata);
+ rc = protect_dir(dir, mode, 1, idata->flags & PAMNS_MOUNT_PRIVATE, idata);
if (rc == -1) {
pam_syslog(idata->pamh, LOG_ERR,
"Error creating directory %s: %m", dir);
@@ -1445,7 +1447,7 @@ static int ns_setup(struct polydir_s *polyptr,
pam_syslog(idata->pamh, LOG_DEBUG,
"Set namespace for directory %s", polyptr->dir);
- retval = protect_dir(polyptr->dir, 0, 0, idata);
+ retval = protect_dir(polyptr->dir, 0, 0, idata->flags & PAMNS_MOUNT_PRIVATE, idata);
if (retval < 0 && errno != ENOENT) {
pam_syslog(idata->pamh, LOG_ERR, "Polydir %s access error: %m",
@@ -1453,8 +1455,9 @@ static int ns_setup(struct polydir_s *polyptr,
return PAM_SESSION_ERR;
}
- if (retval < 0 && (polyptr->flags & POLYDIR_CREATE)) {
- if (create_polydir(polyptr, idata) != PAM_SUCCESS)
+ if (retval < 0) {
+ if ((polyptr->flags & POLYDIR_CREATE) &&
+ create_polydir(polyptr, idata) != PAM_SUCCESS)
return PAM_SESSION_ERR;
} else {
close(retval);
@@ -1531,6 +1534,22 @@ static int ns_setup(struct polydir_s *polyptr,
goto error_out;
}
+ if (idata->flags & PAMNS_MOUNT_PRIVATE) {
+ /*
+ * Make the polyinstantiated dir private mount. This depends
+ * on making the dir a mount point in the protect_dir call.
+ */
+ if (mount(polyptr->dir, polyptr->dir, NULL, MS_PRIVATE|MS_REC, NULL) < 0) {
+ pam_syslog(idata->pamh, LOG_ERR, "Error making %s a private mount, %m",
+ polyptr->dir);
+ goto error_out;
+ }
+ if (idata->flags & PAMNS_DEBUG)
+ pam_syslog(idata->pamh, LOG_DEBUG,
+ "Polyinstantiated directory %s made as private mount", polyptr->dir);
+
+ }
+
/*
* Bind mount instance directory on top of the polyinstantiated
* directory to provide an instance of polyinstantiated directory
@@ -1871,6 +1890,53 @@ static int ctxt_based_inst_needed(void)
}
#endif
+static int root_shared(void)
+{
+ FILE *f;
+ char *line = NULL;
+ size_t n = 0;
+ int rv = 0;
+
+ f = fopen("/proc/self/mountinfo", "r");
+
+ if (f == NULL)
+ return 0;
+
+ while(getline(&line, &n, f) != -1) {
+ char *l;
+ char *sptr;
+ int i;
+
+ l = line;
+ sptr = NULL;
+ for (i = 0; i < 7; i++) {
+ char *tok;
+
+ tok = strtok_r(l, " ", &sptr);
+ l = NULL;
+ if (tok == NULL)
+ /* next mountinfo line */
+ break;
+
+ if (i == 4 && strcmp(tok, "/") != 0)
+ /* next mountinfo line */
+ break;
+
+ if (i == 6) {
+ if (strncmp(tok, "shared:", 7) == 0)
+ /* there might be more / mounts, the last one counts */
+ rv = 1;
+ else
+ rv = 0;
+ }
+ }
+ }
+
+ free(line);
+ fclose(f);
+
+ return rv;
+}
static int get_user_data(struct instance_data *idata)
{
@@ -1961,12 +2027,15 @@ PAM_EXTERN int pam_sm_open_session(pam_handle_t *pamh, int flags UNUSED,
idata.flags |= PAMNS_USE_DEFAULT_CONTEXT;
idata.flags |= PAMNS_CTXT_BASED_INST;
}
+ if (strcmp(argv[i], "mount_private") == 0) {
+ idata.flags |= PAMNS_MOUNT_PRIVATE;
+ }
if (strcmp(argv[i], "unmnt_remnt") == 0)
unmnt = UNMNT_REMNT;
if (strcmp(argv[i], "unmnt_only") == 0)
unmnt = UNMNT_ONLY;
if (strcmp(argv[i], "require_selinux") == 0) {
- if (~(idata.flags & PAMNS_SELINUX_ENABLED)) {
+ if (!(idata.flags & PAMNS_SELINUX_ENABLED)) {
pam_syslog(idata.pamh, LOG_ERR,
"selinux_required option given and selinux is disabled");
return PAM_SESSION_ERR;
@@ -1980,6 +2049,10 @@ PAM_EXTERN int pam_sm_open_session(pam_handle_t *pamh, int flags UNUSED,
if (retval != PAM_SUCCESS)
return retval;
+ if (root_shared()) {
+ idata.flags |= PAMNS_MOUNT_PRIVATE;
+ }
+
/*
* Parse namespace configuration file which lists directories to
* polyinstantiate, directory where instance directories are to
diff --git a/modules/pam_namespace/pam_namespace.h b/modules/pam_namespace/pam_namespace.h
index da21bd70..c49995c0 100644
--- a/modules/pam_namespace/pam_namespace.h
+++ b/modules/pam_namespace/pam_namespace.h
@@ -74,6 +74,14 @@
#define CLONE_NEWNS 0x00020000 /* Flag to create new namespace */
#endif
+/* mount flags for mount_private */
+#ifndef MS_REC
+#define MS_REC (1<<14)
+#endif
+#ifndef MS_PRIVATE
+#define MS_PRIVATE (1<<18)
+#endif
+
/*
* Module defines
*/
@@ -96,6 +104,7 @@
#define PAMNS_NO_UNMOUNT_ON_CLOSE 0x00010000 /* no unmount at session close */
#define PAMNS_USE_CURRENT_CONTEXT 0x00020000 /* use getcon instead of getexeccon */
#define PAMNS_USE_DEFAULT_CONTEXT 0x00040000 /* use get_default_context instead of getexeccon */
+#define PAMNS_MOUNT_PRIVATE 0x00080000 /* Make the polydir mounts private */
/* polydir flags */
#define POLYDIR_EXCLUSIVE 0x00000001 /* polyinstatiate exclusively for override uids */
diff --git a/modules/pam_nologin/pam_nologin.8.xml b/modules/pam_nologin/pam_nologin.8.xml
index 94c4887b..e4f63707 100644
--- a/modules/pam_nologin/pam_nologin.8.xml
+++ b/modules/pam_nologin/pam_nologin.8.xml
@@ -34,7 +34,7 @@
<para>
pam_nologin is a PAM module that prevents users from logging into
the system when <filename>/var/run/nologin</filename> or
- <filename>/etc/nologin</filename>exists. The contents
+ <filename>/etc/nologin</filename> exists. The contents
of the file are displayed to the user. The pam_nologin module
has no effect on the root user's ability to log in.
</para>
diff --git a/modules/pam_pwhistory/opasswd.c b/modules/pam_pwhistory/opasswd.c
index f045555f..738483ac 100644
--- a/modules/pam_pwhistory/opasswd.c
+++ b/modules/pam_pwhistory/opasswd.c
@@ -181,15 +181,13 @@ check_old_password (pam_handle_t *pamh, const char *user,
fclose (oldpf);
- if (found)
+ if (found && entry.old_passwords)
{
const char delimiters[] = ",";
char *running;
char *oldpass;
- running = strdupa (entry.old_passwords);
- if (running == NULL)
- return PAM_BUF_ERR;
+ running = entry.old_passwords;
do {
oldpass = strsep (&running, delimiters);
@@ -311,8 +309,12 @@ save_old_password (pam_handle_t *pamh, const char *user, uid_t uid,
buflen = DEFAULT_BUFLEN;
buf = malloc (buflen);
if (buf == NULL)
- return PAM_BUF_ERR;
-
+ {
+ fclose (oldpf);
+ fclose (newpf);
+ retval = PAM_BUF_ERR;
+ goto error_opasswd;
+ }
}
buf[0] = '\0';
fgets (buf, buflen - 1, oldpf);
@@ -322,7 +324,12 @@ save_old_password (pam_handle_t *pamh, const char *user, uid_t uid,
cp = buf;
save = strdup (buf); /* Copy to write the original data back. */
if (save == NULL)
- return PAM_BUF_ERR;
+ {
+ fclose (oldpf);
+ fclose (newpf);
+ retval = PAM_BUF_ERR;
+ goto error_opasswd;
+ }
if (n < 1)
break;
@@ -351,31 +358,30 @@ save_old_password (pam_handle_t *pamh, const char *user, uid_t uid,
found = 1;
/* Don't save the current password twice */
- if (entry.old_passwords)
+ if (entry.old_passwords && entry.old_passwords[0] != '\0')
{
- /* there is only one password */
- if (strcmp (entry.old_passwords, oldpass) == 0)
- goto write_old_data;
- else
+ char *last = entry.old_passwords;
+
+ cp = entry.old_passwords;
+ entry.count = 1; /* Don't believe the count */
+ while ((cp = strchr (cp, ',')) != NULL)
{
- /* check last entry */
- cp = strstr (entry.old_passwords, oldpass);
-
- if (cp && strcmp (cp, oldpass) == 0)
- { /* the end is the same, check that there
- is a "," before. */
- --cp;
- if (*cp == ',')
- goto write_old_data;
- }
+ entry.count++;
+ last = ++cp;
}
+
+ /* compare the last password */
+ if (strcmp (last, oldpass) == 0)
+ goto write_old_data;
}
+ else
+ entry.count = 0;
/* increase count. */
entry.count++;
/* check that we don't remember to many passwords. */
- while (entry.count > howmany)
+ while (entry.count > howmany && entry.count > 1)
{
char *p = strpbrk (entry.old_passwords, ",");
if (p != NULL)
@@ -383,12 +389,13 @@ save_old_password (pam_handle_t *pamh, const char *user, uid_t uid,
entry.count--;
}
- if (entry.old_passwords == NULL)
+ if (entry.count == 1)
{
if (asprintf (&out, "%s:%s:%d:%s\n",
entry.user, entry.uid, entry.count,
oldpass) < 0)
{
+ free (save);
retval = PAM_AUTHTOK_ERR;
fclose (oldpf);
fclose (newpf);
@@ -401,6 +408,7 @@ save_old_password (pam_handle_t *pamh, const char *user, uid_t uid,
entry.user, entry.uid, entry.count,
entry.old_passwords, oldpass) < 0)
{
+ free (save);
retval = PAM_AUTHTOK_ERR;
fclose (oldpf);
fclose (newpf);
@@ -493,6 +501,7 @@ save_old_password (pam_handle_t *pamh, const char *user, uid_t uid,
rename (opasswd_tmp, OLD_PASSWORDS_FILE);
error_opasswd:
unlink (opasswd_tmp);
+ free (buf);
return retval;
}
diff --git a/modules/pam_pwhistory/pam_pwhistory.8.xml b/modules/pam_pwhistory/pam_pwhistory.8.xml
index 7696353f..9e1056b2 100644
--- a/modules/pam_pwhistory/pam_pwhistory.8.xml
+++ b/modules/pam_pwhistory/pam_pwhistory.8.xml
@@ -105,7 +105,9 @@
<para>
The last <replaceable>N</replaceable> passwords for each
user are saved in <filename>/etc/security/opasswd</filename>.
- The default is <emphasis>10</emphasis>.
+ The default is <emphasis>10</emphasis>. Value of
+ <emphasis>0</emphasis> makes the module to keep the existing
+ contents of the <filename>opasswd</filename> file unchanged.
</para>
</listitem>
</varlistentry>
diff --git a/modules/pam_pwhistory/pam_pwhistory.c b/modules/pam_pwhistory/pam_pwhistory.c
index 0f6ffca3..9b588958 100644
--- a/modules/pam_pwhistory/pam_pwhistory.c
+++ b/modules/pam_pwhistory/pam_pwhistory.c
@@ -187,12 +187,13 @@ pam_sm_chauthtok (pam_handle_t *pamh, int flags, int argc, const char **argv)
{
retval = pam_get_authtok (pamh, PAM_AUTHTOK, &newpass, NULL);
if (retval != PAM_SUCCESS && retval != PAM_TRY_AGAIN)
- return retval;
+ {
+ if (retval == PAM_CONV_AGAIN)
+ retval = PAM_INCOMPLETE;
+ return retval;
+ }
tries++;
- if (newpass == NULL || retval == PAM_TRY_AGAIN)
- continue;
-
if (options.debug)
{
if (newpass)
@@ -201,12 +202,8 @@ pam_sm_chauthtok (pam_handle_t *pamh, int flags, int argc, const char **argv)
pam_syslog (pamh, LOG_DEBUG, "got no auth token");
}
- if (retval != PAM_SUCCESS || newpass == NULL)
- {
- if (retval == PAM_CONV_AGAIN)
- retval = PAM_INCOMPLETE;
- return retval;
- }
+ if (newpass == NULL || retval == PAM_TRY_AGAIN)
+ continue;
if (options.debug)
pam_syslog (pamh, LOG_DEBUG, "check against old password file");
@@ -219,7 +216,6 @@ pam_sm_chauthtok (pam_handle_t *pamh, int flags, int argc, const char **argv)
newpass = NULL;
/* Remove password item, else following module will use it */
pam_set_item (pamh, PAM_AUTHTOK, (void *) NULL);
- continue;
}
}
@@ -230,8 +226,7 @@ pam_sm_chauthtok (pam_handle_t *pamh, int flags, int argc, const char **argv)
return PAM_MAXTRIES;
}
- /* Remember new password */
- return pam_set_item (pamh, PAM_AUTHTOK, newpass);
+ return PAM_SUCCESS;
}
diff --git a/modules/pam_securetty/pam_securetty.8.xml b/modules/pam_securetty/pam_securetty.8.xml
index dd57705b..48215f5f 100644
--- a/modules/pam_securetty/pam_securetty.8.xml
+++ b/modules/pam_securetty/pam_securetty.8.xml
@@ -33,7 +33,10 @@
user is logging in on a "secure" tty, as defined by the listing
in <filename>/etc/securetty</filename>. pam_securetty also checks
to make sure that <filename>/etc/securetty</filename> is a plain
- file and not world writable.
+ file and not world writable. It will also allow root logins on
+ the tty specified with <option>console=</option> switch on the
+ kernel command line and on ttys from the
+ <filename>/sys/class/tty/console/active</filename>.
</para>
<para>
This module has no effect on non-root users and requires that the
@@ -61,6 +64,19 @@
</para>
</listitem>
</varlistentry>
+ <varlistentry>
+ <term>
+ <option>noconsole</option>
+ </term>
+ <listitem>
+ <para>
+ Do not automatically allow root logins on the kernel console
+ device, as specified on the kernel command line or by the sys file,
+ if it is not also specified in the
+ <filename>/etc/securetty</filename> file.
+ </para>
+ </listitem>
+ </varlistentry>
</variablelist>
</refsect1>
diff --git a/modules/pam_securetty/pam_securetty.c b/modules/pam_securetty/pam_securetty.c
index a3c2010d..4e97ef59 100644
--- a/modules/pam_securetty/pam_securetty.c
+++ b/modules/pam_securetty/pam_securetty.c
@@ -2,6 +2,8 @@
#define SECURETTY_FILE "/etc/securetty"
#define TTY_PREFIX "/dev/"
+#define CMDLINE_FILE "/proc/cmdline"
+#define CONSOLEACTIVE_FILE "/sys/class/tty/console/active"
/*
* by Elliot Lee <sopwith@redhat.com>, Red Hat Software.
@@ -22,6 +24,7 @@
#include <pwd.h>
#include <string.h>
#include <ctype.h>
+#include <limits.h>
/*
* here, we make a definition for the externally accessible function
@@ -38,6 +41,7 @@
#include <security/pam_ext.h>
#define PAM_DEBUG_ARG 0x0001
+#define PAM_NOCONSOLE_ARG 0x0002
static int
_pam_parse (const pam_handle_t *pamh, int argc, const char **argv)
@@ -51,6 +55,8 @@ _pam_parse (const pam_handle_t *pamh, int argc, const char **argv)
if (!strcmp(*argv,"debug"))
ctrl |= PAM_DEBUG_ARG;
+ else if (!strcmp(*argv, "noconsole"))
+ ctrl |= PAM_NOCONSOLE_ARG;
else {
pam_syslog(pamh, LOG_ERR, "unknown option: %s", *argv);
}
@@ -144,6 +150,70 @@ securetty_perform_check (pam_handle_t *pamh, int ctrl,
}
fclose(ttyfile);
+ if (retval && !(ctrl & PAM_NOCONSOLE_ARG)) {
+ FILE *cmdlinefile;
+
+ /* Allow access from the kernel console, if enabled */
+ cmdlinefile = fopen(CMDLINE_FILE, "r");
+
+ if (cmdlinefile != NULL) {
+ char line[LINE_MAX], *p;
+
+ line[0] = 0;
+ fgets(line, sizeof(line), cmdlinefile);
+ fclose(cmdlinefile);
+
+ for (p = line; p; p = strstr(p+1, "console=")) {
+ char *e;
+
+ /* Test whether this is a beginning of a word? */
+ if (p > line && p[-1] != ' ')
+ continue;
+
+ /* Is this our console? */
+ if (strncmp(p + 8, uttyname, strlen(uttyname)))
+ continue;
+
+ /* Is there any garbage after the TTY name? */
+ e = p + 8 + strlen(uttyname);
+ if (*e == ',' || *e == ' ' || *e == '\n' || *e == 0) {
+ retval = 0;
+ break;
+ }
+ }
+ }
+ }
+ if (retval && !(ctrl & PAM_NOCONSOLE_ARG)) {
+ FILE *consoleactivefile;
+
+ /* Allow access from the active console */
+ consoleactivefile = fopen(CONSOLEACTIVE_FILE, "r");
+
+ if (consoleactivefile != NULL) {
+ char line[LINE_MAX], *p, *n;
+
+ line[0] = 0;
+ p = fgets(line, sizeof(line), consoleactivefile);
+ fclose(consoleactivefile);
+
+ if (p) {
+ /* remove the newline character at end */
+ if (line[strlen(line)-1] == '\n')
+ line[strlen(line)-1] = 0;
+
+ for (n = p; n != NULL; p = n+1) {
+ if ((n = strchr(p, ' ')) != NULL)
+ *n = '\0';
+
+ if (strcmp(p, uttyname) == 0) {
+ retval = 0;
+ break;
+ }
+ }
+ }
+ }
+ }
+
if (retval) {
pam_syslog(pamh, LOG_WARNING, "access denied: tty '%s' is not secure !",
uttyname);
diff --git a/modules/pam_selinux/pam_selinux.c b/modules/pam_selinux/pam_selinux.c
index 64fabafd..f99d433a 100644
--- a/modules/pam_selinux/pam_selinux.c
+++ b/modules/pam_selinux/pam_selinux.c
@@ -196,6 +196,7 @@ manual_context (pam_handle_t *pamh, const char *user, int debug)
goto fail_set;
if (context_type_set (new_context, type))
goto fail_set;
+ _pam_drop(type);
}
_pam_drop(response);
@@ -236,19 +237,35 @@ static int mls_range_allowed(pam_handle_t *pamh, security_context_t src, securit
{
struct av_decision avd;
int retval;
- unsigned int bit = CONTEXT__CONTAINS;
- context_t src_context = context_new (src);
- context_t dst_context = context_new (dst);
+ security_class_t class;
+ access_vector_t bit;
+ context_t src_context;
+ context_t dst_context;
+
+ class = string_to_security_class("context");
+ if (!class) {
+ pam_syslog(pamh, LOG_ERR, "Failed to translate security class context. %m");
+ return 0;
+ }
+
+ bit = string_to_av_perm(class, "contains");
+ if (!bit) {
+ pam_syslog(pamh, LOG_ERR, "Failed to translate av perm contains. %m");
+ return 0;
+ }
+
+ src_context = context_new (src);
+ dst_context = context_new (dst);
context_range_set(dst_context, context_range_get(src_context));
if (debug)
pam_syslog(pamh, LOG_NOTICE, "Checking if %s mls range valid for %s", dst, context_str(dst_context));
- retval = security_compute_av(context_str(dst_context), dst, SECCLASS_CONTEXT, bit, &avd);
+ retval = security_compute_av(context_str(dst_context), dst, class, bit, &avd);
context_free(src_context);
context_free(dst_context);
if (retval || ((bit & avd.allowed) != bit))
return 0;
-
+
return 1;
}
@@ -290,6 +307,7 @@ config_context (pam_handle_t *pamh, security_context_t defaultcon, int use_curre
goto fail_set;
if (context_type_set (new_context, type))
goto fail_set;
+ _pam_drop(type);
}
}
_pam_drop(response);
@@ -374,6 +392,7 @@ context_from_env (pam_handle_t *pamh, security_context_t defaultcon, int env_par
int mls_enabled = is_selinux_mls_enabled();
const char *env = NULL;
char *type = NULL;
+ int fail = 1;
if ((new_context = context_new(defaultcon)) == NULL)
goto fail_set;
@@ -434,9 +453,6 @@ context_from_env (pam_handle_t *pamh, security_context_t defaultcon, int env_par
/* Get the string value of the context and see if it is valid. */
if (security_check_context(newcon)) {
pam_syslog(pamh, LOG_NOTICE, "Not a valid security context %s", newcon);
- send_audit_message(pamh, 0, defaultcon, newcon);
- freecon(newcon);
- newcon = NULL;
goto fail_set;
}
@@ -446,16 +462,21 @@ context_from_env (pam_handle_t *pamh, security_context_t defaultcon, int env_par
be checked at setexeccon time */
if (mls_enabled && !mls_range_allowed(pamh, defaultcon, newcon, debug)) {
pam_syslog(pamh, LOG_NOTICE, "Security context %s is not allowed for %s", defaultcon, newcon);
- send_audit_message(pamh, 0, defaultcon, newcon);
- freecon(newcon);
- newcon = NULL;
+
+ goto fail_set;
}
+ fail = 0;
+
fail_set:
free(type);
context_free(my_context);
context_free(new_context);
- send_audit_message(pamh, 0, defaultcon, NULL);
+ if (fail) {
+ send_audit_message(pamh, 0, defaultcon, newcon);
+ freecon(newcon);
+ newcon = NULL;
+ }
return newcon;
}
@@ -642,10 +663,10 @@ pam_sm_open_session(pam_handle_t *pamh, int flags UNUSED,
if (debug)
pam_syslog(pamh, LOG_DEBUG, "Username= %s SELinux User = %s Level= %s",
username, seuser, level);
- free(seuser);
free(level);
}
if (num_contexts > 0) {
+ free(seuser);
default_user_context=strdup(contextlist[0]);
freeconary(contextlist);
if (default_user_context == NULL) {
@@ -672,7 +693,10 @@ pam_sm_open_session(pam_handle_t *pamh, int flags UNUSED,
}
}
else {
- user_context = manual_context(pamh,seuser,debug);
+ if (seuser != NULL) {
+ user_context = manual_context(pamh,seuser,debug);
+ free(seuser);
+ }
if (user_context == NULL) {
pam_syslog (pamh, LOG_ERR, "Unable to get valid context for %s",
username);
diff --git a/modules/pam_sepermit/pam_sepermit.c b/modules/pam_sepermit/pam_sepermit.c
index 8b2360b5..4879b685 100644
--- a/modules/pam_sepermit/pam_sepermit.c
+++ b/modules/pam_sepermit/pam_sepermit.c
@@ -117,6 +117,7 @@ check_running (pam_handle_t *pamh, uid_t uid, int killall, int debug)
max_pids = 256;
pid_table = malloc(max_pids * sizeof (pid_t));
if (!pid_table) {
+ (void)closedir(dir);
pam_syslog(pamh, LOG_CRIT, "Memory allocation error");
return -1;
}
@@ -126,10 +127,15 @@ check_running (pam_handle_t *pamh, uid_t uid, int killall, int debug)
continue;
if (pids == max_pids) {
- if (!(pid_table = realloc(pid_table, 2*pids*sizeof(pid_t)))) {
+ pid_t *npt;
+
+ if (!(npt = realloc(pid_table, 2*pids*sizeof(pid_t)))) {
+ free(pid_table);
+ (void)closedir(dir);
pam_syslog(pamh, LOG_CRIT, "Memory allocation error");
return -1;
}
+ pid_table = npt;
max_pids *= 2;
}
pid_table[pids++] = pid;
@@ -175,8 +181,8 @@ sepermit_unlock(pam_handle_t *pamh, void *plockfd, int error_status UNUSED)
while(check_running(pamh, lockfd->uid, 1, lockfd->debug) > 0)
continue;
- fcntl(lockfd->fd, F_SETLK, &fl);
- close(lockfd->fd);
+ (void)fcntl(lockfd->fd, F_SETLK, &fl);
+ (void)close(lockfd->fd);
free(lockfd);
}
diff --git a/modules/pam_stress/pam_stress.c b/modules/pam_stress/pam_stress.c
index 01587fea..b75a597d 100644
--- a/modules/pam_stress/pam_stress.c
+++ b/modules/pam_stress/pam_stress.c
@@ -116,7 +116,7 @@ _pam_parse (const pam_handle_t *pamh, int argc, const char **argv)
}
static int converse(pam_handle_t *pamh, int nargs
- , struct pam_message **message
+ , const struct pam_message **message
, struct pam_response **response)
{
int retval;
@@ -126,8 +126,7 @@ static int converse(pam_handle_t *pamh, int nargs
retval = pam_get_item(pamh,PAM_CONV,&void_conv);
conv = void_conv;
if (retval == PAM_SUCCESS && conv) {
- retval = conv->conv(nargs, (const struct pam_message **) message
- , response, conv->appdata_ptr);
+ retval = conv->conv(nargs, message, response, conv->appdata_ptr);
if (retval != PAM_SUCCESS) {
pam_syslog(pamh, LOG_ERR, "converse returned %d: %s",
retval, pam_strerror(pamh, retval));
@@ -159,7 +158,8 @@ static int stress_get_password(pam_handle_t *pamh, int flags
pam_syslog(pamh, LOG_WARNING, "no forwarded password");
return PAM_PERM_DENIED;
} else { /* we will have to get one */
- struct pam_message msg[1],*pmsg[1];
+ struct pam_message msg[1];
+ const struct pam_message *pmsg[1];
struct pam_response *resp;
int retval;
@@ -412,7 +412,8 @@ int pam_sm_chauthtok(pam_handle_t *pamh, int flags,
return PAM_SUCCESS;
} else if (flags & PAM_UPDATE_AUTHTOK) { /* second call */
- struct pam_message msg[3],*pmsg[3];
+ struct pam_message msg[3];
+ const struct pam_message *pmsg[3];
struct pam_response *resp;
const void *text;
char *txt=NULL;
diff --git a/modules/pam_tally2/pam_tally2.8.xml b/modules/pam_tally2/pam_tally2.8.xml
index 4ad529fd..5fecea24 100644
--- a/modules/pam_tally2/pam_tally2.8.xml
+++ b/modules/pam_tally2/pam_tally2.8.xml
@@ -238,17 +238,6 @@
</varlistentry>
<varlistentry>
<term>
- <option>no_lock_time</option>
- </term>
- <listitem>
- <para>
- Do not use the .fail_locktime field in
- <filename>/var/log/faillog</filename> for this user.
- </para>
- </listitem>
- </varlistentry>
- <varlistentry>
- <term>
<option>even_deny_root</option>
</term>
<listitem>
@@ -446,4 +435,3 @@ session optional pam_mail.so standard
</refsect1>
</refentry>
-
diff --git a/modules/pam_time/pam_time.c b/modules/pam_time/pam_time.c
index 7e418808..dff4a6da 100644
--- a/modules/pam_time/pam_time.c
+++ b/modules/pam_time/pam_time.c
@@ -4,6 +4,7 @@
* Written by Andrew Morgan <morgan@linux.kernel.org> 1996/6/22
* (File syntax and much other inspiration from the shadow package
* shadow-960129)
+ * Field parsing rewritten by Tomas Mraz <tm@t8m.info>
*/
#include "config.h"
@@ -23,7 +24,7 @@
#include <netdb.h>
#ifdef HAVE_LIBAUDIT
-#include <libaudit.h>
+#include <libaudit.h>
#endif
#define PAM_TIME_BUFLEN 1000
@@ -79,163 +80,157 @@ _pam_parse (const pam_handle_t *pamh, int argc, const char **argv)
/* --- static functions for checking whether the user should be let in --- */
-static void
-shift_bytes(char *mem, int from, int by)
+static char *
+shift_buf(char *mem, int from)
{
- while (by-- > 0) {
- *mem = mem[from];
+ char *start = mem;
+ while ((*mem = mem[from]) != '\0')
++mem;
+ memset(mem, '\0', PAM_TIME_BUFLEN - (mem - start));
+ return mem;
+}
+
+static void
+trim_spaces(char *buf, char *from)
+{
+ while (from > buf) {
+ --from;
+ if (*from == ' ')
+ *from = '\0';
+ else
+ break;
}
}
+#define STATE_NL 0 /* new line starting */
+#define STATE_COMMENT 1 /* inside comment */
+#define STATE_FIELD 2 /* field following */
+#define STATE_EOF 3 /* end of file or error */
+
static int
-read_field(const pam_handle_t *pamh, int fd, char **buf, int *from, int *to)
+read_field(const pam_handle_t *pamh, int fd, char **buf, int *from, int *state)
{
- /* is buf set ? */
+ char *to;
+ char *src;
+ int i;
+ char c;
+ int onspace;
+ /* is buf set ? */
if (! *buf) {
- *buf = (char *) malloc(PAM_TIME_BUFLEN);
+ *buf = (char *) calloc(1, PAM_TIME_BUFLEN+1);
if (! *buf) {
pam_syslog(pamh, LOG_ERR, "out of memory");
D(("no memory"));
+ *state = STATE_EOF;
return -1;
}
- *from = *to = 0;
+ *from = 0;
+ *state = STATE_NL;
fd = open(PAM_TIME_CONF, O_RDONLY);
+ if (fd < 0) {
+ pam_syslog(pamh, LOG_ERR, "error opening %s: %m", PAM_TIME_CONF);
+ _pam_drop(*buf);
+ *state = STATE_EOF;
+ return -1;
+ }
}
+
- /* do we have a file open ? return error */
-
- if (fd < 0 && *to <= 0) {
- pam_syslog(pamh, LOG_ERR, "error opening %s: %m", PAM_TIME_CONF);
- memset(*buf, 0, PAM_TIME_BUFLEN);
- _pam_drop(*buf);
- return -1;
- }
-
- /* check if there was a newline last time */
-
- if ((*to > *from) && (*to > 0)
- && ((*buf)[*from] == '\0')) { /* previous line ended */
- (*from)++;
- (*buf)[0] = '\0';
- return fd;
- }
-
- /* ready for more data: first shift the buffer's remaining data */
-
- *to -= *from;
- shift_bytes(*buf, *from, *to);
- *from = 0;
- (*buf)[*to] = '\0';
-
- while (fd >= 0 && *to < PAM_TIME_BUFLEN) {
- int i;
-
- /* now try to fill the remainder of the buffer */
+ if (*from > 0)
+ to = shift_buf(*buf, *from);
+ else
+ to = *buf;
- i = read(fd, *to + *buf, PAM_TIME_BUFLEN - *to);
+ while (fd != -1 && to - *buf < PAM_TIME_BUFLEN) {
+ i = pam_modutil_read(fd, to, PAM_TIME_BUFLEN - (to - *buf));
if (i < 0) {
pam_syslog(pamh, LOG_ERR, "error reading %s: %m", PAM_TIME_CONF);
close(fd);
+ memset(*buf, 0, PAM_TIME_BUFLEN);
+ _pam_drop(*buf);
+ *state = STATE_EOF;
return -1;
} else if (!i) {
close(fd);
fd = -1; /* end of file reached */
- } else
- *to += i;
+ }
- /*
- * contract the buffer. Delete any comments, and replace all
- * multiple spaces with single commas
- */
+ to += i;
+ }
- i = 0;
-#ifdef DEBUG_DUMP
- D(("buffer=<%s>",*buf));
-#endif
- while (i < *to) {
- if ((*buf)[i] == ',') {
- int j;
-
- for (j=++i; j<*to && (*buf)[j] == ','; ++j);
- if (j!=i) {
- shift_bytes(i + (*buf), j-i, (*to) - j);
- *to -= j-i;
- }
- }
- switch ((*buf)[i]) {
- int j,c;
- case '#':
- c = 0;
- for (j=i; j < *to && (c = (*buf)[j]) != '\n'; ++j);
- if (j >= *to) {
- (*buf)[*to = ++i] = '\0';
- } else if (c == '\n') {
- shift_bytes(i + (*buf), j-i, (*to) - j);
- *to -= j-i;
- ++i;
- } else {
- pam_syslog(pamh, LOG_CRIT,
- "internal error in file %s at line %d",
- __FILE__, __LINE__);
- close(fd);
- return -1;
- }
- break;
- case '\\':
- if ((*buf)[i+1] == '\n') {
- shift_bytes(i + *buf, 2, *to - (i+2));
- *to -= 2;
- } else {
- ++i; /* we don't escape non-newline characters */
- }
- break;
- case '!':
- case ' ':
- case '\t':
- if ((*buf)[i] != '!')
- (*buf)[i] = ',';
- /* delete any trailing spaces */
- for (j=++i; j < *to && ( (c = (*buf)[j]) == ' '
- || c == '\t' ); ++j);
- shift_bytes(i + *buf, j-i, (*to)-j );
- *to -= j-i;
- break;
- default:
- ++i;
- }
- }
+ if (to == *buf) {
+ /* nothing previously in buf, nothing read */
+ _pam_drop(*buf);
+ *state = STATE_EOF;
+ return -1;
}
- (*buf)[*to] = '\0';
+ memset(to, '\0', PAM_TIME_BUFLEN - (to - *buf));
- /* now return the next field (set the from/to markers) */
- {
- int i;
+ to = *buf;
+ onspace = 1; /* delete any leading spaces */
+
+ for (src = to; (c=*src) != '\0'; ++src) {
+ if (*state == STATE_COMMENT && c != '\n') {
+ continue;
+ }
+
+ switch (c) {
+ case '\n':
+ *state = STATE_NL;
+ *to = '\0';
+ *from = (src - *buf) + 1;
+ trim_spaces(*buf, to);
+ return fd;
+
+ case '\t':
+ case ' ':
+ if (!onspace) {
+ onspace = 1;
+ *to++ = ' ';
+ }
+ break;
+
+ case '!':
+ onspace = 1; /* ignore following spaces */
+ *to++ = '!';
+ break;
- for (i=0; i<*to; ++i) {
- switch ((*buf)[i]) {
case '#':
- case '\n': /* end of the line/file */
- (*buf)[i] = '\0';
- *from = i;
+ *state = STATE_COMMENT;
+ break;
+
+ case FIELD_SEPARATOR:
+ *state = STATE_FIELD;
+ *to = '\0';
+ *from = (src - *buf) + 1;
+ trim_spaces(*buf, to);
return fd;
- case FIELD_SEPARATOR: /* end of the field */
- (*buf)[i] = '\0';
- *from = ++i;
- return fd;
- }
+
+ case '\\':
+ if (src[1] == '\n') {
+ ++src; /* skip it */
+ break;
+ }
+ default:
+ *to++ = c;
+ onspace = 0;
}
- *from = i;
- (*buf)[*from] = '\0';
+ if (src > to)
+ *src = '\0'; /* clearing */
}
- if (*to <= 0) {
- D(("[end of text]"));
- *buf = NULL;
+ if (*state != STATE_COMMENT) {
+ *state = STATE_COMMENT;
+ pam_syslog(pamh, LOG_ERR, "field too long - ignored");
+ **buf = '\0';
+ } else {
+ *to = '\0';
+ trim_spaces(*buf, to);
}
+ *from = 0;
return fd;
}
@@ -511,7 +506,7 @@ static int
check_account(pam_handle_t *pamh, const char *service,
const char *tty, const char *user)
{
- int from=0,to=0,fd=-1;
+ int from=0, state=STATE_NL, fd=-1;
char *buffer=NULL;
int count=0;
TIME here_and_now;
@@ -523,23 +518,28 @@ check_account(pam_handle_t *pamh, const char *service,
/* here we get the service name field */
- fd = read_field(pamh, fd, &buffer, &from, &to);
-
+ fd = read_field(pamh, fd, &buffer, &from, &state);
if (!buffer || !buffer[0]) {
/* empty line .. ? */
continue;
}
++count;
+ if (state != STATE_FIELD) {
+ pam_syslog(pamh, LOG_ERR,
+ "%s: malformed rule #%d", PAM_TIME_CONF, count);
+ continue;
+ }
+
good = logic_field(pamh, service, buffer, count, is_same);
D(("with service: %s", good ? "passes":"fails" ));
/* here we get the terminal name field */
- fd = read_field(pamh, fd, &buffer, &from, &to);
- if (!buffer || !buffer[0]) {
+ fd = read_field(pamh, fd, &buffer, &from, &state);
+ if (state != STATE_FIELD) {
pam_syslog(pamh, LOG_ERR,
- "%s: no tty entry #%d", PAM_TIME_CONF, count);
+ "%s: malformed rule #%d", PAM_TIME_CONF, count);
continue;
}
good &= logic_field(pamh, tty, buffer, count, is_same);
@@ -547,10 +547,10 @@ check_account(pam_handle_t *pamh, const char *service,
/* here we get the username field */
- fd = read_field(pamh, fd, &buffer, &from, &to);
- if (!buffer || !buffer[0]) {
+ fd = read_field(pamh, fd, &buffer, &from, &state);
+ if (state != STATE_FIELD) {
pam_syslog(pamh, LOG_ERR,
- "%s: no user entry #%d", PAM_TIME_CONF, count);
+ "%s: malformed rule #%d", PAM_TIME_CONF, count);
continue;
}
/* If buffer starts with @, we are using netgroups */
@@ -562,23 +562,16 @@ check_account(pam_handle_t *pamh, const char *service,
/* here we get the time field */
- fd = read_field(pamh, fd, &buffer, &from, &to);
- if (!buffer || !buffer[0]) {
+ fd = read_field(pamh, fd, &buffer, &from, &state);
+ if (state == STATE_FIELD) {
pam_syslog(pamh, LOG_ERR,
- "%s: no time entry #%d", PAM_TIME_CONF, count);
+ "%s: poorly terminated rule #%d", PAM_TIME_CONF, count);
continue;
}
intime = logic_field(pamh, &here_and_now, buffer, count, check_time);
D(("with time: %s", intime ? "passes":"fails" ));
- fd = read_field(pamh, fd, &buffer, &from, &to);
- if (buffer && buffer[0]) {
- pam_syslog(pamh, LOG_ERR,
- "%s: poorly terminated rule #%d", PAM_TIME_CONF, count);
- continue;
- }
-
if (good && !intime) {
/*
* for security parse whole file.. also need to ensure
@@ -588,7 +581,7 @@ check_account(pam_handle_t *pamh, const char *service,
} else {
D(("rule passed"));
}
- } while (buffer);
+ } while (state != STATE_EOF);
return retval;
}
diff --git a/modules/pam_timestamp/pam_timestamp.c b/modules/pam_timestamp/pam_timestamp.c
index 26876769..51937333 100644
--- a/modules/pam_timestamp/pam_timestamp.c
+++ b/modules/pam_timestamp/pam_timestamp.c
@@ -483,6 +483,7 @@ pam_sm_authenticate(pam_handle_t *pamh, int flags, int argc, const char **argv)
pam_syslog(pamh, LOG_NOTICE, "timestamp file `%s' is "
"corrupted", path);
close(fd);
+ free(mac);
free(message);
return PAM_AUTH_ERR;
}
@@ -636,6 +637,8 @@ pam_sm_open_session(pam_handle_t *pamh, int flags UNUSED, int argc, const char *
"error setting ownership of `%s': %m",
path);
}
+ close(fd);
+ free(text);
return PAM_SESSION_ERR;
}
@@ -684,7 +687,7 @@ struct pam_module _pam_timestamp_modstruct = {
int
main(int argc, char **argv)
{
- int i, pretval = -1, retval = 0, dflag = 0, kflag = 0;
+ int i, retval = 0, dflag = 0, kflag = 0;
const char *target_user = NULL, *user = NULL, *tty = NULL;
struct passwd *pwd;
struct timeval tv;
@@ -826,7 +829,6 @@ main(int argc, char **argv)
select(STDOUT_FILENO + 1,
NULL, NULL, &write_fds,
&tv);
- pretval = retval;
retval = 0;
}
} while (dflag > 0);
diff --git a/modules/pam_unix/Makefile.am b/modules/pam_unix/Makefile.am
index 44b37e94..ea5a7318 100644
--- a/modules/pam_unix/Makefile.am
+++ b/modules/pam_unix/Makefile.am
@@ -1,5 +1,5 @@
#
-# Copyright (c) 2005, 2006, 2009 Thorsten Kukuk <kukuk@suse.de>
+# Copyright (c) 2005, 2006, 2009, 2011 Thorsten Kukuk <kukuk@suse.de>
#
CLEANFILES = *~
@@ -18,7 +18,8 @@ secureconfdir = $(SCONFIGDIR)
AM_CFLAGS = -I$(top_srcdir)/libpam/include -I$(top_srcdir)/libpamc/include \
-DCHKPWD_HELPER=\"$(sbindir)/unix_chkpwd\" \
- -DUPDATE_HELPER=\"$(sbindir)/unix_update\"
+ -DUPDATE_HELPER=\"$(sbindir)/unix_update\" \
+ $(NIS_CFLAGS)
if HAVE_LIBSELINUX
AM_CFLAGS += -D"WITH_SELINUX"
@@ -28,8 +29,8 @@ pam_unix_la_LDFLAGS = -no-undefined -avoid-version -module
if HAVE_VERSIONING
pam_unix_la_LDFLAGS += -Wl,--version-script=$(srcdir)/../modules.map
endif
-pam_unix_la_LIBADD = @LIBNSL@ -L$(top_builddir)/libpam -lpam \
- @LIBCRYPT@ @LIBSELINUX@
+pam_unix_la_LIBADD = -L$(top_builddir)/libpam -lpam \
+ @LIBCRYPT@ @LIBSELINUX@ $(NIS_LIBS)
securelib_LTLIBRARIES = pam_unix.la
diff --git a/modules/pam_unix/md5.c b/modules/pam_unix/md5.c
index 7ee9ed00..1c622ecd 100644
--- a/modules/pam_unix/md5.c
+++ b/modules/pam_unix/md5.c
@@ -148,7 +148,7 @@ void MD5Name(MD5Final)(unsigned char digest[16], struct MD5Context *ctx)
MD5Name(MD5Transform)(ctx->buf, (uint32 *) ctx->in);
byteReverse((unsigned char *) ctx->buf, 4);
memcpy(digest, ctx->buf, 16);
- memset(ctx, 0, sizeof(ctx)); /* In case it's sensitive */
+ memset(ctx, 0, sizeof(*ctx)); /* In case it's sensitive */
}
#ifndef ASM_MD5
diff --git a/modules/pam_unix/pam_unix_passwd.c b/modules/pam_unix/pam_unix_passwd.c
index 320bc547..631df318 100644
--- a/modules/pam_unix/pam_unix_passwd.c
+++ b/modules/pam_unix/pam_unix_passwd.c
@@ -54,13 +54,6 @@
#include <ctype.h>
#include <sys/time.h>
#include <sys/stat.h>
-#include <rpc/rpc.h>
-#ifdef HAVE_RPCSVC_YP_PROT_H
-#include <rpcsvc/yp_prot.h>
-#endif
-#ifdef HAVE_RPCSVC_YPCLNT_H
-#include <rpcsvc/ypclnt.h>
-#endif
#include <signal.h>
#include <errno.h>
@@ -76,16 +69,33 @@
#include <security/pam_ext.h>
#include <security/pam_modutil.h>
-#include "yppasswd.h"
#include "md5.h"
#include "support.h"
#include "passverify.h"
#include "bigcrypt.h"
-#if !((__GLIBC__ == 2) && (__GLIBC_MINOR__ >= 1))
+#if (HAVE_YP_GET_DEFAULT_DOMAIN || HAVE_GETDOMAINNAME) && HAVE_YP_MASTER
+# define HAVE_NIS
+#endif
+
+#ifdef HAVE_NIS
+# include <rpc/rpc.h>
+
+# if HAVE_RPCSVC_YP_PROT_H
+# include <rpcsvc/yp_prot.h>
+# endif
+
+# if HAVE_RPCSVC_YPCLNT_H
+# include <rpcsvc/ypclnt.h>
+# endif
+
+# include "yppasswd.h"
+
+# if !HAVE_DECL_GETRPCPORT
extern int getrpcport(const char *host, unsigned long prognum,
unsigned long versnum, unsigned int proto);
-#endif /* GNU libc 2.1 */
+# endif /* GNU libc 2.1 */
+#endif
/*
How it works:
@@ -102,9 +112,9 @@ extern int getrpcport(const char *host, unsigned long prognum,
#define MAX_PASSWD_TRIES 3
+#ifdef HAVE_NIS
static char *getNISserver(pam_handle_t *pamh, unsigned int ctrl)
{
-#if (defined(HAVE_YP_GET_DEFAULT_DOMAIN) || defined(HAVE_GETDOMAINNAME)) && defined(HAVE_YP_MASTER)
char *master;
char *domainname;
int port, err;
@@ -151,14 +161,8 @@ static char *getNISserver(pam_handle_t *pamh, unsigned int ctrl)
master, port);
}
return master;
-#else
- if (on(UNIX_DEBUG, ctrl)) {
- pam_syslog(pamh, LOG_DEBUG, "getNISserver: No NIS support available");
- }
-
- return NULL;
-#endif
}
+#endif
#ifdef WITH_SELINUX
@@ -326,6 +330,7 @@ static int _do_setpass(pam_handle_t* pamh, const char *forwho,
}
if (on(UNIX_NIS, ctrl) && _unix_comesfromsource(pamh, forwho, 0, 1)) {
+#ifdef HAVE_NIS
if ((master=getNISserver(pamh, ctrl)) != NULL) {
struct timeval timeout;
struct yppasswd yppwd;
@@ -391,6 +396,13 @@ static int _do_setpass(pam_handle_t* pamh, const char *forwho,
} else {
retval = PAM_TRY_AGAIN;
}
+#else
+ if (on(UNIX_DEBUG, ctrl)) {
+ pam_syslog(pamh, LOG_DEBUG, "No NIS support available");
+ }
+
+ retval = PAM_TRY_AGAIN;
+#endif
}
if (_unix_comesfromsource(pamh, forwho, 1, 0)) {
diff --git a/modules/pam_unix/support.c b/modules/pam_unix/support.c
index bddafd4b..cc350e58 100644
--- a/modules/pam_unix/support.c
+++ b/modules/pam_unix/support.c
@@ -83,7 +83,7 @@ int _set_ctrl(pam_handle_t *pamh, int flags, int *remember, int *rounds,
}
/* now parse the arguments to this module */
- while (argc-- > 0) {
+ for (; argc-- > 0; ++argv) {
int j;
D(("pam_unix arg: %s", *argv));
@@ -99,24 +99,37 @@ int _set_ctrl(pam_handle_t *pamh, int flags, int *remember, int *rounds,
pam_syslog(pamh, LOG_ERR,
"unrecognized option [%s]", *argv);
} else {
- ctrl &= unix_args[j].mask; /* for turning things off */
- ctrl |= unix_args[j].flag; /* for turning things on */
-
/* special cases */
- if (remember != NULL && j == UNIX_REMEMBER_PASSWD) {
+ if (j == UNIX_REMEMBER_PASSWD) {
+ if (remember == NULL) {
+ pam_syslog(pamh, LOG_ERR,
+ "option remember not allowed for this module type");
+ continue;
+ }
*remember = strtol(*argv + 9, NULL, 10);
if ((*remember == INT_MIN) || (*remember == INT_MAX))
*remember = -1;
if (*remember > 400)
*remember = 400;
- } else if (pass_min_len && j == UNIX_MIN_PASS_LEN) {
+ } else if (j == UNIX_MIN_PASS_LEN) {
+ if (pass_min_len == NULL) {
+ pam_syslog(pamh, LOG_ERR,
+ "option minlen not allowed for this module type");
+ continue;
+ }
*pass_min_len = atoi(*argv + 7);
- }
- if (rounds != NULL && j == UNIX_ALGO_ROUNDS)
+ } else if (j == UNIX_ALGO_ROUNDS) {
+ if (rounds == NULL) {
+ pam_syslog(pamh, LOG_ERR,
+ "option rounds not allowed for this module type");
+ continue;
+ }
*rounds = strtol(*argv + 7, NULL, 10);
- }
+ }
- ++argv; /* step to next argument */
+ ctrl &= unix_args[j].mask; /* for turning things off */
+ ctrl |= unix_args[j].flag; /* for turning things on */
+ }
}
if (UNIX_DES_CRYPT(ctrl)
@@ -132,7 +145,7 @@ int _set_ctrl(pam_handle_t *pamh, int flags, int *remember, int *rounds,
}
/* Set default rounds for blowfish */
- if (on(UNIX_BLOWFISH_PASS, ctrl) && off(UNIX_ALGO_ROUNDS, ctrl)) {
+ if (on(UNIX_BLOWFISH_PASS, ctrl) && off(UNIX_ALGO_ROUNDS, ctrl) && rounds != NULL) {
*rounds = 5;
set(UNIX_ALGO_ROUNDS, ctrl);
}
@@ -493,14 +506,12 @@ static int _unix_run_helper_binary(pam_handle_t *pamh, const char *passwd,
if (passwd != NULL) { /* send the password to the child */
if (write(fds[1], passwd, strlen(passwd)+1) == -1) {
pam_syslog (pamh, LOG_ERR, "Cannot send password to helper: %m");
- close(fds[1]);
retval = PAM_AUTH_ERR;
}
passwd = NULL;
} else { /* blank password */
if (write(fds[1], "", 1) == -1) {
pam_syslog (pamh, LOG_ERR, "Cannot send password to helper: %m");
- close(fds[1]);
retval = PAM_AUTH_ERR;
}
}
diff --git a/modules/pam_unix/yppasswd_xdr.c b/modules/pam_unix/yppasswd_xdr.c
index 0b95b82b..f2b86a56 100644
--- a/modules/pam_unix/yppasswd_xdr.c
+++ b/modules/pam_unix/yppasswd_xdr.c
@@ -12,6 +12,8 @@
#include "config.h"
+#ifdef HAVE_RPC_RPC_H
+
#include <rpc/rpc.h>
#include "yppasswd.h"
@@ -34,3 +36,5 @@ xdr_yppasswd(XDR * xdrs, yppasswd * objp)
return xdr_string(xdrs, &objp->oldpass, ~0)
&& xdr_xpasswd(xdrs, &objp->newpw);
}
+
+#endif
diff --git a/modules/pam_xauth/pam_xauth.c b/modules/pam_xauth/pam_xauth.c
index a64ae89f..88624b1c 100644
--- a/modules/pam_xauth/pam_xauth.c
+++ b/modules/pam_xauth/pam_xauth.c
@@ -459,24 +459,33 @@ pam_sm_open_session (pam_handle_t *pamh, int flags UNUSED,
goto cleanup;
}
- /* Check that both users are amenable to this. By default, this
- * boils down to this policy:
- * export(ruser=root): only if <user> is listed in .xauth/export
- * export(ruser=*) if <user> is listed in .xauth/export, or
- * if .xauth/export does not exist
- * import(user=*): if <ruser> is listed in .xauth/import, or
- * if .xauth/import does not exist */
- i = (getuid() != 0 || tpwd->pw_uid == 0) ? PAM_SUCCESS : PAM_PERM_DENIED;
- i = check_acl(pamh, "export", rpwd->pw_name, user, i, debug);
- if (i != PAM_SUCCESS) {
- retval = PAM_SESSION_ERR;
- goto cleanup;
- }
- i = PAM_SUCCESS;
- i = check_acl(pamh, "import", user, rpwd->pw_name, i, debug);
- if (i != PAM_SUCCESS) {
- retval = PAM_SESSION_ERR;
- goto cleanup;
+
+ /* If current user and the target user are the same, don't
+ check the ACL list, but forward X11 */
+ if (strcmp (rpwd->pw_name, tpwd->pw_name) != 0) {
+
+ /* Check that both users are amenable to this. By default, this
+ * boils down to this policy:
+ * export(ruser=root): only if <user> is listed in .xauth/export
+ * export(ruser=*) if <user> is listed in .xauth/export, or
+ * if .xauth/export does not exist
+ * import(user=*): if <ruser> is listed in .xauth/import, or
+ * if .xauth/import does not exist */
+ i = (getuid() != 0 || tpwd->pw_uid == 0) ? PAM_SUCCESS : PAM_PERM_DENIED;
+ i = check_acl(pamh, "export", rpwd->pw_name, user, i, debug);
+ if (i != PAM_SUCCESS) {
+ retval = PAM_SESSION_ERR;
+ goto cleanup;
+ }
+ i = PAM_SUCCESS;
+ i = check_acl(pamh, "import", user, rpwd->pw_name, i, debug);
+ if (i != PAM_SUCCESS) {
+ retval = PAM_SESSION_ERR;
+ goto cleanup;
+ }
+ } else {
+ if (debug)
+ pam_syslog (pamh, LOG_DEBUG, "current and target user are the same, forward X11");
}
/* Figure out where the source user's .Xauthority file is. */