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"
21 //#include "aclsupport.h"
22 #include <Security/Access.h>
23 #include <Security/osxsigning.h>
26 using namespace CssmClient
;
27 using namespace KeychainCore
;
30 // Manage CSPDL attachments
32 CSPDLImpl::CSPDLImpl(const Guid
&guid
)
33 : CSPImpl(Cssm::standard()->autoModule(guid
)),
34 DLImpl(CSPImpl::module())
38 CSPDLImpl::CSPDLImpl(const Module
&module)
44 CSPDLImpl::~CSPDLImpl()
48 CssmAllocator
&CSPDLImpl::allocator() const
50 DLImpl::allocator(); return CSPImpl::allocator();
53 void CSPDLImpl::allocator(CssmAllocator
&alloc
)
55 CSPImpl::allocator(alloc
); DLImpl::allocator(alloc
);
58 bool CSPDLImpl::operator <(const CSPDLImpl
&other
) const
60 return (static_cast<const CSPImpl
&>(*this) < static_cast<const CSPImpl
&>(other
) ||
61 (!(static_cast<const CSPImpl
&>(other
) < static_cast<const CSPImpl
&>(*this))
62 && static_cast<const DLImpl
&>(*this) < static_cast<const DLImpl
&>(other
)));
65 bool CSPDLImpl::operator ==(const CSPDLImpl
&other
) const
67 return (static_cast<const CSPImpl
&>(*this) == static_cast<const CSPImpl
&>(other
)
68 && static_cast<const DLImpl
&>(*this) == static_cast<const DLImpl
&>(other
));
71 CSSM_SERVICE_MASK
CSPDLImpl::subserviceMask() const
73 return CSPImpl::subserviceType() | DLImpl::subserviceType();
76 void CSPDLImpl::subserviceId(uint32 id
)
78 CSPImpl::subserviceId(id
); DLImpl::subserviceId(id
);
85 SSCSPDLImpl::SSCSPDLImpl(const Guid
&guid
) : CSPDLImpl::CSPDLImpl(guid
)
89 SSCSPDLImpl::SSCSPDLImpl(const Module
&module) : CSPDLImpl::CSPDLImpl(module)
93 SSCSPDLImpl::~SSCSPDLImpl()
98 SSCSPDLImpl::newDb(const char *inDbName
, const CSSM_NET_ADDRESS
*inDbLocation
)
100 return new SSDbImpl(SSCSPDL(this), inDbName
, inDbLocation
);
105 // SSDbImpl -- Secure Storage Database Implementation
107 SSDbImpl::SSDbImpl(const SSCSPDL
&cspdl
, const char *inDbName
,
108 const CSSM_NET_ADDRESS
*inDbLocation
)
109 : DbImpl(cspdl
, inDbName
, inDbLocation
)
113 SSDbImpl::~SSDbImpl()
130 SSDbImpl::insert(CSSM_DB_RECORDTYPE recordType
,
131 const CSSM_DB_RECORD_ATTRIBUTE_DATA
*attributes
,
132 const CSSM_DATA
*data
,
133 const CSSM_RESOURCE_CONTROL_CONTEXT
*rc
)
135 SSGroup
group(SSDb(this), rc
);
136 const CSSM_ACCESS_CREDENTIALS
*cred
= rc
? rc
->AccessCred
: NULL
;
139 return insert(recordType
, attributes
, data
, group
, cred
);
143 // @@@ Look at rc for credentials
144 group
->deleteKey(cred
);
150 SSDbImpl::insert(CSSM_DB_RECORDTYPE recordType
,
151 const CSSM_DB_RECORD_ATTRIBUTE_DATA
*attributes
,
152 const CSSM_DATA
*data
, const SSGroup
&group
,
153 const CSSM_ACCESS_CREDENTIALS
*cred
)
155 // Create an encoded dataBlob for this item.
156 CssmDataContainer
dataBlob(allocator());
157 group
->encodeDataBlob(data
, cred
, dataBlob
);
159 // Insert the record with the new juicy dataBlob.
160 return SSDbUniqueRecord(safe_cast
<SSDbUniqueRecordImpl
*>
161 (&(*DbImpl::insert(recordType
, attributes
, &dataBlob
))));
167 SSDbImpl::newDbCursor(const CSSM_QUERY
&query
, CssmAllocator
&allocator
)
169 return new SSDbCursorImpl(Db(this), query
, allocator
);
173 SSDbImpl::newDbCursor(uint32 capacity
, CssmAllocator
&allocator
)
175 return new SSDbCursorImpl(Db(this), capacity
, allocator
);
179 // SSDbUniqueRecordMaker
181 SSDbImpl::newDbUniqueRecord()
183 return new SSDbUniqueRecordImpl(Db(this));
188 // SSGroup -- Group key with acl, used to protect a group of items.
190 // @@@ Get this from a shared spot.
191 CSSM_DB_NAME_ATTR(SSGroupImpl::kLabel
, 6, "Label", 0, NULL
, BLOB
);
193 // Create a new group.
194 SSGroupImpl::SSGroupImpl(const SSDb
&ssDb
,
195 const CSSM_RESOURCE_CONTROL_CONTEXT
*credAndAclEntry
)
196 : KeyImpl(ssDb
->csp()), mLabel(ssDb
->allocator())
198 mLabel
.Length
= kLabelSize
;
199 mLabel
.Data
= reinterpret_cast<uint8
*>
200 (mLabel
.mAllocator
.malloc(mLabel
.Length
));
202 // Get our csp and set up a random number generation context.
204 Random
random(csp
, CSSM_ALGID_APPLE_YARROW
);
206 // Generate a kLabelSize byte random number that will be the label of
207 // the key which we store in the dataBlob.
208 random
.generate(mLabel
, mLabel
.Length
);
210 // Overwrite the first 4 bytes with the magic cookie for a group.
211 reinterpret_cast<uint32
*>(mLabel
.Data
)[0] = kGroupMagic
;
213 // @@@ Ensure that the label is unique (Chance of collision is 2^80 --
214 // birthday paradox).
216 // Generate a permanent 3DES key that we will use to encrypt the data.
217 GenerateKey
genKey(csp
, CSSM_ALGID_3DES_3KEY
, 192);
218 genKey
.database(ssDb
);
220 // Set the acl of the key correctly here
221 genKey
.initialAcl(ResourceControlContext::overlay(credAndAclEntry
));
224 genKey(*this, KeySpec(CSSM_KEYUSE_ENCRYPT
|CSSM_KEYUSE_DECRYPT
,
225 CSSM_KEYATTR_PERMANENT
|CSSM_KEYATTR_SENSITIVE
,
228 // Activate ourself so CSSM_FreeKey will get called when we go out of
233 // Lookup an existing group based on a dataBlob.
234 SSGroupImpl::SSGroupImpl(const SSDb
&ssDb
, const CSSM_DATA
&dataBlob
)
235 : KeyImpl(ssDb
->csp()), mLabel(ssDb
->allocator())
237 if (dataBlob
.Length
< kLabelSize
+ kIVSize
)
238 CssmError::throwMe(CSSMERR_DL_RECORD_NOT_FOUND
); // Not a SS record
240 mLabel
= CssmData(dataBlob
.Data
, kLabelSize
);
241 if (*reinterpret_cast<const uint32
*>(mLabel
.Data
) != kGroupMagic
)
242 CssmError::throwMe(CSSMERR_DL_RECORD_NOT_FOUND
); // Not a SS record
244 // Look up the symmetric key with that label.
245 DbCursor
cursor(new DbDbCursorImpl(ssDb
, 0, CssmAllocator::standard()));
246 cursor
->recordType(CSSM_DL_DB_RECORD_SYMMETRIC_KEY
);
247 cursor
->add(CSSM_DB_EQUAL
, kLabel
, mLabel
);
249 DbUniqueRecord keyId
;
250 CssmDataContainer
keyData(ssDb
->allocator());
251 if (!cursor
->next(NULL
, &keyData
, keyId
))
252 CssmError::throwMe(CSSMERR_DL_RECORD_NOT_FOUND
); // The key can't be found
254 // Set the key part of ourself.
255 static_cast<CSSM_KEY
&>(*this) =
256 *reinterpret_cast<const CSSM_KEY
*>(keyData
.Data
);
258 // Activate ourself so CSSM_FreeKey will get called when we go out of
264 SSGroupImpl::isGroup(const CSSM_DATA
&dataBlob
)
266 return dataBlob
.Length
>= kLabelSize
+ kIVSize
267 && *reinterpret_cast<const uint32
*>(dataBlob
.Data
) == kGroupMagic
;
271 SSGroupImpl::label() const
277 SSGroupImpl::decodeDataBlob(const CSSM_DATA
&dataBlob
,
278 const CSSM_ACCESS_CREDENTIALS
*cred
,
279 CssmAllocator
&allocator
, CSSM_DATA
&data
)
281 // First get the IV and the cipherText from the blob.
282 CssmData
iv(&dataBlob
.Data
[kLabelSize
], kIVSize
);
283 CssmData
cipherText(&dataBlob
.Data
[kLabelSize
+ kIVSize
],
284 dataBlob
.Length
- (kLabelSize
+ kIVSize
));
286 CssmDataContainer
plainText1(allocator
);
287 CssmDataContainer
plainText2(allocator
);
291 // @@@ Don't use staged decrypt once the AppleCSPDL can do combo
293 // Setup decryption context
294 Decrypt
decrypt(csp(), algorithm());
295 decrypt
.mode(CSSM_ALGMODE_CBCPadIV8
);
296 decrypt
.padding(CSSM_PADDING_PKCS1
);
297 decrypt
.initVector(iv
);
298 decrypt
.key(Key(this));
299 decrypt
.cred(AccessCredentials::overlay(cred
));
300 decrypt
.decrypt(&cipherText
, 1, &plainText1
, 1);
301 decrypt
.final(plainText2
);
303 catch (const CssmError
&e
)
305 if (e
.cssmError() != CSSMERR_CSP_APPLE_ADD_APPLICATION_ACL_SUBJECT
)
308 // The user checked to don't ask again checkbox in the rogue app alert. Let's edit the ACL for this key and add the calling application to it.
310 Key
key(this); // the underlying key
311 RefPointer
<Access
> access
= new Access(*key
); // extract access rights
312 RefPointer
<TrustedApplication
> thisApp
= new TrustedApplication
;
313 access
->addApplicationToRight(CSSM_ACL_AUTHORIZATION_DECRYPT
, thisApp
.get()); // add this app
314 access
->setAccess(*key
, true); // commit
316 KeychainACL
acl(Key(this));
318 acl
.alwaysAskUser(true);
320 RefPointer
<CodeSigning::OSXCode
> code(CodeSigning::OSXCode::main());
321 const char *path
= code
->canonicalPath().c_str();
322 CssmData
comment(const_cast<char *>(path
), strlen(path
) + 1);
323 acl
.push_back(TrustedApplication(path
, comment
));
329 // Retry the decrypt operation.
330 Decrypt
decrypt(csp(), algorithm());
331 decrypt
.mode(CSSM_ALGMODE_CBCPadIV8
);
332 decrypt
.padding(CSSM_PADDING_PKCS1
);
333 decrypt
.initVector(iv
);
334 decrypt
.key(Key(this));
335 decrypt
.cred(AccessCredentials::overlay(cred
));
336 decrypt
.decrypt(&cipherText
, 1, &plainText1
, 1);
337 decrypt
.final(plainText2
);
340 // Use DL allocator for allocating memory for data.
341 uint32 length
= plainText1
.Length
+ plainText2
.Length
;
342 data
.Data
= allocator
.alloc
<uint8
>(length
);
343 data
.Length
= length
;
344 memcpy(data
.Data
, plainText1
.Data
, plainText1
.Length
);
345 memcpy(&data
.Data
[plainText1
.Length
], plainText2
.Data
, plainText2
.Length
);
349 SSGroupImpl::encodeDataBlob(const CSSM_DATA
*data
,
350 const CSSM_ACCESS_CREDENTIALS
*cred
,
351 CssmDataContainer
&dataBlob
)
353 // Get our csp and set up a random number generation context.
355 Random
random(csp
, CSSM_ALGID_APPLE_YARROW
);
357 // Encrypt data using key and encode it in a dataBlob.
359 // First calculate a random IV.
360 uint8 ivBuf
[kIVSize
];
361 CssmData
iv(ivBuf
, kIVSize
);
362 random
.generate(iv
, kIVSize
);
364 // Setup encryption context
365 Encrypt
encrypt(csp
, algorithm());
366 encrypt
.mode(CSSM_ALGMODE_CBCPadIV8
);
367 encrypt
.padding(CSSM_PADDING_PKCS1
);
368 encrypt
.initVector(iv
);
369 encrypt
.key(Key(this));
370 encrypt
.cred(AccessCredentials::overlay(cred
));
373 const CssmData nothing
;
374 const CssmData
*plainText
= data
? CssmData::overlay(data
) : ¬hing
;
375 // @@@ Don't use staged encrypt once the AppleCSPDL can do combo
377 CssmDataContainer cipherText1
, cipherText2
;
378 encrypt
.encrypt(plainText
, 1, &cipherText1
, 1);
379 encrypt
.final(cipherText2
);
381 // Create a dataBlob containing the label followed by the IV followed
382 // by the cipherText.
383 uint32 length
= (kLabelSize
+ kIVSize
384 + cipherText1
.Length
+ cipherText2
.Length
);
385 dataBlob
.Data
= dataBlob
.mAllocator
.alloc
<uint8
>(length
);
386 dataBlob
.Length
= length
;
387 memcpy(dataBlob
.Data
, mLabel
.Data
, kLabelSize
);
388 memcpy(&dataBlob
.Data
[kLabelSize
], iv
.Data
, kIVSize
);
389 memcpy(&dataBlob
.Data
[kLabelSize
+ kIVSize
],
390 cipherText1
.Data
, cipherText1
.Length
);
391 memcpy(&dataBlob
.Data
[kLabelSize
+ kIVSize
+ cipherText1
.Length
],
392 cipherText2
.Data
, cipherText2
.Length
);
397 // SSDbCursorImpl -- Secure Storage Database Cursor Implementation.
399 SSDbCursorImpl::SSDbCursorImpl(const Db
&db
, const CSSM_QUERY
&query
,
400 CssmAllocator
&allocator
)
401 : DbDbCursorImpl(db
, query
, allocator
)
405 SSDbCursorImpl::SSDbCursorImpl(const Db
&db
, uint32 capacity
,
406 CssmAllocator
&allocator
)
407 : DbDbCursorImpl(db
, capacity
, allocator
)
412 SSDbCursorImpl::next(DbAttributes
*attributes
, ::CssmDataContainer
*data
,
413 DbUniqueRecord
&uniqueId
)
415 return next(attributes
, data
, uniqueId
, NULL
);
419 SSDbCursorImpl::next(DbAttributes
*attributes
, ::CssmDataContainer
*data
,
420 DbUniqueRecord
&uniqueId
,
421 const CSSM_ACCESS_CREDENTIALS
*cred
)
424 return DbDbCursorImpl::next(attributes
, data
, uniqueId
);
426 DbAttributes noAttrs
, *attrs
;
427 attrs
= attributes
? attributes
: &noAttrs
;
429 // Get the datablob for this record
430 CssmDataContainer
dataBlob(allocator());
433 if (!DbDbCursorImpl::next(attrs
, &dataBlob
, uniqueId
))
436 // Keep going until we find a non key type record.
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 // @@@ Check the label and if it doesn't start with the magic for a SSKey return the key.
447 // Free the key we just retrieved
448 database()->csp()->freeKey(*reinterpret_cast<CssmKey
*>(dataBlob
.Data
));
452 if (!SSGroupImpl::isGroup(dataBlob
))
454 data
->Data
= dataBlob
.Data
;
455 data
->Length
= dataBlob
.Length
;
456 dataBlob
.Data
= NULL
;
461 // Get the group for dataBlob
462 SSGroup
group(database(), dataBlob
);
464 // Decode the dataBlob, pass in the DL allocator.
465 group
->decodeDataBlob(dataBlob
, cred
, database()->allocator(), *data
);
470 SSDbCursorImpl::nextKey(DbAttributes
*attributes
, Key
&key
,
471 DbUniqueRecord
&uniqueId
)
473 DbAttributes noAttrs
, *attrs
;
474 attrs
= attributes
? attributes
: &noAttrs
;
475 CssmDataContainer
keyData(database()->allocator());
478 if (!DbDbCursorImpl::next(attrs
, &keyData
, uniqueId
))
480 // Keep going until we find a key type record.
481 CSSM_DB_RECORDTYPE rt
= attrs
->recordType();
482 if (rt
== CSSM_DL_DB_RECORD_SYMMETRIC_KEY
483 || rt
== CSSM_DL_DB_RECORD_PRIVATE_KEY
484 || rt
== CSSM_DL_DB_RECORD_PUBLIC_KEY
)
488 key
= Key(database()->csp(), *reinterpret_cast<CSSM_KEY
*>(keyData
.Data
));
493 SSDbCursorImpl::activate()
495 return DbDbCursorImpl::activate();
499 SSDbCursorImpl::deactivate()
501 return DbDbCursorImpl::deactivate();
506 // SSDbUniqueRecordImpl -- Secure Storage UniqueRecord Implementation.
508 SSDbUniqueRecordImpl::SSDbUniqueRecordImpl(const Db
&db
)
509 : DbUniqueRecordImpl(db
)
513 SSDbUniqueRecordImpl::~SSDbUniqueRecordImpl()
518 SSDbUniqueRecordImpl::deleteRecord()
524 SSDbUniqueRecordImpl::deleteRecord(const CSSM_ACCESS_CREDENTIALS
*cred
)
526 // Get the datablob for this record
527 // @@@ Fixme so we don't need to call DbUniqueRecordImpl::get
528 CssmDataContainer
dataBlob(allocator());
529 DbUniqueRecordImpl::get(NULL
, &dataBlob
);
531 // @@@ Use transactions.
532 if (SSGroupImpl::isGroup(dataBlob
))
534 // Get the group for dataBlob
535 SSGroup
group(database(), dataBlob
);
537 // @@@ What if the group is shared?
538 group
->deleteKey(cred
);
541 // Delete the record.
542 DbUniqueRecordImpl::deleteRecord();
546 SSDbUniqueRecordImpl::modify(CSSM_DB_RECORDTYPE recordType
,
547 const CSSM_DB_RECORD_ATTRIBUTE_DATA
*attributes
,
548 const CSSM_DATA
*data
,
549 CSSM_DB_MODIFY_MODE modifyMode
)
551 modify(recordType
, attributes
, data
, modifyMode
, NULL
);
555 SSDbUniqueRecordImpl::modify(CSSM_DB_RECORDTYPE recordType
,
556 const CSSM_DB_RECORD_ATTRIBUTE_DATA
*attributes
,
557 const CSSM_DATA
*data
,
558 CSSM_DB_MODIFY_MODE modifyMode
,
559 const CSSM_ACCESS_CREDENTIALS
*cred
)
563 DbUniqueRecordImpl::modify(recordType
, attributes
, NULL
, modifyMode
);
567 // Get the datablob for this record
568 // @@@ Fixme so we don't need to call DbUniqueRecordImpl::get
569 CssmDataContainer
oldDataBlob(allocator());
570 DbUniqueRecordImpl::get(NULL
, &oldDataBlob
);
572 if (!SSGroupImpl::isGroup(oldDataBlob
))
574 DbUniqueRecordImpl::modify(recordType
, attributes
, data
, modifyMode
);
578 // Get the group for oldDataBlob
579 SSGroup
group(database(), oldDataBlob
);
581 // Create a new dataBlob.
582 CssmDataContainer
dataBlob(allocator());
583 group
->encodeDataBlob(data
, cred
, dataBlob
);
584 DbUniqueRecordImpl::modify(recordType
, attributes
, &dataBlob
, modifyMode
);
588 SSDbUniqueRecordImpl::get(DbAttributes
*attributes
, ::CssmDataContainer
*data
)
590 get(attributes
, data
, NULL
);
594 SSDbUniqueRecordImpl::get(DbAttributes
*attributes
, ::CssmDataContainer
*data
,
595 const CSSM_ACCESS_CREDENTIALS
*cred
)
599 DbUniqueRecordImpl::get(attributes
, NULL
);
603 // Get the datablob for this record
604 // @@@ Fixme so we don't need to call DbUniqueRecordImpl::get
605 CssmDataContainer
dataBlob(allocator());
606 DbUniqueRecordImpl::get(attributes
, &dataBlob
);
608 if (!SSGroupImpl::isGroup(dataBlob
))
610 data
->Data
= dataBlob
.Data
;
611 data
->Length
= dataBlob
.Length
;
612 dataBlob
.Data
= NULL
;
617 // Get the group for dataBlob
618 SSGroup
group(database(), dataBlob
);
620 // Decode the dataBlob, pass in the DL allocator.
621 group
->decodeDataBlob(dataBlob
, cred
, allocator(), *data
);
625 SSDbUniqueRecordImpl::group()
627 // Get the datablob for this record
628 // @@@ Fixme so we don't need to call DbUniqueRecordImpl::get
629 CssmDataContainer
dataBlob(allocator());
630 DbUniqueRecordImpl::get(NULL
, &dataBlob
);
631 return SSGroup(database(), dataBlob
);