secur32: Add some real functionality to the NTLM provider, try 4

Kai Blin blin at gmx.net
Thu Aug 25 12:10:04 CDT 2005


base64_codec.c converts to and from base64 encoding, which is used by
ntlm_auth.

dispatcher.c runs ntlm_auth to do authentication. This will also be
used by the Negotiate provider. Fixed to close all file descriptors.
Also won't use stdio anymore.

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 needed anymore.

This version also checks if ntlm_auth is in the right version. This
check is not really needed for the NTLM provider, but as ntlm_auth in
samba3 can't do Negotiate correctly, it will be more useful for
Negotiate.

The tests have been updated to succeed on either SEC_E_OK and
SEC_E_LOGON_DENIED, as both indicate that the tests ran correctly.
That way the tests will work as long as ntlm_auth is present.

Changelog:
Kai Blin <blin at gmx.net>
Added functionality to the NTLM security provider. Also added some
tests.

-- 
Kai Blin, (blin at gmx dot net)
My Bonnie looked into a gas tank,
The height of its contents to see!
She lit a small match to assist her,
Oh, bring back my Bonnie to me.
-------------- next part --------------
Index: dlls/secur32/Makefile.in
===================================================================
RCS file: /home/wine/wine/dlls/secur32/Makefile.in,v
retrieving revision 1.6
diff -u -3 -r1.6 Makefile.in
--- dlls/secur32/Makefile.in	17 Aug 2005 09:52:30 -0000	1.6
+++ dlls/secur32/Makefile.in	25 Aug 2005 17:01:13 -0000
@@ -4,9 +4,11 @@
 VPATH     = @srcdir@
 MODULE    = secur32.dll
 IMPORTLIB = libsecur32.$(IMPLIBEXT)
-IMPORTS   = user32 advapi32 kernel32 ntdll
+IMPORTS   = user32 advapi32 kernel32 netapi32 ntdll
 
 C_SRCS = \
+	base64_codec.c \
+	dispatcher.c \
 	negotiate.c \
 	ntlm.c \
 	schannel.c \
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	25 Aug 2005 17:01:18 -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,320 @@
     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)
+                    {
+                        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);
+            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 +716,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 +727,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 +771,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 +1035,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 +1054,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 +1153,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 = {
     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 */
@@ -409,13 +1210,13 @@
     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 +1242,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(&negoTableA, &negoTableW,
             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);
     
 }
Index: dlls/secur32/secur32_priv.h
===================================================================
RCS file: /home/wine/wine/dlls/secur32/secur32_priv.h,v
retrieving revision 1.4
diff -u -3 -r1.4 secur32_priv.h
--- dlls/secur32/secur32_priv.h	17 Aug 2005 09:52:30 -0000	1.4
+++ dlls/secur32/secur32_priv.h	25 Aug 2005 17:01:19 -0000
@@ -21,6 +21,9 @@
 #ifndef __SECUR32_PRIV_H__
 #define __SECUR32_PRIV_H__
 
+#include <sys/types.h>
+#include <stdio.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
@@ -47,6 +50,25 @@
     SecureProvider *provider;
 } SecurePackage;
 
+typedef enum _helper_mode {
+    NTLM_SERVER,
+    NTLM_CLIENT,
+    NEGO_SERVER,
+    NEGO_CLIENT,
+    NUM_HELPER_MODES
+} HelperMode;
+
+typedef struct _NegoHelper {
+    pid_t helper_pid;
+    HelperMode mode;
+    SEC_CHAR *password;
+    int pwlen;
+    int pipe_in;
+    int pipe_out;
+    int first_time;
+    int version;
+} NegoHelper, *PNegoHelper;
+
 /* Allocates space for and initializes a new provider.  If fnTableA or fnTableW
  * is non-NULL, assumes the provider is built-in (and is thus already loaded.)
  * Otherwise moduleName must not be NULL.
@@ -82,4 +104,23 @@
 void SECUR32_initNegotiateSP(void);
 void SECUR32_initNTLMSP(void);
 
+/* Functions from dispatcher.c used elsewhere in the code */
+SECURITY_STATUS fork_helper(PNegoHelper *new_helper, const char *prog,
+        char * const argv[]);
+
+SECURITY_STATUS run_helper(PNegoHelper helper, unsigned char *buffer,
+        unsigned int max_buflen, int *buflen);
+
+void cleanup_helper(PNegoHelper helper);
+
+void check_version(PNegoHelper helper);
+
+/* Functions from base64_codec.c used elsewhere */
+SECURITY_STATUS encodeBase64(PBYTE in_buf, int in_len, char* out_buf, 
+        int max_len, int *out_len);
+
+SECURITY_STATUS decodeBase64(char *in_buf, int in_len, BYTE *out_buf, 
+        int max_len, int *out_len);
+
+
 #endif /* ndef __SECUR32_PRIV_H__ */
