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

/*
12
 * (c) Copyright Hewlett-Packard Development Company, L.P., 2007, 2008
13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
 *
 * 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
 * along with this program;  if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 *
 */

#include <linux/spinlock.h>
#include <linux/rcupdate.h>
#include <net/sock.h>
#include <net/netlabel.h>
34 35
#include <net/inet_sock.h>
#include <net/inet_connection_sock.h>
36 37 38

#include "objsec.h"
#include "security.h"
39
#include "netlabel.h"
40

41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67
/**
 * 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,
					   struct netlbl_lsm_secattr *secattr,
					   u32 *sid)
{
	int rc;

	rc = security_netlbl_secattr_to_sid(secattr, sid);
	if (rc == 0 &&
	    (secattr->flags & NETLBL_SECATTR_CACHEABLE) &&
	    (secattr->flags & NETLBL_SECATTR_CACHE))
		netlbl_cache_add(skb, secattr);

	return rc;
}

68
/**
69 70
 * selinux_netlbl_sock_setsid - Label a socket using the NetLabel mechanism
 * @sk: the socket to label
71 72
 *
 * Description:
73 74
 * Attempt to label a socket using the NetLabel mechanism.  Returns zero values
 * on success, negative values on failure.
75 76
 *
 */
77
static int selinux_netlbl_sock_setsid(struct sock *sk)
78 79
{
	int rc;
80
	struct sk_security_struct *sksec = sk->sk_security;
81
	struct netlbl_lsm_secattr secattr;
82 83
	struct inet_sock *sk_inet;
	struct inet_connection_sock *sk_conn;
84

85 86 87
	if (sksec->nlbl_state != NLBL_REQUIRE)
		return 0;

88 89
	netlbl_secattr_init(&secattr);

90
	rc = security_netlbl_sid_to_secattr(sksec->sid, &secattr);
91
	if (rc != 0)
92
		goto sock_setsid_return;
93
	rc = netlbl_sock_setattr(sk, &secattr);
94 95
	switch (rc) {
	case 0:
96
		sksec->nlbl_state = NLBL_LABELED;
97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116
		break;
	case -EDESTADDRREQ:
		/* we are going to possibly end up labeling the individual
		 * packets later which is problematic for stream sockets
		 * because of the additional IP header size, our solution is to
		 * allow for the maximum IP header length (40 bytes for IPv4,
		 * we don't have to worry about IPv6 yet) just in case */
		sk_inet = inet_sk(sk);
		if (sk_inet->is_icsk) {
			sk_conn = inet_csk(sk);
			if (sk_inet->opt)
				sk_conn->icsk_ext_hdr_len -=
							   sk_inet->opt->optlen;
			sk_conn->icsk_ext_hdr_len += 40;
			sk_conn->icsk_sync_mss(sk, sk_conn->icsk_pmtu_cookie);
		}
		sksec->nlbl_state = NLBL_REQSKB;
		rc = 0;
		break;
	}
117

118 119
sock_setsid_return:
	netlbl_secattr_destroy(&secattr);
120 121 122 123 124 125 126 127 128 129 130 131 132 133 134
	return rc;
}

/**
 * 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();
}

135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152
/**
 * 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.
 *
 */
void selinux_netlbl_err(struct sk_buff *skb, int error, int gateway)
{
	netlbl_skbuff_err(skb, error, gateway);
}

153 154 155 156 157 158 159 160 161 162 163 164 165
/**
 * selinux_netlbl_sk_security_reset - Reset the NetLabel fields
 * @ssec: the sk_security_struct
 * @family: the socket family
 *
 * Description:
 * Called when the NetLabel state of a sk_security_struct needs to be reset.
 * The caller is responsibile for all the NetLabel sk_security_struct locking.
 *
 */
void selinux_netlbl_sk_security_reset(struct sk_security_struct *ssec,
				      int family)
{
166
	if (family == PF_INET)
167 168 169 170 171 172 173 174
		ssec->nlbl_state = NLBL_REQUIRE;
	else
		ssec->nlbl_state = NLBL_UNSET;
}

/**
 * selinux_netlbl_skbuff_getsid - Get the sid of a packet using NetLabel
 * @skb: the packet
175
 * @family: protocol family
176
 * @type: NetLabel labeling protocol type
177 178 179 180 181 182 183 184
 * @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.
 *
 */
185 186
int selinux_netlbl_skbuff_getsid(struct sk_buff *skb,
				 u16 family,
187
				 u32 *type,
188
				 u32 *sid)
189 190 191 192
{
	int rc;
	struct netlbl_lsm_secattr secattr;

193 194 195 196 197
	if (!netlbl_enabled()) {
		*sid = SECSID_NULL;
		return 0;
	}

198
	netlbl_secattr_init(&secattr);
199
	rc = netlbl_skbuff_getattr(skb, family, &secattr);
200 201 202
	if (rc == 0 && secattr.flags != NETLBL_SECATTR_NONE)
		rc = selinux_netlbl_sidlookup_cached(skb, &secattr, sid);
	else
203
		*sid = SECSID_NULL;
204
	*type = secattr.type;
205 206 207 208 209
	netlbl_secattr_destroy(&secattr);

	return rc;
}

210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248
/**
 * 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.
 * Returns zero on auccess, negative values on failure.
 *
 */
