diff --git a/lib/libugidfw/ugidfw.c b/lib/libugidfw/ugidfw.c index 0dc423d..a4a38bc 100644 --- a/lib/libugidfw/ugidfw.c +++ b/lib/libugidfw/ugidfw.c @@ -36,6 +36,9 @@ #include #include #include +#include +#include +#include #include @@ -44,6 +47,8 @@ #include #include #include +#include +#include #include "ugidfw.h" @@ -329,14 +334,19 @@ bsde_rule_to_string(struct mac_bsdextended_rule *rule, char *buf, size_t buflen) cur += len; } if (rule->mbr_object.mbo_flags & MBO_FSID_DEFINED) { - numfs = getmntinfo(&mntbuf, MNT_NOWAIT); - for (i = 0; i < numfs; i++) - if (memcmp(&(rule->mbr_object.mbo_fsid), - &(mntbuf[i].f_fsid), - sizeof(mntbuf[i].f_fsid)) == 0) - break; - len = snprintf(cur, left, "filesys %s ", - i == numfs ? "???" : mntbuf[i].f_mntonname); + if (rule->mbr_object.mbo_inode == 0) { + numfs = getmntinfo(&mntbuf, MNT_NOWAIT); + for (i = 0; i < numfs; i++) + if (memcmp(&(rule->mbr_object.mbo_fsid), + &(mntbuf[i].f_fsid), + sizeof(mntbuf[i].f_fsid)) == 0) + break; + len = snprintf(cur, left, "filesys %s ", + i == numfs ? "???" : mntbuf[i].f_mntonname); + } else { + len = snprintf(cur, left, "filesys %s ", + rule->mbr_object.mbo_paxpath); + } if (len < 0 || len > left) goto truncated; left -= len; @@ -500,6 +510,33 @@ bsde_rule_to_string(struct mac_bsdextended_rule *rule, char *buf, size_t buflen) cur += len; } + if (rule->mbr_pax) { + len = snprintf(cur, left, " paxflags "); + if (len < 0 || len > left) + goto truncated; + left -= len; + cur += len; + + if (rule->mbr_pax & MBI_FORCE_ASLR_ENABLED) { + len = snprintf(cur, left, "A"); + if (len < 0 || len > left) + goto truncated; + + left -= len; + cur += len; + } + + if (rule->mbr_pax & MBI_FORCE_ASLR_DISABLED) { + len = snprintf(cur, left, "a"); + if (len < 0 || len > left) + goto truncated; + + left -= len; + cur += len; + } + + } + return (0); truncated: @@ -507,8 +544,8 @@ truncated: } int -bsde_parse_uidrange(char *spec, uid_t *min, uid_t *max, - size_t buflen, char *errstr){ +bsde_parse_uidrange(char *spec, uid_t *min, uid_t *max, size_t buflen, char *errstr) +{ struct passwd *pwd; uid_t uid1, uid2; char *spec1, *spec2, *endp; @@ -556,8 +593,8 @@ bsde_parse_uidrange(char *spec, uid_t *min, uid_t *max, } int -bsde_parse_gidrange(char *spec, gid_t *min, gid_t *max, - size_t buflen, char *errstr){ +bsde_parse_gidrange(char *spec, gid_t *min, gid_t *max, size_t buflen, char *errstr) +{ struct group *grp; gid_t gid1, gid2; char *spec1, *spec2, *endp; @@ -766,10 +803,15 @@ bsde_parse_type(char *spec, int *type, size_t buflen, char *errstr) } int -bsde_parse_fsid(char *spec, struct fsid *fsid, size_t buflen, char *errstr) +bsde_parse_fsid(char *spec, struct fsid *fsid, ino_t *inode, size_t buflen, char *errstr) { size_t len; struct statfs buf; + struct stat sb; + int fd, paxstatus; + size_t bufsz; + + *inode = 0; if (statfs(spec, &buf) < 0) { len = snprintf(errstr, buflen, "Unable to get id for %s: %s", @@ -779,6 +821,21 @@ bsde_parse_fsid(char *spec, struct fsid *fsid, size_t buflen, char *errstr) *fsid = buf.f_fsid; + if (strcmp(buf.f_fstypename, "devfs") != 0) { + bufsz = sizeof(int); + if (!sysctlbyname("kern.features.aslr", &paxstatus, &bufsz, + NULL, 0)) { + fd = open(spec, O_RDONLY); + if (fd != -1) { + if (fstat(fd, &sb) == 0) + if(S_ISDIR(sb.st_mode) == 0) + *inode = sb.st_ino; + + close(fd); + } + } + } + return (0); } @@ -852,13 +909,18 @@ bsde_parse_object(int argc, char *argv[], return (-1); } if (bsde_parse_fsid(argv[current+1], &fsid, - buflen, errstr) < 0) + &object->mbo_inode, buflen, errstr) < 0) return (-1); flags |= MBO_FSID_DEFINED; if (nextnot) { neg ^= MBO_FSID_DEFINED; nextnot = 0; } + if (object->mbo_inode) + snprintf(object->mbo_paxpath, MAXPATHLEN, "%s", + argv[current+1]); + else + memset(object->mbo_paxpath, 0x00, MAXPATHLEN); current += 2; } else if (strcmp(argv[current], "suid") == 0) { flags |= MBO_SUID; @@ -991,12 +1053,48 @@ bsde_parse_mode(int argc, char *argv[], mode_t *mode, size_t buflen, } int +bsde_parse_paxflags(int argc, char *argv[], uint32_t *pax, size_t buflen, char *errstr) +{ + size_t len; + int i; + + if (argc == 0) { + len = snprintf(errstr, buflen, "paxflags expects mode value"); + return (-1); + } + + if (argc != 1) { + len = snprintf(errstr, buflen, "'%s' unexpected", argv[1]); + return (-1); + } + + *pax = 0; + for (i = 0; i < strlen(argv[0]); i++) { + switch (argv[0][i]) { + case 'A': + *pax |= MBI_FORCE_ASLR_ENABLED; + break; + case 'a': + *pax |= MBI_FORCE_ASLR_DISABLED; + break; + default: + len = snprintf(errstr, buflen, "Unknown mode letter: %c", + argv[0][i]); + return (-1); + } + } + + return (0); +} + +int bsde_parse_rule(int argc, char *argv[], struct mac_bsdextended_rule *rule, size_t buflen, char *errstr) { int subject, subject_elements, subject_elements_length; int object, object_elements, object_elements_length; int mode, mode_elements, mode_elements_length; + int paxflags, paxflags_elements, paxflags_elements_length=0; int error, i; size_t len; @@ -1037,11 +1135,23 @@ bsde_parse_rule(int argc, char *argv[], struct mac_bsdextended_rule *rule, return (-1); } + /* Search forward for paxflags */ + paxflags = -1; + for (i = 1; i < argc; i++) + if (strcmp(argv[i], "paxflags") == 0) + paxflags = i; + + if (paxflags >= 0) { + paxflags_elements = paxflags + 1; + paxflags_elements_length = argc - paxflags_elements; + } + subject_elements_length = object - subject - 1; object_elements = object + 1; object_elements_length = mode - object_elements; mode_elements = mode + 1; - mode_elements_length = argc - mode_elements; + mode_elements_length = argc - mode_elements - + (paxflags_elements_length ? paxflags_elements_length+1 : 0); error = bsde_parse_subject(subject_elements_length, argv + subject_elements, &rule->mbr_subject, buflen, errstr); @@ -1058,6 +1168,13 @@ bsde_parse_rule(int argc, char *argv[], struct mac_bsdextended_rule *rule, if (error) return (-1); + if (paxflags >= 0) { + error = bsde_parse_paxflags(paxflags_elements_length, argv + paxflags_elements, + &rule->mbr_pax, buflen, errstr); + if (error) + return (-1); + } + return (0); } diff --git a/lib/libugidfw/ugidfw.h b/lib/libugidfw/ugidfw.h index 5b7fcf2..cef469c 100644 --- a/lib/libugidfw/ugidfw.h +++ b/lib/libugidfw/ugidfw.h @@ -39,6 +39,8 @@ int bsde_rule_to_string(struct mac_bsdextended_rule *rule, char *buf, size_t buflen); int bsde_parse_mode(int argc, char *argv[], mode_t *mode, size_t buflen, char *errstr); +int bsde_parse_paxflags(int argc, char *argv[], uint32_t *pax, size_t buflen, + char *errstr); int bsde_parse_rule(int argc, char *argv[], struct mac_bsdextended_rule *rule, size_t buflen, char *errstr); int bsde_parse_rule_string(const char *string, diff --git a/sys/amd64/amd64/elf_machdep.c b/sys/amd64/amd64/elf_machdep.c index fdc4d56..ffb5e31 100644 --- a/sys/amd64/amd64/elf_machdep.c +++ b/sys/amd64/amd64/elf_machdep.c @@ -26,12 +26,17 @@ #include __FBSDID("$FreeBSD$"); +#include "opt_pax.h" + #include #include #include #include #include #include +#ifdef PAX_ASLR +#include +#endif #include #include #include @@ -81,6 +86,11 @@ struct sysentvec elf64_freebsd_sysvec = { .sv_shared_page_base = SHAREDPAGE, .sv_shared_page_len = PAGE_SIZE, .sv_schedtail = NULL, +#ifdef PAX_ASLR + .sv_pax_aslr_init = _pax_aslr_init, +#else + .sv_pax_aslr_init = NULL, +#endif }; INIT_SYSENTVEC(elf64_sysvec, &elf64_freebsd_sysvec); diff --git a/sys/amd64/include/vmparam.h b/sys/amd64/include/vmparam.h index bda9722..5e83a8f 100644 --- a/sys/amd64/include/vmparam.h +++ b/sys/amd64/include/vmparam.h @@ -170,7 +170,7 @@ #define VM_MAXUSER_ADDRESS UVADDR(NUPML4E, 0, 0, 0) #define SHAREDPAGE (VM_MAXUSER_ADDRESS - PAGE_SIZE) -#define USRSTACK SHAREDPAGE +#define USRSTACK (SHAREDPAGE - 4*PAGE_SIZE) #define VM_MAX_ADDRESS UPT_MAX_ADDRESS #define VM_MIN_ADDRESS (0) diff --git a/sys/amd64/linux32/linux32_sysvec.c b/sys/amd64/linux32/linux32_sysvec.c index c06ce11..f4f99f58 100644 --- a/sys/amd64/linux32/linux32_sysvec.c +++ b/sys/amd64/linux32/linux32_sysvec.c @@ -33,6 +33,7 @@ #include __FBSDID("$FreeBSD$"); #include "opt_compat.h" +#include "opt_pax.h" #ifndef COMPAT_FREEBSD32 #error "Unable to compile Linux-emulator due to missing COMPAT_FREEBSD32 option!" @@ -84,6 +85,10 @@ __FBSDID("$FreeBSD$"); #include #include +#ifdef PAX_ASLR +#include +#endif + MODULE_VERSION(linux, 1); MALLOC_DEFINE(M_LINUX, "linux", "Linux mode structures"); @@ -1037,6 +1042,11 @@ struct sysentvec elf_linux_sysvec = { .sv_shared_page_base = LINUX32_SHAREDPAGE, .sv_shared_page_len = PAGE_SIZE, .sv_schedtail = linux_schedtail, +#ifdef PAX_ASLR + .sv_pax_aslr_init = _pax_aslr_init32, +#else + .sv_pax_aslr_init = NULL, +#endif }; INIT_SYSENTVEC(elf_sysvec, &elf_linux_sysvec); diff --git a/sys/arm/arm/elf_machdep.c b/sys/arm/arm/elf_machdep.c index 8ef9bd4..26e37e6 100644 --- a/sys/arm/arm/elf_machdep.c +++ b/sys/arm/arm/elf_machdep.c @@ -26,6 +26,8 @@ #include __FBSDID("$FreeBSD$"); +#include "opt_pax.h" + #include #include #include @@ -46,6 +48,10 @@ __FBSDID("$FreeBSD$"); #include #include +#ifdef PAX_ASLR +#include +#endif + struct sysentvec elf32_freebsd_sysvec = { .sv_size = SYS_MAXSYSCALL, .sv_table = sysent, @@ -79,6 +85,11 @@ struct sysentvec elf32_freebsd_sysvec = { .sv_fetch_syscall_args = cpu_fetch_syscall_args, .sv_syscallnames = syscallnames, .sv_schedtail = NULL, +#ifdef PAX_ASLR + .sv_pax_aslr_init = _pax_aslr_init, +#else + .sv_pax_aslr_init = NULL, +#endif }; static Elf32_Brandinfo freebsd_brand_info = { diff --git a/sys/compat/freebsd32/freebsd32_misc.c b/sys/compat/freebsd32/freebsd32_misc.c index 68e761b..96a81d9 100644 --- a/sys/compat/freebsd32/freebsd32_misc.c +++ b/sys/compat/freebsd32/freebsd32_misc.c @@ -30,6 +30,7 @@ __FBSDID("$FreeBSD$"); #include "opt_compat.h" #include "opt_inet.h" #include "opt_inet6.h" +#include "opt_pax.h" #define __ELF_WORD_SIZE 32 @@ -113,6 +114,10 @@ __FBSDID("$FreeBSD$"); FEATURE(compat_freebsd_32bit, "Compatible with 32-bit FreeBSD"); +#ifdef PAX_ASLR +#include +#endif + #ifndef __mips__ CTASSERT(sizeof(struct timeval32) == 8); CTASSERT(sizeof(struct timespec32) == 8); @@ -2886,6 +2891,10 @@ freebsd32_copyout_strings(struct image_params *imgp) szsigcode = 0; destp = (uintptr_t)arginfo; +#ifdef PAX_ASLR + pax_aslr_stack(curthread, &destp); +#endif + /* * install sigcode */ diff --git a/sys/compat/ia32/ia32_sysvec.c b/sys/compat/ia32/ia32_sysvec.c index a8e52e8..ade8da5 100644 --- a/sys/compat/ia32/ia32_sysvec.c +++ b/sys/compat/ia32/ia32_sysvec.c @@ -29,6 +29,7 @@ __FBSDID("$FreeBSD$"); #include "opt_compat.h" +#include "opt_pax.h" #define __ELF_WORD_SIZE 32 @@ -74,6 +75,10 @@ __FBSDID("$FreeBSD$"); #include #include +#ifdef PAX_ASLR +#include +#endif + CTASSERT(sizeof(struct ia32_mcontext) == 640); CTASSERT(sizeof(struct ia32_ucontext) == 704); CTASSERT(sizeof(struct ia32_sigframe) == 800); @@ -139,6 +144,11 @@ struct sysentvec ia32_freebsd_sysvec = { .sv_shared_page_base = FREEBSD32_SHAREDPAGE, .sv_shared_page_len = PAGE_SIZE, .sv_schedtail = NULL, +#ifdef PAX_ASLR + .sv_pax_aslr_init = _pax_aslr_init32, +#else + .sv_pax_aslr_init = NULL, +#endif }; INIT_SYSENTVEC(elf_ia32_sysvec, &ia32_freebsd_sysvec); diff --git a/sys/conf/NOTES b/sys/conf/NOTES index 4d27713..671ef83 100644 --- a/sys/conf/NOTES +++ b/sys/conf/NOTES @@ -2972,3 +2972,7 @@ options RANDOM_RWFILE # Read and write entropy cache # Module to enable execution of application via emulators like QEMU options IMAGACT_BINMISC + +# Address Space Layout Randomization (ASLR) +options PAX_ASLR +options PAX_SYSCTLS diff --git a/sys/conf/files b/sys/conf/files index cc907c50..1e73c88 100644 --- a/sys/conf/files +++ b/sys/conf/files @@ -2907,6 +2907,9 @@ kern/kern_mtxpool.c standard kern/kern_mutex.c standard kern/kern_ntptime.c standard kern/kern_osd.c standard +kern/kern_pax.c optional pax_aslr +kern/kern_pax_aslr.c optional pax_aslr +kern/kern_pax_log.c optional pax_aslr kern/kern_physio.c standard kern/kern_pmc.c standard kern/kern_poll.c optional device_polling diff --git a/sys/conf/options b/sys/conf/options index 32fb4d4..6e81e4e 100644 --- a/sys/conf/options +++ b/sys/conf/options @@ -916,6 +916,12 @@ RACCT opt_global.h # Resource Limits RCTL opt_global.h +# PaX - hardening options +PAX_ASLR opt_pax.h +PAX_ASLR_MAX_SEC opt_pax.h +PAX_MPROTECT opt_pax.h +PAX_SYSCTLS opt_pax.h + # Random number generator(s) RANDOM_YARROW opt_random.h RANDOM_FORTUNA opt_random.h diff --git a/sys/i386/i386/elf_machdep.c b/sys/i386/i386/elf_machdep.c index 034b4c4..9571252 100644 --- a/sys/i386/i386/elf_machdep.c +++ b/sys/i386/i386/elf_machdep.c @@ -26,6 +26,8 @@ #include __FBSDID("$FreeBSD$"); +#include "opt_pax.h" + #include #include #include @@ -46,6 +48,10 @@ __FBSDID("$FreeBSD$"); #include #include +#ifdef PAX_ASLR +#include +#endif + struct sysentvec elf32_freebsd_sysvec = { .sv_size = SYS_MAXSYSCALL, .sv_table = sysent, @@ -81,6 +87,11 @@ struct sysentvec elf32_freebsd_sysvec = { .sv_shared_page_base = SHAREDPAGE, .sv_shared_page_len = PAGE_SIZE, .sv_schedtail = NULL, +#ifdef PAX_ASLR + .sv_pax_aslr_init = _pax_aslr_init, +#else + .sv_pax_aslr_init = NULL, +#endif }; INIT_SYSENTVEC(elf32_sysvec, &elf32_freebsd_sysvec); diff --git a/sys/i386/ibcs2/ibcs2_sysvec.c b/sys/i386/ibcs2/ibcs2_sysvec.c index 5d007c7..1bb9d89 100644 --- a/sys/i386/ibcs2/ibcs2_sysvec.c +++ b/sys/i386/ibcs2/ibcs2_sysvec.c @@ -31,6 +31,8 @@ #include __FBSDID("$FreeBSD$"); +#include "opt_pax.h" + #include #include #include @@ -50,6 +52,10 @@ __FBSDID("$FreeBSD$"); #include #include +#ifdef PAX_ASLR +#include +#endif + MODULE_VERSION(ibcs2, 1); extern int bsd_to_ibcs2_errno[]; @@ -89,6 +95,11 @@ struct sysentvec ibcs2_svr3_sysvec = { .sv_fetch_syscall_args = cpu_fetch_syscall_args, .sv_syscallnames = NULL, .sv_schedtail = NULL, +#ifdef PAX_ASLR + .sv_pax_aslr_init = _pax_aslr_init, /* XXXOP */ +#else + .sv_pax_aslr_init = NULL, +#endif }; static int diff --git a/sys/i386/linux/linux_sysvec.c b/sys/i386/linux/linux_sysvec.c index 0ad6791..403070c 100644 --- a/sys/i386/linux/linux_sysvec.c +++ b/sys/i386/linux/linux_sysvec.c @@ -29,6 +29,8 @@ #include __FBSDID("$FreeBSD$"); +#include "opt_pax.h" + #include #include #include @@ -72,6 +74,10 @@ __FBSDID("$FreeBSD$"); #include #include +#ifdef PAX_ASLR +#include +#endif + MODULE_VERSION(linux, 1); MALLOC_DEFINE(M_LINUX, "linux", "Linux mode structures"); @@ -974,6 +980,11 @@ struct sysentvec linux_sysvec = { .sv_shared_page_base = LINUX_SHAREDPAGE, .sv_shared_page_len = PAGE_SIZE, .sv_schedtail = linux_schedtail, +#ifdef PAX_ASLR + .sv_pax_aslr_init = _pax_aslr_init, /* XXXOP */ +#else + .sv_pax_aslr_init = NULL, +#endif }; INIT_SYSENTVEC(aout_sysvec, &linux_sysvec); @@ -1012,6 +1023,11 @@ struct sysentvec elf_linux_sysvec = { .sv_shared_page_base = LINUX_SHAREDPAGE, .sv_shared_page_len = PAGE_SIZE, .sv_schedtail = linux_schedtail, +#ifdef PAX_ASLR + .sv_pax_aslr_init = _pax_aslr_init, +#else + .sv_pax_aslr_init = NULL, +#endif }; INIT_SYSENTVEC(elf_sysvec, &elf_linux_sysvec); diff --git a/sys/ia64/ia64/elf_machdep.c b/sys/ia64/ia64/elf_machdep.c index 05cb641..e3d19c1 100644 --- a/sys/ia64/ia64/elf_machdep.c +++ b/sys/ia64/ia64/elf_machdep.c @@ -25,6 +25,8 @@ * $FreeBSD$ */ +#include "opt_pax.h" + #include #include #include @@ -49,6 +51,10 @@ #include #include +#ifdef PAX_ASLR +#include +#endif + Elf_Addr link_elf_get_gp(linker_file_t); extern Elf_Addr fptr_storage[]; @@ -86,6 +92,12 @@ struct sysentvec elf64_freebsd_sysvec = { .sv_fetch_syscall_args = cpu_fetch_syscall_args, .sv_syscallnames = syscallnames, .sv_schedtail = NULL, +#ifdef PAX_ASLR + .sv_pax_aslr_init = _pax_aslr_init, +#else + .sv_pax_aslr_init = NULL, +#endif + }; static Elf64_Brandinfo freebsd_brand_info = { diff --git a/sys/kern/imgact_aout.c b/sys/kern/imgact_aout.c index 3ae78de..aac03f1 100644 --- a/sys/kern/imgact_aout.c +++ b/sys/kern/imgact_aout.c @@ -27,6 +27,8 @@ #include __FBSDID("$FreeBSD$"); +#include "opt_pax.h" + #include #include #include @@ -62,6 +64,10 @@ __FBSDID("$FreeBSD$"); #include #endif +#ifdef PAX_ASLR +#include +#endif + static int exec_aout_imgact(struct image_params *imgp); static int aout_fixup(register_t **stack_base, struct image_params *imgp); @@ -99,6 +105,11 @@ struct sysentvec aout_sysvec = { .sv_fetch_syscall_args = cpu_fetch_syscall_args, .sv_syscallnames = syscallnames, .sv_schedtail = NULL, +#ifdef PAX_ASLR + .sv_pax_aslr_init = _pax_aslr_init, /* XXXOP */ +#else + .sv_pax_aslr_init = NULL, +#endif }; #elif defined(__amd64__) @@ -143,6 +154,11 @@ struct sysentvec aout_sysvec = { .sv_set_syscall_retval = ia32_set_syscall_retval, .sv_fetch_syscall_args = ia32_fetch_syscall_args, .sv_syscallnames = freebsd32_syscallnames, +#ifdef PAX_ASLR + .sv_pax_aslr_init = _pax_aslr_init32, /* XXXOP */ +#else + .sv_pax_aslr_init = NULL, +#endif }; #else #error "Port me" diff --git a/sys/kern/imgact_elf.c b/sys/kern/imgact_elf.c index 591094e..d0e01d3 100644 --- a/sys/kern/imgact_elf.c +++ b/sys/kern/imgact_elf.c @@ -34,6 +34,7 @@ __FBSDID("$FreeBSD$"); #include "opt_capsicum.h" #include "opt_compat.h" #include "opt_core.h" +#include "opt_pax.h" #include #include @@ -48,6 +49,7 @@ __FBSDID("$FreeBSD$"); #include #include #include +#include #include #include #include @@ -81,6 +83,10 @@ __FBSDID("$FreeBSD$"); #include #include +#if defined(PAX_ASLR) +#include +#endif + #define ELF_NOTE_ROUNDSIZE 4 #define OLD_EI_BRAND 8 @@ -655,16 +661,16 @@ __elfN(load_file)(struct proc *p, const char *file, u_long *addr, hdr = (const Elf_Ehdr *)imgp->image_header; if ((error = __elfN(check_header)(hdr)) != 0) goto fail; - if (hdr->e_type == ET_DYN) + if (hdr->e_type == ET_DYN) { rbase = *addr; - else if (hdr->e_type == ET_EXEC) + } else if (hdr->e_type == ET_EXEC) { rbase = 0; - else { + } else { error = ENOEXEC; goto fail; } - /* Only support headers that fit within first page for now */ + /* Only support headers that fit within first page for now */ if ((hdr->e_phoff > PAGE_SIZE) || (u_int)hdr->e_phentsize * hdr->e_phnum > PAGE_SIZE - hdr->e_phoff) { error = ENOEXEC; @@ -789,16 +795,7 @@ __CONCAT(exec_, __elfN(imgact))(struct image_params *imgp) if (hdr->e_type == ET_DYN) { if ((brand_info->flags & BI_CAN_EXEC_DYN) == 0) return (ENOEXEC); - /* - * Honour the base load address from the dso if it is - * non-zero for some reason. - */ - if (baddr == 0) - et_dyn_addr = ET_DYN_LOAD_ADDR; - else - et_dyn_addr = 0; - } else - et_dyn_addr = 0; + } sv = brand_info->sysvec; if (interp != NULL && brand_info->interp_newpath != NULL) newinterp = brand_info->interp_newpath; @@ -819,6 +816,25 @@ __CONCAT(exec_, __elfN(imgact))(struct image_params *imgp) error = exec_new_vmspace(imgp, sv); imgp->proc->p_sysent = sv; +#if defined(PAX_MPROTECT) || defined(PAX_ASLR) + pax_elf(imgp, 0); +#endif + + et_dyn_addr = 0; + if (hdr->e_type == ET_DYN) { + /* + * Honour the base load address from the dso if it is + * non-zero for some reason. + */ + if (baddr == 0) { + et_dyn_addr = ET_DYN_LOAD_ADDR; +#ifdef PAX_ASLR + if (pax_aslr_active(NULL, imgp->proc)) + et_dyn_addr += imgp->proc->p_vmspace->vm_aslr_delta_exec; +#endif + } + } + vn_lock(imgp->vp, LK_EXCLUSIVE | LK_RETRY); if (error) return (error); diff --git a/sys/kern/init_main.c b/sys/kern/init_main.c index 141d438..9301b57 100644 --- a/sys/kern/init_main.c +++ b/sys/kern/init_main.c @@ -410,6 +410,7 @@ struct sysentvec null_sysvec = { .sv_fetch_syscall_args = null_fetch_syscall_args, .sv_syscallnames = NULL, .sv_schedtail = NULL, + .sv_pax_aslr_init = NULL, }; /* diff --git a/sys/kern/kern_exec.c b/sys/kern/kern_exec.c index 667715e..24caccc 100644 --- a/sys/kern/kern_exec.c +++ b/sys/kern/kern_exec.c @@ -30,6 +30,7 @@ __FBSDID("$FreeBSD$"); #include "opt_capsicum.h" #include "opt_hwpmc_hooks.h" #include "opt_ktrace.h" +#include "opt_pax.h" #include "opt_vm.h" #include @@ -94,6 +95,10 @@ __FBSDID("$FreeBSD$"); dtrace_execexit_func_t dtrace_fasttrap_exec; #endif +#if defined(PAX_ASLR) +#include +#endif + SDT_PROVIDER_DECLARE(proc); SDT_PROBE_DEFINE1(proc, kernel, , exec, "char *"); SDT_PROBE_DEFINE1(proc, kernel, , exec__failure, "int"); @@ -404,6 +409,7 @@ do_execve(td, args, mac_p) imgp->pagesizes = 0; imgp->pagesizeslen = 0; imgp->stack_prot = 0; + imgp->pax_flags = 0; #ifdef MAC error = mac_execve_enter(imgp, mac_p); @@ -1064,6 +1070,10 @@ exec_new_vmspace(imgp, sv) map = &vmspace->vm_map; } +#ifdef PAX_ASLR + pax_aslr_init(curthread, imgp); +#endif + /* Map a shared page */ obj = sv->sv_shared_page_obj; if (obj != NULL) { @@ -1107,6 +1117,9 @@ exec_new_vmspace(imgp, sv) */ vmspace->vm_ssize = sgrowsiz >> PAGE_SHIFT; vmspace->vm_maxsaddr = (char *)sv->sv_usrstack - ssiz; +#ifdef PAX_ASLR + vmspace->vm_maxsaddr -= vmspace->vm_aslr_delta_stack; +#endif return (0); } @@ -1266,6 +1279,9 @@ exec_copyout_strings(imgp) szsigcode = *(p->p_sysent->sv_szsigcode); } destp = (uintptr_t)arginfo; +#ifdef PAX_ASLR + pax_aslr_stack(curthread, &destp); +#endif /* * install sigcode diff --git a/sys/kern/kern_fork.c b/sys/kern/kern_fork.c index b3d9c24..3cd85d8 100644 --- a/sys/kern/kern_fork.c +++ b/sys/kern/kern_fork.c @@ -513,6 +513,11 @@ do_fork(struct thread *td, int flags, struct proc *p2, struct thread *td2, } /* + * XXXOP: this is the right place? + */ + p2->p_pax = p1->p_pax; + + /* * p_limit is copy-on-write. Bump its refcount. */ lim_fork(p1, p2); diff --git a/sys/kern/kern_jail.c b/sys/kern/kern_jail.c index 47cd568..d9036bd 100644 --- a/sys/kern/kern_jail.c +++ b/sys/kern/kern_jail.c @@ -33,6 +33,7 @@ __FBSDID("$FreeBSD$"); #include "opt_ddb.h" #include "opt_inet.h" #include "opt_inet6.h" +#include "opt_pax.h" #include #include @@ -74,6 +75,10 @@ __FBSDID("$FreeBSD$"); #endif /* INET6 */ #endif /* DDB */ +#if defined(PAX_ASLR) +#include +#endif + #include #define DEFAULT_HOSTUUID "00000000-0000-0000-0000-000000000000" @@ -117,6 +122,10 @@ struct prison prison0 = { }; MTX_SYSINIT(prison0, &prison0.pr_mtx, "jail mutex", MTX_DEF); +#if defined(PAX_ASLR) +SYSINIT(pax, SI_SUB_PAX, SI_ORDER_MIDDLE, pax_init_prison, (void *) &prison0); +#endif + /* allprison, allprison_racct and lastprid are protected by allprison_lock. */ struct sx allprison_lock; SX_SYSINIT(allprison_lock, &allprison_lock, "allprison"); @@ -1307,6 +1316,10 @@ kern_jail_set(struct thread *td, struct uio *optuio, int flags) goto done_releroot; } +#if defined(PAX_ASLR) + pax_init_prison(pr); +#endif + mtx_lock(&pr->pr_mtx); /* * New prisons do not yet have a reference, because we do not diff --git a/sys/kern/kern_pax.c b/sys/kern/kern_pax.c new file mode 100644 index 0000000..1bd5ad0 --- /dev/null +++ b/sys/kern/kern_pax.c @@ -0,0 +1,214 @@ +/*- + * Copyright (c) 2006 Elad Efrat + * Copyright (c) 2013-2014, by Oliver Pinter + * Copyright (c) 2014, by Shawn Webb + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#include +__FBSDID("$FreeBSD$"); + +#include "opt_compat.h" +#include "opt_pax.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + +#include +#include +#include + +#include + +#include + +#include + +SYSCTL_NODE(_security, OID_AUTO, pax, CTLFLAG_RD, 0, + "PaX (exploit mitigation) features."); + +struct prison * +pax_get_prison(struct thread *td, struct proc *proc) +{ + + if (td != NULL) { + if ((td->td_proc != NULL) && (td->td_proc->p_ucred != NULL)) + return (td->td_proc->p_ucred->cr_prison); + + return (NULL); + } + if ((proc == NULL) || (proc->p_ucred == NULL)) + return (NULL); + + return (proc->p_ucred->cr_prison); +} + +void +pax_elf(struct image_params *imgp, uint32_t mode) +{ + u_int flags = 0; + + if ((mode & MBI_ALLPAX) == MBI_ALLPAX) + goto end; + + if (mode & MBI_FORCE_ASLR_ENABLED) + flags |= PAX_NOTE_ASLR; + else if (mode & MBI_FORCE_ASLR_DISABLED) + flags |= PAX_NOTE_NOASLR; + +end: + if (imgp != NULL) { + imgp->pax_flags = flags; + if (imgp->proc != NULL) { + PROC_LOCK(imgp->proc); + imgp->proc->p_pax = flags; + PROC_UNLOCK(imgp->proc); + } + } +} + + +/* + * print out PaX settings on boot time, and validate some of them + */ +void +pax_init(void) +{ +#if defined(PAX_ASLR) + const char *status_str[] = { + [0] = "disabled", + [1] = "opt-in", + [2] = "opt-out", + [3] = "force enabled", + [4] = "UNKNOWN -> changed to \"force enabled\"" + }; +#endif + +#ifdef PAX_ASLR + switch (pax_aslr_status) { + case 0: + case 1: + case 2: + case 3: + break; + default: + printf("[PAX ASLR] WARNING, invalid PAX settings in loader.conf!" + " (pax_aslr_status = %d)\n", pax_aslr_status); + pax_aslr_status = 3; + break; + } + printf("[PAX ASLR] status: %s\n", status_str[pax_aslr_status]); + printf("[PAX ASLR] mmap: %d bit\n", pax_aslr_mmap_len); + printf("[PAX ASLR] exec base: %d bit\n", pax_aslr_exec_len); + printf("[PAX ASLR] stack: %d bit\n", pax_aslr_stack_len); + +#ifdef COMPAT_FREEBSD32 + switch (pax_aslr_compat_status) { + case 0: + case 1: + case 2: + case 3: + break; + default: + printf("[PAX ASLR (compat)] WARNING, invalid PAX settings in loader.conf! " + "(pax_aslr_compat_status = %d)\n", pax_aslr_compat_status); + pax_aslr_compat_status = 3; + break; + } + printf("[PAX ASLR (compat)] status: %s\n", status_str[pax_aslr_compat_status]); + printf("[PAX ASLR (compat)] mmap: %d bit\n", pax_aslr_compat_mmap_len); + printf("[PAX ASLR (compat)] exec base: %d bit\n", pax_aslr_compat_exec_len); + printf("[PAX ASLR (compat)] stack: %d bit\n", pax_aslr_compat_stack_len); +#endif /* COMPAT_FREEBSD32 */ +#endif /* PAX_ASLR */ + + printf("[PAX LOG] logging to system: %d\n", pax_log_log); + printf("[PAX LOG] logging to user: %d\n", pax_log_ulog); +} +SYSINIT(pax, SI_SUB_PAX, SI_ORDER_FIRST, pax_init, NULL); + +void +pax_init_prison(struct prison *pr) +{ + + if (pr == NULL) + return; + + if (pr->pr_pax_set) + return; + + mtx_lock(&(pr->pr_mtx)); + + if (pax_aslr_debug) + uprintf("[PaX ASLR] %s: Setting prison %s ASLR variables\n", + __func__, pr->pr_name); + +#ifdef PAX_ASLR + pr->pr_pax_aslr_status = pax_aslr_status; + pr->pr_pax_aslr_debug = pax_aslr_debug; + pr->pr_pax_aslr_mmap_len = pax_aslr_mmap_len; + pr->pr_pax_aslr_stack_len = pax_aslr_stack_len; + pr->pr_pax_aslr_exec_len = pax_aslr_exec_len; + +#ifdef COMPAT_FREEBSD32 + pr->pr_pax_aslr_compat_status = pax_aslr_compat_status; + pr->pr_pax_aslr_compat_mmap_len = pax_aslr_compat_mmap_len; + pr->pr_pax_aslr_compat_stack_len = pax_aslr_compat_stack_len; + pr->pr_pax_aslr_compat_exec_len = pax_aslr_compat_exec_len; +#endif /* COMPAT_FREEBSD32 */ +#endif /* PAX_ASLR */ + + pr->pr_pax_log_log = pax_log_log; + pr->pr_pax_log_ulog = pax_log_ulog; + + pr->pr_pax_set = 1; + + mtx_unlock(&(pr->pr_mtx)); +} diff --git a/sys/kern/kern_pax_aslr.c b/sys/kern/kern_pax_aslr.c new file mode 100644 index 0000000..4b5e8dd --- /dev/null +++ b/sys/kern/kern_pax_aslr.c @@ -0,0 +1,685 @@ +/*- + * Copyright (c) 2006 Elad Efrat + * Copyright (c) 2013-2014, by Oliver Pinter + * Copyright (c) 2014, by Shawn Webb + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#include +__FBSDID("$FreeBSD$"); + +#include "opt_compat.h" +#include "opt_pax.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include + +FEATURE(aslr, "Address Space Layout Randomization."); + +int pax_aslr_status = PAX_ASLR_OPTOUT; +int pax_aslr_debug = 0; + +#ifdef PAX_ASLR_MAX_SEC +int pax_aslr_mmap_len = PAX_ASLR_DELTA_MMAP_MAX_LEN; +int pax_aslr_stack_len = PAX_ASLR_DELTA_STACK_MAX_LEN; +int pax_aslr_exec_len = PAX_ASLR_DELTA_EXEC_MAX_LEN; +#else +int pax_aslr_mmap_len = PAX_ASLR_DELTA_MMAP_DEF_LEN; +int pax_aslr_stack_len = PAX_ASLR_DELTA_STACK_DEF_LEN; +int pax_aslr_exec_len = PAX_ASLR_DELTA_EXEC_DEF_LEN; +#endif /* PAX_ASLR_MAX_SEC */ + +#ifdef COMPAT_FREEBSD32 +int pax_aslr_compat_status = PAX_ASLR_OPTOUT; +#ifdef PAX_ASLR_MAX_SEC +int pax_aslr_compat_mmap_len = PAX_ASLR_COMPAT_DELTA_MMAP_MAX_LEN; +int pax_aslr_compat_stack_len = PAX_ASLR_COMPAT_DELTA_STACK_MAX_LEN; +int pax_aslr_compat_exec_len = PAX_ASLR_COMPAT_DELTA_EXEC_MAX_LEN; +#else +int pax_aslr_compat_mmap_len = PAX_ASLR_COMPAT_DELTA_MMAP_MIN_LEN; +int pax_aslr_compat_stack_len = PAX_ASLR_COMPAT_DELTA_STACK_MIN_LEN; +int pax_aslr_compat_exec_len = PAX_ASLR_COMPAT_DELTA_EXEC_MIN_LEN; +#endif /* PAX_ASLR_MAX_SEC */ +#endif /* COMPAT_FREEBSD32 */ + +TUNABLE_INT("security.pax.aslr.status", &pax_aslr_status); +TUNABLE_INT("security.pax.aslr.mmap_len", &pax_aslr_mmap_len); +TUNABLE_INT("security.pax.aslr.debug", &pax_aslr_debug); +TUNABLE_INT("security.pax.aslr.stack_len", &pax_aslr_stack_len); +TUNABLE_INT("security.pax.aslr.exec_len", &pax_aslr_exec_len); +#ifdef COMPAT_FREEBSD32 +TUNABLE_INT("security.pax.aslr.compat.status", &pax_aslr_compat_status); +TUNABLE_INT("security.pax.aslr.compat.mmap", &pax_aslr_compat_mmap_len); +TUNABLE_INT("security.pax.aslr.compat.stack", &pax_aslr_compat_stack_len); +TUNABLE_INT("security.pax.aslr.compat.stack", &pax_aslr_compat_exec_len); +#endif + + +#ifdef PAX_SYSCTLS +/* + * sysctls and tunables + */ +static int sysctl_pax_aslr_debug(SYSCTL_HANDLER_ARGS); +static int sysctl_pax_aslr_status(SYSCTL_HANDLER_ARGS); +static int sysctl_pax_aslr_mmap(SYSCTL_HANDLER_ARGS); +static int sysctl_pax_aslr_stack(SYSCTL_HANDLER_ARGS); +static int sysctl_pax_aslr_exec(SYSCTL_HANDLER_ARGS); + +SYSCTL_DECL(_security_pax); + +SYSCTL_NODE(_security_pax, OID_AUTO, aslr, CTLFLAG_RD, 0, + "Address Space Layout Randomization."); + +SYSCTL_PROC(_security_pax_aslr, OID_AUTO, status, + CTLTYPE_INT|CTLFLAG_RWTUN|CTLFLAG_PRISON|CTLFLAG_SECURE, + NULL, 0, sysctl_pax_aslr_status, "I", + "Restrictions status. " + "0 - disabled, " + "1 - opt-in, " + "2 - opt-out, " + "3 - force enabled"); + +SYSCTL_PROC(_security_pax_aslr, OID_AUTO, debug, + CTLTYPE_INT|CTLFLAG_RWTUN|CTLFLAG_PRISON|CTLFLAG_SECURE, + NULL, 0, sysctl_pax_aslr_debug, "I", + "ASLR debug mode"); + +SYSCTL_PROC(_security_pax_aslr, OID_AUTO, mmap_len, + CTLTYPE_INT|CTLFLAG_RWTUN|CTLFLAG_PRISON|CTLFLAG_SECURE, + NULL, 0, sysctl_pax_aslr_mmap, "I", + "Number of bits randomized for mmap(2) calls. " + "32 bit: [8,16] 64 bit: [16,32]"); + +SYSCTL_PROC(_security_pax_aslr, OID_AUTO, stack_len, + CTLTYPE_INT|CTLFLAG_RWTUN|CTLFLAG_PRISON|CTLFLAG_SECURE, + NULL, 0, sysctl_pax_aslr_stack, "I", + "Number of bits randomized for the stack. " + "32 bit: [6,12] 64 bit: [12,21]"); + +SYSCTL_PROC(_security_pax_aslr, OID_AUTO, exec_len, + CTLTYPE_INT|CTLFLAG_RWTUN|CTLFLAG_PRISON|CTLFLAG_SECURE, + NULL, 0, sysctl_pax_aslr_exec, "I", + "Number of bits randomized for the PIE exec base. " + "32 bit: [6,12] 64 bit: [12,21]"); + +static int +sysctl_pax_aslr_status(SYSCTL_HANDLER_ARGS) +{ + struct prison *pr; + int err, val; + + pr = pax_get_prison(req->td, NULL); + + val = (pr != NULL) ? pr->pr_pax_aslr_status : pax_aslr_status; + err = sysctl_handle_int(oidp, &val, sizeof(int), req); + if (err || (req->newptr == NULL)) + return (err); + + switch (val) { + case PAX_ASLR_DISABLED: + case PAX_ASLR_OPTIN: + case PAX_ASLR_OPTOUT: + case PAX_ASLR_FORCE_ENABLED: + if ((pr == NULL) || (pr == &prison0)) + pax_aslr_status = val; + + if (pr != NULL) { + mtx_lock(&(pr->pr_mtx)); + pr->pr_pax_aslr_status = val; + mtx_unlock(&(pr->pr_mtx)); + } + break; + default: + return (EINVAL); + } + + return (0); +} + +static int +sysctl_pax_aslr_debug(SYSCTL_HANDLER_ARGS) +{ + struct prison *pr=NULL; + int err, val; + + pr = pax_get_prison(req->td, NULL); + + if ((pr != NULL) && !(pr->pr_pax_set)) + pax_init_prison(pr); + + val = (pr != NULL) ? pr->pr_pax_aslr_debug : pax_aslr_debug; + err = sysctl_handle_int(oidp, &val, sizeof(int), req); + if (err || !req->newptr) + return (err); + + switch (val) { + case 0: + case 1: + break; + default: + return (EINVAL); + + } + + if ((pr == NULL) || (pr == &prison0)) + pax_aslr_debug = val; + + if (pr != NULL) { + mtx_lock(&(pr->pr_mtx)); + pr->pr_pax_aslr_debug = val; + mtx_unlock(&(pr->pr_mtx)); + } + + return (0); +} + +static int +sysctl_pax_aslr_mmap(SYSCTL_HANDLER_ARGS) +{ + struct prison *pr=NULL; + int err, val; + + pr = pax_get_prison(req->td, NULL); + + if ((pr != NULL) && !(pr->pr_pax_set)) + pax_init_prison(pr); + + val = (pr != NULL) ? pr->pr_pax_aslr_mmap_len : pax_aslr_mmap_len; + err = sysctl_handle_int(oidp, &val, sizeof(int), req); + if (err || !req->newptr) + return (err); + + if (val < PAX_ASLR_DELTA_MMAP_MIN_LEN || + val > PAX_ASLR_DELTA_MMAP_MAX_LEN) + return (EINVAL); + + if ((pr == NULL) || (pr == &prison0)) + pax_aslr_mmap_len = val; + + if (pr != NULL) { + mtx_lock(&(pr->pr_mtx)); + pr->pr_pax_aslr_mmap_len = val; + mtx_unlock(&(pr->pr_mtx)); + } + + return (0); +} + +static int +sysctl_pax_aslr_stack(SYSCTL_HANDLER_ARGS) +{ + struct prison *pr=NULL; + int err, val; + + pr = pax_get_prison(req->td, NULL); + + if ((pr != NULL) && !(pr->pr_pax_set)) + pax_init_prison(pr); + + val = (pr != NULL) ? pr->pr_pax_aslr_stack_len : pax_aslr_stack_len; + err = sysctl_handle_int(oidp, &val, sizeof(int), req); + if (err || !req->newptr) + return (err); + + if (val < PAX_ASLR_DELTA_STACK_MIN_LEN || + val > PAX_ASLR_DELTA_STACK_MAX_LEN) + return (EINVAL); + + if ((pr == NULL) || (pr == &prison0)) + pax_aslr_stack_len = val; + + if (pr != NULL) { + mtx_lock(&(pr->pr_mtx)); + pr->pr_pax_aslr_stack_len = val; + mtx_unlock(&(pr->pr_mtx)); + } + + return (0); +} + +static int +sysctl_pax_aslr_exec(SYSCTL_HANDLER_ARGS) +{ + struct prison *pr=NULL; + int err, val; + + pr = pax_get_prison(req->td, NULL); + + if ((pr != NULL) && !(pr->pr_pax_set)) + pax_init_prison(pr); + + val = (pr != NULL) ? pr->pr_pax_aslr_exec_len : pax_aslr_exec_len; + err = sysctl_handle_int(oidp, &val, sizeof(int), req); + if (err || (req->newptr == NULL)) + return (err); + + if (val < PAX_ASLR_DELTA_EXEC_MIN_LEN || + val > PAX_ASLR_DELTA_EXEC_MAX_LEN) + return (EINVAL); + + if ((pr == NULL) || (pr == &prison0)) + pax_aslr_exec_len = val; + + if (pr != NULL) { + mtx_lock(&(pr->pr_mtx)); + pr->pr_pax_aslr_exec_len = val; + mtx_unlock(&(pr->pr_mtx)); + } + + return (0); +} + +/* + * COMPAT_FREEBSD32 and linuxulator.. + */ +#ifdef COMPAT_FREEBSD32 +static int sysctl_pax_aslr_compat_status(SYSCTL_HANDLER_ARGS); +static int sysctl_pax_aslr_compat_mmap(SYSCTL_HANDLER_ARGS); +static int sysctl_pax_aslr_compat_stack(SYSCTL_HANDLER_ARGS); +static int sysctl_pax_aslr_compat_exec(SYSCTL_HANDLER_ARGS); + +SYSCTL_NODE(_security_pax_aslr, OID_AUTO, compat, CTLFLAG_RD, 0, + "Setting for COMPAT_FREEBSD32 and linuxulator."); + +SYSCTL_PROC(_security_pax_aslr_compat, OID_AUTO, status, + CTLTYPE_INT|CTLFLAG_RWTUN|CTLFLAG_PRISON, + NULL, 0, sysctl_pax_aslr_compat_status, "I", + "Restrictions status. " + "0 - disabled, " + "1 - enabled, " + "2 - global enabled, " + "3 - force global enabled"); + +SYSCTL_PROC(_security_pax_aslr_compat, OID_AUTO, mmap_len, + CTLTYPE_INT|CTLFLAG_RWTUN|CTLFLAG_PRISON, + NULL, 0, sysctl_pax_aslr_compat_mmap, "I", + "Number of bits randomized for mmap(2) calls. " + "32 bit: [8,16]"); + +SYSCTL_PROC(_security_pax_aslr_compat, OID_AUTO, stack_len, + CTLTYPE_INT|CTLFLAG_RWTUN|CTLFLAG_PRISON, + NULL, 0, sysctl_pax_aslr_compat_stack, "I", + "Number of bits randomized for the stack. " + "32 bit: [6,12]"); + +SYSCTL_PROC(_security_pax_aslr_compat, OID_AUTO, exec_len, + CTLTYPE_INT|CTLFLAG_RWTUN|CTLFLAG_PRISON, + NULL, 0, sysctl_pax_aslr_compat_exec, "I", + "Number of bits randomized for the PIE exec base. " + "32 bit: [6,12]"); + +static int +sysctl_pax_aslr_compat_status(SYSCTL_HANDLER_ARGS) +{ + struct prison *pr; + int err, val; + + pr = pax_get_prison(req->td, NULL); + + val = (pr != NULL) ?pr->pr_pax_aslr_compat_status : pax_aslr_compat_status; + err = sysctl_handle_int(oidp, &val, sizeof(int), req); + if (err || (req->newptr == NULL)) + return (err); + + switch (val) { + case PAX_ASLR_DISABLED: + case PAX_ASLR_OPTIN: + case PAX_ASLR_OPTOUT: + case PAX_ASLR_FORCE_ENABLED: + if ((pr == NULL) || (pr == &prison0)) + pax_aslr_compat_status = val; + + if (pr != NULL) { + mtx_lock(&(pr->pr_mtx)); + pr->pr_pax_aslr_compat_status = val; + mtx_unlock(&(pr->pr_mtx)); + } + break; + default: + return (EINVAL); + } + + return (0); +} + +static int +sysctl_pax_aslr_compat_mmap(SYSCTL_HANDLER_ARGS) +{ + struct prison *pr; + int err, val; + + pr = pax_get_prison(req->td, NULL); + + val = (pr != NULL) ? pr->pr_pax_aslr_compat_mmap_len : pax_aslr_compat_mmap_len; + err = sysctl_handle_int(oidp, &val, sizeof(int), req); + if (err || !req->newptr) + return (err); + + if (val < PAX_ASLR_COMPAT_DELTA_MMAP_MIN_LEN || + val > PAX_ASLR_COMPAT_DELTA_MMAP_MAX_LEN) + return (EINVAL); + + if ((pr == NULL) || (pr == &prison0)) + pax_aslr_compat_mmap_len = val; + + if (pr != NULL) { + mtx_lock(&(pr->pr_mtx)); + pr->pr_pax_aslr_compat_mmap_len = val; + mtx_unlock(&(pr->pr_mtx)); + } + + return (0); +} + +static int +sysctl_pax_aslr_compat_stack(SYSCTL_HANDLER_ARGS) +{ + struct prison *pr; + int err, val; + + pr = pax_get_prison(req->td, NULL); + + val = (pr != NULL) ? pr->pr_pax_aslr_compat_stack_len : pax_aslr_compat_stack_len; + err = sysctl_handle_int(oidp, &val, sizeof(int), req); + if (err || !req->newptr) + return (err); + + if (val < PAX_ASLR_COMPAT_DELTA_STACK_MIN_LEN || + val > PAX_ASLR_COMPAT_DELTA_STACK_MAX_LEN) + return (EINVAL); + + if ((pr == NULL) || (pr == &prison0)) + pax_aslr_compat_stack_len = val; + + if (pr != NULL) { + mtx_lock(&(pr->pr_mtx)); + pr->pr_pax_aslr_compat_stack_len = val; + mtx_unlock(&(pr->pr_mtx)); + } + + return (0); +} + +static int +sysctl_pax_aslr_compat_exec(SYSCTL_HANDLER_ARGS) +{ + struct prison *pr; + int err, val; + + pr = pax_get_prison(req->td, NULL); + + if (pr != NULL) + val = pr->pr_pax_aslr_compat_exec_len; + else + val = pax_aslr_compat_exec_len; + + err = sysctl_handle_int(oidp, &val, sizeof(int), req); + if (err || !req->newptr) + return (err); + + if (val < PAX_ASLR_COMPAT_DELTA_EXEC_MIN_LEN || + val > PAX_ASLR_COMPAT_DELTA_EXEC_MAX_LEN) + return (EINVAL); + + if ((pr == NULL) || (pr == &prison0)) + pax_aslr_compat_exec_len = val; + + if (pr != NULL) { + mtx_lock(&(pr->pr_mtx)); + pr->pr_pax_aslr_compat_exec_len = val; + mtx_unlock(&(pr->pr_mtx)); + } + + return (0); +} + +#endif /* COMPAT_FREEBSD32 */ +#endif /* PAX_SYSCTLS */ + + +/* + * ASLR functions + */ +bool +pax_aslr_active(struct thread *td, struct proc *proc) +{ + int status; + struct prison *pr; + uint32_t flags; + + if ((td == NULL) && (proc == NULL)) + return (true); + + pr = pax_get_prison(td, proc); + + flags = (td != NULL) ? td->td_proc->p_pax : proc->p_pax; + if (((flags & 0xaaaaaaaa) & ((flags & 0x55555555) << 1)) != 0) { + pax_log_aslr(pr, __func__, "inconsistent paxflags: %x\n", flags); + pax_ulog_aslr(pr, NULL, "inconsistent paxflags: %x\n", flags); + return (true); + } + + if (pr != NULL) + status = pr->pr_pax_aslr_status; + else + status = pax_aslr_status; + + switch (status) { + case PAX_ASLR_DISABLED: + return (false); + case PAX_ASLR_FORCE_ENABLED: + return (true); + case PAX_ASLR_OPTIN: + if ((flags & PAX_NOTE_ASLR) == 0) { + pax_log_aslr(pr, __func__, + "ASLR is opt-in, and executable does not have ASLR enabled\n"); + pax_ulog_aslr(pr, NULL, + "ASLR is opt-in, and executable does not have ASLR enabled\n"); + return (false); + } + break; + case PAX_ASLR_OPTOUT: + if ((flags & PAX_NOTE_NOASLR) != 0) { + pax_log_aslr(pr, __func__, + "ASLR is opt-out, and executable explicitly disabled ASLR\n"); + pax_ulog_aslr(pr, NULL, + "ASLR is opt-out, and executable explicitly disabled ASLR\n"); + return (false); + } + break; + default: + return (true); + } + + return (true); +} + +void +_pax_aslr_init(struct vmspace *vm, struct prison *pr) +{ + if (vm == NULL) + panic("[PaX ASLR] %s: vm == NULL", __func__); + + vm->vm_aslr_delta_mmap = PAX_ASLR_DELTA(arc4random(), + PAX_ASLR_DELTA_MMAP_LSB, (pr != NULL) ? + pr->pr_pax_aslr_mmap_len : + pax_aslr_mmap_len); + vm->vm_aslr_delta_stack = PAX_ASLR_DELTA(arc4random(), + PAX_ASLR_DELTA_STACK_LSB, (pr != NULL) ? + pr->pr_pax_aslr_stack_len : + pax_aslr_stack_len); + vm->vm_aslr_delta_stack = ALIGN(vm->vm_aslr_delta_stack); + vm->vm_aslr_delta_exec = PAX_ASLR_DELTA(arc4random(), + PAX_ASLR_DELTA_EXEC_LSB, (pr != NULL) ? + pr->pr_pax_aslr_exec_len : + pax_aslr_exec_len); + + if ((pr != NULL) && pr->pr_pax_aslr_debug) { + pax_log_aslr(pr, __func__, "vm_aslr_delta_mmap=%p\n", + (void *) vm->vm_aslr_delta_mmap); + pax_log_aslr(pr, __func__, "vm_aslr_delta_stack=%p\n", + (void *) vm->vm_aslr_delta_stack); + pax_log_aslr(pr, __func__, "vm_aslr_delta_exec=%p\n", + (void *) vm->vm_aslr_delta_exec); + pax_ulog_aslr(pr, NULL, "vm_aslr_delta_mmap=%p\n", + (void *) vm->vm_aslr_delta_mmap); + pax_ulog_aslr(pr, NULL, "vm_aslr_delta_stack=%p\n", + (void *) vm->vm_aslr_delta_stack); + pax_ulog_aslr(pr, NULL, "vm_aslr_delta_exec=%p\n", + (void *) vm->vm_aslr_delta_exec); + } +} + +#ifdef COMPAT_FREEBSD32 +void +_pax_aslr_init32(struct vmspace *vm, struct prison *pr) +{ + if (vm == NULL) + panic("[PaX ASLR] %s: vm == NULL", __func__); + + vm->vm_aslr_delta_mmap = PAX_ASLR_DELTA(arc4random(), + PAX_ASLR_COMPAT_DELTA_MMAP_LSB, (pr != NULL) ? + pr->pr_pax_aslr_compat_mmap_len : + pax_aslr_compat_mmap_len); + vm->vm_aslr_delta_stack = PAX_ASLR_DELTA(arc4random(), + PAX_ASLR_COMPAT_DELTA_STACK_LSB, (pr != NULL) ? + pr->pr_pax_aslr_compat_stack_len : + pax_aslr_compat_stack_len); + vm->vm_aslr_delta_stack = ALIGN(vm->vm_aslr_delta_stack); + vm->vm_aslr_delta_exec = PAX_ASLR_DELTA(arc4random(), + PAX_ASLR_DELTA_EXEC_LSB, (pr != NULL) ? + pr->pr_pax_aslr_compat_exec_len : + pax_aslr_compat_exec_len); + + if ((pr != NULL) && pr->pr_pax_aslr_debug) { + pax_log_aslr(pr, __func__, "vm_aslr_delta_mmap=%p\n", + (void *) vm->vm_aslr_delta_mmap); + pax_log_aslr(pr, __func__, "vm_aslr_delta_stack=%p\n", + (void *) vm->vm_aslr_delta_stack); + pax_log_aslr(pr, __func__, "vm_aslr_delta_exec=%p\n", + (void *) vm->vm_aslr_delta_exec); + pax_ulog_aslr(pr, NULL, "vm_aslr_delta_mmap=%p\n", + (void *) vm->vm_aslr_delta_mmap); + pax_ulog_aslr(pr, NULL, "vm_aslr_delta_stack=%p\n", + (void *) vm->vm_aslr_delta_stack); + pax_ulog_aslr(pr, NULL, "vm_aslr_delta_exec=%p\n", + (void *) vm->vm_aslr_delta_exec); + } +} +#endif + +void +pax_aslr_init(struct thread *td, struct image_params *imgp) +{ + struct prison *pr; + struct vmspace *vm; + + pr = pax_get_prison(td, NULL); + + if (imgp == NULL) + panic("[PaX ASLR] %s: imgp == NULL", __func__); + + if (!pax_aslr_active(td, NULL)) + return; + + vm = imgp->proc->p_vmspace; + + if (imgp->sysent->sv_pax_aslr_init != NULL) + imgp->sysent->sv_pax_aslr_init(vm, pr); +} + +void +pax_aslr_mmap(struct thread *td, vm_offset_t *addr, vm_offset_t orig_addr, int flags) +{ + struct prison *pr; + + if (!pax_aslr_active(td, NULL)) + return; + + orig_addr = *addr; + + pr = pax_get_prison(td, NULL); + + if (!(flags & MAP_FIXED) && ((orig_addr == 0) || !(flags & MAP_ANON))) { + pax_log_aslr(pr, __func__, "applying to %p orig_addr=%p flags=%x\n", + (void *)*addr, (void *)orig_addr, flags); + + if (!(td->td_proc->p_vmspace->vm_map.flags & MAP_ENTRY_GROWS_DOWN)) + *addr += td->td_proc->p_vmspace->vm_aslr_delta_mmap; + else + *addr -= td->td_proc->p_vmspace->vm_aslr_delta_mmap; + pax_log_aslr(pr, __func__, "result %p\n", (void *)*addr); + } else { + pax_log_aslr(pr, __func__, "not applying to %p orig_addr=%p flags=%x\n", + (void *)*addr, (void *)orig_addr, flags); + } +} + +void +pax_aslr_stack(struct thread *td, uintptr_t *addr) +{ + struct prison *pr; + uintptr_t orig_addr; + + if (!pax_aslr_active(td, NULL)) + return; + + pr = pax_get_prison(td, NULL); + + orig_addr = *addr; + *addr -= td->td_proc->p_vmspace->vm_aslr_delta_stack; + pax_log_aslr(pr, __func__, "orig_addr=%p, new_addr=%p\n", + (void *)orig_addr, (void *)*addr); + pax_ulog_aslr(pr, NULL, "orig_addr=%p, new_addr=%p\n", + (void *)orig_addr, (void *)*addr); +} diff --git a/sys/kern/kern_pax_log.c b/sys/kern/kern_pax_log.c new file mode 100644 index 0000000..943ac81 --- /dev/null +++ b/sys/kern/kern_pax_log.c @@ -0,0 +1,188 @@ +/*- + * Copyright (c) 2014, by Oliver Pinter + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#define __PAX_LOG_TEMPLATE(SUBJECT, name) \ +void \ +pax_log_##name(struct prison *pr, const char *caller_name, const char* fmt, ...)\ +{ \ + struct sbuf *sb; \ + va_list args; \ + \ + if ((pr != NULL) && (pr->pr_pax_log_log == 0)) \ + return; \ + \ + sb = sbuf_new_auto(); \ + if (sb == NULL) \ + panic("%s: Could not allocate memory", __func__); \ + sbuf_printf(sb, "[PAX "#SUBJECT"] "); \ + if (caller_name != NULL) \ + sbuf_printf(sb, "%s: ", caller_name); \ + va_start(args, fmt); \ + sbuf_vprintf(sb, fmt, args); \ + va_end(args); \ + if (sbuf_finish(sb) != 0) \ + panic("%s: Could not generate message", __func__); \ + \ + printf("%s", sbuf_data(sb)); \ + sbuf_delete(sb); \ +} \ + \ +void \ +pax_ulog_##name(struct prison *pr, const char *caller_name, const char* fmt, ...)\ +{ \ + struct sbuf *sb; \ + va_list args; \ + \ + if ((pr != NULL) && (pr->pr_pax_log_ulog == 0)) \ + return; \ + \ + sb = sbuf_new_auto(); \ + if (sb == NULL) \ + panic("%s: Could not allocate memory", __func__); \ + sbuf_printf(sb, "[PAX "#SUBJECT"] "); \ + if (caller_name != NULL) \ + sbuf_printf(sb, "%s: ", caller_name); \ + va_start(args, fmt); \ + sbuf_vprintf(sb, fmt, args); \ + va_end(args); \ + if (sbuf_finish(sb) != 0) \ + panic("%s: Could not generate message", __func__); \ + \ + uprintf("%s", sbuf_data(sb)); \ + sbuf_delete(sb); \ +} + + +static int sysctl_pax_log_log(SYSCTL_HANDLER_ARGS); +static int sysctl_pax_log_ulog(SYSCTL_HANDLER_ARGS); + +int pax_log_log = PAX_LOG_LOG; +int pax_log_ulog = PAX_LOG_ULOG; + +SYSCTL_DECL(_security_pax); + +SYSCTL_NODE(_security_pax, OID_AUTO, log, CTLFLAG_RD, 0, + "PAX related logging facility."); + +SYSCTL_PROC(_security_pax_log, OID_AUTO, log, + CTLTYPE_INT|CTLFLAG_RWTUN|CTLFLAG_PRISON|CTLFLAG_SECURE, + NULL, 0, sysctl_pax_log_log, "I", + "log to syslog " + "0 - disabled, " + "1 - enabled "); +TUNABLE_INT("security.pax.log.log", &pax_log_log); + +SYSCTL_PROC(_security_pax_log, OID_AUTO, ulog, + CTLTYPE_INT|CTLFLAG_RWTUN|CTLFLAG_PRISON|CTLFLAG_SECURE, + NULL, 0, sysctl_pax_log_ulog, "I", + "log to user terminal" + "0 - disabled, " + "1 - enabled "); +TUNABLE_INT("security.pax.log.ulog", &pax_log_ulog); + +static int +sysctl_pax_log_log(SYSCTL_HANDLER_ARGS) +{ + int err; + int val; + struct prison *pr=NULL; + + pr = pax_get_prison(req->td, NULL); + + if ((pr != NULL) && !(pr->pr_pax_set)) + pax_init_prison(pr); + + val = (pr != NULL) ? pr->pr_pax_log_log : pax_log_log; + err = sysctl_handle_int(oidp, &val, sizeof(int), req); + if (err || !req->newptr) + return (err); + + switch (val) { + case 0: + case 1: + break; + default: + return (EINVAL); + + } + + if ((pr == NULL) || (pr == &prison0)) + pax_log_log = val; + if (pr != NULL) + pr->pr_pax_log_log = val; + + return (0); +} + +static int +sysctl_pax_log_ulog(SYSCTL_HANDLER_ARGS) +{ + int err; + int val; + struct prison *pr=NULL; + + pr = pax_get_prison(req->td, NULL); + + if ((pr != NULL) && !(pr->pr_pax_set)) + pax_init_prison(pr); + + val = (pr != NULL) ? pr->pr_pax_log_ulog : pax_log_ulog; + err = sysctl_handle_int(oidp, &val, sizeof(int), req); + if (err || !req->newptr) + return (err); + + switch (val) { + case 0: + case 1: + break; + default: + return (EINVAL); + + } + + if ((pr == NULL) || (pr == &prison0)) + pax_log_ulog = val; + if (pr != NULL) + pr->pr_pax_log_ulog = val; + + return (0); +} + + +__PAX_LOG_TEMPLATE(ASLR, aslr) diff --git a/sys/mips/mips/elf_machdep.c b/sys/mips/mips/elf_machdep.c index d374713..f95ba35 100644 --- a/sys/mips/mips/elf_machdep.c +++ b/sys/mips/mips/elf_machdep.c @@ -28,6 +28,8 @@ #include __FBSDID("$FreeBSD$"); +#include "opt_pax.h" + #include #include #include @@ -49,6 +51,10 @@ __FBSDID("$FreeBSD$"); #include #include +#ifdef PAX_ASLR +#include +#endif + #ifdef __mips_n64 struct sysentvec elf64_freebsd_sysvec = { .sv_size = SYS_MAXSYSCALL, @@ -83,6 +89,11 @@ struct sysentvec elf64_freebsd_sysvec = { .sv_fetch_syscall_args = cpu_fetch_syscall_args, .sv_syscallnames = syscallnames, .sv_schedtail = NULL, +#ifdef PAX_ASLR + .sv_pax_aslr_init = _pax_aslr_init, +#else + .sv_pax_aslr_init = NULL, +#endif }; static Elf64_Brandinfo freebsd_brand_info = { @@ -139,6 +150,11 @@ struct sysentvec elf32_freebsd_sysvec = { .sv_fetch_syscall_args = cpu_fetch_syscall_args, .sv_syscallnames = syscallnames, .sv_schedtail = NULL, +#ifdef PAX_ASLR + .sv_pax_aslr_init = _pax_aslr_init32, +#else + .sv_pax_aslr_init = NULL, +#endif }; static Elf32_Brandinfo freebsd_brand_info = { diff --git a/sys/mips/mips/freebsd32_machdep.c b/sys/mips/mips/freebsd32_machdep.c index dfdf70f..103ad84 100644 --- a/sys/mips/mips/freebsd32_machdep.c +++ b/sys/mips/mips/freebsd32_machdep.c @@ -31,6 +31,7 @@ */ #include "opt_compat.h" +#include "opt_pax.h" #define __ELF_WORD_SIZE 32 @@ -66,6 +67,10 @@ #include #include +#ifdef PAX_ASLR +#include +#endif + static void freebsd32_exec_setregs(struct thread *, struct image_params *, u_long); static int get_mcontext32(struct thread *, mcontext32_t *, int); static int set_mcontext32(struct thread *, const mcontext32_t *); @@ -106,6 +111,11 @@ struct sysentvec elf32_freebsd_sysvec = { .sv_fetch_syscall_args = cpu_fetch_syscall_args, .sv_syscallnames = freebsd32_syscallnames, .sv_schedtail = NULL, +#ifdef PAX_ASLR + .sv_pax_aslr_init = _pax_aslr_init32, +#else + .sv_pax_aslr_init = NULL, +#endif }; INIT_SYSENTVEC(elf32_sysvec, &elf32_freebsd_sysvec); diff --git a/sys/powerpc/powerpc/elf32_machdep.c b/sys/powerpc/powerpc/elf32_machdep.c index dbe58df..229fe97 100644 --- a/sys/powerpc/powerpc/elf32_machdep.c +++ b/sys/powerpc/powerpc/elf32_machdep.c @@ -25,6 +25,8 @@ * $FreeBSD$ */ +#include "opt_pax.h" + #include #include #include @@ -52,6 +54,10 @@ #include #include +#ifdef PAX_ASLR +#include +#endif + #ifdef __powerpc64__ #include #include @@ -107,6 +113,11 @@ struct sysentvec elf32_freebsd_sysvec = { .sv_shared_page_base = FREEBSD32_SHAREDPAGE, .sv_shared_page_len = PAGE_SIZE, .sv_schedtail = NULL, +#ifdef PAX_ASLR + .sv_pax_aslr_init = _pax_aslr_init32, +#else + .sv_pax_aslr_init = NULL, +#endif }; INIT_SYSENTVEC(elf32_sysvec, &elf32_freebsd_sysvec); diff --git a/sys/powerpc/powerpc/elf64_machdep.c b/sys/powerpc/powerpc/elf64_machdep.c index 0c41a8d..095f37b0 100644 --- a/sys/powerpc/powerpc/elf64_machdep.c +++ b/sys/powerpc/powerpc/elf64_machdep.c @@ -25,6 +25,8 @@ * $FreeBSD$ */ +#include "opt_pax.h" + #include #include #include @@ -48,6 +50,10 @@ #include #include +#ifdef PAX_ASLR +#include +#endif + struct sysentvec elf64_freebsd_sysvec = { .sv_size = SYS_MAXSYSCALL, .sv_table = sysent, @@ -83,6 +89,11 @@ struct sysentvec elf64_freebsd_sysvec = { .sv_shared_page_base = SHAREDPAGE, .sv_shared_page_len = PAGE_SIZE, .sv_schedtail = NULL, +#ifdef PAX_ASLR + .sv_pax_aslr_init = _pax_aslr_init, +#else + .sv_pax_aslr_init = NULL, +#endif }; INIT_SYSENTVEC(elf64_sysvec, &elf64_freebsd_sysvec); diff --git a/sys/security/mac_bsdextended/mac_bsdextended.c b/sys/security/mac_bsdextended/mac_bsdextended.c index ccbc525..520168d 100644 --- a/sys/security/mac_bsdextended/mac_bsdextended.c +++ b/sys/security/mac_bsdextended/mac_bsdextended.c @@ -47,6 +47,8 @@ * firewall-like rules regarding users and file system objects. */ +#include "opt_pax.h" + #include #include #include @@ -56,14 +58,20 @@ #include #include #include +#include #include #include #include #include #include #include +#include #include +#ifdef PAX_ASLR +#include +#endif + #include #include #include @@ -117,7 +125,6 @@ SYSCTL_INT(_security_mac_bsdextended, OID_AUTO, firstmatch_enabled, static int ugidfw_rule_valid(struct mac_bsdextended_rule *rule) { - if ((rule->mbr_subject.mbs_flags | MBS_ALL_FLAGS) != MBS_ALL_FLAGS) return (EINVAL); if ((rule->mbr_subject.mbs_neg | MBS_ALL_FLAGS) != MBS_ALL_FLAGS) @@ -129,8 +136,13 @@ ugidfw_rule_valid(struct mac_bsdextended_rule *rule) if ((rule->mbr_object.mbo_neg | MBO_TYPE_DEFINED) && (rule->mbr_object.mbo_type | MBO_ALL_TYPE) != MBO_ALL_TYPE) return (EINVAL); +#ifdef PAX_ASLR + if ((rule->mbr_pax | MBI_ALLPAX) != MBI_ALLPAX) + return (EINVAL); +#endif if ((rule->mbr_mode | MBI_ALLPERM) != MBI_ALLPERM) return (EINVAL); + return (0); } @@ -227,7 +239,7 @@ ugidfw_destroy(struct mac_policy_conf *mpc) static int ugidfw_rulecheck(struct mac_bsdextended_rule *rule, - struct ucred *cred, struct vnode *vp, struct vattr *vap, int acc_mode) + struct ucred *cred, struct vnode *vp, struct vattr *vap, int acc_mode, struct image_params *imgp) { int mac_granted, match, priv_granted; int i; @@ -305,6 +317,10 @@ ugidfw_rulecheck(struct mac_bsdextended_rule *rule, match = (bcmp(&(vp->v_mount->mnt_stat.f_fsid), &(rule->mbr_object.mbo_fsid), sizeof(rule->mbr_object.mbo_fsid)) == 0); +#if defined(PAX_ASLR) + if (match && rule->mbr_object.mbo_inode) + match = (vap->va_fileid == rule->mbr_object.mbo_inode); +#endif if (rule->mbr_object.mbo_neg & MBO_FSID_DEFINED) match = !match; if (!match) @@ -413,6 +429,11 @@ ugidfw_rulecheck(struct mac_bsdextended_rule *rule, return (EACCES); } +#ifdef PAX_ASLR + if (imgp != NULL) + pax_elf(imgp, rule->mbr_pax); +#endif + /* * If the rule matched, permits access, and first match is enabled, * return success. @@ -425,7 +446,7 @@ ugidfw_rulecheck(struct mac_bsdextended_rule *rule, int ugidfw_check(struct ucred *cred, struct vnode *vp, struct vattr *vap, - int acc_mode) + int acc_mode, struct image_params *imgp) { int error, i; @@ -441,7 +462,7 @@ ugidfw_check(struct ucred *cred, struct vnode *vp, struct vattr *vap, if (rules[i] == NULL) continue; error = ugidfw_rulecheck(rules[i], cred, - vp, vap, acc_mode); + vp, vap, acc_mode, imgp); if (error == EJUSTRETURN) break; if (error) { @@ -454,7 +475,7 @@ ugidfw_check(struct ucred *cred, struct vnode *vp, struct vattr *vap, } int -ugidfw_check_vp(struct ucred *cred, struct vnode *vp, int acc_mode) +ugidfw_check_vp(struct ucred *cred, struct vnode *vp, int acc_mode, struct image_params *imgp) { int error; struct vattr vap; @@ -464,7 +485,7 @@ ugidfw_check_vp(struct ucred *cred, struct vnode *vp, int acc_mode) error = VOP_GETATTR(vp, &vap, cred); if (error) return (error); - return (ugidfw_check(cred, vp, &vap, acc_mode)); + return (ugidfw_check(cred, vp, &vap, acc_mode, imgp)); } int diff --git a/sys/security/mac_bsdextended/mac_bsdextended.h b/sys/security/mac_bsdextended/mac_bsdextended.h index c09abc0..c3cbf28 100644 --- a/sys/security/mac_bsdextended/mac_bsdextended.h +++ b/sys/security/mac_bsdextended/mac_bsdextended.h @@ -51,6 +51,9 @@ #define MBI_ADMIN 010000 #define MBI_STAT 020000 #define MBI_APPEND 040000 +#define MBI_FORCE_ASLR_ENABLED 0x01 +#define MBI_FORCE_ASLR_DISABLED 0x02 +#define MBI_ALLPAX (MBI_FORCE_ASLR_ENABLED | MBI_FORCE_ASLR_DISABLED) #define MBI_ALLPERM (MBI_EXEC | MBI_WRITE | MBI_READ | MBI_ADMIN | \ MBI_STAT | MBI_APPEND) @@ -78,6 +81,7 @@ struct mac_bsdextended_subject { #define MBO_UID_SUBJECT 0x00000020 /* uid must match subject */ #define MBO_GID_SUBJECT 0x00000040 /* gid must match subject */ #define MBO_TYPE_DEFINED 0x00000080 /* object type should be matched */ +#define MBO_PAXPATH_DEFINED 0x00000100 /* TODO: paxpath should be matched */ #define MBO_ALL_FLAGS (MBO_UID_DEFINED | MBO_GID_DEFINED | MBO_FSID_DEFINED | \ MBO_SUID | MBO_SGID | MBO_UID_SUBJECT | MBO_GID_SUBJECT | \ @@ -103,12 +107,15 @@ struct mac_bsdextended_object { gid_t mbo_gid_max; struct fsid mbo_fsid; int mbo_type; + ino_t mbo_inode; + char mbo_paxpath[MAXPATHLEN]; }; struct mac_bsdextended_rule { struct mac_bsdextended_subject mbr_subject; struct mac_bsdextended_object mbr_object; mode_t mbr_mode; /* maximum access */ + uint32_t mbr_pax; }; #endif /* _SYS_SECURITY_MAC_BSDEXTENDED_H */ diff --git a/sys/security/mac_bsdextended/ugidfw_internal.h b/sys/security/mac_bsdextended/ugidfw_internal.h index 5597fd1..18c74dc 100644 --- a/sys/security/mac_bsdextended/ugidfw_internal.h +++ b/sys/security/mac_bsdextended/ugidfw_internal.h @@ -36,8 +36,9 @@ */ int ugidfw_accmode2mbi(accmode_t accmode); int ugidfw_check(struct ucred *cred, struct vnode *vp, struct vattr *vap, - int acc_mode); -int ugidfw_check_vp(struct ucred *cred, struct vnode *vp, int acc_mode); + int acc_mode, struct image_params *imgp); +int ugidfw_check_vp(struct ucred *cred, struct vnode *vp, int acc_mode, + struct image_params *imgp); /* * System access control checks. diff --git a/sys/security/mac_bsdextended/ugidfw_system.c b/sys/security/mac_bsdextended/ugidfw_system.c index 49e4f1d..2829a00 100644 --- a/sys/security/mac_bsdextended/ugidfw_system.c +++ b/sys/security/mac_bsdextended/ugidfw_system.c @@ -66,7 +66,7 @@ ugidfw_system_check_acct(struct ucred *cred, struct vnode *vp, { if (vp != NULL) - return (ugidfw_check_vp(cred, vp, MBI_WRITE)); + return (ugidfw_check_vp(cred, vp, MBI_WRITE, NULL)); else return (0); } @@ -77,7 +77,7 @@ ugidfw_system_check_auditctl(struct ucred *cred, struct vnode *vp, { if (vp != NULL) - return (ugidfw_check_vp(cred, vp, MBI_WRITE)); + return (ugidfw_check_vp(cred, vp, MBI_WRITE, NULL)); else return (0); } @@ -87,5 +87,5 @@ ugidfw_system_check_swapon(struct ucred *cred, struct vnode *vp, struct label *vplabel) { - return (ugidfw_check_vp(cred, vp, MBI_WRITE)); + return (ugidfw_check_vp(cred, vp, MBI_WRITE, NULL)); } diff --git a/sys/security/mac_bsdextended/ugidfw_vnode.c b/sys/security/mac_bsdextended/ugidfw_vnode.c index 8ec2d48..2065e6e 100644 --- a/sys/security/mac_bsdextended/ugidfw_vnode.c +++ b/sys/security/mac_bsdextended/ugidfw_vnode.c @@ -65,7 +65,7 @@ ugidfw_vnode_check_access(struct ucred *cred, struct vnode *vp, struct label *vplabel, accmode_t accmode) { - return (ugidfw_check_vp(cred, vp, ugidfw_accmode2mbi(accmode))); + return (ugidfw_check_vp(cred, vp, ugidfw_accmode2mbi(accmode), NULL)); } int @@ -73,7 +73,7 @@ ugidfw_vnode_check_chdir(struct ucred *cred, struct vnode *dvp, struct label *dvplabel) { - return (ugidfw_check_vp(cred, dvp, MBI_EXEC)); + return (ugidfw_check_vp(cred, dvp, MBI_EXEC, NULL)); } int @@ -81,7 +81,7 @@ ugidfw_vnode_check_chroot(struct ucred *cred, struct vnode *dvp, struct label *dvplabel) { - return (ugidfw_check_vp(cred, dvp, MBI_EXEC)); + return (ugidfw_check_vp(cred, dvp, MBI_EXEC, NULL)); } int @@ -89,7 +89,7 @@ ugidfw_check_create_vnode(struct ucred *cred, struct vnode *dvp, struct label *dvplabel, struct componentname *cnp, struct vattr *vap) { - return (ugidfw_check_vp(cred, dvp, MBI_WRITE)); + return (ugidfw_check_vp(cred, dvp, MBI_WRITE, NULL)); } int @@ -97,7 +97,7 @@ ugidfw_vnode_check_deleteacl(struct ucred *cred, struct vnode *vp, struct label *vplabel, acl_type_t type) { - return (ugidfw_check_vp(cred, vp, MBI_ADMIN)); + return (ugidfw_check_vp(cred, vp, MBI_ADMIN, NULL)); } int @@ -105,7 +105,7 @@ ugidfw_vnode_check_deleteextattr(struct ucred *cred, struct vnode *vp, struct label *vplabel, int attrnamespace, const char *name) { - return (ugidfw_check_vp(cred, vp, MBI_WRITE)); + return (ugidfw_check_vp(cred, vp, MBI_WRITE, NULL)); } int @@ -114,7 +114,7 @@ ugidfw_vnode_check_exec(struct ucred *cred, struct vnode *vp, struct label *execlabel) { - return (ugidfw_check_vp(cred, vp, MBI_READ|MBI_EXEC)); + return (ugidfw_check_vp(cred, vp, MBI_READ|MBI_EXEC, imgp)); } int @@ -122,7 +122,7 @@ ugidfw_vnode_check_getacl(struct ucred *cred, struct vnode *vp, struct label *vplabel, acl_type_t type) { - return (ugidfw_check_vp(cred, vp, MBI_STAT)); + return (ugidfw_check_vp(cred, vp, MBI_STAT, NULL)); } int @@ -130,7 +130,7 @@ ugidfw_vnode_check_getextattr(struct ucred *cred, struct vnode *vp, struct label *vplabel, int attrnamespace, const char *name) { - return (ugidfw_check_vp(cred, vp, MBI_READ)); + return (ugidfw_check_vp(cred, vp, MBI_READ, NULL)); } int @@ -140,10 +140,10 @@ ugidfw_vnode_check_link(struct ucred *cred, struct vnode *dvp, { int error; - error = ugidfw_check_vp(cred, dvp, MBI_WRITE); + error = ugidfw_check_vp(cred, dvp, MBI_WRITE, NULL); if (error) return (error); - error = ugidfw_check_vp(cred, vp, MBI_WRITE); + error = ugidfw_check_vp(cred, vp, MBI_WRITE, NULL); if (error) return (error); return (0); @@ -154,7 +154,7 @@ ugidfw_vnode_check_listextattr(struct ucred *cred, struct vnode *vp, struct label *vplabel, int attrnamespace) { - return (ugidfw_check_vp(cred, vp, MBI_READ)); + return (ugidfw_check_vp(cred, vp, MBI_READ, NULL)); } int @@ -162,7 +162,7 @@ ugidfw_vnode_check_lookup(struct ucred *cred, struct vnode *dvp, struct label *dvplabel, struct componentname *cnp) { - return (ugidfw_check_vp(cred, dvp, MBI_EXEC)); + return (ugidfw_check_vp(cred, dvp, MBI_EXEC, NULL)); } int @@ -170,7 +170,7 @@ ugidfw_vnode_check_open(struct ucred *cred, struct vnode *vp, struct label *vplabel, accmode_t accmode) { - return (ugidfw_check_vp(cred, vp, ugidfw_accmode2mbi(accmode))); + return (ugidfw_check_vp(cred, vp, ugidfw_accmode2mbi(accmode), NULL)); } int @@ -178,7 +178,7 @@ ugidfw_vnode_check_readdir(struct ucred *cred, struct vnode *dvp, struct label *dvplabel) { - return (ugidfw_check_vp(cred, dvp, MBI_READ)); + return (ugidfw_check_vp(cred, dvp, MBI_READ, NULL)); } int @@ -186,7 +186,7 @@ ugidfw_vnode_check_readdlink(struct ucred *cred, struct vnode *vp, struct label *vplabel) { - return (ugidfw_check_vp(cred, vp, MBI_READ)); + return (ugidfw_check_vp(cred, vp, MBI_READ, NULL)); } int @@ -196,10 +196,10 @@ ugidfw_vnode_check_rename_from(struct ucred *cred, struct vnode *dvp, { int error; - error = ugidfw_check_vp(cred, dvp, MBI_WRITE); + error = ugidfw_check_vp(cred, dvp, MBI_WRITE, NULL); if (error) return (error); - return (ugidfw_check_vp(cred, vp, MBI_WRITE)); + return (ugidfw_check_vp(cred, vp, MBI_WRITE, NULL)); } int @@ -209,11 +209,11 @@ ugidfw_vnode_check_rename_to(struct ucred *cred, struct vnode *dvp, { int error; - error = ugidfw_check_vp(cred, dvp, MBI_WRITE); + error = ugidfw_check_vp(cred, dvp, MBI_WRITE, NULL); if (error) return (error); if (vp != NULL) - error = ugidfw_check_vp(cred, vp, MBI_WRITE); + error = ugidfw_check_vp(cred, vp, MBI_WRITE, NULL); return (error); } @@ -222,7 +222,7 @@ ugidfw_vnode_check_revoke(struct ucred *cred, struct vnode *vp, struct label *vplabel) { - return (ugidfw_check_vp(cred, vp, MBI_ADMIN)); + return (ugidfw_check_vp(cred, vp, MBI_ADMIN, NULL)); } int @@ -230,7 +230,7 @@ ugidfw_check_setacl_vnode(struct ucred *cred, struct vnode *vp, struct label *vplabel, acl_type_t type, struct acl *acl) { - return (ugidfw_check_vp(cred, vp, MBI_ADMIN)); + return (ugidfw_check_vp(cred, vp, MBI_ADMIN, NULL)); } int @@ -238,7 +238,7 @@ ugidfw_vnode_check_setextattr(struct ucred *cred, struct vnode *vp, struct label *vplabel, int attrnamespace, const char *name) { - return (ugidfw_check_vp(cred, vp, MBI_WRITE)); + return (ugidfw_check_vp(cred, vp, MBI_WRITE, NULL)); } int @@ -246,7 +246,7 @@ ugidfw_vnode_check_setflags(struct ucred *cred, struct vnode *vp, struct label *vplabel, u_long flags) { - return (ugidfw_check_vp(cred, vp, MBI_ADMIN)); + return (ugidfw_check_vp(cred, vp, MBI_ADMIN, NULL)); } int @@ -254,7 +254,7 @@ ugidfw_vnode_check_setmode(struct ucred *cred, struct vnode *vp, struct label *vplabel, mode_t mode) { - return (ugidfw_check_vp(cred, vp, MBI_ADMIN)); + return (ugidfw_check_vp(cred, vp, MBI_ADMIN, NULL)); } int @@ -262,7 +262,7 @@ ugidfw_vnode_check_setowner(struct ucred *cred, struct vnode *vp, struct label *vplabel, uid_t uid, gid_t gid) { - return (ugidfw_check_vp(cred, vp, MBI_ADMIN)); + return (ugidfw_check_vp(cred, vp, MBI_ADMIN, NULL)); } int @@ -270,7 +270,7 @@ ugidfw_vnode_check_setutimes(struct ucred *cred, struct vnode *vp, struct label *vplabel, struct timespec atime, struct timespec utime) { - return (ugidfw_check_vp(cred, vp, MBI_ADMIN)); + return (ugidfw_check_vp(cred, vp, MBI_ADMIN, NULL)); } int @@ -278,7 +278,7 @@ ugidfw_vnode_check_stat(struct ucred *active_cred, struct ucred *file_cred, struct vnode *vp, struct label *vplabel) { - return (ugidfw_check_vp(active_cred, vp, MBI_STAT)); + return (ugidfw_check_vp(active_cred, vp, MBI_STAT, NULL)); } int @@ -288,8 +288,8 @@ ugidfw_vnode_check_unlink(struct ucred *cred, struct vnode *dvp, { int error; - error = ugidfw_check_vp(cred, dvp, MBI_WRITE); + error = ugidfw_check_vp(cred, dvp, MBI_WRITE, NULL); if (error) return (error); - return (ugidfw_check_vp(cred, vp, MBI_WRITE)); + return (ugidfw_check_vp(cred, vp, MBI_WRITE, NULL)); } diff --git a/sys/sparc64/sparc64/elf_machdep.c b/sys/sparc64/sparc64/elf_machdep.c index 4d55717..e0eba33 100644 --- a/sys/sparc64/sparc64/elf_machdep.c +++ b/sys/sparc64/sparc64/elf_machdep.c @@ -34,6 +34,8 @@ #include __FBSDID("$FreeBSD$"); +#include "opt_pax.h" + #include #include #include @@ -52,6 +54,10 @@ __FBSDID("$FreeBSD$"); #include +#ifdef PAX_ASLR +#include +#endif + #include "linker_if.h" static struct sysentvec elf64_freebsd_sysvec = { @@ -87,6 +93,11 @@ static struct sysentvec elf64_freebsd_sysvec = { .sv_fetch_syscall_args = cpu_fetch_syscall_args, .sv_syscallnames = syscallnames, .sv_schedtail = NULL, +#ifdef PAX_ASLR + .sv_pax_aslr_init = _pax_aslr_init, +#else + .sv_pax_aslr_init = NULL, +#endif }; static Elf64_Brandinfo freebsd_brand_info = { diff --git a/sys/sys/imgact.h b/sys/sys/imgact.h index 17cfcc2..15c2c4f 100644 --- a/sys/sys/imgact.h +++ b/sys/sys/imgact.h @@ -78,6 +78,7 @@ struct image_params { unsigned long pagesizes; int pagesizeslen; vm_prot_t stack_prot; + int pax_flags; }; #ifdef _KERNEL diff --git a/sys/sys/jail.h b/sys/sys/jail.h index 59d791c..699b21c 100644 --- a/sys/sys/jail.h +++ b/sys/sys/jail.h @@ -184,6 +184,19 @@ struct prison { char pr_hostname[MAXHOSTNAMELEN]; /* (p) jail hostname */ char pr_domainname[MAXHOSTNAMELEN]; /* (p) jail domainname */ char pr_hostuuid[HOSTUUIDLEN]; /* (p) jail hostuuid */ + /* Lock only needed for pax_* if pr_pax_set == 0 */ + int pr_pax_set; /* (p) PaX settings initialized */ + int pr_pax_aslr_status; /* (p) PaX ASLR enabled */ + int pr_pax_aslr_debug; /* (p) PaX ASLR debug */ + int pr_pax_aslr_mmap_len; /* (p) Number of bits randomized with mmap */ + int pr_pax_aslr_stack_len; /* (p) Number of bits randomized with stack */ + int pr_pax_aslr_exec_len; /* (p) Number of bits randomized with the execbase */ + int pr_pax_aslr_compat_status; /* (p) PaX ASLR enabled (compat32) */ + int pr_pax_aslr_compat_mmap_len; /* (p) Number of bits randomized with mmap (compat32) */ + int pr_pax_aslr_compat_stack_len; /* (p) Number of bits randomized with stack (compat32) */ + int pr_pax_aslr_compat_exec_len; /* (p) Number of bits randomized with the execbase (compat32) */ + int pr_pax_log_log; /* (p) XXX */ + int pr_pax_log_ulog; /* (p) XXX */ }; struct prison_racct { diff --git a/sys/sys/kernel.h b/sys/sys/kernel.h index 3c5258a..aedb52e 100644 --- a/sys/sys/kernel.h +++ b/sys/sys/kernel.h @@ -102,6 +102,7 @@ enum sysinit_sub_id { SI_SUB_WITNESS = 0x1A80000, /* witness initialization */ SI_SUB_MTX_POOL_DYNAMIC = 0x1AC0000, /* dynamic mutex pool */ SI_SUB_LOCK = 0x1B00000, /* various locks */ + SI_SUB_PAX = 0x1B50000, /* pax setup */ SI_SUB_EVENTHANDLER = 0x1C00000, /* eventhandler init */ SI_SUB_VNET_PRELINK = 0x1E00000, /* vnet init before modules */ SI_SUB_KLD = 0x2000000, /* KLD and module setup */ diff --git a/sys/sys/pax.h b/sys/sys/pax.h new file mode 100644 index 0000000..a0f2bf6 --- /dev/null +++ b/sys/sys/pax.h @@ -0,0 +1,226 @@ +/*- + * Copyright (c) 2006 Elad Efrat + * Copyright (c) 2013-2014, by Oliver Pinter + * Copyright (c) 2014, by Shawn Webb + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#ifndef __SYS_PAX_H +#define __SYS_PAX_H + +struct image_params; +struct prison; +struct thread; +struct vnode; +struct vmspace; +struct vm_offset_t; + +/* + * used in sysctl handler + */ +#define PAX_ASLR_DISABLED 0 +#define PAX_ASLR_OPTIN 1 +#define PAX_ASLR_OPTOUT 2 +#define PAX_ASLR_FORCE_ENABLED 3 + +#ifndef PAX_ASLR_DELTA +#define PAX_ASLR_DELTA(delta, lsb, len) \ + (((delta) & ((1UL << (len)) - 1)) << (lsb)) +#endif /* PAX_ASLR_DELTA */ + +#ifdef PAX_ASLR +/* + * generic ASLR values + * + * MMAP | 32 bit | 64 bit | + * +-------+--------+--------+ + * | MIN | 8 bit | 16 bit | + * +-------+--------+--------+ + * | DEF | 8 bit | 21 bit | + * +-------+--------+--------+ + * | MAX | 16 bit | 32 bit | + * +-------+--------+--------+ + * + * STACK | 32 bit | 64 bit | + * +-------+--------+--------+ + * | MIN | 6 bit | 12 bit | + * +-------+--------+--------+ + * | DEF | 6 bit | 16 bit | + * +-------+--------+--------+ + * | MAX | 10 bit | 21 bit | + * +-------+--------+--------+ + * + * EXEC | 32 bit | 64 bit | + * +-------+--------+--------+ + * | MIN | 6 bit | 12 bit | + * +-------+--------+--------+ + * | DEF | 6 bit | 21 bit | + * +-------+--------+--------+ + * | MAX | 10 bit | 21 bit | + * +-------+--------+--------+ + * + */ +#ifndef PAX_ASLR_DELTA_MMAP_LSB +#define PAX_ASLR_DELTA_MMAP_LSB PAGE_SHIFT +#endif /* PAX_ASLR_DELTA_MMAP_LSB */ + +#ifndef PAX_ASLR_DELTA_MMAP_MIN_LEN +#define PAX_ASLR_DELTA_MMAP_MIN_LEN ((sizeof(void *) * NBBY) / 4) +#endif /* PAX_ASLR_DELTA_MMAP_MAX_LEN */ + +#ifndef PAX_ASLR_DELTA_MMAP_MAX_LEN +#define PAX_ASLR_DELTA_MMAP_MAX_LEN ((sizeof(void *) * NBBY) / 2) +#endif /* PAX_ASLR_DELTA_MMAP_MAX_LEN */ + +#ifndef PAX_ASLR_DELTA_STACK_LSB +#define PAX_ASLR_DELTA_STACK_LSB 3 +#endif /* PAX_ASLR_DELTA_STACK_LSB */ + +#ifndef PAX_ASLR_DELTA_STACK_MIN_LEN +#define PAX_ASLR_DELTA_STACK_MIN_LEN ((sizeof(void *) * NBBY) / 5) +#endif /* PAX_ASLR_DELTA_STACK_MAX_LEN */ + +#ifndef PAX_ASLR_DELTA_STACK_MAX_LEN +#define PAX_ASLR_DELTA_STACK_MAX_LEN ((sizeof(void *) * NBBY) / 3) +#endif /* PAX_ASLR_DELTA_STACK_MAX_LEN */ + +#ifndef PAX_ASLR_DELTA_EXEC_LSB +#define PAX_ASLR_DELTA_EXEC_LSB PAGE_SHIFT +#endif /* PAX_ASLR_DELTA_EXEC_LSB */ + +#ifndef PAX_ASLR_DELTA_EXEC_MIN_LEN +#define PAX_ASLR_DELTA_EXEC_MIN_LEN ((sizeof(void *) * NBBY) / 5) +#endif /* PAX_ASLR_DELTA_EXEC_MAX_LEN */ + +#ifndef PAX_ASLR_DELTA_EXEC_MAX_LEN +#define PAX_ASLR_DELTA_EXEC_MAX_LEN ((sizeof(void *) * NBBY) / 3) +#endif /* PAX_ASLR_DELTA_EXEC_MAX_LEN */ + +/* + * ASLR default values for native host + */ +#ifdef __amd64__ +#ifndef PAX_ASLR_DELTA_MMAP_DEF_LEN +#define PAX_ASLR_DELTA_MMAP_DEF_LEN 21 +#endif /* PAX_ASLR_DELTA_MMAP_DEF_LEN */ +#ifndef PAX_ASLR_DELTA_STACK_DEF_LEN +#define PAX_ASLR_DELTA_STACK_DEF_LEN 16 +#endif /* PAX_ASLR_DELTA_STACK_DEF_LEN */ +#ifndef PAX_ASLR_DELTA_EXEC_DEF_LEN +#define PAX_ASLR_DELTA_EXEC_DEF_LEN 21 +#endif /* PAX_ASLR_DELTA_EXEC_DEF_LEN */ +#else +#ifndef PAX_ASLR_DELTA_MMAP_DEF_LEN +#define PAX_ASLR_DELTA_MMAP_DEF_LEN PAX_ASLR_DELTA_MMAP_MIN_LEN +#endif /* PAX_ASLR_DELTA_MMAP_DEF_LEN */ +#ifndef PAX_ASLR_DELTA_STACK_DEF_LEN +#define PAX_ASLR_DELTA_STACK_DEF_LEN PAX_ASLR_DELTA_STACK_MIN_LEN +#endif /* PAX_ASLR_DELTA_STACK_DEF_LEN */ +#ifndef PAX_ASLR_DELTA_EXEC_DEF_LEN +#define PAX_ASLR_DELTA_EXEC_DEF_LEN PAX_ASLR_DELTA_EXEC_MIN_LEN +#endif /* PAX_ASLR_DELTA_EXEC_DEF_LEN */ +#endif /* __amd64__ */ + +/* + * ASLR values for COMPAT_FREEBSD32 and COMPAT_LINUX + */ +#ifndef PAX_ASLR_COMPAT_DELTA_MMAP_LSB +#define PAX_ASLR_COMPAT_DELTA_MMAP_LSB PAGE_SHIFT +#endif /* PAX_ASLR_COMPAT_DELTA_MMAP_LSB */ + +#ifndef PAX_ASLR_COMPAT_DELTA_MMAP_MIN_LEN +#define PAX_ASLR_COMPAT_DELTA_MMAP_MIN_LEN ((sizeof(int) * NBBY) / 4) +#endif /* PAX_ASLR_COMPAT_DELTA_MMAP_MAX_LEN */ + +#ifndef PAX_ASLR_COMPAT_DELTA_MMAP_MAX_LEN +#define PAX_ASLR_COMPAT_DELTA_MMAP_MAX_LEN ((sizeof(int) * NBBY) / 2) +#endif /* PAX_ASLR_COMPAT_DELTA_MMAP_MAX_LEN */ + +#ifndef PAX_ASLR_COMPAT_DELTA_STACK_LSB +#define PAX_ASLR_COMPAT_DELTA_STACK_LSB 3 +#endif /* PAX_ASLR_COMPAT_DELTA_STACK_LSB */ + +#ifndef PAX_ASLR_COMPAT_DELTA_STACK_MIN_LEN +#define PAX_ASLR_COMPAT_DELTA_STACK_MIN_LEN ((sizeof(int) * NBBY) / 5) +#endif /* PAX_ASLR_COMPAT_DELTA_STACK_MAX_LEN */ + +#ifndef PAX_ASLR_COMPAT_DELTA_STACK_MAX_LEN +#define PAX_ASLR_COMPAT_DELTA_STACK_MAX_LEN ((sizeof(int) * NBBY) / 3) +#endif /* PAX_ASLR_COMPAT_DELTA_STACK_MAX_LEN */ + +#ifndef PAX_ASLR_COMPAT_DELTA_EXEC_MIN_LEN +#define PAX_ASLR_COMPAT_DELTA_EXEC_MIN_LEN ((sizeof(int) * NBBY) / 5) +#endif /* PAX_ASLR_COMPAT_DELTA_EXEC_MAX_LEN */ + +#ifndef PAX_ASLR_COMPAT_DELTA_EXEC_MAX_LEN +#define PAX_ASLR_COMPAT_DELTA_EXEC_MAX_LEN ((sizeof(int) * NBBY) / 3) +#endif /* PAX_ASLR_COMPAT_DELTA_EXEC_MAX_LEN */ + +extern int pax_aslr_status; +extern int pax_aslr_debug; + +extern int pax_aslr_mmap_len; +extern int pax_aslr_stack_len; +extern int pax_aslr_exec_len; +#ifdef COMPAT_FREEBSD32 +extern int pax_aslr_compat_status; +extern int pax_aslr_compat_mmap_len; +extern int pax_aslr_compat_stack_len; +extern int pax_aslr_compat_exec_len; +#endif /* COMPAT_FREEBSD32 */ +#endif /* PAX_ASLR */ + +extern int pax_log_log; +extern int pax_log_ulog; + +#define ELF_NOTE_TYPE_PAX_TAG 3 +#define PAX_NOTE_MPROTECT 0x01 +#define PAX_NOTE_NOMPROTECT 0x02 +#define PAX_NOTE_GUARD 0x04 +#define PAX_NOTE_NOGUARD 0x08 +#define PAX_NOTE_ASLR 0x10 +#define PAX_NOTE_NOASLR 0x20 + +#define PAX_LOG_LOG 0 +#define PAX_LOG_ULOG 0 + +void pax_init(void); +void pax_init_prison(struct prison *pr); +bool pax_aslr_active(struct thread *td, struct proc *proc); +void _pax_aslr_init(struct vmspace *vm, struct prison *pr); +void _pax_aslr_init32(struct vmspace *vm, struct prison *pr); +void pax_aslr_init(struct thread *td, struct image_params *imgp); +void pax_aslr_mmap(struct thread *td, vm_offset_t *addr, + vm_offset_t orig_addr, int flags); +void pax_aslr_stack(struct thread *td, uintptr_t *addr); +struct prison *pax_get_prison(struct thread *td, struct proc *proc); +void pax_elf(struct image_params *, uint32_t); + +void pax_log_aslr(struct prison *pr, const char *func, const char *fmt, ...); +void pax_ulog_aslr(struct prison *pr, const char *func, const char *fmt, ...); + +#endif /* __SYS_PAX_H */ diff --git a/sys/sys/proc.h b/sys/sys/proc.h index fbd064c..558d7bf 100644 --- a/sys/sys/proc.h +++ b/sys/sys/proc.h @@ -539,6 +539,7 @@ struct proc { u_int p_stops; /* (c) Stop event bitmask. */ u_int p_stype; /* (c) Stop event type. */ char p_step; /* (c) Process is stopped. */ + u_int p_pax; /* (b) PaX is enabled to this process */ u_char p_pfsflags; /* (c) Procfs flags. */ struct nlminfo *p_nlminfo; /* (?) Only used by/for lockd. */ struct kaioinfo *p_aioinfo; /* (y) ASYNC I/O info. */ diff --git a/sys/sys/sysent.h b/sys/sys/sysent.h index c49db41..cfbcdc0 100644 --- a/sys/sys/sysent.h +++ b/sys/sys/sysent.h @@ -77,9 +77,11 @@ struct sysent { /* system call table */ #define SY_THR_INCR 0x8 struct image_params; +struct prison; struct __sigset; struct syscall_args; struct trapframe; +struct vmspace; struct vnode; struct sysentvec { @@ -130,6 +132,7 @@ struct sysentvec { uint32_t sv_timekeep_gen; void *sv_shared_page_obj; void (*sv_schedtail)(struct thread *); + void (*sv_pax_aslr_init)(struct vmspace *vm, struct prison *pr); }; #define SV_ILP32 0x000100 diff --git a/sys/vm/vm_map.c b/sys/vm/vm_map.c index d8ba33f..4ba8106 100644 --- a/sys/vm/vm_map.c +++ b/sys/vm/vm_map.c @@ -65,6 +65,8 @@ #include __FBSDID("$FreeBSD$"); +#include "opt_pax.h" + #include #include #include @@ -292,6 +294,12 @@ vmspace_alloc(vm_offset_t min, vm_offset_t max, pmap_pinit_t pinit) vm->vm_taddr = 0; vm->vm_daddr = 0; vm->vm_maxsaddr = 0; +#ifdef PAX_ASLR + vm->vm_aslr_delta_mmap = 0; + vm->vm_aslr_delta_stack = 0; + vm->vm_aslr_delta_exec = 0; +#endif + return (vm); } diff --git a/sys/vm/vm_map.h b/sys/vm/vm_map.h index 8cced05..e8e9ffe 100644 --- a/sys/vm/vm_map.h +++ b/sys/vm/vm_map.h @@ -241,6 +241,9 @@ struct vmspace { caddr_t vm_taddr; /* (c) user virtual address of text */ caddr_t vm_daddr; /* (c) user virtual address of data */ caddr_t vm_maxsaddr; /* user VA at max stack growth */ + vm_size_t vm_aslr_delta_mmap; /* mmap() random delta for ASLR */ + vm_size_t vm_aslr_delta_stack; /* stack random delta for ASLR */ + vm_size_t vm_aslr_delta_exec; /* exec base random delta for ASLR */ volatile int vm_refcnt; /* number of references */ /* * Keep the PMAP last, so that CPU-specific variations of that diff --git a/sys/vm/vm_mmap.c b/sys/vm/vm_mmap.c index a524839..fd8876e 100644 --- a/sys/vm/vm_mmap.c +++ b/sys/vm/vm_mmap.c @@ -45,6 +45,7 @@ __FBSDID("$FreeBSD$"); #include "opt_compat.h" #include "opt_hwpmc_hooks.h" +#include "opt_pax.h" #include #include @@ -91,6 +92,10 @@ __FBSDID("$FreeBSD$"); #include #endif +#ifdef PAX_ASLR +#include +#endif + int old_mlock = 0; SYSCTL_INT(_vm, OID_AUTO, old_mlock, CTLFLAG_RW | CTLFLAG_TUN, &old_mlock, 0, "Do not apply RLIMIT_MEMLOCK on mlockall"); @@ -203,6 +208,9 @@ sys_mmap(td, uap) struct file *fp; struct vnode *vp; vm_offset_t addr; +#ifdef PAX_ASLR + vm_offset_t orig_addr; +#endif vm_size_t size, pageoff; vm_prot_t cap_maxprot, prot, maxprot; void *handle; @@ -213,6 +221,9 @@ sys_mmap(td, uap) cap_rights_t rights; addr = (vm_offset_t) uap->addr; +#ifdef PAX_ASLR + orig_addr = addr; +#endif size = uap->len; prot = uap->prot & VM_PROT_ALL; flags = uap->flags; @@ -416,6 +427,9 @@ sys_mmap(td, uap) map: td->td_fpop = fp; maxprot &= cap_maxprot; +#ifdef PAX_ASLR + pax_aslr_mmap(td, &addr, orig_addr, flags); +#endif error = vm_mmap(&vms->vm_map, &addr, size, prot, maxprot, flags, handle_type, handle, pos); td->td_fpop = NULL; diff --git a/tools/build/options/WITHOUT_PIE b/tools/build/options/WITHOUT_PIE new file mode 100644 index 0000000..82019ce --- /dev/null +++ b/tools/build/options/WITHOUT_PIE @@ -0,0 +1 @@ +Enable building of Position-Independent Executables (PIEs). diff --git a/usr.sbin/ugidfw/ugidfw.c b/usr.sbin/ugidfw/ugidfw.c index 977922a..515df16 100644 --- a/usr.sbin/ugidfw/ugidfw.c +++ b/usr.sbin/ugidfw/ugidfw.c @@ -46,6 +46,8 @@ __FBSDID("$FreeBSD$"); #include #include +#define UGIDFW_BUFSIZ (BUFSIZ*2) + void add_rule(int argc, char *argv[]); void list_rules(void); void remove_rule(int argc, char *argv[]); @@ -71,22 +73,22 @@ usage(void) void add_rule(int argc, char *argv[]) { - char errstr[BUFSIZ], charstr[BUFSIZ]; + char errstr[UGIDFW_BUFSIZ], charstr[UGIDFW_BUFSIZ]; struct mac_bsdextended_rule rule; int error, rulenum; - error = bsde_parse_rule(argc, argv, &rule, BUFSIZ, errstr); + error = bsde_parse_rule(argc, argv, &rule, UGIDFW_BUFSIZ, errstr); if (error) { warnx("%s", errstr); return; } - error = bsde_add_rule(&rulenum, &rule, BUFSIZ, errstr); + error = bsde_add_rule(&rulenum, &rule, UGIDFW_BUFSIZ, errstr); if (error) { warnx("%s", errstr); return; } - if (bsde_rule_to_string(&rule, charstr, BUFSIZ) == -1) + if (bsde_rule_to_string(&rule, charstr, UGIDFW_BUFSIZ) == -1) warnx("Added rule, but unable to print string."); else printf("%d %s\n", rulenum, charstr); @@ -95,25 +97,25 @@ add_rule(int argc, char *argv[]) void list_rules(void) { - char errstr[BUFSIZ], charstr[BUFSIZ]; + char errstr[UGIDFW_BUFSIZ], charstr[UGIDFW_BUFSIZ]; struct mac_bsdextended_rule rule; int error, i, rule_count, rule_slots; - rule_slots = bsde_get_rule_slots(BUFSIZ, errstr); + rule_slots = bsde_get_rule_slots(UGIDFW_BUFSIZ, errstr); if (rule_slots == -1) { warnx("unable to get rule slots; mac_bsdextended.ko " "may not be loaded"); errx(1, "bsde_get_rule_slots: %s", errstr); } - rule_count = bsde_get_rule_count(BUFSIZ, errstr); + rule_count = bsde_get_rule_count(UGIDFW_BUFSIZ, errstr); if (rule_count == -1) errx(1, "bsde_get_rule_count: %s", errstr); printf("%d slots, %d rules\n", rule_slots, rule_count); for (i = 0; i < rule_slots; i++) { - error = bsde_get_rule(i, &rule, BUFSIZ, errstr); + error = bsde_get_rule(i, &rule, UGIDFW_BUFSIZ, errstr); switch (error) { case -2: continue; @@ -124,7 +126,7 @@ list_rules(void) break; } - if (bsde_rule_to_string(&rule, charstr, BUFSIZ) == -1) + if (bsde_rule_to_string(&rule, charstr, UGIDFW_BUFSIZ) == -1) warnx("unable to translate rule %d to string", i); else printf("%d %s\n", i, charstr); @@ -134,7 +136,7 @@ list_rules(void) void set_rule(int argc, char *argv[]) { - char errstr[BUFSIZ]; + char errstr[UGIDFW_BUFSIZ]; struct mac_bsdextended_rule rule; long value; int error, rulenum; @@ -152,13 +154,13 @@ set_rule(int argc, char *argv[]) rulenum = value; - error = bsde_parse_rule(argc - 1, argv + 1, &rule, BUFSIZ, errstr); + error = bsde_parse_rule(argc - 1, argv + 1, &rule, UGIDFW_BUFSIZ, errstr); if (error) { warnx("%s", errstr); return; } - error = bsde_set_rule(rulenum, &rule, BUFSIZ, errstr); + error = bsde_set_rule(rulenum, &rule, UGIDFW_BUFSIZ, errstr); if (error) { warnx("%s", errstr); return; @@ -168,7 +170,7 @@ set_rule(int argc, char *argv[]) void remove_rule(int argc, char *argv[]) { - char errstr[BUFSIZ]; + char errstr[UGIDFW_BUFSIZ]; long value; int error, rulenum; char *endp; @@ -185,7 +187,7 @@ remove_rule(int argc, char *argv[]) rulenum = value; - error = bsde_delete_rule(rulenum, BUFSIZ, errstr); + error = bsde_delete_rule(rulenum, UGIDFW_BUFSIZ, errstr); if (error) warnx("%s", errstr); }