Brooklyn/grsecurity/gracl_fs.c
Scare Crowe 2a709f28fa Auto exploit mitigation feature
* 0day explit mitigation
* Memory corruption prevention
* Privilege escalation prevention
* Buffer over flow prevention
* File System corruption defense
* Thread escape prevention

This may very well be the most intensive inclusion to BrooklynR. This will not be part of an x86 suite nor it will be released as tool kit. The security core toolkit will remain part of kernel base.
2021-11-13 09:26:51 +05:00

449 lines
13 KiB
C

#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/types.h>
#include <linux/fs.h>
#include <linux/file.h>
#include <linux/stat.h>
#include <linux/grsecurity.h>
#include <linux/grinternal.h>
#include <linux/gracl.h>
umode_t
gr_acl_umask(void)
{
if (unlikely(!gr_acl_is_enabled()))
return 0;
return current->role->umask;
}
__u32
gr_acl_handle_hidden_file(const struct dentry * dentry,
const struct vfsmount * mnt)
{
__u32 mode;
if (unlikely(d_is_negative(dentry)))
return GR_FIND;
mode =
gr_search_file(dentry, GR_FIND | GR_AUDIT_FIND | GR_SUPPRESS, mnt);
if (unlikely(mode & GR_FIND && mode & GR_AUDIT_FIND)) {
gr_log_fs_rbac_generic(GR_DO_AUDIT, GR_HIDDEN_ACL_MSG, dentry, mnt);
return mode;
} else if (unlikely(!(mode & GR_FIND) && !(mode & GR_SUPPRESS))) {
gr_log_fs_rbac_generic(GR_DONT_AUDIT, GR_HIDDEN_ACL_MSG, dentry, mnt);
return 0;
} else if (unlikely(!(mode & GR_FIND)))
return 0;
return GR_FIND;
}
__u32
gr_acl_handle_open(const struct dentry * dentry, const struct vfsmount * mnt,
int acc_mode)
{
__u32 reqmode = GR_FIND;
__u32 mode;
if (unlikely(d_is_negative(dentry)))
return reqmode;
if (acc_mode & MAY_APPEND)
reqmode |= GR_APPEND;
else if (acc_mode & MAY_WRITE)
reqmode |= GR_WRITE;
if ((acc_mode & MAY_READ) && !d_is_dir(dentry))
reqmode |= GR_READ;
mode =
gr_search_file(dentry, reqmode | to_gr_audit(reqmode) | GR_SUPPRESS,
mnt);
if (unlikely(((mode & reqmode) == reqmode) && mode & GR_AUDITS)) {
gr_log_fs_rbac_mode2(GR_DO_AUDIT, GR_OPEN_ACL_MSG, dentry, mnt,
reqmode & GR_READ ? " reading" : "",
reqmode & GR_WRITE ? " writing" : reqmode &
GR_APPEND ? " appending" : "");
return reqmode;
} else
if (unlikely((mode & reqmode) != reqmode && !(mode & GR_SUPPRESS)))
{
gr_log_fs_rbac_mode2(GR_DONT_AUDIT, GR_OPEN_ACL_MSG, dentry, mnt,
reqmode & GR_READ ? " reading" : "",
reqmode & GR_WRITE ? " writing" : reqmode &
GR_APPEND ? " appending" : "");
return 0;
} else if (unlikely((mode & reqmode) != reqmode))
return 0;
return reqmode;
}
__u32
gr_acl_handle_creat(const struct dentry * dentry,
const struct dentry * p_dentry,
const struct vfsmount * p_mnt, int open_flags, int acc_mode,
const int imode)
{
__u32 reqmode = GR_WRITE | GR_CREATE;
__u32 mode;
if (acc_mode & MAY_APPEND)
reqmode |= GR_APPEND;
// if a directory was required or the directory already exists, then
// don't count this open as a read
if ((acc_mode & MAY_READ) &&
!((open_flags & O_DIRECTORY) || d_is_dir(dentry)))
reqmode |= GR_READ;
if ((open_flags & O_CREAT) &&
((imode & S_ISUID) || ((imode & (S_ISGID | S_IXGRP)) == (S_ISGID | S_IXGRP))))
reqmode |= GR_SETID;
mode =
gr_check_create(dentry, p_dentry, p_mnt,
reqmode | to_gr_audit(reqmode) | GR_SUPPRESS);
if (unlikely(((mode & reqmode) == reqmode) && mode & GR_AUDITS)) {
gr_log_fs_rbac_mode2(GR_DO_AUDIT, GR_CREATE_ACL_MSG, dentry, p_mnt,
reqmode & GR_READ ? " reading" : "",
reqmode & GR_WRITE ? " writing" : reqmode &
GR_APPEND ? " appending" : "");
return reqmode;
} else
if (unlikely((mode & reqmode) != reqmode && !(mode & GR_SUPPRESS)))
{
gr_log_fs_rbac_mode2(GR_DONT_AUDIT, GR_CREATE_ACL_MSG, dentry, p_mnt,
reqmode & GR_READ ? " reading" : "",
reqmode & GR_WRITE ? " writing" : reqmode &
GR_APPEND ? " appending" : "");
return 0;
} else if (unlikely((mode & reqmode) != reqmode))
return 0;
return reqmode;
}
__u32
gr_acl_handle_access(const struct dentry * dentry, const struct vfsmount * mnt,
const int fmode)
{
__u32 mode, reqmode = GR_FIND;
if ((fmode & S_IXOTH) && !d_is_dir(dentry))
reqmode |= GR_EXEC;
if (fmode & S_IWOTH)
reqmode |= GR_WRITE;
if (fmode & S_IROTH)
reqmode |= GR_READ;
mode =
gr_search_file(dentry, reqmode | to_gr_audit(reqmode) | GR_SUPPRESS,
mnt);
if (unlikely(((mode & reqmode) == reqmode) && mode & GR_AUDITS)) {
gr_log_fs_rbac_mode3(GR_DO_AUDIT, GR_ACCESS_ACL_MSG, dentry, mnt,
reqmode & GR_READ ? " reading" : "",
reqmode & GR_WRITE ? " writing" : "",
reqmode & GR_EXEC ? " executing" : "");
return reqmode;
} else
if (unlikely((mode & reqmode) != reqmode && !(mode & GR_SUPPRESS)))
{
gr_log_fs_rbac_mode3(GR_DONT_AUDIT, GR_ACCESS_ACL_MSG, dentry, mnt,
reqmode & GR_READ ? " reading" : "",
reqmode & GR_WRITE ? " writing" : "",
reqmode & GR_EXEC ? " executing" : "");
return 0;
} else if (unlikely((mode & reqmode) != reqmode))
return 0;
return reqmode;
}
static __u32 generic_fs_handler(const struct dentry *dentry, const struct vfsmount *mnt, __u32 reqmode, const char *fmt)
{
__u32 mode;
mode = gr_search_file(dentry, reqmode | to_gr_audit(reqmode) | GR_SUPPRESS, mnt);
if (unlikely(((mode & (reqmode)) == (reqmode)) && mode & GR_AUDITS)) {
gr_log_fs_rbac_generic(GR_DO_AUDIT, fmt, dentry, mnt);
return mode;
} else if (unlikely((mode & (reqmode)) != (reqmode) && !(mode & GR_SUPPRESS))) {
gr_log_fs_rbac_generic(GR_DONT_AUDIT, fmt, dentry, mnt);
return 0;
} else if (unlikely((mode & (reqmode)) != (reqmode)))
return 0;
return (reqmode);
}
__u32
gr_acl_handle_rmdir(const struct dentry * dentry, const struct vfsmount * mnt)
{
return generic_fs_handler(dentry, mnt, GR_WRITE | GR_DELETE , GR_RMDIR_ACL_MSG);
}
__u32
gr_acl_handle_unlink(const struct dentry *dentry, const struct vfsmount *mnt)
{
return generic_fs_handler(dentry, mnt, GR_WRITE | GR_DELETE , GR_UNLINK_ACL_MSG);
}
__u32
gr_acl_handle_truncate(const struct dentry *dentry, const struct vfsmount *mnt)
{
return generic_fs_handler(dentry, mnt, GR_WRITE, GR_TRUNCATE_ACL_MSG);
}
__u32
gr_acl_handle_utime(const struct dentry *dentry, const struct vfsmount *mnt)
{
return generic_fs_handler(dentry, mnt, GR_WRITE, GR_ATIME_ACL_MSG);
}
__u32
gr_acl_handle_chmod(const struct dentry *dentry, const struct vfsmount *mnt,
umode_t *modeptr)
{
umode_t mode;
struct inode *inode = d_backing_inode(dentry);
*modeptr &= ~gr_acl_umask();
mode = *modeptr;
if (unlikely(inode && S_ISSOCK(inode->i_mode)))
return 1;
if (unlikely(!d_is_dir(dentry) &&
((mode & S_ISUID) || ((mode & (S_ISGID | S_IXGRP)) == (S_ISGID | S_IXGRP))))) {
return generic_fs_handler(dentry, mnt, GR_WRITE | GR_SETID,
GR_CHMOD_ACL_MSG);
} else {
return generic_fs_handler(dentry, mnt, GR_WRITE, GR_CHMOD_ACL_MSG);
}
}
__u32
gr_acl_handle_chown(const struct dentry *dentry, const struct vfsmount *mnt)
{
return generic_fs_handler(dentry, mnt, GR_WRITE, GR_CHOWN_ACL_MSG);
}
__u32
gr_acl_handle_setxattr(const struct dentry *dentry, const struct vfsmount *mnt)
{
return generic_fs_handler(dentry, mnt, GR_WRITE, GR_SETXATTR_ACL_MSG);
}
__u32
gr_acl_handle_removexattr(const struct dentry *dentry, const struct vfsmount *mnt)
{
return generic_fs_handler(dentry, mnt, GR_WRITE, GR_REMOVEXATTR_ACL_MSG);
}
__u32
gr_acl_handle_execve(const struct dentry *dentry, const struct vfsmount *mnt)
{
return generic_fs_handler(dentry, mnt, GR_EXEC, GR_EXEC_ACL_MSG);
}
__u32
gr_acl_handle_unix(const struct dentry *dentry, const struct vfsmount *mnt)
{
return generic_fs_handler(dentry, mnt, GR_READ | GR_WRITE,
GR_UNIXCONNECT_ACL_MSG);
}
/* hardlinks require at minimum create and link permission,
any additional privilege required is based on the
privilege of the file being linked to
*/
__u32
gr_acl_handle_link(const struct dentry * new_dentry,
const struct dentry * parent_dentry,
const struct vfsmount * parent_mnt,
const struct dentry * old_dentry,
const struct vfsmount * old_mnt, const struct filename *to)
{
__u32 mode;
__u32 needmode = GR_CREATE | GR_LINK;
__u32 needaudit = GR_AUDIT_CREATE | GR_AUDIT_LINK;
mode =
gr_check_link(new_dentry, parent_dentry, parent_mnt, old_dentry,
old_mnt);
if (unlikely(((mode & needmode) == needmode) && (mode & needaudit))) {
gr_log_fs_rbac_str(GR_DO_AUDIT, GR_LINK_ACL_MSG, old_dentry, old_mnt, to->name);
return mode;
} else if (unlikely(((mode & needmode) != needmode) && !(mode & GR_SUPPRESS))) {
gr_log_fs_rbac_str(GR_DONT_AUDIT, GR_LINK_ACL_MSG, old_dentry, old_mnt, to->name);
return 0;
} else if (unlikely((mode & needmode) != needmode))
return 0;
return 1;
}
__u32
gr_acl_handle_symlink(const struct dentry * new_dentry,
const struct dentry * parent_dentry,
const struct vfsmount * parent_mnt, const struct filename *from)
{
__u32 needmode = GR_WRITE | GR_CREATE;
__u32 mode;
mode =
gr_check_create(new_dentry, parent_dentry, parent_mnt,
GR_CREATE | GR_AUDIT_CREATE |
GR_WRITE | GR_AUDIT_WRITE | GR_SUPPRESS);
if (unlikely(mode & GR_WRITE && mode & GR_AUDITS)) {
gr_log_fs_str_rbac(GR_DO_AUDIT, GR_SYMLINK_ACL_MSG, from->name, new_dentry, parent_mnt);
return mode;
} else if (unlikely(((mode & needmode) != needmode) && !(mode & GR_SUPPRESS))) {
gr_log_fs_str_rbac(GR_DONT_AUDIT, GR_SYMLINK_ACL_MSG, from->name, new_dentry, parent_mnt);
return 0;
} else if (unlikely((mode & needmode) != needmode))
return 0;
return (GR_WRITE | GR_CREATE);
}
static __u32 generic_fs_create_handler(const struct dentry *new_dentry, const struct dentry *parent_dentry, const struct vfsmount *parent_mnt, __u32 reqmode, const char *fmt)
{
__u32 mode;
mode = gr_check_create(new_dentry, parent_dentry, parent_mnt, reqmode | to_gr_audit(reqmode) | GR_SUPPRESS);
if (unlikely(((mode & (reqmode)) == (reqmode)) && mode & GR_AUDITS)) {
gr_log_fs_rbac_generic(GR_DO_AUDIT, fmt, new_dentry, parent_mnt);
return mode;
} else if (unlikely((mode & (reqmode)) != (reqmode) && !(mode & GR_SUPPRESS))) {
gr_log_fs_rbac_generic(GR_DONT_AUDIT, fmt, new_dentry, parent_mnt);
return 0;
} else if (unlikely((mode & (reqmode)) != (reqmode)))
return 0;
return (reqmode);
}
__u32
gr_acl_handle_mknod(const struct dentry * new_dentry,
const struct dentry * parent_dentry,
const struct vfsmount * parent_mnt,
const int mode)
{
__u32 reqmode = GR_WRITE | GR_CREATE;
if (unlikely((mode & S_ISUID) || ((mode & (S_ISGID | S_IXGRP)) == (S_ISGID | S_IXGRP))))
reqmode |= GR_SETID;
return generic_fs_create_handler(new_dentry, parent_dentry, parent_mnt,
reqmode, GR_MKNOD_ACL_MSG);
}
__u32
gr_acl_handle_mkdir(const struct dentry *new_dentry,
const struct dentry *parent_dentry,
const struct vfsmount *parent_mnt)
{
return generic_fs_create_handler(new_dentry, parent_dentry, parent_mnt,
GR_WRITE | GR_CREATE, GR_MKDIR_ACL_MSG);
}
#define RENAME_CHECK_SUCCESS(old, new) \
(((old & (GR_WRITE | GR_READ)) == (GR_WRITE | GR_READ)) && \
((new & (GR_WRITE | GR_READ)) == (GR_WRITE | GR_READ)))
int
gr_acl_handle_rename(struct dentry *new_dentry,
struct dentry *parent_dentry,
const struct vfsmount *parent_mnt,
struct dentry *old_dentry,
struct inode *old_parent_inode,
struct vfsmount *old_mnt, const struct filename *newname, unsigned int flags)
{
__u32 comp1, comp2;
int error = 0;
if (unlikely(!gr_acl_is_enabled()))
return 0;
if (flags & RENAME_EXCHANGE) {
comp1 = gr_search_file(new_dentry, GR_READ | GR_WRITE |
GR_AUDIT_READ | GR_AUDIT_WRITE |
GR_SUPPRESS, parent_mnt);
comp2 =
gr_search_file(old_dentry,
GR_READ | GR_WRITE | GR_AUDIT_READ |
GR_AUDIT_WRITE | GR_SUPPRESS, old_mnt);
} else if (d_is_negative(new_dentry)) {
comp1 = gr_check_create(new_dentry, parent_dentry, parent_mnt,
GR_READ | GR_WRITE | GR_CREATE | GR_AUDIT_READ |
GR_AUDIT_WRITE | GR_AUDIT_CREATE | GR_SUPPRESS);
comp2 = gr_search_file(old_dentry, GR_READ | GR_WRITE |
GR_DELETE | GR_AUDIT_DELETE |
GR_AUDIT_READ | GR_AUDIT_WRITE |
GR_SUPPRESS, old_mnt);
} else {
comp1 = gr_search_file(new_dentry, GR_READ | GR_WRITE |
GR_CREATE | GR_DELETE |
GR_AUDIT_CREATE | GR_AUDIT_DELETE |
GR_AUDIT_READ | GR_AUDIT_WRITE |
GR_SUPPRESS, parent_mnt);
comp2 =
gr_search_file(old_dentry,
GR_READ | GR_WRITE | GR_AUDIT_READ |
GR_DELETE | GR_AUDIT_DELETE |
GR_AUDIT_WRITE | GR_SUPPRESS, old_mnt);
}
if (RENAME_CHECK_SUCCESS(comp1, comp2) &&
((comp1 & GR_AUDITS) || (comp2 & GR_AUDITS)))
gr_log_fs_rbac_str(GR_DO_AUDIT, GR_RENAME_ACL_MSG, old_dentry, old_mnt, newname->name);
else if (!RENAME_CHECK_SUCCESS(comp1, comp2) && !(comp1 & GR_SUPPRESS)
&& !(comp2 & GR_SUPPRESS)) {
gr_log_fs_rbac_str(GR_DONT_AUDIT, GR_RENAME_ACL_MSG, old_dentry, old_mnt, newname->name);
error = -EACCES;
} else if (unlikely(!RENAME_CHECK_SUCCESS(comp1, comp2)))
error = -EACCES;
return error;
}
void
gr_acl_handle_exit(void)
{
u16 id;
char *rolename;
if (unlikely(current->acl_sp_role && gr_acl_is_enabled() &&
!(current->role->roletype & GR_ROLE_PERSIST))) {
id = current->acl_role_id;
rolename = current->role->rolename;
gr_set_acls(1);
gr_log_str_int(GR_DONT_AUDIT_GOOD, GR_SPROLEL_ACL_MSG, rolename, id);
}
gr_put_exec_file(current);
return;
}
int
gr_acl_handle_procpidmem(const struct task_struct *task)
{
if (unlikely(!gr_acl_is_enabled()))
return 0;
if (task != current && (task->acl->mode & GR_PROTPROCFD) &&
!(current->acl->mode & GR_POVERRIDE) &&
!(current->role->roletype & GR_ROLE_GOD))
return -EACCES;
return 0;
}