xref: /freebsd/sys/security/mac/mac_socket.c (revision 2357939bc239bd5334a169b62313806178dd8f30)
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 	MAC_PERFORM(create_socket_from_socket, oldsocket, oldsocket->so_label,
222 	    newsocket, newsocket->so_label);
223 }
224 
225 static void
226 mac_relabel_socket(struct ucred *cred, struct socket *socket,
227     struct label *newlabel)
228 {
229 
230 	MAC_PERFORM(relabel_socket, cred, socket, socket->so_label, newlabel);
231 }
232 
233 void
234 mac_set_socket_peer_from_mbuf(struct mbuf *mbuf, struct socket *socket)
235 {
236 	struct label *label;
237 
238 	label = mac_mbuf_to_label(mbuf);
239 
240 	MAC_PERFORM(set_socket_peer_from_mbuf, mbuf, label, socket,
241 	    socket->so_peerlabel);
242 }
243 
244 void
245 mac_set_socket_peer_from_socket(struct socket *oldsocket,
246     struct socket *newsocket)
247 {
248 
249 	MAC_PERFORM(set_socket_peer_from_socket, oldsocket,
250 	    oldsocket->so_label, newsocket, newsocket->so_peerlabel);
251 }
252 
253 void
254 mac_create_mbuf_from_socket(struct socket *socket, struct mbuf *mbuf)
255 {
256 	struct label *label;
257 
258 	label = mac_mbuf_to_label(mbuf);
259 
260 	MAC_PERFORM(create_mbuf_from_socket, socket, socket->so_label, mbuf,
261 	    label);
262 }
263 
264 int
265 mac_check_socket_bind(struct ucred *ucred, struct socket *socket,
266     struct sockaddr *sockaddr)
267 {
268 	int error;
269 
270 	if (!mac_enforce_socket)
271 		return (0);
272 
273 	MAC_CHECK(check_socket_bind, ucred, socket, socket->so_label,
274 	    sockaddr);
275 
276 	return (error);
277 }
278 
279 int
280 mac_check_socket_connect(struct ucred *cred, struct socket *socket,
281     struct sockaddr *sockaddr)
282 {
283 	int error;
284 
285 	if (!mac_enforce_socket)
286 		return (0);
287 
288 	MAC_CHECK(check_socket_connect, cred, socket, socket->so_label,
289 	    sockaddr);
290 
291 	return (error);
292 }
293 
294 int
295 mac_check_socket_deliver(struct socket *socket, struct mbuf *mbuf)
296 {
297 	struct label *label;
298 	int error;
299 
300 	if (!mac_enforce_socket)
301 		return (0);
302 
303 	label = mac_mbuf_to_label(mbuf);
304 
305 	MAC_CHECK(check_socket_deliver, socket, socket->so_label, mbuf,
306 	    label);
307 
308 	return (error);
309 }
310 
311 int
312 mac_check_socket_listen(struct ucred *cred, struct socket *socket)
313 {
314 	int error;
315 
316 	if (!mac_enforce_socket)
317 		return (0);
318 
319 	MAC_CHECK(check_socket_listen, cred, socket, socket->so_label);
320 	return (error);
321 }
322 
323 int
324 mac_check_socket_receive(struct ucred *cred, struct socket *so)
325 {
326 	int error;
327 
328 	if (!mac_enforce_socket)
329 		return (0);
330 
331 	MAC_CHECK(check_socket_receive, cred, so, so->so_label);
332 
333 	return (error);
334 }
335 
336 static int
337 mac_check_socket_relabel(struct ucred *cred, struct socket *socket,
338     struct label *newlabel)
339 {
340 	int error;
341 
342 	MAC_CHECK(check_socket_relabel, cred, socket, socket->so_label,
343 	    newlabel);
344 
345 	return (error);
346 }
347 
348 int
349 mac_check_socket_send(struct ucred *cred, struct socket *so)
350 {
351 	int error;
352 
353 	if (!mac_enforce_socket)
354 		return (0);
355 
356 	MAC_CHECK(check_socket_send, cred, so, so->so_label);
357 
358 	return (error);
359 }
360 
361 int
362 mac_check_socket_visible(struct ucred *cred, struct socket *socket)
363 {
364 	int error;
365 
366 	if (!mac_enforce_socket)
367 		return (0);
368 
369 	MAC_CHECK(check_socket_visible, cred, socket, socket->so_label);
370 
371 	return (error);
372 }
373 
374 int
375 mac_socket_label_set(struct ucred *cred, struct socket *so,
376     struct label *label)
377 {
378 	int error;
379 
380 	error = mac_check_socket_relabel(cred, so, label);
381 	if (error)
382 		return (error);
383 
384 	mac_relabel_socket(cred, so, label);
385 
386 	/*
387 	 * If the protocol has expressed interest in socket layer changes,
388 	 * such as if it needs to propagate changes to a cached pcb
389 	 * label from the socket, notify it of the label change while
390 	 * holding the socket lock.
391 	 */
392 	if (so->so_proto->pr_usrreqs->pru_sosetlabel != NULL)
393 		(so->so_proto->pr_usrreqs->pru_sosetlabel)(so);
394 
395 	return (0);
396 }
397 
398 int
399 mac_setsockopt_label(struct ucred *cred, struct socket *so, struct mac *mac)
400 {
401 	struct label *intlabel;
402 	char *buffer;
403 	int error;
404 
405 	error = mac_check_structmac_consistent(mac);
406 	if (error)
407 		return (error);
408 
409 	buffer = malloc(mac->m_buflen, M_MACTEMP, M_WAITOK);
410 	error = copyinstr(mac->m_string, buffer, mac->m_buflen, NULL);
411 	if (error) {
412 		free(buffer, M_MACTEMP);
413 		return (error);
414 	}
415 
416 	intlabel = mac_socket_label_alloc(M_WAITOK);
417 	error = mac_internalize_socket_label(intlabel, buffer);
418 	free(buffer, M_MACTEMP);
419 	if (error)
420 		goto out;
421 
422 	/* XXX: Socket lock here. */
423 	error = mac_socket_label_set(cred, so, intlabel);
424 	/* XXX: Socket unlock here. */
425 out:
426 	mac_socket_label_free(intlabel);
427 	return (error);
428 }
429 
430 int
431 mac_getsockopt_label(struct ucred *cred, struct socket *so, struct mac *mac)
432 {
433 	char *buffer, *elements;
434 	int error;
435 
436 	error = mac_check_structmac_consistent(mac);
437 	if (error)
438 		return (error);
439 
440 	elements = malloc(mac->m_buflen, M_MACTEMP, M_WAITOK);
441 	error = copyinstr(mac->m_string, elements, mac->m_buflen, NULL);
442 	if (error) {
443 		free(elements, M_MACTEMP);
444 		return (error);
445 	}
446 
447 	buffer = malloc(mac->m_buflen, M_MACTEMP, M_WAITOK | M_ZERO);
448 	error = mac_externalize_socket_label(so->so_label, elements,
449 	    buffer, mac->m_buflen);
450 	if (error == 0)
451 		error = copyout(buffer, mac->m_string, strlen(buffer)+1);
452 
453 	free(buffer, M_MACTEMP);
454 	free(elements, M_MACTEMP);
455 
456 	return (error);
457 }
458 
459 int
460 mac_getsockopt_peerlabel(struct ucred *cred, struct socket *so,
461     struct mac *mac)
462 {
463 	char *elements, *buffer;
464 	int error;
465 
466 	error = mac_check_structmac_consistent(mac);
467 	if (error)
468 		return (error);
469 
470 	elements = malloc(mac->m_buflen, M_MACTEMP, M_WAITOK);
471 	error = copyinstr(mac->m_string, elements, mac->m_buflen, NULL);
472 	if (error) {
473 		free(elements, M_MACTEMP);
474 		return (error);
475 	}
476 
477 	buffer = malloc(mac->m_buflen, M_MACTEMP, M_WAITOK | M_ZERO);
478 	error = mac_externalize_socket_peer_label(so->so_peerlabel,
479 	    elements, buffer, mac->m_buflen);
480 	if (error == 0)
481 		error = copyout(buffer, mac->m_string, strlen(buffer)+1);
482 
483 	free(buffer, M_MACTEMP);
484 	free(elements, M_MACTEMP);
485 
486 	return (error);
487 }
488