2015-12-12 22:34:08 +00:00
|
|
|
|
|
|
|
import falcon
|
2015-12-16 17:41:49 +00:00
|
|
|
import logging
|
2017-03-13 11:42:58 +00:00
|
|
|
import json
|
|
|
|
import hashlib
|
|
|
|
from certidude.decorators import csrf_protection
|
2018-03-03 13:54:31 +00:00
|
|
|
from xattr import listxattr, getxattr
|
2018-02-03 11:10:45 +00:00
|
|
|
from .utils import AuthorityHandler
|
2018-05-02 08:11:01 +00:00
|
|
|
from .utils.firewall import login_required, authorize_admin
|
2015-12-12 22:34:08 +00:00
|
|
|
|
2017-04-04 05:02:08 +00:00
|
|
|
logger = logging.getLogger(__name__)
|
2015-12-16 17:41:49 +00:00
|
|
|
|
2018-02-03 11:10:45 +00:00
|
|
|
class SignedCertificateDetailResource(AuthorityHandler):
|
2015-12-12 22:34:08 +00:00
|
|
|
def on_get(self, req, resp, cn):
|
2017-03-13 11:42:58 +00:00
|
|
|
|
|
|
|
preferred_type = req.client_prefers(("application/json", "application/x-pem-file"))
|
2015-12-12 22:34:08 +00:00
|
|
|
try:
|
2018-02-03 10:55:42 +00:00
|
|
|
path, buf, cert, signed, expires = self.authority.get_signed(cn)
|
2016-03-21 21:42:39 +00:00
|
|
|
except EnvironmentError:
|
2017-12-30 13:57:48 +00:00
|
|
|
logger.warning("Failed to serve non-existant certificate %s to %s",
|
2016-03-21 21:42:39 +00:00
|
|
|
cn, req.context.get("remote_addr"))
|
2017-04-25 10:58:21 +00:00
|
|
|
raise falcon.HTTPNotFound()
|
2017-04-25 13:15:39 +00:00
|
|
|
|
|
|
|
if preferred_type == "application/x-pem-file":
|
|
|
|
resp.set_header("Content-Type", "application/x-pem-file")
|
|
|
|
resp.set_header("Content-Disposition", ("attachment; filename=%s.pem" % cn))
|
|
|
|
resp.body = buf
|
2017-12-30 13:57:48 +00:00
|
|
|
logger.debug("Served certificate %s to %s as application/x-pem-file",
|
2017-04-25 13:15:39 +00:00
|
|
|
cn, req.context.get("remote_addr"))
|
|
|
|
elif preferred_type == "application/json":
|
|
|
|
resp.set_header("Content-Type", "application/json")
|
|
|
|
resp.set_header("Content-Disposition", ("attachment; filename=%s.json" % cn))
|
2017-12-30 13:57:48 +00:00
|
|
|
try:
|
|
|
|
signer_username = getxattr(path, "user.signature.username").decode("ascii")
|
|
|
|
except IOError:
|
|
|
|
signer_username = None
|
2018-03-03 13:54:31 +00:00
|
|
|
|
|
|
|
attributes = {}
|
|
|
|
for key in listxattr(path):
|
|
|
|
if key.startswith(b"user.machine."):
|
|
|
|
attributes[key[13:].decode("ascii")] = getxattr(path, key).decode("ascii")
|
|
|
|
|
|
|
|
# TODO: dedup
|
2017-04-25 13:15:39 +00:00
|
|
|
resp.body = json.dumps(dict(
|
|
|
|
common_name = cn,
|
2017-12-30 13:57:48 +00:00
|
|
|
signer = signer_username,
|
2018-04-27 07:48:15 +00:00
|
|
|
serial = "%040x" % cert.serial_number,
|
2018-01-23 13:13:49 +00:00
|
|
|
organizational_unit = cert.subject.native.get("organizational_unit_name"),
|
2017-08-16 20:25:16 +00:00
|
|
|
signed = cert["tbs_certificate"]["validity"]["not_before"].native.strftime("%Y-%m-%dT%H:%M:%S.%f")[:-3] + "Z",
|
|
|
|
expires = cert["tbs_certificate"]["validity"]["not_after"].native.strftime("%Y-%m-%dT%H:%M:%S.%f")[:-3] + "Z",
|
2018-03-03 13:54:31 +00:00
|
|
|
sha256sum = hashlib.sha256(buf).hexdigest(),
|
|
|
|
attributes = attributes or None,
|
|
|
|
lease = None,
|
|
|
|
extensions = dict([
|
|
|
|
(e["extn_id"].native, e["extn_value"].native)
|
|
|
|
for e in cert["tbs_certificate"]["extensions"]
|
2018-04-27 07:48:15 +00:00
|
|
|
if e["extn_id"].native in ("extended_key_usage",)])
|
|
|
|
|
2018-03-03 13:54:31 +00:00
|
|
|
))
|
2017-12-30 13:57:48 +00:00
|
|
|
logger.debug("Served certificate %s to %s as application/json",
|
2017-04-25 13:15:39 +00:00
|
|
|
cn, req.context.get("remote_addr"))
|
2016-03-21 21:42:39 +00:00
|
|
|
else:
|
2017-12-30 13:57:48 +00:00
|
|
|
logger.debug("Client did not accept application/json or application/x-pem-file")
|
2017-04-25 13:15:39 +00:00
|
|
|
raise falcon.HTTPUnsupportedMediaType(
|
|
|
|
"Client did not accept application/json or application/x-pem-file")
|
2015-12-12 22:34:08 +00:00
|
|
|
|
2016-03-27 20:38:14 +00:00
|
|
|
@csrf_protection
|
2015-12-12 22:34:08 +00:00
|
|
|
@login_required
|
|
|
|
@authorize_admin
|
|
|
|
def on_delete(self, req, resp, cn):
|
2018-04-27 07:48:15 +00:00
|
|
|
self.authority.revoke(cn,
|
2018-05-04 08:54:55 +00:00
|
|
|
reason=req.get_param("reason", default="key_compromise"),
|
|
|
|
user=req.context.get("user")
|
|
|
|
)
|
2015-12-12 22:34:08 +00:00
|
|
|
|