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