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