Follow @Openwall on Twitter for new release announcements and other news
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Date: Thu, 24 Mar 2016 02:46:37 +0100
From: Mickaël Salaün <mic@...ikod.net>
To: linux-security-module@...r.kernel.org
Cc: Mickaël Salaün <mic@...ikod.net>,
	Andreas Gruenbacher <agruenba@...hat.com>,
	Andy Lutomirski <luto@...capital.net>,
	Andy Lutomirski <luto@...nel.org>,
	Arnd Bergmann <arnd@...db.de>,
	Casey Schaufler <casey@...aufler-ca.com>,
	Daniel Borkmann <daniel@...earbox.net>,
	David Drysdale <drysdale@...gle.com>,
	Eric Paris <eparis@...hat.com>,
	James Morris <james.l.morris@...cle.com>,
	Jeff Dike <jdike@...toit.com>,
	Julien Tinnes <jln@...gle.com>,
	Kees Cook <keescook@...omium.org>,
	Michael Kerrisk <mtk@...7.org>,
	Paul Moore <pmoore@...hat.com>,
	Richard Weinberger <richard@....at>,
	"Serge E . Hallyn" <serge@...lyn.com>,
	Stephen Smalley <sds@...ho.nsa.gov>,
	Tetsuo Handa <penguin-kernel@...ove.SAKURA.ne.jp>,
	Will Drewry <wad@...omium.org>,
	linux-api@...r.kernel.org,
	kernel-hardening@...ts.openwall.com
Subject: [RFC v1 06/17] seccomp: Add the SECCOMP_ADD_CHECKER_GROUP command

A new command SECCOMP_ADD_CHECKER_GROUP allows userland seccomp filters
to reference kernel objects with checkers in a batch.

Each checker is autonomous and new ones can easily be added in the
future. There is currently two checkers for path objects:
* SECCOMP_CHECK_FS_LITERAL checks if a string match a defined path;
* SECCOMP_CHECK_FS_BENEATH checks if the path representation of a string
  is equal or equivalent to a file belonging to a defined path.

These checkers can use a bitmask of flags to match a path:
* SECCOMP_OBJFLAG_FS_DENTRY match a unique file;
* SECCOMP_OBJFLAG_FS_INODE only match a file inode (must be used with
  the device flag);
* SECCOMP_OBJFLAG_FS_DEVICE match the device of a file;
* SECCOMP_OBJFLAG_FS_MOUNT match the mount point of a file;
* SECCOMP_OBJFLAG_FS_NOFOLLOW do not follow a symlink for the
  initial checker evaluation.

Signed-off-by: Mickaël Salaün <mic@...ikod.net>
Cc: Andy Lutomirski <luto@...nel.org>
Cc: David Drysdale <drysdale@...gle.com>
Cc: James Morris <james.l.morris@...cle.com>
Cc: Kees Cook <keescook@...omium.org>
Cc: Michael Kerrisk <mtk@...7.org>
Cc: Paul Moore <pmoore@...hat.com>
Cc: Serge E. Hallyn <serge@...lyn.com>
Cc: Will Drewry <wad@...omium.org>
---
 include/linux/seccomp.h       |  32 ++++++
 include/uapi/linux/seccomp.h  |  81 ++++++++++++++++
 kernel/seccomp.c              | 221 ++++++++++++++++++++++++++++++++++++++++++
 security/seccomp/Makefile     |   2 +-
 security/seccomp/checker_fs.c | 102 +++++++++++++++++++
 5 files changed, 437 insertions(+), 1 deletion(-)
 create mode 100644 security/seccomp/checker_fs.c

diff --git a/include/linux/seccomp.h b/include/linux/seccomp.h
index 2296e6b2f690..78f5861a0328 100644
--- a/include/linux/seccomp.h
+++ b/include/linux/seccomp.h
@@ -9,8 +9,10 @@
 
 #include <linux/thread_info.h>
 #include <asm/seccomp.h>
+#include <linux/path.h>
 
 struct seccomp_filter;
