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