netlabel.c 16.4 KB
Newer Older
1 2 3 4 5 6
/*
 * SELinux NetLabel Support
 *
 * This file provides the necessary glue to tie NetLabel into the SELinux
 * subsystem.
 *
7
 * Author: Paul Moore <paul@paul-moore.com>
8 9 10 11
 *
 */

/*
12
 * (c) Copyright Hewlett-Packard Development Company, L.P., 2007, 2008
13 14 15 16 17 18 19 20 21 22 23 24
 *
 * This program is free software;  you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY;  without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See
 * the GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
25
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
26 27 28 29 30
 *
 */

#include <linux/spinlock.h>
#include <linux/rcupdate.h>
31
#include <linux/gfp.h>
32 33
#include <linux/ip.h>
#include <linux/ipv6.h>
34 35
#include <net/sock.h>
#include <net/netlabel.h>
36 37
#include <net/ip.h>
#include <net/ipv6.h>
38 39 40

#include "objsec.h"
#include "security.h"
41
#include "netlabel.h"
42

43 44 45 46 47 48 49 50 51 52 53 54 55
/**
 * selinux_netlbl_sidlookup_cached - Cache a SID lookup
 * @skb: the packet
 * @secattr: the NetLabel security attributes
 * @sid: the SID
 *
 * Description:
 * Query the SELinux security server to lookup the correct SID for the given
 * security attributes.  If the query is successful, cache the result to speed
 * up future lookups.  Returns zero on success, negative values on failure.
 *
 */
static int selinux_netlbl_sidlookup_cached(struct sk_buff *skb,
H
Huw Davies 已提交
56
					   u16 family,
57 58 59 60 61
					   struct netlbl_lsm_secattr *secattr,
					   u32 *sid)
{
	int rc;

62
	rc = security_netlbl_secattr_to_sid(&selinux_state, secattr, sid);
63 64 65
	if (rc == 0 &&
	    (secattr->flags & NETLBL_SECATTR_CACHEABLE) &&
	    (secattr->flags & NETLBL_SECATTR_CACHE))
H
Huw Davies 已提交
66
		netlbl_cache_add(skb, family, secattr);
67 68 69 70

	return rc;
}

71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92
/**
 * selinux_netlbl_sock_genattr - Generate the NetLabel socket secattr
 * @sk: the socket
 *
 * Description:
 * Generate the NetLabel security attributes for a socket, making full use of
 * the socket's attribute cache.  Returns a pointer to the security attributes
 * on success, NULL on failure.
 *
 */
static struct netlbl_lsm_secattr *selinux_netlbl_sock_genattr(struct sock *sk)
{
	int rc;
	struct sk_security_struct *sksec = sk->sk_security;
	struct netlbl_lsm_secattr *secattr;

	if (sksec->nlbl_secattr != NULL)
		return sksec->nlbl_secattr;

	secattr = netlbl_secattr_alloc(GFP_ATOMIC);
	if (secattr == NULL)
		return NULL;
93 94
	rc = security_netlbl_sid_to_secattr(&selinux_state, sksec->sid,
					    secattr);
95 96 97 98 99 100 101 102 103
	if (rc != 0) {
		netlbl_secattr_free(secattr);
		return NULL;
	}
	sksec->nlbl_secattr = secattr;

	return secattr;
}

104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129
/**
 * selinux_netlbl_sock_getattr - Get the cached NetLabel secattr
 * @sk: the socket
 * @sid: the SID
 *
 * Query the socket's cached secattr and if the SID matches the cached value
 * return the cache, otherwise return NULL.
 *
 */
static struct netlbl_lsm_secattr *selinux_netlbl_sock_getattr(
							const struct sock *sk,
							u32 sid)
{
	struct sk_security_struct *sksec = sk->sk_security;
	struct netlbl_lsm_secattr *secattr = sksec->nlbl_secattr;

	if (secattr == NULL)
		return NULL;

	if ((secattr->flags & NETLBL_SECATTR_SECID) &&
	    (secattr->attr.secid == sid))
		return secattr;

	return NULL;
}

130 131 132 133 134 135 136 137 138 139 140 141
/**
 * selinux_netlbl_cache_invalidate - Invalidate the NetLabel cache
 *
 * Description:
 * Invalidate the NetLabel security attribute mapping cache.
 *
 */
void selinux_netlbl_cache_invalidate(void)
{
	netlbl_cache_invalidate();
}

