Browse Source

fix #913

tags/v2.1.0-rc1
Shivaram Lingamneni 4 years ago
parent
commit
895a0e6d68
7 changed files with 87 additions and 29 deletions
  1. 33
    0
      irc/accounts.go
  2. 27
    11
      irc/config.go
  3. 1
    0
      irc/errors.go
  4. 3
    3
      irc/handlers.go
  5. 12
    15
      irc/nickserv.go
  6. 3
    0
      irc/server.go
  7. 8
    0
      oragono.yaml

+ 33
- 0
irc/accounts.go View File

@@ -15,6 +15,7 @@ import (
15 15
 	"unicode"
16 16
 
17 17
 	"github.com/oragono/oragono/irc/caps"
18
+	"github.com/oragono/oragono/irc/connection_limits"
18 19
 	"github.com/oragono/oragono/irc/ldap"
19 20
 	"github.com/oragono/oragono/irc/passwd"
20 21
 	"github.com/oragono/oragono/irc/utils"
@@ -62,6 +63,7 @@ type AccountManager struct {
62 63
 	nickToAccount     map[string]string
63 64
 	skeletonToAccount map[string]string
64 65
 	accountToMethod   map[string]NickEnforcementMethod
66
+	registerThrottle  connection_limits.GenericThrottle
65 67
 }
66 68
 
67 69
 func (am *AccountManager) Initialize(server *Server) {
@@ -75,6 +77,24 @@ func (am *AccountManager) Initialize(server *Server) {
75 77
 	am.buildNickToAccountIndex(config)
76 78
 	am.initVHostRequestQueue(config)
77 79
 	am.createAlwaysOnClients(config)
80
+	am.resetRegisterThrottle(config)
81
+}
82
+
83
+func (am *AccountManager) resetRegisterThrottle(config *Config) {
84
+	am.Lock()
85
+	defer am.Unlock()
86
+
87
+	am.registerThrottle = connection_limits.GenericThrottle{
88
+		Duration: config.Accounts.Registration.Throttling.Duration,
89
+		Limit:    config.Accounts.Registration.Throttling.MaxAttempts,
90
+	}
91
+}
92
+
93
+func (am *AccountManager) touchRegisterThrottle() (throttled bool) {
94
+	am.Lock()
95
+	defer am.Unlock()
96
+	throttled, _ = am.registerThrottle.Touch()
97
+	return
78 98
 }
79 99
 
80 100
 func (am *AccountManager) createAlwaysOnClients(config *Config) {
@@ -363,6 +383,15 @@ func (am *AccountManager) Register(client *Client, account string, callbackNames
363 383
 		return errFeatureDisabled
364 384
 	}
365 385
 
386
+	if client != nil && client.Account() != "" {
387
+		return errAccountAlreadyLoggedIn
388
+	}
389
+
390
+	if client != nil && am.touchRegisterThrottle() {
391
+		am.server.logger.Warning("accounts", "global registration throttle exceeded by client", client.Nick())
392
+		return errLimitExceeded
393
+	}
394
+
366 395
 	// if nick reservation is enabled, you can only register your current nickname
367 396
 	// as an account; this prevents "land-grab" situations where someone else
368 397
 	// registers your nick out from under you and then NS GHOSTs you
@@ -725,6 +754,10 @@ func (am *AccountManager) Verify(client *Client, account string, code string) er
725 754
 		return errAccountVerificationFailed
726 755
 	}
727 756
 
757
+	if client != nil && client.Account() != "" {
758
+		return errAccountAlreadyLoggedIn
759
+	}
760
+
728 761
 	verifiedKey := fmt.Sprintf(keyAccountVerified, casefoldedAccount)
729 762
 	accountKey := fmt.Sprintf(keyAccountExists, casefoldedAccount)
730 763
 	accountNameKey := fmt.Sprintf(keyAccountName, casefoldedAccount)

+ 27
- 11
irc/config.go View File

@@ -229,6 +229,29 @@ type MulticlientConfig struct {
229 229
 	AlwaysOn         PersistentStatus `yaml:"always-on"`
230 230
 }
231 231
 
232
+type throttleConfig struct {
233
+	Enabled     bool
234
+	Duration    time.Duration
235
+	MaxAttempts int `yaml:"max-attempts"`
236
+}
237
+
238
+type ThrottleConfig struct {
239
+	throttleConfig
240
+}
241
+
242
+func (t *ThrottleConfig) UnmarshalYAML(unmarshal func(interface{}) error) (err error) {
243
+	// note that this technique only works if the zero value of the struct
244
+	// doesn't need any postprocessing (because if the field is omitted entirely
245
+	// from the YAML, then UnmarshalYAML won't be called at all)
246
+	if err = unmarshal(&t.throttleConfig); err != nil {
247
+		return
248
+	}
249
+	if !t.Enabled {
250
+		t.MaxAttempts = 0 // limit of 0 means disabled
251
+	}
252
+	return
253
+}
254
+
232 255
 type AccountConfig struct {
233 256
 	Registration          AccountRegistrationConfig
234 257
 	AuthenticationEnabled bool `yaml:"authentication-enabled"`
@@ -237,13 +260,9 @@ type AccountConfig struct {
237 260
 		Exempted     []string
238 261
 		exemptedNets []net.IPNet
239 262
 	} `yaml:"require-sasl"`
240
-	LDAP            ldap.ServerConfig
241
-	LoginThrottling struct {
242
-		Enabled     bool
243
-		Duration    time.Duration
244
-		MaxAttempts int `yaml:"max-attempts"`
245
-	} `yaml:"login-throttling"`
246
-	SkipServerPassword bool `yaml:"skip-server-password"`
263
+	LDAP               ldap.ServerConfig
264
+	LoginThrottling    ThrottleConfig `yaml:"login-throttling"`
265
+	SkipServerPassword bool           `yaml:"skip-server-password"`
247 266
 	NickReservation    struct {
248 267
 		Enabled                bool
249 268
 		AdditionalNickLimit    int `yaml:"additional-nick-limit"`
@@ -266,6 +285,7 @@ type AccountConfig struct {
266 285
 // AccountRegistrationConfig controls account registration.
267 286
 type AccountRegistrationConfig struct {
268 287
 	Enabled                bool
288
+	Throttling             ThrottleConfig
269 289
 	EnabledCallbacks       []string         `yaml:"enabled-callbacks"`
270 290
 	EnabledCredentialTypes []string         `yaml:"-"`
271 291
 	VerifyTimeout          custime.Duration `yaml:"verify-timeout"`
@@ -997,10 +1017,6 @@ func LoadConfig(filename string) (config *Config, err error) {
997 1017
 		}
998 1018
 	}
999 1019
 
1000
-	if !config.Accounts.LoginThrottling.Enabled {
1001
-		config.Accounts.LoginThrottling.MaxAttempts = 0 // limit of 0 means disabled
1002
-	}
1003
-
1004 1020
 	config.Server.capValues[caps.SASL] = "PLAIN,EXTERNAL"
1005 1021
 	if !config.Accounts.AuthenticationEnabled {
1006 1022
 		config.Server.supportedCaps.Disable(caps.SASL)

+ 1
- 0
irc/errors.go View File

@@ -22,6 +22,7 @@ var (
22 22
 	errAccountBadPassphrase           = errors.New(`Passphrase contains forbidden characters or is otherwise invalid`)
23 23
 	errAccountNickReservationFailed   = errors.New("Could not (un)reserve nick")
24 24
 	errAccountNotLoggedIn             = errors.New("You're not logged into an account")
25
+	errAccountAlreadyLoggedIn         = errors.New("You're already logged into an account")
25 26
 	errAccountTooManyNicks            = errors.New("Account has too many reserved nicks")
26 27
 	errAccountUnverified              = errors.New(`Account is not yet verified`)
27 28
 	errAccountVerificationFailed      = errors.New("Account verification failed")

+ 3
- 3
irc/handlers.go View File

@@ -66,10 +66,10 @@ func registrationErrorToMessageAndCode(err error) (message, code string) {
66 66
 	case errAccountBadPassphrase:
67 67
 		code = "REG_INVALID_CREDENTIAL"
68 68
 		message = err.Error()
69
-	case errAccountAlreadyRegistered, errAccountAlreadyVerified, errAccountAlreadyUnregistered:
70
-		message = err.Error()
71
-	case errAccountCreation, errAccountMustHoldNick, errAccountBadPassphrase, errCertfpAlreadyExists, errFeatureDisabled:
69
+	case errAccountAlreadyRegistered, errAccountAlreadyVerified, errAccountAlreadyUnregistered, errAccountAlreadyLoggedIn, errAccountCreation, errAccountMustHoldNick, errAccountBadPassphrase, errCertfpAlreadyExists, errFeatureDisabled:
72 70
 		message = err.Error()
71
+	case errLimitExceeded:
72
+		message = `There have been too many registration attempts recently; try again later`
73 73
 	}
74 74
 	return
75 75
 }

+ 12
- 15
irc/nickserv.go View File

@@ -616,7 +616,9 @@ func nsGroupHandler(server *Server, client *Client, command string, params []str
616 616
 }
617 617
 
618 618
 func nsLoginThrottleCheck(client *Client, rb *ResponseBuffer) (success bool) {
619
+	client.stateMutex.Lock()
619 620
 	throttled, remainingTime := client.loginThrottle.Touch()
621
+	client.stateMutex.Unlock()
620 622
 	if throttled {
621 623
 		nsNotice(rb, fmt.Sprintf(client.t("Please wait at least %v and try again"), remainingTime))
622 624
 		return false
@@ -741,11 +743,6 @@ func nsRegisterHandler(server *Server, client *Client, command string, params []
741 743
 		}
742 744
 	}
743 745
 
744
-	if details.account != "" {
745
-		nsNotice(rb, client.t("You're already logged into an account"))
746
-		return
747
-	}
748
-
749 746
 	if !nsLoginThrottleCheck(client, rb) {
750 747
 		return
751 748
 	}
@@ -793,13 +790,10 @@ func nsRegisterHandler(server *Server, client *Client, command string, params []
793 790
 			message := fmt.Sprintf(messageTemplate, fmt.Sprintf("%s:%s", callbackNamespace, callbackValue))
794 791
 			nsNotice(rb, message)
795 792
 		}
796
-	}
797
-
798
-	// details could not be stored and relevant numerics have been dispatched, abort
799
-	message, _ := registrationErrorToMessageAndCode(err)
800
-	if err != nil {
793
+	} else {
794
+		// details could not be stored and relevant numerics have been dispatched, abort
795
+		message, _ := registrationErrorToMessageAndCode(err)
801 796
 		nsNotice(rb, client.t(message))
802
-		return
803 797
 	}
804 798
 }
805 799
 
@@ -894,10 +888,13 @@ func nsVerifyHandler(server *Server, client *Client, command string, params []st
894 888
 	err := server.accounts.Verify(client, username, code)
895 889
 
896 890
 	var errorMessage string
897
-	if err == errAccountVerificationInvalidCode || err == errAccountAlreadyVerified {
898
-		errorMessage = err.Error()
899
-	} else if err != nil {
900
-		errorMessage = errAccountVerificationFailed.Error()
891
+	if err != nil {
892
+		switch err {
893
+		case errAccountAlreadyLoggedIn, errAccountVerificationInvalidCode, errAccountAlreadyVerified:
894
+			errorMessage = err.Error()
895
+		default:
896
+			errorMessage = errAccountVerificationFailed.Error()
897
+		}
901 898
 	}
902 899
 
903 900
 	if errorMessage != "" {

+ 3
- 0
irc/server.go View File

@@ -634,6 +634,9 @@ func (server *Server) applyConfig(config *Config) (err error) {
634 634
 				client.resizeHistory(config)
635 635
 			}
636 636
 		}
637
+		if oldConfig.Accounts.Registration.Throttling != config.Accounts.Registration.Throttling {
638
+			server.accounts.resetRegisterThrottle(config)
639
+		}
637 640
 	}
638 641
 
639 642
 	// activate the new config

+ 8
- 0
oragono.yaml View File

@@ -266,6 +266,14 @@ accounts:
266 266
         # the `accreg` capability can still create accounts with `/NICKSERV SAREGISTER`
267 267
         enabled: true
268 268
 
269
+        # global throttle on new account creation
270
+        throttling:
271
+            enabled: true
272
+            # window
273
+            duration: 10m
274
+            # number of attempts allowed within the window
275
+            max-attempts: 30
276
+
269 277
         # this is the bcrypt cost we'll use for account passwords
270 278
         bcrypt-cost: 9
271 279
 

Loading…
Cancel
Save