/*
 * An example of using Smart Card certificate in a call to LsaLogonUser
 *
 * Copyright (c) 2009 Mounir IDRASSI <mounir.idrassi@idrix.fr>. All rights reserved.
 *
 * This program is distributed in the hope that it will be useful, 
 * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 
 * or FITNESS FOR A PARTICULAR PURPOSE.
 * 
 */


#ifndef _WIN32_WINNT
#define _WIN32_WINNT 0x0600
#endif						

#include <stdio.h>
#include <tchar.h>
#include <windows.h>
#include <Ntsecapi.h>

#ifndef STATUS_SUCCESS
#define STATUS_SUCCESS  ((NTSTATUS)0x00000000L)
#endif

#pragma comment(lib, "Secur32.lib")

// 1Byte packing for this structure
#pragma pack(push, KerbCspInfo, 1)
typedef struct _KERB_SMARTCARD_CSP_INFO
{   
   DWORD dwCspInfoLen; 
   DWORD MessageType; 
   union {     
      PVOID ContextInformation;    
      ULONG64 SpaceHolderForWow64;  
   }; 
   DWORD flags; 
   DWORD KeySpec; 
   ULONG nCardNameOffset; 
   ULONG nReaderNameOffset; 
   ULONG nContainerNameOffset; 
   ULONG nCSPNameOffset; 
   TCHAR bBuffer;
} KERB_SMARTCARD_CSP_INFO,  *PKERB_SMARTCARD_CSP_INFO;
#pragma pack(pop, KerbCspInfo)


// Set the SeTcbPrivilege of the current process
BOOL SetSeTcbPrivilege() 
{
   TOKEN_PRIVILEGES tp;
   LUID luid;
   HANDLE hProcessToken;  

   if ( !OpenProcessToken(GetCurrentProcess(),
               TOKEN_ADJUST_PRIVILEGES,
               &hProcessToken) )
   {
      _tprintf(_T("OpenProcessToken failed with error 0x%.8X\n"), GetLastError() ); 
      return FALSE;
   }

   if ( !LookupPrivilegeValue( 
         NULL,
         SE_TCB_NAME,
         &luid ) )
   {
      _tprintf(_T("LookupPrivilegeValue failed with error 0x%.8X\n"), GetLastError() ); 
      CloseHandle(hProcessToken);
      return FALSE; 
   }

   tp.PrivilegeCount = 1;
   tp.Privileges[0].Luid = luid;
   tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;

   // Enable the privilege
   if ( !AdjustTokenPrivileges(
            hProcessToken, 
            FALSE, 
            &tp, 
            sizeof(TOKEN_PRIVILEGES), 
            (PTOKEN_PRIVILEGES) NULL, 
            (PDWORD) NULL) )
   { 
      _tprintf(_T("AdjustTokenPrivileges failed with error 0x%.8X\n"), GetLastError() ); 
      CloseHandle(hProcessToken);
      return FALSE; 
   }

   CloseHandle(hProcessToken);

   if (GetLastError() == ERROR_NOT_ALL_ASSIGNED)
   {
      _tprintf(_T("The token does not have the privilege \"SeTcbPrivilege\". \n"));
      return FALSE;
   }

   return TRUE;
}

