Quoting Andrew Morgan ([EMAIL PROTECTED]): > -----BEGIN PGP SIGNED MESSAGE----- > Hash: SHA1 > > Attached is what I consider only an RFC patch. > > I've not really thought through (to my satisfaction) the re-purposing of > current->keep_capabilities in the non-filesystem-supporting-capability > configuration, but this is basically the code I'm thinking about. (I'm > typing this email from a system running this patch over 2.6.23-rc3-mm1 > so its not 'obviously' broken.)
Note this needs a prototype for cap_task_prctl in include/linux/security.h for certain configs. Yes, the reuse of keep_capabilities is interesting. Will think about it for awhile :) In any case if this is the approach taken, then the change in meaning for keep_capabilities will need to be commented in both the patch description and in comments in the patch itself. Thanks Andrew, -serge > Adrian Bunk wrote: > >> The user would be userspace... > >> > >> Unless by 'the user' you actually mean the patch itself which will allow > >> the setting of secure_noroot per-process. I don't know for sure, but > >> suspect Andrew might like to wait until file capabilities make it into > >> and stabilize in Linus' tree before going on with that. > > > > That's what I am talking about. > > > > This patch should be submitted and discussed together with the changes > > Andrew has for securebits. > > Cheers > > Andrew > -----BEGIN PGP SIGNATURE----- > Version: GnuPG v1.2.6 (GNU/Linux) > > iD8DBQFG08y0QheEq9QabfIRAnUhAKCEHyUko292kULNTkRqQOGki2NohgCdGXvV > bc+bHzBbI6sPimdf4UTAzGY= > =vB0u > -----END PGP SIGNATURE----- > >From a8366ab7a12ed4283e24096e891ac13c1d471756 Mon Sep 17 00:00:00 2001 > From: Andrew Morgan <[EMAIL PROTECTED]> > Date: Mon, 27 Aug 2007 23:09:20 -0700 > Subject: [PATCH] Remove global securebits from the kernel. > > In the absence of filesystem capability support, there is no > longer any internal support for disabling root. The plain fact is > that while there was internal support for it, the userspace API for > enabling it was not implemented, and having a global personality > of this sort was almost impossible to use. > > With filesystem capability support, a new prctl(PR_[GS]ET_CAPONLY) > per-process flag is available. Once set (to 1) a process and all > of its children will irrevocably lose root-is-all-capable privilege. > > NOTE: just because root within a given process tree is not capable, > it does not mean root is impotent. Most Linux systems are riddled > with critical system files that are owned by root (uid=0), so much > care should be used relying on the security provided by this support. > It is likely that the best way to use this feature is within some > sort of restricted (chroot etc.) environment. Be especially careful > of where you mount /proc/.... > --- > include/linux/capability.h | 10 +++- > include/linux/prctl.h | 4 + > include/linux/sched.h | 1 - > include/linux/securebits.h | 30 ---------- > include/linux/security.h | 17 ++++-- > kernel/sys.c | 14 +---- > security/capability.c | 1 + > security/commoncap.c | 132 > +++++++++++++++++++++++++++++--------------- > security/dummy.c | 5 +- > security/security.c | 5 +- > security/selinux/hooks.c | 6 +- > 11 files changed, 124 insertions(+), 101 deletions(-) > delete mode 100644 include/linux/securebits.h > > diff --git a/include/linux/capability.h b/include/linux/capability.h > index 7a8d7ad..0fcef09 100644 > --- a/include/linux/capability.h > +++ b/include/linux/capability.h > @@ -147,8 +147,14 @@ typedef __u32 kernel_cap_t; > ** Linux-specific capabilities > **/ > > -/* Transfer any capability in your permitted set to any pid, > - remove any capability in your permitted set from any pid */ > +/* With filesystem support for capabilities configured: > + * Permit the current process to raise any capability in its inheritable > set. > + * Permit the current process to lock the process into capability-only mode > + * - prctl(PR_SET_CAPONLY, 1, ...) > + * Otherwise: > + * Transfer any capability in your permitted set to any pid, > + * remove any capability in your permitted set from any pid > + */ > > #define CAP_SETPCAP 8 > > diff --git a/include/linux/prctl.h b/include/linux/prctl.h > index e2eff90..e3f49a7 100644 > --- a/include/linux/prctl.h > +++ b/include/linux/prctl.h > @@ -63,4 +63,8 @@ > #define PR_GET_SECCOMP 21 > #define PR_SET_SECCOMP 22 > > +/* Get/Set process personality to secure-by-capabilities alone */ > +#define PR_GET_CAPONLY 23 > +#define PR_SET_CAPONLY 24 > + > #endif /* _LINUX_PRCTL_H */ > diff --git a/include/linux/sched.h b/include/linux/sched.h > index 2b3c936..be2e9c4 100644 > --- a/include/linux/sched.h > +++ b/include/linux/sched.h > @@ -67,7 +67,6 @@ struct sched_param { > #include <linux/smp.h> > #include <linux/sem.h> > #include <linux/signal.h> > -#include <linux/securebits.h> > #include <linux/fs_struct.h> > #include <linux/compiler.h> > #include <linux/completion.h> > diff --git a/include/linux/securebits.h b/include/linux/securebits.h > deleted file mode 100644 > index 5b06178..0000000 > --- a/include/linux/securebits.h > +++ /dev/null > @@ -1,30 +0,0 @@ > -#ifndef _LINUX_SECUREBITS_H > -#define _LINUX_SECUREBITS_H 1 > - > -#define SECUREBITS_DEFAULT 0x00000000 > - > -extern unsigned securebits; > - > -/* When set UID 0 has no special privileges. When unset, we support > - inheritance of root-permissions and suid-root executable under > - compatibility mode. We raise the effective and inheritable bitmasks > - *of the executable file* if the effective uid of the new process is > - 0. If the real uid is 0, we raise the inheritable bitmask of the > - executable file. */ > -#define SECURE_NOROOT 0 > - > -/* When set, setuid to/from uid 0 does not trigger capability-"fixes" > - to be compatible with old programs relying on set*uid to loose > - privileges. When unset, setuid doesn't change privileges. */ > -#define SECURE_NO_SETUID_FIXUP 2 > - > -/* Each securesetting is implemented using two bits. One bit specify > - whether the setting is on or off. The other bit specify whether the > - setting is fixed or not. A setting which is fixed cannot be changed > - from user-level. */ > - > -#define issecure(X) ( (1 << (X+1)) & SECUREBITS_DEFAULT ? \ > - (1 << (X)) & SECUREBITS_DEFAULT : \ > - (1 << (X)) & securebits ) > - > -#endif /* !_LINUX_SECUREBITS_H */ > diff --git a/include/linux/security.h b/include/linux/security.h > index 13d48fd..4227464 100644 > --- a/include/linux/security.h > +++ b/include/linux/security.h > @@ -660,13 +660,18 @@ struct request_sock; > * Return 0 if permission is granted. > * @task_prctl: > * Check permission before performing a process control operation on the > - * current process. > + * current process. This hook can also be used to provide LSM specific > + * process control functions. > * @option contains the operation. > * @arg2 contains a argument. > * @arg3 contains a argument. > * @arg4 contains a argument. > * @arg5 contains a argument. > - * Return 0 if permission is granted. > + * @errorp contains a pointer to the kernel variable holding the return > + * value. > + * Return 0 if permission is granted (pass-through); return 1 > + * (and set *errorp to the appropriate value, if the LSM is > + * overriding default behavior. > * @task_reparent_to_init: > * Set the security attributes in @p->security for a kernel thread that > * is being reparented to the init task. > @@ -1304,7 +1309,7 @@ struct security_operations { > int (*task_wait) (struct task_struct * p); > int (*task_prctl) (int option, unsigned long arg2, > unsigned long arg3, unsigned long arg4, > - unsigned long arg5); > + unsigned long arg5, long *errorp); > void (*task_reparent_to_init) (struct task_struct * p); > void (*task_to_inode)(struct task_struct *p, struct inode *inode); > > @@ -1550,7 +1555,8 @@ int security_task_kill(struct task_struct *p, struct > siginfo *info, > int sig, u32 secid); > int security_task_wait(struct task_struct *p); > int security_task_prctl(int option, unsigned long arg2, unsigned long arg3, > - unsigned long arg4, unsigned long arg5); > + unsigned long arg4, unsigned long arg5, > + long *errorp); > void security_task_reparent_to_init(struct task_struct *p); > void security_task_to_inode(struct task_struct *p, struct inode *inode); > int security_ipc_permission(struct kern_ipc_perm *ipcp, short flag); > @@ -2096,7 +2102,8 @@ static inline int security_task_wait (struct > task_struct *p) > static inline int security_task_prctl (int option, unsigned long arg2, > unsigned long arg3, > unsigned long arg4, > - unsigned long arg5) > + unsigned long arg5, > + long *errorp) > { > return 0; > } > diff --git a/kernel/sys.c b/kernel/sys.c > index c7c4fa4..42f26fd 100644 > --- a/kernel/sys.c > +++ b/kernel/sys.c > @@ -1639,8 +1639,7 @@ asmlinkage long sys_prctl(int option, unsigned long > arg2, unsigned long arg3, > { > long error; > > - error = security_task_prctl(option, arg2, arg3, arg4, arg5); > - if (error) > + if (security_task_prctl(option, arg2, arg3, arg4, arg5, &error)) > return error; > > switch (option) { > @@ -1693,17 +1692,6 @@ asmlinkage long sys_prctl(int option, unsigned long > arg2, unsigned long arg3, > error = -EINVAL; > break; > > - case PR_GET_KEEPCAPS: > - if (current->keep_capabilities) > - error = 1; > - break; > - case PR_SET_KEEPCAPS: > - if (arg2 != 0 && arg2 != 1) { > - error = -EINVAL; > - break; > - } > - current->keep_capabilities = arg2; > - break; > case PR_SET_NAME: { > struct task_struct *me = current; > unsigned char ncomm[sizeof(me->comm)]; > diff --git a/security/capability.c b/security/capability.c > index 9e99f36..8340655 100644 > --- a/security/capability.c > +++ b/security/capability.c > @@ -45,6 +45,7 @@ static struct security_operations capability_ops = { > .task_setioprio = cap_task_setioprio, > .task_setnice = cap_task_setnice, > .task_post_setuid = cap_task_post_setuid, > + .task_prctl = cap_task_prctl, > .task_reparent_to_init = cap_task_reparent_to_init, > > .syslog = cap_syslog, > diff --git a/security/commoncap.c b/security/commoncap.c > index d65ddd3..44bf3fc 100644 > --- a/security/commoncap.c > +++ b/security/commoncap.c > @@ -24,6 +24,7 @@ > #include <linux/hugetlb.h> > #include <linux/mount.h> > #include <linux/sched.h> > +#include <linux/prctl.h> > > #ifdef CONFIG_SECURITY_FILE_CAPABILITIES > /* > @@ -39,11 +40,6 @@ > kernel_cap_t cap_bset = CAP_INIT_BSET; /* systemwide capability bound */ > EXPORT_SYMBOL(cap_bset); > > -/* Global security state */ > - > -unsigned securebits = SECUREBITS_DEFAULT; /* systemwide security settings */ > -EXPORT_SYMBOL(securebits); > - > int cap_netlink_send(struct sock *sk, struct sk_buff *skb) > { > NETLINK_CB(skb).eff_cap = current->cap_effective; > @@ -164,6 +160,27 @@ static inline void bprm_clear_caps(struct linux_binprm > *bprm) > bprm->cap_effective = false; > } > > +static inline void bprm_force_uid0_caps(struct linux_binprm *bprm) > +{ > + /* > + * To support inheritance of root-permissions and suid-root > + * executables under compatibility mode, we raise all three > + * capability sets for the file. > + * > + * If only the real uid is 0, we only raise the inheritable > + * and permitted sets of the executable file. > + */ > + if (bprm->e_uid == 0 || current->uid == 0) { > + cap_set_full (bprm->cap_inheritable); > + cap_set_full (bprm->cap_permitted); > + } > + if (bprm->e_uid == 0) { > + bprm->cap_effective = true; > + } > + > + return; > +} > + > #ifdef CONFIG_SECURITY_FILE_CAPABILITIES > > int cap_inode_need_killpriv(struct dentry *dentry) > @@ -215,7 +232,7 @@ static inline int cap_from_disk(__le32 *caps, struct > linux_binprm *bprm, > } > > /* Locate any VFS capabilities: */ > -static int get_file_caps(struct linux_binprm *bprm) > +int cap_bprm_set_security(struct linux_binprm *bprm) > { > struct dentry *dentry; > int rc = 0; > @@ -223,8 +240,7 @@ static int get_file_caps(struct linux_binprm *bprm) > struct inode *inode; > > if (bprm->file->f_vfsmnt->mnt_flags & MNT_NOSUID) { > - bprm_clear_caps(bprm); > - return 0; > + goto out_no_dentry; > } > > dentry = dget(bprm->file->f_dentry); > @@ -249,8 +265,16 @@ static int get_file_caps(struct linux_binprm *bprm) > > out: > dput(dentry); > - if (rc) > + > + if (rc) { > +out_no_dentry: > bprm_clear_caps(bprm); > + } > + > + if (! current->keep_capabilities) { > + bprm_force_uid0_caps(bprm); > + rc = 0; > + } > > return rc; > } > @@ -266,42 +290,15 @@ int cap_inode_killpriv(struct dentry *dentry) > return 0; > } > > -static inline int get_file_caps(struct linux_binprm *bprm) > +int cap_bprm_set_security(struct linux_binprm *bprm) > { > bprm_clear_caps(bprm); > + bprm_force_uid0_caps(bprm); > + current->keep_capabilities = 0; > return 0; > } > #endif > > -int cap_bprm_set_security (struct linux_binprm *bprm) > -{ > - int ret; > - > - ret = get_file_caps(bprm); > - if (ret) > - printk(KERN_NOTICE "%s: get_file_caps returned %d for %s\n", > - __FUNCTION__, ret, bprm->filename); > - > - /* To support inheritance of root-permissions and suid-root > - * executables under compatibility mode, we raise all three > - * capability sets for the file. > - * > - * If only the real uid is 0, we only raise the inheritable > - * and permitted sets of the executable file. > - */ > - > - if (!issecure (SECURE_NOROOT)) { > - if (bprm->e_uid == 0 || current->uid == 0) { > - cap_set_full (bprm->cap_inheritable); > - cap_set_full (bprm->cap_permitted); > - } > - if (bprm->e_uid == 0) > - bprm->cap_effective = true; > - } > - > - return ret; > -} > - > void cap_bprm_apply_creds (struct linux_binprm *bprm, int unsafe) > { > /* Derived from fs/exec.c:compute_creds. */ > @@ -341,8 +338,6 @@ void cap_bprm_apply_creds (struct linux_binprm *bprm, int > unsafe) > } > > /* AUD: Audit candidate if current->cap_effective is set */ > - > - current->keep_capabilities = 0; > } > > int cap_bprm_secureexec (struct linux_binprm *bprm) > @@ -441,8 +436,7 @@ int cap_task_post_setuid (uid_t old_ruid, uid_t old_euid, > uid_t old_suid, > case LSM_SETID_RE: > case LSM_SETID_ID: > case LSM_SETID_RES: > - /* Copied from kernel/sys.c:setreuid/setuid/setresuid. */ > - if (!issecure (SECURE_NO_SETUID_FIXUP)) { > + if (! current->keep_capabilities) { > cap_emulate_setxuid (old_ruid, old_euid, old_suid); > } > break; > @@ -457,7 +451,7 @@ int cap_task_post_setuid (uid_t old_ruid, uid_t old_euid, > uid_t old_suid, > * if not, we might be a bit too harsh here. > */ > > - if (!issecure (SECURE_NO_SETUID_FIXUP)) { > + if (! current->keep_capabilities) { > if (old_fsuid == 0 && current->fsuid != 0) { > cap_t (current->cap_effective) &= > ~CAP_FS_MASK; > @@ -579,3 +573,53 @@ int cap_vm_enough_memory(struct mm_struct *mm, long > pages) > return __vm_enough_memory(mm, pages, cap_sys_admin); > } > > +int cap_task_prctl(int option, unsigned long arg2, unsigned long arg3, > + unsigned long arg4, unsigned long arg5, long *errorp) > +{ > + switch (option) { > + > +#ifdef CONFIG_SECURITY_FILE_CAPABILITIES > + > + case PR_GET_CAPONLY: > + *errorp = current->keep_capabilities; > + break; > + > + case PR_SET_CAPONLY: > + /* > + * Only permit setting to 1 > + */ > + if ((arg2 != 1) || !cap_capable(current, CAP_SETPCAP)) { > + *errorp = -EPERM; > + } else { > + /* > + * Once locked, no unlocking for this process > + * and its children > + */ > + current->keep_capabilities = 1; > + *errorp = 0; > + } > + break; > + > +#else /* ie. ndef CONFIG_SECURITY_FILE_CAPABILITIES */ > + > + case PR_GET_KEEPCAPS: > + if (current->keep_capabilities) > + *errorp = 1; > + break; > + > + case PR_SET_KEEPCAPS: > + if (arg2 != 0 && arg2 != 1) { > + *errorp = -EINVAL; > + break; > + } > + current->keep_capabilities = arg2; > + break; > + > +#endif /* def CONFIG_SECURITY_FILE_CAPABILITIES */ > + > + default: > + return 0; /* pass-through */ > + } > + > + return 1; /* overridden by capability LSM */ > +} > diff --git a/security/dummy.c b/security/dummy.c > index 88bb1bc..0497dc8 100644 > --- a/security/dummy.c > +++ b/security/dummy.c > @@ -566,8 +566,9 @@ static int dummy_task_kill (struct task_struct *p, struct > siginfo *info, > return 0; > } > > -static int dummy_task_prctl (int option, unsigned long arg2, unsigned long > arg3, > - unsigned long arg4, unsigned long arg5) > +static int dummy_task_prctl (int option, unsigned long arg2, > + unsigned long arg3, unsigned long arg4, > + unsigned long arg5, long *errorp) > { > return 0; > } > diff --git a/security/security.c b/security/security.c > index e6d53b3..e3a4285 100644 > --- a/security/security.c > +++ b/security/security.c > @@ -665,9 +665,10 @@ int security_task_wait(struct task_struct *p) > } > > int security_task_prctl(int option, unsigned long arg2, unsigned long arg3, > - unsigned long arg4, unsigned long arg5) > + unsigned long arg4, unsigned long arg5, long *errorp) > { > - return security_ops->task_prctl(option, arg2, arg3, arg4, arg5); > + return security_ops->task_prctl(option, arg2, arg3, arg4, arg5, > + errorp); > } > > void security_task_reparent_to_init(struct task_struct *p) > diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c > index dc196b9..c8aac50 100644 > --- a/security/selinux/hooks.c > +++ b/security/selinux/hooks.c > @@ -2913,12 +2913,14 @@ static int selinux_task_prctl(int option, > unsigned long arg2, > unsigned long arg3, > unsigned long arg4, > - unsigned long arg5) > + unsigned long arg5, > + long *errorp) > { > /* The current prctl operations do not appear to require > any SELinux controls since they merely observe or modify > the state of the current process. */ > - return 0; > + return secondary_ops->task_prctl(option, arg2, arg3, arg4, arg5, > + errorp); > } > > static int selinux_task_wait(struct task_struct *p) > -- > 1.5.1.3 > - To unsubscribe from this list: send the line "unsubscribe linux-security-module" in the body of a message to [EMAIL PROTECTED] More majordomo info at http://vger.kernel.org/majordomo-info.html