mirror of
https://github.com/Fishwaldo/Star64_linux.git
synced 2025-06-05 22:28:00 +00:00
Merge branch 'audit.b10' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/audit-current
* 'audit.b10' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/audit-current: [PATCH] Audit Filter Performance [PATCH] Rework of IPC auditing [PATCH] More user space subject labels [PATCH] Reworked patch for labels on user space messages [PATCH] change lspp ipc auditing [PATCH] audit inode patch [PATCH] support for context based audit filtering, part 2 [PATCH] support for context based audit filtering [PATCH] no need to wank with task_lock() and pinning task down in audit_syscall_exit() [PATCH] drop task argument of audit_syscall_{entry,exit} [PATCH] drop gfp_mask in audit_log_exit() [PATCH] move call of audit_free() into do_exit() [PATCH] sockaddr patch [PATCH] deal with deadlocks in audit_free()
This commit is contained in:
commit
532f57da40
33 changed files with 1142 additions and 275 deletions
|
@ -671,7 +671,7 @@ int do_syscall_trace(struct pt_regs *regs, int entryexit)
|
||||||
|
|
||||||
if (unlikely(current->audit_context)) {
|
if (unlikely(current->audit_context)) {
|
||||||
if (entryexit)
|
if (entryexit)
|
||||||
audit_syscall_exit(current, AUDITSC_RESULT(regs->eax),
|
audit_syscall_exit(AUDITSC_RESULT(regs->eax),
|
||||||
regs->eax);
|
regs->eax);
|
||||||
/* Debug traps, when using PTRACE_SINGLESTEP, must be sent only
|
/* Debug traps, when using PTRACE_SINGLESTEP, must be sent only
|
||||||
* on the syscall exit path. Normally, when TIF_SYSCALL_AUDIT is
|
* on the syscall exit path. Normally, when TIF_SYSCALL_AUDIT is
|
||||||
|
@ -720,14 +720,13 @@ int do_syscall_trace(struct pt_regs *regs, int entryexit)
|
||||||
ret = is_sysemu;
|
ret = is_sysemu;
|
||||||
out:
|
out:
|
||||||
if (unlikely(current->audit_context) && !entryexit)
|
if (unlikely(current->audit_context) && !entryexit)
|
||||||
audit_syscall_entry(current, AUDIT_ARCH_I386, regs->orig_eax,
|
audit_syscall_entry(AUDIT_ARCH_I386, regs->orig_eax,
|
||||||
regs->ebx, regs->ecx, regs->edx, regs->esi);
|
regs->ebx, regs->ecx, regs->edx, regs->esi);
|
||||||
if (ret == 0)
|
if (ret == 0)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
regs->orig_eax = -1; /* force skip of syscall restarting */
|
regs->orig_eax = -1; /* force skip of syscall restarting */
|
||||||
if (unlikely(current->audit_context))
|
if (unlikely(current->audit_context))
|
||||||
audit_syscall_exit(current, AUDITSC_RESULT(regs->eax),
|
audit_syscall_exit(AUDITSC_RESULT(regs->eax), regs->eax);
|
||||||
regs->eax);
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
|
@ -312,7 +312,7 @@ static void do_sys_vm86(struct kernel_vm86_struct *info, struct task_struct *tsk
|
||||||
|
|
||||||
/*call audit_syscall_exit since we do not exit via the normal paths */
|
/*call audit_syscall_exit since we do not exit via the normal paths */
|
||||||
if (unlikely(current->audit_context))
|
if (unlikely(current->audit_context))
|
||||||
audit_syscall_exit(current, AUDITSC_RESULT(eax), eax);
|
audit_syscall_exit(AUDITSC_RESULT(eax), eax);
|
||||||
|
|
||||||
__asm__ __volatile__(
|
__asm__ __volatile__(
|
||||||
"movl %0,%%esp\n\t"
|
"movl %0,%%esp\n\t"
|
||||||
|
|
|
@ -1644,7 +1644,7 @@ syscall_trace_enter (long arg0, long arg1, long arg2, long arg3,
|
||||||
arch = AUDIT_ARCH_IA64;
|
arch = AUDIT_ARCH_IA64;
|
||||||
}
|
}
|
||||||
|
|
||||||
audit_syscall_entry(current, arch, syscall, arg0, arg1, arg2, arg3);
|
audit_syscall_entry(arch, syscall, arg0, arg1, arg2, arg3);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1662,7 +1662,7 @@ syscall_trace_leave (long arg0, long arg1, long arg2, long arg3,
|
||||||
|
|
||||||
if (success != AUDITSC_SUCCESS)
|
if (success != AUDITSC_SUCCESS)
|
||||||
result = -result;
|
result = -result;
|
||||||
audit_syscall_exit(current, success, result);
|
audit_syscall_exit(success, result);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (test_thread_flag(TIF_SYSCALL_TRACE)
|
if (test_thread_flag(TIF_SYSCALL_TRACE)
|
||||||
|
|
|
@ -483,7 +483,7 @@ static inline int audit_arch(void)
|
||||||
asmlinkage void do_syscall_trace(struct pt_regs *regs, int entryexit)
|
asmlinkage void do_syscall_trace(struct pt_regs *regs, int entryexit)
|
||||||
{
|
{
|
||||||
if (unlikely(current->audit_context) && entryexit)
|
if (unlikely(current->audit_context) && entryexit)
|
||||||
audit_syscall_exit(current, AUDITSC_RESULT(regs->regs[2]),
|
audit_syscall_exit(AUDITSC_RESULT(regs->regs[2]),
|
||||||
regs->regs[2]);
|
regs->regs[2]);
|
||||||
|
|
||||||
if (!(current->ptrace & PT_PTRACED))
|
if (!(current->ptrace & PT_PTRACED))
|
||||||
|
@ -507,7 +507,7 @@ asmlinkage void do_syscall_trace(struct pt_regs *regs, int entryexit)
|
||||||
}
|
}
|
||||||
out:
|
out:
|
||||||
if (unlikely(current->audit_context) && !entryexit)
|
if (unlikely(current->audit_context) && !entryexit)
|
||||||
audit_syscall_entry(current, audit_arch(), regs->regs[2],
|
audit_syscall_entry(audit_arch(), regs->regs[2],
|
||||||
regs->regs[4], regs->regs[5],
|
regs->regs[4], regs->regs[5],
|
||||||
regs->regs[6], regs->regs[7]);
|
regs->regs[6], regs->regs[7]);
|
||||||
}
|
}
|
||||||
|
|
|
@ -538,7 +538,7 @@ void do_syscall_trace_enter(struct pt_regs *regs)
|
||||||
do_syscall_trace();
|
do_syscall_trace();
|
||||||
|
|
||||||
if (unlikely(current->audit_context))
|
if (unlikely(current->audit_context))
|
||||||
audit_syscall_entry(current,
|
audit_syscall_entry(
|
||||||
#ifdef CONFIG_PPC32
|
#ifdef CONFIG_PPC32
|
||||||
AUDIT_ARCH_PPC,
|
AUDIT_ARCH_PPC,
|
||||||
#else
|
#else
|
||||||
|
@ -556,8 +556,7 @@ void do_syscall_trace_leave(struct pt_regs *regs)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (unlikely(current->audit_context))
|
if (unlikely(current->audit_context))
|
||||||
audit_syscall_exit(current,
|
audit_syscall_exit((regs->ccr&0x1000)?AUDITSC_FAILURE:AUDITSC_SUCCESS,
|
||||||
(regs->ccr&0x1000)?AUDITSC_FAILURE:AUDITSC_SUCCESS,
|
|
||||||
regs->result);
|
regs->result);
|
||||||
|
|
||||||
if ((test_thread_flag(TIF_SYSCALL_TRACE)
|
if ((test_thread_flag(TIF_SYSCALL_TRACE)
|
||||||
|
|
|
@ -734,7 +734,7 @@ asmlinkage void
|
||||||
syscall_trace(struct pt_regs *regs, int entryexit)
|
syscall_trace(struct pt_regs *regs, int entryexit)
|
||||||
{
|
{
|
||||||
if (unlikely(current->audit_context) && entryexit)
|
if (unlikely(current->audit_context) && entryexit)
|
||||||
audit_syscall_exit(current, AUDITSC_RESULT(regs->gprs[2]), regs->gprs[2]);
|
audit_syscall_exit(AUDITSC_RESULT(regs->gprs[2]), regs->gprs[2]);
|
||||||
|
|
||||||
if (!test_thread_flag(TIF_SYSCALL_TRACE))
|
if (!test_thread_flag(TIF_SYSCALL_TRACE))
|
||||||
goto out;
|
goto out;
|
||||||
|
@ -761,8 +761,7 @@ syscall_trace(struct pt_regs *regs, int entryexit)
|
||||||
}
|
}
|
||||||
out:
|
out:
|
||||||
if (unlikely(current->audit_context) && !entryexit)
|
if (unlikely(current->audit_context) && !entryexit)
|
||||||
audit_syscall_entry(current,
|
audit_syscall_entry(test_thread_flag(TIF_31BIT)?AUDIT_ARCH_S390:AUDIT_ARCH_S390X,
|
||||||
test_thread_flag(TIF_31BIT)?AUDIT_ARCH_S390:AUDIT_ARCH_S390X,
|
|
||||||
regs->gprs[2], regs->orig_gpr2, regs->gprs[3],
|
regs->gprs[2], regs->orig_gpr2, regs->gprs[3],
|
||||||
regs->gprs[4], regs->gprs[5]);
|
regs->gprs[4], regs->gprs[5]);
|
||||||
}
|
}
|
||||||
|
|
|
@ -653,7 +653,7 @@ asmlinkage void syscall_trace(struct pt_regs *regs, int syscall_exit_p)
|
||||||
if (unlikely(tstate & (TSTATE_XCARRY | TSTATE_ICARRY)))
|
if (unlikely(tstate & (TSTATE_XCARRY | TSTATE_ICARRY)))
|
||||||
result = AUDITSC_FAILURE;
|
result = AUDITSC_FAILURE;
|
||||||
|
|
||||||
audit_syscall_exit(current, result, regs->u_regs[UREG_I0]);
|
audit_syscall_exit(result, regs->u_regs[UREG_I0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!(current->ptrace & PT_PTRACED))
|
if (!(current->ptrace & PT_PTRACED))
|
||||||
|
@ -677,8 +677,7 @@ asmlinkage void syscall_trace(struct pt_regs *regs, int syscall_exit_p)
|
||||||
|
|
||||||
out:
|
out:
|
||||||
if (unlikely(current->audit_context) && !syscall_exit_p)
|
if (unlikely(current->audit_context) && !syscall_exit_p)
|
||||||
audit_syscall_entry(current,
|
audit_syscall_entry((test_thread_flag(TIF_32BIT) ?
|
||||||
(test_thread_flag(TIF_32BIT) ?
|
|
||||||
AUDIT_ARCH_SPARC :
|
AUDIT_ARCH_SPARC :
|
||||||
AUDIT_ARCH_SPARC64),
|
AUDIT_ARCH_SPARC64),
|
||||||
regs->u_regs[UREG_G1],
|
regs->u_regs[UREG_G1],
|
||||||
|
|
|
@ -275,15 +275,13 @@ void syscall_trace(union uml_pt_regs *regs, int entryexit)
|
||||||
|
|
||||||
if (unlikely(current->audit_context)) {
|
if (unlikely(current->audit_context)) {
|
||||||
if (!entryexit)
|
if (!entryexit)
|
||||||
audit_syscall_entry(current,
|
audit_syscall_entry(HOST_AUDIT_ARCH,
|
||||||
HOST_AUDIT_ARCH,
|
|
||||||
UPT_SYSCALL_NR(regs),
|
UPT_SYSCALL_NR(regs),
|
||||||
UPT_SYSCALL_ARG1(regs),
|
UPT_SYSCALL_ARG1(regs),
|
||||||
UPT_SYSCALL_ARG2(regs),
|
UPT_SYSCALL_ARG2(regs),
|
||||||
UPT_SYSCALL_ARG3(regs),
|
UPT_SYSCALL_ARG3(regs),
|
||||||
UPT_SYSCALL_ARG4(regs));
|
UPT_SYSCALL_ARG4(regs));
|
||||||
else audit_syscall_exit(current,
|
else audit_syscall_exit(AUDITSC_RESULT(UPT_SYSCALL_RET(regs)),
|
||||||
AUDITSC_RESULT(UPT_SYSCALL_RET(regs)),
|
|
||||||
UPT_SYSCALL_RET(regs));
|
UPT_SYSCALL_RET(regs));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -600,12 +600,12 @@ asmlinkage void syscall_trace_enter(struct pt_regs *regs)
|
||||||
|
|
||||||
if (unlikely(current->audit_context)) {
|
if (unlikely(current->audit_context)) {
|
||||||
if (test_thread_flag(TIF_IA32)) {
|
if (test_thread_flag(TIF_IA32)) {
|
||||||
audit_syscall_entry(current, AUDIT_ARCH_I386,
|
audit_syscall_entry(AUDIT_ARCH_I386,
|
||||||
regs->orig_rax,
|
regs->orig_rax,
|
||||||
regs->rbx, regs->rcx,
|
regs->rbx, regs->rcx,
|
||||||
regs->rdx, regs->rsi);
|
regs->rdx, regs->rsi);
|
||||||
} else {
|
} else {
|
||||||
audit_syscall_entry(current, AUDIT_ARCH_X86_64,
|
audit_syscall_entry(AUDIT_ARCH_X86_64,
|
||||||
regs->orig_rax,
|
regs->orig_rax,
|
||||||
regs->rdi, regs->rsi,
|
regs->rdi, regs->rsi,
|
||||||
regs->rdx, regs->r10);
|
regs->rdx, regs->r10);
|
||||||
|
@ -616,7 +616,7 @@ asmlinkage void syscall_trace_enter(struct pt_regs *regs)
|
||||||
asmlinkage void syscall_trace_leave(struct pt_regs *regs)
|
asmlinkage void syscall_trace_leave(struct pt_regs *regs)
|
||||||
{
|
{
|
||||||
if (unlikely(current->audit_context))
|
if (unlikely(current->audit_context))
|
||||||
audit_syscall_exit(current, AUDITSC_RESULT(regs->rax), regs->rax);
|
audit_syscall_exit(AUDITSC_RESULT(regs->rax), regs->rax);
|
||||||
|
|
||||||
if ((test_thread_flag(TIF_SYSCALL_TRACE)
|
if ((test_thread_flag(TIF_SYSCALL_TRACE)
|
||||||
|| test_thread_flag(TIF_SINGLESTEP))
|
|| test_thread_flag(TIF_SINGLESTEP))
|
||||||
|
|
|
@ -83,6 +83,7 @@
|
||||||
#define AUDIT_CONFIG_CHANGE 1305 /* Audit system configuration change */
|
#define AUDIT_CONFIG_CHANGE 1305 /* Audit system configuration change */
|
||||||
#define AUDIT_SOCKADDR 1306 /* sockaddr copied as syscall arg */
|
#define AUDIT_SOCKADDR 1306 /* sockaddr copied as syscall arg */
|
||||||
#define AUDIT_CWD 1307 /* Current working directory */
|
#define AUDIT_CWD 1307 /* Current working directory */
|
||||||
|
#define AUDIT_IPC_SET_PERM 1311 /* IPC new permissions record type */
|
||||||
|
|
||||||
#define AUDIT_AVC 1400 /* SE Linux avc denial or grant */
|
#define AUDIT_AVC 1400 /* SE Linux avc denial or grant */
|
||||||
#define AUDIT_SELINUX_ERR 1401 /* Internal SE Linux Errors */
|
#define AUDIT_SELINUX_ERR 1401 /* Internal SE Linux Errors */
|
||||||
|
@ -145,6 +146,11 @@
|
||||||
#define AUDIT_PERS 10
|
#define AUDIT_PERS 10
|
||||||
#define AUDIT_ARCH 11
|
#define AUDIT_ARCH 11
|
||||||
#define AUDIT_MSGTYPE 12
|
#define AUDIT_MSGTYPE 12
|
||||||
|
#define AUDIT_SE_USER 13 /* security label user */
|
||||||
|
#define AUDIT_SE_ROLE 14 /* security label role */
|
||||||
|
#define AUDIT_SE_TYPE 15 /* security label type */
|
||||||
|
#define AUDIT_SE_SEN 16 /* security label sensitivity label */
|
||||||
|
#define AUDIT_SE_CLR 17 /* security label clearance label */
|
||||||
|
|
||||||
/* These are ONLY useful when checking
|
/* These are ONLY useful when checking
|
||||||
* at syscall exit time (AUDIT_AT_EXIT). */
|
* at syscall exit time (AUDIT_AT_EXIT). */
|
||||||
|
@ -287,10 +293,10 @@ struct netlink_skb_parms;
|
||||||
/* Public API */
|
/* Public API */
|
||||||
extern int audit_alloc(struct task_struct *task);
|
extern int audit_alloc(struct task_struct *task);
|
||||||
extern void audit_free(struct task_struct *task);
|
extern void audit_free(struct task_struct *task);
|
||||||
extern void audit_syscall_entry(struct task_struct *task, int arch,
|
extern void audit_syscall_entry(int arch,
|
||||||
int major, unsigned long a0, unsigned long a1,
|
int major, unsigned long a0, unsigned long a1,
|
||||||
unsigned long a2, unsigned long a3);
|
unsigned long a2, unsigned long a3);
|
||||||
extern void audit_syscall_exit(struct task_struct *task, int failed, long return_code);
|
extern void audit_syscall_exit(int failed, long return_code);
|
||||||
extern void audit_getname(const char *name);
|
extern void audit_getname(const char *name);
|
||||||
extern void audit_putname(const char *name);
|
extern void audit_putname(const char *name);
|
||||||
extern void __audit_inode(const char *name, const struct inode *inode, unsigned flags);
|
extern void __audit_inode(const char *name, const struct inode *inode, unsigned flags);
|
||||||
|
@ -314,7 +320,8 @@ extern void auditsc_get_stamp(struct audit_context *ctx,
|
||||||
struct timespec *t, unsigned int *serial);
|
struct timespec *t, unsigned int *serial);
|
||||||
extern int audit_set_loginuid(struct task_struct *task, uid_t loginuid);
|
extern int audit_set_loginuid(struct task_struct *task, uid_t loginuid);
|
||||||
extern uid_t audit_get_loginuid(struct audit_context *ctx);
|
extern uid_t audit_get_loginuid(struct audit_context *ctx);
|
||||||
extern int audit_ipc_perms(unsigned long qbytes, uid_t uid, gid_t gid, mode_t mode, struct kern_ipc_perm *ipcp);
|
extern int audit_ipc_obj(struct kern_ipc_perm *ipcp);
|
||||||
|
extern int audit_ipc_set_perm(unsigned long qbytes, uid_t uid, gid_t gid, mode_t mode, struct kern_ipc_perm *ipcp);
|
||||||
extern int audit_socketcall(int nargs, unsigned long *args);
|
extern int audit_socketcall(int nargs, unsigned long *args);
|
||||||
extern int audit_sockaddr(int len, void *addr);
|
extern int audit_sockaddr(int len, void *addr);
|
||||||
extern int audit_avc_path(struct dentry *dentry, struct vfsmount *mnt);
|
extern int audit_avc_path(struct dentry *dentry, struct vfsmount *mnt);
|
||||||
|
@ -323,8 +330,8 @@ extern int audit_set_macxattr(const char *name);
|
||||||
#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)
|
||||||
#define audit_syscall_entry(t,ta,a,b,c,d,e) do { ; } while (0)
|
#define audit_syscall_entry(ta,a,b,c,d,e) do { ; } while (0)
|
||||||
#define audit_syscall_exit(t,f,r) do { ; } while (0)
|
#define audit_syscall_exit(f,r) do { ; } while (0)
|
||||||
#define audit_getname(n) do { ; } while (0)
|
#define audit_getname(n) do { ; } while (0)
|
||||||
#define audit_putname(n) do { ; } while (0)
|
#define audit_putname(n) do { ; } while (0)
|
||||||
#define __audit_inode(n,i,f) do { ; } while (0)
|
#define __audit_inode(n,i,f) do { ; } while (0)
|
||||||
|
@ -333,7 +340,8 @@ extern int audit_set_macxattr(const char *name);
|
||||||
#define audit_inode_child(d,i,p) do { ; } while (0)
|
#define audit_inode_child(d,i,p) do { ; } while (0)
|
||||||
#define auditsc_get_stamp(c,t,s) do { BUG(); } while (0)
|
#define auditsc_get_stamp(c,t,s) do { BUG(); } while (0)
|
||||||
#define audit_get_loginuid(c) ({ -1; })
|
#define audit_get_loginuid(c) ({ -1; })
|
||||||
#define audit_ipc_perms(q,u,g,m,i) ({ 0; })
|
#define audit_ipc_obj(i) ({ 0; })
|
||||||
|
#define audit_ipc_set_perm(q,u,g,m,i) ({ 0; })
|
||||||
#define audit_socketcall(n,a) ({ 0; })
|
#define audit_socketcall(n,a) ({ 0; })
|
||||||
#define audit_sockaddr(len, addr) ({ 0; })
|
#define audit_sockaddr(len, addr) ({ 0; })
|
||||||
#define audit_avc_path(dentry, mnt) ({ 0; })
|
#define audit_avc_path(dentry, mnt) ({ 0; })
|
||||||
|
@ -366,7 +374,7 @@ extern void audit_log_d_path(struct audit_buffer *ab,
|
||||||
extern int audit_filter_user(struct netlink_skb_parms *cb, int type);
|
extern int audit_filter_user(struct netlink_skb_parms *cb, int type);
|
||||||
extern int audit_filter_type(int type);
|
extern int audit_filter_type(int type);
|
||||||
extern int audit_receive_filter(int type, int pid, int uid, int seq,
|
extern int audit_receive_filter(int type, int pid, int uid, int seq,
|
||||||
void *data, size_t datasz, uid_t loginuid);
|
void *data, size_t datasz, uid_t loginuid, u32 sid);
|
||||||
#else
|
#else
|
||||||
#define audit_log(c,g,t,f,...) do { ; } while (0)
|
#define audit_log(c,g,t,f,...) do { ; } while (0)
|
||||||
#define audit_log_start(c,g,t) ({ NULL; })
|
#define audit_log_start(c,g,t) ({ NULL; })
|
||||||
|
|
|
@ -143,6 +143,7 @@ struct netlink_skb_parms
|
||||||
__u32 dst_group;
|
__u32 dst_group;
|
||||||
kernel_cap_t eff_cap;
|
kernel_cap_t eff_cap;
|
||||||
__u32 loginuid; /* Login (audit) uid */
|
__u32 loginuid; /* Login (audit) uid */
|
||||||
|
__u32 sid; /* SELinux security id */
|
||||||
};
|
};
|
||||||
|
|
||||||
#define NETLINK_CB(skb) (*(struct netlink_skb_parms*)&((skb)->cb))
|
#define NETLINK_CB(skb) (*(struct netlink_skb_parms*)&((skb)->cb))
|
||||||
|
|
|
@ -869,11 +869,6 @@ struct swap_info_struct;
|
||||||
* @ipcp contains the kernel IPC permission structure
|
* @ipcp contains the kernel IPC permission structure
|
||||||
* @flag contains the desired (requested) permission set
|
* @flag contains the desired (requested) permission set
|
||||||
* Return 0 if permission is granted.
|
* Return 0 if permission is granted.
|
||||||
* @ipc_getsecurity:
|
|
||||||
* Copy the security label associated with the ipc object into
|
|
||||||
* @buffer. @buffer may be NULL to request the size of the buffer
|
|
||||||
* required. @size indicates the size of @buffer in bytes. Return
|
|
||||||
* number of bytes used/required on success.
|
|
||||||
*
|
*
|
||||||
* Security hooks for individual messages held in System V IPC message queues
|
* Security hooks for individual messages held in System V IPC message queues
|
||||||
* @msg_msg_alloc_security:
|
* @msg_msg_alloc_security:
|
||||||
|
@ -1223,7 +1218,6 @@ struct security_operations {
|
||||||
void (*task_to_inode)(struct task_struct *p, struct inode *inode);
|
void (*task_to_inode)(struct task_struct *p, struct inode *inode);
|
||||||
|
|
||||||
int (*ipc_permission) (struct kern_ipc_perm * ipcp, short flag);
|
int (*ipc_permission) (struct kern_ipc_perm * ipcp, short flag);
|
||||||
int (*ipc_getsecurity)(struct kern_ipc_perm *ipcp, void *buffer, size_t size);
|
|
||||||
|
|
||||||
int (*msg_msg_alloc_security) (struct msg_msg * msg);
|
int (*msg_msg_alloc_security) (struct msg_msg * msg);
|
||||||
void (*msg_msg_free_security) (struct msg_msg * msg);
|
void (*msg_msg_free_security) (struct msg_msg * msg);
|
||||||
|
@ -1887,11 +1881,6 @@ static inline int security_ipc_permission (struct kern_ipc_perm *ipcp,
|
||||||
return security_ops->ipc_permission (ipcp, flag);
|
return security_ops->ipc_permission (ipcp, flag);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline int security_ipc_getsecurity(struct kern_ipc_perm *ipcp, void *buffer, size_t size)
|
|
||||||
{
|
|
||||||
return security_ops->ipc_getsecurity(ipcp, buffer, size);
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline int security_msg_msg_alloc (struct msg_msg * msg)
|
static inline int security_msg_msg_alloc (struct msg_msg * msg)
|
||||||
{
|
{
|
||||||
return security_ops->msg_msg_alloc_security (msg);
|
return security_ops->msg_msg_alloc_security (msg);
|
||||||
|
@ -2532,11 +2521,6 @@ static inline int security_ipc_permission (struct kern_ipc_perm *ipcp,
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline int security_ipc_getsecurity(struct kern_ipc_perm *ipcp, void *buffer, size_t size)
|
|
||||||
{
|
|
||||||
return -EOPNOTSUPP;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline int security_msg_msg_alloc (struct msg_msg * msg)
|
static inline int security_msg_msg_alloc (struct msg_msg * msg)
|
||||||
{
|
{
|
||||||
return 0;
|
return 0;
|
||||||
|
|
177
include/linux/selinux.h
Normal file
177
include/linux/selinux.h
Normal file
|
@ -0,0 +1,177 @@
|
||||||
|
/*
|
||||||
|
* SELinux services exported to the rest of the kernel.
|
||||||
|
*
|
||||||
|
* Author: James Morris <jmorris@redhat.com>
|
||||||
|
*
|
||||||
|
* Copyright (C) 2005 Red Hat, Inc., James Morris <jmorris@redhat.com>
|
||||||
|
* Copyright (C) 2006 Trusted Computer Solutions, Inc. <dgoeddel@trustedcs.com>
|
||||||
|
* Copyright (C) 2006 IBM Corporation, Timothy R. Chavez <tinytim@us.ibm.com>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License version 2,
|
||||||
|
* as published by the Free Software Foundation.
|
||||||
|
*/
|
||||||
|
#ifndef _LINUX_SELINUX_H
|
||||||
|
#define _LINUX_SELINUX_H
|
||||||
|
|
||||||
|
struct selinux_audit_rule;
|
||||||
|
struct audit_context;
|
||||||
|
struct inode;
|
||||||
|
struct kern_ipc_perm;
|
||||||
|
|
||||||
|
#ifdef CONFIG_SECURITY_SELINUX
|
||||||
|
|
||||||
|
/**
|
||||||
|
* selinux_audit_rule_init - alloc/init an selinux audit rule structure.
|
||||||
|
* @field: the field this rule refers to
|
||||||
|
* @op: the operater the rule uses
|
||||||
|
* @rulestr: the text "target" of the rule
|
||||||
|
* @rule: pointer to the new rule structure returned via this
|
||||||
|
*
|
||||||
|
* Returns 0 if successful, -errno if not. On success, the rule structure
|
||||||
|
* will be allocated internally. The caller must free this structure with
|
||||||
|
* selinux_audit_rule_free() after use.
|
||||||
|
*/
|
||||||
|
int selinux_audit_rule_init(u32 field, u32 op, char *rulestr,
|
||||||
|
struct selinux_audit_rule **rule);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* selinux_audit_rule_free - free an selinux audit rule structure.
|
||||||
|
* @rule: pointer to the audit rule to be freed
|
||||||
|
*
|
||||||
|
* This will free all memory associated with the given rule.
|
||||||
|
* If @rule is NULL, no operation is performed.
|
||||||
|
*/
|
||||||
|
void selinux_audit_rule_free(struct selinux_audit_rule *rule);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* selinux_audit_rule_match - determine if a context ID matches a rule.
|
||||||
|
* @ctxid: the context ID to check
|
||||||
|
* @field: the field this rule refers to
|
||||||
|
* @op: the operater the rule uses
|
||||||
|
* @rule: pointer to the audit rule to check against
|
||||||
|
* @actx: the audit context (can be NULL) associated with the check
|
||||||
|
*
|
||||||
|
* Returns 1 if the context id matches the rule, 0 if it does not, and
|
||||||
|
* -errno on failure.
|
||||||
|
*/
|
||||||
|
int selinux_audit_rule_match(u32 ctxid, u32 field, u32 op,
|
||||||
|
struct selinux_audit_rule *rule,
|
||||||
|
struct audit_context *actx);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* selinux_audit_set_callback - set the callback for policy reloads.
|
||||||
|
* @callback: the function to call when the policy is reloaded
|
||||||
|
*
|
||||||
|
* This sets the function callback function that will update the rules
|
||||||
|
* upon policy reloads. This callback should rebuild all existing rules
|
||||||
|
* using selinux_audit_rule_init().
|
||||||
|
*/
|
||||||
|
void selinux_audit_set_callback(int (*callback)(void));
|
||||||
|
|
||||||
|
/**
|
||||||
|
* selinux_task_ctxid - determine a context ID for a process.
|
||||||
|
* @tsk: the task object
|
||||||
|
* @ctxid: ID value returned via this
|
||||||
|
*
|
||||||
|
* On return, ctxid will contain an ID for the context. This value
|
||||||
|
* should only be used opaquely.
|
||||||
|
*/
|
||||||
|
void selinux_task_ctxid(struct task_struct *tsk, u32 *ctxid);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* selinux_ctxid_to_string - map a security context ID to a string
|
||||||
|
* @ctxid: security context ID to be converted.
|
||||||
|
* @ctx: address of context string to be returned
|
||||||
|
* @ctxlen: length of returned context string.
|
||||||
|
*
|
||||||
|
* Returns 0 if successful, -errno if not. On success, the context
|
||||||
|
* string will be allocated internally, and the caller must call
|
||||||
|
* kfree() on it after use.
|
||||||
|
*/
|
||||||
|
int selinux_ctxid_to_string(u32 ctxid, char **ctx, u32 *ctxlen);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* selinux_get_inode_sid - get the inode's security context ID
|
||||||
|
* @inode: inode structure to get the sid from.
|
||||||
|
* @sid: pointer to security context ID to be filled in.
|
||||||
|
*
|
||||||
|
* Returns nothing
|
||||||
|
*/
|
||||||
|
void selinux_get_inode_sid(const struct inode *inode, u32 *sid);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* selinux_get_ipc_sid - get the ipc security context ID
|
||||||
|
* @ipcp: ipc structure to get the sid from.
|
||||||
|
* @sid: pointer to security context ID to be filled in.
|
||||||
|
*
|
||||||
|
* Returns nothing
|
||||||
|
*/
|
||||||
|
void selinux_get_ipc_sid(const struct kern_ipc_perm *ipcp, u32 *sid);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* selinux_get_task_sid - return the SID of task
|
||||||
|
* @tsk: the task whose SID will be returned
|
||||||
|
* @sid: pointer to security context ID to be filled in.
|
||||||
|
*
|
||||||
|
* Returns nothing
|
||||||
|
*/
|
||||||
|
void selinux_get_task_sid(struct task_struct *tsk, u32 *sid);
|
||||||
|
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
static inline int selinux_audit_rule_init(u32 field, u32 op,
|
||||||
|
char *rulestr,
|
||||||
|
struct selinux_audit_rule **rule)
|
||||||
|
{
|
||||||
|
return -ENOTSUPP;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void selinux_audit_rule_free(struct selinux_audit_rule *rule)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int selinux_audit_rule_match(u32 ctxid, u32 field, u32 op,
|
||||||
|
struct selinux_audit_rule *rule,
|
||||||
|
struct audit_context *actx)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void selinux_audit_set_callback(int (*callback)(void))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void selinux_task_ctxid(struct task_struct *tsk, u32 *ctxid)
|
||||||
|
{
|
||||||
|
*ctxid = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int selinux_ctxid_to_string(u32 ctxid, char **ctx, u32 *ctxlen)
|
||||||
|
{
|
||||||
|
*ctx = NULL;
|
||||||
|
*ctxlen = 0;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void selinux_get_inode_sid(const struct inode *inode, u32 *sid)
|
||||||
|
{
|
||||||
|
*sid = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void selinux_get_ipc_sid(const struct kern_ipc_perm *ipcp, u32 *sid)
|
||||||
|
{
|
||||||
|
*sid = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void selinux_get_task_sid(struct task_struct *tsk, u32 *sid)
|
||||||
|
{
|
||||||
|
*sid = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* CONFIG_SECURITY_SELINUX */
|
||||||
|
|
||||||
|
#endif /* _LINUX_SELINUX_H */
|
11
ipc/msg.c
11
ipc/msg.c
|
@ -13,6 +13,9 @@
|
||||||
* mostly rewritten, threaded and wake-one semantics added
|
* mostly rewritten, threaded and wake-one semantics added
|
||||||
* MSGMAX limit removed, sysctl's added
|
* MSGMAX limit removed, sysctl's added
|
||||||
* (c) 1999 Manfred Spraul <manfred@colorfullife.com>
|
* (c) 1999 Manfred Spraul <manfred@colorfullife.com>
|
||||||
|
*
|
||||||
|
* support for audit of ipc object properties and permission changes
|
||||||
|
* Dustin Kirkland <dustin.kirkland@us.ibm.com>
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <linux/capability.h>
|
#include <linux/capability.h>
|
||||||
|
@ -447,6 +450,11 @@ asmlinkage long sys_msgctl (int msqid, int cmd, struct msqid_ds __user *buf)
|
||||||
if (msg_checkid(msq,msqid))
|
if (msg_checkid(msq,msqid))
|
||||||
goto out_unlock_up;
|
goto out_unlock_up;
|
||||||
ipcp = &msq->q_perm;
|
ipcp = &msq->q_perm;
|
||||||
|
|
||||||
|
err = audit_ipc_obj(ipcp);
|
||||||
|
if (err)
|
||||||
|
goto out_unlock_up;
|
||||||
|
|
||||||
err = -EPERM;
|
err = -EPERM;
|
||||||
if (current->euid != ipcp->cuid &&
|
if (current->euid != ipcp->cuid &&
|
||||||
current->euid != ipcp->uid && !capable(CAP_SYS_ADMIN))
|
current->euid != ipcp->uid && !capable(CAP_SYS_ADMIN))
|
||||||
|
@ -460,7 +468,8 @@ asmlinkage long sys_msgctl (int msqid, int cmd, struct msqid_ds __user *buf)
|
||||||
switch (cmd) {
|
switch (cmd) {
|
||||||
case IPC_SET:
|
case IPC_SET:
|
||||||
{
|
{
|
||||||
if ((err = audit_ipc_perms(setbuf.qbytes, setbuf.uid, setbuf.gid, setbuf.mode, ipcp)))
|
err = audit_ipc_set_perm(setbuf.qbytes, setbuf.uid, setbuf.gid, setbuf.mode, ipcp);
|
||||||
|
if (err)
|
||||||
goto out_unlock_up;
|
goto out_unlock_up;
|
||||||
|
|
||||||
err = -EPERM;
|
err = -EPERM;
|
||||||
|
|
11
ipc/sem.c
11
ipc/sem.c
|
@ -61,6 +61,9 @@
|
||||||
* (c) 2001 Red Hat Inc <alan@redhat.com>
|
* (c) 2001 Red Hat Inc <alan@redhat.com>
|
||||||
* Lockless wakeup
|
* Lockless wakeup
|
||||||
* (c) 2003 Manfred Spraul <manfred@colorfullife.com>
|
* (c) 2003 Manfred Spraul <manfred@colorfullife.com>
|
||||||
|
*
|
||||||
|
* support for audit of ipc object properties and permission changes
|
||||||
|
* Dustin Kirkland <dustin.kirkland@us.ibm.com>
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <linux/config.h>
|
#include <linux/config.h>
|
||||||
|
@ -820,6 +823,11 @@ static int semctl_down(int semid, int semnum, int cmd, int version, union semun
|
||||||
goto out_unlock;
|
goto out_unlock;
|
||||||
}
|
}
|
||||||
ipcp = &sma->sem_perm;
|
ipcp = &sma->sem_perm;
|
||||||
|
|
||||||
|
err = audit_ipc_obj(ipcp);
|
||||||
|
if (err)
|
||||||
|
goto out_unlock;
|
||||||
|
|
||||||
if (current->euid != ipcp->cuid &&
|
if (current->euid != ipcp->cuid &&
|
||||||
current->euid != ipcp->uid && !capable(CAP_SYS_ADMIN)) {
|
current->euid != ipcp->uid && !capable(CAP_SYS_ADMIN)) {
|
||||||
err=-EPERM;
|
err=-EPERM;
|
||||||
|
@ -836,7 +844,8 @@ static int semctl_down(int semid, int semnum, int cmd, int version, union semun
|
||||||
err = 0;
|
err = 0;
|
||||||
break;
|
break;
|
||||||
case IPC_SET:
|
case IPC_SET:
|
||||||
if ((err = audit_ipc_perms(0, setbuf.uid, setbuf.gid, setbuf.mode, ipcp)))
|
err = audit_ipc_set_perm(0, setbuf.uid, setbuf.gid, setbuf.mode, ipcp);
|
||||||
|
if (err)
|
||||||
goto out_unlock;
|
goto out_unlock;
|
||||||
ipcp->uid = setbuf.uid;
|
ipcp->uid = setbuf.uid;
|
||||||
ipcp->gid = setbuf.gid;
|
ipcp->gid = setbuf.gid;
|
||||||
|
|
19
ipc/shm.c
19
ipc/shm.c
|
@ -13,6 +13,8 @@
|
||||||
* Shared /dev/zero support, Kanoj Sarcar <kanoj@sgi.com>
|
* Shared /dev/zero support, Kanoj Sarcar <kanoj@sgi.com>
|
||||||
* Move the mm functionality over to mm/shmem.c, Christoph Rohland <cr@sap.com>
|
* Move the mm functionality over to mm/shmem.c, Christoph Rohland <cr@sap.com>
|
||||||
*
|
*
|
||||||
|
* support for audit of ipc object properties and permission changes
|
||||||
|
* Dustin Kirkland <dustin.kirkland@us.ibm.com>
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <linux/config.h>
|
#include <linux/config.h>
|
||||||
|
@ -542,6 +544,10 @@ asmlinkage long sys_shmctl (int shmid, int cmd, struct shmid_ds __user *buf)
|
||||||
if(err)
|
if(err)
|
||||||
goto out_unlock;
|
goto out_unlock;
|
||||||
|
|
||||||
|
err = audit_ipc_obj(&(shp->shm_perm));
|
||||||
|
if (err)
|
||||||
|
goto out_unlock;
|
||||||
|
|
||||||
if (!capable(CAP_IPC_LOCK)) {
|
if (!capable(CAP_IPC_LOCK)) {
|
||||||
err = -EPERM;
|
err = -EPERM;
|
||||||
if (current->euid != shp->shm_perm.uid &&
|
if (current->euid != shp->shm_perm.uid &&
|
||||||
|
@ -594,6 +600,10 @@ asmlinkage long sys_shmctl (int shmid, int cmd, struct shmid_ds __user *buf)
|
||||||
if(err)
|
if(err)
|
||||||
goto out_unlock_up;
|
goto out_unlock_up;
|
||||||
|
|
||||||
|
err = audit_ipc_obj(&(shp->shm_perm));
|
||||||
|
if (err)
|
||||||
|
goto out_unlock_up;
|
||||||
|
|
||||||
if (current->euid != shp->shm_perm.uid &&
|
if (current->euid != shp->shm_perm.uid &&
|
||||||
current->euid != shp->shm_perm.cuid &&
|
current->euid != shp->shm_perm.cuid &&
|
||||||
!capable(CAP_SYS_ADMIN)) {
|
!capable(CAP_SYS_ADMIN)) {
|
||||||
|
@ -627,10 +637,13 @@ asmlinkage long sys_shmctl (int shmid, int cmd, struct shmid_ds __user *buf)
|
||||||
err=-EINVAL;
|
err=-EINVAL;
|
||||||
if(shp==NULL)
|
if(shp==NULL)
|
||||||
goto out_up;
|
goto out_up;
|
||||||
if ((err = audit_ipc_perms(0, setbuf.uid, setbuf.gid,
|
|
||||||
setbuf.mode, &(shp->shm_perm))))
|
|
||||||
goto out_unlock_up;
|
|
||||||
err = shm_checkid(shp,shmid);
|
err = shm_checkid(shp,shmid);
|
||||||
|
if(err)
|
||||||
|
goto out_unlock_up;
|
||||||
|
err = audit_ipc_obj(&(shp->shm_perm));
|
||||||
|
if (err)
|
||||||
|
goto out_unlock_up;
|
||||||
|
err = audit_ipc_set_perm(0, setbuf.uid, setbuf.gid, setbuf.mode, &(shp->shm_perm));
|
||||||
if (err)
|
if (err)
|
||||||
goto out_unlock_up;
|
goto out_unlock_up;
|
||||||
err=-EPERM;
|
err=-EPERM;
|
||||||
|
|
|
@ -10,6 +10,8 @@
|
||||||
* Manfred Spraul <manfred@colorfullife.com>
|
* Manfred Spraul <manfred@colorfullife.com>
|
||||||
* Oct 2002 - One lock per IPC id. RCU ipc_free for lock-free grow_ary().
|
* Oct 2002 - One lock per IPC id. RCU ipc_free for lock-free grow_ary().
|
||||||
* Mingming Cao <cmm@us.ibm.com>
|
* Mingming Cao <cmm@us.ibm.com>
|
||||||
|
* Mar 2006 - support for audit of ipc object properties
|
||||||
|
* Dustin Kirkland <dustin.kirkland@us.ibm.com>
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <linux/config.h>
|
#include <linux/config.h>
|
||||||
|
@ -27,6 +29,7 @@
|
||||||
#include <linux/workqueue.h>
|
#include <linux/workqueue.h>
|
||||||
#include <linux/seq_file.h>
|
#include <linux/seq_file.h>
|
||||||
#include <linux/proc_fs.h>
|
#include <linux/proc_fs.h>
|
||||||
|
#include <linux/audit.h>
|
||||||
|
|
||||||
#include <asm/unistd.h>
|
#include <asm/unistd.h>
|
||||||
|
|
||||||
|
@ -464,8 +467,10 @@ void ipc_rcu_putref(void *ptr)
|
||||||
|
|
||||||
int ipcperms (struct kern_ipc_perm *ipcp, short flag)
|
int ipcperms (struct kern_ipc_perm *ipcp, short flag)
|
||||||
{ /* flag will most probably be 0 or S_...UGO from <linux/stat.h> */
|
{ /* flag will most probably be 0 or S_...UGO from <linux/stat.h> */
|
||||||
int requested_mode, granted_mode;
|
int requested_mode, granted_mode, err;
|
||||||
|
|
||||||
|
if (unlikely((err = audit_ipc_obj(ipcp))))
|
||||||
|
return err;
|
||||||
requested_mode = (flag >> 6) | (flag >> 3) | flag;
|
requested_mode = (flag >> 6) | (flag >> 3) | flag;
|
||||||
granted_mode = ipcp->mode;
|
granted_mode = ipcp->mode;
|
||||||
if (current->euid == ipcp->cuid || current->euid == ipcp->uid)
|
if (current->euid == ipcp->cuid || current->euid == ipcp->uid)
|
||||||
|
|
142
kernel/audit.c
142
kernel/audit.c
|
@ -55,6 +55,9 @@
|
||||||
#include <net/netlink.h>
|
#include <net/netlink.h>
|
||||||
#include <linux/skbuff.h>
|
#include <linux/skbuff.h>
|
||||||
#include <linux/netlink.h>
|
#include <linux/netlink.h>
|
||||||
|
#include <linux/selinux.h>
|
||||||
|
|
||||||
|
#include "audit.h"
|
||||||
|
|
||||||
/* No auditing will take place until audit_initialized != 0.
|
/* No auditing will take place until audit_initialized != 0.
|
||||||
* (Initialization happens after skb_init is called.) */
|
* (Initialization happens after skb_init is called.) */
|
||||||
|
@ -227,49 +230,103 @@ void audit_log_lost(const char *message)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static int audit_set_rate_limit(int limit, uid_t loginuid)
|
static int audit_set_rate_limit(int limit, uid_t loginuid, u32 sid)
|
||||||
{
|
{
|
||||||
int old = audit_rate_limit;
|
int old = audit_rate_limit;
|
||||||
audit_rate_limit = limit;
|
|
||||||
|
if (sid) {
|
||||||
|
char *ctx = NULL;
|
||||||
|
u32 len;
|
||||||
|
int rc;
|
||||||
|
if ((rc = selinux_ctxid_to_string(sid, &ctx, &len)))
|
||||||
|
return rc;
|
||||||
|
else
|
||||||
|
audit_log(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE,
|
||||||
|
"audit_rate_limit=%d old=%d by auid=%u subj=%s",
|
||||||
|
limit, old, loginuid, ctx);
|
||||||
|
kfree(ctx);
|
||||||
|
} else
|
||||||
audit_log(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE,
|
audit_log(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE,
|
||||||
"audit_rate_limit=%d old=%d by auid=%u",
|
"audit_rate_limit=%d old=%d by auid=%u",
|
||||||
audit_rate_limit, old, loginuid);
|
limit, old, loginuid);
|
||||||
|
audit_rate_limit = limit;
|
||||||
return old;
|
return old;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int audit_set_backlog_limit(int limit, uid_t loginuid)
|
static int audit_set_backlog_limit(int limit, uid_t loginuid, u32 sid)
|
||||||
{
|
{
|
||||||
int old = audit_backlog_limit;
|
int old = audit_backlog_limit;
|
||||||
audit_backlog_limit = limit;
|
|
||||||
|
if (sid) {
|
||||||
|
char *ctx = NULL;
|
||||||
|
u32 len;
|
||||||
|
int rc;
|
||||||
|
if ((rc = selinux_ctxid_to_string(sid, &ctx, &len)))
|
||||||
|
return rc;
|
||||||
|
else
|
||||||
|
audit_log(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE,
|
||||||
|
"audit_backlog_limit=%d old=%d by auid=%u subj=%s",
|
||||||
|
limit, old, loginuid, ctx);
|
||||||
|
kfree(ctx);
|
||||||
|
} else
|
||||||
audit_log(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE,
|
audit_log(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE,
|
||||||
"audit_backlog_limit=%d old=%d by auid=%u",
|
"audit_backlog_limit=%d old=%d by auid=%u",
|
||||||
audit_backlog_limit, old, loginuid);
|
limit, old, loginuid);
|
||||||
|
audit_backlog_limit = limit;
|
||||||
return old;
|
return old;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int audit_set_enabled(int state, uid_t loginuid)
|
static int audit_set_enabled(int state, uid_t loginuid, u32 sid)
|
||||||
{
|
{
|
||||||
int old = audit_enabled;
|
int old = audit_enabled;
|
||||||
|
|
||||||
if (state != 0 && state != 1)
|
if (state != 0 && state != 1)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
audit_enabled = state;
|
|
||||||
|
if (sid) {
|
||||||
|
char *ctx = NULL;
|
||||||
|
u32 len;
|
||||||
|
int rc;
|
||||||
|
if ((rc = selinux_ctxid_to_string(sid, &ctx, &len)))
|
||||||
|
return rc;
|
||||||
|
else
|
||||||
|
audit_log(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE,
|
||||||
|
"audit_enabled=%d old=%d by auid=%u subj=%s",
|
||||||
|
state, old, loginuid, ctx);
|
||||||
|
kfree(ctx);
|
||||||
|
} else
|
||||||
audit_log(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE,
|
audit_log(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE,
|
||||||
"audit_enabled=%d old=%d by auid=%u",
|
"audit_enabled=%d old=%d by auid=%u",
|
||||||
audit_enabled, old, loginuid);
|
state, old, loginuid);
|
||||||
|
audit_enabled = state;
|
||||||
return old;
|
return old;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int audit_set_failure(int state, uid_t loginuid)
|
static int audit_set_failure(int state, uid_t loginuid, u32 sid)
|
||||||
{
|
{
|
||||||
int old = audit_failure;
|
int old = audit_failure;
|
||||||
|
|
||||||
if (state != AUDIT_FAIL_SILENT
|
if (state != AUDIT_FAIL_SILENT
|
||||||
&& state != AUDIT_FAIL_PRINTK
|
&& state != AUDIT_FAIL_PRINTK
|
||||||
&& state != AUDIT_FAIL_PANIC)
|
&& state != AUDIT_FAIL_PANIC)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
audit_failure = state;
|
|
||||||
|
if (sid) {
|
||||||
|
char *ctx = NULL;
|
||||||
|
u32 len;
|
||||||
|
int rc;
|
||||||
|
if ((rc = selinux_ctxid_to_string(sid, &ctx, &len)))
|
||||||
|
return rc;
|
||||||
|
else
|
||||||
|
audit_log(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE,
|
||||||
|
"audit_failure=%d old=%d by auid=%u subj=%s",
|
||||||
|
state, old, loginuid, ctx);
|
||||||
|
kfree(ctx);
|
||||||
|
} else
|
||||||
audit_log(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE,
|
audit_log(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE,
|
||||||
"audit_failure=%d old=%d by auid=%u",
|
"audit_failure=%d old=%d by auid=%u",
|
||||||
audit_failure, old, loginuid);
|
state, old, loginuid);
|
||||||
|
audit_failure = state;
|
||||||
return old;
|
return old;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -387,7 +444,7 @@ static int audit_netlink_ok(kernel_cap_t eff_cap, u16 msg_type)
|
||||||
|
|
||||||
static int audit_receive_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
|
static int audit_receive_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
|
||||||
{
|
{
|
||||||
u32 uid, pid, seq;
|
u32 uid, pid, seq, sid;
|
||||||
void *data;
|
void *data;
|
||||||
struct audit_status *status_get, status_set;
|
struct audit_status *status_get, status_set;
|
||||||
int err;
|
int err;
|
||||||
|
@ -413,6 +470,7 @@ static int audit_receive_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
|
||||||
pid = NETLINK_CREDS(skb)->pid;
|
pid = NETLINK_CREDS(skb)->pid;
|
||||||
uid = NETLINK_CREDS(skb)->uid;
|
uid = NETLINK_CREDS(skb)->uid;
|
||||||
loginuid = NETLINK_CB(skb).loginuid;
|
loginuid = NETLINK_CB(skb).loginuid;
|
||||||
|
sid = NETLINK_CB(skb).sid;
|
||||||
seq = nlh->nlmsg_seq;
|
seq = nlh->nlmsg_seq;
|
||||||
data = NLMSG_DATA(nlh);
|
data = NLMSG_DATA(nlh);
|
||||||
|
|
||||||
|
@ -433,25 +491,43 @@ static int audit_receive_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
status_get = (struct audit_status *)data;
|
status_get = (struct audit_status *)data;
|
||||||
if (status_get->mask & AUDIT_STATUS_ENABLED) {
|
if (status_get->mask & AUDIT_STATUS_ENABLED) {
|
||||||
err = audit_set_enabled(status_get->enabled, loginuid);
|
err = audit_set_enabled(status_get->enabled,
|
||||||
|
loginuid, sid);
|
||||||
if (err < 0) return err;
|
if (err < 0) return err;
|
||||||
}
|
}
|
||||||
if (status_get->mask & AUDIT_STATUS_FAILURE) {
|
if (status_get->mask & AUDIT_STATUS_FAILURE) {
|
||||||
err = audit_set_failure(status_get->failure, loginuid);
|
err = audit_set_failure(status_get->failure,
|
||||||
|
loginuid, sid);
|
||||||
if (err < 0) return err;
|
if (err < 0) return err;
|
||||||
}
|
}
|
||||||
if (status_get->mask & AUDIT_STATUS_PID) {
|
if (status_get->mask & AUDIT_STATUS_PID) {
|
||||||
int old = audit_pid;
|
int old = audit_pid;
|
||||||
audit_pid = status_get->pid;
|
if (sid) {
|
||||||
|
char *ctx = NULL;
|
||||||
|
u32 len;
|
||||||
|
int rc;
|
||||||
|
if ((rc = selinux_ctxid_to_string(
|
||||||
|
sid, &ctx, &len)))
|
||||||
|
return rc;
|
||||||
|
else
|
||||||
|
audit_log(NULL, GFP_KERNEL,
|
||||||
|
AUDIT_CONFIG_CHANGE,
|
||||||
|
"audit_pid=%d old=%d by auid=%u subj=%s",
|
||||||
|
status_get->pid, old,
|
||||||
|
loginuid, ctx);
|
||||||
|
kfree(ctx);
|
||||||
|
} else
|
||||||
audit_log(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE,
|
audit_log(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE,
|
||||||
"audit_pid=%d old=%d by auid=%u",
|
"audit_pid=%d old=%d by auid=%u",
|
||||||
audit_pid, old, loginuid);
|
status_get->pid, old, loginuid);
|
||||||
|
audit_pid = status_get->pid;
|
||||||
}
|
}
|
||||||
if (status_get->mask & AUDIT_STATUS_RATE_LIMIT)
|
if (status_get->mask & AUDIT_STATUS_RATE_LIMIT)
|
||||||
audit_set_rate_limit(status_get->rate_limit, loginuid);
|
audit_set_rate_limit(status_get->rate_limit,
|
||||||
|
loginuid, sid);
|
||||||
if (status_get->mask & AUDIT_STATUS_BACKLOG_LIMIT)
|
if (status_get->mask & AUDIT_STATUS_BACKLOG_LIMIT)
|
||||||
audit_set_backlog_limit(status_get->backlog_limit,
|
audit_set_backlog_limit(status_get->backlog_limit,
|
||||||
loginuid);
|
loginuid, sid);
|
||||||
break;
|
break;
|
||||||
case AUDIT_USER:
|
case AUDIT_USER:
|
||||||
case AUDIT_FIRST_USER_MSG...AUDIT_LAST_USER_MSG:
|
case AUDIT_FIRST_USER_MSG...AUDIT_LAST_USER_MSG:
|
||||||
|
@ -465,8 +541,23 @@ static int audit_receive_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
|
||||||
ab = audit_log_start(NULL, GFP_KERNEL, msg_type);
|
ab = audit_log_start(NULL, GFP_KERNEL, msg_type);
|
||||||
if (ab) {
|
if (ab) {
|
||||||
audit_log_format(ab,
|
audit_log_format(ab,
|
||||||
"user pid=%d uid=%u auid=%u msg='%.1024s'",
|
"user pid=%d uid=%u auid=%u",
|
||||||
pid, uid, loginuid, (char *)data);
|
pid, uid, loginuid);
|
||||||
|
if (sid) {
|
||||||
|
char *ctx = NULL;
|
||||||
|
u32 len;
|
||||||
|
if (selinux_ctxid_to_string(
|
||||||
|
sid, &ctx, &len)) {
|
||||||
|
audit_log_format(ab,
|
||||||
|
" ssid=%u", sid);
|
||||||
|
/* Maybe call audit_panic? */
|
||||||
|
} else
|
||||||
|
audit_log_format(ab,
|
||||||
|
" subj=%s", ctx);
|
||||||
|
kfree(ctx);
|
||||||
|
}
|
||||||
|
audit_log_format(ab, " msg='%.1024s'",
|
||||||
|
(char *)data);
|
||||||
audit_set_pid(ab, pid);
|
audit_set_pid(ab, pid);
|
||||||
audit_log_end(ab);
|
audit_log_end(ab);
|
||||||
}
|
}
|
||||||
|
@ -480,7 +571,7 @@ static int audit_receive_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
|
||||||
case AUDIT_LIST:
|
case AUDIT_LIST:
|
||||||
err = audit_receive_filter(nlh->nlmsg_type, NETLINK_CB(skb).pid,
|
err = audit_receive_filter(nlh->nlmsg_type, NETLINK_CB(skb).pid,
|
||||||
uid, seq, data, nlmsg_len(nlh),
|
uid, seq, data, nlmsg_len(nlh),
|
||||||
loginuid);
|
loginuid, sid);
|
||||||
break;
|
break;
|
||||||
case AUDIT_ADD_RULE:
|
case AUDIT_ADD_RULE:
|
||||||
case AUDIT_DEL_RULE:
|
case AUDIT_DEL_RULE:
|
||||||
|
@ -490,7 +581,7 @@ static int audit_receive_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
|
||||||
case AUDIT_LIST_RULES:
|
case AUDIT_LIST_RULES:
|
||||||
err = audit_receive_filter(nlh->nlmsg_type, NETLINK_CB(skb).pid,
|
err = audit_receive_filter(nlh->nlmsg_type, NETLINK_CB(skb).pid,
|
||||||
uid, seq, data, nlmsg_len(nlh),
|
uid, seq, data, nlmsg_len(nlh),
|
||||||
loginuid);
|
loginuid, sid);
|
||||||
break;
|
break;
|
||||||
case AUDIT_SIGNAL_INFO:
|
case AUDIT_SIGNAL_INFO:
|
||||||
sig_data.uid = audit_sig_uid;
|
sig_data.uid = audit_sig_uid;
|
||||||
|
@ -564,6 +655,11 @@ static int __init audit_init(void)
|
||||||
skb_queue_head_init(&audit_skb_queue);
|
skb_queue_head_init(&audit_skb_queue);
|
||||||
audit_initialized = 1;
|
audit_initialized = 1;
|
||||||
audit_enabled = audit_default;
|
audit_enabled = audit_default;
|
||||||
|
|
||||||
|
/* Register the callback with selinux. This callback will be invoked
|
||||||
|
* when a new policy is loaded. */
|
||||||
|
selinux_audit_set_callback(&selinux_audit_rule_update);
|
||||||
|
|
||||||
audit_log(NULL, GFP_KERNEL, AUDIT_KERNEL, "initialized");
|
audit_log(NULL, GFP_KERNEL, AUDIT_KERNEL, "initialized");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -57,6 +57,8 @@ struct audit_field {
|
||||||
u32 type;
|
u32 type;
|
||||||
u32 val;
|
u32 val;
|
||||||
u32 op;
|
u32 op;
|
||||||
|
char *se_str;
|
||||||
|
struct selinux_audit_rule *se_rule;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct audit_krule {
|
struct audit_krule {
|
||||||
|
@ -86,3 +88,5 @@ extern void audit_send_reply(int pid, int seq, int type,
|
||||||
extern void audit_log_lost(const char *message);
|
extern void audit_log_lost(const char *message);
|
||||||
extern void audit_panic(const char *message);
|
extern void audit_panic(const char *message);
|
||||||
extern struct mutex audit_netlink_mutex;
|
extern struct mutex audit_netlink_mutex;
|
||||||
|
|
||||||
|
extern int selinux_audit_rule_update(void);
|
||||||
|
|
|
@ -23,6 +23,7 @@
|
||||||
#include <linux/audit.h>
|
#include <linux/audit.h>
|
||||||
#include <linux/kthread.h>
|
#include <linux/kthread.h>
|
||||||
#include <linux/netlink.h>
|
#include <linux/netlink.h>
|
||||||
|
#include <linux/selinux.h>
|
||||||
#include "audit.h"
|
#include "audit.h"
|
||||||
|
|
||||||
/* There are three lists of rules -- one to search at task creation
|
/* There are three lists of rules -- one to search at task creation
|
||||||
|
@ -42,6 +43,13 @@ struct list_head audit_filter_list[AUDIT_NR_FILTERS] = {
|
||||||
|
|
||||||
static inline void audit_free_rule(struct audit_entry *e)
|
static inline void audit_free_rule(struct audit_entry *e)
|
||||||
{
|
{
|
||||||
|
int i;
|
||||||
|
if (e->rule.fields)
|
||||||
|
for (i = 0; i < e->rule.field_count; i++) {
|
||||||
|
struct audit_field *f = &e->rule.fields[i];
|
||||||
|
kfree(f->se_str);
|
||||||
|
selinux_audit_rule_free(f->se_rule);
|
||||||
|
}
|
||||||
kfree(e->rule.fields);
|
kfree(e->rule.fields);
|
||||||
kfree(e);
|
kfree(e);
|
||||||
}
|
}
|
||||||
|
@ -52,9 +60,29 @@ static inline void audit_free_rule_rcu(struct rcu_head *head)
|
||||||
audit_free_rule(e);
|
audit_free_rule(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Initialize an audit filterlist entry. */
|
||||||
|
static inline struct audit_entry *audit_init_entry(u32 field_count)
|
||||||
|
{
|
||||||
|
struct audit_entry *entry;
|
||||||
|
struct audit_field *fields;
|
||||||
|
|
||||||
|
entry = kzalloc(sizeof(*entry), GFP_KERNEL);
|
||||||
|
if (unlikely(!entry))
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
fields = kzalloc(sizeof(*fields) * field_count, GFP_KERNEL);
|
||||||
|
if (unlikely(!fields)) {
|
||||||
|
kfree(entry);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
entry->rule.fields = fields;
|
||||||
|
|
||||||
|
return entry;
|
||||||
|
}
|
||||||
|
|
||||||
/* Unpack a filter field's string representation from user-space
|
/* Unpack a filter field's string representation from user-space
|
||||||
* buffer. */
|
* buffer. */
|
||||||
static __attribute__((unused)) char *audit_unpack_string(void **bufp, size_t *remain, size_t len)
|
static char *audit_unpack_string(void **bufp, size_t *remain, size_t len)
|
||||||
{
|
{
|
||||||
char *str;
|
char *str;
|
||||||
|
|
||||||
|
@ -84,7 +112,6 @@ static inline struct audit_entry *audit_to_entry_common(struct audit_rule *rule)
|
||||||
{
|
{
|
||||||
unsigned listnr;
|
unsigned listnr;
|
||||||
struct audit_entry *entry;
|
struct audit_entry *entry;
|
||||||
struct audit_field *fields;
|
|
||||||
int i, err;
|
int i, err;
|
||||||
|
|
||||||
err = -EINVAL;
|
err = -EINVAL;
|
||||||
|
@ -108,23 +135,14 @@ static inline struct audit_entry *audit_to_entry_common(struct audit_rule *rule)
|
||||||
goto exit_err;
|
goto exit_err;
|
||||||
|
|
||||||
err = -ENOMEM;
|
err = -ENOMEM;
|
||||||
entry = kmalloc(sizeof(*entry), GFP_KERNEL);
|
entry = audit_init_entry(rule->field_count);
|
||||||
if (unlikely(!entry))
|
if (!entry)
|
||||||
goto exit_err;
|
goto exit_err;
|
||||||
fields = kmalloc(sizeof(*fields) * rule->field_count, GFP_KERNEL);
|
|
||||||
if (unlikely(!fields)) {
|
|
||||||
kfree(entry);
|
|
||||||
goto exit_err;
|
|
||||||
}
|
|
||||||
|
|
||||||
memset(&entry->rule, 0, sizeof(struct audit_krule));
|
|
||||||
memset(fields, 0, sizeof(struct audit_field));
|
|
||||||
|
|
||||||
entry->rule.flags = rule->flags & AUDIT_FILTER_PREPEND;
|
entry->rule.flags = rule->flags & AUDIT_FILTER_PREPEND;
|
||||||
entry->rule.listnr = listnr;
|
entry->rule.listnr = listnr;
|
||||||
entry->rule.action = rule->action;
|
entry->rule.action = rule->action;
|
||||||
entry->rule.field_count = rule->field_count;
|
entry->rule.field_count = rule->field_count;
|
||||||
entry->rule.fields = fields;
|
|
||||||
|
|
||||||
for (i = 0; i < AUDIT_BITMASK_SIZE; i++)
|
for (i = 0; i < AUDIT_BITMASK_SIZE; i++)
|
||||||
entry->rule.mask[i] = rule->mask[i];
|
entry->rule.mask[i] = rule->mask[i];
|
||||||
|
@ -150,15 +168,20 @@ static struct audit_entry *audit_rule_to_entry(struct audit_rule *rule)
|
||||||
for (i = 0; i < rule->field_count; i++) {
|
for (i = 0; i < rule->field_count; i++) {
|
||||||
struct audit_field *f = &entry->rule.fields[i];
|
struct audit_field *f = &entry->rule.fields[i];
|
||||||
|
|
||||||
if (rule->fields[i] & AUDIT_UNUSED_BITS) {
|
|
||||||
err = -EINVAL;
|
|
||||||
goto exit_free;
|
|
||||||
}
|
|
||||||
|
|
||||||
f->op = rule->fields[i] & (AUDIT_NEGATE|AUDIT_OPERATORS);
|
f->op = rule->fields[i] & (AUDIT_NEGATE|AUDIT_OPERATORS);
|
||||||
f->type = rule->fields[i] & ~(AUDIT_NEGATE|AUDIT_OPERATORS);
|
f->type = rule->fields[i] & ~(AUDIT_NEGATE|AUDIT_OPERATORS);
|
||||||
f->val = rule->values[i];
|
f->val = rule->values[i];
|
||||||
|
|
||||||
|
if (f->type & AUDIT_UNUSED_BITS ||
|
||||||
|
f->type == AUDIT_SE_USER ||
|
||||||
|
f->type == AUDIT_SE_ROLE ||
|
||||||
|
f->type == AUDIT_SE_TYPE ||
|
||||||
|
f->type == AUDIT_SE_SEN ||
|
||||||
|
f->type == AUDIT_SE_CLR) {
|
||||||
|
err = -EINVAL;
|
||||||
|
goto exit_free;
|
||||||
|
}
|
||||||
|
|
||||||
entry->rule.vers_ops = (f->op & AUDIT_OPERATORS) ? 2 : 1;
|
entry->rule.vers_ops = (f->op & AUDIT_OPERATORS) ? 2 : 1;
|
||||||
|
|
||||||
/* Support for legacy operators where
|
/* Support for legacy operators where
|
||||||
|
@ -188,8 +211,9 @@ static struct audit_entry *audit_data_to_entry(struct audit_rule_data *data,
|
||||||
int err = 0;
|
int err = 0;
|
||||||
struct audit_entry *entry;
|
struct audit_entry *entry;
|
||||||
void *bufp;
|
void *bufp;
|
||||||
/* size_t remain = datasz - sizeof(struct audit_rule_data); */
|
size_t remain = datasz - sizeof(struct audit_rule_data);
|
||||||
int i;
|
int i;
|
||||||
|
char *str;
|
||||||
|
|
||||||
entry = audit_to_entry_common((struct audit_rule *)data);
|
entry = audit_to_entry_common((struct audit_rule *)data);
|
||||||
if (IS_ERR(entry))
|
if (IS_ERR(entry))
|
||||||
|
@ -207,10 +231,35 @@ static struct audit_entry *audit_data_to_entry(struct audit_rule_data *data,
|
||||||
|
|
||||||
f->op = data->fieldflags[i] & AUDIT_OPERATORS;
|
f->op = data->fieldflags[i] & AUDIT_OPERATORS;
|
||||||
f->type = data->fields[i];
|
f->type = data->fields[i];
|
||||||
switch(f->type) {
|
|
||||||
/* call type-specific conversion routines here */
|
|
||||||
default:
|
|
||||||
f->val = data->values[i];
|
f->val = data->values[i];
|
||||||
|
f->se_str = NULL;
|
||||||
|
f->se_rule = NULL;
|
||||||
|
switch(f->type) {
|
||||||
|
case AUDIT_SE_USER:
|
||||||
|
case AUDIT_SE_ROLE:
|
||||||
|
case AUDIT_SE_TYPE:
|
||||||
|
case AUDIT_SE_SEN:
|
||||||
|
case AUDIT_SE_CLR:
|
||||||
|
str = audit_unpack_string(&bufp, &remain, f->val);
|
||||||
|
if (IS_ERR(str))
|
||||||
|
goto exit_free;
|
||||||
|
entry->rule.buflen += f->val;
|
||||||
|
|
||||||
|
err = selinux_audit_rule_init(f->type, f->op, str,
|
||||||
|
&f->se_rule);
|
||||||
|
/* Keep currently invalid fields around in case they
|
||||||
|
* become valid after a policy reload. */
|
||||||
|
if (err == -EINVAL) {
|
||||||
|
printk(KERN_WARNING "audit rule for selinux "
|
||||||
|
"\'%s\' is invalid\n", str);
|
||||||
|
err = 0;
|
||||||
|
}
|
||||||
|
if (err) {
|
||||||
|
kfree(str);
|
||||||
|
goto exit_free;
|
||||||
|
} else
|
||||||
|
f->se_str = str;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -286,7 +335,14 @@ static struct audit_rule_data *audit_krule_to_data(struct audit_krule *krule)
|
||||||
data->fields[i] = f->type;
|
data->fields[i] = f->type;
|
||||||
data->fieldflags[i] = f->op;
|
data->fieldflags[i] = f->op;
|
||||||
switch(f->type) {
|
switch(f->type) {
|
||||||
/* call type-specific conversion routines here */
|
case AUDIT_SE_USER:
|
||||||
|
case AUDIT_SE_ROLE:
|
||||||
|
case AUDIT_SE_TYPE:
|
||||||
|
case AUDIT_SE_SEN:
|
||||||
|
case AUDIT_SE_CLR:
|
||||||
|
data->buflen += data->values[i] =
|
||||||
|
audit_pack_string(&bufp, f->se_str);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
data->values[i] = f->val;
|
data->values[i] = f->val;
|
||||||
}
|
}
|
||||||
|
@ -314,7 +370,14 @@ static int audit_compare_rule(struct audit_krule *a, struct audit_krule *b)
|
||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
switch(a->fields[i].type) {
|
switch(a->fields[i].type) {
|
||||||
/* call type-specific comparison routines here */
|
case AUDIT_SE_USER:
|
||||||
|
case AUDIT_SE_ROLE:
|
||||||
|
case AUDIT_SE_TYPE:
|
||||||
|
case AUDIT_SE_SEN:
|
||||||
|
case AUDIT_SE_CLR:
|
||||||
|
if (strcmp(a->fields[i].se_str, b->fields[i].se_str))
|
||||||
|
return 1;
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
if (a->fields[i].val != b->fields[i].val)
|
if (a->fields[i].val != b->fields[i].val)
|
||||||
return 1;
|
return 1;
|
||||||
|
@ -328,6 +391,81 @@ static int audit_compare_rule(struct audit_krule *a, struct audit_krule *b)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Duplicate selinux field information. The se_rule is opaque, so must be
|
||||||
|
* re-initialized. */
|
||||||
|
static inline int audit_dupe_selinux_field(struct audit_field *df,
|
||||||
|
struct audit_field *sf)
|
||||||
|
{
|
||||||
|
int ret = 0;
|
||||||
|
char *se_str;
|
||||||
|
|
||||||
|
/* our own copy of se_str */
|
||||||
|
se_str = kstrdup(sf->se_str, GFP_KERNEL);
|
||||||
|
if (unlikely(IS_ERR(se_str)))
|
||||||
|
return -ENOMEM;
|
||||||
|
df->se_str = se_str;
|
||||||
|
|
||||||
|
/* our own (refreshed) copy of se_rule */
|
||||||
|
ret = selinux_audit_rule_init(df->type, df->op, df->se_str,
|
||||||
|
&df->se_rule);
|
||||||
|
/* Keep currently invalid fields around in case they
|
||||||
|
* become valid after a policy reload. */
|
||||||
|
if (ret == -EINVAL) {
|
||||||
|
printk(KERN_WARNING "audit rule for selinux \'%s\' is "
|
||||||
|
"invalid\n", df->se_str);
|
||||||
|
ret = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Duplicate an audit rule. This will be a deep copy with the exception
|
||||||
|
* of the watch - that pointer is carried over. The selinux specific fields
|
||||||
|
* will be updated in the copy. The point is to be able to replace the old
|
||||||
|
* rule with the new rule in the filterlist, then free the old rule. */
|
||||||
|
static struct audit_entry *audit_dupe_rule(struct audit_krule *old)
|
||||||
|
{
|
||||||
|
u32 fcount = old->field_count;
|
||||||
|
struct audit_entry *entry;
|
||||||
|
struct audit_krule *new;
|
||||||
|
int i, err = 0;
|
||||||
|
|
||||||
|
entry = audit_init_entry(fcount);
|
||||||
|
if (unlikely(!entry))
|
||||||
|
return ERR_PTR(-ENOMEM);
|
||||||
|
|
||||||
|
new = &entry->rule;
|
||||||
|
new->vers_ops = old->vers_ops;
|
||||||
|
new->flags = old->flags;
|
||||||
|
new->listnr = old->listnr;
|
||||||
|
new->action = old->action;
|
||||||
|
for (i = 0; i < AUDIT_BITMASK_SIZE; i++)
|
||||||
|
new->mask[i] = old->mask[i];
|
||||||
|
new->buflen = old->buflen;
|
||||||
|
new->field_count = old->field_count;
|
||||||
|
memcpy(new->fields, old->fields, sizeof(struct audit_field) * fcount);
|
||||||
|
|
||||||
|
/* deep copy this information, updating the se_rule fields, because
|
||||||
|
* the originals will all be freed when the old rule is freed. */
|
||||||
|
for (i = 0; i < fcount; i++) {
|
||||||
|
switch (new->fields[i].type) {
|
||||||
|
case AUDIT_SE_USER:
|
||||||
|
case AUDIT_SE_ROLE:
|
||||||
|
case AUDIT_SE_TYPE:
|
||||||
|
case AUDIT_SE_SEN:
|
||||||
|
case AUDIT_SE_CLR:
|
||||||
|
err = audit_dupe_selinux_field(&new->fields[i],
|
||||||
|
&old->fields[i]);
|
||||||
|
}
|
||||||
|
if (err) {
|
||||||
|
audit_free_rule(entry);
|
||||||
|
return ERR_PTR(err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return entry;
|
||||||
|
}
|
||||||
|
|
||||||
/* Add rule to given filterlist if not a duplicate. Protected by
|
/* Add rule to given filterlist if not a duplicate. Protected by
|
||||||
* audit_netlink_mutex. */
|
* audit_netlink_mutex. */
|
||||||
static inline int audit_add_rule(struct audit_entry *entry,
|
static inline int audit_add_rule(struct audit_entry *entry,
|
||||||
|
@ -448,9 +586,10 @@ static int audit_list_rules(void *_dest)
|
||||||
* @data: payload data
|
* @data: payload data
|
||||||
* @datasz: size of payload data
|
* @datasz: size of payload data
|
||||||
* @loginuid: loginuid of sender
|
* @loginuid: loginuid of sender
|
||||||
|
* @sid: SE Linux Security ID of sender
|
||||||
*/
|
*/
|
||||||
int audit_receive_filter(int type, int pid, int uid, int seq, void *data,
|
int audit_receive_filter(int type, int pid, int uid, int seq, void *data,
|
||||||
size_t datasz, uid_t loginuid)
|
size_t datasz, uid_t loginuid, u32 sid)
|
||||||
{
|
{
|
||||||
struct task_struct *tsk;
|
struct task_struct *tsk;
|
||||||
int *dest;
|
int *dest;
|
||||||
|
@ -493,8 +632,22 @@ int audit_receive_filter(int type, int pid, int uid, int seq, void *data,
|
||||||
|
|
||||||
err = audit_add_rule(entry,
|
err = audit_add_rule(entry,
|
||||||
&audit_filter_list[entry->rule.listnr]);
|
&audit_filter_list[entry->rule.listnr]);
|
||||||
|
if (sid) {
|
||||||
|
char *ctx = NULL;
|
||||||
|
u32 len;
|
||||||
|
if (selinux_ctxid_to_string(sid, &ctx, &len)) {
|
||||||
|
/* Maybe call audit_panic? */
|
||||||
audit_log(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE,
|
audit_log(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE,
|
||||||
"auid=%u add rule to list=%d res=%d\n",
|
"auid=%u ssid=%u add rule to list=%d res=%d",
|
||||||
|
loginuid, sid, entry->rule.listnr, !err);
|
||||||
|
} else
|
||||||
|
audit_log(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE,
|
||||||
|
"auid=%u subj=%s add rule to list=%d res=%d",
|
||||||
|
loginuid, ctx, entry->rule.listnr, !err);
|
||||||
|
kfree(ctx);
|
||||||
|
} else
|
||||||
|
audit_log(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE,
|
||||||
|
"auid=%u add rule to list=%d res=%d",
|
||||||
loginuid, entry->rule.listnr, !err);
|
loginuid, entry->rule.listnr, !err);
|
||||||
|
|
||||||
if (err)
|
if (err)
|
||||||
|
@ -511,8 +664,23 @@ int audit_receive_filter(int type, int pid, int uid, int seq, void *data,
|
||||||
|
|
||||||
err = audit_del_rule(entry,
|
err = audit_del_rule(entry,
|
||||||
&audit_filter_list[entry->rule.listnr]);
|
&audit_filter_list[entry->rule.listnr]);
|
||||||
|
|
||||||
|
if (sid) {
|
||||||
|
char *ctx = NULL;
|
||||||
|
u32 len;
|
||||||
|
if (selinux_ctxid_to_string(sid, &ctx, &len)) {
|
||||||
|
/* Maybe call audit_panic? */
|
||||||
audit_log(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE,
|
audit_log(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE,
|
||||||
"auid=%u remove rule from list=%d res=%d\n",
|
"auid=%u ssid=%u remove rule from list=%d res=%d",
|
||||||
|
loginuid, sid, entry->rule.listnr, !err);
|
||||||
|
} else
|
||||||
|
audit_log(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE,
|
||||||
|
"auid=%u subj=%s remove rule from list=%d res=%d",
|
||||||
|
loginuid, ctx, entry->rule.listnr, !err);
|
||||||
|
kfree(ctx);
|
||||||
|
} else
|
||||||
|
audit_log(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE,
|
||||||
|
"auid=%u remove rule from list=%d res=%d",
|
||||||
loginuid, entry->rule.listnr, !err);
|
loginuid, entry->rule.listnr, !err);
|
||||||
|
|
||||||
audit_free_rule(entry);
|
audit_free_rule(entry);
|
||||||
|
@ -628,3 +796,62 @@ unlock_and_return:
|
||||||
rcu_read_unlock();
|
rcu_read_unlock();
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Check to see if the rule contains any selinux fields. Returns 1 if there
|
||||||
|
are selinux fields specified in the rule, 0 otherwise. */
|
||||||
|
static inline int audit_rule_has_selinux(struct audit_krule *rule)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < rule->field_count; i++) {
|
||||||
|
struct audit_field *f = &rule->fields[i];
|
||||||
|
switch (f->type) {
|
||||||
|
case AUDIT_SE_USER:
|
||||||
|
case AUDIT_SE_ROLE:
|
||||||
|
case AUDIT_SE_TYPE:
|
||||||
|
case AUDIT_SE_SEN:
|
||||||
|
case AUDIT_SE_CLR:
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* This function will re-initialize the se_rule field of all applicable rules.
|
||||||
|
* It will traverse the filter lists serarching for rules that contain selinux
|
||||||
|
* specific filter fields. When such a rule is found, it is copied, the
|
||||||
|
* selinux field is re-initialized, and the old rule is replaced with the
|
||||||
|
* updated rule. */
|
||||||
|
int selinux_audit_rule_update(void)
|
||||||
|
{
|
||||||
|
struct audit_entry *entry, *n, *nentry;
|
||||||
|
int i, err = 0;
|
||||||
|
|
||||||
|
/* audit_netlink_mutex synchronizes the writers */
|
||||||
|
mutex_lock(&audit_netlink_mutex);
|
||||||
|
|
||||||
|
for (i = 0; i < AUDIT_NR_FILTERS; i++) {
|
||||||
|
list_for_each_entry_safe(entry, n, &audit_filter_list[i], list) {
|
||||||
|
if (!audit_rule_has_selinux(&entry->rule))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
nentry = audit_dupe_rule(&entry->rule);
|
||||||
|
if (unlikely(IS_ERR(nentry))) {
|
||||||
|
/* save the first error encountered for the
|
||||||
|
* return value */
|
||||||
|
if (!err)
|
||||||
|
err = PTR_ERR(nentry);
|
||||||
|
audit_panic("error updating selinux filters");
|
||||||
|
list_del_rcu(&entry->list);
|
||||||
|
} else {
|
||||||
|
list_replace_rcu(&entry->list, &nentry->list);
|
||||||
|
}
|
||||||
|
call_rcu(&entry->rcu, audit_free_rule_rcu);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mutex_unlock(&audit_netlink_mutex);
|
||||||
|
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
247
kernel/auditsc.c
247
kernel/auditsc.c
|
@ -58,6 +58,7 @@
|
||||||
#include <linux/security.h>
|
#include <linux/security.h>
|
||||||
#include <linux/list.h>
|
#include <linux/list.h>
|
||||||
#include <linux/tty.h>
|
#include <linux/tty.h>
|
||||||
|
#include <linux/selinux.h>
|
||||||
|
|
||||||
#include "audit.h"
|
#include "audit.h"
|
||||||
|
|
||||||
|
@ -89,7 +90,7 @@ struct audit_names {
|
||||||
uid_t uid;
|
uid_t uid;
|
||||||
gid_t gid;
|
gid_t gid;
|
||||||
dev_t rdev;
|
dev_t rdev;
|
||||||
char *ctx;
|
u32 osid;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct audit_aux_data {
|
struct audit_aux_data {
|
||||||
|
@ -106,7 +107,7 @@ struct audit_aux_data_ipcctl {
|
||||||
uid_t uid;
|
uid_t uid;
|
||||||
gid_t gid;
|
gid_t gid;
|
||||||
mode_t mode;
|
mode_t mode;
|
||||||
char *ctx;
|
u32 osid;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct audit_aux_data_socketcall {
|
struct audit_aux_data_socketcall {
|
||||||
|
@ -167,7 +168,8 @@ static int audit_filter_rules(struct task_struct *tsk,
|
||||||
struct audit_context *ctx,
|
struct audit_context *ctx,
|
||||||
enum audit_state *state)
|
enum audit_state *state)
|
||||||
{
|
{
|
||||||
int i, j;
|
int i, j, need_sid = 1;
|
||||||
|
u32 sid;
|
||||||
|
|
||||||
for (i = 0; i < rule->field_count; i++) {
|
for (i = 0; i < rule->field_count; i++) {
|
||||||
struct audit_field *f = &rule->fields[i];
|
struct audit_field *f = &rule->fields[i];
|
||||||
|
@ -257,6 +259,27 @@ static int audit_filter_rules(struct task_struct *tsk,
|
||||||
if (ctx)
|
if (ctx)
|
||||||
result = audit_comparator(ctx->loginuid, f->op, f->val);
|
result = audit_comparator(ctx->loginuid, f->op, f->val);
|
||||||
break;
|
break;
|
||||||
|
case AUDIT_SE_USER:
|
||||||
|
case AUDIT_SE_ROLE:
|
||||||
|
case AUDIT_SE_TYPE:
|
||||||
|
case AUDIT_SE_SEN:
|
||||||
|
case AUDIT_SE_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 */
|
||||||
|
if (f->se_rule) {
|
||||||
|
if (need_sid) {
|
||||||
|
selinux_task_ctxid(tsk, &sid);
|
||||||
|
need_sid = 0;
|
||||||
|
}
|
||||||
|
result = selinux_audit_rule_match(sid, f->type,
|
||||||
|
f->op,
|
||||||
|
f->se_rule,
|
||||||
|
ctx);
|
||||||
|
}
|
||||||
|
break;
|
||||||
case AUDIT_ARG0:
|
case AUDIT_ARG0:
|
||||||
case AUDIT_ARG1:
|
case AUDIT_ARG1:
|
||||||
case AUDIT_ARG2:
|
case AUDIT_ARG2:
|
||||||
|
@ -329,7 +352,6 @@ static enum audit_state audit_filter_syscall(struct task_struct *tsk,
|
||||||
return AUDIT_BUILD_CONTEXT;
|
return AUDIT_BUILD_CONTEXT;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* This should be called with task_lock() held. */
|
|
||||||
static inline struct audit_context *audit_get_context(struct task_struct *tsk,
|
static inline struct audit_context *audit_get_context(struct task_struct *tsk,
|
||||||
int return_valid,
|
int return_valid,
|
||||||
int return_code)
|
int return_code)
|
||||||
|
@ -391,9 +413,6 @@ static inline void audit_free_names(struct audit_context *context)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
for (i = 0; i < context->name_count; i++) {
|
for (i = 0; i < context->name_count; i++) {
|
||||||
char *p = context->names[i].ctx;
|
|
||||||
context->names[i].ctx = NULL;
|
|
||||||
kfree(p);
|
|
||||||
if (context->names[i].name)
|
if (context->names[i].name)
|
||||||
__putname(context->names[i].name);
|
__putname(context->names[i].name);
|
||||||
}
|
}
|
||||||
|
@ -416,11 +435,6 @@ static inline void audit_free_aux(struct audit_context *context)
|
||||||
dput(axi->dentry);
|
dput(axi->dentry);
|
||||||
mntput(axi->mnt);
|
mntput(axi->mnt);
|
||||||
}
|
}
|
||||||
if ( aux->type == AUDIT_IPC ) {
|
|
||||||
struct audit_aux_data_ipcctl *axi = (void *)aux;
|
|
||||||
if (axi->ctx)
|
|
||||||
kfree(axi->ctx);
|
|
||||||
}
|
|
||||||
|
|
||||||
context->aux = aux->next;
|
context->aux = aux->next;
|
||||||
kfree(aux);
|
kfree(aux);
|
||||||
|
@ -506,7 +520,7 @@ static inline void audit_free_context(struct audit_context *context)
|
||||||
printk(KERN_ERR "audit: freed %d contexts\n", count);
|
printk(KERN_ERR "audit: freed %d contexts\n", count);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void audit_log_task_context(struct audit_buffer *ab, gfp_t gfp_mask)
|
static void audit_log_task_context(struct audit_buffer *ab)
|
||||||
{
|
{
|
||||||
char *ctx = NULL;
|
char *ctx = NULL;
|
||||||
ssize_t len = 0;
|
ssize_t len = 0;
|
||||||
|
@ -518,7 +532,7 @@ static void audit_log_task_context(struct audit_buffer *ab, gfp_t gfp_mask)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx = kmalloc(len, gfp_mask);
|
ctx = kmalloc(len, GFP_KERNEL);
|
||||||
if (!ctx)
|
if (!ctx)
|
||||||
goto error_path;
|
goto error_path;
|
||||||
|
|
||||||
|
@ -536,23 +550,19 @@ error_path:
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void audit_log_task_info(struct audit_buffer *ab, gfp_t gfp_mask)
|
static void audit_log_task_info(struct audit_buffer *ab, struct task_struct *tsk)
|
||||||
{
|
{
|
||||||
char name[sizeof(current->comm)];
|
char name[sizeof(tsk->comm)];
|
||||||
struct mm_struct *mm = current->mm;
|
struct mm_struct *mm = tsk->mm;
|
||||||
struct vm_area_struct *vma;
|
struct vm_area_struct *vma;
|
||||||
|
|
||||||
get_task_comm(name, current);
|
/* tsk == current */
|
||||||
|
|
||||||
|
get_task_comm(name, tsk);
|
||||||
audit_log_format(ab, " comm=");
|
audit_log_format(ab, " comm=");
|
||||||
audit_log_untrustedstring(ab, name);
|
audit_log_untrustedstring(ab, name);
|
||||||
|
|
||||||
if (!mm)
|
if (mm) {
|
||||||
return;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* this is brittle; all callers that pass GFP_ATOMIC will have
|
|
||||||
* NULL current->mm and we won't get here.
|
|
||||||
*/
|
|
||||||
down_read(&mm->mmap_sem);
|
down_read(&mm->mmap_sem);
|
||||||
vma = mm->mmap;
|
vma = mm->mmap;
|
||||||
while (vma) {
|
while (vma) {
|
||||||
|
@ -566,17 +576,20 @@ static void audit_log_task_info(struct audit_buffer *ab, gfp_t gfp_mask)
|
||||||
vma = vma->vm_next;
|
vma = vma->vm_next;
|
||||||
}
|
}
|
||||||
up_read(&mm->mmap_sem);
|
up_read(&mm->mmap_sem);
|
||||||
audit_log_task_context(ab, gfp_mask);
|
}
|
||||||
|
audit_log_task_context(ab);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void audit_log_exit(struct audit_context *context, gfp_t gfp_mask)
|
static void audit_log_exit(struct audit_context *context, struct task_struct *tsk)
|
||||||
{
|
{
|
||||||
int i;
|
int i, call_panic = 0;
|
||||||
struct audit_buffer *ab;
|
struct audit_buffer *ab;
|
||||||
struct audit_aux_data *aux;
|
struct audit_aux_data *aux;
|
||||||
const char *tty;
|
const char *tty;
|
||||||
|
|
||||||
ab = audit_log_start(context, gfp_mask, AUDIT_SYSCALL);
|
/* tsk == current */
|
||||||
|
|
||||||
|
ab = audit_log_start(context, GFP_KERNEL, AUDIT_SYSCALL);
|
||||||
if (!ab)
|
if (!ab)
|
||||||
return; /* audit_panic has been called */
|
return; /* audit_panic has been called */
|
||||||
audit_log_format(ab, "arch=%x syscall=%d",
|
audit_log_format(ab, "arch=%x syscall=%d",
|
||||||
|
@ -587,8 +600,8 @@ static void audit_log_exit(struct audit_context *context, gfp_t gfp_mask)
|
||||||
audit_log_format(ab, " success=%s exit=%ld",
|
audit_log_format(ab, " success=%s exit=%ld",
|
||||||
(context->return_valid==AUDITSC_SUCCESS)?"yes":"no",
|
(context->return_valid==AUDITSC_SUCCESS)?"yes":"no",
|
||||||
context->return_code);
|
context->return_code);
|
||||||
if (current->signal->tty && current->signal->tty->name)
|
if (tsk->signal && tsk->signal->tty && tsk->signal->tty->name)
|
||||||
tty = current->signal->tty->name;
|
tty = tsk->signal->tty->name;
|
||||||
else
|
else
|
||||||
tty = "(none)";
|
tty = "(none)";
|
||||||
audit_log_format(ab,
|
audit_log_format(ab,
|
||||||
|
@ -607,12 +620,12 @@ static void audit_log_exit(struct audit_context *context, gfp_t gfp_mask)
|
||||||
context->gid,
|
context->gid,
|
||||||
context->euid, context->suid, context->fsuid,
|
context->euid, context->suid, context->fsuid,
|
||||||
context->egid, context->sgid, context->fsgid, tty);
|
context->egid, context->sgid, context->fsgid, tty);
|
||||||
audit_log_task_info(ab, gfp_mask);
|
audit_log_task_info(ab, tsk);
|
||||||
audit_log_end(ab);
|
audit_log_end(ab);
|
||||||
|
|
||||||
for (aux = context->aux; aux; aux = aux->next) {
|
for (aux = context->aux; aux; aux = aux->next) {
|
||||||
|
|
||||||
ab = audit_log_start(context, gfp_mask, aux->type);
|
ab = audit_log_start(context, GFP_KERNEL, aux->type);
|
||||||
if (!ab)
|
if (!ab)
|
||||||
continue; /* audit_panic has been called */
|
continue; /* audit_panic has been called */
|
||||||
|
|
||||||
|
@ -620,8 +633,39 @@ static void audit_log_exit(struct audit_context *context, gfp_t gfp_mask)
|
||||||
case AUDIT_IPC: {
|
case AUDIT_IPC: {
|
||||||
struct audit_aux_data_ipcctl *axi = (void *)aux;
|
struct audit_aux_data_ipcctl *axi = (void *)aux;
|
||||||
audit_log_format(ab,
|
audit_log_format(ab,
|
||||||
" qbytes=%lx iuid=%u igid=%u mode=%x obj=%s",
|
" qbytes=%lx iuid=%u igid=%u mode=%x",
|
||||||
axi->qbytes, axi->uid, axi->gid, axi->mode, axi->ctx);
|
axi->qbytes, axi->uid, axi->gid, axi->mode);
|
||||||
|
if (axi->osid != 0) {
|
||||||
|
char *ctx = NULL;
|
||||||
|
u32 len;
|
||||||
|
if (selinux_ctxid_to_string(
|
||||||
|
axi->osid, &ctx, &len)) {
|
||||||
|
audit_log_format(ab, " osid=%u",
|
||||||
|
axi->osid);
|
||||||
|
call_panic = 1;
|
||||||
|
} else
|
||||||
|
audit_log_format(ab, " obj=%s", ctx);
|
||||||
|
kfree(ctx);
|
||||||
|
}
|
||||||
|
break; }
|
||||||
|
|
||||||
|
case AUDIT_IPC_SET_PERM: {
|
||||||
|
struct audit_aux_data_ipcctl *axi = (void *)aux;
|
||||||
|
audit_log_format(ab,
|
||||||
|
" new qbytes=%lx new iuid=%u new igid=%u new mode=%x",
|
||||||
|
axi->qbytes, axi->uid, axi->gid, axi->mode);
|
||||||
|
if (axi->osid != 0) {
|
||||||
|
char *ctx = NULL;
|
||||||
|
u32 len;
|
||||||
|
if (selinux_ctxid_to_string(
|
||||||
|
axi->osid, &ctx, &len)) {
|
||||||
|
audit_log_format(ab, " osid=%u",
|
||||||
|
axi->osid);
|
||||||
|
call_panic = 1;
|
||||||
|
} else
|
||||||
|
audit_log_format(ab, " obj=%s", ctx);
|
||||||
|
kfree(ctx);
|
||||||
|
}
|
||||||
break; }
|
break; }
|
||||||
|
|
||||||
case AUDIT_SOCKETCALL: {
|
case AUDIT_SOCKETCALL: {
|
||||||
|
@ -649,7 +693,7 @@ static void audit_log_exit(struct audit_context *context, gfp_t gfp_mask)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (context->pwd && context->pwdmnt) {
|
if (context->pwd && context->pwdmnt) {
|
||||||
ab = audit_log_start(context, gfp_mask, AUDIT_CWD);
|
ab = audit_log_start(context, GFP_KERNEL, AUDIT_CWD);
|
||||||
if (ab) {
|
if (ab) {
|
||||||
audit_log_d_path(ab, "cwd=", context->pwd, context->pwdmnt);
|
audit_log_d_path(ab, "cwd=", context->pwd, context->pwdmnt);
|
||||||
audit_log_end(ab);
|
audit_log_end(ab);
|
||||||
|
@ -659,7 +703,7 @@ static void audit_log_exit(struct audit_context *context, gfp_t gfp_mask)
|
||||||
unsigned long ino = context->names[i].ino;
|
unsigned long ino = context->names[i].ino;
|
||||||
unsigned long pino = context->names[i].pino;
|
unsigned long pino = context->names[i].pino;
|
||||||
|
|
||||||
ab = audit_log_start(context, gfp_mask, AUDIT_PATH);
|
ab = audit_log_start(context, GFP_KERNEL, AUDIT_PATH);
|
||||||
if (!ab)
|
if (!ab)
|
||||||
continue; /* audit_panic has been called */
|
continue; /* audit_panic has been called */
|
||||||
|
|
||||||
|
@ -685,32 +729,35 @@ static void audit_log_exit(struct audit_context *context, gfp_t gfp_mask)
|
||||||
context->names[i].gid,
|
context->names[i].gid,
|
||||||
MAJOR(context->names[i].rdev),
|
MAJOR(context->names[i].rdev),
|
||||||
MINOR(context->names[i].rdev));
|
MINOR(context->names[i].rdev));
|
||||||
if (context->names[i].ctx) {
|
if (context->names[i].osid != 0) {
|
||||||
audit_log_format(ab, " obj=%s",
|
char *ctx = NULL;
|
||||||
context->names[i].ctx);
|
u32 len;
|
||||||
|
if (selinux_ctxid_to_string(
|
||||||
|
context->names[i].osid, &ctx, &len)) {
|
||||||
|
audit_log_format(ab, " osid=%u",
|
||||||
|
context->names[i].osid);
|
||||||
|
call_panic = 2;
|
||||||
|
} else
|
||||||
|
audit_log_format(ab, " obj=%s", ctx);
|
||||||
|
kfree(ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
audit_log_end(ab);
|
audit_log_end(ab);
|
||||||
}
|
}
|
||||||
|
if (call_panic)
|
||||||
|
audit_panic("error converting sid to string");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* audit_free - free a per-task audit context
|
* audit_free - free a per-task audit context
|
||||||
* @tsk: task whose audit context block to free
|
* @tsk: task whose audit context block to free
|
||||||
*
|
*
|
||||||
* Called from copy_process and __put_task_struct.
|
* Called from copy_process and do_exit
|
||||||
*/
|
*/
|
||||||
void audit_free(struct task_struct *tsk)
|
void audit_free(struct task_struct *tsk)
|
||||||
{
|
{
|
||||||
struct audit_context *context;
|
struct audit_context *context;
|
||||||
|
|
||||||
/*
|
|
||||||
* No need to lock the task - when we execute audit_free()
|
|
||||||
* then the task has no external references anymore, and
|
|
||||||
* we are tearing it down. (The locking also confuses
|
|
||||||
* DEBUG_LOCKDEP - this freeing may occur in softirq
|
|
||||||
* contexts as well, via RCU.)
|
|
||||||
*/
|
|
||||||
context = audit_get_context(tsk, 0, 0);
|
context = audit_get_context(tsk, 0, 0);
|
||||||
if (likely(!context))
|
if (likely(!context))
|
||||||
return;
|
return;
|
||||||
|
@ -719,8 +766,9 @@ void audit_free(struct task_struct *tsk)
|
||||||
* function (e.g., exit_group), then free context block.
|
* function (e.g., exit_group), then free context block.
|
||||||
* We use GFP_ATOMIC here because we might be doing this
|
* We use GFP_ATOMIC here because we might be doing this
|
||||||
* in the context of the idle thread */
|
* in the context of the idle thread */
|
||||||
|
/* that can happen only if we are called from do_exit() */
|
||||||
if (context->in_syscall && context->auditable)
|
if (context->in_syscall && context->auditable)
|
||||||
audit_log_exit(context, GFP_ATOMIC);
|
audit_log_exit(context, tsk);
|
||||||
|
|
||||||
audit_free_context(context);
|
audit_free_context(context);
|
||||||
}
|
}
|
||||||
|
@ -743,10 +791,11 @@ void audit_free(struct task_struct *tsk)
|
||||||
* will only be written if another part of the kernel requests that it
|
* will only be written if another part of the kernel requests that it
|
||||||
* be written).
|
* be written).
|
||||||
*/
|
*/
|
||||||
void audit_syscall_entry(struct task_struct *tsk, int arch, int major,
|
void audit_syscall_entry(int arch, int major,
|
||||||
unsigned long a1, unsigned long a2,
|
unsigned long a1, unsigned long a2,
|
||||||
unsigned long a3, unsigned long a4)
|
unsigned long a3, unsigned long a4)
|
||||||
{
|
{
|
||||||
|
struct task_struct *tsk = current;
|
||||||
struct audit_context *context = tsk->audit_context;
|
struct audit_context *context = tsk->audit_context;
|
||||||
enum audit_state state;
|
enum audit_state state;
|
||||||
|
|
||||||
|
@ -824,22 +873,18 @@ void audit_syscall_entry(struct task_struct *tsk, int arch, int major,
|
||||||
* message), then write out the syscall information. In call cases,
|
* message), then write out the syscall information. In call cases,
|
||||||
* free the names stored from getname().
|
* free the names stored from getname().
|
||||||
*/
|
*/
|
||||||
void audit_syscall_exit(struct task_struct *tsk, int valid, long return_code)
|
void audit_syscall_exit(int valid, long return_code)
|
||||||
{
|
{
|
||||||
|
struct task_struct *tsk = current;
|
||||||
struct audit_context *context;
|
struct audit_context *context;
|
||||||
|
|
||||||
get_task_struct(tsk);
|
|
||||||
task_lock(tsk);
|
|
||||||
context = audit_get_context(tsk, valid, return_code);
|
context = audit_get_context(tsk, valid, return_code);
|
||||||
task_unlock(tsk);
|
|
||||||
|
|
||||||
/* Not having a context here is ok, since the parent may have
|
|
||||||
* called __put_task_struct. */
|
|
||||||
if (likely(!context))
|
if (likely(!context))
|
||||||
goto out;
|
return;
|
||||||
|
|
||||||
if (context->in_syscall && context->auditable)
|
if (context->in_syscall && context->auditable)
|
||||||
audit_log_exit(context, GFP_KERNEL);
|
audit_log_exit(context, tsk);
|
||||||
|
|
||||||
context->in_syscall = 0;
|
context->in_syscall = 0;
|
||||||
context->auditable = 0;
|
context->auditable = 0;
|
||||||
|
@ -854,8 +899,6 @@ void audit_syscall_exit(struct task_struct *tsk, int valid, long return_code)
|
||||||
audit_free_aux(context);
|
audit_free_aux(context);
|
||||||
tsk->audit_context = context;
|
tsk->audit_context = context;
|
||||||
}
|
}
|
||||||
out:
|
|
||||||
put_task_struct(tsk);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -936,40 +979,11 @@ void audit_putname(const char *name)
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void audit_inode_context(int idx, const struct inode *inode)
|
static void audit_inode_context(int idx, const struct inode *inode)
|
||||||
{
|
{
|
||||||
struct audit_context *context = current->audit_context;
|
struct audit_context *context = current->audit_context;
|
||||||
const char *suffix = security_inode_xattr_getsuffix();
|
|
||||||
char *ctx = NULL;
|
|
||||||
int len = 0;
|
|
||||||
|
|
||||||
if (!suffix)
|
selinux_get_inode_sid(inode, &context->names[idx].osid);
|
||||||
goto ret;
|
|
||||||
|
|
||||||
len = security_inode_getsecurity(inode, suffix, NULL, 0, 0);
|
|
||||||
if (len == -EOPNOTSUPP)
|
|
||||||
goto ret;
|
|
||||||
if (len < 0)
|
|
||||||
goto error_path;
|
|
||||||
|
|
||||||
ctx = kmalloc(len, GFP_KERNEL);
|
|
||||||
if (!ctx)
|
|
||||||
goto error_path;
|
|
||||||
|
|
||||||
len = security_inode_getsecurity(inode, suffix, ctx, len, 0);
|
|
||||||
if (len < 0)
|
|
||||||
goto error_path;
|
|
||||||
|
|
||||||
kfree(context->names[idx].ctx);
|
|
||||||
context->names[idx].ctx = ctx;
|
|
||||||
goto ret;
|
|
||||||
|
|
||||||
error_path:
|
|
||||||
if (ctx)
|
|
||||||
kfree(ctx);
|
|
||||||
audit_panic("error in audit_inode_context");
|
|
||||||
ret:
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -1155,40 +1169,37 @@ uid_t audit_get_loginuid(struct audit_context *ctx)
|
||||||
return ctx ? ctx->loginuid : -1;
|
return ctx ? ctx->loginuid : -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static char *audit_ipc_context(struct kern_ipc_perm *ipcp)
|
/**
|
||||||
|
* audit_ipc_obj - record audit data for ipc object
|
||||||
|
* @ipcp: ipc permissions
|
||||||
|
*
|
||||||
|
* Returns 0 for success or NULL context or < 0 on error.
|
||||||
|
*/
|
||||||
|
int audit_ipc_obj(struct kern_ipc_perm *ipcp)
|
||||||
{
|
{
|
||||||
|
struct audit_aux_data_ipcctl *ax;
|
||||||
struct audit_context *context = current->audit_context;
|
struct audit_context *context = current->audit_context;
|
||||||
char *ctx = NULL;
|
|
||||||
int len = 0;
|
|
||||||
|
|
||||||
if (likely(!context))
|
if (likely(!context))
|
||||||
return NULL;
|
return 0;
|
||||||
|
|
||||||
len = security_ipc_getsecurity(ipcp, NULL, 0);
|
ax = kmalloc(sizeof(*ax), GFP_ATOMIC);
|
||||||
if (len == -EOPNOTSUPP)
|
if (!ax)
|
||||||
goto ret;
|
return -ENOMEM;
|
||||||
if (len < 0)
|
|
||||||
goto error_path;
|
|
||||||
|
|
||||||
ctx = kmalloc(len, GFP_ATOMIC);
|
ax->uid = ipcp->uid;
|
||||||
if (!ctx)
|
ax->gid = ipcp->gid;
|
||||||
goto error_path;
|
ax->mode = ipcp->mode;
|
||||||
|
selinux_get_ipc_sid(ipcp, &ax->osid);
|
||||||
|
|
||||||
len = security_ipc_getsecurity(ipcp, ctx, len);
|
ax->d.type = AUDIT_IPC;
|
||||||
if (len < 0)
|
ax->d.next = context->aux;
|
||||||
goto error_path;
|
context->aux = (void *)ax;
|
||||||
|
return 0;
|
||||||
return ctx;
|
|
||||||
|
|
||||||
error_path:
|
|
||||||
kfree(ctx);
|
|
||||||
audit_panic("error in audit_ipc_context");
|
|
||||||
ret:
|
|
||||||
return NULL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* audit_ipc_perms - record audit data for ipc
|
* audit_ipc_set_perm - record audit data for new ipc permissions
|
||||||
* @qbytes: msgq bytes
|
* @qbytes: msgq bytes
|
||||||
* @uid: msgq user id
|
* @uid: msgq user id
|
||||||
* @gid: msgq group id
|
* @gid: msgq group id
|
||||||
|
@ -1196,7 +1207,7 @@ ret:
|
||||||
*
|
*
|
||||||
* Returns 0 for success or NULL context or < 0 on error.
|
* Returns 0 for success or NULL context or < 0 on error.
|
||||||
*/
|
*/
|
||||||
int audit_ipc_perms(unsigned long qbytes, uid_t uid, gid_t gid, mode_t mode, struct kern_ipc_perm *ipcp)
|
int audit_ipc_set_perm(unsigned long qbytes, uid_t uid, gid_t gid, mode_t mode, struct kern_ipc_perm *ipcp)
|
||||||
{
|
{
|
||||||
struct audit_aux_data_ipcctl *ax;
|
struct audit_aux_data_ipcctl *ax;
|
||||||
struct audit_context *context = current->audit_context;
|
struct audit_context *context = current->audit_context;
|
||||||
|
@ -1212,9 +1223,9 @@ int audit_ipc_perms(unsigned long qbytes, uid_t uid, gid_t gid, mode_t mode, str
|
||||||
ax->uid = uid;
|
ax->uid = uid;
|
||||||
ax->gid = gid;
|
ax->gid = gid;
|
||||||
ax->mode = mode;
|
ax->mode = mode;
|
||||||
ax->ctx = audit_ipc_context(ipcp);
|
selinux_get_ipc_sid(ipcp, &ax->osid);
|
||||||
|
|
||||||
ax->d.type = AUDIT_IPC;
|
ax->d.type = AUDIT_IPC_SET_PERM;
|
||||||
ax->d.next = context->aux;
|
ax->d.next = context->aux;
|
||||||
context->aux = (void *)ax;
|
context->aux = (void *)ax;
|
||||||
return 0;
|
return 0;
|
||||||
|
|
|
@ -35,6 +35,7 @@
|
||||||
#include <linux/futex.h>
|
#include <linux/futex.h>
|
||||||
#include <linux/compat.h>
|
#include <linux/compat.h>
|
||||||
#include <linux/pipe_fs_i.h>
|
#include <linux/pipe_fs_i.h>
|
||||||
|
#include <linux/audit.h> /* for audit_free() */
|
||||||
|
|
||||||
#include <asm/uaccess.h>
|
#include <asm/uaccess.h>
|
||||||
#include <asm/unistd.h>
|
#include <asm/unistd.h>
|
||||||
|
@ -910,6 +911,8 @@ fastcall NORET_TYPE void do_exit(long code)
|
||||||
if (unlikely(tsk->compat_robust_list))
|
if (unlikely(tsk->compat_robust_list))
|
||||||
compat_exit_robust_list(tsk);
|
compat_exit_robust_list(tsk);
|
||||||
#endif
|
#endif
|
||||||
|
if (unlikely(tsk->audit_context))
|
||||||
|
audit_free(tsk);
|
||||||
exit_mm(tsk);
|
exit_mm(tsk);
|
||||||
|
|
||||||
exit_sem(tsk);
|
exit_sem(tsk);
|
||||||
|
|
|
@ -114,8 +114,6 @@ void __put_task_struct(struct task_struct *tsk)
|
||||||
WARN_ON(atomic_read(&tsk->usage));
|
WARN_ON(atomic_read(&tsk->usage));
|
||||||
WARN_ON(tsk == current);
|
WARN_ON(tsk == current);
|
||||||
|
|
||||||
if (unlikely(tsk->audit_context))
|
|
||||||
audit_free(tsk);
|
|
||||||
security_task_free(tsk);
|
security_task_free(tsk);
|
||||||
free_uid(tsk->user);
|
free_uid(tsk->user);
|
||||||
put_group_info(tsk->group_info);
|
put_group_info(tsk->group_info);
|
||||||
|
|
|
@ -56,6 +56,7 @@
|
||||||
#include <linux/mm.h>
|
#include <linux/mm.h>
|
||||||
#include <linux/types.h>
|
#include <linux/types.h>
|
||||||
#include <linux/audit.h>
|
#include <linux/audit.h>
|
||||||
|
#include <linux/selinux.h>
|
||||||
|
|
||||||
#include <net/sock.h>
|
#include <net/sock.h>
|
||||||
#include <net/scm.h>
|
#include <net/scm.h>
|
||||||
|
@ -1156,6 +1157,7 @@ static int netlink_sendmsg(struct kiocb *kiocb, struct socket *sock,
|
||||||
NETLINK_CB(skb).dst_pid = dst_pid;
|
NETLINK_CB(skb).dst_pid = dst_pid;
|
||||||
NETLINK_CB(skb).dst_group = dst_group;
|
NETLINK_CB(skb).dst_group = dst_group;
|
||||||
NETLINK_CB(skb).loginuid = audit_get_loginuid(current->audit_context);
|
NETLINK_CB(skb).loginuid = audit_get_loginuid(current->audit_context);
|
||||||
|
selinux_get_task_sid(current, &(NETLINK_CB(skb).sid));
|
||||||
memcpy(NETLINK_CREDS(skb), &siocb->scm->creds, sizeof(struct ucred));
|
memcpy(NETLINK_CREDS(skb), &siocb->scm->creds, sizeof(struct ucred));
|
||||||
|
|
||||||
/* What can I do? Netlink is asynchronous, so that
|
/* What can I do? Netlink is asynchronous, so that
|
||||||
|
|
|
@ -267,6 +267,8 @@ int move_addr_to_user(void *kaddr, int klen, void __user *uaddr, int __user *ule
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
if(len)
|
if(len)
|
||||||
{
|
{
|
||||||
|
if (audit_sockaddr(klen, kaddr))
|
||||||
|
return -ENOMEM;
|
||||||
if(copy_to_user(uaddr,kaddr,len))
|
if(copy_to_user(uaddr,kaddr,len))
|
||||||
return -EFAULT;
|
return -EFAULT;
|
||||||
}
|
}
|
||||||
|
|
|
@ -563,11 +563,6 @@ static int dummy_ipc_permission (struct kern_ipc_perm *ipcp, short flag)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int dummy_ipc_getsecurity(struct kern_ipc_perm *ipcp, void *buffer, size_t size)
|
|
||||||
{
|
|
||||||
return -EOPNOTSUPP;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int dummy_msg_msg_alloc_security (struct msg_msg *msg)
|
static int dummy_msg_msg_alloc_security (struct msg_msg *msg)
|
||||||
{
|
{
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -976,7 +971,6 @@ void security_fixup_ops (struct security_operations *ops)
|
||||||
set_to_dummy_if_null(ops, task_reparent_to_init);
|
set_to_dummy_if_null(ops, task_reparent_to_init);
|
||||||
set_to_dummy_if_null(ops, task_to_inode);
|
set_to_dummy_if_null(ops, task_to_inode);
|
||||||
set_to_dummy_if_null(ops, ipc_permission);
|
set_to_dummy_if_null(ops, ipc_permission);
|
||||||
set_to_dummy_if_null(ops, ipc_getsecurity);
|
|
||||||
set_to_dummy_if_null(ops, msg_msg_alloc_security);
|
set_to_dummy_if_null(ops, msg_msg_alloc_security);
|
||||||
set_to_dummy_if_null(ops, msg_msg_free_security);
|
set_to_dummy_if_null(ops, msg_msg_free_security);
|
||||||
set_to_dummy_if_null(ops, msg_queue_alloc_security);
|
set_to_dummy_if_null(ops, msg_queue_alloc_security);
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
|
|
||||||
obj-$(CONFIG_SECURITY_SELINUX) := selinux.o ss/
|
obj-$(CONFIG_SECURITY_SELINUX) := selinux.o ss/
|
||||||
|
|
||||||
selinux-y := avc.o hooks.o selinuxfs.o netlink.o nlmsgtab.o netif.o
|
selinux-y := avc.o hooks.o selinuxfs.o netlink.o nlmsgtab.o netif.o exports.o
|
||||||
|
|
||||||
selinux-$(CONFIG_SECURITY_NETWORK_XFRM) += xfrm.o
|
selinux-$(CONFIG_SECURITY_NETWORK_XFRM) += xfrm.o
|
||||||
|
|
||||||
|
|
|
@ -800,7 +800,7 @@ out:
|
||||||
int avc_ss_reset(u32 seqno)
|
int avc_ss_reset(u32 seqno)
|
||||||
{
|
{
|
||||||
struct avc_callback_node *c;
|
struct avc_callback_node *c;
|
||||||
int i, rc = 0;
|
int i, rc = 0, tmprc;
|
||||||
unsigned long flag;
|
unsigned long flag;
|
||||||
struct avc_node *node;
|
struct avc_node *node;
|
||||||
|
|
||||||
|
@ -813,15 +813,16 @@ int avc_ss_reset(u32 seqno)
|
||||||
|
|
||||||
for (c = avc_callbacks; c; c = c->next) {
|
for (c = avc_callbacks; c; c = c->next) {
|
||||||
if (c->events & AVC_CALLBACK_RESET) {
|
if (c->events & AVC_CALLBACK_RESET) {
|
||||||
rc = c->callback(AVC_CALLBACK_RESET,
|
tmprc = c->callback(AVC_CALLBACK_RESET,
|
||||||
0, 0, 0, 0, NULL);
|
0, 0, 0, 0, NULL);
|
||||||
if (rc)
|
/* save the first error encountered for the return
|
||||||
goto out;
|
value and continue processing the callbacks */
|
||||||
|
if (!rc)
|
||||||
|
rc = tmprc;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
avc_latest_notif_update(seqno, 0);
|
avc_latest_notif_update(seqno, 0);
|
||||||
out:
|
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
74
security/selinux/exports.c
Normal file
74
security/selinux/exports.c
Normal file
|
@ -0,0 +1,74 @@
|
||||||
|
/*
|
||||||
|
* SELinux services exported to the rest of the kernel.
|
||||||
|
*
|
||||||
|
* Author: James Morris <jmorris@redhat.com>
|
||||||
|
*
|
||||||
|
* Copyright (C) 2005 Red Hat, Inc., James Morris <jmorris@redhat.com>
|
||||||
|
* Copyright (C) 2006 Trusted Computer Solutions, Inc. <dgoeddel@trustedcs.com>
|
||||||
|
* Copyright (C) 2006 IBM Corporation, Timothy R. Chavez <tinytim@us.ibm.com>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License version 2,
|
||||||
|
* as published by the Free Software Foundation.
|
||||||
|
*/
|
||||||
|
#include <linux/types.h>
|
||||||
|
#include <linux/kernel.h>
|
||||||
|
#include <linux/module.h>
|
||||||
|
#include <linux/selinux.h>
|
||||||
|
#include <linux/fs.h>
|
||||||
|
#include <linux/ipc.h>
|
||||||
|
|
||||||
|
#include "security.h"
|
||||||
|
#include "objsec.h"
|
||||||
|
|
||||||
|
void selinux_task_ctxid(struct task_struct *tsk, u32 *ctxid)
|
||||||
|
{
|
||||||
|
struct task_security_struct *tsec = tsk->security;
|
||||||
|
if (selinux_enabled)
|
||||||
|
*ctxid = tsec->sid;
|
||||||
|
else
|
||||||
|
*ctxid = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int selinux_ctxid_to_string(u32 ctxid, char **ctx, u32 *ctxlen)
|
||||||
|
{
|
||||||
|
if (selinux_enabled)
|
||||||
|
return security_sid_to_context(ctxid, ctx, ctxlen);
|
||||||
|
else {
|
||||||
|
*ctx = NULL;
|
||||||
|
*ctxlen = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void selinux_get_inode_sid(const struct inode *inode, u32 *sid)
|
||||||
|
{
|
||||||
|
if (selinux_enabled) {
|
||||||
|
struct inode_security_struct *isec = inode->i_security;
|
||||||
|
*sid = isec->sid;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
*sid = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void selinux_get_ipc_sid(const struct kern_ipc_perm *ipcp, u32 *sid)
|
||||||
|
{
|
||||||
|
if (selinux_enabled) {
|
||||||
|
struct ipc_security_struct *isec = ipcp->security;
|
||||||
|
*sid = isec->sid;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
*sid = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void selinux_get_task_sid(struct task_struct *tsk, u32 *sid)
|
||||||
|
{
|
||||||
|
if (selinux_enabled) {
|
||||||
|
struct task_security_struct *tsec = tsk->security;
|
||||||
|
*sid = tsec->sid;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
*sid = 0;
|
||||||
|
}
|
||||||
|
|
|
@ -4052,13 +4052,6 @@ static int selinux_ipc_permission(struct kern_ipc_perm *ipcp, short flag)
|
||||||
return ipc_has_perm(ipcp, av);
|
return ipc_has_perm(ipcp, av);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int selinux_ipc_getsecurity(struct kern_ipc_perm *ipcp, void *buffer, size_t size)
|
|
||||||
{
|
|
||||||
struct ipc_security_struct *isec = ipcp->security;
|
|
||||||
|
|
||||||
return selinux_getsecurity(isec->sid, buffer, size);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* module stacking operations */
|
/* module stacking operations */
|
||||||
static int selinux_register_security (const char *name, struct security_operations *ops)
|
static int selinux_register_security (const char *name, struct security_operations *ops)
|
||||||
{
|
{
|
||||||
|
@ -4321,7 +4314,6 @@ static struct security_operations selinux_ops = {
|
||||||
.task_to_inode = selinux_task_to_inode,
|
.task_to_inode = selinux_task_to_inode,
|
||||||
|
|
||||||
.ipc_permission = selinux_ipc_permission,
|
.ipc_permission = selinux_ipc_permission,
|
||||||
.ipc_getsecurity = selinux_ipc_getsecurity,
|
|
||||||
|
|
||||||
.msg_msg_alloc_security = selinux_msg_msg_alloc_security,
|
.msg_msg_alloc_security = selinux_msg_msg_alloc_security,
|
||||||
.msg_msg_free_security = selinux_msg_msg_free_security,
|
.msg_msg_free_security = selinux_msg_msg_free_security,
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
*
|
*
|
||||||
* Support for enhanced MLS infrastructure.
|
* Support for enhanced MLS infrastructure.
|
||||||
*
|
*
|
||||||
* Copyright (C) 2004-2005 Trusted Computer Solutions, Inc.
|
* Copyright (C) 2004-2006 Trusted Computer Solutions, Inc.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <linux/kernel.h>
|
#include <linux/kernel.h>
|
||||||
|
@ -384,6 +384,34 @@ out:
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Set the MLS fields in the security context structure
|
||||||
|
* `context' based on the string representation in
|
||||||
|
* the string `str'. This function will allocate temporary memory with the
|
||||||
|
* given constraints of gfp_mask.
|
||||||
|
*/
|
||||||
|
int mls_from_string(char *str, struct context *context, gfp_t gfp_mask)
|
||||||
|
{
|
||||||
|
char *tmpstr, *freestr;
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
if (!selinux_mls_enabled)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
/* we need freestr because mls_context_to_sid will change
|
||||||
|
the value of tmpstr */
|
||||||
|
tmpstr = freestr = kstrdup(str, gfp_mask);
|
||||||
|
if (!tmpstr) {
|
||||||
|
rc = -ENOMEM;
|
||||||
|
} else {
|
||||||
|
rc = mls_context_to_sid(':', &tmpstr, context,
|
||||||
|
NULL, SECSID_NULL);
|
||||||
|
kfree(freestr);
|
||||||
|
}
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copies the effective MLS range from `src' into `dst'.
|
* Copies the effective MLS range from `src' into `dst'.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
*
|
*
|
||||||
* Support for enhanced MLS infrastructure.
|
* Support for enhanced MLS infrastructure.
|
||||||
*
|
*
|
||||||
* Copyright (C) 2004-2005 Trusted Computer Solutions, Inc.
|
* Copyright (C) 2004-2006 Trusted Computer Solutions, Inc.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef _SS_MLS_H_
|
#ifndef _SS_MLS_H_
|
||||||
|
@ -27,6 +27,8 @@ int mls_context_to_sid(char oldc,
|
||||||
struct sidtab *s,
|
struct sidtab *s,
|
||||||
u32 def_sid);
|
u32 def_sid);
|
||||||
|
|
||||||
|
int mls_from_string(char *str, struct context *context, gfp_t gfp_mask);
|
||||||
|
|
||||||
int mls_convert_context(struct policydb *oldp,
|
int mls_convert_context(struct policydb *oldp,
|
||||||
struct policydb *newp,
|
struct policydb *newp,
|
||||||
struct context *context);
|
struct context *context);
|
||||||
|
|
|
@ -7,12 +7,13 @@
|
||||||
* Updated: Trusted Computer Solutions, Inc. <dgoeddel@trustedcs.com>
|
* Updated: Trusted Computer Solutions, Inc. <dgoeddel@trustedcs.com>
|
||||||
*
|
*
|
||||||
* Support for enhanced MLS infrastructure.
|
* Support for enhanced MLS infrastructure.
|
||||||
|
* Support for context based audit filters.
|
||||||
*
|
*
|
||||||
* Updated: Frank Mayer <mayerf@tresys.com> and Karl MacMillan <kmacmillan@tresys.com>
|
* Updated: Frank Mayer <mayerf@tresys.com> and Karl MacMillan <kmacmillan@tresys.com>
|
||||||
*
|
*
|
||||||
* Added conditional policy language extensions
|
* Added conditional policy language extensions
|
||||||
*
|
*
|
||||||
* Copyright (C) 2004-2005 Trusted Computer Solutions, Inc.
|
* Copyright (C) 2004-2006 Trusted Computer Solutions, Inc.
|
||||||
* Copyright (C) 2003 - 2004 Tresys Technology, LLC
|
* Copyright (C) 2003 - 2004 Tresys Technology, LLC
|
||||||
* Copyright (C) 2003 Red Hat, Inc., James Morris <jmorris@redhat.com>
|
* Copyright (C) 2003 Red Hat, Inc., James Morris <jmorris@redhat.com>
|
||||||
* This program is free software; you can redistribute it and/or modify
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
@ -1811,3 +1812,235 @@ out:
|
||||||
POLICY_RDUNLOCK;
|
POLICY_RDUNLOCK;
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct selinux_audit_rule {
|
||||||
|
u32 au_seqno;
|
||||||
|
struct context au_ctxt;
|
||||||
|
};
|
||||||
|
|
||||||
|
void selinux_audit_rule_free(struct selinux_audit_rule *rule)
|
||||||
|
{
|
||||||
|
if (rule) {
|
||||||
|
context_destroy(&rule->au_ctxt);
|
||||||
|
kfree(rule);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int selinux_audit_rule_init(u32 field, u32 op, char *rulestr,
|
||||||
|
struct selinux_audit_rule **rule)
|
||||||
|
{
|
||||||
|
struct selinux_audit_rule *tmprule;
|
||||||
|
struct role_datum *roledatum;
|
||||||
|
struct type_datum *typedatum;
|
||||||
|
struct user_datum *userdatum;
|
||||||
|
int rc = 0;
|
||||||
|
|
||||||
|
*rule = NULL;
|
||||||
|
|
||||||
|
if (!ss_initialized)
|
||||||
|
return -ENOTSUPP;
|
||||||
|
|
||||||
|
switch (field) {
|
||||||
|
case AUDIT_SE_USER:
|
||||||
|
case AUDIT_SE_ROLE:
|
||||||
|
case AUDIT_SE_TYPE:
|
||||||
|
/* only 'equals' and 'not equals' fit user, role, and type */
|
||||||
|
if (op != AUDIT_EQUAL && op != AUDIT_NOT_EQUAL)
|
||||||
|
return -EINVAL;
|
||||||
|
break;
|
||||||
|
case AUDIT_SE_SEN:
|
||||||
|
case AUDIT_SE_CLR:
|
||||||
|
/* we do not allow a range, indicated by the presense of '-' */
|
||||||
|
if (strchr(rulestr, '-'))
|
||||||
|
return -EINVAL;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
/* only the above fields are valid */
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
tmprule = kzalloc(sizeof(struct selinux_audit_rule), GFP_KERNEL);
|
||||||
|
if (!tmprule)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
context_init(&tmprule->au_ctxt);
|
||||||
|
|
||||||
|
POLICY_RDLOCK;
|
||||||
|
|
||||||
|
tmprule->au_seqno = latest_granting;
|
||||||
|
|
||||||
|
switch (field) {
|
||||||
|
case AUDIT_SE_USER:
|
||||||
|
userdatum = hashtab_search(policydb.p_users.table, rulestr);
|
||||||
|
if (!userdatum)
|
||||||
|
rc = -EINVAL;
|
||||||
|
else
|
||||||
|
tmprule->au_ctxt.user = userdatum->value;
|
||||||
|
break;
|
||||||
|
case AUDIT_SE_ROLE:
|
||||||
|
roledatum = hashtab_search(policydb.p_roles.table, rulestr);
|
||||||
|
if (!roledatum)
|
||||||
|
rc = -EINVAL;
|
||||||
|
else
|
||||||
|
tmprule->au_ctxt.role = roledatum->value;
|
||||||
|
break;
|
||||||
|
case AUDIT_SE_TYPE:
|
||||||
|
typedatum = hashtab_search(policydb.p_types.table, rulestr);
|
||||||
|
if (!typedatum)
|
||||||
|
rc = -EINVAL;
|
||||||
|
else
|
||||||
|
tmprule->au_ctxt.type = typedatum->value;
|
||||||
|
break;
|
||||||
|
case AUDIT_SE_SEN:
|
||||||
|
case AUDIT_SE_CLR:
|
||||||
|
rc = mls_from_string(rulestr, &tmprule->au_ctxt, GFP_ATOMIC);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
POLICY_RDUNLOCK;
|
||||||
|
|
||||||
|
if (rc) {
|
||||||
|
selinux_audit_rule_free(tmprule);
|
||||||
|
tmprule = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
*rule = tmprule;
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
int selinux_audit_rule_match(u32 ctxid, u32 field, u32 op,
|
||||||
|
struct selinux_audit_rule *rule,
|
||||||
|
struct audit_context *actx)
|
||||||
|
{
|
||||||
|
struct context *ctxt;
|
||||||
|
struct mls_level *level;
|
||||||
|
int match = 0;
|
||||||
|
|
||||||
|
if (!rule) {
|
||||||
|
audit_log(actx, GFP_ATOMIC, AUDIT_SELINUX_ERR,
|
||||||
|
"selinux_audit_rule_match: missing rule\n");
|
||||||
|
return -ENOENT;
|
||||||
|
}
|
||||||
|
|
||||||
|
POLICY_RDLOCK;
|
||||||
|
|
||||||
|
if (rule->au_seqno < latest_granting) {
|
||||||
|
audit_log(actx, GFP_ATOMIC, AUDIT_SELINUX_ERR,
|
||||||
|
"selinux_audit_rule_match: stale rule\n");
|
||||||
|
match = -ESTALE;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
ctxt = sidtab_search(&sidtab, ctxid);
|
||||||
|
if (!ctxt) {
|
||||||
|
audit_log(actx, GFP_ATOMIC, AUDIT_SELINUX_ERR,
|
||||||
|
"selinux_audit_rule_match: unrecognized SID %d\n",
|
||||||
|
ctxid);
|
||||||
|
match = -ENOENT;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* a field/op pair that is not caught here will simply fall through
|
||||||
|
without a match */
|
||||||
|
switch (field) {
|
||||||
|
case AUDIT_SE_USER:
|
||||||
|
switch (op) {
|
||||||
|
case AUDIT_EQUAL:
|
||||||
|
match = (ctxt->user == rule->au_ctxt.user);
|
||||||
|
break;
|
||||||
|
case AUDIT_NOT_EQUAL:
|
||||||
|
match = (ctxt->user != rule->au_ctxt.user);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case AUDIT_SE_ROLE:
|
||||||
|
switch (op) {
|
||||||
|
case AUDIT_EQUAL:
|
||||||
|
match = (ctxt->role == rule->au_ctxt.role);
|
||||||
|
break;
|
||||||
|
case AUDIT_NOT_EQUAL:
|
||||||
|
match = (ctxt->role != rule->au_ctxt.role);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case AUDIT_SE_TYPE:
|
||||||
|
switch (op) {
|
||||||
|
case AUDIT_EQUAL:
|
||||||
|
match = (ctxt->type == rule->au_ctxt.type);
|
||||||
|
break;
|
||||||
|
case AUDIT_NOT_EQUAL:
|
||||||
|
match = (ctxt->type != rule->au_ctxt.type);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case AUDIT_SE_SEN:
|
||||||
|
case AUDIT_SE_CLR:
|
||||||
|
level = (op == AUDIT_SE_SEN ?
|
||||||
|
&ctxt->range.level[0] : &ctxt->range.level[1]);
|
||||||
|
switch (op) {
|
||||||
|
case AUDIT_EQUAL:
|
||||||
|
match = mls_level_eq(&rule->au_ctxt.range.level[0],
|
||||||
|
level);
|
||||||
|
break;
|
||||||
|
case AUDIT_NOT_EQUAL:
|
||||||
|
match = !mls_level_eq(&rule->au_ctxt.range.level[0],
|
||||||
|
level);
|
||||||
|
break;
|
||||||
|
case AUDIT_LESS_THAN:
|
||||||
|
match = (mls_level_dom(&rule->au_ctxt.range.level[0],
|
||||||
|
level) &&
|
||||||
|
!mls_level_eq(&rule->au_ctxt.range.level[0],
|
||||||
|
level));
|
||||||
|
break;
|
||||||
|
case AUDIT_LESS_THAN_OR_EQUAL:
|
||||||
|
match = mls_level_dom(&rule->au_ctxt.range.level[0],
|
||||||
|
level);
|
||||||
|
break;
|
||||||
|
case AUDIT_GREATER_THAN:
|
||||||
|
match = (mls_level_dom(level,
|
||||||
|
&rule->au_ctxt.range.level[0]) &&
|
||||||
|
!mls_level_eq(level,
|
||||||
|
&rule->au_ctxt.range.level[0]));
|
||||||
|
break;
|
||||||
|
case AUDIT_GREATER_THAN_OR_EQUAL:
|
||||||
|
match = mls_level_dom(level,
|
||||||
|
&rule->au_ctxt.range.level[0]);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
out:
|
||||||
|
POLICY_RDUNLOCK;
|
||||||
|
return match;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int (*aurule_callback)(void) = NULL;
|
||||||
|
|
||||||
|
static int aurule_avc_callback(u32 event, u32 ssid, u32 tsid,
|
||||||
|
u16 class, u32 perms, u32 *retained)
|
||||||
|
{
|
||||||
|
int err = 0;
|
||||||
|
|
||||||
|
if (event == AVC_CALLBACK_RESET && aurule_callback)
|
||||||
|
err = aurule_callback();
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int __init aurule_init(void)
|
||||||
|
{
|
||||||
|
int err;
|
||||||
|
|
||||||
|
err = avc_add_callback(aurule_avc_callback, AVC_CALLBACK_RESET,
|
||||||
|
SECSID_NULL, SECSID_NULL, SECCLASS_NULL, 0);
|
||||||
|
if (err)
|
||||||
|
panic("avc_add_callback() failed, error %d\n", err);
|
||||||
|
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
__initcall(aurule_init);
|
||||||
|
|
||||||
|
void selinux_audit_set_callback(int (*callback)(void))
|
||||||
|
{
|
||||||
|
aurule_callback = callback;
|
||||||
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue