main.cpp   [plain text]


/*
 * Copyright (c) 2000-2007 Apple Inc. All Rights Reserved.
 * 
 * @APPLE_LICENSE_HEADER_START@
 * 
 * This file contains Original Code and/or Modifications of Original Code
 * as defined in and that are subject to the Apple Public Source License
 * Version 2.0 (the 'License'). You may not use this file except in
 * compliance with the License. Please obtain a copy of the License at
 * http://www.opensource.apple.com/apsl/ and read it before using this
 * file.
 * 
 * The Original Code and all software distributed under the License are
 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
 * Please see the License for the specific language governing rights and
 * limitations under the License.
 * 
 * @APPLE_LICENSE_HEADER_END@
 */


//
// securityd - Apple security services daemon.
//
#include <securityd_client/ucsp.h>

#include "server.h"
#include "entropy.h"
#include "authority.h"
#include "session.h"
#include "notifications.h"
#include "pcscmonitor.h"
#include "self.h"

#include <security_utilities/daemon.h>
#include <security_utilities/machserver.h>
#include <security_utilities/logging.h>

#include <Security/SecKeychainPriv.h>

#include <unistd.h>
#include <sys/types.h>
#include <signal.h>
#include <syslog.h>

// ACL subject types (their makers are instantiated here)
#include <security_cdsa_utilities/acl_any.h>
#include <security_cdsa_utilities/acl_password.h>
#include <security_cdsa_utilities/acl_prompted.h>
#include <security_cdsa_utilities/acl_protectedpw.h>
#include <security_cdsa_utilities/acl_threshold.h>
#include <security_cdsa_utilities/acl_codesigning.h>
#include <security_cdsa_utilities/acl_process.h>
#include <security_cdsa_utilities/acl_comment.h>
#include <security_cdsa_utilities/acl_preauth.h>
#include "acl_keychain.h"


//
// Local functions of the main program driver
//
static void usage(const char *me) __attribute__((noreturn));
static void handleSignals(int sig);
static PCSCMonitor::ServiceLevel scOptions(const char *optionString);


static Port gMainServerPort;
PCSCMonitor *gPCSC;


