2  * Copyright (c) 2000-2001,2011-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. 
  19 #include "securestorage.h" 
  20 #include <security_cdsa_client/genkey.h> 
  21 //#include <Security/Access.h> //@@@CONV 
  22 #include <security_utilities/osxcode.h> 
  25 using namespace CssmClient
; 
  26 //using namespace KeychainCore; 
  29 // Manage CSPDL attachments 
  31 CSPDLImpl::CSPDLImpl(const Guid 
&guid
) 
  32 : CSPImpl(Cssm::standard()->autoModule(guid
)), 
  33 DLImpl(CSPImpl::module()) 
  37 CSPDLImpl::CSPDLImpl(const Module 
&module) 
  43 CSPDLImpl::~CSPDLImpl() 
  51 Allocator 
&CSPDLImpl::allocator() const 
  53         DLImpl::allocator(); return CSPImpl::allocator(); 
  56 void CSPDLImpl::allocator(Allocator 
&alloc
) 
  58         CSPImpl::allocator(alloc
); DLImpl::allocator(alloc
); 
  61 bool CSPDLImpl::operator <(const CSPDLImpl 
&other
) const 
  63         return (static_cast<const CSPImpl 
&>(*this) < static_cast<const CSPImpl 
&>(other
) || 
  64                         (!(static_cast<const CSPImpl 
&>(other
) < static_cast<const CSPImpl 
&>(*this)) 
  65                            && static_cast<const DLImpl 
&>(*this) < static_cast<const DLImpl 
&>(other
))); 
  68 bool CSPDLImpl::operator ==(const CSPDLImpl 
&other
) const 
  70         return (static_cast<const CSPImpl 
&>(*this) == static_cast<const CSPImpl 
&>(other
) 
  71                         && static_cast<const DLImpl 
&>(*this) == static_cast<const DLImpl 
&>(other
)); 
  74 CSSM_SERVICE_MASK 
CSPDLImpl::subserviceMask() const 
  76         return CSPImpl::subserviceType() | DLImpl::subserviceType(); 
  79 void CSPDLImpl::subserviceId(uint32 id
) 
  81         CSPImpl::subserviceId(id
); DLImpl::subserviceId(id
); 
  88 SSCSPDLImpl::SSCSPDLImpl(const Guid 
&guid
) : CSPDLImpl::CSPDLImpl(guid
) 
  92 SSCSPDLImpl::SSCSPDLImpl(const Module 
&module) : CSPDLImpl::CSPDLImpl(module) 
  96 SSCSPDLImpl::~SSCSPDLImpl() 
 101 SSCSPDLImpl::newDb(const char *inDbName
, const CSSM_NET_ADDRESS 
*inDbLocation
) 
 103         return new SSDbImpl(SSCSPDL(this), inDbName
, inDbLocation
); 
 108 // SSDbImpl -- Secure Storage Database Implementation 
 110 SSDbImpl::SSDbImpl(const SSCSPDL 
&cspdl
, const char *inDbName
, 
 111                                    const CSSM_NET_ADDRESS 
*inDbLocation
) 
 112 : DbImpl(cspdl
, inDbName
, inDbLocation
) 
 116 SSDbImpl::~SSDbImpl() 
 133 SSDbImpl::insert(CSSM_DB_RECORDTYPE recordType
, 
 134                                  const CSSM_DB_RECORD_ATTRIBUTE_DATA 
*attributes
, 
 135                                  const CSSM_DATA 
*data
, 
 136                                  const CSSM_RESOURCE_CONTROL_CONTEXT 
*rc
) 
 138         // Get the handle of the DL underlying this CSPDL. 
 139         CSSM_DL_DB_HANDLE dldbh
; 
 140         passThrough(CSSM_APPLECSPDL_DB_GET_HANDLE
, NULL
, 
 141                 reinterpret_cast<void **>(&dldbh
)); 
 143         // Turn off autocommit on the underlying DL and remember the old state. 
 144         CSSM_BOOL autoCommit 
= CSSM_TRUE
; 
 145         check(CSSM_DL_PassThrough(dldbh
, CSSM_APPLEFILEDL_TOGGLE_AUTOCOMMIT
, 
 146                 0, reinterpret_cast<void **>(&autoCommit
))); 
 147         SSGroup 
group(SSDb(this), rc
); 
 148         const CSSM_ACCESS_CREDENTIALS 
