Windows Vista Authentication Features and Changes for Developers

This paper describes the improvements to Windows Vista and Windows Server 2008 authentication features that are relevant to security developers. The authentication features included with Windows Vista and Windows Server 2008 extend a strong set of platform–based authentication features to provide better security, manageability, and user experience.

Modular Credential Providers

In earlier releases of the Microsoft Windows® operating system, the customization of interactive user logon was done by creating custom Graphical Identification and Authentication modules (GINAs). However, GINAs were responsible for more than simply gathering authentication information and rendering the UI to collect it. Therefore, custom GINAs were complex to create and usually required Microsoft Product Support Services (PSS) support for successful implementation. Often, using a custom GINA resulted in unintended side effects, such as preventing fast user switching (FUS) and smart card logon.

In Windows Vista and Windows Server 2008, GINAs are replaced with a new modular Credential Provider model that is easier to program to. Documentation and code samples about how to implement Credential Providers can be downloaded from the Windows Vista Credential Provider Samples page on the Microsoft Download Center.

Developer Scenarios

Developer scenarios for Credential Providers include the following:

·        Customization of the logon experience, including manipulation of the images, customizing how credential information is collected from users, and implementing custom mechanisms for validating the credential information.

·        Implementing logon authentication using non-password–based mechanisms, including biometric and smart card–based logon.

·        Extending an existing credential provider to collect a few additional bits of information from users.

Requirements

Credential Providers require Windows Vista and Windows Server 2008.

Sample Provider

Sample implementations of Credential Providers can be downloaded from the Credential Provider Samples page on the Microsoft Download Center.

 

Credential Security Service Provider (CredSSP)

The Credential Security Service Provider (CredSSP) is a new security service provider that is available through the Security Support Provider Interface (SSPI) in Windows. CredSSP is a method to securely delegate user credentials (by using the client-side SSP) to a target server (by using the server-side SSP). In the context of CredSSP, delegation is passing users' passwords between the client computer and the server.

CredSSP was designed to comply with specific Terminal Server requirements to provide single sign on (SSO) functionality, and is available to any internal or third-party application under SSPI. SSO is the ability for users to authenticate once and gain access to multiple resources on interconnected systems. In Terminal Services, this functionality allows the client to delegate the credentials to the server to perform logon operations on behalf of the client across multiple systems.

CredSSP is similar to the Kerberos authentication protocol in that it can delegate credentials from the client to the server, but it does so using a completely different mechanism and with different usability and security characteristics. CredSSP uses client policies to determine whether the credentials should be delegated or not. These policies are configurable via Group Policy on a service-by-service basis. By default, the only policies configured on the client computer are for Terminal Services targets. Delegation of credentials for all other services is disabled. Depending on policy configuration, delegation of credentials can be configured to perform the following actions:

·        Prompt users before delegating the current logon (default) credentials

·        Delegate users' default credentials without prompting

·        Delegate users' saved (CredMan) credentials

To enable delegation, a system administrator must configure polices via Group Policy. See the Windows Vista Authentication Features document at https://go.microsoft.com/fwlink/?LinkId=111705 for more information about how to configure CredSSP delegation policy.

To provide the required level of security, policy can be configured to not delegate client credentials until the server identity is securely authenticated. In Terminal Services, this capability enables the Terminal Services client to prompt for client credentials and pass them to CredSSP to determine whether the credentials should be delegated. CredSSP also enables the Terminal Server to authenticate the user before spawning a resource-consuming desktop. In addition, CredSSP does not expose the credentials to the callers, which means that if the client application is using Credentials Management application programming interface (API), and the client computer is configured for secure desktop prompting, clear text credentials will not be available to the client application.

 

The following figure shows the message exchange sequence implemented by CredSSP.

Cc540483.image001(en-us,MSDN.10).gif

 

Note that CredSSP uses both TLS/SSL and SPNEGO when authenticating to a target service and can satisfy server authentication through either or both of the protocols. Client authentication is achieved by means of SPNEGO using NTLM or the Kerberos authentication protocol to authenticating users. On-the-wire traffic resembles TLS/SSL traffic as a result of SPNEGO being encapsulated within TLS/SSL.

Developer Scenarios

CredSSP is a method to securely pass user passwords to a server, and it should only be used in scenarios in which this functionality is expected and desired. Terminal Services, for example, depends on having a user password to perform a local logon, and therefore uses CredSSP. Most services do not require this functionality. For most delegation scenarios, the Kerberos authentication protocol is the ideal solution and should be used. The risk that any compromise of the target server causes there to be no distinction of identity between the target server and the client who delegated the credentials should be noted and accepted. The server is able to act as the client without any restrictions.

