Merge branch 'stable-4.8' of git://git.infradead.org/users/pcmoore/audit
authorLinus Torvalds <torvalds@linux-foundation.org>
Sat, 30 Jul 2016 00:54:17 +0000 (17:54 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Sat, 30 Jul 2016 00:54:17 +0000 (17:54 -0700)
Pull audit updates from Paul Moore:
 "Six audit patches for 4.8.

  There are a couple of style and minor whitespace tweaks for the logs,
  as well as a minor fixup to catch errors on user filter rules, however
  the major improvements are a fix to the s390 syscall argument masking
  code (reviewed by the nice s390 folks), some consolidation around the
  exclude filtering (less code, always a win), and a double-fetch fix
  for recording the execve arguments"

* 'stable-4.8' of git://git.infradead.org/users/pcmoore/audit:
  audit: fix a double fetch in audit_log_single_execve_arg()
  audit: fix whitespace in CWD record
  audit: add fields to exclude filter by reusing user filter
  s390: ensure that syscall arguments are properly masked on s390
  audit: fix some horrible switch statement style crimes
  audit: fixup: log on errors from filter user rules

1  2 
arch/s390/kernel/ptrace.c
kernel/audit.c
kernel/audit.h
kernel/auditsc.c

@@@ -821,6 -821,16 +821,8 @@@ long compat_arch_ptrace(struct task_str
  
  asmlinkage long do_syscall_trace_enter(struct pt_regs *regs)
  {
 -      long ret = 0;
+       unsigned long mask = -1UL;
 -      /* Do the secure computing check first. */
 -      if (secure_computing()) {
 -              /* seccomp failures shouldn't expose any additional code. */
 -              ret = -1;
 -              goto out;
 -      }
 -
        /*
         * The sysc_tracesys code in entry.S stored the system
         * call number to gprs[2].
                 * the system call and the system call restart handling.
                 */
                clear_pt_regs_flag(regs, PIF_SYSCALL);
 -              ret = -1;
 +              return -1;
 +      }
 +
 +      /* Do the secure computing check after ptrace. */
 +      if (secure_computing(NULL)) {
 +              /* seccomp failures shouldn't expose any additional code. */
 +              return -1;
        }
  
        if (unlikely(test_thread_flag(TIF_SYSCALL_TRACEPOINT)))
                trace_sys_enter(regs, regs->gprs[2]);
  
-       audit_syscall_entry(regs->gprs[2], regs->orig_gpr2,
-                           regs->gprs[3], regs->gprs[4],
-                           regs->gprs[5]);
+       if (is_compat_task())
+               mask = 0xffffffff;
+       audit_syscall_entry(regs->gprs[2], regs->orig_gpr2 & mask,
 -                          regs->gprs[3] & mask, regs->gprs[4] & mask,
 -                          regs->gprs[5] & mask);
 -out:
 -      return ret ?: regs->gprs[2];
++                          regs->gprs[3] &mask, regs->gprs[4] &mask,
++                          regs->gprs[5] &mask);
 +
 +      return regs->gprs[2];
  }
  
  asmlinkage void do_syscall_trace_exit(struct pt_regs *regs)
diff --combined kernel/audit.c
@@@ -64,6 -64,7 +64,6 @@@
  #include <linux/security.h>
  #endif
  #include <linux/freezer.h>
 -#include <linux/tty.h>
  #include <linux/pid_namespace.h>
  #include <net/netns/generic.h>
  
@@@ -429,6 -430,7 +429,6 @@@ restart
                                        attempts, audit_pid);
                                set_current_state(TASK_INTERRUPTIBLE);
                                schedule();
 -                              __set_current_state(TASK_RUNNING);
                                goto restart;
                        }
                }
