Parcourir la source

concierge-permaudit: report root process that listen to connections but have no unprivileged workers, add --severity option

guillaume il y a 7 ans
Parent
commit
5bae46aa85
1 fichiers modifiés avec 31 ajouts et 6 suppressions
  1. 31 6
      src/concierge-permaudit

+ 31 - 6
src/concierge-permaudit

@@ -9,6 +9,7 @@ import shutil
 import subprocess
 import sys
 import glob
+from enum import IntEnum
 from pathlib import Path
 
 # Third party modules
@@ -178,6 +179,20 @@ interpArgParse.add_argument('script', type=str)
 UID_MIN=1000
 UID_MAX=60000
 
+class Severity(IntEnum):
+  EMERG = 0
+  ALERT = 1
+  CRIT = 2
+  ERR = 3
+  WARNING = 4
+  NOTICE = 5
+  INFO = 6
+  DEBUG = 7
+
+argparser = argparse.ArgumentParser()
+argparser.add_argument('--severity', type = int, default = 3, choices = list(map(int, Severity)))
+args = argparser.parse_args()
+
 homePaths = []
 for pw in pwd.getpwall():
   if pw.pw_uid in range(UID_MIN, UID_MAX):
@@ -260,6 +275,8 @@ exceptions = ''
 
 def logExceptions(description, paths = [], context = None, severity = 3):
   global exceptions
+  if severity > args.severity:
+    return
   exceptions += "%s\n" % description
   if context != None:
     exceptions += "  Context: %s\n" % context
@@ -292,6 +309,14 @@ def auditProcess(proc):
       logWarnings('Executable was moved or deleted', [exePath], 'Process %d' % proc.pid)
     if isWorldWritable(exePath):
       logExceptions('Executable is world-writable', [exePath], 'Process %d' % proc.pid)
+  if ruid == rootPwe.pw_uid:
+    connlist = proc.connections(kind = 'inet')
+    connset = set(c for c in connlist if c.status == psutil.CONN_LISTEN)
+    if len(connset) > 0:
+      childlist = proc.children(recursive = True)
+      childset = set(p for p in childlist if p.uids()[0] != rootPwe.pw_uid)
+      if len(childset) == 0:
+        logExceptions('Root process listen for connections but has no unprivileged worker', [exePath], 'Process %s' % proc.pid, 5)
 
 def auditCommand(ruid, argList, cwd, env = {}, context = None):
   if 'PATH' in env:
@@ -300,10 +325,10 @@ def auditCommand(ruid, argList, cwd, env = {}, context = None):
     path = os.defpath
   absArg0 = shutil.which(argList[0], path=path)
   if absArg0 != None and is_interpreter(absArg0) and len(argList) > 1:
-    (args, remainining) = interpArgParse.parse_known_args(argList)
-    scriptPath = Path(args.script)
+    (interpargs, remainining) = interpArgParse.parse_known_args(argList)
+    scriptPath = Path(interpargs.script)
     if not scriptPath.is_absolute():
-      scriptPath = Path(cwd, args.script)
+      scriptPath = Path(cwd, interpargs.script)
     try:
       scriptPath = scriptPath.resolve()
       if (scriptPath.stat().st_uid != rootPwe.pw_uid and
@@ -383,12 +408,12 @@ if len(rubypathWriteExceptions) > 0:
 for proc in psutil.process_iter():
   pid = proc.pid
   ruid = proc.uids()[0]
-  args = proc.cmdline()
+  procargs = proc.cmdline()
   cwd = proc.cwd()
   env = proc.environ()
   auditProcess(proc)
-  if len(args) > 0:
-    auditCommand(ruid, args, cwd, env, 'Process %d' % pid)
+  if len(procargs) > 0:
+    auditCommand(ruid, procargs, cwd, env, 'Process %d' % pid)
 
 # Passwords should be stored in /etc/shadow, not /etc/passwd
 contentExceptions = []