Figure 1

Figure 1 SSPI Buffer Types

  #define SECBUFFER_EMPTY              	0
#define SECBUFFER_DATA               	1
#define SECBUFFER_TOKEN              	2
#define SECBUFFER_PKG_PARAMS         	3
#define SECBUFFER_MISSING            	4
#define SECBUFFER_EXTRA              	5
#define SECBUFFER_STREAM_TRAILER     	6
#define SECBUFFER_STREAM_HEADER      	7
#define SECBUFFER_NEGOTIATION_INFO   	8
#define SECBUFFER_PADDING            	9
#define SECBUFFER_STREAM             	10
#define SECBUFFER_MECHLIST           	11
#define SECBUFFER_MECHLIST_SIGNATURE 	12
#define SECBUFFER_READONLY           	0x80000000

Figure 2 Encrypting GSSAPI Messages

Buffer
Size
SECBUFFER_TOKEN
sizes.cbSecurityTrailer
SECBUFFER_DATA
Size of plaintext
SECBUFFER_PADDING
sizes.cbBlockSize

Figure 3 Decrypting GSSAPI Messages

Buffer
Description
SECBUFFER_STREAM
Encrypted data goes here
SECBUFFER_DATA
Decrypted data will go here

Figure 4 Raw SSPI Encryption and Decryption Tokens

Encryption Buffer
Size
SECBUFFER_DATA
Size of plaintext
SECBUFFER_TOKEN
sizes.cbSecurityTrailer
Decryption Buffer
Size
SECBUFFER_DATA
Size of ciphertext (same as plaintext)
SECBUFFER_TOKEN
Size of resulting security trailer

Figure 5 Encrypting and Decrypting using SSPI

  ///////////////////////////////////////////////////////////
// excerpt from MODEL.H (SSPI workbench)

// encrypting data
bool Model::encryptMessage(const void* pvMessage,
                           DWORD cbMessage,
                           DWORD nSeqNo,
                           void** ppvSealedMessage,
                           DWORD* pcbSealedMessage) {
    _ASSERT(msAuthnComplete == m_state);

    // see how much extra space the SSP needs
    SecPkgContext_Sizes sizes;
    SECURITY_STATUS err = 
        m_pSSPI->QueryContextAttributes(&m_hctx,
                                        SECPKG_ATTR_SIZES,
                                        &sizes);
    if (err) _err(L"QueryContextAttributes", false, err);

    // allocate a buffer and copy plaintext into it
    *ppvSealedMessage = malloc(cbMessage +
                               sizes.cbSecurityTrailer);
    CopyMemory(*ppvSealedMessage, pvMessage, cbMessage);

    // describe our buffer for SSPI
    SecBuffer rgsb[] = {
        {cbMessage, SECBUFFER_DATA, *ppvSealedMessage},
        {sizes.cbSecurityTrailer, SECBUFFER_TOKEN,
            static_cast<BYTE*>(*ppvSealedMessage) +
            cbMessage},
    };
    SecBufferDesc sbd = {SECBUFFER_VERSION,
                         sizeof rgsb / sizeof *rgsb, rgsb};
    // encrypt in place
    err = m_pSSPI->EncryptMessage(&m_hctx, 0, &sbd, nSeqNo);
    bool bOk = true;
    if (err) {
        _err(L"EncryptMessage", false, err);
        bOk = false;
        free(*ppvSealedMessage);
        *ppvSealedMessage = 0;
    }
    *pcbSealedMessage = cbMessage + rgsb[1].cbBuffer;
    return bOk;
}

