11d658336SSimo Sorce /*
21d658336SSimo Sorce  * GSS Proxy upcall module
31d658336SSimo Sorce  *
41d658336SSimo Sorce  *  Copyright (C) 2012 Simo Sorce <simo@redhat.com>
51d658336SSimo Sorce  *
61d658336SSimo Sorce  * This program is free software; you can redistribute it and/or modify
71d658336SSimo Sorce  * it under the terms of the GNU General Public License as published by
81d658336SSimo Sorce  * the Free Software Foundation; either version 2 of the License, or
91d658336SSimo Sorce  * (at your option) any later version.
101d658336SSimo Sorce  *
111d658336SSimo Sorce  * This program is distributed in the hope that it will be useful,
121d658336SSimo Sorce  * but WITHOUT ANY WARRANTY; without even the implied warranty of
131d658336SSimo Sorce  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
141d658336SSimo Sorce  * GNU General Public License for more details.
151d658336SSimo Sorce  *
161d658336SSimo Sorce  * You should have received a copy of the GNU General Public License
171d658336SSimo Sorce  * along with this program; if not, write to the Free Software
181d658336SSimo Sorce  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
191d658336SSimo Sorce  */
201d658336SSimo Sorce 
211d658336SSimo Sorce #include <linux/sunrpc/svcauth.h>
221d658336SSimo Sorce #include "gss_rpc_xdr.h"
231d658336SSimo Sorce 
241d658336SSimo Sorce static int gssx_enc_bool(struct xdr_stream *xdr, int v)
251d658336SSimo Sorce {
261d658336SSimo Sorce 	__be32 *p;
271d658336SSimo Sorce 
281d658336SSimo Sorce 	p = xdr_reserve_space(xdr, 4);
291d658336SSimo Sorce 	if (unlikely(p == NULL))
301d658336SSimo Sorce 		return -ENOSPC;
311d658336SSimo Sorce 	*p = v ? xdr_one : xdr_zero;
321d658336SSimo Sorce 	return 0;
331d658336SSimo Sorce }
341d658336SSimo Sorce 
351d658336SSimo Sorce static int gssx_dec_bool(struct xdr_stream *xdr, u32 *v)
361d658336SSimo Sorce {
371d658336SSimo Sorce 	__be32 *p;
381d658336SSimo Sorce 
391d658336SSimo Sorce 	p = xdr_inline_decode(xdr, 4);
401d658336SSimo Sorce 	if (unlikely(p == NULL))
411d658336SSimo Sorce 		return -ENOSPC;
421d658336SSimo Sorce 	*v = be32_to_cpu(*p);
431d658336SSimo Sorce 	return 0;
441d658336SSimo Sorce }
451d658336SSimo Sorce 
461d658336SSimo Sorce static int gssx_enc_buffer(struct xdr_stream *xdr,
4789daf360SChristoph Hellwig 			   const gssx_buffer *buf)
481d658336SSimo Sorce {
491d658336SSimo Sorce 	__be32 *p;
501d658336SSimo Sorce 
511d658336SSimo Sorce 	p = xdr_reserve_space(xdr, sizeof(u32) + buf->len);
521d658336SSimo Sorce 	if (!p)
531d658336SSimo Sorce 		return -ENOSPC;
541d658336SSimo Sorce 	xdr_encode_opaque(p, buf->data, buf->len);
551d658336SSimo Sorce 	return 0;
561d658336SSimo Sorce }
571d658336SSimo Sorce 
581d658336SSimo Sorce static int gssx_enc_in_token(struct xdr_stream *xdr,
5989daf360SChristoph Hellwig 			     const struct gssp_in_token *in)
601d658336SSimo Sorce {
611d658336SSimo Sorce 	__be32 *p;
621d658336SSimo Sorce 
631d658336SSimo Sorce 	p = xdr_reserve_space(xdr, 4);
641d658336SSimo Sorce 	if (!p)
651d658336SSimo Sorce 		return -ENOSPC;
661d658336SSimo Sorce 	*p = cpu_to_be32(in->page_len);
671d658336SSimo Sorce 
681d658336SSimo Sorce 	/* all we need to do is to write pages */
691d658336SSimo Sorce 	xdr_write_pages(xdr, in->pages, in->page_base, in->page_len);
701d658336SSimo Sorce 
711d658336SSimo Sorce 	return 0;
721d658336SSimo Sorce }
731d658336SSimo Sorce 
741d658336SSimo Sorce 
751d658336SSimo Sorce static int gssx_dec_buffer(struct xdr_stream *xdr,
761d658336SSimo Sorce 			   gssx_buffer *buf)
771d658336SSimo Sorce {
781d658336SSimo Sorce 	u32 length;
791d658336SSimo Sorce 	__be32 *p;
801d658336SSimo Sorce 
811d658336SSimo Sorce 	p = xdr_inline_decode(xdr, 4);
821d658336SSimo Sorce 	if (unlikely(p == NULL))
831d658336SSimo Sorce 		return -ENOSPC;
841d658336SSimo Sorce 
851d658336SSimo Sorce 	length = be32_to_cpup(p);
861d658336SSimo Sorce 	p = xdr_inline_decode(xdr, length);
871d658336SSimo Sorce 	if (unlikely(p == NULL))
881d658336SSimo Sorce 		return -ENOSPC;
891d658336SSimo Sorce 
901d658336SSimo Sorce 	if (buf->len == 0) {
911d658336SSimo Sorce 		/* we intentionally are not interested in this buffer */
921d658336SSimo Sorce 		return 0;
931d658336SSimo Sorce 	}
941d658336SSimo Sorce 	if (length > buf->len)
951d658336SSimo Sorce 		return -ENOSPC;
961d658336SSimo Sorce 
971d658336SSimo Sorce 	if (!buf->data) {
981d658336SSimo Sorce 		buf->data = kmemdup(p, length, GFP_KERNEL);
991d658336SSimo Sorce 		if (!buf->data)
1001d658336SSimo Sorce 			return -ENOMEM;
1011d658336SSimo Sorce 	} else {
1021d658336SSimo Sorce 		memcpy(buf->data, p, length);
1031d658336SSimo Sorce 	}
1041d658336SSimo Sorce 	buf->len = length;
1051d658336SSimo Sorce 	return 0;
1061d658336SSimo Sorce }
1071d658336SSimo Sorce 
1081d658336SSimo Sorce static int gssx_enc_option(struct xdr_stream *xdr,
1091d658336SSimo Sorce 			   struct gssx_option *opt)
1101d658336SSimo Sorce {
1111d658336SSimo Sorce 	int err;
1121d658336SSimo Sorce 
1131d658336SSimo Sorce 	err = gssx_enc_buffer(xdr, &opt->option);
1141d658336SSimo Sorce 	if (err)
1151d658336SSimo Sorce 		return err;
1161d658336SSimo Sorce 	err = gssx_enc_buffer(xdr, &opt->value);
1171d658336SSimo Sorce 	return err;
1181d658336SSimo Sorce }
1191d658336SSimo Sorce 
1201d658336SSimo Sorce static int gssx_dec_option(struct xdr_stream *xdr,
1211d658336SSimo Sorce 			   struct gssx_option *opt)
1221d658336SSimo Sorce {
1231d658336SSimo Sorce 	int err;
1241d658336SSimo Sorce 
1251d658336SSimo Sorce 	err = gssx_dec_buffer(xdr, &opt->option);
1261d658336SSimo Sorce 	if (err)
1271d658336SSimo Sorce 		return err;
1281d658336SSimo Sorce 	err = gssx_dec_buffer(xdr, &opt->value);
1291d658336SSimo Sorce 	return err;
1301d658336SSimo Sorce }
1311d658336SSimo Sorce 
1321d658336SSimo Sorce static int dummy_enc_opt_array(struct xdr_stream *xdr,
13389daf360SChristoph Hellwig 				const struct gssx_option_array *oa)
1341d658336SSimo Sorce {
1351d658336SSimo Sorce 	__be32 *p;
1361d658336SSimo Sorce 
1371d658336SSimo Sorce 	if (oa->count != 0)
1381d658336SSimo Sorce 		return -EINVAL;
1391d658336SSimo Sorce 
1401d658336SSimo Sorce 	p = xdr_reserve_space(xdr, 4);
1411d658336SSimo Sorce 	if (!p)
1421d658336SSimo Sorce 		return -ENOSPC;
1431d658336SSimo Sorce 	*p = 0;
1441d658336SSimo Sorce 
1451d658336SSimo Sorce 	return 0;
1461d658336SSimo Sorce }
1471d658336SSimo Sorce 
1481d658336SSimo Sorce static int dummy_dec_opt_array(struct xdr_stream *xdr,
1491d658336SSimo Sorce 				struct gssx_option_array *oa)
1501d658336SSimo Sorce {
1511d658336SSimo Sorce 	struct gssx_option dummy;
1521d658336SSimo Sorce 	u32 count, i;
1531d658336SSimo Sorce 	__be32 *p;
1541d658336SSimo Sorce 
1551d658336SSimo Sorce 	p = xdr_inline_decode(xdr, 4);
1561d658336SSimo Sorce 	if (unlikely(p == NULL))
1571d658336SSimo Sorce 		return -ENOSPC;
1581d658336SSimo Sorce 	count = be32_to_cpup(p++);
1591d658336SSimo Sorce 	memset(&dummy, 0, sizeof(dummy));
1601d658336SSimo Sorce 	for (i = 0; i < count; i++) {
1611d658336SSimo Sorce 		gssx_dec_option(xdr, &dummy);
1621d658336SSimo Sorce 	}
1631d658336SSimo Sorce 
1641d658336SSimo Sorce 	oa->count = 0;
1651d658336SSimo Sorce 	oa->data = NULL;
1661d658336SSimo Sorce 	return 0;
1671d658336SSimo Sorce }
1681d658336SSimo Sorce 
1696a36978eSJ. Bruce Fields static int get_host_u32(struct xdr_stream *xdr, u32 *res)
1701d658336SSimo Sorce {
171778e512bSJ. Bruce Fields 	__be32 *p;
172778e512bSJ. Bruce Fields 
173778e512bSJ. Bruce Fields 	p = xdr_inline_decode(xdr, 4);
174778e512bSJ. Bruce Fields 	if (!p)
1751d658336SSimo Sorce 		return -EINVAL;
1766a36978eSJ. Bruce Fields 	/* Contents of linux creds are all host-endian: */
1776a36978eSJ. Bruce Fields 	memcpy(res, p, sizeof(u32));
1781d658336SSimo Sorce 	return 0;
1791d658336SSimo Sorce }
1801d658336SSimo Sorce 
1811d658336SSimo Sorce static int gssx_dec_linux_creds(struct xdr_stream *xdr,
1821d658336SSimo Sorce 				struct svc_cred *creds)
1831d658336SSimo Sorce {
1841d658336SSimo Sorce 	u32 length;
1851d658336SSimo Sorce 	__be32 *p;
1866a36978eSJ. Bruce Fields 	u32 tmp;
1876a36978eSJ. Bruce Fields 	u32 N;
1886a36978eSJ. Bruce Fields 	int i, err;
1891d658336SSimo Sorce 
1901d658336SSimo Sorce 	p = xdr_inline_decode(xdr, 4);
1911d658336SSimo Sorce 	if (unlikely(p == NULL))
1921d658336SSimo Sorce 		return -ENOSPC;
1931d658336SSimo Sorce 
1941d658336SSimo Sorce 	length = be32_to_cpup(p);
1951d658336SSimo Sorce 
196778e512bSJ. Bruce Fields 	if (length > (3 + NGROUPS_MAX) * sizeof(u32))
1971d658336SSimo Sorce 		return -ENOSPC;
1981d658336SSimo Sorce 
1991d658336SSimo Sorce 	/* uid */
2006a36978eSJ. Bruce Fields 	err = get_host_u32(xdr, &tmp);
2011d658336SSimo Sorce 	if (err)
2021d658336SSimo Sorce 		return err;
203d28fcc83SJ. Bruce Fields 	creds->cr_uid = make_kuid(&init_user_ns, tmp);
2041d658336SSimo Sorce 
2051d658336SSimo Sorce 	/* gid */
2066a36978eSJ. Bruce Fields 	err = get_host_u32(xdr, &tmp);
2071d658336SSimo Sorce 	if (err)
2081d658336SSimo Sorce 		return err;
209d28fcc83SJ. Bruce Fields 	creds->cr_gid = make_kgid(&init_user_ns, tmp);
2101d658336SSimo Sorce 
2111d658336SSimo Sorce 	/* number of additional gid's */
2126a36978eSJ. Bruce Fields 	err = get_host_u32(xdr, &tmp);
2131d658336SSimo Sorce 	if (err)
2141d658336SSimo Sorce 		return err;
2151d658336SSimo Sorce 	N = tmp;
216778e512bSJ. Bruce Fields 	if ((3 + N) * sizeof(u32) != length)
217778e512bSJ. Bruce Fields 		return -EINVAL;
2181d658336SSimo Sorce 	creds->cr_group_info = groups_alloc(N);
2191d658336SSimo Sorce 	if (creds->cr_group_info == NULL)
2201d658336SSimo Sorce 		return -ENOMEM;
2211d658336SSimo Sorce 
2221d658336SSimo Sorce 	/* gid's */
2231d658336SSimo Sorce 	for (i = 0; i < N; i++) {
224d28fcc83SJ. Bruce Fields 		kgid_t kgid;
2256a36978eSJ. Bruce Fields 		err = get_host_u32(xdr, &tmp);
226d28fcc83SJ. Bruce Fields 		if (err)
227d28fcc83SJ. Bruce Fields 			goto out_free_groups;
228d28fcc83SJ. Bruce Fields 		err = -EINVAL;
229d28fcc83SJ. Bruce Fields 		kgid = make_kgid(&init_user_ns, tmp);
230d28fcc83SJ. Bruce Fields 		if (!gid_valid(kgid))
231d28fcc83SJ. Bruce Fields 			goto out_free_groups;
23281243eacSAlexey Dobriyan 		creds->cr_group_info->gid[i] = kgid;
2331d658336SSimo Sorce 	}
234bdcf0a42SThiago Rafael Becker 	groups_sort(creds->cr_group_info);
2351d658336SSimo Sorce 
2361d658336SSimo Sorce 	return 0;
237d28fcc83SJ. Bruce Fields out_free_groups:
238d28fcc83SJ. Bruce Fields 	groups_free(creds->cr_group_info);
239d28fcc83SJ. Bruce Fields 	return err;
2401d658336SSimo Sorce }
2411d658336SSimo Sorce 
2421d658336SSimo Sorce static int gssx_dec_option_array(struct xdr_stream *xdr,
2431d658336SSimo Sorce 				 struct gssx_option_array *oa)
2441d658336SSimo Sorce {
2451d658336SSimo Sorce 	struct svc_cred *creds;
2461d658336SSimo Sorce 	u32 count, i;
2471d658336SSimo Sorce 	__be32 *p;
2481d658336SSimo Sorce 	int err;
2491d658336SSimo Sorce 
2501d658336SSimo Sorce 	p = xdr_inline_decode(xdr, 4);
2511d658336SSimo Sorce 	if (unlikely(p == NULL))
2521d658336SSimo Sorce 		return -ENOSPC;
2531d658336SSimo Sorce 	count = be32_to_cpup(p++);
2549fd40c5aSGeert Uytterhoeven 	if (!count)
2559fd40c5aSGeert Uytterhoeven 		return 0;
2569fd40c5aSGeert Uytterhoeven 
2571d658336SSimo Sorce 	/* we recognize only 1 currently: CREDS_VALUE */
2581d658336SSimo Sorce 	oa->count = 1;
2591d658336SSimo Sorce 
2601d658336SSimo Sorce 	oa->data = kmalloc(sizeof(struct gssx_option), GFP_KERNEL);
2611d658336SSimo Sorce 	if (!oa->data)
2621d658336SSimo Sorce 		return -ENOMEM;
2631d658336SSimo Sorce 
264034dd34fSJ. Bruce Fields 	creds = kzalloc(sizeof(struct svc_cred), GFP_KERNEL);
2651d658336SSimo Sorce 	if (!creds) {
2661d658336SSimo Sorce 		kfree(oa->data);
2671d658336SSimo Sorce 		return -ENOMEM;
2681d658336SSimo Sorce 	}
2691d658336SSimo Sorce 
2701d658336SSimo Sorce 	oa->data[0].option.data = CREDS_VALUE;
2711d658336SSimo Sorce 	oa->data[0].option.len = sizeof(CREDS_VALUE);
2721d658336SSimo Sorce 	oa->data[0].value.data = (void *)creds;
2731d658336SSimo Sorce 	oa->data[0].value.len = 0;
2749fd40c5aSGeert Uytterhoeven 
2751d658336SSimo Sorce 	for (i = 0; i < count; i++) {
2761d658336SSimo Sorce 		gssx_buffer dummy = { 0, NULL };
2771d658336SSimo Sorce 		u32 length;
2781d658336SSimo Sorce 
2791d658336SSimo Sorce 		/* option buffer */
2801d658336SSimo Sorce 		p = xdr_inline_decode(xdr, 4);
2811d658336SSimo Sorce 		if (unlikely(p == NULL))
2821d658336SSimo Sorce 			return -ENOSPC;
2831d658336SSimo Sorce 
2841d658336SSimo Sorce 		length = be32_to_cpup(p);
2851d658336SSimo Sorce 		p = xdr_inline_decode(xdr, length);
2861d658336SSimo Sorce 		if (unlikely(p == NULL))
2871d658336SSimo Sorce 			return -ENOSPC;
2881d658336SSimo Sorce 
2891d658336SSimo Sorce 		if (length == sizeof(CREDS_VALUE) &&
2901d658336SSimo Sorce 		    memcmp(p, CREDS_VALUE, sizeof(CREDS_VALUE)) == 0) {
2911d658336SSimo Sorce 			/* We have creds here. parse them */
2921d658336SSimo Sorce 			err = gssx_dec_linux_creds(xdr, creds);
2931d658336SSimo Sorce 			if (err)
2941d658336SSimo Sorce 				return err;
2951d658336SSimo Sorce 			oa->data[0].value.len = 1; /* presence */
2961d658336SSimo Sorce 		} else {
2971d658336SSimo Sorce 			/* consume uninteresting buffer */
2981d658336SSimo Sorce 			err = gssx_dec_buffer(xdr, &dummy);
2991d658336SSimo Sorce 			if (err)
3001d658336SSimo Sorce 				return err;
3011d658336SSimo Sorce 		}
3021d658336SSimo Sorce 	}
3031d658336SSimo Sorce 	return 0;
3041d658336SSimo Sorce }
3051d658336SSimo Sorce 
3061d658336SSimo Sorce static int gssx_dec_status(struct xdr_stream *xdr,
3071d658336SSimo Sorce 			   struct gssx_status *status)
3081d658336SSimo Sorce {
3091d658336SSimo Sorce 	__be32 *p;
3101d658336SSimo Sorce 	int err;
3111d658336SSimo Sorce 
3121d658336SSimo Sorce 	/* status->major_status */
3131d658336SSimo Sorce 	p = xdr_inline_decode(xdr, 8);
3141d658336SSimo Sorce 	if (unlikely(p == NULL))
3151d658336SSimo Sorce 		return -ENOSPC;
3161d658336SSimo Sorce 	p = xdr_decode_hyper(p, &status->major_status);
3171d658336SSimo Sorce 
3181d658336SSimo Sorce 	/* status->mech */
3191d658336SSimo Sorce 	err = gssx_dec_buffer(xdr, &status->mech);
3201d658336SSimo Sorce 	if (err)
3211d658336SSimo Sorce 		return err;
3221d658336SSimo Sorce 
3231d658336SSimo Sorce 	/* status->minor_status */
3241d658336SSimo Sorce 	p = xdr_inline_decode(xdr, 8);
3251d658336SSimo Sorce 	if (unlikely(p == NULL))
3261d658336SSimo Sorce 		return -ENOSPC;
3271d658336SSimo Sorce 	p = xdr_decode_hyper(p, &status->minor_status);
3281d658336SSimo Sorce 
3291d658336SSimo Sorce 	/* status->major_status_string */
3301d658336SSimo Sorce 	err = gssx_dec_buffer(xdr, &status->major_status_string);
3311d658336SSimo Sorce 	if (err)
3321d658336SSimo Sorce 		return err;
3331d658336SSimo Sorce 
3341d658336SSimo Sorce 	/* status->minor_status_string */
3351d658336SSimo Sorce 	err = gssx_dec_buffer(xdr, &status->minor_status_string);
3361d658336SSimo Sorce 	if (err)
3371d658336SSimo Sorce 		return err;
3381d658336SSimo Sorce 
3391d658336SSimo Sorce 	/* status->server_ctx */
3401d658336SSimo Sorce 	err = gssx_dec_buffer(xdr, &status->server_ctx);
3411d658336SSimo Sorce 	if (err)
3421d658336SSimo Sorce 		return err;
3431d658336SSimo Sorce 
3441d658336SSimo Sorce 	/* we assume we have no options for now, so simply consume them */
3451d658336SSimo Sorce 	/* status->options */
3461d658336SSimo Sorce 	err = dummy_dec_opt_array(xdr, &status->options);
3471d658336SSimo Sorce 
3481d658336SSimo Sorce 	return err;
3491d658336SSimo Sorce }
3501d658336SSimo Sorce 
3511d658336SSimo Sorce static int gssx_enc_call_ctx(struct xdr_stream *xdr,
35289daf360SChristoph Hellwig 			     const struct gssx_call_ctx *ctx)
3531d658336SSimo Sorce {
3541d658336SSimo Sorce 	struct gssx_option opt;
3551d658336SSimo Sorce 	__be32 *p;
3561d658336SSimo Sorce 	int err;
3571d658336SSimo Sorce 
3581d658336SSimo Sorce 	/* ctx->locale */
3591d658336SSimo Sorce 	err = gssx_enc_buffer(xdr, &ctx->locale);
3601d658336SSimo Sorce 	if (err)
3611d658336SSimo Sorce 		return err;
3621d658336SSimo Sorce 
3631d658336SSimo Sorce 	/* ctx->server_ctx */
3641d658336SSimo Sorce 	err = gssx_enc_buffer(xdr, &ctx->server_ctx);
3651d658336SSimo Sorce 	if (err)
3661d658336SSimo Sorce 		return err;
3671d658336SSimo Sorce 
3681d658336SSimo Sorce 	/* we always want to ask for lucid contexts */
3691d658336SSimo Sorce 	/* ctx->options */
3701d658336SSimo Sorce 	p = xdr_reserve_space(xdr, 4);
3711d658336SSimo Sorce 	*p = cpu_to_be32(2);
3721d658336SSimo Sorce 
3731d658336SSimo Sorce 	/* we want a lucid_v1 context */
3741d658336SSimo Sorce 	opt.option.data = LUCID_OPTION;
3751d658336SSimo Sorce 	opt.option.len = sizeof(LUCID_OPTION);
3761d658336SSimo Sorce 	opt.value.data = LUCID_VALUE;
3771d658336SSimo Sorce 	opt.value.len = sizeof(LUCID_VALUE);
3781d658336SSimo Sorce 	err = gssx_enc_option(xdr, &opt);
3791d658336SSimo Sorce 
3801d658336SSimo Sorce 	/* ..and user creds */
3811d658336SSimo Sorce 	opt.option.data = CREDS_OPTION;
3821d658336SSimo Sorce 	opt.option.len = sizeof(CREDS_OPTION);
3831d658336SSimo Sorce 	opt.value.data = CREDS_VALUE;
3841d658336SSimo Sorce 	opt.value.len = sizeof(CREDS_VALUE);
3851d658336SSimo Sorce 	err = gssx_enc_option(xdr, &opt);
3861d658336SSimo Sorce 
3871d658336SSimo Sorce 	return err;
3881d658336SSimo Sorce }
3891d658336SSimo Sorce 
3901d658336SSimo Sorce static int gssx_dec_name_attr(struct xdr_stream *xdr,
3911d658336SSimo Sorce 			     struct gssx_name_attr *attr)
3921d658336SSimo Sorce {
3931d658336SSimo Sorce 	int err;
3941d658336SSimo Sorce 
3951d658336SSimo Sorce 	/* attr->attr */
3961d658336SSimo Sorce 	err = gssx_dec_buffer(xdr, &attr->attr);
3971d658336SSimo Sorce 	if (err)
3981d658336SSimo Sorce 		return err;
3991d658336SSimo Sorce 
4001d658336SSimo Sorce 	/* attr->value */
4011d658336SSimo Sorce 	err = gssx_dec_buffer(xdr, &attr->value);
4021d658336SSimo Sorce 	if (err)
4031d658336SSimo Sorce 		return err;
4041d658336SSimo Sorce 
4051d658336SSimo Sorce 	/* attr->extensions */
4061d658336SSimo Sorce 	err = dummy_dec_opt_array(xdr, &attr->extensions);
4071d658336SSimo Sorce 
4081d658336SSimo Sorce 	return err;
4091d658336SSimo Sorce }
4101d658336SSimo Sorce 
4111d658336SSimo Sorce static int dummy_enc_nameattr_array(struct xdr_stream *xdr,
4121d658336SSimo Sorce 				    struct gssx_name_attr_array *naa)
4131d658336SSimo Sorce {
4141d658336SSimo Sorce 	__be32 *p;
4151d658336SSimo Sorce 
4161d658336SSimo Sorce 	if (naa->count != 0)
4171d658336SSimo Sorce 		return -EINVAL;
4181d658336SSimo Sorce 
4191d658336SSimo Sorce 	p = xdr_reserve_space(xdr, 4);
4201d658336SSimo Sorce 	if (!p)
4211d658336SSimo Sorce 		return -ENOSPC;
4221d658336SSimo Sorce 	*p = 0;
4231d658336SSimo Sorce 
4241d658336SSimo Sorce 	return 0;
4251d658336SSimo Sorce }
4261d658336SSimo Sorce 
4271d658336SSimo Sorce static int dummy_dec_nameattr_array(struct xdr_stream *xdr,
4281d658336SSimo Sorce 				    struct gssx_name_attr_array *naa)
4291d658336SSimo Sorce {
430dc43376cSJ. Bruce Fields 	struct gssx_name_attr dummy = { .attr = {.len = 0} };
4311d658336SSimo Sorce 	u32 count, i;
4321d658336SSimo Sorce 	__be32 *p;
4331d658336SSimo Sorce 
4341d658336SSimo Sorce 	p = xdr_inline_decode(xdr, 4);
4351d658336SSimo Sorce 	if (unlikely(p == NULL))
4361d658336SSimo Sorce 		return -ENOSPC;
4371d658336SSimo Sorce 	count = be32_to_cpup(p++);
4381d658336SSimo Sorce 	for (i = 0; i < count; i++) {
4391d658336SSimo Sorce 		gssx_dec_name_attr(xdr, &dummy);
4401d658336SSimo Sorce 	}
4411d658336SSimo Sorce 
4421d658336SSimo Sorce 	naa->count = 0;
4431d658336SSimo Sorce 	naa->data = NULL;
4441d658336SSimo Sorce 	return 0;
4451d658336SSimo Sorce }
4461d658336SSimo Sorce 
4471d658336SSimo Sorce static struct xdr_netobj zero_netobj = {};
4481d658336SSimo Sorce 
4491d658336SSimo Sorce static struct gssx_name_attr_array zero_name_attr_array = {};
4501d658336SSimo Sorce 
4511d658336SSimo Sorce static struct gssx_option_array zero_option_array = {};
4521d658336SSimo Sorce 
4531d658336SSimo Sorce static int gssx_enc_name(struct xdr_stream *xdr,
4541d658336SSimo Sorce 			 struct gssx_name *name)
4551d658336SSimo Sorce {
4561d658336SSimo Sorce 	int err;
4571d658336SSimo Sorce 
4581d658336SSimo Sorce 	/* name->display_name */
4591d658336SSimo Sorce 	err = gssx_enc_buffer(xdr, &name->display_name);
4601d658336SSimo Sorce 	if (err)
4611d658336SSimo Sorce 		return err;
4621d658336SSimo Sorce 
4631d658336SSimo Sorce 	/* name->name_type */
4641d658336SSimo Sorce 	err = gssx_enc_buffer(xdr, &zero_netobj);
4651d658336SSimo Sorce 	if (err)
4661d658336SSimo Sorce 		return err;
4671d658336SSimo Sorce 
4681d658336SSimo Sorce 	/* name->exported_name */
4691d658336SSimo Sorce 	err = gssx_enc_buffer(xdr, &zero_netobj);
4701d658336SSimo Sorce 	if (err)
4711d658336SSimo Sorce 		return err;
4721d658336SSimo Sorce 
4731d658336SSimo Sorce 	/* name->exported_composite_name */
4741d658336SSimo Sorce 	err = gssx_enc_buffer(xdr, &zero_netobj);
4751d658336SSimo Sorce 	if (err)
4761d658336SSimo Sorce 		return err;
4771d658336SSimo Sorce 
4781d658336SSimo Sorce 	/* leave name_attributes empty for now, will add once we have any
4791d658336SSimo Sorce 	 * to pass up at all */
4801d658336SSimo Sorce 	/* name->name_attributes */
4811d658336SSimo Sorce 	err = dummy_enc_nameattr_array(xdr, &zero_name_attr_array);
4821d658336SSimo Sorce 	if (err)
4831d658336SSimo Sorce 		return err;
4841d658336SSimo Sorce 
4851d658336SSimo Sorce 	/* leave options empty for now, will add once we have any options
4861d658336SSimo Sorce 	 * to pass up at all */
4871d658336SSimo Sorce 	/* name->extensions */
4881d658336SSimo Sorce 	err = dummy_enc_opt_array(xdr, &zero_option_array);
4891d658336SSimo Sorce 
4901d658336SSimo Sorce 	return err;
4911d658336SSimo Sorce }
4921d658336SSimo Sorce 
493dc43376cSJ. Bruce Fields 
4941d658336SSimo Sorce static int gssx_dec_name(struct xdr_stream *xdr,
4951d658336SSimo Sorce 			 struct gssx_name *name)
4961d658336SSimo Sorce {
497dc43376cSJ. Bruce Fields 	struct xdr_netobj dummy_netobj = { .len = 0 };
498dc43376cSJ. Bruce Fields 	struct gssx_name_attr_array dummy_name_attr_array = { .count = 0 };
499dc43376cSJ. Bruce Fields 	struct gssx_option_array dummy_option_array = { .count = 0 };
5001d658336SSimo Sorce 	int err;
5011d658336SSimo Sorce 
5021d658336SSimo Sorce 	/* name->display_name */
5031d658336SSimo Sorce 	err = gssx_dec_buffer(xdr, &name->display_name);
5041d658336SSimo Sorce 	if (err)
5051d658336SSimo Sorce 		return err;
5061d658336SSimo Sorce 
5071d658336SSimo Sorce 	/* name->name_type */
5081d658336SSimo Sorce 	err = gssx_dec_buffer(xdr, &dummy_netobj);
5091d658336SSimo Sorce 	if (err)
5101d658336SSimo Sorce 		return err;
5111d658336SSimo Sorce 
5121d658336SSimo Sorce 	/* name->exported_name */
5131d658336SSimo Sorce 	err = gssx_dec_buffer(xdr, &dummy_netobj);
5141d658336SSimo Sorce 	if (err)
5151d658336SSimo Sorce 		return err;
5161d658336SSimo Sorce 
5171d658336SSimo Sorce 	/* name->exported_composite_name */
5181d658336SSimo Sorce 	err = gssx_dec_buffer(xdr, &dummy_netobj);
5191d658336SSimo Sorce 	if (err)
5201d658336SSimo Sorce 		return err;
5211d658336SSimo Sorce 
5221d658336SSimo Sorce 	/* we assume we have no attributes for now, so simply consume them */
5231d658336SSimo Sorce 	/* name->name_attributes */
5241d658336SSimo Sorce 	err = dummy_dec_nameattr_array(xdr, &dummy_name_attr_array);
5251d658336SSimo Sorce 	if (err)
5261d658336SSimo Sorce 		return err;
5271d658336SSimo Sorce 
5281d658336SSimo Sorce 	/* we assume we have no options for now, so simply consume them */
5291d658336SSimo Sorce 	/* name->extensions */
5301d658336SSimo Sorce 	err = dummy_dec_opt_array(xdr, &dummy_option_array);
5311d658336SSimo Sorce 
5321d658336SSimo Sorce 	return err;
5331d658336SSimo Sorce }
5341d658336SSimo Sorce 
5351d658336SSimo Sorce static int dummy_enc_credel_array(struct xdr_stream *xdr,
5361d658336SSimo Sorce 				  struct gssx_cred_element_array *cea)
5371d658336SSimo Sorce {
5381d658336SSimo Sorce 	__be32 *p;
5391d658336SSimo Sorce 
5401d658336SSimo Sorce 	if (cea->count != 0)
5411d658336SSimo Sorce 		return -EINVAL;
5421d658336SSimo Sorce 
5431d658336SSimo Sorce 	p = xdr_reserve_space(xdr, 4);
5441d658336SSimo Sorce 	if (!p)
5451d658336SSimo Sorce 		return -ENOSPC;
5461d658336SSimo Sorce 	*p = 0;
5471d658336SSimo Sorce 
5481d658336SSimo Sorce 	return 0;
5491d658336SSimo Sorce }
5501d658336SSimo Sorce 
5511d658336SSimo Sorce static int gssx_enc_cred(struct xdr_stream *xdr,
5521d658336SSimo Sorce 			 struct gssx_cred *cred)
5531d658336SSimo Sorce {
5541d658336SSimo Sorce 	int err;
5551d658336SSimo Sorce 
5561d658336SSimo Sorce 	/* cred->desired_name */
5571d658336SSimo Sorce 	err = gssx_enc_name(xdr, &cred->desired_name);
5581d658336SSimo Sorce 	if (err)
5591d658336SSimo Sorce 		return err;
5601d658336SSimo Sorce 
5611d658336SSimo Sorce 	/* cred->elements */
5621d658336SSimo Sorce 	err = dummy_enc_credel_array(xdr, &cred->elements);
563b26ec9b1SJ. Bruce Fields 	if (err)
564b26ec9b1SJ. Bruce Fields 		return err;
5651d658336SSimo Sorce 
5661d658336SSimo Sorce 	/* cred->cred_handle_reference */
5671d658336SSimo Sorce 	err = gssx_enc_buffer(xdr, &cred->cred_handle_reference);
5681d658336SSimo Sorce 	if (err)
5691d658336SSimo Sorce 		return err;
5701d658336SSimo Sorce 
5711d658336SSimo Sorce 	/* cred->needs_release */
5721d658336SSimo Sorce 	err = gssx_enc_bool(xdr, cred->needs_release);
5731d658336SSimo Sorce 
5741d658336SSimo Sorce 	return err;
5751d658336SSimo Sorce }
5761d658336SSimo Sorce 
5771d658336SSimo Sorce static int gssx_enc_ctx(struct xdr_stream *xdr,
5781d658336SSimo Sorce 			struct gssx_ctx *ctx)
5791d658336SSimo Sorce {
5801d658336SSimo Sorce 	__be32 *p;
5811d658336SSimo Sorce 	int err;
5821d658336SSimo Sorce 
5831d658336SSimo Sorce 	/* ctx->exported_context_token */
5841d658336SSimo Sorce 	err = gssx_enc_buffer(xdr, &ctx->exported_context_token);
5851d658336SSimo Sorce 	if (err)
5861d658336SSimo Sorce 		return err;
5871d658336SSimo Sorce 
5881d658336SSimo Sorce 	/* ctx->state */
5891d658336SSimo Sorce 	err = gssx_enc_buffer(xdr, &ctx->state);
5901d658336SSimo Sorce 	if (err)
5911d658336SSimo Sorce 		return err;
5921d658336SSimo Sorce 
5931d658336SSimo Sorce 	/* ctx->need_release */
5941d658336SSimo Sorce 	err = gssx_enc_bool(xdr, ctx->need_release);
5951d658336SSimo Sorce 	if (err)
5961d658336SSimo Sorce 		return err;
5971d658336SSimo Sorce 
5981d658336SSimo Sorce 	/* ctx->mech */
5991d658336SSimo Sorce 	err = gssx_enc_buffer(xdr, &ctx->mech);
6001d658336SSimo Sorce 	if (err)
6011d658336SSimo Sorce 		return err;
6021d658336SSimo Sorce 
6031d658336SSimo Sorce 	/* ctx->src_name */
6041d658336SSimo Sorce 	err = gssx_enc_name(xdr, &ctx->src_name);
6051d658336SSimo Sorce 	if (err)
6061d658336SSimo Sorce 		return err;
6071d658336SSimo Sorce 
6081d658336SSimo Sorce 	/* ctx->targ_name */
6091d658336SSimo Sorce 	err = gssx_enc_name(xdr, &ctx->targ_name);
6101d658336SSimo Sorce 	if (err)
6111d658336SSimo Sorce 		return err;
6121d658336SSimo Sorce 
6131d658336SSimo Sorce 	/* ctx->lifetime */
6141d658336SSimo Sorce 	p = xdr_reserve_space(xdr, 8+8);
6151d658336SSimo Sorce 	if (!p)
6161d658336SSimo Sorce 		return -ENOSPC;
6171d658336SSimo Sorce 	p = xdr_encode_hyper(p, ctx->lifetime);
6181d658336SSimo Sorce 
6191d658336SSimo Sorce 	/* ctx->ctx_flags */
6201d658336SSimo Sorce 	p = xdr_encode_hyper(p, ctx->ctx_flags);
6211d658336SSimo Sorce 
6221d658336SSimo Sorce 	/* ctx->locally_initiated */
6231d658336SSimo Sorce 	err = gssx_enc_bool(xdr, ctx->locally_initiated);
6241d658336SSimo Sorce 	if (err)
6251d658336SSimo Sorce 		return err;
6261d658336SSimo Sorce 
6271d658336SSimo Sorce 	/* ctx->open */
6281d658336SSimo Sorce 	err = gssx_enc_bool(xdr, ctx->open);
6291d658336SSimo Sorce 	if (err)
6301d658336SSimo Sorce 		return err;
6311d658336SSimo Sorce 
6321d658336SSimo Sorce 	/* leave options empty for now, will add once we have any options
6331d658336SSimo Sorce 	 * to pass up at all */
6341d658336SSimo Sorce 	/* ctx->options */
6351d658336SSimo Sorce 	err = dummy_enc_opt_array(xdr, &ctx->options);
6361d658336SSimo Sorce 
6371d658336SSimo Sorce 	return err;
6381d658336SSimo Sorce }
6391d658336SSimo Sorce 
6401d658336SSimo Sorce static int gssx_dec_ctx(struct xdr_stream *xdr,
6411d658336SSimo Sorce 			struct gssx_ctx *ctx)
6421d658336SSimo Sorce {
6431d658336SSimo Sorce 	__be32 *p;
6441d658336SSimo Sorce 	int err;
6451d658336SSimo Sorce 
6461d658336SSimo Sorce 	/* ctx->exported_context_token */
6471d658336SSimo Sorce 	err = gssx_dec_buffer(xdr, &ctx->exported_context_token);
6481d658336SSimo Sorce 	if (err)
6491d658336SSimo Sorce 		return err;
6501d658336SSimo Sorce 
6511d658336SSimo Sorce 	/* ctx->state */
6521d658336SSimo Sorce 	err = gssx_dec_buffer(xdr, &ctx->state);
6531d658336SSimo Sorce 	if (err)
6541d658336SSimo Sorce 		return err;
6551d658336SSimo Sorce 
6561d658336SSimo Sorce 	/* ctx->need_release */
6571d658336SSimo Sorce 	err = gssx_dec_bool(xdr, &ctx->need_release);
6581d658336SSimo Sorce 	if (err)
6591d658336SSimo Sorce 		return err;
6601d658336SSimo Sorce 
6611d658336SSimo Sorce 	/* ctx->mech */
6621d658336SSimo Sorce 	err = gssx_dec_buffer(xdr, &ctx->mech);
6631d658336SSimo Sorce 	if (err)
6641d658336SSimo Sorce 		return err;
6651d658336SSimo Sorce 
6661d658336SSimo Sorce 	/* ctx->src_name */
6671d658336SSimo Sorce 	err = gssx_dec_name(xdr, &ctx->src_name);
6681d658336SSimo Sorce 	if (err)
6691d658336SSimo Sorce 		return err;
6701d658336SSimo Sorce 
6711d658336SSimo Sorce 	/* ctx->targ_name */
6721d658336SSimo Sorce 	err = gssx_dec_name(xdr, &ctx->targ_name);
6731d658336SSimo Sorce 	if (err)
6741d658336SSimo Sorce 		return err;
6751d658336SSimo Sorce 
6761d658336SSimo Sorce 	/* ctx->lifetime */
6771d658336SSimo Sorce 	p = xdr_inline_decode(xdr, 8+8);
6781d658336SSimo Sorce 	if (unlikely(p == NULL))
6791d658336SSimo Sorce 		return -ENOSPC;
6801d658336SSimo Sorce 	p = xdr_decode_hyper(p, &ctx->lifetime);
6811d658336SSimo Sorce 
6821d658336SSimo Sorce 	/* ctx->ctx_flags */
6831d658336SSimo Sorce 	p = xdr_decode_hyper(p, &ctx->ctx_flags);
6841d658336SSimo Sorce 
6851d658336SSimo Sorce 	/* ctx->locally_initiated */
6861d658336SSimo Sorce 	err = gssx_dec_bool(xdr, &ctx->locally_initiated);
6871d658336SSimo Sorce 	if (err)
6881d658336SSimo Sorce 		return err;
6891d658336SSimo Sorce 
6901d658336SSimo Sorce 	/* ctx->open */
6911d658336SSimo Sorce 	err = gssx_dec_bool(xdr, &ctx->open);
6921d658336SSimo Sorce 	if (err)
6931d658336SSimo Sorce 		return err;
6941d658336SSimo Sorce 
6951d658336SSimo Sorce 	/* we assume we have no options for now, so simply consume them */
6961d658336SSimo Sorce 	/* ctx->options */
6971d658336SSimo Sorce 	err = dummy_dec_opt_array(xdr, &ctx->options);
6981d658336SSimo Sorce 
6991d658336SSimo Sorce 	return err;
7001d658336SSimo Sorce }
7011d658336SSimo Sorce 
7021d658336SSimo Sorce static int gssx_enc_cb(struct xdr_stream *xdr, struct gssx_cb *cb)
7031d658336SSimo Sorce {
7041d658336SSimo Sorce 	__be32 *p;
7051d658336SSimo Sorce 	int err;
7061d658336SSimo Sorce 
7071d658336SSimo Sorce 	/* cb->initiator_addrtype */
7081d658336SSimo Sorce 	p = xdr_reserve_space(xdr, 8);
7091d658336SSimo Sorce 	if (!p)
7101d658336SSimo Sorce 		return -ENOSPC;
7111d658336SSimo Sorce 	p = xdr_encode_hyper(p, cb->initiator_addrtype);
7121d658336SSimo Sorce 
7131d658336SSimo Sorce 	/* cb->initiator_address */
7141d658336SSimo Sorce 	err = gssx_enc_buffer(xdr, &cb->initiator_address);
7151d658336SSimo Sorce 	if (err)
7161d658336SSimo Sorce 		return err;
7171d658336SSimo Sorce 
7181d658336SSimo Sorce 	/* cb->acceptor_addrtype */
7191d658336SSimo Sorce 	p = xdr_reserve_space(xdr, 8);
7201d658336SSimo Sorce 	if (!p)
7211d658336SSimo Sorce 		return -ENOSPC;
7221d658336SSimo Sorce 	p = xdr_encode_hyper(p, cb->acceptor_addrtype);
7231d658336SSimo Sorce 
7241d658336SSimo Sorce 	/* cb->acceptor_address */
7251d658336SSimo Sorce 	err = gssx_enc_buffer(xdr, &cb->acceptor_address);
7261d658336SSimo Sorce 	if (err)
7271d658336SSimo Sorce 		return err;
7281d658336SSimo Sorce 
7291d658336SSimo Sorce 	/* cb->application_data */
7301d658336SSimo Sorce 	err = gssx_enc_buffer(xdr, &cb->application_data);
7311d658336SSimo Sorce 
7321d658336SSimo Sorce 	return err;
7331d658336SSimo Sorce }
7341d658336SSimo Sorce 
7351d658336SSimo Sorce void gssx_enc_accept_sec_context(struct rpc_rqst *req,
7361d658336SSimo Sorce 				 struct xdr_stream *xdr,
73789daf360SChristoph Hellwig 				 const void *data)
7381d658336SSimo Sorce {
73989daf360SChristoph Hellwig 	const struct gssx_arg_accept_sec_context *arg = data;
7401d658336SSimo Sorce 	int err;
7411d658336SSimo Sorce 
7421d658336SSimo Sorce 	err = gssx_enc_call_ctx(xdr, &arg->call_ctx);
7431d658336SSimo Sorce 	if (err)
7441d658336SSimo Sorce 		goto done;
7451d658336SSimo Sorce 
7461d658336SSimo Sorce 	/* arg->context_handle */
747b26ec9b1SJ. Bruce Fields 	if (arg->context_handle)
7481d658336SSimo Sorce 		err = gssx_enc_ctx(xdr, arg->context_handle);
749b26ec9b1SJ. Bruce Fields 	else
750b26ec9b1SJ. Bruce Fields 		err = gssx_enc_bool(xdr, 0);
7511d658336SSimo Sorce 	if (err)
7521d658336SSimo Sorce 		goto done;
7531d658336SSimo Sorce 
7541d658336SSimo Sorce 	/* arg->cred_handle */
755b26ec9b1SJ. Bruce Fields 	if (arg->cred_handle)
7561d658336SSimo Sorce 		err = gssx_enc_cred(xdr, arg->cred_handle);
757b26ec9b1SJ. Bruce Fields 	else
758b26ec9b1SJ. Bruce Fields 		err = gssx_enc_bool(xdr, 0);
7591d658336SSimo Sorce 	if (err)
7601d658336SSimo Sorce 		goto done;
7611d658336SSimo Sorce 
7621d658336SSimo Sorce 	/* arg->input_token */
7631d658336SSimo Sorce 	err = gssx_enc_in_token(xdr, &arg->input_token);
7641d658336SSimo Sorce 	if (err)
7651d658336SSimo Sorce 		goto done;
7661d658336SSimo Sorce 
7671d658336SSimo Sorce 	/* arg->input_cb */
768b26ec9b1SJ. Bruce Fields 	if (arg->input_cb)
7691d658336SSimo Sorce 		err = gssx_enc_cb(xdr, arg->input_cb);
770b26ec9b1SJ. Bruce Fields 	else
771b26ec9b1SJ. Bruce Fields 		err = gssx_enc_bool(xdr, 0);
7721d658336SSimo Sorce 	if (err)
7731d658336SSimo Sorce 		goto done;
7741d658336SSimo Sorce 
7751d658336SSimo Sorce 	err = gssx_enc_bool(xdr, arg->ret_deleg_cred);
7761d658336SSimo Sorce 	if (err)
7771d658336SSimo Sorce 		goto done;
7781d658336SSimo Sorce 
7791d658336SSimo Sorce 	/* leave options empty for now, will add once we have any options
7801d658336SSimo Sorce 	 * to pass up at all */
7811d658336SSimo Sorce 	/* arg->options */
7821d658336SSimo Sorce 	err = dummy_enc_opt_array(xdr, &arg->options);
7831d658336SSimo Sorce 
7849dfd87daSJ. Bruce Fields 	xdr_inline_pages(&req->rq_rcv_buf,
7859dfd87daSJ. Bruce Fields 		PAGE_SIZE/2 /* pretty arbitrary */,
7869dfd87daSJ. Bruce Fields 		arg->pages, 0 /* page base */, arg->npages * PAGE_SIZE);
7871d658336SSimo Sorce done:
7881d658336SSimo Sorce 	if (err)
7891d658336SSimo Sorce 		dprintk("RPC:       gssx_enc_accept_sec_context: %d\n", err);
7901d658336SSimo Sorce }
7911d658336SSimo Sorce 
7921d658336SSimo Sorce int gssx_dec_accept_sec_context(struct rpc_rqst *rqstp,
7931d658336SSimo Sorce 				struct xdr_stream *xdr,
794305c6241SChristoph Hellwig 				void *data)
7951d658336SSimo Sorce {
796305c6241SChristoph Hellwig 	struct gssx_res_accept_sec_context *res = data;
797fb43f11cSJ. Bruce Fields 	u32 value_follows;
7981d658336SSimo Sorce 	int err;
7999507271dSScott Mayhew 	struct page *scratch;
8009507271dSScott Mayhew 
8019507271dSScott Mayhew 	scratch = alloc_page(GFP_KERNEL);
8029507271dSScott Mayhew 	if (!scratch)
8039507271dSScott Mayhew 		return -ENOMEM;
8049507271dSScott Mayhew 	xdr_set_scratch_buffer(xdr, page_address(scratch), PAGE_SIZE);
8051d658336SSimo Sorce 
8061d658336SSimo Sorce 	/* res->status */
8071d658336SSimo Sorce 	err = gssx_dec_status(xdr, &res->status);
8081d658336SSimo Sorce 	if (err)
8099507271dSScott Mayhew 		goto out_free;
8101d658336SSimo Sorce 
8111d658336SSimo Sorce 	/* res->context_handle */
812fb43f11cSJ. Bruce Fields 	err = gssx_dec_bool(xdr, &value_follows);
813fb43f11cSJ. Bruce Fields 	if (err)
8149507271dSScott Mayhew 		goto out_free;
815fb43f11cSJ. Bruce Fields 	if (value_follows) {
8161d658336SSimo Sorce 		err = gssx_dec_ctx(xdr, res->context_handle);
8171d658336SSimo Sorce 		if (err)
8189507271dSScott Mayhew 			goto out_free;
8191d658336SSimo Sorce 	} else {
8201d658336SSimo Sorce 		res->context_handle = NULL;
8211d658336SSimo Sorce 	}
8221d658336SSimo Sorce 
8231d658336SSimo Sorce 	/* res->output_token */
824fb43f11cSJ. Bruce Fields 	err = gssx_dec_bool(xdr, &value_follows);
825fb43f11cSJ. Bruce Fields 	if (err)
8269507271dSScott Mayhew 		goto out_free;
827fb43f11cSJ. Bruce Fields 	if (value_follows) {
8281d658336SSimo Sorce 		err = gssx_dec_buffer(xdr, res->output_token);
8291d658336SSimo Sorce 		if (err)
8309507271dSScott Mayhew 			goto out_free;
8311d658336SSimo Sorce 	} else {
8321d658336SSimo Sorce 		res->output_token = NULL;
8331d658336SSimo Sorce 	}
8341d658336SSimo Sorce 
8351d658336SSimo Sorce 	/* res->delegated_cred_handle */
836fb43f11cSJ. Bruce Fields 	err = gssx_dec_bool(xdr, &value_follows);
837fb43f11cSJ. Bruce Fields 	if (err)
8389507271dSScott Mayhew 		goto out_free;
839fb43f11cSJ. Bruce Fields 	if (value_follows) {
8401d658336SSimo Sorce 		/* we do not support upcall servers sending this data. */
8419507271dSScott Mayhew 		err = -EINVAL;
8429507271dSScott Mayhew 		goto out_free;
8431d658336SSimo Sorce 	}
8441d658336SSimo Sorce 
8451d658336SSimo Sorce 	/* res->options */
8461d658336SSimo Sorce 	err = gssx_dec_option_array(xdr, &res->options);
8471d658336SSimo Sorce 
8489507271dSScott Mayhew out_free:
8499507271dSScott Mayhew 	__free_page(scratch);
8501d658336SSimo Sorce 	return err;
8511d658336SSimo Sorce }
852