Skip to content

Commit

Permalink
feat(core): Support ARGON2I/ARGON2ID password hashes
Browse files Browse the repository at this point in the history
Requires libsodium ≥ 1.0.9 to be present at compile time.

Thus, the following distributions will have support for at least
ARGON2i:

* rhel ≥ 7 (with EPEL enabled)
* Debian ≥ 9 (stretch)
* Ubuntu ≥ Bionic (18.04)

Fixes #4895
  • Loading branch information
the-nic committed Jul 1, 2020
1 parent 2be7bab commit 4c27826
Show file tree
Hide file tree
Showing 12 changed files with 237 additions and 28 deletions.
28 changes: 18 additions & 10 deletions Documentation/SOGoInstallationGuide.asciidoc
Expand Up @@ -229,10 +229,16 @@ Some of the softwares on which SOGo depends are available from the repository
"Extra Packages for Enterprise Linux" (EPEL). To add EPEL to your packages
sources, install the following package:
On RHEL/CentOS 7,
----
rpm -ivh https://dl.fedoraproject.org/pub/epel/epel-release-latest-7.noarch.rpm
----
For RHEL/CentOS 8
----
yum install https://dl.fedoraproject.org/pub/epel/epel-release-latest-8.noarch.rpm
---
SOGo relies on the GNUstep packages provided by Inverse and must not use the
packages from EPEL. Adjust the repository definition to exclude those packages:
Expand Down Expand Up @@ -694,7 +700,7 @@ Defaults to `NO` when unset.
For this feature to work properly when authenticating against AD or
Samba4, the LDAP connection must use SSL/TLS. Server side restrictions
can also cause the password change to fail, in which case SOGo will only
log a 'Constraint violation (0x13)' error. These restrictions include
log a 'Constraint violation (0x13)' error. These restrictions include
password too young, complexity constraints not satisfied, user cannot
change password, etc... Also note that Samba has a minimum password age
of 1 day by default.
Expand Down Expand Up @@ -1299,9 +1305,9 @@ tweaking the `javax.net.ssl.trustStore` setting, either in the
the SOGo certificate can also be added to the truststore as follows:
----
openssl x509 -in /etc/ssl/certs/sogo-cert.pem -outform DER \
openssl x509 -in /etc/ssl/certs/sogo-cert.pem -outform DER \
-out /tmp/sogo-cert.der
keytool -import -keystore /etc/ssl/certs/java/cacerts \
keytool -import -keystore /etc/ssl/certs/java/cacerts \
-file /tmp/sogo-cert.der -alias sogo-cert
# The keystore password is 'changeit'
# tomcat must be restarted after this operation
Expand All @@ -1315,7 +1321,7 @@ that file differs between distributions). Basically:
----
# export tomcat's cert to openssl format
keytool -keystore /etc/tomcat7/keystore -exportcert -alias tomcat | \
keytool -keystore /etc/tomcat7/keystore -exportcert -alias tomcat | \
openssl x509 -inform der >tomcat.pem
Enter keystore password: tomcat
Expand Down Expand Up @@ -1652,7 +1658,7 @@ present. Required columns are:
* `c_name`: will be used to uniquely identify entries - which can be
identical to `c_uid`
* `c_password`: password of the user, plain text, crypt, md5 or sha
encoded
encoded
* `c_cn`: the user's common name
* `mail`: the user's email address
Expand All @@ -1666,6 +1672,8 @@ passwords. Possible values are: `none`, `plain`, `crypt`, `md5`,
`md5-crypt`, `smd5`, `cram-md5`, `ldap-md5`, and `sha`, `sha256`,
`sha256-crypt`, `sha512`, `sha512-crypt`, its ssha (e.g. `ssha` or
`ssha256`) variants, `blf-crypt`, `PBKDF2`, and `sym-aes-128-cbc`.
The `argon2i` and `argon2id` password hashing algorithms are supported
if SOGo is compiled with `libsodium`.
Passwords can have the scheme prepended in the form
`{scheme}encryptedPass`.
Expand Down Expand Up @@ -2707,7 +2715,7 @@ objectClass: inetOrgPerson
objectClass: person
objectClass: organizationalPerson
uid: sogo
cn: SOGo Administrator
cn: SOGo Administrator
mail: sogo@acme.com
sn: Administrator
givenName: SOGo
Expand Down Expand Up @@ -2774,8 +2782,8 @@ Once installed, simply uncomment the following lines from your SOGo
Apache configuration:
----
ProxyPass /Microsoft-Server-ActiveSync \
http://127.0.0.1:20000/SOGo/Microsoft-Server-ActiveSync \
ProxyPass /Microsoft-Server-ActiveSync \
http://127.0.0.1:20000/SOGo/Microsoft-Server-ActiveSync \
retry=60 connectiontimeout=5 timeout=360
----
Expand Down Expand Up @@ -2861,7 +2869,7 @@ supported.
Free/Busy feature of Outlook 2013/2016. Please
see http://support.microsoft.com/kb/291621 for configuration
instructions. On the SOGo side, _SOGoEnablePublicAccess_ must be set to
`YES` and the URL to use must be of the following format:
`YES` and the URL to use must be of the following format:
`http://<hostname>/SOGo/dav/public/%NAME%/freebusy.ifb`
* If you have very large mail folders (thousands of messages), you will
need to adjust the word size of your IMAP server. In Dovecot, the parameter
Expand Down Expand Up @@ -3154,7 +3162,7 @@ install -d -m 750 -o sogo -g sogo /etc/sogo
sudo -u sogo sogo-tool dump-defaults > /etc/sogo/sogo.conf
chown root:sogo /etc/sogo/sogo.conf
chmod 640 /etc/sogo/sogo.conf
sudo -u sogo mv ~/GNUstep/Defaults/.GNUstepDefaults \
sudo -u sogo mv ~/GNUstep/Defaults/.GNUstepDefaults \
~/GNUstep/Defaults/GNUstepDefaults.old
----
Expand Down
5 changes: 5 additions & 0 deletions SoObjects/SOGo/GNUmakefile.preamble
Expand Up @@ -47,6 +47,11 @@ ifeq ($(HAS_LIBRARY_oath), yes)
SOGo_LIBRARIES_DEPEND_UPON += $(MFA_LIBS)
endif

