xref: /freebsd/sys/security/mac/mac_socket.c (revision 6b3455a7665208c366849f0b2b3bc916fb97516e)
1 /*-
2  * Copyright (c) 1999-2002 Robert N. M. Watson
3  * Copyright (c) 2001 Ilmar S. Habibulin
4  * Copyright (c) 2001-2004 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 Network
11  * Associates Laboratories, the Security Research Division of Network
12  * Associates, Inc. under DARPA/SPAWAR contract N66001-01-C-8035 ("CBOSS"),
13  * as part of the 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_bind(struct ucred *ucred, struct socket *socket,
277     struct sockaddr *sockaddr)
278 {
279 	int error;
280 
281 	SOCK_LOCK_ASSERT(socket);
282 
283 	if (!mac_enforce_socket)
284 		return (0);
285 
286 	MAC_CHECK(check_socket_bind, ucred, socket, socket->so_label,
287 	    sockaddr);
288 
289 	return (error);
290 }
291 
292 int
293 mac_check_socket_connect(struct ucred *cred, struct socket *socket,
294     struct sockaddr *sockaddr)
295 {
296 	int error;
297 
298 	SOCK_LOCK_ASSERT(socket);
299 
300 	if (!mac_enforce_socket)
301 		return (0);
302 
303 	MAC_CHECK(check_socket_connect, cred, socket, socket->so_label,
304 	    sockaddr);
305 
306 	return (error);
307 }
308 
309 int
310 mac_check_socket_deliver(struct socket *socket, struct mbuf *mbuf)
311 {
312 	struct label *label;
313 	int error;
314 
315 	SOCK_LOCK_ASSERT(socket);
316 
317 	if (!mac_enforce_socket)
318 		return (0);
319 
320 	label = mac_mbuf_to_label(mbuf);
321 
322 	MAC_CHECK(check_socket_deliver, socket, socket->so_label, mbuf,
323 	    label);
324 
325 	return (error);
326 }
327 
328 int
329 mac_check_socket_listen(struct ucred *cred, struct socket *socket)
330 {
331 	int error;
332 
333 	SOCK_LOCK_ASSERT(socket);
334 
335 	if (!mac_enforce_socket)
336 		return (0);
337 
338 	MAC_CHECK(check_socket_listen, cred, socket, socket->so_label);
339 	return (error);
340 }
341 
342 int
343 mac_check_socket_receive(struct ucred *cred, struct socket *so)
344 {
345 	int error;
346 
347 	SOCK_LOCK_ASSERT(so);
348 
349 	if (!mac_enforce_socket)
350 		return (0);
351 
352 	MAC_CHECK(check_socket_receive, cred, so, so->so_label);
353 
354 	return (error);
355 }
356 
357 static int
358 mac_check_socket_relabel(struct ucred *cred, struct socket *socket,
359     struct label *newlabel)
360 {
361 	int error;
362 
363 	SOCK_LOCK_ASSERT(socket);
364 
365 	MAC_CHECK(check_socket_relabel, cred, socket, socket->so_label,
366 	    newlabel);
367 
368 	return (error);
369 }
370 
371 int
372 mac_check_socket_send(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_send, cred, so, so->so_label);
382 
383 	return (error);
384 }
385 
386 int
387 mac_check_socket_visible(struct ucred *cred, struct socket *socket)
388 {
389 	int error;
390 
391 	SOCK_LOCK_ASSERT(socket);
392 
393 	if (!mac_enforce_socket)
394 		return (0);
395 
396 	MAC_CHECK(check_socket_visible, cred, socket, socket->so_label);
397 
398 	return (error);
399 }
400 
401 int
402 mac_socket_label_set(struct ucred *cred, struct socket *so,
403     struct label *label)
404 {
405 	int error;
406 
407 	/*
408 	 * We acquire the socket lock when we perform the test and set,
409 	 * but have to release it as the pcb code needs to acquire the
410 	 * pcb lock, which will precede the socket lock in the lock
411 	 * order.  However, this is fine, as any race will simply
412 	 * result in the inpcb being refreshed twice, but still
413 	 * consistently, as the inpcb code will acquire the socket lock
414 	 * before refreshing, holding both locks.
415 	 */
416 	SOCK_LOCK(so);
417 	error = mac_check_socket_relabel(cred, so, label);
418 	if (error) {
419 		SOCK_UNLOCK(so);
420 		return (error);
421 	}
422 
423 	mac_relabel_socket(cred, so, label);
424 	SOCK_UNLOCK(so);
425 	/*
426 	 * If the protocol has expressed interest in socket layer changes,
427 	 * such as if it needs to propagate changes to a cached pcb
428 	 * label from the socket, notify it of the label change while
429 	 * holding the socket lock.
430 	 */
431 	if (so->so_proto->pr_usrreqs->pru_sosetlabel != NULL)
432 		(so->so_proto->pr_usrreqs->pru_sosetlabel)(so);
433 
434 	return (0);
435 }
436 
437 int
438 mac_setsockopt_label(struct ucred *cred, struct socket *so, struct mac *mac)
439 {
440 	struct label *intlabel;
441 	char *buffer;
442 	int error;
443 
444 	error = mac_check_structmac_consistent(mac);
445 	if (error)
446 		return (error);
447 
448 	buffer = malloc(mac->m_buflen, M_MACTEMP, M_WAITOK);
449 	error = copyinstr(mac->m_string, buffer, mac->m_buflen, NULL);
450 	if (error) {
451 		free(buffer, M_MACTEMP);
452 		return (error);
453 	}
454 
455 	intlabel = mac_socket_label_alloc(M_WAITOK);
456 	error = mac_internalize_socket_label(intlabel, buffer);
457 	free(buffer, M_MACTEMP);
458 	if (error)
459 		goto out;
460 
461 	error = mac_socket_label_set(cred, so, intlabel);
462 out:
463 	mac_socket_label_free(intlabel);
464 	return (error);
465 }
466 
467 int
468 mac_getsockopt_label(struct ucred *cred, struct socket *so, struct mac *mac)
469 {
470 	char *buffer, *elements;
471 	struct label *intlabel;
472 	int error;
473 
474 	error = mac_check_structmac_consistent(mac);
475 	if (error)
476 		return (error);
477 
478 	elements = malloc(mac->m_buflen, M_MACTEMP, M_WAITOK);
479 	error = copyinstr(mac->m_string, elements, mac->m_buflen, NULL);
480 	if (error) {
481 		free(elements, M_MACTEMP);
482 		return (error);
483 	}
484 
485 	buffer = malloc(mac->m_buflen, M_MACTEMP, M_WAITOK | M_ZERO);
486 	intlabel = mac_socket_label_alloc(M_WAITOK);
487 	SOCK_LOCK(so);
488 	mac_copy_socket_label(so->so_label, intlabel);
489 	SOCK_UNLOCK(so);
490 	error = mac_externalize_socket_label(intlabel, elements, buffer,
491 	    mac->m_buflen);
492 	mac_socket_label_free(intlabel);
493 	if (error == 0)
494 		error = copyout(buffer, mac->m_string, strlen(buffer)+1);
495 
496 	free(buffer, M_MACTEMP);
497 	free(elements, M_MACTEMP);
498 
499 	return (error);
500 }
501 
502 int
503 mac_getsockopt_peerlabel(struct ucred *cred, struct socket *so,
504     struct mac *mac)
505 {
506 	char *elements, *buffer;
507 	struct label *intlabel;
508 	int error;
509 
510 	error = mac_check_structmac_consistent(mac);
511 	if (error)
512 		return (error);
513 
514 	elements = malloc(mac->m_buflen, M_MACTEMP, M_WAITOK);
515 	error = copyinstr(mac->m_string, elements, mac->m_buflen, NULL);
516 	if (error) {
517 		free(elements, M_MACTEMP);
518 		return (error);
519 	}
520 
521 	buffer = malloc(mac->m_buflen, M_MACTEMP, M_WAITOK | M_ZERO);
522 	intlabel = mac_socket_label_alloc(M_WAITOK);
523 	SOCK_LOCK(so);
524 	mac_copy_socket_label(so->so_peerlabel, intlabel);
525 	SOCK_UNLOCK(so);
526 	error = mac_externalize_socket_peer_label(intlabel, elements, buffer,
527 	    mac->m_buflen);
528 	mac_socket_label_free(intlabel);
529 	if (error == 0)
530 		error = copyout(buffer, mac->m_string, strlen(buffer)+1);
531 
532 	free(buffer, M_MACTEMP);
533 	free(elements, M_MACTEMP);
534 
535 	return (error);
536 }
537