Example #1
0
        // When you use netsh to configure HTTP.SYS with clientcertnegotiation = enable
        // which means negotiate client certificates, when the client makes the
        // initial SSL connection, the server (HTTP.SYS) requests the client certificate.
        //
        // Some apps may not want to negotiate the client cert at the beginning,
        // perhaps serving the default.htm. In this case the HTTP.SYS is configured
        // with clientcertnegotiation = disabled, which means that the client certificate is
        // optional so initially when SSL is established HTTP.SYS won't ask for client
        // certificate. This works fine for the default.htm in the case above,
        // however, if the app wants to demand a client certificate at a later time
        // perhaps showing "YOUR ORDERS" page, then the server wants to negotiate
        // Client certs. This will in turn makes HTTP.SYS to do the
        // SEC_I_RENOGOTIATE through which the client cert demand is made
        //
        // NOTE: When calling HttpReceiveClientCertificate you can get
        // ERROR_NOT_FOUND - which means the client did not provide the cert
        // If this is important, the server should respond with 403 forbidden
        // HTTP.SYS will not do this for you automatically
        internal Task LoadClientCertificateAsync()
        {
            uint size = CertBoblSize;
            bool retry;

            do
            {
                retry = false;
                uint bytesReceived = 0;

                uint statusCode =
                    HttpApi.HttpReceiveClientCertificate(
                        RequestQueueHandle,
                        RequestContext.Request.UConnectionId,
                        (uint)HttpApiTypes.HTTP_FLAGS.NONE,
                        RequestBlob,
                        size,
                        &bytesReceived,
                        NativeOverlapped);

                if (statusCode == UnsafeNclNativeMethods.ErrorCodes.ERROR_MORE_DATA)
                {
                    HttpApiTypes.HTTP_SSL_CLIENT_CERT_INFO *pClientCertInfo = RequestBlob;
                    size = bytesReceived + pClientCertInfo->CertEncodedSize;
                    Reset(size);
                    retry = true;
                }
                else if (statusCode == UnsafeNclNativeMethods.ErrorCodes.ERROR_NOT_FOUND)
                {
                    // The client did not send a cert.
                    Complete(0, null);
                }
                else if (statusCode == UnsafeNclNativeMethods.ErrorCodes.ERROR_SUCCESS &&
                         HttpSysListener.SkipIOCPCallbackOnSuccess)
                {
                    IOCompleted(statusCode, bytesReceived);
                }
                else if (statusCode != UnsafeNclNativeMethods.ErrorCodes.ERROR_SUCCESS &&
                         statusCode != UnsafeNclNativeMethods.ErrorCodes.ERROR_IO_PENDING)
                {
                    // Some other bad error, possible(?) return values are:
                    // ERROR_INVALID_HANDLE, ERROR_INSUFFICIENT_BUFFER, ERROR_OPERATION_ABORTED
                    // Also ERROR_BAD_DATA if we got it twice or it reported smaller size buffer required.
                    Fail(new HttpSysException((int)statusCode));
                }
            }while (retry);

            return(Task);
        }
