#pragma implementation #include #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 "netconf.h" #include "firewall.h" #include #include #include #include #include "../../paths.h" #include "firewall.m" #include "../../askrunlevel/askrunlevel.h" #include #include #include #ifndef IP_FW_POLICY_IN /* #Specification: firewall / compatibility linuxconf requires a kernel newer than 1.3.66 (or close) to support firewalling. Linuxconf works with older kernels but won't activate firewalling rules. */ /* These are just here so it compiles */ #define IP_FW_APPEND_IN 0 #define IP_FW_APPEND_OUT 0 #define IP_FW_APPEND_FWD 0 #define IP_FW_POLICY_IN 0 #define IP_FW_POLICY_OUT 0 #define IP_FW_POLICY_FWD 0 #define IP_FW_FLUSH_IN 0 #define IP_FW_FLUSH_OUT 0 #define IP_FW_FLUSH_FWD 0 #define FIREWALL_NONE #endif extern HELP_FILE help_ipfw; static HELP_FILE help_control ("firewall","control"); static const char subsys_firewall[]="firewall"; static LINUXCONF_SUBSYS sube (subsys_firewall ,P_MSG_U(M_FIREWALLSYS,"Firewalling rules")); static CONFIG_FILE ip_fwchains (PROC_NET_IP_FWCHAINS,help_ipfw ,CONFIGF_OPTIONAL|CONFIGF_PROBED); static CONFIG_FILE ip_tables_names (PROC_NET_IP_TABLES_NAMES,help_ipfw ,CONFIGF_OPTIONAL|CONFIGF_PROBED); static MESSAGE_DEF firewall_updated ("firewall-updated"); static CONFIG_FILE ip_forward (PROC_NET_IP_FORWARD,help_ipfw ,CONFIGF_OPTIONAL|CONFIGF_PROBED); static CONFIG_FILE ip_block (PROC_NET_IP_BLOCK,help_ipfw ,CONFIGF_OPTIONAL|CONFIGF_PROBED); static CONFIG_FILE ip_input (PROC_NET_IP_INPUT,help_ipfw ,CONFIGF_OPTIONAL|CONFIGF_PROBED); static CONFIG_FILE ip_output (PROC_NET_IP_OUTPUT,help_ipfw ,CONFIGF_OPTIONAL|CONFIGF_PROBED); static CONFIG_FILE ip_acct (PROC_NET_IP_ACCT,help_ipfw ,CONFIGF_OPTIONAL|CONFIGF_PROBED); static CONFIG_FILE f_current_acct (VAR_RUN_FIREWALL_ACCT,help_ipfw ,CONFIGF_GENERATED|CONFIGF_OPTIONAL|CONFIGF_ERASED); static CONFIG_FILE f_current_block (VAR_RUN_FIREWALL_BLOCK,help_ipfw ,CONFIGF_GENERATED|CONFIGF_OPTIONAL|CONFIGF_ERASED); static CONFIG_FILE f_current_forwd (VAR_RUN_FIREWALL_FORWD,help_ipfw ,CONFIGF_GENERATED|CONFIGF_OPTIONAL|CONFIGF_ERASED); static CONFIG_FILE f_current_output (VAR_RUN_FIREWALL_OUTPUT,help_ipfw ,CONFIGF_GENERATED|CONFIGF_OPTIONAL|CONFIGF_ERASED); class IPFW_RULES_FORWARD: public IPFW_RULES{ /*~PROTOBEG~ IPFW_RULES_FORWARD */ public: IPFW_RULES_FORWARD (CONFDB *_db); int disable (bool doit, SSTRING *collect); int enable (bool doit, SSTRING *collect); protected: IPFW_RULE *newrule (const char *pt); IPFW_RULE *newrule (void); public: int save (void); /*~PROTOEND~ IPFW_RULES_FORWARD */ }; class IPFW_RULES_INPUT: public IPFW_RULES{ /*~PROTOBEG~ IPFW_RULES_INPUT */ public: IPFW_RULES_INPUT (CONFDB *_db); int disable (bool doit, SSTRING *collect); int enable (bool doit, SSTRING *collect); protected: IPFW_RULE *newrule (const char *pt); IPFW_RULE *newrule (void); public: int save (void); /*~PROTOEND~ IPFW_RULES_INPUT */ }; class IPFW_RULES_OUTPUT: public IPFW_RULES{ /*~PROTOBEG~ IPFW_RULES_OUTPUT */ public: IPFW_RULES_OUTPUT (CONFDB *_db); int disable (bool doit, SSTRING *collect); int enable (bool doit, SSTRING *collect); protected: IPFW_RULE *newrule (const char *pt); IPFW_RULE *newrule (void); public: int save (void); /*~PROTOEND~ IPFW_RULES_OUTPUT */ }; class IPFW_RULES_ACCT: public IPFW_RULES{ /*~PROTOBEG~ IPFW_RULES_ACCT */ public: IPFW_RULES_ACCT (CONFDB *_db); int disable (bool doit, SSTRING *collect); int enable (bool, SSTRING *); protected: void initrules (void); IPFW_RULE *newrule (const char *pt); IPFW_RULE *newrule (void); public: int save (void); /*~PROTOEND~ IPFW_RULES_ACCT */ }; PUBLIC IPFW_RULE *IPFW_RULES::getitem(int no) { return (IPFW_RULE*)ARRAY::getitem(no); } static char ACTIVEF[] = "activef"; static char ACTIVEB[] = "activeb"; static char ACTIVEO[] = "activeo"; static char ACTIVEA[] = "activea"; PROTECTED void IPFW_RULES::init ( CONFDB *_db, const char *key, const char *key_active) { if (_db == NULL) _db = linuxconf_getdb(); db = _db; SSTRINGS strs; active = db->getvalnum (K_FIREWALL,key_active,0); db->getall (K_FIREWALL,key,strs,0); int nb = strs.getnb(); for (int i=0; iget())); } rstmodified(); } PUBLIC IPFW_RULES_FORWARD::IPFW_RULES_FORWARD(CONFDB *_db) { init (_db,K_FORWARD,ACTIVEF); } PUBLIC IPFW_RULES_INPUT::IPFW_RULES_INPUT(CONFDB *_db) { init (_db,K_BLOCK,ACTIVEB); } PUBLIC IPFW_RULES_OUTPUT::IPFW_RULES_OUTPUT(CONFDB *_db) { init (_db,K_OUTPUT,ACTIVEO); } PUBLIC IPFW_RULES_ACCT::IPFW_RULES_ACCT(CONFDB *_db) { init (_db,K_ACCT,ACTIVEA); } PROTECTED int IPFW_RULES::savek(const char *key, const char *key_active) { int ret = -1; if (perm_rootaccess("update firewalling configuration")){ int nb = getnb(); db->setcursys (subsys_firewall); db->replace (K_FIREWALL,key_active,active); db->removeall(K_FIREWALL,key); for (int i=0; isave(db); } ret = db->save(); } return ret; } PUBLIC int IPFW_RULES_FORWARD::save() { return IPFW_RULES::savek (K_FORWARD,ACTIVEF); } PROTECTED IPFW_RULE *IPFW_RULES_FORWARD::newrule() { return new IPFW_RULE_FORWARD; } PROTECTED IPFW_RULE *IPFW_RULES_FORWARD::newrule(const char *pt) { return new IPFW_RULE_FORWARD (pt); } PUBLIC int IPFW_RULES_INPUT::save() { return IPFW_RULES::savek (K_BLOCK,ACTIVEB); } PROTECTED IPFW_RULE *IPFW_RULES_INPUT::newrule() { return new IPFW_RULE_INPUT; } PROTECTED IPFW_RULE *IPFW_RULES_INPUT::newrule(const char *pt) { return new IPFW_RULE_INPUT (pt); } PUBLIC int IPFW_RULES_OUTPUT::save() { return IPFW_RULES::savek (K_OUTPUT,ACTIVEO); } PROTECTED IPFW_RULE *IPFW_RULES_OUTPUT::newrule() { return new IPFW_RULE_OUTPUT; } PROTECTED IPFW_RULE *IPFW_RULES_OUTPUT::newrule(const char *pt) { return new IPFW_RULE_OUTPUT (pt); } PUBLIC int IPFW_RULES_ACCT::save() { return IPFW_RULES::savek (K_ACCT,ACTIVEA); } PROTECTED IPFW_RULE *IPFW_RULES_ACCT::newrule() { return new IPFW_RULE_ACCT; } PROTECTED IPFW_RULE *IPFW_RULES_ACCT::newrule(const char *pt) { return new IPFW_RULE_ACCT (pt); } /* Tasks done before generating the firewalling rules (Only need for accounting so far) */ PROTECTED VIRTUAL void IPFW_RULES::initrules() { } /* Turn off completly blocking */ PUBLIC int IPFW_RULES_INPUT::disable( bool doit, SSTRING *collect) { // The order is important as the reverse will break connectivity // for a moment. return ipfw_policy (doit,collect,IP_FW_POLICY_IN,IP_FW_F_ACCEPT) == -1 || ipfw_flush(doit,collect,IP_FW_FLUSH_IN) == -1 ? -1 : 0; } /* Turn on blocking */ PUBLIC int IPFW_RULES_INPUT::enable( bool doit, SSTRING *collect) { struct ip_fw bf; ipfw_baseinit (FW_ACCEPT ,"lo","all" ,"127.0.0.1","255.255.255.255","","" ,"127.0.0.1","255.255.255.255","","" ,NULL ,false,false,bf); return ipfw_append (FIRESECT_INTERNAL,-1,doit,collect,IP_FW_APPEND_IN,bf,NULL,NULL) == -1 || ipfw_policy (doit,collect,IP_FW_POLICY_IN,0) == -1 ? -1 : 0; } /* check if the kernel do support blocking */ static int firewall_kernelok() { int ret = 1; static char shutoff = 0; if (firewall_useiptable()){ if (!ip_tables_names.exist()){ net_prtlog (NETLOG_WHY,"Iptables firewalling not installed, loading kernel module\n"); netconf_system_if ("modprobe","ip_tables"); if (!simul_ison() && !ip_tables_names.exist() && !shutoff){ xconf_error (MSG_U(E_KERNELOK24 ,"The kernel does not support iptables firewalling\n" "reconfigure it")); shutoff = 1; } ret = 0; } }else if (!ip_fwchains.exist()){ net_prtlog (NETLOG_WHY,"Ipchain firewalling not installed, loading kernel module\n"); netconf_system_if ("modprobe","ipchains"); if (!simul_ison() && !ip_fwchains.exist() && !shutoff){ xconf_error (MSG_U(E_KERNELOK ,"The kernel does not support ipchain firewalling\n" "reconfigure it")); shutoff = 1; } ret = 0; } return ret; } /* Turn off completly forwarding */ PUBLIC int IPFW_RULES_OUTPUT::disable( bool doit, SSTRING *collect) { return ipfw_policy (doit,collect,IP_FW_POLICY_OUT,IP_FW_F_ACCEPT) == -1 || ipfw_flush(doit,collect,IP_FW_FLUSH_OUT) == -1 ? -1 : 0; } /* Turn on forwarding firewall */ PUBLIC int IPFW_RULES_OUTPUT::enable( bool doit, SSTRING *collect) { /* #Specification: firewall / strategy The blocking and outputing mecanism use a positive logic. Everything is deny at first. Each rule supplied by the user is opening a new hole. The firewalling code of Linux is more general than that. It should be good enough for most firewall and simple for users/admin too. */ struct ip_fw bf; ipfw_baseinit (FW_ACCEPT ,"lo","all" ,"127.0.0.1","255.255.255.255","","" ,"127.0.0.1","255.255.255.255","","" ,NULL ,false,false,bf); return ipfw_append (FIRESECT_INTERNAL,-1,doit,collect,IP_FW_APPEND_OUT,bf,NULL,NULL) == -1 || ipfw_policy (doit,collect,IP_FW_POLICY_OUT,0) == -1 ? -1 : 0; } /* Turn off completly forwarding */ PUBLIC int IPFW_RULES_FORWARD::disable( bool doit, SSTRING *collect) { return ipfw_policy (doit,collect,IP_FW_POLICY_FWD,IP_FW_F_ACCEPT) == -1 || ipfw_flush(doit,collect,IP_FW_FLUSH_FWD) == -1 ? -1 : 0; } /* Turn on forwarding firewall */ PUBLIC int IPFW_RULES_FORWARD::enable( bool doit, SSTRING *collect) { return ipfw_policy (doit,collect,IP_FW_POLICY_FWD,0) == -1 ? -1 : 0; } /* Turn off completly accouting rules */ PUBLIC int IPFW_RULES_ACCT::disable( bool doit, SSTRING *collect) { return ipfw_flush(doit,collect,IP_ACCT_FLUSH) == -1 ? -1 : 0; } /* Turn on accounting firewall */ PUBLIC int IPFW_RULES_ACCT::enable( bool , SSTRING *) { return 0; } /* check if the kernel do support accounting */ PROTECTED void IPFW_RULES_ACCT::initrules() { ipfw_initacct(); } /* Compare by netmask. host netmask will be first, 0.0.0.0 will be last */ static int cmp_by_netmask_from (const ARRAY_OBJ *o1, const ARRAY_OBJ *o2) { IPFW_RULE *r1 = (IPFW_RULE*)o1; IPFW_RULE *r2 = (IPFW_RULE*)o2; int ret = r1->getweight() - r2->getweight(); if (ret == 0) ret = r1->cmp_from (r2); return ret; } /* Compare by netmask. host netmask will be first, 0.0.0.0 will be last */ static int cmp_by_netmask_to (const ARRAY_OBJ *o1, const ARRAY_OBJ *o2) { IPFW_RULE *r1 = (IPFW_RULE*)o1; IPFW_RULE *r2 = (IPFW_RULE*)o2; int ret = r1->getweight() - r2->getweight(); if (ret == 0) ret = r1->cmp_to(r2); return ret; } /* Apply the firewalling rules */ PUBLIC int IPFW_RULES::setup( FIREWALL_SECTION section, bool doit, SSTRING *collect) { int ret = 0; /* #Specification: netconf / firewalling / logic The firewalling strategy is this. When activating one rule set (blocking, forwarding), you are closing the door (policy deny). Each rule you enter open a door. This is less general than using ipfw directly but much simpler and enough for most usage. Comments are more than welcome about this. While setting the rules, we set the policy to accept. We set it to deny at the end of the rule sequence. This prevent some interruption in the networking while installing the rules. If we would have start by denying everything, we could have add all kind of network timeout while trying to set the rules. Quite often, setting rules involve DNS access. */ for (int i=0; irulenum = i; r->sectnum = section; } if (active){ if (disable(doit,collect)==-1){ ret = -1; }else{ ret = 0; int nbr = getnb(); int i; SSTRING errmsg; // First, try to lookup the various information, to avoid // doing that too often FWINFO_API *tbapi[MAX_API_PROVIDERS]; int nbapi = fwinfo_apis_init ("IPFWRULES::setup",tbapi); bool some_excluded = false; for (i=0; iactive){ if (r->solve(r->from,tbapi,nbapi)==-1 || r->solve(r->to,tbapi,nbapi)==-1){ r->solved_valid = false; if (collect != NULL){ if (!some_excluded){ some_excluded = true; collect->appendf ("%s\n" ,MSG_U(I_EXCLRULE,"Excluded: Some information are missing to process those rules")); } char buf1[10],buf2[100]; r->present (buf1,sizeof(buf1),buf2,sizeof(buf2)); collect->appendf ("\t%02d: %s\n",r->rulenum+1,buf2); } }else{ r->solved_valid = true; } } } if (some_excluded && collect != NULL) collect->append ("\n"); fwinfo_apis_end (tbapi,nbapi); ipfw_heading(collect); // Now, generate the rules from "from" to "to" sort (cmp_by_netmask_from); if (doit) initrules(); for (i=0; iactive && r->solved_valid){ ret |= r->setup_left(doit,collect,errmsg); } } // Now, generate the rules from "to" to "from" if (ret == 0){ sort (cmp_by_netmask_to); for (i=0; iactive && r->solved_valid && r->is_bidir()){ ret |= r->setup_right(doit,collect,errmsg); } } } if (ret == 0){ ret = enable (doit,collect); }else{ xconf_error (MSG_U(E_IPFWDISABLE ,"Some error has occured, firewall not activated\n\n%s") ,errmsg.get()); net_prtlog (NETLOG_ERR,MSG_R(E_IPFWDISABLE)); } } } return ret; } #ifndef FIREWALL_NONE static int firewall_setup ( FIREWALL_SECTION section, IPFW_RULES &rules, CONFIG_FILE &f_current, bool &msgdone, const char *chain) { int ret = 0; SSTRING collect; if (rules.setup(section,false,&collect) != -1){ SSTRING current; /* #Specification: firewalling / update needed Probing the firewalling information to find out if the current configuration is already configured in the kernel is not easy. A trick is used here. All the command generated to configured the kernel are saved in the files /var/run/firewall.{acct,block,forwd}. When probing the firewall setup, we generate the command in a string and compare that string with the content of the file. If there is any mismatch, the complete firewalling sequence is reprogrammend in the kernel and the string is saved in /var/run/firewall.state. Normally, this file is erased by linuxconf (askrunlevel) at boot time (so is logically empty). Sometime, askrunlevel is not used, so the file lives accross reboot. To make sure linuxconf does not get confused by that, we check that the file is newer than boottime. */ time_t boottime = time(NULL) - sys_uptime(); if (f_current.getdate() > boottime){ FILE_CFG *fin = f_current.fopen ("r"); if (fin != NULL){ char buf[1000]; while (fgets(buf,sizeof(buf)-1,fin)!=NULL){ current.append (buf); } fclose (fin); } } if (current.cmp(collect)!=0){ if(collect.is_empty()){ // No rules needed but the firewall must be desactivated rules.disable(0,&collect); if (!simul_ison()){ f_current.unlink(); rules.disable(1,NULL); } }else{ if (!simul_ison()){ ret = rules.setup(section,true,NULL); if (ret != -1){ FILE_CFG *fout = f_current.fopen ("w"); if (fout != NULL){ fputs (collect.get(),fout); fclose (fout); } } } } if (!msgdone){ net_prtlog (NETLOG_WHY,MSG_U(I_FWCHANGED ,"Firewall state does not match configuration\n")); msgdone = true; } net_prtlog ("%s",collect.get()); }else{ chains_confirm (chain); } } return ret; } #endif static const char *tbmodname[]={ "ip_masq_cuseeme", "ip_masq_ftp", "ip_masq_irc", "ip_masq_raudio", "ip_masq_quake", "ip_masq_vdolive", "ip_masq_autofw", "ip_masq_mfw", "ip_masq_portfw", "ip_masq_user", "ip_masq_ipsec", "ip_masq_pptp", }; #define NBMOD (sizeof(tbmodname)/sizeof(tbmodname[0])) static const char K_FWSPCMOD[]="FWSPCMOD"; static const char K_IRCPORTS[]="IRCPORTS"; static const char K_OTHERKERNMOD[]="otherkernelmod"; static const char K_SMARTUPDATES[]="smartupdates"; static const char K_USEIPCHAINS[]="useipchains"; /* Install the firewalling rules in the kernel. Return -1 if any errors */ int firewall_setup(CONFDB *db) { int ret = -1; IPFW_RULES_FORWARD frules(db); IPFW_RULES_INPUT brules(db); IPFW_RULES_OUTPUT orules(db); IPFW_RULES_ACCT arules(db); if (frules.active || brules.active || orules.active || arules.active){ #ifdef FIREWALL_NONE xconf_error (MSG_U(E_FWOLDKERN ,"Linuxconf was compiled on an old kernel and does not\n" "support firewalling properly. No rule will be activated.")); #else if (perm_rootaccess("activating the firewalling configuration")){ bool msgdone = false; if (firewall_kernelok() != -1 && ipfw_open() != -1 && firewall_setup (FIRESECT_FORWARD,frules,f_current_forwd,msgdone,"forward") != -1 && firewall_setup (FIRESECT_INPUT,brules,f_current_block,msgdone,"input") != -1 && firewall_setup (FIRESECT_OUTPUT,orules,f_current_output,msgdone,"output") != -1 && firewall_setup (FIRESECT_ACCT,arules,f_current_acct,msgdone,"acct") != -1){ ipfw_close(); if (!simul_ison()) module_sendmessage (firewall_updated); // Load the module if needed for (unsigned i=0; iget(); SSTRING mod; mod.copyword (s); if (!module_is_loaded(mod.get())){ netconf_system_if ("modprobe",s); } } ret = 0; } } #endif } return ret; } /* Reset the firewalling rules in the kernel. Return -1 if any errors */ int firewall_reset(CONFDB *db) { int ret = -1; if (perm_rootaccess("disabling the firewalling configuration") && !simul_isdemo()){ IPFW_RULES_FORWARD frules(db); IPFW_RULES_INPUT brules(db); IPFW_RULES_OUTPUT orules(db); IPFW_RULES_ACCT arules(db); f_current_forwd.unlink(); f_current_block.unlink(); f_current_output.unlink(); f_current_acct.unlink(); chains_resetfw(); if (ipfw_open() != -1 && frules.disable(1,NULL) != -1 && brules.disable(1,NULL) != -1 && orules.disable(1,NULL) != -1 && arules.disable(1,NULL) != -1){ ipfw_close(); ret = 0; } } return ret; } static int firewall_chkirc (SSTRING &val) { int ret = 0; if (!val.is_empty()){ char buf[300]; char *dst = buf; const char *src = val.get(); enum { digit, comma} state=comma; while (ret == 0 && *src != '\0'){ if (isdigit(*src)){ if (state==comma){ int port = atoi(src); if (port < 1024 || port > 65000){ ret = -1; break; } while (isdigit(*src)) *dst++ = *src++; state = digit; }else{ ret = -1; } }else if (isspace(*src)){ src++; }else if (*src == ','){ if (state == digit){ state = comma; *dst++ = *src++; }else{ ret = -1; } }else{ ret = -1; } } *dst = '\0'; val.setfrom (buf); } return ret; } /* Return true if the firewall uses smart updates */ bool firewall_smartupdates() { return linuxconf_getvalnum (K_FIREWALL,K_SMARTUPDATES,0) != 0; } /* Return true if the firewall must be configured with ipchains */ bool firewall_useipchains() { return linuxconf_getvalnum (K_FIREWALL,K_USEIPCHAINS,0) != 0; } /* Return true if the firewall may be configured with iptables */ bool firewall_useiptable () { return kernel_newer (2,4,0) && !firewall_useipchains(); } /* Edit the default configuration of the firewall */ void firewall_editc(CONFDB *db) { IPFW_RULES_FORWARD frules(db); IPFW_RULES_INPUT brules(db); IPFW_RULES_OUTPUT orules(db); IPFW_RULES_ACCT arules(db); DIALOG dia; dia.newf_title (MSG_U(T_RULESCTL,"Rules control"),1,"",MSG_R(T_RULESCTL)); dia.newf_chk (MSG_U(F_BRULES,"Inputing rules"),brules.active ,MSG_U(I_AREACTIVE,"are active")); dia.newf_chk (MSG_U(F_FRULES,"forwarding rules"),frules.active ,MSG_R(I_AREACTIVE)); dia.newf_chk (MSG_U(F_ORULES,"outputing rules"),orules.active ,MSG_R(I_AREACTIVE)); dia.newf_chk (MSG_U(F_ARULES,"accounting rules"),arules.active ,MSG_R(I_AREACTIVE)); dia.newf_title (MSG_U(T_KERNMOD,"Special kernel modules"),1,"" ,MSG_R(T_KERNMOD)); struct modinfo{ const char *title; const char *key; }; static const char *tbt[]={ MSG_U(F_MOD_CUSEEME,"CuSeeMe"), MSG_U(F_MOD_FTP,"Ftp"), MSG_U(F_MOD_IRC,"Irc"), MSG_U(F_MOD_RAUDIO,"Real Audio"), MSG_U(F_MOD_QUAKE,"Quake"), MSG_U(F_MOD_VDOLIVE,"Vdolive"), MSG_U(F_MOD_AUTOFW,"Auto Forwarding"), MSG_U(F_MOD_MFW,"Firewall Mark Forwarding"), MSG_U(F_MOD_PORTFW,"Port Forwarding"), MSG_U(F_MOD_USER,"Space Control"), MSG_U(F_MOD_IPSEC,"IpSec VPN support"), MSG_U(F_MOD_PPTP,"PPTP VPN support"), }; char tbmod[NBMOD]; SSTRING ircports; ircports.setfrom(linuxconf_getval (K_FWSPCMOD,K_IRCPORTS)); int f_irc = 0; for (unsigned i=0; i