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
,
136 const CSSM_RESOURCE_CONTROL_CONTEXT
*rc
)
138 // Get the handle of the DL underlying this CSPDL.
139 CSSM_DL_DB_HANDLE dldbh
;
140 passThrough(CSSM_APPLECSPDL_DB_GET_HANDLE
, NULL
,
141 reinterpret_cast<void **>(&dldbh
));
143 // Turn off autocommit on the underlying DL and remember the old state.
144 CSSM_BOOL autoCommit
= CSSM_TRUE
;
145 check(CSSM_DL_PassThrough(dldbh
, CSSM_APPLEFILEDL_TOGGLE_AUTOCOMMIT
,
146 0, reinterpret_cast<void **>(&autoCommit
)));
147 SSGroup
group(SSDb(this), rc
);
148 const CSSM_ACCESS_CREDENTIALS
*cred
= rc
? rc
->AccessCred
: NULL
;
151 return insert(recordType
, attributes
, data
, group
, cred
);
154 // autoCommit was on so commit now that we are done and turn
156 check(CSSM_DL_PassThrough(dldbh
, CSSM_APPLEFILEDL_COMMIT
, NULL
, NULL
));
157 CSSM_DL_PassThrough(dldbh
, CSSM_APPLEFILEDL_TOGGLE_AUTOCOMMIT
,
158 reinterpret_cast<const void *>(autoCommit
), NULL
);
163 try { group
->deleteKey(cred
); } catch (...) {}
166 // autoCommit was off so rollback since we failed and turn
167 // autoCommit back on.
168 CSSM_DL_PassThrough(dldbh
, CSSM_APPLEFILEDL_ROLLBACK
, NULL
, NULL
);
169 CSSM_DL_PassThrough(dldbh
, CSSM_APPLEFILEDL_TOGGLE_AUTOCOMMIT
,
170 reinterpret_cast<const void *>(autoCommit
), NULL
);
175 // keep the compiler happy -- this path is NEVER taken
176 CssmError::throwMe(0);
180 SSDbImpl::insert(CSSM_DB_RECORDTYPE recordType
,
181 const CSSM_DB_RECORD_ATTRIBUTE_DATA
*attributes
,
182 const CSSM_DATA
*data
, const SSGroup
&group
,
183 const CSSM_ACCESS_CREDENTIALS
*cred
)
185 // Create an encoded dataBlob for this item.
186 CssmDataContainer
dataBlob(allocator());
187 group
->encodeDataBlob(data
, cred
, dataBlob
);
189 // Insert the record with the new juicy dataBlob.
190 return SSDbUniqueRecord(safe_cast
<SSDbUniqueRecordImpl
*>
191 (&(*DbImpl::insert(recordType
, attributes
, &dataBlob
))));
197 SSDbImpl::newDbCursor(const CSSM_QUERY
&query
, Allocator
&allocator
)
199 return new SSDbCursorImpl(Db(this), query
, allocator
);
203 SSDbImpl::newDbCursor(uint32 capacity
, Allocator
&allocator
)
205 return new SSDbCursorImpl(Db(this), capacity
, allocator
);
209 // SSDbUniqueRecordMaker
211 SSDbImpl::newDbUniqueRecord()
213 return new SSDbUniqueRecordImpl(Db(this));
218 // SSGroup -- Group key with acl, used to protect a group of items.
220 // @@@ Get this from a shared spot.
221 CSSM_DB_NAME_ATTR(SSGroupImpl::kLabel
, 6, (char*) "Label", 0, NULL
, BLOB
);
223 // Create a new group.
224 SSGroupImpl::SSGroupImpl(const SSDb
&ssDb
,
225 const CSSM_RESOURCE_CONTROL_CONTEXT
*credAndAclEntry
)
226 : KeyImpl(ssDb
->csp()), mLabel(ssDb
->allocator())
228 mLabel
.Length
= kLabelSize
;
229 mLabel
.Data
= reinterpret_cast<uint8
*>
230 (mLabel
.mAllocator
.malloc(mLabel
.Length
));
232 // Get our csp and set up a random number generation context.
233 CSP
csp(this->csp());
234 Random
random(csp
, CSSM_ALGID_APPLE_YARROW
);
236 // Generate a kLabelSize byte random number that will be the label of
237 // the key which we store in the dataBlob.
238 random
.generate(mLabel
, (uint32
)mLabel
.Length
);
240 // Overwrite the first 4 bytes with the magic cookie for a group.
241 reinterpret_cast<uint32
*>(mLabel
.Data
)[0] = h2n(uint32(kGroupMagic
));
243 // @@@ Ensure that the label is unique (Chance of collision is 2^80 --
244 // birthday paradox).
246 // Generate a permanent 3DES key that we will use to encrypt the data.
247 GenerateKey
genKey(csp
, CSSM_ALGID_3DES_3KEY
, 192);
248 genKey
.database(ssDb
);
250 // Set the acl of the key correctly here
251 genKey
.rcc(credAndAclEntry
);
254 genKey(*this, KeySpec(CSSM_KEYUSE_ENCRYPT
|CSSM_KEYUSE_DECRYPT
,
255 CSSM_KEYATTR_PERMANENT
|CSSM_KEYATTR_SENSITIVE
,
258 // Activate ourself so CSSM_FreeKey will get called when we go out of
263 // Lookup an existing group based on a dataBlob.
264 SSGroupImpl::SSGroupImpl(const SSDb
&ssDb
, const CSSM_DATA
&dataBlob
)
265 : KeyImpl(ssDb
->csp()), mLabel(ssDb
->allocator())
267 if (dataBlob
.Length
< kLabelSize
+ kIVSize
)
268 CssmError::throwMe(CSSMERR_DL_RECORD_NOT_FOUND
); // Not a SS record
270 mLabel
= CssmData(dataBlob
.Data
, kLabelSize
);
271 if (*reinterpret_cast<const uint32
*>(mLabel
.Data
) != h2n (uint32(kGroupMagic
)))
272 CssmError::throwMe(CSSMERR_DL_RECORD_NOT_FOUND
); // Not a SS record
274 // Look up the symmetric key with that label.
275 DbCursor
cursor(new DbDbCursorImpl(ssDb
, 0, Allocator::standard()));
276 cursor
->recordType(CSSM_DL_DB_RECORD_SYMMETRIC_KEY
);
277 cursor
->add(CSSM_DB_EQUAL
, kLabel
, mLabel
);
279 DbUniqueRecord keyId
;
280 CssmDataContainer
keyData(ssDb
->allocator());
281 if (!cursor
->next(NULL
, &keyData
, keyId
))
282 CssmError::throwMe(CSSMERR_DL_RECORD_NOT_FOUND
); // The key can't be found
284 // Set the key part of ourself.
285 static_cast<CSSM_KEY
&>(*this) =
286 *reinterpret_cast<const CSSM_KEY
*>(keyData
.Data
);
288 // Activate ourself so CSSM_FreeKey will get called when we go out of
294 SSGroupImpl::isGroup(const CSSM_DATA
&dataBlob
)
296 return dataBlob
.Length
>= kLabelSize
+ kIVSize
297 && *reinterpret_cast<const uint32
*>(dataBlob
.Data
) == h2n(uint32(kGroupMagic
));
301 SSGroupImpl::label() const
307 SSGroupImpl::decodeDataBlob(const CSSM_DATA
&dataBlob
,
308 const CSSM_ACCESS_CREDENTIALS
*cred
,
309 Allocator
&allocator
, CSSM_DATA
&data
)
311 // First get the IV and the cipherText from the blob.
312 CssmData
iv(&dataBlob
.Data
[kLabelSize
], kIVSize
);
313 CssmData
cipherText(&dataBlob
.Data
[kLabelSize
+ kIVSize
],
314 dataBlob
.Length
- (kLabelSize
+ kIVSize
));
316 CssmDataContainer
plainText1(allocator
);
317 CssmDataContainer
plainText2(allocator
);
319 // @@@ Don't use staged decrypt once the AppleCSPDL can do combo
321 // Setup decryption context
322 Decrypt
decrypt(csp(), algorithm());
323 decrypt
.mode(CSSM_ALGMODE_CBCPadIV8
);
324 decrypt
.padding(CSSM_PADDING_PKCS1
);
325 decrypt
.initVector(iv
);
326 decrypt
.key(Key(this));
327 decrypt
.cred(AccessCredentials::overlay(cred
));
328 decrypt
.decrypt(&cipherText
, 1, &plainText1
, 1);
329 decrypt
.final(plainText2
);
331 // Use DL allocator for allocating memory for data.
332 CSSM_SIZE length
= plainText1
.Length
+ plainText2
.Length
;
333 data
.Data
= allocator
.alloc
<uint8
>((UInt32
)length
);
334 data
.Length
= length
;
335 memcpy(data
.Data
, plainText1
.Data
, plainText1
.Length
);
336 memcpy(&data
.Data
[plainText1
.Length
], plainText2
.Data
, plainText2
.Length
);
340 SSGroupImpl::encodeDataBlob(const CSSM_DATA
*data
,
341 const CSSM_ACCESS_CREDENTIALS
*cred
,
342 CssmDataContainer
&dataBlob
)
344 // Get our csp and set up a random number generation context.
345 CSP
csp(this->csp());
346 Random
random(csp
, CSSM_ALGID_APPLE_YARROW
);
348 // Encrypt data using key and encode it in a dataBlob.
350 // First calculate a random IV.
351 uint8 ivBuf
[kIVSize
];
352 CssmData
iv(ivBuf
, kIVSize
);
353 random
.generate(iv
, kIVSize
);
355 // Setup encryption context
356 Encrypt
encrypt(csp
, algorithm());
357 encrypt
.mode(CSSM_ALGMODE_CBCPadIV8
);
358 encrypt
.padding(CSSM_PADDING_PKCS1
);
359 encrypt
.initVector(iv
);
360 encrypt
.key(Key(this));
361 encrypt
.cred(AccessCredentials::overlay(cred
));
364 const CssmData nothing
;
365 const CssmData
*plainText
= data
? CssmData::overlay(data
) : ¬hing
;
366 // @@@ Don't use staged encrypt once the AppleCSPDL can do combo
368 CssmDataContainer cipherText1
, cipherText2
;
369 encrypt
.encrypt(plainText
, 1, &cipherText1
, 1);
370 encrypt
.final(cipherText2
);
372 // Create a dataBlob containing the label followed by the IV followed
373 // by the cipherText.
374 CSSM_SIZE length
= (kLabelSize
+ kIVSize
375 + cipherText1
.Length
+ cipherText2
.Length
);
376 dataBlob
.Data
= dataBlob
.mAllocator
.alloc
<uint8
>((UInt32
)length
);
377 dataBlob
.Length
= length
;
378 memcpy(dataBlob
.Data
, mLabel
.Data
, kLabelSize
);
379 memcpy(&dataBlob
.Data
[kLabelSize
], iv
.Data
, kIVSize
);
380 memcpy(&dataBlob
.Data
[kLabelSize
+ kIVSize
],
381 cipherText1
.Data
, cipherText1
.Length
);
382 memcpy(&dataBlob
.Data
[kLabelSize
+ kIVSize
+ cipherText1
.Length
],
383 cipherText2
.Data
, cipherText2
.Length
);
388 // SSDbCursorImpl -- Secure Storage Database Cursor Implementation.
390 SSDbCursorImpl::SSDbCursorImpl(const Db
&db
, const CSSM_QUERY
&query
,
391 Allocator
&allocator
)
392 : DbDbCursorImpl(db
, query
, allocator
)
396 SSDbCursorImpl::SSDbCursorImpl(const Db
&db
, uint32 capacity
,
397 Allocator
&allocator
)
398 : DbDbCursorImpl(db
, capacity
, allocator
)
403 SSDbCursorImpl::next(DbAttributes
*attributes
, ::CssmDataContainer
*data
,
404 DbUniqueRecord
&uniqueId
)
406 return next(attributes
, data
, uniqueId
, NULL
);
410 SSDbCursorImpl::next(DbAttributes
*attributes
, ::CssmDataContainer
*data
,
411 DbUniqueRecord
&uniqueId
,
412 const CSSM_ACCESS_CREDENTIALS
*cred
)
415 return DbDbCursorImpl::next(attributes
, data
, uniqueId
);
417 DbAttributes noAttrs
, *attrs
;
418 attrs
= attributes
? attributes
: &noAttrs
;
420 // Get the datablob for this record
421 CssmDataContainer
dataBlob(allocator());
424 if (!DbDbCursorImpl::next(attrs
, &dataBlob
, uniqueId
))
427 // Keep going until we find a non key type record.
428 CSSM_DB_RECORDTYPE rt
= attrs
->recordType();
429 if (rt
!= CSSM_DL_DB_RECORD_SYMMETRIC_KEY
430 && rt
!= CSSM_DL_DB_RECORD_PRIVATE_KEY
431 && rt
!= CSSM_DL_DB_RECORD_PUBLIC_KEY
)
433 // @@@ Check the label and if it doesn't start with the magic for a SSKey return the key.
438 // Free the key we just retrieved
439 database()->csp()->freeKey(*reinterpret_cast<CssmKey
*>(dataBlob
.Data
));
443 if (!SSGroupImpl::isGroup(dataBlob
))
445 data
->Data
= dataBlob
.Data
;
446 data
->Length
= dataBlob
.Length
;
447 dataBlob
.Data
= NULL
;
452 // Get the group for dataBlob
453 SSGroup
group(database(), dataBlob
);
455 // Decode the dataBlob, pass in the DL allocator.
456 group
->decodeDataBlob(dataBlob
, cred
, database()->allocator(), *data
);
461 SSDbCursorImpl::nextKey(DbAttributes
*attributes
, Key
&key
,
462 DbUniqueRecord
&uniqueId
)
464 DbAttributes noAttrs
, *attrs
;
465 attrs
= attributes
? attributes
: &noAttrs
;
466 CssmDataContainer
keyData(database()->allocator());
469 if (!DbDbCursorImpl::next(attrs
, &keyData
, uniqueId
))
471 // Keep going until we find a key type record.
472 CSSM_DB_RECORDTYPE rt
= attrs
->recordType();
473 if (rt
== CSSM_DL_DB_RECORD_SYMMETRIC_KEY
474 || rt
== CSSM_DL_DB_RECORD_PRIVATE_KEY
475 || rt
== CSSM_DL_DB_RECORD_PUBLIC_KEY
)
479 key
= Key(database()->csp(), *reinterpret_cast<CSSM_KEY
*>(keyData
.Data
));
484 SSDbCursorImpl::activate()
486 return DbDbCursorImpl::activate();
490 SSDbCursorImpl::deactivate()
492 return DbDbCursorImpl::deactivate();
497 // SSDbUniqueRecordImpl -- Secure Storage UniqueRecord Implementation.
499 SSDbUniqueRecordImpl::SSDbUniqueRecordImpl(const Db
&db
)
500 : DbUniqueRecordImpl(db
)
504 SSDbUniqueRecordImpl::~SSDbUniqueRecordImpl()
509 SSDbUniqueRecordImpl::deleteRecord()
515 SSDbUniqueRecordImpl::deleteRecord(const CSSM_ACCESS_CREDENTIALS
*cred
)
517 // Get the datablob for this record
518 // @@@ Fixme so we don't need to call DbUniqueRecordImpl::get
519 CssmDataContainer
dataBlob(allocator());
520 DbAttributes attributes
;
522 DbUniqueRecordImpl::get(&attributes
, &dataBlob
);
523 CSSM_KEY_PTR keyPtr
= (CSSM_KEY_PTR
) dataBlob
.data();
525 // delete data part first:
526 // (1) don't leave data without keys around
527 // (2) delete orphaned data anyway
528 DbUniqueRecordImpl::deleteRecord();
530 // @@@ Use transactions?
531 if (SSGroupImpl::isGroup(dataBlob
))
533 // Get the group for dataBlob
534 SSGroup
group(database(), dataBlob
);
535 // Delete the group (key)
536 group
->deleteKey(cred
);
537 } catch (const CssmError
&err
) {
539 case CSSMERR_DL_RECORD_NOT_FOUND
:
540 // Zombie item (no group key). Finally at peace! No error
544 if (attributes
.recordType() == CSSM_DL_DB_RECORD_PUBLIC_KEY
||
545 attributes
.recordType() == CSSM_DL_DB_RECORD_PRIVATE_KEY
||
546 attributes
.recordType() == CSSM_DL_DB_RECORD_SYMMETRIC_KEY
)
548 allocator().free(keyPtr
->KeyData
.Data
);
555 if (attributes
.recordType() == CSSM_DL_DB_RECORD_PUBLIC_KEY
||
556 attributes
.recordType() == CSSM_DL_DB_RECORD_PRIVATE_KEY
||
557 attributes
.recordType() == CSSM_DL_DB_RECORD_SYMMETRIC_KEY
)
559 allocator().free(keyPtr
->KeyData
.Data
);
564 SSDbUniqueRecordImpl::modify(CSSM_DB_RECORDTYPE recordType
,
565 const CSSM_DB_RECORD_ATTRIBUTE_DATA
*attributes
,
566 const CSSM_DATA
*data
,
567 CSSM_DB_MODIFY_MODE modifyMode
)
569 modify(recordType
, attributes
, data
, modifyMode
, NULL
);
573 SSDbUniqueRecordImpl::modify(CSSM_DB_RECORDTYPE recordType
,
574 const CSSM_DB_RECORD_ATTRIBUTE_DATA
*attributes
,
575 const CSSM_DATA
*data
,
576 CSSM_DB_MODIFY_MODE modifyMode
,
577 const CSSM_ACCESS_CREDENTIALS
*cred
)
581 DbUniqueRecordImpl::modify(recordType
, attributes
, NULL
, modifyMode
);
585 // Get the datablob for this record
586 // @@@ Fixme so we don't need to call DbUniqueRecordImpl::get
587 CssmDataContainer
oldDataBlob(allocator());
588 DbUniqueRecordImpl::get(NULL
, &oldDataBlob
);
590 if (!SSGroupImpl::isGroup(oldDataBlob
))
592 DbUniqueRecordImpl::modify(recordType
, attributes
, data
, modifyMode
);
596 // Get the group for oldDataBlob
597 SSGroup
group(database(), oldDataBlob
);
599 // Create a new dataBlob.
600 CssmDataContainer
dataBlob(allocator());
601 group
->encodeDataBlob(data
, cred
, dataBlob
);
602 DbUniqueRecordImpl::modify(recordType
, attributes
, &dataBlob
, modifyMode
);
606 SSDbUniqueRecordImpl::get(DbAttributes
*attributes
, ::CssmDataContainer
*data
)
608 get(attributes
, data
, NULL
);
612 SSDbUniqueRecordImpl::get(DbAttributes
*attributes
, ::CssmDataContainer
*data
,
613 const CSSM_ACCESS_CREDENTIALS
*cred
)
617 DbUniqueRecordImpl::get(attributes
, NULL
);
621 // Get the datablob for this record
622 // @@@ Fixme so we don't need to call DbUniqueRecordImpl::get
623 CssmDataContainer
dataBlob(allocator());
624 DbUniqueRecordImpl::get(attributes
, &dataBlob
);
626 if (!SSGroupImpl::isGroup(dataBlob
))
628 data
->Data
= dataBlob
.Data
;
629 data
->Length
= dataBlob
.Length
;
630 dataBlob
.Data
= NULL
;
635 // Get the group for dataBlob
636 SSGroup
group(database(), dataBlob
);
638 // Decode the dataBlob, pass in the DL allocator.
639 group
->decodeDataBlob(dataBlob
, cred
, allocator(), *data
);
643 SSDbUniqueRecordImpl::group()
645 // Get the datablob for this record
646 // @@@ Fixme so we don't need to call DbUniqueRecordImpl::get
647 CssmDataContainer
dataBlob(allocator());
648 DbUniqueRecordImpl::get(NULL
, &dataBlob
);
649 return SSGroup(database(), dataBlob
);