Although these factors should be noted, CredSSP should be considered when there is a need to delegate credentials from a client computer to a server and any of the following conditions are true:

·        The user should be able to influence whether the credential delegation occurs.

·        The user should be able to provide different credentials to the server than the ones with which they are currently logged on to the client computer.

·        The developer would like to leverage the secure desktop to gather the credentials.

Requirements

CredSSP is only available on Windows Vista and Windows Server 2008.

Sample Code

The following sample code and makefile demonstrates the correct usage of the SSP APIs when using the CredSSP package.

 

CredSSP.cpp

Copy and paste the following code into a file named CredSSP.cpp.

 

Note   Some lines in the following code cannot be displayed in the allotted space and therefore display on more than one line. The second lines of such code are indented to the 3-1/2" margin on the left side of the page; they must actually be entered as single lines with no line breaks.

 

/*++

 

© 2007 Microsoft Corporation. All rights reserved.

 

Module Name:

 

   CredSSP.cpp

 

Abstract:

 

A command line application that establishes an authenticated connection between a client computer and a server computer using the Credential Security Service Provider (CredSSP). For demonstration purposes, the following code performs both the client and server sides of the authentication exercise. The client and server components would typically be implemented in two different modules running on two different computers.

 

Revision History:

 

--*/

#define UNICODE

#define _UNICODE

#include <tchar.h>

#include <windows.h>

#include <stdio.h>

#include <stdlib.h>

#define SECURITY_WIN32

#include "sspi.h"

#include "credssp.h"

#include "schannel.h"

#include "wincrypt.h"

 

#define CLIENT_SESSION 1

#define SERVER_SESSION 2

 

BOOL DoAuthentication (void);

BOOL InitPackage (TCHAR *lpPackageName);

 

static TCHAR g_lpPackageName[1024] = TEXT("credssp");

static BOOL  g_fUseConfidentiality = FALSE;

static BOOL  g_fUseIntegrity = FALSE;

static PBYTE g_pMessage = NULL;

static TCHAR g_szTarget[1024] = TEXT("");

static TCHAR g_szDomain[1024] = TEXT("");

static TCHAR g_szPassword[1024] = TEXT("");

static TCHAR g_szUserName[1024] = TEXT("");

#define SEC_SUCCESS(Status) ((Status) >= 0)

 

void Usage(void)

{

 

   printf ("usage: credssp [ <options> ]\n");

   printf ("\n");

   printf ("    -h              show usage\n");

   printf ("    -s<Security Support Provider>     name of security package (default = CredSSP).\n");

   printf ("    -t<targetname>  SPN or security context of server account (req. for serverauth).\n");

   printf ("    -u<username>    username\n");

   printf ("    -d<domain>      domain\n");

   printf ("    -p<password>    password\n");

   printf ("\n");

   printf ("  example: credssp -tadamcar -pcredssp \n");

 

}

 

void _tmain (int argc, TCHAR *argv[])

{

   TCHAR * pMessage = NULL;

   DWORD cbMessage = 0;

 

   int    i;

   int    iOption;

   TCHAR   *pszOption = NULL;

 

   printf("\nUse \"%S /h\" to see the options for this sample application\n\n", argv[0]);

 

   for(i = 1; i < argc; i++)

   {

      if(argv[i][0] == '/') argv[i][0] = '-';

 

      if(argv[i][0] != '-')

      {

         printf("**** Invalid argument \"%S\"\n", argv[i]);

         Usage();

         return;

      }

      iOption = argv[i][1];

      pszOption = &argv[i][2];

 

      switch(iOption)

      {

      case 'h':

         Usage();

         return;

 

      case 's':

         lstrcpy(g_lpPackageName, pszOption);

         break;

 

         case 't':

         lstrcpy(g_szTarget, pszOption);

         break;

 

         case 'u':

         lstrcpy(g_szUserName, pszOption);

         break;

 

         case 'd':

         lstrcpy(g_szDomain, pszOption);

         break;

 

         case 'p':

         lstrcpy(g_szPassword, pszOption);

         break;

 

      default:

         printf("**** Invalid option \"%s\"\n", argv[i]);

         Usage();

         return;

 

      }

 

   }

 

   if (!InitPackage (g_lpPackageName))

      goto endsession;

 

   // Make an authenticated connection

   DoAuthentication();

 

endsession:

 

   printf("\n");

 

}

 

