Merge pull request #12869 from poettering/dynamic-user-re-migrate

DynamicUser=1 state directory back migration
This commit is contained in:
Lennart Poettering
2019-06-25 10:06:03 +02:00
committed by GitHub
+54 -19
View File
@@ -2099,27 +2099,30 @@ static int setup_exec_directory(
(type == EXEC_DIRECTORY_RUNTIME && context->runtime_directory_preserve_mode != EXEC_PRESERVE_NO))) {
_cleanup_free_ char *private_root = NULL;
/* So, here's one extra complication when dealing with DynamicUser=1 units. In that case we
* want to avoid leaving a directory around fully accessible that is owned by a dynamic user
* whose UID is later on reused. To lock this down we use the same trick used by container
* managers to prohibit host users to get access to files of the same UID in containers: we
* place everything inside a directory that has an access mode of 0700 and is owned root:root,
* so that it acts as security boundary for unprivileged host code. We then use fs namespacing
* to make this directory permeable for the service itself.
/* So, here's one extra complication when dealing with DynamicUser=1 units. In that
* case we want to avoid leaving a directory around fully accessible that is owned by
* a dynamic user whose UID is later on reused. To lock this down we use the same
* trick used by container managers to prohibit host users to get access to files of
* the same UID in containers: we place everything inside a directory that has an
* access mode of 0700 and is owned root:root, so that it acts as security boundary
* for unprivileged host code. We then use fs namespacing to make this directory
* permeable for the service itself.
*
* Specifically: for a service which wants a special directory "foo/" we first create a
* directory "private/" with access mode 0700 owned by root:root. Then we place "foo" inside of
* that directory (i.e. "private/foo/"), and make "foo" a symlink to "private/foo". This way,
* privileged host users can access "foo/" as usual, but unprivileged host users can't look
* into it. Inside of the namespaceof the container "private/" is replaced by a more liberally
* accessible tmpfs, into which the host's "private/foo/" is mounted under the same name, thus
* disabling the access boundary for the service and making sure it only gets access to the
* dirs it needs but no others. Tricky? Yes, absolutely, but it works!
* Specifically: for a service which wants a special directory "foo/" we first create
* a directory "private/" with access mode 0700 owned by root:root. Then we place
* "foo" inside of that directory (i.e. "private/foo/"), and make "foo" a symlink to
* "private/foo". This way, privileged host users can access "foo/" as usual, but
* unprivileged host users can't look into it. Inside of the namespace of the unit
* "private/" is replaced by a more liberally accessible tmpfs, into which the host's
* "private/foo/" is mounted under the same name, thus disabling the access boundary
* for the service and making sure it only gets access to the dirs it needs but no
* others. Tricky? Yes, absolutely, but it works!
*
* Note that we don't do this for EXEC_DIRECTORY_CONFIGURATION as that's assumed not to be
* owned by the service itself.
* Also, note that we don't do this for EXEC_DIRECTORY_RUNTIME as that's often used for sharing
* files or sockets with other services. */
* Note that we don't do this for EXEC_DIRECTORY_CONFIGURATION as that's assumed not
* to be owned by the service itself.
*
* Also, note that we don't do this for EXEC_DIRECTORY_RUNTIME as that's often used
* for sharing files or sockets with other services. */
private_root = path_join(params->prefix[type], "private");
if (!private_root) {
@@ -2168,6 +2171,38 @@ static int setup_exec_directory(
goto fail;
} else {
_cleanup_free_ char *target = NULL;
if (type != EXEC_DIRECTORY_CONFIGURATION &&
readlink_and_make_absolute(p, &target) >= 0) {
_cleanup_free_ char *q = NULL;
/* This already exists and is a symlink? Interesting. Maybe it's one created
* by DynamicUser=1 (see above)? */
q = path_join(params->prefix[type], "private", *rt);
if (!q) {
r = -ENOMEM;
goto fail;
}
if (path_equal(q, target)) {
/* Hmm, apparently DynamicUser= was once turned on for this service,
* but is no longer. Let's move the directory back up. */
if (unlink(p) < 0) {
r = -errno;
goto fail;
}
if (rename(q, p) < 0) {
r = -errno;
goto fail;
}
}
}
r = mkdir_label(p, context->directories[type].mode);
if (r < 0) {
if (r != -EEXIST)