]> git.saurik.com Git - apple/security.git/blob - OSX/libsecurity_cdsa_client/lib/securestorage.cpp
Security-57337.60.2.tar.gz
[apple/security.git] / OSX / 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
418 DbAttributes noAttrs, *attrs;
419 attrs = attributes ? attributes : &noAttrs;
420
421 // To comply with previous behavior, this method will not find symmetric or public/private keys
422 // if you ask for the data of each item.
423
424 // Get the datablob for this record
425 CssmDataContainer dataBlob(allocator());
426 for (;;)
427 {
428 if (!DbDbCursorImpl::next(attrs, &dataBlob, uniqueId))
429 return false;
430
431 CSSM_DB_RECORDTYPE rt = attrs->recordType();
432 if (rt == CSSM_DL_DB_RECORD_SYMMETRIC_KEY ||
433 rt == CSSM_DL_DB_RECORD_PRIVATE_KEY ||
434 rt == CSSM_DL_DB_RECORD_PUBLIC_KEY)
435 {
436 // This is a key. Free it, and then check if we should return the item (but not the data)
437 database()->csp()->freeKey(*reinterpret_cast<CssmKey *>(dataBlob.Data));
438
439 if(!data) {
440 break;
441 }
442 } else {
443 // This is a non-key item. Return it.
444 break;
445 }
446 }
447
448 // If the caller requested any data, return the data.
449 if(data) {
450 if (!SSGroupImpl::isGroup(dataBlob))
451 {
452 data->Data = dataBlob.Data;
453 data->Length = dataBlob.Length;
454 dataBlob.Data = NULL;
455 dataBlob.Length = 0;
456 return true;
457 }
458
459 // Get the group for dataBlob
460 SSGroup group(database(), dataBlob);
461
462 // TODO: Add attrs to cred
463
464 // Decode the dataBlob, pass in the DL allocator.
465 group->decodeDataBlob(dataBlob, cred, database()->allocator(), *data);
466 }
467 return true;
468 }
469
470 bool
471 SSDbCursorImpl::nextKey(DbAttributes *attributes, Key &key,
472 DbUniqueRecord &uniqueId)
473 {
474 DbAttributes noAttrs, *attrs;
475 attrs = attributes ? attributes : &noAttrs;
476 CssmDataContainer keyData(database()->allocator());
477 for (;;)
478 {
479 if (!DbDbCursorImpl::next(attrs, &keyData, uniqueId))
480 return false;
481 // Keep going until we find a key type record.
482 CSSM_DB_RECORDTYPE rt = attrs->recordType();
483 if (rt == CSSM_DL_DB_RECORD_SYMMETRIC_KEY
484 || rt == CSSM_DL_DB_RECORD_PRIVATE_KEY
485 || rt == CSSM_DL_DB_RECORD_PUBLIC_KEY)
486 break;
487 }
488
489 key = Key(database()->csp(), *reinterpret_cast<CSSM_KEY *>(keyData.Data));
490 return true;
491 }
492
493 void
494 SSDbCursorImpl::activate()
495 {
496 return DbDbCursorImpl::activate();
497 }
498
499 void
500 SSDbCursorImpl::deactivate()
501 {
502 return DbDbCursorImpl::deactivate();
503 }
504
505
506 //
507 // SSDbUniqueRecordImpl -- Secure Storage UniqueRecord Implementation.
508 //
509 SSDbUniqueRecordImpl::SSDbUniqueRecordImpl(const Db &db)
510 : DbUniqueRecordImpl(db)
511 {
512 }
513
514 SSDbUniqueRecordImpl::~SSDbUniqueRecordImpl()
515 {
516 }
517
518 void
519 SSDbUniqueRecordImpl::deleteRecord()
520 {
521 deleteRecord(NULL);
522 }
523
524 void
525 SSDbUniqueRecordImpl::deleteRecord(const CSSM_ACCESS_CREDENTIALS *cred)
526 {
527 // Get the datablob for this record
528 // @@@ Fixme so we don't need to call DbUniqueRecordImpl::get
529 CssmDataContainer dataBlob(allocator());
530 DbAttributes attributes;
531
532 DbUniqueRecordImpl::get(&attributes, &dataBlob);
533 CSSM_KEY_PTR keyPtr = (CSSM_KEY_PTR) dataBlob.data();
534
535 // delete data part first:
536 // (1) don't leave data without keys around
537 // (2) delete orphaned data anyway
538 DbUniqueRecordImpl::deleteRecord();
539
540 // @@@ Use transactions?
541 if (SSGroupImpl::isGroup(dataBlob))
542 try {
543 // Get the group for dataBlob
544 SSGroup group(database(), dataBlob);
545 // Delete the group (key)
546 group->deleteKey(cred);
547 } catch (const CssmError &err) {
548 switch (err.error) {
549 case CSSMERR_DL_RECORD_NOT_FOUND:
550 // Zombie item (no group key). Finally at peace! No error
551 break;
552 default:
553
554 if (attributes.recordType() == CSSM_DL_DB_RECORD_PUBLIC_KEY ||
555 attributes.recordType() == CSSM_DL_DB_RECORD_PRIVATE_KEY ||
556 attributes.recordType() == CSSM_DL_DB_RECORD_SYMMETRIC_KEY)
557 {
558 allocator().free(keyPtr->KeyData.Data);
559 }
560
561 throw;
562 }
563 }
564
565 if (attributes.recordType() == CSSM_DL_DB_RECORD_PUBLIC_KEY ||
566 attributes.recordType() == CSSM_DL_DB_RECORD_PRIVATE_KEY ||
567 attributes.recordType() == CSSM_DL_DB_RECORD_SYMMETRIC_KEY)
568 {
569 allocator().free(keyPtr->KeyData.Data);
570 }
571 }
572
573 void
574 SSDbUniqueRecordImpl::modify(CSSM_DB_RECORDTYPE recordType,
575 const CSSM_DB_RECORD_ATTRIBUTE_DATA *attributes,
576 const CSSM_DATA *data,
577 CSSM_DB_MODIFY_MODE modifyMode)
578 {
579 modify(recordType, attributes, data, modifyMode, NULL);
580 }
581
582 void
583 SSDbUniqueRecordImpl::modify(CSSM_DB_RECORDTYPE recordType,
584 const CSSM_DB_RECORD_ATTRIBUTE_DATA *attributes,
585 const CSSM_DATA *data,
586 CSSM_DB_MODIFY_MODE modifyMode,
587 const CSSM_ACCESS_CREDENTIALS *cred)
588 {
589 if (!data)
590 {
591 DbUniqueRecordImpl::modify(recordType, attributes, NULL, modifyMode);
592 return;
593 }
594
595 // Get the datablob for this record
596 // @@@ Fixme so we don't need to call DbUniqueRecordImpl::get
597 CssmDataContainer oldDataBlob(allocator());
598 DbUniqueRecordImpl::get(NULL, &oldDataBlob);
599
600 if (!SSGroupImpl::isGroup(oldDataBlob))
601 {
602 DbUniqueRecordImpl::modify(recordType, attributes, data, modifyMode);
603 return;
604 }
605
606 // Get the group for oldDataBlob
607 SSGroup group(database(), oldDataBlob);
608
609 // Create a new dataBlob.
610 CssmDataContainer dataBlob(allocator());
611 group->encodeDataBlob(data, cred, dataBlob);
612 DbUniqueRecordImpl::modify(recordType, attributes, &dataBlob, modifyMode);
613 }
614
615 void
616 SSDbUniqueRecordImpl::get(DbAttributes *attributes, ::CssmDataContainer *data)
617 {
618 get(attributes, data, NULL);
619 }
620
621 void
622 SSDbUniqueRecordImpl::get(DbAttributes *attributes, ::CssmDataContainer *data,
623 const CSSM_ACCESS_CREDENTIALS *cred)
624 {
625 if (!data)
626 {
627 DbUniqueRecordImpl::get(attributes, NULL);
628 return;
629 }
630
631 // Get the datablob for this record
632 // @@@ Fixme so we don't need to call DbUniqueRecordImpl::get
633 CssmDataContainer dataBlob(allocator());
634 DbUniqueRecordImpl::get(attributes, &dataBlob);
635
636 if (!SSGroupImpl::isGroup(dataBlob))
637 {
638 data->Data = dataBlob.Data;
639 data->Length = dataBlob.Length;
640 dataBlob.Data = NULL;
641 dataBlob.Length = 0;
642 return;
643 }
644
645 // Get the group for dataBlob
646 SSGroup group(database(), dataBlob);
647
648 // Decode the dataBlob, pass in the DL allocator.
649 group->decodeDataBlob(dataBlob, cred, allocator(), *data);
650 }
651
652 SSGroup
653 SSDbUniqueRecordImpl::group()
654 {
655 // Get the datablob for this record
656 // @@@ Fixme so we don't need to call DbUniqueRecordImpl::get
657 CssmDataContainer dataBlob(allocator());
658 DbUniqueRecordImpl::get(NULL, &dataBlob);
659 return SSGroup(database(), dataBlob);
660 }