# HG changeset patch
# Parent  aa82f917ddfa41058ec297beb89e16297dda6f4f
# auditing key-based authentication (both server and client)
# based on:
#   https://bugzilla.mindrot.org/show_bug.cgi?id=1402
#   https://bugzilla.mindrot.org/attachment.cgi?id=2012
#   (replaces: https://bugzilla.mindrot.org/attachment.cgi?id=1975)
#   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
@@ -401,16 +401,22 @@ audit_session_open(struct logininfo *li)
 }
 
 void
 audit_session_close(struct logininfo *li)
 {
 	/* not implemented */
 }
 
+int
+audit_keyusage(int host_user, const char *type, unsigned bits, char *fp, int rv)
+{
+	/* not implemented */
+}
+
 void
 audit_event(ssh_audit_event_t event)
 {
 	char    textbuf[BSM_TEXTBUFSZ];
 	static int logged_in = 0;
 	const char *user = the_authctxt ? the_authctxt->user : "(unknown user)";
 
 	if (cannot_audit(0))
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
@@ -36,16 +36,18 @@
 #include "log.h"
 #include "audit.h"
 #include "key.h"
 #include "hostfile.h"
 #include "auth.h"
 #include "servconf.h"
 #include "canohost.h"
 
+#define AUDIT_LOG_SIZE 128
+
 extern ServerOptions options;
 extern Authctxt *the_authctxt;
 extern u_int utmp_len;
 const char* audit_username(void);
 
 static void
 linux_audit_user_logxxx(int uid, const char *username,
     const char *hostname, const char *ip, const char *ttyn, int success, int event)
@@ -125,16 +127,47 @@ linux_audit_user_auth(int uid, const cha
 		rc = 0;
 	errno = saved_errno;
 	if (rc < 0) {
 fatal_report:
 		fatal("linux_audit_write_entry failed: %s", strerror(errno));
 	}
 }
 
+int
+audit_keyusage(int host_user, const char *type, unsigned bits, char *fp, int rv)
+{
+	char buf[AUDIT_LOG_SIZE];
+	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 */
+		else                                                                                                                                       
+			return 0; /* Must prevent login */
+	}
+	snprintf(buf, sizeof(buf), "%s_auth rport=%d", host_user ? "pubkey" : "hostbased", get_remote_port());
+	rc = audit_log_acct_message(audit_fd, AUDIT_USER_AUTH, NULL,
+		buf, audit_username(), -1, NULL, get_remote_ipaddr(), NULL, rv);
+	if ((rc < 0) && ((rc != -1) || (getuid() == 0)))
+		goto out;
+	snprintf(buf, sizeof(buf), "key algo=%s size=%d fp=%s rport=%d",
+			type, bits, fp, get_remote_port());
+	rc = audit_log_acct_message(audit_fd, AUDIT_USER_AUTH, NULL,
+		buf, audit_username(), -1, NULL, get_remote_ipaddr(), NULL, rv);
+out:
+	saved_errno = errno;
+	audit_close(audit_fd);
+	errno = saved_errno;
+	/* do not report error if the error is EPERM and sshd is run as non root user */
+	return (rc >= 0) || ((rc == -EPERM) && (getuid() != 0));
+}
+
 static int user_login_count = 0;
 
 /* Below is the sshd audit API code */
 
 void
 audit_connection_from(const char *host, int port)
 {
 	/* not implemented */
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
@@ -31,16 +31,17 @@
 
 #ifdef SSH_AUDIT_EVENTS
 
 #include "audit.h"
 #include "log.h"
 #include "key.h"
 #include "hostfile.h"
 #include "auth.h"
+#include "xmalloc.h"
 
 /*
  * Care must be taken when using this since it WILL NOT be initialized when
  * audit_connection_from() is called and MAY NOT be initialized when
  * audit_event(CONNECTION_ABANDON) is called.  Test for NULL before using.
  */
 extern Authctxt *the_authctxt;
 
@@ -106,16 +107,32 @@ audit_event_lookup(ssh_audit_event_t ev)
 	};
 
 	for (i = 0; event_lookup[i].event != SSH_AUDIT_UNKNOWN; i++)
 		if (event_lookup[i].event == ev)
 			break;
 	return(event_lookup[i].name);
 }
 
