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
  81/*
  82 * Lookup generic creds for current process
  83 */
  84static struct rpc_cred *
  85generic_lookup_cred(struct rpc_auth *auth, struct auth_cred *acred, int flags)
  86{
  87        return rpcauth_lookup_credcache(&generic_auth, acred, flags, GFP_KERNEL);
  88}
  89
  90static struct rpc_cred *
  91generic_create_cred(struct rpc_auth *auth, struct auth_cred *acred, int flags, gfp_t gfp)
  92{
  93        struct generic_cred *gcred;
  94
  95        gcred = kmalloc(sizeof(*gcred), gfp);
  96        if (gcred == NULL)
  97                return ERR_PTR(-ENOMEM);
  98
  99        rpcauth_init_cred(&gcred->gc_base, acred, &generic_auth, &generic_credops);
 100        gcred->gc_base.cr_flags = 1UL << RPCAUTH_CRED_UPTODATE;
 101
 102        gcred->acred.uid = acred->uid;
 103        gcred->acred.gid = acred->gid;
 104        gcred->acred.group_info = acred->group_info;
 105        gcred->acred.ac_flags = 0;
 106        if (gcred->acred.group_info != NULL)
 107                get_group_info(gcred->acred.group_info);
 108        gcred->acred.machine_cred = acred->machine_cred;
 109        gcred->acred.principal = acred->principal;
 110
 111        dprintk("RPC:       allocated %s cred %p for uid %d gid %d\n",
 112                        gcred->acred.machine_cred ? "machine" : "generic",
 113                        gcred,
 114                        from_kuid(&init_user_ns, acred->uid),
 115                        from_kgid(&init_user_ns, acred->gid));
 116        return &gcred->gc_base;
 117}
 118
 119static void
 120generic_free_cred(struct rpc_cred *cred)
 121{
 122        struct generic_cred *gcred = container_of(cred, struct generic_cred, gc_base);
 123
 124        dprintk("RPC:       generic_free_cred %p\n", gcred);
 125        if (gcred->acred.group_info != NULL)
 126                put_group_info(gcred->acred.group_info);
 127        kfree(gcred);
 128}
 129
 130static void
 131generic_free_cred_callback(struct rcu_head *head)
 132{
 133        struct rpc_cred *cred = container_of(head, struct rpc_cred, cr_rcu);
 134        generic_free_cred(cred);
 135}
 136
 137static void
 138generic_destroy_cred(struct rpc_cred *cred)
 139{
 140        call_rcu(&cred->cr_rcu, generic_free_cred_callback);
 141}
 142
 143static int
 144machine_cred_match(struct auth_cred *acred, struct generic_cred *gcred, int flags)
 145{
 146        if (!gcred->acred.machine_cred ||
 147            gcred->acred.principal != acred->principal ||
 148            !uid_eq(gcred->acred.uid, acred->uid) ||
 149            !gid_eq(gcred->acred.gid, acred->gid))
 150                return 0;
 151        return 1;
 152}
 153
 154/*
 155 * Match credentials against current process creds.
 156 */
 157static int
 158generic_match(struct auth_cred *acred, struct rpc_cred *cred, int flags)
 159{
 160        struct generic_cred *gcred = container_of(cred, struct generic_cred, gc_base);
 161        int i;
 162
 163        if (acred->machine_cred)
 164                return machine_cred_match(acred, gcred, flags);
 165
 166        if (!uid_eq(gcred->acred.uid, acred->uid) ||
 167            !gid_eq(gcred->acred.gid, acred->gid) ||
 168            gcred->acred.machine_cred != 0)
 169                goto out_nomatch;
 170
 171        /* Optimisation in the case where pointers are identical... */
 172        if (gcred->acred.group_info == acred->group_info)
 173                goto out_match;
 174
 175        /* Slow path... */
 176        if (gcred->acred.group_info->ngroups != acred->group_info->ngroups)
 177                goto out_nomatch;
 178        for (i = 0; i < gcred->acred.group_info->ngroups; i++) {
 179                if (!gid_eq(GROUP_AT(gcred->acred.group_info, i),
 180                                GROUP_AT(acred->group_info, i)))
 181                        goto out_nomatch;
 182        }
 183out_match:
 184        return 1;
 185out_nomatch:
 186        return 0;
 187}
 188
 189int __init rpc_init_generic_auth(void)
 190{
 191        return rpcauth_init_credcache(&generic_auth);
 192}
 193
 194void rpc_destroy_generic_auth(void)
 195{
 196        rpcauth_destroy_credcache(&generic_auth);
 197}
 198
 199/*
 200 * Test the the current time (now) against the underlying credential key expiry
 201 * minus a timeout and setup notification.
 202 *
 203 * The normal case:
 204 * If 'now' is before the key expiry minus RPC_KEY_EXPIRE_TIMEO, set
 205 * the RPC_CRED_NOTIFY_TIMEOUT flag to setup the underlying credential
 206 * rpc_credops crmatch routine to notify this generic cred when it's key
 207 * expiration is within RPC_KEY_EXPIRE_TIMEO, and return 0.
 208 *
 209 * The error case:
 210 * If the underlying cred lookup fails, return -EACCES.
 211 *
 212 * The 'almost' error case:
 213 * If 'now' is within key expiry minus RPC_KEY_EXPIRE_TIMEO, but not within
 214 * key expiry minus RPC_KEY_EXPIRE_FAIL, set the RPC_CRED_EXPIRE_SOON bit
 215 * on the acred ac_flags and return 0.
 216 */
 217static int
 218generic_key_timeout(struct rpc_auth *auth, struct rpc_cred *cred)
 219{
 220        struct auth_cred *acred = &container_of(cred, struct generic_cred,
 221                                                gc_base)->acred;
 222        struct rpc_cred *tcred;
 223        int ret = 0;
 224
 225
 226        /* Fast track for non crkey_timeout (no key) underlying credentials */
 227        if (auth->au_flags & RPCAUTH_AUTH_NO_CRKEY_TIMEOUT)
 228                return 0;
 229
 230        /* Fast track for the normal case */
 231        if (test_bit(RPC_CRED_NOTIFY_TIMEOUT, &acred->ac_flags))
 232                return 0;
 233
 234        /* lookup_cred either returns a valid referenced rpc_cred, or PTR_ERR */
 235        tcred = auth->au_ops->lookup_cred(auth, acred, 0);
 236        if (IS_ERR(tcred))
 237                return -EACCES;
 238
 239        /* Test for the almost error case */
 240        ret = tcred->cr_ops->crkey_timeout(tcred);
 241        if (ret != 0) {
 242                set_bit(RPC_CRED_KEY_EXPIRE_SOON, &acred->ac_flags);
 243                ret = 0;
 244        } else {
 245                /* In case underlying cred key has been reset */
 246                if (test_and_clear_bit(RPC_CRED_KEY_EXPIRE_SOON,
 247                                        &acred->ac_flags))
 248                        dprintk("RPC:        UID %d Credential key reset\n",
 249                                from_kuid(&init_user_ns, tcred->cr_uid));
 250                /* set up fasttrack for the normal case */
 251                set_bit(RPC_CRED_NOTIFY_TIMEOUT, &acred->ac_flags);
 252        }
 253
 254        put_rpccred(tcred);
 255        return ret;
 256}
 257
 258static const struct rpc_authops generic_auth_ops = {
 259        .owner = THIS_MODULE,
 260        .au_name = "Generic",
 261        .lookup_cred = generic_lookup_cred,
 262        .crcreate = generic_create_cred,
 263        .key_timeout = generic_key_timeout,
 264};
 265
 266static struct rpc_auth generic_auth = {
 267        .au_ops = &generic_auth_ops,
 268        .au_count = ATOMIC_INIT(0),
 269};
 270
 271static bool generic_key_to_expire(struct rpc_cred *cred)
 272{
 273        struct auth_cred *acred = &container_of(cred, struct generic_cred,
 274                                                gc_base)->acred;
 275        bool ret;
 276
 277        get_rpccred(cred);
 278        ret = test_bit(RPC_CRED_KEY_EXPIRE_SOON, &acred->ac_flags);
 279        put_rpccred(cred);
 280
 281        return ret;
 282}
 283
 284static const struct rpc_credops generic_credops = {
 285        .cr_name = "Generic cred",
 286        .crdestroy = generic_destroy_cred,
 287        .crbind = generic_bind_cred,
 288        .crmatch = generic_match,
 289        .crkey_to_expire = generic_key_to_expire,
 290};
 291