diff options
author | Ross Burton <ross.burton@intel.com> | 2017-07-19 13:34:44 +0100 |
---|---|---|
committer | Richard Purdie <richard.purdie@linuxfoundation.org> | 2017-07-27 22:36:44 +0100 |
commit | b7e7b5e294f944c27fb1d2be61c0cf38f6c81ba8 (patch) | |
tree | 5f510d42d9f72143ad0835dc239668b198b355c1 /meta | |
parent | bc3fa28e93a4006f1625dd6877a179faaa412f10 (diff) | |
download | poky-b7e7b5e294f944c27fb1d2be61c0cf38f6c81ba8.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: a6eaef0f179a341c0b96bb30aaec2d80862a11d6)
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')
-rw-r--r-- | meta/recipes-core/systemd/systemd/0001-core-load-fragment-refuse-units-with-errors-in-certa.patch | 329 | ||||
-rw-r--r-- | meta/recipes-core/systemd/systemd_232.bb | 1 |
2 files changed, 330 insertions, 0 deletions
diff --git a/meta/recipes-core/systemd/systemd/0001-core-load-fragment-refuse-units-with-errors-in-certa.patch b/meta/recipes-core/systemd/systemd/0001-core-load-fragment-refuse-units-with-errors-in-certa.patch new file mode 100644 index 0000000000..80948b2cee --- /dev/null +++ b/meta/recipes-core/systemd/systemd/0001-core-load-fragment-refuse-units-with-errors-in-certa.patch | |||
@@ -0,0 +1,329 @@ | |||
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 d8e1310e1ed7b6f122bc7eb8ba061fbd088783c0 Mon Sep 17 00:00:00 2001 | ||
10 | From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= <zbyszek@in.waw.pl> | ||
11 | Date: Thu, 6 Jul 2017 13:28:19 -0400 | ||
12 | Subject: [PATCH] core/load-fragment: refuse units with errors in certain | ||
13 | directives | ||
14 | |||
15 | If an error is encountered in any of the Exec* lines, WorkingDirectory, | ||
16 | SELinuxContext, ApparmorProfile, SmackProcessLabel, Service (in .socket | ||
17 | units), User, or Group, refuse to load the unit. If the config stanza | ||
18 | has support, ignore the failure if '-' is present. | ||
19 | |||
20 | For those configuration directives, even if we started the unit, it's | ||
21 | pretty likely that it'll do something unexpected (like write files | ||
22 | in a wrong place, or with a wrong context, or run with wrong permissions, | ||
23 | etc). It seems better to refuse to start the unit and have the admin | ||
24 | clean up the configuration without giving the service a chance to mess | ||
25 | up stuff. | ||
26 | |||
27 | Note that all "security" options that restrict what the unit can do | ||
28 | (Capabilities, AmbientCapabilities, Restrict*, SystemCallFilter, Limit*, | ||
29 | PrivateDevices, Protect*, etc) are _not_ treated like this. Such options are | ||
30 | only supplementary, and are not always available depending on the architecture | ||
31 | and compilation options, so unit authors have to make sure that the service | ||
32 | runs correctly without them anyway. | ||
33 | |||
34 | Fixes #6237, #6277. | ||
35 | |||
36 | Signed-off-by: Ross Burton <ross.burton@intel.com> | ||
37 | --- | ||
38 | src/core/load-fragment.c | 104 ++++++++++++++++++++++++++++------------------ | ||
39 | src/test/test-unit-file.c | 14 +++---- | ||
40 | 2 files changed, 70 insertions(+), 48 deletions(-) | ||
41 | |||
42 | diff --git a/src/core/load-fragment.c b/src/core/load-fragment.c | ||
43 | index cbc826809..2047974f4 100644 | ||
44 | --- a/src/core/load-fragment.c | ||
45 | +++ b/src/core/load-fragment.c | ||
46 | @@ -630,20 +630,28 @@ int config_parse_exec( | ||
47 | |||
48 | if (isempty(f)) { | ||
49 | /* First word is either "-" or "@" with no command. */ | ||
50 | - log_syntax(unit, LOG_ERR, filename, line, 0, "Empty path in command line, ignoring: \"%s\"", rvalue); | ||
51 | - return 0; | ||
52 | + log_syntax(unit, LOG_ERR, filename, line, 0, | ||
53 | + "Empty path in command line%s: \"%s\"", | ||
54 | + ignore ? ", ignoring" : "", rvalue); | ||
55 | + return ignore ? 0 : -ENOEXEC; | ||
56 | } | ||
57 | if (!string_is_safe(f)) { | ||
58 | - log_syntax(unit, LOG_ERR, filename, line, 0, "Executable path contains special characters, ignoring: %s", rvalue); | ||
59 | - return 0; | ||
60 | + log_syntax(unit, LOG_ERR, filename, line, 0, | ||
61 | + "Executable path contains special characters%s: %s", | ||
62 | + ignore ? ", ignoring" : "", rvalue); | ||
63 | + return ignore ? 0 : -ENOEXEC; | ||
64 | } | ||
65 | if (!path_is_absolute(f)) { | ||
66 | - log_syntax(unit, LOG_ERR, filename, line, 0, "Executable path is not absolute, ignoring: %s", rvalue); | ||
67 | - return 0; | ||
68 | + log_syntax(unit, LOG_ERR, filename, line, 0, | ||
69 | + "Executable path is not absolute%s: %s", | ||
70 | + ignore ? ", ignoring" : "", rvalue); | ||
71 | + return ignore ? 0 : -ENOEXEC; | ||
72 | } | ||
73 | if (endswith(f, "/")) { | ||
74 | - log_syntax(unit, LOG_ERR, filename, line, 0, "Executable path specifies a directory, ignoring: %s", rvalue); | ||
75 | - return 0; | ||
76 | + log_syntax(unit, LOG_ERR, filename, line, 0, | ||
77 | + "Executable path specifies a directory%s: %s", | ||
78 | + ignore ? ", ignoring" : "", rvalue); | ||
79 | + return ignore ? 0 : -ENOEXEC; | ||
80 | } | ||
81 | |||
82 | if (f == firstword) { | ||
83 | @@ -699,7 +707,7 @@ int config_parse_exec( | ||
84 | if (r == 0) | ||
85 | break; | ||
86 | else if (r < 0) | ||
87 | - return 0; | ||
88 | + return ignore ? 0 : -ENOEXEC; | ||
89 | |||
90 | if (!GREEDY_REALLOC(n, nbufsize, nlen + 2)) | ||
91 | return log_oom(); | ||
92 | @@ -709,8 +717,10 @@ int config_parse_exec( | ||
93 | } | ||
94 | |||
95 | if (!n || !n[0]) { | ||
96 | - log_syntax(unit, LOG_ERR, filename, line, 0, "Empty executable name or zeroeth argument, ignoring: %s", rvalue); | ||
97 | - return 0; | ||
98 | + log_syntax(unit, LOG_ERR, filename, line, 0, | ||
99 | + "Empty executable name or zeroeth argument%s: %s", | ||
100 | + ignore ? ", ignoring" : "", rvalue); | ||
101 | + return ignore ? 0 : -ENOEXEC; | ||
102 | } | ||
103 | |||
104 | nce = new0(ExecCommand, 1); | ||
105 | @@ -1315,8 +1325,10 @@ int config_parse_exec_selinux_context( | ||
106 | |||
107 | r = unit_name_printf(u, rvalue, &k); | ||
108 | if (r < 0) { | ||
109 | - log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve specifiers, ignoring: %m"); | ||
110 | - return 0; | ||
111 | + log_syntax(unit, LOG_ERR, filename, line, r, | ||
112 | + "Failed to resolve specifiers%s: %m", | ||
113 | + ignore ? ", ignoring" : ""); | ||
114 | + return ignore ? 0 : -ENOEXEC; | ||
115 | } | ||
116 | |||
117 | free(c->selinux_context); | ||
118 | @@ -1363,8 +1375,10 @@ int config_parse_exec_apparmor_profile( | ||
119 | |||
120 | r = unit_name_printf(u, rvalue, &k); | ||
121 | if (r < 0) { | ||
122 | - log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve specifiers, ignoring: %m"); | ||
123 | - return 0; | ||
124 | + log_syntax(unit, LOG_ERR, filename, line, r, | ||
125 | + "Failed to resolve specifiers%s: %m", | ||
126 | + ignore ? ", ignoring" : ""); | ||
127 | + return ignore ? 0 : -ENOEXEC; | ||
128 | } | ||
129 | |||
130 | free(c->apparmor_profile); | ||
131 | @@ -1411,8 +1425,10 @@ int config_parse_exec_smack_process_label( | ||
132 | |||
133 | r = unit_name_printf(u, rvalue, &k); | ||
134 | if (r < 0) { | ||
135 | - log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve specifiers, ignoring: %m"); | ||
136 | - return 0; | ||
137 | + log_syntax(unit, LOG_ERR, filename, line, r, | ||
138 | + "Failed to resolve specifiers%s: %m", | ||
139 | + ignore ? ", ignoring" : ""); | ||
140 | + return ignore ? 0 : -ENOEXEC; | ||
141 | } | ||
142 | |||
143 | free(c->smack_process_label); | ||
144 | @@ -1630,19 +1646,19 @@ int config_parse_socket_service( | ||
145 | |||
146 | r = unit_name_printf(UNIT(s), rvalue, &p); | ||
147 | if (r < 0) { | ||
148 | - log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve specifiers, ignoring: %s", rvalue); | ||
149 | - return 0; | ||
150 | + log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve specifiers: %s", rvalue); | ||
151 | + return -ENOEXEC; | ||
152 | } | ||
153 | |||
154 | if (!endswith(p, ".service")) { | ||
155 | - log_syntax(unit, LOG_ERR, filename, line, 0, "Unit must be of type service, ignoring: %s", rvalue); | ||
156 | - return 0; | ||
157 | + log_syntax(unit, LOG_ERR, filename, line, 0, "Unit must be of type service: %s", rvalue); | ||
158 | + return -ENOEXEC; | ||
159 | } | ||
160 | |||
161 | r = manager_load_unit(UNIT(s)->manager, p, NULL, &error, &x); | ||
162 | if (r < 0) { | ||
163 | - log_syntax(unit, LOG_ERR, filename, line, r, "Failed to load unit %s, ignoring: %s", rvalue, bus_error_message(&error, r)); | ||
164 | - return 0; | ||
165 | + log_syntax(unit, LOG_ERR, filename, line, r, "Failed to load unit %s: %s", rvalue, bus_error_message(&error, r)); | ||
166 | + return -ENOEXEC; | ||
167 | } | ||
168 | |||
169 | unit_ref_set(&s->service, x); | ||
170 | @@ -1893,13 +1909,13 @@ int config_parse_user_group( | ||
171 | |||
172 | r = unit_full_printf(u, rvalue, &k); | ||
173 | if (r < 0) { | ||
174 | - log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve unit specifiers in %s, ignoring: %m", rvalue); | ||
175 | - return 0; | ||
176 | + log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve unit specifiers in %s: %m", rvalue); | ||
177 | + return -ENOEXEC; | ||
178 | } | ||
179 | |||
180 | if (!valid_user_group_name_or_id(k)) { | ||
181 | - log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid user/group name or numeric ID, ignoring: %s", k); | ||
182 | - return 0; | ||
183 | + log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid user/group name or numeric ID: %s", k); | ||
184 | + return -ENOEXEC; | ||
185 | } | ||
186 | |||
187 | n = k; | ||
188 | @@ -1957,19 +1973,19 @@ int config_parse_user_group_strv( | ||
189 | if (r == -ENOMEM) | ||
190 | return log_oom(); | ||
191 | if (r < 0) { | ||
192 | - log_syntax(unit, LOG_ERR, filename, line, r, "Invalid syntax, ignoring: %s", rvalue); | ||
193 | - break; | ||
194 | + log_syntax(unit, LOG_ERR, filename, line, r, "Invalid syntax: %s", rvalue); | ||
195 | + return -ENOEXEC; | ||
196 | } | ||
197 | |||
198 | r = unit_full_printf(u, word, &k); | ||
199 | if (r < 0) { | ||
200 | - log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve unit specifiers in %s, ignoring: %m", word); | ||
201 | - continue; | ||
202 | + log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve unit specifiers in %s: %m", word); | ||
203 | + return -ENOEXEC; | ||
204 | } | ||
205 | |||
206 | if (!valid_user_group_name_or_id(k)) { | ||
207 | - log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid user/group name or numeric ID, ignoring: %s", k); | ||
208 | - continue; | ||
209 | + log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid user/group name or numeric ID: %s", k); | ||
210 | + return -ENOEXEC; | ||
211 | } | ||
212 | |||
213 | r = strv_push(users, k); | ||
214 | @@ -2128,25 +2144,28 @@ int config_parse_working_directory( | ||
215 | |||
216 | r = unit_full_printf(u, rvalue, &k); | ||
217 | if (r < 0) { | ||
218 | - log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve unit specifiers in working directory path '%s', ignoring: %m", rvalue); | ||
219 | - return 0; | ||
220 | + log_syntax(unit, LOG_ERR, filename, line, r, | ||
221 | + "Failed to resolve unit specifiers in working directory path '%s'%s: %m", | ||
222 | + rvalue, missing_ok ? ", ignoring" : ""); | ||
223 | + return missing_ok ? 0 : -ENOEXEC; | ||
224 | } | ||
225 | |||
226 | path_kill_slashes(k); | ||
227 | |||
228 | if (!utf8_is_valid(k)) { | ||
229 | log_syntax_invalid_utf8(unit, LOG_ERR, filename, line, rvalue); | ||
230 | - return 0; | ||
231 | + return missing_ok ? 0 : -ENOEXEC; | ||
232 | } | ||
233 | |||
234 | if (!path_is_absolute(k)) { | ||
235 | - log_syntax(unit, LOG_ERR, filename, line, 0, "Working directory path '%s' is not absolute, ignoring.", rvalue); | ||
236 | - return 0; | ||
237 | + log_syntax(unit, LOG_ERR, filename, line, 0, | ||
238 | + "Working directory path '%s' is not absolute%s.", | ||
239 | + rvalue, missing_ok ? ", ignoring" : ""); | ||
240 | + return missing_ok ? 0 : -ENOEXEC; | ||
241 | } | ||
242 | |||
243 | - free_and_replace(c->working_directory, k); | ||
244 | - | ||
245 | c->working_directory_home = false; | ||
246 | + free_and_replace(c->working_directory, k); | ||
247 | } | ||
248 | |||
249 | c->working_directory_missing_ok = missing_ok; | ||
250 | @@ -4228,8 +4247,11 @@ int unit_load_fragment(Unit *u) { | ||
251 | return r; | ||
252 | |||
253 | r = load_from_path(u, k); | ||
254 | - if (r < 0) | ||
255 | + if (r < 0) { | ||
256 | + if (r == -ENOEXEC) | ||
257 | + log_unit_notice(u, "Unit configuration has fatal error, unit will not be started."); | ||
258 | return r; | ||
259 | + } | ||
260 | |||
261 | if (u->load_state == UNIT_STUB) { | ||
262 | SET_FOREACH(t, u->names, i) { | ||
263 | diff --git a/src/test/test-unit-file.c b/src/test/test-unit-file.c | ||
264 | index 12f48bf43..fd797b587 100644 | ||
265 | --- a/src/test/test-unit-file.c | ||
266 | +++ b/src/test/test-unit-file.c | ||
267 | @@ -146,7 +146,7 @@ static void test_config_parse_exec(void) { | ||
268 | r = config_parse_exec(NULL, "fake", 4, "section", 1, | ||
269 | "LValue", 0, "/RValue/ argv0 r1", | ||
270 | &c, u); | ||
271 | - assert_se(r == 0); | ||
272 | + assert_se(r == -ENOEXEC); | ||
273 | assert_se(c1->command_next == NULL); | ||
274 | |||
275 | log_info("/* honour_argv0 */"); | ||
276 | @@ -161,7 +161,7 @@ static void test_config_parse_exec(void) { | ||
277 | r = config_parse_exec(NULL, "fake", 3, "section", 1, | ||
278 | "LValue", 0, "@/RValue", | ||
279 | &c, u); | ||
280 | - assert_se(r == 0); | ||
281 | + assert_se(r == -ENOEXEC); | ||
282 | assert_se(c1->command_next == NULL); | ||
283 | |||
284 | log_info("/* no command, whitespace only, reset */"); | ||
285 | @@ -220,7 +220,7 @@ static void test_config_parse_exec(void) { | ||
286 | "-@/RValue argv0 r1 ; ; " | ||
287 | "/goo/goo boo", | ||
288 | &c, u); | ||
289 | - assert_se(r >= 0); | ||
290 | + assert_se(r == -ENOEXEC); | ||
291 | c1 = c1->command_next; | ||
292 | check_execcommand(c1, "/RValue", "argv0", "r1", NULL, true); | ||
293 | |||
294 | @@ -374,7 +374,7 @@ static void test_config_parse_exec(void) { | ||
295 | r = config_parse_exec(NULL, "fake", 4, "section", 1, | ||
296 | "LValue", 0, path, | ||
297 | &c, u); | ||
298 | - assert_se(r == 0); | ||
299 | + assert_se(r == -ENOEXEC); | ||
300 | assert_se(c1->command_next == NULL); | ||
301 | } | ||
302 | |||
303 | @@ -401,21 +401,21 @@ static void test_config_parse_exec(void) { | ||
304 | r = config_parse_exec(NULL, "fake", 4, "section", 1, | ||
305 | "LValue", 0, "/path\\", | ||
306 | &c, u); | ||
307 | - assert_se(r == 0); | ||
308 | + assert_se(r == -ENOEXEC); | ||
309 | assert_se(c1->command_next == NULL); | ||
310 | |||
311 | log_info("/* missing ending ' */"); | ||
312 | r = config_parse_exec(NULL, "fake", 4, "section", 1, | ||
313 | "LValue", 0, "/path 'foo", | ||
314 | &c, u); | ||
315 | - assert_se(r == 0); | ||
316 | + assert_se(r == -ENOEXEC); | ||
317 | assert_se(c1->command_next == NULL); | ||
318 | |||
319 | log_info("/* missing ending ' with trailing backslash */"); | ||
320 | r = config_parse_exec(NULL, "fake", 4, "section", 1, | ||
321 | "LValue", 0, "/path 'foo\\", | ||
322 | &c, u); | ||
323 | - assert_se(r == 0); | ||
324 | + assert_se(r == -ENOEXEC); | ||
325 | assert_se(c1->command_next == NULL); | ||
326 | |||
327 | log_info("/* invalid space between modifiers */"); | ||
328 | -- | ||
329 | 2.11.0 | ||
diff --git a/meta/recipes-core/systemd/systemd_232.bb b/meta/recipes-core/systemd/systemd_232.bb index 398cb46f0d..e54c723d7f 100644 --- a/meta/recipes-core/systemd/systemd_232.bb +++ b/meta/recipes-core/systemd/systemd_232.bb | |||
@@ -33,6 +33,7 @@ SRC_URI += " \ | |||
33 | file://0018-check-for-uchar.h-in-configure.patch \ | 33 | file://0018-check-for-uchar.h-in-configure.patch \ |
34 | file://0019-socket-util-don-t-fail-if-libc-doesn-t-support-IDN.patch \ | 34 | file://0019-socket-util-don-t-fail-if-libc-doesn-t-support-IDN.patch \ |
35 | file://0020-back-port-233-don-t-use-the-unified-hierarchy-for-the-systemd.patch \ | 35 | file://0020-back-port-233-don-t-use-the-unified-hierarchy-for-the-systemd.patch \ |
36 | file://0001-core-load-fragment-refuse-units-with-errors-in-certa.patch \ | ||
36 | " | 37 | " |
37 | SRC_URI_append_libc-uclibc = "\ | 38 | SRC_URI_append_libc-uclibc = "\ |
38 | file://0002-units-Prefer-getty-to-agetty-in-console-setup-system.patch \ | 39 | file://0002-units-Prefer-getty-to-agetty-in-console-setup-system.patch \ |