BOOL ClientCred (CredHandle * phClientCreds)

/*++

 

 Routine Description:

 

    Creates and returns the client-side credential handle by

    InvokingAcquireCredentialHandle.

 

 Return Value:

 

    Returns TRUE if successful, FALSE if not.

 

--*/

{

   PCREDSSP_CRED           pCred = NULL;

   PSCHANNEL_CRED          pSchannelCred = NULL;

   PSEC_WINNT_AUTH_IDENTITY_W pSpnegoCred = NULL;

   TimeStamp         Lifetime;

   SECURITY_STATUS   ss;

 

   pCred = (PCREDSSP_CRED) LocalAlloc(LMEM_FIXED|LMEM_ZEROINIT, sizeof(CREDSSP_CRED));

   if ( NULL == pCred )

   {

         fprintf(stderr, "Failed pCred alloc\n");

         goto CleanUp;

   }

 

   // Build SPNEGO cred structure

   pSpnegoCred = (PSEC_WINNT_AUTH_IDENTITY_W)

LocalAlloc(LMEM_FIXED|LMEM_ZEROINIT, sizeof(SEC_WINNT_AUTH_IDENTITY_W));

 

   if ( NULL == pSpnegoCred )

   {

          fprintf(stderr, "Failed pSpnegoCred alloc\n");

          goto CleanUp;

   }

 

   if(lstrlen(g_szUserName))

   {

         pSpnegoCred->Domain = (unsigned short *) g_szDomain;

         pSpnegoCred->DomainLength = (unsigned long)wcslen(g_szDomain);

         pSpnegoCred->Password = (unsigned short *) g_szPassword;

         pSpnegoCred->PasswordLength = (unsigned long)wcslen(g_szPassword);

         pSpnegoCred->User = (unsigned short *) g_szUserName;

         pSpnegoCred->UserLength = (unsigned long)wcslen(g_szUserName);

         pSpnegoCred->Flags = SEC_WINNT_AUTH_IDENTITY_UNICODE;

   }

   else

   {

         printf("Using default credentials of user\n");

         pSpnegoCred->Domain = (unsigned short *) NULL;

         pSpnegoCred->DomainLength = (unsigned long) 0;

         pSpnegoCred->Password = (unsigned short *) NULL;

         pSpnegoCred->PasswordLength = (unsigned long) 0;

         pSpnegoCred->User = (unsigned short *) NULL;

         pSpnegoCred->UserLength = (unsigned long) 0;

         pSpnegoCred->Flags = SEC_WINNT_AUTH_IDENTITY_UNICODE;

   }

 

   // Build Schannel cred structure

   pSchannelCred = (PSCHANNEL_CRED) LocalAlloc(LMEM_FIXED|LMEM_ZEROINIT, sizeof(SCHANNEL_CRED));

   if ( NULL == pSchannelCred )

   {

         fprintf (stderr, "Failed pSchannelCred alloc");

         goto CleanUp;

   }

   pSchannelCred->dwVersion = SCHANNEL_CRED_VERSION;

   pSchannelCred->cCreds     = 0;

   pSchannelCred->paCred     = NULL;

 

   pCred->pSpnegoCred = pSpnegoCred;

   pCred->pSchannelCred = pSchannelCred;

 

   printf ("Acquiring client credentials\n");

 

   // Build Client Credential

   ss = AcquireCredentialsHandle (

                     NULL, // principal

                     g_lpPackageName,

                     SECPKG_CRED_OUTBOUND,

                     NULL, // LOGON id

                     (PVOID)pCred, // auth data

                     NULL, // get key fn

                     NULL, // get key arg

                     phClientCreds,

                     &Lifetime

                     );

  

   if (!SEC_SUCCESS (ss))

   {

         fprintf (stderr, "AcquireCreds failed: 0x%08x\n", ss);

   }

 

CleanUp:

 

   if (pCred)

   {

         if (NULL != pCred->pSchannelCred)

         {

                LocalFree( pCred->pSchannelCred );

         }

 

         if ( NULL != pCred->pSpnegoCred )

         {

                LocalFree ( pCred->pSpnegoCred );

         }   

         LocalFree(pCred);

   }

 

   if (SEC_SUCCESS (ss))

         return (true);

   else

         return (false);

}

 

BOOL AddServerCertInfo(IN OUT PSCHANNEL_CRED pSchannelCred)

/*++

 

 Routine Description:

 

Helper function that finds a valid certificate used for the server end of the TLS connection.

 

 Return Value:

 

    Returns TRUE if successful, FALSE if not.

 

--*/

