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.
42 #ident "$Id: limits.c 2849 2009-04-30 21:08:49Z nekral-guest $"
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 resource, const char *value,
69 unsigned int multiplier)
74 if (getlong (value, &limit) == 0) {
78 if (limit != (rlim_t) limit) {
81 rlim.rlim_cur = (rlim_t) limit;
82 rlim.rlim_max = (rlim_t) limit;
83 if (setrlimit (resource, &rlim) != 0) {
84 return LOGIN_ERROR_RLIMIT;
90 static int set_prio (const char *value)
94 if ( (getlong (value, &prio) == 0)
95 || (prio != (int) prio)) {
98 if (setpriority (PRIO_PROCESS, 0, (int) prio) != 0) {
99 return LOGIN_ERROR_RLIMIT;
105 static int set_umask (const char *value)
107 unsigned long int mask;
109 if ( (getulong (value, &mask) == 0)
110 || (mask != (mode_t) mask)) {
114 (void) umask ((mode_t) mask);
119 /* Counts the number of user logins and check against the limit */
120 static int check_logins (const char *name, const char *maxlogins)
124 #else /* !USE_UTMPX */
126 #endif /* !USE_UTMPX */
127 unsigned long limit, count;
129 if (getulong (maxlogins, &limit) == 0) {
133 if (0 == limit) { /* maximum 0 logins ? */
134 SYSLOG ((LOG_WARN, "No logins allowed for `%s'\n", name));
135 return LOGIN_ERROR_LOGIN;
141 while ((ut = getutxent ())) {
142 #else /* !USE_UTMPX */
144 while ((ut = getutent ())) {
145 #endif /* !USE_UTMPX */
146 if (USER_PROCESS != ut->ut_type) {
149 if ('\0' == ut->ut_user[0]) {
152 if (strncmp (name, ut->ut_user, sizeof (ut->ut_user)) != 0) {
162 #else /* !USE_UTMPX */
164 #endif /* !USE_UTMPX */
166 * This is called after setutmp(), so the number of logins counted
167 * includes the user who is currently trying to log in.
170 SYSLOG ((LOG_WARN, "Too many logins (max %d) for %s\n",
172 return LOGIN_ERROR_LOGIN;
177 /* Function setup_user_limits - checks/set limits for the curent login
178 * Original idea from Joel Katz's lshell. Ported to shadow-login
179 * by Cristian Gafton - gafton@sorosis.ro
181 * We are passed a string of the form ('BASH' constants for ulimit)
182 * [Aa][Cc][Dd][Ff][Mm][Nn][Rr][Ss][Tt][Uu][Ll][Pp][Ii][Oo]
183 * (eg. 'C2F256D2048N5' or 'C2 F256 D2048 N5')
185 * [Aa]: a = RLIMIT_AS max address space (KB)
186 * [Cc]: c = RLIMIT_CORE max core file size (KB)
187 * [Dd]: d = RLIMIT_DATA max data size (KB)
188 * [Ff]: f = RLIMIT_FSIZE max file size (KB)
189 * [Mm]: m = RLIMIT_MEMLOCK max locked-in-memory address space (KB)
190 * [Nn]: n = RLIMIT_NOFILE max number of open files
191 * [Rr]: r = RLIMIT_RSS max resident set size (KB)
192 * [Ss]: s = RLIMIT_STACK max stack size (KB)
193 * [Tt]: t = RLIMIT_CPU max CPU time (MIN)
194 * [Uu]: u = RLIMIT_NPROC max number of processes
195 * [Kk]: k = file creation masK (umask)
196 * [Ll]: l = max number of logins for this user
197 * [Pp]: p = process priority -20..20 (negative = high, positive = low)
198 * [Ii]: i = RLIMIT_NICE max nice value (0..39 translates to 20..-19)
199 * [Oo]: o = RLIMIT_RTPRIO max real time priority (linux/sched.h 0..MAX_RT_PRIO)
202 * 0 = okay, of course
203 * LOGIN_ERROR_RLIMIT = error setting some RLIMIT
204 * LOGIN_ERROR_LOGIN = error - too many logins for this user
206 * buf - the limits string
207 * name - the username
209 static int do_user_limits (const char *buf, const char *name)
213 bool reported = false;
217 while ('\0' != *pp) {
222 /* RLIMIT_AS - max address space (KB) */
223 retval |= setrlimit_value (RLIMIT_AS, pp, 1024);
229 /* RLIMIT_CPU - max CPU time (MIN) */
230 retval |= setrlimit_value (RLIMIT_CPU, pp, 60);
236 /* RLIMIT_DATA - max data size (KB) */
237 retval |= setrlimit_value (RLIMIT_DATA, pp, 1024);
243 /* RLIMIT_FSIZE - Maximum filesize (KB) */
244 retval |= setrlimit_value (RLIMIT_FSIZE, pp, 1024);
250 /* RLIMIT_NPROC - max number of processes */
251 retval |= setrlimit_value (RLIMIT_NPROC, pp, 1);
257 /* RLIMIT_CORE - max core file size (KB) */
258 retval |= setrlimit_value (RLIMIT_CORE, pp, 1024);
261 #ifdef RLIMIT_MEMLOCK
264 /* RLIMIT_MEMLOCK - max locked-in-memory address space (KB) */
265 retval |= setrlimit_value (RLIMIT_MEMLOCK, pp, 1024);
271 /* RLIMIT_NOFILE - max number of open files */
272 retval |= setrlimit_value (RLIMIT_NOFILE, pp, 1);
278 /* RLIMIT_RSS - max resident set size (KB) */
279 retval |= setrlimit_value (RLIMIT_RSS, pp, 1024);
285 /* RLIMIT_STACK - max stack size (KB) */
286 retval |= setrlimit_value (RLIMIT_STACK, pp, 1024);
292 /* RLIMIT_NICE - max scheduling priority (0..39) */
293 retval |= setrlimit_value (RLIMIT_NICE, pp, 1);
299 /* RLIMIT_RTPRIO - max real time priority (0..MAX_RT_PRIO) */
300 retval |= setrlimit_value (RLIMIT_RTPRIO, pp, 1);
305 retval |= set_umask (pp);
309 /* LIMIT the number of concurrent logins */
310 retval |= check_logins (name, pp);
314 retval |= set_prio (pp);
317 /* Only report invalid strings once */
320 "Invalid limit string: '%s'",
323 retval |= LOGIN_ERROR_RLIMIT;
330 static int setup_user_limits (const char *uname)
332 /* TODO: allow and use @group syntax --cristiang */
337 char deflimits[1024];
341 memzero (buf, sizeof (buf));
342 memzero (name, sizeof (name));
343 memzero (limits, sizeof (limits));
344 memzero (deflimits, sizeof (deflimits));
345 memzero (tempbuf, sizeof (tempbuf));
347 /* start the checks */
348 fil = fopen (LIMITS_FILE, "r");
352 /* The limits file have the following format:
353 * - '#' (comment) chars only as first chars on a line;
354 * - username must start on first column
355 * A better (smarter) checking should be done --cristiang */
356 while (fgets (buf, 1024, fil) != NULL) {
357 if (('#' == buf[0]) || ('\n' == buf[0])) {
360 memzero (tempbuf, sizeof (tempbuf));
361 /* a valid line should have a username, then spaces,
363 * we allow the format:
364 * username L2 D2048 R4096
365 * where spaces={' ',\t}. Also, we reject invalid limits.
366 * Imposing a limit should be done with care, so a wrong
367 * entry means no care anyway :-). A '-' as a limits
368 * strings means no limits --cristiang */
369 if (sscanf (buf, "%s%[ACDFMNRSTULPIOacdfmnrstulpio0-9 \t-]",
370 name, tempbuf) == 2) {
371 if (strcmp (name, uname) == 0) {
372 strcpy (limits, tempbuf);
374 } else if (strcmp (name, "*") == 0) {
375 strcpy (deflimits, tempbuf);
380 if (limits[0] == '\0') {
381 /* no user specific limits */
382 if (deflimits[0] == '\0') { /* no default limits */
385 strcpy (limits, deflimits); /* use the default limits */
387 return do_user_limits (limits, uname);
392 static void setup_usergroups (const struct passwd *info)
394 const struct group *grp;
398 * if not root, and UID == GID, and username is the same as primary
399 * group name, set umask group bits to be the same as owner bits
400 * (examples: 022 -> 002, 077 -> 007).
402 if ((0 != info->pw_uid) && (info->pw_uid == info->pw_gid)) {
403 /* local, no need for xgetgrgid */
404 grp = getgrgid (info->pw_gid);
406 && (strcmp (info->pw_name, grp->gr_name) == 0)) {
407 tmpmask = umask (0777);
408 tmpmask = (tmpmask & ~070) | ((tmpmask >> 3) & 070);
409 (void) umask (tmpmask);
415 * set the process nice, ulimit, and umask from the password file entry
418 void setup_limits (const struct passwd *info)
422 if (getdef_bool ("USERGROUPS_ENAB")) {
423 setup_usergroups (info);
427 * See if the GECOS field contains values for NICE, UMASK or ULIMIT.
428 * If this feature is enabled in /etc/login.defs, we make those
429 * values the defaults for this login session.
432 if (getdef_bool ("QUOTAS_ENAB")) {
434 if (info->pw_uid != 0) {
435 if (setup_user_limits (info->pw_name) &
437 (void) fputs (_("Too many logins.\n"), stderr);
438 (void) sleep (2); /* XXX: Should be FAIL_DELAY */
443 for (cp = info->pw_gecos; cp != NULL; cp = strchr (cp, ',')) {
448 if (strncmp (cp, "pri=", 4) == 0) {
450 if ( (getlong (cp + 4, &inc) == 1)
451 && (inc >= -20) && (inc <= 20)) {
453 if ( (nice ((int) inc) != -1)
459 /* Failed to parse or failed to nice() */
461 "Can't set the nice value for user %s",
466 if (strncmp (cp, "ulimit=", 7) == 0) {
468 if ( (getlong (cp + 7, &blocks) == 0)
469 || (blocks != (int) blocks)
470 || (set_filesize_limit ((int) blocks) != 0)) {
472 "Can't set the ulimit for user %s",
477 if (strncmp (cp, "umask=", 6) == 0) {
478 unsigned long int mask;
479 if ( (getulong (cp + 6, &mask) == 0)
480 || (mask != (mode_t) mask)) {
482 "Can't set umask value for user %s",
485 (void) umask ((mode_t) mask);
495 extern int errno; /* warning: ANSI C forbids an empty source file */
496 #endif /* !USE_PAM */