xref: /freebsd/sys/security/mac/mac_socket.c (revision 9dba3024c3f1a2df6f42689aac5a2ab4acc7561d)
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  * 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 McAfee
11  * Research, the Technology Research Division of Network Associates, Inc.
12  * under DARPA/SPAWAR contract N66001-01-C-8035 ("CBOSS"), as part of the
13  * 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$");
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  */
74 int	mac_enforce_socket = 1;
75 SYSCTL_INT(_security_mac, OID_AUTO, enforce_socket, CTLFLAG_RW,
76     &mac_enforce_socket, 0, "Enforce MAC policy on socket operations");
77 TUNABLE_INT("security.mac.enforce_socket", &mac_enforce_socket);
78 
79 #ifdef MAC_DEBUG
80 static unsigned int nmacsockets;
81 
82 SYSCTL_UINT(_security_mac_debug_counters, OID_AUTO, sockets, CTLFLAG_RD,
83     &nmacsockets, 0, "number of sockets in use");
84 #endif
85 
86 struct label *
87 mac_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 
106 static struct label *
107 mac_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 
126 int
127 mac_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 
142 void
143 mac_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 
151 static void
152 mac_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 
160 void
161 mac_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 
170 void
171 mac_copy_socket_label(struct label *src, struct label *dest)
172 {
173 
174 	MAC_PERFORM(copy_socket_label, src, dest);
175 }
176 
177 int
178 mac_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 
188 static int
189 mac_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 
199 int
200 mac_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 
209 void
210 mac_create_socket(struct ucred *cred, struct socket *socket)
211 {
212 
213 	MAC_PERFORM(create_socket, cred, socket, socket->so_label);
214 }
215 
216 void
217 mac_create_socket_from_socket(struct socket *oldsocket,
218     struct socket *newsocket)
219 {
220 
221 	SOCK_LOCK_ASSERT(oldsocket);
222 	MAC_PERFORM(create_socket_from_socket, oldsocket, oldsocket->so_label,
223 	    newsocket, newsocket->so_label);
224 }
225 
226 static void
227 mac_relabel_socket(struct ucred *cred, struct socket *socket,
228     struct label *newlabel)
229 {
230 
231 	SOCK_LOCK_ASSERT(socket);
232 	MAC_PERFORM(relabel_socket, cred, socket, socket->so_label, newlabel);
233 }
234 
235 void
236 mac_set_socket_peer_from_mbuf(struct mbuf *mbuf, struct socket *socket)
237 {
238 	struct label *label;
239 
240 	SOCK_LOCK_ASSERT(socket);
241 
242 	label = mac_mbuf_to_label(mbuf);
243 
244 	MAC_PERFORM(set_socket_peer_from_mbuf, mbuf, label, socket,
245 	    socket->so_peerlabel);
246 }
247 
248 void
249 mac_set_socket_peer_from_socket(struct socket *oldsocket,
250     struct socket *newsocket)
251 {
252 
253 	/*
254 	 * XXXRW: only hold the socket lock on one at a time, as one
255 	 * socket is the original, and one is the new.  However, it's
256 	 * called in both directions, so we can't assert the lock
257 	 * here currently.
258 	 */
259 	MAC_PERFORM(set_socket_peer_from_socket, oldsocket,
260 	    oldsocket->so_label, newsocket, newsocket->so_peerlabel);
261 }
262 
263 void
264 mac_create_mbuf_from_socket(struct socket *socket, struct mbuf *mbuf)
265 {
266 	struct label *label;
267 
268 	label = mac_mbuf_to_label(mbuf);
269 
270 	SOCK_LOCK_ASSERT(socket);
271 	MAC_PERFORM(create_mbuf_from_socket, socket, socket->so_label, mbuf,
272 	    label);
273 }
274 
275 int
276 mac_check_socket_accept(struct ucred *cred, struct socket *socket)
277 {
278 	int error;
279 
280 	SOCK_LOCK_ASSERT(socket);
281 
282 	if (!mac_enforce_socket)
283 		return (0);
284 
285 	MAC_CHECK(check_socket_accept, cred, socket, socket->so_label);
286 
287 	return (error);
288 }
289 
290 int
291 mac_check_socket_bind(struct ucred *ucred, struct socket *socket,
292     struct sockaddr *sockaddr)
293 {
294 	int error;
295 
296 	SOCK_LOCK_ASSERT(socket);
297 
298 	if (!mac_enforce_socket)
299 		return (0);
300 
301 	MAC_CHECK(check_socket_bind, ucred, socket, socket->so_label,
302 	    sockaddr);
303 
304 	return (error);
305 }
306 
307 int
308 mac_check_socket_connect(struct ucred *cred, struct socket *socket,
309     struct sockaddr *sockaddr)
310 {
311 	int error;
312 
313 	SOCK_LOCK_ASSERT(socket);
314 
315 	if (!mac_enforce_socket)
316 		return (0);
317 
318 	MAC_CHECK(check_socket_connect, cred, socket, socket->so_label,
319 	    sockaddr);
320 
321 	return (error);
322 }
323 
324 int
325 mac_check_socket_deliver(struct socket *socket, struct mbuf *mbuf)
326 {
327 	struct label *label;
328 	int error;
329 
330 	SOCK_LOCK_ASSERT(socket);
331 
332 	if (!mac_enforce_socket)
333 		return (0);
334 
335 	label = mac_mbuf_to_label(mbuf);
336 
337 	MAC_CHECK(check_socket_deliver, socket, socket->so_label, mbuf,
338 	    label);
339 
340 	return (error);
341 }
342 
343 int
344 mac_check_socket_listen(struct ucred *cred, struct socket *socket)
345 {
346 	int error;
347 
348 	SOCK_LOCK_ASSERT(socket);
349 
350 	if (!mac_enforce_socket)
351 		return (0);
352 
353 	MAC_CHECK(check_socket_listen, cred, socket, socket->so_label);
354 	return (error);
355 }
356 
357 int
358 mac_check_socket_poll(struct ucred *cred, struct socket *so)
359 {
360 	int error;
361 
362 	SOCK_LOCK_ASSERT(so);
363 
364 	if (!mac_enforce_socket)
365 		return (0);
366 
367 	MAC_CHECK(check_socket_poll, cred, so, so->so_label);
368 	return (error);
369 }
370 
371 int
372 mac_check_socket_receive(struct ucred *cred, struct socket *so)
373 {
374 	int error;
375 
376 	SOCK_LOCK_ASSERT(so);
377 
378 	if (!mac_enforce_socket)
379 		return (0);
380 
381 	MAC_CHECK(check_socket_receive, cred, so, so->so_label);
382 
383 	return (error);
384 }
385 
386 static int
387 mac_check_socket_relabel(struct ucred *cred, struct socket *socket,
388     struct label *newlabel)
389 {
390 	int error;
391 
392 	SOCK_LOCK_ASSERT(socket);
393 
394 	MAC_CHECK(check_socket_relabel, cred, socket, socket->so_label,
395 	    newlabel);
396 
397 	return (error);
398 }
399 
400 int
401 mac_check_socket_send(struct ucred *cred, struct socket *so)
402 {
403 	int error;
404 
405 	SOCK_LOCK_ASSERT(so);
406 
407 	if (!mac_enforce_socket)
408 		return (0);
409 
410 	MAC_CHECK(check_socket_send, cred, so, so->so_label);
411 
412 	return (error);
413 }
414 
415 int
416 mac_check_socket_stat(struct ucred *cred, struct socket *so)
417 {
418 	int error;
419 
420 	SOCK_LOCK_ASSERT(so);
421 
422 	if (!mac_enforce_socket)
423 		return (0);
424 
425 	MAC_CHECK(check_socket_stat, cred, so, so->so_label);
426 
427 	return (error);
428 }
429 
430 int
431 mac_check_socket_visible(struct ucred *cred, struct socket *socket)
432 {
433 	int error;
434 
435 	SOCK_LOCK_ASSERT(socket);
436 
437 	if (!mac_enforce_socket)
438 		return (0);
439 
440 	MAC_CHECK(check_socket_visible, cred, socket, socket->so_label);
441 
442 	return (error);
443 }
444 
445 int
446 mac_socket_label_set(struct ucred *cred, struct socket *so,
447     struct label *label)
448 {
449 	int error;
450 
451 	/*
452 	 * We acquire the socket lock when we perform the test and set,
453 	 * but have to release it as the pcb code needs to acquire the
454 	 * pcb lock, which will precede the socket lock in the lock
455 	 * order.  However, this is fine, as any race will simply
456 	 * result in the inpcb being refreshed twice, but still
457 	 * consistently, as the inpcb code will acquire the socket lock
458 	 * before refreshing, holding both locks.
459 	 */
460 	SOCK_LOCK(so);
461 	error = mac_check_socket_relabel(cred, so, label);
462 	if (error) {
463 		SOCK_UNLOCK(so);
464 		return (error);
465 	}
466 
467 	mac_relabel_socket(cred, so, label);
468 	SOCK_UNLOCK(so);
469 	/*
470 	 * If the protocol has expressed interest in socket layer changes,
471 	 * such as if it needs to propagate changes to a cached pcb
472 	 * label from the socket, notify it of the label change while
473 	 * holding the socket lock.
474 	 */
475 	if (so->so_proto->pr_usrreqs->pru_sosetlabel != NULL)
476 		(so->so_proto->pr_usrreqs->pru_sosetlabel)(so);
477 
478 	return (0);
479 }
480 
481 int
482 mac_setsockopt_label(struct ucred *cred, struct socket *so, struct mac *mac)
483 {
484 	struct label *intlabel;
485 	char *buffer;
486 	int error;
487 
488 	error = mac_check_structmac_consistent(mac);
489 	if (error)
490 		return (error);
491 
492 	buffer = malloc(mac->m_buflen, M_MACTEMP, M_WAITOK);
493 	error = copyinstr(mac->m_string, buffer, mac->m_buflen, NULL);
494 	if (error) {
495 		free(buffer, M_MACTEMP);
496 		return (error);
497 	}
498 
499 	intlabel = mac_socket_label_alloc(M_WAITOK);
500 	error = mac_internalize_socket_label(intlabel, buffer);
501 	free(buffer, M_MACTEMP);
502 	if (error)
503 		goto out;
504 
505 	error = mac_socket_label_set(cred, so, intlabel);
506 out:
507 	mac_socket_label_free(intlabel);
508 	return (error);
509 }
510 
511 int
512 mac_getsockopt_label(struct ucred *cred, struct socket *so, struct mac *mac)
513 {
514 	char *buffer, *elements;
515 	struct label *intlabel;
516 	int error;
517 
518 	error = mac_check_structmac_consistent(mac);
519 	if (error)
520 		return (error);
521 
522 	elements = malloc(mac->m_buflen, M_MACTEMP, M_WAITOK);
523 	error = copyinstr(mac->m_string, elements, mac->m_buflen, NULL);
524 	if (error) {
525 		free(elements, M_MACTEMP);
526 		return (error);
527 	}
528 
529 	buffer = malloc(mac->m_buflen, M_MACTEMP, M_WAITOK | M_ZERO);
530 	intlabel = mac_socket_label_alloc(M_WAITOK);
531 	SOCK_LOCK(so);
532 	mac_copy_socket_label(so->so_label, intlabel);
533 	SOCK_UNLOCK(so);
534 	error = mac_externalize_socket_label(intlabel, elements, buffer,
535 	    mac->m_buflen);
536 	mac_socket_label_free(intlabel);
537 	if (error == 0)
538 		error = copyout(buffer, mac->m_string, strlen(buffer)+1);
539 
540 	free(buffer, M_MACTEMP);
541 	free(elements, M_MACTEMP);
542 
543 	return (error);
544 }
545 
546 int
547 mac_getsockopt_peerlabel(struct ucred *cred, struct socket *so,
548     struct mac *mac)
549 {
550 	char *elements, *buffer;
551 	struct label *intlabel;
552 	int error;
553 
554 	error = mac_check_structmac_consistent(mac);
555 	if (error)
556 		return (error);
557 
558 	elements = malloc(mac->m_buflen, M_MACTEMP, M_WAITOK);
559 	error = copyinstr(mac->m_string, elements, mac->m_buflen, NULL);
560 	if (error) {
561 		free(elements, M_MACTEMP);
562 		return (error);
563 	}
564 
565 	buffer = malloc(mac->m_buflen, M_MACTEMP, M_WAITOK | M_ZERO);
566 	intlabel = mac_socket_label_alloc(M_WAITOK);
567 	SOCK_LOCK(so);
568 	mac_copy_socket_label(so->so_peerlabel, intlabel);
569 	SOCK_UNLOCK(so);
570 	error = mac_externalize_socket_peer_label(intlabel, elements, buffer,
571 	    mac->m_buflen);
572 	mac_socket_label_free(intlabel);
573 	if (error == 0)
574 		error = copyout(buffer, mac->m_string, strlen(buffer)+1);
575 
576 	free(buffer, M_MACTEMP);
577 	free(elements, M_MACTEMP);
578 
579 	return (error);
580 }
581