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