RFC - Display context information using iproute2 ss utility

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



I've been patching the iproute2 "ss" utility to display the SELinux
security contexts for process and sockets, however I'm not sure
whether the socket contexts are correct (I expected most to show
system_u:object_r:....). 

I'm taking the socket contexts from /proc/PID/fd as was mentioned in
a previous email regarding socket contexts - is this correct ??

I've attached the patch that is built against:

git clone git://git.kernel.org/pub/scm/linux/kernel/git/shemminger/iproute2.git

Also is it possible to obtain a sockets peer context from userspace
using the /proc/PID/fd information as it would be useful to show this.

Richard
From 7d61b9f9e5098a594305364e1380c492eef52091 Mon Sep 17 00:00:00 2001
From: Richard Haines <richard_c_haines@xxxxxxxxxxxxxx>
Date: Fri, 7 Feb 2014 15:02:53 +0000
Subject: [PATCH] ss: Add support for retrieving SELinux contexts

The SELinux contexts can be added to the output using the -Z option.
The process and socket contexts are retrieved but for netlink sockets:
if valid process show process context, if pid = 0 show kernel initial
context, if unknown show "not available".

Signed-off-by: Richard Haines <richard_c_haines@xxxxxxxxxxxxxx>
---
 configure     |  16 +++++
 man/man8/ss.8 |  19 ++++++
 misc/Makefile |  12 ++++
 misc/ss.c     | 209 ++++++++++++++++++++++++++++++++++++++++++++++++++++------
 4 files changed, 235 insertions(+), 21 deletions(-)

diff --git a/configure b/configure
index da01c19..854837e 100755
--- a/configure
+++ b/configure
@@ -231,6 +231,19 @@ EOF
     rm -f $TMPDIR/ipsettest.c $TMPDIR/ipsettest
 }
 
+check_selinux()
+# SELinux is a compile time option in the ss utility
+{
+	SELINUX_LIB=$(${PKG_CONFIG} --silence-errors libselinux --libs)
+	if [ -n "$SELINUX_LIB" ]
+	then
+	echo "HAVE_SELINUX:=y" >>Config
+	echo "yes"
+    else
+	echo "no"
+	fi
+}
+
 echo "# Generated config based on" $INCLUDE >Config
 check_toolchain
 
@@ -253,3 +266,6 @@ check_ipt_lib_dir
 
 echo -n "libc has setns: "
 check_setns
+
+echo -n "SELinux support: "
+check_selinux
diff --git a/man/man8/ss.8 b/man/man8/ss.8
index 807d9dc..2fbed36 100644
--- a/man/man8/ss.8
+++ b/man/man8/ss.8
@@ -53,6 +53,22 @@ Print summary statistics. This option does not parse socket lists obtaining
 summary from various sources. It is useful when amount of sockets is so huge
 that parsing /proc/net/tcp is painful.
 .TP
+.B \-Z, \-\-context
+Show SELinux security contexts. Both the process and socket contexts will
+be displayed (for those with socket file descriptors). For 
+.BR netlink (7)
+sockets, the context displayed is as follows:
+.RS
+.RS
+.IP "1." 4
+If a valid process show pid context.
+.IP "2." 4
+If destination is kernel (pid = 0) show kernel initial context.
+.IP "3." 4
+If allocated by the kernel or netlink user show context as "not available".
+.RE
+.RE
+.TP
 .B \-b, \-\-bpf
 Show socket BPF filters (only administrators are allowed to get these information).
 .TP
@@ -103,6 +119,9 @@ Please take a look at the official documentation (Debian package iproute-doc) fo
 .B ss -t -a
 Display all TCP sockets.
 .TP
+.B ss -t -a -Z
+Display all TCP sockets with process and socket SELinux security contexts.
+.TP
 .B ss -u -a
 Display all UDP sockets.
 .TP
diff --git a/misc/Makefile b/misc/Makefile
index a59ff87..a946a85 100644
--- a/misc/Makefile
+++ b/misc/Makefile
@@ -8,6 +8,18 @@ include ../Config
 all: $(TARGETS)
 
 ss: $(SSOBJ)
