summaryrefslogtreecommitdiffstats
path: root/libmisc/limits.c
diff options
context:
space:
mode:
Diffstat (limited to 'libmisc/limits.c')
-rw-r--r--libmisc/limits.c589
1 files changed, 0 insertions, 589 deletions
diff --git a/libmisc/limits.c b/libmisc/limits.c
deleted file mode 100644
index fea85fe..0000000
--- a/libmisc/limits.c
+++ /dev/null
@@ -1,589 +0,0 @@
-/*
- * SPDX-FileCopyrightText: 1989 - 1994, Julianne Frances Haugh
- * SPDX-FileCopyrightText: 1996 - 1999, Marek Michałkiewicz
- * SPDX-FileCopyrightText: 2003 - 2006, Tomasz Kłoczko
- * SPDX-FileCopyrightText: 2007 - 2010, Nicolas François
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-/*
- * Separated from setup.c. --marekm
- * Resource limits thanks to Cristian Gafton.
- * Enhancements of resource limit code by Thomas Orgis <thomas@orgis.org>
- */
-
-#include <config.h>
-
-#ifndef USE_PAM
-
-#ident "$Id$"
-
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <stdio.h>
-#include <ctype.h>
-#include "prototypes.h"
-#include "defines.h"
-#include <pwd.h>
-#include "getdef.h"
-#include "shadowlog.h"
-#ifdef HAVE_SYS_RESOURCE_H
-#include <sys/resource.h>
-#define LIMITS
-#endif
-#ifdef LIMITS
-#ifndef LIMITS_FILE
-#define LIMITS_FILE "/etc/limits"
-#endif
-#define LOGIN_ERROR_RLIMIT 1
-#define LOGIN_ERROR_LOGIN 2
-/* Set a limit on a resource */
-/*
- * rlimit - RLIMIT_XXXX
- * value - string value to be read
- * multiplier - value*multiplier is the actual limit
- */
-static int setrlimit_value (unsigned int resource,
- const char *value,
- unsigned int multiplier)
-{
- struct rlimit rlim;
- rlim_t limit;
-
- /* The "-" is special, not belonging to a strange negative limit.
- * It is infinity, in a controlled way.
- */
- if ('-' == value[0]) {
- limit = RLIM_INFINITY;
- }
- else {
- /* We cannot use getlong here because it fails when there
- * is more to the value than just this number!
- * Also, we are limited to base 10 here (hex numbers will not
- * work with the limit string parser as is anyway)
- */
- char *endptr;
- long longlimit = strtol (value, &endptr, 10);
- if ((0 == longlimit) && (value == endptr)) {
- /* No argument at all. No-op.
- * FIXME: We could instead throw an error, though.
- */
- return 0;
- }
- longlimit *= multiplier;
- limit = (rlim_t)longlimit;
- if (longlimit != limit)
- {
- /* FIXME: Again, silent error handling...
- * Wouldn't screaming make more sense?
- */
- return 0;
- }
- }
-
- rlim.rlim_cur = limit;
- rlim.rlim_max = limit;
- if (setrlimit (resource, &rlim) != 0) {
- return LOGIN_ERROR_RLIMIT;
- }
- return 0;
-}
-
-
-static int set_prio (const char *value)
-{
- long prio;
-
- if ( (getlong (value, &prio) == 0)
- || (prio != (int) prio)) {
- return 0;
- }
- if (setpriority (PRIO_PROCESS, 0, (int) prio) != 0) {
- return LOGIN_ERROR_RLIMIT;
- }
- return 0;
-}
-
-
-static int set_umask (const char *value)
-{
- unsigned long int mask;
-
- if ( (getulong (value, &mask) == 0)
- || (mask != (mode_t) mask)) {
- return 0;
- }
-
- (void) umask ((mode_t) mask);
- return 0;
-}
-
-
-/* Counts the number of user logins and check against the limit */
-static int check_logins (const char *name, const char *maxlogins)
-{
-#ifdef USE_UTMPX
- struct utmpx *ut;
-#else /* !USE_UTMPX */
- struct utmp *ut;
-#endif /* !USE_UTMPX */
- unsigned long limit, count;
-
- if (getulong (maxlogins, &limit) == 0) {
- return 0;
- }
-
- if (0 == limit) { /* maximum 0 logins ? */
- SYSLOG ((LOG_WARN, "No logins allowed for `%s'\n", name));
- return LOGIN_ERROR_LOGIN;
- }
-
- count = 0;
-#ifdef USE_UTMPX
- setutxent ();
- while ((ut = getutxent ()))
-#else /* !USE_UTMPX */
- setutent ();
- while ((ut = getutent ()))
-#endif /* !USE_UTMPX */
- {
- if (USER_PROCESS != ut->ut_type) {
- continue;
- }
- if ('\0' == ut->ut_user[0]) {
- continue;
- }
- if (strncmp (name, ut->ut_user, sizeof (ut->ut_user)) != 0) {
- continue;
- }
- count++;
- if (count > limit) {
- break;
- }
- }
-#ifdef USE_UTMPX
- endutxent ();
-#else /* !USE_UTMPX */
- endutent ();
-#endif /* !USE_UTMPX */
- /*
- * This is called after setutmp(), so the number of logins counted
- * includes the user who is currently trying to log in.
- */
- if (count > limit) {
- SYSLOG ((LOG_WARN,
- "Too many logins (max %lu) for %s\n",
- limit, name));
- return LOGIN_ERROR_LOGIN;
- }
- return 0;
-}
-
-/* Function setup_user_limits - checks/set limits for the current login
- * Original idea from Joel Katz's lshell. Ported to shadow-login
- * by Cristian Gafton - gafton@sorosis.ro
- *
- * We are passed a string of the form ('BASH' constants for ulimit)
- * [Aa][Cc][Dd][Ff][Mm][Nn][Rr][Ss][Tt][Uu][Ll][Pp][Ii][Oo]
- * (eg. 'C2F256D2048N5' or 'C2 F256 D2048 N5')
- * where:
- * [Aa]: a = RLIMIT_AS max address space (KB)
- * [Cc]: c = RLIMIT_CORE max core file size (KB)
- * [Dd]: d = RLIMIT_DATA max data size (KB)
- * [Ff]: f = RLIMIT_FSIZE max file size (KB)
- * [Ii]: i = RLIMIT_NICE max nice value (0..39 translates to 20..-19)
- * [Kk]: k = file creation masK (umask)
- * [Ll]: l = max number of logins for this user
- * [Mm]: m = RLIMIT_MEMLOCK max locked-in-memory address space (KB)
- * [Nn]: n = RLIMIT_NOFILE max number of open files
- * [Oo]: o = RLIMIT_RTPRIO max real time priority (linux/sched.h 0..MAX_RT_PRIO)
- * [Pp]: p = process priority -20..20 (negative = high, positive = low)
- * [Rr]: r = RLIMIT_RSS max resident set size (KB)
- * [Ss]: s = RLIMIT_STACK max stack size (KB)
- * [Tt]: t = RLIMIT_CPU max CPU time (MIN)
- * [Uu]: u = RLIMIT_NPROC max number of processes
- *
- * NOTE: Remember to extend the "no-limits" string below when adding a new
- * limit...
- *
- * Return value:
- * 0 = okay, of course
- * LOGIN_ERROR_RLIMIT = error setting some RLIMIT
- * LOGIN_ERROR_LOGIN = error - too many logins for this user
- *
- * buf - the limits string
- * name - the username
- */
-static int do_user_limits (const char *buf, const char *name)
-{
- const char *pp;
- int retval = 0;
- bool reported = false;
-
- pp = buf;
- /* Skip leading whitespace. */
- while ((' ' == *pp) || ('\t' == *pp)) {
- pp++;
- }
-
- /* The special limit string "-" results in no limit for all known
- * limits.
- * We achieve that by parsing a full limit string, parts of it
- * being ignored if a limit type is not known to the system.
- * Though, there will be complaining for unknown limit types.
- */
- if (strcmp (pp, "-") == 0) {
- /* Remember to extend this, too, when adding new limits!
- * Oh... but "unlimited" does not make sense for umask,
- * or does it? (K-)
- */
- pp = "A- C- D- F- I- L- M- N- O- P- R- S- T- U-";
- }
-
- while ('\0' != *pp) {
- switch (*pp++) {
-#ifdef RLIMIT_AS
- case 'a':
- case 'A':
- /* RLIMIT_AS - max address space (KB) */
- retval |= setrlimit_value (RLIMIT_AS, pp, 1024);
- break;
-#endif
-#ifdef RLIMIT_CORE
- case 'c':
- case 'C':
- /* RLIMIT_CORE - max core file size (KB) */
- retval |= setrlimit_value (RLIMIT_CORE, pp, 1024);
- break;
-#endif
-#ifdef RLIMIT_DATA
- case 'd':
- case 'D':
- /* RLIMIT_DATA - max data size (KB) */
- retval |= setrlimit_value (RLIMIT_DATA, pp, 1024);
- break;
-#endif
-#ifdef RLIMIT_FSIZE
- case 'f':
- case 'F':
- /* RLIMIT_FSIZE - Maximum filesize (KB) */
- retval |= setrlimit_value (RLIMIT_FSIZE, pp, 1024);
- break;
-#endif
-#ifdef RLIMIT_NICE
- case 'i':
- case 'I':
- /* RLIMIT_NICE - max scheduling priority (0..39) */
- retval |= setrlimit_value (RLIMIT_NICE, pp, 1);
- break;
-#endif
- case 'k':
- case 'K':
- retval |= set_umask (pp);
- break;
- case 'l':
- case 'L':
- /* LIMIT the number of concurrent logins */
- retval |= check_logins (name, pp);
- break;
-#ifdef RLIMIT_MEMLOCK
- case 'm':
- case 'M':
- /* RLIMIT_MEMLOCK - max locked-in-memory address space (KB) */
- retval |= setrlimit_value (RLIMIT_MEMLOCK, pp, 1024);
- break;
-#endif
-#ifdef RLIMIT_NOFILE
- case 'n':
- case 'N':
- /* RLIMIT_NOFILE - max number of open files */
- retval |= setrlimit_value (RLIMIT_NOFILE, pp, 1);
- break;
-#endif
-#ifdef RLIMIT_RTPRIO
- case 'o':
- case 'O':
- /* RLIMIT_RTPRIO - max real time priority (0..MAX_RT_PRIO) */
- retval |= setrlimit_value (RLIMIT_RTPRIO, pp, 1);
- break;
-#endif
- case 'p':
- case 'P':
- retval |= set_prio (pp);
- break;
-#ifdef RLIMIT_RSS
- case 'r':
- case 'R':
- /* RLIMIT_RSS - max resident set size (KB) */
- retval |= setrlimit_value (RLIMIT_RSS, pp, 1024);
- break;
-#endif
-#ifdef RLIMIT_STACK
- case 's':
- case 'S':
- /* RLIMIT_STACK - max stack size (KB) */
- retval |= setrlimit_value (RLIMIT_STACK, pp, 1024);
- break;
-#endif
-#ifdef RLIMIT_CPU
- case 't':
- case 'T':
- /* RLIMIT_CPU - max CPU time (MIN) */
- retval |= setrlimit_value (RLIMIT_CPU, pp, 60);
- break;
-#endif
-#ifdef RLIMIT_NPROC
- case 'u':
- case 'U':
- /* RLIMIT_NPROC - max number of processes */
- retval |= setrlimit_value (RLIMIT_NPROC, pp, 1);
- break;
-#endif
- default:
- /* Only report invalid strings once */
- /* Note: A string can be invalid just because a
- * specific (theoretically valid) setting is not
- * supported by this build.
- * It is just a warning in syslog anyway. The line
- * is still processed
- */
- if (!reported) {
- SYSLOG ((LOG_WARN,
- "Invalid limit string: '%s'",
- pp-1));
- reported = true;
- retval |= LOGIN_ERROR_RLIMIT;
- }
- }
- /* After parsing one limit setting (or just complaining
- * about it), one still needs to skip its argument to
- * prevent a bogus warning on trying to parse that as
- * limit specification.
- * So, let's skip all digits, "-" and our limited set of
- * whitespace.
- */
- while ( isdigit (*pp)
- || ('-' == *pp)
- || (' ' == *pp)
- || ('\t' ==*pp)) {
- pp++;
- }
- }
- return retval;
-}
-
-/* Check if user uname is in the group gname.
- * Can I be sure that gr_mem contains no UID as string?
- * Returns true when user is in the group, false when not.
- * Any error is treated as false.
- */
-static bool user_in_group (const char *uname, const char *gname)
-{
- struct group *groupdata;
-
- if (uname == NULL || gname == NULL) {
- return false;
- }
-
- /* We are not claiming to be re-entrant!
- * In case of paranoia or a multithreaded login program,
- * one needs to add some mess for getgrnam_r. */
- groupdata = getgrnam (gname);
- if (NULL == groupdata) {
- SYSLOG ((LOG_WARN, "Nonexisting group `%s' in limits file.",
- gname));
- return false;
- }
-
- return is_on_list (groupdata->gr_mem, uname);
-}
-
-static int setup_user_limits (const char *uname)
-{
- FILE *fil;
- char buf[1024];
- char name[1024];
- char limits[1024];
- char deflimits[1024];
- char tempbuf[1024];
-
- /* init things */
- memzero (buf, sizeof (buf));
- memzero (name, sizeof (name));
- memzero (limits, sizeof (limits));
- memzero (deflimits, sizeof (deflimits));
- memzero (tempbuf, sizeof (tempbuf));
-
- /* start the checks */
- fil = fopen (LIMITS_FILE, "r");
- if (fil == NULL) {
- return 0;
- }
- /* The limits file have the following format:
- * - '#' (comment) chars only as first chars on a line;
- * - username must start on first column (or *, or @group)
- *
- * FIXME: A better (smarter) checking should be done
- */
- while (fgets (buf, 1024, fil) != NULL) {
- if (('#' == buf[0]) || ('\n' == buf[0])) {
- continue;
- }
- memzero (tempbuf, sizeof (tempbuf));
- /* a valid line should have a username, then spaces,
- * then limits
- * we allow the format:
- * username L2 D2048 R4096
- * where spaces={' ',\t}. Also, we reject invalid limits.
- * Imposing a limit should be done with care, so a wrong
- * entry means no care anyway :-).
- *
- * A '-' as a limits strings means no limits
- *
- * The username can also be:
- * '*': the default limits (only the last is taken into
- * account)
- * @group: the limit applies to the members of the group
- *
- * To clarify: The first entry with matching user name rules,
- * everything after it is ignored. If there is no user entry,
- * the last encountered entry for a matching group rules.
- * If there is no matching group entry, the default limits rule.
- */
- if (sscanf (buf, "%s%[ACDFIKLMNOPRSTUacdfiklmnoprstu0-9 \t-]",
- name, tempbuf) == 2) {
- if (strcmp (name, uname) == 0) {
- strcpy (limits, tempbuf);
- break;
- } else if (strcmp (name, "*") == 0) {
- strcpy (deflimits, tempbuf);
- } else if (name[0] == '@') {
- /* If the user is in the group, the group
- * limits apply unless later a line for
- * the specific user is found.
- */
- if (user_in_group (uname, name+1)) {
- strcpy (limits, tempbuf);
- }
- }
- }
- }
- (void) fclose (fil);
- if (limits[0] == '\0') {
- /* no user specific limits */
- if (deflimits[0] == '\0') { /* no default limits */
- return 0;
- }
- strcpy (limits, deflimits); /* use the default limits */
- }
- return do_user_limits (limits, uname);
-}
-#endif /* LIMITS */
-
-
-static void setup_usergroups (const struct passwd *info)
-{
- const struct group *grp;
-
-/*
- * if not root, and UID == GID, and username is the same as primary
- * group name, set umask group bits to be the same as owner bits
- * (examples: 022 -> 002, 077 -> 007).
- */
- if ((0 != info->pw_uid) && (info->pw_uid == info->pw_gid)) {
- /* local, no need for xgetgrgid */
- grp = getgrgid (info->pw_gid);
- if ( (NULL != grp)
- && (strcmp (info->pw_name, grp->gr_name) == 0)) {
- mode_t tmpmask;
- tmpmask = umask (0777);
- tmpmask = (tmpmask & ~070) | ((tmpmask >> 3) & 070);
- (void) umask (tmpmask);
- }
- }
-}
-
-/*
- * set the process nice, ulimit, and umask from the password file entry
- */
-
-void setup_limits (const struct passwd *info)
-{
- char *cp;
-
- if (getdef_bool ("USERGROUPS_ENAB")) {
- setup_usergroups (info);
- }
-
- /*
- * See if the GECOS field contains values for NICE, UMASK or ULIMIT.
- * If this feature is enabled in /etc/login.defs, we make those
- * values the defaults for this login session.
- */
-
- if (getdef_bool ("QUOTAS_ENAB")) {
-#ifdef LIMITS
- if (info->pw_uid != 0) {
- if ((setup_user_limits (info->pw_name) & LOGIN_ERROR_LOGIN) != 0) {
- (void) fputs (_("Too many logins.\n"), log_get_logfd());
- (void) sleep (2); /* XXX: Should be FAIL_DELAY */
- exit (EXIT_FAILURE);
- }
- }
-#endif
- for (cp = info->pw_gecos; cp != NULL; cp = strchr (cp, ',')) {
- if (',' == *cp) {
- cp++;
- }
-
- if (strncmp (cp, "pri=", 4) == 0) {
- long int inc;
- if ( (getlong (cp + 4, &inc) == 1)
- && (inc >= -20) && (inc <= 20)) {
- errno = 0;
- if ( (nice ((int) inc) != -1)
- || (0 != errno)) {
- continue;
- }
- }
-
- /* Failed to parse or failed to nice() */
- SYSLOG ((LOG_WARN,
- "Can't set the nice value for user %s",
- info->pw_name));
-
- continue;
- }
- if (strncmp (cp, "ulimit=", 7) == 0) {
- long int blocks;
- if ( (getlong (cp + 7, &blocks) == 0)
- || (blocks != (int) blocks)
- || (set_filesize_limit ((int) blocks) != 0)) {
- SYSLOG ((LOG_WARN,
- "Can't set the ulimit for user %s",
- info->pw_name));
- }
- continue;
- }
- if (strncmp (cp, "umask=", 6) == 0) {
- unsigned long int mask;
- if ( (getulong (cp + 6, &mask) == 0)
- || (mask != (mode_t) mask)) {
- SYSLOG ((LOG_WARN,
- "Can't set umask value for user %s",
- info->pw_name));
- } else {
- (void) umask ((mode_t) mask);
- }
-
- continue;
- }
- }
- }
-}
-
-#else /* !USE_PAM */
-extern int errno; /* warning: ANSI C forbids an empty source file */
-#endif /* !USE_PAM */
-