hooks.c 141 KB
Newer Older
Linus Torvalds's avatar
Linus Torvalds committed
1 2 3 4 5 6
/*
 *  NSA Security-Enhanced Linux (SELinux) security module
 *
 *  This file contains the SELinux hook function implementations.
 *
 *  Authors:  Stephen Smalley, <[email protected]>
7 8 9
 *	      Chris Vance, <[email protected]>
 *	      Wayne Salamon, <[email protected]>
 *	      James Morris <[email protected]>
Linus Torvalds's avatar
Linus Torvalds committed
10 11
 *
 *  Copyright (C) 2001,2002 Networks Associates Technology, Inc.
12 13
 *  Copyright (C) 2003-2008 Red Hat, Inc., James Morris <[email protected]>
 *					   Eric Paris <[email protected]>
Linus Torvalds's avatar
Linus Torvalds committed
14
 *  Copyright (C) 2004-2005 Trusted Computer Solutions, Inc.
15
 *			    <[email protected]>
16 17
 *  Copyright (C) 2006, 2007, 2009 Hewlett-Packard Development Company, L.P.
 *	Paul Moore <[email protected]>
18
 *  Copyright (C) 2007 Hitachi Software Engineering Co., Ltd.
19
 *		       Yuichi Nakamura <[email protected]>
Linus Torvalds's avatar
Linus Torvalds committed
20 21 22
 *
 *	This program is free software; you can redistribute it and/or modify
 *	it under the terms of the GNU General Public License version 2,
23
 *	as published by the Free Software Foundation.
Linus Torvalds's avatar
Linus Torvalds committed
24 25 26 27
 */

#include <linux/init.h>
#include <linux/kernel.h>
28
#include <linux/tracehook.h>
Linus Torvalds's avatar
Linus Torvalds committed
29 30 31 32 33 34 35 36 37 38 39 40 41 42
#include <linux/errno.h>
#include <linux/sched.h>
#include <linux/security.h>
#include <linux/xattr.h>
#include <linux/capability.h>
#include <linux/unistd.h>
#include <linux/mm.h>
#include <linux/mman.h>
#include <linux/slab.h>
#include <linux/pagemap.h>
#include <linux/swap.h>
#include <linux/spinlock.h>
#include <linux/syscalls.h>
#include <linux/file.h>
Al Viro's avatar
Al Viro committed
43
#include <linux/fdtable.h>
Linus Torvalds's avatar
Linus Torvalds committed
44 45 46 47 48 49 50
#include <linux/namei.h>
#include <linux/mount.h>
#include <linux/proc_fs.h>
#include <linux/netfilter_ipv4.h>
#include <linux/netfilter_ipv6.h>
#include <linux/tty.h>
#include <net/icmp.h>
51
#include <net/ip.h>		/* for local_port_range[] */
Linus Torvalds's avatar
Linus Torvalds committed
52
#include <net/tcp.h>		/* struct or_callable used in sock_rcv_skb */
53
#include <net/net_namespace.h>
54
#include <net/netlabel.h>
55
#include <linux/uaccess.h>
Linus Torvalds's avatar
Linus Torvalds committed
56
#include <asm/ioctls.h>
57
#include <asm/atomic.h>
Linus Torvalds's avatar
Linus Torvalds committed
58 59 60 61 62 63
#include <linux/bitops.h>
#include <linux/interrupt.h>
#include <linux/netdevice.h>	/* for network interface checks */
#include <linux/netlink.h>
#include <linux/tcp.h>
#include <linux/udp.h>
James Morris's avatar
James Morris committed
64
#include <linux/dccp.h>
Linus Torvalds's avatar
Linus Torvalds committed
65 66 67 68 69 70 71 72 73 74
#include <linux/quota.h>
#include <linux/un.h>		/* for Unix socket types */
#include <net/af_unix.h>	/* for Unix socket types */
#include <linux/parser.h>
#include <linux/nfs_mount.h>
#include <net/ipv6.h>
#include <linux/hugetlb.h>
#include <linux/personality.h>
#include <linux/sysctl.h>
#include <linux/audit.h>
75
#include <linux/string.h>
76
#include <linux/selinux.h>
77
#include <linux/mutex.h>
78
#include <linux/posix-timers.h>
Linus Torvalds's avatar
Linus Torvalds committed
79 80 81 82

#include "avc.h"
#include "objsec.h"
#include "netif.h"
83
#include "netnode.h"
84
#include "netport.h"
85
#include "xfrm.h"
86
#include "netlabel.h"
87
#include "audit.h"
Linus Torvalds's avatar
Linus Torvalds committed
88 89 90 91

#define XATTR_SELINUX_SUFFIX "selinux"
#define XATTR_NAME_SELINUX XATTR_SECURITY_PREFIX XATTR_SELINUX_SUFFIX

92
#define NUM_SEL_MNT_OPTS 5
93

Linus Torvalds's avatar
Linus Torvalds committed
94
extern int selinux_nlmsg_lookup(u16 sclass, u16 nlmsg_type, u32 *perm);
95
extern struct security_operations *security_ops;
Linus Torvalds's avatar
Linus Torvalds committed
96

97 98 99
/* SECMARK reference count */
atomic_t selinux_secmark_refcount = ATOMIC_INIT(0);

Linus Torvalds's avatar
Linus Torvalds committed
100
#ifdef CONFIG_SECURITY_SELINUX_DEVELOP
101
int selinux_enforcing;
Linus Torvalds's avatar
Linus Torvalds committed
102 103 104

static int __init enforcing_setup(char *str)
{
105 106 107
	unsigned long enforcing;
	if (!strict_strtoul(str, 0, &enforcing))
		selinux_enforcing = enforcing ? 1 : 0;
Linus Torvalds's avatar
Linus Torvalds committed
108 109 110 111 112 113 114 115 116 117
	return 1;
}
__setup("enforcing=", enforcing_setup);
#endif

#ifdef CONFIG_SECURITY_SELINUX_BOOTPARAM
int selinux_enabled = CONFIG_SECURITY_SELINUX_BOOTPARAM_VALUE;

