# HG changeset patch
# Parent  d45e1cfed495a6e6a2aefcaccc529f09aa38da40
# extended auditing of user actions
# based on:
#   https://bugzilla.mindrot.org/show_bug.cgi?id=1402
#   https://bugzilla.mindrot.org/attachment.cgi?id=2011
#   by jchadima@redhat.com

diff --git a/openssh-6.6p1/audit-bsm.c b/openssh-6.6p1/audit-bsm.c
--- a/openssh-6.6p1/audit-bsm.c
+++ b/openssh-6.6p1/audit-bsm.c
@@ -370,20 +370,33 @@ audit_connection_from(const char *host, 
 	/* this is used on IPv4-only machines */
 	tid->port = (dev_t)port;
 	tid->machine = inet_addr(host);
 	snprintf(buf, sizeof(buf), "%08x", tid->machine);
 	debug3("BSM audit: machine ID %s", buf);
 #endif
 }
 
-void
+int
 audit_run_command(const char *command)
 {
 	/* not implemented */
+	return 0;
+}
+
+void
+audit_end_command(int handle, const char *command)
+{
+	/* not implemented */
+}
+
+void
+audit_count_session_open(void)
+{
+	/* not necessary */
 }
 
 void
 audit_session_open(struct logininfo *li)
 {
 	/* not implemented */
 }
 
diff --git a/openssh-6.6p1/audit-linux.c b/openssh-6.6p1/audit-linux.c
--- a/openssh-6.6p1/audit-linux.c
+++ b/openssh-6.6p1/audit-linux.c
@@ -30,97 +30,210 @@
 #include "includes.h"
 #if defined(USE_LINUX_AUDIT)
 #include <libaudit.h>
 #include <unistd.h>
 #include <string.h>
 
 #include "log.h"
 #include "audit.h"
+#include "key.h"
+#include "hostfile.h"
+#include "auth.h"
+#include "servconf.h"
 #include "canohost.h"
 
+extern ServerOptions options;
+extern Authctxt *the_authctxt;
+extern u_int utmp_len;
 const char* audit_username(void);
 
