/*
 * Miscellaneous secur32 tests
 *
 * Copyright 2005 Kai Blin
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library 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.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

#define SECURITY_WIN32

#include <stdio.h>
#include <stdarg.h>
#include <windows.h>
#include "wine/test.h"
#include <winbase.h>
#include <sspi.h>

#define BUFF_SIZE 2048
#define MAX_MESSAGE 12000

static HMODULE secdll;
static PSecurityFunctionTableA (SEC_ENTRY * pInitSecurityInterfaceA)(void);
static SECURITY_STATUS (SEC_ENTRY * pEnumerateSecurityPackagesA)(PULONG, PSecPkgInfoA*);
static SECURITY_STATUS (SEC_ENTRY * pFreeContextBuffer)(PVOID pv);
static SECURITY_STATUS (SEC_ENTRY * pQuerySecurityPackageInfoA)(SEC_CHAR*, PSecPkgInfoA*);
static SECURITY_STATUS (SEC_ENTRY * pAcquireCredentialsHandleA)(SEC_CHAR*, SEC_CHAR*,
                            ULONG, PLUID, PVOID, SEC_GET_KEY_FN, PVOID, PCredHandle, PTimeStamp);
static SECURITY_STATUS (SEC_ENTRY * pInitializeSecurityContextA)(PCredHandle, PCtxtHandle,
                            SEC_CHAR*, ULONG, ULONG, ULONG, PSecBufferDesc, ULONG, 
                            PCtxtHandle, PSecBufferDesc, PULONG, PTimeStamp);
static SECURITY_STATUS (SEC_ENTRY * pCompleteAuthToken)(PCtxtHandle, PSecBufferDesc);
static SECURITY_STATUS (SEC_ENTRY * pAcceptSecurityContext)(PCredHandle, PCtxtHandle,
                            PSecBufferDesc, ULONG, ULONG, PCtxtHandle, PSecBufferDesc,
                            PULONG, PTimeStamp);
static SECURITY_STATUS (SEC_ENTRY * pFreeCredentialsHandle)(PCredHandle);
static SECURITY_STATUS (SEC_ENTRY * pDeleteSecurityContext)(PCtxtHandle);

void InitFunctionPtrs(void)
{
    secdll = LoadLibraryA("secur32.dll");
    if(!secdll)
        secdll = LoadLibraryA("security.dll");
    if(secdll)
    {
        pInitSecurityInterfaceA = (PVOID)GetProcAddress(secdll, "InitSecurityInterfaceA");
        pEnumerateSecurityPackagesA = (PVOID)GetProcAddress(secdll, "EnumerateSecurityPackagesA");
        pFreeContextBuffer = (PVOID)GetProcAddress(secdll, "FreeContextBuffer");
        pQuerySecurityPackageInfoA = (PVOID)GetProcAddress(secdll, "QuerySecurityPackageInfoA");
        pAcquireCredentialsHandleA = (PVOID)GetProcAddress(secdll, "AcquireCredentialsHandleA");
        pInitializeSecurityContextA = (PVOID)GetProcAddress(secdll, "InitializeSecurityContextA");
        pCompleteAuthToken = (PVOID)GetProcAddress(secdll, "CompleteAuthToken");
        pAcceptSecurityContext = (PVOID)GetProcAddress(secdll, "AcceptSecurityContext");
        pFreeCredentialsHandle = (PVOID)GetProcAddress(secdll, "FreeCredentialsHandle");
        pDeleteSecurityContext = (PVOID)GetProcAddress(secdll, "DeleteSecurityContext");
    }
}

/*---------------------------------------------------------*/
/* General helper functions */

