]> git.saurik.com Git - apple/security.git/blob - SecurityServer/systemkeychain.cpp
Security-177.tar.gz
[apple/security.git] / SecurityServer / systemkeychain.cpp
1 /*
2 * Copyright (c) 2000-2001 Apple Computer, Inc. All Rights Reserved.
3 *
4 * The contents of this file constitute Original Code as defined in and are
5 * subject to the Apple Public Source License Version 1.2 (the 'License').
6 * You may not use this file except in compliance with the License. Please obtain
7 * a copy of the License at http://www.apple.com/publicsource and read it before
8 * using this file.
9 *
10 * This Original Code and all software distributed under the License are
11 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS
12 * OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, INCLUDING WITHOUT
13 * LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
14 * PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. Please see the License for the
15 * specific language governing rights and limitations under the License.
16 */
17
18
19 //
20 // systemkeychain command - set up and manipulate system-unlocked keychains
21 //
22 #include <Security/dlclient.h>
23 #include <Security/cryptoclient.h>
24 #include <Security/wrapkey.h>
25 #include <Security/genkey.h>
26 #include <Security/Schema.h>
27 #include "ssblob.h"
28 #include <cstdarg>
29
30 using namespace SecurityServer;
31 using namespace CssmClient;
32 using namespace UnixPlusPlus;
33
34
35 static const char *unlockConfig = kSystemUnlockFile;
36
37
38 //
39 // Values set from command-line options
40 //
41 const char *systemKCName = kSystemKeychainDir kSystemKeychainName;
42 bool verbose = false;
43 bool createIfNeeded = false;
44 bool force = false;
45
46
47 //
48 // CSSM record attribute names
49 //
50 static const CSSM_DB_ATTRIBUTE_INFO dlInfoLabel = {
51 CSSM_DB_ATTRIBUTE_NAME_AS_STRING,
52 {"Label"},
53 CSSM_DB_ATTRIBUTE_FORMAT_BLOB
54 };
55
56
57
58 //
59 // Local functions
60 void usage();
61 void createSystemKeychain(const char *kcName, const char *passphrase);
62 void extract(const char *srcName, const char *dstName);
63 void test(const char *kcName);
64
65 void notice(const char *fmt, ...);
66 void fail(const char *fmt, ...);
67
68 void masterKeyIndex(Db &db, CssmOwnedData &index);
69 void labelForMasterKey(Db &db, CssmOwnedData &data);
70 void deleteKey(Db &db, const CssmData &label); // delete key with this label
71
72
73 //
74 // Main program: parse options and dispatch, catching exceptions
75 //
76 int main (int argc, char * argv[])
77 {
78 enum Action {
79 showUsage,
80 setupSystem,
81 copyKey,
82 testUnlock
83 } action = showUsage;
84
85 extern int optind;
86 extern char *optarg;
87 int arg;
88 while ((arg = getopt(argc, argv, "cCfk:stv")) != -1) {
89 switch (arg) {
90 case 'c':
91 createIfNeeded = true;
92 break;
93 case 'C':
94 action = setupSystem;
95 break;
96 case 'f':
97 force = true;
98 break;
99 case 'k':
100 systemKCName = optarg;
101 break;
102 case 's':
103 action = copyKey;
104 break;
105 case 't':
106 action = testUnlock;
107 break;
108 case 'v':
109 verbose = true;
110 break;
111 default:
112 usage();
113 }
114 }
115 try {
116 switch (action) {
117 case setupSystem:
118 if (optind < argc - 1)
119 usage();
120 createSystemKeychain(systemKCName, argv[optind]);
121 break;
122 case copyKey:
123 if (optind == argc)
124 usage();
125 do {
126 extract(argv[optind], systemKCName);
127 } while (argv[++optind]);
128 break;
129 case testUnlock:
130 test(systemKCName);
131 break;
132 default:
133 usage();
134 }
135 exit(0);
136 } catch (const CssmError &error) {
137 cssmPerror(systemKCName, error.cssmError());
138 exit(1);
139 } catch (const UnixError &error) {
140 fail("%s: %s", systemKCName, strerror(error.error));
141 exit(1);
142 } catch (...) {
143 fail("Unexpected exception");
144 exit(1);
145 }
146 }
147
148
149 //
150 // Partial usage message (some features aren't worth emphasizing...)
151 //
152 void usage()
153 {
154 fprintf(stderr, "Usage: systemkeychain -S [passphrase] # (re)create system root keychain"
155 "\n\tsystemkeychain [-k destination-keychain] -s source-keychain ..."
156 "\n");
157 exit(2);
158 }
159
160
161 //
162 // Create a keychain and set it up as the system-root secret
163 //
164 void createSystemKeychain(const char *kcName, const char *passphrase)
165 {
166 // for the default path only, make sure the directory exists
167 if (!strcmp(kcName, kSystemKeychainDir kSystemKeychainName))
168 ::mkdir(kSystemKeychainDir, 0755);
169
170 CSP csp(gGuidAppleCSPDL);
171 DL dl(gGuidAppleCSPDL);
172
173 // create the keychain, using appropriate credentials
174 Db db(dl, kcName);
175 CssmAllocator &alloc = db.allocator();
176 AutoCredentials cred(alloc); // will leak, but we're quitting soon :-)
177 CSSM_CSP_HANDLE cspHandle = csp->handle();
178 Key masterKey;
179 if (passphrase) {
180 // use this passphrase
181 cred += TypedList(alloc, CSSM_SAMPLE_TYPE_KEYCHAIN_CHANGE_LOCK,
182 new(alloc) ListElement(CSSM_SAMPLE_TYPE_PASSWORD),
183 new(alloc) ListElement(StringData(passphrase)));
184 db->accessCredentials(&cred);
185 } else {
186 // generate a random key
187 notice("warning: this keychain cannot be unlocked with any passphrase");
188 GenerateKey generate(csp, CSSM_ALGID_3DES_3KEY_EDE, 64 * 3);
189 masterKey = generate(KeySpec(CSSM_KEYUSE_ANY,
190 CSSM_KEYATTR_RETURN_REF | CSSM_KEYATTR_EXTRACTABLE));
191 cred += TypedList(alloc, CSSM_SAMPLE_TYPE_KEYCHAIN_CHANGE_LOCK,
192 new(alloc) ListElement(CSSM_WORDID_SYMMETRIC_KEY),
193 new(alloc) ListElement(CssmData::wrap(cspHandle)),
194 new(alloc) ListElement(CssmData::wrap(static_cast<const CssmKey &>(masterKey))));
195 db->accessCredentials(&cred);
196 }
197 db->dbInfo(&KeychainCore::Schema::DBInfo); // Set the standard schema
198 try {
199 db->create();
200 } catch (const CssmError &error) {
201 if (error.cssmError() == CSSMERR_DL_DATASTORE_ALREADY_EXISTS && force) {
202 notice("recreating %s", kcName);
203 unlink(kcName);
204 db->create();
205 } else
206 throw;
207 }
208 chmod(db->name(), 0644);
209
210 // extract the key into the CSPDL
211 DeriveKey derive(csp, CSSM_ALGID_KEYCHAIN_KEY, CSSM_ALGID_3DES_3KEY, 3 * 64);
212 CSSM_DL_DB_HANDLE dlDb = db->handle();
213 CssmData dlDbData = CssmData::wrap(dlDb);
214 CssmKey refKey;
215 KeySpec spec(CSSM_KEYUSE_ANY,
216 CSSM_KEYATTR_RETURN_REF | CSSM_KEYATTR_EXTRACTABLE);
217 derive(&dlDbData, spec, refKey);
218
219 // now extract the raw keybits
220 CssmKey rawKey;
221 WrapKey wrap(csp, CSSM_ALGID_NONE);
222 wrap(refKey, rawKey);
223
224 // form the evidence record
225 UnlockBlob blob;
226 blob.initialize(0);
227 CssmAutoData index(CssmAllocator::standard());
228 masterKeyIndex(db, index);
229 memcpy(&blob.signature, index.data(), sizeof(blob.signature));
230 memcpy(blob.masterKey, rawKey.data(), sizeof(blob.masterKey));
231
232 // write it out, forcibly overwriting an existing file
233 string tempFile(string(unlockConfig) + ",");
234 FileDesc blobFile(tempFile, O_WRONLY | O_CREAT | O_TRUNC, 0400);
235 if (blobFile.write(blob) != sizeof(blob)) {
236 unlink(tempFile.c_str());
237 fail("unable to write %s", tempFile.c_str());
238 }
239 blobFile.close();
240 ::rename(tempFile.c_str(), unlockConfig);
241
242 notice("%s installed as system keychain", kcName);
243 }
244
245
246 //
247 // Extract the master secret from a keychain and install it in another keychain for unlocking
248 //
249 void extract(const char *srcName, const char *dstName)
250 {
251 CSP csp(gGuidAppleCSPDL);
252 DL dl(gGuidAppleCSPDL);
253
254 // open source database
255 Db srcDb(dl, srcName);
256
257 // open destination database
258 Db dstDb(dl, dstName);
259 try {
260 dstDb->open();
261 } catch (const CssmError &err) {
262 if (err.cssmError() == CSSMERR_DL_DATASTORE_DOESNOT_EXIST && createIfNeeded) {
263 notice("creating %s", dstName);
264 dstDb->create();
265 } else
266 throw;
267 }
268
269 // extract master key and place into destination keychain
270 DeriveKey derive(csp, CSSM_ALGID_KEYCHAIN_KEY, CSSM_ALGID_3DES_3KEY, 3 * 64);
271 CSSM_DL_DB_HANDLE dstDlDb = dstDb->handle();
272 derive.add(CSSM_ATTRIBUTE_DL_DB_HANDLE, dstDlDb);
273 CSSM_DL_DB_HANDLE srcDlDb = srcDb->handle();
274 CssmData dlDbData = CssmData::wrap(srcDlDb);
275 CssmAutoData keyLabel(CssmAllocator::standard());
276 labelForMasterKey(srcDb, keyLabel);
277 KeySpec spec(CSSM_KEYUSE_ANY,
278 CSSM_KEYATTR_RETURN_REF | CSSM_KEYATTR_PERMANENT | CSSM_KEYATTR_SENSITIVE,
279 keyLabel);
280 CssmKey masterKey;
281 try {
282 derive(&dlDbData, spec, masterKey);
283 } catch (const CssmError &error) {
284 if (error.cssmError() != CSSMERR_DL_INVALID_UNIQUE_INDEX_DATA)
285 throw;
286 if (!force)
287 fail("existing key in %s not overwritten. Use -f to replace it.", dstDb->name());
288 notice("replacing existing record in %s", dstDb->name());
289 deleteKey(dstDb, keyLabel);
290 derive(&dlDbData, spec, masterKey);
291 }
292 notice("%s can now be unlocked with a key in %s", srcName, dstName);
293 }
294
295
296 //
297 // Run a simple test to see if the system-root keychain can auto-unlock.
298 // This isn't trying really hard to diagnose any problems; it's just a yay-or-nay check.
299 //
300 void test(const char *kcName)
301 {
302 CSP csp(gGuidAppleCSPDL);
303 DL dl(gGuidAppleCSPDL);
304
305 // lock, then unlock the keychain
306 Db db(dl, kcName);
307 printf("Testing system unlock of %s\n", kcName);
308 printf("(If you are prompted for a passphrase, cancel)\n");
309 try {
310 db->lock();
311 db->unlock();
312 notice("System unlock is working");
313 } catch (...) {
314 fail("System unlock is NOT working\n");
315 }
316 }
317
318
319 //
320 // Utility functions
321 //
322 void masterKeyIndex(Db &db, CssmOwnedData &index)
323 {
324 SecurityServer::ClientSession ss(CssmAllocator::standard(), CssmAllocator::standard());
325 SecurityServer::DbHandle dbHandle;
326 db->passThrough(CSSM_APPLECSPDL_DB_GET_HANDLE, (const void *)NULL, &dbHandle);
327 ss.getDbSuggestedIndex(dbHandle, index.get());
328 }
329
330
331 void labelForMasterKey(Db &db, CssmOwnedData &label)
332 {
333 label = StringData("SYSKC**"); // 8 bytes exactly
334 CssmAutoData index(label.allocator);
335 masterKeyIndex(db, index);
336 label.append(index);
337 }
338
339
340 void deleteKey(Db &db, const CssmData &label)
341 {
342 DbCursor search(db);
343 search->recordType(CSSM_DL_DB_RECORD_SYMMETRIC_KEY);
344 search->add(CSSM_DB_EQUAL, dlInfoLabel, label);
345 DbUniqueRecord id;
346 if (search->next(NULL, NULL, id))
347 id->deleteRecord();
348 }
349
350
351 //
352 // Message helpers
353 //
354 void notice(const char *fmt, ...)
355 {
356 if (verbose) {
357 va_list args;
358 va_start(args, fmt);
359 vprintf(fmt, args);
360 putchar('\n');
361 va_end(args);
362 }
363 }
364
365 void fail(const char *fmt, ...)
366 {
367 va_list args;
368 va_start(args, fmt);
369 vprintf(fmt, args);
370 putchar('\n');
371 va_end(args);
372 exit(1);
373 }