#!/usr/bin/perl

# Copyright (C) 2000-2004 NEC Corporation
# All Rights Reserved.

# RCS: $Id: ipchconf,v 1.1 2006/12/22 05:33:58 shodai Exp $

##20001009 4.1 RPMへ追加
##001116 v5.1 ipchains,ip_forward対応(wbmc03-046) haramoto
##040919 v6.0.1 iptables に変更 oyama
##041015 v6.0.1 ethX_in チェインと default チェインのみを削除 oyama
##041029 v6.0.1 定義されていないチェインの削除を取りやめ

require "/opt/nec/wbmc/lib/wbmc.pl";

chdir("/opt/nec/wbmc/adm/system/security");
require "./ipchconf.pl";
&readIpchConfFile($M_SECFN_IPCHAINS_CUR_CONF);
&readFltSrvFile();
chdir("../../bin");

# logging option
$alog = "";
$dlog = "";

# ZZZ for debug
if ($ARGV[0] eq "-d") {
    $alog = "-l";
    $dlog = "-l";
}

@ipchopt = ();
&clearAll();
&denyAll("on");
&setForward();
&setInput();
&denyAll("off");
&cve20084609();

#&writeIpForward();		##001116
&callIpch();

########################################################################

#
# clearAll()
#

sub clearAll {
    my $default = 0;
    my $cve4609seccnt = 0;
    my @cve4609sechit = ();

    push(@ipchopt, "-P INPUT ACCEPT");
    push(@ipchopt, "-P OUTPUT ACCEPT");
    push(@ipchopt, "-P FORWARD ACCEPT");

#2004.10.15 wbmc 以外で設定したポリシーを削除しない
#    push(@ipchopt, "-F");
#    push(@ipchopt, "-X");

#2004.10.15 wbmc で設定したチェーンのみ削除
#default, ethX_in, vad_in
    for ($i = 0; $i < $g_secifcnt; $i++) {
        $if = $g_secif[$i]->{NAME};
        $chain = $if . "_in";
	if ( !open(CMD,"/sbin/iptables -L|") ){
		exit -1;
	}
	while(<CMD>){
		if ($_ =~ /\s*\Q$chain\E\s*/){
			push(@ipchopt, "-F $chain");
			push(@ipchopt, "-D INPUT -i $if -j $chain");
			push(@ipchopt, "-X $chain");
		}
		if ( $_ =~ /^Chain\s+default/ ) {
			$default = 1;
		}

		if ($_ =~ /ctstate\s+RELATED\,ESTABLISHED/){
                	$cve4609sec[0] = 1;
        	}
        	if ($_ =~ /tcp\s+flags:FIN\/FIN\s+recent:\s+REMOVE\s+name:\s+DEFAULT\s+side:\s+source/){
                	$cve4609sec[1] = 1;
        	}
        	if ($_=~ /recent:\s+SET\s+name:\s+DEFAULT\s+side:\s+source/){
                	$cve4609sec[2] = 1;
        	}
        	if ($_=~  /recent:\s+UPDATE\s+seconds:\s+(.*)\s+hit_count:\s+(.*)\s+name:\s+DEFAULT\s+side:\s+source/)
        	{
                        $cve4609sec[3] = 1;
                        $cve4609sechit[$cve4609seccnt]->{'seconds'} = $1;
                        $cve4609sechit[$cve4609seccnt]->{'hit'} = $2;
                        $cve4609seccnt++;
        	}

	}
	close(CMD);
    }

    $cve4609conter = 0;
    foreach $cvecont(@cve4609sec){
        if ($cvecont == 1)
        {
                $cve4609conter++;
        }
    }
   if ($cve4609conter == 4){
        push(@ipchopt,"-D INPUT -p tcp -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT");
        push(@ipchopt,"-D INPUT -p tcp --tcp-flags FIN FIN -m recent --remove");
        push(@ipchopt,"-D INPUT -p tcp -m recent --set");
        for($iressec = 0;$iressec <  $cve4609seccnt;$iressec++){
                $seconds = $cve4609sechit[$iressec]->{'seconds'};
                $hitcount =  $cve4609sechit[$iressec]->{'hit'};
                push(@ipchopt,"-D INPUT -p tcp -m recent --update --seconds $seconds --hitcount $hitcount -j DROP");
    	}
    }
if ( $default ){
        push(@ipchopt, "-F default");
        push(@ipchopt, "-X default");
    }

    return ;
}

