forked from Qortal/Brooklyn
2a709f28fa
* 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.
449 lines
13 KiB
C
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;
|
|
}
|