summaryrefslogtreecommitdiffstats
path: root/sys/security/audit/audit_bsm_klib.c
diff options
context:
space:
mode:
authorcsjp <csjp@FreeBSD.org>2008-07-31 16:57:41 +0000
committercsjp <csjp@FreeBSD.org>2008-07-31 16:57:41 +0000
commit743d0edd9200e99955e096f8c7d04c3712e3c463 (patch)
treec616ad9966bbd6fee782592f1c5f3daa8fffa61d /sys/security/audit/audit_bsm_klib.c
parent26ada4b390d67732f86ca80952574ba039eecdf5 (diff)
downloadFreeBSD-src-743d0edd9200e99955e096f8c7d04c3712e3c463.zip
FreeBSD-src-743d0edd9200e99955e096f8c7d04c3712e3c463.tar.gz
Currently, BSM audit pathname token generation for chrooted or jailed
processes are not producing absolute pathname tokens. It is required that audited pathnames are generated relative to the global root mount point. This modification changes our implementation of audit_canon_path(9) and introduces a new function: vn_fullpath_global(9) which performs a vnode -> pathname translation relative to the global mount point based on the contents of the name cache. Much like vn_fullpath, vn_fullpath_global is a wrapper function which called vn_fullpath1. Further, the string parsing routines have been converted to use the sbuf(9) framework. This change also removes the conditional acquisition of Giant, since the vn_fullpath1 method will not dip into file system dependent code. The vnode locking was modified to use vhold()/vdrop() instead the vref() and vrele(). This will modify the hold count instead of modifying the user count. This makes more sense since it's the kernel that requires the reference to the vnode. This also makes sure that the vnode does not get recycled we hold the reference to it. [1] Discussed with: rwatson Reviewed by: kib [1] MFC after: 2 weeks
Diffstat (limited to 'sys/security/audit/audit_bsm_klib.c')
-rw-r--r--sys/security/audit/audit_bsm_klib.c148
1 files changed, 93 insertions, 55 deletions
diff --git a/sys/security/audit/audit_bsm_klib.c b/sys/security/audit/audit_bsm_klib.c
index ba8325d..75faa32 100644
--- a/sys/security/audit/audit_bsm_klib.c
+++ b/sys/security/audit/audit_bsm_klib.c
@@ -39,6 +39,7 @@ __FBSDID("$FreeBSD$");
#include <sys/mount.h>
#include <sys/proc.h>
#include <sys/sem.h>
+#include <sys/sbuf.h>
#include <sys/syscall.h>
#include <sys/sysctl.h>
#include <sys/sysent.h>
@@ -476,73 +477,110 @@ auditon_command_event(int cmd)
* 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.
- *
- * XXXRW: Since we combine two paths here, ideally a buffer of size
- * MAXPATHLEN * 2 would be passed in.
*/
void
audit_canon_path(struct thread *td, char *path, char *cpath)
{
- char *bufp;
- char *retbuf, *freebuf;
- struct vnode *vnp;
+ struct vnode *cvnp, *rvnp;
+ char *rbuf, *fbuf, *copy;
struct filedesc *fdp;
- int cisr, error, vfslocked;
+ struct sbuf sbf;
+ int error, cwir, locked;
- WITNESS_WARN(WARN_GIANTOK | WARN_SLEEPOK, NULL,
- "audit_canon_path() at %s:%d", __FILE__, __LINE__);
+ 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;
- bufp = path;
- cisr = 0;
FILEDESC_SLOCK(fdp);
- if (*(path) == '/') {
- while (*(bufp) == '/')
- bufp++; /* Skip leading '/'s. */
- /*
- * If no process root, or it is the same as the system root,
- * audit the path as passed in with a single '/'.
- */
- if ((fdp->fd_rdir == NULL) ||
- (fdp->fd_rdir == rootvnode)) {
- vnp = NULL;
- bufp--; /* Restore one '/'. */
- } else {
- vnp = fdp->fd_rdir; /* Use process root. */
- vref(vnp);
- }
- } else {
- vnp = fdp->fd_cdir; /* Prepend the current dir. */
- cisr = (fdp->fd_rdir == fdp->fd_cdir);
- vref(vnp);
- bufp = path;
+ /*
+ * 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 != '/') {
+ cvnp = fdp->fd_cdir;
+ vhold(cvnp);
}
+ cwir = (fdp->fd_rdir == fdp->fd_cdir);
FILEDESC_SUNLOCK(fdp);
- if (vnp != NULL) {
+ /*
+ * 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) {
/*
- * XXX: vn_fullpath() on FreeBSD is "less reliable" than
- * vn_getpath() on Darwin, so this will need more attention
- * in the future. Also, the question and string bounding
- * here seems a bit questionable and will also require
- * attention.
+ * Although unlikely, it is possible for filesystems to define
+ * their own VOP_LOCK, so strictly speaking, we need to
+ * conditionally pickup Giant around calls to vn_lock(9)
*/
- vfslocked = VFS_LOCK_GIANT(vnp->v_mount);
- vn_lock(vnp, LK_EXCLUSIVE | LK_RETRY);
- error = vn_fullpath(td, vnp, &retbuf, &freebuf);
- if (error == 0) {
- /* Copy and free buffer allocated by vn_fullpath().
- * If the current working directory was the same as
- * the root directory, and the path was a relative
- * pathname, do not separate the two components with
- * the '/' character.
- */
- snprintf(cpath, MAXPATHLEN, "%s%s%s", retbuf,
- cisr ? "" : "/", bufp);
- free(freebuf, M_TEMP);
- } else
+ locked = VFS_LOCK_GIANT(rvnp->v_mount);
+ vn_lock(rvnp, LK_EXCLUSIVE | LK_RETRY);
+ vdrop(rvnp);
+ error = vn_fullpath_global(td, rvnp, &rbuf, &fbuf);
+ VOP_UNLOCK(rvnp, 0);
+ VFS_UNLOCK_GIANT(locked);
+ if (error) {
cpath[0] = '\0';
- vput(vnp);
- VFS_UNLOCK_GIANT(vfslocked);
- } else
- strlcpy(cpath, bufp, MAXPATHLEN);
+ if (cvnp != NULL)
+ vdrop(cvnp);
+ return;
+ }
+ (void) sbuf_cat(&sbf, rbuf);
+ free(fbuf, M_TEMP);
+ }
+ if (cvnp != NULL) {
+ locked = VFS_LOCK_GIANT(cvnp->v_mount);
+ vn_lock(cvnp, LK_EXCLUSIVE | LK_RETRY);
+ vdrop(cvnp);
+ error = vn_fullpath(td, cvnp, &rbuf, &fbuf);
+ VOP_UNLOCK(cvnp, 0);
+ VFS_UNLOCK_GIANT(locked);
+ if (error) {
+ cpath[0] = '\0';
+ return;
+ }
+ (void) sbuf_cat(&sbf, rbuf);
+ free(fbuf, M_TEMP);
+ }
+ if (cwir == 0 || (cwir != 0 && cvnp == NULL))
+ (void) sbuf_cat(&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_overflowed(&sbf) != 0) {
+ cpath[0] = '\0';
+ return;
+ }
+ sbuf_finish(&sbf);
}
OpenPOWER on IntegriCloud