+ifeq ($(HAVE_SELINUX),y)
+	$(CC) $(CFLAGS) $(LDFLAGS) -o $@ $(SSOBJ) $(LDLIBS) -lselinux
+else
+	$(CC) $(CFLAGS) $(LDFLAGS) -o $@ $(SSOBJ) $(LDLIBS)
+endif
+
+ss.o: ss.c
+ifeq ($(HAVE_SELINUX),y)
+	$(CC) $(CFLAGS) -DHAVE_SELINUX -c $+
+else
+	$(CC) $(CFLAGS) -c $+
+endif
 
 nstat: nstat.c
 	$(CC) $(CFLAGS) $(LDFLAGS) -o nstat nstat.c -lm
diff --git a/misc/ss.c b/misc/ss.c
index 764ffe2..1e1d3fb 100644
--- a/misc/ss.c
+++ b/misc/ss.c
@@ -40,6 +40,9 @@
 #include <linux/filter.h>
 #include <linux/packet_diag.h>
 #include <linux/netlink_diag.h>
+#if HAVE_SELINUX
+#include <selinux/selinux.h>
+#endif
 
 int resolve_hosts = 0;
 int resolve_services = 1;
@@ -50,6 +53,11 @@ int show_users = 0;
 int show_mem = 0;
 int show_tcpinfo = 0;
 int show_bpf = 0;
+#if HAVE_SELINUX
+int show_context = 0;
+/* If show_users & show_context only do user_ent_hash_build() once */
+int user_ent_hash_build_init = 0;
+#endif
 
 int netid_width;
 int state_width;
@@ -207,7 +215,11 @@ struct user_ent {
 	unsigned int	ino;
 	int		pid;
 	int		fd;
-	char		process[0];
+	char	*process;
+#if HAVE_SELINUX
+	security_context_t	process_ctx;
+	security_context_t	socket_ctx;
+#endif
 };
 
 #define USER_ENT_HASH_SIZE	256
@@ -220,20 +232,29 @@ static int user_ent_hashfn(unsigned int ino)
 	return val & (USER_ENT_HASH_SIZE - 1);
 }
 
