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/osxsigning.h>
25 using namespace CssmClient
;
28 // Manage CSPDL attachments
30 CSPDLImpl::CSPDLImpl(const Guid
&guid
)
31 : CSPImpl(Cssm::standard()->autoModule(guid
)),
32 DLImpl(CSPImpl::module())
36 CSPDLImpl::CSPDLImpl(const Module
&module)
42 CSPDLImpl::~CSPDLImpl()
46 CssmAllocator
&CSPDLImpl::allocator() const
48 DLImpl::allocator(); return CSPImpl::allocator();
51 void CSPDLImpl::allocator(CssmAllocator
&alloc
)
53 CSPImpl::allocator(alloc
); DLImpl::allocator(alloc
);
56 bool CSPDLImpl::operator <(const CSPDLImpl
&other
) const
58 return (static_cast<const CSPImpl
&>(*this) < static_cast<const CSPImpl
&>(other
) ||
59 (!(static_cast<const CSPImpl
&>(other
) < static_cast<const CSPImpl
&>(*this))
60 && static_cast<const DLImpl
&>(*this) < static_cast<const DLImpl
&>(other
)));
63 bool CSPDLImpl::operator ==(const CSPDLImpl
&other
) const
65 return (static_cast<const CSPImpl
&>(*this) == static_cast<const CSPImpl
&>(other
)
66 && static_cast<const DLImpl
&>(*this) == static_cast<const DLImpl
&>(other
));
69 CSSM_SERVICE_MASK
CSPDLImpl::subserviceMask() const
71 return CSPImpl::subserviceType() | DLImpl::subserviceType();
74 void CSPDLImpl::subserviceId(uint32 id
)
76 CSPImpl::subserviceId(id
); DLImpl::subserviceId(id
);
83 SSCSPDLImpl::SSCSPDLImpl(const Guid
&guid
) : CSPDLImpl::CSPDLImpl(guid
)
87 SSCSPDLImpl::SSCSPDLImpl(const Module
&module) : CSPDLImpl::CSPDLImpl(module)
91 SSCSPDLImpl::~SSCSPDLImpl()
96 SSCSPDLImpl::newDb(const char *inDbName
, const CSSM_NET_ADDRESS
*inDbLocation
)
98 return new SSDbImpl(SSCSPDL(this), inDbName
, inDbLocation
);
103 // SSDbImpl -- Secure Storage Database Implementation
105 SSDbImpl::SSDbImpl(const SSCSPDL
&cspdl
, const char *inDbName
,
106 const CSSM_NET_ADDRESS
*inDbLocation
)
107 : DbImpl(cspdl
, inDbName
, inDbLocation
)
111 SSDbImpl::~SSDbImpl()
128 SSDbImpl::insert(CSSM_DB_RECORDTYPE recordType
,
129 const CSSM_DB_RECORD_ATTRIBUTE_DATA
*attributes
,
130 const CSSM_DATA
*data
,
131 const CSSM_RESOURCE_CONTROL_CONTEXT
*rc
)
133 SSGroup
group(SSDb(this), rc
);
134 const CSSM_ACCESS_CREDENTIALS
*cred
= rc
? rc
->AccessCred
: NULL
;
137 return insert(recordType
, attributes
, data
, group
, cred
);
141 // @@@ Look at rc for credentials
142 group
->deleteKey(cred
);
148 SSDbImpl::insert(CSSM_DB_RECORDTYPE recordType
,
149 const CSSM_DB_RECORD_ATTRIBUTE_DATA
*attributes
,
150 const CSSM_DATA
*data
, const SSGroup
&group
,
151 const CSSM_ACCESS_CREDENTIALS
*cred
)
153 // Create an encoded dataBlob for this item.
154 CssmDataContainer dataBlob
;
155 group
->encodeDataBlob(data
, cred
, dataBlob
);
157 // Insert the record with the new juicy dataBlob.
158 return SSDbUniqueRecord(safe_cast
<SSDbUniqueRecordImpl
*>
159 (&(*DbImpl::insert(recordType
, attributes
, &dataBlob
))));
165 SSDbImpl::newDbCursor(const CSSM_QUERY
&query
, CssmAllocator
&allocator
)
167 return new SSDbCursorImpl(Db(this), query
, allocator
);
171 SSDbImpl::newDbCursor(uint32 capacity
, CssmAllocator
&allocator
)
173 return new SSDbCursorImpl(Db(this), capacity
, allocator
);
177 // SSDbUniqueRecordMaker
179 SSDbImpl::newDbUniqueRecord()
181 return new SSDbUniqueRecordImpl(Db(this));
186 // SSGroup -- Group key with acl, used to protect a group of items.
188 // @@@ Get this from a shared spot.
189 CSSM_DB_NAME_ATTR(SSGroupImpl::kLabel
, 6, "Label", 0, NULL
, BLOB
);
191 // Create a new group.
192 SSGroupImpl::SSGroupImpl(const SSDb
&ssDb
,
193 const CSSM_RESOURCE_CONTROL_CONTEXT
*credAndAclEntry
)
194 : KeyImpl(ssDb
->csp()), mLabel(ssDb
->allocator())
196 mLabel
.Length
= kLabelSize
;
197 mLabel
.Data
= reinterpret_cast<uint8
*>
198 (mLabel
.mAllocator
.malloc(mLabel
.Length
));
200 // Get our csp and set up a random number generation context.
202 Random
random(csp
, CSSM_ALGID_APPLE_YARROW
);
204 // Generate a kLabelSize byte random number that will be the label of
205 // the key which we store in the dataBlob.
206 random
.generate(mLabel
, mLabel
.Length
);
208 // Overwrite the first 4 bytes with the magic cookie for a group.
209 reinterpret_cast<uint32
*>(mLabel
.Data
)[0] = kGroupMagic
;
211 // @@@ Ensure that the label is unique (Chance of collision is 2^80 --
212 // birthday paradox).
214 // Generate a permanent 3DES key that we will use to encrypt the data.
215 GenerateKey
genKey(csp
, CSSM_ALGID_3DES_3KEY
, 192);
216 genKey
.database(ssDb
);
218 // Set the acl of the key correctly here
219 genKey
.initialAcl(ResourceControlContext::overlay(credAndAclEntry
));
222 genKey(*this, KeySpec(CSSM_KEYUSE_ENCRYPT
|CSSM_KEYUSE_DECRYPT
,
223 CSSM_KEYATTR_PERMANENT
|CSSM_KEYATTR_SENSITIVE
,
226 // Activate ourself so CSSM_FreeKey will get called when we go out of
231 // Lookup an existing group based on a dataBlob.
232 SSGroupImpl::SSGroupImpl(const SSDb
&ssDb
, const CSSM_DATA
&dataBlob
)
233 : KeyImpl(ssDb
->csp()), mLabel(ssDb
->allocator())
235 if (dataBlob
.Length
< kLabelSize
+ kIVSize
)
236 CssmError::throwMe(CSSMERR_DL_RECORD_NOT_FOUND
); // @@@ Not a SS record
238 mLabel
= CssmData(dataBlob
.Data
, kLabelSize
);
239 if (*reinterpret_cast<const uint32
*>(mLabel
.Data
) != kGroupMagic
)
240 CssmError::throwMe(CSSMERR_DL_RECORD_NOT_FOUND
); // @@@ Not a SS record
242 // Look up the symmetric key with that label.
243 DbCursor
cursor(new DbDbCursorImpl(ssDb
, 0, CssmAllocator::standard()));
244 cursor
->recordType(CSSM_DL_DB_RECORD_SYMMETRIC_KEY
);
245 cursor
->add(CSSM_DB_EQUAL
, kLabel
, mLabel
);
247 DbUniqueRecord keyId
;
248 CssmDataContainer
keyData(ssDb
->allocator());
249 if (!cursor
->next(NULL
, &keyData
, keyId
))
250 CssmError::throwMe(CSSMERR_DL_RECORD_NOT_FOUND
); // @@@ The key is gone
252 // Set the key part of ourself.
253 static_cast<CSSM_KEY
&>(*this) =
254 *reinterpret_cast<const CSSM_KEY
*>(keyData
.Data
);
256 // Activate ourself so CSSM_FreeKey will get called when we go out of
262 SSGroupImpl::label() const
268 SSGroupImpl::decodeDataBlob(const CSSM_DATA
&dataBlob
,
269 const CSSM_ACCESS_CREDENTIALS
*cred
,
270 CssmAllocator
&allocator
, CSSM_DATA
&data
)
272 // First get the IV and the cipherText from the blob.
273 CssmData
iv(&dataBlob
.Data
[kLabelSize
], kIVSize
);
274 CssmData
cipherText(&dataBlob
.Data
[kLabelSize
+ kIVSize
],
275 dataBlob
.Length
- (kLabelSize
+ kIVSize
));
277 CssmDataContainer
plainText1(allocator
);
278 CssmDataContainer
plainText2(allocator
);
282 // @@@ Don't use staged decrypt once the AppleCSPDL can do combo
284 // Setup decryption context
285 Decrypt
decrypt(csp(), algorithm());
286 decrypt
.mode(CSSM_ALGMODE_CBCPadIV8
);
287 decrypt
.padding(CSSM_PADDING_PKCS1
);
288 decrypt
.initVector(iv
);
289 decrypt
.key(Key(this));
290 decrypt
.cred(AccessCredentials::overlay(cred
));
291 decrypt
.decrypt(&cipherText
, 1, &plainText1
, 1);
292 decrypt
.final(plainText2
);
294 catch (const CssmError
&e
)
296 if (e
.cssmError() != CSSMERR_CSP_APPLE_ADD_APPLICATION_ACL_SUBJECT
)
299 // 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.
300 KeychainACL
acl(Key(this));
302 acl
.alwaysAskUser(true);
304 auto_ptr
<CodeSigning::OSXCode
> code(CodeSigning::OSXCode::main());
305 const char *path
= code
->canonicalPath().c_str();
306 CssmData
comment(const_cast<char *>(path
), strlen(path
) + 1);
307 acl
.push_back(TrustedApplication(path
, comment
));
312 // Retry the decrypt operation.
313 Decrypt
decrypt(csp(), algorithm());
314 decrypt
.mode(CSSM_ALGMODE_CBCPadIV8
);
315 decrypt
.padding(CSSM_PADDING_PKCS1
);
316 decrypt
.initVector(iv
);
317 decrypt
.key(Key(this));
318 decrypt
.cred(AccessCredentials::overlay(cred
));
319 decrypt
.decrypt(&cipherText
, 1, &plainText1
, 1);
320 decrypt
.final(plainText2
);
323 // Use DL allocator for allocating memory for data.
324 uint32 length
= plainText1
.Length
+ plainText2
.Length
;
325 data
.Data
= allocator
.alloc
<uint8
>(length
);
326 data
.Length
= length
;
327 memcpy(data
.Data
, plainText1
.Data
, plainText1
.Length
);
328 memcpy(&data
.Data
[plainText1
.Length
], plainText2
.Data
, plainText2
.Length
);
332 SSGroupImpl::encodeDataBlob(const CSSM_DATA
*data
,
333 const CSSM_ACCESS_CREDENTIALS
*cred
,
334 CssmDataContainer
&dataBlob
)
336 // Get our csp and set up a random number generation context.
338 Random
random(csp
, CSSM_ALGID_APPLE_YARROW
);
340 // Encrypt data using key and encode it in a dataBlob.
342 // First calculate a random IV.
343 uint8 ivBuf
[kIVSize
];
344 CssmData
iv(ivBuf
, kIVSize
);
345 random
.generate(iv
, kIVSize
);
347 // Setup encryption context
348 Encrypt
encrypt(csp
, algorithm());
349 encrypt
.mode(CSSM_ALGMODE_CBCPadIV8
);
350 encrypt
.padding(CSSM_PADDING_PKCS1
);
351 encrypt
.initVector(iv
);
352 encrypt
.key(Key(this));
353 encrypt
.cred(AccessCredentials::overlay(cred
));
356 const CssmData nothing
;
357 const CssmData
*plainText
= data
? CssmData::overlay(data
) : ¬hing
;
358 // @@@ Don't use staged encrypt once the AppleCSPDL can do combo
360 CssmDataContainer cipherText1
, cipherText2
;
361 encrypt
.encrypt(plainText
, 1, &cipherText1
, 1);
362 encrypt
.final(cipherText2
);
364 // Create a dataBlob containing the label followed by the IV followed
365 // by the cipherText.
366 uint32 length
= (kLabelSize
+ kIVSize
367 + cipherText1
.Length
+ cipherText2
.Length
);
368 dataBlob
.Data
= dataBlob
.mAllocator
.alloc
<uint8
>(length
);
369 dataBlob
.Length
= length
;
370 memcpy(dataBlob
.Data
, mLabel
.Data
, kLabelSize
);
371 memcpy(&dataBlob
.Data
[kLabelSize
], iv
.Data
, kIVSize
);
372 memcpy(&dataBlob
.Data
[kLabelSize
+ kIVSize
],
373 cipherText1
.Data
, cipherText1
.Length
);
374 memcpy(&dataBlob
.Data
[kLabelSize
+ kIVSize
+ cipherText1
.Length
],
375 cipherText2
.Data
, cipherText2
.Length
);
380 // SSDbCursorImpl -- Secure Storage Database Cursor Implementation.
382 SSDbCursorImpl::SSDbCursorImpl(const Db
&db
, const CSSM_QUERY
&query
,
383 CssmAllocator
&allocator
)
384 : DbDbCursorImpl(db
, query
, allocator
)
388 SSDbCursorImpl::SSDbCursorImpl(const Db
&db
, uint32 capacity
,
389 CssmAllocator
&allocator
)
390 : DbDbCursorImpl(db
, capacity
, allocator
)
395 SSDbCursorImpl::next(DbAttributes
*attributes
, ::CssmDataContainer
*data
,
396 DbUniqueRecord
&uniqueId
)
398 return next(attributes
, data
, uniqueId
, NULL
);
402 SSDbCursorImpl::next(DbAttributes
*attributes
, ::CssmDataContainer
*data
,
403 DbUniqueRecord
&uniqueId
,
404 const CSSM_ACCESS_CREDENTIALS
*cred
)
407 return DbDbCursorImpl::next(attributes
, data
, uniqueId
);
409 DbAttributes noAttrs
, *attrs
;
410 attrs
= attributes
? attributes
: &noAttrs
;
412 // Get the datablob for this record
413 CssmDataContainer dataBlob
;
416 if (!DbDbCursorImpl::next(attrs
, &dataBlob
, uniqueId
))
419 // Keep going until we find a non key type record.
420 CSSM_DB_RECORDTYPE rt
= attrs
->recordType();
421 if (rt
!= CSSM_DL_DB_RECORD_SYMMETRIC_KEY
422 && rt
!= CSSM_DL_DB_RECORD_PRIVATE_KEY
423 && rt
!= CSSM_DL_DB_RECORD_PUBLIC_KEY
)
425 // @@@ Check the label and if it doesn't start with the magic for a SSKey return the key.
430 // Free the key we just retrieved
431 database()->csp()->freeKey(*reinterpret_cast<CssmKey
*>(dataBlob
.Data
));
435 // Get the group for dataBlob
436 // @@@ This might fail in which case we should probably not decrypt the
438 SSGroup
group(database(), dataBlob
);
440 // Decode the dataBlob, pass in the DL allocator.
441 group
->decodeDataBlob(dataBlob
, cred
, database()->allocator(), *data
);
446 SSDbCursorImpl::nextKey(DbAttributes
*attributes
, Key
&key
,
447 DbUniqueRecord
&uniqueId
)
449 DbAttributes noAttrs
, *attrs
;
450 attrs
= attributes
? attributes
: &noAttrs
;
451 CssmDataContainer
keyData(database()->allocator());
454 if (!DbDbCursorImpl::next(attrs
, &keyData
, uniqueId
))
456 // Keep going until we find a key type record.
457 CSSM_DB_RECORDTYPE rt
= attrs
->recordType();
458 if (rt
== CSSM_DL_DB_RECORD_SYMMETRIC_KEY
459 || rt
== CSSM_DL_DB_RECORD_PRIVATE_KEY
460 || rt
== CSSM_DL_DB_RECORD_PUBLIC_KEY
)
464 key
= Key(database()->csp(), *reinterpret_cast<CSSM_KEY
*>(keyData
.Data
));
469 SSDbCursorImpl::activate()
471 return DbDbCursorImpl::activate();
475 SSDbCursorImpl::deactivate()
477 return DbDbCursorImpl::deactivate();
482 // SSDbUniqueRecordImpl -- Secure Storage UniqueRecord Implementation.
484 SSDbUniqueRecordImpl::SSDbUniqueRecordImpl(const Db
&db
)
485 : DbUniqueRecordImpl(db
)
489 SSDbUniqueRecordImpl::~SSDbUniqueRecordImpl()
494 SSDbUniqueRecordImpl::deleteRecord()
500 SSDbUniqueRecordImpl::deleteRecord(const CSSM_ACCESS_CREDENTIALS
*cred
)
502 // Get the datablob for this record
503 // @@@ Fixme so we don't need to call DbUniqueRecordImpl::get
504 CssmDataContainer dataBlob
;
505 DbUniqueRecordImpl::get(NULL
, &dataBlob
);
507 // Get the group for dataBlob
508 // @@@ This might fail in which case we should probably not decrypt the
510 SSGroup
group(database(), dataBlob
);
512 // @@@ Use transactions.
513 // Delete the record.
514 DbUniqueRecordImpl::deleteRecord();
516 // @@@ What if the group is shared?
517 group
->deleteKey(cred
);
521 SSDbUniqueRecordImpl::modify(CSSM_DB_RECORDTYPE recordType
,
522 const CSSM_DB_RECORD_ATTRIBUTE_DATA
*attributes
,
523 const CSSM_DATA
*data
,
524 CSSM_DB_MODIFY_MODE modifyMode
)
526 modify(recordType
, attributes
, data
, modifyMode
, NULL
);
530 SSDbUniqueRecordImpl::modify(CSSM_DB_RECORDTYPE recordType
,
531 const CSSM_DB_RECORD_ATTRIBUTE_DATA
*attributes
,
532 const CSSM_DATA
*data
,
533 CSSM_DB_MODIFY_MODE modifyMode
,
534 const CSSM_ACCESS_CREDENTIALS
*cred
)
538 DbUniqueRecordImpl::modify(recordType
, attributes
, NULL
, modifyMode
);
542 // Get the datablob for this record @@@ Fixme so we don't need to call
543 // DbUniqueRecordImpl::get
544 CssmDataContainer oldDataBlob
;
545 DbUniqueRecordImpl::get(NULL
, &oldDataBlob
);
547 // Get the group for oldDataBlob
548 // @@@ This might fail in which case we should probably not decrypt the
550 SSGroup
group(database(), oldDataBlob
);
552 // Create a new dataBlob.
553 CssmDataContainer dataBlob
;
554 group
->encodeDataBlob(data
, cred
, dataBlob
);
555 DbUniqueRecordImpl::modify(recordType
, attributes
, &dataBlob
, modifyMode
);
559 SSDbUniqueRecordImpl::get(DbAttributes
*attributes
, ::CssmDataContainer
*data
)
561 get(attributes
, data
, NULL
);
565 SSDbUniqueRecordImpl::get(DbAttributes
*attributes
, ::CssmDataContainer
*data
,
566 const CSSM_ACCESS_CREDENTIALS
*cred
)
570 DbUniqueRecordImpl::get(attributes
, NULL
);
574 // Get the datablob for this record @@@ Fixme so we don't need to call
575 // DbUniqueRecordImpl::get
576 CssmDataContainer dataBlob
;
577 DbUniqueRecordImpl::get(attributes
, &dataBlob
);
579 // Get the group for dataBlob
580 // @@@ This might fail in which case we should probably not decrypt the
582 SSGroup
group(database(), dataBlob
);
584 // Decode the dataBlob, pass in the DL allocator.
585 group
->decodeDataBlob(dataBlob
, cred
, allocator(), *data
);
589 SSDbUniqueRecordImpl::group()
591 // Get the datablob for this record
592 // @@@ Fixme so we don't need to call DbUniqueRecordImpl::get
593 CssmDataContainer dataBlob
;
594 DbUniqueRecordImpl::get(NULL
, &dataBlob
);
595 return SSGroup(database(), dataBlob
);