Update atst to atat
This commit is contained in:
59
atat/domain/authnid/__init__.py
Normal file
59
atat/domain/authnid/__init__.py
Normal file
@@ -0,0 +1,59 @@
|
||||
from atat.domain.exceptions import UnauthenticatedError, NotFoundError
|
||||
from atat.domain.users import Users
|
||||
from .utils import parse_sdn, email_from_certificate
|
||||
from .crl import CRLRevocationException, CRLInvalidException
|
||||
|
||||
|
||||
class AuthenticationContext:
|
||||
def __init__(self, crl_cache, auth_status, sdn, cert):
|
||||
if None in locals().values():
|
||||
raise UnauthenticatedError(
|
||||
"Missing required authentication context components"
|
||||
)
|
||||
|
||||
self.crl_cache = crl_cache
|
||||
self.auth_status = auth_status
|
||||
self.sdn = sdn
|
||||
self.cert = cert.encode()
|
||||
self._parsed_sdn = None
|
||||
|
||||
def authenticate(self):
|
||||
if not self.auth_status == "SUCCESS":
|
||||
raise UnauthenticatedError("SSL/TLS client authentication failed")
|
||||
|
||||
self._crl_check()
|
||||
|
||||
return True
|
||||
|
||||
def get_user(self):
|
||||
try:
|
||||
return Users.get_by_dod_id(self.parsed_sdn["dod_id"])
|
||||
|
||||
except NotFoundError:
|
||||
email = self._get_user_email()
|
||||
return Users.create(permission_sets=[], email=email, **self.parsed_sdn)
|
||||
|
||||
def _get_user_email(self):
|
||||
try:
|
||||
return email_from_certificate(self.cert)
|
||||
|
||||
# this just means it is not an email certificate; we might choose to
|
||||
# log in that case
|
||||
except ValueError:
|
||||
return None
|
||||
|
||||
def _crl_check(self):
|
||||
try:
|
||||
self.crl_cache.crl_check(self.cert)
|
||||
except CRLRevocationException as exc:
|
||||
raise UnauthenticatedError("CRL check failed. " + str(exc))
|
||||
|
||||
@property
|
||||
def parsed_sdn(self):
|
||||
if not self._parsed_sdn:
|
||||
try:
|
||||
self._parsed_sdn = parse_sdn(self.sdn)
|
||||
except ValueError as exc:
|
||||
raise UnauthenticatedError(str(exc))
|
||||
|
||||
return self._parsed_sdn
|
||||
186
atat/domain/authnid/crl/__init__.py
Normal file
186
atat/domain/authnid/crl/__init__.py
Normal file
@@ -0,0 +1,186 @@
|
||||
import os
|
||||
import re
|
||||
import hashlib
|
||||
import logging
|
||||
|
||||
from OpenSSL import crypto, SSL
|
||||
from flask import current_app as app
|
||||
|
||||
from .util import load_crl_locations_cache, serialize_crl_locations_cache, CRL_LIST
|
||||
|
||||
# error codes from OpenSSL: https://github.com/openssl/openssl/blob/2c75f03b39de2fa7d006bc0f0d7c58235a54d9bb/include/openssl/x509_vfy.h#L111
|
||||
CRL_EXPIRED_ERROR_CODE = 12
|
||||
|
||||
|
||||
def get_common_name(x509_name_object):
|
||||
for comp in x509_name_object.get_components():
|
||||
if comp[0] == b"CN":
|
||||
return comp[1].decode()
|
||||
|
||||
|
||||
class CRLRevocationException(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class CRLInvalidException(Exception):
|
||||
# CRL expired
|
||||
# CRL missing
|
||||
pass
|
||||
|
||||
|
||||
class CRLInterface:
|
||||
def __init__(self, *args, logger=None, **kwargs):
|
||||
self.logger = logger
|
||||
|
||||
def _log(self, message, level=logging.INFO):
|
||||
if self.logger:
|
||||
self.logger.log(level, message, extra={"tags": ["authorization", "crl"]})
|
||||
|
||||
def crl_check(self, cert):
|
||||
raise NotImplementedError()
|
||||
|
||||
|
||||
class NoOpCRLCache(CRLInterface):
|
||||
def _get_cn(self, cert):
|
||||
try:
|
||||
parsed = crypto.load_certificate(crypto.FILETYPE_PEM, cert)
|
||||
return get_common_name(parsed.get_subject())
|
||||
except crypto.Error:
|
||||
pass
|
||||
|
||||
return "unknown"
|
||||
|
||||
def crl_check(self, cert):
|
||||
cn = self._get_cn(cert)
|
||||
self._log(
|
||||
"Did not perform CRL validation for certificate with Common Name '{}'".format(
|
||||
cn
|
||||
)
|
||||
)
|
||||
|
||||
return True
|
||||
|
||||
|
||||
class CRLCache(CRLInterface):
|
||||
|
||||
_PEM_RE = re.compile(
|
||||
b"-----BEGIN CERTIFICATE-----\r?.+?\r?-----END CERTIFICATE-----\r?\n?",
|
||||
re.DOTALL,
|
||||
)
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
root_location,
|
||||
crl_dir,
|
||||
store_class=crypto.X509Store,
|
||||
logger=None,
|
||||
crl_list=CRL_LIST,
|
||||
):
|
||||
self._crl_dir = crl_dir
|
||||
self.logger = logger
|
||||
self.store_class = store_class
|
||||
self.certificate_authorities = {}
|
||||
self.crl_list = crl_list
|
||||
self._load_roots(root_location)
|
||||
self._build_crl_cache()
|
||||
|
||||
def _get_store(self, cert):
|
||||
return self._build_store(cert.get_issuer())
|
||||
|
||||
def _load_roots(self, root_location):
|
||||
with open(root_location, "rb") as f:
|
||||
for raw_ca in self._parse_roots(f.read()):
|
||||
ca = crypto.load_certificate(crypto.FILETYPE_PEM, raw_ca)
|
||||
self.certificate_authorities[ca.get_subject().der()] = ca
|
||||
|
||||
def _parse_roots(self, root_str):
|
||||
return [match.group(0) for match in self._PEM_RE.finditer(root_str)]
|
||||
|
||||
def _build_crl_cache(self):
|
||||
try:
|
||||
self.crl_cache = load_crl_locations_cache(self._crl_dir)
|
||||
except FileNotFoundError:
|
||||
self.crl_cache = serialize_crl_locations_cache(
|
||||
self._crl_dir, crl_list=self.crl_list
|
||||
)
|
||||
|
||||
def _load_crl(self, crl_location):
|
||||
with open(crl_location, "rb") as crl_file:
|
||||
try:
|
||||
return crypto.load_crl(crypto.FILETYPE_ASN1, crl_file.read())
|
||||
except crypto.Error:
|
||||
self._log(
|
||||
"Could not load CRL at location {}".format(crl_location),
|
||||
level=logging.WARNING,
|
||||
)
|
||||
|
||||
def _build_store(self, issuer):
|
||||
store = self.store_class()
|
||||
self._log("STORE ID: {}. Building store.".format(id(store)))
|
||||
store.set_flags(crypto.X509StoreFlags.CRL_CHECK)
|
||||
crl_location = self.crl_cache.get(issuer.der())
|
||||
issuer_name = get_common_name(issuer)
|
||||
|
||||
if not crl_location:
|
||||
raise CRLInvalidException(
|
||||
"Could not find matching CRL for issuer with Common Name {}".format(
|
||||
issuer_name
|
||||
)
|
||||
)
|
||||
|
||||
crl = self._load_crl(crl_location)
|
||||
store.add_crl(crl)
|
||||
|
||||
self._log(
|
||||
"STORE ID: {}. Adding CRL with issuer Common Name {}".format(
|
||||
id(store), issuer_name
|
||||
)
|
||||
)
|
||||
|
||||
store = self._add_certificate_chain_to_store(store, crl.get_issuer())
|
||||
return store
|
||||
|
||||
# this _should_ happen just twice for the DoD PKI (intermediary, root) but
|
||||
# theoretically it can build a longer certificate chain
|
||||
|
||||
def _add_certificate_chain_to_store(self, store, issuer):
|
||||
ca = self.certificate_authorities.get(issuer.der())
|
||||
store.add_cert(ca)
|
||||
self._log(
|
||||
"STORE ID: {}. Adding CA with subject {}".format(
|
||||
id(store), ca.get_subject()
|
||||
)
|
||||
)
|
||||
|
||||
if issuer == ca.get_issuer():
|
||||
# i.e., it is the root CA and we are at the end of the chain
|
||||
return store
|
||||
|
||||
else:
|
||||
return self._add_certificate_chain_to_store(store, ca.get_issuer())
|
||||
|
||||
def crl_check(self, cert):
|
||||
parsed = crypto.load_certificate(crypto.FILETYPE_PEM, cert)
|
||||
store = self._get_store(parsed)
|
||||
context = crypto.X509StoreContext(store, parsed)
|
||||
try:
|
||||
context.verify_certificate()
|
||||
return True
|
||||
|
||||
except crypto.X509StoreContextError as err:
|
||||
if err.args[0][0] == CRL_EXPIRED_ERROR_CODE:
|
||||
if app.config.get("CRL_FAIL_OPEN"):
|
||||
self._log(
|
||||
"Encountered expired CRL for certificate with CN {} and issuer CN {}, failing open.".format(
|
||||
parsed.get_subject().CN, parsed.get_issuer().CN
|
||||
),
|
||||
level=logging.WARNING,
|
||||
)
|
||||
return True
|
||||
else:
|
||||
raise CRLInvalidException("CRL expired. Args: {}".format(err.args))
|
||||
raise CRLRevocationException(
|
||||
"Certificate revoked or errored. Error: {}. Args: {}".format(
|
||||
type(err), err.args
|
||||
)
|
||||
)
|
||||
367
atat/domain/authnid/crl/util.py
Normal file
367
atat/domain/authnid/crl/util.py
Normal file
@@ -0,0 +1,367 @@
|
||||
import json
|
||||
import os
|
||||
import re
|
||||
|
||||
import pendulum
|
||||
import requests
|
||||
|
||||
|
||||
class CRLNotFoundError(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class CRLParseError(Exception):
|
||||
pass
|
||||
|
||||
|
||||
MODIFIED_TIME_BUFFER = 15 * 60
|
||||
|
||||
|
||||
CRL_LIST = [
|
||||
(
|
||||
"https://crl.gds.disa.mil/crl/DODROOTCA2.crl",
|
||||
"305b310b300906035504061302555331183016060355040a130f552e532e20476f7665726e6d656e74310c300a060355040b1303446f44310c300a060355040b1303504b49311630140603550403130d446f4420526f6f742043412032", # pragma: allowlist secret
|
||||
),
|
||||
(
|
||||
"https://crl.gds.disa.mil/crl/DODROOTCA3.crl",
|
||||
"305b310b300906035504061302555331183016060355040a130f552e532e20476f7665726e6d656e74310c300a060355040b1303446f44310c300a060355040b1303504b49311630140603550403130d446f4420526f6f742043412033", # pragma: allowlist secret
|
||||
),
|
||||
(
|
||||
"https://crl.gds.disa.mil/crl/DODROOTCA4.crl",
|
||||
"305b310b300906035504061302555331183016060355040a130f552e532e20476f7665726e6d656e74310c300a060355040b1303446f44310c300a060355040b1303504b49311630140603550403130d446f4420526f6f742043412034", # pragma: allowlist secret
|
||||
),
|
||||
(
|
||||
"https://crl.gds.disa.mil/crl/DODROOTCA5.crl",
|
||||
"305b310b300906035504061302555331183016060355040a130f552e532e20476f7665726e6d656e74310c300a060355040b1303446f44310c300a060355040b1303504b49311630140603550403130d446f4420526f6f742043412035", # pragma: allowlist secret
|
||||
),
|
||||
(
|
||||
"https://crl.gds.disa.mil/crl/DODIDCA_33.crl",
|
||||
"305a310b300906035504061302555331183016060355040a130f552e532e20476f7665726e6d656e74310c300a060355040b1303446f44310c300a060355040b1303504b49311530130603550403130c444f442049442043412d3333", # pragma: allowlist secret
|
||||
),
|
||||
(
|
||||
"https://crl.gds.disa.mil/crl/DODIDCA_34.crl",
|
||||
"305a310b300906035504061302555331183016060355040a130f552e532e20476f7665726e6d656e74310c300a060355040b1303446f44310c300a060355040b1303504b49311530130603550403130c444f442049442043412d3334", # pragma: allowlist secret
|
||||
),
|
||||
(
|
||||
"https://crl.gds.disa.mil/crl/DODIDSWCA_35.crl",
|
||||
"305d310b300906035504061302555331183016060355040a130f552e532e20476f7665726e6d656e74310c300a060355040b1303446f44310c300a060355040b1303504b49311830160603550403130f444f442049442053572043412d3335", # pragma: allowlist secret
|
||||
),
|
||||
(
|
||||
"https://crl.gds.disa.mil/crl/DODIDSWCA_36.crl",
|
||||
"305d310b300906035504061302555331183016060355040a130f552e532e20476f7665726e6d656e74310c300a060355040b1303446f44310c300a060355040b1303504b49311830160603550403130f444f442049442053572043412d3336", # pragma: allowlist secret
|
||||
),
|
||||
(
|
||||
"https://crl.gds.disa.mil/crl/DODIDSWCA_37.crl",
|
||||
"305d310b300906035504061302555331183016060355040a130f552e532e20476f7665726e6d656e74310c300a060355040b1303446f44310c300a060355040b1303504b49311830160603550403130f444f442049442053572043412d3337", # pragma: allowlist secret
|
||||
),
|
||||
(
|
||||
"https://crl.gds.disa.mil/crl/DODIDSWCA_38.crl",
|
||||
"305d310b300906035504061302555331183016060355040a130f552e532e20476f7665726e6d656e74310c300a060355040b1303446f44310c300a060355040b1303504b49311830160603550403130f444f442049442053572043412d3338", # pragma: allowlist secret
|
||||
),
|
||||
(
|
||||
"https://crl.gds.disa.mil/crl/DODIDCA_39.crl",
|
||||
"305a310b300906035504061302555331183016060355040a130f552e532e20476f7665726e6d656e74310c300a060355040b1303446f44310c300a060355040b1303504b49311530130603550403130c444f442049442043412d3339", # pragma: allowlist secret
|
||||
),
|
||||
(
|
||||
"https://crl.gds.disa.mil/crl/DODIDCA_40.crl",
|
||||
"305a310b300906035504061302555331183016060355040a130f552e532e20476f7665726e6d656e74310c300a060355040b1303446f44310c300a060355040b1303504b49311530130603550403130c444f442049442043412d3430", # pragma: allowlist secret
|
||||
),
|
||||
(
|
||||
"https://crl.gds.disa.mil/crl/DODIDCA_41.crl",
|
||||
"305a310b300906035504061302555331183016060355040a130f552e532e20476f7665726e6d656e74310c300a060355040b1303446f44310c300a060355040b1303504b49311530130603550403130c444f442049442043412d3431", # pragma: allowlist secret
|
||||
),
|
||||
(
|
||||
"https://crl.gds.disa.mil/crl/DODIDCA_42.crl",
|
||||
"305a310b300906035504061302555331183016060355040a130f552e532e20476f7665726e6d656e74310c300a060355040b1303446f44310c300a060355040b1303504b49311530130603550403130c444f442049442043412d3432", # pragma: allowlist secret
|
||||
),
|
||||
(
|
||||
"https://crl.gds.disa.mil/crl/DODIDCA_43.crl",
|
||||
"305a310b300906035504061302555331183016060355040a130f552e532e20476f7665726e6d656e74310c300a060355040b1303446f44310c300a060355040b1303504b49311530130603550403130c444f442049442043412d3433", # pragma: allowlist secret
|
||||
),
|
||||
(
|
||||
"https://crl.gds.disa.mil/crl/DODIDCA_44.crl",
|
||||
"305a310b300906035504061302555331183016060355040a130f552e532e20476f7665726e6d656e74310c300a060355040b1303446f44310c300a060355040b1303504b49311530130603550403130c444f442049442043412d3434", # pragma: allowlist secret
|
||||
),
|
||||
(
|
||||
"https://crl.gds.disa.mil/crl/DODIDSWCA_45.crl",
|
||||
"305d310b300906035504061302555331183016060355040a130f552e532e20476f7665726e6d656e74310c300a060355040b1303446f44310c300a060355040b1303504b49311830160603550403130f444f442049442053572043412d3435", # pragma: allowlist secret
|
||||
),
|
||||
(
|
||||
"https://crl.gds.disa.mil/crl/DODIDSWCA_46.crl",
|
||||
"305d310b300906035504061302555331183016060355040a130f552e532e20476f7665726e6d656e74310c300a060355040b1303446f44310c300a060355040b1303504b49311830160603550403130f444f442049442053572043412d3436", # pragma: allowlist secret
|
||||
),
|
||||
(
|
||||
"https://crl.gds.disa.mil/crl/DODIDSWCA_47.crl",
|
||||
"305d310b300906035504061302555331183016060355040a130f552e532e20476f7665726e6d656e74310c300a060355040b1303446f44310c300a060355040b1303504b49311830160603550403130f444f442049442053572043412d3437", # pragma: allowlist secret
|
||||
),
|
||||
(
|
||||
"https://crl.gds.disa.mil/crl/DODIDSWCA_48.crl",
|
||||
"305d310b300906035504061302555331183016060355040a130f552e532e20476f7665726e6d656e74310c300a060355040b1303446f44310c300a060355040b1303504b49311830160603550403130f444f442049442053572043412d3438", # pragma: allowlist secret
|
||||
),
|
||||
(
|
||||
"https://crl.gds.disa.mil/crl/DODIDCA_49.crl",
|
||||
"305a310b300906035504061302555331183016060355040a0c0f552e532e20476f7665726e6d656e74310c300a060355040b0c03446f44310c300a060355040b0c03504b493115301306035504030c0c444f442049442043412d3439", # pragma: allowlist secret
|
||||
),
|
||||
(
|
||||
"https://crl.gds.disa.mil/crl/DODIDCA_50.crl",
|
||||
"305a310b300906035504061302555331183016060355040a0c0f552e532e20476f7665726e6d656e74310c300a060355040b0c03446f44310c300a060355040b0c03504b493115301306035504030c0c444f442049442043412d3530", # pragma: allowlist secret
|
||||
),
|
||||
(
|
||||
"https://crl.gds.disa.mil/crl/DODIDCA_51.crl",
|
||||
"305a310b300906035504061302555331183016060355040a0c0f552e532e20476f7665726e6d656e74310c300a060355040b0c03446f44310c300a060355040b0c03504b493115301306035504030c0c444f442049442043412d3531", # pragma: allowlist secret
|
||||
),
|
||||
(
|
||||
"https://crl.gds.disa.mil/crl/DODIDCA_52.crl",
|
||||
"305a310b300906035504061302555331183016060355040a0c0f552e532e20476f7665726e6d656e74310c300a060355040b0c03446f44310c300a060355040b0c03504b493115301306035504030c0c444f442049442043412d3532", # pragma: allowlist secret
|
||||
),
|
||||
(
|
||||
"https://crl.gds.disa.mil/crl/DODIDCA_59.crl",
|
||||
"305a310b300906035504061302555331183016060355040a130f552e532e20476f7665726e6d656e74310c300a060355040b1303446f44310c300a060355040b1303504b49311530130603550403130c444f442049442043412d3539", # pragma: allowlist secret
|
||||
),
|
||||
(
|
||||
"https://crl.gds.disa.mil/crl/DODSWCA_53.crl",
|
||||
"305a310b300906035504061302555331183016060355040a0c0f552e532e20476f7665726e6d656e74310c300a060355040b0c03446f44310c300a060355040b0c03504b493115301306035504030c0c444f442053572043412d3533", # pragma: allowlist secret
|
||||
),
|
||||
(
|
||||
"https://crl.gds.disa.mil/crl/DODSWCA_54.crl",
|
||||
"305a310b300906035504061302555331183016060355040a0c0f552e532e20476f7665726e6d656e74310c300a060355040b0c03446f44310c300a060355040b0c03504b493115301306035504030c0c444f442053572043412d3534", # pragma: allowlist secret
|
||||
),
|
||||
(
|
||||
"https://crl.gds.disa.mil/crl/DODSWCA_55.crl",
|
||||
"305a310b300906035504061302555331183016060355040a0c0f552e532e20476f7665726e6d656e74310c300a060355040b0c03446f44310c300a060355040b0c03504b493115301306035504030c0c444f442053572043412d3535", # pragma: allowlist secret
|
||||
),
|
||||
(
|
||||
"https://crl.gds.disa.mil/crl/DODSWCA_56.crl",
|
||||
"305a310b300906035504061302555331183016060355040a0c0f552e532e20476f7665726e6d656e74310c300a060355040b0c03446f44310c300a060355040b0c03504b493115301306035504030c0c444f442053572043412d3536", # pragma: allowlist secret
|
||||
),
|
||||
(
|
||||
"https://crl.gds.disa.mil/crl/DODSWCA_57.crl",
|
||||
"305a310b300906035504061302555331183016060355040a130f552e532e20476f7665726e6d656e74310c300a060355040b1303446f44310c300a060355040b1303504b49311530130603550403130c444f442053572043412d3537", # pragma: allowlist secret
|
||||
),
|
||||
(
|
||||
"https://crl.gds.disa.mil/crl/DODSWCA_58.crl",
|
||||
"305a310b300906035504061302555331183016060355040a130f552e532e20476f7665726e6d656e74310c300a060355040b1303446f44310c300a060355040b1303504b49311530130603550403130c444f442053572043412d3538", # pragma: allowlist secret
|
||||
),
|
||||
(
|
||||
"https://crl.gds.disa.mil/crl/DODSWCA_60.crl",
|
||||
"305a310b300906035504061302555331183016060355040a130f552e532e20476f7665726e6d656e74310c300a060355040b1303446f44310c300a060355040b1303504b49311530130603550403130c444f442053572043412d3630", # pragma: allowlist secret
|
||||
),
|
||||
(
|
||||
"https://crl.gds.disa.mil/crl/DODSWCA_61.crl",
|
||||
"305a310b300906035504061302555331183016060355040a130f552e532e20476f7665726e6d656e74310c300a060355040b1303446f44310c300a060355040b1303504b49311530130603550403130c444f442053572043412d3631", # pragma: allowlist secret
|
||||
),
|
||||
(
|
||||
"https://crl.gds.disa.mil/crl/DODEMAILCA_33.crl",
|
||||
"305d310b300906035504061302555331183016060355040a130f552e532e20476f7665726e6d656e74310c300a060355040b1303446f44310c300a060355040b1303504b49311830160603550403130f444f4420454d41494c2043412d3333", # pragma: allowlist secret
|
||||
),
|
||||
(
|
||||
"https://crl.gds.disa.mil/crl/DODEMAILCA_34.crl",
|
||||
"305d310b300906035504061302555331183016060355040a130f552e532e20476f7665726e6d656e74310c300a060355040b1303446f44310c300a060355040b1303504b49311830160603550403130f444f4420454d41494c2043412d3334", # pragma: allowlist secret
|
||||
),
|
||||
(
|
||||
"https://crl.gds.disa.mil/crl/DODEMAILCA_39.crl",
|
||||
"305d310b300906035504061302555331183016060355040a130f552e532e20476f7665726e6d656e74310c300a060355040b1303446f44310c300a060355040b1303504b49311830160603550403130f444f4420454d41494c2043412d3339", # pragma: allowlist secret
|
||||
),
|
||||
(
|
||||
"https://crl.gds.disa.mil/crl/DODEMAILCA_40.crl",
|
||||
"305d310b300906035504061302555331183016060355040a130f552e532e20476f7665726e6d656e74310c300a060355040b1303446f44310c300a060355040b1303504b49311830160603550403130f444f4420454d41494c2043412d3430", # pragma: allowlist secret
|
||||
),
|
||||
(
|
||||
"https://crl.gds.disa.mil/crl/DODEMAILCA_41.crl",
|
||||
"305d310b300906035504061302555331183016060355040a130f552e532e20476f7665726e6d656e74310c300a060355040b1303446f44310c300a060355040b1303504b49311830160603550403130f444f4420454d41494c2043412d3431", # pragma: allowlist secret
|
||||
),
|
||||
(
|
||||
"https://crl.gds.disa.mil/crl/DODEMAILCA_42.crl",
|
||||
"305d310b300906035504061302555331183016060355040a130f552e532e20476f7665726e6d656e74310c300a060355040b1303446f44310c300a060355040b1303504b49311830160603550403130f444f4420454d41494c2043412d3432", # pragma: allowlist secret
|
||||
),
|
||||
(
|
||||
"https://crl.gds.disa.mil/crl/DODEMAILCA_43.crl",
|
||||
"305d310b300906035504061302555331183016060355040a130f552e532e20476f7665726e6d656e74310c300a060355040b1303446f44310c300a060355040b1303504b49311830160603550403130f444f4420454d41494c2043412d3433", # pragma: allowlist secret
|
||||
),
|
||||
(
|
||||
"https://crl.gds.disa.mil/crl/DODEMAILCA_44.crl",
|
||||
"305d310b300906035504061302555331183016060355040a130f552e532e20476f7665726e6d656e74310c300a060355040b1303446f44310c300a060355040b1303504b49311830160603550403130f444f4420454d41494c2043412d3434", # pragma: allowlist secret
|
||||
),
|
||||
(
|
||||
"https://crl.gds.disa.mil/crl/DODEMAILCA_49.crl",
|
||||
"305d310b300906035504061302555331183016060355040a0c0f552e532e20476f7665726e6d656e74310c300a060355040b0c03446f44310c300a060355040b0c03504b493118301606035504030c0f444f4420454d41494c2043412d3439", # pragma: allowlist secret
|
||||
),
|
||||
(
|
||||
"https://crl.gds.disa.mil/crl/DODEMAILCA_50.crl",
|
||||
"305d310b300906035504061302555331183016060355040a0c0f552e532e20476f7665726e6d656e74310c300a060355040b0c03446f44310c300a060355040b0c03504b493118301606035504030c0f444f4420454d41494c2043412d3530", # pragma: allowlist secret
|
||||
),
|
||||
(
|
||||
"https://crl.gds.disa.mil/crl/DODEMAILCA_51.crl",
|
||||
"305d310b300906035504061302555331183016060355040a0c0f552e532e20476f7665726e6d656e74310c300a060355040b0c03446f44310c300a060355040b0c03504b493118301606035504030c0f444f4420454d41494c2043412d3531", # pragma: allowlist secret
|
||||
),
|
||||
(
|
||||
"https://crl.gds.disa.mil/crl/DODEMAILCA_52.crl",
|
||||
"305d310b300906035504061302555331183016060355040a0c0f552e532e20476f7665726e6d656e74310c300a060355040b0c03446f44310c300a060355040b0c03504b493118301606035504030c0f444f4420454d41494c2043412d3532", # pragma: allowlist secret
|
||||
),
|
||||
(
|
||||
"https://crl.gds.disa.mil/crl/DODEMAILCA_59.crl",
|
||||
"305d310b300906035504061302555331183016060355040a130f552e532e20476f7665726e6d656e74310c300a060355040b1303446f44310c300a060355040b1303504b49311830160603550403130f444f4420454d41494c2043412d3539", # pragma: allowlist secret
|
||||
),
|
||||
(
|
||||
"https://crl.gds.disa.mil/crl/DODINTEROPERABILITYROOTCA1.crl",
|
||||
"306c310b300906035504061302555331183016060355040a130f552e532e20476f7665726e6d656e74310c300a060355040b1303446f44310c300a060355040b1303504b49312730250603550403131e446f4420496e7465726f7065726162696c69747920526f6f742043412031", # pragma: allowlist secret
|
||||
),
|
||||
(
|
||||
"https://crl.gds.disa.mil/crl/DODINTEROPERABILITYROOTCA2.crl",
|
||||
"306c310b300906035504061302555331183016060355040a130f552e532e20476f7665726e6d656e74310c300a060355040b1303446f44310c300a060355040b1303504b49312730250603550403131e446f4420496e7465726f7065726162696c69747920526f6f742043412032", # pragma: allowlist secret
|
||||
),
|
||||
(
|
||||
"https://crl.gds.disa.mil/crl/USDODCCEBINTEROPERABILITYROOTCA1.crl",
|
||||
"3074310b300906035504061302555331183016060355040a130f552e532e20476f7665726e6d656e74310c300a060355040b1303446f44310c300a060355040b1303504b49312f302d06035504031326555320446f44204343454220496e7465726f7065726162696c69747920526f6f742043412031", # pragma: allowlist secret
|
||||
),
|
||||
(
|
||||
"https://crl.gds.disa.mil/crl/USDODCCEBINTEROPERABILITYROOTCA2.crl",
|
||||
"3074310b300906035504061302555331183016060355040a130f552e532e20476f7665726e6d656e74310c300a060355040b1303446f44310c300a060355040b1303504b49312f302d06035504031326555320446f44204343454220496e7465726f7065726162696c69747920526f6f742043412032", # pragma: allowlist secret
|
||||
),
|
||||
(
|
||||
"https://crl.gds.disa.mil/crl/DODNIPRINTERNALNPEROOTCA1.crl",
|
||||
"3075310b300906035504061302555331183016060355040a130f552e532e20476f7665726e6d656e74310c300a060355040b1303446f4431143012060355040b130b496e7465726e616c4e5045312830260603550403131f446f44204e49505220496e7465726e616c204e504520526f6f742043412031", # pragma: allowlist secret
|
||||
),
|
||||
(
|
||||
"https://crl.gds.disa.mil/crl/DODNPEROOTCA1.crl",
|
||||
"305f310b300906035504061302555331183016060355040a130f552e532e20476f7665726e6d656e74310c300a060355040b1303446f44310c300a060355040b1303504b49311a301806035504031311446f44204e504520526f6f742043412031", # pragma: allowlist secret
|
||||
),
|
||||
(
|
||||
"https://crl.gds.disa.mil/crl/DMDNSIGNINGCA_1.crl",
|
||||
"305f310b300906035504061302555331183016060355040a130f552e532e20476f7665726e6d656e74310c300a060355040b1303446f44310c300a060355040b1303504b49311a301806035504031311444d444e205369676e696e672043412d31", # pragma: allowlist secret
|
||||
),
|
||||
(
|
||||
"https://crl.gds.disa.mil/crl/DODWCFROOTCA1.crl",
|
||||
"3063310b300906035504061302555331183016060355040a130f552e532e20476f7665726e6d656e74310c300a060355040b1303446f443110300e060355040b130757434620504b49311a301806035504031311446f442057434620526f6f742043412031", # pragma: allowlist secret
|
||||
),
|
||||
]
|
||||
|
||||
|
||||
JSON_CACHE = "crl_locations.json"
|
||||
|
||||
|
||||
def _deserialize_cache_items(cache):
|
||||
return {bytes.fromhex(der): data for (der, data) in cache.items()}
|
||||
|
||||
|
||||
def load_crl_locations_cache(crl_dir):
|
||||
json_location = "{}/{}".format(crl_dir, JSON_CACHE)
|
||||
with open(json_location, "r") as json_file:
|
||||
cache = json.load(json_file)
|
||||
return _deserialize_cache_items(cache)
|
||||
|
||||
|
||||
def serialize_crl_locations_cache(crl_dir, crl_list=CRL_LIST):
|
||||
crl_cache = {}
|
||||
for crl_uri, crl_issuer in crl_list:
|
||||
crl_path = crl_local_path(crl_dir, crl_uri)
|
||||
if os.path.isfile(crl_path):
|
||||
crl_cache[crl_issuer] = crl_path
|
||||
|
||||
json_location = "{}/{}".format(crl_dir, JSON_CACHE)
|
||||
with open(json_location, "w") as json_file:
|
||||
json.dump(crl_cache, json_file)
|
||||
|
||||
return {bytes.fromhex(k): v for k, v in crl_cache.items()}
|
||||
|
||||
|
||||
def crl_local_path(out_dir, crl_location):
|
||||
name = re.split("/", crl_location)[-1]
|
||||
crl = os.path.join(out_dir, name)
|
||||
return crl
|
||||
|
||||
|
||||
def existing_crl_modification_time(crl):
|
||||
if os.path.exists(crl):
|
||||
prev_time = os.path.getmtime(crl)
|
||||
buffered = prev_time + MODIFIED_TIME_BUFFER
|
||||
mod_time = prev_time if pendulum.now().timestamp() < buffered else buffered
|
||||
dt = pendulum.from_timestamp(mod_time, tz="UTC")
|
||||
return dt.format("ddd, DD MMM YYYY HH:mm:ss zz")
|
||||
|
||||
else:
|
||||
return False
|
||||
|
||||
|
||||
def write_crl(out_dir, target_dir, crl_location):
|
||||
crl = crl_local_path(out_dir, crl_location)
|
||||
existing = crl_local_path(target_dir, crl_location)
|
||||
options = {"stream": True}
|
||||
mod_time = existing_crl_modification_time(existing)
|
||||
if mod_time:
|
||||
options["headers"] = {"If-Modified-Since": mod_time}
|
||||
|
||||
with requests.get(crl_location, **options) as response:
|
||||
if response.status_code > 399:
|
||||
raise CRLNotFoundError()
|
||||
|
||||
if response.status_code == 304:
|
||||
return (False, existing)
|
||||
|
||||
with open(crl, "wb") as crl_file:
|
||||
for chunk in response.iter_content(chunk_size=1024):
|
||||
if chunk:
|
||||
crl_file.write(chunk)
|
||||
|
||||
return (True, existing)
|
||||
|
||||
|
||||
def remove_bad_crl(out_dir, crl_location):
|
||||
crl = crl_local_path(out_dir, crl_location)
|
||||
os.remove(crl)
|
||||
|
||||
|
||||
def log_error(logger, crl_location):
|
||||
if logger:
|
||||
logger.error(
|
||||
"Error downloading {}, removing file and continuing anyway".format(
|
||||
crl_location
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
def refresh_crl(out_dir, target_dir, crl_uri, logger):
|
||||
logger.info("updating CRL from {}".format(crl_uri))
|
||||
try:
|
||||
was_updated, crl_path = write_crl(out_dir, target_dir, crl_uri)
|
||||
if was_updated:
|
||||
logger.info("successfully synced CRL from {}".format(crl_uri))
|
||||
else:
|
||||
logger.info("no updates for CRL from {}".format(crl_uri))
|
||||
|
||||
return crl_path
|
||||
except requests.exceptions.ChunkedEncodingError:
|
||||
log_error(logger, crl_uri)
|
||||
remove_bad_crl(out_dir, crl_uri)
|
||||
except CRLNotFoundError:
|
||||
log_error(logger, crl_uri)
|
||||
|
||||
|
||||
def sync_crls(tmp_location, final_location):
|
||||
crl_cache = {}
|
||||
for crl_uri, crl_issuer in CRL_LIST:
|
||||
crl_path = refresh_crl(tmp_location, final_location, crl_uri, logger)
|
||||
crl_cache[crl_issuer] = crl_path
|
||||
|
||||
json_location = "{}/{}".format(final_location, JSON_CACHE)
|
||||
with open(json_location, "w") as json_file:
|
||||
json.dump(crl_cache, json_file)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
import sys
|
||||
import logging
|
||||
|
||||
logging.basicConfig(
|
||||
level=logging.INFO, format="[%(asctime)s]:%(levelname)s: %(message)s"
|
||||
)
|
||||
logger = logging.getLogger()
|
||||
logger.info("Updating CRLs")
|
||||
try:
|
||||
tmp_location = sys.argv[1]
|
||||
final_location = sys.argv[2]
|
||||
sync_crls(tmp_location, final_location)
|
||||
except Exception as err:
|
||||
logger.exception("Fatal error encountered, stopping")
|
||||
sys.exit(1)
|
||||
logger.info("Finished updating CRLs")
|
||||
39
atat/domain/authnid/utils.py
Normal file
39
atat/domain/authnid/utils.py
Normal file
@@ -0,0 +1,39 @@
|
||||
import re
|
||||
|
||||
import cryptography.x509 as x509
|
||||
from cryptography.hazmat.backends import default_backend
|
||||
|
||||
|
||||
def parse_sdn(sdn):
|
||||
try:
|
||||
parts = sdn.split(",")
|
||||
cn_string = [piece for piece in parts if re.match("^CN=", piece)][0]
|
||||
cn = cn_string.split("=")[-1]
|
||||
info = cn.split(".")
|
||||
return {"last_name": info[0], "first_name": info[1], "dod_id": info[-1]}
|
||||
|
||||
except (IndexError, AttributeError):
|
||||
raise ValueError("'{}' is not a valid SDN".format(sdn))
|
||||
|
||||
|
||||
def email_from_certificate(cert_file):
|
||||
cert = x509.load_pem_x509_certificate(cert_file, default_backend())
|
||||
try:
|
||||
ext = cert.extensions.get_extension_for_class(x509.SubjectAlternativeName)
|
||||
email = ext.value.get_values_for_type(x509.RFC822Name)
|
||||
if email:
|
||||
return email[0]
|
||||
|
||||
else:
|
||||
raise ValueError(
|
||||
"No email available for certificate with serial {}".format(
|
||||
cert.serial_number
|
||||
)
|
||||
)
|
||||
|
||||
except x509.extensions.ExtensionNotFound:
|
||||
raise ValueError(
|
||||
"No subjectAltName available for certificate with serial {}".format(
|
||||
cert.serial_number
|
||||
)
|
||||
)
|
||||
Reference in New Issue
Block a user