static const char* getSecError(SECURITY_STATUS status)
{
#define _SEC_ERR(x) case (x): return #x;
    switch(status)
    {
        _SEC_ERR(SEC_E_OK);
        _SEC_ERR(SEC_E_INSUFFICIENT_MEMORY);
        _SEC_ERR(SEC_E_INVALID_HANDLE);
        _SEC_ERR(SEC_E_UNSUPPORTED_FUNCTION);
        _SEC_ERR(SEC_E_TARGET_UNKNOWN);
        _SEC_ERR(SEC_E_INTERNAL_ERROR);
        _SEC_ERR(SEC_E_SECPKG_NOT_FOUND);
        _SEC_ERR(SEC_E_NOT_OWNER);
        _SEC_ERR(SEC_E_CANNOT_INSTALL);
        _SEC_ERR(SEC_E_INVALID_TOKEN);
        _SEC_ERR(SEC_E_CANNOT_PACK);
        _SEC_ERR(SEC_E_QOP_NOT_SUPPORTED);
        _SEC_ERR(SEC_E_NO_IMPERSONATION);
        _SEC_ERR(SEC_I_CONTINUE_NEEDED);
        _SEC_ERR(SEC_E_BUFFER_TOO_SMALL);
        _SEC_ERR(SEC_E_ILLEGAL_MESSAGE);
        _SEC_ERR(SEC_E_LOGON_DENIED);
        _SEC_ERR(SEC_E_NO_CREDENTIALS);
        default:
            trace("Error = %ld\n", status);
            return "Unknown error";
    }
#undef _SEC_ERR
}

/*---------------------------------------------------------*/
/* Helper for testQuerySecurityPagageInfo */

static SECURITY_STATUS setupPackageA(SEC_CHAR *p_package_name, 
        PSecPkgInfo *p_pkg_info)
{
    SECURITY_STATUS ret = SEC_E_SECPKG_NOT_FOUND;
    
    ret = pQuerySecurityPackageInfoA( p_package_name, p_pkg_info);
    return ret;
}

/*---------------------------------------------------------*/
/* Helper for testAuth* */

SECURITY_STATUS setupClient(PCredHandle cred, const char *user, 
        const char *pass, const char *domain, char *provider)
{
    SECURITY_STATUS ret;
    PSEC_WINNT_AUTH_IDENTITY identity = NULL;
    TimeStamp ttl;

    trace("Running setupClient\n");
    
    if(user != NULL)
    {
        identity = HeapAlloc(GetProcessHeap(), 0, 
                sizeof(SEC_WINNT_AUTH_IDENTITY_A));

        memset( identity, 0, sizeof(identity));
        identity->Domain = HeapAlloc(GetProcessHeap(), 0, 
            lstrlenA( domain ? domain :"") + 1);
        lstrcpyA((char*)identity->Domain, domain ? domain :"");
        identity->DomainLength = domain ? lstrlenA(domain): 0;
        identity->User = HeapAlloc(GetProcessHeap(), 0, 
            lstrlenA( user ? user :"") + 1);
        lstrcpyA((char*)identity->User, user ? user :"");
        identity->UserLength = user ? lstrlenA(user) : 0;
        identity->Password = HeapAlloc(GetProcessHeap(), 0, 
            lstrlenA( pass ? pass :"") + 1);
        lstrcpyA((char*)identity->Password, pass ? pass : "");
        identity->PasswordLength = pass ? lstrlenA(pass): 0;
        identity->Flags = SEC_WINNT_AUTH_IDENTITY_ANSI;
    }

    if((ret = pAcquireCredentialsHandleA(NULL, provider, SECPKG_CRED_OUTBOUND,
            NULL, identity, NULL, NULL, cred, &ttl)) != SEC_E_OK)
    {
        trace("AcquireCredentialsHandle() returned %s\n", getSecError(ret));
    }
    
    ok(ret == SEC_E_OK, "AcquireCredentialsHande() returned %s\n", 
            getSecError(ret));

    if( identity != NULL)
    {
        if(identity->Domain != 0)
            HeapFree(GetProcessHeap(), 0, identity->Domain);
        if(identity->User != 0)
            HeapFree(GetProcessHeap(), 0, identity->User);
        if(identity->Password != 0)
            HeapFree(GetProcessHeap(), 0, identity->Password);
        HeapFree(GetProcessHeap(), 0, identity);
    }       

    return ret;
}

/**********************************************************************/

SECURITY_STATUS setupServer(PCredHandle cred, char *provider)
{
    SECURITY_STATUS ret;
    TimeStamp ttl;

    trace("Running setupServer\n");
    
    if((ret = pAcquireCredentialsHandleA(NULL, provider, SECPKG_CRED_INBOUND, 
                    NULL, NULL, NULL, NULL, cred, &ttl)) != SEC_E_OK)
    {
        trace("AcquireCredentialsHandle() returned %s\n", getSecError(ret));
    }

    ok(ret == SEC_E_OK, "AcquireCredentialsHande() returned %s\n",
            getSecError(ret));

    return ret;
}
 
