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 <linux/fs.h>
#include <linux/namei.h>
#include <linux/export.h>
#include <linux/slab.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/capability.h>
#include "audit.h"
/* flags stating the success for a syscall */
#define AUDITSC_INVALID 0
#define AUDITSC_SUCCESS 1
#define AUDITSC_FAILURE 2
/* AUDIT_NAMES is the number of slots we reserve in the audit_context

Eric Paris
committed
* for saving names from getname(). If we get more names we will allocate
* a name dynamically and also add those to the list anchored by names_list. */
#define AUDIT_NAMES 5
/* Indicates that audit should log the full pathname. */
#define AUDIT_NAME_FULL -1
/* no execve audit message should be longer than this (userspace limits) */
#define MAX_EXECVE_AUDIT_LEN 7500
/* number of audit rules */
int audit_n_rules;
/* determines whether we collect data for signals sent */
int audit_signals;
struct audit_cap_data {
kernel_cap_t permitted;
kernel_cap_t inheritable;
union {
unsigned int fE; /* effective bit of a file capability */
kernel_cap_t effective; /* effective set of a process */
};
};
/* 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 {

Eric Paris
committed
struct list_head list; /* audit_context->names_list */
const char *name;
unsigned long ino;
dev_t dev;
umode_t mode;
uid_t uid;
gid_t gid;
dev_t rdev;
struct audit_cap_data fcap;
unsigned int fcap_ver;

