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