*cred 
= rc 
? rc
->AccessCred 
: NULL
; 
 151                 return insert(recordType
, attributes
, data
, group
, cred
); 
 154                         // autoCommit was on so commit now that we are done and turn 
 156                         check(CSSM_DL_PassThrough(dldbh
, CSSM_APPLEFILEDL_COMMIT
, NULL
, NULL
)); 
 157                         CSSM_DL_PassThrough(dldbh
, CSSM_APPLEFILEDL_TOGGLE_AUTOCOMMIT
, 
 158                                 reinterpret_cast<const void *>(autoCommit
), NULL
); 
 163                 try { group
->deleteKey(cred
); } catch (...) {} 
 166                         // autoCommit was off so rollback since we failed and turn 
 167                         // autoCommit back on. 
 168                         CSSM_DL_PassThrough(dldbh
, CSSM_APPLEFILEDL_ROLLBACK
, NULL
, NULL
); 
 169                         CSSM_DL_PassThrough(dldbh
, CSSM_APPLEFILEDL_TOGGLE_AUTOCOMMIT
, 
 170                                 reinterpret_cast<const void *>(autoCommit
), NULL
); 
 175         // keep the compiler happy -- this path is NEVER taken 
 176         CssmError::throwMe(0); 
 180 SSDbImpl::insert(CSSM_DB_RECORDTYPE recordType
, 
 181                                  const CSSM_DB_RECORD_ATTRIBUTE_DATA 
*attributes
, 
 182                                  const CSSM_DATA 
*data
, const SSGroup 
&group
, 
 183                                  const CSSM_ACCESS_CREDENTIALS 
*cred
) 
 185         // Create an encoded dataBlob for this item. 
 186         CssmDataContainer 
dataBlob(allocator()); 
 187         group
->encodeDataBlob(data
, cred
, dataBlob
); 
 189         // Insert the record with the new juicy dataBlob. 
 190         return SSDbUniqueRecord(safe_cast
<SSDbUniqueRecordImpl 
*> 
 191             (&(*DbImpl::insert(recordType
, attributes
, &dataBlob
)))); 
 197 SSDbImpl::newDbCursor(const CSSM_QUERY 
&query
, Allocator 
&allocator
) 
 199         return new SSDbCursorImpl(Db(this), query
, allocator
); 
 203 SSDbImpl::newDbCursor(uint32 capacity
, Allocator 
&allocator
) 
 205         return new SSDbCursorImpl(Db(this), capacity
, allocator
); 
 209 // SSDbUniqueRecordMaker 
 211 SSDbImpl::newDbUniqueRecord() 
 213         return new SSDbUniqueRecordImpl(Db(this)); 
 218 // SSGroup -- Group key with acl, used to protect a group of items. 
 220 // @@@ Get this from a shared spot. 
 221 CSSM_DB_NAME_ATTR(SSGroupImpl::kLabel
, 6, (char*) "Label", 0, NULL
, BLOB
); 
 223 // Create a new group. 
 224 SSGroupImpl::SSGroupImpl(const SSDb 
&ssDb
, 
 225                                                  const CSSM_RESOURCE_CONTROL_CONTEXT 
*credAndAclEntry
) 
 226 : KeyImpl(ssDb
->csp()), mLabel(ssDb
->allocator()) 
 228         mLabel
.Length 
= kLabelSize
; 
 229         mLabel
.Data 
= reinterpret_cast<uint8 
*> 
 230                 (mLabel
.mAllocator
.malloc(mLabel
.Length
)); 
 232         // Get our csp and set up a random number generation context. 
 233         CSP 
csp(this->csp()); 
 234         Random 
random(csp
, CSSM_ALGID_APPLE_YARROW
); 
 236         // Generate a kLabelSize byte random number that will be the label of 
 237         // the key which we store in the dataBlob. 
 238         random
.generate(mLabel
, (uint32
)mLabel
.Length
); 
 240         // Overwrite the first 4 bytes with the magic cookie for a group. 
 241         reinterpret_cast<uint32 
*>(mLabel
.Data
)[0] = h2n(uint32(kGroupMagic
)); 
 243         // @@@ Ensure that the label is unique (Chance of collision is 2^80 -- 
 244         // birthday paradox). 
 246         // Generate a permanent 3DES key that we will use to encrypt the data. 
 247         GenerateKey 
genKey(csp
, CSSM_ALGID_3DES_3KEY
, 192); 
 248         genKey
.database(ssDb
); 
 250         // Set the acl of the key correctly here 
 251         genKey
.rcc(credAndAclEntry
); 
 254         genKey(*this, KeySpec(CSSM_KEYUSE_ENCRYPT
|CSSM_KEYUSE_DECRYPT
, 
 255                                                   CSSM_KEYATTR_PERMANENT
|CSSM_KEYATTR_SENSITIVE
, 
 258         // Activate ourself so CSSM_FreeKey will get called when we go out of 
 263 // Lookup an existing group based on a dataBlob. 
 264 SSGroupImpl::SSGroupImpl(const SSDb 
&ssDb
, const CSSM_DATA 
&dataBlob
) 
 265 : KeyImpl(ssDb
->csp()), mLabel(ssDb
->allocator()) 
 267         if (dataBlob
.Length 
< kLabelSize 
+ kIVSize
) 
 268                 CssmError::throwMe(CSSMERR_DL_RECORD_NOT_FOUND
); // Not a SS record 
 270         mLabel 
= CssmData(dataBlob
.Data
, kLabelSize
); 
 271         if (*reinterpret_cast<const uint32 
*>(mLabel
.Data
) != h2n (uint32(kGroupMagic
))) 
 272                 CssmError::throwMe(CSSMERR_DL_RECORD_NOT_FOUND
); // Not a SS record 
 274         // Look up the symmetric key with that label. 
 275         DbCursor 
cursor(new DbDbCursorImpl(ssDb
, 0, Allocator::standard())); 
 276         cursor
->recordType(CSSM_DL_DB_RECORD_SYMMETRIC_KEY
); 
 277         cursor
->add(CSSM_DB_EQUAL
, kLabel
, mLabel
); 
 279         DbUniqueRecord keyId
; 
 280         CssmDataContainer 
keyData(ssDb
->allocator()); 
 281         if (!cursor
->next(NULL
, &keyData
, keyId
)) 
 282                 CssmError::throwMe(CSSMERR_DL_RECORD_NOT_FOUND
); // The key can't be found 
 284         // Set the key part of ourself. 
 285         static_cast<CSSM_KEY 
&>(*this) = 
 286                 *reinterpret_cast<const CSSM_KEY 
*>(keyData
.Data
); 
 288         // Activate ourself so CSSM_FreeKey will get called when we go out of 
 294 SSGroupImpl::isGroup(const CSSM_DATA 
&dataBlob
) 
 296         return dataBlob
.Length 
>= kLabelSize 
+ kIVSize
 
 297                 && *reinterpret_cast<const uint32 
*>(dataBlob
.Data
) == h2n(uint32(kGroupMagic
)); 
 301 SSGroupImpl::label() const 
 307 SSGroupImpl::decodeDataBlob(const CSSM_DATA 
&dataBlob
, 
 308                                                         const CSSM_ACCESS_CREDENTIALS 
*cred
, 
 309                                                         Allocator 
&allocator
, CSSM_DATA 
&data
) 
 311         // First get the IV and the cipherText from the blob. 
 312         CssmData 
iv(&dataBlob
.Data
[kLabelSize
], kIVSize
); 
 313         CssmData 
cipherText(&dataBlob
.Data
[kLabelSize 
+ kIVSize
], 
 314                                                 dataBlob
.Length 
- (kLabelSize 
+ kIVSize
)); 
 316         CssmDataContainer 
plainText1(allocator
); 
 317         CssmDataContainer 
plainText2(allocator
); 
 319         // @@@ Don't use staged decrypt once the AppleCSPDL can do combo 
 321         // Setup decryption context 
 322         Decrypt 
decrypt(csp(), algorithm()); 
 323         decrypt
.mode(CSSM_ALGMODE_CBCPadIV8
); 
 324         decrypt
.padding(CSSM_PADDING_PKCS1
); 
 325         decrypt
.initVector(iv
); 
 326         decrypt
.key(Key(this)); 
 327         decrypt
.cred(AccessCredentials::overlay(cred
)); 
 328         decrypt
.decrypt(&cipherText
, 1, &plainText1
, 1); 
 329         decrypt
.final(plainText2
); 
 331         // Use DL allocator for allocating memory for data. 
 332         CSSM_SIZE length 
= plainText1
.Length 
+ plainText2
.Length
; 
 333         data
.Data 
= allocator
.alloc
<uint8
>((UInt32
)length
); 
 334         data
.Length 
= length
; 
 335         memcpy(data
.Data
, plainText1
.Data
, plainText1
.Length
); 
 336         memcpy(&data
.Data
[plainText1
.Length
], plainText2
.Data
, plainText2
.Length
); 
 340 SSGroupImpl::encodeDataBlob(const CSSM_DATA 
*data
, 
 341                                                         const CSSM_ACCESS_CREDENTIALS 
*cred
, 
 342                                                         CssmDataContainer 
&dataBlob
) 
 344         // Get our csp and set up a random number generation context. 
 345         CSP 
csp(this->csp()); 
 346         Random 
random(csp
, CSSM_ALGID_APPLE_YARROW
); 
 348         // Encrypt data using key and encode it in a dataBlob. 
 350         // First calculate a random IV. 
 351         uint8 ivBuf
[kIVSize
]; 
 352         CssmData 
iv(ivBuf
, kIVSize
); 
 353         random
.generate(iv
, kIVSize
); 
 355         // Setup encryption context 
 356         Encrypt 
encrypt(csp
, algorithm()); 
 357         encrypt
.mode(CSSM_ALGMODE_CBCPadIV8
); 
 358         encrypt
.padding(CSSM_PADDING_PKCS1
); 
 359         encrypt
.initVector(iv
); 
 360         encrypt
.key(Key(this)); 
 361         encrypt
.cred(AccessCredentials::overlay(cred
)); 
 364         const CssmData nothing
; 
 365         const CssmData 
*plainText 
= data 
? CssmData::overlay(data
) : ¬hing
; 
 366         // @@@ Don't use staged encrypt once the AppleCSPDL can do combo 
 368         CssmDataContainer cipherText1
, cipherText2
; 
 369         encrypt
.encrypt(plainText
, 1, &cipherText1
, 1); 
 370         encrypt
.final(cipherText2
); 
 372         // Create a dataBlob containing the label followed by the IV followed 
 373         // by the cipherText. 
 374         CSSM_SIZE length 
= (kLabelSize 
+ kIVSize
 
 375                                          + cipherText1
.Length 
+ cipherText2
.Length
); 
 376         dataBlob
.Data 
= dataBlob
.mAllocator
.alloc
<uint8
>((UInt32
)length
); 
 377         dataBlob
.Length 
= length
; 
 378         memcpy(dataBlob
.Data
, mLabel
.Data
, kLabelSize
); 
 379         memcpy(&dataBlob
.Data
[kLabelSize
], iv
.Data
, kIVSize
); 
 380         memcpy(&dataBlob
.Data
[kLabelSize 
+ kIVSize
], 
 381                    cipherText1
.Data
, cipherText1
.Length
); 
 382         memcpy(&dataBlob
.Data
[kLabelSize 
+ kIVSize 
+ cipherText1
.Length
], 
 383                    cipherText2
.Data
, cipherText2
.Length
); 
 388 // SSDbCursorImpl -- Secure Storage Database Cursor Implementation. 
 390 SSDbCursorImpl::SSDbCursorImpl(const Db 
&db
, const CSSM_QUERY 
&query
, 
 391                                                            Allocator 
&allocator
) 
 392 : DbDbCursorImpl(db
, query
, allocator
) 
 396 SSDbCursorImpl::SSDbCursorImpl(const Db 
&db
, uint32 capacity
, 
 397                                                            Allocator 
&allocator
) 
 398 : DbDbCursorImpl(db
, capacity
, allocator
) 
 403 SSDbCursorImpl::next(DbAttributes 
*attributes
, ::CssmDataContainer 
*data
, 
 404                                          DbUniqueRecord 
&uniqueId
) 
 406         return next(attributes
, data
, uniqueId
, NULL
); 
 410 SSDbCursorImpl::next(DbAttributes 
*attributes
, ::CssmDataContainer 
*data
, 
 411                                          DbUniqueRecord 
&uniqueId
, 
 412                                          const CSSM_ACCESS_CREDENTIALS 
*cred
) 
 415                 return DbDbCursorImpl::next(attributes
, data
, uniqueId
); 
 417         DbAttributes noAttrs
, *attrs
; 
 418         attrs 
= attributes 
? attributes 
: &noAttrs
; 
 420         // Get the datablob for this record 
 421         CssmDataContainer 
dataBlob(allocator()); 
 424                 if (!DbDbCursorImpl::next(attrs
, &dataBlob
, uniqueId
)) 
 427                 // Keep going until we find a non key type record. 
 428                 CSSM_DB_RECORDTYPE rt 
= attrs
->recordType(); 
 429                 if (rt 
!= CSSM_DL_DB_RECORD_SYMMETRIC_KEY
 
 430                         && rt 
!= CSSM_DL_DB_RECORD_PRIVATE_KEY
 
 431                         && rt 
!= CSSM_DL_DB_RECORD_PUBLIC_KEY
) 
 433                         // @@@ Check the label and if it doesn't start with the magic for a SSKey return the key. 
 438                         // Free the key we just retrieved 
 439                         database()->csp()->freeKey(*reinterpret_cast<CssmKey 
*>(dataBlob
.Data
)); 
 443         if (!SSGroupImpl::isGroup(dataBlob
)) 
 445                 data
->Data 
= dataBlob
.Data
; 
 446                 data
->Length 
= dataBlob
.Length
; 
 447                 dataBlob
.Data 
= NULL
; 
 452         // Get the group for dataBlob 
 453         SSGroup 
group(database(), dataBlob
); 
 455         // Decode the dataBlob, pass in the DL allocator. 
 456         group
->decodeDataBlob(dataBlob
, cred
, database()->allocator(), *data
); 
 461 SSDbCursorImpl::nextKey(DbAttributes 
*attributes
, Key 
&key
, 
 462                                                 DbUniqueRecord 
&uniqueId
) 
 464         DbAttributes noAttrs
, *attrs
; 
 465         attrs 
= attributes 
? attributes 
: &noAttrs
; 
 466         CssmDataContainer 
keyData(database()->allocator()); 
 469                 if (!DbDbCursorImpl::next(attrs
, &keyData
, uniqueId
)) 
 471                 // Keep going until we find a key type record. 
 472                 CSSM_DB_RECORDTYPE rt 
= attrs
->recordType(); 
 473                 if (rt 
== CSSM_DL_DB_RECORD_SYMMETRIC_KEY
 
 474                         || rt 
== CSSM_DL_DB_RECORD_PRIVATE_KEY
 
 475                         || rt 
== CSSM_DL_DB_RECORD_PUBLIC_KEY
) 
 479         key 
= Key(database()->csp(), *reinterpret_cast<CSSM_KEY 
*>(keyData
.Data
)); 
 484 SSDbCursorImpl::activate() 
 486         return DbDbCursorImpl::activate(); 
 490 SSDbCursorImpl::deactivate() 
 492         return DbDbCursorImpl::deactivate(); 
 497 // SSDbUniqueRecordImpl -- Secure Storage UniqueRecord Implementation. 
 499 SSDbUniqueRecordImpl::SSDbUniqueRecordImpl(const Db 
&db
) 
 500 : DbUniqueRecordImpl(db
) 
 504 SSDbUniqueRecordImpl::~SSDbUniqueRecordImpl() 
 509 SSDbUniqueRecordImpl::deleteRecord() 
 515 SSDbUniqueRecordImpl::deleteRecord(const CSSM_ACCESS_CREDENTIALS 
*cred
) 
 517         // Get the datablob for this record 
 518         // @@@ Fixme so we don't need to call DbUniqueRecordImpl::get 
 519         CssmDataContainer 
dataBlob(allocator()); 
 520         DbAttributes attributes
; 
 522         DbUniqueRecordImpl::get(&attributes
, &dataBlob
); 
 523         CSSM_KEY_PTR keyPtr 
= (CSSM_KEY_PTR
) dataBlob
.data(); 
 525         // delete data part first: 
 526         // (1) don't leave data without keys around 
 527         // (2) delete orphaned data anyway 
 528         DbUniqueRecordImpl::deleteRecord(); 
 530         // @@@ Use transactions? 
 531         if (SSGroupImpl::isGroup(dataBlob
)) 
 533                 // Get the group for dataBlob 
 534                 SSGroup 
group(database(), dataBlob
); 
 535                 // Delete the group (key) 
 536                 group
->deleteKey(cred
); 
 537         } catch (const CssmError 
&err
) { 
 539                 case CSSMERR_DL_RECORD_NOT_FOUND
: 
 540                         // Zombie item (no group key). Finally at peace! No error 
 544                         if (attributes
.recordType() == CSSM_DL_DB_RECORD_PUBLIC_KEY 
|| 
 545                                 attributes
.recordType() == CSSM_DL_DB_RECORD_PRIVATE_KEY 
|| 
 546                                 attributes
.recordType() == CSSM_DL_DB_RECORD_SYMMETRIC_KEY
) 
 548                                 allocator().free(keyPtr
->KeyData
.Data
); 
 555         if (attributes
.recordType() == CSSM_DL_DB_RECORD_PUBLIC_KEY 
|| 
 556                 attributes
.recordType() == CSSM_DL_DB_RECORD_PRIVATE_KEY 
|| 
 557                 attributes
.recordType() == CSSM_DL_DB_RECORD_SYMMETRIC_KEY
) 
 559                 allocator().free(keyPtr
->KeyData
.Data
); 
 564 SSDbUniqueRecordImpl::modify(CSSM_DB_RECORDTYPE recordType
, 
 565                                                          const CSSM_DB_RECORD_ATTRIBUTE_DATA 
*attributes
, 
 566                                                          const CSSM_DATA 
*data
, 
 567                                                          CSSM_DB_MODIFY_MODE modifyMode
) 
 569         modify(recordType
, attributes
, data
, modifyMode
, NULL
); 
 573 SSDbUniqueRecordImpl::modify(CSSM_DB_RECORDTYPE recordType
, 
 574                                                          const CSSM_DB_RECORD_ATTRIBUTE_DATA 
*attributes
, 
 575                                                          const CSSM_DATA 
*data
, 
 576                                                          CSSM_DB_MODIFY_MODE modifyMode
, 
 577                                                          const CSSM_ACCESS_CREDENTIALS 
*cred
) 
 581                 DbUniqueRecordImpl::modify(recordType
, attributes
, NULL
, modifyMode
); 
 585         // Get the datablob for this record 
 586         // @@@ Fixme so we don't need to call DbUniqueRecordImpl::get 
 587         CssmDataContainer 
oldDataBlob(allocator()); 
 588         DbUniqueRecordImpl::get(NULL
, &oldDataBlob
); 
 590         if (!SSGroupImpl::isGroup(oldDataBlob
)) 
 592                 DbUniqueRecordImpl::modify(recordType
, attributes
, data
, modifyMode
); 
 596         // Get the group for oldDataBlob 
 597         SSGroup 
group(database(), oldDataBlob
); 
 599         // Create a new dataBlob. 
 600         CssmDataContainer 
dataBlob(allocator()); 
 601         group
->encodeDataBlob(data
, cred
, dataBlob
); 
 602         DbUniqueRecordImpl::modify(recordType
, attributes
, &dataBlob
, modifyMode
); 
 606 SSDbUniqueRecordImpl::get(DbAttributes 
*attributes
, ::CssmDataContainer 
*data
) 
 608         get(attributes
, data
, NULL
); 
 612 SSDbUniqueRecordImpl::get(DbAttributes 
*attributes
, ::CssmDataContainer 
*data
, 
 613                                                   const CSSM_ACCESS_CREDENTIALS 
*cred
) 
 617                 DbUniqueRecordImpl::get(attributes
, NULL
); 
 621         // Get the datablob for this record 
 622         // @@@ Fixme so we don't need to call DbUniqueRecordImpl::get 
 623         CssmDataContainer 
dataBlob(allocator()); 
 624         DbUniqueRecordImpl::get(attributes
, &dataBlob
); 
 626         if (!SSGroupImpl::isGroup(dataBlob
)) 
 628                 data
->Data 
= dataBlob
.Data
; 
 629                 data
->Length 
= dataBlob
.Length
; 
 630                 dataBlob
.Data 
= NULL
; 
 635         // Get the group for dataBlob 
 636         SSGroup 
group(database(), dataBlob
); 
 638         // Decode the dataBlob, pass in the DL allocator. 
 639         group
->decodeDataBlob(dataBlob
, cred
, allocator(), *data
); 
 643 SSDbUniqueRecordImpl::group() 
 645         // Get the datablob for this record 
 646         // @@@ Fixme so we don't need to call DbUniqueRecordImpl::get 
 647         CssmDataContainer 
dataBlob(allocator()); 
 648         DbUniqueRecordImpl::get(NULL
, &dataBlob
); 
 649         return SSGroup(database(), dataBlob
);