mirror of
https://github.com/inverse-inc/sogo.git
synced 2026-11-10 19:21:01 +00:00
#3126: limit totp attempt
This commit is contained in:
1 parent
1a6642c366
commit
49049f6d42
9 files changed
+105
No files matched your search
@@ -793,6 +793,16 @@ not block logins, and it is enforced even when failed login
|
||||
|
||||
Default value is `5`. Set to `0` to disable.
|
||||
|
||||
|S |SOGoMaximumFailedTotpCount
|
||||
|Parameter used to control the number of wrong TOTP verification codes
|
||||
allowed when logging in to the web interface. When reached, TOTP
|
||||
verification is blocked for the account (even with a valid code) for
|
||||
_SOGoFailedLoginBlockInterval_ seconds since the first wrong code.
|
||||
This counter is independent of _SOGoMaximumFailedLoginCount_, which
|
||||
only counts wrong passwords.
|
||||
|
||||
Default value is `5`. Set to `0` to disable.
|
||||
|
||||
|S |SOGoMaximumMessageSubmissionCount
|
||||
|Parameter used to control the number of email messages a user can send
|
||||
from SOGo's webmail interface, to _SOGoMaximumRecipientCount_, in
|
||||
|
||||
@@ -97,6 +97,11 @@
|
||||
|
||||
- (NSDictionary *) failedPasswordRecoveryCountForLogin: (NSString *) login;
|
||||
|
||||
- (void) setFailedTotpCount: (int) theCount
|
||||
forLogin: (NSString *) theLogin;
|
||||
|
||||
- (NSDictionary *) failedTotpCountForLogin: (NSString *) login;
|
||||
|
||||
- (void) setMessageSubmissionsCount: (int) theCount
|
||||
recipientsCount: (int) theRecipientsCount
|
||||
forLogin: (NSString *) theLogin;
|
||||
|
||||
@@ -568,6 +568,23 @@ static memcached_st *handle = NULL;
|
||||
ofType: @"failedrecovery"];
|
||||
}
|
||||
|
||||
//
|
||||
// SOGo TOTP verification failed counts (web login second factor).
|
||||
//
|
||||
- (void) setFailedTotpCount: (int) theCount
|
||||
forLogin: (NSString *) theLogin
|
||||
{
|
||||
[self _setFailedCount: theCount
|
||||
forLogin: theLogin
|
||||
ofType: @"failedtotp"];
|
||||
}
|
||||
|
||||
- (NSDictionary *) failedTotpCountForLogin: (NSString *) theLogin
|
||||
{
|
||||
return [self _failedCountForLogin: theLogin
|
||||
ofType: @"failedtotp"];
|
||||
}
|
||||
|
||||
//
|
||||
//
|
||||
//
|
||||
|
||||
@@ -209,4 +209,5 @@
|
||||
SOGoPasswordRecoveryMode = "Disabled";
|
||||
SOGoPasswordRecoveryQuestion = "SecretQuestion1";
|
||||
SOGoMaximumFailedPasswordRecoveryCount = 5;
|
||||
SOGoMaximumFailedTotpCount = 5;
|
||||
}
|
||||
@@ -128,6 +128,7 @@ NSComparisonResult languageSort(id el1, id el2, void *context);
|
||||
- (int) maximumFailedLoginInterval;
|
||||
- (int) failedLoginBlockInterval;
|
||||
- (int) maximumFailedPasswordRecoveryCount;
|
||||
- (int) maximumFailedTotpCount;
|
||||
|
||||
- (int) maximumMessageSizeLimit;
|
||||
|
||||
|
||||
@@ -927,6 +927,17 @@ NSComparisonResult languageSort(id el1, id el2, void *context)
|
||||
return [self integerForKey: @"SOGoMaximumFailedPasswordRecoveryCount"];
|
||||
}
|
||||
|
||||
//
|
||||
// Number of wrong TOTP verification codes allowed during
|
||||
// SOGoFailedLoginBlockInterval seconds before TOTP verification
|
||||
// is blocked for the account. Defaults to 5 (see SOGoDefaults.plist),
|
||||
// 0 disables the protection.
|
||||
//
|
||||
- (int) maximumFailedTotpCount
|
||||
{
|
||||
return [self integerForKey: @"SOGoMaximumFailedTotpCount"];
|
||||
}
|
||||
|
||||
//
|
||||
//
|
||||
//
|
||||
|
||||
@@ -112,6 +112,8 @@
|
||||
perr: (SOGoPasswordPolicyError *) perr;
|
||||
- (NSString *) getPasswordRecoveryTokenFor: (NSString *) username domain:(NSString *) domain;
|
||||
- (BOOL) isPasswordRecoveryTokenValidFor: (NSString *) token uid: (NSString *) uid;
|
||||
- (BOOL) isTotpVerificationBlockedForLogin: (NSString *) login
|
||||
failedCount: (int *) failedCount;
|
||||
@end
|
||||
|
||||
#endif /* SOGOUSERMANAGER_H */
|
||||
@@ -1545,6 +1545,45 @@ static const NSString *kObfuscatedSecondaryEmailKey = @"obfuscatedSecondaryEmail
|
||||
return jwtToken;
|
||||
}
|
||||
|
||||
/**
|
||||
* (TOTP) Check whether TOTP verification is temporarily blocked for login
|
||||
* because of too many wrong codes. Resets the counter once the block interval is over.
|
||||
* @param login User login (uid)
|
||||
* @param failedCount Current number of failures (out)
|
||||
* @return True if blocked
|
||||
*/
|
||||
- (BOOL) isTotpVerificationBlockedForLogin: (NSString *) login
|
||||
failedCount: (int *) failedCount
|
||||
{
|
||||
NSDictionary *failedTotp;
|
||||
SOGoSystemDefaults *sd;
|
||||
unsigned int currentTime, startTime;
|
||||
int maxFailed;
|
||||
|
||||
*failedCount = 0;
|
||||
sd = [SOGoSystemDefaults sharedSystemDefaults];
|
||||
maxFailed = [sd maximumFailedTotpCount];
|
||||
if (maxFailed <= 0 || ![login length])
|
||||
return NO;
|
||||
|
||||
failedTotp = [[SOGoCache sharedCache] failedTotpCountForLogin: login];
|
||||
if (!failedTotp)
|
||||
return NO;
|
||||
|
||||
currentTime = [[NSCalendarDate date] timeIntervalSince1970];
|
||||
startTime = [[failedTotp objectForKey: @"InitialDate"] unsignedIntValue];
|
||||
if (currentTime - startTime > (unsigned int) [sd failedLoginBlockInterval])
|
||||
{
|
||||
[[SOGoCache sharedCache] setFailedTotpCount: 0
|
||||
forLogin: login];
|
||||
return NO;
|
||||
}
|
||||
|
||||
*failedCount = [[failedTotp objectForKey: @"FailedCount"] intValue];
|
||||
|
||||
return (*failedCount >= maxFailed);
|
||||
}
|
||||
|
||||
/**
|
||||
* (Secret Question) Check whether password recovery is temporarily blocked for uid
|
||||
* because of too many wrong answers. Resets the counter once the block interval is over.
|
||||
|
||||
@@ -325,11 +325,22 @@ static const NSString *kJwtKey = @"jwt";
|
||||
if ([ud totpEnabled])
|
||||
{
|
||||
NSString *verificationCode;
|
||||
int totpFailedCount;
|
||||
|
||||
verificationCode = [params objectForKey: @"verificationCode"];
|
||||
if ([verificationCode length] == 6 && [verificationCode unsignedIntValue] > 0)
|
||||
{
|
||||
unsigned int code;
|
||||
|
||||
// Too many wrong codes: refuse any code until the block interval is over
|
||||
if ([[SOGoUserManager sharedUserManager] isTotpVerificationBlockedForLogin: [loggedInUser login]
|
||||
failedCount: &totpFailedCount])
|
||||
{
|
||||
[self logWithFormat: @"TOTP verification from '%@' for user '%@' blocked - too many failed attempts",
|
||||
remoteHost, username];
|
||||
return [self _responseWithLDAPPolicyError: PolicyAccountLocked additionalInfos: nil];
|
||||
}
|
||||
|
||||
const char *real_secret;
|
||||
char *secret;
|
||||
|
||||
@@ -407,6 +418,9 @@ static const NSString *kJwtKey = @"jwt";
|
||||
if (old_code != [verificationCode unsignedIntValue])
|
||||
{
|
||||
[self logWithFormat: @"Invalid TOTP key for '%@'", username];
|
||||
if ([[SOGoSystemDefaults sharedSystemDefaults] maximumFailedTotpCount] > 0)
|
||||
[[SOGoCache sharedCache] setFailedTotpCount: totpFailedCount + 1
|
||||
forLogin: [loggedInUser login]];
|
||||
[json setObject: [NSNumber numberWithInt: 1] forKey: @"totpInvalidKey"];
|
||||
return [self responseWithStatus: 403 andJSONRepresentation: json];
|
||||
}
|
||||
@@ -415,6 +429,11 @@ static const NSString *kJwtKey = @"jwt";
|
||||
[us setTotpKey: [[us userPrivateSalt] substringToIndex: 12]];
|
||||
}
|
||||
}
|
||||
|
||||
// Valid code: reset the failed TOTP count
|
||||
if (totpFailedCount > 0)
|
||||
[[SOGoCache sharedCache] setFailedTotpCount: 0
|
||||
forLogin: [loggedInUser login]];
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
Reference in new issue
Block a user