#include #include #include #include #include #include #include #ifdef __GLIBC__ #include #include #include #include #include #include "k2.0/ip_fw.h" #else #include #include #include #include #include #include #include "k2.0/ip_fw.h" #endif #include #include #include #include #include "firewall.h" #include "firewall.m" #include #include #include #ifndef IP_FW_POLICY_IN /* These are just here so it compiles */ #define IP_FW_POLICY_IN 0 #define IP_FW_FLUSH_IN 1 #define IP_FW_APPEND_IN 2 #define IP_FW_POLICY_OUT 3 #define IP_FW_FLUSH_OUT 4 #define IP_FW_F_ACCEPT 5 #define IP_FW_F_REDIR 6 #define IP_FW_F_ICMPRPL 7 #define IP_FW_F_KIND 8 #define FIREWALL_NONE #endif extern HELP_FILE help_ipfw; static CONFIG_FILE f_script_ipchain ("/var/run/ipcmd.sh",help_ipfw ,CONFIGF_OPTIONAL|CONFIGF_GENERATED ,"root","root",0700); static FILE_CFG *ipchain_fout = NULL; static bool ipchain_somecmds = false; static const char *ipcmdpath = NULL; static const char *flushchains = NULL; static const char *KW_INPUT="input"; static const char *KW_FORWARD="forward"; static const char *KW_OUTPUT="output"; static const char *KW_ACCEPT="ACCEPT"; static const char *KW_DENY="DENY"; static const char *KW_REJECT="REJECT"; static const char *KW_IN_INTERFACE="--interface"; static const char *KW_OUT_INTERFACE="--interface"; static const char *KW_SPORT=""; static const char *KW_DPORT=""; static const char *KW_MASQ="MASQ"; static const char *KW_POSTROUTING="POSTROUTING"; static const char *KW_PREROUTING="PREROUTING"; static const char *KW_DNAT="DNAT"; static const char *KW_REDIRECT="REDIRECT"; static const char *KW_RETURN="RETURN"; static bool use_tables = false; static const char *KW_FILTER="filter"; static const char *KW_NAT="nat"; static const char *ipfw_table=KW_FILTER; /* Locate the ipchain command and return its path */ const char *ipfw_setipcmdpath() { if (ipcmdpath == NULL){ if (firewall_useiptable ()){ DAEMON_INTERNAL *dae = daemon_find ("iptables"); if (dae != NULL){ ipcmdpath = dae->getpath(); }else{ ipcmdpath = "/sbin/iptables"; } KW_INPUT="INPUT"; KW_FORWARD="FORWARD"; KW_OUTPUT="OUTPUT"; KW_DENY="DROP"; KW_IN_INTERFACE="--in-interface"; KW_OUT_INTERFACE="--out-interface"; KW_SPORT="--sport"; KW_DPORT="--dport"; KW_MASQ="MASQUERADE"; use_tables = true; }else{ DAEMON_INTERNAL *dae = daemon_find ("ipchains"); if (dae != NULL){ ipcmdpath = dae->getpath(); }else{ ipcmdpath = "/sbin/ipchains"; } } DAEMON_INTERNAL *dae = daemon_find ("firewall-flushchains"); if (dae != NULL){ flushchains = dae->getpath(); }else{ flushchains = "/usr/lib/linuxconf/lib/flushchains"; } } return ipcmdpath; } /* Add an ipchain command line to /var/run/ipchains.sh The line does not include the path of the ipchains command. */ void ipfw_appendcmd (const char *ctl, ...) { if (ipchain_fout != NULL){ ipfw_setipcmdpath(); char buf[1000]; va_list list; va_start (list,ctl); if (use_tables){ sprintf (buf,"%s -t %s",ipcmdpath,ipfw_table); }else{ strcpy (buf,ipcmdpath); } int len = strlen(buf); buf[len++] = ' '; ipchain_somecmds = true; vsnprintf (buf+len,sizeof(buf)-1-len,ctl,list); va_end (list); fprintf (ipchain_fout,"%s\n",buf); } } static int sockfd = -1; static const char *ipfw_u2a ( unsigned long nip, // Network IP number char *buf) // Will receive the Ascii IP number { int num4[4]; nip = ntohl(nip); num4[0] = (nip >> 24 ) & 0xff; num4[1] = (nip >> 16) & 0xff; num4[2] = (nip >> 8) & 0xff; num4[3] = nip & 0xff; sprintf (buf,"%d.%d.%d.%d",num4[0],num4[1],num4[2],num4[3]); return buf; } static const char *ipfw_u2a_not ( unsigned long nip, // Network IP number bool notflag, char *buf) // Will receive the Ascii IP number { char *ret = buf; if (notflag) *buf++ = '!'; ipfw_u2a (nip,buf); return ret; } void ipfw_heading( SSTRING *collect) { if (collect != NULL){ char buffer[300]; sprintf(buffer,"%s %5s %15s/%-15s -> %15s/%-15s %-15s %-15s\n" ,"cmd","proto" ,"network","netmask" ,"network","netmask" ,"iface" ,"ports ... flags"); collect->append (buffer); } } static const char *ipfw_getport (int port, const char *proto, char *buf) { struct servent *ent = getservbyport (ntohs(port),proto); if (ent == NULL){ sprintf (buf,"%d",port); }else{ strcpy (buf,ent->s_name); } return buf; } struct IPCHAIN_INFO{ const char *proto_cmd; const char *proto; const char *verb; const char *chain; char iface_verb[100]; const char *extra; }; /* Check if a string contains a valid netmask. The string may end with a '\0' or a comma. */ bool ipfw_validmask (const char *msk, unsigned long &bmsk) { char tmp[strlen(msk)+1]; strcpy (tmp,msk); char *pt = strchr(tmp,','); if (pt != NULL) *pt = '\0'; bmsk = 0xffffffff; bool ret = ipnum_validmask (tmp); if (ret){ bmsk = ipnum_aip2l (tmp); } return ret; } static int nomark; // mark id for ipchain rules static void ipfw_formatcmd ( FIREWALL_SECTION sectnum, int rulenum, const char *dispatch, struct ip_fw &b, IPCHAIN_INFO &inf, const char *source_port, const char *dest_port) { const char *interface = b.fw_vianame; char b1[LEN_IP_ASC],b2[LEN_IP_ASC],b3[LEN_IP_ASC],b4[LEN_IP_ASC]; char jump_verb[1000]; jump_verb[0] = '\0'; if (inf.verb != NULL){ snprintf (jump_verb,sizeof(jump_verb)-1,"--jump %s",inf.verb); } char cmd[1000]; snprintf (cmd,sizeof(cmd)-1,"%s%s %s %s --source %s%s/%s %s %s%s" " --destination %s%s/%s %s %s%s %s %s" ,(b.fw_flg & IP_FW_F_PRN) ? "--log " : "" ,(b.fw_flg & IP_FW_F_TCPSYN) ? "--syn " : "" ,jump_verb,inf.proto_cmd ,b.not_src ? "! " : "" ,ipfw_u2a(b.fw_src.s_addr,b1),ipfw_u2a(b.fw_smsk.s_addr,b2) ,source_port[0] != '\0' ? KW_SPORT : "" ,b.not_srcport ? "! " : "" ,source_port ,b.not_dst ? "! " : "" ,ipfw_u2a(b.fw_dst.s_addr,b3),ipfw_u2a(b.fw_dmsk.s_addr,b4) ,dest_port[0] != '\0' ? KW_DPORT : "" ,b.not_dstport ? "! " : "" ,dest_port ,inf.iface_verb ,inf.extra); strip_end (cmd); // In smart update mode, we compare commands // and trailing blanks may fool us bool noorder = b.fw_dmsk.s_addr == 0xffffffffl && b.fw_smsk.s_addr == 0xffffffffl; chains_format (sectnum,rulenum,dispatch,inf.chain ,inf.proto ,b.fw_src.s_addr,b.fw_smsk.s_addr,source_port ,b.fw_dst.s_addr,b.fw_dmsk.s_addr,dest_port ,interface,cmd,noorder); } static void ipfw_formatdstports ( FIREWALL_SECTION sectnum, int rulenum, const char *dispatch, struct ip_fw &b, IPCHAIN_INFO &inf, const char *source_port) { int ndp = b.dst_ports.nb; if (b.dst_ports.from != 0){ char dest_port[100]; snprintf(dest_port,sizeof(dest_port)-1, "%u:%u" ,b.dst_ports.from,b.dst_ports.to); ipfw_formatcmd (sectnum,rulenum,dispatch,b,inf,source_port,dest_port); } for (int d=0; d %16s/%-15s %-15s" ,verb,proto ,ipfw_u2a_not(b.fw_src.s_addr,b.not_src,b1),ipfw_u2a(b.fw_smsk.s_addr,b2) ,ipfw_u2a_not(b.fw_dst.s_addr,b.not_dst,b3),ipfw_u2a(b.fw_dmsk.s_addr,b4) ,iface); //len+=sprintf(buffer+len,"%u %u %-9lu %-9lu" // ,b.fw_nsp,b.fw_ndp, b.fw_pcnt,b.fw_bcnt); len += sprintf (buffer+len," ["); int nsp = b.src_ports.nb; if (b.src_ports.from != 0){ len+=sprintf(buffer+len, " %u:%u",b.src_ports.from,b.src_ports.to); } for (int s=0; s ["); int ndp = b.dst_ports.nb; if (b.dst_ports.from != 0){ len+=sprintf(buffer+len, " %u:%u",b.dst_ports.from ,b.dst_ports.to); } for (int d=0; d %s" ,ipfw_getport(b.redir_port,proto,bufp)); } //len += sprintf(buffer+len, " A%02X X%02X", b.fw_tosand, b.fw_tosxor); if (b.fw_flg & IP_FW_F_PRN){ len += sprintf (buffer+len," logging"); } strcpy (buffer+len,"\n"); if (dispatch != NULL && dispatch[0] != '\0'){ collect->appendf ("%s Dispatch %s",buffer,dispatch); }else{ collect->append (buffer); } #endif } if (doit){ if (kernel_newer (2,2,0)){ ipfw_setipcmdpath(); char redir_verb[100]; int mark = nomark; IPCHAIN_INFO inf; ipfw_table = KW_FILTER; inf.proto_cmd = ""; inf.proto = b.protocol; inf.extra = ""; SSTRING proto_cmd; if (strcasecmp(b.protocol,"all")!=0){ proto_cmd.setfromf ("--proto %s",b.protocol); inf.proto_cmd = proto_cmd.get(); } inf.verb = KW_DENY; inf.chain = NULL; const char *interopt = NULL; if (command == IP_FW_APPEND_IN){ inf.chain = KW_INPUT; interopt = KW_IN_INTERFACE; }else if (command == IP_FW_APPEND_OUT){ inf.chain = KW_OUTPUT; interopt = KW_OUT_INTERFACE; }else if (command == IP_FW_APPEND_FWD){ inf.chain = KW_FORWARD; interopt = KW_OUT_INTERFACE; }else{ // Accounting rules, no jump needed inf.chain = "acct"; inf.verb = use_tables ? KW_RETURN : NULL; } int loop = 1; const char *next_verb = NULL; const char *next_chain = NULL; const char *next_extra = ""; if (b.fw_flg & IP_FW_F_MASQ){ if (use_tables){ // Append the rule in the filter table // and in the nat table loop = 2; next_chain = KW_POSTROUTING; next_verb = KW_MASQ; inf.verb = KW_ACCEPT; }else{ inf.verb = KW_MASQ; } }else if (b.fw_flg & IP_FW_F_REDIR){ if (use_tables){ int rport = b.redir_port; if (redirhost == NULL){ snprintf (redir_verb,sizeof(redir_verb)-1,"--to-ports %d" ,rport); next_verb = KW_REDIRECT; next_chain = KW_PREROUTING; }else{ next_verb = KW_DNAT; next_chain = KW_PREROUTING; snprintf (redir_verb,sizeof(redir_verb)-1,"--to-destination %s:%d" ,redirhost,rport); } loop = 2; inf.verb = KW_ACCEPT; next_extra = redir_verb; }else{ if (redirhost == NULL){ char bufp[100]; ipfw_getport(b.redir_port,inf.proto,bufp); snprintf (redir_verb,sizeof(redir_verb)-1,"%s %s" ,KW_REDIRECT,bufp); }else{ snprintf (redir_verb,sizeof(redir_verb)-1,"ACCEPT --mark %d" ,mark); nomark++; } inf.verb = redir_verb; } }else if (b.fw_flg & IP_FW_F_ACCEPT){ inf.verb = KW_ACCEPT; }else if (b.fw_flg & IP_FW_F_ICMPRPL){ inf.verb = KW_REJECT; } inf.iface_verb[0] = '\0'; if (b.fw_vianame[0] != '\0'){ sprintf (inf.iface_verb,"%s %s",interopt,b.fw_vianame); }else if (b.fw_via.s_addr != 0){ char iface[LEN_IP_ASC]; ipfw_u2a (b.fw_via.s_addr,iface); sprintf (inf.iface_verb,"%s %s",interopt,iface); } for (int i=0; igetpath(); }else{ ipmasqadmpath = "/usr/sbin/ipmasqadm"; } } if (mark == 1) fprintf (ipchain_fout,"%s mfw -F\n",ipmasqadmpath); fprintf (ipchain_fout,"%s mfw -A -m %d -r %s %d\n" ,ipmasqadmpath,mark,redirhost ,b.redir_port); } ret = 0; }else{ ret = setsockopt (sockfd, IPPROTO_IP, command ,&b,sizeof(struct ip_fw)); if (ret != 0){ xconf_error ("error append firewall %d(%s)",errno,strerror(errno)); } } } return ret; } /* Remove one list (input,forward,output) of firewalling rule from the kernel */ int ipfw_flush ( bool doit, SSTRING *, int command) { int ret = 0; if (doit){ if (kernel_newer (2,2,0)){ ipfw_setipcmdpath(); if (command == IP_ACCT_FLUSH){ ipfw_appendcmd ("--delete %s --jump acct --source 0.0.0.0/0.0.0.0 --destination 0.0.0.0/0.0.0.0 2>/dev/null" ,KW_INPUT); ipfw_appendcmd ("--flush acct 2>/dev/null"); ipfw_appendcmd ("--delete-chain acct 2>/dev/null"); }else{ const char *chain = NULL; if (command == IP_FW_FLUSH_IN){ chain = KW_INPUT; if (use_tables){ ipfw_table = KW_NAT; ipfw_appendcmd ("--flush %s",KW_PREROUTING); ipfw_table = KW_FILTER; } }else if (command == IP_FW_FLUSH_OUT){ chain = KW_OUTPUT; }else if (command == IP_FW_FLUSH_FWD){ chain = KW_FORWARD; if (use_tables){ ipfw_table = KW_NAT; ipfw_appendcmd ("--flush %s",KW_POSTROUTING); ipfw_table = KW_FILTER; } }else{ assert(0); } ipfw_appendcmd ("--flush %s",chain); if (ipchain_fout != NULL){ fprintf (ipchain_fout,"%s %s %c-\n" ,flushchains,ipcmdpath,toupper(chain[0])); } } ret = 0; }else{ int data = 0; ret = setsockopt (sockfd, IPPROTO_IP, command,&data,sizeof(data)); if (ret != 0){ xconf_error ("error flush firewall %d(%s)",errno,strerror(errno)); } } } return ret; } /* Create the chain for accounting rules. They are stored in input rules. */ void ipfw_initacct () { if (kernel_newer (2,2,0)){ ipfw_appendcmd ("--new-chain acct"); // Insert at the start of the input chain ipfw_appendcmd ("--insert %s --jump acct --source 0.0.0.0/0.0.0.0 --destination 0.0.0.0/0.0.0.0" ,KW_INPUT); if (use_tables){ ipfw_appendcmd ("--policy acct %s",KW_RETURN); } } } int ipfw_policy ( bool doit, SSTRING *collect, int command, int policy) { int ret = 0; ipfw_setipcmdpath(); if (collect != NULL){ char *ctl = NULL; switch (command){ case IP_FW_POLICY_IN: ctl = "IP firewall input rules, default %s\n"; break; case IP_FW_POLICY_OUT: ctl = "IP firewall output rules, default %s\n"; break; case IP_FW_POLICY_FWD: ctl = "IP firewall forward rules, default %s\n"; break; } char buf[100]; const char *pstr = KW_DENY; if (policy == IP_FW_F_ACCEPT){ pstr = KW_ACCEPT; } sprintf(buf,ctl,pstr); collect->append (buf); } if (doit){ if (kernel_newer (2,2,0)){ ipfw_table = KW_FILTER; const char *chain = NULL; if (command == IP_FW_POLICY_IN){ chain = KW_INPUT; }else if (command == IP_FW_POLICY_OUT){ chain = KW_OUTPUT; }else if (command == IP_FW_POLICY_FWD){ chain = KW_FORWARD; }else{ // No policy for accounting rules assert (0); } const char *policy_str = NULL; if (policy == IP_FW_F_ACCEPT){ policy_str = KW_ACCEPT; }else if (policy == 0){ policy_str = KW_DENY; }else{ assert (0); } ipfw_appendcmd ("--policy %s %s",chain,policy_str); ret = 0; }else{ ret = setsockopt (sockfd, IPPROTO_IP, command,&policy,sizeof(policy)); } } return ret; } /* Initialise the sockfd needed to program the rules Return -1 if any error. */ int ipfw_open () { int ret = -1; nomark=1; chains_reset(); if (kernel_newer (2,2,0)){ ipchain_somecmds = false; ipchain_fout = f_script_ipchain.fopen ("w"); if(ipchain_fout != NULL){ fputs ("#!/bin/sh\n",ipchain_fout); ret = 0; } }else{ sockfd = socket(AF_INET, SOCK_RAW, IPPROTO_RAW); ret = sockfd; } return ret; } void ipfw_close() { if (kernel_newer (2,2,0)){ if (ipchain_fout != NULL){ fputs ("exit 0\n",ipchain_fout); fclose (ipchain_fout); ipchain_fout = NULL; if (ipchain_somecmds){ if (chains_write()==-1){ netconf_system (60,f_script_ipchain.getpath()); } } chains_reset(); } }else{ close (sockfd); sockfd = -1; } } #ifndef FIREWALL_NONE /* Translate and validate an ASCII IP addr. */ static int ipfw_a2ip (const char *adr, struct in_addr &ina) { int num4[4]; int ret = ipnum_aip24 (adr,num4); if (ret != -1){ unsigned long ipa = (num4[0] <<24) | (num4[1] << 16) | (num4[2] << 8) | num4[3]; ina.s_addr = htonl (ipa); } return ret; } static int ipfw_setrange ( const char *range, struct ip_fw_ports &bf) { int ret = 0; bf.from = bf.to = 0; const char *s = str_skip (range); if (s[0] != '\0'){ bf.from = atoi(s); s = str_skipdig (s); s = str_skip (s); if (*s == ':'){ s++; s = str_skip (s); bf.to = atoi(s); if (bf.to < bf.from){ ret = -1; } }else{ ret = -1; } } return ret; } /* Validation for the dialog Return -1 if any errors. */ int ipfw_checkrange (const SSTRING &range) { struct ip_fw_ports bf; return ipfw_setrange (range.get(),bf); } /* Convert a port into numeric form */ static int ipfw_setport ( const char *portstr, unsigned short &port, const char *proto) { int ret = 0; struct servent *serv = getservbyname (portstr,proto); if (serv != NULL){ port = ntohs(serv->s_port); }else if (str_checkdig(portstr)){ port = atoi(portstr); }else{ ret = -1; } return ret; } static int ipfw_setports ( const char *ports, struct ip_fw_ports &bf, const char *proto) { int ret = 0; if (strcmp(proto,"udp")!=0 && strcmp(proto,"tcp")!=0){ ports = str_skip(ports); if (ports[0] != '\0'){ ret = -1; } }else{ int noport = 0; while (1){ ports = str_skip (ports); if (ports[0] == '\0'){ break; }else{ bool not_flag = false; if (ports[0] == '!'){ not_flag = true; ports = str_skip(ports+1); } char word[200]; ports = str_copyword(word,ports,sizeof(word)); unsigned short port; ret = ipfw_setport (word,port,proto); if (ret != 0) break; bf.tb[noport].not_flag = not_flag; bf.tb[noport].port = port; noport++; } } bf.nb = noport; } return ret; } /* Validation for the dialog Return -1 if any errors. */ int ipfw_checkports ( const SSTRING &ports, const SSTRING &proto) { struct ip_fw_ports bf; return ipfw_setports (ports.get(),bf,proto.get()); } /* Check if it is a valid protocol, or all. Accept the quotation mark as well, to mean (not this protocol) Return -1 if any error. */ int ipfw_checkproto (const char *protocol) { int ret = -1; if (strcasecmp(protocol,"all")==0){ ret = 0; }else{ if (protocol[0] == '!') protocol = str_skip(protocol+1); if (getprotobyname(protocol) != NULL){ ret = 0; } } return ret; } /* Return -1 if anything is invalid */ int ipfw_baseinit ( int policy, const char *iface, const char *protocol, const char *ip_src, const char *msk_src, const char *sport_range, const char *sports, const char *ip_dst, const char *msk_dst, const char *dport_range, const char *dports, const char *redirport, bool logging, bool match_syn, struct ip_fw &bf) { memset (&bf,0,sizeof(bf)); int ret = ipfw_a2ip (ip_src,bf.fw_src); ret |= ipfw_a2ip (msk_src,bf.fw_smsk); bf.fw_src.s_addr &= bf.fw_smsk.s_addr; ret |= ipfw_a2ip (ip_dst,bf.fw_dst); ret |= ipfw_a2ip (msk_dst,bf.fw_dmsk); bf.fw_dst.s_addr &= bf.fw_dmsk.s_addr; /* #Specification: firewall / iface / assumption We assume that all network device do begin with a letter. This way we differentiate IP number for interface from name */ if (iface[0] == '!' || isalpha(iface[0])){ strcpy (bf.fw_vianame,iface); }else{ ret |= ipfw_a2ip (iface,bf.fw_via); } if (ipfw_checkproto(protocol)!=-1){ strcpy_cut (bf.protocol,protocol,sizeof(bf.protocol)); }else{ ret = -1; } // if (ret == -1) fprintf (stderr,"ipfw_baseinit1 protocol %s error\n",protocol); ret |= ipfw_setrange (sport_range,bf.src_ports); ret |= ipfw_setports (sports,bf.src_ports,protocol); // if (ret == -1) fprintf (stderr,"ipfw_baseinit2 protocol %s error\n",protocol); ret |= ipfw_setrange (dport_range,bf.dst_ports); ret |= ipfw_setports (dports,bf.dst_ports,protocol); // if (ret == -1) fprintf (stderr,"ipfw_baseinit22 protocol %s error\n",protocol); //bf.fw_tosand = 0xFF; //bf.fw_tosxor = 0x00; if (policy == FW_ACCEPT){ bf.fw_flg |= IP_FW_F_ACCEPT; }else if (policy == FW_REJECT){ bf.fw_flg |= IP_FW_F_ICMPRPL; } if (redirport != NULL){ bf.fw_flg |= IP_FW_F_REDIR; ret |= ipfw_setport (redirport,bf.redir_port,protocol); } // if (ret == -1) fprintf (stderr,"ipfw_baseinit3 protocol %s error\n",protocol); if (logging) bf.fw_flg |= IP_FW_F_PRN; if (match_syn) bf.fw_flg |= IP_FW_F_TCPSYN; // if (ret == -1) fprintf (stderr,"ipfw_baseinit4 protocol %s error\n",protocol); return ret; } #else int ipfw_checkports ( const SSTRING &, const SSTRING &) { return 0; } int ipfw_checkrange (const SSTRING &) { return 0; } int ipfw_checkproto (const char *protocol) { return 0; } int ipfw_baseinit ( int, const char *, const char *, const char *, const char *, const char *, const char *, const char *, const char *, const char *, const char *, const char *, bool, bool, struct ip_fw &) { return -1; } #endif