162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * linux/net/sunrpc/svcauth.c
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * The generic interface for RPC authentication on the server side.
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci * Copyright (C) 1995, 1996 Olaf Kirch <okir@monad.swb.de>
862306a36Sopenharmony_ci *
962306a36Sopenharmony_ci * CHANGES
1062306a36Sopenharmony_ci * 19-Apr-2000 Chris Evans      - Security fix
1162306a36Sopenharmony_ci */
1262306a36Sopenharmony_ci
1362306a36Sopenharmony_ci#include <linux/types.h>
1462306a36Sopenharmony_ci#include <linux/module.h>
1562306a36Sopenharmony_ci#include <linux/sunrpc/types.h>
1662306a36Sopenharmony_ci#include <linux/sunrpc/xdr.h>
1762306a36Sopenharmony_ci#include <linux/sunrpc/svcsock.h>
1862306a36Sopenharmony_ci#include <linux/sunrpc/svcauth.h>
1962306a36Sopenharmony_ci#include <linux/err.h>
2062306a36Sopenharmony_ci#include <linux/hash.h>
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_ci#include <trace/events/sunrpc.h>
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_ci#include "sunrpc.h"
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_ci#define RPCDBG_FACILITY	RPCDBG_AUTH
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_ci/*
3062306a36Sopenharmony_ci * Table of authenticators
3162306a36Sopenharmony_ci */
3262306a36Sopenharmony_ciextern struct auth_ops svcauth_null;
3362306a36Sopenharmony_ciextern struct auth_ops svcauth_unix;
3462306a36Sopenharmony_ciextern struct auth_ops svcauth_tls;
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_cistatic struct auth_ops __rcu *authtab[RPC_AUTH_MAXFLAVOR] = {
3762306a36Sopenharmony_ci	[RPC_AUTH_NULL] = (struct auth_ops __force __rcu *)&svcauth_null,
3862306a36Sopenharmony_ci	[RPC_AUTH_UNIX] = (struct auth_ops __force __rcu *)&svcauth_unix,
3962306a36Sopenharmony_ci	[RPC_AUTH_TLS]  = (struct auth_ops __force __rcu *)&svcauth_tls,
4062306a36Sopenharmony_ci};
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_cistatic struct auth_ops *
4362306a36Sopenharmony_cisvc_get_auth_ops(rpc_authflavor_t flavor)
4462306a36Sopenharmony_ci{
4562306a36Sopenharmony_ci	struct auth_ops		*aops;
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_ci	if (flavor >= RPC_AUTH_MAXFLAVOR)
4862306a36Sopenharmony_ci		return NULL;
4962306a36Sopenharmony_ci	rcu_read_lock();
5062306a36Sopenharmony_ci	aops = rcu_dereference(authtab[flavor]);
5162306a36Sopenharmony_ci	if (aops != NULL && !try_module_get(aops->owner))
5262306a36Sopenharmony_ci		aops = NULL;
5362306a36Sopenharmony_ci	rcu_read_unlock();
5462306a36Sopenharmony_ci	return aops;
5562306a36Sopenharmony_ci}
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_cistatic void
5862306a36Sopenharmony_cisvc_put_auth_ops(struct auth_ops *aops)
5962306a36Sopenharmony_ci{
6062306a36Sopenharmony_ci	module_put(aops->owner);
6162306a36Sopenharmony_ci}
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_ci/**
6462306a36Sopenharmony_ci * svc_authenticate - Initialize an outgoing credential
6562306a36Sopenharmony_ci * @rqstp: RPC execution context
6662306a36Sopenharmony_ci *
6762306a36Sopenharmony_ci * Return values:
6862306a36Sopenharmony_ci *   %SVC_OK: XDR encoding of the result can begin
6962306a36Sopenharmony_ci *   %SVC_DENIED: Credential or verifier is not valid
7062306a36Sopenharmony_ci *   %SVC_GARBAGE: Failed to decode credential or verifier
7162306a36Sopenharmony_ci *   %SVC_COMPLETE: GSS context lifetime event; no further action
7262306a36Sopenharmony_ci *   %SVC_DROP: Drop this request; no further action
7362306a36Sopenharmony_ci *   %SVC_CLOSE: Like drop, but also close transport connection
7462306a36Sopenharmony_ci */
7562306a36Sopenharmony_cienum svc_auth_status svc_authenticate(struct svc_rqst *rqstp)
7662306a36Sopenharmony_ci{
7762306a36Sopenharmony_ci	struct auth_ops *aops;
7862306a36Sopenharmony_ci	u32 flavor;
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_ci	rqstp->rq_auth_stat = rpc_auth_ok;
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_ci	/*
8362306a36Sopenharmony_ci	 * Decode the Call credential's flavor field. The credential's
8462306a36Sopenharmony_ci	 * body field is decoded in the chosen ->accept method below.
8562306a36Sopenharmony_ci	 */
8662306a36Sopenharmony_ci	if (xdr_stream_decode_u32(&rqstp->rq_arg_stream, &flavor) < 0)
8762306a36Sopenharmony_ci		return SVC_GARBAGE;
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_ci	aops = svc_get_auth_ops(flavor);
9062306a36Sopenharmony_ci	if (aops == NULL) {
9162306a36Sopenharmony_ci		rqstp->rq_auth_stat = rpc_autherr_badcred;
9262306a36Sopenharmony_ci		return SVC_DENIED;
9362306a36Sopenharmony_ci	}
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_ci	rqstp->rq_auth_slack = 0;
9662306a36Sopenharmony_ci	init_svc_cred(&rqstp->rq_cred);
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_ci	rqstp->rq_authop = aops;
9962306a36Sopenharmony_ci	return aops->accept(rqstp);
10062306a36Sopenharmony_ci}
10162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(svc_authenticate);
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_ci/**
10462306a36Sopenharmony_ci * svc_set_client - Assign an appropriate 'auth_domain' as the client
10562306a36Sopenharmony_ci * @rqstp: RPC execution context
10662306a36Sopenharmony_ci *
10762306a36Sopenharmony_ci * Return values:
10862306a36Sopenharmony_ci *   %SVC_OK: Client was found and assigned
10962306a36Sopenharmony_ci *   %SVC_DENY: Client was explicitly denied
11062306a36Sopenharmony_ci *   %SVC_DROP: Ignore this request
11162306a36Sopenharmony_ci *   %SVC_CLOSE: Ignore this request and close the connection
11262306a36Sopenharmony_ci */
11362306a36Sopenharmony_cienum svc_auth_status svc_set_client(struct svc_rqst *rqstp)
11462306a36Sopenharmony_ci{
11562306a36Sopenharmony_ci	rqstp->rq_client = NULL;
11662306a36Sopenharmony_ci	return rqstp->rq_authop->set_client(rqstp);
11762306a36Sopenharmony_ci}
11862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(svc_set_client);
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_ci/**
12162306a36Sopenharmony_ci * svc_authorise - Finalize credentials/verifier and release resources
12262306a36Sopenharmony_ci * @rqstp: RPC execution context
12362306a36Sopenharmony_ci *
12462306a36Sopenharmony_ci * Returns zero on success, or a negative errno.
12562306a36Sopenharmony_ci */
12662306a36Sopenharmony_ciint svc_authorise(struct svc_rqst *rqstp)
12762306a36Sopenharmony_ci{
12862306a36Sopenharmony_ci	struct auth_ops *aops = rqstp->rq_authop;
12962306a36Sopenharmony_ci	int rv = 0;
13062306a36Sopenharmony_ci
13162306a36Sopenharmony_ci	rqstp->rq_authop = NULL;
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_ci	if (aops) {
13462306a36Sopenharmony_ci		rv = aops->release(rqstp);
13562306a36Sopenharmony_ci		svc_put_auth_ops(aops);
13662306a36Sopenharmony_ci	}
13762306a36Sopenharmony_ci	return rv;
13862306a36Sopenharmony_ci}
13962306a36Sopenharmony_ci
14062306a36Sopenharmony_ciint
14162306a36Sopenharmony_cisvc_auth_register(rpc_authflavor_t flavor, struct auth_ops *aops)
14262306a36Sopenharmony_ci{
14362306a36Sopenharmony_ci	struct auth_ops *old;
14462306a36Sopenharmony_ci	int rv = -EINVAL;
14562306a36Sopenharmony_ci
14662306a36Sopenharmony_ci	if (flavor < RPC_AUTH_MAXFLAVOR) {
14762306a36Sopenharmony_ci		old = cmpxchg((struct auth_ops ** __force)&authtab[flavor], NULL, aops);
14862306a36Sopenharmony_ci		if (old == NULL || old == aops)
14962306a36Sopenharmony_ci			rv = 0;
15062306a36Sopenharmony_ci	}
15162306a36Sopenharmony_ci	return rv;
15262306a36Sopenharmony_ci}
15362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(svc_auth_register);
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_civoid
15662306a36Sopenharmony_cisvc_auth_unregister(rpc_authflavor_t flavor)
15762306a36Sopenharmony_ci{
15862306a36Sopenharmony_ci	if (flavor < RPC_AUTH_MAXFLAVOR)
15962306a36Sopenharmony_ci		rcu_assign_pointer(authtab[flavor], NULL);
16062306a36Sopenharmony_ci}
16162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(svc_auth_unregister);
16262306a36Sopenharmony_ci
16362306a36Sopenharmony_ci/**************************************************
16462306a36Sopenharmony_ci * 'auth_domains' are stored in a hash table indexed by name.
16562306a36Sopenharmony_ci * When the last reference to an 'auth_domain' is dropped,
16662306a36Sopenharmony_ci * the object is unhashed and freed.
16762306a36Sopenharmony_ci * If auth_domain_lookup fails to find an entry, it will return
16862306a36Sopenharmony_ci * it's second argument 'new'.  If this is non-null, it will
16962306a36Sopenharmony_ci * have been atomically linked into the table.
17062306a36Sopenharmony_ci */
17162306a36Sopenharmony_ci
17262306a36Sopenharmony_ci#define	DN_HASHBITS	6
17362306a36Sopenharmony_ci#define	DN_HASHMAX	(1<<DN_HASHBITS)
17462306a36Sopenharmony_ci
17562306a36Sopenharmony_cistatic struct hlist_head	auth_domain_table[DN_HASHMAX];
17662306a36Sopenharmony_cistatic DEFINE_SPINLOCK(auth_domain_lock);
17762306a36Sopenharmony_ci
17862306a36Sopenharmony_cistatic void auth_domain_release(struct kref *kref)
17962306a36Sopenharmony_ci	__releases(&auth_domain_lock)
18062306a36Sopenharmony_ci{
18162306a36Sopenharmony_ci	struct auth_domain *dom = container_of(kref, struct auth_domain, ref);
18262306a36Sopenharmony_ci
18362306a36Sopenharmony_ci	hlist_del_rcu(&dom->hash);
18462306a36Sopenharmony_ci	dom->flavour->domain_release(dom);
18562306a36Sopenharmony_ci	spin_unlock(&auth_domain_lock);
18662306a36Sopenharmony_ci}
18762306a36Sopenharmony_ci
18862306a36Sopenharmony_civoid auth_domain_put(struct auth_domain *dom)
18962306a36Sopenharmony_ci{
19062306a36Sopenharmony_ci	kref_put_lock(&dom->ref, auth_domain_release, &auth_domain_lock);
19162306a36Sopenharmony_ci}
19262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(auth_domain_put);
19362306a36Sopenharmony_ci
19462306a36Sopenharmony_cistruct auth_domain *
19562306a36Sopenharmony_ciauth_domain_lookup(char *name, struct auth_domain *new)
19662306a36Sopenharmony_ci{
19762306a36Sopenharmony_ci	struct auth_domain *hp;
19862306a36Sopenharmony_ci	struct hlist_head *head;
19962306a36Sopenharmony_ci
20062306a36Sopenharmony_ci	head = &auth_domain_table[hash_str(name, DN_HASHBITS)];
20162306a36Sopenharmony_ci
20262306a36Sopenharmony_ci	spin_lock(&auth_domain_lock);
20362306a36Sopenharmony_ci
20462306a36Sopenharmony_ci	hlist_for_each_entry(hp, head, hash) {
20562306a36Sopenharmony_ci		if (strcmp(hp->name, name)==0) {
20662306a36Sopenharmony_ci			kref_get(&hp->ref);
20762306a36Sopenharmony_ci			spin_unlock(&auth_domain_lock);
20862306a36Sopenharmony_ci			return hp;
20962306a36Sopenharmony_ci		}
21062306a36Sopenharmony_ci	}
21162306a36Sopenharmony_ci	if (new)
21262306a36Sopenharmony_ci		hlist_add_head_rcu(&new->hash, head);
21362306a36Sopenharmony_ci	spin_unlock(&auth_domain_lock);
21462306a36Sopenharmony_ci	return new;
21562306a36Sopenharmony_ci}
21662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(auth_domain_lookup);
21762306a36Sopenharmony_ci
21862306a36Sopenharmony_cistruct auth_domain *auth_domain_find(char *name)
21962306a36Sopenharmony_ci{
22062306a36Sopenharmony_ci	struct auth_domain *hp;
22162306a36Sopenharmony_ci	struct hlist_head *head;
22262306a36Sopenharmony_ci
22362306a36Sopenharmony_ci	head = &auth_domain_table[hash_str(name, DN_HASHBITS)];
22462306a36Sopenharmony_ci
22562306a36Sopenharmony_ci	rcu_read_lock();
22662306a36Sopenharmony_ci	hlist_for_each_entry_rcu(hp, head, hash) {
22762306a36Sopenharmony_ci		if (strcmp(hp->name, name)==0) {
22862306a36Sopenharmony_ci			if (!kref_get_unless_zero(&hp->ref))
22962306a36Sopenharmony_ci				hp = NULL;
23062306a36Sopenharmony_ci			rcu_read_unlock();
23162306a36Sopenharmony_ci			return hp;
23262306a36Sopenharmony_ci		}
23362306a36Sopenharmony_ci	}
23462306a36Sopenharmony_ci	rcu_read_unlock();
23562306a36Sopenharmony_ci	return NULL;
23662306a36Sopenharmony_ci}
23762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(auth_domain_find);
23862306a36Sopenharmony_ci
23962306a36Sopenharmony_ci/**
24062306a36Sopenharmony_ci * auth_domain_cleanup - check that the auth_domain table is empty
24162306a36Sopenharmony_ci *
24262306a36Sopenharmony_ci * On module unload the auth_domain_table must be empty.  To make it
24362306a36Sopenharmony_ci * easier to catch bugs which don't clean up domains properly, we
24462306a36Sopenharmony_ci * warn if anything remains in the table at cleanup time.
24562306a36Sopenharmony_ci *
24662306a36Sopenharmony_ci * Note that we cannot proactively remove the domains at this stage.
24762306a36Sopenharmony_ci * The ->release() function might be in a module that has already been
24862306a36Sopenharmony_ci * unloaded.
24962306a36Sopenharmony_ci */
25062306a36Sopenharmony_ci
25162306a36Sopenharmony_civoid auth_domain_cleanup(void)
25262306a36Sopenharmony_ci{
25362306a36Sopenharmony_ci	int h;
25462306a36Sopenharmony_ci	struct auth_domain *hp;
25562306a36Sopenharmony_ci
25662306a36Sopenharmony_ci	for (h = 0; h < DN_HASHMAX; h++)
25762306a36Sopenharmony_ci		hlist_for_each_entry(hp, &auth_domain_table[h], hash)
25862306a36Sopenharmony_ci			pr_warn("svc: domain %s still present at module unload.\n",
25962306a36Sopenharmony_ci				hp->name);
26062306a36Sopenharmony_ci}
261