2005-09-29 02:46:15

by Matt Helsley

[permalink] [raw]
Subject: [RFC][PATCH] Process Events Connector

From: Matt Helsley <[email protected]>

I'd like to propose a single process event connector that reports fork,
exec, id change, and exit events to userspace. The patch merges the fork
and exit connector patches posted by Guillaume and Badari along with two
new types of significant events -- exec and [ug]id changes -- into a
single connector.

Applications that may find these events useful include auditing, system
activity monitoring (e.g. top), security, and a userspace implementation
of CKRM's rule-based classifier (known as RBCE. For CKRM RBCE details
see:
http://sourceforge.net/mailarchive/forum.php?thread_id=8132624&forum_id=35191 )

The additional events are useful because:

exec() often marks a significant change in the nature of
a process. A program similar to top could use this to trigger
a change in the displayed name, memory, etc without having to
rescan all of /proc as frequently. exec() may also be of interest to
auditing and security applications. A CKRM classification engine
written in userspace could use the event to trigger reclassification.

Auditing and security systems are often related to ids. Resource
monitoring and/or management systems often predicate their decisions on
the owner of the process requesting those resources and hence changes in
id are significant events.


Originally I wrote exec and id connectors and relied on previous
connector patches for fork and exit. That approach duplicated a great
deal of code. In comparison to the multi-connector approach this patch
saves 600 lines, reduces the number of per-cpu counters required for
computing sequence numbers, reduces the number of connectors needed, and
slightly reduces the amount of time spent with preemption disabled.

I wrote and tested this patch against 2.6.14-rc2.

Signed-off-by: Matt Helsley <[email protected]>

---

drivers/connector/Kconfig | 8 +
drivers/connector/Makefile | 1
drivers/connector/cn_proc.c | 212 ++++++++++++++++++++++++++++++++++++
fs/exec.c | 3
include/linux/cn_proc.h | 126 +++++++++++++++++++++
include/linux/connector.h | 3
kernel/exit.c | 2
kernel/fork.c | 3
kernel/sys.c | 79 ++++++++++++-
9 files changed, 432 insertions(+), 5 deletions(-)

