#include "MDSDictionary.h"
#include "MDSAttrParser.h"
#include "MDSAttrUtils.h"
#include <security_utilities/logging.h>
#include <security_utilities/cfutilities.h>
namespace Security
{
MDSDictionary::MDSDictionary(
CFURLRef fileUrl,
CFStringRef subdir,
const char *fullPath) : 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);
SInt32 uerr;
Boolean brtn = CFURLCreateDataAndPropertiesFromResource(
NULL,
fileUrl,
&dictData,
NULL, NULL, &uerr);
if(!brtn) {
Syslog::alert("Error reading MDS file %s: %d", mUrlPath, uerr);
CssmError::throwMe(CSSMERR_CSSM_MDS_ERROR);
}
mDict = reinterpret_cast<CFDictionaryRef>(
CFPropertyListCreateFromXMLData(NULL,
dictData,
kCFPropertyListImmutable,
&cfErr));
CFRelease(dictData);
if(mDict == NULL) {
Syslog::alert("Malformed MDS file %s (1)", mUrlPath);
CssmError::throwMe(CSSMERR_CSSM_MDS_ERROR);
}
mWeOwnDict = true;
if(CFGetTypeID(mDict) != CFDictionaryGetTypeID()) {
Syslog::alert("Malformed MDS file %s (2)", mUrlPath);
CssmError::throwMe(CSSMERR_CSSM_MDS_ERROR);
}
CF_RELEASE(cfErr);
CFStringRef cfStr = (CFStringRef)lookup(CFSTR(MDS_INFO_FILE_DESC),
true, CFStringGetTypeID());
if(cfStr) {
CFIndex len = CFStringGetMaximumSizeForEncoding(CFStringGetLength(cfStr), kCFStringEncodingUTF8) + 1;
mFileDesc = new char[len];
if(mFileDesc) {
CFStringGetCString(cfStr, mFileDesc, len,
kCFStringEncodingUTF8);
}
}
}
MDSDictionary::MDSDictionary(CFDictionaryRef theDict)
: mDict(theDict),
mWeOwnDict(false),
mUrlPath(NULL),
mFileDesc(NULL),
mDefaults(NULL)
{
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;
}
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;
}
bool MDSDictionary::lookupToDbAttr(
const char *key,
CSSM_DB_ATTRIBUTE_DATA &attr,
CSSM_DB_ATTRIBUTE_FORMAT attrFormat,
const MDSNameValuePair *nameValues) {
assert(mDict != NULL);
assert(&attr != NULL);
CFTypeRef value; bool ourRtn = false;
const void *srcPtr = NULL; size_t srcLen = 0;
uint32 ival = 0;
uint32 *ivalArray = NULL;
uint32 numValues = 1; string stringVal;
value = (CFTypeRef)lookup(key);
if(value == NULL) {
return false;
}
CFTypeID valueType = CFGetTypeID(value);
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:
{
if(valueType != CFArrayGetTypeID()) {
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 = CFArrayGetCount(cfArray);
if(numValues == 0) {
srcPtr = NULL;
srcLen = 0;
ourRtn = true;
break;
}
ivalArray = new uint32[numValues];
unsigned dex;
bool brtn;
for(dex=0; dex<numValues; dex++) {
CFTypeRef elmt = (CFTypeRef)CFArrayGetValueAtIndex(cfArray, dex);
if(elmt == NULL) {
MPDebug("MDS lookupToDbAttr: key %s: Bad array element (1)", key);
delete [] ivalArray;
return false;
}
size_t itemLen = 0;
brtn = MDSCfTypeToUInt32(elmt, nameValues, key, ivalArray[dex], itemLen);
if(!brtn) {
MPDebug("MDS lookupToDbAttr: key %s Bad element at index %d",
key, dex);
delete [] ivalArray;
return false;
}
srcLen += itemLen;
}
srcPtr = ivalArray;
ourRtn = true;
numValues = 1;
break;
}
case CSSM_DB_ATTRIBUTE_FORMAT_BLOB: {
if(valueType != CFDataGetTypeID()) {
MPDebug("lookupToDbAttr: blob/CFData format mismatch");
break;
}
CFDataRef cfData = (CFDataRef)value;
srcLen = CFDataGetLength(cfData);
srcPtr = CFDataGetBytePtr(cfData);
ourRtn = true;
break;
}
case CSSM_DB_ATTRIBUTE_FORMAT_SINT32: default:
MPDebug("lookupToDbAttr: bad attrForm(%d)", (int)attrFormat);
return false;
}
if(ourRtn) {
MDSRawValueToDbAttr(srcPtr, srcLen, attrFormat, key, attr, numValues);
}
if(ivalArray) {
delete [] ivalArray;
}
return ourRtn;
}
void MDSDictionary::lookupAttributes(
const RelationInfo *relInfo,
CSSM_DB_ATTRIBUTE_DATA_PTR outAttrs, uint32 &numAttrs) {
unsigned dex;
const CSSM_DB_ATTRIBUTE_INFO *inAttr = relInfo->AttributeInfo;
const MDSNameValuePair **nameValues = relInfo->nameValues;
assert(relInfo != NULL);
numAttrs = 0;
for(dex=0; dex<relInfo->NumberOfAttributes; dex++) {
bool brtn;
const MDSNameValuePair *nvp;
if(nameValues != NULL) {
nvp = nameValues[dex];
}
else {
nvp = NULL;
}
brtn = lookupToDbAttr(inAttr->Label.AttributeName,
*outAttrs,
inAttr->AttributeFormat,
nvp);
if(brtn) {
outAttrs++;
numAttrs++;
}
inAttr++; }
}
const CFPropertyListRef MDSDictionary::lookupWithIndirect(
const char *key,
CFBundleRef bundle,
CFTypeID desiredType,
bool &fetchedFromDisk) {
CFPropertyListRef ourRtn = NULL;
CFDataRef dictData = NULL;
CFStringRef cfErr = NULL;
SInt32 uerr;
Boolean brtn;
assert(key != NULL);
assert(bundle != NULL);
fetchedFromDisk = false;
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) {
return (CFPropertyListRef)rtn;
}
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;
}
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);
brtn = CFURLCreateDataAndPropertiesFromResource(
NULL,
fileUrl,
&dictData,
NULL, NULL, &uerr);
if(!brtn) {
MPDebug("lookupWithIndirect: error %d reading %s", (int)uerr, cVal);
goto abort;
}
ourRtn = CFPropertyListCreateFromXMLData(NULL,
dictData,
kCFPropertyListImmutable,
&cfErr);
if(ourRtn == NULL) {
MPDebug("lookupWithIndirect: %s malformed (not a prop list)", cVal);
goto abort;
}
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;
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;
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; CFRelease(tmpDict);
MPDebug("setDefaults: error creating new dictionary");
CssmError::throwMe(CSSMERR_CSSM_MDS_ERROR);
}
CFRelease(oldDict);
CFRelease(tmpDict);
}
}