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.
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#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
/* no execve audit message should be longer than this (userspace limits) */
#define MAX_EXECVE_AUDIT_LEN 7500
/* max length to print of cmdline/proctitle value during audit */
#define MAX_PROCTITLE_AUDIT_LEN 128
/* number of audit rules */
int audit_n_rules;
/* determines whether we collect data for signals sent */
int audit_signals;
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_pids {
struct audit_aux_data d;
pid_t target_pid[AUDIT_AUX_PIDS];
kuid_t target_auid[AUDIT_AUX_PIDS];
kuid_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_tree_refs {
struct audit_tree_refs *next;
struct audit_chunk *c[31];
};
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;
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
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;
}
}
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
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
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_uid(kuid_t uid,
struct audit_names *name,
struct audit_field *f,
struct audit_context *ctx)
{
struct audit_names *n;
int rc;
rc = audit_uid_comparator(uid, f->op, name->uid);
if (ctx) {
list_for_each_entry(n, &ctx->names_list, list) {
rc = audit_uid_comparator(uid, f->op, n->uid);
if (rc)
return rc;
}
}
return 0;
}
static int audit_compare_gid(kgid_t gid,
struct audit_names *name,
struct audit_field *f,
struct audit_context *ctx)
{
struct audit_names *n;
int rc;
if (name) {
rc = audit_gid_comparator(gid, f->op, name->gid);
if (rc)
return rc;
}
if (ctx) {
list_for_each_entry(n, &ctx->names_list, list) {
rc = audit_gid_comparator(gid, f->op, n->gid);
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) {
/* process to file object comparisons */
case AUDIT_COMPARE_UID_TO_OBJ_UID:
return audit_compare_uid(cred->uid, name, f, ctx);
case AUDIT_COMPARE_GID_TO_OBJ_GID:
return audit_compare_gid(cred->gid, name, f, ctx);
case AUDIT_COMPARE_EUID_TO_OBJ_UID:
return audit_compare_uid(cred->euid, name, f, ctx);
case AUDIT_COMPARE_EGID_TO_OBJ_GID:
return audit_compare_gid(cred->egid, name, f, ctx);
case AUDIT_COMPARE_AUID_TO_OBJ_UID:
return audit_compare_uid(tsk->loginuid, name, f, ctx);
case AUDIT_COMPARE_SUID_TO_OBJ_UID:
return audit_compare_uid(cred->suid, name, f, ctx);
case AUDIT_COMPARE_SGID_TO_OBJ_GID:
return audit_compare_gid(cred->sgid, name, f, ctx);
case AUDIT_COMPARE_FSUID_TO_OBJ_UID:
return audit_compare_uid(cred->fsuid, name, f, ctx);
case AUDIT_COMPARE_FSGID_TO_OBJ_GID:
return audit_compare_gid(cred->fsgid, name, f, ctx);
/* uid comparisons */
case AUDIT_COMPARE_UID_TO_AUID:
return audit_uid_comparator(cred->uid, f->op, tsk->loginuid);
return audit_uid_comparator(cred->uid, f->op, cred->euid);
return audit_uid_comparator(cred->uid, f->op, cred->suid);
return audit_uid_comparator(cred->uid, f->op, cred->fsuid);
/* auid comparisons */
case AUDIT_COMPARE_AUID_TO_EUID:
return audit_uid_comparator(tsk->loginuid, f->op, cred->euid);
return audit_uid_comparator(tsk->loginuid, f->op, cred->suid);
case AUDIT_COMPARE_AUID_TO_FSUID:
return audit_uid_comparator(tsk->loginuid, f->op, cred->fsuid);
/* euid comparisons */
case AUDIT_COMPARE_EUID_TO_SUID:
return audit_uid_comparator(cred->euid, f->op, cred->suid);
case AUDIT_COMPARE_EUID_TO_FSUID:
return audit_uid_comparator(cred->euid, f->op, cred->fsuid);
/* suid comparisons */
case AUDIT_COMPARE_SUID_TO_FSUID:
return audit_uid_comparator(cred->suid, f->op, cred->fsuid);
/* gid comparisons */
case AUDIT_COMPARE_GID_TO_EGID:
return audit_gid_comparator(cred->gid, f->op, cred->egid);
return audit_gid_comparator(cred->gid, f->op, cred->sgid);
return audit_gid_comparator(cred->gid, f->op, cred->fsgid);
/* egid comparisons */
case AUDIT_COMPARE_EGID_TO_SGID:
return audit_gid_comparator(cred->egid, f->op, cred->sgid);
case AUDIT_COMPARE_EGID_TO_FSGID:
return audit_gid_comparator(cred->egid, f->op, cred->fsgid);
/* sgid comparison */
case AUDIT_COMPARE_SGID_TO_FSGID:
return audit_gid_comparator(cred->sgid, f->op, cred->fsgid);
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;
pid_t pid;
pid = task_pid_nr(tsk);
result = audit_comparator(pid, f->op, f->val);
ctx->ppid = task_ppid_nr(tsk);
result = audit_comparator(ctx->ppid, f->op, f->val);
result = audit_uid_comparator(cred->uid, f->op, f->uid);
result = audit_uid_comparator(cred->euid, f->op, f->uid);
result = audit_uid_comparator(cred->suid, f->op, f->uid);
result = audit_uid_comparator(cred->fsuid, f->op, f->uid);
result = audit_gid_comparator(cred->gid, f->op, f->gid);
if (f->op == Audit_equal) {
if (!result)
result = in_group_p(f->gid);
} else if (f->op == Audit_not_equal) {
if (result)
result = !in_group_p(f->gid);
}
result = audit_gid_comparator(cred->egid, f->op, f->gid);
if (f->op == Audit_equal) {
if (!result)
result = in_egroup_p(f->gid);
} else if (f->op == Audit_not_equal) {
if (result)
result = !in_egroup_p(f->gid);
}
result = audit_gid_comparator(cred->sgid, f->op, f->gid);
result = audit_gid_comparator(cred->fsgid, f->op, f->gid);
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 = audit_comparator(name->ino, f->op, f->val);

Eric Paris
committed
list_for_each_entry(n, &ctx->names_list, list) {
if (audit_comparator(n->ino, f->op, f->val)) {
result = audit_uid_comparator(name->uid, f->op, f->uid);
} else if (ctx) {
list_for_each_entry(n, &ctx->names_list, list) {
if (audit_uid_comparator(n->uid, f->op, f->uid)) {
++result;
break;
}
}
}
break;
case AUDIT_OBJ_GID:
if (name) {
result = audit_gid_comparator(name->gid, f->op, f->gid);
} else if (ctx) {
list_for_each_entry(n, &ctx->names_list, list) {
if (audit_gid_comparator(n->gid, f->op, f->gid)) {
++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_uid_comparator(tsk->loginuid, f->op, f->uid);
case AUDIT_LOGINUID_SET:
result = audit_comparator(audit_loginuid_set(tsk), f->op, f->val);
break;
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
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
/*
* 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();
}

Richard Guy Briggs
committed
/* Transfer the audit context pointer to the caller, clearing it in the tsk's struct */
static inline struct audit_context *audit_take_context(struct task_struct *tsk,
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_proctitle_free(struct audit_context *context)
{
kfree(context->proctitle.value);
context->proctitle.value = NULL;
context->proctitle.len = 0;
}
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) {
pr_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) {
pr_err("names[%d] = %p = %s\n", i++, n->name,
n->name->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)

Eric Paris
committed
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 struct audit_context *audit_alloc_context(enum audit_state state)
{
struct audit_context *context;
context = kzalloc(sizeof(*context), GFP_KERNEL);
if (!context)
context->state = state;
context->prio = state == AUDIT_RECORD_CONTEXT ? ~0ULL : 0;

Eric Paris
committed
INIT_LIST_HEAD(&context->names_list);
/**
* 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
int audit_alloc(struct task_struct *tsk)
{
struct audit_context *context;
enum audit_state state;
if (likely(!audit_ever_enabled))
state = audit_filter_task(tsk, &key);
if (state == AUDIT_DISABLED) {
clear_tsk_thread_flag(tsk, TIF_SYSCALL_AUDIT);
audit_log_lost("out of memory in audit_alloc");
return -ENOMEM;
}
context->filterkey = key;
tsk->audit_context = context;
set_tsk_thread_flag(tsk, TIF_SYSCALL_AUDIT);
return 0;
}
static inline void audit_free_context(struct audit_context *context)
{
audit_free_names(context);
unroll_tree_refs(context, NULL, 0);
free_tree_refs(context);
audit_free_aux(context);
kfree(context->filterkey);
kfree(context->sockaddr);
audit_proctitle_free(context);
static int audit_log_pid_context(struct audit_context *context, pid_t pid,
kuid_t auid, kuid_t uid, unsigned int sessionid,
char *ctx = NULL;
u32 len;
int rc = 0;
ab = audit_log_start(context, GFP_KERNEL, AUDIT_OBJ_PID);
if (!ab)
return rc;
audit_log_format(ab, "opid=%d oauid=%d ouid=%d oses=%d", pid,
from_kuid(&init_user_ns, auid),
from_kuid(&init_user_ns, uid), sessionid);
if (sid) {
if (security_secid_to_secctx(sid, &ctx, &len)) {
audit_log_format(ab, " obj=(none)");
rc = 1;
} else {
audit_log_format(ab, " obj=%s", ctx);
security_release_secctx(ctx, len);
}