Date: Fri, 10 Aug 2012 12:41:51 +0000 From: gpf@FreeBSD.org To: svn-soc-all@FreeBSD.org Subject: socsvn commit: r240240 - in soc2012/gpf: pefs_head/head/sys/kern pefs_head/head/sys/security/mac pefs_head/head/sys/vm pefs_kmod/sys/fs/pefs Message-ID: <20120810124151.AB7B6106564A@hub.freebsd.org>
next in thread | raw e-mail | index | archive | help
Author: gpf Date: Fri Aug 10 12:41:51 2012 New Revision: 240240 URL: http://svnweb.FreeBSD.org/socsvn/?view=rev&rev=240240 Log: -introduce new MAC hook mac_vnode_check_exec_noscript() that will not be called for scripts; only for regular executables and interpreters. -use mac_vnode_check_mmap() to see if there's an attempt to mmap a vnode with the PROT_EXEC flag turned on. -introduce new MAC hook mac_vnode_set_mmap_maxprot() in an attempt to control mprotect(2) attempts with the PROT_EXEC flag. Please refer to the large comment headers that can be found in pefs_kmod/sys/fs/pefs/pefs_mac.c for a more thorough and up to date explanation of this svn commit. Modified: soc2012/gpf/pefs_head/head/sys/kern/kern_exec.c soc2012/gpf/pefs_head/head/sys/security/mac/mac_framework.h soc2012/gpf/pefs_head/head/sys/security/mac/mac_policy.h soc2012/gpf/pefs_head/head/sys/security/mac/mac_vfs.c soc2012/gpf/pefs_head/head/sys/vm/vm_mmap.c soc2012/gpf/pefs_kmod/sys/fs/pefs/pefs_mac.c Modified: soc2012/gpf/pefs_head/head/sys/kern/kern_exec.c ============================================================================== --- soc2012/gpf/pefs_head/head/sys/kern/kern_exec.c Fri Aug 10 11:11:04 2012 (r240239) +++ soc2012/gpf/pefs_head/head/sys/kern/kern_exec.c Fri Aug 10 12:41:51 2012 (r240240) @@ -548,21 +548,11 @@ goto interpret; } - //{ - ///* XXXgpf: [TODO] place this in a MAC hook */ - //int enabled, rval; - //size_t enabled_len; - - //rval = kernel_sysctlbyname(td, "vfs.pefs.exec.enable", - //&enabled, &enabled_len, NULL, 0, NULL, 0); - - //if (rval == 0 && enabled != 0) { - //if ((imgp->attr->va_flags & SF_IMMUTABLE) == 0) { - //error = EPERM; - //goto exec_fail_dealloc; - //} - //} - //} +#ifdef MAC + error = mac_vnode_check_exec_noscript(td->td_ucred, imgp->vp, imgp); + if (error) + goto exec_fail_dealloc; +#endif /* * NB: We unlock the vnode here because it is believed that none Modified: soc2012/gpf/pefs_head/head/sys/security/mac/mac_framework.h ============================================================================== --- soc2012/gpf/pefs_head/head/sys/security/mac/mac_framework.h Fri Aug 10 11:11:04 2012 (r240239) +++ soc2012/gpf/pefs_head/head/sys/security/mac/mac_framework.h Fri Aug 10 12:41:51 2012 (r240240) @@ -91,6 +91,7 @@ #include <sys/acl.h> /* XXX acl_type_t */ #include <sys/types.h> /* accmode_t */ +#include <vm/vm.h> /* XXX vm_prot_t */ /* * Entry points to the TrustedBSD MAC Framework from the remainder of the @@ -383,6 +384,8 @@ int attrnamespace, const char *name); int mac_vnode_check_exec(struct ucred *cred, struct vnode *vp, struct image_params *imgp); +int mac_vnode_check_exec_noscript(struct ucred *cred, struct vnode *vp, + struct image_params *imgp); int mac_vnode_check_getacl(struct ucred *cred, struct vnode *vp, acl_type_t type); int mac_vnode_check_getextattr(struct ucred *cred, struct vnode *vp, @@ -395,6 +398,8 @@ struct componentname *cnp); int mac_vnode_check_mmap(struct ucred *cred, struct vnode *vp, int prot, int flags); +void mac_vnode_set_mmap_maxprot(struct ucred *cred, struct vnode *vp, + vm_prot_t *maxprotp, int flags); int mac_vnode_check_mprotect(struct ucred *cred, struct vnode *vp, int prot); int mac_vnode_check_open(struct ucred *cred, struct vnode *vp, Modified: soc2012/gpf/pefs_head/head/sys/security/mac/mac_policy.h ============================================================================== --- soc2012/gpf/pefs_head/head/sys/security/mac/mac_policy.h Fri Aug 10 11:11:04 2012 (r240239) +++ soc2012/gpf/pefs_head/head/sys/security/mac/mac_policy.h Fri Aug 10 12:41:51 2012 (r240240) @@ -65,6 +65,7 @@ */ #include <sys/acl.h> /* XXX acl_type_t */ #include <sys/types.h> /* XXX accmode_t */ +#include <vm/vm.h> /* XXX vm_prot_t */ struct acl; struct auditinfo; @@ -566,6 +567,9 @@ typedef int (*mpo_vnode_check_exec_t)(struct ucred *cred, struct vnode *vp, struct label *vplabel, struct image_params *imgp, struct label *execlabel); +typedef int (*mpo_vnode_check_exec_noscript_t)(struct ucred *cred, + struct vnode *vp, struct label *vplabel, + struct image_params *imgp, struct label *execlabel); typedef int (*mpo_vnode_check_getacl_t)(struct ucred *cred, struct vnode *vp, struct label *vplabel, acl_type_t type); @@ -585,6 +589,9 @@ typedef int (*mpo_vnode_check_mmap_t)(struct ucred *cred, struct vnode *vp, struct label *label, int prot, int flags); +typedef void (*mpo_vnode_set_mmap_maxprot_t)(struct ucred *cred, + struct vnode *vp, struct label *label, vm_prot_t *maxprotp, + int flags); typedef void (*mpo_vnode_check_mmap_downgrade_t)(struct ucred *cred, struct vnode *vp, struct label *vplabel, int *prot); typedef int (*mpo_vnode_check_mprotect_t)(struct ucred *cred, @@ -922,12 +929,14 @@ mpo_vnode_check_deleteacl_t mpo_vnode_check_deleteacl; mpo_vnode_check_deleteextattr_t mpo_vnode_check_deleteextattr; mpo_vnode_check_exec_t mpo_vnode_check_exec; + mpo_vnode_check_exec_noscript_t mpo_vnode_check_exec_noscript; mpo_vnode_check_getacl_t mpo_vnode_check_getacl; mpo_vnode_check_getextattr_t mpo_vnode_check_getextattr; mpo_vnode_check_link_t mpo_vnode_check_link; mpo_vnode_check_listextattr_t mpo_vnode_check_listextattr; mpo_vnode_check_lookup_t mpo_vnode_check_lookup; mpo_vnode_check_mmap_t mpo_vnode_check_mmap; + mpo_vnode_set_mmap_maxprot_t mpo_vnode_set_mmap_maxprot; mpo_vnode_check_mmap_downgrade_t mpo_vnode_check_mmap_downgrade; mpo_vnode_check_mprotect_t mpo_vnode_check_mprotect; mpo_vnode_check_open_t mpo_vnode_check_open; Modified: soc2012/gpf/pefs_head/head/sys/security/mac/mac_vfs.c ============================================================================== --- soc2012/gpf/pefs_head/head/sys/security/mac/mac_vfs.c Fri Aug 10 11:11:04 2012 (r240239) +++ soc2012/gpf/pefs_head/head/sys/security/mac/mac_vfs.c Fri Aug 10 12:41:51 2012 (r240240) @@ -489,6 +489,24 @@ return (error); } +MAC_CHECK_PROBE_DEFINE3(vnode_check_exec_noscript, "struct ucred *", + "struct vnode *", "struct image_params *"); + +int +mac_vnode_check_exec_noscript(struct ucred *cred, struct vnode *vp, + struct image_params *imgp) +{ + int error; + + ASSERT_VOP_LOCKED(vp, "mac_vnode_check_exec_noscript"); + + MAC_POLICY_CHECK(vnode_check_exec_noscript, cred, vp, vp->v_label, imgp, + imgp->execlabel); + MAC_CHECK_PROBE3(vnode_check_exec_noscript, error, cred, vp, imgp); + + return (error); +} + MAC_CHECK_PROBE_DEFINE3(vnode_check_getacl, "struct ucred *", "struct vnode *", "acl_type_t"); @@ -597,6 +615,17 @@ } void +mac_vnode_set_mmap_maxprot(struct ucred *cred, struct vnode *vp, + vm_prot_t *maxprotp, int flags) +{ + + ASSERT_VOP_LOCKED(vp, "mac_vnode_set_mmap_maxprot"); + + MAC_POLICY_PERFORM(vnode_set_mmap_maxprot, cred, vp, vp->v_label, + maxprotp, flags); +} + +void mac_vnode_check_mmap_downgrade(struct ucred *cred, struct vnode *vp, int *prot) { Modified: soc2012/gpf/pefs_head/head/sys/vm/vm_mmap.c ============================================================================== --- soc2012/gpf/pefs_head/head/sys/vm/vm_mmap.c Fri Aug 10 11:11:04 2012 (r240239) +++ soc2012/gpf/pefs_head/head/sys/vm/vm_mmap.c Fri Aug 10 12:41:51 2012 (r240240) @@ -1295,6 +1295,7 @@ error = mac_vnode_check_mmap(cred, vp, prot, flags); if (error != 0) goto done; + mac_vnode_set_mmap_maxprot(cred, vp, maxprotp, flags); #endif if ((flags & MAP_SHARED) != 0) { if ((va.va_flags & (SF_SNAPSHOT|IMMUTABLE|APPEND)) != 0) { Modified: soc2012/gpf/pefs_kmod/sys/fs/pefs/pefs_mac.c ============================================================================== --- soc2012/gpf/pefs_kmod/sys/fs/pefs/pefs_mac.c Fri Aug 10 11:11:04 2012 (r240239) +++ soc2012/gpf/pefs_kmod/sys/fs/pefs/pefs_mac.c Fri Aug 10 12:41:51 2012 (r240240) @@ -33,6 +33,7 @@ #include <sys/systm.h> #include <sys/kernel.h> #include <sys/dirent.h> +#include <sys/mman.h> #include <sys/module.h> #include <sys/mount.h> #include <sys/sysctl.h> @@ -40,6 +41,8 @@ #include <sys/vnode.h> #include <sys/imgact.h> +#include <vm/vm.h> + #include <fs/pefs/pefs.h> #include <security/mac/mac_policy.h> @@ -49,24 +52,24 @@ * The problem with this MAC hook is that the hook is called before * do_execve() checks if our executable requires an interpreter. * Therefore, the script file will itself be checked for the schg flag. - * + * * We could: - * + * * a) allow this because it's a feature! During development of a script, - * user will have to pass it as an argument to the interpreter and when it's + * user will have to pass it as an argument to the interpreter and when it's * complete, continue calling it like that or add the schg flag. - * + * * b) add a brand new MAC hook that will be called at the precise point - * in do_execve() where only the interpreter or the regular executable + * in do_execve() where only the interpreter or the regular executable * will be checked for the schg flag. [don't seem the other devs will go * for us modifying MAC framework though] - * - * c) duplicate code from do_execve() and perform the check ourselves. It + * + * c) duplicate code from do_execve() and perform the check ourselves. It * could be done I guess but I'm not sure since image activators seem to have * their own custom functions that are called in order to figure out whether * the interpreted flag should be turned on. Don't know how much they are * allowed to tamper with imgp, besides that flag. - * + * */ static int pefs_vnode_check_exec(struct ucred *cred, struct vnode *vp, @@ -88,18 +91,128 @@ return (0); } +/* + * XXXgpf: + * + * This new hook is placed in do_execve(), found in sys/kern/kern_exec.c. + * Its purpose is to only check the interpreter for the schg flag in case + * there's an executable that requires an interpreter. + * + * It is placed after exec_check_permissions() and it will be called after + * we've looped back for the interpreter. Therefore, only either the interpeter + * or the regular executable itself will ever be checked by this hook; we'll + * never check the script file itself. + * + * It uses a different dbg sysctl var than the above hook for obvious reasons. + */ +static int +pefs_vnode_check_exec_noscript(struct ucred *cred, struct vnode *vp, + struct label *vplabel, struct image_params *imgp, + struct label *execlabel) +{ + int enabled, rval; + size_t enabled_len; + + rval = kernel_sysctlbyname(curthread, "vfs.pefs.exec.enable_noscript", + &enabled, &enabled_len, NULL, 0, NULL, 0); + + if (rval == 0 && enabled != 0) { + if ((imgp->attr->va_flags & SF_IMMUTABLE) == 0) + return (EPERM); + } + + return (0); +} + + +/* + * XXXgpf: Check if schg is turned on when we mmap() a vnode with + * the PROT_EXEC flag. + */ +static int +pefs_vnode_check_mmap(struct ucred *cred, struct vnode *vp, + struct label *vplabel, int prot, int flags) +{ + struct vattr va; + int enabled1, enabled2, error, rval1, rval2; + size_t enabled_len; + + if ((prot & PROT_EXEC) == 0) + return (0); + + rval1 = kernel_sysctlbyname(curthread, "vfs.pefs.exec.enable", + &enabled1, &enabled_len, NULL, 0, NULL, 0); + + rval2 = kernel_sysctlbyname(curthread, "vfs.pefs.exec.enable_noscript", + &enabled2, &enabled_len, NULL, 0, NULL, 0); + + if ((rval1 == 0 && enabled1 != 0) || (rval2 == 0 && enabled2 != 0)) { + error = VOP_GETATTR(vp, &va, cred); + if (error != 0) + return (EACCES); + + if ((va.va_flags & SF_IMMUTABLE) == 0) + return (EACCES); + } + + return (0); +} + +/* + * XXXgpf: + * + * Checking mmap(2) with the above MAC hook is not enough if we wish to + * prevent user from mmaping a file and then executing those pages due to + * mprotect(2). + * + * I did notice the existance of mac_vnode_check_mprotect(), but unfortunately + * it's not used anywhere in the kernel for some reason(?)! If it ever comes + * back into action, I believe it would be preferable to the following solution. + * + * My alternative solution was to set the MAXPROT flag of the mapped area + * during mmap(). If we are mapping a file and we need schg protection, we + * remove VM_PROT_EXECUTE from MAXPROT which in turn causes following attempts + * to mprotect() with PROT_EXEC to fail. + */ +static void +pefs_vnode_set_mmap_maxprot(struct ucred *cred, struct vnode *vp, + struct label *vplabel, vm_prot_t *maxprotp, int flags) +{ + int enabled1, enabled2, rval1, rval2; + size_t enabled_len; + + if ((*maxprotp & VM_PROT_EXECUTE) == 0) + return; + + rval1 = kernel_sysctlbyname(curthread, "vfs.pefs.exec.enable", + &enabled1, &enabled_len, NULL, 0, NULL, 0); + + rval2 = kernel_sysctlbyname(curthread, "vfs.pefs.exec.enable_noscript", + &enabled2, &enabled_len, NULL, 0, NULL, 0); + + if ((rval1 == 0 && enabled1 != 0) || (rval2 == 0 && enabled2 != 0)) + *maxprotp &= ~VM_PROT_EXECUTE; +} + static struct mac_policy_ops pefs_ops = { .mpo_vnode_check_exec = pefs_vnode_check_exec, + .mpo_vnode_check_exec_noscript = pefs_vnode_check_exec_noscript, + .mpo_vnode_check_mmap = pefs_vnode_check_mmap, + .mpo_vnode_set_mmap_maxprot = pefs_vnode_set_mmap_maxprot, }; MAC_POLICY_SET(&pefs_ops, mac_pefs, "pefs exec protection", MPC_LOADTIME_FLAG_UNLOADOK, NULL); -/* XXXgpf: declare our debugging sysctl for kern_exec.c */ +/* XXXgpf: declare our debugging sysctls for schg execution control */ SYSCTL_NODE(_vfs_pefs, OID_AUTO, exec, CTLFLAG_RW, 0, "PEFS kern_exec.c stuff"); int pefs_exec_enable = 0; SYSCTL_INT(_vfs_pefs_exec, OID_AUTO, enable, CTLFLAG_RW, &pefs_exec_enable, 0, "Enable exec protection"); + +int pefs_exec_enable_noscript = 0; +SYSCTL_INT(_vfs_pefs_exec, OID_AUTO, enable_noscript, CTLFLAG_RW, + &pefs_exec_enable_noscript, 0, "Enable exec protection [no scripts]");
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?20120810124151.AB7B6106564A>