Index: head/sys/security/audit/audit_bsm.c =================================================================== --- head/sys/security/audit/audit_bsm.c (revision 316017) +++ head/sys/security/audit/audit_bsm.c (revision 316018) @@ -1,1696 +1,1703 @@ /* * Copyright (c) 1999-2009 Apple Inc. + * Copyright (c) 2016-2017 Robert N. M. Watson * All rights reserved. * + * Portions of this software were developed by BAE Systems, the University of + * Cambridge Computer Laboratory, and Memorial University under DARPA/AFRL + * contract FA8650-15-C-7558 ("CADETS"), as part of the DARPA Transparent + * Computing (TC) research program. + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of Apple Inc. ("Apple") nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include MALLOC_DEFINE(M_AUDITBSM, "audit_bsm", "Audit BSM data"); static void audit_sys_auditon(struct audit_record *ar, struct au_record *rec); /* * Initialize the BSM auditing subsystem. */ void kau_init(void) { au_evclassmap_init(); + au_evnamemap_init(); } /* * This call reserves memory for the audit record. Memory must be guaranteed * before any auditable event can be generated. The au_record structure * maintains a reference to the memory allocated above and also the list of * tokens associated with this record. */ static struct au_record * kau_open(void) { struct au_record *rec; rec = malloc(sizeof(*rec), M_AUDITBSM, M_WAITOK); rec->data = NULL; TAILQ_INIT(&rec->token_q); rec->len = 0; rec->used = 1; return (rec); } /* * Store the token with the record descriptor. */ static void kau_write(struct au_record *rec, struct au_token *tok) { KASSERT(tok != NULL, ("kau_write: tok == NULL")); TAILQ_INSERT_TAIL(&rec->token_q, tok, tokens); rec->len += tok->len; } /* * Close out the audit record by adding the header token, identifying any * missing tokens. Write out the tokens to the record memory. */ static void kau_close(struct au_record *rec, struct timespec *ctime, short event) { u_char *dptr; size_t tot_rec_size; token_t *cur, *hdr, *trail; struct timeval tm; size_t hdrsize; struct auditinfo_addr ak; struct in6_addr *ap; audit_get_kinfo(&ak); hdrsize = 0; switch (ak.ai_termid.at_type) { case AU_IPv4: hdrsize = (ak.ai_termid.at_addr[0] == INADDR_ANY) ? AUDIT_HEADER_SIZE : AUDIT_HEADER_EX_SIZE(&ak); break; case AU_IPv6: ap = (struct in6_addr *)&ak.ai_termid.at_addr[0]; hdrsize = (IN6_IS_ADDR_UNSPECIFIED(ap)) ? AUDIT_HEADER_SIZE : AUDIT_HEADER_EX_SIZE(&ak); break; default: panic("kau_close: invalid address family"); } tot_rec_size = rec->len + hdrsize + AUDIT_TRAILER_SIZE; rec->data = malloc(tot_rec_size, M_AUDITBSM, M_WAITOK | M_ZERO); tm.tv_usec = ctime->tv_nsec / 1000; tm.tv_sec = ctime->tv_sec; if (hdrsize != AUDIT_HEADER_SIZE) hdr = au_to_header32_ex_tm(tot_rec_size, event, 0, tm, &ak); else hdr = au_to_header32_tm(tot_rec_size, event, 0, tm); TAILQ_INSERT_HEAD(&rec->token_q, hdr, tokens); trail = au_to_trailer(tot_rec_size); TAILQ_INSERT_TAIL(&rec->token_q, trail, tokens); rec->len = tot_rec_size; dptr = rec->data; TAILQ_FOREACH(cur, &rec->token_q, tokens) { memcpy(dptr, cur->t_data, cur->len); dptr += cur->len; } } /* * Free a BSM audit record by releasing all the tokens and clearing the audit * record information. */ void kau_free(struct au_record *rec) { struct au_token *tok; /* Free the token list. */ while ((tok = TAILQ_FIRST(&rec->token_q))) { TAILQ_REMOVE(&rec->token_q, tok, tokens); free(tok->t_data, M_AUDITBSM); free(tok, M_AUDITBSM); } rec->used = 0; rec->len = 0; free(rec->data, M_AUDITBSM); free(rec, M_AUDITBSM); } /* * XXX: May want turn some (or all) of these macros into functions in order * to reduce the generated code size. * * XXXAUDIT: These macros assume that 'kar', 'ar', 'rec', and 'tok' in the * caller are OK with this. */ #define ATFD1_TOKENS(argnum) do { \ if (ARG_IS_VALID(kar, ARG_ATFD1)) { \ tok = au_to_arg32(argnum, "at fd 1", ar->ar_arg_atfd1); \ kau_write(rec, tok); \ } \ } while (0) #define ATFD2_TOKENS(argnum) do { \ if (ARG_IS_VALID(kar, ARG_ATFD2)) { \ tok = au_to_arg32(argnum, "at fd 2", ar->ar_arg_atfd2); \ kau_write(rec, tok); \ } \ } while (0) #define UPATH1_TOKENS do { \ if (ARG_IS_VALID(kar, ARG_UPATH1)) { \ tok = au_to_path(ar->ar_arg_upath1); \ kau_write(rec, tok); \ } \ } while (0) #define UPATH2_TOKENS do { \ if (ARG_IS_VALID(kar, ARG_UPATH2)) { \ tok = au_to_path(ar->ar_arg_upath2); \ kau_write(rec, tok); \ } \ } while (0) #define VNODE1_TOKENS do { \ if (ARG_IS_VALID(kar, ARG_ATFD)) { \ tok = au_to_arg32(1, "at fd", ar->ar_arg_atfd); \ kau_write(rec, tok); \ } \ if (ARG_IS_VALID(kar, ARG_VNODE1)) { \ tok = au_to_attr32(&ar->ar_arg_vnode1); \ kau_write(rec, tok); \ } \ } while (0) #define UPATH1_VNODE1_TOKENS do { \ UPATH1_TOKENS; \ if (ARG_IS_VALID(kar, ARG_VNODE1)) { \ tok = au_to_attr32(&ar->ar_arg_vnode1); \ kau_write(rec, tok); \ } \ } while (0) #define VNODE2_TOKENS do { \ if (ARG_IS_VALID(kar, ARG_VNODE2)) { \ tok = au_to_attr32(&ar->ar_arg_vnode2); \ kau_write(rec, tok); \ } \ } while (0) #define FD_VNODE1_TOKENS do { \ if (ARG_IS_VALID(kar, ARG_VNODE1)) { \ if (ARG_IS_VALID(kar, ARG_FD)) { \ tok = au_to_arg32(1, "fd", ar->ar_arg_fd); \ kau_write(rec, tok); \ } \ tok = au_to_attr32(&ar->ar_arg_vnode1); \ kau_write(rec, tok); \ } else { \ if (ARG_IS_VALID(kar, ARG_FD)) { \ tok = au_to_arg32(1, "non-file: fd", \ ar->ar_arg_fd); \ kau_write(rec, tok); \ } \ } \ } while (0) #define PROCESS_PID_TOKENS(argn) do { \ if ((ar->ar_arg_pid > 0) /* Reference a single process */ \ && (ARG_IS_VALID(kar, ARG_PROCESS))) { \ tok = au_to_process32_ex(ar->ar_arg_auid, \ ar->ar_arg_euid, ar->ar_arg_egid, \ ar->ar_arg_ruid, ar->ar_arg_rgid, \ ar->ar_arg_pid, ar->ar_arg_asid, \ &ar->ar_arg_termid_addr); \ kau_write(rec, tok); \ } else if (ARG_IS_VALID(kar, ARG_PID)) { \ tok = au_to_arg32(argn, "process", ar->ar_arg_pid); \ kau_write(rec, tok); \ } \ } while (0) #define EXTATTR_TOKENS(namespace_argnum) do { \ if (ARG_IS_VALID(kar, ARG_VALUE)) { \ switch (ar->ar_arg_value) { \ case EXTATTR_NAMESPACE_USER: \ tok = au_to_text(EXTATTR_NAMESPACE_USER_STRING);\ break; \ case EXTATTR_NAMESPACE_SYSTEM: \ tok = au_to_text(EXTATTR_NAMESPACE_SYSTEM_STRING);\ break; \ default: \ tok = au_to_arg32((namespace_argnum), \ "attrnamespace", ar->ar_arg_value); \ break; \ } \ kau_write(rec, tok); \ } \ /* attrname is in the text field */ \ if (ARG_IS_VALID(kar, ARG_TEXT)) { \ tok = au_to_text(ar->ar_arg_text); \ kau_write(rec, tok); \ } \ } while (0) /* * Not all pointer arguments to system calls are of interest, but in some * cases they reflect delegation of rights, such as mmap(2) followed by * minherit(2) before execve(2), so do the best we can. */ #define ADDR_TOKEN(argnum, argname) do { \ if (ARG_IS_VALID(kar, ARG_ADDR)) { \ if (sizeof(void *) == sizeof(uint32_t)) \ tok = au_to_arg32((argnum), (argname), \ (uint32_t)(uintptr_t)ar->ar_arg_addr); \ else \ tok = au_to_arg64((argnum), (argname), \ (uint64_t)(uintptr_t)ar->ar_arg_addr); \ kau_write(rec, tok); \ } \ } while (0) /* * Implement auditing for the auditon() system call. The audit tokens that * are generated depend on the command that was sent into the auditon() * system call. */ static void audit_sys_auditon(struct audit_record *ar, struct au_record *rec) { struct au_token *tok; tok = au_to_arg32(3, "length", ar->ar_arg_len); kau_write(rec, tok); switch (ar->ar_arg_cmd) { case A_OLDSETPOLICY: if ((size_t)ar->ar_arg_len == sizeof(int64_t)) { tok = au_to_arg64(2, "policy", ar->ar_arg_auditon.au_policy64); kau_write(rec, tok); break; } /* FALLTHROUGH */ case A_SETPOLICY: tok = au_to_arg32(2, "policy", ar->ar_arg_auditon.au_policy); kau_write(rec, tok); break; case A_SETKMASK: tok = au_to_arg32(2, "setkmask:as_success", ar->ar_arg_auditon.au_mask.am_success); kau_write(rec, tok); tok = au_to_arg32(2, "setkmask:as_failure", ar->ar_arg_auditon.au_mask.am_failure); kau_write(rec, tok); break; case A_OLDSETQCTRL: if ((size_t)ar->ar_arg_len == sizeof(au_qctrl64_t)) { tok = au_to_arg64(2, "setqctrl:aq_hiwater", ar->ar_arg_auditon.au_qctrl64.aq64_hiwater); kau_write(rec, tok); tok = au_to_arg64(2, "setqctrl:aq_lowater", ar->ar_arg_auditon.au_qctrl64.aq64_lowater); kau_write(rec, tok); tok = au_to_arg64(2, "setqctrl:aq_bufsz", ar->ar_arg_auditon.au_qctrl64.aq64_bufsz); kau_write(rec, tok); tok = au_to_arg64(2, "setqctrl:aq_delay", ar->ar_arg_auditon.au_qctrl64.aq64_delay); kau_write(rec, tok); tok = au_to_arg64(2, "setqctrl:aq_minfree", ar->ar_arg_auditon.au_qctrl64.aq64_minfree); kau_write(rec, tok); break; } /* FALLTHROUGH */ case A_SETQCTRL: tok = au_to_arg32(2, "setqctrl:aq_hiwater", ar->ar_arg_auditon.au_qctrl.aq_hiwater); kau_write(rec, tok); tok = au_to_arg32(2, "setqctrl:aq_lowater", ar->ar_arg_auditon.au_qctrl.aq_lowater); kau_write(rec, tok); tok = au_to_arg32(2, "setqctrl:aq_bufsz", ar->ar_arg_auditon.au_qctrl.aq_bufsz); kau_write(rec, tok); tok = au_to_arg32(2, "setqctrl:aq_delay", ar->ar_arg_auditon.au_qctrl.aq_delay); kau_write(rec, tok); tok = au_to_arg32(2, "setqctrl:aq_minfree", ar->ar_arg_auditon.au_qctrl.aq_minfree); kau_write(rec, tok); break; case A_SETUMASK: tok = au_to_arg32(2, "setumask:as_success", ar->ar_arg_auditon.au_auinfo.ai_mask.am_success); kau_write(rec, tok); tok = au_to_arg32(2, "setumask:as_failure", ar->ar_arg_auditon.au_auinfo.ai_mask.am_failure); kau_write(rec, tok); break; case A_SETSMASK: tok = au_to_arg32(2, "setsmask:as_success", ar->ar_arg_auditon.au_auinfo.ai_mask.am_success); kau_write(rec, tok); tok = au_to_arg32(2, "setsmask:as_failure", ar->ar_arg_auditon.au_auinfo.ai_mask.am_failure); kau_write(rec, tok); break; case A_OLDSETCOND: if ((size_t)ar->ar_arg_len == sizeof(int64_t)) { tok = au_to_arg64(2, "setcond", ar->ar_arg_auditon.au_cond64); kau_write(rec, tok); break; } /* FALLTHROUGH */ case A_SETCOND: tok = au_to_arg32(2, "setcond", ar->ar_arg_auditon.au_cond); kau_write(rec, tok); break; case A_SETCLASS: kau_write(rec, tok); tok = au_to_arg32(2, "setclass:ec_event", ar->ar_arg_auditon.au_evclass.ec_number); kau_write(rec, tok); tok = au_to_arg32(2, "setclass:ec_class", ar->ar_arg_auditon.au_evclass.ec_class); kau_write(rec, tok); break; case A_SETPMASK: tok = au_to_arg32(2, "setpmask:as_success", ar->ar_arg_auditon.au_aupinfo.ap_mask.am_success); kau_write(rec, tok); tok = au_to_arg32(2, "setpmask:as_failure", ar->ar_arg_auditon.au_aupinfo.ap_mask.am_failure); kau_write(rec, tok); break; case A_SETFSIZE: tok = au_to_arg32(2, "setfsize:filesize", ar->ar_arg_auditon.au_fstat.af_filesz); kau_write(rec, tok); break; default: break; } } /* * Convert an internal kernel audit record to a BSM record and return a * success/failure indicator. The BSM record is passed as an out parameter to * this function. * * Return conditions: * BSM_SUCCESS: The BSM record is valid * BSM_FAILURE: Failure; the BSM record is NULL. * BSM_NOAUDIT: The event is not auditable for BSM; the BSM record is NULL. */ int kaudit_to_bsm(struct kaudit_record *kar, struct au_record **pau) { struct au_token *tok, *subj_tok, *jail_tok; struct au_record *rec; au_tid_t tid; struct audit_record *ar; int ctr; KASSERT(kar != NULL, ("kaudit_to_bsm: kar == NULL")); *pau = NULL; ar = &kar->k_ar; rec = kau_open(); /* * Create the subject token. If this credential was jailed be sure to * generate a zonename token. */ if (ar->ar_jailname[0] != '\0') jail_tok = au_to_zonename(ar->ar_jailname); else jail_tok = NULL; switch (ar->ar_subj_term_addr.at_type) { case AU_IPv4: tid.port = ar->ar_subj_term_addr.at_port; tid.machine = ar->ar_subj_term_addr.at_addr[0]; subj_tok = au_to_subject32(ar->ar_subj_auid, /* audit ID */ ar->ar_subj_cred.cr_uid, /* eff uid */ ar->ar_subj_egid, /* eff group id */ ar->ar_subj_ruid, /* real uid */ ar->ar_subj_rgid, /* real group id */ ar->ar_subj_pid, /* process id */ ar->ar_subj_asid, /* session ID */ &tid); break; case AU_IPv6: subj_tok = au_to_subject32_ex(ar->ar_subj_auid, ar->ar_subj_cred.cr_uid, ar->ar_subj_egid, ar->ar_subj_ruid, ar->ar_subj_rgid, ar->ar_subj_pid, ar->ar_subj_asid, &ar->ar_subj_term_addr); break; default: bzero(&tid, sizeof(tid)); subj_tok = au_to_subject32(ar->ar_subj_auid, ar->ar_subj_cred.cr_uid, ar->ar_subj_egid, ar->ar_subj_ruid, ar->ar_subj_rgid, ar->ar_subj_pid, ar->ar_subj_asid, &tid); } /* * The logic inside each case fills in the tokens required for the * event, except for the header, trailer, and return tokens. The * header and trailer tokens are added by the kau_close() function. * The return token is added outside of the switch statement. */ switch(ar->ar_event) { case AUE_ACCEPT: case AUE_BIND: case AUE_LISTEN: case AUE_CONNECT: case AUE_RECV: case AUE_RECVFROM: case AUE_RECVMSG: case AUE_SEND: case AUE_SENDFILE: case AUE_SENDMSG: case AUE_SENDTO: /* * Socket-related events. */ if (ARG_IS_VALID(kar, ARG_FD)) { tok = au_to_arg32(1, "fd", ar->ar_arg_fd); kau_write(rec, tok); } if (ARG_IS_VALID(kar, ARG_SADDRINET)) { tok = au_to_sock_inet((struct sockaddr_in *) &ar->ar_arg_sockaddr); kau_write(rec, tok); } if (ARG_IS_VALID(kar, ARG_SADDRUNIX)) { tok = au_to_sock_unix((struct sockaddr_un *) &ar->ar_arg_sockaddr); kau_write(rec, tok); UPATH1_TOKENS; } /* XXX Need to handle ARG_SADDRINET6 */ break; case AUE_BINDAT: case AUE_CONNECTAT: ATFD1_TOKENS(1); if (ARG_IS_VALID(kar, ARG_FD)) { tok = au_to_arg32(2, "fd", ar->ar_arg_fd); kau_write(rec, tok); } if (ARG_IS_VALID(kar, ARG_SADDRUNIX)) { tok = au_to_sock_unix((struct sockaddr_un *) &ar->ar_arg_sockaddr); kau_write(rec, tok); UPATH1_TOKENS; } break; case AUE_SOCKET: case AUE_SOCKETPAIR: if (ARG_IS_VALID(kar, ARG_SOCKINFO)) { tok = au_to_arg32(1, "domain", ar->ar_arg_sockinfo.so_domain); kau_write(rec, tok); tok = au_to_arg32(2, "type", ar->ar_arg_sockinfo.so_type); kau_write(rec, tok); tok = au_to_arg32(3, "protocol", ar->ar_arg_sockinfo.so_protocol); kau_write(rec, tok); } break; case AUE_SETSOCKOPT: case AUE_SHUTDOWN: if (ARG_IS_VALID(kar, ARG_FD)) { tok = au_to_arg32(1, "fd", ar->ar_arg_fd); kau_write(rec, tok); } break; case AUE_ACCT: if (ARG_IS_VALID(kar, ARG_UPATH1)) { UPATH1_VNODE1_TOKENS; } else { tok = au_to_arg32(1, "accounting off", 0); kau_write(rec, tok); } break; case AUE_SETAUID: if (ARG_IS_VALID(kar, ARG_AUID)) { tok = au_to_arg32(2, "setauid", ar->ar_arg_auid); kau_write(rec, tok); } break; case AUE_SETAUDIT: if (ARG_IS_VALID(kar, ARG_AUID) && ARG_IS_VALID(kar, ARG_ASID) && ARG_IS_VALID(kar, ARG_AMASK) && ARG_IS_VALID(kar, ARG_TERMID)) { tok = au_to_arg32(1, "setaudit:auid", ar->ar_arg_auid); kau_write(rec, tok); tok = au_to_arg32(1, "setaudit:port", ar->ar_arg_termid.port); kau_write(rec, tok); tok = au_to_arg32(1, "setaudit:machine", ar->ar_arg_termid.machine); kau_write(rec, tok); tok = au_to_arg32(1, "setaudit:as_success", ar->ar_arg_amask.am_success); kau_write(rec, tok); tok = au_to_arg32(1, "setaudit:as_failure", ar->ar_arg_amask.am_failure); kau_write(rec, tok); tok = au_to_arg32(1, "setaudit:asid", ar->ar_arg_asid); kau_write(rec, tok); } break; case AUE_SETAUDIT_ADDR: if (ARG_IS_VALID(kar, ARG_AUID) && ARG_IS_VALID(kar, ARG_ASID) && ARG_IS_VALID(kar, ARG_AMASK) && ARG_IS_VALID(kar, ARG_TERMID_ADDR)) { tok = au_to_arg32(1, "setaudit_addr:auid", ar->ar_arg_auid); kau_write(rec, tok); tok = au_to_arg32(1, "setaudit_addr:as_success", ar->ar_arg_amask.am_success); kau_write(rec, tok); tok = au_to_arg32(1, "setaudit_addr:as_failure", ar->ar_arg_amask.am_failure); kau_write(rec, tok); tok = au_to_arg32(1, "setaudit_addr:asid", ar->ar_arg_asid); kau_write(rec, tok); tok = au_to_arg32(1, "setaudit_addr:type", ar->ar_arg_termid_addr.at_type); kau_write(rec, tok); tok = au_to_arg32(1, "setaudit_addr:port", ar->ar_arg_termid_addr.at_port); kau_write(rec, tok); if (ar->ar_arg_termid_addr.at_type == AU_IPv6) tok = au_to_in_addr_ex((struct in6_addr *) &ar->ar_arg_termid_addr.at_addr[0]); if (ar->ar_arg_termid_addr.at_type == AU_IPv4) tok = au_to_in_addr((struct in_addr *) &ar->ar_arg_termid_addr.at_addr[0]); kau_write(rec, tok); } break; case AUE_AUDITON: /* * For AUDITON commands without own event, audit the cmd. */ if (ARG_IS_VALID(kar, ARG_CMD)) { tok = au_to_arg32(1, "cmd", ar->ar_arg_cmd); kau_write(rec, tok); } /* FALLTHROUGH */ case AUE_AUDITON_GETCAR: case AUE_AUDITON_GETCLASS: case AUE_AUDITON_GETCOND: case AUE_AUDITON_GETCWD: case AUE_AUDITON_GETKMASK: case AUE_AUDITON_GETSTAT: case AUE_AUDITON_GPOLICY: case AUE_AUDITON_GQCTRL: case AUE_AUDITON_SETCLASS: case AUE_AUDITON_SETCOND: case AUE_AUDITON_SETKMASK: case AUE_AUDITON_SETSMASK: case AUE_AUDITON_SETSTAT: case AUE_AUDITON_SETUMASK: case AUE_AUDITON_SPOLICY: case AUE_AUDITON_SQCTRL: if (ARG_IS_VALID(kar, ARG_AUDITON)) audit_sys_auditon(ar, rec); break; case AUE_AUDITCTL: UPATH1_VNODE1_TOKENS; break; case AUE_EXIT: if (ARG_IS_VALID(kar, ARG_EXIT)) { tok = au_to_exit(ar->ar_arg_exitretval, ar->ar_arg_exitstatus); kau_write(rec, tok); } break; case AUE_ADJTIME: case AUE_CLOCK_SETTIME: case AUE_AUDIT: case AUE_DUP2: case AUE_GETAUDIT: case AUE_GETAUDIT_ADDR: case AUE_GETAUID: case AUE_GETCWD: case AUE_GETFSSTAT: case AUE_GETRESUID: case AUE_GETRESGID: case AUE_KQUEUE: case AUE_MODLOAD: case AUE_MODUNLOAD: case AUE_MSGSYS: case AUE_NTP_ADJTIME: case AUE_PIPE: case AUE_POSIX_OPENPT: case AUE_PROFILE: case AUE_RTPRIO: case AUE_SEMSYS: case AUE_SHMSYS: case AUE_SETPGRP: case AUE_SETRLIMIT: case AUE_SETSID: case AUE_SETTIMEOFDAY: case AUE_SYSARCH: /* * Header, subject, and return tokens added at end. */ break; case AUE_CHDIR: case AUE_CHROOT: case AUE_FSTATAT: case AUE_FUTIMESAT: case AUE_GETATTRLIST: case AUE_JAIL: case AUE_LUTIMES: case AUE_NFS_GETFH: case AUE_LSTAT: case AUE_LPATHCONF: case AUE_PATHCONF: case AUE_READLINK: case AUE_READLINKAT: case AUE_REVOKE: case AUE_RMDIR: case AUE_SEARCHFS: case AUE_SETATTRLIST: case AUE_STAT: case AUE_STATFS: case AUE_SWAPON: case AUE_SWAPOFF: case AUE_TRUNCATE: case AUE_UNDELETE: case AUE_UNLINK: case AUE_UNLINKAT: case AUE_UTIMES: ATFD1_TOKENS(1); UPATH1_VNODE1_TOKENS; break; case AUE_ACCESS: case AUE_EACCESS: case AUE_FACCESSAT: ATFD1_TOKENS(1); UPATH1_VNODE1_TOKENS; if (ARG_IS_VALID(kar, ARG_VALUE)) { tok = au_to_arg32(2, "mode", ar->ar_arg_value); kau_write(rec, tok); } break; case AUE_FHSTATFS: case AUE_FHOPEN: case AUE_FHSTAT: /* XXXRW: Need to audit vnode argument. */ break; case AUE_CHFLAGS: case AUE_LCHFLAGS: if (ARG_IS_VALID(kar, ARG_FFLAGS)) { tok = au_to_arg32(2, "flags", ar->ar_arg_fflags); kau_write(rec, tok); } UPATH1_VNODE1_TOKENS; break; case AUE_CHMOD: case AUE_LCHMOD: if (ARG_IS_VALID(kar, ARG_MODE)) { tok = au_to_arg32(2, "new file mode", ar->ar_arg_mode); kau_write(rec, tok); } UPATH1_VNODE1_TOKENS; break; case AUE_FCHMODAT: ATFD1_TOKENS(1); if (ARG_IS_VALID(kar, ARG_MODE)) { tok = au_to_arg32(3, "new file mode", ar->ar_arg_mode); kau_write(rec, tok); } UPATH1_VNODE1_TOKENS; break; case AUE_CHOWN: case AUE_LCHOWN: if (ARG_IS_VALID(kar, ARG_UID)) { tok = au_to_arg32(2, "new file uid", ar->ar_arg_uid); kau_write(rec, tok); } if (ARG_IS_VALID(kar, ARG_GID)) { tok = au_to_arg32(3, "new file gid", ar->ar_arg_gid); kau_write(rec, tok); } UPATH1_VNODE1_TOKENS; break; case AUE_FCHOWNAT: ATFD1_TOKENS(1); if (ARG_IS_VALID(kar, ARG_UID)) { tok = au_to_arg32(3, "new file uid", ar->ar_arg_uid); kau_write(rec, tok); } if (ARG_IS_VALID(kar, ARG_GID)) { tok = au_to_arg32(4, "new file gid", ar->ar_arg_gid); kau_write(rec, tok); } UPATH1_VNODE1_TOKENS; break; case AUE_EXCHANGEDATA: UPATH1_VNODE1_TOKENS; UPATH2_TOKENS; break; case AUE_CLOSE: if (ARG_IS_VALID(kar, ARG_FD)) { tok = au_to_arg32(1, "fd", ar->ar_arg_fd); kau_write(rec, tok); } UPATH1_VNODE1_TOKENS; break; case AUE_CLOSEFROM: if (ARG_IS_VALID(kar, ARG_FD)) { tok = au_to_arg32(1, "fd", ar->ar_arg_fd); kau_write(rec, tok); } break; case AUE_CORE: if (ARG_IS_VALID(kar, ARG_SIGNUM)) { tok = au_to_arg32(1, "signal", ar->ar_arg_signum); kau_write(rec, tok); } UPATH1_VNODE1_TOKENS; break; case AUE_EXTATTRCTL: UPATH1_VNODE1_TOKENS; if (ARG_IS_VALID(kar, ARG_CMD)) { tok = au_to_arg32(2, "cmd", ar->ar_arg_cmd); kau_write(rec, tok); } /* extattrctl(2) filename parameter is in upath2/vnode2 */ UPATH2_TOKENS; VNODE2_TOKENS; EXTATTR_TOKENS(4); break; case AUE_EXTATTR_GET_FILE: case AUE_EXTATTR_SET_FILE: case AUE_EXTATTR_LIST_FILE: case AUE_EXTATTR_DELETE_FILE: case AUE_EXTATTR_GET_LINK: case AUE_EXTATTR_SET_LINK: case AUE_EXTATTR_LIST_LINK: case AUE_EXTATTR_DELETE_LINK: UPATH1_VNODE1_TOKENS; EXTATTR_TOKENS(2); break; case AUE_EXTATTR_GET_FD: case AUE_EXTATTR_SET_FD: case AUE_EXTATTR_LIST_FD: case AUE_EXTATTR_DELETE_FD: if (ARG_IS_VALID(kar, ARG_FD)) { tok = au_to_arg32(2, "fd", ar->ar_arg_fd); kau_write(rec, tok); } EXTATTR_TOKENS(2); break; case AUE_FEXECVE: if (ARG_IS_VALID(kar, ARG_FD)) { tok = au_to_arg32(1, "fd", ar->ar_arg_fd); kau_write(rec, tok); } /* FALLTHROUGH */ case AUE_EXECVE: case AUE_MAC_EXECVE: if (ARG_IS_VALID(kar, ARG_ARGV)) { tok = au_to_exec_args(ar->ar_arg_argv, ar->ar_arg_argc); kau_write(rec, tok); } if (ARG_IS_VALID(kar, ARG_ENVV)) { tok = au_to_exec_env(ar->ar_arg_envv, ar->ar_arg_envc); kau_write(rec, tok); } UPATH1_VNODE1_TOKENS; break; case AUE_FCHMOD: if (ARG_IS_VALID(kar, ARG_MODE)) { tok = au_to_arg32(2, "new file mode", ar->ar_arg_mode); kau_write(rec, tok); } FD_VNODE1_TOKENS; break; /* * XXXRW: Some of these need to handle non-vnode cases as well. */ case AUE_FCHDIR: case AUE_FPATHCONF: case AUE_FSTAT: case AUE_FSTATFS: case AUE_FSYNC: case AUE_FTRUNCATE: case AUE_FUTIMES: case AUE_GETDIRENTRIES: case AUE_GETDIRENTRIESATTR: case AUE_LSEEK: case AUE_POLL: case AUE_PREAD: case AUE_PWRITE: case AUE_READ: case AUE_READV: case AUE_WRITE: case AUE_WRITEV: FD_VNODE1_TOKENS; break; case AUE_FCHOWN: if (ARG_IS_VALID(kar, ARG_UID)) { tok = au_to_arg32(2, "new file uid", ar->ar_arg_uid); kau_write(rec, tok); } if (ARG_IS_VALID(kar, ARG_GID)) { tok = au_to_arg32(3, "new file gid", ar->ar_arg_gid); kau_write(rec, tok); } FD_VNODE1_TOKENS; break; case AUE_FCNTL: if (ARG_IS_VALID(kar, ARG_CMD)) { tok = au_to_arg32(2, "cmd", au_fcntl_cmd_to_bsm(ar->ar_arg_cmd)); kau_write(rec, tok); } FD_VNODE1_TOKENS; break; case AUE_FCHFLAGS: if (ARG_IS_VALID(kar, ARG_FFLAGS)) { tok = au_to_arg32(2, "flags", ar->ar_arg_fflags); kau_write(rec, tok); } FD_VNODE1_TOKENS; break; case AUE_FLOCK: if (ARG_IS_VALID(kar, ARG_CMD)) { tok = au_to_arg32(2, "operation", ar->ar_arg_cmd); kau_write(rec, tok); } FD_VNODE1_TOKENS; break; case AUE_RFORK: if (ARG_IS_VALID(kar, ARG_FFLAGS)) { tok = au_to_arg32(1, "flags", ar->ar_arg_fflags); kau_write(rec, tok); } /* FALLTHROUGH */ case AUE_FORK: case AUE_VFORK: if (ARG_IS_VALID(kar, ARG_PID)) { tok = au_to_arg32(0, "child PID", ar->ar_arg_pid); kau_write(rec, tok); } break; case AUE_IOCTL: if (ARG_IS_VALID(kar, ARG_CMD)) { tok = au_to_arg32(2, "cmd", ar->ar_arg_cmd); kau_write(rec, tok); } if (ARG_IS_VALID(kar, ARG_VNODE1)) FD_VNODE1_TOKENS; else { if (ARG_IS_VALID(kar, ARG_SOCKINFO)) { tok = kau_to_socket(&ar->ar_arg_sockinfo); kau_write(rec, tok); } else { if (ARG_IS_VALID(kar, ARG_FD)) { tok = au_to_arg32(1, "fd", ar->ar_arg_fd); kau_write(rec, tok); } } } break; case AUE_KILL: case AUE_KILLPG: if (ARG_IS_VALID(kar, ARG_SIGNUM)) { tok = au_to_arg32(2, "signal", ar->ar_arg_signum); kau_write(rec, tok); } PROCESS_PID_TOKENS(1); break; case AUE_KTRACE: if (ARG_IS_VALID(kar, ARG_CMD)) { tok = au_to_arg32(2, "ops", ar->ar_arg_cmd); kau_write(rec, tok); } if (ARG_IS_VALID(kar, ARG_VALUE)) { tok = au_to_arg32(3, "trpoints", ar->ar_arg_value); kau_write(rec, tok); } PROCESS_PID_TOKENS(4); UPATH1_VNODE1_TOKENS; break; case AUE_LINK: case AUE_LINKAT: case AUE_RENAME: case AUE_RENAMEAT: ATFD1_TOKENS(1); UPATH1_VNODE1_TOKENS; ATFD2_TOKENS(3); UPATH2_TOKENS; break; case AUE_LOADSHFILE: ADDR_TOKEN(4, "base addr"); UPATH1_VNODE1_TOKENS; break; case AUE_MKDIR: case AUE_MKDIRAT: case AUE_MKFIFO: case AUE_MKFIFOAT: ATFD1_TOKENS(1); if (ARG_IS_VALID(kar, ARG_MODE)) { tok = au_to_arg32(2, "mode", ar->ar_arg_mode); kau_write(rec, tok); } UPATH1_VNODE1_TOKENS; break; case AUE_MKNOD: case AUE_MKNODAT: ATFD1_TOKENS(1); if (ARG_IS_VALID(kar, ARG_MODE)) { tok = au_to_arg32(2, "mode", ar->ar_arg_mode); kau_write(rec, tok); } if (ARG_IS_VALID(kar, ARG_DEV)) { tok = au_to_arg32(3, "dev", ar->ar_arg_dev); kau_write(rec, tok); } UPATH1_VNODE1_TOKENS; break; case AUE_MMAP: case AUE_MUNMAP: case AUE_MPROTECT: case AUE_MLOCK: case AUE_MUNLOCK: case AUE_MINHERIT: ADDR_TOKEN(1, "addr"); if (ARG_IS_VALID(kar, ARG_LEN)) { tok = au_to_arg32(2, "len", ar->ar_arg_len); kau_write(rec, tok); } if (ar->ar_event == AUE_MMAP) FD_VNODE1_TOKENS; if (ar->ar_event == AUE_MPROTECT) { if (ARG_IS_VALID(kar, ARG_VALUE)) { tok = au_to_arg32(3, "protection", ar->ar_arg_value); kau_write(rec, tok); } } if (ar->ar_event == AUE_MINHERIT) { if (ARG_IS_VALID(kar, ARG_VALUE)) { tok = au_to_arg32(3, "inherit", ar->ar_arg_value); kau_write(rec, tok); } } break; case AUE_MOUNT: case AUE_NMOUNT: /* XXX Need to handle NFS mounts */ if (ARG_IS_VALID(kar, ARG_FFLAGS)) { tok = au_to_arg32(3, "flags", ar->ar_arg_fflags); kau_write(rec, tok); } if (ARG_IS_VALID(kar, ARG_TEXT)) { tok = au_to_text(ar->ar_arg_text); kau_write(rec, tok); } /* FALLTHROUGH */ case AUE_NFS_SVC: if (ARG_IS_VALID(kar, ARG_CMD)) { tok = au_to_arg32(1, "flags", ar->ar_arg_cmd); kau_write(rec, tok); } break; case AUE_UMOUNT: if (ARG_IS_VALID(kar, ARG_VALUE)) { tok = au_to_arg32(2, "flags", ar->ar_arg_value); kau_write(rec, tok); } UPATH1_VNODE1_TOKENS; if (ARG_IS_VALID(kar, ARG_TEXT)) { tok = au_to_text(ar->ar_arg_text); kau_write(rec, tok); } break; case AUE_MSGCTL: ar->ar_event = audit_msgctl_to_event(ar->ar_arg_svipc_cmd); /* Fall through */ case AUE_MSGRCV: case AUE_MSGSND: tok = au_to_arg32(1, "msg ID", ar->ar_arg_svipc_id); kau_write(rec, tok); if (ar->ar_errno != EINVAL) { tok = au_to_ipc(AT_IPC_MSG, ar->ar_arg_svipc_id); kau_write(rec, tok); } break; case AUE_MSGGET: if (ar->ar_errno == 0) { if (ARG_IS_VALID(kar, ARG_SVIPC_ID)) { tok = au_to_ipc(AT_IPC_MSG, ar->ar_arg_svipc_id); kau_write(rec, tok); } } break; case AUE_RESETSHFILE: ADDR_TOKEN(1, "base addr"); break; case AUE_OPEN_RC: case AUE_OPEN_RTC: case AUE_OPEN_RWC: case AUE_OPEN_RWTC: case AUE_OPEN_WC: case AUE_OPEN_WTC: case AUE_CREAT: if (ARG_IS_VALID(kar, ARG_MODE)) { tok = au_to_arg32(3, "mode", ar->ar_arg_mode); kau_write(rec, tok); } /* FALLTHROUGH */ case AUE_OPEN_R: case AUE_OPEN_RT: case AUE_OPEN_RW: case AUE_OPEN_RWT: case AUE_OPEN_W: case AUE_OPEN_WT: if (ARG_IS_VALID(kar, ARG_FFLAGS)) { tok = au_to_arg32(2, "flags", ar->ar_arg_fflags); kau_write(rec, tok); } UPATH1_VNODE1_TOKENS; break; case AUE_OPENAT_RC: case AUE_OPENAT_RTC: case AUE_OPENAT_RWC: case AUE_OPENAT_RWTC: case AUE_OPENAT_WC: case AUE_OPENAT_WTC: if (ARG_IS_VALID(kar, ARG_MODE)) { tok = au_to_arg32(3, "mode", ar->ar_arg_mode); kau_write(rec, tok); } /* FALLTHROUGH */ case AUE_OPENAT_R: case AUE_OPENAT_RT: case AUE_OPENAT_RW: case AUE_OPENAT_RWT: case AUE_OPENAT_W: case AUE_OPENAT_WT: if (ARG_IS_VALID(kar, ARG_FFLAGS)) { tok = au_to_arg32(2, "flags", ar->ar_arg_fflags); kau_write(rec, tok); } ATFD1_TOKENS(1); UPATH1_VNODE1_TOKENS; break; case AUE_PTRACE: if (ARG_IS_VALID(kar, ARG_CMD)) { tok = au_to_arg32(1, "request", ar->ar_arg_cmd); kau_write(rec, tok); } if (ARG_IS_VALID(kar, ARG_VALUE)) { tok = au_to_arg32(4, "data", ar->ar_arg_value); kau_write(rec, tok); } PROCESS_PID_TOKENS(2); break; case AUE_QUOTACTL: if (ARG_IS_VALID(kar, ARG_CMD)) { tok = au_to_arg32(2, "command", ar->ar_arg_cmd); kau_write(rec, tok); } if (ARG_IS_VALID(kar, ARG_UID)) { tok = au_to_arg32(3, "uid", ar->ar_arg_uid); kau_write(rec, tok); } if (ARG_IS_VALID(kar, ARG_GID)) { tok = au_to_arg32(3, "gid", ar->ar_arg_gid); kau_write(rec, tok); } UPATH1_VNODE1_TOKENS; break; case AUE_REBOOT: if (ARG_IS_VALID(kar, ARG_CMD)) { tok = au_to_arg32(1, "howto", ar->ar_arg_cmd); kau_write(rec, tok); } break; case AUE_SEMCTL: ar->ar_event = audit_semctl_to_event(ar->ar_arg_svipc_cmd); /* Fall through */ case AUE_SEMOP: if (ARG_IS_VALID(kar, ARG_SVIPC_ID)) { tok = au_to_arg32(1, "sem ID", ar->ar_arg_svipc_id); kau_write(rec, tok); if (ar->ar_errno != EINVAL) { tok = au_to_ipc(AT_IPC_SEM, ar->ar_arg_svipc_id); kau_write(rec, tok); } } break; case AUE_SEMGET: if (ar->ar_errno == 0) { if (ARG_IS_VALID(kar, ARG_SVIPC_ID)) { tok = au_to_ipc(AT_IPC_SEM, ar->ar_arg_svipc_id); kau_write(rec, tok); } } break; case AUE_SETEGID: if (ARG_IS_VALID(kar, ARG_EGID)) { tok = au_to_arg32(1, "egid", ar->ar_arg_egid); kau_write(rec, tok); } break; case AUE_SETEUID: if (ARG_IS_VALID(kar, ARG_EUID)) { tok = au_to_arg32(1, "euid", ar->ar_arg_euid); kau_write(rec, tok); } break; case AUE_SETREGID: if (ARG_IS_VALID(kar, ARG_RGID)) { tok = au_to_arg32(1, "rgid", ar->ar_arg_rgid); kau_write(rec, tok); } if (ARG_IS_VALID(kar, ARG_EGID)) { tok = au_to_arg32(2, "egid", ar->ar_arg_egid); kau_write(rec, tok); } break; case AUE_SETREUID: if (ARG_IS_VALID(kar, ARG_RUID)) { tok = au_to_arg32(1, "ruid", ar->ar_arg_ruid); kau_write(rec, tok); } if (ARG_IS_VALID(kar, ARG_EUID)) { tok = au_to_arg32(2, "euid", ar->ar_arg_euid); kau_write(rec, tok); } break; case AUE_SETRESGID: if (ARG_IS_VALID(kar, ARG_RGID)) { tok = au_to_arg32(1, "rgid", ar->ar_arg_rgid); kau_write(rec, tok); } if (ARG_IS_VALID(kar, ARG_EGID)) { tok = au_to_arg32(2, "egid", ar->ar_arg_egid); kau_write(rec, tok); } if (ARG_IS_VALID(kar, ARG_SGID)) { tok = au_to_arg32(3, "sgid", ar->ar_arg_sgid); kau_write(rec, tok); } break; case AUE_SETRESUID: if (ARG_IS_VALID(kar, ARG_RUID)) { tok = au_to_arg32(1, "ruid", ar->ar_arg_ruid); kau_write(rec, tok); } if (ARG_IS_VALID(kar, ARG_EUID)) { tok = au_to_arg32(2, "euid", ar->ar_arg_euid); kau_write(rec, tok); } if (ARG_IS_VALID(kar, ARG_SUID)) { tok = au_to_arg32(3, "suid", ar->ar_arg_suid); kau_write(rec, tok); } break; case AUE_SETGID: if (ARG_IS_VALID(kar, ARG_GID)) { tok = au_to_arg32(1, "gid", ar->ar_arg_gid); kau_write(rec, tok); } break; case AUE_SETUID: if (ARG_IS_VALID(kar, ARG_UID)) { tok = au_to_arg32(1, "uid", ar->ar_arg_uid); kau_write(rec, tok); } break; case AUE_SETGROUPS: if (ARG_IS_VALID(kar, ARG_GROUPSET)) { for(ctr = 0; ctr < ar->ar_arg_groups.gidset_size; ctr++) { tok = au_to_arg32(1, "setgroups", ar->ar_arg_groups.gidset[ctr]); kau_write(rec, tok); } } break; case AUE_SETLOGIN: if (ARG_IS_VALID(kar, ARG_LOGIN)) { tok = au_to_text(ar->ar_arg_login); kau_write(rec, tok); } break; case AUE_SETPRIORITY: if (ARG_IS_VALID(kar, ARG_CMD)) { tok = au_to_arg32(1, "which", ar->ar_arg_cmd); kau_write(rec, tok); } if (ARG_IS_VALID(kar, ARG_UID)) { tok = au_to_arg32(2, "who", ar->ar_arg_uid); kau_write(rec, tok); } PROCESS_PID_TOKENS(2); if (ARG_IS_VALID(kar, ARG_VALUE)) { tok = au_to_arg32(3, "priority", ar->ar_arg_value); kau_write(rec, tok); } break; case AUE_SETPRIVEXEC: if (ARG_IS_VALID(kar, ARG_VALUE)) { tok = au_to_arg32(1, "flag", ar->ar_arg_value); kau_write(rec, tok); } break; /* AUE_SHMAT, AUE_SHMCTL, AUE_SHMDT and AUE_SHMGET are SysV IPC */ case AUE_SHMAT: if (ARG_IS_VALID(kar, ARG_SVIPC_ID)) { tok = au_to_arg32(1, "shmid", ar->ar_arg_svipc_id); kau_write(rec, tok); /* XXXAUDIT: Does having the ipc token make sense? */ tok = au_to_ipc(AT_IPC_SHM, ar->ar_arg_svipc_id); kau_write(rec, tok); } if (ARG_IS_VALID(kar, ARG_SVIPC_ADDR)) { tok = au_to_arg32(2, "shmaddr", (int)(uintptr_t)ar->ar_arg_svipc_addr); kau_write(rec, tok); } if (ARG_IS_VALID(kar, ARG_SVIPC_PERM)) { tok = au_to_ipc_perm(&ar->ar_arg_svipc_perm); kau_write(rec, tok); } break; case AUE_SHMCTL: if (ARG_IS_VALID(kar, ARG_SVIPC_ID)) { tok = au_to_arg32(1, "shmid", ar->ar_arg_svipc_id); kau_write(rec, tok); /* XXXAUDIT: Does having the ipc token make sense? */ tok = au_to_ipc(AT_IPC_SHM, ar->ar_arg_svipc_id); kau_write(rec, tok); } switch (ar->ar_arg_svipc_cmd) { case IPC_STAT: ar->ar_event = AUE_SHMCTL_STAT; break; case IPC_RMID: ar->ar_event = AUE_SHMCTL_RMID; break; case IPC_SET: ar->ar_event = AUE_SHMCTL_SET; if (ARG_IS_VALID(kar, ARG_SVIPC_PERM)) { tok = au_to_ipc_perm(&ar->ar_arg_svipc_perm); kau_write(rec, tok); } break; default: break; /* We will audit a bad command */ } break; case AUE_SHMDT: if (ARG_IS_VALID(kar, ARG_SVIPC_ADDR)) { tok = au_to_arg32(1, "shmaddr", (int)(uintptr_t)ar->ar_arg_svipc_addr); kau_write(rec, tok); } break; case AUE_SHMGET: /* This is unusual; the return value is in an argument token */ if (ARG_IS_VALID(kar, ARG_SVIPC_ID)) { tok = au_to_arg32(0, "shmid", ar->ar_arg_svipc_id); kau_write(rec, tok); tok = au_to_ipc(AT_IPC_SHM, ar->ar_arg_svipc_id); kau_write(rec, tok); } if (ARG_IS_VALID(kar, ARG_SVIPC_PERM)) { tok = au_to_ipc_perm(&ar->ar_arg_svipc_perm); kau_write(rec, tok); } break; /* AUE_SHMOPEN, AUE_SHMUNLINK, AUE_SEMOPEN, AUE_SEMCLOSE * and AUE_SEMUNLINK are Posix IPC */ case AUE_SHMOPEN: if (ARG_IS_VALID(kar, ARG_SVIPC_ADDR)) { tok = au_to_arg32(2, "flags", ar->ar_arg_fflags); kau_write(rec, tok); } if (ARG_IS_VALID(kar, ARG_MODE)) { tok = au_to_arg32(3, "mode", ar->ar_arg_mode); kau_write(rec, tok); } /* FALLTHROUGH */ case AUE_SHMUNLINK: if (ARG_IS_VALID(kar, ARG_TEXT)) { tok = au_to_text(ar->ar_arg_text); kau_write(rec, tok); } if (ARG_IS_VALID(kar, ARG_POSIX_IPC_PERM)) { struct ipc_perm perm; perm.uid = ar->ar_arg_pipc_perm.pipc_uid; perm.gid = ar->ar_arg_pipc_perm.pipc_gid; perm.cuid = ar->ar_arg_pipc_perm.pipc_uid; perm.cgid = ar->ar_arg_pipc_perm.pipc_gid; perm.mode = ar->ar_arg_pipc_perm.pipc_mode; perm.seq = 0; perm.key = 0; tok = au_to_ipc_perm(&perm); kau_write(rec, tok); } break; case AUE_SEMOPEN: if (ARG_IS_VALID(kar, ARG_FFLAGS)) { tok = au_to_arg32(2, "flags", ar->ar_arg_fflags); kau_write(rec, tok); } if (ARG_IS_VALID(kar, ARG_MODE)) { tok = au_to_arg32(3, "mode", ar->ar_arg_mode); kau_write(rec, tok); } if (ARG_IS_VALID(kar, ARG_VALUE)) { tok = au_to_arg32(4, "value", ar->ar_arg_value); kau_write(rec, tok); } /* FALLTHROUGH */ case AUE_SEMUNLINK: if (ARG_IS_VALID(kar, ARG_TEXT)) { tok = au_to_text(ar->ar_arg_text); kau_write(rec, tok); } if (ARG_IS_VALID(kar, ARG_POSIX_IPC_PERM)) { struct ipc_perm perm; perm.uid = ar->ar_arg_pipc_perm.pipc_uid; perm.gid = ar->ar_arg_pipc_perm.pipc_gid; perm.cuid = ar->ar_arg_pipc_perm.pipc_uid; perm.cgid = ar->ar_arg_pipc_perm.pipc_gid; perm.mode = ar->ar_arg_pipc_perm.pipc_mode; perm.seq = 0; perm.key = 0; tok = au_to_ipc_perm(&perm); kau_write(rec, tok); } break; case AUE_SEMCLOSE: if (ARG_IS_VALID(kar, ARG_FD)) { tok = au_to_arg32(1, "sem", ar->ar_arg_fd); kau_write(rec, tok); } break; case AUE_SYMLINK: case AUE_SYMLINKAT: if (ARG_IS_VALID(kar, ARG_TEXT)) { tok = au_to_text(ar->ar_arg_text); kau_write(rec, tok); } ATFD1_TOKENS(1); UPATH1_VNODE1_TOKENS; break; case AUE_SYSCTL: case AUE_SYSCTL_NONADMIN: if (ARG_IS_VALID(kar, ARG_CTLNAME | ARG_LEN)) { for (ctr = 0; ctr < ar->ar_arg_len; ctr++) { tok = au_to_arg32(1, "name", ar->ar_arg_ctlname[ctr]); kau_write(rec, tok); } } if (ARG_IS_VALID(kar, ARG_VALUE)) { tok = au_to_arg32(5, "newval", ar->ar_arg_value); kau_write(rec, tok); } if (ARG_IS_VALID(kar, ARG_TEXT)) { tok = au_to_text(ar->ar_arg_text); kau_write(rec, tok); } break; case AUE_UMASK: if (ARG_IS_VALID(kar, ARG_MASK)) { tok = au_to_arg32(1, "new mask", ar->ar_arg_mask); kau_write(rec, tok); } tok = au_to_arg32(0, "prev mask", ar->ar_retval); kau_write(rec, tok); break; case AUE_WAIT4: case AUE_WAIT6: PROCESS_PID_TOKENS(1); if (ARG_IS_VALID(kar, ARG_VALUE)) { tok = au_to_arg32(3, "options", ar->ar_arg_value); kau_write(rec, tok); } break; case AUE_CAP_RIGHTS_LIMIT: /* * XXXRW/XXXJA: Would be nice to audit socket/etc information. */ FD_VNODE1_TOKENS; if (ARG_IS_VALID(kar, ARG_RIGHTS)) { tok = au_to_rights(&ar->ar_arg_rights); kau_write(rec, tok); } break; case AUE_CAP_FCNTLS_GET: case AUE_CAP_IOCTLS_GET: case AUE_CAP_IOCTLS_LIMIT: case AUE_CAP_RIGHTS_GET: if (ARG_IS_VALID(kar, ARG_FD)) { tok = au_to_arg32(1, "fd", ar->ar_arg_fd); kau_write(rec, tok); } break; case AUE_CAP_FCNTLS_LIMIT: FD_VNODE1_TOKENS; if (ARG_IS_VALID(kar, ARG_FCNTL_RIGHTS)) { tok = au_to_arg32(2, "fcntlrights", ar->ar_arg_fcntl_rights); kau_write(rec, tok); } break; case AUE_CAP_ENTER: case AUE_CAP_GETMODE: break; case AUE_NULL: default: printf("BSM conversion requested for unknown event %d\n", ar->ar_event); /* * Write the subject token so it is properly freed here. */ if (jail_tok != NULL) kau_write(rec, jail_tok); kau_write(rec, subj_tok); kau_free(rec); return (BSM_NOAUDIT); } if (jail_tok != NULL) kau_write(rec, jail_tok); kau_write(rec, subj_tok); tok = au_to_return32(au_errno_to_bsm(ar->ar_errno), ar->ar_retval); kau_write(rec, tok); /* Every record gets a return token */ kau_close(rec, &ar->ar_endtime, ar->ar_event); *pau = rec; return (BSM_SUCCESS); } /* * Verify that a record is a valid BSM record. This verification is simple * now, but may be expanded on sometime in the future. Return 1 if the * record is good, 0 otherwise. */ int bsm_rec_verify(void *rec) { char c = *(char *)rec; /* * Check the token ID of the first token; it has to be a header * token. * * XXXAUDIT There needs to be a token structure to map a token. * XXXAUDIT 'Shouldn't be simply looking at the first char. */ if ((c != AUT_HEADER32) && (c != AUT_HEADER32_EX) && (c != AUT_HEADER64) && (c != AUT_HEADER64_EX)) return (0); return (1); } Index: head/sys/security/audit/audit_bsm_klib.c =================================================================== --- head/sys/security/audit/audit_bsm_klib.c (revision 316017) +++ head/sys/security/audit/audit_bsm_klib.c (revision 316018) @@ -1,571 +1,712 @@ /* * Copyright (c) 1999-2009 Apple Inc. - * Copyright (c) 2005 Robert N. M. Watson + * Copyright (c) 2005, 2016 Robert N. M. Watson * All rights reserved. * + * Portions of this software were developed by BAE Systems, the University of + * Cambridge Computer Laboratory, and Memorial University under DARPA/AFRL + * contract FA8650-15-C-7558 ("CADETS"), as part of the DARPA Transparent + * Computing (TC) research program. + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of Apple Inc. ("Apple") nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include +#include #include #include #include #include #include #include #include #include /* * Hash table functions for the audit event number to event class mask * mapping. */ #define EVCLASSMAP_HASH_TABLE_SIZE 251 struct evclass_elem { au_event_t event; au_class_t class; LIST_ENTRY(evclass_elem) entry; }; struct evclass_list { LIST_HEAD(, evclass_elem) head; }; static MALLOC_DEFINE(M_AUDITEVCLASS, "audit_evclass", "Audit event class"); static struct rwlock evclass_lock; static struct evclass_list evclass_hash[EVCLASSMAP_HASH_TABLE_SIZE]; #define EVCLASS_LOCK_INIT() rw_init(&evclass_lock, "evclass_lock") #define EVCLASS_RLOCK() rw_rlock(&evclass_lock) #define EVCLASS_RUNLOCK() rw_runlock(&evclass_lock) #define EVCLASS_WLOCK() rw_wlock(&evclass_lock) #define EVCLASS_WUNLOCK() rw_wunlock(&evclass_lock) +/* + * Hash table maintaining a mapping from audit event numbers to audit event + * names. For now, used only by DTrace, but present always so that userspace + * tools can register and inspect fields consistently even if DTrace is not + * present. + * + * struct evname_elem is defined in audit_private.h so that audit_dtrace.c can + * use the definition. + */ +#define EVNAMEMAP_HASH_TABLE_SIZE 251 +struct evname_list { + LIST_HEAD(, evname_elem) enl_head; +}; + +static MALLOC_DEFINE(M_AUDITEVNAME, "audit_evname", "Audit event name"); +static struct sx evnamemap_lock; +static struct evname_list evnamemap_hash[EVNAMEMAP_HASH_TABLE_SIZE]; + +#define EVNAMEMAP_LOCK_INIT() sx_init(&evnamemap_lock, "evnamemap_lock"); +#define EVNAMEMAP_RLOCK() sx_slock(&evnamemap_lock) +#define EVNAMEMAP_RUNLOCK() sx_sunlock(&evnamemap_lock) +#define EVNAMEMAP_WLOCK() sx_xlock(&evnamemap_lock) +#define EVNAMEMAP_WUNLOCK() sx_xunlock(&evnamemap_lock) + struct aue_open_event { int aoe_flags; au_event_t aoe_event; }; static const struct aue_open_event aue_open[] = { { O_RDONLY, AUE_OPEN_R }, { (O_RDONLY | O_CREAT), AUE_OPEN_RC }, { (O_RDONLY | O_CREAT | O_TRUNC), AUE_OPEN_RTC }, { (O_RDONLY | O_TRUNC), AUE_OPEN_RT }, { O_RDWR, AUE_OPEN_RW }, { (O_RDWR | O_CREAT), AUE_OPEN_RWC }, { (O_RDWR | O_CREAT | O_TRUNC), AUE_OPEN_RWTC }, { (O_RDWR | O_TRUNC), AUE_OPEN_RWT }, { O_WRONLY, AUE_OPEN_W }, { (O_WRONLY | O_CREAT), AUE_OPEN_WC }, { (O_WRONLY | O_CREAT | O_TRUNC), AUE_OPEN_WTC }, { (O_WRONLY | O_TRUNC), AUE_OPEN_WT }, }; static const struct aue_open_event aue_openat[] = { { O_RDONLY, AUE_OPENAT_R }, { (O_RDONLY | O_CREAT), AUE_OPENAT_RC }, { (O_RDONLY | O_CREAT | O_TRUNC), AUE_OPENAT_RTC }, { (O_RDONLY | O_TRUNC), AUE_OPENAT_RT }, { O_RDWR, AUE_OPENAT_RW }, { (O_RDWR | O_CREAT), AUE_OPENAT_RWC }, { (O_RDWR | O_CREAT | O_TRUNC), AUE_OPENAT_RWTC }, { (O_RDWR | O_TRUNC), AUE_OPENAT_RWT }, { O_WRONLY, AUE_OPENAT_W }, { (O_WRONLY | O_CREAT), AUE_OPENAT_WC }, { (O_WRONLY | O_CREAT | O_TRUNC), AUE_OPENAT_WTC }, { (O_WRONLY | O_TRUNC), AUE_OPENAT_WT }, }; /* * Look up the class for an audit event in the class mapping table. */ au_class_t au_event_class(au_event_t event) { struct evclass_list *evcl; struct evclass_elem *evc; au_class_t class; EVCLASS_RLOCK(); evcl = &evclass_hash[event % EVCLASSMAP_HASH_TABLE_SIZE]; class = 0; LIST_FOREACH(evc, &evcl->head, entry) { if (evc->event == event) { class = evc->class; goto out; } } out: EVCLASS_RUNLOCK(); return (class); } /* * Insert a event to class mapping. If the event already exists in the * mapping, then replace the mapping with the new one. * * XXX There is currently no constraints placed on the number of mappings. * May want to either limit to a number, or in terms of memory usage. */ void au_evclassmap_insert(au_event_t event, au_class_t class) { struct evclass_list *evcl; struct evclass_elem *evc, *evc_new; /* * Pessimistically, always allocate storage before acquiring mutex. * Free if there is already a mapping for this event. */ evc_new = malloc(sizeof(*evc), M_AUDITEVCLASS, M_WAITOK); EVCLASS_WLOCK(); evcl = &evclass_hash[event % EVCLASSMAP_HASH_TABLE_SIZE]; LIST_FOREACH(evc, &evcl->head, entry) { if (evc->event == event) { evc->class = class; EVCLASS_WUNLOCK(); free(evc_new, M_AUDITEVCLASS); return; } } evc = evc_new; evc->event = event; evc->class = class; LIST_INSERT_HEAD(&evcl->head, evc, entry); EVCLASS_WUNLOCK(); } void au_evclassmap_init(void) { int i; EVCLASS_LOCK_INIT(); for (i = 0; i < EVCLASSMAP_HASH_TABLE_SIZE; i++) LIST_INIT(&evclass_hash[i].head); /* * Set up the initial event to class mapping for system calls. * * XXXRW: Really, this should walk all possible audit events, not all * native ABI system calls, as there may be audit events reachable * only through non-native system calls. It also seems a shame to * frob the mutex this early. */ for (i = 0; i < SYS_MAXSYSCALL; i++) { if (sysent[i].sy_auevent != AUE_NULL) au_evclassmap_insert(sysent[i].sy_auevent, 0); } } /* * Check whether an event is aditable by comparing the mask of classes this * event is part of against the given mask. */ int au_preselect(au_event_t event, au_class_t class, au_mask_t *mask_p, int sorf) { au_class_t effmask = 0; if (mask_p == NULL) return (-1); /* * Perform the actual check of the masks against the event. */ if (sorf & AU_PRS_SUCCESS) effmask |= (mask_p->am_success & class); if (sorf & AU_PRS_FAILURE) effmask |= (mask_p->am_failure & class); if (effmask) return (1); else return (0); +} + +/* + * Look up the name for an audit event in the event-to-name mapping table. + */ +int +au_event_name(au_event_t event, char *name) +{ + struct evname_list *enl; + struct evname_elem *ene; + int error; + + error = ENOENT; + EVNAMEMAP_RLOCK(); + enl = &evnamemap_hash[event % EVNAMEMAP_HASH_TABLE_SIZE]; + LIST_FOREACH(ene, &enl->enl_head, ene_entry) { + if (ene->ene_event == event) { + strlcpy(name, ene->ene_name, EVNAMEMAP_NAME_SIZE); + error = 0; + goto out; + } + } +out: + EVNAMEMAP_RUNLOCK(); + return (error); +} + +/* + * Insert a event-to-name mapping. If the event already exists in the + * mapping, then replace the mapping with the new one. + * + * XXX There is currently no constraints placed on the number of mappings. + * May want to either limit to a number, or in terms of memory usage. + * + * XXXRW: Accepts truncated name -- but perhaps should return failure instead? + * + * XXXRW: It could be we need a way to remove existing names...? + * + * XXXRW: We handle collisions between numbers, but I wonder if we also need a + * way to handle name collisions, for DTrace, where probe names must be + * unique? + */ +void +au_evnamemap_insert(au_event_t event, const char *name) +{ + struct evname_list *enl; + struct evname_elem *ene, *ene_new; + + /* + * Pessimistically, always allocate storage before acquiring lock. + * Free if there is already a mapping for this event. + */ + ene_new = malloc(sizeof(*ene_new), M_AUDITEVNAME, M_WAITOK | M_ZERO); + EVNAMEMAP_WLOCK(); + enl = &evnamemap_hash[event % EVNAMEMAP_HASH_TABLE_SIZE]; + LIST_FOREACH(ene, &enl->enl_head, ene_entry) { + if (ene->ene_event == event) { + EVNAME_LOCK(ene); + (void)strlcpy(ene->ene_name, name, + sizeof(ene->ene_name)); + EVNAME_UNLOCK(ene); + EVNAMEMAP_WUNLOCK(); + free(ene_new, M_AUDITEVNAME); + return; + } + } + ene = ene_new; + mtx_init(&ene->ene_lock, "au_evnamemap", NULL, MTX_DEF); + ene->ene_event = event; + (void)strlcpy(ene->ene_name, name, sizeof(ene->ene_name)); + LIST_INSERT_HEAD(&enl->enl_head, ene, ene_entry); + EVNAMEMAP_WUNLOCK(); +} + +void +au_evnamemap_init(void) +{ + int i; + + EVNAMEMAP_LOCK_INIT(); + for (i = 0; i < EVNAMEMAP_HASH_TABLE_SIZE; i++) + LIST_INIT(&evnamemap_hash[i].enl_head); + + /* + * XXXRW: Unlike the event-to-class mapping, we don't attempt to + * pre-populate the list. Perhaps we should...? But not sure we + * really want to duplicate /etc/security/audit_event in the kernel + * -- and we'd need a way to remove names? + */ +} + +/* + * The DTrace audit provider occasionally needs to walk the entries in the + * event-to-name mapping table, and uses this public interface to do so. A + * write lock is acquired so that the provider can safely update its fields in + * table entries. + */ +void +au_evnamemap_foreach(au_evnamemap_callback_t callback) +{ + struct evname_list *enl; + struct evname_elem *ene; + int i; + + EVNAMEMAP_WLOCK(); + for (i = 0; i < EVNAMEMAP_HASH_TABLE_SIZE; i++) { + enl = &evnamemap_hash[i]; + LIST_FOREACH(ene, &enl->enl_head, ene_entry) + callback(ene); + } + EVNAMEMAP_WUNLOCK(); } /* * Convert sysctl names and present arguments to events. */ au_event_t audit_ctlname_to_sysctlevent(int name[], uint64_t valid_arg) { /* can't parse it - so return the worst case */ if ((valid_arg & (ARG_CTLNAME | ARG_LEN)) != (ARG_CTLNAME | ARG_LEN)) return (AUE_SYSCTL); switch (name[0]) { /* non-admin "lookups" treat them special */ case KERN_OSTYPE: case KERN_OSRELEASE: case KERN_OSREV: case KERN_VERSION: case KERN_ARGMAX: case KERN_CLOCKRATE: case KERN_BOOTTIME: case KERN_POSIX1: case KERN_NGROUPS: case KERN_JOB_CONTROL: case KERN_SAVED_IDS: case KERN_OSRELDATE: case KERN_DUMMY: return (AUE_SYSCTL_NONADMIN); /* only treat the changeable controls as admin */ case KERN_MAXVNODES: case KERN_MAXPROC: case KERN_MAXFILES: case KERN_MAXPROCPERUID: case KERN_MAXFILESPERPROC: case KERN_HOSTID: case KERN_SECURELVL: case KERN_HOSTNAME: case KERN_VNODE: case KERN_PROC: case KERN_FILE: case KERN_PROF: case KERN_NISDOMAINNAME: case KERN_UPDATEINTERVAL: case KERN_NTP_PLL: case KERN_BOOTFILE: case KERN_DUMPDEV: case KERN_IPC: case KERN_PS_STRINGS: case KERN_USRSTACK: case KERN_LOGSIGEXIT: case KERN_IOV_MAX: return ((valid_arg & ARG_VALUE) ? AUE_SYSCTL : AUE_SYSCTL_NONADMIN); default: return (AUE_SYSCTL); } /* NOTREACHED */ } /* * Convert an open flags specifier into a specific type of open event for * auditing purposes. */ au_event_t audit_flags_and_error_to_openevent(int oflags, int error) { int i; /* * Need to check only those flags we care about. */ oflags = oflags & (O_RDONLY | O_CREAT | O_TRUNC | O_RDWR | O_WRONLY); for (i = 0; i < nitems(aue_open); i++) { if (aue_open[i].aoe_flags == oflags) return (aue_open[i].aoe_event); } return (AUE_OPEN); } au_event_t audit_flags_and_error_to_openatevent(int oflags, int error) { int i; /* * Need to check only those flags we care about. */ oflags = oflags & (O_RDONLY | O_CREAT | O_TRUNC | O_RDWR | O_WRONLY); for (i = 0; i < nitems(aue_openat); i++) { if (aue_openat[i].aoe_flags == oflags) return (aue_openat[i].aoe_event); } return (AUE_OPENAT); } /* * Convert a MSGCTL command to a specific event. */ au_event_t audit_msgctl_to_event(int cmd) { switch (cmd) { case IPC_RMID: return (AUE_MSGCTL_RMID); case IPC_SET: return (AUE_MSGCTL_SET); case IPC_STAT: return (AUE_MSGCTL_STAT); default: /* We will audit a bad command. */ return (AUE_MSGCTL); } } /* * Convert a SEMCTL command to a specific event. */ au_event_t audit_semctl_to_event(int cmd) { switch (cmd) { case GETALL: return (AUE_SEMCTL_GETALL); case GETNCNT: return (AUE_SEMCTL_GETNCNT); case GETPID: return (AUE_SEMCTL_GETPID); case GETVAL: return (AUE_SEMCTL_GETVAL); case GETZCNT: return (AUE_SEMCTL_GETZCNT); case IPC_RMID: return (AUE_SEMCTL_RMID); case IPC_SET: return (AUE_SEMCTL_SET); case SETALL: return (AUE_SEMCTL_SETALL); case SETVAL: return (AUE_SEMCTL_SETVAL); case IPC_STAT: return (AUE_SEMCTL_STAT); default: /* We will audit a bad command. */ return (AUE_SEMCTL); } } /* * Convert a command for the auditon() system call to a audit event. */ au_event_t auditon_command_event(int cmd) { switch(cmd) { case A_GETPOLICY: return (AUE_AUDITON_GPOLICY); case A_SETPOLICY: return (AUE_AUDITON_SPOLICY); case A_GETKMASK: return (AUE_AUDITON_GETKMASK); case A_SETKMASK: return (AUE_AUDITON_SETKMASK); case A_GETQCTRL: return (AUE_AUDITON_GQCTRL); case A_SETQCTRL: return (AUE_AUDITON_SQCTRL); case A_GETCWD: return (AUE_AUDITON_GETCWD); case A_GETCAR: return (AUE_AUDITON_GETCAR); case A_GETSTAT: return (AUE_AUDITON_GETSTAT); case A_SETSTAT: return (AUE_AUDITON_SETSTAT); case A_SETUMASK: return (AUE_AUDITON_SETUMASK); case A_SETSMASK: return (AUE_AUDITON_SETSMASK); case A_GETCOND: return (AUE_AUDITON_GETCOND); case A_SETCOND: return (AUE_AUDITON_SETCOND); case A_GETCLASS: return (AUE_AUDITON_GETCLASS); case A_SETCLASS: return (AUE_AUDITON_SETCLASS); case A_GETPINFO: case A_SETPMASK: case A_SETFSIZE: case A_GETFSIZE: case A_GETPINFO_ADDR: case A_GETKAUDIT: case A_SETKAUDIT: default: return (AUE_AUDITON); /* No special record */ } } /* * Create a canonical path from given path by prefixing either the root * directory, or the current working directory. If the process working * directory is NULL, we could use 'rootvnode' to obtain the root directory, * but this results in a volfs name written to the audit log. So we will * leave the filename starting with '/' in the audit log in this case. */ void audit_canon_path(struct thread *td, int dirfd, char *path, char *cpath) { struct vnode *cvnp, *rvnp; char *rbuf, *fbuf, *copy; struct filedesc *fdp; struct sbuf sbf; cap_rights_t rights; int error, needslash; WITNESS_WARN(WARN_GIANTOK | WARN_SLEEPOK, NULL, "%s: at %s:%d", __func__, __FILE__, __LINE__); copy = path; rvnp = cvnp = NULL; fdp = td->td_proc->p_fd; FILEDESC_SLOCK(fdp); /* * Make sure that we handle the chroot(2) case. If there is an * alternate root directory, prepend it to the audited pathname. */ if (fdp->fd_rdir != NULL && fdp->fd_rdir != rootvnode) { rvnp = fdp->fd_rdir; vhold(rvnp); } /* * If the supplied path is relative, make sure we capture the current * working directory so we can prepend it to the supplied relative * path. */ if (*path != '/') { if (dirfd == AT_FDCWD) { cvnp = fdp->fd_cdir; vhold(cvnp); } else { /* XXX: fgetvp() that vhold()s vnode instead of vref()ing it would be better */ error = fgetvp(td, dirfd, cap_rights_init(&rights), &cvnp); if (error) { FILEDESC_SUNLOCK(fdp); cpath[0] = '\0'; if (rvnp != NULL) vdrop(rvnp); return; } vhold(cvnp); vrele(cvnp); } needslash = (fdp->fd_rdir != cvnp); } else { needslash = 1; } FILEDESC_SUNLOCK(fdp); /* * NB: We require that the supplied array be at least MAXPATHLEN bytes * long. If this is not the case, then we can run into serious trouble. */ (void) sbuf_new(&sbf, cpath, MAXPATHLEN, SBUF_FIXEDLEN); /* * Strip leading forward slashes. */ while (*copy == '/') copy++; /* * Make sure we handle chroot(2) and prepend the global path to these * environments. * * NB: vn_fullpath(9) on FreeBSD is less reliable than vn_getpath(9) * on Darwin. As a result, this may need some additional attention * in the future. */ if (rvnp != NULL) { error = vn_fullpath_global(td, rvnp, &rbuf, &fbuf); vdrop(rvnp); if (error) { cpath[0] = '\0'; if (cvnp != NULL) vdrop(cvnp); return; } (void) sbuf_cat(&sbf, rbuf); free(fbuf, M_TEMP); } if (cvnp != NULL) { error = vn_fullpath(td, cvnp, &rbuf, &fbuf); vdrop(cvnp); if (error) { cpath[0] = '\0'; return; } (void) sbuf_cat(&sbf, rbuf); free(fbuf, M_TEMP); } if (needslash) (void) sbuf_putc(&sbf, '/'); /* * Now that we have processed any alternate root and relative path * names, add the supplied pathname. */ (void) sbuf_cat(&sbf, copy); /* * One or more of the previous sbuf operations could have resulted in * the supplied buffer being overflowed. Check to see if this is the * case. */ if (sbuf_error(&sbf) != 0) { cpath[0] = '\0'; return; } sbuf_finish(&sbf); } Index: head/sys/security/audit/audit_private.h =================================================================== --- head/sys/security/audit/audit_private.h (revision 316017) +++ head/sys/security/audit/audit_private.h (revision 316018) @@ -1,431 +1,470 @@ /*- * Copyright (c) 1999-2009 Apple Inc. + * Copyright (c) 2016-2017 Robert N. M. Watson * All rights reserved. * + * Portions of this software were developed by BAE Systems, the University of + * Cambridge Computer Laboratory, and Memorial University under DARPA/AFRL + * contract FA8650-15-C-7558 ("CADETS"), as part of the DARPA Transparent + * Computing (TC) research program. + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of Apple Inc. ("Apple") nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * * $FreeBSD$ */ /* * This include file contains function prototypes and type definitions used * within the audit implementation. */ #ifndef _SECURITY_AUDIT_PRIVATE_H_ #define _SECURITY_AUDIT_PRIVATE_H_ #ifndef _KERNEL #error "no user-serviceable parts inside" #endif #include #include #include #include #ifdef MALLOC_DECLARE MALLOC_DECLARE(M_AUDITBSM); MALLOC_DECLARE(M_AUDITDATA); MALLOC_DECLARE(M_AUDITPATH); MALLOC_DECLARE(M_AUDITTEXT); MALLOC_DECLARE(M_AUDITGIDSET); #endif /* * Audit control variables that are usually set/read via system calls and * used to control various aspects of auditing. */ extern struct au_qctrl audit_qctrl; extern struct audit_fstat audit_fstat; extern struct au_mask audit_nae_mask; extern int audit_panic_on_write_fail; extern int audit_fail_stop; extern int audit_argv; extern int audit_arge; /* * Success/failure conditions for the conversion of a kernel audit record to * BSM format. */ #define BSM_SUCCESS 0 #define BSM_FAILURE 1 #define BSM_NOAUDIT 2 /* * Defines for the kernel audit record k_ar_commit field. Flags are set to * indicate what sort of record it is, and which preselection mechanism * selected it. */ #define AR_COMMIT_KERNEL 0x00000001U #define AR_COMMIT_USER 0x00000010U #define AR_PRESELECT_TRAIL 0x00001000U #define AR_PRESELECT_PIPE 0x00002000U #define AR_PRESELECT_USER_TRAIL 0x00004000U #define AR_PRESELECT_USER_PIPE 0x00008000U /* * Audit data is generated as a stream of struct audit_record structures, * linked by struct kaudit_record, and contain storage for possible audit so * that it will not need to be allocated during the processing of a system * call, both improving efficiency and avoiding sleeping at untimely moments. * This structure is converted to BSM format before being written to disk. */ struct vnode_au_info { mode_t vn_mode; uid_t vn_uid; gid_t vn_gid; dev_t vn_dev; long vn_fsid; long vn_fileid; long vn_gen; }; struct groupset { gid_t *gidset; u_int gidset_size; }; struct socket_au_info { int so_domain; int so_type; int so_protocol; in_addr_t so_raddr; /* Remote address if INET socket. */ in_addr_t so_laddr; /* Local address if INET socket. */ u_short so_rport; /* Remote port. */ u_short so_lport; /* Local port. */ }; /* * The following is used for A_OLDSETQCTRL and AU_OLDGETQCTRL and a 64-bit * userland. */ struct au_qctrl64 { u_int64_t aq64_hiwater; u_int64_t aq64_lowater; u_int64_t aq64_bufsz; u_int64_t aq64_delay; u_int64_t aq64_minfree; }; typedef struct au_qctrl64 au_qctrl64_t; union auditon_udata { char *au_path; int au_cond; int au_flags; int au_policy; int au_trigger; int64_t au_cond64; int64_t au_policy64; au_evclass_map_t au_evclass; au_mask_t au_mask; auditinfo_t au_auinfo; auditpinfo_t au_aupinfo; auditpinfo_addr_t au_aupinfo_addr; au_qctrl_t au_qctrl; au_qctrl64_t au_qctrl64; au_stat_t au_stat; au_fstat_t au_fstat; auditinfo_addr_t au_kau_info; + au_evname_map_t au_evname; }; struct posix_ipc_perm { uid_t pipc_uid; gid_t pipc_gid; mode_t pipc_mode; }; struct audit_record { /* Audit record header. */ u_int32_t ar_magic; int ar_event; int ar_retval; /* value returned to the process */ int ar_errno; /* return status of system call */ struct timespec ar_starttime; struct timespec ar_endtime; u_int64_t ar_valid_arg; /* Bitmask of valid arguments */ /* Audit subject information. */ struct xucred ar_subj_cred; uid_t ar_subj_ruid; gid_t ar_subj_rgid; gid_t ar_subj_egid; uid_t ar_subj_auid; /* Audit user ID */ pid_t ar_subj_asid; /* Audit session ID */ pid_t ar_subj_pid; struct au_tid ar_subj_term; struct au_tid_addr ar_subj_term_addr; struct au_mask ar_subj_amask; /* Operation arguments. */ uid_t ar_arg_euid; uid_t ar_arg_ruid; uid_t ar_arg_suid; gid_t ar_arg_egid; gid_t ar_arg_rgid; gid_t ar_arg_sgid; pid_t ar_arg_pid; pid_t ar_arg_asid; struct au_tid ar_arg_termid; struct au_tid_addr ar_arg_termid_addr; uid_t ar_arg_uid; uid_t ar_arg_auid; gid_t ar_arg_gid; struct groupset ar_arg_groups; int ar_arg_fd; int ar_arg_atfd1; int ar_arg_atfd2; int ar_arg_fflags; mode_t ar_arg_mode; int ar_arg_dev; long ar_arg_value; void *ar_arg_addr; int ar_arg_len; int ar_arg_mask; u_int ar_arg_signum; char ar_arg_login[MAXLOGNAME]; int ar_arg_ctlname[CTL_MAXNAME]; struct socket_au_info ar_arg_sockinfo; char *ar_arg_upath1; char *ar_arg_upath2; char *ar_arg_text; struct au_mask ar_arg_amask; struct vnode_au_info ar_arg_vnode1; struct vnode_au_info ar_arg_vnode2; int ar_arg_cmd; int ar_arg_svipc_cmd; struct ipc_perm ar_arg_svipc_perm; int ar_arg_svipc_id; void *ar_arg_svipc_addr; struct posix_ipc_perm ar_arg_pipc_perm; union auditon_udata ar_arg_auditon; char *ar_arg_argv; int ar_arg_argc; char *ar_arg_envv; int ar_arg_envc; int ar_arg_exitstatus; int ar_arg_exitretval; struct sockaddr_storage ar_arg_sockaddr; cap_rights_t ar_arg_rights; uint32_t ar_arg_fcntl_rights; char ar_jailname[MAXHOSTNAMELEN]; }; /* * Arguments in the audit record are initially not defined; flags are set to * indicate if they are present so they can be included in the audit log * stream only if defined. */ #define ARG_EUID 0x0000000000000001ULL #define ARG_RUID 0x0000000000000002ULL #define ARG_SUID 0x0000000000000004ULL #define ARG_EGID 0x0000000000000008ULL #define ARG_RGID 0x0000000000000010ULL #define ARG_SGID 0x0000000000000020ULL #define ARG_PID 0x0000000000000040ULL #define ARG_UID 0x0000000000000080ULL #define ARG_AUID 0x0000000000000100ULL #define ARG_GID 0x0000000000000200ULL #define ARG_FD 0x0000000000000400ULL #define ARG_POSIX_IPC_PERM 0x0000000000000800ULL #define ARG_FFLAGS 0x0000000000001000ULL #define ARG_MODE 0x0000000000002000ULL #define ARG_DEV 0x0000000000004000ULL #define ARG_ADDR 0x0000000000008000ULL #define ARG_LEN 0x0000000000010000ULL #define ARG_MASK 0x0000000000020000ULL #define ARG_SIGNUM 0x0000000000040000ULL #define ARG_LOGIN 0x0000000000080000ULL #define ARG_SADDRINET 0x0000000000100000ULL #define ARG_SADDRINET6 0x0000000000200000ULL #define ARG_SADDRUNIX 0x0000000000400000ULL #define ARG_TERMID_ADDR 0x0000000000400000ULL #define ARG_UNUSED2 0x0000000001000000ULL #define ARG_UPATH1 0x0000000002000000ULL #define ARG_UPATH2 0x0000000004000000ULL #define ARG_TEXT 0x0000000008000000ULL #define ARG_VNODE1 0x0000000010000000ULL #define ARG_VNODE2 0x0000000020000000ULL #define ARG_SVIPC_CMD 0x0000000040000000ULL #define ARG_SVIPC_PERM 0x0000000080000000ULL #define ARG_SVIPC_ID 0x0000000100000000ULL #define ARG_SVIPC_ADDR 0x0000000200000000ULL #define ARG_GROUPSET 0x0000000400000000ULL #define ARG_CMD 0x0000000800000000ULL #define ARG_SOCKINFO 0x0000001000000000ULL #define ARG_ASID 0x0000002000000000ULL #define ARG_TERMID 0x0000004000000000ULL #define ARG_AUDITON 0x0000008000000000ULL #define ARG_VALUE 0x0000010000000000ULL #define ARG_AMASK 0x0000020000000000ULL #define ARG_CTLNAME 0x0000040000000000ULL #define ARG_PROCESS 0x0000080000000000ULL #define ARG_MACHPORT1 0x0000100000000000ULL #define ARG_MACHPORT2 0x0000200000000000ULL #define ARG_EXIT 0x0000400000000000ULL #define ARG_IOVECSTR 0x0000800000000000ULL #define ARG_ARGV 0x0001000000000000ULL #define ARG_ENVV 0x0002000000000000ULL #define ARG_ATFD1 0x0004000000000000ULL #define ARG_ATFD2 0x0008000000000000ULL #define ARG_RIGHTS 0x0010000000000000ULL #define ARG_FCNTL_RIGHTS 0x0020000000000000ULL #define ARG_NONE 0x0000000000000000ULL #define ARG_ALL 0xFFFFFFFFFFFFFFFFULL #define ARG_IS_VALID(kar, arg) ((kar)->k_ar.ar_valid_arg & (arg)) #define ARG_SET_VALID(kar, arg) do { \ (kar)->k_ar.ar_valid_arg |= (arg); \ } while (0) #define ARG_CLEAR_VALID(kar, arg) do { \ (kar)->k_ar.ar_valid_arg &= ~(arg); \ } while (0) /* * In-kernel version of audit record; the basic record plus queue meta-data. * This record can also have a pointer set to some opaque data that will be * passed through to the audit writing mechanism. */ struct kaudit_record { struct audit_record k_ar; u_int32_t k_ar_commit; void *k_udata; /* User data. */ u_int k_ulen; /* User data length. */ struct uthread *k_uthread; /* Audited thread. */ TAILQ_ENTRY(kaudit_record) k_q; }; TAILQ_HEAD(kaudit_queue, kaudit_record); /* * Functions to manage the allocation, release, and commit of kernel audit * records. */ void audit_abort(struct kaudit_record *ar); void audit_commit(struct kaudit_record *ar, int error, int retval); struct kaudit_record *audit_new(int event, struct thread *td); /* * Functions relating to the conversion of internal kernel audit records to * the BSM file format. */ struct au_record; int kaudit_to_bsm(struct kaudit_record *kar, struct au_record **pau); int bsm_rec_verify(void *rec); /* * Kernel versions of the libbsm audit record functions. */ void kau_free(struct au_record *rec); void kau_init(void); /* * Return values for pre-selection and post-selection decisions. */ #define AU_PRS_SUCCESS 1 #define AU_PRS_FAILURE 2 #define AU_PRS_BOTH (AU_PRS_SUCCESS|AU_PRS_FAILURE) /* * Data structures relating to the kernel audit queue. Ideally, these might * be abstracted so that only accessor methods are exposed. */ extern struct mtx audit_mtx; extern struct cv audit_watermark_cv; extern struct cv audit_worker_cv; extern struct kaudit_queue audit_q; extern int audit_q_len; extern int audit_pre_q_len; extern int audit_in_failure; /* * Flags to use on audit files when opening and closing. */ #define AUDIT_OPEN_FLAGS (FWRITE | O_APPEND) #define AUDIT_CLOSE_FLAGS (FWRITE | O_APPEND) +/* + * Audit event-to-name mapping structure, maintained in audit_bsm_klib.c. It + * appears in this header so that the DTrace audit provider can dereference + * instances passed back in the au_evname_foreach() callbacks. Safe access to + * its fields rquires holding ene_lock (after it is visible in the global + * table). + * + * Locking: + * (c) - Constant after inserted in the global table + * (l) - Protected by ene_lock + * (m) - Protected by evnamemap_lock (audit_bsm_klib.c) + * (M) - Writes protected by evnamemap_lock; reads unprotected. + */ +struct evname_elem { + au_event_t ene_event; /* (c) */ + char ene_name[EVNAMEMAP_NAME_SIZE]; /* (l) */ + LIST_ENTRY(evname_elem) ene_entry; /* (m) */ + struct mtx ene_lock; +}; + +#define EVNAME_LOCK(ene) mtx_lock(&(ene)->ene_lock) +#define EVNAME_UNLOCK(ene) mtx_unlock(&(ene)->ene_lock) + +/* + * Callback function typedef for the same. + */ +typedef void (*au_evnamemap_callback_t)(struct evname_elem *ene); + #include #include #include /* * Some of the BSM tokenizer functions take different parameters in the * kernel implementations in order to save the copying of large kernel data * structures. The prototypes of these functions are declared here. */ token_t *kau_to_socket(struct socket_au_info *soi); /* * audit_klib prototypes */ int au_preselect(au_event_t event, au_class_t class, au_mask_t *mask_p, int sorf); void au_evclassmap_init(void); void au_evclassmap_insert(au_event_t event, au_class_t class); au_class_t au_event_class(au_event_t event); +void au_evnamemap_init(void); +void au_evnamemap_insert(au_event_t event, const char *name); +void au_evnamemap_foreach(au_evnamemap_callback_t callback); +int au_event_name(au_event_t event, char *name); au_event_t audit_ctlname_to_sysctlevent(int name[], uint64_t valid_arg); au_event_t audit_flags_and_error_to_openevent(int oflags, int error); au_event_t audit_flags_and_error_to_openatevent(int oflags, int error); au_event_t audit_msgctl_to_event(int cmd); au_event_t audit_semctl_to_event(int cmr); void audit_canon_path(struct thread *td, int dirfd, char *path, char *cpath); au_event_t auditon_command_event(int cmd); /* * Audit trigger events notify user space of kernel audit conditions * asynchronously. */ void audit_trigger_init(void); int audit_send_trigger(unsigned int trigger); /* * Accessor functions to manage global audit state. */ void audit_set_kinfo(struct auditinfo_addr *); void audit_get_kinfo(struct auditinfo_addr *); /* * General audit related functions. */ struct kaudit_record *currecord(void); void audit_free(struct kaudit_record *ar); void audit_shutdown(void *arg, int howto); void audit_rotate_vnode(struct ucred *cred, struct vnode *vp); void audit_worker_init(void); /* * Audit pipe functions. */ int audit_pipe_preselect(au_id_t auid, au_event_t event, au_class_t class, int sorf, int trail_select); void audit_pipe_submit(au_id_t auid, au_event_t event, au_class_t class, int sorf, int trail_select, void *record, u_int record_len); void audit_pipe_submit_user(void *record, u_int record_len); #endif /* ! _SECURITY_AUDIT_PRIVATE_H_ */ Index: head/sys/security/audit/audit_syscalls.c =================================================================== --- head/sys/security/audit/audit_syscalls.c (revision 316017) +++ head/sys/security/audit/audit_syscalls.c (revision 316018) @@ -1,873 +1,901 @@ /*- * Copyright (c) 1999-2009 Apple Inc. + * Copyright (c) 2016 Robert N. M. Watson * All rights reserved. * + * Portions of this software were developed by BAE Systems, the University of + * Cambridge Computer Laboratory, and Memorial University under DARPA/AFRL + * contract FA8650-15-C-7558 ("CADETS"), as part of the DARPA Transparent + * Computing (TC) research program. + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of Apple Inc. ("Apple") nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef AUDIT /* * System call to allow a user space application to submit a BSM audit record * to the kernel for inclusion in the audit log. This function does little * verification on the audit record that is submitted. * * XXXAUDIT: Audit preselection for user records does not currently work, * since we pre-select only based on the AUE_audit event type, not the event * type submitted as part of the user audit data. */ /* ARGSUSED */ int sys_audit(struct thread *td, struct audit_args *uap) { int error; void * rec; struct kaudit_record *ar; if (jailed(td->td_ucred)) return (ENOSYS); error = priv_check(td, PRIV_AUDIT_SUBMIT); if (error) return (error); if ((uap->length <= 0) || (uap->length > audit_qctrl.aq_bufsz)) return (EINVAL); ar = currecord(); /* * If there's no current audit record (audit() itself not audited) * commit the user audit record. */ if (ar == NULL) { /* * This is not very efficient; we're required to allocate a * complete kernel audit record just so the user record can * tag along. * * XXXAUDIT: Maybe AUE_AUDIT in the system call context and * special pre-select handling? */ td->td_ar = audit_new(AUE_NULL, td); if (td->td_ar == NULL) return (ENOTSUP); td->td_pflags |= TDP_AUDITREC; ar = td->td_ar; } if (uap->length > MAX_AUDIT_RECORD_SIZE) return (EINVAL); rec = malloc(uap->length, M_AUDITDATA, M_WAITOK); error = copyin(uap->record, rec, uap->length); if (error) goto free_out; /* Verify the record. */ if (bsm_rec_verify(rec) == 0) { error = EINVAL; goto free_out; } #ifdef MAC error = mac_system_check_audit(td->td_ucred, rec, uap->length); if (error) goto free_out; #endif /* * Attach the user audit record to the kernel audit record. Because * this system call is an auditable event, we will write the user * record along with the record for this audit event. * * XXXAUDIT: KASSERT appropriate starting values of k_udata, k_ulen, * k_ar_commit & AR_COMMIT_USER? */ ar->k_udata = rec; ar->k_ulen = uap->length; ar->k_ar_commit |= AR_COMMIT_USER; /* * Currently we assume that all preselection has been performed in * userspace. We unconditionally set these masks so that the records * get committed both to the trail and pipe. In the future we will * want to setup kernel based preselection. */ ar->k_ar_commit |= (AR_PRESELECT_USER_TRAIL | AR_PRESELECT_USER_PIPE); return (0); free_out: /* * audit_syscall_exit() will free the audit record on the thread even * if we allocated it above. */ free(rec, M_AUDITDATA); return (error); } /* * System call to manipulate auditing. */ /* ARGSUSED */ int sys_auditon(struct thread *td, struct auditon_args *uap) { struct ucred *cred, *newcred, *oldcred; int error; union auditon_udata udata; struct proc *tp; if (jailed(td->td_ucred)) return (ENOSYS); AUDIT_ARG_CMD(uap->cmd); #ifdef MAC error = mac_system_check_auditon(td->td_ucred, uap->cmd); if (error) return (error); #endif error = priv_check(td, PRIV_AUDIT_CONTROL); if (error) return (error); if ((uap->length <= 0) || (uap->length > sizeof(union auditon_udata))) return (EINVAL); memset((void *)&udata, 0, sizeof(udata)); /* * Some of the GET commands use the arguments too. */ switch (uap->cmd) { case A_SETPOLICY: case A_OLDSETPOLICY: case A_SETKMASK: case A_SETQCTRL: case A_OLDSETQCTRL: case A_SETSTAT: case A_SETUMASK: case A_SETSMASK: case A_SETCOND: case A_OLDSETCOND: case A_SETCLASS: + case A_SETEVENT: case A_SETPMASK: case A_SETFSIZE: case A_SETKAUDIT: case A_GETCLASS: + case A_GETEVENT: case A_GETPINFO: case A_GETPINFO_ADDR: case A_SENDTRIGGER: error = copyin(uap->data, (void *)&udata, uap->length); if (error) return (error); AUDIT_ARG_AUDITON(&udata); break; } /* * XXXAUDIT: Locking? */ switch (uap->cmd) { case A_OLDGETPOLICY: case A_GETPOLICY: if (uap->length == sizeof(udata.au_policy64)) { if (!audit_fail_stop) udata.au_policy64 |= AUDIT_CNT; if (audit_panic_on_write_fail) udata.au_policy64 |= AUDIT_AHLT; if (audit_argv) udata.au_policy64 |= AUDIT_ARGV; if (audit_arge) udata.au_policy64 |= AUDIT_ARGE; break; } if (uap->length != sizeof(udata.au_policy)) return (EINVAL); if (!audit_fail_stop) udata.au_policy |= AUDIT_CNT; if (audit_panic_on_write_fail) udata.au_policy |= AUDIT_AHLT; if (audit_argv) udata.au_policy |= AUDIT_ARGV; if (audit_arge) udata.au_policy |= AUDIT_ARGE; break; case A_OLDSETPOLICY: case A_SETPOLICY: if (uap->length == sizeof(udata.au_policy64)) { if (udata.au_policy & (~AUDIT_CNT|AUDIT_AHLT| AUDIT_ARGV|AUDIT_ARGE)) return (EINVAL); audit_fail_stop = ((udata.au_policy64 & AUDIT_CNT) == 0); audit_panic_on_write_fail = (udata.au_policy64 & AUDIT_AHLT); audit_argv = (udata.au_policy64 & AUDIT_ARGV); audit_arge = (udata.au_policy64 & AUDIT_ARGE); break; } if (uap->length != sizeof(udata.au_policy)) return (EINVAL); if (udata.au_policy & ~(AUDIT_CNT|AUDIT_AHLT|AUDIT_ARGV| AUDIT_ARGE)) return (EINVAL); /* * XXX - Need to wake up waiters if the policy relaxes? */ audit_fail_stop = ((udata.au_policy & AUDIT_CNT) == 0); audit_panic_on_write_fail = (udata.au_policy & AUDIT_AHLT); audit_argv = (udata.au_policy & AUDIT_ARGV); audit_arge = (udata.au_policy & AUDIT_ARGE); break; case A_GETKMASK: if (uap->length != sizeof(udata.au_mask)) return (EINVAL); udata.au_mask = audit_nae_mask; break; case A_SETKMASK: if (uap->length != sizeof(udata.au_mask)) return (EINVAL); audit_nae_mask = udata.au_mask; break; case A_OLDGETQCTRL: case A_GETQCTRL: if (uap->length == sizeof(udata.au_qctrl64)) { udata.au_qctrl64.aq64_hiwater = (u_int64_t)audit_qctrl.aq_hiwater; udata.au_qctrl64.aq64_lowater = (u_int64_t)audit_qctrl.aq_lowater; udata.au_qctrl64.aq64_bufsz = (u_int64_t)audit_qctrl.aq_bufsz; udata.au_qctrl64.aq64_minfree = (u_int64_t)audit_qctrl.aq_minfree; break; } if (uap->length != sizeof(udata.au_qctrl)) return (EINVAL); udata.au_qctrl = audit_qctrl; break; case A_OLDSETQCTRL: case A_SETQCTRL: if (uap->length == sizeof(udata.au_qctrl64)) { /* NB: aq64_minfree is unsigned unlike aq_minfree. */ if ((udata.au_qctrl64.aq64_hiwater > AQ_MAXHIGH) || (udata.au_qctrl64.aq64_lowater >= udata.au_qctrl.aq_hiwater) || (udata.au_qctrl64.aq64_bufsz > AQ_MAXBUFSZ) || (udata.au_qctrl64.aq64_minfree > 100)) return (EINVAL); audit_qctrl.aq_hiwater = (int)udata.au_qctrl64.aq64_hiwater; audit_qctrl.aq_lowater = (int)udata.au_qctrl64.aq64_lowater; audit_qctrl.aq_bufsz = (int)udata.au_qctrl64.aq64_bufsz; audit_qctrl.aq_minfree = (int)udata.au_qctrl64.aq64_minfree; audit_qctrl.aq_delay = -1; /* Not used. */ break; } if (uap->length != sizeof(udata.au_qctrl)) return (EINVAL); if ((udata.au_qctrl.aq_hiwater > AQ_MAXHIGH) || (udata.au_qctrl.aq_lowater >= udata.au_qctrl.aq_hiwater) || (udata.au_qctrl.aq_bufsz > AQ_MAXBUFSZ) || (udata.au_qctrl.aq_minfree < 0) || (udata.au_qctrl.aq_minfree > 100)) return (EINVAL); audit_qctrl = udata.au_qctrl; /* XXX The queue delay value isn't used with the kernel. */ audit_qctrl.aq_delay = -1; break; case A_GETCWD: return (ENOSYS); break; case A_GETCAR: return (ENOSYS); break; case A_GETSTAT: return (ENOSYS); break; case A_SETSTAT: return (ENOSYS); break; case A_SETUMASK: return (ENOSYS); break; case A_SETSMASK: return (ENOSYS); break; case A_OLDGETCOND: case A_GETCOND: if (uap->length == sizeof(udata.au_cond64)) { if (audit_enabled && !audit_suspended) udata.au_cond64 = AUC_AUDITING; else udata.au_cond64 = AUC_NOAUDIT; break; } if (uap->length != sizeof(udata.au_cond)) return (EINVAL); if (audit_enabled && !audit_suspended) udata.au_cond = AUC_AUDITING; else udata.au_cond = AUC_NOAUDIT; break; case A_OLDSETCOND: case A_SETCOND: if (uap->length == sizeof(udata.au_cond64)) { if (udata.au_cond64 == AUC_NOAUDIT) audit_suspended = 1; if (udata.au_cond64 == AUC_AUDITING) audit_suspended = 0; if (udata.au_cond64 == AUC_DISABLED) { audit_suspended = 1; audit_shutdown(NULL, 0); } break; } if (uap->length != sizeof(udata.au_cond)) return (EINVAL); if (udata.au_cond == AUC_NOAUDIT) audit_suspended = 1; if (udata.au_cond == AUC_AUDITING) audit_suspended = 0; if (udata.au_cond == AUC_DISABLED) { audit_suspended = 1; audit_shutdown(NULL, 0); } break; case A_GETCLASS: if (uap->length != sizeof(udata.au_evclass)) return (EINVAL); udata.au_evclass.ec_class = au_event_class( udata.au_evclass.ec_number); break; + case A_GETEVENT: + if (uap->length != sizeof(udata.au_evname)) + return (EINVAL); + error = au_event_name(udata.au_evname.en_number, + udata.au_evname.en_name); + if (error != 0) + return (error); + break; + case A_SETCLASS: if (uap->length != sizeof(udata.au_evclass)) return (EINVAL); au_evclassmap_insert(udata.au_evclass.ec_number, udata.au_evclass.ec_class); + break; + + case A_SETEVENT: + if (uap->length != sizeof(udata.au_evname)) + return (EINVAL); + + /* Ensure nul termination from userspace. */ + udata.au_evname.en_name[sizeof(udata.au_evname.en_name) - 1] + = 0; + au_evnamemap_insert(udata.au_evname.en_number, + udata.au_evname.en_name); break; case A_GETPINFO: if (uap->length != sizeof(udata.au_aupinfo)) return (EINVAL); if (udata.au_aupinfo.ap_pid < 1) return (ESRCH); if ((tp = pfind(udata.au_aupinfo.ap_pid)) == NULL) return (ESRCH); if ((error = p_cansee(td, tp)) != 0) { PROC_UNLOCK(tp); return (error); } cred = tp->p_ucred; if (cred->cr_audit.ai_termid.at_type == AU_IPv6) { PROC_UNLOCK(tp); return (EINVAL); } udata.au_aupinfo.ap_auid = cred->cr_audit.ai_auid; udata.au_aupinfo.ap_mask.am_success = cred->cr_audit.ai_mask.am_success; udata.au_aupinfo.ap_mask.am_failure = cred->cr_audit.ai_mask.am_failure; udata.au_aupinfo.ap_termid.machine = cred->cr_audit.ai_termid.at_addr[0]; udata.au_aupinfo.ap_termid.port = (dev_t)cred->cr_audit.ai_termid.at_port; udata.au_aupinfo.ap_asid = cred->cr_audit.ai_asid; PROC_UNLOCK(tp); break; case A_SETPMASK: if (uap->length != sizeof(udata.au_aupinfo)) return (EINVAL); if (udata.au_aupinfo.ap_pid < 1) return (ESRCH); newcred = crget(); if ((tp = pfind(udata.au_aupinfo.ap_pid)) == NULL) { crfree(newcred); return (ESRCH); } if ((error = p_cansee(td, tp)) != 0) { PROC_UNLOCK(tp); crfree(newcred); return (error); } oldcred = tp->p_ucred; crcopy(newcred, oldcred); newcred->cr_audit.ai_mask.am_success = udata.au_aupinfo.ap_mask.am_success; newcred->cr_audit.ai_mask.am_failure = udata.au_aupinfo.ap_mask.am_failure; proc_set_cred(tp, newcred); PROC_UNLOCK(tp); crfree(oldcred); break; case A_SETFSIZE: if (uap->length != sizeof(udata.au_fstat)) return (EINVAL); if ((udata.au_fstat.af_filesz != 0) && (udata.au_fstat.af_filesz < MIN_AUDIT_FILE_SIZE)) return (EINVAL); audit_fstat.af_filesz = udata.au_fstat.af_filesz; break; case A_GETFSIZE: if (uap->length != sizeof(udata.au_fstat)) return (EINVAL); udata.au_fstat.af_filesz = audit_fstat.af_filesz; udata.au_fstat.af_currsz = audit_fstat.af_currsz; break; case A_GETPINFO_ADDR: if (uap->length != sizeof(udata.au_aupinfo_addr)) return (EINVAL); if (udata.au_aupinfo_addr.ap_pid < 1) return (ESRCH); if ((tp = pfind(udata.au_aupinfo_addr.ap_pid)) == NULL) return (ESRCH); cred = tp->p_ucred; udata.au_aupinfo_addr.ap_auid = cred->cr_audit.ai_auid; udata.au_aupinfo_addr.ap_mask.am_success = cred->cr_audit.ai_mask.am_success; udata.au_aupinfo_addr.ap_mask.am_failure = cred->cr_audit.ai_mask.am_failure; udata.au_aupinfo_addr.ap_termid = cred->cr_audit.ai_termid; udata.au_aupinfo_addr.ap_asid = cred->cr_audit.ai_asid; PROC_UNLOCK(tp); break; case A_GETKAUDIT: if (uap->length != sizeof(udata.au_kau_info)) return (EINVAL); audit_get_kinfo(&udata.au_kau_info); break; case A_SETKAUDIT: if (uap->length != sizeof(udata.au_kau_info)) return (EINVAL); if (udata.au_kau_info.ai_termid.at_type != AU_IPv4 && udata.au_kau_info.ai_termid.at_type != AU_IPv6) return (EINVAL); audit_set_kinfo(&udata.au_kau_info); break; case A_SENDTRIGGER: if (uap->length != sizeof(udata.au_trigger)) return (EINVAL); if ((udata.au_trigger < AUDIT_TRIGGER_MIN) || (udata.au_trigger > AUDIT_TRIGGER_MAX)) return (EINVAL); return (audit_send_trigger(udata.au_trigger)); default: return (EINVAL); } /* * Copy data back to userspace for the GET comands. */ switch (uap->cmd) { case A_GETPOLICY: case A_OLDGETPOLICY: case A_GETKMASK: case A_GETQCTRL: case A_OLDGETQCTRL: case A_GETCWD: case A_GETCAR: case A_GETSTAT: case A_GETCOND: case A_OLDGETCOND: case A_GETCLASS: case A_GETPINFO: case A_GETFSIZE: case A_GETPINFO_ADDR: case A_GETKAUDIT: error = copyout((void *)&udata, uap->data, uap->length); if (error) return (error); break; } return (0); } /* * System calls to manage the user audit information. */ /* ARGSUSED */ int sys_getauid(struct thread *td, struct getauid_args *uap) { int error; if (jailed(td->td_ucred)) return (ENOSYS); error = priv_check(td, PRIV_AUDIT_GETAUDIT); if (error) return (error); return (copyout(&td->td_ucred->cr_audit.ai_auid, uap->auid, sizeof(td->td_ucred->cr_audit.ai_auid))); } /* ARGSUSED */ int sys_setauid(struct thread *td, struct setauid_args *uap) { struct ucred *newcred, *oldcred; au_id_t id; int error; if (jailed(td->td_ucred)) return (ENOSYS); error = copyin(uap->auid, &id, sizeof(id)); if (error) return (error); audit_arg_auid(id); newcred = crget(); PROC_LOCK(td->td_proc); oldcred = td->td_proc->p_ucred; crcopy(newcred, oldcred); #ifdef MAC error = mac_cred_check_setauid(oldcred, id); if (error) goto fail; #endif error = priv_check_cred(oldcred, PRIV_AUDIT_SETAUDIT, 0); if (error) goto fail; newcred->cr_audit.ai_auid = id; proc_set_cred(td->td_proc, newcred); PROC_UNLOCK(td->td_proc); crfree(oldcred); return (0); fail: PROC_UNLOCK(td->td_proc); crfree(newcred); return (error); } /* * System calls to get and set process audit information. */ /* ARGSUSED */ int sys_getaudit(struct thread *td, struct getaudit_args *uap) { struct auditinfo ai; struct ucred *cred; int error; cred = td->td_ucred; if (jailed(cred)) return (ENOSYS); error = priv_check(td, PRIV_AUDIT_GETAUDIT); if (error) return (error); if (cred->cr_audit.ai_termid.at_type == AU_IPv6) return (E2BIG); bzero(&ai, sizeof(ai)); ai.ai_auid = cred->cr_audit.ai_auid; ai.ai_mask = cred->cr_audit.ai_mask; ai.ai_asid = cred->cr_audit.ai_asid; ai.ai_termid.machine = cred->cr_audit.ai_termid.at_addr[0]; ai.ai_termid.port = cred->cr_audit.ai_termid.at_port; return (copyout(&ai, uap->auditinfo, sizeof(ai))); } /* ARGSUSED */ int sys_setaudit(struct thread *td, struct setaudit_args *uap) { struct ucred *newcred, *oldcred; struct auditinfo ai; int error; if (jailed(td->td_ucred)) return (ENOSYS); error = copyin(uap->auditinfo, &ai, sizeof(ai)); if (error) return (error); audit_arg_auditinfo(&ai); newcred = crget(); PROC_LOCK(td->td_proc); oldcred = td->td_proc->p_ucred; crcopy(newcred, oldcred); #ifdef MAC error = mac_cred_check_setaudit(oldcred, &ai); if (error) goto fail; #endif error = priv_check_cred(oldcred, PRIV_AUDIT_SETAUDIT, 0); if (error) goto fail; bzero(&newcred->cr_audit, sizeof(newcred->cr_audit)); newcred->cr_audit.ai_auid = ai.ai_auid; newcred->cr_audit.ai_mask = ai.ai_mask; newcred->cr_audit.ai_asid = ai.ai_asid; newcred->cr_audit.ai_termid.at_addr[0] = ai.ai_termid.machine; newcred->cr_audit.ai_termid.at_port = ai.ai_termid.port; newcred->cr_audit.ai_termid.at_type = AU_IPv4; proc_set_cred(td->td_proc, newcred); PROC_UNLOCK(td->td_proc); crfree(oldcred); return (0); fail: PROC_UNLOCK(td->td_proc); crfree(newcred); return (error); } /* ARGSUSED */ int sys_getaudit_addr(struct thread *td, struct getaudit_addr_args *uap) { int error; if (jailed(td->td_ucred)) return (ENOSYS); if (uap->length < sizeof(*uap->auditinfo_addr)) return (EOVERFLOW); error = priv_check(td, PRIV_AUDIT_GETAUDIT); if (error) return (error); return (copyout(&td->td_ucred->cr_audit, uap->auditinfo_addr, sizeof(*uap->auditinfo_addr))); } /* ARGSUSED */ int sys_setaudit_addr(struct thread *td, struct setaudit_addr_args *uap) { struct ucred *newcred, *oldcred; struct auditinfo_addr aia; int error; if (jailed(td->td_ucred)) return (ENOSYS); error = copyin(uap->auditinfo_addr, &aia, sizeof(aia)); if (error) return (error); audit_arg_auditinfo_addr(&aia); if (aia.ai_termid.at_type != AU_IPv6 && aia.ai_termid.at_type != AU_IPv4) return (EINVAL); newcred = crget(); PROC_LOCK(td->td_proc); oldcred = td->td_proc->p_ucred; crcopy(newcred, oldcred); #ifdef MAC error = mac_cred_check_setaudit_addr(oldcred, &aia); if (error) goto fail; #endif error = priv_check_cred(oldcred, PRIV_AUDIT_SETAUDIT, 0); if (error) goto fail; newcred->cr_audit = aia; proc_set_cred(td->td_proc, newcred); PROC_UNLOCK(td->td_proc); crfree(oldcred); return (0); fail: PROC_UNLOCK(td->td_proc); crfree(newcred); return (error); } /* * Syscall to manage audit files. */ /* ARGSUSED */ int sys_auditctl(struct thread *td, struct auditctl_args *uap) { struct nameidata nd; struct ucred *cred; struct vnode *vp; int error = 0; int flags; if (jailed(td->td_ucred)) return (ENOSYS); error = priv_check(td, PRIV_AUDIT_CONTROL); if (error) return (error); vp = NULL; cred = NULL; /* * If a path is specified, open the replacement vnode, perform * validity checks, and grab another reference to the current * credential. * * On Darwin, a NULL path argument is also used to disable audit. */ if (uap->path == NULL) return (EINVAL); NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF | AUDITVNODE1, UIO_USERSPACE, uap->path, td); flags = AUDIT_OPEN_FLAGS; error = vn_open(&nd, &flags, 0, NULL); if (error) return (error); vp = nd.ni_vp; #ifdef MAC error = mac_system_check_auditctl(td->td_ucred, vp); VOP_UNLOCK(vp, 0); if (error) { vn_close(vp, AUDIT_CLOSE_FLAGS, td->td_ucred, td); return (error); } #else VOP_UNLOCK(vp, 0); #endif NDFREE(&nd, NDF_ONLY_PNBUF); if (vp->v_type != VREG) { vn_close(vp, AUDIT_CLOSE_FLAGS, td->td_ucred, td); return (EINVAL); } cred = td->td_ucred; crhold(cred); /* * XXXAUDIT: Should audit_suspended actually be cleared by * audit_worker? */ audit_suspended = 0; audit_rotate_vnode(cred, vp); return (error); } #else /* !AUDIT */ int sys_audit(struct thread *td, struct audit_args *uap) { return (ENOSYS); } int sys_auditon(struct thread *td, struct auditon_args *uap) { return (ENOSYS); } int sys_getauid(struct thread *td, struct getauid_args *uap) { return (ENOSYS); } int sys_setauid(struct thread *td, struct setauid_args *uap) { return (ENOSYS); } int sys_getaudit(struct thread *td, struct getaudit_args *uap) { return (ENOSYS); } int sys_setaudit(struct thread *td, struct setaudit_args *uap) { return (ENOSYS); } int sys_getaudit_addr(struct thread *td, struct getaudit_addr_args *uap) { return (ENOSYS); } int sys_setaudit_addr(struct thread *td, struct setaudit_addr_args *uap) { return (ENOSYS); } int sys_auditctl(struct thread *td, struct auditctl_args *uap) { return (ENOSYS); } #endif /* AUDIT */