#
# denyAll($flag)
#     $flag  (i) : "on" add all INPUT deny / "off" remove all INPUT deny
#

sub denyAll {
    my($flag) = @_;

    if ($flag eq "on") {
        push(@ipchopt, "-A INPUT -i ! lo -j DROP");
    } else {
        push(@ipchopt, "-D INPUT -i ! lo -j DROP");
    }
}

#
# setForward()
#

sub setForward {
    my($i);

    if ($g_secfw eq $M_SECFW_OFF) {
        return ;
    }

    for ($i = 0; $i < $g_secifcnt; $i++) {
        if ($g_secif[$i]->{MASQ} eq $M_SECIF_MASQ_ON) {
            push(@ipchopt, "-A forward -i $g_secif[$i]->{NAME} -j MASQ");
        }
    }

    return ;
}

#
# setInput()
#

sub setInput {
    my($i, $if, $chain, @chainlist);

    @chainlist = ();

    for ($i = 0; $i < $g_secifcnt; $i++) {
        if ($g_secif[$i]->{FILTERING} eq $M_SECIF_FLT_OFF) {
            next;
        }

        $if = $g_secif[$i]->{NAME};
        $chain = $if . "_in";
        push(@ipchopt, "-N $chain");
        &setIfInput($g_secif[$i]->{FILTER}, $chain); 
        push(@ipchopt, "-A INPUT -i $if -j $chain");
        push(@chainlist, $chain);
    }

    if (@chainlist) {
        push(@ipchopt, "-N default");
        &createDefaultInput("default");
        foreach (@chainlist) {
            push(@ipchopt, "-A $_ -j default");
        }
    }

    return ;
}

#
# createDefaultInput
#

sub createDefaultInput {
    my($chain) = @_;
    my(@type);

    @type = ("echo-reply", "destination-unreachable","source-quench",
             "redirect","router-advertisement","time-exceeded",
             "parameter-problem", "timestamp-reply", "address-mask-reply");

    push(@ipchopt, "-A $chain -m state --state ESTABLISHED,RELATED -j ACCEPT $alog");
    foreach (@type) {
        push(@ipchopt, "-A $chain -p icmp --icmp-type $_ -j ACCEPT $alog");
    }

    push(@ipchopt, "-A $chain -p tcp -j DROP $dlog");
    push(@ipchopt, "-A $chain -p udp -j DROP $dlog");
    push(@ipchopt, "-A $chain -p icmp -j DROP $dlog");

    return ;
}

#
# setIfInput($filter, $chain)
#     $filter :  filter name (eg. "1,2,3")
#     $chain  :  chain name  (eg. "eth0_in")
# 

sub setIfInput {
    my($filter, $chain) = @_;
    my($i, $buf);
    local(@protocol, @dstport, @srcaddr);
    @protocol = ();
    @dstport = ();
    @srcaddr = ();

    &getIfRuleList($filter, *protocol, *dstport, *srcaddr);

    for ($i = 0; $i <= $#protocol; $i++) {
        $buf = "";
        if ($protocol[$i] ne "") {
            $buf .= " -p $protocol[$i]";
        }
        if ($dstport[$i] ne "") {
            if ($protocol[$i] eq "icmp") {
                $buf .= " --icmp-type $dstport[$i]";
            } else {
                $buf .= " --dport $dstport[$i]";
            } 
        }
        if ($srcaddr[$i] ne "") {
            $buf .= " -s $srcaddr[$i]";
        }
        push(@ipchopt, "-A $chain $buf -j ACCEPT $alog");
    }

    return ;
}

#
# getIfRuleList($filter, *protocol, *dstport, *srcaddr)
#     $filter  (i) : filter string (eg. "1,2,3")
#     @protocol(o) : protocol list
#     @dstport (o) : dstport list
#     @srcaddr (o) : srcaddr list
#

sub getIfRuleList {
    local($filter, *protocol, *dstport, *srcaddr) = @_;
    my($i, $idx, $j, $addr, $portstr, $num);

    if ($filter eq $M_SECIF_FLT_NONE) {
        return ;
    }

    @fltent = split(/,/, $filter);

    for ($i = 0; $i <= $#fltent; $i++) {
        $idx = &searchIpchConfDataFlt($fltent[$i]);
        if ($idx == -1 ) {
            next;
        }
        if ($g_secflt[$idx]->{SRCADDR} eq $M_SECFLT_SRCADDR_ALL) {
            $addr = "";
        } else {
            $addr = $g_secflt[$idx]->{SRCADDR};
        }
        $portstr = &makeCompletePort($g_secflt[$idx]->{DSTPORT});
        $num = &getPortList($portstr, *protocol, *dstport);
        for ($j = 0; $j < $num; $j++) {
            push(@srcaddr, $addr);
        }
    }

    return ;
}

