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_set_settings.c
24 */
25
26#include "keychain_set_settings.h"
27#include "keychain_utilities.h"
28#include "readline.h"
29#include "security.h"
30
31#include <limits.h>
32#include <stdio.h>
33#include <stdlib.h>
34#include <string.h>
35#include <unistd.h>
36#include <Security/SecKeychain.h>
37#include <Security/SecKeychainPriv.h>
38
39#define PW_BUF_SIZE 512				/* size of buffer to alloc for password */
40
41
42static int
43do_keychain_set_settings(const char *keychainName, SecKeychainSettings newKeychainSettings)
44{
45	SecKeychainRef keychain = NULL;
46	OSStatus result;
47
48	if (keychainName)
49	{
50		keychain = keychain_open(keychainName);
51		if (!keychain)
52		{
53			result = 1;
54			goto cleanup;
55		}
56	}
57	result = SecKeychainSetSettings(keychain, &newKeychainSettings);
58	if (result)
59	{
60		sec_error("SecKeychainSetSettings %s: %s", keychainName ? keychainName : "<NULL>", sec_errstr(result));
61	}
62
63cleanup:
64	if (keychain)
65		CFRelease(keychain);
66
67	return result;
68}
69
70
71static int
72do_keychain_set_password(const char *keychainName, const char* oldPassword, const char* newPassword)
73{
74	SecKeychainRef keychain = NULL;
75	OSStatus result = 1;
76	UInt32 oldLen = (oldPassword) ? strlen(oldPassword) : 0;
77	UInt32 newLen = (newPassword) ? strlen(newPassword) : 0;
78	char *oldPass = (oldPassword) ? (char*)oldPassword : NULL;
79	char *newPass = (newPassword) ? (char*)newPassword : NULL;
80	char *oldBuf = NULL;
81	char *newBuf = NULL;
82
83	if (keychainName)
84	{
85		keychain = keychain_open(keychainName);
86		if (!keychain)
87		{
88			result = 1;
89			goto cleanup;
90		}
91	}
92
93	if (!oldPass) {
94		/* prompt for old password */
95		char *pBuf = getpass("Old Password: ");
96		if (pBuf) {
97			oldBuf = (char*) calloc(PW_BUF_SIZE, 1);
98			oldLen = strlen(pBuf);
99			memcpy(oldBuf, pBuf, oldLen);
100			bzero(pBuf, oldLen);
101			oldPass = oldBuf;
102		}
103	}
104
105	if (!newPass) {
106		/* prompt for new password */
107		char *pBuf = getpass("New Password: ");
108		if (pBuf) {
109			newBuf = (char*) calloc(PW_BUF_SIZE, 1);
110			newLen = strlen(pBuf);
111			memcpy(newBuf, pBuf, newLen);
112			bzero(pBuf, newLen);
113		}
114		/* confirm new password */
115		pBuf = getpass("Retype New Password: ");
116		if (pBuf) {
117			UInt32 confirmLen = strlen(pBuf);
118			if (confirmLen == newLen && newBuf &&
119				!memcmp(pBuf, newBuf, newLen)) {
120				newPass = newBuf;
121			}
122			bzero(pBuf, confirmLen);
123		}
124	}
125
126	if (!oldPass || !newPass) {
127		sec_error("try again");
128		goto cleanup;
129	}
130
131	/* lock keychain first to remove existing credentials */
132	(void)SecKeychainLock(keychain);
133
134	/* change the password */
135	result = SecKeychainChangePassword(keychain, oldLen, oldPass, newLen, newPass);
136	if (result)
137	{
138		sec_error("error changing password for \"%s\": %s",
139			keychainName ? keychainName : "<NULL>", sec_errstr(result));
140	}
141
142cleanup:
143	/* if we allocated password buffers, zero and free them */
144	if (oldBuf) {
145		bzero(oldBuf, PW_BUF_SIZE);
146		free(oldBuf);
147	}
148	if (newBuf) {
149		bzero(newBuf, PW_BUF_SIZE);
150		free(newBuf);
151	}
152	if (keychain) {
153		CFRelease(keychain);
154	}
155	return result;
156}
157
158
159int
160keychain_set_settings(int argc, char * const *argv)
161{
162	char *keychainName = NULL;
163	int ch, result = 0;
164    SecKeychainSettings newKeychainSettings =
165		{ SEC_KEYCHAIN_SETTINGS_VERS1, FALSE, FALSE, INT_MAX };
166
167    while ((ch = getopt(argc, argv, "hlt:u")) != -1)
168	{
169		switch  (ch)
170		{
171        case 'l':
172            newKeychainSettings.lockOnSleep = TRUE;
173			break;
174		case 't':
175            newKeychainSettings.lockInterval = atoi(optarg);
176			break;
177		case 'u':
178            newKeychainSettings.useLockInterval = TRUE;
179			break;
180		case '?':
181		default:
182			result = 2; /* @@@ Return 2 triggers usage message. */
183			goto cleanup;
184		}
185	}
186
187	if (newKeychainSettings.lockInterval != INT_MAX) {
188		// -t was specified, which implies -u
189		newKeychainSettings.useLockInterval = TRUE;
190	} else {
191		// -t was unspecified, so revert to no timeout
192		newKeychainSettings.useLockInterval = FALSE;
193	}
194
195	argc -= optind;
196	argv += optind;
197
198	if (argc == 1)
199	{
200		keychainName = argv[0];
201		if (*keychainName == '\0')
202		{
203			result = 2;
204			goto cleanup;
205		}
206	}
207	else if (argc != 0)
208	{
209		result = 2;
210		goto cleanup;
211	}
212
213	result = do_keychain_set_settings(keychainName, newKeychainSettings);
214
215cleanup:
216
217	return result;
218}
219
220int
221keychain_set_password(int argc, char * const *argv)
222{
223	char *keychainName = NULL;
224	char *oldPassword = NULL;
225	char *newPassword = NULL;
226	int ch, result = 0;
227
228    while ((ch = getopt(argc, argv, "ho:p:")) != -1)
229	{
230		switch  (ch)
231		{
232        case 'o':
233            oldPassword = optarg;
234			break;
235        case 'p':
236            newPassword = optarg;
237			break;
238		case '?':
239		default:
240			result = 2; /* @@@ Return 2 triggers usage message. */
241			goto cleanup;
242		}
243	}
244
245	argc -= optind;
246	argv += optind;
247
248	if (argc == 1)
249	{
250		keychainName = argv[0];
251		if (*keychainName == '\0')
252		{
253			result = 2;
254			goto cleanup;
255		}
256	}
257	else if (argc != 0)
258	{
259		result = 2;
260		goto cleanup;
261	}
262
263	result = do_keychain_set_password(keychainName, oldPassword, newPassword);
264
265cleanup:
266
267	return result;
268}
269
270