1/*
2 * Copyright (c) 2000-2001 Apple Computer, Inc. All Rights Reserved.
3 *
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
8 * using this file.
9 *
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.
16 */
17
18
19//
20// MetaRecord.cpp
21//
22
23#include "MetaRecord.h"
24#include <security_utilities/trackingallocator.h>
25#include <security_cdsa_utilities/cssmbridge.h>
26
27
28MetaRecord::MetaRecord(CSSM_DB_RECORDTYPE inRecordType) :
29    mRecordType(inRecordType)
30{
31}
32
33MetaRecord::MetaRecord(const CSSM_DB_RECORD_ATTRIBUTE_INFO &inInfo)
34:	mRecordType(inInfo.DataRecordType)
35{
36	try
37	{
38		setRecordAttributeInfo(inInfo);
39	}
40	catch (...)
41	{
42		for_each_delete(mAttributeVector.begin(), mAttributeVector.end());
43	}
44}
45
46MetaRecord::MetaRecord(CSSM_DB_RECORDTYPE inRelationID,
47                       uint32 inNumberOfAttributes,
48					   const CSSM_DB_SCHEMA_ATTRIBUTE_INFO *inAttributeInfo) :
49    mRecordType(inRelationID)
50{
51	try {
52		for (uint32 anIndex = 0; anIndex < inNumberOfAttributes; anIndex++)
53		{
54			string aName;
55			if (inAttributeInfo[anIndex].AttributeName)
56				aName = string(inAttributeInfo[anIndex].AttributeName);
57
58			const CssmData *aNameID = NULL;
59			if (inAttributeInfo[anIndex].AttributeNameID.Length > 0)
60				aNameID = &CssmData::overlay(inAttributeInfo[anIndex].AttributeNameID);
61
62			uint32 aNumber = inAttributeInfo[anIndex].AttributeId;
63			createAttribute(
64				inAttributeInfo[anIndex].AttributeName ? &aName : NULL,
65				aNameID, aNumber,
66				inAttributeInfo[anIndex].DataType);
67		}
68	}
69	catch (...)
70	{
71		for_each_delete(mAttributeVector.begin(), mAttributeVector.end());
72	}
73}
74
75MetaRecord::~MetaRecord()
76{
77	// for_each_delete(mAttributeVector.begin(), mAttributeVector.end());
78	AttributeVector::iterator it = mAttributeVector.begin();
79	while (it != mAttributeVector.end())
80	{
81		MetaAttribute* mat = *it++;
82		if (mat != NULL)
83		{
84			delete mat;
85		}
86	}
87}
88
89void
90MetaRecord::setRecordAttributeInfo(const CSSM_DB_RECORD_ATTRIBUTE_INFO &inInfo)
91{
92    for (uint32 anIndex = 0; anIndex < inInfo.NumberOfAttributes; anIndex++)
93    {
94        switch (inInfo.AttributeInfo[anIndex].AttributeNameFormat)
95        {
96            case CSSM_DB_ATTRIBUTE_NAME_AS_STRING:
97            {
98                string aName(inInfo.AttributeInfo[anIndex].Label.AttributeName);
99                createAttribute(&aName, nil, anIndex,
100								inInfo.AttributeInfo[anIndex].AttributeFormat);
101                break;
102            }
103            case CSSM_DB_ATTRIBUTE_NAME_AS_OID:
104            {
105                const CssmData &aNameID = CssmOid::overlay(inInfo.AttributeInfo[anIndex].Label.AttributeOID);
106                createAttribute(nil, &aNameID, anIndex,
107								inInfo.AttributeInfo[anIndex].AttributeFormat);
108                break;
109            }
110            case CSSM_DB_ATTRIBUTE_NAME_AS_INTEGER:
111            {
112                uint32 aNumber = inInfo.AttributeInfo[anIndex].Label.AttributeID;
113                createAttribute(nil, nil, aNumber,
114								inInfo.AttributeInfo[anIndex].AttributeFormat);
115                break;
116            }
117            default:
118                CssmError::throwMe(CSSMERR_DL_INVALID_FIELD_NAME);
119                break;
120        }
121    }
122}
123
124void
125MetaRecord::createAttribute(const string *inAttributeName,
126							const CssmOid *inAttributeOID,
127                            uint32 inAttributeID,
128							CSSM_DB_ATTRIBUTE_FORMAT inAttributeFormat)
129{
130	// Index of new element is current size of vector
131    uint32 anAttributeIndex = (uint32)mAttributeVector.size();
132    bool aInsertedAttributeName = false;
133    bool aInsertedAttributeOID = false;
134    bool aInsertedAttributeID = false;
135
136    if (inAttributeName)
137    {
138        if (!mNameStringMap.insert(NameStringMap::value_type(*inAttributeName, anAttributeIndex)).second)
139            CssmError::throwMe(CSSMERR_DL_FIELD_SPECIFIED_MULTIPLE);
140        aInsertedAttributeName = true;
141    }
142    try
143    {
144        if (inAttributeOID)
145        {
146            if (!mNameOIDMap.insert(NameOIDMap::value_type(*inAttributeOID, anAttributeIndex)).second)
147                CssmError::throwMe(CSSMERR_DL_FIELD_SPECIFIED_MULTIPLE);
148            aInsertedAttributeOID = true;
149        }
150
151		if (!mNameIntMap.insert(NameIntMap::value_type(inAttributeID, anAttributeIndex)).second)
152			CssmError::throwMe(CSSMERR_DL_FIELD_SPECIFIED_MULTIPLE);
153		aInsertedAttributeID = true;
154
155		// Note: this no longer throws INVALID_FIELD_NAME since the attribute will always have
156		// an attribute ID by which it is known
157
158		mAttributeVector.push_back(MetaAttribute::create(inAttributeFormat,
159			anAttributeIndex, inAttributeID));
160    }
161    catch(...)
162    {
163        if (aInsertedAttributeName)
164            mNameStringMap.erase(*inAttributeName);
165        if (aInsertedAttributeOID)
166            mNameOIDMap.erase(*inAttributeOID);
167        if (inAttributeID)
168            mNameIntMap.erase(inAttributeID);
169
170        throw;
171    }
172}
173
174
175// Create a packed record from the given inputs.
176void
177MetaRecord::packRecord(WriteSection &inWriteSection,
178                       const CSSM_DB_RECORD_ATTRIBUTE_DATA *inAttributes,
179                       const CssmData *inData) const
180{
181    uint32 aDataSize;
182    if (inData)
183        aDataSize = (uint32)inData->Length;
184    else
185        aDataSize = 0;
186
187    inWriteSection.put(OffsetDataSize, aDataSize);
188    uint32 anOffset = (uint32)(OffsetAttributeOffsets + AtomSize * mAttributeVector.size());
189    if (aDataSize)
190        anOffset = inWriteSection.put(anOffset, aDataSize, inData->Data);
191
192    vector<uint32> aNumValues(mAttributeVector.size(), ~(uint32)0);
193    vector<CSSM_DATA_PTR> aValues(mAttributeVector.size());
194    uint32 anIndex;
195
196    if (inAttributes == NULL)
197        inWriteSection.put(OffsetSemanticInformation, 0);
198    else
199    {
200        inWriteSection.put(OffsetSemanticInformation, inAttributes->SemanticInformation);
201
202        // Put the supplied attribute values into the list of attributes
203        // and values.
204        anIndex = inAttributes->NumberOfAttributes;
205        // Make sure that AttributeData is a valid array.
206		if (anIndex > 0)
207			Required(inAttributes->AttributeData);
208
209        while (anIndex-- > 0)
210        {
211            CSSM_DB_ATTRIBUTE_DATA &anAttribute = inAttributes->AttributeData[anIndex];
212            uint32 anAttributeIndex = attributeIndex(anAttribute.Info);
213			// Make sure that the caller specified the attribute values in the correct format.
214			if (anAttribute.Info.AttributeFormat != mAttributeVector[anAttributeIndex]->attributeFormat())
215				CssmError::throwMe(CSSMERR_DL_INCOMPATIBLE_FIELD_FORMAT);
216
217            // If this attribute was specified before, throw.
218            if (aNumValues[anAttributeIndex] != ~(uint32)0)
219                CssmError::throwMe(CSSMERR_DL_FIELD_SPECIFIED_MULTIPLE);
220
221            aNumValues[anAttributeIndex] = anAttribute.NumberOfValues;
222            aValues[anAttributeIndex] = anAttribute.Value;
223        }
224    }
225
226    for (anIndex = 0; anIndex < mAttributeVector.size(); ++anIndex)
227    {
228        const MetaAttribute &aMetaAttribute = *mAttributeVector[anIndex];
229        uint32 aNumberOfValues = aNumValues[anIndex];
230        // Now call the parsingmodule for each attribute that
231        // wasn't explicitly specified and that has a parsingmodule.
232        if (aNumberOfValues == ~(uint32)0)
233            aNumberOfValues = aDataSize == 0 ? 0 : aMetaAttribute.parse(*inData, aValues[anIndex]);
234
235        // XXX When do we throw CSSMERR_DL_MISSING_VALUE?  Maybe if an
236		// attribute is part of a unique index.
237
238        // Now we have a valuelist for this attribute.  Let's encode it.
239        aMetaAttribute.packAttribute(inWriteSection, anOffset, aNumberOfValues, aValues[anIndex]);
240    }
241
242	inWriteSection.put(OffsetRecordSize, anOffset);
243    inWriteSection.size(anOffset);
244}
245
246inline void
247MetaRecord::unpackAttribute(const ReadSection &inReadSection,
248							Allocator &inAllocator,
249                            CSSM_DB_ATTRIBUTE_DATA &inoutAttribute) const
250{
251    const MetaAttribute &aMetaAttribute = metaAttribute(inoutAttribute.Info);
252    // XXX: See ISSUES on whether AttributeFormat should be an outputvalue or not.
253	inoutAttribute.Info.AttributeFormat = aMetaAttribute.attributeFormat();
254    aMetaAttribute.unpackAttribute(inReadSection, inAllocator,
255                                   inoutAttribute.NumberOfValues,
256								   inoutAttribute.Value);
257}
258
259void
260MetaRecord::unpackRecord(const ReadSection &inReadSection,
261						 Allocator &inAllocator,
262                         CSSM_DB_RECORD_ATTRIBUTE_DATA_PTR inoutAttributes,
263                         CssmData *inoutData,
264						 CSSM_QUERY_FLAGS inQueryFlags) const
265{
266	// XXX Use POD wrapper for inoutAttributes here.
267	TrackingAllocator anAllocator(inAllocator);
268
269	try
270	{
271		if (inoutData)
272		{
273			// XXX Treat KEY records specially.
274
275			// If inQueryFlags & CSSM_QUERY_RETURN_DATA is true return the raw
276			// key bits in the CSSM_KEY structure
277			Range aDataRange = dataRange(inReadSection);
278			inoutData->Length = aDataRange.mSize;
279			inoutData->Data = inReadSection.allocCopyRange(aDataRange, anAllocator);
280		}
281
282		if (inoutAttributes)
283		{
284			inoutAttributes->DataRecordType = dataRecordType();
285			inoutAttributes->SemanticInformation = semanticInformation(inReadSection);
286			uint32 anIndex = inoutAttributes->NumberOfAttributes;
287
288			// Make sure that AttributeData is a valid array.
289			if (anIndex > 0 && inoutAttributes->AttributeData == NULL)
290				CssmError::throwMe(CSSM_ERRCODE_INVALID_POINTER);
291
292			while (anIndex-- > 0)
293			{
294				unpackAttribute(inReadSection, anAllocator,
295								inoutAttributes->AttributeData[anIndex]);
296			}
297		}
298	}
299	catch (CssmError e)
300	{
301		if (e.osStatus() != CSSMERR_DL_DATABASE_CORRUPT)
302		{
303			// clear all pointers so that nothing dangles back to the user
304			if (inoutData)
305			{
306				inoutData->Data = NULL;
307			}
308
309			if (inoutAttributes)
310			{
311				unsigned i;
312				for (i = 0; i < inoutAttributes->NumberOfAttributes; ++i)
313				{
314					CSSM_DB_ATTRIBUTE_DATA& data = inoutAttributes->AttributeData[i];
315
316					unsigned j;
317					for (j = 0; j < data.NumberOfValues; ++j)
318					{
319						data.Value[j].Data = NULL;
320					}
321
322					data.Value = NULL;
323
324					if (data.Info.AttributeNameFormat == CSSM_DB_ATTRIBUTE_NAME_AS_STRING)
325					{
326						data.Info.Label.AttributeName = NULL;
327					}
328				}
329			}
330		}
331
332		throw;
333	}
334	catch (...)
335	{
336		// clear all pointers so that nothing dangles back to the user
337		if (inoutData)
338		{
339			inoutData->Data = NULL;
340		}
341
342		if (inoutAttributes)
343		{
344			unsigned i;
345			for (i = 0; i < inoutAttributes->NumberOfAttributes; ++i)
346			{
347				CSSM_DB_ATTRIBUTE_DATA& data = inoutAttributes->AttributeData[i];
348
349				unsigned j;
350				for (j = 0; j < data.NumberOfValues; ++j)
351				{
352					data.Value[j].Data = NULL;
353				}
354
355				data.Value = NULL;
356
357				if (data.Info.AttributeNameFormat == CSSM_DB_ATTRIBUTE_NAME_AS_STRING)
358				{
359					data.Info.Label.AttributeName = NULL;
360				}
361			}
362		}
363
364		throw;
365	}
366
367
368	// Don't free anything the trackingAllocator allocated when it is destructed.
369	anAllocator.commit();
370}
371
372// Return the index (0 though NumAttributes - 1) of the attribute
373// represented by inAttributeInfo
374
375#ifndef	NDEBUG
376#define LOG_NAME_AS_STRING_FAIL
377#endif
378uint32
379MetaRecord::attributeIndex(const CSSM_DB_ATTRIBUTE_INFO &inAttributeInfo) const
380{
381	uint32 anIndex;
382	switch (inAttributeInfo.AttributeNameFormat)
383	{
384	    case CSSM_DB_ATTRIBUTE_NAME_AS_STRING:
385		{
386			string aName(inAttributeInfo.Label.AttributeName);
387			assert(aName.size() < 500);		// MDS leak debug
388			NameStringMap::const_iterator it = mNameStringMap.find(aName);
389			if (it == mNameStringMap.end()) {
390				#ifdef	LOG_NAME_AS_STRING_FAIL
391				printf("NAME_AS_STRING failure; attrName %s\n",
392					inAttributeInfo.Label.AttributeName);
393				for(it = mNameStringMap.begin();
394				    it != mNameStringMap.end();
395					it++) {
396						printf("name %s val %d\n", it->first.c_str(), it->second);
397				}
398				#endif
399				CssmError::throwMe(CSSMERR_DL_INVALID_FIELD_NAME);
400			}
401			anIndex = it->second;
402			break;
403		}
404	    case CSSM_DB_ATTRIBUTE_NAME_AS_OID:
405	    {
406			const CssmOid &aName = CssmOid::overlay(inAttributeInfo.Label.AttributeOID);
407			NameOIDMap::const_iterator it = mNameOIDMap.find(aName);
408			if (it == mNameOIDMap.end())
409				CssmError::throwMe(CSSMERR_DL_INVALID_FIELD_NAME);
410			anIndex = it->second;
411			break;
412		}
413		case CSSM_DB_ATTRIBUTE_NAME_AS_INTEGER:
414		{
415			uint32 aName = inAttributeInfo.Label.AttributeID;
416			NameIntMap::const_iterator it = mNameIntMap.find(aName);
417			if (it == mNameIntMap.end())
418				CssmError::throwMe(CSSMERR_DL_INVALID_FIELD_NAME);
419			anIndex = it->second;
420			break;
421		}
422		default:
423			CssmError::throwMe(CSSMERR_DL_INVALID_FIELD_NAME);
424			break;
425	}
426
427	return anIndex;
428}
429
430const MetaAttribute &
431MetaRecord::metaAttribute(const CSSM_DB_ATTRIBUTE_INFO &inAttributeInfo) const
432{
433	return *mAttributeVector[attributeIndex(inAttributeInfo)];
434}
435
436// Create a packed record from the given inputs and the old packed record inReadSection.
437void
438MetaRecord::updateRecord(const ReadSection &inReadSection,
439						 WriteSection &inWriteSection,
440						 const CssmDbRecordAttributeData *inAttributes,
441						 const CssmData *inData,
442						 CSSM_DB_MODIFY_MODE inModifyMode) const
443{
444	TrackingAllocator anAllocator(Allocator::standard());
445
446	// modify the opaque data associated with the record
447
448    uint32 aDataSize;
449	const uint8 *aDataData = NULL;
450
451	if (inData)
452	{
453		// prepare to write new data
454        aDataSize = (uint32)inData->Length;
455		aDataData = inData->Data;
456	}
457    else
458	{
459		// prepare to copy old data
460        Range aDataRange = dataRange(inReadSection);
461    	aDataSize = aDataRange.mSize;
462		if (aDataSize)
463			aDataData = inReadSection.range(aDataRange);
464	}
465
466	// compute the data offset; this will keep a running total of the record size
467        uint32 anOffset = (uint32)(OffsetAttributeOffsets + AtomSize * mAttributeVector.size());
468
469	// write the appropriate data to the new record
470	inWriteSection.put(OffsetDataSize, aDataSize);
471	if (aDataSize)
472		anOffset = inWriteSection.put(anOffset, aDataSize, aDataData);
473
474	// unpack the old attributes since some of them may need to be preserved
475
476	auto_array<CssmDbAttributeData> attributeData(mAttributeVector.size());
477
478	for (size_t anAttributeIndex = mAttributeVector.size(); anAttributeIndex-- > 0; )
479	{
480		// unpack the old attribute data for this attribute index
481		const MetaAttribute &attribute = *mAttributeVector[anAttributeIndex];
482		attribute.unpackAttribute(inReadSection, anAllocator,
483								  attributeData[anAttributeIndex].NumberOfValues,
484								  attributeData[anAttributeIndex].Value);
485	}
486
487	// retrieve the currrent semantic information
488
489	uint32 oldSemanticInformation = semanticInformation(inReadSection);
490
491	// process each input attribute as necessary, based on the modification mode
492
493	if (inAttributes == NULL)
494	{
495		// make sure the modification mode is NONE, otherwise it's an
496		// error accordining to the spec
497		if (inModifyMode != CSSM_DB_MODIFY_ATTRIBUTE_NONE)
498			CssmError::throwMe(CSSMERR_DL_INVALID_MODIFY_MODE);
499	}
500
501	else {
502
503		// modify the semantic information
504
505		uint32 inSemanticInformation = inAttributes ? inAttributes->SemanticInformation : 0;
506
507		if (inModifyMode == CSSM_DB_MODIFY_ATTRIBUTE_ADD)
508			oldSemanticInformation |= inSemanticInformation;
509
510		else if (inModifyMode == CSSM_DB_MODIFY_ATTRIBUTE_DELETE)
511			oldSemanticInformation &= ~inSemanticInformation;
512
513		else if (inModifyMode == CSSM_DB_MODIFY_ATTRIBUTE_REPLACE)
514			oldSemanticInformation = inSemanticInformation;
515
516		uint32 anIndex = inAttributes->NumberOfAttributes;
517		if (anIndex > 0)
518			Required(inAttributes->AttributeData);
519
520		// modify the attributes
521
522		while (anIndex-- > 0) {
523
524			const CssmDbAttributeData &anAttribute = inAttributes->at(anIndex);
525			uint32 anAttributeIndex = attributeIndex(anAttribute.info());
526			if (anAttribute.format() != mAttributeVector[anAttributeIndex]->attributeFormat())
527				CssmError::throwMe(CSSMERR_DL_INCOMPATIBLE_FIELD_FORMAT);
528
529			CssmDbAttributeData &oldAttribute = attributeData[anAttributeIndex];
530
531			// if the modify mode is ADD, merge new values with pre-existing values
532
533			if (inModifyMode == CSSM_DB_MODIFY_ATTRIBUTE_ADD)
534				oldAttribute.add(anAttribute, anAllocator);
535
536			// if the modify mode is DELETE, remove the indicated values, or remove
537			// all values if none are specified
538
539			else if (inModifyMode == CSSM_DB_MODIFY_ATTRIBUTE_DELETE)
540			{
541				if (anAttribute.size() == 0)
542					oldAttribute.deleteValues(anAllocator);
543				else
544					oldAttribute.deleteValues(anAttribute, anAllocator);
545			}
546
547			// if the modify mode is REPLACE, then replace the specified values, or
548			// delete all values if no values are specified
549
550			else if (inModifyMode == CSSM_DB_MODIFY_ATTRIBUTE_REPLACE)
551			{
552				oldAttribute.deleteValues(anAllocator);
553				if (anAttribute.size() > 0)
554					oldAttribute.add(anAttribute, anAllocator);
555				else
556					// The spec says "all values are deleted or the the value is replaced
557					// with the default" but doesn't say which. We could call the parsing
558					// module for the attribute here...if they were implemented! But instead
559					// we choose "all values are deleted" and leave it at that.
560					;
561			}
562		}
563	}
564
565	// write the resulting attributes into the new record
566
567	inWriteSection.put(OffsetSemanticInformation, oldSemanticInformation);
568
569	for (uint32 anIndex = 0; anIndex < mAttributeVector.size(); ++anIndex)
570	{
571		const MetaAttribute &metaAttribute = *mAttributeVector[anIndex];
572		metaAttribute.packAttribute(inWriteSection, anOffset,
573								    attributeData[anIndex].NumberOfValues,
574									attributeData[anIndex].Value);
575	}
576
577	inWriteSection.put(OffsetRecordSize, anOffset);
578	inWriteSection.size(anOffset);
579}
580
581