This is a cherry-pick of commit
009521ac (master).
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.
Signed-off-by: Steffan Karger <steffan@karger.me>
Acked-by: David Sommerseth <davids@openvpn.net>
Message-Id: <
1494449775-22199-1-git-send-email-steffan@karger.me>
URL: https://www.mail-archive.com/openvpn-devel@lists.sourceforge.net/msg14628.html
Signed-off-by: David Sommerseth <davids@openvpn.net>
/* 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
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;
}
{
if (str)
{
- const int len = strlen (str);
- if (len > 0)
- memset (str, 0, len);
+ secure_memzero (str, strlen (str));
}
}
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
/* 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;
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);
ASSERT (opt->flags & CO_USE_IV); /* IV and packet-ID required */
ASSERT (opt->packet_id); /* for this mode. */
- memset (iv_buf, 0, iv_size);
buf_set_write (&b, iv_buf, iv_size);
ASSERT (packet_id_write (&opt->packet_id->send, &b, true, false));
}
{
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)
init_key_ctx (&ctx->decrypt, &key2.keys[kds.in_key], &kt, OPENVPN_OP_DECRYPT,
"Incoming Control Channel Authentication");
- CLEAR (key2);
+ secure_memzero (&key2, sizeof (key2));
}
else
{
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);
&c->c1.ks.key_type, OPENVPN_OP_DECRYPT, "Static Decrypt");
/* Erase the temporary copy of key */
- CLEAR (key2);
+ secure_memzero (&key2, sizeof(key2));
}
else
{
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);
static bool warn_shown = false;
if (nocache || force)
{
- CLEAR (*up);
+ secure_memzero (up, sizeof(*up));
up->nocache = nocache;
}
else if (!warn_shown)
ret = string_alloc (BSTR (&buf), gc);
buf_clear (&buf);
free_buf (&buf);
- CLEAR (line);
+ secure_memzero (line, sizeof (line));
return ret;
}
{
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);
}
}
CLEAR (p);
}
- CLEAR (line);
+ secure_memzero (line, sizeof (line));
}
void
}
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);
const struct session_id *server_sid,
bool server)
{
- uint8_t master[48];
- struct key2 key2;
+ uint8_t master[48] = { 0 };
+ struct key2 key2 = { 0 };
bool ret = false;
int i;
- CLEAR (master);
- CLEAR (key2);
-
/* debugging print of source key material */
key_source2_print (key_src);
ret = true;
exit:
- CLEAR (master);
- CLEAR (key2);
+ secure_memzero (&master, sizeof (master));
+ secure_memzero (&key2, sizeof (key2));
return ret;
}
init_key_ctx (&ks->key.encrypt, &key, &session->opt->key_type,
OPENVPN_OP_ENCRYPT, "Data Channel Encrypt");
- CLEAR (key);
+ secure_memzero (&key, sizeof (key));
/* send local options string */
{
}
}
- CLEAR (*ks->key_src);
+ secure_memzero (ks->key_src, sizeof (*ks->key_src));
tls_limit_reneg_bytes (session->opt->key_type.cipher,
&session->opt->renegotiate_bytes);
}
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;
}
init_key_ctx (&ks->key.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;
}
if (!username_status || !password_status)
{
- CLEAR (*up);
+ secure_memzero (up, sizeof(*up));
if (!(session->opt->ssl_flags & SSLF_AUTH_USER_PASS_OPTIONAL))
{
msg (D_TLS_ERRORS, "TLS Error: Auth Username/Password was not provided by peer");
#endif
verify_user_pass(up, multi, session);
- CLEAR (*up);
+ secure_memzero (up, sizeof (*up));
}
else
{
goto error;
}
- CLEAR (*ks->key_src);
+ secure_memzero (ks->key_src, sizeof (*ks->key_src));
}
gc_free (&gc);
return true;
error:
- CLEAR (*ks->key_src);
+ secure_memzero (ks->key_src, sizeof (*ks->key_src));
buf_clear (buf);
gc_free (&gc);
return false;