CVE-2020-9433 (published 2020-02-27T23:15Z)

openssl_x509_check_email in lua-openssl 0.7.7-1 mishandles X.509 certificate validation because it uses lua_pushboolean for certain non-boolean return values.

Severity: MEDIUM
CVSS v2 base score: 6.4 (AV:N/AC:L/Au:N/C:P/I:P/A:N)
CVSS v3 base score: 9.1, CRITICAL (CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:N)

Weakness information:
cwe_id: CWE-295
cwe_name: Improper Certificate Validation
description: The product does not validate, or incorrectly validates, a certificate.
extended_description: When a certificate is invalid or malicious, it might allow an attacker to spoof a trusted entity by interfering in the communication path between the host and client. The product might connect to a malicious host while believing it is a trusted host, or the product might be deceived into accepting spoofed data that appears to originate from a trusted host.
         

Fix information:
repo_url: https://github.com/zhaozg/lua-openssl
commit_details: repo_url: https://github.com/zhaozg/lua-openssl
author: zhaozg
author_date: 2020-02-27 21:53:15+08:00
msg: fix security: GSL-2020-026
repository: repo_url: https://github.com/zhaozg/lua-openssl
repo_name: zhaozg/lua-openssl
description: Openssl binding for Lua
repo_language: C
owner: zhaozg
file_changes: filename: x509.c
change_type: ModificationType.MODIFY
diff: @@ -751,6 +751,46 @@ static LUA_FUNCTION(openssl_x509_check)
   }
 }
 
+/***
+The functions return 1 for a successful match, 0 for a failed match and -1 for
+an internal error: typically a memory allocation failure or an ASN.1 decoding
+error.
+
+All functions can also return -2 if the input is malformed. For example,
+X509_check_host() returns -2 if the provided name contains embedded NULs.
+*/
+static int openssl_push_check_result(lua_State *L, int ret, const char* name)
+{
+  switch (ret)
+  {
+  case 1:
+    lua_pushboolean(L, 1);
+    if (name)
+    {
+      lua_pushstring(L, name);
+      ret = 2;
+    }
+    break;
+  case 0:
+    lua_pushboolean(L, 0);
+    ret = 1;
+    break;
+  case -1:
+    lua_pushnil(L);
+    lua_pushliteral(L, "internal");
+    ret = 2;
+  case -2:
+    lua_pushnil(L);
+    lua_pushliteral(L, "malformed");
+    ret = 2;
+  default:
+    lua_pushnil(L);
+    lua_pushinteger(L, ret);
+    ret = 2;
+  }
+  return ret;
+}
+
 #if OPENSSL_VERSION_NUMBER > 0x10002000L
 /***
 check x509 for host (only for openssl 1.0.2 or greater)
@@ -761,17 +801,17 @@ check x509 for host (only for openssl 1.0.2 or greater)
 static LUA_FUNCTION(openssl_x509_check_host)
 {
   X509 * cert = CHECK_OBJECT(1, X509, "openssl.x509");
-  if (lua_isstring(L, 2))
-  {
-    const char *hostname = lua_tostring(L, 2);
-    lua_pushboolean(L, X509_check_host(cert, hostname, strlen(hostname), 0, NULL));
-  }
-  else
-  {
-    lua_pushboolean(L, 0);
-  }
-  return 1;
+  size_t sz;
+  const char* hostname = luaL_checklstring(L, 2, &sz);
+  int flags = luaL_optint(L, 3, 0);
+  char *peer = NULL;
+
+  int ret = X509_check_host(cert, hostname, sz, flags, &peer);
+  ret = openssl_push_check_result(L, ret, peer);
+  OPENSSL_free(peer);
+  return ret;
 }
+
 /***
 check x509 for email address (only for openssl 1.0.2 or greater)
 @tparam string email to check for match match with x509 subject
@@ -781,16 +821,11 @@ check x509 for email address (only for openssl 1.0.2 or greater)
 static LUA_FUNCTION(openssl_x509_check_email)
 {
   X509 * cert = CHECK_OBJECT(1, X509, "openssl.x509");
-  if (lua_isstring(L, 2))
-  {
-    const char *email = lua_tostring(L, 2);
-    lua_pushboolean(L, X509_check_email(cert, email, strlen(email), 0));
-  }
-  else
-  {
-    lua_pushboolean(L, 0);
-  }
-  return 1;
+  size_t sz;
+  const char *email = luaL_checklstring(L, 2, &sz);
+  int flags = luaL_optint(L, 3, 0);
+  int ret = X509_check_email(cert, email, sz, flags);
+  return openssl_push_check_result(L, ret, NULL);
 }
 
 /***
@@ -799,19 +834,14 @@ check x509 for ip address (ipv4 or ipv6, only for openssl 1.0.2 or greater)
 @tparam string ip to check for match match with x509 subject
 @treturn boolean result true if host is present and matches the certificate
 */
