Bug 165359

Summary: netfilter fails to place packets into established state when ipsec in transport mode used
Product: Red Hat Enterprise Linux 4 Reporter: Aleksandar Milivojevic <alex>
Component: kernelAssignee: James Morris <jmorris>
Status: CLOSED CANTFIX QA Contact: Brian Brock <bbrock>
Severity: medium Docs Contact:
Priority: medium    
Version: 4.0CC: davem, jbaron, trevor
Target Milestone: ---Keywords: Security
Target Release: ---   
Hardware: i386   
OS: Linux   
Whiteboard:
Fixed In Version: Doc Type: Bug Fix
Doc Text:
Story Points: ---
Clone Of: Environment:
Last Closed: 2005-08-09 19:53:54 UTC Type: ---
Regression: --- Mount Type: ---
Documentation: --- CRM:
Verified Versions: Category: ---
oVirt Team: --- RHEL 7.3 requirements from Atomic Host:
Cloudforms Team: --- Target Upstream Version:
Embargoed:

Description Aleksandar Milivojevic 2005-08-08 15:25:40 UTC
From Bugzilla Helper:
User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.7.10) Gecko/20050721 CentOS/1.0.6-1.4.1.centos4 Firefox/1.0.6

Description of problem:
If I define IPSec between two hosts in transport mode, Netfilter's connection
tracking module fails to place returning packets into established state.

Configuration details.  Small note, I have patched ifup-ipsec/down scripts to allow for ESP-only IPSec.

Host A /etc/sysconfig/network-scripts/ifcfg-hostb:

TYPE=IPSEC
ONBOOT=yes
IKE_METHOD=PSK
IKE_PSK=secret
SRC=1.2.3.4
DST=4.3.2.1
AH_PROTO=none

Host B /etc/sysconfig/network-scripts/ifcfg-hosta:

TYPE=IPSEC
ONBOOT=yes
IKE_METHOD=PSK
IKE_PSK=secret
SRC=4.3.2.1
DST=1.2.3.4
AH_PROTO=none

Host B /etc/sysconfig/iptables:

*filter
:INPUT DROP [0:0]
:FORWARD DROP [0:0]
:OUTPUT DROP [0:0]

-A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
-A OUTPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
-A FORWARD -m state --state ESTABLISHED,RELATED -j ACCEPT

-A INPUT -p icmp --icmp-type ping -s 1.2.3.4 -m state --state NEW -j ACCEPT

-A INPUT -p esp -s 1.2.3.4 -j ACCEPT
-A INPUT -p udp -s 1.2.3.4 --sport 500 --dport 500 -m state --state NEW -j ACCEPT
-A OUTPUT -p esp -d 1.2.3.4 -j ACCEPT
-A OUTPUT -p udp -d 1.2.3.4 --sport 500 --dport 500 -m state --state NEW -j ACCEPT

-A INPUT -j LOG --log-prefix "INPUT "
-A OUTPUT -j LOG --log-prefix "OUTPUT "
-A FORWARD -j LOG --log-prefix "FORWARD "

COMMIT

If I run "ping -c 1 hostb" and then go to hostb and run "iptables -nvxL", it shows following output:

Chain INPUT (policy DROP 0 packets, 0 bytes)
    pkts      bytes target     prot opt in     out     source               destination
       0        0 ACCEPT     all  --  *      *       0.0.0.0/0            0.0.0.0/0           state RELATED,ESTABLISHED
       0        0 ACCEPT     icmp --  *      *       1.2.3.4              0.0.0.0/0           icmp type 8 state NEW
       1      120 ACCEPT     esp  --  *      *       1.2.3.4              0.0.0.0/0
       0        0 ACCEPT     udp  --  *      *       1.2.3.4              0.0.0.0/0           udp spt:500 dpt:500 state NEW
       0        0 LOG        all  --  *      *       0.0.0.0/0            0.0.0.0/0           LOG flags 0 level 4 prefix `INPUT '

Chain FORWARD (policy DROP 0 packets, 0 bytes)
    pkts      bytes target     prot opt in     out     source               destination
       0        0 ACCEPT     all  --  *      *       0.0.0.0/0            0.0.0.0/0           state RELATED,ESTABLISHED
       0        0 LOG        all  --  *      *       0.0.0.0/0            0.0.0.0/0           LOG flags 0 level 4 prefix `FORWARD '