int selinux_netlbl_skbuff_setsid(struct sk_buff *skb,
				 u16 family,
				 u32 sid)
{
	int rc;
	struct netlbl_lsm_secattr secattr;
	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 */
	sk = skb->sk;
	if (sk != NULL) {
		struct sk_security_struct *sksec = sk->sk_security;
		if (sksec->nlbl_state != NLBL_REQSKB)
			return 0;
	}

	netlbl_secattr_init(&secattr);
	rc = security_netlbl_sid_to_secattr(sid, &secattr);
	if (rc != 0)
		goto skbuff_setsid_return;
	rc = netlbl_skbuff_setattr(skb, family, &secattr);

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

249 250 251 252 253 254 255 256 257 258 259 260 261 262 263
/**
 * selinux_netlbl_sock_graft - Netlabel the new socket
 * @sk: the new connection
 * @sock: the new socket
 *
 * Description:
 * The connection represented by @sk is being grafted onto @sock so set the
 * socket's NetLabel to match the SID of @sk.
 *
 */
void selinux_netlbl_sock_graft(struct sock *sk, struct socket *sock)
{
	/* Try to set the NetLabel on the socket to save time later, if we fail
	 * here we will pick up the pieces in later calls to
	 * selinux_netlbl_inode_permission(). */
264
	selinux_netlbl_sock_setsid(sk);
265 266 267 268 269 270 271 272 273 274 275 276 277
}

/**
 * selinux_netlbl_socket_post_create - Label a socket using NetLabel
 * @sock: the socket to label
 *
 * Description:
 * Attempt to label a socket using the NetLabel mechanism using the given
 * SID.  Returns zero values on success, negative values on failure.
 *
 */
int selinux_netlbl_socket_post_create(struct socket *sock)
{
278
	return selinux_netlbl_sock_setsid(sock->sk);
279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295
}

/**
 * selinux_netlbl_inode_permission - Verify the socket is NetLabel labeled
 * @inode: the file descriptor's inode
 * @mask: the permission mask
 *
 * Description:
 * Looks at a file's inode and if it is marked as a socket protected by
 * NetLabel then verify that the socket has been labeled, if not try to label
 * the socket now with the inode's SID.  Returns zero on success, negative
 * values on failure.
 *
 */
int selinux_netlbl_inode_permission(struct inode *inode, int mask)
{
	int rc;
296
	struct sock *sk;
297
	struct socket *sock;
298
	struct sk_security_struct *sksec;
299 300 301 302

	if (!S_ISSOCK(inode->i_mode) ||
	    ((mask & (MAY_WRITE | MAY_APPEND)) == 0))
		return 0;
303

304
	sock = SOCKET_I(inode);
305 306
	sk = sock->sk;
	sksec = sk->sk_security;
307
	if (sksec->nlbl_state != NLBL_REQUIRE)
308
		return 0;
309

310
	local_bh_disable();
311
	bh_lock_sock_nested(sk);
312
	if (likely(sksec->nlbl_state == NLBL_REQUIRE))
313
		rc = selinux_netlbl_sock_setsid(sk);
314 315
	else
		rc = 0;
316
	bh_unlock_sock(sk);
317 318 319 320 321 322 323 324 325
	local_bh_enable();

	return rc;
}

/**
 * selinux_netlbl_sock_rcv_skb - Do an inbound access check using NetLabel
 * @sksec: the sock's sk_security_struct
 * @skb: the packet
326
 * @family: protocol family
327 328 329 330 331 332 333 334 335 336
 * @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,
337
				u16 family,
338 339 340
				struct avc_audit_data *ad)
{
	int rc;
341 342 343
	u32 nlbl_sid;
	u32 perm;
	struct netlbl_lsm_secattr secattr;
344

345 346 347
	if (!netlbl_enabled())
		return 0;

348
	netlbl_secattr_init(&secattr);
349
	rc = netlbl_skbuff_getattr(skb, family, &secattr);
350 351 352
	if (rc == 0 && secattr.flags != NETLBL_SECATTR_NONE)
		rc = selinux_netlbl_sidlookup_cached(skb, &secattr, &nlbl_sid);
	else
353 354
		nlbl_sid = SECINITSID_UNLABELED;
	netlbl_secattr_destroy(&secattr);
355 356
	if (rc != 0)
		return rc;
357

358 359
	switch (sksec->sclass) {
	case SECCLASS_UDP_SOCKET:
360
		perm = UDP_SOCKET__RECVFROM;
361 362
		break;
	case SECCLASS_TCP_SOCKET:
363
		perm = TCP_SOCKET__RECVFROM;
364 365
		break;
	default:
366
		perm = RAWIP_SOCKET__RECVFROM;
367 368
	}

369
	rc = avc_has_perm(sksec->sid, nlbl_sid, sksec->sclass, perm, ad);
370 371 372
	if (rc == 0)
		return 0;

373
	if (nlbl_sid != SECINITSID_UNLABELED)
374
		netlbl_skbuff_err(skb, rc, 0);
375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395
	return rc;
}

/**
 * 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;
396 397
	struct sock *sk = sock->sk;
	struct sk_security_struct *sksec = sk->sk_security;
398 399 400 401 402
	struct netlbl_lsm_secattr secattr;

	if (level == IPPROTO_IP && optname == IP_OPTIONS &&
	    sksec->nlbl_state == NLBL_LABELED) {
		netlbl_secattr_init(&secattr);
403 404 405
		lock_sock(sk);
		rc = netlbl_sock_getattr(sk, &secattr);
		release_sock(sk);
406 407 408 409 410 411 412
		if (rc == 0 && secattr.flags != NETLBL_SECATTR_NONE)
			rc = -EACCES;
		netlbl_secattr_destroy(&secattr);
	}

	return rc;
}