+struct seccomp_filter_checker_group;
 /**
  * struct seccomp - the state of a seccomp'ed process
  *
@@ -19,12 +21,20 @@ struct seccomp_filter;
  * @filter: must always point to a valid seccomp-filter or NULL as it is
  *          accessed without locking during system call entry.
  *
+ * @checker_group: an append-only list of argument checkers usable by filters
+ *                 created after the last update.
+ *
  *          @filter must only be accessed from the context of current as there
  *          is no read locking.
  */
 struct seccomp {
 	int mode;
 	struct seccomp_filter *filter;
+
+#ifdef CONFIG_SECURITY_SECCOMP
+	/* @checker_group is only used for filter creation and unique per thread */
+	struct seccomp_filter_checker_group *checker_group;
+#endif
 };
 
 #ifdef CONFIG_HAVE_ARCH_SECCOMP_FILTER
@@ -85,6 +95,28 @@ static inline int seccomp_mode(struct seccomp *s)
 #ifdef CONFIG_SECCOMP_FILTER
 extern void put_seccomp_filter(struct task_struct *tsk);
 extern void get_seccomp_filter(struct task_struct *tsk);
+
+#ifdef CONFIG_SECURITY_SECCOMP
+struct seccomp_filter_object_path {
+	u32 flags;
+	struct path path;
+};
+
+struct seccomp_filter_checker {
+	/* e.g. SECCOMP_ARGCHECK_FS_LITERAL */
+	u32 check;
+	/* e.g. SECCOMP_ARGTYPE_PATH */
+	u32 type;
+	union {
+		struct seccomp_filter_object_path object_path;
+	};
+};
+
+
+long seccomp_set_argcheck_fs(const struct seccomp_checker *,
+			     struct seccomp_filter_checker *);
+#endif /* CONFIG_SECURITY_SECCOMP */
+
 #else  /* CONFIG_SECCOMP_FILTER */
 static inline void put_seccomp_filter(struct task_struct *tsk)
 {
diff --git a/include/uapi/linux/seccomp.h b/include/uapi/linux/seccomp.h
index 0f238a43ff1e..ca7e9343f3d7 100644
--- a/include/uapi/linux/seccomp.h
+++ b/include/uapi/linux/seccomp.h
@@ -13,6 +13,7 @@
 /* Valid operations for seccomp syscall. */
 #define SECCOMP_SET_MODE_STRICT	0
 #define SECCOMP_SET_MODE_FILTER	1
+#define SECCOMP_ADD_CHECKER_GROUP	2 /* add a group of checkers */
 
 /* Valid flags for SECCOMP_SET_MODE_FILTER */
 #define SECCOMP_FILTER_FLAG_TSYNC	1
@@ -35,6 +36,25 @@
 #define SECCOMP_RET_ACTION	0x7fff0000U
 #define SECCOMP_RET_DATA	0x0000ffffU
 
+/* Object checks */
+#define SECCOMP_CHECK_FS_LITERAL	1
+#define SECCOMP_CHECK_FS_BENEATH	2
+
+/* Object flags */
+#define SECCOMP_OBJFLAG_FS_DENTRY	(1 << 0)
+#define SECCOMP_OBJFLAG_FS_INODE	(1 << 1)
+#define SECCOMP_OBJFLAG_FS_DEVICE	(1 << 2)
+#define SECCOMP_OBJFLAG_FS_MOUNT	(1 << 3)
+/* Do the evaluation follow the argument path? (cf. fs/namei.c)
+ * This flag is only used for the seccomp filter but not by the LSM check to
+ * enforce access control. You need to take care of the different path
+ * interpretation per syscall (e.g. rename(2) or open(2) with O_NOFOLLOW).
+ */
+#define SECCOMP_OBJFLAG_FS_NOFOLLOW	(1 << 4)
+
+/* Argument types */
+#define SECCOMP_OBJTYPE_PATH		1
+
 /**
  * struct seccomp_data - the format the BPF program executes over.
  * @nr: the system call number
@@ -51,4 +71,65 @@ struct seccomp_data {
 	__u64 args[6];
 };
 
+/* TODO: Add a "at" field (default to AT_FDCWD) */
+struct seccomp_object_path {
+	/* e.g. SECCOMP_OBJFLAG_FS_DENTRY */
+	__u32 flags;
+	const char *path;
+};
+
+struct seccomp_checker {
+	__u32 check;
+	__u32 type;
+	/* Must match the checker extra size, if any */
+	unsigned int len;
+	/* Checkers must be pointers to allow futur additions */
+	union {
+		const struct seccomp_object_path *object_path;
+	};
+};
+
+#define SECCOMP_MAKE_PATH_DENTRY(_p)				\
+	{							\
+		.flags = SECCOMP_OBJFLAG_FS_DENTRY,		\
+		.path = _p,					\
+	}
+
+#define SECCOMP_MAKE_PATH_INODE(_p)				\
+	{							\
+		.flags = SECCOMP_OBJFLAG_FS_INODE |		\
+			SECCOMP_OBJFLAG_FS_DEVICE,		\
+		.path = _p,					\
+	}
+
+#define SECCOMP_MAKE_PATH_MOUNT(_p)				\
+	{							\
+		.flags = SECCOMP_OBJFLAG_FS_MOUNT,		\
+		.path = _p,					\
+	}
+
+#define SECCOMP_MAKE_PATH_ALL(_p)				\
+	{							\
+		.flags = SECCOMP_OBJFLAG_FS_DENTRY |		\
+			SECCOMP_OBJFLAG_FS_INODE |		\
+			SECCOMP_OBJFLAG_FS_DEVICE |		\
+			SECCOMP_OBJFLAG_FS_MOUNT,		\
+		.path = _p,					\
+	}
+
+#define SECCOMP_MAKE_OBJ_PATH(_c, _p)				\
+	{							\
+		.check = SECCOMP_CHECK_##_c,			\
+		.type = SECCOMP_OBJTYPE_PATH,			\
+		.len = 0,					\
+		.object_path = _p,				\
+	}
+
+struct seccomp_checker_group {
+	__u8 version;
+	__u8 id;
+	unsigned int len;
+	const struct seccomp_checker (*checkers)[];
+};
+
 #endif /* _UAPI_LINUX_SECCOMP_H */
diff --git a/kernel/seccomp.c b/kernel/seccomp.c
index 2c94693e4163..0e5471d2891c 100644
--- a/kernel/seccomp.c
+++ b/kernel/seccomp.c
@@ -6,6 +6,8 @@
  * Copyright (C) 2012 Google, Inc.
  * Will Drewry <wad@...omium.org>
  *
+ * Copyright (C) 2016  Mickaël Salaün <mic@...ikod.net>
+ *
  * This defines a simple but solid secure-computing facility.
  *
  * Mode 1 uses a fixed list of allowed system calls.
@@ -60,6 +62,34 @@ struct seccomp_filter {
 	struct bpf_prog *prog;
 };
 
+/* Argument group attached to seccomp filters
+ *
+ * @usage keep track of the references
+ * @prev link to the previous checker_group
+ * @id is given by userland to easely check a filter statically and not
+ *     leak data from the kernel
+ * @checkers_len is the number of @checkers elements
+ * @checkers contains the checkers
+ *
+ * seccomp_filter_checker_group checkers are organized in a tree linked via the
+ * @prev pointer. For any task, it appears to be a singly-linked list starting
+ * with current->seccomp.filter->checker_group, the most recently added argument
+ * group. All filters created by a process share the argument groups created by
+ * this process until the filter creation but they can not be changed. However,
+ * multiple argument groups may share a @prev node, which results in a
+ * unidirectional tree existing in memory. They are not inherited through
+ * fork().
+ */
+#ifdef CONFIG_SECURITY_SECCOMP
+struct seccomp_filter_checker_group {
+	atomic_t usage;
+	struct seccomp_filter_checker_group *prev;
+	u8 id;
+	unsigned int checkers_len;
+	struct seccomp_filter_checker checkers[];
+};
+#endif /* CONFIG_SECURITY_SECCOMP */
+
 /* Limit any path through the tree to 256KB worth of instructions. */
 #define MAX_INSNS_PER_PATH ((1 << 18) / sizeof(struct sock_filter))
 
@@ -467,6 +497,38 @@ void get_seccomp_filter(struct task_struct *tsk)
 	atomic_inc(&orig->usage);
 }
 
