From 58d434e0fea2b2f9544e7ef1fba07bf50e07b7c0 Mon Sep 17 00:00:00 2001 From: Simo Sorce Date: Thu, 1 Oct 2015 09:58:40 -0400 Subject: Add simple Keys-in-Header based authentication Signed-off-by: Simo Sorce Reviewed-by: Christian Heimes --- custodia.conf | 6 +++++ custodia/httpd/authenticators.py | 48 ++++++++++++++++++++++++++++++++++++++- examples/enclite.db | Bin 7168 -> 9216 bytes 3 files changed, 53 insertions(+), 1 deletion(-) diff --git a/custodia.conf b/custodia.conf index c3c20af..1a987df 100644 --- a/custodia.conf +++ b/custodia.conf @@ -54,6 +54,11 @@ table = enclite master_key = examples/enclite.sample.key master_enctype = A128CBC-HS256 +[auth:sak] +handler = custodia.httpd.authenticators.SimpleAuthKeys +store = encrypted +# sample key: test=foo-host-key + [authz:encrypted] handler = custodia.httpd.authorizers.UserNameSpace path = /enc/secrets/ @@ -75,3 +80,4 @@ store = kemkeys handler = custodia.root.Secrets allowed_keytypes = simple kem store = encrypted + diff --git a/custodia/httpd/authenticators.py b/custodia/httpd/authenticators.py index de722e0..dbb34bd 100644 --- a/custodia/httpd/authenticators.py +++ b/custodia/httpd/authenticators.py @@ -1,5 +1,10 @@ # Copyright (C) 2015 Custodia Project Contributors - see LICENSE file +import os + +from cryptography.hazmat.primitives import constant_time + +from custodia import log from custodia.httpd.server import HTTPError @@ -45,7 +50,7 @@ class SimpleHeaderAuth(HTTPAuthenticator): def handle(self, request): if self.name not in request['headers']: - return False + return None value = request['headers'][self.name] if self.value is None: # Any value is accepted @@ -61,3 +66,44 @@ class SimpleHeaderAuth(HTTPAuthenticator): request['remote_user'] = value return True + + +class SimpleAuthKeys(HTTPAuthenticator): + + def __init__(self, config=None): + super(SimpleAuthKeys, self).__init__(config) + self.id_header = self.config.get('header', 'CUSTODIA_AUTH_ID') + self.key_header = self.config.get('header', 'CUSTODIA_AUTH_KEY') + self.store_name = self.config['store'] + self.store = None + self.namespace = self.config.get('store_namespace', 'custodiaSAK') + self._auditlog = log.AuditLog(self.config) + + def _db_key(self, name): + return os.path.join(self.namespace, name) + + def handle(self, request): + name = request['headers'].get(self.id_header, None) + key = request['headers'].get(self.key_header, None) + if name is None and key is None: + return None + + validated = False + try: + val = self.store.get(self._db_key(name)) + if val is None: + raise Exception("No such ID") + if constant_time.bytes_eq(val.encode('utf-8'), + key.encode('utf-8')): + validated = True + except Exception as err: + self._auditlog._log("AUTH ERROR: (%s) %s" % (name, err)) + return False + + if validated: + self._auditlog._log("AUTH SUCCESS: %s" % name) + request['remote_user'] = name + return True + + self._auditlog._log("AUTH FAIL: %s" % name) + return False diff --git a/examples/enclite.db b/examples/enclite.db index ab78258..e4404eb 100644 Binary files a/examples/enclite.db and b/examples/enclite.db differ -- cgit