# 
# makeCompletePort  (make uncomplete port strings to complete port strings)
# makeCompletePort($port)
#   $port     port strings (eg. "http icmp/8 udp/137,138 udp/100-200 ...")
# return      port strings (eg. "tcp/80 icmp/8 udp/137,138 udp/100-200 ...")
#
sub makeCompletePort {
    my($port) = @_;
    my(@ent);
    my($i, $idx, $ret);

    if ($port eq $M_SECFLT_DSTPORT_ALL) {
        return ("");
    }

    $ret = "";
    @ent = split(/\s+/, $port);

    for ($i = 0; $i <= $#ent; $i++) {
        if ($ent[$i] =~ /([^\/]+)\/(.+)$/) {
            $ret .= " $ent[$i]";
        } else {
            $idx = &searchFltSrvData($ent[$i]);
            if ($idx != -1) {
                $ret .= " $g_secsrv[$idx]->{'PORT'}";
            } else {
                next;
            }
        }
    }

    $ret =~ s/^\s//;

    return ($ret);
}

# 
# getPortList  (make uncomplete port strings to complete protocol & port list)
# getPortList($port, *protolist, *portlist)
#   $port       port strings (eg. "tcp/80 icmp/8 udp/137,138 udp/100-200 ...")
#   @protolist  protocol list
#   @portlist   port list
# return     num
#
sub getPortList
{
    local($port, *protolist, *portlist) = @_;
    my($num);

    if ($port eq "") {
        push(@protolist, "");
        push(@portlist, "");
        return (1);
    }

    $num = 0;

    @ent = split(/\s+/, $port);

    for ($i = 0; $i <= $#ent; $i++) {
        $buf = $ent[$i];
        $buf =~ /([^\/]+)\/(.+)$/;
	$protocol = $1;
	$portbuf = $2;
        @ent1 = split(/,/, $portbuf);
        for ($j = 0; $j <= $#ent1; $j++) {
            push(@protolist, $protocol);
            push(@portlist, $ent1[$j]);
            $num++;
        }
    }

    return ($num);
}

#
# writeIpForward()
#

sub writeIpForward {
    my($value);

    if ($g_secfw eq $M_SECFW_OFF) {
        $value = "0" ;
    } else {
        $value = "1" ;
    }

    system("/bin/echo $value > /proc/sys/net/ipv4/ip_forward");

    return;
}

#
# callIpch()
#

sub callIpch {

    foreach (@ipchopt) {
        system ("/sbin/iptables $_");
   }

    return ;
}

#
#cve20084609脆弱性対処
#

sub cve20084609 {
#/etc/opt/nec/wbmc/ipchains_cur.confファイル読み込み
&readFile("/etc/opt/nec/wbmc/ipchains_cur.conf", "ipchains", *ipchains);

for ($linecount = 0; $linecount <= $#ipchains; $linecount++ ) {
    if ($ipchains[$linecount] =~ /^\s*\#/i) {
        next;
    }
}
$line = $linecount - 1;

#現在のファイルに書き込み
$seconds = -1;
$hitcount = -1;
for($linecount = 0,$cveline = 0 ; $linecount <= $line ; $linecount++){
        if ($ipchains[$linecount] =~ /cve20084609/i) {
                @cveparam = split(/\t/,$ipchains[$cveline]);
                $seconds = $cveparam[1];
                $hitcount = $cveparam[2];
        }
}

#cve20084609設定

$seconds =~ tr/\x0D\x0A//d;
$hitcount =~ tr/\x0D\x0A//d;

if ($seconds != -1 && $hitcount != -1 && ($seconds =~ /^\d+$/) && ($hitcount =~ /^\d+$/)){
	push(@ipchopt, "-A INPUT -p tcp -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT");
	push(@ipchopt, "-A INPUT -p tcp --tcp-flags FIN FIN -m recent --remove");
	push(@ipchopt, "-A INPUT -p tcp -m recent --set");
	push(@ipchopt, "-A INPUT -p tcp -m recent --update --seconds $seconds --hitcount $hitcount -j DROP");
  }
}
