2  * Copyright (c) 2006,2011-2014 Apple Inc. All Rights Reserved. 
   4  * @APPLE_LICENSE_HEADER_START@ 
   6  * This file contains Original Code and/or Modifications of Original Code 
   7  * as defined in and that are subject to the Apple Public Source License 
   8  * Version 2.0 (the 'License'). You may not use this file except in 
   9  * compliance with the License. Please obtain a copy of the License at 
  10  * http://www.opensource.apple.com/apsl/ and read it before using this 
  13  * The Original Code and all software distributed under the License are 
  14  * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 
  15  * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 
  16  * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 
  17  * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 
  18  * Please see the License for the specific language governing rights and 
  19  * limitations under the License. 
  21  * @APPLE_LICENSE_HEADER_END@ 
  24 #include <security_utilities/casts.h> 
  25 #include "SecKeychainItemExtendedAttributes.h" 
  26 #include "SecKeychainItemPriv.h" 
  27 #include "ExtendedAttribute.h" 
  28 #include "SecBridge.h" 
  29 #include "StorageManager.h" 
  32 /* I'm not sure we need this */ 
  34 CFTypeID 
SecKeychainItemExtendedAttributesGetTypeID(void); 
  36 static CFTypeID 
SecKeychainItemExtendedAttributesGetTypeID(void) 
  40         return gTypes().ExtendedAttribute
.typeID
; 
  42         END_SECAPI1(_kCFRuntimeNotATypeID
) 
  46 extern "C" Boolean 
SecKeyIsCDSAKey(SecKeyRef ref
); 
  49  * Determine if incoming itemRef can be considered for  
  50  * this mechanism; throw if not. 
  52 static void isItemRefCapable( 
  53         SecKeychainItemRef                      itemRef
) 
  55         CFTypeID id 
