lsm_audit.c 10.6 KB
Newer Older
Etienne Basset's avatar
Etienne Basset committed
1 2 3 4
/*
 * common LSM auditing functions
 *
 * Based on code written for SELinux by :
5
 *			Stephen Smalley, <sds@tycho.nsa.gov>
Etienne Basset's avatar
Etienne Basset committed
6 7 8 9 10 11 12 13 14 15 16
 * 			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
		break;
	}
	default:
		ret = -EINVAL;
	}
	return ret;
}
102
#if IS_ENABLED(CONFIG_IPV6)
Etienne Basset's avatar
Etienne Basset committed
103 104 105 106 107 108 109 110 111 112 113 114 115 116
/**
 * 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
	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)
{
214
	char comm[sizeof(current->comm)];
Etienne Basset's avatar
Etienne Basset committed
215

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
	audit_log_format(ab, " pid=%d comm=", task_tgid_nr(current));
224
	audit_log_untrustedstring(ab, memcpy(comm, current->comm, sizeof(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 = d_backing_inode(a->u.path.dentry);
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
		break;
	}
248 249 250 251 252 253 254 255 256 257 258 259 260
	case LSM_AUDIT_DATA_FILE: {
		struct inode *inode;

		audit_log_d_path(ab, " path=", &a->u.file->f_path);

		inode = file_inode(a->u.file);
		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;
	}
261 262 263 264 265 266 267 268 269 270 271 272
	case LSM_AUDIT_DATA_IOCTL_OP: {
		struct inode *inode;

		audit_log_d_path(ab, " path=", &a->u.op->path);

		inode = a->u.op->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);
		}

273
		audit_log_format(ab, " ioctlcmd=0x%hx", a->u.op->cmd);
274 275
		break;
	}
276 277 278 279 280 281
	case LSM_AUDIT_DATA_DENTRY: {
		struct inode *inode;

		audit_log_format(ab, " name=");
		audit_log_untrustedstring(ab, a->u.dentry->d_name.name);

282
		inode = d_backing_inode(a->u.dentry);
283 284 285 286 287
		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
288
		break;
289 290 291 292 293 294 295 296 297 298 299 300 301
	}
	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);
		}
302 303 304
		audit_log_format(ab, " dev=");
		audit_log_untrustedstring(ab, inode->i_sb->s_id);
		audit_log_format(ab, " ino=%lu", inode->i_ino);
305 306
		break;
	}
307 308
	case LSM_AUDIT_DATA_TASK: {
		struct task_struct *tsk = a->u.tsk;
309
		if (tsk) {
310
			pid_t pid = task_tgid_nr(tsk);
311
			if (pid) {
312
				char comm[sizeof(tsk->comm)];
313
				audit_log_format(ab, " opid=%d ocomm=", pid);
314 315
				audit_log_untrustedstring(ab,
				    memcpy(comm, tsk->comm, sizeof(comm)));
316
			}
Etienne Basset's avatar
Etienne Basset committed
317 318
		}
		break;
319
	}
Etienne Basset's avatar
Etienne Basset committed
320
	case LSM_AUDIT_DATA_NET:
321 322
		if (a->u.net->sk) {
			struct sock *sk = a->u.net->sk;
Etienne Basset's avatar
Etienne Basset committed
323 324 325 326 327 328 329 330
			struct unix_sock *u;
			int len = 0;
			char *p = NULL;

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

331 332
				print_ipv4_addr(ab, inet->inet_rcv_saddr,
						inet->inet_sport,
Etienne Basset's avatar
Etienne Basset committed
333
						"laddr", "lport");
334 335
				print_ipv4_addr(ab, inet->inet_daddr,
						inet->inet_dport,
Etienne Basset's avatar
Etienne Basset committed
336 337 338
						"faddr", "fport");
				break;
			}
339
#if IS_ENABLED(CONFIG_IPV6)
Etienne Basset's avatar
Etienne Basset committed
340 341 342
			case AF_INET6: {
				struct inet_sock *inet = inet_sk(sk);

343
				print_ipv6_addr(ab, &sk->sk_v6_rcv_saddr,
344
						inet->inet_sport,
Etienne Basset's avatar
Etienne Basset committed
345
						"laddr", "lport");
346
				print_ipv6_addr(ab, &sk->sk_v6_daddr,
347
						inet->inet_dport,
Etienne Basset's avatar
Etienne Basset committed
348 349 350
						"faddr", "fport");
				break;
			}
351
#endif
Etienne Basset's avatar
Etienne Basset committed
352 353
			case AF_UNIX:
				u = unix_sk(sk);
Al Viro's avatar
Al Viro committed
354 355
				if (u->path.dentry) {
					audit_log_d_path(ab, " path=", &u->path);
Etienne Basset's avatar
Etienne Basset committed
356 357 358 359 360 361 362 363 364 365 366 367 368 369 370
					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;
			}
		}

371
		switch (a->u.net->family) {
Etienne Basset's avatar
Etienne Basset committed
372
		case AF_INET:
373 374
			print_ipv4_addr(ab, a->u.net->v4info.saddr,
					a->u.net->sport,
Etienne Basset's avatar
Etienne Basset committed
375
					"saddr", "src");
376 377
			print_ipv4_addr(ab, a->u.net->v4info.daddr,
					a->u.net->dport,
Etienne Basset's avatar
Etienne Basset committed
378 379 380
					"daddr", "dest");
			break;
		case AF_INET6:
381 382
			print_ipv6_addr(ab, &a->u.net->v6info.saddr,
					a->u.net->sport,
Etienne Basset's avatar
Etienne Basset committed
383
					"saddr", "src");
384 385
			print_ipv6_addr(ab, &a->u.net->v6info.daddr,
					a->u.net->dport,
Etienne Basset's avatar
Etienne Basset committed
386 387 388
					"daddr", "dest");
			break;
		}
389
		if (a->u.net->netif > 0) {
Etienne Basset's avatar
Etienne Basset committed
390 391 392
			struct net_device *dev;

			/* NOTE: we always use init's namespace */