142 143 144 145 146 147 148 149 150 151 152 153 154
/**
 * selinux_netlbl_err - Handle a NetLabel packet error
 * @skb: the packet
 * @error: the error code
 * @gateway: true if host is acting as a gateway, false otherwise
 *
 * Description:
 * When a packet is dropped due to a call to avc_has_perm() pass the error
 * code to the NetLabel subsystem so any protocol specific processing can be
 * done.  This is safe to call even if you are unsure if NetLabel labeling is
 * present on the packet, NetLabel is smart enough to only act when it should.
 *
 */
155
void selinux_netlbl_err(struct sk_buff *skb, u16 family, int error, int gateway)
156
{
157
	netlbl_skbuff_err(skb, family, error, gateway);
158 159
}

160 161
/**
 * selinux_netlbl_sk_security_free - Free the NetLabel fields
162
 * @sksec: the sk_security_struct
163 164 165 166 167
 *
 * Description:
 * Free all of the memory in the NetLabel fields of a sk_security_struct.
 *
 */
168
void selinux_netlbl_sk_security_free(struct sk_security_struct *sksec)
169
{
170 171
	if (sksec->nlbl_secattr != NULL)
		netlbl_secattr_free(sksec->nlbl_secattr);
172 173
}

174 175
/**
 * selinux_netlbl_sk_security_reset - Reset the NetLabel fields
176
 * @sksec: the sk_security_struct
177 178 179 180
 * @family: the socket family
 *
 * Description:
 * Called when the NetLabel state of a sk_security_struct needs to be reset.
L
Lucas De Marchi 已提交
181
 * The caller is responsible for all the NetLabel sk_security_struct locking.
182 183
 *
 */
184
void selinux_netlbl_sk_security_reset(struct sk_security_struct *sksec)
185
{
186
	sksec->nlbl_state = NLBL_UNSET;
187 188 189 190 191
}

/**
 * selinux_netlbl_skbuff_getsid - Get the sid of a packet using NetLabel
 * @skb: the packet
192
 * @family: protocol family
193
 * @type: NetLabel labeling protocol type
194 195 196 197 198 199 200 201
 * @sid: the SID
 *
 * Description:
 * Call the NetLabel mechanism to get the security attributes of the given
 * packet and use those attributes to determine the correct context/SID to
 * assign to the packet.  Returns zero on success, negative values on failure.
 *
 */
202 203
int selinux_netlbl_skbuff_getsid(struct sk_buff *skb,
				 u16 family,
204
				 u32 *type,
205
				 u32 *sid)
206 207 208 209
{
	int rc;
	struct netlbl_lsm_secattr secattr;

210 211 212 213 214
	if (!netlbl_enabled()) {
		*sid = SECSID_NULL;
		return 0;
	}

215
	netlbl_secattr_init(&secattr);
216
	rc = netlbl_skbuff_getattr(skb, family, &secattr);
217
	if (rc == 0 && secattr.flags != NETLBL_SECATTR_NONE)
H
Huw Davies 已提交
218 219
		rc = selinux_netlbl_sidlookup_cached(skb, family,
						     &secattr, sid);
220
	else
221
		*sid = SECSID_NULL;
222
	*type = secattr.type;
223 224 225 226 227
	netlbl_secattr_destroy(&secattr);

	return rc;
}

228 229 230 231 232 233 234 235
/**
 * selinux_netlbl_skbuff_setsid - Set the NetLabel on a packet given a sid
 * @skb: the packet
 * @family: protocol family
 * @sid: the SID
 *
 * Description
 * Call the NetLabel mechanism to set the label of a packet using @sid.
236
 * Returns zero on success, negative values on failure.
237 238 239 240 241 242 243
 *
 */
int selinux_netlbl_skbuff_setsid(struct sk_buff *skb,
				 u16 family,
				 u32 sid)
{
	int rc;
244 245
	struct netlbl_lsm_secattr secattr_storage;
	struct netlbl_lsm_secattr *secattr = NULL;
246 247 248 249
	struct sock *sk;

	/* if this is a locally generated packet check to see if it is already
	 * being labeled by it's parent socket, if it is just exit */
250
	sk = skb_to_full_sk(skb);
251 252
	if (sk != NULL) {
		struct sk_security_struct *sksec = sk->sk_security;
R
Richard Haines 已提交
253

254 255
		if (sksec->nlbl_state != NLBL_REQSKB)
			return 0;
256
		secattr = selinux_netlbl_sock_getattr(sk, sid);
257 258 259 260
	}
	if (secattr == NULL) {
		secattr = &secattr_storage;
		netlbl_secattr_init(secattr);
261 262
		rc = security_netlbl_sid_to_secattr(&selinux_state, sid,
						    secattr);
263 264
		if (rc != 0)
			goto skbuff_setsid_return;
265 266
	}

267
	rc = netlbl_skbuff_setattr(skb, family, secattr);
268 269

skbuff_setsid_return:
270 271
	if (secattr == &secattr_storage)
		netlbl_secattr_destroy(secattr);
272 273 274
	return rc;
}

