View Issue Details
| ID | Project | Category | View Status | Date Submitted | Last Update |
|---|---|---|---|---|---|
| 0001608 | SOGo | Backend General | public | 2012-01-29 15:28 | 2012-05-31 14:52 |
| Reporter | the_nic | Assigned To | ludovic | ||
| Priority | normal | Severity | feature | Reproducibility | N/A |
| Status | closed | Resolution | fixed | ||
| Product Version | 1.3.11 | ||||
| Target Version | 1.3.16 | Fixed in Version | 1.3.16 | ||
| Summary | 0001608: More password schemes for SQL backend | ||||
| Description | Since the SQL backend only supports a few password schemes, I've created a patch which adds md5-crypt and ssha, sha256 and sha512. Could someone please review this patch? | ||||
| Tags | No tags attached. | ||||
|
2012-01-29 15:28
|
nsstring+crypto.patch (6,614 bytes)
#
# old_revision [df5dd81d62db331ba5c8de9f2257f9430c586534]
#
# patch "SoObjects/SOGo/GNUmakefile"
# from [9e466added539ccacaecd830173afdf9d4755802]
# to [0a6f033637d6af50a1768f0462e2bd4fe4c6909f]
#
# patch "SoObjects/SOGo/NSString+Utilities.h"
# from [09d123e206a68c40e8fc0301e25d2e6052a2abf4]
# to [ed9a551529054cdd3fb175e24db3316b24309987]
#
# patch "SoObjects/SOGo/NSString+Utilities.m"
# from [26a0ba2fa690ba7464585e9959c25229992a66ed]
# to [d7e02c1a2b5633935f3fe16fd8b154e949c01c9b]
#
# patch "SoObjects/SOGo/SQLSource.m"
# from [3ba7f2ddd8b79c5caa63d11a0d9fea1cfe1e354a]
# to [4544aedab94707f1aded7b4de8e5414f9b68a82f]
#
============================================================
--- SoObjects/SOGo/GNUmakefile 9e466added539ccacaecd830173afdf9d4755802
+++ SoObjects/SOGo/GNUmakefile 0a6f033637d6af50a1768f0462e2bd4fe4c6909f
@@ -46,6 +46,7 @@ SOGo_HEADER_FILES = \
NSObject+Utilities.h \
NSString+DAV.h \
NSString+Utilities.h \
+ NSString+Crypto.h \
NSURL+DAV.h \
\
SOGoAuthenticator.h \
@@ -114,6 +115,7 @@ SOGo_OBJC_FILES = \
NSObject+Utilities.m \
NSString+DAV.m \
NSString+Utilities.m \
+ NSString+Crypto.m \
NSURL+DAV.m \
\
SOGoSession.m \
============================================================
--- SoObjects/SOGo/NSString+Utilities.h 09d123e206a68c40e8fc0301e25d2e6052a2abf4
+++ SoObjects/SOGo/NSString+Utilities.h ed9a551529054cdd3fb175e24db3316b24309987
@@ -66,10 +66,6 @@
- (id) objectFromJSONString;
-- (NSString *) asCryptStringUsingSalt: (NSString *) theSalt;
-- (NSString *) asMD5String;
-- (NSString *) asSHA1String;
-
- (NSString *) asSafeSQLString;
- (NSUInteger) countOccurrencesOfString: (NSString *) substring;
============================================================
--- SoObjects/SOGo/NSString+Utilities.m 26a0ba2fa690ba7464585e9959c25229992a66ed
+++ SoObjects/SOGo/NSString+Utilities.m d7e02c1a2b5633935f3fe16fd8b154e949c01c9b
@@ -21,10 +21,6 @@
* Boston, MA 02111-1307, USA.
*/
-#ifndef __OpenBSD__
-#include <crypt.h>
-#endif
-
#import <Foundation/NSArray.h>
#import <Foundation/NSCharacterSet.h>
#import <Foundation/NSData.h>
@@ -44,12 +40,6 @@
#import "NSString+Utilities.h"
-#define _XOPEN_SOURCE 1
-#include <unistd.h>
-#include <openssl/evp.h>
-#include <openssl/md5.h>
-#include <openssl/sha.h>
-
static NSMutableCharacterSet *urlNonEndingChars = nil;
static NSMutableCharacterSet *urlAfterEndingChars = nil;
static NSMutableCharacterSet *urlStartChars = nil;
@@ -522,48 +512,6 @@ static int cssEscapingCount;
return object;
}
-- (NSString *) asCryptStringUsingSalt: (NSString *) theSalt
-{
- char *buf;
-
- // The salt is weak here, but who cares anyway, crypt should not
- // be used anymore
- buf = crypt([self UTF8String], [theSalt UTF8String]);
- return [NSString stringWithUTF8String: buf];
-}
-
-- (NSString *) asMD5String
-{
- unsigned char md[MD5_DIGEST_LENGTH];
- char buf[80];
- int i;
-
- memset(md, 0, MD5_DIGEST_LENGTH);
- memset(buf, 0, 80);
-
- EVP_Digest((const void *) [self UTF8String], strlen([self UTF8String]), md, NULL, EVP_md5(), NULL);
- for (i = 0; i < MD5_DIGEST_LENGTH; i++)
- sprintf(&(buf[i*2]), "%02x", md[i]);
-
- return [NSString stringWithUTF8String: buf];
-}
-
-- (NSString *) asSHA1String
-{
- unsigned char sha[SHA_DIGEST_LENGTH];
- char buf[80];
- int i;
-
- memset(sha, 0, SHA_DIGEST_LENGTH);
- memset(buf, 0, 80);
-
- SHA1((const void *)[self UTF8String], strlen([self UTF8String]), sha);
- for (i = 0; i < SHA_DIGEST_LENGTH; i++)
- sprintf(&(buf[i*2]), "%02x", sha[i]);
-
- return [NSString stringWithUTF8String: buf];
-}
-
- (NSString *) asSafeSQLString
{
return [[self stringByReplacingString: @"\\" withString: @"\\\\"]
============================================================
--- SoObjects/SOGo/SQLSource.m 3ba7f2ddd8b79c5caa63d11a0d9fea1cfe1e354a
+++ SoObjects/SOGo/SQLSource.m 4544aedab94707f1aded7b4de8e5414f9b68a82f
@@ -39,6 +39,7 @@
#import "SOGoConstants.h"
#import "NSString+Utilities.h"
+#import "NSString+Crypto.h"
#import "SQLSource.h"
@@ -47,7 +48,7 @@
*
* c_uid - will be used for authentication - it's a username or username@domain.tld)
* c_name - which can be identical to c_uid - will be used to uniquely identify entries)
- * c_password - password of the user, plain-text, md5 or sha encoded for now
+ * c_password - password of the user, plain-text, md5, sha, ssha, sha256, sha2512 encoded for now
* c_cn - the user's common name
* mail - the user's mail address
*
@@ -151,28 +152,8 @@
if (!plainPassword || !encryptedPassword)
return NO;
- if ([_userPasswordAlgorithm caseInsensitiveCompare: @"none"] == NSOrderedSame)
- {
- return [plainPassword isEqualToString: encryptedPassword];
- }
- else if ([_userPasswordAlgorithm caseInsensitiveCompare: @"crypt"] == NSOrderedSame)
- {
- return [[plainPassword asCryptStringUsingSalt: encryptedPassword] isEqualToString: encryptedPassword];
- }
- else if ([_userPasswordAlgorithm caseInsensitiveCompare: @"md5"] == NSOrderedSame)
- {
- return [[plainPassword asMD5String] isEqualToString: encryptedPassword];
- }
- else if ([_userPasswordAlgorithm caseInsensitiveCompare: @"sha"] == NSOrderedSame)
- {
-
- return [[plainPassword asSHA1String] isEqualToString: encryptedPassword];
- }
-
-
- [self errorWithFormat: @"Unsupported user-password algorithm: %@", _userPasswordAlgorithm];
-
- return NO;
+ return [plainPassword isEqualToCrypted: encryptedPassword
+ withDefaultScheme: _userPasswordAlgorithm];
}
/**
@@ -183,26 +164,8 @@
*/
- (NSString *) _encryptPassword: (NSString *) plainPassword
{
- if ([_userPasswordAlgorithm caseInsensitiveCompare: @"none"] == NSOrderedSame)
- {
- return plainPassword;
- }
- else if ([_userPasswordAlgorithm caseInsensitiveCompare: @"crypt"] == NSOrderedSame)
- {
- return [plainPassword asCryptStringUsingSalt: [plainPassword asMD5String]];
- }
- else if ([_userPasswordAlgorithm caseInsensitiveCompare: @"md5"] == NSOrderedSame)
- {
- return [plainPassword asMD5String];
- }
- else if ([_userPasswordAlgorithm caseInsensitiveCompare: @"sha"] == NSOrderedSame)
- {
- return [plainPassword asSHA1String];
- }
-
- [self errorWithFormat: @"Unsupported user-password algorithm: %@", _userPasswordAlgorithm];
-
- return plainPassword;
+ return [NSString stringWithFormat: @"{%@}%@", _userPasswordAlgorithm,
+ [plainPassword asCryptedPassUsingScheme: _userPasswordAlgorithm] ];
}
//
|
|
Your patch is incomplete - you forgot to show the NSString+Crypto category. |
|
|
Ah ok, I missed adding this to the mtn repository. Updated the patch My main issue with Obj-C was that I have no idea when to use autorelease (which seems to me some kind of garbage collection, right?). |
|
|
2012-01-29 17:48
|
nsstring+crypto_updated.patch (17,022 bytes)
#
# old_revision [df5dd81d62db331ba5c8de9f2257f9430c586534]
#
# add_file "SoObjects/SOGo/NSString+Crypto.h"
# content [05c7e1f20826bdcc22933b8bac31e2d444ae2ef6]
#
# add_file "SoObjects/SOGo/NSString+Crypto.m"
# content [6e74b500ba1b5a864cc8b5cd13c0f2b39c226baf]
#
# patch "SoObjects/SOGo/GNUmakefile"
# from [9e466added539ccacaecd830173afdf9d4755802]
# to [0a6f033637d6af50a1768f0462e2bd4fe4c6909f]
#
# patch "SoObjects/SOGo/NSString+Utilities.h"
# from [09d123e206a68c40e8fc0301e25d2e6052a2abf4]
# to [ed9a551529054cdd3fb175e24db3316b24309987]
#
# patch "SoObjects/SOGo/NSString+Utilities.m"
# from [26a0ba2fa690ba7464585e9959c25229992a66ed]
# to [d7e02c1a2b5633935f3fe16fd8b154e949c01c9b]
#
# patch "SoObjects/SOGo/SQLSource.m"
# from [3ba7f2ddd8b79c5caa63d11a0d9fea1cfe1e354a]
# to [4544aedab94707f1aded7b4de8e5414f9b68a82f]
#
============================================================
--- SoObjects/SOGo/GNUmakefile 9e466added539ccacaecd830173afdf9d4755802
+++ SoObjects/SOGo/GNUmakefile 0a6f033637d6af50a1768f0462e2bd4fe4c6909f
@@ -46,6 +46,7 @@ SOGo_HEADER_FILES = \
NSObject+Utilities.h \
NSString+DAV.h \
NSString+Utilities.h \
+ NSString+Crypto.h \
NSURL+DAV.h \
\
SOGoAuthenticator.h \
@@ -114,6 +115,7 @@ SOGo_OBJC_FILES = \
NSObject+Utilities.m \
NSString+DAV.m \
NSString+Utilities.m \
+ NSString+Crypto.m \
NSURL+DAV.m \
\
SOGoSession.m \
============================================================
--- SoObjects/SOGo/NSString+Utilities.h 09d123e206a68c40e8fc0301e25d2e6052a2abf4
+++ SoObjects/SOGo/NSString+Utilities.h ed9a551529054cdd3fb175e24db3316b24309987
@@ -66,10 +66,6 @@
- (id) objectFromJSONString;
-- (NSString *) asCryptStringUsingSalt: (NSString *) theSalt;
-- (NSString *) asMD5String;
-- (NSString *) asSHA1String;
-
- (NSString *) asSafeSQLString;
- (NSUInteger) countOccurrencesOfString: (NSString *) substring;
============================================================
--- SoObjects/SOGo/NSString+Utilities.m 26a0ba2fa690ba7464585e9959c25229992a66ed
+++ SoObjects/SOGo/NSString+Utilities.m d7e02c1a2b5633935f3fe16fd8b154e949c01c9b
@@ -21,10 +21,6 @@
* Boston, MA 02111-1307, USA.
*/
-#ifndef __OpenBSD__
-#include <crypt.h>
-#endif
-
#import <Foundation/NSArray.h>
#import <Foundation/NSCharacterSet.h>
#import <Foundation/NSData.h>
@@ -44,12 +40,6 @@
#import "NSString+Utilities.h"
-#define _XOPEN_SOURCE 1
-#include <unistd.h>
-#include <openssl/evp.h>
-#include <openssl/md5.h>
-#include <openssl/sha.h>
-
static NSMutableCharacterSet *urlNonEndingChars = nil;
static NSMutableCharacterSet *urlAfterEndingChars = nil;
static NSMutableCharacterSet *urlStartChars = nil;
@@ -522,48 +512,6 @@ static int cssEscapingCount;
return object;
}
-- (NSString *) asCryptStringUsingSalt: (NSString *) theSalt
-{
- char *buf;
-
- // The salt is weak here, but who cares anyway, crypt should not
- // be used anymore
- buf = crypt([self UTF8String], [theSalt UTF8String]);
- return [NSString stringWithUTF8String: buf];
-}
-
-- (NSString *) asMD5String
-{
- unsigned char md[MD5_DIGEST_LENGTH];
- char buf[80];
- int i;
-
- memset(md, 0, MD5_DIGEST_LENGTH);
- memset(buf, 0, 80);
-
- EVP_Digest((const void *) [self UTF8String], strlen([self UTF8String]), md, NULL, EVP_md5(), NULL);
- for (i = 0; i < MD5_DIGEST_LENGTH; i++)
- sprintf(&(buf[i*2]), "%02x", md[i]);
-
- return [NSString stringWithUTF8String: buf];
-}
-
-- (NSString *) asSHA1String
-{
- unsigned char sha[SHA_DIGEST_LENGTH];
- char buf[80];
- int i;
-
- memset(sha, 0, SHA_DIGEST_LENGTH);
- memset(buf, 0, 80);
-
- SHA1((const void *)[self UTF8String], strlen([self UTF8String]), sha);
- for (i = 0; i < SHA_DIGEST_LENGTH; i++)
- sprintf(&(buf[i*2]), "%02x", sha[i]);
-
- return [NSString stringWithUTF8String: buf];
-}
-
- (NSString *) asSafeSQLString
{
return [[self stringByReplacingString: @"\\" withString: @"\\\\"]
============================================================
--- SoObjects/SOGo/SQLSource.m 3ba7f2ddd8b79c5caa63d11a0d9fea1cfe1e354a
+++ SoObjects/SOGo/SQLSource.m 4544aedab94707f1aded7b4de8e5414f9b68a82f
@@ -39,6 +39,7 @@
#import "SOGoConstants.h"
#import "NSString+Utilities.h"
+#import "NSString+Crypto.h"
#import "SQLSource.h"
@@ -47,7 +48,7 @@
*
* c_uid - will be used for authentication - it's a username or username@domain.tld)
* c_name - which can be identical to c_uid - will be used to uniquely identify entries)
- * c_password - password of the user, plain-text, md5 or sha encoded for now
+ * c_password - password of the user, plain-text, md5, sha, ssha, sha256, sha2512 encoded for now
* c_cn - the user's common name
* mail - the user's mail address
*
@@ -151,28 +152,8 @@
if (!plainPassword || !encryptedPassword)
return NO;
- if ([_userPasswordAlgorithm caseInsensitiveCompare: @"none"] == NSOrderedSame)
- {
- return [plainPassword isEqualToString: encryptedPassword];
- }
- else if ([_userPasswordAlgorithm caseInsensitiveCompare: @"crypt"] == NSOrderedSame)
- {
- return [[plainPassword asCryptStringUsingSalt: encryptedPassword] isEqualToString: encryptedPassword];
- }
- else if ([_userPasswordAlgorithm caseInsensitiveCompare: @"md5"] == NSOrderedSame)
- {
- return [[plainPassword asMD5String] isEqualToString: encryptedPassword];
- }
- else if ([_userPasswordAlgorithm caseInsensitiveCompare: @"sha"] == NSOrderedSame)
- {
-
- return [[plainPassword asSHA1String] isEqualToString: encryptedPassword];
- }
-
-
- [self errorWithFormat: @"Unsupported user-password algorithm: %@", _userPasswordAlgorithm];
-
- return NO;
+ return [plainPassword isEqualToCrypted: encryptedPassword
+ withDefaultScheme: _userPasswordAlgorithm];
}
/**
@@ -183,26 +164,8 @@
*/
- (NSString *) _encryptPassword: (NSString *) plainPassword
{
- if ([_userPasswordAlgorithm caseInsensitiveCompare: @"none"] == NSOrderedSame)
- {
- return plainPassword;
- }
- else if ([_userPasswordAlgorithm caseInsensitiveCompare: @"crypt"] == NSOrderedSame)
- {
- return [plainPassword asCryptStringUsingSalt: [plainPassword asMD5String]];
- }
- else if ([_userPasswordAlgorithm caseInsensitiveCompare: @"md5"] == NSOrderedSame)
- {
- return [plainPassword asMD5String];
- }
- else if ([_userPasswordAlgorithm caseInsensitiveCompare: @"sha"] == NSOrderedSame)
- {
- return [plainPassword asSHA1String];
- }
-
- [self errorWithFormat: @"Unsupported user-password algorithm: %@", _userPasswordAlgorithm];
-
- return plainPassword;
+ return [NSString stringWithFormat: @"{%@}%@", _userPasswordAlgorithm,
+ [plainPassword asCryptedPassUsingScheme: _userPasswordAlgorithm] ];
}
//
============================================================
--- /dev/null
+++ SoObjects/SOGo/NSString+Crypto.h 05c7e1f20826bdcc22933b8bac31e2d444ae2ef6
@@ -0,0 +1,48 @@
+/* NSString+Crypto.h - this file is part of SOGo
+ *
+ * Author: Nicolas Höft
+ *
+ * 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
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This file is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef NSSTRING_CRYPTO_H
+#define NSSTRING_CRYPTO_H
+
+#import <Foundation/NSString.h>
+
+@class NSObject;
+
+@interface NSString (SOGoCryptoExtension)
+- (BOOL) isEqualToCrypted: (NSString *) cryptedPassword
+ withDefaultScheme: (NSString *) theScheme;
+
+- (NSString *) asCryptedPassUsingScheme: (NSString *) passwordScheme
+ withSalt: (NSString*) theSalt;
+- (NSString *) asCryptedPassUsingScheme: (NSString *) passwordScheme;
+
++ (NSString *) generateSaltForLength: (unsigned int) theLength;
+- (NSArray *) splitPasswordWithDefaultScheme: (NSString *) defaultScheme;
+
+- (NSString *) asCryptStringUsingSalt: (NSString *) theSalt;
+- (NSString *) asMD5String;
+- (NSString *) asMD5CryptStringUsingSalt: (NSString*) theSalt;
+- (NSString *) asSHA1String;
+- (NSString *) asSSHAStringUsingSalt: (NSString*) theSalt;
+- (NSString *) asSHA256String;
+- (NSString *) asSHA512String;
+@end
+
+#endif /* NSSTRING_CRYPTO_H */
============================================================
--- /dev/null
+++ SoObjects/SOGo/NSString+Crypto.m 6e74b500ba1b5a864cc8b5cd13c0f2b39c226baf
@@ -0,0 +1,290 @@
+/* NSString+Crypto.m - this file is part of SOGo
+ *
+ *
+ * Author: Nicolas Höft
+ *
+ * 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
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This file is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __OpenBSD__
+#include <crypt.h>
+#endif
+
+#import <Foundation/NSArray.h>
+
+#import "NSString+Crypto.h"
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+
+#define _XOPEN_SOURCE 1
+#include <unistd.h>
+#include <openssl/evp.h>
+#include <openssl/md5.h>
+#include <openssl/sha.h>
+
+
+@implementation NSString (SOGoCryptoExtension)
+
++ (NSString *) generateSaltForLength: (unsigned int) theLength
+{
+ char *buf;
+ int fd;
+ int i;
+ char rnd;
+
+ fd = open("/dev/urandom", O_RDONLY);
+
+ if (fd > 0)
+ {
+ i = theLength;
+ buf = (char *)malloc(theLength);
+ while(--i)
+ {
+ read(fd, &rnd, 1);
+ // generate only printable characters between 32 and 94
+ buf[i] = (char)(((float)rnd / 256.)*94. + 32);
+ }
+ close(fd);
+ return [NSString stringWithUTF8String: buf];
+ }
+ return nil;
+}
+
+- (NSString *) extractCryptScheme
+{
+ NSRange r;
+ NSString* pwscheme;
+ int len;
+
+ len = [self length];
+ if(len == 0)
+ return @"";
+ if ([self characterAtIndex:0] != '{')
+ return @"";
+
+ r = [self rangeOfString:@"}" options:(NSLiteralSearch)];
+ if (r.length == 0)
+ return @"";
+
+ r.length = (r.location - 1);
+ r.location = 1;
+ pwscheme = [[self substringWithRange:r] lowercaseString];
+ [pwscheme autorelease];
+ return pwscheme;
+}
+
+- (NSString *) extractSalt: (NSString *) theScheme
+{
+ if([theScheme caseInsensitiveCompare: @"crypt"] == NSOrderedSame ||
+ [theScheme caseInsensitiveCompare: @"md5-crypt"] == NSOrderedSame)
+ {
+ return self;
+ }
+ else if([theScheme caseInsensitiveCompare: @"ssha"] == NSOrderedSame)
+ {
+ return [self substringFromIndex: SHA_DIGEST_LENGTH*2];
+ }
+ return @"";
+}
+
+- (NSArray *) splitPasswordWithDefaultScheme: (NSString *) defaultScheme
+{
+ NSString *scheme;
+ NSString *pass;
+ NSString *salt;
+ NSRange range;
+ int selflen, len;
+
+ selflen = [self length];
+
+ scheme = [self extractCryptScheme];
+ len = [scheme length];
+ if(len > 0)
+ range = NSMakeRange (len+2, selflen-len-2);
+ else
+ range = NSMakeRange (0, selflen);
+ if(len == 0)
+ scheme = defaultScheme;
+
+ pass = [self substringWithRange: range];
+ salt = [pass extractSalt: scheme];
+
+ return [NSArray arrayWithObjects: scheme, pass, salt, nil];
+}
+
+
+- (BOOL) isEqualToCrypted: (NSString *) cryptedPassword
+ withDefaultScheme: (NSString *) theScheme
+{
+ NSArray *passInfo;
+ NSString *selfCrypted;
+
+ passInfo = [cryptedPassword splitPasswordWithDefaultScheme: theScheme];
+ selfCrypted = [self asCryptedPassUsingScheme: [passInfo objectAtIndex:0]
+ withSalt: [passInfo objectAtIndex:2] ];
+ if( [selfCrypted isEqualToString: [passInfo objectAtIndex:1] ] == YES )
+ return YES;
+ return NO;
+}
+
+- (NSString *) asCryptedPassUsingScheme: (NSString *) passwordScheme
+{
+ return [self asCryptedPassUsingScheme: passwordScheme withSalt: @""];
+}
+
+- (NSString *) asCryptedPassUsingScheme: (NSString *) passwordScheme
+ withSalt: (NSString*) theSalt
+{
+ if ([passwordScheme caseInsensitiveCompare: @"none"] == NSOrderedSame ||
+ [passwordScheme caseInsensitiveCompare: @"plain"] == NSOrderedSame ||
+ [passwordScheme caseInsensitiveCompare: @"cleartext"] == NSOrderedSame)
+ {
+ return self;
+ }
+ else if ([passwordScheme caseInsensitiveCompare: @"crypt"] == NSOrderedSame)
+ {
+ return [self asCryptStringUsingSalt: theSalt];
+ }
+ else if ([passwordScheme caseInsensitiveCompare: @"md5-crypt"] == NSOrderedSame)
+ {
+ return [self asMD5CryptStringUsingSalt: theSalt];
+ }
+ else if ([passwordScheme caseInsensitiveCompare: @"md5"] == NSOrderedSame ||
+ [passwordScheme caseInsensitiveCompare: @"plain-md5"] == NSOrderedSame)
+ {
+ return [self asMD5String];
+ }
+ else if ([passwordScheme caseInsensitiveCompare: @"sha"] == NSOrderedSame)
+ {
+ return [self asSHA1String];
+ }
+ else if ([passwordScheme caseInsensitiveCompare: @"ssha"] == NSOrderedSame)
+ {
+ return [self asSSHAStringUsingSalt: theSalt];
+ }
+ else if ([passwordScheme caseInsensitiveCompare: @"sha256"] == NSOrderedSame)
+ {
+ return [self asSHA256String];
+ }
+ else if ([passwordScheme caseInsensitiveCompare: @"sha512"] == NSOrderedSame)
+ {
+ return [self asSHA512String];
+ }
+ return self;
+}
+
+- (NSString *) asCryptStringUsingSalt: (NSString *) theSalt
+{
+ if([theSalt length] == 0) theSalt = [NSString generateSaltForLength: 10];
+ char *buf;
+
+ // The salt is weak here, but who cares anyway, crypt should not
+ // be used anymore
+ buf = crypt([self UTF8String], [theSalt UTF8String]);
+ return [NSString stringWithUTF8String: buf];
+}
+
+- (NSString *) asMD5String
+{
+ unsigned char md[MD5_DIGEST_LENGTH];
+ char buf[MD5_DIGEST_LENGTH*2+1];
+ int i;
+
+ memset(md, 0, MD5_DIGEST_LENGTH);
+ memset(buf, 0, MD5_DIGEST_LENGTH*2+1);
+
+ EVP_Digest((const void *) [self UTF8String], strlen([self UTF8String]), md, NULL, EVP_md5(), NULL);
+ for (i = 0; i < MD5_DIGEST_LENGTH; i++)
+ sprintf(&(buf[i*2]), "%02x", md[i]);
+
+ return [NSString stringWithUTF8String: buf];
+}
+
+- (NSString *) asSSHAStringUsingSalt: (NSString*) theSalt
+{
+ NSString * saltedPass;
+
+ if([theSalt length] == 0) theSalt = [NSString generateSaltForLength: 10];
+
+ saltedPass = [[NSString stringWithFormat: @"%@%@", self, theSalt] asSHA1String];
+ return [NSString stringWithFormat: @"%@%@", saltedPass, theSalt];
+}
+
+- (NSString *) asSHA1String
+{
+ unsigned char sha[SHA_DIGEST_LENGTH];
+ char buf[SHA_DIGEST_LENGTH*2+1];
+ int i;
+
+ memset(sha, 0, SHA_DIGEST_LENGTH);
+ memset(buf, 0, SHA_DIGEST_LENGTH*2+1);
+
+ SHA1((const void *)[self UTF8String], strlen([self UTF8String]), sha);
+ for (i = 0; i < SHA_DIGEST_LENGTH; i++)
+ sprintf(&(buf[i*2]), "%02x", sha[i]);
+
+ return [NSString stringWithUTF8String: buf];
+}
+
+- (NSString *) asSHA256String
+{
+ unsigned char sha[SHA256_DIGEST_LENGTH];
+ char buf[SHA256_DIGEST_LENGTH*2+1];
+ int i;
+
+ memset(sha, 0, SHA256_DIGEST_LENGTH);
+ memset(buf, 0, SHA256_DIGEST_LENGTH*2+1);
+
+ SHA256((const void *)[self UTF8String], strlen([self UTF8String]), sha);
+ for (i = 0; i < SHA256_DIGEST_LENGTH; i++)
+ sprintf(&(buf[i*2]), "%02x", sha[i]);
+
+ return [NSString stringWithUTF8String: buf];
+}
+
+- (NSString *) asSHA512String
+{
+ unsigned char sha[SHA256_DIGEST_LENGTH];
+ char buf[SHA512_DIGEST_LENGTH*2+1];
+ int i;
+
+ memset(sha, 0, SHA512_DIGEST_LENGTH);
+ memset(buf, 0, SHA512_DIGEST_LENGTH*2+1);
+
+ SHA512((const void *)[self UTF8String], strlen([self UTF8String]), sha);
+ for (i = 0; i < SHA512_DIGEST_LENGTH; i++)
+ sprintf(&(buf[i*2]), "%02x", sha[i]);
+
+ return [NSString stringWithUTF8String: buf];
+}
+
+
+- (NSString *) asMD5CryptStringUsingSalt: (NSString*) theSalt
+{
+ char *buf;
+
+ if([theSalt length] == 0)
+ theSalt = [NSString stringWithFormat: @"$1$%@$", [NSString generateSaltForLength: 10]];
+
+ buf = crypt([self UTF8String], [theSalt UTF8String]);
+ return [NSString stringWithUTF8String: buf];
+}
+
+@end
|
|
Any feedback? I'd be willing to improve it, if there are any comments.. |
|
|
This patch looks really great. Do you think you could implement SSHA256 and SSHA512 as well? That would make SOGo fit nicely into dovecot. |
|
|
This shouldn't be an issue to add SSHA256/515, just look for SSHA in the source, the rest is copy&paste and minor adjustment. |
|
|
Could we also make these algorithms available to the LDAP backend as well? |
|
|
I am interested in SSHA for LDAP, too :-) |
|
|
See comments in bug 0001804 about using extended operations to modify ldap passwords. (The encryption/encoding is then left over for the ldap server to descide) |
|
|
I am actually rewriting the patch right now, including SSHA(1/256/512) with base64 encoding in order to make it more compatible with dovecot (see http://wiki.dovecot.org/Authentication/PasswordSchemes ) |
|
|
2012-05-20 11:34
|
crypto_v3.patch (34,277 bytes)
#
# old_revision [471dc0f5e3b2a6fc571aca4e14c0a5161cf7af0b]
#
# add_file "SoObjects/SOGo/NSData+Crypto.h"
# content [c9187ee4b0ebd1f7a2eeeb0f13837493aa284580]
#
# add_file "SoObjects/SOGo/NSData+Crypto.m"
# content [0ea66d4e8b75ff60c4e4c57980860ffcd6fe3572]
#
# add_file "SoObjects/SOGo/NSString+Crypto.h"
# content [50ffbd75973627dda748687423fb4bbb7b798724]
#
# add_file "SoObjects/SOGo/NSString+Crypto.m"
# content [2bb1ed1c269a147c2285a0d2f4629997d20961bf]
#
# patch "SoObjects/SOGo/GNUmakefile"
# from [9e466added539ccacaecd830173afdf9d4755802]
# to [0e4d9af53dddf014fe6fccf1d81e751d8dd2bc3c]
#
# patch "SoObjects/SOGo/LDAPSource.m"
# from [49cd1d275323dd81ba0ad5566e542b725856c4a9]
# to [29be142502d24ed06e5b41dbd330ef24efb4a4b6]
#
# patch "SoObjects/SOGo/NSString+Utilities.h"
# from [d2661ef19253d96d64dcf11127ffe6882f90b4d4]
# to [c38d80d6ebe9592a9f2ee954282718d7265dec51]
#
# patch "SoObjects/SOGo/NSString+Utilities.m"
# from [800b97cfc8f806e14c310ed8628c94a9934fd80a]
# to [eb4c7cd585c64cb8fb0273c3e89be36da11e5f24]
#
# patch "SoObjects/SOGo/SOGoUserManager.m"
# from [9bf968b36558145edd71840dd180876c1d5d7175]
# to [344659dcdf8d929bb824d8856d05c1de85ce801e]
#
# patch "SoObjects/SOGo/SQLSource.m"
# from [8e7fb09f6d34231ed0e1c57db516961d7a8bf0bb]
# to [3928a5ed6dca41cf9fa04a0f15c964b13d000747]
#
============================================================
--- SoObjects/SOGo/GNUmakefile 9e466added539ccacaecd830173afdf9d4755802
+++ SoObjects/SOGo/GNUmakefile 0e4d9af53dddf014fe6fccf1d81e751d8dd2bc3c
@@ -46,6 +46,8 @@ SOGo_HEADER_FILES = \
NSObject+Utilities.h \
NSString+DAV.h \
NSString+Utilities.h \
+ NSString+Crypto.h \
+ NSData+Crypto.h \
NSURL+DAV.h \
\
SOGoAuthenticator.h \
@@ -114,6 +116,8 @@ SOGo_OBJC_FILES = \
NSObject+Utilities.m \
NSString+DAV.m \
NSString+Utilities.m \
+ NSString+Crypto.m \
+ NSData+Crypto.m \
NSURL+DAV.m \
\
SOGoSession.m \
============================================================
--- SoObjects/SOGo/LDAPSource.m 49cd1d275323dd81ba0ad5566e542b725856c4a9
+++ SoObjects/SOGo/LDAPSource.m 29be142502d24ed06e5b41dbd330ef24efb4a4b6
@@ -40,6 +40,7 @@
#import "LDAPSourceSchema.h"
#import "NSArray+Utilities.h"
#import "NSString+Utilities.h"
+#import "NSString+Crypto.h"
#import "SOGoDomainDefaults.h"
#import "SOGoSystemDefaults.h"
@@ -639,26 +640,13 @@ andMultipleBookingsField: (NSString *) n
*/
- (NSString *) _encryptPassword: (NSString *) plainPassword
{
- if ([_userPasswordAlgorithm caseInsensitiveCompare: @"none"] == NSOrderedSame)
- {
- return plainPassword;
- }
- else if ([_userPasswordAlgorithm caseInsensitiveCompare: @"crypt"] == NSOrderedSame)
- {
- return [NSString stringWithFormat: @"{CRYPT}%@", [plainPassword asCryptStringUsingSalt: [plainPassword asMD5String]]];
- }
- else if ([_userPasswordAlgorithm caseInsensitiveCompare: @"md5"] == NSOrderedSame)
- {
- return [NSString stringWithFormat: @"{MD5}%@", [plainPassword asMD5String]];
- }
- else if ([_userPasswordAlgorithm caseInsensitiveCompare: @"sha"] == NSOrderedSame)
- {
- return [NSString stringWithFormat: @"{SHA}%@", [plainPassword asSHA1String]];
- }
-
- [self errorWithFormat: @"Unsupported user-password algorithm: %@", _userPasswordAlgorithm];
-
- return plainPassword;
+ NSString *password;
+ password = [plainPassword asCryptedPassUsingScheme: _userPasswordAlgorithm];
+
+ if (password == nil)
+ [self errorWithFormat: @"Unsupported user-password algorithm: %@", _userPasswordAlgorithm];
+
+ return [NSString stringWithFormat: @"{%@}%@", _userPasswordAlgorithm, password];
}
//
============================================================
--- /dev/null
+++ SoObjects/SOGo/NSData+Crypto.h c9187ee4b0ebd1f7a2eeeb0f13837493aa284580
@@ -0,0 +1,56 @@
+/* NSData+Crypto.h - this file is part of SOGo
+ *
+ * Author: Nicolas Höft
+ *
+ * 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
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This file is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef NSDATA_CRYPTO_H
+#define NSDATA_CRYPTO_H
+
+#import <Foundation/NSData.h>
+
+@class NSObject;
+
+@interface NSData (SOGoCryptoExtension)
+
+- (NSData *) asCryptedPassUsingScheme: (NSString *) passwordScheme
+ withSalt: (NSData *) theSalt;
+
+- (NSData *) asMD5;
+- (NSData *) asSMD5UsingSalt: (NSData *) theSalt;
+- (NSData *) asSHA1;
+- (NSData *) asSSHAUsingSalt: (NSData *) theSalt;
+- (NSData *) asSHA256;
+- (NSData *) asSSHA256UsingSalt: (NSData *) theSalt;
+- (NSData *) asSHA512;
+- (NSData *) asSSHA512UsingSalt: (NSData *) theSalt;
+
+- (NSData *) asCryptUsingSalt: (NSData *) theSalt;
+- (NSData *) asMD5CryptUsingSalt: (NSData *) theSalt;
+
+- (NSData *) extractSalt: (NSString *) theScheme;
+
++ (NSData *) generateSaltForLength: (unsigned int) theLength
+ withBase64: (BOOL) doBase64;
++ (NSData *) generateSaltForLength: (unsigned int) theLength;
+
++ (NSString *) encodeDataAsHexString: (NSData* ) theData;
++ (NSData *) decodeDataFromHexString: (NSString* ) theString;
+
+@end
+
+#endif /* NSDATA_CRYPTO_H */
============================================================
--- /dev/null
+++ SoObjects/SOGo/NSData+Crypto.m 0ea66d4e8b75ff60c4e4c57980860ffcd6fe3572
@@ -0,0 +1,411 @@
+/* NSData+Crypto.m - this file is part of SOGo
+ *
+ *
+ * Author: Nicolas Höft
+ *
+ * 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
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This file is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __OpenBSD__
+#include <crypt.h>
+#endif
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+#define _XOPEN_SOURCE 1
+#include <unistd.h>
+#include <openssl/evp.h>
+#include <openssl/md5.h>
+#include <openssl/sha.h>
+
+#import <NGExtensions/NGBase64Coding.h>
+#import "NSData+Crypto.h"
+
+unsigned charTo4Bits(char c);
+
+
+@implementation NSData (SOGoCryptoExtension)
+
++ (NSString *) encodeDataAsHexString: (NSData *) theData
+{
+ unsigned int byteLength = [theData length], byteCounter = 0;
+ unsigned int stringLength = (byteLength * 2) + 1, stringCounter = 0;
+ unsigned char dstBuffer[stringLength];
+ unsigned char srcBuffer[byteLength];
+ unsigned char *srcPtr = srcBuffer;
+ [theData getBytes: srcBuffer];
+ const unsigned char t[16] = "0123456789abcdef";
+
+ for (; byteCounter < byteLength; byteCounter++)
+ {
+ unsigned src = *srcPtr;
+ dstBuffer[stringCounter++] = t[src >> 4];
+ dstBuffer[stringCounter++] = t[src & 15];
+ srcPtr++;
+ }
+
+ dstBuffer[stringCounter] = '\0';
+ return [NSString stringWithUTF8String: (char*)dstBuffer];
+}
+
++ (NSData *) decodeDataFromHexString: (NSString *) theString
+{
+ unsigned int stringLength = [theString length];
+ unsigned int byteLength = stringLength/2;
+ unsigned int byteCounter = 0;
+ unsigned char srcBuffer[stringLength];
+ [theString getCString:(char *)srcBuffer];
+ unsigned char *srcPtr = srcBuffer;
+ unsigned char dstBuffer[byteLength];
+ unsigned char *dst = dstBuffer;
+ while (byteCounter < byteLength)
+ {
+ unsigned char c = *srcPtr++;
+ unsigned char d = *srcPtr++;
+ unsigned hi = 0, lo = 0;
+ hi = charTo4Bits(c);
+ lo = charTo4Bits(d);
+ if (hi == 255 || lo == 255)
+ {
+ //errorCase
+ return nil;
+ }
+ dstBuffer[byteCounter++] = ((hi << 4) | lo);
+ }
+ return [NSData dataWithBytes: dst length: byteLength];
+}
+
++ (NSData *) generateSaltForLength: (unsigned int) theLength
+{
+ return [NSData generateSaltForLength: theLength withBase64: NO];
+}
+
++ (NSData *) generateSaltForLength: (unsigned int) theLength
+ withBase64: (BOOL) doBase64
+{
+ char *buf;
+ int fd;
+ NSData *data;
+
+ fd = open("/dev/urandom", O_RDONLY);
+
+ if (fd > 0)
+ {
+ buf = (char *)malloc(theLength);
+ read(fd, buf, theLength);
+ close(fd);
+
+ data = [NSData dataWithBytesNoCopy: buf length: theLength freeWhenDone: YES];
+ if(doBase64 == YES)
+ {
+ return [data dataByEncodingBase64WithLineLength: 1024];
+ }
+ return data;
+ }
+ return nil;
+}
+
+- (NSData *) asCryptedPassUsingScheme: (NSString *) passwordScheme
+ withSalt: (NSData *) theSalt
+{
+ if ([passwordScheme caseInsensitiveCompare: @"none"] == NSOrderedSame ||
+ [passwordScheme caseInsensitiveCompare: @"plain"] == NSOrderedSame ||
+ [passwordScheme caseInsensitiveCompare: @"cleartext"] == NSOrderedSame)
+ {
+ return self;
+ }
+ else if ([passwordScheme caseInsensitiveCompare: @"crypt"] == NSOrderedSame)
+ {
+ return [self asCryptUsingSalt: theSalt];
+ }
+ else if ([passwordScheme caseInsensitiveCompare: @"md5-crypt"] == NSOrderedSame)
+ {
+ return [self asMD5CryptUsingSalt: theSalt];
+ }
+ else if ([passwordScheme caseInsensitiveCompare: @"md5"] == NSOrderedSame ||
+ [passwordScheme caseInsensitiveCompare: @"plain-md5"] == NSOrderedSame ||
+ [passwordScheme caseInsensitiveCompare: @"ldap-md5"] == NSOrderedSame)
+ {
+ return [self asMD5];
+ }
+ else if ([passwordScheme caseInsensitiveCompare: @"smd5"] == NSOrderedSame)
+ {
+ return [self asSMD5UsingSalt: theSalt];
+ }
+ else if ([passwordScheme caseInsensitiveCompare: @"sha"] == NSOrderedSame)
+ {
+ return [self asSHA1];
+ }
+ else if ([passwordScheme caseInsensitiveCompare: @"ssha"] == NSOrderedSame)
+ {
+ return [self asSSHAUsingSalt: theSalt];
+ }
+ else if ([passwordScheme caseInsensitiveCompare: @"sha256"] == NSOrderedSame)
+ {
+ return [self asSHA256];
+ }
+ else if ([passwordScheme caseInsensitiveCompare: @"ssha256"] == NSOrderedSame)
+ {
+ return [self asSSHA256UsingSalt: theSalt];
+ }
+ else if ([passwordScheme caseInsensitiveCompare: @"sha512"] == NSOrderedSame)
+ {
+ return [self asSHA512];
+ }
+ else if ([passwordScheme caseInsensitiveCompare: @"ssha512"] == NSOrderedSame)
+ {
+ return [self asSSHA512UsingSalt: theSalt];
+ }
+ // in case the scheme was not detected, return nil
+ return nil;
+}
+
+
+- (NSData *) asMD5
+{
+ unsigned char md[MD5_DIGEST_LENGTH];
+ memset(md, 0, MD5_DIGEST_LENGTH);
+
+ EVP_Digest([self bytes], [self length], md, NULL, EVP_md5(), NULL);
+
+ return [NSData dataWithBytes: md length: MD5_DIGEST_LENGTH];
+}
+
+- (NSData *) asSHA1
+{
+ unsigned char sha[SHA_DIGEST_LENGTH];
+ memset(sha, 0, SHA_DIGEST_LENGTH);
+
+ SHA1([self bytes], [self length], sha);
+
+ return [NSData dataWithBytes: sha length: SHA_DIGEST_LENGTH];
+}
+
+- (NSData *) asSHA256
+{
+ unsigned char sha[SHA256_DIGEST_LENGTH];
+ memset(sha, 0, SHA256_DIGEST_LENGTH);
+
+ SHA256([self bytes], [self length], sha);
+
+ return [NSData dataWithBytes: sha length: SHA256_DIGEST_LENGTH];
+}
+
+- (NSData *) asSHA512
+{
+ unsigned char sha[SHA512_DIGEST_LENGTH];
+ memset(sha, 0, SHA512_DIGEST_LENGTH);
+
+ SHA512([self bytes], [self length], sha);
+
+ return [NSData dataWithBytes: sha length: SHA512_DIGEST_LENGTH];
+}
+
+- (NSData *) asSSHAUsingSalt: (NSData *) theSalt
+{
+ // SSHA works following: SSHA(pass, salt) = SHA1(pass + salt) + salt
+ NSMutableData *sshaData;
+
+ // generate salt, if not available
+ if ([theSalt length] == 0) theSalt = [NSData generateSaltForLength: 8];
+
+ // put the pass and salt together as one data array
+ sshaData = [NSMutableData dataWithData: self];
+ [sshaData appendData: theSalt];
+ // generate SHA1 from pass + salt
+ sshaData = [NSMutableData dataWithData: [sshaData asSHA1]];
+ // append salt again
+ [sshaData appendData: theSalt];
+
+ return sshaData;
+}
+
+- (NSData *) asSSHA256UsingSalt: (NSData *) theSalt
+{
+ // SSHA256 works following: SSHA(pass, salt) = SHA256(pass + salt) + salt
+ NSMutableData *sshaData;
+
+ // generate salt, if not available
+ if ([theSalt length] == 0) theSalt = [NSData generateSaltForLength: 8];
+
+ // put the pass and salt together as one data array
+ sshaData = [NSMutableData dataWithData: self];
+ [sshaData appendData: theSalt];
+ // generate SHA1 from pass + salt
+ sshaData = [NSMutableData dataWithData: [sshaData asSHA256]];
+ // append salt again
+ [sshaData appendData: theSalt];
+
+ return sshaData;
+}
+
+- (NSData *) asSSHA512UsingSalt: (NSData *) theSalt
+{
+ // SSHA512 works following: SSHA(pass, salt) = SHA512(pass + salt) + salt
+ NSMutableData *sshaData;
+
+ // generate salt, if not available
+ if ([theSalt length] == 0) theSalt = [NSData generateSaltForLength: 8];
+
+ // put the pass and salt together as one data array
+ sshaData = [NSMutableData dataWithData: self];
+ [sshaData appendData: theSalt];
+ // generate SHA1 from pass + salt
+ sshaData = [NSMutableData dataWithData: [sshaData asSHA512]];
+ // append salt again
+ [sshaData appendData: theSalt];
+
+ return sshaData;
+}
+
+- (NSData *) asSMD5UsingSalt: (NSData *) theSalt
+{
+ // SMD5 works following: SMD5(pass, salt) = MD5(pass + salt) + salt
+ NSMutableData *smdData;
+
+ // generate salt, if not available
+ if ([theSalt length] == 0) theSalt = [NSData generateSaltForLength: 8];
+
+ // put the pass and salt together as one data array
+ smdData = [NSMutableData dataWithData: self];
+ [smdData appendData: theSalt];
+ // generate SHA1 from pass + salt
+ smdData = [NSMutableData dataWithData: [smdData asMD5]];
+ // append salt again
+ [smdData appendData: theSalt];
+
+ return smdData;
+}
+
+
+- (NSData *) asMD5CryptUsingSalt: (NSData *) theSalt
+{
+ char *buf;
+ NSString *saltString;
+ NSString *cryptString;
+
+ // crypt() works with strings, so convert NSData to strings
+ cryptString = [[NSString alloc] initWithData: self encoding: NSUTF8StringEncoding];
+ [cryptString autorelease];
+
+ if ([theSalt length] == 0)
+ {
+ // make sure these characters are all printable by using base64
+ NSData *saltData = [NSData generateSaltForLength: 8 withBase64: YES];
+ NSString *tmpStr = [[NSString alloc] initWithData: saltData encoding: NSASCIIStringEncoding];
+ [tmpStr autorelease];
+ // and prepend a "$1$" to mark this as md5-crypt
+ saltString = [NSString stringWithFormat: @"$1$%@$", tmpStr];
+ }
+ else
+ {
+ saltString = [[NSString alloc] initWithData: theSalt encoding: NSUTF8StringEncoding];
+ [saltString autorelease];
+ }
+
+ buf = crypt([cryptString UTF8String], [saltString UTF8String]);
+ return [NSData dataWithBytes: buf length: strlen(buf)];
+}
+
+- (NSData *) asCryptUsingSalt: (NSData *) theSalt
+{
+ char *buf;
+ NSString *saltString;
+ NSString *cryptString;
+
+ // crypt() works with strings, so convert NSData to strings
+ cryptString = [[NSString alloc] initWithData: self encoding: NSUTF8StringEncoding];
+ [cryptString autorelease];
+
+ if ([theSalt length] == 0) theSalt = [NSData generateSaltForLength: 8 withBase64: YES];
+
+ saltString = [[NSString alloc] initWithData: theSalt encoding: NSUTF8StringEncoding];
+ [saltString autorelease];
+
+ // The salt is weak here, but who cares anyway, crypt should not
+ // be used anymore
+ buf = crypt([cryptString UTF8String], [saltString UTF8String]);
+ return [NSData dataWithBytes: buf length: strlen(buf)];
+}
+
+- (NSData *) extractSalt: (NSString *) theScheme
+{
+ NSRange r;
+ int len;
+ len = [self length];
+ if (len == 0)
+ return [NSData data];
+
+ // for the ssha schemes the salt is appended at the endif
+ // so the range with the salt are bytes after each digest length
+ if ([theScheme caseInsensitiveCompare: @"crypt"] == NSOrderedSame ||
+ [theScheme caseInsensitiveCompare: @"md5-crypt"] == NSOrderedSame)
+ {
+ // for crypt schemes simply use the whole string
+ // the crypt() function is able to extract it by itself
+ r = NSMakeRange(0, len);
+ }
+ else if ([theScheme caseInsensitiveCompare: @"ssha"] == NSOrderedSame)
+ {
+ r = NSMakeRange(SHA_DIGEST_LENGTH, len - SHA_DIGEST_LENGTH);
+ }
+ else if ([theScheme caseInsensitiveCompare: @"ssha256"] == NSOrderedSame)
+ {
+ r = NSMakeRange(SHA256_DIGEST_LENGTH, len - SHA256_DIGEST_LENGTH);
+ }
+ else if ([theScheme caseInsensitiveCompare: @"ssha512"] == NSOrderedSame)
+ {
+ r = NSMakeRange(SHA512_DIGEST_LENGTH, len - SHA512_DIGEST_LENGTH);
+ }
+ else if ([theScheme caseInsensitiveCompare: @"smd5"] == NSOrderedSame)
+ {
+ r = NSMakeRange(MD5_DIGEST_LENGTH, len - MD5_DIGEST_LENGTH);
+ }
+ else
+ {
+ // return empty string on unknown scheme
+ return [NSData data];
+ }
+
+ return [self subdataWithRange: r];
+}
+
+@end
+
+unsigned charTo4Bits(char c)
+{
+ unsigned bits = 0;
+ if (c > '/' && c < ':')
+ {
+ bits = c - '0';
+ }
+ else if (c > '@' && c < 'G')
+ {
+ bits = (c- 'A') + 10;
+ }
+ else if (c > '`' && c < 'g')
+ {
+ bits = (c- 'a') + 10;
+ }
+ else
+ {
+ bits = 255;
+ }
+ return bits;
+}
============================================================
--- /dev/null
+++ SoObjects/SOGo/NSString+Crypto.h 50ffbd75973627dda748687423fb4bbb7b798724
@@ -0,0 +1,58 @@
+/* NSString+Crypto.h - this file is part of SOGo
+ *
+ * Author: Nicolas Höft
+ *
+ * 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
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This file is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef NSSTRING_CRYPTO_H
+#define NSSTRING_CRYPTO_H
+
+#import <Foundation/NSData.h>
+#import <Foundation/NSString.h>
+
+typedef enum {
+ encDefault,
+ encPlain,
+ encHex,
+ encBase64,
+} keyEncoding;
+
+@class NSObject;
+
+@interface NSString (SOGoCryptoExtension)
+
+- (BOOL) isEqualToCrypted: (NSString *) cryptedPassword
+ withDefaultScheme: (NSString *) theScheme;
+
+- (NSString *) asCryptedPassUsingScheme: (NSString *) passwordScheme
+ withSalt: (NSData *) theSalt
+ andEncoding: (keyEncoding) encoding;
+
+// this method uses the default encoding (base64, plain, hex)
+// and generates a salt when necessary
+- (NSString *) asCryptedPassUsingScheme: (NSString *) passwordScheme;
+
+- (NSArray *) splitPasswordWithDefaultScheme: (NSString *) defaultScheme;
+
+- (NSString *) asSHA1String;
+- (NSString *) asMD5String;
+
++ (keyEncoding) getDefaultEncodingForScheme: (NSString *) passwordScheme;
+
+@end
+
+#endif /* NSSTRING_CRYPTO_H */
============================================================
--- /dev/null
+++ SoObjects/SOGo/NSString+Crypto.m 2bb1ed1c269a147c2285a0d2f4629997d20961bf
@@ -0,0 +1,248 @@
+/* NSString+Crypto.m - this file is part of SOGo
+ *
+ *
+ * Author: Nicolas Höft
+ *
+ * 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
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This file is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#import <Foundation/NSArray.h>
+#import <Foundation/NSValue.h>
+
+#import "NSString+Crypto.h"
+#import "NSData+Crypto.h"
+#import <NGExtensions/NGBase64Coding.h>
+
+@implementation NSString (SOGoCryptoExtension)
+
+- (NSString *) extractCryptScheme
+{
+ NSRange r;
+ int len;
+
+ len = [self length];
+ if (len == 0)
+ return @"";
+ if ([self characterAtIndex:0] != '{')
+ return @"";
+
+ r = [self rangeOfString:@"}" options:(NSLiteralSearch)];
+ if (r.length == 0)
+ return @"";
+
+ r.length = (r.location - 1);
+ r.location = 1;
+ return [[self substringWithRange:r] lowercaseString];
+}
+
+
+// This function returns an two-element array containing
+// the scheme and the rest of the (complete, including optional salt) password
+- (NSArray *) splitPasswordWithDefaultScheme: (NSString *) defaultScheme
+{
+ NSString *scheme;
+ NSString *pass;
+ NSArray *schemeComps;
+ keyEncoding encoding;
+
+ NSRange range;
+ int selflen, len;
+
+ selflen = [self length];
+
+ scheme = [self extractCryptScheme];
+ len = [scheme length];
+ if (len > 0)
+ range = NSMakeRange (len+2, selflen-len-2);
+ else
+ range = NSMakeRange (0, selflen);
+ if (len == 0)
+ scheme = defaultScheme;
+
+ encoding = [NSString getDefaultEncodingForScheme: scheme];
+
+ // get the encoding which may be part of the scheme
+ // e.g. ssha.hex forces a hex encoded ssha scheme
+ // possible is "b64" or "hex"
+ schemeComps = [scheme componentsSeparatedByString: @"."];
+ if ([schemeComps count] == 2)
+ {
+ NSString *stringEncoding;
+ // scheme without encoding string is the first item
+ scheme = [schemeComps objectAtIndex: 0];
+ // encoding string is second item
+ stringEncoding = [schemeComps objectAtIndex: 1];
+ if ([stringEncoding caseInsensitiveCompare: @"hex"] == NSOrderedSame)
+ {
+ encoding = encHex;
+ }
+ else if ([stringEncoding caseInsensitiveCompare: @"b64"] == NSOrderedSame)
+ {
+ encoding = encBase64;
+ }
+ }
+
+ pass = [self substringWithRange: range];
+ return [NSArray arrayWithObjects: scheme, pass, [NSNumber numberWithInt: encoding], nil];
+}
+
+
+- (BOOL) isEqualToCrypted: (NSString *) cryptedPassword
+ withDefaultScheme: (NSString *) theScheme
+{
+ NSArray *passInfo;
+ NSString *selfCrypted;
+ NSString *pass;
+ NSString *scheme;
+ NSData *salt;
+ NSData *decodedData;
+ NSNumber *encodingNumber;
+ keyEncoding encoding;
+
+ // split scheme and pass
+ passInfo = [cryptedPassword splitPasswordWithDefaultScheme: theScheme];
+
+ scheme = [passInfo objectAtIndex: 0];
+ pass = [passInfo objectAtIndex: 1];
+ encodingNumber = [passInfo objectAtIndex: 2];
+ encoding = [encodingNumber integerValue];
+
+ if (encoding == encHex)
+ {
+ decodedData = [NSData decodeDataFromHexString: pass];
+
+ if(decodedData == nil)
+ {
+ decodedData = [NSData data];
+ }
+ else
+ {
+ // decoding was successful, now make sure
+ // that the pass is in lowercase since decodeDataFromHexString uses
+ // lowercase charaters, too
+ pass = [pass lowercaseString];
+ }
+ }
+ else if(encoding == encBase64)
+ {
+ decodedData = [pass dataByDecodingBase64];
+ if(decodedData == nil)
+ {
+ decodedData = [NSData data];
+ }
+ }
+ else
+ {
+ decodedData = [pass dataUsingEncoding: NSUTF8StringEncoding];
+ }
+
+ salt = [decodedData extractSalt: scheme];
+
+ // encrypt self with the salt an compare the results
+ selfCrypted = [self asCryptedPassUsingScheme: scheme
+ withSalt: salt
+ andEncoding: encoding];
+ // return always false when there was a problem
+ if (selfCrypted == nil)
+ return NO;
+
+ if ([selfCrypted isEqualToString: pass] == YES)
+ return YES;
+ return NO;
+}
+
+- (NSString *) asCryptedPassUsingScheme: (NSString *) passwordScheme
+{
+ return [self asCryptedPassUsingScheme: passwordScheme
+ withSalt: [NSData data]
+ andEncoding: encDefault];
+}
+
+- (NSString *) asCryptedPassUsingScheme: (NSString *) passwordScheme
+ withSalt: (NSData *) theSalt
+ andEncoding: (keyEncoding) userEncoding
+{
+ keyEncoding dataEncoding;
+ NSData* cryptedData;
+ // convert NSString to NSData and apply encryption scheme
+ cryptedData = [self dataUsingEncoding: NSUTF8StringEncoding];
+ cryptedData = [cryptedData asCryptedPassUsingScheme: passwordScheme withSalt: theSalt];
+ // abort on unsupported scheme or error
+ if (cryptedData == nil)
+ return nil;
+
+ // use default encoding scheme, when set to default
+ if (userEncoding == encDefault)
+ dataEncoding = [NSString getDefaultEncodingForScheme: passwordScheme];
+ else
+ dataEncoding = userEncoding;
+
+ if (dataEncoding == encHex)
+ {
+ // hex encoding
+ return [NSData encodeDataAsHexString: cryptedData];
+ }
+ else if(dataEncoding == encBase64)
+ {
+ // base64 encoding
+ NSString *s = [[NSString alloc] initWithData: [cryptedData dataByEncodingBase64WithLineLength: 1024]
+ encoding: NSASCIIStringEncoding];
+ return [s autorelease];
+ }
+
+ // plain string
+ return [[[NSString alloc] initWithData: cryptedData encoding: NSUTF8StringEncoding] autorelease];
+}
+
++ (keyEncoding) getDefaultEncodingForScheme: (NSString *) passwordScheme
+{
+ // in order to keep backwards-compatibility, hex encoding is used for sha1 here
+ if ([passwordScheme caseInsensitiveCompare: @"md5"] == NSOrderedSame ||
+ [passwordScheme caseInsensitiveCompare: @"plain-md5"] == NSOrderedSame ||
+ [passwordScheme caseInsensitiveCompare: @"sha"] == NSOrderedSame)
+ {
+ return encHex;
+ }
+ else if ([passwordScheme caseInsensitiveCompare: @"smd5"] == NSOrderedSame ||
+ [passwordScheme caseInsensitiveCompare: @"ldap-md5"] == NSOrderedSame ||
+ [passwordScheme caseInsensitiveCompare: @"ssha"] == NSOrderedSame ||
+ [passwordScheme caseInsensitiveCompare: @"sha256"] == NSOrderedSame ||
+ [passwordScheme caseInsensitiveCompare: @"ssha256"] == NSOrderedSame ||
+ [passwordScheme caseInsensitiveCompare: @"sha512"] == NSOrderedSame ||
+ [passwordScheme caseInsensitiveCompare: @"ssha512"] == NSOrderedSame)
+ {
+ return encBase64;
+ }
+ return encPlain;
+}
+
+// backwards-compatibility
+- (NSString *) asSHA1String;
+{
+ NSData *cryptData;
+ cryptData = [self dataUsingEncoding: NSUTF8StringEncoding];
+ return [NSData encodeDataAsHexString: [cryptData asSHA1] ];
+}
+
+// backwards-compatibility
+- (NSString *) asMD5String;
+{
+ NSData *cryptData;
+ cryptData = [self dataUsingEncoding: NSUTF8StringEncoding];
+ return [NSData encodeDataAsHexString: [cryptData asMD5] ];
+}
+
+@end
============================================================
--- SoObjects/SOGo/NSString+Utilities.h d2661ef19253d96d64dcf11127ffe6882f90b4d4
+++ SoObjects/SOGo/NSString+Utilities.h c38d80d6ebe9592a9f2ee954282718d7265dec51
@@ -66,10 +66,6 @@
- (id) objectFromJSONString;
-- (NSString *) asCryptStringUsingSalt: (NSString *) theSalt;
-- (NSString *) asMD5String;
-- (NSString *) asSHA1String;
-
- (NSString *) asSafeSQLString;
- (NSUInteger) countOccurrencesOfString: (NSString *) substring;
============================================================
--- SoObjects/SOGo/NSString+Utilities.m 800b97cfc8f806e14c310ed8628c94a9934fd80a
+++ SoObjects/SOGo/NSString+Utilities.m eb4c7cd585c64cb8fb0273c3e89be36da11e5f24
@@ -21,10 +21,6 @@
* Boston, MA 02111-1307, USA.
*/
-#ifndef __OpenBSD__
-#include <crypt.h>
-#endif
-
#import <Foundation/NSArray.h>
#import <Foundation/NSCharacterSet.h>
#import <Foundation/NSData.h>
@@ -45,12 +41,6 @@
#import "NSString+Utilities.h"
-#define _XOPEN_SOURCE 1
-#include <unistd.h>
-#include <openssl/evp.h>
-#include <openssl/md5.h>
-#include <openssl/sha.h>
-
static NSMutableCharacterSet *urlNonEndingChars = nil;
static NSMutableCharacterSet *urlAfterEndingChars = nil;
static NSMutableCharacterSet *urlStartChars = nil;
@@ -534,48 +524,6 @@ static int cssEscapingCount;
return object;
}
-- (NSString *) asCryptStringUsingSalt: (NSString *) theSalt
-{
- char *buf;
-
- // The salt is weak here, but who cares anyway, crypt should not
- // be used anymore
- buf = crypt([self UTF8String], [theSalt UTF8String]);
- return [NSString stringWithUTF8String: buf];
-}
-
-- (NSString *) asMD5String
-{
- unsigned char md[MD5_DIGEST_LENGTH];
- char buf[80];
- int i;
-
- memset(md, 0, MD5_DIGEST_LENGTH);
- memset(buf, 0, 80);
-
- EVP_Digest((const void *) [self UTF8String], strlen([self UTF8String]), md, NULL, EVP_md5(), NULL);
- for (i = 0; i < MD5_DIGEST_LENGTH; i++)
- sprintf(&(buf[i*2]), "%02x", md[i]);
-
- return [NSString stringWithUTF8String: buf];
-}
-
-- (NSString *) asSHA1String
-{
- unsigned char sha[SHA_DIGEST_LENGTH];
- char buf[80];
- int i;
-
- memset(sha, 0, SHA_DIGEST_LENGTH);
- memset(buf, 0, 80);
-
- SHA1((const void *)[self UTF8String], strlen([self UTF8String]), sha);
- for (i = 0; i < SHA_DIGEST_LENGTH; i++)
- sprintf(&(buf[i*2]), "%02x", sha[i]);
-
- return [NSString stringWithUTF8String: buf];
-}
-
- (NSString *) asSafeSQLString
{
return [[self stringByReplacingString: @"\\" withString: @"\\\\"]
============================================================
--- SoObjects/SOGo/SOGoUserManager.m 9bf968b36558145edd71840dd180876c1d5d7175
+++ SoObjects/SOGo/SOGoUserManager.m 344659dcdf8d929bb824d8856d05c1de85ce801e
@@ -33,6 +33,7 @@
#import "NSArray+Utilities.h"
#import "NSString+Utilities.h"
+#import "NSString+Crypto.h"
#import "NSObject+Utilities.h"
#import "SOGoDomainDefaults.h"
#import "SOGoSource.h"
============================================================
--- SoObjects/SOGo/SQLSource.m 8e7fb09f6d34231ed0e1c57db516961d7a8bf0bb
+++ SoObjects/SOGo/SQLSource.m 3928a5ed6dca41cf9fa04a0f15c964b13d000747
@@ -39,6 +39,7 @@
#import "SOGoConstants.h"
#import "NSString+Utilities.h"
+#import "NSString+Crypto.h"
#import "SQLSource.h"
@@ -47,7 +48,7 @@
*
* c_uid - will be used for authentication - it's a username or username@domain.tld)
* c_name - which can be identical to c_uid - will be used to uniquely identify entries)
- * c_password - password of the user, plain-text, md5 or sha encoded for now
+ * c_password - password of the user, possible values: plain, md5, sha, ssha, sha256, ssha256, ssha2512, smd5, crypt, md5-crypt (with or without ".hex" or ".b64" appended)
* c_cn - the user's common name
* mail - the user's mail address
*
@@ -157,28 +158,8 @@
if (!plainPassword || !encryptedPassword)
return NO;
- if ([_userPasswordAlgorithm caseInsensitiveCompare: @"none"] == NSOrderedSame)
- {
- return [plainPassword isEqualToString: encryptedPassword];
- }
- else if ([_userPasswordAlgorithm caseInsensitiveCompare: @"crypt"] == NSOrderedSame)
- {
- return [[plainPassword asCryptStringUsingSalt: encryptedPassword] isEqualToString: encryptedPassword];
- }
- else if ([_userPasswordAlgorithm caseInsensitiveCompare: @"md5"] == NSOrderedSame)
- {
- return [[plainPassword asMD5String] isEqualToString: encryptedPassword];
- }
- else if ([_userPasswordAlgorithm caseInsensitiveCompare: @"sha"] == NSOrderedSame)
- {
-
- return [[plainPassword asSHA1String] isEqualToString: encryptedPassword];
- }
-
-
- [self errorWithFormat: @"Unsupported user-password algorithm: %@", _userPasswordAlgorithm];
-
- return NO;
+ return [plainPassword isEqualToCrypted: encryptedPassword
+ withDefaultScheme: _userPasswordAlgorithm];
}
/**
@@ -189,26 +170,13 @@
*/
- (NSString *) _encryptPassword: (NSString *) plainPassword
{
- if ([_userPasswordAlgorithm caseInsensitiveCompare: @"none"] == NSOrderedSame)
- {
- return plainPassword;
- }
- else if ([_userPasswordAlgorithm caseInsensitiveCompare: @"crypt"] == NSOrderedSame)
- {
- return [plainPassword asCryptStringUsingSalt: [plainPassword asMD5String]];
- }
- else if ([_userPasswordAlgorithm caseInsensitiveCompare: @"md5"] == NSOrderedSame)
- {
- return [plainPassword asMD5String];
- }
- else if ([_userPasswordAlgorithm caseInsensitiveCompare: @"sha"] == NSOrderedSame)
- {
- return [plainPassword asSHA1String];
- }
-
- [self errorWithFormat: @"Unsupported user-password algorithm: %@", _userPasswordAlgorithm];
-
- return plainPassword;
+ NSString *password;
+ password = [plainPassword asCryptedPassUsingScheme: _userPasswordAlgorithm];
+
+ if (password == nil)
+ [self errorWithFormat: @"Unsupported user-password algorithm: %@", _userPasswordAlgorithm];
+
+ return [NSString stringWithFormat: @"{%@}%@", _userPasswordAlgorithm, password];
}
//
|
|
I've now added my third version of the cryptographic extension for SQL (and now also LDAP). BTW: you may change line 90 of NSString+Crypto.m to this: else if ([stringEncoding caseInsensitiveCompare: @"b64"] == NSOrderedSame || And a further note explaining the patch (v3): The conversation will be handled by the NSString+Crypto extension. It also handles the splitting of the scheme from a string like "{SSHA.b64}986H5cS9JcDYQeJd6wKaITMho4M9CrXM" into the scheme and the binary data. I also moves the functions asMD5String and asSHA1String NSString crypto extension for consistency. I hope this is getting added soon. |
|
|
2012-05-22 12:09
|
crypto_v4.patch (43,524 bytes)
#
# old_revision [471dc0f5e3b2a6fc571aca4e14c0a5161cf7af0b]
#
# add_file "SoObjects/SOGo/NSData+Crypto.h"
# content [4e1a09d2530e3934966cf7547ffc6ee9411b4d0d]
#
# add_file "SoObjects/SOGo/NSData+Crypto.m"
# content [6d71c327de926a32a542f1cbdf923c1d6435c3f2]
#
# add_file "SoObjects/SOGo/NSString+Crypto.h"
# content [a15042732241b52c399c8667dc0fd281f97bc131]
#
# add_file "SoObjects/SOGo/NSString+Crypto.m"
# content [24e1ff6773c04e588410609f312824912d333911]
#
# patch "SoObjects/SOGo/GNUmakefile"
# from [9e466added539ccacaecd830173afdf9d4755802]
# to [0e4d9af53dddf014fe6fccf1d81e751d8dd2bc3c]
#
# patch "SoObjects/SOGo/LDAPSource.m"
# from [49cd1d275323dd81ba0ad5566e542b725856c4a9]
# to [0570acb4f559ddabe647634f52a877d17dbbce2d]
#
# patch "SoObjects/SOGo/NSString+Utilities.h"
# from [d2661ef19253d96d64dcf11127ffe6882f90b4d4]
# to [c38d80d6ebe9592a9f2ee954282718d7265dec51]
#
# patch "SoObjects/SOGo/NSString+Utilities.m"
# from [800b97cfc8f806e14c310ed8628c94a9934fd80a]
# to [eb4c7cd585c64cb8fb0273c3e89be36da11e5f24]
#
# patch "SoObjects/SOGo/SOGoUserManager.m"
# from [9bf968b36558145edd71840dd180876c1d5d7175]
# to [344659dcdf8d929bb824d8856d05c1de85ce801e]
#
# patch "SoObjects/SOGo/SQLSource.m"
# from [8e7fb09f6d34231ed0e1c57db516961d7a8bf0bb]
# to [569afbdf32cf6a95315d8e99cd910a0abaa2e9ad]
#
============================================================
--- SoObjects/SOGo/GNUmakefile 9e466added539ccacaecd830173afdf9d4755802
+++ SoObjects/SOGo/GNUmakefile 0e4d9af53dddf014fe6fccf1d81e751d8dd2bc3c
@@ -46,6 +46,8 @@ SOGo_HEADER_FILES = \
NSObject+Utilities.h \
NSString+DAV.h \
NSString+Utilities.h \
+ NSString+Crypto.h \
+ NSData+Crypto.h \
NSURL+DAV.h \
\
SOGoAuthenticator.h \
@@ -114,6 +116,8 @@ SOGo_OBJC_FILES = \
NSObject+Utilities.m \
NSString+DAV.m \
NSString+Utilities.m \
+ NSString+Crypto.m \
+ NSData+Crypto.m \
NSURL+DAV.m \
\
SOGoSession.m \
============================================================
--- SoObjects/SOGo/LDAPSource.m 49cd1d275323dd81ba0ad5566e542b725856c4a9
+++ SoObjects/SOGo/LDAPSource.m 0570acb4f559ddabe647634f52a877d17dbbce2d
@@ -40,6 +40,7 @@
#import "LDAPSourceSchema.h"
#import "NSArray+Utilities.h"
#import "NSString+Utilities.h"
+#import "NSString+Crypto.h"
#import "SOGoDomainDefaults.h"
#import "SOGoSystemDefaults.h"
@@ -639,26 +640,13 @@ andMultipleBookingsField: (NSString *) n
*/
- (NSString *) _encryptPassword: (NSString *) plainPassword
{
- if ([_userPasswordAlgorithm caseInsensitiveCompare: @"none"] == NSOrderedSame)
- {
- return plainPassword;
- }
- else if ([_userPasswordAlgorithm caseInsensitiveCompare: @"crypt"] == NSOrderedSame)
- {
- return [NSString stringWithFormat: @"{CRYPT}%@", [plainPassword asCryptStringUsingSalt: [plainPassword asMD5String]]];
- }
- else if ([_userPasswordAlgorithm caseInsensitiveCompare: @"md5"] == NSOrderedSame)
- {
- return [NSString stringWithFormat: @"{MD5}%@", [plainPassword asMD5String]];
- }
- else if ([_userPasswordAlgorithm caseInsensitiveCompare: @"sha"] == NSOrderedSame)
- {
- return [NSString stringWithFormat: @"{SHA}%@", [plainPassword asSHA1String]];
- }
-
- [self errorWithFormat: @"Unsupported user-password algorithm: %@", _userPasswordAlgorithm];
-
- return plainPassword;
+ NSString *pass;
+ pass = [plainPassword asCryptedPassUsingScheme: _userPasswordAlgorithm];
+
+ if (pass == nil)
+ [self errorWithFormat: @"Unsupported user-password algorithm: %@", _userPasswordAlgorithm];
+
+ return [NSString stringWithFormat: @"{%@}%@", _userPasswordAlgorithm, pass];
}
//
============================================================
--- /dev/null
+++ SoObjects/SOGo/NSData+Crypto.h 4e1a09d2530e3934966cf7547ffc6ee9411b4d0d
@@ -0,0 +1,57 @@
+/* NSData+Crypto.h - this file is part of SOGo
+ *
+ * Author: Nicolas Höft
+ *
+ * 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
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This file is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef NSDATA_CRYPTO_H
+#define NSDATA_CRYPTO_H
+
+#import <Foundation/NSData.h>
+
+@class NSObject;
+
+@interface NSData (SOGoCryptoExtension)
+
+- (NSData *) asCryptedPassUsingScheme: (NSString *) passwordScheme
+ withSalt: (NSData *) theSalt;
+
+- (NSData *) asMD5;
+- (NSData *) asSMD5UsingSalt: (NSData *) theSalt;
+- (NSData *) asSHA1;
+- (NSData *) asSSHAUsingSalt: (NSData *) theSalt;
+- (NSData *) asSHA256;
+- (NSData *) asSSHA256UsingSalt: (NSData *) theSalt;
+- (NSData *) asSHA512;
+- (NSData *) asSSHA512UsingSalt: (NSData *) theSalt;
+- (NSData *) asCramMD5;
+
+- (NSData *) asCryptUsingSalt: (NSData *) theSalt;
+- (NSData *) asMD5CryptUsingSalt: (NSData *) theSalt;
+
+- (NSData *) extractSalt: (NSString *) theScheme;
+
++ (NSData *) generateSaltForLength: (unsigned int) theLength
+ withBase64: (BOOL) doBase64;
++ (NSData *) generateSaltForLength: (unsigned int) theLength;
+
++ (NSString *) encodeDataAsHexString: (NSData* ) theData;
++ (NSData *) decodeDataFromHexString: (NSString* ) theString;
+
+@end
+
+#endif /* NSDATA_CRYPTO_H */
============================================================
--- /dev/null
+++ SoObjects/SOGo/NSData+Crypto.m 6d71c327de926a32a542f1cbdf923c1d6435c3f2
@@ -0,0 +1,617 @@
+/* NSData+Crypto.m - this file is part of SOGo
+ *
+ *
+ * Author: Nicolas Höft
+ *
+ * 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
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This file is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __OpenBSD__
+#include <crypt.h>
+#endif
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+#define _XOPEN_SOURCE 1
+#include <unistd.h>
+#include <openssl/evp.h>
+#include <openssl/md5.h>
+#include <openssl/sha.h>
+
+#import <Foundation/NSArray.h>
+#import <NGExtensions/NGBase64Coding.h>
+#import "NSData+Crypto.h"
+
+unsigned charTo4Bits(char c);
+
+
+@implementation NSData (SOGoCryptoExtension)
+
+/**
+ * Covert binary data to hex encoded data (lower-case).
+ *
+ * @param theData The NSData to be converted into a hex-encoded string.
+ * @return Hex-Encoded data
+ */
++ (NSString *) encodeDataAsHexString: (NSData *) theData
+{
+ unsigned int byteLength = [theData length], byteCounter = 0;
+ unsigned int stringLength = (byteLength * 2) + 1, stringCounter = 0;
+ unsigned char dstBuffer[stringLength];
+ unsigned char srcBuffer[byteLength];
+ unsigned char *srcPtr = srcBuffer;
+ [theData getBytes: srcBuffer];
+ const unsigned char t[16] = "0123456789abcdef";
+
+ for (; byteCounter < byteLength; byteCounter++)
+ {
+ unsigned src = *srcPtr;
+ dstBuffer[stringCounter++] = t[src >> 4];
+ dstBuffer[stringCounter++] = t[src & 15];
+ srcPtr++;
+ }
+
+ dstBuffer[stringCounter] = '\0';
+ return [NSString stringWithUTF8String: (char*)dstBuffer];
+}
+
+/**
+ * Covert hex-encoded data to binary data.
+ *
+ * @param theString The hex-encoded string to be converted into binary data (works both for upper and lowe case characters)
+ * @return binary data or nil if unsuccessful
+ */
++ (NSData *) decodeDataFromHexString: (NSString *) theString
+{
+ unsigned int stringLength = [theString length];
+ unsigned int byteLength = stringLength/2;
+ unsigned int byteCounter = 0;
+ unsigned char srcBuffer[stringLength];
+ [theString getCString:(char *)srcBuffer];
+ unsigned char *srcPtr = srcBuffer;
+ unsigned char dstBuffer[byteLength];
+ unsigned char *dst = dstBuffer;
+ while (byteCounter < byteLength)
+ {
+ unsigned char c = *srcPtr++;
+ unsigned char d = *srcPtr++;
+ unsigned hi = 0, lo = 0;
+ hi = charTo4Bits(c);
+ lo = charTo4Bits(d);
+ if (hi == 255 || lo == 255)
+ {
+ //errorCase
+ return nil;
+ }
+ dstBuffer[byteCounter++] = ((hi << 4) | lo);
+ }
+ return [NSData dataWithBytes: dst length: byteLength];
+}
+
+/**
+ * Generate a binary key which can be used for salting hashes.
+ *
+ * @param theLength length of the binary data to be generated in bytes
+ * @return Pseudo-random binary data with length theLength or nil, if an error occured
+ */
++ (NSData *) generateSaltForLength: (unsigned int) theLength
+{
+ return [NSData generateSaltForLength: theLength withBase64: NO];
+}
+
+/**
+ * Generate a binary key which can be used for salting hashes. When using
+ * with doBase64 == YES then the data will be longer than theLength
+ *
+ * @param theLength Length of the binary data to be generated in bytes
+ * @param doBase64 Convert the data into Base-64 before retuning it, be aware that this makes the binary data longer
+ * @return Pseudo-random binary data with length theLength or nil, if an error occured
+ */
++ (NSData *) generateSaltForLength: (unsigned int) theLength
+ withBase64: (BOOL) doBase64
+{
+ char *buf;
+ int fd;
+ NSData *data;
+
+ fd = open("/dev/urandom", O_RDONLY);
+
+ if (fd > 0)
+ {
+ buf = (char *)malloc(theLength);
+ read(fd, buf, theLength);
+ close(fd);
+
+ data = [NSData dataWithBytesNoCopy: buf length: theLength freeWhenDone: YES];
+ if(doBase64 == YES)
+ {
+ return [data dataByEncodingBase64WithLineLength: 1024];
+ }
+ return data;
+ }
+ return nil;
+}
+
+/**
+ * Encrypt/Hash the data with a given scheme
+ *
+ * @param passwordScheme The scheme to use for hashing/encryption.
+ * @param theSalt The salt to be used. If none is given but needed, it will be generated
+ * @return Binary data from the encryption by the specified scheme. On error the funciton returns nil.
+ */
+- (NSData *) asCryptedPassUsingScheme: (NSString *) passwordScheme
+ withSalt: (NSData *) theSalt
+{
+ if ([passwordScheme caseInsensitiveCompare: @"none"] == NSOrderedSame ||
+ [passwordScheme caseInsensitiveCompare: @"plain"] == NSOrderedSame ||
+ [passwordScheme caseInsensitiveCompare: @"cleartext"] == NSOrderedSame)
+ {
+ return self;
+ }
+ else if ([passwordScheme caseInsensitiveCompare: @"crypt"] == NSOrderedSame)
+ {
+ return [self asCryptUsingSalt: theSalt];
+ }
+ else if ([passwordScheme caseInsensitiveCompare: @"md5-crypt"] == NSOrderedSame)
+ {
+ return [self asMD5CryptUsingSalt: theSalt];
+ }
+ else if ([passwordScheme caseInsensitiveCompare: @"md5"] == NSOrderedSame ||
+ [passwordScheme caseInsensitiveCompare: @"plain-md5"] == NSOrderedSame ||
+ [passwordScheme caseInsensitiveCompare: @"ldap-md5"] == NSOrderedSame)
+ {
+ return [self asMD5];
+ }
+ else if ([passwordScheme caseInsensitiveCompare: @"cram-md5"] == NSOrderedSame)
+ {
+ return [self asCramMD5];
+ }
+ else if ([passwordScheme caseInsensitiveCompare: @"smd5"] == NSOrderedSame)
+ {
+ return [self asSMD5UsingSalt: theSalt];
+ }
+ else if ([passwordScheme caseInsensitiveCompare: @"sha"] == NSOrderedSame)
+ {
+ return [self asSHA1];
+ }
+ else if ([passwordScheme caseInsensitiveCompare: @"ssha"] == NSOrderedSame)
+ {
+ return [self asSSHAUsingSalt: theSalt];
+ }
+ else if ([passwordScheme caseInsensitiveCompare: @"sha256"] == NSOrderedSame)
+ {
+ return [self asSHA256];
+ }
+ else if ([passwordScheme caseInsensitiveCompare: @"ssha256"] == NSOrderedSame)
+ {
+ return [self asSSHA256UsingSalt: theSalt];
+ }
+ else if ([passwordScheme caseInsensitiveCompare: @"sha512"] == NSOrderedSame)
+ {
+ return [self asSHA512];
+ }
+ else if ([passwordScheme caseInsensitiveCompare: @"ssha512"] == NSOrderedSame)
+ {
+ return [self asSSHA512UsingSalt: theSalt];
+ }
+ // in case the scheme was not detected, return nil
+ return nil;
+}
+
+
+/**
+ * Hash the data with MD5. Uses openssl functions to generate it
+ *
+ * @return Binary data from MD5 hashing. On error the funciton returns nil.
+ */
+- (NSData *) asMD5
+{
+ unsigned char md5[MD5_DIGEST_LENGTH];
+ memset(md5, 0, MD5_DIGEST_LENGTH);
+
+ MD5([self bytes], [self length], md5);
+
+ return [NSData dataWithBytes: md5 length: MD5_DIGEST_LENGTH];
+}
+
+/**
+ * Hash the data with CRAM-MD5. Uses openssl functions to generate it.
+ *
+ * Note that the actual CRAM-MD5 algorithm also needs a challenge
+ * but this is not provided, this function actually calculalates
+ * only the context data which can be used for the challange-response
+ * algorithm then. This is just the underlying algorithm to store the passwords.
+ *
+ * The code is adopts the dovecot behaviour of storing the passwords
+ *
+ * @return Binary data from CRAM-MD5 'hashing'. On error the funciton returns nil.
+ */
+- (NSData *) asCramMD5
+{
+
+ MD5_CTX ctx;
+ unsigned char inner[64];
+ unsigned char outer[64];
+ unsigned char result[32];
+ unsigned char *r;
+ int i;
+ int len;
+ NSData *key;
+
+ if ([self length] > 64)
+ {
+ key = [self asMD5];
+ }
+ else
+ {
+ key = self;
+ }
+
+ len = [key length];
+ // fill with both inner and outer with key
+ memcpy(inner, [key bytes], len);
+ // make sure the rest of the bytes is zero
+ memset(inner + len, 0, 64 - len);
+ memcpy(outer, inner, 64);
+
+ for (i = 0; i < 64; i++)
+ {
+ inner[i] ^= 0x36;
+ outer[i] ^= 0x5c;
+ }
+// this transformation is needed for the correct cast to binary data
+#define CDPUT(p, c) { \
+ *p = (c) & 0xff; p++; \
+ *p = (c) >> 8 & 0xff; p++; \
+ *p = (c) >> 16 & 0xff; p++; \
+ *p = (c) >> 24 & 0xff; p++; \
+}
+
+ // generate first set of context bytes from outer data
+ MD5_Init(&ctx);
+ MD5_Transform(&ctx, outer);
+ r = result;
+ // convert this to correct binary data according to RFC 1321
+ CDPUT(r, ctx.A);
+ CDPUT(r, ctx.B);
+ CDPUT(r, ctx.C);
+ CDPUT(r, ctx.D);
+
+ // second set with inner data is appended to result string
+ MD5_Init(&ctx);
+ MD5_Transform(&ctx, inner);
+ // convert this to correct binary data
+ CDPUT(r, ctx.A);
+ CDPUT(r, ctx.B);
+ CDPUT(r, ctx.C);
+ CDPUT(r, ctx.D);
+
+ return [NSData dataWithBytes: result length: 32];
+}
+
+/**
+ * Hash the data with SHA1. Uses openssl functions to generate it.
+ *
+ * @return Binary data from SHA1 hashing. On error the funciton returns nil.
+ */
+- (NSData *) asSHA1
+{
+ unsigned char sha[SHA_DIGEST_LENGTH];
+ memset(sha, 0, SHA_DIGEST_LENGTH);
+
+ SHA1([self bytes], [self length], sha);
+
+ return [NSData dataWithBytes: sha length: SHA_DIGEST_LENGTH];
+}
+
+/**
+ * Hash the data with SHA256. Uses openssl functions to generate it.
+ *
+ * @return Binary data from SHA256 hashing. On error the funciton returns nil.
+ */
+- (NSData *) asSHA256
+{
+ unsigned char sha[SHA256_DIGEST_LENGTH];
+ memset(sha, 0, SHA256_DIGEST_LENGTH);
+
+ SHA256([self bytes], [self length], sha);
+
+ return [NSData dataWithBytes: sha length: SHA256_DIGEST_LENGTH];
+}
+
+/**
+ * Hash the data with SHA512. Uses openssl functions to generate it.
+ *
+ * @return Binary data from SHA512 hashing. On error the funciton returns nil.
+ */
+- (NSData *) asSHA512
+{
+ unsigned char sha[SHA512_DIGEST_LENGTH];
+ memset(sha, 0, SHA512_DIGEST_LENGTH);
+
+ SHA512([self bytes], [self length], sha);
+
+ return [NSData dataWithBytes: sha length: SHA512_DIGEST_LENGTH];
+}
+
+/**
+ * Hash the data with SSHA. Uses openssl functions to generate it.
+ *
+ * SSHA works following: SSHA(pass, salt) = SHA1(pass + salt) + saltData
+ *
+ * @param theSalt The salt to be used must not be nil, if empty, one will be generated
+ * @return Binary data from SHA1 hashing. On error the funciton returns nil.
+ */
+- (NSData *) asSSHAUsingSalt: (NSData *) theSalt
+{
+ //
+ NSMutableData *sshaData;
+
+ // generate salt, if not available
+ if ([theSalt length] == 0) theSalt = [NSData generateSaltForLength: 8];
+
+ // put the pass and salt together as one data array
+ sshaData = [NSMutableData dataWithData: self];
+ [sshaData appendData: theSalt];
+ // generate SHA1 from pass + salt
+ sshaData = [NSMutableData dataWithData: [sshaData asSHA1]];
+ // append salt again
+ [sshaData appendData: theSalt];
+
+ return sshaData;
+}
+
+/**
+ * Hash the data with SSHA256. Uses openssl functions to generate it.
+ *
+ * SSHA256 works following: SSHA256(pass, salt) = SHA256(pass + salt) + saltData
+ *
+ * @param theSalt The salt to be used must not be nil, if empty, one will be generated
+ * @return Binary data from SHA1 hashing. On error the funciton returns nil.
+ */
+
+- (NSData *) asSSHA256UsingSalt: (NSData *) theSalt
+{
+ NSMutableData *sshaData;
+
+ // generate salt, if not available
+ if ([theSalt length] == 0) theSalt = [NSData generateSaltForLength: 8];
+
+ // put the pass and salt together as one data array
+ sshaData = [NSMutableData dataWithData: self];
+ [sshaData appendData: theSalt];
+ // generate SHA1 from pass + salt
+ sshaData = [NSMutableData dataWithData: [sshaData asSHA256]];
+ // append salt again
+ [sshaData appendData: theSalt];
+
+ return sshaData;
+}
+
+/**
+ * Hash the data with SSHA512. Uses openssl functions to generate it.
+ *
+ * SSHA works following: SSHA512(pass, salt) = SHA512(pass + salt) + saltData
+ *
+ * @param theSalt The salt to be used must not be nil, if empty, one will be generated
+ * @return Binary data from SHA512 hashing. On error the funciton returns nil.
+ */
+
+- (NSData *) asSSHA512UsingSalt: (NSData *) theSalt
+{
+ NSMutableData *sshaData;
+
+ // generate salt, if not available
+ if ([theSalt length] == 0) theSalt = [NSData generateSaltForLength: 8];
+
+ // put the pass and salt together as one data array
+ sshaData = [NSMutableData dataWithData: self];
+ [sshaData appendData: theSalt];
+ // generate SHA1 from pass + salt
+ sshaData = [NSMutableData dataWithData: [sshaData asSHA512]];
+ // append salt again
+ [sshaData appendData: theSalt];
+
+ return sshaData;
+}
+
+/**
+ * Hash the data with SMD5. Uses openssl functions to generate it.
+ *
+ * SMD5 works following: SMD5(pass, salt) = MD5(pass + salt) + saltData
+ *
+ * @param theSalt The salt to be used must not be nil, if empty, one will be generated
+ * @return Binary data from SMD5 hashing. On error the funciton returns nil.
+ */
+- (NSData *) asSMD5UsingSalt: (NSData *) theSalt
+{
+ // SMD5 works following: SMD5(pass, salt) = MD5(pass + salt) + salt
+ NSMutableData *smdData;
+
+ // generate salt, if not available
+ if ([theSalt length] == 0) theSalt = [NSData generateSaltForLength: 8];
+
+ // put the pass and salt together as one data array
+ smdData = [NSMutableData dataWithData: self];
+ [smdData appendData: theSalt];
+ // generate SHA1 from pass + salt
+ smdData = [NSMutableData dataWithData: [smdData asMD5]];
+ // append salt again
+ [smdData appendData: theSalt];
+
+ return smdData;
+}
+
+
+/**
+ * Hash the data with CRYPT-MD5 as used in /etc/passwd nowadays. Uses crypt() function to generate it.
+ *
+ *
+ * @param theSalt The salt to be used must not be nil, if empty, one will be generated. It must be printable characters only.
+ * @return Binary data from CRYPT-MD5 hashing. On error the funciton returns nil.
+ */
+- (NSData *) asMD5CryptUsingSalt: (NSData *) theSalt
+{
+ char *buf;
+ NSMutableData *saltData;
+ NSString *cryptString;
+ NSString *saltString;
+
+ if ([theSalt length] == 0)
+ {
+ // make sure these characters are all printable by using base64
+ theSalt = [NSData generateSaltForLength: 8 withBase64: YES];
+ }
+ cryptString = [[NSString alloc] initWithData: self encoding: NSUTF8StringEncoding];
+
+ NSString * magic = @"$1$";
+ saltData = [NSMutableData dataWithData: [magic dataUsingEncoding: NSUTF8StringEncoding]];
+ [saltData appendData: theSalt];
+ // terminate with "$"
+ [saltData appendData: [@"$" dataUsingEncoding: NSUTF8StringEncoding]];
+
+ saltString = [[NSString alloc] initWithData: saltData encoding: NSUTF8StringEncoding];
+
+ buf = crypt([cryptString UTF8String], [saltString UTF8String]);
+ [cryptString release];
+ [saltString release];
+ if (!buf)
+ return nil;
+ return [NSData dataWithBytes: buf length: strlen(buf)];
+}
+
+/**
+ * Hash the data using crypt() function.
+ *
+ * @param theSalt The salt to be used must not be nil, if empty, one will be generated
+ * @return Binary data from CRYPT-MD5 hashing. On error the funciton returns nil.
+ */
+- (NSData *) asCryptUsingSalt: (NSData *) theSalt
+{
+ char *buf;
+ NSString *saltString;
+ NSString *cryptString;
+
+ // crypt() works with strings, so convert NSData to strings
+ cryptString = [[NSString alloc] initWithData: self encoding: NSUTF8StringEncoding];
+
+ if ([theSalt length] == 0) theSalt = [NSData generateSaltForLength: 8 withBase64: YES];
+
+ saltString = [[NSString alloc] initWithData: theSalt encoding: NSUTF8StringEncoding];
+
+ // The salt is weak here, but who cares anyway, crypt should not
+ // be used anymore
+ buf = crypt([cryptString UTF8String], [saltString UTF8String]);
+ [saltString release];
+ [cryptString release];
+ if (!buf)
+ return nil;
+ return [NSData dataWithBytes: buf length: strlen(buf)];
+}
+
+/**
+ * Get the salt from a password encrypted with a specied scheme
+ *
+ * @param theScheme Needed to get the salt correctly out of the pass
+ * @return The salt, if one was available in the password/scheme, else empty data
+ */
+- (NSData *) extractSalt: (NSString *) theScheme
+{
+ NSRange r;
+ int len;
+ len = [self length];
+ if (len == 0)
+ return [NSData data];
+
+ // for the ssha schemes the salt is appended at the endif
+ // so the range with the salt are bytes after each digest length
+ if ([theScheme caseInsensitiveCompare: @"crypt"] == NSOrderedSame)
+ {
+ // for crypt schemes simply use the whole string
+ // the crypt() function is able to extract it by itself
+ r = NSMakeRange(0, len);
+ }
+ else if ([theScheme caseInsensitiveCompare: @"md5-crypt"] == NSOrderedSame)
+ {
+ // md5 crypt is generated the following "$1$<salt>$<encrypted pass>"
+ NSString *cryptString;
+ NSArray *cryptParts;
+ cryptString = [NSString stringWithUTF8String: [self bytes] ];
+ cryptParts = [cryptString componentsSeparatedByString: @"$"];
+ // correct number of elements (first one is an empty string)
+ if ([cryptParts count] != 4)
+ {
+ return [NSData data];
+ }
+ // second is the identifier of md5-crypt
+ else if( [[cryptParts objectAtIndex: 1] caseInsensitiveCompare: @"1"] != NSOrderedSame )
+ {
+ return [NSData data];
+ }
+ // third is the salt; convert it to NSData
+ return [[cryptParts objectAtIndex: 2] dataUsingEncoding: NSUTF8StringEncoding];
+ }
+ else if ([theScheme caseInsensitiveCompare: @"ssha"] == NSOrderedSame)
+ {
+ r = NSMakeRange(SHA_DIGEST_LENGTH, len - SHA_DIGEST_LENGTH);
+ }
+ else if ([theScheme caseInsensitiveCompare: @"ssha256"] == NSOrderedSame)
+ {
+ r = NSMakeRange(SHA256_DIGEST_LENGTH, len - SHA256_DIGEST_LENGTH);
+ }
+ else if ([theScheme caseInsensitiveCompare: @"ssha512"] == NSOrderedSame)
+ {
+ r = NSMakeRange(SHA512_DIGEST_LENGTH, len - SHA512_DIGEST_LENGTH);
+ }
+ else if ([theScheme caseInsensitiveCompare: @"smd5"] == NSOrderedSame)
+ {
+ r = NSMakeRange(MD5_DIGEST_LENGTH, len - MD5_DIGEST_LENGTH);
+ }
+ else
+ {
+ // return empty string on unknown scheme
+ return [NSData data];
+ }
+
+ return [self subdataWithRange: r];
+}
+
+@end
+
+unsigned charTo4Bits(char c)
+{
+ unsigned bits = 0;
+ if (c > '/' && c < ':')
+ {
+ bits = c - '0';
+ }
+ else if (c > '@' && c < 'G')
+ {
+ bits = (c- 'A') + 10;
+ }
+ else if (c > '`' && c < 'g')
+ {
+ bits = (c- 'a') + 10;
+ }
+ else
+ {
+ bits = 255;
+ }
+ return bits;
+}
============================================================
--- /dev/null
+++ SoObjects/SOGo/NSString+Crypto.h a15042732241b52c399c8667dc0fd281f97bc131
@@ -0,0 +1,59 @@
+/* NSString+Crypto.h - this file is part of SOGo
+ *
+ * Author: Nicolas Höft
+ *
+ * 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
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This file is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef NSSTRING_CRYPTO_H
+#define NSSTRING_CRYPTO_H
+
+#import <Foundation/NSData.h>
+#import <Foundation/NSString.h>
+
+typedef enum {
+ encDefault, //!< default encoding, let the algorithm decide
+ encPlain, //!< the data is plain text, simply convert to string
+ encHex, //!< the data is hex encoded
+ encBase64, //!< base64 encoding
+} keyEncoding;
+
+@class NSObject;
+
+@interface NSString (SOGoCryptoExtension)
+
+
+- (BOOL) isEqualToCrypted: (NSString *) cryptedPassword
+ withDefaultScheme: (NSString *) theScheme;
+
+- (NSString *) asCryptedPassUsingScheme: (NSString *) passwordScheme
+ withSalt: (NSData *) theSalt
+ andEncoding: (keyEncoding) encoding;
+
+// this method uses the default encoding (base64, plain, hex)
+// and generates a salt when necessary
+- (NSString *) asCryptedPassUsingScheme: (NSString *) passwordScheme;
+
+- (NSArray *) splitPasswordWithDefaultScheme: (NSString *) defaultScheme;
+
+- (NSString *) asSHA1String;
+- (NSString *) asMD5String;
+
++ (keyEncoding) getDefaultEncodingForScheme: (NSString *) passwordScheme;
+
+@end
+
+#endif /* NSSTRING_CRYPTO_H */
============================================================
--- /dev/null
+++ SoObjects/SOGo/NSString+Crypto.m 24e1ff6773c04e588410609f312824912d333911
@@ -0,0 +1,301 @@
+/* NSString+Crypto.m - this file is part of SOGo
+ *
+ *
+ * Author: Nicolas Höft
+ *
+ * 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
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This file is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#import <Foundation/NSArray.h>
+#import <Foundation/NSValue.h>
+
+#import "NSString+Crypto.h"
+#import "NSData+Crypto.h"
+#import <NGExtensions/NGBase64Coding.h>
+
+@implementation NSString (SOGoCryptoExtension)
+
+/**
+ * Extracts the scheme from a string formed "{scheme}pass".
+ *
+ * @return The scheme or an empty string if the string did not contained a scheme in the format above
+ */
+- (NSString *) extractCryptScheme
+{
+ NSRange r;
+ int len;
+
+ len = [self length];
+ if (len == 0)
+ return @"";
+ if ([self characterAtIndex:0] != '{')
+ return @"";
+
+ r = [self rangeOfString:@"}" options:(NSLiteralSearch)];
+ if (r.length == 0)
+ return @"";
+
+ r.length = (r.location - 1);
+ r.location = 1;
+ return [[self substringWithRange:r] lowercaseString];
+}
+
+
+/**
+ * Split a password of the form {scheme}pass into an array of its components:
+ * {NSString *scheme, NString *pass, NSInteger encoding}, where encoding is
+ * the enum keyEncoding converted to an integer value.
+ *
+ * @param defaultScheme If no scheme is given in cryptedPassword, fall back to this scheme
+ * @see asCryptedPassUsingScheme
+ * @see keyEncoding
+ * @return NSArray with the three elements described above
+ */
+- (NSArray *) splitPasswordWithDefaultScheme: (NSString *) defaultScheme
+{
+ NSString *scheme;
+ NSString *pass;
+ NSArray *schemeComps;
+ keyEncoding encoding;
+
+ NSRange range;
+ int selflen, len;
+
+ selflen = [self length];
+
+ scheme = [self extractCryptScheme];
+ len = [scheme length];
+ if (len > 0)
+ range = NSMakeRange (len+2, selflen-len-2);
+ else
+ range = NSMakeRange (0, selflen);
+ if (len == 0)
+ scheme = defaultScheme;
+
+ encoding = [NSString getDefaultEncodingForScheme: scheme];
+
+ // get the encoding which may be part of the scheme
+ // e.g. ssha.hex forces a hex encoded ssha scheme
+ // possible is "b64" or "hex"
+ schemeComps = [scheme componentsSeparatedByString: @"."];
+ if ([schemeComps count] == 2)
+ {
+ NSString *stringEncoding;
+ // scheme without encoding string is the first item
+ scheme = [schemeComps objectAtIndex: 0];
+ // encoding string is second item
+ stringEncoding = [schemeComps objectAtIndex: 1];
+ if ([stringEncoding caseInsensitiveCompare: @"hex"] == NSOrderedSame)
+ {
+ encoding = encHex;
+ }
+ else if ([stringEncoding caseInsensitiveCompare: @"b64"] == NSOrderedSame ||
+ [stringEncoding caseInsensitiveCompare: @"base64"] == NSOrderedSame)
+ {
+ encoding = encBase64;
+ }
+ }
+
+ pass = [self substringWithRange: range];
+ return [NSArray arrayWithObjects: scheme, pass, [NSNumber numberWithInt: encoding], nil];
+}
+
+/**
+ * Compare the hex or base64 encoded password with an encrypted password
+ *
+ * @param cryptedPassword The password to compare with, format {scheme}pass , "{scheme}" is optional
+ * @param theScheme If no scheme is given in cryptedPassword, fall back to this scheme
+ * @see asCryptedPassUsingScheme
+ * @return YES if the passwords are identical using this encryption scheme
+ */
+- (BOOL) isEqualToCrypted: (NSString *) cryptedPassword
+ withDefaultScheme: (NSString *) theScheme
+{
+ NSArray *passInfo;
+ NSString *selfCrypted;
+ NSString *pass;
+ NSString *scheme;
+ NSData *salt;
+ NSData *decodedData;
+ NSNumber *encodingNumber;
+ keyEncoding encoding;
+
+ // split scheme and pass
+ passInfo = [cryptedPassword splitPasswordWithDefaultScheme: theScheme];
+
+ scheme = [passInfo objectAtIndex: 0];
+ pass = [passInfo objectAtIndex: 1];
+ encodingNumber = [passInfo objectAtIndex: 2];
+ encoding = [encodingNumber integerValue];
+
+ if (encoding == encHex)
+ {
+ decodedData = [NSData decodeDataFromHexString: pass];
+
+ if(decodedData == nil)
+ {
+ decodedData = [NSData data];
+ }
+ else
+ {
+ // decoding was successful, now make sure
+ // that the pass is in lowercase since decodeDataFromHexString uses
+ // lowercase charaters, too
+ pass = [pass lowercaseString];
+ }
+ }
+ else if(encoding == encBase64)
+ {
+ decodedData = [pass dataByDecodingBase64];
+ if(decodedData == nil)
+ {
+ decodedData = [NSData data];
+ }
+ }
+ else
+ {
+ decodedData = [pass dataUsingEncoding: NSUTF8StringEncoding];
+ }
+
+ salt = [decodedData extractSalt: scheme];
+
+ // encrypt self with the salt an compare the results
+ selfCrypted = [self asCryptedPassUsingScheme: scheme
+ withSalt: salt
+ andEncoding: encoding];
+ // return always false when there was a problem
+ if (selfCrypted == nil)
+ return NO;
+
+ if ([selfCrypted isEqualToString: pass] == YES)
+ return YES;
+ return NO;
+}
+
+/**
+ * Calls asCryptedPassUsingScheme:withSalt:andEncoding: with an empty salt and uses
+ * the default encoding.
+ *
+ * @param passwordScheme
+ * @return If successful, the encrypted and encoded NSString of the format {scheme}pass, or nil if the scheme did not exists or an error occured
+ */
+- (NSString *) asCryptedPassUsingScheme: (NSString *) passwordScheme
+{
+ return [self asCryptedPassUsingScheme: passwordScheme
+ withSalt: [NSData data]
+ andEncoding: encDefault];
+}
+
+/**
+ * Uses NSData -asCryptedPassUsingScheme to encrypt the string and converts the
+ * binary data back to a readable string using userEncoding
+ *
+ * @param passwordScheme The scheme to use
+ * @param theSalt The binary data of the salt
+ * @param userEncoding The encoding (plain, hex, base64) to be used
+ * @return If successful, the encrypted and encoded NSString of the format {scheme}pass, or nil if the scheme did not exists or an error occured
+ */
+- (NSString *) asCryptedPassUsingScheme: (NSString *) passwordScheme
+ withSalt: (NSData *) theSalt
+ andEncoding: (keyEncoding) userEncoding
+{
+ keyEncoding dataEncoding;
+ NSData* cryptedData;
+ // convert NSString to NSData and apply encryption scheme
+ cryptedData = [self dataUsingEncoding: NSUTF8StringEncoding];
+ cryptedData = [cryptedData asCryptedPassUsingScheme: passwordScheme withSalt: theSalt];
+ // abort on unsupported scheme or error
+ if (cryptedData == nil)
+ return nil;
+
+ // use default encoding scheme, when set to default
+ if (userEncoding == encDefault)
+ dataEncoding = [NSString getDefaultEncodingForScheme: passwordScheme];
+ else
+ dataEncoding = userEncoding;
+
+ if (dataEncoding == encHex)
+ {
+ // hex encoding
+ return [NSData encodeDataAsHexString: cryptedData];
+ }
+ else if(dataEncoding == encBase64)
+ {
+ // base64 encoding
+ NSString *s = [[NSString alloc] initWithData: [cryptedData dataByEncodingBase64WithLineLength: 1024]
+ encoding: NSASCIIStringEncoding];
+ return [s autorelease];
+ }
+
+ // plain string
+ return [[[NSString alloc] initWithData: cryptedData encoding: NSUTF8StringEncoding] autorelease];
+}
+
+/**
+ * Returns the encoding for a specified scheme
+ *
+ * @param passwordScheme The scheme for which to get the encoding.
+ * @see keyEncoding
+ * @return returns the encoding, if unknown returns encPlain
+ */
++ (keyEncoding) getDefaultEncodingForScheme: (NSString *) passwordScheme
+{
+ // in order to keep backwards-compatibility, hex encoding is used for sha1 here
+ if ([passwordScheme caseInsensitiveCompare: @"md5"] == NSOrderedSame ||
+ [passwordScheme caseInsensitiveCompare: @"plain-md5"] == NSOrderedSame ||
+ [passwordScheme caseInsensitiveCompare: @"sha"] == NSOrderedSame ||
+ [passwordScheme caseInsensitiveCompare: @"cram-md5"] == NSOrderedSame)
+ {
+ return encHex;
+ }
+ else if ([passwordScheme caseInsensitiveCompare: @"smd5"] == NSOrderedSame ||
+ [passwordScheme caseInsensitiveCompare: @"ldap-md5"] == NSOrderedSame ||
+ [passwordScheme caseInsensitiveCompare: @"ssha"] == NSOrderedSame ||
+ [passwordScheme caseInsensitiveCompare: @"sha256"] == NSOrderedSame ||
+ [passwordScheme caseInsensitiveCompare: @"ssha256"] == NSOrderedSame ||
+ [passwordScheme caseInsensitiveCompare: @"sha512"] == NSOrderedSame ||
+ [passwordScheme caseInsensitiveCompare: @"ssha512"] == NSOrderedSame)
+ {
+ return encBase64;
+ }
+ return encPlain;
+}
+
+/**
+ * Encrypts the data with SHA1 scheme and returns the hex-encoded data
+ *
+ * @return If successful, sha1 encrypted and with hex encoded string
+ */
+- (NSString *) asSHA1String;
+{
+ NSData *cryptData;
+ cryptData = [self dataUsingEncoding: NSUTF8StringEncoding];
+ return [NSData encodeDataAsHexString: [cryptData asSHA1] ];
+}
+
+/**
+ * Encrypts the data with Plain MD5 scheme and returns the hex-encoded data
+ *
+ * @return If successful, MD5 encrypted and with hex encoded string
+ */
+- (NSString *) asMD5String;
+{
+ NSData *cryptData;
+ cryptData = [self dataUsingEncoding: NSUTF8StringEncoding];
+ return [NSData encodeDataAsHexString: [cryptData asMD5] ];
+}
+
+@end
============================================================
--- SoObjects/SOGo/NSString+Utilities.h d2661ef19253d96d64dcf11127ffe6882f90b4d4
+++ SoObjects/SOGo/NSString+Utilities.h c38d80d6ebe9592a9f2ee954282718d7265dec51
@@ -66,10 +66,6 @@
- (id) objectFromJSONString;
-- (NSString *) asCryptStringUsingSalt: (NSString *) theSalt;
-- (NSString *) asMD5String;
-- (NSString *) asSHA1String;
-
- (NSString *) asSafeSQLString;
- (NSUInteger) countOccurrencesOfString: (NSString *) substring;
============================================================
--- SoObjects/SOGo/NSString+Utilities.m 800b97cfc8f806e14c310ed8628c94a9934fd80a
+++ SoObjects/SOGo/NSString+Utilities.m eb4c7cd585c64cb8fb0273c3e89be36da11e5f24
@@ -21,10 +21,6 @@
* Boston, MA 02111-1307, USA.
*/
-#ifndef __OpenBSD__
-#include <crypt.h>
-#endif
-
#import <Foundation/NSArray.h>
#import <Foundation/NSCharacterSet.h>
#import <Foundation/NSData.h>
@@ -45,12 +41,6 @@
#import "NSString+Utilities.h"
-#define _XOPEN_SOURCE 1
-#include <unistd.h>
-#include <openssl/evp.h>
-#include <openssl/md5.h>
-#include <openssl/sha.h>
-
static NSMutableCharacterSet *urlNonEndingChars = nil;
static NSMutableCharacterSet *urlAfterEndingChars = nil;
static NSMutableCharacterSet *urlStartChars = nil;
@@ -534,48 +524,6 @@ static int cssEscapingCount;
return object;
}
-- (NSString *) asCryptStringUsingSalt: (NSString *) theSalt
-{
- char *buf;
-
- // The salt is weak here, but who cares anyway, crypt should not
- // be used anymore
- buf = crypt([self UTF8String], [theSalt UTF8String]);
- return [NSString stringWithUTF8String: buf];
-}
-
-- (NSString *) asMD5String
-{
- unsigned char md[MD5_DIGEST_LENGTH];
- char buf[80];
- int i;
-
- memset(md, 0, MD5_DIGEST_LENGTH);
- memset(buf, 0, 80);
-
- EVP_Digest((const void *) [self UTF8String], strlen([self UTF8String]), md, NULL, EVP_md5(), NULL);
- for (i = 0; i < MD5_DIGEST_LENGTH; i++)
- sprintf(&(buf[i*2]), "%02x", md[i]);
-
- return [NSString stringWithUTF8String: buf];
-}
-
-- (NSString *) asSHA1String
-{
- unsigned char sha[SHA_DIGEST_LENGTH];
- char buf[80];
- int i;
-
- memset(sha, 0, SHA_DIGEST_LENGTH);
- memset(buf, 0, 80);
-
- SHA1((const void *)[self UTF8String], strlen([self UTF8String]), sha);
- for (i = 0; i < SHA_DIGEST_LENGTH; i++)
- sprintf(&(buf[i*2]), "%02x", sha[i]);
-
- return [NSString stringWithUTF8String: buf];
-}
-
- (NSString *) asSafeSQLString
{
return [[self stringByReplacingString: @"\\" withString: @"\\\\"]
============================================================
--- SoObjects/SOGo/SOGoUserManager.m 9bf968b36558145edd71840dd180876c1d5d7175
+++ SoObjects/SOGo/SOGoUserManager.m 344659dcdf8d929bb824d8856d05c1de85ce801e
@@ -33,6 +33,7 @@
#import "NSArray+Utilities.h"
#import "NSString+Utilities.h"
+#import "NSString+Crypto.h"
#import "NSObject+Utilities.h"
#import "SOGoDomainDefaults.h"
#import "SOGoSource.h"
============================================================
--- SoObjects/SOGo/SQLSource.m 8e7fb09f6d34231ed0e1c57db516961d7a8bf0bb
+++ SoObjects/SOGo/SQLSource.m 569afbdf32cf6a95315d8e99cd910a0abaa2e9ad
@@ -39,6 +39,7 @@
#import "SOGoConstants.h"
#import "NSString+Utilities.h"
+#import "NSString+Crypto.h"
#import "SQLSource.h"
@@ -47,7 +48,7 @@
*
* c_uid - will be used for authentication - it's a username or username@domain.tld)
* c_name - which can be identical to c_uid - will be used to uniquely identify entries)
- * c_password - password of the user, plain-text, md5 or sha encoded for now
+ * c_password - password of the user, possible values: plain, md5, plain-md5, sha, ssha, sha256, ssha256, ssha2512, smd5, crypt, md5-crypt, cram-md5 (with or without ".hex" or ".b64" appended)
* c_cn - the user's common name
* mail - the user's mail address
*
@@ -157,28 +158,8 @@
if (!plainPassword || !encryptedPassword)
return NO;
- if ([_userPasswordAlgorithm caseInsensitiveCompare: @"none"] == NSOrderedSame)
- {
- return [plainPassword isEqualToString: encryptedPassword];
- }
- else if ([_userPasswordAlgorithm caseInsensitiveCompare: @"crypt"] == NSOrderedSame)
- {
- return [[plainPassword asCryptStringUsingSalt: encryptedPassword] isEqualToString: encryptedPassword];
- }
- else if ([_userPasswordAlgorithm caseInsensitiveCompare: @"md5"] == NSOrderedSame)
- {
- return [[plainPassword asMD5String] isEqualToString: encryptedPassword];
- }
- else if ([_userPasswordAlgorithm caseInsensitiveCompare: @"sha"] == NSOrderedSame)
- {
-
- return [[plainPassword asSHA1String] isEqualToString: encryptedPassword];
- }
-
-
- [self errorWithFormat: @"Unsupported user-password algorithm: %@", _userPasswordAlgorithm];
-
- return NO;
+ return [plainPassword isEqualToCrypted: encryptedPassword
+ withDefaultScheme: _userPasswordAlgorithm];
}
/**
@@ -189,26 +170,13 @@
*/
- (NSString *) _encryptPassword: (NSString *) plainPassword
{
- if ([_userPasswordAlgorithm caseInsensitiveCompare: @"none"] == NSOrderedSame)
- {
- return plainPassword;
- }
- else if ([_userPasswordAlgorithm caseInsensitiveCompare: @"crypt"] == NSOrderedSame)
- {
- return [plainPassword asCryptStringUsingSalt: [plainPassword asMD5String]];
- }
- else if ([_userPasswordAlgorithm caseInsensitiveCompare: @"md5"] == NSOrderedSame)
- {
- return [plainPassword asMD5String];
- }
- else if ([_userPasswordAlgorithm caseInsensitiveCompare: @"sha"] == NSOrderedSame)
- {
- return [plainPassword asSHA1String];
- }
-
- [self errorWithFormat: @"Unsupported user-password algorithm: %@", _userPasswordAlgorithm];
-
- return plainPassword;
+ NSString *password;
+ password = [plainPassword asCryptedPassUsingScheme: _userPasswordAlgorithm];
+
+ if (password == nil)
+ [self errorWithFormat: @"Unsupported user-password algorithm: %@", _userPasswordAlgorithm];
+
+ return [NSString stringWithFormat: @"{%@}%@", _userPasswordAlgorithm, password];
}
//
|
|
The v4 patch adds CRAM-MD5 as scheme and documents all methods implemented by me. |
|
supportedExtension: 1.3.6.1.4.1.4203.1.11.1 and leave it at the LDAP side to handle password changes. http://www.networksorcery.com/enp/rfc/rfc3062.txt LDAP password changes related to http://www.sogo.nu/bugs/view.php?id=1804&nbn=2 |
|
|
Yes, for LDAP this is clearly the better solution (if supported), but as long as this extension is not supported, this may be a way... Anyways, for SQL based authentication this is clearly needed. |
|
|
2012-05-22 13:53
|
sqlsource_prepend_pw_schemes.patch (5,001 bytes)
#
# old_revision [471dc0f5e3b2a6fc571aca4e14c0a5161cf7af0b]
#
# patch "SoObjects/SOGo/SQLSource.h"
# from [da3279f8ce2fc584f8c4e28ab8718a2d59e0d182]
# to [c336b0e395923d3dbd0112dfe61fff4d0e64ab02]
#
# patch "SoObjects/SOGo/SQLSource.m"
# from [8e7fb09f6d34231ed0e1c57db516961d7a8bf0bb]
# to [16b4bee44ea4099f441c684a17aead9329be0bae]
#
============================================================
--- SoObjects/SOGo/SQLSource.h da3279f8ce2fc584f8c4e28ab8718a2d59e0d182
+++ SoObjects/SOGo/SQLSource.h c336b0e395923d3dbd0112dfe61fff4d0e64ab02
@@ -45,6 +45,7 @@
NSString *_imapHostField;
NSString *_userPasswordAlgorithm;
NSURL *_viewURL;
+ BOOL _prependPasswordScheme;
/* resources handling */
NSString *_kindField;
============================================================
--- SoObjects/SOGo/SQLSource.m 8e7fb09f6d34231ed0e1c57db516961d7a8bf0bb
+++ SoObjects/SOGo/SQLSource.m 16b4bee44ea4099f441c684a17aead9329be0bae
@@ -39,6 +39,7 @@
#import "SOGoConstants.h"
#import "NSString+Utilities.h"
+#import "NSString+Crypto.h"
#import "SQLSource.h"
@@ -47,7 +48,10 @@
*
* c_uid - will be used for authentication - it's a username or username@domain.tld)
* c_name - which can be identical to c_uid - will be used to uniquely identify entries)
- * c_password - password of the user, plain-text, md5 or sha encoded for now
+ * c_password - password of the user, can be encoded in {scheme}pass format, or when stored without
+ * scheme it uses the scheme set in userPasswordAlgorithm.
+ * Possible algorithms are: plain, md5, crypt-md5, sha, ssha (including 256/512 variants),
+ * cram-md5, smd5, crypt, crypt-md5
* c_cn - the user's common name
* mail - the user's mail address
*
@@ -63,8 +67,12 @@
* canAuthenticate = YES;
* isAddressBook = YES;
* userPasswordAlgorithm = md5;
+ * prependPasswordScheme = YES;
* }
*
+ * If prependPasswordScheme is set to YES, the generated passwords will have the format {scheme}password.
+ * If it is NO (the default), the password will be written to database without encryption scheme.
+ *
*/
@implementation SQLSource
@@ -126,6 +134,10 @@
ASSIGN(_kindField, [udSource objectForKey: @"KindFieldName"]);
ASSIGN(_multipleBookingsField, [udSource objectForKey: @"MultipleBookingsFieldName"]);
ASSIGN(_domainField, [udSource objectForKey: @"DomainFieldName"]);
+ if ([udSource objectForKey: @"prependPasswordScheme"])
+ _prependPasswordScheme = [[udSource objectForKey: @"prependPasswordScheme"] boolValue];
+ else
+ _prependPasswordScheme = NO;
if (!_userPasswordAlgorithm)
_userPasswordAlgorithm = @"none";
@@ -157,28 +169,8 @@
if (!plainPassword || !encryptedPassword)
return NO;
- if ([_userPasswordAlgorithm caseInsensitiveCompare: @"none"] == NSOrderedSame)
- {
- return [plainPassword isEqualToString: encryptedPassword];
- }
- else if ([_userPasswordAlgorithm caseInsensitiveCompare: @"crypt"] == NSOrderedSame)
- {
- return [[plainPassword asCryptStringUsingSalt: encryptedPassword] isEqualToString: encryptedPassword];
- }
- else if ([_userPasswordAlgorithm caseInsensitiveCompare: @"md5"] == NSOrderedSame)
- {
- return [[plainPassword asMD5String] isEqualToString: encryptedPassword];
- }
- else if ([_userPasswordAlgorithm caseInsensitiveCompare: @"sha"] == NSOrderedSame)
- {
-
- return [[plainPassword asSHA1String] isEqualToString: encryptedPassword];
- }
-
-
- [self errorWithFormat: @"Unsupported user-password algorithm: %@", _userPasswordAlgorithm];
-
- return NO;
+ return [plainPassword isEqualToCrypted: encryptedPassword
+ withDefaultScheme: _userPasswordAlgorithm];
}
/**
@@ -189,26 +181,20 @@
*/
- (NSString *) _encryptPassword: (NSString *) plainPassword
{
- if ([_userPasswordAlgorithm caseInsensitiveCompare: @"none"] == NSOrderedSame)
- {
- return plainPassword;
- }
- else if ([_userPasswordAlgorithm caseInsensitiveCompare: @"crypt"] == NSOrderedSame)
- {
- return [plainPassword asCryptStringUsingSalt: [plainPassword asMD5String]];
- }
- else if ([_userPasswordAlgorithm caseInsensitiveCompare: @"md5"] == NSOrderedSame)
- {
- return [plainPassword asMD5String];
- }
- else if ([_userPasswordAlgorithm caseInsensitiveCompare: @"sha"] == NSOrderedSame)
- {
- return [plainPassword asSHA1String];
- }
+ NSString *pass;
+ NSString* result;
- [self errorWithFormat: @"Unsupported user-password algorithm: %@", _userPasswordAlgorithm];
-
- return plainPassword;
+ pass = [plainPassword asCryptedPassUsingScheme: _userPasswordAlgorithm];
+
+ if (pass == nil)
+ [self errorWithFormat: @"Unsupported user-password algorithm: %@", _userPasswordAlgorithm];
+
+ if (_prependPasswordScheme)
+ result = [NSString stringWithFormat: @"{%@}%@", _userPasswordAlgorithm, pass];
+ else
+ result = pass;
+
+ return result;
}
//
|
|
The patch sqlsource_prepend_pw_schemes.patch adds a new option to the SQL settings, it is now optional to add the scheme in the format "{scheme}pass", the default is to add the pass without the scheme to be more backwards-compatible. |
|
|
Is there any news when this (md5-crypt) will be included in a SOGo 1.3.15+ release? I really need this to have SOGo handle my old style /etc/passwd passwords. At the moment SOGo can read them but not write. |
|
|
2012-05-31 14:25
|
crypto_v5.patch (45,206 bytes)
#
# old_revision [471dc0f5e3b2a6fc571aca4e14c0a5161cf7af0b]
#
# add_file "SoObjects/SOGo/NSData+Crypto.h"
# content [4e1a09d2530e3934966cf7547ffc6ee9411b4d0d]
#
# add_file "SoObjects/SOGo/NSData+Crypto.m"
# content [6d71c327de926a32a542f1cbdf923c1d6435c3f2]
#
# add_file "SoObjects/SOGo/NSString+Crypto.h"
# content [a15042732241b52c399c8667dc0fd281f97bc131]
#
# add_file "SoObjects/SOGo/NSString+Crypto.m"
# content [24e1ff6773c04e588410609f312824912d333911]
#
# patch "SoObjects/SOGo/GNUmakefile"
# from [9e466added539ccacaecd830173afdf9d4755802]
# to [0e4d9af53dddf014fe6fccf1d81e751d8dd2bc3c]
#
# patch "SoObjects/SOGo/LDAPSource.m"
# from [49cd1d275323dd81ba0ad5566e542b725856c4a9]
# to [0570acb4f559ddabe647634f52a877d17dbbce2d]
#
# patch "SoObjects/SOGo/NSString+Utilities.h"
# from [d2661ef19253d96d64dcf11127ffe6882f90b4d4]
# to [c38d80d6ebe9592a9f2ee954282718d7265dec51]
#
# patch "SoObjects/SOGo/NSString+Utilities.m"
# from [800b97cfc8f806e14c310ed8628c94a9934fd80a]
# to [eb4c7cd585c64cb8fb0273c3e89be36da11e5f24]
#
# patch "SoObjects/SOGo/SOGoUserManager.m"
# from [9bf968b36558145edd71840dd180876c1d5d7175]
# to [344659dcdf8d929bb824d8856d05c1de85ce801e]
#
# patch "SoObjects/SOGo/SQLSource.h"
# from [da3279f8ce2fc584f8c4e28ab8718a2d59e0d182]
# to [c336b0e395923d3dbd0112dfe61fff4d0e64ab02]
#
# patch "SoObjects/SOGo/SQLSource.m"
# from [8e7fb09f6d34231ed0e1c57db516961d7a8bf0bb]
# to [16b4bee44ea4099f441c684a17aead9329be0bae]
#
============================================================
--- SoObjects/SOGo/GNUmakefile 9e466added539ccacaecd830173afdf9d4755802
+++ SoObjects/SOGo/GNUmakefile 0e4d9af53dddf014fe6fccf1d81e751d8dd2bc3c
@@ -46,6 +46,8 @@ SOGo_HEADER_FILES = \
NSObject+Utilities.h \
NSString+DAV.h \
NSString+Utilities.h \
+ NSString+Crypto.h \
+ NSData+Crypto.h \
NSURL+DAV.h \
\
SOGoAuthenticator.h \
@@ -114,6 +116,8 @@ SOGo_OBJC_FILES = \
NSObject+Utilities.m \
NSString+DAV.m \
NSString+Utilities.m \
+ NSString+Crypto.m \
+ NSData+Crypto.m \
NSURL+DAV.m \
\
SOGoSession.m \
============================================================
--- SoObjects/SOGo/LDAPSource.m 49cd1d275323dd81ba0ad5566e542b725856c4a9
+++ SoObjects/SOGo/LDAPSource.m 0570acb4f559ddabe647634f52a877d17dbbce2d
@@ -40,6 +40,7 @@
#import "LDAPSourceSchema.h"
#import "NSArray+Utilities.h"
#import "NSString+Utilities.h"
+#import "NSString+Crypto.h"
#import "SOGoDomainDefaults.h"
#import "SOGoSystemDefaults.h"
@@ -639,26 +640,13 @@ andMultipleBookingsField: (NSString *) n
*/
- (NSString *) _encryptPassword: (NSString *) plainPassword
{
- if ([_userPasswordAlgorithm caseInsensitiveCompare: @"none"] == NSOrderedSame)
- {
- return plainPassword;
- }
- else if ([_userPasswordAlgorithm caseInsensitiveCompare: @"crypt"] == NSOrderedSame)
- {
- return [NSString stringWithFormat: @"{CRYPT}%@", [plainPassword asCryptStringUsingSalt: [plainPassword asMD5String]]];
- }
- else if ([_userPasswordAlgorithm caseInsensitiveCompare: @"md5"] == NSOrderedSame)
- {
- return [NSString stringWithFormat: @"{MD5}%@", [plainPassword asMD5String]];
- }
- else if ([_userPasswordAlgorithm caseInsensitiveCompare: @"sha"] == NSOrderedSame)
- {
- return [NSString stringWithFormat: @"{SHA}%@", [plainPassword asSHA1String]];
- }
-
- [self errorWithFormat: @"Unsupported user-password algorithm: %@", _userPasswordAlgorithm];
-
- return plainPassword;
+ NSString *pass;
+ pass = [plainPassword asCryptedPassUsingScheme: _userPasswordAlgorithm];
+
+ if (pass == nil)
+ [self errorWithFormat: @"Unsupported user-password algorithm: %@", _userPasswordAlgorithm];
+
+ return [NSString stringWithFormat: @"{%@}%@", _userPasswordAlgorithm, pass];
}
//
============================================================
--- /dev/null
+++ SoObjects/SOGo/NSData+Crypto.h 4e1a09d2530e3934966cf7547ffc6ee9411b4d0d
@@ -0,0 +1,57 @@
+/* NSData+Crypto.h - this file is part of SOGo
+ *
+ * Author: Nicolas Höft
+ *
+ * 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
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This file is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef NSDATA_CRYPTO_H
+#define NSDATA_CRYPTO_H
+
+#import <Foundation/NSData.h>
+
+@class NSObject;
+
+@interface NSData (SOGoCryptoExtension)
+
+- (NSData *) asCryptedPassUsingScheme: (NSString *) passwordScheme
+ withSalt: (NSData *) theSalt;
+
+- (NSData *) asMD5;
+- (NSData *) asSMD5UsingSalt: (NSData *) theSalt;
+- (NSData *) asSHA1;
+- (NSData *) asSSHAUsingSalt: (NSData *) theSalt;
+- (NSData *) asSHA256;
+- (NSData *) asSSHA256UsingSalt: (NSData *) theSalt;
+- (NSData *) asSHA512;
+- (NSData *) asSSHA512UsingSalt: (NSData *) theSalt;
+- (NSData *) asCramMD5;
+
+- (NSData *) asCryptUsingSalt: (NSData *) theSalt;
+- (NSData *) asMD5CryptUsingSalt: (NSData *) theSalt;
+
+- (NSData *) extractSalt: (NSString *) theScheme;
+
++ (NSData *) generateSaltForLength: (unsigned int) theLength
+ withBase64: (BOOL) doBase64;
++ (NSData *) generateSaltForLength: (unsigned int) theLength;
+
++ (NSString *) encodeDataAsHexString: (NSData* ) theData;
++ (NSData *) decodeDataFromHexString: (NSString* ) theString;
+
+@end
+
+#endif /* NSDATA_CRYPTO_H */
============================================================
--- /dev/null
+++ SoObjects/SOGo/NSData+Crypto.m 6d71c327de926a32a542f1cbdf923c1d6435c3f2
@@ -0,0 +1,617 @@
+/* NSData+Crypto.m - this file is part of SOGo
+ *
+ *
+ * Author: Nicolas Höft
+ *
+ * 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
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This file is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __OpenBSD__
+#include <crypt.h>
+#endif
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+#define _XOPEN_SOURCE 1
+#include <unistd.h>
+#include <openssl/evp.h>
+#include <openssl/md5.h>
+#include <openssl/sha.h>
+
+#import <Foundation/NSArray.h>
+#import <NGExtensions/NGBase64Coding.h>
+#import "NSData+Crypto.h"
+
+unsigned charTo4Bits(char c);
+
+
+@implementation NSData (SOGoCryptoExtension)
+
+/**
+ * Covert binary data to hex encoded data (lower-case).
+ *
+ * @param theData The NSData to be converted into a hex-encoded string.
+ * @return Hex-Encoded data
+ */
++ (NSString *) encodeDataAsHexString: (NSData *) theData
+{
+ unsigned int byteLength = [theData length], byteCounter = 0;
+ unsigned int stringLength = (byteLength * 2) + 1, stringCounter = 0;
+ unsigned char dstBuffer[stringLength];
+ unsigned char srcBuffer[byteLength];
+ unsigned char *srcPtr = srcBuffer;
+ [theData getBytes: srcBuffer];
+ const unsigned char t[16] = "0123456789abcdef";
+
+ for (; byteCounter < byteLength; byteCounter++)
+ {
+ unsigned src = *srcPtr;
+ dstBuffer[stringCounter++] = t[src >> 4];
+ dstBuffer[stringCounter++] = t[src & 15];
+ srcPtr++;
+ }
+
+ dstBuffer[stringCounter] = '\0';
+ return [NSString stringWithUTF8String: (char*)dstBuffer];
+}
+
+/**
+ * Covert hex-encoded data to binary data.
+ *
+ * @param theString The hex-encoded string to be converted into binary data (works both for upper and lowe case characters)
+ * @return binary data or nil if unsuccessful
+ */
++ (NSData *) decodeDataFromHexString: (NSString *) theString
+{
+ unsigned int stringLength = [theString length];
+ unsigned int byteLength = stringLength/2;
+ unsigned int byteCounter = 0;
+ unsigned char srcBuffer[stringLength];
+ [theString getCString:(char *)srcBuffer];
+ unsigned char *srcPtr = srcBuffer;
+ unsigned char dstBuffer[byteLength];
+ unsigned char *dst = dstBuffer;
+ while (byteCounter < byteLength)
+ {
+ unsigned char c = *srcPtr++;
+ unsigned char d = *srcPtr++;
+ unsigned hi = 0, lo = 0;
+ hi = charTo4Bits(c);
+ lo = charTo4Bits(d);
+ if (hi == 255 || lo == 255)
+ {
+ //errorCase
+ return nil;
+ }
+ dstBuffer[byteCounter++] = ((hi << 4) | lo);
+ }
+ return [NSData dataWithBytes: dst length: byteLength];
+}
+
+/**
+ * Generate a binary key which can be used for salting hashes.
+ *
+ * @param theLength length of the binary data to be generated in bytes
+ * @return Pseudo-random binary data with length theLength or nil, if an error occured
+ */
++ (NSData *) generateSaltForLength: (unsigned int) theLength
+{
+ return [NSData generateSaltForLength: theLength withBase64: NO];
+}
+
+/**
+ * Generate a binary key which can be used for salting hashes. When using
+ * with doBase64 == YES then the data will be longer than theLength
+ *
+ * @param theLength Length of the binary data to be generated in bytes
+ * @param doBase64 Convert the data into Base-64 before retuning it, be aware that this makes the binary data longer
+ * @return Pseudo-random binary data with length theLength or nil, if an error occured
+ */
++ (NSData *) generateSaltForLength: (unsigned int) theLength
+ withBase64: (BOOL) doBase64
+{
+ char *buf;
+ int fd;
+ NSData *data;
+
+ fd = open("/dev/urandom", O_RDONLY);
+
+ if (fd > 0)
+ {
+ buf = (char *)malloc(theLength);
+ read(fd, buf, theLength);
+ close(fd);
+
+ data = [NSData dataWithBytesNoCopy: buf length: theLength freeWhenDone: YES];
+ if(doBase64 == YES)
+ {
+ return [data dataByEncodingBase64WithLineLength: 1024];
+ }
+ return data;
+ }
+ return nil;
+}
+
+/**
+ * Encrypt/Hash the data with a given scheme
+ *
+ * @param passwordScheme The scheme to use for hashing/encryption.
+ * @param theSalt The salt to be used. If none is given but needed, it will be generated
+ * @return Binary data from the encryption by the specified scheme. On error the funciton returns nil.
+ */
+- (NSData *) asCryptedPassUsingScheme: (NSString *) passwordScheme
+ withSalt: (NSData *) theSalt
+{
+ if ([passwordScheme caseInsensitiveCompare: @"none"] == NSOrderedSame ||
+ [passwordScheme caseInsensitiveCompare: @"plain"] == NSOrderedSame ||
+ [passwordScheme caseInsensitiveCompare: @"cleartext"] == NSOrderedSame)
+ {
+ return self;
+ }
+ else if ([passwordScheme caseInsensitiveCompare: @"crypt"] == NSOrderedSame)
+ {
+ return [self asCryptUsingSalt: theSalt];
+ }
+ else if ([passwordScheme caseInsensitiveCompare: @"md5-crypt"] == NSOrderedSame)
+ {
+ return [self asMD5CryptUsingSalt: theSalt];
+ }
+ else if ([passwordScheme caseInsensitiveCompare: @"md5"] == NSOrderedSame ||
+ [passwordScheme caseInsensitiveCompare: @"plain-md5"] == NSOrderedSame ||
+ [passwordScheme caseInsensitiveCompare: @"ldap-md5"] == NSOrderedSame)
+ {
+ return [self asMD5];
+ }
+ else if ([passwordScheme caseInsensitiveCompare: @"cram-md5"] == NSOrderedSame)
+ {
+ return [self asCramMD5];
+ }
+ else if ([passwordScheme caseInsensitiveCompare: @"smd5"] == NSOrderedSame)
+ {
+ return [self asSMD5UsingSalt: theSalt];
+ }
+ else if ([passwordScheme caseInsensitiveCompare: @"sha"] == NSOrderedSame)
+ {
+ return [self asSHA1];
+ }
+ else if ([passwordScheme caseInsensitiveCompare: @"ssha"] == NSOrderedSame)
+ {
+ return [self asSSHAUsingSalt: theSalt];
+ }
+ else if ([passwordScheme caseInsensitiveCompare: @"sha256"] == NSOrderedSame)
+ {
+ return [self asSHA256];
+ }
+ else if ([passwordScheme caseInsensitiveCompare: @"ssha256"] == NSOrderedSame)
+ {
+ return [self asSSHA256UsingSalt: theSalt];
+ }
+ else if ([passwordScheme caseInsensitiveCompare: @"sha512"] == NSOrderedSame)
+ {
+ return [self asSHA512];
+ }
+ else if ([passwordScheme caseInsensitiveCompare: @"ssha512"] == NSOrderedSame)
+ {
+ return [self asSSHA512UsingSalt: theSalt];
+ }
+ // in case the scheme was not detected, return nil
+ return nil;
+}
+
+
+/**
+ * Hash the data with MD5. Uses openssl functions to generate it
+ *
+ * @return Binary data from MD5 hashing. On error the funciton returns nil.
+ */
+- (NSData *) asMD5
+{
+ unsigned char md5[MD5_DIGEST_LENGTH];
+ memset(md5, 0, MD5_DIGEST_LENGTH);
+
+ MD5([self bytes], [self length], md5);
+
+ return [NSData dataWithBytes: md5 length: MD5_DIGEST_LENGTH];
+}
+
+/**
+ * Hash the data with CRAM-MD5. Uses openssl functions to generate it.
+ *
+ * Note that the actual CRAM-MD5 algorithm also needs a challenge
+ * but this is not provided, this function actually calculalates
+ * only the context data which can be used for the challange-response
+ * algorithm then. This is just the underlying algorithm to store the passwords.
+ *
+ * The code is adopts the dovecot behaviour of storing the passwords
+ *
+ * @return Binary data from CRAM-MD5 'hashing'. On error the funciton returns nil.
+ */
+- (NSData *) asCramMD5
+{
+
+ MD5_CTX ctx;
+ unsigned char inner[64];
+ unsigned char outer[64];
+ unsigned char result[32];
+ unsigned char *r;
+ int i;
+ int len;
+ NSData *key;
+
+ if ([self length] > 64)
+ {
+ key = [self asMD5];
+ }
+ else
+ {
+ key = self;
+ }
+
+ len = [key length];
+ // fill with both inner and outer with key
+ memcpy(inner, [key bytes], len);
+ // make sure the rest of the bytes is zero
+ memset(inner + len, 0, 64 - len);
+ memcpy(outer, inner, 64);
+
+ for (i = 0; i < 64; i++)
+ {
+ inner[i] ^= 0x36;
+ outer[i] ^= 0x5c;
+ }
+// this transformation is needed for the correct cast to binary data
+#define CDPUT(p, c) { \
+ *p = (c) & 0xff; p++; \
+ *p = (c) >> 8 & 0xff; p++; \
+ *p = (c) >> 16 & 0xff; p++; \
+ *p = (c) >> 24 & 0xff; p++; \
+}
+
+ // generate first set of context bytes from outer data
+ MD5_Init(&ctx);
+ MD5_Transform(&ctx, outer);
+ r = result;
+ // convert this to correct binary data according to RFC 1321
+ CDPUT(r, ctx.A);
+ CDPUT(r, ctx.B);
+ CDPUT(r, ctx.C);
+ CDPUT(r, ctx.D);
+
+ // second set with inner data is appended to result string
+ MD5_Init(&ctx);
+ MD5_Transform(&ctx, inner);
+ // convert this to correct binary data
+ CDPUT(r, ctx.A);
+ CDPUT(r, ctx.B);
+ CDPUT(r, ctx.C);
+ CDPUT(r, ctx.D);
+
+ return [NSData dataWithBytes: result length: 32];
+}
+
+/**
+ * Hash the data with SHA1. Uses openssl functions to generate it.
+ *
+ * @return Binary data from SHA1 hashing. On error the funciton returns nil.
+ */
+- (NSData *) asSHA1
+{
+ unsigned char sha[SHA_DIGEST_LENGTH];
+ memset(sha, 0, SHA_DIGEST_LENGTH);
+
+ SHA1([self bytes], [self length], sha);
+
+ return [NSData dataWithBytes: sha length: SHA_DIGEST_LENGTH];
+}
+
+/**
+ * Hash the data with SHA256. Uses openssl functions to generate it.
+ *
+ * @return Binary data from SHA256 hashing. On error the funciton returns nil.
+ */
+- (NSData *) asSHA256
+{
+ unsigned char sha[SHA256_DIGEST_LENGTH];
+ memset(sha, 0, SHA256_DIGEST_LENGTH);
+
+ SHA256([self bytes], [self length], sha);
+
+ return [NSData dataWithBytes: sha length: SHA256_DIGEST_LENGTH];
+}
+
+/**
+ * Hash the data with SHA512. Uses openssl functions to generate it.
+ *
+ * @return Binary data from SHA512 hashing. On error the funciton returns nil.
+ */
+- (NSData *) asSHA512
+{
+ unsigned char sha[SHA512_DIGEST_LENGTH];
+ memset(sha, 0, SHA512_DIGEST_LENGTH);
+
+ SHA512([self bytes], [self length], sha);
+
+ return [NSData dataWithBytes: sha length: SHA512_DIGEST_LENGTH];
+}
+
+/**
+ * Hash the data with SSHA. Uses openssl functions to generate it.
+ *
+ * SSHA works following: SSHA(pass, salt) = SHA1(pass + salt) + saltData
+ *
+ * @param theSalt The salt to be used must not be nil, if empty, one will be generated
+ * @return Binary data from SHA1 hashing. On error the funciton returns nil.
+ */
+- (NSData *) asSSHAUsingSalt: (NSData *) theSalt
+{
+ //
+ NSMutableData *sshaData;
+
+ // generate salt, if not available
+ if ([theSalt length] == 0) theSalt = [NSData generateSaltForLength: 8];
+
+ // put the pass and salt together as one data array
+ sshaData = [NSMutableData dataWithData: self];
+ [sshaData appendData: theSalt];
+ // generate SHA1 from pass + salt
+ sshaData = [NSMutableData dataWithData: [sshaData asSHA1]];
+ // append salt again
+ [sshaData appendData: theSalt];
+
+ return sshaData;
+}
+
+/**
+ * Hash the data with SSHA256. Uses openssl functions to generate it.
+ *
+ * SSHA256 works following: SSHA256(pass, salt) = SHA256(pass + salt) + saltData
+ *
+ * @param theSalt The salt to be used must not be nil, if empty, one will be generated
+ * @return Binary data from SHA1 hashing. On error the funciton returns nil.
+ */
+
+- (NSData *) asSSHA256UsingSalt: (NSData *) theSalt
+{
+ NSMutableData *sshaData;
+
+ // generate salt, if not available
+ if ([theSalt length] == 0) theSalt = [NSData generateSaltForLength: 8];
+
+ // put the pass and salt together as one data array
+ sshaData = [NSMutableData dataWithData: self];
+ [sshaData appendData: theSalt];
+ // generate SHA1 from pass + salt
+ sshaData = [NSMutableData dataWithData: [sshaData asSHA256]];
+ // append salt again
+ [sshaData appendData: theSalt];
+
+ return sshaData;
+}
+
+/**
+ * Hash the data with SSHA512. Uses openssl functions to generate it.
+ *
+ * SSHA works following: SSHA512(pass, salt) = SHA512(pass + salt) + saltData
+ *
+ * @param theSalt The salt to be used must not be nil, if empty, one will be generated
+ * @return Binary data from SHA512 hashing. On error the funciton returns nil.
+ */
+
+- (NSData *) asSSHA512UsingSalt: (NSData *) theSalt
+{
+ NSMutableData *sshaData;
+
+ // generate salt, if not available
+ if ([theSalt length] == 0) theSalt = [NSData generateSaltForLength: 8];
+
+ // put the pass and salt together as one data array
+ sshaData = [NSMutableData dataWithData: self];
+ [sshaData appendData: theSalt];
+ // generate SHA1 from pass + salt
+ sshaData = [NSMutableData dataWithData: [sshaData asSHA512]];
+ // append salt again
+ [sshaData appendData: theSalt];
+
+ return sshaData;
+}
+
+/**
+ * Hash the data with SMD5. Uses openssl functions to generate it.
+ *
+ * SMD5 works following: SMD5(pass, salt) = MD5(pass + salt) + saltData
+ *
+ * @param theSalt The salt to be used must not be nil, if empty, one will be generated
+ * @return Binary data from SMD5 hashing. On error the funciton returns nil.
+ */
+- (NSData *) asSMD5UsingSalt: (NSData *) theSalt
+{
+ // SMD5 works following: SMD5(pass, salt) = MD5(pass + salt) + salt
+ NSMutableData *smdData;
+
+ // generate salt, if not available
+ if ([theSalt length] == 0) theSalt = [NSData generateSaltForLength: 8];
+
+ // put the pass and salt together as one data array
+ smdData = [NSMutableData dataWithData: self];
+ [smdData appendData: theSalt];
+ // generate SHA1 from pass + salt
+ smdData = [NSMutableData dataWithData: [smdData asMD5]];
+ // append salt again
+ [smdData appendData: theSalt];
+
+ return smdData;
+}
+
+
+/**
+ * Hash the data with CRYPT-MD5 as used in /etc/passwd nowadays. Uses crypt() function to generate it.
+ *
+ *
+ * @param theSalt The salt to be used must not be nil, if empty, one will be generated. It must be printable characters only.
+ * @return Binary data from CRYPT-MD5 hashing. On error the funciton returns nil.
+ */
+- (NSData *) asMD5CryptUsingSalt: (NSData *) theSalt
+{
+ char *buf;
+ NSMutableData *saltData;
+ NSString *cryptString;
+ NSString *saltString;
+
+ if ([theSalt length] == 0)
+ {
+ // make sure these characters are all printable by using base64
+ theSalt = [NSData generateSaltForLength: 8 withBase64: YES];
+ }
+ cryptString = [[NSString alloc] initWithData: self encoding: NSUTF8StringEncoding];
+
+ NSString * magic = @"$1$";
+ saltData = [NSMutableData dataWithData: [magic dataUsingEncoding: NSUTF8StringEncoding]];
+ [saltData appendData: theSalt];
+ // terminate with "$"
+ [saltData appendData: [@"$" dataUsingEncoding: NSUTF8StringEncoding]];
+
+ saltString = [[NSString alloc] initWithData: saltData encoding: NSUTF8StringEncoding];
+
+ buf = crypt([cryptString UTF8String], [saltString UTF8String]);
+ [cryptString release];
+ [saltString release];
+ if (!buf)
+ return nil;
+ return [NSData dataWithBytes: buf length: strlen(buf)];
+}
+
+/**
+ * Hash the data using crypt() function.
+ *
+ * @param theSalt The salt to be used must not be nil, if empty, one will be generated
+ * @return Binary data from CRYPT-MD5 hashing. On error the funciton returns nil.
+ */
+- (NSData *) asCryptUsingSalt: (NSData *) theSalt
+{
+ char *buf;
+ NSString *saltString;
+ NSString *cryptString;
+
+ // crypt() works with strings, so convert NSData to strings
+ cryptString = [[NSString alloc] initWithData: self encoding: NSUTF8StringEncoding];
+
+ if ([theSalt length] == 0) theSalt = [NSData generateSaltForLength: 8 withBase64: YES];
+
+ saltString = [[NSString alloc] initWithData: theSalt encoding: NSUTF8StringEncoding];
+
+ // The salt is weak here, but who cares anyway, crypt should not
+ // be used anymore
+ buf = crypt([cryptString UTF8String], [saltString UTF8String]);
+ [saltString release];
+ [cryptString release];
+ if (!buf)
+ return nil;
+ return [NSData dataWithBytes: buf length: strlen(buf)];
+}
+
+/**
+ * Get the salt from a password encrypted with a specied scheme
+ *
+ * @param theScheme Needed to get the salt correctly out of the pass
+ * @return The salt, if one was available in the password/scheme, else empty data
+ */
+- (NSData *) extractSalt: (NSString *) theScheme
+{
+ NSRange r;
+ int len;
+ len = [self length];
+ if (len == 0)
+ return [NSData data];
+
+ // for the ssha schemes the salt is appended at the endif
+ // so the range with the salt are bytes after each digest length
+ if ([theScheme caseInsensitiveCompare: @"crypt"] == NSOrderedSame)
+ {
+ // for crypt schemes simply use the whole string
+ // the crypt() function is able to extract it by itself
+ r = NSMakeRange(0, len);
+ }
+ else if ([theScheme caseInsensitiveCompare: @"md5-crypt"] == NSOrderedSame)
+ {
+ // md5 crypt is generated the following "$1$<salt>$<encrypted pass>"
+ NSString *cryptString;
+ NSArray *cryptParts;
+ cryptString = [NSString stringWithUTF8String: [self bytes] ];
+ cryptParts = [cryptString componentsSeparatedByString: @"$"];
+ // correct number of elements (first one is an empty string)
+ if ([cryptParts count] != 4)
+ {
+ return [NSData data];
+ }
+ // second is the identifier of md5-crypt
+ else if( [[cryptParts objectAtIndex: 1] caseInsensitiveCompare: @"1"] != NSOrderedSame )
+ {
+ return [NSData data];
+ }
+ // third is the salt; convert it to NSData
+ return [[cryptParts objectAtIndex: 2] dataUsingEncoding: NSUTF8StringEncoding];
+ }
+ else if ([theScheme caseInsensitiveCompare: @"ssha"] == NSOrderedSame)
+ {
+ r = NSMakeRange(SHA_DIGEST_LENGTH, len - SHA_DIGEST_LENGTH);
+ }
+ else if ([theScheme caseInsensitiveCompare: @"ssha256"] == NSOrderedSame)
+ {
+ r = NSMakeRange(SHA256_DIGEST_LENGTH, len - SHA256_DIGEST_LENGTH);
+ }
+ else if ([theScheme caseInsensitiveCompare: @"ssha512"] == NSOrderedSame)
+ {
+ r = NSMakeRange(SHA512_DIGEST_LENGTH, len - SHA512_DIGEST_LENGTH);
+ }
+ else if ([theScheme caseInsensitiveCompare: @"smd5"] == NSOrderedSame)
+ {
+ r = NSMakeRange(MD5_DIGEST_LENGTH, len - MD5_DIGEST_LENGTH);
+ }
+ else
+ {
+ // return empty string on unknown scheme
+ return [NSData data];
+ }
+
+ return [self subdataWithRange: r];
+}
+
+@end
+
+unsigned charTo4Bits(char c)
+{
+ unsigned bits = 0;
+ if (c > '/' && c < ':')
+ {
+ bits = c - '0';
+ }
+ else if (c > '@' && c < 'G')
+ {
+ bits = (c- 'A') + 10;
+ }
+ else if (c > '`' && c < 'g')
+ {
+ bits = (c- 'a') + 10;
+ }
+ else
+ {
+ bits = 255;
+ }
+ return bits;
+}
============================================================
--- /dev/null
+++ SoObjects/SOGo/NSString+Crypto.h a15042732241b52c399c8667dc0fd281f97bc131
@@ -0,0 +1,59 @@
+/* NSString+Crypto.h - this file is part of SOGo
+ *
+ * Author: Nicolas Höft
+ *
+ * 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
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This file is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef NSSTRING_CRYPTO_H
+#define NSSTRING_CRYPTO_H
+
+#import <Foundation/NSData.h>
+#import <Foundation/NSString.h>
+
+typedef enum {
+ encDefault, //!< default encoding, let the algorithm decide
+ encPlain, //!< the data is plain text, simply convert to string
+ encHex, //!< the data is hex encoded
+ encBase64, //!< base64 encoding
+} keyEncoding;
+
+@class NSObject;
+
+@interface NSString (SOGoCryptoExtension)
+
+
+- (BOOL) isEqualToCrypted: (NSString *) cryptedPassword
+ withDefaultScheme: (NSString *) theScheme;
+
+- (NSString *) asCryptedPassUsingScheme: (NSString *) passwordScheme
+ withSalt: (NSData *) theSalt
+ andEncoding: (keyEncoding) encoding;
+
+// this method uses the default encoding (base64, plain, hex)
+// and generates a salt when necessary
+- (NSString *) asCryptedPassUsingScheme: (NSString *) passwordScheme;
+
+- (NSArray *) splitPasswordWithDefaultScheme: (NSString *) defaultScheme;
+
+- (NSString *) asSHA1String;
+- (NSString *) asMD5String;
+
++ (keyEncoding) getDefaultEncodingForScheme: (NSString *) passwordScheme;
+
+@end
+
+#endif /* NSSTRING_CRYPTO_H */
============================================================
--- /dev/null
+++ SoObjects/SOGo/NSString+Crypto.m 24e1ff6773c04e588410609f312824912d333911
@@ -0,0 +1,301 @@
+/* NSString+Crypto.m - this file is part of SOGo
+ *
+ *
+ * Author: Nicolas Höft
+ *
+ * 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
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This file is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#import <Foundation/NSArray.h>
+#import <Foundation/NSValue.h>
+
+#import "NSString+Crypto.h"
+#import "NSData+Crypto.h"
+#import <NGExtensions/NGBase64Coding.h>
+
+@implementation NSString (SOGoCryptoExtension)
+
+/**
+ * Extracts the scheme from a string formed "{scheme}pass".
+ *
+ * @return The scheme or an empty string if the string did not contained a scheme in the format above
+ */
+- (NSString *) extractCryptScheme
+{
+ NSRange r;
+ int len;
+
+ len = [self length];
+ if (len == 0)
+ return @"";
+ if ([self characterAtIndex:0] != '{')
+ return @"";
+
+ r = [self rangeOfString:@"}" options:(NSLiteralSearch)];
+ if (r.length == 0)
+ return @"";
+
+ r.length = (r.location - 1);
+ r.location = 1;
+ return [[self substringWithRange:r] lowercaseString];
+}
+
+
+/**
+ * Split a password of the form {scheme}pass into an array of its components:
+ * {NSString *scheme, NString *pass, NSInteger encoding}, where encoding is
+ * the enum keyEncoding converted to an integer value.
+ *
+ * @param defaultScheme If no scheme is given in cryptedPassword, fall back to this scheme
+ * @see asCryptedPassUsingScheme
+ * @see keyEncoding
+ * @return NSArray with the three elements described above
+ */
+- (NSArray *) splitPasswordWithDefaultScheme: (NSString *) defaultScheme
+{
+ NSString *scheme;
+ NSString *pass;
+ NSArray *schemeComps;
+ keyEncoding encoding;
+
+ NSRange range;
+ int selflen, len;
+
+ selflen = [self length];
+
+ scheme = [self extractCryptScheme];
+ len = [scheme length];
+ if (len > 0)
+ range = NSMakeRange (len+2, selflen-len-2);
+ else
+ range = NSMakeRange (0, selflen);
+ if (len == 0)
+ scheme = defaultScheme;
+
+ encoding = [NSString getDefaultEncodingForScheme: scheme];
+
+ // get the encoding which may be part of the scheme
+ // e.g. ssha.hex forces a hex encoded ssha scheme
+ // possible is "b64" or "hex"
+ schemeComps = [scheme componentsSeparatedByString: @"."];
+ if ([schemeComps count] == 2)
+ {
+ NSString *stringEncoding;
+ // scheme without encoding string is the first item
+ scheme = [schemeComps objectAtIndex: 0];
+ // encoding string is second item
+ stringEncoding = [schemeComps objectAtIndex: 1];
+ if ([stringEncoding caseInsensitiveCompare: @"hex"] == NSOrderedSame)
+ {
+ encoding = encHex;
+ }
+ else if ([stringEncoding caseInsensitiveCompare: @"b64"] == NSOrderedSame ||
+ [stringEncoding caseInsensitiveCompare: @"base64"] == NSOrderedSame)
+ {
+ encoding = encBase64;
+ }
+ }
+
+ pass = [self substringWithRange: range];
+ return [NSArray arrayWithObjects: scheme, pass, [NSNumber numberWithInt: encoding], nil];
+}
+
+/**
+ * Compare the hex or base64 encoded password with an encrypted password
+ *
+ * @param cryptedPassword The password to compare with, format {scheme}pass , "{scheme}" is optional
+ * @param theScheme If no scheme is given in cryptedPassword, fall back to this scheme
+ * @see asCryptedPassUsingScheme
+ * @return YES if the passwords are identical using this encryption scheme
+ */
+- (BOOL) isEqualToCrypted: (NSString *) cryptedPassword
+ withDefaultScheme: (NSString *) theScheme
+{
+ NSArray *passInfo;
+ NSString *selfCrypted;
+ NSString *pass;
+ NSString *scheme;
+ NSData *salt;
+ NSData *decodedData;
+ NSNumber *encodingNumber;
+ keyEncoding encoding;
+
+ // split scheme and pass
+ passInfo = [cryptedPassword splitPasswordWithDefaultScheme: theScheme];
+
+ scheme = [passInfo objectAtIndex: 0];
+ pass = [passInfo objectAtIndex: 1];
+ encodingNumber = [passInfo objectAtIndex: 2];
+ encoding = [encodingNumber integerValue];
+
+ if (encoding == encHex)
+ {
+ decodedData = [NSData decodeDataFromHexString: pass];
+
+ if(decodedData == nil)
+ {
+ decodedData = [NSData data];
+ }
+ else
+ {
+ // decoding was successful, now make sure
+ // that the pass is in lowercase since decodeDataFromHexString uses
+ // lowercase charaters, too
+ pass = [pass lowercaseString];
+ }
+ }
+ else if(encoding == encBase64)
+ {
+ decodedData = [pass dataByDecodingBase64];
+ if(decodedData == nil)
+ {
+ decodedData = [NSData data];
+ }
+ }
+ else
+ {
+ decodedData = [pass dataUsingEncoding: NSUTF8StringEncoding];
+ }
+
+ salt = [decodedData extractSalt: scheme];
+
+ // encrypt self with the salt an compare the results
+ selfCrypted = [self asCryptedPassUsingScheme: scheme
+ withSalt: salt
+ andEncoding: encoding];
+ // return always false when there was a problem
+ if (selfCrypted == nil)
+ return NO;
+
+ if ([selfCrypted isEqualToString: pass] == YES)
+ return YES;
+ return NO;
+}
+
+/**
+ * Calls asCryptedPassUsingScheme:withSalt:andEncoding: with an empty salt and uses
+ * the default encoding.
+ *
+ * @param passwordScheme
+ * @return If successful, the encrypted and encoded NSString of the format {scheme}pass, or nil if the scheme did not exists or an error occured
+ */
+- (NSString *) asCryptedPassUsingScheme: (NSString *) passwordScheme
+{
+ return [self asCryptedPassUsingScheme: passwordScheme
+ withSalt: [NSData data]
+ andEncoding: encDefault];
+}
+
+/**
+ * Uses NSData -asCryptedPassUsingScheme to encrypt the string and converts the
+ * binary data back to a readable string using userEncoding
+ *
+ * @param passwordScheme The scheme to use
+ * @param theSalt The binary data of the salt
+ * @param userEncoding The encoding (plain, hex, base64) to be used
+ * @return If successful, the encrypted and encoded NSString of the format {scheme}pass, or nil if the scheme did not exists or an error occured
+ */
+- (NSString *) asCryptedPassUsingScheme: (NSString *) passwordScheme
+ withSalt: (NSData *) theSalt
+ andEncoding: (keyEncoding) userEncoding
+{
+ keyEncoding dataEncoding;
+ NSData* cryptedData;
+ // convert NSString to NSData and apply encryption scheme
+ cryptedData = [self dataUsingEncoding: NSUTF8StringEncoding];
+ cryptedData = [cryptedData asCryptedPassUsingScheme: passwordScheme withSalt: theSalt];
+ // abort on unsupported scheme or error
+ if (cryptedData == nil)
+ return nil;
+
+ // use default encoding scheme, when set to default
+ if (userEncoding == encDefault)
+ dataEncoding = [NSString getDefaultEncodingForScheme: passwordScheme];
+ else
+ dataEncoding = userEncoding;
+
+ if (dataEncoding == encHex)
+ {
+ // hex encoding
+ return [NSData encodeDataAsHexString: cryptedData];
+ }
+ else if(dataEncoding == encBase64)
+ {
+ // base64 encoding
+ NSString *s = [[NSString alloc] initWithData: [cryptedData dataByEncodingBase64WithLineLength: 1024]
+ encoding: NSASCIIStringEncoding];
+ return [s autorelease];
+ }
+
+ // plain string
+ return [[[NSString alloc] initWithData: cryptedData encoding: NSUTF8StringEncoding] autorelease];
+}
+
+/**
+ * Returns the encoding for a specified scheme
+ *
+ * @param passwordScheme The scheme for which to get the encoding.
+ * @see keyEncoding
+ * @return returns the encoding, if unknown returns encPlain
+ */
++ (keyEncoding) getDefaultEncodingForScheme: (NSString *) passwordScheme
+{
+ // in order to keep backwards-compatibility, hex encoding is used for sha1 here
+ if ([passwordScheme caseInsensitiveCompare: @"md5"] == NSOrderedSame ||
+ [passwordScheme caseInsensitiveCompare: @"plain-md5"] == NSOrderedSame ||
+ [passwordScheme caseInsensitiveCompare: @"sha"] == NSOrderedSame ||
+ [passwordScheme caseInsensitiveCompare: @"cram-md5"] == NSOrderedSame)
+ {
+ return encHex;
+ }
+ else if ([passwordScheme caseInsensitiveCompare: @"smd5"] == NSOrderedSame ||
+ [passwordScheme caseInsensitiveCompare: @"ldap-md5"] == NSOrderedSame ||
+ [passwordScheme caseInsensitiveCompare: @"ssha"] == NSOrderedSame ||
+ [passwordScheme caseInsensitiveCompare: @"sha256"] == NSOrderedSame ||
+ [passwordScheme caseInsensitiveCompare: @"ssha256"] == NSOrderedSame ||
+ [passwordScheme caseInsensitiveCompare: @"sha512"] == NSOrderedSame ||
+ [passwordScheme caseInsensitiveCompare: @"ssha512"] == NSOrderedSame)
+ {
+ return encBase64;
+ }
+ return encPlain;
+}
+
+/**
+ * Encrypts the data with SHA1 scheme and returns the hex-encoded data
+ *
+ * @return If successful, sha1 encrypted and with hex encoded string
+ */
+- (NSString *) asSHA1String;
+{
+ NSData *cryptData;
+ cryptData = [self dataUsingEncoding: NSUTF8StringEncoding];
+ return [NSData encodeDataAsHexString: [cryptData asSHA1] ];
+}
+
+/**
+ * Encrypts the data with Plain MD5 scheme and returns the hex-encoded data
+ *
+ * @return If successful, MD5 encrypted and with hex encoded string
+ */
+- (NSString *) asMD5String;
+{
+ NSData *cryptData;
+ cryptData = [self dataUsingEncoding: NSUTF8StringEncoding];
+ return [NSData encodeDataAsHexString: [cryptData asMD5] ];
+}
+
+@end
============================================================
--- SoObjects/SOGo/NSString+Utilities.h d2661ef19253d96d64dcf11127ffe6882f90b4d4
+++ SoObjects/SOGo/NSString+Utilities.h c38d80d6ebe9592a9f2ee954282718d7265dec51
@@ -66,10 +66,6 @@
- (id) objectFromJSONString;
-- (NSString *) asCryptStringUsingSalt: (NSString *) theSalt;
-- (NSString *) asMD5String;
-- (NSString *) asSHA1String;
-
- (NSString *) asSafeSQLString;
- (NSUInteger) countOccurrencesOfString: (NSString *) substring;
============================================================
--- SoObjects/SOGo/NSString+Utilities.m 800b97cfc8f806e14c310ed8628c94a9934fd80a
+++ SoObjects/SOGo/NSString+Utilities.m eb4c7cd585c64cb8fb0273c3e89be36da11e5f24
@@ -21,10 +21,6 @@
* Boston, MA 02111-1307, USA.
*/
-#ifndef __OpenBSD__
-#include <crypt.h>
-#endif
-
#import <Foundation/NSArray.h>
#import <Foundation/NSCharacterSet.h>
#import <Foundation/NSData.h>
@@ -45,12 +41,6 @@
#import "NSString+Utilities.h"
-#define _XOPEN_SOURCE 1
-#include <unistd.h>
-#include <openssl/evp.h>
-#include <openssl/md5.h>
-#include <openssl/sha.h>
-
static NSMutableCharacterSet *urlNonEndingChars = nil;
static NSMutableCharacterSet *urlAfterEndingChars = nil;
static NSMutableCharacterSet *urlStartChars = nil;
@@ -534,48 +524,6 @@ static int cssEscapingCount;
return object;
}
-- (NSString *) asCryptStringUsingSalt: (NSString *) theSalt
-{
- char *buf;
-
- // The salt is weak here, but who cares anyway, crypt should not
- // be used anymore
- buf = crypt([self UTF8String], [theSalt UTF8String]);
- return [NSString stringWithUTF8String: buf];
-}
-
-- (NSString *) asMD5String
-{
- unsigned char md[MD5_DIGEST_LENGTH];
- char buf[80];
- int i;
-
- memset(md, 0, MD5_DIGEST_LENGTH);
- memset(buf, 0, 80);
-
- EVP_Digest((const void *) [self UTF8String], strlen([self UTF8String]), md, NULL, EVP_md5(), NULL);
- for (i = 0; i < MD5_DIGEST_LENGTH; i++)
- sprintf(&(buf[i*2]), "%02x", md[i]);
-
- return [NSString stringWithUTF8String: buf];
-}
-
-- (NSString *) asSHA1String
-{
- unsigned char sha[SHA_DIGEST_LENGTH];
- char buf[80];
- int i;
-
- memset(sha, 0, SHA_DIGEST_LENGTH);
- memset(buf, 0, 80);
-
- SHA1((const void *)[self UTF8String], strlen([self UTF8String]), sha);
- for (i = 0; i < SHA_DIGEST_LENGTH; i++)
- sprintf(&(buf[i*2]), "%02x", sha[i]);
-
- return [NSString stringWithUTF8String: buf];
-}
-
- (NSString *) asSafeSQLString
{
return [[self stringByReplacingString: @"\\" withString: @"\\\\"]
============================================================
--- SoObjects/SOGo/SOGoUserManager.m 9bf968b36558145edd71840dd180876c1d5d7175
+++ SoObjects/SOGo/SOGoUserManager.m 344659dcdf8d929bb824d8856d05c1de85ce801e
@@ -33,6 +33,7 @@
#import "NSArray+Utilities.h"
#import "NSString+Utilities.h"
+#import "NSString+Crypto.h"
#import "NSObject+Utilities.h"
#import "SOGoDomainDefaults.h"
#import "SOGoSource.h"
============================================================
--- SoObjects/SOGo/SQLSource.h da3279f8ce2fc584f8c4e28ab8718a2d59e0d182
+++ SoObjects/SOGo/SQLSource.h c336b0e395923d3dbd0112dfe61fff4d0e64ab02
@@ -45,6 +45,7 @@
NSString *_imapHostField;
NSString *_userPasswordAlgorithm;
NSURL *_viewURL;
+ BOOL _prependPasswordScheme;
/* resources handling */
NSString *_kindField;
============================================================
--- SoObjects/SOGo/SQLSource.m 8e7fb09f6d34231ed0e1c57db516961d7a8bf0bb
+++ SoObjects/SOGo/SQLSource.m 16b4bee44ea4099f441c684a17aead9329be0bae
@@ -39,6 +39,7 @@
#import "SOGoConstants.h"
#import "NSString+Utilities.h"
+#import "NSString+Crypto.h"
#import "SQLSource.h"
@@ -47,7 +48,10 @@
*
* c_uid - will be used for authentication - it's a username or username@domain.tld)
* c_name - which can be identical to c_uid - will be used to uniquely identify entries)
- * c_password - password of the user, plain-text, md5 or sha encoded for now
+ * c_password - password of the user, can be encoded in {scheme}pass format, or when stored without
+ * scheme it uses the scheme set in userPasswordAlgorithm.
+ * Possible algorithms are: plain, md5, crypt-md5, sha, ssha (including 256/512 variants),
+ * cram-md5, smd5, crypt, crypt-md5
* c_cn - the user's common name
* mail - the user's mail address
*
@@ -63,8 +67,12 @@
* canAuthenticate = YES;
* isAddressBook = YES;
* userPasswordAlgorithm = md5;
+ * prependPasswordScheme = YES;
* }
*
+ * If prependPasswordScheme is set to YES, the generated passwords will have the format {scheme}password.
+ * If it is NO (the default), the password will be written to database without encryption scheme.
+ *
*/
@implementation SQLSource
@@ -126,6 +134,10 @@
ASSIGN(_kindField, [udSource objectForKey: @"KindFieldName"]);
ASSIGN(_multipleBookingsField, [udSource objectForKey: @"MultipleBookingsFieldName"]);
ASSIGN(_domainField, [udSource objectForKey: @"DomainFieldName"]);
+ if ([udSource objectForKey: @"prependPasswordScheme"])
+ _prependPasswordScheme = [[udSource objectForKey: @"prependPasswordScheme"] boolValue];
+ else
+ _prependPasswordScheme = NO;
if (!_userPasswordAlgorithm)
_userPasswordAlgorithm = @"none";
@@ -157,28 +169,8 @@
if (!plainPassword || !encryptedPassword)
return NO;
- if ([_userPasswordAlgorithm caseInsensitiveCompare: @"none"] == NSOrderedSame)
- {
- return [plainPassword isEqualToString: encryptedPassword];
- }
- else if ([_userPasswordAlgorithm caseInsensitiveCompare: @"crypt"] == NSOrderedSame)
- {
- return [[plainPassword asCryptStringUsingSalt: encryptedPassword] isEqualToString: encryptedPassword];
- }
- else if ([_userPasswordAlgorithm caseInsensitiveCompare: @"md5"] == NSOrderedSame)
- {
- return [[plainPassword asMD5String] isEqualToString: encryptedPassword];
- }
- else if ([_userPasswordAlgorithm caseInsensitiveCompare: @"sha"] == NSOrderedSame)
- {
-
- return [[plainPassword asSHA1String] isEqualToString: encryptedPassword];
- }
-
-
- [self errorWithFormat: @"Unsupported user-password algorithm: %@", _userPasswordAlgorithm];
-
- return NO;
+ return [plainPassword isEqualToCrypted: encryptedPassword
+ withDefaultScheme: _userPasswordAlgorithm];
}
/**
@@ -189,26 +181,20 @@
*/
- (NSString *) _encryptPassword: (NSString *) plainPassword
{
- if ([_userPasswordAlgorithm caseInsensitiveCompare: @"none"] == NSOrderedSame)
- {
- return plainPassword;
- }
- else if ([_userPasswordAlgorithm caseInsensitiveCompare: @"crypt"] == NSOrderedSame)
- {
- return [plainPassword asCryptStringUsingSalt: [plainPassword asMD5String]];
- }
- else if ([_userPasswordAlgorithm caseInsensitiveCompare: @"md5"] == NSOrderedSame)
- {
- return [plainPassword asMD5String];
- }
- else if ([_userPasswordAlgorithm caseInsensitiveCompare: @"sha"] == NSOrderedSame)
- {
- return [plainPassword asSHA1String];
- }
+ NSString *pass;
+ NSString* result;
- [self errorWithFormat: @"Unsupported user-password algorithm: %@", _userPasswordAlgorithm];
-
- return plainPassword;
+ pass = [plainPassword asCryptedPassUsingScheme: _userPasswordAlgorithm];
+
+ if (pass == nil)
+ [self errorWithFormat: @"Unsupported user-password algorithm: %@", _userPasswordAlgorithm];
+
+ if (_prependPasswordScheme)
+ result = [NSString stringWithFormat: @"{%@}%@", _userPasswordAlgorithm, pass];
+ else
+ result = pass;
+
+ return result;
}
//
|
| Date Modified | Username | Field | Change |
|---|---|---|---|
| 2012-01-29 15:28 | the_nic | New Issue | |
| 2012-01-29 15:28 | the_nic | File Added: nsstring+crypto.patch | |
| 2012-01-29 16:00 | ludovic | Note Added: 0003337 | |
| 2012-01-29 17:47 | the_nic | Note Added: 0003338 | |
| 2012-01-29 17:48 | the_nic | File Added: nsstring+crypto_updated.patch | |
| 2012-03-04 12:24 | the_nic | Note Added: 0003520 | |
| 2012-03-16 18:36 | ludovic | Target Version | => 1.3.14 |
| 2012-03-23 12:33 | francis | Target Version | 1.3.14 => 1.3.15 |
| 2012-04-02 07:47 | BattleMage | Note Added: 0003682 | |
| 2012-04-16 15:43 | the_nic | Note Added: 0003743 | |
| 2012-05-09 15:41 | ludovic | Target Version | 1.3.15 => 1.3.16 |
| 2012-05-10 21:03 | pgauret | Note Added: 0003884 | |
| 2012-05-16 17:20 | chrroessner | Note Added: 0003917 | |
| 2012-05-16 18:17 | aschild | Note Added: 0003918 | |
| 2012-05-19 15:20 | the_nic | Note Added: 0003926 | |
| 2012-05-20 11:34 | the_nic | File Added: crypto_v3.patch | |
| 2012-05-20 11:47 | the_nic | Note Added: 0003927 | |
| 2012-05-20 12:30 | the_nic | Note Edited: 0003927 | |
| 2012-05-22 12:09 | the_nic | File Added: crypto_v4.patch | |
| 2012-05-22 12:10 | the_nic | Note Added: 0003931 | |
| 2012-05-22 12:20 | chrroessner | Note Added: 0003932 | |
| 2012-05-22 12:25 | the_nic | Note Added: 0003934 | |
| 2012-05-22 13:53 | the_nic | File Added: sqlsource_prepend_pw_schemes.patch | |
| 2012-05-22 13:55 | the_nic | Note Added: 0003935 | |
| 2012-05-31 06:44 | Hans de Groot | Note Added: 0003986 | |
| 2012-05-31 14:25 | the_nic | File Added: crypto_v5.patch | |
| 2012-05-31 14:52 | ludovic | Note Added: 0003988 | |
| 2012-05-31 14:52 | ludovic | Status | new => resolved |
| 2012-05-31 14:52 | ludovic | Fixed in Version | => 1.3.16 |
| 2012-05-31 14:52 | ludovic | Resolution | open => fixed |
| 2012-05-31 14:52 | ludovic | Assigned To | => ludovic |
| 2012-05-31 14:52 | ludovic | Status | resolved => closed |