Skip to content
Snippets Groups Projects
auditsc.c 65.9 KiB
Newer Older
  • Learn to ignore specific revisions
  • 	audit_log_format(ab, " ocomm=");
    	audit_log_untrustedstring(ab, comm);
    
    	audit_log_end(ab);
    
    	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
    
    Lucas De Marchi's avatar
    Lucas De Marchi committed
     * 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 (unlikely((len == -1) || len > MAX_ARG_STRLEN - 1)) {
    
    		WARN_ON(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.
    
    			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;
    
    		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;
    
    			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);
    
    			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,
    
    	int i, len;
    	size_t len_sent = 0;
    
    	p = (const char __user *)current->mm->arg_start;
    
    	audit_log_format(*ab, "argc=%d", context->execve.argc);
    
    
    	/*
    	 * 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) {
    		audit_panic("out of memory for argv string\n");
    		return;
    
    	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);
    
    Al Viro's avatar
    Al Viro committed
    static void show_special(struct audit_context *context, int *call_panic)
    
    Al Viro's avatar
    Al Viro committed
    {
    	struct audit_buffer *ab;
    	int i;
    
    	ab = audit_log_start(context, GFP_KERNEL, context->type);
    	if (!ab)
    		return;
    
    	switch (context->type) {
    	case AUDIT_SOCKETCALL: {
    		int nargs = context->socketcall.nargs;
    		audit_log_format(ab, "nargs=%d", nargs);
    		for (i = 0; i < nargs; i++)
    			audit_log_format(ab, " a%d=%lx", i,
    				context->socketcall.args[i]);
    		break; }
    
    Al Viro's avatar
    Al Viro committed
    	case AUDIT_IPC: {
    		u32 osid = context->ipc.osid;
    
    
    Al Viro's avatar
    Al Viro committed
    		audit_log_format(ab, "ouid=%u ogid=%u mode=%#ho",
    
    				 from_kuid(&init_user_ns, context->ipc.uid),
    				 from_kgid(&init_user_ns, context->ipc.gid),
    				 context->ipc.mode);
    
    Al Viro's avatar
    Al Viro committed
    		if (osid) {
    			char *ctx = NULL;
    			u32 len;
    			if (security_secid_to_secctx(osid, &ctx, &len)) {
    				audit_log_format(ab, " osid=%u", osid);
    				*call_panic = 1;
    			} else {
    				audit_log_format(ab, " obj=%s", ctx);
    				security_release_secctx(ctx, len);
    			}
    		}
    
    Al Viro's avatar
    Al Viro committed
    		if (context->ipc.has_perm) {
    			audit_log_end(ab);
    			ab = audit_log_start(context, GFP_KERNEL,
    					     AUDIT_IPC_SET_PERM);
    
    			if (unlikely(!ab))
    				return;
    
    Al Viro's avatar
    Al Viro committed
    			audit_log_format(ab,
    
    Al Viro's avatar
    Al Viro committed
    				"qbytes=%lx ouid=%u ogid=%u mode=%#ho",
    
    Al Viro's avatar
    Al Viro committed
    				context->ipc.qbytes,
    				context->ipc.perm_uid,
    				context->ipc.perm_gid,
    				context->ipc.perm_mode);
    		}
    
    Al Viro's avatar
    Al Viro committed
    		break; }
    
    Al Viro's avatar
    Al Viro committed
    	case AUDIT_MQ_OPEN: {
    		audit_log_format(ab,
    
    Al Viro's avatar
    Al Viro committed
    			"oflag=0x%x mode=%#ho mq_flags=0x%lx mq_maxmsg=%ld "
    
    Al Viro's avatar
    Al Viro committed
    			"mq_msgsize=%ld mq_curmsgs=%ld",
    			context->mq_open.oflag, context->mq_open.mode,
    			context->mq_open.attr.mq_flags,
    			context->mq_open.attr.mq_maxmsg,
    			context->mq_open.attr.mq_msgsize,
    			context->mq_open.attr.mq_curmsgs);
    		break; }
    
    Al Viro's avatar
    Al Viro committed
    	case AUDIT_MQ_SENDRECV: {
    		audit_log_format(ab,
    			"mqdes=%d msg_len=%zd msg_prio=%u "
    			"abs_timeout_sec=%ld abs_timeout_nsec=%ld",
    			context->mq_sendrecv.mqdes,
    			context->mq_sendrecv.msg_len,
    			context->mq_sendrecv.msg_prio,
    			context->mq_sendrecv.abs_timeout.tv_sec,
    			context->mq_sendrecv.abs_timeout.tv_nsec);
    		break; }
    
    Al Viro's avatar
    Al Viro committed
    	case AUDIT_MQ_NOTIFY: {
    		audit_log_format(ab, "mqdes=%d sigev_signo=%d",
    				context->mq_notify.mqdes,
    				context->mq_notify.sigev_signo);
    		break; }
    
    Al Viro's avatar
    Al Viro committed
    	case AUDIT_MQ_GETSETATTR: {
    		struct mq_attr *attr = &context->mq_getsetattr.mqstat;
    		audit_log_format(ab,
    			"mqdes=%d mq_flags=0x%lx mq_maxmsg=%ld mq_msgsize=%ld "
    			"mq_curmsgs=%ld ",
    			context->mq_getsetattr.mqdes,
    			attr->mq_flags, attr->mq_maxmsg,
    			attr->mq_msgsize, attr->mq_curmsgs);
    		break; }
    
    Al Viro's avatar
    Al Viro committed
    	case AUDIT_CAPSET: {
    		audit_log_format(ab, "pid=%d", context->capset.pid);
    		audit_log_cap(ab, "cap_pi", &context->capset.cap.inheritable);
    		audit_log_cap(ab, "cap_pp", &context->capset.cap.permitted);
    		audit_log_cap(ab, "cap_pe", &context->capset.cap.effective);
    		break; }
    
    Al Viro's avatar
    Al Viro committed
    	case AUDIT_MMAP: {
    		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; }
    
    Al Viro's avatar
    Al Viro committed
    	}
    	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)
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    {
    
    	int i, call_panic = 0;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	struct audit_buffer *ab;
    
    	struct audit_aux_data *aux;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    
    	/* tsk == current */
    
    	context->personality = tsk->personality;
    
    
    	ab = audit_log_start(context, GFP_KERNEL, AUDIT_SYSCALL);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	if (!ab)
    		return;		/* audit_panic has been called */
    
    	audit_log_format(ab, "arch=%x syscall=%d",
    			 context->arch, context->major);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	if (context->personality != PER_LINUX)
    		audit_log_format(ab, " per=%lx", context->personality);
    	if (context->return_valid)
    
    		audit_log_format(ab, " success=%s exit=%ld",
    
    				 (context->return_valid==AUDITSC_SUCCESS)?"yes":"no",
    				 context->return_code);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	audit_log_format(ab,
    
    			 " a0=%lx a1=%lx a2=%lx a3=%lx items=%d",
    			 context->argv[0],
    			 context->argv[1],
    			 context->argv[2],
    			 context->argv[3],
    			 context->name_count);
    
    	audit_log_task_info(ab, tsk);
    
    	audit_log_key(ab, context->filterkey);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	audit_log_end(ab);
    
    
    	for (aux = context->aux; aux; aux = aux->next) {
    
    		ab = audit_log_start(context, GFP_KERNEL, aux->type);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    		if (!ab)
    			continue; /* audit_panic has been called */
    
    		switch (aux->type) {
    
    		case AUDIT_BPRM_FCAPS: {
    			struct audit_aux_data_bprm_fcaps *axs = (void *)aux;
    			audit_log_format(ab, "fver=%x", axs->fcap_ver);
    			audit_log_cap(ab, "fp", &axs->fcap.permitted);
    			audit_log_cap(ab, "fi", &axs->fcap.inheritable);
    			audit_log_format(ab, " fe=%d", axs->fcap.fE);
    			audit_log_cap(ab, "old_pp", &axs->old_pcap.permitted);
    			audit_log_cap(ab, "old_pi", &axs->old_pcap.inheritable);
    			audit_log_cap(ab, "old_pe", &axs->old_pcap.effective);
    			audit_log_cap(ab, "new_pp", &axs->new_pcap.permitted);
    			audit_log_cap(ab, "new_pi", &axs->new_pcap.inheritable);
    			audit_log_cap(ab, "new_pe", &axs->new_pcap.effective);
    			break; }
    
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    		}
    		audit_log_end(ab);
    	}
    
    
    Al Viro's avatar
    Al Viro committed
    	if (context->type)
    
    Al Viro's avatar
    Al Viro committed
    		show_special(context, &call_panic);
    
    Al Viro's avatar
    Al Viro committed
    	if (context->fds[0] >= 0) {
    		ab = audit_log_start(context, GFP_KERNEL, AUDIT_FD_PAIR);
    		if (ab) {
    			audit_log_format(ab, "fd0=%d fd1=%d",
    					context->fds[0], context->fds[1]);
    			audit_log_end(ab);
    		}
    	}
    
    
    	if (context->sockaddr_len) {
    		ab = audit_log_start(context, GFP_KERNEL, AUDIT_SOCKADDR);
    		if (ab) {
    			audit_log_format(ab, "saddr=");
    			audit_log_n_hex(ab, (void *)context->sockaddr,
    					context->sockaddr_len);
    			audit_log_end(ab);
    		}
    	}
    
    
    	for (aux = context->aux_pids; aux; aux = aux->next) {
    		struct audit_aux_data_pids *axs = (void *)aux;
    
    		for (i = 0; i < axs->pid_count; i++)
    			if (audit_log_pid_context(context, axs->target_pid[i],
    
    						  axs->target_auid[i],
    						  axs->target_uid[i],
    
    						  axs->target_sessionid[i],
    
    						  axs->target_sid[i],
    						  axs->target_comm[i]))
    
    				call_panic = 1;
    
    	if (context->target_pid &&
    	    audit_log_pid_context(context, context->target_pid,
    
    				  context->target_auid, context->target_uid,
    
    				  context->target_sessionid,
    
    				  context->target_sid, context->target_comm))
    
    			call_panic = 1;
    
    
    	if (context->pwd.dentry && context->pwd.mnt) {
    
    		ab = audit_log_start(context, GFP_KERNEL, AUDIT_CWD);
    
    			audit_log_d_path(ab, " cwd=", &context->pwd);
    
    	list_for_each_entry(n, &context->names_list, list) {
    		if (n->hidden)
    			continue;
    
    		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)
    		audit_log_end(ab);
    
    	if (call_panic)
    		audit_panic("error converting sid to string");
    
    /**
     * audit_free - free a per-task audit context
     * @tsk: task whose audit context block to free
     *
    
     * Called from copy_process and do_exit
    
    void __audit_free(struct task_struct *tsk)
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    {
    	struct audit_context *context;
    
    
    	context = audit_take_context(tsk, 0, 0);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    		return;
    
    	/* Check for system calls that do not go through the exit
    
    	 * function (e.g., exit_group), then free context block.
    	 * We use GFP_ATOMIC here because we might be doing this
    
    	 * in the context of the idle thread */
    
    	/* that can happen only if we are called from do_exit() */
    
    	if (context->in_syscall && context->current_state == AUDIT_RECORD_CONTEXT)
    
    		audit_log_exit(context, tsk);
    
    	if (!list_empty(&context->killed_trees))
    		audit_kill_trees(&context->killed_trees);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    	audit_free_context(context);
    }
    
    
    /**
     * audit_syscall_entry - fill in an audit record at syscall entry
     * @arch: architecture type
     * @major: major syscall type (function)
     * @a1: additional syscall register 1
     * @a2: additional syscall register 2
     * @a3: additional syscall register 3
     * @a4: additional syscall register 4
     *
     * Fill in audit context at syscall entry.  This only happens if the
    
    Linus Torvalds's avatar
    Linus Torvalds committed
     * audit context was created when the task was created and the state or
     * filters demand the audit context be built.  If the state from the
     * per-task filter or from the per-syscall filter is AUDIT_RECORD_CONTEXT,
     * then the record will be written at syscall exit time (otherwise, it
     * will only be written if another part of the kernel requests that it
    
    void __audit_syscall_entry(int arch, int major,
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    			 unsigned long a1, unsigned long a2,
    			 unsigned long a3, unsigned long a4)
    {
    
    	struct task_struct *tsk = current;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	struct audit_context *context = tsk->audit_context;
    	enum audit_state     state;
    
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    	BUG_ON(context->in_syscall || context->name_count);
    
    	if (!audit_enabled)
    		return;
    
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	context->major      = major;
    	context->argv[0]    = a1;
    	context->argv[1]    = a2;
    	context->argv[2]    = a3;
    	context->argv[3]    = a4;
    
    	state = context->state;
    
    	context->dummy = !audit_n_rules;
    
    	if (!context->dummy && state == AUDIT_BUILD_CONTEXT) {
    		context->prio = 0;
    
    		state = audit_filter_syscall(tsk, context, &audit_filter_list[AUDIT_FILTER_ENTRY]);
    
    	if (state == AUDIT_DISABLED)
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    		return;
    
    
    	context->serial     = 0;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	context->ctime      = CURRENT_TIME;
    	context->in_syscall = 1;
    
    	context->current_state  = state;
    
    	context->ppid       = 0;
    
    /**
     * audit_syscall_exit - deallocate audit context after a system call
    
     * @success: success value of the syscall
     * @return_code: return value of the syscall
    
     *
     * Tear down after system call.  If the audit context has been marked as
    
    Linus Torvalds's avatar
    Linus Torvalds committed
     * auditable (either because of the AUDIT_RECORD_CONTEXT state from
    
     * filtering, or because some other part of the kernel wrote an audit
    
    Linus Torvalds's avatar
    Linus Torvalds committed
     * message), then write out the syscall information.  In call cases,
    
     * free the names stored from getname().
     */
    
    void __audit_syscall_exit(int success, long return_code)
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    {
    
    	struct task_struct *tsk = current;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	struct audit_context *context;
    
    
    	if (success)
    		success = AUDITSC_SUCCESS;
    	else
    		success = AUDITSC_FAILURE;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    
    	context = audit_take_context(tsk, success, return_code);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    
    	if (context->in_syscall && context->current_state == AUDIT_RECORD_CONTEXT)
    
    		audit_log_exit(context, tsk);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    	context->in_syscall = 0;
    
    	context->prio = context->state == AUDIT_RECORD_CONTEXT ? ~0ULL : 0;
    
    	if (!list_empty(&context->killed_trees))
    		audit_kill_trees(&context->killed_trees);
    
    
    	audit_free_names(context);
    	unroll_tree_refs(context, NULL, 0);
    	audit_free_aux(context);
    	context->aux = NULL;
    	context->aux_pids = NULL;
    	context->target_pid = 0;
    	context->target_sid = 0;
    	context->sockaddr_len = 0;
    	context->type = 0;
    	context->fds[0] = -1;
    	if (context->state != AUDIT_RECORD_CONTEXT) {
    		kfree(context->filterkey);
    		context->filterkey = NULL;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	}
    
    	tsk->audit_context = context;
    
    static inline void handle_one(const struct inode *inode)
    {
    #ifdef CONFIG_AUDIT_TREE
    	struct audit_context *context;
    	struct audit_tree_refs *p;
    	struct audit_chunk *chunk;
    	int count;
    
    	if (likely(hlist_empty(&inode->i_fsnotify_marks)))
    
    		return;
    	context = current->audit_context;
    	p = context->trees;
    	count = context->tree_count;
    	rcu_read_lock();
    	chunk = audit_tree_lookup(inode);
    	rcu_read_unlock();
    	if (!chunk)
    		return;
    	if (likely(put_tree_ref(context, chunk)))
    		return;
    	if (unlikely(!grow_tree_refs(context))) {
    
    		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);
    		return;
    	}
    	put_tree_ref(context, chunk);
    #endif
    }
    
    static void handle_path(const struct dentry *dentry)
    {
    #ifdef CONFIG_AUDIT_TREE
    	struct audit_context *context;
    	struct audit_tree_refs *p;
    	const struct dentry *d, *parent;
    	struct audit_chunk *drop;
    	unsigned long seq;
    	int count;
    
    	context = current->audit_context;
    	p = context->trees;
    	count = context->tree_count;
    retry:
    	drop = NULL;
    	d = dentry;
    	rcu_read_lock();
    	seq = read_seqbegin(&rename_lock);
    	for(;;) {
    		struct inode *inode = d->d_inode;
    
    		if (inode && unlikely(!hlist_empty(&inode->i_fsnotify_marks))) {
    
    			struct audit_chunk *chunk;
    			chunk = audit_tree_lookup(inode);
    			if (chunk) {
    				if (unlikely(!put_tree_ref(context, chunk))) {
    					drop = chunk;
    					break;
    				}
    			}
    		}
    		parent = d->d_parent;
    		if (parent == d)
    			break;
    		d = parent;
    	}
    	if (unlikely(read_seqretry(&rename_lock, seq) || drop)) {  /* in this order */
    		rcu_read_unlock();
    		if (!drop) {
    			/* just a race with rename */
    			unroll_tree_refs(context, p, count);
    			goto retry;
    		}
    		audit_put_chunk(drop);
    		if (grow_tree_refs(context)) {
    			/* OK, got more space */
    			unroll_tree_refs(context, p, count);
    			goto retry;
    		}
    		/* too bad */
    
    		pr_warn("out of memory, audit has lost a tree reference\n");
    
    		unroll_tree_refs(context, p, count);
    		audit_set_auditable(context);
    		return;
    	}
    	rcu_read_unlock();
    #endif
    }
    
    
    static struct audit_names *audit_alloc_name(struct audit_context *context,
    						unsigned char type)
    
    {
    	struct audit_names *aname;
    
    	if (context->name_count < AUDIT_NAMES) {
    		aname = &context->preallocated_names[context->name_count];
    		memset(aname, 0, sizeof(*aname));
    	} else {
    		aname = kzalloc(sizeof(*aname), GFP_NOFS);
    		if (!aname)
    			return NULL;
    		aname->should_free = true;
    	}
    
    	aname->ino = (unsigned long)-1;
    
    	list_add_tail(&aname->list, &context->names_list);
    
    	context->name_count++;
    #if AUDIT_DEBUG
    	context->ino_count++;
    #endif
    	return aname;
    }
    
    
    /**
     * audit_reusename - fill out filename with info from existing entry
     * @uptr: userland ptr to pathname
     *
     * Search the audit_names list for the current audit context. If there is an
     * existing entry with a matching "uptr" then return the filename
     * associated with that audit_name. If not, return NULL.
     */
    struct filename *
    __audit_reusename(const __user char *uptr)
    {
    	struct audit_context *context = current->audit_context;
    	struct audit_names *n;
    
    	list_for_each_entry(n, &context->names_list, list) {
    		if (!n->name)
    			continue;
    		if (n->name->uptr == uptr)
    			return n->name;
    	}
    	return NULL;
    }
    
    
    /**
     * audit_getname - add a name to the list
     * @name: name to add
     *
     * Add a name to the list of audit names for this context.
     * Called from fs/namei.c:getname().
     */
    
    void __audit_getname(struct filename *name)
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    {
    	struct audit_context *context = current->audit_context;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    	if (!context->in_syscall) {
    #if AUDIT_DEBUG == 2
    
    		pr_err("%s:%d(:%d): ignoring getname(%p)\n",
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    		       __FILE__, __LINE__, context->serial, name);
    		dump_stack();
    #endif
    		return;
    	}
    
    #if AUDIT_DEBUG
    	/* The filename _must_ have a populated ->name */
    	BUG_ON(!name->name);
    #endif
    
    
    	n = audit_alloc_name(context, AUDIT_TYPE_UNKNOWN);
    
    	if (!n)
    		return;
    
    	n->name = name;
    	n->name_len = AUDIT_NAME_FULL;
    	n->name_put = true;
    
    	name->aname = n;
    
    	if (!context->pwd.dentry)
    		get_fs_pwd(current->fs, &context->pwd);
    
    /* audit_putname - intercept a putname request
     * @name: name to intercept and delay for putname
     *
     * If we have stored the name from getname in the audit context,
     * then we delay the putname until syscall exit.
     * Called from include/linux/fs.h:putname().
     */
    
    void audit_putname(struct filename *name)
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    {
    	struct audit_context *context = current->audit_context;
    
    	BUG_ON(!context);
    	if (!context->in_syscall) {
    #if AUDIT_DEBUG == 2
    
    		pr_err("%s:%d(:%d): final_putname(%p)\n",
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    		       __FILE__, __LINE__, context->serial, name);
    		if (context->name_count) {
    
    
    			list_for_each_entry(n, &context->names_list, list)
    
    				pr_err("name[%d] = %p = %s\n", i++, n->name,
    				       n->name->name ?: "(null)");
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    #endif
    
    		final_putname(name);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	}
    #if AUDIT_DEBUG
    	else {
    		++context->put_count;
    		if (context->put_count > context->name_count) {
    
    			pr_err("%s:%d(:%d): major=%d in_syscall=%d putname(%p)"
    			       " name_count=%d put_count=%d\n",
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    			       __FILE__, __LINE__,
    			       context->serial, context->major,
    
    			       context->in_syscall, name->name,
    			       context->name_count, context->put_count);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    			dump_stack();
    		}
    	}
    #endif
    }
    
    
     * __audit_inode - store the inode and device from a lookup
    
     * @name: name being audited
    
     * @dentry: dentry being audited
    
     * @flags: attributes for this particular entry
    
    void __audit_inode(struct filename *name, const struct dentry *dentry,
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    {
    	struct audit_context *context = current->audit_context;
    
    	const struct inode *inode = dentry->d_inode;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    	if (!context->in_syscall)
    		return;
    
    #if AUDIT_DEBUG
    	/* The struct filename _must_ have a populated ->name */
    	BUG_ON(!name->name);
    #endif
    	/*
    	 * If we have a pointer to an audit_names entry already, then we can
    	 * just use it directly if the type is correct.
    	 */
    	n = name->aname;
    	if (n) {
    		if (parent) {
    			if (n->type == AUDIT_TYPE_PARENT ||
    			    n->type == AUDIT_TYPE_UNKNOWN)
    				goto out;
    		} else {
    			if (n->type != AUDIT_TYPE_PARENT)
    				goto out;
    		}
    	}
    
    
    	list_for_each_entry_reverse(n, &context->names_list, list) {
    
    		/* does the name pointer match? */
    
    		if (!n->name || n->name->name != name->name)
    
    			continue;
    
    		/* match the correct record type */
    		if (parent) {
    			if (n->type == AUDIT_TYPE_PARENT ||
    			    n->type == AUDIT_TYPE_UNKNOWN)
    				goto out;
    		} else {
    			if (n->type != AUDIT_TYPE_PARENT)
    				goto out;
    		}
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	}
    
    	/* unable to find the name from a previous getname(). Allocate a new
    	 * anonymous entry.
    	 */
    
    	n = audit_alloc_name(context, AUDIT_TYPE_NORMAL);
    
    		n->name_len = n->name ? parent_len(n->name->name) : AUDIT_NAME_FULL;
    
    		n->type = AUDIT_TYPE_PARENT;
    
    	} else {
    		n->name_len = AUDIT_NAME_FULL;
    		n->type = AUDIT_TYPE_NORMAL;
    	}
    
    	handle_path(dentry);
    
     * __audit_inode_child - collect inode info for created/removed objects
    
     * @parent: inode of dentry parent
    
     * @dentry: dentry being audited
    
     * @type:   AUDIT_TYPE_* value that we're looking for
    
     *
     * For syscalls that create or remove filesystem objects, audit_inode
     * can only collect information for the filesystem object's parent.
     * This call updates the audit context with the child's information.
     * Syscalls that create a new filesystem object must be hooked after
     * the object is created.  Syscalls that remove a filesystem object
     * must be hooked prior, in order to capture the target inode during
     * unsuccessful attempts.
     */
    
    void __audit_inode_child(const struct inode *parent,
    
    			 const struct dentry *dentry,
    			 const unsigned char type)
    
    {
    	struct audit_context *context = current->audit_context;
    
    	const struct inode *inode = dentry->d_inode;
    
    	const char *dname = dentry->d_name.name;
    
    	struct audit_names *n, *found_parent = NULL, *found_child = NULL;
    
    	if (inode)
    		handle_one(inode);
    
    	/* look for a parent entry first */
    
    	list_for_each_entry(n, &context->names_list, list) {
    
    		if (!n->name || n->type != AUDIT_TYPE_PARENT)
    
    			continue;
    
    		if (n->ino == parent->i_ino &&
    
    		    !audit_compare_dname_path(dname, n->name->name, n->name_len)) {
    
    	/* is there a matching child entry? */
    
    	list_for_each_entry(n, &context->names_list, list) {
    
    		/* can only match entries that have a name */
    		if (!n->name || n->type != type)
    			continue;
    
    		/* if we found a parent, make sure this one is a child of it */
    		if (found_parent && (n->name != found_parent->name))
    
    		if (!strcmp(dname, n->name->name) ||
    		    !audit_compare_dname_path(dname, n->name->name,
    
    						found_parent ?
    						found_parent->name_len :
    
    						AUDIT_NAME_FULL)) {
    
    
    	if (!found_parent) {
    
    		/* create a new, "anonymous" parent record */
    		n = audit_alloc_name(context, AUDIT_TYPE_PARENT);
    
    			return;
    
    
    	if (!found_child) {
    
    		found_child = audit_alloc_name(context, type);
    		if (!found_child)
    
    			return;
    
    		/* Re-use the name belonging to the slot for a matching parent
    		 * directory. All names for this context are relinquished in
    		 * audit_free_names() */
    		if (found_parent) {
    
    			found_child->name = found_parent->name;
    			found_child->name_len = AUDIT_NAME_FULL;
    
    			/* don't call __putname() */
    
    			found_child->name_put = false;
    
    	if (inode)
    		audit_copy_inode(found_child, dentry, inode);
    	else
    		found_child->ino = (unsigned long)-1;
    
    EXPORT_SYMBOL_GPL(__audit_inode_child);
    
    /**
     * auditsc_get_stamp - get local copies of audit_context values
     * @ctx: audit_context for the task
     * @t: timespec to store time recorded in the audit_context
     * @serial: serial value that is recorded in the audit_context
     *
     * Also sets the context as auditable.
     */
    
    int auditsc_get_stamp(struct audit_context *ctx,
    
    		       struct timespec *t, unsigned int *serial)