996 lines · 28.1 KB · C
RawDownload
/* Common capabilities, needed by capability.o. * *	This program is free software; you can redistribute it and/or modify *	it under the terms of the GNU General Public License as published by *	the Free Software Foundation; either version 2 of the License, or *	(at your option) any later version. * */ #include <linux/capability.h>#include <linux/audit.h>#include <linux/module.h>#include <linux/init.h>#include <linux/kernel.h>#include <linux/security.h>#include <linux/file.h>#include <linux/mm.h>#include <linux/mman.h>#include <linux/pagemap.h>#include <linux/swap.h>#include <linux/skbuff.h>#include <linux/netlink.h>#include <linux/ptrace.h>#include <linux/xattr.h>#include <linux/hugetlb.h>#include <linux/mount.h>#include <linux/sched.h>#include <linux/prctl.h>#include <linux/securebits.h>#include <linux/user_namespace.h>#include <linux/binfmts.h>#include <linux/personality.h> /* * If a non-root user executes a setuid-root binary in * !secure(SECURE_NOROOT) mode, then we raise capabilities. * However if fE is also set, then the intent is for only * the file capabilities to be applied, and the setuid-root * bit is left on either to change the uid (plausible) or * to get full privilege on a kernel without file capabilities * support.  So in that case we do not raise capabilities. * * Warn if that happens, once per boot. */static void warn_setuid_and_fcaps_mixed(const char *fname){	static int warned;	if (!warned) {		printk(KERN_INFO "warning: `%s' has both setuid-root and"			" effective capabilities. Therefore not raising all"			" capabilities.\n", fname);		warned = 1;	}} int cap_netlink_send(struct sock *sk, struct sk_buff *skb){	return 0;} /** * cap_capable - Determine whether a task has a particular effective capability * @cred: The credentials to use * @ns:  The user namespace in which we need the capability * @cap: The capability to check for * @audit: Whether to write an audit message or not * * Determine whether the nominated task has the specified capability amongst * its effective set, returning 0 if it does, -ve if it does not. * * NOTE WELL: cap_has_capability() cannot be used like the kernel's capable() * and has_capability() functions.  That is, it has the reverse semantics: * cap_has_capability() returns 0 when a task has a capability, but the * kernel's capable() and has_capability() returns 1 for this case. */int cap_capable(const struct cred *cred, struct user_namespace *targ_ns,		int cap, int audit){	struct user_namespace *ns = targ_ns; 	/* See if cred has the capability in the target user namespace	 * by examining the target user namespace and all of the target	 * user namespace's parents.	 */	for (;;) {		/* Do we have the necessary capabilities? */		if (ns == cred->user_ns)			return cap_raised(cred->cap_effective, cap) ? 0 : -EPERM; 		/* Have we tried all of the parent namespaces? */		if (ns == &init_user_ns)			return -EPERM; 		/* 		 * The owner of the user namespace in the parent of the		 * user namespace has all caps.		 */		if ((ns->parent == cred->user_ns) && uid_eq(ns->owner, cred->euid))			return 0; 		/*		 * If you have a capability in a parent user ns, then you have		 * it over all children user namespaces as well.		 */		ns = ns->parent;	} 	/* We never get here */} /** * cap_settime - Determine whether the current process may set the system clock * @ts: The time to set * @tz: The timezone to set * * Determine whether the current process may set the system clock and timezone * information, returning 0 if permission granted, -ve if denied. */int cap_settime(const struct timespec *ts, const struct timezone *tz){	if (!capable(CAP_SYS_TIME))		return -EPERM;	return 0;} /** * cap_ptrace_access_check - Determine whether the current process may access *			   another * @child: The process to be accessed * @mode: The mode of attachment. * * If we are in the same or an ancestor user_ns and have all the target * task's capabilities, then ptrace access is allowed. * If we have the ptrace capability to the target user_ns, then ptrace * access is allowed. * Else denied. * * Determine whether a process may access another, returning 0 if permission * granted, -ve if denied. */int cap_ptrace_access_check(struct task_struct *child, unsigned int mode){	int ret = 0;	const struct cred *cred, *child_cred; 	rcu_read_lock();	cred = current_cred();	child_cred = __task_cred(child);	if (cred->user_ns == child_cred->user_ns &&	    cap_issubset(child_cred->cap_permitted, cred->cap_permitted))		goto out;	if (ns_capable(child_cred->user_ns, CAP_SYS_PTRACE))		goto out;	ret = -EPERM;out:	rcu_read_unlock();	return ret;} /** * cap_ptrace_traceme - Determine whether another process may trace the current * @parent: The task proposed to be the tracer * * If parent is in the same or an ancestor user_ns and has all current's * capabilities, then ptrace access is allowed. * If parent has the ptrace capability to current's user_ns, then ptrace * access is allowed. * Else denied. * * Determine whether the nominated task is permitted to trace the current * process, returning 0 if permission is granted, -ve if denied. */int cap_ptrace_traceme(struct task_struct *parent){	int ret = 0;	const struct cred *cred, *child_cred; 	rcu_read_lock();	cred = __task_cred(parent);	child_cred = current_cred();	if (cred->user_ns == child_cred->user_ns &&	    cap_issubset(child_cred->cap_permitted, cred->cap_permitted))		goto out;	if (has_ns_capability(parent, child_cred->user_ns, CAP_SYS_PTRACE))		goto out;	ret = -EPERM;out:	rcu_read_unlock();	return ret;} /** * cap_capget - Retrieve a task's capability sets * @target: The task from which to retrieve the capability sets * @effective: The place to record the effective set * @inheritable: The place to record the inheritable set * @permitted: The place to record the permitted set * * This function retrieves the capabilities of the nominated task and returns * them to the caller. */int cap_capget(struct task_struct *target, kernel_cap_t *effective,	       kernel_cap_t *inheritable, kernel_cap_t *permitted){	const struct cred *cred; 	/* Derived from kernel/capability.c:sys_capget. */	rcu_read_lock();	cred = __task_cred(target);	*effective   = cred->cap_effective;	*inheritable = cred->cap_inheritable;	*permitted   = cred->cap_permitted;	rcu_read_unlock();	return 0;} /* * Determine whether the inheritable capabilities are limited to the old * permitted set.  Returns 1 if they are limited, 0 if they are not. */static inline int cap_inh_is_capped(void){ 	/* they are so limited unless the current task has the CAP_SETPCAP	 * capability	 */	if (cap_capable(current_cred(), current_cred()->user_ns,			CAP_SETPCAP, SECURITY_CAP_AUDIT) == 0)		return 0;	return 1;} /** * cap_capset - Validate and apply proposed changes to current's capabilities * @new: The proposed new credentials; alterations should be made here * @old: The current task's current credentials * @effective: A pointer to the proposed new effective capabilities set * @inheritable: A pointer to the proposed new inheritable capabilities set * @permitted: A pointer to the proposed new permitted capabilities set * * This function validates and applies a proposed mass change to the current * process's capability sets.  The changes are made to the proposed new * credentials, and assuming no error, will be committed by the caller of LSM. */int cap_capset(struct cred *new,	       const struct cred *old,	       const kernel_cap_t *effective,	       const kernel_cap_t *inheritable,	       const kernel_cap_t *permitted){	if (cap_inh_is_capped() &&	    !cap_issubset(*inheritable,			  cap_combine(old->cap_inheritable,				      old->cap_permitted)))		/* incapable of using this inheritable set */		return -EPERM; 	if (!cap_issubset(*inheritable,			  cap_combine(old->cap_inheritable,				      old->cap_bset)))		/* no new pI capabilities outside bounding set */		return -EPERM; 	/* verify restrictions on target's new Permitted set */	if (!cap_issubset(*permitted, old->cap_permitted))		return -EPERM; 	/* verify the _new_Effective_ is a subset of the _new_Permitted_ */	if (!cap_issubset(*effective, *permitted))		return -EPERM; 	new->cap_effective   = *effective;	new->cap_inheritable = *inheritable;	new->cap_permitted   = *permitted;	return 0;} /* * Clear proposed capability sets for execve(). */static inline void bprm_clear_caps(struct linux_binprm *bprm){	cap_clear(bprm->cred->cap_permitted);	bprm->cap_effective = false;} /** * cap_inode_need_killpriv - Determine if inode change affects privileges * @dentry: The inode/dentry in being changed with change marked ATTR_KILL_PRIV * * Determine if an inode having a change applied that's marked ATTR_KILL_PRIV * affects the security markings on that inode, and if it is, should * inode_killpriv() be invoked or the change rejected? * * Returns 0 if granted; +ve if granted, but inode_killpriv() is required; and * -ve to deny the change. */int cap_inode_need_killpriv(struct dentry *dentry){	struct inode *inode = dentry->d_inode;	int error; 	if (!inode->i_op->getxattr)	       return 0; 	error = inode->i_op->getxattr(dentry, XATTR_NAME_CAPS, NULL, 0);	if (error <= 0)		return 0;	return 1;} /** * cap_inode_killpriv - Erase the security markings on an inode * @dentry: The inode/dentry to alter * * Erase the privilege-enhancing security markings on an inode. * * Returns 0 if successful, -ve on error. */int cap_inode_killpriv(struct dentry *dentry){	struct inode *inode = dentry->d_inode; 	if (!inode->i_op->removexattr)	       return 0; 	return inode->i_op->removexattr(dentry, XATTR_NAME_CAPS);} /* * Calculate the new process capability sets from the capability sets attached * to a file. */static inline int bprm_caps_from_vfs_caps(struct cpu_vfs_cap_data *caps,					  struct linux_binprm *bprm,					  bool *effective,					  bool *has_cap){	struct cred *new = bprm->cred;	unsigned i;	int ret = 0; 	if (caps->magic_etc & VFS_CAP_FLAGS_EFFECTIVE)		*effective = true; 	if (caps->magic_etc & VFS_CAP_REVISION_MASK)		*has_cap = true; 	CAP_FOR_EACH_U32(i) {		__u32 permitted = caps->permitted.cap[i];		__u32 inheritable = caps->inheritable.cap[i]; 		/*		 * pP' = (X & fP) | (pI & fI)		 */		new->cap_permitted.cap[i] =			(new->cap_bset.cap[i] & permitted) |			(new->cap_inheritable.cap[i] & inheritable); 		if (permitted & ~new->cap_permitted.cap[i])			/* insufficient to execute correctly */			ret = -EPERM;	} 	/*	 * For legacy apps, with no internal support for recognizing they	 * do not have enough capabilities, we return an error if they are	 * missing some "forced" (aka file-permitted) capabilities.	 */	return *effective ? ret : 0;} /* * Extract the on-exec-apply capability sets for an executable file. */int get_vfs_caps_from_disk(const struct dentry *dentry, struct cpu_vfs_cap_data *cpu_caps){	struct inode *inode = dentry->d_inode;	__u32 magic_etc;	unsigned tocopy, i;	int size;	struct vfs_cap_data caps; 	memset(cpu_caps, 0, sizeof(struct cpu_vfs_cap_data)); 	if (!inode || !inode->i_op->getxattr)		return -ENODATA; 	size = inode->i_op->getxattr((struct dentry *)dentry, XATTR_NAME_CAPS, &caps,				   XATTR_CAPS_SZ);	if (size == -ENODATA || size == -EOPNOTSUPP)		/* no data, that's ok */		return -ENODATA;	if (size < 0)		return size; 	if (size < sizeof(magic_etc))		return -EINVAL; 	cpu_caps->magic_etc = magic_etc = le32_to_cpu(caps.magic_etc); 	switch (magic_etc & VFS_CAP_REVISION_MASK) {	case VFS_CAP_REVISION_1:		if (size != XATTR_CAPS_SZ_1)			return -EINVAL;		tocopy = VFS_CAP_U32_1;		break;	case VFS_CAP_REVISION_2:		if (size != XATTR_CAPS_SZ_2)			return -EINVAL;		tocopy = VFS_CAP_U32_2;		break;	default:		return -EINVAL;	} 	CAP_FOR_EACH_U32(i) {		if (i >= tocopy)			break;		cpu_caps->permitted.cap[i] = le32_to_cpu(caps.data[i].permitted);		cpu_caps->inheritable.cap[i] = le32_to_cpu(caps.data[i].inheritable);	} 	return 0;} /* * Attempt to get the on-exec apply capability sets for an executable file from * its xattrs and, if present, apply them to the proposed credentials being * constructed by execve(). */static int get_file_caps(struct linux_binprm *bprm, bool *effective, bool *has_cap){	struct dentry *dentry;	int rc = 0;	struct cpu_vfs_cap_data vcaps; 	bprm_clear_caps(bprm); 	if (!file_caps_enabled)		return 0; 	if (bprm->file->f_vfsmnt->mnt_flags & MNT_NOSUID)		return 0; 	dentry = dget(bprm->file->f_dentry); 	rc = get_vfs_caps_from_disk(dentry, &vcaps);	if (rc < 0) {		if (rc == -EINVAL)			printk(KERN_NOTICE "%s: get_vfs_caps_from_disk returned %d for %s\n",				__func__, rc, bprm->filename);		else if (rc == -ENODATA)			rc = 0;		goto out;	} 	rc = bprm_caps_from_vfs_caps(&vcaps, bprm, effective, has_cap);	if (rc == -EINVAL)		printk(KERN_NOTICE "%s: cap_from_disk returned %d for %s\n",		       __func__, rc, bprm->filename); out:	dput(dentry);	if (rc)		bprm_clear_caps(bprm); 	return rc;} /** * cap_bprm_set_creds - Set up the proposed credentials for execve(). * @bprm: The execution parameters, including the proposed creds * * Set up the proposed credentials for a new execution context being * constructed by execve().  The proposed creds in @bprm->cred is altered, * which won't take effect immediately.  Returns 0 if successful, -ve on error. */int cap_bprm_set_creds(struct linux_binprm *bprm){	const struct cred *old = current_cred();	struct cred *new = bprm->cred;	bool effective, has_cap = false;	int ret;	kuid_t root_uid; 	effective = false;	ret = get_file_caps(bprm, &effective, &has_cap);	if (ret < 0)		return ret; 	root_uid = make_kuid(new->user_ns, 0); 	if (!issecure(SECURE_NOROOT)) {		/*		 * If the legacy file capability is set, then don't set privs		 * for a setuid root binary run by a non-root user.  Do set it		 * for a root user just to cause least surprise to an admin.		 */		if (has_cap && !uid_eq(new->uid, root_uid) && uid_eq(new->euid, root_uid)) {

Lines 1–500 of 996.