18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * linux/net/sunrpc/svcauth.c
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * The generic interface for RPC authentication on the server side.
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci * Copyright (C) 1995, 1996 Olaf Kirch <okir@monad.swb.de>
88c2ecf20Sopenharmony_ci *
98c2ecf20Sopenharmony_ci * CHANGES
108c2ecf20Sopenharmony_ci * 19-Apr-2000 Chris Evans      - Security fix
118c2ecf20Sopenharmony_ci */
128c2ecf20Sopenharmony_ci
138c2ecf20Sopenharmony_ci#include <linux/types.h>
148c2ecf20Sopenharmony_ci#include <linux/module.h>
158c2ecf20Sopenharmony_ci#include <linux/sunrpc/types.h>
168c2ecf20Sopenharmony_ci#include <linux/sunrpc/xdr.h>
178c2ecf20Sopenharmony_ci#include <linux/sunrpc/svcsock.h>
188c2ecf20Sopenharmony_ci#include <linux/sunrpc/svcauth.h>
198c2ecf20Sopenharmony_ci#include <linux/err.h>
208c2ecf20Sopenharmony_ci#include <linux/hash.h>
218c2ecf20Sopenharmony_ci
228c2ecf20Sopenharmony_ci#include <trace/events/sunrpc.h>
238c2ecf20Sopenharmony_ci
248c2ecf20Sopenharmony_ci#include "sunrpc.h"
258c2ecf20Sopenharmony_ci
268c2ecf20Sopenharmony_ci#define RPCDBG_FACILITY	RPCDBG_AUTH
278c2ecf20Sopenharmony_ci
288c2ecf20Sopenharmony_ci
298c2ecf20Sopenharmony_ci/*
308c2ecf20Sopenharmony_ci * Table of authenticators
318c2ecf20Sopenharmony_ci */
328c2ecf20Sopenharmony_ciextern struct auth_ops svcauth_null;
338c2ecf20Sopenharmony_ciextern struct auth_ops svcauth_unix;
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_cistatic struct auth_ops __rcu *authtab[RPC_AUTH_MAXFLAVOR] = {
368c2ecf20Sopenharmony_ci	[RPC_AUTH_NULL] = (struct auth_ops __force __rcu *)&svcauth_null,
378c2ecf20Sopenharmony_ci	[RPC_AUTH_UNIX] = (struct auth_ops __force __rcu *)&svcauth_unix,
388c2ecf20Sopenharmony_ci};
398c2ecf20Sopenharmony_ci
408c2ecf20Sopenharmony_cistatic struct auth_ops *
418c2ecf20Sopenharmony_cisvc_get_auth_ops(rpc_authflavor_t flavor)
428c2ecf20Sopenharmony_ci{
438c2ecf20Sopenharmony_ci	struct auth_ops		*aops;
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_ci	if (flavor >= RPC_AUTH_MAXFLAVOR)
468c2ecf20Sopenharmony_ci		return NULL;
478c2ecf20Sopenharmony_ci	rcu_read_lock();
488c2ecf20Sopenharmony_ci	aops = rcu_dereference(authtab[flavor]);
498c2ecf20Sopenharmony_ci	if (aops != NULL && !try_module_get(aops->owner))
508c2ecf20Sopenharmony_ci		aops = NULL;
518c2ecf20Sopenharmony_ci	rcu_read_unlock();
528c2ecf20Sopenharmony_ci	return aops;
538c2ecf20Sopenharmony_ci}
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_cistatic void
568c2ecf20Sopenharmony_cisvc_put_auth_ops(struct auth_ops *aops)
578c2ecf20Sopenharmony_ci{
588c2ecf20Sopenharmony_ci	module_put(aops->owner);
598c2ecf20Sopenharmony_ci}
608c2ecf20Sopenharmony_ci
618c2ecf20Sopenharmony_ciint
628c2ecf20Sopenharmony_cisvc_authenticate(struct svc_rqst *rqstp, __be32 *authp)
638c2ecf20Sopenharmony_ci{
648c2ecf20Sopenharmony_ci	rpc_authflavor_t	flavor;
658c2ecf20Sopenharmony_ci	struct auth_ops		*aops;
668c2ecf20Sopenharmony_ci
678c2ecf20Sopenharmony_ci	*authp = rpc_auth_ok;
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_ci	flavor = svc_getnl(&rqstp->rq_arg.head[0]);
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_ci	dprintk("svc: svc_authenticate (%d)\n", flavor);
728c2ecf20Sopenharmony_ci
738c2ecf20Sopenharmony_ci	aops = svc_get_auth_ops(flavor);
748c2ecf20Sopenharmony_ci	if (aops == NULL) {
758c2ecf20Sopenharmony_ci		*authp = rpc_autherr_badcred;
768c2ecf20Sopenharmony_ci		return SVC_DENIED;
778c2ecf20Sopenharmony_ci	}
788c2ecf20Sopenharmony_ci
798c2ecf20Sopenharmony_ci	rqstp->rq_auth_slack = 0;
808c2ecf20Sopenharmony_ci	init_svc_cred(&rqstp->rq_cred);
818c2ecf20Sopenharmony_ci
828c2ecf20Sopenharmony_ci	rqstp->rq_authop = aops;
838c2ecf20Sopenharmony_ci	return aops->accept(rqstp, authp);
848c2ecf20Sopenharmony_ci}
858c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(svc_authenticate);
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_ciint svc_set_client(struct svc_rqst *rqstp)
888c2ecf20Sopenharmony_ci{
898c2ecf20Sopenharmony_ci	rqstp->rq_client = NULL;
908c2ecf20Sopenharmony_ci	return rqstp->rq_authop->set_client(rqstp);
918c2ecf20Sopenharmony_ci}
928c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(svc_set_client);
938c2ecf20Sopenharmony_ci
948c2ecf20Sopenharmony_ci/* A request, which was authenticated, has now executed.
958c2ecf20Sopenharmony_ci * Time to finalise the credentials and verifier
968c2ecf20Sopenharmony_ci * and release and resources
978c2ecf20Sopenharmony_ci */
988c2ecf20Sopenharmony_ciint svc_authorise(struct svc_rqst *rqstp)
998c2ecf20Sopenharmony_ci{
1008c2ecf20Sopenharmony_ci	struct auth_ops *aops = rqstp->rq_authop;
1018c2ecf20Sopenharmony_ci	int rv = 0;
1028c2ecf20Sopenharmony_ci
1038c2ecf20Sopenharmony_ci	rqstp->rq_authop = NULL;
1048c2ecf20Sopenharmony_ci
1058c2ecf20Sopenharmony_ci	if (aops) {
1068c2ecf20Sopenharmony_ci		rv = aops->release(rqstp);
1078c2ecf20Sopenharmony_ci		svc_put_auth_ops(aops);
1088c2ecf20Sopenharmony_ci	}
1098c2ecf20Sopenharmony_ci	return rv;
1108c2ecf20Sopenharmony_ci}
1118c2ecf20Sopenharmony_ci
1128c2ecf20Sopenharmony_ciint
1138c2ecf20Sopenharmony_cisvc_auth_register(rpc_authflavor_t flavor, struct auth_ops *aops)
1148c2ecf20Sopenharmony_ci{
1158c2ecf20Sopenharmony_ci	struct auth_ops *old;
1168c2ecf20Sopenharmony_ci	int rv = -EINVAL;
1178c2ecf20Sopenharmony_ci
1188c2ecf20Sopenharmony_ci	if (flavor < RPC_AUTH_MAXFLAVOR) {
1198c2ecf20Sopenharmony_ci		old = cmpxchg((struct auth_ops ** __force)&authtab[flavor], NULL, aops);
1208c2ecf20Sopenharmony_ci		if (old == NULL || old == aops)
1218c2ecf20Sopenharmony_ci			rv = 0;
1228c2ecf20Sopenharmony_ci	}
1238c2ecf20Sopenharmony_ci	return rv;
1248c2ecf20Sopenharmony_ci}
1258c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(svc_auth_register);
1268c2ecf20Sopenharmony_ci
1278c2ecf20Sopenharmony_civoid
1288c2ecf20Sopenharmony_cisvc_auth_unregister(rpc_authflavor_t flavor)
1298c2ecf20Sopenharmony_ci{
1308c2ecf20Sopenharmony_ci	if (flavor < RPC_AUTH_MAXFLAVOR)
1318c2ecf20Sopenharmony_ci		rcu_assign_pointer(authtab[flavor], NULL);
1328c2ecf20Sopenharmony_ci}
1338c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(svc_auth_unregister);
1348c2ecf20Sopenharmony_ci
1358c2ecf20Sopenharmony_ci/**************************************************
1368c2ecf20Sopenharmony_ci * 'auth_domains' are stored in a hash table indexed by name.
1378c2ecf20Sopenharmony_ci * When the last reference to an 'auth_domain' is dropped,
1388c2ecf20Sopenharmony_ci * the object is unhashed and freed.
1398c2ecf20Sopenharmony_ci * If auth_domain_lookup fails to find an entry, it will return
1408c2ecf20Sopenharmony_ci * it's second argument 'new'.  If this is non-null, it will
1418c2ecf20Sopenharmony_ci * have been atomically linked into the table.
1428c2ecf20Sopenharmony_ci */
1438c2ecf20Sopenharmony_ci
1448c2ecf20Sopenharmony_ci#define	DN_HASHBITS	6
1458c2ecf20Sopenharmony_ci#define	DN_HASHMAX	(1<<DN_HASHBITS)
1468c2ecf20Sopenharmony_ci
1478c2ecf20Sopenharmony_cistatic struct hlist_head	auth_domain_table[DN_HASHMAX];
1488c2ecf20Sopenharmony_cistatic DEFINE_SPINLOCK(auth_domain_lock);
1498c2ecf20Sopenharmony_ci
1508c2ecf20Sopenharmony_cistatic void auth_domain_release(struct kref *kref)
1518c2ecf20Sopenharmony_ci	__releases(&auth_domain_lock)
1528c2ecf20Sopenharmony_ci{
1538c2ecf20Sopenharmony_ci	struct auth_domain *dom = container_of(kref, struct auth_domain, ref);
1548c2ecf20Sopenharmony_ci
1558c2ecf20Sopenharmony_ci	hlist_del_rcu(&dom->hash);
1568c2ecf20Sopenharmony_ci	dom->flavour->domain_release(dom);
1578c2ecf20Sopenharmony_ci	spin_unlock(&auth_domain_lock);
1588c2ecf20Sopenharmony_ci}
1598c2ecf20Sopenharmony_ci
1608c2ecf20Sopenharmony_civoid auth_domain_put(struct auth_domain *dom)
1618c2ecf20Sopenharmony_ci{
1628c2ecf20Sopenharmony_ci	kref_put_lock(&dom->ref, auth_domain_release, &auth_domain_lock);
1638c2ecf20Sopenharmony_ci}
1648c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(auth_domain_put);
1658c2ecf20Sopenharmony_ci
1668c2ecf20Sopenharmony_cistruct auth_domain *
1678c2ecf20Sopenharmony_ciauth_domain_lookup(char *name, struct auth_domain *new)
1688c2ecf20Sopenharmony_ci{
1698c2ecf20Sopenharmony_ci	struct auth_domain *hp;
1708c2ecf20Sopenharmony_ci	struct hlist_head *head;
1718c2ecf20Sopenharmony_ci
1728c2ecf20Sopenharmony_ci	head = &auth_domain_table[hash_str(name, DN_HASHBITS)];
1738c2ecf20Sopenharmony_ci
1748c2ecf20Sopenharmony_ci	spin_lock(&auth_domain_lock);
1758c2ecf20Sopenharmony_ci
1768c2ecf20Sopenharmony_ci	hlist_for_each_entry(hp, head, hash) {
1778c2ecf20Sopenharmony_ci		if (strcmp(hp->name, name)==0) {
1788c2ecf20Sopenharmony_ci			kref_get(&hp->ref);
1798c2ecf20Sopenharmony_ci			spin_unlock(&auth_domain_lock);
1808c2ecf20Sopenharmony_ci			return hp;
1818c2ecf20Sopenharmony_ci		}
1828c2ecf20Sopenharmony_ci	}
1838c2ecf20Sopenharmony_ci	if (new)
1848c2ecf20Sopenharmony_ci		hlist_add_head_rcu(&new->hash, head);
1858c2ecf20Sopenharmony_ci	spin_unlock(&auth_domain_lock);
1868c2ecf20Sopenharmony_ci	return new;
1878c2ecf20Sopenharmony_ci}
1888c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(auth_domain_lookup);
1898c2ecf20Sopenharmony_ci
1908c2ecf20Sopenharmony_cistruct auth_domain *auth_domain_find(char *name)
1918c2ecf20Sopenharmony_ci{
1928c2ecf20Sopenharmony_ci	struct auth_domain *hp;
1938c2ecf20Sopenharmony_ci	struct hlist_head *head;
1948c2ecf20Sopenharmony_ci
1958c2ecf20Sopenharmony_ci	head = &auth_domain_table[hash_str(name, DN_HASHBITS)];
1968c2ecf20Sopenharmony_ci
1978c2ecf20Sopenharmony_ci	rcu_read_lock();
1988c2ecf20Sopenharmony_ci	hlist_for_each_entry_rcu(hp, head, hash) {
1998c2ecf20Sopenharmony_ci		if (strcmp(hp->name, name)==0) {
2008c2ecf20Sopenharmony_ci			if (!kref_get_unless_zero(&hp->ref))
2018c2ecf20Sopenharmony_ci				hp = NULL;
2028c2ecf20Sopenharmony_ci			rcu_read_unlock();
2038c2ecf20Sopenharmony_ci			return hp;
2048c2ecf20Sopenharmony_ci		}
2058c2ecf20Sopenharmony_ci	}
2068c2ecf20Sopenharmony_ci	rcu_read_unlock();
2078c2ecf20Sopenharmony_ci	return NULL;
2088c2ecf20Sopenharmony_ci}
2098c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(auth_domain_find);
2108c2ecf20Sopenharmony_ci
2118c2ecf20Sopenharmony_ci/**
2128c2ecf20Sopenharmony_ci * auth_domain_cleanup - check that the auth_domain table is empty
2138c2ecf20Sopenharmony_ci *
2148c2ecf20Sopenharmony_ci * On module unload the auth_domain_table must be empty.  To make it
2158c2ecf20Sopenharmony_ci * easier to catch bugs which don't clean up domains properly, we
2168c2ecf20Sopenharmony_ci * warn if anything remains in the table at cleanup time.
2178c2ecf20Sopenharmony_ci *
2188c2ecf20Sopenharmony_ci * Note that we cannot proactively remove the domains at this stage.
2198c2ecf20Sopenharmony_ci * The ->release() function might be in a module that has already been
2208c2ecf20Sopenharmony_ci * unloaded.
2218c2ecf20Sopenharmony_ci */
2228c2ecf20Sopenharmony_ci
2238c2ecf20Sopenharmony_civoid auth_domain_cleanup(void)
2248c2ecf20Sopenharmony_ci{
2258c2ecf20Sopenharmony_ci	int h;
2268c2ecf20Sopenharmony_ci	struct auth_domain *hp;
2278c2ecf20Sopenharmony_ci
2288c2ecf20Sopenharmony_ci	for (h = 0; h < DN_HASHMAX; h++)
2298c2ecf20Sopenharmony_ci		hlist_for_each_entry(hp, &auth_domain_table[h], hash)
2308c2ecf20Sopenharmony_ci			pr_warn("svc: domain %s still present at module unload.\n",
2318c2ecf20Sopenharmony_ci				hp->name);
2328c2ecf20Sopenharmony_ci}
233