diff options
author | Ross Burton <ross.burton@intel.com> | 2017-08-04 17:27:00 +0100 |
---|---|---|
committer | Richard Purdie <richard.purdie@linuxfoundation.org> | 2017-08-29 16:50:54 +0100 |
commit | 07e5111828d6cad3133beb23e50312e4a5d6b73c (patch) | |
tree | 9fddc30e54563a135f770c98657b6385e9ddc4c1 /meta/recipes-core | |
parent | 7cc37c53909d958fd432f93c8f569a4de69a298f (diff) | |
download | poky-07e5111828d6cad3133beb23e50312e4a5d6b73c.tar.gz |
systemd: refuse to load units with errors (CVE-2017-1000082)
If a unit has a statement such as User=0day where the username exists but is
strictly speaking invalid, the unit will be started as the root user instead.
Backport a patch from upstream to mitigate this by refusing to start units such
as this.
(From OE-Core rev: e56cb926c170f493ee2a9c4c63d0ecbf883d4685)
Signed-off-by: Ross Burton <ross.burton@intel.com>
Signed-off-by: Armin Kuster <akuster808@gmail.com>
Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
Diffstat (limited to 'meta/recipes-core')
-rw-r--r-- | meta/recipes-core/systemd/systemd/validate-user.patch | 856 | ||||
-rw-r--r-- | meta/recipes-core/systemd/systemd_230.bb | 1 |
2 files changed, 857 insertions, 0 deletions
diff --git a/meta/recipes-core/systemd/systemd/validate-user.patch b/meta/recipes-core/systemd/systemd/validate-user.patch new file mode 100644 index 0000000000..8e0e0c1b9a --- /dev/null +++ b/meta/recipes-core/systemd/systemd/validate-user.patch | |||
@@ -0,0 +1,856 @@ | |||
1 | If a user is created with a strictly-speaking invalid name such as '0day' and a | ||
2 | unit created to run as that user, systemd rejects the username and runs the unit | ||
3 | as root. | ||
4 | |||
5 | CVE: CVE-2017-1000082 | ||
6 | Upstream-Status: Backport | ||
7 | Signed-off-by: Ross Burton <ross.burton@intel.com> | ||
8 | |||
9 | From e0c4eb1435d50cb3797cf94100d4886dc2022bce Mon Sep 17 00:00:00 2001 | ||
10 | From: Lennart Poettering <lennart@poettering.net> | ||
11 | Date: Thu, 14 Jul 2016 12:23:39 +0200 | ||
12 | Subject: [PATCH 1/3] sysusers: move various user credential validity checks to | ||
13 | src/basic/ | ||
14 | |||
15 | This way we can reuse them for validating User=/Group= settings in unit files | ||
16 | (to be added in a later commit). | ||
17 | |||
18 | Also, add some tests for them. | ||
19 | --- | ||
20 | src/basic/user-util.c | 93 +++++++++++++++++++++++++++++++++++++++++++++++ | ||
21 | src/basic/user-util.h | 5 +++ | ||
22 | src/sysusers/sysusers.c | 75 -------------------------------------- | ||
23 | src/test/test-user-util.c | 87 ++++++++++++++++++++++++++++++++++++++++++++ | ||
24 | 4 files changed, 185 insertions(+), 75 deletions(-) | ||
25 | |||
26 | diff --git a/src/basic/user-util.c b/src/basic/user-util.c | ||
27 | index f65ca3eda..c85b5c6a8 100644 | ||
28 | --- a/src/basic/user-util.c | ||
29 | +++ b/src/basic/user-util.c | ||
30 | @@ -29,6 +29,7 @@ | ||
31 | #include <string.h> | ||
32 | #include <sys/stat.h> | ||
33 | #include <unistd.h> | ||
34 | +#include <utmp.h> | ||
35 | |||
36 | #include "missing.h" | ||
37 | #include "alloc-util.h" | ||
38 | @@ -39,6 +40,7 @@ | ||
39 | #include "path-util.h" | ||
40 | #include "string-util.h" | ||
41 | #include "user-util.h" | ||
42 | +#include "utf8.h" | ||
43 | |||
44 | bool uid_is_valid(uid_t uid) { | ||
45 | |||
46 | @@ -479,3 +481,94 @@ int take_etc_passwd_lock(const char *root) { | ||
47 | |||
48 | return fd; | ||
49 | } | ||
50 | + | ||
51 | +bool valid_user_group_name(const char *u) { | ||
52 | + const char *i; | ||
53 | + long sz; | ||
54 | + | ||
55 | + /* Checks if the specified name is a valid user/group name. */ | ||
56 | + | ||
57 | + if (isempty(u)) | ||
58 | + return false; | ||
59 | + | ||
60 | + if (!(u[0] >= 'a' && u[0] <= 'z') && | ||
61 | + !(u[0] >= 'A' && u[0] <= 'Z') && | ||
62 | + u[0] != '_') | ||
63 | + return false; | ||
64 | + | ||
65 | + for (i = u+1; *i; i++) { | ||
66 | + if (!(*i >= 'a' && *i <= 'z') && | ||
67 | + !(*i >= 'A' && *i <= 'Z') && | ||
68 | + !(*i >= '0' && *i <= '9') && | ||
69 | + *i != '_' && | ||
70 | + *i != '-') | ||
71 | + return false; | ||
72 | + } | ||
73 | + | ||
74 | + sz = sysconf(_SC_LOGIN_NAME_MAX); | ||
75 | + assert_se(sz > 0); | ||
76 | + | ||
77 | + if ((size_t) (i-u) > (size_t) sz) | ||
78 | + return false; | ||
79 | + | ||
80 | + if ((size_t) (i-u) > UT_NAMESIZE - 1) | ||
81 | + return false; | ||
82 | + | ||
83 | + return true; | ||
84 | +} | ||
85 | + | ||
86 | +bool valid_user_group_name_or_id(const char *u) { | ||
87 | + | ||
88 | + /* Similar as above, but is also fine with numeric UID/GID specifications, as long as they are in the right | ||
89 | + * range, and not the invalid user ids. */ | ||
90 | + | ||
91 | + if (isempty(u)) | ||
92 | + return false; | ||
93 | + | ||
94 | + if (valid_user_group_name(u)) | ||
95 | + return true; | ||
96 | + | ||
97 | + return parse_uid(u, NULL) >= 0; | ||
98 | +} | ||
99 | + | ||
100 | +bool valid_gecos(const char *d) { | ||
101 | + | ||
102 | + if (!d) | ||
103 | + return false; | ||
104 | + | ||
105 | + if (!utf8_is_valid(d)) | ||
106 | + return false; | ||
107 | + | ||
108 | + if (string_has_cc(d, NULL)) | ||
109 | + return false; | ||
110 | + | ||
111 | + /* Colons are used as field separators, and hence not OK */ | ||
112 | + if (strchr(d, ':')) | ||
113 | + return false; | ||
114 | + | ||
115 | + return true; | ||
116 | +} | ||
117 | + | ||
118 | +bool valid_home(const char *p) { | ||
119 | + | ||
120 | + if (isempty(p)) | ||
121 | + return false; | ||
122 | + | ||
123 | + if (!utf8_is_valid(p)) | ||
124 | + return false; | ||
125 | + | ||
126 | + if (string_has_cc(p, NULL)) | ||
127 | + return false; | ||
128 | + | ||
129 | + if (!path_is_absolute(p)) | ||
130 | + return false; | ||
131 | + | ||
132 | + if (!path_is_safe(p)) | ||
133 | + return false; | ||
134 | + | ||
135 | + /* Colons are used as field separators, and hence not OK */ | ||
136 | + if (strchr(p, ':')) | ||
137 | + return false; | ||
138 | + | ||
139 | + return true; | ||
140 | +} | ||
141 | diff --git a/src/basic/user-util.h b/src/basic/user-util.h | ||
142 | index 8026eca3f..36f71fb00 100644 | ||
143 | --- a/src/basic/user-util.h | ||
144 | +++ b/src/basic/user-util.h | ||
145 | @@ -68,3 +68,8 @@ int take_etc_passwd_lock(const char *root); | ||
146 | static inline bool userns_supported(void) { | ||
147 | return access("/proc/self/uid_map", F_OK) >= 0; | ||
148 | } | ||
149 | + | ||
150 | +bool valid_user_group_name(const char *u); | ||
151 | +bool valid_user_group_name_or_id(const char *u); | ||
152 | +bool valid_gecos(const char *d); | ||
153 | +bool valid_home(const char *p); | ||
154 | diff --git a/src/sysusers/sysusers.c b/src/sysusers/sysusers.c | ||
155 | index 4377f1b91..df3b7de30 100644 | ||
156 | --- a/src/sysusers/sysusers.c | ||
157 | +++ b/src/sysusers/sysusers.c | ||
158 | @@ -1299,81 +1299,6 @@ static bool item_equal(Item *a, Item *b) { | ||
159 | return true; | ||
160 | } | ||
161 | |||
162 | -static bool valid_user_group_name(const char *u) { | ||
163 | - const char *i; | ||
164 | - long sz; | ||
165 | - | ||
166 | - if (isempty(u)) | ||
167 | - return false; | ||
168 | - | ||
169 | - if (!(u[0] >= 'a' && u[0] <= 'z') && | ||
170 | - !(u[0] >= 'A' && u[0] <= 'Z') && | ||
171 | - u[0] != '_') | ||
172 | - return false; | ||
173 | - | ||
174 | - for (i = u+1; *i; i++) { | ||
175 | - if (!(*i >= 'a' && *i <= 'z') && | ||
176 | - !(*i >= 'A' && *i <= 'Z') && | ||
177 | - !(*i >= '0' && *i <= '9') && | ||
178 | - *i != '_' && | ||
179 | - *i != '-') | ||
180 | - return false; | ||
181 | - } | ||
182 | - | ||
183 | - sz = sysconf(_SC_LOGIN_NAME_MAX); | ||
184 | - assert_se(sz > 0); | ||
185 | - | ||
186 | - if ((size_t) (i-u) > (size_t) sz) | ||
187 | - return false; | ||
188 | - | ||
189 | - if ((size_t) (i-u) > UT_NAMESIZE - 1) | ||
190 | - return false; | ||
191 | - | ||
192 | - return true; | ||
193 | -} | ||
194 | - | ||
195 | -static bool valid_gecos(const char *d) { | ||
196 | - | ||
197 | - if (!d) | ||
198 | - return false; | ||
199 | - | ||
200 | - if (!utf8_is_valid(d)) | ||
201 | - return false; | ||
202 | - | ||
203 | - if (string_has_cc(d, NULL)) | ||
204 | - return false; | ||
205 | - | ||
206 | - /* Colons are used as field separators, and hence not OK */ | ||
207 | - if (strchr(d, ':')) | ||
208 | - return false; | ||
209 | - | ||
210 | - return true; | ||
211 | -} | ||
212 | - | ||
213 | -static bool valid_home(const char *p) { | ||
214 | - | ||
215 | - if (isempty(p)) | ||
216 | - return false; | ||
217 | - | ||
218 | - if (!utf8_is_valid(p)) | ||
219 | - return false; | ||
220 | - | ||
221 | - if (string_has_cc(p, NULL)) | ||
222 | - return false; | ||
223 | - | ||
224 | - if (!path_is_absolute(p)) | ||
225 | - return false; | ||
226 | - | ||
227 | - if (!path_is_safe(p)) | ||
228 | - return false; | ||
229 | - | ||
230 | - /* Colons are used as field separators, and hence not OK */ | ||
231 | - if (strchr(p, ':')) | ||
232 | - return false; | ||
233 | - | ||
234 | - return true; | ||
235 | -} | ||
236 | - | ||
237 | static int parse_line(const char *fname, unsigned line, const char *buffer) { | ||
238 | |||
239 | static const Specifier specifier_table[] = { | ||
240 | diff --git a/src/test/test-user-util.c b/src/test/test-user-util.c | ||
241 | index 8d1ec19f1..2a344a9f9 100644 | ||
242 | --- a/src/test/test-user-util.c | ||
243 | +++ b/src/test/test-user-util.c | ||
244 | @@ -61,6 +61,88 @@ static void test_uid_ptr(void) { | ||
245 | assert_se(PTR_TO_UID(UID_TO_PTR(1000)) == 1000); | ||
246 | } | ||
247 | |||
248 | +static void test_valid_user_group_name(void) { | ||
249 | + assert_se(!valid_user_group_name(NULL)); | ||
250 | + assert_se(!valid_user_group_name("")); | ||
251 | + assert_se(!valid_user_group_name("1")); | ||
252 | + assert_se(!valid_user_group_name("65535")); | ||
253 | + assert_se(!valid_user_group_name("-1")); | ||
254 | + assert_se(!valid_user_group_name("-kkk")); | ||
255 | + assert_se(!valid_user_group_name("rööt")); | ||
256 | + assert_se(!valid_user_group_name(".")); | ||
257 | + assert_se(!valid_user_group_name("eff.eff")); | ||
258 | + assert_se(!valid_user_group_name("foo\nbar")); | ||
259 | + assert_se(!valid_user_group_name("0123456789012345678901234567890123456789")); | ||
260 | + assert_se(!valid_user_group_name_or_id("aaa:bbb")); | ||
261 | + | ||
262 | + assert_se(valid_user_group_name("root")); | ||
263 | + assert_se(valid_user_group_name("lennart")); | ||
264 | + assert_se(valid_user_group_name("LENNART")); | ||
265 | + assert_se(valid_user_group_name("_kkk")); | ||
266 | + assert_se(valid_user_group_name("kkk-")); | ||
267 | + assert_se(valid_user_group_name("kk-k")); | ||
268 | + | ||
269 | + assert_se(valid_user_group_name("some5")); | ||
270 | + assert_se(!valid_user_group_name("5some")); | ||
271 | + assert_se(valid_user_group_name("INNER5NUMBER")); | ||
272 | +} | ||
273 | + | ||
274 | +static void test_valid_user_group_name_or_id(void) { | ||
275 | + assert_se(!valid_user_group_name_or_id(NULL)); | ||
276 | + assert_se(!valid_user_group_name_or_id("")); | ||
277 | + assert_se(valid_user_group_name_or_id("0")); | ||
278 | + assert_se(valid_user_group_name_or_id("1")); | ||
279 | + assert_se(valid_user_group_name_or_id("65534")); | ||
280 | + assert_se(!valid_user_group_name_or_id("65535")); | ||
281 | + assert_se(valid_user_group_name_or_id("65536")); | ||
282 | + assert_se(!valid_user_group_name_or_id("-1")); | ||
283 | + assert_se(!valid_user_group_name_or_id("-kkk")); | ||
284 | + assert_se(!valid_user_group_name_or_id("rööt")); | ||
285 | + assert_se(!valid_user_group_name_or_id(".")); | ||
286 | + assert_se(!valid_user_group_name_or_id("eff.eff")); | ||
287 | + assert_se(!valid_user_group_name_or_id("foo\nbar")); | ||
288 | + assert_se(!valid_user_group_name_or_id("0123456789012345678901234567890123456789")); | ||
289 | + assert_se(!valid_user_group_name_or_id("aaa:bbb")); | ||
290 | + | ||
291 | + assert_se(valid_user_group_name_or_id("root")); | ||
292 | + assert_se(valid_user_group_name_or_id("lennart")); | ||
293 | + assert_se(valid_user_group_name_or_id("LENNART")); | ||
294 | + assert_se(valid_user_group_name_or_id("_kkk")); | ||
295 | + assert_se(valid_user_group_name_or_id("kkk-")); | ||
296 | + assert_se(valid_user_group_name_or_id("kk-k")); | ||
297 | + | ||
298 | + assert_se(valid_user_group_name_or_id("some5")); | ||
299 | + assert_se(!valid_user_group_name_or_id("5some")); | ||
300 | + assert_se(valid_user_group_name_or_id("INNER5NUMBER")); | ||
301 | +} | ||
302 | + | ||
303 | +static void test_valid_gecos(void) { | ||
304 | + | ||
305 | + assert_se(!valid_gecos(NULL)); | ||
306 | + assert_se(valid_gecos("")); | ||
307 | + assert_se(valid_gecos("test")); | ||
308 | + assert_se(valid_gecos("Ümläüt")); | ||
309 | + assert_se(!valid_gecos("In\nvalid")); | ||
310 | + assert_se(!valid_gecos("In:valid")); | ||
311 | +} | ||
312 | + | ||
313 | +static void test_valid_home(void) { | ||
314 | + | ||
315 | + assert_se(!valid_home(NULL)); | ||
316 | + assert_se(!valid_home("")); | ||
317 | + assert_se(!valid_home(".")); | ||
318 | + assert_se(!valid_home("/home/..")); | ||
319 | + assert_se(!valid_home("/home/../")); | ||
320 | + assert_se(!valid_home("/home\n/foo")); | ||
321 | + assert_se(!valid_home("./piep")); | ||
322 | + assert_se(!valid_home("piep")); | ||
323 | + assert_se(!valid_home("/home/user:lennart")); | ||
324 | + | ||
325 | + assert_se(valid_home("/")); | ||
326 | + assert_se(valid_home("/home")); | ||
327 | + assert_se(valid_home("/home/foo")); | ||
328 | +} | ||
329 | + | ||
330 | int main(int argc, char*argv[]) { | ||
331 | |||
332 | test_uid_to_name_one(0, "root"); | ||
333 | @@ -75,5 +157,10 @@ int main(int argc, char*argv[]) { | ||
334 | test_parse_uid(); | ||
335 | test_uid_ptr(); | ||
336 | |||
337 | + test_valid_user_group_name(); | ||
338 | + test_valid_user_group_name_or_id(); | ||
339 | + test_valid_gecos(); | ||
340 | + test_valid_home(); | ||
341 | + | ||
342 | return 0; | ||
343 | } | ||
344 | -- | ||
345 | 2.11.0 | ||
346 | |||
347 | |||
348 | From 1affacaaf6eff93e53563a644567cc5c3930cb28 Mon Sep 17 00:00:00 2001 | ||
349 | From: Lennart Poettering <lennart@poettering.net> | ||
350 | Date: Thu, 14 Jul 2016 12:28:06 +0200 | ||
351 | Subject: [PATCH 2/3] core: be stricter when parsing User=/Group= fields | ||
352 | |||
353 | Let's verify the validity of the syntax of the user/group names set. | ||
354 | --- | ||
355 | src/core/load-fragment-gperf.gperf.m4 | 10 +-- | ||
356 | src/core/load-fragment.c | 118 ++++++++++++++++++++++++++++++++++ | ||
357 | src/core/load-fragment.h | 2 + | ||
358 | 3 files changed, 125 insertions(+), 5 deletions(-) | ||
359 | |||
360 | diff --git a/src/core/load-fragment-gperf.gperf.m4 b/src/core/load-fragment-gperf.gperf.m4 | ||
361 | index 819341898..110089696 100644 | ||
362 | --- a/src/core/load-fragment-gperf.gperf.m4 | ||
363 | +++ b/src/core/load-fragment-gperf.gperf.m4 | ||
364 | @@ -19,9 +19,9 @@ m4_dnl Define the context options only once | ||
365 | m4_define(`EXEC_CONTEXT_CONFIG_ITEMS', | ||
366 | `$1.WorkingDirectory, config_parse_working_directory, 0, offsetof($1, exec_context) | ||
367 | $1.RootDirectory, config_parse_unit_path_printf, 0, offsetof($1, exec_context.root_directory) | ||
368 | -$1.User, config_parse_unit_string_printf, 0, offsetof($1, exec_context.user) | ||
369 | -$1.Group, config_parse_unit_string_printf, 0, offsetof($1, exec_context.group) | ||
370 | -$1.SupplementaryGroups, config_parse_strv, 0, offsetof($1, exec_context.supplementary_groups) | ||
371 | +$1.User, config_parse_user_group, 0, offsetof($1, exec_context.user) | ||
372 | +$1.Group, config_parse_user_group, 0, offsetof($1, exec_context.group) | ||
373 | +$1.SupplementaryGroups, config_parse_user_group_strv, 0, offsetof($1, exec_context.supplementary_groups) | ||
374 | $1.Nice, config_parse_exec_nice, 0, offsetof($1, exec_context) | ||
375 | $1.OOMScoreAdjust, config_parse_exec_oom_score_adjust, 0, offsetof($1, exec_context) | ||
376 | $1.IOSchedulingClass, config_parse_exec_io_class, 0, offsetof($1, exec_context) | ||
377 | @@ -275,8 +275,8 @@ Socket.ExecStartPost, config_parse_exec, SOCKET_EXEC | ||
378 | Socket.ExecStopPre, config_parse_exec, SOCKET_EXEC_STOP_PRE, offsetof(Socket, exec_command) | ||
379 | Socket.ExecStopPost, config_parse_exec, SOCKET_EXEC_STOP_POST, offsetof(Socket, exec_command) | ||
380 | Socket.TimeoutSec, config_parse_sec, 0, offsetof(Socket, timeout_usec) | ||
381 | -Socket.SocketUser, config_parse_unit_string_printf, 0, offsetof(Socket, user) | ||
382 | -Socket.SocketGroup, config_parse_unit_string_printf, 0, offsetof(Socket, group) | ||
383 | +Socket.SocketUser, config_parse_user_group, 0, offsetof(Socket, user) | ||
384 | +Socket.SocketGroup, config_parse_user_group, 0, offsetof(Socket, group) | ||
385 | Socket.SocketMode, config_parse_mode, 0, offsetof(Socket, socket_mode) | ||
386 | Socket.DirectoryMode, config_parse_mode, 0, offsetof(Socket, directory_mode) | ||
387 | Socket.Accept, config_parse_bool, 0, offsetof(Socket, accept) | ||
388 | diff --git a/src/core/load-fragment.c b/src/core/load-fragment.c | ||
389 | index 86b4fb071..f43781803 100644 | ||
390 | --- a/src/core/load-fragment.c | ||
391 | +++ b/src/core/load-fragment.c | ||
392 | @@ -64,6 +64,7 @@ | ||
393 | #include "unit-name.h" | ||
394 | #include "unit-printf.h" | ||
395 | #include "unit.h" | ||
396 | +#include "user-util.h" | ||
397 | #include "utf8.h" | ||
398 | #include "web-util.h" | ||
399 | |||
400 | @@ -1758,6 +1759,123 @@ int config_parse_sec_fix_0( | ||
401 | return 0; | ||
402 | } | ||
403 | |||
404 | +int config_parse_user_group( | ||
405 | + const char *unit, | ||
406 | + const char *filename, | ||
407 | + unsigned line, | ||
408 | + const char *section, | ||
409 | + unsigned section_line, | ||
410 | + const char *lvalue, | ||
411 | + int ltype, | ||
412 | + const char *rvalue, | ||
413 | + void *data, | ||
414 | + void *userdata) { | ||
415 | + | ||
416 | + char **user = data, *n; | ||
417 | + Unit *u = userdata; | ||
418 | + int r; | ||
419 | + | ||
420 | + assert(filename); | ||
421 | + assert(lvalue); | ||
422 | + assert(rvalue); | ||
423 | + assert(u); | ||
424 | + | ||
425 | + if (isempty(rvalue)) | ||
426 | + n = NULL; | ||
427 | + else { | ||
428 | + _cleanup_free_ char *k = NULL; | ||
429 | + | ||
430 | + r = unit_full_printf(u, rvalue, &k); | ||
431 | + if (r < 0) { | ||
432 | + log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve unit specifiers in %s, ignoring: %m", rvalue); | ||
433 | + return 0; | ||
434 | + } | ||
435 | + | ||
436 | + if (!valid_user_group_name_or_id(k)) { | ||
437 | + log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid user/group name or numeric ID, ignoring: %s", k); | ||
438 | + return 0; | ||
439 | + } | ||
440 | + | ||
441 | + n = k; | ||
442 | + k = NULL; | ||
443 | + } | ||
444 | + | ||
445 | + free(*user); | ||
446 | + *user = n; | ||
447 | + | ||
448 | + return 0; | ||
449 | +} | ||
450 | + | ||
451 | +int config_parse_user_group_strv( | ||
452 | + const char *unit, | ||
453 | + const char *filename, | ||
454 | + unsigned line, | ||
455 | + const char *section, | ||
456 | + unsigned section_line, | ||
457 | + const char *lvalue, | ||
458 | + int ltype, | ||
459 | + const char *rvalue, | ||
460 | + void *data, | ||
461 | + void *userdata) { | ||
462 | + | ||
463 | + char ***users = data; | ||
464 | + Unit *u = userdata; | ||
465 | + const char *p; | ||
466 | + int r; | ||
467 | + | ||
468 | + assert(filename); | ||
469 | + assert(lvalue); | ||
470 | + assert(rvalue); | ||
471 | + assert(u); | ||
472 | + | ||
473 | + if (isempty(rvalue)) { | ||
474 | + char **empty; | ||
475 | + | ||
476 | + empty = new0(char*, 1); | ||
477 | + if (!empty) | ||
478 | + return log_oom(); | ||
479 | + | ||
480 | + strv_free(*users); | ||
481 | + *users = empty; | ||
482 | + | ||
483 | + return 0; | ||
484 | + } | ||
485 | + | ||
486 | + p = rvalue; | ||
487 | + for (;;) { | ||
488 | + _cleanup_free_ char *word = NULL, *k = NULL; | ||
489 | + | ||
490 | + r = extract_first_word(&p, &word, WHITESPACE, 0); | ||
491 | + if (r == 0) | ||
492 | + break; | ||
493 | + if (r == -ENOMEM) | ||
494 | + return log_oom(); | ||
495 | + if (r < 0) { | ||
496 | + log_syntax(unit, LOG_ERR, filename, line, r, "Invalid syntax, ignoring: %s", rvalue); | ||
497 | + break; | ||
498 | + } | ||
499 | + | ||
500 | + r = unit_full_printf(u, word, &k); | ||
501 | + if (r < 0) { | ||
502 | + log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve unit specifiers in %s, ignoring: %m", word); | ||
503 | + continue; | ||
504 | + } | ||
505 | + | ||
506 | + if (!valid_user_group_name_or_id(k)) { | ||
507 | + log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid user/group name or numeric ID, ignoring: %s", k); | ||
508 | + continue; | ||
509 | + } | ||
510 | + | ||
511 | + r = strv_push(users, k); | ||
512 | + if (r < 0) | ||
513 | + return log_oom(); | ||
514 | + | ||
515 | + k = NULL; | ||
516 | + } | ||
517 | + | ||
518 | + return 0; | ||
519 | +} | ||
520 | + | ||
521 | int config_parse_busname_service( | ||
522 | const char *unit, | ||
523 | const char *filename, | ||
524 | diff --git a/src/core/load-fragment.h b/src/core/load-fragment.h | ||
525 | index b36a2e3a0..213bce55a 100644 | ||
526 | --- a/src/core/load-fragment.h | ||
527 | +++ b/src/core/load-fragment.h | ||
528 | @@ -111,6 +111,8 @@ int config_parse_exec_utmp_mode(const char *unit, const char *filename, unsigned | ||
529 | int config_parse_working_directory(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); | ||
530 | int config_parse_fdname(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); | ||
531 | int config_parse_sec_fix_0(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); | ||
532 | +int config_parse_user_group(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); | ||
533 | +int config_parse_user_group_strv(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); | ||
534 | |||
535 | /* gperf prototypes */ | ||
536 | const struct ConfigPerfItem* load_fragment_gperf_lookup(const char *key, unsigned length); | ||
537 | -- | ||
538 | 2.11.0 | ||
539 | |||
540 | |||
541 | From 97e0456384ed5c930394062d340237ea6130ece0 Mon Sep 17 00:00:00 2001 | ||
542 | From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= <zbyszek@in.waw.pl> | ||
543 | Date: Thu, 6 Jul 2017 13:28:19 -0400 | ||
544 | Subject: [PATCH 3/3] core/load-fragment: refuse units with errors in certain | ||
545 | directives | ||
546 | |||
547 | If an error is encountered in any of the Exec* lines, WorkingDirectory, | ||
548 | SELinuxContext, ApparmorProfile, SmackProcessLabel, Service (in .socket | ||
549 | units), User, or Group, refuse to load the unit. If the config stanza | ||
550 | has support, ignore the failure if '-' is present. | ||
551 | |||
552 | For those configuration directives, even if we started the unit, it's | ||
553 | pretty likely that it'll do something unexpected (like write files | ||
554 | in a wrong place, or with a wrong context, or run with wrong permissions, | ||
555 | etc). It seems better to refuse to start the unit and have the admin | ||
556 | clean up the configuration without giving the service a chance to mess | ||
557 | up stuff. | ||
558 | |||
559 | Note that all "security" options that restrict what the unit can do | ||
560 | (Capabilities, AmbientCapabilities, Restrict*, SystemCallFilter, Limit*, | ||
561 | PrivateDevices, Protect*, etc) are _not_ treated like this. Such options are | ||
562 | only supplementary, and are not always available depending on the architecture | ||
563 | and compilation options, so unit authors have to make sure that the service | ||
564 | runs correctly without them anyway. | ||
565 | |||
566 | Fixes #6237, #6277. | ||
567 | |||
568 | Signed-off-by: Ross Burton <ross.burton@intel.com> | ||
569 | --- | ||
570 | src/core/load-fragment.c | 101 ++++++++++++++++++++++++++++------------------ | ||
571 | src/test/test-unit-file.c | 14 +++---- | ||
572 | 2 files changed, 69 insertions(+), 46 deletions(-) | ||
573 | |||
574 | diff --git a/src/core/load-fragment.c b/src/core/load-fragment.c | ||
575 | index f43781803..b1fb1d407 100644 | ||
576 | --- a/src/core/load-fragment.c | ||
577 | +++ b/src/core/load-fragment.c | ||
578 | @@ -626,20 +626,28 @@ int config_parse_exec( | ||
579 | |||
580 | if (isempty(f)) { | ||
581 | /* First word is either "-" or "@" with no command. */ | ||
582 | - log_syntax(unit, LOG_ERR, filename, line, 0, "Empty path in command line, ignoring: \"%s\"", rvalue); | ||
583 | - return 0; | ||
584 | + log_syntax(unit, LOG_ERR, filename, line, 0, | ||
585 | + "Empty path in command line%s: \"%s\"", | ||
586 | + ignore ? ", ignoring" : "", rvalue); | ||
587 | + return ignore ? 0 : -ENOEXEC; | ||
588 | } | ||
589 | if (!string_is_safe(f)) { | ||
590 | - log_syntax(unit, LOG_ERR, filename, line, 0, "Executable path contains special characters, ignoring: %s", rvalue); | ||
591 | - return 0; | ||
592 | + log_syntax(unit, LOG_ERR, filename, line, 0, | ||
593 | + "Executable path contains special characters%s: %s", | ||
594 | + ignore ? ", ignoring" : "", rvalue); | ||
595 | + return ignore ? 0 : -ENOEXEC; | ||
596 | } | ||
597 | if (!path_is_absolute(f)) { | ||
598 | - log_syntax(unit, LOG_ERR, filename, line, 0, "Executable path is not absolute, ignoring: %s", rvalue); | ||
599 | - return 0; | ||
600 | + log_syntax(unit, LOG_ERR, filename, line, 0, | ||
601 | + "Executable path is not absolute%s: %s", | ||
602 | + ignore ? ", ignoring" : "", rvalue); | ||
603 | + return ignore ? 0 : -ENOEXEC; | ||
604 | } | ||
605 | if (endswith(f, "/")) { | ||
606 | - log_syntax(unit, LOG_ERR, filename, line, 0, "Executable path specifies a directory, ignoring: %s", rvalue); | ||
607 | - return 0; | ||
608 | + log_syntax(unit, LOG_ERR, filename, line, 0, | ||
609 | + "Executable path specifies a directory%s: %s", | ||
610 | + ignore ? ", ignoring" : "", rvalue); | ||
611 | + return ignore ? 0 : -ENOEXEC; | ||
612 | } | ||
613 | |||
614 | if (f == firstword) { | ||
615 | @@ -695,7 +703,7 @@ int config_parse_exec( | ||
616 | if (r == 0) | ||
617 | break; | ||
618 | else if (r < 0) | ||
619 | - return 0; | ||
620 | + return ignore ? 0 : -ENOEXEC; | ||
621 | |||
622 | if (!GREEDY_REALLOC(n, nbufsize, nlen + 2)) | ||
623 | return log_oom(); | ||
624 | @@ -705,8 +713,10 @@ int config_parse_exec( | ||
625 | } | ||
626 | |||
627 | if (!n || !n[0]) { | ||
628 | - log_syntax(unit, LOG_ERR, filename, line, 0, "Empty executable name or zeroeth argument, ignoring: %s", rvalue); | ||
629 | - return 0; | ||
630 | + log_syntax(unit, LOG_ERR, filename, line, 0, | ||
631 | + "Empty executable name or zeroeth argument%s: %s", | ||
632 | + ignore ? ", ignoring" : "", rvalue); | ||
633 | + return ignore ? 0 : -ENOEXEC; | ||
634 | } | ||
635 | |||
636 | nce = new0(ExecCommand, 1); | ||
637 | @@ -1214,8 +1224,10 @@ int config_parse_exec_selinux_context( | ||
638 | |||
639 | r = unit_name_printf(u, rvalue, &k); | ||
640 | if (r < 0) { | ||
641 | - log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve specifiers, ignoring: %m"); | ||
642 | - return 0; | ||
643 | + log_syntax(unit, LOG_ERR, filename, line, r, | ||
644 | + "Failed to resolve specifiers%s: %m", | ||
645 | + ignore ? ", ignoring" : ""); | ||
646 | + return ignore ? 0 : -ENOEXEC; | ||
647 | } | ||
648 | |||
649 | free(c->selinux_context); | ||
650 | @@ -1262,8 +1274,10 @@ int config_parse_exec_apparmor_profile( | ||
651 | |||
652 | r = unit_name_printf(u, rvalue, &k); | ||
653 | if (r < 0) { | ||
654 | - log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve specifiers, ignoring: %m"); | ||
655 | - return 0; | ||
656 | + log_syntax(unit, LOG_ERR, filename, line, r, | ||
657 | + "Failed to resolve specifiers%s: %m", | ||
658 | + ignore ? ", ignoring" : ""); | ||
659 | + return ignore ? 0 : -ENOEXEC; | ||
660 | } | ||
661 | |||
662 | free(c->apparmor_profile); | ||
663 | @@ -1310,8 +1324,10 @@ int config_parse_exec_smack_process_label( | ||
664 | |||
665 | r = unit_name_printf(u, rvalue, &k); | ||
666 | if (r < 0) { | ||
667 | - log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve specifiers, ignoring: %m"); | ||
668 | - return 0; | ||
669 | + log_syntax(unit, LOG_ERR, filename, line, r, | ||
670 | + "Failed to resolve specifiers%s: %m", | ||
671 | + ignore ? ", ignoring" : ""); | ||
672 | + return ignore ? 0 : -ENOEXEC; | ||
673 | } | ||
674 | |||
675 | free(c->smack_process_label); | ||
676 | @@ -1520,19 +1536,19 @@ int config_parse_socket_service( | ||
677 | |||
678 | r = unit_name_printf(UNIT(s), rvalue, &p); | ||
679 | if (r < 0) { | ||
680 | - log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve specifiers, ignoring: %s", rvalue); | ||
681 | - return 0; | ||
682 | + log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve specifiers: %s", rvalue); | ||
683 | + return -ENOEXEC; | ||
684 | } | ||
685 | |||
686 | if (!endswith(p, ".service")) { | ||
687 | - log_syntax(unit, LOG_ERR, filename, line, 0, "Unit must be of type service, ignoring: %s", rvalue); | ||
688 | - return 0; | ||
689 | + log_syntax(unit, LOG_ERR, filename, line, 0, "Unit must be of type service: %s", rvalue); | ||
690 | + return -ENOEXEC; | ||
691 | } | ||
692 | |||
693 | r = manager_load_unit(UNIT(s)->manager, p, NULL, &error, &x); | ||
694 | if (r < 0) { | ||
695 | - log_syntax(unit, LOG_ERR, filename, line, r, "Failed to load unit %s, ignoring: %s", rvalue, bus_error_message(&error, r)); | ||
696 | - return 0; | ||
697 | + log_syntax(unit, LOG_ERR, filename, line, r, "Failed to load unit %s: %s", rvalue, bus_error_message(&error, r)); | ||
698 | + return -ENOEXEC; | ||
699 | } | ||
700 | |||
701 | unit_ref_set(&s->service, x); | ||
702 | @@ -1787,13 +1803,13 @@ int config_parse_user_group( | ||
703 | |||
704 | r = unit_full_printf(u, rvalue, &k); | ||
705 | if (r < 0) { | ||
706 | - log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve unit specifiers in %s, ignoring: %m", rvalue); | ||
707 | - return 0; | ||
708 | + log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve unit specifiers in %s: %m", rvalue); | ||
709 | + return -ENOEXEC; | ||
710 | } | ||
711 | |||
712 | if (!valid_user_group_name_or_id(k)) { | ||
713 | - log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid user/group name or numeric ID, ignoring: %s", k); | ||
714 | - return 0; | ||
715 | + log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid user/group name or numeric ID: %s", k); | ||
716 | + return -ENOEXEC; | ||
717 | } | ||
718 | |||
719 | n = k; | ||
720 | @@ -1851,19 +1867,19 @@ int config_parse_user_group_strv( | ||
721 | if (r == -ENOMEM) | ||
722 | return log_oom(); | ||
723 | if (r < 0) { | ||
724 | - log_syntax(unit, LOG_ERR, filename, line, r, "Invalid syntax, ignoring: %s", rvalue); | ||
725 | - break; | ||
726 | + log_syntax(unit, LOG_ERR, filename, line, r, "Invalid syntax: %s", rvalue); | ||
727 | + return -ENOEXEC; | ||
728 | } | ||
729 | |||
730 | r = unit_full_printf(u, word, &k); | ||
731 | if (r < 0) { | ||
732 | - log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve unit specifiers in %s, ignoring: %m", word); | ||
733 | - continue; | ||
734 | + log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve unit specifiers in %s: %m", word); | ||
735 | + return -ENOEXEC; | ||
736 | } | ||
737 | |||
738 | if (!valid_user_group_name_or_id(k)) { | ||
739 | - log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid user/group name or numeric ID, ignoring: %s", k); | ||
740 | - continue; | ||
741 | + log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid user/group name or numeric ID: %s", k); | ||
742 | + return -ENOEXEC; | ||
743 | } | ||
744 | |||
745 | r = strv_push(users, k); | ||
746 | @@ -2022,20 +2038,24 @@ int config_parse_working_directory( | ||
747 | |||
748 | r = unit_full_printf(u, rvalue, &k); | ||
749 | if (r < 0) { | ||
750 | - log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve unit specifiers in working directory path '%s', ignoring: %m", rvalue); | ||
751 | - return 0; | ||
752 | + log_syntax(unit, LOG_ERR, filename, line, r, | ||
753 | + "Failed to resolve unit specifiers in working directory path '%s'%s: %m", | ||
754 | + rvalue, missing_ok ? ", ignoring" : ""); | ||
755 | + return missing_ok ? 0 : -ENOEXEC; | ||
756 | } | ||
757 | |||
758 | path_kill_slashes(k); | ||
759 | |||
760 | if (!utf8_is_valid(k)) { | ||
761 | log_syntax_invalid_utf8(unit, LOG_ERR, filename, line, rvalue); | ||
762 | - return 0; | ||
763 | + return missing_ok ? 0 : -ENOEXEC; | ||
764 | } | ||
765 | |||
766 | if (!path_is_absolute(k)) { | ||
767 | - log_syntax(unit, LOG_ERR, filename, line, 0, "Working directory path '%s' is not absolute, ignoring.", rvalue); | ||
768 | - return 0; | ||
769 | + log_syntax(unit, LOG_ERR, filename, line, 0, | ||
770 | + "Working directory path '%s' is not absolute%s.", | ||
771 | + rvalue, missing_ok ? ", ignoring" : ""); | ||
772 | + return missing_ok ? 0 : -ENOEXEC; | ||
773 | } | ||
774 | |||
775 | free(c->working_directory); | ||
776 | @@ -4043,8 +4063,11 @@ int unit_load_fragment(Unit *u) { | ||
777 | return r; | ||
778 | |||
779 | r = load_from_path(u, k); | ||
780 | - if (r < 0) | ||
781 | + if (r < 0) { | ||
782 | + if (r == -ENOEXEC) | ||
783 | + log_unit_notice(u, "Unit configuration has fatal error, unit will not be started."); | ||
784 | return r; | ||
785 | + } | ||
786 | |||
787 | if (u->load_state == UNIT_STUB) { | ||
788 | SET_FOREACH(t, u->names, i) { | ||
789 | diff --git a/src/test/test-unit-file.c b/src/test/test-unit-file.c | ||
790 | index ade0ff2a6..fe1969570 100644 | ||
791 | --- a/src/test/test-unit-file.c | ||
792 | +++ b/src/test/test-unit-file.c | ||
793 | @@ -146,7 +146,7 @@ static void test_config_parse_exec(void) { | ||
794 | r = config_parse_exec(NULL, "fake", 4, "section", 1, | ||
795 | "LValue", 0, "/RValue/ argv0 r1", | ||
796 | &c, u); | ||
797 | - assert_se(r == 0); | ||
798 | + assert_se(r == -ENOEXEC); | ||
799 | assert_se(c1->command_next == NULL); | ||
800 | |||
801 | log_info("/* honour_argv0 */"); | ||
802 | @@ -161,7 +161,7 @@ static void test_config_parse_exec(void) { | ||
803 | r = config_parse_exec(NULL, "fake", 3, "section", 1, | ||
804 | "LValue", 0, "@/RValue", | ||
805 | &c, u); | ||
806 | - assert_se(r == 0); | ||
807 | + assert_se(r == -ENOEXEC); | ||
808 | assert_se(c1->command_next == NULL); | ||
809 | |||
810 | log_info("/* no command, whitespace only, reset */"); | ||
811 | @@ -220,7 +220,7 @@ static void test_config_parse_exec(void) { | ||
812 | "-@/RValue argv0 r1 ; ; " | ||
813 | "/goo/goo boo", | ||
814 | &c, u); | ||
815 | - assert_se(r >= 0); | ||
816 | + assert_se(r == -ENOEXEC); | ||
817 | c1 = c1->command_next; | ||
818 | check_execcommand(c1, "/RValue", "argv0", "r1", NULL, true); | ||
819 | |||
820 | @@ -374,7 +374,7 @@ static void test_config_parse_exec(void) { | ||
821 | r = config_parse_exec(NULL, "fake", 4, "section", 1, | ||
822 | "LValue", 0, path, | ||
823 | &c, u); | ||
824 | - assert_se(r == 0); | ||
825 | + assert_se(r == -ENOEXEC); | ||
826 | assert_se(c1->command_next == NULL); | ||
827 | } | ||
828 | |||
829 | @@ -401,21 +401,21 @@ static void test_config_parse_exec(void) { | ||
830 | r = config_parse_exec(NULL, "fake", 4, "section", 1, | ||
831 | "LValue", 0, "/path\\", | ||
832 | &c, u); | ||
833 | - assert_se(r == 0); | ||
834 | + assert_se(r == -ENOEXEC); | ||
835 | assert_se(c1->command_next == NULL); | ||
836 | |||
837 | log_info("/* missing ending ' */"); | ||
838 | r = config_parse_exec(NULL, "fake", 4, "section", 1, | ||
839 | "LValue", 0, "/path 'foo", | ||
840 | &c, u); | ||
841 | - assert_se(r == 0); | ||
842 | + assert_se(r == -ENOEXEC); | ||
843 | assert_se(c1->command_next == NULL); | ||
844 | |||
845 | log_info("/* missing ending ' with trailing backslash */"); | ||
846 | r = config_parse_exec(NULL, "fake", 4, "section", 1, | ||
847 | "LValue", 0, "/path 'foo\\", | ||
848 | &c, u); | ||
849 | - assert_se(r == 0); | ||
850 | + assert_se(r == -ENOEXEC); | ||
851 | assert_se(c1->command_next == NULL); | ||
852 | |||
853 | log_info("/* invalid space between modifiers */"); | ||
854 | -- | ||
855 | 2.11.0 | ||
856 | |||
diff --git a/meta/recipes-core/systemd/systemd_230.bb b/meta/recipes-core/systemd/systemd_230.bb index 702e377264..40f1428340 100644 --- a/meta/recipes-core/systemd/systemd_230.bb +++ b/meta/recipes-core/systemd/systemd_230.bb | |||
@@ -36,6 +36,7 @@ SRC_URI += " \ | |||
36 | file://0022-socket-util-don-t-fail-if-libc-doesn-t-support-IDN.patch \ | 36 | file://0022-socket-util-don-t-fail-if-libc-doesn-t-support-IDN.patch \ |
37 | file://udev-re-enable-mount-propagation-for-udevd.patch \ | 37 | file://udev-re-enable-mount-propagation-for-udevd.patch \ |
38 | file://CVE-2016-7795.patch \ | 38 | file://CVE-2016-7795.patch \ |
39 | file://validate-user.patch \ | ||
39 | " | 40 | " |
40 | SRC_URI_append_libc-uclibc = "\ | 41 | SRC_URI_append_libc-uclibc = "\ |
41 | file://0002-units-Prefer-getty-to-agetty-in-console-setup-system.patch \ | 42 | file://0002-units-Prefer-getty-to-agetty-in-console-setup-system.patch \ |