1// 2// keychain_test.m 3// Keychain item access control example 4// 5// Created by Perry Kiehtreiber on Wed Jun 19 2002 6// Modified by Ken McLeod, Mon Apr 21 2003 -- added "always allow" ACL support 7// Wed Jul 28 2004 -- add test code for persistent ref SPI 8// Mon Aug 02 2004 -- add test code to change label attributes 9// 10// To build and run this example: 11// cc -framework Security -framework Foundation keychain_test.m ; ./a.out 12// 13// Copyright (c) 2003-2005,2007 Apple Inc. All Rights Reserved. 14// 15 16#define TEST_PERSISTENT_REFS 0 17#define USE_SYSTEM_KEYCHAIN 0 18 19 20#import <Cocoa/Cocoa.h> 21 22#include <Security/SecBase.h> 23#include <Security/SecKeychain.h> 24#include <Security/SecKeychainItem.h> 25#include <Security/SecKeychainItemPriv.h> 26#include <Security/SecKeychainSearch.h> 27#include <Security/SecAccess.h> 28#include <Security/SecTrustedApplication.h> 29#include <Security/SecACL.h> 30 31#import "testmore.h" 32#import "testenv.h" 33#import "testleaks.h" 34 35void renameItemViaModifyAttributesAndData(SecKeychainItemRef item) 36{ 37 const char *labelSuffix = " [MAD]"; 38 39 // get the item's label attribute (allocated for us by 40 // SecKeychainItemCopyAttributesAndData, must free later...) 41 UInt32 itemTags[] = { kSecLabelItemAttr }; 42 UInt32 itemFmts[] = { CSSM_DB_ATTRIBUTE_FORMAT_STRING }; 43 SecKeychainAttributeInfo attrInfo = { 1, itemTags, itemFmts }; 44 SecKeychainAttributeList *attrList = NULL; 45 SecItemClass itemClass; 46 47 ok_status(SecKeychainItemCopyAttributesAndData(item, &attrInfo, &itemClass, &attrList, NULL, NULL), 48 "get label attribute"); 49 50 ok(attrList && attrList->count == 1, "check that exactly one attribute was returned"); 51 52 // malloc enough space to hold our new label string 53 // (length = old label string + suffix string + terminating NULL) 54 CFIndex newLen = attrList->attr[0].length + strlen(labelSuffix); 55 char *p = (char*) malloc(newLen); 56 memcpy(p, attrList->attr[0].data, attrList->attr[0].length); 57 memcpy(p + attrList->attr[0].length, labelSuffix, strlen(labelSuffix)); 58 59 // set up the attribute we want to change with its new value 60 SecKeychainAttribute newAttrs[] = { { kSecLabelItemAttr, newLen, p } }; 61 SecKeychainAttributeList newAttrList = 62 { sizeof(newAttrs) / sizeof(newAttrs[0]), newAttrs }; 63 64 // modify the attribute 65 ok_status(SecKeychainItemModifyAttributesAndData(item, &newAttrList, 0, NULL), 66 "SecKeychainItemModifyAttributesAndData"); 67 68 // free the memory we allocated for the new label string 69 free(p); 70 71 // free the attrList which was allocated by SecKeychainItemCopyAttributesAndData 72 ok_status(SecKeychainItemFreeAttributesAndData(attrList, NULL), 73 "SecKeychainItemFreeAttributesAndData"); 74} 75 76void renameItemViaModifyContent(SecKeychainItemRef item) 77{ 78 const char *labelSuffix = " [MC]"; 79 80 // get the item's label attribute (allocated for us by 81 // SecKeychainItemCopyContent, must free later...) 82 SecKeychainAttribute itemAttrs[] = { { kSecLabelItemAttr, 0, NULL } }; 83 SecKeychainAttributeList itemAttrList = 84 { sizeof(itemAttrs) / sizeof(itemAttrs[0]), itemAttrs }; 85 86 ok_status(SecKeychainItemCopyContent(item, NULL, &itemAttrList, 87 NULL, NULL), "get label"); 88 89 ok(itemAttrs[0].data != NULL, "check that attribute data was returned"); 90 91 // malloc enough space to hold our new label string 92 // (length = old label string + suffix string + terminating NULL) 93 CFIndex newLen = itemAttrs[0].length + strlen(labelSuffix); 94 char *p = (char*) malloc(newLen); 95 memcpy(p, itemAttrs[0].data, itemAttrs[0].length); 96 memcpy(p + itemAttrs[0].length, labelSuffix, strlen(labelSuffix)); 97 98 // set up the attribute we want to change with its new value 99 SecKeychainAttribute newAttrs[] = { { kSecLabelItemAttr, newLen, p } }; 100 SecKeychainAttributeList newAttrList = 101 { sizeof(newAttrs) / sizeof(newAttrs[0]), newAttrs }; 102 103 // modify the attribute 104 ok_status(SecKeychainItemModifyContent(item, &newAttrList, 105 0, NULL), "modify label"); 106 107 // free the memory we allocated for the new label string 108 free(p); 109 110 // free the memory in the itemAttrList structure which was 111 // allocated by SecKeychainItemCopyContent 112 ok_status(SecKeychainItemFreeContent(&itemAttrList, NULL), 113 "SecKeychainItemFreeContent"); 114} 115 116void testRenameItemLabels(SecKeychainRef keychain) 117{ 118 // Find each generic password item in the given keychain whose label 119 // is "sample service", and modify the existing label attribute 120 // by adding a " [label]" suffix. 121 122 const char *searchString = "sample service"; 123 124 SecKeychainSearchRef searchRef = nil; 125 SecKeychainAttribute sAttrs[] = 126 { { kSecServiceItemAttr, strlen(searchString), (char*)searchString } }; 127 SecKeychainAttributeList sAttrList = 128 { sizeof(sAttrs) / sizeof(sAttrs[0]), sAttrs }; 129 ok_status(SecKeychainSearchCreateFromAttributes(keychain, 130 kSecGenericPasswordItemClass, &sAttrList, &searchRef), 131 "SecKeychainSearchCreateFromAttributes"); 132 133 SecKeychainItemRef foundItemRef = NULL; 134 int count; 135 for (count = 0; count < 2; ++count) 136 { 137 ok_status(SecKeychainSearchCopyNext(searchRef, &foundItemRef), 138 "SecKeychainSearchCopyNext"); 139 140 renameItemViaModifyAttributesAndData(foundItemRef); // 4 141 renameItemViaModifyContent(foundItemRef); // 4 142 143 if (foundItemRef) 144 CFRelease(foundItemRef); 145 } 146 147 is_status(SecKeychainSearchCopyNext(searchRef, &foundItemRef), 148 errSecItemNotFound, "SecKeychainSearchCopyNext at end"); 149 150 if (searchRef) CFRelease(searchRef); 151} 152 153SecAccessRef createAccess(NSString *accessLabel, BOOL allowAny) 154{ 155 SecAccessRef access=nil; 156 NSArray *trustedApplications=nil; 157 158 if (!allowAny) // use default access ("confirm access") 159 { 160 // make an exception list of applications you want to trust, which 161 // are allowed to access the item without requiring user confirmation 162 SecTrustedApplicationRef myself, someOther; 163 ok_status(SecTrustedApplicationCreateFromPath(NULL, &myself), 164 "create trusted app for self"); 165 ok_status(SecTrustedApplicationCreateFromPath("/Applications/Mail.app", 166 &someOther), "create trusted app for Mail.app"); 167 trustedApplications = [NSArray arrayWithObjects:(id)myself, 168 (id)someOther, nil]; 169 CFRelease(myself); 170 CFRelease(someOther); 171 } 172 173 ok_status(SecAccessCreate((CFStringRef)accessLabel, 174 (CFArrayRef)trustedApplications, &access), "SecAccessCreate"); 175 176 if (allowAny) 177 { 178 // change access to be wide-open for decryption ("always allow access") 179 // get the access control list for decryption operations 180 CFArrayRef aclList=nil; 181 ok_status(SecAccessCopySelectedACLList(access, 182 CSSM_ACL_AUTHORIZATION_DECRYPT, &aclList), 183 "SecAccessCopySelectedACLList"); 184 185 // get the first entry in the access control list 186 SecACLRef aclRef=(SecACLRef)CFArrayGetValueAtIndex(aclList, 0); 187 CFArrayRef appList=nil; 188 CFStringRef promptDescription=nil; 189 CSSM_ACL_KEYCHAIN_PROMPT_SELECTOR promptSelector; 190 ok_status(SecACLCopySimpleContents(aclRef, &appList, 191 &promptDescription, &promptSelector), "SecACLCopySimpleContents"); 192 193 // modify the default ACL to not require the passphrase, and have a 194 // nil application list 195 promptSelector.flags &= ~CSSM_ACL_KEYCHAIN_PROMPT_REQUIRE_PASSPHRASE; 196 ok_status(SecACLSetSimpleContents(aclRef, NULL, promptDescription, 197 &promptSelector), "SecACLSetSimpleContents"); 198 199 if (appList) CFRelease(appList); 200 if (promptDescription) CFRelease(promptDescription); 201 if (aclList) CFRelease(aclList); 202 } 203 204 return access; 205} 206 207void addApplicationPassword(SecKeychainRef keychain, NSString *password, 208 NSString *account, NSString *service, BOOL allowAny, SecKeychainItemRef *outItem) 209{ 210 SecKeychainItemRef item = nil; 211 const char *serviceUTF8 = [service UTF8String]; 212 const char *accountUTF8 = [account UTF8String]; 213 const char *passwordUTF8 = [password UTF8String]; 214 // use the service string as the name of this item for display purposes 215 NSString *itemLabel = service; 216 const char *itemLabelUTF8 = [itemLabel UTF8String]; 217 218#if USE_SYSTEM_KEYCHAIN 219 const char *sysKeychainPath = "/Library/Keychains/System.keychain"; 220 status = SecKeychainOpen(sysKeychainPath, &keychain); 221 if (status) { NSLog(@"SecKeychainOpen returned %d", status); return; } 222 status = SecKeychainSetPreferenceDomain(kSecPreferencesDomainSystem); 223 if (status) { NSLog(@"SecKeychainSetPreferenceDomain returned %d", status); return; } 224#endif 225 226 // create initial access control settings for the item 227 SecAccessRef access = createAccess(itemLabel, allowAny); 228 229 // Below is the lower-layer equivalent to the 230 // SecKeychainAddGenericPassword() function; it does the same thing 231 // (except specify the access controls) set up attribute vector 232 // (each attribute consists of {tag, length, pointer}) 233 SecKeychainAttribute attrs[] = { 234 { kSecLabelItemAttr, strlen(itemLabelUTF8), (char *)itemLabelUTF8 }, 235 { kSecAccountItemAttr, strlen(accountUTF8), (char *)accountUTF8 }, 236 { kSecServiceItemAttr, strlen(serviceUTF8), (char *)serviceUTF8 } 237 }; 238 SecKeychainAttributeList attributes = 239 { sizeof(attrs) / sizeof(attrs[0]), attrs }; 240 241 ok_status(SecKeychainItemCreateFromContent(kSecGenericPasswordItemClass, 242 &attributes, strlen(passwordUTF8), passwordUTF8, keychain, access, 243 &item), "SecKeychainItemCreateFromContent"); 244 245 if (access) CFRelease(access); 246 247 if (outItem) { 248 *outItem = item; 249 } else if (item) { 250 CFRelease(item); 251 } 252} 253 254// 1 255void addInternetPassword(SecKeychainRef keychain, NSString *password, 256 NSString *account, NSString *server, NSString *path, 257 SecProtocolType protocol, int port, BOOL allowAny, SecKeychainItemRef *outItem) 258{ 259 SecKeychainItemRef item = nil; 260 const char *pathUTF8 = [path UTF8String]; 261 const char *serverUTF8 = [server UTF8String]; 262 const char *accountUTF8 = [account UTF8String]; 263 const char *passwordUTF8 = [password UTF8String]; 264 // use the server string as the name of this item for display purposes 265 NSString *itemLabel = server; 266 const char *itemLabelUTF8 = [itemLabel UTF8String]; 267 268#if USE_SYSTEM_KEYCHAIN 269 const char *sysKeychainPath = "/Library/Keychains/System.keychain"; 270 status = SecKeychainOpen(sysKeychainPath, &keychain); 271 if (status) { NSLog(@"SecKeychainOpen returned %d", status); return 1; } 272 status = SecKeychainSetPreferenceDomain(kSecPreferencesDomainSystem); 273 if (status) { NSLog(@"SecKeychainSetPreferenceDomain returned %d", status); return 1; } 274#endif 275 276 // create initial access control settings for the item 277 SecAccessRef access = createAccess(itemLabel, allowAny); 278 279 // below is the lower-layer equivalent to the 280 // SecKeychainAddInternetPassword() function; it does the same 281 // thing (except specify the access controls) set up attribute 282 // vector (each attribute consists of {tag, length, pointer}) 283 SecKeychainAttribute attrs[] = { 284 { kSecLabelItemAttr, strlen(itemLabelUTF8), (char *)itemLabelUTF8 }, 285 { kSecAccountItemAttr, strlen(accountUTF8), (char *)accountUTF8 }, 286 { kSecServerItemAttr, strlen(serverUTF8), (char *)serverUTF8 }, 287 { kSecPortItemAttr, sizeof(int), (int *)&port }, 288 { kSecProtocolItemAttr, sizeof(SecProtocolType), 289 (SecProtocolType *)&protocol }, 290 { kSecPathItemAttr, strlen(pathUTF8), (char *)pathUTF8 } 291 }; 292 SecKeychainAttributeList attributes = 293 { sizeof(attrs) / sizeof(attrs[0]), attrs }; 294 295 ok_status(SecKeychainItemCreateFromContent(kSecInternetPasswordItemClass, 296 &attributes, strlen(passwordUTF8), passwordUTF8, keychain, access, 297 &item), "SecKeychainItemCreateFromContent"); 298 299 if (access) CFRelease(access); 300 301 if (outItem) { 302 *outItem = item; 303 } else if (item) { 304 CFRelease(item); 305 } 306} 307 308void tests(void) 309{ 310 SecKeychainRef keychain = NULL; 311 ok_status(SecKeychainCreate("login.keychain", 4, "test", NO, NULL, 312 &keychain), "SecKeychainCreate"); 313 314 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; 315 316 // add some example passwords to the keychain 317 addApplicationPassword(keychain, @"sample password", 318 @"sample account", @"sample service", NO, NULL); 319 addApplicationPassword(keychain, @"sample password", 320 @"different account", @"sample service", NO, NULL); 321 addApplicationPassword(keychain, @"sample password", 322 @"sample account", @"sample unprotected service", YES, NULL); 323 addInternetPassword(keychain, @"sample password", 324 @"sample account", @"samplehost.apple.com", 325 @"cgi-bin/bogus/testpath", kSecProtocolTypeHTTP, 8080, NO, NULL); 326 327 // test searching and changing item label attributes 328 testRenameItemLabels(keychain); 329 330 [pool release]; 331 332 SKIP: { 333 skip("no keychain", 1, keychain); 334 ok_status(SecKeychainDelete(keychain), "SecKeychainDelete"); 335 CFRelease(keychain); 336 } 337 338 tests_end(1); 339} 340 341int main(int argc, char * const *argv) 342{ 343 plan_tests(40); 344 tests_begin(argc, argv); 345 346 tests(); 347 348 ok_leaks("leaks"); 349 350 return 0; 351} 352