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 typedef struct _NtlmCredentials
47 /* these are all in the Unix codepage */
50 char *password
; /* not nul-terminated */
52 } NtlmCredentials
, *PNtlmCredentials
;
54 /***********************************************************************
55 * QueryCredentialsAttributesA
57 static SECURITY_STATUS SEC_ENTRY
ntlm_QueryCredentialsAttributesA(
58 PCredHandle phCredential
, ULONG ulAttribute
, PVOID pBuffer
)
62 TRACE("(%p, %d, %p)\n", phCredential
, ulAttribute
, pBuffer
);
64 if(ulAttribute
== SECPKG_ATTR_NAMES
)
66 FIXME("SECPKG_CRED_ATTR_NAMES: stub\n");
67 ret
= SEC_E_UNSUPPORTED_FUNCTION
;
70 ret
= SEC_E_UNSUPPORTED_FUNCTION
;
75 /***********************************************************************
76 * QueryCredentialsAttributesW
78 static SECURITY_STATUS SEC_ENTRY
ntlm_QueryCredentialsAttributesW(
79 PCredHandle phCredential
, ULONG ulAttribute
, PVOID pBuffer
)
83 TRACE("(%p, %d, %p)\n", phCredential
, ulAttribute
, pBuffer
);
85 if(ulAttribute
== SECPKG_ATTR_NAMES
)
87 FIXME("SECPKG_CRED_ATTR_NAMES: stub\n");
88 ret
= SEC_E_UNSUPPORTED_FUNCTION
;
91 ret
= SEC_E_UNSUPPORTED_FUNCTION
;
96 /***********************************************************************
97 * AcquireCredentialsHandleW
99 static SECURITY_STATUS SEC_ENTRY
ntlm_AcquireCredentialsHandleW(
100 SEC_WCHAR
*pszPrincipal
, SEC_WCHAR
*pszPackage
, ULONG fCredentialUse
,
101 PLUID pLogonID
, PVOID pAuthData
, SEC_GET_KEY_FN pGetKeyFn
,
102 PVOID pGetKeyArgument
, PCredHandle phCredential
, PTimeStamp ptsExpiry
)
105 PNtlmCredentials ntlm_cred
= NULL
;
106 SEC_WCHAR
*username
= NULL
, *domain
= NULL
;
108 TRACE("(%s, %s, 0x%08x, %p, %p, %p, %p, %p, %p)\n",
109 debugstr_w(pszPrincipal
), debugstr_w(pszPackage
), fCredentialUse
,
110 pLogonID
, pAuthData
, pGetKeyFn
, pGetKeyArgument
, phCredential
, ptsExpiry
);
112 switch(fCredentialUse
)
114 case SECPKG_CRED_INBOUND
:
115 ntlm_cred
= HeapAlloc(GetProcessHeap(), 0, sizeof(*ntlm_cred
));
117 ret
= SEC_E_INSUFFICIENT_MEMORY
;
120 ntlm_cred
->mode
= NTLM_SERVER
;
121 ntlm_cred
->username_arg
= NULL
;
122 ntlm_cred
->domain_arg
= NULL
;
123 ntlm_cred
->password
= NULL
;
124 ntlm_cred
->pwlen
= 0;
125 phCredential
->dwUpper
= fCredentialUse
;
126 phCredential
->dwLower
= (ULONG_PTR
)ntlm_cred
;
130 case SECPKG_CRED_OUTBOUND
:
132 static const char username_arg
[] = "--username=";
133 static const char domain_arg
[] = "--domain=";
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';
180 ntlm_cred
= HeapAlloc(GetProcessHeap(), 0, sizeof(*ntlm_cred
));
183 ret
= SEC_E_INSUFFICIENT_MEMORY
;
186 ntlm_cred
->mode
= NTLM_CLIENT
;
187 ntlm_cred
->password
= NULL
;
188 ntlm_cred
->pwlen
= 0;
190 TRACE("Username is %s\n", debugstr_w(username
));
191 unixcp_size
= WideCharToMultiByte(CP_UNIXCP
, WC_NO_BEST_FIT_CHARS
,
192 username
, -1, NULL
, 0, NULL
, NULL
) + sizeof(username_arg
);
193 ntlm_cred
->username_arg
= HeapAlloc(GetProcessHeap(), 0, unixcp_size
);
194 memcpy(ntlm_cred
->username_arg
, username_arg
, sizeof(username_arg
) - 1);
195 WideCharToMultiByte(CP_UNIXCP
, WC_NO_BEST_FIT_CHARS
, username
, -1,
196 ntlm_cred
->username_arg
+ sizeof(username_arg
) - 1,
197 unixcp_size
- sizeof(username_arg
) + 1, NULL
, NULL
);
199 TRACE("Domain name is %s\n", debugstr_w(domain
));
200 unixcp_size
= WideCharToMultiByte(CP_UNIXCP
, WC_NO_BEST_FIT_CHARS
,
201 domain
, -1, NULL
, 0, NULL
, NULL
) + sizeof(domain_arg
);
202 ntlm_cred
->domain_arg
= HeapAlloc(GetProcessHeap(), 0, unixcp_size
);
203 memcpy(ntlm_cred
->domain_arg
, domain_arg
, sizeof(domain_arg
) - 1);
204 WideCharToMultiByte(CP_UNIXCP
, WC_NO_BEST_FIT_CHARS
, domain
,
205 -1, ntlm_cred
->domain_arg
+ sizeof(domain_arg
) - 1,
206 unixcp_size
- sizeof(domain
) + 1, NULL
, NULL
);
208 if(pAuthData
!= NULL
)
210 PSEC_WINNT_AUTH_IDENTITY_W auth_data
=
211 (PSEC_WINNT_AUTH_IDENTITY_W
)pAuthData
;
213 if(auth_data
->PasswordLength
!= 0)
215 ntlm_cred
->pwlen
= WideCharToMultiByte(CP_UNIXCP
,
216 WC_NO_BEST_FIT_CHARS
, auth_data
->Password
,
217 auth_data
->PasswordLength
, NULL
, 0, NULL
,
220 ntlm_cred
->password
= HeapAlloc(GetProcessHeap(), 0,
223 WideCharToMultiByte(CP_UNIXCP
, WC_NO_BEST_FIT_CHARS
,
224 auth_data
->Password
, auth_data
->PasswordLength
,
225 ntlm_cred
->password
, ntlm_cred
->pwlen
, NULL
, NULL
);
229 phCredential
->dwUpper
= fCredentialUse
;
230 phCredential
->dwLower
= (ULONG_PTR
)ntlm_cred
;
231 TRACE("ACH phCredential->dwUpper: 0x%08lx, dwLower: 0x%08lx\n",
232 phCredential
->dwUpper
, phCredential
->dwLower
);
236 case SECPKG_CRED_BOTH
:
237 FIXME("AcquireCredentialsHandle: SECPKG_CRED_BOTH stub\n");
238 ret
= SEC_E_UNSUPPORTED_FUNCTION
;
243 ret
= SEC_E_UNKNOWN_CREDENTIALS
;
246 HeapFree(GetProcessHeap(), 0, username
);
247 HeapFree(GetProcessHeap(), 0, domain
);
252 /***********************************************************************
253 * AcquireCredentialsHandleA
255 static SECURITY_STATUS SEC_ENTRY
ntlm_AcquireCredentialsHandleA(
256 SEC_CHAR
*pszPrincipal
, SEC_CHAR
*pszPackage
, ULONG fCredentialUse
,
257 PLUID pLogonID
, PVOID pAuthData
, SEC_GET_KEY_FN pGetKeyFn
,
258 PVOID pGetKeyArgument
, PCredHandle phCredential
, PTimeStamp ptsExpiry
)
261 int user_sizeW
, domain_sizeW
, passwd_sizeW
;
263 SEC_WCHAR
*user
= NULL
, *domain
= NULL
, *passwd
= NULL
, *package
= NULL
;
265 PSEC_WINNT_AUTH_IDENTITY_W pAuthDataW
= NULL
;
266 PSEC_WINNT_AUTH_IDENTITY_A identity
= NULL
;
268 TRACE("(%s, %s, 0x%08x, %p, %p, %p, %p, %p, %p)\n",
269 debugstr_a(pszPrincipal
), debugstr_a(pszPackage
), fCredentialUse
,
270 pLogonID
, pAuthData
, pGetKeyFn
, pGetKeyArgument
, phCredential
, ptsExpiry
);
272 if(pszPackage
!= NULL
)
274 int package_sizeW
= MultiByteToWideChar(CP_ACP
, 0, pszPackage
, -1,
277 package
= HeapAlloc(GetProcessHeap(), 0, package_sizeW
*
279 MultiByteToWideChar(CP_ACP
, 0, pszPackage
, -1, package
, package_sizeW
);
283 if(pAuthData
!= NULL
)
285 identity
= (PSEC_WINNT_AUTH_IDENTITY_A
)pAuthData
;
287 if(identity
->Flags
== SEC_WINNT_AUTH_IDENTITY_ANSI
)
289 pAuthDataW
= HeapAlloc(GetProcessHeap(), 0,
290 sizeof(SEC_WINNT_AUTH_IDENTITY_W
));
292 if(identity
->UserLength
!= 0)
294 user_sizeW
= MultiByteToWideChar(CP_ACP
, 0,
295 (LPCSTR
)identity
->User
, identity
->UserLength
, NULL
, 0);
296 user
= HeapAlloc(GetProcessHeap(), 0, user_sizeW
*
298 MultiByteToWideChar(CP_ACP
, 0, (LPCSTR
)identity
->User
,
299 identity
->UserLength
, user
, user_sizeW
);
306 if(identity
->DomainLength
!= 0)
308 domain_sizeW
= MultiByteToWideChar(CP_ACP
, 0,
309 (LPCSTR
)identity
->Domain
, identity
->DomainLength
, NULL
, 0);
310 domain
= HeapAlloc(GetProcessHeap(), 0, domain_sizeW
311 * sizeof(SEC_WCHAR
));
312 MultiByteToWideChar(CP_ACP
, 0, (LPCSTR
)identity
->Domain
,
313 identity
->DomainLength
, domain
, domain_sizeW
);
320 if(identity
->PasswordLength
!= 0)
322 passwd_sizeW
= MultiByteToWideChar(CP_ACP
, 0,
323 (LPCSTR
)identity
->Password
, identity
->PasswordLength
,
325 passwd
= HeapAlloc(GetProcessHeap(), 0, passwd_sizeW
326 * sizeof(SEC_WCHAR
));
327 MultiByteToWideChar(CP_ACP
, 0, (LPCSTR
)identity
->Password
,
328 identity
->PasswordLength
, passwd
, passwd_sizeW
);
335 pAuthDataW
->Flags
= SEC_WINNT_AUTH_IDENTITY_UNICODE
;
336 pAuthDataW
->User
= user
;
337 pAuthDataW
->UserLength
= user_sizeW
;
338 pAuthDataW
->Domain
= domain
;
339 pAuthDataW
->DomainLength
= domain_sizeW
;
340 pAuthDataW
->Password
= passwd
;
341 pAuthDataW
->PasswordLength
= passwd_sizeW
;
345 pAuthDataW
= (PSEC_WINNT_AUTH_IDENTITY_W
)identity
;
349 ret
= ntlm_AcquireCredentialsHandleW(NULL
, package
, fCredentialUse
,
350 pLogonID
, pAuthDataW
, pGetKeyFn
, pGetKeyArgument
, phCredential
,
353 HeapFree(GetProcessHeap(), 0, package
);
354 HeapFree(GetProcessHeap(), 0, user
);
355 HeapFree(GetProcessHeap(), 0, domain
);
356 HeapFree(GetProcessHeap(), 0, passwd
);
357 if(pAuthDataW
!= (PSEC_WINNT_AUTH_IDENTITY_W
)identity
)
358 HeapFree(GetProcessHeap(), 0, pAuthDataW
);
363 /*************************************************************************
364 * ntlm_GetTokenBufferIndex
365 * Calculates the index of the secbuffer with BufferType == SECBUFFER_TOKEN
366 * Returns index if found or -1 if not found.
368 static int ntlm_GetTokenBufferIndex(PSecBufferDesc pMessage
)
372 TRACE("%p\n", pMessage
);
374 for( i
= 0; i
< pMessage
->cBuffers
; ++i
)
376 if(pMessage
->pBuffers
[i
].BufferType
== SECBUFFER_TOKEN
)
383 /***********************************************************************
384 * InitializeSecurityContextW
386 static SECURITY_STATUS SEC_ENTRY
ntlm_InitializeSecurityContextW(
387 PCredHandle phCredential
, PCtxtHandle phContext
, SEC_WCHAR
*pszTargetName
,
388 ULONG fContextReq
, ULONG Reserved1
, ULONG TargetDataRep
,
389 PSecBufferDesc pInput
, ULONG Reserved2
, PCtxtHandle phNewContext
,
390 PSecBufferDesc pOutput
, ULONG
*pfContextAttr
, PTimeStamp ptsExpiry
)
393 PNtlmCredentials ntlm_cred
= NULL
;
394 PNegoHelper helper
= NULL
;
396 char* buffer
, *want_flags
= NULL
;
398 int buffer_len
, bin_len
, max_len
= NTLM_MAX_BUF
;
401 TRACE("%p %p %s %d %d %d %p %d %p %p %p %p\n", phCredential
, phContext
,
402 debugstr_w(pszTargetName
), fContextReq
, Reserved1
, TargetDataRep
, pInput
,
403 Reserved1
, phNewContext
, pOutput
, pfContextAttr
, ptsExpiry
);
405 /****************************************
406 * When communicating with the client, there can be the
407 * following reply packets:
408 * YR <base64 blob> should be sent to the server
409 * PW should be sent back to helper with
410 * base64 encoded password
411 * AF <base64 blob> client is done, blob should be
412 * sent to server with KK prefixed
413 * GF <string list> A string list of negotiated flags
414 * GK <base64 blob> base64 encoded session key
415 * BH <char reason> something broke
417 /* The squid cache size is 2010 chars, and that's what ntlm_auth uses */
421 TRACE("According to a MS whitepaper pszTargetName is ignored.\n");
424 if(TargetDataRep
== SECURITY_NETWORK_DREP
){
425 TRACE("Setting SECURITY_NETWORK_DREP\n");
428 buffer
= HeapAlloc(GetProcessHeap(), 0, sizeof(char) * NTLM_MAX_BUF
);
429 bin
= HeapAlloc(GetProcessHeap(), 0, sizeof(BYTE
) * NTLM_MAX_BUF
);
431 if((phContext
== NULL
) && (pInput
== NULL
))
433 static char helper_protocol
[] = "--helper-protocol=ntlmssp-client-1";
434 static CHAR credentials_argv
[] = "--use-cached-creds";
435 SEC_CHAR
*client_argv
[6];
437 TRACE("First time in ISC()\n");
440 return SEC_E_INVALID_HANDLE
;
442 /* As the server side of sspi never calls this, make sure that
443 * the handler is a client handler.
445 ntlm_cred
= (PNtlmCredentials
)phCredential
->dwLower
;
446 if(ntlm_cred
->mode
!= NTLM_CLIENT
)
448 TRACE("Cred mode = %d\n", ntlm_cred
->mode
);
449 return SEC_E_INVALID_HANDLE
;
452 client_argv
[0] = ntlm_auth
;
453 client_argv
[1] = helper_protocol
;
454 client_argv
[2] = ntlm_cred
->username_arg
;
455 client_argv
[3] = ntlm_cred
->domain_arg
;
456 client_argv
[4] = credentials_argv
;
457 client_argv
[5] = NULL
;
459 if((ret
= fork_helper(&helper
, ntlm_auth
, client_argv
)) != SEC_E_OK
)
462 helper
->mode
= NTLM_CLIENT
;
463 helper
->session_key
= HeapAlloc(GetProcessHeap(), 0, 16);
464 if (!helper
->session_key
)
466 cleanup_helper(helper
);
467 ret
= SEC_E_INSUFFICIENT_MEMORY
;
471 /* Generate the dummy session key = MD4(MD4(password))*/
472 if(ntlm_cred
->password
)
474 SEC_WCHAR
*unicode_password
;
477 TRACE("Converting password to unicode.\n");
478 passwd_lenW
= MultiByteToWideChar(CP_ACP
, 0,
479 (LPCSTR
)ntlm_cred
->password
, ntlm_cred
->pwlen
,
481 unicode_password
= HeapAlloc(GetProcessHeap(), 0,
482 passwd_lenW
* sizeof(SEC_WCHAR
));
483 MultiByteToWideChar(CP_ACP
, 0, (LPCSTR
)ntlm_cred
->password
,
484 ntlm_cred
->pwlen
, unicode_password
, passwd_lenW
);
486 SECUR32_CreateNTLMv1SessionKey((PBYTE
)unicode_password
,
487 passwd_lenW
* sizeof(SEC_WCHAR
), helper
->session_key
);
489 HeapFree(GetProcessHeap(), 0, unicode_password
);
492 memset(helper
->session_key
, 0, 16);
494 /* Allocate space for a maximal string of
495 * "SF NTLMSSP_FEATURE_SIGN NTLMSSP_FEATURE_SEAL
496 * NTLMSSP_FEATURE_SESSION_KEY"
498 want_flags
= HeapAlloc(GetProcessHeap(), 0, 73);
499 if(want_flags
== NULL
)
501 cleanup_helper(helper
);
502 ret
= SEC_E_INSUFFICIENT_MEMORY
;
505 lstrcpyA(want_flags
, "SF");
506 if(fContextReq
& ISC_REQ_CONFIDENTIALITY
)
509 if((ptr
= strstr(want_flags
, "NTLMSSP_FEATURE_SEAL")) == NULL
)
510 lstrcatA(want_flags
, " NTLMSSP_FEATURE_SEAL");
512 if(fContextReq
& ISC_REQ_CONNECTION
)
513 ctxt_attr
|= ISC_RET_CONNECTION
;
514 if(fContextReq
& ISC_REQ_EXTENDED_ERROR
)
515 ctxt_attr
|= ISC_RET_EXTENDED_ERROR
;
516 if(fContextReq
& ISC_REQ_INTEGRITY
)
519 if((ptr
= strstr(want_flags
, "NTLMSSP_FEATURE_SIGN")) == NULL
)
520 lstrcatA(want_flags
, " NTLMSSP_FEATURE_SIGN");
522 if(fContextReq
& ISC_REQ_MUTUAL_AUTH
)
523 ctxt_attr
|= ISC_RET_MUTUAL_AUTH
;
524 if(fContextReq
& ISC_REQ_REPLAY_DETECT
)
527 if((ptr
= strstr(want_flags
, "NTLMSSP_FEATURE_SIGN")) == NULL
)
528 lstrcatA(want_flags
, " NTLMSSP_FEATURE_SIGN");
530 if(fContextReq
& ISC_REQ_SEQUENCE_DETECT
)
533 if((ptr
= strstr(want_flags
, "NTLMSSP_FEATURE_SIGN")) == NULL
)
534 lstrcatA(want_flags
, " NTLMSSP_FEATURE_SIGN");
536 if(fContextReq
& ISC_REQ_STREAM
)
537 FIXME("ISC_REQ_STREAM\n");
538 if(fContextReq
& ISC_REQ_USE_DCE_STYLE
)
539 ctxt_attr
|= ISC_RET_USED_DCE_STYLE
;
540 if(fContextReq
& ISC_REQ_DELEGATE
)
541 ctxt_attr
|= ISC_RET_DELEGATE
;
543 /* If no password is given, try to use cached credentials. Fall back to an empty
544 * password if this failed. */
545 if(ntlm_cred
->password
== NULL
)
547 lstrcpynA(buffer
, "OK", max_len
-1);
548 if((ret
= run_helper(helper
, buffer
, max_len
, &buffer_len
)) != SEC_E_OK
)
550 cleanup_helper(helper
);
553 /* If the helper replied with "PW", using cached credentials failed */
554 if(!strncmp(buffer
, "PW", 2))
556 TRACE("Using cached credentials failed. Using empty password.\n");
557 lstrcpynA(buffer
, "PW AA==", max_len
-1);
559 else /* Just do a noop on the next run */
560 lstrcpynA(buffer
, "OK", max_len
-1);
564 lstrcpynA(buffer
, "PW ", max_len
-1);
565 if((ret
= encodeBase64((unsigned char*)ntlm_cred
->password
,
566 ntlm_cred
->pwlen
, buffer
+3,
567 max_len
-3, &buffer_len
)) != SEC_E_OK
)
569 cleanup_helper(helper
);
575 TRACE("Sending to helper: %s\n", debugstr_a(buffer
));
576 if((ret
= run_helper(helper
, buffer
, max_len
, &buffer_len
)) != SEC_E_OK
)
578 cleanup_helper(helper
);
582 TRACE("Helper returned %s\n", debugstr_a(buffer
));
584 if(lstrlenA(want_flags
) > 2)
586 TRACE("Want flags are %s\n", debugstr_a(want_flags
));
587 lstrcpynA(buffer
, want_flags
, max_len
-1);
588 if((ret
= run_helper(helper
, buffer
, max_len
, &buffer_len
))
591 if(!strncmp(buffer
, "BH", 2))
592 ERR("Helper doesn't understand new command set. Expect more things to fail.\n");
595 lstrcpynA(buffer
, "YR", max_len
-1);
597 if((ret
= run_helper(helper
, buffer
, max_len
, &buffer_len
)) != SEC_E_OK
)
599 cleanup_helper(helper
);
603 TRACE("%s\n", buffer
);
605 if(strncmp(buffer
, "YR ", 3) != 0)
607 /* Something borked */
608 TRACE("Helper returned %c%c\n", buffer
[0], buffer
[1]);
609 ret
= SEC_E_INTERNAL_ERROR
;
610 cleanup_helper(helper
);
613 if((ret
= decodeBase64(buffer
+3, buffer_len
-3, bin
,
614 max_len
-1, &bin_len
)) != SEC_E_OK
)
616 cleanup_helper(helper
);
620 /* put the decoded client blob into the out buffer */
622 phNewContext
->dwUpper
= ctxt_attr
;
623 phNewContext
->dwLower
= (ULONG_PTR
)helper
;
625 ret
= SEC_I_CONTINUE_NEEDED
;
631 /* handle second call here */
632 /* encode server data to base64 */
633 if (!pInput
|| ((input_token_idx
= ntlm_GetTokenBufferIndex(pInput
)) == -1))
635 ret
= SEC_E_INVALID_TOKEN
;
640 return SEC_E_INVALID_HANDLE
;
642 /* As the server side of sspi never calls this, make sure that
643 * the handler is a client handler.
645 helper
= (PNegoHelper
)phContext
->dwLower
;
646 if(helper
->mode
!= NTLM_CLIENT
)
648 TRACE("Helper mode = %d\n", helper
->mode
);
649 return SEC_E_INVALID_HANDLE
;
652 if (!pInput
->pBuffers
[input_token_idx
].pvBuffer
)
654 ret
= SEC_E_INTERNAL_ERROR
;
658 if(pInput
->pBuffers
[input_token_idx
].cbBuffer
> max_len
)
660 TRACE("pInput->pBuffers[%d].cbBuffer is: %ld\n",
662 pInput
->pBuffers
[input_token_idx
].cbBuffer
);
663 ret
= SEC_E_INVALID_TOKEN
;
667 bin_len
= pInput
->pBuffers
[input_token_idx
].cbBuffer
;
669 memcpy(bin
, pInput
->pBuffers
[input_token_idx
].pvBuffer
, bin_len
);
671 lstrcpynA(buffer
, "TT ", max_len
-1);
673 if((ret
= encodeBase64(bin
, bin_len
, buffer
+3,
674 max_len
-3, &buffer_len
)) != SEC_E_OK
)
677 TRACE("Server sent: %s\n", debugstr_a(buffer
));
679 /* send TT base64 blob to ntlm_auth */
680 if((ret
= run_helper(helper
, buffer
, max_len
, &buffer_len
)) != SEC_E_OK
)
683 TRACE("Helper replied: %s\n", debugstr_a(buffer
));
685 if( (strncmp(buffer
, "KK ", 3) != 0) &&
686 (strncmp(buffer
, "AF ", 3) !=0))
688 TRACE("Helper returned %c%c\n", buffer
[0], buffer
[1]);
689 ret
= SEC_E_INVALID_TOKEN
;
693 /* decode the blob and send it to server */
694 if((ret
= decodeBase64(buffer
+3, buffer_len
-3, bin
, max_len
,
695 &bin_len
)) != SEC_E_OK
)
700 phNewContext
->dwUpper
= ctxt_attr
;
701 phNewContext
->dwLower
= (ULONG_PTR
)helper
;
706 /* put the decoded client blob into the out buffer */
708 if (!pOutput
|| ((token_idx
= ntlm_GetTokenBufferIndex(pOutput
)) == -1))
710 TRACE("no SECBUFFER_TOKEN buffer could be found\n");
711 ret
= SEC_E_BUFFER_TOO_SMALL
;
712 if ((phContext
== NULL
) && (pInput
== NULL
))
714 cleanup_helper(helper
);
715 phNewContext
->dwUpper
= 0;
716 phNewContext
->dwLower
= 0;
721 if (fContextReq
& ISC_REQ_ALLOCATE_MEMORY
)
723 pOutput
->pBuffers
[token_idx
].pvBuffer
= SECUR32_ALLOC(bin_len
);
724 pOutput
->pBuffers
[token_idx
].cbBuffer
= bin_len
;
726 else if (pOutput
->pBuffers
[token_idx
].cbBuffer
< bin_len
)
728 TRACE("out buffer is NULL or has not enough space\n");
729 ret
= SEC_E_BUFFER_TOO_SMALL
;
730 if ((phContext
== NULL
) && (pInput
== NULL
))
732 cleanup_helper(helper
);
733 phNewContext
->dwUpper
= 0;
734 phNewContext
->dwLower
= 0;
739 if (!pOutput
->pBuffers
[token_idx
].pvBuffer
)
741 TRACE("out buffer is NULL\n");
742 ret
= SEC_E_INTERNAL_ERROR
;
743 if ((phContext
== NULL
) && (pInput
== NULL
))
745 cleanup_helper(helper
);
746 phNewContext
->dwUpper
= 0;
747 phNewContext
->dwLower
= 0;
752 pOutput
->pBuffers
[token_idx
].cbBuffer
= bin_len
;
753 memcpy(pOutput
->pBuffers
[token_idx
].pvBuffer
, bin
, bin_len
);
757 TRACE("Getting negotiated flags\n");
758 lstrcpynA(buffer
, "GF", max_len
- 1);
759 if((ret
= run_helper(helper
, buffer
, max_len
, &buffer_len
)) != SEC_E_OK
)
764 TRACE("No flags negotiated.\n");
765 helper
->neg_flags
= 0l;
769 TRACE("Negotiated %s\n", debugstr_a(buffer
));
770 sscanf(buffer
+ 3, "%lx", &(helper
->neg_flags
));
771 TRACE("Stored 0x%08lx as flags\n", helper
->neg_flags
);
774 TRACE("Getting session key\n");
775 lstrcpynA(buffer
, "GK", max_len
- 1);
776 if((ret
= run_helper(helper
, buffer
, max_len
, &buffer_len
)) != SEC_E_OK
)
779 if(strncmp(buffer
, "BH", 2) == 0)
780 TRACE("No key negotiated.\n");
781 else if(strncmp(buffer
, "GK ", 3) == 0)
783 if((ret
= decodeBase64(buffer
+3, buffer_len
-3, bin
, max_len
,
784 &bin_len
)) != SEC_E_OK
)
786 TRACE("Failed to decode session key\n");
788 TRACE("Session key is %s\n", debugstr_a(buffer
+3));
789 HeapFree(GetProcessHeap(), 0, helper
->session_key
);
790 helper
->session_key
= HeapAlloc(GetProcessHeap(), 0, bin_len
);
791 if(!helper
->session_key
)
793 TRACE("Failed to allocate memory for session key\n");
794 ret
= SEC_E_INTERNAL_ERROR
;
797 memcpy(helper
->session_key
, bin
, bin_len
);
800 helper
->crypt
.ntlm
.a4i
= SECUR32_arc4Alloc();
801 SECUR32_arc4Init(helper
->crypt
.ntlm
.a4i
, helper
->session_key
, 16);
802 helper
->crypt
.ntlm
.seq_num
= 0l;
803 SECUR32_CreateNTLMv2SubKeys(helper
);
804 helper
->crypt
.ntlm2
.send_a4i
= SECUR32_arc4Alloc();
805 helper
->crypt
.ntlm2
.recv_a4i
= SECUR32_arc4Alloc();
806 SECUR32_arc4Init(helper
->crypt
.ntlm2
.send_a4i
,
807 (BYTE
*)helper
->crypt
.ntlm2
.send_seal_key
, 16);
808 SECUR32_arc4Init(helper
->crypt
.ntlm2
.recv_a4i
,
809 (BYTE
*)helper
->crypt
.ntlm2
.recv_seal_key
, 16);
810 helper
->crypt
.ntlm2
.send_seq_no
= 0l;
811 helper
->crypt
.ntlm2
.recv_seq_no
= 0l;
815 HeapFree(GetProcessHeap(), 0, want_flags
);
816 HeapFree(GetProcessHeap(), 0, buffer
);
817 HeapFree(GetProcessHeap(), 0, bin
);
821 /***********************************************************************
822 * InitializeSecurityContextA
824 static SECURITY_STATUS SEC_ENTRY
ntlm_InitializeSecurityContextA(
825 PCredHandle phCredential
, PCtxtHandle phContext
, SEC_CHAR
*pszTargetName
,
826 ULONG fContextReq
, ULONG Reserved1
, ULONG TargetDataRep
,
827 PSecBufferDesc pInput
,ULONG Reserved2
, PCtxtHandle phNewContext
,
828 PSecBufferDesc pOutput
, ULONG
*pfContextAttr
, PTimeStamp ptsExpiry
)
831 SEC_WCHAR
*target
= NULL
;
833 TRACE("%p %p %s %d %d %d %p %d %p %p %p %p\n", phCredential
, phContext
,
834 debugstr_a(pszTargetName
), fContextReq
, Reserved1
, TargetDataRep
, pInput
,
835 Reserved1
, phNewContext
, pOutput
, pfContextAttr
, ptsExpiry
);
837 if(pszTargetName
!= NULL
)
839 int target_size
= MultiByteToWideChar(CP_ACP
, 0, pszTargetName
,
840 strlen(pszTargetName
)+1, NULL
, 0);
841 target
= HeapAlloc(GetProcessHeap(), 0, target_size
*
843 MultiByteToWideChar(CP_ACP
, 0, pszTargetName
, strlen(pszTargetName
)+1,
844 target
, target_size
);
847 ret
= ntlm_InitializeSecurityContextW(phCredential
, phContext
, target
,
848 fContextReq
, Reserved1
, TargetDataRep
, pInput
, Reserved2
,
849 phNewContext
, pOutput
, pfContextAttr
, ptsExpiry
);
851 HeapFree(GetProcessHeap(), 0, target
);
855 /***********************************************************************
856 * AcceptSecurityContext
858 static SECURITY_STATUS SEC_ENTRY
ntlm_AcceptSecurityContext(
859 PCredHandle phCredential
, PCtxtHandle phContext
, PSecBufferDesc pInput
,
860 ULONG fContextReq
, ULONG TargetDataRep
, PCtxtHandle phNewContext
,
861 PSecBufferDesc pOutput
, ULONG
*pfContextAttr
, PTimeStamp ptsExpiry
)
864 char *buffer
, *want_flags
= NULL
;
866 int buffer_len
, bin_len
, max_len
= NTLM_MAX_BUF
;
869 PNtlmCredentials ntlm_cred
;
871 TRACE("%p %p %p %d %d %p %p %p %p\n", phCredential
, phContext
, pInput
,
872 fContextReq
, TargetDataRep
, phNewContext
, pOutput
, pfContextAttr
,
875 buffer
= HeapAlloc(GetProcessHeap(), 0, sizeof(char) * NTLM_MAX_BUF
);
876 bin
= HeapAlloc(GetProcessHeap(),0, sizeof(BYTE
) * NTLM_MAX_BUF
);
878 if(TargetDataRep
== SECURITY_NETWORK_DREP
){
879 TRACE("Using SECURITY_NETWORK_DREP\n");
882 if(phContext
== NULL
)
884 static CHAR server_helper_protocol
[] = "--helper-protocol=squid-2.5-ntlmssp";
885 SEC_CHAR
*server_argv
[] = { ntlm_auth
,
886 server_helper_protocol
,
891 ret
= SEC_E_INVALID_HANDLE
;
895 ntlm_cred
= (PNtlmCredentials
)phCredential
->dwLower
;
897 if(ntlm_cred
->mode
!= NTLM_SERVER
)
899 ret
= SEC_E_INVALID_HANDLE
;
903 /* This is the first call to AcceptSecurityHandle */
906 ret
= SEC_E_INCOMPLETE_MESSAGE
;
910 if(pInput
->cBuffers
< 1)
912 ret
= SEC_E_INCOMPLETE_MESSAGE
;
916 if(pInput
->pBuffers
[0].cbBuffer
> max_len
)
918 ret
= SEC_E_INVALID_TOKEN
;
922 bin_len
= pInput
->pBuffers
[0].cbBuffer
;
924 if( (ret
= fork_helper(&helper
, ntlm_auth
, server_argv
)) !=
927 ret
= SEC_E_INTERNAL_ERROR
;
930 helper
->mode
= NTLM_SERVER
;
932 /* Handle all the flags */
933 want_flags
= HeapAlloc(GetProcessHeap(), 0, 73);
934 if(want_flags
== NULL
)
936 TRACE("Failed to allocate memory for the want_flags!\n");
937 ret
= SEC_E_INSUFFICIENT_MEMORY
;
938 cleanup_helper(helper
);
941 lstrcpyA(want_flags
, "SF");
942 if(fContextReq
& ASC_REQ_ALLOCATE_MEMORY
)
944 FIXME("ASC_REQ_ALLOCATE_MEMORY stub\n");
946 if(fContextReq
& ASC_REQ_CONFIDENTIALITY
)
948 lstrcatA(want_flags
, " NTLMSSP_FEATURE_SEAL");
950 if(fContextReq
& ASC_REQ_CONNECTION
)
952 /* This is default, so we'll enable it */
953 lstrcatA(want_flags
, " NTLMSSP_FEATURE_SESSION_KEY");
954 ctxt_attr
|= ASC_RET_CONNECTION
;
956 if(fContextReq
& ASC_REQ_EXTENDED_ERROR
)
958 FIXME("ASC_REQ_EXTENDED_ERROR stub\n");
960 if(fContextReq
& ASC_REQ_INTEGRITY
)
962 lstrcatA(want_flags
, " NTLMSSP_FEATURE_SIGN");
964 if(fContextReq
& ASC_REQ_MUTUAL_AUTH
)
966 FIXME("ASC_REQ_MUTUAL_AUTH stub\n");
968 if(fContextReq
& ASC_REQ_REPLAY_DETECT
)
970 FIXME("ASC_REQ_REPLAY_DETECT stub\n");
972 if(fContextReq
& ISC_REQ_SEQUENCE_DETECT
)
974 FIXME("ASC_REQ_SEQUENCE_DETECT stub\n");
976 if(fContextReq
& ISC_REQ_STREAM
)
978 FIXME("ASC_REQ_STREAM stub\n");
980 /* Done with the flags */
982 if(lstrlenA(want_flags
) > 3)
984 TRACE("Server set want_flags: %s\n", debugstr_a(want_flags
));
985 lstrcpynA(buffer
, want_flags
, max_len
- 1);
986 if((ret
= run_helper(helper
, buffer
, max_len
, &buffer_len
)) !=
989 cleanup_helper(helper
);
992 if(!strncmp(buffer
, "BH", 2))
993 TRACE("Helper doesn't understand new command set\n");
996 /* This is the YR request from the client, encode to base64 */
998 memcpy(bin
, pInput
->pBuffers
[0].pvBuffer
, bin_len
);
1000 lstrcpynA(buffer
, "YR ", max_len
-1);
1002 if((ret
= encodeBase64(bin
, bin_len
, buffer
+3, max_len
-3,
1003 &buffer_len
)) != SEC_E_OK
)
1005 cleanup_helper(helper
);
1009 TRACE("Client sent: %s\n", debugstr_a(buffer
));
1011 if((ret
= run_helper(helper
, buffer
, max_len
, &buffer_len
)) !=
1014 cleanup_helper(helper
);
1018 TRACE("Reply from ntlm_auth: %s\n", debugstr_a(buffer
));
1019 /* The expected answer is TT <base64 blob> */
1021 if(strncmp(buffer
, "TT ", 3) != 0)
1023 ret
= SEC_E_INTERNAL_ERROR
;
1024 cleanup_helper(helper
);
1028 if((ret
= decodeBase64(buffer
+3, buffer_len
-3, bin
, max_len
,
1029 &bin_len
)) != SEC_E_OK
)
1031 cleanup_helper(helper
);
1035 /* send this to the client */
1038 ret
= SEC_E_INSUFFICIENT_MEMORY
;
1039 cleanup_helper(helper
);
1043 if(pOutput
->cBuffers
< 1)
1045 ret
= SEC_E_INSUFFICIENT_MEMORY
;
1046 cleanup_helper(helper
);
1050 pOutput
->pBuffers
[0].cbBuffer
= bin_len
;
1051 pOutput
->pBuffers
[0].BufferType
= SECBUFFER_DATA
;
1052 memcpy(pOutput
->pBuffers
[0].pvBuffer
, bin
, bin_len
);
1053 ret
= SEC_I_CONTINUE_NEEDED
;
1058 /* we expect a KK request from client */
1061 ret
= SEC_E_INCOMPLETE_MESSAGE
;
1065 if(pInput
->cBuffers
< 1)
1067 ret
= SEC_E_INCOMPLETE_MESSAGE
;
1073 ret
= SEC_E_INVALID_HANDLE
;
1077 helper
= (PNegoHelper
)phContext
->dwLower
;
1079 if(helper
->mode
!= NTLM_SERVER
)
1081 ret
= SEC_E_INVALID_HANDLE
;
1085 if(pInput
->pBuffers
[0].cbBuffer
> max_len
)
1087 ret
= SEC_E_INVALID_TOKEN
;
1091 bin_len
= pInput
->pBuffers
[0].cbBuffer
;
1093 memcpy(bin
, pInput
->pBuffers
[0].pvBuffer
, bin_len
);
1095 lstrcpynA(buffer
, "KK ", max_len
-1);
1097 if((ret
= encodeBase64(bin
, bin_len
, buffer
+3, max_len
-3,
1098 &buffer_len
)) != SEC_E_OK
)
1103 TRACE("Client sent: %s\n", debugstr_a(buffer
));
1105 if((ret
= run_helper(helper
, buffer
, max_len
, &buffer_len
)) !=
1111 TRACE("Reply from ntlm_auth: %s\n", debugstr_a(buffer
));
1113 if(strncmp(buffer
, "AF ", 3) != 0)
1115 if(strncmp(buffer
, "NA ", 3) == 0)
1117 ret
= SEC_E_LOGON_DENIED
;
1122 ret
= SEC_E_INTERNAL_ERROR
;
1126 pOutput
->pBuffers
[0].cbBuffer
= 0;
1129 TRACE("Getting negotiated flags\n");
1130 lstrcpynA(buffer
, "GF", max_len
- 1);
1131 if((ret
= run_helper(helper
, buffer
, max_len
, &buffer_len
)) != SEC_E_OK
)
1136 TRACE("No flags negotiated, or helper does not support GF command\n");
1140 TRACE("Negotiated %s\n", debugstr_a(buffer
));
1141 sscanf(buffer
+ 3, "%lx", &(helper
->neg_flags
));
1142 TRACE("Stored 0x%08lx as flags\n", helper
->neg_flags
);
1145 TRACE("Getting session key\n");
1146 lstrcpynA(buffer
, "GK", max_len
- 1);
1147 if((ret
= run_helper(helper
, buffer
, max_len
, &buffer_len
)) != SEC_E_OK
)
1151 TRACE("Helper does not support GK command\n");
1154 if(strncmp(buffer
, "BH ", 3) == 0)
1156 TRACE("Helper sent %s\n", debugstr_a(buffer
+3));
1157 helper
->session_key
= HeapAlloc(GetProcessHeap(), 0, 16);
1158 /*FIXME: Generate the dummy session key = MD4(MD4(password))*/
1159 memset(helper
->session_key
, 0 , 16);
1161 else if(strncmp(buffer
, "GK ", 3) == 0)
1163 if((ret
= decodeBase64(buffer
+3, buffer_len
-3, bin
, max_len
,
1164 &bin_len
)) != SEC_E_OK
)
1166 TRACE("Failed to decode session key\n");
1168 TRACE("Session key is %s\n", debugstr_a(buffer
+3));
1169 helper
->session_key
= HeapAlloc(GetProcessHeap(), 0, 16);
1170 if(!helper
->session_key
)
1172 TRACE("Failed to allocate memory for session key\n");
1173 ret
= SEC_E_INTERNAL_ERROR
;
1176 memcpy(helper
->session_key
, bin
, 16);
1179 helper
->crypt
.ntlm
.a4i
= SECUR32_arc4Alloc();
1180 SECUR32_arc4Init(helper
->crypt
.ntlm
.a4i
, helper
->session_key
, 16);
1181 helper
->crypt
.ntlm
.seq_num
= 0l;
1184 phNewContext
->dwUpper
= ctxt_attr
;
1185 phNewContext
->dwLower
= (ULONG_PTR
)helper
;
1188 HeapFree(GetProcessHeap(), 0, want_flags
);
1189 HeapFree(GetProcessHeap(), 0, buffer
);
1190 HeapFree(GetProcessHeap(), 0, bin
);
1194 /***********************************************************************
1197 static SECURITY_STATUS SEC_ENTRY
ntlm_CompleteAuthToken(PCtxtHandle phContext
,
1198 PSecBufferDesc pToken
)
1200 /* We never need to call CompleteAuthToken anyway */
1201 TRACE("%p %p\n", phContext
, pToken
);
1203 return SEC_E_INVALID_HANDLE
;
1208 /***********************************************************************
1209 * DeleteSecurityContext
1211 static SECURITY_STATUS SEC_ENTRY
ntlm_DeleteSecurityContext(PCtxtHandle phContext
)
1215 TRACE("%p\n", phContext
);
1217 return SEC_E_INVALID_HANDLE
;
1219 helper
= (PNegoHelper
)phContext
->dwLower
;
1221 phContext
->dwUpper
= 0;
1222 phContext
->dwLower
= 0;
1224 SECUR32_arc4Cleanup(helper
->crypt
.ntlm
.a4i
);
1225 HeapFree(GetProcessHeap(), 0, helper
->session_key
);
1226 SECUR32_arc4Cleanup(helper
->crypt
.ntlm2
.send_a4i
);
1227 SECUR32_arc4Cleanup(helper
->crypt
.ntlm2
.recv_a4i
);
1228 HeapFree(GetProcessHeap(), 0, helper
->crypt
.ntlm2
.send_sign_key
);
1229 HeapFree(GetProcessHeap(), 0, helper
->crypt
.ntlm2
.send_seal_key
);
1230 HeapFree(GetProcessHeap(), 0, helper
->crypt
.ntlm2
.recv_sign_key
);
1231 HeapFree(GetProcessHeap(), 0, helper
->crypt
.ntlm2
.recv_seal_key
);
1233 cleanup_helper(helper
);
1238 /***********************************************************************
1239 * QueryContextAttributesW
1241 static SECURITY_STATUS SEC_ENTRY
ntlm_QueryContextAttributesW(PCtxtHandle phContext
,
1242 ULONG ulAttribute
, void *pBuffer
)
1244 TRACE("%p %d %p\n", phContext
, ulAttribute
, pBuffer
);
1246 return SEC_E_INVALID_HANDLE
;
1250 #define _x(x) case (x) : FIXME(#x" stub\n"); break
1251 _x(SECPKG_ATTR_ACCESS_TOKEN
);
1252 _x(SECPKG_ATTR_AUTHORITY
);
1253 _x(SECPKG_ATTR_DCE_INFO
);
1254 case SECPKG_ATTR_FLAGS
:
1256 PSecPkgContext_Flags spcf
= (PSecPkgContext_Flags
)pBuffer
;
1257 PNegoHelper helper
= (PNegoHelper
)phContext
->dwLower
;
1260 if(helper
->neg_flags
& NTLMSSP_NEGOTIATE_SIGN
)
1261 spcf
->Flags
|= ISC_RET_INTEGRITY
;
1262 if(helper
->neg_flags
& NTLMSSP_NEGOTIATE_SEAL
)
1263 spcf
->Flags
|= ISC_RET_CONFIDENTIALITY
;
1266 _x(SECPKG_ATTR_KEY_INFO
);
1267 _x(SECPKG_ATTR_LIFESPAN
);
1268 _x(SECPKG_ATTR_NAMES
);
1269 _x(SECPKG_ATTR_NATIVE_NAMES
);
1270 _x(SECPKG_ATTR_NEGOTIATION_INFO
);
1271 _x(SECPKG_ATTR_PACKAGE_INFO
);
1272 _x(SECPKG_ATTR_PASSWORD_EXPIRY
);
1273 _x(SECPKG_ATTR_SESSION_KEY
);
1274 case SECPKG_ATTR_SIZES
:
1276 PSecPkgContext_Sizes spcs
= (PSecPkgContext_Sizes
)pBuffer
;
1277 spcs
->cbMaxToken
= NTLM_MAX_BUF
;
1278 spcs
->cbMaxSignature
= 16;
1279 spcs
->cbBlockSize
= 0;
1280 spcs
->cbSecurityTrailer
= 16;
1283 _x(SECPKG_ATTR_STREAM_SIZES
);
1284 _x(SECPKG_ATTR_TARGET_INFORMATION
);
1287 TRACE("Unknown value %d passed for ulAttribute\n", ulAttribute
);
1290 return SEC_E_UNSUPPORTED_FUNCTION
;
1293 /***********************************************************************
1294 * QueryContextAttributesA
1296 static SECURITY_STATUS SEC_ENTRY
ntlm_QueryContextAttributesA(PCtxtHandle phContext
,
1297 ULONG ulAttribute
, void *pBuffer
)
1299 return ntlm_QueryContextAttributesW(phContext
, ulAttribute
, pBuffer
);
1302 /***********************************************************************
1303 * ImpersonateSecurityContext
1305 static SECURITY_STATUS SEC_ENTRY
ntlm_ImpersonateSecurityContext(PCtxtHandle phContext
)
1307 SECURITY_STATUS ret
;
1309 TRACE("%p\n", phContext
);
1312 ret
= SEC_E_UNSUPPORTED_FUNCTION
;
1316 ret
= SEC_E_INVALID_HANDLE
;
1321 /***********************************************************************
1322 * RevertSecurityContext
1324 static SECURITY_STATUS SEC_ENTRY
ntlm_RevertSecurityContext(PCtxtHandle phContext
)
1326 SECURITY_STATUS ret
;
1328 TRACE("%p\n", phContext
);
1331 ret
= SEC_E_UNSUPPORTED_FUNCTION
;
1335 ret
= SEC_E_INVALID_HANDLE
;
1340 /***********************************************************************
1341 * ntlm_CreateSignature
1342 * As both MakeSignature and VerifySignature need this, but different keys
1343 * are needed for NTLMv2, the logic goes into a helper function.
1344 * To ensure maximal reusability, we can specify the direction as NTLM_SEND for
1345 * signing/encrypting and NTLM_RECV for verfying/decrypting. When encrypting,
1346 * the signature is encrypted after the message was encrypted, so
1347 * CreateSignature shouldn't do it. In this case, encrypt_sig can be set to
1350 static SECURITY_STATUS
ntlm_CreateSignature(PNegoHelper helper
, PSecBufferDesc pMessage
,
1351 int token_idx
, SignDirection direction
, BOOL encrypt_sig
)
1353 ULONG sign_version
= 1;
1356 TRACE("%p, %p, %d, %d, %d\n", helper
, pMessage
, token_idx
, direction
,
1359 sig
= pMessage
->pBuffers
[token_idx
].pvBuffer
;
1361 if(helper
->neg_flags
& NTLMSSP_NEGOTIATE_NTLM2
&&
1362 helper
->neg_flags
& NTLMSSP_NEGOTIATE_SIGN
)
1366 HMAC_MD5_CTX hmac_md5_ctx
;
1368 TRACE("Signing NTLM2 style\n");
1370 if(direction
== NTLM_SEND
)
1372 seq_no
[0] = (helper
->crypt
.ntlm2
.send_seq_no
>> 0) & 0xff;
1373 seq_no
[1] = (helper
->crypt
.ntlm2
.send_seq_no
>> 8) & 0xff;
1374 seq_no
[2] = (helper
->crypt
.ntlm2
.send_seq_no
>> 16) & 0xff;
1375 seq_no
[3] = (helper
->crypt
.ntlm2
.send_seq_no
>> 24) & 0xff;
1377 ++(helper
->crypt
.ntlm2
.send_seq_no
);
1379 HMACMD5Init(&hmac_md5_ctx
, helper
->crypt
.ntlm2
.send_sign_key
, 16);
1383 seq_no
[0] = (helper
->crypt
.ntlm2
.recv_seq_no
>> 0) & 0xff;
1384 seq_no
[1] = (helper
->crypt
.ntlm2
.recv_seq_no
>> 8) & 0xff;
1385 seq_no
[2] = (helper
->crypt
.ntlm2
.recv_seq_no
>> 16) & 0xff;
1386 seq_no
[3] = (helper
->crypt
.ntlm2
.recv_seq_no
>> 24) & 0xff;
1388 ++(helper
->crypt
.ntlm2
.recv_seq_no
);
1390 HMACMD5Init(&hmac_md5_ctx
, helper
->crypt
.ntlm2
.recv_sign_key
, 16);
1393 HMACMD5Update(&hmac_md5_ctx
, seq_no
, 4);
1394 for( i
= 0; i
< pMessage
->cBuffers
; ++i
)
1396 if(pMessage
->pBuffers
[i
].BufferType
& SECBUFFER_DATA
)
1397 HMACMD5Update(&hmac_md5_ctx
, (BYTE
*)pMessage
->pBuffers
[i
].pvBuffer
,
1398 pMessage
->pBuffers
[i
].cbBuffer
);
1401 HMACMD5Final(&hmac_md5_ctx
, digest
);
1403 if(encrypt_sig
&& helper
->neg_flags
& NTLMSSP_NEGOTIATE_KEY_EXCHANGE
)
1405 if(direction
== NTLM_SEND
)
1406 SECUR32_arc4Process(helper
->crypt
.ntlm2
.send_a4i
, digest
, 8);
1408 SECUR32_arc4Process(helper
->crypt
.ntlm2
.recv_a4i
, digest
, 8);
1411 /* The NTLM2 signature is the sign version */
1412 sig
[ 0] = (sign_version
>> 0) & 0xff;
1413 sig
[ 1] = (sign_version
>> 8) & 0xff;
1414 sig
[ 2] = (sign_version
>> 16) & 0xff;
1415 sig
[ 3] = (sign_version
>> 24) & 0xff;
1416 /* The first 8 bytes of the digest */
1417 memcpy(sig
+4, digest
, 8);
1418 /* And the sequence number */
1419 memcpy(sig
+12, seq_no
, 4);
1421 pMessage
->pBuffers
[token_idx
].cbBuffer
= 16;
1425 if(helper
->neg_flags
& NTLMSSP_NEGOTIATE_SIGN
)
1428 TRACE("Signing NTLM1 style\n");
1430 for(i
=0; i
< pMessage
->cBuffers
; ++i
)
1432 if(pMessage
->pBuffers
[i
].BufferType
& SECBUFFER_DATA
)
1434 crc
= ComputeCrc32(pMessage
->pBuffers
[i
].pvBuffer
,
1435 pMessage
->pBuffers
[i
].cbBuffer
, crc
);
1439 sig
[ 0] = (sign_version
>> 0) & 0xff;
1440 sig
[ 1] = (sign_version
>> 8) & 0xff;
1441 sig
[ 2] = (sign_version
>> 16) & 0xff;
1442 sig
[ 3] = (sign_version
>> 24) & 0xff;
1443 memset(sig
+4, 0, 4);
1444 sig
[ 8] = (crc
>> 0) & 0xff;
1445 sig
[ 9] = (crc
>> 8) & 0xff;
1446 sig
[10] = (crc
>> 16) & 0xff;
1447 sig
[11] = (crc
>> 24) & 0xff;
1448 sig
[12] = (helper
->crypt
.ntlm
.seq_num
>> 0) & 0xff;
1449 sig
[13] = (helper
->crypt
.ntlm
.seq_num
>> 8) & 0xff;
1450 sig
[14] = (helper
->crypt
.ntlm
.seq_num
>> 16) & 0xff;
1451 sig
[15] = (helper
->crypt
.ntlm
.seq_num
>> 24) & 0xff;
1453 ++(helper
->crypt
.ntlm
.seq_num
);
1456 SECUR32_arc4Process(helper
->crypt
.ntlm
.a4i
, sig
+4, 12);
1460 if(helper
->neg_flags
& NTLMSSP_NEGOTIATE_ALWAYS_SIGN
|| helper
->neg_flags
== 0)
1462 TRACE("Creating a dummy signature.\n");
1463 /* A dummy signature is 0x01 followed by 15 bytes of 0x00 */
1464 memset(pMessage
->pBuffers
[token_idx
].pvBuffer
, 0, 16);
1465 memset(pMessage
->pBuffers
[token_idx
].pvBuffer
, 0x01, 1);
1466 pMessage
->pBuffers
[token_idx
].cbBuffer
= 16;
1470 return SEC_E_UNSUPPORTED_FUNCTION
;
1473 /***********************************************************************
1476 static SECURITY_STATUS SEC_ENTRY
ntlm_MakeSignature(PCtxtHandle phContext
, ULONG fQOP
,
1477 PSecBufferDesc pMessage
, ULONG MessageSeqNo
)
1482 TRACE("%p %d %p %d\n", phContext
, fQOP
, pMessage
, MessageSeqNo
);
1484 return SEC_E_INVALID_HANDLE
;
1487 FIXME("Ignoring fQOP 0x%08x\n", fQOP
);
1490 FIXME("Ignoring MessageSeqNo\n");
1492 if(!pMessage
|| !pMessage
->pBuffers
|| pMessage
->cBuffers
< 2)
1493 return SEC_E_INVALID_TOKEN
;
1495 /* If we didn't find a SECBUFFER_TOKEN type buffer */
1496 if((token_idx
= ntlm_GetTokenBufferIndex(pMessage
)) == -1)
1497 return SEC_E_INVALID_TOKEN
;
1499 if(pMessage
->pBuffers
[token_idx
].cbBuffer
< 16)
1500 return SEC_E_BUFFER_TOO_SMALL
;
1502 helper
= (PNegoHelper
)phContext
->dwLower
;
1503 TRACE("Negotiated flags are: 0x%08lx\n", helper
->neg_flags
);
1505 return ntlm_CreateSignature(helper
, pMessage
, token_idx
, NTLM_SEND
, TRUE
);
1508 /***********************************************************************
1511 static SECURITY_STATUS SEC_ENTRY
ntlm_VerifySignature(PCtxtHandle phContext
,
1512 PSecBufferDesc pMessage
, ULONG MessageSeqNo
, PULONG pfQOP
)
1518 SECURITY_STATUS ret
;
1519 SecBufferDesc local_desc
;
1520 PSecBuffer local_buff
;
1523 TRACE("%p %p %d %p\n", phContext
, pMessage
, MessageSeqNo
, pfQOP
);
1525 return SEC_E_INVALID_HANDLE
;
1527 if(!pMessage
|| !pMessage
->pBuffers
|| pMessage
->cBuffers
< 2)
1528 return SEC_E_INVALID_TOKEN
;
1530 if((token_idx
= ntlm_GetTokenBufferIndex(pMessage
)) == -1)
1531 return SEC_E_INVALID_TOKEN
;
1533 if(pMessage
->pBuffers
[token_idx
].cbBuffer
< 16)
1534 return SEC_E_BUFFER_TOO_SMALL
;
1537 FIXME("Ignoring MessageSeqNo\n");
1539 helper
= (PNegoHelper
)phContext
->dwLower
;
1540 TRACE("Negotiated flags: 0x%08lx\n", helper
->neg_flags
);
1542 local_buff
= HeapAlloc(GetProcessHeap(), 0, pMessage
->cBuffers
* sizeof(SecBuffer
));
1544 local_desc
.ulVersion
= SECBUFFER_VERSION
;
1545 local_desc
.cBuffers
= pMessage
->cBuffers
;
1546 local_desc
.pBuffers
= local_buff
;
1548 for(i
=0; i
< pMessage
->cBuffers
; ++i
)
1550 if(pMessage
->pBuffers
[i
].BufferType
== SECBUFFER_TOKEN
)
1552 local_buff
[i
].BufferType
= SECBUFFER_TOKEN
;
1553 local_buff
[i
].cbBuffer
= 16;
1554 local_buff
[i
].pvBuffer
= local_sig
;
1558 local_buff
[i
].BufferType
= pMessage
->pBuffers
[i
].BufferType
;
1559 local_buff
[i
].cbBuffer
= pMessage
->pBuffers
[i
].cbBuffer
;
1560 local_buff
[i
].pvBuffer
= pMessage
->pBuffers
[i
].pvBuffer
;
1564 if((ret
= ntlm_CreateSignature(helper
, &local_desc
, token_idx
, NTLM_RECV
, TRUE
)) != SEC_E_OK
)
1567 if(memcmp(((PBYTE
)local_buff
[token_idx
].pvBuffer
) + 8,
1568 ((PBYTE
)pMessage
->pBuffers
[token_idx
].pvBuffer
) + 8, 8))
1569 ret
= SEC_E_MESSAGE_ALTERED
;
1573 HeapFree(GetProcessHeap(), 0, local_buff
);
1580 /***********************************************************************
1581 * FreeCredentialsHandle
1583 static SECURITY_STATUS SEC_ENTRY
ntlm_FreeCredentialsHandle(
1584 PCredHandle phCredential
)
1586 SECURITY_STATUS ret
;
1589 PNtlmCredentials ntlm_cred
= (PNtlmCredentials
) phCredential
->dwLower
;
1590 phCredential
->dwUpper
= 0;
1591 phCredential
->dwLower
= 0;
1592 if (ntlm_cred
->password
)
1593 memset(ntlm_cred
->password
, 0, ntlm_cred
->pwlen
);
1594 HeapFree(GetProcessHeap(), 0, ntlm_cred
->password
);
1595 HeapFree(GetProcessHeap(), 0, ntlm_cred
->username_arg
);
1596 HeapFree(GetProcessHeap(), 0, ntlm_cred
->domain_arg
);
1605 /***********************************************************************
1608 static SECURITY_STATUS SEC_ENTRY
ntlm_EncryptMessage(PCtxtHandle phContext
,
1609 ULONG fQOP
, PSecBufferDesc pMessage
, ULONG MessageSeqNo
)
1614 TRACE("(%p %d %p %d)\n", phContext
, fQOP
, pMessage
, MessageSeqNo
);
1617 return SEC_E_INVALID_HANDLE
;
1620 FIXME("Ignoring fQOP\n");
1623 FIXME("Ignoring MessageSeqNo\n");
1625 if(!pMessage
|| !pMessage
->pBuffers
|| pMessage
->cBuffers
< 2)
1626 return SEC_E_INVALID_TOKEN
;
1628 if((token_idx
= ntlm_GetTokenBufferIndex(pMessage
)) == -1)
1629 return SEC_E_INVALID_TOKEN
;
1631 if(pMessage
->pBuffers
[token_idx
].cbBuffer
< 16)
1632 return SEC_E_BUFFER_TOO_SMALL
;
1634 helper
= (PNegoHelper
) phContext
->dwLower
;
1636 if(helper
->neg_flags
& NTLMSSP_NEGOTIATE_NTLM2
&&
1637 helper
->neg_flags
& NTLMSSP_NEGOTIATE_SEAL
)
1639 ntlm_CreateSignature(helper
, pMessage
, token_idx
, NTLM_SEND
, FALSE
);
1640 SECUR32_arc4Process(helper
->crypt
.ntlm2
.send_a4i
,
1641 (BYTE
*)pMessage
->pBuffers
[1].pvBuffer
,
1642 pMessage
->pBuffers
[1].cbBuffer
);
1644 if(helper
->neg_flags
& NTLMSSP_NEGOTIATE_KEY_EXCHANGE
)
1645 SECUR32_arc4Process(helper
->crypt
.ntlm2
.send_a4i
,
1646 ((BYTE
*)pMessage
->pBuffers
[token_idx
].pvBuffer
)+4, 8);
1656 /* EncryptMessage always produces real signatures, so make sure
1657 * NTLMSSP_NEGOTIATE_SIGN is set*/
1658 save_flags
= helper
->neg_flags
;
1659 helper
->neg_flags
|= NTLMSSP_NEGOTIATE_SIGN
;
1660 ntlm_CreateSignature(helper
, pMessage
, token_idx
, NTLM_SEND
, FALSE
);
1661 helper
->neg_flags
= save_flags
;
1663 sig
= pMessage
->pBuffers
[token_idx
].pvBuffer
;
1665 SECUR32_arc4Process(helper
->crypt
.ntlm
.a4i
, pMessage
->pBuffers
[1].pvBuffer
,
1666 pMessage
->pBuffers
[1].cbBuffer
);
1667 SECUR32_arc4Process(helper
->crypt
.ntlm
.a4i
, sig
+4, 12);
1669 if(helper
->neg_flags
& NTLMSSP_NEGOTIATE_ALWAYS_SIGN
|| helper
->neg_flags
== 0)
1670 memset(sig
+4, 0, 4);
1677 /***********************************************************************
1680 static SECURITY_STATUS SEC_ENTRY
ntlm_DecryptMessage(PCtxtHandle phContext
,
1681 PSecBufferDesc pMessage
, ULONG MessageSeqNo
, PULONG pfQOP
)
1683 SECURITY_STATUS ret
;
1684 ULONG ntlmssp_flags_save
;
1687 TRACE("(%p %p %d %p)\n", phContext
, pMessage
, MessageSeqNo
, pfQOP
);
1690 return SEC_E_INVALID_HANDLE
;
1693 FIXME("Ignoring MessageSeqNo\n");
1695 if(!pMessage
|| !pMessage
->pBuffers
|| pMessage
->cBuffers
< 2)
1696 return SEC_E_INVALID_TOKEN
;
1698 if((token_idx
= ntlm_GetTokenBufferIndex(pMessage
)) == -1)
1699 return SEC_E_INVALID_TOKEN
;
1701 if(pMessage
->pBuffers
[token_idx
].cbBuffer
< 16)
1702 return SEC_E_BUFFER_TOO_SMALL
;
1704 helper
= (PNegoHelper
) phContext
->dwLower
;
1706 if(helper
->neg_flags
& NTLMSSP_NEGOTIATE_NTLM2
&& helper
->neg_flags
& NTLMSSP_NEGOTIATE_SEAL
)
1708 SECUR32_arc4Process(helper
->crypt
.ntlm2
.recv_a4i
,
1709 pMessage
->pBuffers
[1].pvBuffer
, pMessage
->pBuffers
[1].cbBuffer
);
1713 SECUR32_arc4Process(helper
->crypt
.ntlm
.a4i
,
1714 pMessage
->pBuffers
[1].pvBuffer
, pMessage
->pBuffers
[1].cbBuffer
);
1717 /* Make sure we use a session key for the signature check, EncryptMessage
1718 * always does that, even in the dummy case */
1719 ntlmssp_flags_save
= helper
->neg_flags
;
1721 helper
->neg_flags
|= NTLMSSP_NEGOTIATE_SIGN
;
1722 ret
= ntlm_VerifySignature(phContext
, pMessage
, MessageSeqNo
, pfQOP
);
1724 helper
->neg_flags
= ntlmssp_flags_save
;
1729 static const SecurityFunctionTableA ntlmTableA
= {
1731 NULL
, /* EnumerateSecurityPackagesA */
1732 ntlm_QueryCredentialsAttributesA
, /* QueryCredentialsAttributesA */
1733 ntlm_AcquireCredentialsHandleA
, /* AcquireCredentialsHandleA */
1734 ntlm_FreeCredentialsHandle
, /* FreeCredentialsHandle */
1735 NULL
, /* Reserved2 */
1736 ntlm_InitializeSecurityContextA
, /* InitializeSecurityContextA */
1737 ntlm_AcceptSecurityContext
, /* AcceptSecurityContext */
1738 ntlm_CompleteAuthToken
, /* CompleteAuthToken */
1739 ntlm_DeleteSecurityContext
, /* DeleteSecurityContext */
1740 NULL
, /* ApplyControlToken */
1741 ntlm_QueryContextAttributesA
, /* QueryContextAttributesA */
1742 ntlm_ImpersonateSecurityContext
, /* ImpersonateSecurityContext */
1743 ntlm_RevertSecurityContext
, /* RevertSecurityContext */
1744 ntlm_MakeSignature
, /* MakeSignature */
1745 ntlm_VerifySignature
, /* VerifySignature */
1746 FreeContextBuffer
, /* FreeContextBuffer */
1747 NULL
, /* QuerySecurityPackageInfoA */
1748 NULL
, /* Reserved3 */
1749 NULL
, /* Reserved4 */
1750 NULL
, /* ExportSecurityContext */
1751 NULL
, /* ImportSecurityContextA */
1752 NULL
, /* AddCredentialsA */
1753 NULL
, /* Reserved8 */
1754 NULL
, /* QuerySecurityContextToken */
1755 ntlm_EncryptMessage
, /* EncryptMessage */
1756 ntlm_DecryptMessage
, /* DecryptMessage */
1757 NULL
, /* SetContextAttributesA */
1760 static const SecurityFunctionTableW ntlmTableW
= {
1762 NULL
, /* EnumerateSecurityPackagesW */
1763 ntlm_QueryCredentialsAttributesW
, /* QueryCredentialsAttributesW */
1764 ntlm_AcquireCredentialsHandleW
, /* AcquireCredentialsHandleW */
1765 ntlm_FreeCredentialsHandle
, /* FreeCredentialsHandle */
1766 NULL
, /* Reserved2 */
1767 ntlm_InitializeSecurityContextW
, /* InitializeSecurityContextW */
1768 ntlm_AcceptSecurityContext
, /* AcceptSecurityContext */
1769 ntlm_CompleteAuthToken
, /* CompleteAuthToken */
1770 ntlm_DeleteSecurityContext
, /* DeleteSecurityContext */
1771 NULL
, /* ApplyControlToken */
1772 ntlm_QueryContextAttributesW
, /* QueryContextAttributesW */
1773 ntlm_ImpersonateSecurityContext
, /* ImpersonateSecurityContext */
1774 ntlm_RevertSecurityContext
, /* RevertSecurityContext */
1775 ntlm_MakeSignature
, /* MakeSignature */
1776 ntlm_VerifySignature
, /* VerifySignature */
1777 FreeContextBuffer
, /* FreeContextBuffer */
1778 NULL
, /* QuerySecurityPackageInfoW */
1779 NULL
, /* Reserved3 */
1780 NULL
, /* Reserved4 */
1781 NULL
, /* ExportSecurityContext */
1782 NULL
, /* ImportSecurityContextW */
1783 NULL
, /* AddCredentialsW */
1784 NULL
, /* Reserved8 */
1785 NULL
, /* QuerySecurityContextToken */
1786 ntlm_EncryptMessage
, /* EncryptMessage */
1787 ntlm_DecryptMessage
, /* DecryptMessage */
1788 NULL
, /* SetContextAttributesW */
1791 #define NTLM_COMMENT \
1792 { 'N', 'T', 'L', 'M', ' ', \
1793 'S', 'e', 'c', 'u', 'r', 'i', 't', 'y', ' ', \
1794 'P', 'a', 'c', 'k', 'a', 'g', 'e', 0}
1796 static CHAR ntlm_comment_A
[] = NTLM_COMMENT
;
1797 static WCHAR ntlm_comment_W
[] = NTLM_COMMENT
;
1799 #define NTLM_NAME {'N', 'T', 'L', 'M', 0}
1801 static char ntlm_name_A
[] = NTLM_NAME
;
1802 static WCHAR ntlm_name_W
[] = NTLM_NAME
;
1804 /* According to Windows, NTLM has the following capabilities. */
1806 SECPKG_FLAG_INTEGRITY | \
1807 SECPKG_FLAG_PRIVACY | \
1808 SECPKG_FLAG_TOKEN_ONLY | \
1809 SECPKG_FLAG_CONNECTION | \
1810 SECPKG_FLAG_MULTI_REQUIRED | \
1811 SECPKG_FLAG_IMPERSONATION | \
1812 SECPKG_FLAG_ACCEPT_WIN32_NAME | \
1813 SECPKG_FLAG_READONLY_WITH_CHECKSUM)
1815 static const SecPkgInfoW infoW
= {
1824 static const SecPkgInfoA infoA
= {
1833 void SECUR32_initNTLMSP(void)
1835 SECURITY_STATUS ret
;
1837 static CHAR version
[] = "--version";
1839 SEC_CHAR
*args
[] = {
1844 if((ret
= fork_helper(&helper
, ntlm_auth
, args
)) != SEC_E_OK
)
1846 /* Cheat and allocate a helper anyway, so cleanup later will work. */
1847 helper
= HeapAlloc(GetProcessHeap(),0, sizeof(PNegoHelper
));
1848 helper
->major
= helper
->minor
= helper
->micro
= -1;
1851 check_version(helper
);
1853 if( (helper
->major
> MIN_NTLM_AUTH_MAJOR_VERSION
) ||
1854 (helper
->major
== MIN_NTLM_AUTH_MAJOR_VERSION
&&
1855 helper
->minor
> MIN_NTLM_AUTH_MINOR_VERSION
) ||
1856 (helper
->major
== MIN_NTLM_AUTH_MAJOR_VERSION
&&
1857 helper
->minor
== MIN_NTLM_AUTH_MINOR_VERSION
&&
1858 helper
->micro
>= MIN_NTLM_AUTH_MICRO_VERSION
) )
1860 SecureProvider
*provider
= SECUR32_addProvider(&ntlmTableA
, &ntlmTableW
, NULL
);
1861 SECUR32_addPackages(provider
, 1L, &infoA
, &infoW
);
1865 ERR("%s was not found or is outdated. "
1866 "Make sure that ntlm_auth >= %d.%d.%d is in your path.\n",
1868 MIN_NTLM_AUTH_MAJOR_VERSION
,
1869 MIN_NTLM_AUTH_MINOR_VERSION
,
1870 MIN_NTLM_AUTH_MICRO_VERSION
);
1872 cleanup_helper(helper
);