public static SecurityStatusPal EncryptMessage(SafeDeleteContext securityContext, byte[] input, int offset, int size, int headerSize, int trailerSize, ref byte[] output, out int resultSize) { // Ensure that there is sufficient space for the message output. try { int bufferSizeNeeded = checked (size + headerSize + trailerSize); if (output == null || output.Length < bufferSizeNeeded) { output = new byte[bufferSizeNeeded]; } } catch (Exception e) { if (!ExceptionCheck.IsFatal(e)) { if (GlobalLog.IsEnabled) { GlobalLog.Assert("SslStreamPal.Windows: SecureChannel#" + LoggingHash.HashString(securityContext) + "::Encrypt", "Arguments out of range."); } Debug.Fail("SslStreamPal.Windows: SecureChannel#" + LoggingHash.HashString(securityContext) + "::Encrypt", "Arguments out of range."); } throw; } byte[] writeBuffer = output; // Copy the input into the output buffer to prepare for SCHANNEL's expectations Buffer.BlockCopy(input, offset, writeBuffer, headerSize, size); // Encryption using SCHANNEL requires 4 buffers: header, payload, trailer, empty. SecurityBuffer[] securityBuffer = new SecurityBuffer[4]; securityBuffer[0] = new SecurityBuffer(writeBuffer, 0, headerSize, SecurityBufferType.Header); securityBuffer[1] = new SecurityBuffer(writeBuffer, headerSize, size, SecurityBufferType.Data); securityBuffer[2] = new SecurityBuffer(writeBuffer, headerSize + size, trailerSize, SecurityBufferType.Trailer); securityBuffer[3] = new SecurityBuffer(null, SecurityBufferType.Empty); int errorCode = SSPIWrapper.EncryptMessage(GlobalSSPI.SSPISecureChannel, securityContext, securityBuffer, 0); if (errorCode != 0) { if (GlobalLog.IsEnabled) { GlobalLog.Print("SslStreamPal.Windows: SecureChannel#" + LoggingHash.HashString(securityContext) + "::Encrypt ERROR" + errorCode.ToString("x")); } resultSize = 0; } else { // The full buffer may not be used. resultSize = securityBuffer[0].size + securityBuffer[1].size + securityBuffer[2].size; } return(SecurityStatusAdapterPal.GetSecurityStatusPalFromNativeInt(errorCode)); }
private static unsafe void IOCompleted(ListenerClientCertAsyncResult asyncResult, uint errorCode, uint numBytes) { HttpListenerRequest httpListenerRequest = (HttpListenerRequest)asyncResult.AsyncObject; object result = null; try { if (errorCode == Interop.HttpApi.ERROR_MORE_DATA) { //There is a bug that has existed in http.sys since w2k3. Bytesreceived will only //return the size of the inital cert structure. To get the full size, //we need to add the certificate encoding size as well. Interop.HttpApi.HTTP_SSL_CLIENT_CERT_INFO *pClientCertInfo = asyncResult.RequestBlob; asyncResult.Reset(numBytes + pClientCertInfo->CertEncodedSize); uint bytesReceived = 0; errorCode = Interop.HttpApi.HttpReceiveClientCertificate( httpListenerRequest.HttpListenerContext.RequestQueueHandle, httpListenerRequest._connectionId, (uint)Interop.HttpApi.HTTP_FLAGS.NONE, asyncResult._memoryBlob, asyncResult._size, &bytesReceived, asyncResult._pOverlapped); if (errorCode == Interop.HttpApi.ERROR_IO_PENDING || (errorCode == Interop.HttpApi.ERROR_SUCCESS && !HttpListener.SkipIOCPCallbackOnSuccess)) { return; } } if (errorCode != Interop.HttpApi.ERROR_SUCCESS) { asyncResult.ErrorCode = (int)errorCode; result = new HttpListenerException((int)errorCode); } else { Interop.HttpApi.HTTP_SSL_CLIENT_CERT_INFO *pClientCertInfo = asyncResult._memoryBlob; if (pClientCertInfo != null) { if (NetEventSource.IsEnabled) { NetEventSource.Info(null, $"pClientCertInfo:{(IntPtr)pClientCertInfo} pClientCertInfo->CertFlags: {pClientCertInfo->CertFlags} pClientCertInfo->CertEncodedSize: {pClientCertInfo->CertEncodedSize} pClientCertInfo->pCertEncoded: {(IntPtr)pClientCertInfo->pCertEncoded} pClientCertInfo->Token: {(IntPtr)pClientCertInfo->Token} pClientCertInfo->CertDeniedByMapper: {pClientCertInfo->CertDeniedByMapper}"); } if (pClientCertInfo->pCertEncoded != null) { try { byte[] certEncoded = new byte[pClientCertInfo->CertEncodedSize]; Marshal.Copy((IntPtr)pClientCertInfo->pCertEncoded, certEncoded, 0, certEncoded.Length); result = httpListenerRequest.ClientCertificate = new X509Certificate2(certEncoded); } catch (CryptographicException exception) { if (NetEventSource.IsEnabled) { NetEventSource.Info(null, $"HttpListenerRequest: {httpListenerRequest} caught CryptographicException: {exception}"); } result = exception; } catch (SecurityException exception) { if (NetEventSource.IsEnabled) { NetEventSource.Info(null, $"HttpListenerRequest: {httpListenerRequest} caught SecurityException: {exception}"); } result = exception; } } httpListenerRequest.SetClientCertificateError((int)pClientCertInfo->CertFlags); } } // complete the async IO and invoke the callback if (NetEventSource.IsEnabled) { NetEventSource.Info(null, "Calling Complete()"); } } catch (Exception exception) when(!ExceptionCheck.IsFatal(exception)) { result = exception; } finally { if (errorCode != Interop.HttpApi.ERROR_IO_PENDING) { httpListenerRequest.ClientCertState = ListenerClientCertState.Completed; } } asyncResult.InvokeCallback(result); }
/// <summary> /// <para>Thread for the timer. Ignores all exceptions. If no activity occurs for a while, /// the thread will shut down.</para> /// </summary> private static void ThreadProc() { if (NetEventSource.IsEnabled) { NetEventSource.Enter(null); } #if DEBUG DebugThreadTracking.SetThreadSource(ThreadKinds.Timer); using (DebugThreadTracking.SetThreadKind(ThreadKinds.System | ThreadKinds.Async)) { #endif // Set this thread as a background thread. On AppDomain/Process shutdown, the thread will just be killed. Thread.CurrentThread.IsBackground = true; // Keep a permanent lock on s_Queues. This lets for example Shutdown() know when this thread isn't running. lock (s_Queues) { // If shutdown was recently called, abort here. if (Interlocked.CompareExchange(ref s_ThreadState, (int)TimerThreadState.Running, (int)TimerThreadState.Running) != (int)TimerThreadState.Running) { return; } bool running = true; while (running) { try { s_ThreadReadyEvent.Reset(); while (true) { // Copy all the new queues to the real queues. Since only this thread modifies the real queues, it doesn't have to lock it. if (s_NewQueues.Count > 0) { lock (s_NewQueues) { for (LinkedListNode <WeakReference> node = s_NewQueues.First; node != null; node = s_NewQueues.First) { s_NewQueues.Remove(node); s_Queues.AddLast(node); } } } int now = Environment.TickCount; int nextTick = 0; bool haveNextTick = false; for (LinkedListNode <WeakReference> node = s_Queues.First; node != null; /* node = node.Next must be done in the body */) { TimerQueue queue = (TimerQueue)node.Value.Target; if (queue == null) { LinkedListNode <WeakReference> next = node.Next; s_Queues.Remove(node); node = next; continue; } // Fire() will always return values that should be interpreted as later than 'now' (that is, even if 'now' is // returned, it is 0x100000000 milliseconds in the future). There's also a chance that Fire() will return a value // intended as > 0x100000000 milliseconds from 'now'. Either case will just cause an extra scan through the timers. int nextTickInstance; if (queue.Fire(out nextTickInstance) && (!haveNextTick || IsTickBetween(now, nextTick, nextTickInstance))) { nextTick = nextTickInstance; haveNextTick = true; } node = node.Next; } // Figure out how long to wait, taking into account how long the loop took. // Add 15 ms to compensate for poor TickCount resolution (want to guarantee a firing). int newNow = Environment.TickCount; int waitDuration = haveNextTick ? (int)(IsTickBetween(now, nextTick, newNow) ? Math.Min(unchecked ((uint)(nextTick - newNow)), (uint)(Int32.MaxValue - c_TickCountResolution)) + c_TickCountResolution : 0) : c_ThreadIdleTimeoutMilliseconds; if (NetEventSource.IsEnabled) { NetEventSource.Info(null, $"Waiting for {waitDuration}ms"); } int waitResult = WaitHandle.WaitAny(s_ThreadEvents, waitDuration, false); // 0 is s_ThreadShutdownEvent - die. if (waitResult == 0) { if (NetEventSource.IsEnabled) { NetEventSource.Info(null, "Awoke, cause: Shutdown"); } running = false; break; } if (NetEventSource.IsEnabled) { NetEventSource.Info(null, $"Awoke, cause {(waitResult == WaitHandle.WaitTimeout ? "Timeout" : "Prod")}"); } // If we timed out with nothing to do, shut down. if (waitResult == WaitHandle.WaitTimeout && !haveNextTick) { Interlocked.CompareExchange(ref s_ThreadState, (int)TimerThreadState.Idle, (int)TimerThreadState.Running); // There could have been one more prod between the wait and the exchange. Check, and abort if necessary. if (s_ThreadReadyEvent.WaitOne(0, false)) { if (Interlocked.CompareExchange(ref s_ThreadState, (int)TimerThreadState.Running, (int)TimerThreadState.Idle) == (int)TimerThreadState.Idle) { continue; } } running = false; break; } } } catch (Exception exception) { if (ExceptionCheck.IsFatal(exception)) { throw; } if (NetEventSource.IsEnabled) { NetEventSource.Error(null, exception); } // The only options are to continue processing and likely enter an error-loop, // shut down timers for this AppDomain, or shut down the AppDomain. Go with shutting // down the AppDomain in debug, and going into a loop in retail, but try to make the // loop somewhat slow. Note that in retail, this can only be triggered by OutOfMemory or StackOverflow, // or an exception thrown within TimerThread - the rest are caught in Fire(). #if !DEBUG Thread.Sleep(1000); #else throw; #endif } } } if (NetEventSource.IsEnabled) { NetEventSource.Info(null, "Stop"); } #if DEBUG } #endif }
/// <summary> /// <para>Fires the timer if it is still active and has expired. Returns /// true if it can be deleted, or false if it is still timing.</para> /// </summary> internal bool Fire() { if (_timerState == TimerState.Sentinel) { if (NetEventSource.IsEnabled) { NetEventSource.Info(this, "TimerQueue tried to Fire a Sentinel."); } } if (_timerState != TimerState.Ready) { return(true); } // Must get the current tick count within this method so it is guaranteed not to be before // StartTime, which is set in the constructor. int nowMilliseconds = Environment.TickCount; if (IsTickBetween(StartTime, Expiration, nowMilliseconds)) { if (NetEventSource.IsEnabled) { NetEventSource.Info(this, $"TimerThreadTimer#{StartTime}::Fire() Not firing ({StartTime} <= {nowMilliseconds} < {Expiration})"); } return(false); } bool needCallback = false; lock (_queueLock) { if (_timerState == TimerState.Ready) { if (NetEventSource.IsEnabled) { NetEventSource.Info(this, $"TimerThreadTimer#{StartTime}::Fire() Firing ({StartTime} <= {nowMilliseconds} >= " + Expiration + ")"); } _timerState = TimerState.Fired; // Remove it from the list. Next.Prev = Prev; Prev.Next = Next; Next = null; Prev = null; needCallback = _callback != null; } } if (needCallback) { try { Callback callback = _callback; object context = _context; _callback = null; _context = null; callback(this, nowMilliseconds, context); } catch (Exception exception) { if (ExceptionCheck.IsFatal(exception)) { throw; } if (NetEventSource.IsEnabled) { NetEventSource.Error(this, $"exception in callback: {exception}"); } // This thread is not allowed to go into user code, so we should never get an exception here. // So, in debug, throw it up, killing the AppDomain. In release, we'll just ignore it. #if DEBUG throw; #endif } } return(true); }
/// <summary> /// <para>Fires the timer if it is still active and has expired. Returns /// true if it can be deleted, or false if it is still timing.</para> /// </summary> internal bool Fire() { if (_timerState == TimerState.Sentinel) { if (GlobalLog.IsEnabled) { GlobalLog.AssertFormat("TimerThread#{0}::Fire()|TimerQueue tried to Fire a Sentinel.", Thread.CurrentThread.ManagedThreadId.ToString()); } Debug.Fail(string.Format("TimerThread#{0}::Fire()|TimerQueue tried to Fire a Sentinel.", Thread.CurrentThread.ManagedThreadId.ToString())); } if (_timerState != TimerState.Ready) { return(true); } // Must get the current tick count within this method so it is guaranteed not to be before // StartTime, which is set in the constructor. int nowMilliseconds = Environment.TickCount; if (IsTickBetween(StartTime, Expiration, nowMilliseconds)) { if (GlobalLog.IsEnabled) { GlobalLog.Print("TimerThreadTimer#" + StartTime + "::Fire() Not firing (" + StartTime + " <= " + nowMilliseconds + " < " + Expiration + ")"); } return(false); } bool needCallback = false; lock (_queueLock) { if (_timerState == TimerState.Ready) { if (GlobalLog.IsEnabled) { GlobalLog.Print("TimerThreadTimer#" + StartTime + "::Fire() Firing (" + StartTime + " <= " + nowMilliseconds + " >= " + Expiration + ")"); } _timerState = TimerState.Fired; // Remove it from the list. Next.Prev = Prev; Prev.Next = Next; Next = null; Prev = null; needCallback = _callback != null; } } if (needCallback) { try { Callback callback = _callback; object context = _context; _callback = null; _context = null; callback(this, nowMilliseconds, context); } catch (Exception exception) { if (ExceptionCheck.IsFatal(exception)) { throw; } if (NetEventSource.Log.IsEnabled()) { NetEventSource.PrintError(NetEventSource.ComponentType.Web, "TimerThreadTimer#" + StartTime.ToString(NumberFormatInfo.InvariantInfo) + "::Fire() - exception in callback: " + exception); } if (GlobalLog.IsEnabled) { GlobalLog.Print("TimerThreadTimer#" + StartTime + "::Fire() exception in callback: " + exception); } // This thread is not allowed to go into user code, so we should never get an exception here. // So, in debug, throw it up, killing the AppDomain. In release, we'll just ignore it. #if DEBUG throw; #endif } } return(true); }
private static void IOCompleted(ListenerAsyncResult asyncResult, uint errorCode, uint numBytes) { object result = null; try { if (NetEventSource.IsEnabled) { NetEventSource.Info(null, $"errorCode:[{errorCode}] numBytes:[{numBytes}]"); } if (errorCode != Interop.HttpApi.ERROR_SUCCESS && errorCode != Interop.HttpApi.ERROR_MORE_DATA) { asyncResult.ErrorCode = (int)errorCode; result = new HttpListenerException((int)errorCode); } else { HttpListener httpWebListener = asyncResult.AsyncObject as HttpListener; if (errorCode == Interop.HttpApi.ERROR_SUCCESS) { // at this point we have received an unmanaged HTTP_REQUEST and memoryBlob // points to it we need to hook up our authentication handling code here. bool stoleBlob = false; try { if (httpWebListener.ValidateRequest(asyncResult._requestContext)) { result = httpWebListener.HandleAuthentication(asyncResult._requestContext, out stoleBlob); } } finally { if (stoleBlob) { // The request has been handed to the user, which means this code can't reuse the blob. Reset it here. asyncResult._requestContext = result == null ? new AsyncRequestContext(httpWebListener.RequestQueueBoundHandle, asyncResult) : null; } else { asyncResult._requestContext.Reset(httpWebListener.RequestQueueBoundHandle, 0, 0); } } } else { asyncResult._requestContext.Reset(httpWebListener.RequestQueueBoundHandle, asyncResult._requestContext.RequestBlob->RequestId, numBytes); } // We need to issue a new request, either because auth failed, or because our buffer was too small the first time. if (result == null) { uint statusCode = asyncResult.QueueBeginGetContext(); if (statusCode != Interop.HttpApi.ERROR_SUCCESS && statusCode != Interop.HttpApi.ERROR_IO_PENDING) { // someother bad error, possible return values are: // ERROR_INVALID_HANDLE, ERROR_INSUFFICIENT_BUFFER, ERROR_OPERATION_ABORTED result = new HttpListenerException((int)statusCode); } } if (result == null) { return; } } // complete the async IO and invoke the callback if (NetEventSource.IsEnabled) { NetEventSource.Info(null, "Calling Complete()"); } } catch (Exception exception) when(!ExceptionCheck.IsFatal(exception)) { if (NetEventSource.IsEnabled) { NetEventSource.Info(null, $"Caught exception: {exception}"); } result = exception; } asyncResult.InvokeCallback(result); }
internal int Encrypt(byte[] buffer, int offset, int count, ref byte[] output, uint sequenceNumber) { SecSizes sizes = Sizes; try { int maxCount = checked (Int32.MaxValue - 4 - sizes.BlockSize - sizes.SecurityTrailer); if (count > maxCount || count < 0) { throw new ArgumentOutOfRangeException("count", SR.Format(SR.net_io_out_range, maxCount)); } } catch (Exception e) { if (!ExceptionCheck.IsFatal(e) && GlobalLog.IsEnabled) { GlobalLog.Assert("NTAuthentication#" + LoggingHash.HashString(this) + "::Encrypt", "Arguments out of range."); } throw; } int resultSize = count + sizes.SecurityTrailer + sizes.BlockSize; if (output == null || output.Length < resultSize + 4) { output = new byte[resultSize + 4]; } // Make a copy of user data for in-place encryption. Buffer.BlockCopy(buffer, offset, output, 4 + sizes.SecurityTrailer, count); // Prepare buffers TOKEN(signature), DATA and Padding. var securityBuffer = new SecurityBuffer[3]; securityBuffer[0] = new SecurityBuffer(output, 4, sizes.SecurityTrailer, SecurityBufferType.Token); securityBuffer[1] = new SecurityBuffer(output, 4 + sizes.SecurityTrailer, count, SecurityBufferType.Data); securityBuffer[2] = new SecurityBuffer(output, 4 + sizes.SecurityTrailer + count, sizes.BlockSize, SecurityBufferType.Padding); int errorCode; if (IsConfidentialityFlag) { errorCode = SSPIWrapper.EncryptMessage(GlobalSSPI.SSPIAuth, _securityContext, securityBuffer, sequenceNumber); } else { if (IsNTLM) { securityBuffer[1].type |= SecurityBufferType.ReadOnlyFlag; } errorCode = SSPIWrapper.MakeSignature(GlobalSSPI.SSPIAuth, _securityContext, securityBuffer, 0); } if (errorCode != 0) { GlobalLog.Print("NTAuthentication#" + LoggingHash.HashString(this) + "::Encrypt() throw Error = " + errorCode.ToString("x", NumberFormatInfo.InvariantInfo)); throw new Win32Exception(errorCode); } // Compacting the result. resultSize = securityBuffer[0].size; bool forceCopy = false; if (resultSize != sizes.SecurityTrailer) { forceCopy = true; Buffer.BlockCopy(output, securityBuffer[1].offset, output, 4 + resultSize, securityBuffer[1].size); } resultSize += securityBuffer[1].size; if (securityBuffer[2].size != 0 && (forceCopy || resultSize != (count + sizes.SecurityTrailer))) { Buffer.BlockCopy(output, securityBuffer[2].offset, output, 4 + resultSize, securityBuffer[2].size); } resultSize += securityBuffer[2].size; unchecked { output[0] = (byte)((resultSize) & 0xFF); output[1] = (byte)(((resultSize) >> 8) & 0xFF); output[2] = (byte)(((resultSize) >> 16) & 0xFF); output[3] = (byte)(((resultSize) >> 24) & 0xFF); } return(resultSize + 4); }