1 /*
2 * linux/fs/nfs/rpcauth.c
3 *
4 * Generic RPC 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/slab.h>
12 #include <linux/errno.h>
13 #include <linux/socket.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
21 #define RPC_MAXFLAVOR 8
22
23 static struct rpc_authops * auth_flavors[RPC_MAXFLAVOR] = {
24 &authnull_ops, /* AUTH_NULL */
25 &authunix_ops, /* AUTH_UNIX */
26 NULL, /* others can be loadable modules */
27 };
28
29 int
rpcauth_register(struct rpc_authops * ops)30 rpcauth_register(struct rpc_authops *ops)
31 {
32 unsigned int flavor;
33
34 if ((flavor = ops->au_flavor) >= RPC_MAXFLAVOR)
35 return -EINVAL;
36 if (auth_flavors[flavor] != NULL)
37 return -EPERM; /* what else? */
38 auth_flavors[flavor] = ops;
39 return 0;
40 }
41
42 int
rpcauth_unregister(struct rpc_authops * ops)43 rpcauth_unregister(struct rpc_authops *ops)
44 {
45 unsigned int flavor;
46
47 if ((flavor = ops->au_flavor) >= RPC_MAXFLAVOR)
48 return -EINVAL;
49 if (auth_flavors[flavor] != ops)
50 return -EPERM; /* what else? */
51 auth_flavors[flavor] = NULL;
52 return 0;
53 }
54
55 struct rpc_auth *
rpcauth_create(unsigned int flavor,struct rpc_clnt * clnt)56 rpcauth_create(unsigned int flavor, struct rpc_clnt *clnt)
57 {
58 struct rpc_authops *ops;
59
60 if (flavor >= RPC_MAXFLAVOR || !(ops = auth_flavors[flavor]))
61 return NULL;
62 clnt->cl_auth = ops->create(clnt);
63 return clnt->cl_auth;
64 }
65
66 void
rpcauth_destroy(struct rpc_auth * auth)67 rpcauth_destroy(struct rpc_auth *auth)
68 {
69 auth->au_ops->destroy(auth);
70 }
71
72 static spinlock_t rpc_credcache_lock = SPIN_LOCK_UNLOCKED;
73
74 /*
75 * Initialize RPC credential cache
76 */
77 void
rpcauth_init_credcache(struct rpc_auth * auth)78 rpcauth_init_credcache(struct rpc_auth *auth)
79 {
80 memset(auth->au_credcache, 0, sizeof(auth->au_credcache));
81 auth->au_nextgc = jiffies + (auth->au_expire >> 1);
82 }
83
84 /*
85 * Destroy an unreferenced credential
86 */
87 static inline void
rpcauth_crdestroy(struct rpc_cred * cred)88 rpcauth_crdestroy(struct rpc_cred *cred)
89 {
90 #ifdef RPC_DEBUG
91 if (cred->cr_magic != RPCAUTH_CRED_MAGIC)
92 BUG();
93 cred->cr_magic = 0;
94 if (atomic_read(&cred->cr_count) || cred->cr_auth)
95 BUG();
96 #endif
97 cred->cr_ops->crdestroy(cred);
98 }
99
100 /*
101 * Destroy a list of credentials
102 */
103 static inline
rpcauth_destroy_credlist(struct rpc_cred * head)104 void rpcauth_destroy_credlist(struct rpc_cred *head)
105 {
106 struct rpc_cred *cred;
107
108 while ((cred = head) != NULL) {
109 head = cred->cr_next;
110 rpcauth_crdestroy(cred);
111 }
112 }
113
114 /*
115 * Clear the RPC credential cache, and delete those credentials
116 * that are not referenced.
117 */
118 void
rpcauth_free_credcache(struct rpc_auth * auth)119 rpcauth_free_credcache(struct rpc_auth *auth)
120 {
121 struct rpc_cred **q, *cred, *free = NULL;
122 int i;
123
124 spin_lock(&rpc_credcache_lock);
125 for (i = 0; i < RPC_CREDCACHE_NR; i++) {
126 q = &auth->au_credcache[i];
127 while ((cred = *q) != NULL) {
128 *q = cred->cr_next;
129 cred->cr_auth = NULL;
130 if (atomic_read(&cred->cr_count) == 0) {
131 cred->cr_next = free;
132 free = cred;
133 } else
134 cred->cr_next = NULL;
135 }
136 }
137 spin_unlock(&rpc_credcache_lock);
138 rpcauth_destroy_credlist(free);
139 }
140
141 /*
142 * Remove stale credentials. Avoid sleeping inside the loop.
143 */
144 static void
rpcauth_gc_credcache(struct rpc_auth * auth)145 rpcauth_gc_credcache(struct rpc_auth *auth)
146 {
147 struct rpc_cred **q, *cred, *free = NULL;
148 int i;
149
150 dprintk("RPC: gc'ing RPC credentials for auth %p\n", auth);
151 spin_lock(&rpc_credcache_lock);
152 for (i = 0; i < RPC_CREDCACHE_NR; i++) {
153 q = &auth->au_credcache[i];
154 while ((cred = *q) != NULL) {
155 if (!atomic_read(&cred->cr_count) &&
156 time_before(cred->cr_expire, jiffies)) {
157 *q = cred->cr_next;
158 cred->cr_auth = NULL;
159 cred->cr_next = free;
160 free = cred;
161 continue;
162 }
163 q = &cred->cr_next;
164 }
165 }
166 spin_unlock(&rpc_credcache_lock);
167 rpcauth_destroy_credlist(free);
168 auth->au_nextgc = jiffies + auth->au_expire;
169 }
170
171 /*
172 * Insert credential into cache
173 */
174 void
rpcauth_insert_credcache(struct rpc_auth * auth,struct rpc_cred * cred)175 rpcauth_insert_credcache(struct rpc_auth *auth, struct rpc_cred *cred)
176 {
177 int nr;
178
179 nr = (cred->cr_uid & RPC_CREDCACHE_MASK);
180 spin_lock(&rpc_credcache_lock);
181 cred->cr_next = auth->au_credcache[nr];
182 auth->au_credcache[nr] = cred;
183 cred->cr_auth = auth;
184 get_rpccred(cred);
185 spin_unlock(&rpc_credcache_lock);
186 }
187
188 /*
189 * Look up a process' credentials in the authentication cache
190 */
191 static struct rpc_cred *
rpcauth_lookup_credcache(struct rpc_auth * auth,int taskflags)192 rpcauth_lookup_credcache(struct rpc_auth *auth, int taskflags)
193 {
194 struct rpc_cred **q, *cred = NULL;
195 int nr = 0;
196
197 if (!(taskflags & RPC_TASK_ROOTCREDS))
198 nr = current->uid & RPC_CREDCACHE_MASK;
199
200 if (time_before(auth->au_nextgc, jiffies))
201 rpcauth_gc_credcache(auth);
202
203 spin_lock(&rpc_credcache_lock);
204 q = &auth->au_credcache[nr];
205 while ((cred = *q) != NULL) {
206 if (!(cred->cr_flags & RPCAUTH_CRED_DEAD) &&
207 cred->cr_ops->crmatch(cred, taskflags)) {
208 *q = cred->cr_next;
209 break;
210 }
211 q = &cred->cr_next;
212 }
213 spin_unlock(&rpc_credcache_lock);
214
215 if (!cred) {
216 cred = auth->au_ops->crcreate(taskflags);
217 #ifdef RPC_DEBUG
218 if (cred)
219 cred->cr_magic = RPCAUTH_CRED_MAGIC;
220 #endif
221 }
222
223 if (cred)
224 rpcauth_insert_credcache(auth, cred);
225
226 return (struct rpc_cred *) cred;
227 }
228
229 /*
230 * Remove cred handle from cache
231 */
232 static void
rpcauth_remove_credcache(struct rpc_cred * cred)233 rpcauth_remove_credcache(struct rpc_cred *cred)
234 {
235 struct rpc_auth *auth = cred->cr_auth;
236 struct rpc_cred **q, *cr;
237 int nr;
238
239 nr = (cred->cr_uid & RPC_CREDCACHE_MASK);
240 q = &auth->au_credcache[nr];
241 while ((cr = *q) != NULL) {
242 if (cred == cr) {
243 *q = cred->cr_next;
244 cred->cr_next = NULL;
245 cred->cr_auth = NULL;
246 break;
247 }
248 q = &cred->cr_next;
249 }
250 }
251
252 struct rpc_cred *
rpcauth_lookupcred(struct rpc_auth * auth,int taskflags)253 rpcauth_lookupcred(struct rpc_auth *auth, int taskflags)
254 {
255 dprintk("RPC: looking up %s cred\n",
256 auth->au_ops->au_name);
257 return rpcauth_lookup_credcache(auth, taskflags);
258 }
259
260 struct rpc_cred *
rpcauth_bindcred(struct rpc_task * task)261 rpcauth_bindcred(struct rpc_task *task)
262 {
263 struct rpc_auth *auth = task->tk_auth;
264
265 dprintk("RPC: %4d looking up %s cred\n",
266 task->tk_pid, task->tk_auth->au_ops->au_name);
267 task->tk_msg.rpc_cred = rpcauth_lookup_credcache(auth, task->tk_flags);
268 if (task->tk_msg.rpc_cred == 0)
269 task->tk_status = -ENOMEM;
270 return task->tk_msg.rpc_cred;
271 }
272
273 int
rpcauth_matchcred(struct rpc_auth * auth,struct rpc_cred * cred,int taskflags)274 rpcauth_matchcred(struct rpc_auth *auth, struct rpc_cred *cred, int taskflags)
275 {
276 dprintk("RPC: matching %s cred %d\n",
277 auth->au_ops->au_name, taskflags);
278 return cred->cr_ops->crmatch(cred, taskflags);
279 }
280
281 void
rpcauth_holdcred(struct rpc_task * task)282 rpcauth_holdcred(struct rpc_task *task)
283 {
284 dprintk("RPC: %4d holding %s cred %p\n",
285 task->tk_pid, task->tk_auth->au_ops->au_name, task->tk_msg.rpc_cred);
286 if (task->tk_msg.rpc_cred)
287 get_rpccred(task->tk_msg.rpc_cred);
288 }
289
290 void
put_rpccred(struct rpc_cred * cred)291 put_rpccred(struct rpc_cred *cred)
292 {
293 if (!atomic_dec_and_lock(&cred->cr_count, &rpc_credcache_lock))
294 return;
295
296 if (cred->cr_auth && cred->cr_flags & RPCAUTH_CRED_DEAD)
297 rpcauth_remove_credcache(cred);
298
299 if (!cred->cr_auth) {
300 spin_unlock(&rpc_credcache_lock);
301 rpcauth_crdestroy(cred);
302 return;
303 }
304 cred->cr_expire = jiffies + cred->cr_auth->au_expire;
305 spin_unlock(&rpc_credcache_lock);
306 }
307
308 void
rpcauth_unbindcred(struct rpc_task * task)309 rpcauth_unbindcred(struct rpc_task *task)
310 {
311 struct rpc_auth *auth = task->tk_auth;
312 struct rpc_cred *cred = task->tk_msg.rpc_cred;
313
314 dprintk("RPC: %4d releasing %s cred %p\n",
315 task->tk_pid, auth->au_ops->au_name, cred);
316
317 put_rpccred(cred);
318 task->tk_msg.rpc_cred = NULL;
319 }
320
321 u32 *
rpcauth_marshcred(struct rpc_task * task,u32 * p)322 rpcauth_marshcred(struct rpc_task *task, u32 *p)
323 {
324 struct rpc_auth *auth = task->tk_auth;
325 struct rpc_cred *cred = task->tk_msg.rpc_cred;
326
327 dprintk("RPC: %4d marshaling %s cred %p\n",
328 task->tk_pid, auth->au_ops->au_name, cred);
329 return cred->cr_ops->crmarshal(task, p,
330 task->tk_flags & RPC_CALL_REALUID);
331 }
332
333 u32 *
rpcauth_checkverf(struct rpc_task * task,u32 * p)334 rpcauth_checkverf(struct rpc_task *task, u32 *p)
335 {
336 struct rpc_auth *auth = task->tk_auth;
337 struct rpc_cred *cred = task->tk_msg.rpc_cred;
338
339 dprintk("RPC: %4d validating %s cred %p\n",
340 task->tk_pid, auth->au_ops->au_name, cred);
341 return cred->cr_ops->crvalidate(task, p);
342 }
343
344 int
rpcauth_refreshcred(struct rpc_task * task)345 rpcauth_refreshcred(struct rpc_task *task)
346 {
347 struct rpc_auth *auth = task->tk_auth;
348 struct rpc_cred *cred = task->tk_msg.rpc_cred;
349
350 dprintk("RPC: %4d refreshing %s cred %p\n",
351 task->tk_pid, auth->au_ops->au_name, cred);
352 task->tk_status = cred->cr_ops->crrefresh(task);
353 return task->tk_status;
354 }
355
356 void
rpcauth_invalcred(struct rpc_task * task)357 rpcauth_invalcred(struct rpc_task *task)
358 {
359 dprintk("RPC: %4d invalidating %s cred %p\n",
360 task->tk_pid, task->tk_auth->au_ops->au_name, task->tk_msg.rpc_cred);
361 spin_lock(&rpc_credcache_lock);
362 if (task->tk_msg.rpc_cred)
363 task->tk_msg.rpc_cred->cr_flags &= ~RPCAUTH_CRED_UPTODATE;
364 spin_unlock(&rpc_credcache_lock);
365 }
366
367 int
rpcauth_uptodatecred(struct rpc_task * task)368 rpcauth_uptodatecred(struct rpc_task *task)
369 {
370 int retval;
371 spin_lock(&rpc_credcache_lock);
372 retval = !(task->tk_msg.rpc_cred) ||
373 (task->tk_msg.rpc_cred->cr_flags & RPCAUTH_CRED_UPTODATE);
374 spin_unlock(&rpc_credcache_lock);
375 return retval;
376 }
377