stream: add more liberal timestamp behavior in 3WHS

RFC 7323 forbids a server to respond with a timestamp option in the
SYN/ACK when the SYN didn't have a timestamp option:

   A TCP MAY send the TSopt in an initial <SYN> segment (i.e., segment
   containing a SYN bit and no ACK bit), and MAY send a TSopt in
   <SYN,ACK> only if it received a TSopt in the initial <SYN> segment
   for the connection.

   Once TSopt has been successfully negotiated, that is both <SYN> and
   <SYN,ACK> contain TSopt, the TSopt MUST be sent in every non-<RST>
   segment for the duration of the connection, and SHOULD be sent in an
   <RST> segment (see Section 5.2 for details).

However, in the real world this pattern happens on benign traffic. This
would lead to missing logs and detection, and in IPS mode such sessions
would be blocked.

This patch allows this pattern when the `stream.liberal-timestamps` is
enabled (enabled by default).

Bug #4702.
pull/13817/head
Victor Julien 3 months ago committed by Victor Julien
parent be6315dba0
commit d352b75ac6

@ -1890,9 +1890,14 @@ static void TcpStateQueueInitFromPktSynAck(const Packet *p, TcpStateQueue *q)
/** \internal /** \internal
* \brief Find the Queued SYN that is the same as this SYN/ACK * \brief Find the Queued SYN that is the same as this SYN/ACK
* \retval q or NULL */ * \param[in] ignore_ts if true, ignore the timestamp
* \retval q or NULL
*
* \note When `ignore_ts`, the following is accepted: SYN w/o TS, SYN/ACK with TS.
* \note When `ignore_ts` is set, `s` corresponds to the SYN/ACK packet and the
* queue holds the stored SYN packets. */
static const TcpStateQueue *StreamTcp3whsFindSyn( static const TcpStateQueue *StreamTcp3whsFindSyn(
const TcpSession *ssn, TcpStateQueue *s, TcpStateQueue **ret_tail) const TcpSession *ssn, TcpStateQueue *s, TcpStateQueue **ret_tail, const bool ignore_ts)
{ {
SCLogDebug("ssn %p: search state:%p, isn:%u/win:%u/has_ts:%s/tsval:%u", ssn, s, s->seq, s->win, SCLogDebug("ssn %p: search state:%p, isn:%u/win:%u/has_ts:%s/tsval:%u", ssn, s, s->seq, s->win,
BOOL2STR(s->flags & STREAMTCP_QUEUE_FLAG_TS), s->ts); BOOL2STR(s->flags & STREAMTCP_QUEUE_FLAG_TS), s->ts);
@ -1902,10 +1907,16 @@ static const TcpStateQueue *StreamTcp3whsFindSyn(
SCLogDebug("ssn %p: queue state:%p, isn:%u/win:%u/has_ts:%s/tsval:%u (last:%s)", ssn, q, SCLogDebug("ssn %p: queue state:%p, isn:%u/win:%u/has_ts:%s/tsval:%u (last:%s)", ssn, q,
q->seq, q->win, BOOL2STR(q->flags & STREAMTCP_QUEUE_FLAG_TS), q->ts, q->seq, q->win, BOOL2STR(q->flags & STREAMTCP_QUEUE_FLAG_TS), q->ts,
BOOL2STR(q->next == NULL)); BOOL2STR(q->next == NULL));
if ((s->flags & STREAMTCP_QUEUE_FLAG_TS) == (q->flags & STREAMTCP_QUEUE_FLAG_TS) &&
s->ts == q->ts && s->seq == q->seq) { if (s->flags & STREAMTCP_QUEUE_FLAG_TS) {
if ((q->flags & STREAMTCP_QUEUE_FLAG_TS) && s->ts == q->ts && s->seq == q->seq) {
return q; return q;
} }
} else if (ignore_ts) {
if (s->seq == q->seq) {
return q;
}
}
last = q; last = q;
} }
if (ret_tail) if (ret_tail)
@ -1937,7 +1948,7 @@ static int StreamTcp3whsStoreSyn(TcpSession *ssn, Packet *p)
TcpStateQueue *tail = NULL; TcpStateQueue *tail = NULL;
/* first see if this is already in our list */ /* first see if this is already in our list */
if (ssn->queue != NULL && StreamTcp3whsFindSyn(ssn, &search, &tail) != NULL) if (ssn->queue != NULL && StreamTcp3whsFindSyn(ssn, &search, &tail, false) != NULL)
return 0; return 0;
if (ssn->queue_len == stream_config.max_syn_queued) { if (ssn->queue_len == stream_config.max_syn_queued) {
@ -2025,7 +2036,8 @@ static inline bool StateSynSentCheckSynAck3Whs(TcpSession *ssn, Packet *p, const
TcpStateQueueInitFromPktSynAck(p, &search); TcpStateQueueInitFromPktSynAck(p, &search);
SCLogDebug("%" PRIu64 ": ssn %p: SYN/ACK looking for SEQ %u", p->pcap_cnt, ssn, search.seq); SCLogDebug("%" PRIu64 ": ssn %p: SYN/ACK looking for SEQ %u", p->pcap_cnt, ssn, search.seq);
const TcpStateQueue *q = StreamTcp3whsFindSyn(ssn, &search, NULL); const TcpStateQueue *q =
StreamTcp3whsFindSyn(ssn, &search, NULL, stream_config.liberal_timestamps);
if (q == NULL) { if (q == NULL) {
SCLogDebug("not found: mismatch"); SCLogDebug("not found: mismatch");
goto failure; goto failure;
@ -2074,7 +2086,8 @@ static inline bool StateSynSentCheckSynAckTFO(TcpSession *ssn, Packet *p, const
TcpStateQueueInitFromPktSynAck(p, &search); TcpStateQueueInitFromPktSynAck(p, &search);
SCLogDebug("%" PRIu64 ": ssn %p: SYN/ACK looking for SEQ %u", p->pcap_cnt, ssn, search.seq); SCLogDebug("%" PRIu64 ": ssn %p: SYN/ACK looking for SEQ %u", p->pcap_cnt, ssn, search.seq);
const TcpStateQueue *q = StreamTcp3whsFindSyn(ssn, &search, NULL); const TcpStateQueue *q =
StreamTcp3whsFindSyn(ssn, &search, NULL, stream_config.liberal_timestamps);
if (q == NULL) { if (q == NULL) {
SCLogDebug("not found: mismatch"); SCLogDebug("not found: mismatch");
goto failure; goto failure;
@ -2117,7 +2130,11 @@ static int StreamTcpPacketStateSynSent(
if ((tcph->th_flags & (TH_SYN | TH_ACK)) == (TH_SYN | TH_ACK) && PKT_IS_TOCLIENT(p)) { if ((tcph->th_flags & (TH_SYN | TH_ACK)) == (TH_SYN | TH_ACK) && PKT_IS_TOCLIENT(p)) {
SCLogDebug("%" PRIu64 ": ssn %p: SYN/ACK on SYN_SENT state for packet %" PRIu64, SCLogDebug("%" PRIu64 ": ssn %p: SYN/ACK on SYN_SENT state for packet %" PRIu64,
p->pcap_cnt, ssn, p->pcap_cnt); p->pcap_cnt, ssn, p->pcap_cnt);
const bool ts_mismatch = !StateSynSentValidateTimestamp(ssn, p); /* if timestamps are liberal, allow a SYN/ACK with TS even if the SYN
* had none (violates RFC 7323, see bug #4702). */
const bool ts_mismatch =
!(stream_config.liberal_timestamps || StateSynSentValidateTimestamp(ssn, p));
SCLogDebug("ts_mismatch %s", BOOL2STR(ts_mismatch));
if (!(TCP_HAS_TFO(p) || (ssn->flags & STREAMTCP_FLAG_TCP_FAST_OPEN))) { if (!(TCP_HAS_TFO(p) || (ssn->flags & STREAMTCP_FLAG_TCP_FAST_OPEN))) {
if (StateSynSentCheckSynAck3Whs(ssn, p, ts_mismatch)) { if (StateSynSentCheckSynAck3Whs(ssn, p, ts_mismatch)) {

Loading…
Cancel
Save