Index: linux-2.6.14-rc2/include/linux/cn_proc.h
===================================================================
--- /dev/null
+++ linux-2.6.14-rc2/include/linux/cn_proc.h
@@ -0,0 +1,126 @@
+/*
+ * cn_proc.h - process events connector
+ *
+ * Copyright (C) Matt Helsley, IBM Corp. 2005
+ * Based on cn_fork.h by Nguyen Anh Quynh and Guillaume Thouvenin
+ * Original copyright notice follows:
+ * Copyright (C) 2005 Nguyen Anh Quynh <[email protected]>
+ * Copyright (C) 2005 Guillaume Thouvenin <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef CN_PROC_H
+#define CN_PROC_H 1
+
+#include <linux/types.h>
+#include <linux/connector.h>
+
+typedef int __bitwise proc_cn_mcast_op_t;
+enum proc_cn_mcast_op {
+ PROC_CN_MCAST_LISTEN = (__force proc_cn_mcast_op_t)1,
+ PROC_CN_MCAST_IGNORE = (__force proc_cn_mcast_op_t)2
+};
+
+/*
+ * From the user's point of view, the process
+ * ID is the thread group ID and thread ID is the internal
+ * kernel "pid". So, fields are assigned as follow:
+ *
+ * In user space - In kernel space
+ *
+ * parent process ID = parent->tgid
+ * parent thread ID = parent->pid
+ * child process ID = child->tgid
+ * child thread ID = child->pid
+ */
+
+typedef int __bitwise proc_event_what_t;
+struct proc_event {
+ enum what {
+ /* Use successive bits so the enums can be used to record
+ * sets of events as well
+ */
+ PROC_EVENT_FORK = (__force proc_event_what_t)0x0001,
+ PROC_EVENT_EXEC = (__force proc_event_what_t)0x0002,
+ PROC_EVENT_RUID = (__force proc_event_what_t)0x0004,
+ PROC_EVENT_RGID = (__force proc_event_what_t)0x0008,
+ PROC_EVENT_EUID = (__force proc_event_what_t)0x0010,
+ PROC_EVENT_EGID = (__force proc_event_what_t)0x0020,
+ PROC_EVENT_SUID = (__force proc_event_what_t)0x0040,
+ PROC_EVENT_SGID = (__force proc_event_what_t)0x0080,
+ PROC_EVENT_FSUID = (__force proc_event_what_t)0x0100,
+ PROC_EVENT_FSGID = (__force proc_event_what_t)0x0200,
+ PROC_EVENT_EXIT = (__force proc_event_what_t)0x80000000 /* last */
+ } what;
+ int cpu;
+ union { /* must be last field of struct */
+ struct fork_proc_event {
+ pid_t parent_pid;
+ pid_t parent_tgid;
+ pid_t child_pid;
+ pid_t child_tgid;
+ } fork;
+
+ struct exec_proc_event {
+ pid_t process_pid;
+ pid_t process_tgid;
+ } exec;
+
+ struct id_proc_event {
+ pid_t process_pid;
+ pid_t process_tgid;
+ union {
+ uid_t uid;
+ gid_t gid;
+ } from;
+ union {
+ uid_t uid;
+ gid_t gid;
+ } to;
+ } id;
+
+ struct exit_proc_event {
+ pid_t process_pid;
+ pid_t process_tgid;
+ int exit_code;
+ } exit;
+ };
+};
+
+#ifdef __KERNEL__
+#ifdef CONFIG_PROC_EVENTS
+void proc_fork_connector(pid_t ppid, pid_t ptgid, pid_t cpid, pid_t ctgid);
+void proc_exec_connector(pid_t pid, pid_t tgid);
+void proc_id_connector (pid_t pid, pid_t tgid, int from, int to, int which_id);
+void proc_exit_connector(pid_t pid, pid_t tgid, int exit_code);
+#else
+static inline void proc_fork_connector(pid_t ppid, pid_t ptgid, pid_t cpid,
+ pid_t ctgid)
+{}
+
+static inline void proc_exec_connector(pid_t pid, pid_t tgid)
+{}
+
+static inline void proc_id_connector (pid_t pid, pid_t tgid, int from,
+ int to, int which_id)
+{}
+
+static inline void proc_exit_connector(pid_t pid, pid_t tgid, int exit_code)
+{}
+
+#endif /* CONFIG_PROC_EVENTS */
+#endif /* __KERNEL__ */
+#endif /* CN_PROC_H */
Index: linux-2.6.14-rc2/drivers/connector/cn_proc.c
===================================================================
--- /dev/null
+++ linux-2.6.14-rc2/drivers/connector/cn_proc.c
@@ -0,0 +1,212 @@
+/*
+ * cn_proc.c - process events connector
+ *
+ * Copyright (C) Matt Helsley, IBM Corp. 2005
+ * Based on cn_fork.c by Guillaume Thouvenin <[email protected]>
+ * Original copyright notice follows:
+ * Copyright (C) 2005 BULL SA.
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+
+#include <linux/cn_proc.h>
+
+#define CN_PROC_MSG_SIZE (sizeof(struct cn_msg) + sizeof(struct proc_event))
+
+static int proc_event_num_listeners = 0;
+struct cb_id cn_proc_event_id = { CN_IDX_PROC, CN_VAL_PROC };
+
+/* proc_counts is used as the sequence number of the netlink message */
+DEFINE_PER_CPU(__u32, proc_event_counts);
+
+static inline void get_seq (__u32 *ts, int *cpu)
+{
+ *ts = get_cpu_var(proc_event_counts)++;
+ *cpu = smp_processor_id();
+ put_cpu_var(proc_counts);
+}
+
+void proc_fork_connector(pid_t ppid, pid_t ptgid, pid_t cpid, pid_t ctgid)
+{
+ struct cn_msg *msg;
+ struct proc_event *ev;
+ __u8 buffer[CN_PROC_MSG_SIZE];
+
+ if (proc_event_num_listeners < 1)
+ return;
+
+ msg = (struct cn_msg*)buffer;
+ ev = (struct proc_event*)msg->data;
+
+ get_seq(&msg->seq, &ev->cpu);
+
+ ev->what = PROC_EVENT_FORK;
+ ev->fork.parent_pid = ppid;
+ ev->fork.parent_tgid = ptgid;
+ ev->fork.child_pid = cpid;
+ ev->fork.child_tgid = ctgid;
+
+ memcpy(&msg->id, &cn_proc_event_id, sizeof(msg->id));
+ msg->ack = 0; /* not used */
+ msg->len = sizeof(*ev);
+
+ /* If cn_netlink_send() failed, the data is not send */
+ cn_netlink_send(msg, CN_IDX_PROC, GFP_KERNEL);
+}
+
+void proc_exec_connector(pid_t pid, pid_t tgid)
+{
+ struct cn_msg *msg;
+ struct proc_event *ev;
+ __u8 buffer[CN_PROC_MSG_SIZE];
+
+ if (proc_event_num_listeners < 1)
+ return;
+
+ msg = (struct cn_msg*)buffer;
+ ev = (struct proc_event*)msg->data;
+
+ get_seq(&msg->seq, &ev->cpu);
+
+ ev->what = PROC_EVENT_EXEC;
+ ev->exec.process_pid = pid;
+ ev->exec.process_tgid = tgid;
+
+ memcpy(&msg->id, &cn_proc_event_id, sizeof(msg->id));
+ msg->ack = 0; /* not used */
+ msg->len = sizeof(*ev);
+
+ cn_netlink_send(msg, CN_IDX_PROC, GFP_KERNEL);
+}
+
+void proc_id_connector (pid_t task_pid, pid_t task_tgid,
+ int from, int to, int which_id)
+{
+ struct cn_msg *msg;
+ struct proc_event *ev;
+ __u8 buffer[CN_PROC_MSG_SIZE];
+
+ if (proc_event_num_listeners < 1)
+ return;
+
+ msg = (struct cn_msg*)buffer;
+ ev = (struct proc_event*)msg->data;
+
+ get_seq(&msg->seq, &ev->cpu);
+
+ ev->what = which_id;
+ ev->id.process_pid = task_pid;
+ ev->id.process_tgid = task_tgid;
+
+ switch (which_id) {
+ case PROC_EVENT_RUID:
+ case PROC_EVENT_EUID:
+ case PROC_EVENT_SUID:
+ case PROC_EVENT_FSUID:
+ ev->id.from.uid = from;
+ ev->id.to.uid = to;
+ break;
+ case PROC_EVENT_RGID:
+ case PROC_EVENT_EGID:
+ case PROC_EVENT_SGID:
+ case PROC_EVENT_FSGID:
+ ev->id.from.gid = from;
+ ev->id.to.gid = to;
+ break;
+ default:
+ return;
+ break;
+ }
+ memcpy(&msg->id, &cn_proc_event_id, sizeof(msg->id));
+ msg->ack = 0; /* not used */
+ msg->len = sizeof(*ev);
+ cn_netlink_send(msg, CN_IDX_PROC, GFP_KERNEL);
+}
+
+void proc_exit_connector(pid_t pid, pid_t tgid, int exit_code)
+{
+ struct cn_msg *msg;
+ struct proc_event *ev;
+ __u8 buffer[CN_PROC_MSG_SIZE];
+
+ if (proc_event_num_listeners < 1)
+ return;
+
+ msg = (struct cn_msg*)buffer;
+ ev = (struct proc_event*)msg->data;
+
+ get_seq(&msg->seq, &ev->cpu);
+
+ ev->what = PROC_EVENT_EXIT;
+ ev->exit.process_pid = pid;
+ ev->exit.process_tgid = tgid;
+ ev->exit.exit_code = exit_code;
+
+ memcpy(&msg->id, &cn_proc_event_id, sizeof(msg->id));
+ msg->ack = 0; /* not used */
+ msg->len = sizeof(*ev);
+
+ cn_netlink_send(msg, CN_IDX_PROC, GFP_KERNEL);
+}
+
+/**
+ * cn_proc_mcast_ctl
+ * @data: message sent from userspace via the connector
+ */
+static void cn_proc_mcast_ctl(void *data)
+{
+ struct cn_msg *msg = data;
+ enum proc_cn_mcast_op *mc_op = NULL;
+
+ if (!(cn_already_initialized &&
+ (msg->len == sizeof(*mc_op))))
+ return;
+
+ mc_op = (enum proc_cn_mcast_op*)msg->data;
+ switch (*mc_op) {
+ case PROC_CN_MCAST_LISTEN:
+ proc_event_num_listeners++;
+ break;
+ case PROC_CN_MCAST_IGNORE:
+ if (proc_event_num_listeners < 1)
+ break;
+ proc_event_num_listeners--;
+ break;
+ default:
+ break;
+ }
+}
+
+/*
+ * cn_proc_init - initialization entry point
+ *
+ * Adds the connector callback to the connector driver.
+ */
+int __init cn_proc_init(void)
+{
+ if (cn_add_callback(&cn_proc_event_id, "cn_proc",
+ &cn_proc_mcast_ctl)) {
+ printk(KERN_WARNING "cn_proc failed to register\n");
+ return -EINVAL;
+ }
+ return 0;
+}
+
+__initcall(cn_proc_init);
Index: linux-2.6.14-rc2/kernel/exit.c
===================================================================
--- linux-2.6.14-rc2.orig/kernel/exit.c
+++ linux-2.6.14-rc2/kernel/exit.c
@@ -26,10 +26,11 @@
#include <linux/proc_fs.h>
#include <linux/mempolicy.h>
#include <linux/cpuset.h>
#include <linux/syscalls.h>
#include <linux/signal.h>
+#include <linux/cn_proc.h>