{

    BOOL fRet = FALSE;

 

    LPWSTR pwszSubjectName = NULL;

    LPWSTR pwszMachineName  = NULL;

    DWORD cchMachineName  = 0;

 

    HCERTSTORE  hCertStore = NULL;

    PCCERT_CONTEXT*  ppCertContext   = NULL; // server cert array

    BOOL fCloseStore = FALSE;

 

    if (!pSchannelCred)

    {

        goto CleanUp;

    }

 

   

    if (!pwszSubjectName)

    {

           

        if( !GetComputerNameExW( ComputerNameNetBIOS,

                                NULL,

                                &cchMachineName) )

        {

            if( ERROR_MORE_DATA != GetLastError() )

            {

                fprintf(stderr, "GetComputerNameEx failed:

0x%08x\n", GetLastError() );       

                goto CleanUp;

            }

        }

 

        pwszMachineName = (LPWSTR) LocalAlloc(LMEM_FIXED|LMEM_ZEROINIT, cchMachineName* sizeof(WCHAR) );

        if( !pwszMachineName )

        {

            fprintf(stderr, "Unable to allocate memory for machine name" );       

            goto CleanUp;

        }

 

        if( !GetComputerNameExW( ComputerNameNetBIOS,

                                pwszMachineName,

                                &cchMachineName) )

        {

             fprintf(stderr, "GetComputerNameEx failed: 0x%08x\n", GetLastError() );       

             goto CleanUp;

        }

 

        pwszSubjectName = pwszMachineName;

        printf("Certificate subject name = %S\n", pwszSubjectName);

    } 

   

  

    if( !hCertStore )

    {

        // Open LM:MY store

        hCertStore = CertOpenStore(

                           CERT_STORE_PROV_SYSTEM,

                           X509_ASN_ENCODING,

                           0,

                           CERT_SYSTEM_STORE_LOCAL_MACHINE,

                           TEXT("MY") );

           

        if(!hCertStore)

        {

            fprintf(stderr, "Unable to open cert store: 0x%08x\n", GetLastError());     

            goto CleanUp;

        }

 

        fCloseStore = TRUE;

    }

 

    ppCertContext = (PCCERT_CONTEXT *) LocalAlloc(LMEM_FIXED|LMEM_ZEROINIT, sizeof(PCCERT_CONTEXT) * 1);

    if( !ppCertContext )

    {

           

                     fprintf(stderr, "Cert context alloc failed\n");

            goto CleanUp;

    }

   

    // Find server certificates using the server cert CN

    // Simply searching for a certificate that contains

    // the supplied name somewhere in the subject name.

    ppCertContext[0] = CertFindCertificateInStore(

                               hCertStore,

                               X509_ASN_ENCODING,

                               0,

                               CERT_FIND_SUBJECT_STR_A,

                               pwszSubjectName,

                               ppCertContext[0]);

   

    if(!ppCertContext[0])

    {

        fprintf(stderr, "A valid certificate was not found in the store\n");     

        goto CleanUp;

    }

 

    pSchannelCred->cCreds = 1;

    pSchannelCred->paCred =  ppCertContext;

    pSchannelCred->dwCredFormat = 0;

   

    fRet = TRUE;

 

CleanUp:

  

    if(fCloseStore)

    {

        CertCloseStore(hCertStore, 0);

    }   

 

    if (pwszMachineName)

    {

        LocalFree(pwszMachineName);

        pwszSubjectName = NULL;

    }

   

    return fRet;

}

 

 

BOOL ServerCred (CredHandle * phServerCreds)

/*++

 

 Routine Description:

 

Creates and returns the server-side credential handle by invoking

AcquireCredentialHandle.

 

 Return Value:

 

    Returns TRUE if successful, FALSE if not.

 

--*/

{

   PCREDSSP_CRED           pCred = NULL;

   PSCHANNEL_CRED          pSchannelCred = NULL;

   PSEC_WINNT_AUTH_IDENTITY_W pSpnegoCred = NULL;

   TimeStamp         Lifetime;

   SECURITY_STATUS   ss;

 

   printf ("Acquiring server credentials\n");

 

   pCred = (PCREDSSP_CRED) LocalAlloc(LMEM_FIXED|LMEM_ZEROINIT,
                                       sizeof(CREDSSP_CRED));

   if ( NULL == pCred )

   {

         fprintf(stderr, "Failed pCred alloc\n");

         goto CleanUp;

   }

 

   // Build Schannel cred structure

   pSchannelCred = (PSCHANNEL_CRED) LocalAlloc(LMEM_FIXED|LMEM_ZEROINIT, sizeof(SCHANNEL_CRED));

   if ( NULL == pSchannelCred )

   {

         fprintf (stderr, "Failed pSchannelCred alloc");

         goto CleanUp;

   }

   pSchannelCred->dwVersion = SCHANNEL_CRED_VERSION;

 

   AddServerCertInfo(pSchannelCred);

 

   pCred->pSpnegoCred = pSpnegoCred;

   pCred->pSchannelCred = pSchannelCred;

 

   // Build Server Credential

   ss = AcquireCredentialsHandle (

                     NULL, // principal

                     g_lpPackageName,

                     SECPKG_CRED_INBOUND,

                     NULL, // LOGON id

                     (PVOID)pCred, // auth data

                     NULL, // get key fn

                     NULL, // get key arg

                     phServerCreds,

                     &Lifetime

                     );

  

   if (!SEC_SUCCESS (ss))

   {

         fprintf (stderr, "AcquireCreds failed: 0x%08x\n", ss);

   }

 

CleanUp:

 

   if (pSchannelCred &&  pSchannelCred->paCred)

   {

         PCCERT_CONTEXT* ppCertContext = (PCCERT_CONTEXT*) pSchannelCred->paCred;

         CertFreeCertificateContext(ppCertContext[0]);

         LocalFree(ppCertContext);

   }

 

   if (pCred)

   {

         if (NULL != pCred->pSchannelCred)

         {

                LocalFree( pCred->pSchannelCred );

         }

 

         LocalFree(pCred);

   }

 

   if (SEC_SUCCESS (ss))

         return (true);

   else

         return (false);

 

 

}

 

BOOL DoAuthentication (void)

/*++

 

 Routine Description:

 

Manages the authentication conversation between the client and server.

 

 Return Value:

 

    Returns TRUE if successful, FALSE if not.

 

--*/

{

   ULONG attrib = 0;

 

   BOOL fFirstISC = TRUE;

   BOOL fFirstASC = TRUE;

 

   SECURITY_STATUS   dwISCStatus = SEC_E_OK;

   SECURITY_STATUS   dwASCStatus = SEC_E_OK;

   TimeStamp         Lifetime;  

 

   // Credential Handles

   CredHandle              hClientCreds = {0};

   CredHandle              hServerCreds = {0};

 

   // Generic Context Vars

   ULONG             ContextAttributes;

  

   // Client Context (ISC) Vars

   CtxtHandle              hClientContext = {0};

   SecBufferDesc     CliOutBuffDesc = {0};

   SecBufferDesc           CliInBuffDesc = {0};

   SecBuffer         CliInSecBuff[2];

   SecBuffer         CliOutSecBuff = {0};

 

   // Server Context (ASC) Vars

   CtxtHandle              hServerContext = {0};

   SecBufferDesc           SrvInBuffDesc = {0};

   SecBufferDesc           SrvOutBuffDesc = {0};

   SecBuffer               SrvInSecBuff[2];

   SecBuffer               SrvOutSecBuff = {0};

 

   TCHAR szUserName[1024] = TEXT("");

   DWORD cbUserName = 1024;

 

   // CredSSP Requires these flags

   attrib = ASC_REQ_DELEGATE | ASC_REQ_CONNECTION | ASC_REQ_ALLOCATE_MEMORY;

 

   if (g_fUseConfidentiality)

      attrib |= ASC_REQ_CONFIDENTIALITY;

 

   if (g_fUseIntegrity)

      attrib |= ASC_REQ_INTEGRITY;

 

   if(!ClientCred(&hClientCreds))

         return (false);

 

   if(!ServerCred(&hServerCreds))

         return (false);

  

   do {

 

         // prepare output buffer

         CliOutBuffDesc.ulVersion = 0;

         CliOutBuffDesc.cBuffers = 1;

         CliOutBuffDesc.pBuffers = &CliOutSecBuff;

 

         CliOutSecBuff.BufferType = SECBUFFER_TOKEN;

         CliOutSecBuff.cbBuffer = 0;

         CliOutSecBuff.pvBuffer = NULL;

 

         // prepare input buffer

         if (!fFirstISC)  {

               CliInBuffDesc.ulVersion = SECBUFFER_VERSION;

               CliInBuffDesc.cBuffers = 2;

               CliInBuffDesc.pBuffers = CliInSecBuff;

 

               // Input of ISC is the output of ASC (after

// first response)

               CliInSecBuff[0].BufferType = SECBUFFER_TOKEN;

               CliInSecBuff[0].cbBuffer = SrvOutSecBuff.cbBuffer;

               CliInSecBuff[0].pvBuffer = SrvOutSecBuff.pvBuffer;

 

               CliInSecBuff[1].BufferType = SECBUFFER_EMPTY;

               CliInSecBuff[1].cbBuffer = 0;

               CliInSecBuff[1].pvBuffer = NULL;

         }

 

         dwISCStatus = InitializeSecurityContext (

                           &hClientCreds,

                           fFirstISC ? NULL : &hClientContext,

                           g_szTarget,

                           attrib,

                           0, // reserved1

                           SECURITY_NATIVE_DREP,

                           fFirstISC ? NULL : &CliInBuffDesc,

                           0, // reserved2

                           &hClientContext,

                           &CliOutBuffDesc,

                           &ContextAttributes,

                           &Lifetime

                           );

 

         if (!SEC_SUCCESS (dwISCStatus))  {

               fprintf (stderr, "InitializeSecurityContext failed: 0x%08x\n", dwISCStatus);

               return FALSE;

         }

         else

               printf("ISC Success\n");

 

         fFirstISC = FALSE;

     

      // Prepare server (ACH) output buffer

         SrvOutBuffDesc.ulVersion = 0;

         SrvOutBuffDesc.cBuffers = 1;

         SrvOutBuffDesc.pBuffers = &SrvOutSecBuff;

 

         SrvOutSecBuff.BufferType = SECBUFFER_TOKEN;

         SrvOutSecBuff.cbBuffer = 0;

         SrvOutSecBuff.pvBuffer = NULL;

 

         // Prepare server (ACH) input buffer

         SrvInBuffDesc.ulVersion = SECBUFFER_VERSION;

         SrvInBuffDesc.cBuffers = 2;

         SrvInBuffDesc.pBuffers = SrvInSecBuff;

 

         // Input of ASC is the output of ISC

         SrvInSecBuff[0].BufferType = SECBUFFER_TOKEN;

         SrvInSecBuff[0].cbBuffer = CliOutSecBuff.cbBuffer;

         SrvInSecBuff[0].pvBuffer = CliOutSecBuff.pvBuffer;

 

         SrvInSecBuff[1].BufferType = SECBUFFER_EMPTY;

         SrvInSecBuff[1].cbBuffer = 0;

         SrvInSecBuff[1].pvBuffer = NULL;

 

         dwASCStatus = AcceptSecurityContext (

                           &hServerCreds,

                           fFirstASC ? NULL : &hServerContext,

                           &SrvInBuffDesc,

                           attrib, // context requirements

                           SECURITY_NATIVE_DREP,

                           &hServerContext,

                           &SrvOutBuffDesc,

                           &ContextAttributes,

                           &Lifetime

                           );

 

         fFirstASC = FALSE;

 

         if (!SEC_SUCCESS (dwASCStatus)) 

         {

               fprintf (stderr, "AcceptSecurityContext failed: 0x%08x\n", dwASCStatus);

               return FALSE;

         }

         else

         {

               // Display success

               printf("ASC Success\n");

         }

 

         if ((dwISCStatus == SEC_E_OK) && (dwASCStatus == SEC_E_OK))

         {

               // All done - exit the negotiation loop

               printf("Successfully negotiated security context!\n");

               break;

         }

    

   }

   while(true);

 

   // Impersonate the client

   if (!ImpersonateSecurityContext (&hServerContext))

   {

 

         if (!GetUserName (szUserName, &cbUserName))

               goto cleanup;

 

         // Display the user name

         printf ("Client connected as user %S\n", szUserName);

 

         // Revert to self

         RevertSecurityContext (&hServerContext);

   }

 

cleanup:

 

   if (!fFirstISC)

   {

         DeleteSecurityContext(&hClientContext);

   }

 

   if (!fFirstASC)

   {

         DeleteSecurityContext(&hServerContext);

   }

   return(TRUE);

 

BOOL InitPackage (TCHAR *lpPackageName)

/*++

 

 Routine Description:

 

    Finds, loads and initializes the security package

 

 Return Value:

 

    Returns TRUE is successful; otherwise FALSE is returned.

 

--*/

{

   SECURITY_STATUS ss;

   PSecPkgInfo pkgInfo;

 

   // Query for the package we're interested in

   ss = QuerySecurityPackageInfo (lpPackageName, &pkgInfo);

 

   if (!SEC_SUCCESS(ss)) {

      fprintf (stderr, "Couldn't query package info for %S, error 0x%08x\n", lpPackageName, ss);

      return(FALSE);

   }

 

   lstrcpy (g_lpPackageName, lpPackageName);

 

   FreeContextBuffer (pkgInfo);

 

   printf ("Using package: %S\n", g_lpPackageName);

 

   return TRUE;

}

 

Makefile

Copy and paste the following text into a file named makefile.

 

!include <win32.mak>

 

CPP          = $(cc)

CPPFLAGS     = $(cflags) $(cvars) $(cdebug)

LINKFLAGS    = $(linkdebug) $(conflags) /subsystem:console

          

all: credssp.exe

                    

# ------------------------------------------------------------------

#                      L I N K / R E S   C O M M A N D S

# ------------------------------------------------------------------

 

credssp.exe: credssp.obj

    @echo Linking ...

     $(link) $(LINKFLAGS) $** $(conlibs) secur32.lib crypt32.lib -out:$@

 

 

# ------------------------------------------------------------------

#                           B U I L D   R U L E S

# ------------------------------------------------------------------

 

.c.obj:

    @echo Compiling $<...

    $(CPP) $(CPPFLAGS) /c -I..\include $<

 

 

# ------------------------------------------------------------------

#                       D E P E N D E N C Y   R U L E S

# ------------------------------------------------------------------

 

credssp.obj : credssp.cpp

 

Building the Sample Code

Building the sample application requires Visual Studio 2005 and the Microsoft Windows Software Development Kit Update for Windows Vista to be installed. On the development computer, open the SDK environment command window, navigate to the directory where you created the CredSSP.cpp and makefile files, type nmake and press ENTER.

Configuring the Computer to Run the Sample Code

Before you run the sample code, you need to configure the Credentials Delegation Group Policy and the SSL certificate. These tasks are described in the following paragraphs.

Configure Credentials Delegation Group Policy

For CredSSP to delegate credentials, the CredSSP policies must be properly configured using Group Policy. Use the Local Group Policy Editor on the Windows Vista or Windows Server 2008 computer and navigate to Local Computer Policy\Computer Configuration\Administrative Templates\System\Credentials Delegation. The sample shows how to use both fresh credentials and default credentials so both of those policy settings should be enabled.

For each policy setting to be enabled, In the Add servers to the list dialog box, add the Service Prinicipal Name or username that will be used to run the sample application. The following screenshot shows a properly configured policy that will allow CredSSP to delegate the default credentials of a logged on user to a process running as the user Adam Carter (adamcar).

 

Configure SSL Certificate

The sample code demonstrates usage of the CredSSP with an SSL encrypted channel to protect the delegated credentials. This approach is recommended to provide the greatest degree of security. To use SSL, the “server” side of the negotiation must be able to find and use an appropriate server certificate. If your test environment does not include a Certificate Authority (CA), then the SelfSSL utility from the IIS 6.0 Resource Kit Tools can be used to generate a self-signed SSL certificate and install it in the correct certificate store. The default parameters are sufficient to allow the CredSSP sample to work correctly.

Note   The SelfSSL utility must be run from an Administrator command prompt on Windows Vista and Windows Server 2008 to function correctly.

Running the Sample Application

After the configuration steps are completed, it is time to try the sample application. Open a command window and navigate to the directory where you built the sample application. The appropriate command line syntax to run the sample application with fresh credentials is shown in the following example, which depicts a test environment with a user named adamcar::

Z:\Auth_CredSSP>credssp -tadamcar -uadamcar -dextranet -pasdf~1234 –scredssp

This command should be entered on a single line at the command prompt. When run, it produces the output shown in the following screenshot:

To run the sample application with default credentials, run the following command:

Z:\Auth_CredSSP>credssp -tadamcar  –scredssp

 

New Stored User Names and Passwords (CredMan) APIs

Significant changes to the Credential Management (CredMan) APIs were made in Windows Vista and Windows Server 2008, including a set of APIs that were specifically introduced to provide support for modular credential providers. Links to additional information about these APIs are provided in the following subsections.

New General-purpose CredMan APIs

CredFindBestCredential

New CredMan APIs that Support Modular Credential Providers 

CredUIPromptForWindowsCredentials

CredUnPackAuthenticationBuffer

CredPackAuthenticationBuffer

CredProtect

CredIsProtected

CredUnprotect

Deprecated APIs

The CredRename API was removed in Windows Vista and Windows Server 2008 because of potential security issues.

Developer Scenarios

As indicated earlier in this paper, most of the new CredMan APIs target developers who write Credential Providers.

Requirements

Windows Vista and Windows Server 2008 are required to use the new CredMan APIs.

TLS/SSL Cryptographic Enhancements

Microsoft has improved the security and performance of the Transport Layer Security (TLS) and Secure Sockets Layer (SSL) message protection mechanisms in Windows Vista and Windows Server 2008 with the following features:

·        Support for ECC cipher suites.

·        Advanced Encryption Standard (AES) cipher suites for use with TLS and SSL.

·        A pluggable cryptographic mechanism for Schannel called crypto-agility that supports the use of custom cipher suites, reimplementation of existing suites, or the offloading of cryptographic computations to hardware.

ECC Cipher Suites for TLS

Elliptic curve cryptography (ECC) is a key generation technique that is based on elliptic curve theory, and is used to create more efficient and smaller cryptographic keys. ECC key generation differs from the traditional method that uses the product of very large prime numbers to create keys. Instead, ECC uses an elliptic curve equation to create keys. ECC keys are approximately six times smaller than the equivalent strength traditional keys, which significantly reduces the computations that are needed during the TLS handshake to establish a secure connection.

In Windows Vista and Windows Server 2008, the Schannel SSP includes new cipher suites that support ECC cryptography. ECC cipher suites can now be negotiated as part of the standard TLS handshake. The three curves supported by Windows Vista and Windows Server 2008 are secpr256, secpr384, and secpr521.

Developer Scenarios

Code that properly sets up an SSL/TLS handshake will automatically use ECC if both the client computer and the server have the ability to use ECC keys and if the server is configured to prioritize ECC cipher suites over those suites that specify RSA as the key derivation/exchange algorithm.

Requirements

For SSL or TLS to use AES, both the client and the server must support the AES cipher suites. Therefore, Windows Vista and Windows Server 2008 are required.

AES Cipher Suites

The support for AES symmetric ciphers, which is not available in Microsoft Windows 2000 or Windows Server 2003, is important because AES has become a National Institute of Standards and Technology (NIST) standard. AES replaces RC4 as the default cipher suite in Windows Vista and Windows Server 2008. The Microsoft implementation of AES provides support for both 128-bit and 256-bit keys.

Developer Scenarios

Code that properly sets up a TLS/SSL handshake will automatically use AES if both the client and the server support AES.

Requirements

For TLS or SSL to use AES, both the client and the server must support the AES cipher suites. Therefore, Windows Vista and Windows Server 2008 are required.

Schannel Crypto-agility

Windows Vista and Windows Server 2008 introduced an entirely new implementation of the cryptographic libraries, referred to as Cryptography API: Next Generation, or CNG. CNG provides an extensible provider model for cryptographic algorithms. Schannel builds on this new, more robust infrastructure to provide crypto-agile capabilities for Schannel. Crypto-agile Schannel is agnostic to the underlying cryptographic mechanisms. By providing crypto-agnostic capabilities, Microsoft enables organizations to implement increased functionality, including advanced combinations of cipher suites. Organizations can now create new cipher suites and then plug them into Schannel.

Developer Scenarios

If a product or organization requires a certain level of security, or simply has requirements to use a specific bulk encryption cipher, the Schannel crypto-agility feature allows developers to implement the desired bulk encryption cipher and then configure Schannel to use that mechanism. In addition, the priority of cipher suites that will be used by Schannel can be modified using the CNG interfaces.

Requirements

Because both the client and the server must be able to negotiate the same TLS/SSL cipher, Schannel crypto-agility requires Windows Vista and Windows Server 2008 with the same custom cipher configured for use on both the client and the server. In addition, the custom cipher must be prioritized over other ciphers that could be negotiated.

Sample

Developers should consult the forthcoming Crypto Next Generation (CNG) Software Development Kit (SDK) for a sample implementation of a custom cipher suite and provider that can be used with Schannel.

The following code sample shows how to modify the priority of a cryptographic algorithm to affect the cipher suites that are offered and negotiated during the SSL/TLS session negotiation.

 

#define MYTEST_CIPHERSUITE L“TLS_RSA_WITH_NEW_CIPHER_CBC_SHA”

Status = BcryptAddContextFunctionProvider (

   CRYPT_LOCAL,

   L”SSL”, //default

   NCRYPT_SCHANNEL_INTERFACE,

   MYTEST_CIPHERSUITE,

   L"MyTest Provider"

   CRYPT PRIORITY TOP);

 

Note   The MyTest Provider must be registered before the new cipher suite can be prioritized using the above code fragment.