Home | History | Annotate | Line # | Download | only in kern
kern_pax.c revision 1.26.12.1
      1 /*	$NetBSD: kern_pax.c,v 1.26.12.1 2014/05/18 17:46:07 rmind Exp $	*/
      2 
      3 /*-
      4  * Copyright (c) 2006 Elad Efrat <elad (at) NetBSD.org>
      5  * All rights reserved.
      6  *
      7  * Redistribution and use in source and binary forms, with or without
      8  * modification, are permitted provided that the following conditions
      9  * are met:
     10  * 1. Redistributions of source code must retain the above copyright
     11  *    notice, this list of conditions and the following disclaimer.
     12  * 2. Redistributions in binary form must reproduce the above copyright
     13  *    notice, this list of conditions and the following disclaimer in the
     14  *    documentation and/or other materials provided with the distribution.
     15  * 3. The name of the author may not be used to endorse or promote products
     16  *    derived from this software without specific prior written permission.
     17  *
     18  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
     19  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
     20  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
     21  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
     22  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
     23  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     24  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     25  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     26  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
     27  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     28  */
     29 
     30 #include <sys/cdefs.h>
     31 __KERNEL_RCSID(0, "$NetBSD: kern_pax.c,v 1.26.12.1 2014/05/18 17:46:07 rmind Exp $");
     32 
     33 #include "opt_pax.h"
     34 
     35 #include <sys/param.h>
     36 #include <sys/proc.h>
     37 #include <sys/exec_elf.h>
     38 #include <sys/pax.h>
     39 #include <sys/sysctl.h>
     40 #include <sys/kmem.h>
     41 #include <sys/fileassoc.h>
     42 #include <sys/syslog.h>
     43 #include <sys/vnode.h>
     44 #include <sys/queue.h>
     45 #include <sys/kauth.h>
     46 #include <sys/cprng.h>
     47 
     48 #ifdef PAX_ASLR
     49 #include <sys/mman.h>
     50 #include <sys/rnd.h>
     51 #include <sys/exec.h>
     52 
     53 int pax_aslr_enabled = 1;
     54 int pax_aslr_global = PAX_ASLR;
     55 
     56 #ifndef PAX_ASLR_DELTA_MMAP_LSB
     57 #define PAX_ASLR_DELTA_MMAP_LSB		PGSHIFT
     58 #endif
     59 #ifndef PAX_ASLR_DELTA_MMAP_LEN
     60 #define PAX_ASLR_DELTA_MMAP_LEN		((sizeof(void *) * NBBY) / 2)
     61 #endif
     62 #ifndef PAX_ASLR_DELTA_STACK_LSB
     63 #define PAX_ASLR_DELTA_STACK_LSB	PGSHIFT
     64 #endif
     65 #ifndef PAX_ASLR_DELTA_STACK_LEN
     66 #define PAX_ASLR_DELTA_STACK_LEN 	12
     67 #endif
     68 
     69 #endif /* PAX_ASLR */
     70 
     71 #ifdef PAX_MPROTECT
     72 static int pax_mprotect_enabled = 1;
     73 static int pax_mprotect_global = PAX_MPROTECT;
     74 #endif /* PAX_MPROTECT */
     75 
     76 #ifdef PAX_SEGVGUARD
     77 #ifndef PAX_SEGVGUARD_EXPIRY
     78 #define	PAX_SEGVGUARD_EXPIRY		(2 * 60)
     79 #endif
     80 
     81 #ifndef PAX_SEGVGUARD_SUSPENSION
     82 #define	PAX_SEGVGUARD_SUSPENSION	(10 * 60)
     83 #endif
     84 
     85 #ifndef	PAX_SEGVGUARD_MAXCRASHES
     86 #define	PAX_SEGVGUARD_MAXCRASHES	5
     87 #endif
     88 
     89 static int pax_segvguard_enabled = 1;
     90 static int pax_segvguard_global = PAX_SEGVGUARD;
     91 static int pax_segvguard_expiry = PAX_SEGVGUARD_EXPIRY;
     92 static int pax_segvguard_suspension = PAX_SEGVGUARD_SUSPENSION;
     93 static int pax_segvguard_maxcrashes = PAX_SEGVGUARD_MAXCRASHES;
     94 
     95 static fileassoc_t segvguard_id;
     96 
     97 struct pax_segvguard_uid_entry {
     98 	uid_t sue_uid;
     99 	size_t sue_ncrashes;
    100 	time_t sue_expiry;
    101 	time_t sue_suspended;
    102 	LIST_ENTRY(pax_segvguard_uid_entry) sue_list;
    103 };
    104 
    105 struct pax_segvguard_entry {
    106 	LIST_HEAD(, pax_segvguard_uid_entry) segv_uids;
    107 };
    108 
    109 static void pax_segvguard_cb(void *);
    110 #endif /* PAX_SEGVGUARD */
    111 
    112 /* PaX internal setspecific flags */
    113 #define	PAX_MPROTECT_EXPLICIT_ENABLE	(void *)0x01
    114 #define	PAX_MPROTECT_EXPLICIT_DISABLE	(void *)0x02
    115 #define	PAX_SEGVGUARD_EXPLICIT_ENABLE	(void *)0x03
    116 #define	PAX_SEGVGUARD_EXPLICIT_DISABLE	(void *)0x04
    117 #define	PAX_ASLR_EXPLICIT_ENABLE	(void *)0x05
    118 #define	PAX_ASLR_EXPLICIT_DISABLE	(void *)0x06
    119 
    120 SYSCTL_SETUP(sysctl_security_pax_setup, "sysctl security.pax setup")
    121 {
    122 	const struct sysctlnode *rnode = NULL, *cnode;
    123 
    124 	sysctl_createv(clog, 0, NULL, &rnode,
    125 		       CTLFLAG_PERMANENT,
    126 		       CTLTYPE_NODE, "pax",
    127 		       SYSCTL_DESCR("PaX (exploit mitigation) features."),
    128 		       NULL, 0, NULL, 0,
    129 		       CTL_SECURITY, CTL_CREATE, CTL_EOL);
    130 
    131 	cnode = rnode;
    132 
    133 #ifdef PAX_MPROTECT
    134 	rnode = cnode;
    135 	sysctl_createv(clog, 0, &rnode, &rnode,
    136 		       CTLFLAG_PERMANENT,
    137 		       CTLTYPE_NODE, "mprotect",
    138 		       SYSCTL_DESCR("mprotect(2) W^X restrictions."),
    139 		       NULL, 0, NULL, 0,
    140 		       CTL_CREATE, CTL_EOL);
    141 	sysctl_createv(clog, 0, &rnode, NULL,
    142 		       CTLFLAG_PERMANENT|CTLFLAG_READWRITE,
    143 		       CTLTYPE_INT, "enabled",
    144 		       SYSCTL_DESCR("Restrictions enabled."),
    145 		       NULL, 0, &pax_mprotect_enabled, 0,
    146 		       CTL_CREATE, CTL_EOL);
    147 	sysctl_createv(clog, 0, &rnode, NULL,
    148 		       CTLFLAG_PERMANENT|CTLFLAG_READWRITE,
    149 		       CTLTYPE_INT, "global",
    150 		       SYSCTL_DESCR("When enabled, unless explicitly "
    151 				    "specified, apply restrictions to "
    152 				    "all processes."),
    153 		       NULL, 0, &pax_mprotect_global, 0,
    154 		       CTL_CREATE, CTL_EOL);
    155 #endif /* PAX_MPROTECT */
    156 
    157 #ifdef PAX_SEGVGUARD
    158 	rnode = cnode;
    159 	sysctl_createv(clog, 0, &rnode, &rnode,
    160 		       CTLFLAG_PERMANENT,
    161 		       CTLTYPE_NODE, "segvguard",
    162 		       SYSCTL_DESCR("PaX segvguard."),
    163 		       NULL, 0, NULL, 0,
    164 		       CTL_CREATE, CTL_EOL);
    165 	sysctl_createv(clog, 0, &rnode, NULL,
    166 		       CTLFLAG_PERMANENT|CTLFLAG_READWRITE,
    167 		       CTLTYPE_INT, "enabled",
    168 		       SYSCTL_DESCR("segvguard enabled."),
    169 		       NULL, 0, &pax_segvguard_enabled, 0,
    170 		       CTL_CREATE, CTL_EOL);
    171 	sysctl_createv(clog, 0, &rnode, NULL,
    172 		       CTLFLAG_PERMANENT|CTLFLAG_READWRITE,
    173 		       CTLTYPE_INT, "global",
    174 		       SYSCTL_DESCR("segvguard all programs."),
    175 		       NULL, 0, &pax_segvguard_global, 0,
    176 		       CTL_CREATE, CTL_EOL);
    177 	sysctl_createv(clog, 0, &rnode, NULL,
    178 		       CTLFLAG_PERMANENT|CTLFLAG_READWRITE,
    179 		       CTLTYPE_INT, "expiry_timeout",
    180 		       SYSCTL_DESCR("Entry expiry timeout (in seconds)."),
    181 		       NULL, 0, &pax_segvguard_expiry, 0,
    182 		       CTL_CREATE, CTL_EOL);
    183 	sysctl_createv(clog, 0, &rnode, NULL,
    184 		       CTLFLAG_PERMANENT|CTLFLAG_READWRITE,
    185 		       CTLTYPE_INT, "suspend_timeout",
    186 		       SYSCTL_DESCR("Entry suspension timeout (in seconds)."),
    187 		       NULL, 0, &pax_segvguard_suspension, 0,
    188 		       CTL_CREATE, CTL_EOL);
    189 	sysctl_createv(clog, 0, &rnode, NULL,
    190 		       CTLFLAG_PERMANENT|CTLFLAG_READWRITE,
    191 		       CTLTYPE_INT, "max_crashes",
    192 		       SYSCTL_DESCR("Max number of crashes before expiry."),
    193 		       NULL, 0, &pax_segvguard_maxcrashes, 0,
    194 		       CTL_CREATE, CTL_EOL);
    195 #endif /* PAX_SEGVGUARD */
    196 
    197 #ifdef PAX_ASLR
    198 	rnode = cnode;
    199 	sysctl_createv(clog, 0, &rnode, &rnode,
    200 		       CTLFLAG_PERMANENT,
    201 		       CTLTYPE_NODE, "aslr",
    202 		       SYSCTL_DESCR("Address Space Layout Randomization."),
    203 		       NULL, 0, NULL, 0,
    204 		       CTL_CREATE, CTL_EOL);
    205 	sysctl_createv(clog, 0, &rnode, NULL,
    206 		       CTLFLAG_PERMANENT|CTLFLAG_READWRITE,
    207 		       CTLTYPE_INT, "enabled",
    208 		       SYSCTL_DESCR("Restrictions enabled."),
    209 		       NULL, 0, &pax_aslr_enabled, 0,
    210 		       CTL_CREATE, CTL_EOL);
    211 	sysctl_createv(clog, 0, &rnode, NULL,
    212 		       CTLFLAG_PERMANENT|CTLFLAG_READWRITE,
    213 		       CTLTYPE_INT, "global",
    214 		       SYSCTL_DESCR("When enabled, unless explicitly "
    215 				    "specified, apply to all processes."),
    216 		       NULL, 0, &pax_aslr_global, 0,
    217 		       CTL_CREATE, CTL_EOL);
    218 	sysctl_createv(clog, 0, &rnode, NULL,
    219 		       CTLFLAG_PERMANENT|CTLFLAG_IMMEDIATE,
    220 		       CTLTYPE_INT, "mmap_len",
    221 		       SYSCTL_DESCR("Number of bits randomized for "
    222 				    "mmap(2) calls."),
    223 		       NULL, PAX_ASLR_DELTA_MMAP_LEN, NULL, 0,
    224 		       CTL_CREATE, CTL_EOL);
    225 	sysctl_createv(clog, 0, &rnode, NULL,
    226 		       CTLFLAG_PERMANENT|CTLFLAG_IMMEDIATE,
    227 		       CTLTYPE_INT, "stack_len",
    228 		       SYSCTL_DESCR("Number of bits randomized for "
    229 				    "the stack."),
    230 		       NULL, PAX_ASLR_DELTA_STACK_LEN, NULL, 0,
    231 		       CTL_CREATE, CTL_EOL);
    232 	sysctl_createv(clog, 0, &rnode, NULL,
    233 		       CTLFLAG_PERMANENT|CTLFLAG_IMMEDIATE,
    234 		       CTLTYPE_INT, "exec_len",
    235 		       SYSCTL_DESCR("Number of bits randomized for "
    236 				    "the PIE exec base."),
    237 		       NULL, PAX_ASLR_DELTA_EXEC_LEN, NULL, 0,
    238 		       CTL_CREATE, CTL_EOL);
    239 
    240 #endif /* PAX_ASLR */
    241 }
    242 
    243 /*
    244  * Initialize PaX.
    245  */
    246 void
    247 pax_init(void)
    248 {
    249 #ifdef PAX_SEGVGUARD
    250 	int error;
    251 #endif /* PAX_SEGVGUARD */
    252 
    253 #ifdef PAX_SEGVGUARD
    254 	error = fileassoc_register("segvguard", pax_segvguard_cb,
    255 	    &segvguard_id);
    256 	if (error) {
    257 		panic("pax_init: segvguard_id: error=%d\n", error);
    258 	}
    259 #endif /* PAX_SEGVGUARD */
    260 }
    261 
    262 #ifdef PAX_MPROTECT
    263 void
    264 pax_mprotect(struct lwp *l, vm_prot_t *prot, vm_prot_t *maxprot)
    265 {
    266 	uint32_t f;
    267 
    268 	if (!pax_mprotect_enabled)
    269 		return;
    270 
    271 	f = l->l_proc->p_pax;
    272 	if ((pax_mprotect_global && (f & ELF_NOTE_PAX_NOMPROTECT) != 0) ||
    273 	    (!pax_mprotect_global && (f & ELF_NOTE_PAX_MPROTECT) == 0))
    274 		return;
    275 
    276 	if ((*prot & (VM_PROT_WRITE|VM_PROT_EXECUTE)) != VM_PROT_EXECUTE) {
    277 		*prot &= ~VM_PROT_EXECUTE;
    278 		*maxprot &= ~VM_PROT_EXECUTE;
    279 	} else {
    280 		*prot &= ~VM_PROT_WRITE;
    281 		*maxprot &= ~VM_PROT_WRITE;
    282 	}
    283 }
    284 #endif /* PAX_MPROTECT */
    285 
    286 #ifdef PAX_ASLR
    287 bool
    288 pax_aslr_active(struct lwp *l)
    289 {
    290 	uint32_t f;
    291 
    292 	if (!pax_aslr_enabled)
    293 		return false;
    294 
    295 	f = l->l_proc->p_pax;
    296 	if ((pax_aslr_global && (f & ELF_NOTE_PAX_NOASLR) != 0) ||
    297 	    (!pax_aslr_global && (f & ELF_NOTE_PAX_ASLR) == 0))
    298 		return false;
    299 	return true;
    300 }
    301 
    302 void
    303 pax_aslr_init(struct lwp *l, struct vmspace *vm)
    304 {
    305 	if (!pax_aslr_active(l))
    306 		return;
    307 
    308 	vm->vm_aslr_delta_mmap = PAX_ASLR_DELTA(cprng_fast32(),
    309 	    PAX_ASLR_DELTA_MMAP_LSB, PAX_ASLR_DELTA_MMAP_LEN);
    310 }
    311 
    312 void
    313 pax_aslr(struct lwp *l, vaddr_t *addr, vaddr_t orig_addr, int f)
    314 {
    315 	if (!pax_aslr_active(l))
    316 		return;
    317 
    318 	if (!(f & MAP_FIXED) && ((orig_addr == 0) || !(f & MAP_ANON))) {
    319 #ifdef PAX_ASLR_DEBUG
    320 		uprintf("applying to 0x%lx orig_addr=0x%lx f=%x\n",
    321 		    (unsigned long)*addr, (unsigned long)orig_addr, f);
    322 #endif
    323 		if (!(l->l_proc->p_vmspace->vm_map.flags & VM_MAP_TOPDOWN))
    324 			*addr += l->l_proc->p_vmspace->vm_aslr_delta_mmap;
    325 		else
    326 			*addr -= l->l_proc->p_vmspace->vm_aslr_delta_mmap;
    327 #ifdef PAX_ASLR_DEBUG
    328 		uprintf("result 0x%lx\n", *addr);
    329 #endif
    330 	}
    331 #ifdef PAX_ASLR_DEBUG
    332 	else
    333 	    uprintf("not applying to 0x%lx orig_addr=0x%lx f=%x\n",
    334 		(unsigned long)*addr, (unsigned long)orig_addr, f);
    335 #endif
    336 }
    337 
    338 void
    339 pax_aslr_stack(struct lwp *l, struct exec_package *epp, u_long *max_stack_size)
    340 {
    341 	if (pax_aslr_active(l)) {
    342 		u_long d =  PAX_ASLR_DELTA(cprng_fast32(),
    343 		    PAX_ASLR_DELTA_STACK_LSB,
    344 		    PAX_ASLR_DELTA_STACK_LEN);
    345 #ifdef PAX_ASLR_DEBUG
    346 		uprintf("stack 0x%lx d=0x%lx 0x%lx\n",
    347 		    epp->ep_minsaddr, d, epp->ep_minsaddr - d);
    348 #endif
    349 		epp->ep_minsaddr -= d;
    350 		*max_stack_size -= d;
    351 		if (epp->ep_ssize > *max_stack_size)
    352 			epp->ep_ssize = *max_stack_size;
    353 	}
    354 }
    355 #endif /* PAX_ASLR */
    356 
    357 #ifdef PAX_SEGVGUARD
    358 static void
    359 pax_segvguard_cb(void *v)
    360 {
    361 	struct pax_segvguard_entry *p = v;
    362 	struct pax_segvguard_uid_entry *up;
    363 
    364 	if (p == NULL) {
    365 		return;
    366 	}
    367 	while ((up = LIST_FIRST(&p->segv_uids)) != NULL) {
    368 		LIST_REMOVE(up, sue_list);
    369 		kmem_free(up, sizeof(*up));
    370 	}
    371 	kmem_free(p, sizeof(*p));
    372 }
    373 
    374 /*
    375  * Called when a process of image vp generated a segfault.
    376  */
    377 int
    378 pax_segvguard(struct lwp *l, struct vnode *vp, const char *name,
    379     bool crashed)
    380 {
    381 	struct pax_segvguard_entry *p;
    382 	struct pax_segvguard_uid_entry *up;
    383 	struct timeval tv;
    384 	uid_t uid;
    385 	uint32_t f;
    386 	bool have_uid;
    387 
    388 	if (!pax_segvguard_enabled)
    389 		return (0);
    390 
    391 	f = l->l_proc->p_pax;
    392 	if ((pax_segvguard_global && (f & ELF_NOTE_PAX_NOGUARD) != 0) ||
    393 	    (!pax_segvguard_global && (f & ELF_NOTE_PAX_GUARD) == 0))
    394 		return (0);
    395 
    396 	if (vp == NULL)
    397 		return (EFAULT);
    398 
    399 	/* Check if we already monitor the file. */
    400 	p = fileassoc_lookup(vp, segvguard_id);
    401 
    402 	/* Fast-path if starting a program we don't know. */
    403 	if (p == NULL && !crashed)
    404 		return (0);
    405 
    406 	microtime(&tv);
    407 
    408 	/*
    409 	 * If a program we don't know crashed, we need to create a new entry
    410 	 * for it.
    411 	 */
    412 	if (p == NULL) {
    413 		p = kmem_alloc(sizeof(*p), KM_SLEEP);
    414 		fileassoc_add(vp, segvguard_id, p);
    415 		LIST_INIT(&p->segv_uids);
    416 
    417 		/*
    418 		 * Initialize a new entry with "crashes so far" of 1.
    419 		 * The expiry time is when we purge the entry if it didn't
    420 		 * reach the limit.
    421 		 */
    422 		up = kmem_alloc(sizeof(*up), KM_SLEEP);
    423 		up->sue_uid = kauth_cred_getuid(l->l_cred);
    424 		up->sue_ncrashes = 1;
    425 		up->sue_expiry = tv.tv_sec + pax_segvguard_expiry;
    426 		up->sue_suspended = 0;
    427 
    428 		LIST_INSERT_HEAD(&p->segv_uids, up, sue_list);
    429 
    430 		return (0);
    431 	}
    432 
    433 	/*
    434 	 * A program we "know" either executed or crashed again.
    435 	 * See if it's a culprit we're familiar with.
    436 	 */
    437 	uid = kauth_cred_getuid(l->l_cred);
    438 	have_uid = false;
    439 	LIST_FOREACH(up, &p->segv_uids, sue_list) {
    440 		if (up->sue_uid == uid) {
    441 			have_uid = true;
    442 			break;
    443 		}
    444 	}
    445 
    446 	/*
    447 	 * It's someone else. Add an entry for him if we crashed.
    448 	 */
    449 	if (!have_uid) {
    450 		if (crashed) {
    451 			up = kmem_alloc(sizeof(*up), KM_SLEEP);
    452 			up->sue_uid = uid;
    453 			up->sue_ncrashes = 1;
    454 			up->sue_expiry = tv.tv_sec + pax_segvguard_expiry;
    455 			up->sue_suspended = 0;
    456 
    457 			LIST_INSERT_HEAD(&p->segv_uids, up, sue_list);
    458 		}
    459 		return (0);
    460 	}
    461 
    462 	if (crashed) {
    463 		/* Check if timer on previous crashes expired first. */
    464 		if (up->sue_expiry < tv.tv_sec) {
    465 			log(LOG_INFO, "PaX Segvguard: [%s] Suspension"
    466 			    " expired.\n", name ? name : "unknown");
    467 
    468 			up->sue_ncrashes = 1;
    469 			up->sue_expiry = tv.tv_sec + pax_segvguard_expiry;
    470 			up->sue_suspended = 0;
    471 
    472 			return (0);
    473 		}
    474 
    475 		up->sue_ncrashes++;
    476 
    477 		if (up->sue_ncrashes >= pax_segvguard_maxcrashes) {
    478 			log(LOG_ALERT, "PaX Segvguard: [%s] Suspending "
    479 			    "execution for %d seconds after %zu crashes.\n",
    480 			    name ? name : "unknown", pax_segvguard_suspension,
    481 			    up->sue_ncrashes);
    482 
    483 			/* Suspend this program for a while. */
    484 			up->sue_suspended = tv.tv_sec + pax_segvguard_suspension;
    485 			up->sue_ncrashes = 0;
    486 			up->sue_expiry = 0;
    487 		}
    488 	} else {
    489 		/* Are we supposed to be suspended? */
    490 		if (up->sue_suspended > tv.tv_sec) {
    491 			log(LOG_ALERT, "PaX Segvguard: [%s] Preventing "
    492 			    "execution due to repeated segfaults.\n", name ?
    493 			    name : "unknown");
    494 
    495 			return (EPERM);
    496 		}
    497 	}
    498 
    499 	return (0);
    500 }
    501 #endif /* PAX_SEGVGUARD */
    502