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_cdsa_client/securestorage.h>
23#include <security_cdsa_client/cryptoclient.h>
24#include <security_cdsa_utilities/uniformrandom.h>
25#include <security_utilities/devrandom.h>
26#include <security_utilities/errors.h>
27#include <security_cdsa_client/wrapkey.h>
28#include <security_cdsa_client/genkey.h>
29#include <security_cdsa_utilities/Schema.h>
30#include <security_cdsa_utilities/cssmendian.h>
31#include <securityd_client/ssblob.h>
32#include <securityd_client/ssclient.h>
33#include <Security/cssmapple.h>
34#include <cstdarg>
35#include <CoreServices/../Frameworks/CarbonCore.framework/Headers/MacErrors.h>
36#include <launch.h>
37#include <syslog.h>
38#include <sysexits.h>
39#include <dispatch/dispatch.h>
40#include <sys/socket.h>
41#include <sys/un.h>
42#include "TokenIDHelper.h"
43
44using namespace SecurityServer;
45using namespace CssmClient;
46using namespace UnixPlusPlus;
47
48
49static const char *unlockConfig = kSystemUnlockFile;
50
51
52//
53// Values set from command-line options
54//
55const char *systemKCName = kSystemKeychainDir kSystemKeychainName;
56bool verbose = false;
57bool createIfNeeded = false;
58bool force = false;
59
60
61//
62// CSSM record attribute names
63//
64static const CSSM_DB_ATTRIBUTE_INFO dlInfoLabel = {
65	CSSM_DB_ATTRIBUTE_NAME_AS_STRING,
66	{(char*)"Label"},
67	CSSM_DB_ATTRIBUTE_FORMAT_BLOB
68};
69
70
71
72//
73// Local functions
74void usage();
75void createSystemKeychain(const char *kcName, const char *passphrase);
76void extract(const char *srcName, const char *dstName);
77void test(const char *kcName);
78
79void notice(const char *fmt, ...);
80void fail(const char *fmt, ...);
81
82void labelForMasterKey(Db &db, CssmOwnedData &data);
83void deleteKey(Db &db, const CssmData &label);	// delete key with this label
84
85void addReferralRecord(Db &srcDb, const DLDbIdentifier &ident, CssmData *refData, CFDataRef labelData);
86void createUnlockConfig(CSP &csp, Db &db);
87void createAKeychain(const char *kcName, const char *passphrase, CSP &csp, DL &dl, Db &db, Key *masterKeyRef);
88OSStatus createTokenProtectedKeychain(const char *kcName);
89void createMasterKey(CSP &csp, Key &masterKeyRef);
90void flattenKey  (const CssmKey &rawKey, CssmDataContainer &flatKey);
91void systemKeychainCheck();
92
93//
94// Main program: parse options and dispatch, catching exceptions
95//
96int main (int argc, char * argv[])
97{
98	enum Action {
99		showUsage,
100		setupSystem,
101		copyKey,
102		testUnlock,
103		tokenProtectedKCCreate,
104		systemKeychainCheckDaemon
105	} action = showUsage;
106
107	extern int optind;
108	extern char *optarg;
109	int arg;
110	OSStatus status;
111	while ((arg = getopt(argc, argv, "cCdfk:stvT:")) != -1) {
112		switch (arg) {
113		case 'c':
114			createIfNeeded = true;
115			break;
116		case 'C':
117			action = setupSystem;
118			break;
119		case 'd':
120			action = systemKeychainCheckDaemon;
121			break;
122		case 'f':
123			force = true;
124			break;
125		case 'k':
126			systemKCName = optarg;
127			break;
128		case 's':
129			action = copyKey;
130			break;
131		case 't':
132			action = testUnlock;
133			break;
134		case 'v':
135			verbose = true;
136			break;
137		case 'T':					// Create token protected keychain
138			action = tokenProtectedKCCreate;
139			systemKCName = optarg;
140			break;
141		default:
142			usage();
143		}
144	}
145	try {
146		switch (action) {
147		case setupSystem:
148			if (optind < argc - 1)
149				usage();
150			createSystemKeychain(systemKCName, argv[optind]);
151			break;
152		case copyKey:
153			if (optind == argc)
154				usage();
155			do {
156				extract(argv[optind], systemKCName);
157			} while (argv[++optind]);
158			break;
159		case testUnlock:
160			test(systemKCName);
161			break;
162		case tokenProtectedKCCreate:
163			if ((status = createTokenProtectedKeychain(systemKCName)))
164			{
165				cssmPerror("unable to create token protected keychain", status);
166				exit(1);
167			}
168			break;
169		case systemKeychainCheckDaemon:
170			systemKeychainCheck();
171			exit(0);
172			break;
173		default:
174			usage();
175		}
176		exit(0);
177	} catch (const CssmError &error) {
178		cssmPerror(systemKCName, error.error);
179		exit(1);
180	} catch (const UnixError &error) {
181		fail("%s: %s", systemKCName, strerror(error.error));
182		exit(1);
183	} catch (const MacOSError &error) {
184		cssmPerror(systemKCName, error.error);
185		exit(1);
186	}
187	catch (...) {
188		fail("Unexpected exception");
189		exit(1);
190	}
191}
192
193
194//
195// Partial usage message (some features aren't worth emphasizing...)
196//
197void usage()
198{
199	fprintf(stderr, "Usage: systemkeychain -C [passphrase]  # (re)create system root keychain"
200		"\n\tsystemkeychain [-k destination-keychain] -s source-keychain ..."
201		"\n\tsystemkeychain -T token-protected-keychain-name"
202		"\n");
203	exit(2);
204}
205
206
207//
208// Create a keychain and set it up as the system-root secret
209//
210void createSystemKeychain(const char *kcName, const char *passphrase)
211{
212	// for the default path only, make sure the directory exists
213	if (!strcmp(kcName, kSystemKeychainDir kSystemKeychainName))
214		::mkdir(kSystemKeychainDir, 0755);
215
216	CSP csp(gGuidAppleCSPDL);
217	DL dl(gGuidAppleCSPDL);
218
219	// create the keychain, using appropriate credentials
220	Db db(dl, kcName);
221	Key masterKey;
222
223	createMasterKey(csp, masterKey);
224	createAKeychain(kcName, passphrase, csp, dl, db, &masterKey);
225	createUnlockConfig(csp, db);
226
227	notice("%s installed as system keychain", kcName);
228}
229
230void createAKeychain(const char *kcName, const char *passphrase, CSP &csp, DL &dl, Db &db, Key *masterKeyRef)
231{
232	// create the keychain, using appropriate credentials
233	Allocator &alloc = db->allocator();
234	AutoCredentials cred(alloc);	// will leak, but we're quitting soon :-)
235	CSSM_CSP_HANDLE cspHandle = csp->handle();
236	if (passphrase) {
237		// use this passphrase
238		cred += TypedList(alloc, CSSM_SAMPLE_TYPE_KEYCHAIN_CHANGE_LOCK,
239			new(alloc) ListElement(CSSM_SAMPLE_TYPE_PASSWORD),
240			new(alloc) ListElement(StringData(passphrase)));
241		cred += TypedList(alloc, CSSM_SAMPLE_TYPE_KEYCHAIN_LOCK,
242			new(alloc) ListElement(CSSM_SAMPLE_TYPE_PASSWORD),
243			new(alloc) ListElement(StringData(passphrase)));
244		db->accessCredentials(&cred);
245	}
246	else
247	{
248		// generate a random key
249		notice("warning: this keychain cannot be unlocked with any passphrase");
250		if (!masterKeyRef)				// caller does not need it returned for later use
251			MacOSError::throwMe(paramErr);
252		cred += TypedList(alloc, CSSM_SAMPLE_TYPE_KEYCHAIN_CHANGE_LOCK,
253			new(alloc) ListElement(CSSM_SAMPLE_TYPE_SYMMETRIC_KEY),
254			new(alloc) ListElement(CssmData::wrap(cspHandle)),
255			new(alloc) ListElement(CssmData::wrap(static_cast<const CssmKey &>(*masterKeyRef))));
256		cred += TypedList(alloc, CSSM_SAMPLE_TYPE_KEYCHAIN_LOCK,
257			new(alloc) ListElement(CSSM_SAMPLE_TYPE_SYMMETRIC_KEY),
258			new(alloc) ListElement(CssmData::wrap(cspHandle)),
259			new(alloc) ListElement(CssmData::wrap(static_cast<const CssmKey &>(*masterKeyRef))),
260			new(alloc) ListElement(CssmData()));
261		db->accessCredentials(&cred);
262	}
263	db->dbInfo(&KeychainCore::Schema::DBInfo); // Set the standard schema
264	try {
265		db->create();
266	} catch (const CssmError &error) {
267		if (error.error == CSSMERR_DL_DATASTORE_ALREADY_EXISTS && force) {
268			notice("recreating %s", kcName);
269			unlink(kcName);
270			db->create();
271		} else
272			throw;
273	}
274	chmod(db->name(), 0644);
275}
276
277void createMasterKey(CSP &csp, Key &masterKeyRef)
278{
279	// generate a random key
280	CssmClient::GenerateKey generate(csp, CSSM_ALGID_3DES_3KEY_EDE, 64 * 3);
281	masterKeyRef =	generate(KeySpec(CSSM_KEYUSE_ANY, CSSM_KEYATTR_RETURN_REF | CSSM_KEYATTR_EXTRACTABLE));
282}
283
284void createUnlockConfig(CSP &csp, Db &db)
285{
286	// extract the key into the CSPDL
287	DeriveKey derive(csp, CSSM_ALGID_KEYCHAIN_KEY, CSSM_ALGID_3DES_3KEY, 3 * 64);
288	CSSM_DL_DB_HANDLE dlDb = db->handle();
289	CssmData dlDbData = CssmData::wrap(dlDb);
290	CssmKey refKey;
291	KeySpec spec(CSSM_KEYUSE_ANY,
292		CSSM_KEYATTR_RETURN_REF | CSSM_KEYATTR_EXTRACTABLE);
293	derive(&dlDbData, spec, refKey);
294
295	// now extract the raw keybits
296	CssmKey rawKey;
297	WrapKey wrap(csp, CSSM_ALGID_NONE);
298	wrap(refKey, rawKey);
299
300	// form the evidence record
301	UnlockBlob blob;
302	blob.initialize(0);
303
304	CssmAutoData dbBlobData(Allocator::standard());
305	SecurityServer::ClientSession client(dbBlobData.allocator, dbBlobData.allocator);
306	db->copyBlob(dbBlobData.get());
307	DbBlob *dbBlob = dbBlobData.get().interpretedAs<DbBlob>();
308
309	memcpy(&blob.signature, &dbBlob->randomSignature, sizeof(dbBlob->randomSignature));
310	memcpy(blob.masterKey, rawKey.data(), sizeof(blob.masterKey));
311
312	// write it out, forcibly overwriting an existing file
313	string tempFile(string(unlockConfig) + ",");
314	FileDesc blobFile(tempFile, O_WRONLY | O_CREAT | O_TRUNC, 0400);
315	if (blobFile.write(blob) != sizeof(blob)) {
316		unlink(tempFile.c_str());
317		fail("unable to write %s", tempFile.c_str());
318	}
319	blobFile.close();
320	::rename(tempFile.c_str(), unlockConfig);
321}
322
323//
324// Extract the master secret from a keychain and install it in another keychain for unlocking
325//
326void extract(const char *srcName, const char *dstName)
327{
328	using namespace KeychainCore;
329
330	CSPDL cspdl(gGuidAppleCSPDL);
331
332	// open source database
333	Db srcDb(cspdl, srcName);
334
335	// open destination database
336	Db dstDb(cspdl, dstName);
337	try {
338		dstDb->open();
339	} catch (const CssmError &err) {
340		if (err.error == CSSMERR_DL_DATASTORE_DOESNOT_EXIST && createIfNeeded) {
341			notice("creating %s", dstName);
342			dstDb->create();
343		} else
344			throw;
345	}
346
347	// extract master key and place into destination keychain
348	DeriveKey derive(cspdl, CSSM_ALGID_KEYCHAIN_KEY, CSSM_ALGID_3DES_3KEY, 3 * 64);
349	CSSM_DL_DB_HANDLE dstDlDb = dstDb->handle();
350	derive.add(CSSM_ATTRIBUTE_DL_DB_HANDLE, dstDlDb);
351	CSSM_DL_DB_HANDLE srcDlDb = srcDb->handle();
352	CssmData dlDbData = CssmData::wrap(srcDlDb);
353	CssmAutoData keyLabel(Allocator::standard());
354	labelForMasterKey(srcDb, keyLabel);
355	KeySpec spec(CSSM_KEYUSE_ANY,
356		CSSM_KEYATTR_RETURN_REF | CSSM_KEYATTR_PERMANENT | CSSM_KEYATTR_SENSITIVE,
357		keyLabel);
358	CssmKey masterKey;
359	try {
360		derive(&dlDbData, spec, masterKey);
361	} catch (const CssmError &error) {
362		if (error.error != CSSMERR_DL_INVALID_UNIQUE_INDEX_DATA)
363			throw;
364		if (!force)
365			fail("existing key in %s not overwritten. Use -f to replace it.", dstDb->name());
366		notice("replacing existing record in %s", dstDb->name());
367		deleteKey(dstDb, keyLabel);
368		derive(&dlDbData, spec, masterKey);
369	}
370
371	addReferralRecord(srcDb, dstDb->dlDbIdentifier(), NULL, NULL);
372
373	notice("%s can now be unlocked with a key in %s", srcName, dstName);
374}
375
376void addReferralRecord(Db &srcDb, const DLDbIdentifier &ident, CssmData *refData, CFDataRef label)
377{
378	// If you have a Db dstDb, call with dstDb->dlDbIdentifier() as second parameter
379	using namespace KeychainCore;
380	// now add a referral record to the source database
381	uint32 referralType = (!refData)?CSSM_APPLE_UNLOCK_TYPE_KEY_DIRECT:CSSM_APPLE_UNLOCK_TYPE_WRAPPED_PRIVATE;
382	try {
383		// build attribute vector
384		CssmAutoData masterKeyLabel(Allocator::standard());
385		CssmData externalLabel;
386		if (label)
387		{
388			externalLabel.Data = const_cast<uint8 *>(CFDataGetBytePtr(label));
389			externalLabel.Length = CFDataGetLength(label);
390		}
391		else
392			labelForMasterKey(srcDb, masterKeyLabel);
393		CssmAutoDbRecordAttributeData refAttrs(9);
394		refAttrs.add(Schema::kUnlockReferralType, uint32(referralType));
395		refAttrs.add(Schema::kUnlockReferralDbName, ident.dbName());
396		refAttrs.add(Schema::kUnlockReferralDbNetname, CssmData());
397		refAttrs.add(Schema::kUnlockReferralDbGuid, ident.ssuid().guid());
398		refAttrs.add(Schema::kUnlockReferralDbSSID, ident.ssuid().subserviceId());
399		refAttrs.add(Schema::kUnlockReferralDbSSType, ident.ssuid().subserviceType());
400		refAttrs.add(Schema::kUnlockReferralKeyLabel, label?externalLabel:masterKeyLabel.get());
401		refAttrs.add(Schema::kUnlockReferralKeyAppTag, CssmData());
402		refAttrs.add(Schema::kUnlockReferralPrintName,
403			StringData("Keychain Unlock Referral Record"));
404
405		// no reference data for this form
406		CssmData emptyRefData;
407		if (!refData)
408			refData = &emptyRefData;
409
410		try {
411			srcDb->insert(CSSM_DL_DB_RECORD_UNLOCK_REFERRAL,
412				&refAttrs,
413				refData);
414			secdebug("kcreferral", "referral record stored in %s", srcDb->name());
415		} catch (const CssmError &e) {
416			if (e.error != CSSMERR_DL_INVALID_RECORDTYPE)
417				throw;
418
419			// Create the referral relation and retry
420			secdebug("kcreferral", "adding referral schema relation to %s", srcDb->name());
421			srcDb->createRelation(CSSM_DL_DB_RECORD_UNLOCK_REFERRAL, "CSSM_DL_DB_RECORD_UNLOCK_REFERRAL",
422				Schema::UnlockReferralSchemaAttributeCount,
423				Schema::UnlockReferralSchemaAttributeList,
424				Schema::UnlockReferralSchemaIndexCount,
425				Schema::UnlockReferralSchemaIndexList);
426			srcDb->insert(CSSM_DL_DB_RECORD_UNLOCK_REFERRAL,
427				&refAttrs,
428				refData);
429			secdebug("kcreferral", "referral record inserted in %s (on retry)", srcDb->name());
430		}
431	} catch (...) {
432		notice("kcreferral", "cannot store referral in %s", srcDb->name());
433		throw;
434	}
435}
436
437//
438// Create a keychain protected with an asymmetric key stored on a token (e.g. smartcard)
439// See createSystemKeychain for a similar call
440//
441#include <Security/SecKey.h>
442#include <Security/SecKeychain.h>
443#include <Security/SecKeychainItem.h>
444
445OSStatus createTokenProtectedKeychain(const char *kcName)
446{
447	// In the notation of "extract", srcDb is the keychain to be unlocked
448	// (e.g. login keychain), and dstDb is the one to unlock with (e.g. smartcard/token)
449	// @@@ A later enhancement should allow lookup by public key hash
450
451	SecKeychainRef keychainRef = NULL;
452	SecKeyRef publicKeyRef = NULL;
453	const CSSM_KEY *pcssmKey;
454	CssmKey pubEncryptionKey;
455	CssmKey nullWrappedPubEncryptionKey;
456	CFDataRef label = NULL;
457	OSStatus status = findFirstEncryptionPublicKeyOnToken(&publicKeyRef, &keychainRef, &label);
458	if (status)
459		return status;
460
461	status = SecKeyGetCSSMKey(publicKeyRef,  &pcssmKey);
462	if (status)
463		return status;
464	pubEncryptionKey = *pcssmKey;
465
466	CSSM_DL_DB_HANDLE dstdldbHandle;
467	status = SecKeychainGetDLDBHandle(keychainRef, &dstdldbHandle);
468	if (status)
469		return status;
470
471	char *tokenName;
472	CSSM_SUBSERVICE_UID tokenUID;
473
474	CSSM_RETURN cx;
475	cx = CSSM_DL_GetDbNameFromHandle (dstdldbHandle, &tokenName);
476	if (cx)
477		return cx;
478
479	cx = CSSM_GetSubserviceUIDFromHandle (dstdldbHandle.DLHandle, &tokenUID);
480	if (cx)
481		return cx;
482
483	secdebug("kcreferral", "Key found on token with subserviceId: %d", tokenUID.SubserviceId);
484	DLDbIdentifier tokenIdentifier(tokenName, Guid(gGuidAppleSdCSPDL), tokenUID.SubserviceId,
485			CSSM_SERVICE_CSP | CSSM_SERVICE_DL , NULL);
486
487	CSP csp(gGuidAppleCSPDL);
488	DL dl(gGuidAppleCSPDL);
489
490	// create the keychain, using appropriate credentials
491	Db srcdb(dl, kcName);
492	Key masterKey;
493
494	createMasterKey(csp, masterKey);
495	createAKeychain(kcName, NULL, csp, dl, srcdb, &masterKey);
496
497	// Turn the raw key into a reference key in the local csp with a NULL unwrap
498	CssmClient::Key publicEncryptionKey(csp, pubEncryptionKey, true);
499
500	UnwrapKey tunwrap(csp, CSSM_ALGID_NONE);
501	CSSM_KEYUSE uu = CSSM_KEYUSE_ANY;
502	CSSM_KEYATTR_FLAGS attrs = CSSM_KEYATTR_RETURN_REF | CSSM_KEYATTR_EXTRACTABLE;
503	tunwrap(publicEncryptionKey, KeySpec(uu, attrs), nullWrappedPubEncryptionKey);
504	CssmClient::Key nullWrappedPubEncryptionKeyX(csp, nullWrappedPubEncryptionKey, true);
505
506	// use a CMS wrap to encrypt the key
507	CssmKey wrappedKey;
508	WrapKey wrap(csp, CSSM_ALGID_RSA);		// >>>> will be key on token -- WrapKey wrap
509	wrap.key(nullWrappedPubEncryptionKeyX);
510	wrap.mode(CSSM_ALGMODE_NONE);
511	wrap.padding(CSSM_PADDING_PKCS1);
512	// only add if not CSSM_KEYBLOB_WRAPPED_FORMAT_NONE
513	wrap.add(CSSM_ATTRIBUTE_WRAPPED_KEY_FORMAT, uint32(CSSM_KEYBLOB_WRAPPED_FORMAT_PKCS7));
514	wrap(masterKey, wrappedKey);
515
516	CssmDataContainer flatKey;
517	flattenKey(wrappedKey, flatKey);
518	addReferralRecord(srcdb, tokenIdentifier, &flatKey, label);
519
520	if (publicKeyRef)
521		CFRelease(publicKeyRef);
522	if (label)
523		CFRelease(label);
524
525	return noErr;
526}
527
528void flattenKey(const CssmKey &rawKey, CssmDataContainer &flatKey)
529{
530	// Flatten the raw input key naively: key header then key data
531	// We also convert it to network byte order in case the referral
532	// record ends up being used on a different machine
533	// A CSSM_KEY is a CSSM_KEYHEADER followed by a CSSM_DATA
534
535	CssmKey rawKeyCopy(rawKey);
536	const uint32 keyDataLength = (uint32)rawKeyCopy.length();
537	uint32 sz = (sizeof(CSSM_KEY) + keyDataLength);
538	flatKey.Data = flatKey.mAllocator.alloc<uint8>(sz);
539	flatKey.Length = sz;
540
541	Security::h2ni(rawKeyCopy.KeyHeader);	// convert it to network byte order
542
543	// Now copy: header, then key struct, then key data
544	memcpy(flatKey.Data, &rawKeyCopy.header(), sizeof(CSSM_KEYHEADER));
545	memcpy(flatKey.Data + sizeof(CSSM_KEYHEADER), &rawKeyCopy.keyData(), sizeof(CSSM_DATA));
546	memcpy(flatKey.Data + sizeof(CSSM_KEYHEADER) + sizeof(CSSM_DATA), rawKeyCopy.data(), keyDataLength);
547	// Note that the Data pointer in the CSSM_DATA portion will not be meaningful when unpacked later
548	// We will also fill in the unflattened key length based on the size of flatKey
549}
550
551void systemKeychainCheck()
552{
553	launch_data_t checkinRequest = launch_data_new_string(LAUNCH_KEY_CHECKIN);
554    launch_data_t checkinResponse = launch_msg(checkinRequest);
555
556	if (!checkinResponse) {
557		syslog(LOG_ERR, "unable to check in with launchd %m");
558		exit(EX_UNAVAILABLE);
559	}
560
561	launch_data_type_t replyType = launch_data_get_type(checkinResponse);
562
563	if (LAUNCH_DATA_DICTIONARY == replyType) {
564		launch_data_t sockets = launch_data_dict_lookup(checkinResponse, LAUNCH_JOBKEY_SOCKETS);
565		if (NULL == sockets) {
566			syslog(LOG_ERR, "Unable to find sockets to listen to");
567			exit(EX_DATAERR);
568		}
569		launch_data_t listening_fd_array = launch_data_dict_lookup(sockets, "Listener");
570		if (NULL == listening_fd_array || launch_data_array_get_count(listening_fd_array) <= 0) {
571			syslog(LOG_ERR, "No sockets to listen to");
572			exit(EX_DATAERR);
573		}
574
575		__block uint32 requestsProcessedSinceLastTimeoutCheck = 0;
576		dispatch_queue_t makeDoneFile = dispatch_queue_create("com.apple.security.make-done-file", NULL);
577		// Use low priority I/O for making the done files -- keeping this process running a little longer
578		// is cheaper then defering other I/O
579		dispatch_set_target_queue(makeDoneFile, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0));
580		// We suspend makeDoneFile until the keychain check is complete
581		dispatch_suspend(makeDoneFile);
582
583		// In theory there can be multiple sockets to listen to, and for the sake of not letting you set something
584		// up in launchd that doesn't get handled we have a little loop.
585		for(unsigned int i = 0; i < launch_data_array_get_count(listening_fd_array); i++) {
586			int serverSocket = launch_data_get_fd(launch_data_array_get_index(listening_fd_array, i));
587			// Note the target queue is the main queue so the handler will not run until after we check the system keychain
588			dispatch_source_t connectRequest = dispatch_source_create(DISPATCH_SOURCE_TYPE_READ, serverSocket, 0, dispatch_get_main_queue());
589			dispatch_source_set_event_handler(connectRequest, ^{
590				sockaddr_un remoteAddress;
591				socklen_t remoteAddressLength = sizeof(remoteAddress);
592				int connection = accept(serverSocket, (sockaddr*)&remoteAddress, &remoteAddressLength);
593				if (connection < 0) {
594					syslog(LOG_NOTICE, "accept failure %m");
595				} else {
596					close(connection);
597				}
598
599				if (requestsProcessedSinceLastTimeoutCheck < INT_MAX) {
600					requestsProcessedSinceLastTimeoutCheck++;
601				}
602			});
603
604			dispatch_async(makeDoneFile, ^{
605				// All of the errors in here are "merely" logged, failure to write the done file just
606				// means we will be spawned at some future time and given a chance to do it again.
607				struct sockaddr_un socketName;
608				socklen_t socketNameLength = sizeof(socketName);
609				int rc = getsockname(serverSocket, (struct sockaddr *)&socketName, &socketNameLength);
610				if (0 != rc) {
611					syslog(LOG_ERR, "Can't getsockname fd#%d %m", serverSocket);
612					return;
613				}
614
615				// If searchFor or replaceWith is ever changed, make sure strlen(searchFor) <= strlen(replaceWith),
616				// or change the code below to check for overflowing sun_path's length.
617				const char *searchFor = ".socket";
618				const char *replaceWith = ".done";
619				size_t searchForLength = strlen(searchFor);
620				size_t pathLength = strlen(socketName.sun_path);
621				if (pathLength > searchForLength && socketName.sun_path[0] == '/') {
622					strcpy(socketName.sun_path + pathLength - searchForLength, replaceWith);
623					syslog(LOG_ERR, "done file: %s", socketName.sun_path);
624					int fd = open(socketName.sun_path, O_CREAT | O_TRUNC | O_WRONLY, S_IRUSR|S_IRGRP|S_IROTH);
625					if (fd < 0) {
626						syslog(LOG_ERR, "Could not create done file %s: %m", socketName.sun_path);
627						return;
628					} else {
629						if (-1 == close(fd)) {
630							syslog(LOG_ERR, "Could not close file %s (fd#%d): %m", socketName.sun_path, fd);
631							return;
632						}
633					}
634				} else {
635					syslog(LOG_ERR, "Unexpected socket name %s", socketName.sun_path);
636				}
637			});
638
639			dispatch_resume(connectRequest);
640		}
641
642		launch_data_free(checkinResponse);
643
644		int64_t timeoutInterval = 30 * NSEC_PER_SEC;
645		dispatch_source_t timeoutCheck = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, dispatch_get_main_queue());
646		dispatch_source_set_event_handler(timeoutCheck, ^{
647			if (requestsProcessedSinceLastTimeoutCheck) {
648				requestsProcessedSinceLastTimeoutCheck = 0;
649			} else {
650				exit(0);
651			}
652		});
653		dispatch_source_set_timer(timeoutCheck, dispatch_time(DISPATCH_TIME_NOW, timeoutInterval), timeoutInterval, timeoutInterval / 4);
654		dispatch_async(makeDoneFile, ^{
655			// The idle timeout starts after we write the done file
656			dispatch_resume(timeoutCheck);
657		});
658
659		int rc = system("/usr/libexec/security-checksystem");
660		if (rc) {
661			int exit_code = EX_SOFTWARE;
662			if (rc < 0) {
663				syslog(LOG_ERR, "Failure %d running security-checksystem", rc);
664			} else {
665				syslog(LOG_ERR, "Could not run security-checksystem failure %m");
666				exit_code = EX_OSERR;
667			}
668
669			exit(exit_code);
670		}
671
672		// Start the first idle check after we handle the first batch of requests (this makes
673		// no difference unless the keychain check somehow takes more then timeoutInterval +/-25%,
674		// but will prevent a premature exit in that unlikely case).
675		dispatch_async(dispatch_get_main_queue(), ^{
676			dispatch_resume(makeDoneFile);
677		});
678
679		dispatch_main();
680		// NOTE: dispatch_main does not return.
681	} else if (LAUNCH_DATA_ERRNO == replyType) {
682		errno = launch_data_get_errno(checkinResponse);
683		syslog(LOG_ERR, "launchd checkin error %m");
684		exit(EX_OSERR);
685	} else {
686		syslog(LOG_ERR, "launchd unexpected message type: %d", launch_data_get_type(checkinResponse));
687		exit(EX_OSERR);
688	}
689}
690
691//
692// Run a simple test to see if the system-root keychain can auto-unlock.
693// This isn't trying really hard to diagnose any problems; it's just a yay-or-nay check.
694//
695void test(const char *kcName)
696{
697	CSP csp(gGuidAppleCSPDL);
698	DL dl(gGuidAppleCSPDL);
699
700	// lock, then unlock the keychain
701	Db db(dl, kcName);
702	printf("Testing system unlock of %s\n", kcName);
703	printf("(If you are prompted for a passphrase, cancel)\n");
704	try {
705		db->lock();
706		db->unlock();
707		notice("System unlock is working");
708	} catch (...) {
709		fail("System unlock is NOT working\n");
710	}
711}
712
713
714//
715// Utility functions
716//
717void labelForMasterKey(Db &db, CssmOwnedData &label)
718{
719	// create a random signature
720	char signature[8];
721	UniformRandomBlobs<DevRandomGenerator>().random(signature);
722
723	// concatenate prefix string with random signature
724	label = StringData("*UNLOCK*");	// 8 bytes exactly
725	label.append(signature, sizeof(signature));
726	assert(label.length() == 8 + sizeof(signature));
727}
728
729
730void deleteKey(Db &db, const CssmData &label)
731{
732	DbCursor search(db);
733	search->recordType(CSSM_DL_DB_RECORD_SYMMETRIC_KEY);
734	search->add(CSSM_DB_EQUAL, dlInfoLabel, label);
735	DbUniqueRecord id;
736	if (search->next(NULL, NULL, id))
737		id->deleteRecord();
738}
739
740
741//
742// Message helpers
743//
744void notice(const char *fmt, ...)
745{
746	if (verbose) {
747		va_list args;
748		va_start(args, fmt);
749		vprintf(fmt, args);
750		putchar('\n');
751		va_end(args);
752	}
753}
754
755void fail(const char *fmt, ...)
756{
757	va_list args;
758	va_start(args, fmt);
759	vprintf(fmt, args);
760	putchar('\n');
761	va_end(args);
762	exit(1);
763}
764