Skip to content

Commit e24d832

Browse files
committed
feat: verify incoming CSR subject matches what we expect
1 parent 68a2398 commit e24d832

File tree

3 files changed

+29
-2
lines changed

3 files changed

+29
-2
lines changed

src/rasenmaeher_api/db/enrollments.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
from .errors import ForbiddenOperation, CallsignReserved, NotFound, Deleted, PoolInactive
1919
from ..rmsettings import RMSettings
2020
from .engine import EngineWrapper
21+
from ..web.api.utils.csr_utils import verify_csr
2122

2223
LOGGER = logging.getLogger(__name__)
2324
CODE_ALPHABET = string.ascii_uppercase + string.digits
@@ -283,7 +284,8 @@ async def create_for_callsign(
283284
"""Create a new one with random code for the callsign"""
284285
if callsign in RMSettings.singleton().valid_product_cns:
285286
raise CallsignReserved("Using product CNs as callsigns is forbidden")
286-
# FIXME: Verify the CSR has the callsign as CN
287+
if csr and not verify_csr(csr, callsign):
288+
raise CallsignReserved("CSR CN must match callsign")
287289
with EngineWrapper.get_session() as session:
288290
try:
289291
await Enrollment.by_callsign(callsign)

src/rasenmaeher_api/db/people.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
from ..rmsettings import RMSettings
2929
from ..kchelpers import KCClient, KCUserData
3030
from .engine import EngineWrapper
31+
from ..web.api.utils.csr_utils import verify_csr
3132

3233
LOGGER = logging.getLogger(__name__)
3334

@@ -94,7 +95,8 @@ async def create_with_cert(
9495
cls, callsign: str, extra: Optional[Dict[str, Any]] = None, csrpem: Optional[str] = None
9596
) -> "Person":
9697
"""Create the cert etc and save the person"""
97-
# FIXME: Verify the CSR has the callsign as CN
98+
if csrpem and not verify_csr(csrpem, callsign):
99+
raise CallsignReserved("CSR CN must match callsign")
98100
cnf = RMSettings.singleton()
99101
if callsign in cnf.valid_product_cns:
100102
raise CallsignReserved("Using product CNs as callsigns is forbidden")
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
"""Utils for checking CSR:s etc"""
2+
3+
from cryptography import x509
4+
from libadvian.binpackers import ensure_utf8
5+
6+
from rasenmaeher_api.web.api.utils.views import LOGGER
7+
8+
9+
# FIXME: This should be part of libpvarki
10+
11+
12+
def verify_csr(csrpem: str, callsign: str) -> bool:
13+
"""Verify CSR matches our rules for CN/DN for the given callsign"""
14+
csr = x509.load_pem_x509_csr(ensure_utf8(csrpem))
15+
dn = csr.subject.rfc4514_string()
16+
LOGGER.debug("DN={} callsign={}".format(dn, callsign))
17+
if f"CN={callsign}" not in dn:
18+
LOGGER.warning("Callsign does not match CSR subject. DN={} callsign={}".format(dn, callsign))
19+
return False
20+
# TODO: check that keyusages in the CSR are fine
21+
# crypto.X509Extension(b"keyUsage", True, b"digitalSignature,nonRepudiation,keyEncipherment"),
22+
# crypto.X509Extension(b"extendedKeyUsage", True, b"clientAuth"),
23+
return True

0 commit comments

Comments
 (0)