secur32: Added real funcionality to the NTLM provider, try 5

Kai Blin blin at gmx.net
Sat Aug 27 11:00:27 CDT 2005


NTLM will authenticate clients against ntlm_auth. If it's a samba3
ntlm_auth, this requires access to the winbindd privileged pipe. Client
side authentication in samba3 will work, it's broken in samba4's
ntlm_auth, but will work there once ntlm_auth is fixed. Will also zero
the password that is stored in the helper once it's not neededed
anymore. Also includes a version check of ntlm_auth, with the nice
effect of not registering when ntlm_auth isn't present.

Changelog:
Kai Blin  <blin at gmx.net>
Added real functionality to the NTLM security provider.

-- 
Kai Blin, (blin at gmx dot net)
Coward, n.:
	One who in a perilous emergency thinks with his legs.
		-- Ambrose Bierce, "The Devil's Dictionary"
-------------- next part --------------
Index: dlls/secur32/ntlm.c
===================================================================
RCS file: /home/wine/wine/dlls/secur32/ntlm.c,v
retrieving revision 1.1
diff -u -3 -r1.1 ntlm.c
--- dlls/secur32/ntlm.c	17 Aug 2005 09:52:30 -0000	1.1
+++ dlls/secur32/ntlm.c	27 Aug 2005 15:25:24 -0000
@@ -16,22 +16,25 @@
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  *
  * This file implements the NTLM security provider.
- * FIXME: So far, this beast doesn't do anything.
  */
 #include <assert.h>
 #include <stdarg.h>
 #include "windef.h"
 #include "winbase.h"
+#include "winnls.h"
+#include "rpc.h"
 #include "sspi.h"
+#include "lm.h"
 #include "secur32_priv.h"
 #include "wine/debug.h"
 
 WINE_DEFAULT_DEBUG_CHANNEL(secur32);
 
+#define NTLM_MAX_BUF 2010
+
 static char ntlm_name_A[] = "NTLM";
 static WCHAR ntlm_name_W[] = {'N', 'T', 'L', 'M', 0};
 
-
 /***********************************************************************
  *              QueryCredentialsAttributesA
  */
@@ -74,27 +77,197 @@
     return ret;
 }
 
