Commit d5290d88 authored by Serge Hallyn's avatar Serge Hallyn Committed by Linus Torvalds

[PATCH] audit: handle loginuid through proc

The audit subsystem uses netlink messages to request loginuid changes.  Due
to the sensitivity of loginuid, netlink appears to be insufficient.  For
instance, it is not easy to guarantee that the loginuid message will be
handled before any other auditable actions, and there is even the remote
possibility of the process terminating and another process with the same
pid being created before the message is handled.  Finally, other kernel
code, in particular selinux, is interested in easily querying the loginuid
for inclusion in its own messages.

The following patch moves loginuid handling from netlink to the
/proc/$$/loginuid file, and adds a audit_get_loginuid() function.  It also
includes Stephen Smalley's patch to correctly inherit the loginuid on fork.
 It has been actively discussed on the linux-audit mailing list.
Signed-off-by: default avatarSerge Hallyn <serue@us.ibm.com>
Signed-off-by: default avatarAndrew Morton <akpm@osdl.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@osdl.org>
parent fe3a3a34
......@@ -70,6 +70,9 @@ enum pid_directory_inos {
PROC_TGID_ATTR_PREV,
PROC_TGID_ATTR_EXEC,
PROC_TGID_ATTR_FSCREATE,
#endif
#ifdef CONFIG_AUDITSYSCALL
PROC_TGID_LOGINUID,
#endif
PROC_TGID_FD_DIR,
PROC_TGID_OOM_SCORE,
......@@ -98,6 +101,9 @@ enum pid_directory_inos {
PROC_TID_ATTR_PREV,
PROC_TID_ATTR_EXEC,
PROC_TID_ATTR_FSCREATE,
#endif
#ifdef CONFIG_AUDITSYSCALL
PROC_TID_LOGINUID,
#endif
PROC_TID_FD_DIR = 0x8000, /* 0x8000-0xffff */
PROC_TID_OOM_SCORE,
......@@ -139,6 +145,9 @@ static struct pid_entry tgid_base_stuff[] = {
#endif
E(PROC_TGID_OOM_SCORE, "oom_score",S_IFREG|S_IRUGO),
E(PROC_TGID_OOM_ADJUST,"oom_adj", S_IFREG|S_IRUGO|S_IWUSR),
#ifdef CONFIG_AUDITSYSCALL
E(PROC_TGID_LOGINUID, "loginuid", S_IFREG|S_IWUSR|S_IRUGO),
#endif
{0,0,NULL,0}
};
static struct pid_entry tid_base_stuff[] = {
......@@ -166,6 +175,9 @@ static struct pid_entry tid_base_stuff[] = {
#endif
E(PROC_TID_OOM_SCORE, "oom_score",S_IFREG|S_IRUGO),
E(PROC_TID_OOM_ADJUST, "oom_adj", S_IFREG|S_IRUGO|S_IWUSR),
#ifdef CONFIG_AUDITSYSCALL
E(PROC_TID_LOGINUID, "loginuid", S_IFREG|S_IWUSR|S_IRUGO),
#endif
{0,0,NULL,0}
};
......@@ -731,6 +743,71 @@ static struct inode_operations proc_mem_inode_operations = {
.permission = proc_permission,
};
#ifdef CONFIG_AUDITSYSCALL
#define TMPBUFLEN 21
static ssize_t proc_loginuid_read(struct file * file, char __user * buf,
size_t count, loff_t *ppos)
{
struct inode * inode = file->f_dentry->d_inode;
struct task_struct *task = proc_task(inode);
ssize_t length;
char tmpbuf[TMPBUFLEN];
length = scnprintf(tmpbuf, TMPBUFLEN, "%u",
audit_get_loginuid(task->audit_context));
return simple_read_from_buffer(buf, count, ppos, tmpbuf, length);
}
static ssize_t proc_loginuid_write(struct file * file, const char __user * buf,
size_t count, loff_t *ppos)
{
struct inode * inode = file->f_dentry->d_inode;
char *page, *tmp;
ssize_t length;
struct task_struct *task = proc_task(inode);
uid_t loginuid;
if (!capable(CAP_AUDIT_CONTROL))
return -EPERM;
if (current != task)
return -EPERM;
if (count > PAGE_SIZE)
count = PAGE_SIZE;
if (*ppos != 0) {
/* No partial writes. */
return -EINVAL;
}
page = (char*)__get_free_page(GFP_USER);
if (!page)
return -ENOMEM;
length = -EFAULT;
if (copy_from_user(page, buf, count))
goto out_free_page;
loginuid = simple_strtoul(page, &tmp, 10);
if (tmp == page) {
length = -EINVAL;
goto out_free_page;
}
length = audit_set_loginuid(task->audit_context, loginuid);
if (likely(length == 0))
length = count;
out_free_page:
free_page((unsigned long) page);
return length;
}
static struct file_operations proc_loginuid_operations = {
.read = proc_loginuid_read,
.write = proc_loginuid_write,
};
#endif
static int proc_pid_follow_link(struct dentry *dentry, struct nameidata *nd)
{
struct inode *inode = dentry->d_inode;
......@@ -1415,6 +1492,12 @@ static struct dentry *proc_pident_lookup(struct inode *dir,
case PROC_TGID_OOM_ADJUST:
inode->i_fop = &proc_oom_adjust_operations;
break;
#ifdef CONFIG_AUDITSYSCALL
case PROC_TID_LOGINUID:
case PROC_TGID_LOGINUID:
inode->i_fop = &proc_loginuid_operations;
break;
#endif
default:
printk("procfs: impossible type (%d)",p->type);
iput(inode);
......
......@@ -114,12 +114,6 @@ struct audit_status {
__u32 backlog; /* messages waiting in queue */
};
struct audit_login {
__u32 loginuid;
int msglen;
char msg[1024];
};
struct audit_rule { /* for AUDIT_LIST, AUDIT_ADD, and AUDIT_DEL */
__u32 flags; /* AUDIT_PER_{TASK,CALL}, AUDIT_PREPEND */
__u32 action; /* AUDIT_NEVER, AUDIT_POSSIBLE, AUDIT_ALWAYS */
......@@ -155,6 +149,7 @@ extern int audit_receive_filter(int type, int pid, int uid, int seq,
extern void audit_get_stamp(struct audit_context *ctx,
struct timespec *t, int *serial);
extern int audit_set_loginuid(struct audit_context *ctx, uid_t loginuid);
extern uid_t audit_get_loginuid(struct audit_context *ctx);
#else
#define audit_alloc(t) ({ 0; })
#define audit_free(t) do { ; } while (0)
......@@ -163,6 +158,7 @@ extern int audit_set_loginuid(struct audit_context *ctx, uid_t loginuid);
#define audit_getname(n) do { ; } while (0)
#define audit_putname(n) do { ; } while (0)
#define audit_inode(n,i,d) do { ; } while (0)
#define audit_get_loginuid(c) ({ -1; })
#endif
#ifdef CONFIG_AUDIT
......
......@@ -145,6 +145,11 @@ struct audit_buffer {
int count; /* Times requeued */
};
void audit_set_type(struct audit_buffer *ab, int type)
{
ab->type = type;
}
struct audit_entry {
struct list_head list;
struct audit_rule rule;
......@@ -312,7 +317,6 @@ static int audit_netlink_ok(kernel_cap_t eff_cap, u16 msg_type)
case AUDIT_GET:
case AUDIT_LIST:
case AUDIT_SET:
case AUDIT_LOGIN:
case AUDIT_ADD:
case AUDIT_DEL:
if (!cap_raised(eff_cap, CAP_AUDIT_CONTROL))
......@@ -334,7 +338,6 @@ static int audit_receive_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
u32 uid, pid, seq;
void *data;
struct audit_status *status_get, status_set;
struct audit_login *login;
int err;
struct audit_buffer *ab;
u16 msg_type = nlh->nlmsg_type;
......@@ -397,27 +400,6 @@ static int audit_receive_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
ab->pid = pid;
audit_log_end(ab);
break;
case AUDIT_LOGIN:
if (nlh->nlmsg_len < sizeof(struct audit_login))
return -EINVAL;
login = (struct audit_login *)data;
ab = audit_log_start(NULL);
if (ab) {
audit_log_format(ab, "login pid=%d uid=%d loginuid=%d"
" length=%d msg='%.1024s'",
pid, uid,
login->loginuid,
login->msglen,
login->msg);
ab->type = AUDIT_LOGIN;
ab->pid = pid;
audit_log_end(ab);
}
#ifdef CONFIG_AUDITSYSCALL
err = audit_set_loginuid(current->audit_context,
login->loginuid);
#endif
break;
case AUDIT_ADD:
case AUDIT_DEL:
if (nlh->nlmsg_len < sizeof(struct audit_rule))
......
......@@ -547,8 +547,8 @@ int audit_alloc(struct task_struct *tsk)
/* Preserve login uid */
context->loginuid = -1;
if (tsk->audit_context)
context->loginuid = tsk->audit_context->loginuid;
if (current->audit_context)
context->loginuid = current->audit_context->loginuid;
tsk->audit_context = context;
set_tsk_thread_flag(tsk, TIF_SYSCALL_AUDIT);
......@@ -903,12 +903,27 @@ void audit_get_stamp(struct audit_context *ctx,
}
}
extern int audit_set_type(struct audit_buffer *ab, int type);
int audit_set_loginuid(struct audit_context *ctx, uid_t loginuid)
{
if (ctx) {
if (loginuid < 0)
return -EINVAL;
struct audit_buffer *ab;
ab = audit_log_start(NULL);
if (ab) {
audit_log_format(ab, "login pid=%d uid=%u "
"old loginuid=%u new loginuid=%u",
ctx->pid, ctx->uid, ctx->loginuid, loginuid);
audit_set_type(ab, AUDIT_LOGIN);
audit_log_end(ab);
}
ctx->loginuid = loginuid;
}
return 0;
}
uid_t audit_get_loginuid(struct audit_context *ctx)
{
return ctx ? ctx->loginuid : -1;
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment