提高ASIO SSL async_shutdown总是出错完成?

提高ASIO SSL async_shutdown总是出错完成?

问题描述:

我有升压1.55已设定ASIO小SSL客户端,我试图找出原因的boost ::支持ASIO :: SSL ::流:: async_shutdown()总是失败。客户端是非常相似的(几乎相同)升压文档中的SSL客户端的例子,因为它通过一个的boost ::支持ASIO ::知识产权:: TCP ::解析器:: async_resolve() - > 的boost ::支持ASIO :: SSL ::流:: async_connect() - > 的boost :: ASIO :: SSL ::流:: async_handshake()回调序列。所有这一切都为预期和 async_handshake()回调得到一个全清的boost ::系统::错误_ code工作

I have a small ssl client that I've programmed in boost 1.55 asio, and I'm trying to figure out why boost::asio::ssl::stream::async_shutdown() always fails. The client is very similar (almost identical) to the ssl client examples in the boost documentation, in that it goes through an boost::asio::ip::tcp::resolver::async_resolve() -> boost::asio::ssl::stream::async_connect() -> boost::asio::ssl::stream::async_handshake() callback sequence. All of this works as expected and the async_handshake() callback gets an all-clear boost::system::error_code.

async_handshake()回调,我称之为 async_shutdown()(我不传送任何数据 - 这个对象是多为测试握手):

