diff --git a/SoObjects/SOGo/SOGoUser.h b/SoObjects/SOGo/SOGoUser.h index 7cb85ef37..927a48246 100644 --- a/SoObjects/SOGo/SOGoUser.h +++ b/SoObjects/SOGo/SOGoUser.h @@ -124,7 +124,8 @@ - (BOOL) isSuperUser; - (BOOL) canAuthenticate; -- (NSString *) totpKey; +- (NSString *) totpKey: (bool) isCheck; +- (NSString *) oldtotpKey; /* resource */ - (BOOL) isResource; diff --git a/SoObjects/SOGo/SOGoUser.m b/SoObjects/SOGo/SOGoUser.m index 675b88aa8..14ad6b6cd 100644 --- a/SoObjects/SOGo/SOGoUser.m +++ b/SoObjects/SOGo/SOGoUser.m @@ -1298,7 +1298,7 @@ return [authValue boolValue]; } -- (NSString *) totpKey +- (NSString *) totpKey: (bool) isCheck { #if defined(MFA_CONFIG) NSString *key, *result; @@ -1307,6 +1307,51 @@ size_t s_len, secret_len; + //Until 5.12.4, SOGo had two problems with totp: + // * It was not renew after a user disable it/renable it. + // * The length was too small: 12 instead of the recommanded 20 + + if(![_defaults totpEnabled]) + { + //Totp was not enabled + //Only renew if this is not a check (happen when the user enable it for the first time and save its preferences + //the saveAction will check the totp code but [_defaults totpEnabled] is still False ) + key = [[self userSettings] userCurrentTotpKey: !isCheck]; + } + else + { + //Totp currently enabled + key = [[self userSettings] userCurrentTotpKey: NO]; + } + + s = [key UTF8String]; + s_len = strlen(s); + + oath_init(); + oath_base32_encode(s,s_len, &secret, &secret_len); + oath_done(); + + result = [[NSString alloc] initWithBytesNoCopy: secret + length: secret_len + encoding: NSASCIIStringEncoding + freeWhenDone: YES]; + + return [result autorelease]; +#else + return nil; +#endif +} + +- (NSString *) oldtotpKey +{ +#if defined(MFA_CONFIG) + //Was used before 5.12.5, is to not make obsolete totp profile set before + NSString *key, *result; + const char *s; + char *secret; + + size_t s_len, secret_len; + 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 280943953..749b2f751 100644 --- a/SoObjects/SOGo/SOGoUserSettings.h +++ b/SoObjects/SOGo/SOGoUserSettings.h @@ -35,6 +35,8 @@ - (NSArray *) subscribedCalendars; - (NSArray *) subscribedAddressBooks; - (NSString *) userPrivateSalt; +- (NSString *) userCurrentTotpKey: (bool) renew; +- (void) setTotpKey: (NSString* ) newKey; - (NSString *) userPublicSalt; - (void)enableForceResetPassword; - (void)disableForceResetPassword; diff --git a/SoObjects/SOGo/SOGoUserSettings.m b/SoObjects/SOGo/SOGoUserSettings.m index f4f6d58a6..c73506756 100644 --- a/SoObjects/SOGo/SOGoUserSettings.m +++ b/SoObjects/SOGo/SOGoUserSettings.m @@ -116,6 +116,42 @@ static Class SOGoUserProfileKlass = Nil; return salt; } +- (NSString *) userCurrentTotpKey: (bool) renew +{ + NSMutableDictionary *values; + NSString *key; + + key = [[self dictionaryForKey: @"General"] objectForKey: @"totpKey"]; + + if (!key || renew) + { + key = [[[[NSProcessInfo processInfo] globallyUniqueString] asSHA1String] substringToIndex: 20]; + values = [self objectForKey: @"General"]; + + if (!values) + values = [NSMutableDictionary dictionary]; + + [values setObject: key forKey: @"totpKey"]; + [self setObject: values forKey: @"General"]; + [self synchronize]; + } + + return key; +} + +- (void) setTotpKey: (NSString* ) newKey +{ + NSMutableDictionary *values; + values = [self objectForKey: @"General"]; + + if (!values) + values = [NSMutableDictionary dictionary]; + + [values setObject: newKey forKey: @"totpKey"]; + [self setObject: values forKey: @"General"]; + [self synchronize]; +} + - (void) enableForceResetPassword { [self setObject: [NSNumber numberWithInt:1] forKey: @"ForceResetPassword"]; diff --git a/UI/MainUI/SOGoRootPage.m b/UI/MainUI/SOGoRootPage.m index ff24f4419..c5cde4ae4 100644 --- a/UI/MainUI/SOGoRootPage.m +++ b/UI/MainUI/SOGoRootPage.m @@ -338,7 +338,7 @@ static const NSString *kJwtKey = @"jwt"; const auto time_step = OATH_TOTP_DEFAULT_TIME_STEP_SIZE; const auto digits = 6; - real_secret = [[loggedInUser totpKey] UTF8String]; + real_secret = [[loggedInUser totpKey: YES] UTF8String]; auto result = oath_init(); auto t = time(NULL); @@ -366,13 +366,56 @@ static const NSString *kJwtKey = @"jwt"; if (code != [verificationCode unsignedIntValue]) { - [self logWithFormat: @"Invalid TOTP key for '%@'", username]; - [json setObject: [NSNumber numberWithInt: 1] - forKey: @"totpInvalidKey"]; - return [self responseWithStatus: 403 - andJSONRepresentation: json]; + //With 5.12.5, the totpKey has changed (non longer from salt but from a propoer totpkey parameter) + //To avoid making all old totp configuration obsolete, we're trying the verification code with + //the old method first + unsigned int old_code; + const char *old_real_secret; + char *old_secret; + + size_t old_secret_len; + + const auto old_time_step = OATH_TOTP_DEFAULT_TIME_STEP_SIZE; + const auto old_digits = 6; + + old_real_secret = [[loggedInUser oldtotpKey] UTF8String]; + + auto old_result = oath_init(); + auto old_time = time(NULL); + auto old_left = old_time_step - (old_time % old_time_step); + + char old_otp[old_digits + 1]; + + oath_base32_decode (old_real_secret, + strlen(old_real_secret), + &old_secret, &old_secret_len); + + old_result = oath_totp_generate2(old_secret, + old_secret_len, + old_time, + old_time_step, + OATH_TOTP_DEFAULT_START_TIME, + old_digits, + 0, + old_otp); + + sscanf(old_otp, "%u", &old_code); + + oath_done(); + free(old_secret); + + if (old_code != [verificationCode unsignedIntValue]) + { + [self logWithFormat: @"Invalid TOTP key for '%@'", username]; + [json setObject: [NSNumber numberWithInt: 1] forKey: @"totpInvalidKey"]; + return [self responseWithStatus: 403 andJSONRepresentation: json]; + } + else { + //Move the old secret to the new parameter + [us setTotpKey: [[us userPrivateSalt] substringToIndex: 12]]; + } } - } // if ([verificationCode length] == 6 && [verificationCode unsignedIntValue] > 0) + } else { if ([us dictionaryForKey: @"General"] && ![[us dictionaryForKey: @"General"] objectForKey: @"PrivateSalt"]) diff --git a/UI/PreferencesUI/UIxPreferences.m b/UI/PreferencesUI/UIxPreferences.m index 9e2e66bf8..c8fae8007 100644 --- a/UI/PreferencesUI/UIxPreferences.m +++ b/UI/PreferencesUI/UIxPreferences.m @@ -1123,7 +1123,7 @@ static NSArray *reminderValues = nil; - (NSString *) totpKey { - return [[context activeUser] totpKey]; + return [[context activeUser] totpKey: NO]; } // @@ -1910,7 +1910,7 @@ static NSArray *reminderValues = nil; const auto time_step = OATH_TOTP_DEFAULT_TIME_STEP_SIZE; const auto digits = 6; - real_secret = [[user totpKey] UTF8String]; + real_secret = [[user totpKey: YES] UTF8String]; auto result = oath_init(); auto t = time(NULL);