Introduce and use secure_memzero() to erase secrets
PATCH 1/1 Introduce and use secure_memzero() to erase secrets
As described in trac #751, and shortly after reported by Zhaomo Yang, of the University of California, San Diego, we use memset() (often through the CLEAR() macro) to erase secrets after use. In some cases however, the compiler might optimize these calls away.
This patch replaces these memset() calls on secrets by calls to a new secure_memzero() function, that will not be optimized away.
Since we use CLEAR() a LOT of times, I'm not changing that to use secure_memzero() to prevent performance impact. I did annotate the macro to point people at secure_memzero().
This patch also replaces some CLEAR() or memset() calls with a zero- initialization using "= { 0 }" if that has the same effect, because that better captures the intend of that code.
--- src/openvpn/basic.h | 2 +- src/openvpn/buffer.c | 8 ++++---- src/openvpn/buffer.h | 43 +++++++++++++++++++++++++++++++++++++++++++ src/openvpn/crypto.c | 17 ++++++----------- src/openvpn/manage.c | 2 +- src/openvpn/misc.c | 2 +- src/openvpn/options.c | 6 +++--- src/openvpn/ssl.c | 22 +++++++++++----------- 8 files changed, 70 insertions(+), 32 deletions(-)
src/openvpn/basic.h +1 -1
@@ -30,7 +30,7 @@/* size of an array */ #define SIZE (x) (sizeof(x)/sizeof(x[0])) -/* clear an object */ +/* clear an object (may be optimized away, use secure_memzero() to erase secrets) */ #define CLEAR (x) memset(&(x), 0, sizeof(x))#define IPV4_NETMASK_HOST 0xffffffffU
src/openvpn/buffer.c +4 -4
@@ -155,7 +155,9 @@ void buf_clear (struct buffer *buf) {if (buf->capacity >0 ) -memset (buf->data,0 , buf->capacity); + { +secure_memzero (buf->data, buf->capacity); + } buf->len =0 ; buf->offset =0 ; }
@@ -616,9 +618,7 @@ string_clear (char *str) {if (str) { -const int len =strlen (str); -if (len >0 ) -memset (str,0 , len); +secure_memzero (str,strlen (str)); } }
src/openvpn/buffer.h +43 -0
49 lines not on this page · raw message
src/openvpn/crypto.c +6 -11
@@ -86,12 +86,11 @@ openvpn_encrypt_aead (struct buffer *buf, struct buffer work, {struct buffer iv_buffer;struct packet_id_net pin; -uint8_t iv[OPENVPN_MAX_IV_LENGTH ]; +uint8_t iv[OPENVPN_MAX_IV_LENGTH ] = {0 };const int iv_len =cipher_ctx_iv_length (ctx->cipher);ASSERT (iv_len >=OPENVPN_AEAD_MIN_IV_LEN && iv_len <=OPENVPN_MAX_IV_LENGTH ); -memset (iv,0 ,sizeof (iv));buf_set_write (&iv_buffer, iv, iv_len);/* IV starts with packet id to make the IV unique for packet */
@@ -175,7 +174,7 @@ openvpn_encrypt_v1 (struct buffer *buf, struct buffer work,/* Do Encrypt from buf -> work */ if (ctx->cipher) { -uint8_t iv_buf[OPENVPN_MAX_IV_LENGTH ]; +uint8_t iv_buf[OPENVPN_MAX_IV_LENGTH ] = {0 };const int iv_size =cipher_ctx_iv_length (ctx->cipher);const cipher_kt_t *cipher_kt =cipher_ctx_get_cipher_kt (ctx->cipher);int outlen;
@@ -190,8 +189,6 @@ openvpn_encrypt_v1 (struct buffer *buf, struct buffer work,if (cipher_kt_mode_cbc (cipher_kt)) { -CLEAR (iv_buf); -/* generate pseudo-random IV */ if (opt->flags &CO_USE_IV )prng_bytes (iv_buf, iv_size);
@@ -214,7 +211,6 @@ openvpn_encrypt_v1 (struct buffer *buf, struct buffer work,ASSERT (packet_id_initialized (&opt->packet_id));packet_id_alloc_outgoing (&opt->packet_id.send, &pin, true); -memset (iv_buf,0 , iv_size);buf_set_write (&b, iv_buf, iv_size);ASSERT (packet_id_write (&pin, &b, true, false)); }
@@ -550,14 +546,13 @@ openvpn_decrypt_v1 (struct buffer *buf, struct buffer work, {const int iv_size =cipher_ctx_iv_length (ctx->cipher);const cipher_kt_t *cipher_kt =cipher_ctx_get_cipher_kt (ctx->cipher); -uint8_t iv_buf[OPENVPN_MAX_IV_LENGTH ]; +uint8_t iv_buf[OPENVPN_MAX_IV_LENGTH ] = {0 };int outlen;/* initialize work buffer with FRAME_HEADROOM bytes of prepend capacity */ ASSERT (buf_init (&work,FRAME_HEADROOM_ADJ (frame,FRAME_HEADROOM_MARKER_DECRYPT )));/* use IV if user requested it */ -CLEAR (iv_buf);if (opt->flags &CO_USE_IV ) {if (buf->len < iv_size)
@@ -1128,7 +1123,7 @@ crypto_read_openvpn_key (const struct key_type *key_type,init_key_ctx (&ctx->decrypt, &key2.keys[kds.in_key], key_type,OPENVPN_OP_DECRYPT , log_prefix); -CLEAR (key2); +secure_memzero (&key2,sizeof (key2)); }/* header and footer for static key file */
@@ -1380,8 +1375,8 @@ write_key_file (const int nkeys, const char *filename)buf_printf (&out,"%s\n" , fmt);/* zero memory which held key component (will be freed by GC) */ -memset (fmt,0 ,strlen (fmt)); -CLEAR (key); +secure_memzero (fmt,strlen (fmt)); +secure_memzero (&key,sizeof (key)); }buf_printf (&out,"%s\n" , static_key_foot);
src/openvpn/manage.c +1 -1
@@ -3154,7 +3154,7 @@ management_query_user_pass (struct management *man, man->connection.up_query.nocache = up->nocache;/* preserve caller's nocache setting */ *up = man->connection.up_query; } -CLEAR (man->connection.up_query); +secure_memzero (&man->connection.up_query,sizeof (man->connection.up_query)); }gc_free (&gc);
src/openvpn/misc.c +1 -1
@@ -1342,7 +1342,7 @@ purge_user_pass (struct user_pass *up, const bool force)static bool warn_shown = false;if (nocache || force) { -CLEAR (*up); +secure_memzero (up,sizeof (*up)); up->nocache = nocache; }else if (!warn_shown)
src/openvpn/options.c +3 -3
@@ -3968,7 +3968,7 @@ read_inline_file (struct in_src *is, const char *close_tag, struct gc_arena *gc) ret =string_alloc (BSTR (&buf), gc);buf_clear (&buf);free_buf (&buf); -CLEAR (line); +secure_memzero (line,sizeof (line));return ret; }
@@ -4083,7 +4083,7 @@ read_config_file (struct options *options, {msg (msglevel,"In %s:%d: Maximum recursive include levels exceeded in include attempt of file %s -- probably you have a configuration file that tries to include itself." , top_file, top_line, file); } -CLEAR (line); +secure_memzero (line,sizeof (line));CLEAR (p); }
@@ -4115,7 +4115,7 @@ read_config_string (const char *prefix, }CLEAR (p); } -CLEAR (line); +secure_memzero (line,sizeof (line)); }void
src/openvpn/ssl.c +11 -11
@@ -1222,7 +1222,7 @@ tls_multi_free (struct tls_multi *multi, bool clear)tls_session_free (&multi->session[i], false);if (clear) -CLEAR (*multi); +secure_memzero (multi,sizeof (*multi));free (multi); }
@@ -1512,7 +1512,7 @@ tls1_P_hash(const md_kt_t *md_kt, }hmac_ctx_cleanup (&ctx);hmac_ctx_cleanup (&ctx_tmp); -CLEAR (A1 ); +secure_memzero (A1 ,sizeof (A1 ));dmsg (D_SHOW_KEY_SOURCE ,"tls1_P_hash out: %s" ,format_hex (out_orig, olen_orig,0 , &gc));gc_free (&gc);
@@ -1702,8 +1702,8 @@ generate_key_expansion (struct key_ctx_bi *key, ret = true; exit: -CLEAR (master); -CLEAR (key2); +secure_memzero (&master,sizeof (master)); +secure_memzero (&key2,sizeof (key2));return ret; }
@@ -1787,7 +1787,7 @@ tls_session_generate_data_channel_keys(struct tls_session *session) ret = true; cleanup: -CLEAR (*ks->key_src); +secure_memzero (ks->key_src,sizeof (*ks->key_src));return ret; }
@@ -2017,7 +2017,7 @@ key_method_1_write (struct buffer *buf, struct tls_session *session)init_key_ctx (&ks->crypto_options.key_ctx_bi.encrypt, &key, &session->opt->key_type,OPENVPN_OP_ENCRYPT ,"Data Channel Encrypt" ); -CLEAR (key); +secure_memzero (&key,sizeof (key));/* send local options string */ {
@@ -2202,7 +2202,7 @@ key_method_2_write (struct buffer *buf, struct tls_session *session) error:msg (D_TLS_ERRORS ,"TLS Error: Key Method #2 write failed" ); -CLEAR (*ks->key_src); +secure_memzero (ks->key_src,sizeof (*ks->key_src));return false; }
@@ -2257,13 +2257,13 @@ key_method_1_read (struct buffer *buf, struct tls_session *session)init_key_ctx (&ks->crypto_options.key_ctx_bi.decrypt, &key, &session->opt->key_type,OPENVPN_OP_DECRYPT ,"Data Channel Decrypt" ); -CLEAR (key); +secure_memzero (&key,sizeof (key)); ks->authenticated = true;return true; error:buf_clear (buf); -CLEAR (key); +secure_memzero (&key,sizeof (key));return false; }
@@ -2377,7 +2377,7 @@ key_method_2_read (struct buffer *buf, struct tls_multi *multi, struct tls_sessi }/* clear username and password from memory */ -CLEAR (*up); +secure_memzero (up,sizeof (*up));/* Perform final authentication checks */ if (ks->authenticated)
@@ -2433,7 +2433,7 @@ key_method_2_read (struct buffer *buf, struct tls_multi *multi, struct tls_sessireturn true; error: -CLEAR (*ks->key_src); +secure_memzero (ks->key_src,sizeof (*ks->key_src));buf_clear (buf);gc_free (&gc);return false;
Signature · 1 line
2.7.4
Quoted 12 lines · Steffan Karger
As described in trac #751, and shortly after reported by Zhaomo Yang, of the University of California, San Diego, we use
memset()(often through theCLEAR()macro) to erase secrets after use. In some cases however, the compiler might optimize these calls away.This patch replaces these
memset()calls on secrets by calls to a newsecure_memzero()function, that will not be optimized away.Since we use
CLEAR()a LOT of times, I'm not changing that to usesecure_memzero()to prevent performance impact. I did annotate the macro to point people atsecure_memzero().
Quoted 6 lines
This patch also replaces some
CLEAR()ormemset()calls with a zero- initialization using "= { 0 }" if that has the same effect, because that better captures the intend of that code.Signed-off-by Steffan Karger <[email protected]...>
All look good except that it might have missed some memset calls that could benefit from a SecureZeroMemory() or equivalent:
With no pretense of completeness, here is a list with some context: calls marked with <-- SecureZero
src/openvpn/console_builtin.c +0 -0
@@ -218,7 +218,7 @@ static bool get_console_input (const char *prompt,
const bool echo, char *input,
if (gp)
{
strncpynt (input, gp, capacity);
- memset (gp, 0, strlen (gp));
+ memset (gp, 0, strlen (gp)); <-- SecureZero
ret = true;
}
}src/openvpn/misc.c +0 -0
@@ -501,7 +501,7 @@ remove_env_item (const char *str, const bool do_free,
struct env_item **list)
*list = current->next;
if (do_free)
{
- memset (current->string, 0, strlen (current->string));
+ memset (current->string, 0, strlen (current->string)); <--
SecureZero
free (current->string);
free (current);
}src/openvpn/ssl.c +1 -1
@@ -1212,7 +1212,7 @@ tls_multi_free (struct tls_multi *multi, bool clear)if (multi->auth_token) { -memset (multi->auth_token,0 ,AUTH_TOKEN_SIZE ); + memset (multi->auth_token, 0, AUTH_TOKEN_SIZE); <-- SecureZero free (multi->auth_token); }
src/openvpn/ssl_verify.c +0 -0
@@ -1176,7 +1176,7 @@ verify_user_pass(struct user_pass *up, struct
tls_multi *multi,
if (memcmp_constant_time(multi->auth_token, up->password,
strlen(multi->auth_token)) != 0)
{
- memset (multi->auth_token, 0, AUTH_TOKEN_SIZE);
+ memset (multi->auth_token, 0, AUTH_TOKEN_SIZE); <-- SecureZero
free (multi->auth_token);Selva
Hi,
My sloppy cut-n-paste missed this one:
in ssl_verify.c
@@ -1262,7 +1262,7 @@ verify_user_pass(struct user_pass *up, struct tls_multi *multi,
"No auth-token will be activated now");
if (multi->auth_token) {
- memset (multi->auth_token, 0, AUTH_TOKEN_SIZE);
+ memset (multi->auth_token, 0, AUTH_TOKEN_SIZE); <-- SecureZero free (multi->auth_token);
multi->auth_token = NULL;
}
Selva
Quoted 56 lines · Selva Nair
On Mon, Nov 28, 2016 at 7:13 AM, Steffan Karger <[email protected]... <mailto:[email protected]...>> wrote:
As described in trac #751, and shortly after reported by Zhaomo Yang, of the University of California, San Diego, we use
memset()(often through theCLEAR()macro) to erase secrets after use. In some cases however, the
compiler might optimize these calls away.This patch replaces these
memset()calls on secrets by calls to a newsecure_memzero()function, that will not be optimized away.Since we use
CLEAR()a LOT of times, I'm not changing that to usesecure_memzero()to prevent performance impact. I did annotate the macro
to point people atsecure_memzero().This patch also replaces some
CLEAR()ormemset()calls with a zero- initialization using "= { 0 }" if that has the same effect, because that better captures the intend of that code.Signed-off-by Steffan Karger <[email protected]...<mailto:[email protected]...>>
All look good except that it might have missed some memset calls that could benefit from a
SecureZeroMemory()or equivalent:With no pretense of completeness, here is a list with some context:
calls marked with <-- SecureZerosrc/openvpn/console_builtin.c +1
-1@@ -218,7 +218,7 @@ static bool get_console_input (const char *prompt, const bool echo, char *input, if (gp) {strncpynt (input ,gp ,capacity ); -memset (gp,0 ,strlen (gp)); + memset (gp, 0, strlen (gp)); <-- SecureZero ret = true; }}src/openvpn/misc.c +1
-1@@ -501,7 +501,7 @@ remove_env_item (const char *str, const bool do_free, struct env_item **list) *list = current->next;if (do_free) { -memset (current->string,0 ,strlen (current->string)); + memset (current->string, 0, strlen (current->string)); <--SecureZero
Quoted 4 lines
free (current->string);
free (current);
}Thanks, these make sense to include too, indeed.
Quoted 33 lines
src/openvpn/ssl.c +1
-1@@ -1212,7 +1212,7 @@ tls_multi_free (struct tls_multi *multi, bool clear)if (multi->auth_token) { -memset (multi->auth_token,0 ,AUTH_TOKEN_SIZE ); + memset (multi->auth_token, 0, AUTH_TOKEN_SIZE); <-- SecureZero free (multi->auth_token); }src/openvpn/ssl_verify.c +2
-2@@ -1176,7 +1176,7 @@ verify_user_pass(struct user_pass *up, struct tls_multi *multi, if (memcmp_constant_time(multi->auth_token, up->password, strlen(multi->auth_token)) != 0) { -memset (multi->auth_token,0 ,AUTH_TOKEN_SIZE ); + memset (multi->auth_token, 0, AUTH_TOKEN_SIZE); <-- SecureZero free (multi->auth_token);@@ -1262,7 +1262,7 @@ verify_user_pass(struct user_pass *up, struct tls_multi *multi, "No auth-token will be activated now");if (multi->auth_token) { -memset (multi->auth_token,0 ,AUTH_TOKEN_SIZE ); + memset (multi->auth_token, 0, AUTH_TOKEN_SIZE); <-- SecureZero free (multi->auth_token); multi->auth_token =NULL ;
Quoted 1 line
}
This is just silly, these lines are the reason I created trac #741 in the first place...
v2 coming soon!
-Steffan
PATCH v2 1/1 Introduce and use secure_memzero() to erase secrets
As described in trac #751, and shortly after reported by Zhaomo Yang, of the University of California, San Diego, we use memset() (often through the CLEAR() macro) to erase secrets after use. In some cases however, the compiler might optimize these calls away.
This patch replaces these memset() calls on secrets by calls to a new secure_memzero() function, that will not be optimized away.
Since we use CLEAR() a LOT of times, I'm not changing that to use secure_memzero() to prevent performance impact. I did annotate the macro to point people at secure_memzero().
This patch also replaces some CLEAR() or memset() calls with a zero- initialization using "= { 0 }" if that has the same effect, because that better captures the intend of that code.
---
v2: replace more memset()/CLEAR() calls with secure_memzero()
src/openvpn/basic.h | 2 +- src/openvpn/buffer.c | 8 ++++---- src/openvpn/buffer.h | 43 ++++++++++++++++++++++++++++++++++++++++ src/openvpn/console_builtin.c | 2 +- src/openvpn/crypto.c | 17 ++++++---------- src/openvpn/manage.c | 2 +- src/openvpn/misc.c | 4 ++-- src/openvpn/options.c | 6 +++--- src/openvpn/ssl.c | 32 +++++++++++++++--------------- src/openvpn/ssl_verify.c | 4 ++-- src/openvpn/ssl_verify_mbedtls.c | 4 +--- 11 files changed, 80 insertions(+), 44 deletions(-)
src/openvpn/basic.h +1 -1
@@ -30,7 +30,7 @@/* size of an array */ #define SIZE (x) (sizeof(x)/sizeof(x[0])) -/* clear an object */ +/* clear an object (may be optimized away, use secure_memzero() to erase secrets) */ #define CLEAR (x) memset(&(x), 0, sizeof(x))#define IPV4_NETMASK_HOST 0xffffffffU
src/openvpn/buffer.c +4 -4
@@ -155,7 +155,9 @@ void buf_clear (struct buffer *buf) {if (buf->capacity >0 ) -memset (buf->data,0 , buf->capacity); + { +secure_memzero (buf->data, buf->capacity); + } buf->len =0 ; buf->offset =0 ; }
@@ -624,9 +626,7 @@ string_clear (char *str) {if (str) { -const int len =strlen (str); -if (len >0 ) -memset (str,0 , len); +secure_memzero (str,strlen (str)); } }
src/openvpn/buffer.h +43 -0
49 lines not on this page · raw message
src/openvpn/console_builtin.c +1 -1
@@ -218,7 +218,7 @@ static bool get_console_input (const char *prompt, const bool echo, char *input,if (gp) {strncpynt (input, gp, capacity); -memset (gp,0 ,strlen (gp)); +secure_memzero (gp,strlen (gp)); ret = true; } }
src/openvpn/crypto.c +6 -11
@@ -86,12 +86,11 @@ openvpn_encrypt_aead (struct buffer *buf, struct buffer work, {struct buffer iv_buffer;struct packet_id_net pin; -uint8_t iv[OPENVPN_MAX_IV_LENGTH ]; +uint8_t iv[OPENVPN_MAX_IV_LENGTH ] = {0 };const int iv_len =cipher_ctx_iv_length (ctx->cipher);ASSERT (iv_len >=OPENVPN_AEAD_MIN_IV_LEN && iv_len <=OPENVPN_MAX_IV_LENGTH ); -memset (iv,0 ,sizeof (iv));buf_set_write (&iv_buffer, iv, iv_len);/* IV starts with packet id to make the IV unique for packet */
@@ -175,7 +174,7 @@ openvpn_encrypt_v1 (struct buffer *buf, struct buffer work,/* Do Encrypt from buf -> work */ if (ctx->cipher) { -uint8_t iv_buf[OPENVPN_MAX_IV_LENGTH ]; +uint8_t iv_buf[OPENVPN_MAX_IV_LENGTH ] = {0 };const int iv_size =cipher_ctx_iv_length (ctx->cipher);const cipher_kt_t *cipher_kt =cipher_ctx_get_cipher_kt (ctx->cipher);int outlen;
@@ -190,8 +189,6 @@ openvpn_encrypt_v1 (struct buffer *buf, struct buffer work,if (cipher_kt_mode_cbc (cipher_kt)) { -CLEAR (iv_buf); -/* generate pseudo-random IV */ if (opt->flags &CO_USE_IV )prng_bytes (iv_buf, iv_size);
@@ -214,7 +211,6 @@ openvpn_encrypt_v1 (struct buffer *buf, struct buffer work,ASSERT (packet_id_initialized (&opt->packet_id));packet_id_alloc_outgoing (&opt->packet_id.send, &pin, true); -memset (iv_buf,0 , iv_size);buf_set_write (&b, iv_buf, iv_size);ASSERT (packet_id_write (&pin, &b, true, false)); }
@@ -550,14 +546,13 @@ openvpn_decrypt_v1 (struct buffer *buf, struct buffer work, {const int iv_size =cipher_ctx_iv_length (ctx->cipher);const cipher_kt_t *cipher_kt =cipher_ctx_get_cipher_kt (ctx->cipher); -uint8_t iv_buf[OPENVPN_MAX_IV_LENGTH ]; +uint8_t iv_buf[OPENVPN_MAX_IV_LENGTH ] = {0 };int outlen;/* initialize work buffer with FRAME_HEADROOM bytes of prepend capacity */ ASSERT (buf_init (&work,FRAME_HEADROOM_ADJ (frame,FRAME_HEADROOM_MARKER_DECRYPT )));/* use IV if user requested it */ -CLEAR (iv_buf);if (opt->flags &CO_USE_IV ) {if (buf->len < iv_size)
@@ -1128,7 +1123,7 @@ crypto_read_openvpn_key (const struct key_type *key_type,init_key_ctx (&ctx->decrypt, &key2.keys[kds.in_key], key_type,OPENVPN_OP_DECRYPT , log_prefix); -CLEAR (key2); +secure_memzero (&key2,sizeof (key2)); }/* header and footer for static key file */
@@ -1380,8 +1375,8 @@ write_key_file (const int nkeys, const char *filename)buf_printf (&out,"%s\n" , fmt);/* zero memory which held key component (will be freed by GC) */ -memset (fmt,0 ,strlen (fmt)); -CLEAR (key); +secure_memzero (fmt,strlen (fmt)); +secure_memzero (&key,sizeof (key)); }buf_printf (&out,"%s\n" , static_key_foot);
src/openvpn/manage.c +1 -1
@@ -3154,7 +3154,7 @@ management_query_user_pass (struct management *man, man->connection.up_query.nocache = up->nocache;/* preserve caller's nocache setting */ *up = man->connection.up_query; } -CLEAR (man->connection.up_query); +secure_memzero (&man->connection.up_query,sizeof (man->connection.up_query)); }gc_free (&gc);
src/openvpn/misc.c +2 -2
@@ -501,7 +501,7 @@ remove_env_item (const char *str, const bool do_free, struct env_item **list) *list = current->next;if (do_free) { -memset (current->string,0 ,strlen (current->string)); +secure_memzero (current->string,strlen (current->string));free (current->string);free (current); }
@@ -1342,7 +1342,7 @@ purge_user_pass (struct user_pass *up, const bool force)static bool warn_shown = false;if (nocache || force) { -CLEAR (*up); +secure_memzero (up,sizeof (*up)); up->nocache = nocache; }else if (!warn_shown)
src/openvpn/options.c +3 -3
@@ -3957,7 +3957,7 @@ read_inline_file (struct in_src *is, const char *close_tag, struct gc_arena *gc) ret =string_alloc (BSTR (&buf), gc);buf_clear (&buf);free_buf (&buf); -CLEAR (line); +secure_memzero (line,sizeof (line));return ret; }
@@ -4072,7 +4072,7 @@ read_config_file (struct options *options, {msg (msglevel,"In %s:%d: Maximum recursive include levels exceeded in include attempt of file %s -- probably you have a configuration file that tries to include itself." , top_file, top_line, file); } -CLEAR (line); +secure_memzero (line,sizeof (line));CLEAR (p); }
@@ -4104,7 +4104,7 @@ read_config_string (const char *prefix, }CLEAR (p); } -CLEAR (line); +secure_memzero (line,sizeof (line)); }void
src/openvpn/ssl.c +16 -16
@@ -894,7 +894,7 @@ key_state_free (struct key_state *ks, bool clear)#endif if (clear) -CLEAR (*ks); +secure_memzero (ks,sizeof (*ks)); }/** @} name Functions for initialization and cleanup of key_state structures */
@@ -1024,7 +1024,7 @@ tls_session_free (struct tls_session *session, bool clear)cert_hash_free (session->cert_hash_set);if (clear) -CLEAR (*session); +secure_memzero (session,sizeof (*session)); }/** @} name Functions for initialization and cleanup of tls_session structures */
@@ -1048,7 +1048,7 @@ move_session (struct tls_multi* multi, int dest, int src, bool reinit_src)if (reinit_src)tls_session_init (multi, &multi->session[src]);else -CLEAR (multi->session[src]); +secure_memzero (&multi->session[src],sizeof (multi->session[src]));dmsg (D_TLS_DEBUG ,"TLS: move_session: exit" ); }
@@ -1212,7 +1212,7 @@ tls_multi_free (struct tls_multi *multi, bool clear)if (multi->auth_token) { -memset (multi->auth_token,0 ,AUTH_TOKEN_SIZE ); +secure_memzero (multi->auth_token,AUTH_TOKEN_SIZE );free (multi->auth_token); }
@@ -1222,7 +1222,7 @@ tls_multi_free (struct tls_multi *multi, bool clear)tls_session_free (&multi->session[i], false);if (clear) -CLEAR (*multi); +secure_memzero (multi,sizeof (*multi));free (multi); }
@@ -1512,7 +1512,7 @@ tls1_P_hash(const md_kt_t *md_kt, }hmac_ctx_cleanup (&ctx);hmac_ctx_cleanup (&ctx_tmp); -CLEAR (A1 ); +secure_memzero (A1 ,sizeof (A1 ));dmsg (D_SHOW_KEY_SOURCE ,"tls1_P_hash out: %s" ,format_hex (out_orig, olen_orig,0 , &gc));gc_free (&gc);
@@ -1565,7 +1565,7 @@ tls1_PRF(const uint8_t *label,for (i=0 ; i<olen; i++) out1[i]^=out2[i]; -memset (out2,0 , olen); +secure_memzero (out2, olen);dmsg (D_SHOW_KEY_SOURCE ,"tls1_PRF out[%d]: %s" , olen,format_hex (out1, olen,0 , &gc));
@@ -1702,8 +1702,8 @@ generate_key_expansion (struct key_ctx_bi *key, ret = true; exit: -CLEAR (master); -CLEAR (key2); +secure_memzero (&master,sizeof (master)); +secure_memzero (&key2,sizeof (key2));return ret; }
@@ -1787,7 +1787,7 @@ tls_session_generate_data_channel_keys(struct tls_session *session) ret = true; cleanup: -CLEAR (*ks->key_src); +secure_memzero (ks->key_src,sizeof (*ks->key_src));return ret; }
@@ -2017,7 +2017,7 @@ key_method_1_write (struct buffer *buf, struct tls_session *session)init_key_ctx (&ks->crypto_options.key_ctx_bi.encrypt, &key, &session->opt->key_type,OPENVPN_OP_ENCRYPT ,"Data Channel Encrypt" ); -CLEAR (key); +secure_memzero (&key,sizeof (key));/* send local options string */ {
@@ -2202,7 +2202,7 @@ key_method_2_write (struct buffer *buf, struct tls_session *session) error:msg (D_TLS_ERRORS ,"TLS Error: Key Method #2 write failed" ); -CLEAR (*ks->key_src); +secure_memzero (ks->key_src,sizeof (*ks->key_src));return false; }
@@ -2257,13 +2257,13 @@ key_method_1_read (struct buffer *buf, struct tls_session *session)init_key_ctx (&ks->crypto_options.key_ctx_bi.decrypt, &key, &session->opt->key_type,OPENVPN_OP_DECRYPT ,"Data Channel Decrypt" ); -CLEAR (key); +secure_memzero (&key,sizeof (key)); ks->authenticated = true;return true; error:buf_clear (buf); -CLEAR (key); +secure_memzero (&key,sizeof (key));return false; }
@@ -2377,7 +2377,7 @@ key_method_2_read (struct buffer *buf, struct tls_multi *multi, struct tls_sessi }/* clear username and password from memory */ -CLEAR (*up); +secure_memzero (up,sizeof (*up));/* Perform final authentication checks */ if (ks->authenticated)
@@ -2433,7 +2433,7 @@ key_method_2_read (struct buffer *buf, struct tls_multi *multi, struct tls_sessireturn true; error: -CLEAR (*ks->key_src); +secure_memzero (ks->key_src,sizeof (*ks->key_src));buf_clear (buf);gc_free (&gc);return false;
src/openvpn/ssl_verify.c +2 -2
@@ -1176,7 +1176,7 @@ verify_user_pass(struct user_pass *up, struct tls_multi *multi,if (memcmp_constant_time (multi->auth_token, up->password,strlen (multi->auth_token)) !=0 ) { -memset (multi->auth_token,0 ,AUTH_TOKEN_SIZE ); +secure_memzero (multi->auth_token,AUTH_TOKEN_SIZE );free (multi->auth_token); multi->auth_token =NULL ; multi->auth_token_sent = false;
@@ -1262,7 +1262,7 @@ verify_user_pass(struct user_pass *up, struct tls_multi *multi, "No auth-token will be activated now");if (multi->auth_token) { -memset (multi->auth_token,0 ,AUTH_TOKEN_SIZE ); +secure_memzero (multi->auth_token,AUTH_TOKEN_SIZE );free (multi->auth_token); multi->auth_token =NULL ; }
src/openvpn/ssl_verify_mbedtls.c +1 -3
@@ -348,12 +348,10 @@ x509_setenv (struct env_set *es, int cert_depth, mbedtls_x509_crt *cert)int i;unsigned char c;const mbedtls_x509_name *name; -char s[128 ]; +char s[128 ] = {0 }; name = &cert->subject; -memset ( s,0 ,sizeof ( s ) ); -while ( name !=NULL ) {char name_expand[64 +8 ];
Signature · 1 line
2.7.4
Hi folks,
Just wanted to let you know that we have a new implementation of secure_memzero.h, which is available at
https://compsec.sysnet.ucsd.edu/secure_memzero.h.
The version I sent to you guys has a minor issue in dealing with memset_s. Also, this implementation synthesizes most of the existing working solutions for this problem. It allows you to specify, via macros, which implementation you prefer. This will let users override our defaults, if they want.
Thanks,
Zhaomo
Hi,
Quoted 10 lines · Zhaomo Yang
Just wanted to let you know that we have a new implementation of
secure_memzero.h, which is available at
https://compsec.sysnet.ucsd.edu/secure_memzero.h
<https://compsec.sysnet.ucsd.edu/secure_memzero.h>.The version I sent to you guys has a minor issue in dealing with
memset_s. Also, this implementation synthesizes most of the existing working solutions for this problem. It allows you to specify, via macros, which implementation you prefer. This will let users override our defaults, if they want.
Thanks, but I think I prefer the more light-weight approach you sent earlier (slightly molded to use static inline wrapper functions instead of macro's). It does what it needs to do for us, and is sufficiently small to easily integrate into existing code.
And thanks again for notifying us (on security@) on your findings.
-Steffan
Am 29.11.16 um 09:20 schrieb Steffan Karger:
Quoted 13 lines
Hi,
Quoted 10 lines · Zhaomo Yang
Just wanted to let you know that we have a new implementation of
secure_memzero.h, which is available at
https://compsec.sysnet.ucsd.edu/secure_memzero.h
<https://compsec.sysnet.ucsd.edu/secure_memzero.h>.The version I sent to you guys has a minor issue in dealing with
memset_s. Also, this implementation synthesizes most of the existing working solutions for this problem. It allows you to specify, via macros, which implementation you prefer. This will let users override our defaults, if they want.
Quoted 7 lines
Thanks, but I think I prefer the more light-weight approach you sent earlier (slightly molded to use static inline wrapper functions instead of macro's). It does what it needs to do for us, and is sufficiently small to easily integrate into existing code.
And thanks again for notifying us (on security@) on your findings.
I think we should prefer memset_s if available since it is a C11 standard.
Arne
Hi,
Quoted 1 line · Arne Schwabe
I think we should prefer
memset_sif available since it is a C11 standard.
Since we're currently compiling against C99, we can't rely on it being there (in my OS Zoo, only MacOS seems to have it, at least "have a manpage for it" - not even FreeBSD 11 or "gentoo current") - but then, configure could check that for us...
Shall we merge the current patch anyway, as the memory barrier does the job on all current compilers, and add a memset_s() variant "whenever one of us finds time to add and test this"?
gert
Signature · 4 lines
USENET is *not* the non-clickable part of WWW!
//www.muc.de/~gert/
Gert Doering - Munich, Germany [email protected]...
fax: +49-89-35655025 [email protected]...Am 29.11.16 um 11:18 schrieb Gert Doering:
Quoted 8 lines
Hi,
Quoted 1 line · Arne Schwabe
I think we should prefer
memset_sif available since it is a C11 standard.Since we're currently compiling against C99, we can't rely on it being there (in my OS Zoo, only MacOS seems to have it, at least "have a manpage for it" - not even FreeBSD 11 or "gentoo current") - but then, configure could check that for us...
I wouldn't add a configure test for that. According to
http://en.cppreference.com/w/c/string/byte/memset:
As all bounds-checked functions, |memset_s| is only guaranteed to be available if __STDC_LIB_EXT1__ is defined by the implementation and if the user defines __STDC_WANT_LIB_EXT1__ to the integer constant 1 before including |string.h|.
so preprocessor magic can check that.
Quoted 5 lines
Shall we merge the current patch anyway, as the memory barrier does the job on all current compilers, and add a
memset_s()variant "whenever one of us finds time to add and test this"?
Yes. Go ahead. I might
Quoted 14 lines · Arne Schwabe
Am 29.11.16 um 11:18 schrieb Gert Doering:
Quoted 8 lines
Hi,
Quoted 1 line · Arne Schwabe
I think we should prefer
memset_sif available since it is a C11 standard.Since we're currently compiling against C99, we can't rely on it being there (in my OS Zoo, only MacOS seems to have it, at least "have a manpage for it" - not even FreeBSD 11 or "gentoo current") - but then, configure could check that for us...
I wouldn't add a configure test for that. According to
http://en.cppreference.com/w/c/string/byte/memset:
Quoted 6 lines
As all bounds-checked functions, |memset_s| is only guaranteed to be available if
__STDC_LIB_EXT1__is defined by the implementation and if the user defines__STDC_WANT_LIB_EXT1__to the integer constant 1 before including |string.h|.so preprocessor magic can check that.
This is indeed how it should be done (and almost how the new version of Yang et al does it). However, memset_s() is part of the *optional* annex K of C11, and there seems to be no agreement on whether it is a good idea to implemented at all[0].
Given that Windows has SecureZeroMemory() and the memory barrier approach works fine on the unixes, I don't see much gain in adding support for memset_s(). Even so, I won't object to a patch adding it, and will send a v3 if there are more people who want it.
-Steffan
[0] http://comments.gmane.org/gmane.comp.lib.glibc.alpha/44430
Quoted 8 lines · Steffan Karger
As described in trac #751, and shortly after reported by Zhaomo Yang, of the University of California, San Diego, we use
memset()(often through theCLEAR()macro) to erase secrets after use. In some cases however, the compiler might optimize these calls away.This patch replaces these
memset()calls on secrets by calls to a newsecure_memzero()function, that will not be optimized away.
Quoted 7 lines
Since we use
CLEAR()a LOT of times, I'm not changing that to usesecure_memzero()to prevent performance impact. I did annotate the macro to point people atsecure_memzero().This patch also replaces some
CLEAR()ormemset()calls with a zero- initialization using "= { 0 }" if that has the same effect, because that better captures the intend of that code.
First of all, wonderful that you've looked into an improved memset() approach. We need that! I am very happy to see these patches and this discussion.
But I am very reluctant to this approach here, to be honest. I do not like that we have a two semantics for a overlapping functions; one which always ensures memory is wiped (but might be slower) and the other one which might or might not wipe the memory (depending on the mood of the compiler, etc)
If we "do not care" if a memory region is wiped or not (just that it's nice if it happens), then I'd say these are code paths which *do* *not* require CLEAR() at all.
If we *know* a certain memory region *must* be cleared, then we do the proper call which does what it promises us.
I think we should not have "do not care" code paths, because that ambiguity gives us nothing but confusing complexity and such code paths bitrots faster than other type of code.
So I'd say that either we move over to secure_memzero() and yank out CLEAR() all together. Or we yank out CLEAR() calls where we "don't care" and let CLEAR() always do what it tries to promise through its name.
Signature · 4 lines
kind regards, David Sommerseth OpenVPN Technologies, Inc
Quoted 449 lines
Signed-off-by Steffan Karger <[email protected]...>---
v2: replace more memset()/CLEAR() calls withsecure_memzero()src/openvpn/basic.h | 2 +- src/openvpn/buffer.c | 8 ++++---- src/openvpn/buffer.h | 43 ++++++++++++++++++++++++++++++++++++++++ src/openvpn/console_builtin.c | 2 +- src/openvpn/crypto.c | 17 ++++++---------- src/openvpn/manage.c | 2 +- src/openvpn/misc.c | 4 ++-- src/openvpn/options.c | 6 +++--- src/openvpn/ssl.c | 32 +++++++++++++++--------------- src/openvpn/ssl_verify.c | 4 ++-- src/openvpn/ssl_verify_mbedtls.c | 4 +--- 11 files changed, 80 insertions(+), 44 deletions(-)src/openvpn/basic.h +1
-1@@ -30,7 +30,7 @@/* size of an array */ #define SIZE (x) (sizeof(x)/sizeof(x[0])) -/* clear an object */ +/* clear an object (may be optimized away, use secure_memzero() to erase secrets) */ #define CLEAR (x) memset(&(x), 0, sizeof(x))#define IPV4_NETMASK_HOST 0xffffffffUsrc/openvpn/buffer.c +4
-4@@ -155,7 +155,9 @@ void buf_clear (struct buffer *buf) {if (buf->capacity >0 ) -memset (buf->data,0 , buf->capacity); + { +secure_memzero (buf->data, buf->capacity); + } buf->len =0 ; buf->offset =0 ; }@@ -624,9 +626,7 @@ string_clear (char *str) {if (str) { -const int len =strlen (str); -if (len >0 ) -memset (str,0 , len); +secure_memzero (str,strlen (str)); } }src/openvpn/buffer.h +43
-0@@ -333,6 +333,49 @@ has_digit (const unsigned char* src)return false; } +/** +* Securely zeroise memory. +* +* This code and description are based on code supplied by Zhaomo Yang, of the +* University of California, San Diego (which was released into the public +* domain). +* +* The secure_memzero function attempts to ensure that an optimizing compiler +* does not remove the intended operation if cleared memory is not accessed +* again by the program. This code has been tested under Clang 3.9.0 and GCC +* 6.2 with optimization flags -O, -Os, -O0, -O1, -O2, and -O3 on +* Ubuntu 16.04.1 LTS; under Clang 3.9.0 with optimization flags -O, -Os, +* -O0, -O1, -O2, and -O3 on FreeBSD 10.2-RELEASE; under Microsoft Visual Studio +* 2015 with optimization flags /O1, /O2 and /Ox on Windows 10. +* +* Theory of operation: +* +* 1. On Windows, use the SecureZeroMemory which ensures that data is +* overwritten. +* 2. Under GCC or Clang, use a memory barrier, which forces the preceding +* memset to be carried out. The overhead of a memory barrier is usually +* negligible. +* 3. If none of the above are available, use the volatile pointer +* technique to zero memory one byte at a time. +* +* @param data Pointer to data to zeroise. +* @param len Length of data, in bytes. +*/ +static inline void +secure_memzero (void *data,size_t len) +{ +#if defined(_WIN32) +SecureZeroMemory (data, len); +#elif defined(__GNUC__) || defined(__clang__) +memset (data,0 , len); + __asm__ __volatile__("" : :"r" (data) :"memory" ); +#else +volatile char *p = (volatile char *) data; +while (len--) + *p++ =0 ; +#endif +} +/* * printf append to a buffer with overflow check, * due to usage of vsnprintf, it will leave space for src/openvpn/console_builtin.c +1
-1@@ -218,7 +218,7 @@ static bool get_console_input (const char *prompt, const bool echo, char *input,if (gp) {strncpynt (input, gp, capacity); -memset (gp,0 ,strlen (gp)); +secure_memzero (gp,strlen (gp)); ret = true; } }src/openvpn/crypto.c +6
-11@@ -86,12 +86,11 @@ openvpn_encrypt_aead (struct buffer *buf, struct buffer work, {struct buffer iv_buffer;struct packet_id_net pin; -uint8_t iv[OPENVPN_MAX_IV_LENGTH ]; +uint8_t iv[OPENVPN_MAX_IV_LENGTH ] = {0 };const int iv_len =cipher_ctx_iv_length (ctx->cipher);ASSERT (iv_len >=OPENVPN_AEAD_MIN_IV_LEN && iv_len <=OPENVPN_MAX_IV_LENGTH ); -memset (iv,0 ,sizeof (iv));buf_set_write (&iv_buffer, iv, iv_len);/* IV starts with packet id to make the IV unique for packet */ @@ -175,7 +174,7 @@ openvpn_encrypt_v1 (struct buffer *buf, struct buffer work,/* Do Encrypt from buf -> work */ if (ctx->cipher) { -uint8_t iv_buf[OPENVPN_MAX_IV_LENGTH ]; +uint8_t iv_buf[OPENVPN_MAX_IV_LENGTH ] = {0 };const int iv_size =cipher_ctx_iv_length (ctx->cipher);const cipher_kt_t *cipher_kt =cipher_ctx_get_cipher_kt (ctx->cipher);int outlen;@@ -190,8 +189,6 @@ openvpn_encrypt_v1 (struct buffer *buf, struct buffer work,if (cipher_kt_mode_cbc (cipher_kt)) { -CLEAR (iv_buf); -/* generate pseudo-random IV */ if (opt->flags &CO_USE_IV )prng_bytes (iv_buf, iv_size);@@ -214,7 +211,6 @@ openvpn_encrypt_v1 (struct buffer *buf, struct buffer work,ASSERT (packet_id_initialized (&opt->packet_id));packet_id_alloc_outgoing (&opt->packet_id.send, &pin, true); -memset (iv_buf,0 , iv_size);buf_set_write (&b, iv_buf, iv_size);ASSERT (packet_id_write (&pin, &b, true, false)); }@@ -550,14 +546,13 @@ openvpn_decrypt_v1 (struct buffer *buf, struct buffer work, {const int iv_size =cipher_ctx_iv_length (ctx->cipher);const cipher_kt_t *cipher_kt =cipher_ctx_get_cipher_kt (ctx->cipher); -uint8_t iv_buf[OPENVPN_MAX_IV_LENGTH ]; +uint8_t iv_buf[OPENVPN_MAX_IV_LENGTH ] = {0 };int outlen;/* initialize work buffer with FRAME_HEADROOM bytes of prepend capacity */ ASSERT (buf_init (&work,FRAME_HEADROOM_ADJ (frame,FRAME_HEADROOM_MARKER_DECRYPT )));/* use IV if user requested it */ -CLEAR (iv_buf);if (opt->flags &CO_USE_IV ) {if (buf->len < iv_size)@@ -1128,7 +1123,7 @@ crypto_read_openvpn_key (const struct key_type *key_type,init_key_ctx (&ctx->decrypt, &key2.keys[kds.in_key], key_type,OPENVPN_OP_DECRYPT , log_prefix); -CLEAR (key2); +secure_memzero (&key2,sizeof (key2)); }/* header and footer for static key file */ @@ -1380,8 +1375,8 @@ write_key_file (const int nkeys, const char *filename)buf_printf (&out,"%s\n" , fmt);/* zero memory which held key component (will be freed by GC) */ -memset (fmt,0 ,strlen (fmt)); -CLEAR (key); +secure_memzero (fmt,strlen (fmt)); +secure_memzero (&key,sizeof (key)); }buf_printf (&out,"%s\n" , static_key_foot);src/openvpn/manage.c +1
-1@@ -3154,7 +3154,7 @@ management_query_user_pass (struct management *man, man->connection.up_query.nocache = up->nocache;/* preserve caller's nocache setting */ *up = man->connection.up_query; } -CLEAR (man->connection.up_query); +secure_memzero (&man->connection.up_query,sizeof (man->connection.up_query)); }gc_free (&gc);src/openvpn/misc.c +2
-2@@ -501,7 +501,7 @@ remove_env_item (const char *str, const bool do_free, struct env_item **list) *list = current->next;if (do_free) { -memset (current->string,0 ,strlen (current->string)); +secure_memzero (current->string,strlen (current->string));free (current->string);free (current); }@@ -1342,7 +1342,7 @@ purge_user_pass (struct user_pass *up, const bool force)static bool warn_shown = false;if (nocache || force) { -CLEAR (*up); +secure_memzero (up,sizeof (*up)); up->nocache = nocache; }else if (!warn_shown)src/openvpn/options.c +3
-3@@ -3957,7 +3957,7 @@ read_inline_file (struct in_src *is, const char *close_tag, struct gc_arena *gc) ret =string_alloc (BSTR (&buf), gc);buf_clear (&buf);free_buf (&buf); -CLEAR (line); +secure_memzero (line,sizeof (line));return ret; }@@ -4072,7 +4072,7 @@ read_config_file (struct options *options, {msg (msglevel,"In %s:%d: Maximum recursive include levels exceeded in include attempt of file %s -- probably you have a configuration file that tries to include itself." , top_file, top_line, file); } -CLEAR (line); +secure_memzero (line,sizeof (line));CLEAR (p); }@@ -4104,7 +4104,7 @@ read_config_string (const char *prefix, }CLEAR (p); } -CLEAR (line); +secure_memzero (line,sizeof (line)); }void src/openvpn/ssl.c +16
-16@@ -894,7 +894,7 @@ key_state_free (struct key_state *ks, bool clear)#endif if (clear) -CLEAR (*ks); +secure_memzero (ks,sizeof (*ks)); }/** @} name Functions for initialization and cleanup of key_state structures */ @@ -1024,7 +1024,7 @@ tls_session_free (struct tls_session *session, bool clear)cert_hash_free (session->cert_hash_set);if (clear) -CLEAR (*session); +secure_memzero (session,sizeof (*session)); }/** @} name Functions for initialization and cleanup of tls_session structures */ @@ -1048,7 +1048,7 @@ move_session (struct tls_multi* multi, int dest, int src, bool reinit_src)if (reinit_src)tls_session_init (multi, &multi->session[src]);else -CLEAR (multi->session[src]); +secure_memzero (&multi->session[src],sizeof (multi->session[src]));dmsg (D_TLS_DEBUG ,"TLS: move_session: exit" ); }@@ -1212,7 +1212,7 @@ tls_multi_free (struct tls_multi *multi, bool clear)if (multi->auth_token) { -memset (multi->auth_token,0 ,AUTH_TOKEN_SIZE ); +secure_memzero (multi->auth_token,AUTH_TOKEN_SIZE );free (multi->auth_token); }@@ -1222,7 +1222,7 @@ tls_multi_free (struct tls_multi *multi, bool clear)tls_session_free (&multi->session[i], false);if (clear) -CLEAR (*multi); +secure_memzero (multi,sizeof (*multi));free (multi); }@@ -1512,7 +1512,7 @@ tls1_P_hash(const md_kt_t *md_kt, }hmac_ctx_cleanup (&ctx);hmac_ctx_cleanup (&ctx_tmp); -CLEAR (A1 ); +secure_memzero (A1 ,sizeof (A1 ));dmsg (D_SHOW_KEY_SOURCE ,"tls1_P_hash out: %s" ,format_hex (out_orig, olen_orig,0 , &gc));gc_free (&gc);@@ -1565,7 +1565,7 @@ tls1_PRF(const uint8_t *label,for (i=0 ; i<olen; i++) out1[i]^=out2[i]; -memset (out2,0 , olen); +secure_memzero (out2, olen);dmsg (D_SHOW_KEY_SOURCE ,"tls1_PRF out[%d]: %s" , olen,format_hex (out1, olen,0 , &gc));@@ -1702,8 +1702,8 @@ generate_key_expansion (struct key_ctx_bi *key, ret = true; exit: -CLEAR (master); -CLEAR (key2); +secure_memzero (&master,sizeof (master)); +secure_memzero (&key2,sizeof (key2));return ret; }@@ -1787,7 +1787,7 @@ tls_session_generate_data_channel_keys(struct tls_session *session) ret = true; cleanup: -CLEAR (*ks->key_src); +secure_memzero (ks->key_src,sizeof (*ks->key_src));return ret; }@@ -2017,7 +2017,7 @@ key_method_1_write (struct buffer *buf, struct tls_session *session)init_key_ctx (&ks->crypto_options.key_ctx_bi.encrypt, &key, &session->opt->key_type,OPENVPN_OP_ENCRYPT ,"Data Channel Encrypt" ); -CLEAR (key); +secure_memzero (&key,sizeof (key));/* send local options string */ {@@ -2202,7 +2202,7 @@ key_method_2_write (struct buffer *buf, struct tls_session *session) error:msg (D_TLS_ERRORS ,"TLS Error: Key Method #2 write failed" ); -CLEAR (*ks->key_src); +secure_memzero (ks->key_src,sizeof (*ks->key_src));return false; }@@ -2257,13 +2257,13 @@ key_method_1_read (struct buffer *buf, struct tls_session *session)init_key_ctx (&ks->crypto_options.key_ctx_bi.decrypt, &key, &session->opt->key_type,OPENVPN_OP_DECRYPT ,"Data Channel Decrypt" ); -CLEAR (key); +secure_memzero (&key,sizeof (key)); ks->authenticated = true;return true; error:buf_clear (buf); -CLEAR (key); +secure_memzero (&key,sizeof (key));return false; }@@ -2377,7 +2377,7 @@ key_method_2_read (struct buffer *buf, struct tls_multi *multi, struct tls_sessi }/* clear username and password from memory */ -CLEAR (*up); +secure_memzero (up,sizeof (*up));/* Perform final authentication checks */ if (ks->authenticated)@@ -2433,7 +2433,7 @@ key_method_2_read (struct buffer *buf, struct tls_multi *multi, struct tls_sessireturn true; error: -CLEAR (*ks->key_src); +secure_memzero (ks->key_src,sizeof (*ks->key_src));buf_clear (buf);gc_free (&gc);return false;src/openvpn/ssl_verify.c +2
-2@@ -1176,7 +1176,7 @@ verify_user_pass(struct user_pass *up, struct tls_multi *multi,if (memcmp_constant_time (multi->auth_token, up->password,strlen (multi->auth_token)) !=0 ) { -memset (multi->auth_token,0 ,AUTH_TOKEN_SIZE ); +secure_memzero (multi->auth_token,AUTH_TOKEN_SIZE );free (multi->auth_token); multi->auth_token =NULL ; multi->auth_token_sent = false;@@ -1262,7 +1262,7 @@ verify_user_pass(struct user_pass *up, struct tls_multi *multi, "No auth-token will be activated now");if (multi->auth_token) { -memset (multi->auth_token,0 ,AUTH_TOKEN_SIZE ); +secure_memzero (multi->auth_token,AUTH_TOKEN_SIZE );free (multi->auth_token); multi->auth_token =NULL ; }src/openvpn/ssl_verify_mbedtls.c +1
-3@@ -348,12 +348,10 @@ x509_setenv (struct env_set *es, int cert_depth, mbedtls_x509_crt *cert)int i;unsigned char c;const mbedtls_x509_name *name; -char s[128 ]; +char s[128 ] = {0 }; name = &cert->subject; -memset ( s,0 ,sizeof ( s ) ); -while ( name !=NULL ) {char name_expand[64 +8 ];
Quoted 35 lines · David Sommerseth
Quoted 3 lines · Steffan Karger
As described in trac #751, and shortly after reported by Zhaomo Yang, of the University of California, San Diego, we use
memset()(often through theCLEAR()macro) to erase secrets after use. In some cases however,the
Quoted 5 lines
compiler might optimize these calls away.
This patch replaces these
memset()calls on secrets by calls to a newsecure_memzero()function, that will not be optimized away.Quoted 7 lines
Since we use
CLEAR()a LOT of times, I'm not changing that to usesecure_memzero()to prevent performance impact. I did annotate the macro to point people atsecure_memzero().This patch also replaces some
CLEAR()ormemset()calls with a zero- initialization using "= { 0 }" if that has the same effect, because that better captures the intend of that code.First of all, wonderful that you've looked into an improved
memset()approach. We need that! I am very happy to see these patches and this discussion.But I am very reluctant to this approach here, to be honest. I do not like that we have a two semantics for a overlapping functions; one which always ensures memory is wiped (but might be slower) and the other one which might or might not wipe the memory (depending on the mood of the compiler, etc)
If we "do not care" if a memory region is wiped or not (just that it's nice if it happens), then I'd say these are code paths which *do* *not* require
CLEAR()at all.If we *know* a certain memory region *must* be cleared, then we do the proper call which does what it promises us.
Quoted 7 lines
I think we should not have "do not care" code paths, because that ambiguity gives us nothing but confusing complexity and such code paths bitrots faster than other type of code.
So I'd say that either we move over to
secure_memzero()and yank outCLEAR()all together. Or we yank outCLEAR()calls where we "don't care" and letCLEAR()always do what it tries to promise through its name.
The logic is opposite to what you posit here.
The purpose of secure_memzero is to wipe memory even if its not necessary for logically correct operation of the program. Especially when its not necessary for correct logical operation. but is needed to protect privacy.
Whereas when a memory segment must be cleared for correct logic, use the good old memset and let the compiler optimize it away if/when its smarter than us. Unless we are discussing Bernsteins "Death of optimizing compilers"
SecureZeroMemory on Windows is not a replacement for ZeroMemory nor is memset_s a replacement for memset. We need both.
Selva
Hi,
Quoted 3 lines · David Sommerseth
If we "do not care" if a memory region is wiped or not (just that it's nice if it happens), then I'd say these are code paths which *do* *not* require
CLEAR()at all.
"wiping" and "clearing a structure before using, so everything is well-defined" are two very different things.
Quoted 2 lines
If we *know* a certain memory region *must* be cleared, then we do the proper call which does what it promises us.
CLEAR() will also do what it promises: that the block in question is zeroed *for subsequent use*.
What it does not promise is "if we never use it again, it is cleared nevertheless" - that's what secure_memzero() is for.
One thing is "initialization before use", one is "scrubbing so secret material doesn't leak *after* use".
Different requirements, different functions. Reasonable approach.
gert
Signature · 4 lines
USENET is *not* the non-clickable part of WWW!
//www.muc.de/~gert/
Gert Doering - Munich, Germany [email protected]...
fax: +49-89-35655025 [email protected]...Hi,
Quoted 20 lines · Steffan Karger
As described in trac #751, and shortly after reported by Zhaomo Yang, of the University of California, San Diego, we use
memset()(often through theCLEAR()macro) to erase secrets after use. In some cases however, the compiler might optimize these calls away.This patch replaces these
memset()calls on secrets by calls to a newsecure_memzero()function, that will not be optimized away.Since we use
CLEAR()a LOT of times, I'm not changing that to usesecure_memzero()to prevent performance impact. I did annotate the macro to point people atsecure_memzero().This patch also replaces some
CLEAR()ormemset()calls with a zero- initialization using "= { 0 }" if that has the same effect, because that better captures the intend of that code.Signed-off-by Steffan Karger <[email protected]...>---
v2: replace more memset()/CLEAR() calls withsecure_memzero()
Quoted 13 lines
src/openvpn/basic.h | 2 +- src/openvpn/buffer.c | 8 ++++---- src/openvpn/buffer.h | 43 ++++++++++++++++++++++++++++++ ++++++++++ src/openvpn/console_builtin.c | 2 +- src/openvpn/crypto.c | 17 ++++++---------- src/openvpn/manage.c | 2 +- src/openvpn/misc.c | 4 ++-- src/openvpn/options.c | 6 +++--- src/openvpn/ssl.c | 32 +++++++++++++++--------------- src/openvpn/ssl_verify.c | 4 ++-- src/openvpn/ssl_verify_mbedtls.c | 4 +--- 11 files changed, 80 insertions(+), 44 deletions(-)
Took a second look at v2: sizes of data structs/buffers correctly passed to secure_memset and no new warnings with gcc -Wall :)
ACK.
Selva
Another ACK from me.
(David agreed on IRC that even while he is not happy with CLEAR(), it's ok to go forward with this one as it's a somewhat independent issue)
Your patch has been applied to the master branch.
commit 009521ac8ae613084b23b9e3e5dc4ebeccd4c6c8
Author: Steffan Karger Date: Mon Nov 28 23:14:12 2016 +0100
Introduce and use secure_memzero() to erase secrets
Signed-off-by: Steffan Karger <[email protected]...>
Acked-by: Selva Nair <[email protected]...>
Acked-by: Gert Doering <[email protected]...>
Message-Id: <[email protected]...>
URL: https://www.mail-archive.com/[email protected]/msg13278.html
Signed-off-by: Gert Doering <[email protected]...>--
kind regards,
Gert Doering