- (dtucker) [Makefile.in auth.c auth.h auth1.c auth2.c loginrec.c monitor.c
monitor.h monitor_wrap.c monitor_wrap.h session.c sshd.c] Bug #125:
(first stage) Add audit instrumentation to sshd, currently disabled by
default. with suggestions from and djm@
diff --git a/ChangeLog b/ChangeLog
index 07ae663..e2dc30e 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -14,6 +14,10 @@
attempts (currently only for password, kbdint and C/R, only on Linux and
HP-UX), based on code from login.c from util-linux. With ashok_kovai at
hotmail.com, ok djm@
+ - (dtucker) [Makefile.in auth.c auth.h auth1.c auth2.c loginrec.c monitor.c
+ monitor.h monitor_wrap.c monitor_wrap.h session.c sshd.c] Bug #125:
+ (first stage) Add audit instrumentation to sshd, currently disabled by
+ default. with suggestions from and djm@
20050201
- (dtucker) [log.c] Bug #973: force log_init() to open syslog, since on some
@@ -2068,4 +2072,4 @@
- (djm) Trim deprecated options from INSTALL. Mention UsePAM
- (djm) Fix quote handling in sftp; Patch from admorten AT umich.edu
-$Id: ChangeLog,v 1.3632 2005/02/02 12:30:24 dtucker Exp $
+$Id: ChangeLog,v 1.3633 2005/02/02 13:20:53 dtucker Exp $
diff --git a/Makefile.in b/Makefile.in
index 8a1c9f7..c6cfef1 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -1,4 +1,4 @@
-# $Id: Makefile.in,v 1.267 2005/01/18 01:05:18 dtucker Exp $
+# $Id: Makefile.in,v 1.268 2005/02/02 13:20:53 dtucker Exp $
# uncomment if you run a non bourne compatable shell. Ie. csh
#SHELL = @SH@
@@ -85,7 +85,7 @@
monitor_mm.o monitor.o monitor_wrap.o kexdhs.o kexgexs.o \
auth-krb5.o \
auth2-gss.o gss-serv.o gss-serv-krb5.o \
- loginrec.o auth-pam.o auth-shadow.o auth-sia.o md5crypt.o
+ loginrec.o auth-pam.o auth-shadow.o auth-sia.o md5crypt.o audit.o
MANPAGES = scp.1.out ssh-add.1.out ssh-agent.1.out ssh-keygen.1.out ssh-keyscan.1.out ssh.1.out sshd.8.out sftp-server.8.out sftp.1.out ssh-rand-helper.8.out ssh-keysign.8.out sshd_config.5.out ssh_config.5.out
MANPAGES_IN = scp.1 ssh-add.1 ssh-agent.1 ssh-keygen.1 ssh-keyscan.1 ssh.1 sshd.8 sftp-server.8 sftp.1 ssh-rand-helper.8 ssh-keysign.8 sshd_config.5 ssh_config.5
diff --git a/auth.c b/auth.c
index b6c00c1..bbf3a54 100644
--- a/auth.c
+++ b/auth.c
@@ -51,6 +51,7 @@
#include "bufaux.h"
#include "packet.h"
#include "loginrec.h"
+#include "monitor_wrap.h"
/* import */
extern ServerOptions options;
@@ -251,6 +252,44 @@
record_failed_login(authctxt->user,
get_canonical_hostname(options.use_dns), "ssh");
#endif
+#ifdef AUDIT_EVENTS
+ if (authenticated == 0 && !authctxt->postponed) {
+ ssh_audit_event_t event;
+
+ debug3("audit failed auth attempt, method %s euid %d",
+ method, (int)geteuid());
+ /*
+ * Because the auth loop is used in both monitor and slave,
+ * we must be careful to send each event only once and with
+ * enough privs to write the event.
+ */
+ event = audit_classify_auth(method);
+ switch(event) {
+ case AUTH_FAIL_NONE:
+ case AUTH_FAIL_PASSWD:
+ case AUTH_FAIL_KBDINT:
+ if (geteuid() == 0)
+ audit_event(event);
+ break;
+ case AUTH_FAIL_PUBKEY:
+ case AUTH_FAIL_HOSTBASED:
+ case AUTH_FAIL_GSSAPI:
+ /*
+ * This is required to handle the case where privsep
+ * is enabled but it's root logging in, since
+ * use_privsep won't be cleared until after a
+ * successful login.
+ */
+ if (geteuid() == 0)
+ audit_event(event);
+ else
+ PRIVSEP(audit_event(event));
+ break;
+ default:
+ error("unknown authentication audit event %d", event);
+ }
+ }
+#endif
}
/*
@@ -476,6 +515,9 @@
record_failed_login(user,
get_canonical_hostname(options.use_dns), "ssh");
#endif
+#ifdef AUDIT_EVENTS
+ audit_event(INVALID_USER);
+#endif /* AUDIT_EVENTS */
return (NULL);
}
if (!allowed_user(pw))
diff --git a/auth.h b/auth.h
index 6c0089d..8d1f934 100644
--- a/auth.h
+++ b/auth.h
@@ -130,6 +130,7 @@
#endif
#include "auth-pam.h"
+#include "audit.h"
void remove_kbdint_device(const char *);
void disable_forwarding(void);
diff --git a/auth1.c b/auth1.c
index 2a9d18b..aeb5d8c 100644
--- a/auth1.c
+++ b/auth1.c
@@ -247,8 +247,12 @@
#else
/* Special handling for root */
if (authenticated && authctxt->pw->pw_uid == 0 &&
- !auth_root_allowed(get_authname(type)))
+ !auth_root_allowed(get_authname(type))) {
authenticated = 0;
+# ifdef AUDIT_EVENTS
+ PRIVSEP(audit_event(LOGIN_ROOT_DENIED));
+# endif
+ }
#endif
#ifdef USE_PAM
@@ -283,8 +287,12 @@
if (authenticated)
return;
- if (authctxt->failures++ > options.max_authtries)
+ if (authctxt->failures++ > options.max_authtries) {
+#ifdef AUDIT_EVENTS
+ PRIVSEP(audit_event(LOGIN_EXCEED_MAXTRIES));
+#endif
packet_disconnect(AUTH_FAIL_MSG, authctxt->user);
+ }
packet_start(SSH_SMSG_FAILURE);
packet_send();
diff --git a/auth2.c b/auth2.c
index 60e261f..2727e0f 100644
--- a/auth2.c
+++ b/auth2.c
@@ -167,6 +167,9 @@
if (options.use_pam)
PRIVSEP(start_pam(authctxt));
#endif
+#ifdef AUDIT_EVENTS
+ PRIVSEP(audit_event(INVALID_USER));
+#endif
}
setproctitle("%s%s", authctxt->valid ? user : "unknown",
use_privsep ? " [net]" : "");
@@ -214,8 +217,12 @@
/* Special handling for root */
if (authenticated && authctxt->pw->pw_uid == 0 &&
- !auth_root_allowed(method))
+ !auth_root_allowed(method)) {
authenticated = 0;
+#ifdef AUDIT_EVENTS
+ PRIVSEP(audit_event(LOGIN_ROOT_DENIED));
+#endif
+ }
#ifdef USE_PAM
if (options.use_pam && authenticated) {
@@ -255,8 +262,12 @@
/* now we can break out */
authctxt->success = 1;
} else {
- if (authctxt->failures++ > options.max_authtries)
+ if (authctxt->failures++ > options.max_authtries) {
+#ifdef AUDIT_EVENTS
+ PRIVSEP(audit_event(LOGIN_EXCEED_MAXTRIES));
+#endif
packet_disconnect(AUTH_FAIL_MSG, authctxt->user);
+ }
methods = authmethods_get();
packet_start(SSH2_MSG_USERAUTH_FAILURE);
packet_put_cstring(methods);
diff --git a/loginrec.c b/loginrec.c
index e77318b..0fa9bde 100644
--- a/loginrec.c
+++ b/loginrec.c
@@ -154,6 +154,7 @@
#include "atomicio.h"
#include "packet.h"
#include "canohost.h"
+#include "auth.h"
#ifdef HAVE_UTIL_H
# include <util.h>
@@ -163,7 +164,7 @@
# include <libutil.h>
#endif
-RCSID("$Id: loginrec.c,v 1.63 2005/02/02 12:30:25 dtucker Exp $");
+RCSID("$Id: loginrec.c,v 1.64 2005/02/02 13:20:53 dtucker Exp $");
/**
** prototypes for helper functions in this file
@@ -443,6 +444,12 @@
!sys_auth_record_login(li->username,li->hostname,li->line))
logit("Writing login record failed for %s", li->username);
#endif
+#ifdef AUDIT_EVENTS
+ if (li->type == LTYPE_LOGIN)
+ audit_session_open(li->line);
+ else if (li->type == LTYPE_LOGOUT)
+ audit_session_close(li->line);
+#endif
return (0);
}
diff --git a/monitor.c b/monitor.c
index 00d4a78..ce7784a 100644
--- a/monitor.c
+++ b/monitor.c
@@ -143,6 +143,11 @@
int mm_answer_gss_checkmic(int, Buffer *);
#endif
+#ifdef AUDIT_EVENTS
+int mm_answer_audit_event(int, Buffer *);
+int mm_answer_audit_command(int, Buffer *);
+#endif
+
static Authctxt *authctxt;
static BIGNUM *ssh1_challenge = NULL; /* used for ssh1 rsa auth */
@@ -186,6 +191,9 @@
{MONITOR_REQ_PAM_RESPOND, MON_ISAUTH, mm_answer_pam_respond},
{MONITOR_REQ_PAM_FREE_CTX, MON_ONCE|MON_AUTHDECIDE, mm_answer_pam_free_ctx},
#endif
+#ifdef AUDIT_EVENTS
+ {MONITOR_REQ_AUDIT_EVENT, 0, mm_answer_audit_event},
+#endif
#ifdef BSD_AUTH
{MONITOR_REQ_BSDAUTHQUERY, MON_ISAUTH, mm_answer_bsdauthquery},
{MONITOR_REQ_BSDAUTHRESPOND, MON_AUTH,mm_answer_bsdauthrespond},
@@ -211,6 +219,10 @@
{MONITOR_REQ_PTY, 0, mm_answer_pty},
{MONITOR_REQ_PTYCLEANUP, 0, mm_answer_pty_cleanup},
{MONITOR_REQ_TERM, 0, mm_answer_term},
+#ifdef AUDIT_EVENTS
+ {MONITOR_REQ_AUDIT_EVENT, MON_PERMIT, mm_answer_audit_event},
+ {MONITOR_REQ_AUDIT_COMMAND, MON_PERMIT, mm_answer_audit_command},
+#endif
{0, 0, NULL}
};
@@ -239,6 +251,9 @@
{MONITOR_REQ_PAM_RESPOND, MON_ISAUTH, mm_answer_pam_respond},
{MONITOR_REQ_PAM_FREE_CTX, MON_ONCE|MON_AUTHDECIDE, mm_answer_pam_free_ctx},
#endif
+#ifdef AUDIT_EVENTS
+ {MONITOR_REQ_AUDIT_EVENT, 0, mm_answer_audit_event},
+#endif
{0, 0, NULL}
};
@@ -246,6 +261,10 @@
{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 AUDIT_EVENTS
+ {MONITOR_REQ_AUDIT_EVENT, MON_PERMIT, mm_answer_audit_event},
+ {MONITOR_REQ_AUDIT_COMMAND, MON_PERMIT|MON_ONCE, mm_answer_audit_command},
+#endif
{0, 0, NULL}
};
@@ -609,6 +628,9 @@
if (options.use_pam)
monitor_permit(mon_dispatch, MONITOR_REQ_PAM_START, 1);
#endif
+#ifdef AUDIT_EVENTS
+ monitor_permit(mon_dispatch, MONITOR_REQ_AUDIT_EVENT, 1);
+#endif
return (0);
}
@@ -1491,6 +1513,49 @@
exit(res);
}
+#ifdef AUDIT_EVENTS
+/* Report that an audit event occurred */
+int
+mm_answer_audit_event(int socket, Buffer *m)
+{
+ ssh_audit_event_t event;
+
+ debug3("%s entering", __func__);
+
+ event = buffer_get_int(m);
+ buffer_free(m);
+ switch(event) {
+ case AUTH_FAIL_PUBKEY:
+ case AUTH_FAIL_HOSTBASED:
+ case AUTH_FAIL_GSSAPI:
+ case LOGIN_EXCEED_MAXTRIES:
+ case LOGIN_ROOT_DENIED:
+ case CONNECTION_CLOSE:
+ audit_event(event);
+ break;
+ default:
+ fatal("Audit event type %d not permitted", event);
+ }
+
+ return (0);
+}
+
+int
+mm_answer_audit_command(int socket, Buffer *m)
+{
+ u_int len;
+ char *cmd;
+
+ debug3("%s entering", __func__);
+ cmd = buffer_get_string(m, &len);
+ /* sanity check command, if so how? */
+ audit_run_command(cmd);
+ xfree(cmd);
+ buffer_free(m);
+ return (0);
+}
+#endif /* AUDIT_EVENTS */
+
void
monitor_apply_keystate(struct monitor *pmonitor)
{
diff --git a/monitor.h b/monitor.h
index 621a4ad..13ce3e1 100644
--- a/monitor.h
+++ b/monitor.h
@@ -59,6 +59,7 @@
MONITOR_REQ_PAM_QUERY, MONITOR_ANS_PAM_QUERY,
MONITOR_REQ_PAM_RESPOND, MONITOR_ANS_PAM_RESPOND,
MONITOR_REQ_PAM_FREE_CTX, MONITOR_ANS_PAM_FREE_CTX,
+ MONITOR_REQ_AUDIT_EVENT, MONITOR_REQ_AUDIT_COMMAND,
MONITOR_REQ_TERM
};
diff --git a/monitor_wrap.c b/monitor_wrap.c
index 2385763..983b240 100644
--- a/monitor_wrap.c
+++ b/monitor_wrap.c
@@ -1103,6 +1103,36 @@
return (success);
}
+#ifdef AUDIT_EVENTS
+void
+mm_audit_event(ssh_audit_event_t event)
+{
+ Buffer m;
+
+ debug3("%s entering", __func__);
+
+ buffer_init(&m);
+ buffer_put_int(&m, event);
+
+ mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_AUDIT_EVENT, &m);
+ buffer_free(&m);
+}
+
+void
+mm_audit_run_command(const char *command)
+{
+ Buffer m;
+
+ 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);
+ buffer_free(&m);
+}
+#endif /* AUDIT_EVENTS */
+
#ifdef GSSAPI
OM_uint32
mm_ssh_gssapi_server_ctx(Gssctxt **ctx, gss_OID goid)
diff --git a/monitor_wrap.h b/monitor_wrap.h
index e5cf571..7ed241a 100644
--- a/monitor_wrap.h
+++ b/monitor_wrap.h
@@ -74,6 +74,12 @@
void mm_sshpam_free_ctx(void *);
#endif
+#ifdef AUDIT_EVENTS
+#include "audit.h"
+void mm_audit_event(ssh_audit_event_t);
+void mm_audit_run_command(const char *);
+#endif
+
struct Session;
void mm_terminate(void);
int mm_pty_allocate(int *, int *, char *, int);
diff --git a/session.c b/session.c
index 4d7ac9d..b645144 100644
--- a/session.c
+++ b/session.c
@@ -665,6 +665,18 @@
debug("Forced command '%.900s'", command);
}
+#ifdef AUDIT_EVENTS
+ if (command != NULL)
+ PRIVSEP(audit_run_command(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));
+ }
+#endif
+
#ifdef GSSAPI
if (options.gss_authentication) {
temporarily_use_uid(s->pw);
@@ -2321,6 +2333,10 @@
}
#endif
+#ifdef AUDIT_EVENTS
+ PRIVSEP(audit_event(CONNECTION_CLOSE));
+#endif
+
/* remove agent socket */
auth_sock_cleanup_proc(authctxt->pw);
diff --git a/sshd.c b/sshd.c
index 23d6962..e61870e 100644
--- a/sshd.c
+++ b/sshd.c
@@ -1628,6 +1628,9 @@
remote_port = get_remote_port();
remote_ip = get_remote_ipaddr();
+#ifdef AUDIT_EVENTS
+ audit_connection_from(remote_ip, remote_port);
+#endif
#ifdef LIBWRAP
/* Check whether logins are denied from this host. */
if (packet_connection_is_on_socket()) {
@@ -1697,6 +1700,10 @@
}
authenticated:
+#ifdef AUDIT_EVENTS
+ audit_event(AUTH_SUCCESS);
+#endif
+
/*
* In privilege separation, we fork another child and prepare
* file descriptor passing.
@@ -2010,5 +2017,10 @@
{
if (the_authctxt)
do_cleanup(the_authctxt);
+#ifdef AUDIT_EVENTS
+ /* done after do_cleanup so it can cancel the PAM auth 'thread' */
+ if (!use_privsep || mm_is_monitor())
+ audit_event(CONNECTION_ABANDON);
+#endif
_exit(i);
}