3
0
mirror of https://github.com/ergochat/ergo.git synced 2024-11-22 11:59:40 +01:00

fix #328 (implement DEFCON)

This commit is contained in:
Shivaram Lingamneni 2020-07-08 05:32:14 -04:00
parent f2d0842453
commit 6ea2eb367d
11 changed files with 70 additions and 7 deletions

View File

@ -553,6 +553,7 @@ oper-classes:
- "vhosts" - "vhosts"
- "chanreg" - "chanreg"
- "history" - "history"
- "defcon"
# ircd operators # ircd operators
opers: opers:

View File

@ -579,6 +579,7 @@ oper-classes:
- "vhosts" - "vhosts"
- "chanreg" - "chanreg"
- "history" - "history"
- "defcon"
# ircd operators # ircd operators
opers: opers:

View File

@ -380,8 +380,8 @@ func (am *AccountManager) Register(client *Client, account string, callbackNames
config := am.server.Config() config := am.server.Config()
// final "is registration allowed" check, probably redundant: // final "is registration allowed" check:
if !(config.Accounts.Registration.Enabled || callbackNamespace == "admin") { if !(config.Accounts.Registration.Enabled || callbackNamespace == "admin") || am.server.Defcon() <= 4 {
return errFeatureDisabled return errFeatureDisabled
} }
@ -1642,6 +1642,11 @@ func (am *AccountManager) performVHostChange(account string, munger vhostMunger)
return return
} }
if am.server.Defcon() <= 3 {
err = errFeatureDisabled
return
}
am.vHostUpdateMutex.Lock() am.vHostUpdateMutex.Lock()
defer am.vHostUpdateMutex.Unlock() defer am.vHostUpdateMutex.Unlock()

View File

@ -706,7 +706,8 @@ func (channel *Channel) Join(client *Client, key string, isSajoin bool, rb *Resp
return errBanned return errBanned
} }
if channel.flags.HasMode(modes.RegisteredOnly) && details.account == "" { if details.account == "" &&
(channel.flags.HasMode(modes.RegisteredOnly) || channel.server.Defcon() <= 3) {
return errRegisteredOnly return errRegisteredOnly
} }
} }

View File

