diff options
author | Steve Langasek <steve.langasek@ubuntu.com> | 2019-01-03 21:13:04 -0800 |
---|---|---|
committer | Steve Langasek <steve.langasek@ubuntu.com> | 2019-01-03 21:13:04 -0800 |
commit | c55c14c5c6762139ec6695d84ea0e2e917da5264 (patch) | |
tree | 9e6119760c93841b2bc3e05680ac9e4e15ae9c25 /modules | |
parent | f3c0273b7bd2d7fdcac3fe3604cedd82afc57f49 (diff) | |
parent | fc772e7236a7aea9c9c26b0be2ee6f3ed8ae444a (diff) |
New upstream version 1.1.5
Diffstat (limited to 'modules')
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><min_uid></replaceable><emphasis + remap='B'>:</emphasis><replaceable><max_uid></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><min_gid></replaceable><emphasis + remap='B'>:</emphasis><replaceable><max_gid></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><gid></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. */ |