From 9f9e8ab04f7478688d41c9bcb6ec3e3a7913fd8d Mon Sep 17 00:00:00 2001 From: Simo Sorce Date: Mon, 5 Oct 2015 13:46:24 -0400 Subject: Add auditing to auth/authz modules Signed-off-by: Simo Sorce Reviewed-by: Christian Heimes --- custodia/httpd/authenticators.py | 34 +++++++++++++++++++++++++++++----- custodia/httpd/authorizers.py | 18 ++++++++++++++++++ custodia/httpd/server.py | 16 +++++++++++----- custodia/log.py | 19 +++++++++++++++++++ 4 files changed, 77 insertions(+), 10 deletions(-) diff --git a/custodia/httpd/authenticators.py b/custodia/httpd/authenticators.py index dbb34bd..bed2bc4 100644 --- a/custodia/httpd/authenticators.py +++ b/custodia/httpd/authenticators.py @@ -12,6 +12,7 @@ class HTTPAuthenticator(object): def __init__(self, config=None): self.config = config + self._auditlog = log.AuditLog(self.config) def handle(self, request): raise HTTPError(403) @@ -32,8 +33,14 @@ class SimpleCredsAuth(HTTPAuthenticator): uid = int(request['creds']['gid']) gid = int(request['creds']['uid']) if self._gid == gid or self._uid == uid: + self._auditlog.svc_access(log.AUDIT_SVC_AUTH_PASS, + request['creds']['pid'], + "SCA", "%d, %d" % (uid, gid)) return True else: + self._auditlog.svc_access(log.AUDIT_SVC_AUTH_FAIL, + request['creds']['pid'], + "SCA", "%d, %d" % (uid, gid)) return False @@ -57,13 +64,25 @@ class SimpleHeaderAuth(HTTPAuthenticator): pass elif isinstance(self.value, str): if value != self.value: + self._auditlog.svc_access(log.AUDIT_SVC_AUTH_FAIL, + request['creds']['pid'], + "SHA", value) return False elif isinstance(self.value, list): if value not in self.value: + self._auditlog.svc_access(log.AUDIT_SVC_AUTH_FAIL, + request['creds']['pid'], + "SHA", value) return False else: + self._auditlog.svc_access(log.AUDIT_SVC_AUTH_FAIL, + request['creds']['pid'], + "SHA", value) return False + self._auditlog.svc_access(log.AUDIT_SVC_AUTH_PASS, + request['creds']['pid'], + "SHA", value) request['remote_user'] = value return True @@ -77,7 +96,6 @@ class SimpleAuthKeys(HTTPAuthenticator): 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) @@ -96,14 +114,20 @@ class SimpleAuthKeys(HTTPAuthenticator): 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)) + except Exception: + self._auditlog.svc_access(log.AUDIT_SVC_AUTH_FAIL, + request['creds']['pid'], + "SAK", name) return False if validated: - self._auditlog._log("AUTH SUCCESS: %s" % name) + self._auditlog.svc_access(log.AUDIT_SVC_AUTH_PASS, + request['creds']['pid'], + "SAK", name) request['remote_user'] = name return True - self._auditlog._log("AUTH FAIL: %s" % name) + self._auditlog.svc_access(log.AUDIT_SVC_AUTH_FAIL, + request['creds']['pid'], + "SAK", name) return False diff --git a/custodia/httpd/authorizers.py b/custodia/httpd/authorizers.py index dbf3d37..d6fe7c7 100644 --- a/custodia/httpd/authorizers.py +++ b/custodia/httpd/authorizers.py @@ -2,11 +2,14 @@ import os +from custodia import log + class HTTPAuthorizer(object): def __init__(self, config=None): self.config = config + self._auditlog = log.AuditLog(self.config) self.store_name = None if self.config and 'store' in self.config: self.store_name = self.config['store'] @@ -36,10 +39,16 @@ class SimplePathAuthz(HTTPAuthorizer): # special case to match a path ending in / authz = authz[:-1] if authz == path: + self._auditlog.svc_access(log.AUDIT_SVC_AUTHZ_PASS, + request['creds']['pid'], + "SPA", path) return True while path != '': if path in self.paths: + self._auditlog.svc_access(log.AUDIT_SVC_AUTHZ_PASS, + request['creds']['pid'], + "SPA", path) return True if path == '/': path = '' @@ -63,12 +72,21 @@ class UserNameSpace(HTTPAuthorizer): name = request.get('remote_user', None) if name is None: # UserNameSpace requires a user ... + self._auditlog.svc_access(log.AUDIT_SVC_AUTHZ_FAIL, + request.get('creds', {'pid': 0})['pid'], + "UNS(%s)" % self.path, path) return False namespace = self.path.rstrip('/') + '/' + name + '/' if not path.startswith(namespace): # Not in the namespace + self._auditlog.svc_access(log.AUDIT_SVC_AUTHZ_FAIL, + request.get('creds', {'pid': 0})['pid'], + "UNS(%s)" % self.path, path) return False request['default_namespace'] = name + self._auditlog.svc_access(log.AUDIT_SVC_AUTHZ_PASS, + request.get('creds', {'pid': 0})['pid'], + "UNS(%s)" % self.path, path) return True diff --git a/custodia/httpd/server.py b/custodia/httpd/server.py index 7a84526..8f02a78 100644 --- a/custodia/httpd/server.py +++ b/custodia/httpd/server.py @@ -20,8 +20,7 @@ except ImportError: from socketserver import ForkingMixIn, UnixStreamServer from urllib.parse import urlparse, parse_qs, unquote -from custodia.log import debug as log_debug -from custodia.log import stacktrace +from custodia import log SO_PEERCRED = getattr(socket, 'SO_PEERCRED', 17) @@ -36,7 +35,7 @@ class HTTPError(Exception): self.code = code if code is not None else 500 self.mesg = message errstring = '%d: %s' % (self.code, self.mesg) - log_debug(errstring) + log.debug(errstring) super(HTTPError, self).__init__(errstring) @@ -63,6 +62,7 @@ class ForkingLocalHTTPServer(ForkingMixIn, UnixStreamServer): self.config = config if 'server_string' in self.config: self.server_string = self.config['server_string'] + self._auditlog = log.AuditLog(self.config) def server_bind(self): oldmask = os.umask(000) @@ -144,7 +144,7 @@ class LocalHTTPRequestHandler(BaseHTTPRequestHandler): SELINUX_CONTEXT_LEN) context = creds.decode('utf-8') except Exception as e: - log_debug("Couldn't retrieve SELinux Context: (%s)" % str(e)) + log.debug("Couldn't retrieve SELinux Context: (%s)" % str(e)) context = None return {'pid': pid, 'uid': uid, 'gid': gid, 'context': context} @@ -254,7 +254,7 @@ class LocalHTTPRequestHandler(BaseHTTPRequestHandler): return def log_traceback(self): - self.log_error('Traceback:\n%s' % stacktrace()) + self.log_error('Traceback:\n%s' % log.stacktrace()) def pipeline(self, config, request): """ @@ -299,6 +299,9 @@ class LocalHTTPRequestHandler(BaseHTTPRequestHandler): elif valid is True: valid_once = True if valid_once is not True: + self.server._auditlog.svc_access(log.AUDIT_SVC_AUTH_FAIL, + request['creds']['pid'], "MAIN", + 'No auth') raise HTTPError(403) # auhz framework here @@ -310,6 +313,9 @@ class LocalHTTPRequestHandler(BaseHTTPRequestHandler): if valid is not None: break if valid is not True: + self.server._auditlog.svc_access(log.AUDIT_SVC_AUTHZ_FAIL, + request['creds']['pid'], "MAIN", + request.get('path', '/')) raise HTTPError(403) # Select consumer diff --git a/custodia/log.py b/custodia/log.py index ff71137..c49a29e 100644 --- a/custodia/log.py +++ b/custodia/log.py @@ -52,6 +52,12 @@ AUDIT_SET_DENIED = 4 AUDIT_DEL_ALLOWED = 5 AUDIT_DEL_DENIED = 6 AUDIT_LAST = 7 +AUDIT_SVC_NONE = 8 +AUDIT_SVC_AUTH_PASS = 9 +AUDIT_SVC_AUTH_FAIL = 10 +AUDIT_SVC_AUTHZ_PASS = 11 +AUDIT_SVC_AUTHZ_FAIL = 12 +AUDIT_SVC_LAST = 13 AUDIT_MESSAGES = [ "AUDIT FAILURE", "ALLOWED: '{client:s}' requested key '{key:s}'", # AUDIT_GET_ALLOWED @@ -60,6 +66,13 @@ AUDIT_MESSAGES = [ "DENIED: '{client:s}' stored key '{key:s}'", # AUDIT_SET_DENIED "ALLOWED: '{client:s}' deleted key '{key:s}'", # AUDIT_DEL_ALLOWED "DENIED: '{client:s}' deleted key '{key:s}'", # AUDIT_DEL_DENIED + "AUDIT FAILURE 7", + "AUDIT FAILURE 8", + "PASS({tag:s}): '{cli:s}' authenticated as '{name:s}'", # SVC_AUTH_PASS + "FAIL({tag:s}): '{cli:s}' authenticated as '{name:s}'", # SVC_AUTH_FAIL + "PASS({tag:s}): '{cli:s}' authorized for '{name:s}'", # SVC_AUTHZ_PASS + "FAIL({tag:s}): '{cli:s}' authorized for '{name:s}'", # SVC_AUTHZ_FAIL + "AUDIT FAILURE 13", ] @@ -79,3 +92,9 @@ class AuditLog(object): if action <= AUDIT_NONE or action >= AUDIT_LAST: action = AUDIT_NONE self._log(AUDIT_MESSAGES[action].format(client=client, key=keyname)) + + def svc_access(self, action, client, tag, name): + if action <= AUDIT_SVC_NONE or action >= AUDIT_SVC_LAST: + action = AUDIT_NONE + self._log(AUDIT_MESSAGES[action].format(cli=str(client), tag=tag, + name=name)) -- cgit