summary refs log tree commit diff
path: root/drivers/tty/tty_audit.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/tty/tty_audit.c')
-rw-r--r--drivers/tty/tty_audit.c237
1 files changed, 68 insertions, 169 deletions
diff --git a/drivers/tty/tty_audit.c b/drivers/tty/tty_audit.c
index 3d245cd3d8e6..df2d735338e2 100644
--- a/drivers/tty/tty_audit.c
+++ b/drivers/tty/tty_audit.c
@@ -14,16 +14,23 @@
 #include <linux/tty.h>
 
 struct tty_audit_buf {
-	atomic_t count;
 	struct mutex mutex;	/* Protects all data below */
-	int major, minor;	/* The TTY which the data is from */
+	dev_t dev;		/* The TTY which the data is from */
 	unsigned icanon:1;
 	size_t valid;
 	unsigned char *data;	/* Allocated size N_TTY_BUF_SIZE */
 };
 
-static struct tty_audit_buf *tty_audit_buf_alloc(int major, int minor,
-						 unsigned icanon)
+static struct tty_audit_buf *tty_audit_buf_ref(void)
+{
+	struct tty_audit_buf *buf;
+
+	buf = current->signal->tty_audit_buf;
+	WARN_ON(buf == ERR_PTR(-ESRCH));
+	return buf;
+}
+
+static struct tty_audit_buf *tty_audit_buf_alloc(void)
 {
 	struct tty_audit_buf *buf;
 
@@ -33,11 +40,9 @@ static struct tty_audit_buf *tty_audit_buf_alloc(int major, int minor,
 	buf->data = kmalloc(N_TTY_BUF_SIZE, GFP_KERNEL);
 	if (!buf->data)
 		goto err_buf;
-	atomic_set(&buf->count, 1);
 	mutex_init(&buf->mutex);
-	buf->major = major;
-	buf->minor = minor;
-	buf->icanon = icanon;
+	buf->dev = MKDEV(0, 0);
+	buf->icanon = 0;
 	buf->valid = 0;
 	return buf;
 
@@ -54,13 +59,7 @@ static void tty_audit_buf_free(struct tty_audit_buf *buf)
 	kfree(buf);
 }
 
-static void tty_audit_buf_put(struct tty_audit_buf *buf)
-{
-	if (atomic_dec_and_test(&buf->count))
-		tty_audit_buf_free(buf);
-}
-
-static void tty_audit_log(const char *description, int major, int minor,
+static void tty_audit_log(const char *description, dev_t dev,
 			  unsigned char *data, size_t size)
 {
 	struct audit_buffer *ab;
@@ -76,7 +75,7 @@ static void tty_audit_log(const char *description, int major, int minor,
 
 		audit_log_format(ab, "%s pid=%u uid=%u auid=%u ses=%u major=%d"
 				 " minor=%d comm=", description, pid, uid,
-				 loginuid, sessionid, major, minor);
+				 loginuid, sessionid, MAJOR(dev), MINOR(dev));
 		get_task_comm(name, tsk);
 		audit_log_untrustedstring(ab, name);
 		audit_log_format(ab, " data=");
@@ -99,7 +98,7 @@ static void tty_audit_buf_push(struct tty_audit_buf *buf)
 		buf->valid = 0;
 		return;
 	}
-	tty_audit_log("tty", buf->major, buf->minor, buf->data, buf->valid);
+	tty_audit_log("tty", buf->dev, buf->data, buf->valid);
 	buf->valid = 0;
 }
 
@@ -108,21 +107,20 @@ static void tty_audit_buf_push(struct tty_audit_buf *buf)
  *
  *	Make sure all buffered data is written out and deallocate the buffer.
  *	Only needs to be called if current->signal->tty_audit_buf != %NULL.
+ *
+ *	The process is single-threaded at this point; no other threads share
+ *	current->signal.
  */
 void tty_audit_exit(void)
 {
 	struct tty_audit_buf *buf;
 
-	buf = current->signal->tty_audit_buf;
-	current->signal->tty_audit_buf = NULL;
+	buf = xchg(&current->signal->tty_audit_buf, ERR_PTR(-ESRCH));
 	if (!buf)
 		return;
 
-	mutex_lock(&buf->mutex);
 	tty_audit_buf_push(buf);
-	mutex_unlock(&buf->mutex);
-
-	tty_audit_buf_put(buf);
+	tty_audit_buf_free(buf);
 }
 
 /**
@@ -133,7 +131,6 @@ void tty_audit_exit(void)
 void tty_audit_fork(struct signal_struct *sig)
 {
 	sig->audit_tty = current->signal->audit_tty;
-	sig->audit_tty_log_passwd = current->signal->audit_tty_log_passwd;
 }
 
 /**
@@ -141,123 +138,62 @@ void tty_audit_fork(struct signal_struct *sig)
  */
 void tty_audit_tiocsti(struct tty_struct *tty, char ch)
 {
-	struct tty_audit_buf *buf;
-	int major, minor, should_audit;
-	unsigned long flags;
+	dev_t dev;
 
-	spin_lock_irqsave(&current->sighand->siglock, flags);
-	should_audit = current->signal->audit_tty;
-	buf = current->signal->tty_audit_buf;
-	if (buf)
-		atomic_inc(&buf->count);
-	spin_unlock_irqrestore(&current->sighand->siglock, flags);
-
-	major = tty->driver->major;
-	minor = tty->driver->minor_start + tty->index;
-	if (buf) {
-		mutex_lock(&buf->mutex);
-		if (buf->major == major && buf->minor == minor)
-			tty_audit_buf_push(buf);
-		mutex_unlock(&buf->mutex);
-		tty_audit_buf_put(buf);
-	}
-
-	if (should_audit && audit_enabled) {
-		kuid_t auid;
-		unsigned int sessionid;
+	dev = MKDEV(tty->driver->major, tty->driver->minor_start) + tty->index;
+	if (tty_audit_push())
+		return;
 
-		auid = audit_get_loginuid(current);
-		sessionid = audit_get_sessionid(current);
-		tty_audit_log("ioctl=TIOCSTI", major, minor, &ch, 1);
-	}
+	if (audit_enabled)
+		tty_audit_log("ioctl=TIOCSTI", dev, &ch, 1);
 }
 
 /**
- * tty_audit_push_current -	Flush current's pending audit data
+ *	tty_audit_push	-	Flush current's pending audit data
  *
- * Try to lock sighand and get a reference to the tty audit buffer if available.
- * Flush the buffer or return an appropriate error code.
+ *	Returns 0 if success, -EPERM if tty audit is disabled
  */
-int tty_audit_push_current(void)
+int tty_audit_push(void)
 {
-	struct tty_audit_buf *buf = ERR_PTR(-EPERM);
-	struct task_struct *tsk = current;
-	unsigned long flags;
+	struct tty_audit_buf *buf;
 
-	if (!lock_task_sighand(tsk, &flags))
-		return -ESRCH;
+	if (~current->signal->audit_tty & AUDIT_TTY_ENABLE)
+		return -EPERM;
 
-	if (tsk->signal->audit_tty) {
-		buf = tsk->signal->tty_audit_buf;
-		if (buf)
-			atomic_inc(&buf->count);
+	buf = tty_audit_buf_ref();
+	if (!IS_ERR_OR_NULL(buf)) {
+		mutex_lock(&buf->mutex);
+		tty_audit_buf_push(buf);
+		mutex_unlock(&buf->mutex);
 	}
-	unlock_task_sighand(tsk, &flags);
-
-	/*
-	 * Return 0 when signal->audit_tty set
-	 * but tsk->signal->tty_audit_buf == NULL.
-	 */
-	if (!buf || IS_ERR(buf))
-		return PTR_ERR(buf);
-
-	mutex_lock(&buf->mutex);
-	tty_audit_buf_push(buf);
-	mutex_unlock(&buf->mutex);
-
-	tty_audit_buf_put(buf);
 	return 0;
 }
 
 /**
  *	tty_audit_buf_get	-	Get an audit buffer.
  *
- *	Get an audit buffer for @tty, allocate it if necessary.  Return %NULL
- *	if TTY auditing is disabled or out of memory.  Otherwise, return a new
- *	reference to the buffer.
+ *	Get an audit buffer, allocate it if necessary.  Return %NULL
+ *	if out of memory or ERR_PTR(-ESRCH) if tty_audit_exit() has already
+ *	occurred.  Otherwise, return a new reference to the buffer.
  */
-static struct tty_audit_buf *tty_audit_buf_get(struct tty_struct *tty,
-		unsigned icanon)
+static struct tty_audit_buf *tty_audit_buf_get(void)
 {
-	struct tty_audit_buf *buf, *buf2;
-	unsigned long flags;
-
-	buf = NULL;
-	buf2 = NULL;
-	spin_lock_irqsave(&current->sighand->siglock, flags);
-	if (likely(!current->signal->audit_tty))
-		goto out;
-	buf = current->signal->tty_audit_buf;
-	if (buf) {
-		atomic_inc(&buf->count);
-		goto out;
-	}
-	spin_unlock_irqrestore(&current->sighand->siglock, flags);
+	struct tty_audit_buf *buf;
+
+	buf = tty_audit_buf_ref();
+	if (buf)
+		return buf;
 
-	buf2 = tty_audit_buf_alloc(tty->driver->major,
-				   tty->driver->minor_start + tty->index,
-				   icanon);
-	if (buf2 == NULL) {
+	buf = tty_audit_buf_alloc();
+	if (buf == NULL) {
 		audit_log_lost("out of memory in TTY auditing");
 		return NULL;
 	}
 
-	spin_lock_irqsave(&current->sighand->siglock, flags);
-	if (!current->signal->audit_tty)
-		goto out;
-	buf = current->signal->tty_audit_buf;
-	if (!buf) {
-		current->signal->tty_audit_buf = buf2;
-		buf = buf2;
-		buf2 = NULL;
-	}
-	atomic_inc(&buf->count);
-	/* Fall through */
- out:
-	spin_unlock_irqrestore(&current->sighand->siglock, flags);
-	if (buf2)
-		tty_audit_buf_free(buf2);
-	return buf;
+	/* Race to use this buffer, free it if another wins */
+	if (cmpxchg(&current->signal->tty_audit_buf, NULL, buf) != NULL)
+		tty_audit_buf_free(buf);
+	return tty_audit_buf_ref();
 }
 
 /**
@@ -265,39 +201,36 @@ static struct tty_audit_buf *tty_audit_buf_get(struct tty_struct *tty,
  *
  *	Audit @data of @size from @tty, if necessary.
  */
-void tty_audit_add_data(struct tty_struct *tty, const void *data,
-			size_t size, unsigned icanon)
+void tty_audit_add_data(struct tty_struct *tty, const void *data, size_t size)
 {
 	struct tty_audit_buf *buf;
-	int major, minor;
-	int audit_log_tty_passwd;
-	unsigned long flags;
+	unsigned int icanon = !!L_ICANON(tty);
+	unsigned int audit_tty;
+	dev_t dev;
 
-	if (unlikely(size == 0))
+	audit_tty = READ_ONCE(current->signal->audit_tty);
+	if (~audit_tty & AUDIT_TTY_ENABLE)
 		return;
 
-	spin_lock_irqsave(&current->sighand->siglock, flags);
-	audit_log_tty_passwd = current->signal->audit_tty_log_passwd;
-	spin_unlock_irqrestore(&current->sighand->siglock, flags);
-	if (!audit_log_tty_passwd && icanon && !L_ECHO(tty))
+	if (unlikely(size == 0))
 		return;
 
 	if (tty->driver->type == TTY_DRIVER_TYPE_PTY
 	    && tty->driver->subtype == PTY_TYPE_MASTER)
 		return;
 
-	buf = tty_audit_buf_get(tty, icanon);
-	if (!buf)
+	if ((~audit_tty & AUDIT_TTY_LOG_PASSWD) && icanon && !L_ECHO(tty))
+		return;
+
+	buf = tty_audit_buf_get();
+	if (IS_ERR_OR_NULL(buf))
 		return;
 
 	mutex_lock(&buf->mutex);
-	major = tty->driver->major;
-	minor = tty->driver->minor_start + tty->index;
-	if (buf->major != major || buf->minor != minor
-	    || buf->icanon != icanon) {
+	dev = MKDEV(tty->driver->major, tty->driver->minor_start) + tty->index;
+	if (buf->dev != dev || buf->icanon != icanon) {
 		tty_audit_buf_push(buf);
-		buf->major = major;
-		buf->minor = minor;
+		buf->dev = dev;
 		buf->icanon = icanon;
 	}
 	do {
@@ -314,38 +247,4 @@ void tty_audit_add_data(struct tty_struct *tty, const void *data,
 			tty_audit_buf_push(buf);
 	} while (size != 0);
 	mutex_unlock(&buf->mutex);
-	tty_audit_buf_put(buf);
-}
-
-/**
- *	tty_audit_push	-	Push buffered data out
- *
- *	Make sure no audit data is pending for @tty on the current process.
- */
-void tty_audit_push(struct tty_struct *tty)
-{
-	struct tty_audit_buf *buf;
-	unsigned long flags;
-
-	spin_lock_irqsave(&current->sighand->siglock, flags);
-	if (likely(!current->signal->audit_tty)) {
-		spin_unlock_irqrestore(&current->sighand->siglock, flags);
-		return;
-	}
-	buf = current->signal->tty_audit_buf;
-	if (buf)
-		atomic_inc(&buf->count);
-	spin_unlock_irqrestore(&current->sighand->siglock, flags);
-
-	if (buf) {
-		int major, minor;
-
-		major = tty->driver->major;
-		minor = tty->driver->minor_start + tty->index;
-		mutex_lock(&buf->mutex);
-		if (buf->major == major && buf->minor == minor)
-			tty_audit_buf_push(buf);
-		mutex_unlock(&buf->mutex);
-		tty_audit_buf_put(buf);
-	}
 }