xref: /freebsd/sys/security/mac/mac_socket.c (revision d056fa046c6a91b90cd98165face0e42a33a5173)
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_internal.h>
73 
74 /*
75  * mac_enforce_socket is used by the inet code when delivering to an inpcb
76  * without hitting the socket layer, and has to be non-static for now.
77  */
78 int	mac_enforce_socket = 1;
79 SYSCTL_INT(_security_mac, OID_AUTO, enforce_socket, CTLFLAG_RW,
80     &mac_enforce_socket, 0, "Enforce MAC policy on socket operations");
81 TUNABLE_INT("security.mac.enforce_socket", &mac_enforce_socket);
82 
83 #ifdef MAC_DEBUG
84 static unsigned int nmacsockets;
85 
86 SYSCTL_UINT(_security_mac_debug_counters, OID_AUTO, sockets, CTLFLAG_RD,
87     &nmacsockets, 0, "number of sockets in use");
88 #endif
89 
90 struct label *
91 mac_socket_label_alloc(int flag)
92 {
93 	struct label *label;
94 	int error;
95 
96 	label = mac_labelzone_alloc(flag);
97 	if (label == NULL)
98 		return (NULL);
99 
100 	MAC_CHECK(init_socket_label, label, flag);
101 	if (error) {
102 		MAC_PERFORM(destroy_socket_label, label);
103 		mac_labelzone_free(label);
104 		return (NULL);
105 	}
106 	MAC_DEBUG_COUNTER_INC(&nmacsockets);
107 	return (label);
108 }
109 
110 static struct label *
111 mac_socket_peer_label_alloc(int flag)
112 {
113 	struct label *label;
114 	int error;
115 
116 	label = mac_labelzone_alloc(flag);
117 	if (label == NULL)
118 		return (NULL);
119 
120 	MAC_CHECK(init_socket_peer_label, label, flag);
121 	if (error) {
122 		MAC_PERFORM(destroy_socket_peer_label, label);
123 		mac_labelzone_free(label);
124 		return (NULL);
125 	}
126 	MAC_DEBUG_COUNTER_INC(&nmacsockets);
127 	return (label);
128 }
129 
130 int
131 mac_init_socket(struct socket *so, int flag)
132 {
133 
134 	so->so_label = mac_socket_label_alloc(flag);
135 	if (so->so_label == NULL)
136 		return (ENOMEM);
137 	so->so_peerlabel = mac_socket_peer_label_alloc(flag);
138 	if (so->so_peerlabel == NULL) {
139 		mac_socket_label_free(so->so_label);
140 		so->so_label = NULL;
141 		return (ENOMEM);
142 	}
143 	return (0);
144 }
145 
146 void
147 mac_socket_label_free(struct label *label)
148 {
149 
150 	MAC_PERFORM(destroy_socket_label, label);
151 	mac_labelzone_free(label);
152 	MAC_DEBUG_COUNTER_DEC(&nmacsockets);
153 }
154 
155 static void
156 mac_socket_peer_label_free(struct label *label)
157 {
158 
159 	MAC_PERFORM(destroy_socket_peer_label, label);
160 	mac_labelzone_free(label);
161 	MAC_DEBUG_COUNTER_DEC(&nmacsockets);
162 }
163 
164 void
165 mac_destroy_socket(struct socket *socket)
166 {
167 
168 	mac_socket_label_free(socket->so_label);
169 	socket->so_label = NULL;
170 	mac_socket_peer_label_free(socket->so_peerlabel);
171 	socket->so_peerlabel = NULL;
172 }
173 
174 void
175 mac_copy_socket_label(struct label *src, struct label *dest)
176 {
177 
178 	MAC_PERFORM(copy_socket_label, src, dest);
179 }
180 
181 int
182 mac_externalize_socket_label(struct label *label, char *elements,
183     char *outbuf, size_t outbuflen)
184 {
185 	int error;
186 
187 	MAC_EXTERNALIZE(socket, label, elements, outbuf, outbuflen);
188 
189 	return (error);
190 }
191 
192 static int
193 mac_externalize_socket_peer_label(struct label *label, char *elements,
194     char *outbuf, size_t outbuflen)
195 {
196 	int error;
197 
198 	MAC_EXTERNALIZE(socket_peer, label, elements, outbuf, outbuflen);
199 
200 	return (error);
201 }
202 
203 int
204 mac_internalize_socket_label(struct label *label, char *string)
205 {
206 	int error;
207 
208 	MAC_INTERNALIZE(socket, label, string);
209 
210 	return (error);
211 }
212 
213 void
214 mac_create_socket(struct ucred *cred, struct socket *socket)
215 {
216 
217 	MAC_PERFORM(create_socket, cred, socket, socket->so_label);
218 }
219 
220 void
221 mac_create_socket_from_socket(struct socket *oldsocket,
222     struct socket *newsocket)
223 {
224 
225 	SOCK_LOCK_ASSERT(oldsocket);
226 	MAC_PERFORM(create_socket_from_socket, oldsocket, oldsocket->so_label,
227 	    newsocket, newsocket->so_label);
228 }
229 
230 static void
231 mac_relabel_socket(struct ucred *cred, struct socket *socket,
232     struct label *newlabel)
233 {
234 
235 	SOCK_LOCK_ASSERT(socket);
236 	MAC_PERFORM(relabel_socket, cred, socket, socket->so_label, newlabel);
237 }
238 
239 void
240 mac_set_socket_peer_from_mbuf(struct mbuf *mbuf, struct socket *socket)
241 {
242 	struct label *label;
243 
244 	SOCK_LOCK_ASSERT(socket);
245 
246 	label = mac_mbuf_to_label(mbuf);
247 
248 	MAC_PERFORM(set_socket_peer_from_mbuf, mbuf, label, socket,
249 	    socket->so_peerlabel);
250 }
251 
252 void
253 mac_set_socket_peer_from_socket(struct socket *oldsocket,
254     struct socket *newsocket)
255 {
256 
257 	/*
258 	 * XXXRW: only hold the socket lock on one at a time, as one
259 	 * socket is the original, and one is the new.  However, it's
260 	 * called in both directions, so we can't assert the lock
261 	 * here currently.
262 	 */
263 	MAC_PERFORM(set_socket_peer_from_socket, oldsocket,
264 	    oldsocket->so_label, newsocket, newsocket->so_peerlabel);
265 }
266 
267 void
268 mac_create_mbuf_from_socket(struct socket *socket, struct mbuf *mbuf)
269 {
270 	struct label *label;
271 
272 	label = mac_mbuf_to_label(mbuf);
273 
274 	SOCK_LOCK_ASSERT(socket);
275 	MAC_PERFORM(create_mbuf_from_socket, socket, socket->so_label, mbuf,
276 	    label);
277 }
278 
279 int
280 mac_check_socket_accept(struct ucred *cred, struct socket *socket)
281 {
282 	int error;
283 
284 	SOCK_LOCK_ASSERT(socket);
285 
286 	if (!mac_enforce_socket)
287 		return (0);
288 
289 	MAC_CHECK(check_socket_accept, cred, socket, socket->so_label);
290 
291 	return (error);
292 }
293 
294 int
295 mac_check_socket_bind(struct ucred *ucred, struct socket *socket,
296     struct sockaddr *sockaddr)
297 {
298 	int error;
299 
300 	SOCK_LOCK_ASSERT(socket);
301 
302 	if (!mac_enforce_socket)
303 		return (0);
304 
305 	MAC_CHECK(check_socket_bind, ucred, socket, socket->so_label,
306 	    sockaddr);
307 
308 	return (error);
309 }
310 
311 int
312 mac_check_socket_connect(struct ucred *cred, struct socket *socket,
313     struct sockaddr *sockaddr)
314 {
315 	int error;
316 
317 	SOCK_LOCK_ASSERT(socket);
318 
319 	if (!mac_enforce_socket)
320 		return (0);
321 
322 	MAC_CHECK(check_socket_connect, cred, socket, socket->so_label,
323 	    sockaddr);
324 
325 	return (error);
326 }
327 
328 int
329 mac_check_socket_create(struct ucred *cred, int domain, int type,
330     int protocol)
331 {
332 	int error;
333 
334 	if (!mac_enforce_socket)
335 		return (0);
336 
337 	MAC_CHECK(check_socket_create, cred, domain, type, protocol);
338 
339 	return (error);
340 }
341 
342 int
343 mac_check_socket_deliver(struct socket *socket, struct mbuf *mbuf)
344 {
345 	struct label *label;
346 	int error;
347 
348 	SOCK_LOCK_ASSERT(socket);
349 
350 	if (!mac_enforce_socket)
351 		return (0);
352 
353 	label = mac_mbuf_to_label(mbuf);
354 
355 	MAC_CHECK(check_socket_deliver, socket, socket->so_label, mbuf,
356 	    label);
357 
358 	return (error);
359 }
360 
361 int
362 mac_check_socket_listen(struct ucred *cred, struct socket *socket)
363 {
364 	int error;
365 
366 	SOCK_LOCK_ASSERT(socket);
367 
368 	if (!mac_enforce_socket)
369 		return (0);
370 
371 	MAC_CHECK(check_socket_listen, cred, socket, socket->so_label);
372 	return (error);
373 }
374 
375 int
376 mac_check_socket_poll(struct ucred *cred, struct socket *so)
377 {
378 	int error;
379 
380 	SOCK_LOCK_ASSERT(so);
381 
382 	if (!mac_enforce_socket)
383 		return (0);
384 
385 	MAC_CHECK(check_socket_poll, cred, so, so->so_label);
386 	return (error);
387 }
388 
389 int
390 mac_check_socket_receive(struct ucred *cred, struct socket *so)
391 {
392 	int error;
393 
394 	SOCK_LOCK_ASSERT(so);
395 
396 	if (!mac_enforce_socket)
397 		return (0);
398 
399 	MAC_CHECK(check_socket_receive, cred, so, so->so_label);
400 
401 	return (error);
402 }
403 
404 static int
405 mac_check_socket_relabel(struct ucred *cred, struct socket *socket,
406     struct label *newlabel)
407 {
408 	int error;
409 
410 	SOCK_LOCK_ASSERT(socket);
411 
412 	MAC_CHECK(check_socket_relabel, cred, socket, socket->so_label,
413 	    newlabel);
414 
415 	return (error);
416 }
417 
418 int
419 mac_check_socket_send(struct ucred *cred, struct socket *so)
420 {
421 	int error;
422 
423 	SOCK_LOCK_ASSERT(so);
424 
425 	if (!mac_enforce_socket)
426 		return (0);
427 
428 	MAC_CHECK(check_socket_send, cred, so, so->so_label);
429 
430 	return (error);
431 }
432 
433 int
434 mac_check_socket_stat(struct ucred *cred, struct socket *so)
435 {
436 	int error;
437 
438 	SOCK_LOCK_ASSERT(so);
439 
440 	if (!mac_enforce_socket)
441 		return (0);
442 
443 	MAC_CHECK(check_socket_stat, cred, so, so->so_label);
444 
445 	return (error);
446 }
447 
448 int
449 mac_check_socket_visible(struct ucred *cred, struct socket *socket)
450 {
451 	int error;
452 
453 	SOCK_LOCK_ASSERT(socket);
454 
455 	if (!mac_enforce_socket)
456 		return (0);
457 
458 	MAC_CHECK(check_socket_visible, cred, socket, socket->so_label);
459 
460 	return (error);
461 }
462 
463 int
464 mac_socket_label_set(struct ucred *cred, struct socket *so,
465     struct label *label)
466 {
467 	int error;
468 
469 	/*
470 	 * We acquire the socket lock when we perform the test and set,
471 	 * but have to release it as the pcb code needs to acquire the
472 	 * pcb lock, which will precede the socket lock in the lock
473 	 * order.  However, this is fine, as any race will simply
474 	 * result in the inpcb being refreshed twice, but still
475 	 * consistently, as the inpcb code will acquire the socket lock
476 	 * before refreshing, holding both locks.
477 	 */
478 	SOCK_LOCK(so);
479 	error = mac_check_socket_relabel(cred, so, label);
480 	if (error) {
481 		SOCK_UNLOCK(so);
482 		return (error);
483 	}
484 
485 	mac_relabel_socket(cred, so, label);
486 	SOCK_UNLOCK(so);
487 	/*
488 	 * If the protocol has expressed interest in socket layer changes,
489 	 * such as if it needs to propagate changes to a cached pcb
490 	 * label from the socket, notify it of the label change while
491 	 * holding the socket lock.
492 	 */
493 	if (so->so_proto->pr_usrreqs->pru_sosetlabel != NULL)
494 		(so->so_proto->pr_usrreqs->pru_sosetlabel)(so);
495 
496 	return (0);
497 }
498 
499 int
500 mac_setsockopt_label(struct ucred *cred, struct socket *so, struct mac *mac)
501 {
502 	struct label *intlabel;
503 	char *buffer;
504 	int error;
505 
506 	error = mac_check_structmac_consistent(mac);
507 	if (error)
508 		return (error);
509 
510 	buffer = malloc(mac->m_buflen, M_MACTEMP, M_WAITOK);
511 	error = copyinstr(mac->m_string, buffer, mac->m_buflen, NULL);
512 	if (error) {
513 		free(buffer, M_MACTEMP);
514 		return (error);
515 	}
516 
517 	intlabel = mac_socket_label_alloc(M_WAITOK);
518 	error = mac_internalize_socket_label(intlabel, buffer);
519 	free(buffer, M_MACTEMP);
520 	if (error)
521 		goto out;
522 
523 	error = mac_socket_label_set(cred, so, intlabel);
524 out:
525 	mac_socket_label_free(intlabel);
526 	return (error);
527 }
528 
529 int
530 mac_getsockopt_label(struct ucred *cred, struct socket *so, struct mac *mac)
531 {
532 	char *buffer, *elements;
533 	struct label *intlabel;
534 	int error;
535 
536 	error = mac_check_structmac_consistent(mac);
537 	if (error)
538 		return (error);
539 
540 	elements = malloc(mac->m_buflen, M_MACTEMP, M_WAITOK);
541 	error = copyinstr(mac->m_string, elements, mac->m_buflen, NULL);
542 	if (error) {
543 		free(elements, M_MACTEMP);
544 		return (error);
545 	}
546 
547 	buffer = malloc(mac->m_buflen, M_MACTEMP, M_WAITOK | M_ZERO);
548 	intlabel = mac_socket_label_alloc(M_WAITOK);
549 	SOCK_LOCK(so);
550 	mac_copy_socket_label(so->so_label, intlabel);
551 	SOCK_UNLOCK(so);
552 	error = mac_externalize_socket_label(intlabel, elements, buffer,
553 	    mac->m_buflen);
554 	mac_socket_label_free(intlabel);
555 	if (error == 0)
556 		error = copyout(buffer, mac->m_string, strlen(buffer)+1);
557 
558 	free(buffer, M_MACTEMP);
559 	free(elements, M_MACTEMP);
560 
561 	return (error);
562 }
563 
564 int
565 mac_getsockopt_peerlabel(struct ucred *cred, struct socket *so,
566     struct mac *mac)
567 {
568 	char *elements, *buffer;
569 	struct label *intlabel;
570 	int error;
571 
572 	error = mac_check_structmac_consistent(mac);
573 	if (error)
574 		return (error);
575 
576 	elements = malloc(mac->m_buflen, M_MACTEMP, M_WAITOK);
577 	error = copyinstr(mac->m_string, elements, mac->m_buflen, NULL);
578 	if (error) {
579 		free(elements, M_MACTEMP);
580 		return (error);
581 	}
582 
583 	buffer = malloc(mac->m_buflen, M_MACTEMP, M_WAITOK | M_ZERO);
584 	intlabel = mac_socket_label_alloc(M_WAITOK);
585 	SOCK_LOCK(so);
586 	mac_copy_socket_label(so->so_peerlabel, intlabel);
587 	SOCK_UNLOCK(so);
588 	error = mac_externalize_socket_peer_label(intlabel, elements, buffer,
589 	    mac->m_buflen);
590 	mac_socket_label_free(intlabel);
591 	if (error == 0)
592 		error = copyout(buffer, mac->m_string, strlen(buffer)+1);
593 
594 	free(buffer, M_MACTEMP);
595 	free(elements, M_MACTEMP);
596 
597 	return (error);
598 }
599