# 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.5p1/audit-bsm.c b/openssh-6.5p1/audit-bsm.c --- a/openssh-6.5p1/audit-bsm.c +++ b/openssh-6.5p1/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.5p1/audit-linux.c b/openssh-6.5p1/audit-linux.c --- a/openssh-6.5p1/audit-linux.c +++ b/openssh-6.5p1/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.5p1/audit.c b/openssh-6.5p1/audit.c --- a/openssh-6.5p1/audit.c +++ b/openssh-6.5p1/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.5p1/audit.h b/openssh-6.5p1/audit.h --- a/openssh-6.5p1/audit.h +++ b/openssh-6.5p1/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.5p1/auth-rsa.c b/openssh-6.5p1/auth-rsa.c --- a/openssh-6.5p1/auth-rsa.c +++ b/openssh-6.5p1/auth-rsa.c @@ -87,17 +87,20 @@ auth_rsa_generate_challenge(Key *key) return challenge; } int auth_rsa_verify_response(Key *key, BIGNUM *challenge, u_char response[16]) { u_char buf[32], mdbuf[16]; MD5_CTX md; - int len; + int len, rv; +#ifdef SSH_AUDIT_EVENTS + char *fp; +#endif /* don't allow short keys */ if (BN_num_bits(key->rsa->n) < SSH_RSA_MINIMUM_MODULUS_SIZE) { error("auth_rsa_verify_response: RSA modulus too small: %d < minimum %d bits", BN_num_bits(key->rsa->n), SSH_RSA_MINIMUM_MODULUS_SIZE); return (0); } @@ -108,22 +111,28 @@ auth_rsa_verify_response(Key *key, BIGNU memset(buf, 0, 32); BN_bn2bin(challenge, buf + 32 - len); MD5_Init(&md); MD5_Update(&md, buf, 32); MD5_Update(&md, session_id, 16); MD5_Final(mdbuf, &md); /* Verify that the response is the original challenge. */ - if (timingsafe_bcmp(response, mdbuf, 16) != 0) { - /* Wrong answer. */ - return (0); + rv = timingsafe_bcmp(response, mdbuf, 16) == 0; + +#ifdef SSH_AUDIT_EVENTS + fp = key_fingerprint(key, key_fp_type_select(), SSH_FP_HEX); + if (audit_keyusage(1, "ssh-rsa1", RSA_size(key->rsa) * 8, fp, rv) == 0) { + debug("unsuccessful audit"); + rv = 0; } - /* Correct answer. */ - return (1); + free(fp); +#endif + + return rv; } /* * Performs the RSA authentication challenge-response dialog with the client, * and returns true (non-zero) if the client gave the correct answer to * our challenge; returns zero if the client gives a wrong answer. */ diff --git a/openssh-6.5p1/auth.h b/openssh-6.5p1/auth.h --- a/openssh-6.5p1/auth.h +++ b/openssh-6.5p1/auth.h @@ -182,16 +182,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 *); @@ -199,16 +200,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.5p1/auth2-hostbased.c b/openssh-6.5p1/auth2-hostbased.c --- a/openssh-6.5p1/auth2-hostbased.c +++ b/openssh-6.5p1/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.5p1/auth2-pubkey.c b/openssh-6.5p1/auth2-pubkey.c --- a/openssh-6.5p1/auth2-pubkey.c +++ b/openssh-6.5p1/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.5p1/monitor.c b/openssh-6.5p1/monitor.c --- a/openssh-6.5p1/monitor.c +++ b/openssh-6.5p1/monitor.c @@ -1362,26 +1362,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); @@ -1392,17 +1396,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.5p1/monitor_wrap.c b/openssh-6.5p1/monitor_wrap.c --- a/openssh-6.5p1/monitor_wrap.c +++ b/openssh-6.5p1/monitor_wrap.c @@ -428,30 +428,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__); @@ -459,16 +460,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.5p1/monitor_wrap.h b/openssh-6.5p1/monitor_wrap.h --- a/openssh-6.5p1/monitor_wrap.h +++ b/openssh-6.5p1/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 *);