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