1/*
2 * Copyright (c) 2003-2009 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 * keychain_list.c
24 */
25
26#include "keychain_list.h"
27
28#include "keychain_utilities.h"
29#include "security.h"
30
31#include <stdio.h>
32#include <string.h>
33#include <sys/param.h>
34#include <unistd.h>
35#include <CoreFoundation/CFArray.h>
36#include <Security/SecKeychain.h>
37
38// SecKeychainCopyLogin
39#include <Security/SecKeychainPriv.h>
40
41
42typedef enum
43{
44	kList,
45	kAdd,
46	kRemove,
47	kSet,
48} list_operation;
49
50static void
51display_name(const void *value, void *context)
52{
53	SecKeychainRef keychain = (SecKeychainRef)value;
54	UInt32 pathLength = MAXPATHLEN;
55	char pathName[MAXPATHLEN + 1];
56	OSStatus result = SecKeychainGetPath(keychain, &pathLength, pathName);
57	if (result)
58		sec_error("SecKeychainGetPath %p: %s", keychain, sec_errstr(result));
59	else
60		fprintf(stdout, "    \"%*s\"\n", (int)pathLength, pathName);
61}
62
63
64static void
65display_list(const char *desc, CFTypeRef keychainOrArray)
66{
67	if (desc && strlen(desc))
68		fprintf(stdout, "%s\n", desc);
69
70	if (!keychainOrArray)
71	{
72		fprintf(stdout, " <NULL>\n");
73	}
74	else if (CFGetTypeID(keychainOrArray) == SecKeychainGetTypeID())
75	{
76		display_name(keychainOrArray, NULL);
77	}
78	else
79	{
80		CFArrayRef array = (CFArrayRef)keychainOrArray;
81		CFRange range = { 0, CFArrayGetCount(array) };
82		CFArrayApplyFunction(array, range, display_name, NULL);
83	}
84}
85
86static int
87parse_domain(const char *name, SecPreferencesDomain *domain)
88{
89	size_t len = strlen(name);
90
91	if (!strncmp("user", name, len))
92		*domain = kSecPreferencesDomainUser;
93	else if (!strncmp("system", name, len))
94		*domain = kSecPreferencesDomainSystem;
95	else if (!strncmp("common", name, len))
96		*domain = kSecPreferencesDomainCommon;
97	else if (!strncmp("dynamic", name, len))
98		*domain = kSecPreferencesDomainDynamic;
99	else
100	{
101		sec_error("Invalid domain: %s", name);
102		return 2;
103	}
104
105	return 0;
106}
107
108const char *
109domain2str(SecPreferencesDomain domain)
110{
111	switch (domain)
112	{
113	case kSecPreferencesDomainUser:
114		return "user";
115	case kSecPreferencesDomainSystem:
116		return "system";
117	case kSecPreferencesDomainCommon:
118		return "common";
119	case kSecPreferencesDomainDynamic:
120		return "dynamic";
121	default:
122		return "unknown";
123	}
124}
125
126int
127keychain_list(int argc, char * const *argv)
128{
129	CFTypeRef keychainOrArray = NULL;
130	CFArrayRef searchList = NULL;
131	list_operation operation = kList;
132	SecPreferencesDomain domain = kSecPreferencesDomainUser;
133	Boolean use_domain = false;
134	int ch, result = 0;
135	OSStatus status;
136
137	while ((ch = getopt(argc, argv, "d:hs")) != -1)
138	{
139		switch  (ch)
140		{
141		case 'd':
142			result = parse_domain(optarg, &domain);
143			if (result)
144				return result;
145			use_domain = true;
146			break;
147		case 's':
148			operation = kSet;
149			break;
150		case '?':
151		default:
152			return 2; /* @@@ Return 2 triggers usage message. */
153		}
154	}
155
156	argc -= optind;
157	argv += optind;
158
159	switch (operation)
160	{
161	case kAdd:
162		result = 1;
163		break;
164	case kRemove:
165		result = 1;
166		break;
167	case kList:
168		if (argc > 0)
169			result = 2; // Show usage
170		else
171		{
172			if (use_domain)
173			{
174				status = SecKeychainCopyDomainSearchList(domain, &searchList);
175				if (status)
176				{
177					sec_error("SecKeychainCopyDomainSearchList %s: %s", domain2str(domain), sec_errstr(status));
178					result = 1;
179				}
180				else
181				{
182#if 0
183					fprintf(stdout, "%s search list: ", domain2str(domain));
184#endif
185					display_list("", searchList);
186				}
187			}
188			else
189			{
190				status = SecKeychainCopySearchList(&searchList);
191				if (status)
192				{
193					sec_perror("SecKeychainCopySearchList", status);
194					result = 1;
195				}
196				else
197				{
198#if 0
199					display_list("search list:", searchList);
200#else
201					display_list("", searchList);
202#endif
203				}
204			}
205		}
206		break;
207	case kSet:
208		keychainOrArray = keychain_create_array(argc, argv);
209		if (argc == 0)
210			searchList = CFArrayCreate(NULL, NULL, 0, &kCFTypeArrayCallBacks);
211		else if (argc == 1)
212			searchList = CFArrayCreate(NULL, &keychainOrArray, 1, &kCFTypeArrayCallBacks);
213		else
214			searchList = (CFArrayRef)CFRetain(keychainOrArray);
215
216		if (use_domain)
217		{
218			status = SecKeychainSetDomainSearchList(domain, searchList);
219			if (status)
220			{
221				sec_error("SecKeychainSetDomainSearchList %s: %s", domain2str(domain), sec_errstr(status));
222				result = 1;
223			}
224		}
225		else
226		{
227			status = SecKeychainSetSearchList(searchList);
228			if (status)
229			{
230				sec_perror("SecKeychainSetSearchList", status);
231				result = 1;
232			}
233		}
234		break;
235	}
236
237	if (keychainOrArray)
238		CFRelease(keychainOrArray);
239	if (searchList)
240		CFRelease(searchList);
241
242	return result;
243}
244
245int
246keychain_default(int argc, char * const *argv)
247{
248	SecPreferencesDomain domain = kSecPreferencesDomainUser;
249	SecKeychainRef keychain = NULL;
250	Boolean use_domain = false;
251	Boolean do_set = false;
252	int ch, result = 0;
253	OSStatus status;
254
255	while ((ch = getopt(argc, argv, "d:hs")) != -1)
256	{
257		switch  (ch)
258		{
259		case 'd':
260			result = parse_domain(optarg, &domain);
261			if (result)
262				return result;
263			use_domain = true;
264			break;
265		case 's':
266			do_set = true;
267			break;
268		case '?':
269		default:
270			return 2; /* @@@ Return 2 triggers usage message. */
271		}
272	}
273
274	argc -= optind;
275	argv += optind;
276
277	if (do_set)
278	{
279		if (argc == 1)
280			keychain = (SecKeychainRef)keychain_create_array(argc, argv);
281		else if (argc > 0)
282			return 2;
283
284		if (use_domain)
285		{
286			status = SecKeychainSetDomainDefault(domain, keychain);
287			if (status)
288			{
289				sec_error("SecKeychainSetDomainDefault %s: %s", domain2str(domain), sec_errstr(status));
290				result = 1;
291			}
292		}
293		else
294		{
295			status = SecKeychainSetDefault(keychain);
296			if (status)
297			{
298				sec_perror("SecKeychainSetDefault", status);
299				result = 1;
300			}
301		}
302	}
303	else
304	{
305		if (argc > 0)
306			return 2;
307
308		if (use_domain)
309		{
310			status = SecKeychainCopyDomainDefault(domain, &keychain);
311			if (status)
312			{
313				sec_error("SecKeychainCopyDomainDefault %s: %s", domain2str(domain), sec_errstr(status));
314				result = 1;
315			}
316			else
317			{
318#if 0
319				fprintf(stdout, "default %s keychain: ", domain2str(domain));
320#endif
321				display_list("", keychain);
322			}
323		}
324		else
325		{
326			status = SecKeychainCopyDefault(&keychain);
327			if (status)
328			{
329				sec_perror("SecKeychainCopyDefault", status);
330				result = 1;
331			}
332			else
333			{
334#if 0
335				display_list("default keychain: ", keychain);
336#else
337				display_list("", keychain);
338#endif
339			}
340		}
341	}
342
343	if (keychain)
344		CFRelease(keychain);
345
346	return result;
347}
348
349int
350keychain_login(int argc, char * const *argv)
351{
352	SecPreferencesDomain domain = kSecPreferencesDomainUser;
353	SecKeychainRef keychain = NULL;
354	Boolean use_domain = false;
355	Boolean do_set = false;
356	int ch, result = 0;
357	OSStatus status;
358
359	while ((ch = getopt(argc, argv, "d:hs")) != -1)
360	{
361		switch  (ch)
362		{
363		case 'd':
364			result = parse_domain(optarg, &domain);
365			if (result)
366				return result;
367			use_domain = true;
368			break;
369		case 's':
370			do_set = true;
371			break;
372		case '?':
373		default:
374			return 2; /* @@@ Return 2 triggers usage message. */
375		}
376	}
377
378	argc -= optind;
379	argv += optind;
380
381	if (do_set)
382	{
383		if (argc == 1)
384			keychain = (SecKeychainRef)keychain_create_array(argc, argv);
385		else if (argc > 0)
386			return 2;
387
388#if 0
389		if (use_domain)
390		{
391			status = SecKeychainSetDomainLogin(domain, keychain);
392			if (status)
393			{
394				sec_error("SecKeychainSetDomainLogin %s: %s", domain2str(domain), sec_errstr(status));
395				result = 1;
396			}
397		}
398		else
399		{
400			status = SecKeychainSetLogin(keychain);
401			if (status)
402			{
403				sec_perror("SecKeychainSetLogin", status);
404				result = 1;
405			}
406		}
407#else
408		result = 1;
409#endif
410	}
411	else
412	{
413		if (argc > 0)
414			return 2;
415
416		if (use_domain)
417		{
418#if 0
419			status = SecKeychainCopyDomainLogin(domain, &keychain);
420			if (status)
421			{
422				sec_error("SecKeychainCopyDomainLogin %s: %s", domain2str(domain), sec_errstr(status));
423				result = 1;
424			}
425			else
426			{
427#if 0
428				fprintf(stdout, "login %s keychain: ", domain2str(domain));
429#endif
430				display_list("", keychain);
431			}
432#else
433			result = 1;
434#endif
435		}
436		else
437		{
438			status = SecKeychainCopyLogin(&keychain);
439			if (status)
440			{
441				sec_perror("SecKeychainCopyLogin", status);
442				result = 1;
443			}
444			else
445			{
446#if 0
447				display_list("login keychain: ", keychain);
448#else
449				display_list("", keychain);
450#endif
451			}
452		}
453	}
454
455	if (keychain)
456		CFRelease(keychain);
457
458	return result;
459}
460