2 * Copyright (c) 2000-2001,2011-2014 Apple 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,2011-2014 Apple 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_utilities/cfutilities.h>
33 #include <Security/mds_schema.h>
38 MDSAttrParser::MDSAttrParser(
39 const char *bundlePath
,
41 CSSM_DB_HANDLE objectHand
,
42 CSSM_DB_HANDLE cdsaDirHand
) :
46 mObjectHand(objectHand
),
47 mCdsaDirHand(cdsaDirHand
),
51 /* Only task here is to cook up a CFBundle for the specified path */
52 size_t pathLen
= strlen(bundlePath
);
53 CFURLRef url
= CFURLCreateFromFileSystemRepresentation(NULL
,
54 (unsigned char *)bundlePath
,
58 Syslog::alert("CFURLCreateFromFileSystemRepresentation(%s) failure", mPath
);
59 CssmError::throwMe(CSSMERR_DL_INVALID_DB_NAME
);
62 /* FIXME - this leaks 28 bytes each time thru, even though we CFRelease the
63 * mBundle in out destructor. I think this is a CF leak. */
64 mBundle
= CFBundleCreate(NULL
, url
);
67 Syslog::alert("CFBundleCreate(%s) failure", mPath
);
68 CssmError::throwMe(CSSMERR_DL_INVALID_DB_NAME
);
70 mPath
= new char[pathLen
+ 1];
71 strcpy(mPath
, bundlePath
);
74 MDSAttrParser::~MDSAttrParser()
81 /*********************
85 get all *.mdsinfo files;
87 get contents of that file as dictionary;
90 parse this mdsinfo --> MDS_OBJECT_RECORDTYPE, MDS_CDSADIR_CSSM_RECORDTYPE;
93 parse this info --> MDS_OBJECT_RECORDTYPE, MDS_CDSADIR_COMMON_RECORDTYPE;
95 recordType = lookup("MdsRecordType");
96 dispatch to recordtype-specific parsing;
102 void MDSAttrParser::parseAttrs(CFStringRef subdir
)
104 /* get all *.mdsinfo files */
105 CFRef
<CFArrayRef
> bundleInfoFiles
= CFBundleCopyResourceURLsOfType(mBundle
,
106 CFSTR(MDS_INFO_TYPE
),
108 if(!bundleInfoFiles
) {
109 Syslog::alert("MDSAttrParser: no mdsattr files for %s", mPath
);
112 assert(CFGetTypeID(bundleInfoFiles
) == CFArrayGetTypeID());
114 /* process each .mdsinfo file */
115 CFIndex numFiles
= CFArrayGetCount(bundleInfoFiles
);
116 for(CFIndex i
=0; i
<numFiles
; i
++) {
117 /* get filename as CFURL */
118 CFURLRef infoUrl
= NULL
;
120 infoUrl
= reinterpret_cast<CFURLRef
>(
121 CFArrayGetValueAtIndex(bundleInfoFiles
, i
));
122 if(infoUrl
== NULL
) {
123 MPDebug("MDSAttrParser: CFBundleCopyResourceURLsOfType screwup 1");
126 if(CFGetTypeID(infoUrl
) != CFURLGetTypeID()) {
127 MPDebug("MDSAttrParser: CFBundleCopyResourceURLsOfType screwup 2");
131 // @@@ Workaround for 4234967: skip any filename beginning with "._"
132 CFRef
<CFStringRef
> lastComponent
= CFURLCopyLastPathComponent(infoUrl
);
134 CFStringRef resFilePfx
= CFSTR("._");
135 // setting the search length and location like this permits,
136 // e.g., ".foo.mdsinfo" to be valid
137 CFIndex resFilePfxLen
= CFStringGetMaximumSizeForEncoding(CFStringGetLength(resFilePfx
), kCFStringEncodingUTF8
);
138 CFRange range
= CFRangeMake(0, resFilePfxLen
);
139 Boolean skip
= CFStringFindWithOptions(lastComponent
,
143 NULL
/*returned substr*/);
145 Syslog::warning("MDSAttrParser: ignoring resource file");
150 parseFile(infoUrl
, subdir
);
151 } /* for each mdsinfo */
154 void MDSAttrParser::parseFile(CFURLRef infoUrl
, CFStringRef subdir
)
156 CFStringRef infoType
= NULL
;
158 /* Get contents of mdsinfo file as dictionary */
159 MDSDictionary
mdsDict(infoUrl
, subdir
, mPath
);
160 /* Make sure we set all possible MDS values before checking for GUID */
161 mdsDict
.setDefaults(mDefaults
);
163 CFStringRef guid
= (CFStringRef
)mdsDict
.lookup("ModuleID", true, CFStringGetTypeID());
165 CFIndex copylen
= CFStringGetMaximumSizeForEncoding(CFStringGetLength(guid
), kCFStringEncodingUTF8
) + 1/*nul terminator*/;
166 mGuid
= new char[copylen
];
167 if (false == CFStringGetCString(guid
, mGuid
, copylen
, kCFStringEncodingUTF8
)) {
168 logFileError("Error copying GUID", infoUrl
, NULL
, NULL
);
171 CssmError::throwMe(CSSM_ERRCODE_MDS_ERROR
);
175 logFileError("No GUID associated with plugin?", infoUrl
, NULL
, NULL
);
176 CssmError::throwMe(CSSM_ERRCODE_MDS_ERROR
);
180 MPDebug("Parsing mdsinfo file %s", mdsDict
.fileDesc());
182 /* Determine what kind of info file this is and dispatch accordingly */
183 infoType
= (CFStringRef
)mdsDict
.lookup(CFSTR(MDS_INFO_FILE_TYPE
),
184 true, CFStringGetTypeID());
185 if(infoType
== NULL
) {
186 logFileError("Malformed MDS Info file", infoUrl
, NULL
, NULL
);
187 CssmError::throwMe(CSSM_ERRCODE_MDS_ERROR
);
190 /* be robust here, errors in these low-level routines do not affect
191 * the rest of our task */
193 if(CFStringCompare(infoType
, CFSTR(MDS_INFO_FILE_TYPE_CSSM
), 0)
194 == kCFCompareEqualTo
) {
195 parseCssmInfo(&mdsDict
);
197 else if(CFStringCompare(infoType
, CFSTR(MDS_INFO_FILE_TYPE_PLUGIN
), 0)
198 == kCFCompareEqualTo
) {
199 parsePluginCommon(&mdsDict
);
201 else if(CFStringCompare(infoType
, CFSTR(MDS_INFO_FILE_TYPE_RECORD
), 0)
202 == kCFCompareEqualTo
) {
203 parsePluginSpecific(&mdsDict
);
206 logFileError("Malformed MDS Info file", infoUrl
, NULL
, NULL
);
214 void MDSAttrParser::logFileError(
217 CFStringRef errStr
, // optional if you have it
218 SInt32
*errNo
) // optional if you have it
220 CFStringRef urlStr
= CFURLGetString(fileUrl
);
221 const char *cUrlStr
= CFStringGetCStringPtr(urlStr
, kCFStringEncodingUTF8
);
222 char* stringBuffer
= NULL
;
226 CFIndex maxLen
= CFStringGetMaximumSizeForEncoding(CFStringGetLength(urlStr
), kCFStringEncodingUTF8
) + 1;
227 stringBuffer
= (char*) malloc(maxLen
);
228 CFStringGetCString(urlStr
, stringBuffer
, maxLen
, kCFStringEncodingUTF8
);
229 cUrlStr
= stringBuffer
;
233 const char *cerrStr
= CFStringGetCStringPtr(errStr
, kCFStringEncodingUTF8
);
238 CFIndex maxLen
= CFStringGetMaximumSizeForEncoding(CFStringGetLength(errStr
), kCFStringEncodingUTF8
) + 1;
239 sbuf2
= (char*) malloc(maxLen
);
240 CFStringGetCString(urlStr
, sbuf2
, maxLen
, kCFStringEncodingUTF8
);
244 Syslog::alert("MDS: %s: bundle %s url %s: error %s",
245 op
, mPath
, cUrlStr
, cerrStr
);
253 Syslog::alert("MDS: %s: bundle %s url %s: error %d",
254 op
, mPath
, cUrlStr
, (int)(errNo
? *errNo
: 0));
257 if (stringBuffer
!= NULL
)
264 * Parse a CSSM info file.
266 void MDSAttrParser::parseCssmInfo(
267 MDSDictionary
*mdsDict
)
269 /* first get object info */
270 parseObjectRecord(mdsDict
);
272 /* now CSSM relation */
273 const RelationInfo
*relationInfo
=
274 MDSRecordTypeToRelation(MDS_CDSADIR_CSSM_RECORDTYPE
);
275 assert(relationInfo
!= NULL
);
276 parseMdsRecord(mdsDict
, relationInfo
, mCdsaDirHand
);
280 * Parse a PluginCommon file.
282 void MDSAttrParser::parsePluginCommon(
283 MDSDictionary
*mdsDict
)
286 /* first get object info */
287 parseObjectRecord(mdsDict
);
289 /* now common relation */
290 const RelationInfo
*relationInfo
=
291 MDSRecordTypeToRelation(MDS_CDSADIR_COMMON_RECORDTYPE
);
292 assert(relationInfo
!= NULL
);
293 parseMdsRecord(mdsDict
, relationInfo
, mCdsaDirHand
);
297 * Parse a Plugin Specific file.
299 void MDSAttrParser::parsePluginSpecific(
300 MDSDictionary
*mdsDict
)
302 /* determine record type from the file itself */
303 CFStringRef recordTypeStr
=
304 (CFStringRef
)mdsDict
->lookup(MDS_INFO_FILE_RECORD_TYPE
,
305 true, CFStringGetTypeID());
306 if(recordTypeStr
== NULL
) {
307 MPDebug("%s: no %s record found\n", mdsDict
->fileDesc(),
308 MDS_INFO_FILE_RECORD_TYPE
);
312 /* convert to a known schema */
313 const char *recordTypeCStr
= MDSCFStringToCString(recordTypeStr
);
314 const RelationInfo
*relationInfo
= MDSRecordTypeNameToRelation(recordTypeCStr
);
315 if(relationInfo
== NULL
) {
316 Syslog::alert("MDS file %s has unsupported record type %s",
317 mdsDict
->fileDesc(), recordTypeCStr
);
318 MPDebug("MDS file %s has unsupported record type %s",
319 mdsDict
->fileDesc(), recordTypeCStr
);
320 delete [] recordTypeCStr
;
323 MPDebug("Parsing MDS file %s, recordType %s", mdsDict
->fileDesc(), recordTypeCStr
);
324 delete [] recordTypeCStr
;
326 /* handle special cases here */
327 switch(relationInfo
->DataRecordType
) {
328 case MDS_CDSADIR_CSP_CAPABILITY_RECORDTYPE
:
329 parseCspCapabilitiesRecord(mdsDict
);
331 case MDS_CDSADIR_TP_OIDS_RECORDTYPE
:
332 parseTpPolicyOidsRecord(mdsDict
);
335 /* all (normal) linear schema */
336 parseMdsRecord(mdsDict
, relationInfo
, mCdsaDirHand
);
342 * Given an open MDSDictionary, create an MDS_OBJECT_RECORDTYPE record and
343 * add it to mObjectHand. Used when parsing both CSSM records and MOduleCommon
346 void MDSAttrParser::parseObjectRecord(
347 MDSDictionary
*mdsDict
)
349 assert(mdsDict
!= NULL
);
350 assert(mObjectHand
!= 0);
351 parseMdsRecord(mdsDict
, &kObjectRelation
, mObjectHand
);
356 * Given an open dictionary and a RelationInfo defining a schema, fetch all
357 * attributes associated with the specified schema from the dictionary
358 * and write them to specified DB.
360 void MDSAttrParser::parseMdsRecord(
361 MDSDictionary
*mdsDict
,
362 const RelationInfo
*relInfo
,
363 CSSM_DB_HANDLE dbHand
)
365 assert(mdsDict
!= NULL
);
366 assert(relInfo
!= NULL
);
370 * malloc an CSSM_DB_ATTRIBUTE_DATA array associated with specified schema.
372 unsigned numSchemaAttrs
= relInfo
->NumberOfAttributes
;
373 CSSM_DB_ATTRIBUTE_DATA
*dbAttrs
= new CSSM_DB_ATTRIBUTE_DATA
[numSchemaAttrs
];
376 * Grind thru the attributes in the specified schema. Do not assume the presence
377 * of any given attribute.
379 uint32 foundAttrs
= 0;
380 mdsDict
->lookupAttributes(relInfo
, dbAttrs
, foundAttrs
);
382 /* write to the DB */
383 MDSInsertRecord(dbAttrs
, foundAttrs
, relInfo
->DataRecordType
, mDl
, dbHand
);
385 MDSFreeDbRecordAttrs(dbAttrs
, foundAttrs
);
390 * Parse CSP capabilities. This is much more complicated than most records.
391 * The propertly list (*.mdsinfo) is set up like this:
396 * Capabilities(Array) {
397 * index 0(Dictionary) {
398 * AlgType(String) -- CSSM_ALGID_SHA1
399 * ContextType(String) -- CSSM_ALGCLASS_DIGEST
400 * UseeTag(String) -- CSSM_USEE_NONE
401 * Description(String) -- "SHA1 Digest"
403 * index 0(Dictionary)
404 * AttributeType(String) -- CSSM_ATTRIBUTE_OUTPUT_SIZE
405 * AttributeValue(Array) {
406 * index 0(Number) -- 20
415 * The plist can specify multiple Capabilities, multiple Attributes for each
416 * Capability, and multiple values for each Attribute. (Note that MULTI_UINT32
417 * in the DB is represented in the plist as an Array of Numbers.) Each element
418 * of each Attributes array maps to one record in the DB. The GroupID attribute
419 * of a record is the index into the plist's Capabilities array.
421 void MDSAttrParser::parseCspCapabilitiesRecord(
422 MDSDictionary
*mdsDict
)
425 * Malloc an attribute array big enough for the whole schema. We're going
426 * to re-use this array every time we write a new record. Portions of
427 * the array are invariant for some inner loops.
429 const RelationInfo
*topRelInfo
=
430 MDSRecordTypeToRelation(MDS_CDSADIR_CSP_CAPABILITY_RECORDTYPE
);
431 assert(topRelInfo
!= NULL
);
432 uint32 numInAttrs
= topRelInfo
->NumberOfAttributes
;
433 CSSM_DB_ATTRIBUTE_DATA_PTR outAttrs
= new CSSM_DB_ATTRIBUTE_DATA
[numInAttrs
];
435 /* these attrs are only set once, then they remain invariant */
436 uint32 numTopLevelAttrs
;
437 mdsDict
->lookupAttributes(&CSPCapabilitiesDict1RelInfo
, outAttrs
,
440 /* obtain Capabilities array */
441 CFRef
<CFArrayRef
> capArray
= (CFArrayRef
)mdsDict
->lookupWithIndirect("Capabilities",
445 /* well we did not get very far.... */
446 MPDebug("parseCspCapabilitiesRecord: no (or bad) Capabilities");
452 * Descend into Capabilities array. Each element is a dictionary defined
453 * by CSPCapabilitiesDict2RelInfo.
455 CFIndex capArraySize
= CFArrayGetCount(capArray
);
457 for(capDex
=0; capDex
<capArraySize
; capDex
++) {
458 MPDebug("...parsing Capability %d", (int)capDex
);
459 CFDictionaryRef capDict
=
460 (CFDictionaryRef
)CFArrayGetValueAtIndex(capArray
, capDex
);
461 if((capDict
== NULL
) ||
462 (CFGetTypeID(capDict
) != CFDictionaryGetTypeID())) {
463 MPDebug("parseCspCapabilitiesRecord: bad Capabilities element");
466 MDSDictionary
capDictMds(capDict
);
469 * Append this dictionary's attributes to outAttrs, after the fixed
470 * attributes from CSPCapabilitiesDict1RelInfo.
472 uint32 numCapDictAttrs
;
473 capDictMds
.lookupAttributes(&CSPCapabilitiesDict2RelInfo
,
474 &outAttrs
[numTopLevelAttrs
],
478 * Append the GroupId attribute, which we infer from the current index
479 * into Capabilitites. However, thou shalt not use > 4-byte values
480 * for MDS, so convert from CFIndex first.
482 if (capDex
> uint32(~0)) {
483 MPDebug("parseCspCapabilitiesRecord: too large an index for MDS");
486 uint32 index32
= uint32(capDex
);
487 MDSRawValueToDbAttr(&index32
, sizeof(index32
), CSSM_DB_ATTRIBUTE_FORMAT_UINT32
,
488 "GroupId", outAttrs
[numTopLevelAttrs
+ numCapDictAttrs
]);
492 * Now descend into the array of this capability's attributes.
493 * Each element is a dictionary defined by
494 * by CSPCapabilitiesDict3RelInfo.
496 CFArrayRef attrArray
= (CFArrayRef
)capDictMds
.lookup("Attributes",
497 true, CFArrayGetTypeID());
498 if(attrArray
== NULL
) {
499 MPDebug("parseCspCapabilitiesRecord: no (or bad) Attributes");
502 CFIndex attrArraySize
= CFArrayGetCount(attrArray
);
504 for(attrDex
=0; attrDex
<attrArraySize
; attrDex
++) {
505 MPDebug(" ...parsing Attribute %d", (int)attrDex
);
506 CFDictionaryRef attrDict
=
507 (CFDictionaryRef
)CFArrayGetValueAtIndex(attrArray
, attrDex
);
508 if((attrDict
== NULL
) ||
509 (CFGetTypeID(attrDict
) != CFDictionaryGetTypeID())) {
510 MPDebug("parseCspCapabilitiesRecord: bad Attributes element");
513 MDSDictionary
attrDictMds(attrDict
);
516 * Append this dictionary's attributes to outAttrs, after the fixed
517 * attributes from CSPCapabilitiesDict1RelInfo and this capability's
518 * CSPCapabilitiesDict2RelInfo.
520 uint32 numAttrDictAttrs
;
521 attrDictMds
.lookupAttributes(&CSPCapabilitiesDict3RelInfo
,
522 &outAttrs
[numTopLevelAttrs
+ numCapDictAttrs
],
526 MDSInsertRecord(outAttrs
,
527 numTopLevelAttrs
+ numCapDictAttrs
+ numAttrDictAttrs
,
528 MDS_CDSADIR_CSP_CAPABILITY_RECORDTYPE
,
532 /* just free the attrs we allocated in this loop */
533 MDSFreeDbRecordAttrs(&outAttrs
[numTopLevelAttrs
+ numCapDictAttrs
],
535 } /* for each attribute */
536 /* just free the attrs we allocated in this loop */
537 MDSFreeDbRecordAttrs(&outAttrs
[numTopLevelAttrs
], numCapDictAttrs
);
538 } /* for each capability */
540 MDSFreeDbRecordAttrs(outAttrs
, numTopLevelAttrs
);
545 * Parse TP Policy OIDs.
546 * The propertly list (*.mdsinfo) is set up like this:
552 * index 0(Dictionary) {
553 * OID(Data) -- <092a8648 86f76364 0102>
554 * Value(Data) -- optional, OID-specific
559 * The plist can specify multiple Policies. Each element of the Policies
560 * array maps to one record in the DB.
562 void MDSAttrParser::parseTpPolicyOidsRecord(
563 MDSDictionary
*mdsDict
)
566 * Malloc an attribute array big enough for the whole schema. We're going
567 * to re-use this array every time we write a new record. Portions of
568 * the array are invariant for some inner loops.
570 const RelationInfo
*topRelInfo
=
571 MDSRecordTypeToRelation(MDS_CDSADIR_TP_OIDS_RECORDTYPE
);
572 assert(topRelInfo
!= NULL
);
573 uint32 numInAttrs
= topRelInfo
->NumberOfAttributes
;
574 CSSM_DB_ATTRIBUTE_DATA_PTR outAttrs
= new CSSM_DB_ATTRIBUTE_DATA
[numInAttrs
];
576 /* these attrs are only set once, then they remain invariant */
577 uint32 numTopLevelAttrs
;
578 mdsDict
->lookupAttributes(&TpPolicyOidsDict1RelInfo
, outAttrs
,
581 /* obtain Policies array */
582 CFArrayRef policyArray
= (CFArrayRef
)mdsDict
->lookup("Policies",
583 true, CFArrayGetTypeID());
584 if(policyArray
== NULL
) {
585 /* well we did not get very far.... */
586 MPDebug("parseTpPolicyOidsRecord: no (or bad) Policies");
592 * Descend into Policies array. Each element is a dictionary defined
593 * by TpPolicyOidsDict2RelInfo.
595 CFIndex policyArraySize
= CFArrayGetCount(policyArray
);
597 for(policyDex
=0; policyDex
<policyArraySize
; policyDex
++) {
598 MPDebug("...parsing Policy %d", (int)policyDex
);
599 CFDictionaryRef policyDict
=
600 (CFDictionaryRef
)CFArrayGetValueAtIndex(policyArray
, policyDex
);
601 if((policyDict
== NULL
) ||
602 (CFGetTypeID(policyDict
) != CFDictionaryGetTypeID())) {
603 MPDebug("parseTpPolicyOidsRecord: bad Policies element");
606 MDSDictionary
policyDictMds(policyDict
);
609 * Append this dictionary's attributes to outAttrs, after the fixed
610 * attributes from TpPolicyOidsDict1RelInfo.
612 uint32 numPolicyDictAttrs
;
613 policyDictMds
.lookupAttributes(&TpPolicyOidsDict2RelInfo
,
614 &outAttrs
[numTopLevelAttrs
],
619 MDSInsertRecord(outAttrs
,
620 numTopLevelAttrs
+ numPolicyDictAttrs
,
621 MDS_CDSADIR_TP_OIDS_RECORDTYPE
,
625 /* free the attrs allocated in this loop */
626 MDSFreeDbRecordAttrs(outAttrs
+ numTopLevelAttrs
, numPolicyDictAttrs
);
627 } /* for each policy */
628 MDSFreeDbRecordAttrs(outAttrs
, numTopLevelAttrs
);
633 } // end namespace Security