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