lsm_audit.c 9.29 KB
Newer Older
Etienne Basset's avatar
Etienne Basset committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
/*
 * common LSM auditing functions
 *
 * Based on code written for SELinux by :
 *			Stephen Smalley, <sds@epoch.ncsc.mil>
 * 			James Morris <jmorris@redhat.com>
 * Author : Etienne Basset, <etienne.basset@ensta.org>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2,
 * as published by the Free Software Foundation.
 */

#include <linux/types.h>
#include <linux/stddef.h>
#include <linux/kernel.h>
17
#include <linux/gfp.h>
Etienne Basset's avatar
Etienne Basset committed
18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51
#include <linux/fs.h>
#include <linux/init.h>
#include <net/sock.h>
#include <linux/un.h>
#include <net/af_unix.h>
#include <linux/audit.h>
#include <linux/ipv6.h>
#include <linux/ip.h>
#include <net/ip.h>
#include <net/ipv6.h>
#include <linux/tcp.h>
#include <linux/udp.h>
#include <linux/dccp.h>
#include <linux/sctp.h>
#include <linux/lsm_audit.h>

/**
 * ipv4_skb_to_auditdata : fill auditdata from skb
 * @skb : the skb
 * @ad : the audit data to fill
 * @proto : the layer 4 protocol
 *
 * return  0 on success
 */
int ipv4_skb_to_auditdata(struct sk_buff *skb,
		struct common_audit_data *ad, u8 *proto)
{
	int ret = 0;
	struct iphdr *ih;

	ih = ip_hdr(skb);
	if (ih == NULL)
		return -EINVAL;

52 53
	ad->u.net->v4info.saddr = ih->saddr;
	ad->u.net->v4info.daddr = ih->daddr;
Etienne Basset's avatar
Etienne Basset committed
54 55 56 57 58 59 60 61 62 63 64 65 66

	if (proto)
		*proto = ih->protocol;
	/* non initial fragment */
	if (ntohs(ih->frag_off) & IP_OFFSET)
		return 0;

	switch (ih->protocol) {
	case IPPROTO_TCP: {
		struct tcphdr *th = tcp_hdr(skb);
		if (th == NULL)
			break;

67 68
		ad->u.net->sport = th->source;
		ad->u.net->dport = th->dest;
Etienne Basset's avatar
Etienne Basset committed
69 70 71 72 73 74 75
		break;
	}
	case IPPROTO_UDP: {
		struct udphdr *uh = udp_hdr(skb);
		if (uh == NULL)
			break;

76 77
		ad->u.net->sport = uh->source;
		ad->u.net->dport = uh->dest;
Etienne Basset's avatar
Etienne Basset committed
78 79 80 81 82 83 84
		break;
	}
	case IPPROTO_DCCP: {
		struct dccp_hdr *dh = dccp_hdr(skb);
		if (dh == NULL)
			break;

85 86
		ad->u.net->sport = dh->dccph_sport;
		ad->u.net->dport = dh->dccph_dport;
Etienne Basset's avatar
Etienne Basset committed
87 88 89 90 91 92
		break;
	}
	case IPPROTO_SCTP: {
		struct sctphdr *sh = sctp_hdr(skb);
		if (sh == NULL)
			break;
93 94
		ad->u.net->sport = sh->source;
		ad->u.net->dport = sh->dest;
Etienne Basset's avatar
Etienne Basset committed
95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116
		break;
	}
	default:
		ret = -EINVAL;
	}
	return ret;
}
#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
/**
 * ipv6_skb_to_auditdata : fill auditdata from skb
 * @skb : the skb
 * @ad : the audit data to fill
 * @proto : the layer 4 protocol
 *
 * return  0 on success
 */
int ipv6_skb_to_auditdata(struct sk_buff *skb,
		struct common_audit_data *ad, u8 *proto)
{
	int offset, ret = 0;
	struct ipv6hdr *ip6;
	u8 nexthdr;
117
	__be16 frag_off;
Etienne Basset's avatar
Etienne Basset committed
118 119 120 121

