Tuesday, April 17, 2012

console app that monitors smart cards

Here is a console app that demonstrates how to tie the smart card functions already covered into a working monitor that prints reader state changes and the certs and subject names for the cards that are present.

#pragma once

// Exclude rarely-used stuff from
// Windows headers
#define WIN32_LEAN_AND_MEAN
// skip min/max macros from
// Windows Headers
#define NOMINMAX
// Windows Header Files:
#include <windows.h>
#include <Unknwn.h>
#include <winscard.h>
#include <ncrypt.h>
#include <Wincrypt.h>
#include <credentialprovider.h>

// C++ library header files:
#include <type_traits>
#include <algorithm>
#include <new>
#include <memory>
#include <utility>
#include <limits>
#include <iterator>
#include <thread>
#include <future>
#include <mutex>
#include <vector>
#include <iostream>
#include <iomanip>

// local header files:
#define LIBRARIES_NAMESPACE nslib
#include <libraries.h>
namespace lib = LIBRARIES_NAMESPACE;

#include "..\win32project1\scard_monitor.h"

Set a breakpoint in each of these functions and see where error codes first make an appearance in this module. the static anchor is a device to make sure that the compiler does not eliminate or fold them together.

void unique_error_report_initiated(
  HRESULT value, 
  unique_hresult_def::tag &&)
{
  static HRESULT anchor;
  anchor = value;
}

void unique_error_report_reset(
  HRESULT value, 
  unique_hresult_def::tag &&)
{
  static HRESULT anchor;
  anchor = value;
}

void unique_error_report_initiated(
  DWORD value, 
  unique_winerror_def::tag &&)
{
  static DWORD anchor;
  anchor = value;
}

void unique_error_report_reset(
  DWORD value, 
  unique_winerror_def::tag &&)
{
  static DWORD anchor;
  anchor = value;
}

Helper functions to print status and certificate properties.

template<typename Certificate>
void printCertificates(
  lib::rng::range<Certificate> certificates)
{
  for (auto & key : certificates) {
    unique_winerror winerror;

    PCCERT_CONTEXT certcontext = (
      CertCreateCertificateContext(
        X509_ASN_ENCODING | 
          PKCS_7_ASN_ENCODING,
        &key.cert[0],
        key.cert.size()
      )
    );
    winerror = make_winerror_if(!certcontext);

    if (!winerror) {
      std::wcout 
        << L"could not get cert context" 
        << std::endl;
      continue;
    }

    DWORD sizesubject = 0;
    std::wstring subjectname;
    for (bool getsize = true; ; getsize = false) {
      sizesubject = CertGetNameString(
        certcontext,
        CERT_NAME_FRIENDLY_DISPLAY_TYPE,
        0,
        NULL,
        getsize ? nullptr : &subjectname[0],
        sizesubject
      );
      if (sizesubject == 1) {
        std::wcout 
          << L"could not get subject name" 
          << std::endl;
        break;
      }
      if (getsize) {
        subjectname.resize(sizesubject - 1);
      } else {
        std::wcout 
          << L"key name: " << key.key.c_str() 
          << L" subject name: " << subjectname.c_str() 
          << std::endl;
        break;
      }
    }
  }
}

template<typename Stream>
Stream& printSCardState(Stream& stream, DWORD state)
{
  stream
    << ((state == SCARD_STATE_UNAWARE)  ? 
      L" SCARD_STATE_UNAWARE" : L"")
    << ((state & SCARD_STATE_PRESENT)  ? 
      L" SCARD_STATE_PRESENT" : L"")
    << ((state & SCARD_STATE_ATRMATCH)  ? 
      L" SCARD_STATE_ATRMATCH" : L"")
    << ((state & SCARD_STATE_CHANGED)  ? 
      L" SCARD_STATE_CHANGED" : L"")
    << ((state & SCARD_STATE_EMPTY)  ? 
      L" SCARD_STATE_EMPTY" : L"")
    << ((state & SCARD_STATE_EXCLUSIVE)  ? 
      L" SCARD_STATE_EXCLUSIVE" : L"")
    << ((state & SCARD_STATE_IGNORE)  ? 
      L" SCARD_STATE_IGNORE" : L"")
    << ((state & SCARD_STATE_INUSE)  ? 
      L" SCARD_STATE_INUSE" : L"")
    << ((state & SCARD_STATE_MUTE)  ? 
      L" SCARD_STATE_MUTE" : L"")
    << ((state & SCARD_STATE_UNAVAILABLE)  ? 
      L" SCARD_STATE_UNAVAILABLE" : L"")
    << ((state & SCARD_STATE_UNKNOWN)  ? 
      L" SCARD_STATE_UNKNOWN" : L"")
    << ((state & SCARD_STATE_UNPOWERED)  ? 
      L" SCARD_STATE_UNPOWERED" : L"")
  ;
  return stream;
}

