]> git.saurik.com Git - apple/security.git/blob - Keychain/Item.cpp
Security-28.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 "Globals.h"
26 #include "Schema.h"
27 #include "KCEventNotifier.h"
28 #include "cssmdatetime.h"
29 #include <Security/keychainacl.h>
30 #include <Security/SecKeychainAPIPriv.h>
31 #include <Security/aclsupport.h>
32 #include <Security/osxsigning.h>
33
34 using namespace KeychainCore;
35 using namespace CSSMDateTimeUtils;
36
37 //
38 // ItemImpl
39 //
40
41 // NewItemImpl constructor
42 ItemImpl::ItemImpl(SecItemClass itemClass, OSType itemCreator, UInt32 length, const void* data)
43 : mDbAttributes(new DbAttributes())
44 {
45 if (length && data)
46 mData.reset(new CssmDataContainer(data, length));
47
48 mDbAttributes->recordType(Schema::recordTypeFor(itemClass));
49 mDbAttributes->add(Schema::attributeInfo(kSecCreatorItemAttr), itemCreator);
50
51 SInt64 date;
52 GetCurrentMacLongDateTime(date);
53 setAttribute(Schema::attributeInfo(kSecCreationDateItemAttr), date);
54 setAttribute(Schema::attributeInfo(kSecModDateItemAttr), date);
55 }
56
57 ItemImpl::ItemImpl(SecItemClass itemClass, SecKeychainAttributeList *attrList, UInt32 length, const void* data)
58 : mDbAttributes(new DbAttributes())
59 {
60 if (length && data)
61 mData.reset(new CssmDataContainer(data, length));
62
63
64 mDbAttributes->recordType(Schema::recordTypeFor(itemClass));
65
66 if(attrList)
67 {
68 for(UInt32 i=0; i < attrList->count; i++)
69 {
70 mDbAttributes->add(Schema::attributeInfo(attrList->attr[i].tag), CssmData(attrList->attr[i].data, attrList->attr[i].length));
71 }
72 }
73
74 SInt64 date;
75 GetCurrentMacLongDateTime(date);
76 setAttribute(Schema::attributeInfo(kSecCreationDateItemAttr), date);
77 setAttribute(Schema::attributeInfo(kSecModDateItemAttr), date);
78 }
79
80 // DbItemImpl constructor
81 ItemImpl::ItemImpl(const Keychain &keychain, const PrimaryKey &primaryKey, const DbUniqueRecord &uniqueId)
82 : mUniqueId(uniqueId), mKeychain(keychain), mPrimaryKey(primaryKey)
83 {
84 mKeychain->addItem(mPrimaryKey, this);
85 }
86
87 // PrimaryKey ItemImpl constructor
88 ItemImpl::ItemImpl(const Keychain &keychain, const PrimaryKey &primaryKey)
89 : mKeychain(keychain), mPrimaryKey(primaryKey)
90 {
91 mKeychain->addItem(mPrimaryKey, this);
92 }
93
94 // Constructor used when copying an item to a keychain.
95
96 ItemImpl::ItemImpl(ItemImpl &item) :
97 mData(item.modifiedData() ? NULL : new CssmDataContainer()),
98 mDbAttributes(new DbAttributes())
99 {
100 mDbAttributes->recordType(item.recordType());
101 CSSM_DB_RECORD_ATTRIBUTE_INFO *schemaAttributes = NULL;
102
103 if (item.mKeychain) {
104 // get the entire source item from its keychain. This requires figuring
105 // out the schema for the item based on its record type.
106
107 for (uint32 i = 0; i < Schema::DBInfo.NumberOfRecordTypes; i++)
108 if (item.recordType() == Schema::DBInfo.RecordAttributeNames[i].DataRecordType) {
109 schemaAttributes = &Schema::DBInfo.RecordAttributeNames[i];
110 break;
111 }
112
113 if (schemaAttributes == NULL)
114 // the source item is invalid
115 MacOSError::throwMe(errSecInvalidItemRef);
116
117 for (uint32 i = 0; i < schemaAttributes->NumberOfAttributes; i++)
118 mDbAttributes->add(schemaAttributes->AttributeInfo[i]);
119
120 item.getContent(mDbAttributes.get(), mData.get());
121 }
122
123 // @@@ We don't deal with modified attributes.
124
125 if (item.modifiedData())
126 // the copied data comes from the source item
127 mData.reset(new CssmDataContainer(item.modifiedData()->Data,
128 item.modifiedData()->Length));
129 }
130
131 ItemImpl::~ItemImpl()
132 {
133 if (mKeychain && *mPrimaryKey)
134 mKeychain->removeItem(*mPrimaryKey, this);
135 }
136
137 void
138 ItemImpl::didModify()
139 {
140 mData.reset(NULL);
141 mDbAttributes.reset(NULL);
142 }
143
144 const CSSM_DATA &
145 ItemImpl::defaultAttributeValue(const CSSM_DB_ATTRIBUTE_INFO &info)
146 {
147 static const uint32 zeroInt = 0;
148 static const double zeroDouble = 0.0;
149 static const char timeBytes[] = "20010101000000Z";
150
151 static const CSSM_DATA defaultFourBytes = { 4, (uint8 *) &zeroInt };
152 static const CSSM_DATA defaultEightBytes = { 8, (uint8 *) &zeroDouble };
153 static const CSSM_DATA defaultTime = { 16, (uint8 *) timeBytes };
154 static const CSSM_DATA defaultZeroBytes = { 0, NULL };
155
156 switch (info.AttributeFormat)
157 {
158 case CSSM_DB_ATTRIBUTE_FORMAT_SINT32:
159 case CSSM_DB_ATTRIBUTE_FORMAT_UINT32:
160 return defaultFourBytes;
161
162 case CSSM_DB_ATTRIBUTE_FORMAT_REAL:
163 return defaultEightBytes;
164
165 case CSSM_DB_ATTRIBUTE_FORMAT_TIME_DATE:
166 return defaultTime;
167
168 default:
169 return defaultZeroBytes;
170 }
171 }
172
173 PrimaryKey
174 ItemImpl::add(const Keychain &keychain)
175 {
176 // If we already have a Keychain we can't be added.
177 if (mKeychain)
178 MacOSError::throwMe(errSecDuplicateItem);
179
180 // If we don't have any attributes we can't be added.
181 // (this might occur if attempting to add the item twice, since our attributes
182 // and data are set to NULL at the end of this function.)
183 if (!mDbAttributes.get())
184 MacOSError::throwMe(errSecDuplicateItem);
185
186 // If the label (PrintName) attribute isn't specified, set a default label.
187 if (!mDbAttributes->find(Schema::attributeInfo(kSecLabelItemAttr)))
188 {
189 CssmDbAttributeData *label = NULL;
190 switch (mDbAttributes->recordType())
191 {
192 case CSSM_DL_DB_RECORD_GENERIC_PASSWORD:
193 label = mDbAttributes->find(Schema::attributeInfo(kSecServiceItemAttr));
194 break;
195
196 case CSSM_DL_DB_RECORD_APPLESHARE_PASSWORD:
197 case CSSM_DL_DB_RECORD_INTERNET_PASSWORD:
198 label = mDbAttributes->find(Schema::attributeInfo(kSecServerItemAttr));
199 // if AppleShare server name wasn't specified, try the server address
200 if (!label) label = mDbAttributes->find(Schema::attributeInfo(kSecAddressItemAttr));
201 break;
202
203 default:
204 break;
205 }
206 // if all else fails, use the account name.
207 if (!label) label = mDbAttributes->find(Schema::attributeInfo(kSecAccountItemAttr));
208
209 if (label && label->size())
210 mDbAttributes->add(Schema::attributeInfo(kSecLabelItemAttr), label->at<CssmData>(0));
211 }
212
213 // get the attributes that are part of the primary key
214 const CssmAutoDbRecordAttributeInfo &primaryKeyInfos =
215 keychain->primaryKeyInfosFor(recordType());
216
217 // make sure each primary key element has a value in the item, otherwise
218 // the database will complain. we make a set of the provided attribute infos
219 // to avoid O(N^2) behavior.
220
221 DbAttributes *attributes = mDbAttributes.get();
222 typedef set<CssmDbAttributeInfo> InfoSet;
223 InfoSet infoSet;
224
225 for (uint32 i = 0; i < attributes->size(); i++)
226 infoSet.insert(attributes->at(i).Info);
227
228 for (uint32 i = 0; i < primaryKeyInfos.size(); i++) {
229 InfoSet::const_iterator it = infoSet.find(primaryKeyInfos.at(i));
230
231 if (it == infoSet.end()) {
232 // we need to add a default value to the item attributes
233 attributes->add(primaryKeyInfos.at(i),
234 defaultAttributeValue(primaryKeyInfos.at(i)));
235 }
236 }
237
238 Db db(keychain->database());
239 if (db->dl()->subserviceMask() & CSSM_SERVICE_CSP)
240 {
241 // Add the item to the secure storage db
242 SSDb ssDb(safe_cast<SSDbImpl *>(&(*db)));
243
244 TrackingAllocator allocator(CssmAllocator::standard());
245 // @@@ Share this instance
246 KeychainAclFactory aclFactory(allocator);
247
248 AclEntryPrototype anyEncrypt(TypedList(allocator, CSSM_ACL_SUBJECT_TYPE_ANY));
249 AuthorizationGroup &anyEncryptAuthGroup = anyEncrypt.authorization();
250 CSSM_ACL_AUTHORIZATION_TAG encryptTag = CSSM_ACL_AUTHORIZATION_ENCRYPT;
251 anyEncryptAuthGroup.NumberOfAuthTags = 1;
252 anyEncryptAuthGroup.AuthTags = &encryptTag;
253
254 const AccessCredentials *nullCred = aclFactory.nullCredentials();
255
256 const ResourceControlContext credAndAclEntry
257 (anyEncrypt, const_cast<AccessCredentials *>(nullCred));
258
259 // Create a new SSGroup with owner = ANY, encrypt = ANY
260 SSGroup ssGroup(ssDb, &credAndAclEntry);
261
262 // Now we edit the acl to look like we want it to.
263
264 // Find the PrintName (which we want SecurityAgent to display when evaluating the ACL
265 CssmDbAttributeData *data = mDbAttributes->find(Schema::attributeInfo(kSecLabelItemAttr));
266 CssmData noName;
267 CssmData &printName = data ? CssmData::overlay(data->Value[0]) : noName;
268
269 // @@@ This code should use KeychainACL instead, but that class will need some changes.
270 // Defering integration with KeychainACL to Puma.
271
272 // Figure out if we should special case this to have an anyAllow in this ACL or not.
273 // Currently only generic password items with sevicename "iTools" passwords are always anyAllow.
274 bool anyAllow = false;
275 if (mDbAttributes->recordType() == CSSM_DL_DB_RECORD_GENERIC_PASSWORD)
276 {
277 CssmDbAttributeData *data = mDbAttributes->find(Schema::attributeInfo(kSecServiceItemAttr));
278 if (data && data->Value[0].Length == 6 && !memcmp("iTools", data->Value[0].Data, 6))
279 anyAllow = true;
280 }
281
282 CssmList &list = *new(allocator) CssmList();
283
284 // List is a threshold acl with 2 elements or 3 if anyAllow is true.
285 list.append(new(allocator) ListElement(CSSM_ACL_SUBJECT_TYPE_THRESHOLD));
286 list.append(new(allocator) ListElement(1));
287 list.append(new(allocator) ListElement(2 + anyAllow));
288
289 // If anyAllow is true start the threshold list with a any allow sublist.
290 if(anyAllow)
291 {
292 CssmList &anySublist = *new(allocator) CssmList();
293 anySublist.append(new(allocator) ListElement(CSSM_ACL_SUBJECT_TYPE_ANY));
294 list.append(new(allocator) ListElement(anySublist));
295 }
296
297 // Now add a sublist to trust the current application.
298 auto_ptr<CodeSigning::OSXCode> code(CodeSigning::OSXCode::main());
299 const char *path = code->canonicalPath().c_str();
300 CssmData comment(const_cast<char *>(path), strlen(path) + 1);
301 TrustedApplication app(path, comment);
302 CssmList &appSublist = *new(allocator) CssmList();
303 appSublist.append(new(allocator) ListElement(CSSM_ACL_SUBJECT_TYPE_CODE_SIGNATURE));
304 appSublist.append(new(allocator) ListElement(CSSM_ACL_CODE_SIGNATURE_OSX));
305 appSublist.append(new(allocator) ListElement(app->signature()));
306 appSublist.append(new(allocator) ListElement(app->comment()));
307 list.append(new(allocator) ListElement(appSublist));
308
309 // Finally add the keychain prompt sublist to the list so we default to asking
310 // the user for permission if all else fails.
311 CssmList &promptSublist = *new(allocator) CssmList();
312 promptSublist.append(new(allocator) ListElement(CSSM_ACL_SUBJECT_TYPE_KEYCHAIN_PROMPT));
313 promptSublist.append(new(allocator) ListElement(printName));
314 list.append(new(allocator) ListElement(promptSublist));
315
316 // The acl prototype we want to add contains the list we just made.
317 AclEntryPrototype promptDecrypt(list);
318
319 // Now make sure it only authorizes decrypt.
320 AuthorizationGroup &promptDecryptAuthGroup = promptDecrypt.authorization();
321 CSSM_ACL_AUTHORIZATION_TAG decryptTag = CSSM_ACL_AUTHORIZATION_DECRYPT;
322 promptDecryptAuthGroup.NumberOfAuthTags = 1;
323 promptDecryptAuthGroup.AuthTags = &decryptTag;
324
325 // Add an acl entry for decrypt we just made
326 AclEdit edit(promptDecrypt);
327 ssGroup->changeAcl(nullCred, edit);
328
329 try
330 {
331 // Insert the record using the newly created group.
332 mUniqueId = ssDb->insert(recordType(), mDbAttributes.get(),
333 mData.get(), ssGroup, nullCred);
334 }
335 catch(...)
336 {
337 ssGroup->deleteKey(nullCred);
338 throw;
339 }
340
341 // Change the owner so change acl = KeychainPrompt
342 AclEntryPrototype promptOwner(TypedList(allocator, CSSM_ACL_SUBJECT_TYPE_KEYCHAIN_PROMPT,
343 new(allocator) ListElement(allocator, printName)));
344 AclOwnerPrototype owner(promptOwner);
345 ssGroup->changeOwner(nullCred, owner);
346 }
347 else
348 {
349 // add the item to the (regular) db
350 mUniqueId = db->insert(recordType(), mDbAttributes.get(), mData.get());
351 }
352
353 mPrimaryKey = keychain->makePrimaryKey(recordType(), mUniqueId);
354 mKeychain = keychain;
355
356 // Forget our data and attributes.
357 mData.reset(NULL);
358 mDbAttributes.reset(NULL);
359
360 return mPrimaryKey;
361 }
362
363 Item
364 ItemImpl::copyTo(const Keychain &keychain)
365 {
366 Item item(*this);
367 keychain->add(item);
368 return item;
369 }
370
371 void
372 ItemImpl::update()
373 {
374 if (!mKeychain)
375 MacOSError::throwMe(errSecNoSuchKeychain);
376
377 // Don't update if nothing changed.
378 if (!isModified())
379 return;
380
381 // Set the modification date on the item.
382 SInt64 date;
383 GetCurrentMacLongDateTime(date);
384 setAttribute(Schema::attributeInfo(kSecModDateItemAttr), date);
385
386 // Make sure that we have mUniqueId
387 dbUniqueRecord();
388 Db db(mUniqueId->database());
389 if (db->dl()->subserviceMask() & CSSM_SERVICE_CSP)
390 {
391 // Add the item to the secure storage db
392 SSDbUniqueRecord ssUniqueId(safe_cast<SSDbUniqueRecordImpl *>
393 (&(*mUniqueId)));
394
395 // @@@ Share this instance
396 const AccessCredentials *autoPrompt = globals().credentials();
397
398
399 // Only call this is user interaction is enabled.
400 ssUniqueId->modify(recordType(),
401 mDbAttributes.get(),
402 mData.get(),
403 CSSM_DB_MODIFY_ATTRIBUTE_REPLACE,
404 autoPrompt);
405 }
406 else
407 {
408 mUniqueId->modify(recordType(),
409 mDbAttributes.get(),
410 mData.get(),
411 CSSM_DB_MODIFY_ATTRIBUTE_REPLACE);
412 }
413
414 PrimaryKey oldPK = mPrimaryKey;
415 mPrimaryKey = mKeychain->makePrimaryKey(recordType(), mUniqueId);
416
417 // Forget our data and attributes.
418 mData.reset(NULL);
419 mDbAttributes.reset(NULL);
420
421 // Let the Keychain update what it needs to.
422 mKeychain->didUpdate(this, oldPK, mPrimaryKey);
423 }
424
425 void
426 ItemImpl::getClass(SecKeychainAttribute &attr, UInt32 *actualLength)
427 {
428 if (actualLength)
429 *actualLength = sizeof(SecItemClass);
430
431 if (attr.length < sizeof(SecItemClass))
432 MacOSError::throwMe(errSecBufferTooSmall);
433
434 SecItemClass aClass = Schema::itemClassFor(recordType());
435 memcpy(attr.data, &aClass, sizeof(SecItemClass));
436 }
437
438 void
439 ItemImpl::setAttribute(SecKeychainAttribute& attr)
440 {
441 setAttribute(Schema::attributeInfo(attr.tag), CssmData(attr.data, attr.length));
442 }
443
444 CSSM_DB_RECORDTYPE
445 ItemImpl::recordType() const
446 {
447 if (mDbAttributes.get())
448 return mDbAttributes->recordType();
449
450 return mPrimaryKey->recordType();
451 }
452
453 const DbAttributes *
454 ItemImpl::modifiedAttributes() const
455 {
456 return mDbAttributes.get();
457 }
458
459 const CssmData *
460 ItemImpl::modifiedData() const
461 {
462 return mData.get();
463 }
464
465 void
466 ItemImpl::setData(UInt32 length,const void *data)
467 {
468 mData.reset(new CssmDataContainer(data, length));
469 }
470
471 CssmClient::DbUniqueRecord
472 ItemImpl::dbUniqueRecord()
473 {
474 if (!mUniqueId)
475 {
476 assert(mKeychain && mPrimaryKey);
477 DbCursor cursor(mPrimaryKey->createCursor(mKeychain));
478 if (!cursor->next(NULL, NULL, mUniqueId))
479 {
480 killRef();
481 MacOSError::throwMe(errSecInvalidItemRef);
482 }
483 }
484
485 return mUniqueId;
486 }
487
488 PrimaryKey
489 ItemImpl::primaryKey() const
490 {
491 return mPrimaryKey;
492 }
493
494 bool
495 ItemImpl::isPersistant() const
496 {
497 return mKeychain;
498 }
499
500 bool
501 ItemImpl::isModified() const
502 {
503 return mData.get() || mDbAttributes.get();
504 }
505
506 Keychain
507 ItemImpl::keychain() const
508 {
509 return mKeychain;
510 }
511
512 bool
513 ItemImpl::operator <(const ItemImpl &other) const
514 {
515
516 if (*mData)
517 {
518 // Pointer compare
519 return this < &other;
520 }
521
522 // XXX Deal with not having a mPrimaryKey
523 return *mPrimaryKey < *(other.mPrimaryKey);
524
525 }
526
527 void
528 ItemImpl::setAttribute(const CssmDbAttributeInfo &info, const CssmPolyData &data)
529 {
530 if (!mDbAttributes.get())
531 {
532 mDbAttributes.reset(new DbAttributes());
533 mDbAttributes->recordType(mPrimaryKey->recordType());
534 }
535
536 uint32 length = data.Length;
537 const void *buf = reinterpret_cast<const void *>(data.Data);
538 uint8 timeString[16];
539
540 // XXX This code is duplicated in KCCursorImpl::KCCursorImpl()
541 // Convert a 4 or 8 byte TIME_DATE to a CSSM_DB_ATTRIBUTE_FORMAT_TIME_DATE
542 // style attribute value.
543 if (info.format() == CSSM_DB_ATTRIBUTE_FORMAT_TIME_DATE)
544 {
545 if (length == sizeof(UInt32))
546 {
547 MacSecondsToTimeString(*reinterpret_cast<const UInt32 *>(buf), 16, &timeString);
548 buf = &timeString;
549 length = 16;
550 }
551 else if (length == sizeof(SInt64))
552 {
553 MacLongDateTimeToTimeString(*reinterpret_cast<const SInt64 *>(buf),
554 16, &timeString);
555 buf = &timeString;
556 length = 16;
557 }
558 }
559
560 mDbAttributes->add(info, CssmData(const_cast<void*>(buf), length));
561 }
562
563 void
564 ItemImpl::modifyContent(const SecKeychainAttributeList *attrList, UInt32 dataLength, const void *inData)
565 {
566 if (!mDbAttributes.get())
567 {
568 mDbAttributes.reset(new DbAttributes());
569 mDbAttributes->recordType(mPrimaryKey->recordType());
570 }
571
572 if(attrList) // optional
573 {
574 for(UInt32 ix=0; ix < attrList->count; ix++)
575 {
576 mDbAttributes->add(Schema::attributeInfo(attrList->attr[ix].tag), CssmData(attrList->attr[ix].data, attrList->attr[ix].length));
577 }
578 }
579
580 if(inData)
581 {
582 mData.reset(new CssmDataContainer(inData, dataLength));
583 }
584
585 update();
586 }
587
588 void
589 ItemImpl::getContent(SecItemClass *itemClass, SecKeychainAttributeList *attrList, UInt32 *length, void **outData)
590 {
591
592 // If the data hasn't been set we can't return it.
593 if (!mKeychain && outData)
594 {
595 CssmData *data = mData.get();
596 if (!data)
597 MacOSError::throwMe(errSecDataNotAvailable);
598 }
599 // TODO: need to check and make sure attrs are valid and handle error condition
600
601
602 if(itemClass)
603 *itemClass = Schema::itemClassFor(recordType());
604
605 dbUniqueRecord();
606
607 UInt32 attrCount = attrList ? attrList->count : 0;
608 DbAttributes dbAttributes(mUniqueId->database(), attrCount);
609 for (UInt32 ix = 0; ix < attrCount; ++ix)
610 dbAttributes.add(Schema::attributeInfo(attrList->attr[ix].tag));
611
612 CssmDataContainer itemData;
613 getContent(&dbAttributes, outData ? &itemData : NULL);
614
615 if (outData) KCEventNotifier::PostKeychainEvent(kSecDataAccessEvent, mKeychain, this);
616
617 for (UInt32 ix = 0; ix < attrCount; ++ix)
618 {
619 if (dbAttributes.at(ix).NumberOfValues > 0)
620 {
621 attrList->attr[ix].data = dbAttributes.at(ix).Value[0].Data;
622 attrList->attr[ix].length = dbAttributes.at(ix).Value[0].Length;
623
624 // We don't want the data released, it is up the client
625 dbAttributes.at(ix).Value[0].Data = NULL;
626 dbAttributes.at(ix).Value[0].Length = 0;
627 }
628 else
629 {
630 attrList->attr[ix].data = NULL;
631 attrList->attr[ix].length = 0;
632 }
633 }
634
635 if (outData)
636 {
637 *outData=itemData.data();
638 itemData.Data=NULL;
639
640 *length=itemData.length();
641 itemData.Length=0;
642 }
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 UInt32 attrCount = attrList ? attrList->count : 0;
674 for (UInt32 ix = 0; ix < attrCount; ix++)
675 {
676 CssmDbAttributeInfo info=mKeychain->attributeInfoForTag(attrList->attr[ix].tag);
677
678 if (attrList->attr[ix].length || info.AttributeFormat==CSSM_DB_ATTRIBUTE_FORMAT_STRING || info.AttributeFormat==CSSM_DB_ATTRIBUTE_FORMAT_BLOB
679 || info.AttributeFormat==CSSM_DB_ATTRIBUTE_FORMAT_STRING || info.AttributeFormat==CSSM_DB_ATTRIBUTE_FORMAT_BIG_NUM
680 || info.AttributeFormat==CSSM_DB_ATTRIBUTE_FORMAT_MULTI_UINT32)
681 mDbAttributes->add(info, CssmData(attrList->attr[ix].data, attrList->attr[ix].length));
682 else
683 mDbAttributes->add(info);
684 }
685
686 if(inData)
687 {
688 mData.reset(new CssmDataContainer(inData, dataLength));
689 }
690
691 update();
692 }
693
694 void
695 ItemImpl::getAttributesAndData(SecKeychainAttributeInfo *info, SecItemClass *itemClass, SecKeychainAttributeList **attrList, UInt32 *length, void **outData)
696 {
697 // If the data hasn't been set we can't return it.
698 if (!mKeychain && outData)
699 {
700 CssmData *data = mData.get();
701 if (!data)
702 MacOSError::throwMe(errSecDataNotAvailable);
703 }
704 // TODO: need to check and make sure attrs are valid and handle error condition
705
706
707 if(itemClass)
708 *itemClass = Schema::itemClassFor(recordType());
709
710 dbUniqueRecord();
711
712 UInt32 attrCount = info ? info->count : 0;
713 DbAttributes dbAttributes(mUniqueId->database(), attrCount);
714 for (UInt32 ix = 0; ix < attrCount; ix++)
715 {
716 CssmDbAttributeData &record = dbAttributes.add();
717 record.Info.AttributeNameFormat=CSSM_DB_ATTRIBUTE_NAME_AS_INTEGER;
718 record.Info.Label.AttributeID=info->tag[ix];
719 }
720
721 CssmDataContainer itemData;
722 getContent(&dbAttributes, outData ? &itemData : NULL);
723
724 if(info && attrList)
725 {
726 SecKeychainAttributeList *theList=reinterpret_cast<SecKeychainAttributeList *>(malloc(sizeof(SecKeychainAttributeList)));
727 SecKeychainAttribute *attr=reinterpret_cast<SecKeychainAttribute *>(malloc(sizeof(SecKeychainAttribute)*attrCount));
728 theList->count=attrCount;
729 theList->attr=attr;
730
731 for (UInt32 ix = 0; ix < attrCount; ++ix)
732 {
733 attr[ix].tag=info->tag[ix];
734
735 if (dbAttributes.at(ix).NumberOfValues > 0)
736 {
737 attr[ix].data = dbAttributes.at(ix).Value[0].Data;
738 attr[ix].length = dbAttributes.at(ix).Value[0].Length;
739
740 // We don't want the data released, it is up the client
741 dbAttributes.at(ix).Value[0].Data = NULL;
742 dbAttributes.at(ix).Value[0].Length = 0;
743 }
744 else
745 {
746 attr[ix].data = NULL;
747 attr[ix].length = 0;
748 }
749 }
750 *attrList=theList;
751 }
752
753 if (outData)
754 {
755 *outData=itemData.data();
756 itemData.Data=NULL;
757
758 *length=itemData.length();
759 itemData.Length=0;
760
761 KCEventNotifier::PostKeychainEvent(kSecDataAccessEvent, mKeychain, this);
762 }
763
764 }
765
766 void
767 ItemImpl::freeAttributesAndData(SecKeychainAttributeList *attrList, void *data)
768 {
769 CssmAllocator &allocator = CssmAllocator::standard(); // @@@ This might not match the one used originally
770
771 if (data)
772 allocator.free(data);
773
774 if(attrList)
775 {
776 for (UInt32 ix = 0; ix < attrList->count; ++ix)
777 {
778 allocator.free(attrList->attr[ix].data);
779 }
780 free(attrList->attr);
781 free(attrList);
782 }
783 }
784
785 void
786 ItemImpl::getAttribute(SecKeychainAttribute& attr, UInt32 *actualLength)
787 {
788 if (attr.tag == kSecClassItemAttr)
789 return getClass(attr, actualLength);
790
791 if (mDbAttributes.get())
792 {
793 CssmDbAttributeData *data = mDbAttributes->find(Schema::attributeInfo(attr.tag));
794 if (data)
795 {
796 getAttributeFrom(data, attr, actualLength);
797 return;
798 }
799 }
800
801 if (!mKeychain)
802 MacOSError::throwMe(errSecNoSuchAttr);
803
804 dbUniqueRecord();
805 DbAttributes dbAttributes(mUniqueId->database(), 1);
806 dbAttributes.add(Schema::attributeInfo(attr.tag));
807 mUniqueId->get(&dbAttributes, NULL);
808 getAttributeFrom(&dbAttributes.at(0), attr, actualLength);
809 }
810
811 void
812 ItemImpl::getAttributeFrom(CssmDbAttributeData *data, SecKeychainAttribute &attr, UInt32 *actualLength)
813 {
814 static const uint32 zero = 0;
815 uint32 length;
816 const void *buf;
817
818 // Temporary storage for buf.
819 SInt64 macLDT;
820 UInt32 macSeconds;
821 sint16 svalue16;
822 uint16 uvalue16;
823 sint8 svalue8;
824 uint8 uvalue8;
825
826 if (!data)
827 length = 0;
828 else if (data->size() < 1) // Attribute has no values.
829 {
830 if (data->format() == CSSM_DB_ATTRIBUTE_FORMAT_SINT32
831 || data->format() == CSSM_DB_ATTRIBUTE_FORMAT_UINT32)
832 {
833 length = sizeof(zero);
834 buf = &zero;
835 }
836 else if (CSSM_DB_ATTRIBUTE_FORMAT_TIME_DATE)
837 length = 0; // Should we throw here?
838 else // All other formats
839 length = 0;
840 }
841 else // Get the first value
842 {
843 length = data->Value[0].Length;
844 buf = data->Value[0].Data;
845
846 if (data->format() == CSSM_DB_ATTRIBUTE_FORMAT_SINT32)
847 {
848 if (attr.length == sizeof(sint8))
849 {
850 length = attr.length;
851 svalue8 = sint8(*reinterpret_cast<const sint32 *>(buf));
852 buf = &svalue8;
853 }
854 else if (attr.length == sizeof(sint16))
855 {
856 length = attr.length;
857 svalue16 = sint16(*reinterpret_cast<const sint32 *>(buf));
858 buf = &svalue16;
859 }
860 }
861 else if (data->format() == CSSM_DB_ATTRIBUTE_FORMAT_UINT32)
862 {
863 if (attr.length == sizeof(uint8))
864 {
865 length = attr.length;
866 uvalue8 = uint8(*reinterpret_cast<const uint32 *>(buf));
867 buf = &uvalue8;
868 }
869 else if (attr.length == sizeof(uint16))
870 {
871 length = attr.length;
872 uvalue16 = uint16(*reinterpret_cast<const uint32 *>(buf));
873 buf = &uvalue16;
874 }
875 }
876 else if (data->format() == CSSM_DB_ATTRIBUTE_FORMAT_TIME_DATE)
877 {
878 if (attr.length == sizeof(UInt32))
879 {
880 TimeStringToMacSeconds(data->Value[0], macSeconds);
881 buf = &macSeconds;
882 length = attr.length;
883 }
884 else if (attr.length == sizeof(SInt64))
885 {
886 TimeStringToMacLongDateTime(data->Value[0], macLDT);
887 buf = &macLDT;
888 length = attr.length;
889 }
890 }
891 }
892
893 if (actualLength)
894 *actualLength = length;
895
896 if (length)
897 {
898 if (attr.length < length)
899 MacOSError::throwMe(errSecBufferTooSmall);
900
901 memcpy(attr.data, buf, length);
902 }
903 }
904
905 void
906 ItemImpl::getData(CssmDataContainer& outData)
907 {
908 if (!mKeychain)
909 {
910 CssmData *data = mData.get();
911 // If the data hasn't been set we can't return it.
912 if (!data)
913 MacOSError::throwMe(errSecDataNotAvailable);
914
915 outData = *data;
916 return;
917 }
918
919 getContent(NULL, &outData);
920
921 //%%%<might> be done elsewhere, but here is good for now
922 KCEventNotifier::PostKeychainEvent(kSecDataAccessEvent, mKeychain, this);
923 }
924
925 SSGroup
926 ItemImpl::group()
927 {
928 SSGroup group;
929 if (&*mUniqueId)
930 {
931 Db db(mKeychain->database());
932 if (db->dl()->subserviceMask() & CSSM_SERVICE_CSP)
933 {
934 group = safer_cast<SSDbUniqueRecordImpl &>(*mUniqueId).group();
935 }
936 }
937
938 return group;
939 }
940
941 void
942 ItemImpl::getContent(DbAttributes *dbAttributes, CssmDataContainer *itemData)
943 {
944 // Make sure mUniqueId is set.
945 dbUniqueRecord();
946 if (itemData)
947 {
948 Db db(mUniqueId->database());
949 if (db->dl()->subserviceMask() & CSSM_SERVICE_CSP)
950 {
951 SSDbUniqueRecord ssUniqueId(safe_cast<SSDbUniqueRecordImpl *>(&(*mUniqueId)));
952 const AccessCredentials *autoPrompt = globals().credentials();
953 ssUniqueId->get(dbAttributes, itemData, autoPrompt);
954 return;
955 }
956 }
957
958 mUniqueId->get(dbAttributes, itemData);
959 }