summaryrefslogtreecommitdiffstats
path: root/source/a/shadow/patches/r3160.diff
blob: 5aabbaa9e185f08c2bb9b288456e1d28b638c227 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
Index: libmisc/limits.c
===================================================================
--- libmisc/limits.c	(revision 3159)
+++ libmisc/limits.c	(revision 3160)
@@ -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 @@
 		 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 @@
  * [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,7 +239,20 @@
 	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++) {
 #ifdef RLIMIT_AS
@@ -316,6 +354,10 @@
 			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 @@
 				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 @@
 	}
 	/* 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 @@
 		 * 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 @@
 				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: man/limits.5.xml
===================================================================
--- man/limits.5.xml	(revision 3159)
+++ man/limits.5.xml	(revision 3160)
@@ -64,7 +64,13 @@
       <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
       followed by a numerical limit.
@@ -125,11 +131,23 @@
     </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,
       but for now this will have to do ;)