propagate from branch 'ca.inverse.sogo.1_3_16' (head 3c4baa92c5634d4da061da739a9b544d04c44586)
to branch 'ca.inverse.sogo' (head 2a93bffe818fbb8a4df73ae7b00f528618484717) Monotone-Parent: 2a93bffe818fbb8a4df73ae7b00f528618484717 Monotone-Parent: 3c4baa92c5634d4da061da739a9b544d04c44586 Monotone-Revision: 1448263394a13cd5b07e5e7b96a8698dece9f745 Monotone-Author: ludovic@Sophos.ca Monotone-Date: 2012-05-31T13:25:20 Monotone-Branch: ca.inverse.sogo
This commit is contained in:
commit
a449fd3fae
|
@ -4,6 +4,8 @@
|
|||
all attribute names
|
||||
* SoObject/SOGo/LDAPSource.m - moved the lowercasing
|
||||
of attributes to SOPE.
|
||||
* Added patch from bug #1608. This add many more password
|
||||
schemes for SQL authentication.
|
||||
|
||||
2012-05-29 Francis Lachapelle <flachapelle@inverse.ca>
|
||||
|
||||
|
|
|
@ -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 \
|
||||
|
|
|
@ -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"
|
||||
|
||||
|
@ -581,26 +582,13 @@ andMultipleBookingsField: (NSString *) newMultipleBookingsField
|
|||
*/
|
||||
- (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]];
|
||||
}
|
||||
NSString *pass;
|
||||
pass = [plainPassword asCryptedPassUsingScheme: _userPasswordAlgorithm];
|
||||
|
||||
if (pass == nil)
|
||||
[self errorWithFormat: @"Unsupported user-password algorithm: %@", _userPasswordAlgorithm];
|
||||
|
||||
return plainPassword;
|
||||
return [NSString stringWithFormat: @"{%@}%@", _userPasswordAlgorithm, pass];
|
||||
}
|
||||
|
||||
//
|
||||
|
|
61
SoObjects/SOGo/NSData+Crypto.h
Normal file
61
SoObjects/SOGo/NSData+Crypto.h
Normal file
|
@ -0,0 +1,61 @@
|
|||
/* NSData+Crypto.h - this file is part of SOGo
|
||||
*
|
||||
* Copyright (C) 2012 Nicolas Höft
|
||||
* Copyright (C) 2012 Inverse inc.
|
||||
*
|
||||
* Author: Nicolas Höft
|
||||
* Inverse inc.
|
||||
*
|
||||
* This file is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* 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 */
|
620
SoObjects/SOGo/NSData+Crypto.m
Normal file
620
SoObjects/SOGo/NSData+Crypto.m
Normal file
|
@ -0,0 +1,620 @@
|
|||
/* NSData+Crypto.m - this file is part of SOGo
|
||||
*
|
||||
* Copyright (C) 2012 Nicolas Höft
|
||||
* Copyright (C) 2012 Inverse inc.
|
||||
*
|
||||
* Author: Nicolas Höft
|
||||
* Inverse inc.
|
||||
*
|
||||
* This file is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* 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;
|
||||
}
|
63
SoObjects/SOGo/NSString+Crypto.h
Normal file
63
SoObjects/SOGo/NSString+Crypto.h
Normal file
|
@ -0,0 +1,63 @@
|
|||
/* NSString+Crypto.h - this file is part of SOGo
|
||||
*
|
||||
* Copyright (C) 2012 Nicolas Höft
|
||||
* Copyright (C) 2012 Inverse inc.
|
||||
*
|
||||
* Author: Nicolas Höft
|
||||
* Inverse inc.
|
||||
*
|
||||
* This file is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* 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 */
|
304
SoObjects/SOGo/NSString+Crypto.m
Normal file
304
SoObjects/SOGo/NSString+Crypto.m
Normal file
|
@ -0,0 +1,304 @@
|
|||
/* NSString+Crypto.m - this file is part of SOGo
|
||||
*
|
||||
* Copyright (C) 2012 Nicolas Höft
|
||||
* Copyright (C) 2012 Inverse inc.
|
||||
*
|
||||
* Author: Nicolas Höft
|
||||
* Inverse inc.
|
||||
*
|
||||
* This file is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* 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
|
|
@ -66,10 +66,6 @@
|
|||
|
||||
- (id) objectFromJSONString;
|
||||
|
||||
- (NSString *) asCryptStringUsingSalt: (NSString *) theSalt;
|
||||
- (NSString *) asMD5String;
|
||||
- (NSString *) asSHA1String;
|
||||
|
||||
- (NSString *) asSafeSQLString;
|
||||
|
||||
- (NSUInteger) countOccurrencesOfString: (NSString *) substring;
|
||||
|
|
|
@ -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;
|
||||
|
@ -535,48 +525,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: @"\\\\"]
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -45,6 +45,7 @@
|
|||
NSString *_imapHostField;
|
||||
NSString *_userPasswordAlgorithm;
|
||||
NSURL *_viewURL;
|
||||
BOOL _prependPasswordScheme;
|
||||
|
||||
/* resources handling */
|
||||
NSString *_kindField;
|
||||
|
|
|
@ -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;
|
||||
|
||||
pass = [plainPassword asCryptedPassUsingScheme: _userPasswordAlgorithm];
|
||||
|
||||
if (pass == nil)
|
||||
[self errorWithFormat: @"Unsupported user-password algorithm: %@", _userPasswordAlgorithm];
|
||||
|
||||
return plainPassword;
|
||||
if (_prependPasswordScheme)
|
||||
result = [NSString stringWithFormat: @"{%@}%@", _userPasswordAlgorithm, pass];
|
||||
else
|
||||
result = pass;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
//
|
||||
|
|
Loading…
Reference in a new issue