# HG changeset patch
# User jfp <jf.pieronne@laposte.net>
# Date 1673969408 -3600
#      Tue Jan 17 16:30:08 2023 +0100
# Node ID b4e6c1c444844a788e0b823bc01e8c155de44956
# Parent  fe61049b37e490e54745da08c28c98468868f164
get_security.py: update stub information
rule302: better filters
rule401: Only report ACE if IDENTIFIER=* and access allow DELETE or WRITE
rule403: fix a bug

diff --git a/secrules/get_security.py b/secrules/get_security.py
--- a/secrules/get_security.py
+++ b/secrules/get_security.py
@@ -1,9 +1,10 @@
+from typing import Tuple, List
 from ovms import starlet
 from ovms import itemList, ossdef, ssdef
 from ovms.rtl import lib
 
 
-def get_security(fn, clsnam=b'FILE'):
+def get_security(fn: bytes | str, clsnam=b'FILE') -> Tuple[bytes, bytes, List[bytes]]:
     itm = (
         itemList.itemList(code=ossdef.OSS__ACL_READ),
         itemList.itemList(
@@ -42,8 +43,8 @@
     filename = sys.argv[1]
     clsnam = sys.argv[2]
     owner, protection, acl = get_security(filename, clsnam.encode())
-    print('    Owner:', owner)
-    print('    Protection:', protection)
+    print('    Owner:', owner.decode())
+    print('    Protection:', protection.decode())
     print('    Access control list:')
     for e in acl:
-        print(9 * ' ', e)
+        print(9 * ' ', e.decode())
diff --git a/secrules/rules03.py b/secrules/rules03.py
--- a/secrules/rules03.py
+++ b/secrules/rules03.py
@@ -12,6 +12,40 @@
     signifies an unauthorized change was made to the file, and represents an area
     of vulnerability with regards to protection of critical files."""
 
+    fn_owner = {
+        b'SYS$SYSROOT:[000000]CML$SERVER.DIR': b'CML$SERVER',
+        b'SYS$SYSROOT:[000000]MAIL$SERVER.DIR': b'MAIL$SERVER',
+        b'SYS$SYSROOT:[000000]MIRRO$SERVER.DIR': b'MIRRO$SERVER',
+        b'SYS$SYSROOT:[000000]PHONE$SERVER.DIR': b'PHONE$SERVER',
+        b'SYS$SYSROOT:[000000]TCPIP$LPD.DIR': b'TCPIP$LPD',
+        b'SYS$SYSROOT:[000000]TCPIP$NTP.DIR': b'TCPIP$NTP',
+        b'SYS$SYSROOT:[000000]TCPIP$SMTP.DIR': b'TCPIP$SMTP',
+        b'SYS$SYSROOT:[000000]VPM$SERVER.DIR': b'VPM$SERVER',
+        b'SYS$COMMON:[000000]OMNI.DIR': b'OMNIADMIN',
+        b'SYS$COMMON:[000000]RDB$REMOTE72.DIR': b'RDB$REMOTE72',
+        b'SYS$COMMON:[000000]RDB$REMOTE73.DIR': b'RDB$REMOTE73',
+        b'SYS$COMMON:[000000]RDB$REMOTE74.DIR': b'RDB$REMOTE74',
+        b'SYS$COMMON:[000000]RDMAIJ72.DIR': b'RDMAIJ72',
+        b'SYS$COMMON:[000000]RDMAIJ73.DIR': b'RDMAIJ73',
+        b'SYS$COMMON:[000000]RDMAIJ74.DIR': b'RDMAIJ74',
+        b'SYS$COMMON:[000000]RDMSTT72.DIR': b'RDMSTT72',
+        b'SYS$COMMON:[000000]RDMSTT73.DIR': b'RDMSTT73',
+        b'SYS$COMMON:[000000]RDMSTT74.DIR': b'RDMSTT74',
+        b'SYS$COMMON:[000000.SYS$STARTUP]OMNI$STARTUP.COM': b'OMNIADMIN',
+        b'SYS$COMMON:[000000.SYS$STARTUP]OMNI$SHUTDOWN.COM': b'OMNIADMIN',
+        b'SYS$COMMON:[000000.SYS$STARTUP]OMNI$SYSTARTUP.COM': b'OMNIADMIN',
+        b'SYS$COMMON:[000000.SYSLIB]LIBAE.EXE': b'OMNIADMIN',
+        b'SYS$COMMON:[000000.SYSLIB]LIBDC.EXE': b'OMNIADMIN',
+        b'SYS$COMMON:[000000.SYSLIB]LIBDE.EXE': b'OMNIADMIN',
+        b'SYS$SYSROOT:[000000.TCPIP$ETC]MOUNTDTAB.DAT': b'TCPIP$NFS',
+    }
+    fndir_own = {
+        b'SYS$COMMON:[000000.OMNI': (b'OMNIADMIN',),
+        b'SYS$COMMON:[000000.SAMBA': (b'SAMBA$SMBD', b'SAMBA$NMBD'),
+        b'SYS$SYSROOT:[000000.TCPIP$SMTP': b'TCPIP$SMTP',
+        b'SYS$SYSROOT:[000000.TCPIP$NTP': b'TCPIP$NTP',
+        b'SYS$SYSROOT:[000000.TCPIP$LPD': b'TCPIP$LPD',
+    }
     if not fmt:
         print(file=fo)
         print('Rule 0302', file=fo)
@@ -20,14 +54,27 @@
     with FindFile(b'SYS$SYSROOT:[000000...]*.*') as fi:
         for fn in fi:  # type: ignore
             fn: bytes
-            id = get_security(fn)[0]
-            if (id != b'SYSTEM') and (id != b'[1,1]'):
-                if (fn != b'MOM$SYSTEM') and (id != b'[376,375]'):
+            fn = fn.rsplit(b';')[0]
+            if fn.startswith(b'SYS$SYSROOT:[000000.SYSCOMMON'):
+                continue
+            idf = get_security(fn)[0]
+            if fn in fn_owner and fn_owner[fn] == idf:
+                continue
+            for sfn, own in fndir_own.items():
+                if fn.startswith(sfn) and idf in own:
+                    ownok = True
+                    break
+            else:
+                ownok = False
+            if ownok:
+                continue
+            if (idf != b'SYSTEM') and (idf != b'[1,1]'):
+                if (fn != b'MOM$SYSTEM') and (idf != b'[376,375]'):
                     if fmt:
                         print('0302"2"', fn.decode(), file=fo)
                     else:
                         print(fn.decode(), file=fo)
-                        print(' ' * 10, id.decode(), file=fo)
+                        print(' ' * 10, idf.decode(), file=fo)
 
 
 if __name__ == '__main__':
diff --git a/secrules/rules04.py b/secrules/rules04.py
--- a/secrules/rules04.py
+++ b/secrules/rules04.py
@@ -43,14 +43,18 @@
                     )
                     acllen = int(retacl[2][ossdef.OSS__ACL_LENGTH])
                     if acllen != 0:
-                        if fmt:
-                            print('0401"2"', fn.decode(), file=fo)
-                        else:
-                            print(fn.decode(), file=fo)
+                        fn_printed = False
                         for e in get_security.get_security(fn)[2]:
-                            e: bytes
-                            if not fmt:
-                                print(' ' * 9, e.decode(), file=fo)
+                            acc = e.split(b',')[-1]
+                            if b'IDENTIFIER=*' in e and (b'DELETE' in acc or b'WRITE' in acc):
+                                if not fn_printed:
+                                    fn_printed = True
+                                    if fmt:
+                                        print('0401"2"', fn.decode(), file=fo)
+                                    else:
+                                        print(fn.decode(), file=fo)
+                                if not fmt:
+                                    print(' ' * 9, e.decode(), file=fo)
                 except VMSError as err:
                     if err.errno not in (
                         rmsdef.RMS__FNF,
@@ -82,7 +86,7 @@
             for fn in fi:   # type: ignore
                 fn: bytes
                 own = get_security.get_security(fn)[0]
-                if own not in ('SYSTEM', '[1,1]'):
+                if own not in (b'SYSTEM', b'[1,1]'):
                     if fmt:
                         print('0403"2"', fn.decode(), file=fo)
                     else:
@@ -121,7 +125,7 @@
             for fn in fi:  # type: ignore
                 fn: bytes
                 prot = get_security.get_security(fn)[1]
-                if not (prot == 'System: RWED, Owner: RWED, Group: RE, World'):
+                if prot != b'System: RWED, Owner: RWED, Group: RE, World':
                     if fmt:
                         print('0404"2"', fn.decode(), file=fo)
                     else: