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