diff options
Diffstat (limited to 'kernel/auditsc.c')
| -rw-r--r-- | kernel/auditsc.c | 313 | 
1 files changed, 197 insertions, 116 deletions
diff --git a/kernel/auditsc.c b/kernel/auditsc.c index 9845cb32b60..21eae3c05ec 100644 --- a/kernel/auditsc.c +++ b/kernel/auditsc.c @@ -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)) { @@ -733,6 +728,22 @@ static enum audit_state audit_filter_task(struct task_struct *tsk, char **key)  	return AUDIT_BUILD_CONTEXT;  } +static int audit_in_mask(const struct audit_krule *rule, unsigned long val) +{ +	int word, bit; + +	if (val > 0xffffffff) +		return false; + +	word = AUDIT_WORD(val); +	if (word >= AUDIT_BITMASK_SIZE) +		return false; + +	bit = AUDIT_BIT(val); + +	return rule->mask[word] & bit; +} +  /* At syscall entry and exit time, this filter is called if the   * audit_state is not low enough that auditing cannot take place, but is   * also not high enough that we already know we have to write an audit @@ -750,11 +761,8 @@ static enum audit_state audit_filter_syscall(struct task_struct *tsk,  	rcu_read_lock();  	if (!list_empty(list)) { -		int word = AUDIT_WORD(ctx->major); -		int bit  = AUDIT_BIT(ctx->major); -  		list_for_each_entry_rcu(e, list, list) { -			if ((e->rule.mask[word] & bit) == bit && +			if (audit_in_mask(&e->rule, ctx->major) &&  			    audit_filter_rules(tsk, &e->rule, ctx, NULL,  					       &state, false)) {  				rcu_read_unlock(); @@ -774,20 +782,16 @@ static enum audit_state audit_filter_syscall(struct task_struct *tsk,  static int audit_filter_inode_name(struct task_struct *tsk,  				   struct audit_names *n,  				   struct audit_context *ctx) { -	int word, bit;  	int h = audit_hash_ino((u32)n->ino);  	struct list_head *list = &audit_inode_hash[h];  	struct audit_entry *e;  	enum audit_state state; -	word = AUDIT_WORD(ctx->major); -	bit  = AUDIT_BIT(ctx->major); -  	if (list_empty(list))  		return 0;  	list_for_each_entry_rcu(e, list, list) { -		if ((e->rule.mask[word] & bit) == bit && +		if (audit_in_mask(&e->rule, ctx->major) &&  		    audit_filter_rules(tsk, &e->rule, ctx, n, &state, false)) {  			ctx->current_state = state;  			return 1; @@ -818,7 +822,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 +860,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 +875,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 +954,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 +979,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 +1163,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 @@ -1172,11 +1182,11 @@ static void audit_log_execve_info(struct audit_context *context,  	 */  	buf = kmalloc(MAX_EXECVE_AUDIT_LEN + 1, GFP_KERNEL);  	if (!buf) { -		audit_panic("out of memory for argv string\n"); +		audit_panic("out of memory for argv string");  		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,10 +1289,66 @@ 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); +} +  static void audit_log_exit(struct audit_context *context, struct task_struct *tsk)  {  	int i, call_panic = 0; @@ -1325,11 +1391,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 +1466,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 +1486,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 +1580,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 +1630,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 +1689,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 +1761,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 @@ -1736,17 +1798,17 @@ void audit_putname(struct filename *name)  	struct audit_context *context = current->audit_context;  	BUG_ON(!context); -	if (!context->in_syscall) { +	if (!name->aname || !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 +1817,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 +2025,47 @@ 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", task_pid_nr(current), uid); +	audit_log_task_context(ab); +	audit_log_format(ab, " old-auid=%u auid=%u old-ses=%u ses=%u res=%d", +			 oldloginuid, loginuid, oldsessionid, sessionid, !rc); +	audit_log_end(ab); +} +  /**   * audit_set_loginuid - set current task's audit_context loginuid   * @loginuid: loginuid value @@ -1975,37 +2077,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 +2217,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 +2284,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 +2309,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 +2323,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 +2344,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 +2403,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 +2432,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 +2444,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 +2475,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);  }  | 
