[apparmor] [patch 3/4] utils/aa-unconfined: allow specifying ss/netstat binary locations

Steve Beattie steve at nxnw.org
Fri Dec 30 07:24:57 UTC 2016

This patch allows a user to specify a specific location for ss or
netstat for use in aa-unconfined, allowing a user to work around a
tool that's buggy, uninstalled, or installed in an unexpected location.
Note this option in the manpage.

[The downside to this patch is that if an environment uses something
 like a restrictive sudo policy around aa-unconfined, this would
 possibly give a user a way to subvert that. So I'm ambivalent about
 this patch.]

Signed-off-by: Steve Beattie <steve at nxnw.org>
 utils/aa-unconfined     |   24 +++++++++++++++---------
 utils/aa-unconfined.pod |    8 +++++---
 2 files changed, 20 insertions(+), 12 deletions(-)

Index: b/utils/aa-unconfined
--- a/utils/aa-unconfined
+++ b/utils/aa-unconfined
@@ -34,8 +34,8 @@ _ = init_translation()
 parser = argparse.ArgumentParser(description=_("Lists unconfined processes having tcp or udp ports"))
 parser.add_argument("--paranoid", action="store_true", help=_("scan all processes from /proc"))
 bin_group = parser.add_mutually_exclusive_group()
-bin_group.add_argument("--with-ss", action='store_true', help=_("use ss(8) to find listening processes (default)"))
-bin_group.add_argument("--with-netstat", action='store_true', help=_("use netstat(8) to find listening processes"))
+bin_group.add_argument("--with-ss", nargs='?', const='ss', metavar='SS_PATH', help=_("use ss(8) to find listening processes (default)"))
+bin_group.add_argument("--with-netstat", nargs='?', const='netstat', metavar='NETSTAT_PATH', help=_("use netstat(8) to find listening processes"))
 args = parser.parse_args()
 paranoid = args.paranoid
@@ -50,17 +50,20 @@ def get_all_pids():
     return set(filter(lambda x: re.search(r"^\d+$", x), aa.get_subdirectories("/proc")))
-def get_pids_ss():
+def get_pids_ss(ss):
     '''Get a set of pids listening on network sockets via ss(8)'''
     regex_lines = re.compile(r"^(tcp|udp|raw|p_dgr)\s.+\s+users:(?P<users>\(\(.*\)\))$")
     regex_users_pids = re.compile(r'(\("[^"]+",(pid=)?(\d+),[^)]+\))')
+    if ss is None:
+        ss = 'ss'
     pids = set()
     my_env = os.environ.copy()
     my_env['LANG'] = 'C'
     my_env['PATH'] = '/bin:/usr/bin:/sbin:/usr/sbin'
     for family in ['inet', 'inet6', 'link']:
-        cmd = ['ss', '-nlp', '--family', family]
+        cmd = [ss, '-nlp', '--family', family]
         if sys.version_info < (3, 0):
             output = subprocess.check_output(cmd, shell=False, env=my_env).split("\n")
@@ -76,11 +79,14 @@ def get_pids_ss():
     return pids
-def get_pids_netstat():
+def get_pids_netstat(netstat):
     '''Get a set of pids listening on network sockets via netstat(8)'''
     regex_tcp_udp = re.compile(r"^(tcp|udp|raw)6?\s+\d+\s+\d+\s+\S+\:(\d+)\s+\S+\:(\*|\d+)\s+(LISTEN|\d+|\s+)\s+(?P<pid>\d+)\/(\S+)")
-    cmd = ['netstat', '-nlp', '--protocol', 'inet,inet6']
+    if netstat is None:
+        netstat = 'netstat'
+    cmd = [netstat, '-nlp', '--protocol', 'inet,inet6']
     my_env = os.environ.copy()
     my_env['LANG'] = 'C'
     my_env['PATH'] = '/bin:/usr/bin:/sbin:/usr/sbin'
@@ -101,10 +107,10 @@ def get_pids_netstat():
 pids = set()
 if paranoid:
     pids = get_all_pids()
-elif args.with_ss or (not args.with_netstat and (os.path.exists('/bin/ss') or os.path.exists('/usr/bin/ss'))):
-    pids = get_pids_ss()
+elif args.with_ss is not None or (args.with_netstat is None and (os.path.exists('/bin/ss') or os.path.exists('/usr/bin/ss'))):
+    pids = get_pids_ss(args.with_ss)
-    pids = get_pids_netstat()
+    pids = get_pids_netstat(args.with_netstat)
 for pid in sorted(map(int, pids)):
Index: b/utils/aa-unconfined.pod
--- a/utils/aa-unconfined.pod
+++ b/utils/aa-unconfined.pod
@@ -27,7 +27,7 @@ not have AppArmor profiles loaded
 =head1 SYNOPSIS
-B<aa-unconfined [I<--paranoid>] [I<--with-ss> | I<--with-netstat>]>
+B<aa-unconfined [I<--paranoid>] [I<--with-ss> [SS_PATH] | I<--with-netstat> [NETSTAT_PATH]]>
 =head1 OPTIONS
@@ -41,13 +41,15 @@ that do not have AppArmor profiles loade
 =item B<--with-ss>
 Use the ss(8) command to find processes listening on network sockets
-(the default).
+(the default). An alternate ss binary can be specified using the
+optional I<SS_PATH> argument.
 =item B<--with-netstat>
 Use the netstat(8) command to find processes listening on network
 sockets. This is also what aa-unconfined will fall back to when ss(8)
-is not available.
+is not available. An alternate netstat binary can be specified using
+the optional I<NETSTAT_PATH> argument.

More information about the AppArmor mailing list