Newer
Older
/* auditsc.c -- System-call auditing support
* Handles all system-call specific auditing features.
*
* Copyright 2003-2004 Red Hat Inc., Durham, North Carolina.
* 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.
*
* The support of additional filter rules compares (>, <, >=, <=) was
* added by Dustin Kirkland <dustin.kirkland@us.ibm.com>, 2005.
*
*/
#include <linux/init.h>
#include <asm/types.h>
#include <linux/mount.h>
#include <linux/socket.h>
#include <linux/audit.h>
#include <linux/personality.h>
#include <linux/time.h>
#include <linux/kthread.h>
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
#include <asm/unistd.h>
/* 0 = no checking
1 = put_count checking
2 = verbose put_count checking
*/
#define AUDIT_DEBUG 0
/* 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
/* At task start time, the audit_state is set in the audit_context using
a per-task filter. At syscall entry, the audit_state is augmented by
the syscall filter. */
enum audit_state {
AUDIT_DISABLED, /* Do not create per-task audit_context.
* No syscall-specific audit records can
* be generated. */
AUDIT_SETUP_CONTEXT, /* Create the per-task audit_context,
* but don't necessarily fill it in at
* syscall entry time (i.e., filter
* instead). */
AUDIT_BUILD_CONTEXT, /* Create the per-task audit_context,
* and always fill it in at syscall
* entry time. This makes a full
* syscall record available if some
* other part of the kernel decides it
* should be recorded. */
AUDIT_RECORD_CONTEXT /* Create the per-task audit_context,
* always fill it in at syscall entry
* time, and always write out the audit
* record at syscall exit time. */
};
/* 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;
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
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_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 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];
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 */
pid_t pid;
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
};
/* Public API */
/* There are three lists of rules -- one to search at task creation
* time, one to search at syscall entry time, and another to search at
* syscall exit time. */
static struct list_head audit_filter_list[AUDIT_NR_FILTERS] = {
LIST_HEAD_INIT(audit_filter_list[0]),
LIST_HEAD_INIT(audit_filter_list[1]),
LIST_HEAD_INIT(audit_filter_list[2]),
LIST_HEAD_INIT(audit_filter_list[3]),
LIST_HEAD_INIT(audit_filter_list[4]),
#if AUDIT_NR_FILTERS != 5
#error Fix audit_filter_list initialiser
#endif
};
struct audit_entry {
struct list_head list;
struct rcu_head rcu;
struct audit_rule rule;
};
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
/* Copy rule from user-space to kernel-space. Called from
* audit_add_rule during AUDIT_ADD. */
static inline int audit_copy_rule(struct audit_rule *d, struct audit_rule *s)
{
int i;
if (s->action != AUDIT_NEVER
&& s->action != AUDIT_POSSIBLE
&& s->action != AUDIT_ALWAYS)
return -1;
if (s->field_count < 0 || s->field_count > AUDIT_MAX_FIELDS)
return -1;
if ((s->flags & ~AUDIT_FILTER_PREPEND) >= AUDIT_NR_FILTERS)
return -1;
d->flags = s->flags;
d->action = s->action;
d->field_count = s->field_count;
for (i = 0; i < d->field_count; i++) {
d->fields[i] = s->fields[i];
d->values[i] = s->values[i];
}
for (i = 0; i < AUDIT_BITMASK_SIZE; i++) d->mask[i] = s->mask[i];
return 0;
}
/* Check to see if two rules are identical. It is called from
* audit_add_rule during AUDIT_ADD and
static inline int audit_compare_rule(struct audit_rule *a, struct audit_rule *b)
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
{
int i;
if (a->flags != b->flags)
return 1;
if (a->action != b->action)
return 1;
if (a->field_count != b->field_count)
return 1;
for (i = 0; i < a->field_count; i++) {
if (a->fields[i] != b->fields[i]
|| a->values[i] != b->values[i])
return 1;
}
for (i = 0; i < AUDIT_BITMASK_SIZE; i++)
if (a->mask[i] != b->mask[i])
return 1;
return 0;
}
/* Note that audit_add_rule and audit_del_rule are called via
* audit_receive() in audit.c, and are protected by
* audit_netlink_sem. */
static inline int audit_add_rule(struct audit_rule *rule,
/* Do not use the _rcu iterator here, since this is the only
* addition routine. */
list_for_each_entry(entry, list, list) {
if (!audit_compare_rule(rule, &entry->rule)) {
return -EEXIST;
}
}
for (i = 0; i < rule->field_count; i++) {
if (rule->fields[i] & AUDIT_UNUSED_BITS)
return -EINVAL;
if ( rule->fields[i] & AUDIT_NEGATE )
rule->fields[i] |= AUDIT_NOT_EQUAL;
else if ( (rule->fields[i] & AUDIT_OPERATORS) == 0 )
rule->fields[i] |= AUDIT_EQUAL;
rule->fields[i] &= (~AUDIT_NEGATE);
}
if (!(entry = kmalloc(sizeof(*entry), GFP_KERNEL)))
return -ENOMEM;
if (audit_copy_rule(&entry->rule, rule)) {
kfree(entry);
return -EINVAL;
}
if (entry->rule.flags & AUDIT_FILTER_PREPEND) {
entry->rule.flags &= ~AUDIT_FILTER_PREPEND;
list_add_rcu(&entry->list, list);
} else {
list_add_tail_rcu(&entry->list, list);
}
static inline void audit_free_rule(struct rcu_head *head)
{
struct audit_entry *e = container_of(head, struct audit_entry, rcu);
kfree(e);
}
/* Note that audit_add_rule and audit_del_rule are called via
* audit_receive() in audit.c, and are protected by
* audit_netlink_sem. */
static inline int audit_del_rule(struct audit_rule *rule,
struct list_head *list)
{
struct audit_entry *e;
/* Do not use the _rcu iterator here, since this is the only
* deletion routine. */
list_for_each_entry(e, list, list) {
if (!audit_compare_rule(rule, &e->rule)) {
list_del_rcu(&e->list);
call_rcu(&e->rcu, audit_free_rule);
return 0;
}
}
return -ENOENT; /* No matching rule */
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
static int audit_list_rules(void *_dest)
{
int pid, seq;
int *dest = _dest;
struct audit_entry *entry;
int i;
pid = dest[0];
seq = dest[1];
kfree(dest);
down(&audit_netlink_sem);
/* The *_rcu iterators not needed here because we are
always called with audit_netlink_sem held. */
for (i=0; i<AUDIT_NR_FILTERS; i++) {
list_for_each_entry(entry, &audit_filter_list[i], list)
audit_send_reply(pid, seq, AUDIT_LIST, 0, 1,
&entry->rule, sizeof(entry->rule));
}
audit_send_reply(pid, seq, AUDIT_LIST, 1, 1, NULL, 0);
up(&audit_netlink_sem);
return 0;
}
/**
* audit_receive_filter - apply all rules to the specified message type
* @type: audit message type
* @pid: target pid for netlink audit messages
* @uid: target uid for netlink audit messages
* @seq: netlink audit message sequence (serial) number
* @data: payload data
* @loginuid: loginuid of sender
*/
int audit_receive_filter(int type, int pid, int uid, int seq, void *data,
uid_t loginuid)
struct task_struct *tsk;
int *dest;
/* We can't just spew out the rules here because we might fill
* the available socket buffer space and deadlock waiting for
* auditctl to read from it... which isn't ever going to
* happen if we're actually running in the context of auditctl
* trying to _send_ the stuff */
dest = kmalloc(2 * sizeof(int), GFP_KERNEL);
if (!dest)
return -ENOMEM;
dest[0] = pid;
dest[1] = seq;
tsk = kthread_run(audit_list_rules, dest, "audit_list_rules");
if (IS_ERR(tsk)) {
kfree(dest);
err = PTR_ERR(tsk);
listnr =((struct audit_rule *)data)->flags & ~AUDIT_FILTER_PREPEND;
if (listnr >= AUDIT_NR_FILTERS)
err = audit_add_rule(data, &audit_filter_list[listnr]);
if (!err)
audit_log(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE,
"auid=%u added an audit rule\n", loginuid);
listnr =((struct audit_rule *)data)->flags & ~AUDIT_FILTER_PREPEND;
if (listnr >= AUDIT_NR_FILTERS)
return -EINVAL;
err = audit_del_rule(data, &audit_filter_list[listnr]);
if (!err)
audit_log(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE,
"auid=%u removed an audit rule\n", loginuid);
break;
default:
return -EINVAL;
}
return err;
}
static int audit_comparator(const u32 left, const u32 op, const u32 right)
{
switch (op) {
case AUDIT_EQUAL:
return (left == right);
case AUDIT_NOT_EQUAL:
return (left != right);
case AUDIT_LESS_THAN:
return (left < right);
case AUDIT_LESS_THAN_OR_EQUAL:
return (left <= right);
case AUDIT_GREATER_THAN:
return (left > right);
case AUDIT_GREATER_THAN_OR_EQUAL:
return (left >= right);
default:
return -EINVAL;
}
}
/* 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_rule *rule,
struct audit_context *ctx,
enum audit_state *state)
{
int i, j;
for (i = 0; i < rule->field_count; i++) {
u32 field = rule->fields[i] & ~AUDIT_OPERATORS;
u32 op = rule->fields[i] & AUDIT_OPERATORS;
u32 value = rule->values[i];
int result = 0;
switch (field) {
case AUDIT_PID:
result = audit_comparator(tsk->pid, op, value);
result = audit_comparator(tsk->uid, op, value);
result = audit_comparator(tsk->euid, op, value);
result = audit_comparator(tsk->suid, op, value);
result = audit_comparator(tsk->fsuid, op, value);
result = audit_comparator(tsk->gid, op, value);
result = audit_comparator(tsk->egid, op, value);
result = audit_comparator(tsk->sgid, op, value);
result = audit_comparator(tsk->fsgid, op, value);
result = audit_comparator(tsk->personality, op, value);

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

David Woodhouse
committed
break;
case AUDIT_EXIT:
if (ctx && ctx->return_valid)
result = audit_comparator(ctx->return_code, op, value);
if (ctx && ctx->return_valid) {
if (value)
result = audit_comparator(ctx->return_valid, op, AUDITSC_SUCCESS);
result = audit_comparator(ctx->return_valid, op, AUDITSC_FAILURE);
break;
case AUDIT_DEVMAJOR:
if (ctx) {
for (j = 0; j < ctx->name_count; j++) {
if (audit_comparator(MAJOR(ctx->names[j].dev), op, value)) {
++result;
break;
}
}
}
break;
case AUDIT_DEVMINOR:
if (ctx) {
for (j = 0; j < ctx->name_count; j++) {
if (audit_comparator(MINOR(ctx->names[j].dev), op, value)) {
++result;
break;
}
}
}
break;
case AUDIT_INODE:
if (ctx) {
for (j = 0; j < ctx->name_count; j++) {
if ( audit_comparator(ctx->names[j].ino, op, value)) {
++result;
break;
}
}
}
break;
case AUDIT_LOGINUID:
result = 0;
if (ctx)
result = audit_comparator(ctx->loginuid, op, value);
break;
case AUDIT_ARG0:
case AUDIT_ARG1:
case AUDIT_ARG2:
case AUDIT_ARG3:
if (ctx)
result = audit_comparator(ctx->argv[field-AUDIT_ARG0], op, value);
break;
}
if (!result)
return 0;
}
switch (rule->action) {
case AUDIT_NEVER: *state = AUDIT_DISABLED; break;
case AUDIT_POSSIBLE: *state = AUDIT_BUILD_CONTEXT; 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, &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) {
if ((e->rule.mask[word] & bit) == bit
&& audit_filter_rules(tsk, &e->rule, ctx, &state)) {
rcu_read_unlock();
return state;
}
}
}
rcu_read_unlock();
return AUDIT_BUILD_CONTEXT;
}
static int audit_filter_user_rules(struct netlink_skb_parms *cb,
struct audit_rule *rule,
enum audit_state *state)
{
int i;
for (i = 0; i < rule->field_count; i++) {
u32 field = rule->fields[i] & ~AUDIT_OPERATORS;
u32 op = rule->fields[i] & AUDIT_OPERATORS;
u32 value = rule->values[i];
int result = 0;
switch (field) {
case AUDIT_PID:
result = audit_comparator(cb->creds.pid, op, value);
result = audit_comparator(cb->creds.uid, op, value);
result = audit_comparator(cb->creds.gid, op, value);
break;
case AUDIT_LOGINUID:
result = audit_comparator(cb->loginuid, op, value);
break;
}
if (!result)
return 0;
}
switch (rule->action) {
case AUDIT_NEVER: *state = AUDIT_DISABLED; break;
case AUDIT_POSSIBLE: *state = AUDIT_BUILD_CONTEXT; break;
case AUDIT_ALWAYS: *state = AUDIT_RECORD_CONTEXT; break;
}
return 1;
}
int audit_filter_user(struct netlink_skb_parms *cb, int type)
{
struct audit_entry *e;
enum audit_state state;
int ret = 1;
rcu_read_lock();
list_for_each_entry_rcu(e, &audit_filter_list[AUDIT_FILTER_USER], list) {
if (audit_filter_user_rules(cb, &e->rule, &state)) {
if (state == AUDIT_DISABLED)
ret = 0;
break;
}
}
rcu_read_unlock();
return ret; /* Audit by default */
/* This should be called with task_lock() held. */
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->auditable) {
state = audit_filter_syscall(tsk, context, &audit_filter_list[AUDIT_FILTER_EXIT]);
674
675
676
677
678
679
680
681
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
if (state == AUDIT_RECORD_CONTEXT)
context->auditable = 1;
}
context->pid = tsk->pid;
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;
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 "audit.c:%d(:%d): major=%d in_syscall=%d"
" name_count=%d put_count=%d"
" ino_count=%d [NOT freeing]\n",
__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);
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)
__putname(context->names[i].name);
context->name_count = 0;
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
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
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_info(struct audit_buffer *ab)
{
char name[sizeof(current->comm)];
struct mm_struct *mm = current->mm;
struct vm_area_struct *vma;
get_task_comm(name, current);
audit_log_format(ab, " comm=");
audit_log_untrustedstring(ab, name);
if (!mm)
return;
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;
}
up_read(&mm->mmap_sem);
}
static void audit_log_exit(struct audit_context *context, gfp_t gfp_mask)
struct audit_aux_data *aux;
ab = audit_log_start(context, gfp_mask, 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);
audit_log_format(ab,
" a0=%lx a1=%lx a2=%lx a3=%lx items=%d"
" pid=%d auid=%u uid=%u gid=%u"
" euid=%u suid=%u fsuid=%u"
" egid=%u sgid=%u fsgid=%u",
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);
for (aux = context->aux; aux; aux = aux->next) {
ab = audit_log_start(context, gfp_mask, aux->type);
if (!ab)
continue; /* audit_panic has been called */
switch (aux->type) {
struct audit_aux_data_ipcctl *axi = (void *)aux;
audit_log_format(ab,
" qbytes=%lx iuid=%u igid=%u mode=%x",
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_mask, AUDIT_CWD);
if (ab) {
audit_log_d_path(ab, "cwd=", context->pwd, context->pwdmnt);
audit_log_end(ab);
}
}
ab = audit_log_start(context, gfp_mask, AUDIT_PATH);
if (!ab)
continue; /* audit_panic has been called */
if (context->names[i].name) {
audit_log_format(ab, " name=");
audit_log_untrustedstring(ab, context->names[i].name);
}
audit_log_format(ab, " flags=%x\n", context->names[i].flags);
if (context->names[i].ino != (unsigned long)-1)
audit_log_format(ab, " inode=%lu dev=%02x:%02x mode=%#o"
" ouid=%u ogid=%u rdev=%02x:%02x",
context->names[i].ino,
MAJOR(context->names[i].dev),
MINOR(context->names[i].dev),
context->names[i].mode,
context->names[i].uid,
context->names[i].gid,
MAJOR(context->names[i].rdev),
MINOR(context->names[i].rdev));
audit_log_end(ab);
}
}
/**
* audit_free - free a per-task audit context
* @tsk: task whose audit context block to free
*
* Called from copy_process and __put_task_struct.
*/
void audit_free(struct task_struct *tsk)
{
struct audit_context *context;
task_lock(tsk);
context = audit_get_context(tsk, 0, 0);
task_unlock(tsk);
if (likely(!context))
return;
/* Check for system calls that do not go through the exit
* function (e.g., exit_group), then free context block.
* We use GFP_ATOMIC here because we might be doing this
* in the context of the idle thread */
if (context->in_syscall && context->auditable)
audit_log_exit(context, GFP_ATOMIC);
/**
* audit_syscall_entry - fill in an audit record at syscall entry
* @tsk: task being audited
* @arch: architecture type
* @major: major syscall type (function)
* @a1: additional syscall register 1
* @a2: additional syscall register 2
* @a3: additional syscall register 3
* @a4: additional syscall register 4
*
* Fill in audit context at syscall entry. This only happens if the
* audit context was created when the task was created and the state or
* filters demand the audit context be built. If the state from the