R
Richard Haines 已提交
275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298
/**
 * selinux_netlbl_sctp_assoc_request - Label an incoming sctp association.
 * @ep: incoming association endpoint.
 * @skb: the packet.
 *
 * Description:
 * A new incoming connection is represented by @ep, ......
 * Returns zero on success, negative values on failure.
 *
 */
int selinux_netlbl_sctp_assoc_request(struct sctp_endpoint *ep,
				     struct sk_buff *skb)
{
	int rc;
	struct netlbl_lsm_secattr secattr;
	struct sk_security_struct *sksec = ep->base.sk->sk_security;
	struct sockaddr_in addr4;
	struct sockaddr_in6 addr6;

	if (ep->base.sk->sk_family != PF_INET &&
				ep->base.sk->sk_family != PF_INET6)
		return 0;

	netlbl_secattr_init(&secattr);
299 300
	rc = security_netlbl_sid_to_secattr(&selinux_state,
					    ep->secid, &secattr);
R
Richard Haines 已提交
301 302 303 304 305 306 307 308 309
	if (rc != 0)
		goto assoc_request_return;

	/* Move skb hdr address info to a struct sockaddr and then call
	 * netlbl_conn_setattr().
	 */
	if (ip_hdr(skb)->version == 4) {
		addr4.sin_family = AF_INET;
		addr4.sin_addr.s_addr = ip_hdr(skb)->saddr;
310 311
		rc = netlbl_conn_setattr(ep->base.sk, (void *)&addr4, &secattr);
	} else if (IS_ENABLED(CONFIG_IPV6) && ip_hdr(skb)->version == 6) {
R
Richard Haines 已提交
312 313
		addr6.sin6_family = AF_INET6;
		addr6.sin6_addr = ipv6_hdr(skb)->saddr;
314 315 316
		rc = netlbl_conn_setattr(ep->base.sk, (void *)&addr6, &secattr);
	} else {
		rc = -EAFNOSUPPORT;
R
Richard Haines 已提交
317 318 319 320 321 322 323 324 325 326
	}

	if (rc == 0)
		sksec->nlbl_state = NLBL_LABELED;

assoc_request_return:
	netlbl_secattr_destroy(&secattr);
	return rc;
}

327
/**
328 329
 * selinux_netlbl_inet_conn_request - Label an incoming stream connection
 * @req: incoming connection request socket
330 331
 *
 * Description:
332 333 334 335
 * A new incoming connection request is represented by @req, we need to label
 * the new request_sock here and the stack will ensure the on-the-wire label
 * will get preserved when a full sock is created once the connection handshake
 * is complete.  Returns zero on success, negative values on failure.
336 337
 *
 */
338
int selinux_netlbl_inet_conn_request(struct request_sock *req, u16 family)
339
{
340
	int rc;
341
	struct netlbl_lsm_secattr secattr;
342

343
	if (family != PF_INET && family != PF_INET6)
344
		return 0;
345

346
	netlbl_secattr_init(&secattr);
347 348
	rc = security_netlbl_sid_to_secattr(&selinux_state, req->secid,
					    &secattr);
349 350 351 352 353 354
	if (rc != 0)
		goto inet_conn_request_return;
	rc = netlbl_req_setattr(req, &secattr);
inet_conn_request_return:
	netlbl_secattr_destroy(&secattr);
	return rc;
355 356 357
}

/**
358 359
 * selinux_netlbl_inet_csk_clone - Initialize the newly created sock
 * @sk: the new sock
360 361
 *
 * Description:
362 363 364
 * A new connection has been established using @sk, we've already labeled the
 * socket via the request_sock struct in selinux_netlbl_inet_conn_request() but
 * we need to set the NetLabel state here since we now have a sock structure.
365 366
 *
 */
367
void selinux_netlbl_inet_csk_clone(struct sock *sk, u16 family)
368
{
369 370 371 372 373 374
	struct sk_security_struct *sksec = sk->sk_security;

	if (family == PF_INET)
		sksec->nlbl_state = NLBL_LABELED;
	else
		sksec->nlbl_state = NLBL_UNSET;
375 376
}

