/// <devdoc> /// <para> Reads data from the stream. </para> /// </devdoc> public override int Read(byte[] buffer, int offset, int size) { int result = m_NetworkStream.Read(buffer, offset, size); GlobalLog.Dump(buffer, offset, result); return(result); }
internal static byte[] EncodingRightGetBytes(string rawString) { GlobalLog.Enter("BasicClient::EncodingRightGetBytes", "[" + rawString.Length.ToString() + ":" + rawString + "]"); // // in order to know if there will not be any '?' translations (which means // we should use the Default Encoding) we need to attempt encoding and then decoding. // GlobalLog.Print("BasicClient::EncodingRightGetBytes(): Default Encoding is:" + Encoding.Default.EncodingName); byte[] bytes = Encoding.Default.GetBytes(rawString); string rawCopy = Encoding.Default.GetString(bytes); bool canMapToCurrentCodePage = string.Compare(rawString, rawCopy, false, CultureInfo.InvariantCulture) == 0; GlobalLog.Print("BasicClient::EncodingRightGetBytes(): canMapToCurrentCodePage:" + canMapToCurrentCodePage.ToString()); if (!canMapToCurrentCodePage) { throw ExceptionHelper.MethodNotSupportedException; /* * GlobalLog.Print("BasicClient::EncodingRightGetBytes(): using:" + Encoding.UTF8.EncodingName); * bytes = Encoding.UTF8.GetBytes(rawString); * * string blob = "=?utf-8?B?" + Convert.ToBase64String(bytes) + "?="; * bytes = Encoding.ASCII.GetBytes(blob); */ } GlobalLog.Dump(bytes); GlobalLog.Leave("BasicClient::EncodingRightGetBytes", bytes.Length.ToString()); return(bytes); }
/// <devdoc> /// <para>Writes data to the stream.</para> /// </devdoc> public override void Write(byte[] buffer, int offset, int size) { GlobalLog.Dump(buffer, offset, size); try { if (ServicePointManager.UseSafeSynchronousClose) { int newValue = Interlocked.Increment(ref m_SynchronousIOClosingState); if ((newValue & (ClosedFlag | ClosingFlag)) != 0) { // We don't want new IO after we started cleanup. throw new ObjectDisposedException(GetType().FullName); } } m_NetworkStream.Write(buffer, offset, size); } finally { if (ServicePointManager.UseSafeSynchronousClose && ClosingFlag == Interlocked.Decrement(ref m_SynchronousIOClosingState)) { // Close was attempted while in Write() call and no other IO is pending. // Do deferred Close() now. try { // Attempt to close m_NetworkStream - it may not happen on this thread if another call to Read/Write // increased m_SynchronousIOClosingState from another thread in the meantime TryCloseNetworkStream(false, 0); } catch { } } } }
internal IAsyncResult BeginMultipleWrite(BufferOffsetSize[] buffers, AsyncCallback callback, object state) { #if TRAVE for (int i = 0; i < buffers.Length; ++i) { GlobalLog.Dump(buffers[i].Buffer, buffers[i].Offset, buffers[i].Size); } #endif return(m_NetworkStream.BeginMultipleWrite(buffers, callback, state)); }
/// <devdoc> /// <para>Writes multiple buffers at once</para> /// </devdoc> internal void MultipleWrite(BufferOffsetSize[] buffers) { #if TRAVE for (int i = 0; i < buffers.Length; ++i) { GlobalLog.Dump(buffers[i].Buffer, buffers[i].Offset, buffers[i].Size); } #endif m_NetworkStream.MultipleWrite(buffers); }
private void AsyncReceiveComplete(IAsyncResult result) { GlobalLog.Enter("TlsStream#" + ValidationHelper.HashString(this) + "::AsyncReceiveComplete"); try { int bytesRead = base.EndRead(result); GlobalLog.Print("TlsStream#" + ValidationHelper.HashString(this) + "::AsyncReceiveComplete: received " + bytesRead + " bytes"); GlobalLog.Dump(m_AsyncResponseBuffer, bytesRead); NextRecord(m_AsyncResponseBuffer, bytesRead); } catch (Exception exception) { InnerException = new IOException(SR.GetString(SR.net_io_readfailure), exception); } ((NestedSingleAsyncResult)result.AsyncState).InvokeCallback(false); GlobalLog.Leave("TlsStream#" + ValidationHelper.HashString(this) + "::AsyncReceiveComplete"); }
internal static byte[] EncodingRightGetBytes(string rawString) { GlobalLog.Enter("BasicClient::EncodingRightGetBytes", "[" + rawString.Length.ToString() + ":" + rawString + "]"); // // in order to know if there will not be any '?' translations (which means // we should use the Default Encoding) we need to attempt encoding and then decoding. // <STRIP>this is a limitation only on win9x, if we ever drop support for this platform there might be // a more efficient way of doing this.</STRIP> // GlobalLog.Print("BasicClient::EncodingRightGetBytes(): Default Encoding is:" + Encoding.Default.EncodingName); byte[] bytes = Encoding.Default.GetBytes(rawString); string rawCopy = Encoding.Default.GetString(bytes); bool canMapToCurrentCodePage = string.Compare(rawString, rawCopy, StringComparison.Ordinal) == 0; GlobalLog.Print("BasicClient::EncodingRightGetBytes(): canMapToCurrentCodePage:" + canMapToCurrentCodePage.ToString()); //<STRIP> // if mapping to the current code page leaves characters out of the // [0x00, 0xFF] range, then we need to use the new encoding that IIS6.0 // will support. do it when they decide it's good enough. // </STRIP> if (!canMapToCurrentCodePage) { //<STRIP> // for now throw. when IIS 6.0 adds support test it. //</STRIP> GlobalLog.LeaveException("BasicClient::EncodingRightGetBytes", ExceptionHelper.MethodNotSupportedException); throw ExceptionHelper.MethodNotSupportedException; /* * GlobalLog.Print("BasicClient::EncodingRightGetBytes(): using:" + Encoding.UTF8.EncodingName); * bytes = Encoding.UTF8.GetBytes(rawString); * * string blob = "=?utf-8?B?" + Convert.ToBase64String(bytes) + "?="; * bytes = Encoding.ASCII.GetBytes(blob); */ } GlobalLog.Dump(bytes); GlobalLog.Leave("BasicClient::EncodingRightGetBytes", bytes.Length.ToString()); return(bytes); }
// // Handshake - the Handshake is perhaps the most important part of the SSL process, // this is a Handshake protocol between server & client, where we send a // a HELLO message / server responds, we respond back, and after a few round trips, // we have an SSL connection with the server. But this process may be repeated, // if a higher level of security is required for by the server, therefore, // this function may be called several times in the life of the connection. // // returns an Exception on error, containing the error code of the failure // private Exception Handshake(ProtocolToken message) { // // With some SSPI APIs, the SSPI wrapper may throw // uncaught Win32Exceptions, so we need to add // this try - catch here. // try { int round = 0; byte[] incoming = null; // will be null == message on connection creation/otherwise should be // renegotation if (message == null) { GlobalLog.Assert( (SecureChannel == null), "had assembed a null SecureChannel at this point", "SecureChannel != null"); m_SecureChannel = new SecureChannel(m_DestinationHost, m_ClientCertificates); } else { incoming = message.Payload; } do { GlobalLog.Print("Handshake::Round #" + round); // // this code runs in the constructor, hence there's no // way SecureChannel can become null // message = SecureChannel.NextMessage(incoming); #if TRAVE GlobalLog.Print("Handshake::generating TLS message(Status:" + SecureChannel.MapSecurityStatus((uint)message.Status) + " Done:" + message.Done.ToString() + ")"); #endif if (message.Failed) { break; } if (message.Payload != null) { GlobalLog.Print("Handshake::Outgoing message size: " + message.Payload.Length); GlobalLog.Dump(message.Payload); base.Write(message.Payload, 0, message.Payload.Length); } else { GlobalLog.Print("Handshake::No message necessary."); } if (message.Done) { break; } // // ReadFullRecord attempts to parse read data // from the byte stream, this can be dangerous as its not // always sure about protocols, at this point // incoming = ReadFullRecord(null, 0); if (incoming == null) { // // Handshake failed // GlobalLog.Print("Handshake::ReadFullRecord is null, Handshake failed"); GlobalLog.Assert( (!message.Done), "attempted bad return / must always fail", "message.Done"); return(message.GetException()); } GlobalLog.Print("Handshake::Incoming message size: " + incoming.Length); round++; } while (!message.Done); if (message.Done) { SecureChannel.ProcessHandshakeSuccess(); GlobalLog.Print("Handshake::Handshake completed successfully."); } else { // SEC_I_CONTINUE_NEEDED #if TRAVE GlobalLog.Print("Handshake::FAILED Handshake, last error: " + SecureChannel.MapSecurityStatus((uint)message.Status)); #endif } return(message.GetException()); } catch (Exception exception) { return(exception); } }
// // ReadFullRecord - reads a block of bytes, // attemps to ascertain, how much to read, by // assuming block encoding of the byte stream. // // This can be dangerous as these things // tend to change from protocol to protocol // // WARNING: Can throw! // public byte[] ReadFullRecord(byte[] buffer, int length) { // after shutdown/Close throw an exception if (m_ShutDown > 0) { throw new ObjectDisposedException(this.GetType().FullName); } SecureChannel chkSecureChannel = SecureChannel; if (chkSecureChannel == null) { return(null); } int headerSize = chkSecureChannel.HeaderSize; byte[] header = new byte[headerSize]; int read = length; if (buffer != null) { GlobalLog.Assert(length <= headerSize, "length > headerSize", ""); Buffer.BlockCopy(buffer, 0, header, 0, Math.Min(length, headerSize)); } if (length < headerSize) { GlobalLog.Print("RecordLayer::ReadFullRecord Reading " + headerSize + " byte header from the stream"); read += ForceRead(header, length, headerSize - length); } GlobalLog.Dump(header); if (read != headerSize) { GlobalLog.Print("RecordLayer::ReadFullRecord returning null"); return(null); } // if we can't verify, just return what we can find if (!verifyRecordFormat(header)) { return(header); } // WARNING this line, I find worrisome, because it // can differ on new protocols int payloadSize = (0x100 * header[3]) + header[4]; byte[] record = new byte[payloadSize + headerSize]; Buffer.BlockCopy(header, 0, record, 0, headerSize); int received = ForceRead(record, headerSize, payloadSize); GlobalLog.Dump(record); if (received < payloadSize) { GlobalLog.Print("RecordLayer::ReadFullRecord returning null"); return(null); } return(record); }
private List <GCHandle> SerializeHeaders(ref UnsafeNclNativeMethods.HttpApi.HTTP_RESPONSE_HEADERS headers, bool isWebSocketHandshake) { UnsafeNclNativeMethods.HttpApi.HTTP_UNKNOWN_HEADER[] unknownHeaders = null; List <GCHandle> pinnedHeaders; GCHandle gcHandle; /* * // here we would check for BoundaryType.Raw, in this case we wouldn't need to do anything * if (m_BoundaryType==BoundaryType.Raw) { * return null; * } */ GlobalLog.Print("HttpListenerResponse#" + ValidationHelper.HashString(this) + "::SerializeHeaders(HTTP_RESPONSE_HEADERS)"); if (Headers.Count == 0) { return(null); } string headerName; string headerValue; int lookup; byte[] bytes = null; pinnedHeaders = new List <GCHandle>(); //--------------------------------------------------- // DTS Issue: 609383: // The Set-Cookie headers are being merged into one. // There are two issues here. // 1. When Set-Cookie headers are set through SetCookie method on the ListenerResponse, // there is code in the SetCookie method and the methods it calls to flatten the Set-Cookie // values. This blindly concatenates the cookies with a comma delimiter. There could be // a cookie value that contains comma, but we don't escape it with %XX value // // As an alternative users can add the Set-Cookie header through the AddHeader method // like ListenerResponse.Headers.Add("name", "value") // That way they can add multiple headers - AND They can format the value like they want it. // // 2. Now that the header collection contains multiple Set-Cookie name, value pairs // you would think the problem would go away. However here is an interesting thing. // For NameValueCollection, when you add // "Set-Cookie", "value1" // "Set-Cookie", "value2" // The NameValueCollection.Count == 1. Because there is only one key // NameValueCollection.Get("Set-Cookie") would conviniently take these two valuess // concatenate them with a comma like // value1,value2. // In order to get individual values, you need to use // string[] values = NameValueCollection.GetValues("Set-Cookie"); // // ------------------------------------------------------------- // So here is the proposed fix here. // We must first to loop through all the NameValueCollection keys // and if the name is a unknown header, we must compute the number of // values it has. Then, we should allocate that many unknown header array // elements. // // Note that a part of the fix here is to treat Set-Cookie as an unknown header // // //----------------------------------------------------------- int numUnknownHeaders = 0; for (int index = 0; index < Headers.Count; index++) { headerName = Headers.GetKey(index) as string; //See if this is an unknown header lookup = UnsafeNclNativeMethods.HttpApi.HTTP_RESPONSE_HEADER_ID.IndexOfKnownHeader(headerName); //Treat Set-Cookie as well as Connection header in Websocket mode as unknown if (lookup == (int)HttpResponseHeader.SetCookie || isWebSocketHandshake && lookup == (int)HttpResponseHeader.Connection) { lookup = -1; } if (lookup == -1) { string[] headerValues = Headers.GetValues(index); numUnknownHeaders += headerValues.Length; } } try { fixed(UnsafeNclNativeMethods.HttpApi.HTTP_KNOWN_HEADER *pKnownHeaders = &headers.KnownHeaders) { for (int index = 0; index < Headers.Count; index++) { headerName = Headers.GetKey(index) as string; headerValue = Headers.Get(index) as string; lookup = UnsafeNclNativeMethods.HttpApi.HTTP_RESPONSE_HEADER_ID.IndexOfKnownHeader(headerName); if (lookup == (int)HttpResponseHeader.SetCookie || isWebSocketHandshake && lookup == (int)HttpResponseHeader.Connection) { lookup = -1; } GlobalLog.Print("HttpListenerResponse#" + ValidationHelper.HashString(this) + "::SerializeHeaders(" + index + "/" + Headers.Count + ") headerName:" + ValidationHelper.ToString(headerName) + " lookup:" + lookup + " headerValue:" + ValidationHelper.ToString(headerValue)); if (lookup == -1) { if (unknownHeaders == null) { //---------------------------------------- //*** This following comment is no longer true *** // we waste some memory here (up to 32*41=1312 bytes) but we gain speed //unknownHeaders = new UnsafeNclNativeMethods.HttpApi.HTTP_UNKNOWN_HEADER[Headers.Count-index]; //-------------------------------------------- unknownHeaders = new UnsafeNclNativeMethods.HttpApi.HTTP_UNKNOWN_HEADER[numUnknownHeaders]; gcHandle = GCHandle.Alloc(unknownHeaders, GCHandleType.Pinned); pinnedHeaders.Add(gcHandle); headers.pUnknownHeaders = (UnsafeNclNativeMethods.HttpApi.HTTP_UNKNOWN_HEADER *)gcHandle.AddrOfPinnedObject(); } //---------------------------------------- //FOR UNKNOWN HEADERS //ALLOW MULTIPLE HEADERS to be added //--------------------------------------- string[] headerValues = Headers.GetValues(index); for (int headerValueIndex = 0; headerValueIndex < headerValues.Length; headerValueIndex++) { //Add Name bytes = new byte[WebHeaderCollection.HeaderEncoding.GetByteCount(headerName)]; unknownHeaders[headers.UnknownHeaderCount].NameLength = (ushort)bytes.Length; WebHeaderCollection.HeaderEncoding.GetBytes(headerName, 0, bytes.Length, bytes, 0); gcHandle = GCHandle.Alloc(bytes, GCHandleType.Pinned); pinnedHeaders.Add(gcHandle); unknownHeaders[headers.UnknownHeaderCount].pName = (sbyte *)gcHandle.AddrOfPinnedObject(); //Add Value headerValue = headerValues[headerValueIndex]; bytes = new byte[WebHeaderCollection.HeaderEncoding.GetByteCount(headerValue)]; unknownHeaders[headers.UnknownHeaderCount].RawValueLength = (ushort)bytes.Length; WebHeaderCollection.HeaderEncoding.GetBytes(headerValue, 0, bytes.Length, bytes, 0); gcHandle = GCHandle.Alloc(bytes, GCHandleType.Pinned); pinnedHeaders.Add(gcHandle); unknownHeaders[headers.UnknownHeaderCount].pRawValue = (sbyte *)gcHandle.AddrOfPinnedObject(); headers.UnknownHeaderCount++; GlobalLog.Print("HttpListenerResponse#" + ValidationHelper.HashString(this) + "::SerializeHeaders(Unknown) UnknownHeaderCount:" + headers.UnknownHeaderCount); } } else { GlobalLog.Print("HttpListenerResponse#" + ValidationHelper.HashString(this) + "::SerializeHeaders(Known) HttpResponseHeader[" + lookup + "]:" + ((HttpResponseHeader)lookup) + " headerValue:" + ValidationHelper.ToString(headerValue)); if (headerValue != null) { bytes = new byte[WebHeaderCollection.HeaderEncoding.GetByteCount(headerValue)]; pKnownHeaders[lookup].RawValueLength = (ushort)bytes.Length; WebHeaderCollection.HeaderEncoding.GetBytes(headerValue, 0, bytes.Length, bytes, 0); gcHandle = GCHandle.Alloc(bytes, GCHandleType.Pinned); pinnedHeaders.Add(gcHandle); pKnownHeaders[lookup].pRawValue = (sbyte *)gcHandle.AddrOfPinnedObject(); GlobalLog.Print("HttpListenerResponse#" + ValidationHelper.HashString(this) + "::SerializeHeaders(Known) pRawValue:" + ValidationHelper.ToString((IntPtr)(pKnownHeaders[lookup].pRawValue)) + " RawValueLength:" + pKnownHeaders[lookup].RawValueLength + " lookup:" + lookup); GlobalLog.Dump((IntPtr)pKnownHeaders[lookup].pRawValue, 0, pKnownHeaders[lookup].RawValueLength); } } } } } catch { FreePinnedHeaders(pinnedHeaders); throw; } return(pinnedHeaders); }
internal virtual IAsyncResult UnsafeBeginWrite(byte[] buffer, int offset, int size, AsyncCallback callback, Object state) { GlobalLog.Dump(buffer, offset, size); return(m_NetworkStream.UnsafeBeginWrite(buffer, offset, size, callback, state)); }
public override IAsyncResult BeginWrite(byte[] buffer, int offset, int size, AsyncCallback callback, Object state) { GlobalLog.Dump(buffer, offset, size); return(m_NetworkStream.BeginWrite(buffer, offset, size, callback, state)); }
/// <devdoc> /// <para>Writes data to the stream.</para> /// </devdoc> public override void Write(byte[] buffer, int offset, int size) { GlobalLog.Dump(buffer, offset, size); m_NetworkStream.Write(buffer, offset, size); }
// // 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); }