-static SECURITY_STATUS ntlm_AcquireCredentialsHandle(ULONG fCredentialsUse,
-        PCredHandle phCredential, PTimeStamp ptsExpiry)
+/***********************************************************************
+ *              AcquireCredentialsHandleW
+ */
+static SECURITY_STATUS SEC_ENTRY ntlm_AcquireCredentialsHandleW(
+ SEC_WCHAR *pszPrincipal, SEC_WCHAR *pszPackage, ULONG fCredentialUse,
+ PLUID pLogonID, PVOID pAuthData, SEC_GET_KEY_FN pGetKeyFn,
+ PVOID pGetKeyArgument, PCredHandle phCredential, PTimeStamp ptsExpiry)
 {
     SECURITY_STATUS ret;
+    TRACE("(%s, %s, 0x%08lx, %p, %p, %p, %p, %p, %p)\n",
+     debugstr_w(pszPrincipal), debugstr_w(pszPackage), fCredentialUse,
+     pLogonID, pAuthData, pGetKeyFn, pGetKeyArgument, phCredential, ptsExpiry);
 
-    if(fCredentialsUse == SECPKG_CRED_BOTH)
-    {
-        ret = SEC_E_NO_CREDENTIALS;
-    }
-    else
-    {
-        /* Ok, just store the direction like schannel does for now.
-         * FIXME: This should probably do something useful later on
-         */
-        phCredential->dwUpper = fCredentialsUse;
-        phCredential->dwLower = 0;
-        /* Same here, shamelessly stolen from schannel.c */
-        if (ptsExpiry)
-            ptsExpiry->QuadPart = 0;
-        ret = SEC_E_OK;
+    PNegoHelper helper = NULL;
+    
+    SEC_CHAR *client_user_arg = NULL;
+    SEC_CHAR *client_domain_arg = NULL;
+    SEC_WCHAR *username = NULL, *domain = NULL;
+    
+    SEC_CHAR *client_argv[5];
+    SEC_CHAR *server_argv[] = { "ntlm_auth",
+        "--helper-protocol=squid-2.5-ntlmssp",
+        NULL };
+
+    switch(fCredentialUse)
+    {
+        case SECPKG_CRED_INBOUND:
+            if( (ret = fork_helper(&helper, "ntlm_auth", server_argv)) !=
+                    SEC_E_OK)
+            {
+                phCredential = NULL;
+                break;
+            }
+            else
+            {
+                helper->mode = NTLM_SERVER;
+                phCredential->dwUpper = fCredentialUse;
+                phCredential->dwLower = (DWORD)helper;
+            }
+            ret = SEC_E_OK;
+            break;
+        case SECPKG_CRED_OUTBOUND:
+            {
+                static const char username_arg[] = "--username=";
+                static const char domain_arg[] = "--domain=";
+                int unixcp_size;
+
+                if(pAuthData == NULL)
+                {
+                    LPWKSTA_USER_INFO_1 ui = NULL;
+                    NET_API_STATUS status;
+
+                    if((status = NetWkstaUserGetInfo(NULL, 1, (LPBYTE *)&ui)) !=
+                            NERR_Success)
+                    {
+                        ret = SEC_E_NO_CREDENTIALS;
+                        phCredential = NULL;
+                        break;
+                    }
+                    
+                    if(ui != NULL)
+                    {
+                        username = HeapAlloc(GetProcessHeap(), 0, 
+                                (lstrlenW(ui->wkui1_username)+1) * 
+                                sizeof(SEC_WCHAR));
+                        lstrcpyW(username, ui->wkui1_username);
+                        
+                        /* same for the domain */
+                        domain = HeapAlloc(GetProcessHeap(), 0, 
+                                (lstrlenW(ui->wkui1_logon_domain)+1) * 
+                                sizeof(SEC_WCHAR));
+                    }
+                    else
+                    {
+                        ret = SEC_E_NO_CREDENTIALS;
+                        phCredential = NULL;
+                        break;
+                    }
+                
+                }
+                else
+                {
+                    PSEC_WINNT_AUTH_IDENTITY_W auth_data = 
+                        (PSEC_WINNT_AUTH_IDENTITY_W)pAuthData;
+
+                    if(auth_data->UserLength != 0)
+                    {
+                        /* Get username and domain from pAuthData */
+                        username = HeapAlloc(GetProcessHeap(), 0, 
+                                (auth_data->UserLength + 1) * sizeof(SEC_WCHAR));
+                        lstrcpyW(username, auth_data->User);
+                    }
+                    else
+                    {
+                        ret = SEC_E_NO_CREDENTIALS;
+                        phCredential = NULL;
+                        break;
+                    }
+                    if(auth_data->DomainLength != 0)
+                    {
+                        domain = HeapAlloc(GetProcessHeap(), 0,
+                                (auth_data->DomainLength + 1) * sizeof(SEC_WCHAR));
+                        lstrcpyW(domain, auth_data->Domain);
+
+                    }
+                    else
+                    {
+                        ret = SEC_E_NO_CREDENTIALS;
+                        phCredential = NULL;
+                        break;
+                    }
+
+                }
+                TRACE("Username is %s\n", debugstr_w(username));
+                unixcp_size =  WideCharToMultiByte(CP_UNIXCP, WC_NO_BEST_FIT_CHARS,
+                        username, -1, NULL, 0, NULL, NULL) + sizeof(username_arg);
+                client_user_arg = HeapAlloc(GetProcessHeap(), 0, unixcp_size);
+                lstrcpyA(client_user_arg, username_arg);
+                WideCharToMultiByte(CP_UNIXCP, WC_NO_BEST_FIT_CHARS, username, -1,
+                        client_user_arg + sizeof(username_arg) - 1, 
+                        unixcp_size - sizeof(username_arg) + 1, NULL, NULL);
+
+                TRACE("Domain name is %s\n", debugstr_w(domain));
+                unixcp_size = WideCharToMultiByte(CP_UNIXCP, WC_NO_BEST_FIT_CHARS,
+                        domain, -1, NULL, 0,  NULL, NULL) + sizeof(domain_arg);
+                client_domain_arg = HeapAlloc(GetProcessHeap(), 0, unixcp_size);
+                lstrcpyA(client_domain_arg, domain_arg);
+                WideCharToMultiByte(CP_UNIXCP, WC_NO_BEST_FIT_CHARS, domain,
+                        -1, client_domain_arg + sizeof(domain_arg) - 1, 
+                        unixcp_size - sizeof(domain) + 1, NULL, NULL);
+
+                client_argv[0] = "ntlm_auth";
+                client_argv[1] = "--helper-protocol=ntlmssp-client-1";
+                client_argv[2] = client_user_arg;
+                client_argv[3] = client_domain_arg;
+                client_argv[4] = NULL;
+
+                if((ret = fork_helper(&helper, "ntlm_auth", client_argv)) != 
+                        SEC_E_OK)
+                {
+                    phCredential = NULL;
+                    break;
+                }
+                else
+                {
+                    helper->mode = NTLM_CLIENT;
+
+                    if(pAuthData != NULL)
+                    {
+                        PSEC_WINNT_AUTH_IDENTITY_W auth_data = 
+                           (PSEC_WINNT_AUTH_IDENTITY_W)pAuthData;
+
+                        if(auth_data->PasswordLength != 0)
+                        {
+                            helper->pwlen = WideCharToMultiByte(CP_UNIXCP, 
+                                WC_NO_BEST_FIT_CHARS, auth_data->Password, 
+                                auth_data->PasswordLength+1, NULL, 0, NULL, NULL);
+                        
+                            helper->password = HeapAlloc(GetProcessHeap(), 0, 
+                                    helper->pwlen);
+
+                            WideCharToMultiByte(CP_UNIXCP, WC_NO_BEST_FIT_CHARS,
+                                auth_data->Password, auth_data->PasswordLength+1,
+                                helper->password, helper->pwlen, NULL, NULL);
+                        }
+                    }
+           
+                    phCredential->dwUpper = fCredentialUse;
+                    phCredential->dwLower = (DWORD)helper;
+                    TRACE("ACH phCredential->dwUpper: 0x%08lx, dwLower: 0x%08lx\n",
+                            phCredential->dwUpper, phCredential->dwLower);
+                }
+                ret = SEC_E_OK;
+                break;
+            }
+        case SECPKG_CRED_BOTH:
+            FIXME("AcquireCredentialsHandle: SECPKG_CRED_BOTH stub\n");
+            ret = SEC_E_UNSUPPORTED_FUNCTION;
+            phCredential = NULL;
+            break;
+        default:
+            phCredential = NULL;
+            ret = SEC_E_UNKNOWN_CREDENTIALS;
     }
+    
+
+    HeapFree(GetProcessHeap(), 0, client_user_arg);
+    HeapFree(GetProcessHeap(), 0, client_domain_arg);
+    HeapFree(GetProcessHeap(), 0, username);
+    HeapFree(GetProcessHeap(), 0, domain);
+
     return ret;
 }
 
@@ -106,33 +279,113 @@
  PLUID pLogonID, PVOID pAuthData, SEC_GET_KEY_FN pGetKeyFn,
  PVOID pGetKeyArgument, PCredHandle phCredential, PTimeStamp ptsExpiry)
 {
+    SECURITY_STATUS ret;
     TRACE("(%s, %s, 0x%08lx, %p, %p, %p, %p, %p, %p)\n",
      debugstr_a(pszPrincipal), debugstr_a(pszPackage), fCredentialUse,
      pLogonID, pAuthData, pGetKeyFn, pGetKeyArgument, phCredential, ptsExpiry);
-    return ntlm_AcquireCredentialsHandle(fCredentialUse, phCredential,
-            ptsExpiry);
-}
 
