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