xref: /freebsd/sys/security/audit/audit_bsm_klib.c (revision e0c4386e7e71d93b0edc0c8fa156263fc4a8b0b6)
1 /*-
2  * SPDX-License-Identifier: BSD-3-Clause
3  *
4  * Copyright (c) 1999-2009 Apple Inc.
5  * Copyright (c) 2005, 2016-2017 Robert N. M. Watson
6  * All rights reserved.
7  *
8  * Portions of this software were developed by BAE Systems, the University of
9  * Cambridge Computer Laboratory, and Memorial University under DARPA/AFRL
10  * contract FA8650-15-C-7558 ("CADETS"), as part of the DARPA Transparent
11  * Computing (TC) research program.
12  *
13  * Redistribution and use in source and binary forms, with or without
14  * modification, are permitted provided that the following conditions
15  * are met:
16  * 1.  Redistributions of source code must retain the above copyright
17  *     notice, this list of conditions and the following disclaimer.
18  * 2.  Redistributions in binary form must reproduce the above copyright
19  *     notice, this list of conditions and the following disclaimer in the
20  *     documentation and/or other materials provided with the distribution.
21  * 3.  Neither the name of Apple Inc. ("Apple") nor the names of
22  *     its contributors may be used to endorse or promote products derived
23  *     from this software without specific prior written permission.
24  *
25  * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND
26  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
27  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
28  * ARE DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR
29  * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
30  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
31  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
32  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
33  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
34  * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
35  * POSSIBILITY OF SUCH DAMAGE.
36  */
37 
38 #include <sys/param.h>
39 #include <sys/capsicum.h>
40 #include <sys/fcntl.h>
41 #include <sys/filedesc.h>
42 #include <sys/libkern.h>
43 #include <sys/malloc.h>
44 #include <sys/mount.h>
45 #include <sys/proc.h>
46 #include <sys/rwlock.h>
47 #include <sys/sem.h>
48 #include <sys/sbuf.h>
49 #include <sys/sx.h>
50 #include <sys/syscall.h>
51 #include <sys/sysctl.h>
52 #include <sys/sysent.h>
53 #include <sys/vnode.h>
54 
55 #include <bsm/audit.h>
56 #include <bsm/audit_kevents.h>
57 #include <security/audit/audit.h>
58 #include <security/audit/audit_private.h>
59 
60 struct aue_open_event {
61 	int		aoe_flags;
62 	au_event_t	aoe_event;
63 };
64 
65 static const struct aue_open_event aue_open[] = {
66 	{ O_RDONLY,					AUE_OPEN_R },
67 	{ (O_RDONLY | O_CREAT),				AUE_OPEN_RC },
68 	{ (O_RDONLY | O_CREAT | O_TRUNC),		AUE_OPEN_RTC },
69 	{ (O_RDONLY | O_TRUNC),				AUE_OPEN_RT },
70 	{ O_RDWR,					AUE_OPEN_RW },
71 	{ (O_RDWR | O_CREAT),				AUE_OPEN_RWC },
72 	{ (O_RDWR | O_CREAT | O_TRUNC),			AUE_OPEN_RWTC },
73 	{ (O_RDWR | O_TRUNC),				AUE_OPEN_RWT },
74 	{ O_WRONLY,					AUE_OPEN_W },
75 	{ (O_WRONLY | O_CREAT),				AUE_OPEN_WC },
76 	{ (O_WRONLY | O_CREAT | O_TRUNC),		AUE_OPEN_WTC },
77 	{ (O_WRONLY | O_TRUNC),				AUE_OPEN_WT },
78 };
79 
80 static const struct aue_open_event aue_openat[] = {
81 	{ O_RDONLY,					AUE_OPENAT_R },
82 	{ (O_RDONLY | O_CREAT),				AUE_OPENAT_RC },
83 	{ (O_RDONLY | O_CREAT | O_TRUNC),		AUE_OPENAT_RTC },
84 	{ (O_RDONLY | O_TRUNC),				AUE_OPENAT_RT },
85 	{ O_RDWR,					AUE_OPENAT_RW },
86 	{ (O_RDWR | O_CREAT),				AUE_OPENAT_RWC },
87 	{ (O_RDWR | O_CREAT | O_TRUNC),			AUE_OPENAT_RWTC },
88 	{ (O_RDWR | O_TRUNC),				AUE_OPENAT_RWT },
89 	{ O_WRONLY,					AUE_OPENAT_W },
90 	{ (O_WRONLY | O_CREAT),				AUE_OPENAT_WC },
91 	{ (O_WRONLY | O_CREAT | O_TRUNC),		AUE_OPENAT_WTC },
92 	{ (O_WRONLY | O_TRUNC),				AUE_OPENAT_WT },
93 };
94 
95 static const int aue_msgsys[] = {
96 	/* 0 */ AUE_MSGCTL,
97 	/* 1 */ AUE_MSGGET,
98 	/* 2 */ AUE_MSGSND,
99 	/* 3 */ AUE_MSGRCV,
100 };
101 static const int aue_msgsys_count = sizeof(aue_msgsys) / sizeof(int);
102 
103 static const int aue_semsys[] = {
104 	/* 0 */ AUE_SEMCTL,
105 	/* 1 */ AUE_SEMGET,
106 	/* 2 */ AUE_SEMOP,
107 };
108 static const int aue_semsys_count = sizeof(aue_semsys) / sizeof(int);
109 
110 static const int aue_shmsys[] = {
111 	/* 0 */ AUE_SHMAT,
112 	/* 1 */ AUE_SHMDT,
113 	/* 2 */ AUE_SHMGET,
114 	/* 3 */ AUE_SHMCTL,
115 };
116 static const int aue_shmsys_count = sizeof(aue_shmsys) / sizeof(int);
117 
118 /*
119  * Check whether an event is auditable by comparing the mask of classes this
120  * event is part of against the given mask.
121  */
122 int
123 au_preselect(au_event_t event, au_class_t class, au_mask_t *mask_p, int sorf)
124 {
125 	au_class_t effmask = 0;
126 
127 	if (mask_p == NULL)
128 		return (-1);
129 
130 	/*
131 	 * Perform the actual check of the masks against the event.
132 	 */
133 	if (sorf & AU_PRS_SUCCESS)
134 		effmask |= (mask_p->am_success & class);
135 
136 	if (sorf & AU_PRS_FAILURE)
137 		effmask |= (mask_p->am_failure & class);
138 
139 	if (effmask)
140 		return (1);
141 	else
142 		return (0);
143 }
144 
145 /*
146  * Convert sysctl names and present arguments to events.
147  */
148 au_event_t
149 audit_ctlname_to_sysctlevent(int name[], uint64_t valid_arg)
150 {
151 
152 	/* can't parse it - so return the worst case */
153 	if ((valid_arg & (ARG_CTLNAME | ARG_LEN)) != (ARG_CTLNAME | ARG_LEN))
154 		return (AUE_SYSCTL);
155 
156 	switch (name[0]) {
157 	/* non-admin "lookups" treat them special */
158 	case KERN_OSTYPE:
159 	case KERN_OSRELEASE:
160 	case KERN_OSREV:
161 	case KERN_VERSION:
162 	case KERN_ARGMAX:
163 	case KERN_CLOCKRATE:
164 	case KERN_BOOTTIME:
165 	case KERN_POSIX1:
166 	case KERN_NGROUPS:
167 	case KERN_JOB_CONTROL:
168 	case KERN_SAVED_IDS:
169 	case KERN_OSRELDATE:
170 	case KERN_DUMMY:
171 		return (AUE_SYSCTL_NONADMIN);
172 
173 	/* only treat the changeable controls as admin */
174 	case KERN_MAXVNODES:
175 	case KERN_MAXPROC:
176 	case KERN_MAXFILES:
177 	case KERN_MAXPROCPERUID:
178 	case KERN_MAXFILESPERPROC:
179 	case KERN_HOSTID:
180 	case KERN_SECURELVL:
181 	case KERN_HOSTNAME:
182 	case KERN_PROC:
183 	case KERN_FILE:
184 	case KERN_PROF:
185 	case KERN_NISDOMAINNAME:
186 	case KERN_UPDATEINTERVAL:
187 	case KERN_NTP_PLL:
188 	case KERN_BOOTFILE:
189 	case KERN_DUMPDEV:
190 	case KERN_IPC:
191 	case KERN_PS_STRINGS:
192 	case KERN_USRSTACK:
193 	case KERN_LOGSIGEXIT:
194 	case KERN_IOV_MAX:
195 		return ((valid_arg & ARG_VALUE) ?
196 		    AUE_SYSCTL : AUE_SYSCTL_NONADMIN);
197 
198 	default:
199 		return (AUE_SYSCTL);
200 	}
201 	/* NOTREACHED */
202 }
203 
204 /*
205  * Convert an open flags specifier into a specific type of open event for
206  * auditing purposes.
207  */
208 au_event_t
209 audit_flags_and_error_to_openevent(int oflags, int error)
210 {
211 	int i;
212 
213 	/*
214 	 * Need to check only those flags we care about.
215 	 */
216 	oflags = oflags & (O_RDONLY | O_CREAT | O_TRUNC | O_RDWR | O_WRONLY);
217 	for (i = 0; i < nitems(aue_open); i++) {
218 		if (aue_open[i].aoe_flags == oflags)
219 			return (aue_open[i].aoe_event);
220 	}
221 	return (AUE_OPEN);
222 }
223 
224 au_event_t
225 audit_flags_and_error_to_openatevent(int oflags, int error)
226 {
227 	int i;
228 
229 	/*
230 	 * Need to check only those flags we care about.
231 	 */
232 	oflags = oflags & (O_RDONLY | O_CREAT | O_TRUNC | O_RDWR | O_WRONLY);
233 	for (i = 0; i < nitems(aue_openat); i++) {
234 		if (aue_openat[i].aoe_flags == oflags)
235 			return (aue_openat[i].aoe_event);
236 	}
237 	return (AUE_OPENAT);
238 }
239 
240 /*
241  * Convert a MSGCTL command to a specific event.
242  */
243 au_event_t
244 audit_msgctl_to_event(int cmd)
245 {
246 
247 	switch (cmd) {
248 	case IPC_RMID:
249 		return (AUE_MSGCTL_RMID);
250 
251 	case IPC_SET:
252 		return (AUE_MSGCTL_SET);
253 
254 	case IPC_STAT:
255 		return (AUE_MSGCTL_STAT);
256 
257 	default:
258 		/* We will audit a bad command. */
259 		return (AUE_MSGCTL);
260 	}
261 }
262 
263 /*
264  * Convert a SEMCTL command to a specific event.
265  */
266 au_event_t
267 audit_semctl_to_event(int cmd)
268 {
269 
270 	switch (cmd) {
271 	case GETALL:
272 		return (AUE_SEMCTL_GETALL);
273 
274 	case GETNCNT:
275 		return (AUE_SEMCTL_GETNCNT);
276 
277 	case GETPID:
278 		return (AUE_SEMCTL_GETPID);
279 
280 	case GETVAL:
281 		return (AUE_SEMCTL_GETVAL);
282 
283 	case GETZCNT:
284 		return (AUE_SEMCTL_GETZCNT);
285 
286 	case IPC_RMID:
287 		return (AUE_SEMCTL_RMID);
288 
289 	case IPC_SET:
290 		return (AUE_SEMCTL_SET);
291 
292 	case SETALL:
293 		return (AUE_SEMCTL_SETALL);
294 
295 	case SETVAL:
296 		return (AUE_SEMCTL_SETVAL);
297 
298 	case IPC_STAT:
299 		return (AUE_SEMCTL_STAT);
300 
301 	default:
302 		/* We will audit a bad command. */
303 		return (AUE_SEMCTL);
304 	}
305 }
306 
307 /*
308  * Convert msgsys(2), semsys(2), and shmsys(2) system-call variations into
309  * audit events, if possible.
310  */
311 au_event_t
312 audit_msgsys_to_event(int which)
313 {
314 
315 	if ((which >= 0) && (which < aue_msgsys_count))
316 		return (aue_msgsys[which]);
317 
318 	/* Audit a bad command. */
319 	return (AUE_MSGSYS);
320 }
321 
322 au_event_t
323 audit_semsys_to_event(int which)
324 {
325 
326 	if ((which >= 0) && (which < aue_semsys_count))
327 		return (aue_semsys[which]);
328 
329 	/* Audit a bad command. */
330 	return (AUE_SEMSYS);
331 }
332 
333 au_event_t
334 audit_shmsys_to_event(int which)
335 {
336 
337 	if ((which >= 0) && (which < aue_shmsys_count))
338 		return (aue_shmsys[which]);
339 
340 	/* Audit a bad command. */
341 	return (AUE_SHMSYS);
342 }
343 
344 /*
345  * Convert a command for the auditon() system call to a audit event.
346  */
347 au_event_t
348 auditon_command_event(int cmd)
349 {
350 
351 	switch(cmd) {
352 	case A_GETPOLICY:
353 		return (AUE_AUDITON_GPOLICY);
354 
355 	case A_SETPOLICY:
356 		return (AUE_AUDITON_SPOLICY);
357 
358 	case A_GETKMASK:
359 		return (AUE_AUDITON_GETKMASK);
360 
361 	case A_SETKMASK:
362 		return (AUE_AUDITON_SETKMASK);
363 
364 	case A_GETQCTRL:
365 		return (AUE_AUDITON_GQCTRL);
366 
367 	case A_SETQCTRL:
368 		return (AUE_AUDITON_SQCTRL);
369 
370 	case A_GETCWD:
371 		return (AUE_AUDITON_GETCWD);
372 
373 	case A_GETCAR:
374 		return (AUE_AUDITON_GETCAR);
375 
376 	case A_GETSTAT:
377 		return (AUE_AUDITON_GETSTAT);
378 
379 	case A_SETSTAT:
380 		return (AUE_AUDITON_SETSTAT);
381 
382 	case A_SETUMASK:
383 		return (AUE_AUDITON_SETUMASK);
384 
385 	case A_SETSMASK:
386 		return (AUE_AUDITON_SETSMASK);
387 
388 	case A_GETCOND:
389 		return (AUE_AUDITON_GETCOND);
390 
391 	case A_SETCOND:
392 		return (AUE_AUDITON_SETCOND);
393 
394 	case A_GETCLASS:
395 		return (AUE_AUDITON_GETCLASS);
396 
397 	case A_SETCLASS:
398 		return (AUE_AUDITON_SETCLASS);
399 
400 	case A_GETPINFO:
401 	case A_SETPMASK:
402 	case A_SETFSIZE:
403 	case A_GETFSIZE:
404 	case A_GETPINFO_ADDR:
405 	case A_GETKAUDIT:
406 	case A_SETKAUDIT:
407 	default:
408 		return (AUE_AUDITON);	/* No special record */
409 	}
410 }
411 
412 /*
413  * Create a canonical path from given path by prefixing either the root
414  * directory, or the current working directory.  If the process working
415  * directory is NULL, we could use 'rootvnode' to obtain the root directory,
416  * but this results in a volfs name written to the audit log. So we will
417  * leave the filename starting with '/' in the audit log in this case.
418  */
419 void
420 audit_canon_path_vp(struct thread *td, struct vnode *rdir, struct vnode *cdir,
421     char *path, char *cpath)
422 {
423 	struct vnode *vp;
424 	char *rbuf, *fbuf, *copy;
425 	struct sbuf sbf;
426 	int error;
427 
428 	WITNESS_WARN(WARN_GIANTOK | WARN_SLEEPOK, NULL, "%s: at %s:%d",
429 	    __func__,  __FILE__, __LINE__);
430 
431 	copy = path;
432 	if (*path == '/') {
433 		vp = rdir;
434 	} else {
435 		if (cdir == NULL) {
436 			cpath[0] = '\0';
437 			return;
438 		}
439 		vp = cdir;
440 	}
441 	MPASS(vp != NULL);
442 	/*
443 	 * NB: We require that the supplied array be at least MAXPATHLEN bytes
444 	 * long.  If this is not the case, then we can run into serious trouble.
445 	 */
446 	(void) sbuf_new(&sbf, cpath, MAXPATHLEN, SBUF_FIXEDLEN);
447 	/*
448 	 * Strip leading forward slashes.
449 	 *
450 	 * Note this does nothing to fully canonicalize the path.
451 	 */
452 	while (*copy == '/')
453 		copy++;
454 	/*
455 	 * Make sure we handle chroot(2) and prepend the global path to these
456 	 * environments.
457 	 *
458 	 * NB: vn_fullpath(9) on FreeBSD is less reliable than vn_getpath(9)
459 	 * on Darwin.  As a result, this may need some additional attention
460 	 * in the future.
461 	 */
462 	error = vn_fullpath_global(vp, &rbuf, &fbuf);
463 	if (error) {
464 		cpath[0] = '\0';
465 		return;
466 	}
467 	(void) sbuf_cat(&sbf, rbuf);
468 	/*
469 	 * We are going to concatenate the resolved path with the passed path
470 	 * with all slashes removed and we want them glued with a single slash.
471 	 * However, if the directory is /, the slash is already there.
472 	 */
473 	if (rbuf[1] != '\0')
474 		(void) sbuf_putc(&sbf, '/');
475 	free(fbuf, M_TEMP);
476 	/*
477 	 * Now that we have processed any alternate root and relative path
478 	 * names, add the supplied pathname.
479 	 */
480 	(void) sbuf_cat(&sbf, copy);
481 	/*
482 	 * One or more of the previous sbuf operations could have resulted in
483 	 * the supplied buffer being overflowed.  Check to see if this is the
484 	 * case.
485 	 */
486 	if (sbuf_error(&sbf) != 0) {
487 		cpath[0] = '\0';
488 		return;
489 	}
490 	sbuf_finish(&sbf);
491 }
492 
493 void
494 audit_canon_path(struct thread *td, int dirfd, char *path, char *cpath)
495 {
496 	struct vnode *cdir, *rdir;
497 	struct pwd *pwd;
498 	cap_rights_t rights;
499 	int error;
500 	bool vrele_cdir;
501 
502 	WITNESS_WARN(WARN_GIANTOK | WARN_SLEEPOK, NULL, "%s: at %s:%d",
503 	    __func__,  __FILE__, __LINE__);
504 
505 	pwd = pwd_hold(td);
506 	rdir = pwd->pwd_rdir;
507 	cdir = NULL;
508 	vrele_cdir = false;
509 	if (*path != '/') {
510 		if (dirfd == AT_FDCWD) {
511 			cdir = pwd->pwd_cdir;
512 		} else {
513 			error = fgetvp(td, dirfd, cap_rights_init(&rights), &cdir);
514 			if (error != 0) {
515 				cpath[0] = '\0';
516 				pwd_drop(pwd);
517 				return;
518 			}
519 			vrele_cdir = true;
520 		}
521 	}
522 
523 	audit_canon_path_vp(td, rdir, cdir, path, cpath);
524 
525 	pwd_drop(pwd);
526 	if (vrele_cdir)
527 		vrele(cdir);
528 }
529