Audit file descriptors passed to fooat(2) system calls, which are used instead of the root/current working directory as the starting point for lookups. Up to two such descriptors can be audited. Add audit record BSM encoding for fooat(2). Rework vnode argument auditing to follow the same structure, in order to avoid exposing ARG_ macros/flag values outside of the audit code in order to name which one of two possible vnodes will be audited for a system call. Note: due to an error in the OpenBSM 1.1p1 configuration file, a further change is required to that file in order to fix openat(2) auditing. Reviewed by: rdivacky Obtained from: TrustedBSD Project MFC after: 1 month Index: kern/vfs_lookup.c =================================================================== --- kern/vfs_lookup.c (revision 195897) +++ kern/vfs_lookup.c (working copy) @@ -203,8 +203,13 @@ namei(struct nameidata *ndp) if (ndp->ni_startdir != NULL) { dp = ndp->ni_startdir; error = 0; - } else if (ndp->ni_dirfd != AT_FDCWD) + } else if (ndp->ni_dirfd != AT_FDCWD) { + if (cnp->cn_flags & AUDITVNODE1) + AUDIT_ARG_ATFD1(ndp->ni_dirfd); + if (cnp->cn_flags & AUDITVNODE2) + AUDIT_ARG_ATFD2(ndp->ni_dirfd); error = fgetvp(td, ndp->ni_dirfd, &dp); + } if (error != 0 || dp != NULL) { FILEDESC_SUNLOCK(fdp); if (error == 0 && dp->v_type != VDIR) { Index: security/audit/audit_arg.c =================================================================== --- security/audit/audit_arg.c (revision 195897) +++ security/audit/audit_arg.c (working copy) @@ -101,6 +101,32 @@ audit_arg_len(int len) } void +audit_arg_atfd1(int atfd) +{ + struct kaudit_record *ar; + + ar = currecord(); + if (ar == NULL) + return; + + ar->k_ar.ar_arg_atfd1 = atfd; + ARG_SET_VALID(ar, ARG_ATFD1); +} + +void +audit_arg_atfd2(int atfd) +{ + struct kaudit_record *ar; + + ar = currecord(); + if (ar == NULL) + return; + + ar->k_ar.ar_arg_atfd2 = atfd; + ARG_SET_VALID(ar, ARG_ATFD2); +} + +void audit_arg_fd(int fd) { struct kaudit_record *ar; Index: security/audit/audit_private.h =================================================================== --- security/audit/audit_private.h (revision 195897) +++ security/audit/audit_private.h (working copy) @@ -196,6 +196,8 @@ struct audit_record { 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; @@ -323,6 +328,7 @@ void au_evclassmap_insert(au_event_t event, au_c au_class_t au_event_class(au_event_t event); 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, char *path, char *cpath); Index: security/audit/audit.c =================================================================== --- security/audit/audit.c (revision 195897) +++ security/audit/audit.c (working copy) @@ -409,17 +409,22 @@ audit_commit(struct kaudit_record *ar, int error, else sorf = AU_PRS_SUCCESS; + /* + * syscalls.master sometimes contains a prototype event number, which + * we will transform into a more specific event number now that we + * have more complete information gathered during the system call. + */ switch(ar->k_ar.ar_event) { case AUE_OPEN_RWTC: - /* - * The open syscall always writes a AUE_OPEN_RWTC event; - * change it to the proper type of event based on the flags - * and the error value. - */ ar->k_ar.ar_event = audit_flags_and_error_to_openevent( ar->k_ar.ar_arg_fflags, error); break; + case AUE_OPENAT_RWTC: + ar->k_ar.ar_event = audit_flags_and_error_to_openatevent( + ar->k_ar.ar_arg_fflags, error); + break; + case AUE_SYSCTL: ar->k_ar.ar_event = audit_ctlname_to_sysctlevent( ar->k_ar.ar_arg_ctlname, ar->k_ar.ar_valid_arg); Index: security/audit/audit_bsm.c =================================================================== --- security/audit/audit_bsm.c (revision 195897) +++ security/audit/audit_bsm.c (working copy) @@ -183,6 +183,20 @@ kau_free(struct au_record *rec) * 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); \ @@ -198,6 +212,10 @@ kau_free(struct au_record *rec) } 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); \ @@ -715,6 +733,8 @@ kaudit_to_bsm(struct kaudit_record *kar, struct au case AUE_CHDIR: case AUE_CHROOT: + case AUE_FSTATAT: + case AUE_FUTIMESAT: case AUE_GETATTRLIST: case AUE_JAIL: case AUE_LUTIMES: @@ -733,7 +753,9 @@ kaudit_to_bsm(struct kaudit_record *kar, struct au case AUE_TRUNCATE: case AUE_UNDELETE: case AUE_UNLINK: + case AUE_UNLINKAT: case AUE_UTIMES: + ATFD1_TOKENS(1); UPATH1_VNODE1_TOKENS; break; @@ -771,6 +793,16 @@ kaudit_to_bsm(struct kaudit_record *kar, struct au 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)) { @@ -784,6 +816,19 @@ kaudit_to_bsm(struct kaudit_record *kar, struct au 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; @@ -991,8 +1036,12 @@ kaudit_to_bsm(struct kaudit_record *kar, struct au 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; @@ -1136,6 +1185,32 @@ kaudit_to_bsm(struct kaudit_record *kar, struct au 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); Index: security/audit/audit_bsm_klib.c =================================================================== --- security/audit/audit_bsm_klib.c (revision 195897) +++ security/audit/audit_bsm_klib.c (working copy) @@ -75,6 +75,43 @@ static struct evclass_list evclass_hash[EVCLASSMAP #define EVCLASS_WLOCK() rw_wlock(&evclass_lock) #define EVCLASS_WUNLOCK() rw_wunlock(&evclass_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 int aue_open_count = sizeof(aue_open) / sizeof(aue_open[0]); + +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 }, +}; +static const int aue_openat_count = sizeof(aue_openat) / sizeof(aue_openat[0]); + /* * Look up the class for an audit event in the class mapping table. */ @@ -253,94 +290,33 @@ audit_ctlname_to_sysctlevent(int name[], uint64_t au_event_t audit_flags_and_error_to_openevent(int oflags, int error) { - au_event_t aevent; + int i; /* * Need to check only those flags we care about. */ oflags = oflags & (O_RDONLY | O_CREAT | O_TRUNC | O_RDWR | O_WRONLY); - - /* - * These checks determine what flags are on with the condition that - * ONLY that combination is on, and no other flags are on. - */ - switch (oflags) { - case O_RDONLY: - aevent = AUE_OPEN_R; - break; - - case (O_RDONLY | O_CREAT): - aevent = AUE_OPEN_RC; - break; - - case (O_RDONLY | O_CREAT | O_TRUNC): - aevent = AUE_OPEN_RTC; - break; - - case (O_RDONLY | O_TRUNC): - aevent = AUE_OPEN_RT; - break; - - case O_RDWR: - aevent = AUE_OPEN_RW; - break; - - case (O_RDWR | O_CREAT): - aevent = AUE_OPEN_RWC; - break; - - case (O_RDWR | O_CREAT | O_TRUNC): - aevent = AUE_OPEN_RWTC; - break; - - case (O_RDWR | O_TRUNC): - aevent = AUE_OPEN_RWT; - break; - - case O_WRONLY: - aevent = AUE_OPEN_W; - break; - - case (O_WRONLY | O_CREAT): - aevent = AUE_OPEN_WC; - break; - - case (O_WRONLY | O_CREAT | O_TRUNC): - aevent = AUE_OPEN_WTC; - break; - - case (O_WRONLY | O_TRUNC): - aevent = AUE_OPEN_WT; - break; - - default: - aevent = AUE_OPEN; - break; + for (i = 0; i < aue_open_count; i++) { + if (aue_open[i].aoe_flags == oflags) + return (aue_open[i].aoe_event); } + return (AUE_OPEN); +} -#if 0 +au_event_t +audit_flags_and_error_to_openatevent(int oflags, int error) +{ + int i; + /* - * Convert chatty errors to better matching events. Failures to - * find a file are really just attribute events -- so recast them as - * such. - * - * XXXAUDIT: Solaris defines that AUE_OPEN will never be returned, it - * is just a placeholder. However, in Darwin we return that in - * preference to other events. For now, comment this out as we don't - * have a BSM conversion routine for AUE_OPEN. + * Need to check only those flags we care about. */ - switch (aevent) { - 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 (error == ENOENT) - aevent = AUE_OPEN; + oflags = oflags & (O_RDONLY | O_CREAT | O_TRUNC | O_RDWR | O_WRONLY); + for (i = 0; i < aue_openat_count; i++) { + if (aue_openat[i].aoe_flags == oflags) + return (aue_openat[i].aoe_event); } -#endif - return (aevent); + return (AUE_OPENAT); } /* Index: security/audit/audit.h =================================================================== --- security/audit/audit.h (revision 195897) +++ security/audit/audit.h (working copy) @@ -114,6 +114,8 @@ extern int audit_suspended; #define ARG_IOVECSTR 0x0000800000000000ULL #define ARG_ARGV 0x0001000000000000ULL #define ARG_ENVV 0x0002000000000000ULL +#define ARG_ATFD1 0x0004000000000000ULL +#define ARG_ATFD2 0x0008000000000000ULL #define ARG_NONE 0x0000000000000000ULL #define ARG_ALL 0xFFFFFFFFFFFFFFFFULL @@ -132,6 +134,8 @@ union auditon_udata; void audit_arg_addr(void * addr); void audit_arg_exit(int status, int retval); void audit_arg_len(int len); +void audit_arg_atfd1(int atfd); +void audit_arg_atfd2(int atfd); void audit_arg_fd(int fd); void audit_arg_fflags(int fflags); void audit_arg_gid(gid_t gid); @@ -197,6 +202,16 @@ void audit_thread_free(struct thread *td); audit_arg_argv((argv), (argc), (length)); \ } while (0) +#define AUDIT_ARG_ATFD1(atfd) do { \ + if (AUDITING_TD(curthread)) \ + audit_arg_atfd1((atfd)); \ +} while (0) + +#define AUDIT_ARG_ATFD2(atfd) do { \ + if (AUDITING_TD(curthread)) \ + audit_arg_atfd2((atfd)); \ +} while (0) + #define AUDIT_ARG_AUDITON(udata) do { \ if (AUDITING_TD(curthread)) \ audit_arg_auditon((udata)); \ @@ -360,6 +380,8 @@ void audit_thread_free(struct thread *td); #define AUDIT_ARG_ADDR(addr) #define AUDIT_ARG_ARGV(argv, argc, length) +#define AUDIT_ARG_ATFD1(atfd) +#define AUDIT_ARG_ATFD2(atfd) #define AUDIT_ARG_AUDITON(udata) #define AUDIT_ARG_CMD(cmd) #define AUDIT_ARG_DEV(dev)