Example #2
0
        internal static unsafe ChannelBinding GetChannelBindingFromTls(RequestQueue requestQueue, ulong connectionId, ILogger logger)
        {
            // +128 since a CBT is usually <128 thus we need to call HRCC just once. If the CBT
            // is >128 we will get ERROR_MORE_DATA and call again
            int size = RequestChannelBindStatusSize + 128;

            Debug.Assert(size >= 0);

            byte[] blob = null;
            SafeLocalFreeChannelBinding token = null;

            uint bytesReceived = 0;;
            uint statusCode;

            do
            {
                blob = new byte[size];
                fixed(byte *blobPtr = blob)
                {
                    // Http.sys team: ServiceName will always be null if
                    // HTTP_RECEIVE_SECURE_CHANNEL_TOKEN flag is set.
                    statusCode = HttpApi.HttpReceiveClientCertificate(
                        requestQueue.Handle,
                        connectionId,
                        (uint)HttpApiTypes.HTTP_FLAGS.HTTP_RECEIVE_SECURE_CHANNEL_TOKEN,
                        blobPtr,
                        (uint)size,
                        &bytesReceived,
                        SafeNativeOverlapped.Zero);

                    if (statusCode == UnsafeNclNativeMethods.ErrorCodes.ERROR_SUCCESS)
                    {
                        int tokenOffset = GetTokenOffsetFromBlob((IntPtr)blobPtr);
                        int tokenSize   = GetTokenSizeFromBlob((IntPtr)blobPtr);
                        Debug.Assert(tokenSize < Int32.MaxValue);

                        token = SafeLocalFreeChannelBinding.LocalAlloc(tokenSize);

                        Marshal.Copy(blob, tokenOffset, token.DangerousGetHandle(), tokenSize);
                    }
                    else if (statusCode == UnsafeNclNativeMethods.ErrorCodes.ERROR_MORE_DATA)
                    {
                        int tokenSize = GetTokenSizeFromBlob((IntPtr)blobPtr);
                        Debug.Assert(tokenSize < Int32.MaxValue);

                        size = RequestChannelBindStatusSize + tokenSize;
                    }
                    else if (statusCode == UnsafeNclNativeMethods.ErrorCodes.ERROR_INVALID_PARAMETER)
                    {
                        LogHelper.LogError(logger, "GetChannelBindingFromTls", "Channel binding is not supported.");
                        return(null); // old schannel library which doesn't support CBT
                    }
                    else
                    {
                        // It's up to the consumer to fail if the missing ChannelBinding matters to them.
                        LogHelper.LogException(logger, "GetChannelBindingFromTls", new HttpSysException((int)statusCode));
                        break;
                    }
                }
            }while (statusCode != UnsafeNclNativeMethods.ErrorCodes.ERROR_SUCCESS);

            return(token);
        }
Example #3
0
        private static unsafe void IOCompleted(ClientCertLoader asyncResult, uint errorCode, uint numBytes)
        {
            RequestContext requestContext = asyncResult.RequestContext;

            try
            {
                if (errorCode == UnsafeNclNativeMethods.ErrorCodes.ERROR_MORE_DATA)
                {
                    // There is a bug that has existed in http.sys since w2k3.  Bytesreceived will only
                    // return the size of the initial cert structure.  To get the full size,
                    // we need to add the certificate encoding size as well.

                    HttpApiTypes.HTTP_SSL_CLIENT_CERT_INFO *pClientCertInfo = asyncResult.RequestBlob;
                    asyncResult.Reset(numBytes + pClientCertInfo->CertEncodedSize);

                    uint bytesReceived = 0;
                    errorCode =
                        HttpApi.HttpReceiveClientCertificate(
                            requestContext.Server.RequestQueue.Handle,
                            requestContext.Request.UConnectionId,
                            (uint)HttpApiTypes.HTTP_FLAGS.NONE,
                            asyncResult._memoryBlob,
                            asyncResult._size,
                            &bytesReceived,
                            asyncResult._overlapped);

                    if (errorCode == UnsafeNclNativeMethods.ErrorCodes.ERROR_IO_PENDING ||
                        (errorCode == UnsafeNclNativeMethods.ErrorCodes.ERROR_SUCCESS && !HttpSysListener.SkipIOCPCallbackOnSuccess))
                    {
                        return;
                    }
                }

                if (errorCode == UnsafeNclNativeMethods.ErrorCodes.ERROR_NOT_FOUND)
                {
                    // The client did not send a cert.
                    asyncResult.Complete(0, null);
                }
                else if (errorCode != UnsafeNclNativeMethods.ErrorCodes.ERROR_SUCCESS)
                {
                    asyncResult.Fail(new HttpSysException((int)errorCode));
                }
                else
                {
                    HttpApiTypes.HTTP_SSL_CLIENT_CERT_INFO *pClientCertInfo = asyncResult._memoryBlob;
                    if (pClientCertInfo == null)
                    {
                        asyncResult.Complete(0, null);
                    }
                    else
                    {
                        if (pClientCertInfo->pCertEncoded != null)
                        {
                            try
                            {
                                byte[] certEncoded = new byte[pClientCertInfo->CertEncodedSize];
                                Marshal.Copy((IntPtr)pClientCertInfo->pCertEncoded, certEncoded, 0, certEncoded.Length);
                                asyncResult.Complete((int)pClientCertInfo->CertFlags, new X509Certificate2(certEncoded));
                            }
                            catch (CryptographicException exception)
                            {
                                // TODO: Log
                                asyncResult.Fail(exception);
                            }
                            catch (SecurityException exception)
                            {
                                // TODO: Log
                                asyncResult.Fail(exception);
                            }
                        }
                    }
                }
            }
            catch (Exception exception)
            {
                asyncResult.Fail(exception);
            }
        }