+#ifdef CONFIG_SECURITY_SECCOMP
+/* Do not free @checker */
+static void put_seccomp_obj(struct seccomp_filter_checker *checker)
+{
+	switch (checker->type) {
+	case SECCOMP_OBJTYPE_PATH:
+		/* Pointer checks done in path_put() */
+		path_put(&checker->object_path.path);
+		break;
+	default:
+		WARN_ON(1);
+	}
+}
+
+/* Free @checker_group */
+static void put_seccomp_checker_group(struct seccomp_filter_checker_group *checker_group)
+{
+	int i;
+	struct seccomp_filter_checker_group *orig = checker_group;
+
+	/* Clean up single-reference branches iteratively. */
+	while (orig && atomic_dec_and_test(&orig->usage)) {
+		struct seccomp_filter_checker_group *freeme = orig;
+
+		for (i = 0; i < freeme->checkers_len; i++)
+			put_seccomp_obj(&freeme->checkers[i]);
+		orig = orig->prev;
+		kfree(freeme);
+	}
+}
+#endif /* CONFIG_SECURITY_SECCOMP */
+
 static inline void seccomp_filter_free(struct seccomp_filter *filter)
 {
 	if (filter) {
@@ -485,6 +547,9 @@ void put_seccomp_filter(struct task_struct *tsk)
 		orig = orig->prev;
 		seccomp_filter_free(freeme);
 	}
+#ifdef CONFIG_SECURITY_SECCOMP
+	put_seccomp_checker_group(tsk->seccomp.checker_group);
+#endif
 }
 
 /**
@@ -813,6 +878,158 @@ static inline long seccomp_set_mode_filter(unsigned int flags,
 }
 #endif
 
+#ifdef CONFIG_SECURITY_SECCOMP
+
+/* Limit checkers number to 64 to be able to show matches with a bitmask. */
+#define SECCOMP_CHECKERS_MAX 64
+
+/* Limit arg group list and their checkers to 256KB. */
+#define SECCOMP_GROUP_CHECKERS_MAX_SIZE (1 << 18)
+
+static long seccomp_add_checker_group(unsigned int flags, const char __user *group)
+{
+	struct seccomp_checker_group kgroup;
+	struct seccomp_checker (*kcheckers)[], *user_checker;
+	struct seccomp_filter_checker_group *filter_group, *walker;
+	struct seccomp_filter_checker *kernel_obj;
+	unsigned int i;
+	unsigned long group_size, kcheckers_size, full_group_size;
+	long result;
+
+	if (!task_no_new_privs(current) &&
+	    security_capable_noaudit(current_cred(),
+				     current_user_ns(), CAP_SYS_ADMIN) != 0)
+		return -EACCES;
+	if (flags != 0 || !group)
+		return -EINVAL;
+
+#ifdef CONFIG_COMPAT
+	if (is_compat_task()) {
+		struct compat_seccomp_checker_group kgroup32;
+
+		if (copy_from_user(&kgroup32, group, sizeof(kgroup32)))
+			return -EFAULT;
+		kgroup.version = kgroup32.version;
+		kgroup.id = kgroup32.id;
+		kgroup.len = kgroup32.len;
+		kgroup.checkers = compat_ptr(kgroup32.checkers);
+	} else			/* Falls through to the if below */
+#endif /* CONFIG_COMPAT */
+	if (copy_from_user(&kgroup, group, sizeof(kgroup)))
+		return -EFAULT;
+
+	if (kgroup.version != 1)
+		return -EINVAL;
+	/* The group ID 0 means no evaluated checkers */
+	if (kgroup.id == 0)
+		return -EINVAL;
+	if (kgroup.len == 0)
+		return -EINVAL;
+	if (kgroup.len > SECCOMP_CHECKERS_MAX)
+		return -E2BIG;
+
+	/* Validate resulting checker_group ID and length. */
+	group_size = sizeof(*filter_group) +
+		kgroup.len * sizeof(filter_group->checkers[0]);
+	full_group_size = group_size;
+	for (walker = current->seccomp.checker_group;
+			walker; walker = walker->prev) {
+		if (walker->id == kgroup.id)
+			return -EINVAL;
+		/* TODO: add penalty? */
+		full_group_size += sizeof(*walker) +
+			walker->checkers_len * sizeof(walker->checkers[0]);
+	}
+	if (full_group_size > SECCOMP_GROUP_CHECKERS_MAX_SIZE)
+		return -ENOMEM;
+
+	kcheckers_size = kgroup.len * sizeof((*kcheckers)[0]);
+	kcheckers = kmalloc(kcheckers_size, GFP_KERNEL);
+	if (!kcheckers)
+		return -ENOMEM;
+
+#ifdef CONFIG_COMPAT
+	if (is_compat_task()) {
+		unsigned int i, kcheckers32_size;
+		struct compat_seccomp_checker (*kcheckers32)[];
+
+		kcheckers32_size = kgroup.len * sizeof((*kcheckers32)[0]);
+		kcheckers32 = kmalloc(kcheckers32_size, GFP_KERNEL);
+		if (!kcheckers32) {
+			result = -ENOMEM;
+			goto free_kcheckers;
+		}
+		if (copy_from_user(kcheckers32, kgroup.checkers, kcheckers32_size)) {
+			kfree(kcheckers32);
+			result = -EFAULT;
+			goto free_kcheckers;
+		}
+		for (i = 0; i < kgroup.len; i++) {
+			(*kcheckers)[i].check = (*kcheckers32)[i].check;
+			(*kcheckers)[i].type = (*kcheckers32)[i].type;
+			(*kcheckers)[i].len = (*kcheckers32)[i].len;
+			(*kcheckers)[i].object_path = compat_ptr((*kcheckers32)[i].checker);
+		}
+		kfree(kcheckers32);
+	} else			/* Falls through to the if below */
+#endif /* CONFIG_COMPAT */
+	if (copy_from_user(kcheckers, kgroup.checkers, kcheckers_size)) {
+		result = -EFAULT;
+		goto free_kcheckers;
+	}
+
+	/* filter_group->checkers must be zeroed to correctly be freed on error */
+	filter_group = kzalloc(group_size, GFP_KERNEL);
+	if (!filter_group) {
+		result = -ENOMEM;
+		goto free_kcheckers;
+	}
+	filter_group->prev = NULL;
+	filter_group->id = kgroup.id;
+	filter_group->checkers_len = kgroup.len;
+	for (i = 0; i < filter_group->checkers_len; i++) {
+		user_checker = &(*kcheckers)[i];
+		kernel_obj = &filter_group->checkers[i];
+		switch (user_checker->check) {
+		case SECCOMP_CHECK_FS_LITERAL:
+		case SECCOMP_CHECK_FS_BENEATH:
+			kernel_obj->check = user_checker->check;
+			result =
+			    seccomp_set_argcheck_fs(user_checker, kernel_obj);
+			if (result)
+				goto free_group;
+			break;
+		default:
+			result = -EINVAL;
+			goto free_group;
+		}
+	}
+
+	atomic_set(&filter_group->usage, 1);
+	filter_group->prev = current->seccomp.checker_group;
+	/* No need to update filter_group->prev->usage because it get one
+	 * reference from this filter but lose one from
+	 * current->seccomp.checker_group.
+	 */
+	current->seccomp.checker_group = filter_group;
+	/* XXX: Return the number of groups? */
+	result = 0;
+	goto free_kcheckers;
+
+free_group:
+	for (i = 0; i < filter_group->checkers_len; i++) {
+		kernel_obj = &filter_group->checkers[i];
+		if (kernel_obj->type)
+			put_seccomp_obj(kernel_obj);
+	}
+	kfree(filter_group);
+
+free_kcheckers:
+	kfree(kcheckers);
+	return result;
+}
+#endif /* CONFIG_SECURITY_SECCOMP */
+
 /* Common entry point for both prctl and syscall. */
 static long do_seccomp(unsigned int op, unsigned int flags,
 		       const char __user *uargs)
