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()
51 Allocator
&CSPDLImpl::allocator() const
53 DLImpl::allocator(); return CSPImpl::allocator();
56 void CSPDLImpl::allocator(Allocator
&alloc
)
58 CSPImpl::allocator(alloc
); DLImpl::allocator(alloc
);
61 bool CSPDLImpl::operator <(const CSPDLImpl
&other
) const
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
)));
68 bool CSPDLImpl::operator ==(const CSPDLImpl
&other
) const
70 return (static_cast<const CSPImpl
&>(*this) == static_cast<const CSPImpl
&>(other
)
71 && static_cast<const DLImpl
&>(*this) == static_cast<const DLImpl
&>(other
));
74 CSSM_SERVICE_MASK
CSPDLImpl::subserviceMask() const
76 return CSPImpl::subserviceType() | DLImpl::subserviceType();
79 void CSPDLImpl::subserviceId(uint32 id
)
81 CSPImpl::subserviceId(id
); DLImpl::subserviceId(id
);
88 SSCSPDLImpl::SSCSPDLImpl(const Guid
&guid
) : CSPDLImpl::CSPDLImpl(guid
)
92 SSCSPDLImpl::SSCSPDLImpl(const Module
&module) : CSPDLImpl::CSPDLImpl(module)
96 SSCSPDLImpl::~SSCSPDLImpl()
101 SSCSPDLImpl::newDb(const char *inDbName
, const CSSM_NET_ADDRESS
*inDbLocation
)
103 return new SSDbImpl(SSCSPDL(this), inDbName
, inDbLocation
);
108 // SSDbImpl -- Secure Storage Database Implementation
110 SSDbImpl::SSDbImpl(const SSCSPDL
&cspdl
, const char *inDbName
,
111 const CSSM_NET_ADDRESS
*inDbLocation
)
112 : DbImpl(cspdl
, inDbName
, inDbLocation
)
116 SSDbImpl::~SSDbImpl()
133 SSDbImpl::insert(CSSM_DB_RECORDTYPE recordType
,
134 const CSSM_DB_RECORD_ATTRIBUTE_DATA
*attributes
,
135 const CSSM_DATA
*data
)
137 return DbImpl::insert(recordType
, attributes
, data
);
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
)
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
));
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
;
159 SSDbUniqueRecord ssdbur
= ssInsert(recordType
, attributes
, data
, group
, cred
);
162 // autoCommit was on so commit now that we are done and turn
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
);
172 try { group
->deleteKey(cred
); } catch (...) {}
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
);
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
)
191 // Create an encoded dataBlob for this item.
192 CssmDataContainer
dataBlob(allocator());
193 group
->encodeDataBlob(data
, cred
, dataBlob
);
195 // Insert the record with the new juicy dataBlob.
196 return SSDbUniqueRecord(safe_cast
<SSDbUniqueRecordImpl
*>
197 (&(*DbImpl::insert(recordType
, attributes
, &dataBlob
))));
203 SSDbImpl::newDbCursor(const CSSM_QUERY
&query
, Allocator
&allocator
)
205 return new SSDbCursorImpl(Db(this), query
, allocator
);
209 SSDbImpl::newDbCursor(uint32 capacity
, Allocator
&allocator
)
211 return new SSDbCursorImpl(Db(this), capacity
, allocator
);
215 // SSDbUniqueRecordMaker
217 SSDbImpl::newDbUniqueRecord()
219 return new SSDbUniqueRecordImpl(Db(this));
224 // SSGroup -- Group key with acl, used to protect a group of items.
226 // @@@ Get this from a shared spot.
227 CSSM_DB_NAME_ATTR(SSGroupImpl::kLabel
, 6, (char*) "Label", 0, NULL
, BLOB
);
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())
234 mLabel
.Length
= kLabelSize
;
235 mLabel
.Data
= reinterpret_cast<uint8
*>
236 (mLabel
.mAllocator
.malloc(mLabel
.Length
));
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
);
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
);
246 // Overwrite the first 4 bytes with the magic cookie for a group.
247 reinterpret_cast<uint32
*>(mLabel
.Data
)[0] = h2n(uint32(kGroupMagic
));
249 // @@@ Ensure that the label is unique (Chance of collision is 2^80 --
250 // birthday paradox).
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
);
256 // Set the acl of the key correctly here
257 genKey
.rcc(credAndAclEntry
);
260 genKey(*this, KeySpec(CSSM_KEYUSE_ENCRYPT
|CSSM_KEYUSE_DECRYPT
,
261 CSSM_KEYATTR_PERMANENT
|CSSM_KEYATTR_SENSITIVE
,
264 // Activate ourself so CSSM_FreeKey will get called when we go out of
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())
273 if (dataBlob
.Length
< kLabelSize
+ kIVSize
)
274 CssmError::throwMe(CSSMERR_DL_RECORD_NOT_FOUND
); // Not a SS record
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
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
);
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
290 // Set the key part of ourself.
291 static_cast<CSSM_KEY
&>(*this) =
292 *reinterpret_cast<const CSSM_KEY
*>(keyData
.Data
);
294 // Activate ourself so CSSM_FreeKey will get called when we go out of
300 SSGroupImpl::isGroup(const CSSM_DATA
&dataBlob
)
302 return dataBlob
.Length
>= kLabelSize
+ kIVSize
303 && *reinterpret_cast<const uint32
*>(dataBlob
.Data
) == h2n(uint32(kGroupMagic
));
307 SSGroupImpl::label() const
313 SSGroupImpl::decodeDataBlob(const CSSM_DATA
&dataBlob
,
314 const CSSM_ACCESS_CREDENTIALS
*cred
,
315 Allocator
&allocator
, CSSM_DATA
&data
)
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
));
322 CssmDataContainer
plainText1(allocator
);
323 CssmDataContainer
plainText2(allocator
);
325 // @@@ Don't use staged decrypt once the AppleCSPDL can do combo
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
);
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
);
346 SSGroupImpl::encodeDataBlob(const CSSM_DATA
*data
,
347 const CSSM_ACCESS_CREDENTIALS
*cred
,
348 CssmDataContainer
&dataBlob
)
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
);
354 // Encrypt data using key and encode it in a dataBlob.
356 // First calculate a random IV.
357 uint8 ivBuf
[kIVSize
];
358 CssmData
iv(ivBuf
, kIVSize
);
359 random
.generate(iv
, kIVSize
);
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
));
370 const CssmData nothing
;
371 const CssmData
*plainText
= data
? CssmData::overlay(data
) : ¬hing
;
372 // @@@ Don't use staged encrypt once the AppleCSPDL can do combo
374 CssmDataContainer cipherText1
, cipherText2
;
375 encrypt
.encrypt(plainText
, 1, &cipherText1
, 1);
376 encrypt
.final(cipherText2
);
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
);
394 // SSDbCursorImpl -- Secure Storage Database Cursor Implementation.
396 SSDbCursorImpl::SSDbCursorImpl(const Db
&db
, const CSSM_QUERY
&query
,
397 Allocator
&allocator
)
398 : DbDbCursorImpl(db
, query
, allocator
)
402 SSDbCursorImpl::SSDbCursorImpl(const Db
&db
, uint32 capacity
,
403 Allocator
&allocator
)
404 : DbDbCursorImpl(db
, capacity
, allocator
)
409 SSDbCursorImpl::next(DbAttributes
*attributes
, ::CssmDataContainer
*data
,
410 DbUniqueRecord
&uniqueId
)
412 return next(attributes
, data
, uniqueId
, NULL
);
416 SSDbCursorImpl::next(DbAttributes
*attributes
, ::CssmDataContainer
*data
,
417 DbUniqueRecord
&uniqueId
,
418 const CSSM_ACCESS_CREDENTIALS
*cred
)
421 return DbDbCursorImpl::next(attributes
, data
, uniqueId
);
424 DbAttributes noAttrs
, *attrs
;
425 attrs
= attributes
? attributes
: &noAttrs
;
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.
430 // Get the datablob for this record
431 CssmDataContainer
dataBlob(allocator());
434 if (!DbDbCursorImpl::next(attrs
, &dataBlob
, uniqueId
))
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
)
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
));
449 // This is a non-key item. Return it.
454 // If the caller requested any data, return the data.
456 if (!SSGroupImpl::isGroup(dataBlob
))
458 data
->Data
= dataBlob
.Data
;
459 data
->Length
= dataBlob
.Length
;
460 dataBlob
.Data
= NULL
;
465 // Get the group for dataBlob
466 SSGroup
group(database(), dataBlob
);
468 // TODO: Add attrs to cred
470 // Decode the dataBlob, pass in the DL allocator.
471 group
->decodeDataBlob(dataBlob
, cred
, database()->allocator(), *data
);
477 SSDbCursorImpl::nextKey(DbAttributes
*attributes
, Key
&key
,
478 DbUniqueRecord
&uniqueId
)
480 DbAttributes noAttrs
, *attrs
;
481 attrs
= attributes
? attributes
: &noAttrs
;
482 CssmDataContainer
keyData(database()->allocator());
485 if (!DbDbCursorImpl::next(attrs
, &keyData
, uniqueId
))
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
)
495 key
= Key(database()->csp(), *reinterpret_cast<CSSM_KEY
*>(keyData
.Data
));
500 SSDbCursorImpl::activate()
502 return DbDbCursorImpl::activate();
506 SSDbCursorImpl::deactivate()
508 return DbDbCursorImpl::deactivate();
513 // SSDbUniqueRecordImpl -- Secure Storage UniqueRecord Implementation.
515 SSDbUniqueRecordImpl::SSDbUniqueRecordImpl(const Db
&db
)
516 : DbUniqueRecordImpl(db
)
520 SSDbUniqueRecordImpl::~SSDbUniqueRecordImpl()
525 SSDbUniqueRecordImpl::deleteRecord()
531 SSDbUniqueRecordImpl::deleteRecord(const CSSM_ACCESS_CREDENTIALS
*cred
)
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
;
538 DbUniqueRecordImpl::get(&attributes
, &dataBlob
);
539 CSSM_KEY_PTR keyPtr
= (CSSM_KEY_PTR
) dataBlob
.data();
541 // delete data part first:
542 // (1) don't leave data without keys around
543 // (2) delete orphaned data anyway
544 DbUniqueRecordImpl::deleteRecord();
546 // @@@ Use transactions?
547 if (SSGroupImpl::isGroup(dataBlob
))
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
) {
555 case CSSMERR_DL_RECORD_NOT_FOUND
:
556 // Zombie item (no group key). Finally at peace! No error
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
)
564 allocator().free(keyPtr
->KeyData
.Data
);
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
)
575 allocator().free(keyPtr
->KeyData
.Data
);
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
)
585 modify(recordType
, attributes
, data
, modifyMode
, NULL
);
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
)
597 DbUniqueRecordImpl::modify(recordType
, attributes
, NULL
, modifyMode
);
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
);
606 if (!SSGroupImpl::isGroup(oldDataBlob
))
608 DbUniqueRecordImpl::modify(recordType
, attributes
, data
, modifyMode
);
612 // Get the group for oldDataBlob
613 SSGroup
group(database(), oldDataBlob
);
615 // Create a new dataBlob.
616 CssmDataContainer
dataBlob(allocator());
617 group
->encodeDataBlob(data
, cred
, dataBlob
);
618 DbUniqueRecordImpl::modify(recordType
, attributes
, &dataBlob
, modifyMode
);
622 SSDbUniqueRecordImpl::get(DbAttributes
*attributes
, ::CssmDataContainer
*data
)
624 get(attributes
, data
, NULL
);
628 SSDbUniqueRecordImpl::get(DbAttributes
*attributes
, ::CssmDataContainer
*data
,
629 const CSSM_ACCESS_CREDENTIALS
*cred
)
633 DbUniqueRecordImpl::get(attributes
, NULL
);
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
);
642 if (!SSGroupImpl::isGroup(dataBlob
))
644 data
->Data
= dataBlob
.Data
;
645 data
->Length
= dataBlob
.Length
;
646 dataBlob
.Data
= NULL
;
651 // Get the group for dataBlob
652 SSGroup
group(database(), dataBlob
);
654 // Decode the dataBlob, pass in the DL allocator.
655 group
->decodeDataBlob(dataBlob
, cred
, allocator(), *data
);
659 SSDbUniqueRecordImpl::group()
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
);