linux/net/sunrpc/auth_generic.c
<<
>>
Prefs
   1/*
   2 * Generic RPC credential
   3 *
   4 * Copyright (C) 2008, Trond Myklebust <Trond.Myklebust@netapp.com>
   5 */
   6
   7#include <linux/err.h>
   8#include <linux/slab.h>
   9#include <linux/types.h>
  10#include <linux/module.h>
  11#include <linux/sched.h>
  12#include <linux/sunrpc/auth.h>
  13#include <linux/sunrpc/clnt.h>
  14#include <linux/sunrpc/debug.h>
  15#include <linux/sunrpc/sched.h>
  16
  17#if IS_ENABLED(CONFIG_SUNRPC_DEBUG)
  18# define RPCDBG_FACILITY        RPCDBG_AUTH
  19#endif
  20
  21#define RPC_MACHINE_CRED_USERID         GLOBAL_ROOT_UID
  22#define RPC_MACHINE_CRED_GROUPID        GLOBAL_ROOT_GID
  23
  24struct generic_cred {
  25        struct rpc_cred gc_base;
  26        struct auth_cred acred;
  27};
  28
  29static struct rpc_auth generic_auth;
  30static const struct rpc_credops generic_credops;
  31
  32/*
  33 * Public call interface
  34 */
  35struct rpc_cred *rpc_lookup_cred(void)
  36{
  37        return rpcauth_lookupcred(&generic_auth, 0);
  38}
  39EXPORT_SYMBOL_GPL(rpc_lookup_cred);
  40
  41struct rpc_cred *
  42rpc_lookup_generic_cred(struct auth_cred *acred, int flags, gfp_t gfp)
  43{
  44        return rpcauth_lookup_credcache(&generic_auth, acred, flags, gfp);
  45}
  46EXPORT_SYMBOL_GPL(rpc_lookup_generic_cred);
  47
  48struct rpc_cred *rpc_lookup_cred_nonblock(void)
  49{
  50        return rpcauth_lookupcred(&generic_auth, RPCAUTH_LOOKUP_RCU);
  51}
  52EXPORT_SYMBOL_GPL(rpc_lookup_cred_nonblock);
  53
  54/*
  55 * Public call interface for looking up machine creds.
  56 */
  57struct rpc_cred *rpc_lookup_machine_cred(const char *service_name)
  58{
  59        struct auth_cred acred = {
  60                .uid = RPC_MACHINE_CRED_USERID,
  61                .gid = RPC_MACHINE_CRED_GROUPID,
  62                .principal = service_name,
  63                .machine_cred = 1,
  64        };
  65
  66        dprintk("RPC:       looking up machine cred for service %s\n",
  67                        service_name);
  68        return generic_auth.au_ops->lookup_cred(&generic_auth, &acred, 0);
  69}
  70EXPORT_SYMBOL_GPL(rpc_lookup_machine_cred);
  71
  72static struct rpc_cred *generic_bind_cred(struct rpc_task *task,
  73                struct rpc_cred *cred, int lookupflags)
  74{
  75        struct rpc_auth *auth = task->tk_client->cl_auth;
  76        struct auth_cred *acred = &container_of(cred, struct generic_cred, gc_base)->acred;
  77
  78        return auth->au_ops->lookup_cred(auth, acred, lookupflags);
  79}
  80
  81static int
  82generic_hash_cred(struct auth_cred *acred, unsigned int hashbits)
  83{
  84        return hash_64(from_kgid(&init_user_ns, acred->gid) |
  85                ((u64)from_kuid(&init_user_ns, acred->uid) <<
  86                        (sizeof(gid_t) * 8)), hashbits);
  87}
  88
  89/*
  90 * Lookup generic creds for current process
  91 */
  92static struct rpc_cred *
  93generic_lookup_cred(struct rpc_auth *auth, struct auth_cred *acred, int flags)
  94{
  95        return rpcauth_lookup_credcache(&generic_auth, acred, flags, GFP_KERNEL);
  96}
  97
  98static struct rpc_cred *
  99generic_create_cred(struct rpc_auth *auth, struct auth_cred *acred, int flags, gfp_t gfp)
 100{
 101        struct generic_cred *gcred;
 102
 103        gcred = kmalloc(sizeof(*gcred), gfp);
 104        if (gcred == NULL)
 105                return ERR_PTR(-ENOMEM);
 106
 107        rpcauth_init_cred(&gcred->gc_base, acred, &generic_auth, &generic_credops);
 108        gcred->gc_base.cr_flags = 1UL << RPCAUTH_CRED_UPTODATE;
 109
 110        gcred->acred.uid = acred->uid;
 111        gcred->acred.gid = acred->gid;
 112        gcred->acred.group_info = acred->group_info;
 113        gcred->acred.ac_flags = 0;
 114        if (gcred->acred.group_info != NULL)
 115                get_group_info(gcred->acred.group_info);
 116        gcred->acred.machine_cred = acred->machine_cred;
 117        gcred->acred.principal = acred->principal;
 118
 119        dprintk("RPC:       allocated %s cred %p for uid %d gid %d\n",
 120                        gcred->acred.machine_cred ? "machine" : "generic",
 121                        gcred,
 122                        from_kuid(&init_user_ns, acred->uid),
 123                        from_kgid(&init_user_ns, acred->gid));
 124        return &gcred->gc_base;
 125}
 126
 127static void
 128generic_free_cred(struct rpc_cred *cred)
 129{
 130        struct generic_cred *gcred = container_of(cred, struct generic_cred, gc_base);
 131
 132        dprintk("RPC:       generic_free_cred %p\n", gcred);
 133        if (gcred->acred.group_info != NULL)
 134                put_group_info(gcred->acred.group_info);
 135        kfree(gcred);
 136}
 137
 138static void
 139generic_free_cred_callback(struct rcu_head *head)
 140{
 141        struct rpc_cred *cred = container_of(head, struct rpc_cred, cr_rcu);
 142        generic_free_cred(cred);
 143}
 144
 145static void
 146generic_destroy_cred(struct rpc_cred *cred)
 147{
 148        call_rcu(&cred->cr_rcu, generic_free_cred_callback);
 149}
 150
 151static int
 152machine_cred_match(struct auth_cred *acred, struct generic_cred *gcred, int flags)
 153{
 154        if (!gcred->acred.machine_cred ||
 155            gcred->acred.principal != acred->principal ||
 156            !uid_eq(gcred->acred.uid, acred->uid) ||
 157            !gid_eq(gcred->acred.gid, acred->gid))
 158                return 0;
 159        return 1;
 160}
 161
 162/*
 163 * Match credentials against current process creds.
 164 */
 165static int
 166generic_match(struct auth_cred *acred, struct rpc_cred *cred, int flags)
 167{
 168        struct generic_cred *gcred = container_of(cred, struct generic_cred, gc_base);
 169        int i;
 170
 171        if (acred->machine_cred)
 172                return machine_cred_match(acred, gcred, flags);
 173
 174        if (!uid_eq(gcred->acred.uid, acred->uid) ||
 175            !gid_eq(gcred->acred.gid, acred->gid) ||
 176            gcred->acred.machine_cred != 0)
 177                goto out_nomatch;
 178
 179        /* Optimisation in the case where pointers are identical... */
 180        if (gcred->acred.group_info == acred->group_info)
 181                goto out_match;
 182
 183        /* Slow path... */
 184        if (gcred->acred.group_info->ngroups != acred->group_info->ngroups)
 185                goto out_nomatch;
 186        for (i = 0; i < gcred->acred.group_info->ngroups; i++) {
 187                if (!gid_eq(gcred->acred.group_info->gid[i],
 188                                acred->group_info->gid[i]))
 189                        goto out_nomatch;
 190        }
 191out_match:
 192        return 1;
 193out_nomatch:
 194        return 0;
 195}
 196
 197int __init rpc_init_generic_auth(void)
 198{
 199        return rpcauth_init_credcache(&generic_auth);
 200}
 201
 202void rpc_destroy_generic_auth(void)
 203{
 204        rpcauth_destroy_credcache(&generic_auth);
 205}
 206
 207/*
 208 * Test the the current time (now) against the underlying credential key expiry
 209 * minus a timeout and setup notification.
 210 *
 211 * The normal case:
 212 * If 'now' is before the key expiry minus RPC_KEY_EXPIRE_TIMEO, set
 213 * the RPC_CRED_NOTIFY_TIMEOUT flag to setup the underlying credential
 214 * rpc_credops crmatch routine to notify this generic cred when it's key
 215 * expiration is within RPC_KEY_EXPIRE_TIMEO, and return 0.
 216 *
 217 * The error case:
 218 * If the underlying cred lookup fails, return -EACCES.
 219 *
 220 * The 'almost' error case:
 221 * If 'now' is within key expiry minus RPC_KEY_EXPIRE_TIMEO, but not within
 222 * key expiry minus RPC_KEY_EXPIRE_FAIL, set the RPC_CRED_EXPIRE_SOON bit
 223 * on the acred ac_flags and return 0.
 224 */
 225static int
 226generic_key_timeout(struct rpc_auth *auth, struct rpc_cred *cred)
 227{
 228        struct auth_cred *acred = &container_of(cred, struct generic_cred,
 229                                                gc_base)->acred;
 230        struct rpc_cred *tcred;
 231        int ret = 0;
 232
 233
 234        /* Fast track for non crkey_timeout (no key) underlying credentials */
 235        if (auth->au_flags & RPCAUTH_AUTH_NO_CRKEY_TIMEOUT)
 236                return 0;
 237
 238        /* Fast track for the normal case */
 239        if (test_bit(RPC_CRED_NOTIFY_TIMEOUT, &acred->ac_flags))
 240                return 0;
 241
 242        /* lookup_cred either returns a valid referenced rpc_cred, or PTR_ERR */
 243        tcred = auth->au_ops->lookup_cred(auth, acred, 0);
 244        if (IS_ERR(tcred))
 245                return -EACCES;
 246
 247        /* Test for the almost error case */
 248        ret = tcred->cr_ops->crkey_timeout(tcred);
 249        if (ret != 0) {
 250                set_bit(RPC_CRED_KEY_EXPIRE_SOON, &acred->ac_flags);
 251                ret = 0;
 252        } else {
 253                /* In case underlying cred key has been reset */
 254                if (test_and_clear_bit(RPC_CRED_KEY_EXPIRE_SOON,
 255                                        &acred->ac_flags))
 256                        dprintk("RPC:        UID %d Credential key reset\n",
 257                                from_kuid(&init_user_ns, tcred->cr_uid));
 258                /* set up fasttrack for the normal case */
 259                set_bit(RPC_CRED_NOTIFY_TIMEOUT, &acred->ac_flags);
 260        }
 261
 262        put_rpccred(tcred);
 263        return ret;
 264}
 265
 266static const struct rpc_authops generic_auth_ops = {
 267        .owner = THIS_MODULE,
 268        .au_name = "Generic",
 269        .hash_cred = generic_hash_cred,
 270        .lookup_cred = generic_lookup_cred,
 271        .crcreate = generic_create_cred,
 272        .key_timeout = generic_key_timeout,
 273};
 274
 275static struct rpc_auth generic_auth = {
 276        .au_ops = &generic_auth_ops,
 277        .au_count = ATOMIC_INIT(0),
 278};
 279
 280static bool generic_key_to_expire(struct rpc_cred *cred)
 281{
 282        struct auth_cred *acred = &container_of(cred, struct generic_cred,
 283                                                gc_base)->acred;
 284        bool ret;
 285
 286        get_rpccred(cred);
 287        ret = test_bit(RPC_CRED_KEY_EXPIRE_SOON, &acred->ac_flags);
 288        put_rpccred(cred);
 289
 290        return ret;
 291}
 292
 293static const struct rpc_credops generic_credops = {
 294        .cr_name = "Generic cred",
 295        .crdestroy = generic_destroy_cred,
 296        .crbind = generic_bind_cred,
 297        .crmatch = generic_match,
 298        .crkey_to_expire = generic_key_to_expire,
 299};
 300