/ OSX / libsecurity_cdsa_client / lib / securestorage.cpp
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  }