#include <asm/uaccess.h>
#include <asm/unistd.h>
#include <asm/pgtable.h>
#include <asm/mmu_context.h>
@@ -805,10 +806,11 @@ fastcall NORET_TYPE void do_exit(long co
if (unlikely(tsk->pid == 1))
panic("Attempted to kill init!");
if (tsk->io_context)
exit_io_context();

+ proc_exit_connector(current->pid, current->tgid, code);
if (unlikely(current->ptrace & PT_TRACE_EXIT)) {
current->ptrace_message = code;
ptrace_notify((PTRACE_EVENT_EXIT << 8) | SIGTRAP);
}

Index: linux-2.6.14-rc2/fs/exec.c
===================================================================
--- linux-2.6.14-rc2.orig/fs/exec.c
+++ linux-2.6.14-rc2/fs/exec.c
@@ -46,10 +46,11 @@
#include <linux/mount.h>
#include <linux/security.h>
#include <linux/syscalls.h>
#include <linux/rmap.h>
#include <linux/acct.h>
+#include <linux/cn_proc.h>

#include <asm/uaccess.h>
#include <asm/mmu_context.h>

#ifdef CONFIG_KMOD
@@ -1098,10 +1099,12 @@ int search_binary_handler(struct linux_b
allow_write_access(bprm->file);
if (bprm->file)
fput(bprm->file);
bprm->file = NULL;
current->did_exec = 1;
+ proc_exec_connector(current->pid,
+ current->tgid);
return retval;
}
read_lock(&binfmt_lock);
put_binfmt(fmt);
if (retval != -ENOEXEC || bprm->mm == NULL)
Index: linux-2.6.14-rc2/include/linux/connector.h
===================================================================
--- linux-2.6.14-rc2.orig/include/linux/connector.h
+++ linux-2.6.14-rc2/include/linux/connector.h
@@ -25,10 +25,13 @@
#include <asm/types.h>

#define CN_IDX_CONNECTOR 0xffffffff
#define CN_VAL_CONNECTOR 0xffffffff

+#define CN_IDX_PROC 0x1
+#define CN_VAL_PROC 0x1
+
#define CN_NETLINK_USERS 1

/*
* Maximum connector's message size.
*/
Index: linux-2.6.14-rc2/kernel/sys.c
===================================================================
--- linux-2.6.14-rc2.orig/kernel/sys.c
+++ linux-2.6.14-rc2/kernel/sys.c
@@ -29,10 +29,11 @@
#include <linux/tty.h>
#include <linux/signal.h>

#include <linux/compat.h>
#include <linux/syscalls.h>
+#include <linux/cn_proc.h>

#include <asm/uaccess.h>
#include <asm/io.h>
#include <asm/unistd.h>

@@ -574,16 +575,29 @@ asmlinkage long sys_setregid(gid_t rgid,
if (new_egid != old_egid)
{
current->mm->dumpable = suid_dumpable;
smp_wmb();
}
+
if (rgid != (gid_t) -1 ||
- (egid != (gid_t) -1 && egid != old_rgid))
+ (egid != (gid_t) -1 && egid != old_rgid)) {
+ proc_id_connector(current->pid, current->tgid,
+ current->sgid, new_egid, PROC_EVENT_SGID);
current->sgid = new_egid;
+ }
+
+ proc_id_connector(current->pid, current->tgid,
+ current->fsgid, new_egid,PROC_EVENT_FSGID);
+ proc_id_connector(current->pid, current->tgid,
+ old_egid, new_egid, PROC_EVENT_EGID);
+ proc_id_connector(current->pid, current->tgid,
+ old_rgid, new_rgid, PROC_EVENT_RGID);
+
current->fsgid = new_egid;
current->egid = new_egid;
current->gid = new_rgid;
+
key_fsgid_changed(current);
return 0;
}

