]> git.saurik.com Git - apple/security.git/blob - cdsa/mds/MDSDictionary.cpp
Security-54.1.3.tar.gz
[apple/security.git] / cdsa / mds / MDSDictionary.cpp
1 /*
2 * Copyright (c) 2000-2001 Apple Computer, 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 Apple Computer, Inc., all rights reserved.
26 */
27
28 #include "MDSDictionary.h"
29 #include "MDSAttrParser.h"
30 #include "MDSAttrUtils.h"
31 #include <Security/cssmerrno.h>
32 #include <Security/utilities.h>
33 #include <Security/logging.h>
34
35 namespace Security
36 {
37
38 /* heavyweight constructor from file */
39 MDSDictionary::MDSDictionary(
40 CFURLRef fileUrl,
41 const char *fullPath) // could get from fileUrl, but very messy!
42 : mDict(NULL),
43 mWeOwnDict(false),
44 mUrlPath(NULL),
45 mFileDesc(NULL)
46 {
47 CFDataRef dictData = NULL;
48 CFStringRef cfErr = NULL;
49
50 assert(fileUrl != NULL);
51 mUrlPath = MDSCopyCstring(fullPath);
52 MPDebug("Creating MDSDictionary from %s", mUrlPath);
53
54 /* Load data from URL */
55 SInt32 uerr;
56 Boolean brtn = CFURLCreateDataAndPropertiesFromResource(
57 NULL,
58 fileUrl,
59 &dictData,
60 NULL, // properties
61 NULL, // desiredProperties
62 &uerr);
63 if(!brtn) {
64 Syslog::alert("Error reading MDS file %s: %d", mUrlPath, uerr);
65 CssmError::throwMe(CSSMERR_CSSM_MDS_ERROR);
66 }
67
68 /* if it's not a dictionary, we don't want it */
69 mDict = reinterpret_cast<CFDictionaryRef>(
70 CFPropertyListCreateFromXMLData(NULL,
71 dictData,
72 kCFPropertyListImmutable,
73 &cfErr));
74 CFRelease(dictData);
75 if(mDict == NULL) {
76 Syslog::alert("Malformed MDS file %s (1)", mUrlPath);
77 CssmError::throwMe(CSSMERR_CSSM_MDS_ERROR);
78 }
79
80 /* henceforth we must release this dictionary */
81 mWeOwnDict = true;
82 if(CFGetTypeID(mDict) != CFDictionaryGetTypeID()) {
83 Syslog::alert("Malformed MDS file %s (2)", mUrlPath);
84 CssmError::throwMe(CSSMERR_CSSM_MDS_ERROR);
85 }
86 CF_RELEASE(cfErr);
87
88 /* get file description for error logging and debugging */
89 CFStringRef cfStr = (CFStringRef)lookup(CFSTR(MDS_INFO_FILE_DESC),
90 true, CFStringGetTypeID());
91 if(cfStr) {
92 unsigned len = CFStringGetLength(cfStr) + 1;
93 mFileDesc = new char[len];
94 if(mFileDesc) {
95 CFStringGetCString(cfStr, mFileDesc, len,
96 CFStringGetSystemEncoding());
97 }
98 }
99 }
100
101 /* lightweight constructor from existing CFDictionary */
102 MDSDictionary::MDSDictionary(CFDictionaryRef theDict)
103 : mDict(theDict),
104 mWeOwnDict(false),
105 mUrlPath(NULL),
106 mFileDesc(NULL)
107 {
108 /* note caller owns and releases the dictionary */
109 if(mDict == NULL) {
110 MPDebug("Malformed MDS file (3)");
111 CssmError::throwMe(CSSMERR_CSSM_MDS_ERROR);
112 }
113 if(CFGetTypeID(mDict) != CFDictionaryGetTypeID()) {
114 MPDebug("Malformed MDS file (4)");
115 CssmError::throwMe(CSSMERR_CSSM_MDS_ERROR);
116 }
117 }
118
119 MDSDictionary::~MDSDictionary()
120 {
121 if(mWeOwnDict) {
122 CF_RELEASE(mDict);
123 }
124 mDict = NULL;
125 delete [] mUrlPath;
126 delete [] mFileDesc;
127 }
128
129 /* lookup by either C string or CFStringRef - returns NULL on error */
130 const void *MDSDictionary::lookup(
131 const char *key,
132 bool checkType,
133 CFTypeID type)
134 {
135 #if 0
136 CFStringRef cfKey = CFStringCreateWithCStringNoCopy(NULL,
137 key,
138 CFStringGetSystemEncoding(),
139 kCFAllocatorNull);
140 #else
141 CFStringRef cfKey = CFStringCreateWithCString(NULL,
142 key,
143 CFStringGetSystemEncoding());
144 #endif
145 if(cfKey == NULL) {
146 MPDebug("MDSDictionary::lookup: error creating CFString for key");
147 return NULL;
148 }
149 const void *rtn = lookup(cfKey, checkType, type);
150 CFRelease(cfKey);
151 return rtn;
152
153 }
154
155 const void *MDSDictionary::lookup(
156 CFStringRef key,
157 bool checkType,
158 CFTypeID type)
159 {
160 assert(mDict != NULL);
161 const void *rtn = CFDictionaryGetValue(mDict, key);
162 if(rtn && checkType) {
163 if(CFGetTypeID((CFTypeRef)rtn) != type) {
164 return NULL;
165 }
166 }
167 return rtn;
168 }
169
170 /*
171 * Common means to perform a lookup in a dictionary given a C-string key and
172 * placing the value - if present - in a CSSM_DB_ATTRIBUTE_DATA. Any errors
173 * are only logged via MPDebug. Returns true if the value was found and
174 * successfully placed in supplied CSSM_DB_ATTRIBUTE_DATA.
175 *
176 * For now we assume that the key in the dictionary is the same as the key
177 * in the DB to which we're writing.
178 *
179 * We're also assuming that all DB keys are of format CSSM_DB_ATTRIBUTE_NAME_AS_STRING.
180 */
181 bool MDSDictionary::lookupToDbAttr(
182 const char *key,
183 CSSM_DB_ATTRIBUTE_DATA &attr,
184 CSSM_DB_ATTRIBUTE_FORMAT attrFormat,
185 const MDSNameValuePair *nameValues) // optional for converting strings to numbers
186 {
187 assert(mDict != NULL);
188 assert(&attr != NULL);
189
190 CFTypeRef value; // polymorphic dictionary value
191 bool ourRtn = false;
192 const void *srcPtr = NULL; // polymorphic raw source bytes
193 unsigned srcLen;
194 CSSM_STRING cstr;
195 uint32 ival = 0;
196 uint32 *ivalArray = NULL;
197 uint32 numValues = 1; // the default for MDSRawValueToDbAttr
198
199 value = (CFTypeRef)lookup(key);
200 if(value == NULL) {
201 /*
202 * Special case here: we implicitly provide a value for the "Path" key
203 * if it's not in the dictionary and we have it.
204 */
205 if((attrFormat == CSSM_DB_ATTRIBUTE_FORMAT_STRING) &&
206 !strcmp(key, "Path") &&
207 (mUrlPath != NULL)) {
208 MDSRawValueToDbAttr(mUrlPath,
209 strlen(mUrlPath) + 1,
210 attrFormat,
211 key,
212 attr,
213 1); // numValues
214 return true;
215 }
216 else {
217 return false;
218 }
219 }
220 CFTypeID valueType = CFGetTypeID(value);
221
222 /*
223 * We have the value; could be any type. Handle it based on caller's
224 * CSSM_DB_ATTRIBUTE_FORMAT.
225 */
226 switch(attrFormat) {
227 case CSSM_DB_ATTRIBUTE_FORMAT_STRING:
228 {
229 Boolean brtn;
230
231 if(valueType != CFStringGetTypeID()) {
232 MPDebug("lookupToDbAttr: string format mismatch");
233 break;
234 }
235 brtn = CFStringGetCString((CFStringRef)value, cstr,
236 CSSM_MODULE_STRING_SIZE, CFStringGetSystemEncoding());
237 if(!brtn) {
238 /* this could be "string too large for a CSSM_STRING" */
239 MPDebug("lookupToDbAttr: CFStringGetCString error");
240 }
241 else {
242 srcPtr = cstr;
243 srcLen = strlen(cstr) + 1;
244 ourRtn = true;
245 }
246 break;
247 }
248 case CSSM_DB_ATTRIBUTE_FORMAT_UINT32:
249 {
250 bool brtn = MDSCfTypeToInt(value, nameValues, key, ival);
251 if(!brtn) {
252 MPDebug("MDS lookupToDbAttr: Bad number conversion");
253 return false;
254 }
255 srcPtr = &ival;
256 srcLen = sizeof(uint32);
257 ourRtn = true;
258 break;
259 }
260 case CSSM_DB_ATTRIBUTE_FORMAT_MULTI_UINT32:
261 {
262 /*
263 * This is expressed in the dictionary as an array of numbers.
264 * as in CSSM_DB_ATTRIBUTE_FORMAT_UINT32, each number can be
265 * expressed as either a string or a number.
266 */
267 if(valueType != CFArrayGetTypeID()) {
268 /*
269 * Let's be extremely slick and allow one number here, either
270 * in string or number form....
271 */
272 bool brtn = MDSCfTypeToInt(value, nameValues, key, ival);
273 if(!brtn) {
274 MPDebug("MDS lookupToDbAttr: Bad array element");
275 return false;
276 }
277 srcPtr = &ival;
278 srcLen = sizeof(uint32);
279 ourRtn = true;
280 break;
281 }
282 CFArrayRef cfArray = (CFArrayRef)value;
283 numValues = CFArrayGetCount(cfArray);
284 if(numValues == 0) {
285 /* degenerate case, legal - right? Can AppleDatabase do this? */
286 srcPtr = NULL;
287 srcLen = 0;
288 ourRtn = true;
289 break;
290 }
291
292 /*
293 * malloc an array of uint32s
294 * convert each element in cfArray to a uint32
295 * store as CSSM_DB_ATTRIBUTE_FORMAT_MULTI_UINT32
296 *
297 * Note this does not have to be endian independent; the MDS DBs
298 * are not portable across machines let alone platforms.
299 */
300 ivalArray = new uint32[numValues];
301 unsigned dex;
302 bool brtn;
303 for(dex=0; dex<numValues; dex++) {
304 CFTypeRef elmt = (CFTypeRef)CFArrayGetValueAtIndex(cfArray, dex);
305 if(elmt == NULL) {
306 MPDebug("MDS lookupToDbAttr: key %s: Bad array element (1)", key);
307 delete [] ivalArray;
308 return false;
309 }
310 brtn = MDSCfTypeToInt(elmt, nameValues, key, ivalArray[dex]);
311 if(!brtn) {
312 MPDebug("MDS lookupToDbAttr: key %s Bad element at index %d",
313 key, dex);
314 delete [] ivalArray;
315 return false;
316 }
317 }
318 srcPtr = ivalArray;
319 srcLen = sizeof(uint32) * numValues;
320 ourRtn = true;
321 /*
322 * FIXME - numValues as used by MDSRawValueToDbAttr and placed in
323 * CSSM_DB_ATTRIBUTE_DATA.NumberOfValues, appears to need to be
324 * one even for MULTI_UINT32 format; the number of ints in inferred
325 * from Value.Length....
326 */
327 numValues = 1;
328 break;
329 }
330 case CSSM_DB_ATTRIBUTE_FORMAT_BLOB: // CFData
331 {
332 if(valueType != CFDataGetTypeID()) {
333 MPDebug("lookupToDbAttr: blob/CFData format mismatch");
334 break;
335 }
336 CFDataRef cfData = (CFDataRef)value;
337 srcLen = CFDataGetLength(cfData);
338 srcPtr = CFDataGetBytePtr(cfData);
339 ourRtn = true;
340 break;
341 }
342 case CSSM_DB_ATTRIBUTE_FORMAT_SINT32: // I don't think we support this
343 default:
344 MPDebug("lookupToDbAttr: bad attrForm(%d)", (int)attrFormat);
345 return false;
346 }
347 if(ourRtn) {
348 MDSRawValueToDbAttr(srcPtr, srcLen, attrFormat, key, attr, numValues);
349 }
350 if(ivalArray) {
351 delete [] ivalArray;
352 }
353 return ourRtn;
354 }
355
356 /*
357 * Given a RelationInfo and an array of CSSM_DB_ATTRIBUTE_DATAs, fill in
358 * the CSSM_DB_ATTRIBUTE_DATA array with as many fields as we can find in
359 * the dictionary. All fields are treated as optional.
360 */
361 void MDSDictionary::lookupAttributes(
362 const RelationInfo *relInfo,
363 CSSM_DB_ATTRIBUTE_DATA_PTR outAttrs, // filled in on return
364 uint32 &numAttrs) // RETURNED
365 {
366 unsigned dex;
367 const CSSM_DB_ATTRIBUTE_INFO *inAttr = relInfo->AttributeInfo;
368 const MDSNameValuePair **nameValues = relInfo->nameValues;
369
370 assert(relInfo != NULL);
371 numAttrs = 0;
372 for(dex=0; dex<relInfo->NumberOfAttributes; dex++) {
373 bool brtn;
374 const MDSNameValuePair *nvp;
375
376 /* the array itself, or any element in it, can be NULL */
377 if(nameValues != NULL) {
378 nvp = nameValues[dex];
379 }
380 else {
381 nvp = NULL;
382 }
383 brtn = lookupToDbAttr(inAttr->Label.AttributeName,
384 *outAttrs,
385 inAttr->AttributeFormat,
386 nvp);
387 if(brtn) {
388 /* successfully added to dbAttrs */
389 outAttrs++;
390 numAttrs++;
391 }
392 inAttr++; // regardless
393 }
394 }
395
396 /*
397 * Lookup with file-based indirection. Allows multiple mdsinfo files to share commmon
398 * info from a separate plist file.
399 *
400 * Do a lookup for specified key. If not found, return NULL. If found:
401 * {
402 * if type of value matches desiredType {
403 * return the value;
404 * }
405 * else if type of value is string {
406 * if string starts with "file:" {
407 * attempt to read property list with that filename relative to
408 * specified bundle;
409 * if CFType of that propList matches desiredType {
410 * return newly read propList;
411 * }
412 * }
413 * }
414 * ...else return error;
415 */
416 const CFPropertyListRef MDSDictionary::lookupWithIndirect(
417 const char *key,
418 CFBundleRef bundle,
419 CFTypeID desiredType,
420 bool &fetchedFromDisk) // true --> caller must CFRelease the returned
421 // value
422 // false -> it's part of this dictionary
423 {
424 CFPropertyListRef ourRtn = NULL;
425 CFDataRef dictData = NULL;
426 CFStringRef cfErr = NULL;
427 SInt32 uerr;
428 Boolean brtn;
429
430
431 assert(key != NULL);
432 assert(bundle != NULL);
433
434 fetchedFromDisk = false;
435
436 /* basic local lookup */
437 CFStringRef cfKey = CFStringCreateWithCString(NULL,
438 key,
439 CFStringGetSystemEncoding());
440 if(cfKey == NULL) {
441 MPDebug("CFStringCreateWithCString error");
442 return NULL;
443 }
444 const void *rtn = CFDictionaryGetValue(mDict, cfKey);
445 CFRelease(cfKey);
446 if(rtn == NULL) {
447 return NULL;
448 }
449 CFTypeID foundType = CFGetTypeID((CFTypeRef)rtn);
450 if(foundType == desiredType) {
451 /* found what we're looking for; done */
452 return (CFPropertyListRef)rtn;
453 }
454
455 /* is it a string which starts with "file:"? */
456 if(foundType != CFStringGetTypeID()) {
457 return NULL;
458 }
459 const char *cVal = MDSCFStringToCString((CFStringRef)rtn);
460 if(cVal == NULL) {
461 MPDebug("MDSCFStringToCString error in lookupWithIndirect");
462 return NULL;
463 }
464 if(strstr(cVal, "file:") != cVal) {
465 delete [] cVal;
466 return NULL;
467 }
468 /* delete [] cval on return */
469
470 /* OK, this specifies a resource file in the bundle. Fetch it. */
471 CFStringRef cfFileName = CFStringCreateWithCString(NULL,
472 cVal + 5,
473 CFStringGetSystemEncoding());
474 if(cfFileName == NULL) {
475 MPDebug("lookupWithIndirect: bad file name spec");
476 goto abort;
477 }
478 CFURLRef fileUrl;
479 fileUrl = CFBundleCopyResourceURL(bundle,
480 cfFileName,
481 NULL,
482 NULL);
483 if(fileUrl == NULL) {
484 MPDebug("lookupWithIndirect: file %s not found", cVal);
485 goto abort;
486 }
487
488 MPDebug("Fetching indirect resource %s", cVal);
489
490 /* Load data from URL */
491 brtn = CFURLCreateDataAndPropertiesFromResource(
492 NULL,
493 fileUrl,
494 &dictData,
495 NULL, // properties
496 NULL, // desiredProperties
497 &uerr);
498 if(!brtn) {
499 MPDebug("lookupWithIndirect: error %d reading %s", (int)uerr, cVal);
500 goto abort;
501 }
502
503 /* if it's not a property list, we don't want it */
504 ourRtn = CFPropertyListCreateFromXMLData(NULL,
505 dictData,
506 kCFPropertyListImmutable,
507 &cfErr);
508 if(ourRtn == NULL) {
509 MPDebug("lookupWithIndirect: %s malformed (not a prop list)", cVal);
510 goto abort;
511 }
512
513 /* if it doesn't match the caller's spec, we don't want it */
514 if(CFGetTypeID(ourRtn) != desiredType) {
515 MPDebug("lookupWithIndirect: %s malformed (mismatch)", cVal);
516 CF_RELEASE(ourRtn);
517 ourRtn = NULL;
518 goto abort;
519 }
520
521 MPDebug("lookupWithIndirect: resource %s FOUND", cVal);
522 fetchedFromDisk = true;
523
524 abort:
525 delete [] cVal;
526 CF_RELEASE(cfFileName);
527 CF_RELEASE(fileUrl);
528 CF_RELEASE(dictData);
529 CF_RELEASE(cfErr);
530 return ourRtn;
531 }
532
533 } // end namespace Security