-static void user_ent_add(unsigned int ino, const char *process, int pid, int fd)
+#if HAVE_SELINUX
+static void user_ent_add(unsigned int ino, char *process,
+							int pid, int fd,
+							security_context_t proc_ctx,
+							security_context_t sock_ctx)
+#else
+static void user_ent_add(unsigned int ino, char *process, int pid, int fd)
+#endif
 {
 	struct user_ent *p, **pp;
-	int str_len;
 
-	str_len = strlen(process) + 1;
-	p = malloc(sizeof(struct user_ent) + str_len);
+	p = malloc(sizeof(struct user_ent));
 	if (!p)
 		abort();
 	p->next = NULL;
 	p->ino = ino;
 	p->pid = pid;
 	p->fd = fd;
-	strcpy(p->process, process);
+	p->process = strdup(process);
+#if HAVE_SELINUX
+	p->process_ctx = strdup(proc_ctx);
+	p->socket_ctx = strdup(sock_ctx);
+#endif
 
 	pp = &user_ent_hash[user_ent_hashfn(ino)];
 	p->next = *pp;
@@ -247,6 +268,17 @@ static void user_ent_hash_build(void)
 	char name[1024];
 	int nameoff;
 	DIR *dir;
+#if HAVE_SELINUX
+    security_context_t pid_context;
+    security_context_t sock_context;
+	security_context_t no_ctx = "not available";
+
+	/* If show_users & show_context only do this once */
+	if (user_ent_hash_build_init != 0)
+		return;
+
+	user_ent_hash_build_init = 1;
+#endif
 
 	strcpy(name, root);
 	if (strlen(name) == 0 || name[strlen(name)-1] != '/')
@@ -261,19 +293,24 @@ static void user_ent_hash_build(void)
 	while ((d = readdir(dir)) != NULL) {
 		struct dirent *d1;
 		char process[16];
+		char *p;
 		int pid, pos;
 		DIR *dir1;
 		char crap;
 
 		if (sscanf(d->d_name, "%d%c", &pid, &crap) != 1)
 			continue;
-
+#if HAVE_SELINUX
+		if (getpidcon(pid, &pid_context) != 0)
+			pid_context = strdup(no_ctx);
+#endif
 		sprintf(name + nameoff, "%d/fd/", pid);
 		pos = strlen(name);
 		if ((dir1 = opendir(name)) == NULL)
 			continue;
 
 		process[0] = '\0';
+		p = process;
 
 		while ((d1 = readdir(dir1)) != NULL) {
 			const char *pattern = "socket:[";
@@ -281,6 +318,7 @@ static void user_ent_hash_build(void)
 			char lnk[64];
 			int fd;
 			ssize_t link_len;
+			char tmp[1024];
 
 			if (sscanf(d1->d_name, "%d%c", &fd, &crap) != 1)
 				continue;
@@ -296,21 +334,33 @@ static void user_ent_hash_build(void)
 				continue;
 
 			sscanf(lnk, "socket:[%u]", &ino);
-
-			if (process[0] == '\0') {
-				char tmp[1024];
+#if HAVE_SELINUX
+			snprintf(tmp, sizeof(tmp), "%s/%d/fd/%s",
+					root, pid, d1->d_name);
+
+			if (getfilecon(tmp, &sock_context) < 0)
+				sock_context = strdup(no_ctx);
+#endif
+			if (*p == '\0') {
 				FILE *fp;
 
 				snprintf(tmp, sizeof(tmp), "%s/%d/stat", root, pid);
 				if ((fp = fopen(tmp, "r")) != NULL) {
-					fscanf(fp, "%*d (%[^)])", process);
+					fscanf(fp, "%*d (%[^)])", p);
 					fclose(fp);
 				}
 			}
-
-			user_ent_add(ino, process, pid, fd);
+#if HAVE_SELINUX
+			user_ent_add(ino, p, pid, fd, pid_context, sock_context);
+			freecon(sock_context);
+		}
+		freecon(pid_context);
+		closedir(dir1);
+#else
+			user_ent_add(ino, p, pid, fd);
 		}
 		closedir(dir1);
+#endif
 	}
 	closedir(dir);
 }
@@ -349,6 +399,43 @@ static int find_users(unsigned ino, char *buf, int buflen)
 	return cnt;
 }
 
+#if HAVE_SELINUX
+static int find_context(unsigned ino, char *buf, int buflen)
+{
+	struct user_ent *p;
+	int cnt = 0;
+	char *ptr;
+
+	if (!ino)
+		return 0;
+
+	p = user_ent_hash[user_ent_hashfn(ino)];
+	ptr = buf;
+	while (p) {
+		if (p->ino != ino)
+			goto next;
+
+		if (ptr - buf >= buflen - 1)
+			break;
+
+		snprintf(ptr, buflen - (ptr - buf),
+				"proc_ctx=%s sock_ctx=%s ",
+				p->process_ctx, p->socket_ctx);
+
+		ptr += strlen(ptr);
+		cnt++;
+
+	next:
+		p = p->next;
+	}
+
+	if (ptr != buf)
+		ptr[-1] = '\0';
+
+	return cnt;
+}
+#endif
+
 /* Get stats from slab */
 
 struct slabstat
@@ -1282,11 +1369,17 @@ static int tcp_show_line(char *line, const struct filter *f, int family)
 		if (s.qack&1)
 			printf(" bidir");
 	}
+	char ubuf[4096];
 	if (show_users) {
-		char ubuf[4096];
 		if (find_users(s.ino, ubuf, sizeof(ubuf)) > 0)
 			printf(" users:(%s)", ubuf);
 	}
+#if HAVE_SELINUX
+	if (show_context) {
+		if (find_context(s.ino, ubuf, sizeof(ubuf)) > 0)
+			printf(" %s", ubuf);
+	}
+#endif
 	if (show_details) {
 		if (s.uid)
 			printf(" uid:%u", (unsigned)s.uid);
@@ -1506,11 +1599,17 @@ static int inet_show_sock(struct nlmsghdr *nlh, struct filter *f)
 			       r->idiag_retrans);
 		}
 	}
