]> git.saurik.com Git - apple/security.git/blob - cdsa/cdsa_client/securestorage.cpp
Security-30.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/osxsigning.h>
23 #include <memory>
24
25 using namespace CssmClient;
26
27 //
28 // Manage CSPDL attachments
29 //
30 CSPDLImpl::CSPDLImpl(const Guid &guid)
31 : CSPImpl(Cssm::standard()->autoModule(guid)),
32 DLImpl(CSPImpl::module())
33 {
34 }
35
36 CSPDLImpl::CSPDLImpl(const Module &module)
37 : CSPImpl(module),
38 DLImpl(module)
39 {
40 }
41
42 CSPDLImpl::~CSPDLImpl()
43 {
44 }
45
46 CssmAllocator &CSPDLImpl::allocator() const
47 {
48 DLImpl::allocator(); return CSPImpl::allocator();
49 }
50
51 void CSPDLImpl::allocator(CssmAllocator &alloc)
52 {
53 CSPImpl::allocator(alloc); DLImpl::allocator(alloc);
54 }
55
56 bool CSPDLImpl::operator <(const CSPDLImpl &other) const
57 {
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)));
61 }
62
63 bool CSPDLImpl::operator ==(const CSPDLImpl &other) const
64 {
65 return (static_cast<const CSPImpl &>(*this) == static_cast<const CSPImpl &>(other)
66 && static_cast<const DLImpl &>(*this) == static_cast<const DLImpl &>(other));
67 }
68
69 CSSM_SERVICE_MASK CSPDLImpl::subserviceMask() const
70 {
71 return CSPImpl::subserviceType() | DLImpl::subserviceType();
72 }
73
74 void CSPDLImpl::subserviceId(uint32 id)
75 {
76 CSPImpl::subserviceId(id); DLImpl::subserviceId(id);
77 }
78
79
80 //
81 // Secure storage
82 //
83 SSCSPDLImpl::SSCSPDLImpl(const Guid &guid) : CSPDLImpl::CSPDLImpl(guid)
84 {
85 }
86
87 SSCSPDLImpl::SSCSPDLImpl(const Module &module) : CSPDLImpl::CSPDLImpl(module)
88 {
89 }
90
91 SSCSPDLImpl::~SSCSPDLImpl()
92 {
93 }
94
95 DbImpl *
96 SSCSPDLImpl::newDb(const char *inDbName, const CSSM_NET_ADDRESS *inDbLocation)
97 {
98 return new SSDbImpl(SSCSPDL(this), inDbName, inDbLocation);
99 }
100
101
102 //
103 // SSDbImpl -- Secure Storage Database Implementation
104 //
105 SSDbImpl::SSDbImpl(const SSCSPDL &cspdl, const char *inDbName,
106 const CSSM_NET_ADDRESS *inDbLocation)
107 : DbImpl(cspdl, inDbName, inDbLocation)
108 {
109 }
110
111 SSDbImpl::~SSDbImpl()
112 {
113 }
114
115 void
116 SSDbImpl::create()
117 {
118 DbImpl::create();
119 }
120
121 void
122 SSDbImpl::open()
123 {
124 DbImpl::open();
125 }
126
127 SSDbUniqueRecord
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)
132 {
133 SSGroup group(SSDb(this), rc);
134 const CSSM_ACCESS_CREDENTIALS *cred = rc ? rc->AccessCred : NULL;
135 try
136 {
137 return insert(recordType, attributes, data, group, cred);
138 }
139 catch(...)
140 {
141 // @@@ Look at rc for credentials
142 group->deleteKey(cred);
143 throw;
144 }
145 }
146
147 SSDbUniqueRecord
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)
152 {
153 // Create an encoded dataBlob for this item.
154 CssmDataContainer dataBlob;
155 group->encodeDataBlob(data, cred, dataBlob);
156
157 // Insert the record with the new juicy dataBlob.
158 return SSDbUniqueRecord(safe_cast<SSDbUniqueRecordImpl *>
159 (&(*DbImpl::insert(recordType, attributes, &dataBlob))));
160 }
161
162
163 // DbCursorMaker
164 DbCursorImpl *
165 SSDbImpl::newDbCursor(const CSSM_QUERY &query, CssmAllocator &allocator)
166 {
167 return new SSDbCursorImpl(Db(this), query, allocator);
168 }
169
170 DbCursorImpl *
171 SSDbImpl::newDbCursor(uint32 capacity, CssmAllocator &allocator)
172 {
173 return new SSDbCursorImpl(Db(this), capacity, allocator);
174 }
175
176
177 // SSDbUniqueRecordMaker
178 DbUniqueRecordImpl *
179 SSDbImpl::newDbUniqueRecord()
180 {
181 return new SSDbUniqueRecordImpl(Db(this));
182 }
183
184
185 //
186 // SSGroup -- Group key with acl, used to protect a group of items.
187 //
188 // @@@ Get this from a shared spot.
189 CSSM_DB_NAME_ATTR(SSGroupImpl::kLabel, 6, "Label", 0, NULL, BLOB);
190
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())
195 {
196 mLabel.Length = kLabelSize;
197 mLabel.Data = reinterpret_cast<uint8 *>
198 (mLabel.mAllocator.malloc(mLabel.Length));
199
200 // Get our csp and set up a random number generation context.
201 CSP csp(csp());
202 Random random(csp, CSSM_ALGID_APPLE_YARROW);
203
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);
207
208 // Overwrite the first 4 bytes with the magic cookie for a group.
209 reinterpret_cast<uint32 *>(mLabel.Data)[0] = kGroupMagic;
210
211 // @@@ Ensure that the label is unique (Chance of collision is 2^80 --
212 // birthday paradox).
213
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);
217
218 // Set the acl of the key correctly here
219 genKey.initialAcl(ResourceControlContext::overlay(credAndAclEntry));
220
221 // Generate the key
222 genKey(*this, KeySpec(CSSM_KEYUSE_ENCRYPT|CSSM_KEYUSE_DECRYPT,
223 CSSM_KEYATTR_PERMANENT|CSSM_KEYATTR_SENSITIVE,
224 mLabel));
225
226 // Activate ourself so CSSM_FreeKey will get called when we go out of
227 // scope.
228 activate();
229 }
230
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())
234 {
235 if (dataBlob.Length < kLabelSize + kIVSize)
236 CssmError::throwMe(CSSMERR_DL_RECORD_NOT_FOUND); // @@@ Not a SS record
237
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
241
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);
246
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
251
252 // Set the key part of ourself.
253 static_cast<CSSM_KEY &>(*this) =
254 *reinterpret_cast<const CSSM_KEY *>(keyData.Data);
255
256 // Activate ourself so CSSM_FreeKey will get called when we go out of
257 // scope.
258 activate();
259 }
260
261 const CssmData
262 SSGroupImpl::label() const
263 {
264 return mLabel;
265 }
266
267 void
268 SSGroupImpl::decodeDataBlob(const CSSM_DATA &dataBlob,
269 const CSSM_ACCESS_CREDENTIALS *cred,
270 CssmAllocator &allocator, CSSM_DATA &data)
271 {
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));
276
277 CssmDataContainer plainText1(allocator);
278 CssmDataContainer plainText2(allocator);
279 try
280 {
281 // Decrypt the data
282 // @@@ Don't use staged decrypt once the AppleCSPDL can do combo
283 // encryption.
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);
293 }
294 catch (const CssmError &e)
295 {
296 if (e.cssmError() != CSSMERR_CSP_APPLE_ADD_APPLICATION_ACL_SUBJECT)
297 throw;
298
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));
301 acl.anyAllow(false);
302 acl.alwaysAskUser(true);
303
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));
308
309 // Change the acl.
310 acl.commit();
311
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);
321 }
322
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);
329 }
330
331 void
332 SSGroupImpl::encodeDataBlob(const CSSM_DATA *data,
333 const CSSM_ACCESS_CREDENTIALS *cred,
334 CssmDataContainer &dataBlob)
335 {
336 // Get our csp and set up a random number generation context.
337 CSP csp(csp());
338 Random random(csp, CSSM_ALGID_APPLE_YARROW);
339
340 // Encrypt data using key and encode it in a dataBlob.
341
342 // First calculate a random IV.
343 uint8 ivBuf[kIVSize];
344 CssmData iv(ivBuf, kIVSize);
345 random.generate(iv, kIVSize);
346
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));
354
355 // Encrypt the data
356 const CssmData nothing;
357 const CssmData *plainText = data ? CssmData::overlay(data) : &nothing;
358 // @@@ Don't use staged encrypt once the AppleCSPDL can do combo
359 // encryption.
360 CssmDataContainer cipherText1, cipherText2;
361 encrypt.encrypt(plainText, 1, &cipherText1, 1);
362 encrypt.final(cipherText2);
363
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);
376 }
377
378
379 //
380 // SSDbCursorImpl -- Secure Storage Database Cursor Implementation.
381 //
382 SSDbCursorImpl::SSDbCursorImpl(const Db &db, const CSSM_QUERY &query,
383 CssmAllocator &allocator)
384 : DbDbCursorImpl(db, query, allocator)
385 {
386 }
387
388 SSDbCursorImpl::SSDbCursorImpl(const Db &db, uint32 capacity,
389 CssmAllocator &allocator)
390 : DbDbCursorImpl(db, capacity, allocator)
391 {
392 }
393
394 bool
395 SSDbCursorImpl::next(DbAttributes *attributes, ::CssmDataContainer *data,
396 DbUniqueRecord &uniqueId)
397 {
398 return next(attributes, data, uniqueId, NULL);
399 }
400
401 bool
402 SSDbCursorImpl::next(DbAttributes *attributes, ::CssmDataContainer *data,
403 DbUniqueRecord &uniqueId,
404 const CSSM_ACCESS_CREDENTIALS *cred)
405 {
406 if (!data)
407 return DbDbCursorImpl::next(attributes, data, uniqueId);
408
409 DbAttributes noAttrs, *attrs;
410 attrs = attributes ? attributes : &noAttrs;
411
412 // Get the datablob for this record
413 CssmDataContainer dataBlob;
414 for (;;)
415 {
416 if (!DbDbCursorImpl::next(attrs, &dataBlob, uniqueId))
417 return false;
418
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)
424 {
425 // @@@ Check the label and if it doesn't start with the magic for a SSKey return the key.
426 break;
427 }
428 else
429 {
430 // Free the key we just retrieved
431 database()->csp()->freeKey(*reinterpret_cast<CssmKey *>(dataBlob.Data));
432 }
433 }
434
435 // Get the group for dataBlob
436 // @@@ This might fail in which case we should probably not decrypt the
437 // data.
438 SSGroup group(database(), dataBlob);
439
440 // Decode the dataBlob, pass in the DL allocator.
441 group->decodeDataBlob(dataBlob, cred, database()->allocator(), *data);
442 return true;
443 }
444
445 bool
446 SSDbCursorImpl::nextKey(DbAttributes *attributes, Key &key,
447 DbUniqueRecord &uniqueId)
448 {
449 DbAttributes noAttrs, *attrs;
450 attrs = attributes ? attributes : &noAttrs;
451 CssmDataContainer keyData(database()->allocator());
452 for (;;)
453 {
454 if (!DbDbCursorImpl::next(attrs, &keyData, uniqueId))
455 return false;
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)
461 break;
462 }
463
464 key = Key(database()->csp(), *reinterpret_cast<CSSM_KEY *>(keyData.Data));
465 return true;
466 }
467
468 void
469 SSDbCursorImpl::activate()
470 {
471 return DbDbCursorImpl::activate();
472 }
473
474 void
475 SSDbCursorImpl::deactivate()
476 {
477 return DbDbCursorImpl::deactivate();
478 }
479
480
481 //
482 // SSDbUniqueRecordImpl -- Secure Storage UniqueRecord Implementation.
483 //
484 SSDbUniqueRecordImpl::SSDbUniqueRecordImpl(const Db &db)
485 : DbUniqueRecordImpl(db)
486 {
487 }
488
489 SSDbUniqueRecordImpl::~SSDbUniqueRecordImpl()
490 {
491 }
492
493 void
494 SSDbUniqueRecordImpl::deleteRecord()
495 {
496 deleteRecord(NULL);
497 }
498
499 void
500 SSDbUniqueRecordImpl::deleteRecord(const CSSM_ACCESS_CREDENTIALS *cred)
501 {
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);
506
507 // Get the group for dataBlob
508 // @@@ This might fail in which case we should probably not decrypt the
509 // data.
510 SSGroup group(database(), dataBlob);
511
512 // @@@ Use transactions.
513 // Delete the record.
514 DbUniqueRecordImpl::deleteRecord();
515 // Delete the group
516 // @@@ What if the group is shared?
517 group->deleteKey(cred);
518 }
519
520 void
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)
525 {
526 modify(recordType, attributes, data, modifyMode, NULL);
527 }
528
529 void
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)
535 {
536 if (!data)
537 {
538 DbUniqueRecordImpl::modify(recordType, attributes, NULL, modifyMode);
539 return;
540 }
541
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);
546
547 // Get the group for oldDataBlob
548 // @@@ This might fail in which case we should probably not decrypt the
549 // data.
550 SSGroup group(database(), oldDataBlob);
551
552 // Create a new dataBlob.
553 CssmDataContainer dataBlob;
554 group->encodeDataBlob(data, cred, dataBlob);
555 DbUniqueRecordImpl::modify(recordType, attributes, &dataBlob, modifyMode);
556 }
557
558 void
559 SSDbUniqueRecordImpl::get(DbAttributes *attributes, ::CssmDataContainer *data)
560 {
561 get(attributes, data, NULL);
562 }
563
564 void
565 SSDbUniqueRecordImpl::get(DbAttributes *attributes, ::CssmDataContainer *data,
566 const CSSM_ACCESS_CREDENTIALS *cred)
567 {
568 if (!data)
569 {
570 DbUniqueRecordImpl::get(attributes, NULL);
571 return;
572 }
573
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);
578
579 // Get the group for dataBlob
580 // @@@ This might fail in which case we should probably not decrypt the
581 // data.
582 SSGroup group(database(), dataBlob);
583
584 // Decode the dataBlob, pass in the DL allocator.
585 group->decodeDataBlob(dataBlob, cred, allocator(), *data);
586 }
587
588 SSGroup
589 SSDbUniqueRecordImpl::group()
590 {
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);
596 }