static int __init selinux_enabled_setup(char *str)
{
118 119 120
	unsigned long enabled;
	if (!strict_strtoul(str, 0, &enabled))
		selinux_enabled = enabled ? 1 : 0;
Linus Torvalds's avatar
Linus Torvalds committed
121 122 123
	return 1;
}
__setup("selinux=", selinux_enabled_setup);
124 125
#else
int selinux_enabled = 1;
Linus Torvalds's avatar
Linus Torvalds committed
126 127 128
#endif


129 130 131 132
/*
 * Minimal support for a secondary security module,
 * just to allow the use of the capability module.
 */
133
static struct security_operations *secondary_ops;
Linus Torvalds's avatar
Linus Torvalds committed
134 135 136 137 138 139

/* Lists of inode and superblock security structures initialized
   before the policy was loaded. */
static LIST_HEAD(superblock_security_head);
static DEFINE_SPINLOCK(sb_security_lock);

140
static struct kmem_cache *sel_inode_cache;
141

142 143 144 145 146 147 148 149 150 151 152 153 154 155 156
/**
 * selinux_secmark_enabled - Check to see if SECMARK is currently enabled
 *
 * Description:
 * This function checks the SECMARK reference counter to see if any SECMARK
 * targets are currently configured, if the reference counter is greater than
 * zero SECMARK is considered to be enabled.  Returns true (1) if SECMARK is
 * enabled, false (0) if SECMARK is disabled.
 *
 */
static int selinux_secmark_enabled(void)
{
	return (atomic_read(&selinux_secmark_refcount) > 0);
}

157 158 159 160
/*
 * initialise the security for the init task
 */
static void cred_init_security(void)
Linus Torvalds's avatar
Linus Torvalds committed
161
{
162
	struct cred *cred = (struct cred *) current->real_cred;
Linus Torvalds's avatar
Linus Torvalds committed
163 164
	struct task_security_struct *tsec;

165
	tsec = kzalloc(sizeof(struct task_security_struct), GFP_KERNEL);
Linus Torvalds's avatar
Linus Torvalds committed
166
	if (!tsec)
167
		panic("SELinux:  Failed to initialize initial task.\n");
Linus Torvalds's avatar
Linus Torvalds committed
168

169
	tsec->osid = tsec->sid = SECINITSID_KERNEL;
170
	cred->security = tsec;
Linus Torvalds's avatar
Linus Torvalds committed
171 172
}

173 174 175 176 177 178 179 180 181 182 183
/*
 * get the security ID of a set of credentials
 */
static inline u32 cred_sid(const struct cred *cred)
{
	const struct task_security_struct *tsec;

	tsec = cred->security;
	return tsec->sid;
}

184
/*
185
 * get the objective security ID of a task
186 187 188 189 190 191
 */
static inline u32 task_sid(const struct task_struct *task)
{
	u32 sid;

	rcu_read_lock();
192
	sid = cred_sid(__task_cred(task));
193 194 195 196 197
	rcu_read_unlock();
	return sid;
}

/*
198
 * get the subjective security ID of the current task
199 200 201 202 203 204 205 206
 */
static inline u32 current_sid(void)
{
	const struct task_security_struct *tsec = current_cred()->security;

	return tsec->sid;
}

207 208
/* Allocate and free functions for each kind of security blob. */

Linus Torvalds's avatar
Linus Torvalds committed
209 210 211
static int inode_alloc_security(struct inode *inode)
{
	struct inode_security_struct *isec;
212
	u32 sid = current_sid();
Linus Torvalds's avatar
Linus Torvalds committed
213

214
	isec = kmem_cache_zalloc(sel_inode_cache, GFP_NOFS);
Linus Torvalds's avatar
Linus Torvalds committed
215 216 217
	if (!isec)
		return -ENOMEM;

218
	mutex_init(&isec->lock);
Linus Torvalds's avatar
Linus Torvalds committed
219 220 221 222
	INIT_LIST_HEAD(&isec->list);
	isec->inode = inode;
	isec->sid = SECINITSID_UNLABELED;
	isec->sclass = SECCLASS_FILE;
223
	isec->task_sid = sid;
Linus Torvalds's avatar
Linus Torvalds committed
224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239
	inode->i_security = isec;

	return 0;
}

static void inode_free_security(struct inode *inode)
{
	struct inode_security_struct *isec = inode->i_security;
	struct superblock_security_struct *sbsec = inode->i_sb->s_security;

	spin_lock(&sbsec->isec_lock);
	if (!list_empty(&isec->list))
		list_del_init(&isec->list);
	spin_unlock(&sbsec->isec_lock);

	inode->i_security = NULL;
240
	kmem_cache_free(sel_inode_cache, isec);
Linus Torvalds's avatar
Linus Torvalds committed
241 242 243 244 245
}

static int file_alloc_security(struct file *file)
{
	struct file_security_struct *fsec;
246
	u32 sid = current_sid();
Linus Torvalds's avatar
Linus Torvalds committed
247

248
	fsec = kzalloc(sizeof(struct file_security_struct), GFP_KERNEL);
Linus Torvalds's avatar
Linus Torvalds committed
249 250 251
	if (!fsec)
		return -ENOMEM;

252 253
	fsec->sid = sid;
	fsec->fown_sid = sid;
Linus Torvalds's avatar
Linus Torvalds committed
254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269
	file->f_security = fsec;

	return 0;
}

static void file_free_security(struct file *file)
{
	struct file_security_struct *fsec = file->f_security;
	file->f_security = NULL;
	kfree(fsec);
}

static int superblock_alloc_security(struct super_block *sb)
{
	struct superblock_security_struct *sbsec;

270
	sbsec = kzalloc(sizeof(struct superblock_security_struct), GFP_KERNEL);
Linus Torvalds's avatar
Linus Torvalds committed
271 272 273
	if (!sbsec)
		return -ENOMEM;

274
	mutex_init(&sbsec->lock);
Linus Torvalds's avatar
Linus Torvalds committed
275 276 277 278 279 280
	INIT_LIST_HEAD(&sbsec->list);
	INIT_LIST_HEAD(&sbsec->isec_head);
	spin_lock_init(&sbsec->isec_lock);
	sbsec->sb = sb;
	sbsec->sid = SECINITSID_UNLABELED;
	sbsec->def_sid = SECINITSID_FILE;
281
	sbsec->mntpoint_sid = SECINITSID_UNLABELED;
Linus Torvalds's avatar
Linus Torvalds committed
282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299
	sb->s_security = sbsec;

	return 0;
}

