mirror of https://github.com/tongzx/nt5src
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
3275 lines
81 KiB
3275 lines
81 KiB
/*++
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Copyright (c) 1995-1996 Microsoft Corporation
|
|
|
|
Module Name :
|
|
|
|
authent.cxx
|
|
|
|
Abstract:
|
|
Authentication support functions for IIS
|
|
|
|
Author:
|
|
|
|
Murali R. Krishnan ( MuraliK ) 11-Dec-1996
|
|
|
|
Environment:
|
|
User Mode - Win32
|
|
|
|
Project:
|
|
|
|
Internet Server DLL
|
|
|
|
Functions Exported:
|
|
TCP_AUTHENT::*
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
|
|
/************************************************************
|
|
* Include Headers
|
|
************************************************************/
|
|
#include "tcpdllp.hxx"
|
|
#pragma hdrstop
|
|
|
|
#if 1 // DBCS
|
|
#include <mbstring.h>
|
|
#endif
|
|
# include "infosec.hxx"
|
|
#include "TokenAcl.hxx"
|
|
#include <softpub.h>
|
|
#include <wininet.h>
|
|
#include <wincrypt.h>
|
|
|
|
#include <iiscert.hxx>
|
|
#include <iisctl.hxx>
|
|
#include <sslinfo.hxx>
|
|
|
|
#if !defined(CRYPT_E_REVOKED)
|
|
#define CRYPT_E_REVOKED _HRESULT_TYPEDEF_(0x80092010L)
|
|
#endif
|
|
#if !defined(CRYPT_E_NO_REVOCATION_CHECK)
|
|
#define CRYPT_E_NO_REVOCATION_CHECK _HRESULT_TYPEDEF_(0x80092012L)
|
|
#endif
|
|
#if !defined(CRYPT_E_REVOCATION_OFFLINE)
|
|
#define CRYPT_E_REVOCATION_OFFLINE _HRESULT_TYPEDEF_(0x80092013L)
|
|
#endif
|
|
|
|
|
|
BOOL
|
|
IsCertificateVerified(
|
|
PSecPkgContext_RemoteCredentialInfo pspRCI
|
|
);
|
|
extern BOOL g_fCertCheckCA;
|
|
|
|
/************************************************************
|
|
* Functions
|
|
************************************************************/
|
|
|
|
BOOL
|
|
IsCertificateVerified(
|
|
PSecPkgContext_RemoteCredentialInfo pspRCI
|
|
)
|
|
{
|
|
if ( g_fCertCheckCA )
|
|
{
|
|
return ((pspRCI->fFlags & RCRED_STATUS_UNKNOWN_ISSUER) == 0);
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
TCP_AUTHENT::TCP_AUTHENT(
|
|
DWORD AuthFlags
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Constructor for the Authentication class
|
|
Arguments:
|
|
|
|
AuthFlags - One of the TCPAUTH_* flags.
|
|
|
|
--*/
|
|
: _hToken ( NULL ),
|
|
_hSSPToken ( NULL ),
|
|
_hSSPPrimaryToken( NULL ),
|
|
_fHaveCredHandle ( FALSE ),
|
|
_fHaveCtxtHandle ( FALSE ),
|
|
_fClient ( FALSE ),
|
|
_fUUEncodeData ( FALSE ),
|
|
_pDeleteFunction ( NULL ),
|
|
_fBase64 ( FALSE ),
|
|
_fKnownToBeGuest ( FALSE ),
|
|
_pClientCertContext( NULL ),
|
|
_pSslInfo ( NULL ),
|
|
_pServerX509Certificate( NULL ),
|
|
_fCertCheckForRevocation( TRUE ),
|
|
_fCertCheckCacheOnly( FALSE )
|
|
{
|
|
if ( AuthFlags & TCPAUTH_SERVER )
|
|
{
|
|
DBG_ASSERT( !(AuthFlags & TCPAUTH_CLIENT));
|
|
}
|
|
|
|
if ( AuthFlags & TCPAUTH_CLIENT )
|
|
{
|
|
_fClient = TRUE;
|
|
}
|
|
|
|
if ( AuthFlags & TCPAUTH_UUENCODE )
|
|
{
|
|
_fUUEncodeData = TRUE;
|
|
}
|
|
|
|
if ( AuthFlags & TCPAUTH_BASE64 )
|
|
{
|
|
_fBase64 = TRUE;
|
|
}
|
|
|
|
DBG_REQUIRE( Reset( TRUE ) );
|
|
}
|
|
|
|
/*******************************************************************/
|
|
|
|
TCP_AUTHENT::~TCP_AUTHENT(
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Destructor for the Authentication class
|
|
|
|
--*/
|
|
{
|
|
Reset( TRUE );
|
|
}
|
|
|
|
|
|
BOOL
|
|
TCP_AUTHENT::DeleteCachedTokenOnReset(
|
|
VOID
|
|
)
|
|
{
|
|
if ( _hToken != NULL )
|
|
{
|
|
CACHED_TOKEN *pct = (CACHED_TOKEN*)_hToken;
|
|
DBG_ASSERT( _fClearText );
|
|
RemoveTokenFromCache( pct);
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
BOOL
|
|
TCP_AUTHENT::Reset(
|
|
BOOL fSessionReset
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Resets this object in preparation for a brand new conversation
|
|
|
|
--*/
|
|
{
|
|
if ( _hToken != NULL )
|
|
{
|
|
DBG_ASSERT( _fClearText );
|
|
TsDeleteUserToken( _hToken );
|
|
|
|
_hToken = NULL;
|
|
}
|
|
|
|
if ( _hSSPToken )
|
|
{
|
|
////if ( !_pDeleteFunction )
|
|
{
|
|
CloseHandle( _hSSPToken );
|
|
}
|
|
|
|
//
|
|
// Don't delete the SSPI Token as we queried it directly from SSPI
|
|
//
|
|
|
|
_hSSPToken = NULL;
|
|
}
|
|
|
|
//
|
|
// We close this token because we duplicated it from _hSSPToken
|
|
//
|
|
|
|
if ( _hSSPPrimaryToken )
|
|
{
|
|
CloseHandle( _hSSPPrimaryToken );
|
|
_hSSPPrimaryToken = NULL;
|
|
}
|
|
|
|
if ( _pDeleteFunction )
|
|
{
|
|
(_pDeleteFunction)( &_hctxt, _pDeleteArg );
|
|
_pDeleteFunction = NULL;
|
|
_fHaveCtxtHandle = FALSE;
|
|
_fHaveCredHandle = FALSE;
|
|
}
|
|
else
|
|
{
|
|
if ( _fHaveCtxtHandle )
|
|
{
|
|
pfnDeleteSecurityContext( &_hctxt );
|
|
_fHaveCtxtHandle = FALSE;
|
|
}
|
|
|
|
if ( _fHaveCredHandle )
|
|
{
|
|
pfnFreeCredentialsHandle( &_hcred );
|
|
_fHaveCredHandle = FALSE;
|
|
}
|
|
}
|
|
|
|
if ( fSessionReset )
|
|
{
|
|
if ( _pClientCertContext )
|
|
{
|
|
(pfnFreeCertCtxt)( _pClientCertContext );
|
|
_pClientCertContext = NULL;
|
|
}
|
|
if ( _pServerX509Certificate )
|
|
{
|
|
(fnFreeCert)( _pServerX509Certificate );
|
|
_pServerX509Certificate = NULL;
|
|
}
|
|
_phSslCtxt = NULL;
|
|
_fCertCheckForRevocation = TRUE;
|
|
_fCertCheckCacheOnly = FALSE;
|
|
|
|
if ( _pSslInfo )
|
|
{
|
|
IIS_SSL_INFO::Release( _pSslInfo );
|
|
|
|
_pSslInfo = NULL;
|
|
}
|
|
}
|
|
|
|
_fNewConversation = TRUE;
|
|
_fClearText = FALSE;
|
|
_fHaveAccessTokens= FALSE;
|
|
_cbMaxToken = 0;
|
|
_fDelegate = FALSE;
|
|
|
|
_pDeleteArg = NULL;
|
|
_fKnownToBeGuest = FALSE;
|
|
_fHaveExpiry = FALSE;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/*******************************************************************/
|
|
|
|
|
|
BOOL
|
|
TCP_AUTHENT::SetSecurityContextToken(
|
|
CtxtHandle* pCtxt,
|
|
HANDLE hPrimaryToken,
|
|
SECURITY_CONTEXT_DELETE_FUNCTION pFn,
|
|
PVOID pArg,
|
|
IIS_SSL_INFO *pSslInfo
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Store security context & impersonation token
|
|
|
|
Arguments:
|
|
|
|
pCtxt - SSPI security context
|
|
hPrimaryToken - access token associated with security context
|
|
pFn - function to call to notify security context destruction
|
|
pArg - argument for pFn call
|
|
pSslInfo - pointer to SSL info object to be used for this security context
|
|
|
|
Return Value:
|
|
|
|
TRUE on success, FALSE on failure
|
|
|
|
--*/
|
|
{
|
|
if ( hPrimaryToken != NULL )
|
|
{
|
|
if ( _hSSPToken )
|
|
{
|
|
CloseHandle( _hSSPToken );
|
|
_hSSPToken = NULL;
|
|
}
|
|
|
|
if ( _hSSPPrimaryToken )
|
|
{
|
|
CloseHandle( _hSSPPrimaryToken );
|
|
_hSSPPrimaryToken = NULL;
|
|
}
|
|
|
|
AdjustTokenPrivileges( hPrimaryToken, TRUE, NULL, NULL, NULL, NULL );
|
|
if ( g_pTokPrev )
|
|
{
|
|
AdjustTokenPrivileges( hPrimaryToken,
|
|
FALSE,
|
|
g_pTokPrev,
|
|
NULL,
|
|
NULL,
|
|
NULL );
|
|
}
|
|
|
|
//
|
|
// We need to convert the NTLM primary token into a
|
|
// impersonation token
|
|
//
|
|
|
|
if ( !pfnDuplicateTokenEx( hPrimaryToken,
|
|
TOKEN_ALL_ACCESS,
|
|
NULL,
|
|
SecurityImpersonation,
|
|
TokenImpersonation,
|
|
&_hSSPToken ))
|
|
{
|
|
DBGPRINTF(( DBG_CONTEXT,
|
|
"[SetSecurityContextToken] DuplicateToken failed, error %lx\n",
|
|
GetLastError() ));
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
// Bug 86498:
|
|
// Grant all access to the token for "Everyone" so that ISAPIs that run out of proc
|
|
// can do an OpenThreadToken call
|
|
HRESULT hr;
|
|
if (FAILED( hr = GrantAllAccessToToken( _hSSPToken ) ) )
|
|
{
|
|
DBG_ASSERT( FALSE );
|
|
DBGPRINTF(( DBG_CONTEXT,
|
|
"[SetSecurityContextToken] Failed to grant access to the token to everyone, error %lx\n",
|
|
hr ));
|
|
return FALSE;
|
|
}
|
|
|
|
_hSSPPrimaryToken = hPrimaryToken;
|
|
|
|
_hctxt = *pCtxt;
|
|
_fHaveCtxtHandle = TRUE;
|
|
_pDeleteFunction = pFn;
|
|
_pDeleteArg = pArg;
|
|
}
|
|
|
|
_phSslCtxt = pCtxt;
|
|
|
|
if ( _pSslInfo )
|
|
{
|
|
IIS_SSL_INFO::Release( _pSslInfo );
|
|
}
|
|
_pSslInfo = pSslInfo;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
BOOL
|
|
TCP_AUTHENT::IsForwardable(
|
|
VOID
|
|
) const
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
returns TRUE if auth info is forwardable ( e.g. kerberos )
|
|
|
|
Arguments:
|
|
|
|
None
|
|
|
|
Return Value:
|
|
|
|
TRUE if forwardable, otherwise FALSE
|
|
|
|
--*/
|
|
{
|
|
return _fDelegate;
|
|
}
|
|
|
|
|
|
BOOL
|
|
TCP_AUTHENT::IsSslCertPresent(
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Check if SSL cert bound on this security context
|
|
|
|
Arguments:
|
|
|
|
None
|
|
|
|
Return Value:
|
|
|
|
TRUE if SSL cert present, otherwise FALSE
|
|
|
|
--*/
|
|
{
|
|
return _phSslCtxt != NULL;
|
|
}
|
|
|
|
|
|
static BOOL IsValidKeyUsageForClientCert(
|
|
PCCERT_CONTEXT pCliCert
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
private routine checkin extended key usage validity for client certificate
|
|
|
|
Arguments:
|
|
|
|
pCliCert - client certificate to be verified
|
|
|
|
Return Value:
|
|
|
|
TRUE if client cert usage is valid, otherwise FALSE
|
|
in the case of any error FALSE is returned and GetLastError() can be used to
|
|
retrieve the error code
|
|
|
|
--*/
|
|
|
|
{
|
|
HRESULT hr = S_OK;
|
|
BOOL fRet = TRUE;
|
|
DWORD dwRet = ERROR_SUCCESS;
|
|
const DWORD cbDefaultEnhKeyUsage = 100;
|
|
STACK_BUFFER( buffEnhKeyUsage, cbDefaultEnhKeyUsage );
|
|
PCERT_ENHKEY_USAGE pCertEnhKeyUsage = NULL;
|
|
DWORD cbEnhKeyUsage = cbDefaultEnhKeyUsage;
|
|
BOOL fEnablesClientAuth = FALSE;
|
|
|
|
//
|
|
// Now verify extended usage flags (only for the end certificate)
|
|
//
|
|
|
|
fRet = CertGetEnhancedKeyUsage( pCliCert,
|
|
0, //dwFlags,
|
|
(PCERT_ENHKEY_USAGE) buffEnhKeyUsage.QueryPtr(),
|
|
&cbEnhKeyUsage );
|
|
dwRet = GetLastError();
|
|
|
|
if ( !fRet && ( dwRet == ERROR_MORE_DATA ) )
|
|
{
|
|
//
|
|
// Resize buffer
|
|
//
|
|
if ( !buffEnhKeyUsage.Resize( cbEnhKeyUsage ) )
|
|
{
|
|
dwRet = GetLastError();
|
|
goto ExitPoint;
|
|
}
|
|
fRet = CertGetEnhancedKeyUsage( pCliCert,
|
|
0, //dwFlags,
|
|
(PCERT_ENHKEY_USAGE) buffEnhKeyUsage.QueryPtr(),
|
|
&cbEnhKeyUsage );
|
|
dwRet = GetLastError();
|
|
}
|
|
if ( !fRet )
|
|
{
|
|
//
|
|
// Bad. Couldn't get the Enhanced Key Usage
|
|
//
|
|
|
|
goto ExitPoint;
|
|
}
|
|
|
|
pCertEnhKeyUsage = (PCERT_ENHKEY_USAGE) buffEnhKeyUsage.QueryPtr();
|
|
|
|
//
|
|
// If the cUsageIdentifier member is zero (0), the certificate might be valid
|
|
// for all uses or the certificate it might have no valid uses. The return from
|
|
// a call to GetLastError can be used to determine whether the certificate is
|
|
// good for all uses or for no uses. If GetLastError returns CRYPT_E_NOT_FOUND
|
|
// if the certificate is good for all uses. If it returns zero (0), the
|
|
// certificate has no valid uses
|
|
//
|
|
|
|
if ( pCertEnhKeyUsage->cUsageIdentifier == 0 )
|
|
{
|
|
if ( dwRet == CRYPT_E_NOT_FOUND )
|
|
{
|
|
//
|
|
// Certificate valid for any use
|
|
//
|
|
fEnablesClientAuth = TRUE;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Certificate NOT valid for any use
|
|
//
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Find out if pCertEnhKeyUsage enables CLIENT_AUTH
|
|
//
|
|
for ( DWORD i = 0; i < pCertEnhKeyUsage->cUsageIdentifier; i++ )
|
|
{
|
|
DBG_ASSERT( pCertEnhKeyUsage->rgpszUsageIdentifier[i] != NULL );
|
|
|
|
if ( strcmp( pCertEnhKeyUsage->rgpszUsageIdentifier[i], szOID_PKIX_KP_CLIENT_AUTH ) == 0 )
|
|
{
|
|
//
|
|
// certificate enables CLIENT_AUTH
|
|
//
|
|
fEnablesClientAuth = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// If ExtendedKeyUsage doesn't enable CLIENT_AUTH then add flag to CertFlags
|
|
//
|
|
ExitPoint:
|
|
return fEnablesClientAuth;
|
|
}
|
|
|
|
|
|
BOOL
|
|
TCP_AUTHENT::QueryCertificateInfo(
|
|
LPBOOL pfNoCert
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Get certificate information from SSL security context
|
|
|
|
Arguments:
|
|
|
|
pfNoCert - updated with TRUE if no cert
|
|
|
|
Return Value:
|
|
|
|
TRUE on success, FALSE on failure
|
|
|
|
--*/
|
|
{
|
|
SECURITY_STATUS ss;
|
|
CERT_REVOCATION_STATUS CertRevocationStatus;
|
|
SecPkgContext_RemoteCredentialInfo spRCI;
|
|
HCERTCHAINENGINE hEngine = NULL;
|
|
PCCERT_CHAIN_CONTEXT pCertChain = NULL;
|
|
|
|
if ( _pClientCertContext != NULL )
|
|
{
|
|
*pfNoCert = FALSE;
|
|
return TRUE;
|
|
}
|
|
|
|
//
|
|
// NULL handle <==> no certificate
|
|
//
|
|
|
|
if ( _phSslCtxt == NULL || pfnFreeCertCtxt == NULL )
|
|
{
|
|
goto LNoCertificate;
|
|
}
|
|
|
|
//
|
|
// Win95 <==> no certificate
|
|
// NOTE Currently security.dll is not supported in Win95.
|
|
//
|
|
|
|
if ( TsIsWindows95() )
|
|
{
|
|
goto LNoCertificate;
|
|
}
|
|
|
|
//
|
|
// get cert context - if null, we have no certificate
|
|
//
|
|
|
|
DBG_ASSERT( RtlValidateProcessHeaps() );
|
|
|
|
ss = (pfnQueryContextAttributes)( _phSslCtxt,
|
|
SECPKG_ATTR_REMOTE_CERT_CONTEXT,
|
|
&_pClientCertContext );
|
|
|
|
|
|
DBG_ASSERT( RtlValidateProcessHeaps() );
|
|
|
|
if ( ss || _pClientCertContext == NULL ) {
|
|
|
|
goto LNoCertificate;
|
|
}
|
|
|
|
|
|
//
|
|
// Try to verify the client certificate
|
|
//
|
|
|
|
#if DBG
|
|
CHAR szSubjectName[1024];
|
|
if ( CertGetNameString( _pClientCertContext,
|
|
CERT_NAME_SIMPLE_DISPLAY_TYPE,
|
|
0,
|
|
NULL,
|
|
szSubjectName,
|
|
1024 ) )
|
|
{
|
|
DBGPRINTF((DBG_CONTEXT,
|
|
"Verifying client cert for %s \n",
|
|
szSubjectName));
|
|
}
|
|
|
|
if ( CertGetNameString( _pClientCertContext,
|
|
CERT_NAME_SIMPLE_DISPLAY_TYPE,
|
|
CERT_NAME_ISSUER_FLAG,
|
|
NULL,
|
|
szSubjectName,
|
|
1024 ) )
|
|
{
|
|
DBGPRINTF((DBG_CONTEXT,
|
|
"Client cert issued by %s \n",
|
|
szSubjectName));
|
|
}
|
|
#endif
|
|
|
|
_dwX509Flags = RCRED_CRED_EXISTS;
|
|
|
|
if ( _pSslInfo && _pSslInfo->GetCertChainEngine( &hEngine ) )
|
|
{
|
|
//
|
|
// Don't set any usage bits, because there are probably lots of certs floating
|
|
// around out there with no usage bits and we don't want to break them
|
|
//
|
|
// CODEWORK : might want to consider adding eg MB flag that allows setting of
|
|
// required usage bits on certs
|
|
//
|
|
DWORD dwRevocationFlags = 0;
|
|
DWORD dwCacheFlags = 0;
|
|
CERT_CHAIN_PARA CCP;
|
|
memset( &CCP, 0, sizeof( CCP ) );
|
|
CCP.cbSize = sizeof(CCP);
|
|
|
|
DBG_ASSERT( RtlValidateProcessHeaps() );
|
|
|
|
//
|
|
// Determine the CertGetCertificateChain() flags related to revocation
|
|
// checking and cache retrieval
|
|
//
|
|
|
|
dwRevocationFlags = ( _fCertCheckForRevocation
|
|
|| g_fCertCheckForRevocation )
|
|
? CERT_CHAIN_REVOCATION_CHECK_CHAIN : 0;
|
|
|
|
dwCacheFlags = _fCertCheckCacheOnly
|
|
? CERT_CHAIN_REVOCATION_CHECK_CACHE_ONLY : 0;
|
|
|
|
if ( CertGetCertificateChain( hEngine,
|
|
_pClientCertContext,
|
|
NULL,
|
|
_pClientCertContext->hCertStore,
|
|
&CCP,
|
|
dwRevocationFlags | dwCacheFlags,
|
|
NULL,
|
|
&pCertChain ) )
|
|
{
|
|
//
|
|
// Got a chain, look at the status bits and see whether we like it
|
|
//
|
|
DWORD dwChainStatus = pCertChain->TrustStatus.dwErrorStatus;
|
|
|
|
if ( ( dwChainStatus & CERT_TRUST_IS_NOT_TIME_VALID ) ||
|
|
( dwChainStatus & CERT_TRUST_CTL_IS_NOT_TIME_VALID ) )
|
|
{
|
|
DBGPRINTF((DBG_CONTEXT,
|
|
"Cert/CTL is not time-valid\n"));
|
|
|
|
_dwX509Flags |= CRED_STATUS_INVALID_TIME;
|
|
}
|
|
|
|
|
|
if ( ( dwChainStatus & CERT_TRUST_IS_UNTRUSTED_ROOT ) ||
|
|
( dwChainStatus & CERT_TRUST_IS_PARTIAL_CHAIN ) )
|
|
{
|
|
//
|
|
// If we sign our CTLs, then we should be able to construct a complete
|
|
// chain up to a trusted root for valid client certs, so we just look at
|
|
// the status bits of the chain.
|
|
//
|
|
// If our CTLs are not signed, we have to manually check whether the cert at the
|
|
// top of the chain is in our CTL
|
|
//
|
|
|
|
#if SIGNED_CTL
|
|
DBGPRINTF((DBG_CONTEXT,
|
|
"Cert doesn't chain up to a trusted root\n"));
|
|
|
|
_dwX509Flags |= RCRED_STATUS_UNKNOWN_ISSUER;
|
|
|
|
#else //SIGNED_CTL
|
|
|
|
PCERT_SIMPLE_CHAIN pSimpleChain = pCertChain->rgpChain[pCertChain->cChain - 1];
|
|
|
|
PCERT_CHAIN_ELEMENT pChainElement =
|
|
pSimpleChain->rgpElement[pSimpleChain->cElement - 1];
|
|
|
|
PCCERT_CONTEXT pChainTop = pChainElement->pCertContext;
|
|
|
|
BOOL fContains = FALSE;
|
|
|
|
if ( !_pSslInfo->CTLContainsCert( pChainTop,
|
|
&fContains ) ||
|
|
!fContains )
|
|
{
|
|
_dwX509Flags |= RCRED_STATUS_UNKNOWN_ISSUER;
|
|
}
|
|
#endif //SIGNED_CTL
|
|
}
|
|
|
|
if ( ( dwChainStatus & CERT_TRUST_IS_NOT_SIGNATURE_VALID ) ||
|
|
( dwChainStatus & CERT_TRUST_CTL_IS_NOT_SIGNATURE_VALID ) ||
|
|
( !IsValidKeyUsageForClientCert(_pClientCertContext) ) )
|
|
{
|
|
DBGPRINTF((DBG_CONTEXT,
|
|
"Cert/CTL is not signature valid\n"));
|
|
//
|
|
// if the signature has been tampered with, we'll just treat it
|
|
// as an unknown issuer
|
|
//
|
|
_dwX509Flags |= RCRED_STATUS_UNKNOWN_ISSUER;
|
|
}
|
|
|
|
if ( dwChainStatus & CERT_TRUST_CTL_IS_NOT_VALID_FOR_USAGE )
|
|
{
|
|
DBGPRINTF((DBG_CONTEXT,
|
|
"CTL isn't valid for usage\n"));
|
|
}
|
|
|
|
//
|
|
// If we were supposed to check for revocation and we couldn't do so,
|
|
// treat it as if it were revoked ... ?
|
|
//
|
|
|
|
if ( _fCertCheckForRevocation || g_fCertCheckForRevocation )
|
|
{
|
|
BOOL fRevoke = FALSE;
|
|
|
|
if ( dwChainStatus & CERT_TRUST_IS_REVOKED )
|
|
{
|
|
fRevoke = TRUE;
|
|
}
|
|
else if ( dwChainStatus & CERT_TRUST_REVOCATION_STATUS_UNKNOWN )
|
|
{
|
|
PCERT_SIMPLE_CHAIN pSimpleChain;
|
|
PCERT_CHAIN_ELEMENT pChainElement;
|
|
PCERT_REVOCATION_INFO pRevocationInfo;
|
|
DWORD dwCounter;
|
|
|
|
pSimpleChain = pCertChain->rgpChain[ 0 ];
|
|
|
|
for ( dwCounter = 0;
|
|
dwCounter < pSimpleChain->cElement;
|
|
dwCounter++ )
|
|
{
|
|
pChainElement = pSimpleChain->rgpElement[ dwCounter ];
|
|
pRevocationInfo = pChainElement->pRevocationInfo;
|
|
|
|
if ( pRevocationInfo &&
|
|
( ( pRevocationInfo->dwRevocationResult == CRYPT_E_REVOKED ) ||
|
|
( pRevocationInfo->dwRevocationResult != CRYPT_E_NO_REVOCATION_CHECK ) ) )
|
|
{
|
|
fRevoke = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( fRevoke )
|
|
{
|
|
DBGPRINTF(( DBG_CONTEXT,
|
|
"'Certificate revoked' or 'CRL needed but not found'\n" ));
|
|
|
|
_dwX509Flags |= CRED_STATUS_REVOKED;
|
|
}
|
|
}
|
|
|
|
CERT_CHAIN_POLICY_STATUS ChainPolicyStatus = {0};
|
|
ChainPolicyStatus.cbSize = sizeof(ChainPolicyStatus);
|
|
|
|
if ( CertVerifyCertificateChainPolicy(CERT_CHAIN_POLICY_BASIC_CONSTRAINTS,
|
|
pCertChain, NULL, &ChainPolicyStatus) )
|
|
{
|
|
if ( ChainPolicyStatus.dwError != NOERROR )
|
|
_dwX509Flags |= RCRED_STATUS_UNKNOWN_ISSUER;
|
|
}
|
|
else
|
|
{
|
|
DBGPRINTF((DBG_CONTEXT,
|
|
"Couldn't verify certificate chain basic constraints : 0x%d\n",
|
|
GetLastError()));
|
|
|
|
_dwX509Flags |= RCRED_STATUS_UNKNOWN_ISSUER;
|
|
}
|
|
|
|
CertFreeCertificateChain( pCertChain );
|
|
pCertChain = NULL;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// We don't know anything about the client cert, so treat it as being
|
|
// issued by an unknown CA
|
|
//
|
|
DBGPRINTF((DBG_CONTEXT,
|
|
"Couldn't get certificate chain : 0x%d\n",
|
|
GetLastError()));
|
|
|
|
_dwX509Flags |= RCRED_STATUS_UNKNOWN_ISSUER;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// We don't know anything about the client cert, so treat it as being issued by
|
|
// an unknown CA
|
|
//
|
|
|
|
DBGPRINTF((DBG_CONTEXT,
|
|
"Can't check anything about the client cert\n"));
|
|
|
|
_dwX509Flags |= RCRED_STATUS_UNKNOWN_ISSUER;
|
|
}
|
|
|
|
DBG_ASSERT( RtlValidateProcessHeaps() );
|
|
|
|
*pfNoCert = FALSE;
|
|
|
|
return TRUE;
|
|
|
|
LNoCertificate:
|
|
|
|
*pfNoCert = TRUE;
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
|
|
BOOL
|
|
TCP_AUTHENT::QueryServerCertificateInfo(
|
|
LPBOOL pfNoCert
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Get servercertificate information from SSL security context
|
|
|
|
Arguments:
|
|
|
|
pfNoCert - updated with TRUE if no cert
|
|
|
|
Return Value:
|
|
|
|
TRUE on success, FALSE on failure
|
|
|
|
--*/
|
|
{
|
|
SECURITY_STATUS ss;
|
|
_SecPkgContext_LocalCredentialInfo spcRCI;
|
|
|
|
if ( _pServerX509Certificate == NULL )
|
|
{
|
|
if ( _phSslCtxt == NULL
|
|
|| fnCrackCert == NULL
|
|
|| fnFreeCert == NULL )
|
|
{
|
|
*pfNoCert = TRUE;
|
|
return FALSE;
|
|
}
|
|
|
|
ss = pfnQueryContextAttributes( _phSslCtxt,
|
|
SECPKG_ATTR_LOCAL_CRED,
|
|
&spcRCI );
|
|
|
|
if ( ss != STATUS_SUCCESS || !spcRCI.cCertificates )
|
|
{
|
|
*pfNoCert = FALSE;
|
|
SetLastError( ss );
|
|
return FALSE;
|
|
}
|
|
|
|
if ( !(fnCrackCert)( spcRCI.pbCertificateChain,
|
|
spcRCI.cbCertificateChain,
|
|
0,
|
|
&_pServerX509Certificate ) )
|
|
{
|
|
pfnFreeContextBuffer(spcRCI.pbCertificateChain);
|
|
|
|
*pfNoCert = FALSE;
|
|
return FALSE;
|
|
}
|
|
_dwServerX509Flags = spcRCI.fFlags;
|
|
_dwServerBitsInKey = spcRCI.dwBits;
|
|
|
|
pfnFreeContextBuffer(spcRCI.pbCertificateChain);
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
BOOL
|
|
TCP_AUTHENT::QueryEncryptionKeySize(
|
|
LPDWORD pdw,
|
|
LPBOOL pfNoCert
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Get encryption session key size
|
|
|
|
Arguments:
|
|
|
|
pdw - updated with number of bits in key size
|
|
pfNoCert - updated with TRUE if no cert
|
|
|
|
Return Value:
|
|
|
|
TRUE on success, FALSE on failure
|
|
|
|
--*/
|
|
{
|
|
SecPkgContext_KeyInfo ski;
|
|
SECURITY_STATUS ss;
|
|
|
|
if ( _phSslCtxt == NULL )
|
|
{
|
|
*pfNoCert = TRUE;
|
|
return FALSE;
|
|
}
|
|
|
|
ss = pfnQueryContextAttributes( _phSslCtxt,
|
|
SECPKG_ATTR_KEY_INFO,
|
|
&ski );
|
|
|
|
if ( ss != STATUS_SUCCESS )
|
|
{
|
|
SetLastError( ss );
|
|
*pfNoCert = FALSE;
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
*pdw = ski.KeySize;
|
|
|
|
if ( ski.sSignatureAlgorithmName )
|
|
{
|
|
pfnFreeContextBuffer( ski.sSignatureAlgorithmName );
|
|
}
|
|
|
|
if ( ski.sEncryptAlgorithmName )
|
|
{
|
|
pfnFreeContextBuffer( ski.sEncryptAlgorithmName );
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
BOOL
|
|
TCP_AUTHENT::QueryEncryptionServerPrivateKeySize(
|
|
LPDWORD pdw,
|
|
LPBOOL pfNoCert
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Get encryption server private key size
|
|
|
|
Arguments:
|
|
|
|
pdw - updated with number of bits in key size
|
|
pfNoCert - updated with TRUE if no cert
|
|
|
|
Return Value:
|
|
|
|
TRUE on success, FALSE on failure
|
|
|
|
--*/
|
|
{
|
|
if ( QueryServerCertificateInfo( pfNoCert ) )
|
|
{
|
|
*pdw = _dwServerBitsInKey;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
BOOL
|
|
TCP_AUTHENT::QueryServerCertificateIssuer(
|
|
LPSTR* ppIssuer,
|
|
LPBOOL pfNoCert
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Get server certificate Issuer information from SSL security context
|
|
|
|
Arguments:
|
|
|
|
ppIssuer - updated with ptr to Issuer name
|
|
guaranteed to remain valid until auth Reset()
|
|
pfNoCert - updated with TRUE if no cert
|
|
|
|
Return Value:
|
|
|
|
TRUE on success, FALSE on failure
|
|
|
|
--*/
|
|
{
|
|
if ( QueryServerCertificateInfo( pfNoCert ) )
|
|
{
|
|
*ppIssuer = _pServerX509Certificate->pszIssuer;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
BOOL
|
|
TCP_AUTHENT::QueryServerCertificateSubject(
|
|
LPSTR* ppSubject,
|
|
LPBOOL pfNoCert
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Get server certificate Subject information from SSL security context
|
|
|
|
Arguments:
|
|
|
|
ppSubject - updated with ptr to Subject name
|
|
guaranteed to remain valid until auth Reset()
|
|
pfNoCert - updated with TRUE if no cert
|
|
|
|
Return Value:
|
|
|
|
TRUE on success, FALSE on failure
|
|
|
|
--*/
|
|
{
|
|
if ( QueryServerCertificateInfo( pfNoCert ) )
|
|
{
|
|
*ppSubject = _pServerX509Certificate->pszSubject;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
BOOL
|
|
TCP_AUTHENT::QueryCertificateIssuer(
|
|
LPSTR pIssuer,
|
|
DWORD dwIssuerMaxLen,
|
|
LPBOOL pfNoCert
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Get certificate Issuer information from SSL security context
|
|
|
|
Arguments:
|
|
|
|
pIssuer - ptr to buffer filled with Issuer name
|
|
dwIssuerMaxLen - size of pIssuer
|
|
pfNoCert - updated with TRUE if no cert
|
|
|
|
Return Value:
|
|
|
|
TRUE on success, FALSE on failure
|
|
|
|
--*/
|
|
{
|
|
if ( QueryCertificateInfo( pfNoCert) && pfnCertNameToStrA )
|
|
{
|
|
pfnCertNameToStrA( _pClientCertContext->dwCertEncodingType,
|
|
&_pClientCertContext->pCertInfo->Issuer,
|
|
CERT_X500_NAME_STR,
|
|
pIssuer,
|
|
dwIssuerMaxLen );
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
BOOL
|
|
TCP_AUTHENT::QueryCertificateFlags(
|
|
LPDWORD pdwFlags,
|
|
LPBOOL pfNoCert
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Get certificate flags information from SSL security context
|
|
|
|
Arguments:
|
|
|
|
pdwFlags - updated with certificate flags
|
|
pfNoCert - updated with TRUE if no cert
|
|
|
|
Return Value:
|
|
|
|
TRUE on success, FALSE on failure
|
|
|
|
--*/
|
|
{
|
|
if ( QueryCertificateInfo( pfNoCert ) )
|
|
{
|
|
*pdwFlags = _dwX509Flags;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
BOOL
|
|
TCP_AUTHENT::QueryCertificateSubject(
|
|
LPSTR pSubject,
|
|
DWORD dwSubjectMaxLen,
|
|
LPBOOL pfNoCert
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Get certificate Subject information from SSL security context
|
|
|
|
Arguments:
|
|
|
|
ppSubject - updated with ptr to Subject name
|
|
guaranteed to remain valid until auth Reset()
|
|
pfNoCert - updated with TRUE if no cert
|
|
|
|
Return Value:
|
|
|
|
TRUE on success, FALSE on failure
|
|
|
|
--*/
|
|
{
|
|
if ( QueryCertificateInfo( pfNoCert) && pfnCertNameToStrA )
|
|
{
|
|
pfnCertNameToStrA( _pClientCertContext->dwCertEncodingType,
|
|
&_pClientCertContext->pCertInfo->Subject,
|
|
CERT_X500_NAME_STR,
|
|
pSubject,
|
|
dwSubjectMaxLen );
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
BOOL
|
|
TCP_AUTHENT::QueryCertificateSerialNumber(
|
|
LPBYTE* ppSerialNumber,
|
|
LPDWORD pdwLen,
|
|
LPBOOL pfNoCert
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Get certificate serial number information from SSL security context
|
|
|
|
Arguments:
|
|
|
|
ppSerialNumber - updated with ptr to serial number as
|
|
array of bytes
|
|
guaranteed to remain valid until auth Reset()
|
|
pdwLen - length of serial number
|
|
pfNoCert - updated with TRUE if no cert
|
|
|
|
Return Value:
|
|
|
|
TRUE on success, FALSE on failure
|
|
|
|
--*/
|
|
{
|
|
if ( QueryCertificateInfo( pfNoCert ) )
|
|
{
|
|
*ppSerialNumber = _pClientCertContext->pCertInfo->SerialNumber.pbData;
|
|
|
|
*pdwLen = _pClientCertContext->pCertInfo->SerialNumber.cbData;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
HANDLE
|
|
TCP_AUTHENT::QueryPrimaryToken(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Returns a non-impersonated token suitable for use with CreateProcessAsUser
|
|
|
|
--*/
|
|
{
|
|
SECURITY_STATUS sc;
|
|
|
|
if ( _hToken && _fClearText )
|
|
{
|
|
return CTO_TO_TOKEN( _hToken );
|
|
}
|
|
else if ( _fHaveAccessTokens )
|
|
{
|
|
return _hSSPPrimaryToken;
|
|
}
|
|
else if ( _fHaveCtxtHandle )
|
|
{
|
|
if ( !_hSSPPrimaryToken )
|
|
{
|
|
if ( !_hSSPToken )
|
|
{
|
|
sc = pfnQuerySecurityContextToken( &_hctxt,
|
|
&_hSSPToken );
|
|
|
|
if ( !NT_SUCCESS( sc ))
|
|
{
|
|
DBGPRINTF(( DBG_CONTEXT,
|
|
"[QueryPrimaryToken] QuerySecurityContext failed, error 0x%lx\n",
|
|
sc ));
|
|
|
|
SetLastError( sc );
|
|
|
|
return NULL;
|
|
}
|
|
|
|
// Bug 86498:
|
|
// Grant all access to the token for "Everyone" so that ISAPIs that run out of proc
|
|
// can do an OpenThreadToken call
|
|
HRESULT hr;
|
|
if (FAILED( hr = GrantAllAccessToToken( _hSSPToken ) ) )
|
|
{
|
|
DBG_ASSERT( FALSE );
|
|
DBGPRINTF(( DBG_CONTEXT,
|
|
"[QueryPrimaryToken] Failed to grant access to the token to everyone, error %lx\n",
|
|
hr ));
|
|
return FALSE;
|
|
}
|
|
|
|
AdjustTokenPrivileges( _hSSPToken, TRUE, NULL, NULL, NULL, NULL );
|
|
if ( g_pTokPrev )
|
|
{
|
|
AdjustTokenPrivileges( _hSSPToken,
|
|
FALSE,
|
|
g_pTokPrev,
|
|
NULL,
|
|
NULL,
|
|
NULL );
|
|
}
|
|
}
|
|
|
|
//
|
|
// We need to convert the NTLM impersonation token into a
|
|
// primary token
|
|
//
|
|
|
|
if ( !pfnDuplicateTokenEx( _hSSPToken,
|
|
TOKEN_ALL_ACCESS,
|
|
NULL,
|
|
SecurityImpersonation,
|
|
TokenPrimary,
|
|
&_hSSPPrimaryToken ))
|
|
{
|
|
DBGPRINTF(( DBG_CONTEXT,
|
|
"[QueryPrimaryToken] DuplicateToken failed, error %lx\n",
|
|
GetLastError() ));
|
|
}
|
|
}
|
|
|
|
return _hSSPPrimaryToken;
|
|
}
|
|
|
|
SetLastError( ERROR_INVALID_HANDLE );
|
|
return NULL;
|
|
}
|
|
|
|
HANDLE
|
|
TCP_AUTHENT::QueryImpersonationToken(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Returns an impersonation token for use with APIs like AccessCheck.
|
|
|
|
--*/
|
|
{
|
|
SECURITY_STATUS sc;
|
|
|
|
if ( _hToken == (TS_TOKEN)BOGUS_WIN95_TOKEN ) {
|
|
return((HANDLE)BOGUS_WIN95_TOKEN);
|
|
}
|
|
|
|
if ( _hToken && _fClearText )
|
|
{
|
|
return ((CACHED_TOKEN *) _hToken)->QueryImpersonationToken();
|
|
}
|
|
else if ( _fHaveAccessTokens )
|
|
{
|
|
return _hSSPToken;
|
|
}
|
|
else if ( _fHaveCtxtHandle )
|
|
{
|
|
//
|
|
// We don't need to impersonate since this is already an impersonation
|
|
// token
|
|
//
|
|
|
|
if ( !_hSSPToken )
|
|
{
|
|
sc = pfnQuerySecurityContextToken( &_hctxt,
|
|
&_hSSPToken );
|
|
|
|
if ( !NT_SUCCESS( sc ))
|
|
{
|
|
DBGPRINTF(( DBG_CONTEXT,
|
|
"[QueryImpersonationToken] QuerySecurityContext failed, error 0x%lx\n",
|
|
sc ));
|
|
|
|
SetLastError( sc );
|
|
|
|
return NULL;
|
|
}
|
|
|
|
// Bug 86498:
|
|
// Grant all access to the token for "Everyone" so that ISAPIs that run out of proc
|
|
// can do an OpenThreadToken call
|
|
HRESULT hr;
|
|
if (FAILED( hr = GrantAllAccessToToken( _hSSPToken ) ) )
|
|
{
|
|
DBG_ASSERT( FALSE );
|
|
DBGPRINTF(( DBG_CONTEXT,
|
|
"[QueryImpersonationToken] Failed to grant access to the token to everyone, error %lx\n",
|
|
hr ));
|
|
return NULL;
|
|
}
|
|
|
|
AdjustTokenPrivileges( _hSSPToken, TRUE, NULL, NULL, NULL, NULL );
|
|
if ( g_pTokPrev )
|
|
{
|
|
AdjustTokenPrivileges( _hSSPToken,
|
|
FALSE,
|
|
g_pTokPrev,
|
|
NULL,
|
|
NULL,
|
|
NULL );
|
|
}
|
|
}
|
|
|
|
return _hSSPToken;
|
|
}
|
|
|
|
SetLastError( ERROR_INVALID_HANDLE );
|
|
return NULL;
|
|
}
|
|
|
|
|
|
BOOL
|
|
TCP_AUTHENT::IsGuest(
|
|
BOOL fIsImpersonated
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Returns TRUE if the account is the guest account
|
|
|
|
--*/
|
|
{
|
|
fIsImpersonated; // Unreferenced variable
|
|
|
|
if ( _fHaveCtxtHandle )
|
|
{
|
|
return _fKnownToBeGuest;
|
|
}
|
|
|
|
return IsGuestUser( GetUserHandle() );
|
|
}
|
|
|
|
BOOL TCP_AUTHENT::EnumAuthPackages(
|
|
BUFFER * pBuff
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Places a double null terminated list of authentication packages on the
|
|
system in pBuff that looks like:
|
|
|
|
NTLM\0
|
|
MSKerberos\0
|
|
Netware\0
|
|
\0
|
|
|
|
Arguments:
|
|
|
|
pBuff - Buffer to receive list
|
|
|
|
Return Value:
|
|
|
|
TRUE if successful, FALSE otherwise (call GetLastError)
|
|
|
|
--*/
|
|
{
|
|
|
|
SECURITY_STATUS ss;
|
|
PSecPkgInfo pPackageInfo = NULL;
|
|
ULONG cPackages;
|
|
ULONG i;
|
|
ULONG fCaps;
|
|
DWORD cbBuffNew = 0;
|
|
DWORD cbBuffOld = 0;
|
|
|
|
|
|
if ( !pBuff->Resize( 64 ) )
|
|
return FALSE;
|
|
|
|
//
|
|
// Get the list of security packages on this machine
|
|
//
|
|
|
|
ss = pfnEnumerateSecurityPackages( &cPackages,
|
|
&pPackageInfo );
|
|
|
|
if ( ss != STATUS_SUCCESS )
|
|
{
|
|
DBGPRINTF(( DBG_CONTEXT,
|
|
"[EnumAuthPackages] Failed with error %d\n",
|
|
ss ));
|
|
|
|
SetLastError( ERROR_NOT_ENOUGH_MEMORY );
|
|
return FALSE;
|
|
}
|
|
|
|
for ( i = 0; i < cPackages ; i++ )
|
|
{
|
|
//
|
|
// We'll only use the security package if it supports connection
|
|
// oriented security and it supports the appropriate side (client
|
|
// or server)
|
|
//
|
|
|
|
fCaps = pPackageInfo[i].fCapabilities;
|
|
|
|
if ( fCaps & SECPKG_FLAG_CONNECTION )
|
|
{
|
|
if ( (fCaps & SECPKG_FLAG_CLIENT_ONLY) && !_fClient )
|
|
continue;
|
|
|
|
cbBuffNew += strlen( pPackageInfo[i].Name ) + 1;
|
|
|
|
if ( pBuff->QuerySize() < cbBuffNew )
|
|
{
|
|
if ( !pBuff->Resize( cbBuffNew + 64 ))
|
|
{
|
|
pfnFreeContextBuffer( pPackageInfo );
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
strcpy( (CHAR *)pBuff->QueryPtr() + cbBuffOld,
|
|
pPackageInfo[i].Name );
|
|
|
|
cbBuffOld = cbBuffNew;
|
|
}
|
|
}
|
|
|
|
*((CHAR *)pBuff->QueryPtr() + cbBuffOld) = '\0';
|
|
|
|
pfnFreeContextBuffer( pPackageInfo );
|
|
|
|
return TRUE;
|
|
|
|
} // EnumAuthPackages
|
|
|
|
|
|
BOOL
|
|
TCP_AUTHENT::QueryExpiry(
|
|
PTimeStamp pExpiry
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Queries the expiration date/time for a SSPI logon
|
|
|
|
Arguments:
|
|
|
|
pExpiry - ptr to buffer to update with expiration date
|
|
|
|
Return Value:
|
|
|
|
TRUE if successful, FALSE if not available
|
|
|
|
--*/
|
|
{
|
|
SECURITY_STATUS ss;
|
|
SecPkgContext_PasswordExpiry speExpiry;
|
|
|
|
if ( _fHaveCtxtHandle && !_pDeleteFunction )
|
|
{
|
|
ss = pfnQueryContextAttributes( &_hctxt,
|
|
SECPKG_ATTR_PASSWORD_EXPIRY,
|
|
&speExpiry );
|
|
|
|
if ( ss != STATUS_SUCCESS )
|
|
{
|
|
((LARGE_INTEGER*)pExpiry)->HighPart = 0x7fffffff;
|
|
((LARGE_INTEGER*)pExpiry)->LowPart = 0xffffffff;
|
|
SetLastError( ss );
|
|
return FALSE;
|
|
}
|
|
|
|
memcpy( pExpiry,
|
|
&speExpiry.tsPasswordExpires,
|
|
sizeof(speExpiry.tsPasswordExpires) );
|
|
|
|
return TRUE;
|
|
}
|
|
else if ( _fHaveExpiry )
|
|
{
|
|
memcpy( pExpiry, (LPVOID)&_liPwdExpiry, sizeof(_liPwdExpiry) );
|
|
return TRUE;
|
|
}
|
|
else
|
|
{
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
|
|
BOOL
|
|
TCP_AUTHENT::QueryFullyQualifiedUserName(
|
|
LPSTR pszUser,
|
|
STR * pstrU,
|
|
PIIS_SERVER_INSTANCE psi,
|
|
PTCP_AUTHENT_INFO pTAI
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Get fully qualified user name ( domain \ user name )
|
|
|
|
Arguments:
|
|
|
|
pszUser - user name ( prefixed by optional domain )
|
|
strU - string updated with fully qualified name
|
|
psi - server instance data
|
|
|
|
Return Value:
|
|
|
|
TRUE if successful, FALSE otherwise
|
|
|
|
--*/
|
|
{
|
|
CHAR szDomainAndUser[IIS_DNLEN+UNLEN+2];
|
|
|
|
//
|
|
// Empty user defaults to the anonymous user
|
|
//
|
|
|
|
if ( !pszUser || *pszUser == '\0' )
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Validate parameters & state.
|
|
//
|
|
|
|
if ( strlen( pszUser ) >= sizeof( szDomainAndUser ) )
|
|
{
|
|
SetLastError( ERROR_INSUFFICIENT_BUFFER );
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Save a copy of the domain\user so we can squirrel around
|
|
// with it a bit.
|
|
//
|
|
|
|
int cL = 0;
|
|
|
|
//
|
|
// prepend default logon domain if no domain
|
|
// and the default user name was not used
|
|
//
|
|
|
|
if ( strchr( pszUser, '/' ) == NULL
|
|
#if 1 // DBCS enabling for user name
|
|
&& _mbschr( (PUCHAR)pszUser, '\\' ) == NULL )
|
|
#else
|
|
&& strchr( pszUser, '\\' ) == NULL )
|
|
#endif
|
|
{
|
|
psi->LockThisForRead();
|
|
PCSTR pD = pTAI->strDefaultLogonDomain.QueryStr();
|
|
PCSTR pL;
|
|
if ( pD != NULL && pD[0] != '\0' )
|
|
{
|
|
#if 1 // DBCS enabling for user name
|
|
if ( ( pL = (PCHAR)_mbschr( (PUCHAR)pD, '\\' ) ) )
|
|
#else
|
|
if ( ( pL = strchr( pD, '\\' ) ) )
|
|
#endif
|
|
{
|
|
cL = DIFF(pL - pD);
|
|
}
|
|
else
|
|
{
|
|
cL = strlen( pD );
|
|
}
|
|
memcpy( szDomainAndUser, pD, cL );
|
|
szDomainAndUser[ cL++ ] = '\\';
|
|
}
|
|
else
|
|
{
|
|
DWORD dwL = DNLEN + 1;
|
|
if ( GetComputerName( szDomainAndUser, &dwL ) )
|
|
{
|
|
cL = dwL;
|
|
szDomainAndUser[ cL++ ] = '\\';
|
|
}
|
|
}
|
|
psi->UnlockThis();
|
|
}
|
|
|
|
strcpy( szDomainAndUser + cL, pszUser );
|
|
|
|
return pstrU->Copy( (TCHAR*)szDomainAndUser );
|
|
}
|
|
|
|
|
|
BOOL
|
|
TCP_AUTHENT::QueryUserName(
|
|
STR * pBuff,
|
|
BOOL fImpersonated
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Queries the name associated with this *authenticated* object
|
|
|
|
Arguments:
|
|
|
|
pBuff - Buffer to receive name
|
|
|
|
Return Value:
|
|
|
|
TRUE if successful, FALSE otherwise (call GetLastError)
|
|
|
|
--*/
|
|
{
|
|
SECURITY_STATUS ss;
|
|
DWORD cbName;
|
|
SecPkgContext_Names CredNames;
|
|
UINT l;
|
|
|
|
if ( _pDeleteFunction )
|
|
{
|
|
//
|
|
// this context is bound to a SSL/PCT client certificate
|
|
// SECPKG_ATTR_NAMES returns certificate info in this case,
|
|
// 1st try SECPKG_ATTR_MAPNAMES. If fails, use GetUserName()
|
|
//
|
|
|
|
#if defined(SECPKG_ATTR_MAPNAMES)
|
|
|
|
if ( _fHaveCtxtHandle )
|
|
{
|
|
ss = pfnQueryContextAttributes( &_hctxt,
|
|
SECPKG_ATTR_MAPNAMES,
|
|
&CredNames );
|
|
|
|
if ( ss == STATUS_SUCCESS )
|
|
{
|
|
cbName = strlen( CredNames.sUserName ) + 1;
|
|
|
|
if ( !pBuff->Resize( cbName ))
|
|
{
|
|
LocalFree( CredNames.sUserName );
|
|
return FALSE;
|
|
}
|
|
|
|
memcpy( pBuff->QueryPtr(), CredNames.sUserName, cbName );
|
|
|
|
pBuff->SetLen( cbName - 1 );
|
|
|
|
LocalFree( CredNames.sUserName );
|
|
|
|
return TRUE;
|
|
}
|
|
}
|
|
#endif
|
|
if ( !fImpersonated && !Impersonate() )
|
|
{
|
|
return FALSE;
|
|
}
|
|
DWORD cL = pBuff->QuerySize();
|
|
BOOL fSt = TRUE;
|
|
if ( !GetUserName( (LPTSTR)pBuff->QueryPtr(), &cL ) )
|
|
{
|
|
if ( fSt = pBuff->Resize( cL ) )
|
|
{
|
|
fSt = GetUserName( (LPTSTR)pBuff->QueryPtr(), &cL );
|
|
}
|
|
}
|
|
|
|
//
|
|
// Add domain name if not present
|
|
//
|
|
|
|
#if 1 // DBCS enabling for user name
|
|
if ( fSt && _mbschr( (PUCHAR)pBuff->QueryPtr(), '\\') == NULL )
|
|
#else
|
|
if ( fSt && strchr( (LPSTR)pBuff->QueryPtr(), '\\') == NULL )
|
|
#endif
|
|
{
|
|
cL = strlen(g_achComputerName);
|
|
if ( pBuff->Resize( l=(cL+1+strlen((LPSTR)pBuff->QueryPtr()))+1 ) )
|
|
{
|
|
memmove( (LPBYTE)pBuff->QueryPtr()+cL+1,
|
|
pBuff->QueryPtr(),
|
|
strlen((LPSTR)pBuff->QueryPtr())+sizeof(CHAR) );
|
|
memcpy( pBuff->QueryPtr(), g_achComputerName, cL );
|
|
((LPBYTE)pBuff->QueryPtr())[cL] = '\\';
|
|
pBuff->SetLen( l );
|
|
}
|
|
}
|
|
|
|
if ( !fImpersonated )
|
|
{
|
|
RevertToSelf();
|
|
}
|
|
|
|
return fSt;
|
|
}
|
|
else
|
|
{
|
|
if ( _fHaveCtxtHandle )
|
|
{
|
|
ss = pfnQueryContextAttributes( &_hctxt,
|
|
SECPKG_ATTR_NAMES,
|
|
&CredNames );
|
|
}
|
|
else
|
|
{
|
|
ss = ERROR_INVALID_HANDLE;
|
|
}
|
|
|
|
if ( ss != STATUS_SUCCESS )
|
|
{
|
|
SetLastError( ss );
|
|
return FALSE;
|
|
}
|
|
|
|
cbName = strlen( CredNames.sUserName ) + 1;
|
|
|
|
if ( !pBuff->Resize( cbName ))
|
|
{
|
|
pfnFreeContextBuffer( CredNames.sUserName );
|
|
return FALSE;
|
|
}
|
|
|
|
memcpy( pBuff->QueryPtr(), CredNames.sUserName, cbName );
|
|
|
|
pBuff->SetLen( cbName - 1 );
|
|
|
|
pfnFreeContextBuffer( CredNames.sUserName );
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
/*******************************************************************/
|
|
|
|
BOOL TCP_AUTHENT::Converse(
|
|
VOID * pBuffIn,
|
|
DWORD cbBuffIn,
|
|
BUFFER * pbuffOut,
|
|
DWORD * pcbBuffOut,
|
|
BOOL * pfNeedMoreData,
|
|
PTCP_AUTHENT_INFO pTAI,
|
|
CHAR * pszPackage,
|
|
CHAR * pszUser,
|
|
CHAR * pszPassword,
|
|
PIIS_SERVER_INSTANCE psi
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Initiates or continues a previously initiated authentication conversation
|
|
|
|
Client calls this first to get the negotiation message which
|
|
it then sends to the server. The server calls this with the
|
|
client result and sends back the result. The conversation
|
|
continues until *pfNeedMoreData is FALSE.
|
|
|
|
On the first call, pszPackage must point to the zero terminated
|
|
authentication package name to be used and pszUser and pszPassword
|
|
should point to the user name and password to authenticated with
|
|
on the client side (server side will always be NULL).
|
|
|
|
Arguments:
|
|
|
|
pBuffIn - Points to SSP message received from the
|
|
client. If TCPAUTH_UUENCODE is used, then this must point to a
|
|
zero terminated uuencoded string (except for the first call).
|
|
cbBuffIn - Number of bytes in pBuffIn or zero if pBuffIn points to a
|
|
zero terminated, uuencoded string.
|
|
pbuffOut - If *pfDone is not set to TRUE, this buffer contains the data
|
|
that should be sent to the other side. If this is zero, then no
|
|
data needs to be sent.
|
|
pcbBuffOut - Number of bytes in pbuffOut
|
|
pfNeedMoreData - Set to TRUE while this side of the conversation is
|
|
expecting more data from the remote side.
|
|
pszPackage - On the first call points to a zero terminate string indicating
|
|
the security package to use
|
|
pszUser - Specifies user or domain\user the first time the client calls
|
|
this method (client side only)
|
|
pszPassword - Specifies the password for pszUser the first time the
|
|
client calls this method (client side only)
|
|
|
|
Return Value:
|
|
|
|
TRUE if successful, FALSE otherwise (call GetLastError). Access is
|
|
denied if FALSE is returned and GetLastError is ERROR_ACCESS_DENIED.
|
|
|
|
--*/
|
|
{
|
|
SECURITY_STATUS ss;
|
|
TimeStamp Lifetime;
|
|
SecBufferDesc OutBuffDesc;
|
|
SecBuffer OutSecBuff;
|
|
SecBufferDesc InBuffDesc;
|
|
SecBuffer InSecBuff;
|
|
ULONG ContextAttributes;
|
|
BUFFER buffData;
|
|
BUFFER buff;
|
|
STACK_STR ( strDefaultLogonDomain, IIS_DNLEN+1 );
|
|
|
|
//
|
|
// Decode the data if there's something to decode
|
|
//
|
|
|
|
if ( _fUUEncodeData &&
|
|
pBuffIn &&
|
|
PackageSupportsEncoding( pszPackage ) )
|
|
{
|
|
if ( !uudecode( (CHAR *) pBuffIn,
|
|
&buffData,
|
|
&cbBuffIn,
|
|
_fBase64
|
|
))
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
pBuffIn = buffData.QueryPtr();
|
|
}
|
|
|
|
//
|
|
// If this is a new conversation, then we need to get the credential
|
|
// handle and find out the maximum token size
|
|
//
|
|
|
|
if ( _fNewConversation )
|
|
{
|
|
if ( !_fClient )
|
|
{
|
|
if ( !CACHED_CREDENTIAL::GetCredential(
|
|
pszPackage,
|
|
psi,
|
|
pTAI,
|
|
&_hcred,
|
|
&_cbMaxToken ) )
|
|
{
|
|
return FALSE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
SecPkgInfo * pspkg;
|
|
SEC_WINNT_AUTH_IDENTITY AuthIdentity;
|
|
SEC_WINNT_AUTH_IDENTITY * pAuthIdentity;
|
|
CHAR * pszDomain = NULL;
|
|
CHAR szDomainAndUser[IIS_DNLEN+UNLEN+2];
|
|
|
|
|
|
//
|
|
// If this is the client and a username and password were
|
|
// specified, then fill out the authentication information
|
|
//
|
|
|
|
if ( _fClient &&
|
|
((pszUser != NULL) ||
|
|
(pszPassword != NULL)) )
|
|
{
|
|
pAuthIdentity = &AuthIdentity;
|
|
|
|
//
|
|
// Break out the domain from the username if one was specified
|
|
//
|
|
|
|
if ( pszUser != NULL )
|
|
{
|
|
strcpy( szDomainAndUser, pszUser );
|
|
if ( !CrackUserAndDomain( szDomainAndUser,
|
|
&pszUser,
|
|
&pszDomain ))
|
|
{
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
memset( &AuthIdentity,
|
|
0,
|
|
sizeof( AuthIdentity ));
|
|
|
|
if ( pszUser != NULL )
|
|
{
|
|
AuthIdentity.User = (unsigned char *) pszUser;
|
|
AuthIdentity.UserLength = strlen( pszUser );
|
|
}
|
|
|
|
if ( pszPassword != NULL )
|
|
{
|
|
AuthIdentity.Password = (unsigned char *) pszPassword;
|
|
AuthIdentity.PasswordLength = strlen( pszPassword );
|
|
}
|
|
|
|
if ( pszDomain != NULL )
|
|
{
|
|
AuthIdentity.Domain = (unsigned char *) pszDomain;
|
|
AuthIdentity.DomainLength = strlen( pszDomain );
|
|
}
|
|
|
|
AuthIdentity.Flags = SEC_WINNT_AUTH_IDENTITY_ANSI;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// provide default logon domain
|
|
//
|
|
|
|
if ( psi == NULL )
|
|
{
|
|
pAuthIdentity = NULL;
|
|
}
|
|
else
|
|
{
|
|
pAuthIdentity = &AuthIdentity;
|
|
|
|
memset( &AuthIdentity,
|
|
0,
|
|
sizeof( AuthIdentity ));
|
|
|
|
if ( pTAI->strDefaultLogonDomain.QueryCCH() <= IIS_DNLEN )
|
|
{
|
|
strDefaultLogonDomain.Copy( pTAI->strDefaultLogonDomain );
|
|
AuthIdentity.Domain = (LPBYTE)strDefaultLogonDomain.QueryStr();
|
|
}
|
|
if ( AuthIdentity.Domain != NULL )
|
|
{
|
|
if ( AuthIdentity.DomainLength =
|
|
strlen( (LPCTSTR)AuthIdentity.Domain ) )
|
|
{
|
|
// remove trailing '\\' if present
|
|
|
|
if ( AuthIdentity.Domain[AuthIdentity.DomainLength-1]
|
|
== '\\' )
|
|
{
|
|
--AuthIdentity.DomainLength;
|
|
}
|
|
}
|
|
}
|
|
if ( AuthIdentity.DomainLength == 0 )
|
|
{
|
|
pAuthIdentity = NULL;
|
|
}
|
|
else
|
|
{
|
|
AuthIdentity.Flags = SEC_WINNT_AUTH_IDENTITY_ANSI;
|
|
}
|
|
}
|
|
}
|
|
|
|
ss = pfnAcquireCredentialsHandle( NULL, // New principal
|
|
pszPackage, // Package name
|
|
(_fClient ? SECPKG_CRED_OUTBOUND :
|
|
SECPKG_CRED_INBOUND),
|
|
NULL, // Logon ID
|
|
pAuthIdentity, // Auth Data
|
|
NULL, // Get key func
|
|
NULL, // Get key arg
|
|
&_hcred,
|
|
&Lifetime );
|
|
|
|
//
|
|
// Need to determine the max token size for this package
|
|
//
|
|
|
|
if ( ss == STATUS_SUCCESS )
|
|
{
|
|
_fHaveCredHandle = TRUE;
|
|
ss = pfnQuerySecurityPackageInfo( (char *) pszPackage,
|
|
&pspkg );
|
|
}
|
|
|
|
if ( ss != STATUS_SUCCESS )
|
|
{
|
|
DBGPRINTF(( DBG_CONTEXT,
|
|
"[Converse] AcquireCredentialsHandle or QuerySecurityPackageInfo failed, error %d\n",
|
|
ss ));
|
|
|
|
SetLastError( ss );
|
|
return FALSE;
|
|
}
|
|
|
|
_cbMaxToken = pspkg->cbMaxToken;
|
|
DBG_ASSERT( pspkg->fCapabilities & SECPKG_FLAG_CONNECTION );
|
|
|
|
pfnFreeContextBuffer( pspkg );
|
|
}
|
|
}
|
|
|
|
//
|
|
// Prepare our output buffer. We use a temporary buffer because
|
|
// the real output buffer will most likely need to be uuencoded
|
|
//
|
|
|
|
if ( !buff.Resize( _cbMaxToken ))
|
|
return FALSE;
|
|
|
|
OutBuffDesc.ulVersion = 0;
|
|
OutBuffDesc.cBuffers = 1;
|
|
OutBuffDesc.pBuffers = &OutSecBuff;
|
|
|
|
OutSecBuff.cbBuffer = _cbMaxToken;
|
|
OutSecBuff.BufferType = SECBUFFER_TOKEN;
|
|
OutSecBuff.pvBuffer = buff.QueryPtr();
|
|
|
|
//
|
|
// Prepare our Input buffer - Note the server is expecting the client's
|
|
// negotiation packet on the first call
|
|
//
|
|
|
|
if ( pBuffIn )
|
|
{
|
|
InBuffDesc.ulVersion = 0;
|
|
InBuffDesc.cBuffers = 1;
|
|
InBuffDesc.pBuffers = &InSecBuff;
|
|
|
|
InSecBuff.cbBuffer = cbBuffIn;
|
|
InSecBuff.BufferType = SECBUFFER_TOKEN;
|
|
InSecBuff.pvBuffer = pBuffIn;
|
|
}
|
|
|
|
//
|
|
// Client side uses InitializeSecurityContext, server side uses
|
|
// AcceptSecurityContext
|
|
//
|
|
|
|
if ( _fClient )
|
|
{
|
|
//
|
|
// Note the client will return success when its done but we still
|
|
// need to send the out buffer if there are bytes to send
|
|
//
|
|
|
|
ss = pfnInitializeSecurityContext( &_hcred,
|
|
_fNewConversation ? NULL :
|
|
&_hctxt,
|
|
_strTarget.IsEmpty() ?
|
|
TCPAUTH_TARGET_NAME :
|
|
_strTarget.QueryStr(),
|
|
0,
|
|
0,
|
|
SECURITY_NATIVE_DREP,
|
|
_fNewConversation ? NULL :
|
|
&InBuffDesc,
|
|
0,
|
|
&_hctxt,
|
|
&OutBuffDesc,
|
|
&ContextAttributes,
|
|
&Lifetime );
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// This is the server side
|
|
//
|
|
|
|
SetLastError ( 0 );
|
|
|
|
ss = pfnAcceptSecurityContext( &_hcred,
|
|
_fNewConversation ? NULL :
|
|
&_hctxt,
|
|
&InBuffDesc,
|
|
ASC_REQ_EXTENDED_ERROR,
|
|
SECURITY_NATIVE_DREP,
|
|
&_hctxt,
|
|
&OutBuffDesc,
|
|
&ContextAttributes,
|
|
&Lifetime );
|
|
}
|
|
|
|
if ( !NT_SUCCESS( ss ) )
|
|
{
|
|
DBGPRINTF(( DBG_CONTEXT,
|
|
"[Converse] Initialize/AcceptCredentialsHandle failed, error %d\n",
|
|
ss ));
|
|
|
|
if ( !_fNewConversation )
|
|
{
|
|
//
|
|
// If not a new conversation, then when we fail we still have a context
|
|
// handle from previous to AcceptSecurityContext. Need to call
|
|
// DeleteSecurityContext to avoid leaking the context.
|
|
// AcceptSecurityContext does not touch _hctxt if it fails.
|
|
//
|
|
|
|
DBG_ASSERT( _fHaveCtxtHandle );
|
|
|
|
pfnDeleteSecurityContext( &_hctxt );
|
|
}
|
|
|
|
_fHaveCtxtHandle = FALSE;
|
|
|
|
if ( ss == SEC_E_LOGON_DENIED ||
|
|
ss == SEC_E_INVALID_TOKEN )
|
|
{
|
|
ss = ERROR_LOGON_FAILURE;
|
|
}
|
|
|
|
if ( GetLastError() != ERROR_PASSWORD_EXPIRED
|
|
&& GetLastError() != ERROR_PASSWORD_MUST_CHANGE )
|
|
{
|
|
SetLastError( ss );
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
if( ContextAttributes & ASC_RET_NULL_SESSION )
|
|
{
|
|
SetLastError( ERROR_LOGON_FAILURE );
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
_fHaveCtxtHandle = TRUE;
|
|
|
|
//
|
|
// NTLMSSP will set the last error to ERROR_NO_SUCH_USER
|
|
// if success and Guest account was used
|
|
//
|
|
|
|
if ( GetLastError() == ERROR_NO_SUCH_USER )
|
|
{
|
|
_fKnownToBeGuest = TRUE;
|
|
}
|
|
|
|
//
|
|
// Now we just need to complete the token (if requested) and prepare
|
|
// it for shipping to the other side if needed
|
|
//
|
|
|
|
BOOL fReply = !!OutSecBuff.cbBuffer;
|
|
|
|
if ( (ss == SEC_I_COMPLETE_NEEDED) ||
|
|
(ss == SEC_I_COMPLETE_AND_CONTINUE) )
|
|
{
|
|
ss = pfnCompleteAuthToken( &_hctxt,
|
|
&OutBuffDesc );
|
|
|
|
if ( !NT_SUCCESS( ss ))
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
//
|
|
// Format or copy to the output buffer if we need to reply
|
|
//
|
|
|
|
if ( fReply )
|
|
{
|
|
if ( _fUUEncodeData &&
|
|
PackageSupportsEncoding( pszPackage ) )
|
|
{
|
|
if ( !uuencode( (BYTE *) OutSecBuff.pvBuffer,
|
|
OutSecBuff.cbBuffer,
|
|
pbuffOut,
|
|
_fBase64 ))
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
*pcbBuffOut = strlen( (CHAR *) pbuffOut->QueryPtr() );
|
|
}
|
|
else
|
|
{
|
|
if ( !pbuffOut->Resize( OutSecBuff.cbBuffer ))
|
|
return FALSE;
|
|
|
|
memcpy( pbuffOut->QueryPtr(),
|
|
OutSecBuff.pvBuffer,
|
|
OutSecBuff.cbBuffer );
|
|
|
|
*pcbBuffOut = OutSecBuff.cbBuffer;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
*pcbBuffOut = 0;
|
|
}
|
|
|
|
if ( _fNewConversation )
|
|
_fNewConversation = FALSE;
|
|
|
|
*pfNeedMoreData = ((ss == SEC_I_CONTINUE_NEEDED) ||
|
|
(ss == SEC_I_COMPLETE_AND_CONTINUE));
|
|
|
|
if ( !*pfNeedMoreData && !_fClient )
|
|
{
|
|
_fDelegate = !!(ContextAttributes & ASC_RET_DELEGATE);
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
BOOL TCP_AUTHENT::ConverseEx(
|
|
SecBufferDesc* pInSecBufDesc, // passed in by caller
|
|
BUFFER * pDecodedBuffer, // passed in by caller
|
|
BUFFER * pbuffOut,
|
|
DWORD * pcbBuffOut,
|
|
BOOL * pfNeedMoreData,
|
|
PTCP_AUTHENT_INFO pTAI,
|
|
CHAR * pszPackage,
|
|
CHAR * pszUser,
|
|
CHAR * pszPassword,
|
|
PIIS_SERVER_INSTANCE psi
|
|
)
|
|
/*
|
|
* A variant of Converse that takes variable number of input SecBuffer.
|
|
* Caller will set the SecBuffers and pass in the SecBufferDesc pointer to ConverseEx.
|
|
* If the SecBuffer.pvBuffer needs to be decoded, caller has to pass in
|
|
* an array of BUFFER to hold the decoded data. The number of BUFFER elements
|
|
* should be the same as the number of SecBuffer.
|
|
* If decoding it not needed, pass NULL instead.
|
|
*
|
|
*/
|
|
{
|
|
|
|
SECURITY_STATUS ss;
|
|
TimeStamp Lifetime;
|
|
SecBufferDesc OutBuffDesc;
|
|
SecBuffer OutSecBuff;
|
|
ULONG ContextAttributes;
|
|
BUFFER buffData;
|
|
BUFFER buff;
|
|
STACK_STR ( strDefaultLogonDomain, IIS_DNLEN+1 );
|
|
DWORD dw, dwDecodedLen;
|
|
SecBuffer *pSecBuffer;
|
|
|
|
// make sure we have at least one SecBuffer to process
|
|
if (pInSecBufDesc->cBuffers == 0)
|
|
return FALSE;
|
|
|
|
//
|
|
// Decode the data if there's something to decode
|
|
//
|
|
if (_fUUEncodeData &&
|
|
pInSecBufDesc &&
|
|
pDecodedBuffer &&
|
|
PackageSupportsEncoding( pszPackage ) )
|
|
{
|
|
pSecBuffer = pInSecBufDesc->pBuffers;
|
|
for (dw = 0; dw < pInSecBufDesc->cBuffers; dw++, pSecBuffer++)
|
|
{
|
|
if (!uudecode((CHAR *)pSecBuffer->pvBuffer, // points to data to be decoded
|
|
&pDecodedBuffer[dw], // to hold decoded data
|
|
&dwDecodedLen, // length of decoded data
|
|
_fBase64
|
|
))
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
// update the SecBuffer so it now points to the decoded data in BUFFER
|
|
pSecBuffer->pvBuffer = pDecodedBuffer[dw].QueryPtr();
|
|
pSecBuffer->cbBuffer = dwDecodedLen;
|
|
}
|
|
}
|
|
|
|
//
|
|
// If this is a new conversation, then we need to get the credential
|
|
// handle and find out the maximum token size
|
|
//
|
|
|
|
if ( _fNewConversation )
|
|
{
|
|
SecPkgInfo * pspkg;
|
|
SEC_WINNT_AUTH_IDENTITY AuthIdentity;
|
|
SEC_WINNT_AUTH_IDENTITY * pAuthIdentity;
|
|
CHAR * pszDomain = NULL;
|
|
CHAR szDomainAndUser[IIS_DNLEN+UNLEN+2];
|
|
|
|
|
|
//
|
|
// If this is the client and a username and password were
|
|
// specified, then fill out the authentication information
|
|
//
|
|
|
|
if ( _fClient &&
|
|
((pszUser != NULL) ||
|
|
(pszPassword != NULL)) )
|
|
{
|
|
pAuthIdentity = &AuthIdentity;
|
|
|
|
//
|
|
// Break out the domain from the username if one was specified
|
|
//
|
|
|
|
if ( pszUser != NULL )
|
|
{
|
|
strcpy( szDomainAndUser, pszUser );
|
|
if ( !CrackUserAndDomain( szDomainAndUser,
|
|
&pszUser,
|
|
&pszDomain ))
|
|
{
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
memset( &AuthIdentity,
|
|
0,
|
|
sizeof( AuthIdentity ));
|
|
|
|
if ( pszUser != NULL )
|
|
{
|
|
AuthIdentity.User = (unsigned char *) pszUser;
|
|
AuthIdentity.UserLength = strlen( pszUser );
|
|
}
|
|
|
|
if ( pszPassword != NULL )
|
|
{
|
|
AuthIdentity.Password = (unsigned char *) pszPassword;
|
|
AuthIdentity.PasswordLength = strlen( pszPassword );
|
|
}
|
|
|
|
if ( pszDomain != NULL )
|
|
{
|
|
AuthIdentity.Domain = (unsigned char *) pszDomain;
|
|
AuthIdentity.DomainLength = strlen( pszDomain );
|
|
}
|
|
|
|
AuthIdentity.Flags = SEC_WINNT_AUTH_IDENTITY_ANSI;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// provide default logon domain
|
|
//
|
|
|
|
if ( psi == NULL )
|
|
{
|
|
pAuthIdentity = NULL;
|
|
}
|
|
else
|
|
{
|
|
pAuthIdentity = &AuthIdentity;
|
|
|
|
memset( &AuthIdentity,
|
|
0,
|
|
sizeof( AuthIdentity ));
|
|
|
|
if ( pTAI->strDefaultLogonDomain.QueryCCH() <= IIS_DNLEN )
|
|
{
|
|
strDefaultLogonDomain.Copy( pTAI->strDefaultLogonDomain );
|
|
AuthIdentity.Domain = (LPBYTE)strDefaultLogonDomain.QueryStr();
|
|
}
|
|
if ( AuthIdentity.Domain != NULL )
|
|
{
|
|
if ( AuthIdentity.DomainLength =
|
|
strlen( (LPCTSTR)AuthIdentity.Domain ) )
|
|
{
|
|
// remove trailing '\\' if present
|
|
|
|
if ( AuthIdentity.Domain[AuthIdentity.DomainLength-1]
|
|
== '\\' )
|
|
{
|
|
--AuthIdentity.DomainLength;
|
|
}
|
|
}
|
|
}
|
|
if ( AuthIdentity.DomainLength == 0 )
|
|
{
|
|
pAuthIdentity = NULL;
|
|
}
|
|
else
|
|
{
|
|
AuthIdentity.Flags = SEC_WINNT_AUTH_IDENTITY_ANSI;
|
|
}
|
|
}
|
|
}
|
|
|
|
ss = pfnAcquireCredentialsHandle( NULL, // New principal
|
|
pszPackage, // Package name
|
|
(_fClient ? SECPKG_CRED_OUTBOUND :
|
|
SECPKG_CRED_INBOUND),
|
|
NULL, // Logon ID
|
|
pAuthIdentity, // Auth Data
|
|
NULL, // Get key func
|
|
NULL, // Get key arg
|
|
&_hcred,
|
|
&Lifetime );
|
|
|
|
//
|
|
// Need to determine the max token size for this package
|
|
//
|
|
|
|
if ( ss == STATUS_SUCCESS )
|
|
{
|
|
_fHaveCredHandle = TRUE;
|
|
ss = pfnQuerySecurityPackageInfo( (char *) pszPackage,
|
|
&pspkg );
|
|
}
|
|
|
|
if ( ss != STATUS_SUCCESS )
|
|
{
|
|
DBGPRINTF(( DBG_CONTEXT,
|
|
"[Converse] AcquireCredentialsHandle or QuerySecurityPackageInfo failed, error %d\n",
|
|
ss ));
|
|
|
|
SetLastError( ss );
|
|
return FALSE;
|
|
}
|
|
|
|
_cbMaxToken = pspkg->cbMaxToken;
|
|
DBG_ASSERT( pspkg->fCapabilities & SECPKG_FLAG_CONNECTION );
|
|
|
|
pfnFreeContextBuffer( pspkg );
|
|
|
|
}
|
|
|
|
//
|
|
// Prepare our output buffer. We use a temporary buffer because
|
|
// the real output buffer will most likely need to be uuencoded
|
|
//
|
|
|
|
if ( !buff.Resize( _cbMaxToken ))
|
|
return FALSE;
|
|
|
|
OutBuffDesc.ulVersion = 0;
|
|
OutBuffDesc.cBuffers = 1;
|
|
OutBuffDesc.pBuffers = &OutSecBuff;
|
|
|
|
OutSecBuff.cbBuffer = _cbMaxToken;
|
|
OutSecBuff.BufferType = SECBUFFER_TOKEN;
|
|
OutSecBuff.pvBuffer = buff.QueryPtr();
|
|
|
|
//
|
|
// Input sec buffer is passed by caller
|
|
//
|
|
|
|
//
|
|
// Client side uses InitializeSecurityContext, server side uses
|
|
// AcceptSecurityContext
|
|
//
|
|
|
|
if ( _fClient )
|
|
{
|
|
//
|
|
// Note the client will return success when its done but we still
|
|
// need to send the out buffer if there are bytes to send
|
|
//
|
|
|
|
ss = pfnInitializeSecurityContext( &_hcred,
|
|
_fNewConversation ? NULL : &_hctxt,
|
|
_strTarget.IsEmpty() ?
|
|
TCPAUTH_TARGET_NAME :
|
|
_strTarget.QueryStr(),
|
|
0,
|
|
0,
|
|
SECURITY_NATIVE_DREP,
|
|
_fNewConversation ? NULL : pInSecBufDesc,
|
|
0,
|
|
&_hctxt,
|
|
&OutBuffDesc,
|
|
&ContextAttributes,
|
|
&Lifetime );
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// This is the server side
|
|
//
|
|
|
|
SetLastError ( 0 );
|
|
|
|
ss = pfnAcceptSecurityContext( &_hcred,
|
|
_fNewConversation ? NULL : &_hctxt,
|
|
pInSecBufDesc,
|
|
ASC_REQ_EXTENDED_ERROR,
|
|
SECURITY_NATIVE_DREP,
|
|
&_hctxt,
|
|
&OutBuffDesc,
|
|
&ContextAttributes,
|
|
&Lifetime );
|
|
}
|
|
|
|
if ( !NT_SUCCESS( ss ) )
|
|
{
|
|
DBGPRINTF(( DBG_CONTEXT,
|
|
"[Converse] Initialize/AcceptCredentialsHandle failed, error %d\n",
|
|
ss ));
|
|
|
|
if ( ss == SEC_E_LOGON_DENIED ||
|
|
ss == SEC_E_INVALID_TOKEN )
|
|
{
|
|
ss = ERROR_LOGON_FAILURE;
|
|
}
|
|
|
|
if ( GetLastError() != ERROR_PASSWORD_EXPIRED
|
|
&& GetLastError() != ERROR_PASSWORD_MUST_CHANGE )
|
|
{
|
|
SetLastError( ss );
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
_fHaveCtxtHandle = TRUE;
|
|
|
|
//
|
|
// NTLMSSP will set the last error to ERROR_NO_SUCH_USER
|
|
// if success and Guest account was used
|
|
//
|
|
|
|
if ( GetLastError() == ERROR_NO_SUCH_USER )
|
|
{
|
|
_fKnownToBeGuest = TRUE;
|
|
}
|
|
|
|
//
|
|
// Now we just need to complete the token (if requested) and prepare
|
|
// it for shipping to the other side if needed
|
|
//
|
|
|
|
BOOL fReply = !!OutSecBuff.cbBuffer;
|
|
|
|
if ( (ss == SEC_I_COMPLETE_NEEDED) ||
|
|
(ss == SEC_I_COMPLETE_AND_CONTINUE) )
|
|
{
|
|
ss = pfnCompleteAuthToken( &_hctxt,
|
|
&OutBuffDesc );
|
|
|
|
if ( !NT_SUCCESS( ss ))
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
//
|
|
// Format or copy to the output buffer if we need to reply
|
|
//
|
|
|
|
if ( fReply )
|
|
{
|
|
if ( _fUUEncodeData &&
|
|
PackageSupportsEncoding( pszPackage ) )
|
|
{
|
|
if ( !uuencode( (BYTE *) OutSecBuff.pvBuffer,
|
|
OutSecBuff.cbBuffer,
|
|
pbuffOut,
|
|
_fBase64 ))
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
*pcbBuffOut = strlen( (CHAR *) pbuffOut->QueryPtr() );
|
|
}
|
|
else
|
|
{
|
|
if ( !pbuffOut->Resize( OutSecBuff.cbBuffer ))
|
|
return FALSE;
|
|
|
|
memcpy( pbuffOut->QueryPtr(),
|
|
OutSecBuff.pvBuffer,
|
|
OutSecBuff.cbBuffer );
|
|
|
|
*pcbBuffOut = OutSecBuff.cbBuffer;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
*pcbBuffOut = 0;
|
|
}
|
|
|
|
if ( _fNewConversation )
|
|
_fNewConversation = FALSE;
|
|
|
|
*pfNeedMoreData = ((ss == SEC_I_CONTINUE_NEEDED) ||
|
|
(ss == SEC_I_COMPLETE_AND_CONTINUE));
|
|
|
|
if ( !*pfNeedMoreData && !_fClient )
|
|
{
|
|
_fDelegate = !!(ContextAttributes & ASC_RET_DELEGATE);
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
/*******************************************************************/
|
|
|
|
#if 0
|
|
BOOL
|
|
TCP_AUTHENT::DigestLogon(
|
|
PSTR pszUserName,
|
|
PSTR pszRealm,
|
|
PSTR pszUri,
|
|
PSTR pszMethod,
|
|
PSTR pszNonce,
|
|
PSTR pszServerNonce,
|
|
PSTR pszDigest,
|
|
DWORD dwAlgo,
|
|
LPTSVC_INFO psi
|
|
)
|
|
{
|
|
HANDLE hToken;
|
|
CHAR szDomainAndUser[IIS_DNLEN+UNLEN+2];
|
|
int cL = 0;
|
|
CHAR * pszUserOnly;
|
|
CHAR * pszDomain;
|
|
|
|
//
|
|
// prepend default logon domain if no domain
|
|
//
|
|
|
|
if ( strchr( pszUserName, '/' ) == NULL
|
|
&& strchr( pszUserName, '\\' ) == NULL )
|
|
{
|
|
psi->LockThisForRead();
|
|
PCSTR pD = psi->QueryDefaultLogonDomain();
|
|
PCSTR pL;
|
|
if ( pD != NULL && pD[0] != '\0' )
|
|
{
|
|
if ( ( pL = strchr( pD, '\\' ) ) )
|
|
{
|
|
cL = pL - pD;
|
|
}
|
|
else
|
|
{
|
|
cL = strlen( pD );
|
|
}
|
|
memcpy( szDomainAndUser, pD, cL );
|
|
szDomainAndUser[ cL++ ] = '\\';
|
|
}
|
|
psi->UnlockThis();
|
|
}
|
|
|
|
strcpy( szDomainAndUser + cL, pszUserName );
|
|
|
|
//
|
|
// Crack the name into domain/user components.
|
|
//
|
|
|
|
if ( !CrackUserAndDomain( szDomainAndUser,
|
|
&pszUserOnly,
|
|
&pszDomain ))
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
if ( LogonDigestUserA(
|
|
pszUserOnly,
|
|
pszDomain,
|
|
pszRealm,
|
|
pszUri,
|
|
pszMethod,
|
|
pszNonce,
|
|
pszServerNonce,
|
|
pszDigest,
|
|
dwAlgo,
|
|
&hToken )
|
|
&& SetAccessToken( NULL, hToken ) )
|
|
{
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
#endif
|
|
|
|
|
|
BOOL
|
|
TCP_AUTHENT::ClearTextLogon(
|
|
IN PCHAR pszUser,
|
|
IN PCHAR pszPassword,
|
|
OUT PBOOL pfAsGuest,
|
|
OUT PBOOL pfAsAnonymous,
|
|
IN PIIS_SERVER_INSTANCE pInstance,
|
|
PTCP_AUTHENT_INFO pTAI,
|
|
IN PCHAR pszWorkstation
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Gets a network logon token using clear text
|
|
|
|
Arguments:
|
|
|
|
pszUser - User name (optionally with domain)
|
|
pszPassword - password
|
|
pfAsGuest - Set to TRUE if granted with guest access (NOT SUPPORTED)
|
|
pfAsAnonymous - Set to TRUE if the user received the anonymous token
|
|
pInstance - pointer to Server instance
|
|
|
|
Return Value:
|
|
|
|
TRUE if successful, FALSE otherwise (call GetLastError)
|
|
|
|
--*/
|
|
{
|
|
BOOL fHaveExp;
|
|
|
|
DBG_ASSERT( !_fHaveCredHandle && !_fHaveCtxtHandle );
|
|
|
|
//
|
|
// short circuit fast path
|
|
//
|
|
|
|
if ( pszUser == NULL ) {
|
|
|
|
_hToken = FastFindAnonymousToken( pTAI );
|
|
|
|
//
|
|
// success!
|
|
//
|
|
|
|
if ( _hToken != NULL ) {
|
|
|
|
_liPwdExpiry.LowPart = _hToken->QueryExpiry()->LowPart;
|
|
_liPwdExpiry.HighPart = _hToken->QueryExpiry()->HighPart;
|
|
|
|
_fHaveExpiry = TRUE;
|
|
_fClearText = TRUE;
|
|
|
|
*pfAsGuest = _hToken->IsGuest();
|
|
*pfAsAnonymous = TRUE;
|
|
return TRUE;
|
|
}
|
|
|
|
//
|
|
// use normal path
|
|
//
|
|
}
|
|
|
|
_hToken = TsLogonUser( pszUser,
|
|
pszPassword,
|
|
pfAsGuest,
|
|
pfAsAnonymous,
|
|
pInstance,
|
|
pTAI,
|
|
pszWorkstation,
|
|
&_liPwdExpiry,
|
|
&fHaveExp );
|
|
|
|
if ( _hToken == NULL ) {
|
|
return FALSE;
|
|
}
|
|
|
|
_fClearText = TRUE;
|
|
_fHaveExpiry = fHaveExp;
|
|
|
|
switch ( pTAI->dwLogonMethod )
|
|
{
|
|
case LOGON32_LOGON_BATCH:
|
|
case LOGON32_LOGON_INTERACTIVE:
|
|
case LOGON32_LOGON_NETWORK_CLEARTEXT:
|
|
_fDelegate = TRUE;
|
|
}
|
|
|
|
return TRUE;
|
|
|
|
} // TCP_AUTHENT::ClearTextLogon
|
|
|
|
|
|
BOOL TCP_AUTHENT::SetAccessToken(
|
|
HANDLE hPrimaryToken,
|
|
HANDLE hImpersonationToken
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Set primary & impersonation token
|
|
|
|
Arguments:
|
|
|
|
hPrimaryToken -- Primary Access Token
|
|
hImpersonationToken -- Impersonation Access Token
|
|
One the two above tokens can be NULL ( but not both )
|
|
psi - pointer to Service info struct
|
|
|
|
Return Value:
|
|
|
|
TRUE if successful, FALSE otherwise ( tokens will be closed )
|
|
|
|
--*/
|
|
{
|
|
if ( !hPrimaryToken )
|
|
{
|
|
if ( !hImpersonationToken )
|
|
{
|
|
SetLastError( ERROR_INVALID_PARAMETER );
|
|
return FALSE;
|
|
}
|
|
|
|
if ( !pfnDuplicateTokenEx( hImpersonationToken,
|
|
TOKEN_ALL_ACCESS,
|
|
NULL,
|
|
SecurityImpersonation,
|
|
TokenPrimary,
|
|
&hPrimaryToken ))
|
|
{
|
|
|
|
CloseHandle( hImpersonationToken );
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
if ( !hImpersonationToken )
|
|
{
|
|
if ( !pfnDuplicateTokenEx( hPrimaryToken,
|
|
TOKEN_ALL_ACCESS,
|
|
NULL,
|
|
SecurityImpersonation,
|
|
TokenImpersonation,
|
|
&hImpersonationToken ))
|
|
{
|
|
|
|
CloseHandle( hPrimaryToken );
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
_hSSPToken = hImpersonationToken;
|
|
_hSSPPrimaryToken = hPrimaryToken;
|
|
|
|
_fHaveAccessTokens = TRUE;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
/*******************************************************************/
|
|
|
|
BOOL TCP_AUTHENT::Impersonate(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Impersonates the authenticated user
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
TRUE if successful, FALSE otherwise (call GetLastError)
|
|
|
|
--*/
|
|
{
|
|
if ( _hToken == (TS_TOKEN)BOGUS_WIN95_TOKEN )
|
|
{
|
|
return(TRUE);
|
|
}
|
|
|
|
if ( _fClearText )
|
|
{
|
|
return TsImpersonateUser( _hToken );
|
|
}
|
|
else if ( _fHaveAccessTokens || _pDeleteFunction )
|
|
{
|
|
return ImpersonateLoggedOnUser( _hSSPToken );
|
|
}
|
|
else
|
|
{
|
|
DBG_ASSERT( _fHaveCtxtHandle );
|
|
|
|
return !!NT_SUCCESS( pfnImpersonateSecurityContext( &_hctxt ));
|
|
}
|
|
}
|
|
|
|
/*******************************************************************/
|
|
|
|
BOOL TCP_AUTHENT::RevertToSelf(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Undoes the impersonation
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
TRUE if successful, FALSE otherwise (call GetLastError)
|
|
|
|
--*/
|
|
{
|
|
if ( _hToken == (TS_TOKEN)BOGUS_WIN95_TOKEN )
|
|
{
|
|
return(TRUE);
|
|
}
|
|
|
|
if ( _fClearText || _fHaveAccessTokens || _pDeleteFunction )
|
|
{
|
|
return ::RevertToSelf();
|
|
}
|
|
else
|
|
{
|
|
DBG_ASSERT( _fHaveCtxtHandle );
|
|
|
|
return !!NT_SUCCESS( pfnRevertSecurityContext( &_hctxt ));
|
|
}
|
|
}
|
|
|
|
/*******************************************************************/
|
|
|
|
BOOL TCP_AUTHENT::StartProcessAsUser(
|
|
LPCSTR lpApplicationName,
|
|
LPSTR lpCommandLine,
|
|
BOOL bInheritHandles,
|
|
DWORD dwCreationFlags,
|
|
LPVOID lpEnvironment,
|
|
LPCSTR lpCurrentDirectory,
|
|
LPSTARTUPINFOA lpStartupInfo,
|
|
LPPROCESS_INFORMATION lpProcessInformation
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Creates a process as the authenticated user
|
|
|
|
Arguments:
|
|
|
|
Standard CreateProcess args
|
|
|
|
Return Value:
|
|
|
|
TRUE if successful, FALSE otherwise (call GetLastError)
|
|
|
|
--*/
|
|
{
|
|
HANDLE htoken;
|
|
BOOL fRet;
|
|
|
|
if ( _fClearText )
|
|
{
|
|
htoken = CTO_TO_TOKEN( _hToken );
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Need to extract the impersonation token from the opaque SSP
|
|
// structures
|
|
//
|
|
|
|
if ( !Impersonate() )
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
if ( !OpenThreadToken( GetCurrentThread(),
|
|
TOKEN_QUERY,
|
|
TRUE,
|
|
&htoken ))
|
|
{
|
|
RevertToSelf();
|
|
return FALSE;
|
|
}
|
|
|
|
RevertToSelf();
|
|
}
|
|
|
|
fRet = CreateProcessAsUser( htoken,
|
|
lpApplicationName,
|
|
lpCommandLine,
|
|
NULL,
|
|
NULL,
|
|
bInheritHandles,
|
|
dwCreationFlags,
|
|
lpEnvironment,
|
|
lpCurrentDirectory,
|
|
lpStartupInfo,
|
|
lpProcessInformation );
|
|
|
|
if ( !_fClearText )
|
|
{
|
|
DBG_REQUIRE( CloseHandle( htoken ) );
|
|
}
|
|
|
|
return fRet;
|
|
}
|
|
|
|
|
|
BOOL
|
|
TCP_AUTHENT::GetClientCertBlob
|
|
(
|
|
IN DWORD cbAllocated,
|
|
OUT DWORD * pdwCertEncodingType,
|
|
OUT unsigned char * pbCertEncoded,
|
|
OUT DWORD * pcbCertEncoded,
|
|
OUT DWORD * pdwCertificateFlags
|
|
)
|
|
{
|
|
|
|
if ( (pdwCertEncodingType == NULL) ||
|
|
(pbCertEncoded == NULL) ||
|
|
(pcbCertEncoded == NULL)
|
|
) {
|
|
|
|
SetLastError( ERROR_INVALID_PARAMETER );
|
|
return ( FALSE);
|
|
}
|
|
|
|
BOOL fReturn = FALSE;
|
|
BOOL fNoCert;
|
|
|
|
|
|
//
|
|
// Win95 <==> no certificate
|
|
// NOTE Currently security.dll is not supported in Win95.
|
|
//
|
|
|
|
if ( TsIsWindows95() ) {
|
|
|
|
goto LNoCertificate;
|
|
}
|
|
|
|
|
|
if ( !QueryCertificateInfo( &fNoCert) ) {
|
|
|
|
goto LNoCertificate;
|
|
}
|
|
|
|
//
|
|
// fill in cert size out-parameter
|
|
//
|
|
|
|
*pcbCertEncoded = _pClientCertContext->cbCertEncoded;
|
|
|
|
|
|
//
|
|
// if buffer is adequate, fill in remaining out-parameters
|
|
// else return error
|
|
//
|
|
|
|
if ( cbAllocated >= *pcbCertEncoded ) {
|
|
|
|
CopyMemory( pbCertEncoded,
|
|
_pClientCertContext->pbCertEncoded,
|
|
_pClientCertContext->cbCertEncoded );
|
|
|
|
*pdwCertEncodingType = _pClientCertContext->dwCertEncodingType;
|
|
*pdwCertificateFlags = _dwX509Flags;
|
|
fReturn = TRUE;
|
|
|
|
} else {
|
|
|
|
SetLastError( ERROR_INSUFFICIENT_BUFFER );
|
|
fReturn = FALSE;
|
|
|
|
}
|
|
|
|
|
|
LExit:
|
|
|
|
return fReturn;
|
|
|
|
|
|
LNoCertificate:
|
|
|
|
//
|
|
// No cert: zero-out buffer and return success
|
|
//
|
|
|
|
*pbCertEncoded = NULL;
|
|
*pcbCertEncoded = 0;
|
|
*pdwCertEncodingType = 0;
|
|
*pdwCertificateFlags = 0;
|
|
|
|
fReturn = TRUE;
|
|
|
|
goto LExit;
|
|
|
|
} // TCP_AUTHENT::GetClientCertBlob()
|
|
|
|
|
|
BOOL
|
|
TCP_AUTHENT::UpdateClientCertFlags(
|
|
DWORD dwFlags,
|
|
LPBOOL pfNoCert,
|
|
LPBYTE pbCa,
|
|
DWORD dwCa
|
|
)
|
|
{
|
|
BOOL fNoCert;
|
|
|
|
_fCertCheckForRevocation = !( dwFlags & MD_CERT_NO_REVOC_CHECK );
|
|
_fCertCheckCacheOnly = !!( dwFlags & MD_CERT_CACHE_RETRIEVAL_ONLY );
|
|
|
|
if ( QueryCertificateInfo( pfNoCert ) )
|
|
{
|
|
*pfNoCert = FALSE;
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
BOOL
|
|
TCP_AUTHENT::PackageSupportsEncoding(
|
|
LPSTR pszPackage
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Check whether the SSPI package should (not) be encoded.
|
|
|
|
Arguments:
|
|
|
|
pszPackage - Name of SSPI package
|
|
|
|
Return Value:
|
|
|
|
TRUE if should encode, FALSE otherwise
|
|
|
|
--*/
|
|
{
|
|
SECURITY_STATUS SecurityStatus;
|
|
PSecPkgInfo pPackageInfo;
|
|
BOOL fRet = FALSE;
|
|
|
|
if ( pszPackage != NULL )
|
|
{
|
|
SecurityStatus = pfnQuerySecurityPackageInfo( pszPackage,
|
|
&pPackageInfo );
|
|
|
|
if ( SecurityStatus == SEC_E_OK )
|
|
{
|
|
if ( !( pPackageInfo->fCapabilities & SECPKG_FLAG_ASCII_BUFFERS ) )
|
|
{
|
|
fRet = TRUE;
|
|
}
|
|
pfnFreeContextBuffer( pPackageInfo );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
SetLastError( ERROR_INVALID_PARAMETER );
|
|
DBG_ASSERT( FALSE );
|
|
}
|
|
|
|
return fRet;
|
|
}
|
|
|
|
BOOL
|
|
TCP_AUTHENT::SetTargetName(
|
|
LPSTR pszTargetName
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Set the target name to pass into InitializeSecurityContext() calls
|
|
|
|
Arguments:
|
|
|
|
pszTargetName - Target name
|
|
|
|
Return Value:
|
|
|
|
TRUE if successful, else FALSE. Use GetLastError() for error
|
|
|
|
--*/
|
|
{
|
|
if ( pszTargetName != NULL )
|
|
{
|
|
return _strTarget.Copy( pszTargetName );
|
|
}
|
|
else
|
|
{
|
|
SetLastError( ERROR_INVALID_PARAMETER );
|
|
return FALSE;
|
|
}
|
|
}
|
|
/************************ End of File ***********************/
|