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