2 * Copyright 2005, 2006 Kai Blin
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.
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.
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
18 * This file implements the NTLM security provider.
30 #include "secur32_priv.h"
32 #include "wine/debug.h"
34 WINE_DEFAULT_DEBUG_CHANNEL(ntlm
);
36 #define NTLM_MAX_BUF 1904
37 #define MIN_NTLM_AUTH_MAJOR_VERSION 3
38 #define MIN_NTLM_AUTH_MINOR_VERSION 0
39 #define MIN_NTLM_AUTH_MICRO_VERSION 25
41 static CHAR ntlm_auth
[] = "ntlm_auth";
43 /***********************************************************************
44 * QueryCredentialsAttributesA
46 static SECURITY_STATUS SEC_ENTRY
ntlm_QueryCredentialsAttributesA(
47 PCredHandle phCredential
, ULONG ulAttribute
, PVOID pBuffer
)
51 TRACE("(%p, %d, %p)\n", phCredential
, ulAttribute
, pBuffer
);
53 if(ulAttribute
== SECPKG_ATTR_NAMES
)
55 FIXME("SECPKG_CRED_ATTR_NAMES: stub\n");
56 ret
= SEC_E_UNSUPPORTED_FUNCTION
;
59 ret
= SEC_E_UNSUPPORTED_FUNCTION
;
64 /***********************************************************************
65 * QueryCredentialsAttributesW
67 static SECURITY_STATUS SEC_ENTRY
ntlm_QueryCredentialsAttributesW(
68 PCredHandle phCredential
, ULONG ulAttribute
, PVOID pBuffer
)
72 TRACE("(%p, %d, %p)\n", phCredential
, ulAttribute
, pBuffer
);
74 if(ulAttribute
== SECPKG_ATTR_NAMES
)
76 FIXME("SECPKG_CRED_ATTR_NAMES: stub\n");
77 ret
= SEC_E_UNSUPPORTED_FUNCTION
;
80 ret
= SEC_E_UNSUPPORTED_FUNCTION
;
85 /***********************************************************************
86 * AcquireCredentialsHandleW
88 static SECURITY_STATUS SEC_ENTRY
ntlm_AcquireCredentialsHandleW(
89 SEC_WCHAR
*pszPrincipal
, SEC_WCHAR
*pszPackage
, ULONG fCredentialUse
,
90 PLUID pLogonID
, PVOID pAuthData
, SEC_GET_KEY_FN pGetKeyFn
,
91 PVOID pGetKeyArgument
, PCredHandle phCredential
, PTimeStamp ptsExpiry
)
94 PNegoHelper helper
= NULL
;
95 static CHAR server_helper_protocol
[] = "--helper-protocol=squid-2.5-ntlmssp",
96 credentials_argv
[] = "--use-cached-creds";
98 SEC_CHAR
*client_user_arg
= NULL
;
99 SEC_CHAR
*client_domain_arg
= NULL
;
100 SEC_WCHAR
*username
= NULL
, *domain
= NULL
;
102 SEC_CHAR
*client_argv
[6];
103 SEC_CHAR
*server_argv
[] = { ntlm_auth
,
104 server_helper_protocol
,
107 TRACE("(%s, %s, 0x%08x, %p, %p, %p, %p, %p, %p)\n",
108 debugstr_w(pszPrincipal
), debugstr_w(pszPackage
), fCredentialUse
,
109 pLogonID
, pAuthData
, pGetKeyFn
, pGetKeyArgument
, phCredential
, ptsExpiry
);
112 switch(fCredentialUse
)
114 case SECPKG_CRED_INBOUND
:
115 if( (ret
= fork_helper(&helper
, ntlm_auth
, server_argv
)) !=
123 helper
->mode
= NTLM_SERVER
;
124 phCredential
->dwUpper
= fCredentialUse
;
125 phCredential
->dwLower
= (ULONG_PTR
)helper
;
129 case SECPKG_CRED_OUTBOUND
:
131 static const char username_arg
[] = "--username=";
132 static const char domain_arg
[] = "--domain=";
133 static char helper_protocol
[] = "--helper-protocol=ntlmssp-client-1";
136 if(pAuthData
== NULL
)
138 LPWKSTA_USER_INFO_1 ui
= NULL
;
139 NET_API_STATUS status
;
141 status
= NetWkstaUserGetInfo(NULL
, 1, (LPBYTE
*)&ui
);
142 if (status
!= NERR_Success
|| ui
== NULL
)
144 ret
= SEC_E_NO_CREDENTIALS
;
149 username
= HeapAlloc(GetProcessHeap(), 0,
150 (lstrlenW(ui
->wkui1_username
)+1) *
152 lstrcpyW(username
, ui
->wkui1_username
);
154 /* same for the domain */
155 domain
= HeapAlloc(GetProcessHeap(), 0,
156 (lstrlenW(ui
->wkui1_logon_domain
)+1) *
158 lstrcpyW(domain
, ui
->wkui1_logon_domain
);
159 NetApiBufferFree(ui
);
163 PSEC_WINNT_AUTH_IDENTITY_W auth_data
=
164 (PSEC_WINNT_AUTH_IDENTITY_W
)pAuthData
;
166 /* Get username and domain from pAuthData */
167 username
= HeapAlloc(GetProcessHeap(), 0,
168 (auth_data
->UserLength
+ 1) * sizeof(SEC_WCHAR
));
169 memcpy(username
, auth_data
->User
,
170 auth_data
->UserLength
* sizeof(SEC_WCHAR
));
171 username
[auth_data
->UserLength
] = '\0';
173 domain
= HeapAlloc(GetProcessHeap(), 0,
174 (auth_data
->DomainLength
+ 1) * sizeof(SEC_WCHAR
));
175 memcpy(domain
, auth_data
->Domain
,
176 auth_data
->DomainLength
* sizeof(SEC_WCHAR
));
177 domain
[auth_data
->DomainLength
] = '\0';
179 TRACE("Username is %s\n", debugstr_w(username
));
180 unixcp_size
= WideCharToMultiByte(CP_UNIXCP
, WC_NO_BEST_FIT_CHARS
,
181 username
, -1, NULL
, 0, NULL
, NULL
) + sizeof(username_arg
);
182 client_user_arg
= HeapAlloc(GetProcessHeap(), 0, unixcp_size
);
183 lstrcpyA(client_user_arg
, username_arg
);
184 WideCharToMultiByte(CP_UNIXCP
, WC_NO_BEST_FIT_CHARS
, username
, -1,
185 client_user_arg
+ sizeof(username_arg
) - 1,
186 unixcp_size
- sizeof(username_arg
) + 1, NULL
, NULL
);
188 TRACE("Domain name is %s\n", debugstr_w(domain
));
189 unixcp_size
= WideCharToMultiByte(CP_UNIXCP
, WC_NO_BEST_FIT_CHARS
,
190 domain
, -1, NULL
, 0, NULL
, NULL
) + sizeof(domain_arg
);
191 client_domain_arg
= HeapAlloc(GetProcessHeap(), 0, unixcp_size
);
192 lstrcpyA(client_domain_arg
, domain_arg
);
193 WideCharToMultiByte(CP_UNIXCP
, WC_NO_BEST_FIT_CHARS
, domain
,
194 -1, client_domain_arg
+ sizeof(domain_arg
) - 1,
195 unixcp_size
- sizeof(domain
) + 1, NULL
, NULL
);
197 client_argv
[0] = ntlm_auth
;
198 client_argv
[1] = helper_protocol
;
199 client_argv
[2] = client_user_arg
;
200 client_argv
[3] = client_domain_arg
;
201 client_argv
[4] = credentials_argv
;
202 client_argv
[5] = NULL
;
204 if((ret
= fork_helper(&helper
, ntlm_auth
, client_argv
)) !=
212 helper
->mode
= NTLM_CLIENT
;
214 if(pAuthData
!= NULL
)
216 PSEC_WINNT_AUTH_IDENTITY_W auth_data
=
217 (PSEC_WINNT_AUTH_IDENTITY_W
)pAuthData
;
219 if(auth_data
->PasswordLength
!= 0)
221 helper
->pwlen
= WideCharToMultiByte(CP_UNIXCP
,
222 WC_NO_BEST_FIT_CHARS
, auth_data
->Password
,
223 auth_data
->PasswordLength
, NULL
, 0, NULL
,
226 helper
->password
= HeapAlloc(GetProcessHeap(), 0,
229 WideCharToMultiByte(CP_UNIXCP
, WC_NO_BEST_FIT_CHARS
,
230 auth_data
->Password
, auth_data
->PasswordLength
,
231 helper
->password
, helper
->pwlen
, NULL
, NULL
);
235 phCredential
->dwUpper
= fCredentialUse
;
236 phCredential
->dwLower
= (ULONG_PTR
)helper
;
237 TRACE("ACH phCredential->dwUpper: 0x%08lx, dwLower: 0x%08lx\n",
238 phCredential
->dwUpper
, phCredential
->dwLower
);
243 case SECPKG_CRED_BOTH
:
244 FIXME("AcquireCredentialsHandle: SECPKG_CRED_BOTH stub\n");
245 ret
= SEC_E_UNSUPPORTED_FUNCTION
;
250 ret
= SEC_E_UNKNOWN_CREDENTIALS
;
254 HeapFree(GetProcessHeap(), 0, client_user_arg
);
255 HeapFree(GetProcessHeap(), 0, client_domain_arg
);
256 HeapFree(GetProcessHeap(), 0, username
);
257 HeapFree(GetProcessHeap(), 0, domain
);
262 /***********************************************************************
263 * AcquireCredentialsHandleA
265 static SECURITY_STATUS SEC_ENTRY
ntlm_AcquireCredentialsHandleA(
266 SEC_CHAR
*pszPrincipal
, SEC_CHAR
*pszPackage
, ULONG fCredentialUse
,
267 PLUID pLogonID
, PVOID pAuthData
, SEC_GET_KEY_FN pGetKeyFn
,
268 PVOID pGetKeyArgument
, PCredHandle phCredential
, PTimeStamp ptsExpiry
)
271 int user_sizeW
, domain_sizeW
, passwd_sizeW
;
273 SEC_WCHAR
*user
= NULL
, *domain
= NULL
, *passwd
= NULL
, *package
= NULL
;
275 PSEC_WINNT_AUTH_IDENTITY_W pAuthDataW
= NULL
;
276 PSEC_WINNT_AUTH_IDENTITY_A identity
= NULL
;
278 TRACE("(%s, %s, 0x%08x, %p, %p, %p, %p, %p, %p)\n",
279 debugstr_a(pszPrincipal
), debugstr_a(pszPackage
), fCredentialUse
,
280 pLogonID
, pAuthData
, pGetKeyFn
, pGetKeyArgument
, phCredential
, ptsExpiry
);
282 if(pszPackage
!= NULL
)
284 int package_sizeW
= MultiByteToWideChar(CP_ACP
, 0, pszPackage
, -1,
287 package
= HeapAlloc(GetProcessHeap(), 0, package_sizeW
*
289 MultiByteToWideChar(CP_ACP
, 0, pszPackage
, -1, package
, package_sizeW
);
293 if(pAuthData
!= NULL
)
295 identity
= (PSEC_WINNT_AUTH_IDENTITY_A
)pAuthData
;
297 if(identity
->Flags
== SEC_WINNT_AUTH_IDENTITY_ANSI
)
299 pAuthDataW
= HeapAlloc(GetProcessHeap(), 0,
300 sizeof(SEC_WINNT_AUTH_IDENTITY_W
));
302 if(identity
->UserLength
!= 0)
304 user_sizeW
= MultiByteToWideChar(CP_ACP
, 0,
305 (LPCSTR
)identity
->User
, identity
->UserLength
, NULL
, 0);
306 user
= HeapAlloc(GetProcessHeap(), 0, user_sizeW
*
308 MultiByteToWideChar(CP_ACP
, 0, (LPCSTR
)identity
->User
,
309 identity
->UserLength
, user
, user_sizeW
);
316 if(identity
->DomainLength
!= 0)
318 domain_sizeW
= MultiByteToWideChar(CP_ACP
, 0,
319 (LPCSTR
)identity
->Domain
, identity
->DomainLength
, NULL
, 0);
320 domain
= HeapAlloc(GetProcessHeap(), 0, domain_sizeW
321 * sizeof(SEC_WCHAR
));
322 MultiByteToWideChar(CP_ACP
, 0, (LPCSTR
)identity
->Domain
,
323 identity
->DomainLength
, domain
, domain_sizeW
);
330 if(identity
->PasswordLength
!= 0)
332 passwd_sizeW
= MultiByteToWideChar(CP_ACP
, 0,
333 (LPCSTR
)identity
->Password
, identity
->PasswordLength
,
335 passwd
= HeapAlloc(GetProcessHeap(), 0, passwd_sizeW
336 * sizeof(SEC_WCHAR
));
337 MultiByteToWideChar(CP_ACP
, 0, (LPCSTR
)identity
->Password
,
338 identity
->PasswordLength
, passwd
, passwd_sizeW
);
345 pAuthDataW
->Flags
= SEC_WINNT_AUTH_IDENTITY_UNICODE
;
346 pAuthDataW
->User
= user
;
347 pAuthDataW
->UserLength
= user_sizeW
;
348 pAuthDataW
->Domain
= domain
;
349 pAuthDataW
->DomainLength
= domain_sizeW
;
350 pAuthDataW
->Password
= passwd
;
351 pAuthDataW
->PasswordLength
= passwd_sizeW
;
355 pAuthDataW
= (PSEC_WINNT_AUTH_IDENTITY_W
)identity
;
359 ret
= ntlm_AcquireCredentialsHandleW(NULL
, package
, fCredentialUse
,
360 pLogonID
, pAuthDataW
, pGetKeyFn
, pGetKeyArgument
, phCredential
,
363 HeapFree(GetProcessHeap(), 0, package
);
364 HeapFree(GetProcessHeap(), 0, user
);
365 HeapFree(GetProcessHeap(), 0, domain
);
366 HeapFree(GetProcessHeap(), 0, passwd
);
367 if(pAuthDataW
!= (PSEC_WINNT_AUTH_IDENTITY_W
)identity
)
368 HeapFree(GetProcessHeap(), 0, pAuthDataW
);
373 /*************************************************************************
374 * ntlm_GetTokenBufferIndex
375 * Calculates the index of the secbuffer with BufferType == SECBUFFER_TOKEN
376 * Returns index if found or -1 if not found.
378 static int ntlm_GetTokenBufferIndex(PSecBufferDesc pMessage
)
382 TRACE("%p\n", pMessage
);
384 for( i
= 0; i
< pMessage
->cBuffers
; ++i
)
386 if(pMessage
->pBuffers
[i
].BufferType
== SECBUFFER_TOKEN
)
393 /***********************************************************************
394 * InitializeSecurityContextW
396 static SECURITY_STATUS SEC_ENTRY
ntlm_InitializeSecurityContextW(
397 PCredHandle phCredential
, PCtxtHandle phContext
, SEC_WCHAR
*pszTargetName
,
398 ULONG fContextReq
, ULONG Reserved1
, ULONG TargetDataRep
,
399 PSecBufferDesc pInput
, ULONG Reserved2
, PCtxtHandle phNewContext
,
400 PSecBufferDesc pOutput
, ULONG
*pfContextAttr
, PTimeStamp ptsExpiry
)
405 char* buffer
, *want_flags
= NULL
;
407 int buffer_len
, bin_len
, max_len
= NTLM_MAX_BUF
;
410 TRACE("%p %p %s %d %d %d %p %d %p %p %p %p\n", phCredential
, phContext
,
411 debugstr_w(pszTargetName
), fContextReq
, Reserved1
, TargetDataRep
, pInput
,
412 Reserved1
, phNewContext
, pOutput
, pfContextAttr
, ptsExpiry
);
414 /****************************************
415 * When communicating with the client, there can be the
416 * following reply packets:
417 * YR <base64 blob> should be sent to the server
418 * PW should be sent back to helper with
419 * base64 encoded password
420 * AF <base64 blob> client is done, blob should be
421 * sent to server with KK prefixed
422 * GF <string list> A string list of negotiated flags
423 * GK <base64 blob> base64 encoded session key
424 * BH <char reason> something broke
426 /* The squid cache size is 2010 chars, and that's what ntlm_auth uses */
430 TRACE("According to a MS whitepaper pszTargetName is ignored.\n");
433 if(TargetDataRep
== SECURITY_NETWORK_DREP
){
434 TRACE("Setting SECURITY_NETWORK_DREP\n");
437 buffer
= HeapAlloc(GetProcessHeap(), 0, sizeof(char) * NTLM_MAX_BUF
);
438 bin
= HeapAlloc(GetProcessHeap(), 0, sizeof(BYTE
) * NTLM_MAX_BUF
);
440 if((phContext
== NULL
) && (pInput
== NULL
))
442 TRACE("First time in ISC()\n");
445 return SEC_E_INVALID_HANDLE
;
447 /* As the server side of sspi never calls this, make sure that
448 * the handler is a client handler.
450 helper
= (PNegoHelper
)phCredential
->dwLower
;
451 if(helper
->mode
!= NTLM_CLIENT
)
453 TRACE("Helper mode = %d\n", helper
->mode
);
454 return SEC_E_INVALID_HANDLE
;
457 /* Allocate space for a maximal string of
458 * "SF NTLMSSP_FEATURE_SIGN NTLMSSP_FEATURE_SEAL
459 * NTLMSSP_FEATURE_SESSION_KEY"
461 want_flags
= HeapAlloc(GetProcessHeap(), 0, 73);
462 if(want_flags
== NULL
)
464 ret
= SEC_E_INSUFFICIENT_MEMORY
;
467 lstrcpyA(want_flags
, "SF");
468 if(fContextReq
& ISC_REQ_CONFIDENTIALITY
)
471 if((ptr
= strstr(want_flags
, "NTLMSSP_FEATURE_SEAL")) == NULL
)
472 lstrcatA(want_flags
, " NTLMSSP_FEATURE_SEAL");
474 if(fContextReq
& ISC_REQ_CONNECTION
)
475 ctxt_attr
|= ISC_RET_CONNECTION
;
476 if(fContextReq
& ISC_REQ_EXTENDED_ERROR
)
477 ctxt_attr
|= ISC_RET_EXTENDED_ERROR
;
478 if(fContextReq
& ISC_REQ_INTEGRITY
)
481 if((ptr
= strstr(want_flags
, "NTLMSSP_FEATURE_SIGN")) == NULL
)
482 lstrcatA(want_flags
, " NTLMSSP_FEATURE_SIGN");
484 if(fContextReq
& ISC_REQ_MUTUAL_AUTH
)
485 ctxt_attr
|= ISC_RET_MUTUAL_AUTH
;
486 if(fContextReq
& ISC_REQ_REPLAY_DETECT
)
489 if((ptr
= strstr(want_flags
, "NTLMSSP_FEATURE_SIGN")) == NULL
)
490 lstrcatA(want_flags
, " NTLMSSP_FEATURE_SIGN");
492 if(fContextReq
& ISC_REQ_SEQUENCE_DETECT
)
495 if((ptr
= strstr(want_flags
, "NTLMSSP_FEATURE_SIGN")) == NULL
)
496 lstrcatA(want_flags
, " NTLMSSP_FEATURE_SIGN");
498 if(fContextReq
& ISC_REQ_STREAM
)
499 FIXME("ISC_REQ_STREAM\n");
500 if(fContextReq
& ISC_REQ_USE_DCE_STYLE
)
501 ctxt_attr
|= ISC_RET_USED_DCE_STYLE
;
502 if(fContextReq
& ISC_REQ_DELEGATE
)
503 ctxt_attr
|= ISC_RET_DELEGATE
;
505 /* If no password is given, try to use cached credentials. Fall back to an empty
506 * password if this failed. */
507 if(helper
->password
== NULL
)
509 lstrcpynA(buffer
, "OK", max_len
-1);
510 if((ret
= run_helper(helper
, buffer
, max_len
, &buffer_len
)) != SEC_E_OK
)
512 /* If the helper replied with "PW", using cached credentials failed */
513 if(!strncmp(buffer
, "PW", 2))
515 TRACE("Using cached credentials failed. Using empty password.\n");
516 lstrcpynA(buffer
, "PW AA==", max_len
-1);
518 else /* Just do a noop on the next run */
519 lstrcpynA(buffer
, "OK", max_len
-1);
523 lstrcpynA(buffer
, "PW ", max_len
-1);
524 if((ret
= encodeBase64((unsigned char*)helper
->password
,
525 helper
->pwlen
, buffer
+3,
526 max_len
-3, &buffer_len
)) != SEC_E_OK
)
531 TRACE("Sending to helper: %s\n", debugstr_a(buffer
));
532 if((ret
= run_helper(helper
, buffer
, max_len
, &buffer_len
)) != SEC_E_OK
)
535 TRACE("Helper returned %s\n", debugstr_a(buffer
));
537 if(lstrlenA(want_flags
) > 2)
539 TRACE("Want flags are %s\n", debugstr_a(want_flags
));
540 lstrcpynA(buffer
, want_flags
, max_len
-1);
541 if((ret
= run_helper(helper
, buffer
, max_len
, &buffer_len
))
544 if(!strncmp(buffer
, "BH", 2))
545 ERR("Helper doesn't understand new command set. Expect more things to fail.\n");
548 lstrcpynA(buffer
, "YR", max_len
-1);
550 if((ret
= run_helper(helper
, buffer
, max_len
, &buffer_len
)) != SEC_E_OK
)
553 TRACE("%s\n", buffer
);
555 if(strncmp(buffer
, "YR ", 3) != 0)
557 /* Something borked */
558 TRACE("Helper returned %c%c\n", buffer
[0], buffer
[1]);
559 ret
= SEC_E_INTERNAL_ERROR
;
562 if((ret
= decodeBase64(buffer
+3, buffer_len
-3, bin
,
563 max_len
-1, &bin_len
)) != SEC_E_OK
)
566 /* put the decoded client blob into the out buffer */
568 phNewContext
->dwUpper
= ctxt_attr
;
569 phNewContext
->dwLower
= (ULONG_PTR
)helper
;
571 ret
= SEC_I_CONTINUE_NEEDED
;
577 /* handle second call here */
578 /* encode server data to base64 */
579 if (!pInput
|| ((input_token_idx
= ntlm_GetTokenBufferIndex(pInput
)) == -1))
581 ret
= SEC_E_INVALID_TOKEN
;
586 return SEC_E_INVALID_HANDLE
;
588 /* As the server side of sspi never calls this, make sure that
589 * the handler is a client handler.
591 helper
= (PNegoHelper
)phContext
->dwLower
;
592 if(helper
->mode
!= NTLM_CLIENT
)
594 TRACE("Helper mode = %d\n", helper
->mode
);
595 return SEC_E_INVALID_HANDLE
;
598 if (!pInput
->pBuffers
[input_token_idx
].pvBuffer
)
600 ret
= SEC_E_INTERNAL_ERROR
;
604 if(pInput
->pBuffers
[input_token_idx
].cbBuffer
> max_len
)
606 TRACE("pInput->pBuffers[%d].cbBuffer is: %ld\n",
608 pInput
->pBuffers
[input_token_idx
].cbBuffer
);
609 ret
= SEC_E_INVALID_TOKEN
;
613 bin_len
= pInput
->pBuffers
[input_token_idx
].cbBuffer
;
615 memcpy(bin
, pInput
->pBuffers
[input_token_idx
].pvBuffer
, bin_len
);
617 lstrcpynA(buffer
, "TT ", max_len
-1);
619 if((ret
= encodeBase64(bin
, bin_len
, buffer
+3,
620 max_len
-3, &buffer_len
)) != SEC_E_OK
)
623 TRACE("Server sent: %s\n", debugstr_a(buffer
));
625 /* send TT base64 blob to ntlm_auth */
626 if((ret
= run_helper(helper
, buffer
, max_len
, &buffer_len
)) != SEC_E_OK
)
629 TRACE("Helper replied: %s\n", debugstr_a(buffer
));
631 if( (strncmp(buffer
, "KK ", 3) != 0) &&
632 (strncmp(buffer
, "AF ", 3) !=0))
634 TRACE("Helper returned %c%c\n", buffer
[0], buffer
[1]);
635 ret
= SEC_E_INVALID_TOKEN
;
639 /* decode the blob and send it to server */
640 if((ret
= decodeBase64(buffer
+3, buffer_len
-3, bin
, max_len
,
641 &bin_len
)) != SEC_E_OK
)
646 phNewContext
->dwUpper
= ctxt_attr
;
647 phNewContext
->dwLower
= (ULONG_PTR
)helper
;
652 /* put the decoded client blob into the out buffer */
654 if (!pOutput
|| ((token_idx
= ntlm_GetTokenBufferIndex(pOutput
)) == -1))
656 TRACE("no SECBUFFER_TOKEN buffer could be found\n");
657 ret
= SEC_E_BUFFER_TOO_SMALL
;
661 if (fContextReq
& ISC_REQ_ALLOCATE_MEMORY
)
663 pOutput
->pBuffers
[token_idx
].pvBuffer
= SECUR32_ALLOC(bin_len
);
664 pOutput
->pBuffers
[token_idx
].cbBuffer
= bin_len
;
666 else if (pOutput
->pBuffers
[token_idx
].cbBuffer
< bin_len
)
668 TRACE("out buffer is NULL or has not enough space\n");
669 ret
= SEC_E_BUFFER_TOO_SMALL
;
673 if (!pOutput
->pBuffers
[token_idx
].pvBuffer
)
675 TRACE("out buffer is NULL\n");
676 ret
= SEC_E_INTERNAL_ERROR
;
680 pOutput
->pBuffers
[token_idx
].cbBuffer
= bin_len
;
681 memcpy(pOutput
->pBuffers
[token_idx
].pvBuffer
, bin
, bin_len
);
685 TRACE("Getting negotiated flags\n");
686 lstrcpynA(buffer
, "GF", max_len
- 1);
687 if((ret
= run_helper(helper
, buffer
, max_len
, &buffer_len
)) != SEC_E_OK
)
692 TRACE("No flags negotiated.\n");
693 helper
->neg_flags
= 0l;
697 TRACE("Negotiated %s\n", debugstr_a(buffer
));
698 sscanf(buffer
+ 3, "%lx", &(helper
->neg_flags
));
699 TRACE("Stored 0x%08lx as flags\n", helper
->neg_flags
);
702 TRACE("Getting session key\n");
703 lstrcpynA(buffer
, "GK", max_len
- 1);
704 if((ret
= run_helper(helper
, buffer
, max_len
, &buffer_len
)) != SEC_E_OK
)
707 if(strncmp(buffer
, "BH", 2) == 0)
709 TRACE("No key negotiated.\n");
710 helper
->valid_session_key
= FALSE
;
711 helper
->session_key
= HeapAlloc(GetProcessHeap(), 0, 16);
712 /*Generate the dummy session key = MD4(MD4(password))*/
715 SEC_WCHAR
*unicode_password
;
718 TRACE("Converting password to unicode.\n");
719 passwd_lenW
= MultiByteToWideChar(CP_ACP
, 0,
720 (LPCSTR
)helper
->password
, helper
->pwlen
,
722 unicode_password
= HeapAlloc(GetProcessHeap(), 0,
723 passwd_lenW
* sizeof(SEC_WCHAR
));
724 MultiByteToWideChar(CP_ACP
, 0, (LPCSTR
)helper
->password
,
725 helper
->pwlen
, unicode_password
, passwd_lenW
);
727 SECUR32_CreateNTLMv1SessionKey((PBYTE
)unicode_password
,
728 passwd_lenW
* sizeof(SEC_WCHAR
), helper
->session_key
);
730 HeapFree(GetProcessHeap(), 0, unicode_password
);
733 memset(helper
->session_key
, 0, 16);
735 else if(strncmp(buffer
, "GK ", 3) == 0)
737 if((ret
= decodeBase64(buffer
+3, buffer_len
-3, bin
, max_len
,
738 &bin_len
)) != SEC_E_OK
)
740 TRACE("Failed to decode session key\n");
742 TRACE("Session key is %s\n", debugstr_a(buffer
+3));
743 helper
->valid_session_key
= TRUE
;
744 helper
->session_key
= HeapAlloc(GetProcessHeap(), 0, bin_len
);
745 if(!helper
->session_key
)
747 TRACE("Failed to allocate memory for session key\n");
748 ret
= SEC_E_INTERNAL_ERROR
;
751 memcpy(helper
->session_key
, bin
, bin_len
);
754 helper
->crypt
.ntlm
.a4i
= SECUR32_arc4Alloc();
755 SECUR32_arc4Init(helper
->crypt
.ntlm
.a4i
, helper
->session_key
, 16);
756 helper
->crypt
.ntlm
.seq_num
= 0l;
757 SECUR32_CreateNTLMv2SubKeys(helper
);
758 helper
->crypt
.ntlm2
.send_a4i
= SECUR32_arc4Alloc();
759 helper
->crypt
.ntlm2
.recv_a4i
= SECUR32_arc4Alloc();
760 SECUR32_arc4Init(helper
->crypt
.ntlm2
.send_a4i
,
761 (BYTE
*)helper
->crypt
.ntlm2
.send_seal_key
, 16);
762 SECUR32_arc4Init(helper
->crypt
.ntlm2
.recv_a4i
,
763 (BYTE
*)helper
->crypt
.ntlm2
.recv_seal_key
, 16);
764 helper
->crypt
.ntlm2
.send_seq_no
= 0l;
765 helper
->crypt
.ntlm2
.recv_seq_no
= 0l;
769 HeapFree(GetProcessHeap(), 0, want_flags
);
770 HeapFree(GetProcessHeap(), 0, buffer
);
771 HeapFree(GetProcessHeap(), 0, bin
);
775 /***********************************************************************
776 * InitializeSecurityContextA
778 static SECURITY_STATUS SEC_ENTRY
ntlm_InitializeSecurityContextA(
779 PCredHandle phCredential
, PCtxtHandle phContext
, SEC_CHAR
*pszTargetName
,
780 ULONG fContextReq
, ULONG Reserved1
, ULONG TargetDataRep
,
781 PSecBufferDesc pInput
,ULONG Reserved2
, PCtxtHandle phNewContext
,
782 PSecBufferDesc pOutput
, ULONG
*pfContextAttr
, PTimeStamp ptsExpiry
)
785 SEC_WCHAR
*target
= NULL
;
787 TRACE("%p %p %s %d %d %d %p %d %p %p %p %p\n", phCredential
, phContext
,
788 debugstr_a(pszTargetName
), fContextReq
, Reserved1
, TargetDataRep
, pInput
,
789 Reserved1
, phNewContext
, pOutput
, pfContextAttr
, ptsExpiry
);
791 if(pszTargetName
!= NULL
)
793 int target_size
= MultiByteToWideChar(CP_ACP
, 0, pszTargetName
,
794 strlen(pszTargetName
)+1, NULL
, 0);
795 target
= HeapAlloc(GetProcessHeap(), 0, target_size
*
797 MultiByteToWideChar(CP_ACP
, 0, pszTargetName
, strlen(pszTargetName
)+1,
798 target
, target_size
);
801 ret
= ntlm_InitializeSecurityContextW(phCredential
, phContext
, target
,
802 fContextReq
, Reserved1
, TargetDataRep
, pInput
, Reserved2
,
803 phNewContext
, pOutput
, pfContextAttr
, ptsExpiry
);
805 HeapFree(GetProcessHeap(), 0, target
);
809 /***********************************************************************
810 * AcceptSecurityContext
812 static SECURITY_STATUS SEC_ENTRY
ntlm_AcceptSecurityContext(
813 PCredHandle phCredential
, PCtxtHandle phContext
, PSecBufferDesc pInput
,
814 ULONG fContextReq
, ULONG TargetDataRep
, PCtxtHandle phNewContext
,
815 PSecBufferDesc pOutput
, ULONG
*pfContextAttr
, PTimeStamp ptsExpiry
)
818 char *buffer
, *want_flags
= NULL
;
820 int buffer_len
, bin_len
, max_len
= NTLM_MAX_BUF
;
824 TRACE("%p %p %p %d %d %p %p %p %p\n", phCredential
, phContext
, pInput
,
825 fContextReq
, TargetDataRep
, phNewContext
, pOutput
, pfContextAttr
,
829 return SEC_E_INVALID_HANDLE
;
831 helper
= (PNegoHelper
)phCredential
->dwLower
;
833 buffer
= HeapAlloc(GetProcessHeap(), 0, sizeof(char) * NTLM_MAX_BUF
);
834 bin
= HeapAlloc(GetProcessHeap(),0, sizeof(BYTE
) * NTLM_MAX_BUF
);
836 if(helper
->mode
!= NTLM_SERVER
)
838 ret
= SEC_E_INVALID_HANDLE
;
842 if(TargetDataRep
== SECURITY_NETWORK_DREP
){
843 TRACE("Using SECURITY_NETWORK_DREP\n");
846 if(phContext
== NULL
)
848 /* This is the first call to AcceptSecurityHandle */
851 ret
= SEC_E_INCOMPLETE_MESSAGE
;
855 if(pInput
->cBuffers
< 1)
857 ret
= SEC_E_INCOMPLETE_MESSAGE
;
861 if(pInput
->pBuffers
[0].cbBuffer
> max_len
)
863 ret
= SEC_E_INVALID_TOKEN
;
867 bin_len
= pInput
->pBuffers
[0].cbBuffer
;
869 /* Handle all the flags */
870 want_flags
= HeapAlloc(GetProcessHeap(), 0, 73);
871 if(want_flags
== NULL
)
873 TRACE("Failed to allocate memory for the want_flags!\n");
874 ret
= SEC_E_INSUFFICIENT_MEMORY
;
877 lstrcpyA(want_flags
, "SF");
878 if(fContextReq
& ASC_REQ_ALLOCATE_MEMORY
)
880 FIXME("ASC_REQ_ALLOCATE_MEMORY stub\n");
882 if(fContextReq
& ASC_REQ_CONFIDENTIALITY
)
884 lstrcatA(want_flags
, " NTLMSSP_FEATURE_SEAL");
886 if(fContextReq
& ASC_REQ_CONNECTION
)
888 /* This is default, so we'll enable it */
889 lstrcatA(want_flags
, " NTLMSSP_FEATURE_SESSION_KEY");
890 ctxt_attr
|= ASC_RET_CONNECTION
;
892 if(fContextReq
& ASC_REQ_EXTENDED_ERROR
)
894 FIXME("ASC_REQ_EXTENDED_ERROR stub\n");
896 if(fContextReq
& ASC_REQ_INTEGRITY
)
898 lstrcatA(want_flags
, " NTLMSSP_FEATURE_SIGN");
900 if(fContextReq
& ASC_REQ_MUTUAL_AUTH
)
902 FIXME("ASC_REQ_MUTUAL_AUTH stub\n");
904 if(fContextReq
& ASC_REQ_REPLAY_DETECT
)
906 FIXME("ASC_REQ_REPLAY_DETECT stub\n");
908 if(fContextReq
& ISC_REQ_SEQUENCE_DETECT
)
910 FIXME("ASC_REQ_SEQUENCE_DETECT stub\n");
912 if(fContextReq
& ISC_REQ_STREAM
)
914 FIXME("ASC_REQ_STREAM stub\n");
916 /* Done with the flags */
918 if(lstrlenA(want_flags
) > 3)
920 TRACE("Server set want_flags: %s\n", debugstr_a(want_flags
));
921 lstrcpynA(buffer
, want_flags
, max_len
- 1);
922 if((ret
= run_helper(helper
, buffer
, max_len
, &buffer_len
)) !=
925 if(!strncmp(buffer
, "BH", 2))
926 TRACE("Helper doesn't understand new command set\n");
929 /* This is the YR request from the client, encode to base64 */
931 memcpy(bin
, pInput
->pBuffers
[0].pvBuffer
, bin_len
);
933 lstrcpynA(buffer
, "YR ", max_len
-1);
935 if((ret
= encodeBase64(bin
, bin_len
, buffer
+3, max_len
-3,
936 &buffer_len
)) != SEC_E_OK
)
941 TRACE("Client sent: %s\n", debugstr_a(buffer
));
943 if((ret
= run_helper(helper
, buffer
, max_len
, &buffer_len
)) !=
949 TRACE("Reply from ntlm_auth: %s\n", debugstr_a(buffer
));
950 /* The expected answer is TT <base64 blob> */
952 if(strncmp(buffer
, "TT ", 3) != 0)
954 ret
= SEC_E_INTERNAL_ERROR
;
958 if((ret
= decodeBase64(buffer
+3, buffer_len
-3, bin
, max_len
,
959 &bin_len
)) != SEC_E_OK
)
964 /* send this to the client */
967 ret
= SEC_E_INSUFFICIENT_MEMORY
;
971 if(pOutput
->cBuffers
< 1)
973 ret
= SEC_E_INSUFFICIENT_MEMORY
;
977 pOutput
->pBuffers
[0].cbBuffer
= bin_len
;
978 pOutput
->pBuffers
[0].BufferType
= SECBUFFER_DATA
;
979 memcpy(pOutput
->pBuffers
[0].pvBuffer
, bin
, bin_len
);
980 ret
= SEC_I_CONTINUE_NEEDED
;
985 /* we expect a KK request from client */
988 ret
= SEC_E_INCOMPLETE_MESSAGE
;
992 if(pInput
->cBuffers
< 1)
994 ret
= SEC_E_INCOMPLETE_MESSAGE
;
998 if(pInput
->pBuffers
[0].cbBuffer
> max_len
)
1000 ret
= SEC_E_INVALID_TOKEN
;
1004 bin_len
= pInput
->pBuffers
[0].cbBuffer
;
1006 memcpy(bin
, pInput
->pBuffers
[0].pvBuffer
, bin_len
);
1008 lstrcpynA(buffer
, "KK ", max_len
-1);
1010 if((ret
= encodeBase64(bin
, bin_len
, buffer
+3, max_len
-3,
1011 &buffer_len
)) != SEC_E_OK
)
1016 TRACE("Client sent: %s\n", debugstr_a(buffer
));
1018 if((ret
= run_helper(helper
, buffer
, max_len
, &buffer_len
)) !=
1024 TRACE("Reply from ntlm_auth: %s\n", debugstr_a(buffer
));
1026 if(strncmp(buffer
, "AF ", 3) != 0)
1028 if(strncmp(buffer
, "NA ", 3) == 0)
1030 ret
= SEC_E_LOGON_DENIED
;
1035 ret
= SEC_E_INTERNAL_ERROR
;
1039 pOutput
->pBuffers
[0].cbBuffer
= 0;
1042 TRACE("Getting negotiated flags\n");
1043 lstrcpynA(buffer
, "GF", max_len
- 1);
1044 if((ret
= run_helper(helper
, buffer
, max_len
, &buffer_len
)) != SEC_E_OK
)
1049 TRACE("No flags negotiated, or helper does not support GF command\n");
1053 TRACE("Negotiated %s\n", debugstr_a(buffer
));
1054 sscanf(buffer
+ 3, "%lx", &(helper
->neg_flags
));
1055 TRACE("Stored 0x%08lx as flags\n", helper
->neg_flags
);
1058 TRACE("Getting session key\n");
1059 lstrcpynA(buffer
, "GK", max_len
- 1);
1060 if((ret
= run_helper(helper
, buffer
, max_len
, &buffer_len
)) != SEC_E_OK
)
1064 TRACE("Helper does not support GK command\n");
1067 if(strncmp(buffer
, "BH ", 3) == 0)
1069 TRACE("Helper sent %s\n", debugstr_a(buffer
+3));
1070 helper
->valid_session_key
= FALSE
;
1071 helper
->session_key
= HeapAlloc(GetProcessHeap(), 0, 16);
1072 /*FIXME: Generate the dummy session key = MD4(MD4(password))*/
1073 memset(helper
->session_key
, 0 , 16);
1075 else if(strncmp(buffer
, "GK ", 3) == 0)
1077 if((ret
= decodeBase64(buffer
+3, buffer_len
-3, bin
, max_len
,
1078 &bin_len
)) != SEC_E_OK
)
1080 TRACE("Failed to decode session key\n");
1082 TRACE("Session key is %s\n", debugstr_a(buffer
+3));
1083 helper
->valid_session_key
= TRUE
;
1084 helper
->session_key
= HeapAlloc(GetProcessHeap(), 0, 16);
1085 if(!helper
->session_key
)
1087 TRACE("Failed to allocate memory for session key\n");
1088 ret
= SEC_E_INTERNAL_ERROR
;
1091 memcpy(helper
->session_key
, bin
, 16);
1094 helper
->crypt
.ntlm
.a4i
= SECUR32_arc4Alloc();
1095 SECUR32_arc4Init(helper
->crypt
.ntlm
.a4i
, helper
->session_key
, 16);
1096 helper
->crypt
.ntlm
.seq_num
= 0l;
1099 phNewContext
->dwUpper
= ctxt_attr
;
1100 phNewContext
->dwLower
= (ULONG_PTR
)helper
;
1103 HeapFree(GetProcessHeap(), 0, want_flags
);
1104 HeapFree(GetProcessHeap(), 0, buffer
);
1105 HeapFree(GetProcessHeap(), 0, bin
);
1109 /***********************************************************************
1112 static SECURITY_STATUS SEC_ENTRY
ntlm_CompleteAuthToken(PCtxtHandle phContext
,
1113 PSecBufferDesc pToken
)
1115 /* We never need to call CompleteAuthToken anyway */
1116 TRACE("%p %p\n", phContext
, pToken
);
1118 return SEC_E_INVALID_HANDLE
;
1123 /***********************************************************************
1124 * DeleteSecurityContext
1126 static SECURITY_STATUS SEC_ENTRY
ntlm_DeleteSecurityContext(PCtxtHandle phContext
)
1130 TRACE("%p\n", phContext
);
1132 return SEC_E_INVALID_HANDLE
;
1134 helper
= (PNegoHelper
)phContext
->dwLower
;
1136 phContext
->dwUpper
= 0;
1137 phContext
->dwLower
= 0;
1139 SECUR32_arc4Cleanup(helper
->crypt
.ntlm
.a4i
);
1140 HeapFree(GetProcessHeap(), 0, helper
->session_key
);
1141 helper
->valid_session_key
= FALSE
;
1142 SECUR32_arc4Cleanup(helper
->crypt
.ntlm2
.send_a4i
);
1143 SECUR32_arc4Cleanup(helper
->crypt
.ntlm2
.recv_a4i
);
1144 HeapFree(GetProcessHeap(), 0, helper
->crypt
.ntlm2
.send_sign_key
);
1145 HeapFree(GetProcessHeap(), 0, helper
->crypt
.ntlm2
.send_seal_key
);
1146 HeapFree(GetProcessHeap(), 0, helper
->crypt
.ntlm2
.recv_sign_key
);
1147 HeapFree(GetProcessHeap(), 0, helper
->crypt
.ntlm2
.recv_seal_key
);
1152 /***********************************************************************
1153 * QueryContextAttributesW
1155 static SECURITY_STATUS SEC_ENTRY
ntlm_QueryContextAttributesW(PCtxtHandle phContext
,
1156 ULONG ulAttribute
, void *pBuffer
)
1158 TRACE("%p %d %p\n", phContext
, ulAttribute
, pBuffer
);
1160 return SEC_E_INVALID_HANDLE
;
1164 #define _x(x) case (x) : FIXME(#x" stub\n"); break
1165 _x(SECPKG_ATTR_ACCESS_TOKEN
);
1166 _x(SECPKG_ATTR_AUTHORITY
);
1167 _x(SECPKG_ATTR_DCE_INFO
);
1168 case SECPKG_ATTR_FLAGS
:
1170 PSecPkgContext_Flags spcf
= (PSecPkgContext_Flags
)pBuffer
;
1171 PNegoHelper helper
= (PNegoHelper
)phContext
->dwLower
;
1174 if(helper
->neg_flags
& NTLMSSP_NEGOTIATE_SIGN
)
1175 spcf
->Flags
|= ISC_RET_INTEGRITY
;
1176 if(helper
->neg_flags
& NTLMSSP_NEGOTIATE_SEAL
)
1177 spcf
->Flags
|= ISC_RET_CONFIDENTIALITY
;
1180 _x(SECPKG_ATTR_KEY_INFO
);
1181 _x(SECPKG_ATTR_LIFESPAN
);
1182 _x(SECPKG_ATTR_NAMES
);
1183 _x(SECPKG_ATTR_NATIVE_NAMES
);
1184 _x(SECPKG_ATTR_NEGOTIATION_INFO
);
1185 _x(SECPKG_ATTR_PACKAGE_INFO
);
1186 _x(SECPKG_ATTR_PASSWORD_EXPIRY
);
1187 _x(SECPKG_ATTR_SESSION_KEY
);
1188 case SECPKG_ATTR_SIZES
:
1190 PSecPkgContext_Sizes spcs
= (PSecPkgContext_Sizes
)pBuffer
;
1191 spcs
->cbMaxToken
= NTLM_MAX_BUF
;
1192 spcs
->cbMaxSignature
= 16;
1193 spcs
->cbBlockSize
= 0;
1194 spcs
->cbSecurityTrailer
= 16;
1197 _x(SECPKG_ATTR_STREAM_SIZES
);
1198 _x(SECPKG_ATTR_TARGET_INFORMATION
);
1201 TRACE("Unknown value %d passed for ulAttribute\n", ulAttribute
);
1204 return SEC_E_UNSUPPORTED_FUNCTION
;
1207 /***********************************************************************
1208 * QueryContextAttributesA
1210 static SECURITY_STATUS SEC_ENTRY
ntlm_QueryContextAttributesA(PCtxtHandle phContext
,
1211 ULONG ulAttribute
, void *pBuffer
)
1213 return ntlm_QueryContextAttributesW(phContext
, ulAttribute
, pBuffer
);
1216 /***********************************************************************
1217 * ImpersonateSecurityContext
1219 static SECURITY_STATUS SEC_ENTRY
ntlm_ImpersonateSecurityContext(PCtxtHandle phContext
)
1221 SECURITY_STATUS ret
;
1223 TRACE("%p\n", phContext
);
1226 ret
= SEC_E_UNSUPPORTED_FUNCTION
;
1230 ret
= SEC_E_INVALID_HANDLE
;
1235 /***********************************************************************
1236 * RevertSecurityContext
1238 static SECURITY_STATUS SEC_ENTRY
ntlm_RevertSecurityContext(PCtxtHandle phContext
)
1240 SECURITY_STATUS ret
;
1242 TRACE("%p\n", phContext
);
1245 ret
= SEC_E_UNSUPPORTED_FUNCTION
;
1249 ret
= SEC_E_INVALID_HANDLE
;
1254 /***********************************************************************
1255 * ntlm_CreateSignature
1256 * As both MakeSignature and VerifySignature need this, but different keys
1257 * are needed for NTLMv2, the logic goes into a helper function.
1258 * To ensure maximal reusability, we can specify the direction as NTLM_SEND for
1259 * signing/encrypting and NTLM_RECV for verfying/decrypting. When encrypting,
1260 * the signature is encrypted after the message was encrypted, so
1261 * CreateSignature shouldn't do it. In this case, encrypt_sig can be set to
1264 static SECURITY_STATUS
ntlm_CreateSignature(PNegoHelper helper
, PSecBufferDesc pMessage
,
1265 int token_idx
, SignDirection direction
, BOOL encrypt_sig
)
1267 ULONG sign_version
= 1;
1270 TRACE("%p, %p, %d, %d, %d\n", helper
, pMessage
, token_idx
, direction
,
1273 sig
= pMessage
->pBuffers
[token_idx
].pvBuffer
;
1275 if(helper
->neg_flags
& NTLMSSP_NEGOTIATE_NTLM2
&&
1276 helper
->neg_flags
& NTLMSSP_NEGOTIATE_SIGN
)
1280 HMAC_MD5_CTX hmac_md5_ctx
;
1282 TRACE("Signing NTLM2 style\n");
1284 if(direction
== NTLM_SEND
)
1286 seq_no
[0] = (helper
->crypt
.ntlm2
.send_seq_no
>> 0) & 0xff;
1287 seq_no
[1] = (helper
->crypt
.ntlm2
.send_seq_no
>> 8) & 0xff;
1288 seq_no
[2] = (helper
->crypt
.ntlm2
.send_seq_no
>> 16) & 0xff;
1289 seq_no
[3] = (helper
->crypt
.ntlm2
.send_seq_no
>> 24) & 0xff;
1291 ++(helper
->crypt
.ntlm2
.send_seq_no
);
1293 HMACMD5Init(&hmac_md5_ctx
, helper
->crypt
.ntlm2
.send_sign_key
, 16);
1297 seq_no
[0] = (helper
->crypt
.ntlm2
.recv_seq_no
>> 0) & 0xff;
1298 seq_no
[1] = (helper
->crypt
.ntlm2
.recv_seq_no
>> 8) & 0xff;
1299 seq_no
[2] = (helper
->crypt
.ntlm2
.recv_seq_no
>> 16) & 0xff;
1300 seq_no
[3] = (helper
->crypt
.ntlm2
.recv_seq_no
>> 24) & 0xff;
1302 ++(helper
->crypt
.ntlm2
.recv_seq_no
);
1304 HMACMD5Init(&hmac_md5_ctx
, helper
->crypt
.ntlm2
.recv_sign_key
, 16);
1307 HMACMD5Update(&hmac_md5_ctx
, seq_no
, 4);
1308 for( i
= 0; i
< pMessage
->cBuffers
; ++i
)
1310 if(pMessage
->pBuffers
[i
].BufferType
& SECBUFFER_DATA
)
1311 HMACMD5Update(&hmac_md5_ctx
, (BYTE
*)pMessage
->pBuffers
[i
].pvBuffer
,
1312 pMessage
->pBuffers
[i
].cbBuffer
);
1315 HMACMD5Final(&hmac_md5_ctx
, digest
);
1317 if(encrypt_sig
&& helper
->neg_flags
& NTLMSSP_NEGOTIATE_KEY_EXCHANGE
)
1319 if(direction
== NTLM_SEND
)
1320 SECUR32_arc4Process(helper
->crypt
.ntlm2
.send_a4i
, digest
, 8);
1322 SECUR32_arc4Process(helper
->crypt
.ntlm2
.recv_a4i
, digest
, 8);
1325 /* The NTLM2 signature is the sign version */
1326 sig
[ 0] = (sign_version
>> 0) & 0xff;
1327 sig
[ 1] = (sign_version
>> 8) & 0xff;
1328 sig
[ 2] = (sign_version
>> 16) & 0xff;
1329 sig
[ 3] = (sign_version
>> 24) & 0xff;
1330 /* The first 8 bytes of the digest */
1331 memcpy(sig
+4, digest
, 8);
1332 /* And the sequence number */
1333 memcpy(sig
+12, seq_no
, 4);
1335 pMessage
->pBuffers
[token_idx
].cbBuffer
= 16;
1339 if(helper
->neg_flags
& NTLMSSP_NEGOTIATE_SIGN
)
1342 TRACE("Signing NTLM1 style\n");
1344 for(i
=0; i
< pMessage
->cBuffers
; ++i
)
1346 if(pMessage
->pBuffers
[i
].BufferType
& SECBUFFER_DATA
)
1348 crc
= ComputeCrc32(pMessage
->pBuffers
[i
].pvBuffer
,
1349 pMessage
->pBuffers
[i
].cbBuffer
, crc
);
1353 sig
[ 0] = (sign_version
>> 0) & 0xff;
1354 sig
[ 1] = (sign_version
>> 8) & 0xff;
1355 sig
[ 2] = (sign_version
>> 16) & 0xff;
1356 sig
[ 3] = (sign_version
>> 24) & 0xff;
1357 memset(sig
+4, 0, 4);
1358 sig
[ 8] = (crc
>> 0) & 0xff;
1359 sig
[ 9] = (crc
>> 8) & 0xff;
1360 sig
[10] = (crc
>> 16) & 0xff;
1361 sig
[11] = (crc
>> 24) & 0xff;
1362 sig
[12] = (helper
->crypt
.ntlm
.seq_num
>> 0) & 0xff;
1363 sig
[13] = (helper
->crypt
.ntlm
.seq_num
>> 8) & 0xff;
1364 sig
[14] = (helper
->crypt
.ntlm
.seq_num
>> 16) & 0xff;
1365 sig
[15] = (helper
->crypt
.ntlm
.seq_num
>> 24) & 0xff;
1367 ++(helper
->crypt
.ntlm
.seq_num
);
1370 SECUR32_arc4Process(helper
->crypt
.ntlm
.a4i
, sig
+4, 12);
1374 if(helper
->neg_flags
& NTLMSSP_NEGOTIATE_ALWAYS_SIGN
|| helper
->neg_flags
== 0)
1376 TRACE("Creating a dummy signature.\n");
1377 /* A dummy signature is 0x01 followed by 15 bytes of 0x00 */
1378 memset(pMessage
->pBuffers
[token_idx
].pvBuffer
, 0, 16);
1379 memset(pMessage
->pBuffers
[token_idx
].pvBuffer
, 0x01, 1);
1380 pMessage
->pBuffers
[token_idx
].cbBuffer
= 16;
1384 return SEC_E_UNSUPPORTED_FUNCTION
;
1387 /***********************************************************************
1390 static SECURITY_STATUS SEC_ENTRY
ntlm_MakeSignature(PCtxtHandle phContext
, ULONG fQOP
,
1391 PSecBufferDesc pMessage
, ULONG MessageSeqNo
)
1396 TRACE("%p %d %p %d\n", phContext
, fQOP
, pMessage
, MessageSeqNo
);
1398 return SEC_E_INVALID_HANDLE
;
1401 FIXME("Ignoring fQOP 0x%08x\n", fQOP
);
1404 FIXME("Ignoring MessageSeqNo\n");
1406 if(!pMessage
|| !pMessage
->pBuffers
|| pMessage
->cBuffers
< 2)
1407 return SEC_E_INVALID_TOKEN
;
1409 /* If we didn't find a SECBUFFER_TOKEN type buffer */
1410 if((token_idx
= ntlm_GetTokenBufferIndex(pMessage
)) == -1)
1411 return SEC_E_INVALID_TOKEN
;
1413 if(pMessage
->pBuffers
[token_idx
].cbBuffer
< 16)
1414 return SEC_E_BUFFER_TOO_SMALL
;
1416 helper
= (PNegoHelper
)phContext
->dwLower
;
1417 TRACE("Negotiated flags are: 0x%08lx\n", helper
->neg_flags
);
1419 return ntlm_CreateSignature(helper
, pMessage
, token_idx
, NTLM_SEND
, TRUE
);
1422 /***********************************************************************
1425 static SECURITY_STATUS SEC_ENTRY
ntlm_VerifySignature(PCtxtHandle phContext
,
1426 PSecBufferDesc pMessage
, ULONG MessageSeqNo
, PULONG pfQOP
)
1432 SECURITY_STATUS ret
;
1433 SecBufferDesc local_desc
;
1434 PSecBuffer local_buff
;
1437 TRACE("%p %p %d %p\n", phContext
, pMessage
, MessageSeqNo
, pfQOP
);
1439 return SEC_E_INVALID_HANDLE
;
1441 if(!pMessage
|| !pMessage
->pBuffers
|| pMessage
->cBuffers
< 2)
1442 return SEC_E_INVALID_TOKEN
;
1444 if((token_idx
= ntlm_GetTokenBufferIndex(pMessage
)) == -1)
1445 return SEC_E_INVALID_TOKEN
;
1447 if(pMessage
->pBuffers
[token_idx
].cbBuffer
< 16)
1448 return SEC_E_BUFFER_TOO_SMALL
;
1451 FIXME("Ignoring MessageSeqNo\n");
1453 helper
= (PNegoHelper
)phContext
->dwLower
;
1454 TRACE("Negotiated flags: 0x%08lx\n", helper
->neg_flags
);
1456 local_buff
= HeapAlloc(GetProcessHeap(), 0, pMessage
->cBuffers
* sizeof(SecBuffer
));
1458 local_desc
.ulVersion
= SECBUFFER_VERSION
;
1459 local_desc
.cBuffers
= pMessage
->cBuffers
;
1460 local_desc
.pBuffers
= local_buff
;
1462 for(i
=0; i
< pMessage
->cBuffers
; ++i
)
1464 if(pMessage
->pBuffers
[i
].BufferType
== SECBUFFER_TOKEN
)
1466 local_buff
[i
].BufferType
= SECBUFFER_TOKEN
;
1467 local_buff
[i
].cbBuffer
= 16;
1468 local_buff
[i
].pvBuffer
= local_sig
;
1472 local_buff
[i
].BufferType
= pMessage
->pBuffers
[i
].BufferType
;
1473 local_buff
[i
].cbBuffer
= pMessage
->pBuffers
[i
].cbBuffer
;
1474 local_buff
[i
].pvBuffer
= pMessage
->pBuffers
[i
].pvBuffer
;
1478 if((ret
= ntlm_CreateSignature(helper
, &local_desc
, token_idx
, NTLM_RECV
, TRUE
)) != SEC_E_OK
)
1481 if(memcmp(((PBYTE
)local_buff
[token_idx
].pvBuffer
) + 8,
1482 ((PBYTE
)pMessage
->pBuffers
[token_idx
].pvBuffer
) + 8, 8))
1483 ret
= SEC_E_MESSAGE_ALTERED
;
1487 HeapFree(GetProcessHeap(), 0, local_buff
);
1494 /***********************************************************************
1495 * FreeCredentialsHandle
1497 static SECURITY_STATUS SEC_ENTRY
ntlm_FreeCredentialsHandle(
1498 PCredHandle phCredential
)
1500 SECURITY_STATUS ret
;
1503 PNegoHelper helper
= (PNegoHelper
) phCredential
->dwLower
;
1504 phCredential
->dwUpper
= 0;
1505 phCredential
->dwLower
= 0;
1506 if (helper
->password
)
1507 memset(helper
->password
, 0, helper
->pwlen
);
1508 HeapFree(GetProcessHeap(), 0, helper
->password
);
1509 cleanup_helper(helper
);
1518 /***********************************************************************
1521 static SECURITY_STATUS SEC_ENTRY
ntlm_EncryptMessage(PCtxtHandle phContext
,
1522 ULONG fQOP
, PSecBufferDesc pMessage
, ULONG MessageSeqNo
)
1527 TRACE("(%p %d %p %d)\n", phContext
, fQOP
, pMessage
, MessageSeqNo
);
1530 return SEC_E_INVALID_HANDLE
;
1533 FIXME("Ignoring fQOP\n");
1536 FIXME("Ignoring MessageSeqNo\n");
1538 if(!pMessage
|| !pMessage
->pBuffers
|| pMessage
->cBuffers
< 2)
1539 return SEC_E_INVALID_TOKEN
;
1541 if((token_idx
= ntlm_GetTokenBufferIndex(pMessage
)) == -1)
1542 return SEC_E_INVALID_TOKEN
;
1544 if(pMessage
->pBuffers
[token_idx
].cbBuffer
< 16)
1545 return SEC_E_BUFFER_TOO_SMALL
;
1547 helper
= (PNegoHelper
) phContext
->dwLower
;
1549 if(helper
->neg_flags
& NTLMSSP_NEGOTIATE_NTLM2
&&
1550 helper
->neg_flags
& NTLMSSP_NEGOTIATE_SEAL
)
1552 ntlm_CreateSignature(helper
, pMessage
, token_idx
, NTLM_SEND
, FALSE
);
1553 SECUR32_arc4Process(helper
->crypt
.ntlm2
.send_a4i
,
1554 (BYTE
*)pMessage
->pBuffers
[1].pvBuffer
,
1555 pMessage
->pBuffers
[1].cbBuffer
);
1557 if(helper
->neg_flags
& NTLMSSP_NEGOTIATE_KEY_EXCHANGE
)
1558 SECUR32_arc4Process(helper
->crypt
.ntlm2
.send_a4i
,
1559 ((BYTE
*)pMessage
->pBuffers
[token_idx
].pvBuffer
)+4, 8);
1569 /* EncryptMessage always produces real signatures, so make sure
1570 * NTLMSSP_NEGOTIATE_SIGN is set*/
1571 save_flags
= helper
->neg_flags
;
1572 helper
->neg_flags
|= NTLMSSP_NEGOTIATE_SIGN
;
1573 ntlm_CreateSignature(helper
, pMessage
, token_idx
, NTLM_SEND
, FALSE
);
1574 helper
->neg_flags
= save_flags
;
1576 sig
= pMessage
->pBuffers
[token_idx
].pvBuffer
;
1578 SECUR32_arc4Process(helper
->crypt
.ntlm
.a4i
, pMessage
->pBuffers
[1].pvBuffer
,
1579 pMessage
->pBuffers
[1].cbBuffer
);
1580 SECUR32_arc4Process(helper
->crypt
.ntlm
.a4i
, sig
+4, 12);
1582 if(helper
->neg_flags
& NTLMSSP_NEGOTIATE_ALWAYS_SIGN
|| helper
->neg_flags
== 0)
1583 memset(sig
+4, 0, 4);
1590 /***********************************************************************
1593 static SECURITY_STATUS SEC_ENTRY
ntlm_DecryptMessage(PCtxtHandle phContext
,
1594 PSecBufferDesc pMessage
, ULONG MessageSeqNo
, PULONG pfQOP
)
1596 SECURITY_STATUS ret
;
1597 ULONG ntlmssp_flags_save
;
1600 TRACE("(%p %p %d %p)\n", phContext
, pMessage
, MessageSeqNo
, pfQOP
);
1603 return SEC_E_INVALID_HANDLE
;
1606 FIXME("Ignoring MessageSeqNo\n");
1608 if(!pMessage
|| !pMessage
->pBuffers
|| pMessage
->cBuffers
< 2)
1609 return SEC_E_INVALID_TOKEN
;
1611 if((token_idx
= ntlm_GetTokenBufferIndex(pMessage
)) == -1)
1612 return SEC_E_INVALID_TOKEN
;
1614 if(pMessage
->pBuffers
[token_idx
].cbBuffer
< 16)
1615 return SEC_E_BUFFER_TOO_SMALL
;
1617 helper
= (PNegoHelper
) phContext
->dwLower
;
1619 if(helper
->neg_flags
& NTLMSSP_NEGOTIATE_NTLM2
&& helper
->neg_flags
& NTLMSSP_NEGOTIATE_SEAL
)
1621 SECUR32_arc4Process(helper
->crypt
.ntlm2
.recv_a4i
,
1622 pMessage
->pBuffers
[1].pvBuffer
, pMessage
->pBuffers
[1].cbBuffer
);
1626 SECUR32_arc4Process(helper
->crypt
.ntlm
.a4i
,
1627 pMessage
->pBuffers
[1].pvBuffer
, pMessage
->pBuffers
[1].cbBuffer
);
1630 /* Make sure we use a session key for the signature check, EncryptMessage
1631 * always does that, even in the dummy case */
1632 ntlmssp_flags_save
= helper
->neg_flags
;
1634 helper
->neg_flags
|= NTLMSSP_NEGOTIATE_SIGN
;
1635 ret
= ntlm_VerifySignature(phContext
, pMessage
, MessageSeqNo
, pfQOP
);
1637 helper
->neg_flags
= ntlmssp_flags_save
;
1642 static const SecurityFunctionTableA ntlmTableA
= {
1644 NULL
, /* EnumerateSecurityPackagesA */
1645 ntlm_QueryCredentialsAttributesA
, /* QueryCredentialsAttributesA */
1646 ntlm_AcquireCredentialsHandleA
, /* AcquireCredentialsHandleA */
1647 ntlm_FreeCredentialsHandle
, /* FreeCredentialsHandle */
1648 NULL
, /* Reserved2 */
1649 ntlm_InitializeSecurityContextA
, /* InitializeSecurityContextA */
1650 ntlm_AcceptSecurityContext
, /* AcceptSecurityContext */
1651 ntlm_CompleteAuthToken
, /* CompleteAuthToken */
1652 ntlm_DeleteSecurityContext
, /* DeleteSecurityContext */
1653 NULL
, /* ApplyControlToken */
1654 ntlm_QueryContextAttributesA
, /* QueryContextAttributesA */
1655 ntlm_ImpersonateSecurityContext
, /* ImpersonateSecurityContext */
1656 ntlm_RevertSecurityContext
, /* RevertSecurityContext */
1657 ntlm_MakeSignature
, /* MakeSignature */
1658 ntlm_VerifySignature
, /* VerifySignature */
1659 FreeContextBuffer
, /* FreeContextBuffer */
1660 NULL
, /* QuerySecurityPackageInfoA */
1661 NULL
, /* Reserved3 */
1662 NULL
, /* Reserved4 */
1663 NULL
, /* ExportSecurityContext */
1664 NULL
, /* ImportSecurityContextA */
1665 NULL
, /* AddCredentialsA */
1666 NULL
, /* Reserved8 */
1667 NULL
, /* QuerySecurityContextToken */
1668 ntlm_EncryptMessage
, /* EncryptMessage */
1669 ntlm_DecryptMessage
, /* DecryptMessage */
1670 NULL
, /* SetContextAttributesA */
1673 static const SecurityFunctionTableW ntlmTableW
= {
1675 NULL
, /* EnumerateSecurityPackagesW */
1676 ntlm_QueryCredentialsAttributesW
, /* QueryCredentialsAttributesW */
1677 ntlm_AcquireCredentialsHandleW
, /* AcquireCredentialsHandleW */
1678 ntlm_FreeCredentialsHandle
, /* FreeCredentialsHandle */
1679 NULL
, /* Reserved2 */
1680 ntlm_InitializeSecurityContextW
, /* InitializeSecurityContextW */
1681 ntlm_AcceptSecurityContext
, /* AcceptSecurityContext */
1682 ntlm_CompleteAuthToken
, /* CompleteAuthToken */
1683 ntlm_DeleteSecurityContext
, /* DeleteSecurityContext */
1684 NULL
, /* ApplyControlToken */
1685 ntlm_QueryContextAttributesW
, /* QueryContextAttributesW */
1686 ntlm_ImpersonateSecurityContext
, /* ImpersonateSecurityContext */
1687 ntlm_RevertSecurityContext
, /* RevertSecurityContext */
1688 ntlm_MakeSignature
, /* MakeSignature */
1689 ntlm_VerifySignature
, /* VerifySignature */
1690 FreeContextBuffer
, /* FreeContextBuffer */
1691 NULL
, /* QuerySecurityPackageInfoW */
1692 NULL
, /* Reserved3 */
1693 NULL
, /* Reserved4 */
1694 NULL
, /* ExportSecurityContext */
1695 NULL
, /* ImportSecurityContextW */
1696 NULL
, /* AddCredentialsW */
1697 NULL
, /* Reserved8 */
1698 NULL
, /* QuerySecurityContextToken */
1699 ntlm_EncryptMessage
, /* EncryptMessage */
1700 ntlm_DecryptMessage
, /* DecryptMessage */
1701 NULL
, /* SetContextAttributesW */
1704 #define NTLM_COMMENT \
1705 { 'N', 'T', 'L', 'M', ' ', \
1706 'S', 'e', 'c', 'u', 'r', 'i', 't', 'y', ' ', \
1707 'P', 'a', 'c', 'k', 'a', 'g', 'e', 0}
1709 static CHAR ntlm_comment_A
[] = NTLM_COMMENT
;
1710 static WCHAR ntlm_comment_W
[] = NTLM_COMMENT
;
1712 #define NTLM_NAME {'N', 'T', 'L', 'M', 0}
1714 static char ntlm_name_A
[] = NTLM_NAME
;
1715 static WCHAR ntlm_name_W
[] = NTLM_NAME
;
1717 /* According to Windows, NTLM has the following capabilities. */
1719 SECPKG_FLAG_INTEGRITY | \
1720 SECPKG_FLAG_PRIVACY | \
1721 SECPKG_FLAG_TOKEN_ONLY | \
1722 SECPKG_FLAG_CONNECTION | \
1723 SECPKG_FLAG_MULTI_REQUIRED | \
1724 SECPKG_FLAG_IMPERSONATION | \
1725 SECPKG_FLAG_ACCEPT_WIN32_NAME | \
1726 SECPKG_FLAG_READONLY_WITH_CHECKSUM)
1728 static const SecPkgInfoW infoW
= {
1737 static const SecPkgInfoA infoA
= {
1746 void SECUR32_initNTLMSP(void)
1748 SECURITY_STATUS ret
;
1750 static CHAR version
[] = "--version";
1752 SEC_CHAR
*args
[] = {
1757 if((ret
= fork_helper(&helper
, ntlm_auth
, args
)) != SEC_E_OK
)
1759 /* Cheat and allocate a helper anyway, so cleanup later will work. */
1760 helper
= HeapAlloc(GetProcessHeap(),0, sizeof(PNegoHelper
));
1761 helper
->major
= helper
->minor
= helper
->micro
= -1;
1764 check_version(helper
);
1766 if( (helper
->major
> MIN_NTLM_AUTH_MAJOR_VERSION
) ||
1767 (helper
->major
== MIN_NTLM_AUTH_MAJOR_VERSION
&&
1768 helper
->minor
> MIN_NTLM_AUTH_MINOR_VERSION
) ||
1769 (helper
->major
== MIN_NTLM_AUTH_MAJOR_VERSION
&&
1770 helper
->minor
== MIN_NTLM_AUTH_MINOR_VERSION
&&
1771 helper
->micro
>= MIN_NTLM_AUTH_MICRO_VERSION
) )
1773 SecureProvider
*provider
= SECUR32_addProvider(&ntlmTableA
, &ntlmTableW
, NULL
);
1774 SECUR32_addPackages(provider
, 1L, &infoA
, &infoW
);
1778 ERR("%s was not found or is outdated. "
1779 "Make sure that ntlm_auth >= %d.%d.%d is in your path.\n",
1781 MIN_NTLM_AUTH_MAJOR_VERSION
,
1782 MIN_NTLM_AUTH_MINOR_VERSION
,
1783 MIN_NTLM_AUTH_MICRO_VERSION
);
1785 cleanup_helper(helper
);