2 * Copyright (c) 2000-2001,2011-2012,2014 Apple Inc. All Rights Reserved.
4 * The contents of this file constitute Original Code as defined in and are
5 * subject to the Apple Public Source License Version 1.2 (the 'License').
6 * You may not use this file except in compliance with the License. Please obtain
7 * a copy of the License at http://www.apple.com/publicsource and read it before
10 * This Original Code and all software distributed under the License are
11 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS
12 * OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, INCLUDING WITHOUT
13 * LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
14 * PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. Please see the License for the
15 * specific language governing rights and limitations under the License.
23 #include "MetaRecord.h"
24 #include <security_utilities/trackingallocator.h>
25 #include <security_cdsa_utilities/cssmbridge.h>
28 MetaRecord::MetaRecord(CSSM_DB_RECORDTYPE inRecordType
) :
29 mRecordType(inRecordType
)
33 MetaRecord::MetaRecord(const CSSM_DB_RECORD_ATTRIBUTE_INFO
&inInfo
)
34 : mRecordType(inInfo
.DataRecordType
)
38 setRecordAttributeInfo(inInfo
);
42 for_each_delete(mAttributeVector
.begin(), mAttributeVector
.end());
46 MetaRecord::MetaRecord(CSSM_DB_RECORDTYPE inRelationID
,
47 uint32 inNumberOfAttributes
,
48 const CSSM_DB_SCHEMA_ATTRIBUTE_INFO
*inAttributeInfo
) :
49 mRecordType(inRelationID
)
52 for (uint32 anIndex
= 0; anIndex
< inNumberOfAttributes
; anIndex
++)
55 if (inAttributeInfo
[anIndex
].AttributeName
)
56 aName
= string(inAttributeInfo
[anIndex
].AttributeName
);
58 const CssmData
*aNameID
= NULL
;
59 if (inAttributeInfo
[anIndex
].AttributeNameID
.Length
> 0)
60 aNameID
= &CssmData::overlay(inAttributeInfo
[anIndex
].AttributeNameID
);
62 uint32 aNumber
= inAttributeInfo
[anIndex
].AttributeId
;
64 inAttributeInfo
[anIndex
].AttributeName
? &aName
: NULL
,
66 inAttributeInfo
[anIndex
].DataType
);
71 for_each_delete(mAttributeVector
.begin(), mAttributeVector
.end());
75 MetaRecord::~MetaRecord()
77 // for_each_delete(mAttributeVector.begin(), mAttributeVector.end());
78 AttributeVector::iterator it
= mAttributeVector
.begin();
79 while (it
!= mAttributeVector
.end())
81 MetaAttribute
* mat
= *it
++;
90 MetaRecord::setRecordAttributeInfo(const CSSM_DB_RECORD_ATTRIBUTE_INFO
&inInfo
)
92 for (uint32 anIndex
= 0; anIndex
< inInfo
.NumberOfAttributes
; anIndex
++)
94 switch (inInfo
.AttributeInfo
[anIndex
].AttributeNameFormat
)
96 case CSSM_DB_ATTRIBUTE_NAME_AS_STRING
:
98 string
aName(inInfo
.AttributeInfo
[anIndex
].Label
.AttributeName
);
99 createAttribute(&aName
, nil
, anIndex
,
100 inInfo
.AttributeInfo
[anIndex
].AttributeFormat
);
103 case CSSM_DB_ATTRIBUTE_NAME_AS_OID
:
105 const CssmData
&aNameID
= CssmOid::overlay(inInfo
.AttributeInfo
[anIndex
].Label
.AttributeOID
);
106 createAttribute(nil
, &aNameID
, anIndex
,
107 inInfo
.AttributeInfo
[anIndex
].AttributeFormat
);
110 case CSSM_DB_ATTRIBUTE_NAME_AS_INTEGER
:
112 uint32 aNumber
= inInfo
.AttributeInfo
[anIndex
].Label
.AttributeID
;
113 createAttribute(nil
, nil
, aNumber
,
114 inInfo
.AttributeInfo
[anIndex
].AttributeFormat
);
118 CssmError::throwMe(CSSMERR_DL_INVALID_FIELD_NAME
);
124 MetaRecord::createAttribute(const string
*inAttributeName
,
125 const CssmOid
*inAttributeOID
,
126 uint32 inAttributeID
,
127 CSSM_DB_ATTRIBUTE_FORMAT inAttributeFormat
)
129 // Index of new element is current size of vector
130 uint32 anAttributeIndex
= (uint32
)mAttributeVector
.size();
131 bool aInsertedAttributeName
= false;
132 bool aInsertedAttributeOID
= false;
133 bool aInsertedAttributeID
= false;
137 if (!mNameStringMap
.insert(NameStringMap::value_type(*inAttributeName
, anAttributeIndex
)).second
)
138 CssmError::throwMe(CSSMERR_DL_FIELD_SPECIFIED_MULTIPLE
);
139 aInsertedAttributeName
= true;
145 if (!mNameOIDMap
.insert(NameOIDMap::value_type(*inAttributeOID
, anAttributeIndex
)).second
)
146 CssmError::throwMe(CSSMERR_DL_FIELD_SPECIFIED_MULTIPLE
);
147 aInsertedAttributeOID
= true;
150 if (!mNameIntMap
.insert(NameIntMap::value_type(inAttributeID
, anAttributeIndex
)).second
)
151 CssmError::throwMe(CSSMERR_DL_FIELD_SPECIFIED_MULTIPLE
);
152 aInsertedAttributeID
= true;
154 // Note: this no longer throws INVALID_FIELD_NAME since the attribute will always have
155 // an attribute ID by which it is known
157 mAttributeVector
.push_back(MetaAttribute::create(inAttributeFormat
,
158 anAttributeIndex
, inAttributeID
));
162 if (aInsertedAttributeName
)
163 mNameStringMap
.erase(*inAttributeName
);
164 if (aInsertedAttributeOID
)
165 mNameOIDMap
.erase(*inAttributeOID
);
167 mNameIntMap
.erase(inAttributeID
);
174 // Create a packed record from the given inputs.
176 MetaRecord::packRecord(WriteSection
&inWriteSection
,
177 const CSSM_DB_RECORD_ATTRIBUTE_DATA
*inAttributes
,
178 const CssmData
*inData
) const
182 aDataSize
= (uint32
)inData
->Length
;
186 inWriteSection
.put(OffsetDataSize
, aDataSize
);
187 uint32 anOffset
= (uint32
)(OffsetAttributeOffsets
+ AtomSize
* mAttributeVector
.size());
189 anOffset
= inWriteSection
.put(anOffset
, aDataSize
, inData
->Data
);
191 vector
<uint32
> aNumValues(mAttributeVector
.size(), ~(uint32
)0);
192 vector
<CSSM_DATA_PTR
> aValues(mAttributeVector
.size());
195 if (inAttributes
== NULL
)
196 inWriteSection
.put(OffsetSemanticInformation
, 0);
199 inWriteSection
.put(OffsetSemanticInformation
, inAttributes
->SemanticInformation
);
201 // Put the supplied attribute values into the list of attributes
203 anIndex
= inAttributes
->NumberOfAttributes
;
204 // Make sure that AttributeData is a valid array.
206 Required(inAttributes
->AttributeData
);
208 while (anIndex
-- > 0)
210 CSSM_DB_ATTRIBUTE_DATA
&anAttribute
= inAttributes
->AttributeData
[anIndex
];
211 uint32 anAttributeIndex
= attributeIndex(anAttribute
.Info
);
212 // Make sure that the caller specified the attribute values in the correct format.
213 if (anAttribute
.Info
.AttributeFormat
!= mAttributeVector
[anAttributeIndex
]->attributeFormat())
214 CssmError::throwMe(CSSMERR_DL_INCOMPATIBLE_FIELD_FORMAT
);
216 // If this attribute was specified before, throw.
217 if (aNumValues
[anAttributeIndex
] != ~(uint32
)0)
218 CssmError::throwMe(CSSMERR_DL_FIELD_SPECIFIED_MULTIPLE
);
220 aNumValues
[anAttributeIndex
] = anAttribute
.NumberOfValues
;
221 aValues
[anAttributeIndex
] = anAttribute
.Value
;
225 for (anIndex
= 0; anIndex
< mAttributeVector
.size(); ++anIndex
)
227 const MetaAttribute
&aMetaAttribute
= *mAttributeVector
[anIndex
];
228 uint32 aNumberOfValues
= aNumValues
[anIndex
];
229 // Now call the parsingmodule for each attribute that
230 // wasn't explicitly specified and that has a parsingmodule.
231 if (aNumberOfValues
== ~(uint32
)0)
232 aNumberOfValues
= aDataSize
== 0 ? 0 : aMetaAttribute
.parse(*inData
, aValues
[anIndex
]);
234 // XXX When do we throw CSSMERR_DL_MISSING_VALUE? Maybe if an
235 // attribute is part of a unique index.
237 // Now we have a valuelist for this attribute. Let's encode it.
238 aMetaAttribute
.packAttribute(inWriteSection
, anOffset
, aNumberOfValues
, aValues
[anIndex
]);
241 inWriteSection
.put(OffsetRecordSize
, anOffset
);
242 inWriteSection
.size(anOffset
);
246 MetaRecord::unpackAttribute(const ReadSection
&inReadSection
,
247 Allocator
&inAllocator
,
248 CSSM_DB_ATTRIBUTE_DATA
&inoutAttribute
) const
250 const MetaAttribute
&aMetaAttribute
= metaAttribute(inoutAttribute
.Info
);
251 // XXX: See ISSUES on whether AttributeFormat should be an outputvalue or not.
252 inoutAttribute
.Info
.AttributeFormat
= aMetaAttribute
.attributeFormat();
253 aMetaAttribute
.unpackAttribute(inReadSection
, inAllocator
,
254 inoutAttribute
.NumberOfValues
,
255 inoutAttribute
.Value
);
259 MetaRecord::unpackRecord(const ReadSection
&inReadSection
,
260 Allocator
&inAllocator
,
261 CSSM_DB_RECORD_ATTRIBUTE_DATA_PTR inoutAttributes
,
263 CSSM_QUERY_FLAGS inQueryFlags
) const
265 // XXX Use POD wrapper for inoutAttributes here.
266 TrackingAllocator
anAllocator(inAllocator
);
272 // XXX Treat KEY records specially.
274 // If inQueryFlags & CSSM_QUERY_RETURN_DATA is true return the raw
275 // key bits in the CSSM_KEY structure
276 Range aDataRange
= dataRange(inReadSection
);
277 inoutData
->Length
= aDataRange
.mSize
;
278 inoutData
->Data
= inReadSection
.allocCopyRange(aDataRange
, anAllocator
);
283 inoutAttributes
->DataRecordType
= dataRecordType();
284 inoutAttributes
->SemanticInformation
= semanticInformation(inReadSection
);
285 uint32 anIndex
= inoutAttributes
->NumberOfAttributes
;
287 // Make sure that AttributeData is a valid array.
288 if (anIndex
> 0 && inoutAttributes
->AttributeData
== NULL
)
289 CssmError::throwMe(CSSM_ERRCODE_INVALID_POINTER
);
291 while (anIndex
-- > 0)
293 unpackAttribute(inReadSection
, anAllocator
,
294 inoutAttributes
->AttributeData
[anIndex
]);
300 if (e
.osStatus() != CSSMERR_DL_DATABASE_CORRUPT
)
302 // clear all pointers so that nothing dangles back to the user
305 inoutData
->Data
= NULL
;
311 for (i
= 0; i
< inoutAttributes
->NumberOfAttributes
; ++i
)
313 CSSM_DB_ATTRIBUTE_DATA
& data
= inoutAttributes
->AttributeData
[i
];
316 for (j
= 0; j
< data
.NumberOfValues
; ++j
)
318 data
.Value
[j
].Data
= NULL
;
323 if (data
.Info
.AttributeNameFormat
== CSSM_DB_ATTRIBUTE_NAME_AS_STRING
)
325 data
.Info
.Label
.AttributeName
= NULL
;
335 // clear all pointers so that nothing dangles back to the user
338 inoutData
->Data
= NULL
;
344 for (i
= 0; i
< inoutAttributes
->NumberOfAttributes
; ++i
)
346 CSSM_DB_ATTRIBUTE_DATA
& data
= inoutAttributes
->AttributeData
[i
];
349 for (j
= 0; j
< data
.NumberOfValues
; ++j
)
351 data
.Value
[j
].Data
= NULL
;
356 if (data
.Info
.AttributeNameFormat
== CSSM_DB_ATTRIBUTE_NAME_AS_STRING
)
358 data
.Info
.Label
.AttributeName
= NULL
;
367 // Don't free anything the trackingAllocator allocated when it is destructed.
368 anAllocator
.commit();
371 // Return the index (0 though NumAttributes - 1) of the attribute
372 // represented by inAttributeInfo
375 #define LOG_NAME_AS_STRING_FAIL
378 MetaRecord::attributeIndex(const CSSM_DB_ATTRIBUTE_INFO
&inAttributeInfo
) const
381 switch (inAttributeInfo
.AttributeNameFormat
)
383 case CSSM_DB_ATTRIBUTE_NAME_AS_STRING
:
385 string
aName(inAttributeInfo
.Label
.AttributeName
);
386 assert(aName
.size() < 500); // MDS leak debug
387 NameStringMap::const_iterator it
= mNameStringMap
.find(aName
);
388 if (it
== mNameStringMap
.end()) {
389 #ifdef LOG_NAME_AS_STRING_FAIL
390 printf("NAME_AS_STRING failure; attrName %s\n",
391 inAttributeInfo
.Label
.AttributeName
);
392 for(it
= mNameStringMap
.begin();
393 it
!= mNameStringMap
.end();
395 printf("name %s val %d\n", it
->first
.c_str(), it
->second
);
398 CssmError::throwMe(CSSMERR_DL_INVALID_FIELD_NAME
);
400 anIndex
= it
->second
;
403 case CSSM_DB_ATTRIBUTE_NAME_AS_OID
:
405 const CssmOid
&aName
= CssmOid::overlay(inAttributeInfo
.Label
.AttributeOID
);
406 NameOIDMap::const_iterator it
= mNameOIDMap
.find(aName
);
407 if (it
== mNameOIDMap
.end())
408 CssmError::throwMe(CSSMERR_DL_INVALID_FIELD_NAME
);
409 anIndex
= it
->second
;
412 case CSSM_DB_ATTRIBUTE_NAME_AS_INTEGER
:
414 uint32 aName
= inAttributeInfo
.Label
.AttributeID
;
415 NameIntMap::const_iterator it
= mNameIntMap
.find(aName
);
416 if (it
== mNameIntMap
.end())
417 CssmError::throwMe(CSSMERR_DL_INVALID_FIELD_NAME
);
418 anIndex
= it
->second
;
422 CssmError::throwMe(CSSMERR_DL_INVALID_FIELD_NAME
);
428 const MetaAttribute
&
429 MetaRecord::metaAttribute(const CSSM_DB_ATTRIBUTE_INFO
&inAttributeInfo
) const
431 return *mAttributeVector
[attributeIndex(inAttributeInfo
)];
434 // Create a packed record from the given inputs and the old packed record inReadSection.
436 MetaRecord::updateRecord(const ReadSection
&inReadSection
,
437 WriteSection
&inWriteSection
,
438 const CssmDbRecordAttributeData
*inAttributes
,
439 const CssmData
*inData
,
440 CSSM_DB_MODIFY_MODE inModifyMode
) const
442 TrackingAllocator
anAllocator(Allocator::standard());
444 // modify the opaque data associated with the record
447 const uint8
*aDataData
= NULL
;
451 // prepare to write new data
452 aDataSize
= (uint32
)inData
->Length
;
453 aDataData
= inData
->Data
;
457 // prepare to copy old data
458 Range aDataRange
= dataRange(inReadSection
);
459 aDataSize
= aDataRange
.mSize
;
461 aDataData
= inReadSection
.range(aDataRange
);
464 // compute the data offset; this will keep a running total of the record size
465 uint32 anOffset
= (uint32
)(OffsetAttributeOffsets
+ AtomSize
* mAttributeVector
.size());
467 // write the appropriate data to the new record
468 inWriteSection
.put(OffsetDataSize
, aDataSize
);
470 anOffset
= inWriteSection
.put(anOffset
, aDataSize
, aDataData
);
472 // unpack the old attributes since some of them may need to be preserved
474 auto_array
<CssmDbAttributeData
> attributeData(mAttributeVector
.size());
476 for (size_t anAttributeIndex
= mAttributeVector
.size(); anAttributeIndex
-- > 0; )
478 // unpack the old attribute data for this attribute index
479 const MetaAttribute
&attribute
= *mAttributeVector
[anAttributeIndex
];
480 attribute
.unpackAttribute(inReadSection
, anAllocator
,
481 attributeData
[anAttributeIndex
].NumberOfValues
,
482 attributeData
[anAttributeIndex
].Value
);
485 // retrieve the currrent semantic information
487 uint32 oldSemanticInformation
= semanticInformation(inReadSection
);
489 // process each input attribute as necessary, based on the modification mode
491 if (inAttributes
== NULL
)
493 // make sure the modification mode is NONE, otherwise it's an
494 // error accordining to the spec
495 if (inModifyMode
!= CSSM_DB_MODIFY_ATTRIBUTE_NONE
)
496 CssmError::throwMe(CSSMERR_DL_INVALID_MODIFY_MODE
);
501 // modify the semantic information
503 uint32 inSemanticInformation
= inAttributes
? inAttributes
->SemanticInformation
: 0;
505 if (inModifyMode
== CSSM_DB_MODIFY_ATTRIBUTE_ADD
)
506 oldSemanticInformation
|= inSemanticInformation
;
508 else if (inModifyMode
== CSSM_DB_MODIFY_ATTRIBUTE_DELETE
)
509 oldSemanticInformation
&= ~inSemanticInformation
;
511 else if (inModifyMode
== CSSM_DB_MODIFY_ATTRIBUTE_REPLACE
)
512 oldSemanticInformation
= inSemanticInformation
;
514 uint32 anIndex
= inAttributes
->NumberOfAttributes
;
516 Required(inAttributes
->AttributeData
);
518 // modify the attributes
520 while (anIndex
-- > 0) {
522 const CssmDbAttributeData
&anAttribute
= inAttributes
->at(anIndex
);
523 uint32 anAttributeIndex
= attributeIndex(anAttribute
.info());
524 if (anAttribute
.format() != mAttributeVector
[anAttributeIndex
]->attributeFormat())
525 CssmError::throwMe(CSSMERR_DL_INCOMPATIBLE_FIELD_FORMAT
);
527 CssmDbAttributeData
&oldAttribute
= attributeData
[anAttributeIndex
];
529 // if the modify mode is ADD, merge new values with pre-existing values
531 if (inModifyMode
== CSSM_DB_MODIFY_ATTRIBUTE_ADD
)
532 oldAttribute
.add(anAttribute
, anAllocator
);
534 // if the modify mode is DELETE, remove the indicated values, or remove
535 // all values if none are specified
537 else if (inModifyMode
== CSSM_DB_MODIFY_ATTRIBUTE_DELETE
)
539 if (anAttribute
.size() == 0)
540 oldAttribute
.deleteValues(anAllocator
);
542 oldAttribute
.deleteValues(anAttribute
, anAllocator
);
545 // if the modify mode is REPLACE, then replace the specified values, or
546 // delete all values if no values are specified
548 else if (inModifyMode
== CSSM_DB_MODIFY_ATTRIBUTE_REPLACE
)
550 oldAttribute
.deleteValues(anAllocator
);
551 if (anAttribute
.size() > 0)
552 oldAttribute
.add(anAttribute
, anAllocator
);
554 // The spec says "all values are deleted or the the value is replaced
555 // with the default" but doesn't say which. We could call the parsing
556 // module for the attribute here...if they were implemented! But instead
557 // we choose "all values are deleted" and leave it at that.
563 // write the resulting attributes into the new record
565 inWriteSection
.put(OffsetSemanticInformation
, oldSemanticInformation
);
567 for (uint32 anIndex
= 0; anIndex
< mAttributeVector
.size(); ++anIndex
)
569 const MetaAttribute
&metaAttribute
= *mAttributeVector
[anIndex
];
570 metaAttribute
.packAttribute(inWriteSection
, anOffset
,
571 attributeData
[anIndex
].NumberOfValues
,
572 attributeData
[anIndex
].Value
);
575 inWriteSection
.put(OffsetRecordSize
, anOffset
);
576 inWriteSection
.size(anOffset
);