ifeq ($(HAS_LIBRARY_sodium), yes)
ADDITIONAL_CPPFLAGS += -DHAVE_SODIUM=1 `pkg-config --cflags libsodium`
SOGo_LIBRARIES_DEPEND_UPON += -lsodium
endif

ifeq ($(findstring openbsd, $(GNUSTEP_HOST_OS)), openbsd)
SOGo_LIBRARIES_DEPEND_UPON += -lcrypto
else
Expand Down
4 changes: 4 additions & 0 deletions SoObjects/SOGo/NSData+Crypto.h
Expand Up @@ -58,6 +58,10 @@
- (NSData *) asCryptUsingSalt: (NSData *) theSalt;
- (NSData *) asMD5CryptUsingSalt: (NSData *) theSalt;
- (NSData *) asBlowfishCryptUsingSalt: (NSData *) theSalt;
#ifdef HAVE_SODIUM
- (NSData *) asArgon2iUsingSalt: (NSData *) theSalt;
- (NSData *) asArgon2idUsingSalt: (NSData *) theSalt;
#endif /* HAVE_SODIUM */

- (NSData *) extractSalt: (NSString *) theScheme;

Expand Down
79 changes: 78 additions & 1 deletion SoObjects/SOGo/NSData+Crypto.m
Expand Up @@ -49,6 +49,10 @@
#error this module requires either gnutls or openssl
#endif

#ifdef HAVE_SODIUM
#include <sodium.h>
#endif

