secur32: Cleanup InitializeSecurityContext (reduce indent and duplication).
[wine] / dlls / secur32 / ntlm.c
1 /*
2  * Copyright 2005 Kai Blin
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2.1 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the Free Software
16  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
17  *
18  * This file implements the NTLM security provider.
19  */
20
21 #include <assert.h>
22 #include <stdarg.h>
23 #include "windef.h"
24 #include "winbase.h"
25 #include "winnls.h"
26 #include "rpc.h"
27 #include "sspi.h"
28 #include "lm.h"
29 #include "secur32_priv.h"
30 #include "wine/debug.h"
31
32 WINE_DEFAULT_DEBUG_CHANNEL(secur32);
33
34 #define NTLM_MAX_BUF 2010
35
36
37 /***********************************************************************
38  *              QueryCredentialsAttributesA
39  */
40 static SECURITY_STATUS SEC_ENTRY ntlm_QueryCredentialsAttributesA(
41         PCredHandle phCredential, ULONG ulAttribute, PVOID pBuffer)
42 {
43     SECURITY_STATUS ret;
44
45     TRACE("(%p, %ld, %p)\n", phCredential, ulAttribute, pBuffer);
46
47     if(ulAttribute == SECPKG_ATTR_NAMES)
48     {
49         FIXME("SECPKG_CRED_ATTR_NAMES: stub\n");
50         ret = SEC_E_UNSUPPORTED_FUNCTION;
51     }
52     else
53         ret = SEC_E_UNSUPPORTED_FUNCTION;
54     
55     return ret;
56 }
57
58 /***********************************************************************
59  *              QueryCredentialsAttributesW
60  */
61 static SECURITY_STATUS SEC_ENTRY ntlm_QueryCredentialsAttributesW(
62         PCredHandle phCredential, ULONG ulAttribute, PVOID pBuffer)
63 {
64     SECURITY_STATUS ret;
65
66     TRACE("(%p, %ld, %p)\n", phCredential, ulAttribute, pBuffer);
67
68     if(ulAttribute == SECPKG_ATTR_NAMES)
69     {
70         FIXME("SECPKG_CRED_ATTR_NAMES: stub\n");
71         ret = SEC_E_UNSUPPORTED_FUNCTION;
72     }
73     else
74         ret = SEC_E_UNSUPPORTED_FUNCTION;
75     
76     return ret;
77 }
78
79 /***********************************************************************
80  *              AcquireCredentialsHandleW
81  */
82 static SECURITY_STATUS SEC_ENTRY ntlm_AcquireCredentialsHandleW(
83  SEC_WCHAR *pszPrincipal, SEC_WCHAR *pszPackage, ULONG fCredentialUse,
84  PLUID pLogonID, PVOID pAuthData, SEC_GET_KEY_FN pGetKeyFn,
85  PVOID pGetKeyArgument, PCredHandle phCredential, PTimeStamp ptsExpiry)
86 {
87     SECURITY_STATUS ret;
88     PNegoHelper helper = NULL;
89     
90     SEC_CHAR *client_user_arg = NULL;
91     SEC_CHAR *client_domain_arg = NULL;
92     SEC_WCHAR *username = NULL, *domain = NULL;
93     
94     SEC_CHAR *client_argv[5];
95     SEC_CHAR *server_argv[] = { "ntlm_auth",
96         "--helper-protocol=squid-2.5-ntlmssp",
97         NULL };
98
99     TRACE("(%s, %s, 0x%08lx, %p, %p, %p, %p, %p, %p)\n",
100      debugstr_w(pszPrincipal), debugstr_w(pszPackage), fCredentialUse,
101      pLogonID, pAuthData, pGetKeyFn, pGetKeyArgument, phCredential, ptsExpiry);
102
103
104     switch(fCredentialUse)
105     {
106         case SECPKG_CRED_INBOUND:
107             if( (ret = fork_helper(&helper, "ntlm_auth", server_argv)) !=
108                     SEC_E_OK)
109             {
110                 phCredential = NULL;
111                 break;
112             }
113             else
114             {
115                 helper->mode = NTLM_SERVER;
116                 phCredential->dwUpper = fCredentialUse;
117                 phCredential->dwLower = (DWORD)helper;
118             }
119             ret = SEC_E_OK;
120             break;
121         case SECPKG_CRED_OUTBOUND:
122             {
123                 static const char username_arg[] = "--username=";
124                 static const char domain_arg[] = "--domain=";
125                 int unixcp_size;
126
127                 if(pAuthData == NULL)
128                 {
129                     LPWKSTA_USER_INFO_1 ui = NULL;
130                     NET_API_STATUS status;
131
132                     if((status = NetWkstaUserGetInfo(NULL, 1, (LPBYTE *)&ui)) !=
133                             NERR_Success)
134                     {
135                         ret = SEC_E_NO_CREDENTIALS;
136                         phCredential = NULL;
137                         break;
138                     }
139                     
140                     if(ui != NULL)
141                     {
142                         username = HeapAlloc(GetProcessHeap(), 0, 
143                                 (lstrlenW(ui->wkui1_username)+1) * 
144                                 sizeof(SEC_WCHAR));
145                         lstrcpyW(username, ui->wkui1_username);
146                         
147                         /* same for the domain */
148                         domain = HeapAlloc(GetProcessHeap(), 0, 
149                                 (lstrlenW(ui->wkui1_logon_domain)+1) * 
150                                 sizeof(SEC_WCHAR));
151                         lstrcpyW(domain, ui->wkui1_logon_domain);
152                         NetApiBufferFree(ui);
153                     }
154                     else
155                     {
156                         ret = SEC_E_NO_CREDENTIALS;
157                         phCredential = NULL;
158                         break;
159                     }
160                     
161                                     
162                 }
163                 else
164                 {
165                     PSEC_WINNT_AUTH_IDENTITY_W auth_data = 
166                         (PSEC_WINNT_AUTH_IDENTITY_W)pAuthData;
167
168                     if(auth_data->UserLength != 0)
169                     {
170                         /* Get username and domain from pAuthData */
171                         username = HeapAlloc(GetProcessHeap(), 0, 
172                                 (auth_data->UserLength + 1) * sizeof(SEC_WCHAR));
173                         lstrcpyW(username, auth_data->User);
174                     }
175                     else
176                     {
177                         ret = SEC_E_NO_CREDENTIALS;
178                         phCredential = NULL;
179                         break;
180                     }
181                     if(auth_data->DomainLength != 0)
182                     {
183                         domain = HeapAlloc(GetProcessHeap(), 0,
184                                 (auth_data->DomainLength + 1) * sizeof(SEC_WCHAR));
185                         lstrcpyW(domain, auth_data->Domain);
186
187                     }
188                     else
189                     {
190                         ret = SEC_E_NO_CREDENTIALS;
191                         phCredential = NULL;
192                         break;
193                     }
194
195                 }
196                 TRACE("Username is %s\n", debugstr_w(username));
197                 unixcp_size =  WideCharToMultiByte(CP_UNIXCP, WC_NO_BEST_FIT_CHARS,
198                         username, -1, NULL, 0, NULL, NULL) + sizeof(username_arg);
199                 client_user_arg = HeapAlloc(GetProcessHeap(), 0, unixcp_size);
200                 lstrcpyA(client_user_arg, username_arg);
201                 WideCharToMultiByte(CP_UNIXCP, WC_NO_BEST_FIT_CHARS, username, -1,
202                         client_user_arg + sizeof(username_arg) - 1, 
203                         unixcp_size - sizeof(username_arg) + 1, NULL, NULL);
204
205                 TRACE("Domain name is %s\n", debugstr_w(domain));
206                 unixcp_size = WideCharToMultiByte(CP_UNIXCP, WC_NO_BEST_FIT_CHARS,
207                         domain, -1, NULL, 0,  NULL, NULL) + sizeof(domain_arg);
208                 client_domain_arg = HeapAlloc(GetProcessHeap(), 0, unixcp_size);
209                 lstrcpyA(client_domain_arg, domain_arg);
210                 WideCharToMultiByte(CP_UNIXCP, WC_NO_BEST_FIT_CHARS, domain,
211                         -1, client_domain_arg + sizeof(domain_arg) - 1, 
212                         unixcp_size - sizeof(domain) + 1, NULL, NULL);
213
214                 client_argv[0] = "ntlm_auth";
215                 client_argv[1] = "--helper-protocol=ntlmssp-client-1";
216                 client_argv[2] = client_user_arg;
217                 client_argv[3] = client_domain_arg;
218                 client_argv[4] = NULL;
219
220                 if((ret = fork_helper(&helper, "ntlm_auth", client_argv)) != 
221                         SEC_E_OK)
222                 {
223                     phCredential = NULL;
224                     break;
225                 }
226                 else
227                 {
228                     helper->mode = NTLM_CLIENT;
229
230                     if(pAuthData != NULL)
231                     {
232                         PSEC_WINNT_AUTH_IDENTITY_W auth_data = 
233                            (PSEC_WINNT_AUTH_IDENTITY_W)pAuthData;
234
235                         if(auth_data->PasswordLength != 0)
236                         {
237                             helper->pwlen = WideCharToMultiByte(CP_UNIXCP, 
238                                 WC_NO_BEST_FIT_CHARS, auth_data->Password, 
239                                 auth_data->PasswordLength+1, NULL, 0, NULL, NULL);
240                         
241                             helper->password = HeapAlloc(GetProcessHeap(), 0, 
242                                     helper->pwlen);
243
244                             WideCharToMultiByte(CP_UNIXCP, WC_NO_BEST_FIT_CHARS,
245                                 auth_data->Password, auth_data->PasswordLength+1,
246                                 helper->password, helper->pwlen, NULL, NULL);
247                         }
248                     }
249            
250                     phCredential->dwUpper = fCredentialUse;
251                     phCredential->dwLower = (DWORD)helper;
252                     TRACE("ACH phCredential->dwUpper: 0x%08lx, dwLower: 0x%08lx\n",
253                             phCredential->dwUpper, phCredential->dwLower);
254                 }
255                 ret = SEC_E_OK;
256                 break;
257             }
258         case SECPKG_CRED_BOTH:
259             FIXME("AcquireCredentialsHandle: SECPKG_CRED_BOTH stub\n");
260             ret = SEC_E_UNSUPPORTED_FUNCTION;
261             phCredential = NULL;
262             break;
263         default:
264             phCredential = NULL;
265             ret = SEC_E_UNKNOWN_CREDENTIALS;
266     }
267     
268
269     HeapFree(GetProcessHeap(), 0, client_user_arg);
270     HeapFree(GetProcessHeap(), 0, client_domain_arg);
271     HeapFree(GetProcessHeap(), 0, username);
272     HeapFree(GetProcessHeap(), 0, domain);
273
274     return ret;
275 }
276
277 /***********************************************************************
278  *              AcquireCredentialsHandleA
279  */
280 static SECURITY_STATUS SEC_ENTRY ntlm_AcquireCredentialsHandleA(
281  SEC_CHAR *pszPrincipal, SEC_CHAR *pszPackage, ULONG fCredentialUse,
282  PLUID pLogonID, PVOID pAuthData, SEC_GET_KEY_FN pGetKeyFn,
283  PVOID pGetKeyArgument, PCredHandle phCredential, PTimeStamp ptsExpiry)
284 {
285     SECURITY_STATUS ret;
286     int user_sizeW, domain_sizeW, passwd_sizeW;
287     
288     SEC_WCHAR *user = NULL, *domain = NULL, *passwd = NULL, *package = NULL;
289     
290     PSEC_WINNT_AUTH_IDENTITY_W pAuthDataW = NULL;
291     PSEC_WINNT_AUTH_IDENTITY_A identity  = NULL;
292
293     TRACE("(%s, %s, 0x%08lx, %p, %p, %p, %p, %p, %p)\n",
294      debugstr_a(pszPrincipal), debugstr_a(pszPackage), fCredentialUse,
295      pLogonID, pAuthData, pGetKeyFn, pGetKeyArgument, phCredential, ptsExpiry);
296     
297     if(pszPackage != NULL)
298     {
299         int package_sizeW = MultiByteToWideChar(CP_ACP, 0, pszPackage, -1,
300                 NULL, 0);
301
302         package = HeapAlloc(GetProcessHeap(), 0, package_sizeW * 
303                 sizeof(SEC_WCHAR));
304         MultiByteToWideChar(CP_ACP, 0, pszPackage, -1, package, package_sizeW);
305     }
306
307     
308     if(pAuthData != NULL)
309     {
310         identity = (PSEC_WINNT_AUTH_IDENTITY_A)pAuthData;
311         
312         if(identity->Flags == SEC_WINNT_AUTH_IDENTITY_ANSI)
313         {
314             pAuthDataW = HeapAlloc(GetProcessHeap(), 0, 
315                     sizeof(SEC_WINNT_AUTH_IDENTITY_W));
316
317             if(identity->UserLength != 0)
318             {
319                 user_sizeW = MultiByteToWideChar(CP_ACP, 0, 
320                     (LPCSTR)identity->User, identity->UserLength+1, NULL, 0);
321                 user = HeapAlloc(GetProcessHeap(), 0, user_sizeW * 
322                         sizeof(SEC_WCHAR));
323                 MultiByteToWideChar(CP_ACP, 0, (LPCSTR)identity->User, 
324                     identity->UserLength+1, user, user_sizeW);
325             }
326             else
327             {
328                 user_sizeW = 0;
329             }
330              
331             if(identity->DomainLength != 0)
332             {
333                 domain_sizeW = MultiByteToWideChar(CP_ACP, 0, 
334                     (LPCSTR)identity->Domain, identity->DomainLength+1, NULL, 0);
335                 domain = HeapAlloc(GetProcessHeap(), 0, domain_sizeW 
336                     * sizeof(SEC_WCHAR));
337                 MultiByteToWideChar(CP_ACP, 0, (LPCSTR)identity->Domain, 
338                     identity->DomainLength+1, domain, domain_sizeW);
339             }
340             else
341             {
342                 domain_sizeW = 0;
343             }
344
345             if(identity->PasswordLength != 0)
346             {
347                 passwd_sizeW = MultiByteToWideChar(CP_ACP, 0, 
348                     (LPCSTR)identity->Password, identity->PasswordLength+1, 
349                     NULL, 0);
350                 passwd = HeapAlloc(GetProcessHeap(), 0, passwd_sizeW
351                     * sizeof(SEC_WCHAR));
352                 MultiByteToWideChar(CP_ACP, 0, (LPCSTR)identity->Password,
353                     identity->PasswordLength+1, passwd, passwd_sizeW);
354             }
355             else
356             {
357                 passwd_sizeW = 0;
358             }
359             
360             pAuthDataW->Flags = SEC_WINNT_AUTH_IDENTITY_UNICODE;
361             pAuthDataW->User = user;
362             pAuthDataW->UserLength = user_sizeW;
363             pAuthDataW->Domain = domain;
364             pAuthDataW->DomainLength = domain_sizeW;
365             pAuthDataW->Password = passwd;
366             pAuthDataW->PasswordLength = passwd_sizeW;
367         }
368         else
369         {
370             pAuthDataW = (PSEC_WINNT_AUTH_IDENTITY_W)identity;
371         }
372     }       
373     
374     ret = ntlm_AcquireCredentialsHandleW(NULL, package, fCredentialUse, 
375             pLogonID, pAuthDataW, pGetKeyFn, pGetKeyArgument, phCredential,
376             ptsExpiry);
377     
378     HeapFree(GetProcessHeap(), 0, package);
379     HeapFree(GetProcessHeap(), 0, user);
380     HeapFree(GetProcessHeap(), 0, domain);
381     HeapFree(GetProcessHeap(), 0, passwd);
382     if(pAuthDataW != (PSEC_WINNT_AUTH_IDENTITY_W)identity)
383         HeapFree(GetProcessHeap(), 0, pAuthDataW);
384     
385     return ret;
386 }
387
388 /***********************************************************************
389  *              InitializeSecurityContextW
390  */
391 static SECURITY_STATUS SEC_ENTRY ntlm_InitializeSecurityContextW(
392  PCredHandle phCredential, PCtxtHandle phContext, SEC_WCHAR *pszTargetName, 
393  ULONG fContextReq, ULONG Reserved1, ULONG TargetDataRep, 
394  PSecBufferDesc pInput, ULONG Reserved2, PCtxtHandle phNewContext, 
395  PSecBufferDesc pOutput, ULONG *pfContextAttr, PTimeStamp ptsExpiry)
396 {
397     SECURITY_STATUS ret;
398     PNegoHelper helper;
399     ULONG ctxt_attr = 0;
400     char* buffer;
401     PBYTE bin;
402     int buffer_len, bin_len, max_len = NTLM_MAX_BUF;
403
404     TRACE("%p %p %s %ld %ld %ld %p %ld %p %p %p %p\n", phCredential, phContext,
405      debugstr_w(pszTargetName), fContextReq, Reserved1, TargetDataRep, pInput,
406      Reserved1, phNewContext, pOutput, pfContextAttr, ptsExpiry);
407
408     if(!phCredential)
409         return SEC_E_INVALID_HANDLE;
410
411     /* As the server side of sspi never calls this, make sure that
412      * the handler is a client handler.
413      */
414     helper = (PNegoHelper)phCredential->dwLower;
415     if(helper->mode != NTLM_CLIENT)
416     {
417         TRACE("Helper mode = %d\n", helper->mode);
418         return SEC_E_INVALID_HANDLE;
419     }
420
421     /****************************************
422      * When communicating with the client, there can be the
423      * following reply packets:
424      * YR <base64 blob>         should be sent to the server
425      * PW                       should be sent back to helper with
426      *                          base64 encoded password
427      * AF <base64 blob>         client is done, blob should be
428      *                          sent to server with KK prefixed
429      * BH <char reason>         something broke
430      */
431     /* The squid cache size is 2010 chars, and that's what ntlm_auth uses */
432
433     if (pszTargetName)
434     {
435         TRACE("According to a MS whitepaper pszTargetName is ignored.\n");
436     }
437     /* Handle all the flags */
438     if(fContextReq & ISC_REQ_CONFIDENTIALITY)
439     {
440         FIXME("InitializeSecurityContext(): ISC_REQ_CONFIDENTIALITY stub\n");
441     }
442     if(fContextReq & ISC_REQ_CONNECTION)
443     {
444         /* This is default, so we'll enable it */
445         ctxt_attr |= ISC_RET_CONNECTION;
446     }
447     if(fContextReq & ISC_REQ_EXTENDED_ERROR)
448         FIXME("ISC_REQ_EXTENDED_ERROR\n");
449     if(fContextReq & ISC_REQ_INTEGRITY)
450         FIXME("ISC_REQ_INTEGRITY\n");
451     if(fContextReq & ISC_REQ_MUTUAL_AUTH)
452         FIXME("ISC_REQ_MUTUAL_AUTH\n");
453     if(fContextReq & ISC_REQ_REPLAY_DETECT)
454         FIXME("ISC_REQ_REPLAY_DETECT\n");
455     if(fContextReq & ISC_REQ_SEQUENCE_DETECT)
456         FIXME("ISC_REQ_SEQUENCE_DETECT\n");
457     if(fContextReq & ISC_REQ_STREAM)
458         FIXME("ISC_REQ_STREAM\n");
459
460     /* Done with the flags */
461     if(TargetDataRep == SECURITY_NETWORK_DREP){
462         FIXME("Don't know how to do SECURITY_NETWORK_DREP\n");
463         return SEC_E_UNSUPPORTED_FUNCTION;
464     }
465
466     buffer = HeapAlloc(GetProcessHeap(), 0, sizeof(char) * NTLM_MAX_BUF);
467     bin = HeapAlloc(GetProcessHeap(), 0, sizeof(BYTE) * NTLM_MAX_BUF);
468
469     if((phContext == NULL) && (pInput == NULL))
470     {
471         TRACE("First time in ISC()\n");
472         /* Request a challenge request from ntlm_auth */
473         if(helper->password == NULL)
474         {
475             FIXME("Using empty password for now.\n");
476             lstrcpynA(buffer, "PW AA==", max_len-1);
477         }
478         else
479         {
480             lstrcpynA(buffer, "PW ", max_len-1);
481             if((ret = encodeBase64((unsigned char*)helper->password,
482                         helper->pwlen-2, buffer+3,
483                         max_len-3, &buffer_len)) != SEC_E_OK)
484             {
485                 TRACE("Deleting password!\n");
486                 memset(helper->password, 0, helper->pwlen-2);
487                 HeapFree(GetProcessHeap(), 0, helper->password);
488                 goto end;
489             }
490
491         }
492
493         TRACE("Sending to helper: %s\n", debugstr_a(buffer));
494         if((ret = run_helper(helper, buffer, max_len, &buffer_len)) != SEC_E_OK)
495             goto end;
496
497         TRACE("Helper returned %s\n", debugstr_a(buffer));
498         lstrcpynA(buffer, "YR", max_len-1);
499
500         if((ret = run_helper(helper, buffer, max_len, &buffer_len)) != SEC_E_OK)
501             goto end;
502
503         TRACE("%s\n", buffer);
504
505         if(strncmp(buffer, "YR ", 3) != 0)
506         {
507             /* Something borked */
508             TRACE("Helper returned %c%c\n", buffer[0], buffer[1]);
509             ret = SEC_E_INTERNAL_ERROR;
510             goto end;
511         }
512         if((ret = decodeBase64(buffer+3, buffer_len-3, bin,
513                         max_len-1, &bin_len)) != SEC_E_OK)
514             goto end;
515
516         /* put the decoded client blob into the out buffer */
517
518         ret = SEC_I_CONTINUE_NEEDED;
519     }
520     else
521     {
522         /* handle second call here */
523         /* encode server data to base64 */
524         if (!pInput || !pInput->cBuffers)
525         {
526             ret = SEC_E_INCOMPLETE_MESSAGE;
527             goto end;
528         }
529
530         if (!pInput->pBuffers[0].pvBuffer)
531         {
532             ret = SEC_E_INTERNAL_ERROR;
533             goto end;
534         }
535
536         if(pInput->pBuffers[0].cbBuffer > max_len)
537         {
538             TRACE("pInput->pBuffers[0].cbBuffer is: %ld\n",
539                     pInput->pBuffers[0].cbBuffer);
540             ret = SEC_E_INVALID_TOKEN;
541             goto end;
542         }
543         else
544             bin_len = pInput->pBuffers[0].cbBuffer;
545
546         memcpy(bin, pInput->pBuffers[0].pvBuffer, bin_len);
547
548         lstrcpynA(buffer, "TT ", max_len-1);
549
550         if((ret = encodeBase64(bin, bin_len, buffer+3,
551                         max_len-3, &buffer_len)) != SEC_E_OK)
552             goto end;
553
554         TRACE("Server sent: %s\n", debugstr_a(buffer));
555
556         /* send TT base64 blob to ntlm_auth */
557         if((ret = run_helper(helper, buffer, max_len, &buffer_len)) != SEC_E_OK)
558             goto end;
559
560         TRACE("Helper replied: %s\n", debugstr_a(buffer));
561
562         if( (strncmp(buffer, "KK ", 3) != 0) &&
563                 (strncmp(buffer, "AF ", 3) !=0))
564         {
565             TRACE("Helper returned %c%c\n", buffer[0], buffer[1]);
566             HeapFree(GetProcessHeap(), 0, buffer);
567             HeapFree(GetProcessHeap(), 0, bin);
568             return SEC_E_INVALID_TOKEN;
569         }
570
571         /* decode the blob and send it to server */
572         if((ret = decodeBase64(buffer+3, buffer_len-3, bin, max_len,
573                         &bin_len)) != SEC_E_OK)
574         {
575             HeapFree(GetProcessHeap(), 0, buffer);
576             HeapFree(GetProcessHeap(), 0, bin);
577             return ret;
578         }
579
580         phNewContext->dwUpper = ctxt_attr;
581         phNewContext->dwLower = ret;
582
583         ret = SEC_E_OK;
584     }
585
586     /* put the decoded client blob into the out buffer */
587
588     if (fContextReq & ISC_REQ_ALLOCATE_MEMORY)
589     {
590         if (pOutput)
591         {
592             pOutput->cBuffers = 1;
593             pOutput->pBuffers[0].pvBuffer = SECUR32_ALLOC(bin_len);
594             pOutput->pBuffers[0].cbBuffer = bin_len;
595         }
596     }
597
598     if (!pOutput || !pOutput->cBuffers || pOutput->pBuffers[0].cbBuffer < bin_len)
599     {
600         TRACE("out buffer is NULL or has not enough space\n");
601         ret = SEC_E_BUFFER_TOO_SMALL;
602         goto end;
603     }
604
605     if (!pOutput->pBuffers[0].pvBuffer)
606     {
607         TRACE("out buffer is NULL\n");
608         ret = SEC_E_INTERNAL_ERROR;
609         goto end;
610     }
611
612     pOutput->pBuffers[0].cbBuffer = bin_len;
613     pOutput->pBuffers[0].BufferType = SECBUFFER_DATA;
614     memcpy(pOutput->pBuffers[0].pvBuffer, bin, bin_len);
615
616     if(ret != SEC_I_CONTINUE_NEEDED)
617     {
618         TRACE("Deleting password!\n");
619         if(helper->password)
620             memset(helper->password, 0, helper->pwlen-2);
621         HeapFree(GetProcessHeap(), 0, helper->password);
622     }
623 end:
624     HeapFree(GetProcessHeap(), 0, buffer);
625     HeapFree(GetProcessHeap(), 0, bin);
626     return ret;
627 }
628
629 /***********************************************************************
630  *              InitializeSecurityContextA
631  */
632 static SECURITY_STATUS SEC_ENTRY ntlm_InitializeSecurityContextA(
633  PCredHandle phCredential, PCtxtHandle phContext, SEC_CHAR *pszTargetName,
634  ULONG fContextReq, ULONG Reserved1, ULONG TargetDataRep, 
635  PSecBufferDesc pInput,ULONG Reserved2, PCtxtHandle phNewContext, 
636  PSecBufferDesc pOutput, ULONG *pfContextAttr, PTimeStamp ptsExpiry)
637 {
638     SECURITY_STATUS ret;
639
640     TRACE("%p %p %s %ld %ld %ld %p %ld %p %p %p %p\n", phCredential, phContext,
641      debugstr_a(pszTargetName), fContextReq, Reserved1, TargetDataRep, pInput,
642      Reserved1, phNewContext, pOutput, pfContextAttr, ptsExpiry);
643     
644     if (phCredential)
645     {
646         SEC_WCHAR *target = NULL;
647         if(pszTargetName != NULL)
648         {
649             int target_size = MultiByteToWideChar(CP_ACP, 0, pszTargetName, 
650                 strlen(pszTargetName)+1, NULL, 0);
651             target = HeapAlloc(GetProcessHeap(), 0, target_size * 
652                     sizeof(SEC_WCHAR));
653             MultiByteToWideChar(CP_ACP, 0, pszTargetName, strlen(pszTargetName)+1,
654                 target, target_size);
655         }
656         
657         ret = ntlm_InitializeSecurityContextW(phCredential, phContext, target, 
658                 fContextReq, Reserved1, TargetDataRep, pInput, Reserved2,
659                 phNewContext, pOutput, pfContextAttr, ptsExpiry);
660         
661         HeapFree(GetProcessHeap(), 0, target);
662     }
663     else
664     {
665         ret = SEC_E_INVALID_HANDLE;
666     }
667     return ret;
668 }
669
670 /***********************************************************************
671  *              AcceptSecurityContext
672  */
673 static SECURITY_STATUS SEC_ENTRY ntlm_AcceptSecurityContext(
674  PCredHandle phCredential, PCtxtHandle phContext, PSecBufferDesc pInput,
675  ULONG fContextReq, ULONG TargetDataRep, PCtxtHandle phNewContext, 
676  PSecBufferDesc pOutput, ULONG *pfContextAttr, PTimeStamp ptsExpiry)
677 {
678     SECURITY_STATUS ret;
679
680     TRACE("%p %p %p %ld %ld %p %p %p %p\n", phCredential, phContext, pInput,
681      fContextReq, TargetDataRep, phNewContext, pOutput, pfContextAttr,
682      ptsExpiry);
683     if (phCredential)
684     {
685         PNegoHelper helper = (PNegoHelper)phCredential->dwLower;
686         /* Max size of input data is 2010 byte, as that's the maximum size 
687          * ntlm_auth will handle*/
688         char *buffer = HeapAlloc(GetProcessHeap(), 0, 
689                 sizeof(char) * NTLM_MAX_BUF);
690         PBYTE bin = HeapAlloc(GetProcessHeap(),0, sizeof(BYTE) * NTLM_MAX_BUF);
691         int buffer_len, bin_len, max_len = NTLM_MAX_BUF;
692         ULONG ctxt_attr = 0;
693
694         if(helper->mode != NTLM_SERVER)
695         {
696             HeapFree(GetProcessHeap(), 0, buffer);
697             HeapFree(GetProcessHeap(), 0, bin);
698             return SEC_E_INVALID_HANDLE;
699         }
700
701         /* Handle all the flags */
702         if(fContextReq & ISC_REQ_ALLOCATE_MEMORY)
703         {
704             FIXME("AcceptSecurityContext(): ISC_REQ_ALLOCATE_MEMORY stub\n");
705         }
706         if(fContextReq & ISC_REQ_CONFIDENTIALITY)
707         {
708             FIXME("AcceptSecurityContext(): ISC_REQ_CONFIDENTIALITY stub\n");
709         }
710         if(fContextReq & ISC_REQ_CONNECTION)
711         {
712             /* This is default, so we'll enable it */
713             ctxt_attr |= ISC_RET_CONNECTION;
714         }
715         if(fContextReq & ISC_REQ_EXTENDED_ERROR)
716         {
717             FIXME("AcceptSecurityContext(): ISC_REQ_EXTENDED_ERROR stub\n");
718         }
719         if(fContextReq & ISC_REQ_INTEGRITY)
720         {
721             FIXME("AcceptSecurityContext(): ISC_REQ_INTEGRITY stub\n");
722         }
723         if(fContextReq & ISC_REQ_MUTUAL_AUTH)
724         {
725             FIXME("AcceptSecurityContext(): ISC_REQ_MUTUAL_AUTH stub\n");
726         }
727         if(fContextReq & ISC_REQ_REPLAY_DETECT)
728         {
729             FIXME("AcceptSecurityContext(): ISC_REQ_REPLAY_DETECT stub\n");
730         }
731         if(fContextReq & ISC_REQ_SEQUENCE_DETECT)
732         {
733             FIXME("AcceptSecurityContext(): ISC_REQ_SEQUENCE_DETECT stub\n");
734         }
735         if(fContextReq & ISC_REQ_STREAM)
736         {
737             FIXME("AcceptSecurityContext(): ISC_REQ_STREAM stub\n");
738         }
739         /* Done with the flags */
740         if(TargetDataRep == SECURITY_NETWORK_DREP){
741             FIXME("Don't know how to do SECURITY_NETWORK_DREP\n");
742             HeapFree(GetProcessHeap(), 0, buffer);
743             HeapFree(GetProcessHeap(), 0, bin);
744             return SEC_E_UNSUPPORTED_FUNCTION;
745         }
746
747         
748         if(phContext == NULL)
749         {
750             /* This is the first call to AcceptSecurityHandle */
751             if(pInput == NULL)
752             {
753                 HeapFree(GetProcessHeap(), 0, buffer);
754                 HeapFree(GetProcessHeap(), 0, bin);
755                 return SEC_E_INCOMPLETE_MESSAGE;
756             }
757             
758             if(pInput->cBuffers < 1)
759             {
760                 HeapFree(GetProcessHeap(), 0, buffer);
761                 HeapFree(GetProcessHeap(), 0, bin);
762                 return SEC_E_INCOMPLETE_MESSAGE;
763             }
764
765             if(pInput->pBuffers[0].cbBuffer > max_len)
766             {
767                 HeapFree(GetProcessHeap(), 0, buffer);
768                 HeapFree(GetProcessHeap(), 0, bin);
769                 return SEC_E_INVALID_TOKEN;
770             }
771             else
772                 bin_len = pInput->pBuffers[0].cbBuffer;
773
774             /* This is the YR request from the client, encode to base64 */
775             
776             memcpy(bin, pInput->pBuffers[0].pvBuffer, bin_len);
777
778             lstrcpynA(buffer, "YR ", max_len-1);
779
780             if((ret = encodeBase64(bin, bin_len, buffer+3, max_len-3,
781                         &buffer_len)) != SEC_E_OK)
782             {
783                 HeapFree(GetProcessHeap(), 0, buffer);
784                 HeapFree(GetProcessHeap(), 0, bin);
785                 return ret;
786             }
787             
788             TRACE("Client sent: %s\n", debugstr_a(buffer));
789             
790             if((ret = run_helper(helper, buffer, max_len, &buffer_len)) !=
791                         SEC_E_OK)
792             {
793                 HeapFree(GetProcessHeap(), 0, buffer);
794                 HeapFree(GetProcessHeap(), 0, bin);
795                 return ret;
796             }
797
798             TRACE("Reply from ntlm_auth: %s\n", debugstr_a(buffer));
799             /* The expected answer is TT <base64 blob> */
800
801             if(strncmp(buffer, "TT ", 3) != 0)
802             {
803                 HeapFree(GetProcessHeap(), 0, buffer);
804                 HeapFree(GetProcessHeap(), 0, bin);
805                 return SEC_E_INVALID_TOKEN;
806             }
807
808             if((ret = decodeBase64(buffer+3, buffer_len-3, bin, max_len,
809                             &bin_len)) != SEC_E_OK)
810             {
811                 HeapFree(GetProcessHeap(), 0, buffer);
812                 HeapFree(GetProcessHeap(), 0, bin);
813                 return ret;
814             }
815             
816             /* send this to the client */
817             if(pOutput == NULL)
818             {
819                 HeapFree(GetProcessHeap(), 0, buffer);
820                 HeapFree(GetProcessHeap(), 0, bin);
821                 return SEC_E_INSUFFICIENT_MEMORY;
822             }
823
824             if(pOutput->cBuffers < 1)
825             {
826                 HeapFree(GetProcessHeap(), 0, buffer);
827                 HeapFree(GetProcessHeap(), 0, bin);
828                 return SEC_E_INSUFFICIENT_MEMORY;
829             }
830
831             pOutput->pBuffers[0].cbBuffer = bin_len;
832             pOutput->pBuffers[0].BufferType = SECBUFFER_DATA;
833             memcpy(pOutput->pBuffers[0].pvBuffer, bin, bin_len);
834             ret = SEC_I_CONTINUE_NEEDED;
835             
836         }
837         else
838         {
839             /* we expect a KK request from client */
840             if(pInput == NULL)
841             {
842                 HeapFree(GetProcessHeap(), 0, buffer);
843                 HeapFree(GetProcessHeap(), 0, bin);
844                 return SEC_E_INCOMPLETE_MESSAGE;
845             }
846             
847             if(pInput->cBuffers < 1)
848             {
849                 HeapFree(GetProcessHeap(), 0, buffer);
850                 HeapFree(GetProcessHeap(), 0, bin);
851                 return SEC_E_INCOMPLETE_MESSAGE;
852             }
853
854             if(pInput->pBuffers[0].cbBuffer > max_len)
855             {
856                 HeapFree(GetProcessHeap(), 0, buffer);
857                 HeapFree(GetProcessHeap(), 0, bin);
858                 return SEC_E_INVALID_TOKEN;
859             }
860             else
861                 bin_len = pInput->pBuffers[0].cbBuffer;
862
863             memcpy(bin, pInput->pBuffers[0].pvBuffer, bin_len);
864
865             lstrcpynA(buffer, "KK ", max_len-1);
866
867             if((ret = encodeBase64(bin, bin_len, buffer+3, max_len-3,
868                         &buffer_len)) != SEC_E_OK)
869             {
870                 HeapFree(GetProcessHeap(), 0, buffer);
871                 HeapFree(GetProcessHeap(), 0, bin);
872                 return ret;
873             }
874             
875             TRACE("Client sent: %s\n", debugstr_a(buffer));
876             
877             if((ret = run_helper(helper, buffer, max_len, &buffer_len)) !=
878                         SEC_E_OK)
879             {
880                 HeapFree(GetProcessHeap(), 0, buffer);
881                 HeapFree(GetProcessHeap(), 0, bin);
882                 return ret;
883             }
884
885             TRACE("Reply from ntlm_auth: %s\n", debugstr_a(buffer));
886             
887             if(strncmp(buffer, "AF ", 3) != 0)
888             {
889                 if(strncmp(buffer, "NA ", 3) == 0)
890                 {
891                     HeapFree(GetProcessHeap(), 0, buffer);
892                     HeapFree(GetProcessHeap(), 0, bin);
893                     return SEC_E_LOGON_DENIED;
894                 }
895                 else
896                 {
897                     HeapFree(GetProcessHeap(), 0, buffer);
898                     HeapFree(GetProcessHeap(), 0, bin);
899                     return SEC_E_INVALID_TOKEN;
900                 }
901             }
902             
903             ret = SEC_E_OK;
904         }
905         
906         phNewContext->dwUpper = ctxt_attr;
907         phNewContext->dwLower = ret;
908         HeapFree(GetProcessHeap(), 0, buffer);
909         HeapFree(GetProcessHeap(), 0, bin);
910
911     }
912     else
913     {
914         ret = SEC_E_INVALID_HANDLE;
915     }
916     return ret;
917 }
918
919 /***********************************************************************
920  *              CompleteAuthToken
921  */
922 static SECURITY_STATUS SEC_ENTRY ntlm_CompleteAuthToken(PCtxtHandle phContext,
923  PSecBufferDesc pToken)
924 {
925     SECURITY_STATUS ret;
926
927     TRACE("%p %p\n", phContext, pToken);
928     if (phContext)
929     {
930         ret = SEC_E_UNSUPPORTED_FUNCTION;
931     }
932     else
933     {
934         ret = SEC_E_INVALID_HANDLE;
935     }
936     return ret;
937 }
938
939 /***********************************************************************
940  *              DeleteSecurityContext
941  */
942 static SECURITY_STATUS SEC_ENTRY ntlm_DeleteSecurityContext(PCtxtHandle phContext)
943 {
944     SECURITY_STATUS ret;
945
946     TRACE("%p\n", phContext);
947     if (phContext)
948     {
949         phContext->dwUpper = 0;
950         phContext->dwLower = 0;
951         ret = SEC_E_OK;
952     }
953     else
954     {
955         ret = SEC_E_INVALID_HANDLE;
956     }
957     return ret;
958 }
959
960 /***********************************************************************
961  *              QueryContextAttributesW
962  */
963 static SECURITY_STATUS SEC_ENTRY ntlm_QueryContextAttributesW(PCtxtHandle phContext,
964  unsigned long ulAttribute, void *pBuffer)
965 {
966     SECURITY_STATUS ret;
967
968     /* FIXME: From reading wrapper.h, I think the dwUpper part of a context is
969      * the SecurePackage part and the dwLower part is the actual context 
970      * handle. It should be easy to extract the context attributes from that.
971      */
972     TRACE("%p %ld %p\n", phContext, ulAttribute, pBuffer);
973     if (phContext)
974     {
975         ret = SEC_E_UNSUPPORTED_FUNCTION;
976     }
977     else
978     {
979         ret = SEC_E_INVALID_HANDLE;
980     }
981     return ret;
982 }
983
984 /***********************************************************************
985  *              QueryContextAttributesA
986  */
987 static SECURITY_STATUS SEC_ENTRY ntlm_QueryContextAttributesA(PCtxtHandle phContext,
988  unsigned long ulAttribute, void *pBuffer)
989 {
990     return ntlm_QueryContextAttributesW(phContext, ulAttribute, pBuffer);
991 }
992
993 /***********************************************************************
994  *              ImpersonateSecurityContext
995  */
996 static SECURITY_STATUS SEC_ENTRY ntlm_ImpersonateSecurityContext(PCtxtHandle phContext)
997 {
998     SECURITY_STATUS ret;
999
1000     TRACE("%p\n", phContext);
1001     if (phContext)
1002     {
1003         ret = SEC_E_UNSUPPORTED_FUNCTION;
1004     }
1005     else
1006     {
1007         ret = SEC_E_INVALID_HANDLE;
1008     }
1009     return ret;
1010 }
1011
1012 /***********************************************************************
1013  *              RevertSecurityContext
1014  */
1015 static SECURITY_STATUS SEC_ENTRY ntlm_RevertSecurityContext(PCtxtHandle phContext)
1016 {
1017     SECURITY_STATUS ret;
1018
1019     TRACE("%p\n", phContext);
1020     if (phContext)
1021     {
1022         ret = SEC_E_UNSUPPORTED_FUNCTION;
1023     }
1024     else
1025     {
1026         ret = SEC_E_INVALID_HANDLE;
1027     }
1028     return ret;
1029 }
1030
1031 /***********************************************************************
1032  *              MakeSignature
1033  */
1034 static SECURITY_STATUS SEC_ENTRY ntlm_MakeSignature(PCtxtHandle phContext, ULONG fQOP,
1035  PSecBufferDesc pMessage, ULONG MessageSeqNo)
1036 {
1037     SECURITY_STATUS ret;
1038
1039     TRACE("%p %ld %p %ld\n", phContext, fQOP, pMessage, MessageSeqNo);
1040     if (phContext)
1041     {
1042         ret = SEC_E_UNSUPPORTED_FUNCTION;
1043     }
1044     else
1045     {
1046         ret = SEC_E_INVALID_HANDLE;
1047     }
1048     return ret;
1049 }
1050
1051 /***********************************************************************
1052  *              VerifySignature
1053  */
1054 static SECURITY_STATUS SEC_ENTRY ntlm_VerifySignature(PCtxtHandle phContext,
1055  PSecBufferDesc pMessage, ULONG MessageSeqNo, PULONG pfQOP)
1056 {
1057     SECURITY_STATUS ret;
1058
1059     TRACE("%p %p %ld %p\n", phContext, pMessage, MessageSeqNo, pfQOP);
1060     if (phContext)
1061     {
1062         ret = SEC_E_UNSUPPORTED_FUNCTION;
1063     }
1064     else
1065     {
1066         ret = SEC_E_INVALID_HANDLE;
1067     }
1068     return ret;
1069 }
1070
1071 /***********************************************************************
1072  *             FreeCredentialsHandle
1073  */
1074 static SECURITY_STATUS SEC_ENTRY ntlm_FreeCredentialsHandle(
1075         PCredHandle phCredential)
1076 {
1077     SECURITY_STATUS ret;
1078
1079     if(phCredential){
1080         PNegoHelper helper = (PNegoHelper) phCredential->dwLower;
1081         phCredential->dwUpper = 0;
1082         phCredential->dwLower = 0;
1083         cleanup_helper(helper);
1084         ret = SEC_E_OK;
1085     }
1086     else
1087         ret = SEC_E_OK;
1088     
1089     return ret;
1090 }
1091
1092 static SecurityFunctionTableA ntlmTableA = {
1093     1,
1094     NULL,   /* EnumerateSecurityPackagesA */
1095     ntlm_QueryCredentialsAttributesA,   /* QueryCredentialsAttributesA */
1096     ntlm_AcquireCredentialsHandleA,     /* AcquireCredentialsHandleA */
1097     ntlm_FreeCredentialsHandle,         /* FreeCredentialsHandle */
1098     NULL,   /* Reserved2 */
1099     ntlm_InitializeSecurityContextA,    /* InitializeSecurityContextA */
1100     ntlm_AcceptSecurityContext,         /* AcceptSecurityContext */
1101     ntlm_CompleteAuthToken,             /* CompleteAuthToken */
1102     ntlm_DeleteSecurityContext,         /* DeleteSecurityContext */
1103     NULL,  /* ApplyControlToken */
1104     ntlm_QueryContextAttributesA,       /* QueryContextAttributesA */
1105     ntlm_ImpersonateSecurityContext,    /* ImpersonateSecurityContext */
1106     ntlm_RevertSecurityContext,         /* RevertSecurityContext */
1107     ntlm_MakeSignature,                 /* MakeSignature */
1108     ntlm_VerifySignature,               /* VerifySignature */
1109     FreeContextBuffer,                  /* FreeContextBuffer */
1110     NULL,   /* QuerySecurityPackageInfoA */
1111     NULL,   /* Reserved3 */
1112     NULL,   /* Reserved4 */
1113     NULL,   /* ExportSecurityContext */
1114     NULL,   /* ImportSecurityContextA */
1115     NULL,   /* AddCredentialsA */
1116     NULL,   /* Reserved8 */
1117     NULL,   /* QuerySecurityContextToken */
1118     NULL,   /* EncryptMessage */
1119     NULL,   /* DecryptMessage */
1120     NULL,   /* SetContextAttributesA */
1121 };
1122
1123 static SecurityFunctionTableW ntlmTableW = {
1124     1,
1125     NULL,   /* EnumerateSecurityPackagesW */
1126     ntlm_QueryCredentialsAttributesW,   /* QueryCredentialsAttributesW */
1127     ntlm_AcquireCredentialsHandleW,     /* AcquireCredentialsHandleW */
1128     ntlm_FreeCredentialsHandle,         /* FreeCredentialsHandle */
1129     NULL,   /* Reserved2 */
1130     ntlm_InitializeSecurityContextW,    /* InitializeSecurityContextW */
1131     ntlm_AcceptSecurityContext,         /* AcceptSecurityContext */
1132     ntlm_CompleteAuthToken,             /* CompleteAuthToken */
1133     ntlm_DeleteSecurityContext,         /* DeleteSecurityContext */
1134     NULL,  /* ApplyControlToken */
1135     ntlm_QueryContextAttributesW,       /* QueryContextAttributesW */
1136     ntlm_ImpersonateSecurityContext,    /* ImpersonateSecurityContext */
1137     ntlm_RevertSecurityContext,         /* RevertSecurityContext */
1138     ntlm_MakeSignature,                 /* MakeSignature */
1139     ntlm_VerifySignature,               /* VerifySignature */
1140     FreeContextBuffer,                  /* FreeContextBuffer */
1141     NULL,   /* QuerySecurityPackageInfoW */
1142     NULL,   /* Reserved3 */
1143     NULL,   /* Reserved4 */
1144     NULL,   /* ExportSecurityContext */
1145     NULL,   /* ImportSecurityContextW */
1146     NULL,   /* AddCredentialsW */
1147     NULL,   /* Reserved8 */
1148     NULL,   /* QuerySecurityContextToken */
1149     NULL,   /* EncryptMessage */
1150     NULL,   /* DecryptMessage */
1151     NULL,   /* SetContextAttributesW */
1152 };
1153
1154 #define NTLM_COMMENT \
1155    { 'N', 'T', 'L', 'M', ' ', \
1156      'S', 'e', 'c', 'u', 'r', 'i', 't', 'y', ' ', \
1157      'P', 'a', 'c', 'k', 'a', 'g', 'e', 0}
1158
1159 static CHAR ntlm_comment_A[] = NTLM_COMMENT;
1160 static WCHAR ntlm_comment_W[] = NTLM_COMMENT;
1161
1162 #define NTLM_NAME {'N', 'T', 'L', 'M', 0}
1163
1164 static char ntlm_name_A[] = NTLM_NAME;
1165 static WCHAR ntlm_name_W[] = NTLM_NAME;
1166
1167 /* According to Windows, NTLM has the following capabilities.  */
1168 #define CAPS ( \
1169         SECPKG_FLAG_INTEGRITY | \
1170         SECPKG_FLAG_PRIVACY | \
1171         SECPKG_FLAG_TOKEN_ONLY | \
1172         SECPKG_FLAG_CONNECTION | \
1173         SECPKG_FLAG_MULTI_REQUIRED | \
1174         SECPKG_FLAG_IMPERSONATION | \
1175         SECPKG_FLAG_ACCEPT_WIN32_NAME | \
1176         SECPKG_FLAG_READONLY_WITH_CHECKSUM)
1177
1178 static const SecPkgInfoW infoW = {
1179     CAPS,
1180     1,
1181     RPC_C_AUTHN_WINNT,
1182     NTLM_MAX_BUF,
1183     ntlm_name_W,
1184     ntlm_comment_W
1185 };
1186
1187 static const SecPkgInfoA infoA = {
1188     CAPS,
1189     1,
1190     RPC_C_AUTHN_WINNT,
1191     NTLM_MAX_BUF,
1192     ntlm_name_A,
1193     ntlm_comment_A
1194 };
1195
1196 void SECUR32_initNTLMSP(void)
1197 {   
1198     SECURITY_STATUS ret;
1199     PNegoHelper helper;
1200
1201     SEC_CHAR *args[] = {
1202         "ntlm_auth",
1203         "--version",
1204         NULL };
1205
1206     if((ret = fork_helper(&helper, "ntlm_auth", args)) != SEC_E_OK)
1207     {
1208         /* Cheat and allocate a helper anyway, so cleanup later will work. */
1209         helper = HeapAlloc(GetProcessHeap,0, sizeof(PNegoHelper));
1210         helper->version = -1;
1211     }
1212     else
1213         check_version(helper);
1214
1215     if(helper->version > 2)
1216     {
1217         SecureProvider *provider = SECUR32_addProvider(&ntlmTableA, &ntlmTableW, NULL);
1218         SECUR32_addPackages(provider, 1L, &infoA, &infoW);
1219     }
1220     cleanup_helper(helper);
1221 }