#include #include #include #include "firewall.h" #include "firewall.m" #include #include extern HELP_FILE help_ipfw; static CONFIG_FILE f_chains ("/var/run/firewall.chains",help_ipfw ,CONFIGF_OPTIONAL|CONFIGF_GENERATED|CONFIGF_ERASED ,"root","root",0600); static CONFIG_FILE f_script_ipchain_smart ("/var/run/ipchain_smart.sh",help_ipfw ,CONFIGF_OPTIONAL|CONFIGF_GENERATED ,"root","root",0700); class CHAIN: public ARRAY_OBJ{ public: SSTRING dispatch; char name[12]; bool no_order; // Rules inside may be entered in any order bool is_new; // This is a new chain not already in the kernel SSTRINGS rules; SSTRINGS old_rules; bool referenced; // Was there a jump to this rule CHAIN *next; // Link list for hash access int pos; // Index in CHAINS /*~PROTOBEG~ CHAIN */ public: CHAIN (const char *_name, const char *_dispatch); void addcmd (const char *cmd, bool _no_order); void addoldcmd (const char *cmd); void confirm (void); /*~PROTOEND~ CHAIN */ }; PUBLIC CHAIN::CHAIN (const char *_name, const char *_dispatch) { strcpy (name,_name); dispatch.setfrom(_dispatch); no_order = true; is_new = true; referenced = false; next = NULL; pos = -1; } /* Copy all the old rules in the new one */ PUBLIC void CHAIN::confirm() { rules.remove_all(); rules.append (old_rules); } /* Add a firewall rule in this chain */ PUBLIC void CHAIN::addcmd (const char *cmd, bool _no_order) { if (!_no_order) no_order = false; rules.add(new SSTRING(cmd)); } /* Add a firewall rule currently in the kernel in this chain */ PUBLIC void CHAIN::addoldcmd (const char *cmd) { old_rules.add(new SSTRING(cmd)); } class CHAINS: public ARRAY{ CHAIN *idx[512]; bool fail; /*~PROTOBEG~ CHAINS */ public: CHAINS (void); void add (CHAIN *c); CHAIN *getitem (const char *dispatch)const; CHAIN *getitem (int no)const; void loadold (void); int lookup (const char *dispatch)const; void reset (void); void setfail (void); int updkernel (void); /*~PROTOEND~ CHAINS */ }; PUBLIC CHAINS::CHAINS() { reset(); } PUBLIC CHAIN *CHAINS::getitem(int no) const { return (CHAIN*)ARRAY::getitem(no); } static int chains_hash (const char *pt) { int hash = 0; int pos = 0; while (*pt != '\0'){ hash += *pt++ ^ pos++; } return hash & 0x1f; } PUBLIC void CHAINS::add (CHAIN *c) { c->pos = getnb(); ARRAY::add (c); int hash = chains_hash (c->dispatch.get()); c->next = idx[hash]; idx[hash] = c; // fprintf (stderr,"insert %s hash %d\n",c->dispatch.get(),hash); } /* Locate a CHAIN from its dispatch definition */ PUBLIC CHAIN *CHAINS::getitem(const char *dispatch) const { CHAIN *ret = NULL; int no = lookup(dispatch); if (no != -1) ret = getitem(no); return ret; } //static int nbsearch=0; //static int longuest=0; /* Locate a CHAIN from its dispatch definition */ PUBLIC int CHAINS::lookup(const char *dispatch) const { int ret = -1; int hash = chains_hash (dispatch); CHAIN *c = idx[hash]; //nbsearch++; //int len = 0; while (c != NULL){ //fprintf (stderr,"Compare %d %s %s\n",hash,dispatch,c->dispatch.get()); //len++; if (c->dispatch.cmp(dispatch)==0){ //if (len > longuest) longuest = len; ret = c->pos; break; } c = c->next; } return ret; } PUBLIC void CHAINS::setfail() { fail = true; } /* Forget all chains et insert the 3 default one */ PUBLIC void CHAINS::reset() { memset (idx,0,sizeof(idx)); remove_all(); fail = false; } /* Load Forget all chains et insert the 3 default one */ PUBLIC void CHAINS::loadold() { CHAINS *t; CHAIN *cur; glocal.t = this; glocal.cur = NULL; remove_all(); fail = false; if (f_chains.exist()){ (f_chains.getpath(),true); if (line[0] > ' '){ char dispatch[200],chain[9]; int no_order; if (sscanf (line,"%s %s %d",dispatch,chain,&no_order)==3){ glocal.cur = new CHAIN (chain,dispatch); glocal.cur->is_new = false; glocal.cur->no_order = no_order != 0; glocal.t->add (glocal.cur); }else{ glocal.t->setfail(); } }else{ glocal.cur->addoldcmd (str_skip(line)); } return 0; }else{ reset(); fail = true; } } PUBLIC int CHAINS::updkernel() { int ret = -1; if (!fail){ const char *ipcmd_path = ipfw_setipcmdpath(); FILE_CFG *fout = f_script_ipchain_smart.fopen ("w"); if (fout != NULL){ fprintf (fout,"#!/bin/sh\n"); int n = getnb(); for (int i=0; iis_new){ fprintf (fout,"%s -N %s\n",ipcmd_path,c->name); } } for (int i=0; iname,c->rules.getnb(),c->old_rules.getnb(),c->no_order,c->is_new); if (c->is_new){ lost = true; }else if (c->rules.getnb() != c->old_rules.getnb() && !c->no_order){ // Ok, the two chain set differs and order is important // we will have to clear the chain et redo it lost = true; }else{ // Compare the two command set. We assume that they are // mostly the same with some insertions and some deletions. // Whenever we find a difference, we fall in replace mode int n_nb = c->rules.getnb(); int o_nb = c->old_rules.getnb(); int nb = n_nb < o_nb ? n_nb : o_nb; bool differ = false; for (int j=0; jrules.getitem(j)->get(); const char *o_s = c->old_rules.getitem(j)->get(); if (differ || strcmp(n_s,o_s)!=0){ // if (strcmp(n_s,o_s)!=0){ // fprintf (stderr,"Chaine differe :%s\n",n_s); // fprintf (stderr," :%s\n",o_s); // } differ = true; fprintf (fout," %s --replace %s %d %s\n" ,ipcmd_path,c->name,j+1,n_s); } } if (n_nb > o_nb){ // We simply add the new rules for (int j=o_nb; jrules.getitem(j)->get(); fprintf (fout," %s --append %s %s\n" ,ipcmd_path,c->name,n_s); } }else if (o_nb > n_nb){ // We simply remove the extra old rules // We delete from the end so our index are valid for (int j=o_nb; j>n_nb; j--){ fprintf (fout," %s --delete %s %d\n" ,ipcmd_path,c->name,j); } } } if (lost){ fprintf (fout,"%s --flush %s\n",ipcmd_path,c->name); for (int j=0; jrules.getnb(); j++){ fprintf (fout," %s --append %s %s\n",ipcmd_path ,c->name,c->rules.getitem(j)->get()); } } } // Installs the default policies static const char *tbc[]={"input","forward","output"}; for (int i=0; i<3; i++){ if (getitem(tbc[i])!=NULL){ fprintf (fout,"%s --policy %s DENY\n" ,ipcmd_path,tbc[i]); } } fclose (fout); ret = netconf_system (60,f_script_ipchain_smart.getpath()); } } return ret; } static CHAINS chains; /* Forget all chains */ void chains_reset() { //fprintf (stderr,"stats %d %d\n",nbsearch,longuest); chains.loadold(); } /* Write the layout of the various chains to /var/run/firewall.chains and try to update the firewall gracefully. Return -1 if it failed, or if smart updates is disabled */ int chains_write() { int ret = -1; // We always write the chain layout because it is informative FILE_CFG *fout = f_chains.fopen ("w"); if (fout != NULL){ for (int i=0; idispatch.get(),c->name ,c->no_order ? 1 : 0); for (int j=0; jrules.getnb(); j++){ fprintf (fout,"\t%s\n",c->rules.getitem(j)->get()); } } fclose (fout); if (firewall_smartupdates()){ ret = chains.updkernel(); } } return ret; } int chains_format ( FIREWALL_SECTION sectnum, int rulenum, const char *dispatch, // Logic to dispatch in sub-chains const char *chain, // Normal firewall: input, forward, output const char *proto, // protocol unsigned long from, // From address of the rule unsigned long from_mask,// From netmask const char *fromport, // source port or "" unsigned long to, // Target address of the rule unsigned long to_mask, // To netmask const char *toport, // destination port or "" const char *interface, const char *cmd, // ipchain command to output bool no_order) // This rule may be place anywhere in the chain // (well this is true if all rules in the chain // have this attribute) { int ret = 0; const char letter = toupper(chain[0]); char buf_chain[200]; char *ptbuf = stpcpy (buf_chain,chain); CHAIN *last_chain = chains.getitem(chain); if (last_chain == NULL){ last_chain = new CHAIN(chain,chain); last_chain->is_new = false; chains.add (last_chain); //fprintf (stderr,"last_chain == NULL ???\n"); //ret = -1; } { to = ntohl (to); from = ntohl (from); to_mask = ntohl (to_mask); from_mask = ntohl (from_mask); if (dispatch == NULL) dispatch = ""; while (*dispatch != '\0'){ char ruledisp[200]; *ptbuf++ = '-'; const char *start = dispatch; while (*dispatch != ',' && *dispatch != '\0') dispatch++; int len = (int)(dispatch-start); if (len == 5 && strncmp(start,"proto",5)==0){ if (strcasecmp(proto,"all")==0){ xconf_error (MSG_U(E_DISPATCHALL,"Can't dispatch for protocol ALL")); ret = -1; }else{ ptbuf = stpcpy (ptbuf,proto); sprintf (ruledisp,"--proto %s",proto); } }else if (len == 8 && strncmp(start,"fromport",8)==0){ if (strcasecmp(proto,"udp")!=0 && strcasecmp(proto,"tcp")!=0){ xconf_error (MSG_U(E_NOPORT,"Can't dispatch by port for protocol %s"),proto); ret = -1; }else{ ptbuf += sprintf (ptbuf,"fromport=%s",fromport); sprintf (ruledisp,"--proto %s --source 0.0.0.0/0 %s",proto,fromport); } }else if (len == 6 && strncmp(start,"toport",6)==0){ if (strcasecmp(proto,"udp")!=0 && strcasecmp(proto,"tcp")!=0){ xconf_error (MSG_R(E_NOPORT),proto); ret = -1; }else{ ptbuf += sprintf (ptbuf,"toport=%s",toport); sprintf (ruledisp,"--proto %s --destination 0.0.0.0/0 %s",proto,toport); } }else if (len == 4 && strncmp(start,"from",4)==0){ char fromstr[LEN_IP_ASC],frommsk[LEN_IP_ASC]; ipnum_ip2a (from,fromstr); ipnum_ip2a (from_mask,frommsk); ptbuf += sprintf (ptbuf,"from=%s/%s",fromstr,frommsk); sprintf (ruledisp,"--source %s/%s",fromstr,frommsk); }else if (len == 2 && strncmp(start,"to",2)==0){ char tostr[LEN_IP_ASC],tomsk[LEN_IP_ASC]; ipnum_ip2a (to,tostr); ipnum_ip2a (to_mask,tomsk); ptbuf += sprintf (ptbuf,"to=%s/%s",tostr,tomsk); sprintf (ruledisp,"--destination %s/%s",tostr,tomsk); }else if (strncmp(start,"to/",3)==0){ unsigned long msk; if (ipfw_validmask(start+3,msk)){ unsigned long net = to & msk; char tostr[LEN_IP_ASC],tomsk[LEN_IP_ASC]; ipnum_ip2a (net,tostr); ipnum_ip2a (msk,tomsk); ptbuf += sprintf (ptbuf,"to=%s/%s",tostr,tomsk); sprintf (ruledisp,"--destination %s/%s",tostr,tomsk); }else{ xconf_error (MSG_U(E_IVLDNETSPEC,"Invalid network specification: %s"),start); ret = -1; } }else if (strncmp(start,"from/",5)==0){ unsigned long msk; if (ipfw_validmask(start+5,msk)){ unsigned long net = from & msk; char fromstr[LEN_IP_ASC],frommsk[LEN_IP_ASC]; ipnum_ip2a (net,fromstr); ipnum_ip2a (msk,frommsk); ptbuf += sprintf (ptbuf,"from=%s/%s",fromstr,frommsk); sprintf (ruledisp,"--source %s/%s",fromstr,frommsk); }else{ xconf_error (MSG_R(E_IVLDNETSPEC),start); ret = -1; } }else if (strncmp(start,"iface",len)==0){ if (interface[0] != '\0' && strcasecmp(interface,"any")!=0){ ptbuf += sprintf (ptbuf,"iface=%s",interface); sprintf (ruledisp,"--interface %s",interface); }else{ xconf_error (MSG_U(E_NOINTERFACE,"No interface specified, can't dispatch")); ret = -1; } }else{ ret = -1; xconf_error (MSG_U(E_IVLDTOKEN ,"Invalid dispatch token %*.*s in section %s, rule %d\n" "Chain dispatch will be ignored") ,len,len,start,ipfwrule_getsect(sectnum),rulenum); break; } if (*dispatch == ',') dispatch++; *ptbuf = '\0'; // At this point, new_chain contains a descriptive chain name // which express the full path of a chain. We check if we have // already created this chain. If not, we create one with // a short name since the kernel only accept 8 character names. int nochain = chains.lookup(buf_chain); CHAIN *new_chain; if (nochain==-1){ nochain = chains.getnb(); char chain_name[9]; sprintf (chain_name,"%c-%05d",letter,nochain); new_chain = new CHAIN(chain_name,buf_chain); chains.add (new_chain); }else{ new_chain = chains.getitem(nochain); } if (!new_chain->referenced){ ipfw_appendcmd ("-N %s",new_chain->name); char dispcmd[250]; snprintf (dispcmd,sizeof(dispcmd)-1,"%s --jump %s",ruledisp ,new_chain->name); ipfw_appendcmd ("--append %s %s",last_chain->name,dispcmd); last_chain->addcmd (dispcmd,true); new_chain->referenced = true; } last_chain = new_chain; } cmd = str_skip(cmd); ipfw_appendcmd ("--append %s %s",last_chain->name,cmd); last_chain->addcmd (cmd,no_order); } return ret; } /* Validate a firewall chain dispatch string. Return -1 if any error */ int chains_validdispatch ( const char *dispatch, // Logic to dispatch in sub-chains const char *proto, // protocol const char *interface) { chains.reset(); return chains_format (FIRESECT_INTERNAL,0,dispatch,"input",proto,0,0,"" ,0,0,"",interface,"",0); } /* Forget the chain configuration in the kernel */ void chains_resetfw() { f_chains.unlink(); } /* Tells the smart-updates system that all chains of a given type should be preserved (input,forward,output). This function is called when we find out that nothing has to be done for a given rule set */ void chains_confirm (const char *chain) { { CHAIN *c = chains.getitem(chain); if (c != NULL) c->confirm(); } char letter = toupper (chain[0]); int n = chains.getnb(); for (int i=0; iname[0] == letter) c->confirm(); } }