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