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