/* New configuration files for the firewall module. This module is quite old. It was using configurations stored into /etc/conf.linuxconf. This configuration was -unreadable: One line holding a set of position dependant (no tags) configurations. -No concept of inheritance or shared definition. This was creating a lot of duplication in the rules. The new format is introducing new ideas to enhance hand editing -/etc/firewall.d/ *.conf: Many configuration file. You can drop one as part of a package for example. -Easy to read, with a concept of shared definition -You define a source and destination -Then you define multiple rules associated with the previous definition. Currently, we only support reading the new format. Linuxconf can't edit or update it. The module only reads the new files and map new configuration files to the old format. The rest of the module can continue to operate unchanged. */ #include #include #include #include #include #include "firewall.h" #include "firewall.m" #include "rules.h" using namespace std; extern HELP_FILE help_ipfw; static CONFIG_FILE f_dir ("/etc/firewall.d",help_ipfw,CONFIGF_NONE); static void config_replace ( const char *src, SSTRING &dst, const map ¯os, const char *errprefix, SSTRING &error) { dst = src; src = dst.c_str(); while (1){ const char *pt = strstr(src,"$("); if (pt == NULL) break; const char *end = strchr(pt+2,')'); if (end == NULL) break; SSTRING name; name.setfrom (pt+2,(end-pt)-2); map::const_iterator it = macros.find(name); if (it == macros.end()){ error.appendf ("%s: %s",MSG_U(E_IVLDMAC,"Invalid macro name: %s") ,name.c_str()); break; }else{ SSTRING tmp; tmp.setfrom (src,pt-src); tmp.append (it->second.c_str()); tmp.append (end+1); dst = tmp; src = dst.c_str(); } } } #define K_BIDIR "bidir" #define K_GO "go" #define K_LOGGING "logging" #define K_ORDER "order" #define K_PROTO "proto" #define K_POLICY "policy" #define K_REDIRPORT "redirport" #define K_REDIRHOST "redirhost" #define K_IPTABLE "iptable" #define K_FROM_ADDR "from.addr" #define K_FROM_NETMASK "from.netmask" #define K_FROM_IFACE "from.iface" #define K_FROM_PORTS "from.ports" #define K_FROM_RANGE "from.range" #define K_FROM_SYNOK "from.synok" #define K_FROM_DISPATCH "from.dispatch" #define K_TO_ADDR "to.addr" #define K_TO_NETMASK "to.netmask" #define K_TO_IFACE "to.iface" #define K_TO_PORTS "to.ports" #define K_TO_RANGE "to.range" #define K_TO_SYNOK "to.synok" #define K_TO_DISPATCH "to.dispatch" static const char *tbkeyw[]={ //K_BIDIR, K_GO, K_LOGGING, K_ORDER, K_POLICY, K_PROTO, K_REDIRPORT, K_REDIRHOST, K_FROM_ADDR, K_FROM_NETMASK, K_FROM_IFACE, K_FROM_PORTS, K_FROM_RANGE, K_FROM_SYNOK, K_FROM_DISPATCH, K_TO_ADDR, K_TO_NETMASK, K_TO_IFACE, K_TO_PORTS, K_TO_RANGE, K_TO_SYNOK, K_TO_DISPATCH, K_IPTABLE, NULL }; static bool config_istrue (const SSTRING &s) { return s.icmp("true")==0 || s.icmp("yes")==0; } static void config_fillcommon( IPFW_RULEP *rule, bool reverse, map &conf) { if (reverse){ rule->setto ( conf[K_FROM_ADDR].c_str(),conf[K_FROM_NETMASK].c_str() ,conf[K_FROM_RANGE].c_str(),conf[K_FROM_PORTS].c_str() ,conf[K_FROM_IFACE].c_str(),config_istrue(conf[K_FROM_SYNOK]) ,conf[K_FROM_DISPATCH].c_str()); rule->setfrom ( conf[K_TO_ADDR].c_str(),conf[K_TO_NETMASK].c_str() ,conf[K_TO_RANGE].c_str(),conf[K_TO_PORTS].c_str() ,conf[K_TO_IFACE].c_str(),config_istrue(conf[K_TO_SYNOK]) ,conf[K_TO_DISPATCH].c_str()); }else{ rule->setfrom ( conf[K_FROM_ADDR].c_str(),conf[K_FROM_NETMASK].c_str() ,conf[K_FROM_RANGE].c_str(),conf[K_FROM_PORTS].c_str() ,conf[K_FROM_IFACE].c_str(),config_istrue(conf[K_FROM_SYNOK]) ,conf[K_FROM_DISPATCH].c_str()); rule->setto ( conf[K_TO_ADDR].c_str(),conf[K_TO_NETMASK].c_str() ,conf[K_TO_RANGE].c_str(),conf[K_TO_PORTS].c_str() ,conf[K_TO_IFACE].c_str(),config_istrue(conf[K_TO_SYNOK]) ,conf[K_TO_DISPATCH].c_str()); } rule->setproto (conf[K_PROTO].c_str()); rule->setbidir (false); // config_istrue(conf[K_BIDIR])); rule->setlogging (config_istrue(conf[K_LOGGING])); rule->setweight (conf[K_ORDER].getval()); rule->setproto (conf[K_PROTO].c_str()); rule->setiptable (conf[K_IPTABLE].c_str()); int policy = FW_ACCEPT; SSTRING &pol = conf[K_POLICY]; if (pol.icmp("accept")==0){ policy = FW_ACCEPT; }else if (pol.icmp("reject")==0){ policy = FW_REJECT; }else if (pol.icmp("deny")==0){ policy = FW_DENY; } rule->setpolicy (policy); } /* We map the configuration to the different chain (input,output,...) based on the "go" directive. */ static void config_dispatch ( const SSTRING &go, map &conf, IPFW_RULES_INPUT &brules, IPFW_RULES_FORWARD &frules, IPFW_RULES_OUTPUT &orules, const char *errprefix, SSTRING &error) { SSTRINGS words; int n = str_splitline (go.c_str(),' ',words); for (int i=0; ic_str(); bool reverse = false; if (word[0] == '!'){ reverse = true; word++; } if (strcmp(word,"input")==0){ IPFW_RULE_INPUT *inp = new IPFW_RULE_INPUT; config_fillcommon(inp,reverse,conf); SSTRING &rhost = conf[K_REDIRHOST]; SSTRING &rport = conf[K_REDIRPORT]; inp->setredir (rhost.is_filled(),rhost.c_str(),rport.c_str()); brules.add(inp); }else if (strcmp(word,"forward")==0){ IPFW_RULE_FORWARD *inp = new IPFW_RULE_FORWARD; config_fillcommon(inp,reverse,conf); frules.add(inp); }else if (strcmp(word,"output")==0){ IPFW_RULE_OUTPUT *inp = new IPFW_RULE_OUTPUT; config_fillcommon(inp,reverse,conf); orules.add(inp); }else{ error.appendf (MSG_U(E_IVLDRULE ,"%s Invalid go directive %s\n") ,errprefix,word); } } } /* Do some static validation of the input fields */ static void config_validate ( const SSTRING &name, const SSTRING &val, const SSTRING &errprefix, SSTRING &error) { if ((name == K_FROM_RANGE || name == K_TO_RANGE) && val.is_filled()){ const char *pt = val.c_str(); int nbcol = 0; bool first = isdigit(*pt); while (*pt != '\0'){ if (*pt == ':'){ nbcol++; }else if (!isdigit(*pt)){ break; } pt++; } if (*pt != '\0' || nbcol != 1 || !first){ error.appendf (MSG_U(E_RANGE ,"%s Invalid range specification (NNN:NNN): %s\n") ,errprefix.c_str(),val.c_str()); } }else if (name == K_POLICY && val.is_filled()){ if (val.icmp("accept")!=0 && val.icmp("reject")!=0 && val.icmp("deny")!=0){ error.appendf (MSG_U(E_POLICY ,"%s Invalid policy: %s\n") ,errprefix.c_str(),val.c_str()); } } } /* Load the firewall settings in the old format. */ int config_loadold () { glocal int ret = 0; glocal map macros; glocal map oneconf; glocal SSTRING error; glocal IPFW_RULES_FORWARD frules; glocal IPFW_RULES_INPUT brules; glocal IPFW_RULES_OUTPUT orules; glocal set keyws; glocal stack > confst; for (int i=0; tbkeyw[i] != NULL; i++) glocal.keyws.insert (tbkeyw[i]); (f_dir.getpath()); const char *pt = strstr(path,".conf"); if (pt != NULL && pt[5] == '\0'){ int ret = (path,true); SSTRING tmpline; const char *comment = strchr(line,'#'); if (comment != NULL){ tmpline.setfrom (line,comment-line); tmpline.strip_end(); line = tmpline.c_str(); } const char *pt = str_skip(line); if (strcmp(pt,"{")==0){ glocal.confst.push(glocal.oneconf); }else if (strcmp(pt,"}")==0){ glocal.oneconf = glocal.confst.top(); glocal.confst.pop(); }else if (*pt != '\0'){ const char *start = pt; while (isalpha(*pt) || isdigit(*pt) || *pt == '.' || *pt == '_') pt++; SSTRING name; name.setfrom (start,pt-start); pt = str_skip(pt); if (*pt == ':'){ // This is a definition pt = str_skip(pt+1); SSTRING tmp; SSTRING errprefix; errprefix.setfromf (MSG_U(E_LINECONTEXT ,"File %s, line %d:") ,info.filename,noline+1); config_replace (pt,tmp,glocal.macros ,errprefix.c_str(),glocal.error); // printf ("LINE %s: %s\n",name.c_str(),tmp.c_str()); if (glocal.keyws.count(name)==0){ glocal.error.appendf (MSG_U(E_IVLDKEYW ,"%s Invalid keyword %s\n") ,errprefix.c_str(),name.c_str()); }else if (name == K_GO){ printf ("GO: %s\n",tmp.c_str()); for (map::iterator it = glocal.oneconf.begin(); it != glocal.oneconf.end(); it++){ if (it->second.is_filled()){ printf ("\t%s: %s\n",it->first.c_str() ,it->second.c_str()); } } if (tmp.is_empty()){ glocal.error.appendf (MSG_U(E_NOGO ,"%s Missing GO argument\n") ,errprefix.c_str()); }else{ config_dispatch (tmp ,glocal.oneconf ,glocal.brules ,glocal.frules,glocal.orules ,errprefix.c_str(),glocal.error); } }else{ config_validate(name,tmp,errprefix,glocal.error); glocal.oneconf[name] = tmp; } }else if (*pt == '='){ // This is a macro pt = str_skip(pt+1); glocal.macros[name] = pt; }else{ glocal.error.appendf (MSG_U(E_CONFIG ,"File %s, line %d, invalid token %c: %s\n") ,info.filename,noline+1,*pt,line); } } return 0; if (ret == -1) glocal.ret = -1; } if (glocal.error.is_filled()){ xconf_error (MSG_U(E_LOADOLDCONFIG ,"There are some errors in the configuration files\n\n%s") ,glocal.error.c_str()); glocal.ret = -1; }else{ IPFW_RULES_ACCT arules; // Filler, not used anymore. glocal.ret = firewall_setup (glocal.brules,glocal.frules ,glocal.orules,arules); } return glocal.ret; }