xref: /freebsd/sys/security/audit/audit_bsm_klib.c (revision 2be1a816b9ff69588e55be0a84cbe2a31efc0f2f)
1 /*
2  * Copyright (c) 1999-2005 Apple Computer, Inc.
3  * Copyright (c) 2005 Robert N. M. Watson
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1.  Redistributions of source code must retain the above copyright
10  *     notice, this list of conditions and the following disclaimer.
11  * 2.  Redistributions in binary form must reproduce the above copyright
12  *     notice, this list of conditions and the following disclaimer in the
13  *     documentation and/or other materials provided with the distribution.
14  * 3.  Neither the name of Apple Computer, Inc. ("Apple") nor the names of
15  *     its contributors may be used to endorse or promote products derived
16  *     from this software without specific prior written permission.
17  *
18  * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND
19  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21  * ARE DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR
22  * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
26  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
27  * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28  * POSSIBILITY OF SUCH DAMAGE.
29  */
30 
31 #include <sys/cdefs.h>
32 __FBSDID("$FreeBSD$");
33 
34 #include <sys/param.h>
35 #include <sys/fcntl.h>
36 #include <sys/filedesc.h>
37 #include <sys/libkern.h>
38 #include <sys/malloc.h>
39 #include <sys/mount.h>
40 #include <sys/proc.h>
41 #include <sys/sem.h>
42 #include <sys/syscall.h>
43 #include <sys/sysctl.h>
44 #include <sys/sysent.h>
45 #include <sys/vnode.h>
46 
47 #include <bsm/audit.h>
48 #include <bsm/audit_kevents.h>
49 #include <security/audit/audit.h>
50 #include <security/audit/audit_private.h>
51 
52 /*
53  * Hash table functions for the audit event number to event class mask
54  * mapping.
55  */
56 #define EVCLASSMAP_HASH_TABLE_SIZE 251
57 struct evclass_elem {
58 	au_event_t event;
59 	au_class_t class;
60 	LIST_ENTRY(evclass_elem) entry;
61 };
62 struct evclass_list {
63 	LIST_HEAD(, evclass_elem) head;
64 };
65 
66 static MALLOC_DEFINE(M_AUDITEVCLASS, "audit_evclass", "Audit event class");
67 static struct mtx		evclass_mtx;
68 static struct evclass_list	evclass_hash[EVCLASSMAP_HASH_TABLE_SIZE];
69 
70 /*
71  * Look up the class for an audit event in the class mapping table.
72  */
73 au_class_t
74 au_event_class(au_event_t event)
75 {
76 	struct evclass_list *evcl;
77 	struct evclass_elem *evc;
78 	au_class_t class;
79 
80 	mtx_lock(&evclass_mtx);
81 	evcl = &evclass_hash[event % EVCLASSMAP_HASH_TABLE_SIZE];
82 	class = 0;
83 	LIST_FOREACH(evc, &evcl->head, entry) {
84 		if (evc->event == event) {
85 			class = evc->class;
86 			goto out;
87 		}
88 	}
89 out:
90 	mtx_unlock(&evclass_mtx);
91 	return (class);
92 }
93 
94 /*
95  * Insert a event to class mapping. If the event already exists in the
96  * mapping, then replace the mapping with the new one.
97  *
98  * XXX There is currently no constraints placed on the number of mappings.
99  * May want to either limit to a number, or in terms of memory usage.
100  */
101 void
102 au_evclassmap_insert(au_event_t event, au_class_t class)
103 {
104 	struct evclass_list *evcl;
105 	struct evclass_elem *evc, *evc_new;
106 
107 	/*
108 	 * Pessimistically, always allocate storage before acquiring mutex.
109 	 * Free if there is already a mapping for this event.
110 	 */
111 	evc_new = malloc(sizeof(*evc), M_AUDITEVCLASS, M_WAITOK);
112 
113 	mtx_lock(&evclass_mtx);
114 	evcl = &evclass_hash[event % EVCLASSMAP_HASH_TABLE_SIZE];
115 	LIST_FOREACH(evc, &evcl->head, entry) {
116 		if (evc->event == event) {
117 			evc->class = class;
118 			mtx_unlock(&evclass_mtx);
119 			free(evc_new, M_AUDITEVCLASS);
120 			return;
121 		}
122 	}
123 	evc = evc_new;
124 	evc->event = event;
125 	evc->class = class;
126 	LIST_INSERT_HEAD(&evcl->head, evc, entry);
127 	mtx_unlock(&evclass_mtx);
128 }
129 
130 void
131 au_evclassmap_init(void)
132 {
133 	int i;
134 
135 	mtx_init(&evclass_mtx, "evclass_mtx", NULL, MTX_DEF);
136 	for (i = 0; i < EVCLASSMAP_HASH_TABLE_SIZE; i++)
137 		LIST_INIT(&evclass_hash[i].head);
138 
139 	/*
140 	 * Set up the initial event to class mapping for system calls.
141 	 *
142 	 * XXXRW: Really, this should walk all possible audit events, not all
143 	 * native ABI system calls, as there may be audit events reachable
144 	 * only through non-native system calls.  It also seems a shame to
145 	 * frob the mutex this early.
146 	 */
147 	for (i = 0; i < SYS_MAXSYSCALL; i++) {
148 		if (sysent[i].sy_auevent != AUE_NULL)
149 			au_evclassmap_insert(sysent[i].sy_auevent, 0);
150 	}
151 }
152 
153 /*
154  * Check whether an event is aditable by comparing the mask of classes this
155  * event is part of against the given mask.
156  */
157 int
158 au_preselect(au_event_t event, au_class_t class, au_mask_t *mask_p, int sorf)
159 {
160 	au_class_t effmask = 0;
161 
162 	if (mask_p == NULL)
163 		return (-1);
164 
165 	/*
166 	 * Perform the actual check of the masks against the event.
167 	 */
168 	if (sorf & AU_PRS_SUCCESS)
169 		effmask |= (mask_p->am_success & class);
170 
171 	if (sorf & AU_PRS_FAILURE)
172 		effmask |= (mask_p->am_failure & class);
173 
174 	if (effmask)
175 		return (1);
176 	else
177 		return (0);
178 }
179 
180 /*
181  * Convert sysctl names and present arguments to events.
182  */
183 au_event_t
184 audit_ctlname_to_sysctlevent(int name[], uint64_t valid_arg)
185 {
186 
187 	/* can't parse it - so return the worst case */
188 	if ((valid_arg & (ARG_CTLNAME | ARG_LEN)) != (ARG_CTLNAME | ARG_LEN))
189 		return (AUE_SYSCTL);
190 
191 	switch (name[0]) {
192 	/* non-admin "lookups" treat them special */
193 	case KERN_OSTYPE:
194 	case KERN_OSRELEASE:
195 	case KERN_OSREV:
196 	case KERN_VERSION:
197 	case KERN_ARGMAX:
198 	case KERN_CLOCKRATE:
199 	case KERN_BOOTTIME:
200 	case KERN_POSIX1:
201 	case KERN_NGROUPS:
202 	case KERN_JOB_CONTROL:
203 	case KERN_SAVED_IDS:
204 	case KERN_OSRELDATE:
205 	case KERN_DUMMY:
206 		return (AUE_SYSCTL_NONADMIN);
207 
208 	/* only treat the changeable controls as admin */
209 	case KERN_MAXVNODES:
210 	case KERN_MAXPROC:
211 	case KERN_MAXFILES:
212 	case KERN_MAXPROCPERUID:
213 	case KERN_MAXFILESPERPROC:
214 	case KERN_HOSTID:
215 	case KERN_SECURELVL:
216 	case KERN_HOSTNAME:
217 	case KERN_VNODE:
218 	case KERN_PROC:
219 	case KERN_FILE:
220 	case KERN_PROF:
221 	case KERN_NISDOMAINNAME:
222 	case KERN_UPDATEINTERVAL:
223 	case KERN_NTP_PLL:
224 	case KERN_BOOTFILE:
225 	case KERN_DUMPDEV:
226 	case KERN_IPC:
227 	case KERN_PS_STRINGS:
228 	case KERN_USRSTACK:
229 	case KERN_LOGSIGEXIT:
230 	case KERN_IOV_MAX:
231 	case KERN_MAXID:
232 		return ((valid_arg & ARG_VALUE) ?
233 			AUE_SYSCTL : AUE_SYSCTL_NONADMIN);
234 
235 	default:
236 		return (AUE_SYSCTL);
237 	}
238 	/* NOTREACHED */
239 }
240 
241 /*
242  * Convert an open flags specifier into a specific type of open event for
243  * auditing purposes.
244  */
245 au_event_t
246 audit_flags_and_error_to_openevent(int oflags, int error)
247 {
248 	au_event_t aevent;
249 
250 	/*
251 	 * Need to check only those flags we care about.
252 	 */
253 	oflags = oflags & (O_RDONLY | O_CREAT | O_TRUNC | O_RDWR | O_WRONLY);
254 
255 	/*
256 	 * These checks determine what flags are on with the condition that
257 	 * ONLY that combination is on, and no other flags are on.
258 	 */
259 	switch (oflags) {
260 	case O_RDONLY:
261 		aevent = AUE_OPEN_R;
262 		break;
263 
264 	case (O_RDONLY | O_CREAT):
265 		aevent = AUE_OPEN_RC;
266 		break;
267 
268 	case (O_RDONLY | O_CREAT | O_TRUNC):
269 		aevent = AUE_OPEN_RTC;
270 		break;
271 
272 	case (O_RDONLY | O_TRUNC):
273 		aevent = AUE_OPEN_RT;
274 		break;
275 
276 	case O_RDWR:
277 		aevent = AUE_OPEN_RW;
278 		break;
279 
280 	case (O_RDWR | O_CREAT):
281 		aevent = AUE_OPEN_RWC;
282 		break;
283 
284 	case (O_RDWR | O_CREAT | O_TRUNC):
285 		aevent = AUE_OPEN_RWTC;
286 		break;
287 
288 	case (O_RDWR | O_TRUNC):
289 		aevent = AUE_OPEN_RWT;
290 		break;
291 
292 	case O_WRONLY:
293 		aevent = AUE_OPEN_W;
294 		break;
295 
296 	case (O_WRONLY | O_CREAT):
297 		aevent = AUE_OPEN_WC;
298 		break;
299 
300 	case (O_WRONLY | O_CREAT | O_TRUNC):
301 		aevent = AUE_OPEN_WTC;
302 		break;
303 
304 	case (O_WRONLY | O_TRUNC):
305 		aevent = AUE_OPEN_WT;
306 		break;
307 
308 	default:
309 		aevent = AUE_OPEN;
310 		break;
311 	}
312 
313 #if 0
314 	/*
315 	 * Convert chatty errors to better matching events.  Failures to
316 	 * find a file are really just attribute events -- so recast them as
317 	 * such.
318 	 *
319 	 * XXXAUDIT: Solaris defines that AUE_OPEN will never be returned, it
320 	 * is just a placeholder.  However, in Darwin we return that in
321 	 * preference to other events.  For now, comment this out as we don't
322 	 * have a BSM conversion routine for AUE_OPEN.
323 	 */
324 	switch (aevent) {
325 	case AUE_OPEN_R:
326 	case AUE_OPEN_RT:
327 	case AUE_OPEN_RW:
328 	case AUE_OPEN_RWT:
329 	case AUE_OPEN_W:
330 	case AUE_OPEN_WT:
331 		if (error == ENOENT)
332 			aevent = AUE_OPEN;
333 	}
334 #endif
335 	return (aevent);
336 }
337 
338 /*
339  * Convert a MSGCTL command to a specific event.
340  */
341 int
342 audit_msgctl_to_event(int cmd)
343 {
344 
345 	switch (cmd) {
346 	case IPC_RMID:
347 		return (AUE_MSGCTL_RMID);
348 
349 	case IPC_SET:
350 		return (AUE_MSGCTL_SET);
351 
352 	case IPC_STAT:
353 		return (AUE_MSGCTL_STAT);
354 
355 	default:
356 		/* We will audit a bad command. */
357 		return (AUE_MSGCTL);
358 	}
359 }
360 
361 /*
362  * Convert a SEMCTL command to a specific event.
363  */
364 int
365 audit_semctl_to_event(int cmd)
366 {
367 
368 	switch (cmd) {
369 	case GETALL:
370 		return (AUE_SEMCTL_GETALL);
371 
372 	case GETNCNT:
373 		return (AUE_SEMCTL_GETNCNT);
374 
375 	case GETPID:
376 		return (AUE_SEMCTL_GETPID);
377 
378 	case GETVAL:
379 		return (AUE_SEMCTL_GETVAL);
380 
381 	case GETZCNT:
382 		return (AUE_SEMCTL_GETZCNT);
383 
384 	case IPC_RMID:
385 		return (AUE_SEMCTL_RMID);
386 
387 	case IPC_SET:
388 		return (AUE_SEMCTL_SET);
389 
390 	case SETALL:
391 		return (AUE_SEMCTL_SETALL);
392 
393 	case SETVAL:
394 		return (AUE_SEMCTL_SETVAL);
395 
396 	case IPC_STAT:
397 		return (AUE_SEMCTL_STAT);
398 
399 	default:
400 		/* We will audit a bad command */
401 		return (AUE_SEMCTL);
402 	}
403 }
404 
405 /*
406  * Convert a command for the auditon() system call to a audit event.
407  */
408 int
409 auditon_command_event(int cmd)
410 {
411 
412 	switch(cmd) {
413 	case A_GETPOLICY:
414 		return (AUE_AUDITON_GPOLICY);
415 
416 	case A_SETPOLICY:
417 		return (AUE_AUDITON_SPOLICY);
418 
419 	case A_GETKMASK:
420 		return (AUE_AUDITON_GETKMASK);
421 
422 	case A_SETKMASK:
423 		return (AUE_AUDITON_SETKMASK);
424 
425 	case A_GETQCTRL:
426 		return (AUE_AUDITON_GQCTRL);
427 
428 	case A_SETQCTRL:
429 		return (AUE_AUDITON_SQCTRL);
430 
431 	case A_GETCWD:
432 		return (AUE_AUDITON_GETCWD);
433 
434 	case A_GETCAR:
435 		return (AUE_AUDITON_GETCAR);
436 
437 	case A_GETSTAT:
438 		return (AUE_AUDITON_GETSTAT);
439 
440 	case A_SETSTAT:
441 		return (AUE_AUDITON_SETSTAT);
442 
443 	case A_SETUMASK:
444 		return (AUE_AUDITON_SETUMASK);
445 
446 	case A_SETSMASK:
447 		return (AUE_AUDITON_SETSMASK);
448 
449 	case A_GETCOND:
450 		return (AUE_AUDITON_GETCOND);
451 
452 	case A_SETCOND:
453 		return (AUE_AUDITON_SETCOND);
454 
455 	case A_GETCLASS:
456 		return (AUE_AUDITON_GETCLASS);
457 
458 	case A_SETCLASS:
459 		return (AUE_AUDITON_SETCLASS);
460 
461 	case A_GETPINFO:
462 	case A_SETPMASK:
463 	case A_SETFSIZE:
464 	case A_GETFSIZE:
465 	case A_GETPINFO_ADDR:
466 	case A_GETKAUDIT:
467 	case A_SETKAUDIT:
468 	default:
469 		return (AUE_AUDITON);	/* No special record */
470 	}
471 }
472 
473 /*
474  * Create a canonical path from given path by prefixing either the root
475  * directory, or the current working directory.  If the process working
476  * directory is NULL, we could use 'rootvnode' to obtain the root directory,
477  * but this results in a volfs name written to the audit log. So we will
478  * leave the filename starting with '/' in the audit log in this case.
479  *
480  * XXXRW: Since we combine two paths here, ideally a buffer of size
481  * MAXPATHLEN * 2 would be passed in.
482  */
483 void
484 audit_canon_path(struct thread *td, char *path, char *cpath)
485 {
486 	char *bufp;
487 	char *retbuf, *freebuf;
488 	struct vnode *vnp;
489 	struct filedesc *fdp;
490 	int cisr, error, vfslocked;
491 
492 	WITNESS_WARN(WARN_GIANTOK | WARN_SLEEPOK, NULL,
493 	    "audit_canon_path() at %s:%d", __FILE__, __LINE__);
494 
495 	fdp = td->td_proc->p_fd;
496 	bufp = path;
497 	cisr = 0;
498 	FILEDESC_SLOCK(fdp);
499 	if (*(path) == '/') {
500 		while (*(bufp) == '/')
501 			bufp++;			/* Skip leading '/'s. */
502 		/*
503 		 * If no process root, or it is the same as the system root,
504 		 * audit the path as passed in with a single '/'.
505 		 */
506 		if ((fdp->fd_rdir == NULL) ||
507 		    (fdp->fd_rdir == rootvnode)) {
508 			vnp = NULL;
509 			bufp--;			/* Restore one '/'. */
510 		} else {
511 			vnp = fdp->fd_rdir;	/* Use process root. */
512 			vref(vnp);
513 		}
514 	} else {
515 		vnp = fdp->fd_cdir;	/* Prepend the current dir. */
516 		cisr = (fdp->fd_rdir == fdp->fd_cdir);
517 		vref(vnp);
518 		bufp = path;
519 	}
520 	FILEDESC_SUNLOCK(fdp);
521 	if (vnp != NULL) {
522 		/*
523 		 * XXX: vn_fullpath() on FreeBSD is "less reliable" than
524 		 * vn_getpath() on Darwin, so this will need more attention
525 		 * in the future.  Also, the question and string bounding
526 		 * here seems a bit questionable and will also require
527 		 * attention.
528 		 */
529 		vfslocked = VFS_LOCK_GIANT(vnp->v_mount);
530 		vn_lock(vnp, LK_EXCLUSIVE | LK_RETRY);
531 		error = vn_fullpath(td, vnp, &retbuf, &freebuf);
532 		if (error == 0) {
533 			/* Copy and free buffer allocated by vn_fullpath().
534 			 * If the current working directory was the same as
535 			 * the root directory, and the path was a relative
536 			 * pathname, do not separate the two components with
537 			 * the '/' character.
538 			 */
539 			snprintf(cpath, MAXPATHLEN, "%s%s%s", retbuf,
540 			    cisr ? "" : "/", bufp);
541 			free(freebuf, M_TEMP);
542 		} else
543 			cpath[0] = '\0';
544 		vput(vnp);
545 		VFS_UNLOCK_GIANT(vfslocked);
546 	} else
547 		strlcpy(cpath, bufp, MAXPATHLEN);
548 }
549