static void superblock_free_security(struct super_block *sb)
{
	struct superblock_security_struct *sbsec = sb->s_security;

	spin_lock(&sb_security_lock);
	if (!list_empty(&sbsec->list))
		list_del_init(&sbsec->list);
	spin_unlock(&sb_security_lock);

	sb->s_security = NULL;
	kfree(sbsec);
}

Al Viro's avatar
Al Viro committed
300
static int sk_alloc_security(struct sock *sk, int family, gfp_t priority)
Linus Torvalds's avatar
Linus Torvalds committed
301 302 303
{
	struct sk_security_struct *ssec;

304
	ssec = kzalloc(sizeof(*ssec), priority);
Linus Torvalds's avatar
Linus Torvalds committed
305 306 307 308
	if (!ssec)
		return -ENOMEM;

	ssec->peer_sid = SECINITSID_UNLABELED;
309
	ssec->sid = SECINITSID_UNLABELED;
Linus Torvalds's avatar
Linus Torvalds committed
310 311
	sk->sk_security = ssec;

312
	selinux_netlbl_sk_security_reset(ssec);
313

Linus Torvalds's avatar
Linus Torvalds committed
314 315 316 317 318 319 320 321
	return 0;
}

static void sk_free_security(struct sock *sk)
{
	struct sk_security_struct *ssec = sk->sk_security;

	sk->sk_security = NULL;
322
	selinux_netlbl_sk_security_free(ssec);
Linus Torvalds's avatar
Linus Torvalds committed
323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348
	kfree(ssec);
}

/* The security server must be initialized before
   any labeling or access decisions can be provided. */
extern int ss_initialized;

/* The file system's label must be initialized prior to use. */

static char *labeling_behaviors[6] = {
	"uses xattr",
	"uses transition SIDs",
	"uses task SIDs",
	"uses genfs_contexts",
	"not configured for labeling",
	"uses mountpoint labeling",
};

static int inode_doinit_with_dentry(struct inode *inode, struct dentry *opt_dentry);

static inline int inode_doinit(struct inode *inode)
{
	return inode_doinit_with_dentry(inode, NULL);
}

enum {
349
	Opt_error = -1,
Linus Torvalds's avatar
Linus Torvalds committed
350 351
	Opt_context = 1,
	Opt_fscontext = 2,
352 353
	Opt_defcontext = 3,
	Opt_rootcontext = 4,
354
	Opt_labelsupport = 5,
Linus Torvalds's avatar
Linus Torvalds committed
355 356
};

357
static const match_table_t tokens = {
358 359 360 361
	{Opt_context, CONTEXT_STR "%s"},
	{Opt_fscontext, FSCONTEXT_STR "%s"},
	{Opt_defcontext, DEFCONTEXT_STR "%s"},
	{Opt_rootcontext, ROOTCONTEXT_STR "%s"},
362
	{Opt_labelsupport, LABELSUPP_STR},
363
	{Opt_error, NULL},
Linus Torvalds's avatar
Linus Torvalds committed
364 365 366 367
};

#define SEL_MOUNT_FAIL_MSG "SELinux:  duplicate or incompatible mount options\n"

368 369
static int may_context_mount_sb_relabel(u32 sid,
			struct superblock_security_struct *sbsec,
370
			const struct cred *cred)
371
{
372
	const struct task_security_struct *tsec = cred->security;
373 374 375 376 377 378 379 380 381 382 383 384
	int rc;

	rc = avc_has_perm(tsec->sid, sbsec->sid, SECCLASS_FILESYSTEM,
			  FILESYSTEM__RELABELFROM, NULL);
	if (rc)
		return rc;

	rc = avc_has_perm(tsec->sid, sid, SECCLASS_FILESYSTEM,
			  FILESYSTEM__RELABELTO, NULL);
	return rc;
}

385 386
static int may_context_mount_inode_relabel(u32 sid,
			struct superblock_security_struct *sbsec,
387
			const struct cred *cred)
388
{
389
	const struct task_security_struct *tsec = cred->security;
390 391 392 393 394 395 396 397 398 399 400
	int rc;
	rc = avc_has_perm(tsec->sid, sbsec->sid, SECCLASS_FILESYSTEM,
			  FILESYSTEM__RELABELFROM, NULL);
	if (rc)
		return rc;

	rc = avc_has_perm(sid, sbsec->sid, SECCLASS_FILESYSTEM,
			  FILESYSTEM__ASSOCIATE, NULL);
	return rc;
}

