[PATCH] net/sunrpc: Add user namespace support

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



This adds the ability to pass a non-init user namespace to rpcauth_create,
via rpc_auth_create_args. If the specific authentication mechanism
does not support non-init user namespaces, then it will return an
error.

Currently, the only two authentication mechanisms that support
non-init user namespaces are auth_null, and auth_unix. auth_unix
will send the UID / GID from the user namespace for authentication.

Signed-off-by: Sargun Dhillon <sargun@xxxxxxxxx>
---
 fs/nfs/nfs4proc.c              |  3 +-
 include/linux/sunrpc/auth.h    |  9 ++--
 net/sunrpc/auth.c              | 17 +++-----
 net/sunrpc/auth_generic.c      |  1 +
 net/sunrpc/auth_gss/auth_gss.c | 10 +++--
 net/sunrpc/auth_null.c         |  3 +-
 net/sunrpc/auth_unix.c         | 97 ++++++++++++++++++++++++++++--------------
 net/sunrpc/clnt.c              |  5 ++-
 8 files changed, 89 insertions(+), 56 deletions(-)

diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c
index 6dd146885da9..ab92ac8d48a8 100644
--- a/fs/nfs/nfs4proc.c
+++ b/fs/nfs/nfs4proc.c
@@ -3657,7 +3657,8 @@ static int nfs4_lookup_root_sec(struct nfs_server *server, struct nfs_fh *fhandl
 				struct nfs_fsinfo *info, rpc_authflavor_t flavor)
 {
 	struct rpc_auth_create_args auth_args = {
-		.pseudoflavor = flavor,
+		.pseudoflavor	= flavor,
+		.user_ns	= &init_user_ns,
 	};
 	struct rpc_auth *auth;
 
diff --git a/include/linux/sunrpc/auth.h b/include/linux/sunrpc/auth.h
index d9af474a857d..7f320be28efc 100644
--- a/include/linux/sunrpc/auth.h
+++ b/include/linux/sunrpc/auth.h
@@ -111,6 +111,7 @@ struct rpc_auth {
 
 struct rpc_auth_create_args {
 	rpc_authflavor_t pseudoflavor;
+	struct user_namespace *user_ns;
 	const char *target_name;
 };
 
@@ -125,7 +126,9 @@ struct rpc_authops {
 	struct module		*owner;
 	rpc_authflavor_t	au_flavor;	/* flavor (RPC_AUTH_*) */
 	char *			au_name;
-	struct rpc_auth *	(*create)(struct rpc_auth_create_args *, struct rpc_clnt *);
+	bool			user_ns;	/* supports user namespaces */
+	struct rpc_auth *	(*create)(const struct rpc_auth_create_args *,
+					  struct rpc_clnt *);
 	void			(*destroy)(struct rpc_auth *);
 
 	int			(*hash_cred)(struct auth_cred *, unsigned int);
@@ -161,12 +164,10 @@ struct rpc_credops {
 extern const struct rpc_authops	authunix_ops;
 extern const struct rpc_authops	authnull_ops;
 
-int __init		rpc_init_authunix(void);
 int __init		rpc_init_generic_auth(void);
 int __init		rpcauth_init_module(void);
 void			rpcauth_remove_module(void);
 void			rpc_destroy_generic_auth(void);
-void 			rpc_destroy_authunix(void);
 
 struct rpc_cred *	rpc_lookup_cred(void);
 struct rpc_cred *	rpc_lookup_cred_nonblock(void);
@@ -174,7 +175,7 @@ struct rpc_cred *	rpc_lookup_generic_cred(struct auth_cred *, int, gfp_t);
 struct rpc_cred *	rpc_lookup_machine_cred(const char *service_name);
 int			rpcauth_register(const struct rpc_authops *);
 int			rpcauth_unregister(const struct rpc_authops *);
-struct rpc_auth *	rpcauth_create(struct rpc_auth_create_args *,
+struct rpc_auth *	rpcauth_create(const struct rpc_auth_create_args *,
 				struct rpc_clnt *);
 void			rpcauth_release(struct rpc_auth *);
 rpc_authflavor_t	rpcauth_get_pseudoflavor(rpc_authflavor_t,
diff --git a/net/sunrpc/auth.c b/net/sunrpc/auth.c
index d2623b9f23d6..9cf1076375d5 100644
--- a/net/sunrpc/auth.c
+++ b/net/sunrpc/auth.c
@@ -253,7 +253,7 @@ rpcauth_list_flavors(rpc_authflavor_t *array, int size)
 EXPORT_SYMBOL_GPL(rpcauth_list_flavors);
 
 struct rpc_auth *
-rpcauth_create(struct rpc_auth_create_args *args, struct rpc_clnt *clnt)
+rpcauth_create(const struct rpc_auth_create_args *args, struct rpc_clnt *clnt)
 {
 	struct rpc_auth		*auth;
 	const struct rpc_authops *ops;
@@ -272,7 +272,8 @@ rpcauth_create(struct rpc_auth_create_args *args, struct rpc_clnt *clnt)
 		goto out;
 	}
 	spin_unlock(&rpc_authflavor_lock);
-	auth = ops->create(args, clnt);
+	if (args->user_ns == &init_user_ns || ops->user_ns)
+		auth = ops->create(args, clnt);
 	module_put(ops->owner);
 	if (IS_ERR(auth))
 		return auth;
@@ -870,27 +871,21 @@ int __init rpcauth_init_module(void)
 {
 	int err;
 
-	err = rpc_init_authunix();
-	if (err < 0)
-		goto out1;
 	err = rpc_init_generic_auth();
 	if (err < 0)
-		goto out2;
+		goto out1;
 	err = register_shrinker(&rpc_cred_shrinker);
 	if (err < 0)
-		goto out3;
+		goto out2;
 	return 0;
-out3:
-	rpc_destroy_generic_auth();
 out2:
-	rpc_destroy_authunix();
+	rpc_destroy_generic_auth();
 out1:
 	return err;
 }
 
 void rpcauth_remove_module(void)
 {
-	rpc_destroy_authunix();
 	rpc_destroy_generic_auth();
 	unregister_shrinker(&rpc_cred_shrinker);
 }
diff --git a/net/sunrpc/auth_generic.c b/net/sunrpc/auth_generic.c
index f1df9837f1ac..2ce9dc8a843b 100644
--- a/net/sunrpc/auth_generic.c
+++ b/net/sunrpc/auth_generic.c
@@ -270,6 +270,7 @@ static const struct rpc_authops generic_auth_ops = {
 	.lookup_cred = generic_lookup_cred,
 	.crcreate = generic_create_cred,
 	.key_timeout = generic_key_timeout,
+	.user_ns = false,
 };
 
 static struct rpc_auth generic_auth = {
diff --git a/net/sunrpc/auth_gss/auth_gss.c b/net/sunrpc/auth_gss/auth_gss.c
index be8f103d22fd..34ec2770c71c 100644
--- a/net/sunrpc/auth_gss/auth_gss.c
+++ b/net/sunrpc/auth_gss/auth_gss.c
@@ -985,7 +985,7 @@ static void gss_pipe_free(struct gss_pipe *p)
  * parameters based on the input flavor (which must be a pseudoflavor)
  */
 static struct gss_auth *
-gss_create_new(struct rpc_auth_create_args *args, struct rpc_clnt *clnt)
+gss_create_new(const struct rpc_auth_create_args *args, struct rpc_clnt *clnt)
 {
 	rpc_authflavor_t flavor = args->pseudoflavor;
 	struct gss_auth *gss_auth;
@@ -1132,7 +1132,7 @@ gss_destroy(struct rpc_auth *auth)
  * (which is guaranteed to last as long as any of its descendants).
  */
 static struct gss_auth *
-gss_auth_find_or_add_hashed(struct rpc_auth_create_args *args,
+gss_auth_find_or_add_hashed(const struct rpc_auth_create_args *args,
 		struct rpc_clnt *clnt,
 		struct gss_auth *new)
 {
@@ -1169,7 +1169,8 @@ gss_auth_find_or_add_hashed(struct rpc_auth_create_args *args,
 }
 
 static struct gss_auth *
-gss_create_hashed(struct rpc_auth_create_args *args, struct rpc_clnt *clnt)
+gss_create_hashed(const struct rpc_auth_create_args *args,
+		  struct rpc_clnt *clnt)
 {
 	struct gss_auth *gss_auth;
 	struct gss_auth *new;
@@ -1188,7 +1189,7 @@ gss_create_hashed(struct rpc_auth_create_args *args, struct rpc_clnt *clnt)
 }
 
 static struct rpc_auth *
-gss_create(struct rpc_auth_create_args *args, struct rpc_clnt *clnt)
+gss_create(const struct rpc_auth_create_args *args, struct rpc_clnt *clnt)
 {
 	struct gss_auth *gss_auth;
 	struct rpc_xprt_switch *xps = rcu_access_pointer(clnt->cl_xpi.xpi_xpswitch);
@@ -2005,6 +2006,7 @@ static const struct rpc_authops authgss_ops = {
 	.list_pseudoflavors = gss_mech_list_pseudoflavors,
 	.info2flavor	= gss_mech_info2flavor,
 	.flavor2info	= gss_mech_flavor2info,
+	.user_ns	= false,
 };
 
 static const struct rpc_credops gss_credops = {
diff --git a/net/sunrpc/auth_null.c b/net/sunrpc/auth_null.c
index 75d72e109a04..a2743bfc79f9 100644
--- a/net/sunrpc/auth_null.c
+++ b/net/sunrpc/auth_null.c
@@ -19,7 +19,7 @@ static struct rpc_auth null_auth;
 static struct rpc_cred null_cred;
 
 static struct rpc_auth *
-nul_create(struct rpc_auth_create_args *args, struct rpc_clnt *clnt)
+nul_create(const struct rpc_auth_create_args *args, struct rpc_clnt *clnt)
 {
 	atomic_inc(&null_auth.au_count);
 	return &null_auth;
@@ -110,6 +110,7 @@ const struct rpc_authops authnull_ops = {
 	.create		= nul_create,
 	.destroy	= nul_destroy,
 	.lookup_cred	= nul_lookup_cred,
+	.user_ns	= true,
 };
 
 static
diff --git a/net/sunrpc/auth_unix.c b/net/sunrpc/auth_unix.c
index dafd6b870ba3..9935e878aac0 100644
--- a/net/sunrpc/auth_unix.c
+++ b/net/sunrpc/auth_unix.c
@@ -15,10 +15,16 @@
 #include <linux/sunrpc/auth.h>
 #include <linux/user_namespace.h>
 
+struct unix_auth {
+	struct rpc_auth		rpc_auth;
+	struct user_namespace	*user_ns;
+};
+
 struct unx_cred {
 	struct rpc_cred		uc_base;
 	kgid_t			uc_gid;
 	kgid_t			uc_gids[UNX_NGROUPS];
+	struct user_namespace	*user_ns;
 };
 #define uc_uid			uc_base.cr_uid
 
@@ -26,31 +32,71 @@ struct unx_cred {
 # define RPCDBG_FACILITY	RPCDBG_AUTH
 #endif
 
-static struct rpc_auth		unix_auth;
 static const struct rpc_credops	unix_credops;
 
 static struct rpc_auth *
-unx_create(struct rpc_auth_create_args *args, struct rpc_clnt *clnt)
+unx_create(const struct rpc_auth_create_args *args, struct rpc_clnt *clnt)
 {
+	struct unix_auth *unix_auth;
+	struct rpc_auth *auth;
+	int err = -ENOMEM;
+
 	dprintk("RPC:       creating UNIX authenticator for client %p\n",
 			clnt);
-	atomic_inc(&unix_auth.au_count);
-	return &unix_auth;
+	if (!try_module_get(THIS_MODULE))
+		return ERR_PTR(-EINVAL);
+	unix_auth = kmalloc(sizeof(*unix_auth), GFP_KERNEL);
+	if (!unix_auth)
+		goto error;
+
+	unix_auth->user_ns = get_user_ns(args->user_ns);
+	auth = &unix_auth->rpc_auth;
+
+	auth->au_cslack = UNX_CALLSLACK;
+	auth->au_rslack = NUL_REPLYSLACK,
+	auth->au_flags = RPCAUTH_AUTH_NO_CRKEY_TIMEOUT,
+	auth->au_ops = &authunix_ops,
+	auth->au_flavor = RPC_AUTH_UNIX;
+	atomic_set(&unix_auth->rpc_auth.au_count, 1);
+
+	err = rpcauth_init_credcache(auth);
+	if (err)
+		goto error_free_auth;
+
+	return auth;
+
+error_free_auth:
+	put_user_ns(unix_auth->user_ns);
+	kfree(unix_auth);
+error:
+	module_put(THIS_MODULE);
+	return ERR_PTR(err);
 }
 
 static void
 unx_destroy(struct rpc_auth *auth)
 {
+	struct unix_auth *unix_auth;
+
+	unix_auth = container_of(auth, struct unix_auth, rpc_auth);
 	dprintk("RPC:       destroying UNIX authenticator %p\n", auth);
-	rpcauth_clear_credcache(auth->au_credcache);
+	rpcauth_destroy_credcache(auth);
+	put_user_ns(unix_auth->user_ns);
+	kfree(unix_auth);
+	module_put(THIS_MODULE);
 }
 
 static int
 unx_hash_cred(struct auth_cred *acred, unsigned int hashbits)
 {
-	return hash_64(from_kgid(&init_user_ns, acred->gid) |
-		((u64)from_kuid(&init_user_ns, acred->uid) <<
-			(sizeof(gid_t) * 8)), hashbits);
+	/*
+	 * No need to convert this based on the user namespace, because
+	 * the cred cache is only scoped to the unix_auth instances
+	 */
+	uid_t uid = __kuid_val(acred->uid);
+	gid_t gid = __kgid_val(acred->gid);
+
+	return hash_64(gid | ((u64)uid << (sizeof(gid_t) * 8)), hashbits);
 }
 
 /*
@@ -65,19 +111,22 @@ unx_lookup_cred(struct rpc_auth *auth, struct auth_cred *acred, int flags)
 static struct rpc_cred *
 unx_create_cred(struct rpc_auth *auth, struct auth_cred *acred, int flags, gfp_t gfp)
 {
+	struct unix_auth *unix_auth;
 	struct unx_cred	*cred;
 	unsigned int groups = 0;
 	unsigned int i;
 
+	unix_auth = container_of(auth, struct unix_auth, rpc_auth);
 	dprintk("RPC:       allocating UNIX cred for uid %d gid %d\n",
-			from_kuid(&init_user_ns, acred->uid),
-			from_kgid(&init_user_ns, acred->gid));
+			from_kuid(unix_auth->user_ns, acred->uid),
+			from_kgid(unix_auth->user_ns, acred->gid));
 
 	if (!(cred = kmalloc(sizeof(*cred), gfp)))
 		return ERR_PTR(-ENOMEM);
 
 	rpcauth_init_cred(&cred->uc_base, acred, auth, &unix_credops);
 	cred->uc_base.cr_flags = 1UL << RPCAUTH_CRED_UPTODATE;
+	cred->user_ns = get_user_ns(unix_auth->user_ns);
 
 	if (acred->group_info != NULL)
 		groups = acred->group_info->ngroups;
@@ -97,6 +146,7 @@ static void
 unx_free_cred(struct unx_cred *unx_cred)
 {
 	dprintk("RPC:       unx_free_cred %p\n", unx_cred);
+	put_user_ns(unx_cred->user_ns);
 	kfree(unx_cred);
 }
 
@@ -162,11 +212,11 @@ unx_marshal(struct rpc_task *task, __be32 *p)
 	 */
 	p = xdr_encode_array(p, clnt->cl_nodename, clnt->cl_nodelen);
 
-	*p++ = htonl((u32) from_kuid(&init_user_ns, cred->uc_uid));
-	*p++ = htonl((u32) from_kgid(&init_user_ns, cred->uc_gid));
+	*p++ = htonl((u32)from_kuid(cred->user_ns, cred->uc_uid));
+	*p++ = htonl((u32)from_kgid(cred->user_ns, cred->uc_gid));
 	hold = p++;
 	for (i = 0; i < UNX_NGROUPS && gid_valid(cred->uc_gids[i]); i++)
-		*p++ = htonl((u32) from_kgid(&init_user_ns, cred->uc_gids[i]));
+		*p++ = htonl((u32)from_kgid(cred->user_ns, cred->uc_gids[i]));
 	*hold = htonl(p - hold - 1);		/* gid array length */
 	*base = htonl((p - base - 1) << 2);	/* cred length */
 
@@ -211,16 +261,6 @@ unx_validate(struct rpc_task *task, __be32 *p)
 	return p;
 }
 
-int __init rpc_init_authunix(void)
-{
-	return rpcauth_init_credcache(&unix_auth);
-}
-
-void rpc_destroy_authunix(void)
-{
-	rpcauth_destroy_credcache(&unix_auth);
-}
-
 const struct rpc_authops authunix_ops = {
 	.owner		= THIS_MODULE,
 	.au_flavor	= RPC_AUTH_UNIX,
@@ -230,16 +270,7 @@ const struct rpc_authops authunix_ops = {
 	.hash_cred	= unx_hash_cred,
 	.lookup_cred	= unx_lookup_cred,
 	.crcreate	= unx_create_cred,
-};
-
-static
-struct rpc_auth		unix_auth = {
-	.au_cslack	= UNX_CALLSLACK,
-	.au_rslack	= NUL_REPLYSLACK,
-	.au_flags	= RPCAUTH_AUTH_NO_CRKEY_TIMEOUT,
-	.au_ops		= &authunix_ops,
-	.au_flavor	= RPC_AUTH_UNIX,
-	.au_count	= ATOMIC_INIT(0),
+	.user_ns	= false,
 };
 
 static
diff --git a/net/sunrpc/clnt.c b/net/sunrpc/clnt.c
index d839c33ae7d9..33d4c18060e4 100644
--- a/net/sunrpc/clnt.c
+++ b/net/sunrpc/clnt.c
@@ -294,8 +294,9 @@ static int rpc_client_register(struct rpc_clnt *clnt,
 			       const char *client_name)
 {
 	struct rpc_auth_create_args auth_args = {
-		.pseudoflavor = pseudoflavor,
-		.target_name = client_name,
+		.pseudoflavor	= pseudoflavor,
+		.target_name	= client_name,
+		.user_ns	= &init_user_ns,
 	};
 	struct rpc_auth *auth;
 	struct net *net = rpc_net_ns(clnt);
-- 
2.14.1




[Index of Archives]     [Linux Ext4 Filesystem]     [Union Filesystem]     [Filesystem Testing]     [Ceph Users]     [Ecryptfs]     [AutoFS]     [Kernel Newbies]     [Share Photos]     [Security]     [Netfilter]     [Bugtraq]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux Cachefs]     [Reiser Filesystem]     [Linux RAID]     [Samba]     [Device Mapper]     [CEPH Development]

  Powered by Linux