summaryrefslogblamecommitdiffstats
path: root/extra/source/pam/patches/pam-1.1.3-limits-range.patch
blob: c357eb2822108d3a080e32956ddf761d533cdaf9 (plain) (tree)






























































































































































































































































































































































                                                                                                          
Index: modules/pam_limits/limits.conf.5.xml
===================================================================
RCS file: /cvsroot/pam/Linux-PAM/modules/pam_limits/limits.conf.5.xml,v
retrieving revision 1.9
retrieving revision 1.11
diff -u -p -r1.9 -r1.11
--- modules/pam_limits/limits.conf.5.xml	20 Feb 2009 13:27:14 -0000	1.9
+++ modules/pam_limits/limits.conf.5.xml	14 Dec 2010 08:40:40 -0000	1.11
@@ -53,7 +53,38 @@
             <listitem>
               <para>
                 the wildcard <emphasis remap='B'>%</emphasis>, for maxlogins limit only,
-                can also be used with <emphasis remap='b'>%group</emphasis> syntax.
+                can also be used with <emphasis remap='B'>%group</emphasis> syntax. If the
+                <emphasis remap='B'>%</emphasis> wildcard is used alone it is identical
+                to using <emphasis remap='B'>*</emphasis> with maxsyslogins limit. With
+                a group specified after <emphasis remap='B'>%</emphasis> it limits the total
+                number of logins of all users that are member of the group.
+              </para>
+            </listitem>
+            <listitem>
+              <para>
+                an uid range specified as <replaceable>&lt;min_uid&gt;</replaceable><emphasis
+                remap='B'>:</emphasis><replaceable>&lt;max_uid&gt;</replaceable>. If min_uid
+                is omitted, the match is exact for the max_uid. If max_uid is omitted, all
+                uids greater than or equal min_uid match.
+              </para>
+            </listitem>
+            <listitem>
+              <para>
+                a gid range specified as <emphasis
+                remap='B'>@</emphasis><replaceable>&lt;min_gid&gt;</replaceable><emphasis
+                remap='B'>:</emphasis><replaceable>&lt;max_gid&gt;</replaceable>. If min_gid
+                is omitted, the match is exact for the max_gid. If max_gid is omitted, all
+                gids greater than or equal min_gid match. For the exact match all groups including
+                the user's supplementary groups are examined. For the range matches only
+                the user's primary group is examined.
+              </para>
+            </listitem>
+            <listitem>
+              <para>
+                a gid specified as <emphasis
+                remap='B'>%:</emphasis><replaceable>&lt;gid&gt;</replaceable> applicable
+                to maxlogins limit only. It limits the total number of logins of all users
+                that are member of the group with the specified gid.
               </para>
             </listitem>
           </itemizedlist>
@@ -182,7 +213,7 @@
             <varlistentry>
               <term><option>maxsyslogins</option></term>
               <listitem>
-                <para>maximum number of logins on system</para>
+                <para>maximum number of all logins on system</para>
               </listitem>
             </varlistentry>
             <varlistentry>
@@ -272,12 +303,15 @@
     </para>
     <programlisting>
 *               soft    core            0
-*               hard    rss             10000
+*               hard    nofile          512
 @student        hard    nproc           20
 @faculty        soft    nproc           20
 @faculty        hard    nproc           50
 ftp             hard    nproc           0
 @student        -       maxlogins       4
+:123            hard    cpu             5000
+@500:           soft    cpu             10000
+600:700         hard    locks           10
     </programlisting>
   </refsect1>
 
Index: modules/pam_limits/pam_limits.c
===================================================================
RCS file: /cvsroot/pam/Linux-PAM/modules/pam_limits/pam_limits.c,v
retrieving revision 1.48
retrieving revision 1.49
diff -u -p -r1.48 -r1.49
--- modules/pam_limits/pam_limits.c	18 Nov 2010 09:37:32 -0000	1.48
+++ modules/pam_limits/pam_limits.c	14 Dec 2010 08:40:40 -0000	1.49
@@ -55,6 +55,12 @@
 #define LIMITS_DEF_DEFAULT  4 /* limit was set by an default entry */
 #define LIMITS_DEF_NONE     5 /* this limit was not set yet */
 
+#define LIMIT_RANGE_ERR    -1 /* error in specified uid/gid range */
+#define LIMIT_RANGE_NONE    0 /* no range specified */
+#define LIMIT_RANGE_ONE     1 /* exact uid/gid specified (:max_uid)*/
+#define LIMIT_RANGE_MIN     2 /* only minimum uid/gid specified (min_uid:) */
+#define LIMIT_RANGE_MM      3 /* both min and max uid/gid specified (min_uid:max_uid) */
+
 static const char *limits_def_names[] = {
        "USER",
        "GROUP",
@@ -520,8 +526,57 @@ process_limit (const pam_handle_t *pamh,
     return;
 }
 
-static int parse_config_file(pam_handle_t *pamh, const char *uname, int ctrl,
-			     struct pam_limit_s *pl)
+static int
+parse_uid_range(pam_handle_t *pamh, const char *domain,
+		uid_t *min_uid, uid_t *max_uid)
+{
+    const char *range = domain;
+    char *pmax;
+    char *endptr;
+    int rv = LIMIT_RANGE_MM;
+
+    if ((pmax=strchr(range, ':')) == NULL)
+	return LIMIT_RANGE_NONE;
+    ++pmax;
+
+    if (range[0] == '@' || range[0] == '%')
+	++range;
+
+    if (range[0] == ':')
+	rv = LIMIT_RANGE_ONE;
+    else {
+	    errno = 0;
+	    *min_uid = strtoul (range, &endptr, 10);
+	    if (errno != 0 || (range == endptr) || *endptr != ':') {
+		pam_syslog(pamh, LOG_DEBUG,
+			   "wrong min_uid/gid value in '%s'", domain);
+		return LIMIT_RANGE_ERR;
+	    }
+    }
+
+    if (*pmax == '\0') {
+	if (rv == LIMIT_RANGE_ONE)
+	    return LIMIT_RANGE_ERR;
+	else
+	    return LIMIT_RANGE_MIN;
+    }
+
+    errno = 0;
+    *max_uid = strtoul (pmax, &endptr, 10);
+    if (errno != 0 || (pmax == endptr) || *endptr != '\0') {
+	pam_syslog(pamh, LOG_DEBUG,
+		   "wrong max_uid/gid value in '%s'", domain);
+	return LIMIT_RANGE_ERR;
+    }
+
+    if (rv == LIMIT_RANGE_ONE)
+	*min_uid = *max_uid;
+    return rv;
+}
+
+static int
+parse_config_file(pam_handle_t *pamh, const char *uname, uid_t uid, gid_t gid,
+			     int ctrl, struct pam_limit_s *pl)
 {
     FILE *fil;
     char buf[LINE_LENGTH];
@@ -543,8 +598,10 @@ static int parse_config_file(pam_handle_
         char item[LINE_LENGTH];
         char value[LINE_LENGTH];
         int i;
+        int rngtype;
         size_t j;
         char *tptr,*line;
+        uid_t min_uid = (uid_t)-1, max_uid = (uid_t)-1;
 
         line = buf;
         /* skip the leading white space */
@@ -572,6 +629,11 @@ static int parse_config_file(pam_handle_
         for(j=0; j < strlen(ltype); j++)
             ltype[j]=tolower(ltype[j]);
 
+	if ((rngtype=parse_uid_range(pamh, domain, &min_uid, &max_uid)) < 0) {
+	    pam_syslog(pamh, LOG_WARNING, "invalid uid range '%s' - skipped", domain);
+	    continue;
+	}
+
         if (i == 4) { /* a complete line */
 	    for(j=0; j < strlen(item); j++)
 		item[j]=tolower(item[j]);
@@ -581,47 +643,133 @@ static int parse_config_file(pam_handle_
             if (strcmp(uname, domain) == 0) /* this user have a limit */
                 process_limit(pamh, LIMITS_DEF_USER, ltype, item, value, ctrl, pl);
             else if (domain[0]=='@') {
-		    if (ctrl & PAM_DEBUG_ARG) {
+		if (ctrl & PAM_DEBUG_ARG) {
 			pam_syslog(pamh, LOG_DEBUG,
 				   "checking if %s is in group %s",
 				   uname, domain + 1);
-		    }
-                if (pam_modutil_user_in_group_nam_nam(pamh, uname, domain+1))
-                    process_limit(pamh, LIMITS_DEF_GROUP, ltype, item, value, ctrl,
+		}
+		switch(rngtype) {
+		    case LIMIT_RANGE_NONE:
+			if (pam_modutil_user_in_group_nam_nam(pamh, uname, domain+1))
+			    process_limit(pamh, LIMITS_DEF_GROUP, ltype, item, value, ctrl,
+					  pl);
+			break;
+		    case LIMIT_RANGE_ONE:
+			if (pam_modutil_user_in_group_nam_gid(pamh, uname, (gid_t)max_uid))
+			    process_limit(pamh, LIMITS_DEF_GROUP, ltype, item, value, ctrl,
 				  pl);
+			break;
+		    case LIMIT_RANGE_MM:
+			if (gid > (gid_t)max_uid)
+			    break;
+			/* fallthrough */
+		    case LIMIT_RANGE_MIN:
+			if (gid >= (gid_t)min_uid)
+			    process_limit(pamh, LIMITS_DEF_GROUP, ltype, item, value, ctrl,
+					  pl);
+		}
             } else if (domain[0]=='%') {
-		    if (ctrl & PAM_DEBUG_ARG) {
+		if (ctrl & PAM_DEBUG_ARG) {
 			pam_syslog(pamh, LOG_DEBUG,
 				   "checking if %s is in group %s",
 				   uname, domain + 1);
-		    }
-		if (strcmp(domain,"%") == 0)
-		    process_limit(pamh, LIMITS_DEF_ALL, ltype, item, value, ctrl,
-				  pl);
-		else if (pam_modutil_user_in_group_nam_nam(pamh, uname, domain+1)) {
-		    strcpy(pl->login_group, domain+1);
-                    process_limit(pamh, LIMITS_DEF_ALLGROUP, ltype, item, value, ctrl,
-				  pl);
 		}
-            } else if (strcmp(domain, "*") == 0)
-                process_limit(pamh, LIMITS_DEF_DEFAULT, ltype, item, value, ctrl,
-			      pl);
+		switch(rngtype) {
+		    case LIMIT_RANGE_NONE:
+			if (strcmp(domain,"%") == 0)
+			    process_limit(pamh, LIMITS_DEF_ALL, ltype, item, value, ctrl,
+					  pl);
+			else if (pam_modutil_user_in_group_nam_nam(pamh, uname, domain+1)) {
+			    strcpy(pl->login_group, domain+1);
+			    process_limit(pamh, LIMITS_DEF_ALLGROUP, ltype, item, value, ctrl,
+					  pl);
+			}
+			break;
+		    case LIMIT_RANGE_ONE:
+			if (pam_modutil_user_in_group_nam_gid(pamh, uname, (gid_t)max_uid)) {
+			    struct group *grp;
+			    grp = pam_modutil_getgrgid(pamh, (gid_t)max_uid);
+			    strncpy(pl->login_group, grp->gr_name, sizeof(pl->login_group));
+			    pl->login_group[sizeof(pl->login_group)-1] = '\0';
+			    process_limit(pamh, LIMITS_DEF_ALLGROUP, ltype, item, value, ctrl,
+					  pl);
+			}
+			break;
+		    case LIMIT_RANGE_MIN:
+		    case LIMIT_RANGE_MM:
+			pam_syslog(pamh, LOG_WARNING, "range unsupported for %%group matching - ignored");
+		}
+            } else {
+		switch(rngtype) {
+		    case LIMIT_RANGE_NONE:
+			if (strcmp(domain, "*") == 0)
+			    process_limit(pamh, LIMITS_DEF_DEFAULT, ltype, item, value, ctrl,
+					  pl);
+			break;
+		    case LIMIT_RANGE_ONE:
+			if (uid != max_uid)
+			    break;
+			/* fallthrough */
+		    case LIMIT_RANGE_MM:
+			if (uid > max_uid)
+			    break;
+			/* fallthrough */
+		    case LIMIT_RANGE_MIN:
+			if (uid >= min_uid)
+			    process_limit(pamh, LIMITS_DEF_USER, ltype, item, value, ctrl, pl);
+		}
+	    }
 	} else if (i == 2 && ltype[0] == '-') { /* Probably a no-limit line */
 	    if (strcmp(uname, domain) == 0) {
 		if (ctrl & PAM_DEBUG_ARG) {
 		    pam_syslog(pamh, LOG_DEBUG, "no limits for '%s'", uname);
 		}
-		fclose(fil);
-		return PAM_IGNORE;
-	    } else if (domain[0] == '@' && pam_modutil_user_in_group_nam_nam(pamh, uname, domain+1)) {
+	    } else if (domain[0] == '@') {
+		switch(rngtype) {
+		    case LIMIT_RANGE_NONE:
+			if (!pam_modutil_user_in_group_nam_nam(pamh, uname, domain+1))
+			    continue; /* next line */
+			break;
+		    case LIMIT_RANGE_ONE:
+			if (!pam_modutil_user_in_group_nam_gid(pamh, uname, (gid_t)max_uid))
+			    continue; /* next line */
+			break;
+		    case LIMIT_RANGE_MM:
+			if (gid > (gid_t)max_uid)
+			    continue;  /* next line */
+			/* fallthrough */
+		    case LIMIT_RANGE_MIN:
+			if (gid < (gid_t)min_uid)
+			    continue;  /* next line */
+		}
 		if (ctrl & PAM_DEBUG_ARG) {
 		    pam_syslog(pamh, LOG_DEBUG,
 			       "no limits for '%s' in group '%s'",
 			       uname, domain+1);
 		}
-		fclose(fil);
-		return PAM_IGNORE;
+	    } else {
+		switch(rngtype) {
+		    case LIMIT_RANGE_NONE:
+			continue;  /* next line */
+		    case LIMIT_RANGE_ONE:
+			if (uid != max_uid)
+			    continue;  /* next line */
+			break;
+		    case LIMIT_RANGE_MM:
+			if (uid > max_uid)
+			    continue;  /* next line */
+			/* fallthrough */
+		    case LIMIT_RANGE_MIN:
+			if (uid >= min_uid)
+			    break;
+			continue;  /* next line */
+		}
+		if (ctrl & PAM_DEBUG_ARG) {
+		    pam_syslog(pamh, LOG_DEBUG, "no limits for '%s'", uname);
+		}
 	    }
+	    fclose(fil);
+	    return PAM_IGNORE;
         } else {
             pam_syslog(pamh, LOG_WARNING, "invalid line '%s' - skipped", line);
 	}
@@ -731,7 +879,7 @@ pam_sm_open_session (pam_handle_t *pamh,
         return PAM_ABORT;
     }
 
-    retval = parse_config_file(pamh, pwd->pw_name, ctrl, pl);
+    retval = parse_config_file(pamh, pwd->pw_name, pwd->pw_uid, pwd->pw_gid, ctrl, pl);
     if (retval == PAM_IGNORE) {
 	D(("the configuration file ('%s') has an applicable '<domain> -' entry", CONF_FILE));
 	return PAM_SUCCESS;
@@ -755,7 +903,7 @@ pam_sm_open_session (pam_handle_t *pamh,
 	/* Parse the *.conf files. */
 	for (i = 0; globbuf.gl_pathv[i] != NULL; i++) {
 	    pl->conf_file = globbuf.gl_pathv[i];
-    	    retval = parse_config_file(pamh, pwd->pw_name, ctrl, pl);
+    	    retval = parse_config_file(pamh, pwd->pw_name, pwd->pw_uid, pwd->pw_gid, ctrl, pl);
     	    if (retval == PAM_IGNORE) {
 		D(("the configuration file ('%s') has an applicable '<domain> -' entry", pl->conf_file));
 		globfree(&globbuf);