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 CFPropertyListRef
MDSDictionary::lookupWithIndirect(
390 CFTypeID desiredType
)
392 CFPropertyListRef ourRtn
= NULL
;
393 CFDataRef dictData
= NULL
;
394 CFStringRef cfErr
= NULL
;
400 assert(bundle
!= NULL
);
402 /* basic local lookup */
403 CFStringRef cfKey
= CFStringCreateWithCString(NULL
,
405 kCFStringEncodingUTF8
);
407 MPDebug("CFStringCreateWithCString error");
410 CFCopyRef
<CFStringRef
> rtn
= (CFStringRef
)CFDictionaryGetValue(mDict
, cfKey
);
415 CFTypeID foundType
= CFGetTypeID(rtn
);
416 if(foundType
== desiredType
) {
417 /* found what we're looking for; done */
418 return (CFPropertyListRef
)rtn
.yield();
421 /* is it a string which starts with "file:"? */
422 if(foundType
!= CFStringGetTypeID()) {
425 const char *cVal
= MDSCFStringToCString(rtn
);
427 MPDebug("MDSCFStringToCString error in lookupWithIndirect");
430 if(strstr(cVal
, "file:") != cVal
) {
434 /* delete [] cval on return */
436 /* OK, this specifies a resource file in the bundle. Fetch it. */
437 CFURLRef fileUrl
= NULL
;
438 CFStringRef cfFileName
= CFStringCreateWithCString(NULL
,
440 kCFStringEncodingUTF8
);
441 if(cfFileName
== NULL
) {
442 MPDebug("lookupWithIndirect: bad file name spec");
445 fileUrl
= CFBundleCopyResourceURL(bundle
,
449 if(fileUrl
== NULL
) {
450 MPDebug("lookupWithIndirect: file %s not found", cVal
);
454 MPDebug("Fetching indirect resource %s", cVal
);
456 /* Load data from URL */
457 brtn
= CFURLCreateDataAndPropertiesFromResource(
462 NULL
, // desiredProperties
465 MPDebug("lookupWithIndirect: error %d reading %s", (int)uerr
, cVal
);
469 /* if it's not a property list, we don't want it */
470 ourRtn
= CFPropertyListCreateFromXMLData(NULL
,
472 kCFPropertyListImmutable
,
475 MPDebug("lookupWithIndirect: %s malformed (not a prop list)", cVal
);
479 /* if it doesn't match the caller's spec, we don't want it */
480 if(CFGetTypeID(ourRtn
) != desiredType
) {
481 MPDebug("lookupWithIndirect: %s malformed (mismatch)", cVal
);
487 MPDebug("lookupWithIndirect: resource %s FOUND", cVal
);
491 CF_RELEASE(cfFileName
);
493 CF_RELEASE(dictData
);
498 void MDSDictionary::setDefaults(const MDS_InstallDefaults
*defaults
)
500 mDefaults
= defaults
;
503 * Save the values into (a new) mDict.
505 assert(mDict
!= NULL
);
506 CFMutableDictionaryRef tmpDict
= CFDictionaryCreateMutableCopy(NULL
, 0, mDict
);
507 if(tmpDict
== NULL
) {
508 MPDebug("setDefaults: error copying old dictionary");
509 CssmError::throwMe(CSSMERR_CSSM_MDS_ERROR
);
512 CFStringRef tmpStr
= NULL
;
515 * CFDictionaryAddValue() does nothing if the requested key is already
516 * present. If you need to call setDefaults() more than once, you'll
517 * have to add the code to remove the old key/value pairs first.
521 tmpStr
= CFStringCreateWithCString(NULL
, defaults
->guid
, kCFStringEncodingUTF8
);
523 CFDictionaryAddValue(tmpDict
, CFSTR("ModuleID"), tmpStr
);
527 MPDebug("setDefaults: error creating CFString for GUID");
528 CssmError::throwMe(CSSMERR_CSSM_MDS_ERROR
);
532 CFNumberRef tmpNum
= CFNumberCreate(NULL
, kCFNumberIntType
, &defaults
->ssid
);
534 CFDictionaryAddValue(tmpDict
, CFSTR("SSID"), tmpNum
);
538 MPDebug("setDefaults: error creating CFString for SSID");
539 CssmError::throwMe(CSSMERR_CSSM_MDS_ERROR
);
542 if(defaults
->serial
) {
543 tmpStr
= CFStringCreateWithCString(NULL
, defaults
->serial
, kCFStringEncodingUTF8
);
545 CFDictionaryAddValue(tmpDict
, CFSTR("ScSerialNumber"), tmpStr
);
549 MPDebug("setDefaults: error creating CFString for serial number");
550 CssmError::throwMe(CSSMERR_CSSM_MDS_ERROR
);
554 if(defaults
->printName
) {
555 tmpStr
= CFStringCreateWithCString(NULL
, defaults
->printName
, kCFStringEncodingUTF8
);
557 CFDictionaryAddValue(tmpDict
, CFSTR("ScDesc"), tmpStr
);
561 MPDebug("setDefaults: error creating CFString for description");
562 CssmError::throwMe(CSSMERR_CSSM_MDS_ERROR
);
568 tmpStr
= CFStringCreateWithCString(NULL
, mUrlPath
, kCFStringEncodingUTF8
);
570 CFDictionaryAddValue(tmpDict
, CFSTR("Path"), tmpStr
);
574 MPDebug("setDefaults: error creating CFString for path");
575 CssmError::throwMe(CSSMERR_CSSM_MDS_ERROR
);
578 CFDictionaryRef oldDict
= mDict
;
579 mDict
= CFDictionaryCreateCopy(NULL
, tmpDict
);
581 mDict
= oldDict
; // first do no harm
583 MPDebug("setDefaults: error creating new dictionary");
584 CssmError::throwMe(CSSMERR_CSSM_MDS_ERROR
);
591 } // end namespace Security