Commit message: Use constant time memcmp when comparing HMACs in openvpn_decrypt.

Signed-off-by: Steffan Karger <steffan.karger@fox-it.com>
Acked-by: Gert Doering <gert@greenie.muc.de>
Signed-off-by: Gert Doering <gert@greenie.muc.de>
CVE: CVE-2013-2061
CWE: CWE-200
Vulnerability description: The openvpn_decrypt function in crypto.c in OpenVPN 2.3.0 and earlier, when running in UDP mode, allows remote attackers to obtain sensitive information via a timing attack involving an HMAC comparison function that does not run in constant time and a padding oracle attack on the CBC mode cipher.

Project: openvpn
File: crypto.c

ascii2keydirection (int msglevel, const char *str)
{
  if (!str)
    return KEY_DIRECTION_BIDIRECTIONAL;
  else if (!strcmp (str, "0"))
    return KEY_DIRECTION_NORMAL;
  else if (!strcmp (str, "1"))
    return KEY_DIRECTION_INVERSE;
  else
    {
      msg (msglevel, "Unknown key direction '%s' -- must be '0' or '1'", str);
      return -1;
    }
  return KEY_DIRECTION_BIDIRECTIONAL; /* NOTREACHED */
}

Project: openvpn
File: crypto.c

init_key_ctx (struct key_ctx *ctx, struct key *key,
	      const struct key_type *kt, int enc,
	      const char *prefix)
{
  struct gc_arena gc = gc_new ();
  CLEAR (*ctx);
  if (kt->cipher && kt->cipher_length > 0)
    {

      ALLOC_OBJ(ctx->cipher, cipher_ctx_t);
      cipher_ctx_init (ctx->cipher, key->cipher, kt->cipher_length,
	  kt->cipher, enc);

      msg (D_HANDSHAKE, "%s: Cipher '%s' initialized with %d bit key",
          prefix,
          cipher_kt_name(kt->cipher),
          kt->cipher_length *8);

      dmsg (D_SHOW_KEYS, "%s: CIPHER KEY: %s", prefix,
          format_hex (key->cipher, kt->cipher_length, 0, &gc));
      dmsg (D_CRYPTO_DEBUG, "%s: CIPHER block_size=%d iv_size=%d",
          prefix,
          cipher_kt_block_size(kt->cipher),
          cipher_kt_iv_size(kt->cipher));
    }
  if (kt->digest && kt->hmac_length > 0)
    {
      ALLOC_OBJ(ctx->hmac, hmac_ctx_t);
      hmac_ctx_init (ctx->hmac, key->hmac, kt->hmac_length, kt->digest);

      msg (D_HANDSHAKE,
      "%s: Using %d bit message hash '%s' for HMAC authentication",
      prefix, md_kt_size(kt->digest) * 8, md_kt_name(kt->digest));

      dmsg (D_SHOW_KEYS, "%s: HMAC KEY: %s", prefix,
	  format_hex (key->hmac, kt->hmac_length, 0, &gc));

      dmsg (D_CRYPTO_DEBUG, "%s: HMAC size=%d block_size=%d",
	prefix,
	md_kt_size(kt->digest),
	hmac_ctx_size(ctx->hmac));

    }
  gc_free (&gc);
}

Project: openvpn
File: crypto.c

init_key_type (struct key_type *kt, const char *ciphername,
	       bool ciphername_defined, const char *authname,
	       bool authname_defined, int keysize,
	       bool cfb_ofb_allowed, bool warn)
{
  CLEAR (*kt);
  if (ciphername && ciphername_defined)
    {
      kt->cipher = cipher_kt_get (translate_cipher_name_from_openvpn(ciphername));
      kt->cipher_length = cipher_kt_key_size (kt->cipher);
      if (keysize > 0 && keysize <= MAX_CIPHER_KEY_LENGTH)
	kt->cipher_length = keysize;

      /* check legal cipher mode */
      {
	const unsigned int mode = cipher_kt_mode (kt->cipher);
	if (!(mode == OPENVPN_MODE_CBC
#ifdef ALLOW_NON_CBC_CIPHERS
	      || (cfb_ofb_allowed && (mode == OPENVPN_MODE_CFB || mode == OPENVPN_MODE_OFB))
#endif
	      ))
#ifdef ENABLE_SMALL
	  msg (M_FATAL, "Cipher '%s' mode not supported", ciphername);
#else
	  msg (M_FATAL, "Cipher '%s' uses a mode not supported by " PACKAGE_NAME " in your current configuration.  CBC mode is always supported, while CFB and OFB modes are supported only when using SSL/TLS authentication and key exchange mode, and when " PACKAGE_NAME " has been built with ALLOW_NON_CBC_CIPHERS.", ciphername);
#endif
      }
    }
  else
    {
      if (warn)
	msg (M_WARN, "******* WARNING *******: null cipher specified, no encryption will be used");
    }
  if (authname && authname_defined)
    {
      kt->digest = md_kt_get (authname);
      kt->hmac_length = md_kt_size (kt->digest);
    }
  else
    {
      if (warn)
	msg (M_WARN, "******* WARNING *******: null MAC specified, no authentication will be used");
    }
}

Project: openvpn
File: crypto.c

md5_state_update (struct md5_state *s, void *data, size_t len)
{
  md_ctx_update(&s->ctx, data, len);
}

Project: openvpn
File: crypto.c

md5sum (uint8_t *buf, int len, int n_print_chars, struct gc_arena *gc)
{
  uint8_t digest[MD5_DIGEST_LENGTH];
  const md_kt_t *md5_kt = md_kt_get("MD5");

  md_full(md5_kt, buf, len, digest);

  return format_hex (digest, MD5_DIGEST_LENGTH, n_print_chars, gc);
}

Project: openvpn
File: crypto.c

read_key (struct key *key, const struct key_type *kt, struct buffer *buf)
{
  uint8_t cipher_length;
  uint8_t hmac_length;

  CLEAR (*key);
  if (!buf_read (buf, &cipher_length, 1))
    goto read_err;
  if (!buf_read (buf, &hmac_length, 1))
    goto read_err;

  if (!buf_read (buf, key->cipher, cipher_length))
    goto read_err;
  if (!buf_read (buf, key->hmac, hmac_length))
    goto read_err;

  if (cipher_length != kt->cipher_length || hmac_length != kt->hmac_length)
    goto key_len_err;

  return 1;

read_err:
  msg (D_TLS_ERRORS, "TLS Error: error reading key from remote");
  return -1;

key_len_err:
  msg (D_TLS_ERRORS,
       "TLS Error: key length mismatch, local cipher/hmac %d/%d, remote cipher/hmac %d/%d",
       kt->cipher_length, kt->hmac_length, cipher_length, hmac_length);
  return 0;
}