namei: permit linking with CAP_FOWNER in userns
authorDirk Steinmetz <>
Tue, 20 Oct 2015 14:09:19 +0000 (16:09 +0200)
committerEric W. Biederman <>
Tue, 27 Oct 2015 21:12:35 +0000 (16:12 -0500)
Attempting to hardlink to an unsafe file (e.g. a setuid binary) from
within an unprivileged user namespace fails, even if CAP_FOWNER is held
within the namespace. This may cause various failures, such as a gentoo
installation within a lxc container failing to build and install specific

This change permits hardlinking of files owned by mapped uids, if
CAP_FOWNER is held for that namespace. Furthermore, it improves consistency
by using the existing inode_owner_or_capable(), which is aware of
namespaced capabilities as of 23adbe12ef7d3 ("fs,userns: Change
inode_capable to capable_wrt_inode_uidgid").

Signed-off-by: Dirk Steinmetz <>
This is hitting us in Ubuntu during some dpkg upgrades in containers.
When upgrading a file dpkg creates a hard link to the old file to back
it up before overwriting it. When packages upgrade suid files owned by a
non-root user the link isn't permitted, and the package upgrade fails.
This patch fixes our problem.

Tested-by: Seth Forshee <>
Signed-off-by: Eric W. Biederman <>

index 726d211..29fc6a6 100644 (file)
@@ -955,26 +955,23 @@ static bool safe_hardlink_source(struct inode *inode)
  *  - sysctl_protected_hardlinks enabled
  *  - fsuid does not match inode
  *  - hardlink source is unsafe (see safe_hardlink_source() above)
- *  - not CAP_FOWNER
+ *  - not CAP_FOWNER in a namespace with the inode owner uid mapped
  * Returns 0 if successful, -ve on error.
 static int may_linkat(struct path *link)
-       const struct cred *cred;
        struct inode *inode;
        if (!sysctl_protected_hardlinks)
                return 0;
-       cred = current_cred();
        inode = link->dentry->d_inode;
        /* Source inode owner (or CAP_FOWNER) can hardlink all they like,
         * otherwise, it must be a safe source.
-       if (uid_eq(cred->fsuid, inode->i_uid) || safe_hardlink_source(inode) ||
-           capable(CAP_FOWNER))
+       if (inode_owner_or_capable(inode) || safe_hardlink_source(inode))
                return 0;
        audit_log_link_denied("linkat", link);