]> granicus.if.org Git - shadow/commitdiff
* NEWS, libmisc/limits.c: Fix parsing of limits.
authornekral-guest <nekral-guest@5a98b0ae-9ef6-0310-add3-de5d479b70d7>
Thu, 18 Mar 2010 23:20:15 +0000 (23:20 +0000)
committernekral-guest <nekral-guest@5a98b0ae-9ef6-0310-add3-de5d479b70d7>
Thu, 18 Mar 2010 23:20:15 +0000 (23:20 +0000)
* NEWS, libmisc/limits.c, man/limits.5.xml: Add support for
infinite limits.
* NEWS, libmisc/limits.c, man/limits.5.xml: Add support for @group
syntax.

ChangeLog
NEWS
libmisc/limits.c
man/limits.5.xml

index ff9e2c1c7bc7863eecd2fcb74dc7fc6b59398e6f..de85639e43c6dd518b8e8c1880b517d42a98c5da 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,11 @@
+2010-03-18  Thomas Orgis  <thomas@orgis.org>
+
+       * NEWS, libmisc/limits.c: Fix parsing of limits.
+       * NEWS, libmisc/limits.c, man/limits.5.xml: Add support for
+       infinite limits.
+       * NEWS, libmisc/limits.c, man/limits.5.xml: Add support for @group
+       syntax.
+
 2010-03-18  Nicolas François  <nicolas.francois@centraliens.net>
 
        * lib/tcbfuncs.h: Re-indent.
diff --git a/NEWS b/NEWS
index 42f5d62337022a201ae38d48bde8f8b5b7ff026e..d3fd73980662b725568c9aed49dcffa556d8417b 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -13,6 +13,10 @@ shadow-4.1.4.2 -> shadow-4.1.4.3                                     UNRELEASED
     specified.
 - groupmod
   * Fixed groupmod when configured with --enable-account-tools-setuid.
