]> git.saurik.com Git - apple/security.git/blob - Keychain/Item.cpp
Security-163.tar.gz
[apple/security.git] / Keychain / Item.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 //
20 // Item.cpp
21 //
22
23 #include "Item.h"
24
25 #include "Certificate.h"
26 #include "KeyItem.h"
27
28 #include "Globals.h"
29 #include "Schema.h"
30 #include "KCEventNotifier.h"
31 #include "cssmdatetime.h"
32 #include <Security/keychainacl.h>
33 #include <Security/osxsigning.h>
34 #include <Security/trackingallocator.h>
35 #include <Security/SecKeychainAPIPriv.h>
36
37 #define SENDACCESSNOTIFICATIONS 1
38
39 using namespace KeychainCore;
40 using namespace CSSMDateTimeUtils;
41
42 //
43 // ItemImpl
44 //
45
46 // NewItemImpl constructor
47 ItemImpl::ItemImpl(SecItemClass itemClass, OSType itemCreator, UInt32 length, const void* data)
48 : mDbAttributes(new DbAttributes())
49 {
50 if (length && data)
51 mData.reset(new CssmDataContainer(data, length));
52
53 mDbAttributes->recordType(Schema::recordTypeFor(itemClass));
54
55 if (itemCreator)
56 mDbAttributes->add(Schema::attributeInfo(kSecCreatorItemAttr), itemCreator);
57 }
58
59 ItemImpl::ItemImpl(SecItemClass itemClass, SecKeychainAttributeList *attrList, UInt32 length, const void* data)
60 : mDbAttributes(new DbAttributes())
61 {
62 if (length && data)
63 mData.reset(new CssmDataContainer(data, length));
64
65
66 mDbAttributes->recordType(Schema::recordTypeFor(itemClass));
67
68 if(attrList)
69 {
70 for(UInt32 i=0; i < attrList->count; i++)
71 {
72 mDbAttributes->add(Schema::attributeInfo(attrList->attr[i].tag), CssmData(attrList->attr[i].data, attrList->attr[i].length));
73 }
74 }
75 }
76
77 // DbItemImpl constructor
78 ItemImpl::ItemImpl(const Keychain &keychain, const PrimaryKey &primaryKey, const DbUniqueRecord &uniqueId)
79 : mUniqueId(uniqueId), mKeychain(keychain), mPrimaryKey(primaryKey)
80 {
81 mKeychain->addItem(mPrimaryKey, this);
82 }
83
84 // PrimaryKey ItemImpl constructor
85 ItemImpl::ItemImpl(const Keychain &keychain, const PrimaryKey &primaryKey)
86 : mKeychain(keychain), mPrimaryKey(primaryKey)
87 {
88 mKeychain->addItem(mPrimaryKey, this);
89 }
90
91 // Constructor used when copying an item to a keychain.
92
93 ItemImpl::ItemImpl(ItemImpl &item) :
94 mData(item.modifiedData() ? NULL : new CssmDataContainer()),
95 mDbAttributes(new DbAttributes())
96 {
97 mDbAttributes->recordType(item.recordType());
98 CSSM_DB_RECORD_ATTRIBUTE_INFO *schemaAttributes = NULL;
99
100 if (item.mKeychain) {
101 // get the entire source item from its keychain. This requires figuring
102 // out the schema for the item based on its record type.
103
104 for (uint32 i = 0; i < Schema::DBInfo.NumberOfRecordTypes; i++)
105 if (item.recordType() == Schema::DBInfo.RecordAttributeNames[i].DataRecordType) {
106 schemaAttributes = &Schema::DBInfo.RecordAttributeNames[i];
107 break;
108 }
109
110 if (schemaAttributes == NULL)
111 // the source item is invalid
112 MacOSError::throwMe(errSecInvalidItemRef);
113
114 for (uint32 i = 0; i < schemaAttributes->NumberOfAttributes; i++)
115 mDbAttributes->add(schemaAttributes->AttributeInfo[i]);
116
117 item.getContent(mDbAttributes.get(), mData.get());
118 }
119
120 // @@@ We don't deal with modified attributes.
121
122 if (item.modifiedData())
123 // the copied data comes from the source item
124 mData.reset(new CssmDataContainer(item.modifiedData()->Data,
125 item.modifiedData()->Length));
126 }
127
128 ItemImpl::~ItemImpl() throw()
129 {
130 if (mKeychain && *mPrimaryKey)
131 mKeychain->removeItem(*mPrimaryKey, this);
132 }
133
134 void
135 ItemImpl::didModify()
136 {
137 mData.reset(NULL);
138 mDbAttributes.reset(NULL);
139 }
140
141 const CSSM_DATA &
142 ItemImpl::defaultAttributeValue(const CSSM_DB_ATTRIBUTE_INFO &info)
143 {
144 static const uint32 zeroInt = 0;
145 static const double zeroDouble = 0.0;
146 static const char timeBytes[] = "20010101000000Z";
147
148 static const CSSM_DATA defaultFourBytes = { 4, (uint8 *) &zeroInt };
149 static const CSSM_DATA defaultEightBytes = { 8, (uint8 *) &zeroDouble };
150 static const CSSM_DATA defaultTime = { 16, (uint8 *) timeBytes };
151 static const CSSM_DATA defaultZeroBytes = { 0, NULL };
152
153 switch (info.AttributeFormat)
154 {
155 case CSSM_DB_ATTRIBUTE_FORMAT_SINT32:
156 case CSSM_DB_ATTRIBUTE_FORMAT_UINT32:
157 return defaultFourBytes;
158
159 case CSSM_DB_ATTRIBUTE_FORMAT_REAL:
160 return defaultEightBytes;
161
162 case CSSM_DB_ATTRIBUTE_FORMAT_TIME_DATE:
163 return defaultTime;
164
165 default:
166 return defaultZeroBytes;
167 }
168 }
169
170
171
172 PrimaryKey
173 ItemImpl::add(Keychain &keychain)
174 {
175 // If we already have a Keychain we can't be added.
176 if (mKeychain)
177 MacOSError::throwMe(errSecDuplicateItem);
178
179 // If we don't have any attributes we can't be added.
180 // (this might occur if attempting to add the item twice, since our attributes
181 // and data are set to NULL at the end of this function.)
182 if (!mDbAttributes.get())
183 MacOSError::throwMe(errSecDuplicateItem);
184
185 CSSM_DB_RECORDTYPE recordType = mDbAttributes->recordType();
186
187 // update the creation and update dates on the new item
188 KeychainSchema schema = keychain->keychainSchema();
189 SInt64 date;
190 GetCurrentMacLongDateTime(date);
191 if (schema->hasAttribute(recordType, kSecCreationDateItemAttr))
192 {
193 setAttribute(schema->attributeInfoFor(recordType, kSecCreationDateItemAttr), date);
194 }
195
196 if (schema->hasAttribute(recordType, kSecModDateItemAttr))
197 {
198 setAttribute(schema->attributeInfoFor(recordType, kSecModDateItemAttr), date);
199 }
200
201 // If the label (PrintName) attribute isn't specified, set a default label.
202 if (!mDbAttributes->find(Schema::attributeInfo(kSecLabelItemAttr)))
203 {
204 CssmDbAttributeData *label = NULL;
205 switch (recordType)
206 {
207 case CSSM_DL_DB_RECORD_GENERIC_PASSWORD:
208 label = mDbAttributes->find(Schema::attributeInfo(kSecServiceItemAttr));
209 break;
210
211 case CSSM_DL_DB_RECORD_APPLESHARE_PASSWORD:
212 case CSSM_DL_DB_RECORD_INTERNET_PASSWORD:
213 label = mDbAttributes->find(Schema::attributeInfo(kSecServerItemAttr));
214 // if AppleShare server name wasn't specified, try the server address
215 if (!label) label = mDbAttributes->find(Schema::attributeInfo(kSecAddressItemAttr));
216 break;
217
218 default:
219 break;
220 }
221 // if all else fails, use the account name.
222 if (!label)
223 label = mDbAttributes->find(Schema::attributeInfo(kSecAccountItemAttr));
224
225 if (label && label->size())
226 setAttribute (Schema::attributeInfo(kSecLabelItemAttr), label->at<CssmData>(0));
227 }
228
229 // get the attributes that are part of the primary key
230 const CssmAutoDbRecordAttributeInfo &primaryKeyInfos =
231 keychain->primaryKeyInfosFor(recordType);
232
233 // make sure each primary key element has a value in the item, otherwise
234 // the database will complain. we make a set of the provided attribute infos
235 // to avoid O(N^2) behavior.
236
237 DbAttributes *attributes = mDbAttributes.get();
238 typedef set<CssmDbAttributeInfo> InfoSet;
239 InfoSet infoSet;
240
241 // make a set of all the attributes in the key
242 for (uint32 i = 0; i < attributes->size(); i++)
243 infoSet.insert(attributes->at(i).Info);
244
245 for (uint32 i = 0; i < primaryKeyInfos.size(); i++) { // check to make sure all required attributes are in the key
246 InfoSet::const_iterator it = infoSet.find(primaryKeyInfos.at(i));
247
248 if (it == infoSet.end()) { // not in the key? add the default
249 // we need to add a default value to the item attributes
250 attributes->add(primaryKeyInfos.at(i), defaultAttributeValue(primaryKeyInfos.at(i)));
251 }
252 }
253
254 Db db(keychain->database());
255 if (useSecureStorage(db))
256 {
257 // Add the item to the secure storage db
258 SSDb ssDb(safe_cast<SSDbImpl *>(&(*db)));
259
260 TrackingAllocator allocator(CssmAllocator::standard());
261
262 // hhs replaced with the new aclFactory class
263 AclFactory aclFactory;
264 const AccessCredentials *nullCred = aclFactory.nullCred();
265
266 SecPointer<Access> access = mAccess;
267 if (!access) {
268 // create default access controls for the new item
269 CssmDbAttributeData *data = mDbAttributes->find(Schema::attributeInfo(kSecLabelItemAttr));
270 string printName = data ? CssmData::overlay(data->Value[0]).toString() : "keychain item";
271 access = new Access(printName);
272
273 // special case for "iTools" password - allow anyone to decrypt the item
274 if (recordType == CSSM_DL_DB_RECORD_GENERIC_PASSWORD)
275 {
276 CssmDbAttributeData *data = mDbAttributes->find(Schema::attributeInfo(kSecServiceItemAttr));
277 if (data && data->Value[0].Length == 6 && !memcmp("iTools", data->Value[0].Data, 6))
278 {
279 typedef vector<SecPointer<ACL> > AclSet;
280 AclSet acls;
281 access->findAclsForRight(CSSM_ACL_AUTHORIZATION_DECRYPT, acls);
282 for (AclSet::const_iterator it = acls.begin(); it != acls.end(); it++)
283 (*it)->form(ACL::allowAllForm);
284 }
285 }
286 }
287
288 // Create a new SSGroup with temporary access controls
289 Access::Maker maker;
290 ResourceControlContext prototype;
291 maker.initialOwner(prototype, nullCred);
292 SSGroup ssGroup(ssDb, &prototype);
293
294 try
295 {
296 // Insert the record using the newly created group.
297 mUniqueId = ssDb->insert(recordType, mDbAttributes.get(),
298 mData.get(), ssGroup, maker.cred());
299 }
300 catch(...)
301 {
302 ssGroup->deleteKey(nullCred);
303 throw;
304 }
305
306 // now finalize the access controls on the group
307 access->setAccess(*ssGroup, maker);
308 mAccess = NULL; // use them and lose them
309 }
310 else
311 {
312 // add the item to the (regular) db
313 mUniqueId = db->insert(recordType, mDbAttributes.get(), mData.get());
314 }
315
316 mPrimaryKey = keychain->makePrimaryKey(recordType, mUniqueId);
317 mKeychain = keychain;
318
319 // Forget our data and attributes.
320 mData.reset(NULL);
321 mDbAttributes.reset(NULL);
322
323 return mPrimaryKey;
324 }
325
326 Item
327 ItemImpl::copyTo(const Keychain &keychain, Access *newAccess)
328 {
329 Item item(*this);
330 if (newAccess)
331 item->setAccess(newAccess);
332 else
333 {
334 /* Attempt to copy the access from the current item to the newly created one. */
335 SSGroup myGroup = group();
336 if (myGroup)
337 {
338 SecPointer<Access> access = new Access(*myGroup);
339 item->setAccess(access);
340 }
341 }
342
343 keychain->add(item);
344 return item;
345 }
346
347 void
348 ItemImpl::update()
349 {
350 if (!mKeychain)
351 MacOSError::throwMe(errSecNoSuchKeychain);
352
353 // Don't update if nothing changed.
354 if (!isModified())
355 return;
356
357 CSSM_DB_RECORDTYPE aRecordType = recordType();
358 KeychainSchema schema = mKeychain->keychainSchema();
359
360 // Update the modification date on the item if there is a mod date attribute.
361 if (schema->hasAttribute(aRecordType, kSecModDateItemAttr))
362 {
363 SInt64 date;
364 GetCurrentMacLongDateTime(date);
365 setAttribute(schema->attributeInfoFor(aRecordType, kSecModDateItemAttr), date);
366 }
367
368 // Make sure that we have mUniqueId
369 dbUniqueRecord();
370 Db db(mUniqueId->database());
371 if (useSecureStorage(db))
372 {
373 // Add the item to the secure storage db
374 SSDbUniqueRecord ssUniqueId(safe_cast<SSDbUniqueRecordImpl *>
375 (&(*mUniqueId)));
376
377 // @@@ Share this instance
378 const AccessCredentials *autoPrompt = globals().credentials();
379
380
381 // Only call this is user interaction is enabled.
382 ssUniqueId->modify(aRecordType,
383 mDbAttributes.get(),
384 mData.get(),
385 CSSM_DB_MODIFY_ATTRIBUTE_REPLACE,
386 autoPrompt);
387 }
388 else
389 {
390 mUniqueId->modify(aRecordType,
391 mDbAttributes.get(),
392 mData.get(),
393 CSSM_DB_MODIFY_ATTRIBUTE_REPLACE);
394 }
395
396 PrimaryKey oldPK = mPrimaryKey;
397 mPrimaryKey = mKeychain->makePrimaryKey(aRecordType, mUniqueId);
398
399 // Forget our data and attributes.
400 mData.reset(NULL);
401 mDbAttributes.reset(NULL);
402
403 // Let the Keychain update what it needs to.
404 mKeychain->didUpdate(this, oldPK, mPrimaryKey);
405 }
406
407 void
408 ItemImpl::getClass(SecKeychainAttribute &attr, UInt32 *actualLength)
409 {
410 if (actualLength)
411 *actualLength = sizeof(SecItemClass);
412
413 if (attr.length < sizeof(SecItemClass))
414 MacOSError::throwMe(errSecBufferTooSmall);
415
416 SecItemClass aClass = Schema::itemClassFor(recordType());
417 memcpy(attr.data, &aClass, sizeof(SecItemClass));
418 }
419
420 void
421 ItemImpl::setAttribute(SecKeychainAttribute& attr)
422 {
423 setAttribute(Schema::attributeInfo(attr.tag), CssmData(attr.data, attr.length));
424 }
425
426 CSSM_DB_RECORDTYPE
427 ItemImpl::recordType() const
428 {
429 if (mDbAttributes.get())
430 return mDbAttributes->recordType();
431
432 return mPrimaryKey->recordType();
433 }
434
435 const DbAttributes *
436 ItemImpl::modifiedAttributes() const
437 {
438 return mDbAttributes.get();
439 }
440
441 const CssmData *
442 ItemImpl::modifiedData() const
443 {
444 return mData.get();
445 }
446
447 void
448 ItemImpl::setData(UInt32 length,const void *data)
449 {
450 mData.reset(new CssmDataContainer(data, length));
451 }
452
453 void
454 ItemImpl::setAccess(Access *newAccess)
455 {
456 mAccess = newAccess;
457 }
458
459 CssmClient::DbUniqueRecord
460 ItemImpl::dbUniqueRecord()
461 {
462 if (!isPersistant()) // is there no database attached?
463 {
464 MacOSError::throwMe(errSecNotAvailable);
465 }
466
467 if (!mUniqueId)
468 {
469 DbCursor cursor(mPrimaryKey->createCursor(mKeychain));
470 if (!cursor->next(NULL, NULL, mUniqueId))
471 MacOSError::throwMe(errSecInvalidItemRef);
472 }
473
474 return mUniqueId;
475 }
476
477 PrimaryKey
478 ItemImpl::primaryKey() const
479 {
480 return mPrimaryKey;
481 }
482
483 bool
484 ItemImpl::isPersistant() const
485 {
486 return mKeychain;
487 }
488
489 bool
490 ItemImpl::isModified() const
491 {
492 return mData.get() || mDbAttributes.get();
493 }
494
495 Keychain
496 ItemImpl::keychain() const
497 {
498 return mKeychain;
499 }
500
501 bool
502 ItemImpl::operator <(const ItemImpl &other) const
503 {
504
505 if (*mData)
506 {
507 // Pointer compare
508 return this < &other;
509 }
510
511 // XXX Deal with not having a mPrimaryKey
512 return *mPrimaryKey < *(other.mPrimaryKey);
513
514 }
515
516 void
517 ItemImpl::setAttribute(const CssmDbAttributeInfo &info, const CssmPolyData &data)
518 {
519 if (!mDbAttributes.get())
520 {
521 mDbAttributes.reset(new DbAttributes());
522 mDbAttributes->recordType(mPrimaryKey->recordType());
523 }
524
525 uint32 length = data.Length;
526 const void *buf = reinterpret_cast<const void *>(data.Data);
527 uint8 timeString[16];
528
529 // XXX This code is duplicated in KCCursorImpl::KCCursorImpl()
530 // Convert a 4 or 8 byte TIME_DATE to a CSSM_DB_ATTRIBUTE_FORMAT_TIME_DATE
531 // style attribute value.
532 if (info.format() == CSSM_DB_ATTRIBUTE_FORMAT_TIME_DATE)
533 {
534 if (length == sizeof(UInt32))
535 {
536 MacSecondsToTimeString(*reinterpret_cast<const UInt32 *>(buf), 16, &timeString);
537 buf = &timeString;
538 length = 16;
539 }
540 else if (length == sizeof(SInt64))
541 {
542 MacLongDateTimeToTimeString(*reinterpret_cast<const SInt64 *>(buf), 16, &timeString);
543 buf = &timeString;
544 length = 16;
545 }
546 }
547
548 mDbAttributes->add(info, CssmData(const_cast<void*>(buf), length));
549 }
550
551 void
552 ItemImpl::modifyContent(const SecKeychainAttributeList *attrList, UInt32 dataLength, const void *inData)
553 {
554 if (!mDbAttributes.get())
555 {
556 mDbAttributes.reset(new DbAttributes());
557 mDbAttributes->recordType(mPrimaryKey->recordType());
558 }
559
560 if(attrList) // optional
561 {
562 for(UInt32 ix=0; ix < attrList->count; ix++)
563 {
564 mDbAttributes->add(Schema::attributeInfo(attrList->attr[ix].tag), CssmData(attrList->attr[ix].data, attrList->attr[ix].length));
565 }
566 }
567
568 if(inData)
569 {
570 mData.reset(new CssmDataContainer(inData, dataLength));
571 }
572
573 update();
574 }
575
576 void
577 ItemImpl::getContent(SecItemClass *itemClass, SecKeychainAttributeList *attrList, UInt32 *length, void **outData)
578 {
579
580 // If the data hasn't been set we can't return it.
581 if (!mKeychain && outData)
582 {
583 CssmData *data = mData.get();
584 if (!data)
585 MacOSError::throwMe(errSecDataNotAvailable);
586 }
587 // TODO: need to check and make sure attrs are valid and handle error condition
588
589
590 if(itemClass)
591 *itemClass = Schema::itemClassFor(recordType());
592
593 bool getDataFromDatabase = mKeychain && mPrimaryKey;
594
595 if (getDataFromDatabase) // are we attached to a database?
596
597 {
598 dbUniqueRecord();
599 }
600
601 // get the number of attributes requested by the caller
602 UInt32 attrCount = attrList ? attrList->count : 0;
603
604 if (getDataFromDatabase)
605 {
606 // make a DBAttributes structure and populate it
607 DbAttributes dbAttributes(mUniqueId->database(), attrCount);
608 for (UInt32 ix = 0; ix < attrCount; ++ix)
609 {
610 dbAttributes.add(Schema::attributeInfo(attrList->attr[ix].tag));
611 }
612
613 // request the data from the database (since we are a reference "item" and the data is really stored there)
614 CssmDataContainer itemData;
615 if (getDataFromDatabase)
616 {
617 getContent(&dbAttributes, outData ? &itemData : NULL);
618 }
619
620 // retrieve the data from result
621 for (UInt32 ix = 0; ix < attrCount; ++ix)
622 {
623 if (dbAttributes.at(ix).NumberOfValues > 0)
624 {
625 attrList->attr[ix].data = dbAttributes.at(ix).Value[0].Data;
626 attrList->attr[ix].length = dbAttributes.at(ix).Value[0].Length;
627
628 // We don't want the data released, it is up the client
629 dbAttributes.at(ix).Value[0].Data = NULL;
630 dbAttributes.at(ix).Value[0].Length = 0;
631 }
632 else
633 {
634 attrList->attr[ix].data = NULL;
635 attrList->attr[ix].length = 0;
636 }
637 }
638
639 // clean up
640 if (outData)
641 {
642 *outData=itemData.data();
643 itemData.Data=NULL;
644
645 if (length) *length=itemData.length();
646 itemData.Length=0;
647 }
648 }
649 else if (attrList != NULL)
650 {
651 getLocalContent (*attrList);
652 if (outData) *outData = NULL;
653 if (length) *length = 0;
654 }
655
656 // inform anyone interested that we are doing this
657 #if SENDACCESSNOTIFICATIONS
658 if (outData)
659 {
660 secdebug("kcnotify", "ItemImpl::getContent(0x%x, 0x%x, 0x%x, 0x%x) retrieved content",
661 (unsigned int)itemClass, (unsigned int)attrList, (unsigned int)length, (unsigned int)outData);
662
663 KCEventNotifier::PostKeychainEvent(kSecDataAccessEvent, mKeychain, this);
664 }
665 #endif
666 }
667
668 void
669 ItemImpl::freeContent(SecKeychainAttributeList *attrList, void *data)
670 {
671 CssmAllocator &allocator = CssmAllocator::standard(); // @@@ This might not match the one used originally
672 if (data)
673 allocator.free(data);
674
675 UInt32 attrCount = attrList ? attrList->count : 0;
676 for (UInt32 ix = 0; ix < attrCount; ++ix)
677 {
678 allocator.free(attrList->attr[ix].data);
679 attrList->attr[ix].data = NULL;
680 }
681 }
682
683 void
684 ItemImpl::modifyAttributesAndData(const SecKeychainAttributeList *attrList, UInt32 dataLength, const void *inData)
685 {
686 if (!mKeychain)
687 MacOSError::throwMe(errSecNoSuchKeychain);
688
689 if (!mDbAttributes.get())
690 {
691 mDbAttributes.reset(new DbAttributes());
692 mDbAttributes->recordType(mPrimaryKey->recordType());
693 }
694
695 CSSM_DB_RECORDTYPE recordType = mDbAttributes->recordType();
696 UInt32 attrCount = attrList ? attrList->count : 0;
697 for (UInt32 ix = 0; ix < attrCount; ix++)
698 {
699 CssmDbAttributeInfo info=mKeychain->attributeInfoFor(recordType, attrList->attr[ix].tag);
700
701 if (attrList->attr[ix].length || info.AttributeFormat==CSSM_DB_ATTRIBUTE_FORMAT_STRING || info.AttributeFormat==CSSM_DB_ATTRIBUTE_FORMAT_BLOB
702 || info.AttributeFormat==CSSM_DB_ATTRIBUTE_FORMAT_STRING || info.AttributeFormat==CSSM_DB_ATTRIBUTE_FORMAT_BIG_NUM
703 || info.AttributeFormat==CSSM_DB_ATTRIBUTE_FORMAT_MULTI_UINT32)
704 mDbAttributes->add(info, CssmData(attrList->attr[ix].data, attrList->attr[ix].length));
705 else
706 mDbAttributes->add(info);
707 }
708
709 if(inData)
710 {
711 mData.reset(new CssmDataContainer(inData, dataLength));
712 }
713
714 update();
715 }
716
717 void
718 ItemImpl::getAttributesAndData(SecKeychainAttributeInfo *info, SecItemClass *itemClass, SecKeychainAttributeList **attrList, UInt32 *length, void **outData)
719 {
720 // If the data hasn't been set we can't return it.
721 if (!mKeychain && outData)
722 {
723 CssmData *data = mData.get();
724 if (!data)
725 MacOSError::throwMe(errSecDataNotAvailable);
726 }
727 // TODO: need to check and make sure attrs are valid and handle error condition
728
729
730 if(itemClass)
731 *itemClass = Schema::itemClassFor(recordType());
732
733 dbUniqueRecord();
734
735 UInt32 attrCount = info ? info->count : 0;
736 DbAttributes dbAttributes(mUniqueId->database(), attrCount);
737 for (UInt32 ix = 0; ix < attrCount; ix++)
738 {
739 CssmDbAttributeData &record = dbAttributes.add();
740 record.Info.AttributeNameFormat=CSSM_DB_ATTRIBUTE_NAME_AS_INTEGER;
741 record.Info.Label.AttributeID=info->tag[ix];
742 }
743
744 CssmDataContainer itemData;
745 getContent(&dbAttributes, outData ? &itemData : NULL);
746
747 if(info && attrList)
748 {
749 SecKeychainAttributeList *theList=reinterpret_cast<SecKeychainAttributeList *>(malloc(sizeof(SecKeychainAttributeList)));
750 SecKeychainAttribute *attr=reinterpret_cast<SecKeychainAttribute *>(malloc(sizeof(SecKeychainAttribute)*attrCount));
751 theList->count=attrCount;
752 theList->attr=attr;
753
754 for (UInt32 ix = 0; ix < attrCount; ++ix)
755 {
756 attr[ix].tag=info->tag[ix];
757
758 if (dbAttributes.at(ix).NumberOfValues > 0)
759 {
760 attr[ix].data = dbAttributes.at(ix).Value[0].Data;
761 attr[ix].length = dbAttributes.at(ix).Value[0].Length;
762
763 // We don't want the data released, it is up the client
764 dbAttributes.at(ix).Value[0].Data = NULL;
765 dbAttributes.at(ix).Value[0].Length = 0;
766 }
767 else
768 {
769 attr[ix].data = NULL;
770 attr[ix].length = 0;
771 }
772 }
773 *attrList=theList;
774 }
775
776 if (outData)
777 {
778 *outData=itemData.data();
779 itemData.Data=NULL;
780
781 if (length) *length=itemData.length();
782 itemData.Length=0;
783
784 #if SENDACCESSNOTIFICATIONS
785 secdebug("kcnotify", "ItemImpl::getAttributesAndData(0x%x, 0x%x, 0x%x, 0x%x, 0x%x) retrieved data",
786 (unsigned int)info, (unsigned int)itemClass, (unsigned int)attrList, (unsigned int)length, (unsigned int)outData);
787
788 KCEventNotifier::PostKeychainEvent(kSecDataAccessEvent, mKeychain, this);
789 #endif
790 }
791
792 }
793
794 void
795 ItemImpl::freeAttributesAndData(SecKeychainAttributeList *attrList, void *data)
796 {
797 CssmAllocator &allocator = CssmAllocator::standard(); // @@@ This might not match the one used originally
798
799 if (data)
800 allocator.free(data);
801
802 if(attrList)
803 {
804 for (UInt32 ix = 0; ix < attrList->count; ++ix)
805 {
806 allocator.free(attrList->attr[ix].data);
807 }
808 free(attrList->attr);
809 free(attrList);
810 }
811 }
812
813 void
814 ItemImpl::getAttribute(SecKeychainAttribute& attr, UInt32 *actualLength)
815 {
816 if (attr.tag == kSecClassItemAttr)
817 return getClass(attr, actualLength);
818
819 if (mDbAttributes.get())
820 {
821 CssmDbAttributeData *data = mDbAttributes->find(Schema::attributeInfo(attr.tag));
822 if (data)
823 {
824 getAttributeFrom(data, attr, actualLength);
825 return;
826 }
827 }
828
829 if (!mKeychain)
830 MacOSError::throwMe(errSecNoSuchAttr);
831
832 dbUniqueRecord();
833 DbAttributes dbAttributes(mUniqueId->database(), 1);
834 dbAttributes.add(Schema::attributeInfo(attr.tag));
835 mUniqueId->get(&dbAttributes, NULL);
836 getAttributeFrom(&dbAttributes.at(0), attr, actualLength);
837 }
838
839 void
840 ItemImpl::getAttributeFrom(CssmDbAttributeData *data, SecKeychainAttribute &attr, UInt32 *actualLength)
841 {
842 static const uint32 zero = 0;
843 uint32 length;
844 const void *buf = NULL;
845
846 // Temporary storage for buf.
847 SInt64 macLDT;
848 UInt32 macSeconds;
849 sint16 svalue16;
850 uint16 uvalue16;
851 sint8 svalue8;
852 uint8 uvalue8;
853
854 if (!data)
855 length = 0;
856 else if (data->size() < 1) // Attribute has no values.
857 {
858 if (data->format() == CSSM_DB_ATTRIBUTE_FORMAT_SINT32
859 || data->format() == CSSM_DB_ATTRIBUTE_FORMAT_UINT32)
860 {
861 length = sizeof(zero);
862 buf = &zero;
863 }
864 else if (CSSM_DB_ATTRIBUTE_FORMAT_TIME_DATE)
865 length = 0; // Should we throw here?
866 else // All other formats
867 length = 0;
868 }
869 else // Get the first value
870 {
871 length = data->Value[0].Length;
872 buf = data->Value[0].Data;
873
874 if (data->format() == CSSM_DB_ATTRIBUTE_FORMAT_SINT32)
875 {
876 if (attr.length == sizeof(sint8))
877 {
878 length = attr.length;
879 svalue8 = sint8(*reinterpret_cast<const sint32 *>(buf));
880 buf = &svalue8;
881 }
882 else if (attr.length == sizeof(sint16))
883 {
884 length = attr.length;
885 svalue16 = sint16(*reinterpret_cast<const sint32 *>(buf));
886 buf = &svalue16;
887 }
888 }
889 else if (data->format() == CSSM_DB_ATTRIBUTE_FORMAT_UINT32)
890 {
891 if (attr.length == sizeof(uint8))
892 {
893 length = attr.length;
894 uvalue8 = uint8(*reinterpret_cast<const uint32 *>(buf));
895 buf = &uvalue8;
896 }
897 else if (attr.length == sizeof(uint16))
898 {
899 length = attr.length;
900 uvalue16 = uint16(*reinterpret_cast<const uint32 *>(buf));
901 buf = &uvalue16;
902 }
903 }
904 else if (data->format() == CSSM_DB_ATTRIBUTE_FORMAT_TIME_DATE)
905 {
906 if (attr.length == sizeof(UInt32))
907 {
908 TimeStringToMacSeconds(data->Value[0], macSeconds);
909 buf = &macSeconds;
910 length = attr.length;
911 }
912 else if (attr.length == sizeof(SInt64))
913 {
914 TimeStringToMacLongDateTime(data->Value[0], macLDT);
915 buf = &macLDT;
916 length = attr.length;
917 }
918 }
919 }
920
921 if (actualLength)
922 *actualLength = length;
923
924 if (length)
925 {
926 if (attr.length < length)
927 MacOSError::throwMe(errSecBufferTooSmall);
928
929 memcpy(attr.data, buf, length);
930 }
931 }
932
933 void
934 ItemImpl::getData(CssmDataContainer& outData)
935 {
936 if (!mKeychain)
937 {
938 CssmData *data = mData.get();
939 // If the data hasn't been set we can't return it.
940 if (!data)
941 MacOSError::throwMe(errSecDataNotAvailable);
942
943 outData = *data;
944 return;
945 }
946
947 getContent(NULL, &outData);
948
949 #if SENDACCESSNOTIFICATIONS
950 secdebug("kcnotify", "ItemImpl::getData retrieved data");
951
952 //%%%<might> be done elsewhere, but here is good for now
953 KCEventNotifier::PostKeychainEvent(kSecDataAccessEvent, mKeychain, this);
954 #endif
955 }
956
957 SSGroup
958 ItemImpl::group()
959 {
960 SSGroup group;
961 if (!!mUniqueId)
962 {
963 Db db(mKeychain->database());
964 if (useSecureStorage(db))
965 {
966 group = safer_cast<SSDbUniqueRecordImpl &>(*mUniqueId).group();
967 }
968 }
969
970 return group;
971 }
972
973 void ItemImpl::getLocalContent(SecKeychainAttributeList &attributeList)
974 {
975 CssmAllocator &allocator = CssmAllocator::standard(); // @@@ This might not match the one used originally
976
977 // pull attributes out of a "floating" item, i.e. one that isn't attached to a database
978 unsigned int i;
979 for (i = 0; i < attributeList.count; ++i)
980 {
981 // get the size of the attribute
982 UInt32 actualLength;
983 SecKeychainAttribute attribute;
984 attribute.tag = attributeList.attr[i].tag;
985 attribute.length = 0;
986 attribute.data = NULL;
987 getAttribute (attribute, &actualLength);
988
989 // if we didn't get the actual length, mark zeros.
990 if (actualLength == 0)
991 {
992 attributeList.attr[i].length = 0;
993 attributeList.attr[i].data = NULL;
994 }
995 else
996 {
997 // make room in the item data
998 attributeList.attr[i].length = actualLength;
999 attributeList.attr[i].data = allocator.malloc(actualLength);
1000 getAttribute(attributeList.attr[i], &actualLength);
1001 }
1002 }
1003 }
1004
1005 void
1006 ItemImpl::getContent(DbAttributes *dbAttributes, CssmDataContainer *itemData)
1007 {
1008 // Make sure mUniqueId is set.
1009 dbUniqueRecord();
1010 if (itemData)
1011 {
1012 Db db(mUniqueId->database());
1013 if (useSecureStorage(db))
1014 {
1015 SSDbUniqueRecord ssUniqueId(safe_cast<SSDbUniqueRecordImpl *>(&(*mUniqueId)));
1016 const AccessCredentials *autoPrompt = globals().credentials();
1017 ssUniqueId->get(dbAttributes, itemData, autoPrompt);
1018 return;
1019 }
1020 }
1021
1022 mUniqueId->get(dbAttributes, itemData);
1023 }
1024
1025 bool
1026 ItemImpl::useSecureStorage(const Db &db)
1027 {
1028 switch (recordType())
1029 {
1030 case CSSM_DL_DB_RECORD_GENERIC_PASSWORD:
1031 case CSSM_DL_DB_RECORD_INTERNET_PASSWORD:
1032 case CSSM_DL_DB_RECORD_APPLESHARE_PASSWORD:
1033 if (db->dl()->subserviceMask() & CSSM_SERVICE_CSP)
1034 return true;
1035 break;
1036 default:
1037 break;
1038 }
1039 return false;
1040 }
1041
1042
1043 //
1044 // Item -- This class is here to magically create the right subclass of ItemImpl
1045 // when constructing new items.
1046 //
1047 Item::Item()
1048 {
1049 }
1050
1051 Item::Item(ItemImpl *impl) : SecPointer<ItemImpl>(impl)
1052 {
1053 }
1054
1055 Item::Item(SecItemClass itemClass, OSType itemCreator, UInt32 length, const void* data)
1056 {
1057 if (itemClass == CSSM_DL_DB_RECORD_X509_CERTIFICATE
1058 || itemClass == CSSM_DL_DB_RECORD_PUBLIC_KEY
1059 || itemClass == CSSM_DL_DB_RECORD_PRIVATE_KEY
1060 || itemClass == CSSM_DL_DB_RECORD_SYMMETRIC_KEY)
1061 MacOSError::throwMe(errSecNoSuchClass); /* @@@ errSecInvalidClass */
1062
1063 *this = new ItemImpl(itemClass, itemCreator, length, data);
1064 }
1065
1066 Item::Item(SecItemClass itemClass, SecKeychainAttributeList *attrList, UInt32 length, const void* data)
1067 {
1068 if (itemClass == CSSM_DL_DB_RECORD_X509_CERTIFICATE
1069 || itemClass == CSSM_DL_DB_RECORD_PUBLIC_KEY
1070 || itemClass == CSSM_DL_DB_RECORD_PRIVATE_KEY
1071 || itemClass == CSSM_DL_DB_RECORD_SYMMETRIC_KEY)
1072 MacOSError::throwMe(errSecNoSuchClass); /* @@@ errSecInvalidClass */
1073
1074 *this = new ItemImpl(itemClass, attrList, length, data);
1075 }
1076
1077 Item::Item(const Keychain &keychain, const PrimaryKey &primaryKey, const CssmClient::DbUniqueRecord &uniqueId)
1078 : SecPointer<ItemImpl>(
1079 primaryKey->recordType() == CSSM_DL_DB_RECORD_X509_CERTIFICATE
1080 ? new Certificate(keychain, primaryKey, uniqueId)
1081 : (primaryKey->recordType() == CSSM_DL_DB_RECORD_PUBLIC_KEY
1082 || primaryKey->recordType() == CSSM_DL_DB_RECORD_PRIVATE_KEY
1083 || primaryKey->recordType() == CSSM_DL_DB_RECORD_SYMMETRIC_KEY)
1084 ? new KeyItem(keychain, primaryKey, uniqueId)
1085 : new ItemImpl(keychain, primaryKey, uniqueId))
1086 {
1087 }
1088
1089 Item::Item(const Keychain &keychain, const PrimaryKey &primaryKey)
1090 : SecPointer<ItemImpl>(
1091 primaryKey->recordType() == CSSM_DL_DB_RECORD_X509_CERTIFICATE
1092 ? new Certificate(keychain, primaryKey)
1093 : (primaryKey->recordType() == CSSM_DL_DB_RECORD_PUBLIC_KEY
1094 || primaryKey->recordType() == CSSM_DL_DB_RECORD_PRIVATE_KEY
1095 || primaryKey->recordType() == CSSM_DL_DB_RECORD_SYMMETRIC_KEY)
1096 ? new KeyItem(keychain, primaryKey)
1097 : new ItemImpl(keychain, primaryKey))
1098 {
1099 }
1100
1101 Item::Item(ItemImpl &item)
1102 : SecPointer<ItemImpl>(
1103 item.recordType() == CSSM_DL_DB_RECORD_X509_CERTIFICATE
1104 ? new Certificate(safer_cast<Certificate &>(item))
1105 : (item.recordType() == CSSM_DL_DB_RECORD_PUBLIC_KEY
1106 || item.recordType() == CSSM_DL_DB_RECORD_PRIVATE_KEY
1107 || item.recordType() == CSSM_DL_DB_RECORD_SYMMETRIC_KEY)
1108 ? new KeyItem(safer_cast<KeyItem &>(item))
1109 : new ItemImpl(item))
1110 {
1111 }