/*
@@ -605,19 +619,31 @@ asmlinkage long sys_setgid(gid_t gid)
if(old_egid != gid)
{
current->mm->dumpable = suid_dumpable;
smp_wmb();
}
+ proc_id_connector(current->pid, current->tgid,
+ current->sgid, gid, PROC_EVENT_SGID);
+ proc_id_connector(current->pid, current->tgid,
+ current->fsgid, gid, PROC_EVENT_FSGID);
+ proc_id_connector(current->pid, current->tgid,
+ current->egid, gid, PROC_EVENT_EGID);
+ proc_id_connector(current->pid, current->tgid,
+ current->gid, gid,PROC_EVENT_RGID);
current->gid = current->egid = current->sgid = current->fsgid = gid;
}
else if ((gid == current->gid) || (gid == current->sgid))
{
if(old_egid != gid)
{
current->mm->dumpable = suid_dumpable;
smp_wmb();
}
+ proc_id_connector(current->pid, current->tgid,
+ current->fsgid, gid, PROC_EVENT_FSGID);
+ proc_id_connector(current->pid, current->tgid,
+ current->egid, gid, PROC_EVENT_EGID);
current->egid = current->fsgid = gid;
}
else
return -EPERM;

@@ -645,10 +671,13 @@ static int set_user(uid_t new_ruid, int
if(dumpclear)
{
current->mm->dumpable = suid_dumpable;
smp_wmb();
}
+
+ proc_id_connector(current->pid, current->tgid,
+ current->uid, new_ruid, PROC_EVENT_RUID);
current->uid = new_ruid;
return 0;
}

/*
@@ -702,14 +731,23 @@ asmlinkage long sys_setreuid(uid_t ruid,
if (new_euid != old_euid)
{
current->mm->dumpable = suid_dumpable;
smp_wmb();
}
+
+ proc_id_connector(current->pid, current->tgid,
+ current->euid, new_euid, PROC_EVENT_EUID);
+ proc_id_connector(current->pid, current->tgid,
+ current->fsuid, new_euid, PROC_EVENT_FSUID);
current->fsuid = current->euid = new_euid;
if (ruid != (uid_t) -1 ||
- (euid != (uid_t) -1 && euid != old_ruid))
+ (euid != (uid_t) -1 && euid != old_ruid)) {
+ proc_id_connector(current->pid, current->tgid,
+ current->suid, new_euid, PROC_EVENT_SUID);
current->suid = current->euid;
+ }
+
current->fsuid = current->euid;

key_fsuid_changed(current);

return security_task_post_setuid(old_ruid, old_euid, old_suid, LSM_SETID_RE);
@@ -752,10 +790,18 @@ asmlinkage long sys_setuid(uid_t uid)
if (old_euid != uid)
{
current->mm->dumpable = suid_dumpable;
smp_wmb();
}
+
+ proc_id_connector(current->pid, current->tgid,
+ old_euid, uid, PROC_EVENT_EUID);
+ proc_id_connector(current->pid, current->tgid,
+ current->fsuid, uid, PROC_EVENT_FSUID);
+ proc_id_connector(current->pid, current->tgid,
+ current->suid, new_suid, PROC_EVENT_SUID);
+
current->fsuid = current->euid = uid;
current->suid = new_suid;

key_fsuid_changed(current);

@@ -797,15 +843,23 @@ asmlinkage long sys_setresuid(uid_t ruid
if (euid != current->euid)
{
current->mm->dumpable = suid_dumpable;
smp_wmb();
}
+ proc_id_connector(current->pid, current->tgid,
+ old_euid, euid, PROC_EVENT_EUID);
current->euid = euid;
}
+
+ proc_id_connector(current->pid, current->tgid,
+ current->fsuid, current->euid, PROC_EVENT_FSUID);
current->fsuid = current->euid;
- if (suid != (uid_t) -1)
+ if (suid != (uid_t) -1) {
+ proc_id_connector(current->pid, current->tgid,
+ old_suid, suid, PROC_EVENT_SUID);
current->suid = suid;
+ }

key_fsuid_changed(current);

return security_task_post_setuid(old_ruid, old_euid, old_suid, LSM_SETID_RES);
}
@@ -847,17 +901,27 @@ asmlinkage long sys_setresgid(gid_t rgid
if (egid != current->egid)
{
current->mm->dumpable = suid_dumpable;
smp_wmb();
}
+ proc_id_connector(current->pid, current->tgid,
+ current->egid, egid, PROC_EVENT_EGID);
current->egid = egid;
}
+ proc_id_connector(current->pid, current->tgid,
+ current->fsgid, current->egid, PROC_EVENT_FSGID);
current->fsgid = current->egid;
- if (rgid != (gid_t) -1)
+ if (rgid != (gid_t) -1) {
+ proc_id_connector(current->pid, current->tgid,
+ current->gid, rgid, PROC_EVENT_RGID);
current->gid = rgid;
- if (sgid != (gid_t) -1)
+ }
+ if (sgid != (gid_t) -1) {
+ proc_id_connector(current->pid, current->tgid,
+ current->sgid, sgid, PROC_EVENT_SGID);
current->sgid = sgid;
+ }

key_fsgid_changed(current);
return 0;
}

@@ -894,10 +958,12 @@ asmlinkage long sys_setfsuid(uid_t uid)
if (uid != old_fsuid)
{
current->mm->dumpable = suid_dumpable;
smp_wmb();
}
+ proc_id_connector(current->pid, current->tgid,
+ old_fsuid, uid, PROC_EVENT_FSUID);
current->fsuid = uid;
}

key_fsuid_changed(current);

@@ -924,13 +990,16 @@ asmlinkage long sys_setfsgid(gid_t gid)
if (gid != old_fsgid)
{
current->mm->dumpable = suid_dumpable;
smp_wmb();
}
+ proc_id_connector(current->pid, current->tgid,
+ old_fsgid, gid, PROC_EVENT_FSGID);
current->fsgid = gid;
key_fsgid_changed(current);
}
+
return old_fsgid;
}

asmlinkage long sys_times(struct tms __user * tbuf)
{
Index: linux-2.6.14-rc2/kernel/fork.c
===================================================================
--- linux-2.6.14-rc2.orig/kernel/fork.c
+++ linux-2.6.14-rc2/kernel/fork.c
@@ -40,10 +40,11 @@
#include <linux/mount.h>
#include <linux/audit.h>
#include <linux/profile.h>
#include <linux/rmap.h>
#include <linux/acct.h>
+#include <linux/cn_proc.h>

#include <asm/pgtable.h>
#include <asm/pgalloc.h>
#include <asm/uaccess.h>
#include <asm/mmu_context.h>
@@ -1287,10 +1288,12 @@ long do_fork(unsigned long clone_flags,
if (unlikely (trace)) {
current->ptrace_message = pid;
ptrace_notify ((trace << 8) | SIGTRAP);
}

+ proc_fork_connector(current->pid, current->tgid,
+ p->pid, p->tgid);
if (clone_flags & CLONE_VFORK) {
wait_for_completion(&vfork);
if (unlikely (current->ptrace & PT_TRACE_VFORK_DONE))
ptrace_notify ((PTRACE_EVENT_VFORK_DONE << 8) | SIGTRAP);
}
Index: linux-2.6.14-rc2/drivers/connector/Kconfig
===================================================================
--- linux-2.6.14-rc2.orig/drivers/connector/Kconfig
+++ linux-2.6.14-rc2/drivers/connector/Kconfig
@@ -8,6 +8,14 @@ config CONNECTOR
of the netlink socket protocol.

Connector support can also be built as a module. If so, the module
will be called cn.ko.

+config PROC_EVENTS
+ bool
+ depends on CONNECTOR
+ default y
+ ---help---
+ Provide a connector that reports process events to userspace. Send
+ events such as fork, exec, id change (uid, gid, suid, etc), and exit.
+
endmenu
Index: linux-2.6.14-rc2/drivers/connector/Makefile
===================================================================
--- linux-2.6.14-rc2.orig/drivers/connector/Makefile
+++ linux-2.6.14-rc2/drivers/connector/Makefile
@@ -1,3 +1,4 @@
obj-$(CONFIG_CONNECTOR) += cn.o
+obj-$(CONFIG_PROC_EVENTS) += cn_proc.o

cn-y += cn_queue.o connector.o



2005-09-29 02:53:34

by Matt Helsley

[permalink] [raw]
Subject: [RFC] Process Events Connector (test program)

The following test program can be used to print the process events
received via the proposed connector.

Cheers,
-Matt Helsley < matthltc at us.ibm.com >

---

/*
* test process event connector - test_cn_proc.c
*
* Listens for process events (fork, exec, change uid/gid/..., and exit)
* received through a kernel connector and prints them.
*
* Copyright (C) Matt Helsley, IBM Corp. 2005
* Derived from fcctl.c by Guillaume Thouvenin
* Original copyright notice follows:
*
* Copyright (C) 2005 BULL SA.
* Written by Guillaume Thouvenin <[email protected]>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#include <sys/socket.h>
#include <sys/types.h>

#include <linux/connector.h>
#include <linux/netlink.h>
#include "linux/cn_proc.h"

#include <signal.h>
#include <setjmp.h>

#define SEND_MESSAGE_LEN (NLMSG_LENGTH(sizeof(struct cn_msg) + \
sizeof(enum proc_cn_mcast_op)))
#define RECV_MESSAGE_LEN (NLMSG_LENGTH(sizeof(struct cn_msg) + \
sizeof(struct proc_event)))

#define SEND_MESSAGE_SIZE (NLMSG_SPACE(SEND_MESSAGE_LEN))
#define RECV_MESSAGE_SIZE (NLMSG_SPACE(RECV_MESSAGE_LEN))

#define max(x,y) ((y)<(x)?(x):(y))
#define min(x,y) ((y)>(x)?(x):(y))

#define BUFF_SIZE (max(max(SEND_MESSAGE_SIZE, RECV_MESSAGE_SIZE), 1024))
#define MIN_RECV_SIZE (min(SEND_MESSAGE_SIZE, RECV_MESSAGE_SIZE))

#define PROC_CN_MCAST_LISTEN (1)
#define PROC_CN_MCAST_IGNORE (2)

/*
* SIGINT causes the program to exit gracefully
* this could happen any time after the LISTEN message has
* been sent
*/
#define INTR_SIG SIGINT
sigjmp_buf g_jmp;