401
static int sb_finish_set_opts(struct super_block *sb)
Linus Torvalds's avatar
Linus Torvalds committed
402 403
{
	struct superblock_security_struct *sbsec = sb->s_security;
404 405 406
	struct dentry *root = sb->s_root;
	struct inode *root_inode = root->d_inode;
	int rc = 0;
Linus Torvalds's avatar
Linus Torvalds committed
407

408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432
	if (sbsec->behavior == SECURITY_FS_USE_XATTR) {
		/* Make sure that the xattr handler exists and that no
		   error other than -ENODATA is returned by getxattr on
		   the root directory.  -ENODATA is ok, as this may be
		   the first boot of the SELinux kernel before we have
		   assigned xattr values to the filesystem. */
		if (!root_inode->i_op->getxattr) {
			printk(KERN_WARNING "SELinux: (dev %s, type %s) has no "
			       "xattr support\n", sb->s_id, sb->s_type->name);
			rc = -EOPNOTSUPP;
			goto out;
		}
		rc = root_inode->i_op->getxattr(root, XATTR_NAME_SELINUX, NULL, 0);
		if (rc < 0 && rc != -ENODATA) {
			if (rc == -EOPNOTSUPP)
				printk(KERN_WARNING "SELinux: (dev %s, type "
				       "%s) has no security xattr handler\n",
				       sb->s_id, sb->s_type->name);
			else
				printk(KERN_WARNING "SELinux: (dev %s, type "
				       "%s) getxattr errno %d\n", sb->s_id,
				       sb->s_type->name, -rc);
			goto out;
		}
	}
Linus Torvalds's avatar
Linus Torvalds committed
433

434
	sbsec->flags |= (SE_SBINITIALIZED | SE_SBLABELSUPP);
Linus Torvalds's avatar
Linus Torvalds committed
435

436 437 438 439 440 441 442
	if (sbsec->behavior > ARRAY_SIZE(labeling_behaviors))
		printk(KERN_ERR "SELinux: initialized (dev %s, type %s), unknown behavior\n",
		       sb->s_id, sb->s_type->name);
	else
		printk(KERN_DEBUG "SELinux: initialized (dev %s, type %s), %s\n",
		       sb->s_id, sb->s_type->name,
		       labeling_behaviors[sbsec->behavior-1]);
Linus Torvalds's avatar
Linus Torvalds committed
443

444 445 446 447 448 449
	if (sbsec->behavior == SECURITY_FS_USE_GENFS ||
	    sbsec->behavior == SECURITY_FS_USE_MNTPOINT ||
	    sbsec->behavior == SECURITY_FS_USE_NONE ||
	    sbsec->behavior > ARRAY_SIZE(labeling_behaviors))
		sbsec->flags &= ~SE_SBLABELSUPP;

450 451 452 453
	/* Special handling for sysfs. Is genfs but also has setxattr handler*/
	if (strncmp(sb->s_type->name, "sysfs", sizeof("sysfs")) == 0)
		sbsec->flags |= SE_SBLABELSUPP;

454 455
	/* Initialize the root inode. */
	rc = inode_doinit_with_dentry(root_inode, root);
Linus Torvalds's avatar
Linus Torvalds committed
456

457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482
	/* Initialize any other inodes associated with the superblock, e.g.
	   inodes created prior to initial policy load or inodes created
	   during get_sb by a pseudo filesystem that directly
	   populates itself. */
	spin_lock(&sbsec->isec_lock);
next_inode:
	if (!list_empty(&sbsec->isec_head)) {
		struct inode_security_struct *isec =
				list_entry(sbsec->isec_head.next,
					   struct inode_security_struct, list);
		struct inode *inode = isec->inode;
		spin_unlock(&sbsec->isec_lock);
		inode = igrab(inode);
		if (inode) {
			if (!IS_PRIVATE(inode))
				inode_doinit(inode);
			iput(inode);
		}
		spin_lock(&sbsec->isec_lock);
		list_del_init(&isec->list);
		goto next_inode;
	}
	spin_unlock(&sbsec->isec_lock);
out:
	return rc;
}
Linus Torvalds's avatar
Linus Torvalds committed
483

484 485 486 487 488 489
/*
 * This function should allow an FS to ask what it's mount security
 * options were so it can use those later for submounts, displaying
 * mount options, or whatever.
 */
static int selinux_get_mnt_opts(const struct super_block *sb,
490
				struct security_mnt_opts *opts)
491 492 493 494 495 496
{
	int rc = 0, i;
	struct superblock_security_struct *sbsec = sb->s_security;
	char *context = NULL;
	u32 len;
	char tmp;
Linus Torvalds's avatar
Linus Torvalds committed
497

498
	security_init_mnt_opts(opts);
Linus Torvalds's avatar
Linus Torvalds committed
499

500
	if (!(sbsec->flags & SE_SBINITIALIZED))
501
		return -EINVAL;
Linus Torvalds's avatar
Linus Torvalds committed
502

503 504
	if (!ss_initialized)
		return -EINVAL;
Linus Torvalds's avatar
Linus Torvalds committed
505

506
	tmp = sbsec->flags & SE_MNTMASK;
507 508 509
	/* count the number of mount options for this sb */
	for (i = 0; i < 8; i++) {
		if (tmp & 0x01)
510
			opts->num_mnt_opts++;
511 512
		tmp >>= 1;
	}
513 514 515
	/* Check if the Label support flag is set */
	if (sbsec->flags & SE_SBLABELSUPP)
		opts->num_mnt_opts++;
Linus Torvalds's avatar
Linus Torvalds committed
516

517 518
	opts->mnt_opts = kcalloc(opts->num_mnt_opts, sizeof(char *), GFP_ATOMIC);
	if (!opts->mnt_opts) {
519 520 521
		rc = -ENOMEM;
		goto out_free;
	}
Linus Torvalds's avatar
Linus Torvalds committed
522

523 524
	opts->mnt_opts_flags = kcalloc(opts->num_mnt_opts, sizeof(int), GFP_ATOMIC);
	if (!opts->mnt_opts_flags) {
525 526 527
		rc = -ENOMEM;
		goto out_free;
	}
Linus Torvalds's avatar
Linus Torvalds committed
528

529 530 531 532 533
	i = 0;
	if (sbsec->flags & FSCONTEXT_MNT) {
		rc = security_sid_to_context(sbsec->sid, &context, &len);
		if (rc)
			goto out_free;
534 535
		opts->mnt_opts[i] = context;
		opts->mnt_opts_flags[i++] = FSCONTEXT_MNT;
536 537 538 539 540
	}
	if (sbsec->flags & CONTEXT_MNT) {
		rc = security_sid_to_context(sbsec->mntpoint_sid, &context, &len);
		if (rc)
			goto out_free;
541 542
		opts->mnt_opts[i] = context;
		opts->mnt_opts_flags[i++] = CONTEXT_MNT;
543 544 545 546 547
	}
	if (sbsec->flags & DEFCONTEXT_MNT) {
		rc = security_sid_to_context(sbsec->def_sid, &context, &len);
		if (rc)
			goto out_free;
548 549
		opts->mnt_opts[i] = context;
		opts->mnt_opts_flags[i++] = DEFCONTEXT_MNT;
550 551 552 553
	}
	if (sbsec->flags & ROOTCONTEXT_MNT) {
		struct inode *root = sbsec->sb->s_root->d_inode;
		struct inode_security_struct *isec = root->i_security;
554

555 556 557
		rc = security_sid_to_context(isec->sid, &context, &len);
		if (rc)
			goto out_free;
558 559
		opts->mnt_opts[i] = context;
		opts->mnt_opts_flags[i++] = ROOTCONTEXT_MNT;
560
	}
561 562 563 564
	if (sbsec->flags & SE_SBLABELSUPP) {
		opts->mnt_opts[i] = NULL;
		opts->mnt_opts_flags[i++] = SE_SBLABELSUPP;
	}
Linus Torvalds's avatar
Linus Torvalds committed
565

566
	BUG_ON(i != opts->num_mnt_opts);
Linus Torvalds's avatar
Linus Torvalds committed
567

568 569 570
	return 0;

out_free:
571
	security_free_mnt_opts(opts);
572 573
	return rc;
}
Linus Torvalds's avatar
Linus Torvalds committed
574

