Added new failed login rate-limiting options
parent
2a51f6f385
commit
cfee5aa3f4
Binary file not shown.
3
NEWS
3
NEWS
|
@ -3,6 +3,9 @@
|
|||
|
||||
Enhancements
|
||||
- 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
|
||||
- Fixed decoding of the charset parameter when using single quotes (#2306)
|
||||
|
|
|
@ -113,19 +113,10 @@ static NSString *rfc822Months[] = {@"", @"Jan", @"Feb", @"Mar", @"Apr",
|
|||
timeZoneShift];
|
||||
}
|
||||
|
||||
/* <<<<<<< variant A
|
||||
#define secondsOfDistantFuture 1073741823.0
|
||||
#define secondsOfDistantPast -1518491648.0
|
||||
>>>>>>> variant B */
|
||||
|
||||
#define secondsOfDistantFuture 1073741823.0
|
||||
#define secondsOfDistantPast -1073741823.0
|
||||
|
||||
/*
|
||||
####### Ancestor
|
||||
#define secondsOfDistantFuture 63113990400.0
|
||||
#define secondsOfDistantPast -63113817600.0
|
||||
======= end */
|
||||
|
||||
+ (id) distantFuture
|
||||
{
|
||||
static NSCalendarDate *date = nil;
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/* 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>
|
||||
* Ludovic Marcotte <lmarcotte@inverse.ca>
|
||||
|
@ -98,6 +98,11 @@
|
|||
forLogin: (NSString *) login;
|
||||
- (NSString *) userSettingsForLogin: (NSString *) theLogin;
|
||||
|
||||
- (void) setFailedCount: (int) theCount
|
||||
forLogin: (NSString *) theLogin;
|
||||
|
||||
- (NSDictionary *) failedCountForLogin: (NSString *) login;
|
||||
|
||||
//
|
||||
// CAS support
|
||||
//
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/* 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>
|
||||
* Ludovic Marcotte <lmarcotte@inverse.ca>
|
||||
|
@ -40,6 +40,7 @@
|
|||
* cas-ticket:< > value =
|
||||
* cas-pgtiou:< > 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/NSDictionary.h>
|
||||
#import <Foundation/NSLock.h>
|
||||
#import <Foundation/NSValue.h>
|
||||
#import <Foundation/NSString.h>
|
||||
#import <Foundation/NSTimer.h>
|
||||
|
||||
|
@ -327,6 +329,9 @@ static memcached_st *handle = NULL;
|
|||
" no handle exists"), key];
|
||||
}
|
||||
|
||||
//
|
||||
//
|
||||
//
|
||||
- (void) setValue: (NSString *) value
|
||||
forKey: (NSString *) key
|
||||
{
|
||||
|
@ -334,6 +339,9 @@ static memcached_st *handle = NULL;
|
|||
expire: cleanupInterval];
|
||||
}
|
||||
|
||||
//
|
||||
//
|
||||
//
|
||||
- (NSString *) valueForKey: (NSString *) key
|
||||
{
|
||||
NSString *valueString;
|
||||
|
@ -372,6 +380,9 @@ static memcached_st *handle = NULL;
|
|||
return valueString;
|
||||
}
|
||||
|
||||
//
|
||||
//
|
||||
//
|
||||
- (void) removeValueForKey: (NSString *) key
|
||||
{
|
||||
NSData *keyData;
|
||||
|
@ -420,6 +431,9 @@ static memcached_st *handle = NULL;
|
|||
}
|
||||
}
|
||||
|
||||
//
|
||||
//
|
||||
//
|
||||
- (NSString *) _valuesOfType: (NSString *) theType
|
||||
forKey: (NSString *) theKey
|
||||
{
|
||||
|
@ -439,6 +453,9 @@ static memcached_st *handle = NULL;
|
|||
return valueString;
|
||||
}
|
||||
|
||||
//
|
||||
//
|
||||
//
|
||||
- (void) setUserAttributes: (NSString *) theAttributes
|
||||
forLogin: (NSString *) login
|
||||
{
|
||||
|
@ -475,6 +492,60 @@ static memcached_st *handle = NULL;
|
|||
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
|
||||
//
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/* 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>
|
||||
* Francis Lachapelle <flachapelle@inverse.ca>
|
||||
|
@ -86,6 +86,10 @@
|
|||
|
||||
- (BOOL) enablePublicAccess;
|
||||
|
||||
- (int) maximumFailedLoginCount;
|
||||
- (int) maximumFailedLoginInterval;
|
||||
- (int) failedLoginBlockInternval;
|
||||
|
||||
@end
|
||||
|
||||
#endif /* SOGOSYSTEMDEFAULTS_H */
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/* 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>
|
||||
*
|
||||
* Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
|
||||
|
@ -513,4 +513,33 @@ _injectConfigurationFromFile (NSMutableDictionary *defaultsDict,
|
|||
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
|
||||
|
|
|
@ -447,6 +447,9 @@ static Class NSNullK;
|
|||
return checkOK;
|
||||
}
|
||||
|
||||
//
|
||||
//
|
||||
//
|
||||
- (BOOL) checkLogin: (NSString *) _login
|
||||
password: (NSString *) _pwd
|
||||
domain: (NSString **) _domain
|
||||
|
@ -463,6 +466,9 @@ static Class NSNullK;
|
|||
useCache: YES];
|
||||
}
|
||||
|
||||
//
|
||||
//
|
||||
//
|
||||
- (BOOL) checkLogin: (NSString *) _login
|
||||
password: (NSString *) _pwd
|
||||
domain: (NSString **) _domain
|
||||
|
@ -471,8 +477,9 @@ static Class NSNullK;
|
|||
grace: (int *) _grace
|
||||
useCache: (BOOL) useCache
|
||||
{
|
||||
NSMutableDictionary *currentUser, *failedCount;
|
||||
NSString *dictPassword, *username, *jsonUser;
|
||||
NSMutableDictionary *currentUser;
|
||||
SOGoSystemDefaults *dd;
|
||||
BOOL checkOK;
|
||||
|
||||
// We check for cached passwords. If the entry is cached, we
|
||||
|
@ -482,6 +489,41 @@ static Class NSNullK;
|
|||
username = [NSString stringWithFormat: @"%@@%@", _login, *_domain];
|
||||
else
|
||||
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];
|
||||
currentUser = [jsonUser objectFromJSONString];
|
||||
dictPassword = [currentUser objectForKey: @"password"];
|
||||
|
@ -514,7 +556,16 @@ static Class NSNullK;
|
|||
forLogin: username];
|
||||
}
|
||||
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
|
||||
// to the user's value if bindAsCurrentUser is set to true in the
|
||||
|
@ -538,6 +589,9 @@ static Class NSNullK;
|
|||
return checkOK;
|
||||
}
|
||||
|
||||
//
|
||||
//
|
||||
//
|
||||
- (BOOL) changePasswordForLogin: (NSString *) login
|
||||
inDomain: (NSString *) domain
|
||||
oldPassword: (NSString *) oldPassword
|
||||
|
|
|
@ -172,6 +172,9 @@
|
|||
andJSONRepresentation: jsonError];
|
||||
}
|
||||
|
||||
//
|
||||
//
|
||||
//
|
||||
- (id <WOActionResults>) connectAction
|
||||
{
|
||||
WOResponse *response;
|
||||
|
|
Loading…
Reference in New Issue