X-Git-Url: https://git.saurik.com/apple/security.git/blobdiff_plain/72a12576750f52947eb043106ba5c12c0d07decf..b1ab9ed8d0e0f1c3b66d7daa8fd5564444c56195:/libsecurity_transform/lib/CoreFoundationBasics.cpp diff --git a/libsecurity_transform/lib/CoreFoundationBasics.cpp b/libsecurity_transform/lib/CoreFoundationBasics.cpp new file mode 100644 index 00000000..cb1bb51f --- /dev/null +++ b/libsecurity_transform/lib/CoreFoundationBasics.cpp @@ -0,0 +1,283 @@ +#include +#include + +#include "CoreFoundationBasics.h" +#include "Block.h" +#include "TransformFactory.h" +#include "Utilities.h" +#include "syslog.h" +#include "misc.h" + +using namespace std; + + +const CFStringRef gInternalCFObjectName = CFSTR("SecTransform Internal Object"); +const CFStringRef gInternalProtectedCFObjectName = CFSTR("SecTransform Internal Object (protected)"); + +struct RegisteredClassInfo +{ + CFRuntimeClass mClass; + CFTypeID mClassID; + RegisteredClassInfo(); + void Register(CFStringRef name); + + static const RegisteredClassInfo *Find(CFStringRef name); + static dispatch_queue_t readWriteLock; + static dispatch_once_t initializationGuard; + static CFMutableDictionaryRef registeredInfos; +}; + +dispatch_queue_t RegisteredClassInfo::readWriteLock; +dispatch_once_t RegisteredClassInfo::initializationGuard; +CFMutableDictionaryRef RegisteredClassInfo::registeredInfos; + +/* + THE FOLLOWING FUNCTION REGISTERS YOUR CLASS. YOU MUST CALL YOUR CLASS INITIALIZER HERE! +*/ + +static CFErrorRef gNoMemory; + +CFErrorRef GetNoMemoryError() +{ + return gNoMemory; +} + + + +CFErrorRef GetNoMemoryErrorAndRetain() +{ + return (CFErrorRef) CFRetain(gNoMemory); +} + + + +static void CoreFoundationObjectRegister() +{ + static dispatch_once_t gate = 0; + + dispatch_once(&gate, + ^{ + // Init NoMemory here, so other places we can safely return it (otherwise we might + // not have enough memory to allocate the CFError) + gNoMemory = CreateGenericErrorRef(kCFErrorDomainPOSIX, ENOMEM, "Out of memory."); + + // only one registration for internal objects, cuts down on how many objects + // we register in the CF type table + CoreFoundationObject::RegisterObject(gInternalCFObjectName, false); + CoreFoundationObject::RegisterObject(gInternalProtectedCFObjectName, true); + + // register any objects which may be exposed as API here + + // call for externalizable transforms. + TransformFactory::Setup(); + }); +} + + + +RegisteredClassInfo::RegisteredClassInfo() +{ + memset(&mClass, 0, sizeof(mClass)); + dispatch_once(&RegisteredClassInfo::initializationGuard, ^(void) { + RegisteredClassInfo::readWriteLock = dispatch_queue_create("com.apple.security.transform.cfobject.RegisteredClassInfo", DISPATCH_QUEUE_CONCURRENT); + RegisteredClassInfo::registeredInfos = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, NULL); + }); +} + + + +CoreFoundationObject::CoreFoundationObject(CFStringRef name) : mHolder(NULL), mObjectType(name) +{ +} + + + +CoreFoundationObject::~CoreFoundationObject() +{ +} + + + +CFHashCode CoreFoundationObject::Hash() +{ + // Valid for address equality only, overload for something else. + return (CFHashCode)mHolder; +} + +void CoreFoundationObject::Finalize() +{ + delete this; +} + + +std::string CoreFoundationObject::FormattingDescription(CFDictionaryRef options) +{ + return "CoreFoundationObject"; +} + + + +std::string CoreFoundationObject::DebugDescription() +{ + return "CoreFoundationObject"; +} + + + +Boolean CoreFoundationObject::Equal(const CoreFoundationObject* obj) +{ + return mHolder == obj->mHolder; +} + + + +static void FinalizeStub(CFTypeRef typeRef) +{ + CoreFoundationHolder::ObjectFromCFType(typeRef)->Finalize(); +} + + + +static Boolean IsEqualTo(CFTypeRef ref1, CFTypeRef ref2) +{ + if (CFGetTypeID(ref1) != CFGetTypeID(ref2)) { + // If ref2 isn't a CoreFoundatonHolder treating + // it like one is likely to crash. (One could + // argue that we should check to see if ref2 + // is *any* CoreFoundationHolder registered + // type) + return false; + } + + CoreFoundationHolder* tr1 = (CoreFoundationHolder*) ref1; + CoreFoundationHolder* tr2 = (CoreFoundationHolder*) ref2; + return tr1->mObject->Equal(tr2->mObject); +} + + + +static CFHashCode MakeHash(CFTypeRef typeRef) +{ + CoreFoundationHolder* tr = (CoreFoundationHolder*) typeRef; + return tr->mObject->Hash(); +} + + + +static CFStringRef MakeFormattingDescription(CFTypeRef typeRef, CFDictionaryRef formatOptions) +{ + // get the string + CoreFoundationHolder* tr = (CoreFoundationHolder*) typeRef; + string desc = tr->mObject->FormattingDescription(formatOptions); + + if (desc.length() == 0) + { + return NULL; + } + + // convert it to a CFString + return CFStringCreateWithCString(NULL, desc.c_str(), kCFStringEncodingMacRoman); +} + + +static CFStringRef MakeDebugDescription(CFTypeRef typeRef) +{ + // get the string + CoreFoundationHolder* tr = (CoreFoundationHolder*) typeRef; + string desc = tr->mObject->DebugDescription(); + + if (desc.length() == 0) + { + return NULL; + } + + // convert it to a CFString + return CFStringCreateWithCString(NULL, desc.c_str(), kCFStringEncodingMacRoman); +} + + + + +void CoreFoundationObject::RegisterObject(CFStringRef name, bool protectDelete) +{ + RegisteredClassInfo *classRecord = new RegisteredClassInfo; + + classRecord->mClass.version = 0; + // XXX: this is kind of lame, "name" is almost always a CFSTR, so it would + // be best to use the CFStringGetPtr result AND hold a reference to the + // name string, but fall back to utf8... (note there is no unRegisterObject) + char *utf8_name = utf8(name); + classRecord->mClass.className = strdup(utf8_name); + free(utf8_name); + classRecord->mClass.init = NULL; + classRecord->mClass.copy = NULL; + classRecord->mClass.finalize = protectDelete ? NULL : FinalizeStub; + classRecord->mClass.equal = IsEqualTo; + classRecord->mClass.hash = MakeHash; + classRecord->mClass.copyFormattingDesc = MakeFormattingDescription; + classRecord->mClass.copyDebugDesc = MakeDebugDescription; + classRecord->mClassID = _CFRuntimeRegisterClass((const CFRuntimeClass * const)&classRecord->mClass); + classRecord->Register(name); +} + + + +CFTypeID CoreFoundationObject::FindObjectType(CFStringRef name) +{ + const RegisteredClassInfo *classInfo = RegisteredClassInfo::Find(name); + if (classInfo) { + return classInfo->mClassID; + } else { + return _kCFRuntimeNotATypeID; + } +} + +const RegisteredClassInfo *RegisteredClassInfo::Find(CFStringRef name) +{ + __block const RegisteredClassInfo *ret = NULL; + dispatch_sync(RegisteredClassInfo::readWriteLock, ^(void) { + ret = (const RegisteredClassInfo *)CFDictionaryGetValue(RegisteredClassInfo::registeredInfos, name); + }); + + return ret; +} + +void RegisteredClassInfo::Register(CFStringRef name) +{ + dispatch_barrier_sync(RegisteredClassInfo::readWriteLock, ^(void) { + CFDictionarySetValue(RegisteredClassInfo::registeredInfos, name, this); + }); +} + +CFStringRef CoreFoundationObject::GetTypeAsCFString() +{ + return mObjectType; +} + + + +CoreFoundationHolder* CoreFoundationHolder::MakeHolder(CFStringRef name, CoreFoundationObject* object) +{ + // setup the CoreFoundation registry, just in case we need it. + + CoreFoundationObjectRegister(); + + CoreFoundationHolder* data = (CoreFoundationHolder*) _CFRuntimeCreateInstance(kCFAllocatorDefault, + CoreFoundationObject::FindObjectType(name), + sizeof(CoreFoundationHolder) - sizeof(CFRuntimeBase), + NULL); + data->mObject = object; + object->SetHolder(data); + + return data; +} + + + +CoreFoundationObject* CoreFoundationHolder::ObjectFromCFType(CFTypeRef type) +{ + return ((CoreFoundationHolder*) type)->mObject; +} + + +