diff --git a/conventional.yaml b/conventional.yaml index e3d977e4..a6075227 100644 --- a/conventional.yaml +++ b/conventional.yaml @@ -553,6 +553,7 @@ oper-classes: - "vhosts" - "chanreg" - "history" + - "defcon" # ircd operators opers: diff --git a/default.yaml b/default.yaml index 259737c1..4bff5efc 100644 --- a/default.yaml +++ b/default.yaml @@ -579,6 +579,7 @@ oper-classes: - "vhosts" - "chanreg" - "history" + - "defcon" # ircd operators opers: diff --git a/irc/accounts.go b/irc/accounts.go index 40a40022..0ff8c4af 100644 --- a/irc/accounts.go +++ b/irc/accounts.go @@ -380,8 +380,8 @@ func (am *AccountManager) Register(client *Client, account string, callbackNames config := am.server.Config() - // final "is registration allowed" check, probably redundant: - if !(config.Accounts.Registration.Enabled || callbackNamespace == "admin") { + // final "is registration allowed" check: + if !(config.Accounts.Registration.Enabled || callbackNamespace == "admin") || am.server.Defcon() <= 4 { return errFeatureDisabled } @@ -1642,6 +1642,11 @@ func (am *AccountManager) performVHostChange(account string, munger vhostMunger) return } + if am.server.Defcon() <= 3 { + err = errFeatureDisabled + return + } + am.vHostUpdateMutex.Lock() defer am.vHostUpdateMutex.Unlock() diff --git a/irc/channel.go b/irc/channel.go index 960b1a04..4e1aee85 100644 --- a/irc/channel.go +++ b/irc/channel.go @@ -706,7 +706,8 @@ func (channel *Channel) Join(client *Client, key string, isSajoin bool, rb *Resp return errBanned } - if channel.flags.HasMode(modes.RegisteredOnly) && details.account == "" { + if details.account == "" && + (channel.flags.HasMode(modes.RegisteredOnly) || channel.server.Defcon() <= 2) { return errRegisteredOnly } } diff --git a/irc/channelmanager.go b/irc/channelmanager.go index fce4b0b6..19430d41 100644 --- a/irc/channelmanager.go +++ b/irc/channelmanager.go @@ -187,6 +187,10 @@ func (cm *ChannelManager) Cleanup(channel *Channel) { } func (cm *ChannelManager) SetRegistered(channelName string, account string) (err error) { + if cm.server.Defcon() <= 4 { + return errFeatureDisabled + } + var channel *Channel cfname, err := CasefoldChannel(channelName) if err != nil { diff --git a/irc/client.go b/irc/client.go index 9f772ca3..ea0fd68a 100644 --- a/irc/client.go +++ b/irc/client.go @@ -529,7 +529,7 @@ const ( authFailSaslRequired ) -func (client *Client) isAuthorized(config *Config, session *Session) AuthOutcome { +func (client *Client) isAuthorized(server *Server, config *Config, session *Session) AuthOutcome { saslSent := client.account != "" // PASS requirement if (config.Server.passwordBytes != nil) && session.passStatus != serverPassSuccessful && !(config.Accounts.SkipServerPassword && saslSent) { @@ -540,7 +540,8 @@ func (client *Client) isAuthorized(config *Config, session *Session) AuthOutcome return authFailTorSaslRequired } // finally, enforce require-sasl - if config.Accounts.RequireSasl.Enabled && !saslSent && !utils.IPInNets(session.IP(), config.Accounts.RequireSasl.exemptedNets) { + if !saslSent && (config.Accounts.RequireSasl.Enabled || server.Defcon() <= 2) && + !utils.IPInNets(session.IP(), config.Accounts.RequireSasl.exemptedNets) { return authFailSaslRequired } return authSuccess diff --git a/irc/commands.go b/irc/commands.go index bfe0a7dd..62a7f90d 100644 --- a/irc/commands.go +++ b/irc/commands.go @@ -124,6 +124,10 @@ func init() { minParams: 1, oper: true, }, + "DEFCON": { + handler: defconHandler, + capabs: []string{"defcon"}, + }, "DEOPER": { handler: deoperHandler, minParams: 0, diff --git a/irc/getters.go b/irc/getters.go index c7f207f0..9de62fa9 100644 --- a/irc/getters.go +++ b/irc/getters.go @@ -37,6 +37,14 @@ func (server *Server) Languages() (lm *languages.Manager) { return server.Config().languageManager } +func (server *Server) Defcon() uint32 { + return atomic.LoadUint32(&server.defcon) +} + +func (server *Server) SetDefcon(defcon uint32) { + atomic.StoreUint32(&server.defcon, defcon) +} + func (client *Client) Sessions() (sessions []*Session) { client.stateMutex.RLock() sessions = client.sessions diff --git a/irc/handlers.go b/irc/handlers.go index eaa83e27..6b57fb9e 100644 --- a/irc/handlers.go +++ b/irc/handlers.go @@ -759,6 +759,21 @@ func debugHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *Res return false } +func defconHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *ResponseBuffer) bool { + if len(msg.Params) > 0 { + level, err := strconv.Atoi(msg.Params[0]) + if err == nil && 1 <= level && level <= 5 { + server.SetDefcon(uint32(level)) + server.snomasks.Send(sno.LocalAnnouncements, fmt.Sprintf("%s [%s] set DEFCON level to %d", client.Nick(), client.Oper().Name, level)) + } else { + rb.Add(nil, server.name, ERR_UNKNOWNERROR, client.Nick(), msg.Command, client.t("Invalid DEFCON parameter")) + return false + } + } + rb.Notice(fmt.Sprintf(client.t("Current DEFCON level is %d"), server.Defcon())) + return false +} + // helper for parsing the reason args to DLINE and KLINE func getReasonsFromParams(params []string, currentArg int) (reason, operReason string) { reason = "No reason given" @@ -2052,6 +2067,10 @@ func dispatchMessageToTarget(client *Client, tags map[string]string, histType hi tnick := tDetails.nick details := client.Details() + if details.account == "" && server.Defcon() <= 3 { + rb.Add(nil, server.name, ERR_NEEDREGGEDNICK, client.Nick(), tnick, client.t("Direct messages from unregistered users are temporarily restricted")) + return + } nickMaskString := details.nickMask accountName := details.accountName var deliverySessions []*Session diff --git a/irc/help.go b/irc/help.go index c9021231..77940f35 100644 --- a/irc/help.go +++ b/irc/help.go @@ -164,6 +164,20 @@ Provides various debugging commands for the IRCd.