1/* 2 * Copyright (c) 2004 Apple Computer, Inc. All Rights Reserved. 3 * 4 * @APPLE_LICENSE_HEADER_START@ 5 * 6 * This file contains Original Code and/or Modifications of Original Code 7 * as defined in and that are subject to the Apple Public Source License 8 * Version 2.0 (the 'License'). You may not use this file except in 9 * compliance with the License. Please obtain a copy of the License at 10 * http://www.opensource.apple.com/apsl/ and read it before using this 11 * file. 12 * 13 * The Original Code and all software distributed under the License are 14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 18 * Please see the License for the specific language governing rights and 19 * limitations under the License. 20 * 21 * @APPLE_LICENSE_HEADER_END@ 22 */ 23 24 25// 26// tokend - internal tracker for a tokend smartcard driver process 27// 28#include "tokend.h" 29#include <security_utilities/logging.h> 30 31 32// 33// Construct a TokenDaemon. 34// This will (try to) execute the actual tokend at 'path'; it will not communicate 35// with it beyond the standard securityd checkin mechanism. 36// The constructor will return if the tokend is either checked in and ready, or 37// it has died (or been unable to start at all). It's then our owner's responsibility 38// to manage us from there, including deleting us eventually. 39// 40TokenDaemon::TokenDaemon(RefPointer<Bundle> code, 41 const string &reader, const PCSC::ReaderState &readerState, TokenCache &cache) 42 : Tokend::ClientSession(Allocator::standard(), Allocator::standard()), 43 mMe(code), mReaderName(reader), mState(readerState), 44 mFaultRelay(NULL), mFaulted(false), mProbed(false), 45 mUid(cache.tokendUid()), mGid(cache.tokendGid()) 46{ 47 this->fork(); 48 switch (ServerChild::state()) { 49 case alive: 50 Tokend::ClientSession::servicePort(ServerChild::servicePort()); 51 secdebug("tokend", "%p (pid %d) %s has launched", this, pid(), bundlePath().c_str()); 52 break; 53 case dead: 54 // tokend died or quit before becoming ready 55 secdebug("tokend", "%p (pid %d) %s failed on startup", this, pid(), bundlePath().c_str()); 56 break; 57 default: 58 assert(false); 59 } 60} 61 62 63// 64// The destructor for TokenDaemon *may* be called with tokend still alive. 65// We rely on ServerChild's destructor to kill it for us. 66// If we wanted to do something especally nice just for tokend (such as sending 67// a "go die" message), we'd do it here. 68// 69TokenDaemon::~TokenDaemon() 70{ 71 secdebug("tokend", "%p (pid %d) %s is being destroyed", this, pid(), bundlePath().c_str()); 72} 73 74 75// 76// Calculate a tokenUid as a concatenation of tokend identifier and uid 77// 78std::string TokenDaemon::tokenUid() const 79{ 80 assert(hasTokenUid()); 81 return mTokenUid; 82} 83 84 85// 86// Access to custom Info.plist fields 87// 88uint32 TokenDaemon::maxScore() const 89{ 90 return cfNumber(CFNumberRef(mMe->infoPlistItem("TokendBestScore")), INT_MAX); 91} 92 93 94// 95// Our childAction is to launch tokend after preparing its environment 96// 97void TokenDaemon::childAction() 98{ 99 100 // permanently relinquish high privilege 101#if defined(NDEBUG) 102 UnixError::check(::setgid(mGid)); 103 UnixError::check(::setuid(mUid)); 104#else //NDEBUG 105 // best effort, okay if not 106 ::setgid(mGid); 107 ::setuid(mUid); 108#endif //NDEBUG 109 secdebug("tokend", "uid=%d gid=%d", getuid(), getgid()); 110 111 // go run the tokend 112 char protocol[20]; snprintf(protocol, sizeof(protocol), "%d", TDPROTOVERSION); 113 secdebug("tokend", "executing %s(\"%s\",%s)", 114 mMe->executablePath().c_str(), mReaderName.c_str(), protocol); 115 execl(mMe->executablePath().c_str(), 116 mMe->executablePath().c_str(), 117 protocol, // #1: protocol version 118 mReaderName.c_str(), // #2: reader name 119 CssmData::wrap(mState).toHex().c_str(), // #3: PCSC reader state (hex) 120 NULL); 121} 122 123 124// 125// This will be called (by the UnixChild layer) when UNIX tells us that our tokend 126// has died. That means it's quite dead (a Zombie) already. 127// 128void TokenDaemon::dying() 129{ 130 ServerChild::dying(); // honor prior engagement 131 fault(true, "token daemon has died"); // flag asynchronous fault 132} 133 134 135// 136// Declare a fault. 137//@@@ Semantics TBD. 138// 139void TokenDaemon::fault(bool async, const char *reason) 140{ 141 if (!mFaulted) { 142 secdebug("tokend", "%p declaring %s FAULT condition: %s", 143 this, async ? "ASYNCHRONOUS" : "SYNCHRONOUS", reason); 144 Syslog::notice("card in reader %s has faulted (%s)", 145 mReaderName.c_str(), reason); 146 mFaulted = true; 147 if (mFaultRelay) 148 mFaultRelay->relayFault(async); 149 } 150 if (!async) 151 CssmError::throwMe(CSSM_ERRCODE_FUNCTION_FAILED); 152} 153 154 155// 156// A fault signalled from the ClientSession layer is just a (synchronous) fault 157// of TokenDaemon itself. 158// 159void TokenDaemon::fault() 160{ 161 this->fault(false, "tokend service failed"); 162} 163 164 165// 166// Overridden Tokend::ClientSession methods (to siphon off some return data). 167// Note that this does NOT include the Access magic; you still have to use 168// TokenDaemon::Access to mediate the call. 169// 170bool TokenDaemon::probe() 171{ 172 secdebug("tokend", "%p probing", this); 173 ClientSession::probe(mScore, mTokenUid); 174 secdebug("tokend", "%p probed score=%d tokenUid=\"%s\"", this, mScore, mTokenUid.c_str()); 175 mProbed = true; 176 return mScore > 0; 177} 178 179 180// 181// FaultRelay 182// 183FaultRelay::~FaultRelay() 184{ /* virtual */ } 185 186 187// 188// Debug dump support 189// 190#if defined(DEBUGDUMP) 191 192void TokenDaemon::dumpNode() 193{ 194 PerGlobal::dumpNode(); 195 if (mFaulted) 196 Debug::dump(" FAULT"); 197 Debug::dump(" service=%d/%d", 198 ClientSession::servicePort().port(), ServerChild::servicePort().port()); 199} 200 201#endif //DEBUGDUMP 202