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_utilities/logging.h>
32 #include <Security/mds_schema.h>
37 MDSAttrParser::MDSAttrParser(
38 const char *bundlePath
,
40 CSSM_DB_HANDLE objectHand
,
41 CSSM_DB_HANDLE cdsaDirHand
) :
45 mObjectHand(objectHand
),
46 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()
80 /*********************
84 get all *.mdsinfo files;
86 get contents of that file as dictionary;
89 parse this mdsinfo --> MDS_OBJECT_RECORDTYPE, MDS_CDSADIR_CSSM_RECORDTYPE;
92 parse this info --> MDS_OBJECT_RECORDTYPE, MDS_CDSADIR_COMMON_RECORDTYPE;
94 recordType = lookup("MdsRecordType");
95 dispatch to recordtype-specific parsing;
101 void MDSAttrParser::parseAttrs(CFStringRef subdir
)
103 /* get all *.mdsinfo files */
104 CFArrayRef bundleInfoFiles
= CFBundleCopyResourceURLsOfType(mBundle
,
105 CFSTR(MDS_INFO_TYPE
),
107 if(bundleInfoFiles
== NULL
) {
108 Syslog::alert("MDSAttrParser: no mdsattr files for %s", mPath
);
111 assert(CFGetTypeID(bundleInfoFiles
) == CFArrayGetTypeID());
113 /* process each .mdsinfo file */
114 CFIndex numFiles
= CFArrayGetCount(bundleInfoFiles
);
115 for(CFIndex i
=0; i
<numFiles
; i
++) {
116 /* get filename as CFURL */
117 CFURLRef infoUrl
= NULL
;
119 infoUrl
= reinterpret_cast<CFURLRef
>(
120 CFArrayGetValueAtIndex(bundleInfoFiles
, i
));
121 if(infoUrl
== NULL
) {
122 MPDebug("MDSAttrParser: CFBundleCopyResourceURLsOfType screwup 1");
125 if(CFGetTypeID(infoUrl
) != CFURLGetTypeID()) {
126 MPDebug("MDSAttrParser: CFBundleCopyResourceURLsOfType screwup 2");
130 // @@@ Workaround for 4234967: skip any filename beginning with "._"
131 CFStringRef lastComponent
= CFURLCopyLastPathComponent(infoUrl
);
133 CFStringRef resFilePfx
= CFSTR("._");
134 // setting the search length and location like this permits,
135 // e.g., ".foo.mdsinfo" to be valid
136 CFIndex resFilePfxLen
= CFStringGetMaximumSizeForEncoding(CFStringGetLength(resFilePfx
), kCFStringEncodingUTF8
);
137 CFRange range
= CFRangeMake(0, resFilePfxLen
);
138 Boolean skip
= CFStringFindWithOptions(lastComponent
,
142 NULL
/*returned substr*/);
143 CFRelease(lastComponent
);
145 Syslog::warning("MDSAttrParser: ignoring resource file");
150 parseFile(infoUrl
, subdir
);
151 } /* for each mdsinfo */
152 CF_RELEASE(bundleInfoFiles
);
155 void MDSAttrParser::parseFile(CFURLRef infoUrl
, CFStringRef subdir
)
157 CFStringRef infoType
= NULL
;
159 /* Get contents of mdsinfo file as dictionary */
160 MDSDictionary
mdsDict(infoUrl
, subdir
, mPath
);
161 /* Make sure we set all possible MDS values before checking for GUID */
162 mdsDict
.setDefaults(mDefaults
);
164 CFStringRef guid
= (CFStringRef
)mdsDict
.lookup("ModuleID", true, CFStringGetTypeID());
166 CFIndex copylen
= CFStringGetMaximumSizeForEncoding(CFStringGetLength(guid
), kCFStringEncodingUTF8
) + 1/*nul terminator*/;
167 mGuid
= new char[copylen
];
168 if (false == CFStringGetCString(guid
, mGuid
, copylen
, kCFStringEncodingUTF8
)) {
169 logFileError("Error copying GUID", infoUrl
, NULL
, NULL
);
172 CssmError::throwMe(CSSM_ERRCODE_MDS_ERROR
);
176 logFileError("No GUID associated with plugin?", infoUrl
, NULL
, NULL
);
177 CssmError::throwMe(CSSM_ERRCODE_MDS_ERROR
);
181 MPDebug("Parsing mdsinfo file %s", mdsDict
.fileDesc());
183 /* Determine what kind of info file this is and dispatch accordingly */
184 infoType
= (CFStringRef
)mdsDict
.lookup(CFSTR(MDS_INFO_FILE_TYPE
),
185 true, CFStringGetTypeID());
186 if(infoType
== NULL
) {
187 logFileError("Malformed MDS Info file", infoUrl
, NULL
, NULL
);
188 CssmError::throwMe(CSSM_ERRCODE_MDS_ERROR
);
191 /* be robust here, errors in these low-level routines do not affect
192 * the rest of our task */
194 if(CFStringCompare(infoType
, CFSTR(MDS_INFO_FILE_TYPE_CSSM
), 0)
195 == kCFCompareEqualTo
) {
196 parseCssmInfo(&mdsDict
);
198 else if(CFStringCompare(infoType
, CFSTR(MDS_INFO_FILE_TYPE_PLUGIN
), 0)
199 == kCFCompareEqualTo
) {
200 parsePluginCommon(&mdsDict
);
202 else if(CFStringCompare(infoType
, CFSTR(MDS_INFO_FILE_TYPE_RECORD
), 0)
203 == kCFCompareEqualTo
) {
204 parsePluginSpecific(&mdsDict
);
207 logFileError("Malformed MDS Info file", infoUrl
, NULL
, NULL
);
215 void MDSAttrParser::logFileError(
218 CFStringRef errStr
, // optional if you have it
219 SInt32
*errNo
) // optional if you have it
221 const char *cerrStr
= NULL
;
222 CFStringRef urlStr
= CFURLGetString(fileUrl
);
223 const char *cUrlStr
= CFStringGetCStringPtr(urlStr
, kCFStringEncodingUTF8
);
226 cerrStr
= CFStringGetCStringPtr(errStr
, kCFStringEncodingUTF8
);
227 Syslog::alert("MDS: %s: bundle %s url %s: error %s",
228 op
, mPath
, cUrlStr
, cerrStr
);
231 Syslog::alert("MDS: %s: bundle %s url %s: error %d",
232 op
, mPath
, cUrlStr
, errNo
? *errNo
: 0);
237 * Parse a CSSM info file.
239 void MDSAttrParser::parseCssmInfo(
240 MDSDictionary
*mdsDict
)
242 /* first get object info */
243 parseObjectRecord(mdsDict
);
245 /* now CSSM relation */
246 const RelationInfo
*relationInfo
=
247 MDSRecordTypeToRelation(MDS_CDSADIR_CSSM_RECORDTYPE
);
248 assert(relationInfo
!= NULL
);
249 parseMdsRecord(mdsDict
, relationInfo
, mCdsaDirHand
);
253 * Parse a PluginCommon file.
255 void MDSAttrParser::parsePluginCommon(
256 MDSDictionary
*mdsDict
)
259 /* first get object info */
260 parseObjectRecord(mdsDict
);
262 /* now common relation */
263 const RelationInfo
*relationInfo
=
264 MDSRecordTypeToRelation(MDS_CDSADIR_COMMON_RECORDTYPE
);
265 assert(relationInfo
!= NULL
);
266 parseMdsRecord(mdsDict
, relationInfo
, mCdsaDirHand
);
270 * Parse a Plugin Specific file.
272 void MDSAttrParser::parsePluginSpecific(
273 MDSDictionary
*mdsDict
)
275 /* determine record type from the file itself */
276 CFStringRef recordTypeStr
=
277 (CFStringRef
)mdsDict
->lookup(MDS_INFO_FILE_RECORD_TYPE
,
278 true, CFStringGetTypeID());
279 if(recordTypeStr
== NULL
) {
280 MPDebug("%s: no %s record found\n", mdsDict
->fileDesc(),
281 MDS_INFO_FILE_RECORD_TYPE
);
285 /* convert to a known schema */
286 const char *recordTypeCStr
= MDSCFStringToCString(recordTypeStr
);
287 const RelationInfo
*relationInfo
= MDSRecordTypeNameToRelation(recordTypeCStr
);
288 if(relationInfo
== NULL
) {
289 Syslog::alert("MDS file %s has unsupported record type %s",
290 mdsDict
->fileDesc(), recordTypeCStr
);
291 MPDebug("MDS file %s has unsupported record type %s",
292 mdsDict
->fileDesc(), recordTypeCStr
);
293 delete [] recordTypeCStr
;
296 MPDebug("Parsing MDS file %s, recordType %s", mdsDict
->fileDesc(), recordTypeCStr
);
297 delete [] recordTypeCStr
;
299 /* handle special cases here */
300 switch(relationInfo
->DataRecordType
) {
301 case MDS_CDSADIR_CSP_CAPABILITY_RECORDTYPE
:
302 parseCspCapabilitiesRecord(mdsDict
);
304 case MDS_CDSADIR_TP_OIDS_RECORDTYPE
:
305 parseTpPolicyOidsRecord(mdsDict
);
308 /* all (normal) linear schema */
309 parseMdsRecord(mdsDict
, relationInfo
, mCdsaDirHand
);
315 * Given an open MDSDictionary, create an MDS_OBJECT_RECORDTYPE record and
316 * add it to mObjectHand. Used when parsing both CSSM records and MOduleCommon
319 void MDSAttrParser::parseObjectRecord(
320 MDSDictionary
*mdsDict
)
322 assert(mdsDict
!= NULL
);
323 assert(mObjectHand
!= 0);
324 parseMdsRecord(mdsDict
, &kObjectRelation
, mObjectHand
);
329 * Given an open dictionary and a RelationInfo defining a schema, fetch all
330 * attributes associated with the specified schema from the dictionary
331 * and write them to specified DB.
333 void MDSAttrParser::parseMdsRecord(
334 MDSDictionary
*mdsDict
,
335 const RelationInfo
*relInfo
,
336 CSSM_DB_HANDLE dbHand
)
338 assert(mdsDict
!= NULL
);
339 assert(relInfo
!= NULL
);
343 * malloc an CSSM_DB_ATTRIBUTE_DATA array associated with specified schema.
345 unsigned numSchemaAttrs
= relInfo
->NumberOfAttributes
;
346 CSSM_DB_ATTRIBUTE_DATA
*dbAttrs
= new CSSM_DB_ATTRIBUTE_DATA
[numSchemaAttrs
];
349 * Grind thru the attributes in the specified schema. Do not assume the presence
350 * of any given attribute.
352 uint32 foundAttrs
= 0;
353 mdsDict
->lookupAttributes(relInfo
, dbAttrs
, foundAttrs
);
355 /* write to the DB */
356 MDSInsertRecord(dbAttrs
, foundAttrs
, relInfo
->DataRecordType
, mDl
, dbHand
);
358 MDSFreeDbRecordAttrs(dbAttrs
, foundAttrs
);
363 * Parse CSP capabilities. This is much more complicated than most records.
364 * The propertly list (*.mdsinfo) is set up like this:
369 * Capabilities(Array) {
370 * index 0(Dictionary) {
371 * AlgType(String) -- CSSM_ALGID_SHA1
372 * ContextType(String) -- CSSM_ALGCLASS_DIGEST
373 * UseeTag(String) -- CSSM_USEE_NONE
374 * Description(String) -- "SHA1 Digest"
376 * index 0(Dictionary)
377 * AttributeType(String) -- CSSM_ATTRIBUTE_OUTPUT_SIZE
378 * AttributeValue(Array) {
379 * index 0(Number) -- 20
388 * The plist can specify multiple Capabilities, multiple Attributes for each
389 * Capability, and multiple values for each Attribute. (Note that MULTI_UINT32
390 * in the DB is represented in the plist as an Array of Numbers.) Each element
391 * of each Attributes array maps to one record in the DB. The GroupID attribute
392 * of a record is the index into the plist's Capabilities array.
394 void MDSAttrParser::parseCspCapabilitiesRecord(
395 MDSDictionary
*mdsDict
)
398 * Malloc an attribute array big enough for the whole schema. We're going
399 * to re-use this array every time we write a new record. Portions of
400 * the array are invariant for some inner loops.
402 const RelationInfo
*topRelInfo
=
403 MDSRecordTypeToRelation(MDS_CDSADIR_CSP_CAPABILITY_RECORDTYPE
);
404 assert(topRelInfo
!= NULL
);
405 uint32 numInAttrs
= topRelInfo
->NumberOfAttributes
;
406 CSSM_DB_ATTRIBUTE_DATA_PTR outAttrs
= new CSSM_DB_ATTRIBUTE_DATA
[numInAttrs
];
408 /* these attrs are only set once, then they remain invariant */
409 uint32 numTopLevelAttrs
;
410 mdsDict
->lookupAttributes(&CSPCapabilitiesDict1RelInfo
, outAttrs
,
413 bool fetchedFromDisk
= false;
415 /* obtain Capabilities array */
416 CFArrayRef capArray
= (CFArrayRef
)mdsDict
->lookupWithIndirect("Capabilities",
420 if(capArray
== NULL
) {
421 /* well we did not get very far.... */
422 MPDebug("parseCspCapabilitiesRecord: no (or bad) Capabilities");
428 * Descend into Capabilities array. Each element is a dictionary defined
429 * by CSPCapabilitiesDict2RelInfo.
431 CFIndex capArraySize
= CFArrayGetCount(capArray
);
433 for(capDex
=0; capDex
<capArraySize
; capDex
++) {
434 MPDebug("...parsing Capability %d", (int)capDex
);
435 CFDictionaryRef capDict
=
436 (CFDictionaryRef
)CFArrayGetValueAtIndex(capArray
, capDex
);
437 if((capDict
== NULL
) ||
438 (CFGetTypeID(capDict
) != CFDictionaryGetTypeID())) {
439 MPDebug("parseCspCapabilitiesRecord: bad Capabilities element");
442 MDSDictionary
capDictMds(capDict
);
445 * Append this dictionary's attributes to outAttrs, after the fixed
446 * attributes from CSPCapabilitiesDict1RelInfo.
448 uint32 numCapDictAttrs
;
449 capDictMds
.lookupAttributes(&CSPCapabilitiesDict2RelInfo
,
450 &outAttrs
[numTopLevelAttrs
],
454 * Append the GroupId attribute, which we infer from the current index
455 * into Capabilitites. However, thou shalt not use > 4-byte values
456 * for MDS, so convert from CFIndex first.
458 if (capDex
> uint32(~0)) {
459 MPDebug("parseCspCapabilitiesRecord: too large an index for MDS");
462 uint32 index32
= uint32(capDex
);
463 MDSRawValueToDbAttr(&index32
, sizeof(index32
), CSSM_DB_ATTRIBUTE_FORMAT_UINT32
,
464 "GroupId", outAttrs
[numTopLevelAttrs
+ numCapDictAttrs
]);
468 * Now descend into the array of this capability's attributes.
469 * Each element is a dictionary defined by
470 * by CSPCapabilitiesDict3RelInfo.
472 CFArrayRef attrArray
= (CFArrayRef
)capDictMds
.lookup("Attributes",
473 true, CFArrayGetTypeID());
474 if(attrArray
== NULL
) {
475 MPDebug("parseCspCapabilitiesRecord: no (or bad) Attributes");
478 CFIndex attrArraySize
= CFArrayGetCount(attrArray
);
480 for(attrDex
=0; attrDex
<attrArraySize
; attrDex
++) {
481 MPDebug(" ...parsing Attribute %d", (int)attrDex
);
482 CFDictionaryRef attrDict
=
483 (CFDictionaryRef
)CFArrayGetValueAtIndex(attrArray
, attrDex
);
484 if((attrDict
== NULL
) ||
485 (CFGetTypeID(attrDict
) != CFDictionaryGetTypeID())) {
486 MPDebug("parseCspCapabilitiesRecord: bad Attributes element");
489 MDSDictionary
attrDictMds(attrDict
);
492 * Append this dictionary's attributes to outAttrs, after the fixed
493 * attributes from CSPCapabilitiesDict1RelInfo and this capability's
494 * CSPCapabilitiesDict2RelInfo.
496 uint32 numAttrDictAttrs
;
497 attrDictMds
.lookupAttributes(&CSPCapabilitiesDict3RelInfo
,
498 &outAttrs
[numTopLevelAttrs
+ numCapDictAttrs
],
502 MDSInsertRecord(outAttrs
,
503 numTopLevelAttrs
+ numCapDictAttrs
+ numAttrDictAttrs
,
504 MDS_CDSADIR_CSP_CAPABILITY_RECORDTYPE
,
508 /* just free the attrs we allocated in this loop */
509 MDSFreeDbRecordAttrs(&outAttrs
[numTopLevelAttrs
+ numCapDictAttrs
],
511 } /* for each attribute */
512 /* just free the attrs we allocated in this loop */
513 MDSFreeDbRecordAttrs(&outAttrs
[numTopLevelAttrs
], numCapDictAttrs
);
514 } /* for each capability */
516 MDSFreeDbRecordAttrs(outAttrs
, numTopLevelAttrs
);
518 if(fetchedFromDisk
) {
519 CF_RELEASE(capArray
);
524 * Parse TP Policy OIDs.
525 * The propertly list (*.mdsinfo) is set up like this:
531 * index 0(Dictionary) {
532 * OID(Data) -- <092a8648 86f76364 0102>
533 * Value(Data) -- optional, OID-specific
538 * The plist can specify multiple Policies. Each element of the Policies
539 * array maps to one record in the DB.
541 void MDSAttrParser::parseTpPolicyOidsRecord(
542 MDSDictionary
*mdsDict
)
545 * Malloc an attribute array big enough for the whole schema. We're going
546 * to re-use this array every time we write a new record. Portions of
547 * the array are invariant for some inner loops.
549 const RelationInfo
*topRelInfo
=
550 MDSRecordTypeToRelation(MDS_CDSADIR_TP_OIDS_RECORDTYPE
);
551 assert(topRelInfo
!= NULL
);
552 uint32 numInAttrs
= topRelInfo
->NumberOfAttributes
;
553 CSSM_DB_ATTRIBUTE_DATA_PTR outAttrs
= new CSSM_DB_ATTRIBUTE_DATA
[numInAttrs
];
555 /* these attrs are only set once, then they remain invariant */
556 uint32 numTopLevelAttrs
;
557 mdsDict
->lookupAttributes(&TpPolicyOidsDict1RelInfo
, outAttrs
,
560 /* obtain Policies array */
561 CFArrayRef policyArray
= (CFArrayRef
)mdsDict
->lookup("Policies",
562 true, CFArrayGetTypeID());
563 if(policyArray
== NULL
) {
564 /* well we did not get very far.... */
565 MPDebug("parseTpPolicyOidsRecord: no (or bad) Policies");
571 * Descend into Policies array. Each element is a dictionary defined
572 * by TpPolicyOidsDict2RelInfo.
574 CFIndex policyArraySize
= CFArrayGetCount(policyArray
);
576 for(policyDex
=0; policyDex
<policyArraySize
; policyDex
++) {
577 MPDebug("...parsing Policy %d", (int)policyDex
);
578 CFDictionaryRef policyDict
=
579 (CFDictionaryRef
)CFArrayGetValueAtIndex(policyArray
, policyDex
);
580 if((policyDict
== NULL
) ||
581 (CFGetTypeID(policyDict
) != CFDictionaryGetTypeID())) {
582 MPDebug("parseTpPolicyOidsRecord: bad Policies element");
585 MDSDictionary
policyDictMds(policyDict
);
588 * Append this dictionary's attributes to outAttrs, after the fixed
589 * attributes from TpPolicyOidsDict1RelInfo.
591 uint32 numPolicyDictAttrs
;
592 policyDictMds
.lookupAttributes(&TpPolicyOidsDict2RelInfo
,
593 &outAttrs
[numTopLevelAttrs
],
598 MDSInsertRecord(outAttrs
,
599 numTopLevelAttrs
+ numPolicyDictAttrs
,
600 MDS_CDSADIR_TP_OIDS_RECORDTYPE
,
604 /* free the attrs allocated in this loop */
605 MDSFreeDbRecordAttrs(outAttrs
+ numTopLevelAttrs
, numPolicyDictAttrs
);
606 } /* for each policy */
607 MDSFreeDbRecordAttrs(outAttrs
, numTopLevelAttrs
);
612 } // end namespace Security