3 // Keychain item access control example
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
10 // To build and run this example:
11 // cc -framework Security -framework Foundation keychain_test.m ; ./a.out
13 // Copyright (c) 2003-2005,2007 Apple Inc. All Rights Reserved.
16 #define TEST_PERSISTENT_REFS 0
17 #define USE_SYSTEM_KEYCHAIN 0
20 #import <Cocoa/Cocoa.h>
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>
35 void renameItemViaModifyAttributesAndData(SecKeychainItemRef item)
37 const char *labelSuffix = " [MAD]";
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;
47 ok_status(SecKeychainItemCopyAttributesAndData(item, &attrInfo, &itemClass, &attrList, NULL, NULL),
48 "get label attribute");
50 ok(attrList && attrList->count == 1, "check that exactly one attribute was returned");
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));
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 };
64 // modify the attribute
65 ok_status(SecKeychainItemModifyAttributesAndData(item, &newAttrList, 0, NULL),
66 "SecKeychainItemModifyAttributesAndData");
68 // free the memory we allocated for the new label string
71 // free the attrList which was allocated by SecKeychainItemCopyAttributesAndData
72 ok_status(SecKeychainItemFreeAttributesAndData(attrList, NULL),
73 "SecKeychainItemFreeAttributesAndData");
76 void renameItemViaModifyContent(SecKeychainItemRef item)
78 const char *labelSuffix = " [MC]";
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 };
86 ok_status(SecKeychainItemCopyContent(item, NULL, &itemAttrList,
87 NULL, NULL), "get label");
89 ok(itemAttrs[0].data != NULL, "check that attribute data was returned");
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));
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 };
103 // modify the attribute
104 ok_status(SecKeychainItemModifyContent(item, &newAttrList,
105 0, NULL), "modify label");
107 // free the memory we allocated for the new label string
110 // free the memory in the itemAttrList structure which was
111 // allocated by SecKeychainItemCopyContent
112 ok_status(SecKeychainItemFreeContent(&itemAttrList, NULL),
113 "SecKeychainItemFreeContent");
116 void testRenameItemLabels(SecKeychainRef keychain)
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.
122 const char *searchString = "sample service";
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");
133 SecKeychainItemRef foundItemRef = NULL;
135 for (count = 0; count < 2; ++count)
137 ok_status(SecKeychainSearchCopyNext(searchRef, &foundItemRef),
138 "SecKeychainSearchCopyNext");
140 renameItemViaModifyAttributesAndData(foundItemRef); // 4
141 renameItemViaModifyContent(foundItemRef); // 4
144 CFRelease(foundItemRef);
147 is_status(SecKeychainSearchCopyNext(searchRef, &foundItemRef),
148 errSecItemNotFound, "SecKeychainSearchCopyNext at end");
150 if (searchRef) CFRelease(searchRef);
153 SecAccessRef createAccess(NSString *accessLabel, BOOL allowAny)
155 SecAccessRef access=nil;
156 NSArray *trustedApplications=nil;
158 if (!allowAny) // use default access ("confirm access")
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,
170 CFRelease(someOther);
173 ok_status(SecAccessCreate((CFStringRef)accessLabel,
174 (CFArrayRef)trustedApplications, &access), "SecAccessCreate");
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");
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");
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");
199 if (appList) CFRelease(appList);
200 if (promptDescription) CFRelease(promptDescription);
201 if (aclList) CFRelease(aclList);
207 void addApplicationPassword(SecKeychainRef keychain, NSString *password,
208 NSString *account, NSString *service, BOOL allowAny, SecKeychainItemRef *outItem)
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];
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; }
226 // create initial access control settings for the item
227 SecAccessRef access = createAccess(itemLabel, allowAny);
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 }
238 SecKeychainAttributeList attributes =
239 { sizeof(attrs) / sizeof(attrs[0]), attrs };
241 ok_status(SecKeychainItemCreateFromContent(kSecGenericPasswordItemClass,
242 &attributes, strlen(passwordUTF8), passwordUTF8, keychain, access,
243 &item), "SecKeychainItemCreateFromContent");
245 if (access) CFRelease(access);
255 void addInternetPassword(SecKeychainRef keychain, NSString *password,
256 NSString *account, NSString *server, NSString *path,
257 SecProtocolType protocol, int port, BOOL allowAny, SecKeychainItemRef *outItem)
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];
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; }
276 // create initial access control settings for the item
277 SecAccessRef access = createAccess(itemLabel, allowAny);
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 }
292 SecKeychainAttributeList attributes =
293 { sizeof(attrs) / sizeof(attrs[0]), attrs };
295 ok_status(SecKeychainItemCreateFromContent(kSecInternetPasswordItemClass,
296 &attributes, strlen(passwordUTF8), passwordUTF8, keychain, access,
297 &item), "SecKeychainItemCreateFromContent");
299 if (access) CFRelease(access);
310 SecKeychainRef keychain = NULL;
311 ok_status(SecKeychainCreate("login.keychain", 4, "test", NO, NULL,
312 &keychain), "SecKeychainCreate");
314 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
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);
327 // test searching and changing item label attributes
328 testRenameItemLabels(keychain);
333 skip("no keychain", 1, keychain);
334 ok_status(SecKeychainDelete(keychain), "SecKeychainDelete");
341 int main(int argc, char * const *argv)
344 tests_begin(argc, argv);