]> granicus.if.org Git - linux-pam/blobdiff - modules/pam_limits/pam_limits.c
Relevant BUGIDs:
[linux-pam] / modules / pam_limits / pam_limits.c
index dde9670138e7e4dd633568b0fbf94e8d6c370bcf..4354d3e1239ac959c14602eae3773089da9b4b5a 100644 (file)
@@ -1,13 +1,13 @@
 /*
  * pam_limits - impose resource limits when opening a user session
  *
- * 1.6 - modified for PLD (added process priority settings) 
+ * 1.6 - modified for PLD (added process priority settings)
  *       by Marcin Korzonek <mkorz@shadow.eu.org>
  * 1.5 - Elliot Lee's "max system logins patch"
  * 1.4 - addressed bug in configuration file parser
  * 1.3 - modified the configuration file format
  * 1.2 - added 'debug' and 'conf=' arguments
- * 1.1 - added @group support    
+ * 1.1 - added @group support
  * 1.0 - initial release - Linux ONLY
  *
  * See end for Copyright information
@@ -15,7 +15,7 @@
 
 #if !(defined(linux))
 #error THIS CODE IS KNOWN TO WORK ONLY ON LINUX !!!
-#endif 
+#endif
 
 #include <security/_pam_aconf.h>
 
 #define LIMITS_DEF_GROUP    1 /* limit was set by a group entry */
 #define LIMITS_DEF_DEFAULT  2 /* limit was set by an default entry */
 #define LIMITS_DEF_NONE     3 /* this limit was not set yet */
+#define LIMITS_DEF_ALL      4 /* limit was set by an default entry */
+#define LIMITS_DEF_ALLGROUP 5 /* limit was set by a group entry */
 
 static const char *limits_def_names[] = {
        "USER",
        "GROUP",
        "DEFAULT",
        "NONE",
+       "ALL",
+       "ALLGROUP",
        NULL,
 };
 
