/ securityd / src / tokend.cpp
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