diff options
Diffstat (limited to 'security/lsm_audit.c')
| -rw-r--r-- | security/lsm_audit.c | 193 | 
1 files changed, 109 insertions, 84 deletions
diff --git a/security/lsm_audit.c b/security/lsm_audit.c index 908aa712816..69fdf3bc765 100644 --- a/security/lsm_audit.c +++ b/security/lsm_audit.c @@ -49,8 +49,8 @@ int ipv4_skb_to_auditdata(struct sk_buff *skb,  	if (ih == NULL)  		return -EINVAL; -	ad->u.net.v4info.saddr = ih->saddr; -	ad->u.net.v4info.daddr = ih->daddr; +	ad->u.net->v4info.saddr = ih->saddr; +	ad->u.net->v4info.daddr = ih->daddr;  	if (proto)  		*proto = ih->protocol; @@ -64,8 +64,8 @@ int ipv4_skb_to_auditdata(struct sk_buff *skb,  		if (th == NULL)  			break; -		ad->u.net.sport = th->source; -		ad->u.net.dport = th->dest; +		ad->u.net->sport = th->source; +		ad->u.net->dport = th->dest;  		break;  	}  	case IPPROTO_UDP: { @@ -73,8 +73,8 @@ int ipv4_skb_to_auditdata(struct sk_buff *skb,  		if (uh == NULL)  			break; -		ad->u.net.sport = uh->source; -		ad->u.net.dport = uh->dest; +		ad->u.net->sport = uh->source; +		ad->u.net->dport = uh->dest;  		break;  	}  	case IPPROTO_DCCP: { @@ -82,16 +82,16 @@ int ipv4_skb_to_auditdata(struct sk_buff *skb,  		if (dh == NULL)  			break; -		ad->u.net.sport = dh->dccph_sport; -		ad->u.net.dport = dh->dccph_dport; +		ad->u.net->sport = dh->dccph_sport; +		ad->u.net->dport = dh->dccph_dport;  		break;  	}  	case IPPROTO_SCTP: {  		struct sctphdr *sh = sctp_hdr(skb);  		if (sh == NULL)  			break; -		ad->u.net.sport = sh->source; -		ad->u.net.dport = sh->dest; +		ad->u.net->sport = sh->source; +		ad->u.net->dport = sh->dest;  		break;  	}  	default: @@ -114,19 +114,20 @@ int ipv6_skb_to_auditdata(struct sk_buff *skb,  	int offset, ret = 0;  	struct ipv6hdr *ip6;  	u8 nexthdr; +	__be16 frag_off;  	ip6 = ipv6_hdr(skb);  	if (ip6 == NULL)  		return -EINVAL; -	ipv6_addr_copy(&ad->u.net.v6info.saddr, &ip6->saddr); -	ipv6_addr_copy(&ad->u.net.v6info.daddr, &ip6->daddr); +	ad->u.net->v6info.saddr = ip6->saddr; +	ad->u.net->v6info.daddr = ip6->daddr;  	ret = 0;  	/* IPv6 can have several extension header before the Transport header  	 * skip them */  	offset = skb_network_offset(skb);  	offset += sizeof(*ip6);  	nexthdr = ip6->nexthdr; -	offset = ipv6_skip_exthdr(skb, offset, &nexthdr); +	offset = ipv6_skip_exthdr(skb, offset, &nexthdr, &frag_off);  	if (offset < 0)  		return 0;  	if (proto) @@ -139,8 +140,8 @@ int ipv6_skb_to_auditdata(struct sk_buff *skb,  		if (th == NULL)  			break; -		ad->u.net.sport = th->source; -		ad->u.net.dport = th->dest; +		ad->u.net->sport = th->source; +		ad->u.net->dport = th->dest;  		break;  	}  	case IPPROTO_UDP: { @@ -150,8 +151,8 @@ int ipv6_skb_to_auditdata(struct sk_buff *skb,  		if (uh == NULL)  			break; -		ad->u.net.sport = uh->source; -		ad->u.net.dport = uh->dest; +		ad->u.net->sport = uh->source; +		ad->u.net->dport = uh->dest;  		break;  	}  	case IPPROTO_DCCP: { @@ -161,8 +162,8 @@ int ipv6_skb_to_auditdata(struct sk_buff *skb,  		if (dh == NULL)  			break; -		ad->u.net.sport = dh->dccph_sport; -		ad->u.net.dport = dh->dccph_dport; +		ad->u.net->sport = dh->dccph_sport; +		ad->u.net->dport = dh->dccph_dport;  		break;  	}  	case IPPROTO_SCTP: { @@ -171,8 +172,8 @@ int ipv6_skb_to_auditdata(struct sk_buff *skb,  		sh = skb_header_pointer(skb, offset, sizeof(_sctph), &_sctph);  		if (sh == NULL)  			break; -		ad->u.net.sport = sh->source; -		ad->u.net.dport = sh->dest; +		ad->u.net->sport = sh->source; +		ad->u.net->dport = sh->dest;  		break;  	}  	default: @@ -210,15 +211,17 @@ static inline void print_ipv4_addr(struct audit_buffer *ab, __be32 addr,  static void dump_common_audit_data(struct audit_buffer *ab,  				   struct common_audit_data *a)  { -	struct inode *inode = NULL;  	struct task_struct *tsk = current; -	if (a->tsk) -		tsk = a->tsk; -	if (tsk && tsk->pid) { -		audit_log_format(ab, " pid=%d comm=", tsk->pid); -		audit_log_untrustedstring(ab, tsk->comm); -	} +	/* +	 * To keep stack sizes in check force programers to notice if they +	 * start making this union too large!  See struct lsm_network_audit +	 * as an example of how to deal with large data. +	 */ +	BUILD_BUG_ON(sizeof(a->u) > sizeof(void *)*2); + +	audit_log_format(ab, " pid=%d comm=", task_pid_nr(tsk)); +	audit_log_untrustedstring(ab, tsk->comm);  	switch (a->type) {  	case LSM_AUDIT_DATA_NONE: @@ -229,43 +232,63 @@ static void dump_common_audit_data(struct audit_buffer *ab,  	case LSM_AUDIT_DATA_CAP:  		audit_log_format(ab, " capability=%d ", a->u.cap);  		break; -	case LSM_AUDIT_DATA_FS: -		if (a->u.fs.path.dentry) { -			struct dentry *dentry = a->u.fs.path.dentry; -			if (a->u.fs.path.mnt) { -				audit_log_d_path(ab, "path=", &a->u.fs.path); -			} else { -				audit_log_format(ab, " name="); -				audit_log_untrustedstring(ab, -						 dentry->d_name.name); -			} -			inode = dentry->d_inode; -		} else if (a->u.fs.inode) { -			struct dentry *dentry; -			inode = a->u.fs.inode; -			dentry = d_find_alias(inode); -			if (dentry) { -				audit_log_format(ab, " name="); -				audit_log_untrustedstring(ab, -						 dentry->d_name.name); -				dput(dentry); -			} +	case LSM_AUDIT_DATA_PATH: { +		struct inode *inode; + +		audit_log_d_path(ab, " path=", &a->u.path); + +		inode = a->u.path.dentry->d_inode; +		if (inode) { +			audit_log_format(ab, " dev="); +			audit_log_untrustedstring(ab, inode->i_sb->s_id); +			audit_log_format(ab, " ino=%lu", inode->i_ino);  		} -		if (inode) -			audit_log_format(ab, " dev=%s ino=%lu", -					inode->i_sb->s_id, -					inode->i_ino);  		break; +	} +	case LSM_AUDIT_DATA_DENTRY: { +		struct inode *inode; + +		audit_log_format(ab, " name="); +		audit_log_untrustedstring(ab, a->u.dentry->d_name.name); + +		inode = a->u.dentry->d_inode; +		if (inode) { +			audit_log_format(ab, " dev="); +			audit_log_untrustedstring(ab, inode->i_sb->s_id); +			audit_log_format(ab, " ino=%lu", inode->i_ino); +		} +		break; +	} +	case LSM_AUDIT_DATA_INODE: { +		struct dentry *dentry; +		struct inode *inode; + +		inode = a->u.inode; +		dentry = d_find_alias(inode); +		if (dentry) { +			audit_log_format(ab, " name="); +			audit_log_untrustedstring(ab, +					 dentry->d_name.name); +			dput(dentry); +		} +		audit_log_format(ab, " dev="); +		audit_log_untrustedstring(ab, inode->i_sb->s_id); +		audit_log_format(ab, " ino=%lu", inode->i_ino); +		break; +	}  	case LSM_AUDIT_DATA_TASK:  		tsk = a->u.tsk; -		if (tsk && tsk->pid) { -			audit_log_format(ab, " pid=%d comm=", tsk->pid); -			audit_log_untrustedstring(ab, tsk->comm); +		if (tsk) { +			pid_t pid = task_pid_nr(tsk); +			if (pid) { +				audit_log_format(ab, " pid=%d comm=", pid); +				audit_log_untrustedstring(ab, tsk->comm); +			}  		}  		break;  	case LSM_AUDIT_DATA_NET: -		if (a->u.net.sk) { -			struct sock *sk = a->u.net.sk; +		if (a->u.net->sk) { +			struct sock *sk = a->u.net->sk;  			struct unix_sock *u;  			int len = 0;  			char *p = NULL; @@ -282,26 +305,23 @@ static void dump_common_audit_data(struct audit_buffer *ab,  						"faddr", "fport");  				break;  			} +#if IS_ENABLED(CONFIG_IPV6)  			case AF_INET6: {  				struct inet_sock *inet = inet_sk(sk); -				struct ipv6_pinfo *inet6 = inet6_sk(sk); -				print_ipv6_addr(ab, &inet6->rcv_saddr, +				print_ipv6_addr(ab, &sk->sk_v6_rcv_saddr,  						inet->inet_sport,  						"laddr", "lport"); -				print_ipv6_addr(ab, &inet6->daddr, +				print_ipv6_addr(ab, &sk->sk_v6_daddr,  						inet->inet_dport,  						"faddr", "fport");  				break;  			} +#endif  			case AF_UNIX:  				u = unix_sk(sk); -				if (u->dentry) { -					struct path path = { -						.dentry = u->dentry, -						.mnt = u->mnt -					}; -					audit_log_d_path(ab, "path=", &path); +				if (u->path.dentry) { +					audit_log_d_path(ab, " path=", &u->path);  					break;  				}  				if (!u->addr) @@ -317,29 +337,29 @@ static void dump_common_audit_data(struct audit_buffer *ab,  			}  		} -		switch (a->u.net.family) { +		switch (a->u.net->family) {  		case AF_INET: -			print_ipv4_addr(ab, a->u.net.v4info.saddr, -					a->u.net.sport, +			print_ipv4_addr(ab, a->u.net->v4info.saddr, +					a->u.net->sport,  					"saddr", "src"); -			print_ipv4_addr(ab, a->u.net.v4info.daddr, -					a->u.net.dport, +			print_ipv4_addr(ab, a->u.net->v4info.daddr, +					a->u.net->dport,  					"daddr", "dest");  			break;  		case AF_INET6: -			print_ipv6_addr(ab, &a->u.net.v6info.saddr, -					a->u.net.sport, +			print_ipv6_addr(ab, &a->u.net->v6info.saddr, +					a->u.net->sport,  					"saddr", "src"); -			print_ipv6_addr(ab, &a->u.net.v6info.daddr, -					a->u.net.dport, +			print_ipv6_addr(ab, &a->u.net->v6info.daddr, +					a->u.net->dport,  					"daddr", "dest");  			break;  		} -		if (a->u.net.netif > 0) { +		if (a->u.net->netif > 0) {  			struct net_device *dev;  			/* NOTE: we always use init's namespace */ -			dev = dev_get_by_index(&init_net, a->u.net.netif); +			dev = dev_get_by_index(&init_net, a->u.net->netif);  			if (dev) {  				audit_log_format(ab, " netif=%s", dev->name);  				dev_put(dev); @@ -365,29 +385,34 @@ static void dump_common_audit_data(struct audit_buffer *ab,  /**   * common_lsm_audit - generic LSM auditing function   * @a:  auxiliary audit data + * @pre_audit: lsm-specific pre-audit callback + * @post_audit: lsm-specific post-audit callback   *   * setup the audit buffer for common security information   * uses callback to print LSM specific information   */ -void common_lsm_audit(struct common_audit_data *a) +void common_lsm_audit(struct common_audit_data *a, +	void (*pre_audit)(struct audit_buffer *, void *), +	void (*post_audit)(struct audit_buffer *, void *))  {  	struct audit_buffer *ab;  	if (a == NULL)  		return;  	/* we use GFP_ATOMIC so we won't sleep */ -	ab = audit_log_start(current->audit_context, GFP_ATOMIC, AUDIT_AVC); +	ab = audit_log_start(current->audit_context, GFP_ATOMIC | __GFP_NOWARN, +			     AUDIT_AVC);  	if (ab == NULL)  		return; -	if (a->lsm_pre_audit) -		a->lsm_pre_audit(ab, a); +	if (pre_audit) +		pre_audit(ab, a);  	dump_common_audit_data(ab, a); -	if (a->lsm_post_audit) -		a->lsm_post_audit(ab, a); +	if (post_audit) +		post_audit(ab, a);  	audit_log_end(ab);  }  | 