void handle_intr (int signum)
{
siglongjmp(g_jmp, signum);
}

void handle_msg (struct cn_msg *cn_hdr)
{
struct proc_event *ev;

/* print the message */
printf("cn seq: %d\ncn ack: %d\n",
cn_hdr->seq, cn_hdr->ack);
ev = (struct proc_event*)cn_hdr->data;
printf("ev cpu: %d\n", ev->cpu);
switch(ev->what){
case PROC_EVENT_FORK:
printf("event: fork\n\tparent:\t%d %d\n\tchild: %d %d\n",
ev->fork.parent_pid,
ev->fork.parent_tgid,
ev->fork.child_pid,
ev->fork.child_tgid);
break;
case PROC_EVENT_EXEC:
printf("event: exec\n\tprocess:\t%d %d\n",
ev->exec.process_pid,
ev->exec.process_tgid);
break;
case PROC_EVENT_RUID:
printf("event: ruid\n\tprocess:\t%d %d\n\t%d -> %d\n",
ev->id.process_pid,
ev->id.process_tgid,
ev->id.from.uid,
ev->id.to.uid);
break;
case PROC_EVENT_RGID:
printf("event: rgid\n\tprocess:\t%d %d\n\t%d -> %d\n",
ev->id.process_pid,
ev->id.process_tgid,
ev->id.from.gid,
ev->id.to.gid);
break;
case PROC_EVENT_EUID:
printf("event: euid\n\tprocess:\t%d %d\n\t%d -> %d\n",
ev->id.process_pid,
ev->id.process_tgid,
ev->id.from.uid,
ev->id.to.uid);
break;
case PROC_EVENT_EGID:
printf("event: egid\n\tprocess:\t%d %d\n\t%d -> %d\n",
ev->id.process_pid,
ev->id.process_tgid,
ev->id.from.gid,
ev->id.to.gid);
break;
case PROC_EVENT_SUID:
printf("event: suid\n\tprocess:\t%d %d\n\t%d -> %d\n",
ev->id.process_pid,
ev->id.process_tgid,
ev->id.from.uid,
ev->id.to.uid);
break;
case PROC_EVENT_SGID:
printf("event: sgid\n\tprocess:\t%d %d\n\t%d -> %d\n",
ev->id.process_pid,
ev->id.process_tgid,
ev->id.from.gid,
ev->id.to.gid);
break;
case PROC_EVENT_FSUID:
printf("event: fsuid\n\tprocess:\t%d %d\n\t%d -> %d\n",
ev->id.process_pid,
ev->id.process_tgid,
ev->id.from.uid,
ev->id.to.uid);
break;
case PROC_EVENT_FSGID:
printf("event: fsgid\n\tprocess:\t%d %d\n\t%d -> %d\n",
ev->id.process_pid,
ev->id.process_tgid,
ev->id.from.gid,
ev->id.to.gid);
break;
case PROC_EVENT_EXIT:
printf("event: exit\n\tprocess:\t%d %d\n\texit code:\t%d\n",
ev->exit.process_pid,
ev->exit.process_tgid,
ev->exit.exit_code);
break;
default:
printf("event <unknown>\n");
break;
}
}

