Branch data Line data Source code
1 : : // SPDX-License-Identifier: BSD-3-Clause
2 : : /*
3 : : * linux/net/sunrpc/gss_mech_switch.c
4 : : *
5 : : * Copyright (c) 2001 The Regents of the University of Michigan.
6 : : * All rights reserved.
7 : : *
8 : : * J. Bruce Fields <bfields@umich.edu>
9 : : */
10 : :
11 : : #include <linux/types.h>
12 : : #include <linux/slab.h>
13 : : #include <linux/module.h>
14 : : #include <linux/oid_registry.h>
15 : : #include <linux/sunrpc/msg_prot.h>
16 : : #include <linux/sunrpc/gss_asn1.h>
17 : : #include <linux/sunrpc/auth_gss.h>
18 : : #include <linux/sunrpc/svcauth_gss.h>
19 : : #include <linux/sunrpc/gss_err.h>
20 : : #include <linux/sunrpc/sched.h>
21 : : #include <linux/sunrpc/gss_api.h>
22 : : #include <linux/sunrpc/clnt.h>
23 : : #include <trace/events/rpcgss.h>
24 : :
25 : : #if IS_ENABLED(CONFIG_SUNRPC_DEBUG)
26 : : # define RPCDBG_FACILITY RPCDBG_AUTH
27 : : #endif
28 : :
29 : : static LIST_HEAD(registered_mechs);
30 : : static DEFINE_SPINLOCK(registered_mechs_lock);
31 : :
32 : : static void
33 : 0 : gss_mech_free(struct gss_api_mech *gm)
34 : : {
35 : : struct pf_desc *pf;
36 : : int i;
37 : :
38 [ # # # # ]: 0 : for (i = 0; i < gm->gm_pf_num; i++) {
39 : 0 : pf = &gm->gm_pfs[i];
40 : 0 : kfree(pf->auth_domain_name);
41 : 0 : pf->auth_domain_name = NULL;
42 : : }
43 : : }
44 : :
45 : : static inline char *
46 : 0 : make_auth_domain_name(char *name)
47 : : {
48 : 0 : static char *prefix = "gss/";
49 : 0 : char *new;
50 : :
51 [ # # ]: 0 : new = kmalloc(strlen(name) + strlen(prefix) + 1, GFP_KERNEL);
52 [ # # ]: 0 : if (new) {
53 : 0 : strcpy(new, prefix);
54 : 0 : strcat(new, name);
55 : : }
56 : 0 : return new;
57 : : }
58 : :
59 : : static int
60 : 0 : gss_mech_svc_setup(struct gss_api_mech *gm)
61 : : {
62 : 0 : struct pf_desc *pf;
63 : 0 : int i, status;
64 : :
65 [ # # ]: 0 : for (i = 0; i < gm->gm_pf_num; i++) {
66 : 0 : pf = &gm->gm_pfs[i];
67 : 0 : pf->auth_domain_name = make_auth_domain_name(pf->name);
68 : 0 : status = -ENOMEM;
69 [ # # ]: 0 : if (pf->auth_domain_name == NULL)
70 : 0 : goto out;
71 : 0 : status = svcauth_gss_register_pseudoflavor(pf->pseudoflavor,
72 : : pf->auth_domain_name);
73 [ # # ]: 0 : if (status)
74 : 0 : goto out;
75 : : }
76 : : return 0;
77 : 0 : out:
78 : 0 : gss_mech_free(gm);
79 : : return status;
80 : : }
81 : :
82 : : /**
83 : : * gss_mech_register - register a GSS mechanism
84 : : * @gm: GSS mechanism handle
85 : : *
86 : : * Returns zero if successful, or a negative errno.
87 : : */
88 : 0 : int gss_mech_register(struct gss_api_mech *gm)
89 : : {
90 : 0 : int status;
91 : :
92 : 0 : status = gss_mech_svc_setup(gm);
93 [ # # ]: 0 : if (status)
94 : : return status;
95 : 0 : spin_lock(®istered_mechs_lock);
96 : 0 : list_add_rcu(&gm->gm_list, ®istered_mechs);
97 : 0 : spin_unlock(®istered_mechs_lock);
98 : 0 : dprintk("RPC: registered gss mechanism %s\n", gm->gm_name);
99 : 0 : return 0;
100 : : }
101 : : EXPORT_SYMBOL_GPL(gss_mech_register);
102 : :
103 : : /**
104 : : * gss_mech_unregister - release a GSS mechanism
105 : : * @gm: GSS mechanism handle
106 : : *
107 : : */
108 : 0 : void gss_mech_unregister(struct gss_api_mech *gm)
109 : : {
110 : 0 : spin_lock(®istered_mechs_lock);
111 : 0 : list_del_rcu(&gm->gm_list);
112 : 0 : spin_unlock(®istered_mechs_lock);
113 : 0 : dprintk("RPC: unregistered gss mechanism %s\n", gm->gm_name);
114 : 0 : gss_mech_free(gm);
115 : 0 : }
116 : : EXPORT_SYMBOL_GPL(gss_mech_unregister);
117 : :
118 : 0 : struct gss_api_mech *gss_mech_get(struct gss_api_mech *gm)
119 : : {
120 : 0 : __module_get(gm->gm_owner);
121 : 0 : return gm;
122 : : }
123 : : EXPORT_SYMBOL(gss_mech_get);
124 : :
125 : : static struct gss_api_mech *
126 : 0 : _gss_mech_get_by_name(const char *name)
127 : : {
128 : 0 : struct gss_api_mech *pos, *gm = NULL;
129 : :
130 : 0 : rcu_read_lock();
131 [ # # ]: 0 : list_for_each_entry_rcu(pos, ®istered_mechs, gm_list) {
132 [ # # ]: 0 : if (0 == strcmp(name, pos->gm_name)) {
133 [ # # ]: 0 : if (try_module_get(pos->gm_owner))
134 : 0 : gm = pos;
135 : : break;
136 : : }
137 : : }
138 : 0 : rcu_read_unlock();
139 : 0 : return gm;
140 : :
141 : : }
142 : :
143 : 0 : struct gss_api_mech * gss_mech_get_by_name(const char *name)
144 : : {
145 : 0 : struct gss_api_mech *gm = NULL;
146 : :
147 : 0 : gm = _gss_mech_get_by_name(name);
148 [ # # ]: 0 : if (!gm) {
149 : 0 : request_module("rpc-auth-gss-%s", name);
150 : 0 : gm = _gss_mech_get_by_name(name);
151 : : }
152 : 0 : return gm;
153 : : }
154 : :
155 : 0 : struct gss_api_mech *gss_mech_get_by_OID(struct rpcsec_gss_oid *obj)
156 : : {
157 : 0 : struct gss_api_mech *pos, *gm = NULL;
158 : 0 : char buf[32];
159 : :
160 [ # # ]: 0 : if (sprint_oid(obj->data, obj->len, buf, sizeof(buf)) < 0)
161 : : return NULL;
162 : 0 : request_module("rpc-auth-gss-%s", buf);
163 : :
164 : 0 : rcu_read_lock();
165 [ # # ]: 0 : list_for_each_entry_rcu(pos, ®istered_mechs, gm_list) {
166 [ # # ]: 0 : if (obj->len == pos->gm_oid.len) {
167 [ # # ]: 0 : if (0 == memcmp(obj->data, pos->gm_oid.data, obj->len)) {
168 [ # # ]: 0 : if (try_module_get(pos->gm_owner))
169 : : gm = pos;
170 : : break;
171 : : }
172 : : }
173 : : }
174 : 0 : rcu_read_unlock();
175 [ # # ]: 0 : if (!gm)
176 : 0 : trace_rpcgss_oid_to_mech(buf);
177 : : return gm;
178 : : }
179 : :
180 : : static inline int
181 : 0 : mech_supports_pseudoflavor(struct gss_api_mech *gm, u32 pseudoflavor)
182 : : {
183 : : int i;
184 : :
185 [ # # ]: 0 : for (i = 0; i < gm->gm_pf_num; i++) {
186 [ # # ]: 0 : if (gm->gm_pfs[i].pseudoflavor == pseudoflavor)
187 : : return 1;
188 : : }
189 : : return 0;
190 : : }
191 : :
192 : 0 : static struct gss_api_mech *_gss_mech_get_by_pseudoflavor(u32 pseudoflavor)
193 : : {
194 : 0 : struct gss_api_mech *gm = NULL, *pos;
195 : :
196 : 0 : rcu_read_lock();
197 [ # # ]: 0 : list_for_each_entry_rcu(pos, ®istered_mechs, gm_list) {
198 [ # # ]: 0 : if (!mech_supports_pseudoflavor(pos, pseudoflavor))
199 : 0 : continue;
200 [ # # ]: 0 : if (try_module_get(pos->gm_owner))
201 : 0 : gm = pos;
202 : : break;
203 : : }
204 : 0 : rcu_read_unlock();
205 : 0 : return gm;
206 : : }
207 : :
208 : : struct gss_api_mech *
209 : 0 : gss_mech_get_by_pseudoflavor(u32 pseudoflavor)
210 : : {
211 : 0 : struct gss_api_mech *gm;
212 : :
213 : 0 : gm = _gss_mech_get_by_pseudoflavor(pseudoflavor);
214 : :
215 [ # # ]: 0 : if (!gm) {
216 : 0 : request_module("rpc-auth-gss-%u", pseudoflavor);
217 : 0 : gm = _gss_mech_get_by_pseudoflavor(pseudoflavor);
218 : : }
219 : 0 : return gm;
220 : : }
221 : :
222 : : /**
223 : : * gss_svc_to_pseudoflavor - map a GSS service number to a pseudoflavor
224 : : * @gm: GSS mechanism handle
225 : : * @qop: GSS quality-of-protection value
226 : : * @service: GSS service value
227 : : *
228 : : * Returns a matching security flavor, or RPC_AUTH_MAXFLAVOR if none is found.
229 : : */
230 : 0 : rpc_authflavor_t gss_svc_to_pseudoflavor(struct gss_api_mech *gm, u32 qop,
231 : : u32 service)
232 : : {
233 : 0 : int i;
234 : :
235 [ # # # # ]: 0 : for (i = 0; i < gm->gm_pf_num; i++) {
236 [ # # # # ]: 0 : if (gm->gm_pfs[i].qop == qop &&
237 [ # # # # ]: 0 : gm->gm_pfs[i].service == service) {
238 : 0 : return gm->gm_pfs[i].pseudoflavor;
239 : : }
240 : : }
241 : : return RPC_AUTH_MAXFLAVOR;
242 : : }
243 : :
244 : : /**
245 : : * gss_mech_info2flavor - look up a pseudoflavor given a GSS tuple
246 : : * @info: a GSS mech OID, quality of protection, and service value
247 : : *
248 : : * Returns a matching pseudoflavor, or RPC_AUTH_MAXFLAVOR if the tuple is
249 : : * not supported.
250 : : */
251 : 0 : rpc_authflavor_t gss_mech_info2flavor(struct rpcsec_gss_info *info)
252 : : {
253 : 0 : rpc_authflavor_t pseudoflavor;
254 : 0 : struct gss_api_mech *gm;
255 : :
256 : 0 : gm = gss_mech_get_by_OID(&info->oid);
257 [ # # ]: 0 : if (gm == NULL)
258 : : return RPC_AUTH_MAXFLAVOR;
259 : :
260 : 0 : pseudoflavor = gss_svc_to_pseudoflavor(gm, info->qop, info->service);
261 : :
262 : 0 : gss_mech_put(gm);
263 : 0 : return pseudoflavor;
264 : : }
265 : :
266 : : /**
267 : : * gss_mech_flavor2info - look up a GSS tuple for a given pseudoflavor
268 : : * @pseudoflavor: GSS pseudoflavor to match
269 : : * @info: rpcsec_gss_info structure to fill in
270 : : *
271 : : * Returns zero and fills in "info" if pseudoflavor matches a
272 : : * supported mechanism. Otherwise a negative errno is returned.
273 : : */
274 : 0 : int gss_mech_flavor2info(rpc_authflavor_t pseudoflavor,
275 : : struct rpcsec_gss_info *info)
276 : : {
277 : 0 : struct gss_api_mech *gm;
278 : 0 : int i;
279 : :
280 : 0 : gm = gss_mech_get_by_pseudoflavor(pseudoflavor);
281 [ # # ]: 0 : if (gm == NULL)
282 : : return -ENOENT;
283 : :
284 [ # # ]: 0 : for (i = 0; i < gm->gm_pf_num; i++) {
285 [ # # ]: 0 : if (gm->gm_pfs[i].pseudoflavor == pseudoflavor) {
286 : 0 : memcpy(info->oid.data, gm->gm_oid.data, gm->gm_oid.len);
287 : 0 : info->oid.len = gm->gm_oid.len;
288 : 0 : info->qop = gm->gm_pfs[i].qop;
289 : 0 : info->service = gm->gm_pfs[i].service;
290 : 0 : gss_mech_put(gm);
291 : 0 : return 0;
292 : : }
293 : : }
294 : :
295 : 0 : gss_mech_put(gm);
296 : 0 : return -ENOENT;
297 : : }
298 : :
299 : : u32
300 : 0 : gss_pseudoflavor_to_service(struct gss_api_mech *gm, u32 pseudoflavor)
301 : : {
302 : 0 : int i;
303 : :
304 [ # # ]: 0 : for (i = 0; i < gm->gm_pf_num; i++) {
305 [ # # ]: 0 : if (gm->gm_pfs[i].pseudoflavor == pseudoflavor)
306 : 0 : return gm->gm_pfs[i].service;
307 : : }
308 : : return 0;
309 : : }
310 : : EXPORT_SYMBOL(gss_pseudoflavor_to_service);
311 : :
312 : : bool
313 : 0 : gss_pseudoflavor_to_datatouch(struct gss_api_mech *gm, u32 pseudoflavor)
314 : : {
315 : 0 : int i;
316 : :
317 [ # # ]: 0 : for (i = 0; i < gm->gm_pf_num; i++) {
318 [ # # ]: 0 : if (gm->gm_pfs[i].pseudoflavor == pseudoflavor)
319 : 0 : return gm->gm_pfs[i].datatouch;
320 : : }
321 : : return false;
322 : : }
323 : :
324 : : char *
325 : 0 : gss_service_to_auth_domain_name(struct gss_api_mech *gm, u32 service)
326 : : {
327 : 0 : int i;
328 : :
329 [ # # ]: 0 : for (i = 0; i < gm->gm_pf_num; i++) {
330 [ # # ]: 0 : if (gm->gm_pfs[i].service == service)
331 : 0 : return gm->gm_pfs[i].auth_domain_name;
332 : : }
333 : : return NULL;
334 : : }
335 : :
336 : : void
337 : 0 : gss_mech_put(struct gss_api_mech * gm)
338 : : {
339 [ # # ]: 0 : if (gm)
340 : 0 : module_put(gm->gm_owner);
341 : 0 : }
342 : : EXPORT_SYMBOL(gss_mech_put);
343 : :
344 : : /* The mech could probably be determined from the token instead, but it's just
345 : : * as easy for now to pass it in. */
346 : : int
347 : 0 : gss_import_sec_context(const void *input_token, size_t bufsize,
348 : : struct gss_api_mech *mech,
349 : : struct gss_ctx **ctx_id,
350 : : time64_t *endtime,
351 : : gfp_t gfp_mask)
352 : : {
353 [ # # ]: 0 : if (!(*ctx_id = kzalloc(sizeof(**ctx_id), gfp_mask)))
354 : : return -ENOMEM;
355 : 0 : (*ctx_id)->mech_type = gss_mech_get(mech);
356 : :
357 : 0 : return mech->gm_ops->gss_import_sec_context(input_token, bufsize,
358 : : *ctx_id, endtime, gfp_mask);
359 : : }
360 : :
361 : : /* gss_get_mic: compute a mic over message and return mic_token. */
362 : :
363 : : u32
364 : 0 : gss_get_mic(struct gss_ctx *context_handle,
365 : : struct xdr_buf *message,
366 : : struct xdr_netobj *mic_token)
367 : : {
368 : 0 : return context_handle->mech_type->gm_ops
369 : 0 : ->gss_get_mic(context_handle,
370 : : message,
371 : : mic_token);
372 : : }
373 : :
374 : : /* gss_verify_mic: check whether the provided mic_token verifies message. */
375 : :
376 : : u32
377 : 0 : gss_verify_mic(struct gss_ctx *context_handle,
378 : : struct xdr_buf *message,
379 : : struct xdr_netobj *mic_token)
380 : : {
381 : 0 : return context_handle->mech_type->gm_ops
382 : 0 : ->gss_verify_mic(context_handle,
383 : : message,
384 : : mic_token);
385 : : }
386 : :
387 : : /*
388 : : * This function is called from both the client and server code.
389 : : * Each makes guarantees about how much "slack" space is available
390 : : * for the underlying function in "buf"'s head and tail while
391 : : * performing the wrap.
392 : : *
393 : : * The client and server code allocate RPC_MAX_AUTH_SIZE extra
394 : : * space in both the head and tail which is available for use by
395 : : * the wrap function.
396 : : *
397 : : * Underlying functions should verify they do not use more than
398 : : * RPC_MAX_AUTH_SIZE of extra space in either the head or tail
399 : : * when performing the wrap.
400 : : */
401 : : u32
402 : 0 : gss_wrap(struct gss_ctx *ctx_id,
403 : : int offset,
404 : : struct xdr_buf *buf,
405 : : struct page **inpages)
406 : : {
407 : 0 : return ctx_id->mech_type->gm_ops
408 : 0 : ->gss_wrap(ctx_id, offset, buf, inpages);
409 : : }
410 : :
411 : : u32
412 : 0 : gss_unwrap(struct gss_ctx *ctx_id,
413 : : int offset,
414 : : struct xdr_buf *buf)
415 : : {
416 : 0 : return ctx_id->mech_type->gm_ops
417 : 0 : ->gss_unwrap(ctx_id, offset, buf);
418 : : }
419 : :
420 : :
421 : : /* gss_delete_sec_context: free all resources associated with context_handle.
422 : : * Note this differs from the RFC 2744-specified prototype in that we don't
423 : : * bother returning an output token, since it would never be used anyway. */
424 : :
425 : : u32
426 : 0 : gss_delete_sec_context(struct gss_ctx **context_handle)
427 : : {
428 : 0 : dprintk("RPC: gss_delete_sec_context deleting %p\n",
429 : : *context_handle);
430 : :
431 [ # # ]: 0 : if (!*context_handle)
432 : : return GSS_S_NO_CONTEXT;
433 [ # # ]: 0 : if ((*context_handle)->internal_ctx_id)
434 : 0 : (*context_handle)->mech_type->gm_ops
435 : 0 : ->gss_delete_sec_context((*context_handle)
436 : : ->internal_ctx_id);
437 [ # # ]: 0 : gss_mech_put((*context_handle)->mech_type);
438 : 0 : kfree(*context_handle);
439 : 0 : *context_handle=NULL;
440 : 0 : return GSS_S_COMPLETE;
441 : : }
|