/**********************************************************************/

SECURITY_STATUS setupBuffers(PSecBufferDesc *new_in_buf, 
        PSecBufferDesc *new_out_buf)
{
    int size = MAX_MESSAGE;
    PBYTE buffer = NULL;
    PSecBufferDesc in_buf = HeapAlloc(GetProcessHeap(), 0, 
            sizeof(SecBufferDesc));
    PSecBufferDesc out_buf = HeapAlloc(GetProcessHeap(), 0,
            sizeof(PSecBufferDesc));

    if(in_buf != NULL)
    {
        PSecBuffer sec_buffer = HeapAlloc(GetProcessHeap(), 0,
                sizeof(SecBuffer));
        if(sec_buffer == NULL){
            trace("in_buf: sec_buffer == NULL");
            return SEC_E_INSUFFICIENT_MEMORY;
        }
        
        buffer = HeapAlloc(GetProcessHeap(), 0, size);

        if(buffer == NULL){
            trace("in_buf: buffer == NULL");
            return SEC_E_INSUFFICIENT_MEMORY;
        }

        in_buf->ulVersion = SECBUFFER_VERSION;
        in_buf->cBuffers = 1;
        in_buf->pBuffers = sec_buffer;

        sec_buffer->cbBuffer = size;
        sec_buffer->BufferType = SECBUFFER_TOKEN;
        sec_buffer->pvBuffer = buffer;
        *new_in_buf = in_buf;
    }
    else
    {
        trace("HeapAlloc in_buf returned NULL\n");
        return SEC_E_INSUFFICIENT_MEMORY;
    }
    
    if(out_buf != NULL)
    {
        PSecBuffer sec_buffer = HeapAlloc(GetProcessHeap(), 0,
                sizeof(SecBuffer));
        PBYTE buffer = NULL;
        
        if(sec_buffer == NULL){
            trace("out_buf: sec_buffer == NULL");
            return SEC_E_INSUFFICIENT_MEMORY;
        }

        buffer = HeapAlloc(GetProcessHeap(), 0, size);

        if(buffer == NULL){
            trace("out_buf: buffer == NULL");
            return SEC_E_INSUFFICIENT_MEMORY;
        }

        out_buf->ulVersion = SECBUFFER_VERSION;
        out_buf->cBuffers = 1;
        out_buf->pBuffers = sec_buffer;

        sec_buffer->cbBuffer = 0;
        sec_buffer->BufferType = SECBUFFER_TOKEN;
        sec_buffer->pvBuffer = buffer;
        *new_out_buf = out_buf;
    }
    else
    {
        trace("HeapAlloc out_buf returned NULL\n");
        return SEC_E_INSUFFICIENT_MEMORY;
    }

    return SEC_E_OK;
}

/**********************************************************************/

void cleanupBuffers(PSecBufferDesc in_buf, PSecBufferDesc out_buf)
{
    int i;

    if(in_buf != NULL)
    {
        for(i = 0; i < in_buf->cBuffers; ++i)
        {
            HeapFree(GetProcessHeap(), 0, in_buf->pBuffers[i].pvBuffer);
        }
        HeapFree(GetProcessHeap(), 0, in_buf->pBuffers);
        HeapFree(GetProcessHeap(), 0, in_buf);
    }
    
    if(out_buf != NULL)
    {
        for(i = 0; i < out_buf->cBuffers; ++i)
        {
            HeapFree(GetProcessHeap(), 0, out_buf->pBuffers[i].pvBuffer);
        }
        HeapFree(GetProcessHeap(), 0, out_buf->pBuffers);
        HeapFree(GetProcessHeap(), 0, out_buf);
    }
}

/**********************************************************************/

