]> git.saurik.com Git - apple/security.git/blobdiff - SecurityTests/testclient/dltests.cpp
Security-57031.1.35.tar.gz
[apple/security.git] / SecurityTests / testclient / dltests.cpp
diff --git a/SecurityTests/testclient/dltests.cpp b/SecurityTests/testclient/dltests.cpp
new file mode 100644 (file)
index 0000000..6d70ee7
--- /dev/null
@@ -0,0 +1,804 @@
+#include "dltests.h"
+#include "csptests.h"
+#include "attributes.h"
+#include <security_cdsa_client/multidldb.h>
+#include <vector>
+#include <security_cdsa_client/securestorage.h> // For CSPDL.
+#include <security_cdsa_client/genkey.h>
+#include <security_utilities/trackingallocator.h>
+
+using namespace CssmClient;
+
+// Configuration.
+#define HEX_DIGITS_PER_LINE  20
+#define INDENT_SIZE 2
+
+
+const CSSM_GUID* gSelectedFileGuid = &gGuidAppleFileDL;
+
+
+
+static void testDLCreate(const Guid &dlGuid);
+static void testDLDelete(const Guid &dlGuid, bool throwOnError);
+static void testGen(const Guid &cspDlGuid);
+static void testDLCrypt(const Guid &cspDlGuid);
+static void testMultiDLDb(const Guid &dlGuid);
+static void dumpRelation(uint32 indent, Db &db, uint32 relationID, const char *relationName, bool printSchema);
+static void dumpRecord(uint32 indent, const DbAttributes &record, const CssmData &data, const DbUniqueRecord &uniqueId);
+
+#define CSSM_DB_RELATION(RELATIONID) RecordAttrInfo ## RELATIONID
+
+#define CSSM_DB_DEFINE_RELATION_BEGIN(RELATIONID) \
+static const CSSM_DB_ATTRIBUTE_INFO AttrInfo ## RELATIONID[] =
+
+#define CSSM_DB_DEFINE_RELATION_END(RELATIONID) \
+; \
+static const CSSM_DB_RECORD_ATTRIBUTE_INFO CSSM_DB_RELATION(RELATIONID) = \
+{ \
+    RELATIONID, \
+    sizeof(AttrInfo ## RELATIONID) / sizeof(CSSM_DB_ATTRIBUTE_INFO), \
+    const_cast<CSSM_DB_ATTRIBUTE_INFO_PTR>(AttrInfo ## RELATIONID) \
+}
+
+// GENERIC PASSWORDS
+CSSM_DB_DEFINE_RELATION_BEGIN(CSSM_DL_DB_RECORD_GENERIC_PASSWORD)
+{
+       CSSM_DB_ATTR(Attributes::Class), 
+       CSSM_DB_ATTR(Attributes::CreationDate), 
+       CSSM_DB_ATTR(Attributes::ModDate), 
+       CSSM_DB_ATTR(Attributes::Description),
+       CSSM_DB_ATTR(Attributes::Comment),
+       CSSM_DB_ATTR(Attributes::Creator),
+       CSSM_DB_ATTR(Attributes::Type),
+       CSSM_DB_ATTR(Attributes::ScrCode),
+       CSSM_DB_ATTR(Attributes::Label),
+       CSSM_DB_ATTR(Attributes::Invisible),
+       CSSM_DB_ATTR(Attributes::Negative),
+       CSSM_DB_ATTR(Attributes::Custom),
+       //CSSM_DB_ATTR(Attributes::Protected),
+       CSSM_DB_ATTR(Attributes::Account),
+       CSSM_DB_ATTR(Attributes::Service),
+       CSSM_DB_ATTR(Attributes::Generic)
+}
+CSSM_DB_DEFINE_RELATION_END(CSSM_DL_DB_RECORD_GENERIC_PASSWORD);
+
+
+// APPLESHARE PASSWORDS
+CSSM_DB_DEFINE_RELATION_BEGIN(CSSM_DL_DB_RECORD_APPLESHARE_PASSWORD)
+{
+       CSSM_DB_ATTR(Attributes::Class), 
+       CSSM_DB_ATTR(Attributes::CreationDate), 
+       CSSM_DB_ATTR(Attributes::ModDate), 
+       CSSM_DB_ATTR(Attributes::Description),
+       CSSM_DB_ATTR(Attributes::Comment),
+       CSSM_DB_ATTR(Attributes::Creator),
+       CSSM_DB_ATTR(Attributes::Type),
+       CSSM_DB_ATTR(Attributes::ScrCode),
+       CSSM_DB_ATTR(Attributes::Label),
+       CSSM_DB_ATTR(Attributes::Invisible),
+       CSSM_DB_ATTR(Attributes::Negative),
+       CSSM_DB_ATTR(Attributes::Custom),
+       //CSSM_DB_ATTR(Attributes::Protected),
+       CSSM_DB_ATTR(Attributes::Volume),
+       CSSM_DB_ATTR(Attributes::Addr),
+       CSSM_DB_ATTR(Attributes::Signature),
+       CSSM_DB_ATTR(Attributes::ProtocolType)
+}
+CSSM_DB_DEFINE_RELATION_END(CSSM_DL_DB_RECORD_APPLESHARE_PASSWORD);
+
+// INTERNET PASSWORDS
+CSSM_DB_DEFINE_RELATION_BEGIN(CSSM_DL_DB_RECORD_INTERNET_PASSWORD)
+{ 
+       CSSM_DB_ATTR(Attributes::Class), 
+       CSSM_DB_ATTR(Attributes::CreationDate), 
+       CSSM_DB_ATTR(Attributes::ModDate), 
+       CSSM_DB_ATTR(Attributes::Description),
+       CSSM_DB_ATTR(Attributes::Comment),
+       CSSM_DB_ATTR(Attributes::Creator),
+       CSSM_DB_ATTR(Attributes::Type),
+       CSSM_DB_ATTR(Attributes::ScrCode),
+       CSSM_DB_ATTR(Attributes::Label),
+       CSSM_DB_ATTR(Attributes::Invisible),
+       CSSM_DB_ATTR(Attributes::Negative),
+       CSSM_DB_ATTR(Attributes::Custom),
+       //CSSM_DB_ATTR(Attributes::Protected),
+       CSSM_DB_ATTR(Attributes::Account),
+       CSSM_DB_ATTR(Attributes::SecDomain),
+       CSSM_DB_ATTR(Attributes::Server),
+       CSSM_DB_ATTR(Attributes::AuthType),
+       CSSM_DB_ATTR(Attributes::Port),
+       CSSM_DB_ATTR(Attributes::Path),
+       CSSM_DB_ATTR(Attributes::ProtocolType)
+}
+CSSM_DB_DEFINE_RELATION_END(CSSM_DL_DB_RECORD_INTERNET_PASSWORD);
+
+// INTERNET PASSWORDS
+CSSM_DB_DEFINE_RELATION_BEGIN(CSSM_DL_DB_RECORD_SYMMETRIC_KEY)
+{ 
+       CSSM_DB_ATTR(Attributes::KeyClass),
+       CSSM_DB_ATTR(Attributes::PrintName),
+       CSSM_DB_ATTR(Attributes::Alias),
+       CSSM_DB_ATTR(Attributes::Permanent),
+       CSSM_DB_ATTR(Attributes::Private),
+       CSSM_DB_ATTR(Attributes::Modifiable),
+       CSSM_DB_ATTR(Attributes::Label),
+       CSSM_DB_ATTR(Attributes::ApplicationTag),
+       CSSM_DB_ATTR(Attributes::KeyCreator),
+       CSSM_DB_ATTR(Attributes::KeyType),
+       CSSM_DB_ATTR(Attributes::KeySizeInBits),
+       CSSM_DB_ATTR(Attributes::EffectiveKeySize),
+       CSSM_DB_ATTR(Attributes::StartDate),
+       CSSM_DB_ATTR(Attributes::EndDate),
+       CSSM_DB_ATTR(Attributes::Sensitive),
+       CSSM_DB_ATTR(Attributes::AlwaysSensitive),
+       CSSM_DB_ATTR(Attributes::Extractable),
+       CSSM_DB_ATTR(Attributes::NeverExtractable),
+       CSSM_DB_ATTR(Attributes::Encrypt),
+       CSSM_DB_ATTR(Attributes::Decrypt),
+       CSSM_DB_ATTR(Attributes::Derive),
+       CSSM_DB_ATTR(Attributes::Sign),
+       CSSM_DB_ATTR(Attributes::Verify),
+       CSSM_DB_ATTR(Attributes::SignRecover),
+       CSSM_DB_ATTR(Attributes::VerifyRecover),
+       CSSM_DB_ATTR(Attributes::Wrap),
+       CSSM_DB_ATTR(Attributes::UnWrap)
+}
+CSSM_DB_DEFINE_RELATION_END(CSSM_DL_DB_RECORD_SYMMETRIC_KEY);
+
+
+static const CSSM_DB_RECORD_ATTRIBUTE_INFO KCAttrs[] =
+{
+       CSSM_DB_RELATION(CSSM_DL_DB_RECORD_GENERIC_PASSWORD),
+       CSSM_DB_RELATION(CSSM_DL_DB_RECORD_APPLESHARE_PASSWORD),
+       CSSM_DB_RELATION(CSSM_DL_DB_RECORD_INTERNET_PASSWORD)
+       //CSSM_DB_RELATION(CSSM_DL_DB_RECORD_SYMMETRIC_KEY)
+};
+
+static const CSSM_DB_RECORD_INDEX_INFO recordIndex = 
+{
+    CSSM_DB_RECORDTYPE_APP_DEFINED_START, // CSSM_DB_RECORDTYPE
+    0, //%%% for now
+    NULL       //%%% for now
+};
+static const    CSSM_DB_RECORD_INDEX_INFO recordIndexes[] = {recordIndex, recordIndex, recordIndex};
+
+// parse info (to improve later)
+static const        CSSM_DB_PARSING_MODULE_INFO parseInfo =
+{
+    CSSM_DB_RECORDTYPE_APP_DEFINED_START,
+    {
+        {0,0,0,{0}},
+        {0,0},
+        0,
+        0
+    }
+};
+static const    CSSM_DB_PARSING_MODULE_INFO parseInfos[] = {parseInfo, parseInfo, parseInfo};
+
+static const CSSM_DBINFO       KCDBInfo =
+{
+    sizeof(KCAttrs) / sizeof(CSSM_DB_RECORD_ATTRIBUTE_INFO),
+    const_cast<CSSM_DB_PARSING_MODULE_INFO_PTR>(parseInfos),
+    const_cast<CSSM_DB_RECORD_ATTRIBUTE_INFO_PTR>(KCAttrs),
+    const_cast<CSSM_DB_RECORD_INDEX_INFO_PTR>(recordIndexes),
+    CSSM_TRUE,
+    NULL,
+    NULL
+};
+
+
+
+void dltests(bool autoCommit)
+{
+       testDLDelete(gGuidAppleFileDL, false);
+       testDLCreate(gGuidAppleFileDL);
+       testMultiDLDb(gGuidAppleFileDL);
+
+       testDLDelete(gGuidAppleCSPDL, false);
+       testDLCreate(gGuidAppleCSPDL);
+       testGen(gGuidAppleCSPDL);
+       testDLCrypt(gGuidAppleCSPDL);
+       testMultiDLDb(gGuidAppleCSPDL);
+       //testDLDelete(gGuidAppleCSPDL, true);
+}
+
+static void testDLCreate(const Guid &dlGuid)
+{
+       DL appledl(dlGuid);
+       Db testDb(appledl, DBNAME1);
+       testDb->dbInfo(&KCDBInfo);
+       testDb->create();
+}
+
+static void testDLDelete(const Guid &dlGuid, bool throwOnError)
+{
+       DL appledl(dlGuid);
+       Db testDb(appledl, DBNAME1);
+       try
+       {
+               testDb->deleteDb();
+       }
+       catch(CssmError e)
+       {
+               if (throwOnError || e.osStatus() != CSSMERR_DL_DATASTORE_DOESNOT_EXIST)
+                       throw;
+       }
+}
+
+static void testGen(const Guid &cspDlGuid)
+{
+       printf("\n* performing CSP/DL keygen test...\n");
+       CSPDL cspdl(cspDlGuid);
+       Db db(cspdl, DBNAME1);
+
+    printf("Generating permanent key\n");
+       GenerateKey genKey(cspdl, CSSM_ALGID_DES, 64);
+       genKey.database(db);
+       CssmPolyData label("First Key!");
+       Key key = genKey(KeySpec(CSSM_KEYUSE_ANY,
+                                                        CSSM_KEYATTR_PERMANENT | CSSM_KEYATTR_SENSITIVE,
+                                                        label));
+    printf("done\n");
+}
+
+static void testDLCrypt(const Guid &cspDlGuid)
+{
+    printf("\n* performing encrypt/decrypt test...\n");
+       CSPDL cspdl(cspDlGuid);
+       Db db(cspdl, DBNAME1);
+
+    printf("Finding key\n");
+       DbCursor cursor(db);
+       cursor->recordType(CSSM_DL_DB_RECORD_SYMMETRIC_KEY);
+       DbUniqueRecord keyId;
+       CssmDataContainer keyData;
+       if (!cursor->next(NULL, &keyData, keyId))
+               CssmError::throwMe(CSSMERR_DL_ENDOFDATA);
+
+       Key key(cspdl, *reinterpret_cast<CSSM_KEY *>(keyData.Data));
+
+    printf("done\n");
+
+       // Gnerate IV
+    printf("Generating iv\n");
+       //CssmData iv = Random(csp, CSSM_ALGID_SHARandom)(8);
+       CssmPolyData iv("12345678");
+
+       CssmPolyData in("Om mani padme hum");
+       printf("input=");
+       dump(in);
+
+       // Encrypt
+    printf("Encrypting\n");
+
+       Encrypt encrypt(cspdl, CSSM_ALGID_DES);
+       encrypt.mode(CSSM_ALGMODE_CBCPadIV8);
+       encrypt.padding(CSSM_PADDING_PKCS1);
+       encrypt.initVector(iv);
+       encrypt.key(key);
+       CssmData cipher;
+       CssmData remcipher;
+       encrypt.encrypt(&in, 1, &cipher, 1);
+       encrypt.final(remcipher);
+       printf("ciphertext=");
+       dump(cipher);
+       printf("remainder=");
+       dump(remcipher);
+
+       // Decrypt
+    printf("Decrypting\n");
+
+       Decrypt decrypt(cspdl, CSSM_ALGID_DES);
+       decrypt.key(key);
+       decrypt.mode(CSSM_ALGMODE_CBCPadIV8);
+       decrypt.padding(CSSM_PADDING_PKCS1);
+       decrypt.initVector(iv);
+       CssmData plain;
+       CssmData remplain;
+       CssmData inp[] = { cipher, remcipher };
+       decrypt.decrypt(inp, 2, &plain, 1);
+       decrypt.final(remplain);
+       printf("plaintext=");
+       dump(plain);
+       printf("remainder=");
+       dump(remplain);
+
+    printf("end encrypt/decrypt test\n");
+}
+
+static void print(sint32 value)
+{
+       printf("%ld", value);
+}
+
+static void print(double value)
+{
+       printf("%g", value);
+}
+
+static void print(uint32 value)
+{
+       uint8 *bytes = reinterpret_cast<uint8 *>(&value);
+       bool ascii = true;
+       for (uint32 ix = 0; ix < sizeof(uint32); ++ix)
+               if (bytes[ix] < 0x20 || bytes[ix] > 0x7f)
+               {
+                       ascii = false;
+                       break;
+               }
+
+       if (ascii)
+       {
+               putchar('\'');
+               for (uint32 ix = 0; ix < sizeof(uint32); ++ix)
+                       putchar(bytes[ix]);
+
+               printf("' (0x%08lx)", value);
+       }
+       else
+               printf("0x%08lx", value);
+}
+
+static void printAsString(uint32 indent, const CSSM_DATA &value)
+{
+       printf("%.*s", static_cast<int>(value.Length), value.Data);
+}
+
+static void print(uint32 indent, const char *value)
+{
+       printf("%s", value);
+}
+
+static void printIndent(uint32 indent)
+{
+       //if (indent == 0)
+       //      return;
+
+       putchar('\n');
+       for (uint32 ix = 0; ix < indent; ++ix)
+               putchar(' ');
+}
+
+static void printRange(uint32 length, const uint8 *data)
+{
+       for (uint32 ix = 0; ix < HEX_DIGITS_PER_LINE; ++ix)
+       {
+               if (ix && ix % 4 == 0)
+                       putchar(' ');
+
+               if (ix < length)
+                       printf("%02x", static_cast<unsigned int>(data[ix]));
+               else
+                       printf("  ");
+       }
+
+       printf("  ");
+       for (uint32 ix = 0; ix < length; ++ix)
+       {
+               if (data[ix] < 0x20 || data[ix] > 0x7f)
+                       putchar('.');
+               else
+                       putchar(data[ix]);
+       }
+}
+
+static void print(uint32 indent, const CSSM_DATA &value)
+{
+       if (value.Length == 0)
+               return;
+
+       if (value.Length > HEX_DIGITS_PER_LINE)
+       {
+               uint32 ix;
+               for (ix = 0; ix < value.Length - HEX_DIGITS_PER_LINE; ix += HEX_DIGITS_PER_LINE)
+               {
+                       printIndent(indent);
+                       printRange(HEX_DIGITS_PER_LINE, &value.Data[ix]);
+               }
+               printIndent(indent);
+               printRange(value.Length - ix, &value.Data[ix]);
+               printIndent(indent - INDENT_SIZE);
+       }
+       else
+               printRange(value.Length, value.Data);
+}
+
+static void printOID(uint32 indent, const CSSM_OID &value)
+{
+       print(indent, value);
+}
+
+static const char *format(CSSM_DB_ATTRIBUTE_FORMAT format)
+{
+       switch(format)
+       {
+       case CSSM_DB_ATTRIBUTE_FORMAT_STRING: return "string";
+       case CSSM_DB_ATTRIBUTE_FORMAT_SINT32: return "sint32";
+       case CSSM_DB_ATTRIBUTE_FORMAT_UINT32: return "uint32";
+       case CSSM_DB_ATTRIBUTE_FORMAT_BIG_NUM: return "big_num";
+       case CSSM_DB_ATTRIBUTE_FORMAT_REAL: return "real";
+       case CSSM_DB_ATTRIBUTE_FORMAT_TIME_DATE: return "time_date";
+       case CSSM_DB_ATTRIBUTE_FORMAT_BLOB: return "blob";
+       case CSSM_DB_ATTRIBUTE_FORMAT_MULTI_UINT32: return "multi_uint32";
+       case CSSM_DB_ATTRIBUTE_FORMAT_COMPLEX: return "complex";
+       default: abort();
+       }
+}
+
+static void print(uint32 indent, const CssmDbAttributeData &attr)
+{
+       bool multiValues = false;
+       if (attr.size() == 0)
+       {
+               printf("<array/>");
+               return;
+       }
+
+       if (attr.size() != 1)
+       {
+               printIndent(indent);
+               printf("<array>");
+               indent += INDENT_SIZE;
+               multiValues = true;
+       }
+
+       for (uint32 ix = 0; ix < attr.size(); ++ix)
+       {
+               if (multiValues)
+                       printIndent(indent);
+
+               printf("<%s>", format(attr.format()));
+               switch (attr.format())
+               {
+               case CSSM_DB_ATTRIBUTE_FORMAT_STRING:
+                       printAsString(indent + INDENT_SIZE, attr.at(ix));
+                       break;
+               case CSSM_DB_ATTRIBUTE_FORMAT_UINT32:
+                       print(attr.at<uint32>(ix));
+                       break;
+               case CSSM_DB_ATTRIBUTE_FORMAT_SINT32:
+                       print(attr.at<sint32>(ix));
+                       break;
+               case CSSM_DB_ATTRIBUTE_FORMAT_REAL:
+                       print(attr.at<double>(ix));
+                       break;
+               case CSSM_DB_ATTRIBUTE_FORMAT_TIME_DATE:
+                       printf("%*s", 15, attr.at<const char *>(ix));
+                       break;
+               case CSSM_DB_ATTRIBUTE_FORMAT_MULTI_UINT32:
+               case CSSM_DB_ATTRIBUTE_FORMAT_BIG_NUM:
+               case CSSM_DB_ATTRIBUTE_FORMAT_BLOB:
+               case CSSM_DB_ATTRIBUTE_FORMAT_COMPLEX:
+               default:
+                       print(indent + INDENT_SIZE, attr.at<const CssmData &>(ix));
+                       break;
+               }
+               printf("</%s>", format(attr.format()));
+       }
+
+       if (multiValues)
+       {
+               indent -= INDENT_SIZE;
+               printIndent(indent);
+               printf("</array>");
+       }
+}
+
+static void print(uint32 indent, const CssmDbAttributeInfo &info)
+{
+               switch (info.nameFormat())
+               {
+               case CSSM_DB_ATTRIBUTE_NAME_AS_STRING:
+               {
+                       printf("<string>");
+                       print(indent + INDENT_SIZE, info.Label.AttributeName);
+                       printf("</string>");
+                       break;
+               }
+               case CSSM_DB_ATTRIBUTE_NAME_AS_INTEGER:
+               {
+                       printf("<integer>");
+                       print(info.Label.AttributeID);
+                       printf("</integer>");
+                       break;
+               }
+               case CSSM_DB_ATTRIBUTE_NAME_AS_OID:
+               {
+                       printf("<oid>");
+                       printOID(indent + INDENT_SIZE, info.Label.AttributeOID);
+                       printf("</oid>");
+                       break;
+               }
+               default:
+                       throw Error(CSSMERR_DL_DATABASE_CORRUPT);
+               }
+}
+
+void dumpDb(char *dbName, bool printSchema)
+{
+       DL appledl(*gSelectedFileGuid);
+       Db db(appledl, dbName);
+       DbCursor relations(db);
+       relations->recordType(CSSM_DL_DB_SCHEMA_INFO);
+       DbAttributes schemaRecord(db, 2);
+       schemaRecord.add(Attributes::RelationID);
+       schemaRecord.add(Attributes::RelationName);
+       CssmDataContainer data;
+       DbUniqueRecord uniqueId(db);
+
+       uint32 indent = 0;
+       printf("<database>");
+       indent += INDENT_SIZE;
+       printIndent(indent);
+       printf("<name>%s</name>", dbName);
+       while (relations->next(&schemaRecord, &data, uniqueId))
+       {
+               uint32 relationID = schemaRecord.at(0);
+               if (!printSchema && CSSM_DB_RECORDTYPE_SCHEMA_START <= relationID
+                       && relationID < CSSM_DB_RECORDTYPE_SCHEMA_END)
+                       continue;
+
+               printIndent(indent);
+               printf("<relation>");
+               string relationName = schemaRecord.at(1);
+               dumpRelation(indent + INDENT_SIZE, db, relationID, relationName.c_str(), printSchema);
+               printIndent(indent);
+               printf("</relation>");
+       }
+
+       indent -= INDENT_SIZE;
+       printIndent(indent);
+       printf("</database>\n");
+}
+
+static void dumpRelation(uint32 indent, Db &db, uint32 relationID, const char *relationName, bool printSchema)
+{
+       TrackingAllocator anAllocator(Allocator::standard());
+
+       printIndent(indent);
+       printf("<name>");
+       print(indent + INDENT_SIZE, relationName);
+       printf("</name>");
+       printIndent(indent);
+       printf("<id>");
+       print(relationID);
+       printf("</id>");
+
+       // Create a cursor on the SCHEMA_ATTRIBUTES table for records with RelationID == relationID
+       DbCursor attributes(db);
+       attributes->recordType(CSSM_DL_DB_SCHEMA_ATTRIBUTES);
+       attributes->add(CSSM_DB_EQUAL, Attributes::RelationID, relationID);
+
+       // Set up a record for retriving the SCHEMA_ATTRIBUTES
+       DbAttributes schemaRecord(db, 5);
+       schemaRecord.add(Attributes::AttributeNameFormat);
+       schemaRecord.add(Attributes::AttributeFormat);
+       schemaRecord.add(Attributes::AttributeName);
+       schemaRecord.add(Attributes::AttributeID);
+       schemaRecord.add(Attributes::AttributeNameID);
+
+       DbAttributes record(db);
+       CssmDataContainer data;
+       DbUniqueRecord uniqueId(db);
+
+       if (printSchema)
+       {
+               printIndent(indent);
+               printf("<schema>");
+               indent += INDENT_SIZE;
+       }
+
+       while (attributes->next(&schemaRecord, &data, uniqueId))
+       {
+               CssmDbAttributeInfo &anInfo = record.add().info();
+               anInfo.AttributeNameFormat = schemaRecord.at(0);
+               anInfo.AttributeFormat = schemaRecord.at(1);
+               switch (anInfo.AttributeNameFormat)
+               {
+               case CSSM_DB_ATTRIBUTE_NAME_AS_STRING:
+               {
+                       CssmDbAttributeData &anAttributeName = schemaRecord.at(2);
+                       
+                       string name = static_cast<string>(anAttributeName);
+                       anInfo.Label.AttributeName = reinterpret_cast<char *>(anAllocator.malloc(name.size() + 1));
+                       strcpy(anInfo.Label.AttributeName, name.c_str());
+
+                       // XXX Need to copy the memory.  For now avoid it being freed.
+                       anAttributeName.Value[0].Data = NULL;
+                       anAttributeName.Value[0].Length = 0;
+                       break;
+               }
+               case CSSM_DB_ATTRIBUTE_NAME_AS_INTEGER:
+               {
+                       CssmDbAttributeData &anAttributeID = schemaRecord.at(3);
+                       anInfo.Label.AttributeID = anAttributeID;
+                       break;
+               }
+               case CSSM_DB_ATTRIBUTE_NAME_AS_OID:
+               {
+                       CssmDbAttributeData &anAttributeOID = schemaRecord.at(4);
+                       anInfo.Label.AttributeOID = anAttributeOID;
+
+                       // XXX Need to copy the memory.  For now avoid it being freed.
+                       anAttributeOID.Value[0].Data = NULL;
+                       anAttributeOID.Value[0].Length = 0;
+                       break;
+               }
+               default:
+                       throw Error(CSSMERR_DL_DATABASE_CORRUPT);
+               }
+
+               if (printSchema)
+               {
+                       printIndent(indent);
+                       print(indent, anInfo);
+                       printf("<format>%s</format>", format(anInfo.format()));
+               }
+       }
+
+       if (printSchema)
+       {
+               indent -= INDENT_SIZE;
+               printIndent(indent);
+               printf("</schema>");
+       }
+
+       DbCursor records(db);
+       records->recordType(relationID);
+       printIndent(indent);
+       printf("<records>");
+       indent += INDENT_SIZE;
+       while (records->next(&record, &data, uniqueId))
+               dumpRecord(indent, record, data, uniqueId);
+
+       indent -= INDENT_SIZE;
+       printIndent(indent);
+       printf("</records>");
+}
+
+static void
+dumpRecord(uint32 indent, const DbAttributes &record, const CssmData &data, const DbUniqueRecord &uniqueId)
+{
+       const CSSM_DB_UNIQUE_RECORD *recId = static_cast<const DbUniqueRecord &>(uniqueId);
+       uint32 recCount = recId->RecordIdentifier.Length;
+       const uint32 *recArray = reinterpret_cast<const uint32 *>(recId->RecordIdentifier.Data);
+       printIndent(indent);
+       printf("<recid>");
+       for (uint32 ix = 0; ix < recCount / 4; ++ix)
+       {
+               if (ix != 0)
+                       putchar(' ');
+               printf("0x%08lx", recArray[ix]);
+       }
+       printf("</recid>");
+
+       // Print the attributes
+       printIndent(indent);
+       if (record.size() == 0)
+       {
+               printf("<attributes/>");
+       }
+       else
+       {
+               printf("<attributes>");
+               indent += INDENT_SIZE;
+               for (uint32 ix = 0; ix < record.size(); ix++)
+               {
+                       const CssmDbAttributeData &anAttr = record.at(ix);
+                       if (anAttr.size()) // Skip zero valued attributes.
+                       {
+                               printIndent(indent);
+                               print(indent + INDENT_SIZE, anAttr.info());
+                               print(indent + INDENT_SIZE, anAttr);
+                       }
+               }
+
+               indent -= INDENT_SIZE;
+               printIndent(indent);
+               printf("</attributes>");
+       }
+
+       // Print the data
+       printIndent(indent);
+       if (data.length())
+       {
+               printf("<data>");
+               print(indent + INDENT_SIZE, data);
+               printf("</data>");
+       }
+       else
+               printf("<data/>");
+}
+
+static void testMultiDLDb(const Guid &dlGuid)
+{
+       // Setup a list of DLDbIdentifier object to hand off the MultiDLDb.
+       vector<DLDbIdentifier> list;
+       list.push_back(DLDbIdentifier(CssmSubserviceUid(dlGuid), "multidb1.db", NULL));
+       list.push_back(DLDbIdentifier(CssmSubserviceUid(dlGuid), "multidb2.db", NULL));
+
+       // Create MultiDLDb instance.
+       MultiDLDb multiDLDb(list, false);
+
+       // Get a handle for the first and second Db.
+       Db db1(multiDLDb->database(list[0]));
+       Db db2(multiDLDb->database(list[1]));
+
+       // Until this point no CSSM API's have been called!
+
+       // Delete both databases if they exist. 
+       try
+       { db1->deleteDb(); }
+       catch(CssmError e)
+       { if (e.osStatus() != CSSMERR_DL_DATASTORE_DOESNOT_EXIST) throw; }
+
+       try
+       { db2->deleteDb(); }
+       catch(CssmError e)
+       { if (e.osStatus() != CSSMERR_DL_DATASTORE_DOESNOT_EXIST) throw; }
+
+       // XXX Note to self if you set the schema but do not call create()
+       // explicitly maybe the db should only be created if it did not yet exist...
+
+       // Set the schema of both databases so they get created on activate.
+       db1->dbInfo(&KCDBInfo);
+       db2->dbInfo(&KCDBInfo);
+
+       // Insert a record into each database.
+       DbAttributes attrs(db1);
+       attrs.add(Attributes::Comment, "This is the first comment").add("This is the second comment", attrs);
+       attrs.add(Attributes::Label, "Item1");
+       CssmPolyData testdata1("testdata1");
+       db1->insert(CSSM_DL_DB_RECORD_GENERIC_PASSWORD, &attrs, &testdata1);
+
+       attrs.clear();
+       attrs.add(Attributes::Comment, "This is the second comment");
+       attrs.add(Attributes::Label, "Item (in database2).");
+       CssmPolyData testdata2("testdata2");
+       db2->insert(CSSM_DL_DB_RECORD_GENERIC_PASSWORD, &attrs, &testdata2);
+
+       // Create a cursor on the multiDLDb.
+       DbCursor cursor(multiDLDb);
+       // Set the type of records we wish to query.
+       cursor->recordType(CSSM_DL_DB_RECORD_GENERIC_PASSWORD);
+       cursor->add(CSSM_DB_EQUAL, Attributes::Comment, "This is the second comment");
+
+       DbUniqueRecord uniqueId; // Empty uniqueId calling cursor.next will initialize.
+       CssmDataContainer data; // XXX Think about data's allocator.
+
+       // Iterate over the records in all the db's in the multiDLDb.
+       while (cursor->next(&attrs, &data, uniqueId))
+       {
+               // Print the record data.
+               dumpRecord(0, attrs, data, uniqueId);
+       }
+}
+
+#if 0
+       CssmDb::Impl *CssmDL::Impl::newDb(args) { new CssmDbImpl(args); }
+
+       SecureStorage ss(Guid);
+       CssmDb db(ss, DBNAME);
+       CssmUniqueId unique;
+       db.insert(attr, data, unique);
+
+       Cursor cursor(db);
+       CssmKey key;
+       cursor.next(key);
+
+       Cssm cssm;
+       Module module(cssm);
+       CSPDL cspdl(module);
+
+
+       SecureStorage ss(Guid);
+       CssmDb db = ss->db(DBNAME);
+       CssmUniqueId unique;
+       db->insert(attr, data, unique);
+
+       Cursor cursor(db);
+       CssmKey key;
+       cursor->next(key);
+
+
+#endif