Audit implementation idea.

Pawel Jakub Dawidek nick at garage.freebsd.pl
Fri May 2 13:51:28 GMT 2003


Hello...

Because there is many problems with events auditing implementation I want
to share some ideas of how it could be implemented.

One of the biggest problem with audit is syscalls arguments handling.
I think I've found a clean way to avoid any potential races, etc.
Proposed implementation isn't a fast hack, but take a look.

For every syscall argument that is a buffer there have to be an argument
which describe its size or not, if size of buffer is constant.
First of all we should copy arguments from userland to kernel space and
teach syscalls to use buffers that are in kernel space.
Because of lots of differents in arguments my idea is to write functions
for every syscall only for argument copy operations.

Let's modify sysent struct:

struct sysent {
	int	sy_narg;
        sy_call_t *sy_call;
	sy_args_t *sy_args;
};

typedef	int	sy_args_t(struct thread *, void *, struct karg *);

Needed struct to describe argument:

struct karg {
	union {
		register_t __ka_u_arg;
		void	*__ka_u_buf;
		int	 __ka_u_int;
		long	 __ka_u_long;
	} __ka_u_value;
	size_t	 ka_size;	/* size of buffer when ka_type is KARG_BUF
				 * or 0 in different cases. */
	u_int	 ka_type;
};
#define	ka_arg	__ka_u_value.__ka_u_arg
#define	ka_buf	__ka_u_value.__ka_u_buf
#define	ka_int	__ka_u_value.__ka_u_int
#define	ka_long	__ka_u_value.__ka_u_long

#define	KARG_INT	0x0
#define	KARG_BUF	0x1
#define	KARG_LONG	0x2
#define	KARG_MASK	0xf

#define	KARG_DIR_IN	0x10
#define	KARG_DIR_OUT	0x20
#define	KARG_DIR_INOUT	(KARG_DIR_IN | KARG_DIR_OUT)
#define	KARG_DIR_MASK	0xf0

When ORing KARG_(INT|BUF|LONG) with KARG_DIR_* we got argument type and
information if this is a buffer that imports some data (KARG_DIR_IN) to
kernel, that will be filled out by kernel (KARG_DIR_OUT) or both
(KARG_DIR_INOUT).

Now some modifications in trap.c:
[...]

	if (error == 0) {
		struct karg *kargs = NULL;
		void *syargs = NULL;
		int i;

		td->td_retval[0] = 0;
		td->td_retval[1] = frame.tf_edx;

		STOPEVENT(p, S_SCE, narg);

		if (*callp->sy_args == NULL) {
			error = (*callp->sy_call)(td, args);
			goto noaudit;
		}

		if (narg != 0) {
			kargs = malloc(sizeof(struct karg) * narg, M_KARG,
			    M_NOWAIT);
			if (kargs == NULL) {
				error = ENOMEM;
				goto fail;
			}
			syargs = malloc(sizeof(register_t) * narg, M_KARG,
			    M_NOWAIT);
			if (syargs == NULL) {
				free(kargs, M_KARG);
				kargs = NULL;
				error = ENOMEM;
				goto fail;
			}
		}
		error = (*callp->sy_args)(td, args, kargs);
		if (error != 0) {
			kargs = NULL;
			goto fail;
		}

		/* 'long' arguments aren't supported in this example. */
		for (i = 0; i < narg; ++i)
			(register_t)syargs[i] = kargs[i]->ka_arg;

		audit_before_syscall(td, kargs, code, narg);

		error = (*callp->sy_call)(td, syargs);
		if (error == 0) {
			/* Fill out-buffers. */
			error = copy_args(args, kargs);
		}
fail:
		/*
		 * Note that in some cases this function could be called
		 * without audit_before_syscall() earlier.
		 */
		audit_after_syscall(td, kargs, code, narg, error);
noaudit:
        }

Now example of arguments handling function:

int
rename_args_proc(struct thread *td, struct rename_args *uap, struct karg *kargs)
{
	char	*from, *to;
	size_t	fromsize, tosize;

	from = malloc(MAXPATHLEN, M_KARG, M_NOWAIT);
	if (from == NULL)
		return (ENOMEM);
	error = copyinstr(SCARG(uap, from), from, MAXPATHLEN, &fromsize);
	if (error != 0) {
		free(from, M_KARG);
		return (error);
	}
	to = malloc(MAXPATHLEN, M_KARG, M_NOWAIT);
	if (to == NULL) {
		free(from, M_KARG);
		return (ENOMEM);
	}
	error = copyinstr(SCARG(uap, to), to, MAXPATHLEN, &tosize);
	if (error != 0) {
		free(from, M_KARG);
		free(to, M_KARG);
		return (error);
	}

	kargs[0]->ka_ptr = from;
	kargs[0]->ka_size = fromsize;
	kargs[0]->ka_type = KARG_BUF | KARG_DIR_IN;
	kargs[1]->ka_ptr = to;
	kargs[1]->ka_size = tosize;
	kargs[1]->ka_type = KARG_BUF | KARG_DIR_IN;

	return (0);
}

There should be also some standard function for arguments handling when
argument are 'int's only, because if sy_args is NULL that means that syscall
doesn't support auditing yet and will be called in old way.

Maybe there will be need to write per-syscall functions to fill out buffers,
but I think there is chance to avoid this.

We should also remove all copy(9) functions from original syscalls.

With this infrastructure audit functionality could be imported in little
steps.

What you think about it? Maybe there are other ideas to implement events
auditing?

-- 
Pawel Jakub Dawidek                       pawel at dawidek.net
UNIX Systems Programmer/Administrator     http://garage.freebsd.pl
Am I Evil? Yes, I Am!                     http://cerber.sourceforge.net
-------------- next part --------------
A non-text attachment was scrubbed...
Name: not available
Type: application/pgp-signature
Size: 305 bytes
Desc: not available
Url : http://lists.freebsd.org/pipermail/trustedbsd-audit/attachments/20030502/6ae255ce/attachment.bin


More information about the trustedbsd-audit mailing list