Hello ports@,

Some time ago I hit an issue where my prosody server randomly started to
hang on the netio channel. After some debugging I found out that luasec
puts the underlying socket in blocking mode before calling SSL_shutdown.
This combined with a crappy client who is on the disconnect path can
result in the server not being able to send out the shutdown
notification, and waiting until the underlying TCP layer gives up.

The diffs below is a backport from what is aleady accepted upstream by
prosody[0], and the luasec diff as forwarded to the luasec project via
prosody[1].

Since OpenBSD appears to be disproportionately affected by issue, from
what I've been told by the prosody people, it might be a good idea to
ship these changes as local patches until both upstreams makes the
new releases.

martijn@

[0] https://hg.prosody.im/trunk/rev/a08065207ef0
[1] https://github.com/lunarmodules/luasec/pull/203/files

diff /usr/ports
path + /usr/ports
commit - bfce4c31b71ca3f1fbcd26b4c1c97d12bb453e72
blob - 9c2b8e2f66fcd4d47cb882e8cc8f95fd5cc1a410
file + net/prosody/Makefile
--- net/prosody/Makefile
+++ net/prosody/Makefile
@@ -1,5 +1,6 @@
 COMMENT =      communications server for Jabber/XMPP written in Lua
 DISTNAME =     prosody-0.12.5
+REVISION =     0
 CATEGORIES =   net
 HOMEPAGE =     https://prosody.im/
 
commit - bfce4c31b71ca3f1fbcd26b4c1c97d12bb453e72
blob - /dev/null
file + net/prosody/patches/patch-net_server_epoll_lua (mode 644)
--- /dev/null
+++ net/prosody/patches/patch-net_server_epoll_lua
@@ -0,0 +1,45 @@
+Index: net/server_epoll.lua
+--- net/server_epoll.lua.orig
++++ net/server_epoll.lua
+@@ -576,6 +576,7 @@ interface.send = interface.write;
+ 
+ -- Close, possibly after writing is done
+ function interface:close()
++      local status, err;
+       if self._connected and self.writebuffer and (self.writebuffer[1] or 
type(self.writebuffer) == "string") then
+               self._connected = false;
+               self:set(false, true); -- Flush final buffer contents
+@@ -584,6 +585,24 @@ function interface:close()
+               self.write, self.send = noop, noop; -- No more writing
+               self:debug("Close after writing remaining buffered data");
+               self.ondrain = interface.close;
++      elseif self.conn.shutdown and self._tls then
++              status, err = self.conn:shutdown();
++              self.onreadable = interface.close;
++              self.onwritable = interface.close;
++              if err == nil then
++                      if status == true then
++                              self._tls = false;
++                      end
++                      return self:close();
++              elseif err == "wantread" then
++                      self:set(true, nil);
++                      self:setreadtimeout();
++              elseif err == "wantwrite" then
++                      self:set(nil, true);
++                      self:setwritetimeout();
++              else
++                      self._tls = false;
++              end
+       else
+               self:debug("Closing now");
+               self.write, self.send = noop, noop;
+@@ -594,6 +613,8 @@ function interface:close()
+ end
+ 
+ function interface:destroy()
++      -- make sure tls sockets aren't put in blocking mode
++      if self.conn.shutdown and self._tls then self.conn:shutdown(); end
+       self:del();
+       self:setwritetimeout(false);
+       self:setreadtimeout(false);
commit - bfce4c31b71ca3f1fbcd26b4c1c97d12bb453e72
blob - 98f317ac9f1568932b9ce2de581924d3e193c310
file + security/luasec/Makefile
--- security/luasec/Makefile
+++ security/luasec/Makefile
@@ -3,6 +3,7 @@ COMMENT=        lua binding to OpenSSL to provide TLS/SSL com
 GH_ACCOUNT=    brunoos
 GH_PROJECT=    luasec
 GH_TAGNAME=    v1.2.0
+REVISION=      0
 
 CATEGORIES=    security
 
