secur32: Use a helper function for creating the signature as this simplifies implemen...
[wine] / dlls / secur32 / ntlm.c
1 /*
2  * Copyright 2005, 2006 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
17  *
18  * This file implements the NTLM security provider.
19  */
20
21 #include <assert.h>
22 #include <stdarg.h>
23 #include <stdio.h>
24 #include "windef.h"
25 #include "winbase.h"
26 #include "winnls.h"
27 #include "rpc.h"
28 #include "sspi.h"
29 #include "lm.h"
30 #include "secur32_priv.h"
31 #include "wine/debug.h"
32
33 WINE_DEFAULT_DEBUG_CHANNEL(secur32);
34
35 #define NTLM_MAX_BUF 1904
36
37
38 static CHAR ntlm_auth[] = "ntlm_auth";
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, %d, %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, %d, %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     static CHAR server_helper_protocol[] = "--helper-protocol=squid-2.5-ntlmssp",
93                 credentials_argv[] = "--use-cached-creds";
94
95     SEC_CHAR *client_user_arg = NULL;
96     SEC_CHAR *client_domain_arg = NULL;
97     SEC_WCHAR *username = NULL, *domain = NULL;
98
99     SEC_CHAR *client_argv[6];
100     SEC_CHAR *server_argv[] = { ntlm_auth,
101         server_helper_protocol,
102         NULL };
103
104     TRACE("(%s, %s, 0x%08x, %p, %p, %p, %p, %p, %p)\n",
105      debugstr_w(pszPrincipal), debugstr_w(pszPackage), fCredentialUse,
106      pLogonID, pAuthData, pGetKeyFn, pGetKeyArgument, phCredential, ptsExpiry);
107
108
109     switch(fCredentialUse)
110     {
111         case SECPKG_CRED_INBOUND:
112             if( (ret = fork_helper(&helper, ntlm_auth, server_argv)) !=
113                     SEC_E_OK)
114             {
115                 phCredential = NULL;
116                 break;
117             }
118             else
119             {
120                 helper->mode = NTLM_SERVER;
121                 phCredential->dwUpper = fCredentialUse;
122                 phCredential->dwLower = (ULONG_PTR)helper;
123             }
124             ret = SEC_E_OK;
125             break;
126         case SECPKG_CRED_OUTBOUND:
127             {
128                 static const char username_arg[] = "--username=";
129                 static const char domain_arg[] = "--domain=";
130                 static char helper_protocol[] = "--helper-protocol=ntlmssp-client-1";
131                 int unixcp_size;
132
133                 if(pAuthData == NULL)
134                 {
135                     LPWKSTA_USER_INFO_1 ui = NULL;
136                     NET_API_STATUS status;
137
138                     status = NetWkstaUserGetInfo(NULL, 1, (LPBYTE *)&ui);
139                     if (status != NERR_Success || ui == NULL)
140                     {
141                         ret = SEC_E_NO_CREDENTIALS;
142                         phCredential = NULL;
143                         break;
144                     }
145                     
146                     username = HeapAlloc(GetProcessHeap(), 0, 
147                             (lstrlenW(ui->wkui1_username)+1) * 
148                             sizeof(SEC_WCHAR));
149                     lstrcpyW(username, ui->wkui1_username);
150                         
151                     /* same for the domain */
152                     domain = HeapAlloc(GetProcessHeap(), 0, 
153                             (lstrlenW(ui->wkui1_logon_domain)+1) * 
154                             sizeof(SEC_WCHAR));
155                     lstrcpyW(domain, ui->wkui1_logon_domain);
156                     NetApiBufferFree(ui);
157                 }
158                 else
159                 {
160                     PSEC_WINNT_AUTH_IDENTITY_W auth_data = 
161                         (PSEC_WINNT_AUTH_IDENTITY_W)pAuthData;
162
163                     if (!auth_data->UserLength || !auth_data->DomainLength)
164                     {
165                         ret = SEC_E_NO_CREDENTIALS;
166                         phCredential = NULL;
167                         break;
168                     }
169                     /* Get username and domain from pAuthData */
170                     username = HeapAlloc(GetProcessHeap(), 0, 
171                             (auth_data->UserLength + 1) * sizeof(SEC_WCHAR));
172                     lstrcpyW(username, auth_data->User);
173
174                     domain = HeapAlloc(GetProcessHeap(), 0,
175                             (auth_data->DomainLength + 1) * sizeof(SEC_WCHAR));
176                     lstrcpyW(domain, auth_data->Domain);
177                 }
178                 TRACE("Username is %s\n", debugstr_w(username));
179                 unixcp_size =  WideCharToMultiByte(CP_UNIXCP, WC_NO_BEST_FIT_CHARS,
180                         username, -1, NULL, 0, NULL, NULL) + sizeof(username_arg);
181                 client_user_arg = HeapAlloc(GetProcessHeap(), 0, unixcp_size);
182                 lstrcpyA(client_user_arg, username_arg);
183                 WideCharToMultiByte(CP_UNIXCP, WC_NO_BEST_FIT_CHARS, username, -1,
184                         client_user_arg + sizeof(username_arg) - 1, 
185                         unixcp_size - sizeof(username_arg) + 1, NULL, NULL);
186
187                 TRACE("Domain name is %s\n", debugstr_w(domain));
188                 unixcp_size = WideCharToMultiByte(CP_UNIXCP, WC_NO_BEST_FIT_CHARS,
189                         domain, -1, NULL, 0,  NULL, NULL) + sizeof(domain_arg);
190                 client_domain_arg = HeapAlloc(GetProcessHeap(), 0, unixcp_size);
191                 lstrcpyA(client_domain_arg, domain_arg);
192                 WideCharToMultiByte(CP_UNIXCP, WC_NO_BEST_FIT_CHARS, domain,
193                         -1, client_domain_arg + sizeof(domain_arg) - 1, 
194                         unixcp_size - sizeof(domain) + 1, NULL, NULL);
195
196                 client_argv[0] = ntlm_auth;
197                 client_argv[1] = helper_protocol;
198                 client_argv[2] = client_user_arg;
199                 client_argv[3] = client_domain_arg;
200                 client_argv[4] = credentials_argv;
201                 client_argv[5] = NULL;
202
203                 if((ret = fork_helper(&helper, ntlm_auth, client_argv)) !=
204                         SEC_E_OK)
205                 {
206                     phCredential = NULL;
207                     break;
208                 }
209                 else
210                 {
211                     helper->mode = NTLM_CLIENT;
212
213                     if(pAuthData != NULL)
214                     {
215                         PSEC_WINNT_AUTH_IDENTITY_W auth_data = 
216                            (PSEC_WINNT_AUTH_IDENTITY_W)pAuthData;
217
218                         if(auth_data->PasswordLength != 0)
219                         {
220                             helper->pwlen = WideCharToMultiByte(CP_UNIXCP, 
221                                 WC_NO_BEST_FIT_CHARS, auth_data->Password, 
222                                 auth_data->PasswordLength+1, NULL, 0, NULL, NULL);
223                         
224                             helper->password = HeapAlloc(GetProcessHeap(), 0, 
225                                     helper->pwlen);
226
227                             WideCharToMultiByte(CP_UNIXCP, WC_NO_BEST_FIT_CHARS,
228                                 auth_data->Password, auth_data->PasswordLength+1,
229                                 helper->password, helper->pwlen, NULL, NULL);
230                         }
231                     }
232            
233                     phCredential->dwUpper = fCredentialUse;
234                     phCredential->dwLower = (ULONG_PTR)helper;
235                     TRACE("ACH phCredential->dwUpper: 0x%08lx, dwLower: 0x%08lx\n",
236                             phCredential->dwUpper, phCredential->dwLower);
237                 }
238                 ret = SEC_E_OK;
239                 break;
240             }
241         case SECPKG_CRED_BOTH:
242             FIXME("AcquireCredentialsHandle: SECPKG_CRED_BOTH stub\n");
243             ret = SEC_E_UNSUPPORTED_FUNCTION;
244             phCredential = NULL;
245             break;
246         default:
247             phCredential = NULL;
248             ret = SEC_E_UNKNOWN_CREDENTIALS;
249     }
250     
251
252     HeapFree(GetProcessHeap(), 0, client_user_arg);
253     HeapFree(GetProcessHeap(), 0, client_domain_arg);
254     HeapFree(GetProcessHeap(), 0, username);
255     HeapFree(GetProcessHeap(), 0, domain);
256
257     return ret;
258 }
259
260 /***********************************************************************
261  *              AcquireCredentialsHandleA
262  */
263 static SECURITY_STATUS SEC_ENTRY ntlm_AcquireCredentialsHandleA(
264  SEC_CHAR *pszPrincipal, SEC_CHAR *pszPackage, ULONG fCredentialUse,
265  PLUID pLogonID, PVOID pAuthData, SEC_GET_KEY_FN pGetKeyFn,
266  PVOID pGetKeyArgument, PCredHandle phCredential, PTimeStamp ptsExpiry)
267 {
268     SECURITY_STATUS ret;
269     int user_sizeW, domain_sizeW, passwd_sizeW;
270     
271     SEC_WCHAR *user = NULL, *domain = NULL, *passwd = NULL, *package = NULL;
272     
273     PSEC_WINNT_AUTH_IDENTITY_W pAuthDataW = NULL;
274     PSEC_WINNT_AUTH_IDENTITY_A identity  = NULL;
275
276     TRACE("(%s, %s, 0x%08x, %p, %p, %p, %p, %p, %p)\n",
277      debugstr_a(pszPrincipal), debugstr_a(pszPackage), fCredentialUse,
278      pLogonID, pAuthData, pGetKeyFn, pGetKeyArgument, phCredential, ptsExpiry);
279     
280     if(pszPackage != NULL)
281     {
282         int package_sizeW = MultiByteToWideChar(CP_ACP, 0, pszPackage, -1,
283                 NULL, 0);
284
285         package = HeapAlloc(GetProcessHeap(), 0, package_sizeW * 
286                 sizeof(SEC_WCHAR));
287         MultiByteToWideChar(CP_ACP, 0, pszPackage, -1, package, package_sizeW);
288     }
289
290     
291     if(pAuthData != NULL)
292     {
293         identity = (PSEC_WINNT_AUTH_IDENTITY_A)pAuthData;
294         
295         if(identity->Flags == SEC_WINNT_AUTH_IDENTITY_ANSI)
296         {
297             pAuthDataW = HeapAlloc(GetProcessHeap(), 0, 
298                     sizeof(SEC_WINNT_AUTH_IDENTITY_W));
299
300             if(identity->UserLength != 0)
301             {
302                 user_sizeW = MultiByteToWideChar(CP_ACP, 0, 
303                     (LPCSTR)identity->User, identity->UserLength+1, NULL, 0);
304                 user = HeapAlloc(GetProcessHeap(), 0, user_sizeW * 
305                         sizeof(SEC_WCHAR));
306                 MultiByteToWideChar(CP_ACP, 0, (LPCSTR)identity->User, 
307                     identity->UserLength+1, user, user_sizeW);
308             }
309             else
310             {
311                 user_sizeW = 0;
312             }
313              
314             if(identity->DomainLength != 0)
315             {
316                 domain_sizeW = MultiByteToWideChar(CP_ACP, 0, 
317                     (LPCSTR)identity->Domain, identity->DomainLength+1, NULL, 0);
318                 domain = HeapAlloc(GetProcessHeap(), 0, domain_sizeW 
319                     * sizeof(SEC_WCHAR));
320                 MultiByteToWideChar(CP_ACP, 0, (LPCSTR)identity->Domain, 
321                     identity->DomainLength+1, domain, domain_sizeW);
322             }
323             else
324             {
325                 domain_sizeW = 0;
326             }
327
328             if(identity->PasswordLength != 0)
329             {
330                 passwd_sizeW = MultiByteToWideChar(CP_ACP, 0, 
331                     (LPCSTR)identity->Password, identity->PasswordLength+1, 
332                     NULL, 0);
333                 passwd = HeapAlloc(GetProcessHeap(), 0, passwd_sizeW
334                     * sizeof(SEC_WCHAR));
335                 MultiByteToWideChar(CP_ACP, 0, (LPCSTR)identity->Password,
336                     identity->PasswordLength+1, passwd, passwd_sizeW);
337             }
338             else
339             {
340                 passwd_sizeW = 0;
341             }
342             
343             pAuthDataW->Flags = SEC_WINNT_AUTH_IDENTITY_UNICODE;
344             pAuthDataW->User = user;
345             pAuthDataW->UserLength = user_sizeW;
346             pAuthDataW->Domain = domain;
347             pAuthDataW->DomainLength = domain_sizeW;
348             pAuthDataW->Password = passwd;
349             pAuthDataW->PasswordLength = passwd_sizeW;
350         }
351         else
352         {
353             pAuthDataW = (PSEC_WINNT_AUTH_IDENTITY_W)identity;
354         }
355     }       
356     
357     ret = ntlm_AcquireCredentialsHandleW(NULL, package, fCredentialUse, 
358             pLogonID, pAuthDataW, pGetKeyFn, pGetKeyArgument, phCredential,
359             ptsExpiry);
360     
361     HeapFree(GetProcessHeap(), 0, package);
362     HeapFree(GetProcessHeap(), 0, user);
363     HeapFree(GetProcessHeap(), 0, domain);
364     HeapFree(GetProcessHeap(), 0, passwd);
365     if(pAuthDataW != (PSEC_WINNT_AUTH_IDENTITY_W)identity)
366         HeapFree(GetProcessHeap(), 0, pAuthDataW);
367     
368     return ret;
369 }
370
371 /***********************************************************************
372  *              InitializeSecurityContextW
373  */
374 static SECURITY_STATUS SEC_ENTRY ntlm_InitializeSecurityContextW(
375  PCredHandle phCredential, PCtxtHandle phContext, SEC_WCHAR *pszTargetName, 
376  ULONG fContextReq, ULONG Reserved1, ULONG TargetDataRep, 
377  PSecBufferDesc pInput, ULONG Reserved2, PCtxtHandle phNewContext, 
378  PSecBufferDesc pOutput, ULONG *pfContextAttr, PTimeStamp ptsExpiry)
379 {
380     SECURITY_STATUS ret;
381     PNegoHelper helper;
382     ULONG ctxt_attr = 0;
383     char* buffer, *want_flags = NULL;
384     PBYTE bin;
385     int buffer_len, bin_len, max_len = NTLM_MAX_BUF;
386
387     TRACE("%p %p %s %d %d %d %p %d %p %p %p %p\n", phCredential, phContext,
388      debugstr_w(pszTargetName), fContextReq, Reserved1, TargetDataRep, pInput,
389      Reserved1, phNewContext, pOutput, pfContextAttr, ptsExpiry);
390
391     if(!phCredential)
392         return SEC_E_INVALID_HANDLE;
393
394     /* As the server side of sspi never calls this, make sure that
395      * the handler is a client handler.
396      */
397     helper = (PNegoHelper)phCredential->dwLower;
398     if(helper->mode != NTLM_CLIENT)
399     {
400         TRACE("Helper mode = %d\n", helper->mode);
401         return SEC_E_INVALID_HANDLE;
402     }
403
404     /****************************************
405      * When communicating with the client, there can be the
406      * following reply packets:
407      * YR <base64 blob>         should be sent to the server
408      * PW                       should be sent back to helper with
409      *                          base64 encoded password
410      * AF <base64 blob>         client is done, blob should be
411      *                          sent to server with KK prefixed
412      * GF <string list>         A string list of negotiated flags
413      * GK <base64 blob>         base64 encoded session key
414      * BH <char reason>         something broke
415      */
416     /* The squid cache size is 2010 chars, and that's what ntlm_auth uses */
417
418     if (pszTargetName)
419     {
420         TRACE("According to a MS whitepaper pszTargetName is ignored.\n");
421     }
422
423     if(TargetDataRep == SECURITY_NETWORK_DREP){
424         TRACE("Setting SECURITY_NETWORK_DREP\n");
425     }
426
427     buffer = HeapAlloc(GetProcessHeap(), 0, sizeof(char) * NTLM_MAX_BUF);
428     bin = HeapAlloc(GetProcessHeap(), 0, sizeof(BYTE) * NTLM_MAX_BUF);
429
430     if((phContext == NULL) && (pInput == NULL))
431     {
432         TRACE("First time in ISC()\n");
433         /* Allocate space for a maximal string of 
434          * "SF NTLMSSP_FEATURE_SIGN NTLMSSP_FEATURE_SEAL
435          * NTLMSSP_FEATURE_SESSION_KEY"
436          */
437         want_flags = HeapAlloc(GetProcessHeap(), 0, 73);
438         if(want_flags == NULL)
439         {
440             ret = SEC_E_INSUFFICIENT_MEMORY;
441             goto isc_end;
442         }
443         lstrcpyA(want_flags, "SF");
444         if(fContextReq & ISC_REQ_CONFIDENTIALITY)
445             lstrcatA(want_flags, " NTLMSSP_FEATURE_SEAL");
446         if(fContextReq & ISC_REQ_CONNECTION)
447         {
448             /* This is default, so we'll enable it */
449             ctxt_attr |= ISC_RET_CONNECTION;
450             lstrcatA(want_flags, " NTLMSSP_FEATURE_SESSION_KEY");
451         }
452         if(fContextReq & ISC_REQ_EXTENDED_ERROR)
453             FIXME("ISC_REQ_EXTENDED_ERROR\n");
454         if(fContextReq & ISC_REQ_INTEGRITY)
455             lstrcatA(want_flags, " NTLMSSP_FEATURE_SIGN");
456         if(fContextReq & ISC_REQ_MUTUAL_AUTH)
457             FIXME("ISC_REQ_MUTUAL_AUTH\n");
458         if(fContextReq & ISC_REQ_REPLAY_DETECT)
459             FIXME("ISC_REQ_REPLAY_DETECT\n");
460         if(fContextReq & ISC_REQ_SEQUENCE_DETECT)
461             FIXME("ISC_REQ_SEQUENCE_DETECT\n");
462         if(fContextReq & ISC_REQ_STREAM)
463             FIXME("ISC_REQ_STREAM\n");
464
465         /* If no password is given, try to use cached credentials. Fall back to an empty
466          * password if this failed. */
467         if(helper->password == NULL)
468         {
469             lstrcpynA(buffer, "OK", max_len-1);
470             if((ret = run_helper(helper, buffer, max_len, &buffer_len)) != SEC_E_OK)
471                 goto isc_end;
472             /* If the helper replied with "PW", using cached credentials failed */
473             if(!strncmp(buffer, "PW", 2))
474             {
475                 TRACE("Using cached credentials failed. Using empty password.\n");
476                 lstrcpynA(buffer, "PW AA==", max_len-1);
477             }
478             else /* Just do a noop on the next run */
479                 lstrcpynA(buffer, "OK", max_len-1);
480         }
481         else
482         {
483             lstrcpynA(buffer, "PW ", max_len-1);
484             if((ret = encodeBase64((unsigned char*)helper->password,
485                         helper->pwlen-2, buffer+3,
486                         max_len-3, &buffer_len)) != SEC_E_OK)
487             {
488                 TRACE("Deleting password!\n");
489                 memset(helper->password, 0, helper->pwlen-2);
490                 HeapFree(GetProcessHeap(), 0, helper->password);
491                 goto isc_end;
492             }
493
494         }
495
496         TRACE("Sending to helper: %s\n", debugstr_a(buffer));
497         if((ret = run_helper(helper, buffer, max_len, &buffer_len)) != SEC_E_OK)
498             goto isc_end;
499
500         TRACE("Helper returned %s\n", debugstr_a(buffer));
501
502         if(lstrlenA(want_flags) > 2)
503         {
504             TRACE("Want flags are '%s'\n", debugstr_a(want_flags));
505             lstrcpynA(buffer, want_flags, max_len-1);
506             if((ret = run_helper(helper, buffer, max_len, &buffer_len)) 
507                     != SEC_E_OK)
508                 goto isc_end;
509             if(!strncmp(buffer, "BH", 2))
510                 TRACE("Helper doesn't understand new command set\n");
511         }
512
513         lstrcpynA(buffer, "YR", max_len-1);
514
515         if((ret = run_helper(helper, buffer, max_len, &buffer_len)) != SEC_E_OK)
516             goto isc_end;
517
518         TRACE("%s\n", buffer);
519
520         if(strncmp(buffer, "YR ", 3) != 0)
521         {
522             /* Something borked */
523             TRACE("Helper returned %c%c\n", buffer[0], buffer[1]);
524             ret = SEC_E_INTERNAL_ERROR;
525             goto isc_end;
526         }
527         if((ret = decodeBase64(buffer+3, buffer_len-3, bin,
528                         max_len-1, &bin_len)) != SEC_E_OK)
529             goto isc_end;
530
531         /* Mask away the NTLMv2 flag, as well as the key exchange flag */
532         bin[14] &= ~0x08;
533         bin[15] &= ~0x40;
534
535         /* put the decoded client blob into the out buffer */
536
537         ret = SEC_I_CONTINUE_NEEDED;
538     }
539     else
540     {
541         /* handle second call here */
542         /* encode server data to base64 */
543         if (!pInput || !pInput->cBuffers)
544         {
545             ret = SEC_E_INCOMPLETE_MESSAGE;
546             goto isc_end;
547         }
548
549         if (!pInput->pBuffers[0].pvBuffer)
550         {
551             ret = SEC_E_INTERNAL_ERROR;
552             goto isc_end;
553         }
554
555         if(pInput->pBuffers[0].cbBuffer > max_len)
556         {
557             TRACE("pInput->pBuffers[0].cbBuffer is: %ld\n",
558                     pInput->pBuffers[0].cbBuffer);
559             ret = SEC_E_INVALID_TOKEN;
560             goto isc_end;
561         }
562         else
563             bin_len = pInput->pBuffers[0].cbBuffer;
564
565         memcpy(bin, pInput->pBuffers[0].pvBuffer, bin_len);
566
567         lstrcpynA(buffer, "TT ", max_len-1);
568
569         if((ret = encodeBase64(bin, bin_len, buffer+3,
570                         max_len-3, &buffer_len)) != SEC_E_OK)
571             goto isc_end;
572
573         TRACE("Server sent: %s\n", debugstr_a(buffer));
574
575         /* send TT base64 blob to ntlm_auth */
576         if((ret = run_helper(helper, buffer, max_len, &buffer_len)) != SEC_E_OK)
577             goto isc_end;
578
579         TRACE("Helper replied: %s\n", debugstr_a(buffer));
580
581         if( (strncmp(buffer, "KK ", 3) != 0) &&
582                 (strncmp(buffer, "AF ", 3) !=0))
583         {
584             TRACE("Helper returned %c%c\n", buffer[0], buffer[1]);
585             ret = SEC_E_INVALID_TOKEN;
586             goto isc_end;
587         }
588
589         /* decode the blob and send it to server */
590         if((ret = decodeBase64(buffer+3, buffer_len-3, bin, max_len,
591                         &bin_len)) != SEC_E_OK)
592         {
593             goto isc_end;
594         }
595
596         phNewContext->dwUpper = ctxt_attr;
597         phNewContext->dwLower = (ULONG_PTR)helper;
598
599         ret = SEC_E_OK;
600     }
601
602     /* put the decoded client blob into the out buffer */
603
604     if (fContextReq & ISC_REQ_ALLOCATE_MEMORY)
605     {
606         if (pOutput)
607         {
608             pOutput->cBuffers = 1;
609             pOutput->pBuffers[0].pvBuffer = SECUR32_ALLOC(bin_len);
610             pOutput->pBuffers[0].cbBuffer = bin_len;
611         }
612     }
613
614     if (!pOutput || !pOutput->cBuffers || pOutput->pBuffers[0].cbBuffer < bin_len)
615     {
616         TRACE("out buffer is NULL or has not enough space\n");
617         ret = SEC_E_BUFFER_TOO_SMALL;
618         goto isc_end;
619     }
620
621     if (!pOutput->pBuffers[0].pvBuffer)
622     {
623         TRACE("out buffer is NULL\n");
624         ret = SEC_E_INTERNAL_ERROR;
625         goto isc_end;
626     }
627
628     pOutput->pBuffers[0].cbBuffer = bin_len;
629     pOutput->pBuffers[0].BufferType = SECBUFFER_DATA;
630     memcpy(pOutput->pBuffers[0].pvBuffer, bin, bin_len);
631
632     if(ret == SEC_E_OK)
633     {
634         TRACE("Getting negotiated flags\n");
635         lstrcpynA(buffer, "GF", max_len - 1);
636         if((ret = run_helper(helper, buffer, max_len, &buffer_len)) != SEC_E_OK)
637             goto isc_end;
638
639         if(buffer_len < 3)
640         {
641             TRACE("No flags negotiated, or helper does not support GF command\n");
642             helper->neg_flags = 0l;
643         }
644         else
645         {
646             TRACE("Negotiated %s\n", debugstr_a(buffer));
647             sscanf(buffer + 3, "%lx", &(helper->neg_flags));
648             TRACE("Stored 0x%08lx as flags\n", helper->neg_flags);
649         }
650
651         TRACE("Getting session key\n");
652         lstrcpynA(buffer, "GK", max_len - 1);
653         if((ret = run_helper(helper, buffer, max_len, &buffer_len)) != SEC_E_OK)
654             goto isc_end;
655
656         if(strncmp(buffer, "BH", 2) == 0)
657         {
658             TRACE("Helper does not understand command or no key negotiated.\n");
659             helper->valid_session_key = FALSE;
660             helper->session_key = HeapAlloc(GetProcessHeap(), 0, 16);
661             /*Generate the dummy session key = MD4(MD4(password))*/
662             if(helper->password)
663             {
664                 SEC_WCHAR *unicode_password;
665                 int passwd_lenW;
666
667                 TRACE("Converting password to unicode.\n");
668                 passwd_lenW = MultiByteToWideChar(CP_ACP, 0,
669                         (LPCSTR)helper->password, helper->pwlen,
670                         NULL, 0);
671                 unicode_password = HeapAlloc(GetProcessHeap(), 0,
672                         passwd_lenW * sizeof(SEC_WCHAR));
673                 MultiByteToWideChar(CP_ACP, 0, (LPCSTR)helper->password,
674                         helper->pwlen, unicode_password, passwd_lenW);
675
676                 SECUR32_CreateNTLMv1SessionKey((PBYTE)unicode_password,
677                         lstrlenW(unicode_password) * sizeof(SEC_WCHAR), helper->session_key);
678
679                 HeapFree(GetProcessHeap(), 0, unicode_password);
680             }
681             else
682                 memset(helper->session_key, 0, 16);
683         }
684         else if(strncmp(buffer, "GK ", 3) == 0)
685         {
686             if((ret = decodeBase64(buffer+3, buffer_len-3, bin, max_len, 
687                             &bin_len)) != SEC_E_OK)
688             {
689                 TRACE("Failed to decode session key\n");
690             }
691             TRACE("Session key is %s\n", debugstr_a(buffer+3));
692             helper->valid_session_key = TRUE;
693             helper->session_key = HeapAlloc(GetProcessHeap(), 0, bin_len);
694             if(!helper->session_key)
695             {
696                 TRACE("Failed to allocate memory for session key\n");
697                 ret = SEC_E_INTERNAL_ERROR;
698                 goto isc_end;
699             }
700             memcpy(helper->session_key, bin, bin_len);
701         }
702
703         helper->crypt.ntlm.a4i = SECUR32_arc4Alloc();
704         SECUR32_arc4Init(helper->crypt.ntlm.a4i, helper->session_key, 16);
705         helper->crypt.ntlm.seq_num = 0l;
706     }
707
708     if(ret != SEC_I_CONTINUE_NEEDED)
709     {
710         TRACE("Deleting password!\n");
711         if(helper->password)
712             memset(helper->password, 0, helper->pwlen-2);
713         HeapFree(GetProcessHeap(), 0, helper->password);
714     }
715 isc_end:
716     HeapFree(GetProcessHeap(), 0, want_flags);
717     HeapFree(GetProcessHeap(), 0, buffer);
718     HeapFree(GetProcessHeap(), 0, bin);
719     return ret;
720 }
721
722 /***********************************************************************
723  *              InitializeSecurityContextA
724  */
725 static SECURITY_STATUS SEC_ENTRY ntlm_InitializeSecurityContextA(
726  PCredHandle phCredential, PCtxtHandle phContext, SEC_CHAR *pszTargetName,
727  ULONG fContextReq, ULONG Reserved1, ULONG TargetDataRep, 
728  PSecBufferDesc pInput,ULONG Reserved2, PCtxtHandle phNewContext, 
729  PSecBufferDesc pOutput, ULONG *pfContextAttr, PTimeStamp ptsExpiry)
730 {
731     SECURITY_STATUS ret;
732
733     TRACE("%p %p %s %d %d %d %p %d %p %p %p %p\n", phCredential, phContext,
734      debugstr_a(pszTargetName), fContextReq, Reserved1, TargetDataRep, pInput,
735      Reserved1, phNewContext, pOutput, pfContextAttr, ptsExpiry);
736     
737     if (phCredential)
738     {
739         SEC_WCHAR *target = NULL;
740         if(pszTargetName != NULL)
741         {
742             int target_size = MultiByteToWideChar(CP_ACP, 0, pszTargetName, 
743                 strlen(pszTargetName)+1, NULL, 0);
744             target = HeapAlloc(GetProcessHeap(), 0, target_size * 
745                     sizeof(SEC_WCHAR));
746             MultiByteToWideChar(CP_ACP, 0, pszTargetName, strlen(pszTargetName)+1,
747                 target, target_size);
748         }
749         
750         ret = ntlm_InitializeSecurityContextW(phCredential, phContext, target, 
751                 fContextReq, Reserved1, TargetDataRep, pInput, Reserved2,
752                 phNewContext, pOutput, pfContextAttr, ptsExpiry);
753         
754         HeapFree(GetProcessHeap(), 0, target);
755     }
756     else
757     {
758         ret = SEC_E_INVALID_HANDLE;
759     }
760     return ret;
761 }
762
763 /***********************************************************************
764  *              AcceptSecurityContext
765  */
766 static SECURITY_STATUS SEC_ENTRY ntlm_AcceptSecurityContext(
767  PCredHandle phCredential, PCtxtHandle phContext, PSecBufferDesc pInput,
768  ULONG fContextReq, ULONG TargetDataRep, PCtxtHandle phNewContext, 
769  PSecBufferDesc pOutput, ULONG *pfContextAttr, PTimeStamp ptsExpiry)
770 {
771     SECURITY_STATUS ret;
772     char *buffer, *want_flags = NULL;
773     PBYTE bin;
774     int buffer_len, bin_len, max_len = NTLM_MAX_BUF;
775     ULONG ctxt_attr = 0;
776     PNegoHelper helper;
777
778     TRACE("%p %p %p %d %d %p %p %p %p\n", phCredential, phContext, pInput,
779      fContextReq, TargetDataRep, phNewContext, pOutput, pfContextAttr,
780      ptsExpiry);
781
782     if (!phCredential)
783         return SEC_E_INVALID_HANDLE;
784
785     helper = (PNegoHelper)phCredential->dwLower;
786
787     buffer = HeapAlloc(GetProcessHeap(), 0, sizeof(char) * NTLM_MAX_BUF);
788     bin    = HeapAlloc(GetProcessHeap(),0, sizeof(BYTE) * NTLM_MAX_BUF);
789
790     if(helper->mode != NTLM_SERVER)
791     {
792         ret = SEC_E_INVALID_HANDLE;
793         goto asc_end;
794     }
795
796     if(TargetDataRep == SECURITY_NETWORK_DREP){
797         TRACE("Using SECURITY_NETWORK_DREP\n");
798     }
799
800     if(phContext == NULL)
801     {
802         /* This is the first call to AcceptSecurityHandle */
803         if(pInput == NULL)
804         {
805             ret = SEC_E_INCOMPLETE_MESSAGE;
806             goto asc_end;
807         }
808
809         if(pInput->cBuffers < 1)
810         {
811             ret = SEC_E_INCOMPLETE_MESSAGE;
812             goto asc_end;
813         }
814
815         if(pInput->pBuffers[0].cbBuffer > max_len)
816         {
817             ret = SEC_E_INVALID_TOKEN;
818             goto asc_end;
819         }
820         else
821             bin_len = pInput->pBuffers[0].cbBuffer;
822
823         /* Handle all the flags */
824         want_flags = HeapAlloc(GetProcessHeap(), 0, 73);
825         if(want_flags == NULL)
826         {
827             TRACE("Failed to allocate memory for the want_flags!\n");
828             ret = SEC_E_INSUFFICIENT_MEMORY;
829             goto asc_end;
830         }
831         lstrcpyA(want_flags, "SF");
832         if(fContextReq & ASC_REQ_ALLOCATE_MEMORY)
833         {
834             FIXME("ASC_REQ_ALLOCATE_MEMORY stub\n");
835         }
836         if(fContextReq & ASC_REQ_CONFIDENTIALITY)
837         {
838             lstrcatA(want_flags, " NTLMSSP_FEATURE_SEAL");
839         }
840         if(fContextReq & ASC_REQ_CONNECTION)
841         {
842             /* This is default, so we'll enable it */
843             lstrcatA(want_flags, " NTLMSSP_FEATURE_SESSION_KEY");
844             ctxt_attr |= ASC_RET_CONNECTION;
845         }
846         if(fContextReq & ASC_REQ_EXTENDED_ERROR)
847         {
848             FIXME("ASC_REQ_EXTENDED_ERROR stub\n");
849         }
850         if(fContextReq & ASC_REQ_INTEGRITY)
851         {
852             lstrcatA(want_flags, " NTLMSSP_FEATURE_SIGN");
853         }
854         if(fContextReq & ASC_REQ_MUTUAL_AUTH)
855         {
856             FIXME("ASC_REQ_MUTUAL_AUTH stub\n");
857         }
858         if(fContextReq & ASC_REQ_REPLAY_DETECT)
859         {
860             FIXME("ASC_REQ_REPLAY_DETECT stub\n");
861         }
862         if(fContextReq & ISC_REQ_SEQUENCE_DETECT)
863         {
864             FIXME("ASC_REQ_SEQUENCE_DETECT stub\n");
865         }
866         if(fContextReq & ISC_REQ_STREAM)
867         {
868             FIXME("ASC_REQ_STREAM stub\n");
869         }
870         /* Done with the flags */
871
872         if(lstrlenA(want_flags) > 3)
873         {
874             TRACE("Server set want_flags: %s\n", debugstr_a(want_flags));
875             lstrcpynA(buffer, want_flags, max_len - 1);
876             if((ret = run_helper(helper, buffer, max_len, &buffer_len)) !=
877                     SEC_E_OK)
878                 goto asc_end;
879             if(!strncmp(buffer, "BH", 2))
880                 TRACE("Helper doesn't understand new command set\n");
881         }
882
883         /* This is the YR request from the client, encode to base64 */
884
885         memcpy(bin, pInput->pBuffers[0].pvBuffer, bin_len);
886
887         lstrcpynA(buffer, "YR ", max_len-1);
888
889         if((ret = encodeBase64(bin, bin_len, buffer+3, max_len-3,
890                     &buffer_len)) != SEC_E_OK)
891         {
892             goto asc_end;
893         }
894
895         TRACE("Client sent: %s\n", debugstr_a(buffer));
896
897         if((ret = run_helper(helper, buffer, max_len, &buffer_len)) !=
898                     SEC_E_OK)
899         {
900             goto asc_end;
901         }
902
903         TRACE("Reply from ntlm_auth: %s\n", debugstr_a(buffer));
904         /* The expected answer is TT <base64 blob> */
905
906         if(strncmp(buffer, "TT ", 3) != 0)
907         {
908             ret = SEC_E_INTERNAL_ERROR;
909             goto asc_end;
910         }
911
912         if((ret = decodeBase64(buffer+3, buffer_len-3, bin, max_len,
913                         &bin_len)) != SEC_E_OK)
914         {
915             goto asc_end;
916         }
917
918         /* send this to the client */
919         if(pOutput == NULL)
920         {
921             ret = SEC_E_INSUFFICIENT_MEMORY;
922             goto asc_end;
923         }
924
925         if(pOutput->cBuffers < 1)
926         {
927             ret = SEC_E_INSUFFICIENT_MEMORY;
928             goto asc_end;
929         }
930
931         pOutput->pBuffers[0].cbBuffer = bin_len;
932         pOutput->pBuffers[0].BufferType = SECBUFFER_DATA;
933         memcpy(pOutput->pBuffers[0].pvBuffer, bin, bin_len);
934         ret = SEC_I_CONTINUE_NEEDED;
935
936     }
937     else
938     {
939         /* we expect a KK request from client */
940         if(pInput == NULL)
941         {
942             ret = SEC_E_INCOMPLETE_MESSAGE;
943             goto asc_end;
944         }
945
946         if(pInput->cBuffers < 1)
947         {
948             ret = SEC_E_INCOMPLETE_MESSAGE;
949             goto asc_end;
950         }
951
952         if(pInput->pBuffers[0].cbBuffer > max_len)
953         {
954             ret = SEC_E_INVALID_TOKEN;
955             goto asc_end;
956         }
957         else
958             bin_len = pInput->pBuffers[0].cbBuffer;
959
960         memcpy(bin, pInput->pBuffers[0].pvBuffer, bin_len);
961
962         lstrcpynA(buffer, "KK ", max_len-1);
963
964         if((ret = encodeBase64(bin, bin_len, buffer+3, max_len-3,
965                     &buffer_len)) != SEC_E_OK)
966         {
967             goto asc_end;
968         }
969
970         TRACE("Client sent: %s\n", debugstr_a(buffer));
971
972         if((ret = run_helper(helper, buffer, max_len, &buffer_len)) !=
973                     SEC_E_OK)
974         {
975             goto asc_end;
976         }
977
978         TRACE("Reply from ntlm_auth: %s\n", debugstr_a(buffer));
979
980         if(strncmp(buffer, "AF ", 3) != 0)
981         {
982             if(strncmp(buffer, "NA ", 3) == 0)
983             {
984                 ret = SEC_E_LOGON_DENIED;
985                 goto asc_end;
986             }
987             else
988             {
989                 ret = SEC_E_INTERNAL_ERROR;
990                 goto asc_end;
991             }
992         }
993         pOutput->pBuffers[0].cbBuffer = 0;
994         ret = SEC_E_OK;
995
996         TRACE("Getting negotiated flags\n");
997         lstrcpynA(buffer, "GF", max_len - 1);
998         if((ret = run_helper(helper, buffer, max_len, &buffer_len)) != SEC_E_OK)
999             goto asc_end;
1000
1001         if(buffer_len < 3)
1002         {
1003             TRACE("No flags negotiated, or helper does not support GF command\n");
1004         }
1005         else
1006         {
1007             TRACE("Negotiated %s\n", debugstr_a(buffer));
1008             sscanf(buffer + 3, "%lx", &(helper->neg_flags));
1009             TRACE("Stored 0x%08lx as flags\n", helper->neg_flags);
1010         }
1011
1012         TRACE("Getting session key\n");
1013         lstrcpynA(buffer, "GK", max_len - 1);
1014         if((ret = run_helper(helper, buffer, max_len, &buffer_len)) != SEC_E_OK)
1015             goto asc_end;
1016
1017         if(buffer_len < 3)
1018             TRACE("Helper does not support GK command\n");
1019         else
1020         {
1021             if(strncmp(buffer, "BH ", 3) == 0)
1022             {
1023                 TRACE("Helper sent %s\n", debugstr_a(buffer+3));
1024                 helper->valid_session_key = FALSE;
1025                 helper->session_key = HeapAlloc(GetProcessHeap(), 0, 16);
1026                 /*FIXME: Generate the dummy session key = MD4(MD4(password))*/
1027                 memset(helper->session_key, 0 , 16);
1028             }
1029             else if(strncmp(buffer, "GK ", 3) == 0)
1030             {
1031                 if((ret = decodeBase64(buffer+3, buffer_len-3, bin, max_len, 
1032                                 &bin_len)) != SEC_E_OK)
1033                 {
1034                     TRACE("Failed to decode session key\n");
1035                 }
1036                 TRACE("Session key is %s\n", debugstr_a(buffer+3));
1037                 helper->valid_session_key = TRUE;
1038                 helper->session_key = HeapAlloc(GetProcessHeap(), 0, 16);
1039                 if(!helper->session_key)
1040                 {
1041                     TRACE("Failed to allocate memory for session key\n");
1042                     ret = SEC_E_INTERNAL_ERROR;
1043                     goto asc_end;
1044                 }
1045                 memcpy(helper->session_key, bin, 16);
1046             }
1047         }
1048         helper->crypt.ntlm.a4i = SECUR32_arc4Alloc();
1049         SECUR32_arc4Init(helper->crypt.ntlm.a4i, helper->session_key, 16);
1050         helper->crypt.ntlm.seq_num = 0l;
1051     }
1052
1053     phNewContext->dwUpper = ctxt_attr;
1054     phNewContext->dwLower = (ULONG_PTR)helper;
1055
1056 asc_end:
1057     HeapFree(GetProcessHeap(), 0, want_flags);
1058     HeapFree(GetProcessHeap(), 0, buffer);
1059     HeapFree(GetProcessHeap(), 0, bin);
1060     return ret;
1061 }
1062
1063 /***********************************************************************
1064  *              CompleteAuthToken
1065  */
1066 static SECURITY_STATUS SEC_ENTRY ntlm_CompleteAuthToken(PCtxtHandle phContext,
1067  PSecBufferDesc pToken)
1068 {
1069     /* We never need to call CompleteAuthToken anyway */
1070     TRACE("%p %p\n", phContext, pToken);
1071     if (!phContext)
1072         return SEC_E_INVALID_HANDLE;
1073     
1074     return SEC_E_OK;
1075 }
1076
1077 /***********************************************************************
1078  *              DeleteSecurityContext
1079  */
1080 static SECURITY_STATUS SEC_ENTRY ntlm_DeleteSecurityContext(PCtxtHandle phContext)
1081 {
1082     PNegoHelper helper;
1083
1084     TRACE("%p\n", phContext);
1085     if (!phContext)
1086         return SEC_E_INVALID_HANDLE;
1087
1088     helper = (PNegoHelper)phContext->dwLower;
1089
1090     phContext->dwUpper = 0;
1091     phContext->dwLower = 0;
1092
1093     SECUR32_arc4Cleanup(helper->crypt.ntlm.a4i);
1094     HeapFree(GetProcessHeap(), 0, helper->session_key);
1095     helper->valid_session_key = FALSE;
1096
1097     return SEC_E_OK;
1098 }
1099
1100 /***********************************************************************
1101  *              QueryContextAttributesW
1102  */
1103 static SECURITY_STATUS SEC_ENTRY ntlm_QueryContextAttributesW(PCtxtHandle phContext,
1104  ULONG ulAttribute, void *pBuffer)
1105 {
1106     TRACE("%p %d %p\n", phContext, ulAttribute, pBuffer);
1107     if (!phContext)
1108         return SEC_E_INVALID_HANDLE;
1109
1110     switch(ulAttribute)
1111     {
1112 #define _x(x) case (x) : FIXME(#x" stub\n"); break
1113         _x(SECPKG_ATTR_ACCESS_TOKEN);
1114         _x(SECPKG_ATTR_AUTHORITY);
1115         _x(SECPKG_ATTR_DCE_INFO);
1116         case SECPKG_ATTR_FLAGS:
1117         {
1118             PSecPkgContext_Flags spcf = (PSecPkgContext_Flags)pBuffer;
1119             PNegoHelper helper = (PNegoHelper)phContext->dwLower;
1120
1121             spcf->Flags = 0;
1122             if(helper->neg_flags & NTLMSSP_NEGOTIATE_SIGN)
1123                 spcf->Flags |= ISC_RET_INTEGRITY;
1124             if(helper->neg_flags & NTLMSSP_NEGOTIATE_SEAL)
1125                 spcf->Flags |= ISC_RET_CONFIDENTIALITY;
1126             return SEC_E_OK;
1127         }
1128         _x(SECPKG_ATTR_KEY_INFO);
1129         _x(SECPKG_ATTR_LIFESPAN);
1130         _x(SECPKG_ATTR_NAMES);
1131         _x(SECPKG_ATTR_NATIVE_NAMES);
1132         _x(SECPKG_ATTR_NEGOTIATION_INFO);
1133         _x(SECPKG_ATTR_PACKAGE_INFO);
1134         _x(SECPKG_ATTR_PASSWORD_EXPIRY);
1135         _x(SECPKG_ATTR_SESSION_KEY);
1136         case SECPKG_ATTR_SIZES:
1137         {
1138             PSecPkgContext_Sizes spcs = (PSecPkgContext_Sizes)pBuffer;
1139             spcs->cbMaxToken = NTLM_MAX_BUF;
1140             spcs->cbMaxSignature = 16;
1141             spcs->cbBlockSize = 0;
1142             spcs->cbSecurityTrailer = 16;
1143             return SEC_E_OK;
1144         }
1145         _x(SECPKG_ATTR_STREAM_SIZES);
1146         _x(SECPKG_ATTR_TARGET_INFORMATION);
1147 #undef _x
1148         default:
1149             TRACE("Unknown value %d passed for ulAttribute\n", ulAttribute);
1150     }
1151
1152     return SEC_E_UNSUPPORTED_FUNCTION;
1153 }
1154
1155 /***********************************************************************
1156  *              QueryContextAttributesA
1157  */
1158 static SECURITY_STATUS SEC_ENTRY ntlm_QueryContextAttributesA(PCtxtHandle phContext,
1159  ULONG ulAttribute, void *pBuffer)
1160 {
1161     return ntlm_QueryContextAttributesW(phContext, ulAttribute, pBuffer);
1162 }
1163
1164 /***********************************************************************
1165  *              ImpersonateSecurityContext
1166  */
1167 static SECURITY_STATUS SEC_ENTRY ntlm_ImpersonateSecurityContext(PCtxtHandle phContext)
1168 {
1169     SECURITY_STATUS ret;
1170
1171     TRACE("%p\n", phContext);
1172     if (phContext)
1173     {
1174         ret = SEC_E_UNSUPPORTED_FUNCTION;
1175     }
1176     else
1177     {
1178         ret = SEC_E_INVALID_HANDLE;
1179     }
1180     return ret;
1181 }
1182
1183 /***********************************************************************
1184  *              RevertSecurityContext
1185  */
1186 static SECURITY_STATUS SEC_ENTRY ntlm_RevertSecurityContext(PCtxtHandle phContext)
1187 {
1188     SECURITY_STATUS ret;
1189
1190     TRACE("%p\n", phContext);
1191     if (phContext)
1192     {
1193         ret = SEC_E_UNSUPPORTED_FUNCTION;
1194     }
1195     else
1196     {
1197         ret = SEC_E_INVALID_HANDLE;
1198     }
1199     return ret;
1200 }
1201
1202 /*************************************************************************
1203  *             ntlm_GetTokenBufferIndex
1204  * Calculates the index of the secbuffer with BufferType == SECBUFFER_TOKEN
1205  * Returns index if found or -1 if not found.
1206  */
1207 static int ntlm_GetTokenBufferIndex(PSecBufferDesc pMessage)
1208 {
1209     UINT i;
1210
1211     TRACE("%p\n", pMessage);
1212
1213     for( i = 0; i < pMessage->cBuffers; ++i )
1214     {
1215         if(pMessage->pBuffers[i].BufferType == SECBUFFER_TOKEN)
1216             return i;
1217     }
1218
1219     return -1;
1220 }
1221
1222 /***********************************************************************
1223  *             ntlm_CreateSignature
1224  * As both MakeSignature and VerifySignature need this, but different keys
1225  * are needed for NTLMv2, the logic goes into a helper function.
1226  */
1227 static SECURITY_STATUS ntlm_CreateSignature(PNegoHelper helper, PSecBufferDesc pMessage,
1228         int token_idx, SignDirection direction)
1229 {
1230     ULONG sign_version = 1;
1231     UINT i;
1232     TRACE("%p, %p, %d\n", helper, pMessage, direction);
1233
1234     if(helper->neg_flags & NTLMSSP_NEGOTIATE_NTLM2 && 
1235             helper->neg_flags & NTLMSSP_NEGOTIATE_SIGN)
1236     {
1237         return SEC_E_UNSUPPORTED_FUNCTION;
1238     }
1239     if(helper->neg_flags & NTLMSSP_NEGOTIATE_SIGN)
1240     {
1241         PBYTE sig = pMessage->pBuffers[token_idx].pvBuffer;
1242         ULONG crc = 0U;
1243
1244         for(i=0; i < pMessage->cBuffers; ++i)
1245         {
1246             if(pMessage->pBuffers[i].BufferType & SECBUFFER_DATA)
1247             {
1248                 crc = ComputeCrc32(pMessage->pBuffers[i].pvBuffer,
1249                     pMessage->pBuffers[i].cbBuffer, crc);
1250             }
1251         }
1252
1253         sig[ 0] = (sign_version >>  0) & 0xff;
1254         sig[ 1] = (sign_version >>  8) & 0xff;
1255         sig[ 2] = (sign_version >> 16) & 0xff;
1256         sig[ 3] = (sign_version >> 24) & 0xff;
1257         memset(sig+4, 0, 4);
1258         sig[ 8] = (crc >>  0) & 0xff;
1259         sig[ 9] = (crc >>  8) & 0xff;
1260         sig[10] = (crc >> 16) & 0xff;
1261         sig[11] = (crc >> 24) & 0xff;
1262         sig[12] = (helper->crypt.ntlm.seq_num >>  0) & 0xff;
1263         sig[13] = (helper->crypt.ntlm.seq_num >>  8) & 0xff;
1264         sig[14] = (helper->crypt.ntlm.seq_num >> 16) & 0xff;
1265         sig[15] = (helper->crypt.ntlm.seq_num >> 24) & 0xff;
1266
1267         ++(helper->crypt.ntlm.seq_num);
1268
1269         SECUR32_arc4Process(helper->crypt.ntlm.a4i, sig+4, 12);
1270         return SEC_E_OK;
1271     }
1272
1273     if(helper->neg_flags & NTLMSSP_NEGOTIATE_ALWAYS_SIGN || helper->neg_flags == 0)
1274     {
1275         TRACE("Creating a dummy signature.\n");
1276         /* A dummy signature is 0x01 followed by 15 bytes of 0x00 */
1277         memset(pMessage->pBuffers[token_idx].pvBuffer, 0, 16);
1278         memset(pMessage->pBuffers[token_idx].pvBuffer, 0x01, 1);
1279         pMessage->pBuffers[token_idx].cbBuffer = 16;
1280         return SEC_E_OK;
1281     }
1282
1283     return SEC_E_UNSUPPORTED_FUNCTION;
1284 }
1285
1286 /***********************************************************************
1287  *              MakeSignature
1288  */
1289 static SECURITY_STATUS SEC_ENTRY ntlm_MakeSignature(PCtxtHandle phContext, ULONG fQOP,
1290  PSecBufferDesc pMessage, ULONG MessageSeqNo)
1291 {
1292     PNegoHelper helper;
1293     int token_idx;
1294
1295     TRACE("%p %d %p %d\n", phContext, fQOP, pMessage, MessageSeqNo);
1296     if (!phContext)
1297         return SEC_E_INVALID_HANDLE;
1298
1299     if(fQOP)
1300         FIXME("Ignoring fQOP 0x%08x\n", fQOP);
1301
1302     if(MessageSeqNo)
1303         FIXME("Ignoring MessageSeqNo\n");
1304
1305     if(!pMessage || !pMessage->pBuffers || pMessage->cBuffers < 2)
1306         return SEC_E_INVALID_TOKEN;
1307
1308     /* If we didn't find a SECBUFFER_TOKEN type buffer */
1309     if((token_idx = ntlm_GetTokenBufferIndex(pMessage)) == -1)
1310         return SEC_E_INVALID_TOKEN;
1311
1312     if(pMessage->pBuffers[token_idx].cbBuffer < 16)
1313         return SEC_E_BUFFER_TOO_SMALL;
1314
1315     helper = (PNegoHelper)phContext->dwLower;
1316     TRACE("Negotiated flags are: 0x%08lx\n", helper->neg_flags);
1317
1318     return ntlm_CreateSignature(helper, pMessage, token_idx, NTLM_SEND);
1319 }
1320
1321 /***********************************************************************
1322  *              VerifySignature
1323  */
1324 static SECURITY_STATUS SEC_ENTRY ntlm_VerifySignature(PCtxtHandle phContext,
1325  PSecBufferDesc pMessage, ULONG MessageSeqNo, PULONG pfQOP)
1326 {
1327     PNegoHelper helper;
1328     ULONG fQOP = 0;
1329     UINT i;
1330     int token_idx;
1331     SECURITY_STATUS ret;
1332     SecBufferDesc local_desc;
1333     PSecBuffer     local_buff;
1334     BYTE          local_sig[16];
1335
1336     TRACE("%p %p %d %p\n", phContext, pMessage, MessageSeqNo, pfQOP);
1337     if(!phContext)
1338         return SEC_E_INVALID_HANDLE;
1339
1340     if(!pMessage || !pMessage->pBuffers || pMessage->cBuffers < 2)
1341         return SEC_E_INVALID_TOKEN;
1342
1343     if((token_idx = ntlm_GetTokenBufferIndex(pMessage)) == -1)
1344         return SEC_E_INVALID_TOKEN;
1345
1346     if(pMessage->pBuffers[token_idx].cbBuffer < 16)
1347         return SEC_E_BUFFER_TOO_SMALL;
1348
1349     if(MessageSeqNo)
1350         FIXME("Ignoring MessageSeqNo\n");
1351
1352     helper = (PNegoHelper)phContext->dwLower;
1353     TRACE("Negotiated flags: 0x%08lx\n", helper->neg_flags);
1354
1355     local_buff = HeapAlloc(GetProcessHeap(), 0, pMessage->cBuffers * sizeof(SecBuffer));
1356
1357     local_desc.ulVersion = SECBUFFER_VERSION;
1358     local_desc.cBuffers = pMessage->cBuffers;
1359     local_desc.pBuffers = local_buff;
1360
1361     for(i=0; i < pMessage->cBuffers; ++i)
1362     {
1363         if(pMessage->pBuffers[i].BufferType == SECBUFFER_TOKEN)
1364         {
1365             local_buff[i].BufferType = SECBUFFER_TOKEN;
1366             local_buff[i].cbBuffer = 16;
1367             local_buff[i].pvBuffer = local_sig;
1368         }
1369         else
1370         {
1371             local_buff[i].BufferType = pMessage->pBuffers[i].BufferType;
1372             local_buff[i].cbBuffer = pMessage->pBuffers[i].cbBuffer;
1373             local_buff[i].pvBuffer = pMessage->pBuffers[i].pvBuffer;
1374         }
1375     }
1376
1377     if((ret = ntlm_CreateSignature(helper, &local_desc, token_idx, NTLM_RECV)) != SEC_E_OK)
1378         return ret;
1379
1380     if(memcmp(((PBYTE)local_buff[token_idx].pvBuffer) + 8,
1381                 ((PBYTE)pMessage->pBuffers[token_idx].pvBuffer) + 8, 8))
1382         ret = SEC_E_MESSAGE_ALTERED;
1383     else
1384         ret = SEC_E_OK;
1385
1386     HeapFree(GetProcessHeap(), 0, local_buff);
1387     pfQOP = &fQOP;
1388
1389     return ret;
1390
1391 }
1392
1393 /***********************************************************************
1394  *             FreeCredentialsHandle
1395  */
1396 static SECURITY_STATUS SEC_ENTRY ntlm_FreeCredentialsHandle(
1397         PCredHandle phCredential)
1398 {
1399     SECURITY_STATUS ret;
1400
1401     if(phCredential){
1402         PNegoHelper helper = (PNegoHelper) phCredential->dwLower;
1403         phCredential->dwUpper = 0;
1404         phCredential->dwLower = 0;
1405         cleanup_helper(helper);
1406         ret = SEC_E_OK;
1407     }
1408     else
1409         ret = SEC_E_OK;
1410     
1411     return ret;
1412 }
1413
1414 /***********************************************************************
1415  *             EncryptMessage
1416  */
1417 static SECURITY_STATUS SEC_ENTRY ntlm_EncryptMessage(PCtxtHandle phContext,
1418         ULONG fQOP, PSecBufferDesc pMessage, ULONG MessageSeqNo)
1419 {
1420     PNegoHelper helper;
1421     UINT i;
1422     int token_idx = -1;
1423
1424     TRACE("(%p %d %p %d)\n", phContext, fQOP, pMessage, MessageSeqNo);
1425
1426     if(!phContext)
1427         return SEC_E_INVALID_HANDLE;
1428
1429     if(fQOP)
1430         FIXME("Ignoring fQOP\n");
1431
1432     if(MessageSeqNo)
1433         FIXME("Ignoring MessageSeqNo\n");
1434
1435     if(!pMessage || !pMessage->pBuffers || pMessage->cBuffers < 2)
1436         return SEC_E_INVALID_TOKEN;
1437
1438     for(i=0; i < pMessage->cBuffers; ++i)
1439     {
1440         if(pMessage->pBuffers[i].BufferType == SECBUFFER_TOKEN)
1441         {
1442             token_idx = i;
1443             break;
1444         }
1445     }
1446
1447     if(token_idx == -1)
1448         return SEC_E_INVALID_TOKEN;
1449
1450     if(pMessage->pBuffers[token_idx].cbBuffer < 16)
1451         return SEC_E_BUFFER_TOO_SMALL;
1452
1453     helper = (PNegoHelper) phContext->dwLower;
1454
1455     if(helper->neg_flags & NTLMSSP_NEGOTIATE_NTLM2)
1456     {
1457         FIXME("Can't handle NTLMv2 encryption yet, aborting\n");
1458         return SEC_E_UNSUPPORTED_FUNCTION;
1459     }
1460     else
1461     {
1462         PBYTE sig = pMessage->pBuffers[token_idx].pvBuffer;
1463         ULONG crc = 0U;
1464         ULONG sign_version = 1l;
1465
1466         for(i=0; i < pMessage->cBuffers; ++i)
1467         {
1468             if(pMessage->pBuffers[i].BufferType & SECBUFFER_DATA)
1469             {
1470                 crc = ComputeCrc32(pMessage->pBuffers[i].pvBuffer,
1471                     pMessage->pBuffers[i].cbBuffer, crc);
1472             }
1473         }
1474
1475         sig[ 0] = (sign_version >>  0) & 0xff;
1476         sig[ 1] = (sign_version >>  8) & 0xff;
1477         sig[ 2] = (sign_version >> 16) & 0xff;
1478         sig[ 3] = (sign_version >> 24) & 0xff;
1479         memset(sig+4, 0, 4);
1480         sig[ 8] = (crc >>  0) & 0xff;
1481         sig[ 9] = (crc >>  8) & 0xff;
1482         sig[10] = (crc >> 16) & 0xff;
1483         sig[11] = (crc >> 24) & 0xff;
1484         sig[12] = (helper->crypt.ntlm.seq_num >>  0) & 0xff;
1485         sig[13] = (helper->crypt.ntlm.seq_num >>  8) & 0xff;
1486         sig[14] = (helper->crypt.ntlm.seq_num >> 16) & 0xff;
1487         sig[15] = (helper->crypt.ntlm.seq_num >> 24) & 0xff;
1488
1489         SECUR32_arc4Process(helper->crypt.ntlm.a4i, pMessage->pBuffers[1].pvBuffer,
1490                 pMessage->pBuffers[1].cbBuffer);
1491         SECUR32_arc4Process(helper->crypt.ntlm.a4i, sig+4, 12);
1492
1493         if(helper->neg_flags & NTLMSSP_NEGOTIATE_ALWAYS_SIGN || helper->neg_flags == 0)
1494             memset(sig+4, 0, 4);
1495
1496         ++(helper->crypt.ntlm.seq_num);
1497
1498     }
1499
1500     return SEC_E_OK;
1501 }
1502
1503 /***********************************************************************
1504  *             DecryptMessage
1505  */
1506 static SECURITY_STATUS SEC_ENTRY ntlm_DecryptMessage(PCtxtHandle phContext,
1507         PSecBufferDesc pMessage, ULONG MessageSeqNo, PULONG pfQOP)
1508 {
1509     SECURITY_STATUS ret;
1510     ULONG ntlmssp_flags_save;
1511     PNegoHelper helper;
1512     UINT i;
1513     int token_idx = -1;
1514     TRACE("(%p %p %d %p)\n", phContext, pMessage, MessageSeqNo, pfQOP);
1515
1516     if(!phContext)
1517         return SEC_E_INVALID_HANDLE;
1518
1519     if(MessageSeqNo)
1520         FIXME("Ignoring MessageSeqNo\n");
1521
1522     if(!pMessage || !pMessage->pBuffers || pMessage->cBuffers < 2)
1523         return SEC_E_INVALID_TOKEN;
1524
1525     for(i=0; i < pMessage->cBuffers; ++i)
1526     {
1527         if(pMessage->pBuffers[i].BufferType == SECBUFFER_TOKEN)
1528         {
1529             token_idx = i;
1530             break;
1531         }
1532     }
1533     if(token_idx == -1)
1534         return SEC_E_INVALID_TOKEN;
1535
1536     if(pMessage->pBuffers[token_idx].cbBuffer < 16)
1537         return SEC_E_BUFFER_TOO_SMALL;
1538
1539     helper = (PNegoHelper) phContext->dwLower;
1540
1541     if(helper->neg_flags & NTLMSSP_NEGOTIATE_NTLM2)
1542     {
1543         FIXME("Can't handle NTLMv2 encryption yet, aborting\n");
1544         return SEC_E_UNSUPPORTED_FUNCTION;
1545     }
1546     else
1547     {
1548         SECUR32_arc4Process(helper->crypt.ntlm.a4i,
1549                 pMessage->pBuffers[1].pvBuffer, pMessage->pBuffers[1].cbBuffer);
1550     }
1551
1552     /* Make sure we use a session key for the signature check, EncryptMessage
1553      * always does that, even in the dummy case */
1554     ntlmssp_flags_save = helper->neg_flags;
1555
1556     helper->neg_flags |= NTLMSSP_NEGOTIATE_SIGN;
1557     ret = ntlm_VerifySignature(phContext, pMessage, MessageSeqNo, pfQOP);
1558
1559     helper->neg_flags = ntlmssp_flags_save;
1560
1561     return ret;
1562 }
1563
1564 static SecurityFunctionTableA ntlmTableA = {
1565     1,
1566     NULL,   /* EnumerateSecurityPackagesA */
1567     ntlm_QueryCredentialsAttributesA,   /* QueryCredentialsAttributesA */
1568     ntlm_AcquireCredentialsHandleA,     /* AcquireCredentialsHandleA */
1569     ntlm_FreeCredentialsHandle,         /* FreeCredentialsHandle */
1570     NULL,   /* Reserved2 */
1571     ntlm_InitializeSecurityContextA,    /* InitializeSecurityContextA */
1572     ntlm_AcceptSecurityContext,         /* AcceptSecurityContext */
1573     ntlm_CompleteAuthToken,             /* CompleteAuthToken */
1574     ntlm_DeleteSecurityContext,         /* DeleteSecurityContext */
1575     NULL,  /* ApplyControlToken */
1576     ntlm_QueryContextAttributesA,       /* QueryContextAttributesA */
1577     ntlm_ImpersonateSecurityContext,    /* ImpersonateSecurityContext */
1578     ntlm_RevertSecurityContext,         /* RevertSecurityContext */
1579     ntlm_MakeSignature,                 /* MakeSignature */
1580     ntlm_VerifySignature,               /* VerifySignature */
1581     FreeContextBuffer,                  /* FreeContextBuffer */
1582     NULL,   /* QuerySecurityPackageInfoA */
1583     NULL,   /* Reserved3 */
1584     NULL,   /* Reserved4 */
1585     NULL,   /* ExportSecurityContext */
1586     NULL,   /* ImportSecurityContextA */
1587     NULL,   /* AddCredentialsA */
1588     NULL,   /* Reserved8 */
1589     NULL,   /* QuerySecurityContextToken */
1590     ntlm_EncryptMessage,                /* EncryptMessage */
1591     ntlm_DecryptMessage,                /* DecryptMessage */
1592     NULL,   /* SetContextAttributesA */
1593 };
1594
1595 static SecurityFunctionTableW ntlmTableW = {
1596     1,
1597     NULL,   /* EnumerateSecurityPackagesW */
1598     ntlm_QueryCredentialsAttributesW,   /* QueryCredentialsAttributesW */
1599     ntlm_AcquireCredentialsHandleW,     /* AcquireCredentialsHandleW */
1600     ntlm_FreeCredentialsHandle,         /* FreeCredentialsHandle */
1601     NULL,   /* Reserved2 */
1602     ntlm_InitializeSecurityContextW,    /* InitializeSecurityContextW */
1603     ntlm_AcceptSecurityContext,         /* AcceptSecurityContext */
1604     ntlm_CompleteAuthToken,             /* CompleteAuthToken */
1605     ntlm_DeleteSecurityContext,         /* DeleteSecurityContext */
1606     NULL,  /* ApplyControlToken */
1607     ntlm_QueryContextAttributesW,       /* QueryContextAttributesW */
1608     ntlm_ImpersonateSecurityContext,    /* ImpersonateSecurityContext */
1609     ntlm_RevertSecurityContext,         /* RevertSecurityContext */
1610     ntlm_MakeSignature,                 /* MakeSignature */
1611     ntlm_VerifySignature,               /* VerifySignature */
1612     FreeContextBuffer,                  /* FreeContextBuffer */
1613     NULL,   /* QuerySecurityPackageInfoW */
1614     NULL,   /* Reserved3 */
1615     NULL,   /* Reserved4 */
1616     NULL,   /* ExportSecurityContext */
1617     NULL,   /* ImportSecurityContextW */
1618     NULL,   /* AddCredentialsW */
1619     NULL,   /* Reserved8 */
1620     NULL,   /* QuerySecurityContextToken */
1621     ntlm_EncryptMessage,                /* EncryptMessage */
1622     ntlm_DecryptMessage,                /* DecryptMessage */
1623     NULL,   /* SetContextAttributesW */
1624 };
1625
1626 #define NTLM_COMMENT \
1627    { 'N', 'T', 'L', 'M', ' ', \
1628      'S', 'e', 'c', 'u', 'r', 'i', 't', 'y', ' ', \
1629      'P', 'a', 'c', 'k', 'a', 'g', 'e', 0}
1630
1631 static CHAR ntlm_comment_A[] = NTLM_COMMENT;
1632 static WCHAR ntlm_comment_W[] = NTLM_COMMENT;
1633
1634 #define NTLM_NAME {'N', 'T', 'L', 'M', 0}
1635
1636 static char ntlm_name_A[] = NTLM_NAME;
1637 static WCHAR ntlm_name_W[] = NTLM_NAME;
1638
1639 /* According to Windows, NTLM has the following capabilities.  */
1640 #define CAPS ( \
1641         SECPKG_FLAG_INTEGRITY | \
1642         SECPKG_FLAG_PRIVACY | \
1643         SECPKG_FLAG_TOKEN_ONLY | \
1644         SECPKG_FLAG_CONNECTION | \
1645         SECPKG_FLAG_MULTI_REQUIRED | \
1646         SECPKG_FLAG_IMPERSONATION | \
1647         SECPKG_FLAG_ACCEPT_WIN32_NAME | \
1648         SECPKG_FLAG_READONLY_WITH_CHECKSUM)
1649
1650 static const SecPkgInfoW infoW = {
1651     CAPS,
1652     1,
1653     RPC_C_AUTHN_WINNT,
1654     NTLM_MAX_BUF,
1655     ntlm_name_W,
1656     ntlm_comment_W
1657 };
1658
1659 static const SecPkgInfoA infoA = {
1660     CAPS,
1661     1,
1662     RPC_C_AUTHN_WINNT,
1663     NTLM_MAX_BUF,
1664     ntlm_name_A,
1665     ntlm_comment_A
1666 };
1667
1668 void SECUR32_initNTLMSP(void)
1669 {
1670     SECURITY_STATUS ret;
1671     PNegoHelper helper;
1672     static CHAR version[] = "--version";
1673
1674     SEC_CHAR *args[] = {
1675         ntlm_auth,
1676         version,
1677         NULL };
1678
1679     if((ret = fork_helper(&helper, ntlm_auth, args)) != SEC_E_OK)
1680     {
1681         /* Cheat and allocate a helper anyway, so cleanup later will work. */
1682         helper = HeapAlloc(GetProcessHeap(),0, sizeof(PNegoHelper));
1683         helper->version = -1;
1684     }
1685     else
1686         check_version(helper);
1687
1688     if(helper->version > 2)
1689     {
1690         SecureProvider *provider = SECUR32_addProvider(&ntlmTableA, &ntlmTableW, NULL);
1691         SECUR32_addPackages(provider, 1L, &infoA, &infoW);
1692     }
1693     else
1694     {
1695         ERR("%s was not found or is outdated. "
1696             "Make sure that ntlm_auth >= 3.x is in your path.\n",
1697             ntlm_auth);
1698     }
1699     cleanup_helper(helper);
1700 }