575 576 577
static int bad_option(struct superblock_security_struct *sbsec, char flag,
		      u32 old_sid, u32 new_sid)
{
578 579
	char mnt_flags = sbsec->flags & SE_MNTMASK;

580
	/* check if the old mount command had the same options */
581
	if (sbsec->flags & SE_SBINITIALIZED)
582 583 584 585 586 587 588
		if (!(sbsec->flags & flag) ||
		    (old_sid != new_sid))
			return 1;

	/* check if we were passed the same options twice,
	 * aka someone passed context=a,context=b
	 */
589 590
	if (!(sbsec->flags & SE_SBINITIALIZED))
		if (mnt_flags & flag)
591 592 593
			return 1;
	return 0;
}
594

595 596 597 598
/*
 * Allow filesystems with binary mount data to explicitly set mount point
 * labeling information.
 */
599 600
static int selinux_set_mnt_opts(struct super_block *sb,
				struct security_mnt_opts *opts)
601
{
602
	const struct cred *cred = current_cred();
603 604 605
	int rc = 0, i;
	struct superblock_security_struct *sbsec = sb->s_security;
	const char *name = sb->s_type->name;
606 607
	struct inode *inode = sbsec->sb->s_root->d_inode;
	struct inode_security_struct *root_isec = inode->i_security;
608 609
	u32 fscontext_sid = 0, context_sid = 0, rootcontext_sid = 0;
	u32 defcontext_sid = 0;
610 611 612
	char **mount_options = opts->mnt_opts;
	int *flags = opts->mnt_opts_flags;
	int num_opts = opts->num_mnt_opts;
613 614 615 616 617 618 619 620 621 622 623 624 625 626 627

	mutex_lock(&sbsec->lock);

	if (!ss_initialized) {
		if (!num_opts) {
			/* Defer initialization until selinux_complete_init,
			   after the initial policy is loaded and the security
			   server is ready to handle calls. */
			spin_lock(&sb_security_lock);
			if (list_empty(&sbsec->list))
				list_add(&sbsec->list, &superblock_security_head);
			spin_unlock(&sb_security_lock);
			goto out;
		}
		rc = -EINVAL;
Eric Paris's avatar
Eric Paris committed
628 629
		printk(KERN_WARNING "SELinux: Unable to set superblock options "
			"before the security server is initialized\n");
Linus Torvalds's avatar
Linus Torvalds committed
630
		goto out;
631
	}
Linus Torvalds's avatar
Linus Torvalds committed
632

633 634 635 636 637 638 639 640 641 642 643
	/*
	 * Binary mount data FS will come through this function twice.  Once
	 * from an explicit call and once from the generic calls from the vfs.
	 * Since the generic VFS calls will not contain any security mount data
	 * we need to skip the double mount verification.
	 *
	 * This does open a hole in which we will not notice if the first
	 * mount using this sb set explict options and a second mount using
	 * this sb does not set any security options.  (The first options
	 * will be used for both mounts)
	 */
644
	if ((sbsec->flags & SE_SBINITIALIZED) && (sb->s_type->fs_flags & FS_BINARY_MOUNTDATA)
645
	    && (num_opts == 0))
646
		goto out;
647

648 649 650 651 652 653 654
	/*
	 * parse the mount options, check if they are valid sids.
	 * also check if someone is trying to mount the same sb more
	 * than once with different security options.
	 */
	for (i = 0; i < num_opts; i++) {
		u32 sid;
655 656 657

		if (flags[i] == SE_SBLABELSUPP)
			continue;
658 659
		rc = security_context_to_sid(mount_options[i],
					     strlen(mount_options[i]), &sid);
Linus Torvalds's avatar
Linus Torvalds committed
660 661 662
		if (rc) {
			printk(KERN_WARNING "SELinux: security_context_to_sid"
			       "(%s) failed for (dev %s, type %s) errno=%d\n",
663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707
			       mount_options[i], sb->s_id, name, rc);
			goto out;
		}
		switch (flags[i]) {
		case FSCONTEXT_MNT:
			fscontext_sid = sid;

			if (bad_option(sbsec, FSCONTEXT_MNT, sbsec->sid,
					fscontext_sid))
				goto out_double_mount;

			sbsec->flags |= FSCONTEXT_MNT;
			break;
		case CONTEXT_MNT:
			context_sid = sid;

			if (bad_option(sbsec, CONTEXT_MNT, sbsec->mntpoint_sid,
					context_sid))
				goto out_double_mount;

			sbsec->flags |= CONTEXT_MNT;
			break;
		case ROOTCONTEXT_MNT:
			rootcontext_sid = sid;

			if (bad_option(sbsec, ROOTCONTEXT_MNT, root_isec->sid,
					rootcontext_sid))
				goto out_double_mount;

			sbsec->flags |= ROOTCONTEXT_MNT;

			break;
		case DEFCONTEXT_MNT:
			defcontext_sid = sid;

			if (bad_option(sbsec, DEFCONTEXT_MNT, sbsec->def_sid,
					defcontext_sid))
				goto out_double_mount;

			sbsec->flags |= DEFCONTEXT_MNT;

			break;
		default:
			rc = -EINVAL;
			goto out;
Linus Torvalds's avatar
Linus Torvalds committed
708
		}
709 710
	}

711
	if (sbsec->flags & SE_SBINITIALIZED) {
712
		/* previously mounted with options, but not on this attempt? */
713
		if ((sbsec->flags & SE_MNTMASK) && !num_opts)
714 715 716 717 718
			goto out_double_mount;
		rc = 0;
		goto out;
	}

719
	if (strcmp(sb->s_type->name, "proc") == 0)
720
		sbsec->flags |= SE_SBPROC;
721 722

	/* Determine the labeling behavior to use for this filesystem type. */
723
	rc = security_fs_use((sbsec->flags & SE_SBPROC) ? "proc" : sb->s_type->name, &sbsec->behavior, &sbsec->sid);
724 725
	if (rc) {
		printk(KERN_WARNING "%s: security_fs_use(%s) returned %d\n",
726
		       __func__, sb->s_type->name, rc);
727 728
		goto out;
	}
Linus Torvalds's avatar
Linus Torvalds committed
729

730 731
	/* sets the context of the superblock for the fs being mounted. */
	if (fscontext_sid) {
732
		rc = may_context_mount_sb_relabel(fscontext_sid, sbsec, cred);
Linus Torvalds's avatar
Linus Torvalds committed
733
		if (rc)
734
			goto out;
Linus Torvalds's avatar
Linus Torvalds committed
735

736
		sbsec->sid = fscontext_sid;
737 738 739 740 741 742 743
	}

	/*
	 * Switch to using mount point labeling behavior.
	 * sets the label used on all file below the mountpoint, and will set
	 * the superblock context if not already set.
	 */
744 745
	if (context_sid) {
		if (!fscontext_sid) {
746 747
			rc = may_context_mount_sb_relabel(context_sid, sbsec,
							  cred);
748
			if (rc)
749 750
				goto out;
			sbsec->sid = context_sid;
751
		} else {
752 753
			rc = may_context_mount_inode_relabel(context_sid, sbsec,
							     cred);
754
			if (rc)
755
				goto out;
756
		}
757 758
		if (!rootcontext_sid)
			rootcontext_sid = context_sid;
Linus Torvalds's avatar
Linus Torvalds committed
759

760
		sbsec->mntpoint_sid = context_sid;
761
		sbsec->behavior = SECURITY_FS_USE_MNTPOINT;
Linus Torvalds's avatar
Linus Torvalds committed
762 763
	}

764
	if (rootcontext_sid) {
765 766
		rc = may_context_mount_inode_relabel(rootcontext_sid, sbsec,
						     cred);
767
		if (rc)
768
			goto out;
769

770 771
		root_isec->sid = rootcontext_sid;
		root_isec->initialized = 1;
772 773
	}

774 775 776 777 778 779
	if (defcontext_sid) {
		if (sbsec->behavior != SECURITY_FS_USE_XATTR) {
			rc = -EINVAL;
			printk(KERN_WARNING "SELinux: defcontext option is "
			       "invalid for this filesystem type\n");
			goto out;
Linus Torvalds's avatar
Linus Torvalds committed
780 781
		}

782 783
		if (defcontext_sid != sbsec->def_sid) {
			rc = may_context_mount_inode_relabel(defcontext_sid,
784
							     sbsec, cred);
785 786 787
			if (rc)
				goto out;
		}
Linus Torvalds's avatar
Linus Torvalds committed
788

789
		sbsec->def_sid = defcontext_sid;
Linus Torvalds's avatar
Linus Torvalds committed
790 791
	}

792
	rc = sb_finish_set_opts(sb);
Linus Torvalds's avatar
Linus Torvalds committed
793
out:
794
	mutex_unlock(&sbsec->lock);
Linus Torvalds's avatar
Linus Torvalds committed
795
	return rc;
796 797 798 799 800
out_double_mount:
	rc = -EINVAL;
	printk(KERN_WARNING "SELinux: mount invalid.  Same superblock, different "
	       "security settings for (dev %s, type %s)\n", sb->s_id, name);
	goto out;
Linus Torvalds's avatar
Linus Torvalds committed
801 802
}

