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