tokend.cpp
1 /* 2 * Copyright (c) 2004-2006 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 // 40 TokenDaemon::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 secinfo("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 secinfo("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 // 69 TokenDaemon::~TokenDaemon() 70 { 71 secinfo("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 // 78 std::string TokenDaemon::tokenUid() const 79 { 80 assert(hasTokenUid()); 81 return mTokenUid; 82 } 83 84 85 // 86 // Access to custom Info.plist fields 87 // 88 uint32 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 // 97 void 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 #ifndef __clang_analyzer__ 106 // best effort, okay if not 107 ::setgid(mGid); 108 ::setuid(mUid); 109 #endif // clang_analyzer 110 #endif //NDEBUG 111 secinfo("tokend", "uid=%d gid=%d", getuid(), getgid()); 112 113 // go run the tokend 114 char protocol[20]; snprintf(protocol, sizeof(protocol), "%d", TDPROTOVERSION); 115 secinfo("tokend", "executing %s(\"%s\",%s)", 116 mMe->executablePath().c_str(), mReaderName.c_str(), protocol); 117 execl(mMe->executablePath().c_str(), 118 mMe->executablePath().c_str(), 119 protocol, // #1: protocol version 120 mReaderName.c_str(), // #2: reader name 121 CssmData::wrap(mState).toHex().c_str(), // #3: PCSC reader state (hex) 122 NULL); 123 } 124 125 126 // 127 // This will be called (by the UnixChild layer) when UNIX tells us that our tokend 128 // has died. That means it's quite dead (a Zombie) already. 129 // 130 void TokenDaemon::dying() 131 { 132 ServerChild::dying(); // honor prior engagement 133 fault(true, "token daemon has died"); // flag asynchronous fault 134 } 135 136 137 // 138 // Declare a fault. 139 //@@@ Semantics TBD. 140 // 141 void TokenDaemon::fault(bool async, const char *reason) 142 { 143 if (!mFaulted) { 144 secinfo("tokend", "%p declaring %s FAULT condition: %s", 145 this, async ? "ASYNCHRONOUS" : "SYNCHRONOUS", reason); 146 Syslog::notice("card in reader %s has faulted (%s)", 147 mReaderName.c_str(), reason); 148 mFaulted = true; 149 if (mFaultRelay) 150 mFaultRelay->relayFault(async); 151 } 152 if (!async) 153 CssmError::throwMe(CSSM_ERRCODE_FUNCTION_FAILED); 154 } 155 156 157 // 158 // A fault signalled from the ClientSession layer is just a (synchronous) fault 159 // of TokenDaemon itself. 160 // 161 void TokenDaemon::fault() 162 { 163 this->fault(false, "tokend service failed"); 164 } 165 166 167 // 168 // Overridden Tokend::ClientSession methods (to siphon off some return data). 169 // Note that this does NOT include the Access magic; you still have to use 170 // TokenDaemon::Access to mediate the call. 171 // 172 bool TokenDaemon::probe() 173 { 174 secinfo("tokend", "%p probing", this); 175 ClientSession::probe(mScore, mTokenUid); 176 secinfo("tokend", "%p probed score=%d tokenUid=\"%s\"", this, mScore, mTokenUid.c_str()); 177 mProbed = true; 178 return mScore > 0; 179 } 180 181 182 // 183 // FaultRelay 184 // 185 FaultRelay::~FaultRelay() 186 { /* virtual */ } 187 188 189 // 190 // Debug dump support 191 // 192 #if defined(DEBUGDUMP) 193 194 void TokenDaemon::dumpNode() 195 { 196 PerGlobal::dumpNode(); 197 if (mFaulted) 198 Debug::dump(" FAULT"); 199 Debug::dump(" service=%d/%d", 200 ClientSession::servicePort().port(), ServerChild::servicePort().port()); 201 } 202 203 #endif //DEBUGDUMP