// // 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); }