From: markus Date: Wed, 31 May 2017 08:09:45 +0000 (+0000) Subject: clear session keys from memory; ok djm@ X-Git-Url: http://artulab.com/gitweb/?a=commitdiff_plain;h=2208575303d5c268670d3b594ea64b9a7dc8251a;p=openbsd clear session keys from memory; ok djm@ --- diff --git a/usr.bin/ssh/monitor.c b/usr.bin/ssh/monitor.c index f327e3ed1e9..aa2a77d377b 100644 --- a/usr.bin/ssh/monitor.c +++ b/usr.bin/ssh/monitor.c @@ -1,4 +1,4 @@ -/* $OpenBSD: monitor.c,v 1.169 2017/05/30 14:10:53 markus Exp $ */ +/* $OpenBSD: monitor.c,v 1.170 2017/05/31 08:09:45 markus Exp $ */ /* * Copyright 2002 Niels Provos * Copyright 2002 Markus Friedl @@ -1245,6 +1245,17 @@ mm_answer_term(int sock, Buffer *req) exit(res); } +void +monitor_clear_keystate(struct monitor *pmonitor) +{ + struct ssh *ssh = active_state; /* XXX */ + + ssh_clear_newkeys(ssh, MODE_IN); + ssh_clear_newkeys(ssh, MODE_OUT); + sshbuf_free(child_state); + child_state = NULL; +} + void monitor_apply_keystate(struct monitor *pmonitor) { diff --git a/usr.bin/ssh/monitor_wrap.h b/usr.bin/ssh/monitor_wrap.h index 775f2cc1ec9..f484b3781ec 100644 --- a/usr.bin/ssh/monitor_wrap.h +++ b/usr.bin/ssh/monitor_wrap.h @@ -1,4 +1,4 @@ -/* $OpenBSD: monitor_wrap.h,v 1.34 2017/05/30 14:10:53 markus Exp $ */ +/* $OpenBSD: monitor_wrap.h,v 1.35 2017/05/31 08:09:45 markus Exp $ */ /* * Copyright 2002 Niels Provos @@ -34,7 +34,6 @@ extern int use_privsep; enum mm_keytype { MM_NOKEY, MM_HOSTKEY, MM_USERKEY }; struct monitor; -struct mm_master; struct Authctxt; void mm_log_handler(LogLevel, const char *, void *); @@ -71,6 +70,7 @@ void mm_session_pty_cleanup2(struct Session *); struct newkeys *mm_newkeys_from_blob(u_char *, int); int mm_newkeys_to_blob(int, u_char **, u_int *); +void monitor_clear_keystate(struct monitor *); void monitor_apply_keystate(struct monitor *); void mm_get_keystate(struct monitor *); void mm_send_keystate(struct monitor*); diff --git a/usr.bin/ssh/opacket.h b/usr.bin/ssh/opacket.h index 2241d8e1374..6a39c9b7059 100644 --- a/usr.bin/ssh/opacket.h +++ b/usr.bin/ssh/opacket.h @@ -148,5 +148,7 @@ void packet_read_expect(int expected_type); ssh_packet_set_mux(active_state) #define packet_get_mux() \ ssh_packet_get_mux(active_state) +#define packet_clear_keys() \ + ssh_packet_clear_keys(active_state) #endif /* _OPACKET_H */ diff --git a/usr.bin/ssh/packet.c b/usr.bin/ssh/packet.c index ca47f0986b0..08ab9d4759d 100644 --- a/usr.bin/ssh/packet.c +++ b/usr.bin/ssh/packet.c @@ -1,4 +1,4 @@ -/* $OpenBSD: packet.c,v 1.256 2017/05/08 06:03:39 djm Exp $ */ +/* $OpenBSD: packet.c,v 1.257 2017/05/31 08:09:45 markus Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland @@ -548,8 +548,8 @@ ssh_local_port(struct ssh *ssh) /* Closes the connection and clears and frees internal data structures. */ -void -ssh_packet_close(struct ssh *ssh) +static void +ssh_packet_close_internal(struct ssh *ssh, int do_close) { struct session_state *state = ssh->state; u_int mode; @@ -557,20 +557,26 @@ ssh_packet_close(struct ssh *ssh) if (!state->initialized) return; state->initialized = 0; - if (state->connection_in == state->connection_out) { - shutdown(state->connection_out, SHUT_RDWR); - close(state->connection_out); - } else { - close(state->connection_in); - close(state->connection_out); + if (do_close) { + if (state->connection_in == state->connection_out) { + shutdown(state->connection_out, SHUT_RDWR); + close(state->connection_out); + } else { + close(state->connection_in); + close(state->connection_out); + } } sshbuf_free(state->input); sshbuf_free(state->output); sshbuf_free(state->outgoing_packet); sshbuf_free(state->incoming_packet); - for (mode = 0; mode < MODE_MAX; mode++) - kex_free_newkeys(state->newkeys[mode]); - if (state->compression_buffer) { + for (mode = 0; mode < MODE_MAX; mode++) { + kex_free_newkeys(state->newkeys[mode]); /* current keys */ + state->newkeys[mode] = NULL; + ssh_clear_newkeys(ssh, mode); /* next keys */ + } + /* comression state is in shared mem, so we can only release it once */ + if (do_close && state->compression_buffer) { sshbuf_free(state->compression_buffer); if (state->compression_out_started) { z_streamp stream = &state->compression_out_stream; @@ -598,10 +604,24 @@ ssh_packet_close(struct ssh *ssh) cipher_free(state->send_context); cipher_free(state->receive_context); state->send_context = state->receive_context = NULL; - free(ssh->remote_ipaddr); - ssh->remote_ipaddr = NULL; - free(ssh->state); - ssh->state = NULL; + if (do_close) { + free(ssh->remote_ipaddr); + ssh->remote_ipaddr = NULL; + free(ssh->state); + ssh->state = NULL; + } +} + +void +ssh_packet_close(struct ssh *ssh) +{ + ssh_packet_close_internal(ssh, 1); +} + +void +ssh_packet_clear_keys(struct ssh *ssh) +{ + ssh_packet_close_internal(ssh, 0); } /* Sets remote side protocol flags. */ @@ -780,6 +800,15 @@ uncompress_buffer(struct ssh *ssh, struct sshbuf *in, struct sshbuf *out) /* NOTREACHED */ } +void +ssh_clear_newkeys(struct ssh *ssh, int mode) +{ + if (ssh->kex && ssh->kex->newkeys) { + kex_free_newkeys(ssh->kex->newkeys[mode]); + ssh->kex->newkeys[mode] = NULL; + } +} + int ssh_set_newkeys(struct ssh *ssh, int mode) { @@ -809,26 +838,16 @@ ssh_set_newkeys(struct ssh *ssh, int mode) max_blocks = &state->max_blocks_in; } if (state->newkeys[mode] != NULL) { - debug("%s: rekeying after %llu %s blocks" - " (%llu bytes total)", __func__, - (unsigned long long)ps->blocks, dir, - (unsigned long long)ps->bytes); + debug("set_newkeys: rekeying, input %llu bytes %llu blocks, " + "output %llu bytes %llu blocks", + (unsigned long long)state->p_read.bytes, + (unsigned long long)state->p_read.blocks, + (unsigned long long)state->p_send.bytes, + (unsigned long long)state->p_send.blocks); cipher_free(*ccp); *ccp = NULL; - enc = &state->newkeys[mode]->enc; - mac = &state->newkeys[mode]->mac; - comp = &state->newkeys[mode]->comp; - mac_clear(mac); - explicit_bzero(enc->iv, enc->iv_len); - explicit_bzero(enc->key, enc->key_len); - explicit_bzero(mac->key, mac->key_len); - free(enc->name); - free(enc->iv); - free(enc->key); - free(mac->name); - free(mac->key); - free(comp->name); - free(state->newkeys[mode]); + kex_free_newkeys(state->newkeys[mode]); + state->newkeys[mode] = NULL; } /* note that both bytes and the seqnr are not reset */ ps->packets = ps->blocks = 0; @@ -1772,15 +1791,20 @@ sshpkt_fatal(struct ssh *ssh, const char *tag, int r) switch (r) { case SSH_ERR_CONN_CLOSED: + ssh_packet_clear_keys(ssh); logdie("Connection closed by %s", remote_id); case SSH_ERR_CONN_TIMEOUT: + ssh_packet_clear_keys(ssh); logdie("Connection %s %s timed out", ssh->state->server_side ? "from" : "to", remote_id); case SSH_ERR_DISCONNECTED: + ssh_packet_clear_keys(ssh); logdie("Disconnected from %s", remote_id); case SSH_ERR_SYSTEM_ERROR: - if (errno == ECONNRESET) + if (errno == ECONNRESET) { + ssh_packet_clear_keys(ssh); logdie("Connection reset by %s", remote_id); + } /* FALLTHROUGH */ case SSH_ERR_NO_CIPHER_ALG_MATCH: case SSH_ERR_NO_MAC_ALG_MATCH: @@ -1788,12 +1812,14 @@ sshpkt_fatal(struct ssh *ssh, const char *tag, int r) case SSH_ERR_NO_KEX_ALG_MATCH: case SSH_ERR_NO_HOSTKEY_ALG_MATCH: if (ssh && ssh->kex && ssh->kex->failed_choice) { + ssh_packet_clear_keys(ssh); logdie("Unable to negotiate with %s: %s. " "Their offer: %s", remote_id, ssh_err(r), ssh->kex->failed_choice); } /* FALLTHROUGH */ default: + ssh_packet_clear_keys(ssh); logdie("%s%sConnection %s %s: %s", tag != NULL ? tag : "", tag != NULL ? ": " : "", ssh->state->server_side ? "from" : "to", diff --git a/usr.bin/ssh/packet.h b/usr.bin/ssh/packet.h index 978b3a3fb5b..65c01e902c4 100644 --- a/usr.bin/ssh/packet.h +++ b/usr.bin/ssh/packet.h @@ -1,4 +1,4 @@ -/* $OpenBSD: packet.h,v 1.80 2017/05/30 14:18:15 markus Exp $ */ +/* $OpenBSD: packet.h,v 1.81 2017/05/31 08:09:45 markus Exp $ */ /* * Author: Tatu Ylonen @@ -83,6 +83,8 @@ int ssh_packet_get_connection_in(struct ssh *); int ssh_packet_get_connection_out(struct ssh *); void ssh_packet_close(struct ssh *); void ssh_packet_set_input_hook(struct ssh *, ssh_packet_hook_fn *, void *); +void ssh_packet_clear_keys(struct ssh *); +void ssh_clear_newkeys(struct ssh *, int); int ssh_packet_is_rekeying(struct ssh *); void ssh_packet_set_protocol_flags(struct ssh *, u_int); diff --git a/usr.bin/ssh/session.c b/usr.bin/ssh/session.c index 61d2e42788a..175edb3b5da 100644 --- a/usr.bin/ssh/session.c +++ b/usr.bin/ssh/session.c @@ -1,4 +1,4 @@ -/* $OpenBSD: session.c,v 1.286 2016/11/30 03:00:05 djm Exp $ */ +/* $OpenBSD: session.c,v 1.287 2017/05/31 08:09:45 markus Exp $ */ /* * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland * All rights reserved @@ -1157,6 +1157,7 @@ do_child(Session *s, const char *command) /* remove hostkey from the child's memory */ destroy_sensitive_data(); + packet_clear_keys(); /* Force a password change */ if (s->authctxt->force_pwchange) { diff --git a/usr.bin/ssh/sshd.c b/usr.bin/ssh/sshd.c index a05549bcdf1..161bc7339ea 100644 --- a/usr.bin/ssh/sshd.c +++ b/usr.bin/ssh/sshd.c @@ -1,4 +1,4 @@ -/* $OpenBSD: sshd.c,v 1.489 2017/05/31 07:00:13 markus Exp $ */ +/* $OpenBSD: sshd.c,v 1.490 2017/05/31 08:09:45 markus Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland @@ -608,6 +608,7 @@ privsep_postauth(Authctxt *authctxt) else if (pmonitor->m_pid != 0) { verbose("User child is on pid %ld", (long)pmonitor->m_pid); buffer_clear(&loginmsg); + monitor_clear_keystate(pmonitor); monitor_child_postauth(pmonitor); /* NEVERREACHED */ @@ -1877,6 +1878,7 @@ main(int ac, char **av) */ if (use_privsep) { mm_send_keystate(pmonitor); + packet_clear_keys(); exit(0); } diff --git a/usr.bin/ssh/umac.c b/usr.bin/ssh/umac.c index 6d07b6365b3..b2fca71436e 100644 --- a/usr.bin/ssh/umac.c +++ b/usr.bin/ssh/umac.c @@ -1,4 +1,4 @@ -/* $OpenBSD: umac.c,v 1.11 2014/07/22 07:13:42 guenther Exp $ */ +/* $OpenBSD: umac.c,v 1.12 2017/05/31 08:09:45 markus Exp $ */ /* ----------------------------------------------------------------------- * * umac.c -- C Implementation UMAC Message Authentication @@ -197,6 +197,8 @@ static void kdf(void *buffer_ptr, aes_int_key key, UINT8 ndx, int nbytes) aes_encryption(in_buf, out_buf, key); memcpy(dst_buf,out_buf,nbytes); } + explicit_bzero(in_buf, sizeof(in_buf)); + explicit_bzero(out_buf, sizeof(out_buf)); } /* The final UHASH result is XOR'd with the output of a pseudorandom @@ -221,6 +223,7 @@ static void pdf_init(pdf_ctx *pc, aes_int_key prf_key) /* Initialize pdf and cache */ memset(pc->nonce, 0, sizeof(pc->nonce)); aes_encryption(pc->nonce, pc->cache, pc->prf_key); + explicit_bzero(buf, sizeof(buf)); } static void pdf_gen_xor(pdf_ctx *pc, const UINT8 nonce[8], UINT8 buf[8]) @@ -539,6 +542,7 @@ static void nh_transform(nh_ctx *hc, const UINT8 *buf, UINT32 nbytes) /* ---------------------------------------------------------------------- */ +#if (__LITTLE_ENDIAN__) static void endian_convert(void *buf, UWORD bpw, UINT32 num_bytes) /* We endian convert the keys on little-endian computers to */ /* compensate for the lack of big-endian memory reads during hashing. */ @@ -561,7 +565,6 @@ static void endian_convert(void *buf, UWORD bpw, UINT32 num_bytes) } while (--iters); } } -#if (__LITTLE_ENDIAN__) #define endian_convert_if_le(x,y,z) endian_convert((x),(y),(z)) #else #define endian_convert_if_le(x,y,z) do{}while(0) /* Do nothing */ @@ -985,6 +988,7 @@ static void uhash_init(uhash_ctx_t ahc, aes_int_key prf_key) kdf(ahc->ip_trans, prf_key, 4, STREAMS * sizeof(UINT32)); endian_convert_if_le(ahc->ip_trans, sizeof(UINT32), STREAMS * sizeof(UINT32)); + explicit_bzero(buf, sizeof(buf)); } /* ---------------------------------------------------------------------- */ @@ -1194,6 +1198,7 @@ int umac_delete(struct umac_ctx *ctx) if (ctx) { if (ALLOC_BOUNDARY) ctx = (struct umac_ctx *)ctx->free_ptr; + explicit_bzero(ctx, sizeof(*ctx) + ALLOC_BOUNDARY); free(ctx); } return (1); @@ -1221,6 +1226,7 @@ struct umac_ctx *umac_new(const u_char key[]) aes_key_setup(key, prf_key); pdf_init(&ctx->pdf, prf_key); uhash_init(&ctx->hash, prf_key); + explicit_bzero(prf_key, sizeof(prf_key)); } return (ctx);