Added a secur32.dll that loads other SSP DLLs and forwards calls to
them.

diff --git a/dlls/Makefile.in b/dlls/Makefile.in
index 9a44f97..0e4375c 100644
--- a/dlls/Makefile.in
+++ b/dlls/Makefile.in
@@ -97,6 +97,7 @@
 	richedit \
 	rpcrt4 \
 	rsabase \
+	secur32 \
 	serialui \
 	setupapi \
 	shdocvw \
@@ -313,6 +314,7 @@
 	riched32.dll$(DLLEXT) \
 	rpcrt4.dll$(DLLEXT) \
 	rsabase.dll$(DLLEXT) \
+	secur32.dll$(DLLEXT) \
 	serialui.dll$(DLLEXT) \
 	setupapi.dll$(DLLEXT) \
 	shdocvw.dll$(DLLEXT) \
@@ -694,6 +696,9 @@
 rsabase.dll$(DLLEXT): rsabase/rsabase.dll$(DLLEXT)
 	$(RM) $@ && $(LN_S) rsabase/rsabase.dll$(DLLEXT) $@
 
+secur32.dll$(DLLEXT): secur32/secur32.dll$(DLLEXT)
+	$(RM) $@ && $(LN_S) secur32/secur32.dll$(DLLEXT) $@
+
 serialui.dll$(DLLEXT): serialui/serialui.dll$(DLLEXT)
 	$(RM) $@ && $(LN_S) serialui/serialui.dll$(DLLEXT) $@
 
@@ -956,6 +961,7 @@
 	libriched32 \
 	librpcrt4 \
 	librsabase \
+	libsecur32 \
 	libserialui \
 	libsetupapi \
 	libshdocvw \
@@ -1394,6 +1400,11 @@
 librsabase.a: rsabase/rsabase.spec.def
 	$(DLLTOOL) -k -l $@ -d rsabase/rsabase.spec.def
 
+libsecur32.def: secur32/secur32.spec.def
+	$(RM) $@ && $(LN_S) secur32/secur32.spec.def $@
+libsecur32.a: secur32/secur32.spec.def
+	$(DLLTOOL) -k -l $@ -d secur32/secur32.spec.def
+
 libserialui.def: serialui/serialui.spec.def
 	$(RM) $@ && $(LN_S) serialui/serialui.spec.def $@
 libserialui.a: serialui/serialui.spec.def
@@ -1634,6 +1645,7 @@
 richedit/riched32.spec.def: $(WINEBUILD)
 rpcrt4/rpcrt4.spec.def: $(WINEBUILD)
 rsabase/rsabase.spec.def: $(WINEBUILD)
+secur32/secur32.spec.def: $(WINEBUILD)
 serialui/serialui.spec.def: $(WINEBUILD)
 setupapi/setupapi.spec.def: $(WINEBUILD)
 shdocvw/shdocvw.spec.def: $(WINEBUILD)
@@ -1767,6 +1779,7 @@
 richedit/riched32.dll$(DLLEXT): richedit
 rpcrt4/rpcrt4.dll$(DLLEXT): rpcrt4
 rsabase/rsabase.dll$(DLLEXT): rsabase
+secur32/secur32.dll$(DLLEXT): secur32
 serialui/serialui.dll$(DLLEXT): serialui
 setupapi/setupapi.dll$(DLLEXT): setupapi
 shdocvw/shdocvw.dll$(DLLEXT): shdocvw
