summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--openssl-controller.lua43
-rw-r--r--openssl-generatecacert-html.lsp15
-rw-r--r--openssl-html.lsp12
-rw-r--r--openssl-model.lua223
-rw-r--r--openssl-putcacert-html.lsp2
-rw-r--r--openssl-status-html.lsp18
-rw-r--r--openssl-viewcert-html.lsp2
-rw-r--r--openssl-viewrequest-html.lsp2
-rw-r--r--openssl.roles2
9 files changed, 253 insertions, 66 deletions
diff --git a/openssl-controller.lua b/openssl-controller.lua
index af92ec5..ff9fb91 100644
--- a/openssl-controller.lua
+++ b/openssl-controller.lua
@@ -5,19 +5,21 @@ require("getopts")
default_action = "status"
+local sslstatus
+
mvc={}
mvc.pre_exec = function(self)
- if self.conf.action ~= "status" and self.conf.action ~= "editconfigfile" then
- local verify = self.model.verifyopenssl()
- if verify.value == false then
- redirect(self)
- end
+ sslstatus = self.model.getstatus()
+ if (sslstatus.value.version.errtxt and self.conf.action ~= "status")
+ or (sslstatus.value.conffile.errtxt and self.conf.action ~= "status" and self.conf.action ~= "editconfigfile")
+ or ((sslstatus.value.cacert.errtxt or sslstatus.value.cakey.errtxt) and self.conf.action ~= "status" and self.conf.action ~= "editconfigfile" and self.conf.action ~= "putcacert" and self.conf.action ~= "generatecacert") then
+ redirect(self)
end
end
-- Show openssl status
status = function(self)
- return self.model.getstatus()
+ return sslstatus
end
-- View all pending and approved requests and revoked certificates
@@ -143,15 +145,40 @@ getrevoked = function(self)
end
-- Put the CA cert
--- FIXME this won't work because haserl doesn't support file upload. Untested
putcacert = function(self)
local retval = self.model.putca(self.clientdata.ca, self.clientdata.password, self.clientdata.Upload)
+ if self.clientdata.Upload then
+ if not retval.errtxt then
+ redirect(self)
+ end
+ end
retval.type = "form"
- retval.option = "Upload"
retval.label = "Upload CA Certificate"
+ retval.option = "Upload"
+
return retval
end
+-- Generate a self-signed CA
+generatecacert = function(self)
+ local request
+ if self.clientdata.Generate then
+ -- Try to submit the request
+ request = self.model.generateca(self.clientdata)
+ if not request.errtxt then
+ redirect(self)
+ end
+ else
+ request = self.model.getnewcarequest()
+ end
+
+ request.type = "form"
+ request.label = "Generate CA Certificate"
+ request.option = "Generate"
+
+ return request
+end
+
editconfigfile = function(self)
local saved = false
if self.clientdata.Save then
diff --git a/openssl-generatecacert-html.lsp b/openssl-generatecacert-html.lsp
new file mode 100644
index 0000000..465f53f
--- /dev/null
+++ b/openssl-generatecacert-html.lsp
@@ -0,0 +1,15 @@
+<? local form, viewlibrary = ... ?>
+<? require("viewfunctions") ?>
+
+<? --[[ DEBUG INFORMATION
+io.write(html.cfe_unpack(form))
+--]] ?>
+
+<H1><?= form.label ?></H1>
+<?
+ form.action = "generatecacert"
+ local order = { "countryName", "stateOrProvinceName", "localityName", "organizationName",
+ "organizationalUnitName", "commonName", "emailAddress" }
+ displayform(form, order)
+?>
+
diff --git a/openssl-html.lsp b/openssl-html.lsp
index d8244b9..adac74e 100644
--- a/openssl-html.lsp
+++ b/openssl-html.lsp
@@ -104,12 +104,24 @@ end ?>
<? else ?>
<TABLE>
<TR style="background:#eee;font-weight:bold;">
+ <TD style="padding-right:20px;white-space:nowrap;" class="header">Action</TD>
<TD style="padding-right:20px;white-space:nowrap;" class="header">User</TD>
<TD style="padding-right:20px;white-space:nowrap;" class="header">Cert Type</TD>
<TD style="padding-right:20px;white-space:nowrap;" class="header">Common Name</TD>
<TD style="white-space:nowrap;" class="header">Serial Num</TD>
<? for i,cert in ipairs(revoked) do ?>
<TR>
+ <TD>
+ <? if session.permissions.openssl.viewcert then ?>
+ <?= html.link{value="viewcert?cert="..cert.name, label="View "} ?>
+ <? end ?>
+ <? if session.permissions.openssl.getcert then ?>
+ <?= html.link{value="getcert?cert="..cert.name, label="Download "} ?>
+ <? end ?>
+ <? if session.permissions.openssl.deletecert then ?>
+ <?= html.link{value="deletecert?cert="..cert.name, label="Delete "} ?>
+ <? end ?>
+ </TD>
<TD><?= cert.user ?></TD>
<TD><?= cert.certtype ?></TD>
<TD><?= cert.commonName ?></TD>
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" })
diff --git a/openssl-putcacert-html.lsp b/openssl-putcacert-html.lsp
index 2707c09..ed3ab02 100644
--- a/openssl-putcacert-html.lsp
+++ b/openssl-putcacert-html.lsp
@@ -7,7 +7,7 @@ io.write(html.cfe_unpack(form))
<H1><?= form.label ?></H1>
<?
- form.action = '" enctype="multipart/form-data'
+ form.action = 'putcacert" enctype="multipart/form-data'
form.value.ca.type="file"
form.value.password.type="password"
local order = {"ca", "password"}
diff --git a/openssl-status-html.lsp b/openssl-status-html.lsp
index a49b7ac..37d45fc 100644
--- a/openssl-status-html.lsp
+++ b/openssl-status-html.lsp
@@ -1,4 +1,4 @@
-<? local view, viewlibrary = ... ?>
+<? local view, viewlibrary, pageinfo, session = ... ?>
<? require("viewfunctions") ?>
<? --[[ DEBUG INFORMATION
@@ -10,5 +10,19 @@ io.write(html.cfe_unpack(view))
<? displayitem(view.value.version) ?>
<? displayitem(view.value.conffile) ?>
<? displayitem(view.value.cacert) ?>
-<? displayitem(view.value.cacertcontents) ?>
+<? displayitem(view.value.cakey) ?>
</DL>
+
+<? if not view.value.cacert.errtxt and not view.value.cakey.errtxt then ?>
+<H1>CA Certificate contents</H1>
+<pre><?= view.value.cacertcontents.value ?></pre>
+<? elseif not view.value.version.errtxt and not view.value.conffile.errtxt then
+ if viewlibrary and viewlibrary.dispatch_component then
+ if session.permissions.openssl.putcacert then
+ viewlibrary.dispatch_component("putcacert")
+ end
+ if session.permissions.openssl.generatecacert then
+ viewlibrary.dispatch_component("generatecacert")
+ end
+ end
+end ?>
diff --git a/openssl-viewcert-html.lsp b/openssl-viewcert-html.lsp
index b8d6136..0593afd 100644
--- a/openssl-viewcert-html.lsp
+++ b/openssl-viewcert-html.lsp
@@ -5,4 +5,4 @@ io.write(html.cfe_unpack(view))
--]] ?>
<H1>Certificate Details</H1>
-<?= string.gsub(view.value.value, "\n", "<br>") ?>
+<pre><?= view.value.value ?></pre>
diff --git a/openssl-viewrequest-html.lsp b/openssl-viewrequest-html.lsp
index ace59b7..cea358e 100644
--- a/openssl-viewrequest-html.lsp
+++ b/openssl-viewrequest-html.lsp
@@ -5,4 +5,4 @@ io.write(html.cfe_unpack(view))
--]] ?>
<H1>Request Details</H1>
-<?= string.gsub(view.value.value, "\n", "<br>") ?>
+<pre><?= view.value.value ?></pre>
diff --git a/openssl.roles b/openssl.roles
index 43ebed8..1e7896a 100644
--- a/openssl.roles
+++ b/openssl.roles
@@ -1,2 +1,2 @@
READ=openssl:status,openssl:read,openssl:request,openssl:viewrequest,openssl:deletemyrequest,openssl:viewcert,openssl:getcert,openssl:getrevoked
-UPDATE=openssl:editdefaults,openssl:readall,openssl:approve,openssl:deleterequest,openssl:revoke,openssl:deletecert,openssl:putcacert,openssl:editconfigfile
+UPDATE=openssl:editdefaults,openssl:readall,openssl:approve,openssl:deleterequest,openssl:revoke,openssl:deletecert,openssl:putcacert,openssl:generatecacert,openssl:editconfigfile