2 * Copyright (c) 1989 - 1994, Julianne Frances Haugh
3 * Copyright (c) 1996 - 1999, Marek Michałkiewicz
4 * Copyright (c) 2003 - 2006, Tomasz Kłoczko
5 * Copyright (c) 2007 - 2008, Nicolas François
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 * 3. The name of the copyright holders or contributors may not be used to
17 * endorse or promote products derived from this software without
18 * specific prior written permission.
20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
23 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24 * HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
34 * Separated from setup.c. --marekm
35 * Resource limits thanks to Cristian Gafton.
44 #include <sys/types.h>
47 #include "prototypes.h"
51 #ifdef HAVE_SYS_RESOURCE_H
52 #include <sys/resource.h>
57 #define LIMITS_FILE "/etc/limits"
59 #define LOGIN_ERROR_RLIMIT 1
60 #define LOGIN_ERROR_LOGIN 2
61 /* Set a limit on a resource */
63 * rlimit - RLIMIT_XXXX
64 * value - string value to be read
65 * multiplier - value*multiplier is the actual limit
68 setrlimit_value (unsigned int rlimit, const char *value,
69 unsigned int multiplier)
73 char **endptr = (char **) &value;
74 const char *value_orig = value;
76 limit = strtol (value, endptr, 10);
77 if (limit == 0 && value_orig == *endptr) /* no chars read */
80 rlim.rlim_cur = limit;
81 rlim.rlim_max = limit;
82 if (setrlimit (rlimit, &rlim))
83 return LOGIN_ERROR_RLIMIT;
88 static int set_prio (const char *value)
91 char **endptr = (char **) &value;
93 prio = strtol (value, endptr, 10);
94 if ((prio == 0) && (value == *endptr))
96 if (setpriority (PRIO_PROCESS, 0, prio))
97 return LOGIN_ERROR_RLIMIT;
102 static int set_umask (const char *value)
105 char **endptr = (char **) &value;
107 mask = strtol (value, endptr, 8) & 0777;
108 if ((mask == 0) && (value == *endptr))
115 /* Counts the number of user logins and check against the limit */
116 static int check_logins (const char *name, const char *maxlogins)
123 unsigned int limit, count;
124 char **endptr = (char **) &maxlogins;
125 const char *ml_orig = maxlogins;
127 limit = strtol (maxlogins, endptr, 10);
128 if (limit == 0 && ml_orig == *endptr) /* no chars read */
131 if (limit == 0) { /* maximum 0 logins ? */
132 SYSLOG ((LOG_WARN, "No logins allowed for `%s'\n", name));
133 return LOGIN_ERROR_LOGIN;
139 while ((ut = getutxent ())) {
142 while ((ut = getutent ())) {
144 if (ut->ut_type != USER_PROCESS)
146 if (ut->ut_user[0] == '\0')
148 if (strncmp (name, ut->ut_user, sizeof (ut->ut_user)) != 0)
159 * This is called after setutmp(), so the number of logins counted
160 * includes the user who is currently trying to log in.
163 SYSLOG ((LOG_WARN, "Too many logins (max %d) for %s\n",
165 return LOGIN_ERROR_LOGIN;
170 /* Function setup_user_limits - checks/set limits for the curent login
171 * Original idea from Joel Katz's lshell. Ported to shadow-login
172 * by Cristian Gafton - gafton@sorosis.ro
174 * We are passed a string of the form ('BASH' constants for ulimit)
175 * [Aa][Cc][Dd][Ff][Mm][Nn][Rr][Ss][Tt][Uu][Ll][Pp][Ii][Oo]
176 * (eg. 'C2F256D2048N5' or 'C2 F256 D2048 N5')
178 * [Aa]: a = RLIMIT_AS max address space (KB)
179 * [Cc]: c = RLIMIT_CORE max core file size (KB)
180 * [Dd]: d = RLIMIT_DATA max data size (KB)
181 * [Ff]: f = RLIMIT_FSIZE max file size (KB)
182 * [Mm]: m = RLIMIT_MEMLOCK max locked-in-memory address space (KB)
183 * [Nn]: n = RLIMIT_NOFILE max number of open files
184 * [Rr]: r = RLIMIT_RSS max resident set size (KB)
185 * [Ss]: s = RLIMIT_STACK max stack size (KB)
186 * [Tt]: t = RLIMIT_CPU max CPU time (MIN)
187 * [Uu]: u = RLIMIT_NPROC max number of processes
188 * [Kk]: k = file creation masK (umask)
189 * [Ll]: l = max number of logins for this user
190 * [Pp]: p = process priority -20..20 (negative = high, positive = low)
191 * [Ii]: i = RLIMIT_NICE max nice value (0..39 translates to 20..-19)
192 * [Oo]: o = RLIMIT_RTPRIO max real time priority (linux/sched.h 0..MAX_RT_PRIO)
195 * 0 = okay, of course
196 * LOGIN_ERROR_RLIMIT = error setting some RLIMIT
197 * LOGIN_ERROR_LOGIN = error - too many logins for this user
199 * buf - the limits string
200 * name - the username
202 static int do_user_limits (const char *buf, const char *name)
214 /* RLIMIT_AS - max address space (KB) */
215 retval |= setrlimit_value (RLIMIT_AS, pp, 1024);
221 /* RLIMIT_CPU - max CPU time (MIN) */
222 retval |= setrlimit_value (RLIMIT_CPU, pp, 60);
228 /* RLIMIT_DATA - max data size (KB) */
229 retval |= setrlimit_value (RLIMIT_DATA, pp, 1024);
235 /* RLIMIT_FSIZE - Maximum filesize (KB) */
236 retval |= setrlimit_value (RLIMIT_FSIZE, pp, 1024);
242 /* RLIMIT_NPROC - max number of processes */
243 retval |= setrlimit_value (RLIMIT_NPROC, pp, 1);
249 /* RLIMIT_CORE - max core file size (KB) */
250 retval |= setrlimit_value (RLIMIT_CORE, pp, 1024);
253 #ifdef RLIMIT_MEMLOCK
256 /* RLIMIT_MEMLOCK - max locked-in-memory address space (KB) */
257 retval |= setrlimit_value (RLIMIT_MEMLOCK, pp, 1024);
263 /* RLIMIT_NOFILE - max number of open files */
264 retval |= setrlimit_value (RLIMIT_NOFILE, pp, 1);
270 /* RLIMIT_RSS - max resident set size (KB) */
271 retval |= setrlimit_value (RLIMIT_RSS, pp, 1024);
277 /* RLIMIT_STACK - max stack size (KB) */
278 retval |= setrlimit_value (RLIMIT_STACK, pp, 1024);
284 /* RLIMIT_NICE - max scheduling priority (0..39) */
285 retval |= setrlimit_value (RLIMIT_NICE, pp, 1);
291 /* RLIMIT_RTPRIO - max real time priority (0..MAX_RT_PRIO) */
292 retval |= setrlimit_value (RLIMIT_RTPRIO, pp, 1);
297 retval |= set_umask (pp);
301 /* LIMIT the number of concurent logins */
302 retval |= check_logins (name, pp);
306 retval |= set_prio (pp);
312 static int setup_user_limits (const char *uname)
314 /* TODO: allow and use @group syntax --cristiang */
319 char deflimits[1024];
323 memzero (buf, sizeof (buf));
324 memzero (name, sizeof (name));
325 memzero (limits, sizeof (limits));
326 memzero (deflimits, sizeof (deflimits));
327 memzero (tempbuf, sizeof (tempbuf));
329 /* start the checks */
330 fil = fopen (LIMITS_FILE, "r");
334 /* The limits file have the following format:
335 * - '#' (comment) chars only as first chars on a line;
336 * - username must start on first column
337 * A better (smarter) checking should be done --cristiang */
338 while (fgets (buf, 1024, fil) != NULL) {
339 if (buf[0] == '#' || buf[0] == '\n')
341 memzero (tempbuf, sizeof (tempbuf));
342 /* a valid line should have a username, then spaces,
344 * we allow the format:
345 * username L2 D2048 R4096
346 * where spaces={' ',\t}. Also, we reject invalid limits.
347 * Imposing a limit should be done with care, so a wrong
348 * entry means no care anyway :-). A '-' as a limits
349 * strings means no limits --cristiang */
350 if (sscanf (buf, "%s%[ACDFMNRSTULPIOacdfmnrstulpio0-9 \t-]",
351 name, tempbuf) == 2) {
352 if (strcmp (name, uname) == 0) {
353 strcpy (limits, tempbuf);
355 } else if (strcmp (name, "*") == 0) {
356 strcpy (deflimits, tempbuf);
361 if (limits[0] == '\0') {
362 /* no user specific limits */
363 if (deflimits[0] == '\0') /* no default limits */
365 strcpy (limits, deflimits); /* use the default limits */
367 return do_user_limits (limits, uname);
372 static void setup_usergroups (const struct passwd *info)
374 const struct group *grp;
378 * if not root, and UID == GID, and username is the same as primary
379 * group name, set umask group bits to be the same as owner bits
380 * (examples: 022 -> 002, 077 -> 007).
382 if (info->pw_uid != 0 && info->pw_uid == info->pw_gid) {
383 /* local, no need for xgetgrgid */
384 grp = getgrgid (info->pw_gid);
385 if (grp && (strcmp (info->pw_name, grp->gr_name) == 0)) {
386 oldmask = umask (0777);
387 umask ((oldmask & ~070) | ((oldmask >> 3) & 070));
393 * set the process nice, ulimit, and umask from the password file entry
396 void setup_limits (const struct passwd *info)
402 if (getdef_bool ("USERGROUPS_ENAB"))
403 setup_usergroups (info);
406 * See if the GECOS field contains values for NICE, UMASK or ULIMIT.
407 * If this feature is enabled in /etc/login.defs, we make those
408 * values the defaults for this login session.
411 if (getdef_bool ("QUOTAS_ENAB")) {
413 if (info->pw_uid != 0)
414 if (setup_user_limits (info->pw_name) &
416 fputs (_("Too many logins.\n"), stderr);
421 for (cp = info->pw_gecos; cp != NULL; cp = strchr (cp, ',')) {
425 if (strncmp (cp, "pri=", 4) == 0) {
427 if (i >= -20 && i <= 20)
432 if (strncmp (cp, "ulimit=", 7) == 0) {
433 l = strtol (cp + 7, (char **) 0, 10);
434 set_filesize_limit (l);
437 if (strncmp (cp, "umask=", 6) == 0) {
438 i = strtol (cp + 6, (char **) 0, 8) & 0777;
448 extern int errno; /* warning: ANSI C forbids an empty source file */
449 #endif /* !USE_PAM */