// // 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); }
// // NTAuth::GetOutgoingBlob() // Created: 12-01-1999: L.M. // Description: // Accepts an incoming binary security blob and returns // an outgoing binary security blob // private byte[] GetOutgoingBlob(byte[] incomingBlob, out bool handshakeComplete) { GlobalLog.Enter("NTAuthentication::GetOutgoingBlob", ((incomingBlob == null) ? "0" : incomingBlob.Length.ToString()) + " bytes"); // default to true in case of failure handshakeComplete = true; if (m_SecurityContext.Handle != -1 && incomingBlob == null) { // we tried auth previously, now we got a null blob, we're done. this happens // with Kerberos & valid credentials on the domain but no ACLs on the resource // the handle for m_SecurityContext will be collected at GC time. GlobalLog.Print("NTAuthentication#" + ValidationHelper.HashString(this) + "::GetOutgoingBlob() null blob AND m_SecurityContext#" + ValidationHelper.HashString(m_SecurityContext) + "::Handle:[0x" + m_SecurityContext.Handle.ToString("x8") + "]"); m_SecurityContext.Close(); IsCompleted = true; return(null); } int requestedFlags = (int)ContextFlags.Delegate | (int)ContextFlags.MutualAuth | (int)ContextFlags.ReplayDetect | (int)ContextFlags.SequenceDetect | (int)ContextFlags.Confidentiality | (int)ContextFlags.Connection; SecurityBufferClass inSecurityBuffer = null; if (incomingBlob != null) { GlobalLog.Print("in blob = "); GlobalLog.Dump(incomingBlob); inSecurityBuffer = new SecurityBufferClass(incomingBlob, BufferType.Token); } SecurityBufferClass outSecurityBuffer = new SecurityBufferClass(m_TokenSize, BufferType.Token); int status; #if SERVER_SIDE_SSPI if (m_SecureSessionType == SecureSessionType.ClientSession) { #endif // // client session // requestedFlags |= (int)ContextFlags.ClientIntegrity; status = SSPIWrapper.InitializeSecurityContext( GlobalSSPI.SSPIAuth, m_CredentialsHandle.Handle, m_SecurityContext.Handle, m_RemotePeerId, requestedFlags, m_Endianness, inSecurityBuffer, ref m_SecurityContext.Handle, outSecurityBuffer, ref m_ContextFlags, ref m_SecurityContext.TimeStamp ); GlobalLog.Print("SSPIWrapper.InitializeSecurityContext() returns 0x" + string.Format("{0:x}", status)); #if SERVER_SIDE_SSPI } else { // // server session // requestedFlags |= (int)ContextFlags.ServerIntegrity; status = SSPIWrapper.AcceptSecurityContext( GlobalSSPI.SSPIAuth, m_CredentialsHandle.Handle, m_SecurityContext.Handle, requestedFlags, m_Endianness, inSecurityBuffer, ref m_SecurityContext.Handle, outSecurityBuffer, out m_ContextFlags, out m_SecurityContext.TimeStamp ); GlobalLog.Print("SSPIWrapper.AcceptSecurityContext() returns 0x" + string.Format("{0:x}", status)); } #endif // SERVER_SIDE_SSPI 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. // if (status != (int)SecurityStatus.OK && m_SecurityContext.Handle != -1) { // we need to continue GlobalLog.Print("NTAuthentication#" + ValidationHelper.HashString(this) + "::GetOutgoingBlob() need continue status:[0x" + status.ToString("x8") + "] m_SecurityContext#" + ValidationHelper.HashString(m_SecurityContext) + "::Handle:[0x" + m_SecurityContext.Handle.ToString("x8") + "]"); handshakeComplete = false; } else { // we're done, cleanup GlobalLog.Assert(status == (int)SecurityStatus.OK, "NTAuthentication#" + ValidationHelper.HashString(this) + "::GetOutgoingBlob() status:[0x" + status.ToString("x8") + "] m_SecurityContext#" + ValidationHelper.HashString(m_SecurityContext) + "::Handle:[0x" + m_SecurityContext.Handle.ToString("x8") + "]", "[STATUS != OK]"); m_SecurityContext.Close(); IsCompleted = true; } #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.Print("out token = " + m_TokenSize.ToString()); GlobalLog.Dump(outSecurityBuffer.token); GlobalLog.Leave("NTAuthentication::GetOutgoingBlob", "handshakeComplete:" + handshakeComplete.ToString()); return(outSecurityBuffer.token); }