// decrypting data
bool Model::decryptMessage(const void* pvSealedMessage,
                           DWORD cbSealedMessage,
                           DWORD cbMessage,
                           DWORD nSeqNo,
                           void** ppvMessage) {
    _ASSERT(msAuthnComplete == m_state);

    // allocate a buffer and copy ciphertext into it
    *ppvMessage = malloc(cbSealedMessage);
    CopyMemory(*ppvMessage,
               pvSealedMessage,
               cbSealedMessage);
    const DWORD cbTrailer = cbSealedMessage - cbMessage;

    // describe our buffer to SSPI
    SecBuffer rgsb[] = {
        {cbMessage, SECBUFFER_DATA, *ppvMessage},
        {cbTrailer, SECBUFFER_TOKEN,
         static_cast<BYTE*>(*ppvMessage) + cbMessage},
    };
    SecBufferDesc sbd = {SECBUFFER_VERSION,
                         sizeof rgsb / sizeof *rgsb,
                         rgsb};
    // decrypt in place
    SECURITY_STATUS err =
       m_pSSPI->DecryptMessage(&m_hctx, &sbd, nSeqNo, 0);
    if (err) {
        _err(L"DecryptMessage", false, err);
        free(*ppvMessage);
        *ppvMessage = 0;
    }
    return 0 == err;
}

Figure 6 Creating and Verifying Signatures using SSPI

  ///////////////////////////////////////////////////////////
// excerpt from MODEL.H (SSPI workbench)

///////////////////////////////////////////////////////////
// creating a signature
bool Model::signMessage(const void* pvMessage,
                        DWORD cbMessage,
                        DWORD nSeqNo,
                        void** ppvSignedMessage,
                        DWORD* pcbSignedMessage) {
    _ASSERT(msAuthnComplete == m_state);

    // see how big the signature will be for this SSP
    SecPkgContext_Sizes sizes;
    SECURITY_STATUS err =
        m_pSSPI->QueryContextAttributes(&m_hctx,
                                        SECPKG_ATTR_SIZES,
                                        &sizes);
    if (err)
        _err(L"QueryContextAttributes", false, err);

    // allocate a buffer and copy in the raw message
    *ppvSignedMessage = malloc(cbMessage +
                               sizes.cbMaxSignature);
    CopyMemory(*ppvSignedMessage, pvMessage, cbMessage);

    // describe our buffer for SSPI
    SecBuffer rgsb[] = {
        {cbMessage, SECBUFFER_DATA,  *ppvSignedMessage},
        {sizes.cbMaxSignature, SECBUFFER_TOKEN,
         static_cast<BYTE*>(*ppvSignedMessage) +
         cbMessage},
    };
    SecBufferDesc sbd = {SECBUFFER_VERSION,
                         sizeof rgsb / sizeof *rgsb,
                         rgsb};
    // make the signature
    err = m_pSSPI->MakeSignature(&m_hctx, 0, &sbd, nSeqNo);
    bool bOk = true;
    if (err) {
        _err(L"MakeSignature", false, err);
        bOk = false;
        free(*ppvSignedMessage);
        *ppvSignedMessage = 0;
    }
    *pcbSignedMessage = cbMessage + rgsb[1].cbBuffer;
    return bOk;
}

///////////////////////////////////////////////////////////
// verifying a signature
bool Model::verifySignature(const void* pvSignedMessage,
                            DWORD cbSignedMessage,
                            DWORD cbMessage,
                            DWORD nSeqNo,
                            void** ppvMessage) {
    _ASSERT(msAuthnComplete == m_state);

    // allocate a buffer and copy in the signed message
    *ppvMessage = malloc(cbSignedMessage);
    CopyMemory(*ppvMessage,
               pvSignedMessage,
               cbSignedMessage);
    const DWORD cbSig = cbSignedMessage - cbMessage;

    // describe our buffer for SSPI
    SecBuffer rgsb[] = {
        {cbMessage, SECBUFFER_DATA, *ppvMessage},
        {cbSig, SECBUFFER_TOKEN,
         static_cast<BYTE*>(*ppvMessage) + cbMessage},
    };
    SecBufferDesc sbd = {SECBUFFER_VERSION,
                         sizeof rgsb / sizeof *rgsb,
                         rgsb};
    // verify the signature
    ULONG qop;
    SECURITY_STATUS err =
      m_pSSPI->VerifySignature(&m_hctx, &sbd, nSeqNo, &qop);
    if (err) {
        _err(L"VerifySignature", true, err);
        free(*ppvMessage);
        *ppvMessage = 0;
    }
    return 0 == err;
}

