]> git.hungrycats.org Git - linux/commitdiff
[PATCH] audit: handle loginuid through proc
authorSerge Hallyn <serue@us.ibm.com>
Wed, 2 Feb 2005 00:50:56 +0000 (16:50 -0800)
committerLinus Torvalds <torvalds@ppc970.osdl.org>
Wed, 2 Feb 2005 00:50:56 +0000 (16:50 -0800)
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: Serge Hallyn <serue@us.ibm.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
fs/proc/base.c
include/linux/audit.h
kernel/audit.c
kernel/auditsc.c

index 01ab57f385b9296cb39115802cba3f37f499fcf8..b30ee97c308f9133edfa2f80e7703026c98b2f09 100644 (file)
@@ -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);
index d766482451afbb01999204ad0da11ef9259c85c8..c27b028ec3c8d24d588aad8849a3f7405729c5cb 100644 (file)
@@ -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
index 480b6eaa06751c275577932cb4711670ce5f833e..9b48512a4cf57ecde505c7bae554a04b95382d1c 100644 (file)
@@ -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))
index 5efa31c1dc830b3131ec6fc1f62772b240367a69..c412d6779733a0929b74000b0394735128b5ebf0 100644 (file)
@@ -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;
+}