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
This commit is contained in:
Robert Watson 2009-07-28 21:39:58 +00:00
parent 3d1001cb11
commit e4b4bbb665
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=195925
7 changed files with 191 additions and 85 deletions

View File

@ -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) {

View File

@ -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);

View File

@ -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)

View File

@ -100,6 +100,32 @@ audit_arg_len(int len)
ARG_SET_VALID(ar, ARG_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)
{

View File

@ -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);

View File

@ -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);
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;
/*
* These checks determine what flags are on with the condition that
* ONLY that combination is on, and no other flags are on.
* Need to check only those flags we care about.
*/
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;
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);
}
#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.
*/
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;
}
#endif
return (aevent);
return (AUE_OPENAT);
}
/*

View File

@ -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);