X-Git-Url: https://git.saurik.com/apple/security.git/blobdiff_plain/5dd5f9ec28f304ca377c42fd7f711d6cf12b90e1..5c19dc3ae3bd8e40a9c028b0deddd50ff337692c:/OSX/libsecurity_mds/lib/MDSDictionary.cpp?ds=sidebyside diff --git a/OSX/libsecurity_mds/lib/MDSDictionary.cpp b/OSX/libsecurity_mds/lib/MDSDictionary.cpp new file mode 100644 index 00000000..604e0403 --- /dev/null +++ b/OSX/libsecurity_mds/lib/MDSDictionary.cpp @@ -0,0 +1,597 @@ +/* + * Copyright (c) 2000-2001,2011-2012,2014 Apple Inc. All Rights Reserved. + * + * The contents of this file constitute Original Code as defined in and are + * subject to the Apple Public Source License Version 1.2 (the 'License'). + * You may not use this file except in compliance with the License. Please obtain + * a copy of the License at http://www.apple.com/publicsource and read it before + * using this file. + * + * This 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. + */ + + +/* + File: MDSDictionary.cpp + + Contains: Internal representation of one MDS info file in the form of + a CFDictionary. + + Copyright (c) 2001,2011-2012,2014 Apple Inc. All Rights Reserved. +*/ + +#include "MDSDictionary.h" +#include "MDSAttrParser.h" +#include "MDSAttrUtils.h" +#include +#include + +namespace Security +{ + +/* heavyweight constructor from file */ +MDSDictionary::MDSDictionary( + CFURLRef fileUrl, + CFStringRef subdir, + const char *fullPath) // could get from fileUrl, but very messy! + : mDict(NULL), + mWeOwnDict(false), + mUrlPath(NULL), + mFileDesc(NULL), + mSubdir(subdir), + mDefaults(NULL) +{ + CFDataRef dictData = NULL; + CFStringRef cfErr = NULL; + + assert(fileUrl != NULL); + mUrlPath = MDSCopyCstring(fullPath); + MPDebug("Creating MDSDictionary from %s", mUrlPath); + + /* Load data from URL */ + SInt32 uerr; + Boolean brtn = CFURLCreateDataAndPropertiesFromResource( + NULL, + fileUrl, + &dictData, + NULL, // properties + NULL, // desiredProperties + &uerr); + if(!brtn) { + Syslog::alert("Error reading MDS file %s: %d", mUrlPath, (int)uerr); + CssmError::throwMe(CSSMERR_CSSM_MDS_ERROR); + } + + /* if it's not a dictionary, we don't want it */ + mDict = reinterpret_cast( + CFPropertyListCreateFromXMLData(NULL, + dictData, + kCFPropertyListImmutable, + &cfErr)); + CFRelease(dictData); + if(mDict == NULL) { + Syslog::alert("Malformed MDS file %s (1)", mUrlPath); + CssmError::throwMe(CSSMERR_CSSM_MDS_ERROR); + } + + /* henceforth we must release this dictionary */ + mWeOwnDict = true; + if(CFGetTypeID(mDict) != CFDictionaryGetTypeID()) { + Syslog::alert("Malformed MDS file %s (2)", mUrlPath); + CssmError::throwMe(CSSMERR_CSSM_MDS_ERROR); + } + CF_RELEASE(cfErr); + + /* get file description for error logging and debugging */ + CFStringRef cfStr = (CFStringRef)lookup(CFSTR(MDS_INFO_FILE_DESC), + true, CFStringGetTypeID()); + if(cfStr) { + CFIndex len = CFStringGetMaximumSizeForEncoding(CFStringGetLength(cfStr), kCFStringEncodingUTF8) + 1/*nul terminator*/; + mFileDesc = new char[len]; + if(mFileDesc) { + CFStringGetCString(cfStr, mFileDesc, len, + kCFStringEncodingUTF8); + } + } +} + +/* lightweight constructor from existing CFDictionary */ +MDSDictionary::MDSDictionary(CFDictionaryRef theDict) + : mDict(theDict), + mWeOwnDict(false), + mUrlPath(NULL), + mFileDesc(NULL), + mDefaults(NULL) +{ + /* note caller owns and releases the dictionary */ + if(mDict == NULL) { + MPDebug("Malformed MDS file (3)"); + CssmError::throwMe(CSSMERR_CSSM_MDS_ERROR); + } + if(CFGetTypeID(mDict) != CFDictionaryGetTypeID()) { + MPDebug("Malformed MDS file (4)"); + CssmError::throwMe(CSSMERR_CSSM_MDS_ERROR); + } +} + +MDSDictionary::~MDSDictionary() +{ + if(mWeOwnDict) { + CF_RELEASE(mDict); + } + mDict = NULL; + delete [] mUrlPath; + delete [] mFileDesc; +} + +/* lookup by either C string or CFStringRef - returns NULL on error */ +const void *MDSDictionary::lookup( + const char *key, + bool checkType, + CFTypeID type) +{ + CFStringRef cfKey = CFStringCreateWithCString(NULL, + key, + kCFStringEncodingUTF8); + if(cfKey == NULL) { + MPDebug("MDSDictionary::lookup: error creating CFString for key"); + return NULL; + } + const void *rtn = lookup(cfKey, checkType, type); + CFRelease(cfKey); + return rtn; + +} + +const void *MDSDictionary::lookup( + CFStringRef key, + bool checkType, + CFTypeID type) +{ + assert(mDict != NULL); + const void *rtn = CFDictionaryGetValue(mDict, key); + if(rtn && checkType) { + if(CFGetTypeID((CFTypeRef)rtn) != type) { + return NULL; + } + } + return rtn; +} + +/* + * Common means to perform a lookup in a dictionary given a C-string key and + * placing the value - if present - in a CSSM_DB_ATTRIBUTE_DATA. Any errors + * are only logged via MPDebug. Returns true if the value was found and + * successfully placed in supplied CSSM_DB_ATTRIBUTE_DATA. + * + * For now we assume that the key in the dictionary is the same as the key + * in the DB to which we're writing. + * + * We're also assuming that all DB keys are of format CSSM_DB_ATTRIBUTE_NAME_AS_STRING. + */ +bool MDSDictionary::lookupToDbAttr( + const char *key, + CSSM_DB_ATTRIBUTE_DATA &attr, + CSSM_DB_ATTRIBUTE_FORMAT attrFormat, + const MDSNameValuePair *nameValues) // optional for converting strings to numbers +{ + assert(mDict != NULL); + assert(&attr != NULL); + + CFTypeRef value; // polymorphic dictionary value + bool ourRtn = false; + const void *srcPtr = NULL; // polymorphic raw source bytes + size_t srcLen = 0; + uint32 ival = 0; + uint32 *ivalArray = NULL; + uint32 numValues = 1; // the default for MDSRawValueToDbAttr + string stringVal; + + value = (CFTypeRef)lookup(key); + if(value == NULL) { + return false; + } + CFTypeID valueType = CFGetTypeID(value); + + /* + * We have the value; could be any type. Handle it based on caller's + * CSSM_DB_ATTRIBUTE_FORMAT. + */ + switch(attrFormat) { + case CSSM_DB_ATTRIBUTE_FORMAT_STRING: + { + if(valueType != CFStringGetTypeID()) { + MPDebug("lookupToDbAttr: string format mismatch"); + break; + } + stringVal = cfString((CFStringRef)value, false); + srcPtr = stringVal.c_str(); + srcLen = stringVal.size(); + if(srcLen) { + ourRtn = true; + } + break; + } + case CSSM_DB_ATTRIBUTE_FORMAT_UINT32: + { + bool brtn = MDSCfTypeToUInt32(value, nameValues, key, ival, srcLen); + if(!brtn) { + MPDebug("MDS lookupToDbAttr: Bad number conversion"); + return false; + } + srcPtr = &ival; + ourRtn = true; + break; + } + case CSSM_DB_ATTRIBUTE_FORMAT_MULTI_UINT32: + { + /* + * This is expressed in the dictionary as an array of numbers. + * as in CSSM_DB_ATTRIBUTE_FORMAT_UINT32, each number can be + * expressed as either a string or a number. + */ + if(valueType != CFArrayGetTypeID()) { + /* + * Let's be extremely slick and allow one number here, either + * in string or number form.... + */ + bool brtn = MDSCfTypeToUInt32(value, nameValues, key, ival, srcLen); + if(!brtn) { + MPDebug("MDS lookupToDbAttr: Bad array element"); + return false; + } + srcPtr = &ival; + ourRtn = true; + break; + } + CFArrayRef cfArray = (CFArrayRef)value; + numValues = (uint32)CFArrayGetCount(cfArray); + if(numValues == 0) { + /* degenerate case, legal - right? Can AppleDatabase do this? */ + srcPtr = NULL; + srcLen = 0; + ourRtn = true; + break; + } + + /* + * malloc an array of uint32s + * convert each element in cfArray to a uint32 + * store as CSSM_DB_ATTRIBUTE_FORMAT_MULTI_UINT32 + * + * Note this does not have to be endian independent; the MDS DBs + * are not portable across machines let alone platforms. + */ + ivalArray = new uint32[numValues]; + unsigned dex; + bool brtn; + for(dex=0; dexAttributeInfo; + const MDSNameValuePair **nameValues = relInfo->nameValues; + + assert(relInfo != NULL); + numAttrs = 0; + for(dex=0; dexNumberOfAttributes; dex++) { + bool brtn; + const MDSNameValuePair *nvp; + + /* the array itself, or any element in it, can be NULL */ + if(nameValues != NULL) { + nvp = nameValues[dex]; + } + else { + nvp = NULL; + } + brtn = lookupToDbAttr(inAttr->Label.AttributeName, + *outAttrs, + inAttr->AttributeFormat, + nvp); + if(brtn) { + /* successfully added to dbAttrs */ + outAttrs++; + numAttrs++; + } + inAttr++; // regardless + } +} + +/* + * Lookup with file-based indirection. Allows multiple mdsinfo files to share commmon + * info from a separate plist file. + * + * Do a lookup for specified key. If not found, return NULL. If found: + * { + * if type of value matches desiredType { + * return the value; + * } + * else if type of value is string { + * if string starts with "file:" { + * attempt to read property list with that filename relative to + * specified bundle; + * if CFType of that propList matches desiredType { + * return newly read propList; + * } + * } + * } + * ...else return error; + */ +const CFPropertyListRef MDSDictionary::lookupWithIndirect( + const char *key, + CFBundleRef bundle, + CFTypeID desiredType, + bool &fetchedFromDisk) // true --> caller must CFRelease the returned + // value + // false -> it's part of this dictionary +{ + CFPropertyListRef ourRtn = NULL; + CFDataRef dictData = NULL; + CFStringRef cfErr = NULL; + SInt32 uerr; + Boolean brtn; + + + assert(key != NULL); + assert(bundle != NULL); + + fetchedFromDisk = false; + + /* basic local lookup */ + CFStringRef cfKey = CFStringCreateWithCString(NULL, + key, + kCFStringEncodingUTF8); + if(cfKey == NULL) { + MPDebug("CFStringCreateWithCString error"); + return NULL; + } + const void *rtn = CFDictionaryGetValue(mDict, cfKey); + CFRelease(cfKey); + if(rtn == NULL) { + return NULL; + } + CFTypeID foundType = CFGetTypeID((CFTypeRef)rtn); + if(foundType == desiredType) { + /* found what we're looking for; done */ + return (CFPropertyListRef)rtn; + } + + /* is it a string which starts with "file:"? */ + if(foundType != CFStringGetTypeID()) { + return NULL; + } + const char *cVal = MDSCFStringToCString((CFStringRef)rtn); + if(cVal == NULL) { + MPDebug("MDSCFStringToCString error in lookupWithIndirect"); + return NULL; + } + if(strstr(cVal, "file:") != cVal) { + delete [] cVal; + return NULL; + } + /* delete [] cval on return */ + + /* OK, this specifies a resource file in the bundle. Fetch it. */ + CFURLRef fileUrl = NULL; + CFStringRef cfFileName = CFStringCreateWithCString(NULL, + cVal + 5, + kCFStringEncodingUTF8); + if(cfFileName == NULL) { + MPDebug("lookupWithIndirect: bad file name spec"); + goto abort; + } + fileUrl = CFBundleCopyResourceURL(bundle, + cfFileName, + NULL, + mSubdir); + if(fileUrl == NULL) { + MPDebug("lookupWithIndirect: file %s not found", cVal); + goto abort; + } + + MPDebug("Fetching indirect resource %s", cVal); + + /* Load data from URL */ + brtn = CFURLCreateDataAndPropertiesFromResource( + NULL, + fileUrl, + &dictData, + NULL, // properties + NULL, // desiredProperties + &uerr); + if(!brtn) { + MPDebug("lookupWithIndirect: error %d reading %s", (int)uerr, cVal); + goto abort; + } + + /* if it's not a property list, we don't want it */ + ourRtn = CFPropertyListCreateFromXMLData(NULL, + dictData, + kCFPropertyListImmutable, + &cfErr); + if(ourRtn == NULL) { + MPDebug("lookupWithIndirect: %s malformed (not a prop list)", cVal); + goto abort; + } + + /* if it doesn't match the caller's spec, we don't want it */ + if(CFGetTypeID(ourRtn) != desiredType) { + MPDebug("lookupWithIndirect: %s malformed (mismatch)", cVal); + CF_RELEASE(ourRtn); + ourRtn = NULL; + goto abort; + } + + MPDebug("lookupWithIndirect: resource %s FOUND", cVal); + fetchedFromDisk = true; + +abort: + delete [] cVal; + CF_RELEASE(cfFileName); + CF_RELEASE(fileUrl); + CF_RELEASE(dictData); + CF_RELEASE(cfErr); + return ourRtn; +} + +void MDSDictionary::setDefaults(const MDS_InstallDefaults *defaults) +{ + mDefaults = defaults; + + /* + * Save the values into (a new) mDict. + */ + assert(mDict != NULL); + CFMutableDictionaryRef tmpDict = CFDictionaryCreateMutableCopy(NULL, 0, mDict); + if(tmpDict == NULL) { + MPDebug("setDefaults: error copying old dictionary"); + CssmError::throwMe(CSSMERR_CSSM_MDS_ERROR); + } + + CFStringRef tmpStr = NULL; + + /* + * CFDictionaryAddValue() does nothing if the requested key is already + * present. If you need to call setDefaults() more than once, you'll + * have to add the code to remove the old key/value pairs first. + */ + if(defaults) { + if(defaults->guid) { + tmpStr = CFStringCreateWithCString(NULL, defaults->guid, kCFStringEncodingUTF8); + if(tmpStr) { + CFDictionaryAddValue(tmpDict, CFSTR("ModuleID"), tmpStr); + CFRelease(tmpStr); + } + else { + MPDebug("setDefaults: error creating CFString for GUID"); + CssmError::throwMe(CSSMERR_CSSM_MDS_ERROR); + } + } + + CFNumberRef tmpNum = CFNumberCreate(NULL, kCFNumberIntType, &defaults->ssid); + if(tmpNum) { + CFDictionaryAddValue(tmpDict, CFSTR("SSID"), tmpNum); + CFRelease(tmpNum); + } + else { + MPDebug("setDefaults: error creating CFString for SSID"); + CssmError::throwMe(CSSMERR_CSSM_MDS_ERROR); + } + + if(defaults->serial) { + tmpStr = CFStringCreateWithCString(NULL, defaults->serial, kCFStringEncodingUTF8); + if(tmpStr) { + CFDictionaryAddValue(tmpDict, CFSTR("ScSerialNumber"), tmpStr); + CFRelease(tmpStr); + } + else { + MPDebug("setDefaults: error creating CFString for serial number"); + CssmError::throwMe(CSSMERR_CSSM_MDS_ERROR); + } + } + + if(defaults->printName) { + tmpStr = CFStringCreateWithCString(NULL, defaults->printName, kCFStringEncodingUTF8); + if(tmpStr) { + CFDictionaryAddValue(tmpDict, CFSTR("ScDesc"), tmpStr); + CFRelease(tmpStr); + } + else { + MPDebug("setDefaults: error creating CFString for description"); + CssmError::throwMe(CSSMERR_CSSM_MDS_ERROR); + } + } + } + + if(mUrlPath) { + tmpStr = CFStringCreateWithCString(NULL, mUrlPath, kCFStringEncodingUTF8); + if(tmpStr) { + CFDictionaryAddValue(tmpDict, CFSTR("Path"), tmpStr); + CFRelease(tmpStr); + } + else { + MPDebug("setDefaults: error creating CFString for path"); + CssmError::throwMe(CSSMERR_CSSM_MDS_ERROR); + } + } + CFDictionaryRef oldDict = mDict; + mDict = CFDictionaryCreateCopy(NULL, tmpDict); + if(mDict == NULL) { + mDict = oldDict; // first do no harm + CFRelease(tmpDict); + MPDebug("setDefaults: error creating new dictionary"); + CssmError::throwMe(CSSMERR_CSSM_MDS_ERROR); + } + CFRelease(oldDict); + CFRelease(tmpDict); +} + + +} // end namespace Security