/*++
* GenerateToken - Called after each successive state
* in the Client - Server handshake. This function
* generates a set of bytes that will be sent next to
* the server. The server responds, each response,
* is pass then into this function, again, and the cycle
* repeats until successful connection, or failure.
*
* Input:
* input - bytes from the wire
* output - ref to byte [], what we will send to the
* server in response
* Return:
* errorCode - an SSPI error code
* --*/
private SecurityStatusPal GenerateToken(byte[] input, int offset, int count, ref byte[] output)
{
#if TRACE_VERBOSE
GlobalLog.Enter("SecureChannel#" + Logging.HashString(this) + "::GenerateToken, _refreshCredentialNeeded = " + _refreshCredentialNeeded);
#endif
if (offset < 0 || offset > (input == null ? 0 : input.Length))
{
GlobalLog.Assert(false, "SecureChannel#" + Logging.HashString(this) + "::GenerateToken", "Argument 'offset' out of range.");
throw new ArgumentOutOfRangeException("offset");
}
if (count < 0 || count > (input == null ? 0 : input.Length - offset))
{
GlobalLog.Assert(false, "SecureChannel#" + Logging.HashString(this) + "::GenerateToken", "Argument 'count' out of range.");
throw new ArgumentOutOfRangeException("count");
}
SecurityBuffer incomingSecurity = null;
SecurityBuffer[] incomingSecurityBuffers = null;
if (input != null)
{
incomingSecurity = new SecurityBuffer(input, offset, count, SecurityBufferType.Token);
incomingSecurityBuffers = new SecurityBuffer[]
{
incomingSecurity,
new SecurityBuffer(null, 0, 0, SecurityBufferType.Empty)
};
}
SecurityBuffer outgoingSecurity = new SecurityBuffer(null, SecurityBufferType.Token);
SecurityStatusPal errorCode = 0;
bool cachedCreds = false;
byte[] thumbPrint = null;
//
// Looping through ASC or ISC with potentially cached credential that could have been
// already disposed from a different thread before ISC or ASC dir increment a cred ref count.
//
try
{
do
{
thumbPrint = null;
if (_refreshCredentialNeeded)
{
cachedCreds = _serverMode
? AcquireServerCredentials(ref thumbPrint)
: AcquireClientCredentials(ref thumbPrint);
}
if (_serverMode)
{
errorCode = SslStreamPal.AcceptSecurityContext(
ref _credentialsHandle,
ref _securityContext,
incomingSecurity,
outgoingSecurity,
_remoteCertRequired);
}
else
{
if (incomingSecurity == null)
{
errorCode = SslStreamPal.InitializeSecurityContext(
ref _credentialsHandle,
ref _securityContext,
_destination,
incomingSecurity,
outgoingSecurity);
}
else
{
errorCode = SslStreamPal.InitializeSecurityContext(
_credentialsHandle,
ref _securityContext,
_destination,
incomingSecurityBuffers,
outgoingSecurity);
}
}
} while (cachedCreds && _credentialsHandle == null);
}
finally
{
if (_refreshCredentialNeeded)
{
_refreshCredentialNeeded = false;
//
// Assuming the ISC or ASC has referenced the credential,
// we want to call dispose so to decrement the effective ref count.
//
if (_credentialsHandle != null)
{
_credentialsHandle.Dispose();
}
//
// This call may bump up the credential reference count further.
// Note that thumbPrint is retrieved from a safe cert object that was possible cloned from the user passed cert.
//
if (!cachedCreds && _securityContext != null && !_securityContext.IsInvalid && _credentialsHandle != null && !_credentialsHandle.IsInvalid)
{
SslSessionsCache.CacheCredential(_credentialsHandle, thumbPrint, _sslProtocols, _serverMode, _encryptionPolicy);
}
}
}
output = outgoingSecurity.token;
#if TRACE_VERBOSE
GlobalLog.Leave("SecureChannel#" + Logging.HashString(this) + "::GenerateToken()", Interop.MapSecurityStatus((uint)errorCode));
#endif
return((SecurityStatusPal)errorCode);
}