+	char ubuf[4096];
 	if (show_users) {
-		char ubuf[4096];
 		if (find_users(r->idiag_inode, ubuf, sizeof(ubuf)) > 0)
 			printf(" users:(%s)", ubuf);
 	}
+#if HAVE_SELINUX
+	if (show_context) {
+		if (find_context(r->idiag_inode, ubuf, sizeof(ubuf)) > 0)
+			printf(" %s", ubuf);
+	}
+#endif
 	if (show_details) {
 		if (r->idiag_uid)
 			printf(" uid:%u", (unsigned)r->idiag_uid);
@@ -1995,11 +2094,17 @@ static int dgram_show_line(char *line, const struct filter *f, int family)
 	formatted_print(&s.local, s.lport);
 	formatted_print(&s.remote, s.rport);
 
+	char ubuf[4096];
 	if (show_users) {
-		char ubuf[4096];
 		if (find_users(s.ino, ubuf, sizeof(ubuf)) > 0)
 			printf(" users:(%s)", ubuf);
 	}
+#if HAVE_SELINUX
+	if (show_context) {
+		if (find_context(s.ino, ubuf, sizeof(ubuf)) > 0)
+			printf(" %s", ubuf);
+	}
+#endif
 
 	if (show_details) {
 		if (s.uid)
@@ -2185,11 +2290,17 @@ static void unix_list_print(struct unixstat *list, struct filter *f)
 		printf("%*s %-*d %*s %-*d",
 		       addr_width, s->name ? : "*", serv_width, s->ino,
 		       addr_width, peer, serv_width, s->peer);
+		char ubuf[4096];
 		if (show_users) {
-			char ubuf[4096];
 			if (find_users(s->ino, ubuf, sizeof(ubuf)) > 0)
 				printf(" users:(%s)", ubuf);
 		}
+#if HAVE_SELINUX
+		if (show_context) {
+			if (find_context(s->ino, ubuf, sizeof(ubuf)) > 0)
+				printf(" %s", ubuf);
+		}
+#endif
 		printf("\n");
 	}
 }
@@ -2250,11 +2361,17 @@ static int unix_show_sock(struct nlmsghdr *nlh, struct filter *f)
 			addr_width, "*", /* FIXME */
 			serv_width, peer_ino);
 
+	char ubuf[4096];
 	if (show_users) {
-		char ubuf[4096];
 		if (find_users(r->udiag_ino, ubuf, sizeof(ubuf)) > 0)
 			printf(" users:(%s)", ubuf);
 	}
