/* * 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 #include "server.h" #include "entropy.h" #include "authority.h" #include "session.h" #include "notifications.h" #include "pcscmonitor.h" #include "auditevents.h" #include "self.h" #include #include #include #include #include #include #include #include // ACL subject types (their makers are instantiated here) #include #include #include #include #include #include #include #include #include #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:imN: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 '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(debugMode ? (sessionHasGraphicAccess | sessionHasTTY) : 0, server); // create a monitor thread to watch for audit session events AuditMonitor audits(gMainServerPort); audits.run(); // 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); }