linux/net/sunrpc/svcauth.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3 * linux/net/sunrpc/svcauth.c
   4 *
   5 * The generic interface for RPC authentication on the server side.
   6 *
   7 * Copyright (C) 1995, 1996 Olaf Kirch <okir@monad.swb.de>
   8 *
   9 * CHANGES
  10 * 19-Apr-2000 Chris Evans      - Security fix
  11 */
  12
  13#include <linux/types.h>
  14#include <linux/module.h>
  15#include <linux/sunrpc/types.h>
  16#include <linux/sunrpc/xdr.h>
  17#include <linux/sunrpc/svcsock.h>
  18#include <linux/sunrpc/svcauth.h>
  19#include <linux/err.h>
  20#include <linux/hash.h>
  21
  22#include <trace/events/sunrpc.h>
  23
  24#include "sunrpc.h"
  25
  26#define RPCDBG_FACILITY RPCDBG_AUTH
  27
  28
  29/*
  30 * Table of authenticators
  31 */
  32extern struct auth_ops svcauth_null;
  33extern struct auth_ops svcauth_unix;
  34extern struct auth_ops svcauth_tls;
  35
  36static struct auth_ops __rcu *authtab[RPC_AUTH_MAXFLAVOR] = {
  37        [RPC_AUTH_NULL] = (struct auth_ops __force __rcu *)&svcauth_null,
  38        [RPC_AUTH_UNIX] = (struct auth_ops __force __rcu *)&svcauth_unix,
  39        [RPC_AUTH_TLS]  = (struct auth_ops __force __rcu *)&svcauth_tls,
  40};
  41
  42static struct auth_ops *
  43svc_get_auth_ops(rpc_authflavor_t flavor)
  44{
  45        struct auth_ops         *aops;
  46
  47        if (flavor >= RPC_AUTH_MAXFLAVOR)
  48                return NULL;
  49        rcu_read_lock();
  50        aops = rcu_dereference(authtab[flavor]);
  51        if (aops != NULL && !try_module_get(aops->owner))
  52                aops = NULL;
  53        rcu_read_unlock();
  54        return aops;
  55}
  56
  57static void
  58svc_put_auth_ops(struct auth_ops *aops)
  59{
  60        module_put(aops->owner);
  61}
  62
  63int
  64svc_authenticate(struct svc_rqst *rqstp)
  65{
  66        rpc_authflavor_t        flavor;
  67        struct auth_ops         *aops;
  68
  69        rqstp->rq_auth_stat = rpc_auth_ok;
  70
  71        flavor = svc_getnl(&rqstp->rq_arg.head[0]);
  72
  73        dprintk("svc: svc_authenticate (%d)\n", flavor);
  74
  75        aops = svc_get_auth_ops(flavor);
  76        if (aops == NULL) {
  77                rqstp->rq_auth_stat = rpc_autherr_badcred;
  78                return SVC_DENIED;
  79        }
  80
  81        rqstp->rq_auth_slack = 0;
  82        init_svc_cred(&rqstp->rq_cred);
  83
  84        rqstp->rq_authop = aops;
  85        return aops->accept(rqstp);
  86}
  87EXPORT_SYMBOL_GPL(svc_authenticate);
  88
  89int svc_set_client(struct svc_rqst *rqstp)
  90{
  91        rqstp->rq_client = NULL;
  92        return rqstp->rq_authop->set_client(rqstp);
  93}
  94EXPORT_SYMBOL_GPL(svc_set_client);
  95
  96/* A request, which was authenticated, has now executed.
  97 * Time to finalise the credentials and verifier
  98 * and release and resources
  99 */
 100int svc_authorise(struct svc_rqst *rqstp)
 101{
 102        struct auth_ops *aops = rqstp->rq_authop;
 103        int rv = 0;
 104
 105        rqstp->rq_authop = NULL;
 106
 107        if (aops) {
 108                rv = aops->release(rqstp);
 109                svc_put_auth_ops(aops);
 110        }
 111        return rv;
 112}
 113
 114int
 115svc_auth_register(rpc_authflavor_t flavor, struct auth_ops *aops)
 116{
 117        struct auth_ops *old;
 118        int rv = -EINVAL;
 119
 120        if (flavor < RPC_AUTH_MAXFLAVOR) {
 121                old = cmpxchg((struct auth_ops ** __force)&authtab[flavor], NULL, aops);
 122                if (old == NULL || old == aops)
 123                        rv = 0;
 124        }
 125        return rv;
 126}
 127EXPORT_SYMBOL_GPL(svc_auth_register);
 128
 129void
 130svc_auth_unregister(rpc_authflavor_t flavor)
 131{
 132        if (flavor < RPC_AUTH_MAXFLAVOR)
 133                rcu_assign_pointer(authtab[flavor], NULL);
 134}
 135EXPORT_SYMBOL_GPL(svc_auth_unregister);
 136
 137/**************************************************
 138 * 'auth_domains' are stored in a hash table indexed by name.
 139 * When the last reference to an 'auth_domain' is dropped,
 140 * the object is unhashed and freed.
 141 * If auth_domain_lookup fails to find an entry, it will return
 142 * it's second argument 'new'.  If this is non-null, it will
 143 * have been atomically linked into the table.
 144 */
 145
 146#define DN_HASHBITS     6
 147#define DN_HASHMAX      (1<<DN_HASHBITS)
 148
 149static struct hlist_head        auth_domain_table[DN_HASHMAX];
 150static DEFINE_SPINLOCK(auth_domain_lock);
 151
 152static void auth_domain_release(struct kref *kref)
 153        __releases(&auth_domain_lock)
 154{
 155        struct auth_domain *dom = container_of(kref, struct auth_domain, ref);
 156
 157        hlist_del_rcu(&dom->hash);
 158        dom->flavour->domain_release(dom);
 159        spin_unlock(&auth_domain_lock);
 160}
 161
 162void auth_domain_put(struct auth_domain *dom)
 163{
 164        kref_put_lock(&dom->ref, auth_domain_release, &auth_domain_lock);
 165}
 166EXPORT_SYMBOL_GPL(auth_domain_put);
 167
 168struct auth_domain *
 169auth_domain_lookup(char *name, struct auth_domain *new)
 170{
 171        struct auth_domain *hp;
 172        struct hlist_head *head;
 173
 174        head = &auth_domain_table[hash_str(name, DN_HASHBITS)];
 175
 176        spin_lock(&auth_domain_lock);
 177
 178        hlist_for_each_entry(hp, head, hash) {
 179                if (strcmp(hp->name, name)==0) {
 180                        kref_get(&hp->ref);
 181                        spin_unlock(&auth_domain_lock);
 182                        return hp;
 183                }
 184        }
 185        if (new)
 186                hlist_add_head_rcu(&new->hash, head);
 187        spin_unlock(&auth_domain_lock);
 188        return new;
 189}
 190EXPORT_SYMBOL_GPL(auth_domain_lookup);
 191
 192struct auth_domain *auth_domain_find(char *name)
 193{
 194        struct auth_domain *hp;
 195        struct hlist_head *head;
 196
 197        head = &auth_domain_table[hash_str(name, DN_HASHBITS)];
 198
 199        rcu_read_lock();
 200        hlist_for_each_entry_rcu(hp, head, hash) {
 201                if (strcmp(hp->name, name)==0) {
 202                        if (!kref_get_unless_zero(&hp->ref))
 203                                hp = NULL;
 204                        rcu_read_unlock();
 205                        return hp;
 206                }
 207        }
 208        rcu_read_unlock();
 209        return NULL;
 210}
 211EXPORT_SYMBOL_GPL(auth_domain_find);
 212
 213/**
 214 * auth_domain_cleanup - check that the auth_domain table is empty
 215 *
 216 * On module unload the auth_domain_table must be empty.  To make it
 217 * easier to catch bugs which don't clean up domains properly, we
 218 * warn if anything remains in the table at cleanup time.
 219 *
 220 * Note that we cannot proactively remove the domains at this stage.
 221 * The ->release() function might be in a module that has already been
 222 * unloaded.
 223 */
 224
 225void auth_domain_cleanup(void)
 226{
 227        int h;
 228        struct auth_domain *hp;
 229
 230        for (h = 0; h < DN_HASHMAX; h++)
 231                hlist_for_each_entry(hp, &auth_domain_table[h], hash)
 232                        pr_warn("svc: domain %s still present at module unload.\n",
 233                                hp->name);
 234}
 235