+#if HAVE_SELINUX
+	if (show_context) {
+		if (find_context(r->udiag_ino, ubuf, sizeof(ubuf)) > 0)
+			printf(" %s", ubuf);
+	}
+#endif
 
 	if (show_mem) {
 		printf("\n\t");
@@ -2511,11 +2628,17 @@ static int packet_show_sock(struct nlmsghdr *nlh, struct filter *f)
 	printf("%*s*%-*s",
 	       addr_width, "", serv_width, "");
 
+	char ubuf[4096];
 	if (show_users) {
-		char ubuf[4096];
 		if (find_users(r->pdiag_ino, ubuf, sizeof(ubuf)) > 0)
 			printf(" users:(%s)", ubuf);
 	}
+#if HAVE_SELINUX
+	if (show_context) {
+		if (find_context(r->pdiag_ino, ubuf, sizeof(ubuf)) > 0)
+			printf(" %s", ubuf);
+	}
+#endif
 	if (show_details) {
 		__u32 uid = 0;
 
@@ -2706,11 +2829,17 @@ static int packet_show(struct filter *f)
 		printf("%*s*%-*s",
 		       addr_width, "", serv_width, "");
 
+		char ubuf[4096];
 		if (show_users) {
-			char ubuf[4096];
 			if (find_users(ino, ubuf, sizeof(ubuf)) > 0)
 				printf(" users:(%s)", ubuf);
 		}
+#if HAVE_SELINUX
+		if (show_context) {
+			if (find_context(ino, ubuf, sizeof(ubuf)) > 0)
+				printf(" %s", ubuf);
+		}
+#endif
 		if (show_details) {
 			printf(" ino=%u uid=%u sk=%llx", ino, uid, sk);
 		}
@@ -2785,6 +2914,29 @@ static void netlink_show_one(struct filter *f,
 		printf("%*s*%-*s",
 		       addr_width, "", serv_width, "");
 	}
+#if HAVE_SELINUX
+    security_context_t pid_context = NULL;
+
+	if (show_context) {
+		/* The pid value will either be:
+		 *		0 if destination is kernel - show kernel initial context.
+		 *		A valid process pid - use getpidcon.
+		 *		A unique value allocated by the kernel or netlink user
+		 *		to the process - show context as "not available".
+		 */
+		if (!pid) {
+			security_get_initial_context("kernel", &pid_context);
+		} else if (pid > 0) {
+			getpidcon(pid, &pid_context);
+		}
+		if (pid_context != NULL) {
+			printf("proc_ctx=%-*s ", serv_width, pid_context);
+			freecon (pid_context);
+		} else {
+			printf("proc_ctx=%-*s ", serv_width, "not available");
+		}
+	}
+# endif
 
 	if (show_details) {
 		printf(" sk=%llx cb=%llx groups=0x%08x", sk, cb, groups);
@@ -3060,6 +3212,7 @@ static void _usage(FILE *dest)
 "   -i, --info		show internal TCP information\n"
 "   -s, --summary	show socket usage summary\n"
 "   -b, --bpf           show bpf filter socket information\n"
+"   -Z, --context	display SELinux security contexts for process and socket\n"
 "\n"
 "   -4, --ipv4          display only IP version 4 sockets\n"
 "   -6, --ipv6          display only IP version 6 sockets\n"
@@ -3149,6 +3302,7 @@ static const struct option long_opts[] = {
 	{ "filter", 1, 0, 'F' },
 	{ "version", 0, 0, 'V' },
 	{ "help", 0, 0, 'h' },
+	{ "context", 0, 0, 'Z' },
 	{ 0 }
 
 };
@@ -3167,7 +3321,7 @@ int main(int argc, char *argv[])
 
 	current_filter.states = default_filter.states;
 
-	while ((ch = getopt_long(argc, argv, "dhaletuwxnro460spbf:miA:D:F:vV",
+	while ((ch = getopt_long(argc, argv, "dhaletuwxnro460spbf:miA:D:F:vV:Z",
 				 long_opts, NULL)) != EOF) {
 		switch(ch) {
 		case 'n':
@@ -3327,6 +3481,19 @@ int main(int argc, char *argv[])
 		case 'V':
 			printf("ss utility, iproute2-ss%s\n", SNAPSHOT);
 			exit(0);
+		case 'Z':
+#if HAVE_SELINUX
+	    	if (is_selinux_enabled() <= 0) {
+				fprintf(stderr, "ss: SELinux is not enabled.\n");
+				exit(1);
+	    	}
+	    	show_context++;
+			user_ent_hash_build();
+#else
+            fprintf(stderr, "ss: version does not support SELinux.\n");
+	    	exit(1);
+#endif
+			break;
 		case 'h':
 		case '?':
 			help();
-- 
1.8.5.3

_______________________________________________
Selinux mailing list
Selinux@xxxxxxxxxxxxx
To unsubscribe, send email to Selinux-leave@xxxxxxxxxxxxx.
To get help, send an email containing "help" to Selinux-request@xxxxxxxxxxxxx.

[Index of Archives]     [Selinux Refpolicy]     [Linux SGX]     [Fedora Users]     [Fedora Desktop]     [Yosemite Photos]     [Yosemite Camping]     [Yosemite Campsites]     [KDE Users]     [Gnome Users]

  Powered by Linux