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:      MDSAttrParser.cpp
21
22   Contains:  Classes to parse XML plists and fill in MDS DBs with the
23              attributes found there.
24
25   Copyright: (c) 2001 Apple Computer, Inc., all rights reserved.
26*/
27
28#include "MDSAttrParser.h"
29#include "MDSAttrUtils.h"
30#include "MDSDictionary.h"
31#include <security_utilities/logging.h>
32#include <Security/mds_schema.h>
33
34namespace Security
35{
36
37MDSAttrParser::MDSAttrParser(
38	const char *bundlePath,
39	MDSSession &dl,
40	CSSM_DB_HANDLE objectHand,
41	CSSM_DB_HANDLE cdsaDirHand) :
42		mBundle(NULL),
43		mPath(NULL),
44		mDl(dl),
45		mObjectHand(objectHand),
46		mCdsaDirHand(cdsaDirHand),
47		mGuid(NULL),
48		mDefaults(NULL)
49{
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,
54		pathLen,
55		false);
56	if(url == NULL) {
57		Syslog::alert("CFURLCreateFromFileSystemRepresentation(%s) failure", mPath);
58		CssmError::throwMe(CSSMERR_DL_INVALID_DB_NAME);
59	}
60
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);
64	CFRelease(url);
65	if(mBundle == NULL) {
66		Syslog::alert("CFBundleCreate(%s) failure", mPath);
67		CssmError::throwMe(CSSMERR_DL_INVALID_DB_NAME);
68	}
69	mPath = new char[pathLen + 1];
70	strcpy(mPath, bundlePath);
71}
72
73MDSAttrParser::~MDSAttrParser()
74{
75	CF_RELEASE(mBundle);
76	delete [] mPath;
77	delete [] mGuid;
78}
79
80/*********************
81 Main public function.
82
83Parsing bundle {
84	get all *.mdsinfo files;
85	for each mdsinfo {
86		get contents of that file as dictionary;
87		switch (ModuleType) {
88		case CSSM:
89			parse this mdsinfo --> MDS_OBJECT_RECORDTYPE, MDS_CDSADIR_CSSM_RECORDTYPE;
90			break;
91		case Plugin:
92			parse this info --> MDS_OBJECT_RECORDTYPE, MDS_CDSADIR_COMMON_RECORDTYPE;
93		case PluginInfo:
94			recordType = lookup("MdsRecordType");
95			dispatch to recordtype-specific parsing;
96		}
97	}
98}
99************/
100
101void MDSAttrParser::parseAttrs(CFStringRef subdir)
102{
103	/* get all *.mdsinfo files */
104	CFArrayRef bundleInfoFiles = CFBundleCopyResourceURLsOfType(mBundle,
105		CFSTR(MDS_INFO_TYPE),
106		subdir);
107	if(bundleInfoFiles == NULL) {
108		Syslog::alert("MDSAttrParser: no mdsattr files for %s", mPath);
109		return;
110	}
111	assert(CFGetTypeID(bundleInfoFiles) == CFArrayGetTypeID());
112
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;
118
119		infoUrl = reinterpret_cast<CFURLRef>(
120			CFArrayGetValueAtIndex(bundleInfoFiles, i));
121		if(infoUrl == NULL) {
122			MPDebug("MDSAttrParser: CFBundleCopyResourceURLsOfType screwup 1");
123			continue;
124		}
125		if(CFGetTypeID(infoUrl) != CFURLGetTypeID()) {
126			MPDebug("MDSAttrParser: CFBundleCopyResourceURLsOfType screwup 2");
127			continue;
128		}
129
130		// @@@  Workaround for 4234967: skip any filename beginning with "._"
131		CFStringRef lastComponent = CFURLCopyLastPathComponent(infoUrl);
132		if (lastComponent) {
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,
139												   resFilePfx,
140												   range,
141												   0/*options*/,
142												   NULL/*returned substr*/);
143			CFRelease(lastComponent);
144			if (skip == true) {
145				Syslog::warning("MDSAttrParser: ignoring resource file");
146				continue;
147			}
148		}
149
150		parseFile(infoUrl, subdir);
151	} /* for each mdsinfo */
152	CF_RELEASE(bundleInfoFiles);
153}
154
155void MDSAttrParser::parseFile(CFURLRef infoUrl, CFStringRef subdir)
156{
157	CFStringRef infoType = NULL;
158
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);
163	if (mGuid == NULL) {
164		CFStringRef guid = (CFStringRef)mdsDict.lookup("ModuleID", true, CFStringGetTypeID());
165		if (guid) {
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);
170				delete [] mGuid;
171				mGuid = NULL;
172				CssmError::throwMe(CSSM_ERRCODE_MDS_ERROR);
173			}
174		}
175		else {
176			logFileError("No GUID associated with plugin?", infoUrl, NULL, NULL);
177			CssmError::throwMe(CSSM_ERRCODE_MDS_ERROR);
178		}
179	}
180
181	MPDebug("Parsing mdsinfo file %s", mdsDict.fileDesc());
182
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);
189	}
190
191	/* be robust here, errors in these low-level routines do not affect
192	 * the rest of our task */
193	try {
194		if(CFStringCompare(infoType, CFSTR(MDS_INFO_FILE_TYPE_CSSM), 0)
195				== kCFCompareEqualTo) {
196			parseCssmInfo(&mdsDict);
197		}
198		else if(CFStringCompare(infoType, CFSTR(MDS_INFO_FILE_TYPE_PLUGIN), 0)
199				== kCFCompareEqualTo) {
200			parsePluginCommon(&mdsDict);
201		}
202		else if(CFStringCompare(infoType, CFSTR(MDS_INFO_FILE_TYPE_RECORD), 0)
203				== kCFCompareEqualTo) {
204			parsePluginSpecific(&mdsDict);
205		}
206		else {
207			logFileError("Malformed MDS Info file", infoUrl, NULL, NULL);
208		}
209	}
210	catch(...) {
211
212	}
213}
214
215void MDSAttrParser::logFileError(
216	const char *op,
217	CFURLRef fileUrl,
218	CFStringRef errStr,		// optional if you have it
219	SInt32 *errNo)			// optional if you have it
220{
221	CFStringRef urlStr = CFURLGetString(fileUrl);
222	const char *cUrlStr = CFStringGetCStringPtr(urlStr, kCFStringEncodingUTF8);
223	char* stringBuffer = NULL;
224
225    if (cUrlStr == NULL)
226    {
227        CFIndex maxLen = CFStringGetMaximumSizeForEncoding(CFStringGetLength(urlStr), kCFStringEncodingUTF8) + 1;
228        stringBuffer = (char*) malloc(maxLen);
229        CFStringGetCString(urlStr, stringBuffer, maxLen, kCFStringEncodingUTF8);
230        cUrlStr = stringBuffer;
231    }
232
233	if(errStr) {
234        const char *cerrStr = CFStringGetCStringPtr(errStr, kCFStringEncodingUTF8);
235        char* sbuf2 = NULL;
236
237        if (cerrStr == NULL)
238        {
239            CFIndex maxLen = CFStringGetMaximumSizeForEncoding(CFStringGetLength(errStr), kCFStringEncodingUTF8) + 1;
240            sbuf2 = (char*) malloc(maxLen);
241            CFStringGetCString(urlStr, sbuf2, maxLen, kCFStringEncodingUTF8);
242            cUrlStr = sbuf2;
243        }
244
245		Syslog::alert("MDS: %s: bundle %s url %s: error %s",
246			op, mPath, cUrlStr, cerrStr);
247
248        if (sbuf2 != NULL)
249        {
250            free(sbuf2);
251        }
252	}
253	else {
254		Syslog::alert("MDS: %s: bundle %s url %s: error %d",
255			op, mPath, cUrlStr, (int)(errNo ? *errNo : 0));
256	}
257
258    if (stringBuffer != NULL)
259    {
260        free(stringBuffer);
261    }
262}
263
264/*
265 * Parse a CSSM info file.
266 */
267void MDSAttrParser::parseCssmInfo(
268	MDSDictionary *mdsDict)
269{
270	/* first get object info */
271	parseObjectRecord(mdsDict);
272
273	/* now CSSM relation */
274	const RelationInfo *relationInfo =
275		MDSRecordTypeToRelation(MDS_CDSADIR_CSSM_RECORDTYPE);
276	assert(relationInfo != NULL);
277	parseMdsRecord(mdsDict, relationInfo, mCdsaDirHand);
278}
279
280/*
281 * Parse a PluginCommon file.
282 */
283void MDSAttrParser::parsePluginCommon(
284	MDSDictionary *mdsDict)
285{
286
287	/* first get object info */
288	parseObjectRecord(mdsDict);
289
290	/* now common relation */
291	const RelationInfo *relationInfo =
292		MDSRecordTypeToRelation(MDS_CDSADIR_COMMON_RECORDTYPE);
293	assert(relationInfo != NULL);
294	parseMdsRecord(mdsDict, relationInfo, mCdsaDirHand);
295}
296
297/*
298 * Parse a Plugin Specific file.
299 */
300void MDSAttrParser::parsePluginSpecific(
301	MDSDictionary *mdsDict)
302{
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);
310		return;
311	}
312
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;
322		return;
323	}
324	MPDebug("Parsing MDS file %s, recordType %s", mdsDict->fileDesc(), recordTypeCStr);
325	delete [] recordTypeCStr;
326
327	/* handle special cases here */
328	switch(relationInfo->DataRecordType) {
329		case MDS_CDSADIR_CSP_CAPABILITY_RECORDTYPE:
330			parseCspCapabilitiesRecord(mdsDict);
331			break;
332		case MDS_CDSADIR_TP_OIDS_RECORDTYPE:
333			parseTpPolicyOidsRecord(mdsDict);
334			break;
335		default:
336			/* all (normal) linear schema */
337			parseMdsRecord(mdsDict, relationInfo, mCdsaDirHand);
338	}
339}
340
341
342/*
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
345 * records.
346 */
347void MDSAttrParser::parseObjectRecord(
348	MDSDictionary *mdsDict)
349{
350	assert(mdsDict != NULL);
351	assert(mObjectHand != 0);
352	parseMdsRecord(mdsDict, &kObjectRelation, mObjectHand);
353
354}
355
356/*
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.
360 */
361void MDSAttrParser::parseMdsRecord(
362	MDSDictionary 				*mdsDict,
363	const RelationInfo 			*relInfo,
364	CSSM_DB_HANDLE				dbHand)
365{
366	assert(mdsDict != NULL);
367	assert(relInfo != NULL);
368	assert(dbHand != 0);
369
370	/*
371	 * malloc an CSSM_DB_ATTRIBUTE_DATA array associated with specified schema.
372	 */
373	unsigned numSchemaAttrs = relInfo->NumberOfAttributes;
374	CSSM_DB_ATTRIBUTE_DATA *dbAttrs = new CSSM_DB_ATTRIBUTE_DATA[numSchemaAttrs];
375
376	/*
377	 * Grind thru the attributes in the specified schema. Do not assume the presence
378	 * of any given attribute.
379	 */
380	uint32 foundAttrs = 0;
381	mdsDict->lookupAttributes(relInfo, dbAttrs, foundAttrs);
382
383	/* write to the DB */
384	MDSInsertRecord(dbAttrs, foundAttrs, relInfo->DataRecordType, mDl, dbHand);
385
386	MDSFreeDbRecordAttrs(dbAttrs, foundAttrs);
387	delete [] dbAttrs;
388}
389
390/*
391 * Parse CSP capabilities. This is much more complicated than most records.
392 * The propertly list (*.mdsinfo) is set up like this:
393 *
394 * root(Dictionary) {
395 *    ModuleID(String)
396 *    SSID(Number)
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"
403 *           Attributes(Array)
404 *              index 0(Dictionary)
405 *                 AttributeType(String) 		-- CSSM_ATTRIBUTE_OUTPUT_SIZE
406 *                 AttributeValue(Array) {
407 *                    index 0(Number)			-- 20
408 *                    ...
409 *                 }
410 *              index n ...
411 *           }
412 *       index n...
413 *    }
414 * }
415 *
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.
421 */
422void MDSAttrParser::parseCspCapabilitiesRecord(
423	MDSDictionary *mdsDict)
424{
425	/*
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.
429	 */
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];
435
436	/* these attrs are only set once, then they remain invariant */
437	uint32 numTopLevelAttrs;
438	mdsDict->lookupAttributes(&CSPCapabilitiesDict1RelInfo, outAttrs,
439		numTopLevelAttrs);
440
441	bool fetchedFromDisk = false;
442
443	/* obtain Capabilities array */
444	CFArrayRef capArray = (CFArrayRef)mdsDict->lookupWithIndirect("Capabilities",
445		mBundle,
446		CFArrayGetTypeID(),
447		fetchedFromDisk);
448	if(capArray == NULL) {
449		/* well we did not get very far.... */
450		MPDebug("parseCspCapabilitiesRecord: no (or bad) Capabilities");
451		delete [] outAttrs;
452		return;
453	}
454
455	/*
456	 * Descend into Capabilities array. Each element is a dictionary defined
457	 * by CSPCapabilitiesDict2RelInfo.
458	 */
459	CFIndex capArraySize = CFArrayGetCount(capArray);
460	CFIndex capDex;
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");
468			break;
469		}
470		MDSDictionary capDictMds(capDict);
471
472		/*
473		 * Append this dictionary's attributes to outAttrs, after the fixed
474		 * attributes from CSPCapabilitiesDict1RelInfo.
475		 */
476		uint32 numCapDictAttrs;
477		capDictMds.lookupAttributes(&CSPCapabilitiesDict2RelInfo,
478			&outAttrs[numTopLevelAttrs],
479			numCapDictAttrs);
480
481		/*
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.
485		 */
486        if (capDex > uint32(~0)) {
487            MPDebug("parseCspCapabilitiesRecord: too large an index for MDS");
488            break;
489        }
490        uint32 index32 = uint32(capDex);
491		MDSRawValueToDbAttr(&index32, sizeof(index32), CSSM_DB_ATTRIBUTE_FORMAT_UINT32,
492			"GroupId", outAttrs[numTopLevelAttrs + numCapDictAttrs]);
493		numCapDictAttrs++;
494
495		/*
496		 * Now descend into the array of this capability's attributes.
497		 * Each element is a dictionary defined by
498		 * by CSPCapabilitiesDict3RelInfo.
499		 */
500		CFArrayRef attrArray = (CFArrayRef)capDictMds.lookup("Attributes",
501			true, CFArrayGetTypeID());
502		if(attrArray == NULL) {
503			MPDebug("parseCspCapabilitiesRecord: no (or bad) Attributes");
504			break;
505		}
506		CFIndex attrArraySize = CFArrayGetCount(attrArray);
507		CFIndex attrDex;
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");
515				break;
516			}
517			MDSDictionary attrDictMds(attrDict);
518
519			/*
520			 * Append this dictionary's attributes to outAttrs, after the fixed
521			 * attributes from CSPCapabilitiesDict1RelInfo and this capability's
522			 * CSPCapabilitiesDict2RelInfo.
523			 */
524			uint32 numAttrDictAttrs;
525			attrDictMds.lookupAttributes(&CSPCapabilitiesDict3RelInfo,
526				&outAttrs[numTopLevelAttrs + numCapDictAttrs],
527				numAttrDictAttrs);
528
529			/* write to DB */
530			MDSInsertRecord(outAttrs,
531				numTopLevelAttrs + numCapDictAttrs + numAttrDictAttrs,
532				MDS_CDSADIR_CSP_CAPABILITY_RECORDTYPE,
533				mDl,
534				mCdsaDirHand);
535
536			/* just free the attrs we allocated in this loop */
537			MDSFreeDbRecordAttrs(&outAttrs[numTopLevelAttrs + numCapDictAttrs],
538				numAttrDictAttrs);
539		} 	/* for each attribute */
540		/* just free the attrs we allocated in this loop */
541		MDSFreeDbRecordAttrs(&outAttrs[numTopLevelAttrs], numCapDictAttrs);
542	}		/* for each capability */
543
544	MDSFreeDbRecordAttrs(outAttrs, numTopLevelAttrs);
545	delete [] outAttrs;
546	if(fetchedFromDisk) {
547		CF_RELEASE(capArray);
548	}
549}
550
551/*
552 * Parse TP Policy OIDs.
553 * The propertly list (*.mdsinfo) is set up like this:
554 *
555 * root(Dictionary) {
556 *    ModuleID(String)
557 *    SSID(Number)
558 *    Policies(Array) {
559 *       index 0(Dictionary) {
560 *           OID(Data)							-- <092a8648 86f76364 0102>
561 *           Value(Data)						-- optional, OID-specific
562 *       index n...
563 *    }
564 * }
565 *
566 * The plist can specify multiple Policies. Each element of the Policies
567 * array maps to one record in the DB.
568 */
569void MDSAttrParser::parseTpPolicyOidsRecord(
570	MDSDictionary *mdsDict)
571{
572	/*
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.
576	 */
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];
582
583	/* these attrs are only set once, then they remain invariant */
584	uint32 numTopLevelAttrs;
585	mdsDict->lookupAttributes(&TpPolicyOidsDict1RelInfo, outAttrs,
586		numTopLevelAttrs);
587
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");
594		delete [] outAttrs;
595		return;
596	}
597
598	/*
599	 * Descend into Policies array. Each element is a dictionary defined
600	 * by TpPolicyOidsDict2RelInfo.
601	 */
602	CFIndex policyArraySize = CFArrayGetCount(policyArray);
603	CFIndex policyDex;
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");
611			break;
612		}
613		MDSDictionary policyDictMds(policyDict);
614
615		/*
616		 * Append this dictionary's attributes to outAttrs, after the fixed
617		 * attributes from TpPolicyOidsDict1RelInfo.
618		 */
619		uint32 numPolicyDictAttrs;
620		policyDictMds.lookupAttributes(&TpPolicyOidsDict2RelInfo,
621			&outAttrs[numTopLevelAttrs],
622			numPolicyDictAttrs);
623
624
625		/* write to DB */
626		MDSInsertRecord(outAttrs,
627			numTopLevelAttrs + numPolicyDictAttrs,
628			MDS_CDSADIR_TP_OIDS_RECORDTYPE,
629			mDl,
630			mCdsaDirHand);
631
632		/* free the attrs allocated in this loop */
633		MDSFreeDbRecordAttrs(outAttrs + numTopLevelAttrs, numPolicyDictAttrs);
634	}		/* for each policy */
635	MDSFreeDbRecordAttrs(outAttrs, numTopLevelAttrs);
636	delete [] outAttrs;
637}
638
639
640} // end namespace Security
641