	ip6 = ipv6_hdr(skb);
	if (ip6 == NULL)
		return -EINVAL;
122 123
	ad->u.net->v6info.saddr = ip6->saddr;
	ad->u.net->v6info.daddr = ip6->daddr;
Etienne Basset's avatar
Etienne Basset committed
124 125 126 127 128 129
	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;
130
	offset = ipv6_skip_exthdr(skb, offset, &nexthdr, &frag_off);
Etienne Basset's avatar
Etienne Basset committed
131 132 133 134 135 136 137 138 139 140 141 142
	if (offset < 0)
		return 0;
	if (proto)
		*proto = nexthdr;
	switch (nexthdr) {
	case IPPROTO_TCP: {
		struct tcphdr _tcph, *th;

		th = skb_header_pointer(skb, offset, sizeof(_tcph), &_tcph);
		if (th == NULL)
			break;

143 144
		ad->u.net->sport = th->source;
		ad->u.net->dport = th->dest;
Etienne Basset's avatar
Etienne Basset committed
145 146 147 148 149 150 151 152 153
		break;
	}
	case IPPROTO_UDP: {
		struct udphdr _udph, *uh;

		uh = skb_header_pointer(skb, offset, sizeof(_udph), &_udph);
		if (uh == NULL)
			break;

154 155
		ad->u.net->sport = uh->source;
		ad->u.net->dport = uh->dest;
Etienne Basset's avatar
Etienne Basset committed
156 157 158 159 160 161 162 163 164
		break;
	}
	case IPPROTO_DCCP: {
		struct dccp_hdr _dccph, *dh;

		dh = skb_header_pointer(skb, offset, sizeof(_dccph), &_dccph);
		if (dh == NULL)
			break;

165 166
		ad->u.net->sport = dh->dccph_sport;
		ad->u.net->dport = dh->dccph_dport;
Etienne Basset's avatar
Etienne Basset committed
167 168 169 170 171 172 173 174
		break;
	}
	case IPPROTO_SCTP: {
		struct sctphdr _sctph, *sh;

		sh = skb_header_pointer(skb, offset, sizeof(_sctph), &_sctph);
		if (sh == NULL)
			break;
175 176
		ad->u.net->sport = sh->source;
		ad->u.net->dport = sh->dest;
Etienne Basset's avatar
Etienne Basset committed
177 178 179 180 181 182 183 184 185 186 187 188 189 190 191
		break;
	}
	default:
		ret = -EINVAL;
	}
	return ret;
}
#endif


static inline void print_ipv6_addr(struct audit_buffer *ab,
				   struct in6_addr *addr, __be16 port,
				   char *name1, char *name2)
{
	if (!ipv6_addr_any(addr))
192
		audit_log_format(ab, " %s=%pI6c", name1, addr);
Etienne Basset's avatar
Etienne Basset committed
193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215
	if (port)
		audit_log_format(ab, " %s=%d", name2, ntohs(port));
}

static inline void print_ipv4_addr(struct audit_buffer *ab, __be32 addr,
				   __be16 port, char *name1, char *name2)
{
	if (addr)
		audit_log_format(ab, " %s=%pI4", name1, &addr);
	if (port)
		audit_log_format(ab, " %s=%d", name2, ntohs(port));
}

/**
 * dump_common_audit_data - helper to dump common audit data
 * @a : common audit data
 *
 */
