2 * Copyright (c) 2000-2001,2011-2014 Apple Inc. All Rights Reserved.
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
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.
19 #include "securestorage.h"
20 #include <security_cdsa_client/genkey.h>
21 //#include <Security/Access.h> //@@@CONV
22 #include <security_utilities/osxcode.h>
25 using namespace CssmClient
;
26 //using namespace KeychainCore;
29 // Manage CSPDL attachments
31 CSPDLImpl::CSPDLImpl(const Guid
&guid
)
32 : CSPImpl(Cssm::standard()->autoModule(guid
)),
33 DLImpl(CSPImpl::module())
37 CSPDLImpl::CSPDLImpl(const Module
&module)
43 CSPDLImpl::~CSPDLImpl()
49 return; // Prevent re-throw of exception [function-try-block]
52 Allocator
&CSPDLImpl::allocator() const
54 DLImpl::allocator(); return CSPImpl::allocator();
57 void CSPDLImpl::allocator(Allocator
&alloc
)
59 CSPImpl::allocator(alloc
); DLImpl::allocator(alloc
);
62 bool CSPDLImpl::operator <(const CSPDLImpl
&other
) const
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
)));
69 bool CSPDLImpl::operator ==(const CSPDLImpl
&other
) const
71 return (static_cast<const CSPImpl
&>(*this) == static_cast<const CSPImpl
&>(other
)
72 && static_cast<const DLImpl
&>(*this) == static_cast<const DLImpl
&>(other
));
75 CSSM_SERVICE_MASK
CSPDLImpl::subserviceMask() const
77 return CSPImpl::subserviceType() | DLImpl::subserviceType();
80 void CSPDLImpl::subserviceId(uint32 id
)
82 CSPImpl::subserviceId(id
); DLImpl::subserviceId(id
);
89 SSCSPDLImpl::SSCSPDLImpl(const Guid
&guid
) : CSPDLImpl::CSPDLImpl(guid
)
93 SSCSPDLImpl::SSCSPDLImpl(const Module
&module) : CSPDLImpl::CSPDLImpl(module)
97 SSCSPDLImpl::~SSCSPDLImpl()
102 SSCSPDLImpl::newDb(const char *inDbName
, const CSSM_NET_ADDRESS
*inDbLocation
)
104 return new SSDbImpl(SSCSPDL(this), inDbName
, inDbLocation
);
109 // SSDbImpl -- Secure Storage Database Implementation
111 SSDbImpl::SSDbImpl(const SSCSPDL
&cspdl
, const char *inDbName
,
112 const CSSM_NET_ADDRESS
*inDbLocation
)
113 : DbImpl(cspdl
, inDbName
, inDbLocation
)
117 SSDbImpl::~SSDbImpl()
134 SSDbImpl::insert(CSSM_DB_RECORDTYPE recordType
,
135 const CSSM_DB_RECORD_ATTRIBUTE_DATA
*attributes
,
136 const CSSM_DATA
*data
)
138 return DbImpl::insert(recordType
, attributes
, data
);
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
)
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
));
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
;
160 SSDbUniqueRecord ssdbur
= ssInsert(recordType
, attributes
, data
, group
, cred
);
163 // autoCommit was on so commit now that we are done and turn
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
);
173 try { group
->deleteKey(cred
); } catch (...) {}
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
);
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
)
192 // Create an encoded dataBlob for this item.
193 CssmDataContainer
dataBlob(allocator());
194 group
->encodeDataBlob(data
, cred
, dataBlob
);
196 // Insert the record with the new juicy dataBlob.
197 return SSDbUniqueRecord(safe_cast
<SSDbUniqueRecordImpl
*>
198 (&(*DbImpl::insert(recordType
, attributes
, &dataBlob
))));
204 SSDbImpl::newDbCursor(const CSSM_QUERY
&query
, Allocator
&allocator
)
206 return new SSDbCursorImpl(Db(this), query
, allocator
);
210 SSDbImpl::newDbCursor(uint32 capacity
, Allocator
&allocator
)
212 return new SSDbCursorImpl(Db(this), capacity
, allocator
);
216 // SSDbUniqueRecordMaker
218 SSDbImpl::newDbUniqueRecord()
220 return new SSDbUniqueRecordImpl(Db(this));
225 // SSGroup -- Group key with acl, used to protect a group of items.
227 // @@@ Get this from a shared spot.
228 CSSM_DB_NAME_ATTR(SSGroupImpl::kLabel
, 6, (char*) "Label", 0, NULL
, BLOB
);
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())
235 mLabel
.Length
= kLabelSize
;
236 mLabel
.Data
= reinterpret_cast<uint8
*>
237 (mLabel
.mAllocator
.malloc(mLabel
.Length
));
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
);
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
);
247 // Overwrite the first 4 bytes with the magic cookie for a group.
248 reinterpret_cast<uint32
*>(mLabel
.Data
)[0] = h2n(uint32(kGroupMagic
));
250 // @@@ Ensure that the label is unique (Chance of collision is 2^80 --
251 // birthday paradox).
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
);
257 // Set the acl of the key correctly here
258 genKey
.rcc(credAndAclEntry
);
261 genKey(*this, KeySpec(CSSM_KEYUSE_ENCRYPT
|CSSM_KEYUSE_DECRYPT
,
262 CSSM_KEYATTR_PERMANENT
|CSSM_KEYATTR_SENSITIVE
,
265 // Activate ourself so CSSM_FreeKey will get called when we go out of
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())
274 if (dataBlob
.Length
< kLabelSize
+ kIVSize
)
275 CssmError::throwMe(CSSMERR_DL_RECORD_NOT_FOUND
); // Not a SS record
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
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
);
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
291 // Set the key part of ourself.
292 static_cast<CSSM_KEY
&>(*this) =
293 *reinterpret_cast<const CSSM_KEY
*>(keyData
.Data
);
295 // Activate ourself so CSSM_FreeKey will get called when we go out of
301 SSGroupImpl::isGroup(const CSSM_DATA
&dataBlob
)
303 return dataBlob
.Length
>= kLabelSize
+ kIVSize
304 && *reinterpret_cast<const uint32
*>(dataBlob
.Data
) == h2n(uint32(kGroupMagic
));
308 SSGroupImpl::label() const
314 SSGroupImpl::decodeDataBlob(const CSSM_DATA
&dataBlob
,
315 const CSSM_ACCESS_CREDENTIALS
*cred
,
316 Allocator
&allocator
, CSSM_DATA
&data
)
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
));
323 CssmDataContainer
plainText1(allocator
);
324 CssmDataContainer
plainText2(allocator
);
326 // @@@ Don't use staged decrypt once the AppleCSPDL can do combo
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
);
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
);
347 SSGroupImpl::encodeDataBlob(const CSSM_DATA
*data
,
348 const CSSM_ACCESS_CREDENTIALS
*cred
,
349 CssmDataContainer
&dataBlob
)
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
);
355 // Encrypt data using key and encode it in a dataBlob.
357 // First calculate a random IV.
358 uint8 ivBuf
[kIVSize
];
359 CssmData
iv(ivBuf
, kIVSize
);
360 random
.generate(iv
, kIVSize
);
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
));
371 const CssmData nothing
;
372 const CssmData
*plainText
= data
? CssmData::overlay(data
) : ¬hing
;
373 // @@@ Don't use staged encrypt once the AppleCSPDL can do combo
375 CssmDataContainer cipherText1
, cipherText2
;
376 encrypt
.encrypt(plainText
, 1, &cipherText1
, 1);
377 encrypt
.final(cipherText2
);
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
);
395 // SSDbCursorImpl -- Secure Storage Database Cursor Implementation.
397 SSDbCursorImpl::SSDbCursorImpl(const Db
&db
, const CSSM_QUERY
&query
,
398 Allocator
&allocator
)
399 : DbDbCursorImpl(db
, query
, allocator
)
403 SSDbCursorImpl::SSDbCursorImpl(const Db
&db
, uint32 capacity
,
404 Allocator
&allocator
)
405 : DbDbCursorImpl(db
, capacity
, allocator
)
410 SSDbCursorImpl::next(DbAttributes
*attributes
, ::CssmDataContainer
*data
,
411 DbUniqueRecord
&uniqueId
)
413 return next(attributes
, data
, uniqueId
, NULL
);
417 SSDbCursorImpl::next(DbAttributes
*attributes
, ::CssmDataContainer
*data
,
418 DbUniqueRecord
&uniqueId
,
419 const CSSM_ACCESS_CREDENTIALS
*cred
)
422 return DbDbCursorImpl::next(attributes
, data
, uniqueId
);
425 DbAttributes noAttrs
, *attrs
;
426 attrs
= attributes
? attributes
: &noAttrs
;
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.
431 // Get the datablob for this record
432 CssmDataContainer
dataBlob(allocator());
435 if (!DbDbCursorImpl::next(attrs
, &dataBlob
, uniqueId
))
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
)
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
));
450 // This is a non-key item. Return it.
455 // If the caller requested any data, return the data.
457 if (!SSGroupImpl::isGroup(dataBlob
))
459 data
->Data
= dataBlob
.Data
;
460 data
->Length
= dataBlob
.Length
;
461 dataBlob
.Data
= NULL
;
466 // Get the group for dataBlob
467 SSGroup
group(database(), dataBlob
);
469 // TODO: Add attrs to cred
471 // Decode the dataBlob, pass in the DL allocator.
472 group
->decodeDataBlob(dataBlob
, cred
, database()->allocator(), *data
);
478 SSDbCursorImpl::nextKey(DbAttributes
*attributes
, Key
&key
,
479 DbUniqueRecord
&uniqueId
)
481 DbAttributes noAttrs
, *attrs
;
482 attrs
= attributes
? attributes
: &noAttrs
;
483 CssmDataContainer
keyData(database()->allocator());
486 if (!DbDbCursorImpl::next(attrs
, &keyData
, uniqueId
))
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
)
496 key
= Key(database()->csp(), *reinterpret_cast<CSSM_KEY
*>(keyData
.Data
));
501 SSDbCursorImpl::activate()
503 return DbDbCursorImpl::activate();
507 SSDbCursorImpl::deactivate()
509 return DbDbCursorImpl::deactivate();
514 // SSDbUniqueRecordImpl -- Secure Storage UniqueRecord Implementation.
516 SSDbUniqueRecordImpl::SSDbUniqueRecordImpl(const Db
&db
)
517 : DbUniqueRecordImpl(db
)
521 SSDbUniqueRecordImpl::~SSDbUniqueRecordImpl()
526 SSDbUniqueRecordImpl::deleteRecord()
532 SSDbUniqueRecordImpl::deleteRecord(const CSSM_ACCESS_CREDENTIALS
*cred
)
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
;
539 DbUniqueRecordImpl::get(&attributes
, &dataBlob
);
540 CSSM_KEY_PTR keyPtr
= (CSSM_KEY_PTR
) dataBlob
.data();
542 // delete data part first:
543 // (1) don't leave data without keys around
544 // (2) delete orphaned data anyway
545 DbUniqueRecordImpl::deleteRecord();
547 // @@@ Use transactions?
548 if (SSGroupImpl::isGroup(dataBlob
))
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
) {
556 case CSSMERR_DL_RECORD_NOT_FOUND
:
557 // Zombie item (no group key). Finally at peace! No error
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
)
565 allocator().free(keyPtr
->KeyData
.Data
);
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
)
576 allocator().free(keyPtr
->KeyData
.Data
);
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
)
586 modify(recordType
, attributes
, data
, modifyMode
, NULL
);
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
)
598 DbUniqueRecordImpl::modify(recordType
, attributes
, NULL
, modifyMode
);
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
);
607 if (!SSGroupImpl::isGroup(oldDataBlob
))
609 DbUniqueRecordImpl::modify(recordType
, attributes
, data
, modifyMode
);
613 // Get the group for oldDataBlob
614 SSGroup
group(database(), oldDataBlob
);
616 // Create a new dataBlob.
617 CssmDataContainer
dataBlob(allocator());
618 group
->encodeDataBlob(data
, cred
, dataBlob
);
619 DbUniqueRecordImpl::modify(recordType
, attributes
, &dataBlob
, modifyMode
);
623 SSDbUniqueRecordImpl::get(DbAttributes
*attributes
, ::CssmDataContainer
*data
)
625 get(attributes
, data
, NULL
);
629 SSDbUniqueRecordImpl::get(DbAttributes
*attributes
, ::CssmDataContainer
*data
,
630 const CSSM_ACCESS_CREDENTIALS
*cred
)
634 DbUniqueRecordImpl::get(attributes
, NULL
);
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
);
643 if (!SSGroupImpl::isGroup(dataBlob
))
645 data
->Data
= dataBlob
.Data
;
646 data
->Length
= dataBlob
.Length
;
647 dataBlob
.Data
= NULL
;
652 // Get the group for dataBlob
653 SSGroup
group(database(), dataBlob
);
655 // Decode the dataBlob, pass in the DL allocator.
656 group
->decodeDataBlob(dataBlob
, cred
, allocator(), *data
);
660 SSDbUniqueRecordImpl::group()
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
);