X-Git-Url: https://git.saurik.com/apple/security.git/blobdiff_plain/80e2389990082500d76eb566d4946be3e786c3ef..d8f41ccd20de16f8ebe2ccc84d47bf1cb2b26bbb:/Security/libsecurity_utilities/lib/cfutilities.h diff --git a/Security/libsecurity_utilities/lib/cfutilities.h b/Security/libsecurity_utilities/lib/cfutilities.h new file mode 100644 index 00000000..c69af028 --- /dev/null +++ b/Security/libsecurity_utilities/lib/cfutilities.h @@ -0,0 +1,620 @@ +/* + * Copyright (c) 2000-2004,2011-2014 Apple Inc. All Rights Reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + + +// +//CoreFoundation related utilities +// +#ifndef _H_CFUTILITIES +#define _H_CFUTILITIES + +#include +#include +#include +#include +#include +#undef check + + +namespace Security { + + +// +// Traits of popular CF types +// +template struct CFTraits { }; + +template <> struct CFTraits { + static bool check(CFTypeRef ref) { return true; } +}; + +#define __SEC_CFTYPE(name) \ + template <> struct CFTraits { \ + static CFTypeID cfid() { return name##GetTypeID(); } \ + static bool check(CFTypeRef ref) { return CFGetTypeID(ref) == cfid(); } \ + }; + +__SEC_CFTYPE(CFNull) +__SEC_CFTYPE(CFBoolean) +__SEC_CFTYPE(CFNumber) +__SEC_CFTYPE(CFString) +__SEC_CFTYPE(CFData) +__SEC_CFTYPE(CFDate) +__SEC_CFTYPE(CFURL) +__SEC_CFTYPE(CFBundle) +__SEC_CFTYPE(CFArray) +__SEC_CFTYPE(CFDictionary) +__SEC_CFTYPE(CFSet) + + +// +// Initialize-only self-releasing CF object handler (lightweight). +// +template class CFRef { +public: + CFRef() : mRef(NULL) { } + CFRef(CFType ref) : mRef(ref) { } + ~CFRef() { this->release(); } + CFRef(const CFRef &ref) : mRef(ref) {} + template CFRef(const CFRef<_T> &ref) : mRef(ref) {} + + CFRef(CFTypeRef ref, OSStatus err) + : mRef(CFType(ref)) + { + if (ref && !CFTraits::check(ref)) + MacOSError::throwMe(err); + } + + CFRef &take(CFType ref) + { this->release(); mRef = ref; return *this; } + + CFType yield() + { CFType r = mRef; mRef = NULL; return r; } + + CFRef &operator = (CFType ref) + { if (ref) CFRetain(ref); return take(ref); } + + CFRef &operator = (const CFRef &ref) + { if (ref) CFRetain(ref); return take(ref); } + + // take variant for when newly created CFType is returned + // via a ptr-to-CFType argument. + CFType *take() + { if (mRef) CFRelease(mRef); mRef = NULL; return &mRef; } + + operator CFType () const { return mRef; } + operator bool () const { return mRef != NULL; } + bool operator ! () const { return mRef == NULL; } + + CFType get() const { return mRef; } + + CFType &aref() + { take(NULL); return mRef; } + + CFType retain() const + { if (mRef) CFRetain(mRef); return mRef; } + + void release() const + { if (mRef) CFRelease(mRef); } + + template + bool is() const { return CFTraits::check(mRef); } + + template + static CFType check(OldType cf, OSStatus err) + { + if (cf && !CFTraits::check(cf)) + MacOSError::throwMe(err); + return CFType(cf); + } + + template + NewType as() const { return NewType(mRef); } + + template + NewType as(OSStatus err) const { return CFRef::check(mRef, err); } + +private: + CFType mRef; +}; + + +template class CFCopyRef : public CFRef { + typedef CFRef _Base; +public: + CFCopyRef() { } + CFCopyRef(CFType ref) : _Base(ref) { this->retain(); } + CFCopyRef(const CFCopyRef &ref) : _Base(ref) { this->retain(); } + template CFCopyRef(const CFRef<_T> &ref) : _Base(ref) { this->retain(); } + CFCopyRef(CFTypeRef ref, OSStatus err) : _Base(ref, err) { this->retain(); } + + CFCopyRef &take(CFType ref) + { _Base::take(ref); return *this; } + + CFCopyRef &operator = (CFType ref) + { if (ref) CFRetain(ref); return take(ref); } + + CFCopyRef &operator = (const CFCopyRef &ref) + { _Base::operator = (ref); return *this; } + + template CFCopyRef &operator = (const CFRef<_T> &ref) + { _Base::operator = (ref); return *this; } +}; + + +// +// A simple function that turns a non-array CFTypeRef into +// an array of one with that element. This will retain its argument +// (directly or indirectly). +// +inline CFArrayRef cfArrayize(CFTypeRef arrayOrItem) +{ + if (arrayOrItem == NULL) + return NULL; // NULL is NULL + else if (CFGetTypeID(arrayOrItem) == CFArrayGetTypeID()) { + CFRetain(arrayOrItem); + return CFArrayRef(arrayOrItem); // already an array + } else { + return CFArrayCreate(NULL, + (const void **)&arrayOrItem, 1, &kCFTypeArrayCallBacks); + } +} + + +// +// An empty CFArray. +// Since CFArrays are type-neutral, a single immutable empty array will +// serve for all uses. So keep it. +// +struct CFEmptyArray { + operator CFArrayRef () { return mArray; } + CFEmptyArray(); +private: + CFArrayRef mArray; +}; + +extern ModuleNexus cfEmptyArray; + + +// +// Translate CFStringRef or CFURLRef to (UTF8-encoded) C++ string. +// If release==true, a CFRelease will be performed on the CFWhatever argument +// whether the call succeeds or not(!). +// +string cfString(CFStringRef str); // extract UTF8 string +string cfString(CFURLRef url); // path of file: URL (only) +string cfString(CFBundleRef bundle); // path to bundle root + +string cfStringRelease(CFStringRef str CF_CONSUMED); // extract UTF8 string +string cfStringRelease(CFURLRef url CF_CONSUMED); // path of file: URL (only) +string cfStringRelease(CFBundleRef bundle CF_CONSUMED); // path to bundle root + + +string cfString(CFTypeRef anything, OSStatus err); // dynamic form; throws err on NULL + + +// +// Handle CFNumberRefs. +// This is nasty because CFNumber does not support unsigned types, and there's really no portably-safe +// way of working around this. So the handling of unsigned numbers is "almost correct." +// +template +struct CFNumberTraits; + +template <> struct CFNumberTraits { + static const CFNumberType cfnType = kCFNumberCharType; + typedef char ValueType; +}; +template <> struct CFNumberTraits { + static const CFNumberType cfnType = kCFNumberShortType; + typedef short ValueType; +}; +template <> struct CFNumberTraits { + static const CFNumberType cfnType = kCFNumberIntType; + typedef int ValueType; +}; +template <> struct CFNumberTraits { + static const CFNumberType cfnType = kCFNumberLongType; + typedef long ValueType; +}; +template <> struct CFNumberTraits { + static const CFNumberType cfnType = kCFNumberLongLongType; + typedef long long ValueType; +}; +template <> struct CFNumberTraits { + static const CFNumberType cfnType = kCFNumberFloatType; + typedef float ValueType; +}; +template <> struct CFNumberTraits { + static const CFNumberType cfnType = kCFNumberDoubleType; + typedef double ValueType; +}; + +template <> struct CFNumberTraits { + static const CFNumberType cfnType = kCFNumberIntType; + typedef int ValueType; +}; +template <> struct CFNumberTraits { + static const CFNumberType cfnType = kCFNumberIntType; + typedef int ValueType; +}; +template <> struct CFNumberTraits { + static const CFNumberType cfnType = kCFNumberLongLongType; + typedef long long ValueType; +}; +template <> struct CFNumberTraits { + static const CFNumberType cfnType = kCFNumberLongLongType; + typedef long long ValueType; +}; +template <> struct CFNumberTraits { + static const CFNumberType cfnType = kCFNumberLongLongType; + typedef long long ValueType; +}; + +template +Number cfNumber(CFNumberRef number) +{ + typename CFNumberTraits::ValueType value; + if (CFNumberGetValue(number, CFNumberTraits::cfnType, &value)) + return (Number)value; + else + CFError::throwMe(); +} + +template +Number cfNumber(CFNumberRef number, Number defaultValue) +{ + typename CFNumberTraits::ValueType value; + if (CFNumberGetValue(number, CFNumberTraits::cfnType, &value)) + return value; + else + return defaultValue; +} + +template +CFNumberRef makeCFNumber(Number value) +{ + typename CFNumberTraits::ValueType cfValue = value; + return CFNumberCreate(NULL, CFNumberTraits::cfnType, &cfValue); +} + +// legacy form +inline uint32_t cfNumber(CFNumberRef number) { return cfNumber(number); } + + +// +// Translate strings into CFStrings +// +inline CFStringRef makeCFString(const char *s, CFStringEncoding encoding = kCFStringEncodingUTF8) +{ + return s ? CFStringCreateWithCString(NULL, s, encoding) : NULL; +} + +inline CFStringRef makeCFString(const string &s, CFStringEncoding encoding = kCFStringEncodingUTF8) +{ + return CFStringCreateWithCString(NULL, s.c_str(), encoding); +} + +inline CFStringRef makeCFString(CFDataRef data, CFStringEncoding encoding = kCFStringEncodingUTF8) +{ + return CFStringCreateFromExternalRepresentation(NULL, data, encoding); +} + + +// +// Create CFURL objects from various sources +// +CFURLRef makeCFURL(const char *s, bool isDirectory = false, CFURLRef base = NULL); +CFURLRef makeCFURL(CFStringRef s, bool isDirectory = false, CFURLRef base = NULL); + +inline CFURLRef makeCFURL(const string &s, bool isDirectory = false, CFURLRef base = NULL) +{ + return makeCFURL(s.c_str(), isDirectory, base); +} + + +// +// Make temporary CF objects. +// +class CFTempString : public CFRef { +public: + template + CFTempString(Source s) : CFRef(makeCFString(s)) { } +}; + +class CFTempURL : public CFRef { +public: + template + CFTempURL(Source s, bool isDirectory = false, CFURLRef base = NULL) + : CFRef(makeCFURL(s, isDirectory, base)) { } +}; + + +// +// A temporary CFNumber +// +class CFTempNumber : public CFRef { +public: + template + CFTempNumber(Value value) : CFRef(makeCFNumber(value)) { } +}; + + +// +// A temporary CFData. +// +class CFTempData : public CFRef { +public: + CFTempData(const void *data, size_t length) + : CFRef(CFDataCreate(NULL, (const UInt8 *)data, length)) { } + + template + CFTempData(const Dataoid &dataoid) + : CFRef(CFDataCreate(NULL, (const UInt8 *)dataoid.data(), dataoid.length())) { } +}; + +class CFTempDataWrap : public CFRef { +public: + CFTempDataWrap(const void *data, size_t length) + : CFRef(CFDataCreateWithBytesNoCopy(NULL, (const UInt8 *)data, length, kCFAllocatorNull)) { } + + template + CFTempDataWrap(const Dataoid &dataoid) + : CFRef(CFDataCreateWithBytesNoCopy(NULL, (const UInt8 *)dataoid.data(), dataoid.length(), kCFAllocatorNull)) { } +}; + + +// +// Create CFData objects from various sources. +// +inline CFDataRef makeCFData(const void *data, size_t size) +{ + return CFDataCreate(NULL, (const UInt8 *)data, size); +} + +inline CFDataRef makeCFData(CFDictionaryRef dictionary) +{ + return CFPropertyListCreateXMLData(NULL, dictionary); +} + +template +inline CFDataRef makeCFData(const Data &source) +{ + return CFDataCreate(NULL, reinterpret_cast(source.data()), source.length()); +} + +inline CFDataRef makeCFDataMalloc(const void *data, size_t size) +{ + return CFDataCreateWithBytesNoCopy(NULL, (const UInt8 *)data, size, kCFAllocatorMalloc); +} + +template +inline CFDataRef makeCFDataMalloc(const Data &source) +{ + return CFDataCreateWithBytesNoCopy(NULL, (const UInt8 *)source.data(), source.length(), kCFAllocatorMalloc); +} + + +// +// Create a CFDataRef from malloc'ed data, exception-safely +// +class CFMallocData { +public: + CFMallocData(size_t size) + : mData(::malloc(size)), mSize(size) + { + if (!mData) + UnixError::throwMe(); + } + + ~CFMallocData() + { + if (mData) + ::free(mData); + } + + template + operator T * () + { return static_cast(mData); } + + operator CFDataRef (); + + void *data() { return mData; } + const void *data() const { return mData; } + size_t length() const { return mSize; } + +private: + void *mData; + size_t mSize; +}; + + +// +// Make CFDictionaries from stuff +// +CFDictionaryRef makeCFDictionary(unsigned count, ...); // key/value pairs +CFMutableDictionaryRef makeCFMutableDictionary(); // empty +CFMutableDictionaryRef makeCFMutableDictionary(unsigned count, ...); // (count) key/value pairs +CFMutableDictionaryRef makeCFMutableDictionary(CFDictionaryRef dict); // copy of dictionary + +CFDictionaryRef makeCFDictionaryFrom(CFDataRef data) CF_RETURNS_RETAINED;// interpret plist form +CFDictionaryRef makeCFDictionaryFrom(const void *data, size_t length) CF_RETURNS_RETAINED; // ditto + + +// +// Parsing out a CFDictionary without losing your lunch +// +class CFDictionary : public CFCopyRef { + typedef CFCopyRef _Base; +public: + CFDictionary(CFDictionaryRef ref, OSStatus error) : _Base(ref), mDefaultError(error) + { if (!ref) MacOSError::throwMe(error); } + CFDictionary(CFTypeRef ref, OSStatus error) : _Base(ref, error), mDefaultError(error) + { if (!ref) MacOSError::throwMe(error); } + CFDictionary(OSStatus error) : _Base(NULL), mDefaultError(error) { } + + using CFCopyRef::get; + + CFTypeRef get(CFStringRef key) { return CFDictionaryGetValue(*this, key); } + CFTypeRef get(const char *key) { return CFDictionaryGetValue(*this, CFTempString(key)); } + + template + CFType get(CFStringRef key, OSStatus err = errSecSuccess) const + { + CFTypeRef elem = CFDictionaryGetValue(*this, key); + return CFRef::check(elem, err ? err : mDefaultError); + } + + template + CFType get(const char *key, OSStatus err = errSecSuccess) const + { return get(CFTempString(key), err); } + + void apply(CFDictionaryApplierFunction func, void *context) + { return CFDictionaryApplyFunction(*this, func, context); } + +private: + template + struct Applier { + T *object; + void (T::*func)(CFTypeRef key, CFTypeRef value); + static void apply(CFTypeRef key, CFTypeRef value, void *context) + { Applier *me = (Applier *)context; return ((me->object)->*(me->func))(key, value); } + }; + + template + struct BlockApplier { + void (^action)(Key key, Value value); + static void apply(CFTypeRef key, CFTypeRef value, void* context) + { BlockApplier *me = (BlockApplier *)context; return me->action(Key(key), Value(value)); } + }; + +public: + template + void apply(T *object, void (T::*func)(CFTypeRef key, CFTypeRef value)) + { Applier app; app.object = object; app.func = func; return apply(app.apply, &app); } + + template + void apply(void (^action)(Key key, Value value)) + { BlockApplier app; app.action = action; return apply(app.apply, &app); } + +private: + OSStatus mDefaultError; +}; + + +// +// CFURLAccess wrappers for specific purposes +// +CFDataRef cfLoadFile(CFURLRef url); +CFDataRef cfLoadFile(int fd, size_t bytes); +inline CFDataRef cfLoadFile(CFStringRef path) { return cfLoadFile(CFTempURL(path)); } +inline CFDataRef cfLoadFile(const std::string &path) { return cfLoadFile(CFTempURL(path)); } +inline CFDataRef cfLoadFile(const char *path) { return cfLoadFile(CFTempURL(path)); } + + +// +// Internally used STL adapters. Should probably be in utilities.h. +// +template +Self projectPair(const Self &me) +{ return me; } + +template +Second projectPair(const pair &me) +{ return me.second; } + + +// +// A CFToVector turns a CFArrayRef of items into a flat +// C vector of some type, using a conversion function +// (from CFTypeRef) specified. As a special bonus, if +// you provide a CFTypeRef (other than CFArrayRef), it +// will be transparently handled as an array-of-one. +// The array will be automatically released on destruction +// of the CFToVector object. Any internal structure shared +// with the CFTypeRef inputs will be left alone. +// +template +class CFToVector { +public: + CFToVector(CFArrayRef arrayRef); + ~CFToVector() { delete[] mVector; } + operator UInt32 () const { return mCount; } + operator VectorBase *() const { return mVector; } + bool empty() const { return mCount == 0; } + + VectorBase *begin() const { return mVector; } + VectorBase *end() const { return mVector + mCount; } + + VectorBase &operator [] (UInt32 ix) const { assert(ix < mCount); return mVector[ix]; } + +private: + VectorBase *mVector; + UInt32 mCount; +}; + +template +CFToVector::CFToVector(CFArrayRef arrayRef) +{ + if (arrayRef == NULL) { + mCount = 0; + mVector = NULL; + } else { + mCount = (UInt32)CFArrayGetCount(arrayRef); + mVector = new VectorBase[mCount]; + for (UInt32 n = 0; n < mCount; n++) + mVector[n] = convert(CFRefType(CFArrayGetValueAtIndex(arrayRef, n))); + } +} + + +// +// Make CFArrays from stuff. +// +template +inline CFArrayRef makeCFArray(Generator &generate, Iterator first, Iterator last) +{ + // how many elements? + size_t size = distance(first, last); + + // do the CFArrayCreate tango + auto_array vec(size); + for (UInt32 n = 0; n < size; n++) + vec[n] = generate(projectPair(*first++)); + assert(first == last); + return CFArrayCreate(NULL, (const void **)vec.get(), size, &kCFTypeArrayCallBacks); +} + +template +inline CFArrayRef makeCFArray(Generator &generate, const Container &container) +{ + return makeCFArray(generate, container.begin(), container.end()); +} + +CFArrayRef makeCFArray(CFIndex count, ...) CF_RETURNS_RETAINED; +CFMutableArrayRef makeCFMutableArray(CFIndex count, ...) CF_RETURNS_RETAINED; + + +} // end namespace Security + +#endif //_H_CFUTILITIES