On Thu, Feb 13, 2025 at 05:17:36PM +0100, Martijn van Duren wrote:
> 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.

Well, if you want tests, get it in...

> 
> 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