Leaked source code of windows server 2003
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.
 
 
 
 
 
 

3098 lines
88 KiB

/*++
Copyright (C) Microsoft Corporation, 1991 - 1999
Module Name:
secclnt.cxx
Abstract:
Implementation of security objects.
Author:
Michael Montague (mikemon) 11-Apr-1992
Revision History:
21-Feb-1997 jroberts Added some SSL support.
--*/
#include <precomp.hxx>
#include <wincrypt.h>
#include <rpcssl.h>
#include <cryptimp.hxx>
#include <rpccfg.h>
#include <spseal.h>
#include <schnlsp.h>
#include <hndlsvr.hxx>
int SecuritySupportLoaded = 0;
int FailedToLoad = 0;
unsigned long NumberOfProviders = 0;
unsigned long LoadedProviders = 0;
unsigned long AvailableProviders = 0;
SECURITY_PROVIDER_INFO PAPI * ProviderList = 0;
MUTEX * SecurityCritSect; // Mutex for the Server credentials cache.
// Incremented each time we leak SECURITY_CONTEXT::SecurityContext or
// SECURITY_CREDENTIALS::CredentialsHandle due to DeleteSecurityContext
// or FreeCredentialHandle returning SEC_E_INSUFFICIENT_MEMORY.
unsigned int nSecurityStructuresLeaked = 0;
struct PACKAGE_LEG_INFO
{
DWORD Package;
PACKAGE_LEG_COUNT Legs;
};
const PACKAGE_LEG_INFO PredefinedPackageLegInfo[] =
{
{ RPC_C_AUTHN_WINNT, ThreeLegs },
{ RPC_C_AUTHN_GSS_NEGOTIATE,EvenNumberOfLegs },
{ RPC_C_AUTHN_GSS_KERBEROS, EvenNumberOfLegs },
{ RPC_C_AUTHN_GSS_SCHANNEL, EvenNumberOfLegs },
{ RPC_C_AUTHN_DPA, ThreeLegs },
{ RPC_C_AUTHN_DCE_PRIVATE, ThreeLegs },
{ 0x44, ThreeLegs }, // RPC_C_AUTHN_NETLOGON from net\svcdlls\logonsrv\nlbind.h
{ 0, LegsUnknown }
};
// defined in principal.cxx
//
extern
DWORD
RpcCertMatchPrincipalName(
PCCERT_CONTEXT Context,
RPC_CHAR PrincipalName[]
);
#ifdef UNICODE
#define SEC_TCHAR SEC_WCHAR
#else
#define SEC_TCHAR SEC_CHAR
#endif
RPC_STATUS
FindSecurityPackage (
IN unsigned long AuthenticationService,
IN unsigned long AuthenticationLevel,
OUT unsigned int __RPC_FAR * ProviderIndex,
OUT unsigned int __RPC_FAR * PackageIndex
);
RPC_STATUS
InsureSecuritySupportLoaded (
)
/*++
Routine Description:
This routine insures that the security support is loaded; if it is not
loaded, then we go ahead and load it.
Return Value:
A zero result indicates that security support has successfully been
loaded, and is ready to go.
--*/
{
RPC_STATUS Status = RPC_S_OK;
if ( SecuritySupportLoaded != 0 )
{
return(0);
}
RequestGlobalMutex();
if ( SecuritySupportLoaded != 0 )
{
ClearGlobalMutex();
return(0);
}
SecurityCritSect = new MUTEX(&Status,
TRUE // pre-allocate semaphore
);
if (SecurityCritSect == 0)
{
Status = RPC_S_OUT_OF_MEMORY;
}
if (Status == RPC_S_OK)
{
SecuritySupportLoaded = 1;
}
ClearGlobalMutex();
return (Status);
}
RPC_STATUS
IsAuthenticationServiceSupported (
IN unsigned long AuthenticationService
)
/*++
Routine Description:
This routine is used to determine whether or not an authentication
service is supported by the current configuration.
Arguments:
AuthenticationService - Supplies a proposed authentication service.
Return Value:
RPC_S_OK - The supplied authentication service is supported by the
current configuration.
RPC_S_UNKNOWN_AUTHN_SERVICE - The supplied authentication service
is unknown, and not supported by the current configuration.
--*/
{
unsigned int PackageIndex, ProviderIndex;
// First make sure that the security support has been loaded.
if ( InsureSecuritySupportLoaded() != RPC_S_OK )
{
return(RPC_S_OUT_OF_MEMORY);
}
return ( FindSecurityPackage(
AuthenticationService,
RPC_C_AUTHN_LEVEL_CONNECT,
&ProviderIndex,
&PackageIndex
) );
}
RPC_STATUS
FindSecurityPackage (
IN unsigned long AuthenticationService,
IN unsigned long AuthenticationLevel,
OUT unsigned int __RPC_FAR * ProviderIndex,
OUT unsigned int __RPC_FAR * PackageIndex
)
/*++
Routine Description:
The methods used to acquire credentials for the client and the server use
this routine to find a security package, given the an authentication
service.
Arguments:
AuthenticationService - Supplies the authentication service to be used
(for the credentials and for the context).
AuthenticationLevel - Supplies the authentication level to be used by
these credentials. It will already have been mapped by the protocol
module into the final level.
RpcStatus - Returns the status of the operation. It will be one of the
following values.
RPC_S_OK - The return value from this routine is the index of
the appropriate security package.
RPC_S_UNKNOWN_AUTHN_SERVICE - The specified authentication service is
not supported by the current configuration.
RPC_S_UNKNOWN_AUTHN_LEVEL - The specified authentication level is not
supported by the requested authentication service.
Return Value:
If RpcStatus contains RPC_S_OK, then the index of the appropriate
security package will be returned.
--*/
{
unsigned int Index, i;
INIT_SECURITY_INTERFACE InitSecurityInterface;
PSecurityFunctionTable SecurityInterface = 0;
SecPkgInfo PAPI * tmpPkgList;
SECURITY_PACKAGE_INFO * SecurityPackages;
SECURITY_PROVIDER_INFO PAPI * List;
unsigned long NumberOfPackages, Total;
RPC_CHAR * DllName = NULL;
DLL * ProviderDll;
RPC_STATUS Status = RPC_S_UNKNOWN_AUTHN_SERVICE;
// First make sure that the security support has been loaded.
if ( InsureSecuritySupportLoaded() != RPC_S_OK )
{
return(RPC_S_OUT_OF_MEMORY);
}
// 0xFFFF ia a "not-an-RPC-ID", indicating that the protocol
// is not for use by RPC.
if (AuthenticationService == 0xFFFF)
{
return(RPC_S_UNKNOWN_AUTHN_SERVICE);
}
SecurityCritSect->Request();
List = ProviderList;
for (i = 0; i < LoadedProviders; i ++)
{
SecurityPackages = List->SecurityPackages;
NumberOfPackages = List->Count;
for (Index = 0;Index < (unsigned int) NumberOfPackages;Index++)
{
if ( SecurityPackages[Index].PackageInfo.wRPCID == AuthenticationService )
{
if ( (AuthenticationLevel == RPC_C_AUTHN_LEVEL_PKT_INTEGRITY)
|| ( AuthenticationLevel == RPC_C_AUTHN_LEVEL_PKT) )
{
if ( (SecurityPackages[Index].PackageInfo.fCapabilities
& SECPKG_FLAG_INTEGRITY) == 0 )
{
Status = RPC_S_UNKNOWN_AUTHN_LEVEL;
goto Cleanup;
}
}
if ( AuthenticationLevel == RPC_C_AUTHN_LEVEL_PKT_PRIVACY )
{
if ( (SecurityPackages[Index].PackageInfo.fCapabilities
& SECPKG_FLAG_PRIVACY) == 0 )
{
Status = RPC_S_UNKNOWN_AUTHN_LEVEL;
goto Cleanup;
}
}
Status = RPC_S_OK;
*ProviderIndex = i;
*PackageIndex = Index;
break;
}
} //For over all packages in one provider(dll)
if (Status == RPC_S_OK)
{
SecurityCritSect->Clear();
return(Status);
}
List++;
} //For over all providers(dll)
if ((LoadedProviders == AvailableProviders) && (LoadedProviders != 0))
{
goto Cleanup;
}
Status = RpcGetSecurityProviderInfo (
AuthenticationService, &DllName, &Total);
ASSERT(!RpcpCheckHeap());
if (Status != RPC_S_OK)
{
goto Cleanup;
}
if (ProviderList == 0)
{
ProviderList = (SECURITY_PROVIDER_INFO PAPI *)
new char [sizeof(SECURITY_PROVIDER_INFO) * Total];
if (ProviderList == 0)
{
Status = RPC_S_OUT_OF_MEMORY;
goto Cleanup;
}
AvailableProviders = Total;
}
else
{
List = ProviderList;
for (i = 0; i < LoadedProviders; i ++)
{
if (RpcpStringCompare(DllName, List->ProviderDllName) == 0)
{
delete DllName;
Status = RPC_S_UNKNOWN_AUTHN_SERVICE;
goto Cleanup;
}
List++;
}
}
ProviderDll = new DLL(DllName, &Status);
if (ProviderDll == NULL)
{
Status = RPC_S_OUT_OF_MEMORY;
goto Cleanup;
}
if (Status == RPC_S_INVALID_ARG)
{
delete ProviderDll;
Status = RPC_S_UNKNOWN_AUTHN_SERVICE;
goto Cleanup;
}
else if (Status != RPC_S_OK)
{
delete ProviderDll;
goto Cleanup;
}
ASSERT(!RpcpCheckHeap());
InitSecurityInterface = (INIT_SECURITY_INTERFACE_W)
ProviderDll->GetEntryPoint(SECURITY_ENTRYPOINT_ANSIW);
if ( InitSecurityInterface == 0 )
{
delete ProviderDll;
Status = RPC_S_UNKNOWN_AUTHN_SERVICE;
goto Cleanup;
}
SecurityInterface = (*InitSecurityInterface)();
if ( (SecurityInterface == 0)
|| (SecurityInterface->dwVersion
< SECURITY_SUPPORT_PROVIDER_INTERFACE_VERSION) )
{
delete ProviderDll;
Status = RPC_S_UNKNOWN_AUTHN_SERVICE;
goto Cleanup;
}
Status = (*SecurityInterface->EnumerateSecurityPackages)(
&NumberOfPackages, &tmpPkgList);
if ( Status != SEC_E_OK)
{
delete ProviderDll;
if (Status == SEC_E_INSUFFICIENT_MEMORY)
{
Status = RPC_S_OUT_OF_MEMORY;
}
else
{
VALIDATE(Status) {
SEC_E_SECPKG_NOT_FOUND
} END_VALIDATE;
Status = RPC_S_UNKNOWN_AUTHN_SERVICE;
}
goto Cleanup;
}
ProviderList[LoadedProviders].Count = NumberOfPackages;
ProviderList[LoadedProviders].RpcSecurityInterface = SecurityInterface;
ProviderList[LoadedProviders].ProviderDll = ProviderDll;
ProviderList[LoadedProviders].ProviderDllName = DllName;
*ProviderIndex = LoadedProviders;
//
// Fill in the SecurityPackages member for this Provider.
//
ProviderList[LoadedProviders].SecurityPackages =
(SECURITY_PACKAGE_INFO *) new char
[sizeof(SECURITY_PACKAGE_INFO) * NumberOfPackages];
if (ProviderList[LoadedProviders].SecurityPackages == NULL)
{
Status = RPC_S_OUT_OF_MEMORY;
goto Cleanup;
}
//
// Save the SecPkgInfo array to the SecurityPackages structure.
//
for (i = 0; i < NumberOfPackages; i++)
{
ProviderList[LoadedProviders].
SecurityPackages[i].
PackageInfo = tmpPkgList[i];
ProviderList[LoadedProviders].
SecurityPackages[i].
ServerSecurityCredentials = NULL;
}
SecurityPackages = ProviderList[LoadedProviders].SecurityPackages;
Status = RPC_S_UNKNOWN_AUTHN_SERVICE;
for (i = 0; i < NumberOfPackages; i++)
{
if ( SecurityPackages[i].PackageInfo.wRPCID == AuthenticationService )
{
if ( ( AuthenticationLevel == RPC_C_AUTHN_LEVEL_PKT_INTEGRITY )
||( AuthenticationLevel == RPC_C_AUTHN_LEVEL_PKT) )
{
if ( (SecurityPackages[i].PackageInfo.fCapabilities
& SECPKG_FLAG_INTEGRITY) == 0 )
{
Status = RPC_S_UNKNOWN_AUTHN_LEVEL;
continue;
}
}
if ( AuthenticationLevel == RPC_C_AUTHN_LEVEL_PKT_PRIVACY )
{
if ( (SecurityPackages[i].PackageInfo.fCapabilities
& SECPKG_FLAG_PRIVACY) == 0 )
{
Status = RPC_S_UNKNOWN_AUTHN_LEVEL;
continue;
}
}
*PackageIndex = i;
Status = RPC_S_OK;
break;
}
}
LoadedProviders++;
Cleanup:
SecurityCritSect->Clear();
return(Status);
}
RPC_STATUS
FindServerCredentials (
IN RPC_AUTH_KEY_RETRIEVAL_FN GetKeyFn,
IN void __RPC_FAR * Arg,
IN unsigned long AuthenticationService,
IN unsigned long AuthenticationLevel,
IN RPC_CHAR __RPC_FAR * Principal,
IN OUT SECURITY_CREDENTIALS ** SecurityCredentials
)
/*++
Routine Description:
The method is used to find cached server credentials for use by the
server.
Arguments:
AuthenticationService - Supplies the authentication service to be used
(for the credentials and for the context).
AuthenticationLevel - Supplies the authentication level to be used by
these credentials. It will already have been mapped by the protocol
module into the final level.
SecurityCredentials - TBS
RpcStatus - Returns the status of the operation. It will be one of the
following values.
RPC_S_OK - The return value from this routine is the index of
the appropriate security package.
RPC_S_UNKNOWN_AUTHN_SERVICE - The specified authentication service is
not supported by the current configuration.
RPC_S_UNKNOWN_AUTHN_LEVEL - The specified authentication level is not
supported by the requested authentication service.
Return Value:
If RpcStatus contains RPC_S_OK, then valid credentials are passed
back.
--*/
{
SECURITY_STATUS SecurityStatus;
TimeStamp TimeStamp;
RPC_STATUS RpcStatus, CredStatus = RPC_S_OK;
PSecurityFunctionTable RpcSecurityInterface;
SECURITY_PACKAGE_INFO *pPackageInfo = 0;
SECURITY_CREDENTIALS *pSecCredentials = 0;
CredHandle tmpCredHandle;
unsigned int ProviderIndex;
unsigned int PackageIndex;
//
// NULL out the OUT parameters.
//
*SecurityCredentials = NULL;
//
// Find the right security package
//
RpcStatus = FindSecurityPackage(
AuthenticationService,
AuthenticationLevel,
&ProviderIndex,
&PackageIndex
);
if (RpcStatus != RPC_S_OK)
{
RpcpErrorAddRecord (EEInfoGCRuntime,
RpcStatus,
EEInfoDLFindServerCredentials10,
AuthenticationService,
AuthenticationLevel);
return (RpcStatus);
}
//
// Now, get the server-credentials for this security package.
//
pPackageInfo = &(ProviderList[ProviderIndex].
SecurityPackages[PackageIndex]
);
// Protect the access
SecurityCritSect->Request();
//
// Check to see if credentials have already been acquired for this
// package. If yes, return them.
//
if (pPackageInfo->ServerSecurityCredentials)
{
*SecurityCredentials = pPackageInfo->ServerSecurityCredentials;
// Add a reference for the caller.
pPackageInfo->ServerSecurityCredentials->ReferenceCredentials();
SecurityCritSect->Clear();
return (RPC_S_OK);
}
//
// Allocate a new set of credentials. Ref count is 1, if successful.
//
pSecCredentials = new SECURITY_CREDENTIALS(&CredStatus);
if (pSecCredentials == NULL)
{
SecurityCritSect->Clear();
return (RPC_S_OUT_OF_MEMORY);
}
if (CredStatus != RPC_S_OK)
{
delete pSecCredentials;
SecurityCritSect->Clear();
return (CredStatus);
}
//
// This is the first time Credentials are being acquired for this
// package. Acquire them now.
//
RpcSecurityInterface = ProviderList[ProviderIndex].RpcSecurityInterface;
SecurityStatus = (*RpcSecurityInterface->AcquireCredentialsHandle)(
(SEC_TCHAR __SEC_FAR *) Principal,
pPackageInfo->PackageInfo.Name,
SECPKG_CRED_INBOUND,
0,
Arg,
(SEC_GET_KEY_FN) GetKeyFn,
Arg,
&(pSecCredentials->CredentialsHandle),
&TimeStamp
);
if (SecurityStatus != SEC_E_OK)
{
SetExtendedError(SecurityStatus);
RpcpErrorAddRecord (EEInfoGCSecurityProvider,
SecurityStatus,
EEInfoDLFindServerCredentials20,
Principal,
pPackageInfo->PackageInfo.Name);
SecurityCritSect->Clear();
delete pSecCredentials;
switch (SecurityStatus)
{
case SEC_E_INSUFFICIENT_MEMORY:
RpcStatus = RPC_S_OUT_OF_MEMORY;
break;
case SEC_E_SHUTDOWN_IN_PROGRESS:
RpcStatus = ERROR_SHUTDOWN_IN_PROGRESS;
break;
case SEC_E_SECPKG_NOT_FOUND:
RpcStatus = RPC_S_UNKNOWN_AUTHN_SERVICE;
break;
case SEC_E_NO_SPM:
RpcStatus = RPC_S_SEC_PKG_ERROR;
break;
default:
{
#if DBG
if ((SecurityStatus != SEC_E_NO_CREDENTIALS) &&
(SecurityStatus != SEC_E_UNKNOWN_CREDENTIALS))
{
PrintToDebugger("RPC SEC: AcquireCredentialsForServer "
"Returned 0x%x\n", SecurityStatus);
}
#endif
RpcStatus = RPC_S_INVALID_AUTH_IDENTITY;
}
} // end of switch
RpcpErrorAddRecord (EEInfoGCRuntime,
RpcStatus,
EEInfoDLFindServerCredentials30,
SecurityStatus);
return RpcStatus;
}
//
// Setup the new Credentials.
//
pSecCredentials->Valid = TRUE;
pSecCredentials->AuthenticationService = AuthenticationService;
pSecCredentials->ProviderIndex = ProviderIndex;
pSecCredentials->PackageIndex = PackageIndex;
//
// Cache the new credentials. They are created with refcount 1
// and this refcount will be removed in RemoveCredentialsFromCache
//
pPackageInfo->ServerSecurityCredentials = pSecCredentials;
// Add a reference for the Cache.
pPackageInfo->ServerSecurityCredentials->ReferenceCredentials();
*SecurityCredentials = pSecCredentials;
SecurityCritSect->Clear();
return (RPC_S_OK);
}
RPC_STATUS
RemoveCredentialsFromCache (
IN unsigned long AuthenticationService
)
/*++
Routine Description:
An RPC server can call RpcRegisterAuthInfo() a second time on the same
Authentication Service to update the GetKeyFunction and Arg values.
In this case, we need to flush our credentials cache so that when
the server tries to acquires credentials again, the credentials can
be acquired using the new values.
Arguments:
AuthenticationService - Supplies the authentication service to be used
(for the credentials and for the context).
Return Value:
RPC_S_OK, If Cache has been successfully flushed.
Return Value from FindSecurityPackage(), if not
--*/
{
unsigned int ProviderIndex;
unsigned int PackageIndex;
SECURITY_PACKAGE_INFO *pPackageInfo = 0;
RPC_STATUS RpcStatus;
//
// First, find the right security package
//
RpcStatus = FindSecurityPackage(
AuthenticationService,
RPC_C_AUTHN_LEVEL_DEFAULT, // Doesn't matter
&ProviderIndex,
&PackageIndex
);
ASSERT(RpcStatus == RPC_S_OK);
if (RpcStatus != RPC_S_OK)
{
return (RpcStatus);
}
pPackageInfo = &(ProviderList[ProviderIndex].SecurityPackages[PackageIndex]);
//
// Flush the credentials.
//
SecurityCritSect->Request();
if (pPackageInfo->ServerSecurityCredentials)
{
// Remove the reference maintained by the cache.
pPackageInfo->ServerSecurityCredentials->DereferenceCredentials();
pPackageInfo->ServerSecurityCredentials = NULL;
}
SecurityCritSect->Clear();
return (RPC_S_OK);
}
#define INVALID_INDEX 0xFFFF
SECURITY_CREDENTIALS::SECURITY_CREDENTIALS (
IN OUT RPC_STATUS PAPI * Status
) : CredentialsMutex(Status)
/*++
Routine Description:
We need this here to keep the compiler happy.
--*/
{
DefaultPrincName = NULL;
ReferenceCount = 1;
Valid = FALSE;
// Initialize to invalid values.
ProviderIndex = INVALID_INDEX;
PackageIndex = INVALID_INDEX;
}
SECURITY_CREDENTIALS::~SECURITY_CREDENTIALS (
)
{
PSecurityFunctionTable RpcSecurityInterface;
if (DefaultPrincName != NULL)
{
RpcSecurityInterface = InquireProviderFunctionTable();
ASSERT(RpcSecurityInterface != NULL);
(*RpcSecurityInterface->FreeContextBuffer)(DefaultPrincName);
}
}
void
SECURITY_CREDENTIALS::ReferenceCredentials(
)
{
CredentialsMutex.Request();
LogEvent(SU_SECCRED, EV_INC, this, 0, ReferenceCount, 1, 0);
ReferenceCount++;
CredentialsMutex.Clear();
}
void
SECURITY_CREDENTIALS::DereferenceCredentials(
void
)
{
CredentialsMutex.Request();
LogEvent(SU_SECCRED, EV_DEC, this, 0, ReferenceCount, 1, 0);
ReferenceCount--;
ASSERT(((long)ReferenceCount) >= 0);
if (ReferenceCount == 0)
{
//
// Server side SCENARIOS when (ReferenceCount == 0)
//
// a. RemoveCredentialsFromCache() removes the extra reference
// held by the cache. It does so holding the cache Mutex ie.,
// SecurityCritSect. So, no other thread can get a reference
// on the credentials in the cache.
//
// b. DereferenceCredentials() has been called by one of the
// threads possessing the cached credentials AND the cache
// has already removed it's reference. Cache will remove its
// reference only when it removes this credential from the
// cache. This implies no other thread could have gotten a
// reference on the credentials in the cache, in the meantime.
//
// These two imply that ReferenceCount cannot change here. Also,
// in case (a), credentials will soon be removed from the cache.
// In case (b), credentials have already been removed.
//
CredentialsMutex.Clear();
FreeCredentials();
delete this;
} // if (ReferenceCount == 0)
else
{
CredentialsMutex.Clear();
}
}
RPC_STATUS
SECURITY_CREDENTIALS::AcquireCredentialsForClient (
IN RPC_AUTH_IDENTITY_HANDLE AuthIdentity,
IN unsigned long AuthenticationService,
IN unsigned long AuthenticationLevel
)
/*++
Routine Description:
We need to use this method in order to acquire security credentials. We
need the security credentials so that we (as a client) can initialize
a security context with a server. This method, with
SECURITY_CREDENTIALS::FreeCredentials may cache security credentials,
but that is transparent to clients of this class.
Arguments:
AuthIdentity - Supplies the security identity for which we wish to obtain
credentials. If this argument is not supplied, then we use the
security identity of this process.
AuthenticationService - Supplies the authentication service to be used
(for the credentials and for the context).
AuthenticationLevel - Supplies the authentication level to be used by
these credentials. It will already have been mapped by the protocol
module into the final level.
Return Value:
RPC_S_OK - We now have security credentials for the requested
authentication service.
RPC_S_OUT_OF_MEMORY - Insufficient memory is available to perform the
operation.
RPC_S_UNKNOWN_AUTHN_SERVICE - The specified authentication service is
not supported by the current configuration.
RPC_S_INVALID_AUTH_IDENTITY - The specified identity is not known to
the requested authentication service.
RPC_S_UNKNOWN_AUTHN_LEVEL - The specified authentication level is not
supported by the requested authentication service.
--*/
{
SECURITY_STATUS SecurityStatus;
TimeStamp TimeStamp;
RPC_STATUS RpcStatus;
PSecurityFunctionTable RpcSecurityInterface;
unsigned Flags = SCH_CRED_USE_DEFAULT_CREDS;
RpcStatus = FindSecurityPackage(
AuthenticationService,
AuthenticationLevel,
&ProviderIndex,
&PackageIndex
);
if ( RpcStatus != RPC_S_OK )
{
RpcpErrorAddRecord(EEInfoGCRuntime,
RpcStatus,
EEInfoDLAcquireCredentialsForClient20,
AuthenticationService,
AuthenticationLevel);
return(RpcStatus);
}
RpcSecurityInterface = ProviderList[ProviderIndex].RpcSecurityInterface;
if (AuthIdentity == RPC_C_NO_CREDENTIALS)
{
Flags = 0;
AuthIdentity = 0;
}
//
// RPC does its own name checking using msstd or fullsic names.
// This requires the ability to disable schannel's name checking,
// a feature available only with credentials version-4 or better.
//
#define MINIMUM_SCHANNEL_CRED_VERSION 4
if (AuthenticationService == RPC_C_AUTHN_GSS_SCHANNEL &&
AuthIdentity != NULL)
{
SCHANNEL_CRED * cred = (SCHANNEL_CRED *) AuthIdentity;
Flags |= SCH_CRED_NO_SERVERNAME_CHECK;
if (cred->dwVersion < MINIMUM_SCHANNEL_CRED_VERSION)
{
RpcpErrorAddRecord(EEInfoGCRuntime,
ERROR_INVALID_PARAMETER,
EEInfoDLAcquireCredentialsForClient30,
AuthenticationService,
AuthenticationLevel);
return RPC_S_INVALID_AUTH_IDENTITY;
}
cred->dwFlags |= Flags;
}
SecurityStatus = (*RpcSecurityInterface->AcquireCredentialsHandle)(
0,
ProviderList[ProviderIndex].SecurityPackages[PackageIndex].PackageInfo.Name,
SECPKG_CRED_OUTBOUND,
0,
AuthIdentity, 0, 0, &CredentialsHandle, &TimeStamp);
if ( SecurityStatus != SEC_E_OK )
{
RpcpErrorAddRecord(EEInfoGCSecurityProvider,
SecurityStatus,
EEInfoDLAcquireCredentialsForClient10,
AuthenticationService,
AuthenticationLevel);
}
if (SecurityStatus != SEC_E_OK)
{
if ( SecurityStatus == SEC_E_INSUFFICIENT_MEMORY )
{
RpcStatus = RPC_S_OUT_OF_MEMORY;
}
else if ( SecurityStatus == SEC_E_SECPKG_NOT_FOUND )
{
RpcStatus = RPC_S_UNKNOWN_AUTHN_SERVICE;
}
else if ( SecurityStatus == SEC_E_SHUTDOWN_IN_PROGRESS )
{
RpcStatus = ERROR_SHUTDOWN_IN_PROGRESS;
}
else if ( SecurityStatus == SEC_E_NO_SPM)
{
RpcStatus = RPC_S_SEC_PKG_ERROR;
}
else
{
VALIDATE(SecurityStatus)
{
SEC_E_NO_CREDENTIALS,
SEC_E_UNKNOWN_CREDENTIALS,
SEC_E_NO_AUTHENTICATING_AUTHORITY,
SEC_E_INVALID_TOKEN
}
END_VALIDATE;
RpcStatus = RPC_S_INVALID_AUTH_IDENTITY;
}
RpcpErrorAddRecord(EEInfoGCRuntime,
RpcStatus,
EEInfoDLAcquireCredentialsForClient30,
SecurityStatus);
return RpcStatus;
}
this->AuthenticationService = AuthenticationService;
Valid = TRUE;
return(RPC_S_OK);
}
RPC_STATUS
SECURITY_CREDENTIALS::InquireDefaultPrincName(
OUT SEC_CHAR __SEC_FAR **MyDefaultPrincName
)
{
SECURITY_STATUS SecurityStatus;
SecPkgCredentials_NamesA CredentialsNames;
PSecurityFunctionTable RpcSecurityInterface;
RPC_STATUS Status;
if (DefaultPrincName == NULL)
{
RpcSecurityInterface = InquireProviderFunctionTable();
if (RpcSecurityInterface == NULL) {
return (RPC_S_OUT_OF_MEMORY);
}
if (RpcSecurityInterface->QueryCredentialsAttributes == NULL) {
return (RPC_S_CANNOT_SUPPORT);
}
SecurityStatus = (*RpcSecurityInterface->QueryCredentialsAttributes)(
InquireCredHandle(), SECPKG_CRED_ATTR_NAMES, &CredentialsNames);
if (SecurityStatus != SEC_E_OK)
{
SetExtendedError(SecurityStatus);
RpcpErrorAddRecord (EEInfoGCSecurityProvider,
SecurityStatus,
EEInfoDLInquireDefaultPrincName10,
AuthenticationService);
if (SecurityStatus == SEC_E_INSUFFICIENT_MEMORY)
{
Status = RPC_S_OUT_OF_MEMORY;
}
else if ( SecurityStatus == SEC_E_SHUTDOWN_IN_PROGRESS )
{
Status = ERROR_SHUTDOWN_IN_PROGRESS;
}
else
{
Status = RPC_S_SEC_PKG_ERROR;
}
RpcpErrorAddRecord (EEInfoGCRuntime,
Status,
EEInfoDLInquireDefaultPrincName20,
SecurityStatus);
return Status;
}
if (CredentialsNames.sUserName == NULL)
{
return RPC_S_OUT_OF_MEMORY;
}
DefaultPrincName = CredentialsNames.sUserName;
}
*MyDefaultPrincName = DefaultPrincName;
return (RPC_S_OK);
}
void
SECURITY_CREDENTIALS::FreeCredentials (
)
/*++
Routine Description:
When we are done using the credentials, we call this routine to free
them.
--*/
{
unsigned int nRetries = 0;
if (Valid)
{
SECURITY_STATUS SecurityStatus;
PSecurityFunctionTable RpcSecurityInterface
= ProviderList[ProviderIndex].RpcSecurityInterface;
// SEC_E_INSUFFICIENT_MEMORY may be returned under extremely low
// memory conditions. We will do the following:
// - Retry 10 times to delete the credential handle.
// - Raise a flag in the process that we have leaked one or more security ts.
do
{
SecurityStatus = (*RpcSecurityInterface->FreeCredentialHandle)(
&CredentialsHandle);
nRetries++;
}
while (SecurityStatus == SEC_E_INSUFFICIENT_MEMORY && nRetries < 10);
if (SecurityStatus == SEC_E_INSUFFICIENT_MEMORY)
{
nSecurityStructuresLeaked++;
}
#if DBG
if ((SecurityStatus != SEC_E_OK) && (SecurityStatus != SEC_E_SHUTDOWN_IN_PROGRESS))
{
PrintToDebugger("FreeCredentialHandle(0x%x) Returned [%lx]\n",
&CredentialsHandle,
SecurityStatus);
}
if (SecurityStatus == SEC_E_INSUFFICIENT_MEMORY)
{
PrintToDebugger("CredentialsHandle(0x%x) leaked\n",
&CredentialsHandle);
}
#endif
ASSERT( SecurityStatus == SEC_E_OK ||
SecurityStatus == SEC_E_SECPKG_NOT_FOUND ||
SecurityStatus == SEC_E_SHUTDOWN_IN_PROGRESS ||
SecurityStatus == SEC_E_INSUFFICIENT_MEMORY);
}
}
RPC_STATUS
SECURITY_CONTEXT::SetMaximumLengths (
)
/*++
Routine Description:
This routine initializes the maximum header length and maximum signature
length fields of this object.
--*/
{
SECURITY_STATUS SecurityStatus;
SecPkgContext_Sizes ContextSizes;
RPC_STATUS Status;
if (FailedContext != 0)
{
// We cheat if 3rd Leg Failed as we dont really have a true Context
// Provider is going to really complain if we call QueryContextAttr()
// .. we get around that by picking large values.
// The rest of the code prevents these values to be really used
// We do this because we do not want to block 3rd Leg, rather fail the
// first request!
MaxSignatureLength = 256;
MaxHeaderLength = 256;
cbBlockSize = 64;
return RPC_S_OK;
}
ASSERT( FullyConstructed() );
SecurityStatus = (*RpcSecurityInterface->QueryContextAttributes)(
&SecurityContext, SECPKG_ATTR_SIZES, &ContextSizes);
if (SecurityStatus != SEC_E_OK)
{
#ifdef DEBUGRPC
PrintToDebugger("RPC: secclnt.cxx: QueryContextAttributes returned: %lx\n",
SecurityStatus);
#endif
Status = GetLastError();
RpcpErrorAddRecord (EEInfoGCSecurityProvider,
RPC_S_OUT_OF_MEMORY,
EEInfoDLSetMaximumLengths10,
SecurityStatus,
Status,
AuthenticationService,
(ULONGLONG)&SecurityContext
);
return RPC_S_OUT_OF_MEMORY;
}
MaxSignatureLength = (unsigned int) ContextSizes.cbMaxSignature;
MaxHeaderLength = (unsigned int) ContextSizes.cbSecurityTrailer;
cbBlockSize = (unsigned int) ContextSizes.cbBlockSize;
ASSERT(ContextSizes.cbBlockSize <= MAXIMUM_SECURITY_BLOCK_SIZE );
return RPC_S_OK;
}
SECURITY_CONTEXT::SECURITY_CONTEXT (
CLIENT_AUTH_INFO *myAuthInfo,
unsigned myAuthContextId,
BOOL fUseDatagram,
RPC_STATUS __RPC_FAR * pStatus
)
: CLIENT_AUTH_INFO (myAuthInfo, pStatus),
AuthContextId (myAuthContextId),
fDatagram ((boolean) fUseDatagram),
fFullyConstructed (FALSE),
Legs (LegsUnknown),
ContextAttributes (0)
/*++
Routine Description:
We need to set the flag indicating that there is no security context
to be deleted.
--*/
{
CORRUPTION_ASSERT( AuthenticationLevel != 0 );
DontForgetToDelete = 0;
FailedContext = 0;
FailedContextEEInfo = NULL;
AuthzClientContext = NULL;
}
RPC_STATUS
SECURITY_CONTEXT::CompleteSecurityToken (
IN OUT SECURITY_BUFFER_DESCRIPTOR PAPI * BufferDescriptor
)
/*++
--*/
{
SECURITY_STATUS SecurityStatus;
RPC_STATUS Status;
ASSERT( ( SecuritySupportLoaded != 0 )
&& ( FailedToLoad == 0 ) );
SecurityStatus = (*RpcSecurityInterface->CompleteAuthToken)(
&SecurityContext, BufferDescriptor);
if (SecurityStatus == SEC_E_OK)
{
return (RPC_S_OK);
}
SetExtendedError(SecurityStatus);
RpcpErrorAddRecord (EEInfoGCSecurityProvider,
SecurityStatus,
EEInfoDLCompleteSecurityToken10,
AuthenticationService,
AuthenticationLevel);
if ( (SecurityStatus == SEC_E_NO_CREDENTIALS)
|| (SecurityStatus == SEC_E_LOGON_DENIED)
|| (SecurityStatus == SEC_E_INVALID_TOKEN)
|| (SecurityStatus == SEC_E_UNKNOWN_CREDENTIALS)
|| (SecurityStatus == SEC_E_WRONG_PRINCIPAL)
|| (SecurityStatus == SEC_E_TIME_SKEW))
{
Status = RPC_S_ACCESS_DENIED;
}
else if ( SecurityStatus == SEC_E_INSUFFICIENT_MEMORY )
{
Status = RPC_S_OUT_OF_MEMORY;
}
else if ( SecurityStatus == SEC_E_SHUTDOWN_IN_PROGRESS )
{
Status = ERROR_SHUTDOWN_IN_PROGRESS;
}
else
{
#if DBG
PrintToDebugger("RPC: CompleteSecurityContext Returned %lx\n",
SecurityStatus);
#endif
Status = RPC_S_SEC_PKG_ERROR;
}
RpcpErrorAddRecord (EEInfoGCRuntime,
Status,
EEInfoDLCompleteSecurityToken20,
SecurityStatus);
return Status;
}
RPC_STATUS
SECURITY_CONTEXT::SignOrSeal (
IN unsigned long Sequence,
IN unsigned int SignNotSealFlag,
IN OUT SECURITY_BUFFER_DESCRIPTOR PAPI * BufferDescriptor
)
/*++
Routine Description:
A protocol module will use this routine to prepare a message to be
sent so that it can be verified that the message has not been tampered
with, and that it has not been exchanged out of sequence. The sender
will use this routine to prepare the message; the receiver will use
SECURITY_CONTEXT::VerifyOrUnseal to verify the message. Typically,
the security package will generate a cryptographic checksum of the
message and include sequencing information.
Arguments:
SignNotSealFlag - Supplies a flag indicating that MakeSignature should
be called rather than SealMessage.
BufferDescriptor - Supplies the message to to signed or sealed and returns
the resulting message (after being signed or sealed).
Return Value:
RPC_S_OK - This routine will always succeed.
--*/
{
SECURITY_STATUS SecurityStatus;
SEAL_MESSAGE_FN SealMessage;
{
DWORD Status = 0;
CallTestHook( TH_SECURITY_FN_SIGN, this, &Status );
if (Status)
{
return Status;
}
}
if ( SignNotSealFlag == 0 )
{
SealMessage = (SEAL_MESSAGE_FN) RpcSecurityInterface->Reserved3;
SecurityStatus = (*SealMessage)(&SecurityContext,
0, BufferDescriptor, Sequence);
}
else
{
SecurityStatus = (*RpcSecurityInterface->MakeSignature)(
&SecurityContext, 0, BufferDescriptor, Sequence);
}
#if DBG
if ( (SecurityStatus != SEC_E_OK)
&&(SecurityStatus != SEC_E_CONTEXT_EXPIRED)
&&(SecurityStatus != SEC_E_QOP_NOT_SUPPORTED) )
{
PrintToDebugger("Sign/Seal Returned [%lx]\n", SecurityStatus);
}
#endif
if ( SecurityStatus == SEC_E_SHUTDOWN_IN_PROGRESS )
{
SecurityStatus = ERROR_SHUTDOWN_IN_PROGRESS;
}
if (SecurityStatus != SEC_E_OK)
{
RpcpErrorAddRecord (EEInfoGCSecurityProvider,
SecurityStatus,
EEInfoDLSignOrSeal10,
AuthenticationService,
AuthenticationLevel);
}
return(SecurityStatus);
}
extern BOOL gfRpcDisableVerifyOrUnsealAssert;
RPC_STATUS
SECURITY_CONTEXT::VerifyOrUnseal (
IN unsigned long Sequence,
IN unsigned int VerifyNotUnsealFlag,
IN OUT SECURITY_BUFFER_DESCRIPTOR PAPI * BufferDescriptor
)
/*++
Routine Description:
This routine works with SECURITY_CONTEXT::SignOrSeal. A sender will
prepare a message using SignOrSeal, and then the receiver will use
this routine to verify that the message has not been tampered with, and
that it has not been exchanged out of sequence.
Arguments:
VerifyNotUnsealFlag - Supplies a flag indicating that VerifySignature
should be called rather than UnsealMessage.
BufferDescriptor - Supplies the message to be verified or unsealed.
Return Value:
RPC_S_OK - The message has not been tampered with, and it is from the
expected client.
RPC_S_ACCESS_DENIED - A security violation occured.
--*/
{
SECURITY_STATUS SecurityStatus;
RPC_STATUS RpcStatus;
unsigned long QualityOfProtection;
UNSEAL_MESSAGE_FN UnsealMessage;
{
DWORD Status = 0;
CallTestHook( TH_SECURITY_FN_VERIFY, this, &Status );
if (Status)
{
return Status;
}
}
//
// If the context had failed previously..
// Just go ahead and return an error..
// This is only for connetion-oriented RPC.
//
if (FailedContext != 0 || !FullyConstructed() )
{
RpcpSetEEInfo(FailedContextEEInfo);
FailedContextEEInfo = NULL;
SetLastError(FailedContext);
if ((FailedContext == ERROR_PASSWORD_MUST_CHANGE)
|| (FailedContext == ERROR_PASSWORD_EXPIRED)
|| (FailedContext == ERROR_ACCOUNT_DISABLED)
|| (FailedContext == ERROR_INVALID_LOGON_HOURS))
{
return FailedContext;
}
RpcpErrorAddRecord (EEInfoGCRuntime,
RPC_S_ACCESS_DENIED,
EEInfoDLVerifyOrUnseal20,
FailedContext);
return (RPC_S_ACCESS_DENIED);
}
if ( VerifyNotUnsealFlag == 0 )
{
UnsealMessage = (UNSEAL_MESSAGE_FN) RpcSecurityInterface->Reserved4;
SecurityStatus = (*UnsealMessage)(
&SecurityContext, BufferDescriptor, Sequence,
&QualityOfProtection);
}
else
{
SecurityStatus = (*RpcSecurityInterface->VerifySignature)(
&SecurityContext, BufferDescriptor, Sequence,
&QualityOfProtection);
}
if ( SecurityStatus != SEC_E_OK )
{
#if DBG
if ((SecurityStatus != SEC_E_MESSAGE_ALTERED)
&&(SecurityStatus != SEC_E_OUT_OF_SEQUENCE)
&&(SecurityStatus != SEC_E_SECPKG_NOT_FOUND)) // on system shutdown, if the security
{ // package is already uninitialized, we may get this error
PrintToDebugger("Verify/UnSeal Returned Unexp. Code [%lx]\n",
SecurityStatus);
}
#endif
SetExtendedError(SecurityStatus);
if ( SecurityStatus == SEC_E_INSUFFICIENT_MEMORY )
{
RpcStatus = RPC_S_OUT_OF_MEMORY;
}
else
{
if (!gfRpcDisableVerifyOrUnsealAssert
&& !fDatagram)
{
VALIDATE (SecurityStatus)
{
SEC_E_SHUTDOWN_IN_PROGRESS,
SEC_E_SECPKG_NOT_FOUND
}
CORRUPTION_VALIDATE
{
SEC_E_MESSAGE_ALTERED,
SEC_E_OUT_OF_SEQUENCE,
SEC_E_INVALID_TOKEN
} CORRUPTION_END_VALIDATE;
}
RpcStatus = RPC_S_ACCESS_DENIED;
}
RpcpErrorAddRecord (EEInfoGCSecurityProvider,
RpcStatus,
EEInfoDLVerifyOrUnseal10,
SecurityStatus,
AuthenticationService,
AuthenticationLevel);
SetLastError(RpcStatus);
return(RpcStatus);
}
return(RPC_S_OK);
}
RPC_STATUS
SECURITY_CONTEXT::InitializeFirstTime (
IN SECURITY_CREDENTIALS * Credentials,
IN RPC_CHAR * ServerPrincipalName,
IN unsigned long AuthenticationLevel,
IN OUT SECURITY_BUFFER_DESCRIPTOR PAPI * BufferDescriptor,
IN OUT unsigned char *NewAuthType
)
/*++
Routine Description:
Arguments:
Return Value:
RPC_S_OK - Send the token to the server; everything worked fine so
far.
RPC_P_CONTINUE_NEEDED - Indicates that that everything is ok, but that
we need to call into the security package again when we have
received a token back from the server.
RPC_P_COMPLETE_NEEDED - Indicates that everyting is ok, but that we
need to call CompleteAuthToken before sending the message.
RPC_P_COMPLETE_AND_CONTINUE - Needs both a complete and a continue.
RPC_S_OUT_OF_MEMORY - Insufficient memory is available to perform the
operation.
RPC_S_ACCESS_DENIED - Access is denied.
--*/
{
SECURITY_STATUS SecurityStatus;
TimeStamp TimeStamp;
unsigned long ContextRequirements;
RPC_STATUS Status;
RPC_STATUS Status2;
unsigned char RetrievedAuthType;
BOOL fDone;
ASSERT( ( SecuritySupportLoaded != 0 )
&& ( FailedToLoad == 0 ) );
RpcSecurityInterface = Credentials->InquireProviderFunctionTable();
switch ( AuthenticationLevel )
{
case RPC_C_AUTHN_LEVEL_CONNECT :
ContextRequirements = 0;
break;
case RPC_C_AUTHN_LEVEL_PKT :
ContextRequirements = ISC_REQ_REPLAY_DETECT;
break;
case RPC_C_AUTHN_LEVEL_PKT_INTEGRITY :
ContextRequirements = ISC_REQ_REPLAY_DETECT
| ISC_REQ_SEQUENCE_DETECT
| ISC_REQ_INTEGRITY;
break;
case RPC_C_AUTHN_LEVEL_PKT_PRIVACY :
ContextRequirements = ISC_REQ_REPLAY_DETECT
| ISC_REQ_SEQUENCE_DETECT
| ISC_REQ_CONFIDENTIALITY
| ISC_REQ_INTEGRITY;
break;
default :
ASSERT( ( AuthenticationLevel == RPC_C_AUTHN_LEVEL_CONNECT )
|| ( AuthenticationLevel == RPC_C_AUTHN_LEVEL_PKT )
|| ( AuthenticationLevel == RPC_C_AUTHN_LEVEL_PKT_INTEGRITY )
|| ( AuthenticationLevel == RPC_C_AUTHN_LEVEL_PKT_PRIVACY ) );
return RPC_S_INTERNAL_ERROR;
}
if (fDatagram)
{
ContextRequirements |= ISC_REQ_DATAGRAM;
}
else
{
ContextRequirements |= ISC_REQ_CONNECTION;
}
switch(ImpersonationType)
{
case RPC_C_IMP_LEVEL_IDENTIFY:
ContextRequirements |= ISC_REQ_IDENTIFY;
break;
case RPC_C_IMP_LEVEL_IMPERSONATE:
break;
case RPC_C_IMP_LEVEL_DELEGATE:
ContextRequirements |= ISC_REQ_DELEGATE;
break;
default:
ASSERT( ImpersonationType == RPC_C_IMP_LEVEL_ANONYMOUS
|| ImpersonationType == RPC_C_IMP_LEVEL_IDENTIFY
|| ImpersonationType == RPC_C_IMP_LEVEL_IMPERSONATE
|| ImpersonationType == RPC_C_IMP_LEVEL_DELEGATE
|| ImpersonationType == RPC_C_IMP_LEVEL_DEFAULT );
ContextRequirements |= ISC_REQ_IDENTIFY;
break;
}
if (Capabilities & RPC_C_QOS_CAPABILITIES_MUTUAL_AUTH)
{
ContextRequirements |= ISC_REQ_MUTUAL_AUTH;
}
if (Credentials->AuthenticationService == RPC_C_AUTHN_GSS_SCHANNEL)
{
#if MANUAL_CERT_CHECK
ContextRequirements |= ISC_REQ_MANUAL_CRED_VALIDATION;
#endif
}
else
{
ContextRequirements |= ISC_REQ_USE_DCE_STYLE;
}
SecurityStatus = (*RpcSecurityInterface->InitializeSecurityContext)(
Credentials->InquireCredHandle(),
0,
(SEC_TCHAR __SEC_FAR *) ServerPrincipalName,
ContextRequirements,
0,
0,
(fDatagram ? BufferDescriptor : 0),
0,
&SecurityContext,
(fDatagram ? 0 : BufferDescriptor),
&ContextAttributes,
&TimeStamp
);
if ( ( SecurityStatus != SEC_E_OK )
&& ( SecurityStatus != SEC_I_CONTINUE_NEEDED )
&& ( SecurityStatus != SEC_I_COMPLETE_NEEDED )
&& ( SecurityStatus != SEC_I_COMPLETE_AND_CONTINUE ) )
{
SetExtendedError(SecurityStatus);
RpcpErrorAddRecord (EEInfoGCSecurityProvider,
SecurityStatus,
EEInfoDLInitializeFirstTime10,
AuthenticationService,
AuthenticationLevel,
ServerPrincipalName,
ContextRequirements);
if ((SecurityStatus == SEC_E_NO_CREDENTIALS)
|| (SecurityStatus == SEC_E_LOGON_DENIED)
|| (SecurityStatus == SEC_E_INVALID_TOKEN)
|| (SecurityStatus == SEC_E_UNKNOWN_CREDENTIALS)
|| (SecurityStatus == SEC_E_NO_KERB_KEY)
|| (SecurityStatus == SEC_E_TIME_SKEW)
|| (SecurityStatus == STATUS_AUTHENTICATION_FIREWALL_FAILED))
{
Status = RPC_S_ACCESS_DENIED;
}
else if ( SecurityStatus == SEC_E_SHUTDOWN_IN_PROGRESS )
{
Status = ERROR_SHUTDOWN_IN_PROGRESS;
}
else if (SecurityStatus == SEC_E_SECPKG_NOT_FOUND)
{
Status = RPC_S_UNKNOWN_AUTHN_SERVICE;
}
else if ( SecurityStatus == SEC_E_INSUFFICIENT_MEMORY )
{
Status = RPC_S_OUT_OF_MEMORY;
}
else
{
#if DBG
PrintToDebugger("RPC: InitializeFirstTime Returned %lx\n", SecurityStatus);
#endif
//
// Originally the default for all providers was SEC_PKG_ERROR. This seems less
// helpful than ACCESS_DENIED, but we can't change it for NTLM and Kerberos because
// it might break old app code.
//
// SCHANNEL is new, so we can return ACCESS_DENIED.
//
if (AuthenticationService == RPC_C_AUTHN_GSS_SCHANNEL)
{
Status = RPC_S_ACCESS_DENIED;
}
else
{
Status = RPC_S_SEC_PKG_ERROR;
}
}
RpcpErrorAddRecord (EEInfoGCRuntime,
Status,
EEInfoDLInitializeFirstTime20,
SecurityStatus);
return Status;
}
RetrievedAuthType = 0;
DontForgetToDelete = 1;
if (NewAuthType)
{
ASSERT(*NewAuthType == this->AuthenticationService);
if (*NewAuthType == RPC_C_AUTHN_GSS_NEGOTIATE)
{
SecPkgContext_NegotiationInfo NegoInfo;
SECURITY_STATUS Status;
Status = (*RpcSecurityInterface->QueryContextAttributes)(
&SecurityContext,
SECPKG_ATTR_NEGOTIATION_INFO,
&NegoInfo);
if (Status == SEC_E_OK)
{
if (NegoInfo.NegotiationState == SECPKG_NEGOTIATION_COMPLETE)
{
RetrievedAuthType = (unsigned char) NegoInfo.PackageInfo->wRPCID;
}
(*RpcSecurityInterface->FreeContextBuffer)(NegoInfo.PackageInfo);
}
else
{
RpcpErrorAddRecord (EEInfoGCSecurityProvider,
RPC_S_OUT_OF_MEMORY,
EEInfoDLInitializeFirstTime30,
Status,
(ULONG)AuthenticationService,
AuthenticationLevel,
(ULONG)ContextRequirements);
return RPC_S_OUT_OF_MEMORY;
}
}
}
Flags = ContextRequirements;
fDone = TRUE;
if ( SecurityStatus == SEC_I_CONTINUE_NEEDED )
{
if (!fDatagram)
{
fDone = FALSE;
}
Status = RPC_P_CONTINUE_NEEDED;
}
else if ( SecurityStatus == SEC_I_COMPLETE_NEEDED )
{
// Can't set the maximum lengths on a partly completed connection.
Status = RPC_P_COMPLETE_NEEDED;
}
else if ( SecurityStatus == SEC_I_COMPLETE_AND_CONTINUE )
{
if (!fDatagram)
{
fDone = FALSE;
}
Status = RPC_P_COMPLETE_AND_CONTINUE;
}
else
{
Status = RPC_S_OK;
}
if (fDone)
{
fFullyConstructed = TRUE;
Status2 = SetMaximumLengths();
if (Status2 != RPC_S_OK)
{
return Status2;
}
}
if (RetrievedAuthType)
*NewAuthType = RetrievedAuthType;
return(Status);
}
RPC_STATUS
SECURITY_CONTEXT::InitializeThirdLeg (
IN SECURITY_CREDENTIALS * Credentials,
IN unsigned long DataRepresentation,
IN SECURITY_BUFFER_DESCRIPTOR PAPI * InputBufferDescriptor,
IN OUT SECURITY_BUFFER_DESCRIPTOR PAPI * OutputBufferDescriptor
)
/*++
Routine Description:
Arguments:
Return Value:
RPC_S_OK - Send the token to the server; everything worked fine so
far.
RPC_S_OUT_OF_MEMORY - Insufficient memory is available to perform the
operation.
RPC_S_ACCESS_DENIED - Access is denied.
--*/
{
SECURITY_STATUS SecurityStatus;
TimeStamp TimeStamp;
RPC_STATUS Status;
ASSERT( (SecuritySupportLoaded != 0)
&& (FailedToLoad == 0) );
SecurityStatus = (*RpcSecurityInterface->InitializeSecurityContext)(
Credentials->InquireCredHandle(),
&SecurityContext,
0,
Flags,
0,
DataRepresentation,
InputBufferDescriptor,
0,
&SecurityContext,
OutputBufferDescriptor,
&ContextAttributes,
&TimeStamp
);
if ( ( SecurityStatus != SEC_E_OK )
&& ( SecurityStatus != SEC_I_CONTINUE_NEEDED )
&& ( SecurityStatus != SEC_I_COMPLETE_NEEDED )
&& ( SecurityStatus != SEC_I_COMPLETE_AND_CONTINUE ) )
{
SetExtendedError(SecurityStatus);
RpcpErrorAddRecord (EEInfoGCSecurityProvider,
SecurityStatus,
EEInfoDLInitializeThirdLeg10,
AuthenticationService,
AuthenticationLevel,
ContextAttributes);
if ( (SecurityStatus == SEC_E_NO_CREDENTIALS)
|| (SecurityStatus == SEC_E_LOGON_DENIED)
|| (SecurityStatus == SEC_E_INVALID_TOKEN)
|| (SecurityStatus == SEC_E_UNKNOWN_CREDENTIALS)
|| (SecurityStatus == SEC_E_NO_KERB_KEY)
|| (SecurityStatus == SEC_E_TIME_SKEW)
|| (SecurityStatus == STATUS_AUTHENTICATION_FIREWALL_FAILED))
{
Status = RPC_S_ACCESS_DENIED;
}
else if ( SecurityStatus == SEC_E_SHUTDOWN_IN_PROGRESS )
{
Status = ERROR_SHUTDOWN_IN_PROGRESS;
}
else if (SecurityStatus == SEC_E_SECPKG_NOT_FOUND)
{
Status = RPC_S_UNKNOWN_AUTHN_SERVICE;
}
else if ( SecurityStatus == SEC_E_INSUFFICIENT_MEMORY )
{
Status = RPC_S_OUT_OF_MEMORY;
}
else
{
#if DBG
PrintToDebugger("RPC: InitializeThirdLeg Returned %lx\n",
SecurityStatus );
#endif
//
// Originally the default for all connection-oriented providers
// was SEC_PKG_ERROR. This seems less helpful than ACESS_DENIED,
// but we can't change it for NTLM and Kerberos because it might
// break old app code. SCHANNEL is new, so we can return ACCESS_DENIED.
//
// Datagram returns the bare error code.
//
if (AuthenticationService == RPC_C_AUTHN_GSS_SCHANNEL)
{
Status = RPC_S_ACCESS_DENIED;
}
else if (fDatagram)
{
// leave the error code alone
}
else
{
Status = RPC_S_SEC_PKG_ERROR;
}
}
RpcpErrorAddRecord (EEInfoGCRuntime,
Status,
EEInfoDLInitializeThirdLeg20,
SecurityStatus);
return Status;
}
if ( SecurityStatus == SEC_I_CONTINUE_NEEDED )
{
return(RPC_P_CONTINUE_NEEDED);
}
if ( SecurityStatus == SEC_I_COMPLETE_AND_CONTINUE )
{
return(RPC_P_COMPLETE_AND_CONTINUE);
}
ASSERT(SecurityStatus == SEC_E_OK
|| SecurityStatus == SEC_I_COMPLETE_NEEDED);
if ( (ImpersonationType == RPC_C_IMP_LEVEL_IDENTIFY) &&
(!(ContextAttributes & ISC_RET_IDENTIFY)) )
{
RpcpErrorAddRecord (EEInfoGCRuntime,
RPC_S_SEC_PKG_ERROR,
EEInfoDLInitializeThirdLeg30,
SEC_E_SECURITY_QOS_FAILED,
ImpersonationType,
ContextAttributes);
SetExtendedError(SEC_E_SECURITY_QOS_FAILED);
return (RPC_S_SEC_PKG_ERROR);
}
if ( (ImpersonationType == RPC_C_IMP_LEVEL_DELEGATE) &&
(!(ContextAttributes & ISC_RET_DELEGATE)) &&
(!(Capabilities & RPC_C_QOS_CAPABILITIES_IGNORE_DELEGATE_FAILURE)) &&
(!(IsIgnoreDelegationFailureSet())) )
{
RpcpErrorAddRecord (EEInfoGCRuntime,
RPC_S_SEC_PKG_ERROR,
EEInfoDLInitializeThirdLeg40,
SEC_E_SECURITY_QOS_FAILED,
ImpersonationType,
ContextAttributes,
Capabilities);
SetExtendedError(SEC_E_SECURITY_QOS_FAILED);
return (RPC_S_SEC_PKG_ERROR);
}
if ( (!(ContextAttributes & ISC_RET_MUTUAL_AUTH) )&&
(Capabilities & RPC_C_QOS_CAPABILITIES_MUTUAL_AUTH) )
{
RpcpErrorAddRecord (EEInfoGCRuntime,
RPC_S_SEC_PKG_ERROR,
EEInfoDLInitializeThirdLeg50,
SEC_E_SECURITY_QOS_FAILED,
ImpersonationType,
ContextAttributes);
SetExtendedError(SEC_E_SECURITY_QOS_FAILED);
return (RPC_S_SEC_PKG_ERROR);
}
if ( SecurityStatus == SEC_I_COMPLETE_NEEDED )
{
fFullyConstructed = TRUE;
Status = SetMaximumLengths();
if (Status != RPC_S_OK)
return Status;
return(RPC_P_COMPLETE_NEEDED);
}
fFullyConstructed = TRUE;
Status = SetMaximumLengths();
if (Status != RPC_S_OK)
return Status;
if (AuthenticationService == RPC_C_AUTHN_GSS_SCHANNEL)
{
return VerifyCertificate();
}
return(RPC_S_OK);
}
RPC_STATUS
SECURITY_CONTEXT::GetWireIdForSnego(
OUT unsigned char *WireId
)
{
SecPkgContext_NegotiationInfo NegoInfo;
SECURITY_STATUS SecStatus;
RPC_STATUS RpcStatus = RPC_S_OK;
if (AuthenticationService != RPC_C_AUTHN_GSS_NEGOTIATE)
return RPC_S_INVALID_BINDING;
ASSERT(RpcSecurityInterface != NULL);
SecStatus = (*RpcSecurityInterface->QueryContextAttributes)(
&SecurityContext,
SECPKG_ATTR_NEGOTIATION_INFO,
&NegoInfo);
if (SecStatus == SEC_E_OK)
{
if (NegoInfo.NegotiationState == SECPKG_NEGOTIATION_COMPLETE)
{
*WireId = (unsigned char) NegoInfo.PackageInfo->wRPCID;
}
else
RpcStatus = RPC_S_SEC_PKG_ERROR;
(*RpcSecurityInterface->FreeContextBuffer)(NegoInfo.PackageInfo);
}
else
{
SetExtendedError(SecStatus);
RpcStatus = RPC_S_OUT_OF_MEMORY;
}
return RpcStatus;
}
RPC_STATUS
SECURITY_CONTEXT::InqMarshalledTargetInfo (
OUT unsigned long *MarshalledTargetInfoLength,
OUT unsigned char **MarshalledTargetInfo
)
{
SecPkgContext_TargetInformation TargetInfo;
SECURITY_STATUS SecStatus;
RPC_STATUS RpcStatus = RPC_S_OK;
ASSERT(RpcSecurityInterface != NULL);
SecStatus = (*RpcSecurityInterface->QueryContextAttributes)(
&SecurityContext,
SECPKG_ATTR_TARGET_INFORMATION,
&TargetInfo);
if (SecStatus == SEC_E_OK)
{
// Security will allocate the string containing MarshalledTargetInfo.
// We can just pass it on to user and have the user free it.
*MarshalledTargetInfoLength = TargetInfo.MarshalledTargetInfoLength;
*MarshalledTargetInfo = TargetInfo.MarshalledTargetInfo;
ASSERT(
((*MarshalledTargetInfoLength == 0) && (*MarshalledTargetInfo == NULL)) ||
((*MarshalledTargetInfoLength != 0) && (*MarshalledTargetInfo != NULL))
);
}
else
{
SetExtendedError(SecStatus);
RpcStatus = RPC_S_OUT_OF_MEMORY;
}
return RpcStatus;
}
RPC_STATUS
SECURITY_CONTEXT::AcceptFirstTime (
IN SECURITY_CREDENTIALS * NewCredentials,
IN SECURITY_BUFFER_DESCRIPTOR PAPI * InputBufferDescriptor,
IN OUT SECURITY_BUFFER_DESCRIPTOR PAPI * OutputBufferDescriptor,
IN unsigned long AuthenticationLevel,
IN unsigned long DataRepresentation,
IN unsigned long NewContextNeededFlag
)
/*++
Routine Description:
Arguments:
Return Value:
RPC_S_OK - Everything worked just fine.
RPC_S_OUT_OF_MEMORY - Insufficient memory is available to complete the
operation.
RPC_P_CONTINUE_NEEDED - Indicates that everything is ok, but that we
need to send the output token back to the client, and then wait
for a token back from the client.
RPC_P_COMPLETE_NEEDED - Indicates that everyting is ok, but that we
need to call CompleteAuthToken before sending the message.
RPC_P_COMPLETE_AND_CONTINUE - Needs both a complete and a continue.
RPC_S_ACCESS_DENIED - Access is denied.
--*/
{
SECURITY_STATUS SecurityStatus;
TimeStamp TimeStamp;
unsigned long ContextRequirements;
RPC_STATUS RpcStatus;
DWORD Status = 0;
ASSERT( (SecuritySupportLoaded != 0)
&& (FailedToLoad == 0) );
if (Credentials)
{
Credentials->DereferenceCredentials();
}
Credentials = NewCredentials;
Credentials->ReferenceCredentials();
RpcSecurityInterface = Credentials->InquireProviderFunctionTable();
if (NewContextNeededFlag == 1)
{
DeleteSecurityContext();
}
switch ( AuthenticationLevel )
{
case RPC_C_AUTHN_LEVEL_CONNECT :
ContextRequirements = ASC_REQ_MUTUAL_AUTH;
break;
case RPC_C_AUTHN_LEVEL_PKT :
ContextRequirements = ASC_REQ_MUTUAL_AUTH
| ASC_REQ_REPLAY_DETECT;
break;
case RPC_C_AUTHN_LEVEL_PKT_INTEGRITY :
ContextRequirements = ASC_REQ_MUTUAL_AUTH
| ASC_REQ_REPLAY_DETECT | ASC_REQ_SEQUENCE_DETECT;
break;
case RPC_C_AUTHN_LEVEL_PKT_PRIVACY :
ContextRequirements = ASC_REQ_MUTUAL_AUTH
| ASC_REQ_REPLAY_DETECT | ASC_REQ_SEQUENCE_DETECT
| ASC_REQ_CONFIDENTIALITY;
break;
default :
ASSERT(AuthenticationLevel == RPC_C_AUTHN_LEVEL_CONNECT ||
AuthenticationLevel == RPC_C_AUTHN_LEVEL_PKT ||
AuthenticationLevel == RPC_C_AUTHN_LEVEL_PKT_INTEGRITY ||
AuthenticationLevel == RPC_C_AUTHN_LEVEL_PKT_PRIVACY );
return RPC_S_INTERNAL_ERROR;
}
if (fDatagram)
{
ContextRequirements |= ASC_REQ_DATAGRAM;
}
else
{
ContextRequirements |= ASC_REQ_CONNECTION;
}
if (Credentials->AuthenticationService == RPC_C_AUTHN_WINNT ||
Credentials->AuthenticationService == RPC_C_AUTHN_DCE_PRIVATE ||
Credentials->AuthenticationService == RPC_C_AUTHN_GSS_KERBEROS ||
Credentials->AuthenticationService == RPC_C_AUTHN_GSS_NEGOTIATE)
{
ContextRequirements |= ASC_REQ_USE_DCE_STYLE | ASC_REQ_DELEGATE;
}
if (AuthenticationService == RPC_C_AUTHN_WINNT
|| AuthenticationService == RPC_C_AUTHN_GSS_NEGOTIATE)
{
ContextRequirements |= ASC_REQ_ALLOW_NON_USER_LOGONS;
}
CallTestHook( TH_SECURITY_FN_ACCEPT1, this, &Status );
if (Status)
{
SetExtendedError(Status);
return Status;
}
SecurityStatus = (*RpcSecurityInterface->AcceptSecurityContext)(
Credentials->InquireCredHandle(), 0, InputBufferDescriptor,
ContextRequirements, DataRepresentation, &SecurityContext,
OutputBufferDescriptor, &ContextAttributes, &TimeStamp);
if ( ( SecurityStatus != SEC_E_OK )
&& ( SecurityStatus != SEC_I_CONTINUE_NEEDED )
&& ( SecurityStatus != SEC_I_COMPLETE_NEEDED )
&& ( SecurityStatus != SEC_I_COMPLETE_AND_CONTINUE ) )
{
RpcpErrorAddRecord (EEInfoGCSecurityProvider,
SecurityStatus,
EEInfoDLAcceptFirstTime10,
AuthenticationService,
AuthenticationLevel,
ContextRequirements);
SetExtendedError(SecurityStatus);
if ( (SecurityStatus == SEC_E_NO_CREDENTIALS)
|| (SecurityStatus == SEC_E_LOGON_DENIED)
|| (SecurityStatus == SEC_E_INVALID_TOKEN)
|| (SecurityStatus == SEC_E_NO_AUTHENTICATING_AUTHORITY)
|| (SecurityStatus == SEC_E_UNKNOWN_CREDENTIALS)
|| (SecurityStatus == SEC_E_CONTEXT_EXPIRED)
|| (SecurityStatus == SEC_E_TIME_SKEW)
|| (SecurityStatus == STATUS_AUTHENTICATION_FIREWALL_FAILED))
{
RpcStatus = RPC_S_ACCESS_DENIED;
}
else if ( SecurityStatus == SEC_E_SHUTDOWN_IN_PROGRESS )
{
RpcStatus = ERROR_SHUTDOWN_IN_PROGRESS;
}
else if (SecurityStatus == SEC_E_SECPKG_NOT_FOUND)
{
RpcStatus = RPC_S_UNKNOWN_AUTHN_SERVICE;
}
else
RpcStatus = RPC_S_OUT_OF_MEMORY;
}
else
{
RpcStatus = RPC_S_OK;
}
// We need to make sure that the context returned supports the required
// functionality.
// This needs to be done only for a completed context.
if (SecurityStatus == SEC_E_OK)
{
if (((ContextRequirements & ASC_REQ_REPLAY_DETECT) && !(ContextAttributes & ASC_RET_REPLAY_DETECT))
|| ((ContextRequirements & ASC_REQ_SEQUENCE_DETECT) && !(ContextAttributes & ASC_RET_SEQUENCE_DETECT))
|| ((ContextRequirements & ASC_REQ_CONFIDENTIALITY) && !(ContextAttributes & ASC_RET_CONFIDENTIALITY)))
{
RpcStatus = RPC_S_ACCESS_DENIED;
}
}
if (RpcStatus != RPC_S_OK)
{
RpcpErrorAddRecord (EEInfoGCRuntime,
RpcStatus,
EEInfoDLAcceptFirstTime20,
SecurityStatus);
return RpcStatus;
}
DontForgetToDelete = 1;
Flags = ContextRequirements;
if ( SecurityStatus == SEC_I_CONTINUE_NEEDED )
{
return(RPC_P_CONTINUE_NEEDED);
}
if ( SecurityStatus == SEC_I_COMPLETE_AND_CONTINUE )
{
return(RPC_P_COMPLETE_AND_CONTINUE);
}
ASSERT((SecurityStatus == SEC_I_COMPLETE_NEEDED)
|| (SecurityStatus == SEC_E_OK));
fFullyConstructed = TRUE;
RpcStatus = SetMaximumLengths();
if (RpcStatus != RPC_S_OK)
return RpcStatus;
Credentials->DereferenceCredentials();
Credentials = 0;
if ( SecurityStatus == SEC_I_COMPLETE_NEEDED )
return(RPC_P_COMPLETE_NEEDED);
else
return(RPC_S_OK);
}
RPC_STATUS
SECURITY_CONTEXT::AcceptThirdLeg (
IN unsigned long DataRepresentation,
IN SECURITY_BUFFER_DESCRIPTOR PAPI * BufferDescriptor,
OUT SECURITY_BUFFER_DESCRIPTOR PAPI * OutBufferDescriptor
)
/*++
Routine Description:
Arguments:
Return Value:
RPC_S_OK - Everything worked just fine.
RPC_S_OUT_OF_MEMORY - Insufficient memory is available to complete the
operation.
RPC_S_ACCESS_DENIED - Access is denied.
RPC_P_COMPLETE_NEEDED - Indicates that everyting is ok, but that we
need to call CompleteAuthToken before sending the message.
--*/
{
SECURITY_STATUS SecurityStatus;
TimeStamp TimeStamp;
RPC_STATUS RpcStatus;
// Set when AcceptSecurityContext returns a failure.
BOOL ASCReturnedFailure = FALSE;
// Set when AcceptSecurityContext suceeds but returns ContextAttributes
// which indicate that the resulting security context does not support
// the required security settings and is unusable.
BOOL ASCReturnedBadContextAttributes = FALSE;
ASSERT( (SecuritySupportLoaded != 0)
&& (FailedToLoad == 0) );
SetLastError(0);
{
DWORD Status = 0;
CallTestHook( TH_SECURITY_FN_ACCEPT3, this, &Status );
if (Status)
{
FailedContext = Status;
SetExtendedError(Status);
return Status;
}
}
SecurityStatus = (*RpcSecurityInterface->AcceptSecurityContext)(
Credentials->InquireCredHandle(),
&SecurityContext,
BufferDescriptor,
Flags,
DataRepresentation,
&SecurityContext,
OutBufferDescriptor,
&ContextAttributes,
&TimeStamp
);
//
// If 3rd Leg Failed Bit is set, map all errors other than out of memory
// to SUCCESS
//
if (
(
( SecurityStatus != SEC_E_OK )
&& ( SecurityStatus != SEC_I_COMPLETE_NEEDED)
&& ( SecurityStatus != SEC_I_CONTINUE_NEEDED)
&& ( SecurityStatus != SEC_I_COMPLETE_AND_CONTINUE)
&& ( SecurityStatus != SEC_E_INSUFFICIENT_MEMORY )
&& ( ContextAttributes & ASC_RET_THIRD_LEG_FAILED )
)
||
(
( SecurityStatus == SEC_E_LOGON_DENIED )
|| ( SecurityStatus == SEC_E_NO_CREDENTIALS )
|| ( SecurityStatus == SEC_E_INVALID_TOKEN )
|| ( SecurityStatus == SEC_E_UNKNOWN_CREDENTIALS )
|| ( SecurityStatus == SEC_E_NO_AUTHENTICATING_AUTHORITY )
|| ( SecurityStatus == SEC_E_TIME_SKEW )
|| ( SecurityStatus == STATUS_AUTHENTICATION_FIREWALL_FAILED )
)
)
{
ASCReturnedFailure = TRUE;
}
// We need to make sure that the context returned supports the required
// functionality.
// This needs to be done only for a completed context.
if (SecurityStatus == SEC_E_OK)
{
if (((Flags & ASC_REQ_REPLAY_DETECT) && !(ContextAttributes & ASC_RET_REPLAY_DETECT))
|| ((Flags & ASC_REQ_SEQUENCE_DETECT) && !(ContextAttributes & ASC_RET_SEQUENCE_DETECT))
|| ((Flags & ASC_REQ_CONFIDENTIALITY) && !(ContextAttributes & ASC_RET_CONFIDENTIALITY)))
{
ASCReturnedBadContextAttributes = TRUE;
}
}
if (ASCReturnedFailure || ASCReturnedBadContextAttributes)
{
if (ASCReturnedFailure)
FailedContext = GetLastError();
else
FailedContext = RPC_S_ACCESS_DENIED;
RpcpErrorAddRecord (EEInfoGCSecurityProvider,
SecurityStatus,
EEInfoDLAcceptThirdLeg10,
AuthenticationService,
AuthenticationLevel,
FailedContext);
if (!fDatagram)
{
SecurityStatus = SEC_E_OK;
}
SetExtendedError(SecurityStatus);
if (ASCReturnedFailure)
{
if ( (FailedContext != ERROR_PASSWORD_MUST_CHANGE)
&& (FailedContext != ERROR_PASSWORD_EXPIRED)
&& (FailedContext != ERROR_ACCOUNT_DISABLED)
&& (FailedContext != ERROR_INVALID_LOGON_HOURS) )
{
FailedContext = RPC_S_ACCESS_DENIED;
RpcpErrorAddRecord (EEInfoGCRuntime,
FailedContext,
EEInfoDLAcceptThirdLeg30);
}
}
ASSERT(FailedContextEEInfo == NULL);
FailedContextEEInfo = RpcpGetEEInfo();
RpcpClearEEInfo();
}
if ( ( SecurityStatus != SEC_E_OK )
&& ( SecurityStatus != SEC_I_COMPLETE_NEEDED )
&& ( SecurityStatus != SEC_I_CONTINUE_NEEDED )
&& ( SecurityStatus != SEC_I_COMPLETE_AND_CONTINUE ) )
{
SetExtendedError(SecurityStatus);
RpcpErrorAddRecord (EEInfoGCSecurityProvider,
SecurityStatus,
EEInfoDLAcceptThirdLeg20,
AuthenticationService,
AuthenticationLevel);
if ( SecurityStatus == SEC_E_SHUTDOWN_IN_PROGRESS )
{
RpcStatus = ERROR_SHUTDOWN_IN_PROGRESS;
}
else if ( (SecurityStatus == SEC_E_SECPKG_NOT_FOUND)
|| (SecurityStatus == SEC_E_NO_CREDENTIALS)
|| (SecurityStatus == SEC_E_LOGON_DENIED)
|| (SecurityStatus == SEC_E_INVALID_TOKEN)
|| (SecurityStatus == SEC_E_UNKNOWN_CREDENTIALS)
|| (SecurityStatus == SEC_E_NO_AUTHENTICATING_AUTHORITY)
|| (SecurityStatus == SEC_E_CONTEXT_EXPIRED)
|| (SecurityStatus == SEC_E_TIME_SKEW)
|| (SecurityStatus == STATUS_AUTHENTICATION_FIREWALL_FAILED))
{
RpcStatus = RPC_S_ACCESS_DENIED;
}
else
{
ASSERT( SecurityStatus == SEC_E_INSUFFICIENT_MEMORY );
RpcStatus = RPC_S_OUT_OF_MEMORY;
}
RpcpErrorAddRecord (EEInfoGCRuntime,
RpcStatus,
EEInfoDLAcceptThirdLeg40,
SecurityStatus);
return RpcStatus;
}
if ( SecurityStatus == SEC_I_CONTINUE_NEEDED )
{
return(RPC_P_CONTINUE_NEEDED);
}
if ( SecurityStatus == SEC_I_COMPLETE_AND_CONTINUE )
{
return(RPC_P_COMPLETE_AND_CONTINUE);
}
ASSERT ( (SecurityStatus == SEC_I_COMPLETE_NEEDED)
|| (SecurityStatus == SEC_E_OK));
fFullyConstructed = TRUE;
RpcStatus = SetMaximumLengths();
if (RpcStatus)
{
FailedContext = RpcStatus;
ASSERT(FailedContextEEInfo == NULL);
FailedContextEEInfo = RpcpGetEEInfo();
RpcpClearEEInfo();
//
// We don't want to block third leg - mimic success
// Failed context has already been set
//
MaxSignatureLength = 256;
MaxHeaderLength = 256;
cbBlockSize = 64;
}
if (SecurityStatus == SEC_I_COMPLETE_NEEDED)
return(RPC_P_COMPLETE_NEEDED);
else
return RPC_S_OK;
}
unsigned long
SECURITY_CONTEXT::InquireAuthorizationService (
)
/*++
Return Value:
The authorization service for this security context will be returned.
--*/
{
SecPkgContext_DceInfo DceInfo;
SECURITY_STATUS SecurityStatus;
SecurityStatus = (*RpcSecurityInterface->QueryContextAttributes)(
&SecurityContext, SECPKG_ATTR_DCE_INFO, &DceInfo);
ASSERT( SecurityStatus == SEC_E_OK );
return(DceInfo.AuthzSvc);
}
RPC_AUTHZ_HANDLE
SECURITY_CONTEXT::InquirePrivileges (
)
/*++
Return Value:
The privileges of the client at the other end of this security context
will be returned.
--*/
{
SecPkgContext_DceInfo DceInfo;
SECURITY_STATUS SecurityStatus;
SecurityStatus = (*RpcSecurityInterface->QueryContextAttributes)(
&SecurityContext, SECPKG_ATTR_DCE_INFO, &DceInfo);
ASSERT( SecurityStatus == SEC_E_OK );
return(DceInfo.pPac);
}
RPC_STATUS
SECURITY_CONTEXT::GetDceInfo (
RPC_AUTHZ_HANDLE __RPC_FAR * PacHandle,
unsigned long __RPC_FAR * AuthzSvc
)
/*++
Return Value:
The privileges of the client at the other end of this security context
will be returned.
--*/
{
SecPkgContext_DceInfo DceInfo;
SECURITY_STATUS SecurityStatus;
*PacHandle = 0;
*AuthzSvc = 0;
SecurityStatus = (*RpcSecurityInterface->QueryContextAttributes)(
&SecurityContext, SECPKG_ATTR_DCE_INFO, &DceInfo);
ASSERT( (SecurityStatus == SEC_E_OK)
|| (SecurityStatus == SEC_E_UNSUPPORTED_FUNCTION)
|| (SecurityStatus == SEC_E_INVALID_HANDLE));
if (SecurityStatus == SEC_E_OK)
{
*PacHandle = DceInfo.pPac;
*AuthzSvc = DceInfo.AuthzSvc;
return RPC_S_OK;
}
if (AuthenticationService == RPC_C_AUTHN_GSS_SCHANNEL)
{
SecurityStatus = (*RpcSecurityInterface->QueryContextAttributes)(
&SecurityContext, SECPKG_ATTR_REMOTE_CERT_CONTEXT, PacHandle);
if (SecurityStatus != SEC_E_OK)
{
*PacHandle = 0;
}
}
if (SecurityStatus != SEC_E_OK)
{
return RPC_S_SEC_PKG_ERROR;
}
return RPC_S_OK;
}
void
SECURITY_CONTEXT::DeleteSecurityContext (
void
)
/*++
Routine Description:
If there is a valid security context, we need to delete it.
--*/
{
SECURITY_STATUS SecurityStatus;
unsigned int nRetries = 0;
if ( DontForgetToDelete != 0 )
{
if (AuthzClientContext)
{
AuthzFreeContextFn(AuthzClientContext);
AuthzClientContext = NULL;
}
// SEC_E_INSUFFICIENT_MEMORY may be returned under extremely low
// memory conditions. We will do the following:
// - Retry 10 times to delete the security context.
// - Raise a flag in the process that we have leaked one or more security contexts.
do
{
SecurityStatus = (*RpcSecurityInterface->DeleteSecurityContext)(
&SecurityContext );
nRetries++;
}
while (SecurityStatus == SEC_E_INSUFFICIENT_MEMORY && nRetries < 10);
if (SecurityStatus == SEC_E_INSUFFICIENT_MEMORY)
{
nSecurityStructuresLeaked++;
}
// when the process shutdowns, the security system may return SEC_E_SECPKG_NOT_FOUND
// since it is uninitialized. This is the only time when this error will be
// returned, so it is safe to ignore.
if (SecurityStatus == SEC_E_SECPKG_NOT_FOUND)
SecurityStatus = SEC_E_OK;
#if DBG
if ((SecurityStatus != SEC_E_OK) && (SecurityStatus != SEC_E_SHUTDOWN_IN_PROGRESS))
{
PrintToDebugger("DeleteSecurityContext(0x%x) Returned [%lx]\n",
&SecurityContext,
SecurityStatus);
}
if (SecurityStatus == SEC_E_INSUFFICIENT_MEMORY)
{
PrintToDebugger("SecurityContext(0x%x) leaked\n",
&SecurityContext);
}
#endif
ASSERT( SecurityStatus == SEC_E_OK ||
SecurityStatus == SEC_E_SHUTDOWN_IN_PROGRESS ||
SecurityStatus == SEC_E_INSUFFICIENT_MEMORY);
DontForgetToDelete = 0;
}
if (FailedContextEEInfo)
{
FreeEEInfoChain(FailedContextEEInfo);
FailedContextEEInfo = NULL;
}
}
RPC_STATUS
SECURITY_CONTEXT::CheckForFailedThirdLeg (
void
)
/*++
Routine Description:
If the third leg has failed, we will return the error code
and restore the eeinfo.
--*/
{
ASSERT( ( SecuritySupportLoaded != 0 )
&& ( FailedToLoad == 0 ) );
if (FailedContext != 0)
{
if (FailedContextEEInfo)
{
RpcpSetEEInfo(FailedContextEEInfo);
FailedContextEEInfo = NULL;
}
return (RPC_S_ACCESS_DENIED);
}
return RPC_S_OK;
}
void
SECURITY_CONTEXT::DeletePac(
void __RPC_FAR * PacHandle
)
/*++
Return Value:
--*/
{
if (AuthenticationService == RPC_C_AUTHN_GSS_SCHANNEL)
{
if (!LoadCrypt32Imports())
{
return;
}
CertFreeCertificateContext( (PCERT_CONTEXT) PacHandle );
}
else
{
(*RpcSecurityInterface->FreeContextBuffer)( PacHandle );
}
}
RPC_STATUS
SECURITY_CONTEXT::ImpersonateClient (
)
/*++
Routine Description:
The server thread calling this routine will impersonate the client at the
other end of this security context.
Return Value:
RPC_S_OK - The impersonation successfully occured.
RPC_S_NO_CONTEXT_AVAILABLE - There is no security context available to
be impersonated.
--*/
{
SECURITY_STATUS SecurityStatus;
ASSERT( ( SecuritySupportLoaded != 0 )
&& ( FailedToLoad == 0 ) );
if (FailedContext != 0)
{
if (FailedContextEEInfo)
{
RpcpSetEEInfo(FailedContextEEInfo);
FailedContextEEInfo = NULL;
}
return (RPC_S_ACCESS_DENIED);
}
ASSERT( FullyConstructed() );
SecurityStatus = (*RpcSecurityInterface->ImpersonateSecurityContext)(
&SecurityContext);
if ( SecurityStatus != SEC_E_OK )
{
RpcpErrorAddRecord (EEInfoGCSecurityProvider,
RPC_S_NO_CONTEXT_AVAILABLE,
EEInfoDLImpersonateClient10,
SecurityStatus,
AuthenticationService,
AuthenticationLevel);
ASSERT( SecurityStatus == SEC_E_NO_IMPERSONATION );
return(RPC_S_NO_CONTEXT_AVAILABLE);
}
return(RPC_S_OK);
}
void
SECURITY_CONTEXT::RevertToSelf (
)
/*++
Routine Description:
The server thread calling this routine will stop impersonating. If the
thread is not impersonating, then this is a noop.
--*/
{
SECURITY_STATUS SecurityStatus;
ASSERT( ( SecuritySupportLoaded != 0 )
&& ( FailedToLoad == 0 ) );
SecurityStatus = (*RpcSecurityInterface->RevertSecurityContext)(
&SecurityContext);
ASSERT( SecurityStatus == SEC_E_OK );
}
RPC_STATUS
SECURITY_CONTEXT::GetAccessToken (
OUT HANDLE *ImpersonationToken,
OUT BOOL *fNeedToCloseToken
)
/*++
Routine Description:
Gets the access token maintained by the security provider.
Arguments:
ImpersonationToken - contains the impersonation token on success
fNeedToCloseToken - true if the resulting token needs closing.
False otherwise. Some security providers support handing off
of the token itself (faster). Some don't. All support handing
off a copy of the token. Depending on what security provider
we have, we'll get the token, and set this variable. This
parameter is undefined in case of failure.
Return Value:
RPC_S_OK on success, or RPC_S_* on failure. Supports EEInfo.
--*/
{
SECURITY_STATUS SecurityStatus;
HANDLE Token;
ASSERT( ( SecuritySupportLoaded != 0 )
&& ( FailedToLoad == 0 ) );
ASSERT( FullyConstructed() );
SecurityStatus = (*RpcSecurityInterface->QueryContextAttributes)(
&SecurityContext,
SECPKG_ATTR_ACCESS_TOKEN,
&Token);
if ( (SecurityStatus != SEC_E_OK)
&& (SecurityStatus != SEC_E_UNSUPPORTED_FUNCTION) )
{
RpcpErrorAddRecord (EEInfoGCSecurityProvider,
RPC_S_NO_CONTEXT_AVAILABLE,
EEInfoDLSECURITY_CONTEXT__GetAccessToken10,
SecurityStatus,
AuthenticationService,
AuthenticationLevel);
ASSERT( 0 );
return(RPC_S_NO_CONTEXT_AVAILABLE);
}
if (SecurityStatus != SEC_E_OK)
{
ASSERT(SecurityStatus == SEC_E_UNSUPPORTED_FUNCTION);
// the security provider does not provide quick retrieval
// of token - go the long way
SecurityStatus = (*RpcSecurityInterface->QuerySecurityContextToken)(
&SecurityContext, &Token);
if (SecurityStatus != SEC_E_OK)
{
RpcpErrorAddRecord (EEInfoGCSecurityProvider,
RPC_S_NO_CONTEXT_AVAILABLE,
EEInfoDLSECURITY_CONTEXT__GetAccessToken20,
SecurityStatus,
AuthenticationService,
AuthenticationLevel);
ASSERT( SecurityStatus == SEC_E_NO_IMPERSONATION );
return(RPC_S_NO_CONTEXT_AVAILABLE);
}
*fNeedToCloseToken = TRUE;
}
else
{
*fNeedToCloseToken = FALSE;
}
*ImpersonationToken = Token;
return RPC_S_OK;
}
PACKAGE_LEG_COUNT
GetPackageLegCount(
DWORD id
)
/*++
Routine Description:
This fn determines whether the given security package is a 3- or 4-leg
protocol. The relevance of this information is described in
ReadPackageLegInfo(). This fn. first searches the hardcoded entries in
PredefinedPackageLegInfo[], and if the package is not found it turns to the
registry information stored in FourLeggedPackages[].
Return Values:
LegsUnknown = the fn cannot give a reliable answer
ThreeLegs = this is a 3-leg protocol
EvenNumberOfLegs = this is not a 3-leg protocol
--*/
{
int i;
if ( InsureSecuritySupportLoaded() != RPC_S_OK )
{
return LegsUnknown;
}
for (i=0; PredefinedPackageLegInfo[i].Package != 0; ++i)
{
if (PredefinedPackageLegInfo[i].Package == id)
{
return PredefinedPackageLegInfo[i].Legs;
}
}
CLAIM_MUTEX Lock( *SecurityCritSect );
if (!FourLeggedPackages)
{
if (!ReadPackageLegInfo())
{
return LegsUnknown;
}
}
ASSERT(FourLeggedPackages);
for (i=0; FourLeggedPackages[i] != 0; ++i)
{
if (FourLeggedPackages[i] == id)
{
return EvenNumberOfLegs;
}
}
return ThreeLegs;
}
DWORD
SECURITY_CONTEXT::VerifyCertificate()
{
DWORD SecurityStatus = 0;
//
// Compare the name on the certificate against the expected principal name.
//
if (ServerPrincipalName)
{
//
// Get a copy of the raw certificate.
//
if (!LoadCrypt32Imports())
{
return GetLastError();
}
PCERT_CONTEXT ClientContext;
SecurityStatus = (*RpcSecurityInterface->QueryContextAttributes)(
&SecurityContext,
SECPKG_ATTR_REMOTE_CERT_CONTEXT,
&ClientContext
);
if (SecurityStatus)
{
RpcpErrorAddRecord ( EEInfoGCSecurityProvider,
RPC_S_OUT_OF_MEMORY,
EEInfoDLInitializeThirdLeg60,
SecurityStatus );
ASSERT( SecurityStatus == SEC_E_INSUFFICIENT_MEMORY );
return SecurityStatus;
}
SecurityStatus = RpcCertMatchPrincipalName( ClientContext, ServerPrincipalName );
switch (SecurityStatus)
{
case 0:
case ERROR_NOT_ENOUGH_MEMORY:
break;
default:
//
// we are supposed to have verified the princ name earlier.
//
ASSERT( SecurityStatus != ERROR_INVALID_PARAMETER );
SetExtendedError(SecurityStatus);
SecurityStatus = RPC_S_ACCESS_DENIED;
break;
}
CertFreeCertificateContext( ClientContext );
}
return SecurityStatus;
}