SECURITY_STATUS runClient(PCredHandle cred, PCtxtHandle ctxt, 
        PSecBufferDesc in_buf, PSecBufferDesc out_buf, BOOL first)
{
    SECURITY_STATUS ret;
    ULONG req_attr = ISC_REQ_CONNECTION;
    ULONG ctxt_attr;
    TimeStamp ttl;

    trace("Running the client the %s time.\n", first?"first":"second");

    ret = pInitializeSecurityContextA(cred, first?NULL:ctxt, NULL, req_attr, 
            0, SECURITY_NATIVE_DREP, first?NULL:in_buf, 0, ctxt, out_buf,
            &ctxt_attr, &ttl);
    
    if(ret == SEC_I_COMPLETE_AND_CONTINUE || ret == SEC_I_COMPLETE_NEEDED)
    {
        pCompleteAuthToken(ctxt, out_buf);
        if(ret == SEC_I_COMPLETE_AND_CONTINUE)
            ret = SEC_I_CONTINUE_NEEDED;
        else if(ret == SEC_I_COMPLETE_NEEDED)
            ret = SEC_E_OK;
    }       
    
    return ret;
}

/**********************************************************************/

SECURITY_STATUS runServer(PCredHandle cred, PCtxtHandle ctxt,
        PSecBufferDesc in_buf, PSecBufferDesc out_buf, BOOL first)
{
    SECURITY_STATUS ret;
    ULONG ctxt_attr;
    TimeStamp ttl;

    trace("Running the server the %s time\n", first?"first":"second");

    ret = pAcceptSecurityContext(cred, first?NULL:ctxt, in_buf, 0,
            SECURITY_NATIVE_DREP, ctxt, out_buf, &ctxt_attr, &ttl);

    if(ret == SEC_I_COMPLETE_AND_CONTINUE || ret == SEC_I_COMPLETE_NEEDED)
    {
        pCompleteAuthToken(ctxt, out_buf);
        if(ret == SEC_I_COMPLETE_AND_CONTINUE)
            ret = SEC_I_CONTINUE_NEEDED;
        else if(ret == SEC_I_COMPLETE_NEEDED)
            ret = SEC_E_OK;
    }
    

    return ret;
}

/**********************************************************************/

void communicate(PSecBufferDesc in_buf, PSecBufferDesc out_buf)
{
    if(in_buf != NULL && out_buf != NULL)
    {
        trace("Running communicate.\n");
        if((in_buf->cBuffers >= 1) && (out_buf->cBuffers >= 1))
        {
            if((in_buf->pBuffers[0].pvBuffer != NULL) && 
                    (out_buf->pBuffers[0].pvBuffer != NULL))
            {
                memset(out_buf->pBuffers[0].pvBuffer, 0, MAX_MESSAGE);
                
                memcpy(out_buf->pBuffers[0].pvBuffer, 
                        in_buf->pBuffers[0].pvBuffer,
                        in_buf->pBuffers[0].cbBuffer);
                
                out_buf->pBuffers[0].cbBuffer = in_buf->pBuffers[0].cbBuffer;
                
                memset(in_buf->pBuffers[0].pvBuffer, 0, MAX_MESSAGE);
            }
        }
    }
}
   
/**********************************************************************/

/*--------------------------------------------------------- */
/* The test functions */

static void testInitSecurityInterface(void)
{
    PSecurityFunctionTable sec_fun_table = NULL;

    sec_fun_table = pInitSecurityInterfaceA();
    ok(sec_fun_table != NULL, "InitSecurityInterface() returned NULL.\n");

}

