2 * Copyright (c) 2000-2001 Apple Computer, Inc. All Rights Reserved.
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
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.
20 // systemkeychain command - set up and manipulate system-unlocked keychains
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>
30 using namespace SecurityServer
;
31 using namespace CssmClient
;
32 using namespace UnixPlusPlus
;
35 static const char *unlockConfig
= kSystemUnlockFile
;
39 // Values set from command-line options
41 const char *systemKCName
= kSystemKeychainDir kSystemKeychainName
;
43 bool createIfNeeded
= false;
48 // CSSM record attribute names
50 static const CSSM_DB_ATTRIBUTE_INFO dlInfoLabel
= {
51 CSSM_DB_ATTRIBUTE_NAME_AS_STRING
,
53 CSSM_DB_ATTRIBUTE_FORMAT_BLOB
61 void createSystemKeychain(const char *kcName
, const char *passphrase
);
62 void extract(const char *srcName
, const char *dstName
);
63 void test(const char *kcName
);
65 void notice(const char *fmt
, ...);
66 void fail(const char *fmt
, ...);
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
74 // Main program: parse options and dispatch, catching exceptions
76 int main (int argc
, char * argv
[])
88 while ((arg
= getopt(argc
, argv
, "cCfk:stv")) != -1) {
91 createIfNeeded
= true;
100 systemKCName
= optarg
;
118 if (optind
< argc
- 1)
120 createSystemKeychain(systemKCName
, argv
[optind
]);
126 extract(argv
[optind
], systemKCName
);
127 } while (argv
[++optind
]);
136 } catch (const CssmError
&error
) {
137 cssmPerror(systemKCName
, error
.cssmError());
139 } catch (const UnixError
&error
) {
140 fail("%s: %s", systemKCName
, strerror(error
.error
));
143 fail("Unexpected exception");
150 // Partial usage message (some features aren't worth emphasizing...)
154 fprintf(stderr
, "Usage: systemkeychain -S [passphrase] # (re)create system root keychain"
155 "\n\tsystemkeychain [-k destination-keychain] -s source-keychain ..."
162 // Create a keychain and set it up as the system-root secret
164 void createSystemKeychain(const char *kcName
, const char *passphrase
)
166 // for the default path only, make sure the directory exists
167 if (!strcmp(kcName
, kSystemKeychainDir kSystemKeychainName
))
168 ::mkdir(kSystemKeychainDir
, 0755);
170 CSP
csp(gGuidAppleCSPDL
);
171 DL
dl(gGuidAppleCSPDL
);
173 // create the keychain, using appropriate credentials
175 CssmAllocator
&alloc
= db
.allocator();
176 AutoCredentials
cred(alloc
); // will leak, but we're quitting soon :-)
177 CSSM_CSP_HANDLE cspHandle
= csp
->handle();
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
);
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
);
197 db
->dbInfo(&KeychainCore::Schema::DBInfo
); // Set the standard schema
200 } catch (const CssmError
&error
) {
201 if (error
.cssmError() == CSSMERR_DL_DATASTORE_ALREADY_EXISTS
&& force
) {
202 notice("recreating %s", kcName
);
208 chmod(db
->name(), 0644);
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
);
215 KeySpec
spec(CSSM_KEYUSE_ANY
,
216 CSSM_KEYATTR_RETURN_REF
| CSSM_KEYATTR_EXTRACTABLE
);
217 derive(&dlDbData
, spec
, refKey
);
219 // now extract the raw keybits
221 WrapKey
wrap(csp
, CSSM_ALGID_NONE
);
222 wrap(refKey
, rawKey
);
224 // form the evidence record
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
));
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());
240 ::rename(tempFile
.c_str(), unlockConfig
);
242 notice("%s installed as system keychain", kcName
);
247 // Extract the master secret from a keychain and install it in another keychain for unlocking
249 void extract(const char *srcName
, const char *dstName
)
251 CSP
csp(gGuidAppleCSPDL
);
252 DL
dl(gGuidAppleCSPDL
);
254 // open source database
255 Db
srcDb(dl
, srcName
);
257 // open destination database
258 Db
dstDb(dl
, dstName
);
261 } catch (const CssmError
&err
) {
262 if (err
.cssmError() == CSSMERR_DL_DATASTORE_DOESNOT_EXIST
&& createIfNeeded
) {
263 notice("creating %s", dstName
);
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
,
282 derive(&dlDbData
, spec
, masterKey
);
283 } catch (const CssmError
&error
) {
284 if (error
.cssmError() != CSSMERR_DL_INVALID_UNIQUE_INDEX_DATA
)
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
);
292 notice("%s can now be unlocked with a key in %s", srcName
, dstName
);
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.
300 void test(const char *kcName
)
302 CSP
csp(gGuidAppleCSPDL
);
303 DL
dl(gGuidAppleCSPDL
);
305 // lock, then unlock the keychain
307 printf("Testing system unlock of %s\n", kcName
);
308 printf("(If you are prompted for a passphrase, cancel)\n");
312 notice("System unlock is working");
314 fail("System unlock is NOT working\n");
322 void masterKeyIndex(Db
&db
, CssmOwnedData
&index
)
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());
331 void labelForMasterKey(Db
&db
, CssmOwnedData
&label
)
333 label
= StringData("SYSKC**"); // 8 bytes exactly
334 CssmAutoData
index(label
.allocator
);
335 masterKeyIndex(db
, index
);
340 void deleteKey(Db
&db
, const CssmData
&label
)
343 search
->recordType(CSSM_DL_DB_RECORD_SYMMETRIC_KEY
);
344 search
->add(CSSM_DB_EQUAL
, dlInfoLabel
, label
);
346 if (search
->next(NULL
, NULL
, id
))
354 void notice(const char *fmt
, ...)
365 void fail(const char *fmt
, ...)