diff options
Diffstat (limited to 'fs/nfsd/nfs4idmap.c')
| -rw-r--r-- | fs/nfsd/nfs4idmap.c | 257 | 
1 files changed, 170 insertions, 87 deletions
diff --git a/fs/nfsd/nfs4idmap.c b/fs/nfsd/nfs4idmap.c index f0695e815f0..a0ab0a847d6 100644 --- a/fs/nfsd/nfs4idmap.c +++ b/fs/nfsd/nfs4idmap.c @@ -33,10 +33,22 @@   */  #include <linux/module.h> -#include <linux/nfsd_idmap.h>  #include <linux/seq_file.h>  #include <linux/sched.h>  #include <linux/slab.h> +#include <linux/sunrpc/svc_xprt.h> +#include <net/net_namespace.h> +#include "idmap.h" +#include "nfsd.h" +#include "netns.h" + +/* + * Turn off idmapping when using AUTH_SYS. + */ +static bool nfs4_disable_idmapping = true; +module_param(nfs4_disable_idmapping, bool, 0644); +MODULE_PARM_DESC(nfs4_disable_idmapping, +		"Turn off server's NFSv4 idmapping when using 'sec=sys'");  /*   * Cache entry @@ -53,7 +65,7 @@  struct ent {  	struct cache_head h;  	int               type;		       /* User / Group */ -	uid_t             id; +	u32               id;  	char              name[IDMAP_NAMESZ];  	char              authname[IDMAP_NAMESZ];  }; @@ -62,7 +74,6 @@ struct ent {  #define ENT_HASHBITS          8  #define ENT_HASHMAX           (1 << ENT_HASHBITS) -#define ENT_HASHMASK          (ENT_HASHMAX - 1)  static void  ent_init(struct cache_head *cnew, struct cache_head *citm) @@ -98,8 +109,6 @@ ent_alloc(void)   * ID -> Name cache   */ -static struct cache_head *idtoname_table[ENT_HASHMAX]; -  static uint32_t  idtoname_hash(struct ent *ent)  { @@ -131,12 +140,6 @@ idtoname_request(struct cache_detail *cd, struct cache_head *ch, char **bpp,  }  static int -idtoname_upcall(struct cache_detail *cd, struct cache_head *ch) -{ -	return sunrpc_cache_pipe_upcall(cd, ch, idtoname_request); -} - -static int  idtoname_match(struct cache_head *ca, struct cache_head *cb)  {  	struct ent *a = container_of(ca, struct ent, h); @@ -174,16 +177,16 @@ warn_no_idmapd(struct cache_detail *detail, int has_died)  static int         idtoname_parse(struct cache_detail *, char *, int); -static struct ent *idtoname_lookup(struct ent *); -static struct ent *idtoname_update(struct ent *, struct ent *); +static struct ent *idtoname_lookup(struct cache_detail *, struct ent *); +static struct ent *idtoname_update(struct cache_detail *, struct ent *, +				   struct ent *); -static struct cache_detail idtoname_cache = { +static struct cache_detail idtoname_cache_template = {  	.owner		= THIS_MODULE,  	.hash_size	= ENT_HASHMAX, -	.hash_table	= idtoname_table,  	.name		= "nfs4.idtoname",  	.cache_put	= ent_put, -	.cache_upcall	= idtoname_upcall, +	.cache_request	= idtoname_request,  	.cache_parse	= idtoname_parse,  	.cache_show	= idtoname_show,  	.warn_no_listener = warn_no_idmapd, @@ -235,7 +238,7 @@ idtoname_parse(struct cache_detail *cd, char *buf, int buflen)  		goto out;  	error = -ENOMEM; -	res = idtoname_lookup(&ent); +	res = idtoname_lookup(cd, &ent);  	if (!res)  		goto out; @@ -251,11 +254,11 @@ idtoname_parse(struct cache_detail *cd, char *buf, int buflen)  	else  		memcpy(ent.name, buf1, sizeof(ent.name));  	error = -ENOMEM; -	res = idtoname_update(&ent, res); +	res = idtoname_update(cd, &ent, res);  	if (res == NULL)  		goto out; -	cache_put(&res->h, &idtoname_cache); +	cache_put(&res->h, cd);  	error = 0;  out: @@ -266,10 +269,9 @@ out:  static struct ent * -idtoname_lookup(struct ent *item) +idtoname_lookup(struct cache_detail *cd, struct ent *item)  { -	struct cache_head *ch = sunrpc_cache_lookup(&idtoname_cache, -						    &item->h, +	struct cache_head *ch = sunrpc_cache_lookup(cd, &item->h,  						    idtoname_hash(item));  	if (ch)  		return container_of(ch, struct ent, h); @@ -278,10 +280,9 @@ idtoname_lookup(struct ent *item)  }  static struct ent * -idtoname_update(struct ent *new, struct ent *old) +idtoname_update(struct cache_detail *cd, struct ent *new, struct ent *old)  { -	struct cache_head *ch = sunrpc_cache_update(&idtoname_cache, -						    &new->h, &old->h, +	struct cache_head *ch = sunrpc_cache_update(cd, &new->h, &old->h,  						    idtoname_hash(new));  	if (ch)  		return container_of(ch, struct ent, h); @@ -294,8 +295,6 @@ idtoname_update(struct ent *new, struct ent *old)   * Name -> ID cache   */ -static struct cache_head *nametoid_table[ENT_HASHMAX]; -  static inline int  nametoid_hash(struct ent *ent)  { @@ -316,12 +315,6 @@ nametoid_request(struct cache_detail *cd, struct cache_head *ch, char **bpp,  }  static int -nametoid_upcall(struct cache_detail *cd, struct cache_head *ch) -{ -	return sunrpc_cache_pipe_upcall(cd, ch, nametoid_request); -} - -static int  nametoid_match(struct cache_head *ca, struct cache_head *cb)  {  	struct ent *a = container_of(ca, struct ent, h); @@ -350,17 +343,17 @@ nametoid_show(struct seq_file *m, struct cache_detail *cd, struct cache_head *h)  	return 0;  } -static struct ent *nametoid_lookup(struct ent *); -static struct ent *nametoid_update(struct ent *, struct ent *); +static struct ent *nametoid_lookup(struct cache_detail *, struct ent *); +static struct ent *nametoid_update(struct cache_detail *, struct ent *, +				   struct ent *);  static int         nametoid_parse(struct cache_detail *, char *, int); -static struct cache_detail nametoid_cache = { +static struct cache_detail nametoid_cache_template = {  	.owner		= THIS_MODULE,  	.hash_size	= ENT_HASHMAX, -	.hash_table	= nametoid_table,  	.name		= "nfs4.nametoid",  	.cache_put	= ent_put, -	.cache_upcall	= nametoid_upcall, +	.cache_request	= nametoid_request,  	.cache_parse	= nametoid_parse,  	.cache_show	= nametoid_show,  	.warn_no_listener = warn_no_idmapd, @@ -417,14 +410,14 @@ nametoid_parse(struct cache_detail *cd, char *buf, int buflen)  		set_bit(CACHE_NEGATIVE, &ent.h.flags);  	error = -ENOMEM; -	res = nametoid_lookup(&ent); +	res = nametoid_lookup(cd, &ent);  	if (res == NULL)  		goto out; -	res = nametoid_update(&ent, res); +	res = nametoid_update(cd, &ent, res);  	if (res == NULL)  		goto out; -	cache_put(&res->h, &nametoid_cache); +	cache_put(&res->h, cd);  	error = 0;  out:  	kfree(buf1); @@ -434,10 +427,9 @@ out:  static struct ent * -nametoid_lookup(struct ent *item) +nametoid_lookup(struct cache_detail *cd, struct ent *item)  { -	struct cache_head *ch = sunrpc_cache_lookup(&nametoid_cache, -						    &item->h, +	struct cache_head *ch = sunrpc_cache_lookup(cd, &item->h,  						    nametoid_hash(item));  	if (ch)  		return container_of(ch, struct ent, h); @@ -446,10 +438,9 @@ nametoid_lookup(struct ent *item)  }  static struct ent * -nametoid_update(struct ent *new, struct ent *old) +nametoid_update(struct cache_detail *cd, struct ent *new, struct ent *old)  { -	struct cache_head *ch = sunrpc_cache_update(&nametoid_cache, -						    &new->h, &old->h, +	struct cache_head *ch = sunrpc_cache_update(cd, &new->h, &old->h,  						    nametoid_hash(new));  	if (ch)  		return container_of(ch, struct ent, h); @@ -462,34 +453,55 @@ nametoid_update(struct ent *new, struct ent *old)   */  int -nfsd_idmap_init(void) +nfsd_idmap_init(struct net *net)  {  	int rv; +	struct nfsd_net *nn = net_generic(net, nfsd_net_id); -	rv = cache_register(&idtoname_cache); +	nn->idtoname_cache = cache_create_net(&idtoname_cache_template, net); +	if (IS_ERR(nn->idtoname_cache)) +		return PTR_ERR(nn->idtoname_cache); +	rv = cache_register_net(nn->idtoname_cache, net);  	if (rv) -		return rv; -	rv = cache_register(&nametoid_cache); +		goto destroy_idtoname_cache; +	nn->nametoid_cache = cache_create_net(&nametoid_cache_template, net); +	if (IS_ERR(nn->nametoid_cache)) { +		rv = PTR_ERR(nn->nametoid_cache); +		goto unregister_idtoname_cache; +	} +	rv = cache_register_net(nn->nametoid_cache, net);  	if (rv) -		cache_unregister(&idtoname_cache); +		goto destroy_nametoid_cache; +	return 0; + +destroy_nametoid_cache: +	cache_destroy_net(nn->nametoid_cache, net); +unregister_idtoname_cache: +	cache_unregister_net(nn->idtoname_cache, net); +destroy_idtoname_cache: +	cache_destroy_net(nn->idtoname_cache, net);  	return rv;  }  void -nfsd_idmap_shutdown(void) +nfsd_idmap_shutdown(struct net *net)  { -	cache_unregister(&idtoname_cache); -	cache_unregister(&nametoid_cache); +	struct nfsd_net *nn = net_generic(net, nfsd_net_id); + +	cache_unregister_net(nn->idtoname_cache, net); +	cache_unregister_net(nn->nametoid_cache, net); +	cache_destroy_net(nn->idtoname_cache, net); +	cache_destroy_net(nn->nametoid_cache, net);  }  static int  idmap_lookup(struct svc_rqst *rqstp, -		struct ent *(*lookup_fn)(struct ent *), struct ent *key, -		struct cache_detail *detail, struct ent **item) +		struct ent *(*lookup_fn)(struct cache_detail *, struct ent *), +		struct ent *key, struct cache_detail *detail, struct ent **item)  {  	int ret; -	*item = lookup_fn(key); +	*item = lookup_fn(detail, key);  	if (!*item)  		return -ENOMEM;   retry: @@ -497,7 +509,7 @@ idmap_lookup(struct svc_rqst *rqstp,  	if (ret == -ETIMEDOUT) {  		struct ent *prev_item = *item; -		*item = lookup_fn(key); +		*item = lookup_fn(detail, key);  		if (*item != prev_item)  			goto retry;  		cache_put(&(*item)->h, detail); @@ -514,74 +526,145 @@ rqst_authname(struct svc_rqst *rqstp)  	return clp->name;  } -static int +static __be32  idmap_name_to_id(struct svc_rqst *rqstp, int type, const char *name, u32 namelen, -		uid_t *id) +		u32 *id)  {  	struct ent *item, key = {  		.type = type,  	};  	int ret; +	struct nfsd_net *nn = net_generic(SVC_NET(rqstp), nfsd_net_id);  	if (namelen + 1 > sizeof(key.name)) -		return -EINVAL; +		return nfserr_badowner;  	memcpy(key.name, name, namelen);  	key.name[namelen] = '\0';  	strlcpy(key.authname, rqst_authname(rqstp), sizeof(key.authname)); -	ret = idmap_lookup(rqstp, nametoid_lookup, &key, &nametoid_cache, &item); +	ret = idmap_lookup(rqstp, nametoid_lookup, &key, nn->nametoid_cache, &item);  	if (ret == -ENOENT) -		ret = -ESRCH; /* nfserr_badname */ +		return nfserr_badowner;  	if (ret) -		return ret; +		return nfserrno(ret);  	*id = item->id; -	cache_put(&item->h, &nametoid_cache); +	cache_put(&item->h, nn->nametoid_cache);  	return 0;  } -static int -idmap_id_to_name(struct svc_rqst *rqstp, int type, uid_t id, char *name) +static __be32 encode_ascii_id(struct xdr_stream *xdr, u32 id) +{ +	char buf[11]; +	int len; +	__be32 *p; + +	len = sprintf(buf, "%u", id); +	p = xdr_reserve_space(xdr, len + 4); +	if (!p) +		return nfserr_resource; +	p = xdr_encode_opaque(p, buf, len); +	return 0; +} + +static __be32 idmap_id_to_name(struct xdr_stream *xdr, +			       struct svc_rqst *rqstp, int type, u32 id)  {  	struct ent *item, key = {  		.id = id,  		.type = type,  	}; +	__be32 *p;  	int ret; +	struct nfsd_net *nn = net_generic(SVC_NET(rqstp), nfsd_net_id);  	strlcpy(key.authname, rqst_authname(rqstp), sizeof(key.authname)); -	ret = idmap_lookup(rqstp, idtoname_lookup, &key, &idtoname_cache, &item); +	ret = idmap_lookup(rqstp, idtoname_lookup, &key, nn->idtoname_cache, &item);  	if (ret == -ENOENT) -		return sprintf(name, "%u", id); +		return encode_ascii_id(xdr, id);  	if (ret) -		return ret; +		return nfserrno(ret);  	ret = strlen(item->name); -	BUG_ON(ret > IDMAP_NAMESZ); -	memcpy(name, item->name, ret); -	cache_put(&item->h, &idtoname_cache); -	return ret; +	WARN_ON_ONCE(ret > IDMAP_NAMESZ); +	p = xdr_reserve_space(xdr, ret + 4); +	if (!p) +		return nfserr_resource; +	p = xdr_encode_opaque(p, item->name, ret); +	cache_put(&item->h, nn->idtoname_cache); +	return 0;  } -int +static bool +numeric_name_to_id(struct svc_rqst *rqstp, int type, const char *name, u32 namelen, u32 *id) +{ +	int ret; +	char buf[11]; + +	if (namelen + 1 > sizeof(buf)) +		/* too long to represent a 32-bit id: */ +		return false; +	/* Just to make sure it's null-terminated: */ +	memcpy(buf, name, namelen); +	buf[namelen] = '\0'; +	ret = kstrtouint(buf, 10, id); +	return ret == 0; +} + +static __be32 +do_name_to_id(struct svc_rqst *rqstp, int type, const char *name, u32 namelen, u32 *id) +{ +	if (nfs4_disable_idmapping && rqstp->rq_cred.cr_flavor < RPC_AUTH_GSS) +		if (numeric_name_to_id(rqstp, type, name, namelen, id)) +			return 0; +		/* +		 * otherwise, fall through and try idmapping, for +		 * backwards compatibility with clients sending names: +		 */ +	return idmap_name_to_id(rqstp, type, name, namelen, id); +} + +static __be32 encode_name_from_id(struct xdr_stream *xdr, +				  struct svc_rqst *rqstp, int type, u32 id) +{ +	if (nfs4_disable_idmapping && rqstp->rq_cred.cr_flavor < RPC_AUTH_GSS) +		return encode_ascii_id(xdr, id); +	return idmap_id_to_name(xdr, rqstp, type, id); +} + +__be32  nfsd_map_name_to_uid(struct svc_rqst *rqstp, const char *name, size_t namelen, -		__u32 *id) +		kuid_t *uid)  { -	return idmap_name_to_id(rqstp, IDMAP_TYPE_USER, name, namelen, id); +	__be32 status; +	u32 id = -1; +	status = do_name_to_id(rqstp, IDMAP_TYPE_USER, name, namelen, &id); +	*uid = make_kuid(&init_user_ns, id); +	if (!uid_valid(*uid)) +		status = nfserr_badowner; +	return status;  } -int +__be32  nfsd_map_name_to_gid(struct svc_rqst *rqstp, const char *name, size_t namelen, -		__u32 *id) +		kgid_t *gid)  { -	return idmap_name_to_id(rqstp, IDMAP_TYPE_GROUP, name, namelen, id); +	__be32 status; +	u32 id = -1; +	status = do_name_to_id(rqstp, IDMAP_TYPE_GROUP, name, namelen, &id); +	*gid = make_kgid(&init_user_ns, id); +	if (!gid_valid(*gid)) +		status = nfserr_badowner; +	return status;  } -int -nfsd_map_uid_to_name(struct svc_rqst *rqstp, __u32 id, char *name) +__be32 nfsd4_encode_user(struct xdr_stream *xdr, struct svc_rqst *rqstp, +			 kuid_t uid)  { -	return idmap_id_to_name(rqstp, IDMAP_TYPE_USER, id, name); +	u32 id = from_kuid(&init_user_ns, uid); +	return encode_name_from_id(xdr, rqstp, IDMAP_TYPE_USER, id);  } -int -nfsd_map_gid_to_name(struct svc_rqst *rqstp, __u32 id, char *name) +__be32 nfsd4_encode_group(struct xdr_stream *xdr, struct svc_rqst *rqstp, +			  kgid_t gid)  { -	return idmap_id_to_name(rqstp, IDMAP_TYPE_GROUP, id, name); +	u32 id = from_kgid(&init_user_ns, gid); +	return encode_name_from_id(xdr, rqstp, IDMAP_TYPE_GROUP, id);  }  | 