Figure 7 Validating Passwords using SSPI

  #define _WIN32_WINNT 0x500
#define UNICODE
#include <windows.h>
#include <stdio.h>
#define SECURITY_WIN32
#include <security.h>
#pragma comment(lib, "secur32.lib")

// brain-dead error routine that dumps the last error and exits
void _err(const wchar_t* pszFcn, DWORD nErr = GetLastError())
{
    wchar_t szErr[256];
    if (FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, 0, nErr, 0,
                      szErr, sizeof szErr / sizeof *szErr, 0))
         wprintf(L"%s failed: %s", pszFcn, szErr);
    else wprintf(L"%s failed: 0x%08X", nErr);
    exit(1);
}

// Upon success, returns a handle to a NETWORK logon session
// for the specified principal (it will *not* have network
// credentials). Call CloseHandle to dispose of it when
// you are finished.
HANDLE _logonUserWithSSPI(wchar_t* pszSSP,
                          DWORD grfDesiredAccessToToken,
                          wchar_t* pszAuthority,
                          wchar_t* pszPrincipal,
                          wchar_t* pszPassword) {

    // the following code loads the SSPI interface DLL
    // and initializes it, getting a table of function ptrs
    HINSTANCE hdll = LoadLibrary(L"security.dll");
    if (!hdll)
        _err(L"LoadLibrary");
    INIT_SECURITY_INTERFACE_W initSecurityInterface =
            (INIT_SECURITY_INTERFACE_W)GetProcAddress(hdll,
                SECURITY_ENTRYPOINT_ANSIW);
    if (!initSecurityInterface)
        _err(L"GetProcAddress");
    PSecurityFunctionTable pSSPI = initSecurityInterface();

    // here's where we specify the credentials to verify
    SEC_WINNT_AUTH_IDENTITY_EX authIdent = {
        SEC_WINNT_AUTH_IDENTITY_VERSION,
        sizeof authIdent,
        pszPrincipal, lstrlenW(pszPrincipal),
        pszAuthority, lstrlenW(pszAuthority),
        pszPassword,  lstrlenW(pszPassword),
        SEC_WINNT_AUTH_IDENTITY_UNICODE,
        0, 0
    };

    // get an SSPI handle for these credentials
    CredHandle hcredClient;
    TimeStamp expiryClient;
    SECURITY_STATUS err =
        pSSPI->AcquireCredentialsHandle(0, pszSSP, SECPKG_CRED_OUTBOUND,
                                        0, &authIdent, 0, 0,
                                        &hcredClient, &expiryClient);
    if (err)
        _err(L"AcquireCredentialsHandle for client", err);

    // use the caller's credentials for the server
    CredHandle hcredServer;
    TimeStamp expiryServer;
    err = pSSPI->AcquireCredentialsHandle(0, pszSSP, SECPKG_CRED_INBOUND,
                                          0, 0, 0, 0, &hcredServer,
                                          &expiryServer);
    if (err)
        _err(L"AcquireCredentialsHandle for server", err);

    CtxtHandle hctxClient;
    CtxtHandle hctxServer;

    // create two buffers:
    //    one for the client sending tokens to the server,
    //    one for the server sending tokens to the client
    // (buffer size chosen based on current Kerb SSP setting
    //  for cbMaxToken - you may need to adjust this)
    BYTE bufC2S[8000];
    BYTE bufS2C[8000];
    SecBuffer sbufC2S = { sizeof bufC2S, SECBUFFER_TOKEN, bufC2S };
    SecBuffer sbufS2C = { sizeof bufS2C, SECBUFFER_TOKEN, bufS2C };
    SecBufferDesc bdC2S = { SECBUFFER_VERSION, 1, &sbufC2S };
    SecBufferDesc bdS2C = { SECBUFFER_VERSION, 1, &sbufS2C };

    // don't really need any special context attributes
    DWORD grfRequiredCtxAttrsClient = ISC_REQ_CONNECTION;
    DWORD grfRequiredCtxAttrsServer = ISC_REQ_CONNECTION;

    // set up some aliases to make it obvious what's happening
    PCtxtHandle    pClientCtxHandleIn  = 0;
    PCtxtHandle    pClientCtxHandleOut = &hctxClient;
    PCtxtHandle    pServerCtxHandleIn  = 0;
    PCtxtHandle    pServerCtxHandleOut = &hctxServer;
    SecBufferDesc* pClientInput  = 0;
    SecBufferDesc* pClientOutput = &bdC2S;
    SecBufferDesc* pServerInput  = &bdC2S;
    SecBufferDesc* pServerOutput = &bdS2C;
    DWORD          grfCtxAttrsClient = 0;
    DWORD          grfCtxAttrsServer = 0;
    TimeStamp      expiryClientCtx;
    TimeStamp      expiryServerCtx;

    // since the caller is acting as the server, we need
    // a server principal name so that the client will
    // be able to get a Kerb ticket (if Kerb is used)
    wchar_t szSPN[256];
    ULONG cchSPN = sizeof szSPN / sizeof *szSPN;
    GetUserNameEx(NameSamCompatible, szSPN, &cchSPN);

    // perform the authentication handshake, playing the
    // role of both client *and* server.
    bool bClientContinue = true;
    bool bServerContinue = true;
    while (bClientContinue || bServerContinue) {
        if (bClientContinue) {
            sbufC2S.cbBuffer = sizeof bufC2S;
            err = pSSPI->InitializeSecurityContext(
                &hcredClient, pClientCtxHandleIn,
                szSPN,
                grfRequiredCtxAttrsClient,
                0, SECURITY_NATIVE_DREP,
                pClientInput, 0,
                pClientCtxHandleOut,
                pClientOutput,
                &grfCtxAttrsClient,
                &expiryClientCtx);
            switch (err) {
                case 0:
                    bClientContinue = false;
                    break;
                case SEC_I_CONTINUE_NEEDED:
                    pClientCtxHandleIn = pClientCtxHandleOut;
                    pClientInput       = pServerOutput;
                    break;
                default:
                    _err(L"InitializeSecurityContext", err);
            }
        }

        if (bServerContinue) {
            sbufS2C.cbBuffer = sizeof bufS2C;
            err = pSSPI->AcceptSecurityContext(
                &hcredServer, pServerCtxHandleIn,
                pServerInput,
                grfRequiredCtxAttrsServer,
                SECURITY_NATIVE_DREP,
                pServerCtxHandleOut,
                pServerOutput,
                &grfCtxAttrsServer,
                &expiryServerCtx);
            switch (err) {
                case 0:
                    bServerContinue = false;
                    break;
                case SEC_I_CONTINUE_NEEDED:
                    pServerCtxHandleIn = pServerCtxHandleOut;
                    break;
                default:
                    _err(L"AcceptSecurityContext", err);
            }
        }
    }
    // if everything has gone smoothly, we've now got a logon
    // session for the client - let's grab the token now
    pSSPI->ImpersonateSecurityContext(pServerCtxHandleOut);
    HANDLE htok;
    if (!OpenThreadToken(GetCurrentThread(),
                         grfDesiredAccessToToken,
                         TRUE, &htok))
        _err(L"OpenThreadToken");

    // clean up
    pSSPI->FreeCredentialsHandle(&hcredClient);
    pSSPI->FreeCredentialsHandle(&hcredServer);
    pSSPI->DeleteSecurityContext(pServerCtxHandleOut);
    pSSPI->DeleteSecurityContext(pClientCtxHandleOut);
    
    return htok;
}

// here's an example of usage
void main() {
    // use "NTLM" or "Kerberos"
    HANDLE htok = _logonUserWithSSPI(L"Kerberos",
                                     TOKEN_QUERY,
                                     L"bar.com",
                                     L"alice",
                                     L"alice");
    CloseHandle(htok);
}