📄 hooks.c
字号:
/* * NSA Security-Enhanced Linux (SELinux) security module * * This file contains the SELinux hook function implementations. * * Authors: Stephen Smalley, <sds@epoch.ncsc.mil> * Chris Vance, <cvance@nai.com> * Wayne Salamon, <wsalamon@nai.com> * James Morris <jmorris@redhat.com> * * Copyright (C) 2001,2002 Networks Associates Technology, Inc. * Copyright (C) 2003 Red Hat, Inc., James Morris <jmorris@redhat.com> * Copyright (C) 2004-2005 Trusted Computer Solutions, Inc. * <dgoeddel@trustedcs.com> * Copyright (C) 2006 Hewlett-Packard Development Company, L.P. * Paul Moore, <paul.moore@hp.com> * Copyright (C) 2007 Hitachi Software Engineering Co., Ltd. * Yuichi Nakamura <ynakam@hitachisoft.jp> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2, * as published by the Free Software Foundation. */#include <linux/init.h>#include <linux/kernel.h>#include <linux/ptrace.h>#include <linux/errno.h>#include <linux/sched.h>#include <linux/security.h>#include <linux/xattr.h>#include <linux/capability.h>#include <linux/unistd.h>#include <linux/mm.h>#include <linux/mman.h>#include <linux/slab.h>#include <linux/pagemap.h>#include <linux/swap.h>#include <linux/spinlock.h>#include <linux/syscalls.h>#include <linux/file.h>#include <linux/namei.h>#include <linux/mount.h>#include <linux/ext2_fs.h>#include <linux/proc_fs.h>#include <linux/kd.h>#include <linux/netfilter_ipv4.h>#include <linux/netfilter_ipv6.h>#include <linux/tty.h>#include <net/icmp.h>#include <net/ip.h> /* for local_port_range[] */#include <net/tcp.h> /* struct or_callable used in sock_rcv_skb */#include <asm/uaccess.h>#include <asm/ioctls.h>#include <linux/bitops.h>#include <linux/interrupt.h>#include <linux/netdevice.h> /* for network interface checks */#include <linux/netlink.h>#include <linux/tcp.h>#include <linux/udp.h>#include <linux/dccp.h>#include <linux/quota.h>#include <linux/un.h> /* for Unix socket types */#include <net/af_unix.h> /* for Unix socket types */#include <linux/parser.h>#include <linux/nfs_mount.h>#include <net/ipv6.h>#include <linux/hugetlb.h>#include <linux/personality.h>#include <linux/sysctl.h>#include <linux/audit.h>#include <linux/string.h>#include <linux/selinux.h>#include <linux/mutex.h>#include "avc.h"#include "objsec.h"#include "netif.h"#include "xfrm.h"#include "netlabel.h"#define XATTR_SELINUX_SUFFIX "selinux"#define XATTR_NAME_SELINUX XATTR_SECURITY_PREFIX XATTR_SELINUX_SUFFIXextern unsigned int policydb_loaded_version;extern int selinux_nlmsg_lookup(u16 sclass, u16 nlmsg_type, u32 *perm);extern int selinux_compat_net;extern struct security_operations *security_ops;#ifdef CONFIG_SECURITY_SELINUX_DEVELOPint selinux_enforcing = 0;static int __init enforcing_setup(char *str){ selinux_enforcing = simple_strtol(str,NULL,0); return 1;}__setup("enforcing=", enforcing_setup);#endif#ifdef CONFIG_SECURITY_SELINUX_BOOTPARAMint selinux_enabled = CONFIG_SECURITY_SELINUX_BOOTPARAM_VALUE;static int __init selinux_enabled_setup(char *str){ selinux_enabled = simple_strtol(str, NULL, 0); return 1;}__setup("selinux=", selinux_enabled_setup);#elseint selinux_enabled = 1;#endif/* Original (dummy) security module. */static struct security_operations *original_ops = NULL;/* Minimal support for a secondary security module, just to allow the use of the dummy or capability modules. The owlsm module can alternatively be used as a secondary module as long as CONFIG_OWLSM_FD is not enabled. */static struct security_operations *secondary_ops = NULL;/* Lists of inode and superblock security structures initialized before the policy was loaded. */static LIST_HEAD(superblock_security_head);static DEFINE_SPINLOCK(sb_security_lock);static struct kmem_cache *sel_inode_cache;/* Return security context for a given sid or just the context length if the buffer is null or length is 0 */static int selinux_getsecurity(u32 sid, void *buffer, size_t size){ char *context; unsigned len; int rc; rc = security_sid_to_context(sid, &context, &len); if (rc) return rc; if (!buffer || !size) goto getsecurity_exit; if (size < len) { len = -ERANGE; goto getsecurity_exit; } memcpy(buffer, context, len);getsecurity_exit: kfree(context); return len;}/* Allocate and free functions for each kind of security blob. */static int task_alloc_security(struct task_struct *task){ struct task_security_struct *tsec; tsec = kzalloc(sizeof(struct task_security_struct), GFP_KERNEL); if (!tsec) return -ENOMEM; tsec->task = task; tsec->osid = tsec->sid = tsec->ptrace_sid = SECINITSID_UNLABELED; task->security = tsec; return 0;}static void task_free_security(struct task_struct *task){ struct task_security_struct *tsec = task->security; task->security = NULL; kfree(tsec);}static int inode_alloc_security(struct inode *inode){ struct task_security_struct *tsec = current->security; struct inode_security_struct *isec; isec = kmem_cache_zalloc(sel_inode_cache, GFP_KERNEL); if (!isec) return -ENOMEM; mutex_init(&isec->lock); INIT_LIST_HEAD(&isec->list); isec->inode = inode; isec->sid = SECINITSID_UNLABELED; isec->sclass = SECCLASS_FILE; isec->task_sid = tsec->sid; inode->i_security = isec; return 0;}static void inode_free_security(struct inode *inode){ struct inode_security_struct *isec = inode->i_security; struct superblock_security_struct *sbsec = inode->i_sb->s_security; spin_lock(&sbsec->isec_lock); if (!list_empty(&isec->list)) list_del_init(&isec->list); spin_unlock(&sbsec->isec_lock); inode->i_security = NULL; kmem_cache_free(sel_inode_cache, isec);}static int file_alloc_security(struct file *file){ struct task_security_struct *tsec = current->security; struct file_security_struct *fsec; fsec = kzalloc(sizeof(struct file_security_struct), GFP_KERNEL); if (!fsec) return -ENOMEM; fsec->file = file; fsec->sid = tsec->sid; fsec->fown_sid = tsec->sid; file->f_security = fsec; return 0;}static void file_free_security(struct file *file){ struct file_security_struct *fsec = file->f_security; file->f_security = NULL; kfree(fsec);}static int superblock_alloc_security(struct super_block *sb){ struct superblock_security_struct *sbsec; sbsec = kzalloc(sizeof(struct superblock_security_struct), GFP_KERNEL); if (!sbsec) return -ENOMEM; mutex_init(&sbsec->lock); INIT_LIST_HEAD(&sbsec->list); INIT_LIST_HEAD(&sbsec->isec_head); spin_lock_init(&sbsec->isec_lock); sbsec->sb = sb; sbsec->sid = SECINITSID_UNLABELED; sbsec->def_sid = SECINITSID_FILE; sbsec->mntpoint_sid = SECINITSID_UNLABELED; sb->s_security = sbsec; return 0;}static void superblock_free_security(struct super_block *sb){ struct superblock_security_struct *sbsec = sb->s_security; spin_lock(&sb_security_lock); if (!list_empty(&sbsec->list)) list_del_init(&sbsec->list); spin_unlock(&sb_security_lock); sb->s_security = NULL; kfree(sbsec);}static int sk_alloc_security(struct sock *sk, int family, gfp_t priority){ struct sk_security_struct *ssec; ssec = kzalloc(sizeof(*ssec), priority); if (!ssec) return -ENOMEM; ssec->sk = sk; ssec->peer_sid = SECINITSID_UNLABELED; ssec->sid = SECINITSID_UNLABELED; sk->sk_security = ssec; selinux_netlbl_sk_security_init(ssec, family); return 0;}static void sk_free_security(struct sock *sk){ struct sk_security_struct *ssec = sk->sk_security; sk->sk_security = NULL; kfree(ssec);}/* The security server must be initialized before any labeling or access decisions can be provided. */extern int ss_initialized;/* The file system's label must be initialized prior to use. */static char *labeling_behaviors[6] = { "uses xattr", "uses transition SIDs", "uses task SIDs", "uses genfs_contexts", "not configured for labeling", "uses mountpoint labeling",};static int inode_doinit_with_dentry(struct inode *inode, struct dentry *opt_dentry);static inline int inode_doinit(struct inode *inode){ return inode_doinit_with_dentry(inode, NULL);}enum { Opt_error = -1, Opt_context = 1, Opt_fscontext = 2, Opt_defcontext = 4, Opt_rootcontext = 8,};static match_table_t tokens = { {Opt_context, "context=%s"}, {Opt_fscontext, "fscontext=%s"}, {Opt_defcontext, "defcontext=%s"}, {Opt_rootcontext, "rootcontext=%s"}, {Opt_error, NULL},};#define SEL_MOUNT_FAIL_MSG "SELinux: duplicate or incompatible mount options\n"static int may_context_mount_sb_relabel(u32 sid, struct superblock_security_struct *sbsec, struct task_security_struct *tsec){ int rc; rc = avc_has_perm(tsec->sid, sbsec->sid, SECCLASS_FILESYSTEM, FILESYSTEM__RELABELFROM, NULL); if (rc) return rc; rc = avc_has_perm(tsec->sid, sid, SECCLASS_FILESYSTEM, FILESYSTEM__RELABELTO, NULL); return rc;}static int may_context_mount_inode_relabel(u32 sid, struct superblock_security_struct *sbsec, struct task_security_struct *tsec){ int rc; rc = avc_has_perm(tsec->sid, sbsec->sid, SECCLASS_FILESYSTEM, FILESYSTEM__RELABELFROM, NULL); if (rc) return rc; rc = avc_has_perm(sid, sbsec->sid, SECCLASS_FILESYSTEM, FILESYSTEM__ASSOCIATE, NULL); return rc;}static int try_context_mount(struct super_block *sb, void *data){ char *context = NULL, *defcontext = NULL; char *fscontext = NULL, *rootcontext = NULL; const char *name; u32 sid; int alloc = 0, rc = 0, seen = 0; struct task_security_struct *tsec = current->security; struct superblock_security_struct *sbsec = sb->s_security; if (!data) goto out; name = sb->s_type->name; if (sb->s_type->fs_flags & FS_BINARY_MOUNTDATA) { /* NFS we understand. */ if (!strcmp(name, "nfs")) { struct nfs_mount_data *d = data; if (d->version < NFS_MOUNT_VERSION) goto out; if (d->context[0]) { context = d->context; seen |= Opt_context; } } else goto out; } else { /* Standard string-based options. */ char *p, *options = data; while ((p = strsep(&options, "|")) != NULL) { int token; substring_t args[MAX_OPT_ARGS]; if (!*p) continue; token = match_token(p, tokens, args); switch (token) { case Opt_context: if (seen & (Opt_context|Opt_defcontext)) { rc = -EINVAL; printk(KERN_WARNING SEL_MOUNT_FAIL_MSG); goto out_free; } context = match_strdup(&args[0]); if (!context) { rc = -ENOMEM; goto out_free; } if (!alloc) alloc = 1; seen |= Opt_context; break; case Opt_fscontext: if (seen & Opt_fscontext) { rc = -EINVAL; printk(KERN_WARNING SEL_MOUNT_FAIL_MSG); goto out_free; } fscontext = match_strdup(&args[0]); if (!fscontext) { rc = -ENOMEM; goto out_free; } if (!alloc) alloc = 1; seen |= Opt_fscontext; break; case Opt_rootcontext: if (seen & Opt_rootcontext) { rc = -EINVAL; printk(KERN_WARNING SEL_MOUNT_FAIL_MSG); goto out_free; } rootcontext = match_strdup(&args[0]); if (!rootcontext) { rc = -ENOMEM; goto out_free; } if (!alloc) alloc = 1; seen |= Opt_rootcontext; break; case Opt_defcontext: if (sbsec->behavior != SECURITY_FS_USE_XATTR) { rc = -EINVAL; printk(KERN_WARNING "SELinux: " "defcontext option is invalid " "for this filesystem type\n"); goto out_free; } if (seen & (Opt_context|Opt_defcontext)) { rc = -EINVAL; printk(KERN_WARNING SEL_MOUNT_FAIL_MSG); goto out_free; } defcontext = match_strdup(&args[0]); if (!defcontext) { rc = -ENOMEM; goto out_free; } if (!alloc) alloc = 1; seen |= Opt_defcontext; break; default: rc = -EINVAL; printk(KERN_WARNING "SELinux: unknown mount " "option\n"); goto out_free; } } } if (!seen) goto out; /* sets the context of the superblock for the fs being mounted. */ if (fscontext) { rc = security_context_to_sid(fscontext, strlen(fscontext), &sid); if (rc) { printk(KERN_WARNING "SELinux: security_context_to_sid" "(%s) failed for (dev %s, type %s) errno=%d\n", fscontext, sb->s_id, name, rc); goto out_free; } rc = may_context_mount_sb_relabel(sid, sbsec, tsec); if (rc) goto out_free; sbsec->sid = sid; } /* * Switch to using mount point labeling behavior. * sets the label used on all file below the mountpoint, and will set * the superblock context if not already set. */ if (context) { rc = security_context_to_sid(context, strlen(context), &sid); if (rc) { printk(KERN_WARNING "SELinux: security_context_to_sid" "(%s) failed for (dev %s, type %s) errno=%d\n", context, sb->s_id, name, rc); goto out_free; } if (!fscontext) { rc = may_context_mount_sb_relabel(sid, sbsec, tsec); if (rc) goto out_free; sbsec->sid = sid; } else { rc = may_context_mount_inode_relabel(sid, sbsec, tsec); if (rc)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -