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
au_preselect(au_event_t event,au_class_t class,au_mask_t * mask_p,int sorf)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
audit_ctlname_to_sysctlevent(int name[],uint64_t valid_arg)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
audit_flags_and_error_to_openevent(int oflags,int error)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
audit_flags_and_error_to_openatevent(int oflags,int error)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
audit_msgctl_to_event(int cmd)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
audit_semctl_to_event(int cmd)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
audit_msgsys_to_event(int which)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
audit_semsys_to_event(int which)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
audit_shmsys_to_event(int which)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
auditon_command_event(int cmd)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
audit_canon_path_vp(struct thread * td,struct vnode * rdir,struct vnode * cdir,char * path,char * cpath)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
audit_canon_path(struct thread * td,int dirfd,char * path,char * cpath)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