+void
+audit_key(int host_user, int *rv, const Key *key)
+{
+	char *fp;
+	const char *crypto_name;
+
+	fp = key_fingerprint(key, key_fp_type_select(), SSH_FP_HEX);
+	if (key->type == KEY_RSA1)
+		crypto_name = "ssh-rsa1";
+	else
+		crypto_name = key_ssh_name(key);
+	if (audit_keyusage(host_user, crypto_name, key_size(key), fp, *rv) == 0)
+		*rv = 0;
+	free(fp);
+}
+
 # ifndef CUSTOM_SSH_AUDIT_EVENTS
 /*
  * Null implementations of audit functions.
  * These get used if SSH_AUDIT_EVENTS is defined but no audit module is enabled.
  */
 
 /*
  * Called after a connection has been accepted but before any authentication
@@ -204,10 +221,22 @@ audit_run_command(const char *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);
 }
 
+/*
+ * This will be called when user is successfully autherized by the RSA1/RSA/DSA key.
+ *
+ * Type is the key type, len is the key length(byte) and fp is the fingerprint of the key.
+ */
+int
+audit_keyusage(int host_user, const char *type, unsigned bits, char *fp, int rv)
+{
+	debug("audit %s key usage euid %d user %s key type %s key length %d fingerprint %s, result %d", 
+		host_user ? "pubkey" : "hostbased", geteuid(), audit_username(), type, bits,
+		fp, rv);
+}
 # 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
@@ -23,16 +23,17 @@
  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
 #ifndef _SSH_AUDIT_H
 # define _SSH_AUDIT_H
 
 #include "loginrec.h"
+#include "key.h"
 
 enum ssh_audit_event_type {
 	SSH_LOGIN_EXCEED_MAXTRIES,
 	SSH_LOGIN_ROOT_DENIED,
 	SSH_AUTH_SUCCESS,
 	SSH_AUTH_FAIL_NONE,
 	SSH_AUTH_FAIL_PASSWD,
 	SSH_AUTH_FAIL_KBDINT,	/* keyboard-interactive or challenge-response */
@@ -50,10 +51,12 @@ typedef enum ssh_audit_event_type ssh_au
 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 *);
 int	audit_run_command(const char *);
 void 	audit_end_command(int, const char *);
 ssh_audit_event_t audit_classify_auth(const char *);
+int	audit_keyusage(int, const char *, unsigned, char *, int);
+void	audit_key(int, int *, const Key *);
 
 #endif /* _SSH_AUDIT_H */
diff --git a/openssh-6.6p1/auth.h b/openssh-6.6p1/auth.h
--- a/openssh-6.6p1/auth.h
+++ b/openssh-6.6p1/auth.h
@@ -178,16 +178,17 @@ int	allowed_user(struct passwd *);
 struct passwd * getpwnamallow(const char *user);
 
 char	*get_challenge(Authctxt *);
 int	verify_response(Authctxt *, const char *);
 void	abandon_challenge_response(Authctxt *);
 
 char	*expand_authorized_keys(const char *, struct passwd *pw);
 char	*authorized_principals_file(struct passwd *);
+int	 user_key_verify(const Key *, const u_char *, u_int, const u_char *, u_int);
 
 FILE	*auth_openkeyfile(const char *, struct passwd *, int);
 FILE	*auth_openprincipals(const char *, struct passwd *, int);
 int	 auth_key_is_revoked(Key *);
 
 HostStatus
 check_key_in_hostfiles(struct passwd *, Key *, const char *,
     const char *, const char *);
