1/*-
2 * Copyright (c) 1999-2002, 2008-2009 Robert N. M. Watson
3 * Copyright (c) 2001 Ilmar S. Habibulin
4 * Copyright (c) 2001-2003 Networks Associates Technology, Inc.
5 * Copyright (c) 2006 SPARTA, Inc.
6 * Copyright (c) 2008 Apple Inc.
7 * All rights reserved.
8 *
9 * This software was developed by Robert Watson and Ilmar Habibulin for the
10 * TrustedBSD Project.
11 *
12 * This software was developed for the FreeBSD Project in part by Network
13 * Associates Laboratories, the Security Research Division of Network
14 * Associates, Inc. under DARPA/SPAWAR contract N66001-01-C-8035 ("CBOSS"),
15 * as part of the DARPA CHATS research program.
16 *
17 * This software was enhanced by SPARTA ISSO under SPAWAR contract
18 * N66001-04-C-6019 ("SEFOS").
19 *
20 * This software was developed at the University of Cambridge Computer
21 * Laboratory with support from a grant from Google, Inc.
22 *
23 * Redistribution and use in source and binary forms, with or without
24 * modification, are permitted provided that the following conditions
25 * are met:
26 * 1. Redistributions of source code must retain the above copyright
27 *    notice, this list of conditions and the following disclaimer.
28 * 2. Redistributions in binary form must reproduce the above copyright
29 *    notice, this list of conditions and the following disclaimer in the
30 *    documentation and/or other materials provided with the distribution.
31 *
32 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
33 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
34 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
35 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
36 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
37 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
38 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
39 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
40 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
41 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
42 * SUCH DAMAGE.
43 */
44
45#include <sys/cdefs.h>
46__FBSDID("$FreeBSD$");
47
48#include "opt_mac.h"
49
50#include <sys/param.h>
51#include <sys/condvar.h>
52#include <sys/imgact.h>
53#include <sys/kernel.h>
54#include <sys/lock.h>
55#include <sys/malloc.h>
56#include <sys/mac.h>
57#include <sys/proc.h>
58#include <sys/rwlock.h>
59#include <sys/sbuf.h>
60#include <sys/sdt.h>
61#include <sys/systm.h>
62#include <sys/vnode.h>
63#include <sys/mount.h>
64#include <sys/file.h>
65#include <sys/namei.h>
66#include <sys/sysctl.h>
67
68#include <vm/vm.h>
69#include <vm/pmap.h>
70#include <vm/vm_map.h>
71#include <vm/vm_object.h>
72
73#include <security/mac/mac_framework.h>
74#include <security/mac/mac_internal.h>
75#include <security/mac/mac_policy.h>
76
77static int	mac_mmap_revocation = 1;
78SYSCTL_INT(_security_mac, OID_AUTO, mmap_revocation, CTLFLAG_RW,
79    &mac_mmap_revocation, 0, "Revoke mmap access to files on subject "
80    "relabel");
81
82static int	mac_mmap_revocation_via_cow = 0;
83SYSCTL_INT(_security_mac, OID_AUTO, mmap_revocation_via_cow, CTLFLAG_RW,
84    &mac_mmap_revocation_via_cow, 0, "Revoke mmap access to files via "
85    "copy-on-write semantics, or by removing all write access");
86
87static void	mac_proc_vm_revoke_recurse(struct thread *td,
88		    struct ucred *cred, struct vm_map *map);
89
90static struct label *
91mac_proc_label_alloc(void)
92{
93	struct label *label;
94
95	label = mac_labelzone_alloc(M_WAITOK);
96	MAC_POLICY_PERFORM(proc_init_label, label);
97	return (label);
98}
99
100void
101mac_proc_init(struct proc *p)
102{
103
104	if (mac_labeled & MPC_OBJECT_PROC)
105		p->p_label = mac_proc_label_alloc();
106	else
107		p->p_label = NULL;
108}
109
110static void
111mac_proc_label_free(struct label *label)
112{
113
114	MAC_POLICY_PERFORM_NOSLEEP(proc_destroy_label, label);
115	mac_labelzone_free(label);
116}
117
118void
119mac_proc_destroy(struct proc *p)
120{
121
122	if (p->p_label != NULL) {
123		mac_proc_label_free(p->p_label);
124		p->p_label = NULL;
125	}
126}
127
128void
129mac_thread_userret(struct thread *td)
130{
131
132	MAC_POLICY_PERFORM(thread_userret, td);
133}
134
135int
136mac_execve_enter(struct image_params *imgp, struct mac *mac_p)
137{
138	struct label *label;
139	struct mac mac;
140	char *buffer;
141	int error;
142
143	if (mac_p == NULL)
144		return (0);
145
146	if (!(mac_labeled & MPC_OBJECT_CRED))
147		return (EINVAL);
148
149	error = copyin(mac_p, &mac, sizeof(mac));
150	if (error)
151		return (error);
152
153	error = mac_check_structmac_consistent(&mac);
154	if (error)
155		return (error);
156
157	buffer = malloc(mac.m_buflen, M_MACTEMP, M_WAITOK);
158	error = copyinstr(mac.m_string, buffer, mac.m_buflen, NULL);
159	if (error) {
160		free(buffer, M_MACTEMP);
161		return (error);
162	}
163
164	label = mac_cred_label_alloc();
165	error = mac_cred_internalize_label(label, buffer);
166	free(buffer, M_MACTEMP);
167	if (error) {
168		mac_cred_label_free(label);
169		return (error);
170	}
171	imgp->execlabel = label;
172	return (0);
173}
174
175void
176mac_execve_exit(struct image_params *imgp)
177{
178	if (imgp->execlabel != NULL) {
179		mac_cred_label_free(imgp->execlabel);
180		imgp->execlabel = NULL;
181	}
182}
183
184void
185mac_execve_interpreter_enter(struct vnode *interpvp,
186    struct label **interpvplabel)
187{
188
189	if (mac_labeled & MPC_OBJECT_VNODE) {
190		*interpvplabel = mac_vnode_label_alloc();
191		mac_vnode_copy_label(interpvp->v_label, *interpvplabel);
192	} else
193		*interpvplabel = NULL;
194}
195
196void
197mac_execve_interpreter_exit(struct label *interpvplabel)
198{
199
200	if (interpvplabel != NULL)
201		mac_vnode_label_free(interpvplabel);
202}
203
204/*
205 * When relabeling a process, call out to the policies for the maximum
206 * permission allowed for each object type we know about in its memory space,
207 * and revoke access (in the least surprising ways we know) when necessary.
208 * The process lock is not held here.
209 */
210void
211mac_proc_vm_revoke(struct thread *td)
212{
213	struct ucred *cred;
214
215	PROC_LOCK(td->td_proc);
216	cred = crhold(td->td_proc->p_ucred);
217	PROC_UNLOCK(td->td_proc);
218
219	/* XXX freeze all other threads */
220	mac_proc_vm_revoke_recurse(td, cred,
221	    &td->td_proc->p_vmspace->vm_map);
222	/* XXX allow other threads to continue */
223
224	crfree(cred);
225}
226
227static __inline const char *
228prot2str(vm_prot_t prot)
229{
230
231	switch (prot & VM_PROT_ALL) {
232	case VM_PROT_READ:
233		return ("r--");
234	case VM_PROT_READ | VM_PROT_WRITE:
235		return ("rw-");
236	case VM_PROT_READ | VM_PROT_EXECUTE:
237		return ("r-x");
238	case VM_PROT_READ | VM_PROT_WRITE | VM_PROT_EXECUTE:
239		return ("rwx");
240	case VM_PROT_WRITE:
241		return ("-w-");
242	case VM_PROT_EXECUTE:
243		return ("--x");
244	case VM_PROT_WRITE | VM_PROT_EXECUTE:
245		return ("-wx");
246	default:
247		return ("---");
248	}
249}
250
251static void
252mac_proc_vm_revoke_recurse(struct thread *td, struct ucred *cred,
253    struct vm_map *map)
254{
255	vm_map_entry_t vme;
256	int result;
257	vm_prot_t revokeperms;
258	vm_object_t backing_object, object;
259	vm_ooffset_t offset;
260	struct vnode *vp;
261	struct mount *mp;
262
263	if (!mac_mmap_revocation)
264		return;
265
266	vm_map_lock(map);
267	for (vme = map->header.next; vme != &map->header; vme = vme->next) {
268		if (vme->eflags & MAP_ENTRY_IS_SUB_MAP) {
269			mac_proc_vm_revoke_recurse(td, cred,
270			    vme->object.sub_map);
271			continue;
272		}
273		/*
274		 * Skip over entries that obviously are not shared.
275		 */
276		if (vme->eflags & (MAP_ENTRY_COW | MAP_ENTRY_NOSYNC) ||
277		    !vme->max_protection)
278			continue;
279		/*
280		 * Drill down to the deepest backing object.
281		 */
282		offset = vme->offset;
283		object = vme->object.vm_object;
284		if (object == NULL)
285			continue;
286		VM_OBJECT_RLOCK(object);
287		while ((backing_object = object->backing_object) != NULL) {
288			VM_OBJECT_RLOCK(backing_object);
289			offset += object->backing_object_offset;
290			VM_OBJECT_RUNLOCK(object);
291			object = backing_object;
292		}
293		VM_OBJECT_RUNLOCK(object);
294		/*
295		 * At the moment, vm_maps and objects aren't considered by
296		 * the MAC system, so only things with backing by a normal
297		 * object (read: vnodes) are checked.
298		 */
299		if (object->type != OBJT_VNODE)
300			continue;
301		vp = (struct vnode *)object->handle;
302		vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);
303		result = vme->max_protection;
304		mac_vnode_check_mmap_downgrade(cred, vp, &result);
305		VOP_UNLOCK(vp, 0);
306		/*
307		 * Find out what maximum protection we may be allowing now
308		 * but a policy needs to get removed.
309		 */
310		revokeperms = vme->max_protection & ~result;
311		if (!revokeperms)
312			continue;
313		printf("pid %ld: revoking %s perms from %#lx:%ld "
314		    "(max %s/cur %s)\n", (long)td->td_proc->p_pid,
315		    prot2str(revokeperms), (u_long)vme->start,
316		    (long)(vme->end - vme->start),
317		    prot2str(vme->max_protection), prot2str(vme->protection));
318		/*
319		 * This is the really simple case: if a map has more
320		 * max_protection than is allowed, but it's not being
321		 * actually used (that is, the current protection is still
322		 * allowed), we can just wipe it out and do nothing more.
323		 */
324		if ((vme->protection & revokeperms) == 0) {
325			vme->max_protection -= revokeperms;
326		} else {
327			if (revokeperms & VM_PROT_WRITE) {
328				/*
329				 * In the more complicated case, flush out all
330				 * pending changes to the object then turn it
331				 * copy-on-write.
332				 */
333				vm_object_reference(object);
334				(void) vn_start_write(vp, &mp, V_WAIT);
335				vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);
336				VM_OBJECT_WLOCK(object);
337				vm_object_page_clean(object, offset, offset +
338				    vme->end - vme->start, OBJPC_SYNC);
339				VM_OBJECT_WUNLOCK(object);
340				VOP_UNLOCK(vp, 0);
341				vn_finished_write(mp);
342				vm_object_deallocate(object);
343				/*
344				 * Why bother if there's no read permissions
345				 * anymore?  For the rest, we need to leave
346				 * the write permissions on for COW, or
347				 * remove them entirely if configured to.
348				 */
349				if (!mac_mmap_revocation_via_cow) {
350					vme->max_protection &= ~VM_PROT_WRITE;
351					vme->protection &= ~VM_PROT_WRITE;
352				} if ((revokeperms & VM_PROT_READ) == 0)
353					vme->eflags |= MAP_ENTRY_COW |
354					    MAP_ENTRY_NEEDS_COPY;
355			}
356			if (revokeperms & VM_PROT_EXECUTE) {
357				vme->max_protection &= ~VM_PROT_EXECUTE;
358				vme->protection &= ~VM_PROT_EXECUTE;
359			}
360			if (revokeperms & VM_PROT_READ) {
361				vme->max_protection = 0;
362				vme->protection = 0;
363			}
364			pmap_protect(map->pmap, vme->start, vme->end,
365			    vme->protection & ~revokeperms);
366			vm_map_simplify_entry(map, vme);
367		}
368	}
369	vm_map_unlock(map);
370}
371
372MAC_CHECK_PROBE_DEFINE2(proc_check_debug, "struct ucred *", "struct proc *");
373
374int
375mac_proc_check_debug(struct ucred *cred, struct proc *p)
376{
377	int error;
378
379	PROC_LOCK_ASSERT(p, MA_OWNED);
380
381	MAC_POLICY_CHECK_NOSLEEP(proc_check_debug, cred, p);
382	MAC_CHECK_PROBE2(proc_check_debug, error, cred, p);
383
384	return (error);
385}
386
387MAC_CHECK_PROBE_DEFINE2(proc_check_sched, "struct ucred *", "struct proc *");
388
389int
390mac_proc_check_sched(struct ucred *cred, struct proc *p)
391{
392	int error;
393
394	PROC_LOCK_ASSERT(p, MA_OWNED);
395
396	MAC_POLICY_CHECK_NOSLEEP(proc_check_sched, cred, p);
397	MAC_CHECK_PROBE2(proc_check_sched, error, cred, p);
398
399	return (error);
400}
401
402MAC_CHECK_PROBE_DEFINE3(proc_check_signal, "struct ucred *", "struct proc *",
403    "int");
404
405int
406mac_proc_check_signal(struct ucred *cred, struct proc *p, int signum)
407{
408	int error;
409
410	PROC_LOCK_ASSERT(p, MA_OWNED);
411
412	MAC_POLICY_CHECK_NOSLEEP(proc_check_signal, cred, p, signum);
413	MAC_CHECK_PROBE3(proc_check_signal, error, cred, p, signum);
414
415	return (error);
416}
417
418MAC_CHECK_PROBE_DEFINE2(proc_check_wait, "struct ucred *", "struct proc *");
419
420int
421mac_proc_check_wait(struct ucred *cred, struct proc *p)
422{
423	int error;
424
425	PROC_LOCK_ASSERT(p, MA_OWNED);
426
427	MAC_POLICY_CHECK_NOSLEEP(proc_check_wait, cred, p);
428	MAC_CHECK_PROBE2(proc_check_wait, error, cred, p);
429
430	return (error);
431}
432