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 ntlm_cred
= HeapAlloc(GetProcessHeap(), 0, sizeof(*ntlm_cred
));
139 ret
= SEC_E_INSUFFICIENT_MEMORY
;
142 ntlm_cred
->mode
= NTLM_CLIENT
;
143 ntlm_cred
->username_arg
= NULL
;
144 ntlm_cred
->domain_arg
= NULL
;
145 ntlm_cred
->password
= NULL
;
146 ntlm_cred
->pwlen
= 0;
148 if(pAuthData
!= NULL
)
150 PSEC_WINNT_AUTH_IDENTITY_W auth_data
=
151 (PSEC_WINNT_AUTH_IDENTITY_W
)pAuthData
;
153 TRACE("Username is %s\n", debugstr_wn(auth_data
->User
, auth_data
->UserLength
));
154 TRACE("Domain name is %s\n", debugstr_wn(auth_data
->Domain
, auth_data
->DomainLength
));
156 /* Get username and domain from pAuthData */
157 unixcp_size
= WideCharToMultiByte(CP_UNIXCP
, WC_NO_BEST_FIT_CHARS
,
158 auth_data
->User
, auth_data
->UserLength
, NULL
, 0, NULL
, NULL
) + sizeof(username_arg
);
159 ntlm_cred
->username_arg
= HeapAlloc(GetProcessHeap(), 0, unixcp_size
);
160 memcpy(ntlm_cred
->username_arg
, username_arg
, sizeof(username_arg
) - 1);
161 WideCharToMultiByte(CP_UNIXCP
, WC_NO_BEST_FIT_CHARS
, auth_data
->User
, auth_data
->UserLength
,
162 ntlm_cred
->username_arg
+ sizeof(username_arg
) - 1,
163 unixcp_size
- sizeof(username_arg
) + 1, NULL
, NULL
);
164 ntlm_cred
->username_arg
[unixcp_size
- 1] = '\0';
166 unixcp_size
= WideCharToMultiByte(CP_UNIXCP
, WC_NO_BEST_FIT_CHARS
,
167 auth_data
->Domain
, auth_data
->DomainLength
, NULL
, 0, NULL
, NULL
) + sizeof(domain_arg
);
168 ntlm_cred
->domain_arg
= HeapAlloc(GetProcessHeap(), 0, unixcp_size
);
169 memcpy(ntlm_cred
->domain_arg
, domain_arg
, sizeof(domain_arg
) - 1);
170 WideCharToMultiByte(CP_UNIXCP
, WC_NO_BEST_FIT_CHARS
, auth_data
->Domain
,
171 auth_data
->DomainLength
, ntlm_cred
->domain_arg
+ sizeof(domain_arg
) - 1,
172 unixcp_size
- sizeof(domain
) + 1, NULL
, NULL
);
173 ntlm_cred
->domain_arg
[unixcp_size
- 1] = '\0';
175 if(auth_data
->PasswordLength
!= 0)
177 ntlm_cred
->pwlen
= WideCharToMultiByte(CP_UNIXCP
,
178 WC_NO_BEST_FIT_CHARS
, auth_data
->Password
,
179 auth_data
->PasswordLength
, NULL
, 0, NULL
,
182 ntlm_cred
->password
= HeapAlloc(GetProcessHeap(), 0,
185 WideCharToMultiByte(CP_UNIXCP
, WC_NO_BEST_FIT_CHARS
,
186 auth_data
->Password
, auth_data
->PasswordLength
,
187 ntlm_cred
->password
, ntlm_cred
->pwlen
, NULL
, NULL
);
191 phCredential
->dwUpper
= fCredentialUse
;
192 phCredential
->dwLower
= (ULONG_PTR
)ntlm_cred
;
193 TRACE("ACH phCredential->dwUpper: 0x%08lx, dwLower: 0x%08lx\n",
194 phCredential
->dwUpper
, phCredential
->dwLower
);
198 case SECPKG_CRED_BOTH
:
199 FIXME("AcquireCredentialsHandle: SECPKG_CRED_BOTH stub\n");
200 ret
= SEC_E_UNSUPPORTED_FUNCTION
;
205 ret
= SEC_E_UNKNOWN_CREDENTIALS
;
208 HeapFree(GetProcessHeap(), 0, username
);
209 HeapFree(GetProcessHeap(), 0, domain
);
214 /***********************************************************************
215 * AcquireCredentialsHandleA
217 static SECURITY_STATUS SEC_ENTRY
ntlm_AcquireCredentialsHandleA(
218 SEC_CHAR
*pszPrincipal
, SEC_CHAR
*pszPackage
, ULONG fCredentialUse
,
219 PLUID pLogonID
, PVOID pAuthData
, SEC_GET_KEY_FN pGetKeyFn
,
220 PVOID pGetKeyArgument
, PCredHandle phCredential
, PTimeStamp ptsExpiry
)
223 int user_sizeW
, domain_sizeW
, passwd_sizeW
;
225 SEC_WCHAR
*user
= NULL
, *domain
= NULL
, *passwd
= NULL
, *package
= NULL
;
227 PSEC_WINNT_AUTH_IDENTITY_W pAuthDataW
= NULL
;
228 PSEC_WINNT_AUTH_IDENTITY_A identity
= NULL
;
230 TRACE("(%s, %s, 0x%08x, %p, %p, %p, %p, %p, %p)\n",
231 debugstr_a(pszPrincipal
), debugstr_a(pszPackage
), fCredentialUse
,
232 pLogonID
, pAuthData
, pGetKeyFn
, pGetKeyArgument
, phCredential
, ptsExpiry
);
234 if(pszPackage
!= NULL
)
236 int package_sizeW
= MultiByteToWideChar(CP_ACP
, 0, pszPackage
, -1,
239 package
= HeapAlloc(GetProcessHeap(), 0, package_sizeW
*
241 MultiByteToWideChar(CP_ACP
, 0, pszPackage
, -1, package
, package_sizeW
);
245 if(pAuthData
!= NULL
)
247 identity
= (PSEC_WINNT_AUTH_IDENTITY_A
)pAuthData
;
249 if(identity
->Flags
== SEC_WINNT_AUTH_IDENTITY_ANSI
)
251 pAuthDataW
= HeapAlloc(GetProcessHeap(), 0,
252 sizeof(SEC_WINNT_AUTH_IDENTITY_W
));
254 if(identity
->UserLength
!= 0)
256 user_sizeW
= MultiByteToWideChar(CP_ACP
, 0,
257 (LPCSTR
)identity
->User
, identity
->UserLength
, NULL
, 0);
258 user
= HeapAlloc(GetProcessHeap(), 0, user_sizeW
*
260 MultiByteToWideChar(CP_ACP
, 0, (LPCSTR
)identity
->User
,
261 identity
->UserLength
, user
, user_sizeW
);
268 if(identity
->DomainLength
!= 0)
270 domain_sizeW
= MultiByteToWideChar(CP_ACP
, 0,
271 (LPCSTR
)identity
->Domain
, identity
->DomainLength
, NULL
, 0);
272 domain
= HeapAlloc(GetProcessHeap(), 0, domain_sizeW
273 * sizeof(SEC_WCHAR
));
274 MultiByteToWideChar(CP_ACP
, 0, (LPCSTR
)identity
->Domain
,
275 identity
->DomainLength
, domain
, domain_sizeW
);
282 if(identity
->PasswordLength
!= 0)
284 passwd_sizeW
= MultiByteToWideChar(CP_ACP
, 0,
285 (LPCSTR
)identity
->Password
, identity
->PasswordLength
,
287 passwd
= HeapAlloc(GetProcessHeap(), 0, passwd_sizeW
288 * sizeof(SEC_WCHAR
));
289 MultiByteToWideChar(CP_ACP
, 0, (LPCSTR
)identity
->Password
,
290 identity
->PasswordLength
, passwd
, passwd_sizeW
);
297 pAuthDataW
->Flags
= SEC_WINNT_AUTH_IDENTITY_UNICODE
;
298 pAuthDataW
->User
= user
;
299 pAuthDataW
->UserLength
= user_sizeW
;
300 pAuthDataW
->Domain
= domain
;
301 pAuthDataW
->DomainLength
= domain_sizeW
;
302 pAuthDataW
->Password
= passwd
;
303 pAuthDataW
->PasswordLength
= passwd_sizeW
;
307 pAuthDataW
= (PSEC_WINNT_AUTH_IDENTITY_W
)identity
;
311 ret
= ntlm_AcquireCredentialsHandleW(NULL
, package
, fCredentialUse
,
312 pLogonID
, pAuthDataW
, pGetKeyFn
, pGetKeyArgument
, phCredential
,
315 HeapFree(GetProcessHeap(), 0, package
);
316 HeapFree(GetProcessHeap(), 0, user
);
317 HeapFree(GetProcessHeap(), 0, domain
);
318 HeapFree(GetProcessHeap(), 0, passwd
);
319 if(pAuthDataW
!= (PSEC_WINNT_AUTH_IDENTITY_W
)identity
)
320 HeapFree(GetProcessHeap(), 0, pAuthDataW
);
325 /*************************************************************************
326 * ntlm_GetTokenBufferIndex
327 * Calculates the index of the secbuffer with BufferType == SECBUFFER_TOKEN
328 * Returns index if found or -1 if not found.
330 static int ntlm_GetTokenBufferIndex(PSecBufferDesc pMessage
)
334 TRACE("%p\n", pMessage
);
336 for( i
= 0; i
< pMessage
->cBuffers
; ++i
)
338 if(pMessage
->pBuffers
[i
].BufferType
== SECBUFFER_TOKEN
)
345 /*************************************************************************
346 * ntlm_GetDataBufferIndex
347 * Calculates the index of the first secbuffer with BufferType == SECBUFFER_DATA
348 * Returns index if found or -1 if not found.
350 static int ntlm_GetDataBufferIndex(PSecBufferDesc pMessage
)
354 TRACE("%p\n", pMessage
);
356 for( i
= 0; i
< pMessage
->cBuffers
; ++i
)
358 if(pMessage
->pBuffers
[i
].BufferType
== SECBUFFER_DATA
)
365 /***********************************************************************
366 * InitializeSecurityContextW
368 static SECURITY_STATUS SEC_ENTRY
ntlm_InitializeSecurityContextW(
369 PCredHandle phCredential
, PCtxtHandle phContext
, SEC_WCHAR
*pszTargetName
,
370 ULONG fContextReq
, ULONG Reserved1
, ULONG TargetDataRep
,
371 PSecBufferDesc pInput
, ULONG Reserved2
, PCtxtHandle phNewContext
,
372 PSecBufferDesc pOutput
, ULONG
*pfContextAttr
, PTimeStamp ptsExpiry
)
375 PNtlmCredentials ntlm_cred
= NULL
;
376 PNegoHelper helper
= NULL
;
378 char* buffer
, *want_flags
= NULL
;
380 int buffer_len
, bin_len
, max_len
= NTLM_MAX_BUF
;
382 SEC_CHAR
*username
= NULL
;
384 TRACE("%p %p %s %d %d %d %p %d %p %p %p %p\n", phCredential
, phContext
,
385 debugstr_w(pszTargetName
), fContextReq
, Reserved1
, TargetDataRep
, pInput
,
386 Reserved1
, phNewContext
, pOutput
, pfContextAttr
, ptsExpiry
);
388 /****************************************
389 * When communicating with the client, there can be the
390 * following reply packets:
391 * YR <base64 blob> should be sent to the server
392 * PW should be sent back to helper with
393 * base64 encoded password
394 * AF <base64 blob> client is done, blob should be
395 * sent to server with KK prefixed
396 * GF <string list> A string list of negotiated flags
397 * GK <base64 blob> base64 encoded session key
398 * BH <char reason> something broke
400 /* The squid cache size is 2010 chars, and that's what ntlm_auth uses */
404 TRACE("According to a MS whitepaper pszTargetName is ignored.\n");
407 if(TargetDataRep
== SECURITY_NETWORK_DREP
){
408 TRACE("Setting SECURITY_NETWORK_DREP\n");
411 buffer
= HeapAlloc(GetProcessHeap(), 0, sizeof(char) * NTLM_MAX_BUF
);
412 bin
= HeapAlloc(GetProcessHeap(), 0, sizeof(BYTE
) * NTLM_MAX_BUF
);
414 if((phContext
== NULL
) && (pInput
== NULL
))
416 static char helper_protocol
[] = "--helper-protocol=ntlmssp-client-1";
417 static CHAR credentials_argv
[] = "--use-cached-creds";
418 SEC_CHAR
*client_argv
[5];
420 TRACE("First time in ISC()\n");
424 ret
= SEC_E_INVALID_HANDLE
;
428 /* As the server side of sspi never calls this, make sure that
429 * the handler is a client handler.
431 ntlm_cred
= (PNtlmCredentials
)phCredential
->dwLower
;
432 if(ntlm_cred
->mode
!= NTLM_CLIENT
)
434 TRACE("Cred mode = %d\n", ntlm_cred
->mode
);
435 ret
= SEC_E_INVALID_HANDLE
;
439 client_argv
[0] = ntlm_auth
;
440 client_argv
[1] = helper_protocol
;
441 if (!ntlm_cred
->username_arg
&& !ntlm_cred
->domain_arg
)
443 LPWKSTA_USER_INFO_1 ui
= NULL
;
444 NET_API_STATUS status
;
446 static const char username_arg
[] = "--username=";
448 status
= NetWkstaUserGetInfo(NULL
, 1, (LPBYTE
*)&ui
);
449 if (status
!= NERR_Success
|| ui
== NULL
)
451 ret
= SEC_E_NO_CREDENTIALS
;
455 unixcp_size
= WideCharToMultiByte(CP_UNIXCP
, WC_NO_BEST_FIT_CHARS
,
456 ui
->wkui1_username
, -1, NULL
, 0, NULL
, NULL
) + sizeof(username_arg
);
457 username
= HeapAlloc(GetProcessHeap(), 0, unixcp_size
);
458 memcpy(username
, username_arg
, sizeof(username_arg
) - 1);
459 WideCharToMultiByte(CP_UNIXCP
, WC_NO_BEST_FIT_CHARS
, ui
->wkui1_username
, -1,
460 username
+ sizeof(username_arg
) - 1,
461 unixcp_size
- sizeof(username_arg
) + 1, NULL
, NULL
);
462 username
[unixcp_size
- 1] = '\0';
464 TRACE("using cached credentials\n");
466 client_argv
[2] = username
;
467 client_argv
[3] = credentials_argv
;
468 client_argv
[4] = NULL
;
472 client_argv
[2] = ntlm_cred
->username_arg
;
473 client_argv
[3] = ntlm_cred
->domain_arg
;
474 client_argv
[4] = NULL
;
477 if((ret
= fork_helper(&helper
, ntlm_auth
, client_argv
)) != SEC_E_OK
)
480 helper
->mode
= NTLM_CLIENT
;
481 helper
->session_key
= HeapAlloc(GetProcessHeap(), 0, 16);
482 if (!helper
->session_key
)
484 cleanup_helper(helper
);
485 ret
= SEC_E_INSUFFICIENT_MEMORY
;
489 /* Generate the dummy session key = MD4(MD4(password))*/
490 if(ntlm_cred
->password
)
492 SEC_WCHAR
*unicode_password
;
495 TRACE("Converting password to unicode.\n");
496 passwd_lenW
= MultiByteToWideChar(CP_ACP
, 0,
497 (LPCSTR
)ntlm_cred
->password
, ntlm_cred
->pwlen
,
499 unicode_password
= HeapAlloc(GetProcessHeap(), 0,
500 passwd_lenW
* sizeof(SEC_WCHAR
));
501 MultiByteToWideChar(CP_ACP
, 0, (LPCSTR
)ntlm_cred
->password
,
502 ntlm_cred
->pwlen
, unicode_password
, passwd_lenW
);
504 SECUR32_CreateNTLMv1SessionKey((PBYTE
)unicode_password
,
505 passwd_lenW
* sizeof(SEC_WCHAR
), helper
->session_key
);
507 HeapFree(GetProcessHeap(), 0, unicode_password
);
510 memset(helper
->session_key
, 0, 16);
512 /* Allocate space for a maximal string of
513 * "SF NTLMSSP_FEATURE_SIGN NTLMSSP_FEATURE_SEAL
514 * NTLMSSP_FEATURE_SESSION_KEY"
516 want_flags
= HeapAlloc(GetProcessHeap(), 0, 73);
517 if(want_flags
== NULL
)
519 cleanup_helper(helper
);
520 ret
= SEC_E_INSUFFICIENT_MEMORY
;
523 lstrcpyA(want_flags
, "SF");
524 if(fContextReq
& ISC_REQ_CONFIDENTIALITY
)
527 if((ptr
= strstr(want_flags
, "NTLMSSP_FEATURE_SEAL")) == NULL
)
528 lstrcatA(want_flags
, " NTLMSSP_FEATURE_SEAL");
530 if(fContextReq
& ISC_REQ_CONNECTION
)
531 ctxt_attr
|= ISC_RET_CONNECTION
;
532 if(fContextReq
& ISC_REQ_EXTENDED_ERROR
)
533 ctxt_attr
|= ISC_RET_EXTENDED_ERROR
;
534 if(fContextReq
& ISC_REQ_INTEGRITY
)
537 if((ptr
= strstr(want_flags
, "NTLMSSP_FEATURE_SIGN")) == NULL
)
538 lstrcatA(want_flags
, " NTLMSSP_FEATURE_SIGN");
540 if(fContextReq
& ISC_REQ_MUTUAL_AUTH
)
541 ctxt_attr
|= ISC_RET_MUTUAL_AUTH
;
542 if(fContextReq
& ISC_REQ_REPLAY_DETECT
)
545 if((ptr
= strstr(want_flags
, "NTLMSSP_FEATURE_SIGN")) == NULL
)
546 lstrcatA(want_flags
, " NTLMSSP_FEATURE_SIGN");
548 if(fContextReq
& ISC_REQ_SEQUENCE_DETECT
)
551 if((ptr
= strstr(want_flags
, "NTLMSSP_FEATURE_SIGN")) == NULL
)
552 lstrcatA(want_flags
, " NTLMSSP_FEATURE_SIGN");
554 if(fContextReq
& ISC_REQ_STREAM
)
555 FIXME("ISC_REQ_STREAM\n");
556 if(fContextReq
& ISC_REQ_USE_DCE_STYLE
)
557 ctxt_attr
|= ISC_RET_USED_DCE_STYLE
;
558 if(fContextReq
& ISC_REQ_DELEGATE
)
559 ctxt_attr
|= ISC_RET_DELEGATE
;
561 /* If no password is given, try to use cached credentials. Fall back to an empty
562 * password if this failed. */
563 if(ntlm_cred
->password
== NULL
)
565 lstrcpynA(buffer
, "OK", max_len
-1);
566 if((ret
= run_helper(helper
, buffer
, max_len
, &buffer_len
)) != SEC_E_OK
)
568 cleanup_helper(helper
);
571 /* If the helper replied with "PW", using cached credentials failed */
572 if(!strncmp(buffer
, "PW", 2))
574 TRACE("Using cached credentials failed.\n");
575 ret
= SEC_E_NO_CREDENTIALS
;
578 else /* Just do a noop on the next run */
579 lstrcpynA(buffer
, "OK", max_len
-1);
583 lstrcpynA(buffer
, "PW ", max_len
-1);
584 if((ret
= encodeBase64((unsigned char*)ntlm_cred
->password
,
585 ntlm_cred
->pwlen
, buffer
+3,
586 max_len
-3, &buffer_len
)) != SEC_E_OK
)
588 cleanup_helper(helper
);
594 TRACE("Sending to helper: %s\n", debugstr_a(buffer
));
595 if((ret
= run_helper(helper
, buffer
, max_len
, &buffer_len
)) != SEC_E_OK
)
597 cleanup_helper(helper
);
601 TRACE("Helper returned %s\n", debugstr_a(buffer
));
603 if(lstrlenA(want_flags
) > 2)
605 TRACE("Want flags are %s\n", debugstr_a(want_flags
));
606 lstrcpynA(buffer
, want_flags
, max_len
-1);
607 if((ret
= run_helper(helper
, buffer
, max_len
, &buffer_len
))
610 if(!strncmp(buffer
, "BH", 2))
611 ERR("Helper doesn't understand new command set. Expect more things to fail.\n");
614 lstrcpynA(buffer
, "YR", max_len
-1);
616 if((ret
= run_helper(helper
, buffer
, max_len
, &buffer_len
)) != SEC_E_OK
)
618 cleanup_helper(helper
);
622 TRACE("%s\n", buffer
);
624 if(strncmp(buffer
, "YR ", 3) != 0)
626 /* Something borked */
627 TRACE("Helper returned %c%c\n", buffer
[0], buffer
[1]);
628 ret
= SEC_E_INTERNAL_ERROR
;
629 cleanup_helper(helper
);
632 if((ret
= decodeBase64(buffer
+3, buffer_len
-3, bin
,
633 max_len
-1, &bin_len
)) != SEC_E_OK
)
635 cleanup_helper(helper
);
639 /* put the decoded client blob into the out buffer */
641 phNewContext
->dwUpper
= ctxt_attr
;
642 phNewContext
->dwLower
= (ULONG_PTR
)helper
;
644 ret
= SEC_I_CONTINUE_NEEDED
;
650 /* handle second call here */
651 /* encode server data to base64 */
652 if (!pInput
|| ((input_token_idx
= ntlm_GetTokenBufferIndex(pInput
)) == -1))
654 ret
= SEC_E_INVALID_TOKEN
;
660 ret
= SEC_E_INVALID_HANDLE
;
664 /* As the server side of sspi never calls this, make sure that
665 * the handler is a client handler.
667 helper
= (PNegoHelper
)phContext
->dwLower
;
668 if(helper
->mode
!= NTLM_CLIENT
)
670 TRACE("Helper mode = %d\n", helper
->mode
);
671 ret
= SEC_E_INVALID_HANDLE
;
675 if (!pInput
->pBuffers
[input_token_idx
].pvBuffer
)
677 ret
= SEC_E_INTERNAL_ERROR
;
681 if(pInput
->pBuffers
[input_token_idx
].cbBuffer
> max_len
)
683 TRACE("pInput->pBuffers[%d].cbBuffer is: %ld\n",
685 pInput
->pBuffers
[input_token_idx
].cbBuffer
);
686 ret
= SEC_E_INVALID_TOKEN
;
690 bin_len
= pInput
->pBuffers
[input_token_idx
].cbBuffer
;
692 memcpy(bin
, pInput
->pBuffers
[input_token_idx
].pvBuffer
, bin_len
);
694 lstrcpynA(buffer
, "TT ", max_len
-1);
696 if((ret
= encodeBase64(bin
, bin_len
, buffer
+3,
697 max_len
-3, &buffer_len
)) != SEC_E_OK
)
700 TRACE("Server sent: %s\n", debugstr_a(buffer
));
702 /* send TT base64 blob to ntlm_auth */
703 if((ret
= run_helper(helper
, buffer
, max_len
, &buffer_len
)) != SEC_E_OK
)
706 TRACE("Helper replied: %s\n", debugstr_a(buffer
));
708 if( (strncmp(buffer
, "KK ", 3) != 0) &&
709 (strncmp(buffer
, "AF ", 3) !=0))
711 TRACE("Helper returned %c%c\n", buffer
[0], buffer
[1]);
712 ret
= SEC_E_INVALID_TOKEN
;
716 /* decode the blob and send it to server */
717 if((ret
= decodeBase64(buffer
+3, buffer_len
-3, bin
, max_len
,
718 &bin_len
)) != SEC_E_OK
)
723 phNewContext
->dwUpper
= ctxt_attr
;
724 phNewContext
->dwLower
= (ULONG_PTR
)helper
;
729 /* put the decoded client blob into the out buffer */
731 if (!pOutput
|| ((token_idx
= ntlm_GetTokenBufferIndex(pOutput
)) == -1))
733 TRACE("no SECBUFFER_TOKEN buffer could be found\n");
734 ret
= SEC_E_BUFFER_TOO_SMALL
;
735 if ((phContext
== NULL
) && (pInput
== NULL
))
737 cleanup_helper(helper
);
738 phNewContext
->dwUpper
= 0;
739 phNewContext
->dwLower
= 0;
744 if (fContextReq
& ISC_REQ_ALLOCATE_MEMORY
)
746 pOutput
->pBuffers
[token_idx
].pvBuffer
= HeapAlloc(GetProcessHeap(), 0, bin_len
);
747 pOutput
->pBuffers
[token_idx
].cbBuffer
= bin_len
;
749 else if (pOutput
->pBuffers
[token_idx
].cbBuffer
< bin_len
)
751 TRACE("out buffer is NULL or has not enough space\n");
752 ret
= SEC_E_BUFFER_TOO_SMALL
;
753 if ((phContext
== NULL
) && (pInput
== NULL
))
755 cleanup_helper(helper
);
756 phNewContext
->dwUpper
= 0;
757 phNewContext
->dwLower
= 0;
762 if (!pOutput
->pBuffers
[token_idx
].pvBuffer
)
764 TRACE("out buffer is NULL\n");
765 ret
= SEC_E_INTERNAL_ERROR
;
766 if ((phContext
== NULL
) && (pInput
== NULL
))
768 cleanup_helper(helper
);
769 phNewContext
->dwUpper
= 0;
770 phNewContext
->dwLower
= 0;
775 pOutput
->pBuffers
[token_idx
].cbBuffer
= bin_len
;
776 memcpy(pOutput
->pBuffers
[token_idx
].pvBuffer
, bin
, bin_len
);
780 TRACE("Getting negotiated flags\n");
781 lstrcpynA(buffer
, "GF", max_len
- 1);
782 if((ret
= run_helper(helper
, buffer
, max_len
, &buffer_len
)) != SEC_E_OK
)
787 TRACE("No flags negotiated.\n");
788 helper
->neg_flags
= 0l;
792 TRACE("Negotiated %s\n", debugstr_a(buffer
));
793 sscanf(buffer
+ 3, "%lx", &(helper
->neg_flags
));
794 TRACE("Stored 0x%08lx as flags\n", helper
->neg_flags
);
797 TRACE("Getting session key\n");
798 lstrcpynA(buffer
, "GK", max_len
- 1);
799 if((ret
= run_helper(helper
, buffer
, max_len
, &buffer_len
)) != SEC_E_OK
)
802 if(strncmp(buffer
, "BH", 2) == 0)
803 TRACE("No key negotiated.\n");
804 else if(strncmp(buffer
, "GK ", 3) == 0)
806 if((ret
= decodeBase64(buffer
+3, buffer_len
-3, bin
, max_len
,
807 &bin_len
)) != SEC_E_OK
)
809 TRACE("Failed to decode session key\n");
811 TRACE("Session key is %s\n", debugstr_a(buffer
+3));
812 HeapFree(GetProcessHeap(), 0, helper
->session_key
);
813 helper
->session_key
= HeapAlloc(GetProcessHeap(), 0, bin_len
);
814 if(!helper
->session_key
)
816 TRACE("Failed to allocate memory for session key\n");
817 ret
= SEC_E_INTERNAL_ERROR
;
820 memcpy(helper
->session_key
, bin
, bin_len
);
823 helper
->crypt
.ntlm
.a4i
= SECUR32_arc4Alloc();
824 SECUR32_arc4Init(helper
->crypt
.ntlm
.a4i
, helper
->session_key
, 16);
825 helper
->crypt
.ntlm
.seq_num
= 0l;
826 SECUR32_CreateNTLMv2SubKeys(helper
);
827 helper
->crypt
.ntlm2
.send_a4i
= SECUR32_arc4Alloc();
828 helper
->crypt
.ntlm2
.recv_a4i
= SECUR32_arc4Alloc();
829 SECUR32_arc4Init(helper
->crypt
.ntlm2
.send_a4i
,
830 helper
->crypt
.ntlm2
.send_seal_key
, 16);
831 SECUR32_arc4Init(helper
->crypt
.ntlm2
.recv_a4i
,
832 helper
->crypt
.ntlm2
.recv_seal_key
, 16);
833 helper
->crypt
.ntlm2
.send_seq_no
= 0l;
834 helper
->crypt
.ntlm2
.recv_seq_no
= 0l;
838 HeapFree(GetProcessHeap(), 0, username
);
839 HeapFree(GetProcessHeap(), 0, want_flags
);
840 HeapFree(GetProcessHeap(), 0, buffer
);
841 HeapFree(GetProcessHeap(), 0, bin
);
845 /***********************************************************************
846 * InitializeSecurityContextA
848 static SECURITY_STATUS SEC_ENTRY
ntlm_InitializeSecurityContextA(
849 PCredHandle phCredential
, PCtxtHandle phContext
, SEC_CHAR
*pszTargetName
,
850 ULONG fContextReq
, ULONG Reserved1
, ULONG TargetDataRep
,
851 PSecBufferDesc pInput
,ULONG Reserved2
, PCtxtHandle phNewContext
,
852 PSecBufferDesc pOutput
, ULONG
*pfContextAttr
, PTimeStamp ptsExpiry
)
855 SEC_WCHAR
*target
= NULL
;
857 TRACE("%p %p %s %d %d %d %p %d %p %p %p %p\n", phCredential
, phContext
,
858 debugstr_a(pszTargetName
), fContextReq
, Reserved1
, TargetDataRep
, pInput
,
859 Reserved1
, phNewContext
, pOutput
, pfContextAttr
, ptsExpiry
);
861 if(pszTargetName
!= NULL
)
863 int target_size
= MultiByteToWideChar(CP_ACP
, 0, pszTargetName
,
864 strlen(pszTargetName
)+1, NULL
, 0);
865 target
= HeapAlloc(GetProcessHeap(), 0, target_size
*
867 MultiByteToWideChar(CP_ACP
, 0, pszTargetName
, strlen(pszTargetName
)+1,
868 target
, target_size
);
871 ret
= ntlm_InitializeSecurityContextW(phCredential
, phContext
, target
,
872 fContextReq
, Reserved1
, TargetDataRep
, pInput
, Reserved2
,
873 phNewContext
, pOutput
, pfContextAttr
, ptsExpiry
);
875 HeapFree(GetProcessHeap(), 0, target
);
879 /***********************************************************************
880 * AcceptSecurityContext
882 static SECURITY_STATUS SEC_ENTRY
ntlm_AcceptSecurityContext(
883 PCredHandle phCredential
, PCtxtHandle phContext
, PSecBufferDesc pInput
,
884 ULONG fContextReq
, ULONG TargetDataRep
, PCtxtHandle phNewContext
,
885 PSecBufferDesc pOutput
, ULONG
*pfContextAttr
, PTimeStamp ptsExpiry
)
888 char *buffer
, *want_flags
= NULL
;
890 int buffer_len
, bin_len
, max_len
= NTLM_MAX_BUF
;
893 PNtlmCredentials ntlm_cred
;
895 TRACE("%p %p %p %d %d %p %p %p %p\n", phCredential
, phContext
, pInput
,
896 fContextReq
, TargetDataRep
, phNewContext
, pOutput
, pfContextAttr
,
899 buffer
= HeapAlloc(GetProcessHeap(), 0, sizeof(char) * NTLM_MAX_BUF
);
900 bin
= HeapAlloc(GetProcessHeap(),0, sizeof(BYTE
) * NTLM_MAX_BUF
);
902 if(TargetDataRep
== SECURITY_NETWORK_DREP
){
903 TRACE("Using SECURITY_NETWORK_DREP\n");
906 if(phContext
== NULL
)
908 static CHAR server_helper_protocol
[] = "--helper-protocol=squid-2.5-ntlmssp";
909 SEC_CHAR
*server_argv
[] = { ntlm_auth
,
910 server_helper_protocol
,
915 ret
= SEC_E_INVALID_HANDLE
;
919 ntlm_cred
= (PNtlmCredentials
)phCredential
->dwLower
;
921 if(ntlm_cred
->mode
!= NTLM_SERVER
)
923 ret
= SEC_E_INVALID_HANDLE
;
927 /* This is the first call to AcceptSecurityHandle */
930 ret
= SEC_E_INCOMPLETE_MESSAGE
;
934 if(pInput
->cBuffers
< 1)
936 ret
= SEC_E_INCOMPLETE_MESSAGE
;
940 if(pInput
->pBuffers
[0].cbBuffer
> max_len
)
942 ret
= SEC_E_INVALID_TOKEN
;
946 bin_len
= pInput
->pBuffers
[0].cbBuffer
;
948 if( (ret
= fork_helper(&helper
, ntlm_auth
, server_argv
)) !=
951 ret
= SEC_E_INTERNAL_ERROR
;
954 helper
->mode
= NTLM_SERVER
;
956 /* Handle all the flags */
957 want_flags
= HeapAlloc(GetProcessHeap(), 0, 73);
958 if(want_flags
== NULL
)
960 TRACE("Failed to allocate memory for the want_flags!\n");
961 ret
= SEC_E_INSUFFICIENT_MEMORY
;
962 cleanup_helper(helper
);
965 lstrcpyA(want_flags
, "SF");
966 if(fContextReq
& ASC_REQ_ALLOCATE_MEMORY
)
968 FIXME("ASC_REQ_ALLOCATE_MEMORY stub\n");
970 if(fContextReq
& ASC_REQ_CONFIDENTIALITY
)
972 lstrcatA(want_flags
, " NTLMSSP_FEATURE_SEAL");
974 if(fContextReq
& ASC_REQ_CONNECTION
)
976 /* This is default, so we'll enable it */
977 lstrcatA(want_flags
, " NTLMSSP_FEATURE_SESSION_KEY");
978 ctxt_attr
|= ASC_RET_CONNECTION
;
980 if(fContextReq
& ASC_REQ_EXTENDED_ERROR
)
982 FIXME("ASC_REQ_EXTENDED_ERROR stub\n");
984 if(fContextReq
& ASC_REQ_INTEGRITY
)
986 lstrcatA(want_flags
, " NTLMSSP_FEATURE_SIGN");
988 if(fContextReq
& ASC_REQ_MUTUAL_AUTH
)
990 FIXME("ASC_REQ_MUTUAL_AUTH stub\n");
992 if(fContextReq
& ASC_REQ_REPLAY_DETECT
)
994 FIXME("ASC_REQ_REPLAY_DETECT stub\n");
996 if(fContextReq
& ISC_REQ_SEQUENCE_DETECT
)
998 FIXME("ASC_REQ_SEQUENCE_DETECT stub\n");
1000 if(fContextReq
& ISC_REQ_STREAM
)
1002 FIXME("ASC_REQ_STREAM stub\n");
1004 /* Done with the flags */
1006 if(lstrlenA(want_flags
) > 3)
1008 TRACE("Server set want_flags: %s\n", debugstr_a(want_flags
));
1009 lstrcpynA(buffer
, want_flags
, max_len
- 1);
1010 if((ret
= run_helper(helper
, buffer
, max_len
, &buffer_len
)) !=
1013 cleanup_helper(helper
);
1016 if(!strncmp(buffer
, "BH", 2))
1017 TRACE("Helper doesn't understand new command set\n");
1020 /* This is the YR request from the client, encode to base64 */
1022 memcpy(bin
, pInput
->pBuffers
[0].pvBuffer
, bin_len
);
1024 lstrcpynA(buffer
, "YR ", max_len
-1);
1026 if((ret
= encodeBase64(bin
, bin_len
, buffer
+3, max_len
-3,
1027 &buffer_len
)) != SEC_E_OK
)
1029 cleanup_helper(helper
);
1033 TRACE("Client sent: %s\n", debugstr_a(buffer
));
1035 if((ret
= run_helper(helper
, buffer
, max_len
, &buffer_len
)) !=
1038 cleanup_helper(helper
);
1042 TRACE("Reply from ntlm_auth: %s\n", debugstr_a(buffer
));
1043 /* The expected answer is TT <base64 blob> */
1045 if(strncmp(buffer
, "TT ", 3) != 0)
1047 ret
= SEC_E_INTERNAL_ERROR
;
1048 cleanup_helper(helper
);
1052 if((ret
= decodeBase64(buffer
+3, buffer_len
-3, bin
, max_len
,
1053 &bin_len
)) != SEC_E_OK
)
1055 cleanup_helper(helper
);
1059 /* send this to the client */
1062 ret
= SEC_E_INSUFFICIENT_MEMORY
;
1063 cleanup_helper(helper
);
1067 if(pOutput
->cBuffers
< 1)
1069 ret
= SEC_E_INSUFFICIENT_MEMORY
;
1070 cleanup_helper(helper
);
1074 pOutput
->pBuffers
[0].cbBuffer
= bin_len
;
1075 pOutput
->pBuffers
[0].BufferType
= SECBUFFER_DATA
;
1076 memcpy(pOutput
->pBuffers
[0].pvBuffer
, bin
, bin_len
);
1077 ret
= SEC_I_CONTINUE_NEEDED
;
1082 /* we expect a KK request from client */
1085 ret
= SEC_E_INCOMPLETE_MESSAGE
;
1089 if(pInput
->cBuffers
< 1)
1091 ret
= SEC_E_INCOMPLETE_MESSAGE
;
1097 ret
= SEC_E_INVALID_HANDLE
;
1101 helper
= (PNegoHelper
)phContext
->dwLower
;
1103 if(helper
->mode
!= NTLM_SERVER
)
1105 ret
= SEC_E_INVALID_HANDLE
;
1109 if(pInput
->pBuffers
[0].cbBuffer
> max_len
)
1111 ret
= SEC_E_INVALID_TOKEN
;
1115 bin_len
= pInput
->pBuffers
[0].cbBuffer
;
1117 memcpy(bin
, pInput
->pBuffers
[0].pvBuffer
, bin_len
);
1119 lstrcpynA(buffer
, "KK ", max_len
-1);
1121 if((ret
= encodeBase64(bin
, bin_len
, buffer
+3, max_len
-3,
1122 &buffer_len
)) != SEC_E_OK
)
1127 TRACE("Client sent: %s\n", debugstr_a(buffer
));
1129 if((ret
= run_helper(helper
, buffer
, max_len
, &buffer_len
)) !=
1135 TRACE("Reply from ntlm_auth: %s\n", debugstr_a(buffer
));
1137 /* At this point, we get a NA if the user didn't authenticate, but a BH
1138 * if ntlm_auth could not connect to winbindd. Apart from running Wine
1139 * as root, there is no way to fix this for now, so just handle this as
1140 * a failed login. */
1141 if(strncmp(buffer
, "AF ", 3) != 0)
1143 if(strncmp(buffer
, "NA ", 3) == 0)
1145 ret
= SEC_E_LOGON_DENIED
;
1150 size_t ntlm_pipe_err_len
= strlen("BH NT_STATUS_ACCESS_DENIED");
1152 if( (buffer_len
>= ntlm_pipe_err_len
) &&
1153 (strncmp(buffer
, "BH NT_STATUS_ACCESS_DENIED",
1154 ntlm_pipe_err_len
) == 0))
1156 TRACE("Connection to winbindd failed\n");
1157 ret
= SEC_E_LOGON_DENIED
;
1160 ret
= SEC_E_INTERNAL_ERROR
;
1165 pOutput
->pBuffers
[0].cbBuffer
= 0;
1168 TRACE("Getting negotiated flags\n");
1169 lstrcpynA(buffer
, "GF", max_len
- 1);
1170 if((ret
= run_helper(helper
, buffer
, max_len
, &buffer_len
)) != SEC_E_OK
)
1175 TRACE("No flags negotiated, or helper does not support GF command\n");
1179 TRACE("Negotiated %s\n", debugstr_a(buffer
));
1180 sscanf(buffer
+ 3, "%lx", &(helper
->neg_flags
));
1181 TRACE("Stored 0x%08lx as flags\n", helper
->neg_flags
);
1184 TRACE("Getting session key\n");
1185 lstrcpynA(buffer
, "GK", max_len
- 1);
1186 if((ret
= run_helper(helper
, buffer
, max_len
, &buffer_len
)) != SEC_E_OK
)
1190 TRACE("Helper does not support GK command\n");
1193 if(strncmp(buffer
, "BH ", 3) == 0)
1195 TRACE("Helper sent %s\n", debugstr_a(buffer
+3));
1196 helper
->session_key
= HeapAlloc(GetProcessHeap(), 0, 16);
1197 /*FIXME: Generate the dummy session key = MD4(MD4(password))*/
1198 memset(helper
->session_key
, 0 , 16);
1200 else if(strncmp(buffer
, "GK ", 3) == 0)
1202 if((ret
= decodeBase64(buffer
+3, buffer_len
-3, bin
, max_len
,
1203 &bin_len
)) != SEC_E_OK
)
1205 TRACE("Failed to decode session key\n");
1207 TRACE("Session key is %s\n", debugstr_a(buffer
+3));
1208 helper
->session_key
= HeapAlloc(GetProcessHeap(), 0, 16);
1209 if(!helper
->session_key
)
1211 TRACE("Failed to allocate memory for session key\n");
1212 ret
= SEC_E_INTERNAL_ERROR
;
1215 memcpy(helper
->session_key
, bin
, 16);
1218 helper
->crypt
.ntlm
.a4i
= SECUR32_arc4Alloc();
1219 SECUR32_arc4Init(helper
->crypt
.ntlm
.a4i
, helper
->session_key
, 16);
1220 helper
->crypt
.ntlm
.seq_num
= 0l;
1223 phNewContext
->dwUpper
= ctxt_attr
;
1224 phNewContext
->dwLower
= (ULONG_PTR
)helper
;
1227 HeapFree(GetProcessHeap(), 0, want_flags
);
1228 HeapFree(GetProcessHeap(), 0, buffer
);
1229 HeapFree(GetProcessHeap(), 0, bin
);
1233 /***********************************************************************
1236 static SECURITY_STATUS SEC_ENTRY
ntlm_CompleteAuthToken(PCtxtHandle phContext
,
1237 PSecBufferDesc pToken
)
1239 /* We never need to call CompleteAuthToken anyway */
1240 TRACE("%p %p\n", phContext
, pToken
);
1242 return SEC_E_INVALID_HANDLE
;
1247 /***********************************************************************
1248 * DeleteSecurityContext
1250 static SECURITY_STATUS SEC_ENTRY
ntlm_DeleteSecurityContext(PCtxtHandle phContext
)
1254 TRACE("%p\n", phContext
);
1256 return SEC_E_INVALID_HANDLE
;
1258 helper
= (PNegoHelper
)phContext
->dwLower
;
1260 phContext
->dwUpper
= 0;
1261 phContext
->dwLower
= 0;
1263 SECUR32_arc4Cleanup(helper
->crypt
.ntlm
.a4i
);
1264 HeapFree(GetProcessHeap(), 0, helper
->session_key
);
1265 SECUR32_arc4Cleanup(helper
->crypt
.ntlm2
.send_a4i
);
1266 SECUR32_arc4Cleanup(helper
->crypt
.ntlm2
.recv_a4i
);
1267 HeapFree(GetProcessHeap(), 0, helper
->crypt
.ntlm2
.send_sign_key
);
1268 HeapFree(GetProcessHeap(), 0, helper
->crypt
.ntlm2
.send_seal_key
);
1269 HeapFree(GetProcessHeap(), 0, helper
->crypt
.ntlm2
.recv_sign_key
);
1270 HeapFree(GetProcessHeap(), 0, helper
->crypt
.ntlm2
.recv_seal_key
);
1272 cleanup_helper(helper
);
1277 /***********************************************************************
1278 * QueryContextAttributesW
1280 static SECURITY_STATUS SEC_ENTRY
ntlm_QueryContextAttributesW(PCtxtHandle phContext
,
1281 ULONG ulAttribute
, void *pBuffer
)
1283 TRACE("%p %d %p\n", phContext
, ulAttribute
, pBuffer
);
1285 return SEC_E_INVALID_HANDLE
;
1289 #define _x(x) case (x) : FIXME(#x" stub\n"); break
1290 _x(SECPKG_ATTR_ACCESS_TOKEN
);
1291 _x(SECPKG_ATTR_AUTHORITY
);
1292 _x(SECPKG_ATTR_DCE_INFO
);
1293 case SECPKG_ATTR_FLAGS
:
1295 PSecPkgContext_Flags spcf
= (PSecPkgContext_Flags
)pBuffer
;
1296 PNegoHelper helper
= (PNegoHelper
)phContext
->dwLower
;
1299 if(helper
->neg_flags
& NTLMSSP_NEGOTIATE_SIGN
)
1300 spcf
->Flags
|= ISC_RET_INTEGRITY
;
1301 if(helper
->neg_flags
& NTLMSSP_NEGOTIATE_SEAL
)
1302 spcf
->Flags
|= ISC_RET_CONFIDENTIALITY
;
1305 _x(SECPKG_ATTR_KEY_INFO
);
1306 _x(SECPKG_ATTR_LIFESPAN
);
1307 _x(SECPKG_ATTR_NAMES
);
1308 _x(SECPKG_ATTR_NATIVE_NAMES
);
1309 _x(SECPKG_ATTR_NEGOTIATION_INFO
);
1310 _x(SECPKG_ATTR_PACKAGE_INFO
);
1311 _x(SECPKG_ATTR_PASSWORD_EXPIRY
);
1312 _x(SECPKG_ATTR_SESSION_KEY
);
1313 case SECPKG_ATTR_SIZES
:
1315 PSecPkgContext_Sizes spcs
= (PSecPkgContext_Sizes
)pBuffer
;
1316 spcs
->cbMaxToken
= NTLM_MAX_BUF
;
1317 spcs
->cbMaxSignature
= 16;
1318 spcs
->cbBlockSize
= 0;
1319 spcs
->cbSecurityTrailer
= 16;
1322 _x(SECPKG_ATTR_STREAM_SIZES
);
1323 _x(SECPKG_ATTR_TARGET_INFORMATION
);
1326 TRACE("Unknown value %d passed for ulAttribute\n", ulAttribute
);
1329 return SEC_E_UNSUPPORTED_FUNCTION
;
1332 /***********************************************************************
1333 * QueryContextAttributesA
1335 static SECURITY_STATUS SEC_ENTRY
ntlm_QueryContextAttributesA(PCtxtHandle phContext
,
1336 ULONG ulAttribute
, void *pBuffer
)
1338 return ntlm_QueryContextAttributesW(phContext
, ulAttribute
, pBuffer
);
1341 /***********************************************************************
1342 * ImpersonateSecurityContext
1344 static SECURITY_STATUS SEC_ENTRY
ntlm_ImpersonateSecurityContext(PCtxtHandle phContext
)
1346 SECURITY_STATUS ret
;
1348 TRACE("%p\n", phContext
);
1351 ret
= SEC_E_UNSUPPORTED_FUNCTION
;
1355 ret
= SEC_E_INVALID_HANDLE
;
1360 /***********************************************************************
1361 * RevertSecurityContext
1363 static SECURITY_STATUS SEC_ENTRY
ntlm_RevertSecurityContext(PCtxtHandle phContext
)
1365 SECURITY_STATUS ret
;
1367 TRACE("%p\n", phContext
);
1370 ret
= SEC_E_UNSUPPORTED_FUNCTION
;
1374 ret
= SEC_E_INVALID_HANDLE
;
1379 /***********************************************************************
1380 * ntlm_CreateSignature
1381 * As both MakeSignature and VerifySignature need this, but different keys
1382 * are needed for NTLMv2, the logic goes into a helper function.
1383 * To ensure maximal reusability, we can specify the direction as NTLM_SEND for
1384 * signing/encrypting and NTLM_RECV for verfying/decrypting. When encrypting,
1385 * the signature is encrypted after the message was encrypted, so
1386 * CreateSignature shouldn't do it. In this case, encrypt_sig can be set to
1389 static SECURITY_STATUS
ntlm_CreateSignature(PNegoHelper helper
, PSecBufferDesc pMessage
,
1390 int token_idx
, SignDirection direction
, BOOL encrypt_sig
)
1392 ULONG sign_version
= 1;
1395 TRACE("%p, %p, %d, %d, %d\n", helper
, pMessage
, token_idx
, direction
,
1398 sig
= pMessage
->pBuffers
[token_idx
].pvBuffer
;
1400 if(helper
->neg_flags
& NTLMSSP_NEGOTIATE_NTLM2
&&
1401 helper
->neg_flags
& NTLMSSP_NEGOTIATE_SIGN
)
1405 HMAC_MD5_CTX hmac_md5_ctx
;
1407 TRACE("Signing NTLM2 style\n");
1409 if(direction
== NTLM_SEND
)
1411 seq_no
[0] = (helper
->crypt
.ntlm2
.send_seq_no
>> 0) & 0xff;
1412 seq_no
[1] = (helper
->crypt
.ntlm2
.send_seq_no
>> 8) & 0xff;
1413 seq_no
[2] = (helper
->crypt
.ntlm2
.send_seq_no
>> 16) & 0xff;
1414 seq_no
[3] = (helper
->crypt
.ntlm2
.send_seq_no
>> 24) & 0xff;
1416 ++(helper
->crypt
.ntlm2
.send_seq_no
);
1418 HMACMD5Init(&hmac_md5_ctx
, helper
->crypt
.ntlm2
.send_sign_key
, 16);
1422 seq_no
[0] = (helper
->crypt
.ntlm2
.recv_seq_no
>> 0) & 0xff;
1423 seq_no
[1] = (helper
->crypt
.ntlm2
.recv_seq_no
>> 8) & 0xff;
1424 seq_no
[2] = (helper
->crypt
.ntlm2
.recv_seq_no
>> 16) & 0xff;
1425 seq_no
[3] = (helper
->crypt
.ntlm2
.recv_seq_no
>> 24) & 0xff;
1427 ++(helper
->crypt
.ntlm2
.recv_seq_no
);
1429 HMACMD5Init(&hmac_md5_ctx
, helper
->crypt
.ntlm2
.recv_sign_key
, 16);
1432 HMACMD5Update(&hmac_md5_ctx
, seq_no
, 4);
1433 for( i
= 0; i
< pMessage
->cBuffers
; ++i
)
1435 if(pMessage
->pBuffers
[i
].BufferType
& SECBUFFER_DATA
)
1436 HMACMD5Update(&hmac_md5_ctx
, (BYTE
*)pMessage
->pBuffers
[i
].pvBuffer
,
1437 pMessage
->pBuffers
[i
].cbBuffer
);
1440 HMACMD5Final(&hmac_md5_ctx
, digest
);
1442 if(encrypt_sig
&& helper
->neg_flags
& NTLMSSP_NEGOTIATE_KEY_EXCHANGE
)
1444 if(direction
== NTLM_SEND
)
1445 SECUR32_arc4Process(helper
->crypt
.ntlm2
.send_a4i
, digest
, 8);
1447 SECUR32_arc4Process(helper
->crypt
.ntlm2
.recv_a4i
, digest
, 8);
1450 /* The NTLM2 signature is the sign version */
1451 sig
[ 0] = (sign_version
>> 0) & 0xff;
1452 sig
[ 1] = (sign_version
>> 8) & 0xff;
1453 sig
[ 2] = (sign_version
>> 16) & 0xff;
1454 sig
[ 3] = (sign_version
>> 24) & 0xff;
1455 /* The first 8 bytes of the digest */
1456 memcpy(sig
+4, digest
, 8);
1457 /* And the sequence number */
1458 memcpy(sig
+12, seq_no
, 4);
1460 pMessage
->pBuffers
[token_idx
].cbBuffer
= 16;
1464 if(helper
->neg_flags
& NTLMSSP_NEGOTIATE_SIGN
)
1467 TRACE("Signing NTLM1 style\n");
1469 for(i
=0; i
< pMessage
->cBuffers
; ++i
)
1471 if(pMessage
->pBuffers
[i
].BufferType
& SECBUFFER_DATA
)
1473 crc
= ComputeCrc32(pMessage
->pBuffers
[i
].pvBuffer
,
1474 pMessage
->pBuffers
[i
].cbBuffer
, crc
);
1478 sig
[ 0] = (sign_version
>> 0) & 0xff;
1479 sig
[ 1] = (sign_version
>> 8) & 0xff;
1480 sig
[ 2] = (sign_version
>> 16) & 0xff;
1481 sig
[ 3] = (sign_version
>> 24) & 0xff;
1482 memset(sig
+4, 0, 4);
1483 sig
[ 8] = (crc
>> 0) & 0xff;
1484 sig
[ 9] = (crc
>> 8) & 0xff;
1485 sig
[10] = (crc
>> 16) & 0xff;
1486 sig
[11] = (crc
>> 24) & 0xff;
1487 sig
[12] = (helper
->crypt
.ntlm
.seq_num
>> 0) & 0xff;
1488 sig
[13] = (helper
->crypt
.ntlm
.seq_num
>> 8) & 0xff;
1489 sig
[14] = (helper
->crypt
.ntlm
.seq_num
>> 16) & 0xff;
1490 sig
[15] = (helper
->crypt
.ntlm
.seq_num
>> 24) & 0xff;
1492 ++(helper
->crypt
.ntlm
.seq_num
);
1495 SECUR32_arc4Process(helper
->crypt
.ntlm
.a4i
, sig
+4, 12);
1499 if(helper
->neg_flags
& NTLMSSP_NEGOTIATE_ALWAYS_SIGN
|| helper
->neg_flags
== 0)
1501 TRACE("Creating a dummy signature.\n");
1502 /* A dummy signature is 0x01 followed by 15 bytes of 0x00 */
1503 memset(pMessage
->pBuffers
[token_idx
].pvBuffer
, 0, 16);
1504 memset(pMessage
->pBuffers
[token_idx
].pvBuffer
, 0x01, 1);
1505 pMessage
->pBuffers
[token_idx
].cbBuffer
= 16;
1509 return SEC_E_UNSUPPORTED_FUNCTION
;
1512 /***********************************************************************
1515 static SECURITY_STATUS SEC_ENTRY
ntlm_MakeSignature(PCtxtHandle phContext
, ULONG fQOP
,
1516 PSecBufferDesc pMessage
, ULONG MessageSeqNo
)
1521 TRACE("%p %d %p %d\n", phContext
, fQOP
, pMessage
, MessageSeqNo
);
1523 return SEC_E_INVALID_HANDLE
;
1526 FIXME("Ignoring fQOP 0x%08x\n", fQOP
);
1529 FIXME("Ignoring MessageSeqNo\n");
1531 if(!pMessage
|| !pMessage
->pBuffers
|| pMessage
->cBuffers
< 2)
1532 return SEC_E_INVALID_TOKEN
;
1534 /* If we didn't find a SECBUFFER_TOKEN type buffer */
1535 if((token_idx
= ntlm_GetTokenBufferIndex(pMessage
)) == -1)
1536 return SEC_E_INVALID_TOKEN
;
1538 if(pMessage
->pBuffers
[token_idx
].cbBuffer
< 16)
1539 return SEC_E_BUFFER_TOO_SMALL
;
1541 helper
= (PNegoHelper
)phContext
->dwLower
;
1542 TRACE("Negotiated flags are: 0x%08lx\n", helper
->neg_flags
);
1544 return ntlm_CreateSignature(helper
, pMessage
, token_idx
, NTLM_SEND
, TRUE
);
1547 /***********************************************************************
1550 static SECURITY_STATUS SEC_ENTRY
ntlm_VerifySignature(PCtxtHandle phContext
,
1551 PSecBufferDesc pMessage
, ULONG MessageSeqNo
, PULONG pfQOP
)
1557 SECURITY_STATUS ret
;
1558 SecBufferDesc local_desc
;
1559 PSecBuffer local_buff
;
1562 TRACE("%p %p %d %p\n", phContext
, pMessage
, MessageSeqNo
, pfQOP
);
1564 return SEC_E_INVALID_HANDLE
;
1566 if(!pMessage
|| !pMessage
->pBuffers
|| pMessage
->cBuffers
< 2)
1567 return SEC_E_INVALID_TOKEN
;
1569 if((token_idx
= ntlm_GetTokenBufferIndex(pMessage
)) == -1)
1570 return SEC_E_INVALID_TOKEN
;
1572 if(pMessage
->pBuffers
[token_idx
].cbBuffer
< 16)
1573 return SEC_E_BUFFER_TOO_SMALL
;
1576 FIXME("Ignoring MessageSeqNo\n");
1578 helper
= (PNegoHelper
)phContext
->dwLower
;
1579 TRACE("Negotiated flags: 0x%08lx\n", helper
->neg_flags
);
1581 local_buff
= HeapAlloc(GetProcessHeap(), 0, pMessage
->cBuffers
* sizeof(SecBuffer
));
1583 local_desc
.ulVersion
= SECBUFFER_VERSION
;
1584 local_desc
.cBuffers
= pMessage
->cBuffers
;
1585 local_desc
.pBuffers
= local_buff
;
1587 for(i
=0; i
< pMessage
->cBuffers
; ++i
)
1589 if(pMessage
->pBuffers
[i
].BufferType
== SECBUFFER_TOKEN
)
1591 local_buff
[i
].BufferType
= SECBUFFER_TOKEN
;
1592 local_buff
[i
].cbBuffer
= 16;
1593 local_buff
[i
].pvBuffer
= local_sig
;
1597 local_buff
[i
].BufferType
= pMessage
->pBuffers
[i
].BufferType
;
1598 local_buff
[i
].cbBuffer
= pMessage
->pBuffers
[i
].cbBuffer
;
1599 local_buff
[i
].pvBuffer
= pMessage
->pBuffers
[i
].pvBuffer
;
1603 if((ret
= ntlm_CreateSignature(helper
, &local_desc
, token_idx
, NTLM_RECV
, TRUE
)) != SEC_E_OK
)
1606 if(memcmp(((PBYTE
)local_buff
[token_idx
].pvBuffer
) + 8,
1607 ((PBYTE
)pMessage
->pBuffers
[token_idx
].pvBuffer
) + 8, 8))
1608 ret
= SEC_E_MESSAGE_ALTERED
;
1612 HeapFree(GetProcessHeap(), 0, local_buff
);
1619 /***********************************************************************
1620 * FreeCredentialsHandle
1622 static SECURITY_STATUS SEC_ENTRY
ntlm_FreeCredentialsHandle(
1623 PCredHandle phCredential
)
1625 SECURITY_STATUS ret
;
1628 PNtlmCredentials ntlm_cred
= (PNtlmCredentials
) phCredential
->dwLower
;
1629 phCredential
->dwUpper
= 0;
1630 phCredential
->dwLower
= 0;
1631 if (ntlm_cred
->password
)
1632 memset(ntlm_cred
->password
, 0, ntlm_cred
->pwlen
);
1633 HeapFree(GetProcessHeap(), 0, ntlm_cred
->password
);
1634 HeapFree(GetProcessHeap(), 0, ntlm_cred
->username_arg
);
1635 HeapFree(GetProcessHeap(), 0, ntlm_cred
->domain_arg
);
1644 /***********************************************************************
1647 static SECURITY_STATUS SEC_ENTRY
ntlm_EncryptMessage(PCtxtHandle phContext
,
1648 ULONG fQOP
, PSecBufferDesc pMessage
, ULONG MessageSeqNo
)
1651 int token_idx
, data_idx
;
1653 TRACE("(%p %d %p %d)\n", phContext
, fQOP
, pMessage
, MessageSeqNo
);
1656 return SEC_E_INVALID_HANDLE
;
1659 FIXME("Ignoring fQOP\n");
1662 FIXME("Ignoring MessageSeqNo\n");
1664 if(!pMessage
|| !pMessage
->pBuffers
|| pMessage
->cBuffers
< 2)
1665 return SEC_E_INVALID_TOKEN
;
1667 if((token_idx
= ntlm_GetTokenBufferIndex(pMessage
)) == -1)
1668 return SEC_E_INVALID_TOKEN
;
1670 if((data_idx
= ntlm_GetDataBufferIndex(pMessage
)) ==-1 )
1671 return SEC_E_INVALID_TOKEN
;
1673 if(pMessage
->pBuffers
[token_idx
].cbBuffer
< 16)
1674 return SEC_E_BUFFER_TOO_SMALL
;
1676 helper
= (PNegoHelper
) phContext
->dwLower
;
1678 if(helper
->neg_flags
& NTLMSSP_NEGOTIATE_NTLM2
&&
1679 helper
->neg_flags
& NTLMSSP_NEGOTIATE_SEAL
)
1681 ntlm_CreateSignature(helper
, pMessage
, token_idx
, NTLM_SEND
, FALSE
);
1682 SECUR32_arc4Process(helper
->crypt
.ntlm2
.send_a4i
,
1683 (BYTE
*)pMessage
->pBuffers
[data_idx
].pvBuffer
,
1684 pMessage
->pBuffers
[data_idx
].cbBuffer
);
1686 if(helper
->neg_flags
& NTLMSSP_NEGOTIATE_KEY_EXCHANGE
)
1687 SECUR32_arc4Process(helper
->crypt
.ntlm2
.send_a4i
,
1688 ((BYTE
*)pMessage
->pBuffers
[token_idx
].pvBuffer
)+4, 8);
1698 /* EncryptMessage always produces real signatures, so make sure
1699 * NTLMSSP_NEGOTIATE_SIGN is set*/
1700 save_flags
= helper
->neg_flags
;
1701 helper
->neg_flags
|= NTLMSSP_NEGOTIATE_SIGN
;
1702 ntlm_CreateSignature(helper
, pMessage
, token_idx
, NTLM_SEND
, FALSE
);
1703 helper
->neg_flags
= save_flags
;
1705 sig
= pMessage
->pBuffers
[token_idx
].pvBuffer
;
1707 SECUR32_arc4Process(helper
->crypt
.ntlm
.a4i
,
1708 pMessage
->pBuffers
[data_idx
].pvBuffer
,
1709 pMessage
->pBuffers
[data_idx
].cbBuffer
);
1710 SECUR32_arc4Process(helper
->crypt
.ntlm
.a4i
, sig
+4, 12);
1712 if(helper
->neg_flags
& NTLMSSP_NEGOTIATE_ALWAYS_SIGN
|| helper
->neg_flags
== 0)
1713 memset(sig
+4, 0, 4);
1720 /***********************************************************************
1723 static SECURITY_STATUS SEC_ENTRY
ntlm_DecryptMessage(PCtxtHandle phContext
,
1724 PSecBufferDesc pMessage
, ULONG MessageSeqNo
, PULONG pfQOP
)
1726 SECURITY_STATUS ret
;
1727 ULONG ntlmssp_flags_save
;
1729 int token_idx
, data_idx
;
1730 TRACE("(%p %p %d %p)\n", phContext
, pMessage
, MessageSeqNo
, pfQOP
);
1733 return SEC_E_INVALID_HANDLE
;
1736 FIXME("Ignoring MessageSeqNo\n");
1738 if(!pMessage
|| !pMessage
->pBuffers
|| pMessage
->cBuffers
< 2)
1739 return SEC_E_INVALID_TOKEN
;
1741 if((token_idx
= ntlm_GetTokenBufferIndex(pMessage
)) == -1)
1742 return SEC_E_INVALID_TOKEN
;
1744 if((data_idx
= ntlm_GetDataBufferIndex(pMessage
)) ==-1)
1745 return SEC_E_INVALID_TOKEN
;
1747 if(pMessage
->pBuffers
[token_idx
].cbBuffer
< 16)
1748 return SEC_E_BUFFER_TOO_SMALL
;
1750 helper
= (PNegoHelper
) phContext
->dwLower
;
1752 if(helper
->neg_flags
& NTLMSSP_NEGOTIATE_NTLM2
&& helper
->neg_flags
& NTLMSSP_NEGOTIATE_SEAL
)
1754 SECUR32_arc4Process(helper
->crypt
.ntlm2
.recv_a4i
,
1755 pMessage
->pBuffers
[data_idx
].pvBuffer
,
1756 pMessage
->pBuffers
[data_idx
].cbBuffer
);
1760 SECUR32_arc4Process(helper
->crypt
.ntlm
.a4i
,
1761 pMessage
->pBuffers
[data_idx
].pvBuffer
,
1762 pMessage
->pBuffers
[data_idx
].cbBuffer
);
1765 /* Make sure we use a session key for the signature check, EncryptMessage
1766 * always does that, even in the dummy case */
1767 ntlmssp_flags_save
= helper
->neg_flags
;
1769 helper
->neg_flags
|= NTLMSSP_NEGOTIATE_SIGN
;
1770 ret
= ntlm_VerifySignature(phContext
, pMessage
, MessageSeqNo
, pfQOP
);
1772 helper
->neg_flags
= ntlmssp_flags_save
;
1777 static const SecurityFunctionTableA ntlmTableA
= {
1779 NULL
, /* EnumerateSecurityPackagesA */
1780 ntlm_QueryCredentialsAttributesA
, /* QueryCredentialsAttributesA */
1781 ntlm_AcquireCredentialsHandleA
, /* AcquireCredentialsHandleA */
1782 ntlm_FreeCredentialsHandle
, /* FreeCredentialsHandle */
1783 NULL
, /* Reserved2 */
1784 ntlm_InitializeSecurityContextA
, /* InitializeSecurityContextA */
1785 ntlm_AcceptSecurityContext
, /* AcceptSecurityContext */
1786 ntlm_CompleteAuthToken
, /* CompleteAuthToken */
1787 ntlm_DeleteSecurityContext
, /* DeleteSecurityContext */
1788 NULL
, /* ApplyControlToken */
1789 ntlm_QueryContextAttributesA
, /* QueryContextAttributesA */
1790 ntlm_ImpersonateSecurityContext
, /* ImpersonateSecurityContext */
1791 ntlm_RevertSecurityContext
, /* RevertSecurityContext */
1792 ntlm_MakeSignature
, /* MakeSignature */
1793 ntlm_VerifySignature
, /* VerifySignature */
1794 FreeContextBuffer
, /* FreeContextBuffer */
1795 NULL
, /* QuerySecurityPackageInfoA */
1796 NULL
, /* Reserved3 */
1797 NULL
, /* Reserved4 */
1798 NULL
, /* ExportSecurityContext */
1799 NULL
, /* ImportSecurityContextA */
1800 NULL
, /* AddCredentialsA */
1801 NULL
, /* Reserved8 */
1802 NULL
, /* QuerySecurityContextToken */
1803 ntlm_EncryptMessage
, /* EncryptMessage */
1804 ntlm_DecryptMessage
, /* DecryptMessage */
1805 NULL
, /* SetContextAttributesA */
1808 static const SecurityFunctionTableW ntlmTableW
= {
1810 NULL
, /* EnumerateSecurityPackagesW */
1811 ntlm_QueryCredentialsAttributesW
, /* QueryCredentialsAttributesW */
1812 ntlm_AcquireCredentialsHandleW
, /* AcquireCredentialsHandleW */
1813 ntlm_FreeCredentialsHandle
, /* FreeCredentialsHandle */
1814 NULL
, /* Reserved2 */
1815 ntlm_InitializeSecurityContextW
, /* InitializeSecurityContextW */
1816 ntlm_AcceptSecurityContext
, /* AcceptSecurityContext */
1817 ntlm_CompleteAuthToken
, /* CompleteAuthToken */
1818 ntlm_DeleteSecurityContext
, /* DeleteSecurityContext */
1819 NULL
, /* ApplyControlToken */
1820 ntlm_QueryContextAttributesW
, /* QueryContextAttributesW */
1821 ntlm_ImpersonateSecurityContext
, /* ImpersonateSecurityContext */
1822 ntlm_RevertSecurityContext
, /* RevertSecurityContext */
1823 ntlm_MakeSignature
, /* MakeSignature */
1824 ntlm_VerifySignature
, /* VerifySignature */
1825 FreeContextBuffer
, /* FreeContextBuffer */
1826 NULL
, /* QuerySecurityPackageInfoW */
1827 NULL
, /* Reserved3 */
1828 NULL
, /* Reserved4 */
1829 NULL
, /* ExportSecurityContext */
1830 NULL
, /* ImportSecurityContextW */
1831 NULL
, /* AddCredentialsW */
1832 NULL
, /* Reserved8 */
1833 NULL
, /* QuerySecurityContextToken */
1834 ntlm_EncryptMessage
, /* EncryptMessage */
1835 ntlm_DecryptMessage
, /* DecryptMessage */
1836 NULL
, /* SetContextAttributesW */
1839 #define NTLM_COMMENT \
1840 { 'N', 'T', 'L', 'M', ' ', \
1841 'S', 'e', 'c', 'u', 'r', 'i', 't', 'y', ' ', \
1842 'P', 'a', 'c', 'k', 'a', 'g', 'e', 0}
1844 static CHAR ntlm_comment_A
[] = NTLM_COMMENT
;
1845 static WCHAR ntlm_comment_W
[] = NTLM_COMMENT
;
1847 #define NTLM_NAME {'N', 'T', 'L', 'M', 0}
1849 static char ntlm_name_A
[] = NTLM_NAME
;
1850 static WCHAR ntlm_name_W
[] = NTLM_NAME
;
1852 /* According to Windows, NTLM has the following capabilities. */
1854 SECPKG_FLAG_INTEGRITY | \
1855 SECPKG_FLAG_PRIVACY | \
1856 SECPKG_FLAG_TOKEN_ONLY | \
1857 SECPKG_FLAG_CONNECTION | \
1858 SECPKG_FLAG_MULTI_REQUIRED | \
1859 SECPKG_FLAG_IMPERSONATION | \
1860 SECPKG_FLAG_ACCEPT_WIN32_NAME | \
1861 SECPKG_FLAG_READONLY_WITH_CHECKSUM)
1863 static const SecPkgInfoW infoW
= {
1872 static const SecPkgInfoA infoA
= {
1881 void SECUR32_initNTLMSP(void)
1883 SECURITY_STATUS ret
;
1885 static CHAR version
[] = "--version";
1887 SEC_CHAR
*args
[] = {
1892 if((ret
= fork_helper(&helper
, ntlm_auth
, args
)) != SEC_E_OK
)
1894 /* Cheat and allocate a helper anyway, so cleanup later will work. */
1895 helper
= HeapAlloc(GetProcessHeap(),0, sizeof(PNegoHelper
));
1896 helper
->major
= helper
->minor
= helper
->micro
= -1;
1899 check_version(helper
);
1901 if( (helper
->major
> MIN_NTLM_AUTH_MAJOR_VERSION
) ||
1902 (helper
->major
== MIN_NTLM_AUTH_MAJOR_VERSION
&&
1903 helper
->minor
> MIN_NTLM_AUTH_MINOR_VERSION
) ||
1904 (helper
->major
== MIN_NTLM_AUTH_MAJOR_VERSION
&&
1905 helper
->minor
== MIN_NTLM_AUTH_MINOR_VERSION
&&
1906 helper
->micro
>= MIN_NTLM_AUTH_MICRO_VERSION
) )
1908 SecureProvider
*provider
= SECUR32_addProvider(&ntlmTableA
, &ntlmTableW
, NULL
);
1909 SECUR32_addPackages(provider
, 1L, &infoA
, &infoW
);
1913 ERR("%s was not found or is outdated. "
1914 "Make sure that ntlm_auth >= %d.%d.%d is in your path.\n",
1916 MIN_NTLM_AUTH_MAJOR_VERSION
,
1917 MIN_NTLM_AUTH_MINOR_VERSION
,
1918 MIN_NTLM_AUTH_MICRO_VERSION
);
1919 ERR("Usually, you can find it in the winbind package of your "
1923 cleanup_helper(helper
);