diff --git a/dlls/secur32/.cvsignore b/dlls/secur32/.cvsignore
new file mode 100644
index 0000000..1eb0b25
--- /dev/null
+++ b/dlls/secur32/.cvsignore
@@ -0,0 +1,4 @@
+Makefile
+secur32.dll.dbg.c
+secur32.spec.c
+secur32.spec.def
diff --git a/dlls/secur32/Makefile.in b/dlls/secur32/Makefile.in
new file mode 100644
index 0000000..8f46aa3
--- /dev/null
+++ b/dlls/secur32/Makefile.in
@@ -0,0 +1,15 @@
+TOPSRCDIR = @top_srcdir@
+TOPOBJDIR = ../..
+SRCDIR    = @srcdir@
+VPATH     = @srcdir@
+MODULE    = secur32.dll
+IMPORTS   = user32 advapi32 kernel32 ntdll
+
+C_SRCS = \
+	secur32.c \
+	thunks.c \
+	wrapper.c
+
+@MAKE_DLL_RULES@
+
+### Dependencies:
diff --git a/dlls/secur32/secur32.c b/dlls/secur32/secur32.c
new file mode 100644
index 0000000..2b51458
--- /dev/null
+++ b/dlls/secur32/secur32.c
@@ -0,0 +1,881 @@
+/* Copyright (C) 2004 Juan Lang
+ *
+ * This file implements loading of SSP DLLs.
+ *
+ * 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
+ */
+#include <stdarg.h>
+#include "windef.h"
+#include "winbase.h"
+#include "winnls.h"
+#include "winreg.h"
+#include "winternl.h"
+#include "shlwapi.h"
+#include "sspi.h"
+#include "secur32_priv.h"
+#include "thunks.h"
+
+#include "wine/debug.h"
+
+WINE_DEFAULT_DEBUG_CHANNEL(secur32);
+
+/**
+ *  Type definitions
+ */
+
+typedef struct _SecurePackageTable
+{
+    DWORD numPackages;
+    DWORD numAllocated;
+    SecurePackage table[1];
+} SecurePackageTable;
+
+typedef struct _SecureProviderTable
+{
+    DWORD numProviders;
+    DWORD numAllocated;
+    SecureProvider table[1];
+} SecureProviderTable;
+
+/**
+ *  Prototypes
+ */
+
+/* Makes sure table has space for at least howBig entries.  If table is NULL,
+ * returns a newly allocated table.  Otherwise returns the address of the
+ * modified table, which may not be the same was when called.
+ */
+static SecurePackageTable *_resizePackageTable(SecurePackageTable *table,
+ DWORD howBig);
+
+/* Makes sure table has space for at least howBig entries.  If table is NULL,
+ * returns a newly allocated table.  Otherwise returns the address of the
+ * modified table, which may not be the same was when called.
+ */
+static SecureProviderTable *_resizeProviderTable(SecureProviderTable *table,
+ DWORD howBig);
+
+/* Tries to load moduleName as a provider.  If successful, enumerates what
+ * packages it can and adds them to the package and provider tables.  Resizes
+ * tables as necessary.
+ */
+static void _tryLoadProvider(PWSTR moduleName);
+
+/* Initialization: read securityproviders value and attempt to open each dll
+ * there.  For each DLL, call _tryLoadProvider to see if it's really an SSP.
+ * Two undocumented functions, AddSecurityPackage(A/W) and
+ * DeleteSecurityPackage(A/W), seem suspiciously like they'd register or
+ * unregister a dll, but I'm not sure.
+ */
+static void SECUR32_initializeProviders(void);
+
+/* Frees all loaded packages and providers */
+static void SECUR32_freeProviders(void);
+
+/**
+ *  Globals
+ */
+
+static CRITICAL_SECTION cs;
+static SecurePackageTable *packageTable = NULL;
+static SecureProviderTable *providerTable = NULL;
+
+static SecurityFunctionTableA securityFunctionTableA = {
+    SECURITY_SUPPORT_PROVIDER_INTERFACE_VERSION_2,
+    EnumerateSecurityPackagesA,
+    QueryCredentialsAttributesA,
+    AcquireCredentialsHandleA,
+    FreeCredentialsHandle,
+    NULL, /* Reserved2 */
+    InitializeSecurityContextA,
+    AcceptSecurityContext,
+    CompleteAuthToken,
+    DeleteSecurityContext,
+    ApplyControlToken,
+    QueryContextAttributesA,
+    ImpersonateSecurityContext,
+    RevertSecurityContext,
+    MakeSignature,
+    VerifySignature,
+    FreeContextBuffer,
+    QuerySecurityPackageInfoA,
+    NULL, /* Reserved3 */
+    NULL, /* Reserved4 */
+    ExportSecurityContext,
+    ImportSecurityContextA,
+    AddCredentialsA,
+    NULL, /* Reserved8 */
+    QuerySecurityContextToken,
+    EncryptMessage,
+    DecryptMessage,
+    SetContextAttributesA
+};
+
+static SecurityFunctionTableW securityFunctionTableW = {
+    SECURITY_SUPPORT_PROVIDER_INTERFACE_VERSION_2,
+    EnumerateSecurityPackagesW,
+    QueryCredentialsAttributesW,
+    AcquireCredentialsHandleW,
+    FreeCredentialsHandle,
+    NULL, /* Reserved2 */
+    InitializeSecurityContextW,
+    AcceptSecurityContext,
+    CompleteAuthToken,
+    DeleteSecurityContext,
+    ApplyControlToken,
+    QueryContextAttributesW,
+    ImpersonateSecurityContext,
+    RevertSecurityContext,
+    MakeSignature,
+    VerifySignature,
+    FreeContextBuffer,
+    QuerySecurityPackageInfoW,
+    NULL, /* Reserved3 */
+    NULL, /* Reserved4 */
+    ExportSecurityContext,
+    ImportSecurityContextW,
+    AddCredentialsW,
+    NULL, /* Reserved8 */
+    QuerySecurityContextToken,
+    EncryptMessage,
+    DecryptMessage,
+    SetContextAttributesW
+};
+
+PSecurityFunctionTableA SEC_ENTRY InitSecurityInterfaceA(void)
+{
+    return &securityFunctionTableA;
+}
+
+PSecurityFunctionTableW SEC_ENTRY InitSecurityInterfaceW(void)
+{
+    return &securityFunctionTableW;
+}
+
+/* Allocates or resizes table to have space for at least howBig packages.
+ * Uses Heap functions, because needs to be able to reallocate.
+ */
+static SecurePackageTable *_resizePackageTable(SecurePackageTable *table,
+ DWORD howBig)
+{
+    SecurePackageTable *ret;
+
+    EnterCriticalSection(&cs);
+    if (table)
+    {
+        if (table->numAllocated < howBig)
+        {
+            ret = (SecurePackageTable *)HeapReAlloc(GetProcessHeap(), 0, table,
+             sizeof(SecurePackageTable) + (howBig - 1) * sizeof(SecurePackage));
+            if (ret)
+            {
+                ret->numAllocated = howBig;
+                table = ret;
+            }
+        }
+        else
+            ret = table;
+    }
+    else
+    {
+        DWORD numAllocated = (howBig > 1 ? howBig : 1);
+
+        ret = (SecurePackageTable *)HeapAlloc(GetProcessHeap(), 0,
+         sizeof(SecurePackageTable) +
+         (numAllocated - 1) * sizeof(SecurePackage));
+        if (ret)
+        {
+            ret->numAllocated = numAllocated;
+            ret->numPackages = 0;
+        }
+    }
+    LeaveCriticalSection(&cs);
+    return ret;
+}
+
+/* Allocates or resizes table to have space for at least howBig providers.
+ * Uses Heap functions, because needs to be able to reallocate.
+ */
+static SecureProviderTable *_resizeProviderTable(SecureProviderTable *table,
+ DWORD howBig)
+{
+    SecureProviderTable *ret;
+
+    EnterCriticalSection(&cs);
+    if (table)
+    {
+        if (table->numAllocated < howBig)
+        {
+            ret = (SecureProviderTable *)HeapReAlloc(GetProcessHeap(), 0, table,
+             sizeof(SecureProviderTable) +
+             (howBig - 1) * sizeof(SecureProvider));
+            if (ret)
+            {
+                ret->numAllocated = howBig;
+                table = ret;
+            }
+        }
+        else
+            ret = table;
+    }
+    else
+    {
+        DWORD numAllocated = (howBig > 1 ? howBig : 1);
+
+        ret = (SecureProviderTable *)HeapAlloc(GetProcessHeap(), 0,
+         sizeof(SecureProviderTable) +
+         (numAllocated - 1) * sizeof(SecureProvider));
+        if (ret)
+        {
+            ret->numAllocated = numAllocated;
+            ret->numProviders = 0;
+        }
+    }
+    LeaveCriticalSection(&cs);
+    return ret;
+}
+
+PWSTR SECUR32_strdupW(PCWSTR str)
+{
+    PWSTR ret;
+
+    if (str)
+    {
+        ret = (PWSTR)SECUR32_ALLOC((lstrlenW(str) + 1) * sizeof(WCHAR));
+        if (ret)
+            lstrcpyW(ret, str);
+    }
+    else
+        ret = NULL;
+    return ret;
+}
+
+PWSTR SECUR32_AllocWideFromMultiByte(PCSTR str)
+{
+    PWSTR ret;
+
+    if (str)
+    {
+        int charsNeeded = MultiByteToWideChar(CP_ACP, 0, str, -1, NULL, 0);
+
+        if (charsNeeded)
+        {
+            ret = (PWSTR)SECUR32_ALLOC(charsNeeded * sizeof(WCHAR));
+            if (ret)
+                MultiByteToWideChar(CP_ACP, 0, str, -1, ret, charsNeeded);
+        }
+        else
+            ret = NULL;
+    }
+    else
+        ret = NULL;
+    return ret;
+}
+
+PSTR SECUR32_AllocMultiByteFromWide(PCWSTR str)
+{
+    PSTR ret;
+
+    if (str)
+    {
+        int charsNeeded = WideCharToMultiByte(CP_ACP, 0, str, -1, NULL, 0,
+         NULL, NULL);
+
+        if (charsNeeded)
+        {
+            ret = (PSTR)SECUR32_ALLOC(charsNeeded);
+            if (ret)
+                WideCharToMultiByte(CP_ACP, 0, str, -1, ret, charsNeeded,
+                 NULL, NULL);
+        }
+        else
+            ret = NULL;
+    }
+    else
+        ret = NULL;
+    return ret;
+}
+
+static void _makeFnTableA(PSecurityFunctionTableA fnTableA,
+ const PSecurityFunctionTableA inFnTableA,
+ const PSecurityFunctionTableW inFnTableW)
+{
+    if (fnTableA)
+    {
+        if (inFnTableA)
+        {
+            /* The size of the version 1 table is based on platform sdk's
+             * sspi.h, though the sample ssp also provided with platform sdk
+             * implies only functions through QuerySecurityPackageInfoA are
+             * implemented (yikes)
+             */
+            size_t tableSize = inFnTableA->dwVersion == 1 ?
+             (LPBYTE)&inFnTableA->SetContextAttributesA -
+             (LPBYTE)inFnTableA : sizeof(SecurityFunctionTableA);
+
+            memcpy(fnTableA, inFnTableA, tableSize);
+            /* override this, since we can do it internally anyway */
+            fnTableA->QuerySecurityPackageInfoA =
+             QuerySecurityPackageInfoA;
+        }
+        else if (inFnTableW)
+        {
+            /* functions with thunks */
+            if (inFnTableW->AcquireCredentialsHandleW)
+                fnTableA->AcquireCredentialsHandleA =
+                 thunk_AcquireCredentialsHandleA;
+            if (inFnTableW->InitializeSecurityContextW)
+                fnTableA->InitializeSecurityContextA =
+                 thunk_InitializeSecurityContextA;
+            if (inFnTableW->ImportSecurityContextW)
+                fnTableA->ImportSecurityContextA =
+                 thunk_ImportSecurityContextA;
+            if (inFnTableW->AddCredentialsW)
+                fnTableA->AddCredentialsA =
+                 thunk_AddCredentialsA;
+            if (inFnTableW->QueryCredentialsAttributesW)
+                fnTableA->QueryCredentialsAttributesA =
+                 thunk_QueryCredentialsAttributesA;
+            if (inFnTableW->QueryContextAttributesW)
+                fnTableA->QueryContextAttributesA =
+                 thunk_QueryContextAttributesA;
+            if (inFnTableW->SetContextAttributesW)
+                fnTableA->SetContextAttributesA =
+                 thunk_SetContextAttributesA;
+            /* this can't be thunked, there's no extra param to know which
+             * package to forward to */
+            fnTableA->EnumerateSecurityPackagesA = NULL;
+            /* functions with no thunks needed */
+            fnTableA->AcceptSecurityContext = inFnTableW->AcceptSecurityContext;
+            fnTableA->CompleteAuthToken = inFnTableW->CompleteAuthToken;
+            fnTableA->DeleteSecurityContext = inFnTableW->DeleteSecurityContext;
+            fnTableA->ImpersonateSecurityContext =
+             inFnTableW->ImpersonateSecurityContext;
+            fnTableA->RevertSecurityContext = inFnTableW->RevertSecurityContext;
+            fnTableA->MakeSignature = inFnTableW->MakeSignature;
+            fnTableA->VerifySignature = inFnTableW->VerifySignature;
+            fnTableA->FreeContextBuffer = inFnTableW->FreeContextBuffer;
+            fnTableA->QuerySecurityPackageInfoA =
+             QuerySecurityPackageInfoA;
+            fnTableA->ExportSecurityContext =
+             inFnTableW->ExportSecurityContext;
+            fnTableA->QuerySecurityContextToken =
+             inFnTableW->QuerySecurityContextToken;
+            fnTableA->EncryptMessage = inFnTableW->EncryptMessage;
+            fnTableA->DecryptMessage = inFnTableW->DecryptMessage;
+        }
+    }
+}
+
+static void _makeFnTableW(PSecurityFunctionTableW fnTableW,
+ const PSecurityFunctionTableA inFnTableA,
+ const PSecurityFunctionTableW inFnTableW)
+{
+    if (fnTableW)
+    {
+        if (inFnTableW)
+        {
+            /* The size of the version 1 table is based on platform sdk's
+             * sspi.h, though the sample ssp also provided with platform sdk
+             * implies only functions through QuerySecurityPackageInfoA are
+             * implemented (yikes)
+             */
+            size_t tableSize = inFnTableW->dwVersion == 1 ?
+             (LPBYTE)&inFnTableW->SetContextAttributesW -
+             (LPBYTE)inFnTableW : sizeof(SecurityFunctionTableW);
+
+            memcpy(fnTableW, inFnTableW, tableSize);
+            /* override this, since we can do it internally anyway */
+            fnTableW->QuerySecurityPackageInfoW =
+             QuerySecurityPackageInfoW;
+        }
+        else if (inFnTableA)
+        {
+            /* functions with thunks */
+            if (inFnTableA->AcquireCredentialsHandleA)
+                fnTableW->AcquireCredentialsHandleW =
+                 thunk_AcquireCredentialsHandleW;
+            if (inFnTableA->InitializeSecurityContextA)
+                fnTableW->InitializeSecurityContextW =
+                 thunk_InitializeSecurityContextW;
+            if (inFnTableA->ImportSecurityContextA)
+                fnTableW->ImportSecurityContextW =
+                 thunk_ImportSecurityContextW;
+            if (inFnTableA->AddCredentialsA)
+                fnTableW->AddCredentialsW =
+                 thunk_AddCredentialsW;
+            if (inFnTableA->QueryCredentialsAttributesA)
+                fnTableW->QueryCredentialsAttributesW =
+                 thunk_QueryCredentialsAttributesW;
+            if (inFnTableA->QueryContextAttributesA)
+                fnTableW->QueryContextAttributesW =
+                 thunk_QueryContextAttributesW;
+            if (inFnTableA->SetContextAttributesA)
+                fnTableW->SetContextAttributesW =
+                 thunk_SetContextAttributesW;
+            /* this can't be thunked, there's no extra param to know which
+             * package to forward to */
+            fnTableW->EnumerateSecurityPackagesW = NULL;
+            /* functions with no thunks needed */
+            fnTableW->AcceptSecurityContext = inFnTableA->AcceptSecurityContext;
+            fnTableW->CompleteAuthToken = inFnTableA->CompleteAuthToken;
+            fnTableW->DeleteSecurityContext = inFnTableA->DeleteSecurityContext;
+            fnTableW->ImpersonateSecurityContext =
+             inFnTableA->ImpersonateSecurityContext;
+            fnTableW->RevertSecurityContext = inFnTableA->RevertSecurityContext;
+            fnTableW->MakeSignature = inFnTableA->MakeSignature;
+            fnTableW->VerifySignature = inFnTableA->VerifySignature;
+            fnTableW->FreeContextBuffer = inFnTableA->FreeContextBuffer;
+            fnTableW->QuerySecurityPackageInfoW =
+             QuerySecurityPackageInfoW;
+            fnTableW->ExportSecurityContext =
+             inFnTableA->ExportSecurityContext;
+            fnTableW->QuerySecurityContextToken =
+             inFnTableA->QuerySecurityContextToken;
+            fnTableW->EncryptMessage = inFnTableA->EncryptMessage;
+            fnTableW->DecryptMessage = inFnTableA->DecryptMessage;
+        }
+    }
+}
+
+static void _copyPackageInfo(PSecPkgInfoW info, PSecPkgInfoA inInfoA,
+ PSecPkgInfoW inInfoW)
+{
+    if (info && (inInfoA || inInfoW))
+    {
+        /* odd, I know, but up until Name and Comment the structures are
+         * identical
+         */
+        memcpy(info, inInfoW ? inInfoW : (PSecPkgInfoW)inInfoA, sizeof(*info));
+        if (inInfoW)
+        {
+            info->Name = SECUR32_strdupW(inInfoW->Name);
+            info->Comment = SECUR32_strdupW(inInfoW->Comment);
+        }
+        else
+        {
+            info->Name = SECUR32_AllocWideFromMultiByte(inInfoA->Name);
+            info->Comment = SECUR32_AllocWideFromMultiByte(inInfoA->Comment);
+        }
+    }
+}
+
+static void _tryLoadProvider(PWSTR moduleName)
+{
+    HMODULE lib = LoadLibraryW(moduleName);
+
+    if (lib)
+    {
+        INIT_SECURITY_INTERFACE_W pInitSecurityInterfaceW =
+         (INIT_SECURITY_INTERFACE_W)GetProcAddress(lib,
+         SECURITY_ENTRYPOINT_ANSIW);
+        INIT_SECURITY_INTERFACE_A pInitSecurityInterfaceA =
+         (INIT_SECURITY_INTERFACE_A)GetProcAddress(lib,
+         SECURITY_ENTRYPOINT_ANSIA);
+
+        TRACE("loaded %s, InitSecurityInterfaceA is %p, InitSecurityInterfaceW is %p\n",
+         debugstr_w(moduleName), pInitSecurityInterfaceA,
+         pInitSecurityInterfaceW);
+        if (pInitSecurityInterfaceW || pInitSecurityInterfaceA)
+        {
+            PSecurityFunctionTableA fnTableA = NULL;
+            PSecurityFunctionTableW fnTableW = NULL;
+            ULONG toAdd = 0;
+            PSecPkgInfoA infoA = NULL;
+            PSecPkgInfoW infoW = NULL;
+            SECURITY_STATUS ret = SEC_E_OK;
+
+            if (pInitSecurityInterfaceA)
+                fnTableA = pInitSecurityInterfaceA();
+            if (pInitSecurityInterfaceW)
+                fnTableW = pInitSecurityInterfaceW();
+            if (fnTableW && fnTableW->EnumerateSecurityPackagesW)
+                ret = fnTableW->EnumerateSecurityPackagesW(&toAdd, &infoW);
+            else if (fnTableA && fnTableA->EnumerateSecurityPackagesA)
+                ret = fnTableA->EnumerateSecurityPackagesA(&toAdd, &infoA);
+            if (ret == SEC_E_OK && toAdd > 0 && (infoW || infoA))
+            {
+                providerTable = _resizeProviderTable(providerTable,
+                 providerTable ? providerTable->numProviders + 1 : 1);
+                packageTable = _resizePackageTable(packageTable,
+                 packageTable ? packageTable->numPackages + toAdd : toAdd);
+                if (providerTable && packageTable)
+                {
+                    ULONG i;
+                    SecureProvider *provider =
+                     &providerTable->table[providerTable->numProviders];
+
+                    EnterCriticalSection(&cs);
+                    provider->moduleName = SECUR32_strdupW(moduleName);
+                    provider->lib = NULL;
+                    for (i = 0; i < toAdd; i++)
+                    {
+                        SecurePackage *package =
+                         &packageTable->table[packageTable->numPackages + i];
+
+                        package->provider = provider;
+                        _copyPackageInfo(&package->infoW,
+                         infoA ? &infoA[i] : NULL,
+                         infoW ? &infoW[i] : NULL);
+                    }
+                    packageTable->numPackages += toAdd;
+                    providerTable->numProviders++;
+                    LeaveCriticalSection(&cs);
+                }
+                if (infoW)
+                    fnTableW->FreeContextBuffer(infoW);
+                else
+                    fnTableA->FreeContextBuffer(infoA);
+            }
+        }
+        FreeLibrary(lib);
+    }
+    else
+        WARN("failed to load %s\n", debugstr_w(moduleName));
+}
+
+static WCHAR securityProvidersKeyW[] = {
+ 'S','Y','S','T','E','M','\\','C','u','r','r','e','n','t','C','o','n','t','r',
+ 'o','l','S','e','t','\\','C','o','n','t','r','o','l','\\','S','e','c','u','r',
+ 'i','t','y','P','r','o','v','i','d','e','r','s','\0'
+ };
+static const WCHAR securityProvidersW[] = {
+ 'S','e','c','u','r','i','t','y','P','r','o','v','i','d','e','r','s',0
+ };
+ 
+static void SECUR32_initializeProviders(void)
+{
+    HKEY key;
+    long apiRet;
+
+    TRACE("\n");
+    InitializeCriticalSection(&cs);
+    apiRet = RegOpenKeyExW(HKEY_LOCAL_MACHINE, securityProvidersKeyW, 0,
+     KEY_READ, &key);
+    if (apiRet == ERROR_SUCCESS)
+    {
+        WCHAR securityPkgNames[MAX_PATH]; /* arbitrary len */
+        DWORD size = sizeof(securityPkgNames) / sizeof(WCHAR), type;
+
+        apiRet = RegQueryValueExW(key, securityProvidersW, NULL, &type,
+         (PBYTE)securityPkgNames, &size);
+        if (apiRet == ERROR_SUCCESS && type == REG_SZ)
+        {
+            WCHAR *ptr;
+
+            for (ptr = securityPkgNames;
+             ptr < (PWSTR)((PBYTE)securityPkgNames + size); )
+            {
+                WCHAR *comma;
+
+                for (comma = ptr; *comma && *comma != ','; comma++)
+                    ;
+                if (*comma == ',')
+                    *comma = '\0';
+                for (; *ptr && isspace(*ptr) && ptr < securityPkgNames + size;
+                 ptr++)
+                    ;
+                if (*ptr)
+                    _tryLoadProvider(ptr);
+                ptr += lstrlenW(ptr) + 1;
+            }
+        }
+        RegCloseKey(key);
+    }
+}
+
+SecurePackage *SECUR32_findPackageW(PWSTR packageName)
+{
+    SecurePackage *ret;
+
+    if (packageTable && packageName)
+    {
+        DWORD i;
+
+        for (i = 0, ret = NULL; !ret && i < packageTable->numPackages; i++)
+            if (!lstrcmpiW(packageTable->table[i].infoW.Name, packageName))
+                ret = &packageTable->table[i];
+        if (ret && ret->provider && !ret->provider->lib)
+        {
+            ret->provider->lib = LoadLibraryW(ret->provider->moduleName);
+            if (ret->provider->lib)
+            {
+                INIT_SECURITY_INTERFACE_W pInitSecurityInterfaceW =
+                 (INIT_SECURITY_INTERFACE_W)GetProcAddress(ret->provider->lib,
+                 SECURITY_ENTRYPOINT_ANSIW);
+                INIT_SECURITY_INTERFACE_A pInitSecurityInterfaceA =
+                 (INIT_SECURITY_INTERFACE_A)GetProcAddress(ret->provider->lib,
+                 SECURITY_ENTRYPOINT_ANSIA);
+                PSecurityFunctionTableA fnTableA = NULL;
+                PSecurityFunctionTableW fnTableW = NULL;
+
+                if (pInitSecurityInterfaceA)
+                    fnTableA = pInitSecurityInterfaceA();
+                if (pInitSecurityInterfaceW)
+                    fnTableW = pInitSecurityInterfaceW();
+                _makeFnTableA(&ret->provider->fnTableA, fnTableA, fnTableW);
+                _makeFnTableW(&ret->provider->fnTableW, fnTableA, fnTableW);
+            }
+            else
+                ret = NULL;
+        }
+    }
+    else
+        ret = NULL;
+    return ret;
+}
+
+SecurePackage *SECUR32_findPackageA(PSTR packageName)
+{
+    SecurePackage *ret;
+
+    if (packageTable && packageName)
+    {
+        UNICODE_STRING package;
+
+        RtlCreateUnicodeStringFromAsciiz(&package, packageName);
+        ret = SECUR32_findPackageW(package.Buffer);
+        RtlFreeUnicodeString(&package);
+    }
+    else
+        ret = NULL;
+    return ret;
+}
+
+static void SECUR32_freeProviders(void)
+{
+    DWORD i;
+
+    TRACE("\n");
+    EnterCriticalSection(&cs);
+    if (packageTable)
+    {
+        for (i = 0; i < packageTable->numPackages; i++)
+        {
+            SECUR32_FREE(packageTable->table[i].infoW.Name);
+            SECUR32_FREE(packageTable->table[i].infoW.Comment);
+        }
+        HeapFree(GetProcessHeap(), 0, packageTable);
+        packageTable = NULL;
+    }
+    if (providerTable)
+    {
+        for (i = 0; i < providerTable->numProviders; i++)
+        {
+            if (providerTable->table[i].moduleName)
+                SECUR32_FREE(providerTable->table[i].moduleName);
+            if (providerTable->table[i].lib)
+                FreeLibrary(providerTable->table[i].lib);
+        }
+        HeapFree(GetProcessHeap(), 0, providerTable);
+        providerTable = NULL;
+    }
+    LeaveCriticalSection(&cs);
+    DeleteCriticalSection(&cs);
+}
+
+/* Doh--if pv was allocated by a crypto package, this may not be correct.
+ * The sample ssp seems to use LocalAlloc/LocalFee, but there doesn't seem to
+ * be any guarantee, nor is there an alloc function in secur32.
+ */
+SECURITY_STATUS SEC_ENTRY FreeContextBuffer(PVOID pv)
+{
+    SECURITY_STATUS ret;
+
+    /* as it turns out, SECURITY_STATUSes are actually HRESULTS */
+    if (pv)
+    {
+        if (SECUR32_FREE(pv) == NULL)
+            ret = SEC_E_OK;
+        else
+            ret = HRESULT_FROM_WIN32(GetLastError());
+    }
+    else
+        ret = HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER);
+    return ret;
+}
+
+SECURITY_STATUS SEC_ENTRY EnumerateSecurityPackagesW(PULONG pcPackages,
+ PSecPkgInfoW *ppPackageInfo)
+{
+    SECURITY_STATUS ret = SEC_E_OK;
+
+    /* windows just crashes if pcPackages or ppPackageInfo is NULL, so will I */
+    *pcPackages = 0;
+    EnterCriticalSection(&cs);
+    if (packageTable)
+    {
+        ULONG i;
+        size_t bytesNeeded;
+
+        bytesNeeded = packageTable->numPackages * sizeof(SecPkgInfoW);
+        for (i = 0; i < packageTable->numPackages; i++)
+        {
+            if (packageTable->table[i].infoW.Name)
+                bytesNeeded +=
+                 (lstrlenW(packageTable->table[i].infoW.Name) + 1) *
+                 sizeof(WCHAR);
+            if (packageTable->table[i].infoW.Comment)
+                bytesNeeded +=
+                 (lstrlenW(packageTable->table[i].infoW.Comment) + 1) *
+                 sizeof(WCHAR);
+        }
+        if (bytesNeeded)
+        {
+            *ppPackageInfo = (PSecPkgInfoW)SECUR32_ALLOC(bytesNeeded);
+            if (*ppPackageInfo)
+            {
+                PWSTR nextString;
+
+                *pcPackages = packageTable->numPackages;
+                nextString = (PWSTR)((PBYTE)*ppPackageInfo +
+                 packageTable->numPackages * sizeof(SecPkgInfoW));
+                for (i = 0; i < packageTable->numPackages; i++)
+                {
+                    PSecPkgInfoW pkgInfo = *ppPackageInfo + i;
+
+                    memcpy(pkgInfo, &packageTable->table[i].infoW,
+                     sizeof(SecPkgInfoW));
+                    if (packageTable->table[i].infoW.Name)
+                    {
+                        pkgInfo->Name = nextString;
+                        lstrcpyW(nextString, packageTable->table[i].infoW.Name);
+                        nextString += lstrlenW(nextString) + 1;
+                    }
+                    else
+                        pkgInfo->Name = NULL;
+                    if (packageTable->table[i].infoW.Comment)
+                    {
+                        pkgInfo->Comment = nextString;
+                        lstrcpyW(nextString,
+                         packageTable->table[i].infoW.Comment);
+                        nextString += lstrlenW(nextString) + 1;
+                    }
+                    else
+                        pkgInfo->Comment = NULL;
+                }
+            }
+            else
+                ret = SEC_E_INSUFFICIENT_MEMORY;
+        }
+    }
+    LeaveCriticalSection(&cs);
+    return ret;
+}
+
+/* Converts info (which is assumed to be an array of cPackages SecPkgInfoW
+ * structures) into an array of SecPkgInfoA structures, which it returns.
+ */
+static PSecPkgInfoA thunk_PSecPkgInfoWToA(ULONG cPackages,
+ const PSecPkgInfoW info)
+{
+    PSecPkgInfoA ret;
+
+    if (info)
+    {
+        size_t bytesNeeded = cPackages * sizeof(SecPkgInfoA);
+        ULONG i;
+
+        for (i = 0; i < cPackages; i++)
+        {
+            if (info[i].Name)
+                bytesNeeded += WideCharToMultiByte(CP_ACP, 0, info[i].Name,
+                 -1, NULL, 0, NULL, NULL);
+            if (info[i].Comment)
+                bytesNeeded += WideCharToMultiByte(CP_ACP, 0, info[i].Comment,
+                 -1, NULL, 0, NULL, NULL);
+        }
+        ret = (PSecPkgInfoA)SECUR32_ALLOC(bytesNeeded);
+        if (ret)
+        {
+            PSTR nextString;
+
+            nextString = (PSTR)((PBYTE)ret + cPackages * sizeof(SecPkgInfoA));
+            for (i = 0; i < cPackages; i++)
+            {
+                PSecPkgInfoA pkgInfo = ret + i;
+                int bytes;
+
+                memcpy(pkgInfo, &info[i], sizeof(SecPkgInfoA));
+                if (info[i].Name)
+                {
+                    pkgInfo->Name = nextString;
+                    /* just repeat back to WideCharToMultiByte how many bytes
+                     * it requires, since we asked it earlier
+                     */
+                    bytes = WideCharToMultiByte(CP_ACP, 0, info[i].Name, -1,
+                     NULL, 0, NULL, NULL);
+                    WideCharToMultiByte(CP_ACP, 0, info[i].Name, -1,
+                     pkgInfo->Name, bytes, NULL, NULL);
+                    nextString += lstrlenA(nextString) + 1;
+                }
+                else
+                    pkgInfo->Name = NULL;
+                if (info[i].Comment)
+                {
+                    pkgInfo->Comment = nextString;
+                    /* just repeat back to WideCharToMultiByte how many bytes
+                     * it requires, since we asked it earlier
+                     */
+                    bytes = WideCharToMultiByte(CP_ACP, 0, info[i].Comment, -1,
+                     NULL, 0, NULL, NULL);
+                    WideCharToMultiByte(CP_ACP, 0, info[i].Comment, -1,
+                     pkgInfo->Comment, bytes, NULL, NULL);
+                    nextString += lstrlenA(nextString) + 1;
+                }
+                else
+                    pkgInfo->Comment = NULL;
+            }
+        }
+    }
+    else
+        ret = NULL;
+    return ret;
+}
+
+SECURITY_STATUS SEC_ENTRY EnumerateSecurityPackagesA(PULONG pcPackages,
+ PSecPkgInfoA *ppPackageInfo)
+{
+    SECURITY_STATUS ret;
+    PSecPkgInfoW info;
+
+    ret = EnumerateSecurityPackagesW(pcPackages, &info);
+    if (ret == SEC_E_OK && *pcPackages && info)
+    {
+        *ppPackageInfo = thunk_PSecPkgInfoWToA(*pcPackages, info);
+        if (*pcPackages && !*ppPackageInfo)
+        {
+            *pcPackages = 0;
+            ret = SEC_E_INSUFFICIENT_MEMORY;
+        }
+        FreeContextBuffer(info);
+    }
+    return ret;
+}
+
+BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
+{
+    if (fdwReason == DLL_PROCESS_ATTACH)
+    {
+        DisableThreadLibraryCalls(hinstDLL);
+        SECUR32_initializeProviders();
+    }
+    else if (fdwReason == DLL_PROCESS_DETACH)
+    {
+        SECUR32_freeProviders();
+    }
+
+    return TRUE;
+}
diff --git a/dlls/secur32/secur32.spec b/dlls/secur32/secur32.spec
new file mode 100644
index 0000000..20ccc28
--- /dev/null
+++ b/dlls/secur32/secur32.spec
@@ -0,0 +1,73 @@
+1 stub SecDeleteUserModeContext
+2 stub SecInitUserModeContext
+
+@ stdcall AcceptSecurityContext(ptr ptr ptr long long ptr ptr ptr ptr)
+@ stdcall AcquireCredentialsHandleA(str str long ptr ptr ptr ptr ptr ptr)
+@ stdcall AcquireCredentialsHandleW(wstr wstr long ptr ptr ptr ptr ptr ptr)
+@ stdcall AddCredentialsA(ptr str str long ptr ptr ptr ptr)
+@ stdcall AddCredentialsW(ptr wstr wstr long ptr ptr ptr ptr)
+@ stub AddSecurityPackageA
+@ stub AddSecurityPackageW
+@ stdcall ApplyControlToken(ptr ptr)
+@ stdcall CompleteAuthToken(ptr ptr)
+@ stdcall DecryptMessage(ptr ptr long ptr)
+@ stdcall DeleteSecurityContext(ptr)
+@ stub DeleteSecurityPackageA
+@ stub DeleteSecurityPackageW
+@ stdcall EncryptMessage(ptr long ptr long)
+@ stdcall EnumerateSecurityPackagesA(ptr ptr)
+@ stdcall EnumerateSecurityPackagesW(ptr ptr)
+@ stdcall ExportSecurityContext(ptr long ptr ptr)
+@ stdcall FreeContextBuffer(ptr)
+@ stdcall FreeCredentialsHandle(ptr)
+@ stub GetComputerObjectNameA
+@ stub GetComputerObjectNameW
+@ stub GetSecurityUserInfo
+@ stub GetUserNameExA
+@ stub GetUserNameExW
+@ stdcall ImpersonateSecurityContext(ptr)
+@ stdcall ImportSecurityContextA(str ptr ptr ptr)
+@ stdcall ImportSecurityContextW(wstr ptr ptr ptr)
+@ stdcall InitSecurityInterfaceA()
+@ stdcall InitSecurityInterfaceW()
+@ stdcall InitializeSecurityContextA(ptr ptr str long long long ptr long ptr ptr ptr ptr)
+@ stdcall InitializeSecurityContextW(ptr ptr wstr long long long ptr long ptr ptr ptr ptr)
+@ stub LsaCallAuthenticationPackage
+@ stub LsaConnectUntrusted
+@ stub LsaDeregisterLogonProcess
+@ stub LsaEnumerateLogonSessions
+@ stub LsaFreeReturnBuffer
+@ stub LsaGetLogonSessionData
+@ stub LsaLogonUser
+@ stub LsaLookupAuthenticationPackage
+@ stub LsaRegisterLogonProcess
+@ stub LsaRegisterPolicyChangeNotification
+@ stub LsaUnregisterPolicyChangeNotification
+@ stdcall MakeSignature(ptr long ptr long)
+@ stdcall QueryContextAttributesA(ptr long ptr)
+@ stdcall QueryContextAttributesW(ptr long ptr)
+@ stdcall QueryCredentialsAttributesA(ptr long ptr)
+@ stdcall QueryCredentialsAttributesW(ptr long ptr)
+@ stdcall QuerySecurityContextToken(ptr ptr)
+@ stdcall QuerySecurityPackageInfoA(str ptr)
+@ stdcall QuerySecurityPackageInfoW(wstr ptr)
+@ stdcall RevertSecurityContext(ptr)
+@ stub SaslAcceptSecurityContext
+@ stub SaslEnumerateProfilesA
+@ stub SaslEnumerateProfilesW
+@ stub SaslGetProfilePackageA
+@ stub SaslGetProfilePackageW
+@ stub SaslIdentifyPackageA
+@ stub SaslIdentifyPackageW
+@ stub SaslInitializeSecurityContextA
+@ stub SaslInitializeSecurityContextW
+@ stub SealMessage
+@ stub SecCacheSspiPackages
+@ stub SecGetLocaleSpecificEncryptionRules
+@ stub SecpFreeMemory
+@ stub SecpTranslateName
+@ stub SecpTranslateNameEx
+@ stub TranslateNameA
+@ stub TranslateNameW
+@ stub UnsealMessage
+@ stdcall VerifySignature(ptr ptr long ptr)
diff --git a/dlls/secur32/secur32_priv.h b/dlls/secur32/secur32_priv.h
new file mode 100644
index 0000000..281cf46
--- /dev/null
+++ b/dlls/secur32/secur32_priv.h
@@ -0,0 +1,64 @@
+/*
+ * secur32 private definitions.
+ *
+ * Copyright (C) 2004 Juan Lang
+ *
+ * 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
+ */
+
+#ifndef __SECUR32_PRIV_H__
+#define __SECUR32_PRIV_H__
+
+/* Memory allocation functions for memory accessible by callers of secur32.
+ * There is no REALLOC, because LocalReAlloc can only work if used in
+ * conjunction with LMEM_MOVEABLE and LocalLock, but callers aren't using
+ * LocalLock.  I don't use the Heap functions because there seems to be an
+ * implicit assumption that LocalAlloc and Free will be used--MS' secur32
+ * imports them (but not the heap functions), the sample SSP uses them, and
+ * there isn't an exported secur32 function to allocate memory.
+ */
+#define SECUR32_ALLOC(bytes) LocalAlloc(0, (bytes))
+#define SECUR32_FREE(p)      LocalFree(p)
+
+typedef struct _SecureProvider
+{
+    PWSTR                   moduleName;
+    HMODULE                 lib;
+    SecurityFunctionTableA  fnTableA;
+    SecurityFunctionTableW  fnTableW;
+} SecureProvider;
+
+typedef struct _SecurePackage
+{
+    SecPkgInfoW     infoW;
+    SecureProvider *provider;
+} SecurePackage;
+
+/* Tries to find the package named packageName.  If it finds it, implicitly
+ * loads the package if it isn't already loaded.
+ */
+SecurePackage *SECUR32_findPackageW(PWSTR packageName);
+
+/* Tries to find the package named packageName.  (Thunks to _findPackageW)
+ */
+SecurePackage *SECUR32_findPackageA(PSTR packageName);
+
+/* A few string helpers; will return NULL if str is NULL.  Free return with
+ * SECUR32_FREE */
+PWSTR SECUR32_strdupW(PCWSTR str);
+PWSTR SECUR32_AllocWideFromMultiByte(PCSTR str);
+PSTR  SECUR32_AllocMultiByteFromWide(PCWSTR str);
+
+#endif /* ndef __SECUR32_PRIV_H__ */
diff --git a/dlls/secur32/thunks.c b/dlls/secur32/thunks.c
new file mode 100644
index 0000000..29092e8
--- /dev/null
+++ b/dlls/secur32/thunks.c
@@ -0,0 +1,904 @@
+/* Copyright (C) 2004 Juan Lang
+ *
+ * This file implements thunks between wide char and multibyte functions for
+ * SSPs that only provide one or the other.
+ *
+ * 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
+ */
+#include <stdarg.h>
+#include "windef.h"
+#include "winbase.h"
+#include "winnls.h"
+#include "winreg.h"
+#include "winternl.h"
+#include "sspi.h"
+#include "secur32_priv.h"
+#include "thunks.h"
+
+#include "wine/debug.h"
+
+WINE_DEFAULT_DEBUG_CHANNEL(secur32);
+
+SECURITY_STATUS SEC_ENTRY thunk_AcquireCredentialsHandleA(
+ SEC_CHAR *pszPrincipal, SEC_CHAR *pszPackage, ULONG fCredentialsUse,
+ PLUID pvLogonID, PVOID pAuthData, SEC_GET_KEY_FN pGetKeyFn,
+ PVOID pvGetKeyArgument, PCredHandle phCredential, PTimeStamp ptsExpiry)
+{
+    SECURITY_STATUS ret;
+
+    TRACE("%s %s %ld %p %p %p %p %p %p\n", debugstr_a(pszPrincipal),
+     debugstr_a(pszPackage), fCredentialsUse, pvLogonID, pAuthData, pGetKeyFn,
+     pvGetKeyArgument, phCredential, ptsExpiry);
+    if (pszPackage)
+    {
+        UNICODE_STRING principal, package;
+
+        RtlCreateUnicodeStringFromAsciiz(&principal, pszPrincipal);
+        RtlCreateUnicodeStringFromAsciiz(&package, pszPackage);
+        ret = AcquireCredentialsHandleW(principal.Buffer, package.Buffer,
+         fCredentialsUse, pvLogonID, pAuthData, pGetKeyFn, pvGetKeyArgument,
+         phCredential, ptsExpiry);
+        RtlFreeUnicodeString(&principal);
+        RtlFreeUnicodeString(&package);
+    }
+    else
+        ret = SEC_E_SECPKG_NOT_FOUND;
+    return ret;
+}
+
+SECURITY_STATUS SEC_ENTRY thunk_AcquireCredentialsHandleW(
+ SEC_WCHAR *pszPrincipal, SEC_WCHAR *pszPackage, ULONG fCredentialsUse,
+ PLUID pvLogonID, PVOID pAuthData, SEC_GET_KEY_FN pGetKeyFn,
+ PVOID pvGetKeyArgument, PCredHandle phCredential, PTimeStamp ptsExpiry)
+{
+    SECURITY_STATUS ret;
+
+    TRACE("%s %s %ld %p %p %p %p %p %p\n", debugstr_w(pszPrincipal),
+     debugstr_w(pszPackage), fCredentialsUse, pvLogonID, pAuthData, pGetKeyFn,
+     pvGetKeyArgument, phCredential, ptsExpiry);
+    if (pszPackage)
+    {
+        PSTR principal, package;
+
+        principal = SECUR32_AllocMultiByteFromWide(pszPrincipal);
+        package = SECUR32_AllocMultiByteFromWide(pszPackage);
+        ret = AcquireCredentialsHandleA(principal, package, fCredentialsUse,
+         pvLogonID, pAuthData, pGetKeyFn, pvGetKeyArgument, phCredential,
+         ptsExpiry);
+        if (principal)
+            SECUR32_FREE(principal);
+        if (package)
+            SECUR32_FREE(package);
+    }
+    else
+        ret = SEC_E_SECPKG_NOT_FOUND;
+    return ret;
+}
+
+/* thunking is pretty dicey for these--the output type depends on ulAttribute,
+ * so we have to know about every type the caller does
+ */
+SECURITY_STATUS SEC_ENTRY thunk_QueryCredentialsAttributesA(
+ PCredHandle phCredential, unsigned long ulAttribute, void *pBuffer)
+{
+    SECURITY_STATUS ret;
+
+    TRACE("%p %ld %p\n", phCredential, ulAttribute, pBuffer);
+    if (phCredential)
+    {
+        SecurePackage *package = (SecurePackage *)phCredential->dwUpper;
+        PCredHandle cred = (PCredHandle)phCredential->dwLower;
+
+        if (package && package->provider)
+        {
+            if (package->provider->fnTableW.QueryCredentialsAttributesW)
+            {
+                ret = package->provider->fnTableW.QueryCredentialsAttributesW(
+                 cred, ulAttribute, pBuffer);
+                if (ret == SEC_E_OK)
+                {
+                    switch (ulAttribute)
+                    {
+                        case SECPKG_CRED_ATTR_NAMES:
+                        {
+                            PSecPkgCredentials_NamesW names =
+                             (PSecPkgCredentials_NamesW)pBuffer;
+                            SEC_WCHAR *oldUser = names->sUserName;
+
+                            if (oldUser)
+                            {
+                                names->sUserName =
+                                 (PWSTR)SECUR32_AllocMultiByteFromWide(oldUser);
+                                package->provider->fnTableW.FreeContextBuffer(
+                                 oldUser);
+                            }
+                            break;
+                        }
+                        default:
+                            WARN("attribute type %ld unknown\n", ulAttribute);
+                            ret = SEC_E_INTERNAL_ERROR;
+                    }
+                }
+            }
+            else
+                ret = SEC_E_UNSUPPORTED_FUNCTION;
+        }
+        else
+            ret = SEC_E_INVALID_HANDLE;
+    }
+    else
+        ret = SEC_E_INVALID_HANDLE;
+    return ret;
+}
+
+SECURITY_STATUS SEC_ENTRY thunk_QueryCredentialsAttributesW(
+ PCredHandle phCredential, unsigned long ulAttribute, void *pBuffer)
+{
+    SECURITY_STATUS ret;
+
+    TRACE("%p %ld %p\n", phCredential, ulAttribute, pBuffer);
+    if (phCredential)
+    {
+        SecurePackage *package = (SecurePackage *)phCredential->dwUpper;
+        PCredHandle cred = (PCredHandle)phCredential->dwLower;
+
+        if (package && package->provider)
+        {
+            if (package->provider->fnTableA.QueryCredentialsAttributesA)
+            {
+                ret = package->provider->fnTableA.QueryCredentialsAttributesA(
+                 cred, ulAttribute, pBuffer);
+                if (ret == SEC_E_OK)
+                {
+                    switch (ulAttribute)
+                    {
+                        case SECPKG_CRED_ATTR_NAMES:
+                        {
+                            PSecPkgCredentials_NamesA names =
+                             (PSecPkgCredentials_NamesA)pBuffer;
+                            SEC_CHAR *oldUser = names->sUserName;
+
+                            if (oldUser)
+                            {
+                                names->sUserName =
+                                 (PSTR)SECUR32_AllocWideFromMultiByte(oldUser);
+                                package->provider->fnTableA.FreeContextBuffer(
+                                 oldUser);
+                            }
+                            break;
+                        }
+                        default:
+                            WARN("attribute type %ld unknown\n", ulAttribute);
+                            ret = SEC_E_INTERNAL_ERROR;
+                    }
+                }
+            }
+            else
+                ret = SEC_E_UNSUPPORTED_FUNCTION;
+        }
+        else
+            ret = SEC_E_INVALID_HANDLE;
+    }
+    else
+        ret = SEC_E_INVALID_HANDLE;
+    return ret;
+}
+
+SECURITY_STATUS SEC_ENTRY thunk_InitializeSecurityContextA(
+ PCredHandle phCredential, PCtxtHandle phContext,
+ SEC_CHAR *pszTargetName, unsigned long fContextReq,
+ unsigned long Reserved1, unsigned long TargetDataRep, PSecBufferDesc pInput,
+ unsigned long Reserved2, PCtxtHandle phNewContext, PSecBufferDesc pOutput,
+ unsigned long *pfContextAttr, PTimeStamp ptsExpiry)
+{
+    SECURITY_STATUS ret;
+
+    TRACE("%p %p %s %ld %ld %ld %p %ld %p %p %p %p\n", phCredential, phContext,
+     debugstr_a(pszTargetName), fContextReq, Reserved1, TargetDataRep, pInput,
+     Reserved1, phNewContext, pOutput, pfContextAttr, ptsExpiry);
+    if (phCredential)
+    {
+        SecurePackage *package = (SecurePackage *)phCredential->dwUpper;
+
+        if (package && package->provider)
+        {
+            if (package->provider->fnTableW.InitializeSecurityContextW)
+            {
+                UNICODE_STRING target;
+
+                RtlCreateUnicodeStringFromAsciiz(&target, pszTargetName);
+                ret = package->provider->fnTableW.InitializeSecurityContextW(
+                 phCredential, phContext, target.Buffer, fContextReq, Reserved1,
+                 TargetDataRep, pInput, Reserved2, phNewContext, pOutput,
+                 pfContextAttr, ptsExpiry);
+                RtlFreeUnicodeString(&target);
+            }
+            else
+                ret = SEC_E_UNSUPPORTED_FUNCTION;
+        }
+        else
+            ret = SEC_E_INVALID_HANDLE;
+    }
+    else
+        ret = SEC_E_INVALID_HANDLE;
+    return ret;
+}
+
+SECURITY_STATUS SEC_ENTRY thunk_InitializeSecurityContextW(
+ PCredHandle phCredential, PCtxtHandle phContext,
+ SEC_WCHAR *pszTargetName, unsigned long fContextReq,
+ unsigned long Reserved1, unsigned long TargetDataRep, PSecBufferDesc pInput,
+ unsigned long Reserved2, PCtxtHandle phNewContext, PSecBufferDesc pOutput,
+ unsigned long *pfContextAttr, PTimeStamp ptsExpiry)
+{
+    SECURITY_STATUS ret;
+
+    TRACE("%p %p %s %ld %ld %ld %p %ld %p %p %p %p\n", phCredential, phContext,
+     debugstr_w(pszTargetName), fContextReq, Reserved1, TargetDataRep, pInput,
+     Reserved1, phNewContext, pOutput, pfContextAttr, ptsExpiry);
+    if (phCredential)
+    {
+        SecurePackage *package = (SecurePackage *)phCredential->dwUpper;
+
+        if (package && package->provider)
+        {
+            if (package->provider->fnTableA.InitializeSecurityContextA)
+            {
+                PSTR target = SECUR32_AllocMultiByteFromWide(pszTargetName);
+
+                ret = package->provider->fnTableA.InitializeSecurityContextA(
+                 phCredential, phContext, target, fContextReq, Reserved1,
+                 TargetDataRep, pInput, Reserved2, phNewContext, pOutput,
+                 pfContextAttr, ptsExpiry);
+                if (target)
+                    SECUR32_FREE(target);
+            }
+            else
+                ret = SEC_E_UNSUPPORTED_FUNCTION;
+        }
+        else
+            ret = SEC_E_INVALID_HANDLE;
+    }
+    else
+        ret = SEC_E_INVALID_HANDLE;
+    return ret;
+}
+
+SECURITY_STATUS SEC_ENTRY thunk_AddCredentialsA(PCredHandle hCredentials,
+ SEC_CHAR *pszPrincipal, SEC_CHAR *pszPackage, unsigned long fCredentialUse,
+ void *pAuthData, SEC_GET_KEY_FN pGetKeyFn, void *pvGetKeyArgument,
+ PTimeStamp ptsExpiry)
+{
+    SECURITY_STATUS ret;
+
+    TRACE("%p %s %s %ld %p %p %p %p\n", hCredentials, debugstr_a(pszPrincipal),
+     debugstr_a(pszPackage), fCredentialUse, pAuthData, pGetKeyFn,
+     pvGetKeyArgument, ptsExpiry);
+    if (hCredentials)
+    {
+        SecurePackage *package = (SecurePackage *)hCredentials->dwUpper;
+        PCredHandle cred = (PCtxtHandle)hCredentials->dwLower;
+
+        if (package && package->provider)
+        {
+            if (package->provider->fnTableW.AddCredentialsW)
+            {
+                UNICODE_STRING szPrincipal, szPackage;
+
+                RtlCreateUnicodeStringFromAsciiz(&szPrincipal, pszPrincipal);
+                RtlCreateUnicodeStringFromAsciiz(&szPackage, pszPackage);
+                ret = package->provider->fnTableW.AddCredentialsW(
+                 cred, szPrincipal.Buffer, szPackage.Buffer, fCredentialUse,
+                 pAuthData, pGetKeyFn, pvGetKeyArgument, ptsExpiry);
+                RtlFreeUnicodeString(&szPrincipal);
+                RtlFreeUnicodeString(&szPackage);
+            }
+            else
+                ret = SEC_E_UNSUPPORTED_FUNCTION;
+        }
+        else
+            ret = SEC_E_INVALID_HANDLE;
+    }
+    else
+        ret = SEC_E_INVALID_HANDLE;
+    return ret;
+}
+
+SECURITY_STATUS SEC_ENTRY thunk_AddCredentialsW(PCredHandle hCredentials,
+ SEC_WCHAR *pszPrincipal, SEC_WCHAR *pszPackage, unsigned long fCredentialUse,
+ void *pAuthData, SEC_GET_KEY_FN pGetKeyFn, void *pvGetKeyArgument,
+ PTimeStamp ptsExpiry)
+{
+    SECURITY_STATUS ret;
+
+    TRACE("%p %s %s %ld %p %p %p %p\n", hCredentials, debugstr_w(pszPrincipal),
+     debugstr_w(pszPackage), fCredentialUse, pAuthData, pGetKeyFn,
+     pvGetKeyArgument, ptsExpiry);
+    if (hCredentials)
+    {
+        SecurePackage *package = (SecurePackage *)hCredentials->dwUpper;
+        PCredHandle cred = (PCtxtHandle)hCredentials->dwLower;
+
+        if (package && package->provider)
+        {
+            if (package->provider->fnTableA.AddCredentialsA)
+            {
+                PSTR szPrincipal = SECUR32_AllocMultiByteFromWide(pszPrincipal);
+                PSTR szPackage = SECUR32_AllocMultiByteFromWide(pszPackage);
+
+                ret = package->provider->fnTableA.AddCredentialsA(
+                 cred, szPrincipal, szPackage, fCredentialUse, pAuthData,
+                 pGetKeyFn, pvGetKeyArgument, ptsExpiry);
+                SECUR32_FREE(szPrincipal);
+                SECUR32_FREE(szPackage);
+            }
+            else
+                ret = SEC_E_UNSUPPORTED_FUNCTION;
+        }
+        else
+            ret = SEC_E_INVALID_HANDLE;
+    }
+    else
+        ret = SEC_E_INVALID_HANDLE;
+    return ret;
+}
+
+static PSecPkgInfoA _copyPackageInfoFlatWToA(PSecPkgInfoW infoW)
+{
+    PSecPkgInfoA ret;
+
+    if (infoW)
+    {
+        size_t bytesNeeded = sizeof(SecPkgInfoA);
+        int nameLen = 0, commentLen = 0;
+
+        if (infoW->Name)
+        {
+            nameLen = WideCharToMultiByte(CP_ACP, 0, infoW->Name, -1,
+             NULL, 0, NULL, NULL);
+            bytesNeeded += nameLen;
+        }
+        if (infoW->Comment)
+        {
+            commentLen = WideCharToMultiByte(CP_ACP, 0, infoW->Comment, -1,
+             NULL, 0, NULL, NULL);
+            bytesNeeded += commentLen;
+        }
+        ret = (PSecPkgInfoA)SECUR32_ALLOC(bytesNeeded);
+        if (ret)
+        {
+            PSTR nextString = (PSTR)((PBYTE)ret + sizeof(SecPkgInfoA));
+
+            memcpy(ret, infoW, sizeof(SecPkgInfoA));
+            if (infoW->Name)
+            {
+                ret->Name = nextString;
+                WideCharToMultiByte(CP_ACP, 0, infoW->Name, -1, nextString,
+                 nameLen, NULL, NULL);
+                nextString += nameLen;
+            }
+            else
+                ret->Name = NULL;
+            if (infoW->Comment)
+            {
+                ret->Comment = nextString;
+                WideCharToMultiByte(CP_ACP, 0, infoW->Comment, -1, nextString,
+                 nameLen, NULL, NULL);
+            }
+            else
+                ret->Comment = NULL;
+        }
+    }
+    else
+        ret = NULL;
+    return ret;
+}
+
+static SECURITY_STATUS thunk_ContextAttributesWToA(SecurePackage *package,
+ unsigned long ulAttribute, void *pBuffer)
+{
+    SECURITY_STATUS ret = SEC_E_OK;
+
+    if (package && pBuffer)
+    {
+        switch (ulAttribute)
+        {
+            case SECPKG_ATTR_NAMES:
+            {
+                PSecPkgContext_NamesW names = (PSecPkgContext_NamesW)pBuffer;
+                SEC_WCHAR *oldUser = names->sUserName;
+
+                if (oldUser)
+                {
+                    names->sUserName =
+                     (PWSTR)SECUR32_AllocMultiByteFromWide(oldUser);
+                    package->provider->fnTableW.FreeContextBuffer(oldUser);
+                }
+                break;
+            }
+            case SECPKG_ATTR_AUTHORITY:
+            {
+                PSecPkgContext_AuthorityW names =
+                 (PSecPkgContext_AuthorityW)pBuffer;
+                SEC_WCHAR *oldAuth = names->sAuthorityName;
+
+                if (oldAuth)
+                {
+                    names->sAuthorityName =
+                     (PWSTR)SECUR32_AllocMultiByteFromWide(oldAuth);
+                    package->provider->fnTableW.FreeContextBuffer(oldAuth);
+                }
+                break;
+            }
+            case SECPKG_ATTR_KEY_INFO:
+            {
+                PSecPkgContext_KeyInfoW info = (PSecPkgContext_KeyInfoW)pBuffer;
+                SEC_WCHAR *oldSigAlgName = info->sSignatureAlgorithmName;
+                SEC_WCHAR *oldEncAlgName = info->sEncryptAlgorithmName;
+
+                if (oldSigAlgName)
+                {
+                    info->sSignatureAlgorithmName =
+                     (PWSTR)SECUR32_AllocMultiByteFromWide(oldSigAlgName);
+                    package->provider->fnTableW.FreeContextBuffer(
+                     oldSigAlgName);
+                }
+                if (oldEncAlgName)
+                {
+                    info->sEncryptAlgorithmName =
+                     (PWSTR)SECUR32_AllocMultiByteFromWide(oldEncAlgName);
+                    package->provider->fnTableW.FreeContextBuffer(
+                     oldEncAlgName);
+                }
+                break;
+            }
+            case SECPKG_ATTR_PACKAGE_INFO:
+            {
+                PSecPkgContext_PackageInfoW info =
+                 (PSecPkgContext_PackageInfoW)pBuffer;
+                PSecPkgInfoW oldPkgInfo = info->PackageInfo;
+
+                if (oldPkgInfo)
+                {
+                    info->PackageInfo = (PSecPkgInfoW)
+                     _copyPackageInfoFlatWToA(oldPkgInfo);
+                    package->provider->fnTableW.FreeContextBuffer(oldPkgInfo);
+                }
+                break;
+            }
+            case SECPKG_ATTR_NEGOTIATION_INFO:
+            {
+                PSecPkgContext_NegotiationInfoW info =
+                 (PSecPkgContext_NegotiationInfoW)pBuffer;
+                PSecPkgInfoW oldPkgInfo = info->PackageInfo;
+
+                if (oldPkgInfo)
+                {
+                    info->PackageInfo = (PSecPkgInfoW)
+                     _copyPackageInfoFlatWToA(oldPkgInfo);
+                    package->provider->fnTableW.FreeContextBuffer(oldPkgInfo);
+                }
+                break;
+            }
+            case SECPKG_ATTR_NATIVE_NAMES:
+            {
+                PSecPkgContext_NativeNamesW names =
+                 (PSecPkgContext_NativeNamesW)pBuffer;
+                PWSTR oldClient = names->sClientName;
+                PWSTR oldServer = names->sServerName;
+
+                if (oldClient)
+                {
+                    names->sClientName = (PWSTR)SECUR32_AllocMultiByteFromWide(
+                     oldClient);
+                    package->provider->fnTableW.FreeContextBuffer(oldClient);
+                }
+                if (oldServer)
+                {
+                    names->sServerName = (PWSTR)SECUR32_AllocMultiByteFromWide(
+                     oldServer);
+                    package->provider->fnTableW.FreeContextBuffer(oldServer);
+                }
+                break;
+            }
+            case SECPKG_ATTR_CREDENTIAL_NAME:
+            {
+                PSecPkgContext_CredentialNameW name =
+                 (PSecPkgContext_CredentialNameW)pBuffer;
+                PWSTR oldCred = name->sCredentialName;
+
+                if (oldCred)
+                {
+                    name->sCredentialName =
+                     (PWSTR)SECUR32_AllocMultiByteFromWide(oldCred);
+                    package->provider->fnTableW.FreeContextBuffer(oldCred);
+                }
+                break;
+            }
+            /* no thunking needed: */
+            case SECPKG_ATTR_ACCESS_TOKEN:
+            case SECPKG_ATTR_DCE_INFO:
+            case SECPKG_ATTR_FLAGS:
+            case SECPKG_ATTR_LIFESPAN:
+            case SECPKG_ATTR_PASSWORD_EXPIRY:
+            case SECPKG_ATTR_SESSION_KEY:
+            case SECPKG_ATTR_SIZES:
+            case SECPKG_ATTR_STREAM_SIZES:
+            case SECPKG_ATTR_TARGET_INFORMATION:
+                break;
+            default:
+                WARN("attribute type %ld unknown\n", ulAttribute);
+                ret = SEC_E_INTERNAL_ERROR;
+        }
+    }
+    else
+        ret = SEC_E_INVALID_TOKEN;
+    return ret;
+}
+
+SECURITY_STATUS SEC_ENTRY thunk_QueryContextAttributesA(PCtxtHandle phContext,
+ unsigned long ulAttribute, void *pBuffer)
+{
+    SECURITY_STATUS ret;
+
+    TRACE("%p %ld %p\n", phContext, ulAttribute, pBuffer);
+    if (phContext)
+    {
+        SecurePackage *package = (SecurePackage *)phContext->dwUpper;
+        PCtxtHandle ctxt = (PCtxtHandle)phContext->dwLower;
+
+        if (package && package->provider)
+        {
+            if (package->provider->fnTableW.QueryContextAttributesW)
+            {
+                ret = package->provider->fnTableW.QueryContextAttributesW(
+                 ctxt, ulAttribute, pBuffer);
+                if (ret == SEC_E_OK)
+                    ret = thunk_ContextAttributesWToA(package, ulAttribute,
+                     pBuffer);
+            }
+            else
+                ret = SEC_E_UNSUPPORTED_FUNCTION;
+        }
+        else
+            ret = SEC_E_INVALID_HANDLE;
+    }
+    else
+        ret = SEC_E_INVALID_HANDLE;
+    return ret;
+}
+
+static PSecPkgInfoW _copyPackageInfoFlatAToW(PSecPkgInfoA infoA)
+{
+    PSecPkgInfoW ret;
+
+    if (infoA)
+    {
+        size_t bytesNeeded = sizeof(SecPkgInfoW);
+        int nameLen = 0, commentLen = 0;
+
+        if (infoA->Name)
+        {
+            nameLen = MultiByteToWideChar(CP_ACP, 0, infoA->Name, -1,
+             NULL, 0);
+            bytesNeeded += nameLen * sizeof(WCHAR);
+        }
+        if (infoA->Comment)
+        {
+            commentLen = MultiByteToWideChar(CP_ACP, 0, infoA->Comment, -1,
+             NULL, 0);
+            bytesNeeded += commentLen * sizeof(WCHAR);
+        }
+        ret = (PSecPkgInfoW)SECUR32_ALLOC(bytesNeeded);
+        if (ret)
+        {
+            PWSTR nextString = (PWSTR)((PBYTE)ret + sizeof(SecPkgInfoW));
+
+            memcpy(ret, infoA, sizeof(SecPkgInfoA));
+            if (infoA->Name)
+            {
+                ret->Name = nextString;
+                MultiByteToWideChar(CP_ACP, 0, infoA->Name, -1, nextString,
+                 nameLen);
+                nextString += nameLen;
+            }
+            else
+                ret->Name = NULL;
+            if (infoA->Comment)
+            {
+                ret->Comment = nextString;
+                MultiByteToWideChar(CP_ACP, 0, infoA->Comment, -1, nextString,
+                 nameLen);
+            }
+            else
+                ret->Comment = NULL;
+        }
+    }
+    else
+        ret = NULL;
+    return ret;
+}
+
+static SECURITY_STATUS thunk_ContextAttributesAToW(SecurePackage *package,
+ unsigned long ulAttribute, void *pBuffer)
+{
+    SECURITY_STATUS ret = SEC_E_OK;
+
+    if (package && pBuffer)
+    {
+        switch (ulAttribute)
+        {
+            case SECPKG_ATTR_NAMES:
+            {
+                PSecPkgContext_NamesA names = (PSecPkgContext_NamesA)pBuffer;
+                SEC_CHAR *oldUser = names->sUserName;
+
+                if (oldUser)
+                {
+                    names->sUserName =
+                     (PSTR)SECUR32_AllocWideFromMultiByte(oldUser);
+                    package->provider->fnTableW.FreeContextBuffer(oldUser);
+                }
+                break;
+            }
+            case SECPKG_ATTR_AUTHORITY:
+            {
+                PSecPkgContext_AuthorityA names =
+                 (PSecPkgContext_AuthorityA)pBuffer;
+                SEC_CHAR *oldAuth = names->sAuthorityName;
+
+                if (oldAuth)
+                {
+                    names->sAuthorityName =
+                     (PSTR)SECUR32_AllocWideFromMultiByte(oldAuth);
+                    package->provider->fnTableW.FreeContextBuffer(oldAuth);
+                }
+                break;
+            }
+            case SECPKG_ATTR_KEY_INFO:
+            {
+                PSecPkgContext_KeyInfoA info = (PSecPkgContext_KeyInfoA)pBuffer;
+                SEC_CHAR *oldSigAlgName = info->sSignatureAlgorithmName;
+                SEC_CHAR *oldEncAlgName = info->sEncryptAlgorithmName;
+
+                if (oldSigAlgName)
+                {
+                    info->sSignatureAlgorithmName =
+                     (PSTR)SECUR32_AllocWideFromMultiByte(oldSigAlgName);
+                    package->provider->fnTableW.FreeContextBuffer(
+                     oldSigAlgName);
+                }
+                if (oldEncAlgName)
+                {
+                    info->sEncryptAlgorithmName =
+                     (PSTR)SECUR32_AllocWideFromMultiByte(
+                     oldEncAlgName);
+                    package->provider->fnTableW.FreeContextBuffer(
+                     oldEncAlgName);
+                }
+                break;
+            }
+            case SECPKG_ATTR_PACKAGE_INFO:
+            {
+                PSecPkgContext_PackageInfoA info =
+                 (PSecPkgContext_PackageInfoA)pBuffer;
+                PSecPkgInfoA oldPkgInfo = info->PackageInfo;
+
+                if (oldPkgInfo)
+                {
+                    info->PackageInfo = (PSecPkgInfoA)
+                     _copyPackageInfoFlatAToW(oldPkgInfo);
+                    package->provider->fnTableW.FreeContextBuffer(oldPkgInfo);
+                }
+                break;
+            }
+            case SECPKG_ATTR_NEGOTIATION_INFO:
+            {
+                PSecPkgContext_NegotiationInfoA info =
+                 (PSecPkgContext_NegotiationInfoA)pBuffer;
+                PSecPkgInfoA oldPkgInfo = info->PackageInfo;
+
+                if (oldPkgInfo)
+                {
+                    info->PackageInfo = (PSecPkgInfoA)
+                     _copyPackageInfoFlatAToW(oldPkgInfo);
+                    package->provider->fnTableW.FreeContextBuffer(oldPkgInfo);
+                }
+                break;
+            }
+            case SECPKG_ATTR_NATIVE_NAMES:
+            {
+                PSecPkgContext_NativeNamesA names =
+                 (PSecPkgContext_NativeNamesA)pBuffer;
+                PSTR oldClient = names->sClientName;
+                PSTR oldServer = names->sServerName;
+
+                if (oldClient)
+                {
+                    names->sClientName = (PSTR)SECUR32_AllocWideFromMultiByte(
+                     oldClient);
+                    package->provider->fnTableW.FreeContextBuffer(oldClient);
+                }
+                if (oldServer)
+                {
+                    names->sServerName = (PSTR)SECUR32_AllocWideFromMultiByte(
+                     oldServer);
+                    package->provider->fnTableW.FreeContextBuffer(oldServer);
+                }
+                break;
+            }
+            case SECPKG_ATTR_CREDENTIAL_NAME:
+            {
+                PSecPkgContext_CredentialNameA name =
+                 (PSecPkgContext_CredentialNameA)pBuffer;
+                PSTR oldCred = name->sCredentialName;
+
+                if (oldCred)
+                {
+                    name->sCredentialName =
+                     (PSTR)SECUR32_AllocWideFromMultiByte(oldCred);
+                    package->provider->fnTableW.FreeContextBuffer(oldCred);
+                }
+                break;
+            }
+            /* no thunking needed: */
+            case SECPKG_ATTR_ACCESS_TOKEN:
+            case SECPKG_ATTR_DCE_INFO:
+            case SECPKG_ATTR_FLAGS:
+            case SECPKG_ATTR_LIFESPAN:
+            case SECPKG_ATTR_PASSWORD_EXPIRY:
+            case SECPKG_ATTR_SESSION_KEY:
+            case SECPKG_ATTR_SIZES:
+            case SECPKG_ATTR_STREAM_SIZES:
+            case SECPKG_ATTR_TARGET_INFORMATION:
+                break;
+            default:
+                WARN("attribute type %ld unknown\n", ulAttribute);
+                ret = SEC_E_INTERNAL_ERROR;
+        }
+    }
+    else
+        ret = SEC_E_INVALID_TOKEN;
+    return ret;
+}
+
+SECURITY_STATUS SEC_ENTRY thunk_QueryContextAttributesW(PCtxtHandle phContext,
+ unsigned long ulAttribute, void *pBuffer)
+{
+    SECURITY_STATUS ret;
+
+    TRACE("%p %ld %p\n", phContext, ulAttribute, pBuffer);
+    if (phContext)
+    {
+        SecurePackage *package = (SecurePackage *)phContext->dwUpper;
+        PCtxtHandle ctxt = (PCtxtHandle)phContext->dwLower;
+
+        if (package && package->provider)
+        {
+            if (package->provider->fnTableA.QueryContextAttributesA)
+            {
+                ret = package->provider->fnTableA.QueryContextAttributesA(
+                 ctxt, ulAttribute, pBuffer);
+                if (ret == SEC_E_OK)
+                    ret = thunk_ContextAttributesAToW(package, ulAttribute,
+                     pBuffer);
+            }
+            else
+                ret = SEC_E_UNSUPPORTED_FUNCTION;
+        }
+        else
+            ret = SEC_E_INVALID_HANDLE;
+    }
+    else
+        ret = SEC_E_INVALID_HANDLE;
+    return ret;
+}
+
+SECURITY_STATUS SEC_ENTRY thunk_SetContextAttributesA(PCtxtHandle phContext,
+ unsigned long ulAttribute, void *pBuffer, unsigned long cbBuffer)
+{
+    SECURITY_STATUS ret;
+
+    TRACE("%p %ld %p %ld\n", phContext, ulAttribute, pBuffer, cbBuffer);
+    if (phContext)
+    {
+        SecurePackage *package = (SecurePackage *)phContext->dwUpper;
+        PCtxtHandle ctxt = (PCtxtHandle)phContext->dwLower;
+
+        if (package && package->provider && pBuffer && cbBuffer)
+        {
+            if (package->provider->fnTableW.SetContextAttributesW)
+            {
+                /* TODO: gotta validate size too! */
+                ret = thunk_ContextAttributesWToA(package, ulAttribute,
+                 pBuffer);
+                if (ret == SEC_E_OK)
+                    ret = package->provider->fnTableW.SetContextAttributesW(
+                     ctxt, ulAttribute, pBuffer, cbBuffer);
+            }
+            else
+                ret = SEC_E_UNSUPPORTED_FUNCTION;
+        }
+        else
+            ret = SEC_E_INVALID_HANDLE;
+    }
+    else
+        ret = SEC_E_INVALID_HANDLE;
+    return ret;
+}
+
+SECURITY_STATUS SEC_ENTRY thunk_SetContextAttributesW(PCtxtHandle phContext,
+ unsigned long ulAttribute, void *pBuffer, unsigned long cbBuffer)
+{
+    SECURITY_STATUS ret;
+
+    TRACE("%p %ld %p %ld\n", phContext, ulAttribute, pBuffer, cbBuffer);
+    if (phContext)
+    {
+        SecurePackage *package = (SecurePackage *)phContext->dwUpper;
+        PCtxtHandle ctxt = (PCtxtHandle)phContext->dwLower;
+
+        if (package && package->provider && pBuffer && cbBuffer)
+        {
+            if (package->provider->fnTableA.SetContextAttributesA)
+            {
+                /* TODO: gotta validate size too! */
+                ret = thunk_ContextAttributesAToW(package, ulAttribute,
+                 pBuffer);
+                if (ret == SEC_E_OK)
+                    ret = package->provider->fnTableA.SetContextAttributesA(
+                     ctxt, ulAttribute, pBuffer, cbBuffer);
+            }
+            else
+                ret = SEC_E_UNSUPPORTED_FUNCTION;
+        }
+        else
+            ret = SEC_E_INVALID_HANDLE;
+    }
+    else
+        ret = SEC_E_INVALID_HANDLE;
+    return ret;
+}
+
+SECURITY_STATUS SEC_ENTRY thunk_ImportSecurityContextA(
+ SEC_CHAR *pszPackage, PSecBuffer pPackedContext, void *Token,
+ PCtxtHandle phContext)
+{
+    SECURITY_STATUS ret;
+    UNICODE_STRING package;
+
+    TRACE("%s %p %p %p\n", debugstr_a(pszPackage), pPackedContext, Token,
+     phContext);
+    RtlCreateUnicodeStringFromAsciiz(&package, pszPackage);
+    ret = ImportSecurityContextW(package.Buffer, pPackedContext, Token,
+     phContext);
+    RtlFreeUnicodeString(&package);
+    return ret;
+}
+
+SECURITY_STATUS SEC_ENTRY thunk_ImportSecurityContextW(
+ SEC_WCHAR *pszPackage, PSecBuffer pPackedContext, void *Token,
+ PCtxtHandle phContext)
+{
+    SECURITY_STATUS ret;
+    PSTR package = SECUR32_AllocMultiByteFromWide(pszPackage);
+
+    TRACE("%s %p %p %p\n", debugstr_w(pszPackage), pPackedContext, Token,
+     phContext);
+    ret = ImportSecurityContextA(package, pPackedContext, Token, phContext);
+    if (package)
+        SECUR32_FREE(package);
+    return ret;
+}
diff --git a/dlls/secur32/thunks.h b/dlls/secur32/thunks.h
new file mode 100644
index 0000000..f68e9a3
--- /dev/null
+++ b/dlls/secur32/thunks.h
@@ -0,0 +1,74 @@
+/* Copyright (C) 2004 Juan Lang
+ *
+ * This file defines thunks between wide char and multibyte functions for
+ * SSPs that only provide one or the other.
+ *
+ * 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
+ */
+
+#ifndef __SECUR32_THUNKS_H__
+#define __SECUR32_THUNKS_H__
+
+/* Prototypes for functions that thunk between wide char and multibyte versions,
+ * for SSPs that only provide one or the other.
+ */
+SECURITY_STATUS SEC_ENTRY thunk_AcquireCredentialsHandleA(
+ SEC_CHAR *pszPrincipal, SEC_CHAR *pszPackage, ULONG fCredentialsUse,
+ PLUID pvLogonID, PVOID pAuthData, SEC_GET_KEY_FN pGetKeyFn,
+ PVOID pvGetKeyArgument, PCredHandle phCredential, PTimeStamp ptsExpiry);
+SECURITY_STATUS SEC_ENTRY thunk_AcquireCredentialsHandleW(
+ SEC_WCHAR *pszPrincipal, SEC_WCHAR *pszPackage, ULONG fCredentialsUse,
+ PLUID pvLogonID, PVOID pAuthData, SEC_GET_KEY_FN pGetKeyFn,
+ PVOID pvGetKeyArgument, PCredHandle phCredential, PTimeStamp ptsExpiry);
+SECURITY_STATUS SEC_ENTRY thunk_InitializeSecurityContextA(
+ PCredHandle phCredential, PCtxtHandle phContext,
+ SEC_CHAR *pszTargetName, unsigned long fContextReq,
+ unsigned long Reserved1, unsigned long TargetDataRep, PSecBufferDesc pInput,
+ unsigned long Reserved2, PCtxtHandle phNewContext, PSecBufferDesc pOutput,
+ unsigned long *pfContextAttr, PTimeStamp ptsExpiry);
+SECURITY_STATUS SEC_ENTRY thunk_InitializeSecurityContextW(
+ PCredHandle phCredential, PCtxtHandle phContext,
+ SEC_WCHAR *pszTargetName, unsigned long fContextReq,
+ unsigned long Reserved1, unsigned long TargetDataRep, PSecBufferDesc pInput,
+ unsigned long Reserved2, PCtxtHandle phNewContext, PSecBufferDesc pOutput,
+ unsigned long *pfContextAttr, PTimeStamp ptsExpiry);
+SECURITY_STATUS SEC_ENTRY thunk_ImportSecurityContextA(
+ SEC_CHAR *pszPackage, PSecBuffer pPackedContext, void *Token,
+ PCtxtHandle phContext);
+SECURITY_STATUS SEC_ENTRY thunk_ImportSecurityContextW(
+ SEC_WCHAR *pszPackage, PSecBuffer pPackedContext, void *Token,
+ PCtxtHandle phContext);
+SECURITY_STATUS SEC_ENTRY thunk_AddCredentialsA(PCredHandle hCredentials,
+ SEC_CHAR *pszPrincipal, SEC_CHAR *pszPackage, unsigned long fCredentialUse,
+ void *pAuthData, SEC_GET_KEY_FN pGetKeyFn, void *pvGetKeyArgument,
+ PTimeStamp ptsExpiry);
+SECURITY_STATUS SEC_ENTRY thunk_AddCredentialsW(PCredHandle hCredentials,
+ SEC_WCHAR *pszPrincipal, SEC_WCHAR *pszPackage, unsigned long fCredentialUse,
+ void *pAuthData, SEC_GET_KEY_FN pGetKeyFn, void *pvGetKeyArgument,
+ PTimeStamp ptsExpiry);
+SECURITY_STATUS SEC_ENTRY thunk_QueryCredentialsAttributesA(
+ PCredHandle phCredential, unsigned long ulAttribute, void *pBuffer);
+SECURITY_STATUS SEC_ENTRY thunk_QueryCredentialsAttributesW(
+ PCredHandle phCredential, unsigned long ulAttribute, void *pBuffer);
+SECURITY_STATUS SEC_ENTRY thunk_QueryContextAttributesA(
+ PCtxtHandle phContext, unsigned long ulAttribute, void *pBuffer);
+SECURITY_STATUS SEC_ENTRY thunk_QueryContextAttributesW(
+ PCtxtHandle phContext, unsigned long ulAttribute, void *pBuffer);
+SECURITY_STATUS SEC_ENTRY thunk_SetContextAttributesA(PCtxtHandle phContext,
+ unsigned long ulAttribute, void *pBuffer, unsigned long cbBuffer);
+SECURITY_STATUS SEC_ENTRY thunk_SetContextAttributesW(PCtxtHandle phContext,
+ unsigned long ulAttribute, void *pBuffer, unsigned long cbBuffer);
+
+#endif /* ndef __SECUR32_THUNKS_H__ */
diff --git a/dlls/secur32/wrapper.c b/dlls/secur32/wrapper.c
new file mode 100644
index 0000000..899ed64
--- /dev/null
+++ b/dlls/secur32/wrapper.c
@@ -0,0 +1,1000 @@
+/* Copyright (C) 2004 Juan Lang
+ *
+ * Implements secur32 functions that forward to (wrap) an SSP's implementation.
+ *
+ * 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
+ */
+#include <stdarg.h>
+#include "windef.h"
+#include "winbase.h"
+#include "winnls.h"
+#include "sspi.h"
+#include "secur32_priv.h"
+
+#include "wine/debug.h"
+
+WINE_DEFAULT_DEBUG_CHANNEL(secur32);
+
+/* Tries to allocate a new SecHandle, into which it stores package (in
+ * phSec->dwUpper) and a copy of realHandle (allocated with SECUR32_ALLOC,
+ * and stored in phSec->dwLower).  SecHandle is equivalent to both a
+ * CredHandle and a CtxtHandle.
+ */
+static SECURITY_STATUS SECUR32_makeSecHandle(PSecHandle phSec,
+ SecurePackage *package, PSecHandle realHandle)
+{
+    SECURITY_STATUS ret;
+
+    if (phSec && package && realHandle)
+    {
+        PSecHandle newSec = (PSecHandle)SECUR32_ALLOC(sizeof(SecHandle));
+
+        if (newSec)
+        {
+            memcpy(newSec, realHandle, sizeof(*realHandle));
+            phSec->dwUpper = (ULONG_PTR)package;
+            phSec->dwLower = (ULONG_PTR)newSec;
+            ret = SEC_E_OK;
+        }
+        else
+            ret = SEC_E_INSUFFICIENT_MEMORY;
+    }
+    else
+        ret = SEC_E_INVALID_HANDLE;
+    return ret;
+}
+
+SECURITY_STATUS SEC_ENTRY AcquireCredentialsHandleA(
+ SEC_CHAR *pszPrincipal, SEC_CHAR *pszPackage, ULONG fCredentialsUse,
+ PLUID pvLogonID, PVOID pAuthData, SEC_GET_KEY_FN pGetKeyFn,
+ PVOID pvGetKeyArgument, PCredHandle phCredential, PTimeStamp ptsExpiry)
+{
+    SECURITY_STATUS ret;
+
+    TRACE("%s %s %ld %p %p %p %p %p %p\n", debugstr_a(pszPrincipal),
+     debugstr_a(pszPackage), fCredentialsUse, pvLogonID, pAuthData, pGetKeyFn,
+     pvGetKeyArgument, phCredential, ptsExpiry);
+    if (pszPackage)
+    {
+        SecurePackage *package = SECUR32_findPackageA(pszPackage);
+
+        if (package && package->provider)
+        {
+            if (package->provider->fnTableA.AcquireCredentialsHandleA)
+            {
+                CredHandle myCred;
+
+                ret = package->provider->fnTableA.AcquireCredentialsHandleA(
+                 pszPrincipal, pszPackage, fCredentialsUse, pvLogonID,
+                 pAuthData, pGetKeyFn, pvGetKeyArgument, &myCred,
+                 ptsExpiry);
+                if (ret == SEC_E_OK)
+                {
+                    ret = SECUR32_makeSecHandle(phCredential, package, &myCred);
+                    if (ret != SEC_E_OK)
+                        package->provider->fnTableW.FreeCredentialsHandle(
+                         &myCred);
+                }
+            }
+            else
+                ret = SEC_E_UNSUPPORTED_FUNCTION;
+        }
+        else
+            ret = SEC_E_SECPKG_NOT_FOUND;
+    }
+    else
+        ret = SEC_E_SECPKG_NOT_FOUND;
+    return ret;
+}
+
+SECURITY_STATUS SEC_ENTRY AcquireCredentialsHandleW(
+ SEC_WCHAR *pszPrincipal, SEC_WCHAR *pszPackage, ULONG fCredentialsUse,
+ PLUID pvLogonID, PVOID pAuthData, SEC_GET_KEY_FN pGetKeyFn,
+ PVOID pvGetKeyArgument, PCredHandle phCredential, PTimeStamp ptsExpiry)
+{
+    SECURITY_STATUS ret;
+
+    TRACE("%s %s %ld %p %p %p %p %p %p\n", debugstr_w(pszPrincipal),
+     debugstr_w(pszPackage), fCredentialsUse, pvLogonID, pAuthData, pGetKeyFn,
+     pvGetKeyArgument, phCredential, ptsExpiry);
+    if (pszPackage)
+    {
+        SecurePackage *package = SECUR32_findPackageW(pszPackage);
+
+        if (package && package->provider)
+        {
+            if (package->provider->fnTableW.AcquireCredentialsHandleW)
+            {
+                CredHandle myCred;
+
+                ret = package->provider->fnTableW.AcquireCredentialsHandleW(
+                 pszPrincipal, pszPackage, fCredentialsUse, pvLogonID,
+                 pAuthData, pGetKeyFn, pvGetKeyArgument, &myCred,
+                 ptsExpiry);
+                if (ret == SEC_E_OK)
+                {
+                    ret = SECUR32_makeSecHandle(phCredential, package, &myCred);
+                    if (ret != SEC_E_OK)
+                        package->provider->fnTableW.FreeCredentialsHandle(
+                         &myCred);
+                }
+            }
+            else
+                ret = SEC_E_UNSUPPORTED_FUNCTION;
+        }
+        else
+            ret = SEC_E_SECPKG_NOT_FOUND;
+    }
+    else
+        ret = SEC_E_SECPKG_NOT_FOUND;
+    return ret;
+}
+
+SECURITY_STATUS SEC_ENTRY FreeCredentialsHandle(
+ PCredHandle phCredential)
+{
+    SECURITY_STATUS ret;
+
+    TRACE("%p\n", phCredential);
+    if (phCredential)
+    {
+        SecurePackage *package = (SecurePackage *)phCredential->dwUpper;
+        PCredHandle cred = (PCredHandle)phCredential->dwLower;
+
+        if (package && package->provider &&
+         package->provider->fnTableW.FreeCredentialsHandle)
+            ret = package->provider->fnTableW.FreeCredentialsHandle(cred);
+        else
+            ret = SEC_E_INVALID_HANDLE;
+        SECUR32_FREE(cred);
+    }
+    else
+        ret = SEC_E_INVALID_HANDLE;
+    return ret;
+}
+
+SECURITY_STATUS SEC_ENTRY QueryCredentialsAttributesA(
+ PCredHandle phCredential, unsigned long ulAttribute, void *pBuffer)
+{
+    SECURITY_STATUS ret;
+
+    TRACE("%p %ld %p\n", phCredential, ulAttribute, pBuffer);
+    if (phCredential)
+    {
+        SecurePackage *package = (SecurePackage *)phCredential->dwUpper;
+        PCredHandle cred = (PCredHandle)phCredential->dwLower;
+
+        if (package && package->provider)
+        {
+            if (package->provider->fnTableA.QueryCredentialsAttributesA)
+                ret = package->provider->fnTableA.QueryCredentialsAttributesA(
+                 cred, ulAttribute, pBuffer);
+            else
+                ret = SEC_E_UNSUPPORTED_FUNCTION;
+        }
+        else
+            ret = SEC_E_INVALID_HANDLE;
+    }
+    else
+        ret = SEC_E_INVALID_HANDLE;
+    return ret;
+}
+
+SECURITY_STATUS SEC_ENTRY QueryCredentialsAttributesW(
+ PCredHandle phCredential, unsigned long ulAttribute, void *pBuffer)
+{
+    SECURITY_STATUS ret;
+
+    TRACE("%p %ld %p\n", phCredential, ulAttribute, pBuffer);
+    if (phCredential)
+    {
+        SecurePackage *package = (SecurePackage *)phCredential->dwUpper;
+        PCredHandle cred = (PCredHandle)phCredential->dwLower;
+
+        if (package && package->provider)
+        {
+            if (package->provider->fnTableW.QueryCredentialsAttributesW)
+                ret = package->provider->fnTableW.QueryCredentialsAttributesW(
+                 cred, ulAttribute, pBuffer);
+            else
+                ret = SEC_E_UNSUPPORTED_FUNCTION;
+        }
+        else
+            ret = SEC_E_INVALID_HANDLE;
+    }
+    else
+        ret = SEC_E_INVALID_HANDLE;
+    return ret;
+}
+
+SECURITY_STATUS SEC_ENTRY InitializeSecurityContextA(
+ PCredHandle phCredential, PCtxtHandle phContext,
+ SEC_CHAR *pszTargetName, unsigned long fContextReq,
+ unsigned long Reserved1, unsigned long TargetDataRep, PSecBufferDesc pInput,
+ unsigned long Reserved2, PCtxtHandle phNewContext, PSecBufferDesc pOutput,
+ unsigned long *pfContextAttr, PTimeStamp ptsExpiry)
+{
+    SECURITY_STATUS ret;
+
+    TRACE("%p %p %s %ld %ld %ld %p %ld %p %p %p %p\n", phCredential, phContext,
+     debugstr_a(pszTargetName), fContextReq, Reserved1, TargetDataRep, pInput,
+     Reserved1, phNewContext, pOutput, pfContextAttr, ptsExpiry);
+    if (phCredential)
+    {
+        SecurePackage *package = (SecurePackage *)phCredential->dwUpper;
+        PCredHandle cred = (PCredHandle)phCredential->dwLower;
+
+        if (package && package->provider)
+        {
+            if (package->provider->fnTableA.InitializeSecurityContextA)
+            {
+                CtxtHandle myCtxt;
+
+                ret = package->provider->fnTableA.InitializeSecurityContextA(
+                 cred, phContext ? &myCtxt : NULL, pszTargetName, fContextReq,
+                 Reserved1, TargetDataRep, pInput, Reserved2, &myCtxt,
+                 pOutput, pfContextAttr, ptsExpiry);
+                if (ret == SEC_E_OK)
+                {
+                    ret = SECUR32_makeSecHandle(phContext, package, &myCtxt);
+                    if (ret != SEC_E_OK)
+                        package->provider->fnTableW.DeleteSecurityContext(
+                         &myCtxt);
+                }
+            }
+            else
+                ret = SEC_E_UNSUPPORTED_FUNCTION;
+        }
+        else
+            ret = SEC_E_INVALID_HANDLE;
+    }
+    else
+        ret = SEC_E_INVALID_HANDLE;
+    return ret;
+}
+
+SECURITY_STATUS SEC_ENTRY InitializeSecurityContextW(
+ PCredHandle phCredential, PCtxtHandle phContext,
+ SEC_WCHAR *pszTargetName, unsigned long fContextReq,
+ unsigned long Reserved1, unsigned long TargetDataRep, PSecBufferDesc pInput,
+ unsigned long Reserved2, PCtxtHandle phNewContext, PSecBufferDesc pOutput,
+ unsigned long *pfContextAttr, PTimeStamp ptsExpiry)
+{
+    SECURITY_STATUS ret;
+
+    TRACE("%p %p %s %ld %ld %ld %p %ld %p %p %p %p\n", phCredential, phContext,
+     debugstr_w(pszTargetName), fContextReq, Reserved1, TargetDataRep, pInput,
+     Reserved1, phNewContext, pOutput, pfContextAttr, ptsExpiry);
+    if (phCredential)
+    {
+        SecurePackage *package = (SecurePackage *)phCredential->dwUpper;
+        PCredHandle cred = (PCredHandle)phCredential->dwLower;
+
+        if (package && package->provider)
+        {
+            if (package->provider->fnTableW.QueryCredentialsAttributesW)
+            {
+                CtxtHandle myCtxt;
+
+                ret = package->provider->fnTableW.InitializeSecurityContextW(
+                 cred, phContext ? &myCtxt : NULL, pszTargetName, fContextReq,
+                 Reserved1, TargetDataRep, pInput, Reserved2, &myCtxt,
+                 pOutput, pfContextAttr, ptsExpiry);
+                if (ret == SEC_E_OK)
+                {
+                    ret = SECUR32_makeSecHandle(phContext, package, &myCtxt);
+                    if (ret != SEC_E_OK)
+                        package->provider->fnTableW.DeleteSecurityContext(
+                         &myCtxt);
+                }
+            }
+            else
+                ret = SEC_E_UNSUPPORTED_FUNCTION;
+        }
+        else
+            ret = SEC_E_INVALID_HANDLE;
+    }
+    else
+        ret = SEC_E_INVALID_HANDLE;
+    return ret;
+}
+
+SECURITY_STATUS SEC_ENTRY AcceptSecurityContext(
+ PCredHandle phCredential, PCtxtHandle phContext, PSecBufferDesc pInput,
+ unsigned long fContextReq, unsigned long TargetDataRep,
+ PCtxtHandle phNewContext, PSecBufferDesc pOutput,
+ unsigned long *pfContextAttr, PTimeStamp ptsExpiry)
+{
+    SECURITY_STATUS ret;
+
+    TRACE("%p %p %p %ld %ld %p %p %p %p\n", phCredential, phContext, pInput,
+     fContextReq, TargetDataRep, phNewContext, pOutput, pfContextAttr,
+     ptsExpiry);
+    if (phCredential)
+    {
+        SecurePackage *package = (SecurePackage *)phCredential->dwUpper;
+        PCredHandle cred = (PCredHandle)phCredential->dwLower;
+
+        if (package && package->provider)
+        {
+            if (package->provider->fnTableW.AcceptSecurityContext)
+            {
+                CtxtHandle myCtxt;
+
+                ret = package->provider->fnTableW.AcceptSecurityContext(
+                 cred, phContext ? &myCtxt : NULL, pInput, fContextReq,
+                 TargetDataRep, &myCtxt, pOutput, pfContextAttr, ptsExpiry);
+                if (ret == SEC_E_OK)
+                {
+                    ret = SECUR32_makeSecHandle(phContext, package, &myCtxt);
+                    if (ret != SEC_E_OK)
+                        package->provider->fnTableW.DeleteSecurityContext(
+                         &myCtxt);
+                }
+            }
+            else
+                ret = SEC_E_UNSUPPORTED_FUNCTION;
+        }
+        else
+            ret = SEC_E_INVALID_HANDLE;
+    }
+    else
+        ret = SEC_E_INVALID_HANDLE;
+    return ret;
+}
+
+SECURITY_STATUS SEC_ENTRY CompleteAuthToken(PCtxtHandle phContext,
+ PSecBufferDesc pToken)
+{
+    SECURITY_STATUS ret;
+
+    TRACE("%p %p\n", phContext, pToken);
+    if (phContext)
+    {
+        SecurePackage *package = (SecurePackage *)phContext->dwUpper;
+        PCtxtHandle ctxt = (PCtxtHandle)phContext->dwLower;
+
+        if (package && package->provider)
+        {
+            if (package->provider->fnTableW.CompleteAuthToken)
+                ret = package->provider->fnTableW.CompleteAuthToken(ctxt,
+                 pToken);
+            else
+                ret = SEC_E_UNSUPPORTED_FUNCTION;
+        }
+        else
+            ret = SEC_E_INVALID_HANDLE;
+    }
+    else
+        ret = SEC_E_INVALID_HANDLE;
+    return ret;
+}
+
+SECURITY_STATUS SEC_ENTRY DeleteSecurityContext(PCtxtHandle phContext)
+{
+    SECURITY_STATUS ret;
+
+    TRACE("%p\n", phContext);
+    if (phContext)
+    {
+        SecurePackage *package = (SecurePackage *)phContext->dwUpper;
+        PCtxtHandle ctxt = (PCtxtHandle)phContext->dwLower;
+
+        if (package && package->provider &&
+         package->provider->fnTableW.DeleteSecurityContext)
+            ret = package->provider->fnTableW.DeleteSecurityContext(ctxt);
+        else
+            ret = SEC_E_INVALID_HANDLE;
+        SECUR32_FREE(ctxt);
+    }
+    else
+        ret = SEC_E_INVALID_HANDLE;
+    return ret;
+}
+
+SECURITY_STATUS SEC_ENTRY ApplyControlToken(PCtxtHandle phContext,
+ PSecBufferDesc pInput)
+{
+    SECURITY_STATUS ret;
+
+    TRACE("%p %p\n", phContext, pInput);
+    if (phContext)
+    {
+        SecurePackage *package = (SecurePackage *)phContext->dwUpper;
+        PCtxtHandle ctxt = (PCtxtHandle)phContext->dwLower;
+
+        if (package && package->provider)
+        {
+            if (package->provider->fnTableW.ApplyControlToken)
+                ret = package->provider->fnTableW.ApplyControlToken(
+                 ctxt, pInput);
+            else
+                ret = SEC_E_UNSUPPORTED_FUNCTION;
+        }
+        else
+            ret = SEC_E_INVALID_HANDLE;
+    }
+    else
+        ret = SEC_E_INVALID_HANDLE;
+    return ret;
+}
+
+SECURITY_STATUS SEC_ENTRY QueryContextAttributesA(PCtxtHandle phContext,
+ unsigned long ulAttribute, void *pBuffer)
+{
+    SECURITY_STATUS ret;
+
+    TRACE("%p %ld %p\n", phContext, ulAttribute, pBuffer);
+    if (phContext)
+    {
+        SecurePackage *package = (SecurePackage *)phContext->dwUpper;
+        PCtxtHandle ctxt = (PCtxtHandle)phContext->dwLower;
+
+        if (package && package->provider)
+        {
+            if (package->provider->fnTableA.QueryContextAttributesA)
+                ret = package->provider->fnTableA.QueryContextAttributesA(
+                 ctxt, ulAttribute, pBuffer);
+            else
+                ret = SEC_E_UNSUPPORTED_FUNCTION;
+        }
+        else
+            ret = SEC_E_INVALID_HANDLE;
+    }
+    else
+        ret = SEC_E_INVALID_HANDLE;
+    return ret;
+}
+
+SECURITY_STATUS SEC_ENTRY QueryContextAttributesW(PCtxtHandle phContext,
+ unsigned long ulAttribute, void *pBuffer)
+{
+    SECURITY_STATUS ret;
+
+    TRACE("%p %ld %p\n", phContext, ulAttribute, pBuffer);
+    if (phContext)
+    {
+        SecurePackage *package = (SecurePackage *)phContext->dwUpper;
+        PCtxtHandle ctxt = (PCtxtHandle)phContext->dwLower;
+
+        if (package && package->provider)
+        {
+            if (package->provider->fnTableW.QueryContextAttributesW)
+                ret = package->provider->fnTableW.QueryContextAttributesW(
+                 ctxt, ulAttribute, pBuffer);
+            else
+                ret = SEC_E_UNSUPPORTED_FUNCTION;
+        }
+        else
+            ret = SEC_E_INVALID_HANDLE;
+    }
+    else
+        ret = SEC_E_INVALID_HANDLE;
+    return ret;
+}
+
+SECURITY_STATUS SEC_ENTRY ImpersonateSecurityContext(PCtxtHandle phContext)
+{
+    SECURITY_STATUS ret;
+
+    TRACE("%p\n", phContext);
+    if (phContext)
+    {
+        SecurePackage *package = (SecurePackage *)phContext->dwUpper;
+        PCtxtHandle ctxt = (PCtxtHandle)phContext->dwLower;
+
+        if (package && package->provider)
+        {
+            if (package->provider->fnTableW.ImpersonateSecurityContext)
+                ret = package->provider->fnTableW.ImpersonateSecurityContext(
+                 ctxt);
+            else
+                ret = SEC_E_UNSUPPORTED_FUNCTION;
+        }
+        else
+            ret = SEC_E_INVALID_HANDLE;
+    }
+    else
+        ret = SEC_E_INVALID_HANDLE;
+    return ret;
+}
+
+SECURITY_STATUS SEC_ENTRY RevertSecurityContext(PCtxtHandle phContext)
+{
+    SECURITY_STATUS ret;
+
+    TRACE("%p\n", phContext);
+    if (phContext)
+    {
+        SecurePackage *package = (SecurePackage *)phContext->dwUpper;
+        PCtxtHandle ctxt = (PCtxtHandle)phContext->dwLower;
+
+        if (package && package->provider)
+        {
+            if (package->provider->fnTableW.RevertSecurityContext)
+                ret = package->provider->fnTableW.RevertSecurityContext(
+                 ctxt);
+            else
+                ret = SEC_E_UNSUPPORTED_FUNCTION;
+        }
+        else
+            ret = SEC_E_INVALID_HANDLE;
+    }
+    else
+        ret = SEC_E_INVALID_HANDLE;
+    return ret;
+}
+
+SECURITY_STATUS SEC_ENTRY MakeSignature(PCtxtHandle phContext, ULONG fQOP,
+ PSecBufferDesc pMessage, ULONG MessageSeqNo)
+{
+    SECURITY_STATUS ret;
+
+    TRACE("%p %ld %p %ld\n", phContext, fQOP, pMessage, MessageSeqNo);
+    if (phContext)
+    {
+        SecurePackage *package = (SecurePackage *)phContext->dwUpper;
+        PCtxtHandle ctxt = (PCtxtHandle)phContext->dwLower;
+
+        if (package && package->provider)
+        {
+            if (package->provider->fnTableW.MakeSignature)
+                ret = package->provider->fnTableW.MakeSignature(
+                 ctxt, fQOP, pMessage, MessageSeqNo);
+            else
+                ret = SEC_E_UNSUPPORTED_FUNCTION;
+        }
+        else
+            ret = SEC_E_INVALID_HANDLE;
+    }
+    else
+        ret = SEC_E_INVALID_HANDLE;
+    return ret;
+}
+
+SECURITY_STATUS SEC_ENTRY VerifySignature(PCtxtHandle phContext,
+ PSecBufferDesc pMessage, ULONG MessageSeqNo, PULONG pfQOP)
+{
+    SECURITY_STATUS ret;
+
+    TRACE("%p %p %ld %p\n", phContext, pMessage, MessageSeqNo, pfQOP);
+    if (phContext)
+    {
+        SecurePackage *package = (SecurePackage *)phContext->dwUpper;
+        PCtxtHandle ctxt = (PCtxtHandle)phContext->dwLower;
+
+        if (package && package->provider)
+        {
+            if (package->provider->fnTableW.VerifySignature)
+                ret = package->provider->fnTableW.VerifySignature(
+                 ctxt, pMessage, MessageSeqNo, pfQOP);
+            else
+                ret = SEC_E_UNSUPPORTED_FUNCTION;
+        }
+        else
+            ret = SEC_E_INVALID_HANDLE;
+    }
+    else
+        ret = SEC_E_INVALID_HANDLE;
+    return ret;
+}
+
+SECURITY_STATUS SEC_ENTRY QuerySecurityPackageInfoA(SEC_CHAR *pszPackageName,
+ PSecPkgInfoA *ppPackageInfo)
+{
+    SECURITY_STATUS ret;
+   
+    TRACE("%s %p\n", debugstr_a(pszPackageName), ppPackageInfo);
+    if (pszPackageName)
+    {
+        SecurePackage *package = SECUR32_findPackageA(pszPackageName);
+
+        if (package)
+        {
+            size_t bytesNeeded = sizeof(SecPkgInfoA);
+            int nameLen = 0, commentLen = 0;
+
+            if (package->infoW.Name)
+            {
+                nameLen = WideCharToMultiByte(CP_ACP, 0,
+                 package->infoW.Name, -1, NULL, 0, NULL, NULL);
+                bytesNeeded += nameLen;
+            }
+            if (package->infoW.Comment)
+            {
+                commentLen = WideCharToMultiByte(CP_ACP, 0,
+                 package->infoW.Comment, -1, NULL, 0, NULL, NULL);
+                bytesNeeded += commentLen;
+            }
+            *ppPackageInfo = (PSecPkgInfoA)SECUR32_ALLOC(bytesNeeded);
+            if (*ppPackageInfo)
+            {
+                PSTR nextString = (PSTR)((PBYTE)*ppPackageInfo +
+                 sizeof(SecPkgInfoA));
+
+                memcpy(*ppPackageInfo, &package->infoW, sizeof(package->infoW));
+                if (package->infoW.Name)
+                {
+                    (*ppPackageInfo)->Name = nextString;
+                    nextString += WideCharToMultiByte(CP_ACP, 0,
+                     package->infoW.Name, -1, nextString, nameLen, NULL, NULL);
+                }
+                else
+                    (*ppPackageInfo)->Name = NULL;
+                if (package->infoW.Comment)
+                {
+                    (*ppPackageInfo)->Comment = nextString;
+                    nextString += WideCharToMultiByte(CP_ACP, 0,
+                     package->infoW.Comment, -1, nextString, commentLen, NULL,
+                     NULL);
+                }
+                else
+                    (*ppPackageInfo)->Comment = NULL;
+                ret = SEC_E_OK;
+            }
+            else
+                ret = SEC_E_INSUFFICIENT_MEMORY;
+        }
+        else
+            ret = SEC_E_SECPKG_NOT_FOUND;
+    }
+    else
+        ret = SEC_E_SECPKG_NOT_FOUND;
+    return ret;
+}
+
+SECURITY_STATUS SEC_ENTRY QuerySecurityPackageInfoW(SEC_WCHAR *pszPackageName,
+ PSecPkgInfoW *ppPackageInfo)
+{
+    SECURITY_STATUS ret;
+    SecurePackage *package = SECUR32_findPackageW(pszPackageName);
+
+    TRACE("%s %p\n", debugstr_w(pszPackageName), ppPackageInfo);
+    if (package)
+    {
+        size_t bytesNeeded = sizeof(SecPkgInfoW);
+        int nameLen = 0, commentLen = 0;
+
+        if (package->infoW.Name)
+        {
+            nameLen = lstrlenW(package->infoW.Name) + 1;
+            bytesNeeded += nameLen * sizeof(WCHAR);
+        }
+        if (package->infoW.Comment)
+        {
+            commentLen = lstrlenW(package->infoW.Comment) + 1;
+            bytesNeeded += commentLen * sizeof(WCHAR);
+        }
+        *ppPackageInfo = (PSecPkgInfoW)SECUR32_ALLOC(bytesNeeded);
+        if (*ppPackageInfo)
+        {
+            PWSTR nextString = (PWSTR)((PBYTE)*ppPackageInfo +
+             sizeof(SecPkgInfoW));
+
+            memcpy(*ppPackageInfo, &package->infoW, sizeof(package->infoW));
+            if (package->infoW.Name)
+            {
+                (*ppPackageInfo)->Name = nextString;
+                lstrcpynW(nextString, package->infoW.Name, nameLen);
+                nextString += nameLen;
+            }
+            else
+                (*ppPackageInfo)->Name = NULL;
+            if (package->infoW.Comment)
+            {
+                (*ppPackageInfo)->Comment = nextString;
+                lstrcpynW(nextString, package->infoW.Comment, commentLen);
+                nextString += commentLen;
+            }
+            else
+                (*ppPackageInfo)->Comment = NULL;
+            ret = SEC_E_OK;
+        }
+        else
+            ret = SEC_E_INSUFFICIENT_MEMORY;
+    }
+    else
+        ret = SEC_E_SECPKG_NOT_FOUND;
+    return ret;
+}
+
+SECURITY_STATUS SEC_ENTRY ExportSecurityContext(PCtxtHandle phContext,
+ ULONG fFlags, PSecBuffer pPackedContext, void **pToken)
+{
+    SECURITY_STATUS ret;
+
+    TRACE("%p %ld %p %p\n", phContext, fFlags, pPackedContext, pToken);
+    if (phContext)
+    {
+        SecurePackage *package = (SecurePackage *)phContext->dwUpper;
+        PCtxtHandle ctxt = (PCtxtHandle)phContext->dwLower;
+
+        if (package && package->provider)
+        {
+            if (package->provider->fnTableW.ExportSecurityContext)
+                ret = package->provider->fnTableW.ExportSecurityContext(
+                 ctxt, fFlags, pPackedContext, pToken);
+            else
+                ret = SEC_E_UNSUPPORTED_FUNCTION;
+        }
+        else
+            ret = SEC_E_INVALID_HANDLE;
+    }
+    else
+        ret = SEC_E_INVALID_HANDLE;
+    return ret;
+}
+
+SECURITY_STATUS SEC_ENTRY ImportSecurityContextA(SEC_CHAR *pszPackage,
+ PSecBuffer pPackedContext, void *Token, PCtxtHandle phContext)
+{
+    SECURITY_STATUS ret;
+    SecurePackage *package = SECUR32_findPackageA(pszPackage);
+ 
+    TRACE("%s %p %p %p\n", debugstr_a(pszPackage), pPackedContext, Token,
+     phContext);
+    if (package && package->provider)
+    {
+        if (package->provider->fnTableA.ImportSecurityContextA)
+        {
+            CtxtHandle myCtxt;
+
+            ret = package->provider->fnTableA.ImportSecurityContextA(
+             pszPackage, pPackedContext, Token, &myCtxt);
+            if (ret == SEC_E_OK)
+            {
+                ret = SECUR32_makeSecHandle(phContext, package, &myCtxt);
+                if (ret != SEC_E_OK)
+                    package->provider->fnTableW.DeleteSecurityContext(&myCtxt);
+            }
+        }
+        else
+            ret = SEC_E_UNSUPPORTED_FUNCTION;
+    }
+    else
+        ret = SEC_E_SECPKG_NOT_FOUND;
+    return ret;
+
+}
+
+SECURITY_STATUS SEC_ENTRY ImportSecurityContextW(SEC_WCHAR *pszPackage,
+ PSecBuffer pPackedContext, void *Token, PCtxtHandle phContext)
+{
+    SECURITY_STATUS ret;
+    SecurePackage *package = SECUR32_findPackageW(pszPackage);
+
+    TRACE("%s %p %p %p\n", debugstr_w(pszPackage), pPackedContext, Token,
+     phContext);
+    if (package && package->provider)
+    {
+        if (package->provider->fnTableW.ImportSecurityContextW)
+        {
+            CtxtHandle myCtxt;
+
+            ret = package->provider->fnTableW.ImportSecurityContextW(
+             pszPackage, pPackedContext, Token, &myCtxt);
+            if (ret == SEC_E_OK)
+            {
+                ret = SECUR32_makeSecHandle(phContext, package, &myCtxt);
+                if (ret != SEC_E_OK)
+                    package->provider->fnTableW.DeleteSecurityContext(&myCtxt);
+            }
+        }
+        else
+            ret = SEC_E_UNSUPPORTED_FUNCTION;
+    }
+    else
+        ret = SEC_E_SECPKG_NOT_FOUND;
+    return ret;
+}
+
+SECURITY_STATUS SEC_ENTRY AddCredentialsA(PCredHandle hCredentials,
+ SEC_CHAR *pszPrincipal, SEC_CHAR *pszPackage, unsigned long fCredentialUse,
+ void *pAuthData, SEC_GET_KEY_FN pGetKeyFn, void *pvGetKeyArgument,
+ PTimeStamp ptsExpiry)
+{
+    SECURITY_STATUS ret;
+
+    TRACE("%p %s %s %ld %p %p %p %p\n", hCredentials, debugstr_a(pszPrincipal),
+     debugstr_a(pszPackage), fCredentialUse, pAuthData, pGetKeyFn,
+     pvGetKeyArgument, ptsExpiry);
+    if (hCredentials)
+    {
+        SecurePackage *package = (SecurePackage *)hCredentials->dwUpper;
+        PCredHandle cred = (PCtxtHandle)hCredentials->dwLower;
+
+        if (package && package->provider)
+        {
+            if (package->provider->fnTableA.AddCredentialsA)
+                ret = package->provider->fnTableA.AddCredentialsA(
+                 cred, pszPrincipal, pszPackage, fCredentialUse, pAuthData,
+                 pGetKeyFn, pvGetKeyArgument, ptsExpiry);
+            else
+                ret = SEC_E_UNSUPPORTED_FUNCTION;
+        }
+        else
+            ret = SEC_E_INVALID_HANDLE;
+    }
+    else
+        ret = SEC_E_INVALID_HANDLE;
+    return ret;
+}
+
+SECURITY_STATUS SEC_ENTRY AddCredentialsW(PCredHandle hCredentials,
+ SEC_WCHAR *pszPrincipal, SEC_WCHAR *pszPackage, unsigned long fCredentialUse,
+ void *pAuthData, SEC_GET_KEY_FN pGetKeyFn, void *pvGetKeyArgument,
+ PTimeStamp ptsExpiry)
+{
+    SECURITY_STATUS ret;
+
+    TRACE("%p %s %s %ld %p %p %p %p\n", hCredentials, debugstr_w(pszPrincipal),
+     debugstr_w(pszPackage), fCredentialUse, pAuthData, pGetKeyFn,
+     pvGetKeyArgument, ptsExpiry);
+    if (hCredentials)
+    {
+        SecurePackage *package = (SecurePackage *)hCredentials->dwUpper;
+        PCredHandle cred = (PCtxtHandle)hCredentials->dwLower;
+
+        if (package && package->provider)
+        {
+            if (package->provider->fnTableW.AddCredentialsW)
+                ret = package->provider->fnTableW.AddCredentialsW(
+                 cred, pszPrincipal, pszPackage, fCredentialUse, pAuthData,
+                 pGetKeyFn, pvGetKeyArgument, ptsExpiry);
+            else
+                ret = SEC_E_UNSUPPORTED_FUNCTION;
+        }
+        else
+            ret = SEC_E_INVALID_HANDLE;
+    }
+    else
+        ret = SEC_E_INVALID_HANDLE;
+    return ret;
+}
+
+SECURITY_STATUS SEC_ENTRY QuerySecurityContextToken(PCtxtHandle phContext,
+ HANDLE *phToken)
+{
+    SECURITY_STATUS ret;
+
+    TRACE("%p %p\n", phContext, phToken);
+    if (phContext)
+    {
+        SecurePackage *package = (SecurePackage *)phContext->dwUpper;
+        PCtxtHandle ctxt = (PCtxtHandle)phContext->dwLower;
+
+        if (package && package->provider)
+        {
+            if (package->provider->fnTableW.QuerySecurityContextToken)
+                ret = package->provider->fnTableW.QuerySecurityContextToken(
+                 ctxt, phToken);
+            else
+                ret = SEC_E_UNSUPPORTED_FUNCTION;
+        }
+        else
+            ret = SEC_E_INVALID_HANDLE;
+    }
+    else
+        ret = SEC_E_INVALID_HANDLE;
+    return ret;
+}
+
+SECURITY_STATUS SEC_ENTRY EncryptMessage(PCtxtHandle phContext, ULONG fQOP,
+ PSecBufferDesc pMessage, ULONG MessageSeqNo)
+{
+    SECURITY_STATUS ret;
+
+    TRACE("%p %ld %p %ld\n", phContext, fQOP, pMessage, MessageSeqNo);
+    if (phContext)
+    {
+        SecurePackage *package = (SecurePackage *)phContext->dwUpper;
+        PCtxtHandle ctxt = (PCtxtHandle)phContext->dwLower;
+
+        if (package && package->provider)
+        {
+            if (package->provider->fnTableW.EncryptMessage)
+                ret = package->provider->fnTableW.EncryptMessage(
+                 ctxt, fQOP, pMessage, MessageSeqNo);
+            else
+                ret = SEC_E_UNSUPPORTED_FUNCTION;
+        }
+        else
+            ret = SEC_E_INVALID_HANDLE;
+    }
+    else
+        ret = SEC_E_INVALID_HANDLE;
+    return ret;
+}
+
+SECURITY_STATUS SEC_ENTRY DecryptMessage(PCtxtHandle phContext,
+ PSecBufferDesc pMessage, ULONG MessageSeqNo, PULONG pfQOP)
+{
+    SECURITY_STATUS ret;
+
+    TRACE("%p %p %ld %p\n", phContext, pMessage, MessageSeqNo, pfQOP);
+    if (phContext)
+    {
+        SecurePackage *package = (SecurePackage *)phContext->dwUpper;
+        PCtxtHandle ctxt = (PCtxtHandle)phContext->dwLower;
+
+        if (package && package->provider)
+        {
+            if (package->provider->fnTableW.DecryptMessage)
+                ret = package->provider->fnTableW.DecryptMessage(
+                 ctxt, pMessage, MessageSeqNo, pfQOP);
+            else
+                ret = SEC_E_UNSUPPORTED_FUNCTION;
+        }
+        else
+            ret = SEC_E_INVALID_HANDLE;
+    }
+    else
+        ret = SEC_E_INVALID_HANDLE;
+    return ret;
+}
+
+SECURITY_STATUS SEC_ENTRY SetContextAttributesA(PCtxtHandle phContext,
+ unsigned long ulAttribute, void *pBuffer, unsigned long cbBuffer)
+{
+    SECURITY_STATUS ret;
+
+    TRACE("%p %ld %p %ld\n", phContext, ulAttribute, pBuffer, cbBuffer);
+    if (phContext)
+    {
+        SecurePackage *package = (SecurePackage *)phContext->dwUpper;
+        PCtxtHandle ctxt = (PCtxtHandle)phContext->dwLower;
+
+        if (package && package->provider)
+        {
+            if (package->provider->fnTableA.SetContextAttributesA)
+                ret = package->provider->fnTableA.SetContextAttributesA(
+                 ctxt, ulAttribute, pBuffer, cbBuffer);
+            else
+                ret = SEC_E_UNSUPPORTED_FUNCTION;
+        }
+        else
+            ret = SEC_E_INVALID_HANDLE;
+    }
+    else
+        ret = SEC_E_INVALID_HANDLE;
+    return ret;
+}
+
+SECURITY_STATUS SEC_ENTRY SetContextAttributesW(PCtxtHandle phContext,
+ unsigned long ulAttribute, void *pBuffer, unsigned long cbBuffer)
+{
+    SECURITY_STATUS ret;
+
+    TRACE("%p %ld %p %ld\n", phContext, ulAttribute, pBuffer, cbBuffer);
+    if (phContext)
+    {
+        SecurePackage *package = (SecurePackage *)phContext->dwUpper;
+        PCtxtHandle ctxt = (PCtxtHandle)phContext->dwLower;
+
+        if (package && package->provider)
+        {
+            if (package->provider->fnTableW.SetContextAttributesW)
+                ret = package->provider->fnTableW.SetContextAttributesW(
+                 ctxt, ulAttribute, pBuffer, cbBuffer);
+            else
+                ret = SEC_E_UNSUPPORTED_FUNCTION;
+        }
+        else
+            ret = SEC_E_INVALID_HANDLE;
+    }
+    else
+        ret = SEC_E_INVALID_HANDLE;
+    return ret;
+}