securestorage.cpp
1 /* 2 * Copyright (c) 2000-2001,2011-2014 Apple 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 #include "securestorage.h" 20 #include <security_cdsa_client/genkey.h> 21 //#include <Security/Access.h> //@@@CONV 22 #include <security_utilities/osxcode.h> 23 #include <memory> 24 25 using namespace CssmClient; 26 //using namespace KeychainCore; 27 28 // 29 // Manage CSPDL attachments 30 // 31 CSPDLImpl::CSPDLImpl(const Guid &guid) 32 : CSPImpl(Cssm::standard()->autoModule(guid)), 33 DLImpl(CSPImpl::module()) 34 { 35 } 36 37 CSPDLImpl::CSPDLImpl(const Module &module) 38 : CSPImpl(module), 39 DLImpl(module) 40 { 41 } 42 43 CSPDLImpl::~CSPDLImpl() 44 try 45 { 46 } 47 catch (...) 48 { 49 return; // Prevent re-throw of exception [function-try-block] 50 } 51 52 Allocator &CSPDLImpl::allocator() const 53 { 54 DLImpl::allocator(); return CSPImpl::allocator(); 55 } 56 57 void CSPDLImpl::allocator(Allocator &alloc) 58 { 59 CSPImpl::allocator(alloc); DLImpl::allocator(alloc); 60 } 61 62 bool CSPDLImpl::operator <(const CSPDLImpl &other) const 63 { 64 return (static_cast<const CSPImpl &>(*this) < static_cast<const CSPImpl &>(other) || 65 (!(static_cast<const CSPImpl &>(other) < static_cast<const CSPImpl &>(*this)) 66 && static_cast<const DLImpl &>(*this) < static_cast<const DLImpl &>(other))); 67 } 68 69 bool CSPDLImpl::operator ==(const CSPDLImpl &other) const 70 { 71 return (static_cast<const CSPImpl &>(*this) == static_cast<const CSPImpl &>(other) 72 && static_cast<const DLImpl &>(*this) == static_cast<const DLImpl &>(other)); 73 } 74 75 CSSM_SERVICE_MASK CSPDLImpl::subserviceMask() const 76 { 77 return CSPImpl::subserviceType() | DLImpl::subserviceType(); 78 } 79 80 void CSPDLImpl::subserviceId(uint32 id) 81 { 82 CSPImpl::subserviceId(id); DLImpl::subserviceId(id); 83 } 84 85 86 // 87 // Secure storage 88 // 89 SSCSPDLImpl::SSCSPDLImpl(const Guid &guid) : CSPDLImpl::CSPDLImpl(guid) 90 { 91 } 92 93 SSCSPDLImpl::SSCSPDLImpl(const Module &module) : CSPDLImpl::CSPDLImpl(module) 94 { 95 } 96 97 SSCSPDLImpl::~SSCSPDLImpl() 98 { 99 } 100 101 DbImpl * 102 SSCSPDLImpl::newDb(const char *inDbName, const CSSM_NET_ADDRESS *inDbLocation) 103 { 104 return new SSDbImpl(SSCSPDL(this), inDbName, inDbLocation); 105 } 106 107 108 // 109 // SSDbImpl -- Secure Storage Database Implementation 110 // 111 SSDbImpl::SSDbImpl(const SSCSPDL &cspdl, const char *inDbName, 112 const CSSM_NET_ADDRESS *inDbLocation) 113 : DbImpl(cspdl, inDbName, inDbLocation) 114 { 115 } 116 117 SSDbImpl::~SSDbImpl() 118 { 119 } 120 121 void 122 SSDbImpl::create() 123 { 124 DbImpl::create(); 125 } 126 127 void 128 SSDbImpl::open() 129 { 130 DbImpl::open(); 131 } 132 133 DbUniqueRecord 134 SSDbImpl::insert(CSSM_DB_RECORDTYPE recordType, 135 const CSSM_DB_RECORD_ATTRIBUTE_DATA *attributes, 136 const CSSM_DATA *data) 137 { 138 return DbImpl::insert(recordType, attributes, data); 139 } 140 141 SSDbUniqueRecord 142 SSDbImpl::ssInsert(CSSM_DB_RECORDTYPE recordType, 143 const CSSM_DB_RECORD_ATTRIBUTE_DATA *attributes, 144 const CSSM_DATA *data, 145 const CSSM_RESOURCE_CONTROL_CONTEXT *rc) 146 { 147 // Get the handle of the DL underlying this CSPDL. 148 CSSM_DL_DB_HANDLE dldbh; 149 passThrough(CSSM_APPLECSPDL_DB_GET_HANDLE, NULL, 150 reinterpret_cast<void **>(&dldbh)); 151 152 // Turn off autocommit on the underlying DL and remember the old state. 153 CSSM_BOOL autoCommit = CSSM_TRUE; 154 check(CSSM_DL_PassThrough(dldbh, CSSM_APPLEFILEDL_TOGGLE_AUTOCOMMIT, 155 0, reinterpret_cast<void **>(&autoCommit))); 156 SSGroup group(SSDb(this), rc); 157 const CSSM_ACCESS_CREDENTIALS *cred = rc ? rc->AccessCred : NULL; 158 try 159 { 160 SSDbUniqueRecord ssdbur = ssInsert(recordType, attributes, data, group, cred); 161 if (autoCommit) 162 { 163 // autoCommit was on so commit now that we are done and turn 164 // it back on. 165 check(CSSM_DL_PassThrough(dldbh, CSSM_APPLEFILEDL_COMMIT, NULL, NULL)); 166 CSSM_DL_PassThrough(dldbh, CSSM_APPLEFILEDL_TOGGLE_AUTOCOMMIT, 167 reinterpret_cast<const void *>(autoCommit), NULL); 168 } 169 return ssdbur; 170 } 171 catch(...) 172 { 173 try { group->deleteKey(cred); } catch (...) {} 174 if (autoCommit) 175 { 176 // autoCommit was off so rollback since we failed and turn 177 // autoCommit back on. 178 CSSM_DL_PassThrough(dldbh, CSSM_APPLEFILEDL_ROLLBACK, NULL, NULL); 179 CSSM_DL_PassThrough(dldbh, CSSM_APPLEFILEDL_TOGGLE_AUTOCOMMIT, 180 reinterpret_cast<const void *>(autoCommit), NULL); 181 } 182 throw; 183 } 184 } 185 186 SSDbUniqueRecord 187 SSDbImpl::ssInsert(CSSM_DB_RECORDTYPE recordType, 188 const CSSM_DB_RECORD_ATTRIBUTE_DATA *attributes, 189 const CSSM_DATA *data, const SSGroup &group, 190 const CSSM_ACCESS_CREDENTIALS *cred) 191 { 192 // Create an encoded dataBlob for this item. 193 CssmDataContainer dataBlob(allocator()); 194 group->encodeDataBlob(data, cred, dataBlob); 195 196 // Insert the record with the new juicy dataBlob. 197 return SSDbUniqueRecord(safe_cast<SSDbUniqueRecordImpl *> 198 (&(*DbImpl::insert(recordType, attributes, &dataBlob)))); 199 } 200 201 202 // DbCursorMaker 203 DbCursorImpl * 204 SSDbImpl::newDbCursor(const CSSM_QUERY &query, Allocator &allocator) 205 { 206 return new SSDbCursorImpl(Db(this), query, allocator); 207 } 208 209 DbCursorImpl * 210 SSDbImpl::newDbCursor(uint32 capacity, Allocator &allocator) 211 { 212 return new SSDbCursorImpl(Db(this), capacity, allocator); 213 } 214 215 216 // SSDbUniqueRecordMaker 217 DbUniqueRecordImpl * 218 SSDbImpl::newDbUniqueRecord() 219 { 220 return new SSDbUniqueRecordImpl(Db(this)); 221 } 222 223 224 // 225 // SSGroup -- Group key with acl, used to protect a group of items. 226 // 227 // @@@ Get this from a shared spot. 228 CSSM_DB_NAME_ATTR(SSGroupImpl::kLabel, 6, (char*) "Label", 0, NULL, BLOB); 229 230 // Create a new group. 231 SSGroupImpl::SSGroupImpl(const SSDb &ssDb, 232 const CSSM_RESOURCE_CONTROL_CONTEXT *credAndAclEntry) 233 : KeyImpl(ssDb->csp()), mLabel(ssDb->allocator()) 234 { 235 mLabel.Length = kLabelSize; 236 mLabel.Data = reinterpret_cast<uint8 *> 237 (mLabel.mAllocator.malloc(mLabel.Length)); 238 239 // Get our csp and set up a random number generation context. 240 CSP csp(this->csp()); 241 Random random(csp, CSSM_ALGID_APPLE_YARROW); 242 243 // Generate a kLabelSize byte random number that will be the label of 244 // the key which we store in the dataBlob. 245 random.generate(mLabel, (uint32)mLabel.Length); 246 247 // Overwrite the first 4 bytes with the magic cookie for a group. 248 reinterpret_cast<uint32 *>(mLabel.Data)[0] = h2n(uint32(kGroupMagic)); 249 250 // @@@ Ensure that the label is unique (Chance of collision is 2^80 -- 251 // birthday paradox). 252 253 // Generate a permanent 3DES key that we will use to encrypt the data. 254 GenerateKey genKey(csp, CSSM_ALGID_3DES_3KEY, 192); 255 genKey.database(ssDb); 256 257 // Set the acl of the key correctly here 258 genKey.rcc(credAndAclEntry); 259 260 // Generate the key 261 genKey(*this, KeySpec(CSSM_KEYUSE_ENCRYPT|CSSM_KEYUSE_DECRYPT, 262 CSSM_KEYATTR_PERMANENT|CSSM_KEYATTR_SENSITIVE, 263 mLabel)); 264 265 // Activate ourself so CSSM_FreeKey will get called when we go out of 266 // scope. 267 activate(); 268 } 269 270 // Lookup an existing group based on a dataBlob. 271 SSGroupImpl::SSGroupImpl(const SSDb &ssDb, const CSSM_DATA &dataBlob) 272 : KeyImpl(ssDb->csp()), mLabel(ssDb->allocator()) 273 { 274 if (dataBlob.Length < kLabelSize + kIVSize) 275 CssmError::throwMe(CSSMERR_DL_RECORD_NOT_FOUND); // Not a SS record 276 277 mLabel = CssmData(dataBlob.Data, kLabelSize); 278 if (*reinterpret_cast<const uint32 *>(mLabel.Data) != h2n (uint32(kGroupMagic))) 279 CssmError::throwMe(CSSMERR_DL_RECORD_NOT_FOUND); // Not a SS record 280 281 // Look up the symmetric key with that label. 282 DbCursor cursor(new DbDbCursorImpl(ssDb, 0, Allocator::standard())); 283 cursor->recordType(CSSM_DL_DB_RECORD_SYMMETRIC_KEY); 284 cursor->add(CSSM_DB_EQUAL, kLabel, mLabel); 285 286 DbUniqueRecord keyId; 287 CssmDataContainer keyData(ssDb->allocator()); 288 if (!cursor->next(NULL, &keyData, keyId)) 289 CssmError::throwMe(CSSMERR_DL_RECORD_NOT_FOUND); // The key can't be found 290 291 // Set the key part of ourself. 292 static_cast<CSSM_KEY &>(*this) = 293 *reinterpret_cast<const CSSM_KEY *>(keyData.Data); 294 295 // Activate ourself so CSSM_FreeKey will get called when we go out of 296 // scope. 297 activate(); 298 } 299 300 bool 301 SSGroupImpl::isGroup(const CSSM_DATA &dataBlob) 302 { 303 return dataBlob.Length >= kLabelSize + kIVSize 304 && *reinterpret_cast<const uint32 *>(dataBlob.Data) == h2n(uint32(kGroupMagic)); 305 } 306 307 const CssmData 308 SSGroupImpl::label() const 309 { 310 return mLabel; 311 } 312 313 void 314 SSGroupImpl::decodeDataBlob(const CSSM_DATA &dataBlob, 315 const CSSM_ACCESS_CREDENTIALS *cred, 316 Allocator &allocator, CSSM_DATA &data) 317 { 318 // First get the IV and the cipherText from the blob. 319 CssmData iv(&dataBlob.Data[kLabelSize], kIVSize); 320 CssmData cipherText(&dataBlob.Data[kLabelSize + kIVSize], 321 dataBlob.Length - (kLabelSize + kIVSize)); 322 323 CssmDataContainer plainText1(allocator); 324 CssmDataContainer plainText2(allocator); 325 // Decrypt the data 326 // @@@ Don't use staged decrypt once the AppleCSPDL can do combo 327 // encryption. 328 // Setup decryption context 329 Decrypt decrypt(csp(), algorithm()); 330 decrypt.mode(CSSM_ALGMODE_CBCPadIV8); 331 decrypt.padding(CSSM_PADDING_PKCS1); 332 decrypt.initVector(iv); 333 decrypt.key(Key(this)); 334 decrypt.cred(AccessCredentials::overlay(cred)); 335 decrypt.decrypt(&cipherText, 1, &plainText1, 1); 336 decrypt.final(plainText2); 337 338 // Use DL allocator for allocating memory for data. 339 CSSM_SIZE length = plainText1.Length + plainText2.Length; 340 data.Data = allocator.alloc<uint8>((UInt32)length); 341 data.Length = length; 342 memcpy(data.Data, plainText1.Data, plainText1.Length); 343 memcpy(&data.Data[plainText1.Length], plainText2.Data, plainText2.Length); 344 } 345 346 void 347 SSGroupImpl::encodeDataBlob(const CSSM_DATA *data, 348 const CSSM_ACCESS_CREDENTIALS *cred, 349 CssmDataContainer &dataBlob) 350 { 351 // Get our csp and set up a random number generation context. 352 CSP csp(this->csp()); 353 Random random(csp, CSSM_ALGID_APPLE_YARROW); 354 355 // Encrypt data using key and encode it in a dataBlob. 356 357 // First calculate a random IV. 358 uint8 ivBuf[kIVSize]; 359 CssmData iv(ivBuf, kIVSize); 360 random.generate(iv, kIVSize); 361 362 // Setup encryption context 363 Encrypt encrypt(csp, algorithm()); 364 encrypt.mode(CSSM_ALGMODE_CBCPadIV8); 365 encrypt.padding(CSSM_PADDING_PKCS1); 366 encrypt.initVector(iv); 367 encrypt.key(Key(this)); 368 encrypt.cred(AccessCredentials::overlay(cred)); 369 370 // Encrypt the data 371 const CssmData nothing; 372 const CssmData *plainText = data ? CssmData::overlay(data) : ¬hing; 373 // @@@ Don't use staged encrypt once the AppleCSPDL can do combo 374 // encryption. 375 CssmDataContainer cipherText1, cipherText2; 376 encrypt.encrypt(plainText, 1, &cipherText1, 1); 377 encrypt.final(cipherText2); 378 379 // Create a dataBlob containing the label followed by the IV followed 380 // by the cipherText. 381 CSSM_SIZE length = (kLabelSize + kIVSize 382 + cipherText1.Length + cipherText2.Length); 383 dataBlob.Data = dataBlob.mAllocator.alloc<uint8>((UInt32)length); 384 dataBlob.Length = length; 385 memcpy(dataBlob.Data, mLabel.Data, kLabelSize); 386 memcpy(&dataBlob.Data[kLabelSize], iv.Data, kIVSize); 387 memcpy(&dataBlob.Data[kLabelSize + kIVSize], 388 cipherText1.Data, cipherText1.Length); 389 memcpy(&dataBlob.Data[kLabelSize + kIVSize + cipherText1.Length], 390 cipherText2.Data, cipherText2.Length); 391 } 392 393 394 // 395 // SSDbCursorImpl -- Secure Storage Database Cursor Implementation. 396 // 397 SSDbCursorImpl::SSDbCursorImpl(const Db &db, const CSSM_QUERY &query, 398 Allocator &allocator) 399 : DbDbCursorImpl(db, query, allocator) 400 { 401 } 402 403 SSDbCursorImpl::SSDbCursorImpl(const Db &db, uint32 capacity, 404 Allocator &allocator) 405 : DbDbCursorImpl(db, capacity, allocator) 406 { 407 } 408 409 bool 410 SSDbCursorImpl::next(DbAttributes *attributes, ::CssmDataContainer *data, 411 DbUniqueRecord &uniqueId) 412 { 413 return next(attributes, data, uniqueId, NULL); 414 } 415 416 bool 417 SSDbCursorImpl::next(DbAttributes *attributes, ::CssmDataContainer *data, 418 DbUniqueRecord &uniqueId, 419 const CSSM_ACCESS_CREDENTIALS *cred) 420 { 421 if (!data) { 422 return DbDbCursorImpl::next(attributes, data, uniqueId); 423 } 424 425 DbAttributes noAttrs, *attrs; 426 attrs = attributes ? attributes : &noAttrs; 427 428 // To comply with previous behavior, this method will not find symmetric or public/private keys 429 // if you ask for the data of each item. 430 431 // Get the datablob for this record 432 CssmDataContainer dataBlob(allocator()); 433 for (;;) 434 { 435 if (!DbDbCursorImpl::next(attrs, &dataBlob, uniqueId)) 436 return false; 437 438 CSSM_DB_RECORDTYPE rt = attrs->recordType(); 439 if (rt == CSSM_DL_DB_RECORD_SYMMETRIC_KEY || 440 rt == CSSM_DL_DB_RECORD_PRIVATE_KEY || 441 rt == CSSM_DL_DB_RECORD_PUBLIC_KEY) 442 { 443 // This is a key. Free it, and then check if we should return the item (but not the data) 444 database()->csp()->freeKey(*reinterpret_cast<CssmKey *>(dataBlob.Data)); 445 446 if(!data) { 447 break; 448 } 449 } else { 450 // This is a non-key item. Return it. 451 break; 452 } 453 } 454 455 // If the caller requested any data, return the data. 456 if(data) { 457 if (!SSGroupImpl::isGroup(dataBlob)) 458 { 459 data->Data = dataBlob.Data; 460 data->Length = dataBlob.Length; 461 dataBlob.Data = NULL; 462 dataBlob.Length = 0; 463 return true; 464 } 465 466 // Get the group for dataBlob 467 SSGroup group(database(), dataBlob); 468 469 // TODO: Add attrs to cred 470 471 // Decode the dataBlob, pass in the DL allocator. 472 group->decodeDataBlob(dataBlob, cred, database()->allocator(), *data); 473 } 474 return true; 475 } 476 477 bool 478 SSDbCursorImpl::nextKey(DbAttributes *attributes, Key &key, 479 DbUniqueRecord &uniqueId) 480 { 481 DbAttributes noAttrs, *attrs; 482 attrs = attributes ? attributes : &noAttrs; 483 CssmDataContainer keyData(database()->allocator()); 484 for (;;) 485 { 486 if (!DbDbCursorImpl::next(attrs, &keyData, uniqueId)) 487 return false; 488 // Keep going until we find a key type record. 489 CSSM_DB_RECORDTYPE rt = attrs->recordType(); 490 if (rt == CSSM_DL_DB_RECORD_SYMMETRIC_KEY 491 || rt == CSSM_DL_DB_RECORD_PRIVATE_KEY 492 || rt == CSSM_DL_DB_RECORD_PUBLIC_KEY) 493 break; 494 } 495 496 key = Key(database()->csp(), *reinterpret_cast<CSSM_KEY *>(keyData.Data)); 497 return true; 498 } 499 500 void 501 SSDbCursorImpl::activate() 502 { 503 return DbDbCursorImpl::activate(); 504 } 505 506 void 507 SSDbCursorImpl::deactivate() 508 { 509 return DbDbCursorImpl::deactivate(); 510 } 511 512 513 // 514 // SSDbUniqueRecordImpl -- Secure Storage UniqueRecord Implementation. 515 // 516 SSDbUniqueRecordImpl::SSDbUniqueRecordImpl(const Db &db) 517 : DbUniqueRecordImpl(db) 518 { 519 } 520 521 SSDbUniqueRecordImpl::~SSDbUniqueRecordImpl() 522 { 523 } 524 525 void 526 SSDbUniqueRecordImpl::deleteRecord() 527 { 528 deleteRecord(NULL); 529 } 530 531 void 532 SSDbUniqueRecordImpl::deleteRecord(const CSSM_ACCESS_CREDENTIALS *cred) 533 { 534 // Get the datablob for this record 535 // @@@ Fixme so we don't need to call DbUniqueRecordImpl::get 536 CssmDataContainer dataBlob(allocator()); 537 DbAttributes attributes; 538 539 DbUniqueRecordImpl::get(&attributes, &dataBlob); 540 CSSM_KEY_PTR keyPtr = (CSSM_KEY_PTR) dataBlob.data(); 541 542 // delete data part first: 543 // (1) don't leave data without keys around 544 // (2) delete orphaned data anyway 545 DbUniqueRecordImpl::deleteRecord(); 546 547 // @@@ Use transactions? 548 if (SSGroupImpl::isGroup(dataBlob)) 549 try { 550 // Get the group for dataBlob 551 SSGroup group(database(), dataBlob); 552 // Delete the group (key) 553 group->deleteKey(cred); 554 } catch (const CssmError &err) { 555 switch (err.error) { 556 case CSSMERR_DL_RECORD_NOT_FOUND: 557 // Zombie item (no group key). Finally at peace! No error 558 break; 559 default: 560 561 if (attributes.recordType() == CSSM_DL_DB_RECORD_PUBLIC_KEY || 562 attributes.recordType() == CSSM_DL_DB_RECORD_PRIVATE_KEY || 563 attributes.recordType() == CSSM_DL_DB_RECORD_SYMMETRIC_KEY) 564 { 565 allocator().free(keyPtr->KeyData.Data); 566 } 567 568 throw; 569 } 570 } 571 572 if (attributes.recordType() == CSSM_DL_DB_RECORD_PUBLIC_KEY || 573 attributes.recordType() == CSSM_DL_DB_RECORD_PRIVATE_KEY || 574 attributes.recordType() == CSSM_DL_DB_RECORD_SYMMETRIC_KEY) 575 { 576 allocator().free(keyPtr->KeyData.Data); 577 } 578 } 579 580 void 581 SSDbUniqueRecordImpl::modify(CSSM_DB_RECORDTYPE recordType, 582 const CSSM_DB_RECORD_ATTRIBUTE_DATA *attributes, 583 const CSSM_DATA *data, 584 CSSM_DB_MODIFY_MODE modifyMode) 585 { 586 modify(recordType, attributes, data, modifyMode, NULL); 587 } 588 589 void 590 SSDbUniqueRecordImpl::modify(CSSM_DB_RECORDTYPE recordType, 591 const CSSM_DB_RECORD_ATTRIBUTE_DATA *attributes, 592 const CSSM_DATA *data, 593 CSSM_DB_MODIFY_MODE modifyMode, 594 const CSSM_ACCESS_CREDENTIALS *cred) 595 { 596 if (!data) 597 { 598 DbUniqueRecordImpl::modify(recordType, attributes, NULL, modifyMode); 599 return; 600 } 601 602 // Get the datablob for this record 603 // @@@ Fixme so we don't need to call DbUniqueRecordImpl::get 604 CssmDataContainer oldDataBlob(allocator()); 605 DbUniqueRecordImpl::get(NULL, &oldDataBlob); 606 607 if (!SSGroupImpl::isGroup(oldDataBlob)) 608 { 609 DbUniqueRecordImpl::modify(recordType, attributes, data, modifyMode); 610 return; 611 } 612 613 // Get the group for oldDataBlob 614 SSGroup group(database(), oldDataBlob); 615 616 // Create a new dataBlob. 617 CssmDataContainer dataBlob(allocator()); 618 group->encodeDataBlob(data, cred, dataBlob); 619 DbUniqueRecordImpl::modify(recordType, attributes, &dataBlob, modifyMode); 620 } 621 622 void 623 SSDbUniqueRecordImpl::get(DbAttributes *attributes, ::CssmDataContainer *data) 624 { 625 get(attributes, data, NULL); 626 } 627 628 void 629 SSDbUniqueRecordImpl::get(DbAttributes *attributes, ::CssmDataContainer *data, 630 const CSSM_ACCESS_CREDENTIALS *cred) 631 { 632 if (!data) 633 { 634 DbUniqueRecordImpl::get(attributes, NULL); 635 return; 636 } 637 638 // Get the datablob for this record 639 // @@@ Fixme so we don't need to call DbUniqueRecordImpl::get 640 CssmDataContainer dataBlob(allocator()); 641 DbUniqueRecordImpl::get(attributes, &dataBlob); 642 643 if (!SSGroupImpl::isGroup(dataBlob)) 644 { 645 data->Data = dataBlob.Data; 646 data->Length = dataBlob.Length; 647 dataBlob.Data = NULL; 648 dataBlob.Length = 0; 649 return; 650 } 651 652 // Get the group for dataBlob 653 SSGroup group(database(), dataBlob); 654 655 // Decode the dataBlob, pass in the DL allocator. 656 group->decodeDataBlob(dataBlob, cred, allocator(), *data); 657 } 658 659 SSGroup 660 SSDbUniqueRecordImpl::group() 661 { 662 // Get the datablob for this record 663 // @@@ Fixme so we don't need to call DbUniqueRecordImpl::get 664 CssmDataContainer dataBlob(allocator()); 665 DbUniqueRecordImpl::get(NULL, &dataBlob); 666 return SSGroup(database(), dataBlob); 667 }