2 * Copyright (c) 2000-2001 Apple Computer, 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()
47 Allocator
&CSPDLImpl::allocator() const
49 DLImpl::allocator(); return CSPImpl::allocator();
52 void CSPDLImpl::allocator(Allocator
&alloc
)
54 CSPImpl::allocator(alloc
); DLImpl::allocator(alloc
);
57 bool CSPDLImpl::operator <(const CSPDLImpl
&other
) const
59 return (static_cast<const CSPImpl
&>(*this) < static_cast<const CSPImpl
&>(other
) ||
60 (!(static_cast<const CSPImpl
&>(other
) < static_cast<const CSPImpl
&>(*this))
61 && static_cast<const DLImpl
&>(*this) < static_cast<const DLImpl
&>(other
)));
64 bool CSPDLImpl::operator ==(const CSPDLImpl
&other
) const
66 return (static_cast<const CSPImpl
&>(*this) == static_cast<const CSPImpl
&>(other
)
67 && static_cast<const DLImpl
&>(*this) == static_cast<const DLImpl
&>(other
));
70 CSSM_SERVICE_MASK
CSPDLImpl::subserviceMask() const
72 return CSPImpl::subserviceType() | DLImpl::subserviceType();
75 void CSPDLImpl::subserviceId(uint32 id
)
77 CSPImpl::subserviceId(id
); DLImpl::subserviceId(id
);
84 SSCSPDLImpl::SSCSPDLImpl(const Guid
&guid
) : CSPDLImpl::CSPDLImpl(guid
)
88 SSCSPDLImpl::SSCSPDLImpl(const Module
&module) : CSPDLImpl::CSPDLImpl(module)
92 SSCSPDLImpl::~SSCSPDLImpl()
97 SSCSPDLImpl::newDb(const char *inDbName
, const CSSM_NET_ADDRESS
*inDbLocation
)
99 return new SSDbImpl(SSCSPDL(this), inDbName
, inDbLocation
);
104 // SSDbImpl -- Secure Storage Database Implementation
106 SSDbImpl::SSDbImpl(const SSCSPDL
&cspdl
, const char *inDbName
,
107 const CSSM_NET_ADDRESS
*inDbLocation
)
108 : DbImpl(cspdl
, inDbName
, inDbLocation
)
112 SSDbImpl::~SSDbImpl()
129 SSDbImpl::insert(CSSM_DB_RECORDTYPE recordType
,
130 const CSSM_DB_RECORD_ATTRIBUTE_DATA
*attributes
,
131 const CSSM_DATA
*data
,
132 const CSSM_RESOURCE_CONTROL_CONTEXT
*rc
)
134 // Get the handle of the DL underlying this CSPDL.
135 CSSM_DL_DB_HANDLE dldbh
;
136 passThrough(CSSM_APPLECSPDL_DB_GET_HANDLE
, NULL
,
137 reinterpret_cast<void **>(&dldbh
));
139 // Turn off autocommit on the underlying DL and remember the old state.
140 CSSM_BOOL autoCommit
= CSSM_TRUE
;
141 check(CSSM_DL_PassThrough(dldbh
, CSSM_APPLEFILEDL_TOGGLE_AUTOCOMMIT
,
142 0, reinterpret_cast<void **>(&autoCommit
)));
143 SSGroup
group(SSDb(this), rc
);
144 const CSSM_ACCESS_CREDENTIALS
*cred
= rc
? rc
->AccessCred
: NULL
;
147 return insert(recordType
, attributes
, data
, group
, cred
);
150 // autoCommit was on so commit now that we are done and turn
152 check(CSSM_DL_PassThrough(dldbh
, CSSM_APPLEFILEDL_COMMIT
, NULL
, NULL
));
153 CSSM_DL_PassThrough(dldbh
, CSSM_APPLEFILEDL_TOGGLE_AUTOCOMMIT
,
154 reinterpret_cast<const void *>(autoCommit
), NULL
);
159 try { group
->deleteKey(cred
); } catch (...) {}
162 // autoCommit was off so rollback since we failed and turn
163 // autoCommit back on.
164 CSSM_DL_PassThrough(dldbh
, CSSM_APPLEFILEDL_ROLLBACK
, NULL
, NULL
);
165 CSSM_DL_PassThrough(dldbh
, CSSM_APPLEFILEDL_TOGGLE_AUTOCOMMIT
,
166 reinterpret_cast<const void *>(autoCommit
), NULL
);
171 // keep the compiler happy -- this path is NEVER taken
172 CssmError::throwMe(0);
176 SSDbImpl::insert(CSSM_DB_RECORDTYPE recordType
,
177 const CSSM_DB_RECORD_ATTRIBUTE_DATA
*attributes
,
178 const CSSM_DATA
*data
, const SSGroup
&group
,
179 const CSSM_ACCESS_CREDENTIALS
*cred
)
181 // Create an encoded dataBlob for this item.
182 CssmDataContainer
dataBlob(allocator());
183 group
->encodeDataBlob(data
, cred
, dataBlob
);
185 // Insert the record with the new juicy dataBlob.
186 return SSDbUniqueRecord(safe_cast
<SSDbUniqueRecordImpl
*>
187 (&(*DbImpl::insert(recordType
, attributes
, &dataBlob
))));
193 SSDbImpl::newDbCursor(const CSSM_QUERY
&query
, Allocator
&allocator
)
195 return new SSDbCursorImpl(Db(this), query
, allocator
);
199 SSDbImpl::newDbCursor(uint32 capacity
, Allocator
&allocator
)
201 return new SSDbCursorImpl(Db(this), capacity
, allocator
);
205 // SSDbUniqueRecordMaker
207 SSDbImpl::newDbUniqueRecord()
209 return new SSDbUniqueRecordImpl(Db(this));
214 // SSGroup -- Group key with acl, used to protect a group of items.
216 // @@@ Get this from a shared spot.
217 CSSM_DB_NAME_ATTR(SSGroupImpl::kLabel
, 6, (char*) "Label", 0, NULL
, BLOB
);
219 // Create a new group.
220 SSGroupImpl::SSGroupImpl(const SSDb
&ssDb
,
221 const CSSM_RESOURCE_CONTROL_CONTEXT
*credAndAclEntry
)
222 : KeyImpl(ssDb
->csp()), mLabel(ssDb
->allocator())
224 mLabel
.Length
= kLabelSize
;
225 mLabel
.Data
= reinterpret_cast<uint8
*>
226 (mLabel
.mAllocator
.malloc(mLabel
.Length
));
228 // Get our csp and set up a random number generation context.
229 CSP
csp(this->csp());
230 Random
random(csp
, CSSM_ALGID_APPLE_YARROW
);
232 // Generate a kLabelSize byte random number that will be the label of
233 // the key which we store in the dataBlob.
234 random
.generate(mLabel
, mLabel
.Length
);
236 // Overwrite the first 4 bytes with the magic cookie for a group.
237 reinterpret_cast<uint32
*>(mLabel
.Data
)[0] = h2n(uint32(kGroupMagic
));
239 // @@@ Ensure that the label is unique (Chance of collision is 2^80 --
240 // birthday paradox).
242 // Generate a permanent 3DES key that we will use to encrypt the data.
243 GenerateKey
genKey(csp
, CSSM_ALGID_3DES_3KEY
, 192);
244 genKey
.database(ssDb
);
246 // Set the acl of the key correctly here
247 genKey
.rcc(credAndAclEntry
);
250 genKey(*this, KeySpec(CSSM_KEYUSE_ENCRYPT
|CSSM_KEYUSE_DECRYPT
,
251 CSSM_KEYATTR_PERMANENT
|CSSM_KEYATTR_SENSITIVE
,
254 // Activate ourself so CSSM_FreeKey will get called when we go out of
259 // Lookup an existing group based on a dataBlob.
260 SSGroupImpl::SSGroupImpl(const SSDb
&ssDb
, const CSSM_DATA
&dataBlob
)
261 : KeyImpl(ssDb
->csp()), mLabel(ssDb
->allocator())
263 if (dataBlob
.Length
< kLabelSize
+ kIVSize
)
264 CssmError::throwMe(CSSMERR_DL_RECORD_NOT_FOUND
); // Not a SS record
266 mLabel
= CssmData(dataBlob
.Data
, kLabelSize
);
267 if (*reinterpret_cast<const uint32
*>(mLabel
.Data
) != h2n (uint32(kGroupMagic
)))
268 CssmError::throwMe(CSSMERR_DL_RECORD_NOT_FOUND
); // Not a SS record
270 // Look up the symmetric key with that label.
271 DbCursor
cursor(new DbDbCursorImpl(ssDb
, 0, Allocator::standard()));
272 cursor
->recordType(CSSM_DL_DB_RECORD_SYMMETRIC_KEY
);
273 cursor
->add(CSSM_DB_EQUAL
, kLabel
, mLabel
);
275 DbUniqueRecord keyId
;
276 CssmDataContainer
keyData(ssDb
->allocator());
277 if (!cursor
->next(NULL
, &keyData
, keyId
))
278 CssmError::throwMe(CSSMERR_DL_RECORD_NOT_FOUND
); // The key can't be found
280 // Set the key part of ourself.
281 static_cast<CSSM_KEY
&>(*this) =
282 *reinterpret_cast<const CSSM_KEY
*>(keyData
.Data
);
284 // Activate ourself so CSSM_FreeKey will get called when we go out of
290 SSGroupImpl::isGroup(const CSSM_DATA
&dataBlob
)
292 return dataBlob
.Length
>= kLabelSize
+ kIVSize
293 && *reinterpret_cast<const uint32
*>(dataBlob
.Data
) == h2n(uint32(kGroupMagic
));
297 SSGroupImpl::label() const
303 SSGroupImpl::decodeDataBlob(const CSSM_DATA
&dataBlob
,
304 const CSSM_ACCESS_CREDENTIALS
*cred
,
305 Allocator
&allocator
, CSSM_DATA
&data
)
307 // First get the IV and the cipherText from the blob.
308 CssmData
iv(&dataBlob
.Data
[kLabelSize
], kIVSize
);
309 CssmData
cipherText(&dataBlob
.Data
[kLabelSize
+ kIVSize
],
310 dataBlob
.Length
- (kLabelSize
+ kIVSize
));
312 CssmDataContainer
plainText1(allocator
);
313 CssmDataContainer
plainText2(allocator
);
315 // @@@ Don't use staged decrypt once the AppleCSPDL can do combo
317 // Setup decryption context
318 Decrypt
decrypt(csp(), algorithm());
319 decrypt
.mode(CSSM_ALGMODE_CBCPadIV8
);
320 decrypt
.padding(CSSM_PADDING_PKCS1
);
321 decrypt
.initVector(iv
);
322 decrypt
.key(Key(this));
323 decrypt
.cred(AccessCredentials::overlay(cred
));
324 decrypt
.decrypt(&cipherText
, 1, &plainText1
, 1);
325 decrypt
.final(plainText2
);
327 // Use DL allocator for allocating memory for data.
328 uint32 length
= plainText1
.Length
+ plainText2
.Length
;
329 data
.Data
= allocator
.alloc
<uint8
>(length
);
330 data
.Length
= length
;
331 memcpy(data
.Data
, plainText1
.Data
, plainText1
.Length
);
332 memcpy(&data
.Data
[plainText1
.Length
], plainText2
.Data
, plainText2
.Length
);
336 SSGroupImpl::encodeDataBlob(const CSSM_DATA
*data
,
337 const CSSM_ACCESS_CREDENTIALS
*cred
,
338 CssmDataContainer
&dataBlob
)
340 // Get our csp and set up a random number generation context.
341 CSP
csp(this->csp());
342 Random
random(csp
, CSSM_ALGID_APPLE_YARROW
);
344 // Encrypt data using key and encode it in a dataBlob.
346 // First calculate a random IV.
347 uint8 ivBuf
[kIVSize
];
348 CssmData
iv(ivBuf
, kIVSize
);
349 random
.generate(iv
, kIVSize
);
351 // Setup encryption context
352 Encrypt
encrypt(csp
, algorithm());
353 encrypt
.mode(CSSM_ALGMODE_CBCPadIV8
);
354 encrypt
.padding(CSSM_PADDING_PKCS1
);
355 encrypt
.initVector(iv
);
356 encrypt
.key(Key(this));
357 encrypt
.cred(AccessCredentials::overlay(cred
));
360 const CssmData nothing
;
361 const CssmData
*plainText
= data
? CssmData::overlay(data
) : ¬hing
;
362 // @@@ Don't use staged encrypt once the AppleCSPDL can do combo
364 CssmDataContainer cipherText1
, cipherText2
;
365 encrypt
.encrypt(plainText
, 1, &cipherText1
, 1);
366 encrypt
.final(cipherText2
);
368 // Create a dataBlob containing the label followed by the IV followed
369 // by the cipherText.
370 uint32 length
= (kLabelSize
+ kIVSize
371 + cipherText1
.Length
+ cipherText2
.Length
);
372 dataBlob
.Data
= dataBlob
.mAllocator
.alloc
<uint8
>(length
);
373 dataBlob
.Length
= length
;
374 memcpy(dataBlob
.Data
, mLabel
.Data
, kLabelSize
);
375 memcpy(&dataBlob
.Data
[kLabelSize
], iv
.Data
, kIVSize
);
376 memcpy(&dataBlob
.Data
[kLabelSize
+ kIVSize
],
377 cipherText1
.Data
, cipherText1
.Length
);
378 memcpy(&dataBlob
.Data
[kLabelSize
+ kIVSize
+ cipherText1
.Length
],
379 cipherText2
.Data
, cipherText2
.Length
);
384 // SSDbCursorImpl -- Secure Storage Database Cursor Implementation.
386 SSDbCursorImpl::SSDbCursorImpl(const Db
&db
, const CSSM_QUERY
&query
,
387 Allocator
&allocator
)
388 : DbDbCursorImpl(db
, query
, allocator
)
392 SSDbCursorImpl::SSDbCursorImpl(const Db
&db
, uint32 capacity
,
393 Allocator
&allocator
)
394 : DbDbCursorImpl(db
, capacity
, allocator
)
399 SSDbCursorImpl::next(DbAttributes
*attributes
, ::CssmDataContainer
*data
,
400 DbUniqueRecord
&uniqueId
)
402 return next(attributes
, data
, uniqueId
, NULL
);
406 SSDbCursorImpl::next(DbAttributes
*attributes
, ::CssmDataContainer
*data
,
407 DbUniqueRecord
&uniqueId
,
408 const CSSM_ACCESS_CREDENTIALS
*cred
)
411 return DbDbCursorImpl::next(attributes
, data
, uniqueId
);
413 DbAttributes noAttrs
, *attrs
;
414 attrs
= attributes
? attributes
: &noAttrs
;
416 // Get the datablob for this record
417 CssmDataContainer
dataBlob(allocator());
420 if (!DbDbCursorImpl::next(attrs
, &dataBlob
, uniqueId
))
423 // Keep going until we find a non key type record.
424 CSSM_DB_RECORDTYPE rt
= attrs
->recordType();
425 if (rt
!= CSSM_DL_DB_RECORD_SYMMETRIC_KEY
426 && rt
!= CSSM_DL_DB_RECORD_PRIVATE_KEY
427 && rt
!= CSSM_DL_DB_RECORD_PUBLIC_KEY
)
429 // @@@ Check the label and if it doesn't start with the magic for a SSKey return the key.
434 // Free the key we just retrieved
435 database()->csp()->freeKey(*reinterpret_cast<CssmKey
*>(dataBlob
.Data
));
439 if (!SSGroupImpl::isGroup(dataBlob
))
441 data
->Data
= dataBlob
.Data
;
442 data
->Length
= dataBlob
.Length
;
443 dataBlob
.Data
= NULL
;
448 // Get the group for dataBlob
449 SSGroup
group(database(), dataBlob
);
451 // Decode the dataBlob, pass in the DL allocator.
452 group
->decodeDataBlob(dataBlob
, cred
, database()->allocator(), *data
);
457 SSDbCursorImpl::nextKey(DbAttributes
*attributes
, Key
&key
,
458 DbUniqueRecord
&uniqueId
)
460 DbAttributes noAttrs
, *attrs
;
461 attrs
= attributes
? attributes
: &noAttrs
;
462 CssmDataContainer
keyData(database()->allocator());
465 if (!DbDbCursorImpl::next(attrs
, &keyData
, uniqueId
))
467 // Keep going until we find a key type record.
468 CSSM_DB_RECORDTYPE rt
= attrs
->recordType();
469 if (rt
== CSSM_DL_DB_RECORD_SYMMETRIC_KEY
470 || rt
== CSSM_DL_DB_RECORD_PRIVATE_KEY
471 || rt
== CSSM_DL_DB_RECORD_PUBLIC_KEY
)
475 key
= Key(database()->csp(), *reinterpret_cast<CSSM_KEY
*>(keyData
.Data
));
480 SSDbCursorImpl::activate()
482 return DbDbCursorImpl::activate();
486 SSDbCursorImpl::deactivate()
488 return DbDbCursorImpl::deactivate();
493 // SSDbUniqueRecordImpl -- Secure Storage UniqueRecord Implementation.
495 SSDbUniqueRecordImpl::SSDbUniqueRecordImpl(const Db
&db
)
496 : DbUniqueRecordImpl(db
)
500 SSDbUniqueRecordImpl::~SSDbUniqueRecordImpl()
505 SSDbUniqueRecordImpl::deleteRecord()
511 SSDbUniqueRecordImpl::deleteRecord(const CSSM_ACCESS_CREDENTIALS
*cred
)
513 // Get the datablob for this record
514 // @@@ Fixme so we don't need to call DbUniqueRecordImpl::get
515 CssmDataContainer
dataBlob(allocator());
516 DbAttributes attributes
;
518 DbUniqueRecordImpl::get(&attributes
, &dataBlob
);
519 CSSM_KEY_PTR keyPtr
= (CSSM_KEY_PTR
) dataBlob
.data();
521 // delete data part first:
522 // (1) don't leave data without keys around
523 // (2) delete orphaned data anyway
524 DbUniqueRecordImpl::deleteRecord();
526 // @@@ Use transactions?
527 if (SSGroupImpl::isGroup(dataBlob
))
529 // Get the group for dataBlob
530 SSGroup
group(database(), dataBlob
);
531 // Delete the group (key)
532 group
->deleteKey(cred
);
533 } catch (const CssmError
&err
) {
535 case CSSMERR_DL_RECORD_NOT_FOUND
:
536 // Zombie item (no group key). Finally at peace! No error
540 if (attributes
.recordType() == CSSM_DL_DB_RECORD_PUBLIC_KEY
||
541 attributes
.recordType() == CSSM_DL_DB_RECORD_PRIVATE_KEY
||
542 attributes
.recordType() == CSSM_DL_DB_RECORD_SYMMETRIC_KEY
)
544 allocator().free(keyPtr
->KeyData
.Data
);
551 if (attributes
.recordType() == CSSM_DL_DB_RECORD_PUBLIC_KEY
||
552 attributes
.recordType() == CSSM_DL_DB_RECORD_PRIVATE_KEY
||
553 attributes
.recordType() == CSSM_DL_DB_RECORD_SYMMETRIC_KEY
)
555 allocator().free(keyPtr
->KeyData
.Data
);
560 SSDbUniqueRecordImpl::modify(CSSM_DB_RECORDTYPE recordType
,
561 const CSSM_DB_RECORD_ATTRIBUTE_DATA
*attributes
,
562 const CSSM_DATA
*data
,
563 CSSM_DB_MODIFY_MODE modifyMode
)
565 modify(recordType
, attributes
, data
, modifyMode
, NULL
);
569 SSDbUniqueRecordImpl::modify(CSSM_DB_RECORDTYPE recordType
,
570 const CSSM_DB_RECORD_ATTRIBUTE_DATA
*attributes
,
571 const CSSM_DATA
*data
,
572 CSSM_DB_MODIFY_MODE modifyMode
,
573 const CSSM_ACCESS_CREDENTIALS
*cred
)
577 DbUniqueRecordImpl::modify(recordType
, attributes
, NULL
, modifyMode
);
581 // Get the datablob for this record
582 // @@@ Fixme so we don't need to call DbUniqueRecordImpl::get
583 CssmDataContainer
oldDataBlob(allocator());
584 DbUniqueRecordImpl::get(NULL
, &oldDataBlob
);
586 if (!SSGroupImpl::isGroup(oldDataBlob
))
588 DbUniqueRecordImpl::modify(recordType
, attributes
, data
, modifyMode
);
592 // Get the group for oldDataBlob
593 SSGroup
group(database(), oldDataBlob
);
595 // Create a new dataBlob.
596 CssmDataContainer
dataBlob(allocator());
597 group
->encodeDataBlob(data
, cred
, dataBlob
);
598 DbUniqueRecordImpl::modify(recordType
, attributes
, &dataBlob
, modifyMode
);
602 SSDbUniqueRecordImpl::get(DbAttributes
*attributes
, ::CssmDataContainer
*data
)
604 get(attributes
, data
, NULL
);
608 SSDbUniqueRecordImpl::get(DbAttributes
*attributes
, ::CssmDataContainer
*data
,
609 const CSSM_ACCESS_CREDENTIALS
*cred
)
613 DbUniqueRecordImpl::get(attributes
, NULL
);
617 // Get the datablob for this record
618 // @@@ Fixme so we don't need to call DbUniqueRecordImpl::get
619 CssmDataContainer
dataBlob(allocator());
620 DbUniqueRecordImpl::get(attributes
, &dataBlob
);
622 if (!SSGroupImpl::isGroup(dataBlob
))
624 data
->Data
= dataBlob
.Data
;
625 data
->Length
= dataBlob
.Length
;
626 dataBlob
.Data
= NULL
;
631 // Get the group for dataBlob
632 SSGroup
group(database(), dataBlob
);
634 // Decode the dataBlob, pass in the DL allocator.
635 group
->decodeDataBlob(dataBlob
, cred
, allocator(), *data
);
639 SSDbUniqueRecordImpl::group()
641 // Get the datablob for this record
642 // @@@ Fixme so we don't need to call DbUniqueRecordImpl::get
643 CssmDataContainer
dataBlob(allocator());
644 DbUniqueRecordImpl::get(NULL
, &dataBlob
);
645 return SSGroup(database(), dataBlob
);