Merge branch 'stable-4.8' of git://git.infradead.org/users/pcmoore/audit
Pull audit updates from Paul Moore: "Six audit patches for 4.8. There are a couple of style and minor whitespace tweaks for the logs, as well as a minor fixup to catch errors on user filter rules, however the major improvements are a fix to the s390 syscall argument masking code (reviewed by the nice s390 folks), some consolidation around the exclude filtering (less code, always a win), and a double-fetch fix for recording the execve arguments" * 'stable-4.8' of git://git.infradead.org/users/pcmoore/audit: audit: fix a double fetch in audit_log_single_execve_arg() audit: fix whitespace in CWD record audit: add fields to exclude filter by reusing user filter s390: ensure that syscall arguments are properly masked on s390 audit: fix some horrible switch statement style crimes audit: fixup: log on errors from filter user rules
This commit is contained in:
		
						commit
						797cee982e
					
				| @ -821,6 +821,8 @@ long compat_arch_ptrace(struct task_struct *child, compat_long_t request, | ||||
| 
 | ||||
| asmlinkage long do_syscall_trace_enter(struct pt_regs *regs) | ||||
| { | ||||
| 	unsigned long mask = -1UL; | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * The sysc_tracesys code in entry.S stored the system | ||||
| 	 * call number to gprs[2]. | ||||
| @ -846,9 +848,12 @@ asmlinkage long do_syscall_trace_enter(struct pt_regs *regs) | ||||
| 	if (unlikely(test_thread_flag(TIF_SYSCALL_TRACEPOINT))) | ||||
| 		trace_sys_enter(regs, regs->gprs[2]); | ||||
| 
 | ||||
| 	audit_syscall_entry(regs->gprs[2], regs->orig_gpr2, | ||||
| 			    regs->gprs[3], regs->gprs[4], | ||||
| 			    regs->gprs[5]); | ||||
| 	if (is_compat_task()) | ||||
| 		mask = 0xffffffff; | ||||
| 
 | ||||
| 	audit_syscall_entry(regs->gprs[2], regs->orig_gpr2 & mask, | ||||
| 			    regs->gprs[3] &mask, regs->gprs[4] &mask, | ||||
| 			    regs->gprs[5] &mask); | ||||
| 
 | ||||
| 	return regs->gprs[2]; | ||||
| } | ||||
|  | ||||
| @ -163,8 +163,6 @@ extern void audit_log_task_info(struct audit_buffer *ab, | ||||
| extern int		    audit_update_lsm_rules(void); | ||||
| 
 | ||||
| 				/* Private API (for audit.c only) */ | ||||
| extern int audit_filter_user(int type); | ||||
| extern int audit_filter_type(int type); | ||||
| extern int audit_rule_change(int type, __u32 portid, int seq, | ||||
| 				void *data, size_t datasz); | ||||
| extern int audit_list_rules_send(struct sk_buff *request_skb, int seq); | ||||
|  | ||||
| @ -932,7 +932,7 @@ static int audit_receive_msg(struct sk_buff *skb, struct nlmsghdr *nlh) | ||||
| 		if (!audit_enabled && msg_type != AUDIT_USER_AVC) | ||||
| 			return 0; | ||||
| 
 | ||||
| 		err = audit_filter_user(msg_type); | ||||
| 		err = audit_filter(msg_type, AUDIT_FILTER_USER); | ||||
| 		if (err == 1) { /* match or error */ | ||||
| 			err = 0; | ||||
| 			if (msg_type == AUDIT_USER_TTY) { | ||||
| @ -1379,7 +1379,7 @@ struct audit_buffer *audit_log_start(struct audit_context *ctx, gfp_t gfp_mask, | ||||
| 	if (audit_initialized != AUDIT_INITIALIZED) | ||||
| 		return NULL; | ||||
| 
 | ||||
| 	if (unlikely(audit_filter_type(type))) | ||||
| 	if (unlikely(!audit_filter(type, AUDIT_FILTER_TYPE))) | ||||
| 		return NULL; | ||||
| 
 | ||||
| 	if (gfp_mask & __GFP_DIRECT_RECLAIM) { | ||||
|  | ||||
| @ -331,6 +331,8 @@ extern pid_t audit_sig_pid; | ||||
| extern kuid_t audit_sig_uid; | ||||
| extern u32 audit_sig_sid; | ||||
| 
 | ||||
| extern int audit_filter(int msgtype, unsigned int listtype); | ||||
| 
 | ||||
| #ifdef CONFIG_AUDITSYSCALL | ||||
| extern int __audit_signal_info(int sig, struct task_struct *t); | ||||
| static inline int audit_signal_info(int sig, struct task_struct *t) | ||||
|  | ||||
| @ -1290,113 +1290,72 @@ int audit_compare_dname_path(const char *dname, const char *path, int parentlen) | ||||
| 	return strncmp(p, dname, dlen); | ||||
| } | ||||
| 
 | ||||
| static int audit_filter_user_rules(struct audit_krule *rule, int type, | ||||
| 				   enum audit_state *state) | ||||
| int audit_filter(int msgtype, unsigned int listtype) | ||||
| { | ||||
| 	int i; | ||||
| 
 | ||||
| 	for (i = 0; i < rule->field_count; i++) { | ||||
| 		struct audit_field *f = &rule->fields[i]; | ||||
| 		pid_t pid; | ||||
| 		int result = 0; | ||||
| 		u32 sid; | ||||
| 
 | ||||
| 		switch (f->type) { | ||||
| 		case AUDIT_PID: | ||||
| 			pid = task_pid_nr(current); | ||||
| 			result = audit_comparator(pid, f->op, f->val); | ||||
| 			break; | ||||
| 		case AUDIT_UID: | ||||
| 			result = audit_uid_comparator(current_uid(), f->op, f->uid); | ||||
| 			break; | ||||
| 		case AUDIT_GID: | ||||
| 			result = audit_gid_comparator(current_gid(), f->op, f->gid); | ||||
| 			break; | ||||
| 		case AUDIT_LOGINUID: | ||||
| 			result = audit_uid_comparator(audit_get_loginuid(current), | ||||
| 						  f->op, f->uid); | ||||
| 			break; | ||||
| 		case AUDIT_LOGINUID_SET: | ||||
| 			result = audit_comparator(audit_loginuid_set(current), | ||||
| 						  f->op, f->val); | ||||
| 			break; | ||||
| 		case AUDIT_MSGTYPE: | ||||
| 			result = audit_comparator(type, f->op, f->val); | ||||
| 			break; | ||||
| 		case AUDIT_SUBJ_USER: | ||||
| 		case AUDIT_SUBJ_ROLE: | ||||
| 		case AUDIT_SUBJ_TYPE: | ||||
| 		case AUDIT_SUBJ_SEN: | ||||
| 		case AUDIT_SUBJ_CLR: | ||||
| 			if (f->lsm_rule) { | ||||
| 				security_task_getsecid(current, &sid); | ||||
| 				result = security_audit_rule_match(sid, | ||||
| 								   f->type, | ||||
| 								   f->op, | ||||
| 								   f->lsm_rule, | ||||
| 								   NULL); | ||||
| 			} | ||||
| 			break; | ||||
| 		} | ||||
| 
 | ||||
| 		if (!result) | ||||
| 			return 0; | ||||
| 	} | ||||
| 	switch (rule->action) { | ||||
| 	case AUDIT_NEVER:    *state = AUDIT_DISABLED;	    break; | ||||
| 	case AUDIT_ALWAYS:   *state = AUDIT_RECORD_CONTEXT; break; | ||||
| 	} | ||||
| 	return 1; | ||||
| } | ||||
| 
 | ||||
| int audit_filter_user(int type) | ||||
| { | ||||
| 	enum audit_state state = AUDIT_DISABLED; | ||||
| 	struct audit_entry *e; | ||||
| 	int rc, ret; | ||||
| 
 | ||||
| 	ret = 1; /* Audit by default */ | ||||
| 	int ret = 1; /* Audit by default */ | ||||
| 
 | ||||
| 	rcu_read_lock(); | ||||
| 	list_for_each_entry_rcu(e, &audit_filter_list[AUDIT_FILTER_USER], list) { | ||||
| 		rc = audit_filter_user_rules(&e->rule, type, &state); | ||||
| 		if (rc) { | ||||
| 			if (rc > 0 && state == AUDIT_DISABLED) | ||||
| 	if (list_empty(&audit_filter_list[listtype])) | ||||
| 		goto unlock_and_return; | ||||
| 	list_for_each_entry_rcu(e, &audit_filter_list[listtype], list) { | ||||
| 		int i, result = 0; | ||||
| 
 | ||||
| 		for (i = 0; i < e->rule.field_count; i++) { | ||||
| 			struct audit_field *f = &e->rule.fields[i]; | ||||
| 			pid_t pid; | ||||
| 			u32 sid; | ||||
| 
 | ||||
| 			switch (f->type) { | ||||
| 			case AUDIT_PID: | ||||
| 				pid = task_pid_nr(current); | ||||
| 				result = audit_comparator(pid, f->op, f->val); | ||||
| 				break; | ||||
| 			case AUDIT_UID: | ||||
| 				result = audit_uid_comparator(current_uid(), f->op, f->uid); | ||||
| 				break; | ||||
| 			case AUDIT_GID: | ||||
| 				result = audit_gid_comparator(current_gid(), f->op, f->gid); | ||||
| 				break; | ||||
| 			case AUDIT_LOGINUID: | ||||
| 				result = audit_uid_comparator(audit_get_loginuid(current), | ||||
| 							      f->op, f->uid); | ||||
| 				break; | ||||
| 			case AUDIT_LOGINUID_SET: | ||||
| 				result = audit_comparator(audit_loginuid_set(current), | ||||
| 							  f->op, f->val); | ||||
| 				break; | ||||
| 			case AUDIT_MSGTYPE: | ||||
| 				result = audit_comparator(msgtype, f->op, f->val); | ||||
| 				break; | ||||
| 			case AUDIT_SUBJ_USER: | ||||
| 			case AUDIT_SUBJ_ROLE: | ||||
| 			case AUDIT_SUBJ_TYPE: | ||||
| 			case AUDIT_SUBJ_SEN: | ||||
| 			case AUDIT_SUBJ_CLR: | ||||
| 				if (f->lsm_rule) { | ||||
| 					security_task_getsecid(current, &sid); | ||||
| 					result = security_audit_rule_match(sid, | ||||
| 							f->type, f->op, f->lsm_rule, NULL); | ||||
| 				} | ||||
| 				break; | ||||
| 			default: | ||||
| 				goto unlock_and_return; | ||||
| 			} | ||||
| 			if (result < 0) /* error */ | ||||
| 				goto unlock_and_return; | ||||
| 			if (!result) | ||||
| 				break; | ||||
| 		} | ||||
| 		if (result > 0) { | ||||
| 			if (e->rule.action == AUDIT_NEVER || listtype == AUDIT_FILTER_TYPE) | ||||
| 				ret = 0; | ||||
| 			break; | ||||
| 		} | ||||
| 	} | ||||
| 	rcu_read_unlock(); | ||||
| 
 | ||||
| 	return ret; | ||||
| } | ||||
| 
 | ||||
| int audit_filter_type(int type) | ||||
| { | ||||
| 	struct audit_entry *e; | ||||
| 	int result = 0; | ||||
| 
 | ||||
| 	rcu_read_lock(); | ||||
| 	if (list_empty(&audit_filter_list[AUDIT_FILTER_TYPE])) | ||||
| 		goto unlock_and_return; | ||||
| 
 | ||||
| 	list_for_each_entry_rcu(e, &audit_filter_list[AUDIT_FILTER_TYPE], | ||||
| 				list) { | ||||
| 		int i; | ||||
| 		for (i = 0; i < e->rule.field_count; i++) { | ||||
| 			struct audit_field *f = &e->rule.fields[i]; | ||||
| 			if (f->type == AUDIT_MSGTYPE) { | ||||
| 				result = audit_comparator(type, f->op, f->val); | ||||
| 				if (!result) | ||||
| 					break; | ||||
| 			} | ||||
| 		} | ||||
| 		if (result) | ||||
| 			goto unlock_and_return; | ||||
| 	} | ||||
| unlock_and_return: | ||||
| 	rcu_read_unlock(); | ||||
| 	return result; | ||||
| 	return ret; | ||||
| } | ||||
| 
 | ||||
| static int update_lsm_rule(struct audit_krule *r) | ||||
|  | ||||
							
								
								
									
										342
									
								
								kernel/auditsc.c
									
									
									
									
									
								
							
							
						
						
									
										342
									
								
								kernel/auditsc.c
									
									
									
									
									
								
							| @ -72,6 +72,7 @@ | ||||
| #include <linux/compat.h> | ||||
| #include <linux/ctype.h> | ||||
| #include <linux/string.h> | ||||
| #include <linux/uaccess.h> | ||||
| #include <uapi/linux/limits.h> | ||||
| 
 | ||||
| #include "audit.h" | ||||
| @ -81,7 +82,8 @@ | ||||
| #define AUDITSC_SUCCESS 1 | ||||
| #define AUDITSC_FAILURE 2 | ||||
| 
 | ||||
| /* no execve audit message should be longer than this (userspace limits) */ | ||||
| /* no execve audit message should be longer than this (userspace limits),
 | ||||
|  * see the note near the top of audit_log_execve_info() about this value */ | ||||
| #define MAX_EXECVE_AUDIT_LEN 7500 | ||||
| 
 | ||||
| /* max length to print of cmdline/proctitle value during audit */ | ||||
| @ -694,8 +696,12 @@ static int audit_filter_rules(struct task_struct *tsk, | ||||
| 		ctx->prio = rule->prio; | ||||
| 	} | ||||
| 	switch (rule->action) { | ||||
| 	case AUDIT_NEVER:    *state = AUDIT_DISABLED;	    break; | ||||
| 	case AUDIT_ALWAYS:   *state = AUDIT_RECORD_CONTEXT; break; | ||||
| 	case AUDIT_NEVER: | ||||
| 		*state = AUDIT_DISABLED; | ||||
| 		break; | ||||
| 	case AUDIT_ALWAYS: | ||||
| 		*state = AUDIT_RECORD_CONTEXT; | ||||
| 		break; | ||||
| 	} | ||||
| 	return 1; | ||||
| } | ||||
| @ -987,184 +993,178 @@ static int audit_log_pid_context(struct audit_context *context, pid_t pid, | ||||
| 	return rc; | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  * to_send and len_sent accounting are very loose estimates.  We aren't | ||||
|  * really worried about a hard cap to MAX_EXECVE_AUDIT_LEN so much as being | ||||
|  * within about 500 bytes (next page boundary) | ||||
|  * | ||||
|  * why snprintf?  an int is up to 12 digits long.  if we just assumed when | ||||
|  * logging that a[%d]= was going to be 16 characters long we would be wasting | ||||
|  * space in every audit message.  In one 7500 byte message we can log up to | ||||
|  * about 1000 min size arguments.  That comes down to about 50% waste of space | ||||
|  * if we didn't do the snprintf to find out how long arg_num_len was. | ||||
|  */ | ||||
| static int audit_log_single_execve_arg(struct audit_context *context, | ||||
| 					struct audit_buffer **ab, | ||||
| 					int arg_num, | ||||
| 					size_t *len_sent, | ||||
| 					const char __user *p, | ||||
| 					char *buf) | ||||
| { | ||||
| 	char arg_num_len_buf[12]; | ||||
| 	const char __user *tmp_p = p; | ||||
| 	/* how many digits are in arg_num? 5 is the length of ' a=""' */ | ||||
| 	size_t arg_num_len = snprintf(arg_num_len_buf, 12, "%d", arg_num) + 5; | ||||
| 	size_t len, len_left, to_send; | ||||
| 	size_t max_execve_audit_len = MAX_EXECVE_AUDIT_LEN; | ||||
| 	unsigned int i, has_cntl = 0, too_long = 0; | ||||
| 	int ret; | ||||
| 
 | ||||
| 	/* strnlen_user includes the null we don't want to send */ | ||||
| 	len_left = len = strnlen_user(p, MAX_ARG_STRLEN) - 1; | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * We just created this mm, if we can't find the strings | ||||
| 	 * we just copied into it something is _very_ wrong. Similar | ||||
| 	 * for strings that are too long, we should not have created | ||||
| 	 * any. | ||||
| 	 */ | ||||
| 	if (WARN_ON_ONCE(len < 0 || len > MAX_ARG_STRLEN - 1)) { | ||||
| 		send_sig(SIGKILL, current, 0); | ||||
| 		return -1; | ||||
| 	} | ||||
| 
 | ||||
| 	/* walk the whole argument looking for non-ascii chars */ | ||||
| 	do { | ||||
| 		if (len_left > MAX_EXECVE_AUDIT_LEN) | ||||
| 			to_send = MAX_EXECVE_AUDIT_LEN; | ||||
| 		else | ||||
| 			to_send = len_left; | ||||
| 		ret = copy_from_user(buf, tmp_p, to_send); | ||||
| 		/*
 | ||||
| 		 * There is no reason for this copy to be short. We just | ||||
| 		 * copied them here, and the mm hasn't been exposed to user- | ||||
| 		 * space yet. | ||||
| 		 */ | ||||
| 		if (ret) { | ||||
| 			WARN_ON(1); | ||||
| 			send_sig(SIGKILL, current, 0); | ||||
| 			return -1; | ||||
| 		} | ||||
| 		buf[to_send] = '\0'; | ||||
| 		has_cntl = audit_string_contains_control(buf, to_send); | ||||
| 		if (has_cntl) { | ||||
| 			/*
 | ||||
| 			 * hex messages get logged as 2 bytes, so we can only | ||||
| 			 * send half as much in each message | ||||
| 			 */ | ||||
| 			max_execve_audit_len = MAX_EXECVE_AUDIT_LEN / 2; | ||||
| 			break; | ||||
| 		} | ||||
| 		len_left -= to_send; | ||||
| 		tmp_p += to_send; | ||||
| 	} while (len_left > 0); | ||||
| 
 | ||||
| 	len_left = len; | ||||
| 
 | ||||
| 	if (len > max_execve_audit_len) | ||||
| 		too_long = 1; | ||||
| 
 | ||||
| 	/* rewalk the argument actually logging the message */ | ||||
| 	for (i = 0; len_left > 0; i++) { | ||||
| 		int room_left; | ||||
| 
 | ||||
| 		if (len_left > max_execve_audit_len) | ||||
| 			to_send = max_execve_audit_len; | ||||
| 		else | ||||
| 			to_send = len_left; | ||||
| 
 | ||||
| 		/* do we have space left to send this argument in this ab? */ | ||||
| 		room_left = MAX_EXECVE_AUDIT_LEN - arg_num_len - *len_sent; | ||||
| 		if (has_cntl) | ||||
| 			room_left -= (to_send * 2); | ||||
| 		else | ||||
| 			room_left -= to_send; | ||||
| 		if (room_left < 0) { | ||||
| 			*len_sent = 0; | ||||
| 			audit_log_end(*ab); | ||||
| 			*ab = audit_log_start(context, GFP_KERNEL, AUDIT_EXECVE); | ||||
| 			if (!*ab) | ||||
| 				return 0; | ||||
| 		} | ||||
| 
 | ||||
| 		/*
 | ||||
| 		 * first record needs to say how long the original string was | ||||
| 		 * so we can be sure nothing was lost. | ||||
| 		 */ | ||||
| 		if ((i == 0) && (too_long)) | ||||
| 			audit_log_format(*ab, " a%d_len=%zu", arg_num, | ||||
| 					 has_cntl ? 2*len : len); | ||||
| 
 | ||||
| 		/*
 | ||||
| 		 * normally arguments are small enough to fit and we already | ||||
| 		 * filled buf above when we checked for control characters | ||||
| 		 * so don't bother with another copy_from_user | ||||
| 		 */ | ||||
| 		if (len >= max_execve_audit_len) | ||||
| 			ret = copy_from_user(buf, p, to_send); | ||||
| 		else | ||||
| 			ret = 0; | ||||
| 		if (ret) { | ||||
| 			WARN_ON(1); | ||||
| 			send_sig(SIGKILL, current, 0); | ||||
| 			return -1; | ||||
| 		} | ||||
| 		buf[to_send] = '\0'; | ||||
| 
 | ||||
| 		/* actually log it */ | ||||
| 		audit_log_format(*ab, " a%d", arg_num); | ||||
| 		if (too_long) | ||||
| 			audit_log_format(*ab, "[%d]", i); | ||||
| 		audit_log_format(*ab, "="); | ||||
| 		if (has_cntl) | ||||
| 			audit_log_n_hex(*ab, buf, to_send); | ||||
| 		else | ||||
| 			audit_log_string(*ab, buf); | ||||
| 
 | ||||
| 		p += to_send; | ||||
| 		len_left -= to_send; | ||||
| 		*len_sent += arg_num_len; | ||||
| 		if (has_cntl) | ||||
| 			*len_sent += to_send * 2; | ||||
| 		else | ||||
| 			*len_sent += to_send; | ||||
| 	} | ||||
| 	/* include the null we didn't log */ | ||||
| 	return len + 1; | ||||
| } | ||||
| 
 | ||||
| static void audit_log_execve_info(struct audit_context *context, | ||||
| 				  struct audit_buffer **ab) | ||||
| { | ||||
| 	int i, len; | ||||
| 	size_t len_sent = 0; | ||||
| 	const char __user *p; | ||||
| 	long len_max; | ||||
| 	long len_rem; | ||||
| 	long len_full; | ||||
| 	long len_buf; | ||||
| 	long len_abuf; | ||||
| 	long len_tmp; | ||||
| 	bool require_data; | ||||
| 	bool encode; | ||||
| 	unsigned int iter; | ||||
| 	unsigned int arg; | ||||
| 	char *buf_head; | ||||
| 	char *buf; | ||||
| 	const char __user *p = (const char __user *)current->mm->arg_start; | ||||
| 
 | ||||
| 	p = (const char __user *)current->mm->arg_start; | ||||
| 	/* NOTE: this buffer needs to be large enough to hold all the non-arg
 | ||||
| 	 *       data we put in the audit record for this argument (see the | ||||
| 	 *       code below) ... at this point in time 96 is plenty */ | ||||
| 	char abuf[96]; | ||||
| 
 | ||||
| 	audit_log_format(*ab, "argc=%d", context->execve.argc); | ||||
| 	/* NOTE: we set MAX_EXECVE_AUDIT_LEN to a rather arbitrary limit, the
 | ||||
| 	 *       current value of 7500 is not as important as the fact that it | ||||
| 	 *       is less than 8k, a setting of 7500 gives us plenty of wiggle | ||||
| 	 *       room if we go over a little bit in the logging below */ | ||||
| 	WARN_ON_ONCE(MAX_EXECVE_AUDIT_LEN > 7500); | ||||
| 	len_max = MAX_EXECVE_AUDIT_LEN; | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * we need some kernel buffer to hold the userspace args.  Just | ||||
| 	 * allocate one big one rather than allocating one of the right size | ||||
| 	 * for every single argument inside audit_log_single_execve_arg() | ||||
| 	 * should be <8k allocation so should be pretty safe. | ||||
| 	 */ | ||||
| 	buf = kmalloc(MAX_EXECVE_AUDIT_LEN + 1, GFP_KERNEL); | ||||
| 	if (!buf) { | ||||
| 	/* scratch buffer to hold the userspace args */ | ||||
| 	buf_head = kmalloc(MAX_EXECVE_AUDIT_LEN + 1, GFP_KERNEL); | ||||
| 	if (!buf_head) { | ||||
| 		audit_panic("out of memory for argv string"); | ||||
| 		return; | ||||
| 	} | ||||
| 	buf = buf_head; | ||||
| 
 | ||||
| 	for (i = 0; i < context->execve.argc; i++) { | ||||
| 		len = audit_log_single_execve_arg(context, ab, i, | ||||
| 						  &len_sent, p, buf); | ||||
| 		if (len <= 0) | ||||
| 			break; | ||||
| 		p += len; | ||||
| 	} | ||||
| 	kfree(buf); | ||||
| 	audit_log_format(*ab, "argc=%d", context->execve.argc); | ||||
| 
 | ||||
| 	len_rem = len_max; | ||||
| 	len_buf = 0; | ||||
| 	len_full = 0; | ||||
| 	require_data = true; | ||||
| 	encode = false; | ||||
| 	iter = 0; | ||||
| 	arg = 0; | ||||
| 	do { | ||||
| 		/* NOTE: we don't ever want to trust this value for anything
 | ||||
| 		 *       serious, but the audit record format insists we | ||||
| 		 *       provide an argument length for really long arguments, | ||||
| 		 *       e.g. > MAX_EXECVE_AUDIT_LEN, so we have no choice but | ||||
| 		 *       to use strncpy_from_user() to obtain this value for | ||||
| 		 *       recording in the log, although we don't use it | ||||
| 		 *       anywhere here to avoid a double-fetch problem */ | ||||
| 		if (len_full == 0) | ||||
| 			len_full = strnlen_user(p, MAX_ARG_STRLEN) - 1; | ||||
| 
 | ||||
| 		/* read more data from userspace */ | ||||
| 		if (require_data) { | ||||
| 			/* can we make more room in the buffer? */ | ||||
| 			if (buf != buf_head) { | ||||
| 				memmove(buf_head, buf, len_buf); | ||||
| 				buf = buf_head; | ||||
| 			} | ||||
| 
 | ||||
| 			/* fetch as much as we can of the argument */ | ||||
| 			len_tmp = strncpy_from_user(&buf_head[len_buf], p, | ||||
| 						    len_max - len_buf); | ||||
| 			if (len_tmp == -EFAULT) { | ||||
| 				/* unable to copy from userspace */ | ||||
| 				send_sig(SIGKILL, current, 0); | ||||
| 				goto out; | ||||
| 			} else if (len_tmp == (len_max - len_buf)) { | ||||
| 				/* buffer is not large enough */ | ||||
| 				require_data = true; | ||||
| 				/* NOTE: if we are going to span multiple
 | ||||
| 				 *       buffers force the encoding so we stand | ||||
| 				 *       a chance at a sane len_full value and | ||||
| 				 *       consistent record encoding */ | ||||
| 				encode = true; | ||||
| 				len_full = len_full * 2; | ||||
| 				p += len_tmp; | ||||
| 			} else { | ||||
| 				require_data = false; | ||||
| 				if (!encode) | ||||
| 					encode = audit_string_contains_control( | ||||
| 								buf, len_tmp); | ||||
| 				/* try to use a trusted value for len_full */ | ||||
| 				if (len_full < len_max) | ||||
| 					len_full = (encode ? | ||||
| 						    len_tmp * 2 : len_tmp); | ||||
| 				p += len_tmp + 1; | ||||
| 			} | ||||
| 			len_buf += len_tmp; | ||||
| 			buf_head[len_buf] = '\0'; | ||||
| 
 | ||||
| 			/* length of the buffer in the audit record? */ | ||||
| 			len_abuf = (encode ? len_buf * 2 : len_buf + 2); | ||||
| 		} | ||||
| 
 | ||||
| 		/* write as much as we can to the audit log */ | ||||
| 		if (len_buf > 0) { | ||||
| 			/* NOTE: some magic numbers here - basically if we
 | ||||
| 			 *       can't fit a reasonable amount of data into the | ||||
| 			 *       existing audit buffer, flush it and start with | ||||
| 			 *       a new buffer */ | ||||
| 			if ((sizeof(abuf) + 8) > len_rem) { | ||||
| 				len_rem = len_max; | ||||
| 				audit_log_end(*ab); | ||||
| 				*ab = audit_log_start(context, | ||||
| 						      GFP_KERNEL, AUDIT_EXECVE); | ||||
| 				if (!*ab) | ||||
| 					goto out; | ||||
| 			} | ||||
| 
 | ||||
| 			/* create the non-arg portion of the arg record */ | ||||
| 			len_tmp = 0; | ||||
| 			if (require_data || (iter > 0) || | ||||
| 			    ((len_abuf + sizeof(abuf)) > len_rem)) { | ||||
| 				if (iter == 0) { | ||||
| 					len_tmp += snprintf(&abuf[len_tmp], | ||||
| 							sizeof(abuf) - len_tmp, | ||||
| 							" a%d_len=%lu", | ||||
| 							arg, len_full); | ||||
| 				} | ||||
| 				len_tmp += snprintf(&abuf[len_tmp], | ||||
| 						    sizeof(abuf) - len_tmp, | ||||
| 						    " a%d[%d]=", arg, iter++); | ||||
| 			} else | ||||
| 				len_tmp += snprintf(&abuf[len_tmp], | ||||
| 						    sizeof(abuf) - len_tmp, | ||||
| 						    " a%d=", arg); | ||||
| 			WARN_ON(len_tmp >= sizeof(abuf)); | ||||
| 			abuf[sizeof(abuf) - 1] = '\0'; | ||||
| 
 | ||||
| 			/* log the arg in the audit record */ | ||||
| 			audit_log_format(*ab, "%s", abuf); | ||||
| 			len_rem -= len_tmp; | ||||
| 			len_tmp = len_buf; | ||||
| 			if (encode) { | ||||
| 				if (len_abuf > len_rem) | ||||
| 					len_tmp = len_rem / 2; /* encoding */ | ||||
| 				audit_log_n_hex(*ab, buf, len_tmp); | ||||
| 				len_rem -= len_tmp * 2; | ||||
| 				len_abuf -= len_tmp * 2; | ||||
| 			} else { | ||||
| 				if (len_abuf > len_rem) | ||||
| 					len_tmp = len_rem - 2; /* quotes */ | ||||
| 				audit_log_n_string(*ab, buf, len_tmp); | ||||
| 				len_rem -= len_tmp + 2; | ||||
| 				/* don't subtract the "2" because we still need
 | ||||
| 				 * to add quotes to the remaining string */ | ||||
| 				len_abuf -= len_tmp; | ||||
| 			} | ||||
| 			len_buf -= len_tmp; | ||||
| 			buf += len_tmp; | ||||
| 		} | ||||
| 
 | ||||
| 		/* ready to move to the next argument? */ | ||||
| 		if ((len_buf == 0) && !require_data) { | ||||
| 			arg++; | ||||
| 			iter = 0; | ||||
| 			len_full = 0; | ||||
| 			require_data = true; | ||||
| 			encode = false; | ||||
| 		} | ||||
| 	} while (arg < context->execve.argc); | ||||
| 
 | ||||
| 	/* NOTE: the caller handles the final audit_log_end() call */ | ||||
| 
 | ||||
| out: | ||||
| 	kfree(buf_head); | ||||
| } | ||||
| 
 | ||||
| static void show_special(struct audit_context *context, int *call_panic) | ||||
| @ -1425,7 +1425,7 @@ static void audit_log_exit(struct audit_context *context, struct task_struct *ts | ||||
| 	if (context->pwd.dentry && context->pwd.mnt) { | ||||
| 		ab = audit_log_start(context, GFP_KERNEL, AUDIT_CWD); | ||||
| 		if (ab) { | ||||
| 			audit_log_d_path(ab, " cwd=", &context->pwd); | ||||
| 			audit_log_d_path(ab, "cwd=", &context->pwd); | ||||
| 			audit_log_end(ab); | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user