]> git.saurik.com Git - apple/security.git/blobdiff - OSX/libsecurity_keychain/Security/SecKeychainItemExtendedAttributes.cpp
Security-57336.1.9.tar.gz
[apple/security.git] / OSX / libsecurity_keychain / Security / SecKeychainItemExtendedAttributes.cpp
diff --git a/OSX/libsecurity_keychain/Security/SecKeychainItemExtendedAttributes.cpp b/OSX/libsecurity_keychain/Security/SecKeychainItemExtendedAttributes.cpp
new file mode 100644 (file)
index 0000000..1f7e006
--- /dev/null
@@ -0,0 +1,367 @@
+/*
+ * Copyright (c) 2006,2011-2014 Apple Inc. All Rights Reserved.
+ * 
+ * @APPLE_LICENSE_HEADER_START@
+ * 
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ * 
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ * 
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+#include "SecKeychainItemExtendedAttributes.h"
+#include "SecKeychainItemPriv.h"
+#include "ExtendedAttribute.h"
+#include "SecBridge.h"
+#include "StorageManager.h"
+#include "KCCursor.h"
+
+/* I'm not sure we need this */
+#if 0
+CFTypeID SecKeychainItemExtendedAttributesGetTypeID(void);
+
+static CFTypeID SecKeychainItemExtendedAttributesGetTypeID(void)
+{
+       BEGIN_SECAPI
+
+       return gTypes().ExtendedAttribute.typeID;
+
+       END_SECAPI1(_kCFRuntimeNotATypeID)
+}
+#endif
+
+/*
+ * Determine if incoming itemRef can be considered for 
+ * this mechanism; throw if not.
+ */
+static void isItemRefCapable(
+       SecKeychainItemRef                      itemRef)
+{
+       CFTypeID id = CFGetTypeID(itemRef);
+       if((id == gTypes().ItemImpl.typeID) ||
+          (id == gTypes().Certificate.typeID) || 
+          (id == gTypes().KeyItem.typeID)) {
+               return;
+       }
+       else {
+               MacOSError::throwMe(errSecNoSuchAttr);
+       }   
+}      
+
+static void cfStringToData(
+       CFStringRef cfStr,
+       CssmOwnedData &dst)
+{
+       CFDataRef cfData = CFStringCreateExternalRepresentation(NULL, cfStr,
+               kCFStringEncodingUTF8, 0);
+       if(cfData == NULL) {
+               /* can't convert to UTF8!? */
+               MacOSError::throwMe(errSecParam);
+       }
+       dst.copy(CFDataGetBytePtr(cfData), CFDataGetLength(cfData)); 
+       CFRelease(cfData);
+}
+
+/*
+ * Look up an ExtendedAttribute item associated with specified item.
+ * Returns true if found, false if not. 
+ * Throws errSecNoSuchAttr if item does not reside on a keychain.
+ */
+static bool lookupExtendedAttr(
+       SecKeychainItemRef                      itemRef,
+       CFStringRef                                     attrName,
+       Item                                            &foundItem)
+{
+       isItemRefCapable(itemRef);
+       
+       /* 
+        * Get the info about the extended attribute to look up:
+        * -- RecordType
+        * -- ItemID (i.e., PrimaryKey blob)
+        * -- AttributeName
+        */
+
+       Item inItem = ItemImpl::required(itemRef);
+       const CssmData &itemID  = inItem->itemID();
+       CSSM_DB_RECORDTYPE recType = inItem->recordType();
+       if(!inItem->keychain()) {
+               /* item must reside on a keychain */
+               MacOSError::throwMe(errSecNoSuchAttr);
+       }
+       
+       CssmAutoData nameData(Allocator::standard());
+       cfStringToData(attrName, nameData);
+       CssmData nameCData = nameData;
+       
+       SecKeychainAttribute attrs[3];
+       attrs[0].tag    = kExtendedAttrRecordTypeAttr;
+       attrs[0].length = sizeof(UInt32);
+       attrs[0].data   = (void *)&recType;
+       attrs[1].tag    = kExtendedAttrItemIDAttr;
+       attrs[1].length = (UInt32)itemID.Length;
+       attrs[1].data   = itemID.Data;
+       attrs[2].tag    = kExtendedAttrAttributeNameAttr;
+       attrs[2].length = (UInt32)nameCData.Length;
+       attrs[2].data   = nameCData.Data;
+       SecKeychainAttributeList attrList = {3, attrs};
+       
+       StorageManager::KeychainList kcList;
+       kcList.push_back(inItem->keychain());
+       
+       KCCursor cursor(kcList, CSSM_DL_DB_RECORD_EXTENDED_ATTRIBUTE, &attrList);
+       try {
+               return cursor->next(foundItem);
+       }
+       catch(const CssmError &err) {
+               if(err.error == CSSMERR_DL_INVALID_RECORDTYPE) {
+                       /* this keychain not set up for extended attributes yet */
+                       return false;
+               }
+               else {
+                       throw;
+               }
+       } 
+}
+
+OSStatus SecKeychainItemSetExtendedAttribute(
+       SecKeychainItemRef                      itemRef,
+       CFStringRef                                     attrName,
+       CFDataRef                                       attrValue)                      /* NULL means delete the attribute */
+{
+#if SECTRUST_OSX
+#warning This needs to detect SecCertificateRef items, and when it does, SecKeychainItemDelete must be updated
+#endif
+    BEGIN_SECAPI
+       
+       if((itemRef == NULL) || (attrName == NULL)) {
+               return errSecParam;
+       }
+       
+       /* is there already a matching ExtendedAttribute item? */
+       Item foundItem;
+       bool haveMatch = lookupExtendedAttr(itemRef, attrName, foundItem);
+       if(attrValue == NULL) {
+               /* caller asking us to delete existing record */
+               if(!foundItem) {
+                       return errSecNoSuchAttr;
+               }
+               foundItem->keychain()->deleteItem(foundItem);
+               return errSecSuccess;
+       }
+
+       CSSM_DATA attrCValue = {CFDataGetLength(attrValue), (uint8 *)CFDataGetBytePtr(attrValue)};
+       
+       if(haveMatch) {
+               /* update existing extended attribute record */
+               CssmDbAttributeInfo attrInfo(kExtendedAttrAttributeValueAttr, CSSM_DB_ATTRIBUTE_FORMAT_BLOB);
+               foundItem->setAttribute(attrInfo, attrCValue);
+               foundItem->update();
+       }
+       else {
+               /* create a new one, add it to the same keychain as itemRef */
+               Item inItem = ItemImpl::required(itemRef);
+               CssmAutoData nameData(Allocator::standard());
+               cfStringToData(attrName, nameData);
+               CssmData nameCData = nameData;
+               SecPointer<ExtendedAttribute> extAttr(new ExtendedAttribute(
+                               inItem->recordType(), inItem->itemID(), nameCData,
+                               CssmData::overlay(attrCValue)));
+               Item outItem(extAttr);
+               inItem->keychain()->add(outItem);
+       }
+       
+       END_SECAPI
+}
+
+OSStatus SecKeychainItemCopyExtendedAttribute(
+       SecKeychainItemRef                      itemRef,
+       CFStringRef                                     attrName,
+       CFDataRef                                       *attrValue)             /* RETURNED */
+{
+#if SECTRUST_OSX
+#warning This needs to detect SecCertificateRef items
+#endif
+    BEGIN_SECAPI
+       
+       if((itemRef == NULL) || (attrName == NULL) || (attrValue == NULL)) {
+               return errSecParam;
+       }
+       
+       Item foundItem;
+       if(!lookupExtendedAttr(itemRef, attrName, foundItem)) {
+               return errSecNoSuchAttr;
+       }
+       
+       /* 
+        * Found it - its kExtendedAttrAttributeValueAttr value is what the 
+        * caller is looking for.
+        * We'd like to use getAttribute() here, but that requires that we know
+        * the size of the attribute before hand...
+        */
+       UInt32 tag = kExtendedAttrAttributeValueAttr;
+       UInt32 format = 0;
+       SecKeychainAttributeInfo attrInfo = {1, &tag, &format};
+       SecKeychainAttributeList *attrList = NULL;
+       foundItem->getAttributesAndData(&attrInfo, NULL, &attrList, NULL, NULL);
+       if((attrList == NULL) || (attrList->count != 1)) {
+               /* should never happen... */
+               MacOSError::throwMe(errSecNoSuchAttr);
+       }
+       *attrValue = CFDataCreate(NULL, (const UInt8 *)attrList->attr->data, 
+               attrList->attr->length);
+       ItemImpl::freeAttributesAndData(attrList, NULL);
+       END_SECAPI
+}
+
+OSStatus SecKeychainItemCopyAllExtendedAttributes(
+       SecKeychainItemRef                      itemRef,
+       CFArrayRef                                      *attrNames,                     /* RETURNED, each element is a CFStringRef */
+       CFArrayRef                                      *attrValues)            /* optional, RETURNED, each element is a 
+                                                                                                        *   CFDataRef */
+{
+#if SECTRUST_OSX
+#warning This needs to detect SecCertificateRef items, and when it does, SecKeychainItemDelete must be updated
+#endif
+    BEGIN_SECAPI
+       
+       if((itemRef == NULL) || (attrNames == NULL)) {
+               return errSecParam;
+       }
+
+       isItemRefCapable(itemRef);
+       
+       /* 
+        * Get the info about the extended attribute to look up:
+        * -- RecordType
+        * -- ItemID (i.e., PrimaryKey blob)
+        */
+
+       Item inItem = ItemImpl::required(itemRef);
+       const CssmData &itemID  = inItem->itemID();
+       CSSM_DB_RECORDTYPE recType = inItem->recordType();
+       if(!inItem->keychain()) {
+               /* item must reside on a keychain */
+               MacOSError::throwMe(errSecNoSuchAttr);
+       }
+       
+       SecKeychainAttribute attrs[2];
+       attrs[0].tag    = kExtendedAttrRecordTypeAttr;
+       attrs[0].length = sizeof(UInt32);
+       attrs[0].data   = (void *)&recType;
+       attrs[1].tag    = kExtendedAttrItemIDAttr;
+       attrs[1].length = (UInt32)itemID.Length;
+       attrs[1].data   = itemID.Data;
+       SecKeychainAttributeList attrList = {2, attrs};
+       
+       StorageManager::KeychainList kcList;
+       kcList.push_back(inItem->keychain());
+       
+       CFMutableArrayRef outNames = NULL;
+       CFMutableArrayRef outValues = NULL;
+       OSStatus ourRtn = errSecSuccess;
+       
+       KCCursor cursor(kcList, CSSM_DL_DB_RECORD_EXTENDED_ATTRIBUTE, &attrList);
+       for(;;) {
+               bool gotOne = false;
+               Item foundItem;
+               try {
+                       gotOne = cursor->next(foundItem);
+               }
+               catch(...) {
+                       break;
+               } 
+               if(!gotOne) {
+                       break;
+               }
+               
+               /* 
+                * Found one - return its kExtendedAttrAttributeNameAttr and
+                * (optionally) kExtendedAttrAttributeValueAttr attribute values
+                * to caller.
+                */
+               UInt32 tags[2] = { kExtendedAttrAttributeNameAttr, kExtendedAttrAttributeValueAttr };
+               UInt32 formats[2] = {0};
+               SecKeychainAttributeInfo attrInfo = {2, tags, formats};
+               SecKeychainAttributeList *attrList = NULL;
+               foundItem->getAttributesAndData(&attrInfo, NULL, &attrList, NULL, NULL);
+               if((attrList == NULL) || (attrList->count != 2)) {
+                       /* should never happen... */
+                       ourRtn = errSecNoSuchAttr;
+                       break;
+               }
+               if(outNames == NULL) {
+                       outNames = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
+               }
+               if((outValues == NULL) && (attrValues != NULL)) {
+                       /* this one's optional */
+                       outValues = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
+               }
+               
+               /* 
+                * I don't see how we can assume that the order of the returned
+                * attributes is the same as the order of the tags we specified 
+                */
+               for(unsigned dex=0; dex<2; dex++) {
+                       SecKeychainAttribute *attr = &attrList->attr[dex];
+                       CFDataRef cfd = NULL;
+                       CFStringRef cfs = NULL;
+                       switch(attr->tag) {
+                               case kExtendedAttrAttributeNameAttr:
+                                       cfd = CFDataCreate(NULL, (const UInt8 *)attr->data, attr->length);
+                                       
+                                       /* We created this attribute's data via CFStringCreateExternalRepresentation, so
+                                        * this should always work... */
+                                       cfs = CFStringCreateFromExternalRepresentation(NULL, cfd, kCFStringEncodingUTF8);
+                                       CFArrayAppendValue(outNames, cfs);
+                                       CFRelease(cfd);
+                                       CFRelease(cfs);
+                                       break;
+                               case kExtendedAttrAttributeValueAttr:
+                                       if(outValues == NULL) {
+                                               break;
+                                       }
+                                       cfd = CFDataCreate(NULL, (const UInt8 *)attr->data, attr->length);
+                                       CFArrayAppendValue(outValues, cfd);
+                                       CFRelease(cfd);
+                                       break;
+                               default:
+                                       /* should never happen, right? */
+                                       MacOSError::throwMe(errSecInternalComponent);
+                       }
+               }
+               ItemImpl::freeAttributesAndData(attrList, NULL);
+       }       /* main loop fetching matching Extended Attr records */
+       
+       if(ourRtn) {
+               if(outNames) {
+                       CFRelease(outNames);
+               }
+               if(outValues) {
+                       CFRelease(outValues);
+               }
+               MacOSError::throwMe(ourRtn);
+       }
+       
+       if(outNames == NULL) {
+               /* no extended attributes found */
+               return errSecNoSuchAttr;
+       }
+       *attrNames = outNames;
+       if(outValues) {
+               *attrValues = outValues;
+       }
+       
+       END_SECAPI
+}