]> git.saurik.com Git - apple/security.git/blob - securityd/src/tokend.cpp
Security-59754.80.3.tar.gz
[apple/security.git] / securityd / src / 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