]> git.saurik.com Git - apple/security.git/blobdiff - securityd/src/tokend.cpp
Security-57031.1.35.tar.gz
[apple/security.git] / securityd / src / tokend.cpp
diff --git a/securityd/src/tokend.cpp b/securityd/src/tokend.cpp
new file mode 100644 (file)
index 0000000..268e322
--- /dev/null
@@ -0,0 +1,201 @@
+/*
+ * Copyright (c) 2004-2006 Apple Computer, Inc. All Rights Reserved.
+ * 
+ * @APPLE_LICENSE_HEADER_START@
+ * 
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ * 
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ * 
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+
+//
+// tokend - internal tracker for a tokend smartcard driver process
+//
+#include "tokend.h"
+#include <security_utilities/logging.h>
+
+
+//
+// Construct a TokenDaemon.
+// This will (try to) execute the actual tokend at 'path'; it will not communicate
+// with it beyond the standard securityd checkin mechanism.
+// The constructor will return if the tokend is either checked in and ready, or
+// it has died (or been unable to start at all). It's then our owner's responsibility
+// to manage us from there, including deleting us eventually.
+//
+TokenDaemon::TokenDaemon(RefPointer<Bundle> code,
+               const string &reader, const PCSC::ReaderState &readerState, TokenCache &cache)
+       : Tokend::ClientSession(Allocator::standard(), Allocator::standard()),
+         mMe(code), mReaderName(reader), mState(readerState),
+         mFaultRelay(NULL), mFaulted(false), mProbed(false),
+         mUid(cache.tokendUid()), mGid(cache.tokendGid())
+{
+       this->fork();
+       switch (ServerChild::state()) {
+       case alive:
+               Tokend::ClientSession::servicePort(ServerChild::servicePort());
+               secdebug("tokend", "%p (pid %d) %s has launched", this, pid(), bundlePath().c_str());
+               break;
+       case dead:
+               // tokend died or quit before becoming ready
+               secdebug("tokend", "%p (pid %d) %s failed on startup", this, pid(), bundlePath().c_str());
+               break;
+       default:
+               assert(false);
+       }
+}
+
+
+//
+// The destructor for TokenDaemon *may* be called with tokend still alive.
+// We rely on ServerChild's destructor to kill it for us.
+// If we wanted to do something especally nice just for tokend (such as sending
+// a "go die" message), we'd do it here.
+//
+TokenDaemon::~TokenDaemon()
+{
+       secdebug("tokend", "%p (pid %d) %s is being destroyed", this, pid(), bundlePath().c_str());
+}
+
+
+//
+// Calculate a tokenUid as a concatenation of tokend identifier and uid
+//
+std::string TokenDaemon::tokenUid() const
+{
+       assert(hasTokenUid());
+       return mTokenUid;
+}
+
+
+//
+// Access to custom Info.plist fields
+//
+uint32 TokenDaemon::maxScore() const
+{
+       return cfNumber(CFNumberRef(mMe->infoPlistItem("TokendBestScore")), INT_MAX);
+}
+
+
+//
+// Our childAction is to launch tokend after preparing its environment
+//
+void TokenDaemon::childAction()
+{
+       
+       // permanently relinquish high privilege
+#if defined(NDEBUG)
+       UnixError::check(::setgid(mGid));
+       UnixError::check(::setuid(mUid));
+#else //NDEBUG
+       // best effort, okay if not
+       ::setgid(mGid);
+       ::setuid(mUid);
+#endif //NDEBUG
+       secdebug("tokend", "uid=%d gid=%d", getuid(), getgid());
+
+       // go run the tokend
+       char protocol[20]; snprintf(protocol, sizeof(protocol), "%d", TDPROTOVERSION);
+       secdebug("tokend", "executing %s(\"%s\",%s)",
+               mMe->executablePath().c_str(), mReaderName.c_str(), protocol);
+       execl(mMe->executablePath().c_str(),
+               mMe->executablePath().c_str(),
+               protocol,                                                                       // #1: protocol version
+               mReaderName.c_str(),                                            // #2: reader name
+               CssmData::wrap(mState).toHex().c_str(),         // #3: PCSC reader state (hex)
+               NULL);
+}
+
+
+//
+// This will be called (by the UnixChild layer) when UNIX tells us that our tokend
+// has died. That means it's quite dead (a Zombie) already.
+//
+void TokenDaemon::dying()
+{
+       ServerChild::dying();                                   // honor prior engagement
+       fault(true, "token daemon has died");   // flag asynchronous fault
+}
+
+
+//
+// Declare a fault.
+//@@@ Semantics TBD.
+//
+void TokenDaemon::fault(bool async, const char *reason)
+{
+       if (!mFaulted) {
+               secdebug("tokend", "%p declaring %s FAULT condition: %s",
+                       this, async ? "ASYNCHRONOUS" : "SYNCHRONOUS", reason);
+               Syslog::notice("card in reader %s has faulted (%s)",
+                       mReaderName.c_str(), reason);
+               mFaulted = true;
+               if (mFaultRelay)
+                       mFaultRelay->relayFault(async);
+       }
+       if (!async)
+               CssmError::throwMe(CSSM_ERRCODE_FUNCTION_FAILED);
+}
+
+
+//
+// A fault signalled from the ClientSession layer is just a (synchronous) fault
+// of TokenDaemon itself.
+//
+void TokenDaemon::fault()
+{
+       this->fault(false, "tokend service failed");
+}
+
+
+//
+// Overridden Tokend::ClientSession methods (to siphon off some return data).
+// Note that this does NOT include the Access magic; you still have to use
+// TokenDaemon::Access to mediate the call.
+//
+bool TokenDaemon::probe()
+{
+       secdebug("tokend", "%p probing", this);
+       ClientSession::probe(mScore, mTokenUid);
+       secdebug("tokend", "%p probed score=%d tokenUid=\"%s\"", this, mScore, mTokenUid.c_str());
+       mProbed = true;
+       return mScore > 0;
+}
+
+
+//
+// FaultRelay
+//
+FaultRelay::~FaultRelay()
+{ /* virtual */ }
+
+
+//
+// Debug dump support
+//
+#if defined(DEBUGDUMP)
+
+void TokenDaemon::dumpNode()
+{
+       PerGlobal::dumpNode();
+       if (mFaulted)
+               Debug::dump(" FAULT");
+       Debug::dump(" service=%d/%d",
+               ClientSession::servicePort().port(), ServerChild::servicePort().port());
+}
+
+#endif //DEBUGDUMP