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