113 lines
3.1 KiB
Diff
113 lines
3.1 KiB
Diff
From 52db90d0fa770e2277645eb34956820cec26b2cb Mon Sep 17 00:00:00 2001
|
|
From: Kees Cook <keescook@chromium.org>
|
|
Date: Sat, 25 Feb 2012 12:28:44 +1100
|
|
Subject: [PATCH 5/5] fs: hardlink creation restriction cleanup
|
|
|
|
Clean-up of hardlink restriction logic, as suggested by Andrew Morton.
|
|
|
|
Signed-off-by: Kees Cook <keescook@chromium.org>
|
|
Cc: Ingo Molnar <mingo@elte.hu>
|
|
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
|
|
---
|
|
fs/namei.c | 62 ++++++++++++++++++++++++++++++++++++++++++-----------------
|
|
1 files changed, 44 insertions(+), 18 deletions(-)
|
|
|
|
diff --git a/fs/namei.c b/fs/namei.c
|
|
index fe13533..1436fae 100644
|
|
--- a/fs/namei.c
|
|
+++ b/fs/namei.c
|
|
@@ -693,46 +693,72 @@ static inline int may_follow_link(struct path *link)
|
|
}
|
|
|
|
/**
|
|
+ * safe_hardlink_source - Check for safe hardlink conditions
|
|
+ * @inode: the source inode to hardlink from
|
|
+ *
|
|
+ * Return false if at least one of the following conditions:
|
|
+ * - inode is not a regular file
|
|
+ * - inode is setuid
|
|
+ * - inode is setgid and group-exec
|
|
+ * - access failure for read and write
|
|
+ *
|
|
+ * Otherwise returns true.
|
|
+ */
|
|
+static bool safe_hardlink_source(struct inode *inode)
|
|
+{
|
|
+ mode_t mode = inode->i_mode;
|
|
+
|
|
+ /* Special files should not get pinned to the filesystem. */
|
|
+ if (!S_ISREG(mode))
|
|
+ return false;
|
|
+
|
|
+ /* Setuid files should not get pinned to the filesystem. */
|
|
+ if (mode & S_ISUID)
|
|
+ return false;
|
|
+
|
|
+ /* Executable setgid files should not get pinned to the filesystem. */
|
|
+ if ((mode & (S_ISGID | S_IXGRP)) == (S_ISGID | S_IXGRP))
|
|
+ return false;
|
|
+
|
|
+ /* Hardlinking to unreadable or unwritable sources is dangerous. */
|
|
+ if (inode_permission(inode, MAY_READ | MAY_WRITE))
|
|
+ return false;
|
|
+
|
|
+ return true;
|
|
+}
|
|
+
|
|
+/**
|
|
* may_linkat - Check permissions for creating a hardlink
|
|
* @link: the source to hardlink from
|
|
*
|
|
* Block hardlink when all of:
|
|
* - sysctl_protected_hardlinks enabled
|
|
* - fsuid does not match inode
|
|
- * - at least one of:
|
|
- * - inode is not a regular file
|
|
- * - inode is setuid
|
|
- * - inode is setgid and group-exec
|
|
- * - access failure for read and write
|
|
+ * - hardlink source is unsafe (see safe_hardlink_source() above)
|
|
* - not CAP_FOWNER
|
|
*
|
|
* Returns 0 if successful, -ve on error.
|
|
*/
|
|
static int may_linkat(struct path *link)
|
|
{
|
|
- int error = 0;
|
|
const struct cred *cred;
|
|
struct inode *inode;
|
|
- int mode;
|
|
|
|
if (!sysctl_protected_hardlinks)
|
|
return 0;
|
|
|
|
cred = current_cred();
|
|
inode = link->dentry->d_inode;
|
|
- mode = inode->i_mode;
|
|
-
|
|
- if (cred->fsuid != inode->i_uid &&
|
|
- (!S_ISREG(mode) || (mode & S_ISUID) ||
|
|
- ((mode & (S_ISGID | S_IXGRP)) == (S_ISGID | S_IXGRP)) ||
|
|
- (inode_permission(inode, MAY_READ | MAY_WRITE))) &&
|
|
- !capable(CAP_FOWNER))
|
|
- error = -EPERM;
|
|
|
|
- if (error)
|
|
- audit_log_link_denied("linkat", link);
|
|
+ /* Source inode owner (or CAP_FOWNER) can hardlink all they like,
|
|
+ * otherwise, it must be a safe source.
|
|
+ */
|
|
+ if (cred->fsuid == inode->i_uid || safe_hardlink_source(inode) ||
|
|
+ capable(CAP_FOWNER))
|
|
+ return 0;
|
|
|
|
- return error;
|
|
+ audit_log_link_denied("linkat", link);
|
|
+ return -EPERM;
|
|
}
|
|
#else
|
|
static inline int may_follow_link(struct path *link)
|
|
--
|
|
1.7.9.1
|
|
|