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