diff options
| author | Sona Sarmadi <sona.sarmadi@enea.com> | 2017-10-03 10:55:33 +0200 |
|---|---|---|
| committer | Adrian Dudau <adrian.dudau@enea.com> | 2017-10-04 09:58:01 +0200 |
| commit | d8a0f5de3d13e8747376af9e7fd2b5007ffb8aab (patch) | |
| tree | abb4662c2ca32b2051822471e097875ce51159d9 | |
| parent | 404efc06d1d42e6e56a51b8a703f2bcf653c1705 (diff) | |
| download | meta-el-common-d8a0f5de3d13e8747376af9e7fd2b5007ffb8aab.tar.gz | |
systemd: CVE-2017-1000082
refuse to load units with errors
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)
Reference:
https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2017-1000082
Backport from: http://git.yoctoproject.org/cgit/cgit.cgi/poky/commit/?h=pyro&id=b7e7b5e294f944c27fb1d2be61c0cf38f6c81ba8
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>
Signed-off-by: Sona Sarmadi <sona.sarmadi@enea.com>
Signed-off-by: Adrian Dudau <adrian.dudau@enea.com>
| -rw-r--r-- | recipes-core/systemd/systemd/CVE-2017-1000082.patch | 329 | ||||
| -rw-r--r-- | recipes-core/systemd/systemd_%.bbappend | 1 |
2 files changed, 330 insertions, 0 deletions
diff --git a/recipes-core/systemd/systemd/CVE-2017-1000082.patch b/recipes-core/systemd/systemd/CVE-2017-1000082.patch new file mode 100644 index 0000000..80948b2 --- /dev/null +++ b/recipes-core/systemd/systemd/CVE-2017-1000082.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/recipes-core/systemd/systemd_%.bbappend b/recipes-core/systemd/systemd_%.bbappend index e07dbe1..6688e28 100644 --- a/recipes-core/systemd/systemd_%.bbappend +++ b/recipes-core/systemd/systemd_%.bbappend | |||
| @@ -2,5 +2,6 @@ | |||
| 2 | FILESEXTRAPATHS_prepend := "${THISDIR}/${PN}:" | 2 | FILESEXTRAPATHS_prepend := "${THISDIR}/${PN}:" |
| 3 | 3 | ||
| 4 | SRC_URI += "file://CVE-2017-9445.patch \ | 4 | SRC_URI += "file://CVE-2017-9445.patch \ |
| 5 | file://CVE-2017-1000082.patch \ | ||
| 5 | " | 6 | " |
| 6 | 7 | ||
