]> git.saurik.com Git - apple/security.git/blob - Keychain/Item.cpp
bcb557a5a30c685230283f46f7cc9e4956df9a6e
[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 SENDACCESSNOTIFICATIONS
641 if (outData)
642 {
643 KCEventNotifier::PostKeychainEvent(kSecDataAccessEvent, mKeychain, this);
644 }
645 #endif
646 }
647
648 void
649 ItemImpl::freeContent(SecKeychainAttributeList *attrList, void *data)
650 {
651 CssmAllocator &allocator = CssmAllocator::standard(); // @@@ This might not match the one used originally
652 if (data)
653 allocator.free(data);
654
655 UInt32 attrCount = attrList ? attrList->count : 0;
656 for (UInt32 ix = 0; ix < attrCount; ++ix)
657 {
658 allocator.free(attrList->attr[ix].data);
659 attrList->attr[ix].data = NULL;
660 }
661 }
662
663 void
664 ItemImpl::modifyAttributesAndData(const SecKeychainAttributeList *attrList, UInt32 dataLength, const void *inData)
665 {
666 if (!mKeychain)
667 MacOSError::throwMe(errSecNoSuchKeychain);
668
669 if (!mDbAttributes.get())
670 {
671 mDbAttributes.reset(new DbAttributes());
672 mDbAttributes->recordType(mPrimaryKey->recordType());
673 }
674
675 CSSM_DB_RECORDTYPE recordType = mDbAttributes->recordType();
676 UInt32 attrCount = attrList ? attrList->count : 0;
677 for (UInt32 ix = 0; ix < attrCount; ix++)
678 {
679 CssmDbAttributeInfo info=mKeychain->attributeInfoFor(recordType, attrList->attr[ix].tag);
680
681 if (attrList->attr[ix].length || info.AttributeFormat==CSSM_DB_ATTRIBUTE_FORMAT_STRING || info.AttributeFormat==CSSM_DB_ATTRIBUTE_FORMAT_BLOB
682 || info.AttributeFormat==CSSM_DB_ATTRIBUTE_FORMAT_STRING || info.AttributeFormat==CSSM_DB_ATTRIBUTE_FORMAT_BIG_NUM
683 || info.AttributeFormat==CSSM_DB_ATTRIBUTE_FORMAT_MULTI_UINT32)
684 mDbAttributes->add(info, CssmData(attrList->attr[ix].data, attrList->attr[ix].length));
685 else
686 mDbAttributes->add(info);
687 }
688
689 if(inData)
690 {
691 mData.reset(new CssmDataContainer(inData, dataLength));
692 }
693
694 update();
695 }
696
697 void
698 ItemImpl::getAttributesAndData(SecKeychainAttributeInfo *info, SecItemClass *itemClass, SecKeychainAttributeList **attrList, UInt32 *length, void **outData)
699 {
700 // If the data hasn't been set we can't return it.
701 if (!mKeychain && outData)
702 {
703 CssmData *data = mData.get();
704 if (!data)
705 MacOSError::throwMe(errSecDataNotAvailable);
706 }
707 // TODO: need to check and make sure attrs are valid and handle error condition
708
709
710 if(itemClass)
711 *itemClass = Schema::itemClassFor(recordType());
712
713 dbUniqueRecord();
714
715 UInt32 attrCount = info ? info->count : 0;
716 DbAttributes dbAttributes(mUniqueId->database(), attrCount);
717 for (UInt32 ix = 0; ix < attrCount; ix++)
718 {
719 CssmDbAttributeData &record = dbAttributes.add();
720 record.Info.AttributeNameFormat=CSSM_DB_ATTRIBUTE_NAME_AS_INTEGER;
721 record.Info.Label.AttributeID=info->tag[ix];
722 }
723
724 CssmDataContainer itemData;
725 getContent(&dbAttributes, outData ? &itemData : NULL);
726
727 if(info && attrList)
728 {
729 SecKeychainAttributeList *theList=reinterpret_cast<SecKeychainAttributeList *>(malloc(sizeof(SecKeychainAttributeList)));
730 SecKeychainAttribute *attr=reinterpret_cast<SecKeychainAttribute *>(malloc(sizeof(SecKeychainAttribute)*attrCount));
731 theList->count=attrCount;
732 theList->attr=attr;
733
734 for (UInt32 ix = 0; ix < attrCount; ++ix)
735 {
736 attr[ix].tag=info->tag[ix];
737
738 if (dbAttributes.at(ix).NumberOfValues > 0)
739 {
740 attr[ix].data = dbAttributes.at(ix).Value[0].Data;
741 attr[ix].length = dbAttributes.at(ix).Value[0].Length;
742
743 // We don't want the data released, it is up the client
744 dbAttributes.at(ix).Value[0].Data = NULL;
745 dbAttributes.at(ix).Value[0].Length = 0;
746 }
747 else
748 {
749 attr[ix].data = NULL;
750 attr[ix].length = 0;
751 }
752 }
753 *attrList=theList;
754 }
755
756 if (outData)
757 {
758 *outData=itemData.data();
759 itemData.Data=NULL;
760
761 *length=itemData.length();
762 itemData.Length=0;
763
764 #if SENDACCESSNOTIFICATIONS
765 KCEventNotifier::PostKeychainEvent(kSecDataAccessEvent, mKeychain, this);
766 #endif
767 }
768
769 }
770
771 void
772 ItemImpl::freeAttributesAndData(SecKeychainAttributeList *attrList, void *data)
773 {
774 CssmAllocator &allocator = CssmAllocator::standard(); // @@@ This might not match the one used originally
775
776 if (data)
777 allocator.free(data);
778
779 if(attrList)
780 {
781 for (UInt32 ix = 0; ix < attrList->count; ++ix)
782 {
783 allocator.free(attrList->attr[ix].data);
784 }
785 free(attrList->attr);
786 free(attrList);
787 }
788 }
789
790 void
791 ItemImpl::getAttribute(SecKeychainAttribute& attr, UInt32 *actualLength)
792 {
793 if (attr.tag == kSecClassItemAttr)
794 return getClass(attr, actualLength);
795
796 if (mDbAttributes.get())
797 {
798 CssmDbAttributeData *data = mDbAttributes->find(Schema::attributeInfo(attr.tag));
799 if (data)
800 {
801 getAttributeFrom(data, attr, actualLength);
802 return;
803 }
804 }
805
806 if (!mKeychain)
807 MacOSError::throwMe(errSecNoSuchAttr);
808
809 dbUniqueRecord();
810 DbAttributes dbAttributes(mUniqueId->database(), 1);
811 dbAttributes.add(Schema::attributeInfo(attr.tag));
812 mUniqueId->get(&dbAttributes, NULL);
813 getAttributeFrom(&dbAttributes.at(0), attr, actualLength);
814 }
815
816 void
817 ItemImpl::getAttributeFrom(CssmDbAttributeData *data, SecKeychainAttribute &attr, UInt32 *actualLength)
818 {
819 static const uint32 zero = 0;
820 uint32 length;
821 const void *buf;
822
823 // Temporary storage for buf.
824 SInt64 macLDT;
825 UInt32 macSeconds;
826 sint16 svalue16;
827 uint16 uvalue16;
828 sint8 svalue8;
829 uint8 uvalue8;
830
831 if (!data)
832 length = 0;
833 else if (data->size() < 1) // Attribute has no values.
834 {
835 if (data->format() == CSSM_DB_ATTRIBUTE_FORMAT_SINT32
836 || data->format() == CSSM_DB_ATTRIBUTE_FORMAT_UINT32)
837 {
838 length = sizeof(zero);
839 buf = &zero;
840 }
841 else if (CSSM_DB_ATTRIBUTE_FORMAT_TIME_DATE)
842 length = 0; // Should we throw here?
843 else // All other formats
844 length = 0;
845 }
846 else // Get the first value
847 {
848 length = data->Value[0].Length;
849 buf = data->Value[0].Data;
850
851 if (data->format() == CSSM_DB_ATTRIBUTE_FORMAT_SINT32)
852 {
853 if (attr.length == sizeof(sint8))
854 {
855 length = attr.length;
856 svalue8 = sint8(*reinterpret_cast<const sint32 *>(buf));
857 buf = &svalue8;
858 }
859 else if (attr.length == sizeof(sint16))
860 {
861 length = attr.length;
862 svalue16 = sint16(*reinterpret_cast<const sint32 *>(buf));
863 buf = &svalue16;
864 }
865 }
866 else if (data->format() == CSSM_DB_ATTRIBUTE_FORMAT_UINT32)
867 {
868 if (attr.length == sizeof(uint8))
869 {
870 length = attr.length;
871 uvalue8 = uint8(*reinterpret_cast<const uint32 *>(buf));
872 buf = &uvalue8;
873 }
874 else if (attr.length == sizeof(uint16))
875 {
876 length = attr.length;
877 uvalue16 = uint16(*reinterpret_cast<const uint32 *>(buf));
878 buf = &uvalue16;
879 }
880 }
881 else if (data->format() == CSSM_DB_ATTRIBUTE_FORMAT_TIME_DATE)
882 {
883 if (attr.length == sizeof(UInt32))
884 {
885 TimeStringToMacSeconds(data->Value[0], macSeconds);
886 buf = &macSeconds;
887 length = attr.length;
888 }
889 else if (attr.length == sizeof(SInt64))
890 {
891 TimeStringToMacLongDateTime(data->Value[0], macLDT);
892 buf = &macLDT;
893 length = attr.length;
894 }
895 }
896 }
897
898 if (actualLength)
899 *actualLength = length;
900
901 if (length)
902 {
903 if (attr.length < length)
904 MacOSError::throwMe(errSecBufferTooSmall);
905
906 memcpy(attr.data, buf, length);
907 }
908 }
909
910 void
911 ItemImpl::getData(CssmDataContainer& outData)
912 {
913 if (!mKeychain)
914 {
915 CssmData *data = mData.get();
916 // If the data hasn't been set we can't return it.
917 if (!data)
918 MacOSError::throwMe(errSecDataNotAvailable);
919
920 outData = *data;
921 return;
922 }
923
924 getContent(NULL, &outData);
925
926 #if SENDACCESSNOTIFICATIONS
927 //%%%<might> be done elsewhere, but here is good for now
928 KCEventNotifier::PostKeychainEvent(kSecDataAccessEvent, mKeychain, this);
929 #endif
930 }
931
932 SSGroup
933 ItemImpl::group()
934 {
935 SSGroup group;
936 if (!!mUniqueId)
937 {
938 Db db(mKeychain->database());
939 if (useSecureStorage(db))
940 {
941 group = safer_cast<SSDbUniqueRecordImpl &>(*mUniqueId).group();
942 }
943 }
944
945 return group;
946 }
947
948 void ItemImpl::getLocalContent(SecKeychainAttributeList &attributeList)
949 {
950 CssmAllocator &allocator = CssmAllocator::standard(); // @@@ This might not match the one used originally
951
952 // pull attributes out of a "floating" item, i.e. one that isn't attached to a database
953 unsigned int i;
954 for (i = 0; i < attributeList.count; ++i)
955 {
956 // get the size of the attribute
957 UInt32 actualLength;
958 SecKeychainAttribute attribute;
959 attribute.tag = attributeList.attr[i].tag;
960 attribute.length = 0;
961 attribute.data = NULL;
962 getAttribute (attribute, &actualLength);
963
964 // if we didn't get the actual length, mark zeros.
965 if (actualLength == 0)
966 {
967 attributeList.attr[i].length = 0;
968 attributeList.attr[i].data = NULL;
969 }
970 else
971 {
972 // make room in the item data
973 attributeList.attr[i].length = actualLength;
974 attributeList.attr[i].data = allocator.malloc(actualLength);
975 getAttribute(attributeList.attr[i], &actualLength);
976 }
977 }
978 }
979
980 void
981 ItemImpl::getContent(DbAttributes *dbAttributes, CssmDataContainer *itemData)
982 {
983 // Make sure mUniqueId is set.
984 dbUniqueRecord();
985 if (itemData)
986 {
987 Db db(mUniqueId->database());
988 if (useSecureStorage(db))
989 {
990 SSDbUniqueRecord ssUniqueId(safe_cast<SSDbUniqueRecordImpl *>(&(*mUniqueId)));
991 const AccessCredentials *autoPrompt = globals().credentials();
992 ssUniqueId->get(dbAttributes, itemData, autoPrompt);
993 return;
994 }
995 }
996
997 mUniqueId->get(dbAttributes, itemData);
998 }
999
1000 bool
1001 ItemImpl::useSecureStorage(const Db &db)
1002 {
1003 switch (recordType())
1004 {
1005 case CSSM_DL_DB_RECORD_GENERIC_PASSWORD:
1006 case CSSM_DL_DB_RECORD_INTERNET_PASSWORD:
1007 case CSSM_DL_DB_RECORD_APPLESHARE_PASSWORD:
1008 if (db->dl()->subserviceMask() & CSSM_SERVICE_CSP)
1009 return true;
1010 break;
1011 default:
1012 break;
1013 }
1014 return false;
1015 }
1016
1017
1018 //
1019 // Item -- This class is here to magically create the right subclass of ItemImpl
1020 // when constructing new items.
1021 //
1022 Item::Item()
1023 {
1024 }
1025
1026 Item::Item(ItemImpl *impl) : RefPointer<ItemImpl>(impl)
1027 {
1028 }
1029
1030 Item::Item(SecItemClass itemClass, OSType itemCreator, UInt32 length, const void* data)
1031 {
1032 if (itemClass == CSSM_DL_DB_RECORD_X509_CERTIFICATE
1033 || itemClass == CSSM_DL_DB_RECORD_PUBLIC_KEY
1034 || itemClass == CSSM_DL_DB_RECORD_PRIVATE_KEY
1035 || itemClass == CSSM_DL_DB_RECORD_SYMMETRIC_KEY)
1036 MacOSError::throwMe(errSecNoSuchClass); /* @@@ errSecInvalidClass */
1037
1038 *this = new ItemImpl(itemClass, itemCreator, length, data);
1039 }
1040
1041 Item::Item(SecItemClass itemClass, SecKeychainAttributeList *attrList, UInt32 length, const void* data)
1042 {
1043 if (itemClass == CSSM_DL_DB_RECORD_X509_CERTIFICATE
1044 || itemClass == CSSM_DL_DB_RECORD_PUBLIC_KEY
1045 || itemClass == CSSM_DL_DB_RECORD_PRIVATE_KEY
1046 || itemClass == CSSM_DL_DB_RECORD_SYMMETRIC_KEY)
1047 MacOSError::throwMe(errSecNoSuchClass); /* @@@ errSecInvalidClass */
1048
1049 *this = new ItemImpl(itemClass, attrList, length, data);
1050 }
1051
1052 Item::Item(const Keychain &keychain, const PrimaryKey &primaryKey, const CssmClient::DbUniqueRecord &uniqueId)
1053 : RefPointer<ItemImpl>(
1054 primaryKey->recordType() == CSSM_DL_DB_RECORD_X509_CERTIFICATE
1055 ? new Certificate(keychain, primaryKey, uniqueId)
1056 : (primaryKey->recordType() == CSSM_DL_DB_RECORD_PUBLIC_KEY
1057 || primaryKey->recordType() == CSSM_DL_DB_RECORD_PRIVATE_KEY
1058 || primaryKey->recordType() == CSSM_DL_DB_RECORD_SYMMETRIC_KEY)
1059 ? new KeyItem(keychain, primaryKey, uniqueId)
1060 : new ItemImpl(keychain, primaryKey, uniqueId))
1061 {
1062 }
1063
1064 Item::Item(const Keychain &keychain, const PrimaryKey &primaryKey)
1065 : RefPointer<ItemImpl>(
1066 primaryKey->recordType() == CSSM_DL_DB_RECORD_X509_CERTIFICATE
1067 ? new Certificate(keychain, primaryKey)
1068 : (primaryKey->recordType() == CSSM_DL_DB_RECORD_PUBLIC_KEY
1069 || primaryKey->recordType() == CSSM_DL_DB_RECORD_PRIVATE_KEY
1070 || primaryKey->recordType() == CSSM_DL_DB_RECORD_SYMMETRIC_KEY)
1071 ? new KeyItem(keychain, primaryKey)
1072 : new ItemImpl(keychain, primaryKey))
1073 {
1074 }
1075
1076 Item::Item(ItemImpl &item)
1077 : RefPointer<ItemImpl>(
1078 item.recordType() == CSSM_DL_DB_RECORD_X509_CERTIFICATE
1079 ? new Certificate(safer_cast<Certificate &>(item))
1080 : (item.recordType() == CSSM_DL_DB_RECORD_PUBLIC_KEY
1081 || item.recordType() == CSSM_DL_DB_RECORD_PRIVATE_KEY
1082 || item.recordType() == CSSM_DL_DB_RECORD_SYMMETRIC_KEY)
1083 ? new KeyItem(safer_cast<KeyItem &>(item))
1084 : new ItemImpl(item))
1085 {
1086 }