803 804
static void selinux_sb_clone_mnt_opts(const struct super_block *oldsb,
					struct super_block *newsb)
Linus Torvalds's avatar
Linus Torvalds committed
805
{
806 807
	const struct superblock_security_struct *oldsbsec = oldsb->s_security;
	struct superblock_security_struct *newsbsec = newsb->s_security;
Linus Torvalds's avatar
Linus Torvalds committed
808

809 810 811
	int set_fscontext =	(oldsbsec->flags & FSCONTEXT_MNT);
	int set_context =	(oldsbsec->flags & CONTEXT_MNT);
	int set_rootcontext =	(oldsbsec->flags & ROOTCONTEXT_MNT);
Linus Torvalds's avatar
Linus Torvalds committed
812

813 814 815 816 817 818 819 820 821 822 823 824
	/*
	 * if the parent was able to be mounted it clearly had no special lsm
	 * mount options.  thus we can safely put this sb on the list and deal
	 * with it later
	 */
	if (!ss_initialized) {
		spin_lock(&sb_security_lock);
		if (list_empty(&newsbsec->list))
			list_add(&newsbsec->list, &superblock_security_head);
		spin_unlock(&sb_security_lock);
		return;
	}
825 826

	/* how can we clone if the old one wasn't set up?? */
827
	BUG_ON(!(oldsbsec->flags & SE_SBINITIALIZED));
828

829
	/* if fs is reusing a sb, just let its options stand... */
830
	if (newsbsec->flags & SE_SBINITIALIZED)
831 832
		return;

833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851
	mutex_lock(&newsbsec->lock);

	newsbsec->flags = oldsbsec->flags;

	newsbsec->sid = oldsbsec->sid;
	newsbsec->def_sid = oldsbsec->def_sid;
	newsbsec->behavior = oldsbsec->behavior;

	if (set_context) {
		u32 sid = oldsbsec->mntpoint_sid;

		if (!set_fscontext)
			newsbsec->sid = sid;
		if (!set_rootcontext) {
			struct inode *newinode = newsb->s_root->d_inode;
			struct inode_security_struct *newisec = newinode->i_security;
			newisec->sid = sid;
		}
		newsbsec->mntpoint_sid = sid;
Linus Torvalds's avatar
Linus Torvalds committed
852
	}
853 854 855 856 857
	if (set_rootcontext) {
		const struct inode *oldinode = oldsb->s_root->d_inode;
		const struct inode_security_struct *oldisec = oldinode->i_security;
		struct inode *newinode = newsb->s_root->d_inode;
		struct inode_security_struct *newisec = newinode->i_security;
Linus Torvalds's avatar
Linus Torvalds committed
858

859
		newisec->sid = oldisec->sid;
Linus Torvalds's avatar
Linus Torvalds committed
860 861
	}

862 863 864 865
	sb_finish_set_opts(newsb);
	mutex_unlock(&newsbsec->lock);
}

