xref: /openbmc/linux/net/sunrpc/svcauth_unix.c (revision 4f2c0a4acffbec01079c28f839422e64ddeff004)
1457c8996SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
21da177e4SLinus Torvalds #include <linux/types.h>
31da177e4SLinus Torvalds #include <linux/sched.h>
41da177e4SLinus Torvalds #include <linux/module.h>
51da177e4SLinus Torvalds #include <linux/sunrpc/types.h>
61da177e4SLinus Torvalds #include <linux/sunrpc/xdr.h>
71da177e4SLinus Torvalds #include <linux/sunrpc/svcsock.h>
81da177e4SLinus Torvalds #include <linux/sunrpc/svcauth.h>
9c4170583SAndy Adamson #include <linux/sunrpc/gss_api.h>
105976687aSJeff Layton #include <linux/sunrpc/addr.h>
111da177e4SLinus Torvalds #include <linux/err.h>
121da177e4SLinus Torvalds #include <linux/seq_file.h>
131da177e4SLinus Torvalds #include <linux/hash.h>
14543537bdSPaulo Marques #include <linux/string.h>
155a0e3ad6STejun Heo #include <linux/slab.h>
167b2b1feeSGreg Banks #include <net/sock.h>
17f15364bdSAurélien Charbon #include <net/ipv6.h>
18f15364bdSAurélien Charbon #include <linux/kernel.h>
19ae2975bcSEric W. Biederman #include <linux/user_namespace.h>
201da177e4SLinus Torvalds #include <trace/events/sunrpc.h>
211da177e4SLinus Torvalds 
221da177e4SLinus Torvalds #define RPCDBG_FACILITY	RPCDBG_AUTH
2390d51b02SPavel Emelyanov 
2490d51b02SPavel Emelyanov #include "netns.h"
251da177e4SLinus Torvalds 
261da177e4SLinus Torvalds /*
271da177e4SLinus Torvalds  * AUTHUNIX and AUTHNULL credentials are both handled here.
281da177e4SLinus Torvalds  * AUTHNULL is treated just like AUTHUNIX except that the uid/gid
291da177e4SLinus Torvalds  * are always nobody (-2).  i.e. we do the same IP address checks for
301da177e4SLinus Torvalds  * AUTHNULL as for AUTHUNIX, and that is done here.
311da177e4SLinus Torvalds  */
321da177e4SLinus Torvalds 
331da177e4SLinus Torvalds 
341da177e4SLinus Torvalds struct unix_domain {
351da177e4SLinus Torvalds 	struct auth_domain	h;
361da177e4SLinus Torvalds 	/* other stuff later */
371da177e4SLinus Torvalds };
3813e0e958SH Hartley Sweeten 
39efc36aa5SNeilBrown extern struct auth_ops svcauth_null;
40*74aaf96fSChuck Lever extern struct auth_ops svcauth_unix;
41efc36aa5SNeilBrown extern struct auth_ops svcauth_tls;
42608a0ab2STrond Myklebust 
svcauth_unix_domain_release_rcu(struct rcu_head * head)438b3e07acSJ. Bruce Fields static void svcauth_unix_domain_release_rcu(struct rcu_head *head)
44608a0ab2STrond Myklebust {
458b3e07acSJ. Bruce Fields 	struct auth_domain *dom = container_of(head, struct auth_domain, rcu_head);
468b3e07acSJ. Bruce Fields 	struct unix_domain *ud = container_of(dom, struct unix_domain, h);
478b3e07acSJ. Bruce Fields 
488b3e07acSJ. Bruce Fields 	kfree(dom->name);
498b3e07acSJ. Bruce Fields 	kfree(ud);
508b3e07acSJ. Bruce Fields }
51608a0ab2STrond Myklebust 
svcauth_unix_domain_release(struct auth_domain * dom)52608a0ab2STrond Myklebust static void svcauth_unix_domain_release(struct auth_domain *dom)
53608a0ab2STrond Myklebust {
54608a0ab2STrond Myklebust 	call_rcu(&dom->rcu_head, svcauth_unix_domain_release_rcu);
55608a0ab2STrond Myklebust }
561da177e4SLinus Torvalds 
unix_domain_find(char * name)571da177e4SLinus Torvalds struct auth_domain *unix_domain_find(char *name)
58efc36aa5SNeilBrown {
59efc36aa5SNeilBrown 	struct auth_domain *rv;
601da177e4SLinus Torvalds 	struct unix_domain *new = NULL;
61608a0ab2STrond Myklebust 
62efc36aa5SNeilBrown 	rv = auth_domain_find(name);
63ad1b5229SNeilBrown 	while(1) {
64ad1b5229SNeilBrown 		if (rv) {
65352b5d13SJ. Bruce Fields 			if (new && rv != &new->h)
66ad1b5229SNeilBrown 				svcauth_unix_domain_release(&new->h);
67ad1b5229SNeilBrown 
681da177e4SLinus Torvalds 			if (rv->flavour != &svcauth_unix) {
691da177e4SLinus Torvalds 				auth_domain_put(rv);
701da177e4SLinus Torvalds 				return NULL;
711da177e4SLinus Torvalds 			}
72ad1b5229SNeilBrown 			return rv;
731da177e4SLinus Torvalds 		}
741da177e4SLinus Torvalds 
751da177e4SLinus Torvalds 		new = kmalloc(sizeof(*new), GFP_KERNEL);
761da177e4SLinus Torvalds 		if (new == NULL)
77efc36aa5SNeilBrown 			return NULL;
78543537bdSPaulo Marques 		kref_init(&new->h.ref);
79dd08d6eaSNeilBrown 		new->h.name = kstrdup(name, GFP_KERNEL);
80dd08d6eaSNeilBrown 		if (new->h.name == NULL) {
81dd08d6eaSNeilBrown 			kfree(new);
82dd08d6eaSNeilBrown 			return NULL;
83efc36aa5SNeilBrown 		}
84efc36aa5SNeilBrown 		new->h.flavour = &svcauth_unix;
851da177e4SLinus Torvalds 		rv = auth_domain_lookup(name, &new->h);
861da177e4SLinus Torvalds 	}
8724c3767eSTrond Myklebust }
881da177e4SLinus Torvalds EXPORT_SYMBOL_GPL(unix_domain_find);
891da177e4SLinus Torvalds 
901da177e4SLinus Torvalds 
911da177e4SLinus Torvalds /**************************************************
921da177e4SLinus Torvalds  * cache for IP address to unix_domain
931da177e4SLinus Torvalds  * as needed by AUTH_UNIX
941da177e4SLinus Torvalds  */
951da177e4SLinus Torvalds #define	IP_HASHBITS	8
961da177e4SLinus Torvalds #define	IP_HASHMAX	(1<<IP_HASHBITS)
971da177e4SLinus Torvalds 
981da177e4SLinus Torvalds struct ip_map {
991da177e4SLinus Torvalds 	struct cache_head	h;
100f15364bdSAurélien Charbon 	char			m_class[8]; /* e.g. "nfsd" */
1011da177e4SLinus Torvalds 	struct in6_addr		m_addr;
102fd5d2f78STrond Myklebust 	struct unix_domain	*m_client;
1031da177e4SLinus Torvalds 	struct rcu_head		m_rcu;
1041da177e4SLinus Torvalds };
105baab935fSNeilBrown 
ip_map_put(struct kref * kref)1061da177e4SLinus Torvalds static void ip_map_put(struct kref *kref)
107baab935fSNeilBrown {
1081da177e4SLinus Torvalds 	struct cache_head *item = container_of(kref, struct cache_head, ref);
109baab935fSNeilBrown 	struct ip_map *im = container_of(item, struct ip_map,h);
1101da177e4SLinus Torvalds 
1111da177e4SLinus Torvalds 	if (test_bit(CACHE_VALID, &item->flags) &&
1121da177e4SLinus Torvalds 	    !test_bit(CACHE_NEGATIVE, &item->flags))
113fd5d2f78STrond Myklebust 		auth_domain_put(&im->m_client->h);
1141da177e4SLinus Torvalds 	kfree_rcu(im, m_rcu);
1151da177e4SLinus Torvalds }
116ddbe5032SEric Dumazet 
hash_ip6(const struct in6_addr * ip)1171f1e030bSNeilBrown static inline int hash_ip6(const struct in6_addr *ip)
118ddbe5032SEric Dumazet {
119f15364bdSAurélien Charbon 	return hash_32(ipv6_addr_hash(ip), IP_HASHBITS);
1201a9917c2SNeilBrown }
ip_map_match(struct cache_head * corig,struct cache_head * cnew)1211a9917c2SNeilBrown static int ip_map_match(struct cache_head *corig, struct cache_head *cnew)
1221a9917c2SNeilBrown {
1231a9917c2SNeilBrown 	struct ip_map *orig = container_of(corig, struct ip_map, h);
124f64f9e71SJoe Perches 	struct ip_map *new = container_of(cnew, struct ip_map, h);
125f64f9e71SJoe Perches 	return strcmp(orig->m_class, new->m_class) == 0 &&
1261a9917c2SNeilBrown 	       ipv6_addr_equal(&orig->m_addr, &new->m_addr);
1271a9917c2SNeilBrown }
ip_map_init(struct cache_head * cnew,struct cache_head * citem)1281a9917c2SNeilBrown static void ip_map_init(struct cache_head *cnew, struct cache_head *citem)
1291a9917c2SNeilBrown {
1301a9917c2SNeilBrown 	struct ip_map *new = container_of(cnew, struct ip_map, h);
1311f1e030bSNeilBrown 	struct ip_map *item = container_of(citem, struct ip_map, h);
1321da177e4SLinus Torvalds 
1334e3fd7a0SAlexey Dobriyan 	strcpy(new->m_class, item->m_class);
1341da177e4SLinus Torvalds 	new->m_addr = item->m_addr;
1351a9917c2SNeilBrown }
update(struct cache_head * cnew,struct cache_head * citem)1361da177e4SLinus Torvalds static void update(struct cache_head *cnew, struct cache_head *citem)
1371a9917c2SNeilBrown {
1381a9917c2SNeilBrown 	struct ip_map *new = container_of(cnew, struct ip_map, h);
1391a9917c2SNeilBrown 	struct ip_map *item = container_of(citem, struct ip_map, h);
140efc36aa5SNeilBrown 
1411da177e4SLinus Torvalds 	kref_get(&item->m_client->h.ref);
1421da177e4SLinus Torvalds 	new->m_client = item->m_client;
1431a9917c2SNeilBrown }
ip_map_alloc(void)1441a9917c2SNeilBrown static struct cache_head *ip_map_alloc(void)
1451a9917c2SNeilBrown {
1461a9917c2SNeilBrown 	struct ip_map *i = kmalloc(sizeof(*i), GFP_KERNEL);
1471a9917c2SNeilBrown 	if (i)
1481a9917c2SNeilBrown 		return &i->h;
1491a9917c2SNeilBrown 	else
1501a9917c2SNeilBrown 		return NULL;
1511da177e4SLinus Torvalds }
15265286b88STrond Myklebust 
ip_map_upcall(struct cache_detail * cd,struct cache_head * h)15365286b88STrond Myklebust static int ip_map_upcall(struct cache_detail *cd, struct cache_head *h)
15465286b88STrond Myklebust {
15565286b88STrond Myklebust 	return sunrpc_cache_pipe_upcall(cd, h);
15665286b88STrond Myklebust }
1571da177e4SLinus Torvalds 
ip_map_request(struct cache_detail * cd,struct cache_head * h,char ** bpp,int * blen)1581da177e4SLinus Torvalds static void ip_map_request(struct cache_detail *cd,
1591da177e4SLinus Torvalds 				  struct cache_head *h,
1601da177e4SLinus Torvalds 				  char **bpp, int *blen)
161f15364bdSAurélien Charbon {
1621da177e4SLinus Torvalds 	char text_addr[40];
1631da177e4SLinus Torvalds 	struct ip_map *im = container_of(h, struct ip_map, h);
164f15364bdSAurélien Charbon 
16521454aaaSHarvey Harrison 	if (ipv6_addr_v4mapped(&(im->m_addr))) {
166f15364bdSAurélien Charbon 		snprintf(text_addr, 20, "%pI4", &im->m_addr.s6_addr32[3]);
1675b095d98SHarvey Harrison 	} else {
168f15364bdSAurélien Charbon 		snprintf(text_addr, 40, "%pI6", &im->m_addr);
1691da177e4SLinus Torvalds 	}
1701da177e4SLinus Torvalds 	qword_add(bpp, blen, im->m_class);
1711da177e4SLinus Torvalds 	qword_add(bpp, blen, text_addr);
1721da177e4SLinus Torvalds 	(*bpp)[-1] = '\n';
1731da177e4SLinus Torvalds }
174bf18ab32SPavel Emelyanov 
175f559935eSArnd Bergmann static struct ip_map *__ip_map_lookup(struct cache_detail *cd, char *class, struct in6_addr *addr);
1761da177e4SLinus Torvalds static int __ip_map_update(struct cache_detail *cd, struct ip_map *ipm, struct unix_domain *udom, time64_t expiry);
1771da177e4SLinus Torvalds 
ip_map_parse(struct cache_detail * cd,char * mesg,int mlen)1781da177e4SLinus Torvalds static int ip_map_parse(struct cache_detail *cd,
1791da177e4SLinus Torvalds 			  char *mesg, int mlen)
1801da177e4SLinus Torvalds {
1811da177e4SLinus Torvalds 	/* class ipaddress [domainname] */
1821da177e4SLinus Torvalds 	/* should be safe just to use the start of the input buffer
1831da177e4SLinus Torvalds 	 * for scratch: */
1841da177e4SLinus Torvalds 	char *buf = mesg;
1851a9917c2SNeilBrown 	int len;
18607396051SChuck Lever 	char class[8];
18707396051SChuck Lever 	union {
18807396051SChuck Lever 		struct sockaddr		sa;
18907396051SChuck Lever 		struct sockaddr_in	s4;
19007396051SChuck Lever 		struct sockaddr_in6	s6;
19107396051SChuck Lever 	} address;
1921a9917c2SNeilBrown 	struct sockaddr_in6 sin6;
1931a9917c2SNeilBrown 	int err;
1941a9917c2SNeilBrown 
1951da177e4SLinus Torvalds 	struct ip_map *ipmp;
196f559935eSArnd Bergmann 	struct auth_domain *dom;
1971da177e4SLinus Torvalds 	time64_t expiry;
1981da177e4SLinus Torvalds 
1991da177e4SLinus Torvalds 	if (mesg[mlen-1] != '\n')
2001da177e4SLinus Torvalds 		return -EINVAL;
2011da177e4SLinus Torvalds 	mesg[mlen-1] = 0;
2021da177e4SLinus Torvalds 
2031a9917c2SNeilBrown 	/* class */
2041da177e4SLinus Torvalds 	len = qword_get(&mesg, class, sizeof(class));
2051da177e4SLinus Torvalds 	if (len <= 0) return -EINVAL;
2061da177e4SLinus Torvalds 
2071da177e4SLinus Torvalds 	/* ip address */
2081da177e4SLinus Torvalds 	len = qword_get(&mesg, buf, mlen);
2091da177e4SLinus Torvalds 	if (len <= 0) return -EINVAL;
210d05cc104SStanislav Kinsbursky 
2111da177e4SLinus Torvalds 	if (rpc_pton(cd->net, buf, len, &address.sa, sizeof(address)) == 0)
21207396051SChuck Lever 		return -EINVAL;
21307396051SChuck Lever 	switch (address.sa.sa_family) {
21407396051SChuck Lever 	case AF_INET:
21507396051SChuck Lever 		/* Form a mapped IPv4 address in sin6 */
21670dc78daSPavel Emelyanov 		sin6.sin6_family = AF_INET6;
21770dc78daSPavel Emelyanov 		ipv6_addr_set_v4mapped(address.s4.sin_addr.s_addr,
21807396051SChuck Lever 				&sin6.sin6_addr);
219dfd56b8bSEric Dumazet 		break;
22007396051SChuck Lever #if IS_ENABLED(CONFIG_IPV6)
22107396051SChuck Lever 	case AF_INET6:
22207396051SChuck Lever 		memcpy(&sin6, &address.s6, sizeof(sin6));
22307396051SChuck Lever 		break;
22407396051SChuck Lever #endif
22507396051SChuck Lever 	default:
22607396051SChuck Lever 		return -EINVAL;
2271da177e4SLinus Torvalds 	}
2281da177e4SLinus Torvalds 
2291da177e4SLinus Torvalds 	err = get_expiry(&mesg, &expiry);
2301da177e4SLinus Torvalds 	if (err)
2311da177e4SLinus Torvalds 		return err;
2321da177e4SLinus Torvalds 
2331da177e4SLinus Torvalds 	/* domainname, or empty for NEGATIVE */
2341da177e4SLinus Torvalds 	len = qword_get(&mesg, buf, mlen);
2351da177e4SLinus Torvalds 	if (len < 0) return -EINVAL;
2361da177e4SLinus Torvalds 
2371da177e4SLinus Torvalds 	if (len) {
2381da177e4SLinus Torvalds 		dom = unix_domain_find(buf);
2391da177e4SLinus Torvalds 		if (dom == NULL)
2401da177e4SLinus Torvalds 			return -ENOENT;
2411da177e4SLinus Torvalds 	} else
2421da177e4SLinus Torvalds 		dom = NULL;
24307396051SChuck Lever 
244bf18ab32SPavel Emelyanov 	/* IPv6 scope IDs are ignored for now */
2451a9917c2SNeilBrown 	ipmp = __ip_map_lookup(cd, class, &sin6.sin6_addr);
246bf18ab32SPavel Emelyanov 	if (ipmp) {
2471a9917c2SNeilBrown 		err = __ip_map_update(cd, ipmp,
2481a9917c2SNeilBrown 			     container_of(dom, struct unix_domain, h),
2491a9917c2SNeilBrown 			     expiry);
2501a9917c2SNeilBrown 	} else
2511a9917c2SNeilBrown 		err = -ENOMEM;
2521da177e4SLinus Torvalds 
2531da177e4SLinus Torvalds 	if (dom)
2541a9917c2SNeilBrown 		auth_domain_put(dom);
2551da177e4SLinus Torvalds 
2561a9917c2SNeilBrown 	cache_flush();
2571da177e4SLinus Torvalds 	return err;
2581da177e4SLinus Torvalds }
2591da177e4SLinus Torvalds 
ip_map_show(struct seq_file * m,struct cache_detail * cd,struct cache_head * h)2601da177e4SLinus Torvalds static int ip_map_show(struct seq_file *m,
2611da177e4SLinus Torvalds 		       struct cache_detail *cd,
2621da177e4SLinus Torvalds 		       struct cache_head *h)
2631da177e4SLinus Torvalds {
264f15364bdSAurélien Charbon 	struct ip_map *im;
2651da177e4SLinus Torvalds 	struct in6_addr addr;
2661da177e4SLinus Torvalds 	char *dom = "-no-domain-";
2671da177e4SLinus Torvalds 
2681da177e4SLinus Torvalds 	if (h == NULL) {
2691da177e4SLinus Torvalds 		seq_puts(m, "#class IP domain\n");
2701da177e4SLinus Torvalds 		return 0;
2711da177e4SLinus Torvalds 	}
2721da177e4SLinus Torvalds 	im = container_of(h, struct ip_map, h);
2734e3fd7a0SAlexey Dobriyan 	/* class addr domain */
2741da177e4SLinus Torvalds 	addr = im->m_addr;
2751da177e4SLinus Torvalds 
2761da177e4SLinus Torvalds 	if (test_bit(CACHE_VALID, &h->flags) &&
2771da177e4SLinus Torvalds 	    !test_bit(CACHE_NEGATIVE, &h->flags))
2781da177e4SLinus Torvalds 		dom = im->m_client->h.name;
279f15364bdSAurélien Charbon 
28021454aaaSHarvey Harrison 	if (ipv6_addr_v4mapped(&addr)) {
28121454aaaSHarvey Harrison 		seq_printf(m, "%s %pI4 %s\n",
282f15364bdSAurélien Charbon 			im->m_class, &addr.s6_addr32[3], dom);
2835b095d98SHarvey Harrison 	} else {
284f15364bdSAurélien Charbon 		seq_printf(m, "%s %pI6 %s\n", im->m_class, &addr, dom);
2851da177e4SLinus Torvalds 	}
2861da177e4SLinus Torvalds 	return 0;
2871da177e4SLinus Torvalds }
2881da177e4SLinus Torvalds 
289bf18ab32SPavel Emelyanov 
__ip_map_lookup(struct cache_detail * cd,char * class,struct in6_addr * addr)290bf18ab32SPavel Emelyanov static struct ip_map *__ip_map_lookup(struct cache_detail *cd, char *class,
2911a9917c2SNeilBrown 		struct in6_addr *addr)
2921a9917c2SNeilBrown {
2931a9917c2SNeilBrown 	struct ip_map ip;
2941da177e4SLinus Torvalds 	struct cache_head *ch;
2951a9917c2SNeilBrown 
2964e3fd7a0SAlexey Dobriyan 	strcpy(ip.m_class, class);
297fd5d2f78STrond Myklebust 	ip.m_addr = *addr;
2981a9917c2SNeilBrown 	ch = sunrpc_cache_lookup_rcu(cd, &ip.h,
299ddbe5032SEric Dumazet 				     hash_str(class, IP_HASHBITS) ^
3001a9917c2SNeilBrown 				     hash_ip6(addr));
3011a9917c2SNeilBrown 
3021a9917c2SNeilBrown 	if (ch)
3031a9917c2SNeilBrown 		return container_of(ch, struct ip_map, h);
3041a9917c2SNeilBrown 	else
3051a9917c2SNeilBrown 		return NULL;
3061a9917c2SNeilBrown }
307bf18ab32SPavel Emelyanov 
__ip_map_update(struct cache_detail * cd,struct ip_map * ipm,struct unix_domain * udom,time64_t expiry)308f559935eSArnd Bergmann static int __ip_map_update(struct cache_detail *cd, struct ip_map *ipm,
3091a9917c2SNeilBrown 		struct unix_domain *udom, time64_t expiry)
3101a9917c2SNeilBrown {
3111a9917c2SNeilBrown 	struct ip_map ip;
3121a9917c2SNeilBrown 	struct cache_head *ch;
3131a9917c2SNeilBrown 
3141a9917c2SNeilBrown 	ip.m_client = udom;
3151a9917c2SNeilBrown 	ip.h.flags = 0;
3161a9917c2SNeilBrown 	if (!udom)
3171a9917c2SNeilBrown 		set_bit(CACHE_NEGATIVE, &ip.h.flags);
318bf18ab32SPavel Emelyanov 	ip.h.expiry_time = expiry;
3191a9917c2SNeilBrown 	ch = sunrpc_cache_update(cd, &ip.h, &ipm->h,
320ddbe5032SEric Dumazet 				 hash_str(ipm->m_class, IP_HASHBITS) ^
3211a9917c2SNeilBrown 				 hash_ip6(&ipm->m_addr));
3221a9917c2SNeilBrown 	if (!ch)
323bf18ab32SPavel Emelyanov 		return -ENOMEM;
3241a9917c2SNeilBrown 	cache_put(ch, cd);
3251a9917c2SNeilBrown 	return 0;
3261da177e4SLinus Torvalds }
327e5f06f72SStanislav Kinsbursky 
svcauth_unix_purge(struct net * net)3281da177e4SLinus Torvalds void svcauth_unix_purge(struct net *net)
32990d51b02SPavel Emelyanov {
33090d51b02SPavel Emelyanov 	struct sunrpc_net *sn;
33190d51b02SPavel Emelyanov 
33290d51b02SPavel Emelyanov 	sn = net_generic(net, sunrpc_net_id);
33390d51b02SPavel Emelyanov 	cache_purge(sn->ip_map_cache);
33424c3767eSTrond Myklebust }
3351da177e4SLinus Torvalds EXPORT_SYMBOL_GPL(svcauth_unix_purge);
3367b2b1feeSGreg Banks 
3373be4479fSPavel Emelyanov static inline struct ip_map *
ip_map_cached_get(struct svc_xprt * xprt)3387b2b1feeSGreg Banks ip_map_cached_get(struct svc_xprt *xprt)
339def13d74STom Tucker {
34090d51b02SPavel Emelyanov 	struct ip_map *ipm = NULL;
341def13d74STom Tucker 	struct sunrpc_net *sn;
342def13d74STom Tucker 
343def13d74STom Tucker 	if (test_bit(XPT_CACHE_AUTH, &xprt->xpt_flags)) {
344def13d74STom Tucker 		spin_lock(&xprt->xpt_lock);
3457b2b1feeSGreg Banks 		ipm = xprt->xpt_auth_cache;
3467715cde8SNeilBrown 		if (ipm != NULL) {
3477715cde8SNeilBrown 			sn = net_generic(xprt->xpt_net, sunrpc_net_id);
3487b2b1feeSGreg Banks 			if (cache_is_expired(sn->ip_map_cache, &ipm->h)) {
3497b2b1feeSGreg Banks 				/*
3507b2b1feeSGreg Banks 				 * The entry has been invalidated since it was
3517b2b1feeSGreg Banks 				 * remembered, e.g. by a second mount from the
3527b2b1feeSGreg Banks 				 * same IP address.
353def13d74STom Tucker 				 */
354def13d74STom Tucker 				xprt->xpt_auth_cache = NULL;
35590d51b02SPavel Emelyanov 				spin_unlock(&xprt->xpt_lock);
3567b2b1feeSGreg Banks 				cache_put(&ipm->h, sn->ip_map_cache);
3577b2b1feeSGreg Banks 				return NULL;
3587b2b1feeSGreg Banks 			}
3597b2b1feeSGreg Banks 			cache_get(&ipm->h);
360def13d74STom Tucker 		}
361def13d74STom Tucker 		spin_unlock(&xprt->xpt_lock);
3627b2b1feeSGreg Banks 	}
3637b2b1feeSGreg Banks 	return ipm;
3647b2b1feeSGreg Banks }
3657b2b1feeSGreg Banks 
3663be4479fSPavel Emelyanov static inline void
ip_map_cached_put(struct svc_xprt * xprt,struct ip_map * ipm)3677b2b1feeSGreg Banks ip_map_cached_put(struct svc_xprt *xprt, struct ip_map *ipm)
368def13d74STom Tucker {
369def13d74STom Tucker 	if (test_bit(XPT_CACHE_AUTH, &xprt->xpt_flags)) {
370def13d74STom Tucker 		spin_lock(&xprt->xpt_lock);
37130f3deeeSNeilBrown 		if (xprt->xpt_auth_cache == NULL) {
372def13d74STom Tucker 			/* newly cached, keep the reference */
37330f3deeeSNeilBrown 			xprt->xpt_auth_cache = ipm;
37430f3deeeSNeilBrown 			ipm = NULL;
375def13d74STom Tucker 		}
376def13d74STom Tucker 		spin_unlock(&xprt->xpt_lock);
37790d51b02SPavel Emelyanov 	}
37890d51b02SPavel Emelyanov 	if (ipm) {
37990d51b02SPavel Emelyanov 		struct sunrpc_net *sn;
38090d51b02SPavel Emelyanov 
38190d51b02SPavel Emelyanov 		sn = net_generic(xprt->xpt_net, sunrpc_net_id);
38290d51b02SPavel Emelyanov 		cache_put(&ipm->h, sn->ip_map_cache);
3837b2b1feeSGreg Banks 	}
3847b2b1feeSGreg Banks }
3857b2b1feeSGreg Banks 
386e3bfca01SPavel Emelyanov void
svcauth_unix_info_release(struct svc_xprt * xpt)3877b2b1feeSGreg Banks svcauth_unix_info_release(struct svc_xprt *xpt)
388e3bfca01SPavel Emelyanov {
389e3bfca01SPavel Emelyanov 	struct ip_map *ipm;
390e3bfca01SPavel Emelyanov 
39190d51b02SPavel Emelyanov 	ipm = xpt->xpt_auth_cache;
39290d51b02SPavel Emelyanov 	if (ipm != NULL) {
39390d51b02SPavel Emelyanov 		struct sunrpc_net *sn;
39490d51b02SPavel Emelyanov 
39590d51b02SPavel Emelyanov 		sn = net_generic(xpt->xpt_net, sunrpc_net_id);
39690d51b02SPavel Emelyanov 		cache_put(&ipm->h, sn->ip_map_cache);
3977b2b1feeSGreg Banks 	}
3987b2b1feeSGreg Banks }
3993fc605a2SNeilBrown 
4003fc605a2SNeilBrown /****************************************************************************
4013fc605a2SNeilBrown  * auth.unix.gid cache
4025786461bSKinglong Mee  * simple cache to map a UID to a list of GIDs
4033fc605a2SNeilBrown  * because AUTH_UNIX aka AUTH_SYS has a max of UNX_NGROUPS
4043fc605a2SNeilBrown  */
4053fc605a2SNeilBrown #define	GID_HASHBITS	8
4063fc605a2SNeilBrown #define	GID_HASHMAX	(1<<GID_HASHBITS)
4073fc605a2SNeilBrown 
4083fc605a2SNeilBrown struct unix_gid {
4097eaf040bSEric W. Biederman 	struct cache_head	h;
4103fc605a2SNeilBrown 	kuid_t			uid;
411fd5d2f78STrond Myklebust 	struct group_info	*gi;
4123fc605a2SNeilBrown 	struct rcu_head		rcu;
4133fc605a2SNeilBrown };
4149e469e30SEric W. Biederman 
unix_gid_hash(kuid_t uid)4159e469e30SEric W. Biederman static int unix_gid_hash(kuid_t uid)
4169e469e30SEric W. Biederman {
4179e469e30SEric W. Biederman 	return hash_long(from_kuid(&init_user_ns, uid), GID_HASHBITS);
4189e469e30SEric W. Biederman }
4193fc605a2SNeilBrown 
unix_gid_free(struct rcu_head * rcu)4203fc605a2SNeilBrown static void unix_gid_free(struct rcu_head *rcu)
4213fc605a2SNeilBrown {
4223fc605a2SNeilBrown 	struct unix_gid *ug = container_of(rcu, struct unix_gid, rcu);
4233fc605a2SNeilBrown 	struct cache_head *item = &ug->h;
4243fc605a2SNeilBrown 
4253fc605a2SNeilBrown 	if (test_bit(CACHE_VALID, &item->flags) &&
426fd5d2f78STrond Myklebust 	    !test_bit(CACHE_NEGATIVE, &item->flags))
4273fc605a2SNeilBrown 		put_group_info(ug->gi);
4283fc605a2SNeilBrown 	kfree(ug);
4293fc605a2SNeilBrown }
4303fc605a2SNeilBrown 
unix_gid_put(struct kref * kref)4313fc605a2SNeilBrown static void unix_gid_put(struct kref *kref)
4323fc605a2SNeilBrown {
4330b4d51b0SEric W. Biederman 	struct cache_head *item = container_of(kref, struct cache_head, ref);
4343fc605a2SNeilBrown 	struct unix_gid *ug = container_of(item, struct unix_gid, h);
4353fc605a2SNeilBrown 
4363fc605a2SNeilBrown 	call_rcu(&ug->rcu, unix_gid_free);
4373fc605a2SNeilBrown }
4383fc605a2SNeilBrown 
unix_gid_match(struct cache_head * corig,struct cache_head * cnew)4393fc605a2SNeilBrown static int unix_gid_match(struct cache_head *corig, struct cache_head *cnew)
4403fc605a2SNeilBrown {
4413fc605a2SNeilBrown 	struct unix_gid *orig = container_of(corig, struct unix_gid, h);
4423fc605a2SNeilBrown 	struct unix_gid *new = container_of(cnew, struct unix_gid, h);
4433fc605a2SNeilBrown 	return uid_eq(orig->uid, new->uid);
4443fc605a2SNeilBrown }
unix_gid_init(struct cache_head * cnew,struct cache_head * citem)4453fc605a2SNeilBrown static void unix_gid_init(struct cache_head *cnew, struct cache_head *citem)
4463fc605a2SNeilBrown {
4473fc605a2SNeilBrown 	struct unix_gid *new = container_of(cnew, struct unix_gid, h);
4483fc605a2SNeilBrown 	struct unix_gid *item = container_of(citem, struct unix_gid, h);
4493fc605a2SNeilBrown 	new->uid = item->uid;
4503fc605a2SNeilBrown }
unix_gid_update(struct cache_head * cnew,struct cache_head * citem)4513fc605a2SNeilBrown static void unix_gid_update(struct cache_head *cnew, struct cache_head *citem)
4523fc605a2SNeilBrown {
4533fc605a2SNeilBrown 	struct unix_gid *new = container_of(cnew, struct unix_gid, h);
4543fc605a2SNeilBrown 	struct unix_gid *item = container_of(citem, struct unix_gid, h);
4553fc605a2SNeilBrown 
4563fc605a2SNeilBrown 	get_group_info(item->gi);
4573fc605a2SNeilBrown 	new->gi = item->gi;
45865286b88STrond Myklebust }
unix_gid_alloc(void)45965286b88STrond Myklebust static struct cache_head *unix_gid_alloc(void)
46065286b88STrond Myklebust {
46165286b88STrond Myklebust 	struct unix_gid *g = kmalloc(sizeof(*g), GFP_KERNEL);
46265286b88STrond Myklebust 	if (g)
4633fc605a2SNeilBrown 		return &g->h;
4643fc605a2SNeilBrown 	else
4653fc605a2SNeilBrown 		return NULL;
4663fc605a2SNeilBrown }
4673fc605a2SNeilBrown 
unix_gid_upcall(struct cache_detail * cd,struct cache_head * h)4683fc605a2SNeilBrown static int unix_gid_upcall(struct cache_detail *cd, struct cache_head *h)
4693fc605a2SNeilBrown {
47025da9263SEric W. Biederman 	return sunrpc_cache_pipe_upcall_timeout(cd, h);
4713fc605a2SNeilBrown }
4723fc605a2SNeilBrown 
unix_gid_request(struct cache_detail * cd,struct cache_head * h,char ** bpp,int * blen)4733fc605a2SNeilBrown static void unix_gid_request(struct cache_detail *cd,
4743fc605a2SNeilBrown 			     struct cache_head *h,
4757eaf040bSEric W. Biederman 			     char **bpp, int *blen)
4763fc605a2SNeilBrown {
4773fc605a2SNeilBrown 	char tuid[20];
4783fc605a2SNeilBrown 	struct unix_gid *ug = container_of(h, struct unix_gid, h);
4793fc605a2SNeilBrown 
4803fc605a2SNeilBrown 	snprintf(tuid, 20, "%u", from_kuid(&init_user_ns, ug->uid));
48125da9263SEric W. Biederman 	qword_add(bpp, blen, tuid);
48225da9263SEric W. Biederman 	(*bpp)[-1] = '\n';
4833fc605a2SNeilBrown }
4843fc605a2SNeilBrown 
4853fc605a2SNeilBrown static struct unix_gid *unix_gid_lookup(struct cache_detail *cd, kuid_t uid);
4863fc605a2SNeilBrown 
unix_gid_parse(struct cache_detail * cd,char * mesg,int mlen)487f559935eSArnd Bergmann static int unix_gid_parse(struct cache_detail *cd,
4883fc605a2SNeilBrown 			char *mesg, int mlen)
4893fc605a2SNeilBrown {
4903476964dSDan Carpenter 	/* uid expiry Ngid gid0 gid1 ... gidN-1 */
4913fc605a2SNeilBrown 	int id;
4923fc605a2SNeilBrown 	kuid_t uid;
4933fc605a2SNeilBrown 	int gids;
49425da9263SEric W. Biederman 	int rv;
4953fc605a2SNeilBrown 	int i;
4963fc605a2SNeilBrown 	int err;
497ccfe51a5STrond Myklebust 	time64_t expiry;
4983fc605a2SNeilBrown 	struct unix_gid ug, *ugp;
4993fc605a2SNeilBrown 
5003fc605a2SNeilBrown 	if (mesg[mlen - 1] != '\n')
5013fc605a2SNeilBrown 		return -EINVAL;
5023fc605a2SNeilBrown 	mesg[mlen-1] = 0;
5033fc605a2SNeilBrown 
5043fc605a2SNeilBrown 	rv = get_int(&mesg, &id);
5053fc605a2SNeilBrown 	if (rv)
5063fc605a2SNeilBrown 		return -EINVAL;
5073fc605a2SNeilBrown 	uid = make_kuid(current_user_ns(), id);
5083fc605a2SNeilBrown 	ug.uid = uid;
5093fc605a2SNeilBrown 
5103fc605a2SNeilBrown 	err = get_expiry(&mesg, &expiry);
5113fc605a2SNeilBrown 	if (err)
5123fc605a2SNeilBrown 		return err;
5133fc605a2SNeilBrown 
514ae2975bcSEric W. Biederman 	rv = get_int(&mesg, &gids);
5153fc605a2SNeilBrown 	if (rv || gids < 0 || gids > 8192)
5163fc605a2SNeilBrown 		return -EINVAL;
5173fc605a2SNeilBrown 
5183fc605a2SNeilBrown 	ug.gi = groups_alloc(gids);
519ccfe51a5STrond Myklebust 	if (!ug.gi)
520ae2975bcSEric W. Biederman 		return -ENOMEM;
521ae2975bcSEric W. Biederman 
52281243eacSAlexey Dobriyan 	for (i = 0 ; i < gids ; i++) {
5233fc605a2SNeilBrown 		int gid;
5243fc605a2SNeilBrown 		kgid_t kgid;
525bdcf0a42SThiago Rafael Becker 		rv = get_int(&mesg, &gid);
52673393232SStanislav Kinsbursky 		err = -EINVAL;
5273fc605a2SNeilBrown 		if (rv)
5283fc605a2SNeilBrown 			goto out;
5293fc605a2SNeilBrown 		kgid = make_kgid(current_user_ns(), gid);
5303fc605a2SNeilBrown 		if (!gid_valid(kgid))
53173393232SStanislav Kinsbursky 			goto out;
5323fc605a2SNeilBrown 		ug.gi->gid[i] = kgid;
5339e469e30SEric W. Biederman 	}
5343fc605a2SNeilBrown 
5353fc605a2SNeilBrown 	groups_sort(ug.gi);
5363fc605a2SNeilBrown 	ugp = unix_gid_lookup(cd, uid);
5373fc605a2SNeilBrown 	if (ugp) {
53873393232SStanislav Kinsbursky 		struct cache_head *ch;
5393fc605a2SNeilBrown 		ug.h.flags = 0;
5403fc605a2SNeilBrown 		ug.h.expiry_time = expiry;
5413fc605a2SNeilBrown 		ch = sunrpc_cache_update(cd,
5423fc605a2SNeilBrown 					 &ug.h, &ugp->h,
5433fc605a2SNeilBrown 					 unix_gid_hash(uid));
5443fc605a2SNeilBrown 		if (!ch)
5453fc605a2SNeilBrown 			err = -ENOMEM;
5463fc605a2SNeilBrown 		else {
5473fc605a2SNeilBrown 			err = 0;
5483fc605a2SNeilBrown 			cache_put(ch, cd);
5493fc605a2SNeilBrown 		}
5503fc605a2SNeilBrown 	} else
5513fc605a2SNeilBrown 		err = -ENOMEM;
552ccfe51a5STrond Myklebust  out:
5533fc605a2SNeilBrown 	if (ug.gi)
5543fc605a2SNeilBrown 		put_group_info(ug.gi);
5553fc605a2SNeilBrown 	return err;
5563fc605a2SNeilBrown }
5573fc605a2SNeilBrown 
unix_gid_show(struct seq_file * m,struct cache_detail * cd,struct cache_head * h)5583fc605a2SNeilBrown static int unix_gid_show(struct seq_file *m,
5593fc605a2SNeilBrown 			 struct cache_detail *cd,
5603fc605a2SNeilBrown 			 struct cache_head *h)
5613fc605a2SNeilBrown {
5623fc605a2SNeilBrown 	struct user_namespace *user_ns = m->file->f_cred->user_ns;
5633fc605a2SNeilBrown 	struct unix_gid *ug;
5643fc605a2SNeilBrown 	int i;
5653fc605a2SNeilBrown 	int glen;
5663fc605a2SNeilBrown 
5673fc605a2SNeilBrown 	if (h == NULL) {
56825da9263SEric W. Biederman 		seq_puts(m, "#uid cnt: gids...\n");
5693fc605a2SNeilBrown 		return 0;
57081243eacSAlexey Dobriyan 	}
5713fc605a2SNeilBrown 	ug = container_of(h, struct unix_gid, h);
5723fc605a2SNeilBrown 	if (test_bit(CACHE_VALID, &h->flags) &&
5733fc605a2SNeilBrown 	    !test_bit(CACHE_NEGATIVE, &h->flags))
5743fc605a2SNeilBrown 		glen = ug->gi->ngroups;
575ee24eac3SBhumika Goyal 	else
5763fc605a2SNeilBrown 		glen = 0;
5773fc605a2SNeilBrown 
5783fc605a2SNeilBrown 	seq_printf(m, "%u %d:", from_kuid_munged(user_ns, ug->uid), glen);
5793fc605a2SNeilBrown 	for (i = 0; i < glen; i++)
58065286b88STrond Myklebust 		seq_printf(m, " %d", from_kgid_munged(user_ns, ug->gi->gid[i]));
58173fb847aSStanislav Kinsbursky 	seq_printf(m, "\n");
5823fc605a2SNeilBrown 	return 0;
5833fc605a2SNeilBrown }
5843fc605a2SNeilBrown 
5853fc605a2SNeilBrown static const struct cache_detail unix_gid_cache_template = {
5863fc605a2SNeilBrown 	.owner		= THIS_MODULE,
5873fc605a2SNeilBrown 	.hash_size	= GID_HASHMAX,
5883fc605a2SNeilBrown 	.name		= "auth.unix.gid",
5893fc605a2SNeilBrown 	.cache_put	= unix_gid_put,
59073393232SStanislav Kinsbursky 	.cache_upcall	= unix_gid_upcall,
59173393232SStanislav Kinsbursky 	.cache_request	= unix_gid_request,
59273393232SStanislav Kinsbursky 	.cache_parse	= unix_gid_parse,
59373393232SStanislav Kinsbursky 	.cache_show	= unix_gid_show,
59473393232SStanislav Kinsbursky 	.match		= unix_gid_match,
59573393232SStanislav Kinsbursky 	.init		= unix_gid_init,
59673393232SStanislav Kinsbursky 	.update		= unix_gid_update,
59773393232SStanislav Kinsbursky 	.alloc		= unix_gid_alloc,
59873393232SStanislav Kinsbursky };
59973393232SStanislav Kinsbursky 
unix_gid_cache_create(struct net * net)60073393232SStanislav Kinsbursky int unix_gid_cache_create(struct net *net)
60173393232SStanislav Kinsbursky {
60273393232SStanislav Kinsbursky 	struct sunrpc_net *sn = net_generic(net, sunrpc_net_id);
60373393232SStanislav Kinsbursky 	struct cache_detail *cd;
60473393232SStanislav Kinsbursky 	int err;
60573393232SStanislav Kinsbursky 
60673393232SStanislav Kinsbursky 	cd = cache_create_net(&unix_gid_cache_template, net);
60773393232SStanislav Kinsbursky 	if (IS_ERR(cd))
60873393232SStanislav Kinsbursky 		return PTR_ERR(cd);
60973393232SStanislav Kinsbursky 	err = cache_register_net(cd, net);
61073393232SStanislav Kinsbursky 	if (err) {
61173393232SStanislav Kinsbursky 		cache_destroy_net(cd, net);
61273393232SStanislav Kinsbursky 		return err;
61373393232SStanislav Kinsbursky 	}
61473393232SStanislav Kinsbursky 	sn->unix_gid_cache = cd;
61573393232SStanislav Kinsbursky 	return 0;
61673393232SStanislav Kinsbursky }
61773393232SStanislav Kinsbursky 
unix_gid_cache_destroy(struct net * net)61873393232SStanislav Kinsbursky void unix_gid_cache_destroy(struct net *net)
6197eaf040bSEric W. Biederman {
6203fc605a2SNeilBrown 	struct sunrpc_net *sn = net_generic(net, sunrpc_net_id);
6213fc605a2SNeilBrown 	struct cache_detail *cd = sn->unix_gid_cache;
6223fc605a2SNeilBrown 
6233fc605a2SNeilBrown 	sn->unix_gid_cache = NULL;
6243fc605a2SNeilBrown 	cache_purge(cd);
625fd5d2f78STrond Myklebust 	cache_unregister_net(cd, net);
6263fc605a2SNeilBrown 	cache_destroy_net(cd, net);
6273fc605a2SNeilBrown }
6283fc605a2SNeilBrown 
unix_gid_lookup(struct cache_detail * cd,kuid_t uid)6293fc605a2SNeilBrown static struct unix_gid *unix_gid_lookup(struct cache_detail *cd, kuid_t uid)
6303fc605a2SNeilBrown {
6313fc605a2SNeilBrown 	struct unix_gid ug;
6327eaf040bSEric W. Biederman 	struct cache_head *ch;
6333fc605a2SNeilBrown 
634dc83d6e2SJ. Bruce Fields 	ug.uid = uid;
635dc83d6e2SJ. Bruce Fields 	ch = sunrpc_cache_lookup_rcu(cd, &ug.h, unix_gid_hash(uid));
636dc83d6e2SJ. Bruce Fields 	if (ch)
63773393232SStanislav Kinsbursky 		return container_of(ch, struct unix_gid, h);
63873393232SStanislav Kinsbursky 	else
639dc83d6e2SJ. Bruce Fields 		return NULL;
64073393232SStanislav Kinsbursky }
6413fc605a2SNeilBrown 
unix_gid_find(kuid_t uid,struct svc_rqst * rqstp)642dc83d6e2SJ. Bruce Fields static struct group_info *unix_gid_find(kuid_t uid, struct svc_rqst *rqstp)
64373393232SStanislav Kinsbursky {
644dc83d6e2SJ. Bruce Fields 	struct unix_gid *ug;
6453fc605a2SNeilBrown 	struct group_info *gi;
646dc83d6e2SJ. Bruce Fields 	int ret;
6471ebede86SNeilBrown 	struct sunrpc_net *sn = net_generic(rqstp->rq_xprt->xpt_net,
6481ebede86SNeilBrown 					    sunrpc_net_id);
6493fc605a2SNeilBrown 
650dc83d6e2SJ. Bruce Fields 	ug = unix_gid_lookup(sn->unix_gid_cache, uid);
65173393232SStanislav Kinsbursky 	if (!ug)
652dc83d6e2SJ. Bruce Fields 		return ERR_PTR(-EAGAIN);
6533fc605a2SNeilBrown 	ret = cache_check(sn->unix_gid_cache, &ug->h, &rqstp->rq_chandle);
654dc83d6e2SJ. Bruce Fields 	switch (ret) {
6553fc605a2SNeilBrown 	case -ENOENT:
6563fc605a2SNeilBrown 		return ERR_PTR(-ENOENT);
6573fc605a2SNeilBrown 	case -ETIMEDOUT:
6583ab4d8b1SJ. Bruce Fields 		return ERR_PTR(-ESHUTDOWN);
6591da177e4SLinus Torvalds 	case 0:
6601da177e4SLinus Torvalds 		gi = get_group_info(ug->gi);
661f15364bdSAurélien Charbon 		cache_put(&ug->h, sn->unix_gid_cache);
662f15364bdSAurélien Charbon 		return gi;
6631a9917c2SNeilBrown 	default:
664dc83d6e2SJ. Bruce Fields 		return ERR_PTR(-EAGAIN);
665dc83d6e2SJ. Bruce Fields 	}
6663be4479fSPavel Emelyanov }
66790d51b02SPavel Emelyanov 
66890d51b02SPavel Emelyanov enum svc_auth_status
svcauth_unix_set_client(struct svc_rqst * rqstp)6691da177e4SLinus Torvalds svcauth_unix_set_client(struct svc_rqst *rqstp)
670f15364bdSAurélien Charbon {
671f15364bdSAurélien Charbon 	struct sockaddr_in *sin;
672f15364bdSAurélien Charbon 	struct sockaddr_in6 *sin6, sin6_storage;
673f15364bdSAurélien Charbon 	struct ip_map *ipm;
674b301e82cSBrian Haley 	struct group_info *gi;
675f15364bdSAurélien Charbon 	struct svc_cred *cred = &rqstp->rq_cred;
676f15364bdSAurélien Charbon 	struct svc_xprt *xprt = rqstp->rq_xprt;
677f15364bdSAurélien Charbon 	struct net *net = xprt->xpt_net;
678f15364bdSAurélien Charbon 	struct sunrpc_net *sn = net_generic(net, sunrpc_net_id);
679f15364bdSAurélien Charbon 
680f15364bdSAurélien Charbon 	switch (rqstp->rq_addr.ss_family) {
681f15364bdSAurélien Charbon 	case AF_INET:
682f15364bdSAurélien Charbon 		sin = svc_addr_in(rqstp);
6831da177e4SLinus Torvalds 		sin6 = &sin6_storage;
6841da177e4SLinus Torvalds 		ipv6_addr_set_v4mapped(sin->sin_addr.s_addr, &sin6->sin6_addr);
6855c2465dfSChuck Lever 		break;
6861da177e4SLinus Torvalds 	case AF_INET6:
6875c2465dfSChuck Lever 		sin6 = svc_addr_in6(rqstp);
6883be4479fSPavel Emelyanov 		break;
6897b2b1feeSGreg Banks 	default:
69090d51b02SPavel Emelyanov 		BUG();
691f15364bdSAurélien Charbon 	}
6921da177e4SLinus Torvalds 
6931da177e4SLinus Torvalds 	rqstp->rq_client = NULL;
6941da177e4SLinus Torvalds 	if (rqstp->rq_proc == 0)
6951da177e4SLinus Torvalds 		goto out;
69690d51b02SPavel Emelyanov 
6971da177e4SLinus Torvalds 	rqstp->rq_auth_stat = rpc_autherr_badcred;
6981da177e4SLinus Torvalds 	ipm = ip_map_cached_get(xprt);
699e0bb89efSJ.Bruce Fields 	if (ipm == NULL)
7001ebede86SNeilBrown 		ipm = __ip_map_lookup(sn->ip_map_cache, rqstp->rq_server->sv_program->pg_class,
7011ebede86SNeilBrown 				    &sin6->sin6_addr);
7021da177e4SLinus Torvalds 
7031da177e4SLinus Torvalds 	if (ipm == NULL)
7041da177e4SLinus Torvalds 		return SVC_DENIED;
7051da177e4SLinus Torvalds 
7061da177e4SLinus Torvalds 	switch (cache_check(sn->ip_map_cache, &ipm->h, &rqstp->rq_chandle)) {
707efc36aa5SNeilBrown 		default:
7083be4479fSPavel Emelyanov 			BUG();
7091da177e4SLinus Torvalds 		case -ETIMEDOUT:
7101da177e4SLinus Torvalds 			return SVC_CLOSE;
711dc83d6e2SJ. Bruce Fields 		case -EAGAIN:
712dc83d6e2SJ. Bruce Fields 			return SVC_DROP;
713dc83d6e2SJ. Bruce Fields 		case -ENOENT:
714dc83d6e2SJ. Bruce Fields 			return SVC_DENIED;
715dc83d6e2SJ. Bruce Fields 		case 0:
7161ebede86SNeilBrown 			rqstp->rq_client = &ipm->m_client->h;
7171ebede86SNeilBrown 			kref_get(&rqstp->rq_client->ref);
718dc83d6e2SJ. Bruce Fields 			ip_map_cached_put(xprt, ipm);
719dc83d6e2SJ. Bruce Fields 			break;
720dc83d6e2SJ. Bruce Fields 	}
721dc83d6e2SJ. Bruce Fields 
722dc83d6e2SJ. Bruce Fields 	gi = unix_gid_find(cred->cr_uid, rqstp);
723dc83d6e2SJ. Bruce Fields 	switch (PTR_ERR(gi)) {
7245c2465dfSChuck Lever 	case -EAGAIN:
7255c2465dfSChuck Lever 		return SVC_DROP;
7265c2465dfSChuck Lever 	case -ESHUTDOWN:
7271da177e4SLinus Torvalds 		return SVC_CLOSE;
7281da177e4SLinus Torvalds 	case -ENOENT:
7291da177e4SLinus Torvalds 		break;
73024c3767eSTrond Myklebust 	default:
7313ab4d8b1SJ. Bruce Fields 		put_group_info(cred->cr_group_info);
7321da177e4SLinus Torvalds 		cred->cr_group_info = gi;
733438623a0SChuck Lever 	}
7341da177e4SLinus Torvalds 
7351da177e4SLinus Torvalds out:
7361da177e4SLinus Torvalds 	rqstp->rq_auth_stat = rpc_auth_ok;
7371da177e4SLinus Torvalds 	return SVC_OK;
7381da177e4SLinus Torvalds }
7391da177e4SLinus Torvalds EXPORT_SYMBOL_GPL(svcauth_unix_set_client);
7401da177e4SLinus Torvalds 
7411da177e4SLinus Torvalds /**
7421da177e4SLinus Torvalds  * svcauth_null_accept - Decode and validate incoming RPC_AUTH_NULL credential
7431da177e4SLinus Torvalds  * @rqstp: RPC transaction
744438623a0SChuck Lever  *
7451da177e4SLinus Torvalds  * Return values:
7461da177e4SLinus Torvalds  *   %SVC_OK: Both credential and verifier are valid
74776994313SAlexey Dobriyan  *   %SVC_DENIED: Credential or verifier is not valid
7481da177e4SLinus Torvalds  *   %SVC_GARBAGE: Failed to decode credential or verifier
749438623a0SChuck Lever  *   %SVC_CLOSE: Temporary failure
7501da177e4SLinus Torvalds  *
7511da177e4SLinus Torvalds  * rqstp->rq_auth_stat is set as mandated by RFC 5531.
7521da177e4SLinus Torvalds  */
7531da177e4SLinus Torvalds static enum svc_auth_status
svcauth_null_accept(struct svc_rqst * rqstp)754bf37f794SEric W. Biederman svcauth_null_accept(struct svc_rqst *rqstp)
755bf37f794SEric W. Biederman {
7561da177e4SLinus Torvalds 	struct xdr_stream *xdr = &rqstp->rq_arg_stream;
7571da177e4SLinus Torvalds 	struct svc_cred	*cred = &rqstp->rq_cred;
7581ebede86SNeilBrown 	u32 flavor, len;
7591da177e4SLinus Torvalds 	void *body;
7601da177e4SLinus Torvalds 
76176994313SAlexey Dobriyan 	/* Length of Call's credential body field: */
76276994313SAlexey Dobriyan 	if (xdr_stream_decode_u32(xdr, &len) < 0)
7631da177e4SLinus Torvalds 		return SVC_GARBAGE;
764d5497fc6SJ. Bruce Fields 	if (len != 0) {
7651da177e4SLinus Torvalds 		rqstp->rq_auth_stat = rpc_autherr_badcred;
7661da177e4SLinus Torvalds 		return SVC_DENIED;
7671da177e4SLinus Torvalds 	}
7681da177e4SLinus Torvalds 
7691da177e4SLinus Torvalds 	/* Call's verf field: */
7701da177e4SLinus Torvalds 	if (xdr_stream_decode_opaque_auth(xdr, &flavor, &body, &len) < 0)
7711da177e4SLinus Torvalds 		return SVC_GARBAGE;
7721da177e4SLinus Torvalds 	if (flavor != RPC_AUTH_NULL || len != 0) {
7731da177e4SLinus Torvalds 		rqstp->rq_auth_stat = rpc_autherr_badverf;
7741da177e4SLinus Torvalds 		return SVC_DENIED;
7751da177e4SLinus Torvalds 	}
7761da177e4SLinus Torvalds 
7771da177e4SLinus Torvalds 	/* Signal that mapping to nobody uid/gid is required */
7781da177e4SLinus Torvalds 	cred->cr_uid = INVALID_UID;
7791da177e4SLinus Torvalds 	cred->cr_gid = INVALID_GID;
7801da177e4SLinus Torvalds 	cred->cr_group_info = groups_alloc(0);
7811da177e4SLinus Torvalds 	if (cred->cr_group_info == NULL)
7821da177e4SLinus Torvalds 		return SVC_CLOSE; /* kmalloc failure - client must retry */
7831da177e4SLinus Torvalds 
7841da177e4SLinus Torvalds 	if (xdr_stream_encode_opaque_auth(&rqstp->rq_res_stream,
7851da177e4SLinus Torvalds 					  RPC_AUTH_NULL, NULL, 0) < 0)
7861da177e4SLinus Torvalds 		return SVC_CLOSE;
7871da177e4SLinus Torvalds 	if (!svcxdr_set_accept_stat(rqstp))
7881da177e4SLinus Torvalds 		return SVC_CLOSE;
7891da177e4SLinus Torvalds 
7901da177e4SLinus Torvalds 	rqstp->rq_cred.cr_flavor = RPC_AUTH_NULL;
7911da177e4SLinus Torvalds 	return SVC_OK;
7921da177e4SLinus Torvalds }
793*74aaf96fSChuck Lever 
794*74aaf96fSChuck Lever static int
svcauth_null_release(struct svc_rqst * rqstp)795*74aaf96fSChuck Lever svcauth_null_release(struct svc_rqst *rqstp)
796*74aaf96fSChuck Lever {
797*74aaf96fSChuck Lever 	if (rqstp->rq_client)
798*74aaf96fSChuck Lever 		auth_domain_put(rqstp->rq_client);
799*74aaf96fSChuck Lever 	rqstp->rq_client = NULL;
800*74aaf96fSChuck Lever 	if (rqstp->rq_cred.cr_group_info)
801*74aaf96fSChuck Lever 		put_group_info(rqstp->rq_cred.cr_group_info);
802*74aaf96fSChuck Lever 	rqstp->rq_cred.cr_group_info = NULL;
803*74aaf96fSChuck Lever 
804*74aaf96fSChuck Lever 	return 0; /* don't drop */
805*74aaf96fSChuck Lever }
806*74aaf96fSChuck Lever 
807*74aaf96fSChuck Lever 
808*74aaf96fSChuck Lever struct auth_ops svcauth_null = {
809*74aaf96fSChuck Lever 	.name		= "null",
810*74aaf96fSChuck Lever 	.owner		= THIS_MODULE,
811*74aaf96fSChuck Lever 	.flavour	= RPC_AUTH_NULL,
812*74aaf96fSChuck Lever 	.accept		= svcauth_null_accept,
813*74aaf96fSChuck Lever 	.release	= svcauth_null_release,
814*74aaf96fSChuck Lever 	.set_client	= svcauth_unix_set_client,
815*74aaf96fSChuck Lever };
816*74aaf96fSChuck Lever 
817*74aaf96fSChuck Lever 
818*74aaf96fSChuck Lever /**
819*74aaf96fSChuck Lever  * svcauth_tls_accept - Decode and validate incoming RPC_AUTH_TLS credential
820*74aaf96fSChuck Lever  * @rqstp: RPC transaction
821*74aaf96fSChuck Lever  *
822*74aaf96fSChuck Lever  * Return values:
823*74aaf96fSChuck Lever  *   %SVC_OK: Both credential and verifier are valid
824*74aaf96fSChuck Lever  *   %SVC_DENIED: Credential or verifier is not valid
825*74aaf96fSChuck Lever  *   %SVC_GARBAGE: Failed to decode credential or verifier
826*74aaf96fSChuck Lever  *   %SVC_CLOSE: Temporary failure
827*74aaf96fSChuck Lever  *
828*74aaf96fSChuck Lever  * rqstp->rq_auth_stat is set as mandated by RFC 5531.
829*74aaf96fSChuck Lever  */
830*74aaf96fSChuck Lever static enum svc_auth_status
svcauth_tls_accept(struct svc_rqst * rqstp)831*74aaf96fSChuck Lever svcauth_tls_accept(struct svc_rqst *rqstp)
832*74aaf96fSChuck Lever {
833*74aaf96fSChuck Lever 	struct xdr_stream *xdr = &rqstp->rq_arg_stream;
834*74aaf96fSChuck Lever 	struct svc_cred	*cred = &rqstp->rq_cred;
835*74aaf96fSChuck Lever 	struct svc_xprt *xprt = rqstp->rq_xprt;
836*74aaf96fSChuck Lever 	u32 flavor, len;
837*74aaf96fSChuck Lever 	void *body;
838*74aaf96fSChuck Lever 	__be32 *p;
839*74aaf96fSChuck Lever 
840*74aaf96fSChuck Lever 	/* Length of Call's credential body field: */
841*74aaf96fSChuck Lever 	if (xdr_stream_decode_u32(xdr, &len) < 0)
842*74aaf96fSChuck Lever 		return SVC_GARBAGE;
843*74aaf96fSChuck Lever 	if (len != 0) {
844*74aaf96fSChuck Lever 		rqstp->rq_auth_stat = rpc_autherr_badcred;
845*74aaf96fSChuck Lever 		return SVC_DENIED;
846*74aaf96fSChuck Lever 	}
847*74aaf96fSChuck Lever 
848*74aaf96fSChuck Lever 	/* Call's verf field: */
849*74aaf96fSChuck Lever 	if (xdr_stream_decode_opaque_auth(xdr, &flavor, &body, &len) < 0)
850*74aaf96fSChuck Lever 		return SVC_GARBAGE;
851*74aaf96fSChuck Lever 	if (flavor != RPC_AUTH_NULL || len != 0) {
852438623a0SChuck Lever 		rqstp->rq_auth_stat = rpc_autherr_badverf;
8531da177e4SLinus Torvalds 		return SVC_DENIED;
8541da177e4SLinus Torvalds 	}
8551da177e4SLinus Torvalds 
8561da177e4SLinus Torvalds 	/* AUTH_TLS is not valid on non-NULL procedures */
857ccfe51a5STrond Myklebust 	if (rqstp->rq_proc != 0) {
8581da177e4SLinus Torvalds 		rqstp->rq_auth_stat = rpc_autherr_badcred;
8591da177e4SLinus Torvalds 		return SVC_DENIED;
8601da177e4SLinus Torvalds 	}
8611da177e4SLinus Torvalds 
8621da177e4SLinus Torvalds 	/* Signal that mapping to nobody uid/gid is required */
8631da177e4SLinus Torvalds 	cred->cr_uid = INVALID_UID;
8641da177e4SLinus Torvalds 	cred->cr_gid = INVALID_GID;
8651da177e4SLinus Torvalds 	cred->cr_group_info = groups_alloc(0);
86676994313SAlexey Dobriyan 	if (cred->cr_group_info == NULL)
8671da177e4SLinus Torvalds 		return SVC_CLOSE;
8681da177e4SLinus Torvalds 
869d8ed029dSAlexey Dobriyan 	if (xprt->xpt_ops->xpo_handshake) {
8701da177e4SLinus Torvalds 		p = xdr_reserve_space(&rqstp->rq_res_stream, XDR_UNIT * 2 + 8);
871afe3c3fdSJ. Bruce Fields 		if (!p)
872afe3c3fdSJ. Bruce Fields 			return SVC_CLOSE;
873afe3c3fdSJ. Bruce Fields 		trace_svc_tls_start(xprt);
874afe3c3fdSJ. Bruce Fields 		*p++ = rpc_auth_null;
875afe3c3fdSJ. Bruce Fields 		*p++ = cpu_to_be32(8);
876afe3c3fdSJ. Bruce Fields 		memcpy(p, "STARTTLS", 8);
877afe3c3fdSJ. Bruce Fields 
878ccfe51a5STrond Myklebust 		set_bit(XPT_HANDSHAKE, &xprt->xpt_flags);
879ccfe51a5STrond Myklebust 		svc_xprt_enqueue(xprt);
880ccfe51a5STrond Myklebust 	} else {
881ccfe51a5STrond Myklebust 		trace_svc_tls_unavailable(xprt);
88276994313SAlexey Dobriyan 		if (xdr_stream_encode_opaque_auth(&rqstp->rq_res_stream,
8835786461bSKinglong Mee 						  RPC_AUTH_NULL, NULL, 0) < 0)
8841da177e4SLinus Torvalds 			return SVC_CLOSE;
8851da177e4SLinus Torvalds 	}
8861da177e4SLinus Torvalds 	if (!svcxdr_set_accept_stat(rqstp))
8871ebede86SNeilBrown 		return SVC_CLOSE;
888ae2975bcSEric W. Biederman 
889ccfe51a5STrond Myklebust 	rqstp->rq_cred.cr_flavor = RPC_AUTH_TLS;
89081243eacSAlexey Dobriyan 	return SVC_OK;
891ae2975bcSEric W. Biederman }
892bdcf0a42SThiago Rafael Becker 
89376994313SAlexey Dobriyan struct auth_ops svcauth_tls = {
894438623a0SChuck Lever 	.name		= "tls",
8951da177e4SLinus Torvalds 	.owner		= THIS_MODULE,
8961da177e4SLinus Torvalds 	.flavour	= RPC_AUTH_TLS,
8971da177e4SLinus Torvalds 	.accept		= svcauth_tls_accept,
8981da177e4SLinus Torvalds 	.release	= svcauth_null_release,
89976994313SAlexey Dobriyan 	.set_client	= svcauth_unix_set_client,
90076994313SAlexey Dobriyan };
9011da177e4SLinus Torvalds 
902d5497fc6SJ. Bruce Fields 
9031da177e4SLinus Torvalds /**
9041da177e4SLinus Torvalds  * svcauth_unix_accept - Decode and validate incoming RPC_AUTH_SYS credential
9051da177e4SLinus Torvalds  * @rqstp: RPC transaction
906438623a0SChuck Lever  *
9071da177e4SLinus Torvalds  * Return values:
9081da177e4SLinus Torvalds  *   %SVC_OK: Both credential and verifier are valid
9091da177e4SLinus Torvalds  *   %SVC_DENIED: Credential or verifier is not valid
9101da177e4SLinus Torvalds  *   %SVC_GARBAGE: Failed to decode credential or verifier
9111da177e4SLinus Torvalds  *   %SVC_CLOSE: Temporary failure
9121da177e4SLinus Torvalds  *
9131da177e4SLinus Torvalds  * rqstp->rq_auth_stat is set as mandated by RFC 5531.
9141da177e4SLinus Torvalds  */
9151da177e4SLinus Torvalds static enum svc_auth_status
svcauth_unix_accept(struct svc_rqst * rqstp)9161da177e4SLinus Torvalds svcauth_unix_accept(struct svc_rqst *rqstp)
9171da177e4SLinus Torvalds {
9181da177e4SLinus Torvalds 	struct xdr_stream *xdr = &rqstp->rq_arg_stream;
9191da177e4SLinus Torvalds 	struct svc_cred	*cred = &rqstp->rq_cred;
9201da177e4SLinus Torvalds 	struct user_namespace *userns;
9211da177e4SLinus Torvalds 	u32 flavor, len, i;
9221da177e4SLinus Torvalds 	void *body;
9231da177e4SLinus Torvalds 	__be32 *p;
9241da177e4SLinus Torvalds 
9251da177e4SLinus Torvalds 	/*
9261da177e4SLinus Torvalds 	 * This implementation ignores the length of the Call's
9271da177e4SLinus Torvalds 	 * credential body field and the timestamp and machinename
9281da177e4SLinus Torvalds 	 * fields.
9291da177e4SLinus Torvalds 	 */
9301da177e4SLinus Torvalds 	p = xdr_inline_decode(xdr, XDR_UNIT * 3);
9311da177e4SLinus Torvalds 	if (!p)
9321da177e4SLinus Torvalds 		return SVC_GARBAGE;
9331da177e4SLinus Torvalds 	len = be32_to_cpup(p + 2);
9341da177e4SLinus Torvalds 	if (len > RPC_MAX_MACHINENAME)
9351da177e4SLinus Torvalds 		return SVC_GARBAGE;
936ee24eac3SBhumika Goyal 	if (!xdr_inline_decode(xdr, len))
937d05cc104SStanislav Kinsbursky 		return SVC_GARBAGE;
938d05cc104SStanislav Kinsbursky 
939d05cc104SStanislav Kinsbursky 	/*
940d05cc104SStanislav Kinsbursky 	 * Note: we skip uid_valid()/gid_valid() checks here for
94165286b88STrond Myklebust 	 * backwards compatibility with clients that use -1 id's.
94273fb847aSStanislav Kinsbursky 	 * Instead, -1 uid or gid is later mapped to the
943d05cc104SStanislav Kinsbursky 	 * (export-specific) anonymous id by nfsd_setuser.
944d05cc104SStanislav Kinsbursky 	 * Supplementary gid's will be left alone.
945d05cc104SStanislav Kinsbursky 	 */
946d05cc104SStanislav Kinsbursky 	userns = (rqstp->rq_xprt && rqstp->rq_xprt->xpt_cred) ?
947d05cc104SStanislav Kinsbursky 		rqstp->rq_xprt->xpt_cred->user_ns : &init_user_ns;
948d05cc104SStanislav Kinsbursky 	if (xdr_stream_decode_u32(xdr, &i) < 0)
949d05cc104SStanislav Kinsbursky 		return SVC_GARBAGE;
950d05cc104SStanislav Kinsbursky 	cred->cr_uid = make_kuid(userns, i);
95190d51b02SPavel Emelyanov 	if (xdr_stream_decode_u32(xdr, &i) < 0)
95290d51b02SPavel Emelyanov 		return SVC_GARBAGE;
95390d51b02SPavel Emelyanov 	cred->cr_gid = make_kgid(userns, i);
954d05cc104SStanislav Kinsbursky 
955d05cc104SStanislav Kinsbursky 	if (xdr_stream_decode_u32(xdr, &len) < 0)
95690d51b02SPavel Emelyanov 		return SVC_GARBAGE;
957d05cc104SStanislav Kinsbursky 	if (len > UNX_NGROUPS)
958d05cc104SStanislav Kinsbursky 		goto badcred;
959d05cc104SStanislav Kinsbursky 	p = xdr_inline_decode(xdr, XDR_UNIT * len);
96090d51b02SPavel Emelyanov 	if (!p)
961d05cc104SStanislav Kinsbursky 		return SVC_GARBAGE;
962d05cc104SStanislav Kinsbursky 	cred->cr_group_info = groups_alloc(len);
963d05cc104SStanislav Kinsbursky 	if (cred->cr_group_info == NULL)
964d05cc104SStanislav Kinsbursky 		return SVC_CLOSE;
96590d51b02SPavel Emelyanov 	for (i = 0; i < len; i++) {
96690d51b02SPavel Emelyanov 		kgid_t kgid = make_kgid(userns, be32_to_cpup(p++));
96790d51b02SPavel Emelyanov 		cred->cr_group_info->gid[i] = kgid;
96890d51b02SPavel Emelyanov 	}
96990d51b02SPavel Emelyanov 	groups_sort(cred->cr_group_info);
97090d51b02SPavel Emelyanov 
971d05cc104SStanislav Kinsbursky 	/* Call's verf field: */
972d05cc104SStanislav Kinsbursky 	if (xdr_stream_decode_opaque_auth(xdr, &flavor, &body, &len) < 0)
97390d51b02SPavel Emelyanov 		return SVC_GARBAGE;
974d05cc104SStanislav Kinsbursky 	if (flavor != RPC_AUTH_NULL || len != 0) {
975d05cc104SStanislav Kinsbursky 		rqstp->rq_auth_stat = rpc_autherr_badverf;
976d05cc104SStanislav Kinsbursky 		return SVC_DENIED;
977d05cc104SStanislav Kinsbursky 	}
97890d51b02SPavel Emelyanov 
979 	if (xdr_stream_encode_opaque_auth(&rqstp->rq_res_stream,
980 					  RPC_AUTH_NULL, NULL, 0) < 0)
981 		return SVC_CLOSE;
982 	if (!svcxdr_set_accept_stat(rqstp))
983 		return SVC_CLOSE;
984 
985 	rqstp->rq_cred.cr_flavor = RPC_AUTH_UNIX;
986 	return SVC_OK;
987 
988 badcred:
989 	rqstp->rq_auth_stat = rpc_autherr_badcred;
990 	return SVC_DENIED;
991 }
992 
993 static int
svcauth_unix_release(struct svc_rqst * rqstp)994 svcauth_unix_release(struct svc_rqst *rqstp)
995 {
996 	/* Verifier (such as it is) is already in place.
997 	 */
998 	if (rqstp->rq_client)
999 		auth_domain_put(rqstp->rq_client);
1000 	rqstp->rq_client = NULL;
1001 	if (rqstp->rq_cred.cr_group_info)
1002 		put_group_info(rqstp->rq_cred.cr_group_info);
1003 	rqstp->rq_cred.cr_group_info = NULL;
1004 
1005 	return 0;
1006 }
1007 
1008 
1009 struct auth_ops svcauth_unix = {
1010 	.name		= "unix",
1011 	.owner		= THIS_MODULE,
1012 	.flavour	= RPC_AUTH_UNIX,
1013 	.accept		= svcauth_unix_accept,
1014 	.release	= svcauth_unix_release,
1015 	.domain_release	= svcauth_unix_domain_release,
1016 	.set_client	= svcauth_unix_set_client,
1017 };
1018 
1019 static const struct cache_detail ip_map_cache_template = {
1020 	.owner		= THIS_MODULE,
1021 	.hash_size	= IP_HASHMAX,
1022 	.name		= "auth.unix.ip",
1023 	.cache_put	= ip_map_put,
1024 	.cache_upcall	= ip_map_upcall,
1025 	.cache_request	= ip_map_request,
1026 	.cache_parse	= ip_map_parse,
1027 	.cache_show	= ip_map_show,
1028 	.match		= ip_map_match,
1029 	.init		= ip_map_init,
1030 	.update		= update,
1031 	.alloc		= ip_map_alloc,
1032 };
1033 
ip_map_cache_create(struct net * net)1034 int ip_map_cache_create(struct net *net)
1035 {
1036 	struct sunrpc_net *sn = net_generic(net, sunrpc_net_id);
1037 	struct cache_detail *cd;
1038 	int err;
1039 
1040 	cd = cache_create_net(&ip_map_cache_template, net);
1041 	if (IS_ERR(cd))
1042 		return PTR_ERR(cd);
1043 	err = cache_register_net(cd, net);
1044 	if (err) {
1045 		cache_destroy_net(cd, net);
1046 		return err;
1047 	}
1048 	sn->ip_map_cache = cd;
1049 	return 0;
1050 }
1051 
ip_map_cache_destroy(struct net * net)1052 void ip_map_cache_destroy(struct net *net)
1053 {
1054 	struct sunrpc_net *sn = net_generic(net, sunrpc_net_id);
1055 	struct cache_detail *cd = sn->ip_map_cache;
1056 
1057 	sn->ip_map_cache = NULL;
1058 	cache_purge(cd);
1059 	cache_unregister_net(cd, net);
1060 	cache_destroy_net(cd, net);
1061 }
1062