2 * Copyright (c) 2004 Apple Computer, Inc. All Rights Reserved.
4 * @APPLE_LICENSE_HEADER_START@
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
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.
21 * @APPLE_LICENSE_HEADER_END@
26 // tokend - internal tracker for a tokend smartcard driver process
29 #include <security_utilities/logging.h>
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.
40 TokenDaemon::TokenDaemon(RefPointer
<GenericBundle
> 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())
48 switch (ServerChild::state()) {
50 Tokend::ClientSession::servicePort(ServerChild::servicePort());
51 secdebug("tokend", "%p (pid %d) %s has launched", this, pid(), bundlePath().c_str());
54 // tokend died or quit before becoming ready
55 secdebug("tokend", "%p (pid %d) %s failed on startup", this, pid(), bundlePath().c_str());
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.
69 TokenDaemon::~TokenDaemon()
71 secdebug("tokend", "%p (pid %d) %s is being destroyed", this, pid(), bundlePath().c_str());
76 // Calculate a tokenUid as a concatenation of tokend identifier and uid
78 std::string
TokenDaemon::tokenUid() const
80 assert(hasTokenUid());
86 // Access to custom Info.plist fields
88 uint32
TokenDaemon::maxScore() const
90 return cfNumber(CFNumberRef(mMe
->infoPlistItem("TokendBestScore")), INT_MAX
);
95 // Our childAction is to launch tokend after preparing its environment
97 void TokenDaemon::childAction()
100 // permanently relinquish high privilege
102 UnixError::check(::setgid(mGid
));
103 UnixError::check(::setuid(mUid
));
105 // best effort, okay if not
109 secdebug("tokend", "uid=%d gid=%d", getuid(), getgid());
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)
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.
128 void TokenDaemon::dying()
130 ServerChild::dying(); // honor prior engagement
131 fault(true, "token daemon has died"); // flag asynchronous fault
139 void TokenDaemon::fault(bool async
, const char *reason
)
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
);
148 mFaultRelay
->relayFault(async
);
151 CssmError::throwMe(CSSM_ERRCODE_FUNCTION_FAILED
);
156 // A fault signalled from the ClientSession layer is just a (synchronous) fault
157 // of TokenDaemon itself.
159 void TokenDaemon::fault()
161 this->fault(false, "tokend service failed");
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.
170 bool TokenDaemon::probe()
172 secdebug("tokend", "%p probing", this);
173 ClientSession::probe(mScore
, mTokenUid
);
174 secdebug("tokend", "%p probed score=%ld tokenUid=\"%s\"", this, mScore
, mTokenUid
.c_str());
183 FaultRelay::~FaultRelay()
188 // Debug dump support
190 #if defined(DEBUGDUMP)
192 void TokenDaemon::dumpNode()
194 PerGlobal::dumpNode();
196 Debug::dump(" FAULT");
197 Debug::dump(" service=%d/%d",
198 ClientSession::servicePort().port(), ServerChild::servicePort().port());