@ -187,6 +187,10 @@ func (cm *ChannelManager) Cleanup(channel *Channel) {
} }
func (cm *ChannelManager) SetRegistered(channelName string, account string) (err error) { func (cm *ChannelManager) SetRegistered(channelName string, account string) (err error) {
if cm.server.Defcon() <= 4 {
return errFeatureDisabled
}
var channel *Channel var channel *Channel
cfname, err := CasefoldChannel(channelName) cfname, err := CasefoldChannel(channelName)
if err != nil { if err != nil {

View File

@ -529,7 +529,7 @@ const (
authFailSaslRequired authFailSaslRequired
) )
func (client *Client) isAuthorized(config *Config, session *Session) AuthOutcome { func (client *Client) isAuthorized(server *Server, config *Config, session *Session) AuthOutcome {
saslSent := client.account != "" saslSent := client.account != ""
// PASS requirement // PASS requirement
if (config.Server.passwordBytes != nil) && session.passStatus != serverPassSuccessful && !(config.Accounts.SkipServerPassword && saslSent) { 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 return authFailTorSaslRequired
} }
// finally, enforce require-sasl // 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 authFailSaslRequired
} }
return authSuccess return authSuccess

View File

@ -124,6 +124,10 @@ func init() {
minParams: 1, minParams: 1,
oper: true, oper: true,
}, },
"DEFCON": {
handler: defconHandler,
capabs: []string{"defcon"},
},
"DEOPER": { "DEOPER": {
handler: deoperHandler, handler: deoperHandler,
minParams: 0, minParams: 0,

View File

@ -37,6 +37,14 @@ func (server *Server) Languages() (lm *languages.Manager) {
return server.Config().languageManager 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) { func (client *Client) Sessions() (sessions []*Session) {
client.stateMutex.RLock() client.stateMutex.RLock()
sessions = client.sessions sessions = client.sessions

View File

@ -759,6 +759,21 @@ func debugHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *Res
return false 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 // helper for parsing the reason args to DLINE and KLINE
func getReasonsFromParams(params []string, currentArg int) (reason, operReason string) { func getReasonsFromParams(params []string, currentArg int) (reason, operReason string) {
reason = "No reason given" reason = "No reason given"

View File

@ -164,6 +164,20 @@ Provides various debugging commands for the IRCd. <option> can be one of:
* STOPCPUPROFILE: Stops the CPU profiler. * STOPCPUPROFILE: Stops the CPU profiler.
* PROFILEHEAP: Writes a memory profile. * PROFILEHEAP: Writes a memory profile.
* CRASHSERVER: Crashes the server (for use in failover testing)`, * CRASHSERVER: Crashes the server (for use in failover testing)`,
},
"defcon": {
oper: true,
text: `DEFCON [level]
The DEFCON system can disable server features at runtime, to mitigate
spam or other hostile activity. It has five levels, which are cumulative
(i.e., level 3 includes all restrictions from level 4 and so on):
5: Normal operation
4: No new account or channel registrations
3: All channels are +R; no changes to vhosts
2: No new unauthenticated connections
1: No new connections except from localhost or other trusted IPs`,
}, },
"deoper": { "deoper": {
oper: true, oper: true,

View File

@ -80,6 +80,7 @@ type Server struct {
whoWas WhoWasList whoWas WhoWasList
stats Stats stats Stats
semaphores ServerSemaphores semaphores ServerSemaphores
defcon uint32
} }
// NewServer returns a new Oragono server. // NewServer returns a new Oragono server.
@ -91,6 +92,7 @@ func NewServer(config *Config, logger *logger.Manager) (*Server, error) {
logger: logger, logger: logger,
rehashSignal: make(chan os.Signal, 1), rehashSignal: make(chan os.Signal, 1),
signals: make(chan os.Signal, len(ServerExitSignals)), signals: make(chan os.Signal, len(ServerExitSignals)),
defcon: 5,
} }
server.clients.Initialize() server.clients.Initialize()
@ -149,6 +151,12 @@ func (server *Server) Run() {
} }
func (server *Server) checkBans(ipaddr net.IP) (banned bool, message string) { func (server *Server) checkBans(ipaddr net.IP) (banned bool, message string) {
if server.Defcon() == 1 {
if !(ipaddr.IsLoopback() || utils.IPInNets(ipaddr, server.Config().Server.secureNets)) {
return true, "New connections to this server are temporarily restricted"
}
}
// check DLINEs // check DLINEs
isBanned, info := server.dlines.CheckIP(ipaddr) isBanned, info := server.dlines.CheckIP(ipaddr)
if isBanned { if isBanned {
@ -220,7 +228,8 @@ func (server *Server) tryRegister(c *Client, session *Session) (exiting bool) {
// client MUST send PASS if necessary, or authenticate with SASL if necessary, // client MUST send PASS if necessary, or authenticate with SASL if necessary,
// before completing the other registration commands // before completing the other registration commands
authOutcome := c.isAuthorized(server.Config(), session) config := server.Config()
authOutcome := c.isAuthorized(server, config, session)
var quitMessage string var quitMessage string
switch authOutcome { switch authOutcome {
case authFailPass: case authFailPass:
@ -270,7 +279,7 @@ func (server *Server) tryRegister(c *Client, session *Session) (exiting bool) {
// Apply default user modes (without updating the invisible counter) // Apply default user modes (without updating the invisible counter)
// The number of invisible users will be updated by server.stats.Register // The number of invisible users will be updated by server.stats.Register
// if we're using default user mode +i. // if we're using default user mode +i.
for _, defaultMode := range server.Config().Accounts.defaultUserModes { for _, defaultMode := range config.Accounts.defaultUserModes {
c.SetMode(defaultMode, true) c.SetMode(defaultMode, true)
} }