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