diff options
Diffstat (limited to 'modules/pam_limits/pam_limits.c')
-rw-r--r-- | modules/pam_limits/pam_limits.c | 98 |
1 files changed, 87 insertions, 11 deletions
diff --git a/modules/pam_limits/pam_limits.c b/modules/pam_limits/pam_limits.c index b791cdce..7cc45d77 100644 --- a/modules/pam_limits/pam_limits.c +++ b/modules/pam_limits/pam_limits.c @@ -28,6 +28,7 @@ #include <syslog.h> #include <stdarg.h> #include <signal.h> +#include <sys/prctl.h> #include <sys/types.h> #include <sys/stat.h> #include <sys/resource.h> @@ -49,7 +50,7 @@ /* Module defines */ #define LINE_LENGTH 1024 -#define LIMITS_DEF_USER 0 /* limit was set by an user entry */ +#define LIMITS_DEF_USER 0 /* limit was set by a 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 all entry */ @@ -88,6 +89,7 @@ struct pam_limit_s { int flag_numsyslogins; /* whether to limit logins only for a specific user or to count all logins */ int priority; /* the priority to run user process with */ + int nonewprivs; /* whether to prctl(PR_SET_NO_NEW_PRIVS) */ struct user_limits_struct limits[RLIM_NLIMITS]; const char *conf_file; int utmp_after_pam_call; @@ -98,6 +100,7 @@ struct pam_limit_s { #define LIMIT_NUMSYSLOGINS RLIM_NLIMITS+2 #define LIMIT_PRI RLIM_NLIMITS+3 +#define LIMIT_NONEWPRIVS RLIM_NLIMITS+4 #define LIMIT_SOFT 1 #define LIMIT_HARD 2 @@ -484,6 +487,41 @@ static int init_limits(pam_handle_t *pamh, struct pam_limit_s *pl, int ctrl) return retval; } +/* + * Read the contents of <pathname> and return it in *valuep + * return 1 if conversion succeeds, result is in *valuep + * return 0 if conversion fails, *valuep is untouched. + */ +static int +value_from_file(const char *pathname, rlim_t *valuep) +{ + char buf[128]; + FILE *fp; + int retval; + + retval = 0; + + if ((fp = fopen(pathname, "r")) != NULL) { + if (fgets(buf, sizeof(buf), fp) != NULL) { + char *endptr; + unsigned long long value; + + errno = 0; + value = strtoull(buf, &endptr, 10); + if (endptr != buf && + (value != ULLONG_MAX || errno == 0) && + (unsigned long long) (rlim_t) value == value) { + *valuep = (rlim_t) value; + retval = 1; + } + } + + fclose(fp); + } + + return retval; +} + static void process_limit (const pam_handle_t *pamh, int source, const char *lim_type, const char *lim_item, const char *lim_value, @@ -551,6 +589,8 @@ process_limit (const pam_handle_t *pamh, int source, const char *lim_type, pl->flag_numsyslogins = 1; } else if (strcmp(lim_item, "priority") == 0) { limit_item = LIMIT_PRI; + } else if (strcmp(lim_item, "nonewprivs") == 0) { + limit_item = LIMIT_NONEWPRIVS; } else { pam_syslog(pamh, LOG_DEBUG, "unknown limit item '%s'", lim_item); return; @@ -562,11 +602,23 @@ process_limit (const pam_handle_t *pamh, int source, const char *lim_type, limit_type=LIMIT_HARD; else if (strcmp(lim_type,"-")==0) limit_type=LIMIT_SOFT | LIMIT_HARD; - else if (limit_item != LIMIT_LOGIN && limit_item != LIMIT_NUMSYSLOGINS) { + else if (limit_item != LIMIT_LOGIN && limit_item != LIMIT_NUMSYSLOGINS + && limit_item != LIMIT_NONEWPRIVS) { pam_syslog(pamh, LOG_DEBUG, "unknown limit type '%s'", lim_type); return; } - if (limit_item != LIMIT_PRI + if (limit_item == LIMIT_NONEWPRIVS) { + /* just require a bool-style 0 or 1 */ + if (strcmp(lim_value, "0") == 0) { + int_value = 0; + } else if (strcmp(lim_value, "1") == 0) { + int_value = 1; + } else { + pam_syslog(pamh, LOG_DEBUG, + "wrong limit value '%s' for limit type '%s'", + lim_value, lim_type); + } + } else if (limit_item != LIMIT_PRI #ifdef RLIMIT_NICE && limit_item != RLIMIT_NICE #endif @@ -649,11 +701,26 @@ process_limit (const pam_handle_t *pamh, int source, const char *lim_type, rlimit_value = 20 - int_value; break; #endif + case RLIMIT_NOFILE: + /* + * If nofile is to be set to "unlimited", try to set it to + * the value in /proc/sys/fs/nr_open instead. + */ + if (rlimit_value == RLIM_INFINITY) { + if (!value_from_file("/proc/sys/fs/nr_open", &rlimit_value)) + pam_syslog(pamh, LOG_WARNING, + "Cannot set \"nofile\" to a sensible value"); + else if (ctrl & PAM_DEBUG_ARG) + pam_syslog(pamh, LOG_DEBUG, "Setting \"nofile\" limit to %llu", + (unsigned long long) rlimit_value); + } + break; } if ( (limit_item != LIMIT_LOGIN) && (limit_item != LIMIT_NUMSYSLOGINS) - && (limit_item != LIMIT_PRI) ) { + && (limit_item != LIMIT_PRI) + && (limit_item != LIMIT_NONEWPRIVS) ) { if (limit_type & LIMIT_SOFT) { if (pl->limits[limit_item].src_soft < source) { return; @@ -674,14 +741,16 @@ process_limit (const pam_handle_t *pamh, int source, const char *lim_type, /* recent kernels support negative priority limits (=raise priority) */ if (limit_item == LIMIT_PRI) { - pl->priority = int_value; + pl->priority = int_value; + } else if (limit_item == LIMIT_NONEWPRIVS) { + pl->nonewprivs = int_value; } else { - if (pl->login_limit_def < source) { - return; - } else { - pl->login_limit = int_value; - pl->login_limit_def = source; - } + if (pl->login_limit_def < source) { + return; + } else { + pl->login_limit = int_value; + pl->login_limit_def = source; + } } } return; @@ -995,6 +1064,13 @@ static int setup_limits(pam_handle_t *pamh, retval |= LOGIN_ERR; } + if (pl->nonewprivs) { + if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) < 0) { + pam_syslog(pamh, LOG_ERR, "Could not set prctl(PR_SET_NO_NEW_PRIVS): %m"); + retval |= LIMIT_ERR; + } + } + return retval; } |