]> git.saurik.com Git - apple/security.git/blobdiff - libsecurity_filedb/lib/MetaRecord.cpp
Security-55163.44.tar.gz
[apple/security.git] / libsecurity_filedb / lib / MetaRecord.cpp
diff --git a/libsecurity_filedb/lib/MetaRecord.cpp b/libsecurity_filedb/lib/MetaRecord.cpp
new file mode 100644 (file)
index 0000000..d5ee1be
--- /dev/null
@@ -0,0 +1,580 @@
+/*
+ * 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.
+ */
+
+
+//
+// MetaRecord.cpp
+//
+
+#include "MetaRecord.h"
+#include <security_utilities/trackingallocator.h>
+#include <security_cdsa_utilities/cssmbridge.h>
+
+
+MetaRecord::MetaRecord(CSSM_DB_RECORDTYPE inRecordType) :
+    mRecordType(inRecordType)
+{
+}
+
+MetaRecord::MetaRecord(const CSSM_DB_RECORD_ATTRIBUTE_INFO &inInfo)
+:      mRecordType(inInfo.DataRecordType)
+{
+       try
+       {
+               setRecordAttributeInfo(inInfo);
+       }
+       catch (...)
+       {
+               for_each_delete(mAttributeVector.begin(), mAttributeVector.end());
+       }
+}
+
+MetaRecord::MetaRecord(CSSM_DB_RECORDTYPE inRelationID,
+                       uint32 inNumberOfAttributes,
+                                          const CSSM_DB_SCHEMA_ATTRIBUTE_INFO *inAttributeInfo) :
+    mRecordType(inRelationID)
+{
+       try {
+               for (uint32 anIndex = 0; anIndex < inNumberOfAttributes; anIndex++)
+               {
+                       string aName;
+                       if (inAttributeInfo[anIndex].AttributeName)
+                               aName = string(inAttributeInfo[anIndex].AttributeName);
+                               
+                       const CssmData *aNameID = NULL;
+                       if (inAttributeInfo[anIndex].AttributeNameID.Length > 0)
+                               aNameID = &CssmData::overlay(inAttributeInfo[anIndex].AttributeNameID);
+
+                       uint32 aNumber = inAttributeInfo[anIndex].AttributeId;
+                       createAttribute(
+                               inAttributeInfo[anIndex].AttributeName ? &aName : NULL,
+                               aNameID, aNumber,
+                               inAttributeInfo[anIndex].DataType);
+               }
+       }
+       catch (...)
+       {
+               for_each_delete(mAttributeVector.begin(), mAttributeVector.end());
+       }
+}
+
+MetaRecord::~MetaRecord()
+{
+       // for_each_delete(mAttributeVector.begin(), mAttributeVector.end());
+       AttributeVector::iterator it = mAttributeVector.begin();
+       while (it != mAttributeVector.end())
+       {
+               MetaAttribute* mat = *it++;
+               if (mat != NULL)
+               {
+                       delete mat;
+               }
+       }
+}
+
+void
+MetaRecord::setRecordAttributeInfo(const CSSM_DB_RECORD_ATTRIBUTE_INFO &inInfo)
+{
+    for (uint32 anIndex = 0; anIndex < inInfo.NumberOfAttributes; anIndex++)
+    {
+        switch (inInfo.AttributeInfo[anIndex].AttributeNameFormat)
+        {
+            case CSSM_DB_ATTRIBUTE_NAME_AS_STRING:
+            {
+                string aName(inInfo.AttributeInfo[anIndex].Label.AttributeName);
+                createAttribute(&aName, nil, anIndex,
+                                                               inInfo.AttributeInfo[anIndex].AttributeFormat);
+                break;
+            }
+            case CSSM_DB_ATTRIBUTE_NAME_AS_OID:
+            {
+                const CssmData &aNameID = CssmOid::overlay(inInfo.AttributeInfo[anIndex].Label.AttributeOID);
+                createAttribute(nil, &aNameID, anIndex,
+                                                               inInfo.AttributeInfo[anIndex].AttributeFormat);
+                break;
+            }
+            case CSSM_DB_ATTRIBUTE_NAME_AS_INTEGER:
+            {
+                uint32 aNumber = inInfo.AttributeInfo[anIndex].Label.AttributeID;
+                createAttribute(nil, nil, aNumber,
+                                                               inInfo.AttributeInfo[anIndex].AttributeFormat);
+                break;
+            }
+            default:
+                CssmError::throwMe(CSSMERR_DL_INVALID_FIELD_NAME);
+                break;
+        }
+    }
+}
+
+void
+MetaRecord::createAttribute(const string *inAttributeName,
+                                                       const CssmOid *inAttributeOID,
+                            uint32 inAttributeID,
+                                                       CSSM_DB_ATTRIBUTE_FORMAT inAttributeFormat)
+{
+       // Index of new element is current size of vector
+    uint32 anAttributeIndex = mAttributeVector.size();
+    bool aInsertedAttributeName = false;
+    bool aInsertedAttributeOID = false;
+    bool aInsertedAttributeID = false;
+
+    if (inAttributeName)
+    {
+        if (!mNameStringMap.insert(NameStringMap::value_type(*inAttributeName, anAttributeIndex)).second)
+            CssmError::throwMe(CSSMERR_DL_FIELD_SPECIFIED_MULTIPLE);
+        aInsertedAttributeName = true;
+    }
+    try
+    {
+        if (inAttributeOID)
+        {
+            if (!mNameOIDMap.insert(NameOIDMap::value_type(*inAttributeOID, anAttributeIndex)).second)
+                CssmError::throwMe(CSSMERR_DL_FIELD_SPECIFIED_MULTIPLE);
+            aInsertedAttributeOID = true;
+        }
+
+               if (!mNameIntMap.insert(NameIntMap::value_type(inAttributeID, anAttributeIndex)).second)
+                       CssmError::throwMe(CSSMERR_DL_FIELD_SPECIFIED_MULTIPLE);
+               aInsertedAttributeID = true;
+
+               // Note: this no longer throws INVALID_FIELD_NAME since the attribute will always have
+               // an attribute ID by which it is known
+
+               mAttributeVector.push_back(MetaAttribute::create(inAttributeFormat,
+                       anAttributeIndex, inAttributeID));
+    }
+    catch(...)
+    {
+        if (aInsertedAttributeName)
+            mNameStringMap.erase(*inAttributeName);
+        if (aInsertedAttributeOID)
+            mNameOIDMap.erase(*inAttributeOID);
+        if (inAttributeID)
+            mNameIntMap.erase(inAttributeID);
+               
+        throw;
+    }
+}
+
+
+// Create a packed record from the given inputs.
+void
+MetaRecord::packRecord(WriteSection &inWriteSection,
+                       const CSSM_DB_RECORD_ATTRIBUTE_DATA *inAttributes,
+                       const CssmData *inData) const
+{
+    uint32 aDataSize;
+    if (inData)
+        aDataSize = inData->Length;
+    else
+        aDataSize = 0;
+
+    inWriteSection.put(OffsetDataSize, aDataSize);
+    uint32 anOffset = OffsetAttributeOffsets + AtomSize * mAttributeVector.size();
+    if (aDataSize)
+        anOffset = inWriteSection.put(anOffset, aDataSize, inData->Data);
+
+    vector<uint32> aNumValues(mAttributeVector.size(), ~(uint32)0);
+    vector<CSSM_DATA_PTR> aValues(mAttributeVector.size());
+    uint32 anIndex;
+
+    if (inAttributes == NULL)
+        inWriteSection.put(OffsetSemanticInformation, 0);
+    else
+    {
+        inWriteSection.put(OffsetSemanticInformation, inAttributes->SemanticInformation);
+
+        // Put the supplied attribute values into the list of attributes
+        // and values.
+        anIndex = inAttributes->NumberOfAttributes;
+        // Make sure that AttributeData is a valid array.
+               if (anIndex > 0)
+                       Required(inAttributes->AttributeData);
+
+        while (anIndex-- > 0)
+        {
+            CSSM_DB_ATTRIBUTE_DATA &anAttribute = inAttributes->AttributeData[anIndex];
+            uint32 anAttributeIndex = attributeIndex(anAttribute.Info);
+                       // Make sure that the caller specified the attribute values in the correct format.
+                       if (anAttribute.Info.AttributeFormat != mAttributeVector[anAttributeIndex]->attributeFormat())
+                               CssmError::throwMe(CSSMERR_DL_INCOMPATIBLE_FIELD_FORMAT);
+
+            // If this attribute was specified before, throw.
+            if (aNumValues[anAttributeIndex] != ~(uint32)0)
+                CssmError::throwMe(CSSMERR_DL_FIELD_SPECIFIED_MULTIPLE);
+
+            aNumValues[anAttributeIndex] = anAttribute.NumberOfValues;
+            aValues[anAttributeIndex] = anAttribute.Value;
+        }
+    }
+
+    for (anIndex = 0; anIndex < mAttributeVector.size(); ++anIndex)
+    {
+        const MetaAttribute &aMetaAttribute = *mAttributeVector[anIndex];
+        uint32 aNumberOfValues = aNumValues[anIndex];
+        // Now call the parsingmodule for each attribute that
+        // wasn't explicitly specified and that has a parsingmodule.
+        if (aNumberOfValues == ~(uint32)0)
+            aNumberOfValues = aDataSize == 0 ? 0 : aMetaAttribute.parse(*inData, aValues[anIndex]);
+
+        // XXX When do we throw CSSMERR_DL_MISSING_VALUE?  Maybe if an
+               // attribute is part of a unique index.
+
+        // Now we have a valuelist for this attribute.  Let's encode it.
+        aMetaAttribute.packAttribute(inWriteSection, anOffset, aNumberOfValues, aValues[anIndex]);
+    }
+
+       inWriteSection.put(OffsetRecordSize, anOffset);
+    inWriteSection.size(anOffset);
+}
+
+inline void
+MetaRecord::unpackAttribute(const ReadSection &inReadSection,
+                                                       Allocator &inAllocator,
+                            CSSM_DB_ATTRIBUTE_DATA &inoutAttribute) const
+{
+    const MetaAttribute &aMetaAttribute = metaAttribute(inoutAttribute.Info);
+    // XXX: See ISSUES on whether AttributeFormat should be an outputvalue or not.
+       inoutAttribute.Info.AttributeFormat = aMetaAttribute.attributeFormat();
+    aMetaAttribute.unpackAttribute(inReadSection, inAllocator,
+                                   inoutAttribute.NumberOfValues,
+                                                                  inoutAttribute.Value);
+}
+
+void
+MetaRecord::unpackRecord(const ReadSection &inReadSection,
+                                                Allocator &inAllocator,
+                         CSSM_DB_RECORD_ATTRIBUTE_DATA_PTR inoutAttributes,
+                         CssmData *inoutData,
+                                                CSSM_QUERY_FLAGS inQueryFlags) const
+{
+       // XXX Use POD wrapper for inoutAttributes here.
+       TrackingAllocator anAllocator(inAllocator);
+       
+       try
+       {
+               if (inoutData)
+               {
+                       // XXX Treat KEY records specially.
+
+                       // If inQueryFlags & CSSM_QUERY_RETURN_DATA is true return the raw
+                       // key bits in the CSSM_KEY structure
+                       Range aDataRange = dataRange(inReadSection);
+                       inoutData->Length = aDataRange.mSize;
+                       inoutData->Data = inReadSection.allocCopyRange(aDataRange, anAllocator);
+               }
+
+               if (inoutAttributes)
+               {
+                       inoutAttributes->DataRecordType = dataRecordType();
+                       inoutAttributes->SemanticInformation = semanticInformation(inReadSection);
+                       uint32 anIndex = inoutAttributes->NumberOfAttributes;
+
+                       // Make sure that AttributeData is a valid array.
+                       if (anIndex > 0 && inoutAttributes->AttributeData == NULL)
+                               CssmError::throwMe(CSSM_ERRCODE_INVALID_POINTER);
+
+                       while (anIndex-- > 0)
+                       {
+                               unpackAttribute(inReadSection, anAllocator,
+                                                               inoutAttributes->AttributeData[anIndex]);
+                       }
+               }
+       }
+       catch (CssmError e)
+       {
+               if (e.osStatus() != CSSMERR_DL_DATABASE_CORRUPT)
+               {
+                       // clear all pointers so that nothing dangles back to the user
+                       if (inoutData)
+                       {
+                               inoutData->Data = NULL;
+                       }
+                       
+                       if (inoutAttributes)
+                       {
+                               unsigned i;
+                               for (i = 0; i < inoutAttributes->NumberOfAttributes; ++i)
+                               {
+                                       CSSM_DB_ATTRIBUTE_DATA& data = inoutAttributes->AttributeData[i];
+                                       
+                                       unsigned j;
+                                       for (j = 0; j < data.NumberOfValues; ++j)
+                                       {
+                                               data.Value[j].Data = NULL;
+                                       }
+                                       
+                                       data.Value = NULL;
+                                       
+                                       if (data.Info.AttributeNameFormat == CSSM_DB_ATTRIBUTE_NAME_AS_STRING)
+                                       {
+                                               data.Info.Label.AttributeName = NULL;
+                                       }
+                               }
+                       }
+               }
+               
+               throw;
+       }
+       catch (...)
+       {
+               // clear all pointers so that nothing dangles back to the user
+               if (inoutData)
+               {
+                       inoutData->Data = NULL;
+               }
+               
+               if (inoutAttributes)
+               {
+                       unsigned i;
+                       for (i = 0; i < inoutAttributes->NumberOfAttributes; ++i)
+                       {
+                               CSSM_DB_ATTRIBUTE_DATA& data = inoutAttributes->AttributeData[i];
+                               
+                               unsigned j;
+                               for (j = 0; j < data.NumberOfValues; ++j)
+                               {
+                                       data.Value[j].Data = NULL;
+                               }
+                               
+                               data.Value = NULL;
+                               
+                               if (data.Info.AttributeNameFormat == CSSM_DB_ATTRIBUTE_NAME_AS_STRING)
+                               {
+                                       data.Info.Label.AttributeName = NULL;
+                               }
+                       }
+               }
+               
+               throw;
+       }
+       
+
+       // Don't free anything the trackingAllocator allocated when it is destructed.
+       anAllocator.commit();
+}
+
+// Return the index (0 though NumAttributes - 1) of the attribute
+// represented by inAttributeInfo
+
+#ifndef        NDEBUG
+#define LOG_NAME_AS_STRING_FAIL                
+#endif
+uint32
+MetaRecord::attributeIndex(const CSSM_DB_ATTRIBUTE_INFO &inAttributeInfo) const
+{
+       uint32 anIndex;
+       switch (inAttributeInfo.AttributeNameFormat)
+       {
+           case CSSM_DB_ATTRIBUTE_NAME_AS_STRING:
+               {
+                       string aName(inAttributeInfo.Label.AttributeName);
+                       assert(aName.size() < 500);             // MDS leak debug
+                       NameStringMap::const_iterator it = mNameStringMap.find(aName);
+                       if (it == mNameStringMap.end()) {
+                               #ifdef  LOG_NAME_AS_STRING_FAIL
+                               printf("NAME_AS_STRING failure; attrName %s\n", 
+                                       inAttributeInfo.Label.AttributeName);
+                               for(it = mNameStringMap.begin();
+                                   it != mNameStringMap.end();
+                                       it++) {
+                                               printf("name %s val %d\n", it->first.c_str(), it->second);
+                               }
+                               #endif
+                               CssmError::throwMe(CSSMERR_DL_INVALID_FIELD_NAME);
+                       }
+                       anIndex = it->second;
+                       break;
+               }
+           case CSSM_DB_ATTRIBUTE_NAME_AS_OID:
+           {
+                       const CssmOid &aName = CssmOid::overlay(inAttributeInfo.Label.AttributeOID);
+                       NameOIDMap::const_iterator it = mNameOIDMap.find(aName);
+                       if (it == mNameOIDMap.end())
+                               CssmError::throwMe(CSSMERR_DL_INVALID_FIELD_NAME);
+                       anIndex = it->second;
+                       break;
+               }
+               case CSSM_DB_ATTRIBUTE_NAME_AS_INTEGER:
+               {
+                       uint32 aName = inAttributeInfo.Label.AttributeID;
+                       NameIntMap::const_iterator it = mNameIntMap.find(aName);
+                       if (it == mNameIntMap.end())
+                               CssmError::throwMe(CSSMERR_DL_INVALID_FIELD_NAME);
+                       anIndex = it->second;
+                       break;
+               }
+               default:
+                       CssmError::throwMe(CSSMERR_DL_INVALID_FIELD_NAME);
+                       break;
+       }
+
+       return anIndex;
+}
+
+const MetaAttribute &
+MetaRecord::metaAttribute(const CSSM_DB_ATTRIBUTE_INFO &inAttributeInfo) const
+{
+       return *mAttributeVector[attributeIndex(inAttributeInfo)];
+}
+
+// Create a packed record from the given inputs and the old packed record inReadSection.
+void
+MetaRecord::updateRecord(const ReadSection &inReadSection,
+                                                WriteSection &inWriteSection,
+                                                const CssmDbRecordAttributeData *inAttributes,
+                                                const CssmData *inData,
+                                                CSSM_DB_MODIFY_MODE inModifyMode) const
+{
+       TrackingAllocator anAllocator(Allocator::standard());
+
+       // modify the opaque data associated with the record
+       
+    uint32 aDataSize;
+       const uint8 *aDataData = NULL;
+    
+       if (inData)
+       {
+               // prepare to write new data
+        aDataSize = inData->Length;
+               aDataData = inData->Data;
+       }
+    else
+       {
+               // prepare to copy old data
+        Range aDataRange = dataRange(inReadSection);
+       aDataSize = aDataRange.mSize;
+               if (aDataSize)
+                       aDataData = inReadSection.range(aDataRange);
+       }
+
+       // compute the data offset; this will keep a running total of the record size
+    uint32 anOffset = OffsetAttributeOffsets + AtomSize * mAttributeVector.size();
+       
+       // write the appropriate data to the new record
+       inWriteSection.put(OffsetDataSize, aDataSize);
+       if (aDataSize)
+               anOffset = inWriteSection.put(anOffset, aDataSize, aDataData);
+
+       // unpack the old attributes since some of them may need to be preserved
+       
+       auto_array<CssmDbAttributeData> attributeData(mAttributeVector.size());
+
+       for (uint32 anAttributeIndex = mAttributeVector.size(); anAttributeIndex-- > 0; )
+       {
+               // unpack the old attribute data for this attribute index
+               const MetaAttribute &attribute = *mAttributeVector[anAttributeIndex];
+               attribute.unpackAttribute(inReadSection, anAllocator,
+                                                                 attributeData[anAttributeIndex].NumberOfValues,
+                                                                 attributeData[anAttributeIndex].Value);
+       }
+       
+       // retrieve the currrent semantic information
+       
+       uint32 oldSemanticInformation = semanticInformation(inReadSection);
+       
+       // process each input attribute as necessary, based on the modification mode
+       
+       if (inAttributes == NULL)
+       {
+               // make sure the modification mode is NONE, otherwise it's an
+               // error accordining to the spec
+               if (inModifyMode != CSSM_DB_MODIFY_ATTRIBUTE_NONE)
+                       CssmError::throwMe(CSSMERR_DL_INVALID_MODIFY_MODE);
+       }
+
+       else {
+       
+               // modify the semantic information
+
+               uint32 inSemanticInformation = inAttributes ? inAttributes->SemanticInformation : 0;
+
+               if (inModifyMode == CSSM_DB_MODIFY_ATTRIBUTE_ADD)
+                       oldSemanticInformation |= inSemanticInformation;
+
+               else if (inModifyMode == CSSM_DB_MODIFY_ATTRIBUTE_DELETE)
+                       oldSemanticInformation &= ~inSemanticInformation;
+               
+               else if (inModifyMode == CSSM_DB_MODIFY_ATTRIBUTE_REPLACE)
+                       oldSemanticInformation = inSemanticInformation;
+
+               uint32 anIndex = inAttributes->NumberOfAttributes;
+               if (anIndex > 0)
+                       Required(inAttributes->AttributeData);
+
+               // modify the attributes
+
+               while (anIndex-- > 0) {
+       
+                       const CssmDbAttributeData &anAttribute = inAttributes->at(anIndex);
+                       uint32 anAttributeIndex = attributeIndex(anAttribute.info());
+                       if (anAttribute.format() != mAttributeVector[anAttributeIndex]->attributeFormat())
+                               CssmError::throwMe(CSSMERR_DL_INCOMPATIBLE_FIELD_FORMAT);
+
+                       CssmDbAttributeData &oldAttribute = attributeData[anAttributeIndex];
+               
+                       // if the modify mode is ADD, merge new values with pre-existing values
+               
+                       if (inModifyMode == CSSM_DB_MODIFY_ATTRIBUTE_ADD)
+                               oldAttribute.add(anAttribute, anAllocator);
+
+                       // if the modify mode is DELETE, remove the indicated values, or remove
+                       // all values if none are specified
+
+                       else if (inModifyMode == CSSM_DB_MODIFY_ATTRIBUTE_DELETE)
+                       {
+                               if (anAttribute.size() == 0)
+                                       oldAttribute.deleteValues(anAllocator);
+                               else
+                                       oldAttribute.deleteValues(anAttribute, anAllocator);
+                       }
+               
+                       // if the modify mode is REPLACE, then replace the specified values, or
+                       // delete all values if no values are specified
+               
+                       else if (inModifyMode == CSSM_DB_MODIFY_ATTRIBUTE_REPLACE)
+                       {
+                               oldAttribute.deleteValues(anAllocator);
+                               if (anAttribute.size() > 0)
+                                       oldAttribute.add(anAttribute, anAllocator);
+                               else
+                                       // The spec says "all values are deleted or the the value is replaced
+                                       // with the default" but doesn't say which. We could call the parsing
+                                       // module for the attribute here...if they were implemented! But instead
+                                       // we choose "all values are deleted" and leave it at that.
+                                       ;
+                       }
+               }
+       }
+
+       // write the resulting attributes into the new record
+       
+       inWriteSection.put(OffsetSemanticInformation, oldSemanticInformation);
+
+       for (uint32 anIndex = 0; anIndex < mAttributeVector.size(); ++anIndex)
+       {
+               const MetaAttribute &metaAttribute = *mAttributeVector[anIndex];
+               metaAttribute.packAttribute(inWriteSection, anOffset,
+                                                                   attributeData[anIndex].NumberOfValues,
+                                                                       attributeData[anIndex].Value);
+       }
+       
+       inWriteSection.put(OffsetRecordSize, anOffset);
+       inWriteSection.size(anOffset);
+}
+