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 <Security/Access.h>
22 #include <Security/osxsigning.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 CssmAllocator
&CSPDLImpl::allocator() const
49 DLImpl::allocator(); return CSPImpl::allocator();
52 void CSPDLImpl::allocator(CssmAllocator
&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 SSGroup
group(SSDb(this), rc
);
135 const CSSM_ACCESS_CREDENTIALS
*cred
= rc
? rc
->AccessCred
: NULL
;
138 return insert(recordType
, attributes
, data
, group
, cred
);
142 // @@@ Look at rc for credentials
143 group
->deleteKey(cred
);
149 SSDbImpl::insert(CSSM_DB_RECORDTYPE recordType
,
150 const CSSM_DB_RECORD_ATTRIBUTE_DATA
*attributes
,
151 const CSSM_DATA
*data
, const SSGroup
&group
,
152 const CSSM_ACCESS_CREDENTIALS
*cred
)
154 // Create an encoded dataBlob for this item.
155 CssmDataContainer
dataBlob(allocator());
156 group
->encodeDataBlob(data
, cred
, dataBlob
);
158 // Insert the record with the new juicy dataBlob.
159 return SSDbUniqueRecord(safe_cast
<SSDbUniqueRecordImpl
*>
160 (&(*DbImpl::insert(recordType
, attributes
, &dataBlob
))));
166 SSDbImpl::newDbCursor(const CSSM_QUERY
&query
, CssmAllocator
&allocator
)
168 return new SSDbCursorImpl(Db(this), query
, allocator
);
172 SSDbImpl::newDbCursor(uint32 capacity
, CssmAllocator
&allocator
)
174 return new SSDbCursorImpl(Db(this), capacity
, allocator
);
178 // SSDbUniqueRecordMaker
180 SSDbImpl::newDbUniqueRecord()
182 return new SSDbUniqueRecordImpl(Db(this));
187 // SSGroup -- Group key with acl, used to protect a group of items.
189 // @@@ Get this from a shared spot.
190 CSSM_DB_NAME_ATTR(SSGroupImpl::kLabel
, 6, "Label", 0, NULL
, BLOB
);
192 // Create a new group.
193 SSGroupImpl::SSGroupImpl(const SSDb
&ssDb
,
194 const CSSM_RESOURCE_CONTROL_CONTEXT
*credAndAclEntry
)
195 : KeyImpl(ssDb
->csp()), mLabel(ssDb
->allocator())
197 mLabel
.Length
= kLabelSize
;
198 mLabel
.Data
= reinterpret_cast<uint8
*>
199 (mLabel
.mAllocator
.malloc(mLabel
.Length
));
201 // Get our csp and set up a random number generation context.
203 Random
random(csp
, CSSM_ALGID_APPLE_YARROW
);
205 // Generate a kLabelSize byte random number that will be the label of
206 // the key which we store in the dataBlob.
207 random
.generate(mLabel
, mLabel
.Length
);
209 // Overwrite the first 4 bytes with the magic cookie for a group.
210 reinterpret_cast<uint32
*>(mLabel
.Data
)[0] = h2n(uint32(kGroupMagic
));
212 // @@@ Ensure that the label is unique (Chance of collision is 2^80 --
213 // birthday paradox).
215 // Generate a permanent 3DES key that we will use to encrypt the data.
216 GenerateKey
genKey(csp
, CSSM_ALGID_3DES_3KEY
, 192);
217 genKey
.database(ssDb
);
219 // Set the acl of the key correctly here
220 genKey
.initialAcl(ResourceControlContext::overlay(credAndAclEntry
));
223 genKey(*this, KeySpec(CSSM_KEYUSE_ENCRYPT
|CSSM_KEYUSE_DECRYPT
,
224 CSSM_KEYATTR_PERMANENT
|CSSM_KEYATTR_SENSITIVE
,
227 // Activate ourself so CSSM_FreeKey will get called when we go out of
232 // Lookup an existing group based on a dataBlob.
233 SSGroupImpl::SSGroupImpl(const SSDb
&ssDb
, const CSSM_DATA
&dataBlob
)
234 : KeyImpl(ssDb
->csp()), mLabel(ssDb
->allocator())
236 if (dataBlob
.Length
< kLabelSize
+ kIVSize
)
237 CssmError::throwMe(CSSMERR_DL_RECORD_NOT_FOUND
); // Not a SS record
239 mLabel
= CssmData(dataBlob
.Data
, kLabelSize
);
240 if (*reinterpret_cast<const uint32
*>(mLabel
.Data
) != h2n (uint32(kGroupMagic
)))
241 CssmError::throwMe(CSSMERR_DL_RECORD_NOT_FOUND
); // Not a SS record
243 // Look up the symmetric key with that label.
244 DbCursor
cursor(new DbDbCursorImpl(ssDb
, 0, CssmAllocator::standard()));
245 cursor
->recordType(CSSM_DL_DB_RECORD_SYMMETRIC_KEY
);
246 cursor
->add(CSSM_DB_EQUAL
, kLabel
, mLabel
);
248 DbUniqueRecord keyId
;
249 CssmDataContainer
keyData(ssDb
->allocator());
250 if (!cursor
->next(NULL
, &keyData
, keyId
))
251 CssmError::throwMe(CSSMERR_DL_RECORD_NOT_FOUND
); // The key can't be found
253 // Set the key part of ourself.
254 static_cast<CSSM_KEY
&>(*this) =
255 *reinterpret_cast<const CSSM_KEY
*>(keyData
.Data
);
257 // Activate ourself so CSSM_FreeKey will get called when we go out of
263 SSGroupImpl::isGroup(const CSSM_DATA
&dataBlob
)
265 return dataBlob
.Length
>= kLabelSize
+ kIVSize
266 && *reinterpret_cast<const uint32
*>(dataBlob
.Data
) == h2n(uint32(kGroupMagic
));
270 SSGroupImpl::label() const
276 SSGroupImpl::decodeDataBlob(const CSSM_DATA
&dataBlob
,
277 const CSSM_ACCESS_CREDENTIALS
*cred
,
278 CssmAllocator
&allocator
, CSSM_DATA
&data
)
280 // First get the IV and the cipherText from the blob.
281 CssmData
iv(&dataBlob
.Data
[kLabelSize
], kIVSize
);
282 CssmData
cipherText(&dataBlob
.Data
[kLabelSize
+ kIVSize
],
283 dataBlob
.Length
- (kLabelSize
+ kIVSize
));
285 CssmDataContainer
plainText1(allocator
);
286 CssmDataContainer
plainText2(allocator
);
289 // @@@ Don't use staged decrypt once the AppleCSPDL can do combo
291 // Setup decryption context
292 Decrypt
decrypt(csp(), algorithm());
293 decrypt
.mode(CSSM_ALGMODE_CBCPadIV8
);
294 decrypt
.padding(CSSM_PADDING_PKCS1
);
295 decrypt
.initVector(iv
);
296 decrypt
.key(Key(this));
297 decrypt
.cred(AccessCredentials::overlay(cred
));
298 decrypt
.decrypt(&cipherText
, 1, &plainText1
, 1);
299 decrypt
.final(plainText2
);
301 // Use DL allocator for allocating memory for data.
302 uint32 length
= plainText1
.Length
+ plainText2
.Length
;
303 data
.Data
= allocator
.alloc
<uint8
>(length
);
304 data
.Length
= length
;
305 memcpy(data
.Data
, plainText1
.Data
, plainText1
.Length
);
306 memcpy(&data
.Data
[plainText1
.Length
], plainText2
.Data
, plainText2
.Length
);
310 SSGroupImpl::encodeDataBlob(const CSSM_DATA
*data
,
311 const CSSM_ACCESS_CREDENTIALS
*cred
,
312 CssmDataContainer
&dataBlob
)
314 // Get our csp and set up a random number generation context.
316 Random
random(csp
, CSSM_ALGID_APPLE_YARROW
);
318 // Encrypt data using key and encode it in a dataBlob.
320 // First calculate a random IV.
321 uint8 ivBuf
[kIVSize
];
322 CssmData
iv(ivBuf
, kIVSize
);
323 random
.generate(iv
, kIVSize
);
325 // Setup encryption context
326 Encrypt
encrypt(csp
, algorithm());
327 encrypt
.mode(CSSM_ALGMODE_CBCPadIV8
);
328 encrypt
.padding(CSSM_PADDING_PKCS1
);
329 encrypt
.initVector(iv
);
330 encrypt
.key(Key(this));
331 encrypt
.cred(AccessCredentials::overlay(cred
));
334 const CssmData nothing
;
335 const CssmData
*plainText
= data
? CssmData::overlay(data
) : ¬hing
;
336 // @@@ Don't use staged encrypt once the AppleCSPDL can do combo
338 CssmDataContainer cipherText1
, cipherText2
;
339 encrypt
.encrypt(plainText
, 1, &cipherText1
, 1);
340 encrypt
.final(cipherText2
);
342 // Create a dataBlob containing the label followed by the IV followed
343 // by the cipherText.
344 uint32 length
= (kLabelSize
+ kIVSize
345 + cipherText1
.Length
+ cipherText2
.Length
);
346 dataBlob
.Data
= dataBlob
.mAllocator
.alloc
<uint8
>(length
);
347 dataBlob
.Length
= length
;
348 memcpy(dataBlob
.Data
, mLabel
.Data
, kLabelSize
);
349 memcpy(&dataBlob
.Data
[kLabelSize
], iv
.Data
, kIVSize
);
350 memcpy(&dataBlob
.Data
[kLabelSize
+ kIVSize
],
351 cipherText1
.Data
, cipherText1
.Length
);
352 memcpy(&dataBlob
.Data
[kLabelSize
+ kIVSize
+ cipherText1
.Length
],
353 cipherText2
.Data
, cipherText2
.Length
);
358 // SSDbCursorImpl -- Secure Storage Database Cursor Implementation.
360 SSDbCursorImpl::SSDbCursorImpl(const Db
&db
, const CSSM_QUERY
&query
,
361 CssmAllocator
&allocator
)
362 : DbDbCursorImpl(db
, query
, allocator
)
366 SSDbCursorImpl::SSDbCursorImpl(const Db
&db
, uint32 capacity
,
367 CssmAllocator
&allocator
)
368 : DbDbCursorImpl(db
, capacity
, allocator
)
373 SSDbCursorImpl::next(DbAttributes
*attributes
, ::CssmDataContainer
*data
,
374 DbUniqueRecord
&uniqueId
)
376 return next(attributes
, data
, uniqueId
, NULL
);
380 SSDbCursorImpl::next(DbAttributes
*attributes
, ::CssmDataContainer
*data
,
381 DbUniqueRecord
&uniqueId
,
382 const CSSM_ACCESS_CREDENTIALS
*cred
)
385 return DbDbCursorImpl::next(attributes
, data
, uniqueId
);
387 DbAttributes noAttrs
, *attrs
;
388 attrs
= attributes
? attributes
: &noAttrs
;
390 // Get the datablob for this record
391 CssmDataContainer
dataBlob(allocator());
394 if (!DbDbCursorImpl::next(attrs
, &dataBlob
, uniqueId
))
397 // Keep going until we find a non key type record.
398 CSSM_DB_RECORDTYPE rt
= attrs
->recordType();
399 if (rt
!= CSSM_DL_DB_RECORD_SYMMETRIC_KEY
400 && rt
!= CSSM_DL_DB_RECORD_PRIVATE_KEY
401 && rt
!= CSSM_DL_DB_RECORD_PUBLIC_KEY
)
403 // @@@ Check the label and if it doesn't start with the magic for a SSKey return the key.
408 // Free the key we just retrieved
409 database()->csp()->freeKey(*reinterpret_cast<CssmKey
*>(dataBlob
.Data
));
413 if (!SSGroupImpl::isGroup(dataBlob
))
415 data
->Data
= dataBlob
.Data
;
416 data
->Length
= dataBlob
.Length
;
417 dataBlob
.Data
= NULL
;
422 // Get the group for dataBlob
423 SSGroup
group(database(), dataBlob
);
425 // Decode the dataBlob, pass in the DL allocator.
426 group
->decodeDataBlob(dataBlob
, cred
, database()->allocator(), *data
);
431 SSDbCursorImpl::nextKey(DbAttributes
*attributes
, Key
&key
,
432 DbUniqueRecord
&uniqueId
)
434 DbAttributes noAttrs
, *attrs
;
435 attrs
= attributes
? attributes
: &noAttrs
;
436 CssmDataContainer
keyData(database()->allocator());
439 if (!DbDbCursorImpl::next(attrs
, &keyData
, uniqueId
))
441 // Keep going until we find a key type record.
442 CSSM_DB_RECORDTYPE rt
= attrs
->recordType();
443 if (rt
== CSSM_DL_DB_RECORD_SYMMETRIC_KEY
444 || rt
== CSSM_DL_DB_RECORD_PRIVATE_KEY
445 || rt
== CSSM_DL_DB_RECORD_PUBLIC_KEY
)
449 key
= Key(database()->csp(), *reinterpret_cast<CSSM_KEY
*>(keyData
.Data
));
454 SSDbCursorImpl::activate()
456 return DbDbCursorImpl::activate();
460 SSDbCursorImpl::deactivate()
462 return DbDbCursorImpl::deactivate();
467 // SSDbUniqueRecordImpl -- Secure Storage UniqueRecord Implementation.
469 SSDbUniqueRecordImpl::SSDbUniqueRecordImpl(const Db
&db
)
470 : DbUniqueRecordImpl(db
)
474 SSDbUniqueRecordImpl::~SSDbUniqueRecordImpl()
479 SSDbUniqueRecordImpl::deleteRecord()
485 SSDbUniqueRecordImpl::deleteRecord(const CSSM_ACCESS_CREDENTIALS
*cred
)
487 // Get the datablob for this record
488 // @@@ Fixme so we don't need to call DbUniqueRecordImpl::get
489 CssmDataContainer
dataBlob(allocator());
490 DbUniqueRecordImpl::get(NULL
, &dataBlob
);
492 // delete data part first:
493 // (1) don't leave data without keys around
494 // (2) delete orphaned data anyway
495 DbUniqueRecordImpl::deleteRecord();
497 // @@@ Use transactions?
498 if (SSGroupImpl::isGroup(dataBlob
))
500 // Get the group for dataBlob
501 SSGroup
group(database(), dataBlob
);
502 // Delete the group (key)
503 group
->deleteKey(cred
);
504 } catch (const CssmError
&err
) {
505 switch (err
.cssmError()) {
506 case CSSMERR_DL_RECORD_NOT_FOUND
:
507 // Zombie item (no group key). Finally at peace! No error
516 SSDbUniqueRecordImpl::modify(CSSM_DB_RECORDTYPE recordType
,
517 const CSSM_DB_RECORD_ATTRIBUTE_DATA
*attributes
,
518 const CSSM_DATA
*data
,
519 CSSM_DB_MODIFY_MODE modifyMode
)
521 modify(recordType
, attributes
, data
, modifyMode
, NULL
);
525 SSDbUniqueRecordImpl::modify(CSSM_DB_RECORDTYPE recordType
,
526 const CSSM_DB_RECORD_ATTRIBUTE_DATA
*attributes
,
527 const CSSM_DATA
*data
,
528 CSSM_DB_MODIFY_MODE modifyMode
,
529 const CSSM_ACCESS_CREDENTIALS
*cred
)
533 DbUniqueRecordImpl::modify(recordType
, attributes
, NULL
, modifyMode
);
537 // Get the datablob for this record
538 // @@@ Fixme so we don't need to call DbUniqueRecordImpl::get
539 CssmDataContainer
oldDataBlob(allocator());
540 DbUniqueRecordImpl::get(NULL
, &oldDataBlob
);
542 if (!SSGroupImpl::isGroup(oldDataBlob
))
544 DbUniqueRecordImpl::modify(recordType
, attributes
, data
, modifyMode
);
548 // Get the group for oldDataBlob
549 SSGroup
group(database(), oldDataBlob
);
551 // Create a new dataBlob.
552 CssmDataContainer
dataBlob(allocator());
553 group
->encodeDataBlob(data
, cred
, dataBlob
);
554 DbUniqueRecordImpl::modify(recordType
, attributes
, &dataBlob
, modifyMode
);
558 SSDbUniqueRecordImpl::get(DbAttributes
*attributes
, ::CssmDataContainer
*data
)
560 get(attributes
, data
, NULL
);
564 SSDbUniqueRecordImpl::get(DbAttributes
*attributes
, ::CssmDataContainer
*data
,
565 const CSSM_ACCESS_CREDENTIALS
*cred
)
569 DbUniqueRecordImpl::get(attributes
, NULL
);
573 // Get the datablob for this record
574 // @@@ Fixme so we don't need to call DbUniqueRecordImpl::get
575 CssmDataContainer
dataBlob(allocator());
576 DbUniqueRecordImpl::get(attributes
, &dataBlob
);
578 if (!SSGroupImpl::isGroup(dataBlob
))
580 data
->Data
= dataBlob
.Data
;
581 data
->Length
= dataBlob
.Length
;
582 dataBlob
.Data
= NULL
;
587 // Get the group for dataBlob
588 SSGroup
group(database(), dataBlob
);
590 // Decode the dataBlob, pass in the DL allocator.
591 group
->decodeDataBlob(dataBlob
, cred
, allocator(), *data
);
595 SSDbUniqueRecordImpl::group()
597 // Get the datablob for this record
598 // @@@ Fixme so we don't need to call DbUniqueRecordImpl::get
599 CssmDataContainer
dataBlob(allocator());
600 DbUniqueRecordImpl::get(NULL
, &dataBlob
);
601 return SSGroup(database(), dataBlob
);