= CFGetTypeID(itemRef
); 
  56         if((id 
== gTypes().ItemImpl
.typeID
) || 
  57            (id 
== gTypes().Certificate
.typeID
) ||  
  58            (id 
== SecKeyGetTypeID() && SecKeyIsCDSAKey((SecKeyRef
)itemRef
))) { 
  62                 MacOSError::throwMe(errSecNoSuchAttr
); 
  66 static void cfStringToData( 
  70         CFDataRef cfData 
= CFStringCreateExternalRepresentation(NULL
, cfStr
, 
  71                 kCFStringEncodingUTF8
, 0); 
  73                 /* can't convert to UTF8!? */ 
  74                 MacOSError::throwMe(errSecParam
); 
  76         dst
.copy(CFDataGetBytePtr(cfData
), CFDataGetLength(cfData
));  
  81  * Look up an ExtendedAttribute item associated with specified item. 
  82  * Returns true if found, false if not.  
  83  * Throws errSecNoSuchAttr if item does not reside on a keychain. 
  85 static bool lookupExtendedAttr( 
  86         SecKeychainItemRef                      itemRef
, 
  90         isItemRefCapable(itemRef
); 
  93          * Get the info about the extended attribute to look up: 
  95          * -- ItemID (i.e., PrimaryKey blob) 
  99         Item inItem 
= ItemImpl::required(itemRef
); 
 100         const CssmData 
&itemID  
= inItem
->itemID(); 
 101         CSSM_DB_RECORDTYPE recType 
= inItem
->recordType(); 
 102         if(!inItem
->keychain()) { 
 103                 /* item must reside on a keychain */ 
 104                 MacOSError::throwMe(errSecNoSuchAttr
); 
 107         CssmAutoData 
nameData(Allocator::standard()); 
 108         cfStringToData(attrName
, nameData
); 
 109         CssmData nameCData 
= nameData
; 
 111         SecKeychainAttribute attrs
[3]; 
 112         attrs
[0].tag    
= kExtendedAttrRecordTypeAttr
; 
 113         attrs
[0].length 
= sizeof(UInt32
); 
 114         attrs
[0].data   
= (void *)&recType
; 
 115         attrs
[1].tag    
= kExtendedAttrItemIDAttr
; 
 116         attrs
[1].length 
= (UInt32
)itemID
.Length
; 
 117         attrs
[1].data   
= itemID
.Data
; 
 118         attrs
[2].tag    
= kExtendedAttrAttributeNameAttr
; 
 119         attrs
[2].length 
= (UInt32
)nameCData
.Length
; 
 120         attrs
[2].data   
= nameCData
.Data
; 
 121         SecKeychainAttributeList attrList 
= {3, attrs
}; 
 123         StorageManager::KeychainList kcList
; 
 124         kcList
.push_back(inItem
->keychain()); 
 126         KCCursor 
cursor(kcList
, (SecItemClass
) CSSM_DL_DB_RECORD_EXTENDED_ATTRIBUTE
, &attrList
); 
 128                 return cursor
->next(foundItem
); 
 130         catch(const CssmError 
&err
) { 
 131                 if(err
.error 
== CSSMERR_DL_INVALID_RECORDTYPE
) { 
 132                         /* this keychain not set up for extended attributes yet */ 
 141 OSStatus 
SecKeychainItemSetExtendedAttribute( 
 142         SecKeychainItemRef                      itemRef
, 
 143         CFStringRef                                     attrName
, 
 144         CFDataRef                                       attrValue
)                      /* NULL means delete the attribute */ 
 147     //%%% This needs to detect SecCertificateRef items, and when it does, SecKeychainItemDelete must be updated 
 151         if((itemRef 
== NULL
) || (attrName 
== NULL
)) { 
 155         /* is there already a matching ExtendedAttribute item? */ 
 157         bool haveMatch 
= lookupExtendedAttr(itemRef
, attrName
, foundItem
); 
 158         if(attrValue 
== NULL
) { 
 159                 /* caller asking us to delete existing record */ 
 161                         return errSecNoSuchAttr
; 
 163                 foundItem
->keychain()->deleteItem(foundItem
); 
 164                 return errSecSuccess
; 
 167         CSSM_DATA attrCValue 
= {int_cast
<CFIndex
, CSSM_SIZE
>(CFDataGetLength(attrValue
)), (uint8 
*)CFDataGetBytePtr(attrValue
)}; 
 170                 /* update existing extended attribute record */ 
 171                 CssmDbAttributeInfo 
attrInfo(kExtendedAttrAttributeValueAttr
, CSSM_DB_ATTRIBUTE_FORMAT_BLOB
); 
 172                 foundItem
->setAttribute(attrInfo
, attrCValue
); 
 176                 /* create a new one, add it to the same keychain as itemRef */ 
 177                 Item inItem 
= ItemImpl::required(itemRef
); 
 178                 CssmAutoData 
nameData(Allocator::standard()); 
 179                 cfStringToData(attrName
, nameData
); 
 180                 CssmData nameCData 
= nameData
; 
 181                 SecPointer
<ExtendedAttribute
> extAttr(new ExtendedAttribute( 
 182                                 inItem
->recordType(), inItem
->itemID(), nameCData
, 
 183                                 CssmData::overlay(attrCValue
))); 
 184                 Item 
outItem(extAttr
); 
 185                 inItem
->keychain()->add(outItem
); 
 191 OSStatus 
SecKeychainItemCopyExtendedAttribute( 
 192         SecKeychainItemRef                      itemRef
, 
 193         CFStringRef                                     attrName
, 
 194         CFDataRef                                       
*attrValue
)             /* RETURNED */ 
 197     //%%% This needs to detect SecCertificateRef items 
 201         if((itemRef 
== NULL
) || (attrName 
== NULL
) || (attrValue 
== NULL
)) { 
 206         if(!lookupExtendedAttr(itemRef
, attrName
, foundItem
)) { 
 207                 return errSecNoSuchAttr
; 
 211          * Found it - its kExtendedAttrAttributeValueAttr value is what the  
 212          * caller is looking for. 
 213          * We'd like to use getAttribute() here, but that requires that we know 
 214          * the size of the attribute before hand... 
 216         UInt32 tag 
= kExtendedAttrAttributeValueAttr
; 
 218         SecKeychainAttributeInfo attrInfo 
= {1, &tag
, &format
}; 
 219         SecKeychainAttributeList 
*attrList 
= NULL
; 
 220         foundItem
->getAttributesAndData(&attrInfo
, NULL
, &attrList
, NULL
, NULL
); 
 221         if((attrList 
== NULL
) || (attrList
->count 
!= 1)) { 
 222                 /* should never happen... */ 
 223                 MacOSError::throwMe(errSecNoSuchAttr
); 
 225         *attrValue 
= CFDataCreate(NULL
, (const UInt8 
*)attrList
->attr
->data
,  
 226                 attrList
->attr
->length
); 
 227         ItemImpl::freeAttributesAndData(attrList
, NULL
); 
 231 OSStatus 
SecKeychainItemCopyAllExtendedAttributes( 
 232         SecKeychainItemRef                      itemRef
, 
 233         CFArrayRef                                      
*attrNames
,                     /* RETURNED, each element is a CFStringRef */ 
 234         CFArrayRef                                      
*attrValues
)            /* optional, RETURNED, each element is a  
 238     //%%% This needs to detect SecCertificateRef items, and when it does, SecKeychainItemDelete must be updated 
 242         if((itemRef 
== NULL
) || (attrNames 
== NULL
)) { 
 246         isItemRefCapable(itemRef
); 
 249          * Get the info about the extended attribute to look up: 
 251          * -- ItemID (i.e., PrimaryKey blob) 
 254         Item inItem 
= ItemImpl::required(itemRef
); 
 255         const CssmData 
&itemID  
= inItem
->itemID(); 
 256         CSSM_DB_RECORDTYPE recType 
= inItem
->recordType(); 
 257         if(!inItem
->keychain()) { 
 258                 /* item must reside on a keychain */ 
 259                 MacOSError::throwMe(errSecNoSuchAttr
); 
 262         SecKeychainAttribute attrs
[2]; 
 263         attrs
[0].tag    
= kExtendedAttrRecordTypeAttr
; 
 264         attrs
[0].length 
= sizeof(UInt32
); 
 265         attrs
[0].data   
= (void *)&recType
; 
 266         attrs
[1].tag    
= kExtendedAttrItemIDAttr
; 
 267         attrs
[1].length 
= (UInt32
)itemID
.Length
; 
 268         attrs
[1].data   
= itemID
.Data
; 
 269         SecKeychainAttributeList attrList 
= {2, attrs
}; 
 271         StorageManager::KeychainList kcList
; 
 272         kcList
.push_back(inItem
->keychain()); 
 274         CFMutableArrayRef outNames 
= NULL
; 
 275         CFMutableArrayRef outValues 
= NULL
; 
 276         OSStatus ourRtn 
= errSecSuccess
; 
 278         KCCursor 
cursor(kcList
, (SecItemClass
) CSSM_DL_DB_RECORD_EXTENDED_ATTRIBUTE
, &attrList
); 
 283                         gotOne 
= cursor
->next(foundItem
); 
 293                  * Found one - return its kExtendedAttrAttributeNameAttr and 
 294                  * (optionally) kExtendedAttrAttributeValueAttr attribute values 
 297                 UInt32 tags
[2] = { kExtendedAttrAttributeNameAttr
, kExtendedAttrAttributeValueAttr 
}; 
 298                 UInt32 formats
[2] = {0}; 
 299                 SecKeychainAttributeInfo attrInfo 
= {2, tags
, formats
}; 
 300                 SecKeychainAttributeList 
*attrList 
= NULL
; 
 301                 foundItem
->getAttributesAndData(&attrInfo
, NULL
, &attrList
, NULL
, NULL
); 
 302                 if((attrList 
== NULL
) || (attrList
->count 
!= 2)) { 
 303                         /* should never happen... */ 
 304                         ourRtn 
= errSecNoSuchAttr
; 
 307                 if(outNames 
== NULL
) { 
 308                         outNames 
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
); 
 310                 if((outValues 
== NULL
) && (attrValues 
!= NULL
)) { 
 311                         /* this one's optional */ 
 312                         outValues 
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
); 
 316                  * I don't see how we can assume that the order of the returned 
 317                  * attributes is the same as the order of the tags we specified  
 319                 for(unsigned dex
=0; dex
<2; dex
++) { 
 320                         SecKeychainAttribute 
*attr 
= &attrList
->attr
[dex
]; 
 321                         CFDataRef cfd 
= NULL
; 
 322                         CFStringRef cfs 
= NULL
; 
 324                                 case kExtendedAttrAttributeNameAttr
: 
 325                                         cfd 
= CFDataCreate(NULL
, (const UInt8 
*)attr
->data
, attr
->length
); 
 327                                         /* We created this attribute's data via CFStringCreateExternalRepresentation, so 
 328                                          * this should always work... */ 
 329                                         cfs 
= CFStringCreateFromExternalRepresentation(NULL
, cfd
, kCFStringEncodingUTF8
); 
 330                                         CFArrayAppendValue(outNames
, cfs
); 
 334                                 case kExtendedAttrAttributeValueAttr
: 
 335                                         if(outValues 
== NULL
) { 
 338                                         cfd 
= CFDataCreate(NULL
, (const UInt8 
*)attr
->data
, attr
->length
); 
 339                                         CFArrayAppendValue(outValues
, cfd
); 
 343                                         /* should never happen, right? */ 
 344                                         MacOSError::throwMe(errSecInternalComponent
); 
 347                 ItemImpl::freeAttributesAndData(attrList
, NULL
); 
 348         }       /* main loop fetching matching Extended Attr records */ 
 355                         CFRelease(outValues
); 
 357                 MacOSError::throwMe(ourRtn
); 
 360         if(outNames 
== NULL
) { 
 361                 /* no extended attributes found */ 
 362                 return errSecNoSuchAttr
; 
 364         *attrNames 
= outNames
; 
 366                 *attrValues 
= outValues
;