Reporting file descriptors created by pipe and socketpair
PATCH 1/1 Reporting file descriptors created by pipe and socketpair
The polgen tools suggest SELinux policy by analyzing dynamic traces of a running set of related programs. It derives information flow by tracking file descriptors used by a set of programs, and notes which processes performs reads and writes. We would very much like to get our raw data from autrace, however, the current traces it generates lacks some crucial information. The record for the pipe and socketpair system call does not include the file descriptors created upon success. Enclosed is a patch by Mark Workman that remedies this problem.
John
*** a/kernel/auditsc.c 2006-03-20 00:53:29.000000000 -0500
--- b/kernel/auditsc.c 2006-06-26 08:21:56.000000000 -0400
***************
*** 820,825 ****
--- 820,846 ----
audit_log_format(ab, " success=%s exit=%ld",
(context->return_valid==AUDITSC_SUCCESS)?"yes":"no", context->return_code);
+
+ printk(KERN_INFO "%s auditing what?\n", __FUNCTION__);
+
+ switch (context->major) {
+ case __NR_socketcall:
+ if (context->argv[0] == SYS_SOCKETPAIR)
+ audit_log_format(ab, " descriptor pair=%d,%d",
+ current->audit_pids[0],
+ current->audit_pids[1]);
+ break;
+
+ case __NR_pipe:
+ audit_log_format(ab, " descriptor pair=%d,%d",
+ current->audit_pids[0],
+ current->audit_pids[1]);
+ break;
+
+ default: break;
+ }
+
+
audit_log_format(ab,
" a0=%lx a1=%lx a2=%lx a3=%lx items=%d"
" pid=%d auid=%u uid=%u gid=%u"
*** a/fs/pipe.c 2006-03-20 00:53:29.000000000 -0500
--- b/fs/pipe.c 2006-06-26 10:10:15.000000000 -0400
***************
*** 779,786 ****
fd_install(i, f1);
fd_install(j, f2);
! fd[0] = i;
! fd[1] = j;
return 0;
close_f12_inode_i_j:
--- 779,786 ----
fd_install(i, f1);
fd_install(j, f2);
! current->audit_pids[0] = fd[0] = i;
! current->audit_pids[1] = fd[1] = j;
return 0;
close_f12_inode_i_j:
*** a/include/linux/sched.h 2006-03-20 00:53:29.000000000 -0500
--- b/include/linux/sched.h 2006-06-26 08:19:23.000000000 -0400
***************
*** 819,824 ****
--- 819,825 ----
void *security;
struct audit_context *audit_context;
seccomp_t seccomp;
+ int audit_pids [3];
/* Thread group tracking */
u32 parent_exec_id;
*** a/net/socket.c 2006-03-20 00:53:29.000000000 -0500
--- b/net/socket.c 2006-06-26 12:35:23.000000000 -0400
***************
*** 1256,1263 ****
err = put_user(fd1, &usockvec[0]); if (!err)
err = put_user(fd2, &usockvec[1]);
! if (!err)
return 0;
sys_close(fd2);
sys_close(fd1);
--- 1256,1266 ----
err = put_user(fd1, &usockvec[0]); if (!err)
err = put_user(fd2, &usockvec[1]);
! if (!err) {
! current->audit_pids[0] = fd1;
! current->audit_pids[1] = fd2;
return 0;
+ }
sys_close(fd2); sys_close(fd1);
Alexander Viro
NAKQuoted 9 lines · John D. Ramsdell
The polgen tools suggest SELinux policy by analyzing dynamic traces of a running set of related programs. It derives information flow by tracking file descriptors used by a set of programs, and notes which processes performs reads and writes. We would very much like to get our raw data from autrace, however, the current traces it generates lacks some crucial information. The record for the pipe and socketpair system call does not include the file descriptors created upon success. Enclosed is a patch by Mark Workman that remedies this problem.
NAK
a) __NR_socketpair is not universally present
b) neither __NR_socketpair nor __NR_pipe are guaranteed to be the same for 32bit and 64bit syscalls.
c) just how do you propose to do "tracking file descriptors"? It's so trivial to confuse that it's not even funny; pass an SCM_RIGHTS datagram to yourself and watch that code blow chunks. Moreover, several threads can very legitimately share descriptor table and work with it in parallel. Have fun figuring out the sequence of those events...
IOW, it's broken by design.
Quoted 3 lines · Alexander Viro
c) just how do you propose to do "tracking file descriptors"? It's so trivial to confuse that it's not even funny; pass an
SCM_RIGHTSdatagram to yourself and watch that code blow chunks.
So, what would be a good way to let them see the file descriptors created by these two syscalls? The can get the info for socket, open, accept, ... but these two are difficult to get at the information.
This might be better as an aux record instead of part of the syscall record where the field is meaningless for 99.99% of the syscalls.
-Steve
Quoted 1 line · Alexander Viro
c) just how do you propose to do "tracking file descriptors"?
We aren't proposing to track file descriptors. We already have code that does that. Currently, we collect the traces with a modified version of strace, but for a variety of reasons, autrace would be a much better source of trace data. First, we have to modify strace so it includes security contexts on labeled objects. Second, strace output is designed to be easily consumed by humans, and is a bear to get a program to understand it. You can see the AWK/YACC/sed pipeline required to put strace output into a form that can be easily consumed by a python program by reading the source file polgen/src/trackfd/trackstrace.in in the polgen CVS repository at
http://sf.net/projects/polgen. You'll quickly realize why I am eager for the parsing library to be introduced in audit 1.3.
The program that does the analysis is in polgen/src/trackfd/trackfd.py. It analyzes the records for the following system calls:
close open socket pipe socketpair dup dup2 fcntl64 read write bind accept connect recv send unlink execve clone
The key thing is the program doesn't really track file descriptors, instead it tracks what they refer to. The program generates a data structure when each file descriptor is created via an open or socket system call. The system calls dup, fcntl, close, and execve change mappings of file descriptors to the data structure, and a close system call causes a summary of reads and writes to be written. Here is the summary of the file descriptor tracker from the polgen document.
An essential part of the data reduction is the summarization of the life cycle of a file descriptor. For each file descriptor created by a program, the @code{trackfd} program creates a data structure. The data structure is updated whenever a system call is found that applies to the file descriptor. Finally, when a file descriptor is closed, a summary of the activity associated with the file descriptor is written to the output.
It's one of those programs that is either correct, or explodes and dies horribly when a bug is exercised. On Fedora Core, the program has been quite solid for quite a while now. I has been used to analyze the Jabber server and an application running in a Java Virtual Machine.
By the way, we do not claim to handle every possible path for information flow yet. In fact, we ignore all system calls implemented by the ipc common kernel entry point. Our experience is that the current set of system calls we analyze handles a large number important target applications.
The trackfd.py file is about 600 lines of code. I tried to make it easily read, in case someone took the time to proof read my code.
John
Quoted 6 lines · John D. Ramsdell
Quoted 1 line · Alexander Viro
c) just how do you propose to do "tracking file descriptors"?
We aren't proposing to track file descriptors. We already have code that does that
Indeed? And how, pray tell, do you handle e.g. processes A and B sending SCM_RIGHTS datagrams to C at the same moment? With data part containing the indication which process had sent them, so C can handle them just fine and do that in different ways depending on the origin of datagram. Note that looking at the timestamps will not help you at all - you can get
A enters sendmsg(2)
B enters sendmsg(2)
B puts datagram into queue
A puts datagram into queue
A leaves sendmsg(2)
B leaves sendmsg(2)
quite easily.
Quoted 2 lines · Alexander Viro
Indeed? And how, pray tell, do you handle e.g. processes A and B sending
SCM_RIGHTSdatagrams to C at the same moment?
We don't. We do not try to get all information flows. Our goal is to recognize common information flow patterns, and suggest SELinux policy based on the patterns we find. For example, the Jabber Server has five main processes, and one them routes information between the other four. We can recognize this pattern, and suggest types and allow rules consistent with this hub-and-spoke design pattern.
Here is another major reason to prefer autrace over strace:
performance. We tried to strace a run of an Apache Web Server compiled with threads support enabled. It was a disaster. The only way we could get useful data was to strace a single threaded version of the web server. I bet we could get useful data on run of a multithreaded web server using autrace.
John
Quoted 2 lines · Alexander Viro
a)
__NR_socketpairis not universally present
b) neither__NR_socketpairnor__NR_pipeare guaranteed to be the
We have a fix for these issues which is in preparation, and will submit the next version of the patch using the -urp option with diff. Thank you for pointing out this problem.
John