summaryrefslogtreecommitdiffstats
path: root/openssl-model.lua
diff options
context:
space:
mode:
Diffstat (limited to 'openssl-model.lua')
-rw-r--r--openssl-model.lua223
1 files changed, 171 insertions, 52 deletions
diff --git a/openssl-model.lua b/openssl-model.lua
index 29a9b5f..5f4897d 100644
--- a/openssl-model.lua
+++ b/openssl-model.lua
@@ -1,6 +1,7 @@
module(..., package.seeall)
require("html")
+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
@@ -31,6 +32,23 @@ local extensions = { "basicConstraints", "nsCertType", "nsComment", "keyUsage",
-- list of entries that must be found in ca section
local ca_mandatory_entries = { "new_certs_dir", "certificate", "private_key", "default_md", "database", "policy" }
+-- Create a cfe with the distinguished name defaults
+local getdefaults = function()
+ local defaults = cfe({ type="group", value={} })
+ config = config or getopts.getoptsfromfile(configfile)
+ local distinguished_name = config.req.distinguished_name or ""
+
+ -- Get the distinguished name defaults
+ for i, name in ipairs(distinguished_names) do
+ defaults.value[name.name] = cfe({ label=name.label,
+ value=config[distinguished_name][name.name .. "_default"]
+ or config[distinguished_name]["0."..name.name.."_default"] or "",
+ descr=config[distinguished_name][name.name] or config[distinguished_name]["0."..name.name] })
+ 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 getopts.getoptsfromfile(configfile)
@@ -114,16 +132,18 @@ local handle_req_clientdata = function(clientdata, defaults)
local success
success, defaults = validate_distinguished_names(defaults)
- local foundcert=false
- for i,cert in ipairs(defaults.value.certtype.option) do
- if defaults.value.certtype.value == cert then
- foundcert=true
- break
+ 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 not foundcert then
- success = false
- defaults.value.certtype.errtxt = "Invalid certificate type"
end
return success, defaults
@@ -139,44 +159,41 @@ local getconfigpath = function(section, value)
return result
end
--- FIXME we need to make sure necessary files / directories / private key are there
-verifyopenssl = function()
- -- set the working directory once for model
- posix.chdir(openssldir)
-
- local retval = false
- if fs.is_file(configfile) then
- config = config or getopts.getoptsfromfile(configfile)
- if config and config.ca and config.ca.default_ca then
- local cacert_file = getconfigpath(config.ca.default_ca, "private_key")
- if fs.is_file(cacert_file) then
- retval=true
- end
- end
- end
- return cfe({ type="boolean", value=retval, label="openssl verified" })
+local copyca = function(cacert, cakey)
+ config = config or getopts.getoptsfromfile(configfile)
+ local certpath = getconfigpath(config.ca.default_ca, "certificate")
+ local cmd = "cp "..cacert.." "..certpath
+ local f = io.popen(cmd)
+ f:close()
+ local keypath = getconfigpath(config.ca.default_ca, "private_key")
+ local cmd = "cp "..cakey.." "..keypath
+ local f = io.popen(cmd)
+ f:close()
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 getopts.getoptsfromfile(configfile)
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 = getconfigpath(config.ca.default_ca, "private_key")
cacert.value = getconfigpath(config.ca.default_ca, "certificate")
if not fs.is_file(cacert.value) then
cacert.errtxt="File not found"
@@ -186,23 +203,17 @@ getstatus = function()
cacertcontents.value = f:read("*a")
f:close()
end
+ cakey.value = getconfigpath(config.ca.default_ca, "private_key")
+ if not fs.is_file(cakey.value) then
+ cakey.errtxt="File not found"
+ end
end
end
- return cfe({ type="group", value={version=version, conffile=conffile, cacert=cacert, cacertcontents=cacertcontents}, label="openssl status" })
+ return cfe({ type="group", value={version=version, conffile=conffile, cacert=cacert, cacertcontents=cacertcontents, cakey=cakey}, label="openssl status" })
end
getreqdefaults = function()
- local defaults = cfe({ type="group", value={} })
- config = config or getopts.getoptsfromfile(configfile)
- local distinguished_name = config.req.distinguished_name or ""
-
- -- Get the distinguished name defaults
- for i, name in ipairs(distinguished_names) do
- defaults.value[name.name] = cfe({ label=name.label,
- value=config[distinguished_name][name.name .. "_default"]
- or config[distinguished_name]["0."..name.name.."_default"] or "",
- descr=config[distinguished_name][name.name] or config[distinguished_name]["0."..name.name] })
- end
+ local defaults = getdefaults()
-- Add in the ca type default
defaults.value.certtype = cfe({ type="select", label="Certificate Type",
@@ -264,10 +275,6 @@ submitrequest = function(clientdata, user)
end
if success then
- -- FIXME check to make sure same certificate or request doesn't already exist
- end
-
- if success then
-- Submit the request
local reqname = requestdir..user.."."..defaults.value.certtype.value.."."..defaults.value.commonName.value
local subject = create_subject_string(defaults)
@@ -276,7 +283,11 @@ submitrequest = function(clientdata, user)
local cmdresult = f:read("*a")
f:close()
defaults.descr = cmdresult
- if fs.is_file(reqname..".csr") then
+ 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
+ else
fs.write_file(reqname..".pwd", defaults.value.password.value)
end
end
@@ -329,9 +340,10 @@ approverequest = function(request)
local f = io.popen(cmd)
cmdresult.value = f:read("*a")
f:close()
-
+
-- If certificate created, create the wrapped up pkcs12
- if fs.is_file(certname..".crt") then
+ local filestats = posix.stat(certname..".crt")
+ if filestats and filestats.size > 0 then
cmd = "PATH=/usr/local/bin:/usr/bin:/bin:/usr/local/sbin:/usr/sbin:/sbin openssl pkcs12 -export -inkey "..path..".pem -in "..certname..".crt -out "..certname..".pfx -passout file:"..path..".pwd 2>&1"
f = io.popen(cmd)
local newcmdresult = f:read("*a")
@@ -340,10 +352,19 @@ approverequest = function(request)
end
-- Finally, remove the request
- if fs.is_file(certname..".pfx") then
+ filestats = posix.stat(certname..".pfx")
+ if filestats and filestats.size > 0 then
+ cmd = "cp "..path..".pwd "..certname..".pwd"
+ f = io.popen(cmd)
+ f:close()
cmd = "rm "..path..".*"
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
@@ -374,7 +395,10 @@ listcerts = function(user)
end
viewcert = function(cert)
- local cmdresult = fs.read_file(certdir..cert..".crt")
+ local cmd = "PATH=/usr/local/bin:/usr/bin:/bin:/usr/local/sbin:/usr/sbin:/sbin 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
@@ -382,7 +406,6 @@ end
getcert = function(cert)
local f = fs.read_file(certdir..cert..".pfx")
return cfe({ type="raw", value=f, label=cert..".pfx", option="application/x-pkcs12" })
- --return cfe({ type="raw", value=f, label=cert..".pfx" })
end
revokecert = function(cert)
@@ -435,20 +458,116 @@ getcrl = function(crltype)
return crlfile
end
--- FIXME this won't work because haserl doesn't support file upload. Untested and unfinished
putca = function(file, pword, set)
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" })
local retval = cfe({ type="group", value={ca=ca, password=password} })
if file and pword and set then
- fs.write_file(openssldir.."temp.pfx", file)
- fs.write_file(openssldir.."temp.pwd", pword)
+ local success = true
+ -- Trying to upload a cert/key
+ -- The way haserl works, file contains the temporary file name
+ -- First, get the cert
+ local cmd = "PATH=/usr/local/bin:/usr/bin:/bin:/usr/local/sbin:/usr/sbin:/sbin openssl pkcs12 -in "..file.." -out "..file.."cert.pem -password pass:"..pword.." -nokeys 2>&1"
+ local f = io.popen(cmd)
+ local cmdresult = f:read("*a")
+ f:close()
+ local filestats = posix.stat(file.."cert.pem")
+ if not filestats or filestats.size == 0 then
+ 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=/usr/local/bin:/usr/bin:/bin:/usr/local/sbin:/usr/sbin:/sbin openssl x509 -in "..file.."cert.pem -noout -text"
+ f = io.popen(cmd)
+ cmdresult = f:read("*a")
+ f:close()
+ if not string.find(cmdresult, "CA:TRUE") then
+ ca.errtxt = "Could not find CA Certificate"
+ success = false
+ end
+ end
- -- Still need to verify input (using openssl pkcs12) and put cert and key in right place
+ -- Now, get the key
+ if success then
+ cmd = "PATH=/usr/local/bin:/usr/bin:/bin:/usr/local/sbin:/usr/sbin:/sbin openssl pkcs12 -in "..file.." -out "..file.."key.pem -password pass:"..pword.." -nocerts -nodes 2>&1"
+ f = io.popen(cmd)
+ cmdresult = f:read("*a")
+ f:close()
+ filestats = posix.stat(file.."key.pem")
+ if not filestats or filestats.size == 0 then
+ ca.errtxt = "Could not find CA key\n"..cmdresult
+ success = false
+ end
+ end
+
+ if success then
+ -- copy the keys
+ copyca(file.."cert.pem", file.."key.pem")
+ else
+ retval.errtxt = "Failed to upload CA certificate"
+ end
+
+ -- Delete the temporary files
+ cmd = "rm "..file.."*"
+ f = io.popen(cmd)
+ f:close()
end
return retval
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(clientdata)
+ -- First, get the defaults
+ local defaults = getnewcarequest()
+
+ -- Then, copy in user values and validate
+ local success, defaults = handle_req_clientdata(clientdata, 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)
+ local cmd = "PATH=/usr/local/bin:/usr/bin:/bin:/usr/local/sbin:/usr/sbin:/sbin 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()
local filename = cfe({ value=configfile, label="File Name" })
local filecontent = cfe({ type="longtext", label="Config file" })