X-Git-Url: https://git.saurik.com/apple/security.git/blobdiff_plain/72a12576750f52947eb043106ba5c12c0d07decf..b1ab9ed8d0e0f1c3b66d7daa8fd5564444c56195:/libsecurity_filedb/lib/MetaRecord.cpp diff --git a/libsecurity_filedb/lib/MetaRecord.cpp b/libsecurity_filedb/lib/MetaRecord.cpp new file mode 100644 index 00000000..d5ee1be4 --- /dev/null +++ b/libsecurity_filedb/lib/MetaRecord.cpp @@ -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 +#include + + +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 aNumValues(mAttributeVector.size(), ~(uint32)0); + vector 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 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); +} +