Added new failed login rate-limiting options
parent
2a51f6f385
commit
cfee5aa3f4
Binary file not shown.
3
NEWS
3
NEWS
|
@ -3,6 +3,9 @@
|
||||||
|
|
||||||
Enhancements
|
Enhancements
|
||||||
- updated CKEditor to version 4.1.1 (#2333)
|
- updated CKEditor to version 4.1.1 (#2333)
|
||||||
|
- new failed login attemps rate-limiting options. See the new
|
||||||
|
SOGoMaximumFailedLoginCount, SOGoMaximumFailedLoginInterval and
|
||||||
|
SOGoFailedLoginBlockInterval defaults
|
||||||
|
|
||||||
Bug fixes
|
Bug fixes
|
||||||
- Fixed decoding of the charset parameter when using single quotes (#2306)
|
- Fixed decoding of the charset parameter when using single quotes (#2306)
|
||||||
|
|
|
@ -113,19 +113,10 @@ static NSString *rfc822Months[] = {@"", @"Jan", @"Feb", @"Mar", @"Apr",
|
||||||
timeZoneShift];
|
timeZoneShift];
|
||||||
}
|
}
|
||||||
|
|
||||||
/* <<<<<<< variant A
|
|
||||||
#define secondsOfDistantFuture 1073741823.0
|
|
||||||
#define secondsOfDistantPast -1518491648.0
|
|
||||||
>>>>>>> variant B */
|
|
||||||
#define secondsOfDistantFuture 1073741823.0
|
#define secondsOfDistantFuture 1073741823.0
|
||||||
#define secondsOfDistantPast -1073741823.0
|
#define secondsOfDistantPast -1073741823.0
|
||||||
|
|
||||||
/*
|
|
||||||
####### Ancestor
|
|
||||||
#define secondsOfDistantFuture 63113990400.0
|
|
||||||
#define secondsOfDistantPast -63113817600.0
|
|
||||||
======= end */
|
|
||||||
|
|
||||||
+ (id) distantFuture
|
+ (id) distantFuture
|
||||||
{
|
{
|
||||||
static NSCalendarDate *date = nil;
|
static NSCalendarDate *date = nil;
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/* SOGoCache.h - this file is part of SOGo
|
/* SOGoCache.h - this file is part of SOGo
|
||||||
*
|
*
|
||||||
* Copyright (C) 2008-2011 Inverse inc.
|
* Copyright (C) 2008-2013 Inverse inc.
|
||||||
*
|
*
|
||||||
* Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
|
* Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
|
||||||
* Ludovic Marcotte <lmarcotte@inverse.ca>
|
* Ludovic Marcotte <lmarcotte@inverse.ca>
|
||||||
|
@ -98,6 +98,11 @@
|
||||||
forLogin: (NSString *) login;
|
forLogin: (NSString *) login;
|
||||||
- (NSString *) userSettingsForLogin: (NSString *) theLogin;
|
- (NSString *) userSettingsForLogin: (NSString *) theLogin;
|
||||||
|
|
||||||
|
- (void) setFailedCount: (int) theCount
|
||||||
|
forLogin: (NSString *) theLogin;
|
||||||
|
|
||||||
|
- (NSDictionary *) failedCountForLogin: (NSString *) login;
|
||||||
|
|
||||||
//
|
//
|
||||||
// CAS support
|
// CAS support
|
||||||
//
|
//
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/* SOGoCache.m - this file is part of SOGo
|
/* SOGoCache.m - this file is part of SOGo
|
||||||
*
|
*
|
||||||
* Copyright (C) 2008-2010 Inverse inc.
|
* Copyright (C) 2008-2013 Inverse inc.
|
||||||
*
|
*
|
||||||
* Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
|
* Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
|
||||||
* Ludovic Marcotte <lmarcotte@inverse.ca>
|
* Ludovic Marcotte <lmarcotte@inverse.ca>
|
||||||
|
@ -40,6 +40,7 @@
|
||||||
* cas-ticket:< > value =
|
* cas-ticket:< > value =
|
||||||
* cas-pgtiou:< > value =
|
* cas-pgtiou:< > value =
|
||||||
* session:< > value =
|
* session:< > value =
|
||||||
|
* <uid>+failedlogins value = NSDictionary instance holding the failed count and the date of the first failed authentication
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
@ -47,6 +48,7 @@
|
||||||
#import <Foundation/NSData.h>
|
#import <Foundation/NSData.h>
|
||||||
#import <Foundation/NSDictionary.h>
|
#import <Foundation/NSDictionary.h>
|
||||||
#import <Foundation/NSLock.h>
|
#import <Foundation/NSLock.h>
|
||||||
|
#import <Foundation/NSValue.h>
|
||||||
#import <Foundation/NSString.h>
|
#import <Foundation/NSString.h>
|
||||||
#import <Foundation/NSTimer.h>
|
#import <Foundation/NSTimer.h>
|
||||||
|
|
||||||
|
@ -327,6 +329,9 @@ static memcached_st *handle = NULL;
|
||||||
" no handle exists"), key];
|
" no handle exists"), key];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//
|
||||||
- (void) setValue: (NSString *) value
|
- (void) setValue: (NSString *) value
|
||||||
forKey: (NSString *) key
|
forKey: (NSString *) key
|
||||||
{
|
{
|
||||||
|
@ -334,6 +339,9 @@ static memcached_st *handle = NULL;
|
||||||
expire: cleanupInterval];
|
expire: cleanupInterval];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//
|
||||||
- (NSString *) valueForKey: (NSString *) key
|
- (NSString *) valueForKey: (NSString *) key
|
||||||
{
|
{
|
||||||
NSString *valueString;
|
NSString *valueString;
|
||||||
|
@ -372,6 +380,9 @@ static memcached_st *handle = NULL;
|
||||||
return valueString;
|
return valueString;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//
|
||||||
- (void) removeValueForKey: (NSString *) key
|
- (void) removeValueForKey: (NSString *) key
|
||||||
{
|
{
|
||||||
NSData *keyData;
|
NSData *keyData;
|
||||||
|
@ -420,6 +431,9 @@ static memcached_st *handle = NULL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//
|
||||||
- (NSString *) _valuesOfType: (NSString *) theType
|
- (NSString *) _valuesOfType: (NSString *) theType
|
||||||
forKey: (NSString *) theKey
|
forKey: (NSString *) theKey
|
||||||
{
|
{
|
||||||
|
@ -439,6 +453,9 @@ static memcached_st *handle = NULL;
|
||||||
return valueString;
|
return valueString;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//
|
||||||
- (void) setUserAttributes: (NSString *) theAttributes
|
- (void) setUserAttributes: (NSString *) theAttributes
|
||||||
forLogin: (NSString *) login
|
forLogin: (NSString *) login
|
||||||
{
|
{
|
||||||
|
@ -475,6 +492,60 @@ static memcached_st *handle = NULL;
|
||||||
return [self _valuesOfType: @"settings" forKey: theLogin];
|
return [self _valuesOfType: @"settings" forKey: theLogin];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// SOGo password failed counts
|
||||||
|
//
|
||||||
|
- (void) setFailedCount: (int) theCount
|
||||||
|
forLogin: (NSString *) theLogin
|
||||||
|
{
|
||||||
|
NSMutableDictionary *d;
|
||||||
|
NSNumber *count;
|
||||||
|
|
||||||
|
if (theCount)
|
||||||
|
{
|
||||||
|
count = [NSNumber numberWithInt: theCount];
|
||||||
|
|
||||||
|
d = [NSMutableDictionary dictionaryWithDictionary: [self failedCountForLogin: theLogin]];
|
||||||
|
|
||||||
|
if (![d objectForKey: @"InitialDate"])
|
||||||
|
{
|
||||||
|
[d setObject: [NSNumber numberWithUnsignedInt: [[NSCalendarDate date] timeIntervalSince1970]] forKey: @"InitialDate"];
|
||||||
|
}
|
||||||
|
|
||||||
|
[d setObject: count forKey: @"FailedCount"];
|
||||||
|
[self _cacheValues: [d jsonRepresentation]
|
||||||
|
ofType: @"failedlogins"
|
||||||
|
forKey: theLogin];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
[self removeValueForKey: [NSString stringWithFormat: @"%@+failedlogins", theLogin]];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Returns a dictionary with two keys/values
|
||||||
|
//
|
||||||
|
// FailedCount ->
|
||||||
|
// InitialDate ->
|
||||||
|
//
|
||||||
|
- (NSDictionary *) failedCountForLogin: (NSString *) theLogin
|
||||||
|
{
|
||||||
|
NSDictionary *d;
|
||||||
|
NSString *s;
|
||||||
|
|
||||||
|
s = [self _valuesOfType: @"failedlogins" forKey: theLogin];
|
||||||
|
d = nil;
|
||||||
|
|
||||||
|
if (s)
|
||||||
|
{
|
||||||
|
d = [s objectFromJSONString];
|
||||||
|
}
|
||||||
|
|
||||||
|
return d;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
//
|
//
|
||||||
// CAS session support
|
// CAS session support
|
||||||
//
|
//
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/* SOGoSystemDefaults.h - this file is part of SOGo
|
/* SOGoSystemDefaults.h - this file is part of SOGo
|
||||||
*
|
*
|
||||||
* Copyright (C) 2009-2011 Inverse inc.
|
* Copyright (C) 2009-2013 Inverse inc.
|
||||||
*
|
*
|
||||||
* Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
|
* Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
|
||||||
* Francis Lachapelle <flachapelle@inverse.ca>
|
* Francis Lachapelle <flachapelle@inverse.ca>
|
||||||
|
@ -86,6 +86,10 @@
|
||||||
|
|
||||||
- (BOOL) enablePublicAccess;
|
- (BOOL) enablePublicAccess;
|
||||||
|
|
||||||
|
- (int) maximumFailedLoginCount;
|
||||||
|
- (int) maximumFailedLoginInterval;
|
||||||
|
- (int) failedLoginBlockInternval;
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
#endif /* SOGOSYSTEMDEFAULTS_H */
|
#endif /* SOGOSYSTEMDEFAULTS_H */
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/* SOGoSystemDefaults.m - this file is part of SOGo
|
/* SOGoSystemDefaults.m - this file is part of SOGo
|
||||||
*
|
*
|
||||||
* Copyright (C) 2009-2012 Inverse inc.
|
* Copyright (C) 2009-2013 Inverse inc.
|
||||||
* Copyright (C) 2012 Jeroen Dekkers <jeroen@dekkers.ch>
|
* Copyright (C) 2012 Jeroen Dekkers <jeroen@dekkers.ch>
|
||||||
*
|
*
|
||||||
* Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
|
* Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
|
||||||
|
@ -513,4 +513,33 @@ _injectConfigurationFromFile (NSMutableDictionary *defaultsDict,
|
||||||
return [self boolForKey: @"SOGoEnablePublicAccess"];
|
return [self boolForKey: @"SOGoEnablePublicAccess"];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (int) maximumFailedLoginCount
|
||||||
|
{
|
||||||
|
return [self integerForKey: @"SOGoMaximumFailedLoginCount"];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (int) maximumFailedLoginInterval
|
||||||
|
{
|
||||||
|
int v;
|
||||||
|
|
||||||
|
v = [self integerForKey: @"SOGoMaximumFailedLoginInterval"];
|
||||||
|
|
||||||
|
if (!v)
|
||||||
|
v = 10;
|
||||||
|
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (int) failedLoginBlockInterval
|
||||||
|
{
|
||||||
|
int v;
|
||||||
|
|
||||||
|
v = [self integerForKey: @"SOGoFailedLoginBlockInterval"];
|
||||||
|
|
||||||
|
if (!v)
|
||||||
|
v = 300;
|
||||||
|
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
|
@ -447,6 +447,9 @@ static Class NSNullK;
|
||||||
return checkOK;
|
return checkOK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//
|
||||||
- (BOOL) checkLogin: (NSString *) _login
|
- (BOOL) checkLogin: (NSString *) _login
|
||||||
password: (NSString *) _pwd
|
password: (NSString *) _pwd
|
||||||
domain: (NSString **) _domain
|
domain: (NSString **) _domain
|
||||||
|
@ -463,6 +466,9 @@ static Class NSNullK;
|
||||||
useCache: YES];
|
useCache: YES];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//
|
||||||
- (BOOL) checkLogin: (NSString *) _login
|
- (BOOL) checkLogin: (NSString *) _login
|
||||||
password: (NSString *) _pwd
|
password: (NSString *) _pwd
|
||||||
domain: (NSString **) _domain
|
domain: (NSString **) _domain
|
||||||
|
@ -471,8 +477,9 @@ static Class NSNullK;
|
||||||
grace: (int *) _grace
|
grace: (int *) _grace
|
||||||
useCache: (BOOL) useCache
|
useCache: (BOOL) useCache
|
||||||
{
|
{
|
||||||
|
NSMutableDictionary *currentUser, *failedCount;
|
||||||
NSString *dictPassword, *username, *jsonUser;
|
NSString *dictPassword, *username, *jsonUser;
|
||||||
NSMutableDictionary *currentUser;
|
SOGoSystemDefaults *dd;
|
||||||
BOOL checkOK;
|
BOOL checkOK;
|
||||||
|
|
||||||
// We check for cached passwords. If the entry is cached, we
|
// We check for cached passwords. If the entry is cached, we
|
||||||
|
@ -482,6 +489,41 @@ static Class NSNullK;
|
||||||
username = [NSString stringWithFormat: @"%@@%@", _login, *_domain];
|
username = [NSString stringWithFormat: @"%@@%@", _login, *_domain];
|
||||||
else
|
else
|
||||||
username = _login;
|
username = _login;
|
||||||
|
|
||||||
|
failedCount = [[SOGoCache sharedCache] failedCountForLogin: username];
|
||||||
|
dd = [SOGoSystemDefaults sharedSystemDefaults];
|
||||||
|
|
||||||
|
//
|
||||||
|
// We check the fail count per user in memcache (per server). If the
|
||||||
|
// fail count reaches X in Y minutes, we deny immediately the
|
||||||
|
// authentications for Z minutes
|
||||||
|
//
|
||||||
|
if (failedCount)
|
||||||
|
{
|
||||||
|
unsigned int current_time, start_time, delta, block_time;
|
||||||
|
|
||||||
|
current_time = [[NSCalendarDate date] timeIntervalSince1970];
|
||||||
|
start_time = [[failedCount objectForKey: @"InitialDate"] unsignedIntValue];
|
||||||
|
delta = current_time - start_time;
|
||||||
|
|
||||||
|
block_time = [dd failedLoginBlockInterval];
|
||||||
|
|
||||||
|
if ([[failedCount objectForKey: @"FailedCount"] intValue] >= [dd maximumFailedLoginCount] &&
|
||||||
|
delta >= [dd maximumFailedLoginInterval] &&
|
||||||
|
delta <= block_time )
|
||||||
|
{
|
||||||
|
*_perr = PolicyAccountLocked;
|
||||||
|
return NO;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (delta > block_time)
|
||||||
|
{
|
||||||
|
[[SOGoCache sharedCache] setFailedCount: 0
|
||||||
|
forLogin: username];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
jsonUser = [[SOGoCache sharedCache] userAttributesForLogin: username];
|
jsonUser = [[SOGoCache sharedCache] userAttributesForLogin: username];
|
||||||
currentUser = [jsonUser objectFromJSONString];
|
currentUser = [jsonUser objectFromJSONString];
|
||||||
dictPassword = [currentUser objectForKey: @"password"];
|
dictPassword = [currentUser objectForKey: @"password"];
|
||||||
|
@ -514,7 +556,16 @@ static Class NSNullK;
|
||||||
forLogin: username];
|
forLogin: username];
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
checkOK = NO;
|
{
|
||||||
|
// If failed login "rate-limiting" is enabled, we adjust the stats
|
||||||
|
if ([dd maximumFailedLoginCount])
|
||||||
|
{
|
||||||
|
[[SOGoCache sharedCache] setFailedCount: ([[failedCount objectForKey: @"FailedCount"] intValue] + 1)
|
||||||
|
forLogin: username];
|
||||||
|
}
|
||||||
|
|
||||||
|
checkOK = NO;
|
||||||
|
}
|
||||||
|
|
||||||
// We MUST, for all LDAP sources, update the bindDN and bindPassword
|
// We MUST, for all LDAP sources, update the bindDN and bindPassword
|
||||||
// to the user's value if bindAsCurrentUser is set to true in the
|
// to the user's value if bindAsCurrentUser is set to true in the
|
||||||
|
@ -538,6 +589,9 @@ static Class NSNullK;
|
||||||
return checkOK;
|
return checkOK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//
|
||||||
- (BOOL) changePasswordForLogin: (NSString *) login
|
- (BOOL) changePasswordForLogin: (NSString *) login
|
||||||
inDomain: (NSString *) domain
|
inDomain: (NSString *) domain
|
||||||
oldPassword: (NSString *) oldPassword
|
oldPassword: (NSString *) oldPassword
|
||||||
|
|
|
@ -172,6 +172,9 @@
|
||||||
andJSONRepresentation: jsonError];
|
andJSONRepresentation: jsonError];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//
|
||||||
- (id <WOActionResults>) connectAction
|
- (id <WOActionResults>) connectAction
|
||||||
{
|
{
|
||||||
WOResponse *response;
|
WOResponse *response;
|
||||||
|
|
Loading…
Reference in New Issue