18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci#include <linux/types.h>
38c2ecf20Sopenharmony_ci#include <linux/sched.h>
48c2ecf20Sopenharmony_ci#include <linux/module.h>
58c2ecf20Sopenharmony_ci#include <linux/sunrpc/types.h>
68c2ecf20Sopenharmony_ci#include <linux/sunrpc/xdr.h>
78c2ecf20Sopenharmony_ci#include <linux/sunrpc/svcsock.h>
88c2ecf20Sopenharmony_ci#include <linux/sunrpc/svcauth.h>
98c2ecf20Sopenharmony_ci#include <linux/sunrpc/gss_api.h>
108c2ecf20Sopenharmony_ci#include <linux/sunrpc/addr.h>
118c2ecf20Sopenharmony_ci#include <linux/err.h>
128c2ecf20Sopenharmony_ci#include <linux/seq_file.h>
138c2ecf20Sopenharmony_ci#include <linux/hash.h>
148c2ecf20Sopenharmony_ci#include <linux/string.h>
158c2ecf20Sopenharmony_ci#include <linux/slab.h>
168c2ecf20Sopenharmony_ci#include <net/sock.h>
178c2ecf20Sopenharmony_ci#include <net/ipv6.h>
188c2ecf20Sopenharmony_ci#include <linux/kernel.h>
198c2ecf20Sopenharmony_ci#include <linux/user_namespace.h>
208c2ecf20Sopenharmony_ci#define RPCDBG_FACILITY	RPCDBG_AUTH
218c2ecf20Sopenharmony_ci
228c2ecf20Sopenharmony_ci
238c2ecf20Sopenharmony_ci#include "netns.h"
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_ci/*
268c2ecf20Sopenharmony_ci * AUTHUNIX and AUTHNULL credentials are both handled here.
278c2ecf20Sopenharmony_ci * AUTHNULL is treated just like AUTHUNIX except that the uid/gid
288c2ecf20Sopenharmony_ci * are always nobody (-2).  i.e. we do the same IP address checks for
298c2ecf20Sopenharmony_ci * AUTHNULL as for AUTHUNIX, and that is done here.
308c2ecf20Sopenharmony_ci */
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_ci
338c2ecf20Sopenharmony_cistruct unix_domain {
348c2ecf20Sopenharmony_ci	struct auth_domain	h;
358c2ecf20Sopenharmony_ci	/* other stuff later */
368c2ecf20Sopenharmony_ci};
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_ciextern struct auth_ops svcauth_null;
398c2ecf20Sopenharmony_ciextern struct auth_ops svcauth_unix;
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_cistatic void svcauth_unix_domain_release_rcu(struct rcu_head *head)
428c2ecf20Sopenharmony_ci{
438c2ecf20Sopenharmony_ci	struct auth_domain *dom = container_of(head, struct auth_domain, rcu_head);
448c2ecf20Sopenharmony_ci	struct unix_domain *ud = container_of(dom, struct unix_domain, h);
458c2ecf20Sopenharmony_ci
468c2ecf20Sopenharmony_ci	kfree(dom->name);
478c2ecf20Sopenharmony_ci	kfree(ud);
488c2ecf20Sopenharmony_ci}
498c2ecf20Sopenharmony_ci
508c2ecf20Sopenharmony_cistatic void svcauth_unix_domain_release(struct auth_domain *dom)
518c2ecf20Sopenharmony_ci{
528c2ecf20Sopenharmony_ci	call_rcu(&dom->rcu_head, svcauth_unix_domain_release_rcu);
538c2ecf20Sopenharmony_ci}
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_cistruct auth_domain *unix_domain_find(char *name)
568c2ecf20Sopenharmony_ci{
578c2ecf20Sopenharmony_ci	struct auth_domain *rv;
588c2ecf20Sopenharmony_ci	struct unix_domain *new = NULL;
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_ci	rv = auth_domain_find(name);
618c2ecf20Sopenharmony_ci	while(1) {
628c2ecf20Sopenharmony_ci		if (rv) {
638c2ecf20Sopenharmony_ci			if (new && rv != &new->h)
648c2ecf20Sopenharmony_ci				svcauth_unix_domain_release(&new->h);
658c2ecf20Sopenharmony_ci
668c2ecf20Sopenharmony_ci			if (rv->flavour != &svcauth_unix) {
678c2ecf20Sopenharmony_ci				auth_domain_put(rv);
688c2ecf20Sopenharmony_ci				return NULL;
698c2ecf20Sopenharmony_ci			}
708c2ecf20Sopenharmony_ci			return rv;
718c2ecf20Sopenharmony_ci		}
728c2ecf20Sopenharmony_ci
738c2ecf20Sopenharmony_ci		new = kmalloc(sizeof(*new), GFP_KERNEL);
748c2ecf20Sopenharmony_ci		if (new == NULL)
758c2ecf20Sopenharmony_ci			return NULL;
768c2ecf20Sopenharmony_ci		kref_init(&new->h.ref);
778c2ecf20Sopenharmony_ci		new->h.name = kstrdup(name, GFP_KERNEL);
788c2ecf20Sopenharmony_ci		if (new->h.name == NULL) {
798c2ecf20Sopenharmony_ci			kfree(new);
808c2ecf20Sopenharmony_ci			return NULL;
818c2ecf20Sopenharmony_ci		}
828c2ecf20Sopenharmony_ci		new->h.flavour = &svcauth_unix;
838c2ecf20Sopenharmony_ci		rv = auth_domain_lookup(name, &new->h);
848c2ecf20Sopenharmony_ci	}
858c2ecf20Sopenharmony_ci}
868c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(unix_domain_find);
878c2ecf20Sopenharmony_ci
888c2ecf20Sopenharmony_ci
898c2ecf20Sopenharmony_ci/**************************************************
908c2ecf20Sopenharmony_ci * cache for IP address to unix_domain
918c2ecf20Sopenharmony_ci * as needed by AUTH_UNIX
928c2ecf20Sopenharmony_ci */
938c2ecf20Sopenharmony_ci#define	IP_HASHBITS	8
948c2ecf20Sopenharmony_ci#define	IP_HASHMAX	(1<<IP_HASHBITS)
958c2ecf20Sopenharmony_ci
968c2ecf20Sopenharmony_cistruct ip_map {
978c2ecf20Sopenharmony_ci	struct cache_head	h;
988c2ecf20Sopenharmony_ci	char			m_class[8]; /* e.g. "nfsd" */
998c2ecf20Sopenharmony_ci	struct in6_addr		m_addr;
1008c2ecf20Sopenharmony_ci	struct unix_domain	*m_client;
1018c2ecf20Sopenharmony_ci	struct rcu_head		m_rcu;
1028c2ecf20Sopenharmony_ci};
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_cistatic void ip_map_put(struct kref *kref)
1058c2ecf20Sopenharmony_ci{
1068c2ecf20Sopenharmony_ci	struct cache_head *item = container_of(kref, struct cache_head, ref);
1078c2ecf20Sopenharmony_ci	struct ip_map *im = container_of(item, struct ip_map,h);
1088c2ecf20Sopenharmony_ci
1098c2ecf20Sopenharmony_ci	if (test_bit(CACHE_VALID, &item->flags) &&
1108c2ecf20Sopenharmony_ci	    !test_bit(CACHE_NEGATIVE, &item->flags))
1118c2ecf20Sopenharmony_ci		auth_domain_put(&im->m_client->h);
1128c2ecf20Sopenharmony_ci	kfree_rcu(im, m_rcu);
1138c2ecf20Sopenharmony_ci}
1148c2ecf20Sopenharmony_ci
1158c2ecf20Sopenharmony_cistatic inline int hash_ip6(const struct in6_addr *ip)
1168c2ecf20Sopenharmony_ci{
1178c2ecf20Sopenharmony_ci	return hash_32(ipv6_addr_hash(ip), IP_HASHBITS);
1188c2ecf20Sopenharmony_ci}
1198c2ecf20Sopenharmony_cistatic int ip_map_match(struct cache_head *corig, struct cache_head *cnew)
1208c2ecf20Sopenharmony_ci{
1218c2ecf20Sopenharmony_ci	struct ip_map *orig = container_of(corig, struct ip_map, h);
1228c2ecf20Sopenharmony_ci	struct ip_map *new = container_of(cnew, struct ip_map, h);
1238c2ecf20Sopenharmony_ci	return strcmp(orig->m_class, new->m_class) == 0 &&
1248c2ecf20Sopenharmony_ci	       ipv6_addr_equal(&orig->m_addr, &new->m_addr);
1258c2ecf20Sopenharmony_ci}
1268c2ecf20Sopenharmony_cistatic void ip_map_init(struct cache_head *cnew, struct cache_head *citem)
1278c2ecf20Sopenharmony_ci{
1288c2ecf20Sopenharmony_ci	struct ip_map *new = container_of(cnew, struct ip_map, h);
1298c2ecf20Sopenharmony_ci	struct ip_map *item = container_of(citem, struct ip_map, h);
1308c2ecf20Sopenharmony_ci
1318c2ecf20Sopenharmony_ci	strcpy(new->m_class, item->m_class);
1328c2ecf20Sopenharmony_ci	new->m_addr = item->m_addr;
1338c2ecf20Sopenharmony_ci}
1348c2ecf20Sopenharmony_cistatic void update(struct cache_head *cnew, struct cache_head *citem)
1358c2ecf20Sopenharmony_ci{
1368c2ecf20Sopenharmony_ci	struct ip_map *new = container_of(cnew, struct ip_map, h);
1378c2ecf20Sopenharmony_ci	struct ip_map *item = container_of(citem, struct ip_map, h);
1388c2ecf20Sopenharmony_ci
1398c2ecf20Sopenharmony_ci	kref_get(&item->m_client->h.ref);
1408c2ecf20Sopenharmony_ci	new->m_client = item->m_client;
1418c2ecf20Sopenharmony_ci}
1428c2ecf20Sopenharmony_cistatic struct cache_head *ip_map_alloc(void)
1438c2ecf20Sopenharmony_ci{
1448c2ecf20Sopenharmony_ci	struct ip_map *i = kmalloc(sizeof(*i), GFP_KERNEL);
1458c2ecf20Sopenharmony_ci	if (i)
1468c2ecf20Sopenharmony_ci		return &i->h;
1478c2ecf20Sopenharmony_ci	else
1488c2ecf20Sopenharmony_ci		return NULL;
1498c2ecf20Sopenharmony_ci}
1508c2ecf20Sopenharmony_ci
1518c2ecf20Sopenharmony_cistatic int ip_map_upcall(struct cache_detail *cd, struct cache_head *h)
1528c2ecf20Sopenharmony_ci{
1538c2ecf20Sopenharmony_ci	return sunrpc_cache_pipe_upcall(cd, h);
1548c2ecf20Sopenharmony_ci}
1558c2ecf20Sopenharmony_ci
1568c2ecf20Sopenharmony_cistatic void ip_map_request(struct cache_detail *cd,
1578c2ecf20Sopenharmony_ci				  struct cache_head *h,
1588c2ecf20Sopenharmony_ci				  char **bpp, int *blen)
1598c2ecf20Sopenharmony_ci{
1608c2ecf20Sopenharmony_ci	char text_addr[40];
1618c2ecf20Sopenharmony_ci	struct ip_map *im = container_of(h, struct ip_map, h);
1628c2ecf20Sopenharmony_ci
1638c2ecf20Sopenharmony_ci	if (ipv6_addr_v4mapped(&(im->m_addr))) {
1648c2ecf20Sopenharmony_ci		snprintf(text_addr, 20, "%pI4", &im->m_addr.s6_addr32[3]);
1658c2ecf20Sopenharmony_ci	} else {
1668c2ecf20Sopenharmony_ci		snprintf(text_addr, 40, "%pI6", &im->m_addr);
1678c2ecf20Sopenharmony_ci	}
1688c2ecf20Sopenharmony_ci	qword_add(bpp, blen, im->m_class);
1698c2ecf20Sopenharmony_ci	qword_add(bpp, blen, text_addr);
1708c2ecf20Sopenharmony_ci	(*bpp)[-1] = '\n';
1718c2ecf20Sopenharmony_ci}
1728c2ecf20Sopenharmony_ci
1738c2ecf20Sopenharmony_cistatic struct ip_map *__ip_map_lookup(struct cache_detail *cd, char *class, struct in6_addr *addr);
1748c2ecf20Sopenharmony_cistatic int __ip_map_update(struct cache_detail *cd, struct ip_map *ipm, struct unix_domain *udom, time64_t expiry);
1758c2ecf20Sopenharmony_ci
1768c2ecf20Sopenharmony_cistatic int ip_map_parse(struct cache_detail *cd,
1778c2ecf20Sopenharmony_ci			  char *mesg, int mlen)
1788c2ecf20Sopenharmony_ci{
1798c2ecf20Sopenharmony_ci	/* class ipaddress [domainname] */
1808c2ecf20Sopenharmony_ci	/* should be safe just to use the start of the input buffer
1818c2ecf20Sopenharmony_ci	 * for scratch: */
1828c2ecf20Sopenharmony_ci	char *buf = mesg;
1838c2ecf20Sopenharmony_ci	int len;
1848c2ecf20Sopenharmony_ci	char class[8];
1858c2ecf20Sopenharmony_ci	union {
1868c2ecf20Sopenharmony_ci		struct sockaddr		sa;
1878c2ecf20Sopenharmony_ci		struct sockaddr_in	s4;
1888c2ecf20Sopenharmony_ci		struct sockaddr_in6	s6;
1898c2ecf20Sopenharmony_ci	} address;
1908c2ecf20Sopenharmony_ci	struct sockaddr_in6 sin6;
1918c2ecf20Sopenharmony_ci	int err;
1928c2ecf20Sopenharmony_ci
1938c2ecf20Sopenharmony_ci	struct ip_map *ipmp;
1948c2ecf20Sopenharmony_ci	struct auth_domain *dom;
1958c2ecf20Sopenharmony_ci	time64_t expiry;
1968c2ecf20Sopenharmony_ci
1978c2ecf20Sopenharmony_ci	if (mesg[mlen-1] != '\n')
1988c2ecf20Sopenharmony_ci		return -EINVAL;
1998c2ecf20Sopenharmony_ci	mesg[mlen-1] = 0;
2008c2ecf20Sopenharmony_ci
2018c2ecf20Sopenharmony_ci	/* class */
2028c2ecf20Sopenharmony_ci	len = qword_get(&mesg, class, sizeof(class));
2038c2ecf20Sopenharmony_ci	if (len <= 0) return -EINVAL;
2048c2ecf20Sopenharmony_ci
2058c2ecf20Sopenharmony_ci	/* ip address */
2068c2ecf20Sopenharmony_ci	len = qword_get(&mesg, buf, mlen);
2078c2ecf20Sopenharmony_ci	if (len <= 0) return -EINVAL;
2088c2ecf20Sopenharmony_ci
2098c2ecf20Sopenharmony_ci	if (rpc_pton(cd->net, buf, len, &address.sa, sizeof(address)) == 0)
2108c2ecf20Sopenharmony_ci		return -EINVAL;
2118c2ecf20Sopenharmony_ci	switch (address.sa.sa_family) {
2128c2ecf20Sopenharmony_ci	case AF_INET:
2138c2ecf20Sopenharmony_ci		/* Form a mapped IPv4 address in sin6 */
2148c2ecf20Sopenharmony_ci		sin6.sin6_family = AF_INET6;
2158c2ecf20Sopenharmony_ci		ipv6_addr_set_v4mapped(address.s4.sin_addr.s_addr,
2168c2ecf20Sopenharmony_ci				&sin6.sin6_addr);
2178c2ecf20Sopenharmony_ci		break;
2188c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6)
2198c2ecf20Sopenharmony_ci	case AF_INET6:
2208c2ecf20Sopenharmony_ci		memcpy(&sin6, &address.s6, sizeof(sin6));
2218c2ecf20Sopenharmony_ci		break;
2228c2ecf20Sopenharmony_ci#endif
2238c2ecf20Sopenharmony_ci	default:
2248c2ecf20Sopenharmony_ci		return -EINVAL;
2258c2ecf20Sopenharmony_ci	}
2268c2ecf20Sopenharmony_ci
2278c2ecf20Sopenharmony_ci	expiry = get_expiry(&mesg);
2288c2ecf20Sopenharmony_ci	if (expiry ==0)
2298c2ecf20Sopenharmony_ci		return -EINVAL;
2308c2ecf20Sopenharmony_ci
2318c2ecf20Sopenharmony_ci	/* domainname, or empty for NEGATIVE */
2328c2ecf20Sopenharmony_ci	len = qword_get(&mesg, buf, mlen);
2338c2ecf20Sopenharmony_ci	if (len < 0) return -EINVAL;
2348c2ecf20Sopenharmony_ci
2358c2ecf20Sopenharmony_ci	if (len) {
2368c2ecf20Sopenharmony_ci		dom = unix_domain_find(buf);
2378c2ecf20Sopenharmony_ci		if (dom == NULL)
2388c2ecf20Sopenharmony_ci			return -ENOENT;
2398c2ecf20Sopenharmony_ci	} else
2408c2ecf20Sopenharmony_ci		dom = NULL;
2418c2ecf20Sopenharmony_ci
2428c2ecf20Sopenharmony_ci	/* IPv6 scope IDs are ignored for now */
2438c2ecf20Sopenharmony_ci	ipmp = __ip_map_lookup(cd, class, &sin6.sin6_addr);
2448c2ecf20Sopenharmony_ci	if (ipmp) {
2458c2ecf20Sopenharmony_ci		err = __ip_map_update(cd, ipmp,
2468c2ecf20Sopenharmony_ci			     container_of(dom, struct unix_domain, h),
2478c2ecf20Sopenharmony_ci			     expiry);
2488c2ecf20Sopenharmony_ci	} else
2498c2ecf20Sopenharmony_ci		err = -ENOMEM;
2508c2ecf20Sopenharmony_ci
2518c2ecf20Sopenharmony_ci	if (dom)
2528c2ecf20Sopenharmony_ci		auth_domain_put(dom);
2538c2ecf20Sopenharmony_ci
2548c2ecf20Sopenharmony_ci	cache_flush();
2558c2ecf20Sopenharmony_ci	return err;
2568c2ecf20Sopenharmony_ci}
2578c2ecf20Sopenharmony_ci
2588c2ecf20Sopenharmony_cistatic int ip_map_show(struct seq_file *m,
2598c2ecf20Sopenharmony_ci		       struct cache_detail *cd,
2608c2ecf20Sopenharmony_ci		       struct cache_head *h)
2618c2ecf20Sopenharmony_ci{
2628c2ecf20Sopenharmony_ci	struct ip_map *im;
2638c2ecf20Sopenharmony_ci	struct in6_addr addr;
2648c2ecf20Sopenharmony_ci	char *dom = "-no-domain-";
2658c2ecf20Sopenharmony_ci
2668c2ecf20Sopenharmony_ci	if (h == NULL) {
2678c2ecf20Sopenharmony_ci		seq_puts(m, "#class IP domain\n");
2688c2ecf20Sopenharmony_ci		return 0;
2698c2ecf20Sopenharmony_ci	}
2708c2ecf20Sopenharmony_ci	im = container_of(h, struct ip_map, h);
2718c2ecf20Sopenharmony_ci	/* class addr domain */
2728c2ecf20Sopenharmony_ci	addr = im->m_addr;
2738c2ecf20Sopenharmony_ci
2748c2ecf20Sopenharmony_ci	if (test_bit(CACHE_VALID, &h->flags) &&
2758c2ecf20Sopenharmony_ci	    !test_bit(CACHE_NEGATIVE, &h->flags))
2768c2ecf20Sopenharmony_ci		dom = im->m_client->h.name;
2778c2ecf20Sopenharmony_ci
2788c2ecf20Sopenharmony_ci	if (ipv6_addr_v4mapped(&addr)) {
2798c2ecf20Sopenharmony_ci		seq_printf(m, "%s %pI4 %s\n",
2808c2ecf20Sopenharmony_ci			im->m_class, &addr.s6_addr32[3], dom);
2818c2ecf20Sopenharmony_ci	} else {
2828c2ecf20Sopenharmony_ci		seq_printf(m, "%s %pI6 %s\n", im->m_class, &addr, dom);
2838c2ecf20Sopenharmony_ci	}
2848c2ecf20Sopenharmony_ci	return 0;
2858c2ecf20Sopenharmony_ci}
2868c2ecf20Sopenharmony_ci
2878c2ecf20Sopenharmony_ci
2888c2ecf20Sopenharmony_cistatic struct ip_map *__ip_map_lookup(struct cache_detail *cd, char *class,
2898c2ecf20Sopenharmony_ci		struct in6_addr *addr)
2908c2ecf20Sopenharmony_ci{
2918c2ecf20Sopenharmony_ci	struct ip_map ip;
2928c2ecf20Sopenharmony_ci	struct cache_head *ch;
2938c2ecf20Sopenharmony_ci
2948c2ecf20Sopenharmony_ci	strcpy(ip.m_class, class);
2958c2ecf20Sopenharmony_ci	ip.m_addr = *addr;
2968c2ecf20Sopenharmony_ci	ch = sunrpc_cache_lookup_rcu(cd, &ip.h,
2978c2ecf20Sopenharmony_ci				     hash_str(class, IP_HASHBITS) ^
2988c2ecf20Sopenharmony_ci				     hash_ip6(addr));
2998c2ecf20Sopenharmony_ci
3008c2ecf20Sopenharmony_ci	if (ch)
3018c2ecf20Sopenharmony_ci		return container_of(ch, struct ip_map, h);
3028c2ecf20Sopenharmony_ci	else
3038c2ecf20Sopenharmony_ci		return NULL;
3048c2ecf20Sopenharmony_ci}
3058c2ecf20Sopenharmony_ci
3068c2ecf20Sopenharmony_cistatic inline struct ip_map *ip_map_lookup(struct net *net, char *class,
3078c2ecf20Sopenharmony_ci		struct in6_addr *addr)
3088c2ecf20Sopenharmony_ci{
3098c2ecf20Sopenharmony_ci	struct sunrpc_net *sn;
3108c2ecf20Sopenharmony_ci
3118c2ecf20Sopenharmony_ci	sn = net_generic(net, sunrpc_net_id);
3128c2ecf20Sopenharmony_ci	return __ip_map_lookup(sn->ip_map_cache, class, addr);
3138c2ecf20Sopenharmony_ci}
3148c2ecf20Sopenharmony_ci
3158c2ecf20Sopenharmony_cistatic int __ip_map_update(struct cache_detail *cd, struct ip_map *ipm,
3168c2ecf20Sopenharmony_ci		struct unix_domain *udom, time64_t expiry)
3178c2ecf20Sopenharmony_ci{
3188c2ecf20Sopenharmony_ci	struct ip_map ip;
3198c2ecf20Sopenharmony_ci	struct cache_head *ch;
3208c2ecf20Sopenharmony_ci
3218c2ecf20Sopenharmony_ci	ip.m_client = udom;
3228c2ecf20Sopenharmony_ci	ip.h.flags = 0;
3238c2ecf20Sopenharmony_ci	if (!udom)
3248c2ecf20Sopenharmony_ci		set_bit(CACHE_NEGATIVE, &ip.h.flags);
3258c2ecf20Sopenharmony_ci	ip.h.expiry_time = expiry;
3268c2ecf20Sopenharmony_ci	ch = sunrpc_cache_update(cd, &ip.h, &ipm->h,
3278c2ecf20Sopenharmony_ci				 hash_str(ipm->m_class, IP_HASHBITS) ^
3288c2ecf20Sopenharmony_ci				 hash_ip6(&ipm->m_addr));
3298c2ecf20Sopenharmony_ci	if (!ch)
3308c2ecf20Sopenharmony_ci		return -ENOMEM;
3318c2ecf20Sopenharmony_ci	cache_put(ch, cd);
3328c2ecf20Sopenharmony_ci	return 0;
3338c2ecf20Sopenharmony_ci}
3348c2ecf20Sopenharmony_ci
3358c2ecf20Sopenharmony_civoid svcauth_unix_purge(struct net *net)
3368c2ecf20Sopenharmony_ci{
3378c2ecf20Sopenharmony_ci	struct sunrpc_net *sn;
3388c2ecf20Sopenharmony_ci
3398c2ecf20Sopenharmony_ci	sn = net_generic(net, sunrpc_net_id);
3408c2ecf20Sopenharmony_ci	cache_purge(sn->ip_map_cache);
3418c2ecf20Sopenharmony_ci}
3428c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(svcauth_unix_purge);
3438c2ecf20Sopenharmony_ci
3448c2ecf20Sopenharmony_cistatic inline struct ip_map *
3458c2ecf20Sopenharmony_ciip_map_cached_get(struct svc_xprt *xprt)
3468c2ecf20Sopenharmony_ci{
3478c2ecf20Sopenharmony_ci	struct ip_map *ipm = NULL;
3488c2ecf20Sopenharmony_ci	struct sunrpc_net *sn;
3498c2ecf20Sopenharmony_ci
3508c2ecf20Sopenharmony_ci	if (test_bit(XPT_CACHE_AUTH, &xprt->xpt_flags)) {
3518c2ecf20Sopenharmony_ci		spin_lock(&xprt->xpt_lock);
3528c2ecf20Sopenharmony_ci		ipm = xprt->xpt_auth_cache;
3538c2ecf20Sopenharmony_ci		if (ipm != NULL) {
3548c2ecf20Sopenharmony_ci			sn = net_generic(xprt->xpt_net, sunrpc_net_id);
3558c2ecf20Sopenharmony_ci			if (cache_is_expired(sn->ip_map_cache, &ipm->h)) {
3568c2ecf20Sopenharmony_ci				/*
3578c2ecf20Sopenharmony_ci				 * The entry has been invalidated since it was
3588c2ecf20Sopenharmony_ci				 * remembered, e.g. by a second mount from the
3598c2ecf20Sopenharmony_ci				 * same IP address.
3608c2ecf20Sopenharmony_ci				 */
3618c2ecf20Sopenharmony_ci				xprt->xpt_auth_cache = NULL;
3628c2ecf20Sopenharmony_ci				spin_unlock(&xprt->xpt_lock);
3638c2ecf20Sopenharmony_ci				cache_put(&ipm->h, sn->ip_map_cache);
3648c2ecf20Sopenharmony_ci				return NULL;
3658c2ecf20Sopenharmony_ci			}
3668c2ecf20Sopenharmony_ci			cache_get(&ipm->h);
3678c2ecf20Sopenharmony_ci		}
3688c2ecf20Sopenharmony_ci		spin_unlock(&xprt->xpt_lock);
3698c2ecf20Sopenharmony_ci	}
3708c2ecf20Sopenharmony_ci	return ipm;
3718c2ecf20Sopenharmony_ci}
3728c2ecf20Sopenharmony_ci
3738c2ecf20Sopenharmony_cistatic inline void
3748c2ecf20Sopenharmony_ciip_map_cached_put(struct svc_xprt *xprt, struct ip_map *ipm)
3758c2ecf20Sopenharmony_ci{
3768c2ecf20Sopenharmony_ci	if (test_bit(XPT_CACHE_AUTH, &xprt->xpt_flags)) {
3778c2ecf20Sopenharmony_ci		spin_lock(&xprt->xpt_lock);
3788c2ecf20Sopenharmony_ci		if (xprt->xpt_auth_cache == NULL) {
3798c2ecf20Sopenharmony_ci			/* newly cached, keep the reference */
3808c2ecf20Sopenharmony_ci			xprt->xpt_auth_cache = ipm;
3818c2ecf20Sopenharmony_ci			ipm = NULL;
3828c2ecf20Sopenharmony_ci		}
3838c2ecf20Sopenharmony_ci		spin_unlock(&xprt->xpt_lock);
3848c2ecf20Sopenharmony_ci	}
3858c2ecf20Sopenharmony_ci	if (ipm) {
3868c2ecf20Sopenharmony_ci		struct sunrpc_net *sn;
3878c2ecf20Sopenharmony_ci
3888c2ecf20Sopenharmony_ci		sn = net_generic(xprt->xpt_net, sunrpc_net_id);
3898c2ecf20Sopenharmony_ci		cache_put(&ipm->h, sn->ip_map_cache);
3908c2ecf20Sopenharmony_ci	}
3918c2ecf20Sopenharmony_ci}
3928c2ecf20Sopenharmony_ci
3938c2ecf20Sopenharmony_civoid
3948c2ecf20Sopenharmony_cisvcauth_unix_info_release(struct svc_xprt *xpt)
3958c2ecf20Sopenharmony_ci{
3968c2ecf20Sopenharmony_ci	struct ip_map *ipm;
3978c2ecf20Sopenharmony_ci
3988c2ecf20Sopenharmony_ci	ipm = xpt->xpt_auth_cache;
3998c2ecf20Sopenharmony_ci	if (ipm != NULL) {
4008c2ecf20Sopenharmony_ci		struct sunrpc_net *sn;
4018c2ecf20Sopenharmony_ci
4028c2ecf20Sopenharmony_ci		sn = net_generic(xpt->xpt_net, sunrpc_net_id);
4038c2ecf20Sopenharmony_ci		cache_put(&ipm->h, sn->ip_map_cache);
4048c2ecf20Sopenharmony_ci	}
4058c2ecf20Sopenharmony_ci}
4068c2ecf20Sopenharmony_ci
4078c2ecf20Sopenharmony_ci/****************************************************************************
4088c2ecf20Sopenharmony_ci * auth.unix.gid cache
4098c2ecf20Sopenharmony_ci * simple cache to map a UID to a list of GIDs
4108c2ecf20Sopenharmony_ci * because AUTH_UNIX aka AUTH_SYS has a max of UNX_NGROUPS
4118c2ecf20Sopenharmony_ci */
4128c2ecf20Sopenharmony_ci#define	GID_HASHBITS	8
4138c2ecf20Sopenharmony_ci#define	GID_HASHMAX	(1<<GID_HASHBITS)
4148c2ecf20Sopenharmony_ci
4158c2ecf20Sopenharmony_cistruct unix_gid {
4168c2ecf20Sopenharmony_ci	struct cache_head	h;
4178c2ecf20Sopenharmony_ci	kuid_t			uid;
4188c2ecf20Sopenharmony_ci	struct group_info	*gi;
4198c2ecf20Sopenharmony_ci	struct rcu_head		rcu;
4208c2ecf20Sopenharmony_ci};
4218c2ecf20Sopenharmony_ci
4228c2ecf20Sopenharmony_cistatic int unix_gid_hash(kuid_t uid)
4238c2ecf20Sopenharmony_ci{
4248c2ecf20Sopenharmony_ci	return hash_long(from_kuid(&init_user_ns, uid), GID_HASHBITS);
4258c2ecf20Sopenharmony_ci}
4268c2ecf20Sopenharmony_ci
4278c2ecf20Sopenharmony_cistatic void unix_gid_free(struct rcu_head *rcu)
4288c2ecf20Sopenharmony_ci{
4298c2ecf20Sopenharmony_ci	struct unix_gid *ug = container_of(rcu, struct unix_gid, rcu);
4308c2ecf20Sopenharmony_ci	struct cache_head *item = &ug->h;
4318c2ecf20Sopenharmony_ci
4328c2ecf20Sopenharmony_ci	if (test_bit(CACHE_VALID, &item->flags) &&
4338c2ecf20Sopenharmony_ci	    !test_bit(CACHE_NEGATIVE, &item->flags))
4348c2ecf20Sopenharmony_ci		put_group_info(ug->gi);
4358c2ecf20Sopenharmony_ci	kfree(ug);
4368c2ecf20Sopenharmony_ci}
4378c2ecf20Sopenharmony_ci
4388c2ecf20Sopenharmony_cistatic void unix_gid_put(struct kref *kref)
4398c2ecf20Sopenharmony_ci{
4408c2ecf20Sopenharmony_ci	struct cache_head *item = container_of(kref, struct cache_head, ref);
4418c2ecf20Sopenharmony_ci	struct unix_gid *ug = container_of(item, struct unix_gid, h);
4428c2ecf20Sopenharmony_ci
4438c2ecf20Sopenharmony_ci	call_rcu(&ug->rcu, unix_gid_free);
4448c2ecf20Sopenharmony_ci}
4458c2ecf20Sopenharmony_ci
4468c2ecf20Sopenharmony_cistatic int unix_gid_match(struct cache_head *corig, struct cache_head *cnew)
4478c2ecf20Sopenharmony_ci{
4488c2ecf20Sopenharmony_ci	struct unix_gid *orig = container_of(corig, struct unix_gid, h);
4498c2ecf20Sopenharmony_ci	struct unix_gid *new = container_of(cnew, struct unix_gid, h);
4508c2ecf20Sopenharmony_ci	return uid_eq(orig->uid, new->uid);
4518c2ecf20Sopenharmony_ci}
4528c2ecf20Sopenharmony_cistatic void unix_gid_init(struct cache_head *cnew, struct cache_head *citem)
4538c2ecf20Sopenharmony_ci{
4548c2ecf20Sopenharmony_ci	struct unix_gid *new = container_of(cnew, struct unix_gid, h);
4558c2ecf20Sopenharmony_ci	struct unix_gid *item = container_of(citem, struct unix_gid, h);
4568c2ecf20Sopenharmony_ci	new->uid = item->uid;
4578c2ecf20Sopenharmony_ci}
4588c2ecf20Sopenharmony_cistatic void unix_gid_update(struct cache_head *cnew, struct cache_head *citem)
4598c2ecf20Sopenharmony_ci{
4608c2ecf20Sopenharmony_ci	struct unix_gid *new = container_of(cnew, struct unix_gid, h);
4618c2ecf20Sopenharmony_ci	struct unix_gid *item = container_of(citem, struct unix_gid, h);
4628c2ecf20Sopenharmony_ci
4638c2ecf20Sopenharmony_ci	get_group_info(item->gi);
4648c2ecf20Sopenharmony_ci	new->gi = item->gi;
4658c2ecf20Sopenharmony_ci}
4668c2ecf20Sopenharmony_cistatic struct cache_head *unix_gid_alloc(void)
4678c2ecf20Sopenharmony_ci{
4688c2ecf20Sopenharmony_ci	struct unix_gid *g = kmalloc(sizeof(*g), GFP_KERNEL);
4698c2ecf20Sopenharmony_ci	if (g)
4708c2ecf20Sopenharmony_ci		return &g->h;
4718c2ecf20Sopenharmony_ci	else
4728c2ecf20Sopenharmony_ci		return NULL;
4738c2ecf20Sopenharmony_ci}
4748c2ecf20Sopenharmony_ci
4758c2ecf20Sopenharmony_cistatic int unix_gid_upcall(struct cache_detail *cd, struct cache_head *h)
4768c2ecf20Sopenharmony_ci{
4778c2ecf20Sopenharmony_ci	return sunrpc_cache_pipe_upcall_timeout(cd, h);
4788c2ecf20Sopenharmony_ci}
4798c2ecf20Sopenharmony_ci
4808c2ecf20Sopenharmony_cistatic void unix_gid_request(struct cache_detail *cd,
4818c2ecf20Sopenharmony_ci			     struct cache_head *h,
4828c2ecf20Sopenharmony_ci			     char **bpp, int *blen)
4838c2ecf20Sopenharmony_ci{
4848c2ecf20Sopenharmony_ci	char tuid[20];
4858c2ecf20Sopenharmony_ci	struct unix_gid *ug = container_of(h, struct unix_gid, h);
4868c2ecf20Sopenharmony_ci
4878c2ecf20Sopenharmony_ci	snprintf(tuid, 20, "%u", from_kuid(&init_user_ns, ug->uid));
4888c2ecf20Sopenharmony_ci	qword_add(bpp, blen, tuid);
4898c2ecf20Sopenharmony_ci	(*bpp)[-1] = '\n';
4908c2ecf20Sopenharmony_ci}
4918c2ecf20Sopenharmony_ci
4928c2ecf20Sopenharmony_cistatic struct unix_gid *unix_gid_lookup(struct cache_detail *cd, kuid_t uid);
4938c2ecf20Sopenharmony_ci
4948c2ecf20Sopenharmony_cistatic int unix_gid_parse(struct cache_detail *cd,
4958c2ecf20Sopenharmony_ci			char *mesg, int mlen)
4968c2ecf20Sopenharmony_ci{
4978c2ecf20Sopenharmony_ci	/* uid expiry Ngid gid0 gid1 ... gidN-1 */
4988c2ecf20Sopenharmony_ci	int id;
4998c2ecf20Sopenharmony_ci	kuid_t uid;
5008c2ecf20Sopenharmony_ci	int gids;
5018c2ecf20Sopenharmony_ci	int rv;
5028c2ecf20Sopenharmony_ci	int i;
5038c2ecf20Sopenharmony_ci	int err;
5048c2ecf20Sopenharmony_ci	time64_t expiry;
5058c2ecf20Sopenharmony_ci	struct unix_gid ug, *ugp;
5068c2ecf20Sopenharmony_ci
5078c2ecf20Sopenharmony_ci	if (mesg[mlen - 1] != '\n')
5088c2ecf20Sopenharmony_ci		return -EINVAL;
5098c2ecf20Sopenharmony_ci	mesg[mlen-1] = 0;
5108c2ecf20Sopenharmony_ci
5118c2ecf20Sopenharmony_ci	rv = get_int(&mesg, &id);
5128c2ecf20Sopenharmony_ci	if (rv)
5138c2ecf20Sopenharmony_ci		return -EINVAL;
5148c2ecf20Sopenharmony_ci	uid = make_kuid(current_user_ns(), id);
5158c2ecf20Sopenharmony_ci	ug.uid = uid;
5168c2ecf20Sopenharmony_ci
5178c2ecf20Sopenharmony_ci	expiry = get_expiry(&mesg);
5188c2ecf20Sopenharmony_ci	if (expiry == 0)
5198c2ecf20Sopenharmony_ci		return -EINVAL;
5208c2ecf20Sopenharmony_ci
5218c2ecf20Sopenharmony_ci	rv = get_int(&mesg, &gids);
5228c2ecf20Sopenharmony_ci	if (rv || gids < 0 || gids > 8192)
5238c2ecf20Sopenharmony_ci		return -EINVAL;
5248c2ecf20Sopenharmony_ci
5258c2ecf20Sopenharmony_ci	ug.gi = groups_alloc(gids);
5268c2ecf20Sopenharmony_ci	if (!ug.gi)
5278c2ecf20Sopenharmony_ci		return -ENOMEM;
5288c2ecf20Sopenharmony_ci
5298c2ecf20Sopenharmony_ci	for (i = 0 ; i < gids ; i++) {
5308c2ecf20Sopenharmony_ci		int gid;
5318c2ecf20Sopenharmony_ci		kgid_t kgid;
5328c2ecf20Sopenharmony_ci		rv = get_int(&mesg, &gid);
5338c2ecf20Sopenharmony_ci		err = -EINVAL;
5348c2ecf20Sopenharmony_ci		if (rv)
5358c2ecf20Sopenharmony_ci			goto out;
5368c2ecf20Sopenharmony_ci		kgid = make_kgid(current_user_ns(), gid);
5378c2ecf20Sopenharmony_ci		if (!gid_valid(kgid))
5388c2ecf20Sopenharmony_ci			goto out;
5398c2ecf20Sopenharmony_ci		ug.gi->gid[i] = kgid;
5408c2ecf20Sopenharmony_ci	}
5418c2ecf20Sopenharmony_ci
5428c2ecf20Sopenharmony_ci	groups_sort(ug.gi);
5438c2ecf20Sopenharmony_ci	ugp = unix_gid_lookup(cd, uid);
5448c2ecf20Sopenharmony_ci	if (ugp) {
5458c2ecf20Sopenharmony_ci		struct cache_head *ch;
5468c2ecf20Sopenharmony_ci		ug.h.flags = 0;
5478c2ecf20Sopenharmony_ci		ug.h.expiry_time = expiry;
5488c2ecf20Sopenharmony_ci		ch = sunrpc_cache_update(cd,
5498c2ecf20Sopenharmony_ci					 &ug.h, &ugp->h,
5508c2ecf20Sopenharmony_ci					 unix_gid_hash(uid));
5518c2ecf20Sopenharmony_ci		if (!ch)
5528c2ecf20Sopenharmony_ci			err = -ENOMEM;
5538c2ecf20Sopenharmony_ci		else {
5548c2ecf20Sopenharmony_ci			err = 0;
5558c2ecf20Sopenharmony_ci			cache_put(ch, cd);
5568c2ecf20Sopenharmony_ci		}
5578c2ecf20Sopenharmony_ci	} else
5588c2ecf20Sopenharmony_ci		err = -ENOMEM;
5598c2ecf20Sopenharmony_ci out:
5608c2ecf20Sopenharmony_ci	if (ug.gi)
5618c2ecf20Sopenharmony_ci		put_group_info(ug.gi);
5628c2ecf20Sopenharmony_ci	return err;
5638c2ecf20Sopenharmony_ci}
5648c2ecf20Sopenharmony_ci
5658c2ecf20Sopenharmony_cistatic int unix_gid_show(struct seq_file *m,
5668c2ecf20Sopenharmony_ci			 struct cache_detail *cd,
5678c2ecf20Sopenharmony_ci			 struct cache_head *h)
5688c2ecf20Sopenharmony_ci{
5698c2ecf20Sopenharmony_ci	struct user_namespace *user_ns = m->file->f_cred->user_ns;
5708c2ecf20Sopenharmony_ci	struct unix_gid *ug;
5718c2ecf20Sopenharmony_ci	int i;
5728c2ecf20Sopenharmony_ci	int glen;
5738c2ecf20Sopenharmony_ci
5748c2ecf20Sopenharmony_ci	if (h == NULL) {
5758c2ecf20Sopenharmony_ci		seq_puts(m, "#uid cnt: gids...\n");
5768c2ecf20Sopenharmony_ci		return 0;
5778c2ecf20Sopenharmony_ci	}
5788c2ecf20Sopenharmony_ci	ug = container_of(h, struct unix_gid, h);
5798c2ecf20Sopenharmony_ci	if (test_bit(CACHE_VALID, &h->flags) &&
5808c2ecf20Sopenharmony_ci	    !test_bit(CACHE_NEGATIVE, &h->flags))
5818c2ecf20Sopenharmony_ci		glen = ug->gi->ngroups;
5828c2ecf20Sopenharmony_ci	else
5838c2ecf20Sopenharmony_ci		glen = 0;
5848c2ecf20Sopenharmony_ci
5858c2ecf20Sopenharmony_ci	seq_printf(m, "%u %d:", from_kuid_munged(user_ns, ug->uid), glen);
5868c2ecf20Sopenharmony_ci	for (i = 0; i < glen; i++)
5878c2ecf20Sopenharmony_ci		seq_printf(m, " %d", from_kgid_munged(user_ns, ug->gi->gid[i]));
5888c2ecf20Sopenharmony_ci	seq_printf(m, "\n");
5898c2ecf20Sopenharmony_ci	return 0;
5908c2ecf20Sopenharmony_ci}
5918c2ecf20Sopenharmony_ci
5928c2ecf20Sopenharmony_cistatic const struct cache_detail unix_gid_cache_template = {
5938c2ecf20Sopenharmony_ci	.owner		= THIS_MODULE,
5948c2ecf20Sopenharmony_ci	.hash_size	= GID_HASHMAX,
5958c2ecf20Sopenharmony_ci	.name		= "auth.unix.gid",
5968c2ecf20Sopenharmony_ci	.cache_put	= unix_gid_put,
5978c2ecf20Sopenharmony_ci	.cache_upcall	= unix_gid_upcall,
5988c2ecf20Sopenharmony_ci	.cache_request	= unix_gid_request,
5998c2ecf20Sopenharmony_ci	.cache_parse	= unix_gid_parse,
6008c2ecf20Sopenharmony_ci	.cache_show	= unix_gid_show,
6018c2ecf20Sopenharmony_ci	.match		= unix_gid_match,
6028c2ecf20Sopenharmony_ci	.init		= unix_gid_init,
6038c2ecf20Sopenharmony_ci	.update		= unix_gid_update,
6048c2ecf20Sopenharmony_ci	.alloc		= unix_gid_alloc,
6058c2ecf20Sopenharmony_ci};
6068c2ecf20Sopenharmony_ci
6078c2ecf20Sopenharmony_ciint unix_gid_cache_create(struct net *net)
6088c2ecf20Sopenharmony_ci{
6098c2ecf20Sopenharmony_ci	struct sunrpc_net *sn = net_generic(net, sunrpc_net_id);
6108c2ecf20Sopenharmony_ci	struct cache_detail *cd;
6118c2ecf20Sopenharmony_ci	int err;
6128c2ecf20Sopenharmony_ci
6138c2ecf20Sopenharmony_ci	cd = cache_create_net(&unix_gid_cache_template, net);
6148c2ecf20Sopenharmony_ci	if (IS_ERR(cd))
6158c2ecf20Sopenharmony_ci		return PTR_ERR(cd);
6168c2ecf20Sopenharmony_ci	err = cache_register_net(cd, net);
6178c2ecf20Sopenharmony_ci	if (err) {
6188c2ecf20Sopenharmony_ci		cache_destroy_net(cd, net);
6198c2ecf20Sopenharmony_ci		return err;
6208c2ecf20Sopenharmony_ci	}
6218c2ecf20Sopenharmony_ci	sn->unix_gid_cache = cd;
6228c2ecf20Sopenharmony_ci	return 0;
6238c2ecf20Sopenharmony_ci}
6248c2ecf20Sopenharmony_ci
6258c2ecf20Sopenharmony_civoid unix_gid_cache_destroy(struct net *net)
6268c2ecf20Sopenharmony_ci{
6278c2ecf20Sopenharmony_ci	struct sunrpc_net *sn = net_generic(net, sunrpc_net_id);
6288c2ecf20Sopenharmony_ci	struct cache_detail *cd = sn->unix_gid_cache;
6298c2ecf20Sopenharmony_ci
6308c2ecf20Sopenharmony_ci	sn->unix_gid_cache = NULL;
6318c2ecf20Sopenharmony_ci	cache_purge(cd);
6328c2ecf20Sopenharmony_ci	cache_unregister_net(cd, net);
6338c2ecf20Sopenharmony_ci	cache_destroy_net(cd, net);
6348c2ecf20Sopenharmony_ci}
6358c2ecf20Sopenharmony_ci
6368c2ecf20Sopenharmony_cistatic struct unix_gid *unix_gid_lookup(struct cache_detail *cd, kuid_t uid)
6378c2ecf20Sopenharmony_ci{
6388c2ecf20Sopenharmony_ci	struct unix_gid ug;
6398c2ecf20Sopenharmony_ci	struct cache_head *ch;
6408c2ecf20Sopenharmony_ci
6418c2ecf20Sopenharmony_ci	ug.uid = uid;
6428c2ecf20Sopenharmony_ci	ch = sunrpc_cache_lookup_rcu(cd, &ug.h, unix_gid_hash(uid));
6438c2ecf20Sopenharmony_ci	if (ch)
6448c2ecf20Sopenharmony_ci		return container_of(ch, struct unix_gid, h);
6458c2ecf20Sopenharmony_ci	else
6468c2ecf20Sopenharmony_ci		return NULL;
6478c2ecf20Sopenharmony_ci}
6488c2ecf20Sopenharmony_ci
6498c2ecf20Sopenharmony_cistatic struct group_info *unix_gid_find(kuid_t uid, struct svc_rqst *rqstp)
6508c2ecf20Sopenharmony_ci{
6518c2ecf20Sopenharmony_ci	struct unix_gid *ug;
6528c2ecf20Sopenharmony_ci	struct group_info *gi;
6538c2ecf20Sopenharmony_ci	int ret;
6548c2ecf20Sopenharmony_ci	struct sunrpc_net *sn = net_generic(rqstp->rq_xprt->xpt_net,
6558c2ecf20Sopenharmony_ci					    sunrpc_net_id);
6568c2ecf20Sopenharmony_ci
6578c2ecf20Sopenharmony_ci	ug = unix_gid_lookup(sn->unix_gid_cache, uid);
6588c2ecf20Sopenharmony_ci	if (!ug)
6598c2ecf20Sopenharmony_ci		return ERR_PTR(-EAGAIN);
6608c2ecf20Sopenharmony_ci	ret = cache_check(sn->unix_gid_cache, &ug->h, &rqstp->rq_chandle);
6618c2ecf20Sopenharmony_ci	switch (ret) {
6628c2ecf20Sopenharmony_ci	case -ENOENT:
6638c2ecf20Sopenharmony_ci		return ERR_PTR(-ENOENT);
6648c2ecf20Sopenharmony_ci	case -ETIMEDOUT:
6658c2ecf20Sopenharmony_ci		return ERR_PTR(-ESHUTDOWN);
6668c2ecf20Sopenharmony_ci	case 0:
6678c2ecf20Sopenharmony_ci		gi = get_group_info(ug->gi);
6688c2ecf20Sopenharmony_ci		cache_put(&ug->h, sn->unix_gid_cache);
6698c2ecf20Sopenharmony_ci		return gi;
6708c2ecf20Sopenharmony_ci	default:
6718c2ecf20Sopenharmony_ci		return ERR_PTR(-EAGAIN);
6728c2ecf20Sopenharmony_ci	}
6738c2ecf20Sopenharmony_ci}
6748c2ecf20Sopenharmony_ci
6758c2ecf20Sopenharmony_ciint
6768c2ecf20Sopenharmony_cisvcauth_unix_set_client(struct svc_rqst *rqstp)
6778c2ecf20Sopenharmony_ci{
6788c2ecf20Sopenharmony_ci	struct sockaddr_in *sin;
6798c2ecf20Sopenharmony_ci	struct sockaddr_in6 *sin6, sin6_storage;
6808c2ecf20Sopenharmony_ci	struct ip_map *ipm;
6818c2ecf20Sopenharmony_ci	struct group_info *gi;
6828c2ecf20Sopenharmony_ci	struct svc_cred *cred = &rqstp->rq_cred;
6838c2ecf20Sopenharmony_ci	struct svc_xprt *xprt = rqstp->rq_xprt;
6848c2ecf20Sopenharmony_ci	struct net *net = xprt->xpt_net;
6858c2ecf20Sopenharmony_ci	struct sunrpc_net *sn = net_generic(net, sunrpc_net_id);
6868c2ecf20Sopenharmony_ci
6878c2ecf20Sopenharmony_ci	switch (rqstp->rq_addr.ss_family) {
6888c2ecf20Sopenharmony_ci	case AF_INET:
6898c2ecf20Sopenharmony_ci		sin = svc_addr_in(rqstp);
6908c2ecf20Sopenharmony_ci		sin6 = &sin6_storage;
6918c2ecf20Sopenharmony_ci		ipv6_addr_set_v4mapped(sin->sin_addr.s_addr, &sin6->sin6_addr);
6928c2ecf20Sopenharmony_ci		break;
6938c2ecf20Sopenharmony_ci	case AF_INET6:
6948c2ecf20Sopenharmony_ci		sin6 = svc_addr_in6(rqstp);
6958c2ecf20Sopenharmony_ci		break;
6968c2ecf20Sopenharmony_ci	default:
6978c2ecf20Sopenharmony_ci		BUG();
6988c2ecf20Sopenharmony_ci	}
6998c2ecf20Sopenharmony_ci
7008c2ecf20Sopenharmony_ci	rqstp->rq_client = NULL;
7018c2ecf20Sopenharmony_ci	if (rqstp->rq_proc == 0)
7028c2ecf20Sopenharmony_ci		return SVC_OK;
7038c2ecf20Sopenharmony_ci
7048c2ecf20Sopenharmony_ci	ipm = ip_map_cached_get(xprt);
7058c2ecf20Sopenharmony_ci	if (ipm == NULL)
7068c2ecf20Sopenharmony_ci		ipm = __ip_map_lookup(sn->ip_map_cache, rqstp->rq_server->sv_program->pg_class,
7078c2ecf20Sopenharmony_ci				    &sin6->sin6_addr);
7088c2ecf20Sopenharmony_ci
7098c2ecf20Sopenharmony_ci	if (ipm == NULL)
7108c2ecf20Sopenharmony_ci		return SVC_DENIED;
7118c2ecf20Sopenharmony_ci
7128c2ecf20Sopenharmony_ci	switch (cache_check(sn->ip_map_cache, &ipm->h, &rqstp->rq_chandle)) {
7138c2ecf20Sopenharmony_ci		default:
7148c2ecf20Sopenharmony_ci			BUG();
7158c2ecf20Sopenharmony_ci		case -ETIMEDOUT:
7168c2ecf20Sopenharmony_ci			return SVC_CLOSE;
7178c2ecf20Sopenharmony_ci		case -EAGAIN:
7188c2ecf20Sopenharmony_ci			return SVC_DROP;
7198c2ecf20Sopenharmony_ci		case -ENOENT:
7208c2ecf20Sopenharmony_ci			return SVC_DENIED;
7218c2ecf20Sopenharmony_ci		case 0:
7228c2ecf20Sopenharmony_ci			rqstp->rq_client = &ipm->m_client->h;
7238c2ecf20Sopenharmony_ci			kref_get(&rqstp->rq_client->ref);
7248c2ecf20Sopenharmony_ci			ip_map_cached_put(xprt, ipm);
7258c2ecf20Sopenharmony_ci			break;
7268c2ecf20Sopenharmony_ci	}
7278c2ecf20Sopenharmony_ci
7288c2ecf20Sopenharmony_ci	gi = unix_gid_find(cred->cr_uid, rqstp);
7298c2ecf20Sopenharmony_ci	switch (PTR_ERR(gi)) {
7308c2ecf20Sopenharmony_ci	case -EAGAIN:
7318c2ecf20Sopenharmony_ci		return SVC_DROP;
7328c2ecf20Sopenharmony_ci	case -ESHUTDOWN:
7338c2ecf20Sopenharmony_ci		return SVC_CLOSE;
7348c2ecf20Sopenharmony_ci	case -ENOENT:
7358c2ecf20Sopenharmony_ci		break;
7368c2ecf20Sopenharmony_ci	default:
7378c2ecf20Sopenharmony_ci		put_group_info(cred->cr_group_info);
7388c2ecf20Sopenharmony_ci		cred->cr_group_info = gi;
7398c2ecf20Sopenharmony_ci	}
7408c2ecf20Sopenharmony_ci	return SVC_OK;
7418c2ecf20Sopenharmony_ci}
7428c2ecf20Sopenharmony_ci
7438c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(svcauth_unix_set_client);
7448c2ecf20Sopenharmony_ci
7458c2ecf20Sopenharmony_cistatic int
7468c2ecf20Sopenharmony_cisvcauth_null_accept(struct svc_rqst *rqstp, __be32 *authp)
7478c2ecf20Sopenharmony_ci{
7488c2ecf20Sopenharmony_ci	struct kvec	*argv = &rqstp->rq_arg.head[0];
7498c2ecf20Sopenharmony_ci	struct kvec	*resv = &rqstp->rq_res.head[0];
7508c2ecf20Sopenharmony_ci	struct svc_cred	*cred = &rqstp->rq_cred;
7518c2ecf20Sopenharmony_ci
7528c2ecf20Sopenharmony_ci	if (argv->iov_len < 3*4)
7538c2ecf20Sopenharmony_ci		return SVC_GARBAGE;
7548c2ecf20Sopenharmony_ci
7558c2ecf20Sopenharmony_ci	if (svc_getu32(argv) != 0) {
7568c2ecf20Sopenharmony_ci		dprintk("svc: bad null cred\n");
7578c2ecf20Sopenharmony_ci		*authp = rpc_autherr_badcred;
7588c2ecf20Sopenharmony_ci		return SVC_DENIED;
7598c2ecf20Sopenharmony_ci	}
7608c2ecf20Sopenharmony_ci	if (svc_getu32(argv) != htonl(RPC_AUTH_NULL) || svc_getu32(argv) != 0) {
7618c2ecf20Sopenharmony_ci		dprintk("svc: bad null verf\n");
7628c2ecf20Sopenharmony_ci		*authp = rpc_autherr_badverf;
7638c2ecf20Sopenharmony_ci		return SVC_DENIED;
7648c2ecf20Sopenharmony_ci	}
7658c2ecf20Sopenharmony_ci
7668c2ecf20Sopenharmony_ci	/* Signal that mapping to nobody uid/gid is required */
7678c2ecf20Sopenharmony_ci	cred->cr_uid = INVALID_UID;
7688c2ecf20Sopenharmony_ci	cred->cr_gid = INVALID_GID;
7698c2ecf20Sopenharmony_ci	cred->cr_group_info = groups_alloc(0);
7708c2ecf20Sopenharmony_ci	if (cred->cr_group_info == NULL)
7718c2ecf20Sopenharmony_ci		return SVC_CLOSE; /* kmalloc failure - client must retry */
7728c2ecf20Sopenharmony_ci
7738c2ecf20Sopenharmony_ci	/* Put NULL verifier */
7748c2ecf20Sopenharmony_ci	svc_putnl(resv, RPC_AUTH_NULL);
7758c2ecf20Sopenharmony_ci	svc_putnl(resv, 0);
7768c2ecf20Sopenharmony_ci
7778c2ecf20Sopenharmony_ci	rqstp->rq_cred.cr_flavor = RPC_AUTH_NULL;
7788c2ecf20Sopenharmony_ci	return SVC_OK;
7798c2ecf20Sopenharmony_ci}
7808c2ecf20Sopenharmony_ci
7818c2ecf20Sopenharmony_cistatic int
7828c2ecf20Sopenharmony_cisvcauth_null_release(struct svc_rqst *rqstp)
7838c2ecf20Sopenharmony_ci{
7848c2ecf20Sopenharmony_ci	if (rqstp->rq_client)
7858c2ecf20Sopenharmony_ci		auth_domain_put(rqstp->rq_client);
7868c2ecf20Sopenharmony_ci	rqstp->rq_client = NULL;
7878c2ecf20Sopenharmony_ci	if (rqstp->rq_cred.cr_group_info)
7888c2ecf20Sopenharmony_ci		put_group_info(rqstp->rq_cred.cr_group_info);
7898c2ecf20Sopenharmony_ci	rqstp->rq_cred.cr_group_info = NULL;
7908c2ecf20Sopenharmony_ci
7918c2ecf20Sopenharmony_ci	return 0; /* don't drop */
7928c2ecf20Sopenharmony_ci}
7938c2ecf20Sopenharmony_ci
7948c2ecf20Sopenharmony_ci
7958c2ecf20Sopenharmony_cistruct auth_ops svcauth_null = {
7968c2ecf20Sopenharmony_ci	.name		= "null",
7978c2ecf20Sopenharmony_ci	.owner		= THIS_MODULE,
7988c2ecf20Sopenharmony_ci	.flavour	= RPC_AUTH_NULL,
7998c2ecf20Sopenharmony_ci	.accept 	= svcauth_null_accept,
8008c2ecf20Sopenharmony_ci	.release	= svcauth_null_release,
8018c2ecf20Sopenharmony_ci	.set_client	= svcauth_unix_set_client,
8028c2ecf20Sopenharmony_ci};
8038c2ecf20Sopenharmony_ci
8048c2ecf20Sopenharmony_ci
8058c2ecf20Sopenharmony_cistatic int
8068c2ecf20Sopenharmony_cisvcauth_unix_accept(struct svc_rqst *rqstp, __be32 *authp)
8078c2ecf20Sopenharmony_ci{
8088c2ecf20Sopenharmony_ci	struct kvec	*argv = &rqstp->rq_arg.head[0];
8098c2ecf20Sopenharmony_ci	struct kvec	*resv = &rqstp->rq_res.head[0];
8108c2ecf20Sopenharmony_ci	struct svc_cred	*cred = &rqstp->rq_cred;
8118c2ecf20Sopenharmony_ci	struct user_namespace *userns;
8128c2ecf20Sopenharmony_ci	u32		slen, i;
8138c2ecf20Sopenharmony_ci	int		len   = argv->iov_len;
8148c2ecf20Sopenharmony_ci
8158c2ecf20Sopenharmony_ci	if ((len -= 3*4) < 0)
8168c2ecf20Sopenharmony_ci		return SVC_GARBAGE;
8178c2ecf20Sopenharmony_ci
8188c2ecf20Sopenharmony_ci	svc_getu32(argv);			/* length */
8198c2ecf20Sopenharmony_ci	svc_getu32(argv);			/* time stamp */
8208c2ecf20Sopenharmony_ci	slen = XDR_QUADLEN(svc_getnl(argv));	/* machname length */
8218c2ecf20Sopenharmony_ci	if (slen > 64 || (len -= (slen + 3)*4) < 0)
8228c2ecf20Sopenharmony_ci		goto badcred;
8238c2ecf20Sopenharmony_ci	argv->iov_base = (void*)((__be32*)argv->iov_base + slen);	/* skip machname */
8248c2ecf20Sopenharmony_ci	argv->iov_len -= slen*4;
8258c2ecf20Sopenharmony_ci	/*
8268c2ecf20Sopenharmony_ci	 * Note: we skip uid_valid()/gid_valid() checks here for
8278c2ecf20Sopenharmony_ci	 * backwards compatibility with clients that use -1 id's.
8288c2ecf20Sopenharmony_ci	 * Instead, -1 uid or gid is later mapped to the
8298c2ecf20Sopenharmony_ci	 * (export-specific) anonymous id by nfsd_setuser.
8308c2ecf20Sopenharmony_ci	 * Supplementary gid's will be left alone.
8318c2ecf20Sopenharmony_ci	 */
8328c2ecf20Sopenharmony_ci	userns = (rqstp->rq_xprt && rqstp->rq_xprt->xpt_cred) ?
8338c2ecf20Sopenharmony_ci		rqstp->rq_xprt->xpt_cred->user_ns : &init_user_ns;
8348c2ecf20Sopenharmony_ci	cred->cr_uid = make_kuid(userns, svc_getnl(argv)); /* uid */
8358c2ecf20Sopenharmony_ci	cred->cr_gid = make_kgid(userns, svc_getnl(argv)); /* gid */
8368c2ecf20Sopenharmony_ci	slen = svc_getnl(argv);			/* gids length */
8378c2ecf20Sopenharmony_ci	if (slen > UNX_NGROUPS || (len -= (slen + 2)*4) < 0)
8388c2ecf20Sopenharmony_ci		goto badcred;
8398c2ecf20Sopenharmony_ci	cred->cr_group_info = groups_alloc(slen);
8408c2ecf20Sopenharmony_ci	if (cred->cr_group_info == NULL)
8418c2ecf20Sopenharmony_ci		return SVC_CLOSE;
8428c2ecf20Sopenharmony_ci	for (i = 0; i < slen; i++) {
8438c2ecf20Sopenharmony_ci		kgid_t kgid = make_kgid(userns, svc_getnl(argv));
8448c2ecf20Sopenharmony_ci		cred->cr_group_info->gid[i] = kgid;
8458c2ecf20Sopenharmony_ci	}
8468c2ecf20Sopenharmony_ci	groups_sort(cred->cr_group_info);
8478c2ecf20Sopenharmony_ci	if (svc_getu32(argv) != htonl(RPC_AUTH_NULL) || svc_getu32(argv) != 0) {
8488c2ecf20Sopenharmony_ci		*authp = rpc_autherr_badverf;
8498c2ecf20Sopenharmony_ci		return SVC_DENIED;
8508c2ecf20Sopenharmony_ci	}
8518c2ecf20Sopenharmony_ci
8528c2ecf20Sopenharmony_ci	/* Put NULL verifier */
8538c2ecf20Sopenharmony_ci	svc_putnl(resv, RPC_AUTH_NULL);
8548c2ecf20Sopenharmony_ci	svc_putnl(resv, 0);
8558c2ecf20Sopenharmony_ci
8568c2ecf20Sopenharmony_ci	rqstp->rq_cred.cr_flavor = RPC_AUTH_UNIX;
8578c2ecf20Sopenharmony_ci	return SVC_OK;
8588c2ecf20Sopenharmony_ci
8598c2ecf20Sopenharmony_cibadcred:
8608c2ecf20Sopenharmony_ci	*authp = rpc_autherr_badcred;
8618c2ecf20Sopenharmony_ci	return SVC_DENIED;
8628c2ecf20Sopenharmony_ci}
8638c2ecf20Sopenharmony_ci
8648c2ecf20Sopenharmony_cistatic int
8658c2ecf20Sopenharmony_cisvcauth_unix_release(struct svc_rqst *rqstp)
8668c2ecf20Sopenharmony_ci{
8678c2ecf20Sopenharmony_ci	/* Verifier (such as it is) is already in place.
8688c2ecf20Sopenharmony_ci	 */
8698c2ecf20Sopenharmony_ci	if (rqstp->rq_client)
8708c2ecf20Sopenharmony_ci		auth_domain_put(rqstp->rq_client);
8718c2ecf20Sopenharmony_ci	rqstp->rq_client = NULL;
8728c2ecf20Sopenharmony_ci	if (rqstp->rq_cred.cr_group_info)
8738c2ecf20Sopenharmony_ci		put_group_info(rqstp->rq_cred.cr_group_info);
8748c2ecf20Sopenharmony_ci	rqstp->rq_cred.cr_group_info = NULL;
8758c2ecf20Sopenharmony_ci
8768c2ecf20Sopenharmony_ci	return 0;
8778c2ecf20Sopenharmony_ci}
8788c2ecf20Sopenharmony_ci
8798c2ecf20Sopenharmony_ci
8808c2ecf20Sopenharmony_cistruct auth_ops svcauth_unix = {
8818c2ecf20Sopenharmony_ci	.name		= "unix",
8828c2ecf20Sopenharmony_ci	.owner		= THIS_MODULE,
8838c2ecf20Sopenharmony_ci	.flavour	= RPC_AUTH_UNIX,
8848c2ecf20Sopenharmony_ci	.accept 	= svcauth_unix_accept,
8858c2ecf20Sopenharmony_ci	.release	= svcauth_unix_release,
8868c2ecf20Sopenharmony_ci	.domain_release	= svcauth_unix_domain_release,
8878c2ecf20Sopenharmony_ci	.set_client	= svcauth_unix_set_client,
8888c2ecf20Sopenharmony_ci};
8898c2ecf20Sopenharmony_ci
8908c2ecf20Sopenharmony_cistatic const struct cache_detail ip_map_cache_template = {
8918c2ecf20Sopenharmony_ci	.owner		= THIS_MODULE,
8928c2ecf20Sopenharmony_ci	.hash_size	= IP_HASHMAX,
8938c2ecf20Sopenharmony_ci	.name		= "auth.unix.ip",
8948c2ecf20Sopenharmony_ci	.cache_put	= ip_map_put,
8958c2ecf20Sopenharmony_ci	.cache_upcall	= ip_map_upcall,
8968c2ecf20Sopenharmony_ci	.cache_request	= ip_map_request,
8978c2ecf20Sopenharmony_ci	.cache_parse	= ip_map_parse,
8988c2ecf20Sopenharmony_ci	.cache_show	= ip_map_show,
8998c2ecf20Sopenharmony_ci	.match		= ip_map_match,
9008c2ecf20Sopenharmony_ci	.init		= ip_map_init,
9018c2ecf20Sopenharmony_ci	.update		= update,
9028c2ecf20Sopenharmony_ci	.alloc		= ip_map_alloc,
9038c2ecf20Sopenharmony_ci};
9048c2ecf20Sopenharmony_ci
9058c2ecf20Sopenharmony_ciint ip_map_cache_create(struct net *net)
9068c2ecf20Sopenharmony_ci{
9078c2ecf20Sopenharmony_ci	struct sunrpc_net *sn = net_generic(net, sunrpc_net_id);
9088c2ecf20Sopenharmony_ci	struct cache_detail *cd;
9098c2ecf20Sopenharmony_ci	int err;
9108c2ecf20Sopenharmony_ci
9118c2ecf20Sopenharmony_ci	cd = cache_create_net(&ip_map_cache_template, net);
9128c2ecf20Sopenharmony_ci	if (IS_ERR(cd))
9138c2ecf20Sopenharmony_ci		return PTR_ERR(cd);
9148c2ecf20Sopenharmony_ci	err = cache_register_net(cd, net);
9158c2ecf20Sopenharmony_ci	if (err) {
9168c2ecf20Sopenharmony_ci		cache_destroy_net(cd, net);
9178c2ecf20Sopenharmony_ci		return err;
9188c2ecf20Sopenharmony_ci	}
9198c2ecf20Sopenharmony_ci	sn->ip_map_cache = cd;
9208c2ecf20Sopenharmony_ci	return 0;
9218c2ecf20Sopenharmony_ci}
9228c2ecf20Sopenharmony_ci
9238c2ecf20Sopenharmony_civoid ip_map_cache_destroy(struct net *net)
9248c2ecf20Sopenharmony_ci{
9258c2ecf20Sopenharmony_ci	struct sunrpc_net *sn = net_generic(net, sunrpc_net_id);
9268c2ecf20Sopenharmony_ci	struct cache_detail *cd = sn->ip_map_cache;
9278c2ecf20Sopenharmony_ci
9288c2ecf20Sopenharmony_ci	sn->ip_map_cache = NULL;
9298c2ecf20Sopenharmony_ci	cache_purge(cd);
9308c2ecf20Sopenharmony_ci	cache_unregister_net(cd, net);
9318c2ecf20Sopenharmony_ci	cache_destroy_net(cd, net);
9328c2ecf20Sopenharmony_ci}
933