Index: dlls/secur32/tests/Makefile.in
===================================================================
RCS file: /home/wine/wine/dlls/secur32/tests/Makefile.in,v
retrieving revision 1.1
diff -u -3 -r1.1 Makefile.in
--- dlls/secur32/tests/Makefile.in	3 Aug 2005 13:08:49 -0000	1.1
+++ dlls/secur32/tests/Makefile.in	25 Aug 2005 17:01:19 -0000
@@ -6,7 +6,8 @@
 IMPORTS   = secur32
 
 CTESTS = \
-	main.c
+	main.c \
+	ntlm.c
 
 @MAKE_TEST_RULES@
 
--- /dev/null	2005-08-19 11:42:14.099901488 +0200
+++ dlls/secur32/base64_codec.c	2005-08-25 01:03:16.000000000 +0200
@@ -0,0 +1,199 @@
+/*
+ * base64 encode/decode functions
+ *
+ * Copyright 2005 Kai Blin
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ * Based on code by Matthias Gaertner.
+ */
+
+#include <string.h>
+#include "windef.h"
+#include "winerror.h"
+#include "sspi.h"
+#include "wine/debug.h"
+
+WINE_DEFAULT_DEBUG_CHANNEL(secur32);
+
+static const char* to_b64 = 
+"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+
+SECURITY_STATUS encodeBase64(PBYTE in_buf, int in_len, char* out_buf, 
+        int max_len, int *out_len)
+{
+    unsigned int div;
+    unsigned int rem;
+    unsigned int  num_out = 0;
+    char res[4 + (in_len * 4)/3];
+    BYTE *pIn = in_buf; 
+    
+    if(in_len == 0){
+        memset(out_buf, '\0', max_len);
+        *out_len = 0;
+        return SEC_E_OK;
+    }
+
+    div = in_len / 3;
+    rem = in_len % 3;
+
+    TRACE("in_len = %d, rem = %d, div = %d\n", in_len, rem, div);
+    
+    while(div > 0)
+    {
+        res[num_out + 0] = to_b64[ (pIn[0] >> 2) & 0x3f];
+        res[num_out + 1] = to_b64[((pIn[0] << 4) & 0x30) + ((pIn[1] >> 4) & 0xf)];
+        res[num_out + 2] = to_b64[((pIn[1] << 2) & 0x3c) + ((pIn[2] >> 6) & 0x3)];
+        res[num_out + 3] = to_b64[  pIn[2] & 0x3f];
+        
+        pIn += 3;
+        num_out += 4;
+        div--;
+    }
+    
+    switch(rem)
+    {
+        case 2: 
+            res[num_out + 0] = to_b64[ (pIn[0] >> 2) & 0x3f];
+            res[num_out + 1] = to_b64[((pIn[0] << 4) & 0x30) + ((pIn[1] >> 4) & 0xf)];
+            res[num_out + 2] = to_b64[((pIn[1] << 2) & 0x3c)];
+            res[num_out + 3] = '=';
+
+            num_out += 4;
+            break;
+
+        case 1:
+            res[num_out + 0] = to_b64[ (pIn[0] >> 2) & 0x3f];
+            res[num_out + 1] = to_b64[((pIn[0] << 4) & 0x30)];
+            res[num_out + 2] = '=';
+            res[num_out + 3] = '=';
+
+            num_out += 4;
+            break;
+    }
+
+    /* Input buffer was too long */
+    if(num_out + 1 > max_len){
+        memset(out_buf, '\0', max_len);
+        *out_len = 0;
+        TRACE("Buffer size is too small: num out is %d, max_len is %d\n", 
+                num_out, max_len);
+        return SEC_E_BUFFER_TOO_SMALL;
+    }
+    else
+    {
+        res[num_out] = '\0';
+        num_out++;
+        strncpy(out_buf, res, num_out);
+        *out_len = num_out;
+        return SEC_E_OK;
+    }
+
+    /* We never get here, but some compilers whine about it */
+    return SEC_E_INTERNAL_ERROR;
+}          
+
+BYTE decode(char c)
+{
+    if( (c >= 'A') && (c <= 'Z'))
+        return (unsigned char) (c - 'A');
+    if( (c >= 'a') && (c <= 'z'))
+        return (unsigned char) (c - 'a' + (char)26);
+    if( (c >= '0') && (c <= '9'))
+        return (unsigned char) (c - '0' + (char)52);
+    if( c == '+')
+        return (unsigned char)62;
+    if( c == '/')
+        return (unsigned char)63;
+    return -1;
+}
+
+
+SECURITY_STATUS decodeBase64(char *in_buf, int in_len, BYTE *out_buf, 
+        int max_len, int *out_len)
+{
+    BYTE data[in_len+1];
+    BYTE *pIn = (PBYTE) in_buf;
+    unsigned int num_data = 0;
+    unsigned int div;
+    
+    if( (in_len % 4) != 0)
+    {
+        TRACE("in_len = %d\n", in_len);
+        TRACE("message is %s\n", debugstr_a(in_buf));        
+        return SEC_E_ILLEGAL_MESSAGE;
+    }
+    
+    if( (div = in_len/4) < 1)
+    {
+        TRACE("div = %d\n", div);
+        return SEC_E_ILLEGAL_MESSAGE;
+    }
+    TRACE("in_len = %d, div = %d\n", in_len, div);
+
+    while(div > 1)
+    {
+        data[num_data + 0] = ((decode(pIn[0])<<2) & 0xfc) | ((decode(pIn[1])>>4) & 0x03);
+        data[num_data + 1] = ((decode(pIn[1])<<4) & 0xf0) | ((decode(pIn[2])>>2) & 0x0f);
+        data[num_data + 2] = ((decode(pIn[2])<<6) & 0xc0) | ( decode(pIn[3])     & 0x3f);
+
+        num_data += 3;
+        pIn += 4;
+        div--;
+    }
+
+    if(pIn[2] == '=')
+    {
+        /* We have two bytes of padding */
+        data[num_data] = ((decode(pIn[0])<<2) & 0xfc) | ((decode(pIn[1])>>4) & 0x03);
+        num_data++;
+    }
+    else if(pIn[3] == '=')
+    {
+        /* One byte of padding */
+        data[num_data + 0] = ((decode(pIn[0])<<2) & 0xfc) | ((decode(pIn[1])>>4) & 0x03);
+        data[num_data + 1] = ((decode(pIn[1])<<4) & 0xf0) | ((decode(pIn[2])>>2) & 0x0f);
+        num_data += 2;
+    }
+    else
+    {
+        /* No padding */
+        data[num_data + 0] = ((decode(pIn[0])<<2) & 0xfc) | ((decode(pIn[1])>>4) & 0x03);
+        data[num_data + 1] = ((decode(pIn[1])<<4) & 0xf0) | ((decode(pIn[2])>>2) & 0x0f);
+        data[num_data + 2] = ((decode(pIn[2])<<6) & 0xc0) | ( decode(pIn[3])     & 0x3f);
+        num_data += 3;
+    }
+    
+    TRACE("num_data = %d\n", num_data);
+    
+    if(num_data > max_len)
+    {
+        /* Input data too big for output buffer */
+        memset(out_buf, 0, (size_t)max_len);
+        *out_len = 0;
+        return SEC_E_BUFFER_TOO_SMALL;
+    }
+    else
+    {
+        memcpy(out_buf, data, (size_t)num_data);
+        *out_len = num_data;
+        return SEC_E_OK;
+    }
+    
+    /* This is never reached, but some compilers whine if they don't have it. */
+    return SEC_E_INTERNAL_ERROR;
+}
+    
+
--- /dev/null	2005-08-19 11:42:14.099901488 +0200
+++ dlls/secur32/dispatcher.c	2005-08-25 18:29:02.608190616 +0200
@@ -0,0 +1,215 @@
+/*
+ * Copyright 2005 Kai Blin
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ * A dispatcher to run ntlm_auth for wine's sspi module.
+ */
+
+#include <stdarg.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include "windef.h"
+#include "winbase.h"
+#include "winerror.h"
+#include "sspi.h"
+#include "secur32_priv.h"
+#include "wine/debug.h"
+
+WINE_DEFAULT_DEBUG_CHANNEL(secur32);
+
+SECURITY_STATUS fork_helper(PNegoHelper *new_helper, const char *prog,
+        char* const argv[])
+{
+    int pipe_in[2];
+    int pipe_out[2];
+    int i;
+    
+    TRACE("%s ", debugstr_a(prog));
+    for(i = 0; argv[i] != NULL; ++i)
+    {
+        TRACE("%s ", debugstr_a(argv[i]));
+    }
+    TRACE("\n");           
+    PNegoHelper helper = HeapAlloc(GetProcessHeap(), 0, sizeof(NegoHelper));
+
+    if (helper == NULL)
+    {
+        return SEC_E_INSUFFICIENT_MEMORY;
+    }
+    
+    *new_helper = helper;
+
+    if( ( pipe(pipe_in) < 0 ) || ( pipe(pipe_out) < 0 ) )
+    {
+        close(pipe_in[0]);
+        close(pipe_in[1]);
+        close(pipe_out[0]);
+        close(pipe_out[1]);
+        return SEC_E_INTERNAL_ERROR;
+    }
+
+    helper->first_time = 0;
+    helper->helper_pid = fork();
+
+    if(helper->helper_pid == -1)
+    {
+        close(pipe_in[0]);
+        close(pipe_in[1]);
+        close(pipe_out[0]);
+        close(pipe_out[1]);
+        return SEC_E_INTERNAL_ERROR;
+    }
+
+    if(helper->helper_pid == 0)
+    {
+        /* We're in the child now */
+        close(0);
+        close(1);
+        
+        dup2(pipe_out[0], 0);
+        close(pipe_out[0]);
+        close(pipe_out[1]);
+
+        dup2(pipe_in[1], 1);
+        close(pipe_in[0]);
+        close(pipe_in[1]);
+
+        execvp(prog, argv);
+
+        /* Whoops, we shouldn't get here. Big badaboom.*/
+        write(STDOUT_FILENO, "BH\n", 3);
+        exit(0x302);
+        
+    }
+    else 
+    {
+        helper->pipe_in = pipe_in[0];
+        close(pipe_in[1]);
+        helper->pipe_out = pipe_out[1];
+        close(pipe_out[0]);
+    }
+
+    return SEC_E_OK;
+}
+
+SECURITY_STATUS run_helper(PNegoHelper helper, unsigned char *buffer,
+        unsigned int max_buflen, int *buflen)
+{
+    /* ntlm_auth's buffer size is fixed at 2010 chars */
+    if(strlen(buffer) > 2010){
+        return SEC_E_BUFFER_TOO_SMALL;
+    }
+    TRACE("In helper: sending %s\n", debugstr_a(buffer));
+
+    /* buffer + '\n' */
+    int out_len = strlen(buffer) + 1;
+    char *out_buf = HeapAlloc(GetProcessHeap(), 0, out_len+1);
+    lstrcpyA(out_buf, buffer);
+    strcat(out_buf, "\n");
+    
+    write(helper->pipe_out, out_buf, out_len);
+    HeapFree(GetProcessHeap(), 0, out_buf);
+    
+    if(read(helper->pipe_in, buffer, 3) <= 2)
+    {
+        return SEC_E_INTERNAL_ERROR;
+    }
+    
+    if(strncmp(buffer, "OK", 2) == 0)
+    {
+        /* if the first two chars are OK, kill OK\n */
+        /* workaround for ntlm_auth v4 bug */
+        if(helper->first_time == 0)
+            read(helper->pipe_in, buffer, max_buflen-1);
+    }
+    else
+    {
+        if(strncmp(buffer, "PW", 2) != 0)
+            read(helper->pipe_in, buffer+3, max_buflen-4);
+    }
+            
+    TRACE("In helper: recieved %s\n", debugstr_a(buffer));
+
+    char *newline;
+
+    if((newline = strchr(buffer, '\n'))!= NULL)
+    {
+        *newline = '\0';
+    }
+    *buflen = strlen(buffer);
+
+    if( *buflen < 2 )
+    {
+        return SEC_E_ILLEGAL_MESSAGE;
+    }
+
+    if( (*buflen <= 3) && (strncmp(buffer, "BH", 2) == 0))
+    {
+        return SEC_E_INTERNAL_ERROR;
+    }
+        
+    return SEC_E_OK;
+}
+
+void cleanup_helper(PNegoHelper helper)
+{
+
+    TRACE("Killing helper %p\n", helper);
+    if( (helper == NULL) || (helper->helper_pid == 0))
+        return;
+    
+    kill(helper->helper_pid, SIGTERM);
+    waitpid(helper->helper_pid, NULL, 0);
+
+    HeapFree(GetProcessHeap(), 0, helper->password);
+
+    close(helper->pipe_out);
+    close(helper->pipe_in);
+
+    helper->helper_pid = 0;
+    HeapFree(GetProcessHeap(), 0, helper);
+}
+
+void check_version(PNegoHelper helper)
+{
+    TRACE("Checking version of helper\n");
+    char *temp = HeapAlloc(GetProcessHeap(), 0, 80);
+    char *newline;
+    if(helper != NULL)
+    {
+        read(helper->pipe_in, temp, 79);
+        if((newline = strchr(temp, '\n')) != NULL)
+            *newline = '\0';
+            
+        TRACE("Exact version is %s\n", debugstr_a(temp));
+        if(strncmp(temp+8, "3.9", 3) == 0)
+        {
+            helper->version = 4;
+        }
+        else if(strncmp(temp+8, "3.0", 3) == 0)
+        {
+            helper->version = 3;
+        }
+        else
+        {
+            TRACE("Unknown version!\n");
+            helper->version = -1;
+        }
+    }
+}
--- /dev/null	2005-08-19 11:42:14.099901488 +0200
+++ dlls/secur32/tests/ntlm.c	2005-08-25 18:36:42.486278520 +0200
@@ -0,0 +1,439 @@
+/*
+ * NTLM security provider tests
+ *
+ * Copyright 2005 Kai Blin
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#define SECURITY_WIN32
+
+#include <stdio.h>
+#include <stdarg.h>
+#include <windows.h>
+#include "wine/test.h"
+#include <winbase.h>
+#include <sspi.h>
+#include <rpc.h>
+#include <string.h>
+
+#define MAX_MESSAGE 3000
+
+static const char* getSecError(SECURITY_STATUS status)
+{
+#define _SEC_ERR(x) case (x): return #x;
+    switch(status)
+    {
+        _SEC_ERR(SEC_E_OK);
+        _SEC_ERR(SEC_E_INSUFFICIENT_MEMORY);
+        _SEC_ERR(SEC_E_INVALID_HANDLE);
+        _SEC_ERR(SEC_E_UNSUPPORTED_FUNCTION);
+        _SEC_ERR(SEC_E_TARGET_UNKNOWN);
+        _SEC_ERR(SEC_E_INTERNAL_ERROR);
+        _SEC_ERR(SEC_E_SECPKG_NOT_FOUND);
+        _SEC_ERR(SEC_E_NOT_OWNER);
+        _SEC_ERR(SEC_E_CANNOT_INSTALL);
+        _SEC_ERR(SEC_E_INVALID_TOKEN);
+        _SEC_ERR(SEC_E_CANNOT_PACK);
+        _SEC_ERR(SEC_E_QOP_NOT_SUPPORTED);
+        _SEC_ERR(SEC_E_NO_IMPERSONATION);
+        _SEC_ERR(SEC_I_CONTINUE_NEEDED);
+        _SEC_ERR(SEC_E_BUFFER_TOO_SMALL);
+        _SEC_ERR(SEC_E_ILLEGAL_MESSAGE);
+        _SEC_ERR(SEC_E_LOGON_DENIED);
+        default:
+            trace("Error = 0x%08lx\n", status);
+            return "Unknown error";
+    }
+#undef _SEC_ERR
+}
+
+/**********************************************************************/
+
+SECURITY_STATUS setupClient(PCredHandle cred, const char *user, 
+        const char *pass, const char *domain)
+{
+    SECURITY_STATUS ret;
+    PSEC_WINNT_AUTH_IDENTITY identity = NULL;
+    TimeStamp ttl;
+
+    trace("Running setupClient\n");
+    
+    if(user != NULL)
+    {
+        identity = HeapAlloc(GetProcessHeap(), 0, 
+                sizeof(SEC_WINNT_AUTH_IDENTITY_A));
+
+        memset( identity, 0, sizeof(identity));
+        identity->Domain = HeapAlloc(GetProcessHeap(), 0, 
+            lstrlenA( domain ? domain :"") + 1);
+        lstrcpyA(identity->Domain, domain ? domain :"");
+        identity->DomainLength = domain ? strlen(domain): 0;
+        identity->User = HeapAlloc(GetProcessHeap(), 0, 
+            lstrlenA( user ? user :"") + 1);
+        lstrcpyA(identity->User, user ? user :"");
+        identity->UserLength = user ? strlen(user) : 0;
+        identity->Password = HeapAlloc(GetProcessHeap(), 0, 
+            lstrlenA( pass ? pass :"") + 1);
+        lstrcpyA(identity->Password, pass ? pass : "");
+        identity->PasswordLength = pass ? strlen(pass): 0;
+        identity->Flags = SEC_WINNT_AUTH_IDENTITY_ANSI;
+    }
+
+    if((ret = AcquireCredentialsHandle(NULL, "NTLM", SECPKG_CRED_OUTBOUND,
+            NULL, identity, NULL, NULL, cred, &ttl)) != SEC_E_OK)
+    {
+        trace("AcquireCredentialsHandle() returned %s\n", getSecError(ret));
+    }
+    
+    ok(ret == SEC_E_OK, "AcquireCredentialsHande() returned %s\n", 
+            getSecError(ret));
+
+    if( identity != NULL)
+    {
+        if(identity->Domain != 0)
+            HeapFree(GetProcessHeap(), 0, identity->Domain);
+        if(identity->User != 0)
+            HeapFree(GetProcessHeap(), 0, identity->User);
+        if(identity->Password != 0)
+            HeapFree(GetProcessHeap(), 0, identity->Password);
+        HeapFree(GetProcessHeap(), 0, identity);
+    }       
+
+    return ret;
+}
+
+/**********************************************************************/
+
+SECURITY_STATUS setupServer(PCredHandle cred)
+{
+    SECURITY_STATUS ret;
+    TimeStamp ttl;
+
+    trace("Running setupServer\n");
+    
+    if((ret = AcquireCredentialsHandle(NULL, "NTLM", SECPKG_CRED_INBOUND, NULL,
+            NULL, NULL, NULL, cred, &ttl)) != SEC_E_OK)
+    {
+        trace("AcquireCredentialsHandle() returned %s\n", getSecError(ret));
+    }
+
+    ok(ret == SEC_E_OK, "AcquireCredentialsHande() returned %s\n",
+            getSecError(ret));
+
+    return ret;
+}
+ 
+/**********************************************************************/
+
+SECURITY_STATUS setupBuffers(PSecBufferDesc *new_in_buf, 
+        PSecBufferDesc *new_out_buf)
+{
+    int size = MAX_MESSAGE;
+    PSecBufferDesc in_buf = HeapAlloc(GetProcessHeap(), 0, 
+            sizeof(SecBufferDesc));
+
+    if(in_buf != NULL)
+    {
+        PSecBuffer sec_buffer = HeapAlloc(GetProcessHeap(), 0,
+                sizeof(SecBuffer));
+        if(sec_buffer == NULL){
+            trace("in_buf: sec_buffer == NULL");
+            return SEC_E_INSUFFICIENT_MEMORY;
+        }
+        
+        PBYTE buffer = HeapAlloc(GetProcessHeap(), 0, size);
+
+        if(buffer == NULL){
+            trace("in_buf: buffer == NULL");
+            return SEC_E_INSUFFICIENT_MEMORY;
+        }
+
+        in_buf->ulVersion = SECBUFFER_VERSION;
+        in_buf->cBuffers = 1;
+        in_buf->pBuffers = sec_buffer;
+
+        sec_buffer->cbBuffer = size;
+        sec_buffer->BufferType = SECBUFFER_TOKEN;
+        sec_buffer->pvBuffer = buffer;
+        *new_in_buf = in_buf;
+    }
+    else
+    {
+        trace("HeapAlloc in_buf returned NULL\n");
+        return SEC_E_INSUFFICIENT_MEMORY;
+    }
+
+    PSecBufferDesc out_buf = HeapAlloc(GetProcessHeap(), 0,
+            sizeof(PSecBufferDesc));
+    
+    if(out_buf != NULL)
+    {
+        PSecBuffer sec_buffer = HeapAlloc(GetProcessHeap(), 0,
+                sizeof(SecBuffer));
+        
+        if(sec_buffer == NULL){
+            trace("out_buf: sec_buffer == NULL");
+            return SEC_E_INSUFFICIENT_MEMORY;
+        }
+
+        PBYTE buffer = HeapAlloc(GetProcessHeap(), 0, size);
+
+        if(buffer == NULL){
+            trace("out_buf: buffer == NULL");
+            return SEC_E_INSUFFICIENT_MEMORY;
+        }
+
+        out_buf->ulVersion = SECBUFFER_VERSION;
+        out_buf->cBuffers = 1;
+        out_buf->pBuffers = sec_buffer;
+
+        sec_buffer->cbBuffer = 0;
+        sec_buffer->BufferType = SECBUFFER_TOKEN;
+        sec_buffer->pvBuffer = buffer;
+        *new_out_buf = out_buf;
+    }
+    else
+    {
+        trace("HeapAlloc out_buf returned NULL\n");
+        return SEC_E_INSUFFICIENT_MEMORY;
+    }
+
+    return SEC_E_OK;
+}
+
+/**********************************************************************/
+
+void cleanupBuffers(PSecBufferDesc in_buf, PSecBufferDesc out_buf)
+{
+    int i;
+
+    if(in_buf != NULL)
+    {
+        for(i = 0; i < in_buf->cBuffers; ++i)
+        {
+            HeapFree(GetProcessHeap(), 0, in_buf->pBuffers[i].pvBuffer);
+        }
+        HeapFree(GetProcessHeap(), 0, in_buf->pBuffers);
+        HeapFree(GetProcessHeap(), 0, in_buf);
+    }
+    
+    if(out_buf != NULL)
+    {
+        for(i = 0; i < out_buf->cBuffers; ++i)
+        {
+            HeapFree(GetProcessHeap(), 0, out_buf->pBuffers[i].pvBuffer);
+        }
+        HeapFree(GetProcessHeap(), 0, out_buf->pBuffers);
+        HeapFree(GetProcessHeap(), 0, out_buf);
+    }
+}
+
+/**********************************************************************/
+
+SECURITY_STATUS runClient(PCredHandle cred, PCtxtHandle ctxt, 
+        PSecBufferDesc in_buf, PSecBufferDesc out_buf, BOOL first)
+{
+    SECURITY_STATUS ret;
+    ULONG req_attr = ISC_REQ_CONNECTION;
+    ULONG ctxt_attr;
+    TimeStamp ttl;
+
+    trace("Running the client the %s time.\n", first?"first":"second");
+
+    ret = InitializeSecurityContext(cred, first?NULL:ctxt, NULL, req_attr, 
+            0, SECURITY_NATIVE_DREP, first?NULL:in_buf, 0, ctxt, out_buf,
+            &ctxt_attr, &ttl);
+    
+    if(ret == SEC_I_COMPLETE_AND_CONTINUE || ret == SEC_I_COMPLETE_NEEDED)
+    {
+        CompleteAuthToken(ctxt, out_buf);
+        if(ret == SEC_I_COMPLETE_AND_CONTINUE)
+            ret = SEC_I_CONTINUE_NEEDED;
+        else if(ret == SEC_I_COMPLETE_NEEDED)
+            ret = SEC_E_OK;
+    }       
+    
+    return ret;
+}
+
+/**********************************************************************/
+
+SECURITY_STATUS runServer(PCredHandle cred, PCtxtHandle ctxt,
+        PSecBufferDesc in_buf, PSecBufferDesc out_buf, BOOL first)
+{
+    SECURITY_STATUS ret;
+    ULONG ctxt_attr;
+    TimeStamp ttl;
+
+    trace("Running the server the %s time\n", first?"first":"second");
+
+    ret = AcceptSecurityContext(cred, first?NULL:ctxt, in_buf, 0,
+            SECURITY_NATIVE_DREP, ctxt, out_buf, &ctxt_attr, &ttl);
+
+    if(ret == SEC_I_COMPLETE_AND_CONTINUE || ret == SEC_I_COMPLETE_NEEDED)
+    {
+        CompleteAuthToken(ctxt, out_buf);
+        if(ret == SEC_I_COMPLETE_AND_CONTINUE)
+            ret = SEC_I_CONTINUE_NEEDED;
+        else if(ret == SEC_I_COMPLETE_NEEDED)
+            ret = SEC_E_OK;
+    }
+    
+
+    return ret;
+}
+
+/**********************************************************************/
+
+void communicate(PSecBufferDesc in_buf, PSecBufferDesc out_buf)
+{
+    if(in_buf != NULL && out_buf != NULL)
+    {
+        trace("Running communicate.\n");
+        if((in_buf->cBuffers >= 1) && (out_buf->cBuffers >= 1))
+        {
+            if((in_buf->pBuffers[0].pvBuffer != NULL) && 
+                    (out_buf->pBuffers[0].pvBuffer != NULL))
+            {
+                memset(out_buf->pBuffers[0].pvBuffer, 0, MAX_MESSAGE);
+                
+                memcpy(out_buf->pBuffers[0].pvBuffer, 
+                        in_buf->pBuffers[0].pvBuffer,
+                        in_buf->pBuffers[0].cbBuffer);
+                
+                out_buf->pBuffers[0].cbBuffer = in_buf->pBuffers[0].cbBuffer;
+                
+                memset(in_buf->pBuffers[0].pvBuffer, 0, MAX_MESSAGE);
+            }
+        }
+    }
+}
+   
+/**********************************************************************/
+
+void testNTLMAuthWithUser(void)
+{
+    SECURITY_STATUS sec_status;
+    CredHandle server_cred;
+    CredHandle client_cred;
+    CtxtHandle server_ctxt;
+    CtxtHandle client_ctxt;
+
+    PSecBufferDesc client_in = NULL, client_out = NULL;
+    PSecBufferDesc server_in = NULL, server_out = NULL;
+
+    BOOL continue_client = FALSE, continue_server = FALSE;
+    
+    sec_status = setupClient(&client_cred, "testuser", "testpass","WORKGROUP");
+
+    if(sec_status != SEC_E_OK)
+    {
+        trace("Error: Setting up the client returned %s, exiting test!\n",
+                getSecError(sec_status));
+        FreeCredentialsHandle(&server_cred);
+        FreeCredentialsHandle(&client_cred);
+        return;
+    }
+
+    sec_status = setupServer(&server_cred);
+
+    if(sec_status != SEC_E_OK)
+    {
+        trace("Error: Setting up the server returned %s, exiting test!\n",
+                getSecError(sec_status));
+        FreeCredentialsHandle(&server_cred);
+        FreeCredentialsHandle(&client_cred);
+        return;
+    }
+
+    setupBuffers(&client_in, &client_out);
+    setupBuffers(&server_in, &server_out);
+    
+    sec_status = runClient(&client_cred, &client_ctxt, client_in, client_out, 
+            TRUE);
+
+    ok(sec_status == SEC_E_OK || sec_status == SEC_I_CONTINUE_NEEDED,
+            "Running the client returned %s, more tests will fail from now.\n",
+            getSecError(sec_status));
+    
+    if(sec_status == SEC_I_CONTINUE_NEEDED)
+        continue_client = TRUE;
+
+    communicate(client_out, server_in);
+    
+    sec_status = runServer(&server_cred, &server_ctxt, server_in, server_out, 
+            TRUE);
+
+    ok(sec_status == SEC_E_OK || sec_status == SEC_I_CONTINUE_NEEDED,
+            "Running the server returned %s, more tests will fail from now.\n",
+            getSecError(sec_status));
+    
+    if(sec_status == SEC_I_CONTINUE_NEEDED)
+    {
+        continue_server = TRUE;
+        communicate(server_out, client_in);
+    }
+    
+    if(continue_client)
+    {
+        sec_status = runClient(&client_cred, &client_ctxt, client_in, client_out,
+            FALSE);
+
+        ok(sec_status == SEC_E_OK,
+            "Running the client returned %s, more tests will fail from now.\n",
+            getSecError(sec_status));
+        
+        communicate(client_out, server_in);
+    }
+
+    if(continue_server)
+    {
+        sec_status = runServer(&server_cred, &server_ctxt, server_in, server_out,
+            FALSE);
+        ok(sec_status == SEC_E_OK || SEC_E_LOGON_DENIED,
+                "Running the server returned %s.\n", getSecError(sec_status));
+    }
+
+    /* contine writing the test here */
+
+    trace("Before cleanupBuffers\n");
+    cleanupBuffers(client_in, client_out);
+    cleanupBuffers(server_in, server_out);
+
+    trace("After cleanupBuffers\n");
+    
+    sec_status = DeleteSecurityContext(&server_ctxt);
+    ok(sec_status == SEC_E_OK, "DeleteSecurityContext(server) returned %s\n",
+            getSecError(sec_status));
+
+    sec_status = DeleteSecurityContext(&client_ctxt);
+    ok(sec_status == SEC_E_OK, "DeleteSecurityContext(client) returned %s\n",
+            getSecError(sec_status));
+       
+    sec_status = FreeCredentialsHandle(&server_cred);
+    ok(sec_status == SEC_E_OK, "FreeCredentialsHandle(server) returned %s\n",
+            getSecError(sec_status));
+    
+    sec_status = FreeCredentialsHandle(&client_cred);
+    ok(sec_status == SEC_E_OK, "FreeCredentialsHandle(client) returned %s\n",
+            getSecError(sec_status));
+
+}
+
+START_TEST(ntlm)
+{
+    testNTLMAuthWithUser();
+}


More information about the wine-patches mailing list