]> git.saurik.com Git - apple/security.git/blobdiff - Security/libsecurity_utilities/lib/pcsc++.cpp
Security-57031.1.35.tar.gz
[apple/security.git] / Security / libsecurity_utilities / lib / pcsc++.cpp
diff --git a/Security/libsecurity_utilities/lib/pcsc++.cpp b/Security/libsecurity_utilities/lib/pcsc++.cpp
new file mode 100644 (file)
index 0000000..0a79de0
--- /dev/null
@@ -0,0 +1,414 @@
+/*
+ * Copyright (c) 2004,2011-2012,2014 Apple 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@
+ */
+
+
+//
+// pcsc++ - PCSC client interface layer in C++
+//
+#include "pcsc++.h"
+#include <security_utilities/debugging.h>
+#include <PCSC/pcsclite.h>
+#include <PCSC/wintypes.h>
+#include <Security/cssmapple.h>
+
+namespace Security {
+namespace PCSC {
+
+
+//
+// Internal utilities
+//
+static void decode(vector<string> &names, const char *buffer, size_t size)
+{
+       names.clear();
+       for (size_t pos = 0; pos < size - 1; ) {
+               size_t len = strlen(buffer + pos);
+               names.push_back(string(buffer + pos, len));
+               pos += len + 1;
+       }
+}
+
+inline void decode(vector<string> &names, const vector<char> &buffer, size_t size)
+{
+       decode(names, &buffer[0], size);
+}
+
+
+//
+// PCSC domain errors
+//
+Error::Error(unsigned long err) : error(err)
+{
+       SECURITY_EXCEPTION_THROW_PCSC(this, (unsigned int)err);
+}
+
+
+const char *Error::what() const throw ()
+{
+       return pcsc_stringify_error((int32_t)error);
+}
+
+
+void Error::throwMe(unsigned long err)
+{
+       throw Error(err);
+}
+
+
+OSStatus Error::osStatus() const
+{
+       switch (error)
+       {
+       // @@@ more errors should be mapped here
+       case SCARD_W_RESET_CARD:
+               return CSSMERR_CSP_DEVICE_RESET;
+       default:
+               return CSSMERR_CSP_INTERNAL_ERROR;
+       }
+}
+
+int Error::unixError() const
+{
+       return EINVAL;  //@@@ preliminary
+}
+
+
+//
+// PodWrappers
+//
+void ReaderState::set(const char *name, unsigned long known)
+{
+       clearPod();
+       szReader = name;
+       pvUserData = NULL;
+       dwCurrentState = (uint32_t)known;
+}
+
+
+void ReaderState::lastKnown(unsigned long s)
+{
+       // clear out CHANGED and UNAVAILABLE
+       dwCurrentState = (uint32_t)s & ~(SCARD_STATE_CHANGED | SCARD_STATE_UNAVAILABLE);
+}
+
+
+void ReaderState::setATR(const void *atr, size_t size)
+{
+       if (size > sizeof(rgbAtr))
+               Error::throwMe(SCARD_E_INVALID_ATR);
+       memcpy(rgbAtr, atr, size);
+       cbAtr = (uint32_t)size;
+}
+
+
+#if defined(DEBUGDUMP)
+
+void ReaderState::dump()
+{
+       Debug::dump("reader(%s) state=0x%x events=0x%x",
+               szReader ? szReader : "(null)", dwCurrentState, dwEventState);
+       Debug::dumpData(" ATR", rgbAtr, cbAtr);
+}
+
+#endif //DEBUGDUMP
+
+
+//
+// Session objects
+//
+Session::Session()
+       : mIsOpen(false)
+{
+}
+
+
+Session::~Session()
+{
+       close();
+}
+
+
+//
+// (Re)establish PCSC context.
+// Don't fail on SCARD_F_INTERNAL_ERROR (pcscd not running - urgh).
+//
+void Session::open()
+{
+       if (!mIsOpen) {
+               try {
+                       Error::check(::SCardEstablishContext(SCARD_SCOPE_SYSTEM, NULL, NULL, &mContext));
+                       mIsOpen = true;
+                       secdebug("pcsc", "context opened");
+               } catch (const Error &err) {
+                       if (err.error == SCARD_F_INTERNAL_ERROR)
+                       {
+                               secdebug("pcsc", "got internal error; assuming pcscd absent; context not ready");
+                               return;
+                       }
+               }
+       }
+}
+
+void Session::close()
+{
+       if (mIsOpen) {
+               mIsOpen = false;
+               try {
+                       if (mContext)
+                       Error::check(SCardReleaseContext(mContext));
+                       secdebug("pcsc", "context closed");
+               } catch (const Error &err) {
+                       if (err.error == SCARD_F_INTERNAL_ERROR)
+                       {
+                               secdebug("pcsc", "got internal error; assuming pcscd absent; context not ready");
+                               return;
+                       }
+               }
+       }
+}
+
+bool Session::check(long rc)
+{
+       switch (rc) {
+       case SCARD_S_SUCCESS:
+               return true;    // got reader(s), call succeeded
+       case SCARD_E_READER_UNAVAILABLE:
+               return false;   // no readers, but don't treat as error
+       default:
+               Error::throwMe(rc);
+               return false;   // placebo
+       }
+}
+
+
+void Session::listReaders(vector<string> &readers, const char *groups)
+{
+       uint32_t size = 0;
+       if (check(::SCardListReaders(mContext, groups, NULL, &size)))
+       {
+               mReaderBuffer.resize(size);
+               if (check(::SCardListReaders(mContext, groups, &mReaderBuffer[0], &size)))
+               {
+                       decode(readers, mReaderBuffer, size);
+                       return;
+               }
+       }
+       
+       readers.clear();        // treat as success (returning zero readers)
+}
+
+
+//
+// Reader status check
+//
+void Session::statusChange(ReaderState *readers, unsigned int nReaders, long timeout)
+{
+       check(::SCardGetStatusChange(mContext, (uint32_t)timeout, readers, nReaders));
+}
+
+
+//
+// PCSC Card objects
+//
+Card::Card()
+       : mConnectedState(kInitial)
+{
+}
+
+Card::~Card()
+{
+}
+
+void Card::setIOType(unsigned long activeProtocol)
+{
+       switch (activeProtocol)
+       {
+       case SCARD_PROTOCOL_T0:
+               mIOType = SCARD_PCI_T0;
+               break;
+       case SCARD_PROTOCOL_T1:
+               mIOType = SCARD_PCI_T1;
+               break;
+       default:
+               mIOType = SCARD_PCI_RAW;
+               break;
+       }
+}
+
+void Card::connect(Session &session, const char *reader,
+       unsigned long share, unsigned long protocols)
+{
+       uint32_t activeProtocol;
+       Error::check(::SCardConnect(session.mContext,
+               reader, (uint32_t)share, (uint32_t)protocols, &mHandle, &activeProtocol));
+       setIOType(activeProtocol);
+       mConnectedState = kConnected;
+}
+
+void Card::reconnect(unsigned long share, unsigned long protocols, unsigned long initialization)
+{
+       assert(mConnectedState != kInitial);
+
+       uint32_t activeProtocol;
+       Error::check(::SCardReconnect(mHandle, (uint32_t)share, (uint32_t)protocols,
+               (uint32_t)initialization, &activeProtocol));
+       setIOType(activeProtocol);
+       mConnectedState = kConnected;
+}
+
+void Card::disconnect(unsigned long disposition)
+{
+       if (mConnectedState == kConnected)
+       {
+               if (mTransactionNestLevel > 0)
+               {
+                       secdebug("pcsc", "%p: disconnect, dropping: %d transactions", this, mTransactionNestLevel);
+                       mTransactionNestLevel = 0;
+               }
+
+               checkReset(::SCardDisconnect(mHandle, (uint32_t)disposition));
+               didDisconnect();
+               mConnectedState = kInitial;
+       }
+}
+
+void
+Card::checkReset(unsigned int rv)
+{
+       if (rv == SCARD_W_RESET_CARD)
+       {
+               secdebug("pcsc", "%p: card reset during pcsc call, we're disconnected", this);
+               didDisconnect();
+       }
+    Error::check(rv);
+}
+
+void
+Card::didDisconnect()
+{
+       mConnectedState = kDisconnected;
+       mTransactionNestLevel = 0;
+}
+
+void
+Card::didEnd()
+{
+}
+
+void
+Card::transmit(const unsigned char *pbSendBuffer, size_t cbSendLength,
+       unsigned char *pbRecvBuffer, size_t &pcbRecvLength)
+{
+       if (mConnectedState == kDisconnected)
+       {
+               secdebug("pcsc", "%p: transmit after disconnect, reconnecting", this);
+               reconnect();
+       }
+
+       IFDUMPING("pcsc", dump("->", pbSendBuffer, cbSendLength));
+
+       uint32_t tmpRecvLength = (uint32_t)pcbRecvLength;
+       checkReset(::SCardTransmit(mHandle, mIOType, pbSendBuffer, (uint32_t)cbSendLength,
+               NULL, pbRecvBuffer, &tmpRecvLength));
+       pcbRecvLength = tmpRecvLength;
+       
+       IFDUMPING("pcsc", dump("<-", pbRecvBuffer, pcbRecvLength));
+}
+
+void Card::begin()
+{
+       // Only the first transaction started is sent to PCSC
+       if (mTransactionNestLevel == 0)
+       {
+               if (mConnectedState == kDisconnected)
+               {
+                       secdebug("pcsc", "%p: begin transaction after disconnect, reconnecting", this);
+                       reconnect();
+               }
+
+               checkReset(::SCardBeginTransaction(mHandle));
+       }
+       mTransactionNestLevel++;
+       secdebug("pcsc", "%p begin transaction: %d", this, mTransactionNestLevel);
+}
+
+void Card::end(unsigned long disposition)
+{
+       // Only the last transaction ended is sent to PCSC
+       secdebug("pcsc", "%p end transaction: %d", this, mTransactionNestLevel);
+       if (disposition == SCARD_RESET_CARD)
+       {
+               if (mConnectedState == kDisconnected)
+               {
+                       secdebug("pcsc", "%p: end transaction after disconnect, reconnecting to reset card", this);
+                       reconnect();
+               }
+
+               checkReset(::SCardEndTransaction(mHandle, (uint32_t)disposition));
+               didDisconnect();
+       }
+       else if (mTransactionNestLevel > 0)
+       {
+               --mTransactionNestLevel;
+               if (mTransactionNestLevel == 0)
+               {
+                       if (mConnectedState == kDisconnected)
+                               secdebug("pcsc", "%p: end transaction while disconnected ignored", this);
+                       else
+                       {
+                               checkReset(::SCardEndTransaction(mHandle, (uint32_t)disposition));
+                               didEnd();
+                       }
+               }
+       }
+}
+
+void Card::cancel()
+{
+       end(/*SCARD_RESET_CARD*/);
+}
+
+#if defined(DEBUGDUMP)
+
+void
+Card::dump(const char *direction, const unsigned char *buffer, size_t length)
+{
+       Debug::dump("[%02lu]%s:", length, direction); 
+    
+       for (size_t ix = 0; ix < length; ++ix)
+               Debug::dump(" %02x", buffer[ix]);
+
+       Debug::dump("\n");
+}
+
+#endif
+
+
+void Transaction::commitAction()
+{
+       mCarrier.end(mDisposition);
+}
+
+
+}   // namespace PCSC
+}   // namespace Security