雑記

2000|01|
2003|05|06|07|08|09|10|11|12|
2004|01|02|03|04|05|06|07|09|10|11|12|
2005|01|02|03|04|05|06|07|08|09|10|11|12|
2006|01|02|03|04|05|06|07|08|09|10|11|
2007|01|03|04|05|06|07|08|09|10|11|12|
2008|01|02|03|04|05|06|07|09|11|
2009|02|03|05|06|07|08|10|11|12|
2010|01|03|04|05|06|07|08|09|10|
2011|05|06|09|10|
2012|03|07|09|12|
2013|01|02|04|05|07|08|10|11|
2014|04|05|08|10|12|
2015|01|05|
2016|09|

2004-04-21 [長年日記]

TCP プロトコルに潜在する信頼性の問題

RFCで定義されているTCPの仕様に問題があり、悪意のある第3者がTCPのセッションを勝手に切ってしまうことが出来る可能性が、これまで考えられていたものよりずっと上がったという話らしいです。よく出来たまとめがセキュリティホールmemoなどにあるので、詳しい内容についてはそちらを見てもらうとして、FreeBSD 4.8-RELEASE-p17のソースの該当部分を見てみました。

問題のファイルは/usr/src/sys/netinet/tcp_input.cで、1335行目から始まるコメントの、

    * If the RST bit is set, check the sequence number to see
    * if this is a valid reset segment.
    * RFC 793 page 37:
    *   In all states except SYN-SENT, all reset (RST) segments
    *   are validated by checking their SEQ-fields.  A reset is
    *   valid if its sequence number is in the window.

という部分は今回の問題を6行でうまくまとめてあります(^^;。実際のチェック処理はコメントのすぐ後ろにあり、次のようになっています。

   if (thflags & TH_RST) {
      if (SEQ_GEQ(th->th_seq, tp->last_ack_sent) &&
          SEQ_LT(th->th_seq, tp->last_ack_sent + tp->rcv_wnd)) {
 以下、クローズ処理

TCPヘッダのRSTフラグが立っていて、かつ受け取ったパケットのシーケンス番号が最後に送信したACKパケットのシーケンス番号以上で、シーケンス番号+ウィンドウサイズ以下なら、コネクションを閉じる。という実装のようです。このソースを読むまでは、RSTのシーケンス番号は「ふつー」上のソースで言うlast_ack_sentと同じ値であることを期待して

   if (thflags & TH_RST) {
      if (SEQ_EQ(th->th_seq, tp->last_ack_sent)) { (←実際にはSEQ_EQ()というマクロはありません。/usr/include/netinet/tcp_seq.hを参照)
 以下、クローズ処理

と書き直せば良いんじゃないの?とか思ってたのですが、コメントの1359行目以降に、それが出来ない理由が書いてあります。

    *   The sequence number in the reset segment is normally an
    *   echo of our outgoing acknowlegement numbers, but some hosts
    *   send a reset with the sequence number at the rightmost edge
    *   of our receive window, and we have to handle this case.
    * If we have multiple segments in flight, the intial reset
    * segment sequence numbers will be to the left of last_ack_sent,
    * but they will eventually catch up.
    * In any case, it never made sense to trim reset segments to
    * fit the receive window since RFC 1122 says:
    *   4.2.2.12  RST Segment: RFC-793 Section 3.4
    *
    *    A TCP SHOULD allow a received RST segment to include data.
    *
    *    DISCUSSION
    *         It has been suggested that a RST segment could contain
    *         ASCII text that encoded and explained the cause of the
    *         RST.  No standard has yet been established for such
    *         data.

うーん。読めば読むほど実によくまとまっています。このコメントを書いた人は実は気づいてたんじゃなかろうかと思ったり思わなかったり。