1/*-
2 * Copyright (c) 1999-2002, 2009 Robert N. M. Watson
3 * Copyright (c) 2001 Ilmar S. Habibulin
4 * Copyright (c) 2001-2004 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 enhanced by SPARTA ISSO under SPAWAR contract
13 * N66001-04-C-6019 ("SEFOS").
14 *
15 * This software was developed for the FreeBSD Project in part by Network
16 * Associates Laboratories, the Security Research Division of Network
17 * Associates, Inc. under DARPA/SPAWAR contract N66001-01-C-8035 ("CBOSS"),
18 * as part of the DARPA CHATS research program.
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/kernel.h>
52#include <sys/lock.h>
53#include <sys/malloc.h>
54#include <sys/mutex.h>
55#include <sys/mac.h>
56#include <sys/priv.h>
57#include <sys/sbuf.h>
58#include <sys/sdt.h>
59#include <sys/systm.h>
60#include <sys/mount.h>
61#include <sys/file.h>
62#include <sys/namei.h>
63#include <sys/protosw.h>
64#include <sys/socket.h>
65#include <sys/socketvar.h>
66#include <sys/sysctl.h>
67
68#include <net/bpfdesc.h>
69#include <net/if.h>
70#include <net/if_var.h>
71
72#include <security/mac/mac_framework.h>
73#include <security/mac/mac_internal.h>
74#include <security/mac/mac_policy.h>
75
76/*
77 * XXXRW: struct ifnet locking is incomplete in the network code, so we use
78 * our own global mutex for struct ifnet.  Non-ideal, but should help in the
79 * SMP environment.
80 */
81struct mtx mac_ifnet_mtx;
82MTX_SYSINIT(mac_ifnet_mtx, &mac_ifnet_mtx, "mac_ifnet", MTX_DEF);
83
84/*
85 * Retrieve the label associated with an mbuf by searching for the tag.
86 * Depending on the value of mac_labelmbufs, it's possible that a label will
87 * not be present, in which case NULL is returned.  Policies must handle the
88 * possibility of an mbuf not having label storage if they do not enforce
89 * early loading.
90 */
91struct label *
92mac_mbuf_to_label(struct mbuf *m)
93{
94	struct m_tag *tag;
95	struct label *label;
96
97	if (m == NULL)
98		return (NULL);
99	tag = m_tag_find(m, PACKET_TAG_MACLABEL, NULL);
100	if (tag == NULL)
101		return (NULL);
102	label = (struct label *)(tag+1);
103	return (label);
104}
105
106static struct label *
107mac_bpfdesc_label_alloc(void)
108{
109	struct label *label;
110
111	label = mac_labelzone_alloc(M_WAITOK);
112	MAC_POLICY_PERFORM(bpfdesc_init_label, label);
113	return (label);
114}
115
116void
117mac_bpfdesc_init(struct bpf_d *d)
118{
119
120	if (mac_labeled & MPC_OBJECT_BPFDESC)
121		d->bd_label = mac_bpfdesc_label_alloc();
122	else
123		d->bd_label = NULL;
124}
125
126static struct label *
127mac_ifnet_label_alloc(void)
128{
129	struct label *label;
130
131	label = mac_labelzone_alloc(M_WAITOK);
132	MAC_POLICY_PERFORM(ifnet_init_label, label);
133	return (label);
134}
135
136void
137mac_ifnet_init(struct ifnet *ifp)
138{
139
140	if (mac_labeled & MPC_OBJECT_IFNET)
141		ifp->if_label = mac_ifnet_label_alloc();
142	else
143		ifp->if_label = NULL;
144}
145
146int
147mac_mbuf_tag_init(struct m_tag *tag, int flag)
148{
149	struct label *label;
150	int error;
151
152	label = (struct label *) (tag + 1);
153	mac_init_label(label);
154
155	if (flag & M_WAITOK)
156		MAC_POLICY_CHECK(mbuf_init_label, label, flag);
157	else
158		MAC_POLICY_CHECK_NOSLEEP(mbuf_init_label, label, flag);
159	if (error) {
160		MAC_POLICY_PERFORM_NOSLEEP(mbuf_destroy_label, label);
161		mac_destroy_label(label);
162	}
163	return (error);
164}
165
166int
167mac_mbuf_init(struct mbuf *m, int flag)
168{
169	struct m_tag *tag;
170	int error;
171
172	M_ASSERTPKTHDR(m);
173
174	if (mac_labeled & MPC_OBJECT_MBUF) {
175		tag = m_tag_get(PACKET_TAG_MACLABEL, sizeof(struct label),
176		    flag);
177		if (tag == NULL)
178			return (ENOMEM);
179		error = mac_mbuf_tag_init(tag, flag);
180		if (error) {
181			m_tag_free(tag);
182			return (error);
183		}
184		m_tag_prepend(m, tag);
185	}
186	return (0);
187}
188
189static void
190mac_bpfdesc_label_free(struct label *label)
191{
192
193	MAC_POLICY_PERFORM_NOSLEEP(bpfdesc_destroy_label, label);
194	mac_labelzone_free(label);
195}
196
197void
198mac_bpfdesc_destroy(struct bpf_d *d)
199{
200
201	if (d->bd_label != NULL) {
202		mac_bpfdesc_label_free(d->bd_label);
203		d->bd_label = NULL;
204	}
205}
206
207static void
208mac_ifnet_label_free(struct label *label)
209{
210
211	MAC_POLICY_PERFORM_NOSLEEP(ifnet_destroy_label, label);
212	mac_labelzone_free(label);
213}
214
215void
216mac_ifnet_destroy(struct ifnet *ifp)
217{
218
219	if (ifp->if_label != NULL) {
220		mac_ifnet_label_free(ifp->if_label);
221		ifp->if_label = NULL;
222	}
223}
224
225void
226mac_mbuf_tag_destroy(struct m_tag *tag)
227{
228	struct label *label;
229
230	label = (struct label *)(tag+1);
231
232	MAC_POLICY_PERFORM_NOSLEEP(mbuf_destroy_label, label);
233	mac_destroy_label(label);
234}
235
236/*
237 * mac_mbuf_tag_copy is called when an mbuf header is duplicated, in which
238 * case the labels must also be duplicated.
239 */
240void
241mac_mbuf_tag_copy(struct m_tag *src, struct m_tag *dest)
242{
243	struct label *src_label, *dest_label;
244
245	src_label = (struct label *)(src+1);
246	dest_label = (struct label *)(dest+1);
247
248	/*
249	 * mac_mbuf_tag_init() is called on the target tag in m_tag_copy(),
250	 * so we don't need to call it here.
251	 */
252	MAC_POLICY_PERFORM_NOSLEEP(mbuf_copy_label, src_label, dest_label);
253}
254
255void
256mac_mbuf_copy(struct mbuf *m_from, struct mbuf *m_to)
257{
258	struct label *src_label, *dest_label;
259
260	if (mac_policy_count == 0)
261		return;
262
263	src_label = mac_mbuf_to_label(m_from);
264	dest_label = mac_mbuf_to_label(m_to);
265
266	MAC_POLICY_PERFORM_NOSLEEP(mbuf_copy_label, src_label, dest_label);
267}
268
269static void
270mac_ifnet_copy_label(struct label *src, struct label *dest)
271{
272
273	MAC_POLICY_PERFORM_NOSLEEP(ifnet_copy_label, src, dest);
274}
275
276static int
277mac_ifnet_externalize_label(struct label *label, char *elements,
278    char *outbuf, size_t outbuflen)
279{
280	int error;
281
282	MAC_POLICY_EXTERNALIZE(ifnet, label, elements, outbuf, outbuflen);
283
284	return (error);
285}
286
287static int
288mac_ifnet_internalize_label(struct label *label, char *string)
289{
290	int error;
291
292	MAC_POLICY_INTERNALIZE(ifnet, label, string);
293
294	return (error);
295}
296
297void
298mac_ifnet_create(struct ifnet *ifp)
299{
300
301	if (mac_policy_count == 0)
302		return;
303
304	MAC_IFNET_LOCK(ifp);
305	MAC_POLICY_PERFORM_NOSLEEP(ifnet_create, ifp, ifp->if_label);
306	MAC_IFNET_UNLOCK(ifp);
307}
308
309void
310mac_bpfdesc_create(struct ucred *cred, struct bpf_d *d)
311{
312
313	MAC_POLICY_PERFORM_NOSLEEP(bpfdesc_create, cred, d, d->bd_label);
314}
315
316void
317mac_bpfdesc_create_mbuf(struct bpf_d *d, struct mbuf *m)
318{
319	struct label *label;
320
321	/* Assume reader lock is enough. */
322	BPFD_LOCK_ASSERT(d);
323
324	if (mac_policy_count == 0)
325		return;
326
327	label = mac_mbuf_to_label(m);
328
329	MAC_POLICY_PERFORM_NOSLEEP(bpfdesc_create_mbuf, d, d->bd_label, m,
330	    label);
331}
332
333void
334mac_ifnet_create_mbuf(struct ifnet *ifp, struct mbuf *m)
335{
336	struct label *label;
337
338	if (mac_policy_count == 0)
339		return;
340
341	label = mac_mbuf_to_label(m);
342
343	MAC_IFNET_LOCK(ifp);
344	MAC_POLICY_PERFORM_NOSLEEP(ifnet_create_mbuf, ifp, ifp->if_label, m,
345	    label);
346	MAC_IFNET_UNLOCK(ifp);
347}
348
349MAC_CHECK_PROBE_DEFINE2(bpfdesc_check_receive, "struct bpf_d *",
350    "struct ifnet *");
351
352int
353mac_bpfdesc_check_receive(struct bpf_d *d, struct ifnet *ifp)
354{
355	int error;
356
357	/* Assume reader lock is enough. */
358	BPFD_LOCK_ASSERT(d);
359
360	if (mac_policy_count == 0)
361		return (0);
362
363	MAC_IFNET_LOCK(ifp);
364	MAC_POLICY_CHECK_NOSLEEP(bpfdesc_check_receive, d, d->bd_label, ifp,
365	    ifp->if_label);
366	MAC_CHECK_PROBE2(bpfdesc_check_receive, error, d, ifp);
367	MAC_IFNET_UNLOCK(ifp);
368
369	return (error);
370}
371
372MAC_CHECK_PROBE_DEFINE2(ifnet_check_transmit, "struct ifnet *",
373    "struct mbuf *");
374
375int
376mac_ifnet_check_transmit(struct ifnet *ifp, struct mbuf *m)
377{
378	struct label *label;
379	int error;
380
381	M_ASSERTPKTHDR(m);
382
383	if (mac_policy_count == 0)
384		return (0);
385
386	label = mac_mbuf_to_label(m);
387
388	MAC_IFNET_LOCK(ifp);
389	MAC_POLICY_CHECK_NOSLEEP(ifnet_check_transmit, ifp, ifp->if_label, m,
390	    label);
391	MAC_CHECK_PROBE2(ifnet_check_transmit, error, ifp, m);
392	MAC_IFNET_UNLOCK(ifp);
393
394	return (error);
395}
396
397int
398mac_ifnet_ioctl_get(struct ucred *cred, struct ifreq *ifr,
399    struct ifnet *ifp)
400{
401	char *elements, *buffer;
402	struct label *intlabel;
403	struct mac mac;
404	int error;
405
406	if (!(mac_labeled & MPC_OBJECT_IFNET))
407		return (EINVAL);
408
409	error = copyin(ifr_data_get_ptr(ifr), &mac, sizeof(mac));
410	if (error)
411		return (error);
412
413	error = mac_check_structmac_consistent(&mac);
414	if (error)
415		return (error);
416
417	elements = malloc(mac.m_buflen, M_MACTEMP, M_WAITOK);
418	error = copyinstr(mac.m_string, elements, mac.m_buflen, NULL);
419	if (error) {
420		free(elements, M_MACTEMP);
421		return (error);
422	}
423
424	buffer = malloc(mac.m_buflen, M_MACTEMP, M_WAITOK | M_ZERO);
425	intlabel = mac_ifnet_label_alloc();
426	MAC_IFNET_LOCK(ifp);
427	mac_ifnet_copy_label(ifp->if_label, intlabel);
428	MAC_IFNET_UNLOCK(ifp);
429	error = mac_ifnet_externalize_label(intlabel, elements, buffer,
430	    mac.m_buflen);
431	mac_ifnet_label_free(intlabel);
432	if (error == 0)
433		error = copyout(buffer, mac.m_string, strlen(buffer)+1);
434
435	free(buffer, M_MACTEMP);
436	free(elements, M_MACTEMP);
437
438	return (error);
439}
440
441int
442mac_ifnet_ioctl_set(struct ucred *cred, struct ifreq *ifr, struct ifnet *ifp)
443{
444	struct label *intlabel;
445	struct mac mac;
446	char *buffer;
447	int error;
448
449	if (!(mac_labeled & MPC_OBJECT_IFNET))
450		return (EINVAL);
451
452	error = copyin(ifr_data_get_ptr(ifr), &mac, sizeof(mac));
453	if (error)
454		return (error);
455
456	error = mac_check_structmac_consistent(&mac);
457	if (error)
458		return (error);
459
460	buffer = malloc(mac.m_buflen, M_MACTEMP, M_WAITOK);
461	error = copyinstr(mac.m_string, buffer, mac.m_buflen, NULL);
462	if (error) {
463		free(buffer, M_MACTEMP);
464		return (error);
465	}
466
467	intlabel = mac_ifnet_label_alloc();
468	error = mac_ifnet_internalize_label(intlabel, buffer);
469	free(buffer, M_MACTEMP);
470	if (error) {
471		mac_ifnet_label_free(intlabel);
472		return (error);
473	}
474
475	/*
476	 * XXX: Note that this is a redundant privilege check, since policies
477	 * impose this check themselves if required by the policy
478	 * Eventually, this should go away.
479	 */
480	error = priv_check_cred(cred, PRIV_NET_SETIFMAC, 0);
481	if (error) {
482		mac_ifnet_label_free(intlabel);
483		return (error);
484	}
485
486	MAC_IFNET_LOCK(ifp);
487	MAC_POLICY_CHECK_NOSLEEP(ifnet_check_relabel, cred, ifp,
488	    ifp->if_label, intlabel);
489	if (error) {
490		MAC_IFNET_UNLOCK(ifp);
491		mac_ifnet_label_free(intlabel);
492		return (error);
493	}
494
495	MAC_POLICY_PERFORM_NOSLEEP(ifnet_relabel, cred, ifp, ifp->if_label,
496	    intlabel);
497	MAC_IFNET_UNLOCK(ifp);
498
499	mac_ifnet_label_free(intlabel);
500	return (0);
501}
502