xref: /freebsd/sys/security/mac/mac_socket.c (revision 1e413cf93298b5b97441a21d9a50fdcd0ee9945e)
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  * 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(socket_init_label, label, flag);
98 	if (error) {
99 		MAC_PERFORM(socket_destroy_label, label);
100 		mac_labelzone_free(label);
101 		return (NULL);
102 	}
103 	return (label);
104 }
105 
106 static struct label *
107 mac_socketpeer_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(socketpeer_init_label, label, flag);
117 	if (error) {
118 		MAC_PERFORM(socketpeer_destroy_label, label);
119 		mac_labelzone_free(label);
120 		return (NULL);
121 	}
122 	return (label);
123 }
124 
125 int
126 mac_socket_init(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_socketpeer_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(socket_destroy_label, label);
146 	mac_labelzone_free(label);
147 }
148 
149 static void
150 mac_socketpeer_label_free(struct label *label)
151 {
152 
153 	MAC_PERFORM(socketpeer_destroy_label, label);
154 	mac_labelzone_free(label);
155 }
156 
157 void
158 mac_socket_destroy(struct socket *so)
159 {
160 
161 	mac_socket_label_free(so->so_label);
162 	so->so_label = NULL;
163 	mac_socketpeer_label_free(so->so_peerlabel);
164 	so->so_peerlabel = NULL;
165 }
166 
167 void
168 mac_socket_copy_label(struct label *src, struct label *dest)
169 {
170 
171 	MAC_PERFORM(socket_copy_label, src, dest);
172 }
173 
174 int
175 mac_socket_externalize_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_socketpeer_externalize_label(struct label *label, char *elements,
187     char *outbuf, size_t outbuflen)
188 {
189 	int error;
190 
191 	MAC_EXTERNALIZE(socketpeer, label, elements, outbuf, outbuflen);
192 
193 	return (error);
194 }
195 
196 int
197 mac_socket_internalize_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_socket_create(struct ucred *cred, struct socket *so)
208 {
209 
210 	MAC_PERFORM(socket_create, cred, so, so->so_label);
211 }
212 
213 void
214 mac_socket_newconn(struct socket *oldso, struct socket *newso)
215 {
216 
217 	SOCK_LOCK_ASSERT(oldso);
218 
219 	MAC_PERFORM(socket_newconn, oldso, oldso->so_label, newso,
220 	    newso->so_label);
221 }
222 
223 static void
224 mac_socket_relabel(struct ucred *cred, struct socket *so,
225     struct label *newlabel)
226 {
227 
228 	SOCK_LOCK_ASSERT(so);
229 
230 	MAC_PERFORM(socket_relabel, cred, so, so->so_label, newlabel);
231 }
232 
233 void
234 mac_socketpeer_set_from_mbuf(struct mbuf *m, struct socket *so)
235 {
236 	struct label *label;
237 
238 	SOCK_LOCK_ASSERT(so);
239 
240 	label = mac_mbuf_to_label(m);
241 
242 	MAC_PERFORM(socketpeer_set_from_mbuf, m, label, so,
243 	    so->so_peerlabel);
244 }
245 
246 void
247 mac_socketpeer_set_from_socket(struct socket *oldso, struct socket *newso)
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(socketpeer_set_from_socket, oldso, oldso->so_label,
256 	    newso, newso->so_peerlabel);
257 }
258 
259 void
260 mac_socket_create_mbuf(struct socket *so, struct mbuf *m)
261 {
262 	struct label *label;
263 
264 	SOCK_LOCK_ASSERT(so);
265 
266 	label = mac_mbuf_to_label(m);
267 
268 	MAC_PERFORM(socket_create_mbuf, so, so->so_label, m, label);
269 }
270 
271 int
272 mac_socket_check_accept(struct ucred *cred, struct socket *so)
273 {
274 	int error;
275 
276 	SOCK_LOCK_ASSERT(so);
277 
278 	MAC_CHECK(socket_check_accept, cred, so, so->so_label);
279 
280 	return (error);
281 }
282 
283 int
284 mac_socket_check_bind(struct ucred *ucred, struct socket *so,
285     struct sockaddr *sa)
286 {
287 	int error;
288 
289 	SOCK_LOCK_ASSERT(so);
290 
291 	MAC_CHECK(socket_check_bind, ucred, so, so->so_label, sa);
292 
293 	return (error);
294 }
295 
296 int
297 mac_socket_check_connect(struct ucred *cred, struct socket *so,
298     struct sockaddr *sa)
299 {
300 	int error;
301 
302 	SOCK_LOCK_ASSERT(so);
303 
304 	MAC_CHECK(socket_check_connect, cred, so, so->so_label, sa);
305 
306 	return (error);
307 }
308 
309 int
310 mac_socket_check_create(struct ucred *cred, int domain, int type, int proto)
311 {
312 	int error;
313 
314 	MAC_CHECK(socket_check_create, cred, domain, type, proto);
315 
316 	return (error);
317 }
318 
319 int
320 mac_socket_check_deliver(struct socket *so, struct mbuf *m)
321 {
322 	struct label *label;
323 	int error;
324 
325 	SOCK_LOCK_ASSERT(so);
326 
327 	label = mac_mbuf_to_label(m);
328 
329 	MAC_CHECK(socket_check_deliver, so, so->so_label, m, label);
330 
331 	return (error);
332 }
333 
334 int
335 mac_socket_check_listen(struct ucred *cred, struct socket *so)
336 {
337 	int error;
338 
339 	SOCK_LOCK_ASSERT(so);
340 
341 	MAC_CHECK(socket_check_listen, cred, so, so->so_label);
342 
343 	return (error);
344 }
345 
346 int
347 mac_socket_check_poll(struct ucred *cred, struct socket *so)
348 {
349 	int error;
350 
351 	SOCK_LOCK_ASSERT(so);
352 
353 	MAC_CHECK(socket_check_poll, cred, so, so->so_label);
354 
355 	return (error);
356 }
357 
358 int
359 mac_socket_check_receive(struct ucred *cred, struct socket *so)
360 {
361 	int error;
362 
363 	SOCK_LOCK_ASSERT(so);
364 
365 	MAC_CHECK(socket_check_receive, cred, so, so->so_label);
366 
367 	return (error);
368 }
369 
370 static int
371 mac_socket_check_relabel(struct ucred *cred, struct socket *so,
372     struct label *newlabel)
373 {
374 	int error;
375 
376 	SOCK_LOCK_ASSERT(so);
377 
378 	MAC_CHECK(socket_check_relabel, cred, so, so->so_label, newlabel);
379 
380 	return (error);
381 }
382 
383 int
384 mac_socket_check_send(struct ucred *cred, struct socket *so)
385 {
386 	int error;
387 
388 	SOCK_LOCK_ASSERT(so);
389 
390 	MAC_CHECK(socket_check_send, cred, so, so->so_label);
391 
392 	return (error);
393 }
394 
395 int
396 mac_socket_check_stat(struct ucred *cred, struct socket *so)
397 {
398 	int error;
399 
400 	SOCK_LOCK_ASSERT(so);
401 
402 	MAC_CHECK(socket_check_stat, cred, so, so->so_label);
403 
404 	return (error);
405 }
406 
407 int
408 mac_socket_check_visible(struct ucred *cred, struct socket *so)
409 {
410 	int error;
411 
412 	SOCK_LOCK_ASSERT(so);
413 
414 	MAC_CHECK(socket_check_visible, cred, so, so->so_label);
415 
416 	return (error);
417 }
418 
419 int
420 mac_socket_label_set(struct ucred *cred, struct socket *so,
421     struct label *label)
422 {
423 	int error;
424 
425 	/*
426 	 * We acquire the socket lock when we perform the test and set, but
427 	 * have to release it as the pcb code needs to acquire the pcb lock,
428 	 * which will precede the socket lock in the lock order.  However,
429 	 * this is fine, as any race will simply result in the inpcb being
430 	 * refreshed twice, but still consistently, as the inpcb code will
431 	 * acquire the socket lock before refreshing, holding both locks.
432 	 */
433 	SOCK_LOCK(so);
434 	error = mac_socket_check_relabel(cred, so, label);
435 	if (error) {
436 		SOCK_UNLOCK(so);
437 		return (error);
438 	}
439 
440 	mac_socket_relabel(cred, so, label);
441 	SOCK_UNLOCK(so);
442 
443 	/*
444 	 * If the protocol has expressed interest in socket layer changes,
445 	 * such as if it needs to propagate changes to a cached pcb label
446 	 * from the socket, notify it of the label change while holding the
447 	 * socket lock.
448 	 */
449 	if (so->so_proto->pr_usrreqs->pru_sosetlabel != NULL)
450 		(so->so_proto->pr_usrreqs->pru_sosetlabel)(so);
451 
452 	return (0);
453 }
454 
455 int
456 mac_setsockopt_label(struct ucred *cred, struct socket *so, struct mac *mac)
457 {
458 	struct label *intlabel;
459 	char *buffer;
460 	int error;
461 
462 	error = mac_check_structmac_consistent(mac);
463 	if (error)
464 		return (error);
465 
466 	buffer = malloc(mac->m_buflen, M_MACTEMP, M_WAITOK);
467 	error = copyinstr(mac->m_string, buffer, mac->m_buflen, NULL);
468 	if (error) {
469 		free(buffer, M_MACTEMP);
470 		return (error);
471 	}
472 
473 	intlabel = mac_socket_label_alloc(M_WAITOK);
474 	error = mac_socket_internalize_label(intlabel, buffer);
475 	free(buffer, M_MACTEMP);
476 	if (error)
477 		goto out;
478 
479 	error = mac_socket_label_set(cred, so, intlabel);
480 out:
481 	mac_socket_label_free(intlabel);
482 	return (error);
483 }
484 
485 int
486 mac_getsockopt_label(struct ucred *cred, struct socket *so, struct mac *mac)
487 {
488 	char *buffer, *elements;
489 	struct label *intlabel;
490 	int error;
491 
492 	error = mac_check_structmac_consistent(mac);
493 	if (error)
494 		return (error);
495 
496 	elements = malloc(mac->m_buflen, M_MACTEMP, M_WAITOK);
497 	error = copyinstr(mac->m_string, elements, mac->m_buflen, NULL);
498 	if (error) {
499 		free(elements, M_MACTEMP);
500 		return (error);
501 	}
502 
503 	buffer = malloc(mac->m_buflen, M_MACTEMP, M_WAITOK | M_ZERO);
504 	intlabel = mac_socket_label_alloc(M_WAITOK);
505 	SOCK_LOCK(so);
506 	mac_socket_copy_label(so->so_label, intlabel);
507 	SOCK_UNLOCK(so);
508 	error = mac_socket_externalize_label(intlabel, elements, buffer,
509 	    mac->m_buflen);
510 	mac_socket_label_free(intlabel);
511 	if (error == 0)
512 		error = copyout(buffer, mac->m_string, strlen(buffer)+1);
513 
514 	free(buffer, M_MACTEMP);
515 	free(elements, M_MACTEMP);
516 
517 	return (error);
518 }
519 
520 int
521 mac_getsockopt_peerlabel(struct ucred *cred, struct socket *so,
522     struct mac *mac)
523 {
524 	char *elements, *buffer;
525 	struct label *intlabel;
526 	int error;
527 
528 	error = mac_check_structmac_consistent(mac);
529 	if (error)
530 		return (error);
531 
532 	elements = malloc(mac->m_buflen, M_MACTEMP, M_WAITOK);
533 	error = copyinstr(mac->m_string, elements, mac->m_buflen, NULL);
534 	if (error) {
535 		free(elements, M_MACTEMP);
536 		return (error);
537 	}
538 
539 	buffer = malloc(mac->m_buflen, M_MACTEMP, M_WAITOK | M_ZERO);
540 	intlabel = mac_socket_label_alloc(M_WAITOK);
541 	SOCK_LOCK(so);
542 	mac_socket_copy_label(so->so_peerlabel, intlabel);
543 	SOCK_UNLOCK(so);
544 	error = mac_socketpeer_externalize_label(intlabel, elements, buffer,
545 	    mac->m_buflen);
546 	mac_socket_label_free(intlabel);
547 	if (error == 0)
548 		error = copyout(buffer, mac->m_string, strlen(buffer)+1);
549 
550 	free(buffer, M_MACTEMP);
551 	free(elements, M_MACTEMP);
552 
553 	return (error);
554 }
555