int main(int argc, char **argv)
{
int sk_nl;
int err;
struct sockaddr_nl my_nla, kern_nla, from_nla;
socklen_t from_nla_len;
char buff[BUFF_SIZE];
int rc = -1;
struct nlmsghdr *nl_hdr;
struct cn_msg *cn_hdr;
enum proc_cn_mcast_op *mcop_msg;
size_t recv_len = 0;

if (getuid() != 0) {
printf("Only root can start/stop the fork connector\n");
return 0;
}

if (argc != 1)
return 0;

/*
* Create an endpoint for communication. Use the kernel user
* interface device (PF_NETLINK) which is a datagram oriented
* service (SOCK_DGRAM). The protocol used is the connector
* protocol (NETLINK_CONNECTOR)
*/
sk_nl = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_CONNECTOR);
if (sk_nl == -1) {
printf("socket sk_nl error");
return rc;
}

my_nla.nl_family = AF_NETLINK;
my_nla.nl_groups = CN_IDX_PROC;
my_nla.nl_pid = getpid();

kern_nla.nl_family = AF_NETLINK;
kern_nla.nl_groups = CN_IDX_PROC;
kern_nla.nl_pid = 1;

err = bind(sk_nl, (struct sockaddr *)&my_nla, sizeof(my_nla));
if (err == -1) {
printf("binding sk_nl error");
goto close_and_exit;
}

