xref: /openbmc/linux/fs/nfs/nfs4idmap.c (revision 5a17f040)
17b320382SAnna Schumaker /*
27b320382SAnna Schumaker  * fs/nfs/idmap.c
37b320382SAnna Schumaker  *
47b320382SAnna Schumaker  *  UID and GID to name mapping for clients.
57b320382SAnna Schumaker  *
67b320382SAnna Schumaker  *  Copyright (c) 2002 The Regents of the University of Michigan.
77b320382SAnna Schumaker  *  All rights reserved.
87b320382SAnna Schumaker  *
97b320382SAnna Schumaker  *  Marius Aamodt Eriksen <marius@umich.edu>
107b320382SAnna Schumaker  *
117b320382SAnna Schumaker  *  Redistribution and use in source and binary forms, with or without
127b320382SAnna Schumaker  *  modification, are permitted provided that the following conditions
137b320382SAnna Schumaker  *  are met:
147b320382SAnna Schumaker  *
157b320382SAnna Schumaker  *  1. Redistributions of source code must retain the above copyright
167b320382SAnna Schumaker  *     notice, this list of conditions and the following disclaimer.
177b320382SAnna Schumaker  *  2. Redistributions in binary form must reproduce the above copyright
187b320382SAnna Schumaker  *     notice, this list of conditions and the following disclaimer in the
197b320382SAnna Schumaker  *     documentation and/or other materials provided with the distribution.
207b320382SAnna Schumaker  *  3. Neither the name of the University nor the names of its
217b320382SAnna Schumaker  *     contributors may be used to endorse or promote products derived
227b320382SAnna Schumaker  *     from this software without specific prior written permission.
237b320382SAnna Schumaker  *
247b320382SAnna Schumaker  *  THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
257b320382SAnna Schumaker  *  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
267b320382SAnna Schumaker  *  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
277b320382SAnna Schumaker  *  DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
287b320382SAnna Schumaker  *  FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
297b320382SAnna Schumaker  *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
307b320382SAnna Schumaker  *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
317b320382SAnna Schumaker  *  BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
327b320382SAnna Schumaker  *  LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
337b320382SAnna Schumaker  *  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
347b320382SAnna Schumaker  *  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
357b320382SAnna Schumaker  */
367b320382SAnna Schumaker #include <linux/types.h>
377b320382SAnna Schumaker #include <linux/parser.h>
387b320382SAnna Schumaker #include <linux/fs.h>
397b320382SAnna Schumaker #include <net/net_namespace.h>
407b320382SAnna Schumaker #include <linux/sunrpc/rpc_pipe_fs.h>
417b320382SAnna Schumaker #include <linux/nfs_fs.h>
427b320382SAnna Schumaker #include <linux/nfs_fs_sb.h>
437b320382SAnna Schumaker #include <linux/key.h>
447b320382SAnna Schumaker #include <linux/keyctl.h>
457b320382SAnna Schumaker #include <linux/key-type.h>
467b320382SAnna Schumaker #include <keys/user-type.h>
47822ad64dSDavid Howells #include <keys/request_key_auth-type.h>
487b320382SAnna Schumaker #include <linux/module.h>
4961ca2c4aSSargun Dhillon #include <linux/user_namespace.h>
507b320382SAnna Schumaker 
517b320382SAnna Schumaker #include "internal.h"
527b320382SAnna Schumaker #include "netns.h"
537b320382SAnna Schumaker #include "nfs4idmap.h"
547b320382SAnna Schumaker #include "nfs4trace.h"
557b320382SAnna Schumaker 
567b320382SAnna Schumaker #define NFS_UINT_MAXLEN 11
577b320382SAnna Schumaker 
587b320382SAnna Schumaker static const struct cred *id_resolver_cache;
597b320382SAnna Schumaker static struct key_type key_type_id_resolver_legacy;
607b320382SAnna Schumaker 
617b320382SAnna Schumaker struct idmap_legacy_upcalldata {
627b320382SAnna Schumaker 	struct rpc_pipe_msg pipe_msg;
637b320382SAnna Schumaker 	struct idmap_msg idmap_msg;
64822ad64dSDavid Howells 	struct key	*authkey;
657b320382SAnna Schumaker 	struct idmap *idmap;
667b320382SAnna Schumaker };
677b320382SAnna Schumaker 
687b320382SAnna Schumaker struct idmap {
697b320382SAnna Schumaker 	struct rpc_pipe_dir_object idmap_pdo;
707b320382SAnna Schumaker 	struct rpc_pipe		*idmap_pipe;
717b320382SAnna Schumaker 	struct idmap_legacy_upcalldata *idmap_upcall_data;
727b320382SAnna Schumaker 	struct mutex		idmap_mutex;
7361ca2c4aSSargun Dhillon 	struct user_namespace	*user_ns;
747b320382SAnna Schumaker };
757b320382SAnna Schumaker 
idmap_userns(const struct idmap * idmap)7658002399STrond Myklebust static struct user_namespace *idmap_userns(const struct idmap *idmap)
7758002399STrond Myklebust {
7861ca2c4aSSargun Dhillon 	if (idmap && idmap->user_ns)
7961ca2c4aSSargun Dhillon 		return idmap->user_ns;
8058002399STrond Myklebust 	return &init_user_ns;
8158002399STrond Myklebust }
8258002399STrond Myklebust 
837b320382SAnna Schumaker /**
847b320382SAnna Schumaker  * nfs_fattr_init_names - initialise the nfs_fattr owner_name/group_name fields
857b320382SAnna Schumaker  * @fattr: fully initialised struct nfs_fattr
867b320382SAnna Schumaker  * @owner_name: owner name string cache
877b320382SAnna Schumaker  * @group_name: group name string cache
887b320382SAnna Schumaker  */
nfs_fattr_init_names(struct nfs_fattr * fattr,struct nfs4_string * owner_name,struct nfs4_string * group_name)897b320382SAnna Schumaker void nfs_fattr_init_names(struct nfs_fattr *fattr,
907b320382SAnna Schumaker 		struct nfs4_string *owner_name,
917b320382SAnna Schumaker 		struct nfs4_string *group_name)
927b320382SAnna Schumaker {
937b320382SAnna Schumaker 	fattr->owner_name = owner_name;
947b320382SAnna Schumaker 	fattr->group_name = group_name;
957b320382SAnna Schumaker }
967b320382SAnna Schumaker 
nfs_fattr_free_owner_name(struct nfs_fattr * fattr)977b320382SAnna Schumaker static void nfs_fattr_free_owner_name(struct nfs_fattr *fattr)
987b320382SAnna Schumaker {
997b320382SAnna Schumaker 	fattr->valid &= ~NFS_ATTR_FATTR_OWNER_NAME;
1007b320382SAnna Schumaker 	kfree(fattr->owner_name->data);
1017b320382SAnna Schumaker }
1027b320382SAnna Schumaker 
nfs_fattr_free_group_name(struct nfs_fattr * fattr)1037b320382SAnna Schumaker static void nfs_fattr_free_group_name(struct nfs_fattr *fattr)
1047b320382SAnna Schumaker {
1057b320382SAnna Schumaker 	fattr->valid &= ~NFS_ATTR_FATTR_GROUP_NAME;
1067b320382SAnna Schumaker 	kfree(fattr->group_name->data);
1077b320382SAnna Schumaker }
1087b320382SAnna Schumaker 
nfs_fattr_map_owner_name(struct nfs_server * server,struct nfs_fattr * fattr)1097b320382SAnna Schumaker static bool nfs_fattr_map_owner_name(struct nfs_server *server, struct nfs_fattr *fattr)
1107b320382SAnna Schumaker {
1117b320382SAnna Schumaker 	struct nfs4_string *owner = fattr->owner_name;
1127b320382SAnna Schumaker 	kuid_t uid;
1137b320382SAnna Schumaker 
1147b320382SAnna Schumaker 	if (!(fattr->valid & NFS_ATTR_FATTR_OWNER_NAME))
1157b320382SAnna Schumaker 		return false;
1167b320382SAnna Schumaker 	if (nfs_map_name_to_uid(server, owner->data, owner->len, &uid) == 0) {
1177b320382SAnna Schumaker 		fattr->uid = uid;
1187b320382SAnna Schumaker 		fattr->valid |= NFS_ATTR_FATTR_OWNER;
1197b320382SAnna Schumaker 	}
1207b320382SAnna Schumaker 	return true;
1217b320382SAnna Schumaker }
1227b320382SAnna Schumaker 
nfs_fattr_map_group_name(struct nfs_server * server,struct nfs_fattr * fattr)1237b320382SAnna Schumaker static bool nfs_fattr_map_group_name(struct nfs_server *server, struct nfs_fattr *fattr)
1247b320382SAnna Schumaker {
1257b320382SAnna Schumaker 	struct nfs4_string *group = fattr->group_name;
1267b320382SAnna Schumaker 	kgid_t gid;
1277b320382SAnna Schumaker 
1287b320382SAnna Schumaker 	if (!(fattr->valid & NFS_ATTR_FATTR_GROUP_NAME))
1297b320382SAnna Schumaker 		return false;
1307b320382SAnna Schumaker 	if (nfs_map_group_to_gid(server, group->data, group->len, &gid) == 0) {
1317b320382SAnna Schumaker 		fattr->gid = gid;
1327b320382SAnna Schumaker 		fattr->valid |= NFS_ATTR_FATTR_GROUP;
1337b320382SAnna Schumaker 	}
1347b320382SAnna Schumaker 	return true;
1357b320382SAnna Schumaker }
1367b320382SAnna Schumaker 
1377b320382SAnna Schumaker /**
1387b320382SAnna Schumaker  * nfs_fattr_free_names - free up the NFSv4 owner and group strings
1397b320382SAnna Schumaker  * @fattr: a fully initialised nfs_fattr structure
1407b320382SAnna Schumaker  */
nfs_fattr_free_names(struct nfs_fattr * fattr)1417b320382SAnna Schumaker void nfs_fattr_free_names(struct nfs_fattr *fattr)
1427b320382SAnna Schumaker {
1437b320382SAnna Schumaker 	if (fattr->valid & NFS_ATTR_FATTR_OWNER_NAME)
1447b320382SAnna Schumaker 		nfs_fattr_free_owner_name(fattr);
1457b320382SAnna Schumaker 	if (fattr->valid & NFS_ATTR_FATTR_GROUP_NAME)
1467b320382SAnna Schumaker 		nfs_fattr_free_group_name(fattr);
1477b320382SAnna Schumaker }
1487b320382SAnna Schumaker 
1497b320382SAnna Schumaker /**
1507b320382SAnna Schumaker  * nfs_fattr_map_and_free_names - map owner/group strings into uid/gid and free
1517b320382SAnna Schumaker  * @server: pointer to the filesystem nfs_server structure
1527b320382SAnna Schumaker  * @fattr: a fully initialised nfs_fattr structure
1537b320382SAnna Schumaker  *
1547b320382SAnna Schumaker  * This helper maps the cached NFSv4 owner/group strings in fattr into
1557b320382SAnna Schumaker  * their numeric uid/gid equivalents, and then frees the cached strings.
1567b320382SAnna Schumaker  */
nfs_fattr_map_and_free_names(struct nfs_server * server,struct nfs_fattr * fattr)1577b320382SAnna Schumaker void nfs_fattr_map_and_free_names(struct nfs_server *server, struct nfs_fattr *fattr)
1587b320382SAnna Schumaker {
1597b320382SAnna Schumaker 	if (nfs_fattr_map_owner_name(server, fattr))
1607b320382SAnna Schumaker 		nfs_fattr_free_owner_name(fattr);
1617b320382SAnna Schumaker 	if (nfs_fattr_map_group_name(server, fattr))
1627b320382SAnna Schumaker 		nfs_fattr_free_group_name(fattr);
1637b320382SAnna Schumaker }
1647b320382SAnna Schumaker 
nfs_map_string_to_numeric(const char * name,size_t namelen,__u32 * res)1657b320382SAnna Schumaker int nfs_map_string_to_numeric(const char *name, size_t namelen, __u32 *res)
1667b320382SAnna Schumaker {
1677b320382SAnna Schumaker 	unsigned long val;
1687b320382SAnna Schumaker 	char buf[16];
1697b320382SAnna Schumaker 
1707b320382SAnna Schumaker 	if (memchr(name, '@', namelen) != NULL || namelen >= sizeof(buf))
1717b320382SAnna Schumaker 		return 0;
1727b320382SAnna Schumaker 	memcpy(buf, name, namelen);
1737b320382SAnna Schumaker 	buf[namelen] = '\0';
1747b320382SAnna Schumaker 	if (kstrtoul(buf, 0, &val) != 0)
1757b320382SAnna Schumaker 		return 0;
1767b320382SAnna Schumaker 	*res = val;
1777b320382SAnna Schumaker 	return 1;
1787b320382SAnna Schumaker }
1797b320382SAnna Schumaker EXPORT_SYMBOL_GPL(nfs_map_string_to_numeric);
1807b320382SAnna Schumaker 
nfs_map_numeric_to_string(__u32 id,char * buf,size_t buflen)1817b320382SAnna Schumaker static int nfs_map_numeric_to_string(__u32 id, char *buf, size_t buflen)
1827b320382SAnna Schumaker {
1837b320382SAnna Schumaker 	return snprintf(buf, buflen, "%u", id);
1847b320382SAnna Schumaker }
1857b320382SAnna Schumaker 
1867b320382SAnna Schumaker static struct key_type key_type_id_resolver = {
1877b320382SAnna Schumaker 	.name		= "id_resolver",
1887b320382SAnna Schumaker 	.preparse	= user_preparse,
1897b320382SAnna Schumaker 	.free_preparse	= user_free_preparse,
1907b320382SAnna Schumaker 	.instantiate	= generic_key_instantiate,
1917b320382SAnna Schumaker 	.revoke		= user_revoke,
1927b320382SAnna Schumaker 	.destroy	= user_destroy,
1937b320382SAnna Schumaker 	.describe	= user_describe,
1947b320382SAnna Schumaker 	.read		= user_read,
1957b320382SAnna Schumaker };
1967b320382SAnna Schumaker 
nfs_idmap_init(void)197fb2a525cSAnna Schumaker int nfs_idmap_init(void)
1987b320382SAnna Schumaker {
1997b320382SAnna Schumaker 	struct cred *cred;
2007b320382SAnna Schumaker 	struct key *keyring;
2017b320382SAnna Schumaker 	int ret = 0;
2027b320382SAnna Schumaker 
2037b320382SAnna Schumaker 	printk(KERN_NOTICE "NFS: Registering the %s key type\n",
2047b320382SAnna Schumaker 		key_type_id_resolver.name);
2057b320382SAnna Schumaker 
206*5a17f040SKees Cook 	cred = prepare_kernel_cred(&init_task);
2077b320382SAnna Schumaker 	if (!cred)
2087b320382SAnna Schumaker 		return -ENOMEM;
2097b320382SAnna Schumaker 
2107b320382SAnna Schumaker 	keyring = keyring_alloc(".id_resolver",
2117b320382SAnna Schumaker 				GLOBAL_ROOT_UID, GLOBAL_ROOT_GID, cred,
212028db3e2SLinus Torvalds 				(KEY_POS_ALL & ~KEY_POS_SETATTR) |
213028db3e2SLinus Torvalds 				KEY_USR_VIEW | KEY_USR_READ,
2145ac7eaceSDavid Howells 				KEY_ALLOC_NOT_IN_QUOTA, NULL, NULL);
2157b320382SAnna Schumaker 	if (IS_ERR(keyring)) {
2167b320382SAnna Schumaker 		ret = PTR_ERR(keyring);
2177b320382SAnna Schumaker 		goto failed_put_cred;
2187b320382SAnna Schumaker 	}
2197b320382SAnna Schumaker 
2207b320382SAnna Schumaker 	ret = register_key_type(&key_type_id_resolver);
2217b320382SAnna Schumaker 	if (ret < 0)
2227b320382SAnna Schumaker 		goto failed_put_key;
2237b320382SAnna Schumaker 
2247b320382SAnna Schumaker 	ret = register_key_type(&key_type_id_resolver_legacy);
2257b320382SAnna Schumaker 	if (ret < 0)
2267b320382SAnna Schumaker 		goto failed_reg_legacy;
2277b320382SAnna Schumaker 
2287b320382SAnna Schumaker 	set_bit(KEY_FLAG_ROOT_CAN_CLEAR, &keyring->flags);
2297b320382SAnna Schumaker 	cred->thread_keyring = keyring;
2307b320382SAnna Schumaker 	cred->jit_keyring = KEY_REQKEY_DEFL_THREAD_KEYRING;
2317b320382SAnna Schumaker 	id_resolver_cache = cred;
2327b320382SAnna Schumaker 	return 0;
2337b320382SAnna Schumaker 
2347b320382SAnna Schumaker failed_reg_legacy:
2357b320382SAnna Schumaker 	unregister_key_type(&key_type_id_resolver);
2367b320382SAnna Schumaker failed_put_key:
2377b320382SAnna Schumaker 	key_put(keyring);
2387b320382SAnna Schumaker failed_put_cred:
2397b320382SAnna Schumaker 	put_cred(cred);
2407b320382SAnna Schumaker 	return ret;
2417b320382SAnna Schumaker }
2427b320382SAnna Schumaker 
nfs_idmap_quit(void)243fb2a525cSAnna Schumaker void nfs_idmap_quit(void)
2447b320382SAnna Schumaker {
2457b320382SAnna Schumaker 	key_revoke(id_resolver_cache->thread_keyring);
2467b320382SAnna Schumaker 	unregister_key_type(&key_type_id_resolver);
2477b320382SAnna Schumaker 	unregister_key_type(&key_type_id_resolver_legacy);
2487b320382SAnna Schumaker 	put_cred(id_resolver_cache);
2497b320382SAnna Schumaker }
2507b320382SAnna Schumaker 
2517b320382SAnna Schumaker /*
2527b320382SAnna Schumaker  * Assemble the description to pass to request_key()
2537b320382SAnna Schumaker  * This function will allocate a new string and update dest to point
2547b320382SAnna Schumaker  * at it.  The caller is responsible for freeing dest.
2557b320382SAnna Schumaker  *
2567b320382SAnna Schumaker  * On error 0 is returned.  Otherwise, the length of dest is returned.
2577b320382SAnna Schumaker  */
nfs_idmap_get_desc(const char * name,size_t namelen,const char * type,size_t typelen,char ** desc)2587b320382SAnna Schumaker static ssize_t nfs_idmap_get_desc(const char *name, size_t namelen,
2597b320382SAnna Schumaker 				const char *type, size_t typelen, char **desc)
2607b320382SAnna Schumaker {
2617b320382SAnna Schumaker 	char *cp;
2627b320382SAnna Schumaker 	size_t desclen = typelen + namelen + 2;
2637b320382SAnna Schumaker 
2647b320382SAnna Schumaker 	*desc = kmalloc(desclen, GFP_KERNEL);
2657b320382SAnna Schumaker 	if (!*desc)
2667b320382SAnna Schumaker 		return -ENOMEM;
2677b320382SAnna Schumaker 
2687b320382SAnna Schumaker 	cp = *desc;
2697b320382SAnna Schumaker 	memcpy(cp, type, typelen);
2707b320382SAnna Schumaker 	cp += typelen;
2717b320382SAnna Schumaker 	*cp++ = ':';
2727b320382SAnna Schumaker 
2737b320382SAnna Schumaker 	memcpy(cp, name, namelen);
2747b320382SAnna Schumaker 	cp += namelen;
2757b320382SAnna Schumaker 	*cp = '\0';
2767b320382SAnna Schumaker 	return desclen;
2777b320382SAnna Schumaker }
2787b320382SAnna Schumaker 
nfs_idmap_request_key(const char * name,size_t namelen,const char * type,struct idmap * idmap)2797b320382SAnna Schumaker static struct key *nfs_idmap_request_key(const char *name, size_t namelen,
2807b320382SAnna Schumaker 					 const char *type, struct idmap *idmap)
2817b320382SAnna Schumaker {
2827b320382SAnna Schumaker 	char *desc;
28358002399STrond Myklebust 	struct key *rkey = ERR_PTR(-EAGAIN);
2847b320382SAnna Schumaker 	ssize_t ret;
2857b320382SAnna Schumaker 
2867b320382SAnna Schumaker 	ret = nfs_idmap_get_desc(name, namelen, type, strlen(type), &desc);
287cdb2e53fSDan Carpenter 	if (ret < 0)
2887b320382SAnna Schumaker 		return ERR_PTR(ret);
2897b320382SAnna Schumaker 
29061ca2c4aSSargun Dhillon 	if (!idmap->user_ns || idmap->user_ns == &init_user_ns)
291028db3e2SLinus Torvalds 		rkey = request_key(&key_type_id_resolver, desc, "");
2927b320382SAnna Schumaker 	if (IS_ERR(rkey)) {
2937b320382SAnna Schumaker 		mutex_lock(&idmap->idmap_mutex);
2947b320382SAnna Schumaker 		rkey = request_key_with_auxdata(&key_type_id_resolver_legacy,
295028db3e2SLinus Torvalds 						desc, NULL, "", 0, idmap);
2967b320382SAnna Schumaker 		mutex_unlock(&idmap->idmap_mutex);
2977b320382SAnna Schumaker 	}
2987b320382SAnna Schumaker 	if (!IS_ERR(rkey))
2997b320382SAnna Schumaker 		set_bit(KEY_FLAG_ROOT_CAN_INVAL, &rkey->flags);
3007b320382SAnna Schumaker 
3017b320382SAnna Schumaker 	kfree(desc);
3027b320382SAnna Schumaker 	return rkey;
3037b320382SAnna Schumaker }
3047b320382SAnna Schumaker 
nfs_idmap_get_key(const char * name,size_t namelen,const char * type,void * data,size_t data_size,struct idmap * idmap)3057b320382SAnna Schumaker static ssize_t nfs_idmap_get_key(const char *name, size_t namelen,
3067b320382SAnna Schumaker 				 const char *type, void *data,
3077b320382SAnna Schumaker 				 size_t data_size, struct idmap *idmap)
3087b320382SAnna Schumaker {
3097b320382SAnna Schumaker 	const struct cred *saved_cred;
3107b320382SAnna Schumaker 	struct key *rkey;
311146aa8b1SDavid Howells 	const struct user_key_payload *payload;
3127b320382SAnna Schumaker 	ssize_t ret;
3137b320382SAnna Schumaker 
3147b320382SAnna Schumaker 	saved_cred = override_creds(id_resolver_cache);
3157b320382SAnna Schumaker 	rkey = nfs_idmap_request_key(name, namelen, type, idmap);
3167b320382SAnna Schumaker 	revert_creds(saved_cred);
3177b320382SAnna Schumaker 
3187b320382SAnna Schumaker 	if (IS_ERR(rkey)) {
3197b320382SAnna Schumaker 		ret = PTR_ERR(rkey);
3207b320382SAnna Schumaker 		goto out;
3217b320382SAnna Schumaker 	}
3227b320382SAnna Schumaker 
3237b320382SAnna Schumaker 	rcu_read_lock();
324028db3e2SLinus Torvalds 	rkey->perm |= KEY_USR_VIEW;
325028db3e2SLinus Torvalds 
3267b320382SAnna Schumaker 	ret = key_validate(rkey);
3277b320382SAnna Schumaker 	if (ret < 0)
3287b320382SAnna Schumaker 		goto out_up;
3297b320382SAnna Schumaker 
3300837e49aSDavid Howells 	payload = user_key_payload_rcu(rkey);
3317b320382SAnna Schumaker 	if (IS_ERR_OR_NULL(payload)) {
3327b320382SAnna Schumaker 		ret = PTR_ERR(payload);
3337b320382SAnna Schumaker 		goto out_up;
3347b320382SAnna Schumaker 	}
3357b320382SAnna Schumaker 
3367b320382SAnna Schumaker 	ret = payload->datalen;
3377b320382SAnna Schumaker 	if (ret > 0 && ret <= data_size)
3387b320382SAnna Schumaker 		memcpy(data, payload->data, ret);
3397b320382SAnna Schumaker 	else
3407b320382SAnna Schumaker 		ret = -EINVAL;
3417b320382SAnna Schumaker 
3427b320382SAnna Schumaker out_up:
3437b320382SAnna Schumaker 	rcu_read_unlock();
3447b320382SAnna Schumaker 	key_put(rkey);
3457b320382SAnna Schumaker out:
3467b320382SAnna Schumaker 	return ret;
3477b320382SAnna Schumaker }
3487b320382SAnna Schumaker 
3497b320382SAnna Schumaker /* ID -> Name */
nfs_idmap_lookup_name(__u32 id,const char * type,char * buf,size_t buflen,struct idmap * idmap)3507b320382SAnna Schumaker static ssize_t nfs_idmap_lookup_name(__u32 id, const char *type, char *buf,
3517b320382SAnna Schumaker 				     size_t buflen, struct idmap *idmap)
3527b320382SAnna Schumaker {
3537b320382SAnna Schumaker 	char id_str[NFS_UINT_MAXLEN];
3547b320382SAnna Schumaker 	int id_len;
3557b320382SAnna Schumaker 	ssize_t ret;
3567b320382SAnna Schumaker 
357d6889480SDave Wysochanski 	id_len = nfs_map_numeric_to_string(id, id_str, sizeof(id_str));
3587b320382SAnna Schumaker 	ret = nfs_idmap_get_key(id_str, id_len, type, buf, buflen, idmap);
3597b320382SAnna Schumaker 	if (ret < 0)
3607b320382SAnna Schumaker 		return -EINVAL;
3617b320382SAnna Schumaker 	return ret;
3627b320382SAnna Schumaker }
3637b320382SAnna Schumaker 
3647b320382SAnna Schumaker /* Name -> ID */
nfs_idmap_lookup_id(const char * name,size_t namelen,const char * type,__u32 * id,struct idmap * idmap)3657b320382SAnna Schumaker static int nfs_idmap_lookup_id(const char *name, size_t namelen, const char *type,
3667b320382SAnna Schumaker 			       __u32 *id, struct idmap *idmap)
3677b320382SAnna Schumaker {
3687b320382SAnna Schumaker 	char id_str[NFS_UINT_MAXLEN];
3697b320382SAnna Schumaker 	long id_long;
3707b320382SAnna Schumaker 	ssize_t data_size;
3717b320382SAnna Schumaker 	int ret = 0;
3727b320382SAnna Schumaker 
3737b320382SAnna Schumaker 	data_size = nfs_idmap_get_key(name, namelen, type, id_str, NFS_UINT_MAXLEN, idmap);
3747b320382SAnna Schumaker 	if (data_size <= 0) {
3757b320382SAnna Schumaker 		ret = -EINVAL;
3767b320382SAnna Schumaker 	} else {
3777b320382SAnna Schumaker 		ret = kstrtol(id_str, 10, &id_long);
3784cd1ec95SDan Carpenter 		if (!ret)
3797b320382SAnna Schumaker 			*id = (__u32)id_long;
3807b320382SAnna Schumaker 	}
3817b320382SAnna Schumaker 	return ret;
3827b320382SAnna Schumaker }
3837b320382SAnna Schumaker 
3847b320382SAnna Schumaker /* idmap classic begins here */
3857b320382SAnna Schumaker 
3867b320382SAnna Schumaker enum {
3877b320382SAnna Schumaker 	Opt_find_uid, Opt_find_gid, Opt_find_user, Opt_find_group, Opt_find_err
3887b320382SAnna Schumaker };
3897b320382SAnna Schumaker 
3907b320382SAnna Schumaker static const match_table_t nfs_idmap_tokens = {
3917b320382SAnna Schumaker 	{ Opt_find_uid, "uid:%s" },
3927b320382SAnna Schumaker 	{ Opt_find_gid, "gid:%s" },
3937b320382SAnna Schumaker 	{ Opt_find_user, "user:%s" },
3947b320382SAnna Schumaker 	{ Opt_find_group, "group:%s" },
3957b320382SAnna Schumaker 	{ Opt_find_err, NULL }
3967b320382SAnna Schumaker };
3977b320382SAnna Schumaker 
398822ad64dSDavid Howells static int nfs_idmap_legacy_upcall(struct key *, void *);
3997b320382SAnna Schumaker static ssize_t idmap_pipe_downcall(struct file *, const char __user *,
4007b320382SAnna Schumaker 				   size_t);
4017b320382SAnna Schumaker static void idmap_release_pipe(struct inode *);
4027b320382SAnna Schumaker static void idmap_pipe_destroy_msg(struct rpc_pipe_msg *);
4037b320382SAnna Schumaker 
4047b320382SAnna Schumaker static const struct rpc_pipe_ops idmap_upcall_ops = {
4057b320382SAnna Schumaker 	.upcall		= rpc_pipe_generic_upcall,
4067b320382SAnna Schumaker 	.downcall	= idmap_pipe_downcall,
4077b320382SAnna Schumaker 	.release_pipe	= idmap_release_pipe,
4087b320382SAnna Schumaker 	.destroy_msg	= idmap_pipe_destroy_msg,
4097b320382SAnna Schumaker };
4107b320382SAnna Schumaker 
4117b320382SAnna Schumaker static struct key_type key_type_id_resolver_legacy = {
4127b320382SAnna Schumaker 	.name		= "id_legacy",
4137b320382SAnna Schumaker 	.preparse	= user_preparse,
4147b320382SAnna Schumaker 	.free_preparse	= user_free_preparse,
4157b320382SAnna Schumaker 	.instantiate	= generic_key_instantiate,
4167b320382SAnna Schumaker 	.revoke		= user_revoke,
4177b320382SAnna Schumaker 	.destroy	= user_destroy,
4187b320382SAnna Schumaker 	.describe	= user_describe,
4197b320382SAnna Schumaker 	.read		= user_read,
4207b320382SAnna Schumaker 	.request_key	= nfs_idmap_legacy_upcall,
4217b320382SAnna Schumaker };
4227b320382SAnna Schumaker 
nfs_idmap_pipe_destroy(struct dentry * dir,struct rpc_pipe_dir_object * pdo)4237b320382SAnna Schumaker static void nfs_idmap_pipe_destroy(struct dentry *dir,
4247b320382SAnna Schumaker 		struct rpc_pipe_dir_object *pdo)
4257b320382SAnna Schumaker {
4267b320382SAnna Schumaker 	struct idmap *idmap = pdo->pdo_data;
4277b320382SAnna Schumaker 	struct rpc_pipe *pipe = idmap->idmap_pipe;
4287b320382SAnna Schumaker 
4297b320382SAnna Schumaker 	if (pipe->dentry) {
4307b320382SAnna Schumaker 		rpc_unlink(pipe->dentry);
4317b320382SAnna Schumaker 		pipe->dentry = NULL;
4327b320382SAnna Schumaker 	}
4337b320382SAnna Schumaker }
4347b320382SAnna Schumaker 
nfs_idmap_pipe_create(struct dentry * dir,struct rpc_pipe_dir_object * pdo)4357b320382SAnna Schumaker static int nfs_idmap_pipe_create(struct dentry *dir,
4367b320382SAnna Schumaker 		struct rpc_pipe_dir_object *pdo)
4377b320382SAnna Schumaker {
4387b320382SAnna Schumaker 	struct idmap *idmap = pdo->pdo_data;
4397b320382SAnna Schumaker 	struct rpc_pipe *pipe = idmap->idmap_pipe;
4407b320382SAnna Schumaker 	struct dentry *dentry;
4417b320382SAnna Schumaker 
4427b320382SAnna Schumaker 	dentry = rpc_mkpipe_dentry(dir, "idmap", idmap, pipe);
4437b320382SAnna Schumaker 	if (IS_ERR(dentry))
4447b320382SAnna Schumaker 		return PTR_ERR(dentry);
4457b320382SAnna Schumaker 	pipe->dentry = dentry;
4467b320382SAnna Schumaker 	return 0;
4477b320382SAnna Schumaker }
4487b320382SAnna Schumaker 
4497b320382SAnna Schumaker static const struct rpc_pipe_dir_object_ops nfs_idmap_pipe_dir_object_ops = {
4507b320382SAnna Schumaker 	.create = nfs_idmap_pipe_create,
4517b320382SAnna Schumaker 	.destroy = nfs_idmap_pipe_destroy,
4527b320382SAnna Schumaker };
4537b320382SAnna Schumaker 
4547b320382SAnna Schumaker int
nfs_idmap_new(struct nfs_client * clp)4557b320382SAnna Schumaker nfs_idmap_new(struct nfs_client *clp)
4567b320382SAnna Schumaker {
4577b320382SAnna Schumaker 	struct idmap *idmap;
4587b320382SAnna Schumaker 	struct rpc_pipe *pipe;
4597b320382SAnna Schumaker 	int error;
4607b320382SAnna Schumaker 
4617b320382SAnna Schumaker 	idmap = kzalloc(sizeof(*idmap), GFP_KERNEL);
4627b320382SAnna Schumaker 	if (idmap == NULL)
4637b320382SAnna Schumaker 		return -ENOMEM;
4647b320382SAnna Schumaker 
46558002399STrond Myklebust 	mutex_init(&idmap->idmap_mutex);
46661ca2c4aSSargun Dhillon 	idmap->user_ns = get_user_ns(clp->cl_rpcclient->cl_cred->user_ns);
46758002399STrond Myklebust 
4687b320382SAnna Schumaker 	rpc_init_pipe_dir_object(&idmap->idmap_pdo,
4697b320382SAnna Schumaker 			&nfs_idmap_pipe_dir_object_ops,
4707b320382SAnna Schumaker 			idmap);
4717b320382SAnna Schumaker 
4727b320382SAnna Schumaker 	pipe = rpc_mkpipe_data(&idmap_upcall_ops, 0);
4737b320382SAnna Schumaker 	if (IS_ERR(pipe)) {
4747b320382SAnna Schumaker 		error = PTR_ERR(pipe);
4757b320382SAnna Schumaker 		goto err;
4767b320382SAnna Schumaker 	}
4777b320382SAnna Schumaker 	idmap->idmap_pipe = pipe;
4787b320382SAnna Schumaker 
4797b320382SAnna Schumaker 	error = rpc_add_pipe_dir_object(clp->cl_net,
4807b320382SAnna Schumaker 			&clp->cl_rpcclient->cl_pipedir_objects,
4817b320382SAnna Schumaker 			&idmap->idmap_pdo);
4827b320382SAnna Schumaker 	if (error)
4837b320382SAnna Schumaker 		goto err_destroy_pipe;
4847b320382SAnna Schumaker 
4857b320382SAnna Schumaker 	clp->cl_idmap = idmap;
4867b320382SAnna Schumaker 	return 0;
4877b320382SAnna Schumaker err_destroy_pipe:
4887b320382SAnna Schumaker 	rpc_destroy_pipe_data(idmap->idmap_pipe);
4897b320382SAnna Schumaker err:
490d5f458a9SAlexey Gladkov 	put_user_ns(idmap->user_ns);
4917b320382SAnna Schumaker 	kfree(idmap);
4927b320382SAnna Schumaker 	return error;
4937b320382SAnna Schumaker }
4947b320382SAnna Schumaker 
4957b320382SAnna Schumaker void
nfs_idmap_delete(struct nfs_client * clp)4967b320382SAnna Schumaker nfs_idmap_delete(struct nfs_client *clp)
4977b320382SAnna Schumaker {
4987b320382SAnna Schumaker 	struct idmap *idmap = clp->cl_idmap;
4997b320382SAnna Schumaker 
5007b320382SAnna Schumaker 	if (!idmap)
5017b320382SAnna Schumaker 		return;
5027b320382SAnna Schumaker 	clp->cl_idmap = NULL;
5037b320382SAnna Schumaker 	rpc_remove_pipe_dir_object(clp->cl_net,
5047b320382SAnna Schumaker 			&clp->cl_rpcclient->cl_pipedir_objects,
5057b320382SAnna Schumaker 			&idmap->idmap_pdo);
5067b320382SAnna Schumaker 	rpc_destroy_pipe_data(idmap->idmap_pipe);
50761ca2c4aSSargun Dhillon 	put_user_ns(idmap->user_ns);
5087b320382SAnna Schumaker 	kfree(idmap);
5097b320382SAnna Schumaker }
5107b320382SAnna Schumaker 
nfs_idmap_prepare_message(char * desc,struct idmap * idmap,struct idmap_msg * im,struct rpc_pipe_msg * msg)5117b320382SAnna Schumaker static int nfs_idmap_prepare_message(char *desc, struct idmap *idmap,
5127b320382SAnna Schumaker 				     struct idmap_msg *im,
5137b320382SAnna Schumaker 				     struct rpc_pipe_msg *msg)
5147b320382SAnna Schumaker {
5157b320382SAnna Schumaker 	substring_t substr;
5167b320382SAnna Schumaker 	int token, ret;
5177b320382SAnna Schumaker 
5187b320382SAnna Schumaker 	im->im_type = IDMAP_TYPE_GROUP;
5197b320382SAnna Schumaker 	token = match_token(desc, nfs_idmap_tokens, &substr);
5207b320382SAnna Schumaker 
5217b320382SAnna Schumaker 	switch (token) {
5227b320382SAnna Schumaker 	case Opt_find_uid:
5237b320382SAnna Schumaker 		im->im_type = IDMAP_TYPE_USER;
524df561f66SGustavo A. R. Silva 		fallthrough;
5257b320382SAnna Schumaker 	case Opt_find_gid:
5267b320382SAnna Schumaker 		im->im_conv = IDMAP_CONV_NAMETOID;
5277b320382SAnna Schumaker 		ret = match_strlcpy(im->im_name, &substr, IDMAP_NAMESZ);
5287b320382SAnna Schumaker 		break;
5297b320382SAnna Schumaker 
5307b320382SAnna Schumaker 	case Opt_find_user:
5317b320382SAnna Schumaker 		im->im_type = IDMAP_TYPE_USER;
532df561f66SGustavo A. R. Silva 		fallthrough;
5337b320382SAnna Schumaker 	case Opt_find_group:
5347b320382SAnna Schumaker 		im->im_conv = IDMAP_CONV_IDTONAME;
5357b320382SAnna Schumaker 		ret = match_int(&substr, &im->im_id);
53612b289cfSChengguang Xu 		if (ret)
53712b289cfSChengguang Xu 			goto out;
5387b320382SAnna Schumaker 		break;
5397b320382SAnna Schumaker 
5407b320382SAnna Schumaker 	default:
5417b320382SAnna Schumaker 		ret = -EINVAL;
5427b320382SAnna Schumaker 		goto out;
5437b320382SAnna Schumaker 	}
5447b320382SAnna Schumaker 
5457b320382SAnna Schumaker 	msg->data = im;
5467b320382SAnna Schumaker 	msg->len  = sizeof(struct idmap_msg);
5477b320382SAnna Schumaker 
5487b320382SAnna Schumaker out:
5497b320382SAnna Schumaker 	return ret;
5507b320382SAnna Schumaker }
5517b320382SAnna Schumaker 
5527b320382SAnna Schumaker static bool
nfs_idmap_prepare_pipe_upcall(struct idmap * idmap,struct idmap_legacy_upcalldata * data)5537b320382SAnna Schumaker nfs_idmap_prepare_pipe_upcall(struct idmap *idmap,
5547b320382SAnna Schumaker 		struct idmap_legacy_upcalldata *data)
5557b320382SAnna Schumaker {
5567b320382SAnna Schumaker 	if (idmap->idmap_upcall_data != NULL) {
5577b320382SAnna Schumaker 		WARN_ON_ONCE(1);
5587b320382SAnna Schumaker 		return false;
5597b320382SAnna Schumaker 	}
5607b320382SAnna Schumaker 	idmap->idmap_upcall_data = data;
5617b320382SAnna Schumaker 	return true;
5627b320382SAnna Schumaker }
5637b320382SAnna Schumaker 
nfs_idmap_complete_pipe_upcall(struct idmap_legacy_upcalldata * data,int ret)56451fd2eb5STrond Myklebust static void nfs_idmap_complete_pipe_upcall(struct idmap_legacy_upcalldata *data,
56551fd2eb5STrond Myklebust 					   int ret)
5667b320382SAnna Schumaker {
56751fd2eb5STrond Myklebust 	complete_request_key(data->authkey, ret);
56851fd2eb5STrond Myklebust 	key_put(data->authkey);
56951fd2eb5STrond Myklebust 	kfree(data);
5707b320382SAnna Schumaker }
5717b320382SAnna Schumaker 
nfs_idmap_abort_pipe_upcall(struct idmap * idmap,struct idmap_legacy_upcalldata * data,int ret)57251fd2eb5STrond Myklebust static void nfs_idmap_abort_pipe_upcall(struct idmap *idmap,
57351fd2eb5STrond Myklebust 					struct idmap_legacy_upcalldata *data,
57451fd2eb5STrond Myklebust 					int ret)
5757b320382SAnna Schumaker {
57651fd2eb5STrond Myklebust 	if (cmpxchg(&idmap->idmap_upcall_data, data, NULL) == data)
57751fd2eb5STrond Myklebust 		nfs_idmap_complete_pipe_upcall(data, ret);
5787b320382SAnna Schumaker }
5797b320382SAnna Schumaker 
nfs_idmap_legacy_upcall(struct key * authkey,void * aux)580822ad64dSDavid Howells static int nfs_idmap_legacy_upcall(struct key *authkey, void *aux)
5817b320382SAnna Schumaker {
5827b320382SAnna Schumaker 	struct idmap_legacy_upcalldata *data;
583822ad64dSDavid Howells 	struct request_key_auth *rka = get_request_key_auth(authkey);
5847b320382SAnna Schumaker 	struct rpc_pipe_msg *msg;
5857b320382SAnna Schumaker 	struct idmap_msg *im;
5867e7ce2ccSyuzhe 	struct idmap *idmap = aux;
587822ad64dSDavid Howells 	struct key *key = rka->target_key;
58849686cbbSEric Biggers 	int ret = -ENOKEY;
58949686cbbSEric Biggers 
59049686cbbSEric Biggers 	if (!aux)
59149686cbbSEric Biggers 		goto out1;
5927b320382SAnna Schumaker 
5937b320382SAnna Schumaker 	/* msg and im are freed in idmap_pipe_destroy_msg */
59449686cbbSEric Biggers 	ret = -ENOMEM;
5957b320382SAnna Schumaker 	data = kzalloc(sizeof(*data), GFP_KERNEL);
5967b320382SAnna Schumaker 	if (!data)
5977b320382SAnna Schumaker 		goto out1;
5987b320382SAnna Schumaker 
5997b320382SAnna Schumaker 	msg = &data->pipe_msg;
6007b320382SAnna Schumaker 	im = &data->idmap_msg;
6017b320382SAnna Schumaker 	data->idmap = idmap;
602822ad64dSDavid Howells 	data->authkey = key_get(authkey);
6037b320382SAnna Schumaker 
6047b320382SAnna Schumaker 	ret = nfs_idmap_prepare_message(key->description, idmap, im, msg);
6057b320382SAnna Schumaker 	if (ret < 0)
6067b320382SAnna Schumaker 		goto out2;
6077b320382SAnna Schumaker 
6087b320382SAnna Schumaker 	ret = -EAGAIN;
6097b320382SAnna Schumaker 	if (!nfs_idmap_prepare_pipe_upcall(idmap, data))
6107b320382SAnna Schumaker 		goto out2;
6117b320382SAnna Schumaker 
6127b320382SAnna Schumaker 	ret = rpc_queue_upcall(idmap->idmap_pipe, msg);
6137b320382SAnna Schumaker 	if (ret < 0)
61451fd2eb5STrond Myklebust 		nfs_idmap_abort_pipe_upcall(idmap, data, ret);
6157b320382SAnna Schumaker 
6167b320382SAnna Schumaker 	return ret;
6177b320382SAnna Schumaker out2:
6187b320382SAnna Schumaker 	kfree(data);
6197b320382SAnna Schumaker out1:
620822ad64dSDavid Howells 	complete_request_key(authkey, ret);
6217b320382SAnna Schumaker 	return ret;
6227b320382SAnna Schumaker }
6237b320382SAnna Schumaker 
nfs_idmap_instantiate(struct key * key,struct key * authkey,char * data,size_t datalen)6247b320382SAnna Schumaker static int nfs_idmap_instantiate(struct key *key, struct key *authkey, char *data, size_t datalen)
6257b320382SAnna Schumaker {
6267b320382SAnna Schumaker 	return key_instantiate_and_link(key, data, datalen,
6277b320382SAnna Schumaker 					id_resolver_cache->thread_keyring,
6287b320382SAnna Schumaker 					authkey);
6297b320382SAnna Schumaker }
6307b320382SAnna Schumaker 
nfs_idmap_read_and_verify_message(struct idmap_msg * im,struct idmap_msg * upcall,struct key * key,struct key * authkey)6317b320382SAnna Schumaker static int nfs_idmap_read_and_verify_message(struct idmap_msg *im,
6327b320382SAnna Schumaker 		struct idmap_msg *upcall,
6337b320382SAnna Schumaker 		struct key *key, struct key *authkey)
6347b320382SAnna Schumaker {
6357b320382SAnna Schumaker 	char id_str[NFS_UINT_MAXLEN];
6367b320382SAnna Schumaker 	size_t len;
6377b320382SAnna Schumaker 	int ret = -ENOKEY;
6387b320382SAnna Schumaker 
6397b320382SAnna Schumaker 	/* ret = -ENOKEY */
6407b320382SAnna Schumaker 	if (upcall->im_type != im->im_type || upcall->im_conv != im->im_conv)
6417b320382SAnna Schumaker 		goto out;
6427b320382SAnna Schumaker 	switch (im->im_conv) {
6437b320382SAnna Schumaker 	case IDMAP_CONV_NAMETOID:
6447b320382SAnna Schumaker 		if (strcmp(upcall->im_name, im->im_name) != 0)
6457b320382SAnna Schumaker 			break;
6467b320382SAnna Schumaker 		/* Note: here we store the NUL terminator too */
647d6889480SDave Wysochanski 		len = 1 + nfs_map_numeric_to_string(im->im_id, id_str,
648d6889480SDave Wysochanski 						    sizeof(id_str));
6497b320382SAnna Schumaker 		ret = nfs_idmap_instantiate(key, authkey, id_str, len);
6507b320382SAnna Schumaker 		break;
6517b320382SAnna Schumaker 	case IDMAP_CONV_IDTONAME:
6527b320382SAnna Schumaker 		if (upcall->im_id != im->im_id)
6537b320382SAnna Schumaker 			break;
6547b320382SAnna Schumaker 		len = strlen(im->im_name);
6557b320382SAnna Schumaker 		ret = nfs_idmap_instantiate(key, authkey, im->im_name, len);
6567b320382SAnna Schumaker 		break;
6577b320382SAnna Schumaker 	default:
6587b320382SAnna Schumaker 		ret = -EINVAL;
6597b320382SAnna Schumaker 	}
6607b320382SAnna Schumaker out:
6617b320382SAnna Schumaker 	return ret;
6627b320382SAnna Schumaker }
6637b320382SAnna Schumaker 
6647b320382SAnna Schumaker static ssize_t
idmap_pipe_downcall(struct file * filp,const char __user * src,size_t mlen)6657b320382SAnna Schumaker idmap_pipe_downcall(struct file *filp, const char __user *src, size_t mlen)
6667b320382SAnna Schumaker {
667822ad64dSDavid Howells 	struct request_key_auth *rka;
6687b320382SAnna Schumaker 	struct rpc_inode *rpci = RPC_I(file_inode(filp));
6697b320382SAnna Schumaker 	struct idmap *idmap = (struct idmap *)rpci->private;
67051fd2eb5STrond Myklebust 	struct idmap_legacy_upcalldata *data;
671822ad64dSDavid Howells 	struct key *authkey;
6727b320382SAnna Schumaker 	struct idmap_msg im;
6737b320382SAnna Schumaker 	size_t namelen_in;
6747b320382SAnna Schumaker 	int ret = -ENOKEY;
6757b320382SAnna Schumaker 
6767b320382SAnna Schumaker 	/* If instantiation is successful, anyone waiting for key construction
6777b320382SAnna Schumaker 	 * will have been woken up and someone else may now have used
6787b320382SAnna Schumaker 	 * idmap_key_cons - so after this point we may no longer touch it.
6797b320382SAnna Schumaker 	 */
68051fd2eb5STrond Myklebust 	data = xchg(&idmap->idmap_upcall_data, NULL);
68151fd2eb5STrond Myklebust 	if (data == NULL)
6827b320382SAnna Schumaker 		goto out_noupcall;
6837b320382SAnna Schumaker 
68451fd2eb5STrond Myklebust 	authkey = data->authkey;
685822ad64dSDavid Howells 	rka = get_request_key_auth(authkey);
6867b320382SAnna Schumaker 
6877b320382SAnna Schumaker 	if (mlen != sizeof(im)) {
6887b320382SAnna Schumaker 		ret = -ENOSPC;
6897b320382SAnna Schumaker 		goto out;
6907b320382SAnna Schumaker 	}
6917b320382SAnna Schumaker 
6927b320382SAnna Schumaker 	if (copy_from_user(&im, src, mlen) != 0) {
6937b320382SAnna Schumaker 		ret = -EFAULT;
6947b320382SAnna Schumaker 		goto out;
6957b320382SAnna Schumaker 	}
6967b320382SAnna Schumaker 
6977b320382SAnna Schumaker 	if (!(im.im_status & IDMAP_STATUS_SUCCESS)) {
6987b320382SAnna Schumaker 		ret = -ENOKEY;
6997b320382SAnna Schumaker 		goto out;
7007b320382SAnna Schumaker 	}
7017b320382SAnna Schumaker 
7027b320382SAnna Schumaker 	namelen_in = strnlen(im.im_name, IDMAP_NAMESZ);
7037b320382SAnna Schumaker 	if (namelen_in == 0 || namelen_in == IDMAP_NAMESZ) {
7047b320382SAnna Schumaker 		ret = -EINVAL;
7057b320382SAnna Schumaker 		goto out;
7067b320382SAnna Schumaker 	}
7077b320382SAnna Schumaker 
70851fd2eb5STrond Myklebust 	ret = nfs_idmap_read_and_verify_message(&im, &data->idmap_msg,
709822ad64dSDavid Howells 						rka->target_key, authkey);
7107b320382SAnna Schumaker 	if (ret >= 0) {
711822ad64dSDavid Howells 		key_set_timeout(rka->target_key, nfs_idmap_cache_timeout);
7127b320382SAnna Schumaker 		ret = mlen;
7137b320382SAnna Schumaker 	}
7147b320382SAnna Schumaker 
7157b320382SAnna Schumaker out:
71651fd2eb5STrond Myklebust 	nfs_idmap_complete_pipe_upcall(data, ret);
7177b320382SAnna Schumaker out_noupcall:
7187b320382SAnna Schumaker 	return ret;
7197b320382SAnna Schumaker }
7207b320382SAnna Schumaker 
7217b320382SAnna Schumaker static void
idmap_pipe_destroy_msg(struct rpc_pipe_msg * msg)7227b320382SAnna Schumaker idmap_pipe_destroy_msg(struct rpc_pipe_msg *msg)
7237b320382SAnna Schumaker {
7247b320382SAnna Schumaker 	struct idmap_legacy_upcalldata *data = container_of(msg,
7257b320382SAnna Schumaker 			struct idmap_legacy_upcalldata,
7267b320382SAnna Schumaker 			pipe_msg);
7277b320382SAnna Schumaker 	struct idmap *idmap = data->idmap;
7287b320382SAnna Schumaker 
7297b320382SAnna Schumaker 	if (msg->errno)
73051fd2eb5STrond Myklebust 		nfs_idmap_abort_pipe_upcall(idmap, data, msg->errno);
7317b320382SAnna Schumaker }
7327b320382SAnna Schumaker 
7337b320382SAnna Schumaker static void
idmap_release_pipe(struct inode * inode)7347b320382SAnna Schumaker idmap_release_pipe(struct inode *inode)
7357b320382SAnna Schumaker {
7367b320382SAnna Schumaker 	struct rpc_inode *rpci = RPC_I(inode);
7377b320382SAnna Schumaker 	struct idmap *idmap = (struct idmap *)rpci->private;
73851fd2eb5STrond Myklebust 	struct idmap_legacy_upcalldata *data;
7397b320382SAnna Schumaker 
74051fd2eb5STrond Myklebust 	data = xchg(&idmap->idmap_upcall_data, NULL);
74151fd2eb5STrond Myklebust 	if (data)
74251fd2eb5STrond Myklebust 		nfs_idmap_complete_pipe_upcall(data, -EPIPE);
7437b320382SAnna Schumaker }
7447b320382SAnna Schumaker 
nfs_map_name_to_uid(const struct nfs_server * server,const char * name,size_t namelen,kuid_t * uid)7457b320382SAnna Schumaker int nfs_map_name_to_uid(const struct nfs_server *server, const char *name, size_t namelen, kuid_t *uid)
7467b320382SAnna Schumaker {
7477b320382SAnna Schumaker 	struct idmap *idmap = server->nfs_client->cl_idmap;
7487b320382SAnna Schumaker 	__u32 id = -1;
7497b320382SAnna Schumaker 	int ret = 0;
7507b320382SAnna Schumaker 
7517b320382SAnna Schumaker 	if (!nfs_map_string_to_numeric(name, namelen, &id))
7527b320382SAnna Schumaker 		ret = nfs_idmap_lookup_id(name, namelen, "uid", &id, idmap);
7537b320382SAnna Schumaker 	if (ret == 0) {
75458002399STrond Myklebust 		*uid = make_kuid(idmap_userns(idmap), id);
7557b320382SAnna Schumaker 		if (!uid_valid(*uid))
7567b320382SAnna Schumaker 			ret = -ERANGE;
7577b320382SAnna Schumaker 	}
7587b320382SAnna Schumaker 	trace_nfs4_map_name_to_uid(name, namelen, id, ret);
7597b320382SAnna Schumaker 	return ret;
7607b320382SAnna Schumaker }
7617b320382SAnna Schumaker 
nfs_map_group_to_gid(const struct nfs_server * server,const char * name,size_t namelen,kgid_t * gid)7627b320382SAnna Schumaker int nfs_map_group_to_gid(const struct nfs_server *server, const char *name, size_t namelen, kgid_t *gid)
7637b320382SAnna Schumaker {
7647b320382SAnna Schumaker 	struct idmap *idmap = server->nfs_client->cl_idmap;
7657b320382SAnna Schumaker 	__u32 id = -1;
7667b320382SAnna Schumaker 	int ret = 0;
7677b320382SAnna Schumaker 
7687b320382SAnna Schumaker 	if (!nfs_map_string_to_numeric(name, namelen, &id))
7697b320382SAnna Schumaker 		ret = nfs_idmap_lookup_id(name, namelen, "gid", &id, idmap);
7707b320382SAnna Schumaker 	if (ret == 0) {
77158002399STrond Myklebust 		*gid = make_kgid(idmap_userns(idmap), id);
7727b320382SAnna Schumaker 		if (!gid_valid(*gid))
7737b320382SAnna Schumaker 			ret = -ERANGE;
7747b320382SAnna Schumaker 	}
7757b320382SAnna Schumaker 	trace_nfs4_map_group_to_gid(name, namelen, id, ret);
7767b320382SAnna Schumaker 	return ret;
7777b320382SAnna Schumaker }
7787b320382SAnna Schumaker 
nfs_map_uid_to_name(const struct nfs_server * server,kuid_t uid,char * buf,size_t buflen)7797b320382SAnna Schumaker int nfs_map_uid_to_name(const struct nfs_server *server, kuid_t uid, char *buf, size_t buflen)
7807b320382SAnna Schumaker {
7817b320382SAnna Schumaker 	struct idmap *idmap = server->nfs_client->cl_idmap;
7827b320382SAnna Schumaker 	int ret = -EINVAL;
7837b320382SAnna Schumaker 	__u32 id;
7847b320382SAnna Schumaker 
78558002399STrond Myklebust 	id = from_kuid_munged(idmap_userns(idmap), uid);
7867b320382SAnna Schumaker 	if (!(server->caps & NFS_CAP_UIDGID_NOMAP))
7877b320382SAnna Schumaker 		ret = nfs_idmap_lookup_name(id, "user", buf, buflen, idmap);
7887b320382SAnna Schumaker 	if (ret < 0)
7897b320382SAnna Schumaker 		ret = nfs_map_numeric_to_string(id, buf, buflen);
7907b320382SAnna Schumaker 	trace_nfs4_map_uid_to_name(buf, ret, id, ret);
7917b320382SAnna Schumaker 	return ret;
7927b320382SAnna Schumaker }
nfs_map_gid_to_group(const struct nfs_server * server,kgid_t gid,char * buf,size_t buflen)7937b320382SAnna Schumaker int nfs_map_gid_to_group(const struct nfs_server *server, kgid_t gid, char *buf, size_t buflen)
7947b320382SAnna Schumaker {
7957b320382SAnna Schumaker 	struct idmap *idmap = server->nfs_client->cl_idmap;
7967b320382SAnna Schumaker 	int ret = -EINVAL;
7977b320382SAnna Schumaker 	__u32 id;
7987b320382SAnna Schumaker 
79958002399STrond Myklebust 	id = from_kgid_munged(idmap_userns(idmap), gid);
8007b320382SAnna Schumaker 	if (!(server->caps & NFS_CAP_UIDGID_NOMAP))
8017b320382SAnna Schumaker 		ret = nfs_idmap_lookup_name(id, "group", buf, buflen, idmap);
8027b320382SAnna Schumaker 	if (ret < 0)
8037b320382SAnna Schumaker 		ret = nfs_map_numeric_to_string(id, buf, buflen);
8047b320382SAnna Schumaker 	trace_nfs4_map_gid_to_group(buf, ret, id, ret);
8057b320382SAnna Schumaker 	return ret;
8067b320382SAnna Schumaker }
807