static void testEnumerateSecurityPackages(void)
{

    SECURITY_STATUS sec_status;
    ULONG           num_packages, i;
    PSecPkgInfo     pkg_info = NULL;

    trace("Running testEnumerateSecurityPackages\n");
    
    sec_status = pEnumerateSecurityPackagesA(&num_packages, &pkg_info);

    ok(sec_status == SEC_E_OK, 
            "EnumerateSecurityPackages() should return %ld, not %08lx\n",
            (LONG)SEC_E_OK, (LONG)sec_status);

    ok(num_packages > 0, "Number of sec packages should be > 0 ,but is %ld\n",
            num_packages);

    ok(pkg_info != NULL, 
            "pkg_info should not be NULL after EnumerateSecurityPackages\n");
    
    trace("Number of packages: %ld\n", num_packages);
    for(i = 0; i < num_packages; ++i){
        trace("%ld: Package \"%s\"\n", i, pkg_info[i].Name);
        trace("Supported flags:\n");
        if(pkg_info[i].fCapabilities & SECPKG_FLAG_INTEGRITY)
            trace("\tSECPKG_FLAG_INTEGRITY\n");
        if(pkg_info[i].fCapabilities & SECPKG_FLAG_PRIVACY)
            trace("\tSECPKG_FLAG_PRIVACY\n");
        if(pkg_info[i].fCapabilities & SECPKG_FLAG_TOKEN_ONLY)
            trace("\tSECPKG_FLAG_TOKEN_ONLY\n");
        if(pkg_info[i].fCapabilities & SECPKG_FLAG_DATAGRAM)
            trace("\tSECPKG_FLAG_DATAGRAM\n");
        if(pkg_info[i].fCapabilities & SECPKG_FLAG_CONNECTION)
            trace("\tSECPKG_FLAG_CONNECTION\n");
        if(pkg_info[i].fCapabilities & SECPKG_FLAG_MULTI_REQUIRED)
            trace("\tSECPKG_FLAG_MULTI_REQUIRED\n");
        if(pkg_info[i].fCapabilities & SECPKG_FLAG_CLIENT_ONLY)
            trace("\tSECPKG_FLAG_CLIENT_ONLY\n");
        if(pkg_info[i].fCapabilities & SECPKG_FLAG_EXTENDED_ERROR)
            trace("\tSECPKG_FLAG_EXTENDED_ERROR\n");
        if(pkg_info[i].fCapabilities & SECPKG_FLAG_IMPERSONATION)
            trace("\tSECPKG_FLAG_IMPERSONATION\n");
        if(pkg_info[i].fCapabilities & SECPKG_FLAG_ACCEPT_WIN32_NAME)
            trace("\tSECPKG_FLAG_ACCEPT_WIN32_NAME\n");
        if(pkg_info[i].fCapabilities & SECPKG_FLAG_STREAM)
            trace("\tSECPKG_FLAG_STREAM\n");
        if(pkg_info[i].fCapabilities & SECPKG_FLAG_READONLY_WITH_CHECKSUM)
            trace("\tSECPKG_FLAG_READONLY_WITH_CHECKSUM\n");
        trace("Comment: %s\n", pkg_info[i].Comment);
        trace("\n");
    }

    pFreeContextBuffer(pkg_info);
}


static void testQuerySecurityPackageInfo(void)
{
    SECURITY_STATUS     sec_status;
    SEC_CHAR            sec_pkg_name[256];
    PSecPkgInfo         pkg_info = NULL;
    ULONG               max_token = 0;
    USHORT              version = 0;
    
    trace("Running testQuerySecurityPackageInfo\n");
    
    /* Test with an existing package. Test should pass */
    
    lstrcpy(sec_pkg_name, "NTLM");
    
    sec_status = setupPackageA(sec_pkg_name, &pkg_info);

    ok((sec_status == SEC_E_OK) || (sec_status == SEC_E_SECPKG_NOT_FOUND), 
       "Return value of QuerySecurityPackageInfo() shouldn't be %s\n",
       getSecError(sec_status) );
        
    if(pkg_info != NULL){
        max_token = pkg_info->cbMaxToken;
        version   = pkg_info->wVersion;
        ok(version == 1, "wVersion always should be 1, but is %d\n", version);
        todo_wine{
            ok(max_token == 12000, "cbMaxToken for NTLM is %ld, not 12000.\n",
                max_token);
        }
    }
    
    
    sec_status = pFreeContextBuffer(&pkg_info);
    
    ok( sec_status == SEC_E_OK,
        "Return value of FreeContextBuffer() shouldn't be %s\n",
        getSecError(sec_status) );

    /* Test with a nonexistent package, test should fail */
   
    lstrcpy(sec_pkg_name, "Winetest");

    sec_status = pQuerySecurityPackageInfoA( sec_pkg_name, &pkg_info);

    ok( sec_status != SEC_E_OK,
        "Return value of QuerySecurityPackageInfo() should not be %s for a nonexistent package\n", getSecError(SEC_E_OK));

    sec_status = pFreeContextBuffer(&pkg_info);
    
    ok( sec_status == SEC_E_OK,
        "Return value of FreeContextBuffer() shouldn't be %s\n",
        getSecError(sec_status) );


}