From the async_handshake() callback, I call async_shutdown() (I don't transfer any data - this object is more for testing the handshake):

void ClientCertificateFinder::handle_handshake(const boost::system::error_code& e)
{
    if ( !e )
    {
        m_socket.async_shutdown( boost::bind( &ClientCertificateFinder::handle_shutdown_after_success, 
            this, 
            boost::asio::placeholders::error ) );
    }
    else
    {
        m_handler( e, IssuerNameList() );
    }
}

handle_shutdown_after_success()然后调用,但总是错误?该错误是在 asio.misc 值= 2,这就是'文件结束。我曾与各种SSL服务器尝试了这一点,我似乎总是得到这个 asio.misc 错误。这不是一个潜在的OpenSSL的错误建议,我认为我可能会以某种方式ASIO滥用...?

handle_shutdown_after_success() is then called, but always with an error? The error is value=2 in asio.misc, which is 'End of file'. I've tried this with a variety of ssl servers, and I always seem to get this asio.misc error. That this isn't an underlying openssl error suggests to me that I might be misusing asio in some way...?

任何人都知道为什么会是这样吗?我是关闭async_shutdown()是正确的事情,但我想我可以叫的连接IM pression下>的boost ::支持ASIO :: SSL :: stream.lowestlayer()。关闭()从下面的openssl关闭套接字出来,如果​​这是这样做(乃至ASIO SSL例子似乎预期的方式以表明这是关闭)的正确方法。

Anyone know why this might be happening? I was under the impression that shutting down the connection with async_shutdown() was The Right Thing To Do, but I guess I could just call boost::asio::ssl::stream.lowestlayer().close() to close the socket out from under openssl if that's the expected way to do this (and indeed the asio ssl examples seem to indicate that this is the right way of shutting down).

有关加密安全关机,双方务必在boost::asio::ssl::stream$c$c>由要么调用关机() async_shutdown()和运行 io_service对象。如果操作与错误_ code 不具有SSL类别并没有取消可能发生关闭的部分之前,则连接是牢固地关断和底层传输可以重复使用或关闭。简单地关闭最低层可能使会话易受截断攻击

For a cryptographically secure shutdown, both parties musts execute shutdown operations on the boost::asio::ssl::stream by either invoking shutdown() or async_shutdown() and running the io_service. If the operation completes with an error_code that does not have an SSL category and was not cancelled before part of the shutdown could occur, then the connection was securely shutdown and the underlying transport may be reused or closed. Simply closing the lowest layer may make the session vulnerable to a truncation attack.

在标准化 TLS 的协议和非标准化 SSLv3的协议,安全的关闭涉及各方交换 close_notify消息消息。在Boost.Asio的API而言,任何一方都可以通过调用启动停机时关机() async_shutdown() ,引起了 close_notify消息消息发送给对方,通知收件人发起者不会对SSL连接上发送更多的消息。根据规范要求,收件人必须以 close_notify消息消息作出响应。 Boost.Asio的不会自动执行此行为,并要求收件人显式调用关机() async_shutdown()

In the standardized TLS protocol and the non-standardized SSLv3 protocol, a secure shutdown involves parties exchanging close_notify messages. In terms of the Boost.Asio API, either party may initiate a shutdown by invoking shutdown() or async_shutdown(), causing a close_notify message to be sent to the other party, informing the recipient that the initiator will not send more messages on the SSL connection. Per the specification, the recipient must respond with a close_notify message. Boost.Asio does not automatically perform this behavior, and requires the recipient to explicitly invoke shutdown() or async_shutdown().

在规范允许的停机的发起者接收 close_notify消息响应之前关闭连接它们的读取端。这是在其中应用程序协议不希望重复使用的基本协议的情况下使用。不幸的是,Boost.Asio的目前不(1.56)提供了这种能力的直接支持。在Boost.Asio的,在关机()操作错误时,或者如果一方已发送和接收一个 close_notify消息视为完整>消息。一旦操作已经完成时,应用程序就可以重新使用的基本协议或关闭它。

The specification permits the initiator of the shutdown to close their read side of the connection before receiving the close_notify response. This is used in cases where the application protocol does not wish to reuse the underlying protocol. Unfortunately, Boost.Asio does not currently (1.56) provide direct support for this capability. In Boost.Asio, the shutdown() operation is considered complete upon error or if the party has sent and received a close_notify message. Once the operation has completed, the application may reuse the underlying protocol or close it.

一旦SSL连接已经建立,关闭过程中会出现以下错误codeS:

Once an SSL connection has been established, the following error codes occur during shutdown:


  • 一方发起关机和远程方关闭或已经关闭底层传输,而无需关闭的协议:

    • 发起者的关机()操作将失败,一个SSL短读取错误。

    • One party initiates a shutdown and the remote party closes or has already closed the underlying transport without shutting down the protocol:
      • The initiator's shutdown() operation will fail with an SSL short read error.

      • 发起的关机操作将与错误值的boost ::支持ASIO ::错误:: EOF 完成。

      • 远程方的关机()操作成功完成。

      • The initiator's shutdown operation will complete with an error value of boost::asio::error::eof.
      • The remote party's shutdown() operation completes with success.

      • 发起者的关机()操作将被取消,导致升压的错误:: ASIO ::错误:: operation_aborted 。这是下面的信息指出一种变通方法的结果。

      • 远程方的关机()操作成功完成。

      • The initiator's shutdown() operation will be cancelled, resulting in an error of boost::asio::error::operation_aborted. This is the result of a workaround noted in the details below.
      • The remote party's shutdown() operation completes with success.

      这些各种方案将在下面详述的捕获。每个场景是说明与游泳线图一样,说明每一方是在相同的时间点做的。

      These various scenarios are captured in detailed below. Each scenario is illustrated with a swim-line like diagram, indicating what each party is doing at the exact same point in time.

      在这种情况下,乙方通过关闭底层传输不首先调用关机()上清溪违反关机程序。一旦底层传输已经关闭,甲方尝试启动关机()

      In this scenario, PartyB violates the shutdown procedure by closing the underlying transport without first invoking shutdown() on the stream. Once the underlying transport has been closed, the PartyA attempts to initiate a shutdown().

       PartyA                              | PartyB
      -------------------------------------+----------------------------------------
       ssl_stream.handshake(...);          | ssl_stream.handshake(...);
       ...                                 | ssl_stream.lowest_layer().close();
       ssl_stream.shutdown();              |
      

      甲方会试图发送一个 close_notify消息消息,但写的底层传输将失败,升压: :ASIO ::错误:: EOF 。 Boost.Asio的将explicitly地图底层传输的 EOF 错误给一个SSL短读取错误,如乙方违反了SSL关机程序。

      PartyA will attempt to send a close_notify message, but the write to the underlying transport will fail with boost::asio::error::eof. Boost.Asio will explicitly map the underlying transport's eof error to an SSL short read error, as PartyB violated the SSL shutdown procedure.

      if ((error.category() == boost::asio::error::get_ssl_category())
           && (ERR_GET_REASON(error.value()) == SSL_R_SHORT_READ))
      {
        // Remote peer failed to send a close_notify message.
      }
      

      甲方调用关机()然后乙方关闭,不谈判关机连接。

      在这种情况下,甲方启动关机。然而,虽然乙方收到 close_notify消息消息,乙方作者从来没有与一个关机()前关闭底层传输。

      PartyA invokes shutdown() then PartyB closes connection without negotiating shutdown.

      In this scenario, PartyA initiates a shutdown. However, while PartyB receives the close_notify message, PartyB violates the shutdown procedure by never explicitly responding with a shutdown() before closing the underlying transport.

       PartyA                              | PartyB
      -------------------------------------+---------------------------------------
       ssl_stream.handshake(...);          | ssl_stream.handshake(...);
       ssl_stream.shutdown();              | ...
                                           | ssl_stream.lowest_layer().close();
      

      由于Boost.Asio的的关机()操作被认为是完整的一次 close_notify消息已发送和接收或出现错误,甲方将发送 close_notify消息然后等待响应。 乙方关闭底层传输,而不发送 close_notify消息,违反了SSL协议。 甲方的读操作将失败,的boost ::支持ASIO ::错误:: EOF ,并且将Boost.Asio的其映射到SSL短读取错误。

      As Boost.Asio's shutdown() operation is considered complete once a close_notify has been both sent and received or an error occurs, PartyA will send a close_notify then wait for a response. PartyB closes the underlying transport without sending a close_notify, violating the SSL protocol. PartyA's read will fail with boost::asio::error::eof, and Boost.Asio will map it to an SSL short read error.

      在这种情况下,甲方将启动关闭,并等待乙方与关机回应。

      In this scenario, PartyA will initiate a shutdown and wait for PartyB to respond with a shutdown.

       PartyA                              | PartyB
      -------------------------------------+----------------------------------------
       ssl_stream.handshake(...);          | ssl_stream.handshake(...);
       ssl_stream.shutdown();              | ...
       ...                                 | ssl_stream.shutdown();
      

      这是一个相当基本关闭的,双方当事人发送和接收 close_notify消息消息。一旦关闭已经由供需双方协商,底层传输既可以重复使用或关闭。

      This is a fairly basic shutdown, where both parties send and receive a close_notify message. Once the shutdown has been negotiated by both parties, the underlying transport may either be reused or closed.


      • 甲方的关机操作将错误值的boost ::支持ASIO ::错误:: EOF 完成。

      • 乙方的关机操作将成功完成。

      • PartyA's shutdown operation will complete with an error value of boost::asio::error::eof.
      • PartyB's shutdown operation will complete with success.

      在这种情况下,甲方将启动关闭,然后立即关闭底层传输一次 close_notify消息已发送。 甲方不等待乙方与一个 close_notify消息消息作出响应。这种类型的协商关机每规格和相当普遍允许之间实现。

      In this scenario, PartyA will initiate a shutdown and then immediately close the underlying transport once close_notify has been sent. PartyA does not wait for PartyB to respond with a close_notify message. This type of negotiated shutdown is allowed per the specification and fairly common amongst implementations.

      如上所述,Boost.Asio的不直接支持这种类型的关机。 Boost.Asio的的关机()操作将等待远程端发送其 close_notify消息。然而,这是可能实现的解决方法,同时仍然坚持规范。

      As mentioned above, Boost.Asio does not directly support this type of shutdown. Boost.Asio's shutdown() operation will wait for the remote peer to send its close_notify. However, it is possible to implement a workaround while still upholding the specification.

       PartyA                              | PartyB
      -------------------------------------+---------------------------------------
       ssl_stream.handshake(...);          | ssl_stream.handshake(...)
       ssl_stream.async_shutdown(...);     | ...
       const char buffer[] = "";           | ...
       async_write(ssl_stream, buffer,     | ...
        [](...) { ssl_stream.close(); })   | ...
       io_service.run();                   | ...
       ...                                 | ssl_stream.shutdown();
      

      甲方将启动一个异步关闭操作,然后开始一个异步写入操作。用于写的缓冲器必须是一个非零长度的(空字符上面使用);否则,将Boost.Asio的优化写入无操作。当关机()操作运行时,它会发送 close_notify消息乙方,导致SSL关闭甲方的写入端的SSL流,然后异步等待乙方 close_notify消息。然而,由于写边甲方的SSL流关闭 async_write()操作将失败,SSL错误表明该协议具有一直关机。

      PartyA will initiate an asynchronous shutdown operation and then initiate an asynchronous write operation. The buffer used for the write must be of a non-zero length (null character is used above); otherwise, Boost.Asio will optimize the write to a no-op. When the shutdown() operation runs, it will send close_notify to PartyB, causing SSL to close the write side of PartyA's SSL stream, and then asynchronously wait for PartyB's close_notify. However, as the write side of PartyA's SSL stream has closed, the async_write() operation will fail with an SSL error indicating the protocol has been shutdown.

      if ((error.category() == boost::asio::error::get_ssl_category())
           && (SSL_R_PROTOCOL_IS_SHUTDOWN == ERR_GET_REASON(error.value())))
      {
        ssl_stream.lowest_layer().close();
      }
      

      失败的 async_write()操作,然后将明确地关闭底层传输,引起 async_shutdown()的操作,正在等待乙方 close_notify消息取消。

      The failed async_write() operation will then explicitly close the underlying transport, causing the async_shutdown() operation that is waiting for PartyB's close_notify to be cancelled.


      • 虽然甲方的执行由SSL规范允许的关机程序,关机()操作被明确当底层传输被关闭取消。因此,关机()操作的错误code将的boost ::支持ASIO ::错误:: operation_aborted $ C的值$ C>。

      • 乙方的关机操作将成功完成。

      • Although PartyA performed a shutdown procedure permitted by the SSL specification, the shutdown() operation was explicitly cancelled when underlying transport was closed. Hence, the shutdown() operation's error code will have a value of boost::asio::error::operation_aborted.
      • PartyB's shutdown operation will complete with success.

      总之,Boost.Asio的的SSL关机操作是有点棘手。在适当的停工引发剂和远程对等的错误codeS之间的inconstancies可以处理有点尴尬。作为一般规则,只要错误code的类别不是一个SSL类别,然后该协议是安全地关机。

      In summary, Boost.Asio's SSL shutdown operations are a bit tricky. The inconstancies between the initiator and remote peer's error codes during proper shutdowns can make handling a bit awkward. As a general rule, as long as the error code's category is not an SSL category, then the protocol was securely shutdown.