]> granicus.if.org Git - linux-pam/blob - modules/pam_limits/pam_limits.c
Relevant BUGIDs: Red Hat bz 168790
[linux-pam] / modules / pam_limits / pam_limits.c
1 /*
2  * pam_limits - impose resource limits when opening a user session
3  *
4  * 1.6 - modified for PLD (added process priority settings)
5  *       by Marcin Korzonek <mkorz@shadow.eu.org>
6  * 1.5 - Elliot Lee's "max system logins patch"
7  * 1.4 - addressed bug in configuration file parser
8  * 1.3 - modified the configuration file format
9  * 1.2 - added 'debug' and 'conf=' arguments
10  * 1.1 - added @group support
11  * 1.0 - initial release - Linux ONLY
12  *
13  * See end for Copyright information
14  */
15
16 #if !(defined(linux))
17 #error THIS CODE IS KNOWN TO WORK ONLY ON LINUX !!!
18 #endif
19
20 #include "config.h"
21
22 #include <stdio.h>
23 #include <unistd.h>
24 #include <string.h>
25 #include <ctype.h>
26 #include <stdlib.h>
27 #include <errno.h>
28 #include <syslog.h>
29 #include <stdarg.h>
30 #include <sys/types.h>
31 #include <sys/stat.h>
32 #include <sys/resource.h>
33 #include <limits.h>
34
35 #include <utmp.h>
36 #ifndef UT_USER  /* some systems have ut_name instead of ut_user */
37 #define UT_USER ut_user
38 #endif
39
40 #include <grp.h>
41 #include <pwd.h>
42
43 /* Module defines */
44 #define LINE_LENGTH 1024
45
46 #define LIMITS_DEF_USER     0 /* limit was set by an user entry */
47 #define LIMITS_DEF_GROUP    1 /* limit was set by a group entry */
48 #define LIMITS_DEF_ALLGROUP 2 /* limit was set by a group entry */
49 #define LIMITS_DEF_ALL      3 /* limit was set by an default entry */
50 #define LIMITS_DEF_DEFAULT  4 /* limit was set by an default entry */
51 #define LIMITS_DEF_NONE     5 /* this limit was not set yet */
52
53 static const char *limits_def_names[] = {
54        "USER",
55        "GROUP",
56        "ALLGROUP",
57        "ALL",
58        "DEFAULT",
59        "NONE",
60        NULL
61 };
62
63 struct user_limits_struct {
64     int supported;
65     int src_soft;
66     int src_hard;
67     struct rlimit limit;
68 };
69
70 /* internal data */
71 struct pam_limit_s {
72     int login_limit;     /* the max logins limit */
73     int login_limit_def; /* which entry set the login limit */
74     int flag_numsyslogins; /* whether to limit logins only for a
75                               specific user or to count all logins */
76     int priority;        /* the priority to run user process with */
77     struct user_limits_struct limits[RLIM_NLIMITS];
78     char conf_file[BUFSIZ];
79     int utmp_after_pam_call;
80     char login_group[LINE_LENGTH];
81 };
82
83 #define LIMIT_LOGIN RLIM_NLIMITS+1
84 #define LIMIT_NUMSYSLOGINS RLIM_NLIMITS+2
85
86 #define LIMIT_PRI RLIM_NLIMITS+3
87
88 #define LIMIT_SOFT  1
89 #define LIMIT_HARD  2
90
91 #define PAM_SM_SESSION
92
93 #include <security/pam_modules.h>
94 #include <security/_pam_macros.h>
95 #include <security/_pam_modutil.h>
96 #include <security/pam_ext.h>
97
98 /* argument parsing */
99
100 #define PAM_DEBUG_ARG       0x0001
101 #define PAM_DO_SETREUID     0x0002
102 #define PAM_UTMP_EARLY      0x0004
103
104 static int
105 _pam_parse (const pam_handle_t *pamh, int argc, const char **argv,
106             struct pam_limit_s *pl)
107 {
108     int ctrl=0;
109
110     /* step through arguments */
111     for (ctrl=0; argc-- > 0; ++argv) {
112
113         /* generic options */
114
115         if (!strcmp(*argv,"debug")) {
116             ctrl |= PAM_DEBUG_ARG;
117         } else if (!strncmp(*argv,"conf=",5)) {
118             strncpy(pl->conf_file,*argv+5,sizeof(pl->conf_file)-1);
119         } else if (!strncmp(*argv,"change_uid",10)) {
120             ctrl |= PAM_DO_SETREUID;
121         } else if (!strcmp(*argv,"utmp_early")) {
122             ctrl |= PAM_UTMP_EARLY;
123         } else {
124             pam_syslog(pamh,LOG_ERR,"pam_parse: unknown option; %s",*argv);
125         }
126     }
127     pl->conf_file[sizeof(pl->conf_file) - 1] = '\0';
128
129     return ctrl;
130 }
131
132
133 #define LIMITED_OK 0 /* limit setting appeared to work */
134 #define LIMIT_ERR  1 /* error setting a limit */
135 #define LOGIN_ERR  2 /* too many logins err */
136
137 /* Counts the number of user logins and check against the limit*/
138 static int
139 check_logins (pam_handle_t *pamh, const char *name, int limit, int ctrl,
140               struct pam_limit_s *pl)
141 {
142     struct utmp *ut;
143     int count;
144
145     if (ctrl & PAM_DEBUG_ARG) {
146         pam_syslog(pamh,LOG_DEBUG, "checking logins for '%s' (maximum of %d)\n",
147                  name, limit);
148     }
149
150     if (limit < 0)
151         return 0; /* no limits imposed */
152     if (limit == 0) /* maximum 0 logins ? */ {
153         pam_syslog(pamh,LOG_WARNING, "No logins allowed for '%s'\n", name);
154         return LOGIN_ERR;
155     }
156
157     setutent();
158
159     /* Because there is no definition about when an application
160        actually adds a utmp entry, some applications bizarrely do the
161        utmp call before the have PAM authenticate them to the system:
162        you're logged it, sort of...? Anyway, you can use the
163        "utmp_early" module argument in your PAM config file to make
164        allowances for this sort of problem. (There should be a PAM
165        standard for this, since if a module wants to actually map a
166        username then any early utmp entry will be for the unmapped
167        name = broken.) */
168
169     if (ctrl & PAM_UTMP_EARLY) {
170         count = 0;
171     } else {
172         count = 1;
173     }
174
175     while((ut = getutent())) {
176 #ifdef USER_PROCESS
177         if (ut->ut_type != USER_PROCESS) {
178             continue;
179         }
180 #endif
181         if (ut->UT_USER[0] == '\0') {
182             continue;
183         }
184         if (!pl->flag_numsyslogins) {
185             if (((pl->login_limit_def == LIMITS_DEF_USER)
186                  || (pl->login_limit_def == LIMITS_DEF_GROUP)
187                  || (pl->login_limit_def == LIMITS_DEF_DEFAULT))
188                 && strncmp(name, ut->UT_USER, sizeof(ut->UT_USER)) != 0) {
189                 continue;
190             }
191             if ((pl->login_limit_def == LIMITS_DEF_ALLGROUP)
192                 && !_pammodutil_user_in_group_nam_nam(pamh, ut->UT_USER, pl->login_group)) {
193                 continue;
194             }
195         }
196         if (++count > limit) {
197             break;
198         }
199     }
200     endutent();
201     if (count > limit) {
202         if (name) {
203             pam_syslog(pamh,LOG_WARNING, "Too many logins (max %d) for %s",
204                      limit, name);
205         } else {
206             pam_syslog(pamh,LOG_WARNING, "Too many system logins (max %d)", limit);
207         }
208         return LOGIN_ERR;
209     }
210     return 0;
211 }
212
213 static int init_limits(struct pam_limit_s *pl)
214 {
215     int i;
216     int retval = PAM_SUCCESS;
217
218     D(("called."));
219
220     for(i = 0; i < RLIM_NLIMITS; i++) {
221         int r = getrlimit(i, &pl->limits[i].limit);
222         if (r == -1) {
223             pl->limits[i].supported = 0;
224             if (errno != EINVAL) {
225                 retval = !PAM_SUCCESS;
226             }
227         } else {
228             pl->limits[i].supported = 1;
229             pl->limits[i].src_soft = LIMITS_DEF_NONE;
230             pl->limits[i].src_hard = LIMITS_DEF_NONE;
231         }
232     }
233
234     errno = 0;
235     pl->priority = getpriority (PRIO_PROCESS, 0);
236     if (pl->priority == -1 && errno != 0)
237       retval = !PAM_SUCCESS;
238     pl->login_limit = -2;
239     pl->login_limit_def = LIMITS_DEF_NONE;
240
241     return retval;
242 }
243
244 static void
245 process_limit (const pam_handle_t *pamh, int source, const char *lim_type,
246                const char *lim_item, const char *lim_value,
247                int ctrl, struct pam_limit_s *pl)
248 {
249     int limit_item;
250     int limit_type = 0;
251     int int_value = 0;
252     rlim_t rlimit_value = 0;
253     char *endptr;
254     const char *value_orig = lim_value;
255
256     if (ctrl & PAM_DEBUG_ARG)
257          pam_syslog(pamh,LOG_DEBUG, "%s: processing %s %s %s for %s\n",
258                   __FUNCTION__,lim_type,lim_item,lim_value,
259                   limits_def_names[source]);
260
261     if (strcmp(lim_item, "cpu") == 0)
262         limit_item = RLIMIT_CPU;
263     else if (strcmp(lim_item, "fsize") == 0)
264         limit_item = RLIMIT_FSIZE;
265     else if (strcmp(lim_item, "data") == 0)
266         limit_item = RLIMIT_DATA;
267     else if (strcmp(lim_item, "stack") == 0)
268         limit_item = RLIMIT_STACK;
269     else if (strcmp(lim_item, "core") == 0)
270         limit_item = RLIMIT_CORE;
271     else if (strcmp(lim_item, "rss") == 0)
272         limit_item = RLIMIT_RSS;
273     else if (strcmp(lim_item, "nproc") == 0)
274         limit_item = RLIMIT_NPROC;
275     else if (strcmp(lim_item, "nofile") == 0)
276         limit_item = RLIMIT_NOFILE;
277     else if (strcmp(lim_item, "memlock") == 0)
278         limit_item = RLIMIT_MEMLOCK;
279     else if (strcmp(lim_item, "as") == 0)
280         limit_item = RLIMIT_AS;
281 #ifdef RLIMIT_LOCKS
282     else if (strcmp(lim_item, "locks") == 0)
283         limit_item = RLIMIT_LOCKS;
284 #endif
285 #ifdef RLIMIT_SIGPENDING
286     else if (strcmp(lim_item, "sigpending") == 0)
287         limit_item = RLIMIT_SIGPENDING;
288 #endif
289 #ifdef RLIMIT_MSGQUEUE
290     else if (strcmp(lim_item, "msgqueue") == 0)
291         limit_item = RLIMIT_MSGQUEUE;
292 #endif
293 #ifdef RLIMIT_NICE
294     else if (strcmp(lim_item, "nice") == 0)
295         limit_item = RLIMIT_NICE;
296 #endif
297 #ifdef RLIMIT_RTPRIO
298     else if (strcmp(lim_item, "rtprio") == 0)
299         limit_item = RLIMIT_RTPRIO;
300 #endif
301     else if (strcmp(lim_item, "maxlogins") == 0) {
302         limit_item = LIMIT_LOGIN;
303         pl->flag_numsyslogins = 0;
304     } else if (strcmp(lim_item, "maxsyslogins") == 0) {
305         limit_item = LIMIT_NUMSYSLOGINS;
306         pl->flag_numsyslogins = 1;
307     } else if (strcmp(lim_item, "priority") == 0) {
308         limit_item = LIMIT_PRI;
309     } else {
310         pam_syslog(pamh,LOG_DEBUG,"unknown limit item '%s'", lim_item);
311         return;
312     }
313
314     if (strcmp(lim_type,"soft")==0)
315         limit_type=LIMIT_SOFT;
316     else if (strcmp(lim_type, "hard")==0)
317         limit_type=LIMIT_HARD;
318     else if (strcmp(lim_type,"-")==0)
319         limit_type=LIMIT_SOFT | LIMIT_HARD;
320     else if (limit_item != LIMIT_LOGIN && limit_item != LIMIT_NUMSYSLOGINS) {
321         pam_syslog(pamh,LOG_DEBUG,"unknown limit type '%s'", lim_type);
322         return;
323     }
324         if (limit_item != LIMIT_PRI
325 #ifdef RLIMIT_NICE
326             && limit_item != RLIMIT_NICE
327 #endif
328             && (strcmp(lim_value, "-1") == 0
329                 || strcmp(lim_value, "-") == 0 || strcmp(lim_value, "unlimited") == 0
330                 || strcmp(lim_value, "infinity") == 0)) {
331                 int_value = -1;
332                 rlimit_value = RLIM_INFINITY;
333         } else if (limit_item == LIMIT_PRI || limit_item == LIMIT_LOGIN ||
334 #ifdef RLIMIT_NICE
335                 limit_item == RLIMIT_NICE ||
336 #endif
337                 limit_item == LIMIT_NUMSYSLOGINS) {
338                 long temp;
339                 temp = strtol (lim_value, &endptr, 10);
340                 temp = temp < INT_MAX ? temp : INT_MAX;
341                 int_value = temp > INT_MIN ? temp : INT_MIN;
342                 if (int_value == 0 && value_orig == endptr) {
343                         pam_syslog(pamh,LOG_DEBUG, "wrong limit value '%s' for limit type '%s'",
344                                 lim_value, lim_type);
345             return;
346                 }
347         } else {
348 #ifdef __USE_FILE_OFFSET64
349                 rlimit_value = strtoull (lim_value, &endptr, 10);
350 #else
351                 rlimit_value = strtoul (lim_value, &endptr, 10);
352 #endif
353                 if (rlimit_value == 0 && value_orig == endptr) {
354                         pam_syslog(pamh,LOG_DEBUG, "wrong limit value '%s' for limit type '%s'",
355                                 lim_value, lim_type);
356                         return;
357                 }
358         }
359
360     /* one more special case when limiting logins */
361     if ((source == LIMITS_DEF_ALL || source == LIMITS_DEF_ALLGROUP)
362                 && (limit_item != LIMIT_LOGIN)) {
363         if (ctrl & PAM_DEBUG_ARG)
364             pam_syslog(pamh,LOG_DEBUG,
365                         "'%%' domain valid for maxlogins type only");
366         return;
367     }
368
369     switch(limit_item) {
370         case RLIMIT_CPU:
371          if (rlimit_value != RLIM_INFINITY)
372             rlimit_value *= 60;
373          break;
374         case RLIMIT_FSIZE:
375         case RLIMIT_DATA:
376         case RLIMIT_STACK:
377         case RLIMIT_CORE:
378         case RLIMIT_RSS:
379         case RLIMIT_MEMLOCK:
380         case RLIMIT_AS:
381          if (rlimit_value != RLIM_INFINITY)
382             rlimit_value *= 1024;
383          break;
384 #ifdef RLIMIT_NICE
385         case RLIMIT_NICE:
386          if (int_value > 19)
387             int_value = 19;
388          rlimit_value = 19 - int_value;
389 #endif
390          break;
391     }
392
393     if ( (limit_item != LIMIT_LOGIN)
394          && (limit_item != LIMIT_NUMSYSLOGINS)
395          && (limit_item != LIMIT_PRI) ) {
396         if (limit_type & LIMIT_SOFT) {
397             if (pl->limits[limit_item].src_soft < source) {
398                 return;
399             } else {
400                 pl->limits[limit_item].limit.rlim_cur = rlimit_value;
401                 pl->limits[limit_item].src_soft = source;
402             }
403         }
404         if (limit_type & LIMIT_HARD) {
405             if (pl->limits[limit_item].src_hard < source) {
406                 return;
407             } else {
408                 pl->limits[limit_item].limit.rlim_max = rlimit_value;
409                 pl->limits[limit_item].src_hard = source;
410             }
411         }
412     } else {
413         /* recent kernels support negative priority limits (=raise priority) */
414
415         if (limit_item == LIMIT_PRI) {
416                 pl->priority = int_value;
417         } else {
418                 if (pl->login_limit_def < source) {
419                     return;
420                 } else {
421                     pl->login_limit = int_value;
422                     pl->login_limit_def = source;
423                 }
424         }
425     }
426     return;
427 }
428
429 static int parse_config_file(pam_handle_t *pamh, const char *uname, int ctrl,
430                              struct pam_limit_s *pl)
431 {
432     FILE *fil;
433     char buf[LINE_LENGTH];
434
435 #define CONF_FILE (pl->conf_file[0])?pl->conf_file:LIMITS_FILE
436     /* check for the LIMITS_FILE */
437     if (ctrl & PAM_DEBUG_ARG)
438         pam_syslog(pamh,LOG_DEBUG,"reading settings from '%s'", CONF_FILE);
439     fil = fopen(CONF_FILE, "r");
440     if (fil == NULL) {
441         pam_syslog (pamh, LOG_WARNING,
442                     "can not read settings from %s", CONF_FILE);
443         return PAM_SERVICE_ERR;
444     }
445 #undef CONF_FILE
446
447     /* init things */
448     memset(buf, 0, sizeof(buf));
449     /* start the show */
450     while (fgets(buf, LINE_LENGTH, fil) != NULL) {
451         char domain[LINE_LENGTH];
452         char ltype[LINE_LENGTH];
453         char item[LINE_LENGTH];
454         char value[LINE_LENGTH];
455         int i;
456         size_t j;
457         char *tptr;
458
459         tptr = buf;
460         /* skip the leading white space */
461         while (*tptr && isspace(*tptr))
462             tptr++;
463         strncpy(buf, tptr, sizeof(buf)-1);
464         buf[sizeof(buf)-1] = '\0';
465
466         /* Rip off the comments */
467         tptr = strchr(buf,'#');
468         if (tptr)
469             *tptr = '\0';
470         /* Rip off the newline char */
471         tptr = strchr(buf,'\n');
472         if (tptr)
473             *tptr = '\0';
474         /* Anything left ? */
475         if (!strlen(buf)) {
476             memset(buf, 0, sizeof(buf));
477             continue;
478         }
479
480         memset(domain, 0, sizeof(domain));
481         memset(ltype, 0, sizeof(ltype));
482         memset(item, 0, sizeof(item));
483         memset(value, 0, sizeof(value));
484
485         i = sscanf(buf,"%s%s%s%s", domain, ltype, item, value);
486         D(("scanned line[%d]: domain[%s], ltype[%s], item[%s], value[%s]",
487            i, domain, ltype, item, value));
488
489         for(j=0; j < strlen(ltype); j++)
490             ltype[j]=tolower(ltype[j]);
491         for(j=0; j < strlen(item); j++)
492             item[j]=tolower(item[j]);
493         for(j=0; j < strlen(value); j++)
494             value[j]=tolower(value[j]);
495
496         if (i == 4) { /* a complete line */
497             if (strcmp(uname, domain) == 0) /* this user have a limit */
498                 process_limit(pamh, LIMITS_DEF_USER, ltype, item, value, ctrl, pl);
499             else if (domain[0]=='@') {
500                     if (ctrl & PAM_DEBUG_ARG) {
501                         pam_syslog(pamh,LOG_DEBUG, "checking if %s is in group %s",
502                                 uname, domain + 1);
503                     }
504                 if (_pammodutil_user_in_group_nam_nam(pamh, uname, domain+1))
505                     process_limit(pamh, LIMITS_DEF_GROUP, ltype, item, value, ctrl,
506                                   pl);
507             } else if (domain[0]=='%') {
508                     if (ctrl & PAM_DEBUG_ARG) {
509                         pam_syslog(pamh,LOG_DEBUG, "checking if %s is in group %s",
510                                 uname, domain + 1);
511                     }
512                 if (strcmp(domain,"%") == 0)
513                     process_limit(pamh, LIMITS_DEF_ALL, ltype, item, value, ctrl,
514                                   pl);
515                 else if (_pammodutil_user_in_group_nam_nam(pamh, uname, domain+1)) {
516                     strcpy(pl->login_group, domain+1);
517                     process_limit(pamh, LIMITS_DEF_ALLGROUP, ltype, item, value, ctrl,
518                                   pl);
519                 }
520             } else if (strcmp(domain, "*") == 0)
521                 process_limit(pamh, LIMITS_DEF_DEFAULT, ltype, item, value, ctrl,
522                               pl);
523         } else if (i == 2 && ltype[0] == '-') { /* Probably a no-limit line */
524             if (strcmp(uname, domain) == 0) {
525                 if (ctrl & PAM_DEBUG_ARG) {
526                     pam_syslog(pamh,LOG_DEBUG, "no limits for '%s'", uname);
527                 }
528                 fclose(fil);
529                 return PAM_IGNORE;
530             } else if (domain[0] == '@' && _pammodutil_user_in_group_nam_nam(pamh, uname, domain+1)) {
531                 if (ctrl & PAM_DEBUG_ARG) {
532                     pam_syslog(pamh,LOG_DEBUG, "no limits for '%s' in group '%s'",
533                              uname, domain+1);
534                 }
535                 fclose(fil);
536                 return PAM_IGNORE;
537             }
538         } else {
539             pam_syslog(pamh,LOG_DEBUG,"invalid line '%s' - skipped", buf);
540         }
541     }
542     fclose(fil);
543     return PAM_SUCCESS;
544 }
545
546 static int setup_limits(pam_handle_t *pamh,
547                         const char *uname, uid_t uid, int ctrl,
548                         struct pam_limit_s *pl)
549 {
550     int i;
551     int status;
552     int retval = LIMITED_OK;
553
554     if (uid == 0) {
555         /* do not impose limits (+ve limits anyway) on the superuser */
556         if (pl->priority > 0) {
557             if (ctrl & PAM_DEBUG_ARG) {
558                 pam_syslog(pamh,LOG_DEBUG, "user '%s' has UID 0 - no limits imposed",
559                          uname);
560             }
561             pl->priority = 0;
562         }
563     }
564
565     for (i=0, status=LIMITED_OK; i<RLIM_NLIMITS; i++) {
566         if (!pl->limits[i].supported) {
567             /* skip it if its not known to the system */
568             continue;
569         }
570         if (pl->limits[i].src_soft == LIMITS_DEF_NONE &&
571             pl->limits[i].src_hard == LIMITS_DEF_NONE) {
572             /* skip it if its not initialized */
573             continue;
574         }
575         if (pl->limits[i].limit.rlim_cur > pl->limits[i].limit.rlim_max)
576             pl->limits[i].limit.rlim_cur = pl->limits[i].limit.rlim_max;
577         status |= setrlimit(i, &pl->limits[i].limit);
578     }
579
580     if (status) {
581         retval = LIMIT_ERR;
582     }
583
584     status = setpriority(PRIO_PROCESS, 0, pl->priority);
585     if (status != 0) {
586         retval = LIMIT_ERR;
587     }
588
589     if (uid == 0) {
590         D(("skip login limit check for uid=0"));
591     } else if (pl->login_limit > 0) {
592         if (check_logins(pamh, uname, pl->login_limit, ctrl, pl) == LOGIN_ERR) {
593             retval |= LOGIN_ERR;
594         }
595     } else if (pl->login_limit == 0) {
596         retval |= LOGIN_ERR;
597     }
598
599     return retval;
600 }
601
602 /* now the session stuff */
603 PAM_EXTERN int
604 pam_sm_open_session (pam_handle_t *pamh, int flags UNUSED,
605                      int argc, const char **argv)
606 {
607     int retval;
608     char *user_name;
609     struct passwd *pwd;
610     int ctrl;
611     struct pam_limit_s pl;
612
613     D(("called."));
614
615     memset(&pl, 0, sizeof(pl));
616
617     ctrl = _pam_parse(pamh, argc, argv, &pl);
618     retval = pam_get_item( pamh, PAM_USER, (void*) &user_name );
619     if ( user_name == NULL || retval != PAM_SUCCESS ) {
620         pam_syslog(pamh,LOG_CRIT, "open_session - error recovering username");
621         return PAM_SESSION_ERR;
622      }
623
624     pwd = getpwnam(user_name);
625     if (!pwd) {
626         if (ctrl & PAM_DEBUG_ARG)
627             pam_syslog(pamh,LOG_WARNING, "open_session username '%s' does not exist",
628                                    user_name);
629         return PAM_SESSION_ERR;
630     }
631
632     retval = init_limits(&pl);
633     if (retval != PAM_SUCCESS) {
634         pam_syslog(pamh,LOG_WARNING, "cannot initialize");
635         return PAM_ABORT;
636     }
637
638     retval = parse_config_file(pamh, pwd->pw_name, ctrl, &pl);
639     if (retval == PAM_IGNORE) {
640         D(("the configuration file has an applicable '<domain> -' entry"));
641         return PAM_SUCCESS;
642     }
643     if (retval != PAM_SUCCESS) {
644         pam_syslog(pamh,LOG_WARNING, "error parsing the configuration file");
645         return retval;
646     }
647
648     if (ctrl & PAM_DO_SETREUID) {
649         setreuid(pwd->pw_uid, -1);
650     }
651     retval = setup_limits(pamh, pwd->pw_name, pwd->pw_uid, ctrl, &pl);
652     if (retval != LIMITED_OK) {
653         return PAM_PERM_DENIED;
654     }
655
656     return PAM_SUCCESS;
657 }
658
659 PAM_EXTERN int
660 pam_sm_close_session (pam_handle_t *pamh UNUSED, int flags UNUSED,
661                       int argc UNUSED, const char **argv UNUSED)
662 {
663      /* nothing to do */
664      return PAM_SUCCESS;
665 }
666
667 #ifdef PAM_STATIC
668
669 /* static module data */
670
671 struct pam_module _pam_limits_modstruct = {
672      "pam_limits",
673      NULL,
674      NULL,
675      NULL,
676      pam_sm_open_session,
677      pam_sm_close_session,
678      NULL
679 };
680 #endif
681
682 /*
683  * Copyright (c) Cristian Gafton, 1996-1997, <gafton@redhat.com>
684  *                                              All rights reserved.
685  *
686  * Redistribution and use in source and binary forms, with or without
687  * modification, are permitted provided that the following conditions
688  * are met:
689  * 1. Redistributions of source code must retain the above copyright
690  *    notice, and the entire permission notice in its entirety,
691  *    including the disclaimer of warranties.
692  * 2. Redistributions in binary form must reproduce the above copyright
693  *    notice, this list of conditions and the following disclaimer in the
694  *    documentation and/or other materials provided with the distribution.
695  * 3. The name of the author may not be used to endorse or promote
696  *    products derived from this software without specific prior
697  *    written permission.
698  *
699  * ALTERNATIVELY, this product may be distributed under the terms of
700  * the GNU Public License, in which case the provisions of the GPL are
701  * required INSTEAD OF the above restrictions.  (This clause is
702  * necessary due to a potential bad interaction between the GPL and
703  * the restrictions contained in a BSD-style copyright.)
704  *
705  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
706  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
707  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
708  * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
709  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
710  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
711  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
712  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
713  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
714  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
715  * OF THE POSSIBILITY OF SUCH DAMAGE.
716  */