// Build the authentication data used by LsaLogonUser
void ConstructAuthInfo(LPBYTE* ppbAuthInfo, ULONG *pulAuthInfoLen)
{
   WCHAR szCardName[] = L""; // no card name specified but you can put one if you want
   WCHAR szContainerName[] = L""; // default container
   WCHAR szReaderName[] = L"OMNIKEY CardMan 5x21 0";
   WCHAR szCspName[] = L"IDRIX Generic Cryptographic Service Provider";
   WCHAR szPin[] = L"1234";
   ULONG ulPinByteLen = wcslen(szPin) * sizeof(WCHAR);
   WCHAR szUserName[] = L"tuser1";
   ULONG ulUserByteLen = wcslen(szUserName) * sizeof(WCHAR);
   WCHAR szDomainName[] = L"idrix.local";
   ULONG ulDomainByteLen = wcslen(szDomainName) * sizeof(WCHAR);
   LPBYTE pbAuthInfo = NULL;
   ULONG  ulAuthInfoLen = 0;
   KERB_CERTIFICATE_LOGON *pKerbCertLogon;
   KERB_SMARTCARD_CSP_INFO *pKerbCspInfo;
   LPBYTE pbDomainBuffer, pbUserBuffer, pbPinBuffer;
   LPBYTE pbCspData;
   LPBYTE pbCspDataContent;
   
   ULONG ulCspDataLen = sizeof(KERB_SMARTCARD_CSP_INFO) - sizeof(TCHAR) + 
      (wcslen(szCardName) + 1) * sizeof(WCHAR) +
      (wcslen(szCspName) + 1) * sizeof(WCHAR) +
      (wcslen(szContainerName) + 1) * sizeof(WCHAR) + 
      (wcslen(szReaderName) + 1) * sizeof(WCHAR);

   ulAuthInfoLen = sizeof(KERB_CERTIFICATE_LOGON) + 
      ulDomainByteLen + sizeof(WCHAR) +
      ulUserByteLen + sizeof(WCHAR) +
      ulPinByteLen + sizeof(WCHAR) +       
      ulCspDataLen;

   pbAuthInfo = (LPBYTE) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, ulAuthInfoLen);
   ZeroMemory(pbAuthInfo, ulAuthInfoLen);

   pbDomainBuffer = pbAuthInfo + sizeof(KERB_CERTIFICATE_LOGON);
   pbUserBuffer = pbDomainBuffer + ulDomainByteLen + sizeof(WCHAR);
   pbPinBuffer = pbUserBuffer + ulUserByteLen + sizeof(WCHAR);
   pbCspData = pbPinBuffer + ulPinByteLen + sizeof(WCHAR);

   memcpy(pbDomainBuffer, szDomainName, ulDomainByteLen);
   memcpy(pbUserBuffer, szUserName, ulUserByteLen);
   memcpy(pbPinBuffer, szPin, ulPinByteLen);

   pKerbCertLogon = (KERB_CERTIFICATE_LOGON*) pbAuthInfo;

   pKerbCertLogon->MessageType = KerbCertificateLogon;
   pKerbCertLogon->DomainName.Length = (USHORT) ulDomainByteLen;
   pKerbCertLogon->DomainName.MaximumLength = (USHORT) (ulDomainByteLen + sizeof(WCHAR));
   pKerbCertLogon->DomainName.Buffer = (PWSTR) pbDomainBuffer;
   pKerbCertLogon->UserName.Length = (USHORT) ulUserByteLen;
   pKerbCertLogon->UserName.MaximumLength = (USHORT) (ulUserByteLen + sizeof(WCHAR));
   pKerbCertLogon->UserName.Buffer = (PWSTR) pbUserBuffer;
   pKerbCertLogon->Pin.Length = (USHORT) ulPinByteLen;
   pKerbCertLogon->Pin.MaximumLength = (USHORT) (ulPinByteLen + sizeof(WCHAR));
   pKerbCertLogon->Pin.Buffer = (PWSTR) pbPinBuffer;

   pKerbCertLogon->CspDataLength = ulCspDataLen;
   pKerbCertLogon->CspData = pbCspData;
   
   pKerbCspInfo = (KERB_SMARTCARD_CSP_INFO*) pbCspData;
   pKerbCspInfo->dwCspInfoLen = ulCspDataLen;
   pKerbCspInfo->MessageType = 1;
   pKerbCspInfo->KeySpec = AT_KEYEXCHANGE;
     
   pKerbCspInfo->nCardNameOffset = 0;
   pKerbCspInfo->nReaderNameOffset = pKerbCspInfo->nCardNameOffset + wcslen(szCardName) + 1;
   pKerbCspInfo->nContainerNameOffset = pKerbCspInfo->nReaderNameOffset + wcslen(szReaderName) + 1;
   pKerbCspInfo->nCSPNameOffset = pKerbCspInfo->nContainerNameOffset + wcslen(szContainerName) + 1;

   pbCspDataContent = pbCspData + sizeof(KERB_SMARTCARD_CSP_INFO) - sizeof(TCHAR);
   memcpy(pbCspDataContent + (pKerbCspInfo->nCardNameOffset * sizeof(WCHAR)), szCardName, wcslen(szCardName) * sizeof(WCHAR)); 
   memcpy(pbCspDataContent + (pKerbCspInfo->nReaderNameOffset * sizeof(WCHAR)), szReaderName, wcslen(szReaderName) * sizeof(WCHAR)); 
   memcpy(pbCspDataContent + (pKerbCspInfo->nContainerNameOffset * sizeof(WCHAR)), szContainerName, wcslen(szContainerName) * sizeof(WCHAR)); 
   memcpy(pbCspDataContent + (pKerbCspInfo->nCSPNameOffset * sizeof(WCHAR)), szCspName, wcslen(szCspName) * sizeof(WCHAR)); 
   
   *ppbAuthInfo = pbAuthInfo;
   *pulAuthInfoLen = ulAuthInfoLen;  
}

