diff options
Diffstat (limited to 'kernel/audit.c')
| -rw-r--r-- | kernel/audit.c | 153 | 
1 files changed, 139 insertions, 14 deletions
diff --git a/kernel/audit.c b/kernel/audit.c index 7b0e23a740ce..906ae5a0233a 100644 --- a/kernel/audit.c +++ b/kernel/audit.c @@ -60,7 +60,6 @@  #ifdef CONFIG_SECURITY  #include <linux/security.h>  #endif -#include <net/netlink.h>  #include <linux/freezer.h>  #include <linux/tty.h>  #include <linux/pid_namespace.h> @@ -140,6 +139,17 @@ static struct task_struct *kauditd_task;  static DECLARE_WAIT_QUEUE_HEAD(kauditd_wait);  static DECLARE_WAIT_QUEUE_HEAD(audit_backlog_wait); +static struct audit_features af = {.vers = AUDIT_FEATURE_VERSION, +				   .mask = -1, +				   .features = 0, +				   .lock = 0,}; + +static char *audit_feature_names[2] = { +	"only_unset_loginuid", +	"loginuid_immutable", +}; + +  /* Serialize requests from userspace. */  DEFINE_MUTEX(audit_cmd_mutex); @@ -584,6 +594,8 @@ static int audit_netlink_ok(struct sk_buff *skb, u16 msg_type)  		return -EOPNOTSUPP;  	case AUDIT_GET:  	case AUDIT_SET: +	case AUDIT_GET_FEATURE: +	case AUDIT_SET_FEATURE:  	case AUDIT_LIST_RULES:  	case AUDIT_ADD_RULE:  	case AUDIT_DEL_RULE: @@ -613,7 +625,7 @@ static int audit_log_common_recv_msg(struct audit_buffer **ab, u16 msg_type)  	int rc = 0;  	uid_t uid = from_kuid(&init_user_ns, current_uid()); -	if (!audit_enabled) { +	if (!audit_enabled && msg_type != AUDIT_USER_AVC) {  		*ab = NULL;  		return rc;  	} @@ -628,6 +640,94 @@ static int audit_log_common_recv_msg(struct audit_buffer **ab, u16 msg_type)  	return rc;  } +int is_audit_feature_set(int i) +{ +	return af.features & AUDIT_FEATURE_TO_MASK(i); +} + + +static int audit_get_feature(struct sk_buff *skb) +{ +	u32 seq; + +	seq = nlmsg_hdr(skb)->nlmsg_seq; + +	audit_send_reply(NETLINK_CB(skb).portid, seq, AUDIT_GET, 0, 0, +			 &af, sizeof(af)); + +	return 0; +} + +static void audit_log_feature_change(int which, u32 old_feature, u32 new_feature, +				     u32 old_lock, u32 new_lock, int res) +{ +	struct audit_buffer *ab; + +	ab = audit_log_start(NULL, GFP_KERNEL, AUDIT_FEATURE_CHANGE); +	audit_log_format(ab, "feature=%s new=%d old=%d old_lock=%d new_lock=%d res=%d", +			 audit_feature_names[which], !!old_feature, !!new_feature, +			 !!old_lock, !!new_lock, res); +	audit_log_end(ab); +} + +static int audit_set_feature(struct sk_buff *skb) +{ +	struct audit_features *uaf; +	int i; + +	BUILD_BUG_ON(AUDIT_LAST_FEATURE + 1 > sizeof(audit_feature_names)/sizeof(audit_feature_names[0])); +	uaf = nlmsg_data(nlmsg_hdr(skb)); + +	/* if there is ever a version 2 we should handle that here */ + +	for (i = 0; i <= AUDIT_LAST_FEATURE; i++) { +		u32 feature = AUDIT_FEATURE_TO_MASK(i); +		u32 old_feature, new_feature, old_lock, new_lock; + +		/* if we are not changing this feature, move along */ +		if (!(feature & uaf->mask)) +			continue; + +		old_feature = af.features & feature; +		new_feature = uaf->features & feature; +		new_lock = (uaf->lock | af.lock) & feature; +		old_lock = af.lock & feature; + +		/* are we changing a locked feature? */ +		if ((af.lock & feature) && (new_feature != old_feature)) { +			audit_log_feature_change(i, old_feature, new_feature, +						 old_lock, new_lock, 0); +			return -EPERM; +		} +	} +	/* nothing invalid, do the changes */ +	for (i = 0; i <= AUDIT_LAST_FEATURE; i++) { +		u32 feature = AUDIT_FEATURE_TO_MASK(i); +		u32 old_feature, new_feature, old_lock, new_lock; + +		/* if we are not changing this feature, move along */ +		if (!(feature & uaf->mask)) +			continue; + +		old_feature = af.features & feature; +		new_feature = uaf->features & feature; +		old_lock = af.lock & feature; +		new_lock = (uaf->lock | af.lock) & feature; + +		if (new_feature != old_feature) +			audit_log_feature_change(i, old_feature, new_feature, +						 old_lock, new_lock, 1); + +		if (new_feature) +			af.features |= feature; +		else +			af.features &= ~feature; +		af.lock |= new_lock; +	} + +	return 0; +} +  static int audit_receive_msg(struct sk_buff *skb, struct nlmsghdr *nlh)  {  	u32			seq; @@ -659,6 +759,7 @@ static int audit_receive_msg(struct sk_buff *skb, struct nlmsghdr *nlh)  	switch (msg_type) {  	case AUDIT_GET: +		memset(&status_set, 0, sizeof(status_set));  		status_set.enabled	 = audit_enabled;  		status_set.failure	 = audit_failure;  		status_set.pid		 = audit_pid; @@ -670,7 +771,7 @@ static int audit_receive_msg(struct sk_buff *skb, struct nlmsghdr *nlh)  				 &status_set, sizeof(status_set));  		break;  	case AUDIT_SET: -		if (nlh->nlmsg_len < sizeof(struct audit_status)) +		if (nlmsg_len(nlh) < sizeof(struct audit_status))  			return -EINVAL;  		status_get   = (struct audit_status *)data;  		if (status_get->mask & AUDIT_STATUS_ENABLED) { @@ -699,6 +800,16 @@ static int audit_receive_msg(struct sk_buff *skb, struct nlmsghdr *nlh)  		if (status_get->mask & AUDIT_STATUS_BACKLOG_LIMIT)  			err = audit_set_backlog_limit(status_get->backlog_limit);  		break; +	case AUDIT_GET_FEATURE: +		err = audit_get_feature(skb); +		if (err) +			return err; +		break; +	case AUDIT_SET_FEATURE: +		err = audit_set_feature(skb); +		if (err) +			return err; +		break;  	case AUDIT_USER:  	case AUDIT_FIRST_USER_MSG ... AUDIT_LAST_USER_MSG:  	case AUDIT_FIRST_USER_MSG2 ... AUDIT_LAST_USER_MSG2: @@ -715,7 +826,8 @@ static int audit_receive_msg(struct sk_buff *skb, struct nlmsghdr *nlh)  			}  			audit_log_common_recv_msg(&ab, msg_type);  			if (msg_type != AUDIT_USER_TTY) -				audit_log_format(ab, " msg='%.1024s'", +				audit_log_format(ab, " msg='%.*s'", +						 AUDIT_MESSAGE_TEXT_MAX,  						 (char *)data);  			else {  				int size; @@ -818,7 +930,7 @@ static int audit_receive_msg(struct sk_buff *skb, struct nlmsghdr *nlh)  		struct task_struct *tsk = current;  		spin_lock(&tsk->sighand->siglock); -		s.enabled = tsk->signal->audit_tty != 0; +		s.enabled = tsk->signal->audit_tty;  		s.log_passwd = tsk->signal->audit_tty_log_passwd;  		spin_unlock(&tsk->sighand->siglock); @@ -832,7 +944,7 @@ static int audit_receive_msg(struct sk_buff *skb, struct nlmsghdr *nlh)  		memset(&s, 0, sizeof(s));  		/* guard against past and future API changes */ -		memcpy(&s, data, min(sizeof(s), (size_t)nlh->nlmsg_len)); +		memcpy(&s, data, min_t(size_t, sizeof(s), nlmsg_len(nlh)));  		if ((s.enabled != 0 && s.enabled != 1) ||  		    (s.log_passwd != 0 && s.log_passwd != 1))  			return -EINVAL; @@ -1067,13 +1179,6 @@ static void wait_for_auditd(unsigned long sleep_time)  	remove_wait_queue(&audit_backlog_wait, &wait);  } -/* Obtain an audit buffer.  This routine does locking to obtain the - * audit buffer, but then no locking is required for calls to - * audit_log_*format.  If the tsk is a task that is currently in a - * syscall, then the syscall is marked as auditable and an audit record - * will be written at syscall exit.  If there is no associated task, tsk - * should be NULL. */ -  /**   * audit_log_start - obtain an audit buffer   * @ctx: audit_context (may be NULL) @@ -1389,7 +1494,7 @@ void audit_log_session_info(struct audit_buffer *ab)  	u32 sessionid = audit_get_sessionid(current);  	uid_t auid = from_kuid(&init_user_ns, audit_get_loginuid(current)); -	audit_log_format(ab, " auid=%u ses=%u\n", auid, sessionid); +	audit_log_format(ab, " auid=%u ses=%u", auid, sessionid);  }  void audit_log_key(struct audit_buffer *ab, char *key) @@ -1536,6 +1641,26 @@ void audit_log_name(struct audit_context *context, struct audit_names *n,  		}  	} +	/* log the audit_names record type */ +	audit_log_format(ab, " nametype="); +	switch(n->type) { +	case AUDIT_TYPE_NORMAL: +		audit_log_format(ab, "NORMAL"); +		break; +	case AUDIT_TYPE_PARENT: +		audit_log_format(ab, "PARENT"); +		break; +	case AUDIT_TYPE_CHILD_DELETE: +		audit_log_format(ab, "DELETE"); +		break; +	case AUDIT_TYPE_CHILD_CREATE: +		audit_log_format(ab, "CREATE"); +		break; +	default: +		audit_log_format(ab, "UNKNOWN"); +		break; +	} +  	audit_log_fcaps(ab, n);  	audit_log_end(ab);  }  | 
