]> granicus.if.org Git - shadow/blob - libmisc/limits.c
Updated copyright dates.
[shadow] / libmisc / limits.c
1 /*
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
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
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.
19  *
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.
31  */
32
33 /*
34  * Separated from setup.c.  --marekm
35  * Resource limits thanks to Cristian Gafton.
36  */
37
38 #include <config.h>
39
40 #ifndef USE_PAM
41
42 #ident "$Id$"
43
44 #include <sys/types.h>
45 #include <sys/stat.h>
46 #include <stdio.h>
47 #include "prototypes.h"
48 #include "defines.h"
49 #include <pwd.h>
50 #include "getdef.h"
51 #ifdef HAVE_SYS_RESOURCE_H
52 #include <sys/resource.h>
53 #define LIMITS
54 #endif
55 #ifdef LIMITS
56 #ifndef LIMITS_FILE
57 #define LIMITS_FILE "/etc/limits"
58 #endif
59 #define LOGIN_ERROR_RLIMIT      1
60 #define LOGIN_ERROR_LOGIN       2
61 /* Set a limit on a resource */
62 /*
63  *      rlimit - RLIMIT_XXXX
64  *      value - string value to be read
65  *      multiplier - value*multiplier is the actual limit
66  */
67 static int
68 setrlimit_value (unsigned int rlimit, const char *value,
69                  unsigned int multiplier)
70 {
71         struct rlimit rlim;
72         long limit;
73         char **endptr = (char **) &value;
74         const char *value_orig = value;
75
76         limit = strtol (value, endptr, 10);
77         if (limit == 0 && value_orig == *endptr)        /* no chars read */
78                 return 0;
79         limit *= multiplier;
80         rlim.rlim_cur = limit;
81         rlim.rlim_max = limit;
82         if (setrlimit (rlimit, &rlim))
83                 return LOGIN_ERROR_RLIMIT;
84         return 0;
85 }
86
87
88 static int set_prio (const char *value)
89 {
90         int prio;
91         char **endptr = (char **) &value;
92
93         prio = strtol (value, endptr, 10);
94         if ((prio == 0) && (value == *endptr))
95                 return 0;
96         if (setpriority (PRIO_PROCESS, 0, prio))
97                 return LOGIN_ERROR_RLIMIT;
98         return 0;
99 }
100
101
102 static int set_umask (const char *value)
103 {
104         mode_t mask;
105         char **endptr = (char **) &value;
106
107         mask = strtol (value, endptr, 8) & 0777;
108         if ((mask == 0) && (value == *endptr))
109                 return 0;
110         umask (mask);
111         return 0;
112 }
113
114
115 /* Counts the number of user logins and check against the limit */
116 static int check_logins (const char *name, const char *maxlogins)
117 {
118 #if HAVE_UTMPX_H
119         struct utmpx *ut;
120 #else
121         struct utmp *ut;
122 #endif
123         unsigned int limit, count;
124         char **endptr = (char **) &maxlogins;
125         const char *ml_orig = maxlogins;
126
127         limit = strtol (maxlogins, endptr, 10);
128         if (limit == 0 && ml_orig == *endptr)   /* no chars read */
129                 return 0;
130
131         if (limit == 0) {       /* maximum 0 logins ? */
132                 SYSLOG ((LOG_WARN, "No logins allowed for `%s'\n", name));
133                 return LOGIN_ERROR_LOGIN;
134         }
135
136         count = 0;
137 #if HAVE_UTMPX_H
138         setutxent ();
139         while ((ut = getutxent ())) {
140 #else
141         setutent ();
142         while ((ut = getutent ())) {
143 #endif
144                 if (ut->ut_type != USER_PROCESS)
145                         continue;
146                 if (ut->ut_user[0] == '\0')
147                         continue;
148                 if (strncmp (name, ut->ut_user, sizeof (ut->ut_user)) != 0)
149                         continue;
150                 if (++count > limit)
151                         break;
152         }
153 #if HAVE_UTMPX_H
154         endutxent ();
155 #else
156         endutent ();
157 #endif
158         /*
159          * This is called after setutmp(), so the number of logins counted
160          * includes the user who is currently trying to log in.
161          */
162         if (count > limit) {
163                 SYSLOG ((LOG_WARN, "Too many logins (max %d) for %s\n",
164                          limit, name));
165                 return LOGIN_ERROR_LOGIN;
166         }
167         return 0;
168 }
169
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
173  *
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')
177  * where:
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)
193  *
194  * Return value:
195  *              0 = okay, of course
196  *              LOGIN_ERROR_RLIMIT = error setting some RLIMIT
197  *              LOGIN_ERROR_LOGIN  = error - too many logins for this user
198  *
199  * buf - the limits string
200  * name - the username
201  */
202 static int do_user_limits (const char *buf, const char *name)
203 {
204         const char *pp;
205         int retval = 0;
206
207         pp = buf;
208
209         while (*pp != '\0')
210                 switch (*pp++) {
211 #ifdef RLIMIT_AS
212                 case 'a':
213                 case 'A':
214                         /* RLIMIT_AS - max address space (KB) */
215                         retval |= setrlimit_value (RLIMIT_AS, pp, 1024);
216                         break;
217 #endif
218 #ifdef RLIMIT_CPU
219                 case 't':
220                 case 'T':
221                         /* RLIMIT_CPU - max CPU time (MIN) */
222                         retval |= setrlimit_value (RLIMIT_CPU, pp, 60);
223                         break;
224 #endif
225 #ifdef RLIMIT_DATA
226                 case 'd':
227                 case 'D':
228                         /* RLIMIT_DATA - max data size (KB) */
229                         retval |= setrlimit_value (RLIMIT_DATA, pp, 1024);
230                         break;
231 #endif
232 #ifdef RLIMIT_FSIZE
233                 case 'f':
234                 case 'F':
235                         /* RLIMIT_FSIZE - Maximum filesize (KB) */
236                         retval |= setrlimit_value (RLIMIT_FSIZE, pp, 1024);
237                         break;
238 #endif
239 #ifdef RLIMIT_NPROC
240                 case 'u':
241                 case 'U':
242                         /* RLIMIT_NPROC - max number of processes */
243                         retval |= setrlimit_value (RLIMIT_NPROC, pp, 1);
244                         break;
245 #endif
246 #ifdef RLIMIT_CORE
247                 case 'c':
248                 case 'C':
249                         /* RLIMIT_CORE - max core file size (KB) */
250                         retval |= setrlimit_value (RLIMIT_CORE, pp, 1024);
251                         break;
252 #endif
253 #ifdef RLIMIT_MEMLOCK
254                 case 'm':
255                 case 'M':
256                         /* RLIMIT_MEMLOCK - max locked-in-memory address space (KB) */
257                         retval |= setrlimit_value (RLIMIT_MEMLOCK, pp, 1024);
258                         break;
259 #endif
260 #ifdef RLIMIT_NOFILE
261                 case 'n':
262                 case 'N':
263                         /* RLIMIT_NOFILE - max number of open files */
264                         retval |= setrlimit_value (RLIMIT_NOFILE, pp, 1);
265                         break;
266 #endif
267 #ifdef RLIMIT_RSS
268                 case 'r':
269                 case 'R':
270                         /* RLIMIT_RSS - max resident set size (KB) */
271                         retval |= setrlimit_value (RLIMIT_RSS, pp, 1024);
272                         break;
273 #endif
274 #ifdef RLIMIT_STACK
275                 case 's':
276                 case 'S':
277                         /* RLIMIT_STACK - max stack size (KB) */
278                         retval |= setrlimit_value (RLIMIT_STACK, pp, 1024);
279                         break;
280 #endif
281 #ifdef RLIMIT_NICE
282                 case 'i':
283                 case 'I':
284                         /* RLIMIT_NICE - max scheduling priority (0..39) */
285                         retval |= setrlimit_value (RLIMIT_NICE, pp, 1);
286                         break;
287 #endif
288 #ifdef RLIMIT_RTPRIO
289                 case 'o':
290                 case 'O':
291                         /* RLIMIT_RTPRIO - max real time priority (0..MAX_RT_PRIO) */
292                         retval |= setrlimit_value (RLIMIT_RTPRIO, pp, 1);
293                         break;
294 #endif
295                 case 'k':
296                 case 'K':
297                         retval |= set_umask (pp);
298                         break;
299                 case 'l':
300                 case 'L':
301                         /* LIMIT the number of concurent logins */
302                         retval |= check_logins (name, pp);
303                         break;
304                 case 'p':
305                 case 'P':
306                         retval |= set_prio (pp);
307                         break;
308                 }
309         return retval;
310 }
311
312 static int setup_user_limits (const char *uname)
313 {
314         /* TODO: allow and use @group syntax --cristiang */
315         FILE *fil;
316         char buf[1024];
317         char name[1024];
318         char limits[1024];
319         char deflimits[1024];
320         char tempbuf[1024];
321
322         /* init things */
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));
328
329         /* start the checks */
330         fil = fopen (LIMITS_FILE, "r");
331         if (fil == NULL) {
332                 return 0;
333         }
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')
340                         continue;
341                 memzero (tempbuf, sizeof (tempbuf));
342                 /* a valid line should have a username, then spaces,
343                  * then limits
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);
354                                 break;
355                         } else if (strcmp (name, "*") == 0) {
356                                 strcpy (deflimits, tempbuf);
357                         }
358                 }
359         }
360         fclose (fil);
361         if (limits[0] == '\0') {
362                 /* no user specific limits */
363                 if (deflimits[0] == '\0')       /* no default limits */
364                         return 0;
365                 strcpy (limits, deflimits);     /* use the default limits */
366         }
367         return do_user_limits (limits, uname);
368 }
369 #endif                          /* LIMITS */
370
371
372 static void setup_usergroups (const struct passwd *info)
373 {
374         const struct group *grp;
375         mode_t oldmask;
376
377 /*
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).
381  */
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));
388                 }
389         }
390 }
391
392 /*
393  *      set the process nice, ulimit, and umask from the password file entry
394  */
395
396 void setup_limits (const struct passwd *info)
397 {
398         char *cp;
399         int i;
400         long l;
401
402         if (getdef_bool ("USERGROUPS_ENAB"))
403                 setup_usergroups (info);
404
405         /*
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.
409          */
410
411         if (getdef_bool ("QUOTAS_ENAB")) {
412 #ifdef LIMITS
413                 if (info->pw_uid != 0)
414                         if (setup_user_limits (info->pw_name) &
415                             LOGIN_ERROR_LOGIN) {
416                                 fputs (_("Too many logins.\n"), stderr);
417                                 sleep (2);
418                                 exit (1);
419                         }
420 #endif
421                 for (cp = info->pw_gecos; cp != NULL; cp = strchr (cp, ',')) {
422                         if (*cp == ',')
423                                 cp++;
424
425                         if (strncmp (cp, "pri=", 4) == 0) {
426                                 i = atoi (cp + 4);
427                                 if (i >= -20 && i <= 20)
428                                         (void) nice (i);
429
430                                 continue;
431                         }
432                         if (strncmp (cp, "ulimit=", 7) == 0) {
433                                 l = strtol (cp + 7, (char **) 0, 10);
434                                 set_filesize_limit (l);
435                                 continue;
436                         }
437                         if (strncmp (cp, "umask=", 6) == 0) {
438                                 i = strtol (cp + 6, (char **) 0, 8) & 0777;
439                                 (void) umask (i);
440
441                                 continue;
442                         }
443                 }
444         }
445 }
446
447 #else                           /* !USE_PAM */
448 extern int errno;               /* warning: ANSI C forbids an empty source file */
449 #endif                          /* !USE_PAM */
450