audit: anchor all pid references in the initial pid namespace
[cascardo/linux.git] / kernel / auditsc.c
index 9845cb3..6381f25 100644 (file)
@@ -42,6 +42,8 @@
  * and <dustin.kirkland@us.ibm.com> for LSPP certification compliance.
  */
 
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
 #include <linux/init.h>
 #include <asm/types.h>
 #include <linux/atomic.h>
@@ -68,6 +70,7 @@
 #include <linux/capability.h>
 #include <linux/fs_struct.h>
 #include <linux/compat.h>
+#include <linux/ctype.h>
 
 #include "audit.h"
 
@@ -79,6 +82,9 @@
 /* no execve audit message should be longer than this (userspace limits) */
 #define MAX_EXECVE_AUDIT_LEN 7500
 
+/* max length to print of cmdline/proctitle value during audit */
+#define MAX_PROCTITLE_AUDIT_LEN 128
+
 /* number of audit rules */
 int audit_n_rules;
 
@@ -95,13 +101,6 @@ struct audit_aux_data {
 /* Number of target pids per aux struct. */
 #define AUDIT_AUX_PIDS 16
 
-struct audit_aux_data_execve {
-       struct audit_aux_data   d;
-       int argc;
-       int envc;
-       struct mm_struct *mm;
-};
-
 struct audit_aux_data_pids {
        struct audit_aux_data   d;
        pid_t                   target_pid[AUDIT_AUX_PIDS];
@@ -121,12 +120,6 @@ struct audit_aux_data_bprm_fcaps {
        struct audit_cap_data   new_pcap;
 };
 
-struct audit_aux_data_capset {
-       struct audit_aux_data   d;
-       pid_t                   pid;
-       struct audit_cap_data   cap;
-};
-
 struct audit_tree_refs {
        struct audit_tree_refs *next;
        struct audit_chunk *c[31];
@@ -464,15 +457,17 @@ static int audit_filter_rules(struct task_struct *tsk,
                struct audit_field *f = &rule->fields[i];
                struct audit_names *n;
                int result = 0;
+               pid_t pid;
 
                switch (f->type) {
                case AUDIT_PID:
-                       result = audit_comparator(tsk->pid, f->op, f->val);
+                       pid = task_pid_nr(tsk);
+                       result = audit_comparator(pid, f->op, f->val);
                        break;
                case AUDIT_PPID:
                        if (ctx) {
                                if (!ctx->ppid)
-                                       ctx->ppid = sys_getppid();
+                                       ctx->ppid = task_ppid_nr(tsk);
                                result = audit_comparator(ctx->ppid, f->op, f->val);
                        }
                        break;
@@ -566,7 +561,7 @@ static int audit_filter_rules(struct task_struct *tsk,
                        break;
                case AUDIT_INODE:
                        if (name)
-                               result = (name->ino == f->val);
+                               result = audit_comparator(name->ino, f->op, f->val);
                        else if (ctx) {
                                list_for_each_entry(n, &ctx->names_list, list) {
                                        if (audit_comparator(n->ino, f->op, f->val)) {
@@ -818,7 +813,8 @@ void audit_filter_inodes(struct task_struct *tsk, struct audit_context *ctx)
        rcu_read_unlock();
 }
 
-static inline struct audit_context *audit_get_context(struct task_struct *tsk,
+/* Transfer the audit context pointer to the caller, clearing it in the tsk's struct */
+static inline struct audit_context *audit_take_context(struct task_struct *tsk,
                                                      int return_valid,
                                                      long return_code)
 {
@@ -855,6 +851,13 @@ static inline struct audit_context *audit_get_context(struct task_struct *tsk,
        return context;
 }
 
+static inline void audit_proctitle_free(struct audit_context *context)
+{
+       kfree(context->proctitle.value);
+       context->proctitle.value = NULL;
+       context->proctitle.len = 0;
+}
+
 static inline void audit_free_names(struct audit_context *context)
 {
        struct audit_names *n, *next;
@@ -863,16 +866,15 @@ static inline void audit_free_names(struct audit_context *context)
        if (context->put_count + context->ino_count != context->name_count) {
                int i = 0;
 
-               printk(KERN_ERR "%s:%d(:%d): major=%d in_syscall=%d"
-                      " name_count=%d put_count=%d"
-                      " ino_count=%d [NOT freeing]\n",
-                      __FILE__, __LINE__,
+               pr_err("%s:%d(:%d): major=%d in_syscall=%d"
+                      " name_count=%d put_count=%d ino_count=%d"
+                      " [NOT freeing]\n", __FILE__, __LINE__,
                       context->serial, context->major, context->in_syscall,
                       context->name_count, context->put_count,
                       context->ino_count);
                list_for_each_entry(n, &context->names_list, list) {
-                       printk(KERN_ERR "names[%d] = %p = %s\n", i++,
-                              n->name, n->name->name ?: "(null)");
+                       pr_err("names[%d] = %p = %s\n", i++, n->name,
+                              n->name->name ?: "(null)");
                }
                dump_stack();
                return;
@@ -943,8 +945,10 @@ int audit_alloc(struct task_struct *tsk)
                return 0; /* Return if not auditing. */
 
        state = audit_filter_task(tsk, &key);
-       if (state == AUDIT_DISABLED)
+       if (state == AUDIT_DISABLED) {
+               clear_tsk_thread_flag(tsk, TIF_SYSCALL_AUDIT);
                return 0;
+       }
 
        if (!(context = audit_alloc_context(state))) {
                kfree(key);
@@ -966,6 +970,7 @@ static inline void audit_free_context(struct audit_context *context)
        audit_free_aux(context);
        kfree(context->filterkey);
        kfree(context->sockaddr);
+       audit_proctitle_free(context);
        kfree(context);
 }
 
@@ -1149,20 +1154,16 @@ static int audit_log_single_execve_arg(struct audit_context *context,
 }
 
 static void audit_log_execve_info(struct audit_context *context,
-                                 struct audit_buffer **ab,
-                                 struct audit_aux_data_execve *axi)
+                                 struct audit_buffer **ab)
 {
        int i, len;
        size_t len_sent = 0;
        const char __user *p;
        char *buf;
 
-       if (axi->mm != current->mm)
-               return; /* execve failed, no additional info */
+       p = (const char __user *)current->mm->arg_start;
 
-       p = (const char __user *)axi->mm->arg_start;
-
-       audit_log_format(*ab, "argc=%d", axi->argc);
+       audit_log_format(*ab, "argc=%d", context->execve.argc);
 
        /*
         * we need some kernel buffer to hold the userspace args.  Just
@@ -1176,7 +1177,7 @@ static void audit_log_execve_info(struct audit_context *context,
                return;
        }
 
-       for (i = 0; i < axi->argc; i++) {
+       for (i = 0; i < context->execve.argc; i++) {
                len = audit_log_single_execve_arg(context, ab, i,
                                                  &len_sent, p, buf);
                if (len <= 0)
@@ -1279,7 +1280,63 @@ static void show_special(struct audit_context *context, int *call_panic)
                audit_log_format(ab, "fd=%d flags=0x%x", context->mmap.fd,
                                 context->mmap.flags);
                break; }
+       case AUDIT_EXECVE: {
+               audit_log_execve_info(context, &ab);
+               break; }
+       }
+       audit_log_end(ab);
+}
+
+static inline int audit_proctitle_rtrim(char *proctitle, int len)
+{
+       char *end = proctitle + len - 1;
+       while (end > proctitle && !isprint(*end))
+               end--;
+
+       /* catch the case where proctitle is only 1 non-print character */
+       len = end - proctitle + 1;
+       len -= isprint(proctitle[len-1]) == 0;
+       return len;
+}
+
+static void audit_log_proctitle(struct task_struct *tsk,
+                        struct audit_context *context)
+{
+       int res;
+       char *buf;
+       char *msg = "(null)";
+       int len = strlen(msg);
+       struct audit_buffer *ab;
+
+       ab = audit_log_start(context, GFP_KERNEL, AUDIT_PROCTITLE);
+       if (!ab)
+               return; /* audit_panic or being filtered */
+
+       audit_log_format(ab, "proctitle=");
+
+       /* Not  cached */
+       if (!context->proctitle.value) {
+               buf = kmalloc(MAX_PROCTITLE_AUDIT_LEN, GFP_KERNEL);
+               if (!buf)
+                       goto out;
+               /* Historically called this from procfs naming */
+               res = get_cmdline(tsk, buf, MAX_PROCTITLE_AUDIT_LEN);
+               if (res == 0) {
+                       kfree(buf);
+                       goto out;
+               }
+               res = audit_proctitle_rtrim(buf, res);
+               if (res == 0) {
+                       kfree(buf);
+                       goto out;
+               }
+               context->proctitle.value = buf;
+               context->proctitle.len = res;
        }
+       msg = context->proctitle.value;
+       len = context->proctitle.len;
+out:
+       audit_log_n_untrustedstring(ab, msg, len);
        audit_log_end(ab);
 }
 
@@ -1325,11 +1382,6 @@ static void audit_log_exit(struct audit_context *context, struct task_struct *ts
 
                switch (aux->type) {
 
-               case AUDIT_EXECVE: {
-                       struct audit_aux_data_execve *axi = (void *)aux;
-                       audit_log_execve_info(context, &ab, axi);
-                       break; }
-
                case AUDIT_BPRM_FCAPS: {
                        struct audit_aux_data_bprm_fcaps *axs = (void *)aux;
                        audit_log_format(ab, "fver=%x", axs->fcap_ver);
@@ -1405,6 +1457,8 @@ static void audit_log_exit(struct audit_context *context, struct task_struct *ts
                audit_log_name(context, n, NULL, i++, &call_panic);
        }
 
+       audit_log_proctitle(tsk, context);
+
        /* Send end of event record to help user space know we are finished */
        ab = audit_log_start(context, GFP_KERNEL, AUDIT_EOE);
        if (ab)
@@ -1423,7 +1477,7 @@ void __audit_free(struct task_struct *tsk)
 {
        struct audit_context *context;
 
-       context = audit_get_context(tsk, 0, 0);
+       context = audit_take_context(tsk, 0, 0);
        if (!context)
                return;
 
@@ -1517,7 +1571,7 @@ void __audit_syscall_exit(int success, long return_code)
        else
                success = AUDITSC_FAILURE;
 
-       context = audit_get_context(tsk, success, return_code);
+       context = audit_take_context(tsk, success, return_code);
        if (!context)
                return;
 
@@ -1567,7 +1621,7 @@ static inline void handle_one(const struct inode *inode)
        if (likely(put_tree_ref(context, chunk)))
                return;
        if (unlikely(!grow_tree_refs(context))) {
-               printk(KERN_WARNING "out of memory, audit has lost a tree reference\n");
+               pr_warn("out of memory, audit has lost a tree reference\n");
                audit_set_auditable(context);
                audit_put_chunk(chunk);
                unroll_tree_refs(context, p, count);
@@ -1626,8 +1680,7 @@ retry:
                        goto retry;
                }
                /* too bad */
-               printk(KERN_WARNING
-                       "out of memory, audit has lost a tree reference\n");
+               pr_warn("out of memory, audit has lost a tree reference\n");
                unroll_tree_refs(context, p, count);
                audit_set_auditable(context);
                return;
@@ -1699,7 +1752,7 @@ void __audit_getname(struct filename *name)
 
        if (!context->in_syscall) {
 #if AUDIT_DEBUG == 2
-               printk(KERN_ERR "%s:%d(:%d): ignoring getname(%p)\n",
+               pr_err("%s:%d(:%d): ignoring getname(%p)\n",
                       __FILE__, __LINE__, context->serial, name);
                dump_stack();
 #endif
@@ -1738,15 +1791,15 @@ void audit_putname(struct filename *name)
        BUG_ON(!context);
        if (!context->in_syscall) {
 #if AUDIT_DEBUG == 2
-               printk(KERN_ERR "%s:%d(:%d): final_putname(%p)\n",
+               pr_err("%s:%d(:%d): final_putname(%p)\n",
                       __FILE__, __LINE__, context->serial, name);
                if (context->name_count) {
                        struct audit_names *n;
                        int i = 0;
 
                        list_for_each_entry(n, &context->names_list, list)
-                               printk(KERN_ERR "name[%d] = %p = %s\n", i++,
-                                      n->name, n->name->name ?: "(null)");
+                               pr_err("name[%d] = %p = %s\n", i++, n->name,
+                                      n->name->name ?: "(null)");
                        }
 #endif
                final_putname(name);
@@ -1755,9 +1808,8 @@ void audit_putname(struct filename *name)
        else {
                ++context->put_count;
                if (context->put_count > context->name_count) {
-                       printk(KERN_ERR "%s:%d(:%d): major=%d"
-                              " in_syscall=%d putname(%p) name_count=%d"
-                              " put_count=%d\n",
+                       pr_err("%s:%d(:%d): major=%d in_syscall=%d putname(%p)"
+                              " name_count=%d put_count=%d\n",
                               __FILE__, __LINE__,
                               context->serial, context->major,
                               context->in_syscall, name->name,
@@ -1964,6 +2016,49 @@ int auditsc_get_stamp(struct audit_context *ctx,
 /* global counter which is incremented every time something logs in */
 static atomic_t session_id = ATOMIC_INIT(0);
 
+static int audit_set_loginuid_perm(kuid_t loginuid)
+{
+       /* if we are unset, we don't need privs */
+       if (!audit_loginuid_set(current))
+               return 0;
+       /* if AUDIT_FEATURE_LOGINUID_IMMUTABLE means never ever allow a change*/
+       if (is_audit_feature_set(AUDIT_FEATURE_LOGINUID_IMMUTABLE))
+               return -EPERM;
+       /* it is set, you need permission */
+       if (!capable(CAP_AUDIT_CONTROL))
+               return -EPERM;
+       /* reject if this is not an unset and we don't allow that */
+       if (is_audit_feature_set(AUDIT_FEATURE_ONLY_UNSET_LOGINUID) && uid_valid(loginuid))
+               return -EPERM;
+       return 0;
+}
+
+static void audit_log_set_loginuid(kuid_t koldloginuid, kuid_t kloginuid,
+                                  unsigned int oldsessionid, unsigned int sessionid,
+                                  int rc)
+{
+       struct audit_buffer *ab;
+       uid_t uid, oldloginuid, loginuid;
+
+       if (!audit_enabled)
+               return;
+
+       uid = from_kuid(&init_user_ns, task_uid(current));
+       oldloginuid = from_kuid(&init_user_ns, koldloginuid);
+       loginuid = from_kuid(&init_user_ns, kloginuid),
+
+       ab = audit_log_start(NULL, GFP_KERNEL, AUDIT_LOGIN);
+       if (!ab)
+               return;
+       audit_log_format(ab, "pid=%d uid=%u"
+                        " old-auid=%u new-auid=%u old-ses=%u new-ses=%u"
+                        " res=%d",
+                        task_pid_nr(current), uid,
+                        oldloginuid, loginuid, oldsessionid, sessionid,
+                        !rc);
+       audit_log_end(ab);
+}
+
 /**
  * audit_set_loginuid - set current task's audit_context loginuid
  * @loginuid: loginuid value
@@ -1975,37 +2070,26 @@ static atomic_t session_id = ATOMIC_INIT(0);
 int audit_set_loginuid(kuid_t loginuid)
 {
        struct task_struct *task = current;
-       struct audit_context *context = task->audit_context;
-       unsigned int sessionid;
+       unsigned int oldsessionid, sessionid = (unsigned int)-1;
+       kuid_t oldloginuid;
+       int rc;
 
-#ifdef CONFIG_AUDIT_LOGINUID_IMMUTABLE
-       if (audit_loginuid_set(task))
-               return -EPERM;
-#else /* CONFIG_AUDIT_LOGINUID_IMMUTABLE */
-       if (!capable(CAP_AUDIT_CONTROL))
-               return -EPERM;
-#endif  /* CONFIG_AUDIT_LOGINUID_IMMUTABLE */
+       oldloginuid = audit_get_loginuid(current);
+       oldsessionid = audit_get_sessionid(current);
 
-       sessionid = atomic_inc_return(&session_id);
-       if (context && context->in_syscall) {
-               struct audit_buffer *ab;
+       rc = audit_set_loginuid_perm(loginuid);
+       if (rc)
+               goto out;
+
+       /* are we setting or clearing? */
+       if (uid_valid(loginuid))
+               sessionid = (unsigned int)atomic_inc_return(&session_id);
 
-               ab = audit_log_start(NULL, GFP_KERNEL, AUDIT_LOGIN);
-               if (ab) {
-                       audit_log_format(ab, "login pid=%d uid=%u "
-                               "old auid=%u new auid=%u"
-                               " old ses=%u new ses=%u",
-                               task->pid,
-                               from_kuid(&init_user_ns, task_uid(task)),
-                               from_kuid(&init_user_ns, task->loginuid),
-                               from_kuid(&init_user_ns, loginuid),
-                               task->sessionid, sessionid);
-                       audit_log_end(ab);
-               }
-       }
        task->sessionid = sessionid;
        task->loginuid = loginuid;
-       return 0;
+out:
+       audit_log_set_loginuid(oldloginuid, loginuid, oldsessionid, sessionid, rc);
+       return rc;
 }
 
 /**
@@ -2126,22 +2210,12 @@ void __audit_ipc_set_perm(unsigned long qbytes, uid_t uid, gid_t gid, umode_t mo
        context->ipc.has_perm = 1;
 }
 
-int __audit_bprm(struct linux_binprm *bprm)
+void __audit_bprm(struct linux_binprm *bprm)
 {
-       struct audit_aux_data_execve *ax;
        struct audit_context *context = current->audit_context;
 
-       ax = kmalloc(sizeof(*ax), GFP_KERNEL);
-       if (!ax)
-               return -ENOMEM;
-
-       ax->argc = bprm->argc;
-       ax->envc = bprm->envc;
-       ax->mm = bprm->mm;
-       ax->d.type = AUDIT_EXECVE;
-       ax->d.next = context->aux;
-       context->aux = (void *)ax;
-       return 0;
+       context->type = AUDIT_EXECVE;
+       context->execve.argc = bprm->argc;
 }
 
 
@@ -2203,7 +2277,7 @@ void __audit_ptrace(struct task_struct *t)
 {
        struct audit_context *context = current->audit_context;
 
-       context->target_pid = t->pid;
+       context->target_pid = task_pid_nr(t);
        context->target_auid = audit_get_loginuid(t);
        context->target_uid = task_uid(t);
        context->target_sessionid = audit_get_sessionid(t);
@@ -2228,7 +2302,7 @@ int __audit_signal_info(int sig, struct task_struct *t)
 
        if (audit_pid && t->tgid == audit_pid) {
                if (sig == SIGTERM || sig == SIGHUP || sig == SIGUSR1 || sig == SIGUSR2) {
-                       audit_sig_pid = tsk->pid;
+                       audit_sig_pid = task_pid_nr(tsk);
                        if (uid_valid(tsk->loginuid))
                                audit_sig_uid = tsk->loginuid;
                        else
@@ -2242,7 +2316,7 @@ int __audit_signal_info(int sig, struct task_struct *t)
        /* optimize the common case by putting first signal recipient directly
         * in audit_context */
        if (!ctx->target_pid) {
-               ctx->target_pid = t->tgid;
+               ctx->target_pid = task_tgid_nr(t);
                ctx->target_auid = audit_get_loginuid(t);
                ctx->target_uid = t_uid;
                ctx->target_sessionid = audit_get_sessionid(t);
@@ -2263,7 +2337,7 @@ int __audit_signal_info(int sig, struct task_struct *t)
        }
        BUG_ON(axp->pid_count >= AUDIT_AUX_PIDS);
 
-       axp->target_pid[axp->pid_count] = t->tgid;
+       axp->target_pid[axp->pid_count] = task_tgid_nr(t);
        axp->target_auid[axp->pid_count] = audit_get_loginuid(t);
        axp->target_uid[axp->pid_count] = t_uid;
        axp->target_sessionid[axp->pid_count] = audit_get_sessionid(t);
@@ -2322,18 +2396,16 @@ int __audit_log_bprm_fcaps(struct linux_binprm *bprm,
 
 /**
  * __audit_log_capset - store information about the arguments to the capset syscall
- * @pid: target pid of the capset call
  * @new: the new credentials
  * @old: the old (current) credentials
  *
  * Record the aguments userspace sent to sys_capset for later printing by the
  * audit system if applicable
  */
-void __audit_log_capset(pid_t pid,
-                      const struct cred *new, const struct cred *old)
+void __audit_log_capset(const struct cred *new, const struct cred *old)
 {
        struct audit_context *context = current->audit_context;
-       context->capset.pid = pid;
+       context->capset.pid = task_pid_nr(current);
        context->capset.cap.effective   = new->cap_effective;
        context->capset.cap.inheritable = new->cap_effective;
        context->capset.cap.permitted   = new->cap_permitted;
@@ -2353,6 +2425,7 @@ static void audit_log_task(struct audit_buffer *ab)
        kuid_t auid, uid;
        kgid_t gid;
        unsigned int sessionid;
+       struct mm_struct *mm = current->mm;
 
        auid = audit_get_loginuid(current);
        sessionid = audit_get_sessionid(current);
@@ -2364,17 +2437,17 @@ static void audit_log_task(struct audit_buffer *ab)
                         from_kgid(&init_user_ns, gid),
                         sessionid);
        audit_log_task_context(ab);
-       audit_log_format(ab, " pid=%d comm=", current->pid);
+       audit_log_format(ab, " pid=%d comm=", task_pid_nr(current));
        audit_log_untrustedstring(ab, current->comm);
+       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);
+       } else
+               audit_log_format(ab, " exe=(null)");
 }
 
-static void audit_log_abend(struct audit_buffer *ab, char *reason, long signr)
-{
-       audit_log_task(ab);
-       audit_log_format(ab, " reason=");
-       audit_log_string(ab, reason);
-       audit_log_format(ab, " sig=%ld", signr);
-}
 /**
  * audit_core_dumps - record information about processes that end abnormally
  * @signr: signal value
@@ -2395,7 +2468,8 @@ void audit_core_dumps(long signr)
        ab = audit_log_start(NULL, GFP_KERNEL, AUDIT_ANOM_ABEND);
        if (unlikely(!ab))
                return;
-       audit_log_abend(ab, "memory violation", signr);
+       audit_log_task(ab);
+       audit_log_format(ab, " sig=%ld", signr);
        audit_log_end(ab);
 }