Index: configure.ac ================================================================== --- configure.ac +++ configure.ac @@ -784,10 +784,13 @@ ;; *) AC_CHECK_FUNCS(dladdr) ;; esac + +AC_CHECK_HEADERS(sys/mman.h) +AC_CHECK_FUNCS(mmap mlock) AC_ARG_ENABLE(threads, AS_HELP_STRING([--disable-threads], [disable thread support])) AS_IF([test x"$enable_threads" != x"no"], [ AC_MSG_CHECKING(for threads) Index: src/Makefile ================================================================== --- src/Makefile +++ src/Makefile @@ -60,10 +60,11 @@ OFPair.m \ ${OFPROCESS_M} \ OFRIPEMD160Hash.m \ OFRunLoop.m \ OFSandbox.m \ + OFSecureData.m \ OFSeekableStream.m \ OFSet.m \ OFSHA1Hash.m \ OFSHA224Hash.m \ OFSHA224Or256Hash.m \ Index: src/OFData.m ================================================================== --- src/OFData.m +++ src/OFData.m @@ -130,16 +130,16 @@ @try { if (itemSize == 0) @throw [OFInvalidArgumentException exception]; - _itemSize = itemSize; - _count = count; _items = [self allocMemoryWithSize: itemSize count: count]; + _itemSize = itemSize; + _count = count; - memcpy(_items, items, itemSize * count); + memcpy(_items, items, count * itemSize); } @catch (id e) { [self release]; @throw e; } Index: src/OFMutableData.h ================================================================== --- src/OFMutableData.h +++ src/OFMutableData.h @@ -187,28 +187,28 @@ * @brief All items of the OFMutableData as a C array. * * @warning The pointer is only valid until the OFMutableData is changed! * * Modifying the returned array directly is allowed and will change the contents - * of the data array. + * of the data. */ @property (readonly, nonatomic) void *items OF_RETURNS_INNER_POINTER; /*! * @brief The first item of the OFMutableData or `NULL`. * * Modifying the returned item directly is allowed and will change the contents - * of the data array. + * of the data. */ @property OF_NULLABLE_PROPERTY (readonly, nonatomic) void *firstItem OF_RETURNS_INNER_POINTER; /*! * @brief Last item of the OFMutableData or `NULL`. * * Modifying the returned item directly is allowed and will change the contents - * of the data array. + * of the data. */ @property OF_NULLABLE_PROPERTY (readonly, nonatomic) void *lastItem OF_RETURNS_INNER_POINTER; #else - (void *)items; @@ -218,14 +218,14 @@ /*! * @brief Returns a specific item of the OFMutableData. * * Modifying the returned item directly is allowed and will change the contents - * of the data array. + * of the data. * * @param index The number of the item to return * @return The specified item of the OFMutableData */ - (void *)itemAtIndex: (size_t)index OF_RETURNS_INNER_POINTER; @end OF_ASSUME_NONNULL_END Index: src/OFMutableData.m ================================================================== --- src/OFMutableData.m +++ src/OFMutableData.m @@ -113,23 +113,10 @@ itemSize: itemSize count: count]; _capacity = _count; - return self; -} - -- (instancetype)initWithItemsNoCopy: (void *)items - count: (size_t)count - freeWhenDone: (bool)freeWhenDone -{ - self = [self initWithItems: items - count: count]; - - if (freeWhenDone) - free(items); - return self; } - (instancetype)initWithItemsNoCopy: (void *)items itemSize: (size_t)itemSize ADDED src/OFSecureData.h Index: src/OFSecureData.h ================================================================== --- src/OFSecureData.h +++ src/OFSecureData.h @@ -0,0 +1,152 @@ +/* + * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, + * 2018 + * Jonathan Schleifer + * + * All rights reserved. + * + * This file is part of ObjFW. It may be distributed under the terms of the + * Q Public License 1.0, which can be found in the file LICENSE.QPL included in + * the packaging of this file. + * + * Alternatively, it may be distributed under the terms of the GNU General + * Public License, either version 2 or 3, which can be found in the file + * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this + * file. + */ + +#import "OFData.h" + +OF_ASSUME_NONNULL_BEGIN + +/*! + * @class OFSecureData OFSecureData.h ObjFW/OFSecureData.h + * + * @brief A class for storing arbitrary data in secure memory, securely wiping + * it when it gets deallocated. + * + * @note Secure memory might be unavailable on the platform, in which case this + * falls back to insecure (potentially swappable) memory. + */ +@interface OFSecureData: OFData +{ + size_t _mappingSize; +} + +/*! + * @brief Creates a new, autoreleased OFSecureData with count items of item + * size 1, all set to zero. + * + * @param count The number of zero items the OFSecureData should contain + * @return A new, autoreleased OFSecureData + */ ++ (instancetype)dataWithCount: (size_t)count; + +/*! + * @brief Creates a new, autoreleased OFSecureData with count items of the + * specified item size, all set to zero. + * + * @param itemSize The size of a single item in the OFSecureData in bytes + * @param count The number of zero items the OFSecureData should contain + * @return A new, autoreleased OFSecureData + */ ++ (instancetype)dataWithItemSize: (size_t)itemSize + count: (size_t)count; + +#ifdef OF_HAVE_FILES ++ (instancetype)dataWithContentsOfFile: (OFString *)path OF_UNAVAILABLE; +#endif ++ (instancetype)dataWithContentsOfURL: (OFURL *)URL OF_UNAVAILABLE; ++ (instancetype)dataWithStringRepresentation: (OFString *)string OF_UNAVAILABLE; ++ (instancetype)dataWithBase64EncodedString: (OFString *)string OF_UNAVAILABLE; ++ (instancetype)dataWithSerialization: (OFXMLElement *)element OF_UNAVAILABLE; + +/*! + * @brief Initializes an already allocated OFSecureData with count items of + * item size 1, all set to zero. + * + * @param count The number of zero items the OFSecureData should contain + * @return An initialized OFSecureData + */ +- (instancetype)initWithCount: (size_t)count; + +/*! + * @brief Initializes an already allocated OFSecureData with count items of the + * specified item size, all set to zero. + * + * @param itemSize The size of a single item in the OFSecureData in bytes + * @param count The number of zero items the OFSecureData should contain + * @return An initialized OFSecureData + */ +- (instancetype)initWithItemSize: (size_t)itemSize + count: (size_t)count; + +/*! + * @brief Zeroes the data. + */ +- (void)zero; + +#ifdef OF_HAVE_FILES +- (instancetype)initWithContentsOfFile: (OFString *)path OF_UNAVAILABLE; +#endif +- (instancetype)initWithContentsOfURL: (OFURL *)URL OF_UNAVAILABLE; +- (instancetype)initWithStringRepresentation: (OFString *)string OF_UNAVAILABLE; +- (instancetype)initWithBase64EncodedString: (OFString *)string OF_UNAVAILABLE; +- (instancetype)initWithSerialization: (OFXMLElement *)element OF_UNAVAILABLE; +- (OFString *)stringRepresentation OF_UNAVAILABLE; +- (OFString *)stringByBase64Encoding OF_UNAVAILABLE; +#ifdef OF_HAVE_FILES +- (void)writeToFile: (OFString *)path OF_UNAVAILABLE; +#endif +- (void)writeToURL: (OFURL *)URL OF_UNAVAILABLE; +- (OFXMLElement *)XMLElementBySerializing OF_UNAVAILABLE; +- (OFData *)messagePackRepresentation OF_UNAVAILABLE; +@end + +@interface OFSecureData (MutableRetrieving) +/* GCC does not like overriding properties with a different type. */ +#if defined(__clang__) || defined(DOXYGEN) +/*! + * @brief All items of the OFSecureData as a C array. + * + * Modifying the returned array directly is allowed and will change the contents + * of the data. + */ +@property (readonly, nonatomic) void *items OF_RETURNS_INNER_POINTER; + +/*! + * @brief The first item of the OFSecureData or `NULL`. + * + * Modifying the returned item directly is allowed and will change the contents + * of the data. + */ +@property OF_NULLABLE_PROPERTY (readonly, nonatomic) void *firstItem + OF_RETURNS_INNER_POINTER; + +/*! + * @brief Last item of the OFSecureData or `NULL`. + * + * Modifying the returned item directly is allowed and will change the contents + * of the data. + */ +@property OF_NULLABLE_PROPERTY (readonly, nonatomic) void *lastItem + OF_RETURNS_INNER_POINTER; +#else +- (void *)items; +- (nullable void *)firstItem; +- (nullable void *)lastItem; +#endif + +/*! + * @brief Returns a specific item of the OFSecureData. + * + * Modifying the returned item directly is allowed and will change the contents + * of the data array. + * + * @param index The number of the item to return + * @return The specified item of the OFSecureData + */ +- (void *)itemAtIndex: (size_t)index OF_RETURNS_INNER_POINTER; +@end + +OF_ASSUME_NONNULL_END ADDED src/OFSecureData.m Index: src/OFSecureData.m ================================================================== --- src/OFSecureData.m +++ src/OFSecureData.m @@ -0,0 +1,261 @@ +/* + * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, + * 2018 + * Jonathan Schleifer + * + * All rights reserved. + * + * This file is part of ObjFW. It may be distributed under the terms of the + * Q Public License 1.0, which can be found in the file LICENSE.QPL included in + * the packaging of this file. + * + * Alternatively, it may be distributed under the terms of the GNU General + * Public License, either version 2 or 3, which can be found in the file + * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this + * file. + */ + +#include "config.h" + +#include + +#ifdef HAVE_SYS_MMAN_H +# include +#endif + +#import "OFSecureData.h" +#import "OFString.h" +#import "OFSystemInfo.h" + +#import "OFInvalidArgumentException.h" +#import "OFOutOfMemoryException.h" +#import "OFOutOfRangeException.h" + +@implementation OFSecureData ++ (instancetype)dataWithCount: (size_t)count +{ + return [[[self alloc] initWithCount: count] autorelease]; +} + ++ (instancetype)dataWithItemSize: (size_t)itemSize + count: (size_t)count +{ + return [[[self alloc] initWithItemSize: itemSize + count: count] autorelease]; +} + +#ifdef OF_HAVE_FILES ++ (instancetype)dataWithContentsOfFile: (OFString *)path +{ + OF_UNRECOGNIZED_SELECTOR +} +#endif + ++ (instancetype)dataWithContentsOfURL: (OFURL *)URL +{ + OF_UNRECOGNIZED_SELECTOR +} + ++ (instancetype)dataWithStringRepresentation: (OFString *)string +{ + OF_UNRECOGNIZED_SELECTOR +} + ++ (instancetype)dataWithBase64EncodedString: (OFString *)string +{ + OF_UNRECOGNIZED_SELECTOR +} + ++ (instancetype)dataWithSerialization: (OFXMLElement *)element +{ + OF_UNRECOGNIZED_SELECTOR +} + +- (instancetype)initWithCount: (size_t)count +{ + return [self initWithItemSize: 1 + count: count]; +} + +- (instancetype)initWithItemSize: (size_t)itemSize + count: (size_t)count +{ + self = [super init]; + + @try { + size_t size, pageSize; + + if OF_UNLIKELY (itemSize == 0) + @throw [OFInvalidArgumentException exception]; + + if OF_UNLIKELY (count > SIZE_MAX / itemSize) + @throw [OFOutOfRangeException exception]; + + size = itemSize * count; + pageSize = [OFSystemInfo pageSize]; + +#if defined(HAVE_MMAP) && defined(HAVE_MLOCK) && defined(MAP_ANON) + _mappingSize = OF_ROUND_UP_POW2(pageSize, size); + + if OF_UNLIKELY (_mappingSize < size) + @throw [OFOutOfRangeException exception]; + + if OF_UNLIKELY ((_items = mmap(NULL, _mappingSize, + PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0)) == + MAP_FAILED) + @throw [OFOutOfMemoryException + exceptionWithRequestedSize: _mappingSize]; + + if OF_UNLIKELY (mlock(_items, _mappingSize) != 0) + @throw [OFOutOfMemoryException + exceptionWithRequestedSize: _mappingSize]; + + of_explicit_memset(_items, 0, _mappingSize); +#else + if OF_UNLIKELY ((_items = malloc(size)) == NULL) + @throw [OFOutOfMemoryException + exceptionWithRequestedSize: size]; + + of_explicit_memset(_items, 0, size); +#endif + + _itemSize = itemSize; + _count = count; + } @catch (id e) { + [self release]; + @throw e; + } + + return self; +} + +- (instancetype)initWithItems: (const void *)items + itemSize: (size_t)itemSize + count: (size_t)count +{ + self = [self initWithItemSize: itemSize + count: count]; + + memcpy(_items, items, count * itemSize); + + return self; +} + +- (instancetype)initWithItemsNoCopy: (void *)items + itemSize: (size_t)itemSize + count: (size_t)count + freeWhenDone: (bool)freeWhenDone +{ + self = [self initWithItems: items + itemSize: itemSize + count: count]; + + if (freeWhenDone) { + of_explicit_memset(items, 0, count * itemSize); + free(items); + } + + return self; +} + +#ifdef OF_HAVE_FILES +- (instancetype)initWithContentsOfFile: (OFString *)path +{ + OF_INVALID_INIT_METHOD +} +#endif + +- (instancetype)initWithContentsOfURL: (OFURL *)URL +{ + OF_INVALID_INIT_METHOD +} + +- (instancetype)initWithStringRepresentation: (OFString *)string +{ + OF_INVALID_INIT_METHOD +} + +- (instancetype)initWithBase64EncodedString: (OFString *)string +{ + OF_INVALID_INIT_METHOD +} + +- (instancetype)initWithSerialization: (OFXMLElement *)element +{ + OF_INVALID_INIT_METHOD +} + +- (void)dealloc +{ + [self zero]; + +#if defined(HAVE_MMAP) && defined(HAVE_MLOCK) && defined(MAP_ANON) + munlock(_items, _mappingSize); + munmap(_items, _mappingSize); +#else + free(_items); +#endif + + [super dealloc]; +} + +- (void)zero +{ +#if defined(HAVE_MMAP) && defined(HAVE_MLOCK) && defined(MAP_ANON) + of_explicit_memset(_items, 0, _mappingSize); +#else + of_explicit_memset(_items, 0, _count * _itemSize); +#endif +} + +- (id)copy +{ + return [[OFSecureData alloc] initWithItems: _items + itemSize: _itemSize + count: _count]; +} + +- (id)mutableCopy +{ + return [[OFSecureData alloc] initWithItems: _items + itemSize: _itemSize + count: _count]; +} + +- (OFString *)description +{ + return @""; +} + +- (OFString *)stringRepresentation +{ + OF_UNRECOGNIZED_SELECTOR +} + +- (OFString *)stringByBase64Encoding +{ + OF_UNRECOGNIZED_SELECTOR +} + +#ifdef OF_HAVE_FILES +- (void)writeToFile: (OFString *)path +{ + OF_UNRECOGNIZED_SELECTOR +} +#endif + +- (void)writeToURL: (OFURL *)URL +{ + OF_UNRECOGNIZED_SELECTOR +} + +- (OFXMLElement *)XMLElementBySerializing +{ + OF_UNRECOGNIZED_SELECTOR +} + +- (OFData *)messagePackRepresentation +{ + OF_UNRECOGNIZED_SELECTOR +} +@end Index: src/ObjFW.h ================================================================== --- src/ObjFW.h +++ src/ObjFW.h @@ -22,10 +22,11 @@ #import "OFString.h" #import "OFCharacterSet.h" #import "OFData.h" #import "OFArray.h" +#import "OFSecureData.h" #import "OFList.h" #import "OFSortedList.h" #import "OFDictionary.h"