module(..., package.seeall) require("modelfunctions") require("fs") require("format") require("validator") -- There are two options of how to allow users to specify the type of certificate they want - the request extensions -- and the ca signing extensions. We have opted for making all requests look the same (same extensions) and defining -- different ca sections for the different types of certificates. The ca section to use when signing the request is -- actually stored in the request filename. The request filename is in the following format: -- 'username'.'ca section name'.'common name'.csr local packagename = "openssl" --local configfile = "/etc/ssl/openssl.cnf" local configfile = "/etc/ssl/openssl-ca-acf.cnf" local requestdir = "/etc/ssl/req/" local certdir = "/etc/ssl/cert/" local openssldir = "/etc/ssl/" local path = "PATH=/usr/local/bin:/usr/bin:/bin:/usr/local/sbin:/usr/sbin:/sbin " -- Save the config in a variable so isn't loaded each and every time needed local config = nil -- list of request entries that can be edited local short_names = { countryName="C", stateOrProvinceName="ST", localityName="L", organizationName="O", organizationalUnitName="OU", commonName="CN" } -- list of entries that may be found in cert extensions section local extensions = { "basicConstraints", "nsCertType", "nsComment", "keyUsage", "subjectKeyIdentifier", "authorityKeyIdentifier", "subjectAltName", "issuerAltName" } -- list of entries that must be found in ca section (used to define our certificate types) local ca_mandatory_entries = { "new_certs_dir", "certificate", "private_key", "default_md", "database", "serial", "policy" } -- Create a cfe with the distinguished name defaults local getdefaults = function() local defaults = cfe({ type="group", value={} }) config = config or format.parse_ini_file(fs.read_file(configfile) or "") local distinguished_name = config.req.distinguished_name or "" -- Get the distinguished name defaults for name,value in pairs(config[distinguished_name]) do if nil == string.find(name, "_") then defaults.value[name] = cfe({ label=value, value=config[distinguished_name][name .. "_default"] or "" }) end end return defaults end -- Validate the values of distinguished names using the min/max found in the config file local validate_distinguished_names = function(values) config = config or format.parse_ini_file(fs.read_file(configfile) or "") local distinguished_name = config.req.distinguished_name or "" local success = true for name,value in pairs(values.value) do if string.find(value.value, "[#]") then value.errtxt = "Value cannot contain #" success = false end -- check min, but empty is allowed local min = config[distinguished_name][name.."_min"] if min and value.value and #value.value < tonumber(min) and #value.value > 0 then value.errtxt = "Value too short" success = false end local max = config[distinguished_name][name.."_max"] if max and value.value and #value.value > tonumber(max) then value.errtxt = "Value too long" success = false end end return success, values end -- Write distinguished name defaults to config file local write_distinguished_names = function(file, values, ignorevalues) local reverseignore = {} for i,value in ipairs(ignorevalues) do reverseignore[value]=i end config = config or format.parse_ini_file(file) local distinguished_name = config.req.distinguished_name or "" for name,value in pairs(values.value) do if not reverseignore[name] then local wname = name.."_default" file = format.update_ini_file(file, distinguished_name, wname, value.value) end end config = nil return file end local create_subject_string = function(values, ignorevalues) local outstr = {} local reverseignore = {} for i,value in ipairs(ignorevalues) do reverseignore[value]=i end -- do the ones with short names first local reverseshorts = {} for name,short in pairs(short_names) do reverseshorts[short] = name end for name,value in pairs(values.value) do name = name:gsub(".*%.", "") -- remove the "0." from the front if (short_names[name] or reverseshorts[name]) and value.value and value.value ~= "" then name = short_names[name] or name outstr[#outstr + 1] = name .. "=" .. value.value:gsub("[/=]", "\\%1") -- escape characters end end for name,value in pairs(values.value) do name = name:gsub(".*%.", "") -- remove the "0." from the front if not reverseignore[name] and not short_names[name] and not reverseshorts[name] and value.value and value.value ~= "" then outstr[#outstr + 1] = name .. "=" .. value.value:gsub("[/=]", "\\%1") -- escape characters end end return "/"..table.concat(outstr, "/") end local getconfigentry = function(section, value) config = config or format.parse_ini_file(fs.read_file(configfile) or "") local result = config[section][value] or config[""][value] or "" while string.find(result, "%$[%w_]+") do local sub = string.match(result, "%$[%w_]+") result = string.gsub(result, sub, config[section][string.sub(sub,2)] or config[""][string.sub(sub,2)] or "") end return result end -- Find the sections of the config file that define ca's (ca -name option) local find_ca_sections = function() config = config or format.parse_ini_file(fs.read_file(configfile) or "") local cert_types = {} for section in pairs(config) do local success = true for i,entry in ipairs(ca_mandatory_entries) do if getconfigentry(section,entry) == "" then success = false break end end if success then cert_types[#cert_types + 1] = section end end return cert_types end local validate_request = function(defaults, noextensionsections) local success success, defaults = validate_distinguished_names(defaults) if defaults.value.certtype then local foundcert=false for i,cert in ipairs(defaults.value.certtype.option) do if defaults.value.certtype.value == cert then foundcert=true break end end if not foundcert then success = false defaults.value.certtype.errtxt = "Invalid certificate type" end end if defaults.value.extensions then config = config or format.parse_ini_file(fs.read_file(configfile) or "") local extensions = format.parse_ini_file(defaults.value.extensions.value) for name,value in pairs(extensions or {}) do if name ~= "" and noextensionsections then defaults.value.extensions.errtxt = "Cannot contain sections" success = false elseif name ~= "" and config[name] then defaults.value.extensions.errtxt = "Duplicate section name" success = false end end end return success, defaults end local copyca = function(cacert, cakey) config = config or format.parse_ini_file(fs.read_file(configfile) or "") local certpath = getconfigentry(config.ca.default_ca, "certificate") local cmd = "cp "..cacert.." "..certpath local f = io.popen(cmd) f:close() local keypath = getconfigentry(config.ca.default_ca, "private_key") local cmd = "cp "..cakey.." "..keypath local f = io.popen(cmd) f:close() end local checkdir = function(name, dirpath) local errtxt, cmdline local filestats = posix.stat(dirpath, "type") if not filestats or filestats == "" then errtxt = name.." does not exist" cmdline = "mkdir -p "..dirpath elseif filestats ~= "directory" then errtxt = "UNRECOVERABLE - "..name.." not a directory" end return errtxt, cmdline end local checkfile = function(name, filepath, default) local errtxt, cmdline local filestats = posix.stat(filepath, "type") if not filestats or filestats == "" then errtxt = name.." does not exist" if default then cmdline = "echo "..default.." > "..filepath else cmdline = "touch "..filepath end elseif filestats ~= "regular" then errtxt = "UNRECOVERABLE - "..name.." not a file" end return errtxt, cmdline end local hashname = function(name) local hash = {name:byte(1,-1)} -- no longer returning '-' separated decimal, but 2 char hex --return table.concat(hash, "-") for i,val in ipairs(hash) do hash[i] = string.format("%02X", val) end return table.concat(hash) end local unhashname = function(hashstring) local hash = {} -- this is to be backward compatible with '-' separated decimal if string.find(hashstring, "-") then for char in string.gmatch(hashstring, "([^-]+)-*") do hash[#hash+1] = char end else for char in string.gmatch(hashstring, "%x%x") do hash[#hash+1] = tonumber(char, 16) end end return string.char(unpack(hash)) end getstatus = function() require("processinfo") -- set the working directory once for model posix.chdir(openssldir) local value,errtxt=processinfo.package_version(packagename) local version = cfe({ value=value, errtxt=errtxt, label="Program version" }) local conffile = cfe({ value=configfile, label="Configuration file" }) local cacert = cfe({ label="CA Certificate" }) local cacertcontents = cfe({ type="longtext", label="CA Certificate contents" }) local cakey = cfe({ label="CA Key" }) if not fs.is_file(configfile) then conffile.errtxt="File not found" cacert.errtxt="File not defined" cacertcontents.errtxt="" cakey.errtxt="File not defined" else config = config or format.parse_ini_file(fs.read_file(configfile) or "") if (not config) or (not config.ca) or (not config.ca.default_ca) then conffile.errtxt="Invalid config file" cacert.errtxt="File not defined" cacertcontents.errtxt="" cakey.errtxt="File not defined" else cacert.value = getconfigentry(config.ca.default_ca, "certificate") if not fs.is_file(cacert.value) then cacert.errtxt="File not found" else local cmd = path .. "openssl x509 -in "..cacert.value.." -noout -text" local f = io.popen(cmd) cacertcontents.value = f:read("*a") f:close() local enddate = string.match(cacertcontents.value, "Not After : (.*)") local month, day, year = string.match(enddate, "(%a+)%s+(%d+)%s+%S+%s+(%d+)") local reversemonth = {Jan=1,Feb=2,Mar=3,Apr=4,May=5,Jun=6,Jul=7,Aug=8,Sep=9,Oct=10,Nov=11,Dec=12} local time = os.time({year=year, month=reversemonth[month], day=day}) if os.time() > time then time = 0 cacert.errtxt="Certificate expired" else time = (time-os.time())/86400 end cacert.daysremaining=time end cakey.value = getconfigentry(config.ca.default_ca, "private_key") if not fs.is_file(cakey.value) then cakey.errtxt="File not found" end end end local environment = checkenvironment() return cfe({ type="group", value={version=version, conffile=conffile, environment=environment, cacert=cacert, cacertcontents=cacertcontents, cakey=cakey}, label="openssl status" }) end getreqdefaults = function() local defaults = getdefaults() -- Add in the ca type default defaults.value.certtype = cfe({ type="select", label="Certificate Type", value=config.ca.default_ca, option=find_ca_sections() }) -- Add in the extensions local extensions = "" local content = fs.read_file(configfile) config = config or format.parse_ini_file(content) if config.req.req_extensions then extensions = format.get_ini_section(content, config.req.req_extensions) end defaults.value.extensions = cfe({ type="longtext", label="Additional x509 Extensions", value=extensions, descr="These extensions can be overridden by the Certificate Type" }) return defaults end setreqdefaults = function(defaults) local success, defaults = validate_request(defaults, true) -- If success, write the values to the config file if success then local fileval = fs.read_file(configfile) config = config or format.parse_ini_file(fileval) local ext_section if not config.req or not config.req.req_extensions then ext_section = "v3_req" while config[ext_section] do ext_section = "v3_req_"..tostring(os.time()) end fileval = format.update_ini_file(fileval, "req", "req_extensions", ext_section) else ext_section = config.req.req_extensions end config = nil fileval = format.set_ini_section(fileval, ext_section, format.dostounix(defaults.value.extensions.value)) fileval = format.update_ini_file(fileval, "ca", "default_ca", defaults.value.certtype.value) fileval = write_distinguished_names(fileval, defaults, {"certtype", "extensions"}) fs.write_file(configfile, fileval) end if not success then defaults.errtxt = "Failed to set defaults" end return defaults end getnewrequest = function() local values = getreqdefaults() -- In addition to the request defaults, we need a password and confirmation values.value.password = cfe({ label="Password" }) values.value.password_confirm = cfe({ label="Password confirmation" }) return values end submitrequest = function(defaults, user) local success, defaults = validate_request(defaults) -- Must have a common name if #defaults.value.commonName.value == 0 then defaults.value.commonName.errtxt = "Common Name cannot be blank" success = false end -- Check validity of password if #defaults.value.password.value < 4 then defaults.value.password.errtxt = "Password too short" success = false end if defaults.value.password.value ~= defaults.value.password_confirm.value then defaults.value.password_confirm.errtxt = "You entered wrong password/confirmation" success = false end local reqname = requestdir..user.."."..defaults.value.certtype.value.."."..hashname(defaults.value.commonName.value) if fs.is_file(reqname..".csr") then defaults.errtxt = "Failed to submit request\nRequest already exists" success = false end if success then -- Submit the request local subject = create_subject_string(defaults, {"password", "password_confirm", "certtype", "extensions"}) -- Generate a temp config file for this request local fileval = fs.read_file(configfile) config = config or format.parse_ini_file(fileval) local temp = format.dostounix(defaults.value.extensions.value) local ext_section if not config.req or not config.req.req_extensions then ext_section = "v3_req" while config[ext_section] do ext_section = "v3_req_"..tostring(os.time()) end else ext_section = config.req.req_extensions for name,value in pairs(config[ext_section] or {}) do temp = format.update_ini_file(temp, "", name, value) end end if config[defaults.value.certtype.value].x509_extensions then ext_section = config[defaults.value.certtype.value].x509_extensions for name,value in pairs(config[ext_section] or {}) do if not string.find(value, "issuer") then temp = format.update_ini_file(temp, "", name, value) end end end if temp ~= "" then fileval = format.set_ini_section(fileval, ext_section, temp) fileval = format.update_ini_file(fileval, "req", "req_extensions", ext_section) end fs.write_file(reqname..".cfg", fileval) local cmd = path .. "openssl req -nodes -new -config "..reqname..".cfg -keyout "..reqname..".pem -out "..reqname..".csr -subj '"..subject.."' 2>&1" local f = io.popen(cmd) local cmdresult = f:read("*a") f:close() defaults.descr = cmdresult local certfilestats = posix.stat(reqname..".csr") local keyfilestats = posix.stat(reqname..".pem") if not certfilestats or certfilestats.size == 0 or not keyfilestats or keyfilestats.size == 0 then success = false cmd = "rm "..reqname..".*" f = io.popen(cmd) f:close() else fs.write_file(reqname..".pwd", defaults.value.password.value) end end if not success and not defaults.errtxt then defaults.errtxt = "Failed to submit request" end return defaults end listrequests = function(user) user = user or "*" local list={} local fh = io.popen("find " .. requestdir .. " -name "..user..".*.csr -maxdepth 1") for x in fh:lines() do local name = basename(x,".csr") local a,b,c = string.match(name, "([^%.]*)%.([^%.]*)%.([^%.]*)") list[#list + 1] = {name=name, user=a, certtype=b, commonName=unhashname(c)} end return cfe({ type="list", value=list, label="List of pending requests" }) end viewrequest = function(request) local reqpath = requestdir .. request local cmd = path .. "openssl req -in "..reqpath..".csr -text -noout" local f = io.popen(cmd) local cmdresult = f:read("*a") f:close() local a,b,c = string.match(request, "([^%.]*)%.([^%.]*)%.([^%.]*)") local request = cfe({ type="table", value={name=name, user=a, certtype=b, commonName=c, value=cmdresult}, label="Request" }) return request end approverequest = function(request) local cmdresult = cfe({ value="Failed to approve request", label="Approve result" }) local reqpath = requestdir .. request if fs.is_file(reqpath..".csr") then -- Request file exists, so try to sign local user,certtype,commonName = string.match(request, "([^%.]*)%.([^%.]*)%.([^%.]*)") -- Add the serial number to the end of the cert file name local serialpath = getconfigentry(certtype, "serial") local serialfile = fs.read_file(serialpath) local serial = string.match(serialfile, "%x%x") local certname = certdir..request.."."..serial -- Now, sign the certificate local cmd = path .. "openssl ca -config "..configfile.." -in "..reqpath..".csr -out "..certname..".crt -name "..certtype.." -batch 2>&1" local f = io.popen(cmd) cmdresult.value = f:read("*a") f:close() -- If certificate created, create the wrapped up pkcs12 local filestats = posix.stat(certname..".crt") if filestats and filestats.size > 0 then -- We're wrapping up the key, the cert, and the CA cert (and whatever came with it) cmd = path .. "openssl pkcs12 -export -inkey "..reqpath..".pem -in "..certname..".crt -out "..certname..".pfx -passout file:"..reqpath..".pwd -certfile "..getconfigentry(certtype, "certificate").." 2>&1" f = io.popen(cmd) local newcmdresult = f:read("*a") f:close() cmdresult.value = cmdresult.value .. newcmdresult end -- Finally, remove the request filestats = posix.stat(certname..".pfx") if filestats and filestats.size > 0 then cmd = "cp "..reqpath..".pwd "..certname..".pwd" f = io.popen(cmd) f:close() cmd = "cp "..reqpath..".pem "..certname..".pem" f = io.popen(cmd) f:close() cmd = "cp "..reqpath..".cfg "..certname..".cfg" f = io.popen(cmd) f:close() cmd = "rm "..reqpath..".*" f = io.popen(cmd) f:close() else -- or failed, remove the cert cmd = "rm "..certname..".*" f = io.popen(cmd) f:close() end end return cmdresult end deleterequest = function(request, user) user = user or ".*" if (not fs.is_file(requestdir..request..".csr")) or (not string.find(request, "^"..user.."%.")) then return cfe({ value="Request not found", label="Delete result" }) end cmd = "rm "..requestdir..request..".*" f = io.popen(cmd) f:close() return cfe({ value="Request deleted", label="Delete result" }) end listcerts = function(user) user = user or "*" local list={} local fh = io.popen("find " .. certdir .. " -name "..user..".*.pfx -maxdepth 1") for x in fh:lines() do local name = basename(x,".pfx") local a,b,c,d = string.match(name, "([^%.]*)%.([^%.]*)%.([^%.]*).([^%.]*)") local cmd = path .. "openssl x509 -in "..certdir..name..".crt -noout -enddate" local f = io.popen(cmd) local enddate = f:read("*a") enddate = string.match(enddate, "notAfter=(.*)") f:close() local month, day, year = string.match(enddate, "(%a+)%s+(%d+)%s+%S+%s+(%d+)") local reversemonth = {Jan=1,Feb=2,Mar=3,Apr=4,May=5,Jun=6,Jul=7,Aug=8,Sep=9,Oct=10,Nov=11,Dec=12} local time = os.time({year=year, month=reversemonth[month], day=day}) if os.time() > time then time = 0 else time = (time-os.time())/86400 end list[#list + 1] = {name=name, user=a, certtype=b, commonName=unhashname(c), serial=d, enddate=enddate, daysremaining=time} end fh:close() return cfe({ type="list", value=list, label="List of approved certificates" }) end viewcert = function(cert) local cmd = path .. "openssl x509 -in "..certdir..cert..".crt -noout -text" local f = io.popen(cmd) local cmdresult = f:read("*a") f:close() local a,b,c,d = string.match(cert, "([^%.]*)%.([^%.]*)%.([^%.]*).([^%.]*)") return cfe({ type="table", value={name=name, user=a, certtype=b, commonName=c, serial=d, value=cmdresult}, label="Certificate" }) end getcert = function(cert) local f = fs.read_file(certdir..cert..".pfx") local a,b,c,d = string.match(cert, "([^%.]*)%.([^%.]*)%.([^%.]*).([^%.]*)") c = string.gsub(unhashname(c), "[^%w_-]", "") return cfe({ type="raw", value=f, label=c..".pfx", option="application/x-pkcs12" }) end revokecert = function(cert) local cmdresult = cfe({ label="Revoke result" }) local cmd = path .. "openssl ca -config "..configfile.." -revoke "..certdir .. cert..".crt -batch 2>&1" local f = io.popen(cmd) cmdresult.value = f:read("*a") f:close() return cmdresult end deletecert = function(cert) -- The certificate will still be in the ca directories and index.txt, just not available for web interface cmd = "rm "..certdir..cert..".*" f = io.popen(cmd) f:close() return cfe({ value="Certificate deleted", label="Delete result" }) end renewcert = function(cert, approve) local cmdresult = "" local success = true local user,certtype,commonName,serialnum = string.match(cert, "([^%.]*)%.([^%.]*)%.([^%.]*).([^%.]*)") local reqname = requestdir..user.."."..certtype.."."..commonName if fs.is_file(reqname..".csr") then cmdresult = "Failed to submit request\nRequest already exists" success = false end if success then -- Submit the request -- First, get the subject local cmd = path .. "openssl x509 -in "..certdir..cert..".crt -noout -subject" local f = io.popen(cmd) local subject = f:read("*a") subject = string.match(subject, "subject= ([^\n]*)") f:close() -- Next, put the key and password in place cmd = "cp "..certdir..cert..".pwd "..reqname..".pwd" f = io.popen(cmd) f:close() cmd = "cp "..certdir..cert..".cfg "..reqname..".cfg" f = io.popen(cmd) f:close() -- Next, submit the request cmd = path .. "openssl req -nodes -new -config "..reqname..".cfg -keyout "..reqname..".pem -out "..reqname..".csr -subj '"..subject.."' 2>&1" f = io.popen(cmd) cmdresult = f:read("*a") f:close() local filestats = posix.stat(reqname..".csr") if not filestats or filestats.size == 0 then cmdresult = "Failed to submit request\n"..cmdresult success = false cmd = "rm "..reqname..".*" f = io.popen(cmd) f:close() else cmdresult = "Submitted request" end end if success and approve then approverequest(basename(reqname)) end return cfe({ type="boolean", value=cmdresult, label="Renew result" }) end listrevoked = function() config = config or format.parse_ini_file(fs.read_file(configfile) or "") local databasepath = getconfigentry(config.ca.default_ca, "database") local revoked = {} local database = fs.read_file_as_array(databasepath) for x,line in ipairs(database) do if string.sub(line,1,1) == "R" then revoked[#revoked + 1] = string.match(line, "^%S+%s+%S+%s+%S+%s+(%S+)") end end return cfe({ type="list", value=revoked, label="Revoked serial numbers" }) end getcrl = function(crltype) local crlfile = cfe({ type="raw", label="Revoke list", option="application/pkix-crl" }) local cmd = path .. "openssl ca -config "..configfile.." -gencrl -out "..openssldir.."ca-crl.crl" local f = io.popen(cmd) f:close() local cmd = path .. "openssl crl -in "..openssldir.."ca-crl.crl -out "..openssldir.."ca-der-crl.crl -outform DER" local f = io.popen(cmd) f:close() if crltype == "DER" then crlfile.label = "ca-der-crl.crl" crlfile.value = fs.read_file(crlfile.label) elseif crltype == "PEM" then crlfile.label = "ca-crl.crl" crlfile.value = fs.read_file(crlfile.label) else crlfile.value = fs.read_file("ca-crl.crl") end return crlfile end getnewputca = function() local ca = cfe({ type="raw", value=0, label="CA Certificate", descr='File must be a password protected ".pfx" file' }) local password = cfe({ label="Certificate Password" }) return cfe({ type="group", value={ca=ca, password=password} }) end putca = function(newca) local success = true -- Trying to upload a cert/key -- The way haserl works, ca contains the temporary file name -- First, get the cert local cmd = path .. "openssl pkcs12 -in "..newca.value.ca.value.." -out "..newca.value.ca.value.."cert.pem -password pass:"..newca.value.password.value.." -nokeys 2>&1" local f = io.popen(cmd) local cmdresult = f:read("*a") f:close() local filestats = posix.stat(newca.value.ca.value.."cert.pem") if not filestats or filestats.size == 0 then newca.value.ca.errtxt = "Could not open certificate\n"..cmdresult success = false end -- Since -cacerts doesn't seem to work, we have to check to make sure we got a CA if success then cmd = path .. "openssl x509 -in "..newca.value.ca.value.."cert.pem -noout -text" f = io.popen(cmd) cmdresult = f:read("*a") f:close() if not string.find(cmdresult, "CA:TRUE") then newca.value.ca.errtxt = "Could not find CA Certificate" success = false end end -- Now, get the key if success then cmd = path .. "openssl pkcs12 -in "..newca.value.ca.value.." -out "..newca.value.ca.value.."key.pem -password pass:"..newca.value.password.value.." -nocerts -nodes 2>&1" f = io.popen(cmd) cmdresult = f:read("*a") f:close() filestats = posix.stat(newca.value.ca.value.."key.pem") if not filestats or filestats.size == 0 then newca.value.ca.errtxt = "Could not find CA key\n"..cmdresult success = false end end if success then -- copy the keys copyca(newca.value.ca.value.."cert.pem", newca.value.ca.value.."key.pem") else newca.errtxt = "Failed to upload CA certificate" end -- Delete the temporary files cmd = "rm "..newca.value.ca.value.."*" f = io.popen(cmd) f:close() -- Clear the values newca.value.ca.value = "" newca.value.password.value = "" return newca end getnewcarequest = function() request = getdefaults() -- In addition to the distinguished name defaults, we need days request.value.days = cfe({ value="365", label="Number of days to certify" }) return request end generateca = function(defaults) local success, defaults = validate_request(defaults) if not validator.is_integer(defaults.value.days.value) then defaults.value.days.errtxt = "Must be a number" success = false end if success then -- Submit the request local subject = create_subject_string(defaults, {"days"}) local cmd = path .. "openssl req -x509 -nodes -new -config "..configfile.." -keyout /tmp/cakey.pem -out /tmp/cacert.pem -subj '"..subject.."' -days "..defaults.value.days.value.." 2>&1" local f = io.popen(cmd) local cmdresult = f:read("*a") f:close() local certfilestats = posix.stat("/tmp/cacert.pem") local keyfilestats = posix.stat("/tmp/cakey.pem") if not certfilestats or certfilestats.size == 0 or not keyfilestats or keyfilestats.size == 0 then defaults.errtxt = "Failed to generate CA certificate\n"..cmdresult success = false end if success then -- copy the keys copyca("/tmp/cacert.pem", "/tmp/cakey.pem") end -- Delete the temporary files cmd = "rm /tmp/ca*.pem" f = io.popen(cmd) f:close() end if not success and not defaults.errtxt then defaults.errtxt = "Failed to generate CA certificate" end return defaults end getconfigfile = function() return modelfunctions.getfiledetails(configfile) end setconfigfile = function(filedetails) -- validate return modelfunctions.setfiledetails(filedetails, {configfile}) end checkenvironment = function(set) local errtxt = {} local cmdline = {} -- First check for the openssl, req, and cert directories errtxt[#errtxt+1], cmdline[#cmdline+1] = checkdir("openssl directory", openssldir) errtxt[#errtxt+1], cmdline[#cmdline+1] = checkdir("new certificate directory", certdir) errtxt[#errtxt+1], cmdline[#cmdline+1] = checkdir("request directory", requestdir) -- Then check for the config file entries config = config or format.parse_ini_file(fs.read_file(configfile) or "") if config then local chkpath = getconfigentry(config.ca.default_ca, "new_certs_dir") errtxt[#errtxt+1], cmdline[#cmdline+1] = checkdir("new_certs_dir", chkpath) local file = getconfigentry(config.ca.default_ca, "certificate") chkpath = dirname(file) errtxt[#errtxt+1], cmdline[#cmdline+1] = checkdir("certificate directory", chkpath) file = getconfigentry(config.ca.default_ca, "private_key") chkpath = dirname(file) errtxt[#errtxt+1], cmdline[#cmdline+1] = checkdir("private_key directory", chkpath) file = getconfigentry(config.ca.default_ca, "database") chkpath = dirname(file) errtxt[#errtxt+1], cmdline[#cmdline+1] = checkdir("database directory", chkpath) errtxt[#errtxt+1], cmdline[#cmdline+1] = checkfile("database", file) file = getconfigentry(config.ca.default_ca, "serial") chkpath = dirname(file) errtxt[#errtxt+1], cmdline[#cmdline+1] = checkdir("serial directory", chkpath) errtxt[#errtxt+1], cmdline[#cmdline+1] = checkfile("serial", file, "01") file = getconfigentry(config.ca.default_ca, "crlnumber") if file ~= "" then chkpath = dirname(file) errtxt[#errtxt+1], cmdline[#cmdline+1] = checkdir("crlnumber directory", chkpath) errtxt[#errtxt+1], cmdline[#cmdline+1] = checkfile("crlnumber", file, "01") end else errtxt[#errtxt+1] = "Configuration invalid" end if set then -- loop through the cmdline and execute for x,cmd in ipairs(cmdline) do local f = io.popen(cmd) f:close() end return checkenvironment() else errtxt = table.concat(errtxt, '\n') local value if errtxt == "" then errtxt = nil value = "Environment ready" else value = "Environment not ready" end return cfe({ value=value, errtxt=errtxt, label="Environment" }) end end