nl_hdr = (struct nlmsghdr *)buff;
cn_hdr = (struct cn_msg *)NLMSG_DATA(nl_hdr);
mcop_msg = (enum proc_cn_mcast_op*)&cn_hdr->data[0];
if (sigsetjmp(g_jmp, INTR_SIG) != 0) {
printf("sending proc connector: PROC_CN_MCAST_IGNORE... ");
memset(buff, 0, sizeof(buff));
*mcop_msg = PROC_CN_MCAST_IGNORE;
} else {
printf("sending proc connector: PROC_CN_MCAST_LISTEN... ");
memset(buff, 0, sizeof(buff));
*mcop_msg = PROC_CN_MCAST_LISTEN;
signal(INTR_SIG, handle_intr);
}

/* fill the netlink header */
nl_hdr->nlmsg_len = SEND_MESSAGE_LEN;
nl_hdr->nlmsg_type = NLMSG_DONE;
nl_hdr->nlmsg_flags = 0;
nl_hdr->nlmsg_seq = 0;
nl_hdr->nlmsg_pid = getpid();

/* fill the connector header */
cn_hdr->id.idx = CN_IDX_PROC;
cn_hdr->id.val = CN_VAL_PROC;
cn_hdr->seq = 0;
cn_hdr->ack = 0;