R
Richard Haines 已提交
377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392
/**
 * selinux_netlbl_sctp_sk_clone - Copy state to the newly created sock
 * @sk: current sock
 * @newsk: the new sock
 *
 * Description:
 * Called whenever a new socket is created by accept(2) or sctp_peeloff(3).
 */
void selinux_netlbl_sctp_sk_clone(struct sock *sk, struct sock *newsk)
{
	struct sk_security_struct *sksec = sk->sk_security;
	struct sk_security_struct *newsksec = newsk->sk_security;

	newsksec->nlbl_state = sksec->nlbl_state;
}

393
/**
394 395 396
 * selinux_netlbl_socket_post_create - Label a socket using NetLabel
 * @sock: the socket to label
 * @family: protocol family
397 398
 *
 * Description:
399 400
 * Attempt to label a socket using the NetLabel mechanism using the given
 * SID.  Returns zero values on success, negative values on failure.
401 402
 *
 */
403
int selinux_netlbl_socket_post_create(struct sock *sk, u16 family)
404 405
{
	int rc;
406 407
	struct sk_security_struct *sksec = sk->sk_security;
	struct netlbl_lsm_secattr *secattr;
408

409
	if (family != PF_INET && family != PF_INET6)
410
		return 0;
411

412 413 414 415 416 417 418 419 420 421
	secattr = selinux_netlbl_sock_genattr(sk);
	if (secattr == NULL)
		return -ENOMEM;
	rc = netlbl_sock_setattr(sk, family, secattr);
	switch (rc) {
	case 0:
		sksec->nlbl_state = NLBL_LABELED;
		break;
	case -EDESTADDRREQ:
		sksec->nlbl_state = NLBL_REQSKB;
422
		rc = 0;
423 424
		break;
	}
425 426 427 428 429 430 431 432

	return rc;
}

/**
 * selinux_netlbl_sock_rcv_skb - Do an inbound access check using NetLabel
 * @sksec: the sock's sk_security_struct
 * @skb: the packet
433
 * @family: protocol family
434 435 436 437 438 439 440 441 442 443
 * @ad: the audit data
 *
 * Description:
 * Fetch the NetLabel security attributes from @skb and perform an access check
 * against the receiving socket.  Returns zero on success, negative values on
 * error.
 *
 */
int selinux_netlbl_sock_rcv_skb(struct sk_security_struct *sksec,
				struct sk_buff *skb,
444
				u16 family,
445
				struct common_audit_data *ad)
446 447
{
	int rc;
448 449 450
	u32 nlbl_sid;
	u32 perm;
	struct netlbl_lsm_secattr secattr;
451

452 453 454
	if (!netlbl_enabled())
		return 0;

455
	netlbl_secattr_init(&secattr);
456
	rc = netlbl_skbuff_getattr(skb, family, &secattr);
457
	if (rc == 0 && secattr.flags != NETLBL_SECATTR_NONE)
H
Huw Davies 已提交
458 459
		rc = selinux_netlbl_sidlookup_cached(skb, family,
						     &secattr, &nlbl_sid);
460
	else
461 462
		nlbl_sid = SECINITSID_UNLABELED;
	netlbl_secattr_destroy(&secattr);
463 464
	if (rc != 0)
		return rc;
465

466 467
	switch (sksec->sclass) {
	case SECCLASS_UDP_SOCKET:
468
		perm = UDP_SOCKET__RECVFROM;
469 470
		break;
	case SECCLASS_TCP_SOCKET:
471
		perm = TCP_SOCKET__RECVFROM;
472 473
		break;
	default:
474
		perm = RAWIP_SOCKET__RECVFROM;
475 476
	}

S
Stephen Smalley 已提交
477 478
	rc = avc_has_perm(&selinux_state,
			  sksec->sid, nlbl_sid, sksec->sclass, perm, ad);
479 480 481
	if (rc == 0)
		return 0;

482
	if (nlbl_sid != SECINITSID_UNLABELED)
483
		netlbl_skbuff_err(skb, family, rc, 0);
484 485 486
	return rc;
}

487 488 489 490 491 492 493 494 495 496 497 498 499 500 501
/**
 * selinux_netlbl_option - Is this a NetLabel option
 * @level: the socket level or protocol
 * @optname: the socket option name
 *
 * Description:
 * Returns true if @level and @optname refer to a NetLabel option.
 * Helper for selinux_netlbl_socket_setsockopt().
 */
static inline int selinux_netlbl_option(int level, int optname)
{
	return (level == IPPROTO_IP && optname == IP_OPTIONS) ||
		(level == IPPROTO_IPV6 && optname == IPV6_HOPOPTS);
}

