2 * Copyright (c) 2000-2001 Apple Computer, 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: MDSAttrParser.cpp
22 Contains: Classes to parse XML plists and fill in MDS DBs with the
23 attributes found there.
25 Copyright: (c) 2001 Apple Computer, Inc., all rights reserved.
28 #include "MDSAttrParser.h"
29 #include "MDSAttrUtils.h"
30 #include "MDSDictionary.h"
31 #include <Security/cssmerrno.h>
32 #include <Security/utilities.h>
33 #include <Security/logging.h>
34 #include <Security/mds_schema.h>
39 MDSAttrParser::MDSAttrParser(
40 const char *bundlePath
,
42 CSSM_DB_HANDLE objectHand
,
43 CSSM_DB_HANDLE cdsaDirHand
) :
47 mObjectHand(objectHand
),
48 mCdsaDirHand(cdsaDirHand
)
50 /* Only task here is to cook up a CFBundle for the specified path */
51 unsigned pathLen
= strlen(bundlePath
);
52 CFURLRef url
= CFURLCreateFromFileSystemRepresentation(NULL
,
53 (unsigned char *)bundlePath
,
57 Syslog::alert("CFURLCreateFromFileSystemRepresentation(%s) failure", mPath
);
58 CssmError::throwMe(CSSMERR_DL_INVALID_DB_NAME
);
61 /* FIXME - this leaks 28 bytes each time thru, even though we CFRelease the
62 * mBundle in out destructor. I think this is a CF leak. */
63 mBundle
= CFBundleCreate(NULL
, url
);
66 Syslog::alert("CFBundleCreate(%s) failure", mPath
);
67 CssmError::throwMe(CSSMERR_DL_INVALID_DB_NAME
);
69 mPath
= new char[pathLen
+ 1];
70 strcpy(mPath
, bundlePath
);
73 MDSAttrParser::~MDSAttrParser()
79 /*********************
83 get all *.mdsinfo files;
85 get contents of that file as dictionary;
88 parse this mdsinfo --> MDS_OBJECT_RECORDTYPE, MDS_CDSADIR_CSSM_RECORDTYPE;
91 parse this info --> MDS_OBJECT_RECORDTYPE, MDS_CDSADIR_COMMON_RECORDTYPE;
93 recordType = lookup("MdsRecordType");
94 dispatch to recordtype-specific parsing;
100 #define RELEASE_EACH_URL 0
102 void MDSAttrParser::parseAttrs()
104 /* get all *.mdsinfo files */
106 * FIXME - this leaks like crazy even though we CFRelease the array.
107 * With RELEASE_EACH_URL true, we attempt to release each element of
108 * the array, but that results in a ton of mallocDebug errors. I believe
111 CFArrayRef bundleInfoFiles
= CFBundleCopyResourceURLsOfType(mBundle
,
112 CFSTR(MDS_INFO_TYPE
),
114 if(bundleInfoFiles
== NULL
) {
115 Syslog::alert("MDSAttrParser: no mdsattr files for %s", mPath
);
118 assert(CFGetTypeID(bundleInfoFiles
) == CFArrayGetTypeID());
120 /* process each .mdsinfo file */
121 for(CFIndex i
=0; i
<CFArrayGetCount(bundleInfoFiles
); i
++) {
122 /* get filename as CFURL */
123 CFURLRef infoUrl
= NULL
;
124 MDSDictionary
*mdsDict
= NULL
;
125 CFStringRef infoType
= NULL
;
127 infoUrl
= reinterpret_cast<CFURLRef
>(
128 CFArrayGetValueAtIndex(bundleInfoFiles
, i
));
129 if(infoUrl
== NULL
) {
130 MPDebug("MDSAttrParser: CFBundleCopyResourceURLsOfType screwup 1");
133 if(CFGetTypeID(infoUrl
) != CFURLGetTypeID()) {
134 MPDebug("MDSAttrParser: CFBundleCopyResourceURLsOfType screwup 2");
138 /* Get contents of mdsinfo file as dictionary */
139 mdsDict
= new MDSDictionary(infoUrl
, mPath
);
140 if(mdsDict
== NULL
) {
143 MPDebug("Parsing mdsinfo file %s", mdsDict
->fileDesc());
145 /* Determine what kind of info file this is and dispatch accordingly */
146 infoType
= (CFStringRef
)mdsDict
->lookup(CFSTR(MDS_INFO_FILE_TYPE
),
147 true, CFStringGetTypeID());
148 if(infoType
== NULL
) {
149 logFileError("Malformed MDS Info file", infoUrl
, NULL
, NULL
);
153 /* be robust here, errors in these low-level routines do not affect
154 * the rest of our task */
156 if(CFStringCompare(infoType
, CFSTR(MDS_INFO_FILE_TYPE_CSSM
), 0)
157 == kCFCompareEqualTo
) {
158 parseCssmInfo(mdsDict
);
160 else if(CFStringCompare(infoType
, CFSTR(MDS_INFO_FILE_TYPE_PLUGIN
), 0)
161 == kCFCompareEqualTo
) {
162 parsePluginCommon(mdsDict
);
164 else if(CFStringCompare(infoType
, CFSTR(MDS_INFO_FILE_TYPE_RECORD
), 0)
165 == kCFCompareEqualTo
) {
166 parsePluginSpecific(mdsDict
);
169 logFileError("Malformed MDS Info file", infoUrl
, NULL
, NULL
);
177 } /* for each mdsinfo */
178 /* FIXME - do we have to release each element of the array? */
180 for(CFIndex i
=0; i
<CFArrayGetCount(bundleInfoFiles
); i
++) {
181 CFTypeRef elmt
= (CFTypeRef
)CFArrayGetValueAtIndex(bundleInfoFiles
, i
);
185 CF_RELEASE(bundleInfoFiles
);
188 void MDSAttrParser::logFileError(
191 CFStringRef errStr
, // optional if you have it
192 SInt32
*errNo
) // optional if you have it
194 const char *cerrStr
= NULL
;
195 CFStringRef urlStr
= CFURLGetString(fileUrl
);
196 const char *cUrlStr
= CFStringGetCStringPtr(urlStr
, CFStringGetSystemEncoding());
199 cerrStr
= CFStringGetCStringPtr(errStr
, CFStringGetSystemEncoding());
200 Syslog::alert("MDS: %s: bundle %s url %s: error %s",
201 op
, mPath
, cUrlStr
, cerrStr
);
204 Syslog::alert("MDS: %s: bundle %s url %s: error %d",
205 op
, mPath
, cUrlStr
, errNo
? *errNo
: 0);
210 * Parse a CSSM info file.
212 void MDSAttrParser::parseCssmInfo(
213 MDSDictionary
*mdsDict
)
215 /* first get object info */
216 parseObjectRecord(mdsDict
);
218 /* now CSSM relation */
219 const RelationInfo
*relationInfo
=
220 MDSRecordTypeToRelation(MDS_CDSADIR_CSSM_RECORDTYPE
);
221 assert(relationInfo
!= NULL
);
222 parseMdsRecord(mdsDict
, relationInfo
, mCdsaDirHand
);
226 * Parse a PluginCommon file.
228 void MDSAttrParser::parsePluginCommon(
229 MDSDictionary
*mdsDict
)
232 /* first get object info */
233 parseObjectRecord(mdsDict
);
235 /* now common relation */
236 const RelationInfo
*relationInfo
=
237 MDSRecordTypeToRelation(MDS_CDSADIR_COMMON_RECORDTYPE
);
238 assert(relationInfo
!= NULL
);
239 parseMdsRecord(mdsDict
, relationInfo
, mCdsaDirHand
);
243 * Parse a Plugin Specific file.
245 void MDSAttrParser::parsePluginSpecific(
246 MDSDictionary
*mdsDict
)
248 /* determine record type from the file itself */
249 CFStringRef recordTypeStr
=
250 (CFStringRef
)mdsDict
->lookup(MDS_INFO_FILE_RECORD_TYPE
,
251 true, CFStringGetTypeID());
252 if(recordTypeStr
== NULL
) {
253 MPDebug("%s: no %s record found\n", mdsDict
->fileDesc(),
254 MDS_INFO_FILE_RECORD_TYPE
);
258 /* convert to a known schema */
259 const char *recordTypeCStr
= MDSCFStringToCString(recordTypeStr
);
260 const RelationInfo
*relationInfo
= MDSRecordTypeNameToRelation(recordTypeCStr
);
261 if(relationInfo
== NULL
) {
262 Syslog::alert("MDS file %s has unsupported record type %s",
263 mdsDict
->fileDesc(), recordTypeCStr
);
264 MPDebug("MDS file %s has unsupported record type %s",
265 mdsDict
->fileDesc(), recordTypeCStr
);
266 delete [] recordTypeCStr
;
269 MPDebug("Parsing MDS file %s, recordType %s", mdsDict
->fileDesc(), recordTypeCStr
);
270 delete [] recordTypeCStr
;
272 /* handle special cases here */
273 switch(relationInfo
->DataRecordType
) {
274 case MDS_CDSADIR_CSP_CAPABILITY_RECORDTYPE
:
275 parseCspCapabilitiesRecord(mdsDict
);
277 case MDS_CDSADIR_TP_OIDS_RECORDTYPE
:
278 parseTpPolicyOidsRecord(mdsDict
);
281 /* all (normal) linear schema */
282 parseMdsRecord(mdsDict
, relationInfo
, mCdsaDirHand
);
288 * Given an open MDSDictionary, create an MDS_OBJECT_RECORDTYPE record and
289 * add it to mObjectHand. Used when parsing both CSSM records and MOduleCommon
292 void MDSAttrParser::parseObjectRecord(
293 MDSDictionary
*mdsDict
)
295 assert(mdsDict
!= NULL
);
296 assert(mObjectHand
!= 0);
297 parseMdsRecord(mdsDict
, &kObjectRelation
, mObjectHand
);
302 * Given an open dictionary and a RelationInfo defining a schema, fetch all
303 * attributes associated with the specified schema from the dictionary
304 * and write them to specified DB.
306 void MDSAttrParser::parseMdsRecord(
307 MDSDictionary
*mdsDict
,
308 const RelationInfo
*relInfo
,
309 CSSM_DB_HANDLE dbHand
)
311 assert(mdsDict
!= NULL
);
312 assert(relInfo
!= NULL
);
316 * malloc an CSSM_DB_ATTRIBUTE_DATA array associated with specified schema.
318 unsigned numSchemaAttrs
= relInfo
->NumberOfAttributes
;
319 CSSM_DB_ATTRIBUTE_DATA
*dbAttrs
= new CSSM_DB_ATTRIBUTE_DATA
[numSchemaAttrs
];
322 * Grind thru the attributes in the specified schema. Do not assume the presence
323 * of any given attribute.
325 uint32 foundAttrs
= 0;
326 mdsDict
->lookupAttributes(relInfo
, dbAttrs
, foundAttrs
);
328 /* write to the DB */
329 MDSInsertRecord(dbAttrs
, foundAttrs
, relInfo
->DataRecordType
, mDl
, dbHand
);
331 MDSFreeDbRecordAttrs(dbAttrs
, foundAttrs
);
336 * Parse CSP capabilities. This is much more complicated than most records.
337 * The propertly list (*.mdsinfo) is set up like this:
342 * Capabilities(Array) {
343 * index 0(Dictionary) {
344 * AlgType(String) -- CSSM_ALGID_SHA1
345 * ContextType(String) -- CSSM_ALGCLASS_DIGEST
346 * UseeTag(String) -- CSSM_USEE_NONE
347 * Description(String) -- "SHA1 Digest"
349 * index 0(Dictionary)
350 * AttributeType(String) -- CSSM_ATTRIBUTE_OUTPUT_SIZE
351 * AttributeValue(Array) {
352 * index 0(Number) -- 20
361 * The plist can specify multiple Capabilities, multiple Attributes for each
362 * Capability, and multiple values for each Attribute. (Note that MULTI_UINT32
363 * in the DB is represented in the plist as an Array of Numbers.) Each element
364 * of each Attributes array maps to one record in the DB. The GroupID attribute
365 * of a record is the index into the plist's Capabilities array.
367 void MDSAttrParser::parseCspCapabilitiesRecord(
368 MDSDictionary
*mdsDict
)
371 * Malloc an attribute array big enough for the whole schema. We're going
372 * to re-use this array every time we write a new record. Portions of
373 * the array are invariant for some inner loops.
375 const RelationInfo
*topRelInfo
=
376 MDSRecordTypeToRelation(MDS_CDSADIR_CSP_CAPABILITY_RECORDTYPE
);
377 assert(topRelInfo
!= NULL
);
378 uint32 numInAttrs
= topRelInfo
->NumberOfAttributes
;
379 CSSM_DB_ATTRIBUTE_DATA_PTR outAttrs
= new CSSM_DB_ATTRIBUTE_DATA
[numInAttrs
];
381 /* these attrs are only set once, then they remain invariant */
382 uint32 numTopLevelAttrs
;
383 mdsDict
->lookupAttributes(&CSPCapabilitiesDict1RelInfo
, outAttrs
,
386 bool fetchedFromDisk
= false;
388 /* obtain Capabilities array */
389 CFArrayRef capArray
= (CFArrayRef
)mdsDict
->lookupWithIndirect("Capabilities",
393 if(capArray
== NULL
) {
394 /* well we did not get very far.... */
395 MPDebug("parseCspCapabilitiesRecord: no (or bad) Capabilities");
401 * Descend into Capabilities array. Each element is a dictionary defined
402 * by CSPCapabilitiesDict2RelInfo.
404 CFIndex capArraySize
= CFArrayGetCount(capArray
);
406 for(capDex
=0; capDex
<capArraySize
; capDex
++) {
407 MPDebug("...parsing Capability %d", (int)capDex
);
408 CFDictionaryRef capDict
=
409 (CFDictionaryRef
)CFArrayGetValueAtIndex(capArray
, capDex
);
410 if((capDict
== NULL
) ||
411 (CFGetTypeID(capDict
) != CFDictionaryGetTypeID())) {
412 MPDebug("parseCspCapabilitiesRecord: bad Capabilities element");
415 MDSDictionary
capDictMds(capDict
);
418 * Append this dictionary's attributes to outAttrs, after the fixed
419 * attributes from CSPCapabilitiesDict1RelInfo.
421 uint32 numCapDictAttrs
;
422 capDictMds
.lookupAttributes(&CSPCapabilitiesDict2RelInfo
,
423 &outAttrs
[numTopLevelAttrs
],
427 * Append the GroupId attribute, which we infer from the current index
428 * into Capabilitites.
430 MDSRawValueToDbAttr(&capDex
, sizeof(CFIndex
), CSSM_DB_ATTRIBUTE_FORMAT_UINT32
,
431 "GroupId", outAttrs
[numTopLevelAttrs
+ numCapDictAttrs
]);
435 * Now descend into the array of this capability's attributes.
436 * Each element is a dictionary defined by
437 * by CSPCapabilitiesDict3RelInfo.
439 CFArrayRef attrArray
= (CFArrayRef
)capDictMds
.lookup("Attributes",
440 true, CFArrayGetTypeID());
441 if(attrArray
== NULL
) {
442 MPDebug("parseCspCapabilitiesRecord: no (or bad) Attributes");
445 CFIndex attrArraySize
= CFArrayGetCount(attrArray
);
447 for(attrDex
=0; attrDex
<attrArraySize
; attrDex
++) {
448 MPDebug(" ...parsing Attribute %d", (int)attrDex
);
449 CFDictionaryRef attrDict
=
450 (CFDictionaryRef
)CFArrayGetValueAtIndex(attrArray
, attrDex
);
451 if((attrDict
== NULL
) ||
452 (CFGetTypeID(attrDict
) != CFDictionaryGetTypeID())) {
453 MPDebug("parseCspCapabilitiesRecord: bad Attributes element");
456 MDSDictionary
attrDictMds(attrDict
);
459 * Append this dictionary's attributes to outAttrs, after the fixed
460 * attributes from CSPCapabilitiesDict1RelInfo and this capability's
461 * CSPCapabilitiesDict2RelInfo.
463 uint32 numAttrDictAttrs
;
464 attrDictMds
.lookupAttributes(&CSPCapabilitiesDict3RelInfo
,
465 &outAttrs
[numTopLevelAttrs
+ numCapDictAttrs
],
469 MDSInsertRecord(outAttrs
,
470 numTopLevelAttrs
+ numCapDictAttrs
+ numAttrDictAttrs
,
471 MDS_CDSADIR_CSP_CAPABILITY_RECORDTYPE
,
475 /* just free the attrs we allocated in this loop */
476 MDSFreeDbRecordAttrs(&outAttrs
[numTopLevelAttrs
+ numCapDictAttrs
],
478 } /* for each attribute */
479 /* just free the attrs we allocated in this loop */
480 MDSFreeDbRecordAttrs(&outAttrs
[numTopLevelAttrs
], numCapDictAttrs
);
481 } /* for each capability */
483 MDSFreeDbRecordAttrs(outAttrs
, numTopLevelAttrs
);
485 if(fetchedFromDisk
) {
486 CF_RELEASE(capArray
);
491 * Parse TP Policy OIDs.
492 * The propertly list (*.mdsinfo) is set up like this:
498 * index 0(Dictionary) {
499 * OID(Data) -- <092a8648 86f76364 0102>
500 * Value(Data) -- optional, OID-specific
505 * The plist can specify multiple Policies. Each element of the Policies
506 * array maps to one record in the DB.
508 void MDSAttrParser::parseTpPolicyOidsRecord(
509 MDSDictionary
*mdsDict
)
512 * Malloc an attribute array big enough for the whole schema. We're going
513 * to re-use this array every time we write a new record. Portions of
514 * the array are invariant for some inner loops.
516 const RelationInfo
*topRelInfo
=
517 MDSRecordTypeToRelation(MDS_CDSADIR_TP_OIDS_RECORDTYPE
);
518 assert(topRelInfo
!= NULL
);
519 uint32 numInAttrs
= topRelInfo
->NumberOfAttributes
;
520 CSSM_DB_ATTRIBUTE_DATA_PTR outAttrs
= new CSSM_DB_ATTRIBUTE_DATA
[numInAttrs
];
522 /* these attrs are only set once, then they remain invariant */
523 uint32 numTopLevelAttrs
;
524 mdsDict
->lookupAttributes(&TpPolicyOidsDict1RelInfo
, outAttrs
,
527 /* obtain Policies array */
528 CFArrayRef policyArray
= (CFArrayRef
)mdsDict
->lookup("Policies",
529 true, CFArrayGetTypeID());
530 if(policyArray
== NULL
) {
531 /* well we did not get very far.... */
532 MPDebug("parseTpPolicyOidsRecord: no (or bad) Policies");
538 * Descend into Policies array. Each element is a dictionary defined
539 * by TpPolicyOidsDict2RelInfo.
541 CFIndex policyArraySize
= CFArrayGetCount(policyArray
);
543 for(policyDex
=0; policyDex
<policyArraySize
; policyDex
++) {
544 MPDebug("...parsing Policy %d", (int)policyDex
);
545 CFDictionaryRef policyDict
=
546 (CFDictionaryRef
)CFArrayGetValueAtIndex(policyArray
, policyDex
);
547 if((policyDict
== NULL
) ||
548 (CFGetTypeID(policyDict
) != CFDictionaryGetTypeID())) {
549 MPDebug("parseTpPolicyOidsRecord: bad Policies element");
552 MDSDictionary
policyDictMds(policyDict
);
555 * Append this dictionary's attributes to outAttrs, after the fixed
556 * attributes from TpPolicyOidsDict1RelInfo.
558 uint32 numPolicyDictAttrs
;
559 policyDictMds
.lookupAttributes(&TpPolicyOidsDict2RelInfo
,
560 &outAttrs
[numTopLevelAttrs
],
565 MDSInsertRecord(outAttrs
,
566 numTopLevelAttrs
+ numPolicyDictAttrs
,
567 MDS_CDSADIR_TP_OIDS_RECORDTYPE
,
571 /* free the attrs allocated in this loop */
572 MDSFreeDbRecordAttrs(outAttrs
+ numTopLevelAttrs
, numPolicyDictAttrs
);
573 } /* for each policy */
574 MDSFreeDbRecordAttrs(outAttrs
, numTopLevelAttrs
);
579 } // end namespace Security