local mymodule = {} -- Load libraries posix = require("posix") modelfunctions = require("modelfunctions") fs = require("acf.fs") format = require("acf.format") validator = require("acf.validator") -- Set variables local confdfile = "/etc/conf.d/racoon" local configfile = "/etc/racoon/racoon.conf" local configfile2 = "/etc/ipsec.conf" local processname = "racoon" local packagename = "ipsec-tools" local baseurl = "/etc/racoon/" local descr = { state={ ['9']="Established", }, side={ ['R']="We are 'Responder'.", ['I']="We 'Initiated' this phase1", }, exchange={ ['M']="Main mode", ['A']="Agressive mode", ['B']="Basic mode", }, } -- ################################################################################ -- LOCAL FUNCTIONS local function racoonctl_table() local output = {} local value = modelfunctions.run_executable({"ip", "xfrm", "state"}) -- Get rid of all lines that don't start with "src" local phase2details = string.gsub(value, "\n[^s][^\n]*", "") value = modelfunctions.run_executable({"racoonctl", "-lll", "show-sa", "isakmp"}) for i,line in pairs(format.string_to_table(value,"\n")) do if not ((string.find(line,"^Source")) or (#line == 0)) then entry={} local variable=format.string_to_table(line,"%s+") entry['Source']=cfe({ label="Source", value=variable[1], }) entry['Destination']=cfe({ label="Destination", value=variable[2], }) entry['Cookies']=cfe({ label="Cookies", value=variable[3], }) entry['St']=cfe({ label="State", value=variable[4], descr=descr.state[variable[4]], }) entry['S']=cfe({ label="Side", value=variable[5], descr=descr.side[variable[5]], }) entry['V']=cfe({ label="Version", value=variable[6], }) entry['E']=cfe({ label="Exchange", value=variable[7], descr=descr.exchange[variable[7]], }) entry['Created']=cfe({ label="Created", value=(variable[8] or "") .. " " .. (variable[9] or ""), }) local dst = string.match(variable[2],"^(.*)%.") -- Removes the portnumber local incoming = {} local outgoing = {} for l in string.gmatch(phase2details, "src [^\n]* "..dst.."\n") do incoming[#incoming+1] = l end for l in string.gmatch(phase2details, "src "..dst.." [^\n]*\n") do outgoing[#outgoing+1] = l end local phase2s = {{label="Outgoing", value=table.concat(outgoing)}, {label="Incoming", value=table.concat(incoming)}} entry['Phase2']=cfe({ label="Phase2", value=variable[10], option=phase2s, }) entry['Phase2details']=cfe({ label="Phase2details", value=phase2s[1]['value'] .. phase2s[2]['value'] }) output[#output + 1] = entry end end return output end -- ################################################################################ -- PUBLIC FUNCTIONS function mymodule.get_startstop(self, clientdata) return modelfunctions.get_startstop(processname) end function mymodule.startstop_service(self, startstop, action) return modelfunctions.startstop_service(startstop, action) end function mymodule.getstatus() return modelfunctions.getstatus(processname, packagename, "Racoon Status") end function mymodule.getstatusdetails() local status = {} status.show_isakmp = cfe({ type="structure", value=racoonctl_table(), label="Tunnels" }) status.ip_xfrm_policy = cfe({ type="longtext", label="ip xfrm policy" }) status.ip_xfrm_policy.value, status.ip_xfrm_policy.errtxt = modelfunctions.run_executable({"ip", "xfrm", "policy"}) return cfe({ type="group", value=status, label="Racoon Status Details" }) end function mymodule.get_racoonfiledetails() return modelfunctions.getfiledetails(configfile) end function mymodule.update_racoonfiledetails(self, filedetails) return modelfunctions.setfiledetails(self, filedetails, {configfile}) end function mymodule.get_ipsecfiledetails() return modelfunctions.getfiledetails(configfile2) end function mymodule.update_ipsecfiledetails(self, filedetails) return modelfunctions.setfiledetails(self, filedetails, {configfile2}) end function mymodule.list_certs() local list = {} for file in fs.find(".*%.pem", baseurl) do list[#list+1] = posix.basename(file) end return cfe({ type="list", value=list, label="IPSEC Certificates" }) end function mymodule.get_delete_cert(self, clientdata) local retval = {} retval.cert = cfe({ value=clientdata.cert or "", label="IPSEC Certificate" }) return cfe({ type="group", value=retval, label="Delete Certificate" }) end function mymodule.delete_cert(self, delcert) local list = mymodule.list_certs() delcert.value.cert.errtxt = "Invalid cert name" delcert.errtxt = "Failed to delete Certificate" for i,cert in ipairs(list.value) do if cert == delcert.value.cert.value then os.remove(baseurl..cert) delcert.value.cert.errtxt = nil delcert.errtxt = nil break end end return delcert end function mymodule.new_upload_cert() local value = {} value.cert = cfe({ type="raw", value=0, label="Certificate", descr='File must be a password protected ".pfx" file', seq=1 }) value.password = cfe({ type="password", label="Certificate Password", seq=2 }) value.name = cfe({ label="Certificate Local Name", seq=3 }) return cfe({ type="group", value=value }) end function mymodule.upload_cert(self, newcert) local success = true -- Trying to upload a cert/key -- The way haserl works, cert contains the temporary file name -- First, get the cert local cmd, f, cmdresult, errtxt if validator.is_valid_filename(newcert.value.cert.value, "/tmp/") and fs.is_file(newcert.value.cert.value) then cmdresult, errtxt = modelfunctions.run_executable({"openssl", "pkcs12", "-in", newcert.value.cert.value, "-out", newcert.value.cert.value.."cert.pem", "-password", "pass:"..newcert.value.password.value, "-nokeys", "-clcerts"}, true) local filestats = posix.stat(newcert.value.cert.value.."cert.pem") if errtxt or not filestats or filestats.size == 0 then newcert.value.cert.errtxt = "Could not open certificate\n"..(errtxt or cmdresult) success = false end else newcert.value.cert.errtxt = "Invalid certificate" success = false end -- Now, get the key and the ca certs if success then cmdresult, errtxt = modelfunctions.run_executable({"openssl", "pkcs12", "-in", newcert.value.cert.value, "-out", newcert.value.cert.value.."key.pem", "-password", "pass:"..newcert.value.password.value, "-nocerts", "-nodes"}, true) filestats = posix.stat(newcert.value.cert.value.."key.pem") if errtxt or not filestats or filestats.size == 0 then newcert.value.cert.errtxt = "Could not find key\n"..(errtxt or cmdresult) success = false end cmdresult, errtxt = modelfunctions.run_executable({"openssl", "pkcs12", "-in", newcert.value.cert.value, "-out", newcert.value.cert.value.."ca.pem", "-password", "pass:"..newcert.value.password.value, "-nokeys", "-cacerts"}, true) filestats = posix.stat(newcert.value.cert.value.."ca.pem") if errtxt or not filestats or filestats.size == 0 then newcert.value.cert.errtxt = "Could not find CA certs\n"..(errtxt or cmdresult) success = false end end if newcert.value.name.value == "" then newcert.value.name.errtxt = "Cannot be blank" success = false elseif posix.stat(baseurl..newcert.value.name.value.."-cert.pem") or posix.stat(baseurl..newcert.value.name.value.."-key.pem") or posix.stat(baseurl..newcert.value.name.value.."-ca.pem") then newcert.value.name.errtxt = "Certificate of this name already exists" success = false end if success then if not posix.stat(baseurl) then posix.mkdir(baseurl) end -- copy the keys fs.move_file(newcert.value.cert.value.."cert.pem", baseurl..newcert.value.name.value.."-cert.pem") fs.move_file(newcert.value.cert.value.."key.pem", baseurl..newcert.value.name.value.."-key.pem") fs.move_file(newcert.value.cert.value.."ca.pem", baseurl..newcert.value.name.value.."-ca.pem") posix.chmod(baseurl..newcert.value.name.value.."-key.pem", "rw-------") else newcert.errtxt = "Failed to upload certificate" end -- Delete the temporary files if validator.is_valid_filename(newcert.value.cert.value, "/tmp/") and fs.is_file(newcert.value.cert.value) then os.remove(newcert.value.cert.value.."cert.pem") os.remove(newcert.value.cert.value.."key.pem") os.remove(newcert.value.cert.value.."ca.pem") end return newcert end function mymodule.get_view_cert(self, clientdata) local retval = {} retval.cert = cfe({ value=clientdata.cert or "", label="IPSEC Certificate" }) return cfe({ type="group", value=retval, label="View Certificate" }) end mymodule.view_cert = function(self, viewcert) local list = mymodule.list_certs() viewcert.value.cert.errtxt = "Invalid cert name" viewcert.errtxt = "Failed to find cert" for i,cert in ipairs(list.value) do if cert == viewcert.value.cert.value then viewcert.value.result = cfe({ type="longtext", label="Certificate", readonly=true }) viewcert.value.result.value, viewcert.value.result.errtxt = modelfunctions.run_executable({"openssl", "x509", "-in", baseurl..cert, "-noout", "-text"}) viewcert.errtxt = viewcert.value.result.errtxt viewcert.value.cert.errtxt = nil break end end return viewcert end function mymodule.get_logfile(self, clientdata) -- Can override syslog with logfile specified in command-line options "-l logfile" -- Otherwise, uses syslog with facility LOG_DAEMON local retval = cfe({ type="group", value={}, label="Log File Configuration" }) retval.value.facility = cfe({value="daemon", label="Syslog Facility"}) retval.value.grep = cfe({ value="racoon", label="Grep" }) local opts = format.parse_ini_file(fs.read_file(confdfile), "", "RACOON_OPTS") if opts then -- remove quotes local opts2 = string.match(opts, "\"(.*)\"") local opts = " "..(opts2 or "") local logfile = string.match(opts, "%s%-l%s+(%S+)") if logfile then retval.value.filename = cfe({value=logfile, label="File name"}) retval.value.facility = nil retval.value.grep = nil end end return retval end return mymodule