#include "aes.h"
#include "crypt_blowfish.h"
#include "lmhash.h"
Expand Down Expand Up @@ -267,6 +271,18 @@ - (NSData *) asCryptedPassUsingScheme: (NSString *) passwordScheme
{
return [self asPBKDF2SHA1UsingSalt: theSalt];
}
#ifdef HAVE_SODIUM
else if ([passwordScheme caseInsensitiveCompare: @"argon2i"] == NSOrderedSame)
{
return [self asArgon2iUsingSalt: theSalt];
}
# ifdef crypto_pwhash_ALG_ARGON2ID13
else if ([passwordScheme caseInsensitiveCompare: @"argon2id"] == NSOrderedSame)
{
return [self asArgon2idUsingSalt: theSalt];
}
# endif /* crypto_pwhash_ALG_ARGON2ID13 */
#endif /* HAVE_SODIUM */
else if ([[passwordScheme lowercaseString] hasPrefix: @"sym"])
{
// We first support one sym cipher, AES-128-CBC. If something else is provided
Expand Down Expand Up @@ -309,7 +325,7 @@ - (NSData *) asCryptedPassUsingScheme: (NSString *) passwordScheme
* clear text password using the passed encryption scheme
*
* @param passwordScheme The password scheme to use for comparison
* @param thePassword
* @param thePassword cleartext key
*/
- (BOOL) verifyUsingScheme: (NSString *) passwordScheme
withPassword: (NSData *) thePassword
Expand All @@ -321,6 +337,30 @@ - (BOOL) verifyUsingScheme: (NSString *) passwordScheme
salt = [self extractSalt: passwordScheme];
if (salt == nil)
return NO;

#ifdef HAVE_SODIUM
// use verification function provided by libsodium
if ([passwordScheme caseInsensitiveCompare: @"argon2i"] == NSOrderedSame
#ifdef crypto_pwhash_ALG_ARGON2ID13
|| [passwordScheme caseInsensitiveCompare: @"argon2id"] == NSOrderedSame
#endif /* crypto_pwhash_ALG_ARGON2ID13 */
)
{
NSString *cryptString;
int result;

if (sodium_init() < 0)
return NO;
// For the sodium comparison we need to pass a null-terminated string
// as the first parameter
cryptString = [[NSString alloc] initWithData: self encoding: NSUTF8StringEncoding];
const char* pass = [thePassword bytes];
result = crypto_pwhash_str_verify([cryptString UTF8String], pass, [thePassword length]);
[cryptString release];
return result == 0;
}
#endif /* HAVE_SODIUM */

// encrypt self with the salt an compare the results
passwordCrypted = [thePassword asCryptedPassUsingScheme: passwordScheme
withSalt: salt
Expand Down Expand Up @@ -916,6 +956,43 @@ - (NSData *) asPBKDF2SHA1UsingSalt: (NSData *) theSalt
return [result dataUsingEncoding:NSUTF8StringEncoding];
}


#ifdef HAVE_SODIUM
- (NSData *) asArgon2iUsingSalt: (NSData *) theSalt
{
char hashed_password[crypto_pwhash_argon2i_STRBYTES];
int rounds = crypto_pwhash_argon2i_OPSLIMIT_INTERACTIVE;
size_t memlimit = crypto_pwhash_argon2i_MEMLIMIT_INTERACTIVE;

if (sodium_init() < 0)
return nil;

const char* password = [self bytes];
if (crypto_pwhash_argon2i_str(hashed_password, password, [self length], rounds, memlimit) != 0)
return nil;

return [NSData dataWithBytes: hashed_password length: strlen(hashed_password)];
}

# ifdef crypto_pwhash_ALG_ARGON2ID13
- (NSData *) asArgon2idUsingSalt: (NSData *) theSalt;
{
char hashed_password[crypto_pwhash_argon2id_STRBYTES];
int rounds = crypto_pwhash_argon2id_OPSLIMIT_INTERACTIVE;
size_t memlimit = crypto_pwhash_argon2id_MEMLIMIT_INTERACTIVE;

if (sodium_init() < 0)
return nil;

const char* password = [self bytes];
if (crypto_pwhash_argon2id_str(hashed_password, password, [self length], rounds, memlimit) != 0)
return nil;

return [NSData dataWithBytes: hashed_password length: strlen(hashed_password)];
}
#endif /* crypto_pwhash_ALG_ARGON2ID13 */
#endif /* HAVE_SODIUM */

/**
* Get the salt from a password encrypted with a specied scheme
*
Expand Down
6 changes: 5 additions & 1 deletion Tests/Unit/GNUmakefile
Expand Up @@ -46,10 +46,14 @@ ADDITIONAL_LIB_DIRS += \
-Wl,-rpath,../../SoObjects/SOGo/SOGo.framework/Versions/Current/sogo -Wl,-rpath,../../SOPE/NGCards/obj -Wl,-rpath,../../SOPE/GDLContentStore/obj
ADDITIONAL_LDFLAGS += -Wl,--no-as-needed

ifeq ($(HAS_LIBRARY_sodium), yes)
ADDITIONAL_CPPFLAGS += -DHAVE_SODIUM=1 `pkg-config --cflags libsodium`
endif


-include GNUmakefile.preamble
include $(GNUSTEP_MAKEFILES)/test-tool.make
-include GNUmakefile.postamble

check :: $(TEST_TOOL)
./obj/sogo-tests

29 changes: 29 additions & 0 deletions Tests/Unit/TestNSString+Crypto.m
Expand Up @@ -112,4 +112,33 @@ - (void) test_pbkdf2
test([pbkdf2_key isEqualToCrypted:pkbf2_result withDefaultScheme: @"PBKDF2" keyPath: nil]);
}

#ifdef HAVE_SODIUM
- (void) test_argon2
{
NSString *error;
// well-known comparison
NSString *cleartext = @"123456";
NSString *hash = @"{ARGON2I}$argon2i$v=19$m=32768,t=4,p=1$HWg68rEbwmY6yrdByJ7U1g$z1c06BysT+51u1RXGtYIknTpA9jAHUfw1dAqPgTiQJ8";
NSString *prefix;
NSString *crypted_hash;

error = [NSString stringWithFormat:
@"string '%@' wrong ARGON2ID: '%@'",
cleartext, hash];
testWithMessage([cleartext isEqualToCrypted:hash withDefaultScheme: @"CRYPT" keyPath: nil], error);

// generate a new argon2id key
prefix = @"$argon2id$";
crypted_hash = [cleartext asCryptedPassUsingScheme: @"ARGON2ID" keyPath: nil];
fprintf(stdout, "hash = %s\n", [crypted_hash UTF8String]);

error = [NSString stringWithFormat:
@"returned hash '%@' has incorrect ARGON2ID prefix: '%@'",
crypted_hash, prefix];

testWithMessage([crypted_hash hasPrefix: prefix], error);
test([cleartext isEqualToCrypted:crypted_hash withDefaultScheme: @"ARGON2ID" keyPath: nil]);
}
#endif /* HAVE_SODUM */

@end
19 changes: 17 additions & 2 deletions configure
Expand Up @@ -26,6 +26,7 @@ ARG_WITH_DEBUG=1
ARG_WITH_STRIP=0
ARG_ENABLE_SAML2=0
ARG_ENABLE_MFA=0
ARG_ENABLE_SODIUM=1
ARG_WITH_LDAP_CONFIG=0

GNUSTEP_INSTALLATION_DOMAIN="LOCAL"
Expand Down Expand Up @@ -78,7 +79,7 @@ Installation directories:
--with-ssl=SSL specify ssl library (none, ssl, gnutls, auto) [auto]
--enable-saml2 enable support for SAML2 authentication (requires liblasso)
--enable-mfa enable multi-factor authentication (requires liboath)
--disable-sodium disable building with libsodium (will disable argon2 password schemes)
--enable-ldap-config enable LDAP based configuration of SOGo
_ACEOF
Expand Down Expand Up @@ -113,7 +114,12 @@ printParas() {
else
echo " mfa support: no";
fi
if test $ARG_WITH_LDAP_CONFIG = 1; then
if test $ARG_ENABLE_SODIUM = 1; then
echo " argon2 support: yes";
else
echo " argon2 support: no";
fi
if test $ARG_WITH_LDAP_CONFIG = 1; then
echo " ldap-based configuration: yes";
else
echo " ldap-based configuration: no";
Expand Down Expand Up @@ -407,6 +413,9 @@ checkDependencies() {
cfgwrite "MFA_LIBS := -loath"
fi;
fi
if test "x$ARG_ENABLE_SODIUM" = "x1"; then
checkLinking "sodium" required;
fi
if test "x$ARG_CFGSSL" = "xauto"; then
checkLinking "ssl" optional;
if test $? != 0; then
Expand Down Expand Up @@ -501,6 +510,12 @@ processOption() {
"x--enable-mfa")
ARG_ENABLE_MFA=1
;;
"x--enable-sodium")
ARG_ENABLE_SODIUM=1
;;
"x--disable-sodium")
ARG_ENABLE_SODIUM=0
;;
"x--enable-ldap-config")
ARG_WITH_LDAP_CONFIG=1
;;
Expand Down
2 changes: 1 addition & 1 deletion packaging/debian-multiarch/control
@@ -1,7 +1,7 @@
Source: sogo
Priority: optional
Maintainer: Inverse Support <support@inverse.ca>
Build-Depends: debhelper (>= 8.0.0), gobjc | objc-compiler, libgnustep-base-dev, libsope-appserver4.9-dev, libsope-core4.9-dev, libsope-gdl1-4.9-dev, libsope-ldap4.9-dev, libsope-mime4.9-dev, libsope-xml4.9-dev, libmemcached-dev, libxml2-dev, libsbjson-dev, libssl-dev, libcurl4-openssl-dev | libcurl4-gnutls-dev, libwbxml2-dev (>= 0.11.2), liblasso3-dev (>= 2.3.5), libzip-dev
Build-Depends: debhelper (>= 8.0.0), gobjc | objc-compiler, libgnustep-base-dev, libsope-appserver4.9-dev, libsope-core4.9-dev, libsope-gdl1-4.9-dev, libsope-ldap4.9-dev, libsope-mime4.9-dev, libsope-xml4.9-dev, libmemcached-dev, libxml2-dev, libsbjson-dev, libssl-dev, libcurl4-openssl-dev | libcurl4-gnutls-dev, libwbxml2-dev (>= 0.11.2), liblasso3-dev (>= 2.3.5), libzip-dev, libsodium-dev (>= 1.0.9) | base-files (<< 9.4ubuntu4~)
Section: web
Standards-Version: 3.9.2

Expand Down

0 comments on commit 4c27826

Please sign in to comment.