1/*-
2 * Copyright (c) 1999-2002, 2006, 2009, 2019 Robert N. M. Watson
3 * Copyright (c) 2001 Ilmar S. Habibulin
4 * Copyright (c) 2001-2004 Networks Associates Technology, Inc.
5 * Copyright (c) 2006 nCircle Network Security, Inc.
6 * Copyright (c) 2006 SPARTA, Inc.
7 * Copyright (c) 2009 Apple, Inc.
8 * All rights reserved.
9 *
10 * This software was developed by Robert Watson and Ilmar Habibulin for the
11 * TrustedBSD Project.
12 *
13 * This software was developed for the FreeBSD Project in part by Network
14 * Associates Laboratories, the Security Research Division of Network
15 * Associates, Inc. under DARPA/SPAWAR contract N66001-01-C-8035 ("CBOSS"),
16 * as part of the DARPA CHATS research program.
17 *
18 * This software was developed by Robert N. M. Watson for the TrustedBSD
19 * Project under contract to nCircle Network Security, Inc.
20 *
21 * This software was enhanced by SPARTA ISSO under SPAWAR contract
22 * N66001-04-C-6019 ("SEFOS").
23 *
24 * This software was developed at the University of Cambridge Computer
25 * Laboratory with support from a grant from Google, Inc.
26 *
27 * Redistribution and use in source and binary forms, with or without
28 * modification, are permitted provided that the following conditions
29 * are met:
30 * 1. Redistributions of source code must retain the above copyright
31 *    notice, this list of conditions and the following disclaimer.
32 * 2. Redistributions in binary form must reproduce the above copyright
33 *    notice, this list of conditions and the following disclaimer in the
34 *    documentation and/or other materials provided with the distribution.
35 *
36 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
37 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
38 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
39 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
40 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
41 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
42 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
43 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
44 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
45 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
46 * SUCH DAMAGE.
47 *
48 * $FreeBSD$
49 */
50
51#ifndef _SECURITY_MAC_MAC_INTERNAL_H_
52#define	_SECURITY_MAC_MAC_INTERNAL_H_
53
54#ifndef _KERNEL
55#error "no user-serviceable parts inside"
56#endif
57
58#include <sys/lock.h>
59#include <sys/rmlock.h>
60
61/*
62 * MAC Framework sysctl namespace.
63 */
64#ifdef SYSCTL_DECL
65SYSCTL_DECL(_security_mac);
66#endif /* SYSCTL_DECL */
67
68/*
69 * MAC Framework SDT DTrace probe namespace, macros for declaring entry
70 * point probes, macros for invoking them.
71 */
72#ifdef SDT_PROVIDER_DECLARE
73SDT_PROVIDER_DECLARE(mac);		/* MAC Framework-level events. */
74SDT_PROVIDER_DECLARE(mac_framework);	/* Entry points to MAC. */
75
76#define	MAC_CHECK_PROBE_DEFINE4(name, arg0, arg1, arg2, arg3)		\
77	SDT_PROBE_DEFINE5(mac_framework, , name, mac__check__err,	\
78	    "int", arg0, arg1, arg2, arg3);				\
79	SDT_PROBE_DEFINE5(mac_framework, , name, mac__check__ok,	\
80	    "int", arg0, arg1, arg2, arg3);
81
82#define	MAC_CHECK_PROBE_DEFINE3(name, arg0, arg1, arg2)			\
83	SDT_PROBE_DEFINE4(mac_framework, , name, mac__check__err,	\
84	    "int", arg0, arg1, arg2);					\
85	SDT_PROBE_DEFINE4(mac_framework, , name, mac__check__ok,	\
86	    "int", arg0, arg1, arg2);
87
88#define	MAC_CHECK_PROBE_DEFINE2(name, arg0, arg1)			\
89	SDT_PROBE_DEFINE3(mac_framework, , name, mac__check__err,	\
90	    "int", arg0, arg1);						\
91	SDT_PROBE_DEFINE3(mac_framework, , name, mac__check__ok,	\
92	    "int", arg0, arg1);
93
94#define	MAC_CHECK_PROBE_DEFINE1(name, arg0)				\
95	SDT_PROBE_DEFINE2(mac_framework, , name, mac__check__err,	\
96	    "int", arg0);						\
97	SDT_PROBE_DEFINE2(mac_framework, , name, mac__check__ok,	\
98	    "int", arg0);
99
100#define	MAC_CHECK_PROBE4(name, error, arg0, arg1, arg2, arg3)	do {	\
101	if (SDT_PROBES_ENABLED()) {					\
102		if (error) {						\
103			SDT_PROBE5(mac_framework, , name, mac__check__err,\
104			    error, arg0, arg1, arg2, arg3);		\
105		} else {						\
106			SDT_PROBE5(mac_framework, , name, mac__check__ok,\
107			    0, arg0, arg1, arg2, arg3);			\
108		}							\
109	}								\
110} while (0)
111
112#define	MAC_CHECK_PROBE3(name, error, arg0, arg1, arg2)			\
113	MAC_CHECK_PROBE4(name, error, arg0, arg1, arg2, 0)
114#define	MAC_CHECK_PROBE2(name, error, arg0, arg1)			\
115	MAC_CHECK_PROBE3(name, error, arg0, arg1, 0)
116#define	MAC_CHECK_PROBE1(name, error, arg0)				\
117	MAC_CHECK_PROBE2(name, error, arg0, 0)
118#endif
119
120#define	MAC_GRANT_PROBE_DEFINE2(name, arg0, arg1)			\
121	SDT_PROBE_DEFINE3(mac_framework, , name, mac__grant__err,	\
122	    "int", arg0, arg1);						\
123	SDT_PROBE_DEFINE3(mac_framework, , name, mac__grant__ok,	\
124	    "int", arg0, arg1);
125
126#define	MAC_GRANT_PROBE2(name, error, arg0, arg1)	do {		\
127	if (SDT_PROBES_ENABLED()) {					\
128		if (error) {						\
129			SDT_PROBE3(mac_framework, , name, mac__grant__err,\
130			    error, arg0, arg1);				\
131		} else {						\
132			SDT_PROBE3(mac_framework, , name, mac__grant__ok,\
133			    error, arg0, arg1);				\
134		}							\
135	}								\
136} while (0)
137
138/*
139 * MAC Framework global types and typedefs.
140 */
141LIST_HEAD(mac_policy_list_head, mac_policy_conf);
142#ifdef MALLOC_DECLARE
143MALLOC_DECLARE(M_MACTEMP);
144#endif
145
146/*
147 * MAC labels -- in-kernel storage format.
148 *
149 * In general, struct label pointers are embedded in kernel data structures
150 * representing objects that may be labeled (and protected).  Struct label is
151 * opaque to both kernel services that invoke the MAC Framework and MAC
152 * policy modules.  In particular, we do not wish to encode the layout of the
153 * label structure into any ABIs.  Historically, the slot array contained
154 * unions of {long, void} but now contains uintptr_t.
155 */
156#define	MAC_MAX_SLOTS	4
157#define	MAC_FLAG_INITIALIZED	0x0000001	/* Is initialized for use. */
158struct label {
159	int		l_flags;
160	intptr_t	l_perpolicy[MAC_MAX_SLOTS];
161};
162
163/*
164 * Flags for mac_labeled, a bitmask of object types need across the union of
165 * all policies currently registered with the MAC Framework, used to key
166 * whether or not labels are allocated and constructors for the type are
167 * invoked.
168 */
169#define	MPC_OBJECT_CRED			0x0000000000000001
170#define	MPC_OBJECT_PROC			0x0000000000000002
171#define	MPC_OBJECT_VNODE		0x0000000000000004
172#define	MPC_OBJECT_INPCB		0x0000000000000008
173#define	MPC_OBJECT_SOCKET		0x0000000000000010
174#define	MPC_OBJECT_DEVFS		0x0000000000000020
175#define	MPC_OBJECT_MBUF			0x0000000000000040
176#define	MPC_OBJECT_IPQ			0x0000000000000080
177#define	MPC_OBJECT_IFNET		0x0000000000000100
178#define	MPC_OBJECT_BPFDESC		0x0000000000000200
179#define	MPC_OBJECT_PIPE			0x0000000000000400
180#define	MPC_OBJECT_MOUNT		0x0000000000000800
181#define	MPC_OBJECT_POSIXSEM		0x0000000000001000
182#define	MPC_OBJECT_POSIXSHM		0x0000000000002000
183#define	MPC_OBJECT_SYSVMSG		0x0000000000004000
184#define	MPC_OBJECT_SYSVMSQ		0x0000000000008000
185#define	MPC_OBJECT_SYSVSEM		0x0000000000010000
186#define	MPC_OBJECT_SYSVSHM		0x0000000000020000
187#define	MPC_OBJECT_SYNCACHE		0x0000000000040000
188#define	MPC_OBJECT_IP6Q			0x0000000000080000
189
190/*
191 * MAC Framework global variables.
192 */
193extern struct mac_policy_list_head	mac_policy_list;
194extern struct mac_policy_list_head	mac_static_policy_list;
195extern u_int				mac_policy_count;
196extern uint64_t				mac_labeled;
197extern struct mtx			mac_ifnet_mtx;
198
199/*
200 * MAC Framework infrastructure functions.
201 */
202int	mac_error_select(int error1, int error2);
203
204void	mac_policy_slock_nosleep(struct rm_priotracker *tracker);
205void	mac_policy_slock_sleep(void);
206void	mac_policy_sunlock_nosleep(struct rm_priotracker *tracker);
207void	mac_policy_sunlock_sleep(void);
208
209struct label	*mac_labelzone_alloc(int flags);
210void		 mac_labelzone_free(struct label *label);
211void		 mac_labelzone_init(void);
212
213void	mac_init_label(struct label *label);
214void	mac_destroy_label(struct label *label);
215int	mac_check_structmac_consistent(struct mac *mac);
216int	mac_allocate_slot(void);
217
218/*
219 * Lock ifnets to protect labels only if ifnet labels are in use.
220 */
221#define MAC_IFNET_LOCK(ifp, locked)	do {				\
222	if (mac_labeled & MPC_OBJECT_IFNET) {				\
223		mtx_lock(&mac_ifnet_mtx);				\
224		locked = 1;						\
225	} else {							\
226		locked = 0;						\
227	}								\
228} while (0)
229
230#define MAC_IFNET_UNLOCK(ifp, locked)	do {				\
231	if (locked) {							\
232		mtx_unlock(&mac_ifnet_mtx);				\
233		locked = 0;						\
234	}								\
235} while (0)
236
237/*
238 * MAC Framework per-object type functions.  It's not yet clear how the
239 * namespaces, etc, should work for these, so for now, sort by object type.
240 */
241struct label	*mac_cred_label_alloc(void);
242void		 mac_cred_label_free(struct label *label);
243struct label	*mac_pipe_label_alloc(void);
244void		 mac_pipe_label_free(struct label *label);
245struct label	*mac_socket_label_alloc(int flag);
246void		 mac_socket_label_free(struct label *label);
247struct label	*mac_vnode_label_alloc(void);
248void		 mac_vnode_label_free(struct label *label);
249
250int	mac_cred_check_relabel(struct ucred *cred, struct label *newlabel);
251int	mac_cred_externalize_label(struct label *label, char *elements,
252	    char *outbuf, size_t outbuflen);
253int	mac_cred_internalize_label(struct label *label, char *string);
254void	mac_cred_relabel(struct ucred *cred, struct label *newlabel);
255
256struct label	*mac_mbuf_to_label(struct mbuf *m);
257
258void	mac_pipe_copy_label(struct label *src, struct label *dest);
259int	mac_pipe_externalize_label(struct label *label, char *elements,
260	    char *outbuf, size_t outbuflen);
261int	mac_pipe_internalize_label(struct label *label, char *string);
262
263int	mac_socket_label_set(struct ucred *cred, struct socket *so,
264	    struct label *label);
265void	mac_socket_copy_label(struct label *src, struct label *dest);
266int	mac_socket_externalize_label(struct label *label, char *elements,
267	    char *outbuf, size_t outbuflen);
268int	mac_socket_internalize_label(struct label *label, char *string);
269
270int	mac_vnode_externalize_label(struct label *label, char *elements,
271	    char *outbuf, size_t outbuflen);
272int	mac_vnode_internalize_label(struct label *label, char *string);
273void	mac_vnode_check_mmap_downgrade(struct ucred *cred, struct vnode *vp,
274	    int *prot);
275int	vn_setlabel(struct vnode *vp, struct label *intlabel,
276	    struct ucred *cred);
277
278/*
279 * MAC Framework composition macros invoke all registered MAC policies for a
280 * specific entry point.  They come in two forms: one which permits policies
281 * to sleep/block, and another that does not.
282 *
283 * MAC_POLICY_CHECK performs the designated check by walking the policy
284 * module list and checking with each as to how it feels about the request.
285 * Note that it returns its value via 'error' in the scope of the caller.
286 */
287#define	MAC_POLICY_CHECK(check, args...) do {				\
288	struct mac_policy_conf *mpc;					\
289									\
290	error = 0;							\
291	LIST_FOREACH(mpc, &mac_static_policy_list, mpc_list) {		\
292		if (mpc->mpc_ops->mpo_ ## check != NULL)		\
293			error = mac_error_select(			\
294			    mpc->mpc_ops->mpo_ ## check (args),		\
295			    error);					\
296	}								\
297	if (!LIST_EMPTY(&mac_policy_list)) {				\
298		mac_policy_slock_sleep();				\
299		LIST_FOREACH(mpc, &mac_policy_list, mpc_list) {		\
300			if (mpc->mpc_ops->mpo_ ## check != NULL)	\
301				error = mac_error_select(		\
302				    mpc->mpc_ops->mpo_ ## check (args),	\
303				    error);				\
304		}							\
305		mac_policy_sunlock_sleep();				\
306	}								\
307} while (0)
308
309#define	MAC_POLICY_CHECK_NOSLEEP(check, args...) do {			\
310	struct mac_policy_conf *mpc;					\
311									\
312	error = 0;							\
313	LIST_FOREACH(mpc, &mac_static_policy_list, mpc_list) {		\
314		if (mpc->mpc_ops->mpo_ ## check != NULL)		\
315			error = mac_error_select(			\
316			    mpc->mpc_ops->mpo_ ## check (args),		\
317			    error);					\
318	}								\
319	if (!LIST_EMPTY(&mac_policy_list)) {				\
320		struct rm_priotracker tracker;				\
321									\
322		mac_policy_slock_nosleep(&tracker);			\
323		LIST_FOREACH(mpc, &mac_policy_list, mpc_list) {		\
324			if (mpc->mpc_ops->mpo_ ## check != NULL)	\
325				error = mac_error_select(		\
326				    mpc->mpc_ops->mpo_ ## check (args),	\
327				    error);				\
328		}							\
329		mac_policy_sunlock_nosleep(&tracker);			\
330	}								\
331} while (0)
332
333/*
334 * MAC_POLICY_GRANT performs the designated check by walking the policy
335 * module list and checking with each as to how it feels about the request.
336 * Unlike MAC_POLICY_CHECK, it grants if any policies return '0', and
337 * otherwise returns EPERM.  Note that it returns its value via 'error' in
338 * the scope of the caller.
339 */
340#define	MAC_POLICY_GRANT_NOSLEEP(check, args...) do {			\
341	struct mac_policy_conf *mpc;					\
342									\
343	error = EPERM;							\
344	LIST_FOREACH(mpc, &mac_static_policy_list, mpc_list) {		\
345		if (mpc->mpc_ops->mpo_ ## check != NULL) {		\
346			if (mpc->mpc_ops->mpo_ ## check(args) == 0)	\
347				error = 0;				\
348		}							\
349	}								\
350	if (!LIST_EMPTY(&mac_policy_list)) {				\
351		struct rm_priotracker tracker;				\
352									\
353		mac_policy_slock_nosleep(&tracker);			\
354		LIST_FOREACH(mpc, &mac_policy_list, mpc_list) {		\
355			if (mpc->mpc_ops->mpo_ ## check != NULL) {	\
356				if (mpc->mpc_ops->mpo_ ## check (args)	\
357				    == 0)				\
358					error = 0;			\
359			}						\
360		}							\
361		mac_policy_sunlock_nosleep(&tracker);			\
362	}								\
363} while (0)
364
365/*
366 * MAC_POLICY_BOOLEAN performs the designated boolean composition by walking
367 * the module list, invoking each instance of the operation, and combining
368 * the results using the passed C operator.  Note that it returns its value
369 * via 'result' in the scope of the caller, which should be initialized by
370 * the caller in a meaningful way to get a meaningful result.
371 */
372#define	MAC_POLICY_BOOLEAN(operation, composition, args...) do {	\
373	struct mac_policy_conf *mpc;					\
374									\
375	LIST_FOREACH(mpc, &mac_static_policy_list, mpc_list) {		\
376		if (mpc->mpc_ops->mpo_ ## operation != NULL)		\
377			result = result composition			\
378			    mpc->mpc_ops->mpo_ ## operation (args);	\
379	}								\
380	if (!LIST_EMPTY(&mac_policy_list)) {				\
381		mac_policy_slock_sleep();				\
382		LIST_FOREACH(mpc, &mac_policy_list, mpc_list) {		\
383			if (mpc->mpc_ops->mpo_ ## operation != NULL)	\
384				result = result composition		\
385				    mpc->mpc_ops->mpo_ ## operation	\
386				    (args);				\
387		}							\
388		mac_policy_sunlock_sleep();				\
389	}								\
390} while (0)
391
392#define	MAC_POLICY_BOOLEAN_NOSLEEP(operation, composition, args...) do {\
393	struct mac_policy_conf *mpc;					\
394									\
395	LIST_FOREACH(mpc, &mac_static_policy_list, mpc_list) {		\
396		if (mpc->mpc_ops->mpo_ ## operation != NULL)		\
397			result = result composition			\
398			    mpc->mpc_ops->mpo_ ## operation (args);	\
399	}								\
400	if (!LIST_EMPTY(&mac_policy_list)) {				\
401		struct rm_priotracker tracker;				\
402									\
403		mac_policy_slock_nosleep(&tracker);			\
404		LIST_FOREACH(mpc, &mac_policy_list, mpc_list) {		\
405			if (mpc->mpc_ops->mpo_ ## operation != NULL)	\
406				result = result composition		\
407				    mpc->mpc_ops->mpo_ ## operation	\
408				    (args);				\
409		}							\
410		mac_policy_sunlock_nosleep(&tracker);			\
411	}								\
412} while (0)
413
414/*
415 * MAC_POLICY_EXTERNALIZE queries each policy to see if it can generate an
416 * externalized version of a label element by name.  Policies declare whether
417 * they have matched a particular element name, parsed from the string by
418 * MAC_POLICY_EXTERNALIZE, and an error is returned if any element is matched
419 * by no policy.
420 */
421#define	MAC_POLICY_EXTERNALIZE(type, label, elementlist, outbuf, 	\
422    outbuflen) do {							\
423	int claimed, first, ignorenotfound, savedlen;			\
424	char *element_name, *element_temp;				\
425	struct sbuf sb;							\
426									\
427	error = 0;							\
428	first = 1;							\
429	sbuf_new(&sb, outbuf, outbuflen, SBUF_FIXEDLEN);		\
430	element_temp = elementlist;					\
431	while ((element_name = strsep(&element_temp, ",")) != NULL) {	\
432		if (element_name[0] == '?') {				\
433			element_name++;					\
434			ignorenotfound = 1;				\
435		 } else							\
436			ignorenotfound = 0;				\
437		savedlen = sbuf_len(&sb);				\
438		if (first)						\
439			error = sbuf_printf(&sb, "%s/", element_name);	\
440		else							\
441			error = sbuf_printf(&sb, ",%s/", element_name);	\
442		if (error == -1) {					\
443			error = EINVAL;	/* XXX: E2BIG? */		\
444			break;						\
445		}							\
446		claimed = 0;						\
447		MAC_POLICY_CHECK(type ## _externalize_label, label,	\
448		    element_name, &sb, &claimed);			\
449		if (error)						\
450			break;						\
451		if (claimed == 0 && ignorenotfound) {			\
452			/* Revert last label name. */			\
453			sbuf_setpos(&sb, savedlen);			\
454		} else if (claimed != 1) {				\
455			error = EINVAL;	/* XXX: ENOLABEL? */		\
456			break;						\
457		} else {						\
458			first = 0;					\
459		}							\
460	}								\
461	sbuf_finish(&sb);						\
462} while (0)
463
464/*
465 * MAC_POLICY_INTERNALIZE presents parsed element names and data to each
466 * policy to see if any is willing to claim it and internalize the label
467 * data.  If no policies match, an error is returned.
468 */
469#define	MAC_POLICY_INTERNALIZE(type, label, instring) do {		\
470	char *element, *element_name, *element_data;			\
471	int claimed;							\
472									\
473	error = 0;							\
474	element = instring;						\
475	while ((element_name = strsep(&element, ",")) != NULL) {	\
476		element_data = element_name;				\
477		element_name = strsep(&element_data, "/");		\
478		if (element_data == NULL) {				\
479			error = EINVAL;					\
480			break;						\
481		}							\
482		claimed = 0;						\
483		MAC_POLICY_CHECK(type ## _internalize_label, label,	\
484		    element_name, element_data, &claimed);		\
485		if (error)						\
486			break;						\
487		if (claimed != 1) {					\
488			/* XXXMAC: Another error here? */		\
489			error = EINVAL;					\
490			break;						\
491		}							\
492	}								\
493} while (0)
494
495/*
496 * MAC_POLICY_PERFORM performs the designated operation by walking the policy
497 * module list and invoking that operation for each policy.
498 */
499#define	MAC_POLICY_PERFORM(operation, args...) do {			\
500	struct mac_policy_conf *mpc;					\
501									\
502	LIST_FOREACH(mpc, &mac_static_policy_list, mpc_list) {		\
503		if (mpc->mpc_ops->mpo_ ## operation != NULL)		\
504			mpc->mpc_ops->mpo_ ## operation (args);		\
505	}								\
506	if (!LIST_EMPTY(&mac_policy_list)) {				\
507		mac_policy_slock_sleep();				\
508		LIST_FOREACH(mpc, &mac_policy_list, mpc_list) {		\
509			if (mpc->mpc_ops->mpo_ ## operation != NULL)	\
510				mpc->mpc_ops->mpo_ ## operation (args);	\
511		}							\
512		mac_policy_sunlock_sleep();				\
513	}								\
514} while (0)
515
516#define	MAC_POLICY_PERFORM_NOSLEEP(operation, args...) do {		\
517	struct mac_policy_conf *mpc;					\
518									\
519	LIST_FOREACH(mpc, &mac_static_policy_list, mpc_list) {		\
520		if (mpc->mpc_ops->mpo_ ## operation != NULL)		\
521			mpc->mpc_ops->mpo_ ## operation (args);		\
522	}								\
523	if (!LIST_EMPTY(&mac_policy_list)) {				\
524		struct rm_priotracker tracker;				\
525									\
526		mac_policy_slock_nosleep(&tracker);			\
527		LIST_FOREACH(mpc, &mac_policy_list, mpc_list) {		\
528			if (mpc->mpc_ops->mpo_ ## operation != NULL)	\
529				mpc->mpc_ops->mpo_ ## operation (args);	\
530		}							\
531		mac_policy_sunlock_nosleep(&tracker);			\
532	}								\
533} while (0)
534
535#endif /* !_SECURITY_MAC_MAC_INTERNAL_H_ */
536