Skip to content

Commit 5496197

Browse files
dhowellsJames Morris
authored andcommitted
debugfs: Restrict debugfs when the kernel is locked down
Disallow opening of debugfs files that might be used to muck around when the kernel is locked down as various drivers give raw access to hardware through debugfs. Given the effort of auditing all 2000 or so files and manually fixing each one as necessary, I've chosen to apply a heuristic instead. The following changes are made: (1) chmod and chown are disallowed on debugfs objects (though the root dir can be modified by mount and remount, but I'm not worried about that). (2) When the kernel is locked down, only files with the following criteria are permitted to be opened: - The file must have mode 00444 - The file must not have ioctl methods - The file must not have mmap (3) When the kernel is locked down, files may only be opened for reading. Normal device interaction should be done through configfs, sysfs or a miscdev, not debugfs. Note that this makes it unnecessary to specifically lock down show_dsts(), show_devs() and show_call() in the asus-wmi driver. I would actually prefer to lock down all files by default and have the the files unlocked by the creator. This is tricky to manage correctly, though, as there are 19 creation functions and ~1600 call sites (some of them in loops scanning tables). Signed-off-by: David Howells <[email protected]> cc: Andy Shevchenko <[email protected]> cc: [email protected] cc: [email protected] cc: Matthew Garrett <[email protected]> cc: Thomas Gleixner <[email protected]> Cc: Greg KH <[email protected]> Cc: Rafael J. Wysocki <[email protected]> Signed-off-by: Matthew Garrett <[email protected]> Signed-off-by: James Morris <[email protected]>
1 parent 29d3c1c commit 5496197

File tree

4 files changed

+62
-2
lines changed

4 files changed

+62
-2
lines changed

fs/debugfs/file.c

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
#include <linux/atomic.h>
2020
#include <linux/device.h>
2121
#include <linux/poll.h>
22+
#include <linux/security.h>
2223

2324
#include "internal.h"
2425

@@ -136,6 +137,25 @@ void debugfs_file_put(struct dentry *dentry)
136137
}
137138
EXPORT_SYMBOL_GPL(debugfs_file_put);
138139

