xref: /freebsd/sys/security/mac/mac_syscalls.c (revision 9768746b)
1 /*-
2  * Copyright (c) 1999-2002, 2006, 2009 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 Network
13  * Associates Laboratories, the Security Research Division of Network
14  * Associates, Inc. under DARPA/SPAWAR contract N66001-01-C-8035 ("CBOSS"),
15  * as part of the DARPA CHATS research program.
16  *
17  * This software was enhanced by SPARTA ISSO under SPAWAR contract
18  * N66001-04-C-6019 ("SEFOS").
19  *
20  * This software was developed at the University of Cambridge Computer
21  * Laboratory with support from a grant from Google, Inc.
22  *
23  * Redistribution and use in source and binary forms, with or without
24  * modification, are permitted provided that the following conditions
25  * are met:
26  * 1. Redistributions of source code must retain the above copyright
27  *    notice, this list of conditions and the following disclaimer.
28  * 2. Redistributions in binary form must reproduce the above copyright
29  *    notice, this list of conditions and the following disclaimer in the
30  *    documentation and/or other materials provided with the distribution.
31  *
32  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
33  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
34  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
35  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
36  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
37  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
38  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
39  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
40  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
41  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
42  * SUCH DAMAGE.
43  */
44 
45 #include <sys/cdefs.h>
46 __FBSDID("$FreeBSD$");
47 
48 #include "opt_mac.h"
49 
50 #include <sys/param.h>
51 #include <sys/capsicum.h>
52 #include <sys/fcntl.h>
53 #include <sys/kernel.h>
54 #include <sys/lock.h>
55 #include <sys/malloc.h>
56 #include <sys/mutex.h>
57 #include <sys/mac.h>
58 #include <sys/proc.h>
59 #include <sys/systm.h>
60 #include <sys/sysctl.h>
61 #include <sys/sysproto.h>
62 #include <sys/vnode.h>
63 #include <sys/mount.h>
64 #include <sys/file.h>
65 #include <sys/namei.h>
66 #include <sys/socket.h>
67 #include <sys/pipe.h>
68 #include <sys/socketvar.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 #ifdef MAC
75 
76 FEATURE(security_mac, "Mandatory Access Control Framework support");
77 
78 static int	kern___mac_get_path(struct thread *td, const char *path_p,
79 		    struct mac *mac_p, int follow);
80 static int	kern___mac_set_path(struct thread *td, const char *path_p,
81 		    struct mac *mac_p, int follow);
82 
83 int
84 sys___mac_get_pid(struct thread *td, struct __mac_get_pid_args *uap)
85 {
86 	char *elements, *buffer;
87 	struct mac mac;
88 	struct proc *tproc;
89 	struct ucred *tcred;
90 	int error;
91 
92 	error = copyin(uap->mac_p, &mac, sizeof(mac));
93 	if (error)
94 		return (error);
95 
96 	error = mac_check_structmac_consistent(&mac);
97 	if (error)
98 		return (error);
99 
100 	tproc = pfind(uap->pid);
101 	if (tproc == NULL)
102 		return (ESRCH);
103 
104 	tcred = NULL;				/* Satisfy gcc. */
105 	error = p_cansee(td, tproc);
106 	if (error == 0)
107 		tcred = crhold(tproc->p_ucred);
108 	PROC_UNLOCK(tproc);
109 	if (error)
110 		return (error);
111 
112 	elements = malloc(mac.m_buflen, M_MACTEMP, M_WAITOK);
113 	error = copyinstr(mac.m_string, elements, mac.m_buflen, NULL);
114 	if (error) {
115 		free(elements, M_MACTEMP);
116 		crfree(tcred);
117 		return (error);
118 	}
119 
120 	buffer = malloc(mac.m_buflen, M_MACTEMP, M_WAITOK | M_ZERO);
121 	error = mac_cred_externalize_label(tcred->cr_label, elements,
122 	    buffer, mac.m_buflen);
123 	if (error == 0)
124 		error = copyout(buffer, mac.m_string, strlen(buffer)+1);
125 
126 	free(buffer, M_MACTEMP);
127 	free(elements, M_MACTEMP);
128 	crfree(tcred);
129 	return (error);
130 }
131 
132 int
133 sys___mac_get_proc(struct thread *td, struct __mac_get_proc_args *uap)
134 {
135 	char *elements, *buffer;
136 	struct mac mac;
137 	int error;
138 
139 	error = copyin(uap->mac_p, &mac, sizeof(mac));
140 	if (error)
141 		return (error);
142 
143 	error = mac_check_structmac_consistent(&mac);
144 	if (error)
145 		return (error);
146 
147 	elements = malloc(mac.m_buflen, M_MACTEMP, M_WAITOK);
148 	error = copyinstr(mac.m_string, elements, mac.m_buflen, NULL);
149 	if (error) {
150 		free(elements, M_MACTEMP);
151 		return (error);
152 	}
153 
154 	buffer = malloc(mac.m_buflen, M_MACTEMP, M_WAITOK | M_ZERO);
155 	error = mac_cred_externalize_label(td->td_ucred->cr_label,
156 	    elements, buffer, mac.m_buflen);
157 	if (error == 0)
158 		error = copyout(buffer, mac.m_string, strlen(buffer)+1);
159 
160 	free(buffer, M_MACTEMP);
161 	free(elements, M_MACTEMP);
162 	return (error);
163 }
164 
165 int
166 sys___mac_set_proc(struct thread *td, struct __mac_set_proc_args *uap)
167 {
168 	struct ucred *newcred, *oldcred;
169 	struct label *intlabel;
170 	struct proc *p;
171 	struct mac mac;
172 	char *buffer;
173 	int error;
174 
175 	if (!(mac_labeled & MPC_OBJECT_CRED))
176 		return (EINVAL);
177 
178 	error = copyin(uap->mac_p, &mac, sizeof(mac));
179 	if (error)
180 		return (error);
181 
182 	error = mac_check_structmac_consistent(&mac);
183 	if (error)
184 		return (error);
185 
186 	buffer = malloc(mac.m_buflen, M_MACTEMP, M_WAITOK);
187 	error = copyinstr(mac.m_string, buffer, mac.m_buflen, NULL);
188 	if (error) {
189 		free(buffer, M_MACTEMP);
190 		return (error);
191 	}
192 
193 	intlabel = mac_cred_label_alloc();
194 	error = mac_cred_internalize_label(intlabel, buffer);
195 	free(buffer, M_MACTEMP);
196 	if (error)
197 		goto out;
198 
199 	newcred = crget();
200 
201 	p = td->td_proc;
202 	PROC_LOCK(p);
203 	oldcred = p->p_ucred;
204 
205 	error = mac_cred_check_relabel(oldcred, intlabel);
206 	if (error) {
207 		PROC_UNLOCK(p);
208 		crfree(newcred);
209 		goto out;
210 	}
211 
212 	setsugid(p);
213 	crcopy(newcred, oldcred);
214 	mac_cred_relabel(newcred, intlabel);
215 	proc_set_cred(p, newcred);
216 
217 	PROC_UNLOCK(p);
218 	crfree(oldcred);
219 	mac_proc_vm_revoke(td);
220 
221 out:
222 	mac_cred_label_free(intlabel);
223 	return (error);
224 }
225 
226 int
227 sys___mac_get_fd(struct thread *td, struct __mac_get_fd_args *uap)
228 {
229 	char *elements, *buffer;
230 	struct label *intlabel;
231 	struct file *fp;
232 	struct mac mac;
233 	struct vnode *vp;
234 	struct pipe *pipe;
235 	struct socket *so;
236 	cap_rights_t rights;
237 	int error;
238 
239 	error = copyin(uap->mac_p, &mac, sizeof(mac));
240 	if (error)
241 		return (error);
242 
243 	error = mac_check_structmac_consistent(&mac);
244 	if (error)
245 		return (error);
246 
247 	elements = malloc(mac.m_buflen, M_MACTEMP, M_WAITOK);
248 	error = copyinstr(mac.m_string, elements, mac.m_buflen, NULL);
249 	if (error) {
250 		free(elements, M_MACTEMP);
251 		return (error);
252 	}
253 
254 	buffer = malloc(mac.m_buflen, M_MACTEMP, M_WAITOK | M_ZERO);
255 	error = fget(td, uap->fd, cap_rights_init_one(&rights, CAP_MAC_GET),
256 	    &fp);
257 	if (error)
258 		goto out;
259 
260 	switch (fp->f_type) {
261 	case DTYPE_FIFO:
262 	case DTYPE_VNODE:
263 		if (!(mac_labeled & MPC_OBJECT_VNODE)) {
264 			error = EINVAL;
265 			goto out_fdrop;
266 		}
267 		vp = fp->f_vnode;
268 		intlabel = mac_vnode_label_alloc();
269 		vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);
270 		mac_vnode_copy_label(vp->v_label, intlabel);
271 		VOP_UNLOCK(vp);
272 		error = mac_vnode_externalize_label(intlabel, elements,
273 		    buffer, mac.m_buflen);
274 		mac_vnode_label_free(intlabel);
275 		break;
276 
277 	case DTYPE_PIPE:
278 		if (!(mac_labeled & MPC_OBJECT_PIPE)) {
279 			error = EINVAL;
280 			goto out_fdrop;
281 		}
282 		pipe = fp->f_data;
283 		intlabel = mac_pipe_label_alloc();
284 		PIPE_LOCK(pipe);
285 		mac_pipe_copy_label(pipe->pipe_pair->pp_label, intlabel);
286 		PIPE_UNLOCK(pipe);
287 		error = mac_pipe_externalize_label(intlabel, elements,
288 		    buffer, mac.m_buflen);
289 		mac_pipe_label_free(intlabel);
290 		break;
291 
292 	case DTYPE_SOCKET:
293 		if (!(mac_labeled & MPC_OBJECT_SOCKET)) {
294 			error = EINVAL;
295 			goto out_fdrop;
296 		}
297 		so = fp->f_data;
298 		intlabel = mac_socket_label_alloc(M_WAITOK);
299 		SOCK_LOCK(so);
300 		mac_socket_copy_label(so->so_label, intlabel);
301 		SOCK_UNLOCK(so);
302 		error = mac_socket_externalize_label(intlabel, elements,
303 		    buffer, mac.m_buflen);
304 		mac_socket_label_free(intlabel);
305 		break;
306 
307 	default:
308 		error = EINVAL;
309 	}
310 	if (error == 0)
311 		error = copyout(buffer, mac.m_string, strlen(buffer)+1);
312 out_fdrop:
313 	fdrop(fp, td);
314 out:
315 	free(buffer, M_MACTEMP);
316 	free(elements, M_MACTEMP);
317 	return (error);
318 }
319 
320 int
321 sys___mac_get_file(struct thread *td, struct __mac_get_file_args *uap)
322 {
323 
324 	return (kern___mac_get_path(td, uap->path_p, uap->mac_p, FOLLOW));
325 }
326 
327 int
328 sys___mac_get_link(struct thread *td, struct __mac_get_link_args *uap)
329 {
330 
331 	return (kern___mac_get_path(td, uap->path_p, uap->mac_p, NOFOLLOW));
332 }
333 
334 static int
335 kern___mac_get_path(struct thread *td, const char *path_p, struct mac *mac_p,
336    int follow)
337 {
338 	char *elements, *buffer;
339 	struct nameidata nd;
340 	struct label *intlabel;
341 	struct mac mac;
342 	int error;
343 
344 	if (!(mac_labeled & MPC_OBJECT_VNODE))
345 		return (EINVAL);
346 
347 	error = copyin(mac_p, &mac, sizeof(mac));
348 	if (error)
349 		return (error);
350 
351 	error = mac_check_structmac_consistent(&mac);
352 	if (error)
353 		return (error);
354 
355 	elements = malloc(mac.m_buflen, M_MACTEMP, M_WAITOK);
356 	error = copyinstr(mac.m_string, elements, mac.m_buflen, NULL);
357 	if (error) {
358 		free(elements, M_MACTEMP);
359 		return (error);
360 	}
361 
362 	buffer = malloc(mac.m_buflen, M_MACTEMP, M_WAITOK | M_ZERO);
363 	NDINIT(&nd, LOOKUP, LOCKLEAF | follow, UIO_USERSPACE, path_p);
364 	error = namei(&nd);
365 	if (error)
366 		goto out;
367 
368 	intlabel = mac_vnode_label_alloc();
369 	mac_vnode_copy_label(nd.ni_vp->v_label, intlabel);
370 	error = mac_vnode_externalize_label(intlabel, elements, buffer,
371 	    mac.m_buflen);
372 	vput(nd.ni_vp);
373 	NDFREE_PNBUF(&nd);
374 	mac_vnode_label_free(intlabel);
375 
376 	if (error == 0)
377 		error = copyout(buffer, mac.m_string, strlen(buffer)+1);
378 
379 out:
380 	free(buffer, M_MACTEMP);
381 	free(elements, M_MACTEMP);
382 
383 	return (error);
384 }
385 
386 int
387 sys___mac_set_fd(struct thread *td, struct __mac_set_fd_args *uap)
388 {
389 	struct label *intlabel;
390 	struct pipe *pipe;
391 	struct socket *so;
392 	struct file *fp;
393 	struct mount *mp;
394 	struct vnode *vp;
395 	struct mac mac;
396 	cap_rights_t rights;
397 	char *buffer;
398 	int error;
399 
400 	error = copyin(uap->mac_p, &mac, sizeof(mac));
401 	if (error)
402 		return (error);
403 
404 	error = mac_check_structmac_consistent(&mac);
405 	if (error)
406 		return (error);
407 
408 	buffer = malloc(mac.m_buflen, M_MACTEMP, M_WAITOK);
409 	error = copyinstr(mac.m_string, buffer, mac.m_buflen, NULL);
410 	if (error) {
411 		free(buffer, M_MACTEMP);
412 		return (error);
413 	}
414 
415 	error = fget(td, uap->fd, cap_rights_init_one(&rights, CAP_MAC_SET),
416 	    &fp);
417 	if (error)
418 		goto out;
419 
420 	switch (fp->f_type) {
421 	case DTYPE_FIFO:
422 	case DTYPE_VNODE:
423 		if (!(mac_labeled & MPC_OBJECT_VNODE)) {
424 			error = EINVAL;
425 			goto out_fdrop;
426 		}
427 		intlabel = mac_vnode_label_alloc();
428 		error = mac_vnode_internalize_label(intlabel, buffer);
429 		if (error) {
430 			mac_vnode_label_free(intlabel);
431 			break;
432 		}
433 		vp = fp->f_vnode;
434 		error = vn_start_write(vp, &mp, V_WAIT | V_PCATCH);
435 		if (error != 0) {
436 			mac_vnode_label_free(intlabel);
437 			break;
438 		}
439 		vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);
440 		error = vn_setlabel(vp, intlabel, td->td_ucred);
441 		VOP_UNLOCK(vp);
442 		vn_finished_write(mp);
443 		mac_vnode_label_free(intlabel);
444 		break;
445 
446 	case DTYPE_PIPE:
447 		if (!(mac_labeled & MPC_OBJECT_PIPE)) {
448 			error = EINVAL;
449 			goto out_fdrop;
450 		}
451 		intlabel = mac_pipe_label_alloc();
452 		error = mac_pipe_internalize_label(intlabel, buffer);
453 		if (error == 0) {
454 			pipe = fp->f_data;
455 			PIPE_LOCK(pipe);
456 			error = mac_pipe_label_set(td->td_ucred,
457 			    pipe->pipe_pair, intlabel);
458 			PIPE_UNLOCK(pipe);
459 		}
460 		mac_pipe_label_free(intlabel);
461 		break;
462 
463 	case DTYPE_SOCKET:
464 		if (!(mac_labeled & MPC_OBJECT_SOCKET)) {
465 			error = EINVAL;
466 			goto out_fdrop;
467 		}
468 		intlabel = mac_socket_label_alloc(M_WAITOK);
469 		error = mac_socket_internalize_label(intlabel, buffer);
470 		if (error == 0) {
471 			so = fp->f_data;
472 			error = mac_socket_label_set(td->td_ucred, so,
473 			    intlabel);
474 		}
475 		mac_socket_label_free(intlabel);
476 		break;
477 
478 	default:
479 		error = EINVAL;
480 	}
481 out_fdrop:
482 	fdrop(fp, td);
483 out:
484 	free(buffer, M_MACTEMP);
485 	return (error);
486 }
487 
488 int
489 sys___mac_set_file(struct thread *td, struct __mac_set_file_args *uap)
490 {
491 
492 	return (kern___mac_set_path(td, uap->path_p, uap->mac_p, FOLLOW));
493 }
494 
495 int
496 sys___mac_set_link(struct thread *td, struct __mac_set_link_args *uap)
497 {
498 
499 	return (kern___mac_set_path(td, uap->path_p, uap->mac_p, NOFOLLOW));
500 }
501 
502 static int
503 kern___mac_set_path(struct thread *td, const char *path_p, struct mac *mac_p,
504     int follow)
505 {
506 	struct label *intlabel;
507 	struct nameidata nd;
508 	struct mount *mp;
509 	struct mac mac;
510 	char *buffer;
511 	int error;
512 
513 	if (!(mac_labeled & MPC_OBJECT_VNODE))
514 		return (EINVAL);
515 
516 	error = copyin(mac_p, &mac, sizeof(mac));
517 	if (error)
518 		return (error);
519 
520 	error = mac_check_structmac_consistent(&mac);
521 	if (error)
522 		return (error);
523 
524 	buffer = malloc(mac.m_buflen, M_MACTEMP, M_WAITOK);
525 	error = copyinstr(mac.m_string, buffer, mac.m_buflen, NULL);
526 	if (error) {
527 		free(buffer, M_MACTEMP);
528 		return (error);
529 	}
530 
531 	intlabel = mac_vnode_label_alloc();
532 	error = mac_vnode_internalize_label(intlabel, buffer);
533 	free(buffer, M_MACTEMP);
534 	if (error)
535 		goto out;
536 
537 	NDINIT(&nd, LOOKUP, LOCKLEAF | follow, UIO_USERSPACE, path_p);
538 	error = namei(&nd);
539 	if (error == 0) {
540 		error = vn_start_write(nd.ni_vp, &mp, V_WAIT | V_PCATCH);
541 		if (error == 0) {
542 			error = vn_setlabel(nd.ni_vp, intlabel,
543 			    td->td_ucred);
544 			vn_finished_write(mp);
545 		}
546 		vput(nd.ni_vp);
547 		NDFREE_PNBUF(&nd);
548 	}
549 out:
550 	mac_vnode_label_free(intlabel);
551 	return (error);
552 }
553 
554 int
555 sys_mac_syscall(struct thread *td, struct mac_syscall_args *uap)
556 {
557 	struct mac_policy_conf *mpc;
558 	char target[MAC_MAX_POLICY_NAME];
559 	int error;
560 
561 	error = copyinstr(uap->policy, target, sizeof(target), NULL);
562 	if (error)
563 		return (error);
564 
565 	error = ENOSYS;
566 	LIST_FOREACH(mpc, &mac_static_policy_list, mpc_list) {
567 		if (strcmp(mpc->mpc_name, target) == 0 &&
568 		    mpc->mpc_ops->mpo_syscall != NULL) {
569 			error = mpc->mpc_ops->mpo_syscall(td,
570 			    uap->call, uap->arg);
571 			goto out;
572 		}
573 	}
574 
575 	if (!LIST_EMPTY(&mac_policy_list)) {
576 		mac_policy_slock_sleep();
577 		LIST_FOREACH(mpc, &mac_policy_list, mpc_list) {
578 			if (strcmp(mpc->mpc_name, target) == 0 &&
579 			    mpc->mpc_ops->mpo_syscall != NULL) {
580 				error = mpc->mpc_ops->mpo_syscall(td,
581 				    uap->call, uap->arg);
582 				break;
583 			}
584 		}
585 		mac_policy_sunlock_sleep();
586 	}
587 out:
588 	return (error);
589 }
590 
591 #else /* !MAC */
592 
593 int
594 sys___mac_get_pid(struct thread *td, struct __mac_get_pid_args *uap)
595 {
596 
597 	return (ENOSYS);
598 }
599 
600 int
601 sys___mac_get_proc(struct thread *td, struct __mac_get_proc_args *uap)
602 {
603 
604 	return (ENOSYS);
605 }
606 
607 int
608 sys___mac_set_proc(struct thread *td, struct __mac_set_proc_args *uap)
609 {
610 
611 	return (ENOSYS);
612 }
613 
614 int
615 sys___mac_get_fd(struct thread *td, struct __mac_get_fd_args *uap)
616 {
617 
618 	return (ENOSYS);
619 }
620 
621 int
622 sys___mac_get_file(struct thread *td, struct __mac_get_file_args *uap)
623 {
624 
625 	return (ENOSYS);
626 }
627 
628 int
629 sys___mac_get_link(struct thread *td, struct __mac_get_link_args *uap)
630 {
631 
632 	return (ENOSYS);
633 }
634 
635 int
636 sys___mac_set_fd(struct thread *td, struct __mac_set_fd_args *uap)
637 {
638 
639 	return (ENOSYS);
640 }
641 
642 int
643 sys___mac_set_file(struct thread *td, struct __mac_set_file_args *uap)
644 {
645 
646 	return (ENOSYS);
647 }
648 
649 int
650 sys___mac_set_link(struct thread *td, struct __mac_set_link_args *uap)
651 {
652 
653 	return (ENOSYS);
654 }
655 
656 int
657 sys_mac_syscall(struct thread *td, struct mac_syscall_args *uap)
658 {
659 
660 	return (ENOSYS);
661 }
662 
663 #endif /* !MAC */
664