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