private void Reset(uint size) { if (size == _size) { return; } if (_size != 0) { _overlapped !.Dispose(); } _size = size; if (size == 0) { _overlapped = null; _memoryBlob = null; _backingBuffer = null; return; } _backingBuffer = new byte[checked ((int)size)]; var boundHandle = RequestContext.Server.RequestQueue.BoundHandle; _overlapped = new SafeNativeOverlapped(boundHandle, boundHandle.AllocateNativeOverlapped(IOCallback, this, _backingBuffer)); _memoryBlob = (HttpApiTypes.HTTP_SSL_CLIENT_CERT_INFO *)Marshal.UnsafeAddrOfPinnedArrayElement(_backingBuffer, 0); }
private void Dispose(bool disposing) { if (disposing) { _cancellationRegistration.Dispose(); if (_overlapped != null) { _memoryBlob = null; _overlapped.Dispose(); } } }
// 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); }
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); } }