3 * $Id: sspwin32.c,v 1.2 2006/09/09 15:29:59 serassio Exp $
5 * AUTHOR: Guido Serassio <serassio@squid-cache.org>
6 * inspired by previous work by Robert Collins, Francesco Chemolli.
8 * SQUID Web Proxy Cache http://www.squid-cache.org/
9 * ----------------------------------------------------------
11 * Squid is the result of efforts by numerous individuals from
12 * the Internet community; see the CONTRIBUTORS file for full
13 * details. Many organizations have provided support for Squid's
14 * development; see the SPONSORS file for full details. Squid is
15 * Copyrighted (C) 2001 by the Regents of the University of
16 * California; see the COPYRIGHT file for full details. Squid
17 * incorporates software developed and/or copyrighted by other
18 * sources; see the CREDITS file for full details.
20 * This program is free software; you can redistribute it and/or modify
21 * it under the terms of the GNU General Public License as published by
22 * the Free Software Foundation; either version 2 of the License, or
23 * (at your option) any later version.
25 * This program is distributed in the hope that it will be useful,
26 * but WITHOUT ANY WARRANTY; without even the implied warranty of
27 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
28 * GNU General Public License for more details.
30 * You should have received a copy of the GNU General Public License
31 * along with this program; if not, write to the Free Software
32 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
41 typedef struct _AUTH_SEQ
{
46 TimeStamp hcredLifeTime
;
47 struct _SecHandle hctxt
;
48 TimeStamp hctxtLifeTime
;
49 } AUTH_SEQ
, *PAUTH_SEQ
;
51 BOOL
GenClientContext(PAUTH_SEQ
, PSEC_WINNT_AUTH_IDENTITY
, PVOID
, DWORD
, PVOID
, PDWORD
, PBOOL
);
52 BOOL
GenServerContext(PAUTH_SEQ
, PVOID
, DWORD
, PVOID
, PDWORD
, PBOOL
, char *);
54 static HMODULE hModule
;
55 static int NTLM_mode
= SSP_BASIC
;
56 static char * SSP_Package_InUse
;
57 SECURITY_STATUS SecurityStatus
= SEC_E_OK
;
59 static DWORD cbMaxToken
= 0;
60 static PVOID pClientBuf
= NULL
;
61 static PVOID pServerBuf
= NULL
;
63 static AUTH_SEQ NTLM_asServer
= {0};
65 BOOL Use_Unicode
= FALSE
;
66 BOOL NTLM_LocalCall
= FALSE
;
68 /* Function pointers */
69 ACCEPT_SECURITY_CONTEXT_FN _AcceptSecurityContext
= NULL
;
70 ACQUIRE_CREDENTIALS_HANDLE_FN _AcquireCredentialsHandle
= NULL
;
71 COMPLETE_AUTH_TOKEN_FN _CompleteAuthToken
= NULL
;
72 DELETE_SECURITY_CONTEXT_FN _DeleteSecurityContext
= NULL
;
73 FREE_CONTEXT_BUFFER_FN _FreeContextBuffer
= NULL
;
74 FREE_CREDENTIALS_HANDLE_FN _FreeCredentialsHandle
= NULL
;
75 INITIALIZE_SECURITY_CONTEXT_FN _InitializeSecurityContext
= NULL
;
76 QUERY_SECURITY_PACKAGE_INFO_FN _QuerySecurityPackageInfo
= NULL
;
78 QUERY_CONTEXT_ATTRIBUTES_FN_W _QueryContextAttributes
= NULL
;
80 QUERY_CONTEXT_ATTRIBUTES_FN_A _QueryContextAttributes
= NULL
;
83 void UnloadSecurityDll(void)
85 if (NTLM_asServer
.fHaveCtxtHandle
)
86 _DeleteSecurityContext(&NTLM_asServer
.hctxt
);
87 if (NTLM_asServer
.fHaveCredHandle
)
88 _FreeCredentialsHandle(&NTLM_asServer
.hcred
);
93 xfree(SSP_Package_InUse
);
97 _AcceptSecurityContext
= NULL
;
98 _AcquireCredentialsHandle
= NULL
;
99 _CompleteAuthToken
= NULL
;
100 _DeleteSecurityContext
= NULL
;
101 _FreeContextBuffer
= NULL
;
102 _FreeCredentialsHandle
= NULL
;
103 _InitializeSecurityContext
= NULL
;
104 _QuerySecurityPackageInfo
= NULL
;
105 _QueryContextAttributes
= NULL
;
111 HMODULE
LoadSecurityDll(int mode
, char * SSP_Package
)
113 TCHAR lpszDLL
[MAX_PATH
];
114 OSVERSIONINFO VerInfo
;
115 PSecPkgInfo pSPI
= NULL
;
118 * Find out which security DLL to use, depending on
119 * whether we are on NT or 2000 or XP or 2003 Server
120 * We have to use security.dll on Windows NT 4.0.
121 * All other operating systems, we have to use Secur32.dll
124 if ((mode
!= SSP_BASIC
) && (mode
!= SSP_NTLM
))
127 VerInfo
.dwOSVersionInfoSize
= sizeof (OSVERSIONINFO
);
128 if (!GetVersionEx (&VerInfo
)) { /* If this fails, something has gone wrong */
131 if (VerInfo
.dwPlatformId
== VER_PLATFORM_WIN32_NT
&&
132 VerInfo
.dwMajorVersion
== 4 &&
133 VerInfo
.dwMinorVersion
== 0) {
134 lstrcpy (lpszDLL
, _T(WINNT_SECURITY_DLL
));
136 lstrcpy (lpszDLL
, _T(WIN2K_SECURITY_DLL
));
138 hModule
= LoadLibrary(lpszDLL
);
141 _AcceptSecurityContext
= (ACCEPT_SECURITY_CONTEXT_FN
)
142 GetProcAddress(hModule
, "AcceptSecurityContext");
143 if (!_AcceptSecurityContext
) {
149 _AcquireCredentialsHandle
= (ACQUIRE_CREDENTIALS_HANDLE_FN
)
150 GetProcAddress(hModule
, "AcquireCredentialsHandleW");
152 _AcquireCredentialsHandle
= (ACQUIRE_CREDENTIALS_HANDLE_FN
)
153 GetProcAddress(hModule
, "AcquireCredentialsHandleA");
155 if (!_AcquireCredentialsHandle
) {
160 _CompleteAuthToken
= (COMPLETE_AUTH_TOKEN_FN
)
161 GetProcAddress(hModule
, "CompleteAuthToken");
162 if (!_CompleteAuthToken
) {
167 _DeleteSecurityContext
= (DELETE_SECURITY_CONTEXT_FN
)
168 GetProcAddress(hModule
, "DeleteSecurityContext");
169 if (!_DeleteSecurityContext
) {
174 _FreeContextBuffer
= (FREE_CONTEXT_BUFFER_FN
)
175 GetProcAddress(hModule
, "FreeContextBuffer");
176 if (!_FreeContextBuffer
) {
181 _FreeCredentialsHandle
= (FREE_CREDENTIALS_HANDLE_FN
)
182 GetProcAddress(hModule
, "FreeCredentialsHandle");
183 if (!_FreeCredentialsHandle
) {
189 _InitializeSecurityContext
= (INITIALIZE_SECURITY_CONTEXT_FN
)
190 GetProcAddress(hModule
, "InitializeSecurityContextW");
192 _InitializeSecurityContext
= (INITIALIZE_SECURITY_CONTEXT_FN
)
193 GetProcAddress(hModule
, "InitializeSecurityContextA");
195 if (!_InitializeSecurityContext
) {
201 _QuerySecurityPackageInfo
= (QUERY_SECURITY_PACKAGE_INFO_FN
)
202 GetProcAddress(hModule
, "QuerySecurityPackageInfoW");
204 _QuerySecurityPackageInfo
= (QUERY_SECURITY_PACKAGE_INFO_FN
)
205 GetProcAddress(hModule
, "QuerySecurityPackageInfoA");
207 if (!_QuerySecurityPackageInfo
) {
213 _QueryContextAttributes
= (QUERY_CONTEXT_ATTRIBUTES_FN_W
)
214 GetProcAddress(hModule
, "QueryContextAttributesW");
216 _QueryContextAttributes
= (QUERY_CONTEXT_ATTRIBUTES_FN_A
)
217 GetProcAddress(hModule
, "QueryContextAttributesA");
219 if (!_QueryContextAttributes
) {
224 /* Get max token size */
225 _QuerySecurityPackageInfo((SEC_CHAR
*)_T(SSP_Package
), &pSPI
);
226 cbMaxToken
= pSPI
->cbMaxToken
;
227 _FreeContextBuffer(pSPI
);
229 /* Allocate buffers for client and server messages */
230 pClientBuf
= xcalloc(cbMaxToken
, sizeof(char));
231 pServerBuf
= xcalloc(cbMaxToken
, sizeof(char));
232 SSP_Package_InUse
= xstrdup(SSP_Package
);
238 BOOL
GenClientContext(PAUTH_SEQ pAS
, PSEC_WINNT_AUTH_IDENTITY pAuthIdentity
,
239 PVOID pIn
, DWORD cbIn
, PVOID pOut
, PDWORD pcbOut
, PBOOL pfDone
)
242 * Routine Description:
244 * Optionally takes an input buffer coming from the server and returns
245 * a buffer of information to send back to the server. Also returns
246 * an indication of whether or not the context is complete.
249 * Returns TRUE if successful; otherwise FALSE.
252 SecBufferDesc sbdOut
;
258 if (!pAS
->fInitialized
) {
259 SecurityStatus
= _AcquireCredentialsHandle(NULL
, (SEC_CHAR
*) _T(SSP_Package_InUse
),
260 SECPKG_CRED_OUTBOUND
, NULL
, (NTLM_mode
== SSP_NTLM
) ? NULL
: pAuthIdentity
, NULL
, NULL
,
261 &pAS
->hcred
, &tsExpiry
);
262 if (SecurityStatus
< 0)
264 pAS
->fHaveCredHandle
= TRUE
;
267 /* Prepare output buffer */
268 sbdOut
.ulVersion
= 0;
270 sbdOut
.pBuffers
= &sbOut
;
271 sbOut
.cbBuffer
= *pcbOut
;
272 sbOut
.BufferType
= SECBUFFER_TOKEN
;
273 sbOut
.pvBuffer
= pOut
;
275 /* Prepare input buffer */
276 if (pAS
->fInitialized
) {
279 sbdIn
.pBuffers
= &sbIn
;
280 sbIn
.cbBuffer
= cbIn
;
281 sbIn
.BufferType
= SECBUFFER_TOKEN
;
284 SecurityStatus
= _InitializeSecurityContext(&pAS
->hcred
,
285 pAS
->fInitialized
? &pAS
->hctxt
: NULL
, NULL
, 0, 0,
286 SECURITY_NATIVE_DREP
, pAS
->fInitialized
? &sbdIn
: NULL
,
287 0, &pAS
->hctxt
, &sbdOut
, &fContextAttr
, &tsExpiry
);
288 if (SecurityStatus
< 0)
290 pAS
->fHaveCtxtHandle
= TRUE
;
292 /* If necessary, complete token */
293 if (SecurityStatus
== SEC_I_COMPLETE_NEEDED
|| SecurityStatus
== SEC_I_COMPLETE_AND_CONTINUE
) {
294 SecurityStatus
= _CompleteAuthToken(&pAS
->hctxt
, &sbdOut
);
295 if (SecurityStatus
< 0)
298 *pcbOut
= sbOut
.cbBuffer
;
299 if (!pAS
->fInitialized
)
300 pAS
->fInitialized
= TRUE
;
301 *pfDone
= !(SecurityStatus
== SEC_I_CONTINUE_NEEDED
302 || SecurityStatus
== SEC_I_COMPLETE_AND_CONTINUE
);
307 BOOL
GenServerContext(PAUTH_SEQ pAS
, PVOID pIn
, DWORD cbIn
, PVOID pOut
,
308 PDWORD pcbOut
, PBOOL pfDone
, char * credentials
)
311 * Routine Description:
313 * Takes an input buffer coming from the client and returns a buffer
314 * to be sent to the client. Also returns an indication of whether or
315 * not the context is complete.
319 * Returns TRUE if successful; otherwise FALSE.
322 SecBufferDesc sbdOut
;
327 SecPkgContext_Names namebuffer
;
329 if (!pAS
->fInitialized
) {
330 SecurityStatus
= _AcquireCredentialsHandle(NULL
, (SEC_CHAR
*) _T(SSP_Package_InUse
),
331 SECPKG_CRED_INBOUND
, NULL
, NULL
, NULL
, NULL
, &pAS
->hcred
,
332 &pAS
->hcredLifeTime
);
334 fprintf(stderr
, "AcquireCredentialsHandle returned: %x\n", SecurityStatus
);
336 if (SecurityStatus
< 0) {
338 fprintf(stderr
, "AcquireCredentialsHandle failed: %x\n", SecurityStatus
);
342 pAS
->fHaveCredHandle
= TRUE
;
345 /* Prepare output buffer */
346 sbdOut
.ulVersion
= 0;
348 sbdOut
.pBuffers
= &sbOut
;
349 sbOut
.cbBuffer
= *pcbOut
;
350 sbOut
.BufferType
= SECBUFFER_TOKEN
;
351 sbOut
.pvBuffer
= pOut
;
353 /* Prepare input buffer */
356 sbdIn
.pBuffers
= &sbIn
;
357 sbIn
.cbBuffer
= cbIn
;
358 sbIn
.BufferType
= SECBUFFER_TOKEN
;
360 SecurityStatus
= _AcceptSecurityContext(&pAS
->hcred
,
361 pAS
->fInitialized
? &pAS
->hctxt
: NULL
, &sbdIn
, (NTLM_mode
== SSP_NTLM
) ? ASC_REQ_DELEGATE
: 0,
362 SECURITY_NATIVE_DREP
, &pAS
->hctxt
, &sbdOut
, &fContextAttr
,
363 &pAS
->hctxtLifeTime
);
365 fprintf(stderr
, "AcceptSecurityContext returned: %x\n", SecurityStatus
);
367 if (SecurityStatus
< 0) {
369 fprintf(stderr
, "AcceptSecurityContext failed: %x\n", SecurityStatus
);
373 pAS
->fHaveCtxtHandle
= TRUE
;
375 /* If necessary, complete token */
376 if (SecurityStatus
== SEC_I_COMPLETE_NEEDED
|| SecurityStatus
== SEC_I_COMPLETE_AND_CONTINUE
) {
377 SecurityStatus
= _CompleteAuthToken(&pAS
->hctxt
, &sbdOut
);
379 fprintf(stderr
, "CompleteAuthToken returned: %x\n", SecurityStatus
);
381 if (SecurityStatus
< 0) {
383 fprintf(stderr
, "CompleteAuthToken failed: %x\n", SecurityStatus
);
389 if ((credentials
!= NULL
) &&
390 !(SecurityStatus
== SEC_I_CONTINUE_NEEDED
|| SecurityStatus
== SEC_I_COMPLETE_AND_CONTINUE
)) {
391 SecurityStatus
= _QueryContextAttributes(&pAS
->hctxt
, SECPKG_ATTR_NAMES
, &namebuffer
);
393 fprintf(stderr
, "QueryContextAttributes returned: %x\n", SecurityStatus
);
395 if (SecurityStatus
< 0) {
397 fprintf(stderr
, "QueryContextAttributes failed: %x\n", SecurityStatus
);
401 strncpy(credentials
, namebuffer
.sUserName
, SSP_MAX_CRED_LEN
);
404 *pcbOut
= sbOut
.cbBuffer
;
405 if (!pAS
->fInitialized
)
406 pAS
->fInitialized
= TRUE
;
407 *pfDone
= !(SecurityStatus
== SEC_I_CONTINUE_NEEDED
408 || SecurityStatus
== SEC_I_COMPLETE_AND_CONTINUE
);
413 BOOL WINAPI
SSP_LogonUser(PTSTR szUser
, PTSTR szPassword
, PTSTR szDomain
)
415 AUTH_SEQ asServer
= {0};
416 AUTH_SEQ asClient
= {0};
418 BOOL fResult
= FALSE
;
422 SEC_WINNT_AUTH_IDENTITY ai
;
428 /* Initialize auth identity structure */
429 ZeroMemory(&ai
, sizeof(ai
));
430 ai
.Domain
= (void *)szDomain
;
431 ai
.DomainLength
= lstrlen(szDomain
);
432 ai
.User
= (void *)szUser
;
433 ai
.UserLength
= lstrlen(szUser
);
434 ai
.Password
= (void *)szPassword
;
435 ai
.PasswordLength
= lstrlen(szPassword
);
436 #if defined(UNICODE) || defined(_UNICODE)
437 ai
.Flags
= SEC_WINNT_AUTH_IDENTITY_UNICODE
;
439 ai
.Flags
= SEC_WINNT_AUTH_IDENTITY_ANSI
;
442 /* Prepare client message (negotiate) */
444 if (!GenClientContext(&asClient
, &ai
, NULL
, 0, pClientBuf
, &cbOut
, &fDone
))
447 /* Prepare server message (challenge) */
450 if (!GenServerContext(&asServer
, pClientBuf
, cbIn
, pServerBuf
, &cbOut
,
453 /* Most likely failure: AcceptServerContext fails with SEC_E_LOGON_DENIED
454 * in the case of bad szUser or szPassword.
455 * Unexpected Result: Logon will succeed if you pass in a bad szUser and
456 * the guest account is enabled in the specified domain.
459 /* Prepare client message (authenticate) */
462 if (!GenClientContext(&asClient
, &ai
, pServerBuf
, cbIn
, pClientBuf
, &cbOut
,
466 /* Prepare server message (authentication) */
469 if (!GenServerContext(&asServer
, pClientBuf
, cbIn
, pServerBuf
, &cbOut
,
475 /* Clean up resources */
476 if (asClient
.fHaveCtxtHandle
)
477 _DeleteSecurityContext(&asClient
.hctxt
);
478 if (asClient
.fHaveCredHandle
)
479 _FreeCredentialsHandle(&asClient
.hcred
);
480 if (asServer
.fHaveCtxtHandle
)
481 _DeleteSecurityContext(&asServer
.hctxt
);
482 if (asServer
.fHaveCredHandle
)
483 _FreeCredentialsHandle(&asServer
.hcred
);
489 const char * WINAPI
SSP_MakeChallenge(PVOID PNegotiateBuf
, int NegotiateLen
)
492 PVOID fResult
= NULL
;
495 ntlm_challenge
* challenge
;
496 const char * encoded
= NULL
;
498 if (NTLM_asServer
.fHaveCtxtHandle
)
499 _DeleteSecurityContext(&NTLM_asServer
.hctxt
);
500 if (NTLM_asServer
.fHaveCredHandle
)
501 _FreeCredentialsHandle(&NTLM_asServer
.hcred
);
503 NTLM_LocalCall
= FALSE
;
505 memcpy(pClientBuf
, PNegotiateBuf
, NegotiateLen
);
506 ZeroMemory(pServerBuf
, cbMaxToken
);
507 ZeroMemory(&NTLM_asServer
, sizeof(NTLM_asServer
));
512 /* Prepare server message (challenge) */
515 if (!GenServerContext(&NTLM_asServer
, pClientBuf
, cbIn
, pServerBuf
, &cbOut
,
518 fResult
= pServerBuf
;
520 if (fResult
!= NULL
) {
521 challenge
= (ntlm_challenge
*) fResult
;
522 Use_Unicode
= NEGOTIATE_UNICODE
& challenge
->flags
;
523 NTLM_LocalCall
= NEGOTIATE_THIS_IS_LOCAL_CALL
& challenge
->flags
;
524 encoded
= base64_encode_bin((char *) fResult
, cbOut
);
530 BOOL WINAPI
SSP_ValidateNTLMCredentials(PVOID PAutenticateBuf
, int AutenticateLen
, char * credentials
)
533 BOOL fResult
= FALSE
;
537 memcpy(pClientBuf
, PAutenticateBuf
, AutenticateLen
);
538 ZeroMemory(pServerBuf
, cbMaxToken
);
543 /* Prepare server message (authentication) */
544 cbIn
= AutenticateLen
;
546 if (!GenServerContext(&NTLM_asServer
, pClientBuf
, cbIn
, pServerBuf
, &cbOut
,
547 &fDone
, credentials
))
556 const char * WINAPI
SSP_MakeNegotiateBlob(PVOID PNegotiateBuf
, int NegotiateLen
, PBOOL fDone
, int * Status
, char * credentials
)
560 const char * encoded
= NULL
;
562 if (NTLM_asServer
.fHaveCtxtHandle
)
563 _DeleteSecurityContext(&NTLM_asServer
.hctxt
);
564 if (NTLM_asServer
.fHaveCredHandle
)
565 _FreeCredentialsHandle(&NTLM_asServer
.hcred
);
567 memcpy(pClientBuf
, PNegotiateBuf
, NegotiateLen
);
568 ZeroMemory(pServerBuf
, cbMaxToken
);
569 ZeroMemory(&NTLM_asServer
, sizeof(NTLM_asServer
));
574 /* Prepare server message (challenge) */
577 if (!GenServerContext(&NTLM_asServer
, pClientBuf
, cbIn
, pServerBuf
, &cbOut
,
578 fDone
, credentials
)) {
584 if (pServerBuf
!= NULL
&& cbOut
> 0)
585 encoded
= base64_encode_bin((char *) pServerBuf
, cbOut
);
590 const char * WINAPI
SSP_ValidateNegotiateCredentials(PVOID PAutenticateBuf
, int AutenticateLen
, PBOOL fDone
, int * Status
, char * credentials
)
594 const char * encoded
= NULL
;
596 memcpy(pClientBuf
, PAutenticateBuf
, AutenticateLen
);
597 ZeroMemory(pServerBuf
, cbMaxToken
);
602 /* Prepare server message (authentication) */
603 cbIn
= AutenticateLen
;
605 if (!GenServerContext(&NTLM_asServer
, pClientBuf
, cbIn
, pServerBuf
, &cbOut
,
606 fDone
, credentials
)) {
612 if (pServerBuf
!= NULL
&& cbOut
> 0)
613 encoded
= base64_encode_bin((char *) pServerBuf
, cbOut
);