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.
1596 lines
35 KiB
1596 lines
35 KiB
/*++
|
|
|
|
Copyright (c) 1997 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
base.cxx
|
|
|
|
Abstract:
|
|
|
|
This module implements the IIS_CRYPTO_BASE class.
|
|
|
|
Author:
|
|
|
|
Keith Moore (keithmo) 02-Dec-1996
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
|
|
#include "precomp.hxx"
|
|
#pragma hdrstop
|
|
|
|
|
|
//
|
|
// Private constants.
|
|
//
|
|
|
|
|
|
//
|
|
// Private types.
|
|
//
|
|
|
|
|
|
//
|
|
// Private globals.
|
|
//
|
|
|
|
|
|
//
|
|
// Private prototypes.
|
|
//
|
|
|
|
|
|
//
|
|
// Public functions.
|
|
//
|
|
|
|
|
|
IIS_CRYPTO_BASE::IIS_CRYPTO_BASE()
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
IIS_CRYPTO_BASE class constructor. Just sets the member variables
|
|
to known values; does nothing that can actually fail. All of the
|
|
hard work is in the Initialize() method.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
//
|
|
// Set the handles to known values so we know what to cleanup
|
|
// in the destructor.
|
|
//
|
|
|
|
m_hProv = CRYPT_NULL;
|
|
m_fCloseProv = FALSE;
|
|
m_hKeyExchangeKey = CRYPT_NULL;
|
|
m_hSignatureKey = CRYPT_NULL;
|
|
|
|
} // IIS_CRYPTO_BASE::IIS_CRYPTO_BASE
|
|
|
|
|
|
IIS_CRYPTO_BASE::~IIS_CRYPTO_BASE()
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
IIS_CRYPTO_BASE class destructor. Performs any necessary cleanup.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
//
|
|
// Close any open keys.
|
|
//
|
|
|
|
CLOSE_KEY( m_hSignatureKey );
|
|
CLOSE_KEY( m_hKeyExchangeKey );
|
|
|
|
//
|
|
// Close the container.
|
|
//
|
|
|
|
if( m_fCloseProv && m_hProv != CRYPT_NULL ) {
|
|
(VOID)::IISCryptoCloseContainer( m_hProv );
|
|
m_hProv = CRYPT_NULL;
|
|
}
|
|
|
|
} // IIS_CRYPTO_BASE::~IIS_CRYPTO_BASE
|
|
|
|
|
|
HRESULT
|
|
IIS_CRYPTO_BASE::GetCryptoContainerByName(
|
|
OUT HCRYPTPROV * phProv,
|
|
IN LPTSTR pszContainerName,
|
|
IN DWORD dwAdditionalFlags,
|
|
IN BOOL fApplyAcl
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Creates or opens the specified crypto container. This method plays
|
|
games to ensure the container can be opened regardless of the
|
|
current security impersonation context.
|
|
|
|
Arguments:
|
|
|
|
phProv - Receives the handle to the provider.
|
|
|
|
pszContainerName - The name of the container to open/create.
|
|
(NULL means temporary container)
|
|
|
|
dwAdditionalFlags - Flags to pass into IISCryptoGetStandardContainer().
|
|
|
|
fApplyAcl - If TRUE, then an ACL is applied to the container.
|
|
|
|
Return Value:
|
|
|
|
HRESULT - 0 if successful, !0 otherwise.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
HRESULT result = NO_ERROR;
|
|
HANDLE token = NULL;
|
|
BOOL resetToken = FALSE;
|
|
|
|
//
|
|
// Sanity check.
|
|
//
|
|
|
|
DBG_ASSERT( phProv != NULL );
|
|
|
|
|
|
//
|
|
// If the caller is not already asking for CRYPT_MACHINE_KEYSET
|
|
// *and* the current process is running in the local system context,
|
|
// then forcibly set CRYPT_MACHINE_KEYSET and note that we need to
|
|
// apply an ACL to the container. This is to workaround an issue
|
|
// with the NT5 Protected Storage service that causes the client-side
|
|
// containers to fail after NT5 GUI Setup. Apparently, there is some
|
|
// as-yet unidentified interaction between creating the client-side
|
|
// container under NT5 GUI Setup (which runs in the Local System
|
|
// security context) and later attempts to create containers in
|
|
// lesser security contexts.
|
|
//
|
|
|
|
if( !( dwAdditionalFlags & CRYPT_MACHINE_KEYSET ) ) {
|
|
if( AmIRunningInTheLocalSystemContext() ) {
|
|
dwAdditionalFlags |= CRYPT_MACHINE_KEYSET;
|
|
fApplyAcl = TRUE;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Step 1: Just try to open/create the container.
|
|
//
|
|
|
|
result = ::IISCryptoGetContainerByName(
|
|
phProv,
|
|
pszContainerName,
|
|
dwAdditionalFlags,
|
|
fApplyAcl
|
|
);
|
|
|
|
if( SUCCEEDED(result) ) {
|
|
goto complete;
|
|
}
|
|
|
|
//
|
|
// NTE_BAD_KEYSET is typically returned when the caller has
|
|
// insufficient privilege to access the key container registry
|
|
// tree. If we failed for any other reason, bail.
|
|
//
|
|
|
|
if( result != NTE_BAD_KEYSET ) {
|
|
goto complete;
|
|
}
|
|
|
|
//
|
|
// Step 2: If the caller didn't ask for CRYPT_MACHINE_KEYSET, then
|
|
// retry the operation with this flag set.
|
|
//
|
|
|
|
if( ( dwAdditionalFlags & CRYPT_MACHINE_KEYSET ) == 0 ) {
|
|
|
|
result = ::IISCryptoGetContainerByName(
|
|
phProv,
|
|
pszContainerName,
|
|
dwAdditionalFlags | CRYPT_MACHINE_KEYSET,
|
|
fApplyAcl
|
|
);
|
|
|
|
if( SUCCEEDED(result) ) {
|
|
goto complete;
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
// OK, now things get a bit complex.
|
|
//
|
|
// If the current thread has an impersonation token, then
|
|
// (temporarily) remove it and retry the container operation.
|
|
// This is mainly here so that ISAPI applications (like, say, ASP)
|
|
// can access the DCOM interface while impersonating a non-privileged
|
|
// security context.
|
|
//
|
|
// Note that, after we remove the impersonation token, we first try
|
|
// the operation with CRYPT_MACHINE_KEYSET ORed in. We do this on
|
|
// the assumption that, if the thread had an impersonation token,
|
|
// then we're probably running in the context of a server process.
|
|
// If this operation fails, we try again without forcing the flag.
|
|
//
|
|
|
|
result = GetThreadImpersonationToken( &token );
|
|
|
|
if( FAILED(result) ) {
|
|
goto complete;
|
|
}
|
|
|
|
if( token != NULL ) {
|
|
|
|
result = SetThreadImpersonationToken( NULL );
|
|
|
|
if( FAILED(result) ) {
|
|
goto complete;
|
|
}
|
|
|
|
resetToken = TRUE;
|
|
|
|
//
|
|
// Step 3: With the token removed, retry the operation with the
|
|
// CRYPT_MACHINE_KEYSET flag set if not already set.
|
|
//
|
|
|
|
if( ( dwAdditionalFlags & CRYPT_MACHINE_KEYSET ) == 0 ) {
|
|
|
|
result = ::IISCryptoGetContainerByName(
|
|
phProv,
|
|
pszContainerName,
|
|
dwAdditionalFlags | CRYPT_MACHINE_KEYSET,
|
|
fApplyAcl
|
|
);
|
|
|
|
if( SUCCEEDED(result) ) {
|
|
goto complete;
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
// Step 4: With the token removed, try to open/create the container.
|
|
//
|
|
|
|
result = ::IISCryptoGetContainerByName(
|
|
phProv,
|
|
pszContainerName,
|
|
dwAdditionalFlags,
|
|
fApplyAcl
|
|
);
|
|
|
|
if( SUCCEEDED(result) ) {
|
|
goto complete;
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
// If we made it this far, then the container cannot be opened.
|
|
//
|
|
|
|
result = NTE_BAD_KEYSET;
|
|
|
|
complete:
|
|
|
|
if( resetToken ) {
|
|
|
|
HRESULT result2;
|
|
|
|
DBG_ASSERT( token != NULL );
|
|
result2 = SetThreadImpersonationToken( token );
|
|
|
|
if( FAILED(result2) ) {
|
|
|
|
//
|
|
// This is really, really, really bad news. The current
|
|
// thread, which does not have an impersonation token
|
|
// (and is therefore running in the system context)
|
|
// cannot reset its impersonation token to the original
|
|
// value.
|
|
//
|
|
|
|
DBG_ASSERT( SUCCEEDED( result2 ) );
|
|
}
|
|
|
|
}
|
|
|
|
if( token != NULL ) {
|
|
DBG_REQUIRE( CloseHandle( token ) );
|
|
}
|
|
|
|
return result;
|
|
|
|
} // IIS_CRYPTO_BASE::GetCryptoContainerByName
|
|
|
|
|
|
HRESULT
|
|
IIS_CRYPTO_BASE::Initialize(
|
|
IN HCRYPTPROV hProv,
|
|
IN HCRYPTKEY hKeyExchangeKey,
|
|
IN HCRYPTKEY hSignatureKey,
|
|
IN BOOL fUseMachineKeyset
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Performs any complex initialization.
|
|
|
|
Arguments:
|
|
|
|
hProv - Optional pre-opened handle to a crypto provider. If this
|
|
is not present, then the standard container is opened. If this
|
|
is present, then it is used and it is the responsibility of the
|
|
caller to close the handle when no longer needed.
|
|
|
|
hKeyExchangeKey - Optional pre-opened handle to a key exchange key. If
|
|
this is not present, then the local key exchange key is used.
|
|
|
|
hSignatureKey - Optional pre-opened handle to a signature key. If this
|
|
is not present, then the local signature key is used.
|
|
|
|
fUseMachineKeyset - TRUE if the per-machine keyset container should
|
|
be used, as opposed to the per-user keyset container.
|
|
|
|
Return Value:
|
|
|
|
HRESULT - Completion status, 0 if successful, !0 otherwise.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
HRESULT result;
|
|
|
|
//
|
|
// Sanity check.
|
|
//
|
|
|
|
DBG_ASSERT( m_hProv == CRYPT_NULL );
|
|
DBG_ASSERT( m_hKeyExchangeKey == CRYPT_NULL );
|
|
DBG_ASSERT( m_hSignatureKey == CRYPT_NULL );
|
|
|
|
IcpAcquireGlobalLock();
|
|
|
|
if( hProv == CRYPT_NULL ) {
|
|
|
|
//
|
|
// Open the container.
|
|
//
|
|
|
|
result = ::IISCryptoGetStandardContainer(
|
|
&m_hProv,
|
|
fUseMachineKeyset
|
|
? CRYPT_MACHINE_KEYSET
|
|
: 0
|
|
);
|
|
|
|
m_fCloseProv = TRUE;
|
|
|
|
} else {
|
|
|
|
m_hProv = hProv;
|
|
m_fCloseProv = FALSE;
|
|
result = NO_ERROR;
|
|
|
|
}
|
|
|
|
if( SUCCEEDED(result) ) {
|
|
|
|
if( hKeyExchangeKey == CRYPT_NULL ) {
|
|
|
|
//
|
|
// Get the key exchange key.
|
|
//
|
|
|
|
result = ::IISCryptoGetKeyExchangeKey(
|
|
&m_hKeyExchangeKey,
|
|
m_hProv
|
|
);
|
|
|
|
} else {
|
|
|
|
m_hKeyExchangeKey = hKeyExchangeKey;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if( SUCCEEDED(result) ) {
|
|
|
|
if( hSignatureKey == CRYPT_NULL ) {
|
|
|
|
//
|
|
// Get the signature key.
|
|
//
|
|
|
|
result = ::IISCryptoGetSignatureKey(
|
|
&m_hSignatureKey,
|
|
m_hProv
|
|
);
|
|
|
|
} else {
|
|
|
|
m_hSignatureKey = hSignatureKey;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
IcpReleaseGlobalLock();
|
|
|
|
return result;
|
|
|
|
} // IIS_CRYPTO_BASE::Initialize
|
|
|
|
HRESULT
|
|
IIS_CRYPTO_BASE::Initialize2(
|
|
IN HCRYPTPROV hProv
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Performs any complex initialization.
|
|
|
|
Arguments:
|
|
|
|
hProv - Optional pre-opened handle to a crypto provider. If this
|
|
is not present, then the standard container is opened. If this
|
|
is present, then it is used and it is the responsibility of the
|
|
caller to close the handle when no longer needed.
|
|
|
|
Return Value:
|
|
|
|
HRESULT - Completion status, 0 if successful, !0 otherwise.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
HRESULT result;
|
|
|
|
//
|
|
// Sanity check.
|
|
//
|
|
|
|
DBG_ASSERT( m_hProv == CRYPT_NULL );
|
|
DBG_ASSERT( m_hKeyExchangeKey == CRYPT_NULL );
|
|
DBG_ASSERT( m_hSignatureKey == CRYPT_NULL );
|
|
|
|
IcpAcquireGlobalLock();
|
|
|
|
if( hProv == CRYPT_NULL ) {
|
|
|
|
//
|
|
// Open the container.
|
|
//
|
|
|
|
result = ::IISCryptoGetStandardContainer2(
|
|
&m_hProv
|
|
);
|
|
|
|
m_fCloseProv = TRUE;
|
|
|
|
} else {
|
|
|
|
m_hProv = hProv;
|
|
m_fCloseProv = FALSE;
|
|
result = NO_ERROR;
|
|
|
|
}
|
|
|
|
IcpReleaseGlobalLock();
|
|
|
|
return result;
|
|
|
|
} // IIS_CRYPTO_BASE::Initialize2
|
|
|
|
|
|
HRESULT
|
|
IIS_CRYPTO_BASE::GetKeyExchangeKeyBlob(
|
|
OUT PIIS_CRYPTO_BLOB * ppKeyExchangeKeyBlob
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Exports the key exchange key as a public blob.
|
|
|
|
Arguments:
|
|
|
|
ppKeyExchangeKeyBlob - Receives a pointer to the key exchange key
|
|
public blob if successful.
|
|
|
|
Return Value:
|
|
|
|
HRESULT - Completion status, 0 if successful, !0 otherwise.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
HRESULT result;
|
|
|
|
//
|
|
// Sanity check.
|
|
//
|
|
|
|
DBG_ASSERT( ValidateState() );
|
|
DBG_ASSERT( ppKeyExchangeKeyBlob != NULL );
|
|
|
|
//
|
|
// Let the IIS Crypto APIs do the dirty work.
|
|
//
|
|
|
|
result = ::IISCryptoExportPublicKeyBlob(
|
|
ppKeyExchangeKeyBlob,
|
|
m_hProv,
|
|
m_hKeyExchangeKey
|
|
);
|
|
|
|
return result;
|
|
|
|
} // IIS_CRYPTO_BASE::GetKeyExchangeKeyBlob
|
|
|
|
|
|
HRESULT
|
|
IIS_CRYPTO_BASE::GetSignatureKeyBlob(
|
|
OUT PIIS_CRYPTO_BLOB * ppSignatureKeyBlob
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Exports the signature key as a public blob.
|
|
|
|
Arguments:
|
|
|
|
ppSignatureKeyBlob - Receives a pointer to the signature key
|
|
public blob if successful.
|
|
|
|
Return Value:
|
|
|
|
HRESULT - Completion status, 0 if successful, !0 otherwise.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
HRESULT result;
|
|
|
|
//
|
|
// Sanity check.
|
|
//
|
|
|
|
DBG_ASSERT( ValidateState() );
|
|
DBG_ASSERT( ppSignatureKeyBlob != NULL );
|
|
|
|
//
|
|
// Let the IIS Crypto APIs do the dirty work.
|
|
//
|
|
|
|
result = ::IISCryptoExportPublicKeyBlob(
|
|
ppSignatureKeyBlob,
|
|
m_hProv,
|
|
m_hSignatureKey
|
|
);
|
|
|
|
return result;
|
|
|
|
} // IIS_CRYPTO_BASE::GetSignatureKeyBlob
|
|
|
|
|
|
//
|
|
// Private functions.
|
|
//
|
|
|
|
|
|
#if DBG
|
|
|
|
BOOL
|
|
IIS_CRYPTO_BASE::ValidateState()
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This debug-only routine validates the current object state.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
BOOL - TRUE if state is valid, FALSE otherwise.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
if( m_hProv != CRYPT_NULL &&
|
|
m_hKeyExchangeKey != CRYPT_NULL &&
|
|
m_hSignatureKey != CRYPT_NULL ) {
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
return FALSE;
|
|
|
|
} // IIS_CRYPTO_BASE::ValidateState
|
|
|
|
BOOL
|
|
IIS_CRYPTO_BASE::ValidateState2()
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This debug-only routine validates the current object state.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
BOOL - TRUE if state is valid, FALSE otherwise.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
if( m_hProv != CRYPT_NULL )
|
|
{
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
|
|
} // IIS_CRYPTO_BASE::ValidateState2
|
|
|
|
#endif // DBG
|
|
|
|
|
|
HRESULT
|
|
IIS_CRYPTO_BASE::SafeImportSessionKeyBlob(
|
|
OUT HCRYPTKEY * phSessionKey,
|
|
IN PIIS_CRYPTO_BLOB pSessionKeyBlob,
|
|
IN HCRYPTPROV hProv,
|
|
IN HCRYPTKEY hSignatureKey
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine takes the specified session key blob and creates the
|
|
corresponding session key, iff the encrypted session key can be
|
|
decrypted and the digital signature can be validated.
|
|
|
|
Arguments:
|
|
|
|
phSessionKey - Receives a pointer to the newly created session key
|
|
if successful.
|
|
|
|
pSessionKeyBlob - Pointer to a key blob created with
|
|
IISCryptoExportSessionKeyBlob().
|
|
|
|
hProv - A handle to a crypto service provider.
|
|
|
|
hSignatureKey - Handle to the encryption key to use when validating
|
|
the digital signature.
|
|
|
|
Return Value:
|
|
|
|
HRESULT - Completion status, 0 if successful, !0 otherwise.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
HRESULT result;
|
|
HCRYPTKEY sessionKey = CRYPT_NULL;
|
|
HANDLE token = NULL;
|
|
|
|
//
|
|
// First, just call through to wrapper function. If it succeeds, cool.
|
|
//
|
|
|
|
result = ::IISCryptoImportSessionKeyBlob(
|
|
&sessionKey,
|
|
pSessionKeyBlob,
|
|
hProv,
|
|
hSignatureKey
|
|
);
|
|
|
|
if( FAILED(result) ) {
|
|
|
|
HRESULT result2;
|
|
|
|
//
|
|
// Bummer. If the current thread has an impersonation token, then
|
|
// temporarily remove it and retry the operation.
|
|
//
|
|
|
|
result2 = GetThreadImpersonationToken( &token );
|
|
|
|
if( SUCCEEDED(result2) && token != NULL ) {
|
|
|
|
result2 = SetThreadImpersonationToken( NULL );
|
|
|
|
if( SUCCEEDED(result2) ) {
|
|
|
|
result2 = ::IISCryptoImportSessionKeyBlob(
|
|
&sessionKey,
|
|
pSessionKeyBlob,
|
|
hProv,
|
|
hSignatureKey
|
|
);
|
|
|
|
if( SUCCEEDED(result2) ) {
|
|
result = result2;
|
|
}
|
|
|
|
//
|
|
// Restore the original impersonation token.
|
|
//
|
|
|
|
result2 = SetThreadImpersonationToken( token );
|
|
DBG_ASSERT( SUCCEEDED(result2) );
|
|
|
|
}
|
|
|
|
//
|
|
// Close the token handle.
|
|
//
|
|
|
|
DBG_REQUIRE( CloseHandle( token ) );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if( SUCCEEDED(result) ) {
|
|
DBG_ASSERT( sessionKey != NULL );
|
|
*phSessionKey = sessionKey;
|
|
}
|
|
|
|
return result;
|
|
|
|
} // IIS_CRYPTO_BASE::SafeImportSessionKeyBlob
|
|
|
|
HRESULT
|
|
IIS_CRYPTO_BASE::SafeImportSessionKeyBlob2(
|
|
OUT HCRYPTKEY * phSessionKey,
|
|
IN PIIS_CRYPTO_BLOB pSessionKeyBlob,
|
|
IN HCRYPTPROV hProv,
|
|
IN LPSTR pszPasswd
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine takes the specified session key blob and creates the
|
|
corresponding session key, iff the encrypted session key can be
|
|
decrypted.
|
|
|
|
Arguments:
|
|
|
|
phSessionKey - Receives a pointer to the newly created session key
|
|
if successful.
|
|
|
|
pSessionKeyBlob - Pointer to a key blob created with
|
|
IISCryptoExportSessionKeyBlob().
|
|
|
|
hProv - A handle to a crypto service provider.
|
|
|
|
pszPasswd - The password to use to encrypt the session key.
|
|
|
|
Return Value:
|
|
|
|
HRESULT - Completion status, 0 if successful, !0 otherwise.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
HRESULT result;
|
|
HCRYPTKEY sessionKey = CRYPT_NULL;
|
|
HANDLE token = NULL;
|
|
|
|
//
|
|
// First, just call through to wrapper function. If it succeeds, cool.
|
|
//
|
|
|
|
result = ::IISCryptoImportSessionKeyBlob2(
|
|
&sessionKey,
|
|
pSessionKeyBlob,
|
|
hProv,
|
|
pszPasswd
|
|
);
|
|
|
|
if( FAILED(result) ) {
|
|
|
|
HRESULT result2;
|
|
|
|
//
|
|
// Bummer. If the current thread has an impersonation token, then
|
|
// temporarily remove it and retry the operation.
|
|
//
|
|
|
|
result2 = GetThreadImpersonationToken( &token );
|
|
|
|
if( SUCCEEDED(result2) && token != NULL ) {
|
|
|
|
result2 = SetThreadImpersonationToken( NULL );
|
|
|
|
if( SUCCEEDED(result2) ) {
|
|
|
|
result2 = ::IISCryptoImportSessionKeyBlob2(
|
|
&sessionKey,
|
|
pSessionKeyBlob,
|
|
hProv,
|
|
pszPasswd
|
|
);
|
|
|
|
if( SUCCEEDED(result2) ) {
|
|
result = result2;
|
|
}
|
|
|
|
//
|
|
// Restore the original impersonation token.
|
|
//
|
|
|
|
result2 = SetThreadImpersonationToken( token );
|
|
DBG_ASSERT( SUCCEEDED(result2) );
|
|
|
|
}
|
|
|
|
//
|
|
// Close the token handle.
|
|
//
|
|
|
|
DBG_REQUIRE( CloseHandle( token ) );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if( SUCCEEDED(result) ) {
|
|
DBG_ASSERT( sessionKey != NULL );
|
|
*phSessionKey = sessionKey;
|
|
}
|
|
|
|
return result;
|
|
|
|
} // IIS_CRYPTO_BASE::SafeImportSessionKeyBlob2
|
|
|
|
|
|
HRESULT
|
|
IIS_CRYPTO_BASE::SafeExportSessionKeyBlob(
|
|
OUT PIIS_CRYPTO_BLOB * ppSessionKeyBlob,
|
|
IN HCRYPTPROV hProv,
|
|
IN HCRYPTKEY hSessionKey,
|
|
IN HCRYPTKEY hKeyExchangeKey
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine exports a session key into a secure session key blob.
|
|
The blob contains the session key (encrypted with the specified
|
|
private key exchange key) and a digital signature (also encrypted).
|
|
|
|
Arguments:
|
|
|
|
ppSessionKeyBlob - Will receive a pointer to the newly created
|
|
session key blob if successful.
|
|
|
|
hProv - A handle to a crypto service provider.
|
|
|
|
hSessionKey - The key to export.
|
|
|
|
hKeyExchangeKey - The key to use when encrypting the session key.
|
|
|
|
Return Value:
|
|
|
|
HRESULT - Completion status, 0 if successful, !0 otherwise.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
HRESULT result;
|
|
PIIS_CRYPTO_BLOB sessionKeyBlob = NULL;
|
|
HANDLE token = NULL;
|
|
|
|
//
|
|
// First, just call through to wrapper function. If it succeeds, cool.
|
|
//
|
|
|
|
result = ::IISCryptoExportSessionKeyBlob(
|
|
&sessionKeyBlob,
|
|
hProv,
|
|
hSessionKey,
|
|
hKeyExchangeKey
|
|
);
|
|
|
|
if( FAILED(result) ) {
|
|
|
|
HRESULT result2;
|
|
|
|
//
|
|
// Bummer. If the current thread has an impersonation token, then
|
|
// temporarily remove it and retry the operation.
|
|
//
|
|
|
|
result2 = GetThreadImpersonationToken( &token );
|
|
|
|
if( SUCCEEDED(result2) && token != NULL ) {
|
|
|
|
result2 = SetThreadImpersonationToken( NULL );
|
|
|
|
if( SUCCEEDED(result2) ) {
|
|
|
|
result2 = ::IISCryptoExportSessionKeyBlob(
|
|
&sessionKeyBlob,
|
|
hProv,
|
|
hSessionKey,
|
|
hKeyExchangeKey
|
|
);
|
|
|
|
if( SUCCEEDED(result2) ) {
|
|
result = result2;
|
|
}
|
|
|
|
//
|
|
// Restore the original impersonation token.
|
|
//
|
|
|
|
result2 = SetThreadImpersonationToken( token );
|
|
DBG_ASSERT( SUCCEEDED(result2) );
|
|
|
|
}
|
|
|
|
//
|
|
// Close the token handle.
|
|
//
|
|
|
|
DBG_REQUIRE( CloseHandle( token ) );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if( SUCCEEDED(result) ) {
|
|
DBG_ASSERT( sessionKeyBlob != NULL );
|
|
*ppSessionKeyBlob = sessionKeyBlob;
|
|
}
|
|
|
|
return result;
|
|
|
|
} // IIS_CRYPTO_BASE::SafeExportSessionKeyBlob
|
|
|
|
HRESULT
|
|
IIS_CRYPTO_BASE::SafeExportSessionKeyBlob2(
|
|
OUT PIIS_CRYPTO_BLOB * ppSessionKeyBlob,
|
|
IN HCRYPTPROV hProv,
|
|
IN HCRYPTKEY hSessionKey,
|
|
IN LPSTR pszPasswd
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine exports a session key into a secure session key blob.
|
|
The blob contains the session key (encrypted with the specified
|
|
password) and a digital signature (also encrypted).
|
|
|
|
Arguments:
|
|
|
|
ppSessionKeyBlob - Will receive a pointer to the newly created
|
|
session key blob if successful.
|
|
|
|
hProv - A handle to a crypto service provider.
|
|
|
|
hSessionKey - The key to export.
|
|
|
|
pszPasswd - The password to use to encrypt the session key.
|
|
|
|
Return Value:
|
|
|
|
HRESULT - Completion status, 0 if successful, !0 otherwise.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
HRESULT result;
|
|
PIIS_CRYPTO_BLOB sessionKeyBlob = NULL;
|
|
HANDLE token = NULL;
|
|
|
|
//
|
|
// First, just call through to wrapper function. If it succeeds, cool.
|
|
//
|
|
|
|
result = ::IISCryptoExportSessionKeyBlob2(
|
|
&sessionKeyBlob,
|
|
hProv,
|
|
hSessionKey,
|
|
pszPasswd
|
|
);
|
|
|
|
if( FAILED(result) ) {
|
|
|
|
HRESULT result2;
|
|
|
|
//
|
|
// Bummer. If the current thread has an impersonation token, then
|
|
// temporarily remove it and retry the operation.
|
|
//
|
|
|
|
result2 = GetThreadImpersonationToken( &token );
|
|
|
|
if( SUCCEEDED(result2) && token != NULL ) {
|
|
|
|
result2 = SetThreadImpersonationToken( NULL );
|
|
|
|
if( SUCCEEDED(result2) ) {
|
|
|
|
result2 = ::IISCryptoExportSessionKeyBlob2(
|
|
&sessionKeyBlob,
|
|
hProv,
|
|
hSessionKey,
|
|
pszPasswd
|
|
);
|
|
|
|
if( SUCCEEDED(result2) ) {
|
|
result = result2;
|
|
}
|
|
|
|
//
|
|
// Restore the original impersonation token.
|
|
//
|
|
|
|
result2 = SetThreadImpersonationToken( token );
|
|
DBG_ASSERT( SUCCEEDED(result2) );
|
|
|
|
}
|
|
|
|
//
|
|
// Close the token handle.
|
|
//
|
|
|
|
DBG_REQUIRE( CloseHandle( token ) );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if( SUCCEEDED(result) ) {
|
|
DBG_ASSERT( sessionKeyBlob != NULL );
|
|
*ppSessionKeyBlob = sessionKeyBlob;
|
|
}
|
|
|
|
return result;
|
|
|
|
} // IIS_CRYPTO_BASE::SafeExportSessionKeyBlob
|
|
|
|
HRESULT
|
|
IIS_CRYPTO_BASE::SafeEncryptDataBlob(
|
|
OUT PIIS_CRYPTO_BLOB * ppDataBlob,
|
|
IN PVOID pBuffer,
|
|
IN DWORD dwBufferLength,
|
|
IN DWORD dwRegType,
|
|
IN HCRYPTPROV hProv,
|
|
IN HCRYPTKEY hSessionKey
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine encrypts a block of data, resulting in a data blob.
|
|
The data blob contains the encrypted data and a digital signature
|
|
validating the data.
|
|
|
|
Arguments:
|
|
|
|
ppDataBlob - Receives a pointer to the newly created data blob if
|
|
successful.
|
|
|
|
pBuffer - The buffer to encrypt.
|
|
|
|
dwBufferLength - The length of the buffer.
|
|
|
|
dwRegType - The REG_* type to associate with this data.
|
|
|
|
hProv - A handle to a crypto service provider.
|
|
|
|
hSessionKey - The key used to encrypt the data.
|
|
|
|
Return Value:
|
|
|
|
HRESULT - Completion status, 0 if successful, !0 otherwise.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
HRESULT result;
|
|
PIIS_CRYPTO_BLOB dataBlob = NULL;
|
|
HANDLE token = NULL;
|
|
|
|
//
|
|
// First, just call through to wrapper function. If it succeeds, cool.
|
|
//
|
|
|
|
result = ::IISCryptoEncryptDataBlob(
|
|
&dataBlob,
|
|
pBuffer,
|
|
dwBufferLength,
|
|
dwRegType,
|
|
hProv,
|
|
hSessionKey
|
|
);
|
|
|
|
if( FAILED(result) ) {
|
|
|
|
HRESULT result2;
|
|
|
|
//
|
|
// Bummer. If the current thread has an impersonation token, then
|
|
// temporarily remove it and retry the operation.
|
|
//
|
|
|
|
result2 = GetThreadImpersonationToken( &token );
|
|
|
|
if( SUCCEEDED(result2) && token != NULL ) {
|
|
|
|
result2 = SetThreadImpersonationToken( NULL );
|
|
|
|
if( SUCCEEDED(result2) ) {
|
|
|
|
result2 = ::IISCryptoEncryptDataBlob(
|
|
&dataBlob,
|
|
pBuffer,
|
|
dwBufferLength,
|
|
dwRegType,
|
|
hProv,
|
|
hSessionKey
|
|
);
|
|
|
|
if( SUCCEEDED(result2) ) {
|
|
result = result2;
|
|
}
|
|
|
|
//
|
|
// Restore the original impersonation token.
|
|
//
|
|
|
|
result2 = SetThreadImpersonationToken( token );
|
|
DBG_ASSERT( SUCCEEDED(result2) );
|
|
|
|
}
|
|
|
|
//
|
|
// Close the token handle.
|
|
//
|
|
|
|
DBG_REQUIRE( CloseHandle( token ) );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if( SUCCEEDED(result) ) {
|
|
DBG_ASSERT( dataBlob != NULL );
|
|
*ppDataBlob = dataBlob;
|
|
}
|
|
|
|
return result;
|
|
|
|
} // IIS_CRYPTO_BASE::SafeEncryptDataBlob
|
|
|
|
HRESULT
|
|
IIS_CRYPTO_BASE::SafeEncryptDataBlob2(
|
|
OUT PIIS_CRYPTO_BLOB * ppDataBlob,
|
|
IN PVOID pBuffer,
|
|
IN DWORD dwBufferLength,
|
|
IN DWORD dwRegType,
|
|
IN HCRYPTPROV hProv,
|
|
IN HCRYPTKEY hSessionKey
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine encrypts a block of data, resulting in a data blob.
|
|
The data blob contains the encrypted data and a digital signature
|
|
validating the data.
|
|
|
|
Arguments:
|
|
|
|
ppDataBlob - Receives a pointer to the newly created data blob if
|
|
successful.
|
|
|
|
pBuffer - The buffer to encrypt.
|
|
|
|
dwBufferLength - The length of the buffer.
|
|
|
|
dwRegType - The REG_* type to associate with this data.
|
|
|
|
hProv - A handle to a crypto service provider.
|
|
|
|
hSessionKey - The key used to encrypt the data.
|
|
|
|
Return Value:
|
|
|
|
HRESULT - Completion status, 0 if successful, !0 otherwise.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
HRESULT result;
|
|
PIIS_CRYPTO_BLOB dataBlob = NULL;
|
|
HANDLE token = NULL;
|
|
|
|
//
|
|
// First, just call through to wrapper function. If it succeeds, cool.
|
|
//
|
|
|
|
result = ::IISCryptoEncryptDataBlob2(
|
|
&dataBlob,
|
|
pBuffer,
|
|
dwBufferLength,
|
|
dwRegType,
|
|
hProv,
|
|
hSessionKey
|
|
);
|
|
|
|
if( FAILED(result) ) {
|
|
|
|
HRESULT result2;
|
|
|
|
//
|
|
// Bummer. If the current thread has an impersonation token, then
|
|
// temporarily remove it and retry the operation.
|
|
//
|
|
|
|
result2 = GetThreadImpersonationToken( &token );
|
|
|
|
if( SUCCEEDED(result2) && token != NULL ) {
|
|
|
|
result2 = SetThreadImpersonationToken( NULL );
|
|
|
|
if( SUCCEEDED(result2) ) {
|
|
|
|
result2 = ::IISCryptoEncryptDataBlob2(
|
|
&dataBlob,
|
|
pBuffer,
|
|
dwBufferLength,
|
|
dwRegType,
|
|
hProv,
|
|
hSessionKey
|
|
);
|
|
|
|
if( SUCCEEDED(result2) ) {
|
|
result = result2;
|
|
}
|
|
|
|
//
|
|
// Restore the original impersonation token.
|
|
//
|
|
|
|
result2 = SetThreadImpersonationToken( token );
|
|
DBG_ASSERT( SUCCEEDED(result2) );
|
|
|
|
}
|
|
|
|
//
|
|
// Close the token handle.
|
|
//
|
|
|
|
DBG_REQUIRE( CloseHandle( token ) );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if( SUCCEEDED(result) ) {
|
|
DBG_ASSERT( dataBlob != NULL );
|
|
*ppDataBlob = dataBlob;
|
|
}
|
|
|
|
return result;
|
|
|
|
} // IIS_CRYPTO_BASE::SafeEncryptDataBlob2
|
|
|
|
HRESULT
|
|
IIS_CRYPTO_BASE::GetThreadImpersonationToken(
|
|
OUT HANDLE * Token
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Gets the impersonation token for the current thread.
|
|
|
|
Arguments:
|
|
|
|
Token - Receives a handle to the impersonation token if successful.
|
|
If successful, it is the caller's responsibility to CloseHandle()
|
|
the token when no longer needed.
|
|
|
|
Return Value:
|
|
|
|
HRESULT - 0 if successful, !0 otherwise.
|
|
|
|
--*/
|
|
{
|
|
|
|
HRESULT result = NO_ERROR;
|
|
|
|
DBG_ASSERT( Token != NULL );
|
|
|
|
//
|
|
// Open the token.
|
|
//
|
|
|
|
if( !OpenThreadToken(
|
|
GetCurrentThread(),
|
|
TOKEN_ALL_ACCESS,
|
|
TRUE,
|
|
Token
|
|
) ) {
|
|
|
|
DWORD err = GetLastError();
|
|
|
|
//
|
|
// There are a couple of "expected" errors here:
|
|
//
|
|
// ERROR_NO_TOKEN - The thread has no impersonation token.
|
|
// ERROR_CALL_NOT_IMPLEMENTED - We're probably on Win9x.
|
|
// ERROR_NOT_SUPPORTED - Ditto.
|
|
//
|
|
// If OpenThreadToken() failed with any of the above error codes,
|
|
// then succeed the call, but return a NULL token handle.
|
|
//
|
|
|
|
if( err != ERROR_NO_TOKEN &&
|
|
err != ERROR_CALL_NOT_IMPLEMENTED &&
|
|
err != ERROR_NOT_SUPPORTED ) {
|
|
|
|
result = HRESULT_FROM_WIN32(err);
|
|
|
|
}
|
|
|
|
*Token = NULL;
|
|
}
|
|
|
|
return result;
|
|
|
|
} // IIS_CRYPTO_BASE::GetThreadImpersonationToken
|
|
|
|
|
|
HRESULT
|
|
IIS_CRYPTO_BASE::SetThreadImpersonationToken(
|
|
IN HANDLE Token
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Sets the impersonation token for the current thread.
|
|
|
|
Arguments:
|
|
|
|
Token - A handle to the impersonation token.
|
|
|
|
Return Value:
|
|
|
|
HRESULT - 0 if successful, !0 otherwise.
|
|
|
|
--*/
|
|
{
|
|
|
|
HRESULT result = NO_ERROR;
|
|
|
|
//
|
|
// Set it.
|
|
//
|
|
|
|
if( !SetThreadToken(
|
|
NULL,
|
|
Token
|
|
) ) {
|
|
DWORD err = GetLastError();
|
|
result = HRESULT_FROM_WIN32(err);
|
|
}
|
|
|
|
return result;
|
|
|
|
} // IIS_CRYPTO_BASE::SetThreadImpersonationToken
|
|
|
|
|
|
BOOL
|
|
IIS_CRYPTO_BASE::AmIRunningInTheLocalSystemContext(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Determines if the current process is running in the local system
|
|
security context.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
BOOL - TRUE if we're running in the local system context, FALSE
|
|
otherwise.
|
|
|
|
--*/
|
|
{
|
|
|
|
BOOL result;
|
|
HANDLE token;
|
|
DWORD lengthRequired;
|
|
PTOKEN_USER tokenInfo;
|
|
PSID systemSid;
|
|
SID_IDENTIFIER_AUTHORITY ntAuthority = SECURITY_NT_AUTHORITY;
|
|
|
|
//
|
|
// Setup local so we know how to cleanup on exit.
|
|
//
|
|
|
|
result = FALSE; // until proven otherwise...
|
|
token = NULL;
|
|
tokenInfo = NULL;
|
|
systemSid = NULL;
|
|
|
|
//
|
|
// Get a handle to the current process token.
|
|
//
|
|
|
|
if( !OpenProcessToken(
|
|
GetCurrentProcess(), // ProcessHandle
|
|
TOKEN_READ, // DesiredAccess
|
|
&token // TokenHandle
|
|
) ) {
|
|
goto cleanup;
|
|
}
|
|
|
|
//
|
|
// Determine the length of the token information, then allocate
|
|
// a buffer and read the info.
|
|
//
|
|
|
|
if( !GetTokenInformation(
|
|
token, // TokenHandle
|
|
TokenUser, // TokenInformationClass
|
|
NULL, // TokenInformation
|
|
0, // TokenInformationLength
|
|
&lengthRequired // ReturnLength
|
|
) ) {
|
|
goto cleanup;
|
|
}
|
|
|
|
tokenInfo = (PTOKEN_USER)new char[lengthRequired];
|
|
|
|
if( tokenInfo == NULL ) {
|
|
goto cleanup;
|
|
}
|
|
|
|
if( !GetTokenInformation(
|
|
token, // TokenHandle
|
|
TokenUser, // TokenInformationClass
|
|
tokenInfo, // TokenInformation
|
|
lengthRequired, // TokenInformationLength
|
|
&lengthRequired // ReturnLength
|
|
) ) {
|
|
goto cleanup;
|
|
}
|
|
|
|
//
|
|
// OK, we have the token. Now build the system SID so we can compare
|
|
// it with the one stored in the token.
|
|
//
|
|
|
|
if( !AllocateAndInitializeSid(
|
|
&ntAuthority, // pIdentifierAuthority
|
|
1, // nSubAuthorityCount
|
|
SECURITY_LOCAL_SYSTEM_RID, // nSubAuthority0
|
|
0, // nSubAuthority1
|
|
0, // nSubAuthority2
|
|
0, // nSubAuthority3
|
|
0, // nSubAuthority4
|
|
0, // nSubAuthority5
|
|
0, // nSubAuthority6
|
|
0, // nSubAuthority7
|
|
&systemSid // pSid
|
|
) ) {
|
|
goto cleanup;
|
|
}
|
|
|
|
//
|
|
// Now that we have the SID from the token and our hand-built
|
|
// local system SID, we can compare them.
|
|
//
|
|
|
|
result = EqualSid(
|
|
tokenInfo->User.Sid, // pSid1
|
|
systemSid // pSid2
|
|
);
|
|
|
|
cleanup:
|
|
|
|
if( systemSid != NULL ) {
|
|
FreeSid( systemSid );
|
|
}
|
|
|
|
if( tokenInfo != NULL ) {
|
|
delete[] tokenInfo;
|
|
}
|
|
|
|
if( token != NULL ) {
|
|
CloseHandle( token );
|
|
}
|
|
|
|
return result;
|
|
|
|
} // IIS_CRYPTO_BASE::AmIRunningInTheLocalSystemContext
|
|
|