1 | // SPDX-License-Identifier: GPL-2.0+ |
2 | /* |
3 | * GSS Proxy upcall module |
4 | * |
5 | * Copyright (C) 2012 Simo Sorce <simo@redhat.com> |
6 | */ |
7 | |
8 | #include <linux/sunrpc/svcauth.h> |
9 | #include "gss_rpc_xdr.h" |
10 | |
11 | static int gssx_enc_bool(struct xdr_stream *xdr, int v) |
12 | { |
13 | __be32 *p; |
14 | |
15 | p = xdr_reserve_space(xdr, nbytes: 4); |
16 | if (unlikely(p == NULL)) |
17 | return -ENOSPC; |
18 | *p = v ? xdr_one : xdr_zero; |
19 | return 0; |
20 | } |
21 | |
22 | static int gssx_dec_bool(struct xdr_stream *xdr, u32 *v) |
23 | { |
24 | __be32 *p; |
25 | |
26 | p = xdr_inline_decode(xdr, nbytes: 4); |
27 | if (unlikely(p == NULL)) |
28 | return -ENOSPC; |
29 | *v = be32_to_cpu(*p); |
30 | return 0; |
31 | } |
32 | |
33 | static int gssx_enc_buffer(struct xdr_stream *xdr, |
34 | const gssx_buffer *buf) |
35 | { |
36 | __be32 *p; |
37 | |
38 | p = xdr_reserve_space(xdr, nbytes: sizeof(u32) + buf->len); |
39 | if (!p) |
40 | return -ENOSPC; |
41 | xdr_encode_opaque(p, ptr: buf->data, len: buf->len); |
42 | return 0; |
43 | } |
44 | |
45 | static int gssx_enc_in_token(struct xdr_stream *xdr, |
46 | const struct gssp_in_token *in) |
47 | { |
48 | __be32 *p; |
49 | |
50 | p = xdr_reserve_space(xdr, nbytes: 4); |
51 | if (!p) |
52 | return -ENOSPC; |
53 | *p = cpu_to_be32(in->page_len); |
54 | |
55 | /* all we need to do is to write pages */ |
56 | xdr_write_pages(xdr, pages: in->pages, base: in->page_base, len: in->page_len); |
57 | |
58 | return 0; |
59 | } |
60 | |
61 | |
62 | static int gssx_dec_buffer(struct xdr_stream *xdr, |
63 | gssx_buffer *buf) |
64 | { |
65 | u32 length; |
66 | __be32 *p; |
67 | |
68 | p = xdr_inline_decode(xdr, nbytes: 4); |
69 | if (unlikely(p == NULL)) |
70 | return -ENOSPC; |
71 | |
72 | length = be32_to_cpup(p); |
73 | p = xdr_inline_decode(xdr, nbytes: length); |
74 | if (unlikely(p == NULL)) |
75 | return -ENOSPC; |
76 | |
77 | if (buf->len == 0) { |
78 | /* we intentionally are not interested in this buffer */ |
79 | return 0; |
80 | } |
81 | if (length > buf->len) |
82 | return -ENOSPC; |
83 | |
84 | if (!buf->data) { |
85 | buf->data = kmemdup(p, size: length, GFP_KERNEL); |
86 | if (!buf->data) |
87 | return -ENOMEM; |
88 | } else { |
89 | memcpy(buf->data, p, length); |
90 | } |
91 | buf->len = length; |
92 | return 0; |
93 | } |
94 | |
95 | static int gssx_enc_option(struct xdr_stream *xdr, |
96 | struct gssx_option *opt) |
97 | { |
98 | int err; |
99 | |
100 | err = gssx_enc_buffer(xdr, buf: &opt->option); |
101 | if (err) |
102 | return err; |
103 | err = gssx_enc_buffer(xdr, buf: &opt->value); |
104 | return err; |
105 | } |
106 | |
107 | static int gssx_dec_option(struct xdr_stream *xdr, |
108 | struct gssx_option *opt) |
109 | { |
110 | int err; |
111 | |
112 | err = gssx_dec_buffer(xdr, buf: &opt->option); |
113 | if (err) |
114 | return err; |
115 | err = gssx_dec_buffer(xdr, buf: &opt->value); |
116 | return err; |
117 | } |
118 | |
119 | static int dummy_enc_opt_array(struct xdr_stream *xdr, |
120 | const struct gssx_option_array *oa) |
121 | { |
122 | __be32 *p; |
123 | |
124 | if (oa->count != 0) |
125 | return -EINVAL; |
126 | |
127 | p = xdr_reserve_space(xdr, nbytes: 4); |
128 | if (!p) |
129 | return -ENOSPC; |
130 | *p = 0; |
131 | |
132 | return 0; |
133 | } |
134 | |
135 | static int dummy_dec_opt_array(struct xdr_stream *xdr, |
136 | struct gssx_option_array *oa) |
137 | { |
138 | struct gssx_option dummy; |
139 | u32 count, i; |
140 | __be32 *p; |
141 | |
142 | p = xdr_inline_decode(xdr, nbytes: 4); |
143 | if (unlikely(p == NULL)) |
144 | return -ENOSPC; |
145 | count = be32_to_cpup(p: p++); |
146 | memset(&dummy, 0, sizeof(dummy)); |
147 | for (i = 0; i < count; i++) { |
148 | gssx_dec_option(xdr, opt: &dummy); |
149 | } |
150 | |
151 | oa->count = 0; |
152 | oa->data = NULL; |
153 | return 0; |
154 | } |
155 | |
156 | static int get_host_u32(struct xdr_stream *xdr, u32 *res) |
157 | { |
158 | __be32 *p; |
159 | |
160 | p = xdr_inline_decode(xdr, nbytes: 4); |
161 | if (!p) |
162 | return -EINVAL; |
163 | /* Contents of linux creds are all host-endian: */ |
164 | memcpy(res, p, sizeof(u32)); |
165 | return 0; |
166 | } |
167 | |
168 | static int gssx_dec_linux_creds(struct xdr_stream *xdr, |
169 | struct svc_cred *creds) |
170 | { |
171 | u32 length; |
172 | __be32 *p; |
173 | u32 tmp; |
174 | u32 N; |
175 | int i, err; |
176 | |
177 | p = xdr_inline_decode(xdr, nbytes: 4); |
178 | if (unlikely(p == NULL)) |
179 | return -ENOSPC; |
180 | |
181 | length = be32_to_cpup(p); |
182 | |
183 | if (length > (3 + NGROUPS_MAX) * sizeof(u32)) |
184 | return -ENOSPC; |
185 | |
186 | /* uid */ |
187 | err = get_host_u32(xdr, res: &tmp); |
188 | if (err) |
189 | return err; |
190 | creds->cr_uid = make_kuid(from: &init_user_ns, uid: tmp); |
191 | |
192 | /* gid */ |
193 | err = get_host_u32(xdr, res: &tmp); |
194 | if (err) |
195 | return err; |
196 | creds->cr_gid = make_kgid(from: &init_user_ns, gid: tmp); |
197 | |
198 | /* number of additional gid's */ |
199 | err = get_host_u32(xdr, res: &tmp); |
200 | if (err) |
201 | return err; |
202 | N = tmp; |
203 | if ((3 + N) * sizeof(u32) != length) |
204 | return -EINVAL; |
205 | creds->cr_group_info = groups_alloc(N); |
206 | if (creds->cr_group_info == NULL) |
207 | return -ENOMEM; |
208 | |
209 | /* gid's */ |
210 | for (i = 0; i < N; i++) { |
211 | kgid_t kgid; |
212 | err = get_host_u32(xdr, res: &tmp); |
213 | if (err) |
214 | goto out_free_groups; |
215 | err = -EINVAL; |
216 | kgid = make_kgid(from: &init_user_ns, gid: tmp); |
217 | if (!gid_valid(gid: kgid)) |
218 | goto out_free_groups; |
219 | creds->cr_group_info->gid[i] = kgid; |
220 | } |
221 | groups_sort(creds->cr_group_info); |
222 | |
223 | return 0; |
224 | out_free_groups: |
225 | groups_free(creds->cr_group_info); |
226 | return err; |
227 | } |
228 | |
229 | static int gssx_dec_option_array(struct xdr_stream *xdr, |
230 | struct gssx_option_array *oa) |
231 | { |
232 | struct svc_cred *creds; |
233 | u32 count, i; |
234 | __be32 *p; |
235 | int err; |
236 | |
237 | p = xdr_inline_decode(xdr, nbytes: 4); |
238 | if (unlikely(p == NULL)) |
239 | return -ENOSPC; |
240 | count = be32_to_cpup(p: p++); |
241 | if (!count) |
242 | return 0; |
243 | |
244 | /* we recognize only 1 currently: CREDS_VALUE */ |
245 | oa->count = 1; |
246 | |
247 | oa->data = kmalloc(size: sizeof(struct gssx_option), GFP_KERNEL); |
248 | if (!oa->data) |
249 | return -ENOMEM; |
250 | |
251 | creds = kzalloc(size: sizeof(struct svc_cred), GFP_KERNEL); |
252 | if (!creds) { |
253 | err = -ENOMEM; |
254 | goto free_oa; |
255 | } |
256 | |
257 | oa->data[0].option.data = CREDS_VALUE; |
258 | oa->data[0].option.len = sizeof(CREDS_VALUE); |
259 | oa->data[0].value.data = (void *)creds; |
260 | oa->data[0].value.len = 0; |
261 | |
262 | for (i = 0; i < count; i++) { |
263 | gssx_buffer dummy = { 0, NULL }; |
264 | u32 length; |
265 | |
266 | /* option buffer */ |
267 | p = xdr_inline_decode(xdr, nbytes: 4); |
268 | if (unlikely(p == NULL)) { |
269 | err = -ENOSPC; |
270 | goto free_creds; |
271 | } |
272 | |
273 | length = be32_to_cpup(p); |
274 | p = xdr_inline_decode(xdr, nbytes: length); |
275 | if (unlikely(p == NULL)) { |
276 | err = -ENOSPC; |
277 | goto free_creds; |
278 | } |
279 | |
280 | if (length == sizeof(CREDS_VALUE) && |
281 | memcmp(p, CREDS_VALUE, size: sizeof(CREDS_VALUE)) == 0) { |
282 | /* We have creds here. parse them */ |
283 | err = gssx_dec_linux_creds(xdr, creds); |
284 | if (err) |
285 | goto free_creds; |
286 | oa->data[0].value.len = 1; /* presence */ |
287 | } else { |
288 | /* consume uninteresting buffer */ |
289 | err = gssx_dec_buffer(xdr, buf: &dummy); |
290 | if (err) |
291 | goto free_creds; |
292 | } |
293 | } |
294 | return 0; |
295 | |
296 | free_creds: |
297 | kfree(objp: creds); |
298 | free_oa: |
299 | kfree(objp: oa->data); |
300 | oa->data = NULL; |
301 | return err; |
302 | } |
303 | |
304 | static int gssx_dec_status(struct xdr_stream *xdr, |
305 | struct gssx_status *status) |
306 | { |
307 | __be32 *p; |
308 | int err; |
309 | |
310 | /* status->major_status */ |
311 | p = xdr_inline_decode(xdr, nbytes: 8); |
312 | if (unlikely(p == NULL)) |
313 | return -ENOSPC; |
314 | p = xdr_decode_hyper(p, valp: &status->major_status); |
315 | |
316 | /* status->mech */ |
317 | err = gssx_dec_buffer(xdr, buf: &status->mech); |
318 | if (err) |
319 | return err; |
320 | |
321 | /* status->minor_status */ |
322 | p = xdr_inline_decode(xdr, nbytes: 8); |
323 | if (unlikely(p == NULL)) |
324 | return -ENOSPC; |
325 | p = xdr_decode_hyper(p, valp: &status->minor_status); |
326 | |
327 | /* status->major_status_string */ |
328 | err = gssx_dec_buffer(xdr, buf: &status->major_status_string); |
329 | if (err) |
330 | return err; |
331 | |
332 | /* status->minor_status_string */ |
333 | err = gssx_dec_buffer(xdr, buf: &status->minor_status_string); |
334 | if (err) |
335 | return err; |
336 | |
337 | /* status->server_ctx */ |
338 | err = gssx_dec_buffer(xdr, buf: &status->server_ctx); |
339 | if (err) |
340 | return err; |
341 | |
342 | /* we assume we have no options for now, so simply consume them */ |
343 | /* status->options */ |
344 | err = dummy_dec_opt_array(xdr, oa: &status->options); |
345 | |
346 | return err; |
347 | } |
348 | |
349 | static int gssx_enc_call_ctx(struct xdr_stream *xdr, |
350 | const struct gssx_call_ctx *ctx) |
351 | { |
352 | struct gssx_option opt; |
353 | __be32 *p; |
354 | int err; |
355 | |
356 | /* ctx->locale */ |
357 | err = gssx_enc_buffer(xdr, buf: &ctx->locale); |
358 | if (err) |
359 | return err; |
360 | |
361 | /* ctx->server_ctx */ |
362 | err = gssx_enc_buffer(xdr, buf: &ctx->server_ctx); |
363 | if (err) |
364 | return err; |
365 | |
366 | /* we always want to ask for lucid contexts */ |
367 | /* ctx->options */ |
368 | p = xdr_reserve_space(xdr, nbytes: 4); |
369 | *p = cpu_to_be32(2); |
370 | |
371 | /* we want a lucid_v1 context */ |
372 | opt.option.data = LUCID_OPTION; |
373 | opt.option.len = sizeof(LUCID_OPTION); |
374 | opt.value.data = LUCID_VALUE; |
375 | opt.value.len = sizeof(LUCID_VALUE); |
376 | err = gssx_enc_option(xdr, opt: &opt); |
377 | |
378 | /* ..and user creds */ |
379 | opt.option.data = CREDS_OPTION; |
380 | opt.option.len = sizeof(CREDS_OPTION); |
381 | opt.value.data = CREDS_VALUE; |
382 | opt.value.len = sizeof(CREDS_VALUE); |
383 | err = gssx_enc_option(xdr, opt: &opt); |
384 | |
385 | return err; |
386 | } |
387 | |
388 | static int gssx_dec_name_attr(struct xdr_stream *xdr, |
389 | struct gssx_name_attr *attr) |
390 | { |
391 | int err; |
392 | |
393 | /* attr->attr */ |
394 | err = gssx_dec_buffer(xdr, buf: &attr->attr); |
395 | if (err) |
396 | return err; |
397 | |
398 | /* attr->value */ |
399 | err = gssx_dec_buffer(xdr, buf: &attr->value); |
400 | if (err) |
401 | return err; |
402 | |
403 | /* attr->extensions */ |
404 | err = dummy_dec_opt_array(xdr, oa: &attr->extensions); |
405 | |
406 | return err; |
407 | } |
408 | |
409 | static int dummy_enc_nameattr_array(struct xdr_stream *xdr, |
410 | struct gssx_name_attr_array *naa) |
411 | { |
412 | __be32 *p; |
413 | |
414 | if (naa->count != 0) |
415 | return -EINVAL; |
416 | |
417 | p = xdr_reserve_space(xdr, nbytes: 4); |
418 | if (!p) |
419 | return -ENOSPC; |
420 | *p = 0; |
421 | |
422 | return 0; |
423 | } |
424 | |
425 | static int dummy_dec_nameattr_array(struct xdr_stream *xdr, |
426 | struct gssx_name_attr_array *naa) |
427 | { |
428 | struct gssx_name_attr dummy = { .attr = {.len = 0} }; |
429 | u32 count, i; |
430 | __be32 *p; |
431 | |
432 | p = xdr_inline_decode(xdr, nbytes: 4); |
433 | if (unlikely(p == NULL)) |
434 | return -ENOSPC; |
435 | count = be32_to_cpup(p: p++); |
436 | for (i = 0; i < count; i++) { |
437 | gssx_dec_name_attr(xdr, attr: &dummy); |
438 | } |
439 | |
440 | naa->count = 0; |
441 | naa->data = NULL; |
442 | return 0; |
443 | } |
444 | |
445 | static struct xdr_netobj zero_netobj = {}; |
446 | |
447 | static struct gssx_name_attr_array zero_name_attr_array = {}; |
448 | |
449 | static struct gssx_option_array zero_option_array = {}; |
450 | |
451 | static int gssx_enc_name(struct xdr_stream *xdr, |
452 | struct gssx_name *name) |
453 | { |
454 | int err; |
455 | |
456 | /* name->display_name */ |
457 | err = gssx_enc_buffer(xdr, buf: &name->display_name); |
458 | if (err) |
459 | return err; |
460 | |
461 | /* name->name_type */ |
462 | err = gssx_enc_buffer(xdr, buf: &zero_netobj); |
463 | if (err) |
464 | return err; |
465 | |
466 | /* name->exported_name */ |
467 | err = gssx_enc_buffer(xdr, buf: &zero_netobj); |
468 | if (err) |
469 | return err; |
470 | |
471 | /* name->exported_composite_name */ |
472 | err = gssx_enc_buffer(xdr, buf: &zero_netobj); |
473 | if (err) |
474 | return err; |
475 | |
476 | /* leave name_attributes empty for now, will add once we have any |
477 | * to pass up at all */ |
478 | /* name->name_attributes */ |
479 | err = dummy_enc_nameattr_array(xdr, naa: &zero_name_attr_array); |
480 | if (err) |
481 | return err; |
482 | |
483 | /* leave options empty for now, will add once we have any options |
484 | * to pass up at all */ |
485 | /* name->extensions */ |
486 | err = dummy_enc_opt_array(xdr, oa: &zero_option_array); |
487 | |
488 | return err; |
489 | } |
490 | |
491 | |
492 | static int gssx_dec_name(struct xdr_stream *xdr, |
493 | struct gssx_name *name) |
494 | { |
495 | struct xdr_netobj dummy_netobj = { .len = 0 }; |
496 | struct gssx_name_attr_array dummy_name_attr_array = { .count = 0 }; |
497 | struct gssx_option_array dummy_option_array = { .count = 0 }; |
498 | int err; |
499 | |
500 | /* name->display_name */ |
501 | err = gssx_dec_buffer(xdr, buf: &name->display_name); |
502 | if (err) |
503 | return err; |
504 | |
505 | /* name->name_type */ |
506 | err = gssx_dec_buffer(xdr, buf: &dummy_netobj); |
507 | if (err) |
508 | return err; |
509 | |
510 | /* name->exported_name */ |
511 | err = gssx_dec_buffer(xdr, buf: &dummy_netobj); |
512 | if (err) |
513 | return err; |
514 | |
515 | /* name->exported_composite_name */ |
516 | err = gssx_dec_buffer(xdr, buf: &dummy_netobj); |
517 | if (err) |
518 | return err; |
519 | |
520 | /* we assume we have no attributes for now, so simply consume them */ |
521 | /* name->name_attributes */ |
522 | err = dummy_dec_nameattr_array(xdr, naa: &dummy_name_attr_array); |
523 | if (err) |
524 | return err; |
525 | |
526 | /* we assume we have no options for now, so simply consume them */ |
527 | /* name->extensions */ |
528 | err = dummy_dec_opt_array(xdr, oa: &dummy_option_array); |
529 | |
530 | return err; |
531 | } |
532 | |
533 | static int dummy_enc_credel_array(struct xdr_stream *xdr, |
534 | struct gssx_cred_element_array *cea) |
535 | { |
536 | __be32 *p; |
537 | |
538 | if (cea->count != 0) |
539 | return -EINVAL; |
540 | |
541 | p = xdr_reserve_space(xdr, nbytes: 4); |
542 | if (!p) |
543 | return -ENOSPC; |
544 | *p = 0; |
545 | |
546 | return 0; |
547 | } |
548 | |
549 | static int gssx_enc_cred(struct xdr_stream *xdr, |
550 | struct gssx_cred *cred) |
551 | { |
552 | int err; |
553 | |
554 | /* cred->desired_name */ |
555 | err = gssx_enc_name(xdr, name: &cred->desired_name); |
556 | if (err) |
557 | return err; |
558 | |
559 | /* cred->elements */ |
560 | err = dummy_enc_credel_array(xdr, cea: &cred->elements); |
561 | if (err) |
562 | return err; |
563 | |
564 | /* cred->cred_handle_reference */ |
565 | err = gssx_enc_buffer(xdr, buf: &cred->cred_handle_reference); |
566 | if (err) |
567 | return err; |
568 | |
569 | /* cred->needs_release */ |
570 | err = gssx_enc_bool(xdr, v: cred->needs_release); |
571 | |
572 | return err; |
573 | } |
574 | |
575 | static int gssx_enc_ctx(struct xdr_stream *xdr, |
576 | struct gssx_ctx *ctx) |
577 | { |
578 | __be32 *p; |
579 | int err; |
580 | |
581 | /* ctx->exported_context_token */ |
582 | err = gssx_enc_buffer(xdr, buf: &ctx->exported_context_token); |
583 | if (err) |
584 | return err; |
585 | |
586 | /* ctx->state */ |
587 | err = gssx_enc_buffer(xdr, buf: &ctx->state); |
588 | if (err) |
589 | return err; |
590 | |
591 | /* ctx->need_release */ |
592 | err = gssx_enc_bool(xdr, v: ctx->need_release); |
593 | if (err) |
594 | return err; |
595 | |
596 | /* ctx->mech */ |
597 | err = gssx_enc_buffer(xdr, buf: &ctx->mech); |
598 | if (err) |
599 | return err; |
600 | |
601 | /* ctx->src_name */ |
602 | err = gssx_enc_name(xdr, name: &ctx->src_name); |
603 | if (err) |
604 | return err; |
605 | |
606 | /* ctx->targ_name */ |
607 | err = gssx_enc_name(xdr, name: &ctx->targ_name); |
608 | if (err) |
609 | return err; |
610 | |
611 | /* ctx->lifetime */ |
612 | p = xdr_reserve_space(xdr, nbytes: 8+8); |
613 | if (!p) |
614 | return -ENOSPC; |
615 | p = xdr_encode_hyper(p, val: ctx->lifetime); |
616 | |
617 | /* ctx->ctx_flags */ |
618 | p = xdr_encode_hyper(p, val: ctx->ctx_flags); |
619 | |
620 | /* ctx->locally_initiated */ |
621 | err = gssx_enc_bool(xdr, v: ctx->locally_initiated); |
622 | if (err) |
623 | return err; |
624 | |
625 | /* ctx->open */ |
626 | err = gssx_enc_bool(xdr, v: ctx->open); |
627 | if (err) |
628 | return err; |
629 | |
630 | /* leave options empty for now, will add once we have any options |
631 | * to pass up at all */ |
632 | /* ctx->options */ |
633 | err = dummy_enc_opt_array(xdr, oa: &ctx->options); |
634 | |
635 | return err; |
636 | } |
637 | |
638 | static int gssx_dec_ctx(struct xdr_stream *xdr, |
639 | struct gssx_ctx *ctx) |
640 | { |
641 | __be32 *p; |
642 | int err; |
643 | |
644 | /* ctx->exported_context_token */ |
645 | err = gssx_dec_buffer(xdr, buf: &ctx->exported_context_token); |
646 | if (err) |
647 | return err; |
648 | |
649 | /* ctx->state */ |
650 | err = gssx_dec_buffer(xdr, buf: &ctx->state); |
651 | if (err) |
652 | return err; |
653 | |
654 | /* ctx->need_release */ |
655 | err = gssx_dec_bool(xdr, v: &ctx->need_release); |
656 | if (err) |
657 | return err; |
658 | |
659 | /* ctx->mech */ |
660 | err = gssx_dec_buffer(xdr, buf: &ctx->mech); |
661 | if (err) |
662 | return err; |
663 | |
664 | /* ctx->src_name */ |
665 | err = gssx_dec_name(xdr, name: &ctx->src_name); |
666 | if (err) |
667 | return err; |
668 | |
669 | /* ctx->targ_name */ |
670 | err = gssx_dec_name(xdr, name: &ctx->targ_name); |
671 | if (err) |
672 | return err; |
673 | |
674 | /* ctx->lifetime */ |
675 | p = xdr_inline_decode(xdr, nbytes: 8+8); |
676 | if (unlikely(p == NULL)) |
677 | return -ENOSPC; |
678 | p = xdr_decode_hyper(p, valp: &ctx->lifetime); |
679 | |
680 | /* ctx->ctx_flags */ |
681 | p = xdr_decode_hyper(p, valp: &ctx->ctx_flags); |
682 | |
683 | /* ctx->locally_initiated */ |
684 | err = gssx_dec_bool(xdr, v: &ctx->locally_initiated); |
685 | if (err) |
686 | return err; |
687 | |
688 | /* ctx->open */ |
689 | err = gssx_dec_bool(xdr, v: &ctx->open); |
690 | if (err) |
691 | return err; |
692 | |
693 | /* we assume we have no options for now, so simply consume them */ |
694 | /* ctx->options */ |
695 | err = dummy_dec_opt_array(xdr, oa: &ctx->options); |
696 | |
697 | return err; |
698 | } |
699 | |
700 | static int gssx_enc_cb(struct xdr_stream *xdr, struct gssx_cb *cb) |
701 | { |
702 | __be32 *p; |
703 | int err; |
704 | |
705 | /* cb->initiator_addrtype */ |
706 | p = xdr_reserve_space(xdr, nbytes: 8); |
707 | if (!p) |
708 | return -ENOSPC; |
709 | p = xdr_encode_hyper(p, val: cb->initiator_addrtype); |
710 | |
711 | /* cb->initiator_address */ |
712 | err = gssx_enc_buffer(xdr, buf: &cb->initiator_address); |
713 | if (err) |
714 | return err; |
715 | |
716 | /* cb->acceptor_addrtype */ |
717 | p = xdr_reserve_space(xdr, nbytes: 8); |
718 | if (!p) |
719 | return -ENOSPC; |
720 | p = xdr_encode_hyper(p, val: cb->acceptor_addrtype); |
721 | |
722 | /* cb->acceptor_address */ |
723 | err = gssx_enc_buffer(xdr, buf: &cb->acceptor_address); |
724 | if (err) |
725 | return err; |
726 | |
727 | /* cb->application_data */ |
728 | err = gssx_enc_buffer(xdr, buf: &cb->application_data); |
729 | |
730 | return err; |
731 | } |
732 | |
733 | void gssx_enc_accept_sec_context(struct rpc_rqst *req, |
734 | struct xdr_stream *xdr, |
735 | const void *data) |
736 | { |
737 | const struct gssx_arg_accept_sec_context *arg = data; |
738 | int err; |
739 | |
740 | err = gssx_enc_call_ctx(xdr, ctx: &arg->call_ctx); |
741 | if (err) |
742 | goto done; |
743 | |
744 | /* arg->context_handle */ |
745 | if (arg->context_handle) |
746 | err = gssx_enc_ctx(xdr, ctx: arg->context_handle); |
747 | else |
748 | err = gssx_enc_bool(xdr, v: 0); |
749 | if (err) |
750 | goto done; |
751 | |
752 | /* arg->cred_handle */ |
753 | if (arg->cred_handle) |
754 | err = gssx_enc_cred(xdr, cred: arg->cred_handle); |
755 | else |
756 | err = gssx_enc_bool(xdr, v: 0); |
757 | if (err) |
758 | goto done; |
759 | |
760 | /* arg->input_token */ |
761 | err = gssx_enc_in_token(xdr, in: &arg->input_token); |
762 | if (err) |
763 | goto done; |
764 | |
765 | /* arg->input_cb */ |
766 | if (arg->input_cb) |
767 | err = gssx_enc_cb(xdr, cb: arg->input_cb); |
768 | else |
769 | err = gssx_enc_bool(xdr, v: 0); |
770 | if (err) |
771 | goto done; |
772 | |
773 | err = gssx_enc_bool(xdr, v: arg->ret_deleg_cred); |
774 | if (err) |
775 | goto done; |
776 | |
777 | /* leave options empty for now, will add once we have any options |
778 | * to pass up at all */ |
779 | /* arg->options */ |
780 | err = dummy_enc_opt_array(xdr, oa: &arg->options); |
781 | |
782 | xdr_inline_pages(&req->rq_rcv_buf, |
783 | PAGE_SIZE/2 /* pretty arbitrary */, |
784 | arg->pages, 0 /* page base */, arg->npages * PAGE_SIZE); |
785 | done: |
786 | if (err) |
787 | dprintk("RPC: gssx_enc_accept_sec_context: %d\n" , err); |
788 | } |
789 | |
790 | int gssx_dec_accept_sec_context(struct rpc_rqst *rqstp, |
791 | struct xdr_stream *xdr, |
792 | void *data) |
793 | { |
794 | struct gssx_res_accept_sec_context *res = data; |
795 | u32 value_follows; |
796 | int err; |
797 | struct page *scratch; |
798 | |
799 | scratch = alloc_page(GFP_KERNEL); |
800 | if (!scratch) |
801 | return -ENOMEM; |
802 | xdr_set_scratch_page(xdr, page: scratch); |
803 | |
804 | /* res->status */ |
805 | err = gssx_dec_status(xdr, status: &res->status); |
806 | if (err) |
807 | goto out_free; |
808 | |
809 | /* res->context_handle */ |
810 | err = gssx_dec_bool(xdr, v: &value_follows); |
811 | if (err) |
812 | goto out_free; |
813 | if (value_follows) { |
814 | err = gssx_dec_ctx(xdr, ctx: res->context_handle); |
815 | if (err) |
816 | goto out_free; |
817 | } else { |
818 | res->context_handle = NULL; |
819 | } |
820 | |
821 | /* res->output_token */ |
822 | err = gssx_dec_bool(xdr, v: &value_follows); |
823 | if (err) |
824 | goto out_free; |
825 | if (value_follows) { |
826 | err = gssx_dec_buffer(xdr, buf: res->output_token); |
827 | if (err) |
828 | goto out_free; |
829 | } else { |
830 | res->output_token = NULL; |
831 | } |
832 | |
833 | /* res->delegated_cred_handle */ |
834 | err = gssx_dec_bool(xdr, v: &value_follows); |
835 | if (err) |
836 | goto out_free; |
837 | if (value_follows) { |
838 | /* we do not support upcall servers sending this data. */ |
839 | err = -EINVAL; |
840 | goto out_free; |
841 | } |
842 | |
843 | /* res->options */ |
844 | err = gssx_dec_option_array(xdr, oa: &res->options); |
845 | |
846 | out_free: |
847 | __free_page(scratch); |
848 | return err; |
849 | } |
850 | |