diff --git a/SoObjects/Appointments/SOGoCalendarComponent.m b/SoObjects/Appointments/SOGoCalendarComponent.m
index 44d15c3f5..4fa3a1bdb 100644
--- a/SoObjects/Appointments/SOGoCalendarComponent.m
+++ b/SoObjects/Appointments/SOGoCalendarComponent.m
@@ -234,7 +234,7 @@
tags = [NSArray arrayWithObjects: @"DTSTAMP", @"DTSTART", @"DTEND", @"DUE", @"EXDATE", @"EXRULE", @"RRULE", @"RECURRENCE-ID", nil];
uid = [[component uid] asCryptedPassUsingScheme: @"ssha256"
- withSalt: [[settings userSalt] dataUsingEncoding: NSASCIIStringEncoding]
+ withSalt: [[settings userPublicSalt] dataUsingEncoding: NSASCIIStringEncoding]
andEncoding: encHex
keyPath: nil];
diff --git a/SoObjects/SOGo/SOGoUser.m b/SoObjects/SOGo/SOGoUser.m
index dc9b7773a..fe7751fcd 100644
--- a/SoObjects/SOGo/SOGoUser.m
+++ b/SoObjects/SOGo/SOGoUser.m
@@ -1138,7 +1138,7 @@
size_t s_len, secret_len;
- key = [[[self userSettings] userSalt] substringToIndex: 12];
+ key = [[[self userSettings] userPrivateSalt] substringToIndex: 12];
s = [key UTF8String];
s_len = strlen(s);
diff --git a/SoObjects/SOGo/SOGoUserSettings.h b/SoObjects/SOGo/SOGoUserSettings.h
index 7a3958ae3..973022660 100644
--- a/SoObjects/SOGo/SOGoUserSettings.h
+++ b/SoObjects/SOGo/SOGoUserSettings.h
@@ -1,6 +1,6 @@
/* SOGoUserSettings.h - this file is part of SOGo
*
- * Copyright (C) 2009-2016 Inverse inc.
+ * Copyright (C) 2009-2021 Inverse inc.
*
* This file is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -33,7 +33,8 @@
- (NSArray *) subscribedCalendars;
- (NSArray *) subscribedAddressBooks;
-- (NSString *) userSalt;
+- (NSString *) userPrivateSalt;
+- (NSString *) userPublicSalt;
@end
diff --git a/SoObjects/SOGo/SOGoUserSettings.m b/SoObjects/SOGo/SOGoUserSettings.m
index eb71d84c1..f03b77488 100644
--- a/SoObjects/SOGo/SOGoUserSettings.m
+++ b/SoObjects/SOGo/SOGoUserSettings.m
@@ -1,6 +1,6 @@
/* SOGoUserSettings.m - this file is part of SOGo
*
- * Copyright (C) 2009-2016 Inverse inc.
+ * Copyright (C) 2009-2021 Inverse inc.
*
* This file is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -70,7 +70,7 @@ static Class SOGoUserProfileKlass = Nil;
return [self _subscribedFoldersForModule: @"Contacts"];
}
-- (NSString *) userSalt
+- (NSString *) userPublicSalt
{
NSMutableDictionary *values;
NSString *salt;
@@ -93,5 +93,27 @@ static Class SOGoUserProfileKlass = Nil;
return salt;
}
+- (NSString *) userPrivateSalt
+{
+ NSMutableDictionary *values;
+ NSString *salt;
+
+ salt = [[self dictionaryForKey: @"General"] objectForKey: @"PrivateSalt"];
+
+ if (!salt)
+ {
+ salt = [[[NSProcessInfo processInfo] globallyUniqueString] asSHA1String];
+ values = [self objectForKey: @"General"];
+
+ if (!values)
+ values = [NSMutableDictionary dictionary];
+
+ [values setObject: salt forKey: @"PrivateSalt"];
+ [self setObject: values forKey: @"General"];
+ [self synchronize];
+ }
+
+ return salt;
+}
@end
diff --git a/UI/MainUI/English.lproj/Localizable.strings b/UI/MainUI/English.lproj/Localizable.strings
index c13c28b56..b8afe01b4 100644
--- a/UI/MainUI/English.lproj/Localizable.strings
+++ b/UI/MainUI/English.lproj/Localizable.strings
@@ -26,6 +26,7 @@
"Verification Code" = "Verification Code";
"Enter the 6-digit verification code from your TOTP application." = "Enter the 6-digit verification code from your TOTP application.";
"You provided an invalid TOTP key." = "You provided an invalid TOTP key.";
+"Two-factor authentication has been disabled. Visit the Preferences module to restore two-factor authentication and reconfigure your TOTP application." = "Two-factor authentication has been disabled. Visit the Preferences module to restore two-factor authentication and reconfigure your TOTP application.";
"Download" = "Download";
"Language" = "Language";
diff --git a/UI/MainUI/SOGoRootPage.m b/UI/MainUI/SOGoRootPage.m
index fdf30249c..38a03d34b 100644
--- a/UI/MainUI/SOGoRootPage.m
+++ b/UI/MainUI/SOGoRootPage.m
@@ -202,6 +202,7 @@
WOCookie *authCookie, *xsrfCookie;
SOGoWebAuthenticator *auth;
SOGoUserDefaults *ud;
+ SOGoUserSettings *us;
SOGoUser *loggedInUser;
NSDictionary *params;
NSString *username, *password, *language, *domain, *remoteHost;
@@ -235,7 +236,7 @@
|| (expire < 0 && grace > 0) // password expired, grace still permits login
|| (expire >= 0 && grace == -1))) // password about to expire OR ppolicy activated and passwd never changed
{
- NSDictionary *json;
+ NSMutableDictionary *json = [NSMutableDictionary dictionary];
[self logWithFormat: @"successful login from '%@' for user '%@' - expire = %d grace = %d", remoteHost, username, expire, grace];
@@ -248,9 +249,10 @@
username = [[SOGoUserManager sharedUserManager] getUIDForEmail: username];
loggedInUser = [SOGoUser userWithLogin: username];
+ ud = [loggedInUser userDefaults];
#if defined(MFA_CONFIG)
- if ([[loggedInUser userDefaults] totpEnabled])
+ if ([ud totpEnabled])
{
NSString *verificationCode;
@@ -295,29 +297,45 @@
if (code != [verificationCode unsignedIntValue])
{
[self logWithFormat: @"Invalid TOTP key for '%@'", username];
- json = [NSDictionary dictionaryWithObject: [NSNumber numberWithInt: 1]
- forKey: @"totpInvalidKey"];
+ [json setObject: [NSNumber numberWithInt: 1]
+ forKey: @"totpInvalidKey"];
return [self responseWithStatus: 403
- andJSONRepresentation: json];
+ andJSONRepresentation: json];
}
} // if ([verificationCode length] == 6 && [verificationCode unsignedIntValue] > 0)
else
{
- [self logWithFormat: @"Missing TOTP key for '%@', asking it..", username];
- json = [NSDictionary dictionaryWithObject: [NSNumber numberWithInt: 1]
- forKey: @"totpMissingKey"];
- return [self responseWithStatus: 202
- andJSONRepresentation: json];
+ us = [loggedInUser userSettings];
+ if ([us dictionaryForKey: @"General"] && ![[us dictionaryForKey: @"General"] objectForKey: @"PrivateSalt"])
+ {
+ // Since v5.3.0, a new salt is used for TOTP. If it's missing, disable TOTP and alert the user.
+ [ud setTotpEnabled: NO];
+ [ud synchronize];
+
+ [self logWithFormat: @"New TOTP key for '%@' must be created", username];
+ [json setObject: [NSNumber numberWithInt: 1]
+ forKey: @"totpDisabled"];
+ }
+ else
+ {
+ [self logWithFormat: @"Missing TOTP key for '%@', asking it..", username];
+ [json setObject: [NSNumber numberWithInt: 1]
+ forKey: @"totpMissingKey"];
+ return [self responseWithStatus: 202
+ andJSONRepresentation: json];
+ }
}
}
#endif
[self _checkAutoReloadWebCalendars: loggedInUser];
- json = [NSDictionary dictionaryWithObjectsAndKeys:
- [loggedInUser cn], @"cn",
- [NSNumber numberWithInt: expire], @"expire",
- [NSNumber numberWithInt: grace], @"grace", nil];
+ [json setObject: [loggedInUser cn]
+ forKey: @"cn"];
+ [json setObject: [NSNumber numberWithInt: expire]
+ forKey: @"expire"];
+ [json setObject: [NSNumber numberWithInt: grace]
+ forKey: @"grace"];
response = [self responseWithStatus: 200
andJSONRepresentation: json];
@@ -339,7 +357,6 @@
[context setActiveUser: loggedInUser];
if (language && [supportedLanguages containsObject: language])
{
- ud = [loggedInUser userDefaults];
[ud setLanguage: language];
[ud synchronize];
}
diff --git a/UI/Templates/MainUI/SOGoRootPage.wox b/UI/Templates/MainUI/SOGoRootPage.wox
index 7991f2868..6c14e7216 100644
--- a/UI/Templates/MainUI/SOGoRootPage.wox
+++ b/UI/Templates/MainUI/SOGoRootPage.wox
@@ -126,8 +126,8 @@
-