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>
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
67	auth = ERR_PTR(-EINVAL);
68	if (flavor >= RPC_AUTH_MAXFLAVOR)
69		goto out;
70
71#ifdef CONFIG_KMOD
72	if ((ops = auth_flavors[flavor]) == NULL)
73		request_module("rpc-auth-%u", flavor);
74#endif
75	if ((ops = auth_flavors[flavor]) == NULL)
76		goto out;
77	auth = ops->create(clnt, pseudoflavor);
78	if (IS_ERR(auth))
79		return auth;
80	if (clnt->cl_auth)
81		rpcauth_destroy(clnt->cl_auth);
82	clnt->cl_auth = auth;
83
84out:
85	return auth;
86}
87
88void
89rpcauth_destroy(struct rpc_auth *auth)
90{
91	if (!atomic_dec_and_test(&auth->au_count))
92		return;
93	auth->au_ops->destroy(auth);
94}
95
96static DEFINE_SPINLOCK(rpc_credcache_lock);
97
98/*
99 * Initialize RPC credential cache
100 */
101int
102rpcauth_init_credcache(struct rpc_auth *auth, unsigned long expire)
103{
104	struct rpc_cred_cache *new;
105	int i;
106
107	new = kmalloc(sizeof(*new), GFP_KERNEL);
108	if (!new)
109		return -ENOMEM;
110	for (i = 0; i < RPC_CREDCACHE_NR; i++)
111		INIT_HLIST_HEAD(&new->hashtable[i]);
112	new->expire = expire;
113	new->nextgc = jiffies + (expire >> 1);
114	auth->au_credcache = new;
115	return 0;
116}
117
118/*
119 * Destroy a list of credentials
120 */
121static inline
122void rpcauth_destroy_credlist(struct hlist_head *head)
123{
124	struct rpc_cred *cred;
125
126	while (!hlist_empty(head)) {
127		cred = hlist_entry(head->first, struct rpc_cred, cr_hash);
128		hlist_del_init(&cred->cr_hash);
129		put_rpccred(cred);
130	}
131}
132
133/*
134 * Clear the RPC credential cache, and delete those credentials
135 * that are not referenced.
136 */
137void
138rpcauth_free_credcache(struct rpc_auth *auth)
139{
140	struct rpc_cred_cache *cache = auth->au_credcache;
141	HLIST_HEAD(free);
142	struct hlist_node *pos, *next;
143	struct rpc_cred	*cred;
144	int		i;
145
146	spin_lock(&rpc_credcache_lock);
147	for (i = 0; i < RPC_CREDCACHE_NR; i++) {
148		hlist_for_each_safe(pos, next, &cache->hashtable[i]) {
149			cred = hlist_entry(pos, struct rpc_cred, cr_hash);
150			__hlist_del(&cred->cr_hash);
151			hlist_add_head(&cred->cr_hash, &free);
152		}
153	}
154	spin_unlock(&rpc_credcache_lock);
155	rpcauth_destroy_credlist(&free);
156}
157
158static void
159rpcauth_prune_expired(struct rpc_auth *auth, struct rpc_cred *cred, struct hlist_head *free)
160{
161	if (atomic_read(&cred->cr_count) != 1)
162	       return;
163	if (time_after(jiffies, cred->cr_expire + auth->au_credcache->expire))
164		cred->cr_flags &= ~RPCAUTH_CRED_UPTODATE;
165	if (!(cred->cr_flags & RPCAUTH_CRED_UPTODATE)) {
166		__hlist_del(&cred->cr_hash);
167		hlist_add_head(&cred->cr_hash, free);
168	}
169}
170
171/*
172 * Remove stale credentials. Avoid sleeping inside the loop.
173 */
174static void
175rpcauth_gc_credcache(struct rpc_auth *auth, struct hlist_head *free)
176{
177	struct rpc_cred_cache *cache = auth->au_credcache;
178	struct hlist_node *pos, *next;
179	struct rpc_cred	*cred;
180	int		i;
181
182	dprintk("RPC:       gc'ing RPC credentials for auth %p\n", auth);
183	for (i = 0; i < RPC_CREDCACHE_NR; i++) {
184		hlist_for_each_safe(pos, next, &cache->hashtable[i]) {
185			cred = hlist_entry(pos, struct rpc_cred, cr_hash);
186			rpcauth_prune_expired(auth, cred, free);
187		}
188	}
189	cache->nextgc = jiffies + cache->expire;
190}
191
192/*
193 * Look up a process' credentials in the authentication cache
194 */
195struct rpc_cred *
196rpcauth_lookup_credcache(struct rpc_auth *auth, struct auth_cred * acred,
197		int flags)
198{
199	struct rpc_cred_cache *cache = auth->au_credcache;
200	HLIST_HEAD(free);
201	struct hlist_node *pos, *next;
202	struct rpc_cred	*new = NULL,
203			*cred = NULL;
204	int		nr = 0;
205
206	if (!(flags & RPCAUTH_LOOKUP_ROOTCREDS))
207		nr = acred->uid & RPC_CREDCACHE_MASK;
208retry:
209	spin_lock(&rpc_credcache_lock);
210	if (time_before(cache->nextgc, jiffies))
211		rpcauth_gc_credcache(auth, &free);
212	hlist_for_each_safe(pos, next, &cache->hashtable[nr]) {
213		struct rpc_cred *entry;
214		entry = hlist_entry(pos, struct rpc_cred, cr_hash);
215		if (entry->cr_ops->crmatch(acred, entry, flags)) {
216			hlist_del(&entry->cr_hash);
217			cred = entry;
218			break;
219		}
220		rpcauth_prune_expired(auth, entry, &free);
221	}
222	if (new) {
223		if (cred)
224			hlist_add_head(&new->cr_hash, &free);
225		else
226			cred = new;
227	}
228	if (cred) {
229		hlist_add_head(&cred->cr_hash, &cache->hashtable[nr]);
230		get_rpccred(cred);
231	}
232	spin_unlock(&rpc_credcache_lock);
233
234	rpcauth_destroy_credlist(&free);
235
236	if (!cred) {
237		new = auth->au_ops->crcreate(auth, acred, flags);
238		if (!IS_ERR(new)) {
239#ifdef RPC_DEBUG
240			new->cr_magic = RPCAUTH_CRED_MAGIC;
241#endif
242			goto retry;
243		} else
244			cred = new;
245	} else if ((cred->cr_flags & RPCAUTH_CRED_NEW)
246			&& cred->cr_ops->cr_init != NULL
247			&& !(flags & RPCAUTH_LOOKUP_NEW)) {
248		int res = cred->cr_ops->cr_init(auth, cred);
249		if (res < 0) {
250			put_rpccred(cred);
251			cred = ERR_PTR(res);
252		}
253	}
254
255	return (struct rpc_cred *) cred;
256}
257
258struct rpc_cred *
259rpcauth_lookupcred(struct rpc_auth *auth, int flags)
260{
261	struct auth_cred acred = {
262		.uid = current->fsuid,
263		.gid = current->fsgid,
264		.group_info = current->group_info,
265	};
266	struct rpc_cred *ret;
267
268	dprintk("RPC:       looking up %s cred\n",
269		auth->au_ops->au_name);
270	get_group_info(acred.group_info);
271	ret = auth->au_ops->lookup_cred(auth, &acred, flags);
272	put_group_info(acred.group_info);
273	return ret;
274}
275
276struct rpc_cred *
277rpcauth_bindcred(struct rpc_task *task)
278{
279	struct rpc_auth *auth = task->tk_auth;
280	struct auth_cred acred = {
281		.uid = current->fsuid,
282		.gid = current->fsgid,
283		.group_info = current->group_info,
284	};
285	struct rpc_cred *ret;
286	int flags = 0;
287
288	dprintk("RPC: %5u looking up %s cred\n",
289		task->tk_pid, task->tk_auth->au_ops->au_name);
290	get_group_info(acred.group_info);
291	if (task->tk_flags & RPC_TASK_ROOTCREDS)
292		flags |= RPCAUTH_LOOKUP_ROOTCREDS;
293	ret = auth->au_ops->lookup_cred(auth, &acred, flags);
294	if (!IS_ERR(ret))
295		task->tk_msg.rpc_cred = ret;
296	else
297		task->tk_status = PTR_ERR(ret);
298	put_group_info(acred.group_info);
299	return ret;
300}
301
302void
303rpcauth_holdcred(struct rpc_task *task)
304{
305	dprintk("RPC: %5u holding %s cred %p\n",
306		task->tk_pid, task->tk_auth->au_ops->au_name,
307		task->tk_msg.rpc_cred);
308	if (task->tk_msg.rpc_cred)
309		get_rpccred(task->tk_msg.rpc_cred);
310}
311
312void
313put_rpccred(struct rpc_cred *cred)
314{
315	cred->cr_expire = jiffies;
316	if (!atomic_dec_and_test(&cred->cr_count))
317		return;
318	cred->cr_ops->crdestroy(cred);
319}
320
321void
322rpcauth_unbindcred(struct rpc_task *task)
323{
324	struct rpc_cred	*cred = task->tk_msg.rpc_cred;
325
326	dprintk("RPC: %5u releasing %s cred %p\n",
327		task->tk_pid, task->tk_auth->au_ops->au_name, cred);
328
329	put_rpccred(cred);
330	task->tk_msg.rpc_cred = NULL;
331}
332
333__be32 *
334rpcauth_marshcred(struct rpc_task *task, __be32 *p)
335{
336	struct rpc_cred	*cred = task->tk_msg.rpc_cred;
337
338	dprintk("RPC: %5u marshaling %s cred %p\n",
339		task->tk_pid, task->tk_auth->au_ops->au_name, cred);
340
341	return cred->cr_ops->crmarshal(task, p);
342}
343
344__be32 *
345rpcauth_checkverf(struct rpc_task *task, __be32 *p)
346{
347	struct rpc_cred	*cred = task->tk_msg.rpc_cred;
348
349	dprintk("RPC: %5u validating %s cred %p\n",
350		task->tk_pid, task->tk_auth->au_ops->au_name, cred);
351
352	return cred->cr_ops->crvalidate(task, p);
353}
354
355int
356rpcauth_wrap_req(struct rpc_task *task, kxdrproc_t encode, void *rqstp,
357		__be32 *data, void *obj)
358{
359	struct rpc_cred *cred = task->tk_msg.rpc_cred;
360
361	dprintk("RPC: %5u using %s cred %p to wrap rpc data\n",
362			task->tk_pid, cred->cr_ops->cr_name, cred);
363	if (cred->cr_ops->crwrap_req)
364		return cred->cr_ops->crwrap_req(task, encode, rqstp, data, obj);
365	/* By default, we encode the arguments normally. */
366	return encode(rqstp, data, obj);
367}
368
369int
370rpcauth_unwrap_resp(struct rpc_task *task, kxdrproc_t decode, void *rqstp,
371		__be32 *data, void *obj)
372{
373	struct rpc_cred *cred = task->tk_msg.rpc_cred;
374
375	dprintk("RPC: %5u using %s cred %p to unwrap rpc data\n",
376			task->tk_pid, cred->cr_ops->cr_name, cred);
377	if (cred->cr_ops->crunwrap_resp)
378		return cred->cr_ops->crunwrap_resp(task, decode, rqstp,
379						   data, obj);
380	/* By default, we decode the arguments normally. */
381	return decode(rqstp, data, obj);
382}
383
384int
385rpcauth_refreshcred(struct rpc_task *task)
386{
387	struct rpc_cred	*cred = task->tk_msg.rpc_cred;
388	int err;
389
390	dprintk("RPC: %5u refreshing %s cred %p\n",
391		task->tk_pid, task->tk_auth->au_ops->au_name, cred);
392
393	err = cred->cr_ops->crrefresh(task);
394	if (err < 0)
395		task->tk_status = err;
396	return err;
397}
398
399void
400rpcauth_invalcred(struct rpc_task *task)
401{
402	dprintk("RPC: %5u invalidating %s cred %p\n",
403		task->tk_pid, task->tk_auth->au_ops->au_name, task->tk_msg.rpc_cred);
404	spin_lock(&rpc_credcache_lock);
405	if (task->tk_msg.rpc_cred)
406		task->tk_msg.rpc_cred->cr_flags &= ~RPCAUTH_CRED_UPTODATE;
407	spin_unlock(&rpc_credcache_lock);
408}
409
410int
411rpcauth_uptodatecred(struct rpc_task *task)
412{
413	return !(task->tk_msg.rpc_cred) ||
414		(task->tk_msg.rpc_cred->cr_flags & RPCAUTH_CRED_UPTODATE);
415}
416