cn_hdr->len = sizeof(enum proc_cn_mcast_op);
if (send(sk_nl, nl_hdr, nl_hdr->nlmsg_len, 0) != nl_hdr->nlmsg_len) {
printf("failed to send proc connector mcast ctl op!\n");
goto close_and_exit;
}
printf("sent\n");

if (*mcop_msg == PROC_CN_MCAST_IGNORE) {
rc = 0;
goto close_and_exit;
}

printf("Reading process events from proc connector.\n"
"Hit Ctrl-C to exit\n");
for(memset(buff, 0, sizeof(buff)), from_nla_len = sizeof(from_nla);
; memset(buff, 0, sizeof(buff)), from_nla_len = sizeof(from_nla)) {
struct nlmsghdr *nlh = (struct nlmsghdr*)buff;
memcpy(&from_nla, &kern_nla, sizeof(from_nla));
recv_len = recvfrom(sk_nl, buff, BUFF_SIZE, 0,
(struct sockaddr*)&from_nla, &from_nla_len);
if (recv_len < 1)
continue;
while (NLMSG_OK(nlh, recv_len)) {
cn_hdr = NLMSG_DATA(nlh);
if (nlh->nlmsg_type == NLMSG_NOOP)
continue;
if ((nlh->nlmsg_type == NLMSG_ERROR) ||
(nlh->nlmsg_type == NLMSG_OVERRUN))
break;
handle_msg(cn_hdr);
if (nlh->nlmsg_type == NLMSG_DONE)
break;
nlh = NLMSG_NEXT(nlh, recv_len);
}
}

close_and_exit:
close(sk_nl);
exit(rc);

return 0;
}