Commit 5bdc1623 authored by Andrew Vagin's avatar Andrew Vagin Committed by Pavel Emelyanov

files: try to change fsuid only if linkat() failed

We found that linkat for "unsafe" files doesn't work in userns
if a file uid isn't equal to the currect fsuid. This issue was
fixed by changing fsuid before calling linkat. But in this
case we are not able to createa link if a target directory doesn't
have write premissions.

Starting with the 4.3 kernel, it's possible to create links of
"unsafe files":

f2ca379642d7 ("namei: permit linking with CAP_FOWNER in userns")

So we can try to call linkat() without changing fsuid and make one
more attempt with changing fsuid if the first one failed with EPERM.
Signed-off-by: 's avatarAndrew Vagin <avagin@virtuozzo.com>
Signed-off-by: 's avatarPavel Emelyanov <xemul@virtuozzo.com>
parent 55597274
......@@ -1207,49 +1207,53 @@ static int linkat_hard(int odir, char *opath, int ndir, char *npath, uid_t owner
int ret, old_fsuid = -1;
int errno_save;
if (root_ns_mask & CLONE_NEWUSER)
/*
* Kernel has strange secutiry restrictions about
* linkat. If the fsuid of the caller doesn't equals
* the uid of the file and the file is not "safe"
* one, then only global CAP_CHOWN will be allowed
* to link().
*
* Next, when we're in user namespace we're ns root,
* but not global CAP_CHOWN. Thus, even though we
* ARE ns root, we will not be allowed to link() at
* files that belong to regular users %)
*
* Fortunately, the setfsuid() requires ns-level
* CAP_SETUID which we have.
*/
ret = linkat(odir, opath, ndir, npath, 0);
if (ret < 0)
pr_perror("Can't link %s -> %s", opath, npath);
if (ret == 0 || errno != EPERM || !(root_ns_mask & CLONE_NEWUSER))
return ret;
/*
* Kernel before 4.3 has strange secutiry restrictions about
* linkat. If the fsuid of the caller doesn't equals
* the uid of the file and the file is not "safe"
* one, then only global CAP_CHOWN will be allowed
* to link().
*
* Next, when we're in user namespace we're ns root,
* but not global CAP_CHOWN. Thus, even though we
* ARE ns root, we will not be allowed to link() at
* files that belong to regular users %)
*
* Fortunately, the setfsuid() requires ns-level
* CAP_SETUID which we have.
*/
old_fsuid = setfsuid(owner);
old_fsuid = setfsuid(owner);
ret = linkat(odir, opath, ndir, npath, 0);
errno_save = errno;
if (ret < 0)
pr_perror("Can't link %s -> %s", opath, npath);
if (root_ns_mask & CLONE_NEWUSER) {
setfsuid(old_fsuid);
if (setfsuid(-1) != old_fsuid) {
pr_warn("Failed to restore old fsuid!\n");
/*
* Don't fail here. We still have chances to run till
* the pie/restorer, and if _this_ guy fails to set
* the proper fsuid, then we'll abort the restore.
*/
}
setfsuid(old_fsuid);
if (setfsuid(-1) != old_fsuid) {
pr_warn("Failed to restore old fsuid!\n");
/*
* Restoring PR_SET_DUMPABLE flag is required after setfsuid,
* as if it not set, proc inode will be created with root cred
* (see proc_pid_make_inode), which will result in permission
* check fail when trying to access files in /proc/self/
* Don't fail here. We still have chances to run till
* the pie/restorer, and if _this_ guy fails to set
* the proper fsuid, then we'll abort the restore.
*/
prctl(PR_SET_DUMPABLE, 1, 0);
}
/*
* Restoring PR_SET_DUMPABLE flag is required after setfsuid,
* as if it not set, proc inode will be created with root cred
* (see proc_pid_make_inode), which will result in permission
* check fail when trying to access files in /proc/self/
*/
prctl(PR_SET_DUMPABLE, 1, 0);
errno = errno_save;
return ret;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment