]> git.saurik.com Git - apple/security.git/blob - Security/libsecurity_cdsa_client/lib/securestorage.cpp
Security-57031.1.35.tar.gz
[apple/security.git] / Security / libsecurity_cdsa_client / lib / 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 }
50
51 Allocator &CSPDLImpl::allocator() const
52 {
53 DLImpl::allocator(); return CSPImpl::allocator();
54 }
55
56 void CSPDLImpl::allocator(Allocator &alloc)
57 {
58 CSPImpl::allocator(alloc); DLImpl::allocator(alloc);
59 }
60
61 bool CSPDLImpl::operator <(const CSPDLImpl &other) const
62 {
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)));
66 }
67
68 bool CSPDLImpl::operator ==(const CSPDLImpl &other) const
69 {
70 return (static_cast<const CSPImpl &>(*this) == static_cast<const CSPImpl &>(other)
71 && static_cast<const DLImpl &>(*this) == static_cast<const DLImpl &>(other));
72 }
73
74 CSSM_SERVICE_MASK CSPDLImpl::subserviceMask() const
75 {
76 return CSPImpl::subserviceType() | DLImpl::subserviceType();
77 }
78
79 void CSPDLImpl::subserviceId(uint32 id)
80 {
81 CSPImpl::subserviceId(id); DLImpl::subserviceId(id);
82 }
83
84
85 //
86 // Secure storage
87 //
88 SSCSPDLImpl::SSCSPDLImpl(const Guid &guid) : CSPDLImpl::CSPDLImpl(guid)
89 {
90 }
91
92 SSCSPDLImpl::SSCSPDLImpl(const Module &module) : CSPDLImpl::CSPDLImpl(module)
93 {
94 }
95
96 SSCSPDLImpl::~SSCSPDLImpl()
97 {
98 }
99
100 DbImpl *
101 SSCSPDLImpl::newDb(const char *inDbName, const CSSM_NET_ADDRESS *inDbLocation)
102 {
103 return new SSDbImpl(SSCSPDL(this), inDbName, inDbLocation);
104 }
105
106
107 //
108 // SSDbImpl -- Secure Storage Database Implementation
109 //
110 SSDbImpl::SSDbImpl(const SSCSPDL &cspdl, const char *inDbName,
111 const CSSM_NET_ADDRESS *inDbLocation)
112 : DbImpl(cspdl, inDbName, inDbLocation)
113 {
114 }
115
116 SSDbImpl::~SSDbImpl()
117 {
118 }
119
120 void
121 SSDbImpl::create()
122 {
123 DbImpl::create();
124 }
125
126 void
127 SSDbImpl::open()
128 {
129 DbImpl::open();
130 }
131
132 SSDbUniqueRecord
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)
137 {
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));
142
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;
149 try
150 {
151 return insert(recordType, attributes, data, group, cred);
152 if (autoCommit)
153 {
154 // autoCommit was on so commit now that we are done and turn
155 // it back on.
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);
159 }
160 }
161 catch(...)
162 {
163 try { group->deleteKey(cred); } catch (...) {}
164 if (autoCommit)
165 {
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);
171 }
172 throw;
173 }
174
175 // keep the compiler happy -- this path is NEVER taken
176 CssmError::throwMe(0);
177 }
178
179 SSDbUniqueRecord
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)
184 {
185 // Create an encoded dataBlob for this item.
186 CssmDataContainer dataBlob(allocator());
187 group->encodeDataBlob(data, cred, dataBlob);
188
189 // Insert the record with the new juicy dataBlob.
190 return SSDbUniqueRecord(safe_cast<SSDbUniqueRecordImpl *>
191 (&(*DbImpl::insert(recordType, attributes, &dataBlob))));
192 }
193
194
195 // DbCursorMaker
196 DbCursorImpl *
197 SSDbImpl::newDbCursor(const CSSM_QUERY &query, Allocator &allocator)
198 {
199 return new SSDbCursorImpl(Db(this), query, allocator);
200 }
201
202 DbCursorImpl *
203 SSDbImpl::newDbCursor(uint32 capacity, Allocator &allocator)
204 {
205 return new SSDbCursorImpl(Db(this), capacity, allocator);
206 }
207
208
209 // SSDbUniqueRecordMaker
210 DbUniqueRecordImpl *
211 SSDbImpl::newDbUniqueRecord()
212 {
213 return new SSDbUniqueRecordImpl(Db(this));
214 }
215
216
217 //
218 // SSGroup -- Group key with acl, used to protect a group of items.
219 //
220 // @@@ Get this from a shared spot.
221 CSSM_DB_NAME_ATTR(SSGroupImpl::kLabel, 6, (char*) "Label", 0, NULL, BLOB);
222
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())
227 {
228 mLabel.Length = kLabelSize;
229 mLabel.Data = reinterpret_cast<uint8 *>
230 (mLabel.mAllocator.malloc(mLabel.Length));
231
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);
235
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);
239
240 // Overwrite the first 4 bytes with the magic cookie for a group.
241 reinterpret_cast<uint32 *>(mLabel.Data)[0] = h2n(uint32(kGroupMagic));
242
243 // @@@ Ensure that the label is unique (Chance of collision is 2^80 --
244 // birthday paradox).
245
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);
249
250 // Set the acl of the key correctly here
251 genKey.rcc(credAndAclEntry);
252
253 // Generate the key
254 genKey(*this, KeySpec(CSSM_KEYUSE_ENCRYPT|CSSM_KEYUSE_DECRYPT,
255 CSSM_KEYATTR_PERMANENT|CSSM_KEYATTR_SENSITIVE,
256 mLabel));
257
258 // Activate ourself so CSSM_FreeKey will get called when we go out of
259 // scope.
260 activate();
261 }
262
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())
266 {
267 if (dataBlob.Length < kLabelSize + kIVSize)
268 CssmError::throwMe(CSSMERR_DL_RECORD_NOT_FOUND); // Not a SS record
269
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
273
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);
278
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
283
284 // Set the key part of ourself.
285 static_cast<CSSM_KEY &>(*this) =
286 *reinterpret_cast<const CSSM_KEY *>(keyData.Data);
287
288 // Activate ourself so CSSM_FreeKey will get called when we go out of
289 // scope.
290 activate();
291 }
292
293 bool
294 SSGroupImpl::isGroup(const CSSM_DATA &dataBlob)
295 {
296 return dataBlob.Length >= kLabelSize + kIVSize
297 && *reinterpret_cast<const uint32 *>(dataBlob.Data) == h2n(uint32(kGroupMagic));
298 }
299
300 const CssmData
301 SSGroupImpl::label() const
302 {
303 return mLabel;
304 }
305
306 void
307 SSGroupImpl::decodeDataBlob(const CSSM_DATA &dataBlob,
308 const CSSM_ACCESS_CREDENTIALS *cred,
309 Allocator &allocator, CSSM_DATA &data)
310 {
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));
315
316 CssmDataContainer plainText1(allocator);
317 CssmDataContainer plainText2(allocator);
318 // Decrypt the data
319 // @@@ Don't use staged decrypt once the AppleCSPDL can do combo
320 // encryption.
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);
330
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);
337 }
338
339 void
340 SSGroupImpl::encodeDataBlob(const CSSM_DATA *data,
341 const CSSM_ACCESS_CREDENTIALS *cred,
342 CssmDataContainer &dataBlob)
343 {
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);
347
348 // Encrypt data using key and encode it in a dataBlob.
349
350 // First calculate a random IV.
351 uint8 ivBuf[kIVSize];
352 CssmData iv(ivBuf, kIVSize);
353 random.generate(iv, kIVSize);
354
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));
362
363 // Encrypt the data
364 const CssmData nothing;
365 const CssmData *plainText = data ? CssmData::overlay(data) : &nothing;
366 // @@@ Don't use staged encrypt once the AppleCSPDL can do combo
367 // encryption.
368 CssmDataContainer cipherText1, cipherText2;
369 encrypt.encrypt(plainText, 1, &cipherText1, 1);
370 encrypt.final(cipherText2);
371
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);
384 }
385
386
387 //
388 // SSDbCursorImpl -- Secure Storage Database Cursor Implementation.
389 //
390 SSDbCursorImpl::SSDbCursorImpl(const Db &db, const CSSM_QUERY &query,
391 Allocator &allocator)
392 : DbDbCursorImpl(db, query, allocator)
393 {
394 }
395
396 SSDbCursorImpl::SSDbCursorImpl(const Db &db, uint32 capacity,
397 Allocator &allocator)
398 : DbDbCursorImpl(db, capacity, allocator)
399 {
400 }
401
402 bool
403 SSDbCursorImpl::next(DbAttributes *attributes, ::CssmDataContainer *data,
404 DbUniqueRecord &uniqueId)
405 {
406 return next(attributes, data, uniqueId, NULL);
407 }
408
409 bool
410 SSDbCursorImpl::next(DbAttributes *attributes, ::CssmDataContainer *data,
411 DbUniqueRecord &uniqueId,
412 const CSSM_ACCESS_CREDENTIALS *cred)
413 {
414 if (!data)
415 return DbDbCursorImpl::next(attributes, data, uniqueId);
416
417 DbAttributes noAttrs, *attrs;
418 attrs = attributes ? attributes : &noAttrs;
419
420 // Get the datablob for this record
421 CssmDataContainer dataBlob(allocator());
422 for (;;)
423 {
424 if (!DbDbCursorImpl::next(attrs, &dataBlob, uniqueId))
425 return false;
426
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)
432 {
433 // @@@ Check the label and if it doesn't start with the magic for a SSKey return the key.
434 break;
435 }
436 else
437 {
438 // Free the key we just retrieved
439 database()->csp()->freeKey(*reinterpret_cast<CssmKey *>(dataBlob.Data));
440 }
441 }
442
443 if (!SSGroupImpl::isGroup(dataBlob))
444 {
445 data->Data = dataBlob.Data;
446 data->Length = dataBlob.Length;
447 dataBlob.Data = NULL;
448 dataBlob.Length = 0;
449 return true;
450 }
451
452 // Get the group for dataBlob
453 SSGroup group(database(), dataBlob);
454
455 // Decode the dataBlob, pass in the DL allocator.
456 group->decodeDataBlob(dataBlob, cred, database()->allocator(), *data);
457 return true;
458 }
459
460 bool
461 SSDbCursorImpl::nextKey(DbAttributes *attributes, Key &key,
462 DbUniqueRecord &uniqueId)
463 {
464 DbAttributes noAttrs, *attrs;
465 attrs = attributes ? attributes : &noAttrs;
466 CssmDataContainer keyData(database()->allocator());
467 for (;;)
468 {
469 if (!DbDbCursorImpl::next(attrs, &keyData, uniqueId))
470 return false;
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)
476 break;
477 }
478
479 key = Key(database()->csp(), *reinterpret_cast<CSSM_KEY *>(keyData.Data));
480 return true;
481 }
482
483 void
484 SSDbCursorImpl::activate()
485 {
486 return DbDbCursorImpl::activate();
487 }
488
489 void
490 SSDbCursorImpl::deactivate()
491 {
492 return DbDbCursorImpl::deactivate();
493 }
494
495
496 //
497 // SSDbUniqueRecordImpl -- Secure Storage UniqueRecord Implementation.
498 //
499 SSDbUniqueRecordImpl::SSDbUniqueRecordImpl(const Db &db)
500 : DbUniqueRecordImpl(db)
501 {
502 }
503
504 SSDbUniqueRecordImpl::~SSDbUniqueRecordImpl()
505 {
506 }
507
508 void
509 SSDbUniqueRecordImpl::deleteRecord()
510 {
511 deleteRecord(NULL);
512 }
513
514 void
515 SSDbUniqueRecordImpl::deleteRecord(const CSSM_ACCESS_CREDENTIALS *cred)
516 {
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;
521
522 DbUniqueRecordImpl::get(&attributes, &dataBlob);
523 CSSM_KEY_PTR keyPtr = (CSSM_KEY_PTR) dataBlob.data();
524
525 // delete data part first:
526 // (1) don't leave data without keys around
527 // (2) delete orphaned data anyway
528 DbUniqueRecordImpl::deleteRecord();
529
530 // @@@ Use transactions?
531 if (SSGroupImpl::isGroup(dataBlob))
532 try {
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) {
538 switch (err.error) {
539 case CSSMERR_DL_RECORD_NOT_FOUND:
540 // Zombie item (no group key). Finally at peace! No error
541 break;
542 default:
543
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)
547 {
548 allocator().free(keyPtr->KeyData.Data);
549 }
550
551 throw;
552 }
553 }
554
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)
558 {
559 allocator().free(keyPtr->KeyData.Data);
560 }
561 }
562
563 void
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)
568 {
569 modify(recordType, attributes, data, modifyMode, NULL);
570 }
571
572 void
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)
578 {
579 if (!data)
580 {
581 DbUniqueRecordImpl::modify(recordType, attributes, NULL, modifyMode);
582 return;
583 }
584
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);
589
590 if (!SSGroupImpl::isGroup(oldDataBlob))
591 {
592 DbUniqueRecordImpl::modify(recordType, attributes, data, modifyMode);
593 return;
594 }
595
596 // Get the group for oldDataBlob
597 SSGroup group(database(), oldDataBlob);
598
599 // Create a new dataBlob.
600 CssmDataContainer dataBlob(allocator());
601 group->encodeDataBlob(data, cred, dataBlob);
602 DbUniqueRecordImpl::modify(recordType, attributes, &dataBlob, modifyMode);
603 }
604
605 void
606 SSDbUniqueRecordImpl::get(DbAttributes *attributes, ::CssmDataContainer *data)
607 {
608 get(attributes, data, NULL);
609 }
610
611 void
612 SSDbUniqueRecordImpl::get(DbAttributes *attributes, ::CssmDataContainer *data,
613 const CSSM_ACCESS_CREDENTIALS *cred)
614 {
615 if (!data)
616 {
617 DbUniqueRecordImpl::get(attributes, NULL);
618 return;
619 }
620
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);
625
626 if (!SSGroupImpl::isGroup(dataBlob))
627 {
628 data->Data = dataBlob.Data;
629 data->Length = dataBlob.Length;
630 dataBlob.Data = NULL;
631 dataBlob.Length = 0;
632 return;
633 }
634
635 // Get the group for dataBlob
636 SSGroup group(database(), dataBlob);
637
638 // Decode the dataBlob, pass in the DL allocator.
639 group->decodeDataBlob(dataBlob, cred, allocator(), *data);
640 }
641
642 SSGroup
643 SSDbUniqueRecordImpl::group()
644 {
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);
650 }