Eric Paris
committed
int name_len; /* number of name's characters to log */
bool name_put; /* call __putname() for this name */
/*
* This was an allocated audit_names and not from the array of
* names allocated in the task audit context. Thus this name
* should be freed on syscall exit
*/
bool should_free;
};
struct audit_aux_data {
struct audit_aux_data *next;
int type;
};
#define AUDIT_AUX_IPCPERM 0
/* Number of target pids per aux struct. */
#define AUDIT_AUX_PIDS 16
struct audit_aux_data_execve {
struct audit_aux_data d;
int argc;
int envc;
struct audit_aux_data_pids {
struct audit_aux_data d;
pid_t target_pid[AUDIT_AUX_PIDS];
uid_t target_auid[AUDIT_AUX_PIDS];
uid_t target_uid[AUDIT_AUX_PIDS];
unsigned int target_sessionid[AUDIT_AUX_PIDS];
char target_comm[AUDIT_AUX_PIDS][TASK_COMM_LEN];
struct audit_aux_data_bprm_fcaps {
struct audit_aux_data d;
struct audit_cap_data fcap;
unsigned int fcap_ver;
struct audit_cap_data old_pcap;
struct audit_cap_data new_pcap;
};
struct audit_aux_data_capset {
struct audit_aux_data d;
pid_t pid;
struct audit_cap_data cap;
};
struct audit_tree_refs {
struct audit_tree_refs *next;
struct audit_chunk *c[31];
};
/* The per-task audit context. */
struct audit_context {
int dummy; /* must be the first element */
enum audit_state state, current_state;
unsigned int serial; /* serial number for record */
int major; /* syscall number */
struct timespec ctime; /* time of syscall entry */

David Woodhouse
committed
long return_code;/* syscall return code */
int return_valid; /* return code is valid */

Eric Paris
committed
/*
* The names_list is the list of all audit_names collected during this
* syscall. The first AUDIT_NAMES entries in the names_list will
* actually be from the preallocated_names array for performance
* reasons. Except during allocation they should never be referenced
* through the preallocated_names array and should only be found/used
* by running the names_list.
*/
struct audit_names preallocated_names[AUDIT_NAMES];
int name_count; /* total records in names_list */
struct list_head names_list; /* anchor for struct audit_names->list */
char * filterkey; /* key for rule that triggered record */
struct audit_context *previous; /* For nested syscalls */
struct audit_aux_data *aux;
struct sockaddr_storage *sockaddr;
size_t sockaddr_len;
uid_t uid, euid, suid, fsuid;
gid_t gid, egid, sgid, fsgid;
unsigned long personality;

David Woodhouse
committed
int arch;
uid_t target_auid;
uid_t target_uid;
char target_comm[TASK_COMM_LEN];
int type;
union {
struct {
int nargs;
long args[6];
} socketcall;
int has_perm;
uid_t perm_uid;
gid_t perm_gid;
struct {
mqd_t mqdes;
struct mq_attr mqstat;
} mq_getsetattr;
struct {
mqd_t mqdes;
int sigev_signo;
} mq_notify;
struct {
mqd_t mqdes;
size_t msg_len;
unsigned int msg_prio;
struct timespec abs_timeout;
} mq_sendrecv;
struct {
pid_t pid;
struct audit_cap_data cap;
} capset;
#if AUDIT_DEBUG
int put_count;
int ino_count;
#endif
};
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;
if (unlikely(!ctx))
return 0;
n = ctx->major;
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
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;
}
}
static int audit_match_filetype(struct audit_context *ctx, int val)

Eric Paris
committed
struct audit_names *n;
umode_t mode = (umode_t)val;
if (unlikely(!ctx))
return 0;

Eric Paris
committed
list_for_each_entry(n, &ctx->names_list, list) {
if ((n->ino != -1) &&
((n->mode & S_IFMT) == mode))
return 1;
}

Eric Paris
committed
/*
* We keep a linked list of fixed-sized (31 pointer) arrays of audit_chunk *;
* ->first_trees points to its beginning, ->trees - to the current end of data.
* ->tree_count is the number of free entries in array pointed to by ->trees.
* Original condition is (NULL, NULL, 0); as soon as it grows we never revert to NULL,
* "empty" becomes (p, p, 31) afterwards. We don't shrink the list (and seriously,
* it's going to remain 1-element for almost any setup) until we free context itself.
* References in it _are_ dropped - at the same time we free/drop aux stuff.
*/
#ifdef CONFIG_AUDIT_TREE
static void audit_set_auditable(struct audit_context *ctx)
{
if (!ctx->prio) {
ctx->prio = 1;
ctx->current_state = AUDIT_RECORD_CONTEXT;
}
}
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
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
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
static int put_tree_ref(struct audit_context *ctx, struct audit_chunk *chunk)
{
struct audit_tree_refs *p = ctx->trees;
int left = ctx->tree_count;
if (likely(left)) {
p->c[--left] = chunk;
ctx->tree_count = left;
return 1;
}
if (!p)
return 0;
p = p->next;
if (p) {
p->c[30] = chunk;
ctx->trees = p;
ctx->tree_count = 30;
return 1;
}
return 0;
}
static int grow_tree_refs(struct audit_context *ctx)
{
struct audit_tree_refs *p = ctx->trees;
ctx->trees = kzalloc(sizeof(struct audit_tree_refs), GFP_KERNEL);
if (!ctx->trees) {
ctx->trees = p;
return 0;
}
if (p)
p->next = ctx->trees;
else
ctx->first_trees = ctx->trees;
ctx->tree_count = 31;
return 1;
}
#endif
static void unroll_tree_refs(struct audit_context *ctx,
struct audit_tree_refs *p, int count)
{
#ifdef CONFIG_AUDIT_TREE
struct audit_tree_refs *q;
int n;
if (!p) {
/* we started with empty chain */
p = ctx->first_trees;
count = 31;
/* if the very first allocation has failed, nothing to do */
if (!p)
return;
}
n = count;
for (q = p; q != ctx->trees; q = q->next, n = 31) {
while (n--) {
audit_put_chunk(q->c[n]);
q->c[n] = NULL;
}
}
while (n-- > ctx->tree_count) {
audit_put_chunk(q->c[n]);
q->c[n] = NULL;
}
ctx->trees = p;
ctx->tree_count = count;
#endif
}
static void free_tree_refs(struct audit_context *ctx)
{
struct audit_tree_refs *p, *q;
for (p = ctx->first_trees; p; p = q) {
q = p->next;
kfree(p);
}
}
static int match_tree_refs(struct audit_context *ctx, struct audit_tree *tree)
{
#ifdef CONFIG_AUDIT_TREE
struct audit_tree_refs *p;
int n;
if (!tree)
return 0;
/* full ones */
for (p = ctx->first_trees; p != ctx->trees; p = p->next) {
for (n = 0; n < 31; n++)
if (audit_tree_match(p->c[n], tree))
return 1;
}
/* partial */
if (p) {
for (n = ctx->tree_count; n < 31; n++)
if (audit_tree_match(p->c[n], tree))
return 1;
}
#endif
return 0;
}
static int audit_compare_id(uid_t uid1,
struct audit_names *name,
unsigned long name_offset,
struct audit_field *f,
struct audit_context *ctx)
{
struct audit_names *n;
unsigned long addr;
uid_t uid2;
int rc;
BUILD_BUG_ON(sizeof(uid_t) != sizeof(gid_t));
if (name) {
addr = (unsigned long)name;
addr += name_offset;
uid2 = *(uid_t *)addr;
rc = audit_comparator(uid1, f->op, uid2);
if (rc)
return rc;
}
if (ctx) {
list_for_each_entry(n, &ctx->names_list, list) {
addr = (unsigned long)n;
addr += name_offset;
uid2 = *(uid_t *)addr;
rc = audit_comparator(uid1, f->op, uid2);
if (rc)
return rc;
}
}
return 0;
}
static int audit_field_compare(struct task_struct *tsk,
const struct cred *cred,
struct audit_field *f,
struct audit_context *ctx,
struct audit_names *name)
{
switch (f->val) {
case AUDIT_COMPARE_UID_TO_OBJ_UID:
return audit_compare_id(cred->uid,
name, offsetof(struct audit_names, uid),
f, ctx);
case AUDIT_COMPARE_GID_TO_OBJ_GID:
return audit_compare_id(cred->gid,
name, offsetof(struct audit_names, gid),
f, ctx);
default:
WARN(1, "Missing AUDIT_COMPARE define. Report as a bug\n");
return 0;
}
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.
*
* If task_creation is true, this is an explicit indication that we are
* filtering a task rule at task creation time. This and tsk == current are
* the only situations where tsk->cred may be accessed without an rcu read lock.
*/
static int audit_filter_rules(struct task_struct *tsk,
struct audit_krule *rule,
enum audit_state *state,
bool task_creation)
const struct cred *cred;

Eric Paris
committed
int i, need_sid = 1;
cred = rcu_dereference_check(tsk->cred, tsk == current || task_creation);
struct audit_field *f = &rule->fields[i];

Eric Paris
committed
struct audit_names *n;
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(cred->uid, f->op, f->val);
result = audit_comparator(cred->euid, f->op, f->val);
result = audit_comparator(cred->suid, f->op, f->val);
result = audit_comparator(cred->fsuid, f->op, f->val);
result = audit_comparator(cred->gid, f->op, f->val);
result = audit_comparator(cred->egid, f->op, f->val);
result = audit_comparator(cred->sgid, f->op, f->val);
result = audit_comparator(cred->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);

Eric Paris
committed
if (name) {
if (audit_comparator(MAJOR(name->dev), f->op, f->val) ||
audit_comparator(MAJOR(name->rdev), f->op, f->val))
++result;
} else if (ctx) {

Eric Paris
committed
list_for_each_entry(n, &ctx->names_list, list) {

Eric Paris
committed
if (audit_comparator(MAJOR(n->dev), f->op, f->val) ||
audit_comparator(MAJOR(n->rdev), f->op, f->val)) {
++result;
break;
}
}
}
break;
case AUDIT_DEVMINOR:

Eric Paris
committed
if (name) {
if (audit_comparator(MINOR(name->dev), f->op, f->val) ||
audit_comparator(MINOR(name->rdev), f->op, f->val))
++result;
} else if (ctx) {

Eric Paris
committed
list_for_each_entry(n, &ctx->names_list, list) {

Eric Paris
committed
if (audit_comparator(MINOR(n->dev), f->op, f->val) ||
audit_comparator(MINOR(n->rdev), f->op, f->val)) {
++result;
break;
}
}
}
break;
case AUDIT_INODE:
result = (name->ino == f->val);

Eric Paris
committed
list_for_each_entry(n, &ctx->names_list, list) {
if (audit_comparator(n->ino, f->op, f->val)) {
case AUDIT_OBJ_UID:
if (name) {
result = audit_comparator(name->uid, f->op, f->val);
} else if (ctx) {
list_for_each_entry(n, &ctx->names_list, list) {
if (audit_comparator(n->uid, f->op, f->val)) {
++result;
break;
}
}
}
break;
case AUDIT_OBJ_GID:
if (name) {
result = audit_comparator(name->gid, f->op, f->val);
} else if (ctx) {
list_for_each_entry(n, &ctx->names_list, list) {
if (audit_comparator(n->gid, f->op, f->val)) {
++result;
break;
}
}
}
break;
if (name)
result = audit_watch_compare(rule->watch, name->ino, name->dev);
case AUDIT_DIR:
if (ctx)
result = match_tree_refs(ctx, rule->tree);
break;
result = audit_comparator(tsk->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 */
security_task_getsecid(tsk, &sid);
result = security_audit_rule_match(sid, f->type,
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 */
/* Find files that match */
if (name) {
result = security_audit_rule_match(
name->osid, f->type, f->op,

Eric Paris
committed
list_for_each_entry(n, &ctx->names_list, list) {
if (security_audit_rule_match(n->osid, f->type,
f->op, f->lsm_rule,
ctx)) {
++result;
break;
}
}
}
/* Find ipc objects that match */
if (!ctx || ctx->type != AUDIT_IPC)
break;
if (security_audit_rule_match(ctx->ipc.osid,
f->type, f->op,
f->lsm_rule, ctx))
++result;
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;
case AUDIT_FILETYPE:
result = audit_match_filetype(ctx, f->val);
break;
case AUDIT_FIELD_COMPARE:
result = audit_field_compare(tsk, cred, f, ctx, name);
break;
if (ctx) {
if (rule->prio <= ctx->prio)
return 0;
if (rule->filterkey) {
kfree(ctx->filterkey);
ctx->filterkey = kstrdup(rule->filterkey, GFP_ATOMIC);
}
ctx->prio = rule->prio;
}
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, char **key)
{
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, true)) {
if (state == AUDIT_RECORD_CONTEXT)
*key = kstrdup(e->rule.filterkey, GFP_ATOMIC);
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, NULL,
&state, false)) {
return state;
}
}
}
rcu_read_unlock();
return AUDIT_BUILD_CONTEXT;
}

Eric Paris
committed
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
/*
* Given an audit_name check the inode hash table to see if they match.
* Called holding the rcu read lock to protect the use of audit_inode_hash
*/
static int audit_filter_inode_name(struct task_struct *tsk,
struct audit_names *n,
struct audit_context *ctx) {
int word, bit;
int h = audit_hash_ino((u32)n->ino);
struct list_head *list = &audit_inode_hash[h];
struct audit_entry *e;
enum audit_state state;
word = AUDIT_WORD(ctx->major);
bit = AUDIT_BIT(ctx->major);
if (list_empty(list))
return 0;
list_for_each_entry_rcu(e, list, list) {
if ((e->rule.mask[word] & bit) == bit &&
audit_filter_rules(tsk, &e->rule, ctx, n, &state, false)) {
ctx->current_state = state;
return 1;
}
}
return 0;
}
/* 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

Eric Paris
committed
* buckets applicable to the inode numbers in audit_names.
* Regarding audit_state, same rules apply as for audit_filter_syscall().
*/
void audit_filter_inodes(struct task_struct *tsk, struct audit_context *ctx)

Eric Paris
committed
struct audit_names *n;
if (audit_pid && tsk->tgid == audit_pid)

Eric Paris
committed
list_for_each_entry(n, &ctx->names_list, list) {
if (audit_filter_inode_name(tsk, n, ctx))
break;
}
rcu_read_unlock();
}
static inline struct audit_context *audit_get_context(struct task_struct *tsk,
int return_valid,
long return_code)
{
struct audit_context *context = tsk->audit_context;
return NULL;
context->return_valid = return_valid;
/*
* we need to fix up the return code in the audit logs if the actual
* return codes are later going to be fixed up by the arch specific
* signal handlers
*
* This is actually a test for:
* (rc == ERESTARTSYS ) || (rc == ERESTARTNOINTR) ||
* (rc == ERESTARTNOHAND) || (rc == ERESTART_RESTARTBLOCK)
*
* but is faster than a bunch of ||
*/
if (unlikely(return_code <= -ERESTARTSYS) &&
(return_code >= -ERESTART_RESTARTBLOCK) &&
(return_code != -ENOIOCTLCMD))
context->return_code = -EINTR;
else
context->return_code = return_code;
if (context->in_syscall && !context->dummy) {
audit_filter_syscall(tsk, context, &audit_filter_list[AUDIT_FILTER_EXIT]);
audit_filter_inodes(tsk, context);
}
tsk->audit_context = NULL;
return context;
}
static inline void audit_free_names(struct audit_context *context)
{

Eric Paris
committed
struct audit_names *n, *next;
if (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);

Eric Paris
committed
list_for_each_entry(n, &context->names_list, list) {

Eric Paris
committed
n->name, n->name ?: "(null)");
dump_stack();
return;
}
#endif
#if AUDIT_DEBUG
context->put_count = 0;
context->ino_count = 0;
#endif

Eric Paris
committed
list_for_each_entry_safe(n, next, &context->names_list, list) {
list_del(&n->list);
if (n->name && n->name_put)
__putname(n->name);
if (n->should_free)
kfree(n);
path_put(&context->pwd);
context->pwd.dentry = NULL;
context->pwd.mnt = NULL;
}
static inline void audit_free_aux(struct audit_context *context)
{
struct audit_aux_data *aux;
while ((aux = context->aux)) {
context->aux = aux->next;
kfree(aux);
}
while ((aux = context->aux_pids)) {
context->aux_pids = aux->next;
kfree(aux);
}
}
static inline void audit_zero_context(struct audit_context *context,
enum audit_state state)
{
memset(context, 0, sizeof(*context));
context->state = state;
context->prio = state == AUDIT_RECORD_CONTEXT ? ~0ULL : 0;
}
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);

Eric Paris
committed
INIT_LIST_HEAD(&context->names_list);
/**
* audit_alloc - allocate an audit context block for a task
* @tsk: task