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.