]> git.saurik.com Git - apple/security.git/blob - OSX/libsecurity_mds/lib/MDSDictionary.cpp
Security-59754.41.1.tar.gz
[apple/security.git] / OSX / libsecurity_mds / lib / MDSDictionary.cpp
1 /*
2 * Copyright (c) 2000-2001,2011-2012,2014 Apple Inc. All Rights Reserved.
3 *
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
8 * using this file.
9 *
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.
16 */
17
18
19 /*
20 File: MDSDictionary.cpp
21
22 Contains: Internal representation of one MDS info file in the form of
23 a CFDictionary.
24
25 Copyright (c) 2001,2011-2012,2014 Apple Inc. All Rights Reserved.
26 */
27
28 #include "MDSDictionary.h"
29 #include "MDSAttrParser.h"
30 #include "MDSAttrUtils.h"
31 #include <security_utilities/logging.h>
32 #include <security_utilities/cfutilities.h>
33
34 namespace Security
35 {
36
37 /* heavyweight constructor from file */
38 MDSDictionary::MDSDictionary(
39 CFURLRef fileUrl,
40 CFStringRef subdir,
41 const char *fullPath) // could get from fileUrl, but very messy!
42 : mDict(NULL),
43 mWeOwnDict(false),
44 mUrlPath(NULL),
45 mFileDesc(NULL),
46 mSubdir(subdir),
47 mDefaults(NULL)
48 {
49 CFDataRef dictData = NULL;
50 CFStringRef cfErr = NULL;
51
52 assert(fileUrl != NULL);
53 mUrlPath = MDSCopyCstring(fullPath);
54 MPDebug("Creating MDSDictionary from %s", mUrlPath);
55
56 /* Load data from URL */
57 SInt32 uerr;
58 Boolean brtn = CFURLCreateDataAndPropertiesFromResource(
59 NULL,
60 fileUrl,
61 &dictData,
62 NULL, // properties
63 NULL, // desiredProperties
64 &uerr);
65 if(!brtn) {
66 Syslog::alert("Error reading MDS file %s: %d", mUrlPath, (int)uerr);
67 CssmError::throwMe(CSSMERR_CSSM_MDS_ERROR);
68 }
69
70 /* if it's not a dictionary, we don't want it */
71 mDict = reinterpret_cast<CFDictionaryRef>(
72 CFPropertyListCreateFromXMLData(NULL,
73 dictData,
74 kCFPropertyListImmutable,
75 &cfErr));
76 CFRelease(dictData);
77 if(mDict == NULL) {
78 Syslog::alert("Malformed MDS file %s (1)", mUrlPath);
79 CssmError::throwMe(CSSMERR_CSSM_MDS_ERROR);
80 }
81
82 /* henceforth we must release this dictionary */
83 mWeOwnDict = true;
84 if(CFGetTypeID(mDict) != CFDictionaryGetTypeID()) {
85 Syslog::alert("Malformed MDS file %s (2)", mUrlPath);
86 CssmError::throwMe(CSSMERR_CSSM_MDS_ERROR);
87 }
88 CF_RELEASE(cfErr);
89
90 /* get file description for error logging and debugging */
91 CFStringRef cfStr = (CFStringRef)lookup(CFSTR(MDS_INFO_FILE_DESC),
92 true, CFStringGetTypeID());
93 if(cfStr) {
94 CFIndex len = CFStringGetMaximumSizeForEncoding(CFStringGetLength(cfStr), kCFStringEncodingUTF8) + 1/*nul terminator*/;
95 mFileDesc = new char[len];
96 if(mFileDesc) {
97 CFStringGetCString(cfStr, mFileDesc, len,
98 kCFStringEncodingUTF8);
99 }
100 }
101 }
102
103 /* lightweight constructor from existing CFDictionary */
104 MDSDictionary::MDSDictionary(CFDictionaryRef theDict)
105 : mDict(theDict),
106 mWeOwnDict(false),
107 mUrlPath(NULL),
108 mFileDesc(NULL),
109 mDefaults(NULL)
110 {
111 /* note caller owns and releases the dictionary */
112 if(mDict == NULL) {
113 MPDebug("Malformed MDS file (3)");
114 CssmError::throwMe(CSSMERR_CSSM_MDS_ERROR);
115 }
116 if(CFGetTypeID(mDict) != CFDictionaryGetTypeID()) {
117 MPDebug("Malformed MDS file (4)");
118 CssmError::throwMe(CSSMERR_CSSM_MDS_ERROR);
119 }
120 }
121
122 MDSDictionary::~MDSDictionary()
123 {
124 if(mWeOwnDict) {
125 CF_RELEASE(mDict);
126 }
127 mDict = NULL;
128 delete [] mUrlPath;
129 delete [] mFileDesc;
130 }
131
132 /* lookup by either C string or CFStringRef - returns NULL on error */
133 const void *MDSDictionary::lookup(
134 const char *key,
135 bool checkType,
136 CFTypeID type)
137 {
138 CFStringRef cfKey = CFStringCreateWithCString(NULL,
139 key,
140 kCFStringEncodingUTF8);
141 if(cfKey == NULL) {
142 MPDebug("MDSDictionary::lookup: error creating CFString for key");
143 return NULL;
144 }
145 const void *rtn = lookup(cfKey, checkType, type);
146 CFRelease(cfKey);
147 return rtn;
148
149 }
150
151 const void *MDSDictionary::lookup(
152 CFStringRef key,
153 bool checkType,
154 CFTypeID type)
155 {
156 assert(mDict != NULL);
157 const void *rtn = CFDictionaryGetValue(mDict, key);
158 if(rtn && checkType) {
159 if(CFGetTypeID((CFTypeRef)rtn) != type) {
160 return NULL;
161 }
162 }
163 return rtn;
164 }
165
166 /*
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.
171 *
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.
174 *
175 * We're also assuming that all DB keys are of format CSSM_DB_ATTRIBUTE_NAME_AS_STRING.
176 */
177 bool MDSDictionary::lookupToDbAttr(
178 const char *key,
179 CSSM_DB_ATTRIBUTE_DATA &attr,
180 CSSM_DB_ATTRIBUTE_FORMAT attrFormat,
181 const MDSNameValuePair *nameValues) // optional for converting strings to numbers
182 {
183 assert(mDict != NULL);
184 assert(&attr != NULL);
185
186 CFTypeRef value; // polymorphic dictionary value
187 bool ourRtn = false;
188 const void *srcPtr = NULL; // polymorphic raw source bytes
189 size_t srcLen = 0;
190 uint32 ival = 0;
191 uint32 *ivalArray = NULL;
192 uint32 numValues = 1; // the default for MDSRawValueToDbAttr
193 string stringVal;
194
195 value = (CFTypeRef)lookup(key);
196 if(value == NULL) {
197 return false;
198 }
199 CFTypeID valueType = CFGetTypeID(value);
200
201 /*
202 * We have the value; could be any type. Handle it based on caller's
203 * CSSM_DB_ATTRIBUTE_FORMAT.
204 */
205 switch(attrFormat) {
206 case CSSM_DB_ATTRIBUTE_FORMAT_STRING:
207 {
208 if(valueType != CFStringGetTypeID()) {
209 MPDebug("lookupToDbAttr: string format mismatch");
210 break;
211 }
212 stringVal = cfString((CFStringRef)value, false);
213 srcPtr = stringVal.c_str();
214 srcLen = stringVal.size();
215 if(srcLen) {
216 ourRtn = true;
217 }
218 break;
219 }
220 case CSSM_DB_ATTRIBUTE_FORMAT_UINT32:
221 {
222 bool brtn = MDSCfTypeToUInt32(value, nameValues, key, ival, srcLen);
223 if(!brtn) {
224 MPDebug("MDS lookupToDbAttr: Bad number conversion");
225 return false;
226 }
227 srcPtr = &ival;
228 ourRtn = true;
229 break;
230 }
231 case CSSM_DB_ATTRIBUTE_FORMAT_MULTI_UINT32:
232 {
233 /*
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.
237 */
238 if(valueType != CFArrayGetTypeID()) {
239 /*
240 * Let's be extremely slick and allow one number here, either
241 * in string or number form....
242 */
243 bool brtn = MDSCfTypeToUInt32(value, nameValues, key, ival, srcLen);
244 if(!brtn) {
245 MPDebug("MDS lookupToDbAttr: Bad array element");
246 return false;
247 }
248 srcPtr = &ival;
249 ourRtn = true;
250 break;
251 }
252 CFArrayRef cfArray = (CFArrayRef)value;
253 numValues = (uint32)CFArrayGetCount(cfArray);
254 if(numValues == 0) {
255 /* degenerate case, legal - right? Can AppleDatabase do this? */
256 srcPtr = NULL;
257 srcLen = 0;
258 ourRtn = true;
259 break;
260 }
261
262 /*
263 * malloc an array of uint32s
264 * convert each element in cfArray to a uint32
265 * store as CSSM_DB_ATTRIBUTE_FORMAT_MULTI_UINT32
266 *
267 * Note this does not have to be endian independent; the MDS DBs
268 * are not portable across machines let alone platforms.
269 */
270 ivalArray = new uint32[numValues];
271 unsigned dex;
272 bool brtn;
273 for(dex=0; dex<numValues; dex++) {
274 CFTypeRef elmt = (CFTypeRef)CFArrayGetValueAtIndex(cfArray, dex);
275 if(elmt == NULL) {
276 MPDebug("MDS lookupToDbAttr: key %s: Bad array element (1)", key);
277 delete [] ivalArray;
278 return false;
279 }
280 size_t itemLen = 0;
281 brtn = MDSCfTypeToUInt32(elmt, nameValues, key, ivalArray[dex], itemLen);
282 if(!brtn) {
283 MPDebug("MDS lookupToDbAttr: key %s Bad element at index %d",
284 key, dex);
285 delete [] ivalArray;
286 return false;
287 }
288 srcLen += itemLen;
289 }
290 srcPtr = ivalArray;
291 ourRtn = true;
292 /*
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....
297 */
298 numValues = 1;
299 break;
300 }
301 case CSSM_DB_ATTRIBUTE_FORMAT_BLOB: // CFData
302 {
303 if(valueType != CFDataGetTypeID()) {
304 MPDebug("lookupToDbAttr: blob/CFData format mismatch");
305 break;
306 }
307 CFDataRef cfData = (CFDataRef)value;
308 srcLen = CFDataGetLength(cfData);
309 srcPtr = CFDataGetBytePtr(cfData);
310 ourRtn = true;
311 break;
312 }
313 case CSSM_DB_ATTRIBUTE_FORMAT_SINT32: // I don't think we support this
314 default:
315 MPDebug("lookupToDbAttr: bad attrForm(%d)", (int)attrFormat);
316 return false;
317 }
318 if(ourRtn) {
319 MDSRawValueToDbAttr(srcPtr, srcLen, attrFormat, key, attr, numValues);
320 }
321 if(ivalArray) {
322 delete [] ivalArray;
323 }
324 return ourRtn;
325 }
326
327 /*
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.
331 */
332 void MDSDictionary::lookupAttributes(
333 const RelationInfo *relInfo,
334 CSSM_DB_ATTRIBUTE_DATA_PTR outAttrs, // filled in on return
335 uint32 &numAttrs) // RETURNED
336 {
337 unsigned dex;
338 const CSSM_DB_ATTRIBUTE_INFO *inAttr = relInfo->AttributeInfo;
339 const MDSNameValuePair **nameValues = relInfo->nameValues;
340
341 assert(relInfo != NULL);
342 numAttrs = 0;
343 for(dex=0; dex<relInfo->NumberOfAttributes; dex++) {
344 bool brtn;
345 const MDSNameValuePair *nvp;
346
347 /* the array itself, or any element in it, can be NULL */
348 if(nameValues != NULL) {
349 nvp = nameValues[dex];
350 }
351 else {
352 nvp = NULL;
353 }
354 brtn = lookupToDbAttr(inAttr->Label.AttributeName,
355 *outAttrs,
356 inAttr->AttributeFormat,
357 nvp);
358 if(brtn) {
359 /* successfully added to dbAttrs */
360 outAttrs++;
361 numAttrs++;
362 }
363 inAttr++; // regardless
364 }
365 }
366
367 /*
368 * Lookup with file-based indirection. Allows multiple mdsinfo files to share commmon
369 * info from a separate plist file.
370 *
371 * Do a lookup for specified key. If not found, return NULL. If found:
372 * {
373 * if type of value matches desiredType {
374 * return the value;
375 * }
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
379 * specified bundle;
380 * if CFType of that propList matches desiredType {
381 * return newly read propList;
382 * }
383 * }
384 * }
385 * ...else return error;
386 */
387 CFPropertyListRef MDSDictionary::lookupWithIndirect(
388 const char *key,
389 CFBundleRef bundle,
390 CFTypeID desiredType)
391 {
392 CFPropertyListRef ourRtn = NULL;
393 CFDataRef dictData = NULL;
394 CFStringRef cfErr = NULL;
395 SInt32 uerr;
396 Boolean brtn;
397
398
399 assert(key != NULL);
400 assert(bundle != NULL);
401
402 /* basic local lookup */
403 CFStringRef cfKey = CFStringCreateWithCString(NULL,
404 key,
405 kCFStringEncodingUTF8);
406 if(cfKey == NULL) {
407 MPDebug("CFStringCreateWithCString error");
408 return NULL;
409 }
410 CFCopyRef<CFStringRef> rtn = (CFStringRef)CFDictionaryGetValue(mDict, cfKey);
411 CFRelease(cfKey);
412 if(!rtn) {
413 return NULL;
414 }
415 CFTypeID foundType = CFGetTypeID(rtn);
416 if(foundType == desiredType) {
417 /* found what we're looking for; done */
418 return (CFPropertyListRef)rtn.yield();
419 }
420
421 /* is it a string which starts with "file:"? */
422 if(foundType != CFStringGetTypeID()) {
423 return NULL;
424 }
425 const char *cVal = MDSCFStringToCString(rtn);
426 if(cVal == NULL) {
427 MPDebug("MDSCFStringToCString error in lookupWithIndirect");
428 return NULL;
429 }
430 if(strstr(cVal, "file:") != cVal) {
431 delete [] cVal;
432 return NULL;
433 }
434 /* delete [] cval on return */
435
436 /* OK, this specifies a resource file in the bundle. Fetch it. */
437 CFURLRef fileUrl = NULL;
438 CFStringRef cfFileName = CFStringCreateWithCString(NULL,
439 cVal + 5,
440 kCFStringEncodingUTF8);
441 if(cfFileName == NULL) {
442 MPDebug("lookupWithIndirect: bad file name spec");
443 goto abort;
444 }
445 fileUrl = CFBundleCopyResourceURL(bundle,
446 cfFileName,
447 NULL,
448 mSubdir);
449 if(fileUrl == NULL) {
450 MPDebug("lookupWithIndirect: file %s not found", cVal);
451 goto abort;
452 }
453
454 MPDebug("Fetching indirect resource %s", cVal);
455
456 /* Load data from URL */
457 brtn = CFURLCreateDataAndPropertiesFromResource(
458 NULL,
459 fileUrl,
460 &dictData,
461 NULL, // properties
462 NULL, // desiredProperties
463 &uerr);
464 if(!brtn) {
465 MPDebug("lookupWithIndirect: error %d reading %s", (int)uerr, cVal);
466 goto abort;
467 }
468
469 /* if it's not a property list, we don't want it */
470 ourRtn = CFPropertyListCreateFromXMLData(NULL,
471 dictData,
472 kCFPropertyListImmutable,
473 &cfErr);
474 if(ourRtn == NULL) {
475 MPDebug("lookupWithIndirect: %s malformed (not a prop list)", cVal);
476 goto abort;
477 }
478
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);
482 CF_RELEASE(ourRtn);
483 ourRtn = NULL;
484 goto abort;
485 }
486
487 MPDebug("lookupWithIndirect: resource %s FOUND", cVal);
488
489 abort:
490 delete [] cVal;
491 CF_RELEASE(cfFileName);
492 CF_RELEASE(fileUrl);
493 CF_RELEASE(dictData);
494 CF_RELEASE(cfErr);
495 return ourRtn;
496 }
497
498 void MDSDictionary::setDefaults(const MDS_InstallDefaults *defaults)
499 {
500 mDefaults = defaults;
501
502 /*
503 * Save the values into (a new) mDict.
504 */
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);
510 }
511
512 CFStringRef tmpStr = NULL;
513
514 /*
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.
518 */
519 if(defaults) {
520 if(defaults->guid) {
521 tmpStr = CFStringCreateWithCString(NULL, defaults->guid, kCFStringEncodingUTF8);
522 if(tmpStr) {
523 CFDictionaryAddValue(tmpDict, CFSTR("ModuleID"), tmpStr);
524 CFRelease(tmpStr);
525 }
526 else {
527 MPDebug("setDefaults: error creating CFString for GUID");
528 CssmError::throwMe(CSSMERR_CSSM_MDS_ERROR);
529 }
530 }
531
532 CFNumberRef tmpNum = CFNumberCreate(NULL, kCFNumberIntType, &defaults->ssid);
533 if(tmpNum) {
534 CFDictionaryAddValue(tmpDict, CFSTR("SSID"), tmpNum);
535 CFRelease(tmpNum);
536 }
537 else {
538 MPDebug("setDefaults: error creating CFString for SSID");
539 CssmError::throwMe(CSSMERR_CSSM_MDS_ERROR);
540 }
541
542 if(defaults->serial) {
543 tmpStr = CFStringCreateWithCString(NULL, defaults->serial, kCFStringEncodingUTF8);
544 if(tmpStr) {
545 CFDictionaryAddValue(tmpDict, CFSTR("ScSerialNumber"), tmpStr);
546 CFRelease(tmpStr);
547 }
548 else {
549 MPDebug("setDefaults: error creating CFString for serial number");
550 CssmError::throwMe(CSSMERR_CSSM_MDS_ERROR);
551 }
552 }
553
554 if(defaults->printName) {
555 tmpStr = CFStringCreateWithCString(NULL, defaults->printName, kCFStringEncodingUTF8);
556 if(tmpStr) {
557 CFDictionaryAddValue(tmpDict, CFSTR("ScDesc"), tmpStr);
558 CFRelease(tmpStr);
559 }
560 else {
561 MPDebug("setDefaults: error creating CFString for description");
562 CssmError::throwMe(CSSMERR_CSSM_MDS_ERROR);
563 }
564 }
565 }
566
567 if(mUrlPath) {
568 tmpStr = CFStringCreateWithCString(NULL, mUrlPath, kCFStringEncodingUTF8);
569 if(tmpStr) {
570 CFDictionaryAddValue(tmpDict, CFSTR("Path"), tmpStr);
571 CFRelease(tmpStr);
572 }
573 else {
574 MPDebug("setDefaults: error creating CFString for path");
575 CssmError::throwMe(CSSMERR_CSSM_MDS_ERROR);
576 }
577 }
578 CFDictionaryRef oldDict = mDict;
579 mDict = CFDictionaryCreateCopy(NULL, tmpDict);
580 if(mDict == NULL) {
581 mDict = oldDict; // first do no harm
582 CFRelease(tmpDict);
583 MPDebug("setDefaults: error creating new dictionary");
584 CssmError::throwMe(CSSMERR_CSSM_MDS_ERROR);
585 }
586 CFRelease(oldDict);
587 CFRelease(tmpDict);
588 }
589
590
591 } // end namespace Security