@@ -824,6 +1041,10 @@ static long do_seccomp(unsigned int op, unsigned int flags,
 		return seccomp_set_mode_strict();
 	case SECCOMP_SET_MODE_FILTER:
 		return seccomp_set_mode_filter(flags, uargs);
+#ifdef CONFIG_SECURITY_SECCOMP
+	case SECCOMP_ADD_CHECKER_GROUP:
+		return seccomp_add_checker_group(flags, uargs);
+#endif /* CONFIG_SECURITY_SECCOMP */
 	default:
 		return -EINVAL;
 	}
diff --git a/security/seccomp/Makefile b/security/seccomp/Makefile
index f2e848d81138..1ed68b23a922 100644
--- a/security/seccomp/Makefile
+++ b/security/seccomp/Makefile
@@ -1,3 +1,3 @@
 obj-$(CONFIG_SECURITY_SECCOMP) := seccomp.o
 
-seccomp-y := lsm.o
+seccomp-y := lsm.o checker_fs.o
diff --git a/security/seccomp/checker_fs.c b/security/seccomp/checker_fs.c
new file mode 100644
index 000000000000..c11efc892de5
--- /dev/null
+++ b/security/seccomp/checker_fs.c
@@ -0,0 +1,102 @@
+/*
+ * Seccomp Linux Security Module - File System Checkers
+ *
+ * Copyright (C) 2016  Mickaël Salaün <mic@...ikod.net>
+ *
+ * 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/compat.h>
+#include <linux/namei.h>	/* user_lpath() */
+#include <linux/path.h>
+#include <linux/seccomp.h>
+#include <linux/slab.h>
+#include <linux/uaccess.h>	/* copy_from_user() */
+
+#ifdef CONFIG_COMPAT
+/* struct seccomp_object_path */
+struct compat_seccomp_object_path {
+	__u32 flags;
+	compat_uptr_t path;	/* const char * */
+};
+#endif
+
+static const u32 path_flags_mask_literal =
+	SECCOMP_OBJFLAG_FS_DENTRY |
+	SECCOMP_OBJFLAG_FS_INODE |
+	SECCOMP_OBJFLAG_FS_DEVICE |
+	SECCOMP_OBJFLAG_FS_MOUNT |
+	SECCOMP_OBJFLAG_FS_NOFOLLOW;
+
+static const u32 path_flags_mask_beneath =
+	SECCOMP_OBJFLAG_FS_DENTRY |
+	SECCOMP_OBJFLAG_FS_INODE |
+	SECCOMP_OBJFLAG_FS_NOFOLLOW;
+
+/* Return true for any error, or false if flags are OK. */
+static bool wrong_check_flags(u32 check, u32 flags)
+{
+	u32 path_flags_mask;
+
+	/* Do not allow insecure check: inode without device */
+	if ((flags & SECCOMP_OBJFLAG_FS_INODE) &&
+	    !(flags & SECCOMP_OBJFLAG_FS_DEVICE))
+		return true;
+
+	switch (check) {
+	case SECCOMP_CHECK_FS_LITERAL:
+		path_flags_mask = path_flags_mask_literal;
+		break;
+	case SECCOMP_CHECK_FS_BENEATH:
+		path_flags_mask = path_flags_mask_beneath;
+		break;
+	default:
+		WARN_ON(1);
+		return true;
+	}
+	/* Need at least one flag, but only in the allowed mask */
+	return !(flags & path_flags_mask) ||
+		((flags | path_flags_mask) != path_flags_mask);
+}
+
+static long set_argtype_path(const struct seccomp_checker *user_checker,
+			     struct seccomp_filter_checker *kernel_checker)
+{
+	struct seccomp_object_path user_cp;
+
+	/* @len is not used for @object_path */
+	if (user_checker->len != 0)
+		return -EINVAL;
+
+#ifdef CONFIG_COMPAT
+	if (is_compat_task()) {
+		struct compat_seccomp_object_path user_cp32;
+
+		if (copy_from_user(&user_cp32, user_checker->object_path, sizeof(user_cp32)))
+			return -EFAULT;
+		user_cp.flags = user_cp32.flags;
+		user_cp.path = compat_ptr(user_cp32.path);
+	} else			/* Falls through to the if below */
+#endif
+	if (copy_from_user(&user_cp, user_checker->object_path, sizeof(user_cp)))
+		return -EFAULT;
+
+	if (wrong_check_flags(kernel_checker->check, user_cp.flags))
+		return -EINVAL;
+	kernel_checker->object_path.flags = user_cp.flags;
+	/* Do not follow symlinks for objects */
+	return user_lpath(user_cp.path, &kernel_checker->object_path.path);
+}
+
+long seccomp_set_argcheck_fs(const struct seccomp_checker *user_checker,
+			     struct seccomp_filter_checker *kernel_checker)
+{
+	switch (user_checker->type) {
+	case SECCOMP_OBJTYPE_PATH:
+		kernel_checker->type = user_checker->type;
+		return set_argtype_path(user_checker, kernel_checker);
+	}
+	return -EINVAL;
+}
-- 
2.8.0.rc3

Powered by blists - more mailing lists

Confused about mailing lists and their use? Read about mailing lists on Wikipedia and check out these guidelines on proper formatting of your messages.