-static LUA_FUNCTION(openssl_x509_check_ip_asc)
+static LUA_FUNCTION(openssl_x509_check_ip)
 {
   X509 * cert = CHECK_OBJECT(1, X509, "openssl.x509");
-  if (lua_isstring(L, 2))
-  {
-    const char *ip_asc = lua_tostring(L, 2);
-    lua_pushboolean(L, X509_check_ip_asc(cert, ip_asc, 0));
-  }
-  else
-  {
-    lua_pushboolean(L, 0);
-  }
-  return 1;
+  size_t sz;
+  const char *ip = luaL_checklstring(L, 2, &sz);
+  int flags = luaL_optint(L, 3, 0);
+  int ret = X509_check_ip(cert, (const unsigned char*)ip, sz, flags);
+  return openssl_push_check_result(L, ret, NULL);
 }
 #endif
 
@@ -1360,7 +1390,7 @@ static luaL_Reg x509_funcs[] =
 #if OPENSSL_VERSION_NUMBER > 0x10002000L
   {"check_host",  openssl_x509_check_host},
   {"check_email", openssl_x509_check_email},
-  {"check_ip_asc", openssl_x509_check_ip_asc},
+  {"check_ip_asc", openssl_x509_check_ip},
 #endif
   {"pubkey",      openssl_x509_public_key},
   {"version",     openssl_x509_version},
@@ -1384,6 +1414,25 @@ static luaL_Reg x509_funcs[] =
   {NULL,      NULL},
 };
 
+#if OPENSSL_VERSION_NUMBER > 0x10002000L
+static LuaL_Enumeration check_flags_const[] =
+{
+#define DEFINE_ENUM(x)  \
+  {#x,  X509_CHECK_FLAG_##x}
+  DEFINE_ENUM(ALWAYS_CHECK_SUBJECT),
+#if OPENSSL_VERSION_NUMBER > 0x10100000L
+  DEFINE_ENUM(NEVER_CHECK_SUBJECT),
+#endif
+  DEFINE_ENUM(NO_WILDCARDS),
+  DEFINE_ENUM(NO_PARTIAL_WILDCARDS),
+  DEFINE_ENUM(MULTI_LABEL_WILDCARDS),
+  DEFINE_ENUM(SINGLE_LABEL_SUBDOMAINS),
+#undef DEFINE_ENUM
+
+  {NULL,           0}
+};
+#endif
+
 int luaopen_x509(lua_State *L)
 {
   auxiliar_newclass(L, "openssl.x509", x509_funcs);
@@ -1411,5 +1460,12 @@ int luaopen_x509(lua_State *L)
   lua_pushliteral(L, MYVERSION);
   lua_settable(L, -3);
 
+#if OPENSSL_VERSION_NUMBER > 0x10002000L
+  lua_pushliteral(L, "check_flag");
+  lua_newtable(L);
+  auxiliar_enumerate(L, -1, check_flags_const);
+  lua_settable(L, -3);
+#endif
+
   return 1;
 }

programming_language: C