[PATCH] audit signal recipients

When auditing syscalls that send signals, log the pid and security
context for each target process. Optimize the data collection by
adding a counter for signal-related rules, and avoiding allocating an
aux struct unless we have more than one target process. For process
groups, collect pid/context data in blocks of 16. Move the
audit_signal_info() hook up in check_kill_permission() so we audit
attempts where permission is denied.

Signed-off-by: Amy Griffis <amy.griffis@hp.com>
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
This commit is contained in:
Amy Griffis 2007-03-29 18:01:04 -04:00 committed by Al Viro
parent 7f13da40e3
commit e54dc2431d
11 changed files with 206 additions and 29 deletions

View file

@ -28,6 +28,15 @@ static unsigned signal_class[] = {
~0U ~0U
}; };
int audit_classify_arch(int arch)
{
#ifdef CONFIG_IA32_SUPPORT
if (arch == AUDIT_ARCH_I386)
return 1;
#endif
return 0;
}
int audit_classify_syscall(int abi, unsigned syscall) int audit_classify_syscall(int abi, unsigned syscall)
{ {
#ifdef CONFIG_IA32_SUPPORT #ifdef CONFIG_IA32_SUPPORT

View file

@ -28,6 +28,15 @@ static unsigned signal_class[] = {
~0U ~0U
}; };
int audit_classify_arch(int arch)
{
#ifdef CONFIG_PPC64
if (arch == AUDIT_ARCH_PPC)
return 1;
#endif
return 0;
}
int audit_classify_syscall(int abi, unsigned syscall) int audit_classify_syscall(int abi, unsigned syscall)
{ {
#ifdef CONFIG_PPC64 #ifdef CONFIG_PPC64

View file

@ -28,6 +28,15 @@ static unsigned signal_class[] = {
~0U ~0U
}; };
int audit_classify_arch(int arch)
{
#ifdef CONFIG_COMPAT
if (arch == AUDIT_ARCH_S390)
return 1;
#endif
return 0;
}
int audit_classify_syscall(int abi, unsigned syscall) int audit_classify_syscall(int abi, unsigned syscall)
{ {
#ifdef CONFIG_COMPAT #ifdef CONFIG_COMPAT

View file

@ -28,6 +28,15 @@ static unsigned signal_class[] = {
~0U ~0U
}; };
int audit_classify_arch(int arch)
{
#ifdef CONFIG_SPARC32_COMPAT
if (arch == AUDIT_ARCH_SPARC)
return 1;
#endif
return 0;
}
int audit_classify_syscall(int abi, unsigned syscall) int audit_classify_syscall(int abi, unsigned syscall)
{ {
#ifdef CONFIG_SPARC32_COMPAT #ifdef CONFIG_SPARC32_COMPAT

View file

@ -28,6 +28,15 @@ static unsigned signal_class[] = {
~0U ~0U
}; };
int audit_classify_arch(int arch)
{
#ifdef CONFIG_IA32_EMULATION
if (arch == AUDIT_ARCH_I386)
return 1;
#endif
return 0;
}
int audit_classify_syscall(int abi, unsigned syscall) int audit_classify_syscall(int abi, unsigned syscall)
{ {
#ifdef CONFIG_IA32_EMULATION #ifdef CONFIG_IA32_EMULATION

View file

@ -340,6 +340,7 @@ struct mqstat;
#define AUDITSC_RESULT(x) ( ((long)(x))<0?AUDITSC_FAILURE:AUDITSC_SUCCESS ) #define AUDITSC_RESULT(x) ( ((long)(x))<0?AUDITSC_FAILURE:AUDITSC_SUCCESS )
extern int __init audit_register_class(int class, unsigned *list); extern int __init audit_register_class(int class, unsigned *list);
extern int audit_classify_syscall(int abi, unsigned syscall); extern int audit_classify_syscall(int abi, unsigned syscall);
extern int audit_classify_arch(int arch);
#ifdef CONFIG_AUDITSYSCALL #ifdef CONFIG_AUDITSYSCALL
/* These are defined in auditsc.c */ /* These are defined in auditsc.c */
/* Public API */ /* Public API */
@ -458,6 +459,7 @@ static inline int audit_mq_getsetattr(mqd_t mqdes, struct mq_attr *mqstat)
return 0; return 0;
} }
extern int audit_n_rules; extern int audit_n_rules;
extern int audit_signals;
#else #else
#define audit_alloc(t) ({ 0; }) #define audit_alloc(t) ({ 0; })
#define audit_free(t) do { ; } while (0) #define audit_free(t) do { ; } while (0)
@ -490,6 +492,7 @@ extern int audit_n_rules;
#define audit_mq_getsetattr(d,s) ({ 0; }) #define audit_mq_getsetattr(d,s) ({ 0; })
#define audit_ptrace(t) ((void)0) #define audit_ptrace(t) ((void)0)
#define audit_n_rules 0 #define audit_n_rules 0
#define audit_signals 0
#endif #endif
#ifdef CONFIG_AUDIT #ifdef CONFIG_AUDIT

View file

@ -83,6 +83,7 @@ struct audit_krule {
u32 field_count; u32 field_count;
char *filterkey; /* ties events to rules */ char *filterkey; /* ties events to rules */
struct audit_field *fields; struct audit_field *fields;
struct audit_field *arch_f; /* quick access to arch field */
struct audit_field *inode_f; /* quick access to an inode field */ struct audit_field *inode_f; /* quick access to an inode field */
struct audit_watch *watch; /* associated watch */ struct audit_watch *watch; /* associated watch */
struct list_head rlist; /* entry in audit_watch.rules list */ struct list_head rlist; /* entry in audit_watch.rules list */
@ -131,17 +132,19 @@ extern void audit_handle_ievent(struct inotify_watch *, u32, u32, u32,
extern int selinux_audit_rule_update(void); extern int selinux_audit_rule_update(void);
#ifdef CONFIG_AUDITSYSCALL #ifdef CONFIG_AUDITSYSCALL
extern void __audit_signal_info(int sig, struct task_struct *t); extern int __audit_signal_info(int sig, struct task_struct *t);
static inline void audit_signal_info(int sig, struct task_struct *t) static inline int audit_signal_info(int sig, struct task_struct *t)
{ {
if (unlikely(audit_pid && t->tgid == audit_pid)) if (unlikely((audit_pid && t->tgid == audit_pid) ||
__audit_signal_info(sig, t); (audit_signals && !audit_dummy_context())))
return __audit_signal_info(sig, t);
return 0;
} }
extern enum audit_state audit_filter_inodes(struct task_struct *, extern enum audit_state audit_filter_inodes(struct task_struct *,
struct audit_context *); struct audit_context *);
extern void audit_set_auditable(struct audit_context *); extern void audit_set_auditable(struct audit_context *);
#else #else
#define audit_signal_info(s,t) #define audit_signal_info(s,t) AUDIT_DISABLED
#define audit_filter_inodes(t,c) AUDIT_DISABLED #define audit_filter_inodes(t,c) AUDIT_DISABLED
#define audit_set_auditable(c) #define audit_set_auditable(c)
#endif #endif

View file

@ -311,6 +311,43 @@ int audit_match_class(int class, unsigned syscall)
return classes[class][AUDIT_WORD(syscall)] & AUDIT_BIT(syscall); return classes[class][AUDIT_WORD(syscall)] & AUDIT_BIT(syscall);
} }
static inline int audit_match_class_bits(int class, u32 *mask)
{
int i;
if (classes[class]) {
for (i = 0; i < AUDIT_BITMASK_SIZE; i++)
if (mask[i] & classes[class][i])
return 0;
}
return 1;
}
static int audit_match_signal(struct audit_entry *entry)
{
struct audit_field *arch = entry->rule.arch_f;
if (!arch) {
/* When arch is unspecified, we must check both masks on biarch
* as syscall number alone is ambiguous. */
return (audit_match_class_bits(AUDIT_CLASS_SIGNAL,
entry->rule.mask) &&
audit_match_class_bits(AUDIT_CLASS_SIGNAL_32,
entry->rule.mask));
}
switch(audit_classify_arch(arch->val)) {
case 0: /* native */
return (audit_match_class_bits(AUDIT_CLASS_SIGNAL,
entry->rule.mask));
case 1: /* 32bit on biarch */
return (audit_match_class_bits(AUDIT_CLASS_SIGNAL_32,
entry->rule.mask));
default:
return 1;
}
}
/* Common user-space to kernel rule translation. */ /* Common user-space to kernel rule translation. */
static inline struct audit_entry *audit_to_entry_common(struct audit_rule *rule) static inline struct audit_entry *audit_to_entry_common(struct audit_rule *rule)
{ {
@ -429,6 +466,7 @@ static struct audit_entry *audit_rule_to_entry(struct audit_rule *rule)
err = -EINVAL; err = -EINVAL;
goto exit_free; goto exit_free;
} }
entry->rule.arch_f = f;
break; break;
case AUDIT_PERM: case AUDIT_PERM:
if (f->val & ~15) if (f->val & ~15)
@ -519,7 +557,6 @@ static struct audit_entry *audit_data_to_entry(struct audit_rule_data *data,
case AUDIT_FSGID: case AUDIT_FSGID:
case AUDIT_LOGINUID: case AUDIT_LOGINUID:
case AUDIT_PERS: case AUDIT_PERS:
case AUDIT_ARCH:
case AUDIT_MSGTYPE: case AUDIT_MSGTYPE:
case AUDIT_PPID: case AUDIT_PPID:
case AUDIT_DEVMAJOR: case AUDIT_DEVMAJOR:
@ -531,6 +568,9 @@ static struct audit_entry *audit_data_to_entry(struct audit_rule_data *data,
case AUDIT_ARG2: case AUDIT_ARG2:
case AUDIT_ARG3: case AUDIT_ARG3:
break; break;
case AUDIT_ARCH:
entry->rule.arch_f = f;
break;
case AUDIT_SUBJ_USER: case AUDIT_SUBJ_USER:
case AUDIT_SUBJ_ROLE: case AUDIT_SUBJ_ROLE:
case AUDIT_SUBJ_TYPE: case AUDIT_SUBJ_TYPE:
@ -1221,6 +1261,9 @@ static inline int audit_add_rule(struct audit_entry *entry,
#ifdef CONFIG_AUDITSYSCALL #ifdef CONFIG_AUDITSYSCALL
if (!dont_count) if (!dont_count)
audit_n_rules++; audit_n_rules++;
if (!audit_match_signal(entry))
audit_signals++;
#endif #endif
mutex_unlock(&audit_filter_mutex); mutex_unlock(&audit_filter_mutex);
@ -1294,6 +1337,9 @@ static inline int audit_del_rule(struct audit_entry *entry,
#ifdef CONFIG_AUDITSYSCALL #ifdef CONFIG_AUDITSYSCALL
if (!dont_count) if (!dont_count)
audit_n_rules--; audit_n_rules--;
if (!audit_match_signal(entry))
audit_signals--;
#endif #endif
mutex_unlock(&audit_filter_mutex); mutex_unlock(&audit_filter_mutex);

View file

@ -89,6 +89,9 @@ extern int audit_enabled;
/* number of audit rules */ /* number of audit rules */
int audit_n_rules; int audit_n_rules;
/* determines whether we collect data for signals sent */
int audit_signals;
/* When fs/namei.c:getname() is called, we store the pointer in name and /* 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 * we don't let putname() free it (instead we free all of the saved
* pointers at syscall exit time). * pointers at syscall exit time).
@ -114,6 +117,9 @@ struct audit_aux_data {
#define AUDIT_AUX_IPCPERM 0 #define AUDIT_AUX_IPCPERM 0
/* Number of target pids per aux struct. */
#define AUDIT_AUX_PIDS 16
struct audit_aux_data_mq_open { struct audit_aux_data_mq_open {
struct audit_aux_data d; struct audit_aux_data d;
int oflag; int oflag;
@ -181,6 +187,13 @@ struct audit_aux_data_path {
struct vfsmount *mnt; struct vfsmount *mnt;
}; };
struct audit_aux_data_pids {
struct audit_aux_data d;
pid_t target_pid[AUDIT_AUX_PIDS];
u32 target_sid[AUDIT_AUX_PIDS];
int pid_count;
};
/* The per-task audit context. */ /* The per-task audit context. */
struct audit_context { struct audit_context {
int dummy; /* must be the first element */ int dummy; /* must be the first element */
@ -201,6 +214,7 @@ struct audit_context {
struct vfsmount * pwdmnt; struct vfsmount * pwdmnt;
struct audit_context *previous; /* For nested syscalls */ struct audit_context *previous; /* For nested syscalls */
struct audit_aux_data *aux; struct audit_aux_data *aux;
struct audit_aux_data *aux_pids;
/* Save things to print about task_struct */ /* Save things to print about task_struct */
pid_t pid, ppid; pid_t pid, ppid;
@ -657,6 +671,10 @@ static inline void audit_free_aux(struct audit_context *context)
context->aux = aux->next; context->aux = aux->next;
kfree(aux); kfree(aux);
} }
while ((aux = context->aux_pids)) {
context->aux_pids = aux->next;
kfree(aux);
}
} }
static inline void audit_zero_context(struct audit_context *context, static inline void audit_zero_context(struct audit_context *context,
@ -798,6 +816,29 @@ static void audit_log_task_info(struct audit_buffer *ab, struct task_struct *tsk
audit_log_task_context(ab); audit_log_task_context(ab);
} }
static int audit_log_pid_context(struct audit_context *context, pid_t pid,
u32 sid)
{
struct audit_buffer *ab;
char *s = NULL;
u32 len;
int rc = 0;
ab = audit_log_start(context, GFP_KERNEL, AUDIT_OBJ_PID);
if (!ab)
return 1;
if (selinux_sid_to_string(sid, &s, &len)) {
audit_log_format(ab, "opid=%d obj=(none)", pid);
rc = 1;
} else
audit_log_format(ab, "opid=%d obj=%s", pid, s);
audit_log_end(ab);
kfree(s);
return rc;
}
static void audit_log_exit(struct audit_context *context, struct task_struct *tsk) static void audit_log_exit(struct audit_context *context, struct task_struct *tsk)
{ {
int i, call_panic = 0; int i, call_panic = 0;
@ -976,23 +1017,21 @@ static void audit_log_exit(struct audit_context *context, struct task_struct *ts
audit_log_end(ab); audit_log_end(ab);
} }
if (context->target_pid) { for (aux = context->aux_pids; aux; aux = aux->next) {
ab =audit_log_start(context, GFP_KERNEL, AUDIT_OBJ_PID); struct audit_aux_data_pids *axs = (void *)aux;
if (ab) { int i;
char *s = NULL, *t;
u32 len; for (i = 0; i < axs->pid_count; i++)
if (selinux_sid_to_string(context->target_sid, if (audit_log_pid_context(context, axs->target_pid[i],
&s, &len)) axs->target_sid[i]))
t = "(none)"; call_panic = 1;
else
t = s;
audit_log_format(ab, "opid=%d obj=%s",
context->target_pid, t);
audit_log_end(ab);
kfree(s);
}
} }
if (context->target_pid &&
audit_log_pid_context(context, context->target_pid,
context->target_sid))
call_panic = 1;
if (context->pwd && context->pwdmnt) { if (context->pwd && context->pwdmnt) {
ab = audit_log_start(context, GFP_KERNEL, AUDIT_CWD); ab = audit_log_start(context, GFP_KERNEL, AUDIT_CWD);
if (ab) { if (ab) {
@ -1213,7 +1252,10 @@ void audit_syscall_exit(int valid, long return_code)
} else { } else {
audit_free_names(context); audit_free_names(context);
audit_free_aux(context); audit_free_aux(context);
context->aux = NULL;
context->aux_pids = NULL;
context->target_pid = 0; context->target_pid = 0;
context->target_sid = 0;
kfree(context->filterkey); kfree(context->filterkey);
context->filterkey = NULL; context->filterkey = NULL;
tsk->audit_context = context; tsk->audit_context = context;
@ -1947,15 +1989,17 @@ int audit_avc_path(struct dentry *dentry, struct vfsmount *mnt)
* If the audit subsystem is being terminated, record the task (pid) * If the audit subsystem is being terminated, record the task (pid)
* and uid that is doing that. * and uid that is doing that.
*/ */
void __audit_signal_info(int sig, struct task_struct *t) int __audit_signal_info(int sig, struct task_struct *t)
{ {
struct audit_aux_data_pids *axp;
struct task_struct *tsk = current;
struct audit_context *ctx = tsk->audit_context;
extern pid_t audit_sig_pid; extern pid_t audit_sig_pid;
extern uid_t audit_sig_uid; extern uid_t audit_sig_uid;
extern u32 audit_sig_sid; extern u32 audit_sig_sid;
if (sig == SIGTERM || sig == SIGHUP || sig == SIGUSR1) { if (audit_pid && t->tgid == audit_pid &&
struct task_struct *tsk = current; (sig == SIGTERM || sig == SIGHUP || sig == SIGUSR1)) {
struct audit_context *ctx = tsk->audit_context;
audit_sig_pid = tsk->pid; audit_sig_pid = tsk->pid;
if (ctx) if (ctx)
audit_sig_uid = ctx->loginuid; audit_sig_uid = ctx->loginuid;
@ -1963,4 +2007,33 @@ void __audit_signal_info(int sig, struct task_struct *t)
audit_sig_uid = tsk->uid; audit_sig_uid = tsk->uid;
selinux_get_task_sid(tsk, &audit_sig_sid); selinux_get_task_sid(tsk, &audit_sig_sid);
} }
if (!audit_signals) /* audit_context checked in wrapper */
return 0;
/* optimize the common case by putting first signal recipient directly
* in audit_context */
if (!ctx->target_pid) {
ctx->target_pid = t->tgid;
selinux_get_task_sid(t, &ctx->target_sid);
return 0;
}
axp = (void *)ctx->aux_pids;
if (!axp || axp->pid_count == AUDIT_AUX_PIDS) {
axp = kzalloc(sizeof(*axp), GFP_ATOMIC);
if (!axp)
return -ENOMEM;
axp->d.type = AUDIT_OBJ_PID;
axp->d.next = ctx->aux_pids;
ctx->aux_pids = (void *)axp;
}
BUG_ON(axp->pid_count > AUDIT_AUX_PIDS);
axp->target_pid[axp->pid_count] = t->tgid;
selinux_get_task_sid(t, &axp->target_sid[axp->pid_count]);
axp->pid_count++;
return 0;
} }

View file

@ -497,6 +497,11 @@ static int check_kill_permission(int sig, struct siginfo *info,
int error = -EINVAL; int error = -EINVAL;
if (!valid_signal(sig)) if (!valid_signal(sig))
return error; return error;
error = audit_signal_info(sig, t); /* Let audit system see the signal */
if (error)
return error;
error = -EPERM; error = -EPERM;
if ((info == SEND_SIG_NOINFO || (!is_si_special(info) && SI_FROMUSER(info))) if ((info == SEND_SIG_NOINFO || (!is_si_special(info) && SI_FROMUSER(info)))
&& ((sig != SIGCONT) || && ((sig != SIGCONT) ||
@ -506,10 +511,7 @@ static int check_kill_permission(int sig, struct siginfo *info,
&& !capable(CAP_KILL)) && !capable(CAP_KILL))
return error; return error;
error = security_task_kill(t, info, sig, 0); return security_task_kill(t, info, sig, 0);
if (!error)
audit_signal_info(sig, t); /* Let audit system see the signal */
return error;
} }
/* forward decl */ /* forward decl */

View file

@ -28,6 +28,11 @@ static unsigned signal_class[] = {
~0U ~0U
}; };
int audit_classify_arch(int arch)
{
return 0;
}
int audit_classify_syscall(int abi, unsigned syscall) int audit_classify_syscall(int abi, unsigned syscall)
{ {
switch(syscall) { switch(syscall) {