diff options
3 files changed, 2473 insertions, 0 deletions
diff --git a/meta/recipes-core/systemd/systemd/0001-tmpfiles-don-t-resolve-pathnames-when-traversing-rec.patch b/meta/recipes-core/systemd/systemd/0001-tmpfiles-don-t-resolve-pathnames-when-traversing-rec.patch new file mode 100644 index 0000000000..108e4ad8b8 --- /dev/null +++ b/meta/recipes-core/systemd/systemd/0001-tmpfiles-don-t-resolve-pathnames-when-traversing-rec.patch | |||
| @@ -0,0 +1,643 @@ | |||
| 1 | From 33dc9a280f952f503e5493ee29f6815bef29d551 Mon Sep 17 00:00:00 2001 | ||
| 2 | From: Franck Bui <fbui@suse.com> | ||
| 3 | Date: Fri, 2 Mar 2018 17:19:32 +0100 | ||
| 4 | Subject: [PATCH] tmpfiles: don't resolve pathnames when traversing recursively | ||
| 5 | through directory trees | ||
| 6 | |||
| 7 | Otherwise we can be fooled if one path component is replaced underneath us. | ||
| 8 | |||
| 9 | The patch achieves that by always operating at file descriptor level (by using | ||
| 10 | *at() helpers) and by making sure we do not any path resolution when traversing | ||
| 11 | direcotry trees. | ||
| 12 | |||
| 13 | However this is not always possible, for instance when listing the content of a | ||
| 14 | directory or some operations don't provide the *at() helpers or others (such as | ||
| 15 | fchmodat()) don't have the AT_EMPTY_PATH flag. In such cases we operate on | ||
| 16 | /proc/self/fd/%i pseudo-symlink instead, which works the same for all kinds of | ||
| 17 | objects and requires no checking of type beforehand. | ||
| 18 | |||
| 19 | Also O_PATH flag is used when opening file objects in order to prevent | ||
| 20 | undesired behaviors: device nodes from reacting, automounts from | ||
| 21 | triggering, etc... | ||
| 22 | |||
| 23 | Fixes: CVE-2018-6954 | ||
| 24 | |||
| 25 | Origin: upstream, https://github.com/systemd/systemd/commit/936f6bdb803c432578e2cdcc5f93f3bfff93aff0 | ||
| 26 | Bug: https://github.com/systemd/systemd/issues/7986 | ||
| 27 | |||
| 28 | Patch from: | ||
| 29 | systemd_237-3ubuntu10.13.debian CVE-2018-6954.patch | ||
| 30 | |||
| 31 | https://usn.ubuntu.com/3816-1/ states that CVE-2018-6954 doesn't | ||
| 32 | affect Ubuntu 18.10 which uses the same version of systemd as thud | ||
| 33 | (239). | ||
| 34 | |||
| 35 | CVE: CVE-2018-6954 | ||
| 36 | Upstream-Status: Backport | ||
| 37 | |||
| 38 | Signed-off-by: George McCollister <george.mccollister@gmail.com> | ||
| 39 | --- | ||
| 40 | src/tmpfiles/tmpfiles.c | 363 +++++++++++++++++++++++++++++++----------------- | ||
| 41 | 1 file changed, 239 insertions(+), 124 deletions(-) | ||
| 42 | |||
| 43 | diff --git a/src/tmpfiles/tmpfiles.c b/src/tmpfiles/tmpfiles.c | ||
| 44 | index 88cc543f09..613d418eb3 100644 | ||
| 45 | --- a/src/tmpfiles/tmpfiles.c | ||
| 46 | +++ b/src/tmpfiles/tmpfiles.c | ||
| 47 | @@ -792,94 +792,105 @@ static bool hardlink_vulnerable(struct stat *st) { | ||
| 48 | return !S_ISDIR(st->st_mode) && st->st_nlink > 1 && dangerous_hardlinks(); | ||
| 49 | } | ||
| 50 | |||
| 51 | -static int path_set_perms(Item *i, const char *path) { | ||
| 52 | - char fn[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int)]; | ||
| 53 | - _cleanup_close_ int fd = -1; | ||
| 54 | - struct stat st; | ||
| 55 | +static int fd_set_perms(Item *i, int fd, const struct stat *st) { | ||
| 56 | + _cleanup_free_ char *path = NULL; | ||
| 57 | + int r; | ||
| 58 | |||
| 59 | assert(i); | ||
| 60 | - assert(path); | ||
| 61 | - | ||
| 62 | - if (!i->mode_set && !i->uid_set && !i->gid_set) | ||
| 63 | - goto shortcut; | ||
| 64 | - | ||
| 65 | - /* We open the file with O_PATH here, to make the operation | ||
| 66 | - * somewhat atomic. Also there's unfortunately no fchmodat() | ||
| 67 | - * with AT_SYMLINK_NOFOLLOW, hence we emulate it here via | ||
| 68 | - * O_PATH. */ | ||
| 69 | - | ||
| 70 | - fd = open(path, O_NOFOLLOW|O_CLOEXEC|O_PATH); | ||
| 71 | - if (fd < 0) { | ||
| 72 | - int level = LOG_ERR, r = -errno; | ||
| 73 | + assert(fd); | ||
| 74 | |||
| 75 | - /* Option "e" operates only on existing objects. Do not | ||
| 76 | - * print errors about non-existent files or directories */ | ||
| 77 | - if (i->type == EMPTY_DIRECTORY && errno == ENOENT) { | ||
| 78 | - level = LOG_DEBUG; | ||
| 79 | - r = 0; | ||
| 80 | - } | ||
| 81 | - | ||
| 82 | - log_full_errno(level, errno, "Adjusting owner and mode for %s failed: %m", path); | ||
| 83 | + r = fd_get_path(fd, &path); | ||
| 84 | + if (r < 0) | ||
| 85 | return r; | ||
| 86 | - } | ||
| 87 | |||
| 88 | - if (fstatat(fd, "", &st, AT_EMPTY_PATH) < 0) | ||
| 89 | - return log_error_errno(errno, "Failed to fstat() file %s: %m", path); | ||
| 90 | + if (!i->mode_set && !i->uid_set && !i->gid_set) | ||
| 91 | + goto shortcut; | ||
| 92 | |||
| 93 | - if (hardlink_vulnerable(&st)) { | ||
| 94 | + if (hardlink_vulnerable(st)) { | ||
| 95 | log_error("Refusing to set permissions on hardlinked file %s while the fs.protected_hardlinks sysctl is turned off.", path); | ||
| 96 | return -EPERM; | ||
| 97 | } | ||
| 98 | |||
| 99 | - xsprintf(fn, "/proc/self/fd/%i", fd); | ||
| 100 | - | ||
| 101 | if (i->mode_set) { | ||
| 102 | - if (S_ISLNK(st.st_mode)) | ||
| 103 | + if (S_ISLNK(st->st_mode)) | ||
| 104 | log_debug("Skipping mode fix for symlink %s.", path); | ||
| 105 | else { | ||
| 106 | mode_t m = i->mode; | ||
| 107 | |||
| 108 | if (i->mask_perms) { | ||
| 109 | - if (!(st.st_mode & 0111)) | ||
| 110 | + if (!(st->st_mode & 0111)) | ||
| 111 | m &= ~0111; | ||
| 112 | - if (!(st.st_mode & 0222)) | ||
| 113 | + if (!(st->st_mode & 0222)) | ||
| 114 | m &= ~0222; | ||
| 115 | - if (!(st.st_mode & 0444)) | ||
| 116 | + if (!(st->st_mode & 0444)) | ||
| 117 | m &= ~0444; | ||
| 118 | - if (!S_ISDIR(st.st_mode)) | ||
| 119 | + if (!S_ISDIR(st->st_mode)) | ||
| 120 | m &= ~07000; /* remove sticky/sgid/suid bit, unless directory */ | ||
| 121 | } | ||
| 122 | |||
| 123 | - if (m == (st.st_mode & 07777)) | ||
| 124 | - log_debug("\"%s\" has correct mode %o already.", path, st.st_mode); | ||
| 125 | + if (m == (st->st_mode & 07777)) | ||
| 126 | + log_debug("\"%s\" has correct mode %o already.", path, st->st_mode); | ||
| 127 | else { | ||
| 128 | + char procfs_path[strlen("/proc/self/fd/") + DECIMAL_STR_MAX(int)]; | ||
| 129 | + | ||
| 130 | log_debug("Changing \"%s\" to mode %o.", path, m); | ||
| 131 | |||
| 132 | - if (chmod(fn, m) < 0) | ||
| 133 | - return log_error_errno(errno, "chmod() of %s via %s failed: %m", path, fn); | ||
| 134 | + /* fchmodat() still doesn't have AT_EMPTY_PATH flag. */ | ||
| 135 | + xsprintf(procfs_path, "/proc/self/fd/%i", fd); | ||
| 136 | + | ||
| 137 | + if (chmod(procfs_path, m) < 0) | ||
| 138 | + return log_error_errno(errno, "chmod() of %s via %s failed: %m", path, procfs_path); | ||
| 139 | } | ||
| 140 | } | ||
| 141 | } | ||
| 142 | |||
| 143 | - if ((i->uid_set && i->uid != st.st_uid) || | ||
| 144 | - (i->gid_set && i->gid != st.st_gid)) { | ||
| 145 | + if ((i->uid_set && i->uid != st->st_uid) || | ||
| 146 | + (i->gid_set && i->gid != st->st_gid)) { | ||
| 147 | log_debug("Changing \"%s\" to owner "UID_FMT":"GID_FMT, | ||
| 148 | path, | ||
| 149 | i->uid_set ? i->uid : UID_INVALID, | ||
| 150 | i->gid_set ? i->gid : GID_INVALID); | ||
| 151 | |||
| 152 | - if (chown(fn, | ||
| 153 | - i->uid_set ? i->uid : UID_INVALID, | ||
| 154 | - i->gid_set ? i->gid : GID_INVALID) < 0) | ||
| 155 | - return log_error_errno(errno, "chown() of %s via %s failed: %m", path, fn); | ||
| 156 | + if (fchownat(fd, | ||
| 157 | + "", | ||
| 158 | + i->uid_set ? i->uid : UID_INVALID, | ||
| 159 | + i->gid_set ? i->gid : GID_INVALID, | ||
| 160 | + AT_EMPTY_PATH) < 0) | ||
| 161 | + return log_error_errno(errno, "fchownat() of %s failed: %m", path); | ||
| 162 | } | ||
| 163 | |||
| 164 | - fd = safe_close(fd); | ||
| 165 | - | ||
| 166 | shortcut: | ||
| 167 | return label_fix(path, false, false); | ||
| 168 | } | ||
| 169 | |||
| 170 | +static int path_set_perms(Item *i, const char *path) { | ||
| 171 | + _cleanup_close_ int fd = -1; | ||
| 172 | + struct stat st; | ||
| 173 | + | ||
| 174 | + assert(i); | ||
| 175 | + assert(path); | ||
| 176 | + | ||
| 177 | + fd = open(path, O_NOFOLLOW|O_CLOEXEC|O_PATH); | ||
| 178 | + if (fd < 0) { | ||
| 179 | + int level = LOG_ERR, r = -errno; | ||
| 180 | + | ||
| 181 | + /* Option "e" operates only on existing objects. Do not | ||
| 182 | + * print errors about non-existent files or directories */ | ||
| 183 | + if (i->type == EMPTY_DIRECTORY && errno == ENOENT) { | ||
| 184 | + level = LOG_DEBUG; | ||
| 185 | + r = 0; | ||
| 186 | + } | ||
| 187 | + | ||
| 188 | + log_full_errno(level, errno, "Adjusting owner and mode for %s failed: %m", path); | ||
| 189 | + return r; | ||
| 190 | + } | ||
| 191 | + | ||
| 192 | + if (fstat(fd, &st) < 0) | ||
| 193 | + return log_error_errno(errno, "Failed to fstat() file %s: %m", path); | ||
| 194 | + | ||
| 195 | + return fd_set_perms(i, fd, &st); | ||
| 196 | +} | ||
| 197 | + | ||
| 198 | static int parse_xattrs_from_arg(Item *i) { | ||
| 199 | const char *p; | ||
| 200 | int r; | ||
| 201 | @@ -918,21 +929,43 @@ static int parse_xattrs_from_arg(Item *i) { | ||
| 202 | return 0; | ||
| 203 | } | ||
| 204 | |||
| 205 | -static int path_set_xattrs(Item *i, const char *path) { | ||
| 206 | +static int fd_set_xattrs(Item *i, int fd, const struct stat *st) { | ||
| 207 | + char procfs_path[strlen("/proc/self/fd/") + DECIMAL_STR_MAX(int)]; | ||
| 208 | + _cleanup_free_ char *path = NULL; | ||
| 209 | char **name, **value; | ||
| 210 | + int r; | ||
| 211 | |||
| 212 | assert(i); | ||
| 213 | - assert(path); | ||
| 214 | + assert(fd); | ||
| 215 | + | ||
| 216 | + r = fd_get_path(fd, &path); | ||
| 217 | + if (r < 0) | ||
| 218 | + return r; | ||
| 219 | + | ||
| 220 | + xsprintf(procfs_path, "/proc/self/fd/%i", fd); | ||
| 221 | |||
| 222 | STRV_FOREACH_PAIR(name, value, i->xattrs) { | ||
| 223 | log_debug("Setting extended attribute '%s=%s' on %s.", *name, *value, path); | ||
| 224 | - if (lsetxattr(path, *name, *value, strlen(*value), 0) < 0) | ||
| 225 | + if (setxattr(procfs_path, *name, *value, strlen(*value), 0) < 0) | ||
| 226 | return log_error_errno(errno, "Setting extended attribute %s=%s on %s failed: %m", | ||
| 227 | *name, *value, path); | ||
| 228 | } | ||
| 229 | return 0; | ||
| 230 | } | ||
| 231 | |||
| 232 | +static int path_set_xattrs(Item *i, const char *path) { | ||
| 233 | + _cleanup_close_ int fd = -1; | ||
| 234 | + | ||
| 235 | + assert(i); | ||
| 236 | + assert(path); | ||
| 237 | + | ||
| 238 | + fd = open(path, O_CLOEXEC|O_NOFOLLOW|O_PATH); | ||
| 239 | + if (fd < 0) | ||
| 240 | + return log_error_errno(errno, "Cannot open '%s': %m", path); | ||
| 241 | + | ||
| 242 | + return fd_set_xattrs(i, fd, NULL); | ||
| 243 | +} | ||
| 244 | + | ||
| 245 | static int parse_acls_from_arg(Item *item) { | ||
| 246 | #if HAVE_ACL | ||
| 247 | int r; | ||
| 248 | @@ -998,52 +1031,71 @@ static int path_set_acl(const char *path, const char *pretty, acl_type_t type, a | ||
| 249 | } | ||
| 250 | #endif | ||
| 251 | |||
| 252 | -static int path_set_acls(Item *item, const char *path) { | ||
| 253 | +static int fd_set_acls(Item *item, int fd, const struct stat *st) { | ||
| 254 | int r = 0; | ||
| 255 | #if HAVE_ACL | ||
| 256 | - char fn[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int)]; | ||
| 257 | - _cleanup_close_ int fd = -1; | ||
| 258 | - struct stat st; | ||
| 259 | + char procfs_path[strlen("/proc/self/fd/") + DECIMAL_STR_MAX(int)]; | ||
| 260 | + _cleanup_free_ char *path = NULL; | ||
| 261 | |||
| 262 | assert(item); | ||
| 263 | - assert(path); | ||
| 264 | - | ||
| 265 | - fd = open(path, O_NOFOLLOW|O_CLOEXEC|O_PATH); | ||
| 266 | - if (fd < 0) | ||
| 267 | - return log_error_errno(errno, "Adjusting ACL of %s failed: %m", path); | ||
| 268 | + assert(fd); | ||
| 269 | + assert(st); | ||
| 270 | |||
| 271 | - if (fstatat(fd, "", &st, AT_EMPTY_PATH) < 0) | ||
| 272 | - return log_error_errno(errno, "Failed to fstat() file %s: %m", path); | ||
| 273 | + r = fd_get_path(fd, &path); | ||
| 274 | + if (r < 0) | ||
| 275 | + return r; | ||
| 276 | |||
| 277 | - if (hardlink_vulnerable(&st)) { | ||
| 278 | + if (hardlink_vulnerable(st)) { | ||
| 279 | log_error("Refusing to set ACLs on hardlinked file %s while the fs.protected_hardlinks sysctl is turned off.", path); | ||
| 280 | return -EPERM; | ||
| 281 | } | ||
| 282 | |||
| 283 | - if (S_ISLNK(st.st_mode)) { | ||
| 284 | + if (S_ISLNK(st->st_mode)) { | ||
| 285 | log_debug("Skipping ACL fix for symlink %s.", path); | ||
| 286 | return 0; | ||
| 287 | } | ||
| 288 | |||
| 289 | - xsprintf(fn, "/proc/self/fd/%i", fd); | ||
| 290 | + xsprintf(procfs_path, "/proc/self/fd/%i", fd); | ||
| 291 | |||
| 292 | if (item->acl_access) | ||
| 293 | - r = path_set_acl(fn, path, ACL_TYPE_ACCESS, item->acl_access, item->force); | ||
| 294 | + r = path_set_acl(procfs_path, path, ACL_TYPE_ACCESS, item->acl_access, item->force); | ||
| 295 | |||
| 296 | if (r == 0 && item->acl_default) | ||
| 297 | - r = path_set_acl(fn, path, ACL_TYPE_DEFAULT, item->acl_default, item->force); | ||
| 298 | + r = path_set_acl(procfs_path, path, ACL_TYPE_DEFAULT, item->acl_default, item->force); | ||
| 299 | |||
| 300 | if (r > 0) | ||
| 301 | return -r; /* already warned */ | ||
| 302 | - else if (r == -EOPNOTSUPP) { | ||
| 303 | + if (r == -EOPNOTSUPP) { | ||
| 304 | log_debug_errno(r, "ACLs not supported by file system at %s", path); | ||
| 305 | return 0; | ||
| 306 | - } else if (r < 0) | ||
| 307 | - log_error_errno(r, "ACL operation on \"%s\" failed: %m", path); | ||
| 308 | + } | ||
| 309 | + if (r < 0) | ||
| 310 | + return log_error_errno(r, "ACL operation on \"%s\" failed: %m", path); | ||
| 311 | #endif | ||
| 312 | return r; | ||
| 313 | } | ||
| 314 | |||
| 315 | +static int path_set_acls(Item *item, const char *path) { | ||
| 316 | + int r = 0; | ||
| 317 | +#ifdef HAVE_ACL | ||
| 318 | + _cleanup_close_ int fd = -1; | ||
| 319 | + struct stat st; | ||
| 320 | + | ||
| 321 | + assert(item); | ||
| 322 | + assert(path); | ||
| 323 | + | ||
| 324 | + fd = open(path, O_NOFOLLOW|O_CLOEXEC|O_PATH); | ||
| 325 | + if (fd < 0) | ||
| 326 | + return log_error_errno(errno, "Adjusting ACL of %s failed: %m", path); | ||
| 327 | + | ||
| 328 | + if (fstat(fd, &st) < 0) | ||
| 329 | + return log_error_errno(errno, "Failed to fstat() file %s: %m", path); | ||
| 330 | + | ||
| 331 | + r = fd_set_acls(item, fd, &st); | ||
| 332 | + #endif | ||
| 333 | + return r; | ||
| 334 | + } | ||
| 335 | + | ||
| 336 | #define ATTRIBUTES_ALL \ | ||
| 337 | (FS_NOATIME_FL | \ | ||
| 338 | FS_SYNC_FL | \ | ||
| 339 | @@ -1143,30 +1195,24 @@ static int parse_attribute_from_arg(Item *item) { | ||
| 340 | return 0; | ||
| 341 | } | ||
| 342 | |||
| 343 | -static int path_set_attribute(Item *item, const char *path) { | ||
| 344 | - _cleanup_close_ int fd = -1; | ||
| 345 | - struct stat st; | ||
| 346 | +static int fd_set_attribute(Item *item, int fd, const struct stat *st) { | ||
| 347 | + char procfs_path[strlen("/proc/self/fd/") + DECIMAL_STR_MAX(int)]; | ||
| 348 | + _cleanup_close_ int procfs_fd = -1; | ||
| 349 | + _cleanup_free_ char *path = NULL; | ||
| 350 | unsigned f; | ||
| 351 | int r; | ||
| 352 | |||
| 353 | if (!item->attribute_set || item->attribute_mask == 0) | ||
| 354 | return 0; | ||
| 355 | |||
| 356 | - fd = open(path, O_RDONLY|O_NONBLOCK|O_CLOEXEC|O_NOATIME|O_NOFOLLOW); | ||
| 357 | - if (fd < 0) { | ||
| 358 | - if (errno == ELOOP) | ||
| 359 | - return log_error_errno(errno, "Skipping file attributes adjustment on symlink %s.", path); | ||
| 360 | - | ||
| 361 | - return log_error_errno(errno, "Cannot open '%s': %m", path); | ||
| 362 | - } | ||
| 363 | - | ||
| 364 | - if (fstat(fd, &st) < 0) | ||
| 365 | - return log_error_errno(errno, "Cannot stat '%s': %m", path); | ||
| 366 | + r = fd_get_path(fd, &path); | ||
| 367 | + if (r < 0) | ||
| 368 | + return r; | ||
| 369 | |||
| 370 | /* Issuing the file attribute ioctls on device nodes is not | ||
| 371 | * safe, as that will be delivered to the drivers, not the | ||
| 372 | * file system containing the device node. */ | ||
| 373 | - if (!S_ISREG(st.st_mode) && !S_ISDIR(st.st_mode)) { | ||
| 374 | + if (!S_ISREG(st->st_mode) && !S_ISDIR(st->st_mode)) { | ||
| 375 | log_error("Setting file flags is only supported on regular files and directories, cannot set on '%s'.", path); | ||
| 376 | return -EINVAL; | ||
| 377 | } | ||
| 378 | @@ -1174,10 +1220,16 @@ static int path_set_attribute(Item *item, const char *path) { | ||
| 379 | f = item->attribute_value & item->attribute_mask; | ||
| 380 | |||
| 381 | /* Mask away directory-specific flags */ | ||
| 382 | - if (!S_ISDIR(st.st_mode)) | ||
| 383 | + if (!S_ISDIR(st->st_mode)) | ||
| 384 | f &= ~FS_DIRSYNC_FL; | ||
| 385 | |||
| 386 | - r = chattr_fd(fd, f, item->attribute_mask); | ||
| 387 | + xsprintf(procfs_path, "/proc/self/fd/%i", fd); | ||
| 388 | + | ||
| 389 | + procfs_fd = open(procfs_path, O_RDONLY|O_CLOEXEC|O_NOATIME); | ||
| 390 | + if (procfs_fd < 0) | ||
| 391 | + return -errno; | ||
| 392 | + | ||
| 393 | + r = chattr_fd(procfs_fd, f, item->attribute_mask); | ||
| 394 | if (r < 0) | ||
| 395 | log_full_errno(IN_SET(r, -ENOTTY, -EOPNOTSUPP) ? LOG_DEBUG : LOG_WARNING, | ||
| 396 | r, | ||
| 397 | @@ -1187,6 +1239,23 @@ static int path_set_attribute(Item *item, const char *path) { | ||
| 398 | return 0; | ||
| 399 | } | ||
| 400 | |||
| 401 | +static int path_set_attribute(Item *item, const char *path) { | ||
| 402 | + _cleanup_close_ int fd = -1; | ||
| 403 | + struct stat st; | ||
| 404 | + | ||
| 405 | + if (!item->attribute_set || item->attribute_mask == 0) | ||
| 406 | + return 0; | ||
| 407 | + | ||
| 408 | + fd = open(path, O_CLOEXEC|O_NOFOLLOW|O_PATH); | ||
| 409 | + if (fd < 0) | ||
| 410 | + return log_error_errno(errno, "Cannot open '%s': %m", path); | ||
| 411 | + | ||
| 412 | + if (fstat(fd, &st) < 0) | ||
| 413 | + return log_error_errno(errno, "Cannot stat '%s': %m", path); | ||
| 414 | + | ||
| 415 | + return fd_set_attribute(item, fd, &st); | ||
| 416 | +} | ||
| 417 | + | ||
| 418 | static int write_one_file(Item *i, const char *path) { | ||
| 419 | _cleanup_close_ int fd = -1; | ||
| 420 | int flags, r = 0; | ||
| 421 | @@ -1251,48 +1320,58 @@ done: | ||
| 422 | } | ||
| 423 | |||
| 424 | typedef int (*action_t)(Item *, const char *); | ||
| 425 | +typedef int (*fdaction_t)(Item *, int fd, const struct stat *st); | ||
| 426 | |||
| 427 | -static int item_do_children(Item *i, const char *path, action_t action) { | ||
| 428 | - _cleanup_closedir_ DIR *d; | ||
| 429 | - struct dirent *de; | ||
| 430 | - int r = 0; | ||
| 431 | +static int item_do(Item *i, int fd, const struct stat *st, fdaction_t action) { | ||
| 432 | + int r = 0, q; | ||
| 433 | |||
| 434 | assert(i); | ||
| 435 | - assert(path); | ||
| 436 | + assert(fd >= 0); | ||
| 437 | + assert(st); | ||
| 438 | |||
| 439 | /* This returns the first error we run into, but nevertheless | ||
| 440 | * tries to go on */ | ||
| 441 | + r = action(i, fd, st); | ||
| 442 | |||
| 443 | - d = opendir_nomod(path); | ||
| 444 | - if (!d) | ||
| 445 | - return IN_SET(errno, ENOENT, ENOTDIR, ELOOP) ? 0 : -errno; | ||
| 446 | + if (S_ISDIR(st->st_mode)) { | ||
| 447 | + char procfs_path[strlen("/proc/self/fd/") + DECIMAL_STR_MAX(int)]; | ||
| 448 | + _cleanup_closedir_ DIR *d = NULL; | ||
| 449 | + struct dirent *de; | ||
| 450 | |||
| 451 | - FOREACH_DIRENT_ALL(de, d, r = -errno) { | ||
| 452 | - _cleanup_free_ char *p = NULL; | ||
| 453 | - int q; | ||
| 454 | + /* The passed 'fd' was opened with O_PATH. We need to convert | ||
| 455 | + * it into a 'regular' fd before reading the directory content. */ | ||
| 456 | + xsprintf(procfs_path, "/proc/self/fd/%i", fd); | ||
| 457 | |||
| 458 | - if (dot_or_dot_dot(de->d_name)) | ||
| 459 | - continue; | ||
| 460 | + d = opendir(procfs_path); | ||
| 461 | + if (!d) { | ||
| 462 | + r = r ?: -errno; | ||
| 463 | + goto finish; | ||
| 464 | + } | ||
| 465 | |||
| 466 | - p = strjoin(path, "/", de->d_name); | ||
| 467 | - if (!p) | ||
| 468 | - return -ENOMEM; | ||
| 469 | + FOREACH_DIRENT_ALL(de, d, q = -errno; goto finish) { | ||
| 470 | + struct stat de_st; | ||
| 471 | + int de_fd; | ||
| 472 | + | ||
| 473 | + if (dot_or_dot_dot(de->d_name)) | ||
| 474 | + continue; | ||
| 475 | |||
| 476 | - q = action(i, p); | ||
| 477 | - if (q < 0 && q != -ENOENT && r == 0) | ||
| 478 | - r = q; | ||
| 479 | + de_fd = openat(fd, de->d_name, O_NOFOLLOW|O_CLOEXEC|O_PATH); | ||
| 480 | + if (de_fd >= 0 && fstat(de_fd, &de_st) >= 0) | ||
| 481 | + /* pass ownership of dirent fd over */ | ||
| 482 | + q = item_do(i, de_fd, &de_st, action); | ||
| 483 | + else | ||
| 484 | + q = -errno; | ||
| 485 | |||
| 486 | - if (IN_SET(de->d_type, DT_UNKNOWN, DT_DIR)) { | ||
| 487 | - q = item_do_children(i, p, action); | ||
| 488 | if (q < 0 && r == 0) | ||
| 489 | r = q; | ||
| 490 | } | ||
| 491 | } | ||
| 492 | - | ||
| 493 | +finish: | ||
| 494 | + safe_close(fd); | ||
| 495 | return r; | ||
| 496 | } | ||
| 497 | |||
| 498 | -static int glob_item(Item *i, action_t action, bool recursive) { | ||
| 499 | +static int glob_item(Item *i, action_t action) { | ||
| 500 | _cleanup_globfree_ glob_t g = { | ||
| 501 | #ifdef GLOB_ALTDIRFUNC | ||
| 502 | .gl_opendir = (void *(*)(const char *)) opendir_nomod, | ||
| 503 | @@ -1309,12 +1388,48 @@ static int glob_item(Item *i, action_t action, bool recursive) { | ||
| 504 | k = action(i, *fn); | ||
| 505 | if (k < 0 && r == 0) | ||
| 506 | r = k; | ||
| 507 | + } | ||
| 508 | |||
| 509 | - if (recursive) { | ||
| 510 | - k = item_do_children(i, *fn, action); | ||
| 511 | - if (k < 0 && r == 0) | ||
| 512 | - r = k; | ||
| 513 | + return r; | ||
| 514 | +} | ||
| 515 | + | ||
| 516 | +static int glob_item_recursively(Item *i, fdaction_t action) { | ||
| 517 | + _cleanup_globfree_ glob_t g = { | ||
| 518 | + .gl_opendir = (void *(*)(const char *)) opendir_nomod, | ||
| 519 | + }; | ||
| 520 | + int r = 0, k; | ||
| 521 | + char **fn; | ||
| 522 | + | ||
| 523 | + k = safe_glob(i->path, GLOB_NOSORT|GLOB_BRACE, &g); | ||
| 524 | + if (k < 0 && k != -ENOENT) | ||
| 525 | + return log_error_errno(k, "glob(%s) failed: %m", i->path); | ||
| 526 | + | ||
| 527 | + STRV_FOREACH(fn, g.gl_pathv) { | ||
| 528 | + _cleanup_close_ int fd = -1; | ||
| 529 | + struct stat st; | ||
| 530 | + | ||
| 531 | + /* Make sure we won't trigger/follow file object (such as | ||
| 532 | + * device nodes, automounts, ...) pointed out by 'fn' with | ||
| 533 | + * O_PATH. Note, when O_PATH is used, flags other than | ||
| 534 | + * O_CLOEXEC, O_DIRECTORY, and O_NOFOLLOW are ignored. */ | ||
| 535 | + | ||
| 536 | + fd = open(*fn, O_CLOEXEC|O_NOFOLLOW|O_PATH); | ||
| 537 | + if (fd < 0) { | ||
| 538 | + r = r ?: -errno; | ||
| 539 | + continue; | ||
| 540 | + } | ||
| 541 | + | ||
| 542 | + if (fstat(fd, &st) < 0) { | ||
| 543 | + r = r ?: -errno; | ||
| 544 | + continue; | ||
| 545 | } | ||
| 546 | + | ||
| 547 | + k = item_do(i, fd, &st, action); | ||
| 548 | + if (k < 0 && r == 0) | ||
| 549 | + r = k; | ||
| 550 | + | ||
| 551 | + /* we passed fd ownership to the previous call */ | ||
| 552 | + fd = -1; | ||
| 553 | } | ||
| 554 | |||
| 555 | return r; | ||
| 556 | @@ -1403,7 +1518,7 @@ static int create_item(Item *i) { | ||
| 557 | break; | ||
| 558 | |||
| 559 | case WRITE_FILE: | ||
| 560 | - r = glob_item(i, write_one_file, false); | ||
| 561 | + r = glob_item(i, write_one_file); | ||
| 562 | if (r < 0) | ||
| 563 | return r; | ||
| 564 | |||
| 565 | @@ -1662,49 +1777,49 @@ static int create_item(Item *i) { | ||
| 566 | |||
| 567 | case ADJUST_MODE: | ||
| 568 | case RELABEL_PATH: | ||
| 569 | - r = glob_item(i, path_set_perms, false); | ||
| 570 | + r = glob_item(i, path_set_perms); | ||
| 571 | if (r < 0) | ||
| 572 | return r; | ||
| 573 | break; | ||
| 574 | |||
| 575 | case RECURSIVE_RELABEL_PATH: | ||
| 576 | - r = glob_item(i, path_set_perms, true); | ||
| 577 | + r = glob_item_recursively(i, fd_set_perms); | ||
| 578 | if (r < 0) | ||
| 579 | return r; | ||
| 580 | break; | ||
| 581 | |||
| 582 | case SET_XATTR: | ||
| 583 | - r = glob_item(i, path_set_xattrs, false); | ||
| 584 | + r = glob_item(i, path_set_xattrs); | ||
| 585 | if (r < 0) | ||
| 586 | return r; | ||
| 587 | break; | ||
| 588 | |||
| 589 | case RECURSIVE_SET_XATTR: | ||
| 590 | - r = glob_item(i, path_set_xattrs, true); | ||
| 591 | + r = glob_item_recursively(i, fd_set_xattrs); | ||
| 592 | if (r < 0) | ||
| 593 | return r; | ||
| 594 | break; | ||
| 595 | |||
| 596 | case SET_ACL: | ||
| 597 | - r = glob_item(i, path_set_acls, false); | ||
| 598 | + r = glob_item(i, path_set_acls); | ||
| 599 | if (r < 0) | ||
| 600 | return r; | ||
| 601 | break; | ||
| 602 | |||
| 603 | case RECURSIVE_SET_ACL: | ||
| 604 | - r = glob_item(i, path_set_acls, true); | ||
| 605 | + r = glob_item_recursively(i, fd_set_acls); | ||
| 606 | if (r < 0) | ||
| 607 | return r; | ||
| 608 | break; | ||
| 609 | |||
| 610 | case SET_ATTRIBUTE: | ||
| 611 | - r = glob_item(i, path_set_attribute, false); | ||
| 612 | + r = glob_item(i, path_set_attribute); | ||
| 613 | if (r < 0) | ||
| 614 | return r; | ||
| 615 | break; | ||
| 616 | |||
| 617 | case RECURSIVE_SET_ATTRIBUTE: | ||
| 618 | - r = glob_item(i, path_set_attribute, true); | ||
| 619 | + r = glob_item_recursively(i, fd_set_attribute); | ||
| 620 | if (r < 0) | ||
| 621 | return r; | ||
| 622 | break; | ||
| 623 | @@ -1754,7 +1869,7 @@ static int remove_item(Item *i) { | ||
| 624 | case REMOVE_PATH: | ||
| 625 | case TRUNCATE_DIRECTORY: | ||
| 626 | case RECURSIVE_REMOVE_PATH: | ||
| 627 | - return glob_item(i, remove_item_instance, false); | ||
| 628 | + return glob_item(i, remove_item_instance); | ||
| 629 | |||
| 630 | default: | ||
| 631 | return 0; | ||
| 632 | @@ -1828,7 +1943,7 @@ static int clean_item(Item *i) { | ||
| 633 | return 0; | ||
| 634 | case EMPTY_DIRECTORY: | ||
| 635 | case IGNORE_DIRECTORY_PATH: | ||
| 636 | - return glob_item(i, clean_item_instance, false); | ||
| 637 | + return glob_item(i, clean_item_instance); | ||
| 638 | default: | ||
| 639 | return 0; | ||
| 640 | } | ||
| 641 | -- | ||
| 642 | 2.11.0 | ||
| 643 | |||
diff --git a/meta/recipes-core/systemd/systemd/0002-Make-tmpfiles-safe.patch b/meta/recipes-core/systemd/systemd/0002-Make-tmpfiles-safe.patch new file mode 100644 index 0000000000..80d27c141b --- /dev/null +++ b/meta/recipes-core/systemd/systemd/0002-Make-tmpfiles-safe.patch | |||
| @@ -0,0 +1,1828 @@ | |||
| 1 | From fb95c890cf5116e698347c6a7bb3daeeb2d28cf9 Mon Sep 17 00:00:00 2001 | ||
| 2 | From: George McCollister <george.mccollister@gmail.com> | ||
| 3 | Date: Thu, 21 Feb 2019 18:04:37 -0600 | ||
| 4 | Subject: [PATCH] Make tmpfiles safe | ||
| 5 | |||
| 6 | In addition to backporting the changesets in #8822, this also backports | ||
| 7 | e04fc13 (test: add tests for systemd-tmpfiles), as well as empty_to_root() | ||
| 8 | from v239. | ||
| 9 | |||
| 10 | Origin: upstream, https://github.com/systemd/systemd/pull/8822/commits | ||
| 11 | Bug: https://github.com/systemd/systemd/issues/7986 | ||
| 12 | |||
| 13 | Patch from: | ||
| 14 | systemd_237-3ubuntu10.13.debian CVE-2018-6954_2.patch | ||
| 15 | |||
| 16 | https://usn.ubuntu.com/3816-1/ states that CVE-2018-6954 doesn't | ||
| 17 | affect Ubuntu 18.10 which uses the same version of systemd as thud | ||
| 18 | (239). | ||
| 19 | |||
| 20 | CVE: CVE-2018-6954 | ||
| 21 | Upstream-Status: Backport | ||
| 22 | |||
| 23 | Signed-off-by: George McCollister <george.mccollister@gmail.com> | ||
| 24 | --- | ||
| 25 | src/basic/btrfs-util.c | 26 +- | ||
| 26 | src/basic/btrfs-util.h | 1 + | ||
| 27 | src/basic/fileio.c | 5 +- | ||
| 28 | src/basic/fs-util.c | 27 +- | ||
| 29 | src/basic/fs-util.h | 2 + | ||
| 30 | src/basic/label.h | 1 + | ||
| 31 | src/basic/mkdir-label.c | 17 + | ||
| 32 | src/basic/mkdir.c | 6 + | ||
| 33 | src/basic/mkdir.h | 1 + | ||
| 34 | src/basic/path-util.c | 5 +- | ||
| 35 | src/basic/path-util.h | 4 + | ||
| 36 | src/basic/selinux-util.c | 84 +++-- | ||
| 37 | src/basic/selinux-util.h | 1 + | ||
| 38 | src/basic/smack-util.c | 119 +++++-- | ||
| 39 | src/basic/smack-util.h | 1 + | ||
| 40 | src/basic/stat-util.c | 11 + | ||
| 41 | src/basic/stat-util.h | 1 + | ||
| 42 | src/test/test-fs-util.c | 25 ++ | ||
| 43 | src/tmpfiles/tmpfiles.c | 902 ++++++++++++++++++++++++++++++++--------------- | ||
| 44 | 19 files changed, 882 insertions(+), 357 deletions(-) | ||
| 45 | |||
| 46 | diff --git a/src/basic/btrfs-util.c b/src/basic/btrfs-util.c | ||
| 47 | index 19d385ab7c..26b088f52b 100644 | ||
| 48 | --- a/src/basic/btrfs-util.c | ||
| 49 | +++ b/src/basic/btrfs-util.c | ||
| 50 | @@ -150,8 +150,25 @@ int btrfs_is_subvol(const char *path) { | ||
| 51 | return btrfs_is_subvol_fd(fd); | ||
| 52 | } | ||
| 53 | |||
| 54 | -int btrfs_subvol_make(const char *path) { | ||
| 55 | +int btrfs_subvol_make_fd(int fd, const char *subvolume) { | ||
| 56 | struct btrfs_ioctl_vol_args args = {}; | ||
| 57 | + int r; | ||
| 58 | + | ||
| 59 | + assert(subvolume); | ||
| 60 | + | ||
| 61 | + r = validate_subvolume_name(subvolume); | ||
| 62 | + if (r < 0) | ||
| 63 | + return r; | ||
| 64 | + | ||
| 65 | + strncpy(args.name, subvolume, sizeof(args.name)-1); | ||
| 66 | + | ||
| 67 | + if (ioctl(fd, BTRFS_IOC_SUBVOL_CREATE, &args) < 0) | ||
| 68 | + return -errno; | ||
| 69 | + | ||
| 70 | + return 0; | ||
| 71 | +} | ||
| 72 | + | ||
| 73 | +int btrfs_subvol_make(const char *path) { | ||
| 74 | _cleanup_close_ int fd = -1; | ||
| 75 | const char *subvolume; | ||
| 76 | int r; | ||
| 77 | @@ -166,12 +183,7 @@ int btrfs_subvol_make(const char *path) { | ||
| 78 | if (fd < 0) | ||
| 79 | return fd; | ||
| 80 | |||
| 81 | - strncpy(args.name, subvolume, sizeof(args.name)-1); | ||
| 82 | - | ||
| 83 | - if (ioctl(fd, BTRFS_IOC_SUBVOL_CREATE, &args) < 0) | ||
| 84 | - return -errno; | ||
| 85 | - | ||
| 86 | - return 0; | ||
| 87 | + return btrfs_subvol_make_fd(fd, subvolume); | ||
| 88 | } | ||
| 89 | |||
| 90 | int btrfs_subvol_set_read_only_fd(int fd, bool b) { | ||
| 91 | diff --git a/src/basic/btrfs-util.h b/src/basic/btrfs-util.h | ||
| 92 | index 952b3c26da..e92687bc57 100644 | ||
| 93 | --- a/src/basic/btrfs-util.h | ||
| 94 | +++ b/src/basic/btrfs-util.h | ||
| 95 | @@ -84,6 +84,7 @@ int btrfs_resize_loopback_fd(int fd, uint64_t size, bool grow_only); | ||
| 96 | int btrfs_resize_loopback(const char *path, uint64_t size, bool grow_only); | ||
| 97 | |||
| 98 | int btrfs_subvol_make(const char *path); | ||
| 99 | +int btrfs_subvol_make_fd(int fd, const char *subvolume); | ||
| 100 | |||
| 101 | int btrfs_subvol_snapshot_fd(int old_fd, const char *new_path, BtrfsSnapshotFlags flags); | ||
| 102 | int btrfs_subvol_snapshot(const char *old_path, const char *new_path, BtrfsSnapshotFlags flags); | ||
| 103 | diff --git a/src/basic/fileio.c b/src/basic/fileio.c | ||
| 104 | index 26d6174664..1c7e23332f 100644 | ||
| 105 | --- a/src/basic/fileio.c | ||
| 106 | +++ b/src/basic/fileio.c | ||
| 107 | @@ -1304,7 +1304,10 @@ int tempfn_random_child(const char *p, const char *extra, char **ret) { | ||
| 108 | if (!t) | ||
| 109 | return -ENOMEM; | ||
| 110 | |||
| 111 | - x = stpcpy(stpcpy(stpcpy(t, p), "/.#"), extra); | ||
| 112 | + if (isempty(p)) | ||
| 113 | + x = stpcpy(stpcpy(t, ".#"), extra); | ||
| 114 | + else | ||
| 115 | + x = stpcpy(stpcpy(stpcpy(t, p), "/.#"), extra); | ||
| 116 | |||
| 117 | u = random_u64(); | ||
| 118 | for (i = 0; i < 16; i++) { | ||
| 119 | diff --git a/src/basic/fs-util.c b/src/basic/fs-util.c | ||
| 120 | index a8e50d4c78..c96a8813ea 100644 | ||
| 121 | --- a/src/basic/fs-util.c | ||
| 122 | +++ b/src/basic/fs-util.c | ||
| 123 | @@ -465,6 +465,31 @@ int mkfifo_atomic(const char *path, mode_t mode) { | ||
| 124 | return 0; | ||
| 125 | } | ||
| 126 | |||
| 127 | +int mkfifoat_atomic(int dirfd, const char *path, mode_t mode) { | ||
| 128 | + _cleanup_free_ char *t = NULL; | ||
| 129 | + int r; | ||
| 130 | + | ||
| 131 | + assert(path); | ||
| 132 | + | ||
| 133 | + if (path_is_absolute(path)) | ||
| 134 | + return mkfifo_atomic(path, mode); | ||
| 135 | + | ||
| 136 | + /* We're only interested in the (random) filename. */ | ||
| 137 | + r = tempfn_random_child("", NULL, &t); | ||
| 138 | + if (r < 0) | ||
| 139 | + return r; | ||
| 140 | + | ||
| 141 | + if (mkfifoat(dirfd, t, mode) < 0) | ||
| 142 | + return -errno; | ||
| 143 | + | ||
| 144 | + if (renameat(dirfd, t, dirfd, path) < 0) { | ||
| 145 | + unlink_noerrno(t); | ||
| 146 | + return -errno; | ||
| 147 | + } | ||
| 148 | + | ||
| 149 | + return 0; | ||
| 150 | +} | ||
| 151 | + | ||
| 152 | int get_files_in_directory(const char *path, char ***list) { | ||
| 153 | _cleanup_closedir_ DIR *d = NULL; | ||
| 154 | struct dirent *de; | ||
| 155 | @@ -808,7 +833,7 @@ int chase_symlinks(const char *path, const char *original_root, unsigned flags, | ||
| 156 | fd_is_fs_type(child, AUTOFS_SUPER_MAGIC) > 0) | ||
| 157 | return -EREMOTE; | ||
| 158 | |||
| 159 | - if (S_ISLNK(st.st_mode)) { | ||
| 160 | + if (S_ISLNK(st.st_mode) && !((flags & CHASE_NOFOLLOW) && isempty(todo))) { | ||
| 161 | char *joined; | ||
| 162 | |||
| 163 | _cleanup_free_ char *destination = NULL; | ||
| 164 | diff --git a/src/basic/fs-util.h b/src/basic/fs-util.h | ||
| 165 | index 9c4b02eccd..121345e74d 100644 | ||
| 166 | --- a/src/basic/fs-util.h | ||
| 167 | +++ b/src/basic/fs-util.h | ||
| 168 | @@ -80,6 +80,7 @@ int symlink_idempotent(const char *from, const char *to); | ||
| 169 | int symlink_atomic(const char *from, const char *to); | ||
| 170 | int mknod_atomic(const char *path, mode_t mode, dev_t dev); | ||
| 171 | int mkfifo_atomic(const char *path, mode_t mode); | ||
| 172 | +int mkfifoat_atomic(int dir_fd, const char *path, mode_t mode); | ||
| 173 | |||
| 174 | int get_files_in_directory(const char *path, char ***list); | ||
| 175 | |||
| 176 | @@ -106,6 +107,7 @@ enum { | ||
| 177 | CHASE_NO_AUTOFS = 1U << 2, /* If set, return -EREMOTE if autofs mount point found */ | ||
| 178 | CHASE_SAFE = 1U << 3, /* If set, return EPERM if we ever traverse from unprivileged to privileged files or directories */ | ||
| 179 | CHASE_OPEN = 1U << 4, /* If set, return an O_PATH object to the final component */ | ||
| 180 | + CHASE_NOFOLLOW = 1U << 7, /* Only valid with CHASE_OPEN: when the path's right-most component refers to symlink return O_PATH fd of the symlink, rather than following it. */ | ||
| 181 | }; | ||
| 182 | |||
| 183 | int chase_symlinks(const char *path_with_prefix, const char *root, unsigned flags, char **ret); | ||
| 184 | diff --git a/src/basic/label.h b/src/basic/label.h | ||
| 185 | index d73dacec4f..3ecfed72c6 100644 | ||
| 186 | --- a/src/basic/label.h | ||
| 187 | +++ b/src/basic/label.h | ||
| 188 | @@ -26,6 +26,7 @@ | ||
| 189 | int label_fix(const char *path, bool ignore_enoent, bool ignore_erofs); | ||
| 190 | |||
| 191 | int mkdir_label(const char *path, mode_t mode); | ||
| 192 | +int mkdirat_label(int dirfd, const char *path, mode_t mode); | ||
| 193 | int symlink_label(const char *old_path, const char *new_path); | ||
| 194 | |||
| 195 | int btrfs_subvol_make_label(const char *path); | ||
| 196 | diff --git a/src/basic/mkdir-label.c b/src/basic/mkdir-label.c | ||
| 197 | index 6f3a46f467..3c1a227bfa 100644 | ||
| 198 | --- a/src/basic/mkdir-label.c | ||
| 199 | +++ b/src/basic/mkdir-label.c | ||
| 200 | @@ -47,6 +47,23 @@ int mkdir_label(const char *path, mode_t mode) { | ||
| 201 | return mac_smack_fix(path, false, false); | ||
| 202 | } | ||
| 203 | |||
| 204 | +int mkdirat_label(int dirfd, const char *path, mode_t mode) { | ||
| 205 | + int r; | ||
| 206 | + | ||
| 207 | + assert(path); | ||
| 208 | + | ||
| 209 | + r = mac_selinux_create_file_prepare_at(dirfd, path, S_IFDIR); | ||
| 210 | + if (r < 0) | ||
| 211 | + return r; | ||
| 212 | + | ||
| 213 | + r = mkdirat_errno_wrapper(dirfd, path, mode); | ||
| 214 | + mac_selinux_create_file_clear(); | ||
| 215 | + if (r < 0) | ||
| 216 | + return r; | ||
| 217 | + | ||
| 218 | + return mac_smack_fix_at(dirfd, path, false, false); | ||
| 219 | +} | ||
| 220 | + | ||
| 221 | int mkdir_safe_label(const char *path, mode_t mode, uid_t uid, gid_t gid, bool follow_symlink) { | ||
| 222 | return mkdir_safe_internal(path, mode, uid, gid, follow_symlink, mkdir_label); | ||
| 223 | } | ||
| 224 | diff --git a/src/basic/mkdir.c b/src/basic/mkdir.c | ||
| 225 | index d51518a5a7..418945ad4a 100644 | ||
| 226 | --- a/src/basic/mkdir.c | ||
| 227 | +++ b/src/basic/mkdir.c | ||
| 228 | @@ -77,6 +77,12 @@ int mkdir_errno_wrapper(const char *pathname, mode_t mode) { | ||
| 229 | return 0; | ||
| 230 | } | ||
| 231 | |||
| 232 | +int mkdirat_errno_wrapper(int dirfd, const char *pathname, mode_t mode) { | ||
| 233 | + if (mkdirat(dirfd, pathname, mode) < 0) | ||
| 234 | + return -errno; | ||
| 235 | + return 0; | ||
| 236 | +} | ||
| 237 | + | ||
| 238 | int mkdir_safe(const char *path, mode_t mode, uid_t uid, gid_t gid, bool follow_symlink) { | ||
| 239 | return mkdir_safe_internal(path, mode, uid, gid, follow_symlink, mkdir_errno_wrapper); | ||
| 240 | } | ||
| 241 | diff --git a/src/basic/mkdir.h b/src/basic/mkdir.h | ||
| 242 | index d6c2d579a3..3ec6f3ed2d 100644 | ||
| 243 | --- a/src/basic/mkdir.h | ||
| 244 | +++ b/src/basic/mkdir.h | ||
| 245 | @@ -24,6 +24,7 @@ | ||
| 246 | #include <sys/types.h> | ||
| 247 | |||
| 248 | int mkdir_errno_wrapper(const char *pathname, mode_t mode); | ||
| 249 | +int mkdirat_errno_wrapper(int dirfd, const char *pathname, mode_t mode); | ||
| 250 | int mkdir_safe(const char *path, mode_t mode, uid_t uid, gid_t gid, bool follow_symlink); | ||
| 251 | int mkdir_parents(const char *path, mode_t mode); | ||
| 252 | int mkdir_p(const char *path, mode_t mode); | ||
| 253 | diff --git a/src/basic/path-util.c b/src/basic/path-util.c | ||
| 254 | index df94629385..84404f7ee1 100644 | ||
| 255 | --- a/src/basic/path-util.c | ||
| 256 | +++ b/src/basic/path-util.c | ||
| 257 | @@ -127,10 +127,7 @@ int path_make_absolute_cwd(const char *p, char **ret) { | ||
| 258 | if (r < 0) | ||
| 259 | return r; | ||
| 260 | |||
| 261 | - if (endswith(cwd, "/")) | ||
| 262 | - c = strjoin(cwd, p); | ||
| 263 | - else | ||
| 264 | - c = strjoin(cwd, "/", p); | ||
| 265 | + c = path_join(NULL, cwd, p); | ||
| 266 | } | ||
| 267 | if (!c) | ||
| 268 | return -ENOMEM; | ||
| 269 | diff --git a/src/basic/path-util.h b/src/basic/path-util.h | ||
| 270 | index 89c285e076..1094baca12 100644 | ||
| 271 | --- a/src/basic/path-util.h | ||
| 272 | +++ b/src/basic/path-util.h | ||
| 273 | @@ -156,3 +156,7 @@ static inline const char *skip_dev_prefix(const char *p) { | ||
| 274 | |||
| 275 | return e ?: p; | ||
| 276 | } | ||
| 277 | +static inline const char *empty_to_root(const char *path) { | ||
| 278 | + return isempty(path) ? "/" : path; | ||
| 279 | +} | ||
| 280 | + | ||
| 281 | diff --git a/src/basic/selinux-util.c b/src/basic/selinux-util.c | ||
| 282 | index 0c6e99b1d7..bdef7d148b 100644 | ||
| 283 | --- a/src/basic/selinux-util.c | ||
| 284 | +++ b/src/basic/selinux-util.c | ||
| 285 | @@ -34,6 +34,7 @@ | ||
| 286 | #endif | ||
| 287 | |||
| 288 | #include "alloc-util.h" | ||
| 289 | +#include "fd-util.h" | ||
| 290 | #include "log.h" | ||
| 291 | #include "macro.h" | ||
| 292 | #include "path-util.h" | ||
| 293 | @@ -311,48 +312,89 @@ char* mac_selinux_free(char *label) { | ||
| 294 | return NULL; | ||
| 295 | } | ||
| 296 | |||
| 297 | -int mac_selinux_create_file_prepare(const char *path, mode_t mode) { | ||
| 298 | - | ||
| 299 | #if HAVE_SELINUX | ||
| 300 | +static int selinux_create_file_prepare_abspath(const char *abspath, mode_t mode) { | ||
| 301 | _cleanup_freecon_ char *filecon = NULL; | ||
| 302 | + _cleanup_free_ char *path = NULL; | ||
| 303 | int r; | ||
| 304 | |||
| 305 | - assert(path); | ||
| 306 | - | ||
| 307 | - if (!label_hnd) | ||
| 308 | - return 0; | ||
| 309 | - | ||
| 310 | - if (path_is_absolute(path)) | ||
| 311 | - r = selabel_lookup_raw(label_hnd, &filecon, path, mode); | ||
| 312 | - else { | ||
| 313 | - _cleanup_free_ char *newpath = NULL; | ||
| 314 | - | ||
| 315 | - r = path_make_absolute_cwd(path, &newpath); | ||
| 316 | - if (r < 0) | ||
| 317 | - return r; | ||
| 318 | - | ||
| 319 | - r = selabel_lookup_raw(label_hnd, &filecon, newpath, mode); | ||
| 320 | - } | ||
| 321 | + assert(abspath); | ||
| 322 | + assert(path_is_absolute(abspath)); | ||
| 323 | |||
| 324 | + r = selabel_lookup_raw(label_hnd, &filecon, abspath, mode); | ||
| 325 | if (r < 0) { | ||
| 326 | /* No context specified by the policy? Proceed without setting it. */ | ||
| 327 | if (errno == ENOENT) | ||
| 328 | return 0; | ||
| 329 | |||
| 330 | - log_enforcing("Failed to determine SELinux security context for %s: %m", path); | ||
| 331 | + log_enforcing("Failed to determine SELinux security context for %s: %m", abspath); | ||
| 332 | } else { | ||
| 333 | if (setfscreatecon_raw(filecon) >= 0) | ||
| 334 | return 0; /* Success! */ | ||
| 335 | |||
| 336 | - log_enforcing("Failed to set SELinux security context %s for %s: %m", filecon, path); | ||
| 337 | + log_enforcing("Failed to set SELinux security context %s for %s: %m", filecon, abspath); | ||
| 338 | } | ||
| 339 | |||
| 340 | if (security_getenforce() > 0) | ||
| 341 | return -errno; | ||
| 342 | |||
| 343 | -#endif | ||
| 344 | return 0; | ||
| 345 | } | ||
| 346 | +#endif | ||
| 347 | + | ||
| 348 | +int mac_selinux_create_file_prepare_at(int dirfd, const char *path, mode_t mode) { | ||
| 349 | + int r = 0; | ||
| 350 | + | ||
| 351 | +#if HAVE_SELINUX | ||
| 352 | + _cleanup_free_ char *abspath = NULL; | ||
| 353 | + _cleanup_close_ int fd = -1; | ||
| 354 | + | ||
| 355 | + assert(path); | ||
| 356 | + | ||
| 357 | + if (!label_hnd) | ||
| 358 | + return 0; | ||
| 359 | + | ||
| 360 | + if (!path_is_absolute(path)) { | ||
| 361 | + _cleanup_free_ char *p = NULL; | ||
| 362 | + | ||
| 363 | + if (dirfd == AT_FDCWD) | ||
| 364 | + r = safe_getcwd(&p); | ||
| 365 | + else | ||
| 366 | + r = fd_get_path(dirfd, &p); | ||
| 367 | + if (r < 0) | ||
| 368 | + return r; | ||
| 369 | + | ||
| 370 | + abspath = path_join(NULL, p, path); | ||
| 371 | + if (!abspath) | ||
| 372 | + return -ENOMEM; | ||
| 373 | + | ||
| 374 | + path = abspath; | ||
| 375 | + } | ||
| 376 | + | ||
| 377 | + r = selinux_create_file_prepare_abspath(path, mode); | ||
| 378 | +#endif | ||
| 379 | + return r; | ||
| 380 | +} | ||
| 381 | + | ||
| 382 | +int mac_selinux_create_file_prepare(const char *path, mode_t mode) { | ||
| 383 | + int r = 0; | ||
| 384 | + | ||
| 385 | +#if HAVE_SELINUX | ||
| 386 | + _cleanup_free_ char *abspath = NULL; | ||
| 387 | + | ||
| 388 | + assert(path); | ||
| 389 | + | ||
| 390 | + if (!label_hnd) | ||
| 391 | + return 0; | ||
| 392 | + | ||
| 393 | + r = path_make_absolute_cwd(path, &abspath); | ||
| 394 | + if (r < 0) | ||
| 395 | + return r; | ||
| 396 | + | ||
| 397 | + r = selinux_create_file_prepare_abspath(abspath, mode); | ||
| 398 | +#endif | ||
| 399 | + return r; | ||
| 400 | +} | ||
| 401 | |||
| 402 | void mac_selinux_create_file_clear(void) { | ||
| 403 | |||
| 404 | diff --git a/src/basic/selinux-util.h b/src/basic/selinux-util.h | ||
| 405 | index 9780dca81e..84a8bf9729 100644 | ||
| 406 | --- a/src/basic/selinux-util.h | ||
| 407 | +++ b/src/basic/selinux-util.h | ||
| 408 | @@ -41,6 +41,7 @@ int mac_selinux_get_child_mls_label(int socket_fd, const char *exe, const char * | ||
| 409 | char* mac_selinux_free(char *label); | ||
| 410 | |||
| 411 | int mac_selinux_create_file_prepare(const char *path, mode_t mode); | ||
| 412 | +int mac_selinux_create_file_prepare_at(int dirfd, const char *path, mode_t mode); | ||
| 413 | void mac_selinux_create_file_clear(void); | ||
| 414 | |||
| 415 | int mac_selinux_create_socket_prepare(const char *label); | ||
| 416 | diff --git a/src/basic/smack-util.c b/src/basic/smack-util.c | ||
| 417 | index f0018f013f..ea0af3e45f 100644 | ||
| 418 | --- a/src/basic/smack-util.c | ||
| 419 | +++ b/src/basic/smack-util.c | ||
| 420 | @@ -21,18 +21,21 @@ | ||
| 421 | ***/ | ||
| 422 | |||
| 423 | #include <errno.h> | ||
| 424 | +#include <fcntl.h> | ||
| 425 | #include <string.h> | ||
| 426 | #include <sys/stat.h> | ||
| 427 | #include <sys/xattr.h> | ||
| 428 | #include <unistd.h> | ||
| 429 | |||
| 430 | #include "alloc-util.h" | ||
| 431 | +#include "fd-util.h" | ||
| 432 | #include "fileio.h" | ||
| 433 | #include "log.h" | ||
| 434 | #include "macro.h" | ||
| 435 | #include "path-util.h" | ||
| 436 | #include "process-util.h" | ||
| 437 | #include "smack-util.h" | ||
| 438 | +#include "stdio-util.h" | ||
| 439 | #include "string-table.h" | ||
| 440 | #include "xattr-util.h" | ||
| 441 | |||
| 442 | @@ -134,59 +137,111 @@ int mac_smack_apply_pid(pid_t pid, const char *label) { | ||
| 443 | return r; | ||
| 444 | } | ||
| 445 | |||
| 446 | -int mac_smack_fix(const char *path, bool ignore_enoent, bool ignore_erofs) { | ||
| 447 | +static int smack_fix_fd(int fd , const char *abspath, bool ignore_erofs) { | ||
| 448 | + char procfs_path[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int)]; | ||
| 449 | + const char *label; | ||
| 450 | struct stat st; | ||
| 451 | int r; | ||
| 452 | |||
| 453 | - assert(path); | ||
| 454 | + /* The caller should have done the sanity checks. */ | ||
| 455 | + assert(abspath); | ||
| 456 | + assert(path_is_absolute(abspath)); | ||
| 457 | |||
| 458 | - if (!mac_smack_use()) | ||
| 459 | + /* Path must be in /dev. */ | ||
| 460 | + if (!path_startswith(abspath, "/dev")) | ||
| 461 | return 0; | ||
| 462 | |||
| 463 | + if (fstat(fd, &st) < 0) | ||
| 464 | + return -errno; | ||
| 465 | + | ||
| 466 | /* | ||
| 467 | - * Path must be in /dev and must exist | ||
| 468 | + * Label directories and character devices "*". | ||
| 469 | + * Label symlinks "_". | ||
| 470 | + * Don't change anything else. | ||
| 471 | */ | ||
| 472 | - if (!path_startswith(path, "/dev")) | ||
| 473 | + | ||
| 474 | + if (S_ISDIR(st.st_mode)) | ||
| 475 | + label = SMACK_STAR_LABEL; | ||
| 476 | + else if (S_ISLNK(st.st_mode)) | ||
| 477 | + label = SMACK_FLOOR_LABEL; | ||
| 478 | + else if (S_ISCHR(st.st_mode)) | ||
| 479 | + label = SMACK_STAR_LABEL; | ||
| 480 | + else | ||
| 481 | return 0; | ||
| 482 | |||
| 483 | - r = lstat(path, &st); | ||
| 484 | - if (r >= 0) { | ||
| 485 | - const char *label; | ||
| 486 | - | ||
| 487 | - /* | ||
| 488 | - * Label directories and character devices "*". | ||
| 489 | - * Label symlinks "_". | ||
| 490 | - * Don't change anything else. | ||
| 491 | - */ | ||
| 492 | - | ||
| 493 | - if (S_ISDIR(st.st_mode)) | ||
| 494 | - label = SMACK_STAR_LABEL; | ||
| 495 | - else if (S_ISLNK(st.st_mode)) | ||
| 496 | - label = SMACK_FLOOR_LABEL; | ||
| 497 | - else if (S_ISCHR(st.st_mode)) | ||
| 498 | - label = SMACK_STAR_LABEL; | ||
| 499 | - else | ||
| 500 | - return 0; | ||
| 501 | + xsprintf(procfs_path, "/proc/self/fd/%i", fd); | ||
| 502 | + if (setxattr(procfs_path, "security.SMACK64", label, strlen(label), 0) < 0) { | ||
| 503 | + _cleanup_free_ char *old_label = NULL; | ||
| 504 | |||
| 505 | - r = lsetxattr(path, "security.SMACK64", label, strlen(label), 0); | ||
| 506 | + r = -errno; | ||
| 507 | |||
| 508 | /* If the FS doesn't support labels, then exit without warning */ | ||
| 509 | - if (r < 0 && errno == EOPNOTSUPP) | ||
| 510 | + if (r == -EOPNOTSUPP) | ||
| 511 | + return 0; | ||
| 512 | + | ||
| 513 | + /* It the FS is read-only and we were told to ignore failures caused by that, suppress error */ | ||
| 514 | + if (r == -EROFS && ignore_erofs) | ||
| 515 | + return 0; | ||
| 516 | + | ||
| 517 | + /* If the old label is identical to the new one, suppress any kind of error */ | ||
| 518 | + if (getxattr_malloc(procfs_path, "security.SMACK64", &old_label, false) >= 0 && | ||
| 519 | + streq(old_label, label)) | ||
| 520 | return 0; | ||
| 521 | + | ||
| 522 | + return log_debug_errno(r, "Unable to fix SMACK label of %s: %m", abspath); | ||
| 523 | } | ||
| 524 | |||
| 525 | - if (r < 0) { | ||
| 526 | - /* Ignore ENOENT in some cases */ | ||
| 527 | + return r; | ||
| 528 | +} | ||
| 529 | + | ||
| 530 | +int mac_smack_fix_at(int dirfd, const char *path, bool ignore_enoent, bool ignore_erofs) { | ||
| 531 | + _cleanup_free_ char *p = NULL; | ||
| 532 | + _cleanup_close_ int fd = -1; | ||
| 533 | + int r; | ||
| 534 | + | ||
| 535 | + assert(path); | ||
| 536 | + | ||
| 537 | + if (!mac_smack_use()) | ||
| 538 | + return 0; | ||
| 539 | + | ||
| 540 | + fd = openat(dirfd, path, O_NOFOLLOW|O_CLOEXEC|O_PATH); | ||
| 541 | + if (fd < 0) { | ||
| 542 | if (ignore_enoent && errno == ENOENT) | ||
| 543 | return 0; | ||
| 544 | |||
| 545 | - if (ignore_erofs && errno == EROFS) | ||
| 546 | + return -errno; | ||
| 547 | + } | ||
| 548 | + | ||
| 549 | + r = fd_get_path(fd, &p); | ||
| 550 | + if (r < 0) | ||
| 551 | + return r; | ||
| 552 | + | ||
| 553 | + return smack_fix_fd(fd, p, ignore_erofs); | ||
| 554 | +} | ||
| 555 | + | ||
| 556 | +int mac_smack_fix(const char *path, bool ignore_enoent, bool ignore_erofs) { | ||
| 557 | + _cleanup_free_ char *abspath = NULL; | ||
| 558 | + _cleanup_close_ int fd = -1; | ||
| 559 | + int r; | ||
| 560 | + | ||
| 561 | + assert(path); | ||
| 562 | + | ||
| 563 | + if (!mac_smack_use()) | ||
| 564 | + return 0; | ||
| 565 | + | ||
| 566 | + r = path_make_absolute_cwd(path, &abspath); | ||
| 567 | + if (r < 0) | ||
| 568 | + return r; | ||
| 569 | + | ||
| 570 | + fd = open(abspath, O_NOFOLLOW|O_CLOEXEC|O_PATH); | ||
| 571 | + if (fd < 0) { | ||
| 572 | + if (ignore_enoent && errno == ENOENT) | ||
| 573 | return 0; | ||
| 574 | |||
| 575 | - r = log_debug_errno(errno, "Unable to fix SMACK label of %s: %m", path); | ||
| 576 | + return -errno; | ||
| 577 | } | ||
| 578 | |||
| 579 | - return r; | ||
| 580 | + return smack_fix_fd(fd, abspath, ignore_erofs); | ||
| 581 | } | ||
| 582 | |||
| 583 | int mac_smack_copy(const char *dest, const char *src) { | ||
| 584 | @@ -236,6 +291,10 @@ int mac_smack_fix(const char *path, bool ignore_enoent, bool ignore_erofs) { | ||
| 585 | return 0; | ||
| 586 | } | ||
| 587 | |||
| 588 | +int mac_smack_fix_at(int dirfd, const char *path, bool ignore_enoent, bool ignore_erofs) { | ||
| 589 | + return 0; | ||
| 590 | +} | ||
| 591 | + | ||
| 592 | int mac_smack_copy(const char *dest, const char *src) { | ||
| 593 | return 0; | ||
| 594 | } | ||
| 595 | diff --git a/src/basic/smack-util.h b/src/basic/smack-util.h | ||
| 596 | index e4d46d7736..0c214bbbc0 100644 | ||
| 597 | --- a/src/basic/smack-util.h | ||
| 598 | +++ b/src/basic/smack-util.h | ||
| 599 | @@ -44,6 +44,7 @@ typedef enum SmackAttr { | ||
| 600 | bool mac_smack_use(void); | ||
| 601 | |||
| 602 | int mac_smack_fix(const char *path, bool ignore_enoent, bool ignore_erofs); | ||
| 603 | +int mac_smack_fix_at(int dirfd, const char *path, bool ignore_enoent, bool ignore_erofs); | ||
| 604 | |||
| 605 | const char* smack_attr_to_string(SmackAttr i) _const_; | ||
| 606 | SmackAttr smack_attr_from_string(const char *s) _pure_; | ||
| 607 | diff --git a/src/basic/stat-util.c b/src/basic/stat-util.c | ||
| 608 | index 3a54103f1b..801889ae5b 100644 | ||
| 609 | --- a/src/basic/stat-util.c | ||
| 610 | +++ b/src/basic/stat-util.c | ||
| 611 | @@ -63,6 +63,17 @@ int is_dir(const char* path, bool follow) { | ||
| 612 | return !!S_ISDIR(st.st_mode); | ||
| 613 | } | ||
| 614 | |||
| 615 | +int is_dir_fd(int fd) { | ||
| 616 | + struct stat st; | ||
| 617 | + int r; | ||
| 618 | + | ||
| 619 | + r = fstat(fd, &st); | ||
| 620 | + if (r < 0) | ||
| 621 | + return -errno; | ||
| 622 | + | ||
| 623 | + return !!S_ISDIR(st.st_mode); | ||
| 624 | +} | ||
| 625 | + | ||
| 626 | int is_device_node(const char *path) { | ||
| 627 | struct stat info; | ||
| 628 | |||
| 629 | diff --git a/src/basic/stat-util.h b/src/basic/stat-util.h | ||
| 630 | index d8d3c20496..7ea68abfa3 100644 | ||
| 631 | --- a/src/basic/stat-util.h | ||
| 632 | +++ b/src/basic/stat-util.h | ||
| 633 | @@ -31,6 +31,7 @@ | ||
| 634 | |||
| 635 | int is_symlink(const char *path); | ||
| 636 | int is_dir(const char *path, bool follow); | ||
| 637 | +int is_dir_fd(int fd); | ||
| 638 | int is_device_node(const char *path); | ||
| 639 | |||
| 640 | int dir_is_empty(const char *path); | ||
| 641 | diff --git a/src/test/test-fs-util.c b/src/test/test-fs-util.c | ||
| 642 | index 9f3a500080..a76d6d0f8b 100644 | ||
| 643 | --- a/src/test/test-fs-util.c | ||
| 644 | +++ b/src/test/test-fs-util.c | ||
| 645 | @@ -40,6 +40,7 @@ static void test_chase_symlinks(void) { | ||
| 646 | _cleanup_free_ char *result = NULL; | ||
| 647 | char temp[] = "/tmp/test-chase.XXXXXX"; | ||
| 648 | const char *top, *p, *pslash, *q, *qslash; | ||
| 649 | + struct stat st; | ||
| 650 | int r, pfd; | ||
| 651 | |||
| 652 | assert_se(mkdtemp(temp)); | ||
| 653 | @@ -288,6 +289,30 @@ static void test_chase_symlinks(void) { | ||
| 654 | assert_se(sd_id128_equal(a, b)); | ||
| 655 | } | ||
| 656 | |||
| 657 | + /* Test CHASE_NOFOLLOW */ | ||
| 658 | + | ||
| 659 | + p = strjoina(temp, "/target"); | ||
| 660 | + q = strjoina(temp, "/symlink"); | ||
| 661 | + assert_se(symlink(p, q) >= 0); | ||
| 662 | + pfd = chase_symlinks(q, NULL, CHASE_OPEN|CHASE_NOFOLLOW, &result); | ||
| 663 | + assert_se(pfd > 0); | ||
| 664 | + assert_se(path_equal(result, q)); | ||
| 665 | + assert_se(fstat(pfd, &st) >= 0); | ||
| 666 | + assert_se(S_ISLNK(st.st_mode)); | ||
| 667 | + result = mfree(result); | ||
| 668 | + | ||
| 669 | + /* s1 -> s2 -> nonexistent */ | ||
| 670 | + q = strjoina(temp, "/s1"); | ||
| 671 | + assert_se(symlink("s2", q) >= 0); | ||
| 672 | + p = strjoina(temp, "/s2"); | ||
| 673 | + assert_se(symlink("nonexistent", p) >= 0); | ||
| 674 | + pfd = chase_symlinks(q, NULL, CHASE_OPEN|CHASE_NOFOLLOW, &result); | ||
| 675 | + assert_se(pfd > 0); | ||
| 676 | + assert_se(path_equal(result, q)); | ||
| 677 | + assert_se(fstat(pfd, &st) >= 0); | ||
| 678 | + assert_se(S_ISLNK(st.st_mode)); | ||
| 679 | + result = mfree(result); | ||
| 680 | + | ||
| 681 | assert_se(rm_rf(temp, REMOVE_ROOT|REMOVE_PHYSICAL) >= 0); | ||
| 682 | } | ||
| 683 | |||
| 684 | diff --git a/src/tmpfiles/tmpfiles.c b/src/tmpfiles/tmpfiles.c | ||
| 685 | index 613d418eb3..d59ccbaa39 100644 | ||
| 686 | --- a/src/tmpfiles/tmpfiles.c | ||
| 687 | +++ b/src/tmpfiles/tmpfiles.c | ||
| 688 | @@ -794,6 +794,7 @@ static bool hardlink_vulnerable(struct stat *st) { | ||
| 689 | |||
| 690 | static int fd_set_perms(Item *i, int fd, const struct stat *st) { | ||
| 691 | _cleanup_free_ char *path = NULL; | ||
| 692 | + struct stat stbuf; | ||
| 693 | int r; | ||
| 694 | |||
| 695 | assert(i); | ||
| 696 | @@ -806,6 +807,12 @@ static int fd_set_perms(Item *i, int fd, const struct stat *st) { | ||
| 697 | if (!i->mode_set && !i->uid_set && !i->gid_set) | ||
| 698 | goto shortcut; | ||
| 699 | |||
| 700 | + if (!st) { | ||
| 701 | + if (fstat(fd, &stbuf) < 0) | ||
| 702 | + return log_error_errno(errno, "fstat(%s) failed: %m", path); | ||
| 703 | + st = &stbuf; | ||
| 704 | + } | ||
| 705 | + | ||
| 706 | if (hardlink_vulnerable(st)) { | ||
| 707 | log_error("Refusing to set permissions on hardlinked file %s while the fs.protected_hardlinks sysctl is turned off.", path); | ||
| 708 | return -EPERM; | ||
| 709 | @@ -863,32 +870,62 @@ shortcut: | ||
| 710 | return label_fix(path, false, false); | ||
| 711 | } | ||
| 712 | |||
| 713 | -static int path_set_perms(Item *i, const char *path) { | ||
| 714 | - _cleanup_close_ int fd = -1; | ||
| 715 | - struct stat st; | ||
| 716 | +static int path_open_parent_safe(const char *path) { | ||
| 717 | + _cleanup_free_ char *dn = NULL; | ||
| 718 | + int fd; | ||
| 719 | |||
| 720 | - assert(i); | ||
| 721 | - assert(path); | ||
| 722 | + if (path_equal(path, "/") || !path_is_normalized(path)) { | ||
| 723 | + log_error("Failed to open parent of '%s': invalid path.", path); | ||
| 724 | + return -EINVAL; | ||
| 725 | + } | ||
| 726 | |||
| 727 | - fd = open(path, O_NOFOLLOW|O_CLOEXEC|O_PATH); | ||
| 728 | - if (fd < 0) { | ||
| 729 | - int level = LOG_ERR, r = -errno; | ||
| 730 | + dn = dirname_malloc(path); | ||
| 731 | + if (!dn) | ||
| 732 | + return log_oom(); | ||
| 733 | |||
| 734 | - /* Option "e" operates only on existing objects. Do not | ||
| 735 | - * print errors about non-existent files or directories */ | ||
| 736 | - if (i->type == EMPTY_DIRECTORY && errno == ENOENT) { | ||
| 737 | - level = LOG_DEBUG; | ||
| 738 | - r = 0; | ||
| 739 | - } | ||
| 740 | + fd = chase_symlinks(dn, NULL, CHASE_OPEN|CHASE_SAFE, NULL); | ||
| 741 | + if (fd == -EPERM) | ||
| 742 | + return log_error_errno(fd, "Unsafe symlinks encountered in %s, refusing.", path); | ||
| 743 | + if (fd < 0) | ||
| 744 | + return log_error_errno(fd, "Failed to validate path %s: %m", path); | ||
| 745 | |||
| 746 | - log_full_errno(level, errno, "Adjusting owner and mode for %s failed: %m", path); | ||
| 747 | - return r; | ||
| 748 | + return fd; | ||
| 749 | +} | ||
| 750 | + | ||
| 751 | +static int path_open_safe(const char *path) { | ||
| 752 | + int fd; | ||
| 753 | + | ||
| 754 | + /* path_open_safe() returns a file descriptor opened with O_PATH after | ||
| 755 | + * verifying that the path doesn't contain unsafe transitions, except | ||
| 756 | + * for its final component as the function does not follow symlink. */ | ||
| 757 | + | ||
| 758 | + assert(path); | ||
| 759 | + | ||
| 760 | + if (!path_is_normalized(path)) { | ||
| 761 | + log_error("Failed to open invalid path '%s'.", path); | ||
| 762 | + return -EINVAL; | ||
| 763 | } | ||
| 764 | |||
| 765 | - if (fstat(fd, &st) < 0) | ||
| 766 | - return log_error_errno(errno, "Failed to fstat() file %s: %m", path); | ||
| 767 | + fd = chase_symlinks(path, NULL, CHASE_OPEN|CHASE_SAFE|CHASE_NOFOLLOW, NULL); | ||
| 768 | + if (fd == -EPERM) | ||
| 769 | + return log_error_errno(fd, "Unsafe symlinks encountered in %s, refusing.", path); | ||
| 770 | + if (fd < 0) | ||
| 771 | + return log_error_errno(fd, "Failed to validate path %s: %m", path); | ||
| 772 | + | ||
| 773 | + return fd; | ||
| 774 | +} | ||
| 775 | + | ||
| 776 | +static int path_set_perms(Item *i, const char *path) { | ||
| 777 | + _cleanup_close_ int fd = -1; | ||
| 778 | + | ||
| 779 | + assert(i); | ||
| 780 | + assert(path); | ||
| 781 | |||
| 782 | - return fd_set_perms(i, fd, &st); | ||
| 783 | + fd = path_open_safe(path); | ||
| 784 | + if (fd < 0) | ||
| 785 | + return fd; | ||
| 786 | + | ||
| 787 | + return fd_set_perms(i, fd, NULL); | ||
| 788 | } | ||
| 789 | |||
| 790 | static int parse_xattrs_from_arg(Item *i) { | ||
| 791 | @@ -959,9 +996,9 @@ static int path_set_xattrs(Item *i, const char *path) { | ||
| 792 | assert(i); | ||
| 793 | assert(path); | ||
| 794 | |||
| 795 | - fd = open(path, O_CLOEXEC|O_NOFOLLOW|O_PATH); | ||
| 796 | + fd = path_open_safe(path); | ||
| 797 | if (fd < 0) | ||
| 798 | - return log_error_errno(errno, "Cannot open '%s': %m", path); | ||
| 799 | + return fd; | ||
| 800 | |||
| 801 | return fd_set_xattrs(i, fd, NULL); | ||
| 802 | } | ||
| 803 | @@ -1036,15 +1073,21 @@ static int fd_set_acls(Item *item, int fd, const struct stat *st) { | ||
| 804 | #if HAVE_ACL | ||
| 805 | char procfs_path[strlen("/proc/self/fd/") + DECIMAL_STR_MAX(int)]; | ||
| 806 | _cleanup_free_ char *path = NULL; | ||
| 807 | + struct stat stbuf; | ||
| 808 | |||
| 809 | assert(item); | ||
| 810 | assert(fd); | ||
| 811 | - assert(st); | ||
| 812 | |||
| 813 | r = fd_get_path(fd, &path); | ||
| 814 | if (r < 0) | ||
| 815 | return r; | ||
| 816 | |||
| 817 | + if (!st) { | ||
| 818 | + if (fstat(fd, &stbuf) < 0) | ||
| 819 | + return log_error_errno(errno, "fstat(%s) failed: %m", path); | ||
| 820 | + st = &stbuf; | ||
| 821 | + } | ||
| 822 | + | ||
| 823 | if (hardlink_vulnerable(st)) { | ||
| 824 | log_error("Refusing to set ACLs on hardlinked file %s while the fs.protected_hardlinks sysctl is turned off.", path); | ||
| 825 | return -EPERM; | ||
| 826 | @@ -1079,19 +1122,15 @@ static int path_set_acls(Item *item, const char *path) { | ||
| 827 | int r = 0; | ||
| 828 | #ifdef HAVE_ACL | ||
| 829 | _cleanup_close_ int fd = -1; | ||
| 830 | - struct stat st; | ||
| 831 | |||
| 832 | assert(item); | ||
| 833 | assert(path); | ||
| 834 | |||
| 835 | - fd = open(path, O_NOFOLLOW|O_CLOEXEC|O_PATH); | ||
| 836 | + fd = path_open_safe(path); | ||
| 837 | if (fd < 0) | ||
| 838 | - return log_error_errno(errno, "Adjusting ACL of %s failed: %m", path); | ||
| 839 | + return fd; | ||
| 840 | |||
| 841 | - if (fstat(fd, &st) < 0) | ||
| 842 | - return log_error_errno(errno, "Failed to fstat() file %s: %m", path); | ||
| 843 | - | ||
| 844 | - r = fd_set_acls(item, fd, &st); | ||
| 845 | + r = fd_set_acls(item, fd, NULL); | ||
| 846 | #endif | ||
| 847 | return r; | ||
| 848 | } | ||
| 849 | @@ -1199,6 +1238,7 @@ static int fd_set_attribute(Item *item, int fd, const struct stat *st) { | ||
| 850 | char procfs_path[strlen("/proc/self/fd/") + DECIMAL_STR_MAX(int)]; | ||
| 851 | _cleanup_close_ int procfs_fd = -1; | ||
| 852 | _cleanup_free_ char *path = NULL; | ||
| 853 | + struct stat stbuf; | ||
| 854 | unsigned f; | ||
| 855 | int r; | ||
| 856 | |||
| 857 | @@ -1209,6 +1249,12 @@ static int fd_set_attribute(Item *item, int fd, const struct stat *st) { | ||
| 858 | if (r < 0) | ||
| 859 | return r; | ||
| 860 | |||
| 861 | + if (!st) { | ||
| 862 | + if (fstat(fd, &stbuf) < 0) | ||
| 863 | + return log_error_errno(errno, "fstat(%s) failed: %m", path); | ||
| 864 | + st = &stbuf; | ||
| 865 | + } | ||
| 866 | + | ||
| 867 | /* Issuing the file attribute ioctls on device nodes is not | ||
| 868 | * safe, as that will be delivered to the drivers, not the | ||
| 869 | * file system containing the device node. */ | ||
| 870 | @@ -1241,99 +1287,558 @@ static int fd_set_attribute(Item *item, int fd, const struct stat *st) { | ||
| 871 | |||
| 872 | static int path_set_attribute(Item *item, const char *path) { | ||
| 873 | _cleanup_close_ int fd = -1; | ||
| 874 | - struct stat st; | ||
| 875 | |||
| 876 | if (!item->attribute_set || item->attribute_mask == 0) | ||
| 877 | return 0; | ||
| 878 | |||
| 879 | - fd = open(path, O_CLOEXEC|O_NOFOLLOW|O_PATH); | ||
| 880 | + fd = path_open_safe(path); | ||
| 881 | if (fd < 0) | ||
| 882 | - return log_error_errno(errno, "Cannot open '%s': %m", path); | ||
| 883 | - | ||
| 884 | - if (fstat(fd, &st) < 0) | ||
| 885 | - return log_error_errno(errno, "Cannot stat '%s': %m", path); | ||
| 886 | + return fd; | ||
| 887 | |||
| 888 | - return fd_set_attribute(item, fd, &st); | ||
| 889 | + return fd_set_attribute(item, fd, NULL); | ||
| 890 | } | ||
| 891 | |||
| 892 | static int write_one_file(Item *i, const char *path) { | ||
| 893 | - _cleanup_close_ int fd = -1; | ||
| 894 | - int flags, r = 0; | ||
| 895 | - struct stat st; | ||
| 896 | + _cleanup_close_ int fd = -1, dir_fd = -1; | ||
| 897 | + char *bn; | ||
| 898 | + int r; | ||
| 899 | + | ||
| 900 | + assert(i); | ||
| 901 | + assert(path); | ||
| 902 | + assert(i->argument); | ||
| 903 | + assert(i->type == WRITE_FILE); | ||
| 904 | + | ||
| 905 | + /* Validate the path and keep the fd on the directory for opening the | ||
| 906 | + * file so we're sure that it can't be changed behind our back. */ | ||
| 907 | + dir_fd = path_open_parent_safe(path); | ||
| 908 | + if (dir_fd < 0) | ||
| 909 | + return dir_fd; | ||
| 910 | + | ||
| 911 | + bn = basename(path); | ||
| 912 | + | ||
| 913 | + /* Follows symlinks */ | ||
| 914 | + fd = openat(dir_fd, bn, O_NONBLOCK|O_CLOEXEC|O_WRONLY|O_NOCTTY, i->mode); | ||
| 915 | + if (fd < 0) { | ||
| 916 | + if (errno == ENOENT) { | ||
| 917 | + log_debug_errno(errno, "Not writing missing file \"%s\": %m", path); | ||
| 918 | + return 0; | ||
| 919 | + } | ||
| 920 | + return log_error_errno(errno, "Failed to open file \"%s\": %m", path); | ||
| 921 | + } | ||
| 922 | + | ||
| 923 | + /* 'w' is allowed to write into any kind of files. */ | ||
| 924 | + log_debug("Writing to \"%s\".", path); | ||
| 925 | + | ||
| 926 | + r = loop_write(fd, i->argument, strlen(i->argument), false); | ||
| 927 | + if (r < 0) | ||
| 928 | + return log_error_errno(r, "Failed to write file \"%s\": %m", path); | ||
| 929 | + | ||
| 930 | + return fd_set_perms(i, fd, NULL); | ||
| 931 | +} | ||
| 932 | + | ||
| 933 | +static int create_file(Item *i, const char *path) { | ||
| 934 | + _cleanup_close_ int fd = -1, dir_fd = -1; | ||
| 935 | + struct stat stbuf, *st = NULL; | ||
| 936 | + int r = 0; | ||
| 937 | + char *bn; | ||
| 938 | |||
| 939 | assert(i); | ||
| 940 | assert(path); | ||
| 941 | + assert(i->type == CREATE_FILE); | ||
| 942 | + | ||
| 943 | + /* 'f' operates on regular files exclusively. */ | ||
| 944 | |||
| 945 | - flags = i->type == CREATE_FILE ? O_CREAT|O_EXCL|O_NOFOLLOW : | ||
| 946 | - i->type == TRUNCATE_FILE ? O_CREAT|O_TRUNC|O_NOFOLLOW : 0; | ||
| 947 | + /* Validate the path and keep the fd on the directory for opening the | ||
| 948 | + * file so we're sure that it can't be changed behind our back. */ | ||
| 949 | + dir_fd = path_open_parent_safe(path); | ||
| 950 | + if (dir_fd < 0) | ||
| 951 | + return dir_fd; | ||
| 952 | + | ||
| 953 | + bn = basename(path); | ||
| 954 | |||
| 955 | RUN_WITH_UMASK(0000) { | ||
| 956 | mac_selinux_create_file_prepare(path, S_IFREG); | ||
| 957 | - fd = open(path, flags|O_NDELAY|O_CLOEXEC|O_WRONLY|O_NOCTTY, i->mode); | ||
| 958 | + fd = openat(dir_fd, bn, O_CREAT|O_EXCL|O_NOFOLLOW|O_NONBLOCK|O_CLOEXEC|O_WRONLY|O_NOCTTY, i->mode); | ||
| 959 | mac_selinux_create_file_clear(); | ||
| 960 | } | ||
| 961 | |||
| 962 | if (fd < 0) { | ||
| 963 | - if (i->type == WRITE_FILE && errno == ENOENT) { | ||
| 964 | - log_debug_errno(errno, "Not writing missing file \"%s\": %m", path); | ||
| 965 | - return 0; | ||
| 966 | + /* Even on a read-only filesystem, open(2) returns EEXIST if the | ||
| 967 | + * file already exists. It returns EROFS only if it needs to | ||
| 968 | + * create the file. */ | ||
| 969 | + if (errno != EEXIST) | ||
| 970 | + return log_error_errno(errno, "Failed to create file %s: %m", path); | ||
| 971 | + | ||
| 972 | + /* Re-open the file. At that point it must exist since open(2) | ||
| 973 | + * failed with EEXIST. We still need to check if the perms/mode | ||
| 974 | + * need to be changed. For read-only filesystems, we let | ||
| 975 | + * fd_set_perms() report the error if the perms need to be | ||
| 976 | + * modified. */ | ||
| 977 | + fd = openat(dir_fd, bn, O_NOFOLLOW|O_CLOEXEC|O_PATH, i->mode); | ||
| 978 | + if (fd < 0) | ||
| 979 | + return log_error_errno(errno, "Failed to re-open file %s: %m", path); | ||
| 980 | + | ||
| 981 | + if (fstat(fd, &stbuf) < 0) | ||
| 982 | + return log_error_errno(errno, "stat(%s) failed: %m", path); | ||
| 983 | + | ||
| 984 | + if (!S_ISREG(stbuf.st_mode)) { | ||
| 985 | + log_error("%s exists and is not a regular file.", path); | ||
| 986 | + return -EEXIST; | ||
| 987 | } | ||
| 988 | - if (i->type == CREATE_FILE && errno == EEXIST) { | ||
| 989 | - log_debug_errno(errno, "Not writing to pre-existing file \"%s\": %m", path); | ||
| 990 | - goto done; | ||
| 991 | + | ||
| 992 | + st = &stbuf; | ||
| 993 | + } else { | ||
| 994 | + | ||
| 995 | + log_debug("\"%s\" has been created.", path); | ||
| 996 | + | ||
| 997 | + if (i->argument) { | ||
| 998 | + log_debug("Writing to \"%s\".", path); | ||
| 999 | + | ||
| 1000 | + r = loop_write(fd, i->argument, strlen(i->argument), false); | ||
| 1001 | + if (r < 0) | ||
| 1002 | + return log_error_errno(r, "Failed to write file \"%s\": %m", path); | ||
| 1003 | } | ||
| 1004 | + } | ||
| 1005 | |||
| 1006 | - r = -errno; | ||
| 1007 | - if (!i->argument && errno == EROFS && stat(path, &st) == 0 && | ||
| 1008 | - (i->type == CREATE_FILE || st.st_size == 0)) | ||
| 1009 | - goto check_mode; | ||
| 1010 | + return fd_set_perms(i, fd, st); | ||
| 1011 | +} | ||
| 1012 | |||
| 1013 | - return log_error_errno(r, "Failed to create file %s: %m", path); | ||
| 1014 | +static int truncate_file(Item *i, const char *path) { | ||
| 1015 | + _cleanup_close_ int fd = -1, dir_fd = -1; | ||
| 1016 | + struct stat stbuf, *st = NULL; | ||
| 1017 | + bool erofs = false; | ||
| 1018 | + int r = 0; | ||
| 1019 | + char *bn; | ||
| 1020 | + | ||
| 1021 | + assert(i); | ||
| 1022 | + assert(path); | ||
| 1023 | + assert(i->type == TRUNCATE_FILE); | ||
| 1024 | + | ||
| 1025 | + /* We want to operate on regular file exclusively especially since | ||
| 1026 | + * O_TRUNC is unspecified if the file is neither a regular file nor a | ||
| 1027 | + * fifo nor a terminal device. Therefore we first open the file and make | ||
| 1028 | + * sure it's a regular one before truncating it. */ | ||
| 1029 | + | ||
| 1030 | + /* Validate the path and keep the fd on the directory for opening the | ||
| 1031 | + * file so we're sure that it can't be changed behind our back. */ | ||
| 1032 | + dir_fd = path_open_parent_safe(path); | ||
| 1033 | + if (dir_fd < 0) | ||
| 1034 | + return dir_fd; | ||
| 1035 | + | ||
| 1036 | + bn = basename(path); | ||
| 1037 | + | ||
| 1038 | + RUN_WITH_UMASK(0000) { | ||
| 1039 | + mac_selinux_create_file_prepare(path, S_IFREG); | ||
| 1040 | + fd = openat(dir_fd, bn, O_CREAT|O_NOFOLLOW|O_NONBLOCK|O_CLOEXEC|O_WRONLY|O_NOCTTY, i->mode); | ||
| 1041 | + mac_selinux_create_file_clear(); | ||
| 1042 | } | ||
| 1043 | |||
| 1044 | - if (i->argument) { | ||
| 1045 | - log_debug("%s to \"%s\".", i->type == CREATE_FILE ? "Appending" : "Writing", path); | ||
| 1046 | + if (fd < 0) { | ||
| 1047 | + if (errno != EROFS) | ||
| 1048 | + return log_error_errno(errno, "Failed to open/create file %s: %m", path); | ||
| 1049 | |||
| 1050 | - r = loop_write(fd, i->argument, strlen(i->argument), false); | ||
| 1051 | - if (r < 0) | ||
| 1052 | - return log_error_errno(r, "Failed to write file \"%s\": %m", path); | ||
| 1053 | - } else | ||
| 1054 | - log_debug("\"%s\" has been created.", path); | ||
| 1055 | + /* On a read-only filesystem, we don't want to fail if the | ||
| 1056 | + * target is already empty and the perms are set. So we still | ||
| 1057 | + * proceed with the sanity checks and let the remaining | ||
| 1058 | + * operations fail with EROFS if they try to modify the target | ||
| 1059 | + * file. */ | ||
| 1060 | + | ||
| 1061 | + fd = openat(dir_fd, bn, O_NOFOLLOW|O_CLOEXEC|O_PATH, i->mode); | ||
| 1062 | + if (fd < 0) { | ||
| 1063 | + if (errno == ENOENT) { | ||
| 1064 | + log_error("Cannot create file %s on a read-only file system.", path); | ||
| 1065 | + return -EROFS; | ||
| 1066 | + } | ||
| 1067 | |||
| 1068 | - fd = safe_close(fd); | ||
| 1069 | + return log_error_errno(errno, "Failed to re-open file %s: %m", path); | ||
| 1070 | + } | ||
| 1071 | |||
| 1072 | -done: | ||
| 1073 | - if (stat(path, &st) < 0) | ||
| 1074 | + erofs = true; | ||
| 1075 | + } | ||
| 1076 | + | ||
| 1077 | + if (fstat(fd, &stbuf) < 0) | ||
| 1078 | return log_error_errno(errno, "stat(%s) failed: %m", path); | ||
| 1079 | |||
| 1080 | - check_mode: | ||
| 1081 | - if (!S_ISREG(st.st_mode)) { | ||
| 1082 | - log_error("%s is not a file.", path); | ||
| 1083 | + if (!S_ISREG(stbuf.st_mode)) { | ||
| 1084 | + log_error("%s exists and is not a regular file.", path); | ||
| 1085 | return -EEXIST; | ||
| 1086 | } | ||
| 1087 | |||
| 1088 | - r = path_set_perms(i, path); | ||
| 1089 | + if (stbuf.st_size > 0) { | ||
| 1090 | + if (ftruncate(fd, 0) < 0) { | ||
| 1091 | + r = erofs ? -EROFS : -errno; | ||
| 1092 | + return log_error_errno(r, "Failed to truncate file %s: %m", path); | ||
| 1093 | + } | ||
| 1094 | + } else | ||
| 1095 | + st = &stbuf; | ||
| 1096 | + | ||
| 1097 | + log_debug("\"%s\" has been created.", path); | ||
| 1098 | + | ||
| 1099 | + if (i->argument) { | ||
| 1100 | + log_debug("Writing to \"%s\".", path); | ||
| 1101 | + | ||
| 1102 | + r = loop_write(fd, i->argument, strlen(i->argument), false); | ||
| 1103 | + if (r < 0) { | ||
| 1104 | + r = erofs ? -EROFS : r; | ||
| 1105 | + return log_error_errno(r, "Failed to write file %s: %m", path); | ||
| 1106 | + } | ||
| 1107 | + } | ||
| 1108 | + | ||
| 1109 | + return fd_set_perms(i, fd, st); | ||
| 1110 | +} | ||
| 1111 | + | ||
| 1112 | +static int copy_files(Item *i) { | ||
| 1113 | + _cleanup_close_ int dfd = -1, fd = -1; | ||
| 1114 | + char *bn; | ||
| 1115 | + int r; | ||
| 1116 | + | ||
| 1117 | + log_debug("Copying tree \"%s\" to \"%s\".", i->argument, i->path); | ||
| 1118 | + | ||
| 1119 | + bn = basename(i->path); | ||
| 1120 | + | ||
| 1121 | + /* Validate the path and use the returned directory fd for copying the | ||
| 1122 | + * target so we're sure that the path can't be changed behind our | ||
| 1123 | + * back. */ | ||
| 1124 | + dfd = path_open_parent_safe(i->path); | ||
| 1125 | + if (dfd < 0) | ||
| 1126 | + return dfd; | ||
| 1127 | + | ||
| 1128 | + r = copy_tree_at(AT_FDCWD, i->argument, | ||
| 1129 | + dfd, bn, | ||
| 1130 | + i->uid_set ? i->uid : UID_INVALID, | ||
| 1131 | + i->gid_set ? i->gid : GID_INVALID, | ||
| 1132 | + COPY_REFLINK); | ||
| 1133 | + if (r < 0) { | ||
| 1134 | + struct stat a, b; | ||
| 1135 | + | ||
| 1136 | + /* If the target already exists on read-only filesystems, trying | ||
| 1137 | + * to create the target will not fail with EEXIST but with | ||
| 1138 | + * EROFS. */ | ||
| 1139 | + if (r == -EROFS && faccessat(dfd, bn, F_OK, AT_SYMLINK_NOFOLLOW) == 0) | ||
| 1140 | + r = -EEXIST; | ||
| 1141 | + | ||
| 1142 | + if (r != -EEXIST) | ||
| 1143 | + return log_error_errno(r, "Failed to copy files to %s: %m", i->path); | ||
| 1144 | + | ||
| 1145 | + if (stat(i->argument, &a) < 0) | ||
| 1146 | + return log_error_errno(errno, "stat(%s) failed: %m", i->argument); | ||
| 1147 | + | ||
| 1148 | + if (fstatat(dfd, bn, &b, AT_SYMLINK_NOFOLLOW) < 0) | ||
| 1149 | + return log_error_errno(errno, "stat(%s) failed: %m", i->path); | ||
| 1150 | + | ||
| 1151 | + if ((a.st_mode ^ b.st_mode) & S_IFMT) { | ||
| 1152 | + log_debug("Can't copy to %s, file exists already and is of different type", i->path); | ||
| 1153 | + return 0; | ||
| 1154 | + } | ||
| 1155 | + } | ||
| 1156 | + | ||
| 1157 | + fd = openat(dfd, bn, O_NOFOLLOW|O_CLOEXEC|O_PATH); | ||
| 1158 | + if (fd < 0) | ||
| 1159 | + return log_error_errno(errno, "Failed to openat(%s): %m", i->path); | ||
| 1160 | + | ||
| 1161 | + return fd_set_perms(i, fd, NULL); | ||
| 1162 | +} | ||
| 1163 | + | ||
| 1164 | +typedef enum { | ||
| 1165 | + CREATION_NORMAL, | ||
| 1166 | + CREATION_EXISTING, | ||
| 1167 | + CREATION_FORCE, | ||
| 1168 | + _CREATION_MODE_MAX, | ||
| 1169 | + _CREATION_MODE_INVALID = -1 | ||
| 1170 | +} CreationMode; | ||
| 1171 | + | ||
| 1172 | +static const char *creation_mode_verb_table[_CREATION_MODE_MAX] = { | ||
| 1173 | + [CREATION_NORMAL] = "Created", | ||
| 1174 | + [CREATION_EXISTING] = "Found existing", | ||
| 1175 | + [CREATION_FORCE] = "Created replacement", | ||
| 1176 | +}; | ||
| 1177 | + | ||
| 1178 | +DEFINE_PRIVATE_STRING_TABLE_LOOKUP_TO_STRING(creation_mode_verb, CreationMode); | ||
| 1179 | + | ||
| 1180 | +static int create_directory_or_subvolume(const char *path, mode_t mode, bool subvol) { | ||
| 1181 | + _cleanup_close_ int pfd = -1; | ||
| 1182 | + CreationMode creation; | ||
| 1183 | + int r; | ||
| 1184 | + | ||
| 1185 | + assert(path); | ||
| 1186 | + | ||
| 1187 | + pfd = path_open_parent_safe(path); | ||
| 1188 | + if (pfd < 0) | ||
| 1189 | + return pfd; | ||
| 1190 | + | ||
| 1191 | + if (subvol) { | ||
| 1192 | + if (btrfs_is_subvol(empty_to_root(arg_root)) <= 0) | ||
| 1193 | + | ||
| 1194 | + /* Don't create a subvolume unless the root directory is | ||
| 1195 | + * one, too. We do this under the assumption that if the | ||
| 1196 | + * root directory is just a plain directory (i.e. very | ||
| 1197 | + * light-weight), we shouldn't try to split it up into | ||
| 1198 | + * subvolumes (i.e. more heavy-weight). Thus, chroot() | ||
| 1199 | + * environments and suchlike will get a full brtfs | ||
| 1200 | + * subvolume set up below their tree only if they | ||
| 1201 | + * specifically set up a btrfs subvolume for the root | ||
| 1202 | + * dir too. */ | ||
| 1203 | + | ||
| 1204 | + subvol = false; | ||
| 1205 | + else { | ||
| 1206 | + RUN_WITH_UMASK((~mode) & 0777) | ||
| 1207 | + r = btrfs_subvol_make_fd(pfd, basename(path)); | ||
| 1208 | + } | ||
| 1209 | + } else | ||
| 1210 | + r = 0; | ||
| 1211 | + | ||
| 1212 | + if (!subvol || r == -ENOTTY) | ||
| 1213 | + RUN_WITH_UMASK(0000) | ||
| 1214 | + r = mkdirat_label(pfd, basename(path), mode); | ||
| 1215 | + | ||
| 1216 | + if (r < 0) { | ||
| 1217 | + int k; | ||
| 1218 | + | ||
| 1219 | + if (!IN_SET(r, -EEXIST, -EROFS)) | ||
| 1220 | + return log_error_errno(r, "Failed to create directory or subvolume \"%s\": %m", path); | ||
| 1221 | + | ||
| 1222 | + k = is_dir_fd(pfd); | ||
| 1223 | + if (k == -ENOENT && r == -EROFS) | ||
| 1224 | + return log_error_errno(r, "%s does not exist and cannot be created as the file system is read-only.", path); | ||
| 1225 | + if (k < 0) | ||
| 1226 | + return log_error_errno(k, "Failed to check if %s exists: %m", path); | ||
| 1227 | + if (!k) { | ||
| 1228 | + log_warning("\"%s\" already exists and is not a directory.", path); | ||
| 1229 | + return -EEXIST; | ||
| 1230 | + } | ||
| 1231 | + | ||
| 1232 | + creation = CREATION_EXISTING; | ||
| 1233 | + } else | ||
| 1234 | + creation = CREATION_NORMAL; | ||
| 1235 | + | ||
| 1236 | + log_debug("%s directory \"%s\".", creation_mode_verb_to_string(creation), path); | ||
| 1237 | + | ||
| 1238 | + r = openat(pfd, basename(path), O_NOCTTY|O_CLOEXEC|O_DIRECTORY); | ||
| 1239 | if (r < 0) | ||
| 1240 | - return r; | ||
| 1241 | + return -errno; | ||
| 1242 | + return r; | ||
| 1243 | +} | ||
| 1244 | |||
| 1245 | - return 0; | ||
| 1246 | +static int create_directory(Item *i, const char *path) { | ||
| 1247 | + _cleanup_close_ int fd = -1; | ||
| 1248 | + | ||
| 1249 | + assert(i); | ||
| 1250 | + assert(IN_SET(i->type, CREATE_DIRECTORY, TRUNCATE_DIRECTORY)); | ||
| 1251 | + | ||
| 1252 | + fd = create_directory_or_subvolume(path, i->mode, false); | ||
| 1253 | + if (fd == -EEXIST) | ||
| 1254 | + return 0; | ||
| 1255 | + if (fd < 0) | ||
| 1256 | + return fd; | ||
| 1257 | + | ||
| 1258 | + return fd_set_perms(i, fd, NULL); | ||
| 1259 | +} | ||
| 1260 | + | ||
| 1261 | +static int create_subvolume(Item *i, const char *path) { | ||
| 1262 | + _cleanup_close_ int fd = -1; | ||
| 1263 | + int r, q = 0; | ||
| 1264 | + | ||
| 1265 | + assert(i); | ||
| 1266 | + assert(IN_SET(i->type, CREATE_SUBVOLUME, CREATE_SUBVOLUME_NEW_QUOTA, CREATE_SUBVOLUME_INHERIT_QUOTA)); | ||
| 1267 | + | ||
| 1268 | + fd = create_directory_or_subvolume(path, i->mode, true); | ||
| 1269 | + if (fd == -EEXIST) | ||
| 1270 | + return 0; | ||
| 1271 | + if (fd < 0) | ||
| 1272 | + return fd; | ||
| 1273 | + | ||
| 1274 | + if (IN_SET(i->type, CREATE_SUBVOLUME_NEW_QUOTA, CREATE_SUBVOLUME_INHERIT_QUOTA)) { | ||
| 1275 | + r = btrfs_subvol_auto_qgroup_fd(fd, 0, i->type == CREATE_SUBVOLUME_NEW_QUOTA); | ||
| 1276 | + if (r == -ENOTTY) | ||
| 1277 | + log_debug_errno(r, "Couldn't adjust quota for subvolume \"%s\" (unsupported fs or dir not a subvolume): %m", i->path); | ||
| 1278 | + else if (r == -EROFS) | ||
| 1279 | + log_debug_errno(r, "Couldn't adjust quota for subvolume \"%s\" (fs is read-only).", i->path); | ||
| 1280 | + else if (r == -ENOPROTOOPT) | ||
| 1281 | + log_debug_errno(r, "Couldn't adjust quota for subvolume \"%s\" (quota support is disabled).", i->path); | ||
| 1282 | + else if (r < 0) | ||
| 1283 | + q = log_error_errno(r, "Failed to adjust quota for subvolume \"%s\": %m", i->path); | ||
| 1284 | + else if (r > 0) | ||
| 1285 | + log_debug("Adjusted quota for subvolume \"%s\".", i->path); | ||
| 1286 | + else if (r == 0) | ||
| 1287 | + log_debug("Quota for subvolume \"%s\" already in place, no change made.", i->path); | ||
| 1288 | + } | ||
| 1289 | + | ||
| 1290 | + r = fd_set_perms(i, fd, NULL); | ||
| 1291 | + if (q < 0) | ||
| 1292 | + return q; | ||
| 1293 | + | ||
| 1294 | + return r; | ||
| 1295 | +} | ||
| 1296 | + | ||
| 1297 | +static int empty_directory(Item *i, const char *path) { | ||
| 1298 | + int r; | ||
| 1299 | + | ||
| 1300 | + assert(i); | ||
| 1301 | + assert(i->type == EMPTY_DIRECTORY); | ||
| 1302 | + | ||
| 1303 | + r = is_dir(path, false); | ||
| 1304 | + if (r == -ENOENT) { | ||
| 1305 | + /* Option "e" operates only on existing objects. Do not | ||
| 1306 | + * print errors about non-existent files or directories */ | ||
| 1307 | + log_debug("Skipping missing directory: %s", path); | ||
| 1308 | + return 0; | ||
| 1309 | + } | ||
| 1310 | + if (r < 0) | ||
| 1311 | + return log_error_errno(r, "is_dir() failed on path %s: %m", path); | ||
| 1312 | + | ||
| 1313 | + return path_set_perms(i, path); | ||
| 1314 | +} | ||
| 1315 | + | ||
| 1316 | +static int create_device(Item *i, mode_t file_type) { | ||
| 1317 | + _cleanup_close_ int dfd = -1, fd = -1; | ||
| 1318 | + CreationMode creation; | ||
| 1319 | + char *bn; | ||
| 1320 | + int r; | ||
| 1321 | + | ||
| 1322 | + assert(i); | ||
| 1323 | + assert(IN_SET(file_type, S_IFBLK, S_IFCHR)); | ||
| 1324 | + | ||
| 1325 | + bn = basename(i->path); | ||
| 1326 | + | ||
| 1327 | + /* Validate the path and use the returned directory fd for copying the | ||
| 1328 | + * target so we're sure that the path can't be changed behind our | ||
| 1329 | + * back. */ | ||
| 1330 | + dfd = path_open_parent_safe(i->path); | ||
| 1331 | + if (dfd < 0) | ||
| 1332 | + return dfd; | ||
| 1333 | + | ||
| 1334 | + RUN_WITH_UMASK(0000) { | ||
| 1335 | + mac_selinux_create_file_prepare(i->path, file_type); | ||
| 1336 | + r = mknodat(dfd, bn, i->mode | file_type, i->major_minor); | ||
| 1337 | + mac_selinux_create_file_clear(); | ||
| 1338 | + } | ||
| 1339 | + | ||
| 1340 | + if (r < 0) { | ||
| 1341 | + struct stat st; | ||
| 1342 | + | ||
| 1343 | + if (errno == EPERM) { | ||
| 1344 | + log_debug("We lack permissions, possibly because of cgroup configuration; " | ||
| 1345 | + "skipping creation of device node %s.", i->path); | ||
| 1346 | + return 0; | ||
| 1347 | + } | ||
| 1348 | + | ||
| 1349 | + if (errno != EEXIST) | ||
| 1350 | + return log_error_errno(errno, "Failed to create device node %s: %m", i->path); | ||
| 1351 | + | ||
| 1352 | + if (fstatat(dfd, bn, &st, 0) < 0) | ||
| 1353 | + return log_error_errno(errno, "stat(%s) failed: %m", i->path); | ||
| 1354 | + | ||
| 1355 | + if ((st.st_mode & S_IFMT) != file_type) { | ||
| 1356 | + | ||
| 1357 | + if (i->force) { | ||
| 1358 | + | ||
| 1359 | + RUN_WITH_UMASK(0000) { | ||
| 1360 | + mac_selinux_create_file_prepare(i->path, file_type); | ||
| 1361 | + /* FIXME: need to introduce mknodat_atomic() */ | ||
| 1362 | + r = mknod_atomic(i->path, i->mode | file_type, i->major_minor); | ||
| 1363 | + mac_selinux_create_file_clear(); | ||
| 1364 | + } | ||
| 1365 | + | ||
| 1366 | + if (r < 0) | ||
| 1367 | + return log_error_errno(r, "Failed to create device node \"%s\": %m", i->path); | ||
| 1368 | + creation = CREATION_FORCE; | ||
| 1369 | + } else { | ||
| 1370 | + log_debug("%s is not a device node.", i->path); | ||
| 1371 | + return 0; | ||
| 1372 | + } | ||
| 1373 | + } else | ||
| 1374 | + creation = CREATION_EXISTING; | ||
| 1375 | + } else | ||
| 1376 | + creation = CREATION_NORMAL; | ||
| 1377 | + | ||
| 1378 | + log_debug("%s %s device node \"%s\" %u:%u.", | ||
| 1379 | + creation_mode_verb_to_string(creation), | ||
| 1380 | + i->type == CREATE_BLOCK_DEVICE ? "block" : "char", | ||
| 1381 | + i->path, major(i->mode), minor(i->mode)); | ||
| 1382 | + | ||
| 1383 | + fd = openat(dfd, bn, O_NOFOLLOW|O_CLOEXEC|O_PATH); | ||
| 1384 | + if (fd < 0) | ||
| 1385 | + return log_error_errno(errno, "Failed to openat(%s): %m", i->path); | ||
| 1386 | + | ||
| 1387 | + return fd_set_perms(i, fd, NULL); | ||
| 1388 | +} | ||
| 1389 | + | ||
| 1390 | +static int create_fifo(Item *i, const char *path) { | ||
| 1391 | + _cleanup_close_ int pfd = -1, fd = -1; | ||
| 1392 | + CreationMode creation; | ||
| 1393 | + struct stat st; | ||
| 1394 | + char *bn; | ||
| 1395 | + int r; | ||
| 1396 | + | ||
| 1397 | + pfd = path_open_parent_safe(path); | ||
| 1398 | + if (pfd < 0) | ||
| 1399 | + return pfd; | ||
| 1400 | + | ||
| 1401 | + bn = basename(path); | ||
| 1402 | + | ||
| 1403 | + RUN_WITH_UMASK(0000) { | ||
| 1404 | + mac_selinux_create_file_prepare(path, S_IFIFO); | ||
| 1405 | + r = mkfifoat(pfd, bn, i->mode); | ||
| 1406 | + mac_selinux_create_file_clear(); | ||
| 1407 | + } | ||
| 1408 | + | ||
| 1409 | + if (r < 0) { | ||
| 1410 | + if (errno != EEXIST) | ||
| 1411 | + return log_error_errno(errno, "Failed to create fifo %s: %m", path); | ||
| 1412 | + | ||
| 1413 | + if (fstatat(pfd, bn, &st, AT_SYMLINK_NOFOLLOW) < 0) | ||
| 1414 | + return log_error_errno(errno, "stat(%s) failed: %m", path); | ||
| 1415 | + | ||
| 1416 | + if (!S_ISFIFO(st.st_mode)) { | ||
| 1417 | + | ||
| 1418 | + if (i->force) { | ||
| 1419 | + RUN_WITH_UMASK(0000) { | ||
| 1420 | + mac_selinux_create_file_prepare(path, S_IFIFO); | ||
| 1421 | + r = mkfifoat_atomic(pfd, bn, i->mode); | ||
| 1422 | + mac_selinux_create_file_clear(); | ||
| 1423 | + } | ||
| 1424 | + | ||
| 1425 | + if (r < 0) | ||
| 1426 | + return log_error_errno(r, "Failed to create fifo %s: %m", path); | ||
| 1427 | + creation = CREATION_FORCE; | ||
| 1428 | + } else { | ||
| 1429 | + log_warning("\"%s\" already exists and is not a fifo.", path); | ||
| 1430 | + return 0; | ||
| 1431 | + } | ||
| 1432 | + } else | ||
| 1433 | + creation = CREATION_EXISTING; | ||
| 1434 | + } else | ||
| 1435 | + creation = CREATION_NORMAL; | ||
| 1436 | + | ||
| 1437 | + log_debug("%s fifo \"%s\".", creation_mode_verb_to_string(creation), path); | ||
| 1438 | + | ||
| 1439 | + fd = openat(pfd, bn, O_NOFOLLOW|O_CLOEXEC|O_PATH); | ||
| 1440 | + if (fd < 0) | ||
| 1441 | + return log_error_errno(fd, "Failed to openat(%s): %m", path); | ||
| 1442 | + | ||
| 1443 | + return fd_set_perms(i, fd, NULL); | ||
| 1444 | } | ||
| 1445 | |||
| 1446 | typedef int (*action_t)(Item *, const char *); | ||
| 1447 | typedef int (*fdaction_t)(Item *, int fd, const struct stat *st); | ||
| 1448 | |||
| 1449 | -static int item_do(Item *i, int fd, const struct stat *st, fdaction_t action) { | ||
| 1450 | +static int item_do(Item *i, int fd, fdaction_t action) { | ||
| 1451 | + struct stat st; | ||
| 1452 | int r = 0, q; | ||
| 1453 | |||
| 1454 | assert(i); | ||
| 1455 | assert(fd >= 0); | ||
| 1456 | - assert(st); | ||
| 1457 | + | ||
| 1458 | + if (fstat(fd, &st) < 0) { | ||
| 1459 | + r = -errno; | ||
| 1460 | + goto finish; | ||
| 1461 | + } | ||
| 1462 | |||
| 1463 | /* This returns the first error we run into, but nevertheless | ||
| 1464 | * tries to go on */ | ||
| 1465 | - r = action(i, fd, st); | ||
| 1466 | + r = action(i, fd, &st); | ||
| 1467 | |||
| 1468 | - if (S_ISDIR(st->st_mode)) { | ||
| 1469 | + if (S_ISDIR(st.st_mode)) { | ||
| 1470 | char procfs_path[strlen("/proc/self/fd/") + DECIMAL_STR_MAX(int)]; | ||
| 1471 | _cleanup_closedir_ DIR *d = NULL; | ||
| 1472 | struct dirent *de; | ||
| 1473 | @@ -1349,16 +1854,15 @@ static int item_do(Item *i, int fd, const struct stat *st, fdaction_t action) { | ||
| 1474 | } | ||
| 1475 | |||
| 1476 | FOREACH_DIRENT_ALL(de, d, q = -errno; goto finish) { | ||
| 1477 | - struct stat de_st; | ||
| 1478 | int de_fd; | ||
| 1479 | |||
| 1480 | if (dot_or_dot_dot(de->d_name)) | ||
| 1481 | continue; | ||
| 1482 | |||
| 1483 | de_fd = openat(fd, de->d_name, O_NOFOLLOW|O_CLOEXEC|O_PATH); | ||
| 1484 | - if (de_fd >= 0 && fstat(de_fd, &de_st) >= 0) | ||
| 1485 | + if (de_fd >= 0) | ||
| 1486 | /* pass ownership of dirent fd over */ | ||
| 1487 | - q = item_do(i, de_fd, &de_st, action); | ||
| 1488 | + q = item_do(i, de_fd, action); | ||
| 1489 | else | ||
| 1490 | q = -errno; | ||
| 1491 | |||
| 1492 | @@ -1406,7 +1910,6 @@ static int glob_item_recursively(Item *i, fdaction_t action) { | ||
| 1493 | |||
| 1494 | STRV_FOREACH(fn, g.gl_pathv) { | ||
| 1495 | _cleanup_close_ int fd = -1; | ||
| 1496 | - struct stat st; | ||
| 1497 | |||
| 1498 | /* Make sure we won't trigger/follow file object (such as | ||
| 1499 | * device nodes, automounts, ...) pointed out by 'fn' with | ||
| 1500 | @@ -1419,12 +1922,7 @@ static int glob_item_recursively(Item *i, fdaction_t action) { | ||
| 1501 | continue; | ||
| 1502 | } | ||
| 1503 | |||
| 1504 | - if (fstat(fd, &st) < 0) { | ||
| 1505 | - r = r ?: -errno; | ||
| 1506 | - continue; | ||
| 1507 | - } | ||
| 1508 | - | ||
| 1509 | - k = item_do(i, fd, &st, action); | ||
| 1510 | + k = item_do(i, fd, action); | ||
| 1511 | if (k < 0 && r == 0) | ||
| 1512 | r = k; | ||
| 1513 | |||
| 1514 | @@ -1435,27 +1933,9 @@ static int glob_item_recursively(Item *i, fdaction_t action) { | ||
| 1515 | return r; | ||
| 1516 | } | ||
| 1517 | |||
| 1518 | -typedef enum { | ||
| 1519 | - CREATION_NORMAL, | ||
| 1520 | - CREATION_EXISTING, | ||
| 1521 | - CREATION_FORCE, | ||
| 1522 | - _CREATION_MODE_MAX, | ||
| 1523 | - _CREATION_MODE_INVALID = -1 | ||
| 1524 | -} CreationMode; | ||
| 1525 | - | ||
| 1526 | -static const char *creation_mode_verb_table[_CREATION_MODE_MAX] = { | ||
| 1527 | - [CREATION_NORMAL] = "Created", | ||
| 1528 | - [CREATION_EXISTING] = "Found existing", | ||
| 1529 | - [CREATION_FORCE] = "Created replacement", | ||
| 1530 | -}; | ||
| 1531 | - | ||
| 1532 | -DEFINE_PRIVATE_STRING_TABLE_LOOKUP_TO_STRING(creation_mode_verb, CreationMode); | ||
| 1533 | - | ||
| 1534 | static int create_item(Item *i) { | ||
| 1535 | - struct stat st; | ||
| 1536 | - int r = 0; | ||
| 1537 | - int q = 0; | ||
| 1538 | CreationMode creation; | ||
| 1539 | + int r = 0; | ||
| 1540 | |||
| 1541 | assert(i); | ||
| 1542 | |||
| 1543 | @@ -1470,51 +1950,31 @@ static int create_item(Item *i) { | ||
| 1544 | return 0; | ||
| 1545 | |||
| 1546 | case CREATE_FILE: | ||
| 1547 | - case TRUNCATE_FILE: | ||
| 1548 | RUN_WITH_UMASK(0000) | ||
| 1549 | (void) mkdir_parents_label(i->path, 0755); | ||
| 1550 | |||
| 1551 | - r = write_one_file(i, i->path); | ||
| 1552 | + r = create_file(i, i->path); | ||
| 1553 | if (r < 0) | ||
| 1554 | return r; | ||
| 1555 | break; | ||
| 1556 | |||
| 1557 | - case COPY_FILES: { | ||
| 1558 | - | ||
| 1559 | + case TRUNCATE_FILE: | ||
| 1560 | RUN_WITH_UMASK(0000) | ||
| 1561 | (void) mkdir_parents_label(i->path, 0755); | ||
| 1562 | |||
| 1563 | - log_debug("Copying tree \"%s\" to \"%s\".", i->argument, i->path); | ||
| 1564 | - r = copy_tree(i->argument, i->path, | ||
| 1565 | - i->uid_set ? i->uid : UID_INVALID, | ||
| 1566 | - i->gid_set ? i->gid : GID_INVALID, | ||
| 1567 | - COPY_REFLINK); | ||
| 1568 | - | ||
| 1569 | - if (r == -EROFS && stat(i->path, &st) == 0) | ||
| 1570 | - r = -EEXIST; | ||
| 1571 | - | ||
| 1572 | - if (r < 0) { | ||
| 1573 | - struct stat a, b; | ||
| 1574 | - | ||
| 1575 | - if (r != -EEXIST) | ||
| 1576 | - return log_error_errno(r, "Failed to copy files to %s: %m", i->path); | ||
| 1577 | - | ||
| 1578 | - if (stat(i->argument, &a) < 0) | ||
| 1579 | - return log_error_errno(errno, "stat(%s) failed: %m", i->argument); | ||
| 1580 | + r = truncate_file(i, i->path); | ||
| 1581 | + if (r < 0) | ||
| 1582 | + return r; | ||
| 1583 | + break; | ||
| 1584 | |||
| 1585 | - if (stat(i->path, &b) < 0) | ||
| 1586 | - return log_error_errno(errno, "stat(%s) failed: %m", i->path); | ||
| 1587 | + case COPY_FILES: { | ||
| 1588 | |||
| 1589 | - if ((a.st_mode ^ b.st_mode) & S_IFMT) { | ||
| 1590 | - log_debug("Can't copy to %s, file exists already and is of different type", i->path); | ||
| 1591 | - return 0; | ||
| 1592 | - } | ||
| 1593 | - } | ||
| 1594 | + RUN_WITH_UMASK(0000) | ||
| 1595 | + (void) mkdir_parents_label(i->path, 0755); | ||
| 1596 | |||
| 1597 | - r = path_set_perms(i, i->path); | ||
| 1598 | + r = copy_files(i); | ||
| 1599 | if (r < 0) | ||
| 1600 | return r; | ||
| 1601 | - | ||
| 1602 | break; | ||
| 1603 | |||
| 1604 | case WRITE_FILE: | ||
| 1605 | @@ -1526,132 +1986,39 @@ static int create_item(Item *i) { | ||
| 1606 | |||
| 1607 | case CREATE_DIRECTORY: | ||
| 1608 | case TRUNCATE_DIRECTORY: | ||
| 1609 | + RUN_WITH_UMASK(0000) | ||
| 1610 | + (void) mkdir_parents_label(i->path, 0755); | ||
| 1611 | + | ||
| 1612 | + r = create_directory(i, i->path); | ||
| 1613 | + if (r < 0) | ||
| 1614 | + return r; | ||
| 1615 | + break; | ||
| 1616 | + | ||
| 1617 | case CREATE_SUBVOLUME: | ||
| 1618 | case CREATE_SUBVOLUME_INHERIT_QUOTA: | ||
| 1619 | case CREATE_SUBVOLUME_NEW_QUOTA: | ||
| 1620 | RUN_WITH_UMASK(0000) | ||
| 1621 | (void) mkdir_parents_label(i->path, 0755); | ||
| 1622 | |||
| 1623 | - if (IN_SET(i->type, CREATE_SUBVOLUME, CREATE_SUBVOLUME_INHERIT_QUOTA, CREATE_SUBVOLUME_NEW_QUOTA)) { | ||
| 1624 | - | ||
| 1625 | - if (btrfs_is_subvol(isempty(arg_root) ? "/" : arg_root) <= 0) | ||
| 1626 | - | ||
| 1627 | - /* Don't create a subvolume unless the | ||
| 1628 | - * root directory is one, too. We do | ||
| 1629 | - * this under the assumption that if | ||
| 1630 | - * the root directory is just a plain | ||
| 1631 | - * directory (i.e. very light-weight), | ||
| 1632 | - * we shouldn't try to split it up | ||
| 1633 | - * into subvolumes (i.e. more | ||
| 1634 | - * heavy-weight). Thus, chroot() | ||
| 1635 | - * environments and suchlike will get | ||
| 1636 | - * a full brtfs subvolume set up below | ||
| 1637 | - * their tree only if they | ||
| 1638 | - * specifically set up a btrfs | ||
| 1639 | - * subvolume for the root dir too. */ | ||
| 1640 | - | ||
| 1641 | - r = -ENOTTY; | ||
| 1642 | - else { | ||
| 1643 | - RUN_WITH_UMASK((~i->mode) & 0777) | ||
| 1644 | - r = btrfs_subvol_make(i->path); | ||
| 1645 | - } | ||
| 1646 | - } else | ||
| 1647 | - r = 0; | ||
| 1648 | - | ||
| 1649 | - if (IN_SET(i->type, CREATE_DIRECTORY, TRUNCATE_DIRECTORY) || r == -ENOTTY) | ||
| 1650 | - RUN_WITH_UMASK(0000) | ||
| 1651 | - r = mkdir_label(i->path, i->mode); | ||
| 1652 | - | ||
| 1653 | - if (r < 0) { | ||
| 1654 | - int k; | ||
| 1655 | - | ||
| 1656 | - if (!IN_SET(r, -EEXIST, -EROFS)) | ||
| 1657 | - return log_error_errno(r, "Failed to create directory or subvolume \"%s\": %m", i->path); | ||
| 1658 | - | ||
| 1659 | - k = is_dir(i->path, false); | ||
| 1660 | - if (k == -ENOENT && r == -EROFS) | ||
| 1661 | - return log_error_errno(r, "%s does not exist and cannot be created as the file system is read-only.", i->path); | ||
| 1662 | - if (k < 0) | ||
| 1663 | - return log_error_errno(k, "Failed to check if %s exists: %m", i->path); | ||
| 1664 | - if (!k) { | ||
| 1665 | - log_warning("\"%s\" already exists and is not a directory.", i->path); | ||
| 1666 | - return 0; | ||
| 1667 | - } | ||
| 1668 | - | ||
| 1669 | - creation = CREATION_EXISTING; | ||
| 1670 | - } else | ||
| 1671 | - creation = CREATION_NORMAL; | ||
| 1672 | - | ||
| 1673 | - log_debug("%s directory \"%s\".", creation_mode_verb_to_string(creation), i->path); | ||
| 1674 | - | ||
| 1675 | - if (IN_SET(i->type, CREATE_SUBVOLUME_NEW_QUOTA, CREATE_SUBVOLUME_INHERIT_QUOTA)) { | ||
| 1676 | - r = btrfs_subvol_auto_qgroup(i->path, 0, i->type == CREATE_SUBVOLUME_NEW_QUOTA); | ||
| 1677 | - if (r == -ENOTTY) | ||
| 1678 | - log_debug_errno(r, "Couldn't adjust quota for subvolume \"%s\" (unsupported fs or dir not a subvolume): %m", i->path); | ||
| 1679 | - else if (r == -EROFS) | ||
| 1680 | - log_debug_errno(r, "Couldn't adjust quota for subvolume \"%s\" (fs is read-only).", i->path); | ||
| 1681 | - else if (r == -ENOPROTOOPT) | ||
| 1682 | - log_debug_errno(r, "Couldn't adjust quota for subvolume \"%s\" (quota support is disabled).", i->path); | ||
| 1683 | - else if (r < 0) | ||
| 1684 | - q = log_error_errno(r, "Failed to adjust quota for subvolume \"%s\": %m", i->path); | ||
| 1685 | - else if (r > 0) | ||
| 1686 | - log_debug("Adjusted quota for subvolume \"%s\".", i->path); | ||
| 1687 | - else if (r == 0) | ||
| 1688 | - log_debug("Quota for subvolume \"%s\" already in place, no change made.", i->path); | ||
| 1689 | - } | ||
| 1690 | + r = create_subvolume(i, i->path); | ||
| 1691 | + if (r < 0) | ||
| 1692 | + return r; | ||
| 1693 | + break; | ||
| 1694 | |||
| 1695 | - _fallthrough_; | ||
| 1696 | case EMPTY_DIRECTORY: | ||
| 1697 | - r = path_set_perms(i, i->path); | ||
| 1698 | - if (q < 0) | ||
| 1699 | - return q; | ||
| 1700 | + r = empty_directory(i, i->path); | ||
| 1701 | if (r < 0) | ||
| 1702 | return r; | ||
| 1703 | |||
| 1704 | break; | ||
| 1705 | |||
| 1706 | case CREATE_FIFO: | ||
| 1707 | - RUN_WITH_UMASK(0000) { | ||
| 1708 | + RUN_WITH_UMASK(0000) | ||
| 1709 | (void) mkdir_parents_label(i->path, 0755); | ||
| 1710 | |||
| 1711 | - mac_selinux_create_file_prepare(i->path, S_IFIFO); | ||
| 1712 | - r = mkfifo(i->path, i->mode); | ||
| 1713 | - mac_selinux_create_file_clear(); | ||
| 1714 | - } | ||
| 1715 | - | ||
| 1716 | - if (r < 0) { | ||
| 1717 | - if (errno != EEXIST) | ||
| 1718 | - return log_error_errno(errno, "Failed to create fifo %s: %m", i->path); | ||
| 1719 | - | ||
| 1720 | - if (lstat(i->path, &st) < 0) | ||
| 1721 | - return log_error_errno(errno, "stat(%s) failed: %m", i->path); | ||
| 1722 | - | ||
| 1723 | - if (!S_ISFIFO(st.st_mode)) { | ||
| 1724 | - | ||
| 1725 | - if (i->force) { | ||
| 1726 | - RUN_WITH_UMASK(0000) { | ||
| 1727 | - mac_selinux_create_file_prepare(i->path, S_IFIFO); | ||
| 1728 | - r = mkfifo_atomic(i->path, i->mode); | ||
| 1729 | - mac_selinux_create_file_clear(); | ||
| 1730 | - } | ||
| 1731 | - | ||
| 1732 | - if (r < 0) | ||
| 1733 | - return log_error_errno(r, "Failed to create fifo %s: %m", i->path); | ||
| 1734 | - creation = CREATION_FORCE; | ||
| 1735 | - } else { | ||
| 1736 | - log_warning("\"%s\" already exists and is not a fifo.", i->path); | ||
| 1737 | - return 0; | ||
| 1738 | - } | ||
| 1739 | - } else | ||
| 1740 | - creation = CREATION_EXISTING; | ||
| 1741 | - } else | ||
| 1742 | - creation = CREATION_NORMAL; | ||
| 1743 | - log_debug("%s fifo \"%s\".", creation_mode_verb_to_string(creation), i->path); | ||
| 1744 | - | ||
| 1745 | - r = path_set_perms(i, i->path); | ||
| 1746 | + r = create_fifo(i, i->path); | ||
| 1747 | if (r < 0) | ||
| 1748 | return r; | ||
| 1749 | - | ||
| 1750 | break; | ||
| 1751 | } | ||
| 1752 | |||
| 1753 | @@ -1704,9 +2071,7 @@ static int create_item(Item *i) { | ||
| 1754 | } | ||
| 1755 | |||
| 1756 | case CREATE_BLOCK_DEVICE: | ||
| 1757 | - case CREATE_CHAR_DEVICE: { | ||
| 1758 | - mode_t file_type; | ||
| 1759 | - | ||
| 1760 | + case CREATE_CHAR_DEVICE: | ||
| 1761 | if (have_effective_cap(CAP_MKNOD) == 0) { | ||
| 1762 | /* In a container we lack CAP_MKNOD. We | ||
| 1763 | shouldn't attempt to create the device node in | ||
| 1764 | @@ -1720,60 +2085,11 @@ static int create_item(Item *i) { | ||
| 1765 | RUN_WITH_UMASK(0000) | ||
| 1766 | (void) mkdir_parents_label(i->path, 0755); | ||
| 1767 | |||
| 1768 | - file_type = i->type == CREATE_BLOCK_DEVICE ? S_IFBLK : S_IFCHR; | ||
| 1769 | - | ||
| 1770 | - RUN_WITH_UMASK(0000) { | ||
| 1771 | - mac_selinux_create_file_prepare(i->path, file_type); | ||
| 1772 | - r = mknod(i->path, i->mode | file_type, i->major_minor); | ||
| 1773 | - mac_selinux_create_file_clear(); | ||
| 1774 | - } | ||
| 1775 | - | ||
| 1776 | - if (r < 0) { | ||
| 1777 | - if (errno == EPERM) { | ||
| 1778 | - log_debug("We lack permissions, possibly because of cgroup configuration; " | ||
| 1779 | - "skipping creation of device node %s.", i->path); | ||
| 1780 | - return 0; | ||
| 1781 | - } | ||
| 1782 | - | ||
| 1783 | - if (errno != EEXIST) | ||
| 1784 | - return log_error_errno(errno, "Failed to create device node %s: %m", i->path); | ||
| 1785 | - | ||
| 1786 | - if (lstat(i->path, &st) < 0) | ||
| 1787 | - return log_error_errno(errno, "stat(%s) failed: %m", i->path); | ||
| 1788 | - | ||
| 1789 | - if ((st.st_mode & S_IFMT) != file_type) { | ||
| 1790 | - | ||
| 1791 | - if (i->force) { | ||
| 1792 | - | ||
| 1793 | - RUN_WITH_UMASK(0000) { | ||
| 1794 | - mac_selinux_create_file_prepare(i->path, file_type); | ||
| 1795 | - r = mknod_atomic(i->path, i->mode | file_type, i->major_minor); | ||
| 1796 | - mac_selinux_create_file_clear(); | ||
| 1797 | - } | ||
| 1798 | - | ||
| 1799 | - if (r < 0) | ||
| 1800 | - return log_error_errno(r, "Failed to create device node \"%s\": %m", i->path); | ||
| 1801 | - creation = CREATION_FORCE; | ||
| 1802 | - } else { | ||
| 1803 | - log_debug("%s is not a device node.", i->path); | ||
| 1804 | - return 0; | ||
| 1805 | - } | ||
| 1806 | - } else | ||
| 1807 | - creation = CREATION_EXISTING; | ||
| 1808 | - } else | ||
| 1809 | - creation = CREATION_NORMAL; | ||
| 1810 | - | ||
| 1811 | - log_debug("%s %s device node \"%s\" %u:%u.", | ||
| 1812 | - creation_mode_verb_to_string(creation), | ||
| 1813 | - i->type == CREATE_BLOCK_DEVICE ? "block" : "char", | ||
| 1814 | - i->path, major(i->mode), minor(i->mode)); | ||
| 1815 | - | ||
| 1816 | - r = path_set_perms(i, i->path); | ||
| 1817 | + r = create_device(i, i->type == CREATE_BLOCK_DEVICE ? S_IFBLK : S_IFCHR); | ||
| 1818 | if (r < 0) | ||
| 1819 | return r; | ||
| 1820 | |||
| 1821 | break; | ||
| 1822 | - } | ||
| 1823 | |||
| 1824 | case ADJUST_MODE: | ||
| 1825 | case RELABEL_PATH: | ||
| 1826 | -- | ||
| 1827 | 2.11.0 | ||
| 1828 | |||
diff --git a/meta/recipes-core/systemd/systemd_237.bb b/meta/recipes-core/systemd/systemd_237.bb index 96f419a7f9..bc33fbebdc 100644 --- a/meta/recipes-core/systemd/systemd_237.bb +++ b/meta/recipes-core/systemd/systemd_237.bb | |||
| @@ -61,6 +61,8 @@ SRC_URI += "file://touchscreen.rules \ | |||
| 61 | file://0025-journald-set-a-limit-on-the-number-of-fields-1k.patch \ | 61 | file://0025-journald-set-a-limit-on-the-number-of-fields-1k.patch \ |
| 62 | file://0026-journal-remote-set-a-limit-on-the-number-of-fields-i.patch \ | 62 | file://0026-journal-remote-set-a-limit-on-the-number-of-fields-i.patch \ |
| 63 | file://0027-journal-fix-out-of-bounds-read-CVE-2018-16866.patch \ | 63 | file://0027-journal-fix-out-of-bounds-read-CVE-2018-16866.patch \ |
| 64 | file://0001-tmpfiles-don-t-resolve-pathnames-when-traversing-rec.patch \ | ||
| 65 | file://0002-Make-tmpfiles-safe.patch \ | ||
| 64 | " | 66 | " |
| 65 | SRC_URI_append_qemuall = " file://0001-core-device.c-Change-the-default-device-timeout-to-2.patch" | 67 | SRC_URI_append_qemuall = " file://0001-core-device.c-Change-the-default-device-timeout-to-2.patch" |
| 66 | 68 | ||
