Newer
Older
/* auditsc.c -- System-call auditing support
* Handles all system-call specific auditing features.
*
* Copyright 2003-2004 Red Hat Inc., Durham, North Carolina.
* Copyright 2005 Hewlett-Packard Development Company, L.P.
* Copyright (C) 2005, 2006 IBM Corporation
* All Rights Reserved.
*
* 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
*
* Written by Rickard E. (Rik) Faith <faith@redhat.com>
*
* Many of the ideas implemented here are from Stephen C. Tweedie,
* especially the idea of avoiding a copy by using getname.
*
* The method for actual interception of syscall entry and exit (not in
* this file -- see entry.S) is based on a GPL'd patch written by
* okir@suse.de and Copyright 2003 SuSE Linux AG.
*
* POSIX message queue support added by George Wilson <ltcgcw@us.ibm.com>,
* 2006.
*
* The support of additional filter rules compares (>, <, >=, <=) was
* added by Dustin Kirkland <dustin.kirkland@us.ibm.com>, 2005.
*
* Modified by Amy Griffis <amy.griffis@hp.com> to collect additional
* filesystem information.
*
* Subject and object context labeling support added by <danjones@us.ibm.com>
* and <dustin.kirkland@us.ibm.com> for LSPP certification compliance.
*/
#include <linux/init.h>
#include <asm/types.h>
#include <asm/types.h>
#include <linux/fs.h>
#include <linux/namei.h>
#include <linux/mount.h>
#include <linux/socket.h>
#include <linux/mqueue.h>
#include <linux/audit.h>
#include <linux/personality.h>
#include <linux/time.h>
#include <linux/security.h>
#include <linux/list.h>
#include <linux/selinux.h>
#include "audit.h"
extern struct list_head audit_filter_list[];
/* No syscall auditing will take place unless audit_enabled != 0. */
extern int audit_enabled;
/* AUDIT_NAMES is the number of slots we reserve in the audit_context
* for saving names from getname(). */
#define AUDIT_NAMES 20
/* AUDIT_NAMES_RESERVED is the number of slots we reserve in the
* audit_context from being used for nameless inodes from
* path_lookup. */
#define AUDIT_NAMES_RESERVED 7
/* Indicates that audit should log the full pathname. */
#define AUDIT_NAME_FULL -1
/* number of audit rules */
int audit_n_rules;
/* When fs/namei.c:getname() is called, we store the pointer in name and
* we don't let putname() free it (instead we free all of the saved
* pointers at syscall exit time).
*
* Further, in fs/namei.c:path_lookup() we store the inode and device. */
struct audit_names {
const char *name;
int name_len; /* number of name's characters to log */
unsigned name_put; /* call __putname() for this name */
unsigned long ino;
dev_t dev;
umode_t mode;
uid_t uid;
gid_t gid;
dev_t rdev;
};
struct audit_aux_data {
struct audit_aux_data *next;
int type;
};
#define AUDIT_AUX_IPCPERM 0
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
struct audit_aux_data_mq_open {
struct audit_aux_data d;
int oflag;
mode_t mode;
struct mq_attr attr;
};
struct audit_aux_data_mq_sendrecv {
struct audit_aux_data d;
mqd_t mqdes;
size_t msg_len;
unsigned int msg_prio;
struct timespec abs_timeout;
};
struct audit_aux_data_mq_notify {
struct audit_aux_data d;
mqd_t mqdes;
struct sigevent notification;
};
struct audit_aux_data_mq_getsetattr {
struct audit_aux_data d;
mqd_t mqdes;
struct mq_attr mqstat;
};
struct audit_aux_data_ipcctl {
struct audit_aux_data d;
struct ipc_perm p;
unsigned long qbytes;
uid_t uid;
gid_t gid;
mode_t mode;
struct audit_aux_data_execve {
struct audit_aux_data d;
int argc;
int envc;
char mem[0];
};
struct audit_aux_data_socketcall {
struct audit_aux_data d;
int nargs;
unsigned long args[0];
};
struct audit_aux_data_sockaddr {
struct audit_aux_data d;
int len;
char a[0];
};
struct audit_aux_data_path {
struct audit_aux_data d;
struct dentry *dentry;
struct vfsmount *mnt;
};
/* The per-task audit context. */
struct audit_context {
int dummy; /* must be the first element */
int in_syscall; /* 1 if task is in a syscall */
enum audit_state state;
unsigned int serial; /* serial number for record */
struct timespec ctime; /* time of syscall entry */
uid_t loginuid; /* login uid (identity) */
int major; /* syscall number */
unsigned long argv[4]; /* syscall arguments */
int return_valid; /* return code is valid */

David Woodhouse
committed
long return_code;/* syscall return code */
int auditable; /* 1 if record should be written */
int name_count;
struct audit_names names[AUDIT_NAMES];
char * filterkey; /* key for rule that triggered record */
struct dentry * pwd;
struct vfsmount * pwdmnt;
struct audit_context *previous; /* For nested syscalls */
struct audit_aux_data *aux;
/* Save things to print about task_struct */
uid_t uid, euid, suid, fsuid;
gid_t gid, egid, sgid, fsgid;
unsigned long personality;

David Woodhouse
committed
int arch;
#if AUDIT_DEBUG
int put_count;
int ino_count;
#endif
};
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
#define ACC_MODE(x) ("\004\002\006\006"[(x)&O_ACCMODE])
static inline int open_arg(int flags, int mask)
{
int n = ACC_MODE(flags);
if (flags & (O_TRUNC | O_CREAT))
n |= AUDIT_PERM_WRITE;
return n & mask;
}
static int audit_match_perm(struct audit_context *ctx, int mask)
{
unsigned n = ctx->major;
switch (audit_classify_syscall(ctx->arch, n)) {
case 0: /* native */
if ((mask & AUDIT_PERM_WRITE) &&
audit_match_class(AUDIT_CLASS_WRITE, n))
return 1;
if ((mask & AUDIT_PERM_READ) &&
audit_match_class(AUDIT_CLASS_READ, n))
return 1;
if ((mask & AUDIT_PERM_ATTR) &&
audit_match_class(AUDIT_CLASS_CHATTR, n))
return 1;
return 0;
case 1: /* 32bit on biarch */
if ((mask & AUDIT_PERM_WRITE) &&
audit_match_class(AUDIT_CLASS_WRITE_32, n))
return 1;
if ((mask & AUDIT_PERM_READ) &&
audit_match_class(AUDIT_CLASS_READ_32, n))
return 1;
if ((mask & AUDIT_PERM_ATTR) &&
audit_match_class(AUDIT_CLASS_CHATTR_32, n))
return 1;
return 0;
case 2: /* open */
return mask & ACC_MODE(ctx->argv[1]);
case 3: /* openat */
return mask & ACC_MODE(ctx->argv[2]);
case 4: /* socketcall */
return ((mask & AUDIT_PERM_WRITE) && ctx->argv[0] == SYS_BIND);
case 5: /* execve */
return mask & AUDIT_PERM_EXEC;
default:
return 0;
}
}
/* Determine if any context name data matches a rule's watch data */
/* Compare a task_struct with an audit_rule. Return 1 on match, 0
* otherwise. */
static int audit_filter_rules(struct task_struct *tsk,
struct audit_krule *rule,
struct audit_field *f = &rule->fields[i];
result = audit_comparator(tsk->pid, f->op, f->val);
if (ctx) {
if (!ctx->ppid)
ctx->ppid = sys_getppid();
result = audit_comparator(ctx->ppid, f->op, f->val);
result = audit_comparator(tsk->uid, f->op, f->val);
result = audit_comparator(tsk->euid, f->op, f->val);
result = audit_comparator(tsk->suid, f->op, f->val);
result = audit_comparator(tsk->fsuid, f->op, f->val);
result = audit_comparator(tsk->gid, f->op, f->val);
result = audit_comparator(tsk->egid, f->op, f->val);
result = audit_comparator(tsk->sgid, f->op, f->val);
result = audit_comparator(tsk->fsgid, f->op, f->val);
result = audit_comparator(tsk->personality, f->op, f->val);

David Woodhouse
committed
case AUDIT_ARCH:
result = audit_comparator(ctx->arch, f->op, f->val);

David Woodhouse
committed
break;
case AUDIT_EXIT:
if (ctx && ctx->return_valid)
result = audit_comparator(ctx->return_code, f->op, f->val);
if (ctx && ctx->return_valid) {
if (f->val)
result = audit_comparator(ctx->return_valid, f->op, AUDITSC_SUCCESS);
result = audit_comparator(ctx->return_valid, f->op, AUDITSC_FAILURE);
if (name)
result = audit_comparator(MAJOR(name->dev),
f->op, f->val);
else if (ctx) {
if (audit_comparator(MAJOR(ctx->names[j].dev), f->op, f->val)) {
++result;
break;
}
}
}
break;
case AUDIT_DEVMINOR:
if (name)
result = audit_comparator(MINOR(name->dev),
f->op, f->val);
else if (ctx) {
if (audit_comparator(MINOR(ctx->names[j].dev), f->op, f->val)) {
++result;
break;
}
}
}
break;
case AUDIT_INODE:
result = (name->ino == f->val);
if (audit_comparator(ctx->names[j].ino, f->op, f->val)) {
case AUDIT_WATCH:
if (name && rule->watch->ino != (unsigned long)-1)
result = (name->dev == rule->watch->dev &&
name->ino == rule->watch->ino);
result = audit_comparator(ctx->loginuid, f->op, f->val);
case AUDIT_SUBJ_USER:
case AUDIT_SUBJ_ROLE:
case AUDIT_SUBJ_TYPE:
case AUDIT_SUBJ_SEN:
case AUDIT_SUBJ_CLR:
/* NOTE: this may return negative values indicating
a temporary error. We simply treat this as a
match for now to avoid losing information that
may be wanted. An error message will also be
logged upon error */
selinux_get_task_sid(tsk, &sid);
result = selinux_audit_rule_match(sid, f->type,
f->op,
f->se_rule,
ctx);
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
case AUDIT_OBJ_USER:
case AUDIT_OBJ_ROLE:
case AUDIT_OBJ_TYPE:
case AUDIT_OBJ_LEV_LOW:
case AUDIT_OBJ_LEV_HIGH:
/* The above note for AUDIT_SUBJ_USER...AUDIT_SUBJ_CLR
also applies here */
if (f->se_rule) {
/* Find files that match */
if (name) {
result = selinux_audit_rule_match(
name->osid, f->type, f->op,
f->se_rule, ctx);
} else if (ctx) {
for (j = 0; j < ctx->name_count; j++) {
if (selinux_audit_rule_match(
ctx->names[j].osid,
f->type, f->op,
f->se_rule, ctx)) {
++result;
break;
}
}
}
/* Find ipc objects that match */
if (ctx) {
struct audit_aux_data *aux;
for (aux = ctx->aux; aux;
aux = aux->next) {
if (aux->type == AUDIT_IPC) {
struct audit_aux_data_ipcctl *axi = (void *)aux;
if (selinux_audit_rule_match(axi->osid, f->type, f->op, f->se_rule, ctx)) {
++result;
break;
}
}
}
}
}
break;
case AUDIT_ARG0:
case AUDIT_ARG1:
case AUDIT_ARG2:
case AUDIT_ARG3:
if (ctx)
result = audit_comparator(ctx->argv[f->type-AUDIT_ARG0], f->op, f->val);
case AUDIT_FILTERKEY:
/* ignore this field for filtering */
result = 1;
break;
case AUDIT_PERM:
result = audit_match_perm(ctx, f->val);
break;
if (rule->filterkey)
ctx->filterkey = kstrdup(rule->filterkey, GFP_ATOMIC);
switch (rule->action) {
case AUDIT_NEVER: *state = AUDIT_DISABLED; break;
case AUDIT_ALWAYS: *state = AUDIT_RECORD_CONTEXT; break;
}
return 1;
}
/* At process creation time, we can determine if system-call auditing is
* completely disabled for this task. Since we only have the task
* structure at this point, we can only check uid and gid.
*/
static enum audit_state audit_filter_task(struct task_struct *tsk)
{
struct audit_entry *e;
enum audit_state state;
rcu_read_lock();
list_for_each_entry_rcu(e, &audit_filter_list[AUDIT_FILTER_TASK], list) {
if (audit_filter_rules(tsk, &e->rule, NULL, NULL, &state)) {
rcu_read_unlock();
return state;
}
}
rcu_read_unlock();
return AUDIT_BUILD_CONTEXT;
}
/* At syscall entry and exit time, this filter is called if the
* audit_state is not low enough that auditing cannot take place, but is
* also not high enough that we already know we have to write an audit
* record (i.e., the state is AUDIT_SETUP_CONTEXT or AUDIT_BUILD_CONTEXT).
*/
static enum audit_state audit_filter_syscall(struct task_struct *tsk,
struct audit_context *ctx,
struct list_head *list)
{
struct audit_entry *e;
enum audit_state state;
if (audit_pid && tsk->tgid == audit_pid)
return AUDIT_DISABLED;
if (!list_empty(list)) {
int word = AUDIT_WORD(ctx->major);
int bit = AUDIT_BIT(ctx->major);
list_for_each_entry_rcu(e, list, list) {
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
if ((e->rule.mask[word] & bit) == bit &&
audit_filter_rules(tsk, &e->rule, ctx, NULL,
&state)) {
rcu_read_unlock();
return state;
}
}
}
rcu_read_unlock();
return AUDIT_BUILD_CONTEXT;
}
/* At syscall exit time, this filter is called if any audit_names[] have been
* collected during syscall processing. We only check rules in sublists at hash
* buckets applicable to the inode numbers in audit_names[].
* Regarding audit_state, same rules apply as for audit_filter_syscall().
*/
enum audit_state audit_filter_inodes(struct task_struct *tsk,
struct audit_context *ctx)
{
int i;
struct audit_entry *e;
enum audit_state state;
if (audit_pid && tsk->tgid == audit_pid)
return AUDIT_DISABLED;
rcu_read_lock();
for (i = 0; i < ctx->name_count; i++) {
int word = AUDIT_WORD(ctx->major);
int bit = AUDIT_BIT(ctx->major);
struct audit_names *n = &ctx->names[i];
int h = audit_hash_ino((u32)n->ino);
struct list_head *list = &audit_inode_hash[h];
if (list_empty(list))
continue;
list_for_each_entry_rcu(e, list, list) {
if ((e->rule.mask[word] & bit) == bit &&
audit_filter_rules(tsk, &e->rule, ctx, n, &state)) {
rcu_read_unlock();
return state;
}
}
}
rcu_read_unlock();
void audit_set_auditable(struct audit_context *ctx)
{
ctx->auditable = 1;
}
static inline struct audit_context *audit_get_context(struct task_struct *tsk,
int return_valid,
int return_code)
{
struct audit_context *context = tsk->audit_context;
if (likely(!context))
return NULL;
context->return_valid = return_valid;
context->return_code = return_code;
if (context->in_syscall && !context->dummy && !context->auditable) {
state = audit_filter_syscall(tsk, context, &audit_filter_list[AUDIT_FILTER_EXIT]);
if (state == AUDIT_RECORD_CONTEXT) {
context->auditable = 1;
goto get_context;
}
state = audit_filter_inodes(tsk, context);
if (state == AUDIT_RECORD_CONTEXT)
context->auditable = 1;
tsk->audit_context = NULL;
return context;
}
static inline void audit_free_names(struct audit_context *context)
{
int i;
#if AUDIT_DEBUG == 2
if (context->auditable
||context->put_count + context->ino_count != context->name_count) {
printk(KERN_ERR "%s:%d(:%d): major=%d in_syscall=%d"
" name_count=%d put_count=%d"
" ino_count=%d [NOT freeing]\n",
__FILE__, __LINE__,
context->serial, context->major, context->in_syscall,
context->name_count, context->put_count,
context->ino_count);
for (i = 0; i < context->name_count; i++) {
printk(KERN_ERR "names[%d] = %p = %s\n", i,
context->names[i].name,
context->names[i].name ?: "(null)");
dump_stack();
return;
}
#endif
#if AUDIT_DEBUG
context->put_count = 0;
context->ino_count = 0;
#endif
for (i = 0; i < context->name_count; i++) {
if (context->names[i].name && context->names[i].name_put)
if (context->pwd)
dput(context->pwd);
if (context->pwdmnt)
mntput(context->pwdmnt);
context->pwd = NULL;
context->pwdmnt = NULL;
}
static inline void audit_free_aux(struct audit_context *context)
{
struct audit_aux_data *aux;
while ((aux = context->aux)) {
if (aux->type == AUDIT_AVC_PATH) {
struct audit_aux_data_path *axi = (void *)aux;
dput(axi->dentry);
mntput(axi->mnt);
}
context->aux = aux->next;
kfree(aux);
}
}
static inline void audit_zero_context(struct audit_context *context,
enum audit_state state)
{
uid_t loginuid = context->loginuid;
memset(context, 0, sizeof(*context));
context->state = state;
context->loginuid = loginuid;
}
static inline struct audit_context *audit_alloc_context(enum audit_state state)
{
struct audit_context *context;
if (!(context = kmalloc(sizeof(*context), GFP_KERNEL)))
return NULL;
audit_zero_context(context, state);
return context;
}
/**
* audit_alloc - allocate an audit context block for a task
* @tsk: task
*
* Filter on the task information and allocate a per-task audit context
* if necessary. Doing so turns on system call auditing for the
* specified task. This is called from copy_process, so no lock is
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
int audit_alloc(struct task_struct *tsk)
{
struct audit_context *context;
enum audit_state state;
if (likely(!audit_enabled))
return 0; /* Return if not auditing. */
state = audit_filter_task(tsk);
if (likely(state == AUDIT_DISABLED))
return 0;
if (!(context = audit_alloc_context(state))) {
audit_log_lost("out of memory in audit_alloc");
return -ENOMEM;
}
/* Preserve login uid */
context->loginuid = -1;
if (current->audit_context)
context->loginuid = current->audit_context->loginuid;
tsk->audit_context = context;
set_tsk_thread_flag(tsk, TIF_SYSCALL_AUDIT);
return 0;
}
static inline void audit_free_context(struct audit_context *context)
{
struct audit_context *previous;
int count = 0;
do {
previous = context->previous;
if (previous || (count && count < 10)) {
++count;
printk(KERN_ERR "audit(:%d): major=%d name_count=%d:"
" freeing multiple contexts (%d)\n",
context->serial, context->major,
context->name_count, count);
}
audit_free_names(context);
audit_free_aux(context);
kfree(context);
context = previous;
} while (context);
if (count >= 10)
printk(KERN_ERR "audit: freed %d contexts\n", count);
}
static void audit_log_task_context(struct audit_buffer *ab)
{
char *ctx = NULL;
ssize_t len = 0;
len = security_getprocattr(current, "current", NULL, 0);
if (len < 0) {
if (len != -EINVAL)
goto error_path;
return;
}
goto error_path;
len = security_getprocattr(current, "current", ctx, len);
if (len < 0 )
goto error_path;
audit_log_format(ab, " subj=%s", ctx);
error_path:
kfree(ctx);
audit_panic("error in audit_log_task_context");
return;
}
static void audit_log_task_info(struct audit_buffer *ab, struct task_struct *tsk)
char name[sizeof(tsk->comm)];
struct mm_struct *mm = tsk->mm;
struct vm_area_struct *vma;
audit_log_format(ab, " comm=");
audit_log_untrustedstring(ab, name);
if (mm) {
down_read(&mm->mmap_sem);
vma = mm->mmap;
while (vma) {
if ((vma->vm_flags & VM_EXECUTABLE) &&
vma->vm_file) {
audit_log_d_path(ab, "exe=",
vma->vm_file->f_dentry,
vma->vm_file->f_vfsmnt);
break;
}
vma = vma->vm_next;
static void audit_log_exit(struct audit_context *context, struct task_struct *tsk)
struct audit_aux_data *aux;
context->pid = tsk->pid;
if (!context->ppid)
context->ppid = sys_getppid();
context->uid = tsk->uid;
context->gid = tsk->gid;
context->euid = tsk->euid;
context->suid = tsk->suid;
context->fsuid = tsk->fsuid;
context->egid = tsk->egid;
context->sgid = tsk->sgid;
context->fsgid = tsk->fsgid;
context->personality = tsk->personality;
ab = audit_log_start(context, GFP_KERNEL, AUDIT_SYSCALL);
if (!ab)
return; /* audit_panic has been called */
audit_log_format(ab, "arch=%x syscall=%d",
context->arch, context->major);
if (context->personality != PER_LINUX)
audit_log_format(ab, " per=%lx", context->personality);
if (context->return_valid)

David Woodhouse
committed
audit_log_format(ab, " success=%s exit=%ld",
(context->return_valid==AUDITSC_SUCCESS)?"yes":"no",
context->return_code);
if (tsk->signal && tsk->signal->tty && tsk->signal->tty->name)
tty = tsk->signal->tty->name;
audit_log_format(ab,
" a0=%lx a1=%lx a2=%lx a3=%lx items=%d"
" euid=%u suid=%u fsuid=%u"
" egid=%u sgid=%u fsgid=%u tty=%s",
context->argv[0],
context->argv[1],
context->argv[2],
context->argv[3],
context->name_count,
context->pid,
context->loginuid,
context->uid,
context->gid,
context->euid, context->suid, context->fsuid,
context->egid, context->sgid, context->fsgid, tty);
if (context->filterkey) {
audit_log_format(ab, " key=");
audit_log_untrustedstring(ab, context->filterkey);
} else
audit_log_format(ab, " key=(null)");
for (aux = context->aux; aux; aux = aux->next) {
ab = audit_log_start(context, GFP_KERNEL, aux->type);
if (!ab)
continue; /* audit_panic has been called */
switch (aux->type) {
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
case AUDIT_MQ_OPEN: {
struct audit_aux_data_mq_open *axi = (void *)aux;
audit_log_format(ab,
"oflag=0x%x mode=%#o mq_flags=0x%lx mq_maxmsg=%ld "
"mq_msgsize=%ld mq_curmsgs=%ld",
axi->oflag, axi->mode, axi->attr.mq_flags,
axi->attr.mq_maxmsg, axi->attr.mq_msgsize,
axi->attr.mq_curmsgs);
break; }
case AUDIT_MQ_SENDRECV: {
struct audit_aux_data_mq_sendrecv *axi = (void *)aux;
audit_log_format(ab,
"mqdes=%d msg_len=%zd msg_prio=%u "
"abs_timeout_sec=%ld abs_timeout_nsec=%ld",
axi->mqdes, axi->msg_len, axi->msg_prio,
axi->abs_timeout.tv_sec, axi->abs_timeout.tv_nsec);
break; }
case AUDIT_MQ_NOTIFY: {
struct audit_aux_data_mq_notify *axi = (void *)aux;
audit_log_format(ab,
"mqdes=%d sigev_signo=%d",
axi->mqdes,
axi->notification.sigev_signo);
break; }
case AUDIT_MQ_GETSETATTR: {
struct audit_aux_data_mq_getsetattr *axi = (void *)aux;
audit_log_format(ab,
"mqdes=%d mq_flags=0x%lx mq_maxmsg=%ld mq_msgsize=%ld "
"mq_curmsgs=%ld ",
axi->mqdes,
axi->mqstat.mq_flags, axi->mqstat.mq_maxmsg,
axi->mqstat.mq_msgsize, axi->mqstat.mq_curmsgs);
break; }
struct audit_aux_data_ipcctl *axi = (void *)aux;
audit_log_format(ab,
"ouid=%u ogid=%u mode=%x",
axi->uid, axi->gid, axi->mode);
if (axi->osid != 0) {
char *ctx = NULL;
u32 len;
if (selinux_sid_to_string(
axi->osid);
call_panic = 1;
} else
audit_log_format(ab, " obj=%s", ctx);
kfree(ctx);
}
case AUDIT_IPC_SET_PERM: {
struct audit_aux_data_ipcctl *axi = (void *)aux;
audit_log_format(ab,
"qbytes=%lx ouid=%u ogid=%u mode=%x",
axi->qbytes, axi->uid, axi->gid, axi->mode);
break; }
case AUDIT_EXECVE: {
struct audit_aux_data_execve *axi = (void *)aux;
int i;
const char *p;
for (i = 0, p = axi->mem; i < axi->argc; i++) {
audit_log_format(ab, "a%d=", i);
p = audit_log_untrustedstring(ab, p);
audit_log_format(ab, "\n");
}
break; }
case AUDIT_SOCKETCALL: {
int i;
struct audit_aux_data_socketcall *axs = (void *)aux;
audit_log_format(ab, "nargs=%d", axs->nargs);
for (i=0; i<axs->nargs; i++)
audit_log_format(ab, " a%d=%lx", i, axs->args[i]);
break; }
case AUDIT_SOCKADDR: {
struct audit_aux_data_sockaddr *axs = (void *)aux;
audit_log_format(ab, "saddr=");
audit_log_hex(ab, axs->a, axs->len);
break; }
case AUDIT_AVC_PATH: {
struct audit_aux_data_path *axi = (void *)aux;
audit_log_d_path(ab, "path=", axi->dentry, axi->mnt);
break; }
if (context->pwd && context->pwdmnt) {
ab = audit_log_start(context, GFP_KERNEL, AUDIT_CWD);
if (ab) {
audit_log_d_path(ab, "cwd=", context->pwd, context->pwdmnt);
audit_log_end(ab);
}
}
struct audit_names *n = &context->names[i];
ab = audit_log_start(context, GFP_KERNEL, AUDIT_PATH);
if (!ab)
continue; /* audit_panic has been called */
if (n->name) {
switch(n->name_len) {
case AUDIT_NAME_FULL:
/* log the full path */
audit_log_format(ab, " name=");
audit_log_untrustedstring(ab, n->name);
break;
case 0:
/* name was specified as a relative path and the
* directory component is the cwd */
audit_log_d_path(ab, " name=", context->pwd,
context->pwdmnt);
break;
default:
/* log the name's directory component */
audit_log_format(ab, " name=");
audit_log_n_untrustedstring(ab, n->name_len,
n->name);
}
} else
audit_log_format(ab, " name=(null)");