// for Server side (IIS 6.0) see: \\netindex\Sources\inetsrv\iis\iisrearc\iisplus\ulw3\digestprovider.cxx // for Client side (HTTP.SYS) see: \\netindex\Sources\net\http\sys\ucauth.c internal string GetOutgoingDigestBlob(string incomingBlob, string requestMethod, string requestedUri, string realm, bool isClientPreAuth, bool throwOnError, out SecurityStatus statusCode) { GlobalLog.Enter("NTAuthentication#" + ValidationHelper.HashString(this) + "::GetOutgoingDigestBlob", incomingBlob); // second time call with 3 incoming buffers to select HTTP client. // we should get back a SecurityStatus.OK and a non null outgoingBlob. SecurityBuffer[] inSecurityBuffers = null; SecurityBuffer outSecurityBuffer = new SecurityBuffer(m_TokenSize, isClientPreAuth ? BufferType.Parameters : BufferType.Token); bool firstTime = m_SecurityContext == null; try { if (!m_IsServer) { // client session if (!isClientPreAuth) { if (incomingBlob != null) { List<SecurityBuffer> list = new List<SecurityBuffer>(5); list.Add(new SecurityBuffer(WebHeaderCollection.HeaderEncoding.GetBytes(incomingBlob), BufferType.Token)); list.Add(new SecurityBuffer(WebHeaderCollection.HeaderEncoding.GetBytes(requestMethod), BufferType.Parameters)); list.Add(new SecurityBuffer(null, BufferType.Parameters)); list.Add(new SecurityBuffer(Encoding.Unicode.GetBytes(m_Spn), BufferType.TargetHost)); if (m_ChannelBinding != null) { list.Add(new SecurityBuffer(m_ChannelBinding)); } inSecurityBuffers = list.ToArray(); } statusCode = (SecurityStatus) SSPIWrapper.InitializeSecurityContext( GlobalSSPI.SSPIAuth, m_CredentialsHandle, ref m_SecurityContext, requestedUri, // this must match the Uri in the HTTP status line for the current request m_RequestedContextFlags, Endianness.Network, inSecurityBuffers, outSecurityBuffer, ref m_ContextFlags ); GlobalLog.Print("NTAuthentication#" + ValidationHelper.HashString(this) + "::GetOutgoingDigestBlob() SSPIWrapper.InitializeSecurityContext() returns statusCode:0x" + ((int) statusCode).ToString("x8", NumberFormatInfo.InvariantInfo) + " (" + statusCode.ToString() + ")"); } else { #if WDIGEST_PREAUTH inSecurityBuffers = new SecurityBuffer[] { new SecurityBuffer(null, BufferType.Token), new SecurityBuffer(WebHeaderCollection.HeaderEncoding.GetBytes(requestMethod), BufferType.Parameters), new SecurityBuffer(WebHeaderCollection.HeaderEncoding.GetBytes(requestedUri), BufferType.Parameters), new SecurityBuffer(null, BufferType.Parameters), outSecurityBuffer, }; statusCode = (SecurityStatus) SSPIWrapper.MakeSignature(GlobalSSPI.SSPIAuth, m_SecurityContext, inSecurityBuffers, 0); GlobalLog.Print("NTAuthentication#" + ValidationHelper.HashString(this) + "::GetOutgoingDigestBlob() SSPIWrapper.MakeSignature() returns statusCode:0x" + ((int) statusCode).ToString("x8", NumberFormatInfo.InvariantInfo) + " (" + statusCode.ToString() + ")"); #else statusCode = SecurityStatus.OK; GlobalLog.Assert("NTAuthentication#" + ValidationHelper.HashString(this) + "::GetOutgoingDigestBlob()", "Invalid code path."); #endif } } else { // server session List<SecurityBuffer> list = new List<SecurityBuffer>(6); list.Add(incomingBlob == null ? new SecurityBuffer(0, BufferType.Token) : new SecurityBuffer(WebHeaderCollection.HeaderEncoding.GetBytes(incomingBlob), BufferType.Token)); list.Add(requestMethod == null ? new SecurityBuffer(0, BufferType.Parameters) : new SecurityBuffer(WebHeaderCollection.HeaderEncoding.GetBytes(requestMethod), BufferType.Parameters)); list.Add(requestedUri == null ? new SecurityBuffer(0, BufferType.Parameters) : new SecurityBuffer(WebHeaderCollection.HeaderEncoding.GetBytes(requestedUri), BufferType.Parameters)); list.Add(new SecurityBuffer(0, BufferType.Parameters)); list.Add(realm == null ? new SecurityBuffer(0, BufferType.Parameters) : new SecurityBuffer(Encoding.Unicode.GetBytes(realm), BufferType.Parameters)); if (m_ChannelBinding != null) { list.Add(new SecurityBuffer(m_ChannelBinding)); } inSecurityBuffers = list.ToArray(); statusCode = (SecurityStatus) SSPIWrapper.AcceptSecurityContext( GlobalSSPI.SSPIAuth, m_CredentialsHandle, ref m_SecurityContext, m_RequestedContextFlags, Endianness.Network, inSecurityBuffers, outSecurityBuffer, ref m_ContextFlags ); GlobalLog.Print("NTAuthentication#" + ValidationHelper.HashString(this) + "::GetOutgoingDigestBlob() SSPIWrapper.AcceptSecurityContext() returns statusCode:0x" + ((int)statusCode).ToString("x8", NumberFormatInfo.InvariantInfo) + " (" + statusCode.ToString() + ")"); if (statusCode == SecurityStatus.CompleteNeeded) { inSecurityBuffers[4] = outSecurityBuffer; statusCode = (SecurityStatus) SSPIWrapper.CompleteAuthToken( GlobalSSPI.SSPIAuth, ref m_SecurityContext, inSecurityBuffers ); GlobalLog.Print("NTAuthentication#" + ValidationHelper.HashString(this) + "::GetOutgoingDigestBlob() SSPIWrapper.CompleteAuthToken() returns statusCode:0x" + ((int)statusCode).ToString("x8", NumberFormatInfo.InvariantInfo) + " (" + statusCode.ToString() + ")"); outSecurityBuffer.token = null; } } } finally { // // Assuming the ISC or ASC has referenced the credential on the first successful call, // we want to decrement the effective ref count by "disposing" it. // The real dispose will happen when the security context is closed. // Note if the first call was not successfull the handle is physically destroyed here // if (firstTime && m_CredentialsHandle != null) m_CredentialsHandle.Close(); } if (((int) statusCode & unchecked((int) 0x80000000)) != 0) { CloseContext(); if (throwOnError) { Win32Exception exception = new Win32Exception((int) statusCode); GlobalLog.Leave("NTAuthentication#" + ValidationHelper.HashString(this) + "::GetOutgoingDigestBlob", "Win32Exception:" + exception); throw exception; } GlobalLog.Leave("NTAuthentication#" + ValidationHelper.HashString(this) + "::GetOutgoingDigestBlob", "null statusCode:0x" + ((int) statusCode).ToString("x8", NumberFormatInfo.InvariantInfo) + " (" + statusCode.ToString() + ")"); return null; } else if (firstTime && m_CredentialsHandle != null) { // cache until it is pushed out by newly incoming handles SSPIHandleCache.CacheCredential(m_CredentialsHandle); } // 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 if (statusCode == SecurityStatus.OK) { // we're done, cleanup m_IsCompleted = true; } else { // we need to continue GlobalLog.Print("NTAuthentication#" + ValidationHelper.HashString(this) + "::GetOutgoingDigestBlob() need continue statusCode:[0x" + ((int) statusCode).ToString("x8", NumberFormatInfo.InvariantInfo) + "] (" + statusCode.ToString() + ") m_SecurityContext#" + ValidationHelper.HashString(m_SecurityContext) + "::Handle:" + ValidationHelper.ToString(m_SecurityContext) + "]"); } GlobalLog.Print("out token = " + outSecurityBuffer.ToString()); GlobalLog.Dump(outSecurityBuffer.token); GlobalLog.Print("NTAuthentication#" + ValidationHelper.HashString(this) + "::GetOutgoingDigestBlob() IsCompleted:" + IsCompleted.ToString()); byte[] decodedOutgoingBlob = outSecurityBuffer.token; string outgoingBlob = null; if (decodedOutgoingBlob!=null && decodedOutgoingBlob.Length>0) { outgoingBlob = WebHeaderCollection.HeaderEncoding.GetString(decodedOutgoingBlob, 0, outSecurityBuffer.size); } GlobalLog.Leave("NTAuthentication#" + ValidationHelper.HashString(this) + "::GetOutgoingDigestBlob", outgoingBlob); return outgoingBlob; }