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