502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519
/**
 * selinux_netlbl_socket_setsockopt - Do not allow users to remove a NetLabel
 * @sock: the socket
 * @level: the socket level or protocol
 * @optname: the socket option name
 *
 * Description:
 * Check the setsockopt() call and if the user is trying to replace the IP
 * options on a socket and a NetLabel is in place for the socket deny the
 * access; otherwise allow the access.  Returns zero when the access is
 * allowed, -EACCES when denied, and other negative values on error.
 *
 */
int selinux_netlbl_socket_setsockopt(struct socket *sock,
				     int level,
				     int optname)
{
	int rc = 0;
520 521
	struct sock *sk = sock->sk;
	struct sk_security_struct *sksec = sk->sk_security;
522 523
	struct netlbl_lsm_secattr secattr;

524
	if (selinux_netlbl_option(level, optname) &&
525 526
	    (sksec->nlbl_state == NLBL_LABELED ||
	     sksec->nlbl_state == NLBL_CONNLABELED)) {
527
		netlbl_secattr_init(&secattr);
528
		lock_sock(sk);
529 530 531
		/* call the netlabel function directly as we want to see the
		 * on-the-wire label that is assigned via the socket's options
		 * and not the cached netlabel/lsm attributes */
532 533
		rc = netlbl_sock_getattr(sk, &secattr);
		release_sock(sk);
534
		if (rc == 0)
535
			rc = -EACCES;
536 537
		else if (rc == -ENOMSG)
			rc = 0;
538 539 540 541 542
		netlbl_secattr_destroy(&secattr);
	}

	return rc;
}
543 544

/**
R
Richard Haines 已提交
545 546
 * selinux_netlbl_socket_connect_helper - Help label a client-side socket on
 * connect
547 548 549 550 551 552 553 554
 * @sk: the socket to label
 * @addr: the destination address
 *
 * Description:
 * Attempt to label a connected socket with NetLabel using the given address.
 * Returns zero values on success, negative values on failure.
 *
 */
R
Richard Haines 已提交
555 556
static int selinux_netlbl_socket_connect_helper(struct sock *sk,
						struct sockaddr *addr)
557 558 559
{
	int rc;
	struct sk_security_struct *sksec = sk->sk_security;
560
	struct netlbl_lsm_secattr *secattr;
561 562 563 564 565 566 567 568

	/* connected sockets are allowed to disconnect when the address family
	 * is set to AF_UNSPEC, if that is what is happening we want to reset
	 * the socket */
	if (addr->sa_family == AF_UNSPEC) {
		netlbl_sock_delattr(sk);
		sksec->nlbl_state = NLBL_REQSKB;
		rc = 0;
R
Richard Haines 已提交
569
		return rc;
570
	}
571 572 573
	secattr = selinux_netlbl_sock_genattr(sk);
	if (secattr == NULL) {
		rc = -ENOMEM;
R
Richard Haines 已提交
574
		return rc;
575 576 577 578
	}
	rc = netlbl_conn_setattr(sk, addr, secattr);
	if (rc == 0)
		sksec->nlbl_state = NLBL_CONNLABELED;
579

R
Richard Haines 已提交
580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622
	return rc;
}

/**
 * selinux_netlbl_socket_connect_locked - Label a client-side socket on
 * connect
 * @sk: the socket to label
 * @addr: the destination address
 *
 * Description:
 * Attempt to label a connected socket that already has the socket locked
 * with NetLabel using the given address.
 * Returns zero values on success, negative values on failure.
 *
 */
int selinux_netlbl_socket_connect_locked(struct sock *sk,
					 struct sockaddr *addr)
{
	struct sk_security_struct *sksec = sk->sk_security;

	if (sksec->nlbl_state != NLBL_REQSKB &&
	    sksec->nlbl_state != NLBL_CONNLABELED)
		return 0;

	return selinux_netlbl_socket_connect_helper(sk, addr);
}

/**
 * selinux_netlbl_socket_connect - Label a client-side socket on connect
 * @sk: the socket to label
 * @addr: the destination address
 *
 * Description:
 * Attempt to label a connected socket with NetLabel using the given address.
 * Returns zero values on success, negative values on failure.
 *
 */
int selinux_netlbl_socket_connect(struct sock *sk, struct sockaddr *addr)
{
	int rc;

	lock_sock(sk);
	rc = selinux_netlbl_socket_connect_locked(sk, addr);
623
	release_sock(sk);
R
Richard Haines 已提交
624

625 626
	return rc;
}