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