//
// for Digest, the server will send us the blob immediately, so we need to make sure we
// call InitializeSecurityContext() a first time with a null input buffer, otherwise
// the next call will fail. do so here:
// WDigest.dll requires us to pass in 3 security buffers here
// 1) BufferType: SECBUFFER_TOKEN, Content: server's challenge (incoming)
// 2) BufferType: SECBUFFER_PKG_PARAMS, Content: request's HTTP Method
// 3) BufferType: SECBUFFER_PKG_PARAMS, Content: the HEntity (this would be the MD5 footprint of the request entity
// body, we can pass in NULL as this is not required)
//
public string GetOutgoingDigestBlob(string incomingBlob, string requestMethod, out bool handshakeComplete)
{
GlobalLog.Enter("NTAuthentication::GetOutgoingDigestBlob", incomingBlob);
//
// first time call with null incoming buffer to initialize.
// we should get back a 0x90312 and a null outgoingBlob.
//
byte[] decodedOutgoingBlob = GetOutgoingBlob(null, out handshakeComplete);
GlobalLog.Assert(!handshakeComplete, "NTAuthentication::GetOutgoingDigestBlob() handshakeComplete==true", "");
GlobalLog.Assert(decodedOutgoingBlob == null, "NTAuthentication::GetOutgoingDigestBlob() decodedOutgoingBlob!=null", "");
//
// second time call with 3 incoming buffers to select HTTP client.
// we should get back a SecurityStatus.OK and a non null outgoingBlob.
//
byte[] decodedIncomingBlob = Encoding.Default.GetBytes(incomingBlob);
byte[] decodedRequestMethod = Encoding.Default.GetBytes(requestMethod);
int requestedFlags =
(int)ContextFlags.Delegate |
(int)ContextFlags.MutualAuth |
(int)ContextFlags.ReplayDetect |
(int)ContextFlags.SequenceDetect |
// (int)ContextFlags.Confidentiality | // this would only work if the server provided a qop="auth-conf" directive
// (int)ContextFlags.ClientIntegrity | // this would only work if the server provided a qop="auth-int" directive
(int)ContextFlags.Connection;
SecurityBufferClass[] inSecurityBuffers = new SecurityBufferClass[] {
new SecurityBufferClass(decodedIncomingBlob, BufferType.Token),
new SecurityBufferClass(decodedRequestMethod, BufferType.Parameters),
new SecurityBufferClass(null, BufferType.Parameters),
};
SecurityBufferClass[] outSecurityBuffers = new SecurityBufferClass[] {
new SecurityBufferClass(m_TokenSize, BufferType.Token),
};
SecurityContext newSecurityContext = new SecurityContext(GlobalSSPI.SSPIAuth);
//
// this call is still returning an error. fix together with Kevin Damour
//
int status =
SSPIWrapper.InitializeSecurityContext(
GlobalSSPI.SSPIAuth,
m_CredentialsHandle.Handle,
m_SecurityContext.Handle,
m_RemotePeerId, // this must match the Uri in the HTTP status line for the current request
requestedFlags,
m_Endianness,
inSecurityBuffers,
ref newSecurityContext.Handle,
outSecurityBuffers,
ref m_ContextFlags,
ref newSecurityContext.TimeStamp);
GlobalLog.Print("NTAuthentication::GetOutgoingDigestBlob() SSPIWrapper.InitializeSecurityContext() returns 0x" + string.Format("{0:x}", status));
int errorCode = status & unchecked ((int)0x80000000);
if (errorCode != 0)
{
throw new Win32Exception(status);
}
//
// the return value from SSPI will tell us correctly if the
// handshake is over or not: http://msdn.microsoft.com/library/psdk/secspi/sspiref_67p0.htm
// we also have to consider the case in which SSPI formed a new context, in this case we're done as well.
//
IsCompleted = (status == (int)SecurityStatus.OK) || (m_SecurityContext.Handle != -1 && m_SecurityContext.Handle != newSecurityContext.Handle);
if (IsCompleted)
{
// ... if we're done, clean the handle up or the call to UpdateHandle() might leak it.
SSPIWrapper.DeleteSecurityContext(m_SecurityContext.m_SecModule, m_SecurityContext.Handle);
}
handshakeComplete = IsCompleted;
m_Authenticated = m_SecurityContext.Handle != -1;
m_SecurityContext.UpdateHandle(newSecurityContext);
#if TRAVE
if (handshakeComplete)
{
//
// Kevin Damour says:
// You should not query the securitycontext until you have actually formed one (
// with a success return form ISC). It is only a partially formed context and
// no info is available to user applications (at least for digest).
//
SecurityPackageInfoClass securityPackageInfo = (SecurityPackageInfoClass)SSPIWrapper.QueryContextAttributes(GlobalSSPI.SSPIAuth, m_SecurityContext, ContextAttribute.PackageInfo);
GlobalLog.Print("SecurityPackageInfoClass: using:[" + ((securityPackageInfo == null)?"null":securityPackageInfo.ToString()) + "]");
}
#endif // #if TRAVE
GlobalLog.Assert(outSecurityBuffers.Length == 1, "NTAuthentication::GetOutgoingDigestBlob() outSecurityBuffers.Length==" + outSecurityBuffers.Length.ToString(), "");
GlobalLog.Print("out token = " + m_TokenSize.ToString() + " size = " + outSecurityBuffers[0].size.ToString());
GlobalLog.Dump(outSecurityBuffers[0].token);
GlobalLog.Print("NTAuthentication::GetOutgoingDigestBlob() handshakeComplete:" + handshakeComplete.ToString());
decodedOutgoingBlob = outSecurityBuffers[0].token;
string outgoingBlob = null;
if (decodedOutgoingBlob != null && decodedOutgoingBlob.Length > 0)
{
// CONSIDER V.NEXT
// review Encoding.Default.GetString usage here because it might
// end up creating non ANSI characters in the string
outgoingBlob = Encoding.Default.GetString(decodedOutgoingBlob, 0, outSecurityBuffers[0].size);
}
GlobalLog.Leave("NTAuthentication::GetOutgoingDigestBlob", outgoingBlob);
return(outgoingBlob);
}