dinput: Use dinput device as a base class for keyboard, mouse and joystick.
[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 /***********************************************************************
39  *              QueryCredentialsAttributesA
40  */
41 static SECURITY_STATUS SEC_ENTRY ntlm_QueryCredentialsAttributesA(
42         PCredHandle phCredential, ULONG ulAttribute, PVOID pBuffer)
43 {
44     SECURITY_STATUS ret;
45
46     TRACE("(%p, %d, %p)\n", phCredential, ulAttribute, pBuffer);
47
48     if(ulAttribute == SECPKG_ATTR_NAMES)
49     {
50         FIXME("SECPKG_CRED_ATTR_NAMES: stub\n");
51         ret = SEC_E_UNSUPPORTED_FUNCTION;
52     }
53     else
54         ret = SEC_E_UNSUPPORTED_FUNCTION;
55     
56     return ret;
57 }
58
59 /***********************************************************************
60  *              QueryCredentialsAttributesW
61  */
62 static SECURITY_STATUS SEC_ENTRY ntlm_QueryCredentialsAttributesW(
63         PCredHandle phCredential, ULONG ulAttribute, PVOID pBuffer)
64 {
65     SECURITY_STATUS ret;
66
67     TRACE("(%p, %d, %p)\n", phCredential, ulAttribute, pBuffer);
68
69     if(ulAttribute == SECPKG_ATTR_NAMES)
70     {
71         FIXME("SECPKG_CRED_ATTR_NAMES: stub\n");
72         ret = SEC_E_UNSUPPORTED_FUNCTION;
73     }
74     else
75         ret = SEC_E_UNSUPPORTED_FUNCTION;
76     
77     return ret;
78 }
79
80 /***********************************************************************
81  *              AcquireCredentialsHandleW
82  */
83 static SECURITY_STATUS SEC_ENTRY ntlm_AcquireCredentialsHandleW(
84  SEC_WCHAR *pszPrincipal, SEC_WCHAR *pszPackage, ULONG fCredentialUse,
85  PLUID pLogonID, PVOID pAuthData, SEC_GET_KEY_FN pGetKeyFn,
86  PVOID pGetKeyArgument, PCredHandle phCredential, PTimeStamp ptsExpiry)
87 {
88     SECURITY_STATUS ret;
89     PNegoHelper helper = NULL;
90     static CHAR ntlm_auth[] = "ntlm_auth",
91                 server_helper_protocol[] = "--helper-protocol=squid-2.5-ntlmssp",
92                 credentials_argv[] = "--use-cached-creds";
93
94     SEC_CHAR *client_user_arg = NULL;
95     SEC_CHAR *client_domain_arg = NULL;
96     SEC_WCHAR *username = NULL, *domain = NULL;
97
98     SEC_CHAR *client_argv[6];
99     SEC_CHAR *server_argv[] = { ntlm_auth,
100         server_helper_protocol,
101         NULL };
102
103     TRACE("(%s, %s, 0x%08x, %p, %p, %p, %p, %p, %p)\n",
104      debugstr_w(pszPrincipal), debugstr_w(pszPackage), fCredentialUse,
105      pLogonID, pAuthData, pGetKeyFn, pGetKeyArgument, phCredential, ptsExpiry);
106
107
108     switch(fCredentialUse)
109     {
110         case SECPKG_CRED_INBOUND:
111             if( (ret = fork_helper(&helper, "ntlm_auth", server_argv)) !=
112                     SEC_E_OK)
113             {
114                 phCredential = NULL;
115                 break;
116             }
117             else
118             {
119                 helper->mode = NTLM_SERVER;
120                 phCredential->dwUpper = fCredentialUse;
121                 phCredential->dwLower = (ULONG_PTR)helper;
122             }
123             ret = SEC_E_OK;
124             break;
125         case SECPKG_CRED_OUTBOUND:
126             {
127                 static const char username_arg[] = "--username=";
128                 static const char domain_arg[] = "--domain=";
129                 static char ntlm_auth[] = "ntlm_auth";
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             if(!helper->session_key)
694                 helper->session_key = HeapAlloc(GetProcessHeap(), 0, bin_len);
695             if(!helper->session_key)
696             {
697                 TRACE("Failed to allocate memory for session key\n");
698                 ret = SEC_E_INTERNAL_ERROR;
699                 goto isc_end;
700             }
701             memcpy(helper->session_key, bin, bin_len);
702         }
703
704         helper->crypt.ntlm.a4i = SECUR32_arc4Alloc();
705         SECUR32_arc4Init(helper->crypt.ntlm.a4i, helper->session_key, 16);
706         helper->crypt.ntlm.seq_num = 0l;
707     }
708
709     if(ret != SEC_I_CONTINUE_NEEDED)
710     {
711         TRACE("Deleting password!\n");
712         if(helper->password)
713             memset(helper->password, 0, helper->pwlen-2);
714         HeapFree(GetProcessHeap(), 0, helper->password);
715     }
716 isc_end:
717     HeapFree(GetProcessHeap(), 0, want_flags);
718     HeapFree(GetProcessHeap(), 0, buffer);
719     HeapFree(GetProcessHeap(), 0, bin);
720     return ret;
721 }
722
723 /***********************************************************************
724  *              InitializeSecurityContextA
725  */
726 static SECURITY_STATUS SEC_ENTRY ntlm_InitializeSecurityContextA(
727  PCredHandle phCredential, PCtxtHandle phContext, SEC_CHAR *pszTargetName,
728  ULONG fContextReq, ULONG Reserved1, ULONG TargetDataRep, 
729  PSecBufferDesc pInput,ULONG Reserved2, PCtxtHandle phNewContext, 
730  PSecBufferDesc pOutput, ULONG *pfContextAttr, PTimeStamp ptsExpiry)
731 {
732     SECURITY_STATUS ret;
733
734     TRACE("%p %p %s %d %d %d %p %d %p %p %p %p\n", phCredential, phContext,
735      debugstr_a(pszTargetName), fContextReq, Reserved1, TargetDataRep, pInput,
736      Reserved1, phNewContext, pOutput, pfContextAttr, ptsExpiry);
737     
738     if (phCredential)
739     {
740         SEC_WCHAR *target = NULL;
741         if(pszTargetName != NULL)
742         {
743             int target_size = MultiByteToWideChar(CP_ACP, 0, pszTargetName, 
744                 strlen(pszTargetName)+1, NULL, 0);
745             target = HeapAlloc(GetProcessHeap(), 0, target_size * 
746                     sizeof(SEC_WCHAR));
747             MultiByteToWideChar(CP_ACP, 0, pszTargetName, strlen(pszTargetName)+1,
748                 target, target_size);
749         }
750         
751         ret = ntlm_InitializeSecurityContextW(phCredential, phContext, target, 
752                 fContextReq, Reserved1, TargetDataRep, pInput, Reserved2,
753                 phNewContext, pOutput, pfContextAttr, ptsExpiry);
754         
755         HeapFree(GetProcessHeap(), 0, target);
756     }
757     else
758     {
759         ret = SEC_E_INVALID_HANDLE;
760     }
761     return ret;
762 }
763
764 /***********************************************************************
765  *              AcceptSecurityContext
766  */
767 static SECURITY_STATUS SEC_ENTRY ntlm_AcceptSecurityContext(
768  PCredHandle phCredential, PCtxtHandle phContext, PSecBufferDesc pInput,
769  ULONG fContextReq, ULONG TargetDataRep, PCtxtHandle phNewContext, 
770  PSecBufferDesc pOutput, ULONG *pfContextAttr, PTimeStamp ptsExpiry)
771 {
772     SECURITY_STATUS ret;
773     char *buffer, *want_flags = NULL;
774     PBYTE bin;
775     int buffer_len, bin_len, max_len = NTLM_MAX_BUF;
776     ULONG ctxt_attr = 0;
777     PNegoHelper helper;
778
779     TRACE("%p %p %p %d %d %p %p %p %p\n", phCredential, phContext, pInput,
780      fContextReq, TargetDataRep, phNewContext, pOutput, pfContextAttr,
781      ptsExpiry);
782
783     if (!phCredential)
784         return SEC_E_INVALID_HANDLE;
785
786     helper = (PNegoHelper)phCredential->dwLower;
787
788     buffer = HeapAlloc(GetProcessHeap(), 0, sizeof(char) * NTLM_MAX_BUF);
789     bin    = HeapAlloc(GetProcessHeap(),0, sizeof(BYTE) * NTLM_MAX_BUF);
790
791     if(helper->mode != NTLM_SERVER)
792     {
793         ret = SEC_E_INVALID_HANDLE;
794         goto asc_end;
795     }
796
797     if(TargetDataRep == SECURITY_NETWORK_DREP){
798         TRACE("Using SECURITY_NETWORK_DREP\n");
799     }
800
801     if(phContext == NULL)
802     {
803         /* This is the first call to AcceptSecurityHandle */
804         if(pInput == NULL)
805         {
806             ret = SEC_E_INCOMPLETE_MESSAGE;
807             goto asc_end;
808         }
809
810         if(pInput->cBuffers < 1)
811         {
812             ret = SEC_E_INCOMPLETE_MESSAGE;
813             goto asc_end;
814         }
815
816         if(pInput->pBuffers[0].cbBuffer > max_len)
817         {
818             ret = SEC_E_INVALID_TOKEN;
819             goto asc_end;
820         }
821         else
822             bin_len = pInput->pBuffers[0].cbBuffer;
823
824         /* Handle all the flags */
825         want_flags = HeapAlloc(GetProcessHeap(), 0, 73);
826         if(want_flags == NULL)
827         {
828             TRACE("Failed to allocate memory for the want_flags!\n");
829             ret = SEC_E_INSUFFICIENT_MEMORY;
830             goto asc_end;
831         }
832         lstrcpyA(want_flags, "SF");
833         if(fContextReq & ASC_REQ_ALLOCATE_MEMORY)
834         {
835             FIXME("ASC_REQ_ALLOCATE_MEMORY stub\n");
836         }
837         if(fContextReq & ASC_REQ_CONFIDENTIALITY)
838         {
839             lstrcatA(want_flags, " NTLMSSP_FEATURE_SEAL");
840         }
841         if(fContextReq & ASC_REQ_CONNECTION)
842         {
843             /* This is default, so we'll enable it */
844             lstrcatA(want_flags, " NTLMSSP_FEATURE_SESSION_KEY");
845             ctxt_attr |= ASC_RET_CONNECTION;
846         }
847         if(fContextReq & ASC_REQ_EXTENDED_ERROR)
848         {
849             FIXME("ASC_REQ_EXTENDED_ERROR stub\n");
850         }
851         if(fContextReq & ASC_REQ_INTEGRITY)
852         {
853             lstrcatA(want_flags, " NTLMSSP_FEATURE_SIGN");
854         }
855         if(fContextReq & ASC_REQ_MUTUAL_AUTH)
856         {
857             FIXME("ASC_REQ_MUTUAL_AUTH stub\n");
858         }
859         if(fContextReq & ASC_REQ_REPLAY_DETECT)
860         {
861             FIXME("ASC_REQ_REPLAY_DETECT stub\n");
862         }
863         if(fContextReq & ISC_REQ_SEQUENCE_DETECT)
864         {
865             FIXME("ASC_REQ_SEQUENCE_DETECT stub\n");
866         }
867         if(fContextReq & ISC_REQ_STREAM)
868         {
869             FIXME("ASC_REQ_STREAM stub\n");
870         }
871         /* Done with the flags */
872
873         if(lstrlenA(want_flags) > 3)
874         {
875             TRACE("Server set want_flags: %s\n", debugstr_a(want_flags));
876             lstrcpynA(buffer, want_flags, max_len - 1);
877             if((ret = run_helper(helper, buffer, max_len, &buffer_len)) !=
878                     SEC_E_OK)
879                 goto asc_end;
880             if(!strncmp(buffer, "BH", 2))
881                 TRACE("Helper doesn't understand new command set\n");
882         }
883
884         /* This is the YR request from the client, encode to base64 */
885
886         memcpy(bin, pInput->pBuffers[0].pvBuffer, bin_len);
887
888         lstrcpynA(buffer, "YR ", max_len-1);
889
890         if((ret = encodeBase64(bin, bin_len, buffer+3, max_len-3,
891                     &buffer_len)) != SEC_E_OK)
892         {
893             goto asc_end;
894         }
895
896         TRACE("Client sent: %s\n", debugstr_a(buffer));
897
898         if((ret = run_helper(helper, buffer, max_len, &buffer_len)) !=
899                     SEC_E_OK)
900         {
901             goto asc_end;
902         }
903
904         TRACE("Reply from ntlm_auth: %s\n", debugstr_a(buffer));
905         /* The expected answer is TT <base64 blob> */
906
907         if(strncmp(buffer, "TT ", 3) != 0)
908         {
909             ret = SEC_E_INTERNAL_ERROR;
910             goto asc_end;
911         }
912
913         if((ret = decodeBase64(buffer+3, buffer_len-3, bin, max_len,
914                         &bin_len)) != SEC_E_OK)
915         {
916             goto asc_end;
917         }
918
919         /* send this to the client */
920         if(pOutput == NULL)
921         {
922             ret = SEC_E_INSUFFICIENT_MEMORY;
923             goto asc_end;
924         }
925
926         if(pOutput->cBuffers < 1)
927         {
928             ret = SEC_E_INSUFFICIENT_MEMORY;
929             goto asc_end;
930         }
931
932         pOutput->pBuffers[0].cbBuffer = bin_len;
933         pOutput->pBuffers[0].BufferType = SECBUFFER_DATA;
934         memcpy(pOutput->pBuffers[0].pvBuffer, bin, bin_len);
935         ret = SEC_I_CONTINUE_NEEDED;
936
937     }
938     else
939     {
940         /* we expect a KK request from client */
941         if(pInput == NULL)
942         {
943             ret = SEC_E_INCOMPLETE_MESSAGE;
944             goto asc_end;
945         }
946
947         if(pInput->cBuffers < 1)
948         {
949             ret = SEC_E_INCOMPLETE_MESSAGE;
950             goto asc_end;
951         }
952
953         if(pInput->pBuffers[0].cbBuffer > max_len)
954         {
955             ret = SEC_E_INVALID_TOKEN;
956             goto asc_end;
957         }
958         else
959             bin_len = pInput->pBuffers[0].cbBuffer;
960
961         memcpy(bin, pInput->pBuffers[0].pvBuffer, bin_len);
962
963         lstrcpynA(buffer, "KK ", max_len-1);
964
965         if((ret = encodeBase64(bin, bin_len, buffer+3, max_len-3,
966                     &buffer_len)) != SEC_E_OK)
967         {
968             goto asc_end;
969         }
970
971         TRACE("Client sent: %s\n", debugstr_a(buffer));
972
973         if((ret = run_helper(helper, buffer, max_len, &buffer_len)) !=
974                     SEC_E_OK)
975         {
976             goto asc_end;
977         }
978
979         TRACE("Reply from ntlm_auth: %s\n", debugstr_a(buffer));
980
981         if(strncmp(buffer, "AF ", 3) != 0)
982         {
983             if(strncmp(buffer, "NA ", 3) == 0)
984             {
985                 ret = SEC_E_LOGON_DENIED;
986                 goto asc_end;
987             }
988             else
989             {
990                 ret = SEC_E_INTERNAL_ERROR;
991                 goto asc_end;
992             }
993         }
994         pOutput->pBuffers[0].cbBuffer = 0;
995         ret = SEC_E_OK;
996
997         TRACE("Getting negotiated flags\n");
998         lstrcpynA(buffer, "GF", max_len - 1);
999         if((ret = run_helper(helper, buffer, max_len, &buffer_len)) != SEC_E_OK)
1000             goto asc_end;
1001
1002         if(buffer_len < 3)
1003         {
1004             TRACE("No flags negotiated, or helper does not support GF command\n");
1005         }
1006         else
1007         {
1008             TRACE("Negotiated %s\n", debugstr_a(buffer));
1009             sscanf(buffer + 3, "%lx", &(helper->neg_flags));
1010             TRACE("Stored 0x%08lx as flags\n", helper->neg_flags);
1011         }
1012
1013         TRACE("Getting session key\n");
1014         lstrcpynA(buffer, "GK", max_len - 1);
1015         if((ret = run_helper(helper, buffer, max_len, &buffer_len)) != SEC_E_OK)
1016             goto asc_end;
1017
1018         if(buffer_len < 3)
1019             TRACE("Helper does not support GK command\n");
1020         else
1021         {
1022             if(strncmp(buffer, "BH ", 3) == 0)
1023             {
1024                 TRACE("Helper sent %s\n", debugstr_a(buffer+3));
1025                 helper->valid_session_key = FALSE;
1026                 helper->session_key = HeapAlloc(GetProcessHeap(), 0, 16);
1027                 /*FIXME: Generate the dummy session key = MD4(MD4(password))*/
1028                 memset(helper->session_key, 0 , 16);
1029             }
1030             else if(strncmp(buffer, "GK ", 3) == 0)
1031             {
1032                 if((ret = decodeBase64(buffer+3, buffer_len-3, bin, max_len, 
1033                                 &bin_len)) != SEC_E_OK)
1034                 {
1035                     TRACE("Failed to decode session key\n");
1036                 }
1037                 TRACE("Session key is %s\n", debugstr_a(buffer+3));
1038                 helper->valid_session_key = TRUE;
1039                 if(!helper->session_key)
1040                     helper->session_key = HeapAlloc(GetProcessHeap(), 0, 16);
1041                 if(!helper->session_key)
1042                 {
1043                     TRACE("Failed to allocate memory for session key\n");
1044                     ret = SEC_E_INTERNAL_ERROR;
1045                     goto asc_end;
1046                 }
1047                 memcpy(helper->session_key, bin, 16);
1048             }
1049         }
1050         helper->crypt.ntlm.a4i = SECUR32_arc4Alloc();
1051         SECUR32_arc4Init(helper->crypt.ntlm.a4i, helper->session_key, 16);
1052         helper->crypt.ntlm.seq_num = 0l;
1053     }
1054
1055     phNewContext->dwUpper = ctxt_attr;
1056     phNewContext->dwLower = (ULONG_PTR)helper;
1057
1058 asc_end:
1059     HeapFree(GetProcessHeap(), 0, want_flags);
1060     HeapFree(GetProcessHeap(), 0, buffer);
1061     HeapFree(GetProcessHeap(), 0, bin);
1062     return ret;
1063 }
1064
1065 /***********************************************************************
1066  *              CompleteAuthToken
1067  */
1068 static SECURITY_STATUS SEC_ENTRY ntlm_CompleteAuthToken(PCtxtHandle phContext,
1069  PSecBufferDesc pToken)
1070 {
1071     /* We never need to call CompleteAuthToken anyway */
1072     TRACE("%p %p\n", phContext, pToken);
1073     if (!phContext)
1074         return SEC_E_INVALID_HANDLE;
1075     
1076     return SEC_E_OK;
1077 }
1078
1079 /***********************************************************************
1080  *              DeleteSecurityContext
1081  */
1082 static SECURITY_STATUS SEC_ENTRY ntlm_DeleteSecurityContext(PCtxtHandle phContext)
1083 {
1084     SECURITY_STATUS ret;
1085
1086     TRACE("%p\n", phContext);
1087     if (phContext)
1088     {
1089         phContext->dwUpper = 0;
1090         phContext->dwLower = 0;
1091         ret = SEC_E_OK;
1092     }
1093     else
1094     {
1095         ret = SEC_E_INVALID_HANDLE;
1096     }
1097     return ret;
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  *              MakeSignature
1204  */
1205 static SECURITY_STATUS SEC_ENTRY ntlm_MakeSignature(PCtxtHandle phContext, ULONG fQOP,
1206  PSecBufferDesc pMessage, ULONG MessageSeqNo)
1207 {
1208     PNegoHelper helper;
1209     ULONG sign_version = 1;
1210
1211     TRACE("%p %d %p %d\n", phContext, fQOP, pMessage, MessageSeqNo);
1212     if (!phContext)
1213         return SEC_E_INVALID_HANDLE;
1214
1215     if(fQOP)
1216         FIXME("Ignoring fQOP 0x%08x\n", fQOP);
1217
1218     if(MessageSeqNo)
1219         FIXME("Ignoring MessageSeqNo\n");
1220
1221     if(!pMessage || !pMessage->pBuffers || pMessage->cBuffers < 2 ||
1222             pMessage->pBuffers[0].BufferType != SECBUFFER_TOKEN ||
1223             !pMessage->pBuffers[0].pvBuffer)
1224         return SEC_E_INVALID_TOKEN;
1225
1226     if(pMessage->pBuffers[0].cbBuffer < 16)
1227         return SEC_E_BUFFER_TOO_SMALL;
1228
1229     helper = (PNegoHelper)phContext->dwLower;
1230     TRACE("Negotiated flags are: 0x%08lx\n", helper->neg_flags);
1231     if(helper->neg_flags & NTLMSSP_NEGOTIATE_NTLM2)
1232     {
1233         FIXME("Can't handle NTLMv2 signing yet. Aborting.\n");
1234         return SEC_E_UNSUPPORTED_FUNCTION;
1235     }
1236     if(helper->neg_flags & NTLMSSP_NEGOTIATE_SIGN)
1237     {
1238         PBYTE sig = pMessage->pBuffers[0].pvBuffer;
1239         ULONG crc = ComputeCrc32(pMessage->pBuffers[1].pvBuffer,
1240                 pMessage->pBuffers[1].cbBuffer);
1241
1242         sig[ 0] = (sign_version >>  0) & 0xff;
1243         sig[ 1] = (sign_version >>  8) & 0xff;
1244         sig[ 2] = (sign_version >> 16) & 0xff;
1245         sig[ 3] = (sign_version >> 24) & 0xff;
1246         memset(sig+4, 0, 4);
1247         sig[ 8] = (crc >>  0) & 0xff;
1248         sig[ 9] = (crc >>  8) & 0xff;
1249         sig[10] = (crc >> 16) & 0xff;
1250         sig[11] = (crc >> 24) & 0xff;
1251         sig[12] = (helper->crypt.ntlm.seq_num >>  0) & 0xff;
1252         sig[13] = (helper->crypt.ntlm.seq_num >>  8) & 0xff;
1253         sig[14] = (helper->crypt.ntlm.seq_num >> 16) & 0xff;
1254         sig[15] = (helper->crypt.ntlm.seq_num >> 24) & 0xff;
1255
1256         ++(helper->crypt.ntlm.seq_num);
1257
1258         SECUR32_arc4Process(helper->crypt.ntlm.a4i, sig+4, 12);
1259         return SEC_E_OK;
1260     }
1261
1262     if(helper->neg_flags & NTLMSSP_NEGOTIATE_KEY_EXCHANGE)
1263     {
1264         FIXME("Can't handle encrypted session key yet. Aborting.\n");
1265         return SEC_E_UNSUPPORTED_FUNCTION;
1266     }
1267
1268     if(helper->neg_flags & NTLMSSP_NEGOTIATE_ALWAYS_SIGN || helper->neg_flags == 0)
1269     {
1270         TRACE("Generating dummy signature\n");
1271         /* A dummy signature is 0x01 followed by 15 bytes of 0x00 */
1272         memset(pMessage->pBuffers[0].pvBuffer, 0, 16);
1273         memset(pMessage->pBuffers[0].pvBuffer, 0x01, 1);
1274         pMessage->pBuffers[0].cbBuffer = 16;
1275         return SEC_E_OK;
1276     }
1277
1278     return SEC_E_UNSUPPORTED_FUNCTION;
1279 }
1280
1281 /***********************************************************************
1282  *              VerifySignature
1283  */
1284 static SECURITY_STATUS SEC_ENTRY ntlm_VerifySignature(PCtxtHandle phContext,
1285  PSecBufferDesc pMessage, ULONG MessageSeqNo, PULONG pfQOP)
1286 {
1287     PNegoHelper helper;
1288     ULONG fQOP = 0;
1289
1290     TRACE("%p %p %d %p\n", phContext, pMessage, MessageSeqNo, pfQOP);
1291     if(!phContext)
1292         return SEC_E_INVALID_HANDLE;
1293
1294     if(!pMessage || !pMessage->pBuffers || pMessage->cBuffers < 2 ||
1295             pMessage->pBuffers[0].BufferType != SECBUFFER_TOKEN ||
1296             !pMessage->pBuffers[0].pvBuffer)
1297         return SEC_E_INVALID_TOKEN;
1298
1299     if(pMessage->pBuffers[0].cbBuffer < 16)
1300         return SEC_E_BUFFER_TOO_SMALL;
1301
1302     if(MessageSeqNo)
1303         FIXME("Ignoring MessageSeqNo\n");
1304
1305     helper = (PNegoHelper)phContext->dwLower;
1306     TRACE("Negotiated flags: 0x%08lx\n", helper->neg_flags);
1307
1308     if(helper->neg_flags & NTLMSSP_NEGOTIATE_NTLM2)
1309     {
1310         FIXME("Can't handle NTLMv2 signing yet. Aborting.\n");
1311         return SEC_E_UNSUPPORTED_FUNCTION;
1312     }
1313
1314     if(helper->neg_flags & NTLMSSP_NEGOTIATE_SIGN)
1315     {
1316         SecBufferDesc local_desc;
1317         SecBuffer     local_buff[2];
1318         BYTE          local_sig[16];
1319
1320         local_desc.ulVersion = SECBUFFER_VERSION;
1321         local_desc.cBuffers = 2;
1322         local_desc.pBuffers = local_buff;
1323         local_buff[0].BufferType = SECBUFFER_TOKEN;
1324         local_buff[0].cbBuffer = 16;
1325         local_buff[0].pvBuffer = local_sig;
1326         local_buff[1].BufferType = SECBUFFER_DATA;
1327         local_buff[1].cbBuffer = pMessage->pBuffers[1].cbBuffer;
1328         local_buff[1].pvBuffer = pMessage->pBuffers[1].pvBuffer;
1329
1330         ntlm_MakeSignature(phContext, fQOP, &local_desc, MessageSeqNo);
1331
1332         if(memcmp(((PBYTE)local_buff[0].pvBuffer) + 8, ((PBYTE)pMessage->pBuffers[0].pvBuffer) + 8, 8))
1333             return SEC_E_MESSAGE_ALTERED;
1334
1335         return SEC_E_OK;
1336     }
1337
1338     if(helper->neg_flags & NTLMSSP_NEGOTIATE_KEY_EXCHANGE)
1339     {
1340         FIXME("Can't handle encrypted session keys yet. Aborting.\n");
1341         return SEC_E_UNSUPPORTED_FUNCTION;
1342     }
1343
1344     if(helper->neg_flags & NTLMSSP_NEGOTIATE_ALWAYS_SIGN || helper->neg_flags == 0)
1345     {
1346         const BYTE dummy_sig[] = {0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1347             0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
1348         TRACE("Assuming dummy signature.\n");
1349         if(memcmp(pMessage->pBuffers[0].pvBuffer, dummy_sig, sizeof(dummy_sig)) != 0)
1350         {
1351             TRACE("Failed to verify the packet signature. Not a dummy signature?\n");
1352             return SEC_E_MESSAGE_ALTERED;
1353         }
1354         else
1355         {
1356             pfQOP = &fQOP;
1357             return SEC_E_OK;
1358         }
1359     }
1360
1361     return SEC_E_UNSUPPORTED_FUNCTION;
1362 }
1363
1364 /***********************************************************************
1365  *             FreeCredentialsHandle
1366  */
1367 static SECURITY_STATUS SEC_ENTRY ntlm_FreeCredentialsHandle(
1368         PCredHandle phCredential)
1369 {
1370     SECURITY_STATUS ret;
1371
1372     if(phCredential){
1373         PNegoHelper helper = (PNegoHelper) phCredential->dwLower;
1374         phCredential->dwUpper = 0;
1375         phCredential->dwLower = 0;
1376         HeapFree(GetProcessHeap(), 0, helper->session_key);
1377         cleanup_helper(helper);
1378         ret = SEC_E_OK;
1379     }
1380     else
1381         ret = SEC_E_OK;
1382     
1383     return ret;
1384 }
1385
1386 /***********************************************************************
1387  *             EncryptMessage
1388  */
1389 static SECURITY_STATUS SEC_ENTRY ntlm_EncryptMessage(PCtxtHandle phContext,
1390         ULONG fQOP, PSecBufferDesc pMessage, ULONG MessageSeqNo)
1391 {
1392     PNegoHelper helper;
1393     TRACE("(%p %d %p %d)\n", phContext, fQOP, pMessage, MessageSeqNo);
1394
1395     if(!phContext)
1396         return SEC_E_INVALID_HANDLE;
1397
1398     if(fQOP)
1399         FIXME("Ignoring fQOP\n");
1400
1401     if(MessageSeqNo)
1402         FIXME("Ignoring MessageSeqNo\n");
1403
1404     if(!pMessage || !pMessage->pBuffers || pMessage->cBuffers < 2 ||
1405             pMessage->pBuffers[0].BufferType != SECBUFFER_TOKEN ||
1406             !pMessage->pBuffers[0].pvBuffer)
1407         return SEC_E_INVALID_TOKEN;
1408
1409     if(pMessage->pBuffers[0].cbBuffer < 16)
1410         return SEC_E_BUFFER_TOO_SMALL;
1411
1412     helper = (PNegoHelper) phContext->dwLower;
1413
1414     if(helper->neg_flags & NTLMSSP_NEGOTIATE_NTLM2)
1415     {
1416         FIXME("Can't handle NTLMv2 encryption yet, aborting\n");
1417         return SEC_E_UNSUPPORTED_FUNCTION;
1418     }
1419     else
1420     {
1421         PBYTE sig = pMessage->pBuffers[0].pvBuffer;
1422         ULONG crc = ComputeCrc32(pMessage->pBuffers[1].pvBuffer,
1423                 pMessage->pBuffers[1].cbBuffer);
1424         ULONG sign_version = 1l;
1425
1426         sig[ 0] = (sign_version >>  0) & 0xff;
1427         sig[ 1] = (sign_version >>  8) & 0xff;
1428         sig[ 2] = (sign_version >> 16) & 0xff;
1429         sig[ 3] = (sign_version >> 24) & 0xff;
1430         memset(sig+4, 0, 4);
1431         sig[ 8] = (crc >>  0) & 0xff;
1432         sig[ 9] = (crc >>  8) & 0xff;
1433         sig[10] = (crc >> 16) & 0xff;
1434         sig[11] = (crc >> 24) & 0xff;
1435         sig[12] = (helper->crypt.ntlm.seq_num >>  0) & 0xff;
1436         sig[13] = (helper->crypt.ntlm.seq_num >>  8) & 0xff;
1437         sig[14] = (helper->crypt.ntlm.seq_num >> 16) & 0xff;
1438         sig[15] = (helper->crypt.ntlm.seq_num >> 24) & 0xff;
1439
1440         SECUR32_arc4Process(helper->crypt.ntlm.a4i, pMessage->pBuffers[1].pvBuffer,
1441                 pMessage->pBuffers[1].cbBuffer);
1442         SECUR32_arc4Process(helper->crypt.ntlm.a4i, sig+4, 12);
1443
1444         if(helper->neg_flags & NTLMSSP_NEGOTIATE_ALWAYS_SIGN || helper->neg_flags == 0)
1445             memset(sig+4, 0, 4);
1446
1447         ++(helper->crypt.ntlm.seq_num);
1448
1449     }
1450
1451     return SEC_E_OK;
1452 }
1453
1454 /***********************************************************************
1455  *             DecryptMessage
1456  */
1457 static SECURITY_STATUS SEC_ENTRY ntlm_DecryptMessage(PCtxtHandle phContext,
1458         PSecBufferDesc pMessage, ULONG MessageSeqNo, PULONG pfQOP)
1459 {
1460     SECURITY_STATUS ret;
1461     ULONG ntlmssp_flags_save;
1462     PNegoHelper helper;
1463     TRACE("(%p %p %d %p)\n", phContext, pMessage, MessageSeqNo, pfQOP);
1464
1465     if(!phContext)
1466         return SEC_E_INVALID_HANDLE;
1467
1468     if(MessageSeqNo)
1469         FIXME("Ignoring MessageSeqNo\n");
1470
1471     if(!pMessage || !pMessage->pBuffers || pMessage->cBuffers < 2 ||
1472             pMessage->pBuffers[0].BufferType != SECBUFFER_TOKEN ||
1473             !pMessage->pBuffers[0].pvBuffer)
1474         return SEC_E_INVALID_TOKEN;
1475
1476     if(pMessage->pBuffers[0].cbBuffer < 16)
1477         return SEC_E_BUFFER_TOO_SMALL;
1478
1479     helper = (PNegoHelper) phContext->dwLower;
1480
1481     if(helper->neg_flags & NTLMSSP_NEGOTIATE_NTLM2)
1482     {
1483         FIXME("Can't handle NTLMv2 encryption yet, aborting\n");
1484         return SEC_E_UNSUPPORTED_FUNCTION;
1485     }
1486     else
1487     {
1488         SECUR32_arc4Process(helper->crypt.ntlm.a4i,
1489                 pMessage->pBuffers[1].pvBuffer, pMessage->pBuffers[1].cbBuffer);
1490     }
1491
1492     /* Make sure we use a session key for the signature check, EncryptMessage
1493      * always does that, even in the dummy case */
1494     ntlmssp_flags_save = helper->neg_flags;
1495
1496     helper->neg_flags |= NTLMSSP_NEGOTIATE_SIGN;
1497     ret = ntlm_VerifySignature(phContext, pMessage, MessageSeqNo, pfQOP);
1498
1499     helper->neg_flags = ntlmssp_flags_save;
1500
1501     return ret;
1502 }
1503
1504 static SecurityFunctionTableA ntlmTableA = {
1505     1,
1506     NULL,   /* EnumerateSecurityPackagesA */
1507     ntlm_QueryCredentialsAttributesA,   /* QueryCredentialsAttributesA */
1508     ntlm_AcquireCredentialsHandleA,     /* AcquireCredentialsHandleA */
1509     ntlm_FreeCredentialsHandle,         /* FreeCredentialsHandle */
1510     NULL,   /* Reserved2 */
1511     ntlm_InitializeSecurityContextA,    /* InitializeSecurityContextA */
1512     ntlm_AcceptSecurityContext,         /* AcceptSecurityContext */
1513     ntlm_CompleteAuthToken,             /* CompleteAuthToken */
1514     ntlm_DeleteSecurityContext,         /* DeleteSecurityContext */
1515     NULL,  /* ApplyControlToken */
1516     ntlm_QueryContextAttributesA,       /* QueryContextAttributesA */
1517     ntlm_ImpersonateSecurityContext,    /* ImpersonateSecurityContext */
1518     ntlm_RevertSecurityContext,         /* RevertSecurityContext */
1519     ntlm_MakeSignature,                 /* MakeSignature */
1520     ntlm_VerifySignature,               /* VerifySignature */
1521     FreeContextBuffer,                  /* FreeContextBuffer */
1522     NULL,   /* QuerySecurityPackageInfoA */
1523     NULL,   /* Reserved3 */
1524     NULL,   /* Reserved4 */
1525     NULL,   /* ExportSecurityContext */
1526     NULL,   /* ImportSecurityContextA */
1527     NULL,   /* AddCredentialsA */
1528     NULL,   /* Reserved8 */
1529     NULL,   /* QuerySecurityContextToken */
1530     ntlm_EncryptMessage,                /* EncryptMessage */
1531     ntlm_DecryptMessage,                /* DecryptMessage */
1532     NULL,   /* SetContextAttributesA */
1533 };
1534
1535 static SecurityFunctionTableW ntlmTableW = {
1536     1,
1537     NULL,   /* EnumerateSecurityPackagesW */
1538     ntlm_QueryCredentialsAttributesW,   /* QueryCredentialsAttributesW */
1539     ntlm_AcquireCredentialsHandleW,     /* AcquireCredentialsHandleW */
1540     ntlm_FreeCredentialsHandle,         /* FreeCredentialsHandle */
1541     NULL,   /* Reserved2 */
1542     ntlm_InitializeSecurityContextW,    /* InitializeSecurityContextW */
1543     ntlm_AcceptSecurityContext,         /* AcceptSecurityContext */
1544     ntlm_CompleteAuthToken,             /* CompleteAuthToken */
1545     ntlm_DeleteSecurityContext,         /* DeleteSecurityContext */
1546     NULL,  /* ApplyControlToken */
1547     ntlm_QueryContextAttributesW,       /* QueryContextAttributesW */
1548     ntlm_ImpersonateSecurityContext,    /* ImpersonateSecurityContext */
1549     ntlm_RevertSecurityContext,         /* RevertSecurityContext */
1550     ntlm_MakeSignature,                 /* MakeSignature */
1551     ntlm_VerifySignature,               /* VerifySignature */
1552     FreeContextBuffer,                  /* FreeContextBuffer */
1553     NULL,   /* QuerySecurityPackageInfoW */
1554     NULL,   /* Reserved3 */
1555     NULL,   /* Reserved4 */
1556     NULL,   /* ExportSecurityContext */
1557     NULL,   /* ImportSecurityContextW */
1558     NULL,   /* AddCredentialsW */
1559     NULL,   /* Reserved8 */
1560     NULL,   /* QuerySecurityContextToken */
1561     ntlm_EncryptMessage,                /* EncryptMessage */
1562     ntlm_DecryptMessage,                /* DecryptMessage */
1563     NULL,   /* SetContextAttributesW */
1564 };
1565
1566 #define NTLM_COMMENT \
1567    { 'N', 'T', 'L', 'M', ' ', \
1568      'S', 'e', 'c', 'u', 'r', 'i', 't', 'y', ' ', \
1569      'P', 'a', 'c', 'k', 'a', 'g', 'e', 0}
1570
1571 static CHAR ntlm_comment_A[] = NTLM_COMMENT;
1572 static WCHAR ntlm_comment_W[] = NTLM_COMMENT;
1573
1574 #define NTLM_NAME {'N', 'T', 'L', 'M', 0}
1575
1576 static char ntlm_name_A[] = NTLM_NAME;
1577 static WCHAR ntlm_name_W[] = NTLM_NAME;
1578
1579 /* According to Windows, NTLM has the following capabilities.  */
1580 #define CAPS ( \
1581         SECPKG_FLAG_INTEGRITY | \
1582         SECPKG_FLAG_PRIVACY | \
1583         SECPKG_FLAG_TOKEN_ONLY | \
1584         SECPKG_FLAG_CONNECTION | \
1585         SECPKG_FLAG_MULTI_REQUIRED | \
1586         SECPKG_FLAG_IMPERSONATION | \
1587         SECPKG_FLAG_ACCEPT_WIN32_NAME | \
1588         SECPKG_FLAG_READONLY_WITH_CHECKSUM)
1589
1590 static const SecPkgInfoW infoW = {
1591     CAPS,
1592     1,
1593     RPC_C_AUTHN_WINNT,
1594     NTLM_MAX_BUF,
1595     ntlm_name_W,
1596     ntlm_comment_W
1597 };
1598
1599 static const SecPkgInfoA infoA = {
1600     CAPS,
1601     1,
1602     RPC_C_AUTHN_WINNT,
1603     NTLM_MAX_BUF,
1604     ntlm_name_A,
1605     ntlm_comment_A
1606 };
1607
1608 void SECUR32_initNTLMSP(void)
1609 {
1610     SECURITY_STATUS ret;
1611     PNegoHelper helper;
1612     static CHAR ntlm_auth[] = "ntlm_auth",
1613                 version[]   = "--version";
1614
1615     SEC_CHAR *args[] = {
1616         ntlm_auth,
1617         version,
1618         NULL };
1619
1620     if((ret = fork_helper(&helper, "ntlm_auth", args)) != SEC_E_OK)
1621     {
1622         /* Cheat and allocate a helper anyway, so cleanup later will work. */
1623         helper = HeapAlloc(GetProcessHeap(),0, sizeof(PNegoHelper));
1624         helper->version = -1;
1625     }
1626     else
1627         check_version(helper);
1628
1629     if(helper->version > 2)
1630     {
1631         SecureProvider *provider = SECUR32_addProvider(&ntlmTableA, &ntlmTableW, NULL);
1632         SECUR32_addPackages(provider, 1L, &infoA, &infoW);
1633     }
1634     else
1635     {
1636         ERR("ntlm_auth was not found or is outdated. "
1637             "Make sure that ntlm_auth >= 3.x is in your path.\n");
1638     }
1639     cleanup_helper(helper);
1640 }