]> Pileus Git - ~andy/linux/blobdiff - kernel/audit.c
audit: fix event coverage of AUDIT_ANOM_LINK
[~andy/linux] / kernel / audit.c
index bf1e1330cbb159af035118d6aae7412ea2e4d3bf..8cc5803169480f1b76e3fee2e60df32f6a3a5985 100644 (file)
@@ -49,6 +49,8 @@
 #include <linux/slab.h>
 #include <linux/err.h>
 #include <linux/kthread.h>
+#include <linux/kernel.h>
+#include <linux/syscalls.h>
 
 #include <linux/audit.h>
 
@@ -269,31 +271,15 @@ static int audit_log_config_change(char *function_name, int new, int old,
 {
        struct audit_buffer *ab;
        int rc = 0;
-       u32 sessionid = audit_get_sessionid(current);
-       uid_t auid = from_kuid(&init_user_ns, audit_get_loginuid(current));
-       u32 sid;
-
 
        ab = audit_log_start(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE);
        if (unlikely(!ab))
                return rc;
-       audit_log_format(ab, "%s=%d old=%d auid=%u ses=%u", function_name, new,
-                        old, auid, sessionid);
-
-       security_task_getsecid(current, &sid);
-       if (sid) {
-               char *ctx = NULL;
-               u32 len;
-
-               rc = security_secid_to_secctx(sid, &ctx, &len);
-               if (rc) {
-                       audit_log_format(ab, " sid=%u", sid);
-                       allow_changes = 0; /* Something weird, deny request */
-               } else {
-                       audit_log_format(ab, " subj=%s", ctx);
-                       security_release_secctx(ctx, len);
-               }
-       }
+       audit_log_format(ab, "%s=%d old=%d", function_name, new, old);
+       audit_log_session_info(ab);
+       rc = audit_log_task_context(ab);
+       if (rc)
+               allow_changes = 0; /* Something weird, deny request */
        audit_log_format(ab, " res=%d", allow_changes);
        audit_log_end(ab);
        return rc;
@@ -625,12 +611,7 @@ static int audit_netlink_ok(struct sk_buff *skb, u16 msg_type)
 static int audit_log_common_recv_msg(struct audit_buffer **ab, u16 msg_type)
 {
        int rc = 0;
-       char *ctx = NULL;
-       u32 len;
-       u32 sessionid = audit_get_sessionid(current);
        uid_t uid = from_kuid(&init_user_ns, current_uid());
-       uid_t auid = from_kuid(&init_user_ns, audit_get_loginuid(current));
-       u32 sid;
 
        if (!audit_enabled) {
                *ab = NULL;
@@ -640,18 +621,9 @@ static int audit_log_common_recv_msg(struct audit_buffer **ab, u16 msg_type)
        *ab = audit_log_start(NULL, GFP_KERNEL, msg_type);
        if (unlikely(!*ab))
                return rc;
-       audit_log_format(*ab, "pid=%d uid=%u auid=%u ses=%u",
-                        task_tgid_vnr(current), uid, auid, sessionid);
-       security_task_getsecid(current, &sid);
-       if (sid) {
-               rc = security_secid_to_secctx(sid, &ctx, &len);
-               if (rc)
-                       audit_log_format(*ab, " ssid=%u", sid);
-               else {
-                       audit_log_format(*ab, " subj=%s", ctx);
-                       security_release_secctx(ctx, len);
-               }
-       }
+       audit_log_format(*ab, "pid=%d uid=%u", task_tgid_vnr(current), uid);
+       audit_log_session_info(*ab);
+       audit_log_task_context(*ab);
 
        return rc;
 }
@@ -727,7 +699,7 @@ static int audit_receive_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
                if (err == 1) {
                        err = 0;
                        if (msg_type == AUDIT_USER_TTY) {
-                               err = tty_audit_push_task(current);
+                               err = tty_audit_push_current();
                                if (err)
                                        break;
                        }
@@ -835,27 +807,30 @@ static int audit_receive_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
                struct audit_tty_status s;
                struct task_struct *tsk = current;
 
-               spin_lock_irq(&tsk->sighand->siglock);
+               spin_lock(&tsk->sighand->siglock);
                s.enabled = tsk->signal->audit_tty != 0;
-               spin_unlock_irq(&tsk->sighand->siglock);
+               s.log_passwd = tsk->signal->audit_tty_log_passwd;
+               spin_unlock(&tsk->sighand->siglock);
 
                audit_send_reply(NETLINK_CB(skb).portid, seq,
                                 AUDIT_TTY_GET, 0, 0, &s, sizeof(s));
                break;
        }
        case AUDIT_TTY_SET: {
-               struct audit_tty_status *s;
+               struct audit_tty_status s;
                struct task_struct *tsk = current;
 
-               if (nlh->nlmsg_len < sizeof(struct audit_tty_status))
-                       return -EINVAL;
-               s = data;
-               if (s->enabled != 0 && s->enabled != 1)
+               memset(&s, 0, sizeof(s));
+               /* guard against past and future API changes */
+               memcpy(&s, data, min(sizeof(s), (size_t)nlh->nlmsg_len));
+               if ((s.enabled != 0 && s.enabled != 1) ||
+                   (s.log_passwd != 0 && s.log_passwd != 1))
                        return -EINVAL;
 
-               spin_lock_irq(&tsk->sighand->siglock);
-               tsk->signal->audit_tty = s->enabled != 0;
-               spin_unlock_irq(&tsk->sighand->siglock);
+               spin_lock(&tsk->sighand->siglock);
+               tsk->signal->audit_tty = s.enabled;
+               tsk->signal->audit_tty_log_passwd = s.log_passwd;
+               spin_unlock(&tsk->sighand->siglock);
                break;
        }
        default:
@@ -1402,6 +1377,14 @@ void audit_log_d_path(struct audit_buffer *ab, const char *prefix,
        kfree(pathname);
 }
 
+void audit_log_session_info(struct audit_buffer *ab)
+{
+       u32 sessionid = audit_get_sessionid(current);
+       uid_t auid = from_kuid(&init_user_ns, audit_get_loginuid(current));
+
+       audit_log_format(ab, "auid=%u ses=%u\n", auid, sessionid);
+}
+
 void audit_log_key(struct audit_buffer *ab, char *key)
 {
        audit_log_format(ab, " key=");
@@ -1411,6 +1394,224 @@ void audit_log_key(struct audit_buffer *ab, char *key)
                audit_log_format(ab, "(null)");
 }
 
+void audit_log_cap(struct audit_buffer *ab, char *prefix, kernel_cap_t *cap)
+{
+       int i;
+
+       audit_log_format(ab, " %s=", prefix);
+       CAP_FOR_EACH_U32(i) {
+               audit_log_format(ab, "%08x",
+                                cap->cap[(_KERNEL_CAPABILITY_U32S-1) - i]);
+       }
+}
+
+void audit_log_fcaps(struct audit_buffer *ab, struct audit_names *name)
+{
+       kernel_cap_t *perm = &name->fcap.permitted;
+       kernel_cap_t *inh = &name->fcap.inheritable;
+       int log = 0;
+
+       if (!cap_isclear(*perm)) {
+               audit_log_cap(ab, "cap_fp", perm);
+               log = 1;
+       }
+       if (!cap_isclear(*inh)) {
+               audit_log_cap(ab, "cap_fi", inh);
+               log = 1;
+       }
+
+       if (log)
+               audit_log_format(ab, " cap_fe=%d cap_fver=%x",
+                                name->fcap.fE, name->fcap_ver);
+}
+
+static inline int audit_copy_fcaps(struct audit_names *name,
+                                  const struct dentry *dentry)
+{
+       struct cpu_vfs_cap_data caps;
+       int rc;
+
+       if (!dentry)
+               return 0;
+
+       rc = get_vfs_caps_from_disk(dentry, &caps);
+       if (rc)
+               return rc;
+
+       name->fcap.permitted = caps.permitted;
+       name->fcap.inheritable = caps.inheritable;
+       name->fcap.fE = !!(caps.magic_etc & VFS_CAP_FLAGS_EFFECTIVE);
+       name->fcap_ver = (caps.magic_etc & VFS_CAP_REVISION_MASK) >>
+                               VFS_CAP_REVISION_SHIFT;
+
+       return 0;
+}
+
+/* Copy inode data into an audit_names. */
+void audit_copy_inode(struct audit_names *name, const struct dentry *dentry,
+                     const struct inode *inode)
+{
+       name->ino   = inode->i_ino;
+       name->dev   = inode->i_sb->s_dev;
+       name->mode  = inode->i_mode;
+       name->uid   = inode->i_uid;
+       name->gid   = inode->i_gid;
+       name->rdev  = inode->i_rdev;
+       security_inode_getsecid(inode, &name->osid);
+       audit_copy_fcaps(name, dentry);
+}
+
+/**
+ * audit_log_name - produce AUDIT_PATH record from struct audit_names
+ * @context: audit_context for the task
+ * @n: audit_names structure with reportable details
+ * @path: optional path to report instead of audit_names->name
+ * @record_num: record number to report when handling a list of names
+ * @call_panic: optional pointer to int that will be updated if secid fails
+ */
+void audit_log_name(struct audit_context *context, struct audit_names *n,
+                   struct path *path, int record_num, int *call_panic)
+{
+       struct audit_buffer *ab;
+       ab = audit_log_start(context, GFP_KERNEL, AUDIT_PATH);
+       if (!ab)
+               return;
+
+       audit_log_format(ab, "item=%d", record_num);
+
+       if (path)
+               audit_log_d_path(ab, " name=", path);
+       else if (n->name) {
+               switch (n->name_len) {
+               case AUDIT_NAME_FULL:
+                       /* log the full path */
+                       audit_log_format(ab, " name=");
+                       audit_log_untrustedstring(ab, n->name->name);
+                       break;
+               case 0:
+                       /* name was specified as a relative path and the
+                        * directory component is the cwd */
+                       audit_log_d_path(ab, " name=", &context->pwd);
+                       break;
+               default:
+                       /* log the name's directory component */
+                       audit_log_format(ab, " name=");
+                       audit_log_n_untrustedstring(ab, n->name->name,
+                                                   n->name_len);
+               }
+       } else
+               audit_log_format(ab, " name=(null)");
+
+       if (n->ino != (unsigned long)-1) {
+               audit_log_format(ab, " inode=%lu"
+                                " dev=%02x:%02x mode=%#ho"
+                                " ouid=%u ogid=%u rdev=%02x:%02x",
+                                n->ino,
+                                MAJOR(n->dev),
+                                MINOR(n->dev),
+                                n->mode,
+                                from_kuid(&init_user_ns, n->uid),
+                                from_kgid(&init_user_ns, n->gid),
+                                MAJOR(n->rdev),
+                                MINOR(n->rdev));
+       }
+       if (n->osid != 0) {
+               char *ctx = NULL;
+               u32 len;
+               if (security_secid_to_secctx(
+                       n->osid, &ctx, &len)) {
+                       audit_log_format(ab, " osid=%u", n->osid);
+                       if (call_panic)
+                               *call_panic = 2;
+               } else {
+                       audit_log_format(ab, " obj=%s", ctx);
+                       security_release_secctx(ctx, len);
+               }
+       }
+
+       audit_log_fcaps(ab, n);
+       audit_log_end(ab);
+}
+
+int audit_log_task_context(struct audit_buffer *ab)
+{
+       char *ctx = NULL;
+       unsigned len;
+       int error;
+       u32 sid;
+
+       security_task_getsecid(current, &sid);
+       if (!sid)
+               return 0;
+
+       error = security_secid_to_secctx(sid, &ctx, &len);
+       if (error) {
+               if (error != -EINVAL)
+                       goto error_path;
+               return 0;
+       }
+
+       audit_log_format(ab, " subj=%s", ctx);
+       security_release_secctx(ctx, len);
+       return 0;
+
+error_path:
+       audit_panic("error in audit_log_task_context");
+       return error;
+}
+EXPORT_SYMBOL(audit_log_task_context);
+
+void audit_log_task_info(struct audit_buffer *ab, struct task_struct *tsk)
+{
+       const struct cred *cred;
+       char name[sizeof(tsk->comm)];
+       struct mm_struct *mm = tsk->mm;
+       char *tty;
+
+       if (!ab)
+               return;
+
+       /* tsk == current */
+       cred = current_cred();
+
+       spin_lock_irq(&tsk->sighand->siglock);
+       if (tsk->signal && tsk->signal->tty && tsk->signal->tty->name)
+               tty = tsk->signal->tty->name;
+       else
+               tty = "(none)";
+       spin_unlock_irq(&tsk->sighand->siglock);
+
+       audit_log_format(ab,
+                        " ppid=%ld pid=%d auid=%u uid=%u gid=%u"
+                        " euid=%u suid=%u fsuid=%u"
+                        " egid=%u sgid=%u fsgid=%u ses=%u tty=%s",
+                        sys_getppid(),
+                        tsk->pid,
+                        from_kuid(&init_user_ns, audit_get_loginuid(tsk)),
+                        from_kuid(&init_user_ns, cred->uid),
+                        from_kgid(&init_user_ns, cred->gid),
+                        from_kuid(&init_user_ns, cred->euid),
+                        from_kuid(&init_user_ns, cred->suid),
+                        from_kuid(&init_user_ns, cred->fsuid),
+                        from_kgid(&init_user_ns, cred->egid),
+                        from_kgid(&init_user_ns, cred->sgid),
+                        from_kgid(&init_user_ns, cred->fsgid),
+                        audit_get_sessionid(tsk), tty);
+
+       get_task_comm(name, tsk);
+       audit_log_format(ab, " comm=");
+       audit_log_untrustedstring(ab, name);
+
+       if (mm) {
+               down_read(&mm->mmap_sem);
+               if (mm->exe_file)
+                       audit_log_d_path(ab, " exe=", &mm->exe_file->f_path);
+               up_read(&mm->mmap_sem);
+       }
+       audit_log_task_context(ab);
+}
+EXPORT_SYMBOL(audit_log_task_info);
+
 /**
  * audit_log_link_denied - report a link restriction denial
  * @operation: specific link opreation
@@ -1419,19 +1620,28 @@ void audit_log_key(struct audit_buffer *ab, char *key)
 void audit_log_link_denied(const char *operation, struct path *link)
 {
        struct audit_buffer *ab;
+       struct audit_names *name;
 
+       name = kzalloc(sizeof(*name), GFP_NOFS);
+       if (!name)
+               return;
+
+       /* Generate AUDIT_ANOM_LINK with subject, operation, outcome. */
        ab = audit_log_start(current->audit_context, GFP_KERNEL,
                             AUDIT_ANOM_LINK);
        if (!ab)
-               return;
-       audit_log_format(ab, "op=%s action=denied", operation);
-       audit_log_format(ab, " pid=%d comm=", current->pid);
-       audit_log_untrustedstring(ab, current->comm);
-       audit_log_d_path(ab, " path=", link);
-       audit_log_format(ab, " dev=");
-       audit_log_untrustedstring(ab, link->dentry->d_inode->i_sb->s_id);
-       audit_log_format(ab, " ino=%lu", link->dentry->d_inode->i_ino);
+               goto out;
+       audit_log_format(ab, "op=%s", operation);
+       audit_log_task_info(ab, current);
+       audit_log_format(ab, " res=0");
        audit_log_end(ab);
+
+       /* Generate AUDIT_PATH record with object. */
+       name->type = AUDIT_TYPE_NORMAL;
+       audit_copy_inode(name, link->dentry, link->dentry->d_inode);
+       audit_log_name(current->audit_context, name, link, 0, NULL);
+out:
+       kfree(name);
 }
 
 /**