diff -up openssh-5.9p1/audit-bsm.c.audit5 openssh-5.9p1/audit-bsm.c --- openssh-5.9p1/audit-bsm.c.audit5 2011-09-13 22:07:31.262575526 +0200 +++ openssh-5.9p1/audit-bsm.c 2011-09-13 22:07:33.268491813 +0200 @@ -414,4 +414,22 @@ audit_session_key_free_body(int ctos, pi { /* not implemented */ } + +void +audit_destroy_sensitive_data(const char *fp) +{ + /* not implemented */ +} + +void +audit_destroy_sensitive_data(const char *fp, pid_t pid, uid_t uid) +{ + /* not implemented */ +} + +void +audit_generate_ephemeral_server_key(const char *fp) +{ + /* not implemented */ +} #endif /* BSM */ diff -up openssh-5.9p1/audit-linux.c.audit5 openssh-5.9p1/audit-linux.c --- openssh-5.9p1/audit-linux.c.audit5 2011-09-13 22:07:31.400584308 +0200 +++ openssh-5.9p1/audit-linux.c 2011-09-13 22:07:33.357460348 +0200 @@ -350,4 +350,50 @@ audit_session_key_free_body(int ctos, pi error("cannot write into audit"); } +void +audit_destroy_sensitive_data(const char *fp, pid_t pid, uid_t uid) +{ + char buf[AUDIT_LOG_SIZE]; + int audit_fd, audit_ok; + + snprintf(buf, sizeof(buf), "op=destroy kind=server fp=%s direction=? spid=%jd suid=%jd ", + fp, (intmax_t)pid, (intmax_t)uid); + audit_fd = audit_open(); + if (audit_fd < 0) { + if (errno != EINVAL && errno != EPROTONOSUPPORT && + errno != EAFNOSUPPORT) + error("cannot open audit"); + return; + } + audit_ok = audit_log_user_message(audit_fd, AUDIT_CRYPTO_KEY_USER, + buf, NULL, + listening_for_clients() ? NULL : get_remote_ipaddr(), + NULL, 1); + audit_close(audit_fd); + /* do not abort if the error is EPERM and sshd is run as non root user */ + if ((audit_ok < 0) && ((audit_ok != -1) || (getuid() == 0))) + error("cannot write into audit"); +} + +void +audit_generate_ephemeral_server_key(const char *fp) +{ + char buf[AUDIT_LOG_SIZE]; + int audit_fd, audit_ok; + + snprintf(buf, sizeof(buf), "op=create kind=server fp=%s direction=? ", fp); + audit_fd = audit_open(); + if (audit_fd < 0) { + if (errno != EINVAL && errno != EPROTONOSUPPORT && + errno != EAFNOSUPPORT) + error("cannot open audit"); + return; + } + audit_ok = audit_log_user_message(audit_fd, AUDIT_CRYPTO_KEY_USER, + buf, NULL, 0, NULL, 1); + audit_close(audit_fd); + /* do not abort if the error is EPERM and sshd is run as non root user */ + if ((audit_ok < 0) && ((audit_ok != -1) || (getuid() == 0))) + error("cannot write into audit"); +} #endif /* USE_LINUX_AUDIT */ diff -up openssh-5.9p1/audit.c.audit5 openssh-5.9p1/audit.c --- openssh-5.9p1/audit.c.audit5 2011-09-13 22:07:31.495458797 +0200 +++ openssh-5.9p1/audit.c 2011-09-13 22:07:33.478458341 +0200 @@ -290,5 +290,24 @@ audit_session_key_free_body(int ctos, pi debug("audit session key discard euid %u direction %d from pid %ld uid %u", (unsigned)geteuid(), ctos, (long)pid, (unsigned)uid); } + +/* + * This will be called on destroy private part of the server key + */ +void +audit_destroy_sensitive_data(const char *fp, pid_t pid, uid_t uid) +{ + debug("audit destroy sensitive data euid %d fingerprint %s from pid %ld uid %u", + geteuid(), fp, (long)pid, (unsigned)uid); +} + +/* + * This will be called on generation of the ephemeral server key + */ +void +audit_generate_ephemeral_server_key(const char *) +{ + debug("audit create ephemeral server key euid %d fingerprint %s", geteuid(), fp); +} # endif /* !defined CUSTOM_SSH_AUDIT_EVENTS */ #endif /* SSH_AUDIT_EVENTS */ diff -up openssh-5.9p1/audit.h.audit5 openssh-5.9p1/audit.h --- openssh-5.9p1/audit.h.audit5 2011-09-13 22:07:31.616459125 +0200 +++ openssh-5.9p1/audit.h 2011-09-13 22:07:33.612458074 +0200 @@ -48,6 +48,8 @@ enum ssh_audit_event_type { }; typedef enum ssh_audit_event_type ssh_audit_event_t; +int listening_for_clients(void); + void audit_connection_from(const char *, int); void audit_event(ssh_audit_event_t); void audit_count_session_open(void); @@ -64,5 +66,7 @@ void audit_unsupported_body(int); void audit_kex_body(int, char *, char *, char *, pid_t, uid_t); void audit_session_key_free(int ctos); void audit_session_key_free_body(int ctos, pid_t, uid_t); +void audit_destroy_sensitive_data(const char *, pid_t, uid_t); +void audit_generate_ephemeral_server_key(const char *); #endif /* _SSH_AUDIT_H */ diff -up openssh-5.9p1/key.c.audit5 openssh-5.9p1/key.c --- openssh-5.9p1/key.c.audit5 2011-09-13 22:07:23.054490740 +0200 +++ openssh-5.9p1/key.c 2011-09-13 22:07:33.721583661 +0200 @@ -1799,6 +1799,30 @@ key_demote(const Key *k) } int +key_is_private(const Key *k) +{ + switch (k->type) { + case KEY_RSA_CERT_V00: + case KEY_RSA_CERT: + case KEY_RSA1: + case KEY_RSA: + return k->rsa->d != NULL; + case KEY_DSA_CERT_V00: + case KEY_DSA_CERT: + case KEY_DSA: + return k->dsa->priv_key != NULL; +#ifdef OPENSSL_HAS_ECC + case KEY_ECDSA_CERT: + case KEY_ECDSA: + return EC_KEY_get0_private_key(k->ecdsa) != NULL; +#endif + default: + fatal("key_is_private: bad key type %d", k->type); + return 1; + } +} + +int key_is_cert(const Key *k) { if (k == NULL) diff -up openssh-5.9p1/key.h.audit5 openssh-5.9p1/key.h --- openssh-5.9p1/key.h.audit5 2011-09-13 22:07:23.160459285 +0200 +++ openssh-5.9p1/key.h 2011-09-13 22:07:33.847459341 +0200 @@ -109,6 +109,7 @@ Key *key_generate(int, u_int); Key *key_from_private(const Key *); int key_type_from_name(char *); int key_is_cert(const Key *); +int key_is_private(const Key *k); int key_type_plain(int); int key_to_certified(Key *, int); int key_drop_cert(Key *); diff -up openssh-5.9p1/monitor.c.audit5 openssh-5.9p1/monitor.c --- openssh-5.9p1/monitor.c.audit5 2011-09-13 22:07:32.285495537 +0200 +++ openssh-5.9p1/monitor.c 2011-09-13 22:10:04.148554239 +0200 @@ -114,6 +114,8 @@ extern Buffer auth_debug; extern int auth_debug_init; extern Buffer loginmsg; +extern void destroy_sensitive_data(int); + /* State exported from the child */ struct { @@ -191,6 +193,7 @@ int mm_answer_audit_end_command(int, Buf int mm_answer_audit_unsupported_body(int, Buffer *); int mm_answer_audit_kex_body(int, Buffer *); int mm_answer_audit_session_key_free_body(int, Buffer *); +int mm_answer_audit_server_key_free(int, Buffer *); #endif static int monitor_read_log(struct monitor *); @@ -244,6 +247,7 @@ struct mon_table mon_dispatch_proto20[] {MONITOR_REQ_AUDIT_UNSUPPORTED, MON_PERMIT, mm_answer_audit_unsupported_body}, {MONITOR_REQ_AUDIT_KEX, MON_PERMIT, mm_answer_audit_kex_body}, {MONITOR_REQ_AUDIT_SESSION_KEY_FREE, MON_PERMIT, mm_answer_audit_session_key_free_body}, + {MONITOR_REQ_AUDIT_SERVER_KEY_FREE, MON_PERMIT, mm_answer_audit_server_key_free}, #endif #ifdef BSD_AUTH {MONITOR_REQ_BSDAUTHQUERY, MON_ISAUTH, mm_answer_bsdauthquery}, @@ -285,6 +289,7 @@ struct mon_table mon_dispatch_postauth20 {MONITOR_REQ_AUDIT_UNSUPPORTED, MON_PERMIT, mm_answer_audit_unsupported_body}, {MONITOR_REQ_AUDIT_KEX, MON_PERMIT, mm_answer_audit_kex_body}, {MONITOR_REQ_AUDIT_SESSION_KEY_FREE, MON_PERMIT, mm_answer_audit_session_key_free_body}, + {MONITOR_REQ_AUDIT_SERVER_KEY_FREE, MON_PERMIT, mm_answer_audit_server_key_free}, #endif {0, 0, NULL} }; @@ -319,6 +324,7 @@ struct mon_table mon_dispatch_proto15[] {MONITOR_REQ_AUDIT_UNSUPPORTED, MON_PERMIT, mm_answer_audit_unsupported_body}, {MONITOR_REQ_AUDIT_KEX, MON_PERMIT, mm_answer_audit_kex_body}, {MONITOR_REQ_AUDIT_SESSION_KEY_FREE, MON_PERMIT, mm_answer_audit_session_key_free_body}, + {MONITOR_REQ_AUDIT_SERVER_KEY_FREE, MON_PERMIT, mm_answer_audit_server_key_free}, #endif {0, 0, NULL} }; @@ -334,6 +340,7 @@ struct mon_table mon_dispatch_postauth15 {MONITOR_REQ_AUDIT_UNSUPPORTED, MON_PERMIT, mm_answer_audit_unsupported_body}, {MONITOR_REQ_AUDIT_KEX, MON_PERMIT, mm_answer_audit_kex_body}, {MONITOR_REQ_AUDIT_SESSION_KEY_FREE, MON_PERMIT, mm_answer_audit_session_key_free_body}, + {MONITOR_REQ_AUDIT_SERVER_KEY_FREE, MON_PERMIT, mm_answer_audit_server_key_free}, #endif {0, 0, NULL} }; @@ -1716,6 +1723,8 @@ mm_answer_term(int sock, Buffer *req) sshpam_cleanup(); #endif + destroy_sensitive_data(0); + while (waitpid(pmonitor->m_pid, &status, 0) == -1) if (errno != EINTR) exit(1); @@ -2470,4 +2479,25 @@ mm_answer_audit_session_key_free_body(in mm_request_send(sock, MONITOR_ANS_AUDIT_SESSION_KEY_FREE, m); return 0; } + +int +mm_answer_audit_server_key_free(int sock, Buffer *m) +{ + int len; + char *fp; + pid_t pid; + uid_t uid; + + fp = buffer_get_string(m, &len); + pid = buffer_get_int64(m); + uid = buffer_get_int64(m); + + audit_destroy_sensitive_data(fp, pid, uid); + + xfree(fp); + buffer_clear(m); + + mm_request_send(sock, MONITOR_ANS_AUDIT_SERVER_KEY_FREE, m); + return 0; +} #endif /* SSH_AUDIT_EVENTS */ diff -up openssh-5.9p1/monitor.h.audit5 openssh-5.9p1/monitor.h --- openssh-5.9p1/monitor.h.audit5 2011-09-13 22:07:32.385522626 +0200 +++ openssh-5.9p1/monitor.h 2011-09-13 22:07:34.098459356 +0200 @@ -64,6 +64,7 @@ enum monitor_reqtype { MONITOR_REQ_AUDIT_UNSUPPORTED, MONITOR_ANS_AUDIT_UNSUPPORTED, MONITOR_REQ_AUDIT_KEX, MONITOR_ANS_AUDIT_KEX, MONITOR_REQ_AUDIT_SESSION_KEY_FREE, MONITOR_ANS_AUDIT_SESSION_KEY_FREE, + MONITOR_REQ_AUDIT_SERVER_KEY_FREE, MONITOR_ANS_AUDIT_SERVER_KEY_FREE, MONITOR_REQ_TERM, MONITOR_REQ_JPAKE_STEP1, MONITOR_ANS_JPAKE_STEP1, MONITOR_REQ_JPAKE_GET_PWDATA, MONITOR_ANS_JPAKE_GET_PWDATA, diff -up openssh-5.9p1/monitor_wrap.c.audit5 openssh-5.9p1/monitor_wrap.c --- openssh-5.9p1/monitor_wrap.c.audit5 2011-09-13 22:07:32.510521163 +0200 +++ openssh-5.9p1/monitor_wrap.c 2011-09-13 22:07:34.610458275 +0200 @@ -1559,4 +1559,20 @@ mm_audit_session_key_free_body(int ctos, &m); buffer_free(&m); } + +void +mm_audit_destroy_sensitive_data(const char *fp, pid_t pid, uid_t uid) +{ + Buffer m; + + buffer_init(&m); + buffer_put_cstring(&m, fp); + buffer_put_int64(&m, pid); + buffer_put_int64(&m, uid); + + mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_AUDIT_SERVER_KEY_FREE, &m); + mm_request_receive_expect(pmonitor->m_recvfd, MONITOR_ANS_AUDIT_SERVER_KEY_FREE, + &m); + buffer_free(&m); +} #endif /* SSH_AUDIT_EVENTS */ diff -up openssh-5.9p1/monitor_wrap.h.audit5 openssh-5.9p1/monitor_wrap.h --- openssh-5.9p1/monitor_wrap.h.audit5 2011-09-13 22:07:32.607520810 +0200 +++ openssh-5.9p1/monitor_wrap.h 2011-09-13 22:07:34.716458214 +0200 @@ -81,6 +81,7 @@ void mm_audit_end_command(int, const cha void mm_audit_unsupported_body(int); void mm_audit_kex_body(int, char *, char *, char *, pid_t, uid_t); void mm_audit_session_key_free_body(int, pid_t, uid_t); +void mm_audit_destroy_sensitive_data(const char *, pid_t, uid_t); #endif struct Session; diff -up openssh-5.9p1/session.c.audit5 openssh-5.9p1/session.c --- openssh-5.9p1/session.c.audit5 2011-09-13 22:07:32.973544819 +0200 +++ openssh-5.9p1/session.c 2011-09-13 22:07:34.849585578 +0200 @@ -136,7 +136,7 @@ extern int log_stderr; extern int debug_flag; extern u_int utmp_len; extern int startup_pipe; -extern void destroy_sensitive_data(void); +extern void destroy_sensitive_data(int); extern Buffer loginmsg; /* original command from peer. */ @@ -1633,7 +1633,7 @@ do_child(Session *s, const char *command int r = 0; /* remove hostkey from the child's memory */ - destroy_sensitive_data(); + destroy_sensitive_data(1); /* Don't audit this - both us and the parent would be talking to the monitor over a single socket, with no synchronization. */ packet_destroy_all(0, 1); diff -up openssh-5.9p1/sshd.c.audit5 openssh-5.9p1/sshd.c --- openssh-5.9p1/sshd.c.audit5 2011-09-13 22:07:33.106516378 +0200 +++ openssh-5.9p1/sshd.c 2011-09-13 22:07:34.989470331 +0200 @@ -254,7 +254,7 @@ Buffer loginmsg; struct passwd *privsep_pw = NULL; /* Prototypes for various functions defined later in this file. */ -void destroy_sensitive_data(void); +void destroy_sensitive_data(int); void demote_sensitive_data(void); static void do_ssh1_kex(void); @@ -273,6 +273,15 @@ close_listen_socks(void) num_listen_socks = -1; } +/* + * Is this process listening for clients (i.e. not specific to any specific + * client connection?) + */ +int listening_for_clients(void) +{ + return num_listen_socks > 0; +} + static void close_startup_pipes(void) { @@ -533,22 +542,47 @@ sshd_exchange_identification(int sock_in } } -/* Destroy the host and server keys. They will no longer be needed. */ +/* + * Destroy the host and server keys. They will no longer be needed. Careful, + * this can be called from cleanup_exit() - i.e. from just about anywhere. + */ void -destroy_sensitive_data(void) +destroy_sensitive_data(int privsep) { int i; + pid_t pid; + uid_t uid; if (sensitive_data.server_key) { key_free(sensitive_data.server_key); sensitive_data.server_key = NULL; } + pid = getpid(); + uid = getuid(); for (i = 0; i < options.num_host_key_files; i++) { if (sensitive_data.host_keys[i]) { + char *fp; + + if (key_is_private(sensitive_data.host_keys[i])) + fp = key_fingerprint(sensitive_data.host_keys[i], + FIPS_mode() ? SSH_FP_SHA1 : SSH_FP_MD5, + SSH_FP_HEX); + else + fp = NULL; key_free(sensitive_data.host_keys[i]); sensitive_data.host_keys[i] = NULL; + if (fp != NULL) { + if (privsep) + PRIVSEP(audit_destroy_sensitive_data(fp, + pid, uid)); + else + audit_destroy_sensitive_data(fp, + pid, uid); + xfree(fp); + } } - if (sensitive_data.host_certificates[i]) { + if (sensitive_data.host_certificates + && sensitive_data.host_certificates[i]) { key_free(sensitive_data.host_certificates[i]); sensitive_data.host_certificates[i] = NULL; } @@ -562,6 +596,8 @@ void demote_sensitive_data(void) { Key *tmp; + pid_t pid; + uid_t uid; int i; if (sensitive_data.server_key) { @@ -570,13 +606,27 @@ demote_sensitive_data(void) sensitive_data.server_key = tmp; } + pid = getpid(); + uid = getuid(); for (i = 0; i < options.num_host_key_files; i++) { if (sensitive_data.host_keys[i]) { + char *fp; + + if (key_is_private(sensitive_data.host_keys[i])) + fp = key_fingerprint(sensitive_data.host_keys[i], + FIPS_mode() ? SSH_FP_SHA1 : SSH_FP_MD5, + SSH_FP_HEX); + else + fp = NULL; tmp = key_demote(sensitive_data.host_keys[i]); key_free(sensitive_data.host_keys[i]); sensitive_data.host_keys[i] = tmp; if (tmp->type == KEY_RSA1) sensitive_data.ssh1_host_key = tmp; + if (fp != NULL) { + audit_destroy_sensitive_data(fp, pid, uid); + xfree(fp); + } } /* Certs do not need demotion */ } @@ -1145,6 +1195,7 @@ server_accept_loop(int *sock_in, int *so if (received_sigterm) { logit("Received signal %d; terminating.", (int) received_sigterm); + destroy_sensitive_data(0); close_listen_socks(); unlink(options.pid_file); exit(received_sigterm == SIGTERM ? 0 : 255); @@ -2050,7 +2101,7 @@ main(int ac, char **av) privsep_postauth(authctxt); /* the monitor process [priv] will not return */ if (!compat20) - destroy_sensitive_data(); + destroy_sensitive_data(0); } packet_set_timeout(options.client_alive_interval, @@ -2061,6 +2112,7 @@ main(int ac, char **av) /* The connection has been terminated. */ packet_destroy_all(1, 1); + destroy_sensitive_data(1); packet_get_state(MODE_IN, NULL, NULL, NULL, &ibytes); packet_get_state(MODE_OUT, NULL, NULL, NULL, &obytes); @@ -2289,7 +2341,7 @@ do_ssh1_kex(void) session_id[i] = session_key[i] ^ session_key[i + 16]; } /* Destroy the private and public keys. No longer. */ - destroy_sensitive_data(); + destroy_sensitive_data(0); if (use_privsep) mm_ssh1_session_id(session_id); @@ -2392,6 +2444,8 @@ cleanup_exit(int i) if (the_authctxt) do_cleanup(the_authctxt); is_privsep_child = use_privsep && pmonitor != NULL && !mm_is_monitor(); + if (sensitive_data.host_keys != NULL) + destroy_sensitive_data(is_privsep_child); packet_destroy_all(1, is_privsep_child); #ifdef SSH_AUDIT_EVENTS /* done after do_cleanup so it can cancel the PAM auth 'thread' */