2 * Copyright (C) 1996-2015 The Squid Software Foundation and contributors
4 * Squid software is distributed under GPLv2+ license and includes
5 * contributions from numerous individuals and organizations.
6 * Please see the COPYING and CONTRIBUTORS files for details.
11 #include "ntlmauth/ntlmauth.h"
15 typedef struct _AUTH_SEQ
{
20 TimeStamp hcredLifeTime
;
21 struct _SecHandle hctxt
;
22 TimeStamp hctxtLifeTime
;
23 } AUTH_SEQ
, *PAUTH_SEQ
;
25 BOOL
GenClientContext(PAUTH_SEQ
, PSEC_WINNT_AUTH_IDENTITY
, PVOID
, DWORD
, PVOID
, PDWORD
, PBOOL
);
26 BOOL
GenServerContext(PAUTH_SEQ
, PVOID
, DWORD
, PVOID
, PDWORD
, PBOOL
, char *);
28 static HMODULE hModule
;
29 static int NTLM_mode
= SSP_BASIC
;
30 static char * SSP_Package_InUse
;
31 SECURITY_STATUS SecurityStatus
= SEC_E_OK
;
33 static DWORD cbMaxToken
= 0;
34 static PVOID pClientBuf
= NULL
;
35 static PVOID pServerBuf
= NULL
;
37 static AUTH_SEQ NTLM_asServer
= {0};
39 BOOL Use_Unicode
= FALSE
;
40 BOOL NTLM_LocalCall
= FALSE
;
42 /* Function pointers */
43 ACCEPT_SECURITY_CONTEXT_FN _AcceptSecurityContext
= NULL
;
44 ACQUIRE_CREDENTIALS_HANDLE_FN _AcquireCredentialsHandle
= NULL
;
45 COMPLETE_AUTH_TOKEN_FN _CompleteAuthToken
= NULL
;
46 DELETE_SECURITY_CONTEXT_FN _DeleteSecurityContext
= NULL
;
47 FREE_CONTEXT_BUFFER_FN _FreeContextBuffer
= NULL
;
48 FREE_CREDENTIALS_HANDLE_FN _FreeCredentialsHandle
= NULL
;
49 INITIALIZE_SECURITY_CONTEXT_FN _InitializeSecurityContext
= NULL
;
50 QUERY_SECURITY_PACKAGE_INFO_FN _QuerySecurityPackageInfo
= NULL
;
52 QUERY_CONTEXT_ATTRIBUTES_FN_W _QueryContextAttributes
= NULL
;
54 QUERY_CONTEXT_ATTRIBUTES_FN_A _QueryContextAttributes
= NULL
;
57 void UnloadSecurityDll(void)
59 if (NTLM_asServer
.fHaveCtxtHandle
)
60 _DeleteSecurityContext(&NTLM_asServer
.hctxt
);
61 if (NTLM_asServer
.fHaveCredHandle
)
62 _FreeCredentialsHandle(&NTLM_asServer
.hcred
);
67 xfree(SSP_Package_InUse
);
71 _AcceptSecurityContext
= NULL
;
72 _AcquireCredentialsHandle
= NULL
;
73 _CompleteAuthToken
= NULL
;
74 _DeleteSecurityContext
= NULL
;
75 _FreeContextBuffer
= NULL
;
76 _FreeCredentialsHandle
= NULL
;
77 _InitializeSecurityContext
= NULL
;
78 _QuerySecurityPackageInfo
= NULL
;
79 _QueryContextAttributes
= NULL
;
84 HMODULE
LoadSecurityDll(int mode
, const char * SSP_Package
)
86 TCHAR lpszDLL
[MAX_PATH
];
87 OSVERSIONINFO VerInfo
;
88 PSecPkgInfo pSPI
= NULL
;
91 * Find out which security DLL to use, depending on
92 * whether we are on NT or 2000 or XP or 2003 Server
93 * We have to use security.dll on Windows NT 4.0.
94 * All other operating systems, we have to use Secur32.dll
97 if ((mode
!= SSP_BASIC
) && (mode
!= SSP_NTLM
))
100 VerInfo
.dwOSVersionInfoSize
= sizeof (OSVERSIONINFO
);
101 if (!GetVersionEx (&VerInfo
)) { /* If this fails, something has gone wrong */
104 if (VerInfo
.dwPlatformId
== VER_PLATFORM_WIN32_NT
&&
105 VerInfo
.dwMajorVersion
== 4 &&
106 VerInfo
.dwMinorVersion
== 0) {
107 lstrcpy (lpszDLL
, _T(WINNT_SECURITY_DLL
));
109 lstrcpy (lpszDLL
, _T(WIN2K_SECURITY_DLL
));
111 hModule
= LoadLibrary(lpszDLL
);
114 _AcceptSecurityContext
= (ACCEPT_SECURITY_CONTEXT_FN
)
115 GetProcAddress(hModule
, "AcceptSecurityContext");
116 if (!_AcceptSecurityContext
) {
122 _AcquireCredentialsHandle
= (ACQUIRE_CREDENTIALS_HANDLE_FN
)
123 GetProcAddress(hModule
, "AcquireCredentialsHandleW");
125 _AcquireCredentialsHandle
= (ACQUIRE_CREDENTIALS_HANDLE_FN
)
126 GetProcAddress(hModule
, "AcquireCredentialsHandleA");
128 if (!_AcquireCredentialsHandle
) {
133 _CompleteAuthToken
= (COMPLETE_AUTH_TOKEN_FN
)
134 GetProcAddress(hModule
, "CompleteAuthToken");
135 if (!_CompleteAuthToken
) {
140 _DeleteSecurityContext
= (DELETE_SECURITY_CONTEXT_FN
)
141 GetProcAddress(hModule
, "DeleteSecurityContext");
142 if (!_DeleteSecurityContext
) {
147 _FreeContextBuffer
= (FREE_CONTEXT_BUFFER_FN
)
148 GetProcAddress(hModule
, "FreeContextBuffer");
149 if (!_FreeContextBuffer
) {
154 _FreeCredentialsHandle
= (FREE_CREDENTIALS_HANDLE_FN
)
155 GetProcAddress(hModule
, "FreeCredentialsHandle");
156 if (!_FreeCredentialsHandle
) {
162 _InitializeSecurityContext
= (INITIALIZE_SECURITY_CONTEXT_FN
)
163 GetProcAddress(hModule
, "InitializeSecurityContextW");
165 _InitializeSecurityContext
= (INITIALIZE_SECURITY_CONTEXT_FN
)
166 GetProcAddress(hModule
, "InitializeSecurityContextA");
168 if (!_InitializeSecurityContext
) {
174 _QuerySecurityPackageInfo
= (QUERY_SECURITY_PACKAGE_INFO_FN
)
175 GetProcAddress(hModule
, "QuerySecurityPackageInfoW");
177 _QuerySecurityPackageInfo
= (QUERY_SECURITY_PACKAGE_INFO_FN
)
178 GetProcAddress(hModule
, "QuerySecurityPackageInfoA");
180 if (!_QuerySecurityPackageInfo
) {
186 _QueryContextAttributes
= (QUERY_CONTEXT_ATTRIBUTES_FN_W
)
187 GetProcAddress(hModule
, "QueryContextAttributesW");
189 _QueryContextAttributes
= (QUERY_CONTEXT_ATTRIBUTES_FN_A
)
190 GetProcAddress(hModule
, "QueryContextAttributesA");
192 if (!_QueryContextAttributes
) {
197 /* Get max token size */
198 _QuerySecurityPackageInfo((SEC_CHAR
*)_T(SSP_Package
), &pSPI
);
199 cbMaxToken
= pSPI
->cbMaxToken
;
200 _FreeContextBuffer(pSPI
);
202 /* Allocate buffers for client and server messages */
203 pClientBuf
= xcalloc(cbMaxToken
, sizeof(char));
204 pServerBuf
= xcalloc(cbMaxToken
, sizeof(char));
205 SSP_Package_InUse
= xstrdup(SSP_Package
);
210 BOOL
GenClientContext(PAUTH_SEQ pAS
, PSEC_WINNT_AUTH_IDENTITY pAuthIdentity
,
211 PVOID pIn
, DWORD cbIn
, PVOID pOut
, PDWORD pcbOut
, PBOOL pfDone
)
214 * Routine Description:
216 * Optionally takes an input buffer coming from the server and returns
217 * a buffer of information to send back to the server. Also returns
218 * an indication of whether or not the context is complete.
221 * Returns TRUE if successful; otherwise FALSE.
224 SecBufferDesc sbdOut
;
230 if (!pAS
->fInitialized
) {
231 SecurityStatus
= _AcquireCredentialsHandle(NULL
, (SEC_CHAR
*) _T(SSP_Package_InUse
),
232 SECPKG_CRED_OUTBOUND
, NULL
, (NTLM_mode
== SSP_NTLM
) ? NULL
: pAuthIdentity
, NULL
, NULL
,
233 &pAS
->hcred
, &tsExpiry
);
234 if (SecurityStatus
< 0)
236 pAS
->fHaveCredHandle
= TRUE
;
239 /* Prepare output buffer */
240 sbdOut
.ulVersion
= 0;
242 sbdOut
.pBuffers
= &sbOut
;
243 sbOut
.cbBuffer
= *pcbOut
;
244 sbOut
.BufferType
= SECBUFFER_TOKEN
;
245 sbOut
.pvBuffer
= pOut
;
247 /* Prepare input buffer */
248 if (pAS
->fInitialized
) {
251 sbdIn
.pBuffers
= &sbIn
;
252 sbIn
.cbBuffer
= cbIn
;
253 sbIn
.BufferType
= SECBUFFER_TOKEN
;
256 SecurityStatus
= _InitializeSecurityContext(&pAS
->hcred
,
257 pAS
->fInitialized
? &pAS
->hctxt
: NULL
, NULL
, 0, 0,
258 SECURITY_NATIVE_DREP
, pAS
->fInitialized
? &sbdIn
: NULL
,
259 0, &pAS
->hctxt
, &sbdOut
, &fContextAttr
, &tsExpiry
);
260 if (SecurityStatus
< 0)
262 pAS
->fHaveCtxtHandle
= TRUE
;
264 /* If necessary, complete token */
265 if (SecurityStatus
== SEC_I_COMPLETE_NEEDED
|| SecurityStatus
== SEC_I_COMPLETE_AND_CONTINUE
) {
266 SecurityStatus
= _CompleteAuthToken(&pAS
->hctxt
, &sbdOut
);
267 if (SecurityStatus
< 0)
270 *pcbOut
= sbOut
.cbBuffer
;
271 if (!pAS
->fInitialized
)
272 pAS
->fInitialized
= TRUE
;
273 *pfDone
= !(SecurityStatus
== SEC_I_CONTINUE_NEEDED
274 || SecurityStatus
== SEC_I_COMPLETE_AND_CONTINUE
);
278 BOOL
GenServerContext(PAUTH_SEQ pAS
, PVOID pIn
, DWORD cbIn
, PVOID pOut
,
279 PDWORD pcbOut
, PBOOL pfDone
, char * credentials
)
282 * Routine Description:
284 * Takes an input buffer coming from the client and returns a buffer
285 * to be sent to the client. Also returns an indication of whether or
286 * not the context is complete.
290 * Returns TRUE if successful; otherwise FALSE.
293 SecBufferDesc sbdOut
;
298 SecPkgContext_Names namebuffer
;
300 if (!pAS
->fInitialized
) {
301 SecurityStatus
= _AcquireCredentialsHandle(NULL
, (SEC_CHAR
*) _T(SSP_Package_InUse
),
302 SECPKG_CRED_INBOUND
, NULL
, NULL
, NULL
, NULL
, &pAS
->hcred
,
303 &pAS
->hcredLifeTime
);
305 fprintf(stderr
, "AcquireCredentialsHandle returned: %x\n", SecurityStatus
);
307 if (SecurityStatus
< 0) {
309 fprintf(stderr
, "AcquireCredentialsHandle failed: %x\n", SecurityStatus
);
313 pAS
->fHaveCredHandle
= TRUE
;
316 /* Prepare output buffer */
317 sbdOut
.ulVersion
= 0;
319 sbdOut
.pBuffers
= &sbOut
;
320 sbOut
.cbBuffer
= *pcbOut
;
321 sbOut
.BufferType
= SECBUFFER_TOKEN
;
322 sbOut
.pvBuffer
= pOut
;
324 /* Prepare input buffer */
327 sbdIn
.pBuffers
= &sbIn
;
328 sbIn
.cbBuffer
= cbIn
;
329 sbIn
.BufferType
= SECBUFFER_TOKEN
;
331 SecurityStatus
= _AcceptSecurityContext(&pAS
->hcred
,
332 pAS
->fInitialized
? &pAS
->hctxt
: NULL
, &sbdIn
, (NTLM_mode
== SSP_NTLM
) ? ASC_REQ_DELEGATE
: 0,
333 SECURITY_NATIVE_DREP
, &pAS
->hctxt
, &sbdOut
, &fContextAttr
,
334 &pAS
->hctxtLifeTime
);
336 fprintf(stderr
, "AcceptSecurityContext returned: %x\n", SecurityStatus
);
338 if (SecurityStatus
< 0) {
340 fprintf(stderr
, "AcceptSecurityContext failed: %x\n", SecurityStatus
);
344 pAS
->fHaveCtxtHandle
= TRUE
;
346 /* If necessary, complete token */
347 if (SecurityStatus
== SEC_I_COMPLETE_NEEDED
|| SecurityStatus
== SEC_I_COMPLETE_AND_CONTINUE
) {
348 SecurityStatus
= _CompleteAuthToken(&pAS
->hctxt
, &sbdOut
);
350 fprintf(stderr
, "CompleteAuthToken returned: %x\n", SecurityStatus
);
352 if (SecurityStatus
< 0) {
354 fprintf(stderr
, "CompleteAuthToken failed: %x\n", SecurityStatus
);
360 if ((credentials
!= NULL
) &&
361 !(SecurityStatus
== SEC_I_CONTINUE_NEEDED
|| SecurityStatus
== SEC_I_COMPLETE_AND_CONTINUE
)) {
362 SecurityStatus
= _QueryContextAttributes(&pAS
->hctxt
, SECPKG_ATTR_NAMES
, &namebuffer
);
364 fprintf(stderr
, "QueryContextAttributes returned: %x\n", SecurityStatus
);
366 if (SecurityStatus
< 0) {
368 fprintf(stderr
, "QueryContextAttributes failed: %x\n", SecurityStatus
);
372 strncpy(credentials
, namebuffer
.sUserName
, SSP_MAX_CRED_LEN
);
375 *pcbOut
= sbOut
.cbBuffer
;
376 if (!pAS
->fInitialized
)
377 pAS
->fInitialized
= TRUE
;
378 *pfDone
= !(SecurityStatus
== SEC_I_CONTINUE_NEEDED
379 || SecurityStatus
== SEC_I_COMPLETE_AND_CONTINUE
);
383 BOOL WINAPI
SSP_LogonUser(PTSTR szUser
, PTSTR szPassword
, PTSTR szDomain
)
385 AUTH_SEQ asServer
= {0};
386 AUTH_SEQ asClient
= {0};
388 BOOL fResult
= FALSE
;
392 SEC_WINNT_AUTH_IDENTITY ai
;
398 /* Initialize auth identity structure */
399 ZeroMemory(&ai
, sizeof(ai
));
400 ai
.Domain
= (unsigned char *)szDomain
;
401 ai
.DomainLength
= lstrlen(szDomain
);
402 ai
.User
= (unsigned char *)szUser
;
403 ai
.UserLength
= lstrlen(szUser
);
404 ai
.Password
= (unsigned char *)szPassword
;
405 ai
.PasswordLength
= lstrlen(szPassword
);
406 #if defined(UNICODE) || defined(_UNICODE)
407 ai
.Flags
= SEC_WINNT_AUTH_IDENTITY_UNICODE
;
409 ai
.Flags
= SEC_WINNT_AUTH_IDENTITY_ANSI
;
412 /* Prepare client message (negotiate) */
414 if (!GenClientContext(&asClient
, &ai
, NULL
, 0, pClientBuf
, &cbOut
, &fDone
))
417 /* Prepare server message (challenge) */
420 if (!GenServerContext(&asServer
, pClientBuf
, cbIn
, pServerBuf
, &cbOut
,
423 /* Most likely failure: AcceptServerContext fails with SEC_E_LOGON_DENIED
424 * in the case of bad szUser or szPassword.
425 * Unexpected Result: Logon will succeed if you pass in a bad szUser and
426 * the guest account is enabled in the specified domain.
429 /* Prepare client message (authenticate) */
432 if (!GenClientContext(&asClient
, &ai
, pServerBuf
, cbIn
, pClientBuf
, &cbOut
,
436 /* Prepare server message (authentication) */
439 if (!GenServerContext(&asServer
, pClientBuf
, cbIn
, pServerBuf
, &cbOut
,
445 /* Clean up resources */
446 if (asClient
.fHaveCtxtHandle
)
447 _DeleteSecurityContext(&asClient
.hctxt
);
448 if (asClient
.fHaveCredHandle
)
449 _FreeCredentialsHandle(&asClient
.hcred
);
450 if (asServer
.fHaveCtxtHandle
)
451 _DeleteSecurityContext(&asServer
.hctxt
);
452 if (asServer
.fHaveCredHandle
)
453 _FreeCredentialsHandle(&asServer
.hcred
);
458 const char * WINAPI
SSP_MakeChallenge(PVOID PNegotiateBuf
, int NegotiateLen
)
461 PVOID fResult
= NULL
;
464 ntlm_challenge
* challenge
;
466 if (NTLM_asServer
.fHaveCtxtHandle
)
467 _DeleteSecurityContext(&NTLM_asServer
.hctxt
);
468 if (NTLM_asServer
.fHaveCredHandle
)
469 _FreeCredentialsHandle(&NTLM_asServer
.hcred
);
471 NTLM_LocalCall
= FALSE
;
473 memcpy(pClientBuf
, PNegotiateBuf
, NegotiateLen
);
474 ZeroMemory(pServerBuf
, cbMaxToken
);
475 ZeroMemory(&NTLM_asServer
, sizeof(NTLM_asServer
));
480 /* Prepare server message (challenge) */
483 if (!GenServerContext(&NTLM_asServer
, pClientBuf
, cbIn
, pServerBuf
, &cbOut
,
486 fResult
= pServerBuf
;
488 if (fResult
!= NULL
) {
489 challenge
= (ntlm_challenge
*) fResult
;
490 Use_Unicode
= NTLM_NEGOTIATE_UNICODE
& challenge
->flags
;
491 NTLM_LocalCall
= NTLM_NEGOTIATE_THIS_IS_LOCAL_CALL
& challenge
->flags
;
492 struct base64_encode_ctx ctx
;
493 base64_encode_init(&ctx
);
494 static uint8_t encoded
[8192];
495 size_t dstLen
= base64_encode_update(&ctx
, encoded
, cbOut
, reinterpret_cast<const uint8_t*>(fResult
));
496 assert(dstLen
< sizeof(encoded
));
497 dstLen
+= base64_encode_final(&ctx
, encoded
+dstLen
);
498 assert(dstLen
< sizeof(encoded
));
499 encoded
[dstLen
] = '\0';
500 return reinterpret_cast<char *>(encoded
);
505 BOOL WINAPI
SSP_ValidateNTLMCredentials(PVOID PAutenticateBuf
, int AutenticateLen
, char * credentials
)
508 BOOL fResult
= FALSE
;
512 memcpy(pClientBuf
, PAutenticateBuf
, AutenticateLen
);
513 ZeroMemory(pServerBuf
, cbMaxToken
);
518 /* Prepare server message (authentication) */
519 cbIn
= AutenticateLen
;
521 if (!GenServerContext(&NTLM_asServer
, pClientBuf
, cbIn
, pServerBuf
, &cbOut
,
522 &fDone
, credentials
))
530 const char * WINAPI
SSP_MakeNegotiateBlob(PVOID PNegotiateBuf
, int NegotiateLen
, PBOOL fDone
, int * Status
, char * credentials
)
535 if (NTLM_asServer
.fHaveCtxtHandle
)
536 _DeleteSecurityContext(&NTLM_asServer
.hctxt
);
537 if (NTLM_asServer
.fHaveCredHandle
)
538 _FreeCredentialsHandle(&NTLM_asServer
.hcred
);
540 memcpy(pClientBuf
, PNegotiateBuf
, NegotiateLen
);
541 ZeroMemory(pServerBuf
, cbMaxToken
);
542 ZeroMemory(&NTLM_asServer
, sizeof(NTLM_asServer
));
547 /* Prepare server message (challenge) */
550 if (!GenServerContext(&NTLM_asServer
, pClientBuf
, cbIn
, pServerBuf
, &cbOut
,
551 fDone
, credentials
)) {
557 if (pServerBuf
!= NULL
&& cbOut
> 0) {
558 struct base64_encode_ctx ctx
;
559 base64_encode_init(&ctx
);
560 static uint8_t encoded
[8192];
561 size_t dstLen
= base64_encode_update(&ctx
, encoded
, cbOut
, reinterpret_cast<const uint8_t*>(pServerBuf
));
562 assert(dstLen
< sizeof(encoded
));
563 dstLen
+= base64_encode_final(&ctx
, encoded
+dstLen
);
564 assert(dstLen
< sizeof(encoded
));
565 encoded
[dstLen
] = '\0';
566 return reinterpret_cast<char *>(encoded
);
571 const char * WINAPI
SSP_ValidateNegotiateCredentials(PVOID PAutenticateBuf
, int AutenticateLen
, PBOOL fDone
, int * Status
, char * credentials
)
576 memcpy(pClientBuf
, PAutenticateBuf
, AutenticateLen
);
577 ZeroMemory(pServerBuf
, cbMaxToken
);
582 /* Prepare server message (authentication) */
583 cbIn
= AutenticateLen
;
585 if (!GenServerContext(&NTLM_asServer
, pClientBuf
, cbIn
, pServerBuf
, &cbOut
,
586 fDone
, credentials
)) {
592 if (pServerBuf
!= NULL
&& cbOut
> 0) {
593 struct base64_encode_ctx ctx
;
594 base64_encode_init(&ctx
);
595 static uint8_t encoded
[8192];
596 size_t dstLen
= base64_encode_update(&ctx
, encoded
, cbOut
, reinterpret_cast<const uint8_t*>(pServerBuf
));
597 assert(dstLen
< sizeof(encoded
));
598 dstLen
+= base64_encode_final(&ctx
, encoded
+dstLen
);
599 assert(dstLen
< sizeof(encoded
));
600 encoded
[dstLen
] = '\0';
601 return reinterpret_cast<char *>(encoded
);