393
			dev = dev_get_by_index(&init_net, a->u.net->netif);
Etienne Basset's avatar
Etienne Basset committed
394 395 396 397 398 399 400 401 402 403 404 405 406 407 408
			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
409 410 411 412
	case LSM_AUDIT_DATA_KMOD:
		audit_log_format(ab, " kmod=");
		audit_log_untrustedstring(ab, a->u.kmod_name);
		break;
413 414 415 416 417 418 419 420 421 422 423
	case LSM_AUDIT_DATA_IBPKEY: {
		struct in6_addr sbn_pfx;

		memset(&sbn_pfx.s6_addr, 0,
		       sizeof(sbn_pfx.s6_addr));
		memcpy(&sbn_pfx.s6_addr, &a->u.ibpkey->subnet_prefix,
		       sizeof(a->u.ibpkey->subnet_prefix));
		audit_log_format(ab, " pkey=0x%x subnet_prefix=%pI6c",
				 a->u.ibpkey->pkey, &sbn_pfx);
		break;
	}
424 425 426 427 428
	case LSM_AUDIT_DATA_IBENDPORT:
		audit_log_format(ab, " device=%s port_num=%u",
				 a->u.ibendport->dev_name,
				 a->u.ibendport->port);
		break;
Etienne Basset's avatar
Etienne Basset committed
429 430 431 432 433 434
	} /* switch (a->type) */
}

/**
 * common_lsm_audit - generic LSM auditing function
 * @a:  auxiliary audit data
435 436
 * @pre_audit: lsm-specific pre-audit callback
 * @post_audit: lsm-specific post-audit callback
Etienne Basset's avatar
Etienne Basset committed
437 438 439 440
 *
 * setup the audit buffer for common security information
 * uses callback to print LSM specific information
 */
441 442 443
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
444 445 446 447 448 449
{
	struct audit_buffer *ab;

	if (a == NULL)
		return;
	/* we use GFP_ATOMIC so we won't sleep */
450
	ab = audit_log_start(audit_context(), GFP_ATOMIC | __GFP_NOWARN,
451
			     AUDIT_AVC);
Etienne Basset's avatar
Etienne Basset committed
452 453 454 455

	if (ab == NULL)
		return;

456 457
	if (pre_audit)
		pre_audit(ab, a);
Etienne Basset's avatar
Etienne Basset committed
458 459 460

	dump_common_audit_data(ab, a);

461 462
	if (post_audit)
		post_audit(ab, a);
Etienne Basset's avatar
Etienne Basset committed
463 464 465

	audit_log_end(ab);
}