Initial commit for Tizen
[profile/extras/shadow-utils.git] / 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: limits.c 2849 2009-04-30 21:08:49Z nekral-guest $"
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 resource, const char *value,
69                  unsigned int multiplier)
70 {
71         struct rlimit rlim;
72         long limit;
73
74         if (getlong (value, &limit) == 0) {
75                 return 0;
76         }
77         limit *= multiplier;
78         if (limit != (rlim_t) limit) {
79                 return 0;
80         }
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;
85         }
86         return 0;
87 }
88
89
90 static int set_prio (const char *value)
91 {
92         long prio;
93
94         if (   (getlong (value, &prio) == 0)
95             || (prio != (int) prio)) {
96                 return 0;
97         }
98         if (setpriority (PRIO_PROCESS, 0, (int) prio) != 0) {
99                 return LOGIN_ERROR_RLIMIT;
100         }
101         return 0;
102 }
103
104
105 static int set_umask (const char *value)
106 {
107         unsigned long int mask;
108
109         if (   (getulong (value, &mask) == 0)
110             || (mask != (mode_t) mask)) {
111                 return 0;
112         }
113
114         (void) umask ((mode_t) mask);
115         return 0;
116 }
117
118
119 /* Counts the number of user logins and check against the limit */
120 static int check_logins (const char *name, const char *maxlogins)
121 {
122 #ifdef USE_UTMPX
123         struct utmpx *ut;
124 #else                           /* !USE_UTMPX */
125         struct utmp *ut;
126 #endif                          /* !USE_UTMPX */
127         unsigned long limit, count;
128
129         if (getulong (maxlogins, &limit) == 0) {
130                 return 0;
131         }
132
133         if (0 == limit) {       /* maximum 0 logins ? */
134                 SYSLOG ((LOG_WARN, "No logins allowed for `%s'\n", name));
135                 return LOGIN_ERROR_LOGIN;
136         }
137
138         count = 0;
139 #ifdef USE_UTMPX
140         setutxent ();
141         while ((ut = getutxent ())) {
142 #else                           /* !USE_UTMPX */
143         setutent ();
144         while ((ut = getutent ())) {
145 #endif                          /* !USE_UTMPX */
146                 if (USER_PROCESS != ut->ut_type) {
147                         continue;
148                 }
149                 if ('\0' == ut->ut_user[0]) {
150                         continue;
151                 }
152                 if (strncmp (name, ut->ut_user, sizeof (ut->ut_user)) != 0) {
153                         continue;
154                 }
155                 count++;
156                 if (count > limit) {
157                         break;
158                 }
159         }
160 #ifdef USE_UTMPX
161         endutxent ();
162 #else                           /* !USE_UTMPX */
163         endutent ();
164 #endif                          /* !USE_UTMPX */
165         /*
166          * This is called after setutmp(), so the number of logins counted
167          * includes the user who is currently trying to log in.
168          */
169         if (count > limit) {
170                 SYSLOG ((LOG_WARN, "Too many logins (max %d) for %s\n",
171                          limit, name));
172                 return LOGIN_ERROR_LOGIN;
173         }
174         return 0;
175 }
176
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
180  *
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')
184  * where:
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)
200  *
201  * Return value:
202  *              0 = okay, of course
203  *              LOGIN_ERROR_RLIMIT = error setting some RLIMIT
204  *              LOGIN_ERROR_LOGIN  = error - too many logins for this user
205  *
206  * buf - the limits string
207  * name - the username
208  */
209 static int do_user_limits (const char *buf, const char *name)
210 {
211         const char *pp;
212         int retval = 0;
213         bool reported = false;
214
215         pp = buf;
216
217         while ('\0' != *pp) {
218                 switch (*pp++) {
219 #ifdef RLIMIT_AS
220                 case 'a':
221                 case 'A':
222                         /* RLIMIT_AS - max address space (KB) */
223                         retval |= setrlimit_value (RLIMIT_AS, pp, 1024);
224                         break;
225 #endif
226 #ifdef RLIMIT_CPU
227                 case 't':
228                 case 'T':
229                         /* RLIMIT_CPU - max CPU time (MIN) */
230                         retval |= setrlimit_value (RLIMIT_CPU, pp, 60);
231                         break;
232 #endif
233 #ifdef RLIMIT_DATA
234                 case 'd':
235                 case 'D':
236                         /* RLIMIT_DATA - max data size (KB) */
237                         retval |= setrlimit_value (RLIMIT_DATA, pp, 1024);
238                         break;
239 #endif
240 #ifdef RLIMIT_FSIZE
241                 case 'f':
242                 case 'F':
243                         /* RLIMIT_FSIZE - Maximum filesize (KB) */
244                         retval |= setrlimit_value (RLIMIT_FSIZE, pp, 1024);
245                         break;
246 #endif
247 #ifdef RLIMIT_NPROC
248                 case 'u':
249                 case 'U':
250                         /* RLIMIT_NPROC - max number of processes */
251                         retval |= setrlimit_value (RLIMIT_NPROC, pp, 1);
252                         break;
253 #endif
254 #ifdef RLIMIT_CORE
255                 case 'c':
256                 case 'C':
257                         /* RLIMIT_CORE - max core file size (KB) */
258                         retval |= setrlimit_value (RLIMIT_CORE, pp, 1024);
259                         break;
260 #endif
261 #ifdef RLIMIT_MEMLOCK
262                 case 'm':
263                 case 'M':
264                         /* RLIMIT_MEMLOCK - max locked-in-memory address space (KB) */
265                         retval |= setrlimit_value (RLIMIT_MEMLOCK, pp, 1024);
266                         break;
267 #endif
268 #ifdef RLIMIT_NOFILE
269                 case 'n':
270                 case 'N':
271                         /* RLIMIT_NOFILE - max number of open files */
272                         retval |= setrlimit_value (RLIMIT_NOFILE, pp, 1);
273                         break;
274 #endif
275 #ifdef RLIMIT_RSS
276                 case 'r':
277                 case 'R':
278                         /* RLIMIT_RSS - max resident set size (KB) */
279                         retval |= setrlimit_value (RLIMIT_RSS, pp, 1024);
280                         break;
281 #endif
282 #ifdef RLIMIT_STACK
283                 case 's':
284                 case 'S':
285                         /* RLIMIT_STACK - max stack size (KB) */
286                         retval |= setrlimit_value (RLIMIT_STACK, pp, 1024);
287                         break;
288 #endif
289 #ifdef RLIMIT_NICE
290                 case 'i':
291                 case 'I':
292                         /* RLIMIT_NICE - max scheduling priority (0..39) */
293                         retval |= setrlimit_value (RLIMIT_NICE, pp, 1);
294                         break;
295 #endif
296 #ifdef RLIMIT_RTPRIO
297                 case 'o':
298                 case 'O':
299                         /* RLIMIT_RTPRIO - max real time priority (0..MAX_RT_PRIO) */
300                         retval |= setrlimit_value (RLIMIT_RTPRIO, pp, 1);
301                         break;
302 #endif
303                 case 'k':
304                 case 'K':
305                         retval |= set_umask (pp);
306                         break;
307                 case 'l':
308                 case 'L':
309                         /* LIMIT the number of concurrent logins */
310                         retval |= check_logins (name, pp);
311                         break;
312                 case 'p':
313                 case 'P':
314                         retval |= set_prio (pp);
315                         break;
316                 default:
317                         /* Only report invalid strings once */
318                         if (!reported) {
319                                 SYSLOG ((LOG_WARN,
320                                          "Invalid limit string: '%s'",
321                                          pp-1));
322                                 reported = true;
323                                 retval |= LOGIN_ERROR_RLIMIT;
324                         }
325                 }
326         }
327         return retval;
328 }
329
330 static int setup_user_limits (const char *uname)
331 {
332         /* TODO: allow and use @group syntax --cristiang */
333         FILE *fil;
334         char buf[1024];
335         char name[1024];
336         char limits[1024];
337         char deflimits[1024];
338         char tempbuf[1024];
339
340         /* init things */
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));
346
347         /* start the checks */
348         fil = fopen (LIMITS_FILE, "r");
349         if (fil == NULL) {
350                 return 0;
351         }
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])) {
358                         continue;
359                 }
360                 memzero (tempbuf, sizeof (tempbuf));
361                 /* a valid line should have a username, then spaces,
362                  * then limits
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);
373                                 break;
374                         } else if (strcmp (name, "*") == 0) {
375                                 strcpy (deflimits, tempbuf);
376                         }
377                 }
378         }
379         (void) fclose (fil);
380         if (limits[0] == '\0') {
381                 /* no user specific limits */
382                 if (deflimits[0] == '\0') {     /* no default limits */
383                         return 0;
384                 }
385                 strcpy (limits, deflimits);     /* use the default limits */
386         }
387         return do_user_limits (limits, uname);
388 }
389 #endif                          /* LIMITS */
390
391
392 static void setup_usergroups (const struct passwd *info)
393 {
394         const struct group *grp;
395         mode_t tmpmask;
396
397 /*
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).
401  */
402         if ((0 != info->pw_uid) && (info->pw_uid == info->pw_gid)) {
403                 /* local, no need for xgetgrgid */
404                 grp = getgrgid (info->pw_gid);
405                 if (   (NULL != grp)
406                     && (strcmp (info->pw_name, grp->gr_name) == 0)) {
407                         tmpmask = umask (0777);
408                         tmpmask = (tmpmask & ~070) | ((tmpmask >> 3) & 070);
409                         (void) umask (tmpmask);
410                 }
411         }
412 }
413
414 /*
415  *      set the process nice, ulimit, and umask from the password file entry
416  */
417
418 void setup_limits (const struct passwd *info)
419 {
420         char *cp;
421
422         if (getdef_bool ("USERGROUPS_ENAB")) {
423                 setup_usergroups (info);
424         }
425
426         /*
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.
430          */
431
432         if (getdef_bool ("QUOTAS_ENAB")) {
433 #ifdef LIMITS
434                 if (info->pw_uid != 0) {
435                         if (setup_user_limits (info->pw_name) &
436                             LOGIN_ERROR_LOGIN) {
437                                 (void) fputs (_("Too many logins.\n"), stderr);
438                                 (void) sleep (2); /* XXX: Should be FAIL_DELAY */
439                                 exit (EXIT_FAILURE);
440                         }
441                 }
442 #endif
443                 for (cp = info->pw_gecos; cp != NULL; cp = strchr (cp, ',')) {
444                         if (',' == *cp) {
445                                 cp++;
446                         }
447
448                         if (strncmp (cp, "pri=", 4) == 0) {
449                                 long int inc;
450                                 if (   (getlong (cp + 4, &inc) == 1)
451                                     && (inc >= -20) && (inc <= 20)) {
452                                         errno = 0;
453                                         if (   (nice ((int) inc) != -1)
454                                             || (0 != errno)) {
455                                                 continue;
456                                         }
457                                 }
458
459                                 /* Failed to parse or failed to nice() */
460                                 SYSLOG ((LOG_WARN,
461                                          "Can't set the nice value for user %s",
462                                          info->pw_name));
463
464                                 continue;
465                         }
466                         if (strncmp (cp, "ulimit=", 7) == 0) {
467                                 long int blocks;
468                                 if (   (getlong (cp + 7, &blocks) == 0)
469                                     || (blocks != (int) blocks)
470                                     || (set_filesize_limit ((int) blocks) != 0)) {
471                                         SYSLOG ((LOG_WARN,
472                                                  "Can't set the ulimit for user %s",
473                                                  info->pw_name));
474                                 }
475                                 continue;
476                         }
477                         if (strncmp (cp, "umask=", 6) == 0) {
478                                 unsigned long int mask;
479                                 if (   (getulong (cp + 6, &mask) == 0)
480                                     || (mask != (mode_t) mask)) {
481                                         SYSLOG ((LOG_WARN,
482                                                  "Can't set umask value for user %s",
483                                                  info->pw_name));
484                                 } else {
485                                         (void) umask ((mode_t) mask);
486                                 }
487
488                                 continue;
489                         }
490                 }
491         }
492 }
493
494 #else                           /* !USE_PAM */
495 extern int errno;               /* warning: ANSI C forbids an empty source file */
496 #endif                          /* !USE_PAM */
497