solanum-ircd / solanum

An IRCd for unified networks
https://solanum.chat/
GNU General Public License v2.0
224 stars 54 forks source link

Message sending is not always blocked through `$j` extban #426

Open JustAnotherArchivist opened 8 months ago

JustAnotherArchivist commented 8 months ago

Setup: #channel has a ban on $j:#bans. A user with nickname Eve is already in #channel, then a ban for Eve!*@* is applied in #bans.

Expected behaviour: Once the ban is in place, Eve cannot send messages (PRIVMSG or NOTICE) to #channel anymore and gets an ERR_CANNOTSENDTOCHAN instead.

Actual behaviour: If Eve had already sent a message to the channel before the ban was added, messages are not blocked until the ban list of #channel is modified.

The same behaviour is also observed if #channel has +q $j:#bans rather than +b. Based on my reading of the code, +e might also be affected, though I haven't tested this.

Transcript of a reproduction against Solanum commit 1ccc6422. Each protocol line is prefixed with the originating user (Bob for the channel admin, Eve as above) and the direction (> for lines sent by the client, < for received lines). I've omitted the obvious replies (001, join and mode confirmation, etc.).

Bob > JOIN #channel,#bans
Bob > MODE #channel +b $j:#bans
Eve > JOIN #channel
Eve > PRIVMSG #channel hello
Bob < :Eve!~Eve@hostname PRIVMSG #channel :hello
Bob > MODE #bans +b Eve!*@*
Eve > PRIVMSG #channel :this shouldn't be sent
Bob < :Eve!~Eve@hostname PRIVMSG #channel :this shouldn't be sent
Bob > MODE #channel +b foo!*@*
Eve > PRIVMSG #channel :another attempt after the banlist is modified with an unrelated mask
Eve < :server 404 Eve #channel :Cannot send to nick/channel

I found this last night while fighting a spammer, thinking a single ban in the central channel should quiet them globally (with $j bans everywhere else already in place). It appears that that's currently impossible if they've already sent messages; instead, you have to do something to each channel they're in, e.g. apply any ban/quiet there or kick them.

I suspect that this is due to the caching in can_send. Upon adding a ban, the bants cache needs to be invalidated not only in that channel but also in any channel that embeds its ban list via $j extbans. The latter part is what appears to be missing.

jesopo commented 7 months ago

for what it's worth, the reason this happens is "is this user banned in this channel" is a cached decision and the cache is only updated in a set number of occasions. setting a ban on a $j: downstream is not one of those occasions