static void dump_common_audit_data(struct audit_buffer *ab,
				   struct common_audit_data *a)
{
	struct task_struct *tsk = current;

216 217 218 219 220 221 222
	/*
	 * 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);

223 224
	audit_log_format(ab, " pid=%d comm=", tsk->pid);
	audit_log_untrustedstring(ab, tsk->comm);
Etienne Basset's avatar
Etienne Basset committed
225 226

	switch (a->type) {
227
	case LSM_AUDIT_DATA_NONE:
228
		return;
Etienne Basset's avatar
Etienne Basset committed
229 230 231 232 233 234
	case LSM_AUDIT_DATA_IPC:
		audit_log_format(ab, " key=%d ", a->u.ipc_id);
		break;
	case LSM_AUDIT_DATA_CAP:
		audit_log_format(ab, " capability=%d ", a->u.cap);
		break;
235 236 237
	case LSM_AUDIT_DATA_PATH: {
		struct inode *inode;

238
		audit_log_d_path(ab, " path=", &a->u.path);
239 240

		inode = a->u.path.dentry->d_inode;
241 242 243 244 245
		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);
		}
246 247 248 249 250 251 252 253 254
		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;
255 256 257 258 259
		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);
		}
Etienne Basset's avatar
Etienne Basset committed
260
		break;
261 262 263 264 265 266 267 268 269 270 271 272 273
	}
	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);
		}
274 275 276
		audit_log_format(ab, " dev=");
		audit_log_untrustedstring(ab, inode->i_sb->s_id);
		audit_log_format(ab, " ino=%lu", inode->i_ino);
277 278
		break;
	}
Etienne Basset's avatar
Etienne Basset committed
279 280 281 282 283 284 285 286
	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);
		}
		break;
	case LSM_AUDIT_DATA_NET:
287 288
		if (a->u.net->sk) {
			struct sock *sk = a->u.net->sk;
Etienne Basset's avatar
Etienne Basset committed
289 290 291 292 293 294 295 296
			struct unix_sock *u;
			int len = 0;
			char *p = NULL;

			switch (sk->sk_family) {
			case AF_INET: {
				struct inet_sock *inet = inet_sk(sk);

297 298
				print_ipv4_addr(ab, inet->inet_rcv_saddr,
						inet->inet_sport,
Etienne Basset's avatar
Etienne Basset committed
299
						"laddr", "lport");
300 301
				print_ipv4_addr(ab, inet->inet_daddr,
						inet->inet_dport,
Etienne Basset's avatar
Etienne Basset committed
302 303 304 305 306 307
						"faddr", "fport");
				break;
			}
			case AF_INET6: {
				struct inet_sock *inet = inet_sk(sk);

308
				print_ipv6_addr(ab, &sk->sk_v6_rcv_saddr,
309
						inet->inet_sport,
Etienne Basset's avatar
Etienne Basset committed
310
						"laddr", "lport");
311
				print_ipv6_addr(ab, &sk->sk_v6_daddr,
312
						inet->inet_dport,
Etienne Basset's avatar
Etienne Basset committed
313 314 315 316 317
						"faddr", "fport");
				break;
			}
			case AF_UNIX:
				u = unix_sk(sk);
Al Viro's avatar
Al Viro committed
318 319
				if (u->path.dentry) {
					audit_log_d_path(ab, " path=", &u->path);
Etienne Basset's avatar
Etienne Basset committed
320 321 322 323 324 325 326 327 328 329 330 331 332 333 334
					break;
				}
				if (!u->addr)
					break;
				len = u->addr->len-sizeof(short);
				p = &u->addr->name->sun_path[0];
				audit_log_format(ab, " path=");
				if (*p)
					audit_log_untrustedstring(ab, p);
				else
					audit_log_n_hex(ab, p, len);
				break;
			}
		}

335
		switch (a->u.net->family) {
Etienne Basset's avatar
Etienne Basset committed
336
		case AF_INET:
337 338
			print_ipv4_addr(ab, a->u.net->v4info.saddr,
					a->u.net->sport,
Etienne Basset's avatar
Etienne Basset committed
339
					"saddr", "src");
340 341
			print_ipv4_addr(ab, a->u.net->v4info.daddr,
					a->u.net->dport,
Etienne Basset's avatar
Etienne Basset committed
342 343 344
					"daddr", "dest");
			break;
		case AF_INET6:
345 346
			print_ipv6_addr(ab, &a->u.net->v6info.saddr,
					a->u.net->sport,
Etienne Basset's avatar
Etienne Basset committed
347
					"saddr", "src");
348 349
			print_ipv6_addr(ab, &a->u.net->v6info.daddr,
					a->u.net->dport,
Etienne Basset's avatar
Etienne Basset committed
350 351 352
					"daddr", "dest");
			break;
		}
353
		if (a->u.net->netif > 0) {
Etienne Basset's avatar
Etienne Basset committed
354 355 356
			struct net_device *dev;

			/* NOTE: we always use init's namespace */
357
			dev = dev_get_by_index(&init_net, a->u.net->netif);
Etienne Basset's avatar
Etienne Basset committed
358 359 360 361 362 363 364 365 366 367 368 369 370 371 372
			if (dev) {
				audit_log_format(ab, " netif=%s", dev->name);
				dev_put(dev);
			}
		}
		break;
#ifdef CONFIG_KEYS
	case LSM_AUDIT_DATA_KEY:
		audit_log_format(ab, " key_serial=%u", a->u.key_struct.key);
		if (a->u.key_struct.key_desc) {
			audit_log_format(ab, " key_desc=");
			audit_log_untrustedstring(ab, a->u.key_struct.key_desc);
		}
		break;
#endif
373 374 375 376
	case LSM_AUDIT_DATA_KMOD:
		audit_log_format(ab, " kmod=");
		audit_log_untrustedstring(ab, a->u.kmod_name);
		break;
Etienne Basset's avatar
Etienne Basset committed
377 378 379 380 381 382
	} /* switch (a->type) */
}

/**
 * common_lsm_audit - generic LSM auditing function
 * @a:  auxiliary audit data
383 384
 * @pre_audit: lsm-specific pre-audit callback
 * @post_audit: lsm-specific post-audit callback
Etienne Basset's avatar
Etienne Basset committed
385 386 387 388
 *
 * setup the audit buffer for common security information
 * uses callback to print LSM specific information
 */
389 390 391
void common_lsm_audit(struct common_audit_data *a,
	void (*pre_audit)(struct audit_buffer *, void *),
	void (*post_audit)(struct audit_buffer *, void *))
Etienne Basset's avatar
Etienne Basset committed
392 393 394 395 396 397 398 399 400 401 402
{
	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);

	if (ab == NULL)
		return;

403 404
	if (pre_audit)
		pre_audit(ab, a);
Etienne Basset's avatar
Etienne Basset committed
405 406 407

	dump_common_audit_data(ab, a);

408 409
	if (post_audit)
		post_audit(ab, a);
Etienne Basset's avatar
Etienne Basset committed
410 411 412

	audit_log_end(ab);
}