blob: 31b9dea1bedd1231fa616cce2f0053debcceb8a6 [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001#include <linux/types.h>
2#include <linux/sched.h>
3#include <linux/module.h>
4#include <linux/sunrpc/types.h>
5#include <linux/sunrpc/xdr.h>
6#include <linux/sunrpc/svcsock.h>
7#include <linux/sunrpc/svcauth.h>
Andy Adamsonc4170582007-07-17 04:04:42 -07008#include <linux/sunrpc/gss_api.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -07009#include <linux/err.h>
10#include <linux/seq_file.h>
11#include <linux/hash.h>
Paulo Marques543537b2005-06-23 00:09:02 -070012#include <linux/string.h>
Tejun Heo5a0e3ad2010-03-24 17:04:11 +090013#include <linux/slab.h>
Greg Banks7b2b1fe2006-10-04 02:15:50 -070014#include <net/sock.h>
Aurélien Charbonf15364b2008-01-18 15:50:56 +010015#include <net/ipv6.h>
16#include <linux/kernel.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070017#define RPCDBG_FACILITY RPCDBG_AUTH
18
Chuck Lever07396052010-01-26 14:03:47 -050019#include <linux/sunrpc/clnt.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070020
Pavel Emelyanov90d51b02010-09-27 14:02:29 +040021#include "netns.h"
22
Linus Torvalds1da177e2005-04-16 15:20:36 -070023/*
24 * AUTHUNIX and AUTHNULL credentials are both handled here.
25 * AUTHNULL is treated just like AUTHUNIX except that the uid/gid
26 * are always nobody (-2). i.e. we do the same IP address checks for
27 * AUTHNULL as for AUTHUNIX, and that is done here.
28 */
29
30
Linus Torvalds1da177e2005-04-16 15:20:36 -070031struct unix_domain {
32 struct auth_domain h;
Linus Torvalds1da177e2005-04-16 15:20:36 -070033 /* other stuff later */
34};
35
NeilBrownefc36aa2006-03-27 01:14:59 -080036extern struct auth_ops svcauth_unix;
37
J. Bruce Fields8b3e07a2011-03-07 22:50:47 -050038static void svcauth_unix_domain_release(struct auth_domain *dom)
39{
40 struct unix_domain *ud = container_of(dom, struct unix_domain, h);
41
42 kfree(dom->name);
43 kfree(ud);
44}
45
Linus Torvalds1da177e2005-04-16 15:20:36 -070046struct auth_domain *unix_domain_find(char *name)
47{
NeilBrownefc36aa2006-03-27 01:14:59 -080048 struct auth_domain *rv;
49 struct unix_domain *new = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -070050
NeilBrownefc36aa2006-03-27 01:14:59 -080051 rv = auth_domain_lookup(name, NULL);
52 while(1) {
NeilBrownad1b5222006-03-27 01:15:11 -080053 if (rv) {
54 if (new && rv != &new->h)
J. Bruce Fields352b5d12011-03-09 22:40:30 -050055 svcauth_unix_domain_release(&new->h);
NeilBrownad1b5222006-03-27 01:15:11 -080056
57 if (rv->flavour != &svcauth_unix) {
58 auth_domain_put(rv);
59 return NULL;
60 }
NeilBrownefc36aa2006-03-27 01:14:59 -080061 return rv;
62 }
Linus Torvalds1da177e2005-04-16 15:20:36 -070063
NeilBrownefc36aa2006-03-27 01:14:59 -080064 new = kmalloc(sizeof(*new), GFP_KERNEL);
65 if (new == NULL)
66 return NULL;
67 kref_init(&new->h.ref);
68 new->h.name = kstrdup(name, GFP_KERNEL);
NeilBrowndd08d6e2006-12-13 00:35:44 -080069 if (new->h.name == NULL) {
70 kfree(new);
71 return NULL;
72 }
NeilBrownefc36aa2006-03-27 01:14:59 -080073 new->h.flavour = &svcauth_unix;
NeilBrownefc36aa2006-03-27 01:14:59 -080074 rv = auth_domain_lookup(name, &new->h);
Linus Torvalds1da177e2005-04-16 15:20:36 -070075 }
Linus Torvalds1da177e2005-04-16 15:20:36 -070076}
Trond Myklebust24c37672008-12-23 16:30:12 -050077EXPORT_SYMBOL_GPL(unix_domain_find);
Linus Torvalds1da177e2005-04-16 15:20:36 -070078
Linus Torvalds1da177e2005-04-16 15:20:36 -070079
80/**************************************************
81 * cache for IP address to unix_domain
82 * as needed by AUTH_UNIX
83 */
84#define IP_HASHBITS 8
85#define IP_HASHMAX (1<<IP_HASHBITS)
Linus Torvalds1da177e2005-04-16 15:20:36 -070086
87struct ip_map {
88 struct cache_head h;
89 char m_class[8]; /* e.g. "nfsd" */
Aurélien Charbonf15364b2008-01-18 15:50:56 +010090 struct in6_addr m_addr;
Linus Torvalds1da177e2005-04-16 15:20:36 -070091 struct unix_domain *m_client;
Linus Torvalds1da177e2005-04-16 15:20:36 -070092};
Linus Torvalds1da177e2005-04-16 15:20:36 -070093
NeilBrownbaab9352006-03-27 01:15:09 -080094static void ip_map_put(struct kref *kref)
Linus Torvalds1da177e2005-04-16 15:20:36 -070095{
NeilBrownbaab9352006-03-27 01:15:09 -080096 struct cache_head *item = container_of(kref, struct cache_head, ref);
Linus Torvalds1da177e2005-04-16 15:20:36 -070097 struct ip_map *im = container_of(item, struct ip_map,h);
NeilBrownbaab9352006-03-27 01:15:09 -080098
99 if (test_bit(CACHE_VALID, &item->flags) &&
100 !test_bit(CACHE_NEGATIVE, &item->flags))
101 auth_domain_put(&im->m_client->h);
102 kfree(im);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700103}
104
NeilBrown1f1e0302006-01-06 00:09:49 -0800105#if IP_HASHBITS == 8
106/* hash_long on a 64 bit machine is currently REALLY BAD for
107 * IP addresses in reverse-endian (i.e. on a little-endian machine).
108 * So use a trivial but reliable hash instead
109 */
Al Viro48061262006-11-08 00:22:34 -0800110static inline int hash_ip(__be32 ip)
NeilBrown1f1e0302006-01-06 00:09:49 -0800111{
Al Viro48061262006-11-08 00:22:34 -0800112 int hash = (__force u32)ip ^ ((__force u32)ip>>16);
NeilBrown1f1e0302006-01-06 00:09:49 -0800113 return (hash ^ (hash>>8)) & 0xff;
114}
115#endif
Aurélien Charbonf15364b2008-01-18 15:50:56 +0100116static inline int hash_ip6(struct in6_addr ip)
117{
118 return (hash_ip(ip.s6_addr32[0]) ^
119 hash_ip(ip.s6_addr32[1]) ^
120 hash_ip(ip.s6_addr32[2]) ^
121 hash_ip(ip.s6_addr32[3]));
122}
NeilBrown1a9917c2006-03-27 01:15:02 -0800123static int ip_map_match(struct cache_head *corig, struct cache_head *cnew)
124{
125 struct ip_map *orig = container_of(corig, struct ip_map, h);
126 struct ip_map *new = container_of(cnew, struct ip_map, h);
Joe Perchesf64f9e72009-11-29 16:55:45 -0800127 return strcmp(orig->m_class, new->m_class) == 0 &&
128 ipv6_addr_equal(&orig->m_addr, &new->m_addr);
NeilBrown1a9917c2006-03-27 01:15:02 -0800129}
130static void ip_map_init(struct cache_head *cnew, struct cache_head *citem)
131{
132 struct ip_map *new = container_of(cnew, struct ip_map, h);
133 struct ip_map *item = container_of(citem, struct ip_map, h);
NeilBrown1f1e0302006-01-06 00:09:49 -0800134
Linus Torvalds1da177e2005-04-16 15:20:36 -0700135 strcpy(new->m_class, item->m_class);
Aurélien Charbonf15364b2008-01-18 15:50:56 +0100136 ipv6_addr_copy(&new->m_addr, &item->m_addr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700137}
NeilBrown1a9917c2006-03-27 01:15:02 -0800138static void update(struct cache_head *cnew, struct cache_head *citem)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700139{
NeilBrown1a9917c2006-03-27 01:15:02 -0800140 struct ip_map *new = container_of(cnew, struct ip_map, h);
141 struct ip_map *item = container_of(citem, struct ip_map, h);
142
NeilBrownefc36aa2006-03-27 01:14:59 -0800143 kref_get(&item->m_client->h.ref);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700144 new->m_client = item->m_client;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700145}
NeilBrown1a9917c2006-03-27 01:15:02 -0800146static struct cache_head *ip_map_alloc(void)
147{
148 struct ip_map *i = kmalloc(sizeof(*i), GFP_KERNEL);
149 if (i)
150 return &i->h;
151 else
152 return NULL;
153}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700154
155static void ip_map_request(struct cache_detail *cd,
156 struct cache_head *h,
157 char **bpp, int *blen)
158{
Aurélien Charbonf15364b2008-01-18 15:50:56 +0100159 char text_addr[40];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700160 struct ip_map *im = container_of(h, struct ip_map, h);
YOSHIFUJI Hideakicca51722007-02-09 15:38:13 -0800161
Aurélien Charbonf15364b2008-01-18 15:50:56 +0100162 if (ipv6_addr_v4mapped(&(im->m_addr))) {
Harvey Harrison21454aa2008-10-31 00:54:56 -0700163 snprintf(text_addr, 20, "%pI4", &im->m_addr.s6_addr32[3]);
Aurélien Charbonf15364b2008-01-18 15:50:56 +0100164 } else {
Harvey Harrison5b095d9892008-10-29 12:52:50 -0700165 snprintf(text_addr, 40, "%pI6", &im->m_addr);
Aurélien Charbonf15364b2008-01-18 15:50:56 +0100166 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700167 qword_add(bpp, blen, im->m_class);
168 qword_add(bpp, blen, text_addr);
169 (*bpp)[-1] = '\n';
170}
171
Trond Myklebustbc74b4f2009-08-09 15:14:29 -0400172static int ip_map_upcall(struct cache_detail *cd, struct cache_head *h)
173{
174 return sunrpc_cache_pipe_upcall(cd, h, ip_map_request);
175}
176
Pavel Emelyanovbf18ab32010-09-27 13:57:36 +0400177static struct ip_map *__ip_map_lookup(struct cache_detail *cd, char *class, struct in6_addr *addr);
178static int __ip_map_update(struct cache_detail *cd, struct ip_map *ipm, struct unix_domain *udom, time_t expiry);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700179
180static int ip_map_parse(struct cache_detail *cd,
181 char *mesg, int mlen)
182{
183 /* class ipaddress [domainname] */
184 /* should be safe just to use the start of the input buffer
185 * for scratch: */
186 char *buf = mesg;
187 int len;
NeilBrown1a9917c2006-03-27 01:15:02 -0800188 char class[8];
Chuck Lever07396052010-01-26 14:03:47 -0500189 union {
190 struct sockaddr sa;
191 struct sockaddr_in s4;
192 struct sockaddr_in6 s6;
193 } address;
194 struct sockaddr_in6 sin6;
NeilBrown1a9917c2006-03-27 01:15:02 -0800195 int err;
196
197 struct ip_map *ipmp;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700198 struct auth_domain *dom;
199 time_t expiry;
200
201 if (mesg[mlen-1] != '\n')
202 return -EINVAL;
203 mesg[mlen-1] = 0;
204
205 /* class */
NeilBrown1a9917c2006-03-27 01:15:02 -0800206 len = qword_get(&mesg, class, sizeof(class));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700207 if (len <= 0) return -EINVAL;
208
209 /* ip address */
210 len = qword_get(&mesg, buf, mlen);
211 if (len <= 0) return -EINVAL;
212
Chuck Lever07396052010-01-26 14:03:47 -0500213 if (rpc_pton(buf, len, &address.sa, sizeof(address)) == 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700214 return -EINVAL;
Chuck Lever07396052010-01-26 14:03:47 -0500215 switch (address.sa.sa_family) {
216 case AF_INET:
217 /* Form a mapped IPv4 address in sin6 */
Chuck Lever07396052010-01-26 14:03:47 -0500218 sin6.sin6_family = AF_INET6;
Pavel Emelyanov70dc78d2010-10-05 20:48:02 +0400219 ipv6_addr_set_v4mapped(address.s4.sin_addr.s_addr,
220 &sin6.sin6_addr);
Chuck Lever07396052010-01-26 14:03:47 -0500221 break;
222#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
223 case AF_INET6:
224 memcpy(&sin6, &address.s6, sizeof(sin6));
225 break;
226#endif
227 default:
228 return -EINVAL;
229 }
YOSHIFUJI Hideakicca51722007-02-09 15:38:13 -0800230
Linus Torvalds1da177e2005-04-16 15:20:36 -0700231 expiry = get_expiry(&mesg);
232 if (expiry ==0)
233 return -EINVAL;
234
235 /* domainname, or empty for NEGATIVE */
236 len = qword_get(&mesg, buf, mlen);
237 if (len < 0) return -EINVAL;
238
239 if (len) {
240 dom = unix_domain_find(buf);
241 if (dom == NULL)
242 return -ENOENT;
243 } else
244 dom = NULL;
245
Chuck Lever07396052010-01-26 14:03:47 -0500246 /* IPv6 scope IDs are ignored for now */
Pavel Emelyanovbf18ab32010-09-27 13:57:36 +0400247 ipmp = __ip_map_lookup(cd, class, &sin6.sin6_addr);
NeilBrown1a9917c2006-03-27 01:15:02 -0800248 if (ipmp) {
Pavel Emelyanovbf18ab32010-09-27 13:57:36 +0400249 err = __ip_map_update(cd, ipmp,
NeilBrown1a9917c2006-03-27 01:15:02 -0800250 container_of(dom, struct unix_domain, h),
251 expiry);
252 } else
253 err = -ENOMEM;
254
Linus Torvalds1da177e2005-04-16 15:20:36 -0700255 if (dom)
256 auth_domain_put(dom);
NeilBrown1a9917c2006-03-27 01:15:02 -0800257
Linus Torvalds1da177e2005-04-16 15:20:36 -0700258 cache_flush();
NeilBrown1a9917c2006-03-27 01:15:02 -0800259 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700260}
261
262static int ip_map_show(struct seq_file *m,
263 struct cache_detail *cd,
264 struct cache_head *h)
265{
266 struct ip_map *im;
Aurélien Charbonf15364b2008-01-18 15:50:56 +0100267 struct in6_addr addr;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700268 char *dom = "-no-domain-";
269
270 if (h == NULL) {
271 seq_puts(m, "#class IP domain\n");
272 return 0;
273 }
274 im = container_of(h, struct ip_map, h);
275 /* class addr domain */
Aurélien Charbonf15364b2008-01-18 15:50:56 +0100276 ipv6_addr_copy(&addr, &im->m_addr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700277
YOSHIFUJI Hideakicca51722007-02-09 15:38:13 -0800278 if (test_bit(CACHE_VALID, &h->flags) &&
Linus Torvalds1da177e2005-04-16 15:20:36 -0700279 !test_bit(CACHE_NEGATIVE, &h->flags))
280 dom = im->m_client->h.name;
281
Aurélien Charbonf15364b2008-01-18 15:50:56 +0100282 if (ipv6_addr_v4mapped(&addr)) {
Harvey Harrison21454aa2008-10-31 00:54:56 -0700283 seq_printf(m, "%s %pI4 %s\n",
284 im->m_class, &addr.s6_addr32[3], dom);
Aurélien Charbonf15364b2008-01-18 15:50:56 +0100285 } else {
Harvey Harrison5b095d9892008-10-29 12:52:50 -0700286 seq_printf(m, "%s %pI6 %s\n", im->m_class, &addr, dom);
Aurélien Charbonf15364b2008-01-18 15:50:56 +0100287 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700288 return 0;
289}
YOSHIFUJI Hideakicca51722007-02-09 15:38:13 -0800290
Linus Torvalds1da177e2005-04-16 15:20:36 -0700291
Pavel Emelyanovbf18ab32010-09-27 13:57:36 +0400292static struct ip_map *__ip_map_lookup(struct cache_detail *cd, char *class,
293 struct in6_addr *addr)
NeilBrown1a9917c2006-03-27 01:15:02 -0800294{
295 struct ip_map ip;
296 struct cache_head *ch;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700297
NeilBrown1a9917c2006-03-27 01:15:02 -0800298 strcpy(ip.m_class, class);
Aurélien Charbonf15364b2008-01-18 15:50:56 +0100299 ipv6_addr_copy(&ip.m_addr, addr);
Pavel Emelyanovbf18ab32010-09-27 13:57:36 +0400300 ch = sunrpc_cache_lookup(cd, &ip.h,
NeilBrown1a9917c2006-03-27 01:15:02 -0800301 hash_str(class, IP_HASHBITS) ^
Aurélien Charbonf15364b2008-01-18 15:50:56 +0100302 hash_ip6(*addr));
NeilBrown1a9917c2006-03-27 01:15:02 -0800303
304 if (ch)
305 return container_of(ch, struct ip_map, h);
306 else
307 return NULL;
308}
309
Pavel Emelyanov352114f2010-09-27 13:59:48 +0400310static inline struct ip_map *ip_map_lookup(struct net *net, char *class,
311 struct in6_addr *addr)
Pavel Emelyanovbf18ab32010-09-27 13:57:36 +0400312{
Pavel Emelyanov90d51b02010-09-27 14:02:29 +0400313 struct sunrpc_net *sn;
314
315 sn = net_generic(net, sunrpc_net_id);
316 return __ip_map_lookup(sn->ip_map_cache, class, addr);
Pavel Emelyanovbf18ab32010-09-27 13:57:36 +0400317}
318
319static int __ip_map_update(struct cache_detail *cd, struct ip_map *ipm,
320 struct unix_domain *udom, time_t expiry)
NeilBrown1a9917c2006-03-27 01:15:02 -0800321{
322 struct ip_map ip;
323 struct cache_head *ch;
324
325 ip.m_client = udom;
326 ip.h.flags = 0;
327 if (!udom)
328 set_bit(CACHE_NEGATIVE, &ip.h.flags);
NeilBrown1a9917c2006-03-27 01:15:02 -0800329 ip.h.expiry_time = expiry;
Pavel Emelyanovbf18ab32010-09-27 13:57:36 +0400330 ch = sunrpc_cache_update(cd, &ip.h, &ipm->h,
NeilBrown1a9917c2006-03-27 01:15:02 -0800331 hash_str(ipm->m_class, IP_HASHBITS) ^
Aurélien Charbonf15364b2008-01-18 15:50:56 +0100332 hash_ip6(ipm->m_addr));
NeilBrown1a9917c2006-03-27 01:15:02 -0800333 if (!ch)
334 return -ENOMEM;
Pavel Emelyanovbf18ab32010-09-27 13:57:36 +0400335 cache_put(ch, cd);
NeilBrown1a9917c2006-03-27 01:15:02 -0800336 return 0;
337}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700338
Pavel Emelyanov352114f2010-09-27 13:59:48 +0400339static inline int ip_map_update(struct net *net, struct ip_map *ipm,
340 struct unix_domain *udom, time_t expiry)
Pavel Emelyanovbf18ab32010-09-27 13:57:36 +0400341{
Pavel Emelyanov90d51b02010-09-27 14:02:29 +0400342 struct sunrpc_net *sn;
343
344 sn = net_generic(net, sunrpc_net_id);
345 return __ip_map_update(sn->ip_map_cache, ipm, udom, expiry);
Pavel Emelyanovbf18ab32010-09-27 13:57:36 +0400346}
347
Linus Torvalds1da177e2005-04-16 15:20:36 -0700348
349void svcauth_unix_purge(void)
350{
Pavel Emelyanov90d51b02010-09-27 14:02:29 +0400351 struct net *net;
352
353 for_each_net(net) {
354 struct sunrpc_net *sn;
355
356 sn = net_generic(net, sunrpc_net_id);
357 cache_purge(sn->ip_map_cache);
358 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700359}
Trond Myklebust24c37672008-12-23 16:30:12 -0500360EXPORT_SYMBOL_GPL(svcauth_unix_purge);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700361
Greg Banks7b2b1fe2006-10-04 02:15:50 -0700362static inline struct ip_map *
Pavel Emelyanov3be44792010-09-27 13:59:13 +0400363ip_map_cached_get(struct svc_xprt *xprt)
Greg Banks7b2b1fe2006-10-04 02:15:50 -0700364{
Tom Tuckerdef13d72007-12-30 21:08:08 -0600365 struct ip_map *ipm = NULL;
Pavel Emelyanov90d51b02010-09-27 14:02:29 +0400366 struct sunrpc_net *sn;
Tom Tuckerdef13d72007-12-30 21:08:08 -0600367
368 if (test_bit(XPT_CACHE_AUTH, &xprt->xpt_flags)) {
369 spin_lock(&xprt->xpt_lock);
370 ipm = xprt->xpt_auth_cache;
371 if (ipm != NULL) {
372 if (!cache_valid(&ipm->h)) {
373 /*
374 * The entry has been invalidated since it was
375 * remembered, e.g. by a second mount from the
376 * same IP address.
377 */
Pavel Emelyanov90d51b02010-09-27 14:02:29 +0400378 sn = net_generic(xprt->xpt_net, sunrpc_net_id);
Tom Tuckerdef13d72007-12-30 21:08:08 -0600379 xprt->xpt_auth_cache = NULL;
380 spin_unlock(&xprt->xpt_lock);
Pavel Emelyanov90d51b02010-09-27 14:02:29 +0400381 cache_put(&ipm->h, sn->ip_map_cache);
Tom Tuckerdef13d72007-12-30 21:08:08 -0600382 return NULL;
383 }
384 cache_get(&ipm->h);
Greg Banks7b2b1fe2006-10-04 02:15:50 -0700385 }
Tom Tuckerdef13d72007-12-30 21:08:08 -0600386 spin_unlock(&xprt->xpt_lock);
Greg Banks7b2b1fe2006-10-04 02:15:50 -0700387 }
388 return ipm;
389}
390
391static inline void
Pavel Emelyanov3be44792010-09-27 13:59:13 +0400392ip_map_cached_put(struct svc_xprt *xprt, struct ip_map *ipm)
Greg Banks7b2b1fe2006-10-04 02:15:50 -0700393{
Tom Tuckerdef13d72007-12-30 21:08:08 -0600394 if (test_bit(XPT_CACHE_AUTH, &xprt->xpt_flags)) {
395 spin_lock(&xprt->xpt_lock);
396 if (xprt->xpt_auth_cache == NULL) {
397 /* newly cached, keep the reference */
398 xprt->xpt_auth_cache = ipm;
399 ipm = NULL;
400 }
401 spin_unlock(&xprt->xpt_lock);
NeilBrown30f3dee2007-04-16 22:53:25 -0700402 }
Pavel Emelyanov90d51b02010-09-27 14:02:29 +0400403 if (ipm) {
404 struct sunrpc_net *sn;
405
406 sn = net_generic(xprt->xpt_net, sunrpc_net_id);
407 cache_put(&ipm->h, sn->ip_map_cache);
408 }
Greg Banks7b2b1fe2006-10-04 02:15:50 -0700409}
410
411void
Pavel Emelyanove3bfca02010-09-27 13:58:42 +0400412svcauth_unix_info_release(struct svc_xprt *xpt)
Greg Banks7b2b1fe2006-10-04 02:15:50 -0700413{
Pavel Emelyanove3bfca02010-09-27 13:58:42 +0400414 struct ip_map *ipm;
415
416 ipm = xpt->xpt_auth_cache;
Pavel Emelyanov90d51b02010-09-27 14:02:29 +0400417 if (ipm != NULL) {
418 struct sunrpc_net *sn;
419
420 sn = net_generic(xpt->xpt_net, sunrpc_net_id);
421 cache_put(&ipm->h, sn->ip_map_cache);
422 }
Greg Banks7b2b1fe2006-10-04 02:15:50 -0700423}
424
NeilBrown3fc605a2007-02-14 00:33:13 -0800425/****************************************************************************
426 * auth.unix.gid cache
427 * simple cache to map a UID to a list of GIDs
428 * because AUTH_UNIX aka AUTH_SYS has a max of 16
429 */
430#define GID_HASHBITS 8
431#define GID_HASHMAX (1<<GID_HASHBITS)
NeilBrown3fc605a2007-02-14 00:33:13 -0800432
433struct unix_gid {
434 struct cache_head h;
435 uid_t uid;
436 struct group_info *gi;
437};
438static struct cache_head *gid_table[GID_HASHMAX];
439
440static void unix_gid_put(struct kref *kref)
441{
442 struct cache_head *item = container_of(kref, struct cache_head, ref);
443 struct unix_gid *ug = container_of(item, struct unix_gid, h);
444 if (test_bit(CACHE_VALID, &item->flags) &&
445 !test_bit(CACHE_NEGATIVE, &item->flags))
446 put_group_info(ug->gi);
447 kfree(ug);
448}
449
450static int unix_gid_match(struct cache_head *corig, struct cache_head *cnew)
451{
452 struct unix_gid *orig = container_of(corig, struct unix_gid, h);
453 struct unix_gid *new = container_of(cnew, struct unix_gid, h);
454 return orig->uid == new->uid;
455}
456static void unix_gid_init(struct cache_head *cnew, struct cache_head *citem)
457{
458 struct unix_gid *new = container_of(cnew, struct unix_gid, h);
459 struct unix_gid *item = container_of(citem, struct unix_gid, h);
460 new->uid = item->uid;
461}
462static void unix_gid_update(struct cache_head *cnew, struct cache_head *citem)
463{
464 struct unix_gid *new = container_of(cnew, struct unix_gid, h);
465 struct unix_gid *item = container_of(citem, struct unix_gid, h);
466
467 get_group_info(item->gi);
468 new->gi = item->gi;
469}
470static struct cache_head *unix_gid_alloc(void)
471{
472 struct unix_gid *g = kmalloc(sizeof(*g), GFP_KERNEL);
473 if (g)
474 return &g->h;
475 else
476 return NULL;
477}
478
479static void unix_gid_request(struct cache_detail *cd,
480 struct cache_head *h,
481 char **bpp, int *blen)
482{
483 char tuid[20];
484 struct unix_gid *ug = container_of(h, struct unix_gid, h);
485
486 snprintf(tuid, 20, "%u", ug->uid);
487 qword_add(bpp, blen, tuid);
488 (*bpp)[-1] = '\n';
489}
490
Trond Myklebustbc74b4f2009-08-09 15:14:29 -0400491static int unix_gid_upcall(struct cache_detail *cd, struct cache_head *h)
492{
493 return sunrpc_cache_pipe_upcall(cd, h, unix_gid_request);
494}
495
NeilBrown3fc605a2007-02-14 00:33:13 -0800496static struct unix_gid *unix_gid_lookup(uid_t uid);
497extern struct cache_detail unix_gid_cache;
498
499static int unix_gid_parse(struct cache_detail *cd,
500 char *mesg, int mlen)
501{
502 /* uid expiry Ngid gid0 gid1 ... gidN-1 */
503 int uid;
504 int gids;
505 int rv;
506 int i;
507 int err;
508 time_t expiry;
509 struct unix_gid ug, *ugp;
510
511 if (mlen <= 0 || mesg[mlen-1] != '\n')
512 return -EINVAL;
513 mesg[mlen-1] = 0;
514
515 rv = get_int(&mesg, &uid);
516 if (rv)
517 return -EINVAL;
518 ug.uid = uid;
519
520 expiry = get_expiry(&mesg);
521 if (expiry == 0)
522 return -EINVAL;
523
524 rv = get_int(&mesg, &gids);
525 if (rv || gids < 0 || gids > 8192)
526 return -EINVAL;
527
528 ug.gi = groups_alloc(gids);
529 if (!ug.gi)
530 return -ENOMEM;
531
532 for (i = 0 ; i < gids ; i++) {
533 int gid;
534 rv = get_int(&mesg, &gid);
535 err = -EINVAL;
536 if (rv)
537 goto out;
538 GROUP_AT(ug.gi, i) = gid;
539 }
540
541 ugp = unix_gid_lookup(uid);
542 if (ugp) {
543 struct cache_head *ch;
544 ug.h.flags = 0;
545 ug.h.expiry_time = expiry;
546 ch = sunrpc_cache_update(&unix_gid_cache,
547 &ug.h, &ugp->h,
548 hash_long(uid, GID_HASHBITS));
549 if (!ch)
550 err = -ENOMEM;
551 else {
552 err = 0;
553 cache_put(ch, &unix_gid_cache);
554 }
555 } else
556 err = -ENOMEM;
557 out:
558 if (ug.gi)
559 put_group_info(ug.gi);
560 return err;
561}
562
563static int unix_gid_show(struct seq_file *m,
564 struct cache_detail *cd,
565 struct cache_head *h)
566{
567 struct unix_gid *ug;
568 int i;
569 int glen;
570
571 if (h == NULL) {
572 seq_puts(m, "#uid cnt: gids...\n");
573 return 0;
574 }
575 ug = container_of(h, struct unix_gid, h);
576 if (test_bit(CACHE_VALID, &h->flags) &&
577 !test_bit(CACHE_NEGATIVE, &h->flags))
578 glen = ug->gi->ngroups;
579 else
580 glen = 0;
581
J. Bruce Fieldsccdb3572010-03-02 15:49:21 -0500582 seq_printf(m, "%u %d:", ug->uid, glen);
NeilBrown3fc605a2007-02-14 00:33:13 -0800583 for (i = 0; i < glen; i++)
584 seq_printf(m, " %d", GROUP_AT(ug->gi, i));
585 seq_printf(m, "\n");
586 return 0;
587}
588
589struct cache_detail unix_gid_cache = {
590 .owner = THIS_MODULE,
591 .hash_size = GID_HASHMAX,
592 .hash_table = gid_table,
593 .name = "auth.unix.gid",
594 .cache_put = unix_gid_put,
Trond Myklebustbc74b4f2009-08-09 15:14:29 -0400595 .cache_upcall = unix_gid_upcall,
NeilBrown3fc605a2007-02-14 00:33:13 -0800596 .cache_parse = unix_gid_parse,
597 .cache_show = unix_gid_show,
598 .match = unix_gid_match,
599 .init = unix_gid_init,
600 .update = unix_gid_update,
601 .alloc = unix_gid_alloc,
602};
603
604static struct unix_gid *unix_gid_lookup(uid_t uid)
605{
606 struct unix_gid ug;
607 struct cache_head *ch;
608
609 ug.uid = uid;
610 ch = sunrpc_cache_lookup(&unix_gid_cache, &ug.h,
611 hash_long(uid, GID_HASHBITS));
612 if (ch)
613 return container_of(ch, struct unix_gid, h);
614 else
615 return NULL;
616}
617
J. Bruce Fieldsdc83d6e2009-10-20 18:51:34 -0400618static struct group_info *unix_gid_find(uid_t uid, struct svc_rqst *rqstp)
NeilBrown3fc605a2007-02-14 00:33:13 -0800619{
J. Bruce Fieldsdc83d6e2009-10-20 18:51:34 -0400620 struct unix_gid *ug;
621 struct group_info *gi;
622 int ret;
623
624 ug = unix_gid_lookup(uid);
NeilBrown3fc605a2007-02-14 00:33:13 -0800625 if (!ug)
J. Bruce Fieldsdc83d6e2009-10-20 18:51:34 -0400626 return ERR_PTR(-EAGAIN);
627 ret = cache_check(&unix_gid_cache, &ug->h, &rqstp->rq_chandle);
628 switch (ret) {
NeilBrown3fc605a2007-02-14 00:33:13 -0800629 case -ENOENT:
J. Bruce Fieldsdc83d6e2009-10-20 18:51:34 -0400630 return ERR_PTR(-ENOENT);
NeilBrown1ebede82010-08-12 17:04:07 +1000631 case -ETIMEDOUT:
632 return ERR_PTR(-ESHUTDOWN);
NeilBrown3fc605a2007-02-14 00:33:13 -0800633 case 0:
J. Bruce Fieldsdc83d6e2009-10-20 18:51:34 -0400634 gi = get_group_info(ug->gi);
NeilBrown560ab422009-08-04 15:22:39 +1000635 cache_put(&ug->h, &unix_gid_cache);
J. Bruce Fieldsdc83d6e2009-10-20 18:51:34 -0400636 return gi;
NeilBrown3fc605a2007-02-14 00:33:13 -0800637 default:
J. Bruce Fieldsdc83d6e2009-10-20 18:51:34 -0400638 return ERR_PTR(-EAGAIN);
NeilBrown3fc605a2007-02-14 00:33:13 -0800639 }
640}
641
J. Bruce Fields3ab4d8b2007-07-17 04:04:46 -0700642int
Linus Torvalds1da177e2005-04-16 15:20:36 -0700643svcauth_unix_set_client(struct svc_rqst *rqstp)
644{
Aurélien Charbonf15364b2008-01-18 15:50:56 +0100645 struct sockaddr_in *sin;
646 struct sockaddr_in6 *sin6, sin6_storage;
NeilBrown1a9917c2006-03-27 01:15:02 -0800647 struct ip_map *ipm;
J. Bruce Fieldsdc83d6e2009-10-20 18:51:34 -0400648 struct group_info *gi;
649 struct svc_cred *cred = &rqstp->rq_cred;
Pavel Emelyanov3be44792010-09-27 13:59:13 +0400650 struct svc_xprt *xprt = rqstp->rq_xprt;
Pavel Emelyanov90d51b02010-09-27 14:02:29 +0400651 struct net *net = xprt->xpt_net;
652 struct sunrpc_net *sn = net_generic(net, sunrpc_net_id);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700653
Aurélien Charbonf15364b2008-01-18 15:50:56 +0100654 switch (rqstp->rq_addr.ss_family) {
655 case AF_INET:
656 sin = svc_addr_in(rqstp);
657 sin6 = &sin6_storage;
Brian Haleyb301e822009-10-07 13:58:25 -0700658 ipv6_addr_set_v4mapped(sin->sin_addr.s_addr, &sin6->sin6_addr);
Aurélien Charbonf15364b2008-01-18 15:50:56 +0100659 break;
660 case AF_INET6:
661 sin6 = svc_addr_in6(rqstp);
662 break;
663 default:
664 BUG();
665 }
666
Linus Torvalds1da177e2005-04-16 15:20:36 -0700667 rqstp->rq_client = NULL;
668 if (rqstp->rq_proc == 0)
669 return SVC_OK;
670
Pavel Emelyanov3be44792010-09-27 13:59:13 +0400671 ipm = ip_map_cached_get(xprt);
Greg Banks7b2b1fe2006-10-04 02:15:50 -0700672 if (ipm == NULL)
Pavel Emelyanov90d51b02010-09-27 14:02:29 +0400673 ipm = __ip_map_lookup(sn->ip_map_cache, rqstp->rq_server->sv_program->pg_class,
Aurélien Charbonf15364b2008-01-18 15:50:56 +0100674 &sin6->sin6_addr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700675
676 if (ipm == NULL)
677 return SVC_DENIED;
678
Pavel Emelyanov90d51b02010-09-27 14:02:29 +0400679 switch (cache_check(sn->ip_map_cache, &ipm->h, &rqstp->rq_chandle)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700680 default:
681 BUG();
J.Bruce Fieldse0bb89e2006-12-13 00:35:25 -0800682 case -ETIMEDOUT:
NeilBrown1ebede82010-08-12 17:04:07 +1000683 return SVC_CLOSE;
684 case -EAGAIN:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700685 return SVC_DROP;
686 case -ENOENT:
687 return SVC_DENIED;
688 case 0:
689 rqstp->rq_client = &ipm->m_client->h;
NeilBrownefc36aa2006-03-27 01:14:59 -0800690 kref_get(&rqstp->rq_client->ref);
Pavel Emelyanov3be44792010-09-27 13:59:13 +0400691 ip_map_cached_put(xprt, ipm);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700692 break;
693 }
J. Bruce Fieldsdc83d6e2009-10-20 18:51:34 -0400694
695 gi = unix_gid_find(cred->cr_uid, rqstp);
696 switch (PTR_ERR(gi)) {
697 case -EAGAIN:
698 return SVC_DROP;
NeilBrown1ebede82010-08-12 17:04:07 +1000699 case -ESHUTDOWN:
700 return SVC_CLOSE;
J. Bruce Fieldsdc83d6e2009-10-20 18:51:34 -0400701 case -ENOENT:
702 break;
703 default:
704 put_group_info(cred->cr_group_info);
705 cred->cr_group_info = gi;
706 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700707 return SVC_OK;
708}
709
Trond Myklebust24c37672008-12-23 16:30:12 -0500710EXPORT_SYMBOL_GPL(svcauth_unix_set_client);
J. Bruce Fields3ab4d8b2007-07-17 04:04:46 -0700711
Linus Torvalds1da177e2005-04-16 15:20:36 -0700712static int
Alexey Dobriyand8ed0292006-09-26 22:29:38 -0700713svcauth_null_accept(struct svc_rqst *rqstp, __be32 *authp)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700714{
715 struct kvec *argv = &rqstp->rq_arg.head[0];
716 struct kvec *resv = &rqstp->rq_res.head[0];
717 struct svc_cred *cred = &rqstp->rq_cred;
718
719 cred->cr_group_info = NULL;
720 rqstp->rq_client = NULL;
721
722 if (argv->iov_len < 3*4)
723 return SVC_GARBAGE;
724
YOSHIFUJI Hideakicca51722007-02-09 15:38:13 -0800725 if (svc_getu32(argv) != 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700726 dprintk("svc: bad null cred\n");
727 *authp = rpc_autherr_badcred;
728 return SVC_DENIED;
729 }
Alexey Dobriyan76994312006-09-26 22:28:46 -0700730 if (svc_getu32(argv) != htonl(RPC_AUTH_NULL) || svc_getu32(argv) != 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700731 dprintk("svc: bad null verf\n");
732 *authp = rpc_autherr_badverf;
733 return SVC_DENIED;
734 }
735
736 /* Signal that mapping to nobody uid/gid is required */
737 cred->cr_uid = (uid_t) -1;
738 cred->cr_gid = (gid_t) -1;
739 cred->cr_group_info = groups_alloc(0);
740 if (cred->cr_group_info == NULL)
NeilBrown1ebede82010-08-12 17:04:07 +1000741 return SVC_CLOSE; /* kmalloc failure - client must retry */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700742
743 /* Put NULL verifier */
Alexey Dobriyan76994312006-09-26 22:28:46 -0700744 svc_putnl(resv, RPC_AUTH_NULL);
745 svc_putnl(resv, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700746
Andy Adamsonc4170582007-07-17 04:04:42 -0700747 rqstp->rq_flavor = RPC_AUTH_NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700748 return SVC_OK;
749}
750
751static int
752svcauth_null_release(struct svc_rqst *rqstp)
753{
754 if (rqstp->rq_client)
755 auth_domain_put(rqstp->rq_client);
756 rqstp->rq_client = NULL;
757 if (rqstp->rq_cred.cr_group_info)
758 put_group_info(rqstp->rq_cred.cr_group_info);
759 rqstp->rq_cred.cr_group_info = NULL;
760
761 return 0; /* don't drop */
762}
763
764
765struct auth_ops svcauth_null = {
766 .name = "null",
767 .owner = THIS_MODULE,
768 .flavour = RPC_AUTH_NULL,
769 .accept = svcauth_null_accept,
770 .release = svcauth_null_release,
771 .set_client = svcauth_unix_set_client,
772};
773
774
775static int
Alexey Dobriyand8ed0292006-09-26 22:29:38 -0700776svcauth_unix_accept(struct svc_rqst *rqstp, __be32 *authp)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700777{
778 struct kvec *argv = &rqstp->rq_arg.head[0];
779 struct kvec *resv = &rqstp->rq_res.head[0];
780 struct svc_cred *cred = &rqstp->rq_cred;
781 u32 slen, i;
782 int len = argv->iov_len;
783
784 cred->cr_group_info = NULL;
785 rqstp->rq_client = NULL;
786
787 if ((len -= 3*4) < 0)
788 return SVC_GARBAGE;
789
790 svc_getu32(argv); /* length */
791 svc_getu32(argv); /* time stamp */
Alexey Dobriyan76994312006-09-26 22:28:46 -0700792 slen = XDR_QUADLEN(svc_getnl(argv)); /* machname length */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700793 if (slen > 64 || (len -= (slen + 3)*4) < 0)
794 goto badcred;
Alexey Dobriyand8ed0292006-09-26 22:29:38 -0700795 argv->iov_base = (void*)((__be32*)argv->iov_base + slen); /* skip machname */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700796 argv->iov_len -= slen*4;
797
Alexey Dobriyan76994312006-09-26 22:28:46 -0700798 cred->cr_uid = svc_getnl(argv); /* uid */
799 cred->cr_gid = svc_getnl(argv); /* gid */
800 slen = svc_getnl(argv); /* gids length */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700801 if (slen > 16 || (len -= (slen + 2)*4) < 0)
802 goto badcred;
J. Bruce Fieldsdc83d6e2009-10-20 18:51:34 -0400803 cred->cr_group_info = groups_alloc(slen);
804 if (cred->cr_group_info == NULL)
NeilBrown1ebede82010-08-12 17:04:07 +1000805 return SVC_CLOSE;
J. Bruce Fieldsdc83d6e2009-10-20 18:51:34 -0400806 for (i = 0; i < slen; i++)
807 GROUP_AT(cred->cr_group_info, i) = svc_getnl(argv);
Alexey Dobriyan76994312006-09-26 22:28:46 -0700808 if (svc_getu32(argv) != htonl(RPC_AUTH_NULL) || svc_getu32(argv) != 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700809 *authp = rpc_autherr_badverf;
810 return SVC_DENIED;
811 }
812
813 /* Put NULL verifier */
Alexey Dobriyan76994312006-09-26 22:28:46 -0700814 svc_putnl(resv, RPC_AUTH_NULL);
815 svc_putnl(resv, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700816
Andy Adamsonc4170582007-07-17 04:04:42 -0700817 rqstp->rq_flavor = RPC_AUTH_UNIX;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700818 return SVC_OK;
819
820badcred:
821 *authp = rpc_autherr_badcred;
822 return SVC_DENIED;
823}
824
825static int
826svcauth_unix_release(struct svc_rqst *rqstp)
827{
828 /* Verifier (such as it is) is already in place.
829 */
830 if (rqstp->rq_client)
831 auth_domain_put(rqstp->rq_client);
832 rqstp->rq_client = NULL;
833 if (rqstp->rq_cred.cr_group_info)
834 put_group_info(rqstp->rq_cred.cr_group_info);
835 rqstp->rq_cred.cr_group_info = NULL;
836
837 return 0;
838}
839
840
841struct auth_ops svcauth_unix = {
842 .name = "unix",
843 .owner = THIS_MODULE,
844 .flavour = RPC_AUTH_UNIX,
845 .accept = svcauth_unix_accept,
846 .release = svcauth_unix_release,
847 .domain_release = svcauth_unix_domain_release,
848 .set_client = svcauth_unix_set_client,
849};
850
Pavel Emelyanov90d51b02010-09-27 14:02:29 +0400851int ip_map_cache_create(struct net *net)
852{
853 int err = -ENOMEM;
854 struct cache_detail *cd;
855 struct cache_head **tbl;
856 struct sunrpc_net *sn = net_generic(net, sunrpc_net_id);
857
858 cd = kzalloc(sizeof(struct cache_detail), GFP_KERNEL);
859 if (cd == NULL)
860 goto err_cd;
861
862 tbl = kzalloc(IP_HASHMAX * sizeof(struct cache_head *), GFP_KERNEL);
863 if (tbl == NULL)
864 goto err_tbl;
865
866 cd->owner = THIS_MODULE,
867 cd->hash_size = IP_HASHMAX,
868 cd->hash_table = tbl,
869 cd->name = "auth.unix.ip",
870 cd->cache_put = ip_map_put,
871 cd->cache_upcall = ip_map_upcall,
872 cd->cache_parse = ip_map_parse,
873 cd->cache_show = ip_map_show,
874 cd->match = ip_map_match,
875 cd->init = ip_map_init,
876 cd->update = update,
877 cd->alloc = ip_map_alloc,
878
879 err = cache_register_net(cd, net);
880 if (err)
881 goto err_reg;
882
883 sn->ip_map_cache = cd;
884 return 0;
885
886err_reg:
887 kfree(tbl);
888err_tbl:
889 kfree(cd);
890err_cd:
891 return err;
892}
893
894void ip_map_cache_destroy(struct net *net)
895{
896 struct sunrpc_net *sn;
897
898 sn = net_generic(net, sunrpc_net_id);
899 cache_purge(sn->ip_map_cache);
900 cache_unregister_net(sn->ip_map_cache, net);
901 kfree(sn->ip_map_cache->hash_table);
902 kfree(sn->ip_map_cache);
903}