]> git.saurik.com Git - apple/security.git/blob - cdsa/cdsa_client/securestorage.cpp
Security-54.1.tar.gz
[apple/security.git] / cdsa / cdsa_client / securestorage.cpp
1 /*
2 * Copyright (c) 2000-2001 Apple Computer, Inc. All Rights Reserved.
3 *
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
8 * using this file.
9 *
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.
16 */
17
18
19 #include "securestorage.h"
20 #include "genkey.h"
21 //#include "aclsupport.h"
22 #include <Security/Access.h>
23 #include <Security/osxsigning.h>
24 #include <memory>
25
26 using namespace CssmClient;
27 using namespace KeychainCore;
28
29 //
30 // Manage CSPDL attachments
31 //
32 CSPDLImpl::CSPDLImpl(const Guid &guid)
33 : CSPImpl(Cssm::standard()->autoModule(guid)),
34 DLImpl(CSPImpl::module())
35 {
36 }
37
38 CSPDLImpl::CSPDLImpl(const Module &module)
39 : CSPImpl(module),
40 DLImpl(module)
41 {
42 }
43
44 CSPDLImpl::~CSPDLImpl()
45 {
46 }
47
48 CssmAllocator &CSPDLImpl::allocator() const
49 {
50 DLImpl::allocator(); return CSPImpl::allocator();
51 }
52
53 void CSPDLImpl::allocator(CssmAllocator &alloc)
54 {
55 CSPImpl::allocator(alloc); DLImpl::allocator(alloc);
56 }
57
58 bool CSPDLImpl::operator <(const CSPDLImpl &other) const
59 {
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)));
63 }
64
65 bool CSPDLImpl::operator ==(const CSPDLImpl &other) const
66 {
67 return (static_cast<const CSPImpl &>(*this) == static_cast<const CSPImpl &>(other)
68 && static_cast<const DLImpl &>(*this) == static_cast<const DLImpl &>(other));
69 }
70
71 CSSM_SERVICE_MASK CSPDLImpl::subserviceMask() const
72 {
73 return CSPImpl::subserviceType() | DLImpl::subserviceType();
74 }
75
76 void CSPDLImpl::subserviceId(uint32 id)
77 {
78 CSPImpl::subserviceId(id); DLImpl::subserviceId(id);
79 }
80
81
82 //
83 // Secure storage
84 //
85 SSCSPDLImpl::SSCSPDLImpl(const Guid &guid) : CSPDLImpl::CSPDLImpl(guid)
86 {
87 }
88
89 SSCSPDLImpl::SSCSPDLImpl(const Module &module) : CSPDLImpl::CSPDLImpl(module)
90 {
91 }
92
93 SSCSPDLImpl::~SSCSPDLImpl()
94 {
95 }
96
97 DbImpl *
98 SSCSPDLImpl::newDb(const char *inDbName, const CSSM_NET_ADDRESS *inDbLocation)
99 {
100 return new SSDbImpl(SSCSPDL(this), inDbName, inDbLocation);
101 }
102
103
104 //
105 // SSDbImpl -- Secure Storage Database Implementation
106 //
107 SSDbImpl::SSDbImpl(const SSCSPDL &cspdl, const char *inDbName,
108 const CSSM_NET_ADDRESS *inDbLocation)
109 : DbImpl(cspdl, inDbName, inDbLocation)
110 {
111 }
112
113 SSDbImpl::~SSDbImpl()
114 {
115 }
116
117 void
118 SSDbImpl::create()
119 {
120 DbImpl::create();
121 }
122
123 void
124 SSDbImpl::open()
125 {
126 DbImpl::open();
127 }
128
129 SSDbUniqueRecord
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)
134 {
135 SSGroup group(SSDb(this), rc);
136 const CSSM_ACCESS_CREDENTIALS *cred = rc ? rc->AccessCred : NULL;
137 try
138 {
139 return insert(recordType, attributes, data, group, cred);
140 }
141 catch(...)
142 {
143 // @@@ Look at rc for credentials
144 group->deleteKey(cred);
145 throw;
146 }
147 }
148
149 SSDbUniqueRecord
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)
154 {
155 // Create an encoded dataBlob for this item.
156 CssmDataContainer dataBlob(allocator());
157 group->encodeDataBlob(data, cred, dataBlob);
158
159 // Insert the record with the new juicy dataBlob.
160 return SSDbUniqueRecord(safe_cast<SSDbUniqueRecordImpl *>
161 (&(*DbImpl::insert(recordType, attributes, &dataBlob))));
162 }
163
164
165 // DbCursorMaker
166 DbCursorImpl *
167 SSDbImpl::newDbCursor(const CSSM_QUERY &query, CssmAllocator &allocator)
168 {
169 return new SSDbCursorImpl(Db(this), query, allocator);
170 }
171
172 DbCursorImpl *
173 SSDbImpl::newDbCursor(uint32 capacity, CssmAllocator &allocator)
174 {
175 return new SSDbCursorImpl(Db(this), capacity, allocator);
176 }
177
178
179 // SSDbUniqueRecordMaker
180 DbUniqueRecordImpl *
181 SSDbImpl::newDbUniqueRecord()
182 {
183 return new SSDbUniqueRecordImpl(Db(this));
184 }
185
186
187 //
188 // SSGroup -- Group key with acl, used to protect a group of items.
189 //
190 // @@@ Get this from a shared spot.
191 CSSM_DB_NAME_ATTR(SSGroupImpl::kLabel, 6, "Label", 0, NULL, BLOB);
192
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())
197 {
198 mLabel.Length = kLabelSize;
199 mLabel.Data = reinterpret_cast<uint8 *>
200 (mLabel.mAllocator.malloc(mLabel.Length));
201
202 // Get our csp and set up a random number generation context.
203 CSP csp(csp());
204 Random random(csp, CSSM_ALGID_APPLE_YARROW);
205
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);
209
210 // Overwrite the first 4 bytes with the magic cookie for a group.
211 reinterpret_cast<uint32 *>(mLabel.Data)[0] = kGroupMagic;
212
213 // @@@ Ensure that the label is unique (Chance of collision is 2^80 --
214 // birthday paradox).
215
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);
219
220 // Set the acl of the key correctly here
221 genKey.initialAcl(ResourceControlContext::overlay(credAndAclEntry));
222
223 // Generate the key
224 genKey(*this, KeySpec(CSSM_KEYUSE_ENCRYPT|CSSM_KEYUSE_DECRYPT,
225 CSSM_KEYATTR_PERMANENT|CSSM_KEYATTR_SENSITIVE,
226 mLabel));
227
228 // Activate ourself so CSSM_FreeKey will get called when we go out of
229 // scope.
230 activate();
231 }
232
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())
236 {
237 if (dataBlob.Length < kLabelSize + kIVSize)
238 CssmError::throwMe(CSSMERR_DL_RECORD_NOT_FOUND); // Not a SS record
239
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
243
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);
248
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
253
254 // Set the key part of ourself.
255 static_cast<CSSM_KEY &>(*this) =
256 *reinterpret_cast<const CSSM_KEY *>(keyData.Data);
257
258 // Activate ourself so CSSM_FreeKey will get called when we go out of
259 // scope.
260 activate();
261 }
262
263 bool
264 SSGroupImpl::isGroup(const CSSM_DATA &dataBlob)
265 {
266 return dataBlob.Length >= kLabelSize + kIVSize
267 && *reinterpret_cast<const uint32 *>(dataBlob.Data) == kGroupMagic;
268 }
269
270 const CssmData
271 SSGroupImpl::label() const
272 {
273 return mLabel;
274 }
275
276 void
277 SSGroupImpl::decodeDataBlob(const CSSM_DATA &dataBlob,
278 const CSSM_ACCESS_CREDENTIALS *cred,
279 CssmAllocator &allocator, CSSM_DATA &data)
280 {
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));
285
286 CssmDataContainer plainText1(allocator);
287 CssmDataContainer plainText2(allocator);
288 try
289 {
290 // Decrypt the data
291 // @@@ Don't use staged decrypt once the AppleCSPDL can do combo
292 // encryption.
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);
302 }
303 catch (const CssmError &e)
304 {
305 if (e.cssmError() != CSSMERR_CSP_APPLE_ADD_APPLICATION_ACL_SUBJECT)
306 throw;
307
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.
309 #if 1
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
315 #else
316 KeychainACL acl(Key(this));
317 acl.anyAllow(false);
318 acl.alwaysAskUser(true);
319
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));
324
325 // Change the acl.
326 acl.commit();
327 #endif
328
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);
338 }
339
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);
346 }
347
348 void
349 SSGroupImpl::encodeDataBlob(const CSSM_DATA *data,
350 const CSSM_ACCESS_CREDENTIALS *cred,
351 CssmDataContainer &dataBlob)
352 {
353 // Get our csp and set up a random number generation context.
354 CSP csp(csp());
355 Random random(csp, CSSM_ALGID_APPLE_YARROW);
356
357 // Encrypt data using key and encode it in a dataBlob.
358
359 // First calculate a random IV.
360 uint8 ivBuf[kIVSize];
361 CssmData iv(ivBuf, kIVSize);
362 random.generate(iv, kIVSize);
363
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));
371
372 // Encrypt the data
373 const CssmData nothing;
374 const CssmData *plainText = data ? CssmData::overlay(data) : &nothing;
375 // @@@ Don't use staged encrypt once the AppleCSPDL can do combo
376 // encryption.
377 CssmDataContainer cipherText1, cipherText2;
378 encrypt.encrypt(plainText, 1, &cipherText1, 1);
379 encrypt.final(cipherText2);
380
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);
393 }
394
395
396 //
397 // SSDbCursorImpl -- Secure Storage Database Cursor Implementation.
398 //
399 SSDbCursorImpl::SSDbCursorImpl(const Db &db, const CSSM_QUERY &query,
400 CssmAllocator &allocator)
401 : DbDbCursorImpl(db, query, allocator)
402 {
403 }
404
405 SSDbCursorImpl::SSDbCursorImpl(const Db &db, uint32 capacity,
406 CssmAllocator &allocator)
407 : DbDbCursorImpl(db, capacity, allocator)
408 {
409 }
410
411 bool
412 SSDbCursorImpl::next(DbAttributes *attributes, ::CssmDataContainer *data,
413 DbUniqueRecord &uniqueId)
414 {
415 return next(attributes, data, uniqueId, NULL);
416 }
417
418 bool
419 SSDbCursorImpl::next(DbAttributes *attributes, ::CssmDataContainer *data,
420 DbUniqueRecord &uniqueId,
421 const CSSM_ACCESS_CREDENTIALS *cred)
422 {
423 if (!data)
424 return DbDbCursorImpl::next(attributes, data, uniqueId);
425
426 DbAttributes noAttrs, *attrs;
427 attrs = attributes ? attributes : &noAttrs;
428
429 // Get the datablob for this record
430 CssmDataContainer dataBlob(allocator());
431 for (;;)
432 {
433 if (!DbDbCursorImpl::next(attrs, &dataBlob, uniqueId))
434 return false;
435
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)
441 {
442 // @@@ Check the label and if it doesn't start with the magic for a SSKey return the key.
443 break;
444 }
445 else
446 {
447 // Free the key we just retrieved
448 database()->csp()->freeKey(*reinterpret_cast<CssmKey *>(dataBlob.Data));
449 }
450 }
451
452 if (!SSGroupImpl::isGroup(dataBlob))
453 {
454 data->Data = dataBlob.Data;
455 data->Length = dataBlob.Length;
456 dataBlob.Data = NULL;
457 dataBlob.Length = 0;
458 return true;
459 }
460
461 // Get the group for dataBlob
462 SSGroup group(database(), dataBlob);
463
464 // Decode the dataBlob, pass in the DL allocator.
465 group->decodeDataBlob(dataBlob, cred, database()->allocator(), *data);
466 return true;
467 }
468
469 bool
470 SSDbCursorImpl::nextKey(DbAttributes *attributes, Key &key,
471 DbUniqueRecord &uniqueId)
472 {
473 DbAttributes noAttrs, *attrs;
474 attrs = attributes ? attributes : &noAttrs;
475 CssmDataContainer keyData(database()->allocator());
476 for (;;)
477 {
478 if (!DbDbCursorImpl::next(attrs, &keyData, uniqueId))
479 return false;
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)
485 break;
486 }
487
488 key = Key(database()->csp(), *reinterpret_cast<CSSM_KEY *>(keyData.Data));
489 return true;
490 }
491
492 void
493 SSDbCursorImpl::activate()
494 {
495 return DbDbCursorImpl::activate();
496 }
497
498 void
499 SSDbCursorImpl::deactivate()
500 {
501 return DbDbCursorImpl::deactivate();
502 }
503
504
505 //
506 // SSDbUniqueRecordImpl -- Secure Storage UniqueRecord Implementation.
507 //
508 SSDbUniqueRecordImpl::SSDbUniqueRecordImpl(const Db &db)
509 : DbUniqueRecordImpl(db)
510 {
511 }
512
513 SSDbUniqueRecordImpl::~SSDbUniqueRecordImpl()
514 {
515 }
516
517 void
518 SSDbUniqueRecordImpl::deleteRecord()
519 {
520 deleteRecord(NULL);
521 }
522
523 void
524 SSDbUniqueRecordImpl::deleteRecord(const CSSM_ACCESS_CREDENTIALS *cred)
525 {
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);
530
531 // @@@ Use transactions.
532 if (SSGroupImpl::isGroup(dataBlob))
533 {
534 // Get the group for dataBlob
535 SSGroup group(database(), dataBlob);
536 // Delete the group
537 // @@@ What if the group is shared?
538 group->deleteKey(cred);
539 }
540
541 // Delete the record.
542 DbUniqueRecordImpl::deleteRecord();
543 }
544
545 void
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)
550 {
551 modify(recordType, attributes, data, modifyMode, NULL);
552 }
553
554 void
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)
560 {
561 if (!data)
562 {
563 DbUniqueRecordImpl::modify(recordType, attributes, NULL, modifyMode);
564 return;
565 }
566
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);
571
572 if (!SSGroupImpl::isGroup(oldDataBlob))
573 {
574 DbUniqueRecordImpl::modify(recordType, attributes, data, modifyMode);
575 return;
576 }
577
578 // Get the group for oldDataBlob
579 SSGroup group(database(), oldDataBlob);
580
581 // Create a new dataBlob.
582 CssmDataContainer dataBlob(allocator());
583 group->encodeDataBlob(data, cred, dataBlob);
584 DbUniqueRecordImpl::modify(recordType, attributes, &dataBlob, modifyMode);
585 }
586
587 void
588 SSDbUniqueRecordImpl::get(DbAttributes *attributes, ::CssmDataContainer *data)
589 {
590 get(attributes, data, NULL);
591 }
592
593 void
594 SSDbUniqueRecordImpl::get(DbAttributes *attributes, ::CssmDataContainer *data,
595 const CSSM_ACCESS_CREDENTIALS *cred)
596 {
597 if (!data)
598 {
599 DbUniqueRecordImpl::get(attributes, NULL);
600 return;
601 }
602
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);
607
608 if (!SSGroupImpl::isGroup(dataBlob))
609 {
610 data->Data = dataBlob.Data;
611 data->Length = dataBlob.Length;
612 dataBlob.Data = NULL;
613 dataBlob.Length = 0;
614 return;
615 }
616
617 // Get the group for dataBlob
618 SSGroup group(database(), dataBlob);
619
620 // Decode the dataBlob, pass in the DL allocator.
621 group->decodeDataBlob(dataBlob, cred, allocator(), *data);
622 }
623
624 SSGroup
625 SSDbUniqueRecordImpl::group()
626 {
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);
632 }