mac_socket.c revision 126262
1/*-
2 * Copyright (c) 1999-2002 Robert N. M. Watson
3 * Copyright (c) 2001 Ilmar S. Habibulin
4 * Copyright (c) 2001-2004 Networks Associates Technology, Inc.
5 * All rights reserved.
6 *
7 * This software was developed by Robert Watson and Ilmar Habibulin for the
8 * TrustedBSD Project.
9 *
10 * This software was developed for the FreeBSD Project in part by Network
11 * Associates Laboratories, the Security Research Division of Network
12 * Associates, Inc. under DARPA/SPAWAR contract N66001-01-C-8035 ("CBOSS"),
13 * as part of the DARPA CHATS research program.
14 *
15 * Redistribution and use in source and binary forms, with or without
16 * modification, are permitted provided that the following conditions
17 * are met:
18 * 1. Redistributions of source code must retain the above copyright
19 *    notice, this list of conditions and the following disclaimer.
20 * 2. Redistributions in binary form must reproduce the above copyright
21 *    notice, this list of conditions and the following disclaimer in the
22 *    documentation and/or other materials provided with the distribution.
23 *
24 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
25 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
28 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34 * SUCH DAMAGE.
35 */
36
37#include <sys/cdefs.h>
38__FBSDID("$FreeBSD: head/sys/security/mac/mac_socket.c 126262 2004-02-26 03:51:04Z rwatson $");
39
40#include "opt_mac.h"
41
42#include <sys/param.h>
43#include <sys/kernel.h>
44#include <sys/lock.h>
45#include <sys/malloc.h>
46#include <sys/mutex.h>
47#include <sys/mac.h>
48#include <sys/sbuf.h>
49#include <sys/systm.h>
50#include <sys/mount.h>
51#include <sys/file.h>
52#include <sys/namei.h>
53#include <sys/protosw.h>
54#include <sys/socket.h>
55#include <sys/socketvar.h>
56#include <sys/sysctl.h>
57
58#include <sys/mac_policy.h>
59
60#include <net/bpfdesc.h>
61#include <net/if.h>
62#include <net/if_var.h>
63
64#include <netinet/in.h>
65#include <netinet/in_pcb.h>
66#include <netinet/ip_var.h>
67
68#include <security/mac/mac_internal.h>
69
70/*
71 * mac_enforce_socket is used by the inet code when delivering to an inpcb
72 * without hitting the socket layer, and has to be non-static for now.
73 */
74int	mac_enforce_socket = 1;
75SYSCTL_INT(_security_mac, OID_AUTO, enforce_socket, CTLFLAG_RW,
76    &mac_enforce_socket, 0, "Enforce MAC policy on socket operations");
77TUNABLE_INT("security.mac.enforce_socket", &mac_enforce_socket);
78
79#ifdef MAC_DEBUG
80static unsigned int nmacsockets;
81
82SYSCTL_UINT(_security_mac_debug_counters, OID_AUTO, sockets, CTLFLAG_RD,
83    &nmacsockets, 0, "number of sockets in use");
84#endif
85
86struct label *
87mac_socket_label_alloc(int flag)
88{
89	struct label *label;
90	int error;
91
92	label = mac_labelzone_alloc(flag);
93	if (label == NULL)
94		return (NULL);
95
96	MAC_CHECK(init_socket_label, label, flag);
97	if (error) {
98		MAC_PERFORM(destroy_socket_label, label);
99		mac_labelzone_free(label);
100		return (NULL);
101	}
102	MAC_DEBUG_COUNTER_INC(&nmacsockets);
103	return (label);
104}
105
106static struct label *
107mac_socket_peer_label_alloc(int flag)
108{
109	struct label *label;
110	int error;
111
112	label = mac_labelzone_alloc(flag);
113	if (label == NULL)
114		return (NULL);
115
116	MAC_CHECK(init_socket_peer_label, label, flag);
117	if (error) {
118		MAC_PERFORM(destroy_socket_peer_label, label);
119		mac_labelzone_free(label);
120		return (NULL);
121	}
122	MAC_DEBUG_COUNTER_INC(&nmacsockets);
123	return (label);
124}
125
126int
127mac_init_socket(struct socket *so, int flag)
128{
129
130	so->so_label = mac_socket_label_alloc(flag);
131	if (so->so_label == NULL)
132		return (ENOMEM);
133	so->so_peerlabel = mac_socket_peer_label_alloc(flag);
134	if (so->so_peerlabel == NULL) {
135		mac_socket_label_free(so->so_label);
136		so->so_label = NULL;
137		return (ENOMEM);
138	}
139	return (0);
140}
141
142void
143mac_socket_label_free(struct label *label)
144{
145
146	MAC_PERFORM(destroy_socket_label, label);
147	mac_labelzone_free(label);
148	MAC_DEBUG_COUNTER_DEC(&nmacsockets);
149}
150
151static void
152mac_socket_peer_label_free(struct label *label)
153{
154
155	MAC_PERFORM(destroy_socket_peer_label, label);
156	mac_labelzone_free(label);
157	MAC_DEBUG_COUNTER_DEC(&nmacsockets);
158}
159
160void
161mac_destroy_socket(struct socket *socket)
162{
163
164	mac_socket_label_free(socket->so_label);
165	socket->so_label = NULL;
166	mac_socket_peer_label_free(socket->so_peerlabel);
167	socket->so_peerlabel = NULL;
168}
169
170void
171mac_copy_socket_label(struct label *src, struct label *dest)
172{
173
174	MAC_PERFORM(copy_socket_label, src, dest);
175}
176
177int
178mac_externalize_socket_label(struct label *label, char *elements,
179    char *outbuf, size_t outbuflen)
180{
181	int error;
182
183	MAC_EXTERNALIZE(socket, label, elements, outbuf, outbuflen);
184
185	return (error);
186}
187
188static int
189mac_externalize_socket_peer_label(struct label *label, char *elements,
190    char *outbuf, size_t outbuflen)
191{
192	int error;
193
194	MAC_EXTERNALIZE(socket_peer, label, elements, outbuf, outbuflen);
195
196	return (error);
197}
198
199int
200mac_internalize_socket_label(struct label *label, char *string)
201{
202	int error;
203
204	MAC_INTERNALIZE(socket, label, string);
205
206	return (error);
207}
208
209void
210mac_create_socket(struct ucred *cred, struct socket *socket)
211{
212
213	MAC_PERFORM(create_socket, cred, socket, socket->so_label);
214}
215
216void
217mac_create_socket_from_socket(struct socket *oldsocket,
218    struct socket *newsocket)
219{
220
221	MAC_PERFORM(create_socket_from_socket, oldsocket, oldsocket->so_label,
222	    newsocket, newsocket->so_label);
223}
224
225static void
226mac_relabel_socket(struct ucred *cred, struct socket *socket,
227    struct label *newlabel)
228{
229
230	MAC_PERFORM(relabel_socket, cred, socket, socket->so_label, newlabel);
231}
232
233void
234mac_set_socket_peer_from_mbuf(struct mbuf *mbuf, struct socket *socket)
235{
236	struct label *label;
237
238	label = mac_mbuf_to_label(mbuf);
239
240	MAC_PERFORM(set_socket_peer_from_mbuf, mbuf, label, socket,
241	    socket->so_peerlabel);
242}
243
244void
245mac_set_socket_peer_from_socket(struct socket *oldsocket,
246    struct socket *newsocket)
247{
248
249	MAC_PERFORM(set_socket_peer_from_socket, oldsocket,
250	    oldsocket->so_label, newsocket, newsocket->so_peerlabel);
251}
252
253void
254mac_create_mbuf_from_socket(struct socket *socket, struct mbuf *mbuf)
255{
256	struct label *label;
257
258	label = mac_mbuf_to_label(mbuf);
259
260	MAC_PERFORM(create_mbuf_from_socket, socket, socket->so_label, mbuf,
261	    label);
262}
263
264int
265mac_check_socket_bind(struct ucred *ucred, struct socket *socket,
266    struct sockaddr *sockaddr)
267{
268	int error;
269
270	if (!mac_enforce_socket)
271		return (0);
272
273	MAC_CHECK(check_socket_bind, ucred, socket, socket->so_label,
274	    sockaddr);
275
276	return (error);
277}
278
279int
280mac_check_socket_connect(struct ucred *cred, struct socket *socket,
281    struct sockaddr *sockaddr)
282{
283	int error;
284
285	if (!mac_enforce_socket)
286		return (0);
287
288	MAC_CHECK(check_socket_connect, cred, socket, socket->so_label,
289	    sockaddr);
290
291	return (error);
292}
293
294int
295mac_check_socket_deliver(struct socket *socket, struct mbuf *mbuf)
296{
297	struct label *label;
298	int error;
299
300	if (!mac_enforce_socket)
301		return (0);
302
303	label = mac_mbuf_to_label(mbuf);
304
305	MAC_CHECK(check_socket_deliver, socket, socket->so_label, mbuf,
306	    label);
307
308	return (error);
309}
310
311int
312mac_check_socket_listen(struct ucred *cred, struct socket *socket)
313{
314	int error;
315
316	if (!mac_enforce_socket)
317		return (0);
318
319	MAC_CHECK(check_socket_listen, cred, socket, socket->so_label);
320	return (error);
321}
322
323int
324mac_check_socket_receive(struct ucred *cred, struct socket *so)
325{
326	int error;
327
328	if (!mac_enforce_socket)
329		return (0);
330
331	MAC_CHECK(check_socket_receive, cred, so, so->so_label);
332
333	return (error);
334}
335
336static int
337mac_check_socket_relabel(struct ucred *cred, struct socket *socket,
338    struct label *newlabel)
339{
340	int error;
341
342	MAC_CHECK(check_socket_relabel, cred, socket, socket->so_label,
343	    newlabel);
344
345	return (error);
346}
347
348int
349mac_check_socket_send(struct ucred *cred, struct socket *so)
350{
351	int error;
352
353	if (!mac_enforce_socket)
354		return (0);
355
356	MAC_CHECK(check_socket_send, cred, so, so->so_label);
357
358	return (error);
359}
360
361int
362mac_check_socket_visible(struct ucred *cred, struct socket *socket)
363{
364	int error;
365
366	if (!mac_enforce_socket)
367		return (0);
368
369	MAC_CHECK(check_socket_visible, cred, socket, socket->so_label);
370
371	return (error);
372}
373
374int
375mac_socket_label_set(struct ucred *cred, struct socket *so,
376    struct label *label)
377{
378	int error;
379
380	error = mac_check_socket_relabel(cred, so, label);
381	if (error)
382		return (error);
383
384	mac_relabel_socket(cred, so, label);
385
386	/*
387	 * If the protocol has expressed interest in socket layer changes,
388	 * such as if it needs to propagate changes to a cached pcb
389	 * label from the socket, notify it of the label change while
390	 * holding the socket lock.
391	 */
392	if (so->so_proto->pr_usrreqs->pru_sosetlabel != NULL)
393		(so->so_proto->pr_usrreqs->pru_sosetlabel)(so);
394
395	return (0);
396}
397
398int
399mac_setsockopt_label(struct ucred *cred, struct socket *so, struct mac *mac)
400{
401	struct label *intlabel;
402	char *buffer;
403	int error;
404
405	error = mac_check_structmac_consistent(mac);
406	if (error)
407		return (error);
408
409	buffer = malloc(mac->m_buflen, M_MACTEMP, M_WAITOK);
410	error = copyinstr(mac->m_string, buffer, mac->m_buflen, NULL);
411	if (error) {
412		free(buffer, M_MACTEMP);
413		return (error);
414	}
415
416	intlabel = mac_socket_label_alloc(M_WAITOK);
417	error = mac_internalize_socket_label(intlabel, buffer);
418	free(buffer, M_MACTEMP);
419	if (error)
420		goto out;
421
422	/* XXX: Socket lock here. */
423	error = mac_socket_label_set(cred, so, intlabel);
424	/* XXX: Socket unlock here. */
425out:
426	mac_socket_label_free(intlabel);
427	return (error);
428}
429
430int
431mac_getsockopt_label(struct ucred *cred, struct socket *so, struct mac *mac)
432{
433	char *buffer, *elements;
434	int error;
435
436	error = mac_check_structmac_consistent(mac);
437	if (error)
438		return (error);
439
440	elements = malloc(mac->m_buflen, M_MACTEMP, M_WAITOK);
441	error = copyinstr(mac->m_string, elements, mac->m_buflen, NULL);
442	if (error) {
443		free(elements, M_MACTEMP);
444		return (error);
445	}
446
447	buffer = malloc(mac->m_buflen, M_MACTEMP, M_WAITOK | M_ZERO);
448	error = mac_externalize_socket_label(so->so_label, elements,
449	    buffer, mac->m_buflen);
450	if (error == 0)
451		error = copyout(buffer, mac->m_string, strlen(buffer)+1);
452
453	free(buffer, M_MACTEMP);
454	free(elements, M_MACTEMP);
455
456	return (error);
457}
458
459int
460mac_getsockopt_peerlabel(struct ucred *cred, struct socket *so,
461    struct mac *mac)
462{
463	char *elements, *buffer;
464	int error;
465
466	error = mac_check_structmac_consistent(mac);
467	if (error)
468		return (error);
469
470	elements = malloc(mac->m_buflen, M_MACTEMP, M_WAITOK);
471	error = copyinstr(mac->m_string, elements, mac->m_buflen, NULL);
472	if (error) {
473		free(elements, M_MACTEMP);
474		return (error);
475	}
476
477	buffer = malloc(mac->m_buflen, M_MACTEMP, M_WAITOK | M_ZERO);
478	error = mac_externalize_socket_peer_label(so->so_peerlabel,
479	    elements, buffer, mac->m_buflen);
480	if (error == 0)
481		error = copyout(buffer, mac->m_string, strlen(buffer)+1);
482
483	free(buffer, M_MACTEMP);
484	free(elements, M_MACTEMP);
485
486	return (error);
487}
488