Chain OUTPUT (policy DROP 1 packets, 84 bytes)
    pkts      bytes target     prot opt in     out     source               destination
       0        0 ACCEPT     all  --  *      *       0.0.0.0/0            0.0.0.0/0           state RELATED,ESTABLISHED
       0        0 ACCEPT     esp  --  *      *       0.0.0.0/0            1.2.3.4
       0        0 ACCEPT     udp  --  *      *       0.0.0.0/0            1.2.3.4             udp spt:500 dpt:500 state NEW
       1       84 LOG        all  --  *      *       0.0.0.0/0            0.0.0.0/0           LOG flags 0 level 4 prefix `OUTPUT '

As can be seen from this output, the decrypted packet never went to the filter chain of INPUT table.  That's why the returning packet was not marked as being in ESTABLISHED state, and not matched by generic established/related rule in OUTPUT chain of filter table (it gets dropped).

This also means that *all* packets comming from host A to host B will be accepted by Netfilter.  It is not possible to filter what kind of network traffic (after IPSec packet is decrypted) will be allowed between A and B!

Here's the generated log entry:

OUTPUT IN= OUT=eth0 SRC=1.2.3.4 DST=4.3.2.1 LEN=84 TOS=0x00 PREC=0x00 TTL=64 ID=34905 PROTO=ICMP TYPE=0 CODE=0 ID=22355 SEQ=0

Workaround:

Instead of configuring IPSec in transport mode, configure it in tunnel mode like this (host A config, similar change on host B):

TYPE=IPSEC
ONBOOT=yes
IKE_METHOD=PSK
IKE_PSK=secret
SRC=1.2.3.4
DST=4.3.2.1
SRCNET=1.2.3.4/32
DSTNET=4.3.2.1/32
AH_PROTO=none

Version-Release number of selected component (if applicable):
kernel-2.6.9-11.EL

How reproducible:
Always

Steps to Reproduce:
1. configure IPSec in transport mode between two hosts, ESP, no AH, let racoon handle the keying
2. place some firewall rules on either end
3. try to ping
  

Additional info:

Comment 1 David Miller 2005-08-09 19:53:54 UTC
This is a well known deficiency in netfilter support in the kernel these days.
It is, as a result, mostly unusable in conjunction with IPSEC.  Netfilter
hooks don't get called in a symmetric fashion in the presence of IPSEC, so
that the packet can be seen at the proper pre-decrypted and post-decrypted
states.  Major surgery is required to rectify this.

A fix is being worked on upstream, but it is exceeding unlikely that an
RHEL4 update could ever possibly include the fix when we even have it,
because:

1) the necessary fix is incredibly invasive
2) said fix is guarenteed to break the kernel ABI, and thus break
   3rd party binary-only kernel modules

And as such I'm for now marking this as a "CANTFIX" for RHEL4.


Comment 2 Aleksandar Milivojevic 2005-08-10 20:23:03 UTC
Many thanks for detail reply.  I have just one final question.  Once the
solution is found and implemented in the upstream kernel, I guess it will apear
relatively quickly in the Fedora Core kernels.  Will it be possible to install
(future) Fedora Core kernel RPM package on RHEL4 system, or at least rebuild
kernel from SRPM?  Or the scope of the changes is so big that it would require
userland stuff that interacts with the kernel to be updated too (such as
iptables, ipsec-tools, udev, and so on)?  Or is it still too early to give any
predictions?

I'm aware that using Fedora Core kernel would break compatibility with binary
3rd party device drivers, and might push system into "unsupported" state.

Comment 3 David Miller 2005-08-10 21:02:17 UTC
It should be a kernel-only fix, I do not anticipate any userlevel components
requiring a change.


Comment 4 Trevor Cordes 2005-08-30 23:39:34 UTC
For others finding this bug for the first time: also see bug #143374 which is
essentially the same thing.