cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

svcauth.c (5483B)


      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}