commit - bfce4c31b71ca3f1fbcd26b4c1c97d12bb453e72
blob - /dev/null
file + security/luasec/patches/patch-src_luasocket_usocket_c (mode 644)
--- /dev/null
+++ security/luasec/patches/patch-src_luasocket_usocket_c
@@ -0,0 +1,11 @@
+Index: src/luasocket/usocket.c
+--- src/luasocket/usocket.c.orig
++++ src/luasocket/usocket.c
+@@ -82,7 +82,6 @@ int socket_close(void) {
+ \*-------------------------------------------------------------------------*/
+ void socket_destroy(p_socket ps) {
+     if (*ps != SOCKET_INVALID) {
+-        socket_setblocking(ps);
+         close(*ps);
+         *ps = SOCKET_INVALID;
+     }
commit - bfce4c31b71ca3f1fbcd26b4c1c97d12bb453e72
blob - /dev/null
file + security/luasec/patches/patch-src_ssl_c (mode 644)
--- /dev/null
+++ security/luasec/patches/patch-src_ssl_c
@@ -0,0 +1,86 @@
+Index: src/ssl.c
+--- src/ssl.c.orig
++++ src/ssl.c
+@@ -87,7 +87,8 @@ static int meth_destroy(lua_State *L)
+ {
+   p_ssl ssl = (p_ssl)luaL_checkudata(L, 1, "SSL:Connection");
+   if (ssl->state == LSEC_STATE_CONNECTED) {
+-    socket_setblocking(&ssl->sock);
++    if (!ssl->shutdown)
++      socket_setblocking(&ssl->sock);
+     SSL_shutdown(ssl->ssl);
+   }
+   if (ssl->sock != SOCKET_INVALID) {
+@@ -305,6 +306,7 @@ static int meth_create(lua_State *L)
+     return luaL_argerror(L, 1, "invalid context");
+   }
+   ssl->state = LSEC_STATE_NEW;
++  ssl->shutdown = 0;
+   SSL_set_fd(ssl->ssl, (int)SOCKET_INVALID);
+   SSL_set_mode(ssl->ssl, SSL_MODE_ENABLE_PARTIAL_WRITE | 
+     SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER);
+@@ -341,6 +343,56 @@ static int meth_receive(lua_State *L) {
+ }
+ 
+ /**
++ * SSL shutdown function
++ */
++static int meth_shutdown(lua_State *L) {
++  p_ssl ssl = (p_ssl)luaL_checkudata(L, 1, "SSL:Connection");
++  int err;
++  p_timeout tm = timeout_markstart(&ssl->tm);
++
++  ssl->shutdown = 1;
++  if (ssl->state == LSEC_STATE_CLOSED) {
++    lua_pushboolean(L, 1);
++    return 1;
++  }
++
++  err = SSL_shutdown(ssl->ssl);
++  switch (err) {
++  case 0:
++    lua_pushboolean(L, 0);
++    lua_pushnil(L);
++    return 2;
++  case 1:
++    lua_pushboolean(L, 1);
++    lua_pushnil(L);
++    ssl->state = LSEC_STATE_CLOSED;
++    return 2;
++  case -1:
++    lua_pushboolean(L, 0);
++    ssl->error = SSL_get_error(ssl->ssl, err);
++    switch (ssl->error) {
++    case SSL_ERROR_WANT_READ:
++      err = socket_waitfd(&ssl->sock, WAITFD_R, tm);
++      lua_pushstring(L, ssl_ioerror((void *)ssl, err == IO_TIMEOUT ? 
LSEC_IO_SSL : err));
++      break;
++    case SSL_ERROR_WANT_WRITE:
++      err = socket_waitfd(&ssl->sock, WAITFD_W, tm);
++      lua_pushstring(L, ssl_ioerror((void *)ssl, err == IO_TIMEOUT ? 
LSEC_IO_SSL : err));
++      break;
++    case SSL_ERROR_SYSCALL:
++      if (ERR_peek_error())
++        ssl->error = SSL_ERROR_SSL;
++      lua_pushstring(L, ssl_ioerror((void *)ssl, LSEC_IO_SSL));
++      break;
++    default:
++      lua_pushstring(L, ssl_ioerror((void *)ssl, LSEC_IO_SSL));
++      break;
++    }
++    return 2;
++  }
++}
++
++/**
+  * Get the buffer's statistics.
+  */
+ static int meth_getstats(lua_State *L) {
+@@ -904,6 +956,7 @@ static int meth_tlsa(lua_State *L)
+  */
+ static luaL_Reg methods[] = {
+   {"close",               meth_close},
++  {"shutdown",            meth_shutdown},
+   {"getalpn",             meth_getalpn},
+   {"getfd",               meth_getfd},
+   {"getfinished",         meth_getfinished},
commit - bfce4c31b71ca3f1fbcd26b4c1c97d12bb453e72
blob - /dev/null
file + security/luasec/patches/patch-src_ssl_h (mode 644)
--- /dev/null
+++ security/luasec/patches/patch-src_ssl_h
@@ -0,0 +1,11 @@
+Index: src/ssl.h
+--- src/ssl.h.orig
++++ src/ssl.h
+@@ -32,6 +32,7 @@ typedef struct t_ssl_ {
+   t_timeout tm;
+   SSL *ssl;
+   int state;
++  int shutdown;
+   int error;
+ } t_ssl;
+ typedef t_ssl* p_ssl;

Reply via email to