void testAuth(const char* sec_pkg, const char* domain)
{
    SECURITY_STATUS     sec_status;
    PSecPkgInfo         pkg_info = NULL;
    CredHandle          server_cred;
    CredHandle          client_cred;
    CtxtHandle          server_ctxt;
    CtxtHandle          client_ctxt;
    SEC_CHAR            sec_pkg_name[256];

    PSecBufferDesc client_in = NULL, client_out = NULL;
    PSecBufferDesc server_in = NULL, server_out = NULL;

    BOOL continue_client = FALSE, continue_server = FALSE;

    lstrcpy(sec_pkg_name, sec_pkg);
    if(setupPackageA(sec_pkg_name, &pkg_info) == SEC_E_OK)
    {
        pFreeContextBuffer(&pkg_info);
        sec_status = setupClient(&client_cred, "testuser", "testpass", domain,
                sec_pkg_name);

        if(sec_status != SEC_E_OK)
        {
            trace("Error: Setting up the client returned %s, exiting test!\n",
                    getSecError(sec_status));
            pFreeCredentialsHandle(&client_cred);
            return;
        }

        sec_status = setupServer(&server_cred, sec_pkg_name);

        if(sec_status != SEC_E_OK)
        {
            trace("Error: Setting up the server returned %s, exiting test!\n",
                    getSecError(sec_status));
            pFreeCredentialsHandle(&server_cred);
            pFreeCredentialsHandle(&client_cred);
            return;
        }

        setupBuffers(&client_in, &client_out);
        setupBuffers(&server_in, &server_out);
        
        sec_status = runClient(&client_cred, &client_ctxt, client_in, client_out, 
                TRUE);

        ok(sec_status == SEC_E_OK || sec_status == SEC_I_CONTINUE_NEEDED,
                "Running the client returned %s, more tests will fail from now.\n",
                getSecError(sec_status));
        
        if(sec_status == SEC_I_CONTINUE_NEEDED)
            continue_client = TRUE;

        communicate(client_out, server_in);
        
        sec_status = runServer(&server_cred, &server_ctxt, server_in, server_out, 
                TRUE);

        ok(sec_status == SEC_E_OK || sec_status == SEC_I_CONTINUE_NEEDED,
                "Running the server returned %s, more tests will fail from now.\n",
                getSecError(sec_status));
        
        if(sec_status == SEC_I_CONTINUE_NEEDED)
        {
            continue_server = TRUE;
            communicate(server_out, client_in);
        }
        
        if(continue_client)
        {
            sec_status = runClient(&client_cred, &client_ctxt, client_in, client_out,
                FALSE);

            ok(sec_status == SEC_E_OK,
                "Running the client returned %s, more tests will fail from now.\n",
                getSecError(sec_status));
            
            communicate(client_out, server_in);
        }

        if(continue_server)
        {
            sec_status = runServer(&server_cred, &server_ctxt, server_in, server_out,
                FALSE);
            ok(sec_status == SEC_E_OK || SEC_E_LOGON_DENIED,
                    "Running the server returned %s.\n", getSecError(sec_status));
        }

        cleanupBuffers(client_in, client_out);
        cleanupBuffers(server_in, server_out);
        
        sec_status = pDeleteSecurityContext(&server_ctxt);
        ok(sec_status == SEC_E_OK, "DeleteSecurityContext(server) returned %s\n",
                getSecError(sec_status));

        sec_status = pDeleteSecurityContext(&client_ctxt);
        ok(sec_status == SEC_E_OK, "DeleteSecurityContext(client) returned %s\n",
                getSecError(sec_status));
           
        sec_status = pFreeCredentialsHandle(&server_cred);
        ok(sec_status == SEC_E_OK, "FreeCredentialsHandle(server) returned %s\n",
                getSecError(sec_status));
        
        sec_status = pFreeCredentialsHandle(&client_cred);
        ok(sec_status == SEC_E_OK, "FreeCredentialsHandle(client) returned %s\n",
                getSecError(sec_status));
    }
    else
    {
        trace("Package not installed, skipping test.\n");
    }
    pFreeContextBuffer(&pkg_info);
}


START_TEST(main)
{
    InitFunctionPtrs();
    if(pInitSecurityInterfaceA)
        testInitSecurityInterface();
    if(pFreeContextBuffer)
    {
        if(pEnumerateSecurityPackagesA)
            testEnumerateSecurityPackages();
        if(pQuerySecurityPackageInfoA)
        {
            testQuerySecurityPackageInfo();
            if(pInitSecurityInterfaceA)
                testAuth("NTLM", "WORKGROUP");
        }
    }
    if(secdll)
        FreeLibrary(secdll);
}
