Return-Path: Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S966710AbWKOIzb (ORCPT ); Wed, 15 Nov 2006 03:55:31 -0500 Received: (majordomo@vger.kernel.org) by vger.kernel.org id S966709AbWKOIzb (ORCPT ); Wed, 15 Nov 2006 03:55:31 -0500 Received: from moutng.kundenserver.de ([212.227.126.177]:38623 "EHLO moutng.kundenserver.de") by vger.kernel.org with ESMTP id S966706AbWKOIz3 (ORCPT ); Wed, 15 Nov 2006 03:55:29 -0500 Date: Wed, 15 Nov 2006 09:55:06 +0100 From: Chris Friedhoff To: "Serge E. Hallyn" Cc: linux-kernel@vger.kernel.org, linux-security-module@vger.kernel.org, Stephen Smalley , James Morris , Chris Wright , Andrew Morton , KaiGai Kohei , Alexey Dobriyan Subject: Re: security: introduce file caps Message-Id: <20061115095506.91d1a221.chris@friedhoff.org> In-Reply-To: <20061114030655.GB31893@sergelap> References: <20061114030655.GB31893@sergelap> X-Mailer: Sylpheed version 2.2.10 (GTK+ 2.8.20; i486-slackware-linux-gnu) Mime-Version: 1.0 Content-Type: text/plain; charset=US-ASCII Content-Transfer-Encoding: 7bit X-Provags-ID: kundenserver.de abuse@kundenserver.de login:9d7f00276fac4b25ba506f26988c1e36 Sender: linux-kernel-owner@vger.kernel.org X-Mailing-List: linux-kernel@vger.kernel.org Content-Length: 15192 Lines: 488 Integrates, compiles and runs successfully on 2.6.18.2 ... so the System keeps on humming ... the page http://www.friedhoff.org/fscaps.html is updated ... congratulations for the acceptance in the 2.6.19-rc5-mm2 tree ... Chris On Mon, 13 Nov 2006 21:06:55 -0600 "Serge E. Hallyn" wrote: > From: Serge E. Hallyn > Subject: [PATCH 1/1] security: introduce file caps > > Implement file posix capabilities. This allows programs to be given > a subset of root's powers regardless of who runs them, without > having to use setuid and giving the binary all of root's powers. > > This version works with Kaigai Kohei's userspace tools, found at > http://www.kaigai.gr.jp/index.php. For more information on how to > use this patch, Chris Friedhoff has posted a nice page at > http://www.friedhoff.org/fscaps.html. > > Changelog: > Nov 13: > Integrate comments from Alexey: Remove CONFIG_ ifdef from > capability.h, and use %zd for printing a size_t. > > Nov 13: > Fix endianness warnings by sparse as suggested by Alexey > Dobriyan. > > Nov 09: > Address warnings of unused variables at cap_bprm_set_security > when file capabilities are disabled, and simultaneously clean > up the code a little, by pulling the new code into a helper > function. > > Nov 08: > For pointers to required userspace tools and how to use > them, see http://www.friedhoff.org/fscaps.html. > > Nov 07: > Fix the calculation of the highest bit checked in > check_cap_sanity(). > > Nov 07: > Allow file caps to be enabled without CONFIG_SECURITY, since > capabilities are the default. > Hook cap_task_setscheduler when !CONFIG_SECURITY. > Move capable(TASK_KILL) to end of cap_task_kill to reduce > audit messages. > > Nov 05: > Add secondary calls in selinux/hooks.c to task_setioprio and > task_setscheduler so that selinux and capabilities with file > cap support can be stacked. > > Sep 05: > As Seth Arnold points out, uid checks are out of place > for capability code. > > Sep 01: > Define task_setscheduler, task_setioprio, cap_task_kill, and > task_setnice to make sure a user cannot affect a process in which > they called a program with some fscaps. > > One remaining question is the note under task_setscheduler: are we > ok with CAP_SYS_NICE being sufficient to confine a process to a > cpuset? > > It is a semantic change, as without fsccaps, attach_task doesn't > allow CAP_SYS_NICE to override the uid equivalence check. But since > it uses security_task_setscheduler, which elsewhere is used where > CAP_SYS_NICE can be used to override the uid equivalence check, > fixing it might be tough. > > task_setscheduler > note: this also controls cpuset:attach_task. Are we ok with > CAP_SYS_NICE being used to confine to a cpuset? > task_setioprio > task_setnice > sys_setpriority uses this (through set_one_prio) for another > process. Need same checks as setrlimit > > Aug 21: > Updated secureexec implementation to reflect the fact that > euid and uid might be the same and nonzero, but the process > might still have elevated caps. > > Aug 15: > Handle endianness of xattrs. > Enforce capability version match between kernel and disk. > Enforce that no bits beyond the known max capability are > set, else return -EPERM. > With this extra processing, it may be worth reconsidering > doing all the work at bprm_set_security rather than > d_instantiate. > > Aug 10: > Always call getxattr at bprm_set_security, rather than > caching it at d_instantiate. > > Signed-off-by: Serge E. Hallyn > --- > include/linux/capability.h | 19 +++++ > include/linux/security.h | 12 ++- > security/Kconfig | 9 ++ > security/capability.c | 4 + > security/commoncap.c | 163 ++++++++++++++++++++++++++++++++++++++++++-- > security/selinux/hooks.c | 12 +++ > 6 files changed, 208 insertions(+), 11 deletions(-) > > diff --git a/include/linux/capability.h b/include/linux/capability.h > index 6548b35..9797eee 100644 > --- a/include/linux/capability.h > +++ b/include/linux/capability.h > @@ -39,12 +39,29 @@ typedef struct __user_cap_data_struct { > __u32 permitted; > __u32 inheritable; > } __user *cap_user_data_t; > + > + > +#define XATTR_CAPS_SUFFIX "capability" > +#define XATTR_NAME_CAPS XATTR_SECURITY_PREFIX XATTR_CAPS_SUFFIX > +struct vfs_cap_data_disk { > + __le32 version; > + __le32 effective; > + __le32 permitted; > + __le32 inheritable; > +}; > > #ifdef __KERNEL__ > > #include > #include > > +struct vfs_cap_data { > + __u32 version; > + __u32 effective; > + __u32 permitted; > + __u32 inheritable; > +}; > + > /* #define STRICT_CAP_T_TYPECHECKS */ > > #ifdef STRICT_CAP_T_TYPECHECKS > @@ -288,6 +305,8 @@ #define CAP_AUDIT_WRITE 29 > > #define CAP_AUDIT_CONTROL 30 > > +#define CAP_NUMCAPS 31 > + > #ifdef __KERNEL__ > /* > * Bounding set > diff --git a/include/linux/security.h b/include/linux/security.h > index b200b98..2718aeb 100644 > --- a/include/linux/security.h > +++ b/include/linux/security.h > @@ -53,6 +53,10 @@ extern int cap_inode_setxattr(struct den > extern int cap_inode_removexattr(struct dentry *dentry, char *name); > extern int cap_task_post_setuid (uid_t old_ruid, uid_t old_euid, uid_t old_suid, int flags); > extern void cap_task_reparent_to_init (struct task_struct *p); > +extern int cap_task_kill(struct task_struct *p, struct siginfo *info, int sig, u32 secid); > +extern int cap_task_setscheduler (struct task_struct *p, int policy, struct sched_param *lp); > +extern int cap_task_setioprio (struct task_struct *p, int ioprio); > +extern int cap_task_setnice (struct task_struct *p, int nice); > extern int cap_syslog (int type); > extern int cap_vm_enough_memory (long pages); > > @@ -2594,12 +2598,12 @@ static inline int security_task_setgroup > > static inline int security_task_setnice (struct task_struct *p, int nice) > { > - return 0; > + return cap_task_setnice(p, nice); > } > > static inline int security_task_setioprio (struct task_struct *p, int ioprio) > { > - return 0; > + return cap_task_setioprio(p, ioprio); > } > > static inline int security_task_getioprio (struct task_struct *p) > @@ -2617,7 +2621,7 @@ static inline int security_task_setsched > int policy, > struct sched_param *lp) > { > - return 0; > + return cap_task_setscheduler(p, policy, lp); > } > > static inline int security_task_getscheduler (struct task_struct *p) > @@ -2634,7 +2638,7 @@ static inline int security_task_kill (st > struct siginfo *info, int sig, > u32 secid) > { > - return 0; > + return cap_task_kill(p, info, sig, secid); > } > > static inline int security_task_wait (struct task_struct *p) > diff --git a/security/Kconfig b/security/Kconfig > index 460e5c9..6c9d69e 100644 > --- a/security/Kconfig > +++ b/security/Kconfig > @@ -80,6 +80,15 @@ config SECURITY_CAPABILITIES > This enables the "default" Linux capabilities functionality. > If you are unsure how to answer this question, answer Y. > > +config SECURITY_FS_CAPABILITIES > + bool "File POSIX Capabilities" > + default n > + help > + This enables filesystem capabilities, allowing you to give > + binaries a subset of root's powers without using setuid 0. > + > + If in doubt, answer N. > + > config SECURITY_ROOTPLUG > tristate "Root Plug Support" > depends on USB && SECURITY > diff --git a/security/capability.c b/security/capability.c > index b868e7e..14cb592 100644 > --- a/security/capability.c > +++ b/security/capability.c > @@ -40,6 +40,10 @@ static struct security_operations capabi > .inode_setxattr = cap_inode_setxattr, > .inode_removexattr = cap_inode_removexattr, > > + .task_kill = cap_task_kill, > + .task_setscheduler = cap_task_setscheduler, > + .task_setioprio = cap_task_setioprio, > + .task_setnice = cap_task_setnice, > .task_post_setuid = cap_task_post_setuid, > .task_reparent_to_init = cap_task_reparent_to_init, > > diff --git a/security/commoncap.c b/security/commoncap.c > index 5a5ef5c..0e89f1b 100644 > --- a/security/commoncap.c > +++ b/security/commoncap.c > @@ -109,11 +109,95 @@ void cap_capset_set (struct task_struct > target->cap_permitted = *permitted; > } > > +#ifdef CONFIG_SECURITY_FS_CAPABILITIES > +static inline void cap_from_disk(struct vfs_cap_data_disk *dcap, > + struct vfs_cap_data *cap) > +{ > + cap->version = le32_to_cpu(dcap->version); > + cap->effective = le32_to_cpu(dcap->effective); > + cap->permitted = le32_to_cpu(dcap->permitted); > + cap->inheritable = le32_to_cpu(dcap->inheritable); > +} > + > +static int check_cap_sanity(struct vfs_cap_data *cap) > +{ > + int i; > + > + if (cap->version != _LINUX_CAPABILITY_VERSION) > + return -EPERM; > + > + for (i=CAP_NUMCAPS; i<8*sizeof(cap->effective); i++) { > + if (cap->effective & CAP_TO_MASK(i)) > + return -EPERM; > + } > + for (i=CAP_NUMCAPS; i<8*sizeof(cap->permitted); i++) { > + if (cap->permitted & CAP_TO_MASK(i)) > + return -EPERM; > + } > + for (i=CAP_NUMCAPS; i<8*sizeof(cap->inheritable); i++) { > + if (cap->inheritable & CAP_TO_MASK(i)) > + return -EPERM; > + } > + > + return 0; > +} > + > +/* Locate any VFS capabilities: */ > +static int set_file_caps(struct linux_binprm *bprm) > +{ > + struct dentry *dentry; > + ssize_t rc; > + struct vfs_cap_data_disk dcaps; > + struct vfs_cap_data caps; > + struct inode *inode; > + > + dentry = dget(bprm->file->f_dentry); > + inode = dentry->d_inode; > + if (!inode->i_op || !inode->i_op->getxattr) { > + dput(dentry); > + return 0; > + } > + > + rc = inode->i_op->getxattr(dentry, XATTR_NAME_CAPS, &dcaps, > + sizeof(dcaps)); > + dput(dentry); > + > + if (rc == -ENODATA) > + return 0; > + > + if (rc < 0) { > + printk(KERN_NOTICE "%s: Error (%d) getting xattr\n", > + __FUNCTION__, rc); > + return rc; > + } > + > + if (rc != sizeof(dcaps)) { > + printk(KERN_NOTICE "%s: got wrong size for getxattr (%zd)\n", > + __FUNCTION__, rc); > + return -EPERM; > + } > + > + cap_from_disk(&dcaps, &caps); > + if (check_cap_sanity(&caps)) > + return -EPERM; > + > + bprm->cap_effective = caps.effective; > + bprm->cap_permitted = caps.permitted; > + bprm->cap_inheritable = caps.inheritable; > + > + return 0; > +} > +#else > +static int set_file_caps(struct linux_binprm *bprm) > +{ > + return 0; > +} > +#endif > + > int cap_bprm_set_security (struct linux_binprm *bprm) > { > /* Copied from fs/exec.c:prepare_binprm. */ > > - /* We don't have VFS support for capabilities yet */ > cap_clear (bprm->cap_inheritable); > cap_clear (bprm->cap_permitted); > cap_clear (bprm->cap_effective); > @@ -134,7 +218,8 @@ int cap_bprm_set_security (struct linux_ > if (bprm->e_uid == 0) > cap_set_full (bprm->cap_effective); > } > - return 0; > + > + return set_file_caps(bprm); > } > > void cap_bprm_apply_creds (struct linux_binprm *bprm, int unsafe) > @@ -182,11 +267,15 @@ void cap_bprm_apply_creds (struct linux_ > > int cap_bprm_secureexec (struct linux_binprm *bprm) > { > - /* If/when this module is enhanced to incorporate capability > - bits on files, the test below should be extended to also perform a > - test between the old and new capability sets. For now, > - it simply preserves the legacy decision algorithm used by > - the old userland. */ > + if (current->uid != 0) { > + if (!cap_isclear(bprm->cap_effective)) > + return 1; > + if (!cap_isclear(bprm->cap_permitted)) > + return 1; > + if (!cap_isclear(bprm->cap_inheritable)) > + return 1; > + } > + > return (current->euid != current->uid || > current->egid != current->gid); > } > @@ -300,6 +389,62 @@ int cap_task_post_setuid (uid_t old_ruid > return 0; > } > > +/* > + * Rationale: code calling task_setscheduler, task_setioprio, and > + * task_setnice, assumes that > + * . if capable(cap_sys_nice), then those actions should be allowed > + * . if not capable(cap_sys_nice), but acting on your own processes, > + * then those actions should be allowed > + * This is insufficient now since you can call code without suid, but > + * yet with increased caps. > + * So we check for increased caps on the target process. > + */ > +static inline int cap_safe_nice(struct task_struct *p) > +{ > + if (!cap_issubset(p->cap_permitted, current->cap_permitted) && > + !__capable(current, CAP_SYS_NICE)) > + return -EPERM; > + return 0; > +} > + > +int cap_task_setscheduler (struct task_struct *p, int policy, > + struct sched_param *lp) > +{ > + return cap_safe_nice(p); > +} > + > +int cap_task_setioprio (struct task_struct *p, int ioprio) > +{ > + return cap_safe_nice(p); > +} > + > +int cap_task_setnice (struct task_struct *p, int nice) > +{ > + return cap_safe_nice(p); > +} > + > +int cap_task_kill(struct task_struct *p, struct siginfo *info, > + int sig, u32 secid) > +{ > + if (info != SEND_SIG_NOINFO && (is_si_special(info) || SI_FROMKERNEL(info))) > + return 0; > + > + if (secid) > + /* > + * Signal sent as a particular user. > + * Capabilities are ignored. May be wrong, but it's the > + * only thing we can do at the moment. > + * Used only by usb drivers? > + */ > + return 0; > + if (cap_issubset(p->cap_permitted, current->cap_permitted)) > + return 0; > + if (capable(CAP_KILL)) > + return 0; > + > + return -EPERM; > +} > + > void cap_task_reparent_to_init (struct task_struct *p) > { > p->cap_effective = CAP_INIT_EFF_SET; > @@ -337,6 +482,10 @@ EXPORT_SYMBOL(cap_bprm_secureexec); > EXPORT_SYMBOL(cap_inode_setxattr); > EXPORT_SYMBOL(cap_inode_removexattr); > EXPORT_SYMBOL(cap_task_post_setuid); > +EXPORT_SYMBOL(cap_task_kill); > +EXPORT_SYMBOL(cap_task_setscheduler); > +EXPORT_SYMBOL(cap_task_setioprio); > +EXPORT_SYMBOL(cap_task_setnice); > EXPORT_SYMBOL(cap_task_reparent_to_init); > EXPORT_SYMBOL(cap_syslog); > EXPORT_SYMBOL(cap_vm_enough_memory); > diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c > index 8ab5679..2fcc60f 100644 > --- a/security/selinux/hooks.c > +++ b/security/selinux/hooks.c > @@ -2775,6 +2775,12 @@ static int selinux_task_setnice(struct t > > static int selinux_task_setioprio(struct task_struct *p, int ioprio) > { > + int rc; > + > + rc = secondary_ops->task_setioprio(p, ioprio); > + if (rc) > + return rc; > + > return task_has_perm(current, p, PROCESS__SETSCHED); > } > > @@ -2804,6 +2810,12 @@ static int selinux_task_setrlimit(unsign > > static int selinux_task_setscheduler(struct task_struct *p, int policy, struct sched_param *lp) > { > + int rc; > + > + rc = secondary_ops->task_setscheduler(p, policy, lp); > + if (rc) > + return rc; > + > return task_has_perm(current, p, PROCESS__SETSCHED); > } > > -- > 1.4.1 > -------------------- Chris Friedhoff chris@friedhoff.org - To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/