On Tue, 2006-02-28 at 10:26 -0500, Stephen Smalley wrote:
On Tue, 2006-02-28 at 16:12 +0100, Stephan Mueller wrote:
> On Tuesday 28 February 2006 16:01, Stephen Smalley wrote:
> > It sounds like the functionality should actually be expanded based on
> > the above. Possibly even calling audit_ipc_context() from the SELinux
> > hooks themselves to capture all cases.
>
> Sorry, I cannot comment on that as I am a bit unfamiliar with SELinux atm. But
> as said above: subjects and objects must be listed in the audit trail at any
> case (as long as there is a corresponding object and/or subject).
The reason I suggested it is that we already (hopefully) have complete
coverage of all security-relevant IPC operations in the set of LSM
hooks, so if we insert an audit_ipc_context() call into each of the
SELinux IPC hooks (except for selinux_ipc_permission, as that would
yield redundant collection), then we should have collection for all
cases without needing to insert new hooks in the IPC code itself. Note
that SELinux itself will already generate an audit record with the
subject and object contexts if there is a MAC denial, but IIUC, then you
want the collection to occur always even for success or other forms of
failure (e.g. DAC denial, general error).
Hi-
I posted the message below under the title "[PATCH] Rework of IPC
Auditing". However, I should have probably posted it under this thread
as this thread had considerable activity (and there's been no response
under to this patch under the new subject).
https://www.redhat.com/archives/linux-audit/2006-March/msg00088.html
Copy-and-paste of that message below.
----
I've reworked the handling of IPC auditing altogether. I did this with
Klaus Weidner of atsec over my shoulder for much of it. He helped and
advised exactly where the ipc code should be hooked and approved of the
coverage at this point in terms of LSPP certification. To that extent,
I very much thank Klaus and invite him to add any additional comments to
this post.
There are two key changes in this patch against Al's current git tree:
1) The audit_ipc_perms() function has been split into two different
functions:
- audit_ipc_obj()
- audit_ipc_new_perm()
There's a key shift here... The audit_ipc_obj() collects the uid, gid,
mode, and SElinux context label of the current ipc object. This
audit_ipc_obj() hook is now found in several places. Most notably, it
is hooked in ipcperms(), which is called in various places around the
ipc code permforming a MAC check. Additionally there are several places
where *checkid() is used to validate that an operation is being
performed on a valid object while not necessarily having a nearby
ipcperms() call. In these locations, audit_ipc_obj() is called to
ensure that the information is captured by the audit system.
The audit_ipc_new_perm() function is called any time the permissions on
the ipc object changes. In this case, the NEW permissions are recorded
(and note that an audit_ipc_obj() call exists just a few lines before
each instance).
2) Support for an AUDIT_IPC_NEW_PERM audit message type. This allows
for separate auxiliary audit records for normal operations on an IPC
object and permissions changes. Note that the same struct
audit_aux_data_ipcctl is used and populated, however there are separate
audit_log_format statements based on the type of the message. Finally,
the AUDIT_IPC block of code in audit_free_aux() was extended to handle
aux messages of this new type. No more mem leaks I hope ;-)
Note that this does:
- Change the format of the audit record (sorry test guys)
- And add a new audit record that will need to be tested (sorry again
test guys)
The new auxiliary audit record helps clarify the purpose of the separate
messages. And the separate audit_ipc_* functions eliminates the need
for passing known invalid values where those aren't used and
conditionally not printing those instances (which was messy). In the
old case, it was confusing in that the same message type would mix old
and new ids depending on which code called the audit hook. Now, it's
clear what is being recorded.
Thus, with Klaus, I did an exhaustive examination through the ipc code
and we believe that this patch captures all of the pertinent audit
information about ipc objects for LSPP. Note that the ipc/mqueue.c
POSIX message queue filesystem was NOT hooked, as there do not appear to
by any permissions checks in that code. It's our assumption that the
messages necessary will be covered by audit hooks in the filesystem code
as it looks like this ipc type is built on top of that.
I've compiled and booted the kernel with this patch in all valid
combinations of Audit/AuditSystemCall/SELinux compiled in and out. I
have not tested with SELinux and/or Audit off.
I think at this point this patch is ready for inclusion in our LSPP
kernels and some testing. I have a couple of questions that remain:
- I'd like to run these changes carefully by someone very familiar with
the Linux ipc code. There are some strange nuances between msg.c,
sem.c, and shm.c that I'd like to make sure are interpreted correctly.
Al, is this your area?
- There are a couple of warnings that have been in the ipc compilations
for some time now about possibly using setbuf.* before initialization.
I'm wondering if anyone thinks these compiler warnings are founded and
if anyone has suggestions to silence them?
- Finally a careful look that audit_free_aux() is accurately cleaning
up the memory.
Patch below. Thanks!
:-Dustin
diff --git a/include/linux/audit.h b/include/linux/audit.h
index dd4f759..3614d13 100644
--- a/include/linux/audit.h
+++ b/include/linux/audit.h
@@ -82,6 +82,7 @@
#define AUDIT_CONFIG_CHANGE 1305 /* Audit system configuration change */
#define AUDIT_SOCKADDR 1306 /* sockaddr copied as syscall arg */
#define AUDIT_CWD 1307 /* Current working directory */
+#define AUDIT_IPC_NEW_PERM 1308 /* IPC new permissions record type */
#define AUDIT_AVC 1400 /* SE Linux avc denial or grant */
#define AUDIT_SELINUX_ERR 1401 /* Internal SE Linux Errors */
@@ -315,7 +316,8 @@ extern void auditsc_get_stamp(struct aud
struct timespec *t, unsigned int *serial);
extern int audit_set_loginuid(struct task_struct *task, uid_t loginuid);
extern uid_t audit_get_loginuid(struct audit_context *ctx);
-extern int audit_ipc_perms(unsigned long qbytes, uid_t uid, gid_t gid, mode_t mode,
struct kern_ipc_perm *ipcp);
+extern int audit_ipc_obj(struct kern_ipc_perm *ipcp);
+extern int audit_ipc_new_perm(unsigned long qbytes, uid_t uid, gid_t gid, mode_t mode);
extern int audit_socketcall(int nargs, unsigned long *args);
extern int audit_sockaddr(int len, void *addr);
extern int audit_avc_path(struct dentry *dentry, struct vfsmount *mnt);
@@ -335,7 +337,8 @@ extern int audit_set_macxattr(const char
#define audit_inode_child(d,i,p) do { ; } while (0)
#define auditsc_get_stamp(c,t,s) do { BUG(); } while (0)
#define audit_get_loginuid(c) ({ -1; })
-#define audit_ipc_perms(q,u,g,m,i) ({ 0; })
+#define audit_ipc_obj(i) ({ 0; })
+#define audit_ipc_new_perm(q,u,g,m) ({ 0; })
#define audit_socketcall(n,a) ({ 0; })
#define audit_sockaddr(len, addr) ({ 0; })
#define audit_avc_path(dentry, mnt) ({ 0; })
diff --git a/ipc/msg.c b/ipc/msg.c
index 8c30ec2..382594f 100644
--- a/ipc/msg.c
+++ b/ipc/msg.c
@@ -13,6 +13,9 @@
* mostly rewritten, threaded and wake-one semantics added
* MSGMAX limit removed, sysctl's added
* (c) 1999 Manfred Spraul <manfred(a)colorfullife.com>
+ *
+ * support for audit of ipc object properties and permission changes
+ * Dustin Kirkland <dustin.kirkland(a)us.ibm.com>
*/
#include <linux/capability.h>
@@ -446,6 +449,11 @@ asmlinkage long sys_msgctl (int msqid, i
if (msg_checkid(msq,msqid))
goto out_unlock_up;
ipcp = &msq->q_perm;
+
+ err = audit_ipc_obj(ipcp);
+ if (err)
+ goto out_unlock_up;
+
err = -EPERM;
if (current->euid != ipcp->cuid &&
current->euid != ipcp->uid && !capable(CAP_SYS_ADMIN))
@@ -459,7 +467,8 @@ asmlinkage long sys_msgctl (int msqid, i
switch (cmd) {
case IPC_SET:
{
- if ((err = audit_ipc_perms(setbuf.qbytes, setbuf.uid, setbuf.gid, setbuf.mode, ipcp)))
+ err = audit_ipc_new_perm(setbuf.qbytes, setbuf.uid, setbuf.gid, setbuf.mode);
+ if (err)
goto out_unlock_up;
err = -EPERM;
diff --git a/ipc/sem.c b/ipc/sem.c
index 59696a8..bb5f9f8 100644
--- a/ipc/sem.c
+++ b/ipc/sem.c
@@ -61,6 +61,9 @@
* (c) 2001 Red Hat Inc <alan(a)redhat.com>
* Lockless wakeup
* (c) 2003 Manfred Spraul <manfred(a)colorfullife.com>
+ *
+ * support for audit of ipc object properties and permission changes
+ * Dustin Kirkland <dustin.kirkland(a)us.ibm.com>
*/
#include <linux/config.h>
@@ -819,6 +822,11 @@ static int semctl_down(int semid, int se
goto out_unlock;
}
ipcp = &sma->sem_perm;
+
+ err = audit_ipc_obj(ipcp);
+ if (err)
+ goto out_unlock;
+
if (current->euid != ipcp->cuid &&
current->euid != ipcp->uid && !capable(CAP_SYS_ADMIN)) {
err=-EPERM;
@@ -835,7 +843,8 @@ static int semctl_down(int semid, int se
err = 0;
break;
case IPC_SET:
- if ((err = audit_ipc_perms(0, setbuf.uid, setbuf.gid, setbuf.mode, ipcp)))
+ err = audit_ipc_new_perm(0, setbuf.uid, setbuf.gid, setbuf.mode);
+ if (err)
goto out_unlock;
ipcp->uid = setbuf.uid;
ipcp->gid = setbuf.gid;
diff --git a/ipc/shm.c b/ipc/shm.c
index a88c8a0..f419f5e 100644
--- a/ipc/shm.c
+++ b/ipc/shm.c
@@ -13,6 +13,8 @@
* Shared /dev/zero support, Kanoj Sarcar <kanoj(a)sgi.com>
* Move the mm functionality over to mm/shmem.c, Christoph Rohland <cr(a)sap.com>
*
+ * support for audit of ipc object properties and permission changes
+ * Dustin Kirkland <dustin.kirkland(a)us.ibm.com>
*/
#include <linux/config.h>
@@ -540,6 +542,10 @@ asmlinkage long sys_shmctl (int shmid, i
if(err)
goto out_unlock;
+ err = audit_ipc_obj(&(shp->shm_perm));
+ if (err)
+ goto out_unlock;
+
if (!capable(CAP_IPC_LOCK)) {
err = -EPERM;
if (current->euid != shp->shm_perm.uid &&
@@ -592,6 +598,10 @@ asmlinkage long sys_shmctl (int shmid, i
if(err)
goto out_unlock_up;
+ err = audit_ipc_obj(&(shp->shm_perm));
+ if (err)
+ goto out_unlock_up;
+
if (current->euid != shp->shm_perm.uid &&
current->euid != shp->shm_perm.cuid &&
!capable(CAP_SYS_ADMIN)) {
@@ -625,11 +635,15 @@ asmlinkage long sys_shmctl (int shmid, i
err=-EINVAL;
if(shp==NULL)
goto out_up;
- if ((err = audit_ipc_perms(0, setbuf.uid, setbuf.gid, setbuf.mode,
&(shp->shm_perm))))
- goto out_unlock_up;
err = shm_checkid(shp,shmid);
if(err)
goto out_unlock_up;
+ err = audit_ipc_obj(&(shp->shm_perm));
+ if (err)
+ goto out_unlock_up;
+ err = audit_ipc_new_perm(0, setbuf.uid, setbuf.gid, setbuf.mode);
+ if (err)
+ goto out_unlock_up;
err=-EPERM;
if (current->euid != shp->shm_perm.uid &&
current->euid != shp->shm_perm.cuid &&
diff --git a/ipc/util.c b/ipc/util.c
index e37e1e9..be79943 100644
--- a/ipc/util.c
+++ b/ipc/util.c
@@ -10,6 +10,8 @@
* Manfred Spraul <manfred(a)colorfullife.com>
* Oct 2002 - One lock per IPC id. RCU ipc_free for lock-free grow_ary().
* Mingming Cao <cmm(a)us.ibm.com>
+ * Mar 2006 - support for audit of ipc object properties
+ * Dustin Kirkland <dustin.kirkland(a)us.ibm.com>
*/
#include <linux/config.h>
@@ -467,9 +469,10 @@ void ipc_rcu_putref(void *ptr)
int ipcperms (struct kern_ipc_perm *ipcp, short flag)
{ /* flag will most probably be 0 or S_...UGO from <linux/stat.h> */
- int requested_mode, granted_mode;
-
- audit_ipc_context(ipcp);
+ int requested_mode, granted_mode, err;
+
+ if (unlikely((err = audit_ipc_obj(ipcp))))
+ return err;
requested_mode = (flag >> 6) | (flag >> 3) | flag;
granted_mode = ipcp->mode;
if (current->euid == ipcp->cuid || current->euid == ipcp->uid)
diff --git a/kernel/auditsc.c b/kernel/auditsc.c
index cd83289..acbb424 100644
--- a/kernel/auditsc.c
+++ b/kernel/auditsc.c
@@ -414,10 +414,10 @@ static inline void audit_free_aux(struct
dput(axi->dentry);
mntput(axi->mnt);
}
- if ( aux->type == AUDIT_IPC ) {
+ if ( aux->type == AUDIT_IPC ||
+ aux->type == AUDIT_IPC_NEW_PERM ) {
struct audit_aux_data_ipcctl *axi = (void *)aux;
- if (axi->ctx)
- kfree(axi->ctx);
+ kfree(axi->ctx);
}
context->aux = aux->next;
@@ -618,8 +618,15 @@ static void audit_log_exit(struct audit_
case AUDIT_IPC: {
struct audit_aux_data_ipcctl *axi = (void *)aux;
audit_log_format(ab,
- " qbytes=%lx iuid=%u igid=%u mode=%x obj=%s",
- axi->qbytes, axi->uid, axi->gid, axi->mode, axi->ctx);
+ " iuid=%u igid=%u mode=%x obj=%s",
+ axi->uid, axi->gid, axi->mode, axi->ctx);
+ break; }
+
+ case AUDIT_IPC_NEW_PERM: {
+ struct audit_aux_data_ipcctl *axi = (void *)aux;
+ audit_log_format(ab,
+ " new_qbytes=%lx new_iuid=%u new_igid=%u new_mode=%x",
+ axi->qbytes, axi->uid, axi->gid, axi->mode);
break; }
case AUDIT_SOCKETCALL: {
@@ -1149,7 +1156,36 @@ uid_t audit_get_loginuid(struct audit_co
}
/**
- * audit_ipc_perms - record audit data for ipc
+ * audit_ipc_obj - record audit data for ipc object
+ * @ipcp: ipc permissions
+ *
+ * Returns 0 for success or NULL context or < 0 on error.
+ */
+int audit_ipc_obj(struct kern_ipc_perm *ipcp)
+{
+ struct audit_aux_data_ipcctl *ax;
+ struct audit_context *context = current->audit_context;
+
+ if (likely(!context))
+ return 0;
+
+ ax = kmalloc(sizeof(*ax), GFP_ATOMIC);
+ if (!ax)
+ return -ENOMEM;
+
+ ax->uid = ipcp->uid;
+ ax->gid = ipcp->gid;
+ ax->mode = ipcp->mode;
+ ax->ctx = audit_ipc_context(ipcp);
+
+ ax->d.type = AUDIT_IPC;
+ ax->d.next = context->aux;
+ context->aux = (void *)ax;
+ return 0;
+}
+
+/**
+ * audit_ipc_new_perm - record audit data for new ipc permissions
* @qbytes: msgq bytes
* @uid: msgq user id
* @gid: msgq group id
@@ -1157,7 +1193,7 @@ uid_t audit_get_loginuid(struct audit_co
*
* Returns 0 for success or NULL context or < 0 on error.
*/
-int audit_ipc_perms(unsigned long qbytes, uid_t uid, gid_t gid, mode_t mode, struct
kern_ipc_perm *ipcp)
+int audit_ipc_new_perm(unsigned long qbytes, uid_t uid, gid_t gid, mode_t mode)
{
struct audit_aux_data_ipcctl *ax;
struct audit_context *context = current->audit_context;
@@ -1173,9 +1209,8 @@ int audit_ipc_perms(unsigned long qbytes
ax->uid = uid;
ax->gid = gid;
ax->mode = mode;
- ax->ctx = audit_ipc_context(ipcp);
- ax->d.type = AUDIT_IPC;
+ ax->d.type = AUDIT_IPC_NEW_PERM;
ax->d.next = context->aux;
context->aux = (void *)ax;
return 0;