int wmain(int argc, WCHAR* argv[])
{
  unique_winerror winerror;
  for (;;) {
    SCARDCONTEXT context = NULL;

    HANDLE waitfor[] = {SCardAccessStartedEvent()};
    ON_UNWIND_AUTO([] {SCardReleaseStartedEvent();});

    winerror = smart_card::monitor_smartcard_readers(
      [&](SCARDCONTEXT context) {
        context = context;
      },
      [&]() {
        context = NULL;
      },
      [&]() -> bool {
        if (WAIT_OBJECT_0 != WaitForMultipleObjects(
          lib::rng::size(waitfor), 
          waitfor, 
          FALSE, 
          INFINITE)
        ) {
          // monitor_smardcard_readers will return 
          // SCARD_E_CANCELLED
          return false;
        }
        return true;
      },
      [&](
        lib::rng::range<SCARD_READERSTATE*> readersrange
      ) {
        for (auto & state : readersrange) {
          auto stateChanges = (
            (state.dwCurrentState ^ state.dwEventState) & 
              std::numeric_limits<unsigned short>::max()
          );
          std::wcout
            << L"nothread - "
            << state.szReader
            << L" changes: " 
            << std::hex 
            << std::showbase 
            << stateChanges
            << L"["
          ;
          printSCardState(std::wcout, stateChanges)
            << L"] state: " 
            << std::hex 
            << std::showbase 
            << state.dwEventState
            << L"["
          ;
          printSCardState(std::wcout, state.dwEventState)
            << L"]"
            << std::endl
          ;

          if (
            state.dwCurrentState != 
              SCARD_STATE_UNAWARE &&
            ((state.dwEventState & 
              SCARD_STATE_PRESENT) != 
                SCARD_STATE_PRESENT ||
              stateChanges == SCARD_STATE_INUSE ||
              stateChanges == SCARD_STATE_UNPOWERED ||
              (state.dwEventState & (
                SCARD_STATE_UNPOWERED | 
                SCARD_STATE_EMPTY | 
                SCARD_STATE_IGNORE | 
                SCARD_STATE_UNKNOWN | 
                SCARD_STATE_UNAVAILABLE | 
                SCARD_STATE_MUTE)) ||
            state.cbAtr == 0)
          ) {
            // we have seen this reader before 
            // and one of:
            // no card
            // only flipped INUSE
            // only flipped UNPOWERED
            // UNPOWERED EMPTY UNKNOWN UNAVAILABLE MUTE
            // no atr
            //
            // don't try to read the card
            continue;
          }

          CardWithProvider card;
          std::tie(winerror, card) = 
            smart_card::smartcard_name_and_provider(
              lib::rng::make_range_raw(state.rgbAtr)
          );
          if (!winerror) {
            continue;
          }

          KeyWithCertificateVector certificates;
          std::tie(winerror, certificates) = 
            smart_card::smartcard_certificates(
              card.kspname);
          if (!winerror) {
            continue;
          }

          std::wcout << L"nothread -"
          << L" kspname: " << card.kspname.c_str()
          << std::endl;

          printCertificates(
            lib::rng::make_range_raw(certificates)
          );
        }
      }
    );
    winerror.suppress();
  }

  return 0;
}

No comments:

Post a Comment