@@@ -932,7 -934,7 +932,7 @@@ static int audit_receive_msg(struct sk_
                if (!audit_enabled && msg_type != AUDIT_USER_AVC)
                        return 0;
  
-               err = audit_filter_user(msg_type);
+               err = audit_filter(msg_type, AUDIT_FILTER_USER);
                if (err == 1) { /* match or error */
                        err = 0;
                        if (msg_type == AUDIT_USER_TTY) {
@@@ -1339,14 -1341,15 +1339,14 @@@ static inline void audit_get_stamp(stru
  static long wait_for_auditd(long sleep_time)
  {
        DECLARE_WAITQUEUE(wait, current);
 -      set_current_state(TASK_UNINTERRUPTIBLE);
 -      add_wait_queue_exclusive(&audit_backlog_wait, &wait);
  
        if (audit_backlog_limit &&
 -          skb_queue_len(&audit_skb_queue) > audit_backlog_limit)
 +          skb_queue_len(&audit_skb_queue) > audit_backlog_limit) {
 +              add_wait_queue_exclusive(&audit_backlog_wait, &wait);
 +              set_current_state(TASK_UNINTERRUPTIBLE);
                sleep_time = schedule_timeout(sleep_time);
 -
 -      __set_current_state(TASK_RUNNING);
 -      remove_wait_queue(&audit_backlog_wait, &wait);
 +              remove_wait_queue(&audit_backlog_wait, &wait);
 +      }
  
        return sleep_time;
  }
@@@ -1379,7 -1382,7 +1379,7 @@@ struct audit_buffer *audit_log_start(st
        if (audit_initialized != AUDIT_INITIALIZED)
                return NULL;
  
-       if (unlikely(audit_filter_type(type)))
+       if (unlikely(!audit_filter(type, AUDIT_FILTER_TYPE)))
                return NULL;
  
        if (gfp_mask & __GFP_DIRECT_RECLAIM) {
@@@ -1883,35 -1886,25 +1883,35 @@@ out_null
        audit_log_format(ab, " exe=(null)");
  }
  
 +struct tty_struct *audit_get_tty(struct task_struct *tsk)
 +{
 +      struct tty_struct *tty = NULL;
 +      unsigned long flags;
 +
 +      spin_lock_irqsave(&tsk->sighand->siglock, flags);
 +      if (tsk->signal)
 +              tty = tty_kref_get(tsk->signal->tty);
 +      spin_unlock_irqrestore(&tsk->sighand->siglock, flags);
 +      return tty;
 +}
 +
 +void audit_put_tty(struct tty_struct *tty)
 +{
 +      tty_kref_put(tty);
 +}
 +
  void audit_log_task_info(struct audit_buffer *ab, struct task_struct *tsk)
  {
        const struct cred *cred;
        char comm[sizeof(tsk->comm)];
 -      char *tty;
 +      struct tty_struct *tty;
  
        if (!ab)
                return;
  
        /* tsk == current */
        cred = current_cred();
 -
 -      spin_lock_irq(&tsk->sighand->siglock);
 -      if (tsk->signal && tsk->signal->tty && tsk->signal->tty->name)
 -              tty = tsk->signal->tty->name;
 -      else
 -              tty = "(none)";
 -      spin_unlock_irq(&tsk->sighand->siglock);
 -
 +      tty = audit_get_tty(tsk);
        audit_log_format(ab,
                         " ppid=%d pid=%d auid=%u uid=%u gid=%u"
                         " euid=%u suid=%u fsuid=%u"
                         from_kgid(&init_user_ns, cred->egid),
                         from_kgid(&init_user_ns, cred->sgid),
                         from_kgid(&init_user_ns, cred->fsgid),
 -                       tty, audit_get_sessionid(tsk));
 -
 +                       tty ? tty_name(tty) : "(none)",
 +                       audit_get_sessionid(tsk));
 +      audit_put_tty(tty);
        audit_log_format(ab, " comm=");
        audit_log_untrustedstring(ab, get_task_comm(comm, tsk));
 -
        audit_log_d_path_exe(ab, tsk->mm);
        audit_log_task_context(ab);
  }
diff --combined kernel/audit.h
@@@ -23,7 -23,6 +23,7 @@@
  #include <linux/audit.h>
  #include <linux/skbuff.h>
  #include <uapi/linux/mqueue.h>
 +#include <linux/tty.h>
  
  /* AUDIT_NAMES is the number of slots we reserve in the audit_context
   * for saving names from getname().  If we get more names we will allocate
@@@ -263,9 -262,6 +263,9 @@@ extern struct audit_entry *audit_dupe_r
  extern void audit_log_d_path_exe(struct audit_buffer *ab,
                                 struct mm_struct *mm);
  
 +extern struct tty_struct *audit_get_tty(struct task_struct *tsk);
 +extern void audit_put_tty(struct tty_struct *tty);
 +
  /* audit watch functions */
  #ifdef CONFIG_AUDIT_WATCH
  extern void audit_put_watch(struct audit_watch *watch);
@@@ -331,6 -327,8 +331,8 @@@ extern pid_t audit_sig_pid
  extern kuid_t audit_sig_uid;
  extern u32 audit_sig_sid;
  
+ extern int audit_filter(int msgtype, unsigned int listtype);
  #ifdef CONFIG_AUDITSYSCALL
  extern int __audit_signal_info(int sig, struct task_struct *t);
  static inline int audit_signal_info(int sig, struct task_struct *t)
diff --combined kernel/auditsc.c
@@@ -63,6 -63,7 +63,6 @@@
  #include <asm/unistd.h>
  #include <linux/security.h>
  #include <linux/list.h>
 -#include <linux/tty.h>
  #include <linux/binfmts.h>
  #include <linux/highmem.h>
  #include <linux/syscalls.h>
@@@ -72,6 -73,7 +72,7 @@@
  #include <linux/compat.h>
  #include <linux/ctype.h>
  #include <linux/string.h>
+ #include <linux/uaccess.h>
  #include <uapi/linux/limits.h>
  
  #include "audit.h"
@@@ -81,7 -83,8 +82,8 @@@
  #define AUDITSC_SUCCESS 1
  #define AUDITSC_FAILURE 2
  
- /* no execve audit message should be longer than this (userspace limits) */
+ /* no execve audit message should be longer than this (userspace limits),
+  * see the note near the top of audit_log_execve_info() about this value */
  #define MAX_EXECVE_AUDIT_LEN 7500
  
  /* max length to print of cmdline/proctitle value during audit */
@@@ -694,8 -697,12 +696,12 @@@ static int audit_filter_rules(struct ta
                ctx->prio = rule->prio;
        }
        switch (rule->action) {
-       case AUDIT_NEVER:    *state = AUDIT_DISABLED;       break;
-       case AUDIT_ALWAYS:   *state = AUDIT_RECORD_CONTEXT; break;
+       case AUDIT_NEVER:
+               *state = AUDIT_DISABLED;
+               break;
+       case AUDIT_ALWAYS:
+               *state = AUDIT_RECORD_CONTEXT;
+               break;
        }
        return 1;
  }
@@@ -987,184 -994,178 +993,178 @@@ static int audit_log_pid_context(struc
        return rc;
  }
  
- /*
-  * to_send and len_sent accounting are very loose estimates.  We aren't
-  * really worried about a hard cap to MAX_EXECVE_AUDIT_LEN so much as being
-  * within about 500 bytes (next page boundary)
-  *
-  * why snprintf?  an int is up to 12 digits long.  if we just assumed when
-  * logging that a[%d]= was going to be 16 characters long we would be wasting
-  * space in every audit message.  In one 7500 byte message we can log up to
-  * about 1000 min size arguments.  That comes down to about 50% waste of space
-  * if we didn't do the snprintf to find out how long arg_num_len was.
-  */
- static int audit_log_single_execve_arg(struct audit_context *context,
-                                       struct audit_buffer **ab,
-                                       int arg_num,
-                                       size_t *len_sent,
-                                       const char __user *p,
-                                       char *buf)
+ static void audit_log_execve_info(struct audit_context *context,
+                                 struct audit_buffer **ab)
  {
-       char arg_num_len_buf[12];
-       const char __user *tmp_p = p;
-       /* how many digits are in arg_num? 5 is the length of ' a=""' */
-       size_t arg_num_len = snprintf(arg_num_len_buf, 12, "%d", arg_num) + 5;
-       size_t len, len_left, to_send;
-       size_t max_execve_audit_len = MAX_EXECVE_AUDIT_LEN;
-       unsigned int i, has_cntl = 0, too_long = 0;
-       int ret;
-       /* strnlen_user includes the null we don't want to send */
-       len_left = len = strnlen_user(p, MAX_ARG_STRLEN) - 1;
-       /*
-        * We just created this mm, if we can't find the strings
-        * we just copied into it something is _very_ wrong. Similar
-        * for strings that are too long, we should not have created
-        * any.
-        */
-       if (WARN_ON_ONCE(len < 0 || len > MAX_ARG_STRLEN - 1)) {
-               send_sig(SIGKILL, current, 0);
-               return -1;
+       long len_max;
+       long len_rem;
+       long len_full;
+       long len_buf;
+       long len_abuf;
+       long len_tmp;
+       bool require_data;
+       bool encode;
+       unsigned int iter;
+       unsigned int arg;
+       char *buf_head;
+       char *buf;
+       const char __user *p = (const char __user *)current->mm->arg_start;
+       /* NOTE: this buffer needs to be large enough to hold all the non-arg
+        *       data we put in the audit record for this argument (see the
+        *       code below) ... at this point in time 96 is plenty */
+       char abuf[96];
+       /* NOTE: we set MAX_EXECVE_AUDIT_LEN to a rather arbitrary limit, the
+        *       current value of 7500 is not as important as the fact that it
+        *       is less than 8k, a setting of 7500 gives us plenty of wiggle
+        *       room if we go over a little bit in the logging below */
+       WARN_ON_ONCE(MAX_EXECVE_AUDIT_LEN > 7500);
+       len_max = MAX_EXECVE_AUDIT_LEN;
+       /* scratch buffer to hold the userspace args */
+       buf_head = kmalloc(MAX_EXECVE_AUDIT_LEN + 1, GFP_KERNEL);
+       if (!buf_head) {
+               audit_panic("out of memory for argv string");
+               return;
        }
+       buf = buf_head;
  
-       /* walk the whole argument looking for non-ascii chars */
+       audit_log_format(*ab, "argc=%d", context->execve.argc);
+       len_rem = len_max;
+       len_buf = 0;
+       len_full = 0;
+       require_data = true;
+       encode = false;
+       iter = 0;
+       arg = 0;
        do {
-               if (len_left > MAX_EXECVE_AUDIT_LEN)
-                       to_send = MAX_EXECVE_AUDIT_LEN;
-               else
-                       to_send = len_left;
-               ret = copy_from_user(buf, tmp_p, to_send);
-               /*
-                * There is no reason for this copy to be short. We just
-                * copied them here, and the mm hasn't been exposed to user-
-                * space yet.
-                */
-               if (ret) {
-                       WARN_ON(1);
-                       send_sig(SIGKILL, current, 0);
-                       return -1;
-               }
-               buf[to_send] = '\0';
-               has_cntl = audit_string_contains_control(buf, to_send);
-               if (has_cntl) {
-                       /*
-                        * hex messages get logged as 2 bytes, so we can only
-                        * send half as much in each message
-                        */
-                       max_execve_audit_len = MAX_EXECVE_AUDIT_LEN / 2;
-                       break;
-               }
-               len_left -= to_send;
-               tmp_p += to_send;
-       } while (len_left > 0);
-       len_left = len;
-       if (len > max_execve_audit_len)
-               too_long = 1;
-       /* rewalk the argument actually logging the message */
-       for (i = 0; len_left > 0; i++) {
-               int room_left;
-               if (len_left > max_execve_audit_len)
-                       to_send = max_execve_audit_len;
-               else
-                       to_send = len_left;
-               /* do we have space left to send this argument in this ab? */
-               room_left = MAX_EXECVE_AUDIT_LEN - arg_num_len - *len_sent;
-               if (has_cntl)
-                       room_left -= (to_send * 2);
-               else
-                       room_left -= to_send;
-               if (room_left < 0) {
-                       *len_sent = 0;
-                       audit_log_end(*ab);
-                       *ab = audit_log_start(context, GFP_KERNEL, AUDIT_EXECVE);
-                       if (!*ab)
-                               return 0;
-               }
+               /* NOTE: we don't ever want to trust this value for anything
+                *       serious, but the audit record format insists we
+                *       provide an argument length for really long arguments,
+                *       e.g. > MAX_EXECVE_AUDIT_LEN, so we have no choice but
+                *       to use strncpy_from_user() to obtain this value for
+                *       recording in the log, although we don't use it
+                *       anywhere here to avoid a double-fetch problem */
+               if (len_full == 0)
+                       len_full = strnlen_user(p, MAX_ARG_STRLEN) - 1;
+               /* read more data from userspace */
+               if (require_data) {
+                       /* can we make more room in the buffer? */
+                       if (buf != buf_head) {
+                               memmove(buf_head, buf, len_buf);
+                               buf = buf_head;
+                       }
+                       /* fetch as much as we can of the argument */
+                       len_tmp = strncpy_from_user(&buf_head[len_buf], p,
+                                                   len_max - len_buf);
+                       if (len_tmp == -EFAULT) {
+                               /* unable to copy from userspace */
+                               send_sig(SIGKILL, current, 0);
+                               goto out;
+                       } else if (len_tmp == (len_max - len_buf)) {
+                               /* buffer is not large enough */
+                               require_data = true;
+                               /* NOTE: if we are going to span multiple
+                                *       buffers force the encoding so we stand
+                                *       a chance at a sane len_full value and
+                                *       consistent record encoding */
+                               encode = true;
+                               len_full = len_full * 2;
+                               p += len_tmp;
+                       } else {
+                               require_data = false;
+                               if (!encode)
+                                       encode = audit_string_contains_control(
+                                                               buf, len_tmp);
+                               /* try to use a trusted value for len_full */
+                               if (len_full < len_max)
+                                       len_full = (encode ?
+                                                   len_tmp * 2 : len_tmp);
+                               p += len_tmp + 1;
+                       }
+                       len_buf += len_tmp;
+                       buf_head[len_buf] = '\0';
  
-               /*
-                * first record needs to say how long the original string was
-                * so we can be sure nothing was lost.
-                */
-               if ((i == 0) && (too_long))
-                       audit_log_format(*ab, " a%d_len=%zu", arg_num,
-                                        has_cntl ? 2*len : len);
-               /*
-                * normally arguments are small enough to fit and we already
-                * filled buf above when we checked for control characters
-                * so don't bother with another copy_from_user
-                */
-               if (len >= max_execve_audit_len)
-                       ret = copy_from_user(buf, p, to_send);
-               else
-                       ret = 0;
-               if (ret) {
-                       WARN_ON(1);
-                       send_sig(SIGKILL, current, 0);
-                       return -1;
+                       /* length of the buffer in the audit record? */
+                       len_abuf = (encode ? len_buf * 2 : len_buf + 2);
                }
-               buf[to_send] = '\0';
-               /* actually log it */
-               audit_log_format(*ab, " a%d", arg_num);
-               if (too_long)
-                       audit_log_format(*ab, "[%d]", i);
-               audit_log_format(*ab, "=");
-               if (has_cntl)
-                       audit_log_n_hex(*ab, buf, to_send);
-               else
-                       audit_log_string(*ab, buf);
-               p += to_send;
-               len_left -= to_send;
-               *len_sent += arg_num_len;
-               if (has_cntl)
-                       *len_sent += to_send * 2;
-               else
-                       *len_sent += to_send;
-       }
-       /* include the null we didn't log */
-       return len + 1;
- }
  
- static void audit_log_execve_info(struct audit_context *context,
-                                 struct audit_buffer **ab)
- {
-       int i, len;
-       size_t len_sent = 0;
-       const char __user *p;
-       char *buf;
+               /* write as much as we can to the audit log */
+               if (len_buf > 0) {
+                       /* NOTE: some magic numbers here - basically if we
+                        *       can't fit a reasonable amount of data into the
+                        *       existing audit buffer, flush it and start with
+                        *       a new buffer */
+                       if ((sizeof(abuf) + 8) > len_rem) {
+                               len_rem = len_max;
+                               audit_log_end(*ab);
+                               *ab = audit_log_start(context,
+                                                     GFP_KERNEL, AUDIT_EXECVE);
+                               if (!*ab)
+                                       goto out;
+                       }
  
-       p = (const char __user *)current->mm->arg_start;
+                       /* create the non-arg portion of the arg record */
+                       len_tmp = 0;
+                       if (require_data || (iter > 0) ||
+                           ((len_abuf + sizeof(abuf)) > len_rem)) {
+                               if (iter == 0) {
+                                       len_tmp += snprintf(&abuf[len_tmp],
+                                                       sizeof(abuf) - len_tmp,
+                                                       " a%d_len=%lu",
+                                                       arg, len_full);
+                               }
+                               len_tmp += snprintf(&abuf[len_tmp],
+                                                   sizeof(abuf) - len_tmp,
+                                                   " a%d[%d]=", arg, iter++);
+                       } else
+                               len_tmp += snprintf(&abuf[len_tmp],
+                                                   sizeof(abuf) - len_tmp,
+                                                   " a%d=", arg);
+                       WARN_ON(len_tmp >= sizeof(abuf));
+                       abuf[sizeof(abuf) - 1] = '\0';
+                       /* log the arg in the audit record */
+                       audit_log_format(*ab, "%s", abuf);
+                       len_rem -= len_tmp;
+                       len_tmp = len_buf;
+                       if (encode) {
+                               if (len_abuf > len_rem)
+                                       len_tmp = len_rem / 2; /* encoding */
+                               audit_log_n_hex(*ab, buf, len_tmp);
+                               len_rem -= len_tmp * 2;
+                               len_abuf -= len_tmp * 2;
+                       } else {
+                               if (len_abuf > len_rem)
+                                       len_tmp = len_rem - 2; /* quotes */
+                               audit_log_n_string(*ab, buf, len_tmp);
+                               len_rem -= len_tmp + 2;
+                               /* don't subtract the "2" because we still need
+                                * to add quotes to the remaining string */
+                               len_abuf -= len_tmp;
+                       }
+                       len_buf -= len_tmp;
+                       buf += len_tmp;
+               }
  
-       audit_log_format(*ab, "argc=%d", context->execve.argc);
+               /* ready to move to the next argument? */
+               if ((len_buf == 0) && !require_data) {
+                       arg++;
+                       iter = 0;
+                       len_full = 0;
+                       require_data = true;
+                       encode = false;
+               }
+       } while (arg < context->execve.argc);
  
-       /*
-        * we need some kernel buffer to hold the userspace args.  Just
-        * allocate one big one rather than allocating one of the right size
-        * for every single argument inside audit_log_single_execve_arg()
-        * should be <8k allocation so should be pretty safe.
-        */
-       buf = kmalloc(MAX_EXECVE_AUDIT_LEN + 1, GFP_KERNEL);
-       if (!buf) {
-               audit_panic("out of memory for argv string");
-               return;
-       }
+       /* NOTE: the caller handles the final audit_log_end() call */
  
-       for (i = 0; i < context->execve.argc; i++) {
-               len = audit_log_single_execve_arg(context, ab, i,
-                                                 &len_sent, p, buf);
-               if (len <= 0)
-                       break;
-               p += len;
-       }
-       kfree(buf);
+ out:
+       kfree(buf_head);
  }
  
  static void show_special(struct audit_context *context, int *call_panic)
@@@ -1425,7 -1426,7 +1425,7 @@@ static void audit_log_exit(struct audit
        if (context->pwd.dentry && context->pwd.mnt) {
                ab = audit_log_start(context, GFP_KERNEL, AUDIT_CWD);
                if (ab) {
-                       audit_log_d_path(ab, " cwd=", &context->pwd);
+                       audit_log_d_path(ab, "cwd=", &context->pwd);
                        audit_log_end(ab);
                }
        }
@@@ -1979,26 -1980,21 +1979,26 @@@ static void audit_log_set_loginuid(kuid
  {
        struct audit_buffer *ab;
        uid_t uid, oldloginuid, loginuid;
 +      struct tty_struct *tty;
  
        if (!audit_enabled)
                return;
  
 +      ab = audit_log_start(NULL, GFP_KERNEL, AUDIT_LOGIN);
 +      if (!ab)
 +              return;
 +
        uid = from_kuid(&init_user_ns, task_uid(current));
        oldloginuid = from_kuid(&init_user_ns, koldloginuid);
        loginuid = from_kuid(&init_user_ns, kloginuid),
 +      tty = audit_get_tty(current);
  
 -      ab = audit_log_start(NULL, GFP_KERNEL, AUDIT_LOGIN);
 -      if (!ab)
 -              return;
        audit_log_format(ab, "pid=%d uid=%u", task_pid_nr(current), uid);
        audit_log_task_context(ab);
 -      audit_log_format(ab, " old-auid=%u auid=%u old-ses=%u ses=%u res=%d",
 -                       oldloginuid, loginuid, oldsessionid, sessionid, !rc);
 +      audit_log_format(ab, " old-auid=%u auid=%u tty=%s old-ses=%u ses=%u res=%d",
 +                       oldloginuid, loginuid, tty ? tty_name(tty) : "(none)",
 +                       oldsessionid, sessionid, !rc);
 +      audit_put_tty(tty);
        audit_log_end(ab);
  }