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/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 size_t 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 CFStringRef urlStr
= CFURLGetString(fileUrl
);
222 const char *cUrlStr
= CFStringGetCStringPtr(urlStr
, kCFStringEncodingUTF8
);
223 char* stringBuffer
= NULL
;
227 CFIndex maxLen
= CFStringGetMaximumSizeForEncoding(CFStringGetLength(urlStr
), kCFStringEncodingUTF8
) + 1;
228 stringBuffer
= (char*) malloc(maxLen
);
229 CFStringGetCString(urlStr
, stringBuffer
, maxLen
, kCFStringEncodingUTF8
);
230 cUrlStr
= stringBuffer
;
234 const char *cerrStr
= CFStringGetCStringPtr(errStr
, kCFStringEncodingUTF8
);
239 CFIndex maxLen
= CFStringGetMaximumSizeForEncoding(CFStringGetLength(errStr
), kCFStringEncodingUTF8
) + 1;
240 sbuf2
= (char*) malloc(maxLen
);
241 CFStringGetCString(urlStr
, sbuf2
, maxLen
, kCFStringEncodingUTF8
);
245 Syslog::alert("MDS: %s: bundle %s url %s: error %s",
246 op
, mPath
, cUrlStr
, cerrStr
);
254 Syslog::alert("MDS: %s: bundle %s url %s: error %d",
255 op
, mPath
, cUrlStr
, (int)(errNo
? *errNo
: 0));
258 if (stringBuffer
!= NULL
)
265 * Parse a CSSM info file.
267 void MDSAttrParser::parseCssmInfo(
268 MDSDictionary
*mdsDict
)
270 /* first get object info */
271 parseObjectRecord(mdsDict
);
273 /* now CSSM relation */
274 const RelationInfo
*relationInfo
=
275 MDSRecordTypeToRelation(MDS_CDSADIR_CSSM_RECORDTYPE
);
276 assert(relationInfo
!= NULL
);
277 parseMdsRecord(mdsDict
, relationInfo
, mCdsaDirHand
);
281 * Parse a PluginCommon file.
283 void MDSAttrParser::parsePluginCommon(
284 MDSDictionary
*mdsDict
)
287 /* first get object info */
288 parseObjectRecord(mdsDict
);
290 /* now common relation */
291 const RelationInfo
*relationInfo
=
292 MDSRecordTypeToRelation(MDS_CDSADIR_COMMON_RECORDTYPE
);
293 assert(relationInfo
!= NULL
);
294 parseMdsRecord(mdsDict
, relationInfo
, mCdsaDirHand
);
298 * Parse a Plugin Specific file.
300 void MDSAttrParser::parsePluginSpecific(
301 MDSDictionary
*mdsDict
)
303 /* determine record type from the file itself */
304 CFStringRef recordTypeStr
=
305 (CFStringRef
)mdsDict
->lookup(MDS_INFO_FILE_RECORD_TYPE
,
306 true, CFStringGetTypeID());
307 if(recordTypeStr
== NULL
) {
308 MPDebug("%s: no %s record found\n", mdsDict
->fileDesc(),
309 MDS_INFO_FILE_RECORD_TYPE
);
313 /* convert to a known schema */
314 const char *recordTypeCStr
= MDSCFStringToCString(recordTypeStr
);
315 const RelationInfo
*relationInfo
= MDSRecordTypeNameToRelation(recordTypeCStr
);
316 if(relationInfo
== NULL
) {
317 Syslog::alert("MDS file %s has unsupported record type %s",
318 mdsDict
->fileDesc(), recordTypeCStr
);
319 MPDebug("MDS file %s has unsupported record type %s",
320 mdsDict
->fileDesc(), recordTypeCStr
);
321 delete [] recordTypeCStr
;
324 MPDebug("Parsing MDS file %s, recordType %s", mdsDict
->fileDesc(), recordTypeCStr
);
325 delete [] recordTypeCStr
;
327 /* handle special cases here */
328 switch(relationInfo
->DataRecordType
) {
329 case MDS_CDSADIR_CSP_CAPABILITY_RECORDTYPE
:
330 parseCspCapabilitiesRecord(mdsDict
);
332 case MDS_CDSADIR_TP_OIDS_RECORDTYPE
:
333 parseTpPolicyOidsRecord(mdsDict
);
336 /* all (normal) linear schema */
337 parseMdsRecord(mdsDict
, relationInfo
, mCdsaDirHand
);
343 * Given an open MDSDictionary, create an MDS_OBJECT_RECORDTYPE record and
344 * add it to mObjectHand. Used when parsing both CSSM records and MOduleCommon
347 void MDSAttrParser::parseObjectRecord(
348 MDSDictionary
*mdsDict
)
350 assert(mdsDict
!= NULL
);
351 assert(mObjectHand
!= 0);
352 parseMdsRecord(mdsDict
, &kObjectRelation
, mObjectHand
);
357 * Given an open dictionary and a RelationInfo defining a schema, fetch all
358 * attributes associated with the specified schema from the dictionary
359 * and write them to specified DB.
361 void MDSAttrParser::parseMdsRecord(
362 MDSDictionary
*mdsDict
,
363 const RelationInfo
*relInfo
,
364 CSSM_DB_HANDLE dbHand
)
366 assert(mdsDict
!= NULL
);
367 assert(relInfo
!= NULL
);
371 * malloc an CSSM_DB_ATTRIBUTE_DATA array associated with specified schema.
373 unsigned numSchemaAttrs
= relInfo
->NumberOfAttributes
;
374 CSSM_DB_ATTRIBUTE_DATA
*dbAttrs
= new CSSM_DB_ATTRIBUTE_DATA
[numSchemaAttrs
];
377 * Grind thru the attributes in the specified schema. Do not assume the presence
378 * of any given attribute.
380 uint32 foundAttrs
= 0;
381 mdsDict
->lookupAttributes(relInfo
, dbAttrs
, foundAttrs
);
383 /* write to the DB */
384 MDSInsertRecord(dbAttrs
, foundAttrs
, relInfo
->DataRecordType
, mDl
, dbHand
);
386 MDSFreeDbRecordAttrs(dbAttrs
, foundAttrs
);
391 * Parse CSP capabilities. This is much more complicated than most records.
392 * The propertly list (*.mdsinfo) is set up like this:
397 * Capabilities(Array) {
398 * index 0(Dictionary) {
399 * AlgType(String) -- CSSM_ALGID_SHA1
400 * ContextType(String) -- CSSM_ALGCLASS_DIGEST
401 * UseeTag(String) -- CSSM_USEE_NONE
402 * Description(String) -- "SHA1 Digest"
404 * index 0(Dictionary)
405 * AttributeType(String) -- CSSM_ATTRIBUTE_OUTPUT_SIZE
406 * AttributeValue(Array) {
407 * index 0(Number) -- 20
416 * The plist can specify multiple Capabilities, multiple Attributes for each
417 * Capability, and multiple values for each Attribute. (Note that MULTI_UINT32
418 * in the DB is represented in the plist as an Array of Numbers.) Each element
419 * of each Attributes array maps to one record in the DB. The GroupID attribute
420 * of a record is the index into the plist's Capabilities array.
422 void MDSAttrParser::parseCspCapabilitiesRecord(
423 MDSDictionary
*mdsDict
)
426 * Malloc an attribute array big enough for the whole schema. We're going
427 * to re-use this array every time we write a new record. Portions of
428 * the array are invariant for some inner loops.
430 const RelationInfo
*topRelInfo
=
431 MDSRecordTypeToRelation(MDS_CDSADIR_CSP_CAPABILITY_RECORDTYPE
);
432 assert(topRelInfo
!= NULL
);
433 uint32 numInAttrs
= topRelInfo
->NumberOfAttributes
;
434 CSSM_DB_ATTRIBUTE_DATA_PTR outAttrs
= new CSSM_DB_ATTRIBUTE_DATA
[numInAttrs
];
436 /* these attrs are only set once, then they remain invariant */
437 uint32 numTopLevelAttrs
;
438 mdsDict
->lookupAttributes(&CSPCapabilitiesDict1RelInfo
, outAttrs
,
441 bool fetchedFromDisk
= false;
443 /* obtain Capabilities array */
444 CFArrayRef capArray
= (CFArrayRef
)mdsDict
->lookupWithIndirect("Capabilities",
448 if(capArray
== NULL
) {
449 /* well we did not get very far.... */
450 MPDebug("parseCspCapabilitiesRecord: no (or bad) Capabilities");
456 * Descend into Capabilities array. Each element is a dictionary defined
457 * by CSPCapabilitiesDict2RelInfo.
459 CFIndex capArraySize
= CFArrayGetCount(capArray
);
461 for(capDex
=0; capDex
<capArraySize
; capDex
++) {
462 MPDebug("...parsing Capability %d", (int)capDex
);
463 CFDictionaryRef capDict
=
464 (CFDictionaryRef
)CFArrayGetValueAtIndex(capArray
, capDex
);
465 if((capDict
== NULL
) ||
466 (CFGetTypeID(capDict
) != CFDictionaryGetTypeID())) {
467 MPDebug("parseCspCapabilitiesRecord: bad Capabilities element");
470 MDSDictionary
capDictMds(capDict
);
473 * Append this dictionary's attributes to outAttrs, after the fixed
474 * attributes from CSPCapabilitiesDict1RelInfo.
476 uint32 numCapDictAttrs
;
477 capDictMds
.lookupAttributes(&CSPCapabilitiesDict2RelInfo
,
478 &outAttrs
[numTopLevelAttrs
],
482 * Append the GroupId attribute, which we infer from the current index
483 * into Capabilitites. However, thou shalt not use > 4-byte values
484 * for MDS, so convert from CFIndex first.
486 if (capDex
> uint32(~0)) {
487 MPDebug("parseCspCapabilitiesRecord: too large an index for MDS");
490 uint32 index32
= uint32(capDex
);
491 MDSRawValueToDbAttr(&index32
, sizeof(index32
), CSSM_DB_ATTRIBUTE_FORMAT_UINT32
,
492 "GroupId", outAttrs
[numTopLevelAttrs
+ numCapDictAttrs
]);
496 * Now descend into the array of this capability's attributes.
497 * Each element is a dictionary defined by
498 * by CSPCapabilitiesDict3RelInfo.
500 CFArrayRef attrArray
= (CFArrayRef
)capDictMds
.lookup("Attributes",
501 true, CFArrayGetTypeID());
502 if(attrArray
== NULL
) {
503 MPDebug("parseCspCapabilitiesRecord: no (or bad) Attributes");
506 CFIndex attrArraySize
= CFArrayGetCount(attrArray
);
508 for(attrDex
=0; attrDex
<attrArraySize
; attrDex
++) {
509 MPDebug(" ...parsing Attribute %d", (int)attrDex
);
510 CFDictionaryRef attrDict
=
511 (CFDictionaryRef
)CFArrayGetValueAtIndex(attrArray
, attrDex
);
512 if((attrDict
== NULL
) ||
513 (CFGetTypeID(attrDict
) != CFDictionaryGetTypeID())) {
514 MPDebug("parseCspCapabilitiesRecord: bad Attributes element");
517 MDSDictionary
attrDictMds(attrDict
);
520 * Append this dictionary's attributes to outAttrs, after the fixed
521 * attributes from CSPCapabilitiesDict1RelInfo and this capability's
522 * CSPCapabilitiesDict2RelInfo.
524 uint32 numAttrDictAttrs
;
525 attrDictMds
.lookupAttributes(&CSPCapabilitiesDict3RelInfo
,
526 &outAttrs
[numTopLevelAttrs
+ numCapDictAttrs
],
530 MDSInsertRecord(outAttrs
,
531 numTopLevelAttrs
+ numCapDictAttrs
+ numAttrDictAttrs
,
532 MDS_CDSADIR_CSP_CAPABILITY_RECORDTYPE
,
536 /* just free the attrs we allocated in this loop */
537 MDSFreeDbRecordAttrs(&outAttrs
[numTopLevelAttrs
+ numCapDictAttrs
],
539 } /* for each attribute */
540 /* just free the attrs we allocated in this loop */
541 MDSFreeDbRecordAttrs(&outAttrs
[numTopLevelAttrs
], numCapDictAttrs
);
542 } /* for each capability */
544 MDSFreeDbRecordAttrs(outAttrs
, numTopLevelAttrs
);
546 if(fetchedFromDisk
) {
547 CF_RELEASE(capArray
);
552 * Parse TP Policy OIDs.
553 * The propertly list (*.mdsinfo) is set up like this:
559 * index 0(Dictionary) {
560 * OID(Data) -- <092a8648 86f76364 0102>
561 * Value(Data) -- optional, OID-specific
566 * The plist can specify multiple Policies. Each element of the Policies
567 * array maps to one record in the DB.
569 void MDSAttrParser::parseTpPolicyOidsRecord(
570 MDSDictionary
*mdsDict
)
573 * Malloc an attribute array big enough for the whole schema. We're going
574 * to re-use this array every time we write a new record. Portions of
575 * the array are invariant for some inner loops.
577 const RelationInfo
*topRelInfo
=
578 MDSRecordTypeToRelation(MDS_CDSADIR_TP_OIDS_RECORDTYPE
);
579 assert(topRelInfo
!= NULL
);
580 uint32 numInAttrs
= topRelInfo
->NumberOfAttributes
;
581 CSSM_DB_ATTRIBUTE_DATA_PTR outAttrs
= new CSSM_DB_ATTRIBUTE_DATA
[numInAttrs
];
583 /* these attrs are only set once, then they remain invariant */
584 uint32 numTopLevelAttrs
;
585 mdsDict
->lookupAttributes(&TpPolicyOidsDict1RelInfo
, outAttrs
,
588 /* obtain Policies array */
589 CFArrayRef policyArray
= (CFArrayRef
)mdsDict
->lookup("Policies",
590 true, CFArrayGetTypeID());
591 if(policyArray
== NULL
) {
592 /* well we did not get very far.... */
593 MPDebug("parseTpPolicyOidsRecord: no (or bad) Policies");
599 * Descend into Policies array. Each element is a dictionary defined
600 * by TpPolicyOidsDict2RelInfo.
602 CFIndex policyArraySize
= CFArrayGetCount(policyArray
);
604 for(policyDex
=0; policyDex
<policyArraySize
; policyDex
++) {
605 MPDebug("...parsing Policy %d", (int)policyDex
);
606 CFDictionaryRef policyDict
=
607 (CFDictionaryRef
)CFArrayGetValueAtIndex(policyArray
, policyDex
);
608 if((policyDict
== NULL
) ||
609 (CFGetTypeID(policyDict
) != CFDictionaryGetTypeID())) {
610 MPDebug("parseTpPolicyOidsRecord: bad Policies element");
613 MDSDictionary
policyDictMds(policyDict
);
616 * Append this dictionary's attributes to outAttrs, after the fixed
617 * attributes from TpPolicyOidsDict1RelInfo.
619 uint32 numPolicyDictAttrs
;
620 policyDictMds
.lookupAttributes(&TpPolicyOidsDict2RelInfo
,
621 &outAttrs
[numTopLevelAttrs
],
626 MDSInsertRecord(outAttrs
,
627 numTopLevelAttrs
+ numPolicyDictAttrs
,
628 MDS_CDSADIR_TP_OIDS_RECORDTYPE
,
632 /* free the attrs allocated in this loop */
633 MDSFreeDbRecordAttrs(outAttrs
+ numTopLevelAttrs
, numPolicyDictAttrs
);
634 } /* for each policy */
635 MDSFreeDbRecordAttrs(outAttrs
, numTopLevelAttrs
);
640 } // end namespace Security