866 867
static int selinux_parse_opts_str(char *options,
				  struct security_mnt_opts *opts)
868
{
869
	char *p;
870 871
	char *context = NULL, *defcontext = NULL;
	char *fscontext = NULL, *rootcontext = NULL;
872
	int rc, num_mnt_opts = 0;
Linus Torvalds's avatar
Linus Torvalds committed
873

874
	opts->num_mnt_opts = 0;
Linus Torvalds's avatar
Linus Torvalds committed
875

876 877 878 879
	/* Standard string-based options. */
	while ((p = strsep(&options, "|")) != NULL) {
		int token;
		substring_t args[MAX_OPT_ARGS];
Linus Torvalds's avatar
Linus Torvalds committed
880

881 882
		if (!*p)
			continue;
Linus Torvalds's avatar
Linus Torvalds committed
883

884
		token = match_token(p, tokens, args);
Linus Torvalds's avatar
Linus Torvalds committed
885

886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937
		switch (token) {
		case Opt_context:
			if (context || defcontext) {
				rc = -EINVAL;
				printk(KERN_WARNING SEL_MOUNT_FAIL_MSG);
				goto out_err;
			}
			context = match_strdup(&args[0]);
			if (!context) {
				rc = -ENOMEM;
				goto out_err;
			}
			break;

		case Opt_fscontext:
			if (fscontext) {
				rc = -EINVAL;
				printk(KERN_WARNING SEL_MOUNT_FAIL_MSG);
				goto out_err;
			}
			fscontext = match_strdup(&args[0]);
			if (!fscontext) {
				rc = -ENOMEM;
				goto out_err;
			}
			break;

		case Opt_rootcontext:
			if (rootcontext) {
				rc = -EINVAL;
				printk(KERN_WARNING SEL_MOUNT_FAIL_MSG);
				goto out_err;
			}
			rootcontext = match_strdup(&args[0]);
			if (!rootcontext) {
				rc = -ENOMEM;
				goto out_err;
			}
			break;

		case Opt_defcontext:
			if (context || defcontext) {
				rc = -EINVAL;
				printk(KERN_WARNING SEL_MOUNT_FAIL_MSG);
				goto out_err;
			}
			defcontext = match_strdup(&args[0]);
			if (!defcontext) {
				rc = -ENOMEM;
				goto out_err;
			}
			break;
938 939
		case Opt_labelsupport:
			break;
940 941 942 943
		default:
			rc = -EINVAL;
			printk(KERN_WARNING "SELinux:  unknown mount option\n");
			goto out_err;
Linus Torvalds's avatar
Linus Torvalds committed
944 945 946

		}
	}
947

948 949 950 951 952 953 954 955 956 957 958
	rc = -ENOMEM;
	opts->mnt_opts = kcalloc(NUM_SEL_MNT_OPTS, sizeof(char *), GFP_ATOMIC);
	if (!opts->mnt_opts)
		goto out_err;

	opts->mnt_opts_flags = kcalloc(NUM_SEL_MNT_OPTS, sizeof(int), GFP_ATOMIC);
	if (!opts->mnt_opts_flags) {
		kfree(opts->mnt_opts);
		goto out_err;
	}

959
	if (fscontext) {
960 961
		opts->mnt_opts[num_mnt_opts] = fscontext;
		opts->mnt_opts_flags[num_mnt_opts++] = FSCONTEXT_MNT;
962 963
	}
	if (context) {
964 965
		opts->mnt_opts[num_mnt_opts] = context;
		opts->mnt_opts_flags[num_mnt_opts++] = CONTEXT_MNT;
966 967
	}
	if (rootcontext) {
968 969
		opts->mnt_opts[num_mnt_opts] = rootcontext;
		opts->mnt_opts_flags[num_mnt_opts++] = ROOTCONTEXT_MNT;
970 971
	}
	if (defcontext) {
972 973
		opts->mnt_opts[num_mnt_opts] = defcontext;
		opts->mnt_opts_flags[num_mnt_opts++] = DEFCONTEXT_MNT;
974 975
	}

976 977 978
	opts->num_mnt_opts = num_mnt_opts;
	return 0;

979 980 981 982 983
out_err:
	kfree(context);
	kfree(defcontext);
	kfree(fscontext);
	kfree(rootcontext);
Linus Torvalds's avatar
Linus Torvalds committed
984 985
	return rc;
}
986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012
/*
 * string mount options parsing and call set the sbsec
 */
static int superblock_doinit(struct super_block *sb, void *data)
{
	int rc = 0;
	char *options = data;
	struct security_mnt_opts opts;

	security_init_mnt_opts(&opts);

	if (!data)
		goto out;

	BUG_ON(sb->s_type->fs_flags & FS_BINARY_MOUNTDATA);

	rc = selinux_parse_opts_str(options, &opts);
	if (rc)
		goto out_err;

out:
	rc = selinux_set_mnt_opts(sb, &opts);

out_err:
	security_free_mnt_opts(&opts);
	return rc;
}
Linus Torvalds's avatar
Linus Torvalds committed
1013

Adrian Bunk's avatar
Adrian Bunk committed
1014 1015
static void selinux_write_opts(struct seq_file *m,
			       struct security_mnt_opts *opts)