-/***********************************************************************
- *              AcquireCredentialsHandleW
- */
-static SECURITY_STATUS SEC_ENTRY ntlm_AcquireCredentialsHandleW(
- SEC_WCHAR *pszPrincipal, SEC_WCHAR *pszPackage, ULONG fCredentialUse,
- PLUID pLogonID, PVOID pAuthData, SEC_GET_KEY_FN pGetKeyFn,
- PVOID pGetKeyArgument, PCredHandle phCredential, PTimeStamp ptsExpiry)
-{
-    TRACE("(%s, %s, 0x%08lx, %p, %p, %p, %p, %p, %p)\n",
-     debugstr_w(pszPrincipal), debugstr_w(pszPackage), fCredentialUse,
-     pLogonID, pAuthData, pGetKeyFn, pGetKeyArgument, phCredential, ptsExpiry);
-    return ntlm_AcquireCredentialsHandle(fCredentialUse, phCredential,
+    int user_sizeW, domain_sizeW, passwd_sizeW;
+    
+    SEC_WCHAR *user = NULL, *domain = NULL, *passwd = NULL, *package = NULL;
+    
+    PSEC_WINNT_AUTH_IDENTITY_W pAuthDataW = NULL;
+    PSEC_WINNT_AUTH_IDENTITY_A identity  = NULL;
+    
+    if(pszPackage != NULL)
+    {
+        int package_sizeW = MultiByteToWideChar(CP_ACP, 0, pszPackage, -1,
+                NULL, 0);
+
+        package = HeapAlloc(GetProcessHeap(), 0, package_sizeW * 
+                sizeof(SEC_WCHAR));
+        MultiByteToWideChar(CP_ACP, 0, pszPackage, -1, package, package_sizeW);
+    }
+
+    
+    if(pAuthData != NULL)
+    {
+        identity = (PSEC_WINNT_AUTH_IDENTITY_A)pAuthData;
+        
+        if(identity->Flags == SEC_WINNT_AUTH_IDENTITY_ANSI)
+        {
+            pAuthDataW = HeapAlloc(GetProcessHeap(), 0, 
+                    sizeof(SEC_WINNT_AUTH_IDENTITY_W));
+
+            if(identity->UserLength != 0)
+            {
+                user_sizeW = MultiByteToWideChar(CP_ACP, 0, identity->User, 
+                    identity->UserLength+1, NULL, 0);
+                user = HeapAlloc(GetProcessHeap(), 0, user_sizeW * 
+                        sizeof(SEC_WCHAR));
+                MultiByteToWideChar(CP_ACP, 0, identity->User, 
+                    identity->UserLength+1, user, user_sizeW);
+            }
+            else
+            {
+                user_sizeW = 0;
+            }
+             
+            if(identity->DomainLength != 0)
+            {
+                domain_sizeW = MultiByteToWideChar(CP_ACP, 0, identity->Domain,
+                    identity->DomainLength+1, NULL, 0);
+                domain = HeapAlloc(GetProcessHeap(), 0, domain_sizeW 
+                    * sizeof(SEC_WCHAR));
+                MultiByteToWideChar(CP_ACP, 0, identity->Domain, 
+                    identity->DomainLength+1, domain, domain_sizeW);
+            }
+            else
+            {
+                domain_sizeW = 0;
+            }
+
+            if(identity->PasswordLength != 0)
+            {
+                passwd_sizeW = MultiByteToWideChar(CP_ACP, 0, identity->Password, 
+                    identity->PasswordLength+1, NULL, 0);
+                passwd = HeapAlloc(GetProcessHeap(), 0, passwd_sizeW
+                    * sizeof(SEC_WCHAR));
+                MultiByteToWideChar(CP_ACP, 0, identity->Password,
+                    identity->PasswordLength, passwd, passwd_sizeW);
+            }
+            else
+            {
+                passwd_sizeW = 0;
+            }
+            
+            pAuthDataW->Flags = SEC_WINNT_AUTH_IDENTITY_UNICODE;
+            pAuthDataW->User = user;
+            pAuthDataW->UserLength = user_sizeW;
+            pAuthDataW->Domain = domain;
+            pAuthDataW->DomainLength = domain_sizeW;
+            pAuthDataW->Password = passwd;
+            pAuthDataW->PasswordLength = passwd_sizeW;
+        }
+        else
+        {
+            pAuthDataW = (PSEC_WINNT_AUTH_IDENTITY_W)identity;
+        }
+    }       
+    
+    ret = ntlm_AcquireCredentialsHandleW(NULL, package, fCredentialUse, 
+            pLogonID, pAuthDataW, pGetKeyFn, pGetKeyArgument, phCredential,
             ptsExpiry);
+    
+    HeapFree(GetProcessHeap(), 0, package);
+    HeapFree(GetProcessHeap(), 0, user);
+    HeapFree(GetProcessHeap(), 0, domain);
+    HeapFree(GetProcessHeap(), 0, passwd);
+    if(pAuthDataW != (PSEC_WINNT_AUTH_IDENTITY_W)identity)
+        HeapFree(GetProcessHeap(), 0, pAuthDataW);
+    
+    return ret;
 }
 
 /***********************************************************************
- *              InitializeSecurityContextA
+ *              InitializeSecurityContextW
  */
-static SECURITY_STATUS SEC_ENTRY ntlm_InitializeSecurityContextA(
- PCredHandle phCredential, PCtxtHandle phContext, SEC_CHAR *pszTargetName, 
+static SECURITY_STATUS SEC_ENTRY ntlm_InitializeSecurityContextW(
+ PCredHandle phCredential, PCtxtHandle phContext, SEC_WCHAR *pszTargetName, 
  ULONG fContextReq, ULONG Reserved1, ULONG TargetDataRep, 
  PSecBufferDesc pInput, ULONG Reserved2, PCtxtHandle phNewContext, 
  PSecBufferDesc pOutput, ULONG *pfContextAttr, PTimeStamp ptsExpiry)
@@ -140,10 +393,325 @@
     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,
+     debugstr_w(pszTargetName), fContextReq, Reserved1, TargetDataRep, pInput,
      Reserved1, phNewContext, pOutput, pfContextAttr, ptsExpiry);
+
     if(phCredential){
-        ret = SEC_E_UNSUPPORTED_FUNCTION;
+        /* As the server side of sspi never calls this, make sure that 
+         * the handler is a client handler.
+         */
+        PNegoHelper helper = (PNegoHelper)phCredential->dwLower;
+        ULONG ctxt_attr = 0;
+        if(helper->mode == NTLM_CLIENT)
+        {
+            /****************************************
+             * When communicating with the client, there can be the 
+             * following reply packets:
+             * YR <base64 blob>         should be sent to the server
+             * PW                       should be sent back to helper with
+             *                          base64 encoded password
+             * AF <base64 blob>         client is done, blob should be 
+             *                          sent to server with KK prefixed
+             * BH <char reason>         something broke
+             */
+            BOOL first = FALSE;
+            /* The squid cache size is 2010 chars, and that's what ntlm_auth uses */
+            unsigned char* buffer = HeapAlloc(GetProcessHeap(), 0, 
+                    sizeof(char) * NTLM_MAX_BUF);
+            PBYTE bin = HeapAlloc(GetProcessHeap(), 0, sizeof(BYTE) * 
+                    NTLM_MAX_BUF);
+            int buffer_len, bin_len, max_len = NTLM_MAX_BUF;
+            
+            if((phContext == NULL) && (pInput == NULL))
+                first = TRUE;
+            if (pszTargetName)
+            {
+                TRACE("According to a MS whitepaper pszTargetName is ignored.\n");
+            }
+            /* Handle all the flags */
+            if(fContextReq & ISC_REQ_ALLOCATE_MEMORY)
+            {
+                FIXME("InitializeSecurityContext(): ISC_REQ_ALLOCATE_MEMORY stub\n");
+            }
+            if(fContextReq & ISC_REQ_CONFIDENTIALITY)
+            {
+                FIXME("InitializeSecurityContext(): ISC_REQ_CONFIDENTIALITY stub\n");
+            }
+            if(fContextReq & ISC_REQ_CONNECTION)
+            {
+                /* This is default, so we'll enable it */
+                ctxt_attr |= ISC_RET_CONNECTION;
+            }
+            if(fContextReq & ISC_REQ_EXTENDED_ERROR)
+            {
+                FIXME("InitializeSecurityContext(): ISC_REQ_EXTENDED_ERROR stub\n");
+            }
+            if(fContextReq & ISC_REQ_INTEGRITY)
+            {
+                FIXME("InitializeSecurityContext(): ISC_REQ_INTEGRITY stub\n");
+            }
+            if(fContextReq & ISC_REQ_MUTUAL_AUTH)
+            {
+                FIXME("InitializeSecurityContext(): ISC_REQ_MUTUAL_AUTH stub\n");
+            }
+            if(fContextReq & ISC_REQ_REPLAY_DETECT)
+            {
+                FIXME("InitializeSecurityContext(): ISC_REQ_REPLAY_DETECT stub\n");
+            }
+            if(fContextReq & ISC_REQ_SEQUENCE_DETECT)
+            {
+                FIXME("InitializeSecurityContext(): ISC_REQ_SEQUENCE_DETECT stub\n");
+            }
+            if(fContextReq & ISC_REQ_STREAM)
+            {
+                FIXME("InitializeSecurityContext(): ISC_REQ_STREAM stub\n");
+            }
+            /* Done with the flags */
+            if(TargetDataRep == SECURITY_NETWORK_DREP){
+                FIXME("Don't know how to do SECURITY_NETWORK_DREP\n");
+                HeapFree(GetProcessHeap(), 0, buffer);
+                HeapFree(GetProcessHeap(), 0, bin);
+                return SEC_E_UNSUPPORTED_FUNCTION;
+            }
+
+            if(first)
+            {
+                TRACE("First time in ISC()\n");
+                /* Request a challenge request from ntlm_auth */
+                if(helper->password == NULL)
+                {
+                    FIXME("Using empty password for now.\n");
+                    lstrcpynA(buffer, "PW AA==", max_len-1);
+                }
+                else
+                {
+                    lstrcpynA(buffer, "PW ", max_len-1);
+                    if((ret = encodeBase64(helper->password, 
+                                helper->pwlen-2, buffer+3,
+                                max_len-3, &buffer_len)) != SEC_E_OK)
+                    {
+                        TRACE("Deleting password!\n");
+                        memset(helper->password, 0, helper->pwlen-2);
+                        HeapFree(GetProcessHeap(), 0, helper->password);
+                        HeapFree(GetProcessHeap(), 0, buffer);
+                        HeapFree(GetProcessHeap(), 0, bin);
+                        return ret;
+                    }
+                                    
+                }
+
+                TRACE("Sending to helper: %s\n", debugstr_a(buffer));
+                /* workaround for ntlm_auth v4 bug */
+                helper->first_time = 1;
+                if((ret = run_helper(helper, buffer, max_len, &buffer_len)) != 
+                    SEC_E_OK)
+                {
+                    HeapFree(GetProcessHeap(), 0, buffer);
+                    HeapFree(GetProcessHeap(), 0, bin);
+                    return ret;
+                }
+                TRACE("Helper returned %s\n", debugstr_a(buffer));
+                helper->first_time = 0;
+                /* and restore to ntlm_auth v4 behaviour */
+
+                lstrcpynA(buffer, "YR", max_len-1);
+
+                if((ret = run_helper(helper, buffer, max_len, &buffer_len)) !=
+                        SEC_E_OK)
+                {
+                    HeapFree(GetProcessHeap(), 0, buffer);
+                    HeapFree(GetProcessHeap(), 0, bin);
+                    return ret;
+                }
+
+                TRACE("%s\n", buffer);
+                 
+                if(strncmp(buffer, "YR ", 3) != 0)
+                {
+                    
+                    /* Something borked */
+                    TRACE("Helper returned %c%c\n", buffer[0], buffer[1]);
+                    HeapFree(GetProcessHeap(), 0, buffer);
+                    HeapFree(GetProcessHeap(), 0, bin);
+                    return SEC_E_INTERNAL_ERROR;
+                }
+                if((ret = decodeBase64(buffer+3, buffer_len-3, bin, 
+                                max_len-1, &bin_len)) != SEC_E_OK)
+                {
+                    HeapFree(GetProcessHeap(), 0, buffer);
+                    HeapFree(GetProcessHeap(), 0, bin);
+                    return ret;
+                }
+                
+                /* put the decoded client blob into the out buffer */
+                if(pOutput == NULL){
+                    TRACE("pOutput is NULL\n");
+                    HeapFree(GetProcessHeap(), 0, buffer);
+                    HeapFree(GetProcessHeap(), 0, bin);
+                    return SEC_E_INSUFFICIENT_MEMORY;
+                }
+
+                if(pOutput->cBuffers < 1)
+                {
+                    TRACE("pOutput->cBuffers is %ld\n", pOutput->cBuffers);
+                    HeapFree(GetProcessHeap(), 0, buffer);
+                    HeapFree(GetProcessHeap(), 0, bin);
+                    return SEC_E_INSUFFICIENT_MEMORY;
+                }
+                pOutput->pBuffers[0].cbBuffer = bin_len;
+                pOutput->pBuffers[0].BufferType = SECBUFFER_DATA;
+                memcpy(pOutput->pBuffers[0].pvBuffer, bin, bin_len);
+                
+                ret = SEC_I_CONTINUE_NEEDED;
+            }
+            else
+            {
+                /* handle second call here */
+                /* encode server data to base64 */
+                if(pInput == NULL)
+                {
+                    HeapFree(GetProcessHeap(), 0, buffer);
+                    HeapFree(GetProcessHeap(), 0, bin);
+                    return SEC_E_INCOMPLETE_MESSAGE;
+                }
+                
+                if(pInput->cBuffers < 1)
+                {
+                    HeapFree(GetProcessHeap(), 0, buffer);
+                    HeapFree(GetProcessHeap(), 0, bin);
+                    return SEC_E_INCOMPLETE_MESSAGE;
+                }
+
+                if(pInput->pBuffers[0].cbBuffer > max_len)
+                {
+                    TRACE("pInput->pBuffers[0].cbBuffer is: %ld\n", 
+                            pInput->pBuffers[0].cbBuffer);
+                    HeapFree(GetProcessHeap(), 0, buffer);
+                    HeapFree(GetProcessHeap(), 0, bin);
+                    return SEC_E_INVALID_TOKEN;
+                }
+                else
+                    bin_len = pInput->pBuffers[0].cbBuffer;
+
+                memcpy(bin, pInput->pBuffers[0].pvBuffer, bin_len);
+
+                lstrcpynA(buffer, "TT ", max_len-1);
+
+                if((ret = encodeBase64(bin, bin_len, buffer+3,
+                                max_len-3, &buffer_len)) != SEC_E_OK)
+                {
+                    HeapFree(GetProcessHeap(), 0, buffer);
+                    HeapFree(GetProcessHeap(), 0, bin);
+                    return ret;
+                }
+
+                TRACE("Server sent: %s\n", debugstr_a(buffer));
+
+                /* send TT base64 blob to ntlm_auth */
+                if((ret = run_helper(helper, buffer, max_len, &buffer_len)) !=
+                        SEC_E_OK)
+                {
+                    HeapFree(GetProcessHeap(), 0, buffer);
+                    HeapFree(GetProcessHeap(), 0, bin);
+                    return ret;
+                }
+                
+                TRACE("Helper replied: %s\n", debugstr_a(buffer));
+                
+                /* return should be AF base64 blob or KK base64 blob
+                 * it could be PW, though, which needs to be handled first */
+                if(strncmp(buffer, "PW", 2) == 0)
+                {   
+                    if(helper->password == NULL)
+                    {
+                        FIXME("Using empty password for now.\n");
+                        lstrcpynA(buffer, "PW AA==", max_len-1);
+                    }
+                    else
+                    {
+                        lstrcpynA(buffer, "PW ", max_len-1);
+                        if((ret = encodeBase64(helper->password, 
+                                    helper->pwlen-2, buffer+3,
+                                    max_len-3, &buffer_len)) != SEC_E_OK)
+                        {
+                            memset(helper->password, 0, helper->pwlen-2);
+                            HeapFree(GetProcessHeap(), 0, helper->password);
+                            HeapFree(GetProcessHeap(), 0, buffer);
+                            HeapFree(GetProcessHeap(), 0, bin);
+                            return ret;
+                        }
+                    
+                    }
+
+                    TRACE("Sending to helper: %s\n", debugstr_a(buffer));
+                    
+                    if((ret = run_helper(helper, buffer, max_len, &buffer_len)) != 
+                        SEC_E_OK)
+                    {
+                        HeapFree(GetProcessHeap(), 0, buffer);
+                        HeapFree(GetProcessHeap(), 0, bin);
+                        return ret;
+                    }
+                    TRACE("Helper returned %s\n", debugstr_a(buffer));
+
+                }
+                
+                if( (strncmp(buffer, "KK ", 3) != 0) && 
+                        (strncmp(buffer, "AF ", 3) !=0))
+                {
+                    TRACE("Helper returned %c%c\n", buffer[0], buffer[1]);
+                    HeapFree(GetProcessHeap(), 0, buffer);
+                    HeapFree(GetProcessHeap(), 0, bin);
+                    return SEC_E_INVALID_TOKEN;
+                }
+
+                /* decode the blob and send it to server */
+                if((ret = decodeBase64(buffer+3, buffer_len-3, bin, max_len, 
+                                &bin_len)) != SEC_E_OK)
+                {
+                    HeapFree(GetProcessHeap(), 0, buffer);
+                    HeapFree(GetProcessHeap(), 0, bin);
+                    return ret;
+                }
+
+                if(pOutput == NULL)
+                {
+                    HeapFree(GetProcessHeap(), 0, buffer);
+                    HeapFree(GetProcessHeap(), 0, bin);
+                    return SEC_E_INSUFFICIENT_MEMORY;
+                }
+
+                if(pOutput->cBuffers < 1)
+                {
+                    HeapFree(GetProcessHeap(), 0, buffer);
+                    HeapFree(GetProcessHeap(), 0, bin);
+                    return SEC_E_INSUFFICIENT_MEMORY;
+                }
+
+                pOutput->pBuffers[0].cbBuffer = bin_len;
+                pOutput->pBuffers[0].BufferType = SECBUFFER_DATA;
+                memcpy(pOutput->pBuffers[0].pvBuffer, bin, bin_len);
+                
+                ret = SEC_E_OK;
+                phNewContext->dwUpper = ctxt_attr;
+                phNewContext->dwLower = ret;
+
+            }
+            HeapFree(GetProcessHeap(), 0, buffer);
+            HeapFree(GetProcessHeap(), 0, bin);
+            if(ret != SEC_I_CONTINUE_NEEDED)
+            {
+                TRACE("Deleting password!\n");
+                memset(helper->password, 0, helper->pwlen-2);
+                HeapFree(GetProcessHeap(), 0, helper->password);
+            }
+
+        }
+        else
+        {
+            ret = SEC_E_INVALID_HANDLE;
+            TRACE("Helper mode = %d\n", helper->mode);
+        }
     }
     else
     {
@@ -153,10 +721,10 @@
 }
 
 /***********************************************************************
- *              InitializeSecurityContextW
+ *              InitializeSecurityContextA
  */
-static SECURITY_STATUS SEC_ENTRY ntlm_InitializeSecurityContextW(
- PCredHandle phCredential, PCtxtHandle phContext, SEC_WCHAR *pszTargetName,
+static SECURITY_STATUS SEC_ENTRY ntlm_InitializeSecurityContextA(
+ PCredHandle phCredential, PCtxtHandle phContext, SEC_CHAR *pszTargetName,
  ULONG fContextReq, ULONG Reserved1, ULONG TargetDataRep, 
  PSecBufferDesc pInput,ULONG Reserved2, PCtxtHandle phNewContext, 
  PSecBufferDesc pOutput, ULONG *pfContextAttr, PTimeStamp ptsExpiry)
@@ -164,11 +732,27 @@
     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,
+     debugstr_a(pszTargetName), fContextReq, Reserved1, TargetDataRep, pInput,
      Reserved1, phNewContext, pOutput, pfContextAttr, ptsExpiry);
+    
     if (phCredential)
     {
-        ret = SEC_E_UNSUPPORTED_FUNCTION;
+        SEC_WCHAR *target = NULL;
+        if(pszTargetName != NULL)
+        {
+            int target_size = MultiByteToWideChar(CP_ACP, 0, pszTargetName, 
+                strlen(pszTargetName)+1, NULL, 0);
+            target = HeapAlloc(GetProcessHeap(), 0, target_size * 
+                    sizeof(SEC_WCHAR));
+            MultiByteToWideChar(CP_ACP, 0, pszTargetName, strlen(pszTargetName)+1,
+                target, target_size);
+        }
+        
+        ret = ntlm_InitializeSecurityContextW(phCredential, phContext, target, 
+                fContextReq, Reserved1, TargetDataRep, pInput, Reserved2,
+                phNewContext, pOutput, pfContextAttr, ptsExpiry);
+        
+        HeapFree(GetProcessHeap(), 0, target);
     }
     else
     {
@@ -192,7 +776,232 @@
      ptsExpiry);
     if (phCredential)
     {
-        ret = SEC_E_UNSUPPORTED_FUNCTION;
+        PNegoHelper helper = (PNegoHelper)phCredential->dwLower;
+        /* Max size of input data is 2010 byte, as that's the maximum size 
+         * ntlm_auth will handle*/
+        unsigned char *buffer = HeapAlloc(GetProcessHeap(), 0, 
+                sizeof(char) * NTLM_MAX_BUF);
+        PBYTE bin = HeapAlloc(GetProcessHeap(),0, sizeof(BYTE) * NTLM_MAX_BUF);
+        int buffer_len, bin_len, max_len = NTLM_MAX_BUF;
+        ULONG ctxt_attr = 0;
+
+        if(helper->mode != NTLM_SERVER)
+        {
+            HeapFree(GetProcessHeap(), 0, buffer);
+            HeapFree(GetProcessHeap(), 0, bin);
+            return SEC_E_INVALID_HANDLE;
+        }
+
+        /* Handle all the flags */
+        if(fContextReq & ISC_REQ_ALLOCATE_MEMORY)
+        {
+            FIXME("AcceptSecurityContext(): ISC_REQ_ALLOCATE_MEMORY stub\n");
+        }
+        if(fContextReq & ISC_REQ_CONFIDENTIALITY)
+        {
+            FIXME("AcceptSecurityContext(): ISC_REQ_CONFIDENTIALITY stub\n");
+        }
+        if(fContextReq & ISC_REQ_CONNECTION)
+        {
+            /* This is default, so we'll enable it */
+            ctxt_attr |= ISC_RET_CONNECTION;
+        }
+        if(fContextReq & ISC_REQ_EXTENDED_ERROR)
+        {
+            FIXME("AcceptSecurityContext(): ISC_REQ_EXTENDED_ERROR stub\n");
+        }
+        if(fContextReq & ISC_REQ_INTEGRITY)
+        {
+            FIXME("AcceptSecurityContext(): ISC_REQ_INTEGRITY stub\n");
+        }
+        if(fContextReq & ISC_REQ_MUTUAL_AUTH)
+        {
+            FIXME("AcceptSecurityContext(): ISC_REQ_MUTUAL_AUTH stub\n");
+        }
+        if(fContextReq & ISC_REQ_REPLAY_DETECT)
+        {
+            FIXME("AcceptSecurityContext(): ISC_REQ_REPLAY_DETECT stub\n");
+        }
+        if(fContextReq & ISC_REQ_SEQUENCE_DETECT)
+        {
+            FIXME("AcceptSecurityContext(): ISC_REQ_SEQUENCE_DETECT stub\n");
+        }
+        if(fContextReq & ISC_REQ_STREAM)
+        {
+            FIXME("AcceptSecurityContext(): ISC_REQ_STREAM stub\n");
+        }
+        /* Done with the flags */
+        if(TargetDataRep == SECURITY_NETWORK_DREP){
+            FIXME("Don't know how to do SECURITY_NETWORK_DREP\n");
+            HeapFree(GetProcessHeap(), 0, buffer);
+            HeapFree(GetProcessHeap(), 0, bin);
+            return SEC_E_UNSUPPORTED_FUNCTION;
+        }
+
+        
+        if(phContext == NULL)
+        {
+            /* This is the first call to AcceptSecurityHandle */
+            if(pInput == NULL)
+            {
+                HeapFree(GetProcessHeap(), 0, buffer);
+                HeapFree(GetProcessHeap(), 0, bin);
+                return SEC_E_INCOMPLETE_MESSAGE;
+            }
+            
+            if(pInput->cBuffers < 1)
+            {
+                HeapFree(GetProcessHeap(), 0, buffer);
+                HeapFree(GetProcessHeap(), 0, bin);
+                return SEC_E_INCOMPLETE_MESSAGE;
+            }
+
+            if(pInput->pBuffers[0].cbBuffer > max_len)
+            {
+                HeapFree(GetProcessHeap(), 0, buffer);
+                HeapFree(GetProcessHeap(), 0, bin);
+                return SEC_E_INVALID_TOKEN;
+            }
+            else
+                bin_len = pInput->pBuffers[0].cbBuffer;
+
+            /* This is the YR request from the client, encode to base64 */
+            
+            memcpy(bin, pInput->pBuffers[0].pvBuffer, bin_len);
+
+            lstrcpynA(buffer, "YR ", max_len-1);
+
+            if((ret = encodeBase64(bin, bin_len, buffer+3, max_len-3,
+                        &buffer_len)) != SEC_E_OK)
+            {
+                HeapFree(GetProcessHeap(), 0, buffer);
+                HeapFree(GetProcessHeap(), 0, bin);
+                return ret;
+            }
+            
+            TRACE("Client sent: %s\n", debugstr_a(buffer));
+            
+            if((ret = run_helper(helper, buffer, max_len, &buffer_len)) !=
+                        SEC_E_OK)
+            {
+                HeapFree(GetProcessHeap(), 0, buffer);
+                HeapFree(GetProcessHeap(), 0, bin);
+                return ret;
+            }
+
+            TRACE("Reply from ntlm_auth: %s\n", debugstr_a(buffer));
+            /* The expected answer is TT <base64 blob> */
+
+            if(strncmp(buffer, "TT ", 3) != 0)
+            {
+                HeapFree(GetProcessHeap(), 0, buffer);
+                HeapFree(GetProcessHeap(), 0, bin);
+                return SEC_E_INVALID_TOKEN;
+            }
+
+            if((ret = decodeBase64(buffer+3, buffer_len-3, bin, max_len,
+                            &bin_len)) != SEC_E_OK)
+            {
+                HeapFree(GetProcessHeap(), 0, buffer);
+                HeapFree(GetProcessHeap(), 0, bin);
+                return ret;
+            }
+            
+            /* send this to the client */
+            if(pOutput == NULL)
+            {
+                HeapFree(GetProcessHeap(), 0, buffer);
+                HeapFree(GetProcessHeap(), 0, bin);
+                return SEC_E_INSUFFICIENT_MEMORY;
+            }
+
+            if(pOutput->cBuffers < 1)
+            {
+                HeapFree(GetProcessHeap(), 0, buffer);
+                HeapFree(GetProcessHeap(), 0, bin);
+                return SEC_E_INSUFFICIENT_MEMORY;
+            }
+
+            pOutput->pBuffers[0].cbBuffer = bin_len;
+            pOutput->pBuffers[0].BufferType = SECBUFFER_DATA;
+            memcpy(pOutput->pBuffers[0].pvBuffer, bin, bin_len);
+            ret = SEC_I_CONTINUE_NEEDED;
+            
+        }
+        else
+        {
+            /* we expect a KK request from client */
+            if(pInput == NULL)
+            {
+                HeapFree(GetProcessHeap(), 0, buffer);
+                HeapFree(GetProcessHeap(), 0, bin);
+                return SEC_E_INCOMPLETE_MESSAGE;
+            }
+            
+            if(pInput->cBuffers < 1)
+            {
+                HeapFree(GetProcessHeap(), 0, buffer);
+                HeapFree(GetProcessHeap(), 0, bin);
+                return SEC_E_INCOMPLETE_MESSAGE;
+            }
+
+            if(pInput->pBuffers[0].cbBuffer > max_len)
+            {
+                HeapFree(GetProcessHeap(), 0, buffer);
+                HeapFree(GetProcessHeap(), 0, bin);
+                return SEC_E_INVALID_TOKEN;
+            }
+            else
+                bin_len = pInput->pBuffers[0].cbBuffer;
+
+            memcpy(bin, pInput->pBuffers[0].pvBuffer, bin_len);
+
+            lstrcpynA(buffer, "KK ", max_len-1);
+
+            if((ret = encodeBase64(bin, bin_len, buffer+3, max_len-3,
+                        &buffer_len)) != SEC_E_OK)
+            {
+                HeapFree(GetProcessHeap(), 0, buffer);
+                HeapFree(GetProcessHeap(), 0, bin);
+                return ret;
+            }
+            
+            TRACE("Client sent: %s\n", debugstr_a(buffer));
+            
+            if((ret = run_helper(helper, buffer, max_len, &buffer_len)) !=
+                        SEC_E_OK)
+            {
+                HeapFree(GetProcessHeap(), 0, buffer);
+                HeapFree(GetProcessHeap(), 0, bin);
+                return ret;
+            }
+
+            TRACE("Reply from ntlm_auth: %s\n", debugstr_a(buffer));
+            
+            if(strncmp(buffer, "AF ", 3) != 0)
+            {
+                if(strncmp(buffer, "NA ", 3) == 0)
+                {
+                    HeapFree(GetProcessHeap(), 0, buffer);
+                    HeapFree(GetProcessHeap(), 0, bin);
+                    return SEC_E_LOGON_DENIED;
+                }
+                else
+                {
+                    HeapFree(GetProcessHeap(), 0, buffer);
+                    HeapFree(GetProcessHeap(), 0, bin);
+                    return SEC_E_INVALID_TOKEN;
+                }
+            }
+            
+            ret = SEC_E_OK;
+        }
+        
+        phNewContext->dwUpper = ctxt_attr;
+        phNewContext->dwLower = ret;
+        HeapFree(GetProcessHeap(), 0, buffer);
+        HeapFree(GetProcessHeap(), 0, bin);
+
     }
     else
     {
@@ -231,27 +1040,9 @@
     TRACE("%p\n", phContext);
     if (phContext)
     {
-        ret = SEC_E_UNSUPPORTED_FUNCTION;
-    }
-    else
-    {
-        ret = SEC_E_INVALID_HANDLE;
-    }
-    return ret;
-}
-
-/***********************************************************************
- *              ApplyControlToken
- */
-static SECURITY_STATUS SEC_ENTRY ntlm_ApplyControlToken(PCtxtHandle phContext,
- PSecBufferDesc pInput)
-{
-    SECURITY_STATUS ret;
-
-    TRACE("%p %p\n", phContext, pInput);
-    if (phContext)
-    {
-        ret = SEC_E_UNSUPPORTED_FUNCTION;
+        phContext->dwUpper = 0;
+        phContext->dwLower = 0;
+        ret = SEC_E_OK;
     }
     else
     {
@@ -268,10 +1059,6 @@
 {
     SECURITY_STATUS ret;
 
-    /* FIXME: From reading wrapper.h, I think the dwUpper part of a context is
-     * the SecurePackage part and the dwLower part is the actual context 
-     * handle. It should be easy to extract the context attributes from that.
-     */
     TRACE("%p %ld %p\n", phContext, ulAttribute, pBuffer);
     if (phContext)
     {
@@ -371,20 +1158,39 @@
     return ret;
 }
 
+/***********************************************************************
+ *             FreeCredentialsHandle
+ */
+static SECURITY_STATUS SEC_ENTRY ntlm_FreeCredentialsHandle(
+        PCredHandle phCredential)
+{
+    SECURITY_STATUS ret;
 
+    if(phCredential){
+        PNegoHelper helper = (PNegoHelper) phCredential->dwLower;
+        phCredential->dwUpper = 0;
+        phCredential->dwLower = 0;
+        cleanup_helper(helper);
+        ret = SEC_E_OK;
+    }
+    else
+        ret = SEC_E_OK;
+    
+    return ret;
+}
 
-static SecurityFunctionTableA negoTableA = {
+static SecurityFunctionTableA ntlmTableA = {
     1,
     NULL,   /* EnumerateSecurityPackagesA */
     ntlm_QueryCredentialsAttributesA,   /* QueryCredentialsAttributesA */
     ntlm_AcquireCredentialsHandleA,     /* AcquireCredentialsHandleA */
-    FreeCredentialsHandle,              /* FreeCredentialsHandle */
+    ntlm_FreeCredentialsHandle,         /* FreeCredentialsHandle */
     NULL,   /* Reserved2 */
     ntlm_InitializeSecurityContextA,    /* InitializeSecurityContextA */
     ntlm_AcceptSecurityContext,         /* AcceptSecurityContext */
     ntlm_CompleteAuthToken,             /* CompleteAuthToken */
     ntlm_DeleteSecurityContext,         /* DeleteSecurityContext */
-    ntlm_ApplyControlToken,             /* ApplyControlToken */
+    NULL,  /* ApplyControlToken */
     ntlm_QueryContextAttributesA,       /* QueryContextAttributesA */
     ntlm_ImpersonateSecurityContext,    /* ImpersonateSecurityContext */
     ntlm_RevertSecurityContext,         /* RevertSecurityContext */
@@ -404,18 +1210,18 @@
     NULL,   /* SetContextAttributesA */
 };
 
-static SecurityFunctionTableW negoTableW = {
+static SecurityFunctionTableW ntlmTableW = {
     1,
     NULL,   /* EnumerateSecurityPackagesW */
     ntlm_QueryCredentialsAttributesW,   /* QueryCredentialsAttributesW */
     ntlm_AcquireCredentialsHandleW,     /* AcquireCredentialsHandleW */
-    FreeCredentialsHandle,              /* FreeCredentialsHandle */
+    ntlm_FreeCredentialsHandle,         /* FreeCredentialsHandle */
     NULL,   /* Reserved2 */
     ntlm_InitializeSecurityContextW,    /* InitializeSecurityContextW */
     ntlm_AcceptSecurityContext,         /* AcceptSecurityContext */
     ntlm_CompleteAuthToken,             /* CompleteAuthToken */
     ntlm_DeleteSecurityContext,         /* DeleteSecurityContext */
-    ntlm_ApplyControlToken,             /* ApplyControlToken */
+    NULL,  /* ApplyControlToken */
     ntlm_QueryContextAttributesW,       /* QueryContextAttributesW */
     ntlm_ImpersonateSecurityContext,    /* ImpersonateSecurityContext */
     ntlm_RevertSecurityContext,         /* RevertSecurityContext */
@@ -441,30 +1247,56 @@
 static CHAR ntlm_comment_A[] = "NTLM Security Package";
 
 void SECUR32_initNTLMSP(void)
-{
-    SecureProvider *provider = SECUR32_addProvider(&negoTableA, &negoTableW,
+{   
+    SECURITY_STATUS ret;
+    PNegoHelper helper;
+
+    SEC_CHAR *args[] = {
+        "ntlm_auth",
+        "--version",
+        NULL };
+
+    if((ret = fork_helper(&helper, "ntlm_auth", args)) != SEC_E_OK)
+    {
+        /* Cheat and allocate a helper anyway, so cleanup later will work. */
+        helper = HeapAlloc(GetProcessHeap,0, sizeof(PNegoHelper));
+        helper->version = -1;
+    }
+    else
+    {
+        check_version(helper);
+    }
+
+    if(helper->version > 2)
+    {
+
+        SecureProvider *provider = SECUR32_addProvider(&ntlmTableA, &ntlmTableW,
             NULL);
-    /* According to Windows, NTLM has the following capabilities. 
-     */
+        /* According to Windows, NTLM has the following capabilities. 
+         */
     
-    static const LONG caps =
-        SECPKG_FLAG_INTEGRITY |
-        SECPKG_FLAG_PRIVACY |
-        SECPKG_FLAG_TOKEN_ONLY |
-        SECPKG_FLAG_CONNECTION |
-        SECPKG_FLAG_MULTI_REQUIRED |
-        SECPKG_FLAG_IMPERSONATION |
-        SECPKG_FLAG_ACCEPT_WIN32_NAME |
-        SECPKG_FLAG_READONLY_WITH_CHECKSUM;
-
-    static const USHORT version = 1;
-    static const USHORT rpcid = 10;
-    static const ULONG  max_token = 12000;
-    const SecPkgInfoW infoW = { caps, version, rpcid, max_token, ntlm_name_W, 
-        ntlm_comment_W};
-    const SecPkgInfoA infoA = { caps, version, rpcid, max_token, ntlm_name_A,
-        ntlm_comment_A};
+        static const LONG caps =
+            SECPKG_FLAG_INTEGRITY |
+            SECPKG_FLAG_PRIVACY |
+            SECPKG_FLAG_TOKEN_ONLY |
+            SECPKG_FLAG_CONNECTION |
+            SECPKG_FLAG_MULTI_REQUIRED |
+            SECPKG_FLAG_IMPERSONATION |
+            SECPKG_FLAG_ACCEPT_WIN32_NAME |
+            SECPKG_FLAG_READONLY_WITH_CHECKSUM;
+
+        static const USHORT version = 1;
+        static const USHORT rpcid = 10;
+        /* In Windows, this is 12000, but ntlm_auth won't take more than 2010 
+         * characters, so there is no use reporting a bigger size */
+        static const ULONG  max_token = NTLM_MAX_BUF;
+        const SecPkgInfoW infoW = { caps, version, rpcid, max_token, ntlm_name_W, 
+            ntlm_comment_W};
+        const SecPkgInfoA infoA = { caps, version, rpcid, max_token, ntlm_name_A,
+            ntlm_comment_A};
 
-    SECUR32_addPackages(provider, 1L, &infoA, &infoW);        
+        SECUR32_addPackages(provider, 1L, &infoA, &infoW);
+    }
+    cleanup_helper(helper);
     
 }

--- dlls/secur32/Makefile.in	2005-08-27 17:53:32.402835488 +0200
+++ dlls/secur32/Makefile.in.new	2005-08-27 17:52:29.112457088 +0200
@@ -4,7 +4,7 @@
 VPATH     = @srcdir@
 MODULE    = secur32.dll
 IMPORTLIB = libsecur32.$(IMPLIBEXT)
-IMPORTS   = user32 advapi32 kernel32 ntdll
+IMPORTS   = user32 advapi32 kernel32 netapi32 ntdll
 
 C_SRCS = \
 	base64_codec.c \


More information about the wine-patches mailing list