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