-int
-linux_audit_record_event(int uid, const char *username,
-    const char *hostname, const char *ip, const char *ttyn, int success)
+static void
+linux_audit_user_logxxx(int uid, const char *username,
+    const char *hostname, const char *ip, const char *ttyn, int success, int event)
 {
 	int audit_fd, rc, saved_errno;
 
 	audit_fd = audit_open();
 	if (audit_fd < 0) {
 		if (errno == EINVAL || errno == EPROTONOSUPPORT ||
 		    errno == EAFNOSUPPORT)
-			return 1; /* No audit support in kernel */
+			return; /* No audit support in kernel */
 		else
-			return 0; /* Must prevent login */
+			goto fatal_report; /* Must prevent login */
 	}
-	rc = audit_log_acct_message(audit_fd, AUDIT_USER_LOGIN,
+	rc = audit_log_acct_message(audit_fd, event,
 	    NULL, "login", username ? username : "(unknown)",
 	    username == NULL ? uid : -1, hostname, ip, ttyn, success);
 	saved_errno = errno;
 	close(audit_fd);
 	/*
 	 * Do not report error if the error is EPERM and sshd is run as non
 	 * root user.
 	 */
 	if ((rc == -EPERM) && (geteuid() != 0))
 		rc = 0;
 	errno = saved_errno;
-	return (rc >= 0);
+	if (rc < 0) {
+fatal_report:
+		fatal("linux_audit_write_entry failed: %s", strerror(errno));
+	}
 }
 
+static void
+linux_audit_user_auth(int uid, const char *username,
+    const char *hostname, const char *ip, const char *ttyn, int success, int event)
+{
+	int audit_fd, rc, saved_errno;
+	static const char *event_name[] = {
+		"maxtries exceeded",
+		"root denied",
+		"success",
+		"none",
+		"password",
+		"challenge-response",
+		"pubkey",
+		"hostbased",
+		"gssapi",
+		"invalid user",
+		"nologin",
+		"connection closed",
+		"connection abandoned",
+		"unknown"
+	};
+
+	audit_fd = audit_open();
+	if (audit_fd < 0) {
+		if (errno == EINVAL || errno == EPROTONOSUPPORT ||
+		    errno == EAFNOSUPPORT)
+			return; /* No audit support in kernel */
+		else
+			goto fatal_report; /* Must prevent login */
+	}
+	
+	if ((event < 0) || (event > SSH_AUDIT_UNKNOWN))
+		event = SSH_AUDIT_UNKNOWN;
+
+	rc = audit_log_acct_message(audit_fd, AUDIT_USER_AUTH,
+	    NULL, event_name[event], username ? username : "(unknown)",
+	    username == NULL ? uid : -1, hostname, ip, ttyn, success);
+	saved_errno = errno;
+	close(audit_fd);
+	/*
+	 * Do not report error if the error is EPERM and sshd is run as non
+	 * root user.
+	 */
+	if ((rc == -EPERM) && (geteuid() != 0))
+		rc = 0;
+	errno = saved_errno;
+	if (rc < 0) {
+fatal_report:
+		fatal("linux_audit_write_entry failed: %s", strerror(errno));
+	}
+}
+
+static int user_login_count = 0;
+
 /* Below is the sshd audit API code */
 
 void
 audit_connection_from(const char *host, int port)
 {
+	/* not implemented */
 }
-	/* not implemented */
+
+int
+audit_run_command(const char *command)
+{
+	if (!user_login_count++) 
+		linux_audit_user_logxxx(the_authctxt->pw->pw_uid, NULL, get_remote_name_or_ip(utmp_len, options.use_dns),
+		    NULL, "ssh", 1, AUDIT_USER_LOGIN);
+	linux_audit_user_logxxx(the_authctxt->pw->pw_uid, NULL, get_remote_name_or_ip(utmp_len, options.use_dns),
+	    NULL, "ssh", 1, AUDIT_USER_START);
+	return 0;
+}
 
 void
-audit_run_command(const char *command)
+audit_end_command(int handle, const char *command)
 {
-	/* not implemented */
+	linux_audit_user_logxxx(the_authctxt->pw->pw_uid, NULL, get_remote_name_or_ip(utmp_len, options.use_dns),
+	    NULL, "ssh", 1, AUDIT_USER_END);
+	if (user_login_count && !--user_login_count) 
+		linux_audit_user_logxxx(the_authctxt->pw->pw_uid, NULL, get_remote_name_or_ip(utmp_len, options.use_dns),
+		    NULL, "ssh", 1, AUDIT_USER_LOGOUT);
+}
+
+void
+audit_count_session_open(void)
+{
+	user_login_count++;
 }
 
 void
 audit_session_open(struct logininfo *li)
 {
-	if (linux_audit_record_event(li->uid, NULL, li->hostname,
-	    NULL, li->line, 1) == 0)
-		fatal("linux_audit_write_entry failed: %s", strerror(errno));
+	if (!user_login_count++) 
+		linux_audit_user_logxxx(li->uid, NULL, li->hostname,
+		    NULL, li->line, 1, AUDIT_USER_LOGIN);
+	linux_audit_user_logxxx(li->uid, NULL, li->hostname,
+	    NULL, li->line, 1, AUDIT_USER_START);
 }
 
 void
 audit_session_close(struct logininfo *li)
 {
-	/* not implemented */
+	linux_audit_user_logxxx(li->uid, NULL, li->hostname,
+	    NULL, li->line, 1, AUDIT_USER_END);
+	if (user_login_count && !--user_login_count) 
+		linux_audit_user_logxxx(li->uid, NULL, li->hostname,
+		    NULL, li->line, 1, AUDIT_USER_LOGOUT);
 }
 
 void
 audit_event(ssh_audit_event_t event)
 {
 	switch(event) {
 	case SSH_AUTH_SUCCESS:
-	case SSH_CONNECTION_CLOSE:
-	case SSH_NOLOGIN:
-	case SSH_LOGIN_EXCEED_MAXTRIES:
-	case SSH_LOGIN_ROOT_DENIED:
+		linux_audit_user_auth(-1, audit_username(), NULL,
+			get_remote_ipaddr(), "ssh", 1, event);
 		break;
 
+	case SSH_NOLOGIN:
+	case SSH_LOGIN_ROOT_DENIED:
+		linux_audit_user_auth(-1, audit_username(), NULL,
+			get_remote_ipaddr(), "ssh", 0, event);
+		linux_audit_user_logxxx(-1, audit_username(), NULL,
+			get_remote_ipaddr(), "ssh", 0, AUDIT_USER_LOGIN);
+		break;
+
+	case SSH_LOGIN_EXCEED_MAXTRIES:
 	case SSH_AUTH_FAIL_NONE:
 	case SSH_AUTH_FAIL_PASSWD:
 	case SSH_AUTH_FAIL_KBDINT:
 	case SSH_AUTH_FAIL_PUBKEY:
 	case SSH_AUTH_FAIL_HOSTBASED:
 	case SSH_AUTH_FAIL_GSSAPI:
+		linux_audit_user_auth(-1, audit_username(), NULL,
+			get_remote_ipaddr(), "ssh", 0, event);
+		break;
+
+	case SSH_CONNECTION_CLOSE:
+		if (user_login_count) {
+			while (user_login_count--)
+				linux_audit_user_logxxx(the_authctxt->pw->pw_uid, NULL, get_remote_name_or_ip(utmp_len, options.use_dns),
+				    NULL, "ssh", 1, AUDIT_USER_END);
+			linux_audit_user_logxxx(the_authctxt->pw->pw_uid, NULL, get_remote_name_or_ip(utmp_len, options.use_dns),
+			    NULL, "ssh", 1, AUDIT_USER_LOGOUT);
+		}
+		break;
+
+	case SSH_CONNECTION_ABANDON:
 	case SSH_INVALID_USER:
-		linux_audit_record_event(-1, audit_username(), NULL,
-			get_remote_ipaddr(), "sshd", 0);
+		linux_audit_user_logxxx(-1, audit_username(), NULL,
+			get_remote_ipaddr(), "ssh", 0, AUDIT_USER_LOGIN);
 		break;
 
 	default:
 		debug("%s: unhandled event %d", __func__, event);
 	}
 }
 
 #endif /* USE_LINUX_AUDIT */
diff --git a/openssh-6.6p1/audit.c b/openssh-6.6p1/audit.c
--- a/openssh-6.6p1/audit.c
+++ b/openssh-6.6p1/audit.c
@@ -135,16 +135,27 @@ audit_connection_from(const char *host, 
 void
 audit_event(ssh_audit_event_t event)
 {
 	debug("audit event euid %d user %s event %d (%s)", geteuid(),
 	    audit_username(), event, audit_event_lookup(event));
 }
 
 /*
+ * Called when a child process has called, or will soon call,
+ * audit_session_open.
+ */
+void
+audit_count_session_open(void)
+{
+	debug("audit count session open euid %d user %s", geteuid(),
+	      audit_username());
+}
+
+/*
  * Called when a user session is started.  Argument is the tty allocated to
  * the session, or NULL if no tty was allocated.
  *
  * Note that this may be called multiple times if multiple sessions are used
  * within a single connection.
  */
 void
 audit_session_open(struct logininfo *li)
@@ -169,18 +180,34 @@ audit_session_close(struct logininfo *li
 
 	debug("audit session close euid %d user %s tty name %s", geteuid(),
 	    audit_username(), t);
 }
 
 /*
  * This will be called when a user runs a non-interactive command.  Note that
  * it may be called multiple times for a single connection since SSH2 allows
- * multiple sessions within a single connection.
+ * multiple sessions within a single connection.  Returns a "handle" for
+ * audit_end_command.
  */
-void
+int
 audit_run_command(const char *command)
 {
 	debug("audit run command euid %d user %s command '%.200s'", geteuid(),
 	    audit_username(), command);
+	return 0;
 }
+
+/*
+ * This will be called when the non-interactive command finishes.  Note that
+ * it may be called multiple times for a single connection since SSH2 allows
+ * multiple sessions within a single connection.  "handle" should come from
+ * the corresponding audit_run_command.
+ */
+void
+audit_end_command(int handle, const char *command)
+{
+	debug("audit end nopty exec  euid %d user %s command '%.200s'", geteuid(),
+	    audit_username(), command);
+}
+
 # endif  /* !defined CUSTOM_SSH_AUDIT_EVENTS */
 #endif /* SSH_AUDIT_EVENTS */
diff --git a/openssh-6.6p1/audit.h b/openssh-6.6p1/audit.h
--- a/openssh-6.6p1/audit.h
+++ b/openssh-6.6p1/audit.h
@@ -44,14 +44,16 @@ enum ssh_audit_event_type {
 	SSH_CONNECTION_CLOSE,	/* closed after attempting auth or session */
 	SSH_CONNECTION_ABANDON,	/* closed without completing auth */
 	SSH_AUDIT_UNKNOWN
 };
 typedef enum ssh_audit_event_type ssh_audit_event_t;
 
 void	audit_connection_from(const char *, int);
 void	audit_event(ssh_audit_event_t);
+void	audit_count_session_open(void);
 void	audit_session_open(struct logininfo *);
 void	audit_session_close(struct logininfo *);
-void	audit_run_command(const char *);
+int	audit_run_command(const char *);
+void 	audit_end_command(int, const char *);
 ssh_audit_event_t audit_classify_auth(const char *);
 
 #endif /* _SSH_AUDIT_H */
diff --git a/openssh-6.6p1/monitor.c b/openssh-6.6p1/monitor.c
--- a/openssh-6.6p1/monitor.c
+++ b/openssh-6.6p1/monitor.c
@@ -175,16 +175,17 @@ int mm_answer_gss_setup_ctx(int, Buffer 
 int mm_answer_gss_accept_ctx(int, Buffer *);
 int mm_answer_gss_userok(int, Buffer *);
 int mm_answer_gss_checkmic(int, Buffer *);
 #endif
 
 #ifdef SSH_AUDIT_EVENTS
 int mm_answer_audit_event(int, Buffer *);
 int mm_answer_audit_command(int, Buffer *);
+int mm_answer_audit_end_command(int, Buffer *);
 #endif
 
 static int monitor_read_log(struct monitor *);
 
 static Authctxt *authctxt;
 static BIGNUM *ssh1_challenge = NULL;	/* used for ssh1 rsa auth */
 
 /* local state for key verify */
@@ -255,16 +256,17 @@ struct mon_table mon_dispatch_postauth20
     {MONITOR_REQ_MODULI, 0, mm_answer_moduli},
     {MONITOR_REQ_SIGN, 0, mm_answer_sign},
     {MONITOR_REQ_PTY, 0, mm_answer_pty},
     {MONITOR_REQ_PTYCLEANUP, 0, mm_answer_pty_cleanup},
     {MONITOR_REQ_TERM, 0, mm_answer_term},
 #ifdef SSH_AUDIT_EVENTS
     {MONITOR_REQ_AUDIT_EVENT, MON_PERMIT, mm_answer_audit_event},
     {MONITOR_REQ_AUDIT_COMMAND, MON_PERMIT, mm_answer_audit_command},
+    {MONITOR_REQ_AUDIT_END_COMMAND, MON_PERMIT, mm_answer_audit_end_command},
 #endif
     {0, 0, NULL}
 };
 
 struct mon_table mon_dispatch_proto15[] = {
     {MONITOR_REQ_PWNAM, MON_ONCE, mm_answer_pwnamallow},
     {MONITOR_REQ_SESSKEY, MON_ONCE, mm_answer_sesskey},
     {MONITOR_REQ_SESSID, MON_ONCE, mm_answer_sessid},
@@ -297,16 +299,17 @@ struct mon_table mon_dispatch_proto15[] 
 
 struct mon_table mon_dispatch_postauth15[] = {
     {MONITOR_REQ_PTY, MON_ONCE, mm_answer_pty},
     {MONITOR_REQ_PTYCLEANUP, MON_ONCE, mm_answer_pty_cleanup},
     {MONITOR_REQ_TERM, 0, mm_answer_term},
 #ifdef SSH_AUDIT_EVENTS
     {MONITOR_REQ_AUDIT_EVENT, MON_PERMIT, mm_answer_audit_event},
     {MONITOR_REQ_AUDIT_COMMAND, MON_PERMIT|MON_ONCE, mm_answer_audit_command},
+    {MONITOR_REQ_AUDIT_END_COMMAND, MON_PERMIT, mm_answer_audit_end_command},
 #endif
     {0, 0, NULL}
 };
 
 struct mon_table *mon_dispatch;
 
 /* Specifies if a certain message is allowed at the moment */
 
@@ -1420,16 +1423,22 @@ mm_record_login(Session *s, struct passw
 static void
 mm_session_close(Session *s)
 {
 	debug3("%s: session %d pid %ld", __func__, s->self, (long)s->pid);
 	if (s->ttyfd != -1) {
 		debug3("%s: tty %s ptyfd %d", __func__, s->tty, s->ptyfd);
 		session_pty_cleanup2(s);
 	}
+#ifdef SSH_AUDIT_EVENTS
+	if (s->command != NULL) {
+		debug3("%s: command %d", __func__, s->command_handle);
+		session_end_command2(s);
+	}
+#endif
 	session_unused(s->self);
 }
 
 int
 mm_answer_pty(int sock, Buffer *m)
 {
 	extern struct monitor *pmonitor;
 	Session *s;
@@ -1742,21 +1751,53 @@ mm_answer_audit_event(int socket, Buffer
 	return (0);
 }
 
 int
 mm_answer_audit_command(int socket, Buffer *m)
 {
 	u_int len;
 	char *cmd;
+	Session *s;
 
 	debug3("%s entering", __func__);
 	cmd = buffer_get_string(m, &len);
 	/* sanity check command, if so how? */
-	audit_run_command(cmd);
+	s = session_new();
+	if (s == NULL)
+		fatal("%s: error allocating a session", __func__);
+	s->command = cmd;
+	s->command_handle = audit_run_command(cmd);
+
+	buffer_clear(m);
+	buffer_put_int(m, s->self);
+
+	mm_request_send(socket, MONITOR_ANS_AUDIT_COMMAND, m);
+
+	return (0);
+}
+
+int
+mm_answer_audit_end_command(int socket, Buffer *m)
+{
+	int handle;
+	u_int len;
+	char *cmd;
+	Session *s;
+
+	debug3("%s entering", __func__);
+	handle = buffer_get_int(m);
+	cmd = buffer_get_string(m, &len);
+
+	s = session_by_id(handle);
+	if (s == NULL || s->ttyfd != -1 || s->command == NULL ||
+	    strcmp(s->command, cmd) != 0)
+		fatal("%s: invalid handle", __func__);
+	mm_session_close(s);
+
 	free(cmd);
 	return (0);
 }
 #endif /* SSH_AUDIT_EVENTS */
 
 void
 monitor_apply_keystate(struct monitor *pmonitor)
 {
diff --git a/openssh-6.6p1/monitor.h b/openssh-6.6p1/monitor.h
--- a/openssh-6.6p1/monitor.h
+++ b/openssh-6.6p1/monitor.h
@@ -59,16 +59,17 @@ enum monitor_reqtype {
 
 	MONITOR_REQ_PAM_START = 100,
 	MONITOR_REQ_PAM_ACCOUNT = 102, MONITOR_ANS_PAM_ACCOUNT = 103,
 	MONITOR_REQ_PAM_INIT_CTX = 104, MONITOR_ANS_PAM_INIT_CTX = 105,
 	MONITOR_REQ_PAM_QUERY = 106, MONITOR_ANS_PAM_QUERY = 107,
 	MONITOR_REQ_PAM_RESPOND = 108, MONITOR_ANS_PAM_RESPOND = 109,
 	MONITOR_REQ_PAM_FREE_CTX = 110, MONITOR_ANS_PAM_FREE_CTX = 111,
 	MONITOR_REQ_AUDIT_EVENT = 112, MONITOR_REQ_AUDIT_COMMAND = 113,
+	MONITOR_ANS_AUDIT_COMMAND = 114, MONITOR_REQ_AUDIT_END_COMMAND = 115,
 
 };
 
 struct mm_master;
 struct monitor {
 	int			 m_recvfd;
 	int			 m_sendfd;
 	int			 m_log_recvfd;
diff --git a/openssh-6.6p1/monitor_wrap.c b/openssh-6.6p1/monitor_wrap.c
--- a/openssh-6.6p1/monitor_wrap.c
+++ b/openssh-6.6p1/monitor_wrap.c
@@ -1184,27 +1184,48 @@ mm_audit_event(ssh_audit_event_t event)
 
 	buffer_init(&m);
 	buffer_put_int(&m, event);
 
 	mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_AUDIT_EVENT, &m);
 	buffer_free(&m);
 }
 
-void
+int
 mm_audit_run_command(const char *command)
 {
 	Buffer m;
+	int handle;
 
 	debug3("%s entering command %s", __func__, command);
 
 	buffer_init(&m);
 	buffer_put_cstring(&m, command);
 
 	mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_AUDIT_COMMAND, &m);
+	mm_request_receive_expect(pmonitor->m_recvfd, MONITOR_ANS_AUDIT_COMMAND, &m);
+
+	handle = buffer_get_int(&m);
+	buffer_free(&m);
+
+	return (handle);
+}
+
+void
+mm_audit_end_command(int handle, const char *command)
+{
+	Buffer m;
+
+	debug3("%s entering command %s", __func__, command);
+
+	buffer_init(&m);
+	buffer_put_int(&m, handle);
+	buffer_put_cstring(&m, command);
+
+	mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_AUDIT_END_COMMAND, &m);
 	buffer_free(&m);
 }
 #endif /* SSH_AUDIT_EVENTS */
 
 #ifdef GSSAPI
 OM_uint32
 mm_ssh_gssapi_server_ctx(Gssctxt **ctx, gss_OID goid)
 {
diff --git a/openssh-6.6p1/monitor_wrap.h b/openssh-6.6p1/monitor_wrap.h
--- a/openssh-6.6p1/monitor_wrap.h
+++ b/openssh-6.6p1/monitor_wrap.h
@@ -69,17 +69,18 @@ void *mm_sshpam_init_ctx(struct Authctxt
 int mm_sshpam_query(void *, char **, char **, u_int *, char ***, u_int **);
 int mm_sshpam_respond(void *, u_int, char **);
 void mm_sshpam_free_ctx(void *);
 #endif
 
 #ifdef SSH_AUDIT_EVENTS
 #include "audit.h"
 void mm_audit_event(ssh_audit_event_t);
-void mm_audit_run_command(const char *);
+int mm_audit_run_command(const char *);
+void mm_audit_end_command(int, const char *);
 #endif
 
 struct Session;
 void mm_terminate(void);
 int mm_pty_allocate(int *, int *, char *, size_t);
 void mm_session_pty_cleanup2(struct Session *);
 
 /* SSHv1 interfaces */
diff --git a/openssh-6.6p1/packet.c b/openssh-6.6p1/packet.c
--- a/openssh-6.6p1/packet.c
+++ b/openssh-6.6p1/packet.c
@@ -294,16 +294,21 @@ packet_start_discard(Enc *enc, Mac *mac,
 /* Returns 1 if remote host is connected via socket, 0 if not. */
 
 int
 packet_connection_is_on_socket(void)
 {
 	struct sockaddr_storage from, to;
 	socklen_t fromlen, tolen;
 
+    /* audit code may end up here through get_remote_ipaddr() or similar before
+     * active state is even allocated */
+    if (!active_state)
+        return 0;
+ 
 	/* filedescriptors in and out are the same, so it's a socket */
 	if (active_state->connection_in == active_state->connection_out)
 		return 1;
 	fromlen = sizeof(from);
 	memset(&from, 0, sizeof(from));
 	if (getpeername(active_state->connection_in, (struct sockaddr *)&from,
 	    &fromlen) < 0)
 		return 0;
diff --git a/openssh-6.6p1/session.c b/openssh-6.6p1/session.c
--- a/openssh-6.6p1/session.c
+++ b/openssh-6.6p1/session.c
@@ -740,16 +740,26 @@ do_exec_pty(Session *s, const char *comm
 	cygwin_set_impersonation_token(INVALID_HANDLE_VALUE);
 #endif
 
 	s->pid = pid;
 
 	/* Parent.  Close the slave side of the pseudo tty. */
 	close(ttyfd);
 
+#ifdef SSH_AUDIT_EVENTS
+# ifndef HAVE_OSF_SIA
+	/* do_login in the child did not affect state in this process,
+	   compensate.  From an architectural standpoint, this is extremely
+	   ugly. */
+	if (!(options.use_login && command == NULL))
+		audit_count_session_open();
+# endif
+#endif
+
 	/* Enter interactive session. */
 	s->ptymaster = ptymaster;
 	packet_set_interactive(1, 
 	    options.ip_qos_interactive, options.ip_qos_bulk);
 	if (compat20) {
 		session_set_fds(s, ptyfd, fdout, -1, 1, 1);
 	} else {
 		server_loop(pid, ptyfd, fdout, -1);
@@ -834,25 +844,29 @@ do_exec(Session *s, const char *command)
 	    session_type,
 	    tty == NULL ? "" : " on ",
 	    tty == NULL ? "" : tty,
 	    s->pw->pw_name,
 	    get_remote_ipaddr(),
 	    get_remote_port());
 
 #ifdef SSH_AUDIT_EVENTS
+	if (s->command != NULL || s->command_handle != -1)
+		fatal("do_exec: command already set");
 	if (command != NULL)
-		PRIVSEP(audit_run_command(command));
+		s->command = xstrdup(command);
 	else if (s->ttyfd == -1) {
 		char *shell = s->pw->pw_shell;
 
 		if (shell[0] == '\0')	/* empty shell means /bin/sh */
 			shell =_PATH_BSHELL;
-		PRIVSEP(audit_run_command(shell));
+		s->command = xstrdup(shell);
 	}
+	if (s->command != NULL)
+		s->command_handle = PRIVSEP(audit_run_command(s->command));
 #endif
 	if (s->ttyfd != -1)
 		ret = do_exec_pty(s, command);
 	else
 		ret = do_exec_no_pty(s, command);
 
 	original_command = NULL;
 
@@ -1908,16 +1922,19 @@ session_unused(int id)
 	memset(&sessions[id], 0, sizeof(*sessions));
 	sessions[id].self = id;
 	sessions[id].used = 0;
 	sessions[id].chanid = -1;
 	sessions[id].ptyfd = -1;
 	sessions[id].ttyfd = -1;
 	sessions[id].ptymaster = -1;
 	sessions[id].x11_chanids = NULL;
+#ifdef SSH_AUDIT_EVENTS
+	sessions[id].command_handle = -1;
+#endif
 	sessions[id].next_unused = sessions_first_unused;
 	sessions_first_unused = id;
 }
 
 Session *
 session_new(void)
 {
 	Session *s, *tmp;
@@ -1990,16 +2007,29 @@ session_open(Authctxt *authctxt, int cha
 	if (s->pw == NULL || !authctxt->valid)
 		fatal("no user for session %d", s->self);
 	debug("session_open: session %d: link with channel %d", s->self, chanid);
 	s->chanid = chanid;
 	return 1;
 }
 
 Session *
+session_by_id(int id)
+{
+	if (id >= 0 && id < sessions_nalloc) {
+		Session *s = &sessions[id];
+		if (s->used)
+			return s;
+	}
+	debug("session_by_id: unknown id %d", id);
+	session_dump();
+	return NULL;
+}
+
+Session *
 session_by_tty(char *tty)
 {
 	int i;
 	for (i = 0; i < sessions_nalloc; i++) {
 		Session *s = &sessions[i];
 		if (s->used && s->ttyfd != -1 && strcmp(s->tty, tty) == 0) {
 			debug("session_by_tty: session %d tty %s", i, tty);
 			return s;
@@ -2506,16 +2536,40 @@ session_exit_message(Session *s, int sta
 	 * interested in data we write.
 	 * Note that we must not call 'chan_read_failed', since there could
 	 * be some more data waiting in the pipe.
 	 */
 	if (c->ostate != CHAN_OUTPUT_CLOSED)
 		chan_write_failed(c);
 }
 
+#ifdef SSH_AUDIT_EVENTS
+void
+session_end_command2(Session *s)
+{
+	if (s->command != NULL) {
+		audit_end_command(s->command_handle, s->command);
+		free(s->command);
+		s->command = NULL;
+		s->command_handle = -1;
+	}
+}
+
+static void
+session_end_command(Session *s)
+{
+	if (s->command != NULL) {
+		PRIVSEP(audit_end_command(s->command_handle, s->command));
+		free(s->command);
+		s->command = NULL;
+		s->command_handle = -1;
+	}
+}
+#endif
+
 void
 session_close(Session *s)
 {
 	u_int i;
 	int do_xauth;
 
 	debug("session_close: session %d pid %ld", s->self, (long)s->pid);
 
@@ -2546,16 +2600,20 @@ session_close(Session *s)
 			int status;
 
 			waitpid(pid, &status, 0);
 		}
 	}
 
 	if (s->ttyfd != -1)
 		session_pty_cleanup(s);
+#ifdef SSH_AUDIT_EVENTS
+	if (s->command)
+		session_end_command(s);
+#endif
 	free(s->term);
 	free(s->display);
 	free(s->x11_chanids);
 	free(s->auth_display);
 	free(s->auth_data);
 	free(s->auth_proto);
 	free(s->subsys);
 	if (s->env != NULL) {
@@ -2760,16 +2818,25 @@ session_setup_x11fwd(Session *s)
 }
 
 static void
 do_authenticated2(Authctxt *authctxt)
 {
 	server_loop2(authctxt);
 }
 
+static void
+do_cleanup_one_session(Session *s)
+{
+	session_pty_cleanup2(s);
+#ifdef SSH_AUDIT_EVENTS
+	session_end_command2(s);
+#endif
+}
+
 void
 do_cleanup(Authctxt *authctxt)
 {
 	static int called = 0;
 
 	debug("do_cleanup");
 
 	/* no cleanup if we're in the child for login shell */
@@ -2808,10 +2875,10 @@ do_cleanup(Authctxt *authctxt)
 	/* remove agent socket */
 	auth_sock_cleanup_proc(authctxt->pw);
 
 	/*
 	 * Cleanup ptys/utmp only if privsep is disabled,
 	 * or if running in monitor.
 	 */
 	if (!use_privsep || mm_is_monitor())
-		session_destroy_all(session_pty_cleanup2);
+		session_destroy_all(do_cleanup_one_session);
 }
diff --git a/openssh-6.6p1/session.h b/openssh-6.6p1/session.h
--- a/openssh-6.6p1/session.h
+++ b/openssh-6.6p1/session.h
@@ -56,29 +56,37 @@ struct Session {
 	int	*x11_chanids;
 	int	is_subsystem;
 	char	*subsys;
 	u_int	num_env;
 	struct {
 		char	*name;
 		char	*val;
 	} *env;
+
+	/* exec */
+#ifdef SSH_AUDIT_EVENTS
+	int	command_handle;
+	char	*command;
+#endif
 };
 
 void	 do_authenticated(Authctxt *);
 void	 do_cleanup(Authctxt *);
 
 int	 session_open(Authctxt *, int);
 void	 session_unused(int);
 int	 session_input_channel_req(Channel *, const char *);
 void	 session_close_by_pid(pid_t, int);
 void	 session_close_by_channel(int, void *);
 void	 session_destroy_all(void (*)(Session *));
 void	 session_pty_cleanup2(Session *);
+void	 session_end_command2(Session *);
 
 Session	*session_new(void);
+Session *session_by_id(int);
 Session	*session_by_tty(char *);
 void	 session_close(Session *);
 void	 do_setusercontext(struct passwd *);
 void	 child_set_env(char ***envp, u_int *envsizep, const char *name,
 		       const char *value);
 
 #endif
diff --git a/openssh-6.6p1/sshd.c b/openssh-6.6p1/sshd.c
--- a/openssh-6.6p1/sshd.c
+++ b/openssh-6.6p1/sshd.c
@@ -2537,13 +2537,14 @@ cleanup_exit(int i)
 			if (kill(pmonitor->m_pid, SIGKILL) != 0 &&
 			    errno != ESRCH)
 				error("%s: kill(%d): %s", __func__,
 				    pmonitor->m_pid, strerror(errno));
 		}
 	}
 #ifdef SSH_AUDIT_EVENTS
 	/* done after do_cleanup so it can cancel the PAM auth 'thread' */
-	if (!use_privsep || mm_is_monitor())
+	if ((the_authctxt == NULL || !the_authctxt->authenticated) &&
+	    (!use_privsep || mm_is_monitor()))
 		audit_event(SSH_CONNECTION_ABANDON);
 #endif
 	_exit(i);
 }