@@ -86,6 +90,7 @@ struct pam_limit_s {
 
 #include <security/pam_modules.h>
 #include <security/_pam_macros.h>
+#include <security/_pam_modutil.h>
 
 /* logging */
 static void _pam_log(int err, const char *format, ...)
@@ -143,62 +148,10 @@ static int _pam_parse(int argc, const char **argv, struct pam_limit_s *pl)
 #define LIMIT_ERR  1 /* error setting a limit */
 #define LOGIN_ERR  2 /* too many logins err */
 
-/* checks if a user is on a list of members of the GID 0 group */
-static int is_on_list(char * const *list, const char *member)
-{
-    while (*list) {
-        if (strcmp(*list, member) == 0)
-            return 1;
-        list++;
-    }
-    return 0;
-}
-
-/*
- * Checks if a user is a member of a group - return non-zero if
- * the user is in the group.
- */
-static int is_in_group(const char *user_name, const char *group_name)
-{
-    struct passwd *pwd;
-    struct group *grp, *pgrp;
-    char uname[LINE_LENGTH], gname[LINE_LENGTH];
-    
-    if (!user_name || !strlen(user_name))
-        return 0;
-    if (!group_name || !strlen(group_name))
-        return 0;
-    memset(uname, 0, sizeof(uname));
-    strncpy(uname, user_name, sizeof(uname)-1);
-    memset(gname, 0, sizeof(gname));
-    strncpy(gname, group_name, sizeof(gname)-1);
-        
-    pwd = getpwnam(uname);
-    if (!pwd)
-        return 0;
-
-    /* the info about this group */
-    grp = getgrnam(gname);
-    if (!grp)
-        return 0;
-    
-    /* first check: is a member of the group_name group ? */
-    if (is_on_list(grp->gr_mem, uname))
-        return 1;
-
-    /* next check: user primary group is group_name ? */
-    pgrp = getgrgid(pwd->pw_gid);
-    if (!pgrp)
-        return 0;
-    if (!strcmp(pgrp->gr_name, gname))
-        return 1;
-        
-    return 0;
-}
-    
 /* Counts the number of user logins and check against the limit*/
-static int check_logins(const char *name, int limit, int ctrl,
-                       struct pam_limit_s *pl)
+static int
+check_logins (pam_handle_t *pamh, const char *name, int limit, int ctrl,
+              struct pam_limit_s *pl)
 {
     struct utmp *ut;
     unsigned int count;
@@ -226,7 +179,7 @@ static int check_logins(const char *name, int limit, int ctrl,
        standard for this, since if a module wants to actually map a
        username then any early utmp entry will be for the unmapped
        name = broken.) */
-    
+
     if (ctrl & PAM_UTMP_EARLY) {
        count = 0;
     } else {
@@ -243,12 +196,14 @@ static int check_logins(const char *name, int limit, int ctrl,
             continue;
        }
         if (!pl->flag_numsyslogins) {
-           if ((pl->login_limit_def == LIMITS_DEF_USER)
+           if (((pl->login_limit_def == LIMITS_DEF_USER)
+                || (pl->login_limit_def == LIMITS_DEF_GROUP)
+                || (pl->login_limit_def == LIMITS_DEF_DEFAULT))
                && strncmp(name, ut->UT_USER, sizeof(ut->UT_USER)) != 0) {
                 continue;
            }
-           if ((pl->login_limit_def == LIMITS_DEF_GROUP)
-               && !is_in_group(ut->UT_USER, name)) {
+           if ((pl->login_limit_def == LIMITS_DEF_ALLGROUP)
+               && !_pammodutil_user_in_group_nam_nam(pamh, ut->UT_USER, name)) {
                 continue;
            }
        }
@@ -296,7 +251,7 @@ static int init_limits(struct pam_limit_s *pl)
     pl->login_limit_def = LIMITS_DEF_NONE;
 
     return retval;
-}    
+}
 
 static void process_limit(int source, const char *lim_type,
                          const char *lim_item, const char *lim_value,
@@ -305,9 +260,9 @@ static void process_limit(int source, const char *lim_type,
     int limit_item;
     int limit_type = 0;
     long limit_value;
-    const char **endptr = &lim_value;
+    char *endptr;
     const char *value_orig = lim_value;
-        
+
     if (ctrl & PAM_DEBUG_ARG)
         _pam_log(LOG_DEBUG, "%s: processing %s %s %s for %s\n",
                  __FUNCTION__,lim_type,lim_item,lim_value,
@@ -361,14 +316,10 @@ static void process_limit(int source, const char *lim_type,
         return;
     }
 
-    /*
-     * there is a warning here because the library prototype for this
-     * function is incorrect.
-     */
-    limit_value = strtol(lim_value, endptr, 10);
+    limit_value = strtol (lim_value, &endptr, 10);
 
     /* special case value when limiting logins */
-    if (limit_value == 0 && value_orig == *endptr) { /* no chars read */
+    if (limit_value == 0 && value_orig == endptr) { /* no chars read */
         if (strcmp(lim_value,"-") != 0) {
             _pam_log(LOG_DEBUG,"wrong limit value '%s'", lim_value);
             return;
@@ -381,7 +332,16 @@ static void process_limit(int source, const char *lim_type,
             } else
                 limit_value = -1;
     }
-    
+
+    /* one more special case when limiting logins */
+    if ((source == LIMITS_DEF_ALL || source == LIMITS_DEF_ALLGROUP)
+               && (limit_item != LIMIT_LOGIN)) {
+       if (ctrl & PAM_DEBUG_ARG)
+           _pam_log(LOG_DEBUG,
+                       "'%%' domain valid for maxlogins type only");
+       return;
+    }
+
     switch(limit_item) {
         case RLIMIT_CPU:
             limit_value *= 60;
@@ -433,12 +393,12 @@ static void process_limit(int source, const char *lim_type,
     return;
 }
 
-static int parse_config_file(const char *uname, int ctrl,
+static int parse_config_file(pam_handle_t *pamh, const char *uname, int ctrl,
                             struct pam_limit_s *pl)
 {
     FILE *fil;
     char buf[LINE_LENGTH];
-    
+
 #define CONF_FILE (pl->conf_file[0])?pl->conf_file:LIMITS_FILE
     /* check for the LIMITS_FILE */
     if (ctrl & PAM_DEBUG_ARG)
@@ -449,7 +409,7 @@ static int parse_config_file(const char *uname, int ctrl,
         return PAM_SERVICE_ERR;
     }
 #undef CONF_FILE
-    
+
     /* init things */
     memset(buf, 0, sizeof(buf));
     /* start the show */
@@ -460,14 +420,14 @@ static int parse_config_file(const char *uname, int ctrl,
         char value[LINE_LENGTH];
         int i,j;
         char *tptr;
-        
+
         tptr = buf;
         /* skip the leading white space */
         while (*tptr && isspace(*tptr))
             tptr++;
         strncpy(buf, tptr, sizeof(buf)-1);
        buf[sizeof(buf)-1] = '\0';
-                                
+
         /* Rip off the comments */
         tptr = strchr(buf,'#');
         if (tptr)
@@ -486,7 +446,7 @@ static int parse_config_file(const char *uname, int ctrl,
         memset(ltype, 0, sizeof(ltype));
         memset(item, 0, sizeof(item));
         memset(value, 0, sizeof(value));
-        
+
         i = sscanf(buf,"%s%s%s%s", domain, ltype, item, value);
        D(("scanned line[%d]: domain[%s], ltype[%s], item[%s], value[%s]",
           i, domain, ltype, item, value));
@@ -504,22 +464,39 @@ static int parse_config_file(const char *uname, int ctrl,
             if (strcmp(uname, domain) == 0) /* this user have a limit */
                 process_limit(LIMITS_DEF_USER, ltype, item, value, ctrl, pl);
             else if (domain[0]=='@') {
-               _pam_log(LOG_DEBUG, "checking if %s is in group %s",
-                        uname, domain + 1);
-                if (is_in_group(uname, domain+1))
+                   if (ctrl & PAM_DEBUG_ARG) {
+                       _pam_log(LOG_DEBUG, "checking if %s is in group %s",
+                               uname, domain + 1);
+                   }
+                if (_pammodutil_user_in_group_nam_nam(pamh, uname, domain+1))
                     process_limit(LIMITS_DEF_GROUP, ltype, item, value, ctrl,
                                  pl);
+            } else if (domain[0]=='%') {
+                   if (ctrl & PAM_DEBUG_ARG) {
+                       _pam_log(LOG_DEBUG, "checking if %s is in group %s",
+                               uname, domain + 1);
+                   }
+               if (strcmp(domain,"%") == 0)
+                   process_limit(LIMITS_DEF_ALL, ltype, item, value, ctrl,
+                                 pl);
+               else if (_pammodutil_user_in_group_nam_nam(pamh, uname, domain+1))
+                    process_limit(LIMITS_DEF_ALLGROUP, ltype, item, value, ctrl,
+                                 pl);
             } else if (strcmp(domain, "*") == 0)
                 process_limit(LIMITS_DEF_DEFAULT, ltype, item, value, ctrl,
                              pl);
        } else if (i == 2 && ltype[0] == '-') { /* Probably a no-limit line */
            if (strcmp(uname, domain) == 0) {
-               _pam_log(LOG_DEBUG, "no limits for '%s'", uname);
+               if (ctrl & PAM_DEBUG_ARG) {
+                   _pam_log(LOG_DEBUG, "no limits for '%s'", uname);
+               }
                fclose(fil);
                return PAM_IGNORE;
-           } else if (domain[0] == '@' && is_in_group(uname, domain+1)) {
-               _pam_log(LOG_DEBUG, "no limits for '%s' in group '%s'",
-                        uname, domain+1);
+           } else if (domain[0] == '@' && _pammodutil_user_in_group_nam_nam(pamh, uname, domain+1)) {
+               if (ctrl & PAM_DEBUG_ARG) {
+                   _pam_log(LOG_DEBUG, "no limits for '%s' in group '%s'",
+                            uname, domain+1);
+               }
                fclose(fil);
                return PAM_IGNORE;
            }
@@ -528,10 +505,11 @@ static int parse_config_file(const char *uname, int ctrl,
        }
     }
     fclose(fil);
-    return PAM_SUCCESS;    
+    return PAM_SUCCESS;
 }
 
-static int setup_limits(const char * uname, uid_t uid, int ctrl,
+static int setup_limits(pam_handle_t *pamh,
+                       const char *uname, uid_t uid, int ctrl,
                        struct pam_limit_s *pl)
 {
     int i;
@@ -558,7 +536,7 @@ static int setup_limits(const char * uname, uid_t uid, int ctrl,
        }
        status |= setrlimit(i, &pl->limits[i].limit);
     }
-    
+
     if (status) {
         retval = LIMIT_ERR;
     }
@@ -571,7 +549,7 @@ static int setup_limits(const char * uname, uid_t uid, int ctrl,
     if (uid == 0) {
        D(("skip login limit check for uid=0"));
     } else if (pl->login_limit > 0) {
-        if (check_logins(uname, pl->login_limit, ctrl, pl) == LOGIN_ERR) {
+        if (check_logins(pamh, uname, pl->login_limit, ctrl, pl) == LOGIN_ERR) {
             retval |= LOGIN_ERR;
        }
     } else if (pl->login_limit == 0) {
@@ -580,7 +558,7 @@ static int setup_limits(const char * uname, uid_t uid, int ctrl,
 
     return retval;
 }
-            
+
 /* now the session stuff */
 PAM_EXTERN int pam_sm_open_session(pam_handle_t *pamh, int flags,
                                    int argc, const char **argv)
@@ -601,7 +579,7 @@ PAM_EXTERN int pam_sm_open_session(pam_handle_t *pamh, int flags,
         _pam_log(LOG_CRIT, "open_session - error recovering username");
         return PAM_SESSION_ERR;
      }
-       
+
     pwd = getpwnam(user_name);
     if (!pwd) {
         if (ctrl & PAM_DEBUG_ARG)
@@ -609,14 +587,14 @@ PAM_EXTERN int pam_sm_open_session(pam_handle_t *pamh, int flags,
                                    user_name);
         return PAM_SESSION_ERR;
     }
-                     
+
     retval = init_limits(&pl);
     if (retval != PAM_SUCCESS) {
         _pam_log(LOG_WARNING, "cannot initialize");
         return PAM_IGNORE;
     }
 
-    retval = parse_config_file(pwd->pw_name, ctrl, &pl);
+    retval = parse_config_file(pamh, pwd->pw_name, ctrl, &pl);
     if (retval == PAM_IGNORE) {
        D(("the configuration file has an applicable '<domain> -' entry"));
        return PAM_SUCCESS;
@@ -629,7 +607,7 @@ PAM_EXTERN int pam_sm_open_session(pam_handle_t *pamh, int flags,
     if (ctrl & PAM_DO_SETREUID) {
        setreuid(pwd->pw_uid, -1);
     }
-    retval = setup_limits(pwd->pw_name, pwd->pw_uid, ctrl, &pl);
+    retval = setup_limits(pamh, pwd->pw_name, pwd->pw_uid, ctrl, &pl);
     if (retval != LIMITED_OK) {
         return PAM_PERM_DENIED;
     }
@@ -675,13 +653,13 @@ struct pam_module _pam_limits_modstruct = {
  * 3. The name of the author may not be used to endorse or promote
  *    products derived from this software without specific prior
  *    written permission.
- * 
+ *
  * ALTERNATIVELY, this product may be distributed under the terms of
  * the GNU Public License, in which case the provisions of the GPL are
  * required INSTEAD OF the above restrictions.  (This clause is
  * necessary due to a potential bad interaction between the GPL and
  * the restrictions contained in a BSD-style copyright.)
- * 
+ *
  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE