1/*
2 * Copyright (c) 2007 Apple Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
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
11 * file.
12 *
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.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24#include <Security/Security.h>
25#include <err.h>
26#include <stdio.h>
27#include <unistd.h>
28
29static CFArrayRef
30getTrustedApps(const char *trustedApp)
31    CF_RETURNS_RETAINED;
32
33static CFArrayRef
34getTrustedApps(const char *trustedApp)
35{
36    SecTrustedApplicationRef app;
37    void *values[1];
38    OSStatus ret;
39
40    /*
41     * Create ACL, XXX should add acl, not modify ACL.
42     */
43
44    ret = SecTrustedApplicationCreateFromPath(trustedApp, &app);
45    if (ret)
46	errx(1, "SecTrustedApplicationCreateFromPath");
47
48    values[0] = app;
49
50    return CFArrayCreate(NULL,  (const void **)values,
51			 sizeof(values)/sizeof(values[0]), NULL);
52}
53
54
55
56static SecKeychainItemRef
57findKeychainItem(SecKeychainRef keychain, const char *label)
58{
59    SecKeychainItemRef itemRef;
60    OSStatus ret;
61
62    SecKeychainSearchRef search;
63    SecKeychainAttribute attributes[1];
64    SecKeychainAttributeList list;
65
66    attributes[0].tag = kSecLabelItemAttr;
67    attributes[0].data = (char *)(uintptr_t)label;
68    attributes[0].length = strlen(label);
69
70    list.count = 1;
71    list.attr = attributes;
72
73    /* Should only search the System keychain ! */
74    ret = SecKeychainSearchCreateFromAttributes(keychain,
75						CSSM_DL_DB_RECORD_PRIVATE_KEY,
76						&list,
77						&search);
78    if (ret)
79	errx(1, "failure in SecKeychainSearchCreateFromAttributes");
80
81    ret = SecKeychainSearchCopyNext(search, &itemRef);
82    CFRelease(search);
83    if (ret == errSecItemNotFound)
84	errx(1, "didn't find private key for: %s", label);
85    else if (ret)
86	errx(1, "Failure in SecKeychainSearchCopyNext: %d for %s",
87	     ret, label);
88
89    return itemRef;
90}
91
92static void
93testSign(SecKeychainItemRef itemRef)
94{
95    const CSSM_ACCESS_CREDENTIALS *creds;
96    CSSM_CC_HANDLE sigHandle = 0;
97    CSSM_CSP_HANDLE cspHandle;
98    const CSSM_KEY *cssmKey;
99    uint8_t to[4096];
100    CSSM_DATA sig, in;
101    CSSM_RETURN cret;
102    SecKeyRef privKeyRef = (SecKeyRef)itemRef;
103    OSStatus ret;
104
105
106    cret = SecKeyGetCSSMKey(privKeyRef, &cssmKey);
107    if(cret) abort();
108
109    cret = SecKeyGetCSPHandle(privKeyRef, &cspHandle);
110    if(cret) abort();
111
112    ret = SecKeyGetCredentials(privKeyRef, CSSM_ACL_AUTHORIZATION_SIGN,
113			       kSecCredentialTypeDefault, &creds);
114    if(ret) abort();
115
116    ret = CSSM_CSP_CreateSignatureContext(cspHandle, CSSM_ALGID_RSA,
117					  creds, cssmKey, &sigHandle);
118    if(ret) abort();
119
120    in.Data = (uint8 *)"test signature";
121    in.Length = strlen((char *)in.Data);
122
123    sig.Data = to;
124    sig.Length = sizeof(to);
125
126    cret = CSSM_SignData(sigHandle, &in, 1, CSSM_ALGID_NONE, &sig);
127    if(cret)
128	errx(1, "failed to sign");
129
130    CSSM_DeleteContext(sigHandle);
131
132    printf("signing worked\n");
133}
134
135static int
136checkOnACLAlreadyAndRemoveAny(SecKeychainItemRef itemRef,
137			      const char *trustedAppName)
138{
139    SecAccessRef secaccess;
140    CFArrayRef aclList;
141    OSStatus ret;
142    SecACLRef acl;
143    int modified = 0, found = 0;
144    uint32 i, j;
145
146    ret = SecKeychainItemCopyAccess(itemRef, &secaccess);
147    if (ret)
148	errx(1, "SecKeychainItemCopyAccess");
149
150    ret = SecAccessCopyACLList (secaccess, &aclList);
151    if (ret)
152	errx(1, "SecAccessCopyACLList");
153
154    for (i = 0; i < CFArrayGetCount(aclList) && !found; i++) {
155	CSSM_ACL_KEYCHAIN_PROMPT_SELECTOR prompt;
156	CFStringRef description;
157	CFArrayRef apps;
158
159	acl = (SecACLRef)CFArrayGetValueAtIndex(aclList, i);
160
161	/*
162	 * XXX SecACLCopySimpleContents failes on "complex"
163	 * entries, what is that and should I care ?
164	 */
165
166	ret = SecACLCopySimpleContents(acl, &apps, &description, &prompt);
167	if (ret) {
168	    /* warnx("SecACLCopySimpleContents: %d: %d", i, (int)ret); */
169	    continue;
170	}
171
172	CFRelease(description);
173
174	if (apps == NULL) {
175	    SecACLRemove(acl);
176	    modified = 1;
177	    printf("removing any access\n");
178	} else {
179	    for (j = 0; j < CFArrayGetCount(apps) && !found; j++) {
180		SecTrustedApplicationRef app;
181		CFDataRef data;
182
183		app = (SecTrustedApplicationRef)CFArrayGetValueAtIndex(apps, j);
184
185		ret = SecTrustedApplicationCopyData(app, &data);
186		if (ret)
187		    errx(1, "SecTrustedApplicationCopyData");
188
189		/* http://lists.apple.com/archives/apple-cdsa/2007/Dec/msg00021.html */
190		if (strcmp(trustedAppName, (char *)CFDataGetBytePtr(data)) == 0)
191		    found = 1;
192
193		CFRelease(data);
194	    }
195	    CFRelease(apps);
196	}
197    }
198
199    if (modified) {
200	ret = SecKeychainItemSetAccess(itemRef, secaccess);
201	if (ret)
202	    errx(1, "SecKeychainItemSetAccess: %d (any access)", ret);
203    }
204
205    CFRelease(aclList);
206    CFRelease(secaccess);
207
208    return found;
209}
210
211static void
212addTrustedApplication(SecKeychainItemRef itemRef,
213		      const char *trustedAppName,
214		      const char *identity)
215{
216    CSSM_ACL_KEYCHAIN_PROMPT_SELECTOR prompt;
217    CSSM_ACL_AUTHORIZATION_TAG tags[6];
218    CFArrayRef trustedApps;
219    SecAccessRef secaccess;
220    SecACLRef acl;
221    OSStatus ret;
222
223    /*
224     * Check if we are already on the acl list
225     */
226
227    if (checkOnACLAlreadyAndRemoveAny(itemRef, trustedAppName)) {
228	printf("%s already in acl\n", trustedAppName);
229	return;
230    }
231
232    /*
233     * Add new acl entry for our trusted app
234     */
235
236    ret = SecKeychainItemCopyAccess(itemRef, &secaccess);
237    if (ret)
238	errx(1, "SecKeychainItemCopyAccess");
239
240    prompt.version = CSSM_ACL_KEYCHAIN_PROMPT_CURRENT_VERSION;
241    prompt.flags = 0;
242
243    trustedApps = getTrustedApps(trustedAppName);
244    if (trustedApps == NULL)
245	errx(1, "getTrustedApps");
246
247    ret = SecACLCreateFromSimpleContents(secaccess, trustedApps,
248					 CFSTR("lkdc-acl"), &prompt,
249					 &acl);
250    if (ret)
251	errx(1, "SecACLCreateFromSimpleContents");
252
253    tags[0] = CSSM_ACL_AUTHORIZATION_DECRYPT;
254    tags[1] = CSSM_ACL_AUTHORIZATION_DERIVE;
255    tags[2] = CSSM_ACL_AUTHORIZATION_EXPORT_CLEAR;
256    tags[3] = CSSM_ACL_AUTHORIZATION_EXPORT_WRAPPED;
257    tags[4] = CSSM_ACL_AUTHORIZATION_MAC;
258    tags[5] = CSSM_ACL_AUTHORIZATION_SIGN;
259
260    ret = SecACLSetAuthorizations(acl, tags, sizeof(tags) / sizeof(tags[0]));
261    if (ret)
262	errx(1, "SecACLSetAuthorizations");
263
264    ret = SecKeychainItemSetAccess(itemRef, secaccess);
265    if (ret)
266	errx(1, "SecKeychainItemSetAccess: %d", ret);
267
268    printf("added %s to acl for %s\n", trustedAppName, identity);
269
270    CFRelease(secaccess);
271}
272
273static void
274deleteAllACL(SecKeychainItemRef itemRef)
275{
276    SecAccessRef secaccess;
277    CFArrayRef aclList;
278    OSStatus ret;
279    CFIndex i;
280
281    ret = SecKeychainItemCopyAccess(itemRef, &secaccess);
282    if (ret)
283	errx(1, "deleteAllACL: SecKeychainItemCopyAccess: %d", ret);
284
285    ret = SecAccessCopyACLList (secaccess, &aclList);
286    if (ret)
287	errx(1, "deleteAllACL: SecAccessCopyACLList %d", ret);
288
289    for (i = 0; i < CFArrayGetCount(aclList); i++) {
290	CSSM_ACL_AUTHORIZATION_TAG tags[30];
291	uint32 j, tagcount = sizeof(tags)/sizeof(tags[0]);
292	SecACLRef acl;
293
294	acl = (SecACLRef)CFArrayGetValueAtIndex(aclList, i);
295
296	ret = SecACLGetAuthorizations(acl, tags, &tagcount);
297	if (ret)
298	    errx(1, "deleteAllACL: SecACLGetAuthorizations: %d", ret);
299
300	for (j = 0; j < tagcount; j++) {
301	    /* skip owner */
302	    if (tags[j] == CSSM_ACL_AUTHORIZATION_CHANGE_ACL)
303		break;
304	}
305	if (j < tagcount)
306	    continue;
307
308	ret = SecACLRemove(acl);
309	if (ret)
310	    errx(1, "deleteAllACL: SecACLRemove: %d", ret);
311    }
312
313    ret = SecKeychainItemSetAccess(itemRef, secaccess);
314    if (ret)
315	errx(1, "deleteAllACL: SecKeychainItemSetAccess: %d", ret);
316
317    CFRelease(secaccess);
318}
319
320
321static void
322listACL(SecKeychainItemRef itemRef)
323{
324    SecAccessRef secaccess;
325    CFArrayRef aclList;
326    SecACLRef acl;
327    OSStatus ret;
328    uint32 i, j;
329
330    ret = SecKeychainItemCopyAccess(itemRef, &secaccess);
331    if (ret)
332	errx(1, "SecKeychainItemCopyAccess");
333
334    ret = SecAccessCopyACLList (secaccess, &aclList);
335    if (ret)
336	errx(1, "SecAccessCopyACLList");
337
338    for (i = 0; i < CFArrayGetCount(aclList); i++) {
339	CSSM_ACL_KEYCHAIN_PROMPT_SELECTOR prompt;
340	CFStringRef description;
341	CFArrayRef apps;
342
343	acl = (SecACLRef)CFArrayGetValueAtIndex(aclList, i);
344
345	printf("acl: %lu\n", (unsigned long)i);
346
347	/*
348	 * XXX SecACLCopySimpleContents failes on "complex"
349	 * entries, what is that and should I care ?
350	 */
351
352	ret = SecACLCopySimpleContents(acl, &apps, &description, &prompt);
353	if (ret) {
354	    /* warnx("SecACLCopySimpleContents: %d: %d", i, (int)ret); */
355	    continue;
356	}
357
358	if (description) {
359	    size_t len;
360	    char *str;
361
362	    len = CFStringGetMaximumSizeForEncoding(
363		CFStringGetLength(description), kCFStringEncodingUTF8);
364	    len += 1;
365
366	    str = malloc(len);
367	    if (str == NULL)
368		errx(1, "out of memory");
369
370	    CFStringGetCString(description, str, len, kCFStringEncodingUTF8);
371	    printf("\tdescription: %s\n",  str);
372	    free(str);
373	    CFRelease(description);
374	}
375
376	{
377	    CSSM_ACL_AUTHORIZATION_TAG tags[30];
378	    uint32 k, tagcount = sizeof(tags)/sizeof(tags[0]);
379
380	    ret = SecACLGetAuthorizations(acl, tags, &tagcount);
381	    if (ret)
382		errx(1, "listAcl: SecACLGetAuthorizations: %d", ret);
383
384	    printf("\ttags: ");
385	    for (k = 0; k < tagcount; k++)
386		printf("%d ", tags[k]);
387	    printf("\n");
388
389	}
390
391	if (apps == NULL) {
392	    printf("\tany ACL\n");
393	} else {
394	    for (j = 0; j < CFArrayGetCount(apps); j++) {
395		SecTrustedApplicationRef app;
396		CFDataRef data;
397
398		app = (SecTrustedApplicationRef)CFArrayGetValueAtIndex(apps, j);
399
400		ret = SecTrustedApplicationCopyData(app, &data);
401		if (ret)
402		    errx(1, "SecTrustedApplicationCopyData");
403
404		/* http://lists.apple.com/archives/apple-cdsa/2007/Dec/msg00021.html */
405		printf("\tapp: %s\n", (char *)CFDataGetBytePtr(data));
406		CFRelease(data);
407	    }
408	    CFRelease(apps);
409	}
410    }
411
412    CFRelease(aclList);
413    CFRelease(secaccess);
414}
415
416static void
417usage(int exit_code)
418{
419    printf("%s -s identity -t\t\t\ttest using identitys key\n",
420	   getprogname());
421    printf("%s -s identity -a application\tadd appliction to acl\n",
422	   getprogname());
423    printf("%s -s identity -D\t\t\tdelete all ACL\n",
424	   getprogname());
425    printf("%s -s identity -l\t\t\tlist ACL\n",
426	   getprogname());
427    exit(exit_code);
428}
429
430extern int optind;
431extern char *optarg;
432
433int
434main(int argc, char **argv)
435{
436    SecKeychainItemRef itemRef;
437    SecKeychainRef keychain = NULL;
438    int ch, test_sign = 0, delete_all = 0, do_list = 0;
439    const char *trustedAppName = NULL;
440    const char *identity = NULL;
441
442    setprogname(argv[0]);
443
444    while ((ch = getopt(argc, argv, "Da:s:tl?h")) != -1) {
445	switch(ch) {
446	case 'D':
447	    delete_all = 1;
448	    break;
449	case 'a':
450	    trustedAppName = optarg;
451	    break;
452	case 'l':
453	    do_list = 1;
454	    break;
455	case 's':
456	    identity = optarg;
457	    break;
458	case 't':
459	    test_sign = 1;
460	    break;
461	case '?':
462	case 'h':
463	    usage(0);
464	    break;
465	}
466    }
467
468    if (identity == NULL) {
469	fprintf(stderr, "no identity given\n");
470	usage(1);
471    }
472
473    SecKeychainSetUserInteractionAllowed(FALSE);
474
475    itemRef = findKeychainItem(keychain, identity);
476
477    if (test_sign)
478	testSign(itemRef);
479
480    if (delete_all)
481	deleteAllACL(itemRef);
482
483    if (trustedAppName)
484	addTrustedApplication(itemRef, trustedAppName, identity);
485
486    if (do_list)
487	listACL(itemRef);
488
489    return 0;
490}
491