systemkeychain.cpp [plain text]
#include <Security/dlclient.h>
#include <Security/cryptoclient.h>
#include <Security/wrapkey.h>
#include <Security/genkey.h>
#include <Security/Schema.h>
#include "ssblob.h"
#include <cstdarg>
using namespace SecurityServer;
using namespace CssmClient;
using namespace UnixPlusPlus;
static const char *unlockConfig = kSystemUnlockFile;
const char *systemKCName = kSystemKeychainDir kSystemKeychainName;
bool verbose = false;
bool createIfNeeded = false;
bool force = false;
static const CSSM_DB_ATTRIBUTE_INFO dlInfoLabel = {
CSSM_DB_ATTRIBUTE_NAME_AS_STRING,
{"Label"},
CSSM_DB_ATTRIBUTE_FORMAT_BLOB
};
void usage();
void createSystemKeychain(const char *kcName, const char *passphrase);
void extract(const char *srcName, const char *dstName);
void test(const char *kcName);
void notice(const char *fmt, ...);
void fail(const char *fmt, ...);
void masterKeyIndex(Db &db, CssmOwnedData &index);
void labelForMasterKey(Db &db, CssmOwnedData &data);
void deleteKey(Db &db, const CssmData &label);
int main (int argc, char * argv[])
{
enum Action {
showUsage,
setupSystem,
copyKey,
testUnlock
} action = showUsage;
extern int optind;
extern char *optarg;
int arg;
while ((arg = getopt(argc, argv, "cCfk:stv")) != -1) {
switch (arg) {
case 'c':
createIfNeeded = true;
break;
case 'C':
action = setupSystem;
break;
case 'f':
force = true;
break;
case 'k':
systemKCName = optarg;
break;
case 's':
action = copyKey;
break;
case 't':
action = testUnlock;
break;
case 'v':
verbose = true;
break;
default:
usage();
}
}
try {
switch (action) {
case setupSystem:
if (optind < argc - 1)
usage();
createSystemKeychain(systemKCName, argv[optind]);
break;
case copyKey:
if (optind == argc)
usage();
do {
extract(argv[optind], systemKCName);
} while (argv[++optind]);
break;
case testUnlock:
test(systemKCName);
break;
default:
usage();
}
exit(0);
} catch (const CssmError &error) {
cssmPerror(systemKCName, error.cssmError());
exit(1);
} catch (const UnixError &error) {
fail("%s: %s", systemKCName, strerror(error.error));
exit(1);
} catch (...) {
fail("Unexpected exception");
exit(1);
}
}
void usage()
{
fprintf(stderr, "Usage: systemkeychain -S [passphrase] # (re)create system root keychain"
"\n\tsystemkeychain [-k destination-keychain] -s source-keychain ..."
"\n");
exit(2);
}
void createSystemKeychain(const char *kcName, const char *passphrase)
{
if (!strcmp(kcName, kSystemKeychainDir kSystemKeychainName))
::mkdir(kSystemKeychainDir, 0755);
CSP csp(gGuidAppleCSPDL);
DL dl(gGuidAppleCSPDL);
Db db(dl, kcName);
CssmAllocator &alloc = db.allocator();
AutoCredentials cred(alloc); CSSM_CSP_HANDLE cspHandle = csp->handle();
Key masterKey;
if (passphrase) {
cred += TypedList(alloc, CSSM_SAMPLE_TYPE_KEYCHAIN_CHANGE_LOCK,
new(alloc) ListElement(CSSM_SAMPLE_TYPE_PASSWORD),
new(alloc) ListElement(StringData(passphrase)));
db->accessCredentials(&cred);
} else {
notice("warning: this keychain cannot be unlocked with any passphrase");
GenerateKey generate(csp, CSSM_ALGID_3DES_3KEY_EDE, 64 * 3);
masterKey = generate(KeySpec(CSSM_KEYUSE_ANY,
CSSM_KEYATTR_RETURN_REF | CSSM_KEYATTR_EXTRACTABLE));
cred += TypedList(alloc, CSSM_SAMPLE_TYPE_KEYCHAIN_CHANGE_LOCK,
new(alloc) ListElement(CSSM_WORDID_SYMMETRIC_KEY),
new(alloc) ListElement(CssmData::wrap(cspHandle)),
new(alloc) ListElement(CssmData::wrap(static_cast<const CssmKey &>(masterKey))));
db->accessCredentials(&cred);
}
db->dbInfo(&KeychainCore::Schema::DBInfo); try {
db->create();
} catch (const CssmError &error) {
if (error.cssmError() == CSSMERR_DL_DATASTORE_ALREADY_EXISTS && force) {
notice("recreating %s", kcName);
unlink(kcName);
db->create();
} else
throw;
}
chmod(db->name(), 0644);
DeriveKey derive(csp, CSSM_ALGID_KEYCHAIN_KEY, CSSM_ALGID_3DES_3KEY, 3 * 64);
CSSM_DL_DB_HANDLE dlDb = db->handle();
CssmData dlDbData = CssmData::wrap(dlDb);
CssmKey refKey;
KeySpec spec(CSSM_KEYUSE_ANY,
CSSM_KEYATTR_RETURN_REF | CSSM_KEYATTR_EXTRACTABLE);
derive(&dlDbData, spec, refKey);
CssmKey rawKey;
WrapKey wrap(csp, CSSM_ALGID_NONE);
wrap(refKey, rawKey);
UnlockBlob blob;
blob.initialize(0);
CssmAutoData index(CssmAllocator::standard());
masterKeyIndex(db, index);
memcpy(&blob.signature, index.data(), sizeof(blob.signature));
memcpy(blob.masterKey, rawKey.data(), sizeof(blob.masterKey));
string tempFile(string(unlockConfig) + ",");
FileDesc blobFile(tempFile, O_WRONLY | O_CREAT | O_TRUNC, 0400);
if (blobFile.write(blob) != sizeof(blob)) {
unlink(tempFile.c_str());
fail("unable to write %s", tempFile.c_str());
}
blobFile.close();
::rename(tempFile.c_str(), unlockConfig);
notice("%s installed as system keychain", kcName);
}
void extract(const char *srcName, const char *dstName)
{
CSP csp(gGuidAppleCSPDL);
DL dl(gGuidAppleCSPDL);
Db srcDb(dl, srcName);
Db dstDb(dl, dstName);
try {
dstDb->open();
} catch (const CssmError &err) {
if (err.cssmError() == CSSMERR_DL_DATASTORE_DOESNOT_EXIST && createIfNeeded) {
notice("creating %s", dstName);
dstDb->create();
} else
throw;
}
DeriveKey derive(csp, CSSM_ALGID_KEYCHAIN_KEY, CSSM_ALGID_3DES_3KEY, 3 * 64);
CSSM_DL_DB_HANDLE dstDlDb = dstDb->handle();
derive.add(CSSM_ATTRIBUTE_DL_DB_HANDLE, dstDlDb);
CSSM_DL_DB_HANDLE srcDlDb = srcDb->handle();
CssmData dlDbData = CssmData::wrap(srcDlDb);
CssmAutoData keyLabel(CssmAllocator::standard());
labelForMasterKey(srcDb, keyLabel);
KeySpec spec(CSSM_KEYUSE_ANY,
CSSM_KEYATTR_RETURN_REF | CSSM_KEYATTR_PERMANENT | CSSM_KEYATTR_SENSITIVE,
keyLabel);
CssmKey masterKey;
try {
derive(&dlDbData, spec, masterKey);
} catch (const CssmError &error) {
if (error.cssmError() != CSSMERR_DL_INVALID_UNIQUE_INDEX_DATA)
throw;
if (!force)
fail("existing key in %s not overwritten. Use -f to replace it.", dstDb->name());
notice("replacing existing record in %s", dstDb->name());
deleteKey(dstDb, keyLabel);
derive(&dlDbData, spec, masterKey);
}
notice("%s can now be unlocked with a key in %s", srcName, dstName);
}
void test(const char *kcName)
{
CSP csp(gGuidAppleCSPDL);
DL dl(gGuidAppleCSPDL);
Db db(dl, kcName);
printf("Testing system unlock of %s\n", kcName);
printf("(If you are prompted for a passphrase, cancel)\n");
try {
db->lock();
db->unlock();
notice("System unlock is working");
} catch (...) {
fail("System unlock is NOT working\n");
}
}
void masterKeyIndex(Db &db, CssmOwnedData &index)
{
SecurityServer::ClientSession ss(CssmAllocator::standard(), CssmAllocator::standard());
SecurityServer::DbHandle dbHandle;
db->passThrough(CSSM_APPLECSPDL_DB_GET_HANDLE, (const void *)NULL, &dbHandle);
ss.getDbSuggestedIndex(dbHandle, index.get());
}
void labelForMasterKey(Db &db, CssmOwnedData &label)
{
label = StringData("SYSKC**"); CssmAutoData index(label.allocator);
masterKeyIndex(db, index);
label.append(index);
}
void deleteKey(Db &db, const CssmData &label)
{
DbCursor search(db);
search->recordType(CSSM_DL_DB_RECORD_SYMMETRIC_KEY);
search->add(CSSM_DB_EQUAL, dlInfoLabel, label);
DbUniqueRecord id;
if (search->next(NULL, NULL, id))
id->deleteRecord();
}
void notice(const char *fmt, ...)
{
if (verbose) {
va_list args;
va_start(args, fmt);
vprintf(fmt, args);
putchar('\n');
va_end(args);
}
}
void fail(const char *fmt, ...)
{
va_list args;
va_start(args, fmt);
vprintf(fmt, args);
putchar('\n');
va_end(args);
exit(1);
}