module(..., package.seeall) -- Load libraries require("posix") require("json") require("modelfunctions") processinfo = require("acf.processinfo") fs = require("acf.fs") --validator = require("acf.validator") -- Set variables local packagename = "awall" local etcpath = "/etc/awall/" local etcoptionalpath = "/etc/awall/optional/" local usrpath = "/usr/share/awall/mandatory/" local usroptionalpath = "/usr/share/awall/optional/" local path = "PATH=/usr/local/bin:/usr/bin:/bin:/usr/local/sbin:/usr/sbin:/sbin " -- ################################################################################ -- LOCAL FUNCTIONS local findfiles = function(where) local files = {} for d in posix.files(where) do if string.find(d, "%.json$") then if fs.is_file(where .. d) and string.find(d, "%.json$") then files[#files+1] = where..d elseif fs.is_link(where .. d) then -- awall will use links, but we want to drop the links from /etc/awall/ to one of the optional dirs -- because those are the ones handled by enable/disable local link = posix.dirname(posix.readlink(where..d) or "").."/" logevent("link = "..link) if where == etcpath and (string.find(link, "^"..etcoptionalpath.."$") or string.find(link, "^"..usroptionalpath.."$")) then -- do nothing else files[#files+1] = where..d end end end end return files end local validatefiledetails = function(filedetails) local success = true local res, err = pcall(function() local jsonoutput, count = json.decode(filedetails.value.filecontent.value) end) if not res and err then success = false end if not success then filedetails.value.filecontent.errtxt = "Invalid json code\n"..(err or "") end return success, filedetails end local validateeditablefile = function(filename) -- get/setfiledetails only work for files (not links), this prevents editing/deleting the enable links local dir = posix.dirname(filename or "").."/" if fs.is_file(filename) and string.find(filename, "%.json$") and (dir==etcpath or dir==etcoptionalpath) then return true else return false end end -- ################################################################################ -- PUBLIC FUNCTIONS function getstatus() local status = {} local value, errtxt = processinfo.package_version(packagename) status.version = cfe({ label="Program version", value=value, errtxt=errtxt, name=packagename }) return cfe({ type="group", value=status, label="AWall Status" }) end function get_startstop(self, clientdata) local actions = {"Verify", "Translate", "Activate"} return cfe({ type="group", label="Management", value={}, option=actions }) end function startstop_service(self, startstop, action) if not action then startstop.errtxt = "Invalid Action" else local cmd if action == "Verify" then cmd = path.."awall translate -V 2>&1" elseif action == "Translate" then cmd = path.."awall translate 2>&1" else cmd = path.."awall activate -f 2>&1" end local f = io.popen(cmd) startstop.descr = f:read("*a") f:close() end return startstop end function list_policies() -- core-router disabled BSN core router local policies = {} local reversepolicies = {} local errtxt local cmd = path.."awall list 2>&1" local f = io.popen(cmd) for l in f:lines() do local a,b,c = string.match(l, "(%S+)%s+(%S+)%s*(.*)") if a and a == "/usr/bin/lua:" then errtxt = b.." "..c break end if a then policies[#policies+1] = {name=a, status=b, description=c} reversepolicies[a] = #policies end end f:close() -- Since awall seems to crash (and not give results) when there is any error -- Let's show the actual files too local addfiles = function(files, editable, mandatory) for i,d in ipairs(files) do local name = string.match(d, "([^/]*)%.json$") if reversepolicies[name] then policies[reversepolicies[name]].filename = d policies[reversepolicies[name]].editable = editable if mandatory then policies[reversepolicies[name]].status = "mandatory" end else policies[#policies+1] = {name=name, filename=d, editable=editable} if mandatory then policies[#policies].status = "mandatory" end reversepolicies[name] = #policies end end end local files = findfiles(usroptionalpath) addfiles(files, false, false) files = findfiles(etcoptionalpath) addfiles(files, true, false) files = findfiles(usrpath) addfiles(files, false, true) files = findfiles(etcpath) addfiles(files, true, true) table.sort(policies, function(a,b) return a.name < b.name end) return cfe({ type="list", value=policies, label="Policies", errtxt=errtxt }) end function get_newpolicy() local newpolicy = {} newpolicy.name = cfe({ label="Name", seq=1 }) newpolicy.optional = cfe({ type="boolean", label="Optional", seq=2 }) return cfe({ type="group", value=newpolicy, label="New Policy" }) end function create_policy(self, newpolicy) local success = true local name = newpolicy.value.name.value if name == "" then newpolicy.value.name.errtxt = "Invalid name" success = false else local policies = list_policies() for i,pol in ipairs(policies.value) do if pol.name == newpolicy.value.name.value then newpolicy.value.name.errtxt = "Name already exists" success = false break end end end if success then local path if newpolicy.value.optional.value then path = etcoptionalpath else path = etcpath end path = path..name..".json" if posix.stat(path) then newpolicy.value.name.errtxt = path.." already exists" success = false else fs.write_file(path, "{}") end end if not success then newpolicy.errtxt = "Failed to create policy file" end return newpolicy end function get_delete_policy(self, clientdata) retval = {} retval.filename = cfe({ value=clientdata.filename or "", label="File Name" }) return cfe({ type="group", value=retval, label="Delete Policy File" }) end function delete_policy(self, delpolicy) if validateeditablefile(delpolicy.value.filename.value) then os.remove(delpolicy.value.filename.value) else delpolicy.errtxt = "Failed to delete policy" delpolicy.value.filename.errtxt="Policy not found" end return delpolicy end function read_policyfile(self, clientdata) -- Can read from all 4 locations return modelfunctions.getfiledetails(clientdata.filename, function(filename) local dir = posix.dirname(filename or "").."/" if string.find(filename, "%.json$") and (dir==etcpath or dir==etcoptionalpath or dir==usrpath or dir==usroptionalpath) then return true else return false end end) end function get_policyfile(self, clientdata) -- Can only get (for editing) from /etc/ locations return modelfunctions.getfiledetails(clientdata.filename, validateeditablefile) end function update_policyfile(self, filedetails) return modelfunctions.setfiledetails(self, filedetails, validateeditablefile, validatefiledetails) end function get_enablepolicy(self, clientdata) local policy = {} policy.name = cfe({ value=clientdata.name or "", label="Name", seq=1 }) return cfe({ type="group", value=policy, label="Policy" }) end function enable_policy(self, enable) local cmd = path.."awall enable "..format.escapespecialcharacters(enable.value.name.value).." 2>&1" local f = io.popen(cmd) local result = f:read("*a") f:close() if result ~= "" then enable.errtxt = result end return enable end function disable_policy(self, disable) local cmd = path.."awall disable "..format.escapespecialcharacters(disable.value.name.value).." 2>&1" local f = io.popen(cmd) local result = f:read("*a") f:close() if result ~= "" then disable.errtxt = result end return disable end