int _tmain(int argc, _TCHAR* argv[])
{
   NTSTATUS nStatus;
   CHAR szProcName[] = "LsaTestLogonProcess";
   CHAR szPackageName[] = "Kerberos";
   CHAR szOriginName[] = "LsaSmartCardLogonTest";
   LSA_STRING lsaProcName = { strlen(szProcName), strlen(szProcName) + 1, szProcName};
   LSA_STRING lsaPackageName = { strlen(szPackageName), strlen(szPackageName) + 1, szPackageName};
   LSA_STRING lsaOriginName = { strlen(szOriginName), strlen(szOriginName) + 1, szOriginName};
   HANDLE lsaHandle;
   ULONG ulAuthPackage;
   LPBYTE pbAuthInfo = NULL;
   ULONG  ulAuthInfoLen = 0;
   LSA_OPERATIONAL_MODE dummy;
   TOKEN_SOURCE tokenSource;
   LPVOID pProfileBuffer = NULL;
   ULONG ulProfileBufferLen = 0;
   LUID logonId;
   HANDLE hLogonToken;
   QUOTA_LIMITS quotas;
   NTSTATUS subStatus = STATUS_SUCCESS;

   if (!SetSeTcbPrivilege())
      return -1;

   memcpy(tokenSource.SourceName, "LsaTest", 8);
   AllocateLocallyUniqueId(&tokenSource.SourceIdentifier);

   nStatus = LsaRegisterLogonProcess(&lsaProcName,
               &lsaHandle,
               &dummy);
   if (nStatus == STATUS_SUCCESS)
   {
      nStatus = LsaLookupAuthenticationPackage(lsaHandle,
                  &lsaPackageName,
                  &ulAuthPackage);
      if (nStatus == STATUS_SUCCESS)
      {
         ConstructAuthInfo(&pbAuthInfo, &ulAuthInfoLen);

         nStatus = LsaLogonUser(lsaHandle,
                     &lsaOriginName,
                     Interactive,
                     ulAuthPackage,
                     pbAuthInfo,
                     ulAuthInfoLen,
                     NULL,
                     &tokenSource,
                     &pProfileBuffer,
                     &ulProfileBufferLen,
                     &logonId,
                     &hLogonToken,
                     &quotas,
                     &subStatus);
         if (nStatus == STATUS_SUCCESS)
         {
            if (pProfileBuffer)
               LsaFreeReturnBuffer(pProfileBuffer);

            _tprintf(_T("User logged on successfully!!\n"));
            CloseHandle(hLogonToken);
         }
         else
         {
            _tprintf(_T("LsaLogonUser failed with error 0x%.8X. SubStatus = 0x%.8X\n"), LsaNtStatusToWinError(nStatus), LsaNtStatusToWinError(subStatus));  
         }


         HeapFree(GetProcessHeap(), 0, pbAuthInfo);
      }
      else
      {
         _tprintf(_T("LsaLookupAuthenticationPackage failed with error 0x%.8X\n"), LsaNtStatusToWinError(nStatus));
      }

      LsaDeregisterLogonProcess(lsaHandle);
   }
   else
   {
      _tprintf(_T("LsaRegisterLogonProcess failed with error 0x%.8X\n"), LsaNtStatusToWinError(nStatus));
   }
			
	return 0;
}
