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