/* * Copyright (c) 2004 Apple Computer, 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@ */ // // tokend - internal tracker for a tokend smartcard driver process // #include "tokend.h" #include // // Construct a TokenDaemon. // This will (try to) execute the actual tokend at 'path'; it will not communicate // with it beyond the standard securityd checkin mechanism. // The constructor will return if the tokend is either checked in and ready, or // it has died (or been unable to start at all). It's then our owner's responsibility // to manage us from there, including deleting us eventually. // TokenDaemon::TokenDaemon(RefPointer code, const string &reader, const PCSC::ReaderState &readerState, TokenCache &cache) : Tokend::ClientSession(Allocator::standard(), Allocator::standard()), mMe(code), mReaderName(reader), mState(readerState), mFaultRelay(NULL), mFaulted(false), mProbed(false), mUid(cache.tokendUid()), mGid(cache.tokendGid()) { this->fork(); switch (ServerChild::state()) { case alive: Tokend::ClientSession::servicePort(ServerChild::servicePort()); secdebug("tokend", "%p (pid %d) %s has launched", this, pid(), bundlePath().c_str()); break; case dead: // tokend died or quit before becoming ready secdebug("tokend", "%p (pid %d) %s failed on startup", this, pid(), bundlePath().c_str()); break; default: assert(false); } } // // The destructor for TokenDaemon *may* be called with tokend still alive. // We rely on ServerChild's destructor to kill it for us. // If we wanted to do something especally nice just for tokend (such as sending // a "go die" message), we'd do it here. // TokenDaemon::~TokenDaemon() { secdebug("tokend", "%p (pid %d) %s is being destroyed", this, pid(), bundlePath().c_str()); } // // Calculate a tokenUid as a concatenation of tokend identifier and uid // std::string TokenDaemon::tokenUid() const { assert(hasTokenUid()); return mTokenUid; } // // Access to custom Info.plist fields // uint32 TokenDaemon::maxScore() const { return cfNumber(CFNumberRef(mMe->infoPlistItem("TokendBestScore")), INT_MAX); } // // Our childAction is to launch tokend after preparing its environment // void TokenDaemon::childAction() { // permanently relinquish high privilege #if defined(NDEBUG) UnixError::check(::setgid(mGid)); UnixError::check(::setuid(mUid)); #else //NDEBUG // best effort, okay if not ::setgid(mGid); ::setuid(mUid); #endif //NDEBUG secdebug("tokend", "uid=%d gid=%d", getuid(), getgid()); // go run the tokend char protocol[20]; snprintf(protocol, sizeof(protocol), "%d", TDPROTOVERSION); secdebug("tokend", "executing %s(\"%s\",%s)", mMe->executablePath().c_str(), mReaderName.c_str(), protocol); execl(mMe->executablePath().c_str(), mMe->executablePath().c_str(), protocol, // #1: protocol version mReaderName.c_str(), // #2: reader name CssmData::wrap(mState).toHex().c_str(), // #3: PCSC reader state (hex) NULL); } // // This will be called (by the UnixChild layer) when UNIX tells us that our tokend // has died. That means it's quite dead (a Zombie) already. // void TokenDaemon::dying() { ServerChild::dying(); // honor prior engagement fault(true, "token daemon has died"); // flag asynchronous fault } // // Declare a fault. //@@@ Semantics TBD. // void TokenDaemon::fault(bool async, const char *reason) { if (!mFaulted) { secdebug("tokend", "%p declaring %s FAULT condition: %s", this, async ? "ASYNCHRONOUS" : "SYNCHRONOUS", reason); Syslog::notice("card in reader %s has faulted (%s)", mReaderName.c_str(), reason); mFaulted = true; if (mFaultRelay) mFaultRelay->relayFault(async); } if (!async) CssmError::throwMe(CSSM_ERRCODE_FUNCTION_FAILED); } // // A fault signalled from the ClientSession layer is just a (synchronous) fault // of TokenDaemon itself. // void TokenDaemon::fault() { this->fault(false, "tokend service failed"); } // // Overridden Tokend::ClientSession methods (to siphon off some return data). // Note that this does NOT include the Access magic; you still have to use // TokenDaemon::Access to mediate the call. // bool TokenDaemon::probe() { secdebug("tokend", "%p probing", this); ClientSession::probe(mScore, mTokenUid); secdebug("tokend", "%p probed score=%ld tokenUid=\"%s\"", this, mScore, mTokenUid.c_str()); mProbed = true; return mScore > 0; } // // FaultRelay // FaultRelay::~FaultRelay() { /* virtual */ } // // Debug dump support // #if defined(DEBUGDUMP) void TokenDaemon::dumpNode() { PerGlobal::dumpNode(); if (mFaulted) Debug::dump(" FAULT"); Debug::dump(" service=%d/%d", ClientSession::servicePort().port(), ServerChild::servicePort().port()); } #endif //DEBUGDUMP