1016 1017 1018 1019 1020
{
	int i;
	char *prefix;

	for (i = 0; i < opts->num_mnt_opts; i++) {
1021 1022 1023 1024 1025 1026
		char *has_comma;

		if (opts->mnt_opts[i])
			has_comma = strchr(opts->mnt_opts[i], ',');
		else
			has_comma = NULL;
1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040

		switch (opts->mnt_opts_flags[i]) {
		case CONTEXT_MNT:
			prefix = CONTEXT_STR;
			break;
		case FSCONTEXT_MNT:
			prefix = FSCONTEXT_STR;
			break;
		case ROOTCONTEXT_MNT:
			prefix = ROOTCONTEXT_STR;
			break;
		case DEFCONTEXT_MNT:
			prefix = DEFCONTEXT_STR;
			break;
1041 1042 1043 1044
		case SE_SBLABELSUPP:
			seq_putc(m, ',');
			seq_puts(m, LABELSUPP_STR);
			continue;
1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064
		default:
			BUG();
		};
		/* we need a comma before each option */
		seq_putc(m, ',');
		seq_puts(m, prefix);
		if (has_comma)
			seq_putc(m, '\"');
		seq_puts(m, opts->mnt_opts[i]);
		if (has_comma)
			seq_putc(m, '\"');
	}
}

static int selinux_sb_show_options(struct seq_file *m, struct super_block *sb)
{
	struct security_mnt_opts opts;
	int rc;

	rc = selinux_get_mnt_opts(sb, &opts);
1065 1066 1067 1068
	if (rc) {
		/* before policy load we may get EINVAL, don't show anything */
		if (rc == -EINVAL)
			rc = 0;
1069
		return rc;
1070
	}
1071 1072 1073 1074 1075 1076 1077 1078

	selinux_write_opts(m, &opts);

	security_free_mnt_opts(&opts);

	return rc;
}

Linus Torvalds's avatar
Linus Torvalds committed
1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101
static inline u16 inode_mode_to_security_class(umode_t mode)
{
	switch (mode & S_IFMT) {
	case S_IFSOCK:
		return SECCLASS_SOCK_FILE;
	case S_IFLNK:
		return SECCLASS_LNK_FILE;
	case S_IFREG:
		return SECCLASS_FILE;
	case S_IFBLK:
		return SECCLASS_BLK_FILE;
	case S_IFDIR:
		return SECCLASS_DIR;
	case S_IFCHR:
		return SECCLASS_CHR_FILE;
	case S_IFIFO:
		return SECCLASS_FIFO_FILE;

	}

	return SECCLASS_FILE;
}

1102 1103 1104 1105 1106 1107 1108 1109 1110 1111
static inline int default_protocol_stream(int protocol)
{
	return (protocol == IPPROTO_IP || protocol == IPPROTO_TCP);
}

static inline int default_protocol_dgram(int protocol)
{
	return (protocol == IPPROTO_IP || protocol == IPPROTO_UDP);
}

Linus Torvalds's avatar
Linus Torvalds committed
1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127
static inline u16 socket_type_to_security_class(int family, int type, int protocol)
{
	switch (family) {
	case PF_UNIX:
		switch (type) {
		case SOCK_STREAM:
		case SOCK_SEQPACKET:
			return SECCLASS_UNIX_STREAM_SOCKET;
		case SOCK_DGRAM:
			return SECCLASS_UNIX_DGRAM_SOCKET;
		}
		break;
	case PF_INET:
	case PF_INET6:
		switch (type) {
		case SOCK_STREAM:
1128 1129 1130 1131
			if (default_protocol_stream(protocol))
				return SECCLASS_TCP_SOCKET;
			else
				return SECCLASS_RAWIP_SOCKET;
Linus Torvalds's avatar
Linus Torvalds committed
1132
		case SOCK_DGRAM:
1133 1134 1135 1136
			if (default_protocol_dgram(protocol))
				return SECCLASS_UDP_SOCKET;
			else
				return SECCLASS_RAWIP_SOCKET;
James Morris's avatar
James Morris committed
1137 1138
		case SOCK_DCCP:
			return SECCLASS_DCCP_SOCKET;
1139
		default:
Linus Torvalds's avatar
Linus Torvalds committed
1140 1141 1142 1143 1144 1145 1146 1147 1148
			return SECCLASS_RAWIP_SOCKET;
		}
		break;
	case PF_NETLINK:
		switch (protocol) {
		case NETLINK_ROUTE:
			return SECCLASS_NETLINK_ROUTE_SOCKET;
		case NETLINK_FIREWALL:
			return SECCLASS_NETLINK_FIREWALL_SOCKET;
1149
		case NETLINK_INET_DIAG:
Linus Torvalds's avatar
Linus Torvalds committed
1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162
			return SECCLASS_NETLINK_TCPDIAG_SOCKET;
		case NETLINK_NFLOG:
			return SECCLASS_NETLINK_NFLOG_SOCKET;
		case NETLINK_XFRM:
			return SECCLASS_NETLINK_XFRM_SOCKET;
		case NETLINK_SELINUX:
			return SECCLASS_NETLINK_SELINUX_SOCKET;
		case NETLINK_AUDIT:
			return SECCLASS_NETLINK_AUDIT_SOCKET;
		case NETLINK_IP6_FW:
			return SECCLASS_NETLINK_IP6FW_SOCKET;
		case NETLINK_DNRTMSG:
			return SECCLASS_NETLINK_DNRT_SOCKET;
1163 1164
		case NETLINK_KOBJECT_UEVENT:
			return SECCLASS_NETLINK_KOBJECT_UEVENT_SOCKET;
Linus Torvalds's avatar
Linus Torvalds committed
1165 1166 1167 1168 1169 1170 1171
		default:
			return SECCLASS_NETLINK_SOCKET;
		}
	case PF_PACKET:
		return SECCLASS_PACKET_SOCKET;
	case PF_KEY:
		return SECCLASS_KEY_SOCKET;
1172 1173
	case PF_APPLETALK:
		return SECCLASS_APPLETALK_SOCKET;
Linus Torvalds's avatar
Linus Torvalds committed
1174 1175 1176 1177