2 * Copyright 2011 Samuel SerapiĆ³n
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
23 #include "wine/debug.h"
24 WINE_DEFAULT_DEBUG_CHANNEL(ntlm
);
26 CRITICAL_SECTION ContextCritSect
;
27 LIST_ENTRY ValidContextList
;
30 NtlmContextInitialize(VOID
)
32 InitializeCriticalSection(&ContextCritSect
);
33 InitializeListHead(&ValidContextList
);
35 return STATUS_SUCCESS
;
39 NtlmReferenceContext(IN ULONG_PTR Handle
)
41 PNTLMSSP_CONTEXT context
;
42 EnterCriticalSection(&ContextCritSect
);
44 context
= (PNTLMSSP_CONTEXT
)Handle
;
48 TRACE("%p refcount %lu\n",context
, context
->RefCount
);
49 ASSERT(context
->RefCount
> 0);
51 /* A context that is not authenticated is only valid for a
52 pre-determined interval */
54 if (NtlmHasIntervalElapsed(context
->StartTime
, context
->Timeout
))
56 if ((context
->State
!= Authenticated
) &&
57 (context
->State
!= AuthenticateSent
) &&
58 (context
->State
!= PassedToService
))
60 WARN("%p has timed out\n", context
);
61 LeaveCriticalSection(&ContextCritSect
);
67 LeaveCriticalSection(&ContextCritSect
);
72 NtlmDereferenceContext(IN ULONG_PTR Handle
)
74 PNTLMSSP_CONTEXT context
;
75 EnterCriticalSection(&ContextCritSect
);
77 context
= (PNTLMSSP_CONTEXT
)Handle
;
81 TRACE("%p refcount %lu\n",context
, context
->RefCount
);
82 ASSERT(context
->RefCount
>= 1);
84 /* decrement reference */
87 /* check for object rundown */
88 if (context
->RefCount
== 0)
90 TRACE("Deleting context %p\n",context
);
92 /* dereference credential */
93 if(context
->Credential
)
94 NtlmDereferenceCredential((ULONG_PTR
)context
->Credential
);
96 /* remove from list */
97 RemoveEntryList(&context
->Entry
);
103 LeaveCriticalSection(&ContextCritSect
);
107 NtlmContextTerminate(VOID
)
109 EnterCriticalSection(&ContextCritSect
);
111 /* dereference all items */
112 while (!IsListEmpty(&ValidContextList
))
114 PNTLMSSP_CONTEXT Context
;
115 Context
= CONTAINING_RECORD(ValidContextList
.Flink
,
119 NtlmDereferenceContext((ULONG_PTR
)Context
);
122 LeaveCriticalSection(&ContextCritSect
);
124 /* free critical section */
125 DeleteCriticalSection(&ContextCritSect
);
131 NtlmAllocateContext(VOID
)
133 SECPKG_CALL_INFO CallInfo
;
134 PNTLMSSP_CONTEXT ret
;
136 ret
= (PNTLMSSP_CONTEXT
)NtlmAllocate(sizeof(NTLMSSP_CONTEXT
));
140 ERR("allocate context failed!\n");
144 /* set process fields */
145 ret
->ProcId
= GetCurrentProcessId();
148 if(NtlmLsaFuncTable
->GetCallInfo(&CallInfo
))
149 ret
->ProcId
= CallInfo
.ProcessId
;
154 (VOID
)NtQuerySystemTime(&ret
->StartTime
);
155 ret
->Timeout
= NTLM_DEFAULT_TIMEOUT
;
158 EnterCriticalSection(&ContextCritSect
);
159 InsertHeadList(&ValidContextList
, &ret
->Entry
);
160 LeaveCriticalSection(&ContextCritSect
);
162 TRACE("added context %p\n",ret
);
167 NtlmCreateNegoContext(IN ULONG_PTR Credential
,
168 IN SEC_WCHAR
*pszTargetName
,
169 IN ULONG fContextReq
,
170 OUT PULONG_PTR phNewContext
,
171 OUT PULONG pfContextAttr
,
172 OUT PTimeStamp ptsExpiry
,
173 OUT PUCHAR pSessionKey
,
174 OUT PULONG pfNegotiateFlags
)
176 SECURITY_STATUS ret
= SEC_E_OK
;
177 PNTLMSSP_CONTEXT context
= NULL
;
178 PNTLMSSP_CREDENTIAL cred
;
181 *pfNegotiateFlags
= 0;
183 cred
= NtlmReferenceCredential(Credential
);
184 if ((cred
->UseFlags
& SECPKG_CRED_OUTBOUND
) == 0 )
186 ret
= SEC_E_UNSUPPORTED_FUNCTION
;
187 ERR("Invalid credential use!\n");
191 context
= NtlmAllocateContext();
195 ret
= SEC_E_INSUFFICIENT_MEMORY
;
196 ERR("SEC_E_INSUFFICIENT_MEMORY!\n");
200 /* always on features */
201 context
->NegotiateFlags
= NTLMSSP_NEGOTIATE_UNICODE
|
202 NTLMSSP_NEGOTIATE_OEM
|
203 NTLMSSP_NEGOTIATE_NTLM
|
204 NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY
| //if supported
205 NTLMSSP_REQUEST_TARGET
|
206 NTLMSSP_NEGOTIATE_ALWAYS_SIGN
|
207 NTLMSSP_NEGOTIATE_56
|
208 NTLMSSP_NEGOTIATE_128
; // if supported
210 /* client requested features */
211 if(fContextReq
& ISC_REQ_INTEGRITY
)
213 *pfContextAttr
|= ISC_RET_INTEGRITY
;
214 context
->ContextFlags
|= ISC_RET_INTEGRITY
;
215 context
->NegotiateFlags
|= NTLMSSP_NEGOTIATE_SIGN
;
218 if(fContextReq
& ISC_REQ_SEQUENCE_DETECT
)
220 *pfContextAttr
|= ISC_RET_SEQUENCE_DETECT
;
221 context
->ContextFlags
|= ISC_RET_SEQUENCE_DETECT
;
222 context
->NegotiateFlags
|= NTLMSSP_NEGOTIATE_SIGN
;
225 if(fContextReq
& ISC_REQ_REPLAY_DETECT
)
227 *pfContextAttr
|= ISC_RET_REPLAY_DETECT
;
228 context
->ContextFlags
|= ISC_RET_REPLAY_DETECT
;
229 context
->NegotiateFlags
|= NTLMSSP_NEGOTIATE_SIGN
;
232 if(fContextReq
& ISC_REQ_CONFIDENTIALITY
)
234 context
->NegotiateFlags
|= NTLMSSP_NEGOTIATE_SEAL
|
235 NTLMSSP_NEGOTIATE_LM_KEY
|
236 NTLMSSP_NEGOTIATE_KEY_EXCH
|
237 NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY
;
239 *pfContextAttr
|= ISC_RET_CONFIDENTIALITY
;
240 context
->ContextFlags
|= ISC_RET_CONFIDENTIALITY
;
243 if(fContextReq
& ISC_REQ_NULL_SESSION
)
245 *pfContextAttr
|= ISC_RET_NULL_SESSION
;
246 context
->ContextFlags
|= ISC_RET_NULL_SESSION
;
249 if(fContextReq
& ISC_REQ_CONNECTION
)
251 *pfContextAttr
|= ISC_RET_CONNECTION
;
252 context
->ContextFlags
|= ISC_RET_CONNECTION
;
255 if(fContextReq
& ISC_REQ_IDENTIFY
)
257 context
->NegotiateFlags
|= NTLMSSP_REQUEST_INIT_RESP
;
258 *pfContextAttr
|= ISC_RET_IDENTIFY
;
259 context
->ContextFlags
|= ISC_RET_IDENTIFY
;
262 if(!(fContextReq
& ISC_REQ_DATAGRAM
))
265 context
->NegotiateFlags
|= NTLMSSP_NEGOTIATE_DATAGRAM
;
266 context
->NegotiateFlags
&= ~NTLMSSP_NEGOTIATE_NT_ONLY
;
267 context
->ContextFlags
|= ISC_RET_DATAGRAM
;
268 *pfContextAttr
|= ISC_RET_DATAGRAM
;
270 /* generate session key */
271 if(context
->NegotiateFlags
& (NTLMSSP_NEGOTIATE_SIGN
|
272 NTLMSSP_NEGOTIATE_SEAL
))
274 ret
= NtlmGenerateRandomBits(context
->SessionKey
,
275 MSV1_0_USER_SESSION_KEY_LENGTH
);
279 ERR("Failed to generate session key!\n");
286 /* generate session key */
287 if (context
->NegotiateFlags
& NTLMSSP_NEGOTIATE_KEY_EXCH
)
289 ret
= NtlmGenerateRandomBits(context
->SessionKey
,
290 MSV1_0_USER_SESSION_KEY_LENGTH
);
294 ERR("Failed to generate session key!\n");
300 *pfNegotiateFlags
= context
->NegotiateFlags
;
302 context
->Credential
= cred
;
304 *phNewContext
= (ULONG_PTR
)context
;
310 NtlmDereferenceContext((ULONG_PTR
)context
);
314 /* public functions */
318 InitializeSecurityContextW(IN OPTIONAL PCredHandle phCredential
,
319 IN OPTIONAL PCtxtHandle phContext
,
320 IN OPTIONAL SEC_WCHAR
*pszTargetName
,
321 IN ULONG fContextReq
,
323 IN ULONG TargetDataRep
,
324 IN OPTIONAL PSecBufferDesc pInput
,
326 IN OUT OPTIONAL PCtxtHandle phNewContext
,
327 IN OUT OPTIONAL PSecBufferDesc pOutput
,
328 OUT ULONG
*pfContextAttr
,
329 OUT OPTIONAL PTimeStamp ptsExpiry
)
331 SECURITY_STATUS ret
= SEC_E_OK
;
332 PSecBuffer InputToken1
, InputToken2
;
333 PSecBuffer OutputToken1
, OutputToken2
;
334 ULONG_PTR newContext
;
335 ULONG NegotiateFlags
;
338 TRACE("%p %p %s 0x%08lx %lx %lx %p %lx %p %p %p %p\n", phCredential
, phContext
,
339 debugstr_w(pszTargetName
), fContextReq
, Reserved1
, TargetDataRep
, pInput
,
340 Reserved1
, phNewContext
, pOutput
, pfContextAttr
, ptsExpiry
);
342 if(TargetDataRep
== SECURITY_NETWORK_DREP
)
343 WARN("SECURITY_NETWORK_DREP!!\n");
345 /* get first input token */
346 ret
= NtlmGetSecBuffer(pInput
,
352 ERR("Failed to get input token!\n");
353 return SEC_E_INVALID_TOKEN
;
356 /* get first output token */
357 ret
= NtlmGetSecBuffer(pOutput
,
363 ERR("Failed to get output token!\n");
364 return SEC_E_BUFFER_TOO_SMALL
;
367 /* first call! nego message creation */
368 if(!phContext
&& !pInput
)
372 ret
= SEC_E_INVALID_HANDLE
;
376 ret
= NtlmCreateNegoContext(phCredential
->dwLower
,
385 if(!newContext
|| !NT_SUCCESS(ret
))
387 ERR("NtlmCreateNegoContext failed with %lx\n", ret
);
391 ret
= NtlmGenerateNegotiateMessage(newContext
,
398 ERR("NtlmGenerateNegotiateMessage failed with %lx\n", ret
);
403 phNewContext
->dwUpper
= NegotiateFlags
;
404 phNewContext
->dwLower
= newContext
;
406 else /* challenge! */
408 TRACE("ISC challenged!\n");
409 *phNewContext
= *phContext
;
410 if (fContextReq
& ISC_REQ_USE_SUPPLIED_CREDS
)
412 /* get second input token */
413 ret
= NtlmGetSecBuffer(pInput
,
419 ERR("Failed to get input token!\n");
420 return SEC_E_INVALID_TOKEN
;
424 ret
= NtlmHandleChallengeMessage(phNewContext
->dwLower
,
436 ERR("NtlmHandleChallengeMessage failed with %lx\n", ret
);
442 /* build blob with the output message */
443 SecBufferDesc BufferDesc
;
444 BufferDesc
.ulVersion
= SECBUFFER_VERSION
;
445 BufferDesc
.cBuffers
= 1;
446 BufferDesc
.pBuffers
= OutputToken1
;
448 if(fContextReq
& ISC_REQ_ALLOCATE_MEMORY
)
449 *pfContextAttr
|= ISC_RET_ALLOCATED_MEMORY
;
451 *pOutput
= BufferDesc
;
458 NtlmDereferenceContext(newContext
);
460 if(fContextReq
& ISC_REQ_ALLOCATE_MEMORY
)
462 if(OutputToken1
&& OutputToken1
->pvBuffer
)
463 NtlmFree(OutputToken1
->pvBuffer
);
464 if(OutputToken2
&& OutputToken2
->pvBuffer
)
465 NtlmFree(OutputToken1
->pvBuffer
);
473 InitializeSecurityContextA(IN OPTIONAL PCredHandle phCredential
,
474 IN OPTIONAL PCtxtHandle phContext
,
475 IN OPTIONAL SEC_CHAR
*pszTargetName
,
476 IN ULONG fContextReq
,
478 IN ULONG TargetDataRep
,
479 IN OPTIONAL PSecBufferDesc pInput
,
481 IN OUT OPTIONAL PCtxtHandle phNewContext
,
482 IN OUT OPTIONAL PSecBufferDesc pOutput
,
483 OUT ULONG
*pfContextAttr
,
484 OUT OPTIONAL PTimeStamp ptsExpiry
)
487 SEC_WCHAR
*target
= NULL
;
489 TRACE("%p %p %s %lx %lx %lx %p %lx %p %p %p %p\n", phCredential
, phContext
,
490 debugstr_a(pszTargetName
), fContextReq
, Reserved1
, TargetDataRep
, pInput
,
491 Reserved1
, phNewContext
, pOutput
, pfContextAttr
, ptsExpiry
);
493 if(pszTargetName
!= NULL
)
495 int target_size
= MultiByteToWideChar(CP_ACP
, 0, pszTargetName
,
496 strlen(pszTargetName
)+1, NULL
, 0);
497 target
= HeapAlloc(GetProcessHeap(), 0, target_size
*
499 MultiByteToWideChar(CP_ACP
, 0, pszTargetName
, strlen(pszTargetName
)+1,
500 target
, target_size
);
503 ret
= InitializeSecurityContextW(phCredential
, phContext
, target
,
504 fContextReq
, Reserved1
, TargetDataRep
, pInput
, Reserved2
,
505 phNewContext
, pOutput
, pfContextAttr
, ptsExpiry
);
507 HeapFree(GetProcessHeap(), 0, target
);
513 QueryContextAttributesW(PCtxtHandle phContext
,
517 SECURITY_STATUS ret
= SEC_E_OK
;
518 PNTLMSSP_CONTEXT context
= NtlmReferenceContext(phContext
->dwLower
);
520 TRACE("%p %lx %p\n", phContext
, ulAttribute
, pBuffer
);
523 return SEC_E_INVALID_HANDLE
;
527 case SECPKG_ATTR_SIZES
:
529 PSecPkgContext_Sizes spcs
= (PSecPkgContext_Sizes
) pBuffer
;
530 spcs
->cbMaxToken
= NTLM_MAX_BUF
;
531 spcs
->cbMaxSignature
= sizeof(MESSAGE_SIGNATURE
);
532 spcs
->cbBlockSize
= 0;
533 spcs
->cbSecurityTrailer
= sizeof(MESSAGE_SIGNATURE
);
536 case SECPKG_ATTR_FLAGS
:
538 PSecPkgContext_Flags spcf
= (PSecPkgContext_Flags
)pBuffer
;
540 if(context
->NegotiateFlags
& NTLMSSP_NEGOTIATE_SIGN
)
541 spcf
->Flags
|= ISC_RET_INTEGRITY
;
542 if(context
->NegotiateFlags
& NTLMSSP_NEGOTIATE_SEAL
)
543 spcf
->Flags
|= ISC_RET_CONFIDENTIALITY
;
547 FIXME("ulAttribute %lx unsupported\n", ulAttribute
);
548 ret
= SEC_E_UNSUPPORTED_FUNCTION
;
551 NtlmDereferenceContext((ULONG_PTR
)context
);
557 QueryContextAttributesA(PCtxtHandle phContext
,
561 return QueryContextAttributesW(phContext
, ulAttribute
, pBuffer
);
566 AcceptSecurityContext(IN PCredHandle phCredential
,
567 IN OUT PCtxtHandle phContext
,
568 IN PSecBufferDesc pInput
,
569 IN ULONG fContextReq
,
570 IN ULONG TargetDataRep
,
571 IN OUT PCtxtHandle phNewContext
,
572 IN OUT PSecBufferDesc pOutput
,
573 OUT ULONG
*pfContextAttr
,
574 OUT PTimeStamp ptsExpiry
)
576 SECURITY_STATUS ret
= SEC_E_OK
;
577 PSecBuffer InputToken1
, InputToken2
;
578 PSecBuffer OutputToken1
;
580 TRACE("AcceptSecurityContext %p %p %p %lx %lx %p %p %p %p\n", phCredential
, phContext
, pInput
,
581 fContextReq
, TargetDataRep
, phNewContext
, pOutput
, pfContextAttr
, ptsExpiry
);
583 /* get first input token */
584 ret
= NtlmGetSecBuffer(pInput
,
590 ERR("Failed to get input token!\n");
591 return SEC_E_INVALID_TOKEN
;
594 /* get second input token */
595 ret
= NtlmGetSecBuffer(pInput
,
601 ERR("Failed to get input token!\n");
602 //return SEC_E_INVALID_TOKEN;
605 /* get first output token */
606 ret
= NtlmGetSecBuffer(pOutput
,
612 ERR("Failed to get output token!\n");
613 return SEC_E_BUFFER_TOO_SMALL
;
617 if(!phContext
&& !InputToken2
)
621 ret
= SEC_E_INVALID_HANDLE
;
625 ret
= NtlmHandleNegotiateMessage(phCredential
->dwLower
,
626 &phNewContext
->dwLower
,
635 WARN("Handle Authenticate UNIMPLEMENTED!\n");
637 //if(!NT_SUCCESS(ret))
647 DeleteSecurityContext(PCtxtHandle phContext
)
650 return SEC_E_INVALID_HANDLE
;
652 NtlmDereferenceContext((ULONG_PTR
)phContext
->dwLower
);
659 ImpersonateSecurityContext(PCtxtHandle phContext
)
663 TRACE("%p\n", phContext
);
666 ret
= SEC_E_UNSUPPORTED_FUNCTION
;
670 ret
= SEC_E_INVALID_HANDLE
;
677 RevertSecurityContext(PCtxtHandle phContext
)
681 TRACE("%p\n", phContext
);
684 ret
= SEC_E_UNSUPPORTED_FUNCTION
;
688 ret
= SEC_E_INVALID_HANDLE
;
695 FreeContextBuffer(PVOID pv
)
703 ApplyControlToken(IN PCtxtHandle phContext
,
704 IN PSecBufferDesc pInput
)
708 return SEC_E_UNSUPPORTED_FUNCTION
;