xref: /linux/net/sunrpc/auth_gss/gss_rpc_xdr.c (revision 26fbb4c8c7c3ee9a4c3b4de555a8587b5a19154e)
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, 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, 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, sizeof(u32) + buf->len);
39 	if (!p)
40 		return -ENOSPC;
41 	xdr_encode_opaque(p, buf->data, 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, 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, in->pages, in->page_base, 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, 4);
69 	if (unlikely(p == NULL))
70 		return -ENOSPC;
71 
72 	length = be32_to_cpup(p);
73 	p = xdr_inline_decode(xdr, 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, 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, &opt->option);
101 	if (err)
102 		return err;
103 	err = gssx_enc_buffer(xdr, &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, &opt->option);
113 	if (err)
114 		return err;
115 	err = gssx_dec_buffer(xdr, &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, 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, 4);
143 	if (unlikely(p == NULL))
144 		return -ENOSPC;
145 	count = be32_to_cpup(p++);
146 	memset(&dummy, 0, sizeof(dummy));
147 	for (i = 0; i < count; i++) {
148 		gssx_dec_option(xdr, &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, 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, 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, &tmp);
188 	if (err)
189 		return err;
190 	creds->cr_uid = make_kuid(&init_user_ns, tmp);
191 
192 	/* gid */
193 	err = get_host_u32(xdr, &tmp);
194 	if (err)
195 		return err;
196 	creds->cr_gid = make_kgid(&init_user_ns, tmp);
197 
198 	/* number of additional gid's */
199 	err = get_host_u32(xdr, &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, &tmp);
213 		if (err)
214 			goto out_free_groups;
215 		err = -EINVAL;
216 		kgid = make_kgid(&init_user_ns, tmp);
217 		if (!gid_valid(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, 4);
238 	if (unlikely(p == NULL))
239 		return -ENOSPC;
240 	count = be32_to_cpup(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(sizeof(struct gssx_option), GFP_KERNEL);
248 	if (!oa->data)
249 		return -ENOMEM;
250 
251 	creds = kzalloc(sizeof(struct svc_cred), GFP_KERNEL);
252 	if (!creds) {
253 		kfree(oa->data);
254 		return -ENOMEM;
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, 4);
268 		if (unlikely(p == NULL))
269 			return -ENOSPC;
270 
271 		length = be32_to_cpup(p);
272 		p = xdr_inline_decode(xdr, length);
273 		if (unlikely(p == NULL))
274 			return -ENOSPC;
275 
276 		if (length == sizeof(CREDS_VALUE) &&
277 		    memcmp(p, CREDS_VALUE, sizeof(CREDS_VALUE)) == 0) {
278 			/* We have creds here. parse them */
279 			err = gssx_dec_linux_creds(xdr, creds);
280 			if (err)
281 				return err;
282 			oa->data[0].value.len = 1; /* presence */
283 		} else {
284 			/* consume uninteresting buffer */
285 			err = gssx_dec_buffer(xdr, &dummy);
286 			if (err)
287 				return err;
288 		}
289 	}
290 	return 0;
291 }
292 
293 static int gssx_dec_status(struct xdr_stream *xdr,
294 			   struct gssx_status *status)
295 {
296 	__be32 *p;
297 	int err;
298 
299 	/* status->major_status */
300 	p = xdr_inline_decode(xdr, 8);
301 	if (unlikely(p == NULL))
302 		return -ENOSPC;
303 	p = xdr_decode_hyper(p, &status->major_status);
304 
305 	/* status->mech */
306 	err = gssx_dec_buffer(xdr, &status->mech);
307 	if (err)
308 		return err;
309 
310 	/* status->minor_status */
311 	p = xdr_inline_decode(xdr, 8);
312 	if (unlikely(p == NULL))
313 		return -ENOSPC;
314 	p = xdr_decode_hyper(p, &status->minor_status);
315 
316 	/* status->major_status_string */
317 	err = gssx_dec_buffer(xdr, &status->major_status_string);
318 	if (err)
319 		return err;
320 
321 	/* status->minor_status_string */
322 	err = gssx_dec_buffer(xdr, &status->minor_status_string);
323 	if (err)
324 		return err;
325 
326 	/* status->server_ctx */
327 	err = gssx_dec_buffer(xdr, &status->server_ctx);
328 	if (err)
329 		return err;
330 
331 	/* we assume we have no options for now, so simply consume them */
332 	/* status->options */
333 	err = dummy_dec_opt_array(xdr, &status->options);
334 
335 	return err;
336 }
337 
338 static int gssx_enc_call_ctx(struct xdr_stream *xdr,
339 			     const struct gssx_call_ctx *ctx)
340 {
341 	struct gssx_option opt;
342 	__be32 *p;
343 	int err;
344 
345 	/* ctx->locale */
346 	err = gssx_enc_buffer(xdr, &ctx->locale);
347 	if (err)
348 		return err;
349 
350 	/* ctx->server_ctx */
351 	err = gssx_enc_buffer(xdr, &ctx->server_ctx);
352 	if (err)
353 		return err;
354 
355 	/* we always want to ask for lucid contexts */
356 	/* ctx->options */
357 	p = xdr_reserve_space(xdr, 4);
358 	*p = cpu_to_be32(2);
359 
360 	/* we want a lucid_v1 context */
361 	opt.option.data = LUCID_OPTION;
362 	opt.option.len = sizeof(LUCID_OPTION);
363 	opt.value.data = LUCID_VALUE;
364 	opt.value.len = sizeof(LUCID_VALUE);
365 	err = gssx_enc_option(xdr, &opt);
366 
367 	/* ..and user creds */
368 	opt.option.data = CREDS_OPTION;
369 	opt.option.len = sizeof(CREDS_OPTION);
370 	opt.value.data = CREDS_VALUE;
371 	opt.value.len = sizeof(CREDS_VALUE);
372 	err = gssx_enc_option(xdr, &opt);
373 
374 	return err;
375 }
376 
377 static int gssx_dec_name_attr(struct xdr_stream *xdr,
378 			     struct gssx_name_attr *attr)
379 {
380 	int err;
381 
382 	/* attr->attr */
383 	err = gssx_dec_buffer(xdr, &attr->attr);
384 	if (err)
385 		return err;
386 
387 	/* attr->value */
388 	err = gssx_dec_buffer(xdr, &attr->value);
389 	if (err)
390 		return err;
391 
392 	/* attr->extensions */
393 	err = dummy_dec_opt_array(xdr, &attr->extensions);
394 
395 	return err;
396 }
397 
398 static int dummy_enc_nameattr_array(struct xdr_stream *xdr,
399 				    struct gssx_name_attr_array *naa)
400 {
401 	__be32 *p;
402 
403 	if (naa->count != 0)
404 		return -EINVAL;
405 
406 	p = xdr_reserve_space(xdr, 4);
407 	if (!p)
408 		return -ENOSPC;
409 	*p = 0;
410 
411 	return 0;
412 }
413 
414 static int dummy_dec_nameattr_array(struct xdr_stream *xdr,
415 				    struct gssx_name_attr_array *naa)
416 {
417 	struct gssx_name_attr dummy = { .attr = {.len = 0} };
418 	u32 count, i;
419 	__be32 *p;
420 
421 	p = xdr_inline_decode(xdr, 4);
422 	if (unlikely(p == NULL))
423 		return -ENOSPC;
424 	count = be32_to_cpup(p++);
425 	for (i = 0; i < count; i++) {
426 		gssx_dec_name_attr(xdr, &dummy);
427 	}
428 
429 	naa->count = 0;
430 	naa->data = NULL;
431 	return 0;
432 }
433 
434 static struct xdr_netobj zero_netobj = {};
435 
436 static struct gssx_name_attr_array zero_name_attr_array = {};
437 
438 static struct gssx_option_array zero_option_array = {};
439 
440 static int gssx_enc_name(struct xdr_stream *xdr,
441 			 struct gssx_name *name)
442 {
443 	int err;
444 
445 	/* name->display_name */
446 	err = gssx_enc_buffer(xdr, &name->display_name);
447 	if (err)
448 		return err;
449 
450 	/* name->name_type */
451 	err = gssx_enc_buffer(xdr, &zero_netobj);
452 	if (err)
453 		return err;
454 
455 	/* name->exported_name */
456 	err = gssx_enc_buffer(xdr, &zero_netobj);
457 	if (err)
458 		return err;
459 
460 	/* name->exported_composite_name */
461 	err = gssx_enc_buffer(xdr, &zero_netobj);
462 	if (err)
463 		return err;
464 
465 	/* leave name_attributes empty for now, will add once we have any
466 	 * to pass up at all */
467 	/* name->name_attributes */
468 	err = dummy_enc_nameattr_array(xdr, &zero_name_attr_array);
469 	if (err)
470 		return err;
471 
472 	/* leave options empty for now, will add once we have any options
473 	 * to pass up at all */
474 	/* name->extensions */
475 	err = dummy_enc_opt_array(xdr, &zero_option_array);
476 
477 	return err;
478 }
479 
480 
481 static int gssx_dec_name(struct xdr_stream *xdr,
482 			 struct gssx_name *name)
483 {
484 	struct xdr_netobj dummy_netobj = { .len = 0 };
485 	struct gssx_name_attr_array dummy_name_attr_array = { .count = 0 };
486 	struct gssx_option_array dummy_option_array = { .count = 0 };
487 	int err;
488 
489 	/* name->display_name */
490 	err = gssx_dec_buffer(xdr, &name->display_name);
491 	if (err)
492 		return err;
493 
494 	/* name->name_type */
495 	err = gssx_dec_buffer(xdr, &dummy_netobj);
496 	if (err)
497 		return err;
498 
499 	/* name->exported_name */
500 	err = gssx_dec_buffer(xdr, &dummy_netobj);
501 	if (err)
502 		return err;
503 
504 	/* name->exported_composite_name */
505 	err = gssx_dec_buffer(xdr, &dummy_netobj);
506 	if (err)
507 		return err;
508 
509 	/* we assume we have no attributes for now, so simply consume them */
510 	/* name->name_attributes */
511 	err = dummy_dec_nameattr_array(xdr, &dummy_name_attr_array);
512 	if (err)
513 		return err;
514 
515 	/* we assume we have no options for now, so simply consume them */
516 	/* name->extensions */
517 	err = dummy_dec_opt_array(xdr, &dummy_option_array);
518 
519 	return err;
520 }
521 
522 static int dummy_enc_credel_array(struct xdr_stream *xdr,
523 				  struct gssx_cred_element_array *cea)
524 {
525 	__be32 *p;
526 
527 	if (cea->count != 0)
528 		return -EINVAL;
529 
530 	p = xdr_reserve_space(xdr, 4);
531 	if (!p)
532 		return -ENOSPC;
533 	*p = 0;
534 
535 	return 0;
536 }
537 
538 static int gssx_enc_cred(struct xdr_stream *xdr,
539 			 struct gssx_cred *cred)
540 {
541 	int err;
542 
543 	/* cred->desired_name */
544 	err = gssx_enc_name(xdr, &cred->desired_name);
545 	if (err)
546 		return err;
547 
548 	/* cred->elements */
549 	err = dummy_enc_credel_array(xdr, &cred->elements);
550 	if (err)
551 		return err;
552 
553 	/* cred->cred_handle_reference */
554 	err = gssx_enc_buffer(xdr, &cred->cred_handle_reference);
555 	if (err)
556 		return err;
557 
558 	/* cred->needs_release */
559 	err = gssx_enc_bool(xdr, cred->needs_release);
560 
561 	return err;
562 }
563 
564 static int gssx_enc_ctx(struct xdr_stream *xdr,
565 			struct gssx_ctx *ctx)
566 {
567 	__be32 *p;
568 	int err;
569 
570 	/* ctx->exported_context_token */
571 	err = gssx_enc_buffer(xdr, &ctx->exported_context_token);
572 	if (err)
573 		return err;
574 
575 	/* ctx->state */
576 	err = gssx_enc_buffer(xdr, &ctx->state);
577 	if (err)
578 		return err;
579 
580 	/* ctx->need_release */
581 	err = gssx_enc_bool(xdr, ctx->need_release);
582 	if (err)
583 		return err;
584 
585 	/* ctx->mech */
586 	err = gssx_enc_buffer(xdr, &ctx->mech);
587 	if (err)
588 		return err;
589 
590 	/* ctx->src_name */
591 	err = gssx_enc_name(xdr, &ctx->src_name);
592 	if (err)
593 		return err;
594 
595 	/* ctx->targ_name */
596 	err = gssx_enc_name(xdr, &ctx->targ_name);
597 	if (err)
598 		return err;
599 
600 	/* ctx->lifetime */
601 	p = xdr_reserve_space(xdr, 8+8);
602 	if (!p)
603 		return -ENOSPC;
604 	p = xdr_encode_hyper(p, ctx->lifetime);
605 
606 	/* ctx->ctx_flags */
607 	p = xdr_encode_hyper(p, ctx->ctx_flags);
608 
609 	/* ctx->locally_initiated */
610 	err = gssx_enc_bool(xdr, ctx->locally_initiated);
611 	if (err)
612 		return err;
613 
614 	/* ctx->open */
615 	err = gssx_enc_bool(xdr, ctx->open);
616 	if (err)
617 		return err;
618 
619 	/* leave options empty for now, will add once we have any options
620 	 * to pass up at all */
621 	/* ctx->options */
622 	err = dummy_enc_opt_array(xdr, &ctx->options);
623 
624 	return err;
625 }
626 
627 static int gssx_dec_ctx(struct xdr_stream *xdr,
628 			struct gssx_ctx *ctx)
629 {
630 	__be32 *p;
631 	int err;
632 
633 	/* ctx->exported_context_token */
634 	err = gssx_dec_buffer(xdr, &ctx->exported_context_token);
635 	if (err)
636 		return err;
637 
638 	/* ctx->state */
639 	err = gssx_dec_buffer(xdr, &ctx->state);
640 	if (err)
641 		return err;
642 
643 	/* ctx->need_release */
644 	err = gssx_dec_bool(xdr, &ctx->need_release);
645 	if (err)
646 		return err;
647 
648 	/* ctx->mech */
649 	err = gssx_dec_buffer(xdr, &ctx->mech);
650 	if (err)
651 		return err;
652 
653 	/* ctx->src_name */
654 	err = gssx_dec_name(xdr, &ctx->src_name);
655 	if (err)
656 		return err;
657 
658 	/* ctx->targ_name */
659 	err = gssx_dec_name(xdr, &ctx->targ_name);
660 	if (err)
661 		return err;
662 
663 	/* ctx->lifetime */
664 	p = xdr_inline_decode(xdr, 8+8);
665 	if (unlikely(p == NULL))
666 		return -ENOSPC;
667 	p = xdr_decode_hyper(p, &ctx->lifetime);
668 
669 	/* ctx->ctx_flags */
670 	p = xdr_decode_hyper(p, &ctx->ctx_flags);
671 
672 	/* ctx->locally_initiated */
673 	err = gssx_dec_bool(xdr, &ctx->locally_initiated);
674 	if (err)
675 		return err;
676 
677 	/* ctx->open */
678 	err = gssx_dec_bool(xdr, &ctx->open);
679 	if (err)
680 		return err;
681 
682 	/* we assume we have no options for now, so simply consume them */
683 	/* ctx->options */
684 	err = dummy_dec_opt_array(xdr, &ctx->options);
685 
686 	return err;
687 }
688 
689 static int gssx_enc_cb(struct xdr_stream *xdr, struct gssx_cb *cb)
690 {
691 	__be32 *p;
692 	int err;
693 
694 	/* cb->initiator_addrtype */
695 	p = xdr_reserve_space(xdr, 8);
696 	if (!p)
697 		return -ENOSPC;
698 	p = xdr_encode_hyper(p, cb->initiator_addrtype);
699 
700 	/* cb->initiator_address */
701 	err = gssx_enc_buffer(xdr, &cb->initiator_address);
702 	if (err)
703 		return err;
704 
705 	/* cb->acceptor_addrtype */
706 	p = xdr_reserve_space(xdr, 8);
707 	if (!p)
708 		return -ENOSPC;
709 	p = xdr_encode_hyper(p, cb->acceptor_addrtype);
710 
711 	/* cb->acceptor_address */
712 	err = gssx_enc_buffer(xdr, &cb->acceptor_address);
713 	if (err)
714 		return err;
715 
716 	/* cb->application_data */
717 	err = gssx_enc_buffer(xdr, &cb->application_data);
718 
719 	return err;
720 }
721 
722 void gssx_enc_accept_sec_context(struct rpc_rqst *req,
723 				 struct xdr_stream *xdr,
724 				 const void *data)
725 {
726 	const struct gssx_arg_accept_sec_context *arg = data;
727 	int err;
728 
729 	err = gssx_enc_call_ctx(xdr, &arg->call_ctx);
730 	if (err)
731 		goto done;
732 
733 	/* arg->context_handle */
734 	if (arg->context_handle)
735 		err = gssx_enc_ctx(xdr, arg->context_handle);
736 	else
737 		err = gssx_enc_bool(xdr, 0);
738 	if (err)
739 		goto done;
740 
741 	/* arg->cred_handle */
742 	if (arg->cred_handle)
743 		err = gssx_enc_cred(xdr, arg->cred_handle);
744 	else
745 		err = gssx_enc_bool(xdr, 0);
746 	if (err)
747 		goto done;
748 
749 	/* arg->input_token */
750 	err = gssx_enc_in_token(xdr, &arg->input_token);
751 	if (err)
752 		goto done;
753 
754 	/* arg->input_cb */
755 	if (arg->input_cb)
756 		err = gssx_enc_cb(xdr, arg->input_cb);
757 	else
758 		err = gssx_enc_bool(xdr, 0);
759 	if (err)
760 		goto done;
761 
762 	err = gssx_enc_bool(xdr, arg->ret_deleg_cred);
763 	if (err)
764 		goto done;
765 
766 	/* leave options empty for now, will add once we have any options
767 	 * to pass up at all */
768 	/* arg->options */
769 	err = dummy_enc_opt_array(xdr, &arg->options);
770 
771 	xdr_inline_pages(&req->rq_rcv_buf,
772 		PAGE_SIZE/2 /* pretty arbitrary */,
773 		arg->pages, 0 /* page base */, arg->npages * PAGE_SIZE);
774 done:
775 	if (err)
776 		dprintk("RPC:       gssx_enc_accept_sec_context: %d\n", err);
777 }
778 
779 int gssx_dec_accept_sec_context(struct rpc_rqst *rqstp,
780 				struct xdr_stream *xdr,
781 				void *data)
782 {
783 	struct gssx_res_accept_sec_context *res = data;
784 	u32 value_follows;
785 	int err;
786 	struct page *scratch;
787 
788 	scratch = alloc_page(GFP_KERNEL);
789 	if (!scratch)
790 		return -ENOMEM;
791 	xdr_set_scratch_page(xdr, scratch);
792 
793 	/* res->status */
794 	err = gssx_dec_status(xdr, &res->status);
795 	if (err)
796 		goto out_free;
797 
798 	/* res->context_handle */
799 	err = gssx_dec_bool(xdr, &value_follows);
800 	if (err)
801 		goto out_free;
802 	if (value_follows) {
803 		err = gssx_dec_ctx(xdr, res->context_handle);
804 		if (err)
805 			goto out_free;
806 	} else {
807 		res->context_handle = NULL;
808 	}
809 
810 	/* res->output_token */
811 	err = gssx_dec_bool(xdr, &value_follows);
812 	if (err)
813 		goto out_free;
814 	if (value_follows) {
815 		err = gssx_dec_buffer(xdr, res->output_token);
816 		if (err)
817 			goto out_free;
818 	} else {
819 		res->output_token = NULL;
820 	}
821 
822 	/* res->delegated_cred_handle */
823 	err = gssx_dec_bool(xdr, &value_follows);
824 	if (err)
825 		goto out_free;
826 	if (value_follows) {
827 		/* we do not support upcall servers sending this data. */
828 		err = -EINVAL;
829 		goto out_free;
830 	}
831 
832 	/* res->options */
833 	err = gssx_dec_option_array(xdr, &res->options);
834 
835 out_free:
836 	__free_page(scratch);
837 	return err;
838 }
839