]> git.proxmox.com Git - mirror_ubuntu-zesty-kernel.git/blame - net/sunrpc/auth.c
SUNRPC: Fix a memory leak in gss_create()
[mirror_ubuntu-zesty-kernel.git] / net / sunrpc / auth.c
CommitLineData
1da177e4
LT
1/*
2 * linux/net/sunrpc/auth.c
3 *
4 * Generic RPC client authentication API.
5 *
6 * Copyright (C) 1996, Olaf Kirch <okir@monad.swb.de>
7 */
8
9#include <linux/types.h>
10#include <linux/sched.h>
11#include <linux/module.h>
12#include <linux/slab.h>
13#include <linux/errno.h>
1da177e4
LT
14#include <linux/sunrpc/clnt.h>
15#include <linux/spinlock.h>
16
17#ifdef RPC_DEBUG
18# define RPCDBG_FACILITY RPCDBG_AUTH
19#endif
20
21static struct rpc_authops * auth_flavors[RPC_AUTH_MAXFLAVOR] = {
22 &authnull_ops, /* AUTH_NULL */
23 &authunix_ops, /* AUTH_UNIX */
24 NULL, /* others can be loadable modules */
25};
26
27static u32
28pseudoflavor_to_flavor(u32 flavor) {
29 if (flavor >= RPC_AUTH_MAXFLAVOR)
30 return RPC_AUTH_GSS;
31 return flavor;
32}
33
34int
35rpcauth_register(struct rpc_authops *ops)
36{
37 rpc_authflavor_t flavor;
38
39 if ((flavor = ops->au_flavor) >= RPC_AUTH_MAXFLAVOR)
40 return -EINVAL;
41 if (auth_flavors[flavor] != NULL)
42 return -EPERM; /* what else? */
43 auth_flavors[flavor] = ops;
44 return 0;
45}
46
47int
48rpcauth_unregister(struct rpc_authops *ops)
49{
50 rpc_authflavor_t flavor;
51
52 if ((flavor = ops->au_flavor) >= RPC_AUTH_MAXFLAVOR)
53 return -EINVAL;
54 if (auth_flavors[flavor] != ops)
55 return -EPERM; /* what else? */
56 auth_flavors[flavor] = NULL;
57 return 0;
58}
59
60struct rpc_auth *
61rpcauth_create(rpc_authflavor_t pseudoflavor, struct rpc_clnt *clnt)
62{
63 struct rpc_auth *auth;
64 struct rpc_authops *ops;
65 u32 flavor = pseudoflavor_to_flavor(pseudoflavor);
66
f344f6df
OK
67 auth = ERR_PTR(-EINVAL);
68 if (flavor >= RPC_AUTH_MAXFLAVOR)
69 goto out;
70
71 /* FIXME - auth_flavors[] really needs an rw lock,
72 * and module refcounting. */
73#ifdef CONFIG_KMOD
74 if ((ops = auth_flavors[flavor]) == NULL)
75 request_module("rpc-auth-%u", flavor);
76#endif
77 if ((ops = auth_flavors[flavor]) == NULL)
78 goto out;
1da177e4 79 auth = ops->create(clnt, pseudoflavor);
6a19275a
BF
80 if (IS_ERR(auth))
81 return auth;
1da177e4
LT
82 if (clnt->cl_auth)
83 rpcauth_destroy(clnt->cl_auth);
84 clnt->cl_auth = auth;
f344f6df
OK
85
86out:
1da177e4
LT
87 return auth;
88}
89
90void
91rpcauth_destroy(struct rpc_auth *auth)
92{
93 if (!atomic_dec_and_test(&auth->au_count))
94 return;
95 auth->au_ops->destroy(auth);
96}
97
98static DEFINE_SPINLOCK(rpc_credcache_lock);
99
100/*
101 * Initialize RPC credential cache
102 */
103int
104rpcauth_init_credcache(struct rpc_auth *auth, unsigned long expire)
105{
106 struct rpc_cred_cache *new;
107 int i;
108
8b3a7005 109 new = kmalloc(sizeof(*new), GFP_KERNEL);
1da177e4
LT
110 if (!new)
111 return -ENOMEM;
112 for (i = 0; i < RPC_CREDCACHE_NR; i++)
113 INIT_HLIST_HEAD(&new->hashtable[i]);
114 new->expire = expire;
115 new->nextgc = jiffies + (expire >> 1);
116 auth->au_credcache = new;
117 return 0;
118}
119
120/*
121 * Destroy a list of credentials
122 */
123static inline
124void rpcauth_destroy_credlist(struct hlist_head *head)
125{
126 struct rpc_cred *cred;
127
128 while (!hlist_empty(head)) {
129 cred = hlist_entry(head->first, struct rpc_cred, cr_hash);
130 hlist_del_init(&cred->cr_hash);
131 put_rpccred(cred);
132 }
133}
134
135/*
136 * Clear the RPC credential cache, and delete those credentials
137 * that are not referenced.
138 */
139void
3ab9bb72 140rpcauth_clear_credcache(struct rpc_cred_cache *cache)
1da177e4 141{
1da177e4
LT
142 HLIST_HEAD(free);
143 struct hlist_node *pos, *next;
144 struct rpc_cred *cred;
145 int i;
146
147 spin_lock(&rpc_credcache_lock);
148 for (i = 0; i < RPC_CREDCACHE_NR; i++) {
149 hlist_for_each_safe(pos, next, &cache->hashtable[i]) {
150 cred = hlist_entry(pos, struct rpc_cred, cr_hash);
151 __hlist_del(&cred->cr_hash);
152 hlist_add_head(&cred->cr_hash, &free);
153 }
154 }
155 spin_unlock(&rpc_credcache_lock);
156 rpcauth_destroy_credlist(&free);
157}
158
3ab9bb72
TM
159/*
160 * Destroy the RPC credential cache
161 */
162void
163rpcauth_destroy_credcache(struct rpc_auth *auth)
164{
165 struct rpc_cred_cache *cache = auth->au_credcache;
166
167 if (cache) {
168 auth->au_credcache = NULL;
169 rpcauth_clear_credcache(cache);
170 kfree(cache);
171 }
172}
173
1da177e4
LT
174static void
175rpcauth_prune_expired(struct rpc_auth *auth, struct rpc_cred *cred, struct hlist_head *free)
176{
177 if (atomic_read(&cred->cr_count) != 1)
178 return;
179 if (time_after(jiffies, cred->cr_expire + auth->au_credcache->expire))
180 cred->cr_flags &= ~RPCAUTH_CRED_UPTODATE;
181 if (!(cred->cr_flags & RPCAUTH_CRED_UPTODATE)) {
182 __hlist_del(&cred->cr_hash);
183 hlist_add_head(&cred->cr_hash, free);
184 }
185}
186
187/*
188 * Remove stale credentials. Avoid sleeping inside the loop.
189 */
190static void
191rpcauth_gc_credcache(struct rpc_auth *auth, struct hlist_head *free)
192{
193 struct rpc_cred_cache *cache = auth->au_credcache;
194 struct hlist_node *pos, *next;
195 struct rpc_cred *cred;
196 int i;
197
46121cf7 198 dprintk("RPC: gc'ing RPC credentials for auth %p\n", auth);
1da177e4
LT
199 for (i = 0; i < RPC_CREDCACHE_NR; i++) {
200 hlist_for_each_safe(pos, next, &cache->hashtable[i]) {
201 cred = hlist_entry(pos, struct rpc_cred, cr_hash);
202 rpcauth_prune_expired(auth, cred, free);
203 }
204 }
205 cache->nextgc = jiffies + cache->expire;
206}
207
208/*
209 * Look up a process' credentials in the authentication cache
210 */
211struct rpc_cred *
212rpcauth_lookup_credcache(struct rpc_auth *auth, struct auth_cred * acred,
8a317760 213 int flags)
1da177e4
LT
214{
215 struct rpc_cred_cache *cache = auth->au_credcache;
216 HLIST_HEAD(free);
217 struct hlist_node *pos, *next;
218 struct rpc_cred *new = NULL,
219 *cred = NULL;
220 int nr = 0;
221
8a317760 222 if (!(flags & RPCAUTH_LOOKUP_ROOTCREDS))
1da177e4
LT
223 nr = acred->uid & RPC_CREDCACHE_MASK;
224retry:
225 spin_lock(&rpc_credcache_lock);
226 if (time_before(cache->nextgc, jiffies))
227 rpcauth_gc_credcache(auth, &free);
228 hlist_for_each_safe(pos, next, &cache->hashtable[nr]) {
229 struct rpc_cred *entry;
cca5172a 230 entry = hlist_entry(pos, struct rpc_cred, cr_hash);
8a317760 231 if (entry->cr_ops->crmatch(acred, entry, flags)) {
1da177e4
LT
232 hlist_del(&entry->cr_hash);
233 cred = entry;
234 break;
235 }
236 rpcauth_prune_expired(auth, entry, &free);
237 }
238 if (new) {
239 if (cred)
240 hlist_add_head(&new->cr_hash, &free);
241 else
242 cred = new;
243 }
244 if (cred) {
245 hlist_add_head(&cred->cr_hash, &cache->hashtable[nr]);
246 get_rpccred(cred);
247 }
248 spin_unlock(&rpc_credcache_lock);
249
250 rpcauth_destroy_credlist(&free);
251
252 if (!cred) {
8a317760 253 new = auth->au_ops->crcreate(auth, acred, flags);
1da177e4
LT
254 if (!IS_ERR(new)) {
255#ifdef RPC_DEBUG
256 new->cr_magic = RPCAUTH_CRED_MAGIC;
257#endif
258 goto retry;
259 } else
260 cred = new;
fba3bad4
TM
261 } else if ((cred->cr_flags & RPCAUTH_CRED_NEW)
262 && cred->cr_ops->cr_init != NULL
263 && !(flags & RPCAUTH_LOOKUP_NEW)) {
264 int res = cred->cr_ops->cr_init(auth, cred);
265 if (res < 0) {
266 put_rpccred(cred);
267 cred = ERR_PTR(res);
268 }
1da177e4
LT
269 }
270
271 return (struct rpc_cred *) cred;
272}
273
274struct rpc_cred *
8a317760 275rpcauth_lookupcred(struct rpc_auth *auth, int flags)
1da177e4
LT
276{
277 struct auth_cred acred = {
278 .uid = current->fsuid,
279 .gid = current->fsgid,
280 .group_info = current->group_info,
281 };
282 struct rpc_cred *ret;
283
46121cf7 284 dprintk("RPC: looking up %s cred\n",
1da177e4
LT
285 auth->au_ops->au_name);
286 get_group_info(acred.group_info);
8a317760 287 ret = auth->au_ops->lookup_cred(auth, &acred, flags);
1da177e4
LT
288 put_group_info(acred.group_info);
289 return ret;
290}
291
292struct rpc_cred *
293rpcauth_bindcred(struct rpc_task *task)
294{
295 struct rpc_auth *auth = task->tk_auth;
296 struct auth_cred acred = {
297 .uid = current->fsuid,
298 .gid = current->fsgid,
299 .group_info = current->group_info,
300 };
301 struct rpc_cred *ret;
8a317760 302 int flags = 0;
1da177e4 303
46121cf7 304 dprintk("RPC: %5u looking up %s cred\n",
1da177e4
LT
305 task->tk_pid, task->tk_auth->au_ops->au_name);
306 get_group_info(acred.group_info);
8a317760
TM
307 if (task->tk_flags & RPC_TASK_ROOTCREDS)
308 flags |= RPCAUTH_LOOKUP_ROOTCREDS;
309 ret = auth->au_ops->lookup_cred(auth, &acred, flags);
1da177e4
LT
310 if (!IS_ERR(ret))
311 task->tk_msg.rpc_cred = ret;
312 else
313 task->tk_status = PTR_ERR(ret);
314 put_group_info(acred.group_info);
315 return ret;
316}
317
318void
319rpcauth_holdcred(struct rpc_task *task)
320{
46121cf7
CL
321 dprintk("RPC: %5u holding %s cred %p\n",
322 task->tk_pid, task->tk_auth->au_ops->au_name,
323 task->tk_msg.rpc_cred);
1da177e4
LT
324 if (task->tk_msg.rpc_cred)
325 get_rpccred(task->tk_msg.rpc_cred);
326}
327
328void
329put_rpccred(struct rpc_cred *cred)
330{
331 cred->cr_expire = jiffies;
332 if (!atomic_dec_and_test(&cred->cr_count))
333 return;
334 cred->cr_ops->crdestroy(cred);
335}
336
337void
338rpcauth_unbindcred(struct rpc_task *task)
339{
1da177e4
LT
340 struct rpc_cred *cred = task->tk_msg.rpc_cred;
341
46121cf7 342 dprintk("RPC: %5u releasing %s cred %p\n",
0bbacc40 343 task->tk_pid, task->tk_auth->au_ops->au_name, cred);
1da177e4
LT
344
345 put_rpccred(cred);
346 task->tk_msg.rpc_cred = NULL;
347}
348
d8ed029d
AD
349__be32 *
350rpcauth_marshcred(struct rpc_task *task, __be32 *p)
1da177e4 351{
1da177e4
LT
352 struct rpc_cred *cred = task->tk_msg.rpc_cred;
353
46121cf7 354 dprintk("RPC: %5u marshaling %s cred %p\n",
0bbacc40
CL
355 task->tk_pid, task->tk_auth->au_ops->au_name, cred);
356
1da177e4
LT
357 return cred->cr_ops->crmarshal(task, p);
358}
359
d8ed029d
AD
360__be32 *
361rpcauth_checkverf(struct rpc_task *task, __be32 *p)
1da177e4 362{
1da177e4
LT
363 struct rpc_cred *cred = task->tk_msg.rpc_cred;
364
46121cf7 365 dprintk("RPC: %5u validating %s cred %p\n",
0bbacc40
CL
366 task->tk_pid, task->tk_auth->au_ops->au_name, cred);
367
1da177e4
LT
368 return cred->cr_ops->crvalidate(task, p);
369}
370
371int
372rpcauth_wrap_req(struct rpc_task *task, kxdrproc_t encode, void *rqstp,
d8ed029d 373 __be32 *data, void *obj)
1da177e4
LT
374{
375 struct rpc_cred *cred = task->tk_msg.rpc_cred;
376
46121cf7 377 dprintk("RPC: %5u using %s cred %p to wrap rpc data\n",
1da177e4
LT
378 task->tk_pid, cred->cr_ops->cr_name, cred);
379 if (cred->cr_ops->crwrap_req)
380 return cred->cr_ops->crwrap_req(task, encode, rqstp, data, obj);
381 /* By default, we encode the arguments normally. */
382 return encode(rqstp, data, obj);
383}
384
385int
386rpcauth_unwrap_resp(struct rpc_task *task, kxdrproc_t decode, void *rqstp,
d8ed029d 387 __be32 *data, void *obj)
1da177e4
LT
388{
389 struct rpc_cred *cred = task->tk_msg.rpc_cred;
390
46121cf7 391 dprintk("RPC: %5u using %s cred %p to unwrap rpc data\n",
1da177e4
LT
392 task->tk_pid, cred->cr_ops->cr_name, cred);
393 if (cred->cr_ops->crunwrap_resp)
394 return cred->cr_ops->crunwrap_resp(task, decode, rqstp,
395 data, obj);
396 /* By default, we decode the arguments normally. */
397 return decode(rqstp, data, obj);
398}
399
400int
401rpcauth_refreshcred(struct rpc_task *task)
402{
1da177e4
LT
403 struct rpc_cred *cred = task->tk_msg.rpc_cred;
404 int err;
405
46121cf7 406 dprintk("RPC: %5u refreshing %s cred %p\n",
0bbacc40
CL
407 task->tk_pid, task->tk_auth->au_ops->au_name, cred);
408
1da177e4
LT
409 err = cred->cr_ops->crrefresh(task);
410 if (err < 0)
411 task->tk_status = err;
412 return err;
413}
414
415void
416rpcauth_invalcred(struct rpc_task *task)
417{
46121cf7 418 dprintk("RPC: %5u invalidating %s cred %p\n",
1da177e4
LT
419 task->tk_pid, task->tk_auth->au_ops->au_name, task->tk_msg.rpc_cred);
420 spin_lock(&rpc_credcache_lock);
421 if (task->tk_msg.rpc_cred)
422 task->tk_msg.rpc_cred->cr_flags &= ~RPCAUTH_CRED_UPTODATE;
423 spin_unlock(&rpc_credcache_lock);
424}
425
426int
427rpcauth_uptodatecred(struct rpc_task *task)
428{
429 return !(task->tk_msg.rpc_cred) ||
430 (task->tk_msg.rpc_cred->cr_flags & RPCAUTH_CRED_UPTODATE);
431}