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