@@ -195,16 +196,17 @@ check_key_in_hostfiles(struct passwd *, 
 /* hostkey handling */
 Key	*get_hostkey_by_index(int);
 Key	*get_hostkey_public_by_index(int);
 Key	*get_hostkey_public_by_type(int);
 Key	*get_hostkey_private_by_type(int);
 int	 get_hostkey_index(Key *);
 int	 ssh1_session_key(BIGNUM *);
 void	 sshd_hostkey_sign(Key *, Key *, u_char **, u_int *, u_char *, u_int);
+int	 hostbased_key_verify(const Key *, const u_char *, u_int, const u_char *, u_int);
 
 /* debug messages during authentication */
 void	 auth_debug_add(const char *fmt,...) __attribute__((format(printf, 1, 2)));
 void	 auth_debug_send(void);
 void	 auth_debug_reset(void);
 
 struct passwd *fakepw(void);
 
diff --git a/openssh-6.6p1/auth2-hostbased.c b/openssh-6.6p1/auth2-hostbased.c
--- a/openssh-6.6p1/auth2-hostbased.c
+++ b/openssh-6.6p1/auth2-hostbased.c
@@ -124,33 +124,45 @@ userauth_hostbased(Authctxt *authctxt)
 #endif
 
 	pubkey_auth_info(authctxt, key,
 	    "client user \"%.100s\", client host \"%.100s\"", cuser, chost);
 
 	/* test for allowed key and correct signature */
 	authenticated = 0;
 	if (PRIVSEP(hostbased_key_allowed(authctxt->pw, cuser, chost, key)) &&
-	    PRIVSEP(key_verify(key, sig, slen, buffer_ptr(&b),
+	    PRIVSEP(hostbased_key_verify(key, sig, slen, buffer_ptr(&b),
 			buffer_len(&b))) == 1)
 		authenticated = 1;
 
 	buffer_free(&b);
 done:
 	debug2("userauth_hostbased: authenticated %d", authenticated);
 	if (key != NULL)
 		key_free(key);
 	free(pkalg);
 	free(pkblob);
 	free(cuser);
 	free(chost);
 	free(sig);
 	return authenticated;
 }
 
+int
+hostbased_key_verify(const Key *key, const u_char *sig, u_int slen, const u_char *data, u_int datalen)
+{
+	int rv;
+
+	rv = key_verify(key, sig, slen, data, datalen);
+#ifdef SSH_AUDIT_EVENTS
+	audit_key(0, &rv, key);
+#endif
+	return rv;
+}
+
 /* return 1 if given hostkey is allowed */
 int
 hostbased_key_allowed(struct passwd *pw, const char *cuser, char *chost,
     Key *key)
 {
 	const char *resolvedname, *ipaddr, *lookup, *reason;
 	HostStatus host_status;
 	int len;
diff --git a/openssh-6.6p1/auth2-pubkey.c b/openssh-6.6p1/auth2-pubkey.c
--- a/openssh-6.6p1/auth2-pubkey.c
+++ b/openssh-6.6p1/auth2-pubkey.c
@@ -153,17 +153,17 @@ userauth_pubkey(Authctxt *authctxt)
 #ifdef DEBUG_PK
 		buffer_dump(&b);
 #endif
 		pubkey_auth_info(authctxt, key, NULL);
 
 		/* test for correct signature */
 		authenticated = 0;
 		if (PRIVSEP(user_key_allowed(authctxt->pw, key)) &&
-		    PRIVSEP(key_verify(key, sig, slen, buffer_ptr(&b),
+		    PRIVSEP(user_key_verify(key, sig, slen, buffer_ptr(&b),
 		    buffer_len(&b))) == 1)
 			authenticated = 1;
 		buffer_free(&b);
 		free(sig);
 	} else {
 		debug("test whether pkalg/pkblob are acceptable");
 		packet_check_eom();
 
@@ -190,16 +190,28 @@ done:
 	debug2("userauth_pubkey: authenticated %d pkalg %s", authenticated, pkalg);
 	if (key != NULL)
 		key_free(key);
 	free(pkalg);
 	free(pkblob);
 	return authenticated;
 }
 
+int
+user_key_verify(const Key *key, const u_char *sig, u_int slen, const u_char *data, u_int datalen)
+{
+	int rv;
+
+	rv = key_verify(key, sig, slen, data, datalen);
+#ifdef SSH_AUDIT_EVENTS
+	audit_key(1, &rv, key);
+#endif
+	return rv;
+}
+
 void
 pubkey_auth_info(Authctxt *authctxt, const Key *key, const char *fmt, ...)
 {
 	char *fp, *extra;
 	va_list ap;
 	int i;
 
 	extra = NULL;
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
@@ -1340,26 +1340,30 @@ monitor_valid_hostbasedblob(u_char *data
 }
 
 int
 mm_answer_keyverify(int sock, Buffer *m)
 {
 	Key *key;
 	u_char *signature, *data, *blob;
 	u_int signaturelen, datalen, bloblen;
+	int type = 0;
 	int verified = 0;
 	int valid_data = 0;
 
+	type = buffer_get_int(m);
 	blob = buffer_get_string(m, &bloblen);
 	signature = buffer_get_string(m, &signaturelen);
 	data = buffer_get_string(m, &datalen);
 
 	if (hostbased_cuser == NULL || hostbased_chost == NULL ||
 	  !monitor_allowed_key(blob, bloblen))
 		fatal("%s: bad key, not previously allowed", __func__);
+	if (type != key_blobtype)
+		fatal("%s: bad key type", __func__);
 
 	key = key_from_blob(blob, bloblen);
 	if (key == NULL)
 		fatal("%s: bad public key blob", __func__);
 
 	switch (key_blobtype) {
 	case MM_USERKEY:
 		valid_data = monitor_valid_userblob(data, datalen);
@@ -1370,17 +1374,27 @@ mm_answer_keyverify(int sock, Buffer *m)
 		break;
 	default:
 		valid_data = 0;
 		break;
 	}
 	if (!valid_data)
 		fatal("%s: bad signature data blob", __func__);
 
-	verified = key_verify(key, signature, signaturelen, data, datalen);
+	switch (key_blobtype) {
+	case MM_USERKEY:
+		verified = user_key_verify(key, signature, signaturelen, data, datalen);
+		break;
+	case MM_HOSTKEY:
+		verified = hostbased_key_verify(key, signature, signaturelen, data, datalen);
+		break;
+	default:
+		verified = 0;
+		break;
+	}
 	debug3("%s: key %p signature %s",
 	    __func__, key, (verified == 1) ? "verified" : "unverified");
 
 	key_free(key);
 	free(blob);
 	free(signature);
 	free(data);
 
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
@@ -426,30 +426,31 @@ mm_key_allowed(enum mm_keytype type, cha
 
 /*
  * This key verify needs to send the key type along, because the
  * privileged parent makes the decision if the key is allowed
  * for authentication.
  */
 
 int
-mm_key_verify(Key *key, u_char *sig, u_int siglen, u_char *data, u_int datalen)
+mm_key_verify(enum mm_keytype type, Key *key, u_char *sig, u_int siglen, u_char *data, u_int datalen)
 {
 	Buffer m;
 	u_char *blob;
 	u_int len;
 	int verified = 0;
 
 	debug3("%s entering", __func__);
 
 	/* Convert the key to a blob and the pass it over */
 	if (!key_to_blob(key, &blob, &len))
 		return (0);
 
 	buffer_init(&m);
+	buffer_put_int(&m, type);
 	buffer_put_string(&m, blob, len);
 	buffer_put_string(&m, sig, siglen);
 	buffer_put_string(&m, data, datalen);
 	free(blob);
 
 	mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_KEYVERIFY, &m);
 
 	debug3("%s: waiting for MONITOR_ANS_KEYVERIFY", __func__);
@@ -457,16 +458,29 @@ mm_key_verify(Key *key, u_char *sig, u_i
 
 	verified = buffer_get_int(&m);
 
 	buffer_free(&m);
 
 	return (verified);
 }
 
+int
+mm_hostbased_key_verify(Key *key, u_char *sig, u_int siglen, u_char *data, u_int datalen)
+{
+	return mm_key_verify(MM_HOSTKEY, key, sig, siglen, data, datalen);
+}
+
+int
+mm_user_key_verify(Key *key, u_char *sig, u_int siglen, u_char *data, u_int datalen)
+{
+	return mm_key_verify(MM_USERKEY, key, sig, siglen, data, datalen);
+}
+
+
 /* Export key state after authentication */
 Newkeys *
 mm_newkeys_from_blob(u_char *blob, int blen)
 {
 	Buffer b;
 	u_int len;
 	Newkeys *newkey = NULL;
 	Enc *enc;
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
@@ -44,17 +44,18 @@ int mm_key_sign(Key *, u_char **, u_int 
 void mm_inform_authserv(char *, char *);
 struct passwd *mm_getpwnamallow(const char *);
 char *mm_auth2_read_banner(void);
 int mm_auth_password(struct Authctxt *, char *);
 int mm_key_allowed(enum mm_keytype, char *, char *, Key *);
 int mm_user_key_allowed(struct passwd *, Key *);
 int mm_hostbased_key_allowed(struct passwd *, char *, char *, Key *);
 int mm_auth_rhosts_rsa_key_allowed(struct passwd *, char *, char *, Key *);
-int mm_key_verify(Key *, u_char *, u_int, u_char *, u_int);
+int mm_hostbased_key_verify(Key *, u_char *, u_int, u_char *, u_int);
+int mm_user_key_verify(Key *, u_char *, u_int, u_char *, u_int);
 int mm_auth_rsa_key_allowed(struct passwd *, BIGNUM *, Key **);
 int mm_auth_rsa_verify_response(Key *, BIGNUM *, u_char *);
 BIGNUM *mm_auth_rsa_generate_challenge(Key *);
 
 #ifdef GSSAPI
 OM_uint32 mm_ssh_gssapi_server_ctx(Gssctxt **, gss_OID);
 OM_uint32 mm_ssh_gssapi_accept_ctx(Gssctxt *,
    gss_buffer_desc *, gss_buffer_desc *, OM_uint32 *);