140+
/*
141+
* Only permit access to world-readable files when the kernel is locked down.
142+
* We also need to exclude any file that has ways to write or alter it as root
143+
* can bypass the permissions check.
144+
*/
145+
static bool debugfs_is_locked_down(struct inode *inode,
146+
struct file *filp,
147+
const struct file_operations *real_fops)
148+
{
149+
if ((inode->i_mode & 07777) == 0444 &&
150+
!(filp->f_mode & FMODE_WRITE) &&
151+
!real_fops->unlocked_ioctl &&
152+
!real_fops->compat_ioctl &&
153+
!real_fops->mmap)
154+
return false;
155+
156+
return security_locked_down(LOCKDOWN_DEBUGFS);
157+
}
158+
139159
static int open_proxy_open(struct inode *inode, struct file *filp)
140160
{
141161
struct dentry *dentry = F_DENTRY(filp);
@@ -147,6 +167,11 @@ static int open_proxy_open(struct inode *inode, struct file *filp)
147167
return r == -EIO ? -ENOENT : r;
148168

149169
real_fops = debugfs_real_fops(filp);
170+
171+
r = debugfs_is_locked_down(inode, filp, real_fops);
172+
if (r)
173+
goto out;
174+
150175
real_fops = fops_get(real_fops);
151176
if (!real_fops) {
152177
/* Huh? Module did not clean up after itself at exit? */
@@ -272,6 +297,11 @@ static int full_proxy_open(struct inode *inode, struct file *filp)
272297
return r == -EIO ? -ENOENT : r;
273298

274299
real_fops = debugfs_real_fops(filp);
300+
301+
r = debugfs_is_locked_down(inode, filp, real_fops);
302+
if (r)
303+
goto out;
304+
275305
real_fops = fops_get(real_fops);
276306
if (!real_fops) {
277307
/* Huh? Module did not cleanup after itself at exit? */

fs/debugfs/inode.c

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
#include <linux/parser.h>
2424
#include <linux/magic.h>
2525
#include <linux/slab.h>
26+
#include <linux/security.h>
2627

2728
#include "internal.h"
2829

@@ -32,6 +33,32 @@ static struct vfsmount *debugfs_mount;
3233
static int debugfs_mount_count;
3334
static bool debugfs_registered;
3435

36+
/*
37+
* Don't allow access attributes to be changed whilst the kernel is locked down
38+
* so that we can use the file mode as part of a heuristic to determine whether
39+
* to lock down individual files.
40+
*/
41+
static int debugfs_setattr(struct dentry *dentry, struct iattr *ia)
42+
{
43+
int ret = security_locked_down(LOCKDOWN_DEBUGFS);
44+
45+
if (ret && (ia->ia_valid & (ATTR_MODE | ATTR_UID | ATTR_GID)))
46+
return ret;
47+
return simple_setattr(dentry, ia);
48+
}
49+
50+
static const struct inode_operations debugfs_file_inode_operations = {
51+
.setattr = debugfs_setattr,
52+
};
53+
static const struct inode_operations debugfs_dir_inode_operations = {
54+
.lookup = simple_lookup,
55+
.setattr = debugfs_setattr,
56+
};
57+
static const struct inode_operations debugfs_symlink_inode_operations = {
58+
.get_link = simple_get_link,
59+
.setattr = debugfs_setattr,
60+
};
61+
3562
static struct inode *debugfs_get_inode(struct super_block *sb)
3663
{
3764
struct inode *inode = new_inode(sb);
@@ -355,6 +382,7 @@ static struct dentry *__debugfs_create_file(const char *name, umode_t mode,
355382
inode->i_mode = mode;
356383
inode->i_private = data;
357384

385+
inode->i_op = &debugfs_file_inode_operations;
358386
inode->i_fop = proxy_fops;
359387
dentry->d_fsdata = (void *)((unsigned long)real_fops |
360388
DEBUGFS_FSDATA_IS_REAL_FOPS_BIT);
@@ -515,7 +543,7 @@ struct dentry *debugfs_create_dir(const char *name, struct dentry *parent)
515543
return failed_creating(dentry);
516544

517545
inode->i_mode = S_IFDIR | S_IRWXU | S_IRUGO | S_IXUGO;
518-
inode->i_op = &simple_dir_inode_operations;
546+
inode->i_op = &debugfs_dir_inode_operations;
519547
inode->i_fop = &simple_dir_operations;
520548

521549
/* directory inodes start off with i_nlink == 2 (for "." entry) */
@@ -610,7 +638,7 @@ struct dentry *debugfs_create_symlink(const char *name, struct dentry *parent,
610638
return failed_creating(dentry);
611639
}
612640
inode->i_mode = S_IFLNK | S_IRWXUGO;
613-
inode->i_op = &simple_symlink_inode_operations;
641+
inode->i_op = &debugfs_symlink_inode_operations;
614642
inode->i_link = link;
615643
d_instantiate(dentry, inode);
616644
return end_creating(dentry);

include/linux/security.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,7 @@ enum lockdown_reason {
115115
LOCKDOWN_TIOCSSERIAL,
116116
LOCKDOWN_MODULE_PARAMETERS,
117117
LOCKDOWN_MMIOTRACE,
118+
LOCKDOWN_DEBUGFS,
118119
LOCKDOWN_INTEGRITY_MAX,
119120
LOCKDOWN_KCORE,
120121
LOCKDOWN_KPROBES,

security/lockdown/lockdown.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ static char *lockdown_reasons[LOCKDOWN_CONFIDENTIALITY_MAX+1] = {
3030
[LOCKDOWN_TIOCSSERIAL] = "reconfiguration of serial port IO",
3131
[LOCKDOWN_MODULE_PARAMETERS] = "unsafe module parameters",
3232
[LOCKDOWN_MMIOTRACE] = "unsafe mmio",
33+
[LOCKDOWN_DEBUGFS] = "debugfs access",
3334
[LOCKDOWN_INTEGRITY_MAX] = "integrity",
3435
[LOCKDOWN_KCORE] = "/proc/kcore access",
3536
[LOCKDOWN_KPROBES] = "use of kprobes",

0 commit comments

Comments
 (0)