From 21e3bcee4378f043e902dc1bab9ac2915235f40a Mon Sep 17 00:00:00 2001 From: rwatson Date: Tue, 28 Jul 2009 21:39:58 +0000 Subject: 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). 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. Approved by: re (kib) Reviewed by: rdivacky (fooat(2) portions) Obtained from: TrustedBSD Project MFC after: 1 month --- sys/kern/vfs_lookup.c | 7 +- sys/security/audit/audit.c | 15 ++-- sys/security/audit/audit.h | 16 +++++ sys/security/audit/audit_arg.c | 26 +++++++ sys/security/audit/audit_bsm.c | 75 ++++++++++++++++++++ sys/security/audit/audit_bsm_klib.c | 132 +++++++++++++++--------------------- sys/security/audit/audit_private.h | 3 + 7 files changed, 190 insertions(+), 84 deletions(-) diff --git a/sys/kern/vfs_lookup.c b/sys/kern/vfs_lookup.c index e154c56..2f3b54e 100644 --- a/sys/kern/vfs_lookup.c +++ b/sys/kern/vfs_lookup.c @@ -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) { diff --git a/sys/security/audit/audit.c b/sys/security/audit/audit.c index 1967e05..40daaa9 100644 --- a/sys/security/audit/audit.c +++ b/sys/security/audit/audit.c @@ -409,17 +409,22 @@ audit_commit(struct kaudit_record *ar, int error, int retval) 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); diff --git a/sys/security/audit/audit.h b/sys/security/audit/audit.h index 582cc24..e8b3550 100644 --- a/sys/security/audit/audit.h +++ b/sys/security/audit/audit.h @@ -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 +201,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 +374,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) diff --git a/sys/security/audit/audit_arg.c b/sys/security/audit/audit_arg.c index cf62421..bf4cb0a 100644 --- a/sys/security/audit/audit_arg.c +++ b/sys/security/audit/audit_arg.c @@ -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; diff --git a/sys/security/audit/audit_bsm.c b/sys/security/audit/audit_bsm.c index e5c0d52..b1a9931 100644 --- a/sys/security/audit/audit_bsm.c +++ b/sys/security/audit/audit_bsm.c @@ -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_record **pau) 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_record **pau) 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_record **pau) 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_record **pau) 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_record **pau) 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_record **pau) 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); diff --git a/sys/security/audit/audit_bsm_klib.c b/sys/security/audit/audit_bsm_klib.c index 547e09a..c8d4035 100644 --- a/sys/security/audit/audit_bsm_klib.c +++ b/sys/security/audit/audit_bsm_klib.c @@ -75,6 +75,43 @@ static struct evclass_list evclass_hash[EVCLASSMAP_HASH_TABLE_SIZE]; #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 valid_arg) 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); +} + +au_event_t +audit_flags_and_error_to_openatevent(int oflags, int error) +{ + int i; -#if 0 /* - * 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); } /* diff --git a/sys/security/audit/audit_private.h b/sys/security/audit/audit_private.h index 0da2f2d..97433df 100644 --- a/sys/security/audit/audit_private.h +++ b/sys/security/audit/audit_private.h @@ -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 +325,7 @@ void au_evclassmap_insert(au_event_t event, au_class_t class); 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); -- cgit v1.1