audit/stable-4.21 PR 20181224
-----BEGIN PGP SIGNATURE----- iQJIBAABCAAyFiEES0KozwfymdVUl37v6iDy2pc3iXMFAlwhAwIUHHBhdWxAcGF1 bC1tb29yZS5jb20ACgkQ6iDy2pc3iXNl1w/+PKsewN5VkmmfibIxZ+iZwe1KGB+L iOwkdHDkG1Bae5A7TBdbKMbHq0FdhaiDXAIFrfunBG/tbgBF9O0056edekR4rRLp ReGQVNpGMggiATyVKrc3vi+4+UYQqtS6N7Y8q+mMMX/hVeeESXrTAZdgxSWwsZAX LbYwXXYUyupLvelpkpakE6VPZEcatcYWrVK/vFKLkTt2jLLlLPtanbMf0B71TULi 5EZSVBYWS71a6yvrrYcVDDZjgot31nVQfX4EIqE6CVcXLuL9vqbZBGKZh+iAGbjs UdKgaQMZ/eJ4CRYDJca0Bnba3n1AKO4uNssY0nrMW4s/inDPrJnMZ0kgGWfayE3d QR96aHEP5W3SZoiJCUlYm8a4JFfndYKn4YBvqjvLgIkbd784/rvI+sNGM9BF1DNP f05frIJVHLNO3sECKWMmQyMGWGglj7bLsjtKrai5UQReyFLpM/q/Lh3J1IHZ9KZq YWFTA4G0rg7x2bdEB4Qh/SaLOOHW7uyQ7IJCYfzSKsZCIO++RqCQoArxiKRE6++C hv0UG6NGb6Z6a+k1JSzlxCXPmcui0zow7aqEpZSl/9kiYzkLpBITha/ERP7at5M2 W3JVNfQNn6kPtZFgmNuP7rNE9Yn6jnbIdks0nsi/J/4KUr/p2Mfc5LamyTj1unk6 xf7S+xmOFKHAc2s= =PCHx -----END PGP SIGNATURE----- Merge tag 'audit-pr-20181224' of git://git.kernel.org/pub/scm/linux/kernel/git/pcmoore/audit Pull audit updates from Paul Moore: "In the finest of holiday of traditions, I have a number of gifts to share today. While most of them are re-gifts from others, unlike the typical re-gift, these are things you will want in and around your tree; I promise. This pull request is perhaps a bit larger than our typical PR, but most of it comes from Jan's rework of audit's fanotify code; a very welcome improvement. We ran this through our normal regression tests, as well as some newly created stress tests and everything looks good. Richard added a few patches, mostly cleaning up a few things and and shortening some of the audit records that we send to userspace; a change the userspace folks are quite happy about. Finally YueHaibing and I kick in a few patches to simplify things a bit and make the code less prone to errors. Lastly, I want to say thanks one more time to everyone who has contributed patches, testing, and code reviews for the audit subsystem over the past year. The project is what it is due to your help and contributions - thank you" * tag 'audit-pr-20181224' of git://git.kernel.org/pub/scm/linux/kernel/git/pcmoore/audit: (22 commits) audit: remove duplicated include from audit.c audit: shorten PATH cap values when zero audit: use current whenever possible audit: minimize our use of audit_log_format() audit: remove WATCH and TREE config options audit: use session_info helper audit: localize audit_log_session_info prototype audit: Use 'mark' name for fsnotify_mark variables audit: Replace chunk attached to mark instead of replacing mark audit: Simplify locking around untag_chunk() audit: Drop all unused chunk nodes during deletion audit: Guarantee forward progress of chunk untagging audit: Allocate fsnotify mark independently of chunk audit: Provide helper for dropping mark's chunk reference audit: Remove pointless check in insert_hash() audit: Factor out chunk replacement code audit: Make hash table insertion safe against concurrent lookups audit: Embed key into chunk audit: Fix possible tagging failures audit: Fix possible spurious -ENOSPC error ...
This commit is contained in:
commit
047ce6d380
@ -61,20 +61,19 @@ static void tty_audit_log(const char *description, dev_t dev,
|
||||
unsigned char *data, size_t size)
|
||||
{
|
||||
struct audit_buffer *ab;
|
||||
struct task_struct *tsk = current;
|
||||
pid_t pid = task_pid_nr(tsk);
|
||||
uid_t uid = from_kuid(&init_user_ns, task_uid(tsk));
|
||||
uid_t loginuid = from_kuid(&init_user_ns, audit_get_loginuid(tsk));
|
||||
unsigned int sessionid = audit_get_sessionid(tsk);
|
||||
pid_t pid = task_pid_nr(current);
|
||||
uid_t uid = from_kuid(&init_user_ns, task_uid(current));
|
||||
uid_t loginuid = from_kuid(&init_user_ns, audit_get_loginuid(current));
|
||||
unsigned int sessionid = audit_get_sessionid(current);
|
||||
|
||||
ab = audit_log_start(NULL, GFP_KERNEL, AUDIT_TTY);
|
||||
if (ab) {
|
||||
char name[sizeof(tsk->comm)];
|
||||
char name[sizeof(current->comm)];
|
||||
|
||||
audit_log_format(ab, "%s pid=%u uid=%u auid=%u ses=%u major=%d"
|
||||
" minor=%d comm=", description, pid, uid,
|
||||
loginuid, sessionid, MAJOR(dev), MINOR(dev));
|
||||
get_task_comm(name, tsk);
|
||||
get_task_comm(name, current);
|
||||
audit_log_untrustedstring(ab, name);
|
||||
audit_log_format(ab, " data=");
|
||||
audit_log_n_hex(ab, data, size);
|
||||
|
@ -115,8 +115,6 @@ extern int audit_classify_compat_syscall(int abi, unsigned syscall);
|
||||
|
||||
struct filename;
|
||||
|
||||
extern void audit_log_session_info(struct audit_buffer *ab);
|
||||
|
||||
#define AUDIT_OFF 0
|
||||
#define AUDIT_ON 1
|
||||
#define AUDIT_LOCKED 2
|
||||
@ -153,8 +151,7 @@ extern void audit_log_link_denied(const char *operation);
|
||||
extern void audit_log_lost(const char *message);
|
||||
|
||||
extern int audit_log_task_context(struct audit_buffer *ab);
|
||||
extern void audit_log_task_info(struct audit_buffer *ab,
|
||||
struct task_struct *tsk);
|
||||
extern void audit_log_task_info(struct audit_buffer *ab);
|
||||
|
||||
extern int audit_update_lsm_rules(void);
|
||||
|
||||
@ -202,8 +199,7 @@ static inline int audit_log_task_context(struct audit_buffer *ab)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
static inline void audit_log_task_info(struct audit_buffer *ab,
|
||||
struct task_struct *tsk)
|
||||
static inline void audit_log_task_info(struct audit_buffer *ab)
|
||||
{ }
|
||||
#define audit_enabled AUDIT_OFF
|
||||
#endif /* CONFIG_AUDIT */
|
||||
|
@ -335,15 +335,6 @@ config HAVE_ARCH_AUDITSYSCALL
|
||||
config AUDITSYSCALL
|
||||
def_bool y
|
||||
depends on AUDIT && HAVE_ARCH_AUDITSYSCALL
|
||||
|
||||
config AUDIT_WATCH
|
||||
def_bool y
|
||||
depends on AUDITSYSCALL
|
||||
select FSNOTIFY
|
||||
|
||||
config AUDIT_TREE
|
||||
def_bool y
|
||||
depends on AUDITSYSCALL
|
||||
select FSNOTIFY
|
||||
|
||||
source "kernel/irq/Kconfig"
|
||||
|
@ -76,9 +76,7 @@ obj-$(CONFIG_IKCONFIG) += configs.o
|
||||
obj-$(CONFIG_SMP) += stop_machine.o
|
||||
obj-$(CONFIG_KPROBES_SANITY_TEST) += test_kprobes.o
|
||||
obj-$(CONFIG_AUDIT) += audit.o auditfilter.o
|
||||
obj-$(CONFIG_AUDITSYSCALL) += auditsc.o
|
||||
obj-$(CONFIG_AUDIT_WATCH) += audit_watch.o audit_fsnotify.o
|
||||
obj-$(CONFIG_AUDIT_TREE) += audit_tree.o
|
||||
obj-$(CONFIG_AUDITSYSCALL) += auditsc.o audit_watch.o audit_fsnotify.o audit_tree.o
|
||||
obj-$(CONFIG_GCOV_KERNEL) += gcov/
|
||||
obj-$(CONFIG_KCOV) += kcov.o
|
||||
obj-$(CONFIG_KPROBES) += kprobes.o
|
||||
|
@ -60,7 +60,6 @@
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/gfp.h>
|
||||
#include <linux/pid.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#include <linux/audit.h>
|
||||
|
||||
@ -400,7 +399,7 @@ static int audit_log_config_change(char *function_name, u32 new, u32 old,
|
||||
ab = audit_log_start(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE);
|
||||
if (unlikely(!ab))
|
||||
return rc;
|
||||
audit_log_format(ab, "%s=%u old=%u", function_name, new, old);
|
||||
audit_log_format(ab, "%s=%u old=%u ", function_name, new, old);
|
||||
audit_log_session_info(ab);
|
||||
rc = audit_log_task_context(ab);
|
||||
if (rc)
|
||||
@ -1067,7 +1066,7 @@ static void audit_log_common_recv_msg(struct audit_buffer **ab, u16 msg_type)
|
||||
*ab = audit_log_start(NULL, GFP_KERNEL, msg_type);
|
||||
if (unlikely(!*ab))
|
||||
return;
|
||||
audit_log_format(*ab, "pid=%d uid=%u", pid, uid);
|
||||
audit_log_format(*ab, "pid=%d uid=%u ", pid, uid);
|
||||
audit_log_session_info(*ab);
|
||||
audit_log_task_context(*ab);
|
||||
}
|
||||
@ -1096,10 +1095,11 @@ static void audit_log_feature_change(int which, u32 old_feature, u32 new_feature
|
||||
|
||||
if (audit_enabled == AUDIT_OFF)
|
||||
return;
|
||||
|
||||
ab = audit_log_start(audit_context(), GFP_KERNEL, AUDIT_FEATURE_CHANGE);
|
||||
if (!ab)
|
||||
return;
|
||||
audit_log_task_info(ab, current);
|
||||
audit_log_task_info(ab);
|
||||
audit_log_format(ab, " feature=%s old=%u new=%u old_lock=%u new_lock=%u res=%d",
|
||||
audit_feature_names[which], !!old_feature, !!new_feature,
|
||||
!!old_lock, !!new_lock, res);
|
||||
@ -2042,7 +2042,7 @@ void audit_log_session_info(struct audit_buffer *ab)
|
||||
unsigned int sessionid = audit_get_sessionid(current);
|
||||
uid_t auid = from_kuid(&init_user_ns, audit_get_loginuid(current));
|
||||
|
||||
audit_log_format(ab, " auid=%u ses=%u", auid, sessionid);
|
||||
audit_log_format(ab, "auid=%u ses=%u", auid, sessionid);
|
||||
}
|
||||
|
||||
void audit_log_key(struct audit_buffer *ab, char *key)
|
||||
@ -2058,11 +2058,13 @@ void audit_log_cap(struct audit_buffer *ab, char *prefix, kernel_cap_t *cap)
|
||||
{
|
||||
int i;
|
||||
|
||||
audit_log_format(ab, " %s=", prefix);
|
||||
CAP_FOR_EACH_U32(i) {
|
||||
audit_log_format(ab, "%08x",
|
||||
cap->cap[CAP_LAST_U32 - i]);
|
||||
if (cap_isclear(*cap)) {
|
||||
audit_log_format(ab, " %s=0", prefix);
|
||||
return;
|
||||
}
|
||||
audit_log_format(ab, " %s=", prefix);
|
||||
CAP_FOR_EACH_U32(i)
|
||||
audit_log_format(ab, "%08x", cap->cap[CAP_LAST_U32 - i]);
|
||||
}
|
||||
|
||||
static void audit_log_fcaps(struct audit_buffer *ab, struct audit_names *name)
|
||||
@ -2177,22 +2179,21 @@ void audit_log_name(struct audit_context *context, struct audit_names *n,
|
||||
}
|
||||
|
||||
/* log the audit_names record type */
|
||||
audit_log_format(ab, " nametype=");
|
||||
switch(n->type) {
|
||||
case AUDIT_TYPE_NORMAL:
|
||||
audit_log_format(ab, "NORMAL");
|
||||
audit_log_format(ab, " nametype=NORMAL");
|
||||
break;
|
||||
case AUDIT_TYPE_PARENT:
|
||||
audit_log_format(ab, "PARENT");
|
||||
audit_log_format(ab, " nametype=PARENT");
|
||||
break;
|
||||
case AUDIT_TYPE_CHILD_DELETE:
|
||||
audit_log_format(ab, "DELETE");
|
||||
audit_log_format(ab, " nametype=DELETE");
|
||||
break;
|
||||
case AUDIT_TYPE_CHILD_CREATE:
|
||||
audit_log_format(ab, "CREATE");
|
||||
audit_log_format(ab, " nametype=CREATE");
|
||||
break;
|
||||
default:
|
||||
audit_log_format(ab, "UNKNOWN");
|
||||
audit_log_format(ab, " nametype=UNKNOWN");
|
||||
break;
|
||||
}
|
||||
|
||||
@ -2247,15 +2248,15 @@ out_null:
|
||||
audit_log_format(ab, " exe=(null)");
|
||||
}
|
||||
|
||||
struct tty_struct *audit_get_tty(struct task_struct *tsk)
|
||||
struct tty_struct *audit_get_tty(void)
|
||||
{
|
||||
struct tty_struct *tty = NULL;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&tsk->sighand->siglock, flags);
|
||||
if (tsk->signal)
|
||||
tty = tty_kref_get(tsk->signal->tty);
|
||||
spin_unlock_irqrestore(&tsk->sighand->siglock, flags);
|
||||
spin_lock_irqsave(¤t->sighand->siglock, flags);
|
||||
if (current->signal)
|
||||
tty = tty_kref_get(current->signal->tty);
|
||||
spin_unlock_irqrestore(¤t->sighand->siglock, flags);
|
||||
return tty;
|
||||
}
|
||||
|
||||
@ -2264,25 +2265,24 @@ void audit_put_tty(struct tty_struct *tty)
|
||||
tty_kref_put(tty);
|
||||
}
|
||||
|
||||
void audit_log_task_info(struct audit_buffer *ab, struct task_struct *tsk)
|
||||
void audit_log_task_info(struct audit_buffer *ab)
|
||||
{
|
||||
const struct cred *cred;
|
||||
char comm[sizeof(tsk->comm)];
|
||||
char comm[sizeof(current->comm)];
|
||||
struct tty_struct *tty;
|
||||
|
||||
if (!ab)
|
||||
return;
|
||||
|
||||
/* tsk == current */
|
||||
cred = current_cred();
|
||||
tty = audit_get_tty(tsk);
|
||||
tty = audit_get_tty();
|
||||
audit_log_format(ab,
|
||||
" ppid=%d pid=%d auid=%u uid=%u gid=%u"
|
||||
" euid=%u suid=%u fsuid=%u"
|
||||
" egid=%u sgid=%u fsgid=%u tty=%s ses=%u",
|
||||
task_ppid_nr(tsk),
|
||||
task_tgid_nr(tsk),
|
||||
from_kuid(&init_user_ns, audit_get_loginuid(tsk)),
|
||||
task_ppid_nr(current),
|
||||
task_tgid_nr(current),
|
||||
from_kuid(&init_user_ns, audit_get_loginuid(current)),
|
||||
from_kuid(&init_user_ns, cred->uid),
|
||||
from_kgid(&init_user_ns, cred->gid),
|
||||
from_kuid(&init_user_ns, cred->euid),
|
||||
@ -2292,11 +2292,11 @@ void audit_log_task_info(struct audit_buffer *ab, struct task_struct *tsk)
|
||||
from_kgid(&init_user_ns, cred->sgid),
|
||||
from_kgid(&init_user_ns, cred->fsgid),
|
||||
tty ? tty_name(tty) : "(none)",
|
||||
audit_get_sessionid(tsk));
|
||||
audit_get_sessionid(current));
|
||||
audit_put_tty(tty);
|
||||
audit_log_format(ab, " comm=");
|
||||
audit_log_untrustedstring(ab, get_task_comm(comm, tsk));
|
||||
audit_log_d_path_exe(ab, tsk->mm);
|
||||
audit_log_untrustedstring(ab, get_task_comm(comm, current));
|
||||
audit_log_d_path_exe(ab, current->mm);
|
||||
audit_log_task_context(ab);
|
||||
}
|
||||
EXPORT_SYMBOL(audit_log_task_info);
|
||||
@ -2317,7 +2317,7 @@ void audit_log_link_denied(const char *operation)
|
||||
if (!ab)
|
||||
return;
|
||||
audit_log_format(ab, "op=%s", operation);
|
||||
audit_log_task_info(ab, current);
|
||||
audit_log_task_info(ab);
|
||||
audit_log_format(ab, " res=0");
|
||||
audit_log_end(ab);
|
||||
}
|
||||
|
@ -210,6 +210,8 @@ struct audit_context {
|
||||
|
||||
extern bool audit_ever_enabled;
|
||||
|
||||
extern void audit_log_session_info(struct audit_buffer *ab);
|
||||
|
||||
extern void audit_copy_inode(struct audit_names *name,
|
||||
const struct dentry *dentry,
|
||||
struct inode *inode);
|
||||
@ -262,11 +264,11 @@ extern struct audit_entry *audit_dupe_rule(struct audit_krule *old);
|
||||
extern void audit_log_d_path_exe(struct audit_buffer *ab,
|
||||
struct mm_struct *mm);
|
||||
|
||||
extern struct tty_struct *audit_get_tty(struct task_struct *tsk);
|
||||
extern struct tty_struct *audit_get_tty(void);
|
||||
extern void audit_put_tty(struct tty_struct *tty);
|
||||
|
||||
/* audit watch functions */
|
||||
#ifdef CONFIG_AUDIT_WATCH
|
||||
#ifdef CONFIG_AUDITSYSCALL
|
||||
extern void audit_put_watch(struct audit_watch *watch);
|
||||
extern void audit_get_watch(struct audit_watch *watch);
|
||||
extern int audit_to_watch(struct audit_krule *krule, char *path, int len, u32 op);
|
||||
@ -299,9 +301,9 @@ extern int audit_exe_compare(struct task_struct *tsk, struct audit_fsnotify_mark
|
||||
#define audit_mark_compare(m, i, d) 0
|
||||
#define audit_exe_compare(t, m) (-EINVAL)
|
||||
#define audit_dupe_exe(n, o) (-EINVAL)
|
||||
#endif /* CONFIG_AUDIT_WATCH */
|
||||
#endif /* CONFIG_AUDITSYSCALL */
|
||||
|
||||
#ifdef CONFIG_AUDIT_TREE
|
||||
#ifdef CONFIG_AUDITSYSCALL
|
||||
extern struct audit_chunk *audit_tree_lookup(const struct inode *inode);
|
||||
extern void audit_put_chunk(struct audit_chunk *chunk);
|
||||
extern bool audit_tree_match(struct audit_chunk *chunk, struct audit_tree *tree);
|
||||
|
@ -130,10 +130,8 @@ static void audit_mark_log_rule_change(struct audit_fsnotify_mark *audit_mark, c
|
||||
ab = audit_log_start(NULL, GFP_NOFS, AUDIT_CONFIG_CHANGE);
|
||||
if (unlikely(!ab))
|
||||
return;
|
||||
audit_log_format(ab, "auid=%u ses=%u op=%s",
|
||||
from_kuid(&init_user_ns, audit_get_loginuid(current)),
|
||||
audit_get_sessionid(current), op);
|
||||
audit_log_format(ab, " path=");
|
||||
audit_log_session_info(ab);
|
||||
audit_log_format(ab, " op=%s path=", op);
|
||||
audit_log_untrustedstring(ab, audit_mark->path);
|
||||
audit_log_key(ab, rule->filterkey);
|
||||
audit_log_format(ab, " list=%d res=1", rule->listnr);
|
||||
|
@ -24,9 +24,9 @@ struct audit_tree {
|
||||
|
||||
struct audit_chunk {
|
||||
struct list_head hash;
|
||||
struct fsnotify_mark mark;
|
||||
unsigned long key;
|
||||
struct fsnotify_mark *mark;
|
||||
struct list_head trees; /* with root here */
|
||||
int dead;
|
||||
int count;
|
||||
atomic_long_t refs;
|
||||
struct rcu_head head;
|
||||
@ -37,13 +37,25 @@ struct audit_chunk {
|
||||
} owners[];
|
||||
};
|
||||
|
||||
struct audit_tree_mark {
|
||||
struct fsnotify_mark mark;
|
||||
struct audit_chunk *chunk;
|
||||
};
|
||||
|
||||
static LIST_HEAD(tree_list);
|
||||
static LIST_HEAD(prune_list);
|
||||
static struct task_struct *prune_thread;
|
||||
|
||||
/*
|
||||
* One struct chunk is attached to each inode of interest.
|
||||
* We replace struct chunk on tagging/untagging.
|
||||
* One struct chunk is attached to each inode of interest through
|
||||
* audit_tree_mark (fsnotify mark). We replace struct chunk on tagging /
|
||||
* untagging, the mark is stable as long as there is chunk attached. The
|
||||
* association between mark and chunk is protected by hash_lock and
|
||||
* audit_tree_group->mark_mutex. Thus as long as we hold
|
||||
* audit_tree_group->mark_mutex and check that the mark is alive by
|
||||
* FSNOTIFY_MARK_FLAG_ATTACHED flag check, we are sure the mark points to
|
||||
* the current chunk.
|
||||
*
|
||||
* Rules have pointer to struct audit_tree.
|
||||
* Rules have struct list_head rlist forming a list of rules over
|
||||
* the same tree.
|
||||
@ -62,8 +74,12 @@ static struct task_struct *prune_thread;
|
||||
* tree is refcounted; one reference for "some rules on rules_list refer to
|
||||
* it", one for each chunk with pointer to it.
|
||||
*
|
||||
* chunk is refcounted by embedded fsnotify_mark + .refs (non-zero refcount
|
||||
* of watch contributes 1 to .refs).
|
||||
* chunk is refcounted by embedded .refs. Mark associated with the chunk holds
|
||||
* one chunk reference. This reference is dropped either when a mark is going
|
||||
* to be freed (corresponding inode goes away) or when chunk attached to the
|
||||
* mark gets replaced. This reference must be dropped using
|
||||
* audit_mark_put_chunk() to make sure the reference is dropped only after RCU
|
||||
* grace period as it protects RCU readers of the hash table.
|
||||
*
|
||||
* node.index allows to get from node.list to containing chunk.
|
||||
* MSB of that sucker is stolen to mark taggings that we might have to
|
||||
@ -72,6 +88,7 @@ static struct task_struct *prune_thread;
|
||||
*/
|
||||
|
||||
static struct fsnotify_group *audit_tree_group;
|
||||
static struct kmem_cache *audit_tree_mark_cachep __read_mostly;
|
||||
|
||||
static struct audit_tree *alloc_tree(const char *s)
|
||||
{
|
||||
@ -131,12 +148,43 @@ static void __put_chunk(struct rcu_head *rcu)
|
||||
audit_put_chunk(chunk);
|
||||
}
|
||||
|
||||
static void audit_tree_destroy_watch(struct fsnotify_mark *entry)
|
||||
/*
|
||||
* Drop reference to the chunk that was held by the mark. This is the reference
|
||||
* that gets dropped after we've removed the chunk from the hash table and we
|
||||
* use it to make sure chunk cannot be freed before RCU grace period expires.
|
||||
*/
|
||||
static void audit_mark_put_chunk(struct audit_chunk *chunk)
|
||||
{
|
||||
struct audit_chunk *chunk = container_of(entry, struct audit_chunk, mark);
|
||||
call_rcu(&chunk->head, __put_chunk);
|
||||
}
|
||||
|
||||
static inline struct audit_tree_mark *audit_mark(struct fsnotify_mark *mark)
|
||||
{
|
||||
return container_of(mark, struct audit_tree_mark, mark);
|
||||
}
|
||||
|
||||
static struct audit_chunk *mark_chunk(struct fsnotify_mark *mark)
|
||||
{
|
||||
return audit_mark(mark)->chunk;
|
||||
}
|
||||
|
||||
static void audit_tree_destroy_watch(struct fsnotify_mark *mark)
|
||||
{
|
||||
kmem_cache_free(audit_tree_mark_cachep, audit_mark(mark));
|
||||
}
|
||||
|
||||
static struct fsnotify_mark *alloc_mark(void)
|
||||
{
|
||||
struct audit_tree_mark *amark;
|
||||
|
||||
amark = kmem_cache_zalloc(audit_tree_mark_cachep, GFP_KERNEL);
|
||||
if (!amark)
|
||||
return NULL;
|
||||
fsnotify_init_mark(&amark->mark, audit_tree_group);
|
||||
amark->mark.mask = FS_IN_IGNORED;
|
||||
return &amark->mark;
|
||||
}
|
||||
|
||||
static struct audit_chunk *alloc_chunk(int count)
|
||||
{
|
||||
struct audit_chunk *chunk;
|
||||
@ -156,8 +204,6 @@ static struct audit_chunk *alloc_chunk(int count)
|
||||
INIT_LIST_HEAD(&chunk->owners[i].list);
|
||||
chunk->owners[i].index = i;
|
||||
}
|
||||
fsnotify_init_mark(&chunk->mark, audit_tree_group);
|
||||
chunk->mark.mask = FS_IN_IGNORED;
|
||||
return chunk;
|
||||
}
|
||||
|
||||
@ -172,36 +218,25 @@ static unsigned long inode_to_key(const struct inode *inode)
|
||||
return (unsigned long)&inode->i_fsnotify_marks;
|
||||
}
|
||||
|
||||
/*
|
||||
* Function to return search key in our hash from chunk. Key 0 is special and
|
||||
* should never be present in the hash.
|
||||
*/
|
||||
static unsigned long chunk_to_key(struct audit_chunk *chunk)
|
||||
{
|
||||
/*
|
||||
* We have a reference to the mark so it should be attached to a
|
||||
* connector.
|
||||
*/
|
||||
if (WARN_ON_ONCE(!chunk->mark.connector))
|
||||
return 0;
|
||||
return (unsigned long)chunk->mark.connector->obj;
|
||||
}
|
||||
|
||||
static inline struct list_head *chunk_hash(unsigned long key)
|
||||
{
|
||||
unsigned long n = key / L1_CACHE_BYTES;
|
||||
return chunk_hash_heads + n % HASH_SIZE;
|
||||
}
|
||||
|
||||
/* hash_lock & entry->lock is held by caller */
|
||||
/* hash_lock & mark->group->mark_mutex is held by caller */
|
||||
static void insert_hash(struct audit_chunk *chunk)
|
||||
{
|
||||
unsigned long key = chunk_to_key(chunk);
|
||||
struct list_head *list;
|
||||
|
||||
if (!(chunk->mark.flags & FSNOTIFY_MARK_FLAG_ATTACHED))
|
||||
return;
|
||||
list = chunk_hash(key);
|
||||
/*
|
||||
* Make sure chunk is fully initialized before making it visible in the
|
||||
* hash. Pairs with a data dependency barrier in READ_ONCE() in
|
||||
* audit_tree_lookup().
|
||||
*/
|
||||
smp_wmb();
|
||||
WARN_ON_ONCE(!chunk->key);
|
||||
list = chunk_hash(chunk->key);
|
||||
list_add_rcu(&chunk->hash, list);
|
||||
}
|
||||
|
||||
@ -213,7 +248,11 @@ struct audit_chunk *audit_tree_lookup(const struct inode *inode)
|
||||
struct audit_chunk *p;
|
||||
|
||||
list_for_each_entry_rcu(p, list, hash) {
|
||||
if (chunk_to_key(p) == key) {
|
||||
/*
|
||||
* We use a data dependency barrier in READ_ONCE() to make sure
|
||||
* the chunk we see is fully initialized.
|
||||
*/
|
||||
if (READ_ONCE(p->key) == key) {
|
||||
atomic_long_inc(&p->refs);
|
||||
return p;
|
||||
}
|
||||
@ -239,99 +278,56 @@ static struct audit_chunk *find_chunk(struct node *p)
|
||||
return container_of(p, struct audit_chunk, owners[0]);
|
||||
}
|
||||
|
||||
static void untag_chunk(struct node *p)
|
||||
static void replace_mark_chunk(struct fsnotify_mark *mark,
|
||||
struct audit_chunk *chunk)
|
||||
{
|
||||
struct audit_chunk *old;
|
||||
|
||||
assert_spin_locked(&hash_lock);
|
||||
old = mark_chunk(mark);
|
||||
audit_mark(mark)->chunk = chunk;
|
||||
if (chunk)
|
||||
chunk->mark = mark;
|
||||
if (old)
|
||||
old->mark = NULL;
|
||||
}
|
||||
|
||||
static void replace_chunk(struct audit_chunk *new, struct audit_chunk *old)
|
||||
{
|
||||
struct audit_chunk *chunk = find_chunk(p);
|
||||
struct fsnotify_mark *entry = &chunk->mark;
|
||||
struct audit_chunk *new = NULL;
|
||||
struct audit_tree *owner;
|
||||
int size = chunk->count - 1;
|
||||
int i, j;
|
||||
|
||||
fsnotify_get_mark(entry);
|
||||
|
||||
spin_unlock(&hash_lock);
|
||||
|
||||
if (size)
|
||||
new = alloc_chunk(size);
|
||||
|
||||
mutex_lock(&entry->group->mark_mutex);
|
||||
spin_lock(&entry->lock);
|
||||
/*
|
||||
* mark_mutex protects mark from getting detached and thus also from
|
||||
* mark->connector->obj getting NULL.
|
||||
*/
|
||||
if (chunk->dead || !(entry->flags & FSNOTIFY_MARK_FLAG_ATTACHED)) {
|
||||
spin_unlock(&entry->lock);
|
||||
mutex_unlock(&entry->group->mark_mutex);
|
||||
if (new)
|
||||
fsnotify_put_mark(&new->mark);
|
||||
goto out;
|
||||
}
|
||||
|
||||
owner = p->owner;
|
||||
|
||||
if (!size) {
|
||||
chunk->dead = 1;
|
||||
spin_lock(&hash_lock);
|
||||
list_del_init(&chunk->trees);
|
||||
if (owner->root == chunk)
|
||||
owner->root = NULL;
|
||||
list_del_init(&p->list);
|
||||
list_del_rcu(&chunk->hash);
|
||||
spin_unlock(&hash_lock);
|
||||
spin_unlock(&entry->lock);
|
||||
mutex_unlock(&entry->group->mark_mutex);
|
||||
fsnotify_destroy_mark(entry, audit_tree_group);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!new)
|
||||
goto Fallback;
|
||||
|
||||
if (fsnotify_add_mark_locked(&new->mark, entry->connector->obj,
|
||||
FSNOTIFY_OBJ_TYPE_INODE, 1)) {
|
||||
fsnotify_put_mark(&new->mark);
|
||||
goto Fallback;
|
||||
}
|
||||
|
||||
chunk->dead = 1;
|
||||
spin_lock(&hash_lock);
|
||||
list_replace_init(&chunk->trees, &new->trees);
|
||||
if (owner->root == chunk) {
|
||||
list_del_init(&owner->same_root);
|
||||
owner->root = NULL;
|
||||
}
|
||||
|
||||
for (i = j = 0; j <= size; i++, j++) {
|
||||
struct audit_tree *s;
|
||||
if (&chunk->owners[j] == p) {
|
||||
list_del_init(&p->list);
|
||||
new->key = old->key;
|
||||
list_splice_init(&old->trees, &new->trees);
|
||||
list_for_each_entry(owner, &new->trees, same_root)
|
||||
owner->root = new;
|
||||
for (i = j = 0; j < old->count; i++, j++) {
|
||||
if (!old->owners[j].owner) {
|
||||
i--;
|
||||
continue;
|
||||
}
|
||||
s = chunk->owners[j].owner;
|
||||
new->owners[i].owner = s;
|
||||
new->owners[i].index = chunk->owners[j].index - j + i;
|
||||
if (!s) /* result of earlier fallback */
|
||||
owner = old->owners[j].owner;
|
||||
new->owners[i].owner = owner;
|
||||
new->owners[i].index = old->owners[j].index - j + i;
|
||||
if (!owner) /* result of earlier fallback */
|
||||
continue;
|
||||
get_tree(s);
|
||||
list_replace_init(&chunk->owners[j].list, &new->owners[i].list);
|
||||
get_tree(owner);
|
||||
list_replace_init(&old->owners[j].list, &new->owners[i].list);
|
||||
}
|
||||
replace_mark_chunk(old->mark, new);
|
||||
/*
|
||||
* Make sure chunk is fully initialized before making it visible in the
|
||||
* hash. Pairs with a data dependency barrier in READ_ONCE() in
|
||||
* audit_tree_lookup().
|
||||
*/
|
||||
smp_wmb();
|
||||
list_replace_rcu(&old->hash, &new->hash);
|
||||
}
|
||||
|
||||
list_replace_rcu(&chunk->hash, &new->hash);
|
||||
list_for_each_entry(owner, &new->trees, same_root)
|
||||
owner->root = new;
|
||||
spin_unlock(&hash_lock);
|
||||
spin_unlock(&entry->lock);
|
||||
mutex_unlock(&entry->group->mark_mutex);
|
||||
fsnotify_destroy_mark(entry, audit_tree_group);
|
||||
fsnotify_put_mark(&new->mark); /* drop initial reference */
|
||||
goto out;
|
||||
static void remove_chunk_node(struct audit_chunk *chunk, struct node *p)
|
||||
{
|
||||
struct audit_tree *owner = p->owner;
|
||||
|
||||
Fallback:
|
||||
// do the best we can
|
||||
spin_lock(&hash_lock);
|
||||
if (owner->root == chunk) {
|
||||
list_del_init(&owner->same_root);
|
||||
owner->root = NULL;
|
||||
@ -339,37 +335,102 @@ Fallback:
|
||||
list_del_init(&p->list);
|
||||
p->owner = NULL;
|
||||
put_tree(owner);
|
||||
spin_unlock(&hash_lock);
|
||||
spin_unlock(&entry->lock);
|
||||
mutex_unlock(&entry->group->mark_mutex);
|
||||
out:
|
||||
fsnotify_put_mark(entry);
|
||||
spin_lock(&hash_lock);
|
||||
}
|
||||
|
||||
static int chunk_count_trees(struct audit_chunk *chunk)
|
||||
{
|
||||
int i;
|
||||
int ret = 0;
|
||||
|
||||
for (i = 0; i < chunk->count; i++)
|
||||
if (chunk->owners[i].owner)
|
||||
ret++;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void untag_chunk(struct audit_chunk *chunk, struct fsnotify_mark *mark)
|
||||
{
|
||||
struct audit_chunk *new;
|
||||
int size;
|
||||
|
||||
mutex_lock(&audit_tree_group->mark_mutex);
|
||||
/*
|
||||
* mark_mutex stabilizes chunk attached to the mark so we can check
|
||||
* whether it didn't change while we've dropped hash_lock.
|
||||
*/
|
||||
if (!(mark->flags & FSNOTIFY_MARK_FLAG_ATTACHED) ||
|
||||
mark_chunk(mark) != chunk)
|
||||
goto out_mutex;
|
||||
|
||||
size = chunk_count_trees(chunk);
|
||||
if (!size) {
|
||||
spin_lock(&hash_lock);
|
||||
list_del_init(&chunk->trees);
|
||||
list_del_rcu(&chunk->hash);
|
||||
replace_mark_chunk(mark, NULL);
|
||||
spin_unlock(&hash_lock);
|
||||
fsnotify_detach_mark(mark);
|
||||
mutex_unlock(&audit_tree_group->mark_mutex);
|
||||
audit_mark_put_chunk(chunk);
|
||||
fsnotify_free_mark(mark);
|
||||
return;
|
||||
}
|
||||
|
||||
new = alloc_chunk(size);
|
||||
if (!new)
|
||||
goto out_mutex;
|
||||
|
||||
spin_lock(&hash_lock);
|
||||
/*
|
||||
* This has to go last when updating chunk as once replace_chunk() is
|
||||
* called, new RCU readers can see the new chunk.
|
||||
*/
|
||||
replace_chunk(new, chunk);
|
||||
spin_unlock(&hash_lock);
|
||||
mutex_unlock(&audit_tree_group->mark_mutex);
|
||||
audit_mark_put_chunk(chunk);
|
||||
return;
|
||||
|
||||
out_mutex:
|
||||
mutex_unlock(&audit_tree_group->mark_mutex);
|
||||
}
|
||||
|
||||
/* Call with group->mark_mutex held, releases it */
|
||||
static int create_chunk(struct inode *inode, struct audit_tree *tree)
|
||||
{
|
||||
struct fsnotify_mark *entry;
|
||||
struct fsnotify_mark *mark;
|
||||
struct audit_chunk *chunk = alloc_chunk(1);
|
||||
if (!chunk)
|
||||
return -ENOMEM;
|
||||
|
||||
entry = &chunk->mark;
|
||||
if (fsnotify_add_inode_mark(entry, inode, 0)) {
|
||||
fsnotify_put_mark(entry);
|
||||
if (!chunk) {
|
||||
mutex_unlock(&audit_tree_group->mark_mutex);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
mark = alloc_mark();
|
||||
if (!mark) {
|
||||
mutex_unlock(&audit_tree_group->mark_mutex);
|
||||
kfree(chunk);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
if (fsnotify_add_inode_mark_locked(mark, inode, 0)) {
|
||||
mutex_unlock(&audit_tree_group->mark_mutex);
|
||||
fsnotify_put_mark(mark);
|
||||
kfree(chunk);
|
||||
return -ENOSPC;
|
||||
}
|
||||
|
||||
spin_lock(&entry->lock);
|
||||
spin_lock(&hash_lock);
|
||||
if (tree->goner) {
|
||||
spin_unlock(&hash_lock);
|
||||
chunk->dead = 1;
|
||||
spin_unlock(&entry->lock);
|
||||
fsnotify_destroy_mark(entry, audit_tree_group);
|
||||
fsnotify_put_mark(entry);
|
||||
fsnotify_detach_mark(mark);
|
||||
mutex_unlock(&audit_tree_group->mark_mutex);
|
||||
fsnotify_free_mark(mark);
|
||||
fsnotify_put_mark(mark);
|
||||
kfree(chunk);
|
||||
return 0;
|
||||
}
|
||||
replace_mark_chunk(mark, chunk);
|
||||
chunk->owners[0].index = (1U << 31);
|
||||
chunk->owners[0].owner = tree;
|
||||
get_tree(tree);
|
||||
@ -378,35 +439,49 @@ static int create_chunk(struct inode *inode, struct audit_tree *tree)
|
||||
tree->root = chunk;
|
||||
list_add(&tree->same_root, &chunk->trees);
|
||||
}
|
||||
chunk->key = inode_to_key(inode);
|
||||
/*
|
||||
* Inserting into the hash table has to go last as once we do that RCU
|
||||
* readers can see the chunk.
|
||||
*/
|
||||
insert_hash(chunk);
|
||||
spin_unlock(&hash_lock);
|
||||
spin_unlock(&entry->lock);
|
||||
fsnotify_put_mark(entry); /* drop initial reference */
|
||||
mutex_unlock(&audit_tree_group->mark_mutex);
|
||||
/*
|
||||
* Drop our initial reference. When mark we point to is getting freed,
|
||||
* we get notification through ->freeing_mark callback and cleanup
|
||||
* chunk pointing to this mark.
|
||||
*/
|
||||
fsnotify_put_mark(mark);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* the first tagged inode becomes root of tree */
|
||||
static int tag_chunk(struct inode *inode, struct audit_tree *tree)
|
||||
{
|
||||
struct fsnotify_mark *old_entry, *chunk_entry;
|
||||
struct audit_tree *owner;
|
||||
struct fsnotify_mark *mark;
|
||||
struct audit_chunk *chunk, *old;
|
||||
struct node *p;
|
||||
int n;
|
||||
|
||||
old_entry = fsnotify_find_mark(&inode->i_fsnotify_marks,
|
||||
audit_tree_group);
|
||||
if (!old_entry)
|
||||
mutex_lock(&audit_tree_group->mark_mutex);
|
||||
mark = fsnotify_find_mark(&inode->i_fsnotify_marks, audit_tree_group);
|
||||
if (!mark)
|
||||
return create_chunk(inode, tree);
|
||||
|
||||
old = container_of(old_entry, struct audit_chunk, mark);
|
||||
|
||||
/*
|
||||
* Found mark is guaranteed to be attached and mark_mutex protects mark
|
||||
* from getting detached and thus it makes sure there is chunk attached
|
||||
* to the mark.
|
||||
*/
|
||||
/* are we already there? */
|
||||
spin_lock(&hash_lock);
|
||||
old = mark_chunk(mark);
|
||||
for (n = 0; n < old->count; n++) {
|
||||
if (old->owners[n].owner == tree) {
|
||||
spin_unlock(&hash_lock);
|
||||
fsnotify_put_mark(old_entry);
|
||||
mutex_unlock(&audit_tree_group->mark_mutex);
|
||||
fsnotify_put_mark(mark);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@ -414,83 +489,38 @@ static int tag_chunk(struct inode *inode, struct audit_tree *tree)
|
||||
|
||||
chunk = alloc_chunk(old->count + 1);
|
||||
if (!chunk) {
|
||||
fsnotify_put_mark(old_entry);
|
||||
mutex_unlock(&audit_tree_group->mark_mutex);
|
||||
fsnotify_put_mark(mark);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
chunk_entry = &chunk->mark;
|
||||
|
||||
mutex_lock(&old_entry->group->mark_mutex);
|
||||
spin_lock(&old_entry->lock);
|
||||
/*
|
||||
* mark_mutex protects mark from getting detached and thus also from
|
||||
* mark->connector->obj getting NULL.
|
||||
*/
|
||||
if (!(old_entry->flags & FSNOTIFY_MARK_FLAG_ATTACHED)) {
|
||||
/* old_entry is being shot, lets just lie */
|
||||
spin_unlock(&old_entry->lock);
|
||||
mutex_unlock(&old_entry->group->mark_mutex);
|
||||
fsnotify_put_mark(old_entry);
|
||||
fsnotify_put_mark(&chunk->mark);
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
if (fsnotify_add_mark_locked(chunk_entry, old_entry->connector->obj,
|
||||
FSNOTIFY_OBJ_TYPE_INODE, 1)) {
|
||||
spin_unlock(&old_entry->lock);
|
||||
mutex_unlock(&old_entry->group->mark_mutex);
|
||||
fsnotify_put_mark(chunk_entry);
|
||||
fsnotify_put_mark(old_entry);
|
||||
return -ENOSPC;
|
||||
}
|
||||
|
||||
/* even though we hold old_entry->lock, this is safe since chunk_entry->lock could NEVER have been grabbed before */
|
||||
spin_lock(&chunk_entry->lock);
|
||||
spin_lock(&hash_lock);
|
||||
|
||||
/* we now hold old_entry->lock, chunk_entry->lock, and hash_lock */
|
||||
if (tree->goner) {
|
||||
spin_unlock(&hash_lock);
|
||||
chunk->dead = 1;
|
||||
spin_unlock(&chunk_entry->lock);
|
||||
spin_unlock(&old_entry->lock);
|
||||
mutex_unlock(&old_entry->group->mark_mutex);
|
||||
|
||||
fsnotify_destroy_mark(chunk_entry, audit_tree_group);
|
||||
|
||||
fsnotify_put_mark(chunk_entry);
|
||||
fsnotify_put_mark(old_entry);
|
||||
mutex_unlock(&audit_tree_group->mark_mutex);
|
||||
fsnotify_put_mark(mark);
|
||||
kfree(chunk);
|
||||
return 0;
|
||||
}
|
||||
list_replace_init(&old->trees, &chunk->trees);
|
||||
for (n = 0, p = chunk->owners; n < old->count; n++, p++) {
|
||||
struct audit_tree *s = old->owners[n].owner;
|
||||
p->owner = s;
|
||||
p->index = old->owners[n].index;
|
||||
if (!s) /* result of fallback in untag */
|
||||
continue;
|
||||
get_tree(s);
|
||||
list_replace_init(&old->owners[n].list, &p->list);
|
||||
}
|
||||
p = &chunk->owners[chunk->count - 1];
|
||||
p->index = (chunk->count - 1) | (1U<<31);
|
||||
p->owner = tree;
|
||||
get_tree(tree);
|
||||
list_add(&p->list, &tree->chunks);
|
||||
list_replace_rcu(&old->hash, &chunk->hash);
|
||||
list_for_each_entry(owner, &chunk->trees, same_root)
|
||||
owner->root = chunk;
|
||||
old->dead = 1;
|
||||
if (!tree->root) {
|
||||
tree->root = chunk;
|
||||
list_add(&tree->same_root, &chunk->trees);
|
||||
}
|
||||
/*
|
||||
* This has to go last when updating chunk as once replace_chunk() is
|
||||
* called, new RCU readers can see the new chunk.
|
||||
*/
|
||||
replace_chunk(chunk, old);
|
||||
spin_unlock(&hash_lock);
|
||||
spin_unlock(&chunk_entry->lock);
|
||||
spin_unlock(&old_entry->lock);
|
||||
mutex_unlock(&old_entry->group->mark_mutex);
|
||||
fsnotify_destroy_mark(old_entry, audit_tree_group);
|
||||
fsnotify_put_mark(chunk_entry); /* drop initial reference */
|
||||
fsnotify_put_mark(old_entry); /* pair to fsnotify_find mark_entry */
|
||||
mutex_unlock(&audit_tree_group->mark_mutex);
|
||||
fsnotify_put_mark(mark); /* pair to fsnotify_find_mark */
|
||||
audit_mark_put_chunk(old);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -503,8 +533,7 @@ static void audit_tree_log_remove_rule(struct audit_krule *rule)
|
||||
ab = audit_log_start(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE);
|
||||
if (unlikely(!ab))
|
||||
return;
|
||||
audit_log_format(ab, "op=remove_rule");
|
||||
audit_log_format(ab, " dir=");
|
||||
audit_log_format(ab, "op=remove_rule dir=");
|
||||
audit_log_untrustedstring(ab, rule->tree->pathname);
|
||||
audit_log_key(ab, rule->filterkey);
|
||||
audit_log_format(ab, " list=%d res=1", rule->listnr);
|
||||
@ -534,22 +563,48 @@ static void kill_rules(struct audit_tree *tree)
|
||||
}
|
||||
|
||||
/*
|
||||
* finish killing struct audit_tree
|
||||
* Remove tree from chunks. If 'tagged' is set, remove tree only from tagged
|
||||
* chunks. The function expects tagged chunks are all at the beginning of the
|
||||
* chunks list.
|
||||
*/
|
||||
static void prune_one(struct audit_tree *victim)
|
||||
static void prune_tree_chunks(struct audit_tree *victim, bool tagged)
|
||||
{
|
||||
spin_lock(&hash_lock);
|
||||
while (!list_empty(&victim->chunks)) {
|
||||
struct node *p;
|
||||
struct audit_chunk *chunk;
|
||||
struct fsnotify_mark *mark;
|
||||
|
||||
p = list_entry(victim->chunks.next, struct node, list);
|
||||
p = list_first_entry(&victim->chunks, struct node, list);
|
||||
/* have we run out of marked? */
|
||||
if (tagged && !(p->index & (1U<<31)))
|
||||
break;
|
||||
chunk = find_chunk(p);
|
||||
mark = chunk->mark;
|
||||
remove_chunk_node(chunk, p);
|
||||
/* Racing with audit_tree_freeing_mark()? */
|
||||
if (!mark)
|
||||
continue;
|
||||
fsnotify_get_mark(mark);
|
||||
spin_unlock(&hash_lock);
|
||||
|
||||
untag_chunk(p);
|
||||
untag_chunk(chunk, mark);
|
||||
fsnotify_put_mark(mark);
|
||||
|
||||
spin_lock(&hash_lock);
|
||||
}
|
||||
spin_unlock(&hash_lock);
|
||||
put_tree(victim);
|
||||
}
|
||||
|
||||
/*
|
||||
* finish killing struct audit_tree
|
||||
*/
|
||||
static void prune_one(struct audit_tree *victim)
|
||||
{
|
||||
prune_tree_chunks(victim, false);
|
||||
}
|
||||
|
||||
/* trim the uncommitted chunks from tree */
|
||||
|
||||
static void trim_marked(struct audit_tree *tree)
|
||||
@ -569,18 +624,11 @@ static void trim_marked(struct audit_tree *tree)
|
||||
list_add(p, &tree->chunks);
|
||||
}
|
||||
}
|
||||
spin_unlock(&hash_lock);
|
||||
|
||||
while (!list_empty(&tree->chunks)) {
|
||||
struct node *node;
|
||||
prune_tree_chunks(tree, true);
|
||||
|
||||
node = list_entry(tree->chunks.next, struct node, list);
|
||||
|
||||
/* have we run out of marked? */
|
||||
if (!(node->index & (1U<<31)))
|
||||
break;
|
||||
|
||||
untag_chunk(node);
|
||||
}
|
||||
spin_lock(&hash_lock);
|
||||
if (!tree->root && !tree->goner) {
|
||||
tree->goner = 1;
|
||||
spin_unlock(&hash_lock);
|
||||
@ -661,7 +709,7 @@ void audit_trim_trees(void)
|
||||
/* this could be NULL if the watch is dying else where... */
|
||||
node->index |= 1U<<31;
|
||||
if (iterate_mounts(compare_root,
|
||||
(void *)chunk_to_key(chunk),
|
||||
(void *)(chunk->key),
|
||||
root_mnt))
|
||||
node->index &= ~(1U<<31);
|
||||
}
|
||||
@ -959,10 +1007,6 @@ static void evict_chunk(struct audit_chunk *chunk)
|
||||
int need_prune = 0;
|
||||
int n;
|
||||
|
||||
if (chunk->dead)
|
||||
return;
|
||||
|
||||
chunk->dead = 1;
|
||||
mutex_lock(&audit_filter_mutex);
|
||||
spin_lock(&hash_lock);
|
||||
while (!list_empty(&chunk->trees)) {
|
||||
@ -999,17 +1043,27 @@ static int audit_tree_handle_event(struct fsnotify_group *group,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void audit_tree_freeing_mark(struct fsnotify_mark *entry, struct fsnotify_group *group)
|
||||
static void audit_tree_freeing_mark(struct fsnotify_mark *mark,
|
||||
struct fsnotify_group *group)
|
||||
{
|
||||
struct audit_chunk *chunk = container_of(entry, struct audit_chunk, mark);
|
||||
struct audit_chunk *chunk;
|
||||
|
||||
evict_chunk(chunk);
|
||||
mutex_lock(&mark->group->mark_mutex);
|
||||
spin_lock(&hash_lock);
|
||||
chunk = mark_chunk(mark);
|
||||
replace_mark_chunk(mark, NULL);
|
||||
spin_unlock(&hash_lock);
|
||||
mutex_unlock(&mark->group->mark_mutex);
|
||||
if (chunk) {
|
||||
evict_chunk(chunk);
|
||||
audit_mark_put_chunk(chunk);
|
||||
}
|
||||
|
||||
/*
|
||||
* We are guaranteed to have at least one reference to the mark from
|
||||
* either the inode or the caller of fsnotify_destroy_mark().
|
||||
*/
|
||||
BUG_ON(refcount_read(&entry->refcnt) < 1);
|
||||
BUG_ON(refcount_read(&mark->refcnt) < 1);
|
||||
}
|
||||
|
||||
static const struct fsnotify_ops audit_tree_ops = {
|
||||
@ -1022,6 +1076,8 @@ static int __init audit_tree_init(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
audit_tree_mark_cachep = KMEM_CACHE(audit_tree_mark, SLAB_PANIC);
|
||||
|
||||
audit_tree_group = fsnotify_alloc_group(&audit_tree_ops);
|
||||
if (IS_ERR(audit_tree_group))
|
||||
audit_panic("cannot initialize fsnotify group for rectree watches");
|
||||
|
@ -245,10 +245,8 @@ static void audit_watch_log_rule_change(struct audit_krule *r, struct audit_watc
|
||||
ab = audit_log_start(NULL, GFP_NOFS, AUDIT_CONFIG_CHANGE);
|
||||
if (!ab)
|
||||
return;
|
||||
audit_log_format(ab, "auid=%u ses=%u op=%s",
|
||||
from_kuid(&init_user_ns, audit_get_loginuid(current)),
|
||||
audit_get_sessionid(current), op);
|
||||
audit_log_format(ab, " path=");
|
||||
audit_log_session_info(ab);
|
||||
audit_log_format(ab, "op=%s path=", op);
|
||||
audit_log_untrustedstring(ab, w->path);
|
||||
audit_log_key(ab, r->filterkey);
|
||||
audit_log_format(ab, " list=%d res=1", r->listnr);
|
||||
|
150
kernel/auditsc.c
150
kernel/auditsc.c
@ -200,7 +200,6 @@ static int audit_match_filetype(struct audit_context *ctx, int val)
|
||||
* 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) {
|
||||
@ -245,12 +244,10 @@ static int grow_tree_refs(struct audit_context *ctx)
|
||||
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) {
|
||||
@ -274,7 +271,6 @@ static void unroll_tree_refs(struct audit_context *ctx,
|
||||
}
|
||||
ctx->trees = p;
|
||||
ctx->tree_count = count;
|
||||
#endif
|
||||
}
|
||||
|
||||
static void free_tree_refs(struct audit_context *ctx)
|
||||
@ -288,7 +284,6 @@ static void free_tree_refs(struct audit_context *ctx)
|
||||
|
||||
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)
|
||||
@ -305,7 +300,6 @@ static int match_tree_refs(struct audit_context *ctx, struct audit_tree *tree)
|
||||
if (audit_tree_match(p->c[n], tree))
|
||||
return 1;
|
||||
}
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -836,44 +830,6 @@ void audit_filter_inodes(struct task_struct *tsk, struct audit_context *ctx)
|
||||
rcu_read_unlock();
|
||||
}
|
||||
|
||||
/* 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,
|
||||
int return_valid,
|
||||
long return_code)
|
||||
{
|
||||
struct audit_context *context = tsk->audit_context;
|
||||
|
||||
if (!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);
|
||||
}
|
||||
|
||||
audit_set_context(tsk, NULL);
|
||||
return context;
|
||||
}
|
||||
|
||||
static inline void audit_proctitle_free(struct audit_context *context)
|
||||
{
|
||||
kfree(context->proctitle.value);
|
||||
@ -1107,7 +1063,7 @@ static void audit_log_execve_info(struct audit_context *context,
|
||||
}
|
||||
|
||||
/* write as much as we can to the audit log */
|
||||
if (len_buf > 0) {
|
||||
if (len_buf >= 0) {
|
||||
/* NOTE: some magic numbers here - basically if we
|
||||
* can't fit a reasonable amount of data into the
|
||||
* existing audit buffer, flush it and start with
|
||||
@ -1302,15 +1258,18 @@ static inline int audit_proctitle_rtrim(char *proctitle, int len)
|
||||
return len;
|
||||
}
|
||||
|
||||
static void audit_log_proctitle(struct task_struct *tsk,
|
||||
struct audit_context *context)
|
||||
static void audit_log_proctitle(void)
|
||||
{
|
||||
int res;
|
||||
char *buf;
|
||||
char *msg = "(null)";
|
||||
int len = strlen(msg);
|
||||
struct audit_context *context = audit_context();
|
||||
struct audit_buffer *ab;
|
||||
|
||||
if (!context || context->dummy)
|
||||
return;
|
||||
|
||||
ab = audit_log_start(context, GFP_KERNEL, AUDIT_PROCTITLE);
|
||||
if (!ab)
|
||||
return; /* audit_panic or being filtered */
|
||||
@ -1323,7 +1282,7 @@ static void audit_log_proctitle(struct task_struct *tsk,
|
||||
if (!buf)
|
||||
goto out;
|
||||
/* Historically called this from procfs naming */
|
||||
res = get_cmdline(tsk, buf, MAX_PROCTITLE_AUDIT_LEN);
|
||||
res = get_cmdline(current, buf, MAX_PROCTITLE_AUDIT_LEN);
|
||||
if (res == 0) {
|
||||
kfree(buf);
|
||||
goto out;
|
||||
@ -1343,15 +1302,15 @@ out:
|
||||
audit_log_end(ab);
|
||||
}
|
||||
|
||||
static void audit_log_exit(struct audit_context *context, struct task_struct *tsk)
|
||||
static void audit_log_exit(void)
|
||||
{
|
||||
int i, call_panic = 0;
|
||||
struct audit_context *context = audit_context();
|
||||
struct audit_buffer *ab;
|
||||
struct audit_aux_data *aux;
|
||||
struct audit_names *n;
|
||||
|
||||
/* tsk == current */
|
||||
context->personality = tsk->personality;
|
||||
context->personality = current->personality;
|
||||
|
||||
ab = audit_log_start(context, GFP_KERNEL, AUDIT_SYSCALL);
|
||||
if (!ab)
|
||||
@ -1373,7 +1332,7 @@ static void audit_log_exit(struct audit_context *context, struct task_struct *ts
|
||||
context->argv[3],
|
||||
context->name_count);
|
||||
|
||||
audit_log_task_info(ab, tsk);
|
||||
audit_log_task_info(ab);
|
||||
audit_log_key(ab, context->filterkey);
|
||||
audit_log_end(ab);
|
||||
|
||||
@ -1462,7 +1421,7 @@ static void audit_log_exit(struct audit_context *context, struct task_struct *ts
|
||||
audit_log_name(context, n, NULL, i++, &call_panic);
|
||||
}
|
||||
|
||||
audit_log_proctitle(tsk, context);
|
||||
audit_log_proctitle();
|
||||
|
||||
/* Send end of event record to help user space know we are finished */
|
||||
ab = audit_log_start(context, GFP_KERNEL, AUDIT_EOE);
|
||||
@ -1480,22 +1439,31 @@ static void audit_log_exit(struct audit_context *context, struct task_struct *ts
|
||||
*/
|
||||
void __audit_free(struct task_struct *tsk)
|
||||
{
|
||||
struct audit_context *context;
|
||||
struct audit_context *context = tsk->audit_context;
|
||||
|
||||
context = audit_take_context(tsk, 0, 0);
|
||||
if (!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 */
|
||||
/* that can happen only if we are called from do_exit() */
|
||||
if (context->in_syscall && context->current_state == AUDIT_RECORD_CONTEXT)
|
||||
audit_log_exit(context, tsk);
|
||||
/* We are called either by do_exit() or the fork() error handling code;
|
||||
* in the former case tsk == current and in the latter tsk is a
|
||||
* random task_struct that doesn't doesn't have any meaningful data we
|
||||
* need to log via audit_log_exit().
|
||||
*/
|
||||
if (tsk == current && !context->dummy && context->in_syscall) {
|
||||
context->return_valid = 0;
|
||||
context->return_code = 0;
|
||||
|
||||
audit_filter_syscall(tsk, context,
|
||||
&audit_filter_list[AUDIT_FILTER_EXIT]);
|
||||
audit_filter_inodes(tsk, context);
|
||||
if (context->current_state == AUDIT_RECORD_CONTEXT)
|
||||
audit_log_exit();
|
||||
}
|
||||
|
||||
if (!list_empty(&context->killed_trees))
|
||||
audit_kill_trees(&context->killed_trees);
|
||||
|
||||
audit_set_context(tsk, NULL);
|
||||
audit_free_context(context);
|
||||
}
|
||||
|
||||
@ -1565,17 +1533,40 @@ void __audit_syscall_exit(int success, long return_code)
|
||||
{
|
||||
struct audit_context *context;
|
||||
|
||||
if (success)
|
||||
success = AUDITSC_SUCCESS;
|
||||
else
|
||||
success = AUDITSC_FAILURE;
|
||||
|
||||
context = audit_take_context(current, success, return_code);
|
||||
context = audit_context();
|
||||
if (!context)
|
||||
return;
|
||||
|
||||
if (context->in_syscall && context->current_state == AUDIT_RECORD_CONTEXT)
|
||||
audit_log_exit(context, current);
|
||||
if (!context->dummy && context->in_syscall) {
|
||||
if (success)
|
||||
context->return_valid = AUDITSC_SUCCESS;
|
||||
else
|
||||
context->return_valid = AUDITSC_FAILURE;
|
||||
|
||||
/*
|
||||
* 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;
|
||||
|
||||
audit_filter_syscall(current, context,
|
||||
&audit_filter_list[AUDIT_FILTER_EXIT]);
|
||||
audit_filter_inodes(current, context);
|
||||
if (context->current_state == AUDIT_RECORD_CONTEXT)
|
||||
audit_log_exit();
|
||||
}
|
||||
|
||||
context->in_syscall = 0;
|
||||
context->prio = context->state == AUDIT_RECORD_CONTEXT ? ~0ULL : 0;
|
||||
@ -1597,12 +1588,10 @@ void __audit_syscall_exit(int success, long return_code)
|
||||
kfree(context->filterkey);
|
||||
context->filterkey = NULL;
|
||||
}
|
||||
audit_set_context(current, context);
|
||||
}
|
||||
|
||||
static inline void handle_one(const struct inode *inode)
|
||||
{
|
||||
#ifdef CONFIG_AUDIT_TREE
|
||||
struct audit_context *context;
|
||||
struct audit_tree_refs *p;
|
||||
struct audit_chunk *chunk;
|
||||
@ -1627,12 +1616,10 @@ static inline void handle_one(const struct inode *inode)
|
||||
return;
|
||||
}
|
||||
put_tree_ref(context, chunk);
|
||||
#endif
|
||||
}
|
||||
|
||||
static void handle_path(const struct dentry *dentry)
|
||||
{
|
||||
#ifdef CONFIG_AUDIT_TREE
|
||||
struct audit_context *context;
|
||||
struct audit_tree_refs *p;
|
||||
const struct dentry *d, *parent;
|
||||
@ -1685,7 +1672,6 @@ retry:
|
||||
return;
|
||||
}
|
||||
rcu_read_unlock();
|
||||
#endif
|
||||
}
|
||||
|
||||
static struct audit_names *audit_alloc_name(struct audit_context *context,
|
||||
@ -2035,7 +2021,7 @@ static void audit_log_set_loginuid(kuid_t koldloginuid, kuid_t kloginuid,
|
||||
uid = from_kuid(&init_user_ns, task_uid(current));
|
||||
oldloginuid = from_kuid(&init_user_ns, koldloginuid);
|
||||
loginuid = from_kuid(&init_user_ns, kloginuid),
|
||||
tty = audit_get_tty(current);
|
||||
tty = audit_get_tty();
|
||||
|
||||
audit_log_format(ab, "pid=%d uid=%u", task_tgid_nr(current), uid);
|
||||
audit_log_task_context(ab);
|
||||
@ -2056,7 +2042,6 @@ static void audit_log_set_loginuid(kuid_t koldloginuid, kuid_t kloginuid,
|
||||
*/
|
||||
int audit_set_loginuid(kuid_t loginuid)
|
||||
{
|
||||
struct task_struct *task = current;
|
||||
unsigned int oldsessionid, sessionid = AUDIT_SID_UNSET;
|
||||
kuid_t oldloginuid;
|
||||
int rc;
|
||||
@ -2075,8 +2060,8 @@ int audit_set_loginuid(kuid_t loginuid)
|
||||
sessionid = (unsigned int)atomic_inc_return(&session_id);
|
||||
}
|
||||
|
||||
task->sessionid = sessionid;
|
||||
task->loginuid = loginuid;
|
||||
current->sessionid = sessionid;
|
||||
current->loginuid = loginuid;
|
||||
out:
|
||||
audit_log_set_loginuid(oldloginuid, loginuid, oldsessionid, sessionid, rc);
|
||||
return rc;
|
||||
@ -2513,10 +2498,9 @@ void audit_seccomp_actions_logged(const char *names, const char *old_names,
|
||||
if (unlikely(!ab))
|
||||
return;
|
||||
|
||||
audit_log_format(ab, "op=seccomp-logging");
|
||||
audit_log_format(ab, " actions=%s", names);
|
||||
audit_log_format(ab, " old-actions=%s", old_names);
|
||||
audit_log_format(ab, " res=%d", res);
|
||||
audit_log_format(ab,
|
||||
"op=seccomp-logging actions=%s old-actions=%s res=%d",
|
||||
names, old_names, res);
|
||||
audit_log_end(ab);
|
||||
}
|
||||
|
||||
|
@ -336,7 +336,7 @@ void ima_audit_measurement(struct integrity_iint_cache *iint,
|
||||
audit_log_untrustedstring(ab, filename);
|
||||
audit_log_format(ab, " hash=\"%s:%s\"", algo_name, hash);
|
||||
|
||||
audit_log_task_info(ab, current);
|
||||
audit_log_task_info(ab);
|
||||
audit_log_end(ab);
|
||||
|
||||
iint->flags |= IMA_AUDITED;
|
||||
|
Loading…
Reference in New Issue
Block a user