2 * Copyright (c) 2000-2001,2011-2012,2014 Apple Inc. All Rights Reserved.
4 * The contents of this file constitute Original Code as defined in and are
5 * subject to the Apple Public Source License Version 1.2 (the 'License').
6 * You may not use this file except in compliance with the License. Please obtain
7 * a copy of the License at http://www.apple.com/publicsource and read it before
10 * This Original Code and all software distributed under the License are
11 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS
12 * OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, INCLUDING WITHOUT
13 * LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
14 * PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. Please see the License for the
15 * specific language governing rights and limitations under the License.
20 File: MDSDictionary.cpp
22 Contains: Internal representation of one MDS info file in the form of
25 Copyright (c) 2001,2011-2012,2014 Apple Inc. All Rights Reserved.
28 #include "MDSDictionary.h"
29 #include "MDSAttrParser.h"
30 #include "MDSAttrUtils.h"
31 #include <security_utilities/logging.h>
32 #include <security_utilities/cfutilities.h>
37 /* heavyweight constructor from file */
38 MDSDictionary::MDSDictionary(
41 const char *fullPath
) // could get from fileUrl, but very messy!
49 CFDataRef dictData
= NULL
;
50 CFStringRef cfErr
= NULL
;
52 assert(fileUrl
!= NULL
);
53 mUrlPath
= MDSCopyCstring(fullPath
);
54 MPDebug("Creating MDSDictionary from %s", mUrlPath
);
56 /* Load data from URL */
58 Boolean brtn
= CFURLCreateDataAndPropertiesFromResource(
63 NULL
, // desiredProperties
66 Syslog::alert("Error reading MDS file %s: %d", mUrlPath
, (int)uerr
);
67 CssmError::throwMe(CSSMERR_CSSM_MDS_ERROR
);
70 /* if it's not a dictionary, we don't want it */
71 mDict
= reinterpret_cast<CFDictionaryRef
>(
72 CFPropertyListCreateFromXMLData(NULL
,
74 kCFPropertyListImmutable
,
78 Syslog::alert("Malformed MDS file %s (1)", mUrlPath
);
79 CssmError::throwMe(CSSMERR_CSSM_MDS_ERROR
);
82 /* henceforth we must release this dictionary */
84 if(CFGetTypeID(mDict
) != CFDictionaryGetTypeID()) {
85 Syslog::alert("Malformed MDS file %s (2)", mUrlPath
);
86 CssmError::throwMe(CSSMERR_CSSM_MDS_ERROR
);
90 /* get file description for error logging and debugging */
91 CFStringRef cfStr
= (CFStringRef
)lookup(CFSTR(MDS_INFO_FILE_DESC
),
92 true, CFStringGetTypeID());
94 CFIndex len
= CFStringGetMaximumSizeForEncoding(CFStringGetLength(cfStr
), kCFStringEncodingUTF8
) + 1/*nul terminator*/;
95 mFileDesc
= new char[len
];
97 CFStringGetCString(cfStr
, mFileDesc
, len
,
98 kCFStringEncodingUTF8
);
103 /* lightweight constructor from existing CFDictionary */
104 MDSDictionary::MDSDictionary(CFDictionaryRef theDict
)
111 /* note caller owns and releases the dictionary */
113 MPDebug("Malformed MDS file (3)");
114 CssmError::throwMe(CSSMERR_CSSM_MDS_ERROR
);
116 if(CFGetTypeID(mDict
) != CFDictionaryGetTypeID()) {
117 MPDebug("Malformed MDS file (4)");
118 CssmError::throwMe(CSSMERR_CSSM_MDS_ERROR
);
122 MDSDictionary::~MDSDictionary()
132 /* lookup by either C string or CFStringRef - returns NULL on error */
133 const void *MDSDictionary::lookup(
138 CFStringRef cfKey
= CFStringCreateWithCString(NULL
,
140 kCFStringEncodingUTF8
);
142 MPDebug("MDSDictionary::lookup: error creating CFString for key");
145 const void *rtn
= lookup(cfKey
, checkType
, type
);
151 const void *MDSDictionary::lookup(
156 assert(mDict
!= NULL
);
157 const void *rtn
= CFDictionaryGetValue(mDict
, key
);
158 if(rtn
&& checkType
) {
159 if(CFGetTypeID((CFTypeRef
)rtn
) != type
) {
167 * Common means to perform a lookup in a dictionary given a C-string key and
168 * placing the value - if present - in a CSSM_DB_ATTRIBUTE_DATA. Any errors
169 * are only logged via MPDebug. Returns true if the value was found and
170 * successfully placed in supplied CSSM_DB_ATTRIBUTE_DATA.
172 * For now we assume that the key in the dictionary is the same as the key
173 * in the DB to which we're writing.
175 * We're also assuming that all DB keys are of format CSSM_DB_ATTRIBUTE_NAME_AS_STRING.
177 bool MDSDictionary::lookupToDbAttr(
179 CSSM_DB_ATTRIBUTE_DATA
&attr
,
180 CSSM_DB_ATTRIBUTE_FORMAT attrFormat
,
181 const MDSNameValuePair
*nameValues
) // optional for converting strings to numbers
183 assert(mDict
!= NULL
);
184 assert(&attr
!= NULL
);
186 CFTypeRef value
; // polymorphic dictionary value
188 const void *srcPtr
= NULL
; // polymorphic raw source bytes
191 uint32
*ivalArray
= NULL
;
192 uint32 numValues
= 1; // the default for MDSRawValueToDbAttr
195 value
= (CFTypeRef
)lookup(key
);
199 CFTypeID valueType
= CFGetTypeID(value
);
202 * We have the value; could be any type. Handle it based on caller's
203 * CSSM_DB_ATTRIBUTE_FORMAT.
206 case CSSM_DB_ATTRIBUTE_FORMAT_STRING
:
208 if(valueType
!= CFStringGetTypeID()) {
209 MPDebug("lookupToDbAttr: string format mismatch");
212 stringVal
= cfString((CFStringRef
)value
, false);
213 srcPtr
= stringVal
.c_str();
214 srcLen
= stringVal
.size();
220 case CSSM_DB_ATTRIBUTE_FORMAT_UINT32
:
222 bool brtn
= MDSCfTypeToUInt32(value
, nameValues
, key
, ival
, srcLen
);
224 MPDebug("MDS lookupToDbAttr: Bad number conversion");
231 case CSSM_DB_ATTRIBUTE_FORMAT_MULTI_UINT32
:
234 * This is expressed in the dictionary as an array of numbers.
235 * as in CSSM_DB_ATTRIBUTE_FORMAT_UINT32, each number can be
236 * expressed as either a string or a number.
238 if(valueType
!= CFArrayGetTypeID()) {
240 * Let's be extremely slick and allow one number here, either
241 * in string or number form....
243 bool brtn
= MDSCfTypeToUInt32(value
, nameValues
, key
, ival
, srcLen
);
245 MPDebug("MDS lookupToDbAttr: Bad array element");
252 CFArrayRef cfArray
= (CFArrayRef
)value
;
253 numValues
= (uint32
)CFArrayGetCount(cfArray
);
255 /* degenerate case, legal - right? Can AppleDatabase do this? */
263 * malloc an array of uint32s
264 * convert each element in cfArray to a uint32
265 * store as CSSM_DB_ATTRIBUTE_FORMAT_MULTI_UINT32
267 * Note this does not have to be endian independent; the MDS DBs
268 * are not portable across machines let alone platforms.
270 ivalArray
= new uint32
[numValues
];
273 for(dex
=0; dex
<numValues
; dex
++) {
274 CFTypeRef elmt
= (CFTypeRef
)CFArrayGetValueAtIndex(cfArray
, dex
);
276 MPDebug("MDS lookupToDbAttr: key %s: Bad array element (1)", key
);
281 brtn
= MDSCfTypeToUInt32(elmt
, nameValues
, key
, ivalArray
[dex
], itemLen
);
283 MPDebug("MDS lookupToDbAttr: key %s Bad element at index %d",
293 * FIXME - numValues as used by MDSRawValueToDbAttr and placed in
294 * CSSM_DB_ATTRIBUTE_DATA.NumberOfValues, appears to need to be
295 * one even for MULTI_UINT32 format; the number of ints in inferred
296 * from Value.Length....
301 case CSSM_DB_ATTRIBUTE_FORMAT_BLOB
: // CFData
303 if(valueType
!= CFDataGetTypeID()) {
304 MPDebug("lookupToDbAttr: blob/CFData format mismatch");
307 CFDataRef cfData
= (CFDataRef
)value
;
308 srcLen
= CFDataGetLength(cfData
);
309 srcPtr
= CFDataGetBytePtr(cfData
);
313 case CSSM_DB_ATTRIBUTE_FORMAT_SINT32
: // I don't think we support this
315 MPDebug("lookupToDbAttr: bad attrForm(%d)", (int)attrFormat
);
319 MDSRawValueToDbAttr(srcPtr
, srcLen
, attrFormat
, key
, attr
, numValues
);
328 * Given a RelationInfo and an array of CSSM_DB_ATTRIBUTE_DATAs, fill in
329 * the CSSM_DB_ATTRIBUTE_DATA array with as many fields as we can find in
330 * the dictionary. All fields are treated as optional.
332 void MDSDictionary::lookupAttributes(
333 const RelationInfo
*relInfo
,
334 CSSM_DB_ATTRIBUTE_DATA_PTR outAttrs
, // filled in on return
335 uint32
&numAttrs
) // RETURNED
338 const CSSM_DB_ATTRIBUTE_INFO
*inAttr
= relInfo
->AttributeInfo
;
339 const MDSNameValuePair
**nameValues
= relInfo
->nameValues
;
341 assert(relInfo
!= NULL
);
343 for(dex
=0; dex
<relInfo
->NumberOfAttributes
; dex
++) {
345 const MDSNameValuePair
*nvp
;
347 /* the array itself, or any element in it, can be NULL */
348 if(nameValues
!= NULL
) {
349 nvp
= nameValues
[dex
];
354 brtn
= lookupToDbAttr(inAttr
->Label
.AttributeName
,
356 inAttr
->AttributeFormat
,
359 /* successfully added to dbAttrs */
363 inAttr
++; // regardless
368 * Lookup with file-based indirection. Allows multiple mdsinfo files to share commmon
369 * info from a separate plist file.
371 * Do a lookup for specified key. If not found, return NULL. If found:
373 * if type of value matches desiredType {
376 * else if type of value is string {
377 * if string starts with "file:" {
378 * attempt to read property list with that filename relative to
380 * if CFType of that propList matches desiredType {
381 * return newly read propList;
385 * ...else return error;
387 const CFPropertyListRef
MDSDictionary::lookupWithIndirect(
390 CFTypeID desiredType
,
391 bool &fetchedFromDisk
) // true --> caller must CFRelease the returned
393 // false -> it's part of this dictionary
395 CFPropertyListRef ourRtn
= NULL
;
396 CFDataRef dictData
= NULL
;
397 CFStringRef cfErr
= NULL
;
403 assert(bundle
!= NULL
);
405 fetchedFromDisk
= false;
407 /* basic local lookup */
408 CFStringRef cfKey
= CFStringCreateWithCString(NULL
,
410 kCFStringEncodingUTF8
);
412 MPDebug("CFStringCreateWithCString error");
415 const void *rtn
= CFDictionaryGetValue(mDict
, cfKey
);
420 CFTypeID foundType
= CFGetTypeID((CFTypeRef
)rtn
);
421 if(foundType
== desiredType
) {
422 /* found what we're looking for; done */
423 return (CFPropertyListRef
)rtn
;
426 /* is it a string which starts with "file:"? */
427 if(foundType
!= CFStringGetTypeID()) {
430 const char *cVal
= MDSCFStringToCString((CFStringRef
)rtn
);
432 MPDebug("MDSCFStringToCString error in lookupWithIndirect");
435 if(strstr(cVal
, "file:") != cVal
) {
439 /* delete [] cval on return */
441 /* OK, this specifies a resource file in the bundle. Fetch it. */
442 CFURLRef fileUrl
= NULL
;
443 CFStringRef cfFileName
= CFStringCreateWithCString(NULL
,
445 kCFStringEncodingUTF8
);
446 if(cfFileName
== NULL
) {
447 MPDebug("lookupWithIndirect: bad file name spec");
450 fileUrl
= CFBundleCopyResourceURL(bundle
,
454 if(fileUrl
== NULL
) {
455 MPDebug("lookupWithIndirect: file %s not found", cVal
);
459 MPDebug("Fetching indirect resource %s", cVal
);
461 /* Load data from URL */
462 brtn
= CFURLCreateDataAndPropertiesFromResource(
467 NULL
, // desiredProperties
470 MPDebug("lookupWithIndirect: error %d reading %s", (int)uerr
, cVal
);
474 /* if it's not a property list, we don't want it */
475 ourRtn
= CFPropertyListCreateFromXMLData(NULL
,
477 kCFPropertyListImmutable
,
480 MPDebug("lookupWithIndirect: %s malformed (not a prop list)", cVal
);
484 /* if it doesn't match the caller's spec, we don't want it */
485 if(CFGetTypeID(ourRtn
) != desiredType
) {
486 MPDebug("lookupWithIndirect: %s malformed (mismatch)", cVal
);
492 MPDebug("lookupWithIndirect: resource %s FOUND", cVal
);
493 fetchedFromDisk
= true;
497 CF_RELEASE(cfFileName
);
499 CF_RELEASE(dictData
);
504 void MDSDictionary::setDefaults(const MDS_InstallDefaults
*defaults
)
506 mDefaults
= defaults
;
509 * Save the values into (a new) mDict.
511 assert(mDict
!= NULL
);
512 CFMutableDictionaryRef tmpDict
= CFDictionaryCreateMutableCopy(NULL
, 0, mDict
);
513 if(tmpDict
== NULL
) {
514 MPDebug("setDefaults: error copying old dictionary");
515 CssmError::throwMe(CSSMERR_CSSM_MDS_ERROR
);
518 CFStringRef tmpStr
= NULL
;
521 * CFDictionaryAddValue() does nothing if the requested key is already
522 * present. If you need to call setDefaults() more than once, you'll
523 * have to add the code to remove the old key/value pairs first.
527 tmpStr
= CFStringCreateWithCString(NULL
, defaults
->guid
, kCFStringEncodingUTF8
);
529 CFDictionaryAddValue(tmpDict
, CFSTR("ModuleID"), tmpStr
);
533 MPDebug("setDefaults: error creating CFString for GUID");
534 CssmError::throwMe(CSSMERR_CSSM_MDS_ERROR
);
538 CFNumberRef tmpNum
= CFNumberCreate(NULL
, kCFNumberIntType
, &defaults
->ssid
);
540 CFDictionaryAddValue(tmpDict
, CFSTR("SSID"), tmpNum
);
544 MPDebug("setDefaults: error creating CFString for SSID");
545 CssmError::throwMe(CSSMERR_CSSM_MDS_ERROR
);
548 if(defaults
->serial
) {
549 tmpStr
= CFStringCreateWithCString(NULL
, defaults
->serial
, kCFStringEncodingUTF8
);
551 CFDictionaryAddValue(tmpDict
, CFSTR("ScSerialNumber"), tmpStr
);
555 MPDebug("setDefaults: error creating CFString for serial number");
556 CssmError::throwMe(CSSMERR_CSSM_MDS_ERROR
);
560 if(defaults
->printName
) {
561 tmpStr
= CFStringCreateWithCString(NULL
, defaults
->printName
, kCFStringEncodingUTF8
);
563 CFDictionaryAddValue(tmpDict
, CFSTR("ScDesc"), tmpStr
);
567 MPDebug("setDefaults: error creating CFString for description");
568 CssmError::throwMe(CSSMERR_CSSM_MDS_ERROR
);
574 tmpStr
= CFStringCreateWithCString(NULL
, mUrlPath
, kCFStringEncodingUTF8
);
576 CFDictionaryAddValue(tmpDict
, CFSTR("Path"), tmpStr
);
580 MPDebug("setDefaults: error creating CFString for path");
581 CssmError::throwMe(CSSMERR_CSSM_MDS_ERROR
);
584 CFDictionaryRef oldDict
= mDict
;
585 mDict
= CFDictionaryCreateCopy(NULL
, tmpDict
);
587 mDict
= oldDict
; // first do no harm
589 MPDebug("setDefaults: error creating new dictionary");
590 CssmError::throwMe(CSSMERR_CSSM_MDS_ERROR
);
597 } // end namespace Security