+-login
+  * Fixed limits support (non PAM enabled versions only)
+  * Added support for infinite limits and group based limits (non PAM
+    enabled versions only)
 - su
   * Document the su exit values.
   * When su receives a signal, wait for the child to terminate (after
index bfcce52e198a5ced51599951fa3aa2c8e3cfa664..c007850fca77c692ff44539730995d69dc4876ce 100644 (file)
@@ -33,6 +33,7 @@
 /*
  * Separated from setup.c.  --marekm
  * Resource limits thanks to Cristian Gafton.
+ * Enhancements of resource limit code by Thomas Orgis <thomas@orgis.org> ("thor").
  */
 
 #include <config.h>
@@ -44,6 +45,7 @@
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <stdio.h>
+#include <ctype.h>
 #include "prototypes.h"
 #include "defines.h"
 #include <pwd.h>
@@ -69,17 +71,37 @@ setrlimit_value (unsigned int resource, const char *value,
                 unsigned int multiplier)
 {
        struct rlimit rlim;
-       long limit;
+       rlim_t limit;
 
-       if (getlong (value, &limit) == 0) {
-               return 0;
+       /* The "-" is special, not belonging to a strange negative limit.
+          It is infinity, in a controlled way. --thor */
+       if(value[0] == '-') {
+               limit = RLIM_INFINITY;
        }
-       limit *= multiplier;
-       if (limit != (rlim_t) limit) {
-               return 0;
+       else {
+               /* We cannot use getlong here because it fails when there
+                  is more to the value than just this number!
+                  Also, we are limited to base 10 here (hex numbers will not
+                  work with the limit string parser as is anyway) --thor */
+               char *endptr;
+               long longlimit = strtol(value, &endptr, 10);
+               if ((0 == longlimit) && (value == endptr)) {
+                       /* No argument at all. No-op.
+                          We could instead throw an error, though. --thor */
+                       return 0;
+               }
+               longlimit *= multiplier;
+               limit = (rlim_t)longlimit;
+               if(longlimit != limit)
+               {
+                       /* Again, silent error handling... I left it that way.
+                          Wouldn't screaming make more sense? --thor */
+                       return 0;
+               }
        }
-       rlim.rlim_cur = (rlim_t) limit;
-       rlim.rlim_max = (rlim_t) limit;
+
+       rlim.rlim_cur = limit;
+       rlim.rlim_max = limit;
        if (setrlimit (resource, &rlim) != 0) {
                return LOGIN_ERROR_RLIMIT;
        }
@@ -199,6 +221,9 @@ static int check_logins (const char *name, const char *maxlogins)
  * [Ii]: i = RLIMIT_NICE    max nice value (0..39 translates to 20..-19)
  * [Oo]: o = RLIMIT_RTPRIO  max real time priority (linux/sched.h 0..MAX_RT_PRIO)
  *
+ * Remember to extend the "no-limits" string below when adding a new limit...
+ *   --thor
+ *
  * Return value:
  *             0 = okay, of course
  *             LOGIN_ERROR_RLIMIT = error setting some RLIMIT
@@ -214,6 +239,19 @@ static int do_user_limits (const char *buf, const char *name)
        bool reported = false;
 
        pp = buf;
+       /* Skip leading whitespace. --thor */
+       while(*pp == ' ' || *pp == '\t') ++pp;
+
+       /* The special limit string "-" results in no limit for all known limits.
+          We achieve that by parsing a full limit string, parts of it being ignored
+          if a limit type is not known to the system.
+          Though, there will be complaining for unknown limit types. --thor */
+       if(strcmp(pp, "-") == 0) {
+               /* Remember to extend this, too, when adding new limits!
+                  Oh... but "unlimited" does not make sense for umask, or does it?
+                  --thor */
+               pp = "A- C- D- F- M- N- R- S- T- P- I- O-";
+       }
 
        while ('\0' != *pp) {
                switch (*pp++) {
@@ -316,6 +354,10 @@ static int do_user_limits (const char *buf, const char *name)
                        break;
                default:
                        /* Only report invalid strings once */
+                       /* Note: A string can be invalid just because a specific (theoretically
+                          valid) setting is not supported by this build.
+                          It is just a warning in syslog anyway. The line is still processed
+                          --thor */
                        if (!reported) {
                                SYSLOG ((LOG_WARN,
                                         "Invalid limit string: '%s'",
@@ -324,13 +366,51 @@ static int do_user_limits (const char *buf, const char *name)
                                retval |= LOGIN_ERROR_RLIMIT;
                        }
                }
+               /* After parsing one limit setting (or just complaining about it),
+                  one still needs to skip its argument to prevent a bogus warning on
+                  trying to parse that as limit specification.
+                  So, let's skip all digits, "-" and our limited set of whitespace.
+                  --thor */
+               while(isdigit(*pp) || *pp == '-' || *pp == ' ' || *pp == '\t') {
+                       ++pp;
+               }
        }
        return retval;
 }
 
+/* Check if user uname is in the group gname.
+ * Can I be sure that gr_mem contains no UID as string?
+ * Returns true when user is in the group, false when not.
+ * Any error is treated as false. --thor
+ */
+static bool user_in_group (const char *uname, const char *gname)
+{
+       struct group *groupdata;
+       char **member;
+       if(uname == NULL || gname == NULL){ 
+               return false;
+       }
+       /* We are not claiming to be re-entrant!
+        * In case of paranoia or a multithreaded login program,
+        * one needs to add some mess for getgrnam_r. */
+       groupdata = getgrnam(gname);
+       if(groupdata == NULL) {
+               SYSLOG ((LOG_WARN, "Nonexisting group `%s' in limits file.", gname));
+               return false;
+       }
+       /* Now look for our user in the list of members. */
+       member = groupdata->gr_mem;
+       while(*member != NULL) {
+               if(strcmp(*member, uname) == 0) {
+                       return true;
+               }
+               ++member;
+       }
+       return false;
+}
+
 static int setup_user_limits (const char *uname)
 {
-       /* TODO: allow and use @group syntax --cristiang */
        FILE *fil;
        char buf[1024];
        char name[1024];
@@ -352,7 +432,7 @@ static int setup_user_limits (const char *uname)
        }
        /* The limits file have the following format:
         * - '#' (comment) chars only as first chars on a line;
-        * - username must start on first column
+        * - username must start on first column (or *, or @group --thor)
         * A better (smarter) checking should be done --cristiang */
        while (fgets (buf, 1024, fil) != NULL) {
                if (('#' == buf[0]) || ('\n' == buf[0])) {
@@ -367,6 +447,13 @@ static int setup_user_limits (const char *uname)
                 * Imposing a limit should be done with care, so a wrong
                 * entry means no care anyway :-). A '-' as a limits
                 * strings means no limits --cristiang */
+               /* In addition to the handling of * as name which was alrady present,
+                  I added handling of the @group syntax.
+                  To clarify: The first entry with matching user name rules,
+                  everything after it is ignored. If there is no user entry,
+                  the last encountered entry for a matching group rules.
+                  If there is no matching group entry, the default limits rule.
+                     --thor. */
                if (sscanf (buf, "%s%[ACDFMNRSTULPIOacdfmnrstulpio0-9 \t-]",
                            name, tempbuf) == 2) {
                        if (strcmp (name, uname) == 0) {
@@ -374,6 +461,12 @@ static int setup_user_limits (const char *uname)
                                break;
                        } else if (strcmp (name, "*") == 0) {
                                strcpy (deflimits, tempbuf);
+                       } else if (name[0] == '@') {
+                               /* If the user is in the group, the group limits apply unless
+                                  later a line for the specific user is found. --thor */
+                               if(user_in_group(uname, name+1)) {
+                                       strcpy (limits, tempbuf);
+                               }
                        }
                }
        }
index 4f68aab6390f329e4a699fb6be9ca21cf5a5e84d..080512ecda523cdd24d4471c7bc42aae94e50b2e 100644 (file)
       <emphasis remap='I'>user LIMITS_STRING</emphasis>
     </para>
 
+    <para>or in the form:</para>
+
+    <para>
+      <emphasis remap='I'>@group LIMITS_STRING</emphasis>
+    </para>
+
     <para>
       The <emphasis>LIMITS_STRING</emphasis> is a string of a concatenated
       list of resource limits. Each limit consists of a letter identifier
       as the default entry.
     </para>
 
+    <para>
+      The limits specified in the form "<replaceable>@group</replaceable>"
+      apply to the members of the specified
+      <replaceable>group</replaceable>.
+    </para>
+
     <para>
       To completely disable limits for a user, a single dash
       "<emphasis>-</emphasis>" will do.
     </para>
 
+    <para>
+      To disable a limit for a user, a single dash
+      "<replaceable>-</replaceable>" can be used instead of the numerical
+      value for this limit.
+    </para>
+
     <para>
       Also, please note that all limit settings are set PER LOGIN. They are
       not global, nor are they permanent. Perhaps global limits will come,