//
// Main driver
//
int main(int argc, char *argv[])
{
	// clear the umask - we know what we're doing
	secdebug("SS", "starting umask was 0%o", ::umask(0));
	::umask(0);

	// tell the keychain (client) layer to turn off the server interface
	SecKeychainSetServerMode();
	
	// program arguments (preset to defaults)
	bool debugMode = false;
	const char *bootstrapName = NULL;
	const char* messagingName = SECURITY_MESSAGES_NAME;
	bool doFork = false;
	bool reExecute = false;
	int workerTimeout = 0;
	int maxThreads = 0;
	bool waitForClients = true;
    bool mdsIsInstalled = false;
	const char *authorizationConfig = "/etc/authorization";
	const char *tokenCacheDir = "/var/db/TokenCache";
    const char *entropyFile = "/var/db/SystemEntropyCache";
	const char *equivDbFile = EQUIVALENCEDBPATH;
	const char *smartCardOptions = getenv("SMARTCARDS");
	uint32_t keychainAclDefault = CSSM_ACL_KEYCHAIN_PROMPT_INVALID | CSSM_ACL_KEYCHAIN_PROMPT_UNSIGNED;
	unsigned int verbose = 0;
	
	// check for the Installation-DVD environment and modify some default arguments if found
	if (access("/etc/rc.cdrom", F_OK) == 0) {	// /etc/rc.cdrom exists
		SECURITYD_INSTALLMODE();
		smartCardOptions = "off";	// needs writable directories that aren't
	}

	// parse command line arguments
	extern char *optarg;
	extern int optind;
	int arg;
	while ((arg = getopt(argc, argv, "a:c:de:E:fimN:s:t:T:uvWX")) != -1) {
		switch (arg) {
		case 'a':
			authorizationConfig = optarg;
			break;
		case 'c':
			tokenCacheDir = optarg;
			break;
		case 'd':
			debugMode = true;
			break;
		case 'e':
			equivDbFile = optarg;
			break;
        case 'E':
            entropyFile = optarg;
            break;
        case 'f':
            fprintf(stderr, "%s: the -f option is obsolete\n", argv[0]);
            break;
		case 'i':
			keychainAclDefault &= ~CSSM_ACL_KEYCHAIN_PROMPT_INVALID;
			break;
        case 'm':
            mdsIsInstalled = true;
            break;
		case 'N':
			bootstrapName = optarg;
			break;
		case 's':
			smartCardOptions = optarg;
			break;
		case 't':
			if ((maxThreads = atoi(optarg)) < 0)
				maxThreads = 0;
			break;
		case 'T':
			if ((workerTimeout = atoi(optarg)) < 0)
				workerTimeout = 0;
			break;
		case 'W':
			waitForClients = false;
			break;
		case 'u':
			keychainAclDefault &= ~CSSM_ACL_KEYCHAIN_PROMPT_UNSIGNED;
			break;
		case 'v':
			verbose++;
			break;
		case 'X':
			doFork = true;
			reExecute = true;
			break;
		default:
			usage(argv[0]);
		}
	}
	
	// take no non-option arguments
	if (optind < argc)
		usage(argv[0]);
	
	// figure out the bootstrap name
    if (!bootstrapName) {
		bootstrapName = getenv(SECURITYSERVER_BOOTSTRAP_ENV);
		if (!bootstrapName)
		{
			bootstrapName = SECURITYSERVER_BOOTSTRAP_NAME;
		}
		else
		{
			messagingName = bootstrapName;
		}
	}
	else
	{
		messagingName = bootstrapName;
	}
	
	// configure logging first
	if (debugMode) {
		Syslog::open(bootstrapName, LOG_AUTHPRIV, LOG_PERROR);
		Syslog::notice("%s started in debug mode", argv[0]);
	} else {
		Syslog::open(bootstrapName, LOG_AUTHPRIV, LOG_CONS);
	}
    
    // if we're not running as root in production mode, fail
    // in debug mode, issue a warning
    if (uid_t uid = getuid()) {
#if defined(NDEBUG)
        Syslog::alert("Tried to run securityd as user %d: aborted", uid);
        fprintf(stderr, "You are not allowed to run securityd\n");
        exit(1);
#else
        fprintf(stderr, "securityd is unprivileged (uid=%d); some features may not work.\n", uid);
#endif //NDEBUG
    }
    
    // turn into a properly diabolical daemon unless debugMode is on
    if (!debugMode && getppid() != 1) {
		if (!Daemon::incarnate(doFork))
			exit(1);	// can't daemonize
		
		if (reExecute && !Daemon::executeSelf(argv))
			exit(1);	// can't self-execute
	}
        
    // arm signal handlers; code below may generate signals we want to see
    if (signal(SIGCHLD, handleSignals) == SIG_ERR
		|| signal(SIGINT, handleSignals) == SIG_ERR
		|| signal(SIGTERM, handleSignals) == SIG_ERR
		|| signal(SIGPIPE, handleSignals) == SIG_ERR
#if !defined(NDEBUG)
		|| signal(SIGUSR1, handleSignals) == SIG_ERR
#endif //NDEBUG
		|| signal(SIGUSR2, handleSignals) == SIG_ERR) {
		perror("signal");
		exit(1);
	}

	// create an Authorization engine
	Authority authority(authorizationConfig);
	
	// introduce all supported ACL subject types
	new AnyAclSubject::Maker();
	new PasswordAclSubject::Maker();
    new ProtectedPasswordAclSubject::Maker();
    new PromptedAclSubject::Maker();
	new ThresholdAclSubject::Maker();
    new CommentAclSubject::Maker();
 	new ProcessAclSubject::Maker();
	new CodeSignatureAclSubject::Maker();
    new KeychainPromptAclSubject::Maker(keychainAclDefault);
    new PreAuthorizationAcls::OriginMaker();
    new PreAuthorizationAcls::SourceMaker();
	
	// establish the code equivalents database
	CodeSignatures codeSignatures(equivDbFile);
	
    // create the main server object and register it
 	Server server(authority, codeSignatures, bootstrapName);

    // Remember the primary service port to send signal events to
    gMainServerPort = server.primaryServicePort();

    // set server configuration from arguments, if specified
	if (workerTimeout)
		server.timeout(workerTimeout);
	if (maxThreads)
		server.maxThreads(maxThreads);
	server.floatingThread(true);
	server.waitForClients(waitForClients);
	server.verbosity(verbose);
    
	// add the RNG seed timer
# if defined(NDEBUG)
    EntropyManager entropy(server, entropyFile);
# else
    if (getuid() == 0) new EntropyManager(server, entropyFile);
# endif

	// create a smartcard monitor to manage external token devices
	gPCSC = new PCSCMonitor(server, tokenCacheDir, scOptions(smartCardOptions));
    
    // create the RootSession object (if -d, give it graphics and tty attributes)
    RootSession rootSession(server,
		debugMode ? (sessionHasGraphicAccess | sessionHasTTY) : 0);
    
    // install MDS (if needed) and initialize the local CSSM
    server.loadCssm(mdsIsInstalled);
    
	// create the shared memory notification hub
	new SharedMemoryListener(messagingName, kSharedMemoryPoolSize);
	
	// okay, we're ready to roll
	SECURITYD_INITIALIZED((char*)bootstrapName);
	Syslog::notice("Entering service");
    
	// go
	server.run();
	
	// fell out of runloop (should not happen)
	Syslog::alert("Aborting");
    return 1;
}


//
// Issue usage message and die
//
static void usage(const char *me)
{
	fprintf(stderr, "Usage: %s [-dwX]"
		"\n\t[-a authConfigFile]                    Authorization configuration file"
		"\n\t[-c tokencache]                        smartcard token cache directory"
		"\n\t[-e equivDatabase] 					path to code equivalence database"
		"\n\t[-N serviceName]                       MACH service name"
		"\n\t[-s off|on|conservative|aggressive]    smartcard operation level"
		"\n\t[-t maxthreads] [-T threadTimeout]     server thread control"
		"\n", me);
	exit(2);
}


//
// Translate strings (e.g. "conservative") into PCSCMonitor service levels
//
static PCSCMonitor::ServiceLevel scOptions(const char *optionString)
{
	if (optionString)
		if (!strcmp(optionString, "off"))
			return PCSCMonitor::forcedOff;
		else if (!strcmp(optionString, "on"))
			return PCSCMonitor::forcedOn;
		else if (!strcmp(optionString, "conservative"))
			return PCSCMonitor::conservative;
		else if (!strcmp(optionString, "aggressive"))
			return PCSCMonitor::aggressive;
		else if (!strcmp(optionString, "external"))
			return PCSCMonitor::externalDaemon;
		else
			usage("securityd");
	else
		return PCSCMonitor::aggressive;
}


//
// Handle signals.
// We send ourselves a message (through the "self" service), so actual
// actions happen on the normal event loop path. Note that another thread
// may be picking up the message immediately.
//
static void handleSignals(int sig)
{
	SECURITYD_SIGNAL_RECEIVED(sig);
	if (kern_return_t rc = self_client_handleSignal(gMainServerPort, mach_task_self(), sig))
		Syslog::error("self-send failed (mach error %d)", rc);
}