private ListenerClientCertAsyncResult AsyncProcessClientCertificate(AsyncCallback requestCallback, object state) { if (_clientCertState == ListenerClientCertState.InProgress) throw new InvalidOperationException(SR.Format(SR.net_listener_callinprogress, "GetClientCertificate()/BeginGetClientCertificate()")); _clientCertState = ListenerClientCertState.InProgress; ListenerClientCertAsyncResult asyncResult = null; //-------------------------------------------------------------------- //When you configure the HTTP.SYS with a flag value 2 //which means require client certificates, when the client makes the //initial SSL connection, server (HTTP.SYS) demands the client certificate // //Some apps may not want to demand the client cert at the beginning //perhaps server the default.htm. In this case the HTTP.SYS is configured //with a flag value other than 2, whcih means that the client certificate is //optional.So intially 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 certficate at a later time //perhaps showing "YOUR ORDERS" page, then the server wans to demand //Client certs. this will inturn makes HTTP.SYS to do the //SEC_I_RENOGOTIATE through which the client cert demand is made // //THE BUG HERE IS THAT PRIOR TO QFE 4796, we call //GET Client certificate native API ONLY WHEN THE HTTP.SYS is configured with //flag = 2. Which means that apps using HTTPListener will not be able to //demand a client cert at a later point // //The fix here is to demand the client cert when the channel is NOT INSECURE //which means whether the client certs are requried at the beginning or not, //if this is an SSL connection, Call HttpReceiveClientCertificate, thus //starting the cert negotiation at that point // //NOTE: WHEN CALLING THE 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 *** //-------------------------------------------------------------------- if (_sslStatus != SslStatus.Insecure) { // at this point we know that DefaultFlags has the 2 bit set (Negotiate Client certificate) // the cert, though might or might not be there. try to retrieve it // this number is the same that IIS decided to use uint size = CertBoblSize; asyncResult = new ListenerClientCertAsyncResult(HttpListenerContext.RequestQueueBoundHandle, this, state, requestCallback, size); try { while (true) { if (NetEventSource.IsEnabled) NetEventSource.Info(this, "Calling Interop.HttpApi.HttpReceiveClientCertificate size:" + size); uint bytesReceived = 0; uint statusCode = Interop.HttpApi.HttpReceiveClientCertificate( HttpListenerContext.RequestQueueHandle, _connectionId, (uint)Interop.HttpApi.HTTP_FLAGS.NONE, asyncResult.RequestBlob, size, &bytesReceived, asyncResult.NativeOverlapped); if (NetEventSource.IsEnabled) NetEventSource.Info(this, "Call to Interop.HttpApi.HttpReceiveClientCertificate returned:" + statusCode + " bytesReceived:" + bytesReceived); if (statusCode == Interop.HttpApi.ERROR_MORE_DATA) { Interop.HttpApi.HTTP_SSL_CLIENT_CERT_INFO* pClientCertInfo = asyncResult.RequestBlob; size = bytesReceived + pClientCertInfo->CertEncodedSize; asyncResult.Reset(size); continue; } 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 // Also ERROR_BAD_DATA if we got it twice or it reported smaller size buffer required. throw new HttpListenerException((int)statusCode); } if (statusCode == Interop.HttpApi.ERROR_SUCCESS && HttpListener.SkipIOCPCallbackOnSuccess) { asyncResult.IOCompleted(statusCode, bytesReceived); } break; } } catch { asyncResult?.InternalCleanup(); throw; } } else { asyncResult = new ListenerClientCertAsyncResult(HttpListenerContext.RequestQueueBoundHandle, this, state, requestCallback, 0); asyncResult.InvokeCallback(); } return asyncResult; }
private ListenerClientCertAsyncResult AsyncProcessClientCertificate(AsyncCallback requestCallback, object state) { if (_clientCertState == ListenerClientCertState.InProgress) { throw new InvalidOperationException(SR.Format(SR.net_listener_callinprogress, "GetClientCertificate()/BeginGetClientCertificate()")); } _clientCertState = ListenerClientCertState.InProgress; ListenerClientCertAsyncResult asyncResult = null; //-------------------------------------------------------------------- //When you configure the HTTP.SYS with a flag value 2 //which means require client certificates, when the client makes the //initial SSL connection, server (HTTP.SYS) demands the client certificate // //Some apps may not want to demand the client cert at the beginning //perhaps server the default.htm. In this case the HTTP.SYS is configured //with a flag value other than 2, whcih 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 certficate at a later time //perhaps showing "YOUR ORDERS" page, then the server wans to demand //Client certs. this will inturn makes HTTP.SYS to do the //SEC_I_RENOGOTIATE through which the client cert demand is made // //THE BUG HERE IS THAT PRIOR TO QFE 4796, we call //GET Client certificate native API ONLY WHEN THE HTTP.SYS is configured with //flag = 2. Which means that apps using HTTPListener will not be able to //demand a client cert at a later point // //The fix here is to demand the client cert when the channel is NOT INSECURE //which means whether the client certs are requried at the beginning or not, //if this is an SSL connection, Call HttpReceiveClientCertificate, thus //starting the cert negotiation at that point // //NOTE: WHEN CALLING THE 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 *** //-------------------------------------------------------------------- if (_sslStatus != SslStatus.Insecure) { // at this point we know that DefaultFlags has the 2 bit set (Negotiate Client certificate) // the cert, though might or might not be there. try to retrieve it // this number is the same that IIS decided to use uint size = CertBoblSize; asyncResult = new ListenerClientCertAsyncResult(HttpListenerContext.RequestQueueBoundHandle, this, state, requestCallback, size); try { while (true) { if (NetEventSource.IsEnabled) { NetEventSource.Info(this, "Calling Interop.HttpApi.HttpReceiveClientCertificate size:" + size); } uint bytesReceived = 0; uint statusCode = Interop.HttpApi.HttpReceiveClientCertificate( HttpListenerContext.RequestQueueHandle, _connectionId, (uint)Interop.HttpApi.HTTP_FLAGS.NONE, asyncResult.RequestBlob, size, &bytesReceived, asyncResult.NativeOverlapped); if (NetEventSource.IsEnabled) { NetEventSource.Info(this, "Call to Interop.HttpApi.HttpReceiveClientCertificate returned:" + statusCode + " bytesReceived:" + bytesReceived); } if (statusCode == Interop.HttpApi.ERROR_MORE_DATA) { Interop.HttpApi.HTTP_SSL_CLIENT_CERT_INFO *pClientCertInfo = asyncResult.RequestBlob; size = bytesReceived + pClientCertInfo->CertEncodedSize; asyncResult.Reset(size); continue; } 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 // Also ERROR_BAD_DATA if we got it twice or it reported smaller size buffer required. throw new HttpListenerException((int)statusCode); } if (statusCode == Interop.HttpApi.ERROR_SUCCESS && HttpListener.SkipIOCPCallbackOnSuccess) { asyncResult.IOCompleted(statusCode, bytesReceived); } break; } } catch { asyncResult?.InternalCleanup(); throw; } } else { asyncResult = new ListenerClientCertAsyncResult(HttpListenerContext.RequestQueueBoundHandle, this, state, requestCallback, 0); asyncResult.InvokeCallback(); } return(asyncResult); }
private ListenerClientCertAsyncResult AsyncProcessClientCertificate(AsyncCallback requestCallback, object state) { if (m_ClientCertState == ListenerClientCertState.InProgress) throw new InvalidOperationException(SR.GetString(SR.net_listener_callinprogress, "GetClientCertificate()/BeginGetClientCertificate()")); m_ClientCertState = ListenerClientCertState.InProgress; HttpListenerContext.EnsureBoundHandle(); ListenerClientCertAsyncResult asyncResult = null; //-------------------------------------------------------------------- //When you configure the HTTP.SYS with a flag value 2 //which means require client certificates, when the client makes the //initial SSL connection, server (HTTP.SYS) demands the client certificate // //Some apps may not want to demand the client cert at the beginning //perhaps server the default.htm. In this case the HTTP.SYS is configured //with a flag value other than 2, whcih means that the client certificate is //optional.So intially 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 certficate at a later time //perhaps showing "YOUR ORDERS" page, then the server wans to demand //Client certs. this will inturn makes HTTP.SYS to do the //SEC_I_RENOGOTIATE through which the client cert demand is made // //THE if (m_SslStatus != SslStatus.Insecure) { // at this point we know that DefaultFlags has the 2 bit set (Negotiate Client certificate) // the cert, though might or might not be there. try to retrieve it // this number is the same that IIS decided to use uint size = CertBoblSize; asyncResult = new ListenerClientCertAsyncResult(this, state, requestCallback, size); try { while (true) { GlobalLog.Print("HttpListenerRequest#" + ValidationHelper.HashString(this) + "::ProcessClientCertificate() calling UnsafeNclNativeMethods.HttpApi.HttpReceiveClientCertificate size:" + size); uint bytesReceived = 0; uint statusCode = UnsafeNclNativeMethods.HttpApi.HttpReceiveClientCertificate( HttpListenerContext.RequestQueueHandle, m_ConnectionId, (uint)UnsafeNclNativeMethods.HttpApi.HTTP_FLAGS.NONE, asyncResult.RequestBlob, size, &bytesReceived, asyncResult.NativeOverlapped); GlobalLog.Print("HttpListenerRequest#" + ValidationHelper.HashString(this) + "::ProcessClientCertificate() call to UnsafeNclNativeMethods.HttpApi.HttpReceiveClientCertificate returned:" + statusCode + " bytesReceived:" + bytesReceived); if (statusCode == UnsafeNclNativeMethods.ErrorCodes.ERROR_MORE_DATA) { UnsafeNclNativeMethods.HttpApi.HTTP_SSL_CLIENT_CERT_INFO* pClientCertInfo = asyncResult.RequestBlob; size = bytesReceived + pClientCertInfo->CertEncodedSize; asyncResult.Reset(size); continue; } if (statusCode != UnsafeNclNativeMethods.ErrorCodes.ERROR_SUCCESS && statusCode != UnsafeNclNativeMethods.ErrorCodes.ERROR_IO_PENDING) { // someother 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. throw new HttpListenerException((int)statusCode); } if (statusCode == UnsafeNclNativeMethods.ErrorCodes.ERROR_SUCCESS && HttpListener.SkipIOCPCallbackOnSuccess) { asyncResult.IOCompleted(statusCode, bytesReceived); } break; } } catch { if (asyncResult!=null) { asyncResult.InternalCleanup(); } throw; } } else { asyncResult = new ListenerClientCertAsyncResult(this, state, requestCallback, 0); asyncResult.InvokeCallback(); } return asyncResult; }