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