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
 	"unicode"
15
 	"unicode"
16
 
16
 
17
 	"github.com/oragono/oragono/irc/caps"
17
 	"github.com/oragono/oragono/irc/caps"
18
+	"github.com/oragono/oragono/irc/connection_limits"
18
 	"github.com/oragono/oragono/irc/ldap"
19
 	"github.com/oragono/oragono/irc/ldap"
19
 	"github.com/oragono/oragono/irc/passwd"
20
 	"github.com/oragono/oragono/irc/passwd"
20
 	"github.com/oragono/oragono/irc/utils"
21
 	"github.com/oragono/oragono/irc/utils"
62
 	nickToAccount     map[string]string
63
 	nickToAccount     map[string]string
63
 	skeletonToAccount map[string]string
64
 	skeletonToAccount map[string]string
64
 	accountToMethod   map[string]NickEnforcementMethod
65
 	accountToMethod   map[string]NickEnforcementMethod
66
+	registerThrottle  connection_limits.GenericThrottle
65
 }
67
 }
66
 
68
 
67
 func (am *AccountManager) Initialize(server *Server) {
69
 func (am *AccountManager) Initialize(server *Server) {
75
 	am.buildNickToAccountIndex(config)
77
 	am.buildNickToAccountIndex(config)
76
 	am.initVHostRequestQueue(config)
78
 	am.initVHostRequestQueue(config)
77
 	am.createAlwaysOnClients(config)
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
 func (am *AccountManager) createAlwaysOnClients(config *Config) {
100
 func (am *AccountManager) createAlwaysOnClients(config *Config) {
363
 		return errFeatureDisabled
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
 	// if nick reservation is enabled, you can only register your current nickname
395
 	// if nick reservation is enabled, you can only register your current nickname
367
 	// as an account; this prevents "land-grab" situations where someone else
396
 	// as an account; this prevents "land-grab" situations where someone else
368
 	// registers your nick out from under you and then NS GHOSTs you
397
 	// registers your nick out from under you and then NS GHOSTs you
725
 		return errAccountVerificationFailed
754
 		return errAccountVerificationFailed
726
 	}
755
 	}
727
 
756
 
757
+	if client != nil && client.Account() != "" {
758
+		return errAccountAlreadyLoggedIn
759
+	}
760
+
728
 	verifiedKey := fmt.Sprintf(keyAccountVerified, casefoldedAccount)
761
 	verifiedKey := fmt.Sprintf(keyAccountVerified, casefoldedAccount)
729
 	accountKey := fmt.Sprintf(keyAccountExists, casefoldedAccount)
762
 	accountKey := fmt.Sprintf(keyAccountExists, casefoldedAccount)
730
 	accountNameKey := fmt.Sprintf(keyAccountName, casefoldedAccount)
763
 	accountNameKey := fmt.Sprintf(keyAccountName, casefoldedAccount)

+ 27
- 11
irc/config.go View File

229
 	AlwaysOn         PersistentStatus `yaml:"always-on"`
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
 type AccountConfig struct {
255
 type AccountConfig struct {
233
 	Registration          AccountRegistrationConfig
256
 	Registration          AccountRegistrationConfig
234
 	AuthenticationEnabled bool `yaml:"authentication-enabled"`
257
 	AuthenticationEnabled bool `yaml:"authentication-enabled"`
237
 		Exempted     []string
260
 		Exempted     []string
238
 		exemptedNets []net.IPNet
261
 		exemptedNets []net.IPNet
239
 	} `yaml:"require-sasl"`
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
 	NickReservation    struct {
266
 	NickReservation    struct {
248
 		Enabled                bool
267
 		Enabled                bool
249
 		AdditionalNickLimit    int `yaml:"additional-nick-limit"`
268
 		AdditionalNickLimit    int `yaml:"additional-nick-limit"`
266
 // AccountRegistrationConfig controls account registration.
285
 // AccountRegistrationConfig controls account registration.
267
 type AccountRegistrationConfig struct {
286
 type AccountRegistrationConfig struct {
268
 	Enabled                bool
287
 	Enabled                bool
288
+	Throttling             ThrottleConfig
269
 	EnabledCallbacks       []string         `yaml:"enabled-callbacks"`
289
 	EnabledCallbacks       []string         `yaml:"enabled-callbacks"`
270
 	EnabledCredentialTypes []string         `yaml:"-"`
290
 	EnabledCredentialTypes []string         `yaml:"-"`
271
 	VerifyTimeout          custime.Duration `yaml:"verify-timeout"`
291
 	VerifyTimeout          custime.Duration `yaml:"verify-timeout"`
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
 	config.Server.capValues[caps.SASL] = "PLAIN,EXTERNAL"
1020
 	config.Server.capValues[caps.SASL] = "PLAIN,EXTERNAL"
1005
 	if !config.Accounts.AuthenticationEnabled {
1021
 	if !config.Accounts.AuthenticationEnabled {
1006
 		config.Server.supportedCaps.Disable(caps.SASL)
1022
 		config.Server.supportedCaps.Disable(caps.SASL)

+ 1
- 0
irc/errors.go View File

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

+ 3
- 3
irc/handlers.go View File

66
 	case errAccountBadPassphrase:
66
 	case errAccountBadPassphrase:
67
 		code = "REG_INVALID_CREDENTIAL"
67
 		code = "REG_INVALID_CREDENTIAL"
68
 		message = err.Error()
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
 		message = err.Error()
70
 		message = err.Error()
71
+	case errLimitExceeded:
72
+		message = `There have been too many registration attempts recently; try again later`
73
 	}
73
 	}
74
 	return
74
 	return
75
 }
75
 }

+ 12
- 15
irc/nickserv.go View File

616
 }
616
 }
617
 
617
 
618
 func nsLoginThrottleCheck(client *Client, rb *ResponseBuffer) (success bool) {
618
 func nsLoginThrottleCheck(client *Client, rb *ResponseBuffer) (success bool) {
619
+	client.stateMutex.Lock()
619
 	throttled, remainingTime := client.loginThrottle.Touch()
620
 	throttled, remainingTime := client.loginThrottle.Touch()
621
+	client.stateMutex.Unlock()
620
 	if throttled {
622
 	if throttled {
621
 		nsNotice(rb, fmt.Sprintf(client.t("Please wait at least %v and try again"), remainingTime))
623
 		nsNotice(rb, fmt.Sprintf(client.t("Please wait at least %v and try again"), remainingTime))
622
 		return false
624
 		return false
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
 	if !nsLoginThrottleCheck(client, rb) {
746
 	if !nsLoginThrottleCheck(client, rb) {
750
 		return
747
 		return
751
 	}
748
 	}
793
 			message := fmt.Sprintf(messageTemplate, fmt.Sprintf("%s:%s", callbackNamespace, callbackValue))
790
 			message := fmt.Sprintf(messageTemplate, fmt.Sprintf("%s:%s", callbackNamespace, callbackValue))
794
 			nsNotice(rb, message)
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
 		nsNotice(rb, client.t(message))
796
 		nsNotice(rb, client.t(message))
802
-		return
803
 	}
797
 	}
804
 }
798
 }
805
 
799
 
894
 	err := server.accounts.Verify(client, username, code)
888
 	err := server.accounts.Verify(client, username, code)
895
 
889
 
896
 	var errorMessage string
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
 	if errorMessage != "" {
900
 	if errorMessage != "" {

+ 3
- 0
irc/server.go View File

634
 				client.resizeHistory(config)
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
 	// activate the new config
642
 	// activate the new config

+ 8
- 0
oragono.yaml View File

266
         # the `accreg` capability can still create accounts with `/NICKSERV SAREGISTER`
266
         # the `accreg` capability can still create accounts with `/NICKSERV SAREGISTER`
267
         enabled: true
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
         # this is the bcrypt cost we'll use for account passwords
277
         # this is the bcrypt cost we'll use for account passwords
270
         bcrypt-cost: 9
278
         bcrypt-cost: 9
271
 
279
 

Loading…
Cancel
Save