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