]> git.saurik.com Git - apple/security.git/blobdiff - libsecurity_mds/lib/MDSAttrParser.cpp
Security-55163.44.tar.gz
[apple/security.git] / libsecurity_mds / lib / MDSAttrParser.cpp
diff --git a/libsecurity_mds/lib/MDSAttrParser.cpp b/libsecurity_mds/lib/MDSAttrParser.cpp
new file mode 100644 (file)
index 0000000..5cc0f6e
--- /dev/null
@@ -0,0 +1,612 @@
+/*
+ * Copyright (c) 2000-2001 Apple Computer, Inc. All Rights Reserved.
+ * 
+ * The contents of this file constitute Original Code as defined in and are
+ * subject to the Apple Public Source License Version 1.2 (the 'License').
+ * You may not use this file except in compliance with the License. Please obtain
+ * a copy of the License at http://www.apple.com/publicsource and read it before
+ * using this file.
+ * 
+ * This Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS
+ * OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, INCLUDING WITHOUT
+ * LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
+ * PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. Please see the License for the
+ * specific language governing rights and limitations under the License.
+ */
+
+
+/*
+   File:      MDSAttrParser.cpp
+
+   Contains:  Classes to parse XML plists and fill in MDS DBs with the
+              attributes found there.  
+
+   Copyright: (c) 2001 Apple Computer, Inc., all rights reserved.
+*/
+
+#include "MDSAttrParser.h"
+#include "MDSAttrUtils.h"
+#include "MDSDictionary.h"
+#include <security_utilities/logging.h>
+#include <Security/mds_schema.h>
+
+namespace Security
+{
+
+MDSAttrParser::MDSAttrParser(
+       const char *bundlePath,
+       MDSSession &dl,
+       CSSM_DB_HANDLE objectHand,
+       CSSM_DB_HANDLE cdsaDirHand) :
+               mBundle(NULL),
+               mPath(NULL),
+               mDl(dl),
+               mObjectHand(objectHand),
+               mCdsaDirHand(cdsaDirHand),
+               mGuid(NULL),
+               mDefaults(NULL)
+{
+       /* Only task here is to cook up a CFBundle for the specified path */
+       unsigned pathLen = strlen(bundlePath);
+       CFURLRef url = CFURLCreateFromFileSystemRepresentation(NULL,
+               (unsigned char *)bundlePath,
+               pathLen,
+               false);
+       if(url == NULL) {
+               Syslog::alert("CFURLCreateFromFileSystemRepresentation(%s) failure", mPath);
+               CssmError::throwMe(CSSMERR_DL_INVALID_DB_NAME);
+       }
+       
+       /* FIXME - this leaks 28 bytes each time thru, even though we CFRelease the
+        * mBundle in out destructor. I think this is a CF leak. */
+       mBundle = CFBundleCreate(NULL, url);
+       CFRelease(url);
+       if(mBundle == NULL) {
+               Syslog::alert("CFBundleCreate(%s) failure", mPath);
+               CssmError::throwMe(CSSMERR_DL_INVALID_DB_NAME);
+       }
+       mPath = new char[pathLen + 1];
+       strcpy(mPath, bundlePath);
+}
+
+MDSAttrParser::~MDSAttrParser()
+{
+       CF_RELEASE(mBundle);
+       delete [] mPath;
+       delete [] mGuid;
+}
+
+/*********************
+ Main public function.
+
+Parsing bundle {
+       get all *.mdsinfo files;
+       for each mdsinfo {
+               get contents of that file as dictionary;
+               switch (ModuleType) {
+               case CSSM:
+                       parse this mdsinfo --> MDS_OBJECT_RECORDTYPE, MDS_CDSADIR_CSSM_RECORDTYPE;
+                       break;
+               case Plugin:
+                       parse this info --> MDS_OBJECT_RECORDTYPE, MDS_CDSADIR_COMMON_RECORDTYPE;
+               case PluginInfo:
+                       recordType = lookup("MdsRecordType");
+                       dispatch to recordtype-specific parsing;
+               }
+       }
+}
+************/ 
+
+void MDSAttrParser::parseAttrs(CFStringRef subdir)
+{
+       /* get all *.mdsinfo files */
+       CFArrayRef bundleInfoFiles = CFBundleCopyResourceURLsOfType(mBundle,
+               CFSTR(MDS_INFO_TYPE),
+               subdir);
+       if(bundleInfoFiles == NULL) {
+               Syslog::alert("MDSAttrParser: no mdsattr files for %s", mPath);
+               return;
+       }
+       assert(CFGetTypeID(bundleInfoFiles) == CFArrayGetTypeID());
+       
+       /* process each .mdsinfo file */
+       CFIndex numFiles = CFArrayGetCount(bundleInfoFiles);
+       for(CFIndex i=0; i<numFiles; i++) {
+               /* get filename as CFURL */
+               CFURLRef infoUrl = NULL;
+               
+               infoUrl = reinterpret_cast<CFURLRef>(
+                       CFArrayGetValueAtIndex(bundleInfoFiles, i));
+               if(infoUrl == NULL) {
+                       MPDebug("MDSAttrParser: CFBundleCopyResourceURLsOfType screwup 1");
+                       continue;
+               }
+               if(CFGetTypeID(infoUrl) != CFURLGetTypeID()) {
+                       MPDebug("MDSAttrParser: CFBundleCopyResourceURLsOfType screwup 2");
+                       continue;
+               }
+               
+               // @@@  Workaround for 4234967: skip any filename beginning with "._"
+               CFStringRef lastComponent = CFURLCopyLastPathComponent(infoUrl);
+               if (lastComponent) {
+                       CFStringRef resFilePfx = CFSTR("._");
+                       // setting the search length and location like this permits, 
+                       // e.g., ".foo.mdsinfo" to be valid
+                       CFIndex resFilePfxLen = CFStringGetMaximumSizeForEncoding(CFStringGetLength(resFilePfx), kCFStringEncodingUTF8);
+                       CFRange range = CFRangeMake(0, resFilePfxLen);
+                       Boolean skip = CFStringFindWithOptions(lastComponent, 
+                                                                                                  resFilePfx, 
+                                                                                                  range,
+                                                                                                  0/*options*/,
+                                                                                                  NULL/*returned substr*/);
+                       CFRelease(lastComponent);
+                       if (skip == true) {
+                               Syslog::warning("MDSAttrParser: ignoring resource file");
+                               continue;
+                       }
+               }
+               
+               parseFile(infoUrl, subdir);
+       } /* for each mdsinfo */
+       CF_RELEASE(bundleInfoFiles);
+}
+
+void MDSAttrParser::parseFile(CFURLRef infoUrl, CFStringRef subdir)
+{
+       CFStringRef infoType = NULL;
+       
+       /* Get contents of mdsinfo file as dictionary */
+       MDSDictionary mdsDict(infoUrl, subdir, mPath);
+       /* Make sure we set all possible MDS values before checking for GUID */
+       mdsDict.setDefaults(mDefaults);
+       if (mGuid == NULL) {
+               CFStringRef guid = (CFStringRef)mdsDict.lookup("ModuleID", true, CFStringGetTypeID());
+               if (guid) {
+                       CFIndex copylen = CFStringGetMaximumSizeForEncoding(CFStringGetLength(guid), kCFStringEncodingUTF8) + 1/*nul terminator*/;
+                       mGuid = new char[copylen];
+                       if (false == CFStringGetCString(guid, mGuid, copylen, kCFStringEncodingUTF8)) {
+                               logFileError("Error copying GUID", infoUrl, NULL, NULL);
+                               delete [] mGuid;
+                               mGuid = NULL;
+                               CssmError::throwMe(CSSM_ERRCODE_MDS_ERROR);
+                       }
+               }
+               else {
+                       logFileError("No GUID associated with plugin?", infoUrl, NULL, NULL);
+                       CssmError::throwMe(CSSM_ERRCODE_MDS_ERROR);
+               }
+       }
+       
+       MPDebug("Parsing mdsinfo file %s", mdsDict.fileDesc());
+       
+       /* Determine what kind of info file this is and dispatch accordingly */
+       infoType = (CFStringRef)mdsDict.lookup(CFSTR(MDS_INFO_FILE_TYPE),
+               true, CFStringGetTypeID());
+       if(infoType == NULL) {
+               logFileError("Malformed MDS Info file", infoUrl, NULL, NULL);
+               CssmError::throwMe(CSSM_ERRCODE_MDS_ERROR);
+       }
+       
+       /* be robust here, errors in these low-level routines do not affect
+        * the rest of our task */
+       try {
+               if(CFStringCompare(infoType, CFSTR(MDS_INFO_FILE_TYPE_CSSM), 0) 
+                               == kCFCompareEqualTo) {
+                       parseCssmInfo(&mdsDict);
+               }
+               else if(CFStringCompare(infoType, CFSTR(MDS_INFO_FILE_TYPE_PLUGIN), 0) 
+                               == kCFCompareEqualTo) {
+                       parsePluginCommon(&mdsDict);
+               }
+               else if(CFStringCompare(infoType, CFSTR(MDS_INFO_FILE_TYPE_RECORD), 0) 
+                               == kCFCompareEqualTo) {
+                       parsePluginSpecific(&mdsDict);
+               }
+               else {
+                       logFileError("Malformed MDS Info file", infoUrl, NULL, NULL);
+               }
+       }
+       catch(...) {
+       
+       }
+}
+
+void MDSAttrParser::logFileError(
+       const char *op,
+       CFURLRef fileUrl,
+       CFStringRef errStr,             // optional if you have it
+       SInt32 *errNo)                  // optional if you have it
+{
+       const char *cerrStr = NULL;
+       CFStringRef urlStr = CFURLGetString(fileUrl);
+       const char *cUrlStr = CFStringGetCStringPtr(urlStr, kCFStringEncodingUTF8);
+       
+       if(errStr) {
+               cerrStr = CFStringGetCStringPtr(errStr, kCFStringEncodingUTF8);
+               Syslog::alert("MDS: %s: bundle %s url %s: error %s",
+                       op, mPath, cUrlStr, cerrStr);
+       }
+       else {
+               Syslog::alert("MDS: %s: bundle %s url %s: error %d",
+                       op, mPath, cUrlStr, errNo ? *errNo : 0);
+       }
+}
+        
+/*
+ * Parse a CSSM info file.
+ */    
+void MDSAttrParser::parseCssmInfo(
+       MDSDictionary *mdsDict)
+{
+       /* first get object info */
+       parseObjectRecord(mdsDict);
+       
+       /* now CSSM relation */
+       const RelationInfo *relationInfo = 
+               MDSRecordTypeToRelation(MDS_CDSADIR_CSSM_RECORDTYPE);
+       assert(relationInfo != NULL);
+       parseMdsRecord(mdsDict, relationInfo, mCdsaDirHand);
+}
+       
+/*
+ * Parse a PluginCommon file.
+ */
+void MDSAttrParser::parsePluginCommon(
+       MDSDictionary *mdsDict)
+{
+       
+       /* first get object info */
+       parseObjectRecord(mdsDict);
+       
+       /* now common relation */
+       const RelationInfo *relationInfo = 
+               MDSRecordTypeToRelation(MDS_CDSADIR_COMMON_RECORDTYPE);
+       assert(relationInfo != NULL);
+       parseMdsRecord(mdsDict, relationInfo, mCdsaDirHand);
+}
+
+/*
+ * Parse a Plugin Specific file.
+ */
+void MDSAttrParser::parsePluginSpecific(
+       MDSDictionary *mdsDict)
+{
+       /* determine record type from the file itself */
+       CFStringRef recordTypeStr = 
+               (CFStringRef)mdsDict->lookup(MDS_INFO_FILE_RECORD_TYPE,
+                       true, CFStringGetTypeID());
+       if(recordTypeStr == NULL) {
+               MPDebug("%s: no %s record found\n", mdsDict->fileDesc(),
+                       MDS_INFO_FILE_RECORD_TYPE);
+               return;
+       }
+
+       /* convert to a known schema */
+       const char *recordTypeCStr = MDSCFStringToCString(recordTypeStr);
+       const RelationInfo *relationInfo = MDSRecordTypeNameToRelation(recordTypeCStr);
+       if(relationInfo == NULL) {
+               Syslog::alert("MDS file %s has unsupported record type %s", 
+                       mdsDict->fileDesc(), recordTypeCStr);
+               MPDebug("MDS file %s has unsupported record type %s", 
+                       mdsDict->fileDesc(), recordTypeCStr);
+               delete [] recordTypeCStr;
+               return;
+       }
+       MPDebug("Parsing MDS file %s, recordType %s", mdsDict->fileDesc(), recordTypeCStr);
+       delete [] recordTypeCStr;
+       
+       /* handle special cases here */
+       switch(relationInfo->DataRecordType) {
+               case MDS_CDSADIR_CSP_CAPABILITY_RECORDTYPE:
+                       parseCspCapabilitiesRecord(mdsDict);
+                       break;
+               case MDS_CDSADIR_TP_OIDS_RECORDTYPE:
+                       parseTpPolicyOidsRecord(mdsDict);
+                       break;
+               default:
+                       /* all (normal) linear schema */
+                       parseMdsRecord(mdsDict, relationInfo, mCdsaDirHand);
+       }
+}
+
+
+/*
+ * Given an open MDSDictionary, create an MDS_OBJECT_RECORDTYPE record and 
+ * add it to mObjectHand. Used when parsing both CSSM records and MOduleCommon
+ * records. 
+ */
+void MDSAttrParser::parseObjectRecord(
+       MDSDictionary *mdsDict)
+{
+       assert(mdsDict != NULL);
+       assert(mObjectHand != 0);
+       parseMdsRecord(mdsDict, &kObjectRelation, mObjectHand);
+       
+}
+
+/*
+ * Given an open dictionary and a RelationInfo defining a schema, fetch all
+ * attributes associated with the specified schema from the dictionary
+ * and write them to specified DB.
+ */
+void MDSAttrParser::parseMdsRecord(
+       MDSDictionary                           *mdsDict,
+       const RelationInfo                      *relInfo,
+       CSSM_DB_HANDLE                          dbHand)
+{
+       assert(mdsDict != NULL);
+       assert(relInfo != NULL);
+       assert(dbHand != 0);
+       
+       /* 
+        * malloc an CSSM_DB_ATTRIBUTE_DATA array associated with specified schema.
+        */
+       unsigned numSchemaAttrs = relInfo->NumberOfAttributes;
+       CSSM_DB_ATTRIBUTE_DATA *dbAttrs = new CSSM_DB_ATTRIBUTE_DATA[numSchemaAttrs];
+       
+       /* 
+        * Grind thru the attributes in the specified schema. Do not assume the presence
+        * of any given attribute.
+        */
+       uint32 foundAttrs = 0;  
+       mdsDict->lookupAttributes(relInfo, dbAttrs, foundAttrs);
+       
+       /* write to the DB */
+       MDSInsertRecord(dbAttrs, foundAttrs, relInfo->DataRecordType, mDl, dbHand);
+
+       MDSFreeDbRecordAttrs(dbAttrs, foundAttrs);
+       delete [] dbAttrs;
+}
+
+/*
+ * Parse CSP capabilities. This is much more complicated than most records. 
+ * The propertly list (*.mdsinfo) is set up like this:
+ *
+ * root(Dictionary) {
+ *    ModuleID(String)
+ *    SSID(Number)
+ *    Capabilities(Array) {
+ *       index 0(Dictionary) {
+ *           AlgType(String)                                   -- CSSM_ALGID_SHA1
+ *           ContextType(String)                               -- CSSM_ALGCLASS_DIGEST
+ *           UseeTag(String)                                   -- CSSM_USEE_NONE
+ *           Description(String)                               -- "SHA1 Digest"
+ *           Attributes(Array)
+ *              index 0(Dictionary)
+ *                 AttributeType(String)               -- CSSM_ATTRIBUTE_OUTPUT_SIZE
+ *                 AttributeValue(Array) {
+ *                    index 0(Number)                  -- 20
+ *                    ...
+ *                 }
+ *              index n ...
+ *           }
+ *       index n...
+ *    }
+ * }      
+ *
+ * The plist can specify multiple Capabilities, multiple Attributes for each
+ * Capability, and multiple values for each Attribute. (Note that MULTI_UINT32
+ * in the DB is represented in the plist as an Array of Numbers.) Each element 
+ * of each Attributes array maps to one record in the DB. The GroupID attribute 
+ * of a record is the index into the plist's Capabilities array. 
+ */
+void MDSAttrParser::parseCspCapabilitiesRecord(
+       MDSDictionary *mdsDict)
+{
+       /* 
+        * Malloc an attribute array big enough for the whole schema. We're going 
+        * to re-use this array every time we write a new record. Portions of 
+        * the array are invariant for some inner loops.
+        */ 
+       const RelationInfo *topRelInfo = 
+               MDSRecordTypeToRelation(MDS_CDSADIR_CSP_CAPABILITY_RECORDTYPE);
+       assert(topRelInfo != NULL);
+       uint32 numInAttrs = topRelInfo->NumberOfAttributes;
+       CSSM_DB_ATTRIBUTE_DATA_PTR outAttrs = new CSSM_DB_ATTRIBUTE_DATA[numInAttrs];
+       
+       /* these attrs are only set once, then they remain invariant */
+       uint32 numTopLevelAttrs;
+       mdsDict->lookupAttributes(&CSPCapabilitiesDict1RelInfo, outAttrs, 
+               numTopLevelAttrs);
+               
+       bool fetchedFromDisk = false;
+       
+       /* obtain Capabilities array */
+       CFArrayRef capArray = (CFArrayRef)mdsDict->lookupWithIndirect("Capabilities",
+               mBundle,
+               CFArrayGetTypeID(),
+               fetchedFromDisk);
+       if(capArray == NULL) {
+               /* well we did not get very far.... */
+               MPDebug("parseCspCapabilitiesRecord: no (or bad) Capabilities");
+               delete [] outAttrs;
+               return;
+       }
+       
+       /*
+        * Descend into Capabilities array. Each element is a dictionary defined 
+        * by CSPCapabilitiesDict2RelInfo.
+        */
+       CFIndex capArraySize = CFArrayGetCount(capArray);
+       CFIndex capDex;
+       for(capDex=0; capDex<capArraySize; capDex++) {
+               MPDebug("...parsing Capability %d", (int)capDex);
+               CFDictionaryRef capDict = 
+                       (CFDictionaryRef)CFArrayGetValueAtIndex(capArray, capDex);
+               if((capDict == NULL) || 
+                  (CFGetTypeID(capDict) != CFDictionaryGetTypeID())) {
+                       MPDebug("parseCspCapabilitiesRecord: bad Capabilities element");
+                       break;
+               }
+               MDSDictionary capDictMds(capDict);
+               
+               /* 
+                * Append this dictionary's attributes to outAttrs, after the fixed
+                * attributes from CSPCapabilitiesDict1RelInfo.
+                */
+               uint32 numCapDictAttrs;
+               capDictMds.lookupAttributes(&CSPCapabilitiesDict2RelInfo,
+                       &outAttrs[numTopLevelAttrs],
+                       numCapDictAttrs);
+               
+               /*
+                * Append the GroupId attribute, which we infer from the current index 
+                * into Capabilitites.  However, thou shalt not use > 4-byte values 
+         * for MDS, so convert from CFIndex first.  
+                */
+        if (capDex > uint32(~0)) {
+            MPDebug("parseCspCapabilitiesRecord: too large an index for MDS");
+            break;
+        }
+        uint32 index32 = uint32(capDex);
+               MDSRawValueToDbAttr(&index32, sizeof(index32), CSSM_DB_ATTRIBUTE_FORMAT_UINT32, 
+                       "GroupId", outAttrs[numTopLevelAttrs + numCapDictAttrs]);
+               numCapDictAttrs++;      
+               
+               /* 
+                * Now descend into the array of this capability's attributes.
+                * Each element is a dictionary defined by
+                * by CSPCapabilitiesDict3RelInfo.
+                */
+               CFArrayRef attrArray = (CFArrayRef)capDictMds.lookup("Attributes",
+                       true, CFArrayGetTypeID());
+               if(attrArray == NULL) {
+                       MPDebug("parseCspCapabilitiesRecord: no (or bad) Attributes");
+                       break;
+               }
+               CFIndex attrArraySize = CFArrayGetCount(attrArray);
+               CFIndex attrDex;
+               for(attrDex=0; attrDex<attrArraySize; attrDex++) {
+                       MPDebug("   ...parsing Attribute %d", (int)attrDex);
+                       CFDictionaryRef attrDict = 
+                               (CFDictionaryRef)CFArrayGetValueAtIndex(attrArray, attrDex);
+                       if((attrDict == NULL) || 
+                          (CFGetTypeID(attrDict) != CFDictionaryGetTypeID())) {
+                               MPDebug("parseCspCapabilitiesRecord: bad Attributes element");
+                               break;
+                       }
+                       MDSDictionary attrDictMds(attrDict);
+                       
+                       /* 
+                        * Append this dictionary's attributes to outAttrs, after the fixed
+                        * attributes from CSPCapabilitiesDict1RelInfo and this capability's
+                        * CSPCapabilitiesDict2RelInfo.
+                        */
+                       uint32 numAttrDictAttrs;
+                       attrDictMds.lookupAttributes(&CSPCapabilitiesDict3RelInfo,
+                               &outAttrs[numTopLevelAttrs + numCapDictAttrs],
+                               numAttrDictAttrs);
+                       
+                       /* write to DB */
+                       MDSInsertRecord(outAttrs,
+                               numTopLevelAttrs + numCapDictAttrs + numAttrDictAttrs,
+                               MDS_CDSADIR_CSP_CAPABILITY_RECORDTYPE, 
+                               mDl, 
+                               mCdsaDirHand);
+                               
+                       /* just free the attrs we allocated in this loop */
+                       MDSFreeDbRecordAttrs(&outAttrs[numTopLevelAttrs + numCapDictAttrs],
+                               numAttrDictAttrs);
+               }       /* for each attribute */
+               /* just free the attrs we allocated in this loop */
+               MDSFreeDbRecordAttrs(&outAttrs[numTopLevelAttrs], numCapDictAttrs);
+       }               /* for each capability */
+       
+       MDSFreeDbRecordAttrs(outAttrs, numTopLevelAttrs);
+       delete [] outAttrs;
+       if(fetchedFromDisk) {
+               CF_RELEASE(capArray);
+       }
+}
+
+/*
+ * Parse TP Policy OIDs. 
+ * The propertly list (*.mdsinfo) is set up like this:
+ *
+ * root(Dictionary) {
+ *    ModuleID(String)
+ *    SSID(Number)
+ *    Policies(Array) {
+ *       index 0(Dictionary) {
+ *           OID(Data)                                                 -- <092a8648 86f76364 0102>
+ *           Value(Data)                                               -- optional, OID-specific 
+ *       index n...
+ *    }
+ * }      
+ *
+ * The plist can specify multiple Policies. Each element of the Policies 
+ * array maps to one record in the DB.  
+ */
+void MDSAttrParser::parseTpPolicyOidsRecord(
+       MDSDictionary *mdsDict)
+{
+       /* 
+        * Malloc an attribute array big enough for the whole schema. We're going 
+        * to re-use this array every time we write a new record. Portions of 
+        * the array are invariant for some inner loops.
+        */ 
+       const RelationInfo *topRelInfo = 
+               MDSRecordTypeToRelation(MDS_CDSADIR_TP_OIDS_RECORDTYPE);
+       assert(topRelInfo != NULL);
+       uint32 numInAttrs = topRelInfo->NumberOfAttributes;
+       CSSM_DB_ATTRIBUTE_DATA_PTR outAttrs = new CSSM_DB_ATTRIBUTE_DATA[numInAttrs];
+       
+       /* these attrs are only set once, then they remain invariant */
+       uint32 numTopLevelAttrs;
+       mdsDict->lookupAttributes(&TpPolicyOidsDict1RelInfo, outAttrs, 
+               numTopLevelAttrs);
+               
+       /* obtain Policies array */
+       CFArrayRef policyArray = (CFArrayRef)mdsDict->lookup("Policies",
+               true, CFArrayGetTypeID());
+       if(policyArray == NULL) {
+               /* well we did not get very far.... */
+               MPDebug("parseTpPolicyOidsRecord: no (or bad) Policies");
+               delete [] outAttrs;
+               return;
+       }
+       
+       /*
+        * Descend into Policies array. Each element is a dictionary defined 
+        * by TpPolicyOidsDict2RelInfo.
+        */
+       CFIndex policyArraySize = CFArrayGetCount(policyArray);
+       CFIndex policyDex;
+       for(policyDex=0; policyDex<policyArraySize; policyDex++) {
+               MPDebug("...parsing Policy %d", (int)policyDex);
+               CFDictionaryRef policyDict = 
+                       (CFDictionaryRef)CFArrayGetValueAtIndex(policyArray, policyDex);
+               if((policyDict == NULL) || 
+                  (CFGetTypeID(policyDict) != CFDictionaryGetTypeID())) {
+                       MPDebug("parseTpPolicyOidsRecord: bad Policies element");
+                       break;
+               }
+               MDSDictionary policyDictMds(policyDict);
+               
+               /* 
+                * Append this dictionary's attributes to outAttrs, after the fixed
+                * attributes from TpPolicyOidsDict1RelInfo.
+                */
+               uint32 numPolicyDictAttrs;
+               policyDictMds.lookupAttributes(&TpPolicyOidsDict2RelInfo,
+                       &outAttrs[numTopLevelAttrs],
+                       numPolicyDictAttrs);
+               
+                       
+               /* write to DB */
+               MDSInsertRecord(outAttrs,
+                       numTopLevelAttrs + numPolicyDictAttrs,
+                       MDS_CDSADIR_TP_OIDS_RECORDTYPE, 
+                       mDl, 
+                       mCdsaDirHand);
+                       
+               /* free the attrs allocated in this loop */
+               MDSFreeDbRecordAttrs(outAttrs + numTopLevelAttrs, numPolicyDictAttrs);
+       }               /* for each policy */
+       MDSFreeDbRecordAttrs(outAttrs, numTopLevelAttrs);
+       delete [] outAttrs;
+}
+
+
+} // end namespace Security