12573a464SChuck Lever // SPDX-License-Identifier: GPL-2.0+
21d658336SSimo Sorce /*
31d658336SSimo Sorce  * GSS Proxy upcall module
41d658336SSimo Sorce  *
51d658336SSimo Sorce  *  Copyright (C) 2012 Simo Sorce <simo@redhat.com>
61d658336SSimo Sorce  */
71d658336SSimo Sorce 
81d658336SSimo Sorce #include <linux/sunrpc/svcauth.h>
91d658336SSimo Sorce #include "gss_rpc_xdr.h"
101d658336SSimo Sorce 
111d658336SSimo Sorce static int gssx_enc_bool(struct xdr_stream *xdr, int v)
121d658336SSimo Sorce {
131d658336SSimo Sorce 	__be32 *p;
141d658336SSimo Sorce 
151d658336SSimo Sorce 	p = xdr_reserve_space(xdr, 4);
161d658336SSimo Sorce 	if (unlikely(p == NULL))
171d658336SSimo Sorce 		return -ENOSPC;
181d658336SSimo Sorce 	*p = v ? xdr_one : xdr_zero;
191d658336SSimo Sorce 	return 0;
201d658336SSimo Sorce }
211d658336SSimo Sorce 
221d658336SSimo Sorce static int gssx_dec_bool(struct xdr_stream *xdr, u32 *v)
231d658336SSimo Sorce {
241d658336SSimo Sorce 	__be32 *p;
251d658336SSimo Sorce 
261d658336SSimo Sorce 	p = xdr_inline_decode(xdr, 4);
271d658336SSimo Sorce 	if (unlikely(p == NULL))
281d658336SSimo Sorce 		return -ENOSPC;
291d658336SSimo Sorce 	*v = be32_to_cpu(*p);
301d658336SSimo Sorce 	return 0;
311d658336SSimo Sorce }
321d658336SSimo Sorce 
331d658336SSimo Sorce static int gssx_enc_buffer(struct xdr_stream *xdr,
3489daf360SChristoph Hellwig 			   const gssx_buffer *buf)
351d658336SSimo Sorce {
361d658336SSimo Sorce 	__be32 *p;
371d658336SSimo Sorce 
381d658336SSimo Sorce 	p = xdr_reserve_space(xdr, sizeof(u32) + buf->len);
391d658336SSimo Sorce 	if (!p)
401d658336SSimo Sorce 		return -ENOSPC;
411d658336SSimo Sorce 	xdr_encode_opaque(p, buf->data, buf->len);
421d658336SSimo Sorce 	return 0;
431d658336SSimo Sorce }
441d658336SSimo Sorce 
451d658336SSimo Sorce static int gssx_enc_in_token(struct xdr_stream *xdr,
4689daf360SChristoph Hellwig 			     const struct gssp_in_token *in)
471d658336SSimo Sorce {
481d658336SSimo Sorce 	__be32 *p;
491d658336SSimo Sorce 
501d658336SSimo Sorce 	p = xdr_reserve_space(xdr, 4);
511d658336SSimo Sorce 	if (!p)
521d658336SSimo Sorce 		return -ENOSPC;
531d658336SSimo Sorce 	*p = cpu_to_be32(in->page_len);
541d658336SSimo Sorce 
551d658336SSimo Sorce 	/* all we need to do is to write pages */
561d658336SSimo Sorce 	xdr_write_pages(xdr, in->pages, in->page_base, in->page_len);
571d658336SSimo Sorce 
581d658336SSimo Sorce 	return 0;
591d658336SSimo Sorce }
601d658336SSimo Sorce 
611d658336SSimo Sorce 
621d658336SSimo Sorce static int gssx_dec_buffer(struct xdr_stream *xdr,
631d658336SSimo Sorce 			   gssx_buffer *buf)
641d658336SSimo Sorce {
651d658336SSimo Sorce 	u32 length;
661d658336SSimo Sorce 	__be32 *p;
671d658336SSimo Sorce 
681d658336SSimo Sorce 	p = xdr_inline_decode(xdr, 4);
691d658336SSimo Sorce 	if (unlikely(p == NULL))
701d658336SSimo Sorce 		return -ENOSPC;
711d658336SSimo Sorce 
721d658336SSimo Sorce 	length = be32_to_cpup(p);
731d658336SSimo Sorce 	p = xdr_inline_decode(xdr, length);
741d658336SSimo Sorce 	if (unlikely(p == NULL))
751d658336SSimo Sorce 		return -ENOSPC;
761d658336SSimo Sorce 
771d658336SSimo Sorce 	if (buf->len == 0) {
781d658336SSimo Sorce 		/* we intentionally are not interested in this buffer */
791d658336SSimo Sorce 		return 0;
801d658336SSimo Sorce 	}
811d658336SSimo Sorce 	if (length > buf->len)
821d658336SSimo Sorce 		return -ENOSPC;
831d658336SSimo Sorce 
841d658336SSimo Sorce 	if (!buf->data) {
851d658336SSimo Sorce 		buf->data = kmemdup(p, length, GFP_KERNEL);
861d658336SSimo Sorce 		if (!buf->data)
871d658336SSimo Sorce 			return -ENOMEM;
881d658336SSimo Sorce 	} else {
891d658336SSimo Sorce 		memcpy(buf->data, p, length);
901d658336SSimo Sorce 	}
911d658336SSimo Sorce 	buf->len = length;
921d658336SSimo Sorce 	return 0;
931d658336SSimo Sorce }
941d658336SSimo Sorce 
951d658336SSimo Sorce static int gssx_enc_option(struct xdr_stream *xdr,
961d658336SSimo Sorce 			   struct gssx_option *opt)
971d658336SSimo Sorce {
981d658336SSimo Sorce 	int err;
991d658336SSimo Sorce 
1001d658336SSimo Sorce 	err = gssx_enc_buffer(xdr, &opt->option);
1011d658336SSimo Sorce 	if (err)
1021d658336SSimo Sorce 		return err;
1031d658336SSimo Sorce 	err = gssx_enc_buffer(xdr, &opt->value);
1041d658336SSimo Sorce 	return err;
1051d658336SSimo Sorce }
1061d658336SSimo Sorce 
1071d658336SSimo Sorce static int gssx_dec_option(struct xdr_stream *xdr,
1081d658336SSimo Sorce 			   struct gssx_option *opt)
1091d658336SSimo Sorce {
1101d658336SSimo Sorce 	int err;
1111d658336SSimo Sorce 
1121d658336SSimo Sorce 	err = gssx_dec_buffer(xdr, &opt->option);
1131d658336SSimo Sorce 	if (err)
1141d658336SSimo Sorce 		return err;
1151d658336SSimo Sorce 	err = gssx_dec_buffer(xdr, &opt->value);
1161d658336SSimo Sorce 	return err;
1171d658336SSimo Sorce }
1181d658336SSimo Sorce 
1191d658336SSimo Sorce static int dummy_enc_opt_array(struct xdr_stream *xdr,
12089daf360SChristoph Hellwig 				const struct gssx_option_array *oa)
1211d658336SSimo Sorce {
1221d658336SSimo Sorce 	__be32 *p;
1231d658336SSimo Sorce 
1241d658336SSimo Sorce 	if (oa->count != 0)
1251d658336SSimo Sorce 		return -EINVAL;
1261d658336SSimo Sorce 
1271d658336SSimo Sorce 	p = xdr_reserve_space(xdr, 4);
1281d658336SSimo Sorce 	if (!p)
1291d658336SSimo Sorce 		return -ENOSPC;
1301d658336SSimo Sorce 	*p = 0;
1311d658336SSimo Sorce 
1321d658336SSimo Sorce 	return 0;
1331d658336SSimo Sorce }
1341d658336SSimo Sorce 
1351d658336SSimo Sorce static int dummy_dec_opt_array(struct xdr_stream *xdr,
1361d658336SSimo Sorce 				struct gssx_option_array *oa)
1371d658336SSimo Sorce {
1381d658336SSimo Sorce 	struct gssx_option dummy;
1391d658336SSimo Sorce 	u32 count, i;
1401d658336SSimo Sorce 	__be32 *p;
1411d658336SSimo Sorce 
1421d658336SSimo Sorce 	p = xdr_inline_decode(xdr, 4);
1431d658336SSimo Sorce 	if (unlikely(p == NULL))
1441d658336SSimo Sorce 		return -ENOSPC;
1451d658336SSimo Sorce 	count = be32_to_cpup(p++);
1461d658336SSimo Sorce 	memset(&dummy, 0, sizeof(dummy));
1471d658336SSimo Sorce 	for (i = 0; i < count; i++) {
1481d658336SSimo Sorce 		gssx_dec_option(xdr, &dummy);
1491d658336SSimo Sorce 	}
1501d658336SSimo Sorce 
1511d658336SSimo Sorce 	oa->count = 0;
1521d658336SSimo Sorce 	oa->data = NULL;
1531d658336SSimo Sorce 	return 0;
1541d658336SSimo Sorce }
1551d658336SSimo Sorce 
1566a36978eSJ. Bruce Fields static int get_host_u32(struct xdr_stream *xdr, u32 *res)
1571d658336SSimo Sorce {
158778e512bSJ. Bruce Fields 	__be32 *p;
159778e512bSJ. Bruce Fields 
160778e512bSJ. Bruce Fields 	p = xdr_inline_decode(xdr, 4);
161778e512bSJ. Bruce Fields 	if (!p)
1621d658336SSimo Sorce 		return -EINVAL;
1636a36978eSJ. Bruce Fields 	/* Contents of linux creds are all host-endian: */
1646a36978eSJ. Bruce Fields 	memcpy(res, p, sizeof(u32));
1651d658336SSimo Sorce 	return 0;
1661d658336SSimo Sorce }
1671d658336SSimo Sorce 
1681d658336SSimo Sorce static int gssx_dec_linux_creds(struct xdr_stream *xdr,
1691d658336SSimo Sorce 				struct svc_cred *creds)
1701d658336SSimo Sorce {
1711d658336SSimo Sorce 	u32 length;
1721d658336SSimo Sorce 	__be32 *p;
1736a36978eSJ. Bruce Fields 	u32 tmp;
1746a36978eSJ. Bruce Fields 	u32 N;
1756a36978eSJ. Bruce Fields 	int i, err;
1761d658336SSimo Sorce 
1771d658336SSimo Sorce 	p = xdr_inline_decode(xdr, 4);
1781d658336SSimo Sorce 	if (unlikely(p == NULL))
1791d658336SSimo Sorce 		return -ENOSPC;
1801d658336SSimo Sorce 
1811d658336SSimo Sorce 	length = be32_to_cpup(p);
1821d658336SSimo Sorce 
183778e512bSJ. Bruce Fields 	if (length > (3 + NGROUPS_MAX) * sizeof(u32))
1841d658336SSimo Sorce 		return -ENOSPC;
1851d658336SSimo Sorce 
1861d658336SSimo Sorce 	/* uid */
1876a36978eSJ. Bruce Fields 	err = get_host_u32(xdr, &tmp);
1881d658336SSimo Sorce 	if (err)
1891d658336SSimo Sorce 		return err;
190d28fcc83SJ. Bruce Fields 	creds->cr_uid = make_kuid(&init_user_ns, tmp);
1911d658336SSimo Sorce 
1921d658336SSimo Sorce 	/* gid */
1936a36978eSJ. Bruce Fields 	err = get_host_u32(xdr, &tmp);
1941d658336SSimo Sorce 	if (err)
1951d658336SSimo Sorce 		return err;
196d28fcc83SJ. Bruce Fields 	creds->cr_gid = make_kgid(&init_user_ns, tmp);
1971d658336SSimo Sorce 
1981d658336SSimo Sorce 	/* number of additional gid's */
1996a36978eSJ. Bruce Fields 	err = get_host_u32(xdr, &tmp);
2001d658336SSimo Sorce 	if (err)
2011d658336SSimo Sorce 		return err;
2021d658336SSimo Sorce 	N = tmp;
203778e512bSJ. Bruce Fields 	if ((3 + N) * sizeof(u32) != length)
204778e512bSJ. Bruce Fields 		return -EINVAL;
2051d658336SSimo Sorce 	creds->cr_group_info = groups_alloc(N);
2061d658336SSimo Sorce 	if (creds->cr_group_info == NULL)
2071d658336SSimo Sorce 		return -ENOMEM;
2081d658336SSimo Sorce 
2091d658336SSimo Sorce 	/* gid's */
2101d658336SSimo Sorce 	for (i = 0; i < N; i++) {
211d28fcc83SJ. Bruce Fields 		kgid_t kgid;
2126a36978eSJ. Bruce Fields 		err = get_host_u32(xdr, &tmp);
213d28fcc83SJ. Bruce Fields 		if (err)
214d28fcc83SJ. Bruce Fields 			goto out_free_groups;
215d28fcc83SJ. Bruce Fields 		err = -EINVAL;
216d28fcc83SJ. Bruce Fields 		kgid = make_kgid(&init_user_ns, tmp);
217d28fcc83SJ. Bruce Fields 		if (!gid_valid(kgid))
218d28fcc83SJ. Bruce Fields 			goto out_free_groups;
21981243eacSAlexey Dobriyan 		creds->cr_group_info->gid[i] = kgid;
2201d658336SSimo Sorce 	}
221bdcf0a42SThiago Rafael Becker 	groups_sort(creds->cr_group_info);
2221d658336SSimo Sorce 
2231d658336SSimo Sorce 	return 0;
224d28fcc83SJ. Bruce Fields out_free_groups:
225d28fcc83SJ. Bruce Fields 	groups_free(creds->cr_group_info);
226d28fcc83SJ. Bruce Fields 	return err;
2271d658336SSimo Sorce }
2281d658336SSimo Sorce 
2291d658336SSimo Sorce static int gssx_dec_option_array(struct xdr_stream *xdr,
2301d658336SSimo Sorce 				 struct gssx_option_array *oa)
2311d658336SSimo Sorce {
2321d658336SSimo Sorce 	struct svc_cred *creds;
2331d658336SSimo Sorce 	u32 count, i;
2341d658336SSimo Sorce 	__be32 *p;
2351d658336SSimo Sorce 	int err;
2361d658336SSimo Sorce 
2371d658336SSimo Sorce 	p = xdr_inline_decode(xdr, 4);
2381d658336SSimo Sorce 	if (unlikely(p == NULL))
2391d658336SSimo Sorce 		return -ENOSPC;
2401d658336SSimo Sorce 	count = be32_to_cpup(p++);
2419fd40c5aSGeert Uytterhoeven 	if (!count)
2429fd40c5aSGeert Uytterhoeven 		return 0;
2439fd40c5aSGeert Uytterhoeven 
2441d658336SSimo Sorce 	/* we recognize only 1 currently: CREDS_VALUE */
2451d658336SSimo Sorce 	oa->count = 1;
2461d658336SSimo Sorce 
2471d658336SSimo Sorce 	oa->data = kmalloc(sizeof(struct gssx_option), GFP_KERNEL);
2481d658336SSimo Sorce 	if (!oa->data)
2491d658336SSimo Sorce 		return -ENOMEM;
2501d658336SSimo Sorce 
251034dd34fSJ. Bruce Fields 	creds = kzalloc(sizeof(struct svc_cred), GFP_KERNEL);
2521d658336SSimo Sorce 	if (!creds) {
2531d658336SSimo Sorce 		kfree(oa->data);
2541d658336SSimo Sorce 		return -ENOMEM;
2551d658336SSimo Sorce 	}
2561d658336SSimo Sorce 
2571d658336SSimo Sorce 	oa->data[0].option.data = CREDS_VALUE;
2581d658336SSimo Sorce 	oa->data[0].option.len = sizeof(CREDS_VALUE);
2591d658336SSimo Sorce 	oa->data[0].value.data = (void *)creds;
2601d658336SSimo Sorce 	oa->data[0].value.len = 0;
2619fd40c5aSGeert Uytterhoeven 
2621d658336SSimo Sorce 	for (i = 0; i < count; i++) {
2631d658336SSimo Sorce 		gssx_buffer dummy = { 0, NULL };
2641d658336SSimo Sorce 		u32 length;
2651d658336SSimo Sorce 
2661d658336SSimo Sorce 		/* option buffer */
2671d658336SSimo Sorce 		p = xdr_inline_decode(xdr, 4);
2681d658336SSimo Sorce 		if (unlikely(p == NULL))
2691d658336SSimo Sorce 			return -ENOSPC;
2701d658336SSimo Sorce 
2711d658336SSimo Sorce 		length = be32_to_cpup(p);
2721d658336SSimo Sorce 		p = xdr_inline_decode(xdr, length);
2731d658336SSimo Sorce 		if (unlikely(p == NULL))
2741d658336SSimo Sorce 			return -ENOSPC;
2751d658336SSimo Sorce 
2761d658336SSimo Sorce 		if (length == sizeof(CREDS_VALUE) &&
2771d658336SSimo Sorce 		    memcmp(p, CREDS_VALUE, sizeof(CREDS_VALUE)) == 0) {
2781d658336SSimo Sorce 			/* We have creds here. parse them */
2791d658336SSimo Sorce 			err = gssx_dec_linux_creds(xdr, creds);
2801d658336SSimo Sorce 			if (err)
2811d658336SSimo Sorce 				return err;
2821d658336SSimo Sorce 			oa->data[0].value.len = 1; /* presence */
2831d658336SSimo Sorce 		} else {
2841d658336SSimo Sorce 			/* consume uninteresting buffer */
2851d658336SSimo Sorce 			err = gssx_dec_buffer(xdr, &dummy);
2861d658336SSimo Sorce 			if (err)
2871d658336SSimo Sorce 				return err;
2881d658336SSimo Sorce 		}
2891d658336SSimo Sorce 	}
2901d658336SSimo Sorce 	return 0;
2911d658336SSimo Sorce }
2921d658336SSimo Sorce 
2931d658336SSimo Sorce static int gssx_dec_status(struct xdr_stream *xdr,
2941d658336SSimo Sorce 			   struct gssx_status *status)
2951d658336SSimo Sorce {
2961d658336SSimo Sorce 	__be32 *p;
2971d658336SSimo Sorce 	int err;
2981d658336SSimo Sorce 
2991d658336SSimo Sorce 	/* status->major_status */
3001d658336SSimo Sorce 	p = xdr_inline_decode(xdr, 8);
3011d658336SSimo Sorce 	if (unlikely(p == NULL))
3021d658336SSimo Sorce 		return -ENOSPC;
3031d658336SSimo Sorce 	p = xdr_decode_hyper(p, &status->major_status);
3041d658336SSimo Sorce 
3051d658336SSimo Sorce 	/* status->mech */
3061d658336SSimo Sorce 	err = gssx_dec_buffer(xdr, &status->mech);
3071d658336SSimo Sorce 	if (err)
3081d658336SSimo Sorce 		return err;
3091d658336SSimo Sorce 
3101d658336SSimo Sorce 	/* status->minor_status */
3111d658336SSimo Sorce 	p = xdr_inline_decode(xdr, 8);
3121d658336SSimo Sorce 	if (unlikely(p == NULL))
3131d658336SSimo Sorce 		return -ENOSPC;
3141d658336SSimo Sorce 	p = xdr_decode_hyper(p, &status->minor_status);
3151d658336SSimo Sorce 
3161d658336SSimo Sorce 	/* status->major_status_string */
3171d658336SSimo Sorce 	err = gssx_dec_buffer(xdr, &status->major_status_string);
3181d658336SSimo Sorce 	if (err)
3191d658336SSimo Sorce 		return err;
3201d658336SSimo Sorce 
3211d658336SSimo Sorce 	/* status->minor_status_string */
3221d658336SSimo Sorce 	err = gssx_dec_buffer(xdr, &status->minor_status_string);
3231d658336SSimo Sorce 	if (err)
3241d658336SSimo Sorce 		return err;
3251d658336SSimo Sorce 
3261d658336SSimo Sorce 	/* status->server_ctx */
3271d658336SSimo Sorce 	err = gssx_dec_buffer(xdr, &status->server_ctx);
3281d658336SSimo Sorce 	if (err)
3291d658336SSimo Sorce 		return err;
3301d658336SSimo Sorce 
3311d658336SSimo Sorce 	/* we assume we have no options for now, so simply consume them */
3321d658336SSimo Sorce 	/* status->options */
3331d658336SSimo Sorce 	err = dummy_dec_opt_array(xdr, &status->options);
3341d658336SSimo Sorce 
3351d658336SSimo Sorce 	return err;
3361d658336SSimo Sorce }
3371d658336SSimo Sorce 
3381d658336SSimo Sorce static int gssx_enc_call_ctx(struct xdr_stream *xdr,
33989daf360SChristoph Hellwig 			     const struct gssx_call_ctx *ctx)
3401d658336SSimo Sorce {
3411d658336SSimo Sorce 	struct gssx_option opt;
3421d658336SSimo Sorce 	__be32 *p;
3431d658336SSimo Sorce 	int err;
3441d658336SSimo Sorce 
3451d658336SSimo Sorce 	/* ctx->locale */
3461d658336SSimo Sorce 	err = gssx_enc_buffer(xdr, &ctx->locale);
3471d658336SSimo Sorce 	if (err)
3481d658336SSimo Sorce 		return err;
3491d658336SSimo Sorce 
3501d658336SSimo Sorce 	/* ctx->server_ctx */
3511d658336SSimo Sorce 	err = gssx_enc_buffer(xdr, &ctx->server_ctx);
3521d658336SSimo Sorce 	if (err)
3531d658336SSimo Sorce 		return err;
3541d658336SSimo Sorce 
3551d658336SSimo Sorce 	/* we always want to ask for lucid contexts */
3561d658336SSimo Sorce 	/* ctx->options */
3571d658336SSimo Sorce 	p = xdr_reserve_space(xdr, 4);
3581d658336SSimo Sorce 	*p = cpu_to_be32(2);
3591d658336SSimo Sorce 
3601d658336SSimo Sorce 	/* we want a lucid_v1 context */
3611d658336SSimo Sorce 	opt.option.data = LUCID_OPTION;
3621d658336SSimo Sorce 	opt.option.len = sizeof(LUCID_OPTION);
3631d658336SSimo Sorce 	opt.value.data = LUCID_VALUE;
3641d658336SSimo Sorce 	opt.value.len = sizeof(LUCID_VALUE);
3651d658336SSimo Sorce 	err = gssx_enc_option(xdr, &opt);
3661d658336SSimo Sorce 
3671d658336SSimo Sorce 	/* ..and user creds */
3681d658336SSimo Sorce 	opt.option.data = CREDS_OPTION;
3691d658336SSimo Sorce 	opt.option.len = sizeof(CREDS_OPTION);
3701d658336SSimo Sorce 	opt.value.data = CREDS_VALUE;
3711d658336SSimo Sorce 	opt.value.len = sizeof(CREDS_VALUE);
3721d658336SSimo Sorce 	err = gssx_enc_option(xdr, &opt);
3731d658336SSimo Sorce 
3741d658336SSimo Sorce 	return err;
3751d658336SSimo Sorce }
3761d658336SSimo Sorce 
3771d658336SSimo Sorce static int gssx_dec_name_attr(struct xdr_stream *xdr,
3781d658336SSimo Sorce 			     struct gssx_name_attr *attr)
3791d658336SSimo Sorce {
3801d658336SSimo Sorce 	int err;
3811d658336SSimo Sorce 
3821d658336SSimo Sorce 	/* attr->attr */
3831d658336SSimo Sorce 	err = gssx_dec_buffer(xdr, &attr->attr);
3841d658336SSimo Sorce 	if (err)
3851d658336SSimo Sorce 		return err;
3861d658336SSimo Sorce 
3871d658336SSimo Sorce 	/* attr->value */
3881d658336SSimo Sorce 	err = gssx_dec_buffer(xdr, &attr->value);
3891d658336SSimo Sorce 	if (err)
3901d658336SSimo Sorce 		return err;
3911d658336SSimo Sorce 
3921d658336SSimo Sorce 	/* attr->extensions */
3931d658336SSimo Sorce 	err = dummy_dec_opt_array(xdr, &attr->extensions);
3941d658336SSimo Sorce 
3951d658336SSimo Sorce 	return err;
3961d658336SSimo Sorce }
3971d658336SSimo Sorce 
3981d658336SSimo Sorce static int dummy_enc_nameattr_array(struct xdr_stream *xdr,
3991d658336SSimo Sorce 				    struct gssx_name_attr_array *naa)
4001d658336SSimo Sorce {
4011d658336SSimo Sorce 	__be32 *p;
4021d658336SSimo Sorce 
4031d658336SSimo Sorce 	if (naa->count != 0)
4041d658336SSimo Sorce 		return -EINVAL;
4051d658336SSimo Sorce 
4061d658336SSimo Sorce 	p = xdr_reserve_space(xdr, 4);
4071d658336SSimo Sorce 	if (!p)
4081d658336SSimo Sorce 		return -ENOSPC;
4091d658336SSimo Sorce 	*p = 0;
4101d658336SSimo Sorce 
4111d658336SSimo Sorce 	return 0;
4121d658336SSimo Sorce }
4131d658336SSimo Sorce 
4141d658336SSimo Sorce static int dummy_dec_nameattr_array(struct xdr_stream *xdr,
4151d658336SSimo Sorce 				    struct gssx_name_attr_array *naa)
4161d658336SSimo Sorce {
417dc43376cSJ. Bruce Fields 	struct gssx_name_attr dummy = { .attr = {.len = 0} };
4181d658336SSimo Sorce 	u32 count, i;
4191d658336SSimo Sorce 	__be32 *p;
4201d658336SSimo Sorce 
4211d658336SSimo Sorce 	p = xdr_inline_decode(xdr, 4);
4221d658336SSimo Sorce 	if (unlikely(p == NULL))
4231d658336SSimo Sorce 		return -ENOSPC;
4241d658336SSimo Sorce 	count = be32_to_cpup(p++);
4251d658336SSimo Sorce 	for (i = 0; i < count; i++) {
4261d658336SSimo Sorce 		gssx_dec_name_attr(xdr, &dummy);
4271d658336SSimo Sorce 	}
4281d658336SSimo Sorce 
4291d658336SSimo Sorce 	naa->count = 0;
4301d658336SSimo Sorce 	naa->data = NULL;
4311d658336SSimo Sorce 	return 0;
4321d658336SSimo Sorce }
4331d658336SSimo Sorce 
4341d658336SSimo Sorce static struct xdr_netobj zero_netobj = {};
4351d658336SSimo Sorce 
4361d658336SSimo Sorce static struct gssx_name_attr_array zero_name_attr_array = {};
4371d658336SSimo Sorce 
4381d658336SSimo Sorce static struct gssx_option_array zero_option_array = {};
4391d658336SSimo Sorce 
4401d658336SSimo Sorce static int gssx_enc_name(struct xdr_stream *xdr,
4411d658336SSimo Sorce 			 struct gssx_name *name)
4421d658336SSimo Sorce {
4431d658336SSimo Sorce 	int err;
4441d658336SSimo Sorce 
4451d658336SSimo Sorce 	/* name->display_name */
4461d658336SSimo Sorce 	err = gssx_enc_buffer(xdr, &name->display_name);
4471d658336SSimo Sorce 	if (err)
4481d658336SSimo Sorce 		return err;
4491d658336SSimo Sorce 
4501d658336SSimo Sorce 	/* name->name_type */
4511d658336SSimo Sorce 	err = gssx_enc_buffer(xdr, &zero_netobj);
4521d658336SSimo Sorce 	if (err)
4531d658336SSimo Sorce 		return err;
4541d658336SSimo Sorce 
4551d658336SSimo Sorce 	/* name->exported_name */
4561d658336SSimo Sorce 	err = gssx_enc_buffer(xdr, &zero_netobj);
4571d658336SSimo Sorce 	if (err)
4581d658336SSimo Sorce 		return err;
4591d658336SSimo Sorce 
4601d658336SSimo Sorce 	/* name->exported_composite_name */
4611d658336SSimo Sorce 	err = gssx_enc_buffer(xdr, &zero_netobj);
4621d658336SSimo Sorce 	if (err)
4631d658336SSimo Sorce 		return err;
4641d658336SSimo Sorce 
4651d658336SSimo Sorce 	/* leave name_attributes empty for now, will add once we have any
4661d658336SSimo Sorce 	 * to pass up at all */
4671d658336SSimo Sorce 	/* name->name_attributes */
4681d658336SSimo Sorce 	err = dummy_enc_nameattr_array(xdr, &zero_name_attr_array);
4691d658336SSimo Sorce 	if (err)
4701d658336SSimo Sorce 		return err;
4711d658336SSimo Sorce 
4721d658336SSimo Sorce 	/* leave options empty for now, will add once we have any options
4731d658336SSimo Sorce 	 * to pass up at all */
4741d658336SSimo Sorce 	/* name->extensions */
4751d658336SSimo Sorce 	err = dummy_enc_opt_array(xdr, &zero_option_array);
4761d658336SSimo Sorce 
4771d658336SSimo Sorce 	return err;
4781d658336SSimo Sorce }
4791d658336SSimo Sorce 
480dc43376cSJ. Bruce Fields 
4811d658336SSimo Sorce static int gssx_dec_name(struct xdr_stream *xdr,
4821d658336SSimo Sorce 			 struct gssx_name *name)
4831d658336SSimo Sorce {
484dc43376cSJ. Bruce Fields 	struct xdr_netobj dummy_netobj = { .len = 0 };
485dc43376cSJ. Bruce Fields 	struct gssx_name_attr_array dummy_name_attr_array = { .count = 0 };
486dc43376cSJ. Bruce Fields 	struct gssx_option_array dummy_option_array = { .count = 0 };
4871d658336SSimo Sorce 	int err;
4881d658336SSimo Sorce 
4891d658336SSimo Sorce 	/* name->display_name */
4901d658336SSimo Sorce 	err = gssx_dec_buffer(xdr, &name->display_name);
4911d658336SSimo Sorce 	if (err)
4921d658336SSimo Sorce 		return err;
4931d658336SSimo Sorce 
4941d658336SSimo Sorce 	/* name->name_type */
4951d658336SSimo Sorce 	err = gssx_dec_buffer(xdr, &dummy_netobj);
4961d658336SSimo Sorce 	if (err)
4971d658336SSimo Sorce 		return err;
4981d658336SSimo Sorce 
4991d658336SSimo Sorce 	/* name->exported_name */
5001d658336SSimo Sorce 	err = gssx_dec_buffer(xdr, &dummy_netobj);
5011d658336SSimo Sorce 	if (err)
5021d658336SSimo Sorce 		return err;
5031d658336SSimo Sorce 
5041d658336SSimo Sorce 	/* name->exported_composite_name */
5051d658336SSimo Sorce 	err = gssx_dec_buffer(xdr, &dummy_netobj);
5061d658336SSimo Sorce 	if (err)
5071d658336SSimo Sorce 		return err;
5081d658336SSimo Sorce 
5091d658336SSimo Sorce 	/* we assume we have no attributes for now, so simply consume them */
5101d658336SSimo Sorce 	/* name->name_attributes */
5111d658336SSimo Sorce 	err = dummy_dec_nameattr_array(xdr, &dummy_name_attr_array);
5121d658336SSimo Sorce 	if (err)
5131d658336SSimo Sorce 		return err;
5141d658336SSimo Sorce 
5151d658336SSimo Sorce 	/* we assume we have no options for now, so simply consume them */
5161d658336SSimo Sorce 	/* name->extensions */
5171d658336SSimo Sorce 	err = dummy_dec_opt_array(xdr, &dummy_option_array);
5181d658336SSimo Sorce 
5191d658336SSimo Sorce 	return err;
5201d658336SSimo Sorce }
5211d658336SSimo Sorce 
5221d658336SSimo Sorce static int dummy_enc_credel_array(struct xdr_stream *xdr,
5231d658336SSimo Sorce 				  struct gssx_cred_element_array *cea)
5241d658336SSimo Sorce {
5251d658336SSimo Sorce 	__be32 *p;
5261d658336SSimo Sorce 
5271d658336SSimo Sorce 	if (cea->count != 0)
5281d658336SSimo Sorce 		return -EINVAL;
5291d658336SSimo Sorce 
5301d658336SSimo Sorce 	p = xdr_reserve_space(xdr, 4);
5311d658336SSimo Sorce 	if (!p)
5321d658336SSimo Sorce 		return -ENOSPC;
5331d658336SSimo Sorce 	*p = 0;
5341d658336SSimo Sorce 
5351d658336SSimo Sorce 	return 0;
5361d658336SSimo Sorce }
5371d658336SSimo Sorce 
5381d658336SSimo Sorce static int gssx_enc_cred(struct xdr_stream *xdr,
5391d658336SSimo Sorce 			 struct gssx_cred *cred)
5401d658336SSimo Sorce {
5411d658336SSimo Sorce 	int err;
5421d658336SSimo Sorce 
5431d658336SSimo Sorce 	/* cred->desired_name */
5441d658336SSimo Sorce 	err = gssx_enc_name(xdr, &cred->desired_name);
5451d658336SSimo Sorce 	if (err)
5461d658336SSimo Sorce 		return err;
5471d658336SSimo Sorce 
5481d658336SSimo Sorce 	/* cred->elements */
5491d658336SSimo Sorce 	err = dummy_enc_credel_array(xdr, &cred->elements);
550b26ec9b1SJ. Bruce Fields 	if (err)
551b26ec9b1SJ. Bruce Fields 		return err;
5521d658336SSimo Sorce 
5531d658336SSimo Sorce 	/* cred->cred_handle_reference */
5541d658336SSimo Sorce 	err = gssx_enc_buffer(xdr, &cred->cred_handle_reference);
5551d658336SSimo Sorce 	if (err)
5561d658336SSimo Sorce 		return err;
5571d658336SSimo Sorce 
5581d658336SSimo Sorce 	/* cred->needs_release */
5591d658336SSimo Sorce 	err = gssx_enc_bool(xdr, cred->needs_release);
5601d658336SSimo Sorce 
5611d658336SSimo Sorce 	return err;
5621d658336SSimo Sorce }
5631d658336SSimo Sorce 
5641d658336SSimo Sorce static int gssx_enc_ctx(struct xdr_stream *xdr,
5651d658336SSimo Sorce 			struct gssx_ctx *ctx)
5661d658336SSimo Sorce {
5671d658336SSimo Sorce 	__be32 *p;
5681d658336SSimo Sorce 	int err;
5691d658336SSimo Sorce 
5701d658336SSimo Sorce 	/* ctx->exported_context_token */
5711d658336SSimo Sorce 	err = gssx_enc_buffer(xdr, &ctx->exported_context_token);
5721d658336SSimo Sorce 	if (err)
5731d658336SSimo Sorce 		return err;
5741d658336SSimo Sorce 
5751d658336SSimo Sorce 	/* ctx->state */
5761d658336SSimo Sorce 	err = gssx_enc_buffer(xdr, &ctx->state);
5771d658336SSimo Sorce 	if (err)
5781d658336SSimo Sorce 		return err;
5791d658336SSimo Sorce 
5801d658336SSimo Sorce 	/* ctx->need_release */
5811d658336SSimo Sorce 	err = gssx_enc_bool(xdr, ctx->need_release);
5821d658336SSimo Sorce 	if (err)
5831d658336SSimo Sorce 		return err;
5841d658336SSimo Sorce 
5851d658336SSimo Sorce 	/* ctx->mech */
5861d658336SSimo Sorce 	err = gssx_enc_buffer(xdr, &ctx->mech);
5871d658336SSimo Sorce 	if (err)
5881d658336SSimo Sorce 		return err;
5891d658336SSimo Sorce 
5901d658336SSimo Sorce 	/* ctx->src_name */
5911d658336SSimo Sorce 	err = gssx_enc_name(xdr, &ctx->src_name);
5921d658336SSimo Sorce 	if (err)
5931d658336SSimo Sorce 		return err;
5941d658336SSimo Sorce 
5951d658336SSimo Sorce 	/* ctx->targ_name */
5961d658336SSimo Sorce 	err = gssx_enc_name(xdr, &ctx->targ_name);
5971d658336SSimo Sorce 	if (err)
5981d658336SSimo Sorce 		return err;
5991d658336SSimo Sorce 
6001d658336SSimo Sorce 	/* ctx->lifetime */
6011d658336SSimo Sorce 	p = xdr_reserve_space(xdr, 8+8);
6021d658336SSimo Sorce 	if (!p)
6031d658336SSimo Sorce 		return -ENOSPC;
6041d658336SSimo Sorce 	p = xdr_encode_hyper(p, ctx->lifetime);
6051d658336SSimo Sorce 
6061d658336SSimo Sorce 	/* ctx->ctx_flags */
6071d658336SSimo Sorce 	p = xdr_encode_hyper(p, ctx->ctx_flags);
6081d658336SSimo Sorce 
6091d658336SSimo Sorce 	/* ctx->locally_initiated */
6101d658336SSimo Sorce 	err = gssx_enc_bool(xdr, ctx->locally_initiated);
6111d658336SSimo Sorce 	if (err)
6121d658336SSimo Sorce 		return err;
6131d658336SSimo Sorce 
6141d658336SSimo Sorce 	/* ctx->open */
6151d658336SSimo Sorce 	err = gssx_enc_bool(xdr, ctx->open);
6161d658336SSimo Sorce 	if (err)
6171d658336SSimo Sorce 		return err;
6181d658336SSimo Sorce 
6191d658336SSimo Sorce 	/* leave options empty for now, will add once we have any options
6201d658336SSimo Sorce 	 * to pass up at all */
6211d658336SSimo Sorce 	/* ctx->options */
6221d658336SSimo Sorce 	err = dummy_enc_opt_array(xdr, &ctx->options);
6231d658336SSimo Sorce 
6241d658336SSimo Sorce 	return err;
6251d658336SSimo Sorce }
6261d658336SSimo Sorce 
6271d658336SSimo Sorce static int gssx_dec_ctx(struct xdr_stream *xdr,
6281d658336SSimo Sorce 			struct gssx_ctx *ctx)
6291d658336SSimo Sorce {
6301d658336SSimo Sorce 	__be32 *p;
6311d658336SSimo Sorce 	int err;
6321d658336SSimo Sorce 
6331d658336SSimo Sorce 	/* ctx->exported_context_token */
6341d658336SSimo Sorce 	err = gssx_dec_buffer(xdr, &ctx->exported_context_token);
6351d658336SSimo Sorce 	if (err)
6361d658336SSimo Sorce 		return err;
6371d658336SSimo Sorce 
6381d658336SSimo Sorce 	/* ctx->state */
6391d658336SSimo Sorce 	err = gssx_dec_buffer(xdr, &ctx->state);
6401d658336SSimo Sorce 	if (err)
6411d658336SSimo Sorce 		return err;
6421d658336SSimo Sorce 
6431d658336SSimo Sorce 	/* ctx->need_release */
6441d658336SSimo Sorce 	err = gssx_dec_bool(xdr, &ctx->need_release);
6451d658336SSimo Sorce 	if (err)
6461d658336SSimo Sorce 		return err;
6471d658336SSimo Sorce 
6481d658336SSimo Sorce 	/* ctx->mech */
6491d658336SSimo Sorce 	err = gssx_dec_buffer(xdr, &ctx->mech);
6501d658336SSimo Sorce 	if (err)
6511d658336SSimo Sorce 		return err;
6521d658336SSimo Sorce 
6531d658336SSimo Sorce 	/* ctx->src_name */
6541d658336SSimo Sorce 	err = gssx_dec_name(xdr, &ctx->src_name);
6551d658336SSimo Sorce 	if (err)
6561d658336SSimo Sorce 		return err;
6571d658336SSimo Sorce 
6581d658336SSimo Sorce 	/* ctx->targ_name */
6591d658336SSimo Sorce 	err = gssx_dec_name(xdr, &ctx->targ_name);
6601d658336SSimo Sorce 	if (err)
6611d658336SSimo Sorce 		return err;
6621d658336SSimo Sorce 
6631d658336SSimo Sorce 	/* ctx->lifetime */
6641d658336SSimo Sorce 	p = xdr_inline_decode(xdr, 8+8);
6651d658336SSimo Sorce 	if (unlikely(p == NULL))
6661d658336SSimo Sorce 		return -ENOSPC;
6671d658336SSimo Sorce 	p = xdr_decode_hyper(p, &ctx->lifetime);
6681d658336SSimo Sorce 
6691d658336SSimo Sorce 	/* ctx->ctx_flags */
6701d658336SSimo Sorce 	p = xdr_decode_hyper(p, &ctx->ctx_flags);
6711d658336SSimo Sorce 
6721d658336SSimo Sorce 	/* ctx->locally_initiated */
6731d658336SSimo Sorce 	err = gssx_dec_bool(xdr, &ctx->locally_initiated);
6741d658336SSimo Sorce 	if (err)
6751d658336SSimo Sorce 		return err;
6761d658336SSimo Sorce 
6771d658336SSimo Sorce 	/* ctx->open */
6781d658336SSimo Sorce 	err = gssx_dec_bool(xdr, &ctx->open);
6791d658336SSimo Sorce 	if (err)
6801d658336SSimo Sorce 		return err;
6811d658336SSimo Sorce 
6821d658336SSimo Sorce 	/* we assume we have no options for now, so simply consume them */
6831d658336SSimo Sorce 	/* ctx->options */
6841d658336SSimo Sorce 	err = dummy_dec_opt_array(xdr, &ctx->options);
6851d658336SSimo Sorce 
6861d658336SSimo Sorce 	return err;
6871d658336SSimo Sorce }
6881d658336SSimo Sorce 
6891d658336SSimo Sorce static int gssx_enc_cb(struct xdr_stream *xdr, struct gssx_cb *cb)
6901d658336SSimo Sorce {
6911d658336SSimo Sorce 	__be32 *p;
6921d658336SSimo Sorce 	int err;
6931d658336SSimo Sorce 
6941d658336SSimo Sorce 	/* cb->initiator_addrtype */
6951d658336SSimo Sorce 	p = xdr_reserve_space(xdr, 8);
6961d658336SSimo Sorce 	if (!p)
6971d658336SSimo Sorce 		return -ENOSPC;
6981d658336SSimo Sorce 	p = xdr_encode_hyper(p, cb->initiator_addrtype);
6991d658336SSimo Sorce 
7001d658336SSimo Sorce 	/* cb->initiator_address */
7011d658336SSimo Sorce 	err = gssx_enc_buffer(xdr, &cb->initiator_address);
7021d658336SSimo Sorce 	if (err)
7031d658336SSimo Sorce 		return err;
7041d658336SSimo Sorce 
7051d658336SSimo Sorce 	/* cb->acceptor_addrtype */
7061d658336SSimo Sorce 	p = xdr_reserve_space(xdr, 8);
7071d658336SSimo Sorce 	if (!p)
7081d658336SSimo Sorce 		return -ENOSPC;
7091d658336SSimo Sorce 	p = xdr_encode_hyper(p, cb->acceptor_addrtype);
7101d658336SSimo Sorce 
7111d658336SSimo Sorce 	/* cb->acceptor_address */
7121d658336SSimo Sorce 	err = gssx_enc_buffer(xdr, &cb->acceptor_address);
7131d658336SSimo Sorce 	if (err)
7141d658336SSimo Sorce 		return err;
7151d658336SSimo Sorce 
7161d658336SSimo Sorce 	/* cb->application_data */
7171d658336SSimo Sorce 	err = gssx_enc_buffer(xdr, &cb->application_data);
7181d658336SSimo Sorce 
7191d658336SSimo Sorce 	return err;
7201d658336SSimo Sorce }
7211d658336SSimo Sorce 
7221d658336SSimo Sorce void gssx_enc_accept_sec_context(struct rpc_rqst *req,
7231d658336SSimo Sorce 				 struct xdr_stream *xdr,
72489daf360SChristoph Hellwig 				 const void *data)
7251d658336SSimo Sorce {
72689daf360SChristoph Hellwig 	const struct gssx_arg_accept_sec_context *arg = data;
7271d658336SSimo Sorce 	int err;
7281d658336SSimo Sorce 
7291d658336SSimo Sorce 	err = gssx_enc_call_ctx(xdr, &arg->call_ctx);
7301d658336SSimo Sorce 	if (err)
7311d658336SSimo Sorce 		goto done;
7321d658336SSimo Sorce 
7331d658336SSimo Sorce 	/* arg->context_handle */
734b26ec9b1SJ. Bruce Fields 	if (arg->context_handle)
7351d658336SSimo Sorce 		err = gssx_enc_ctx(xdr, arg->context_handle);
736b26ec9b1SJ. Bruce Fields 	else
737b26ec9b1SJ. Bruce Fields 		err = gssx_enc_bool(xdr, 0);
7381d658336SSimo Sorce 	if (err)
7391d658336SSimo Sorce 		goto done;
7401d658336SSimo Sorce 
7411d658336SSimo Sorce 	/* arg->cred_handle */
742b26ec9b1SJ. Bruce Fields 	if (arg->cred_handle)
7431d658336SSimo Sorce 		err = gssx_enc_cred(xdr, arg->cred_handle);
744b26ec9b1SJ. Bruce Fields 	else
745b26ec9b1SJ. Bruce Fields 		err = gssx_enc_bool(xdr, 0);
7461d658336SSimo Sorce 	if (err)
7471d658336SSimo Sorce 		goto done;
7481d658336SSimo Sorce 
7491d658336SSimo Sorce 	/* arg->input_token */
7501d658336SSimo Sorce 	err = gssx_enc_in_token(xdr, &arg->input_token);
7511d658336SSimo Sorce 	if (err)
7521d658336SSimo Sorce 		goto done;
7531d658336SSimo Sorce 
7541d658336SSimo Sorce 	/* arg->input_cb */
755b26ec9b1SJ. Bruce Fields 	if (arg->input_cb)
7561d658336SSimo Sorce 		err = gssx_enc_cb(xdr, arg->input_cb);
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 	err = gssx_enc_bool(xdr, arg->ret_deleg_cred);
7631d658336SSimo Sorce 	if (err)
7641d658336SSimo Sorce 		goto done;
7651d658336SSimo Sorce 
7661d658336SSimo Sorce 	/* leave options empty for now, will add once we have any options
7671d658336SSimo Sorce 	 * to pass up at all */
7681d658336SSimo Sorce 	/* arg->options */
7691d658336SSimo Sorce 	err = dummy_enc_opt_array(xdr, &arg->options);
7701d658336SSimo Sorce 
7719dfd87daSJ. Bruce Fields 	xdr_inline_pages(&req->rq_rcv_buf,
7729dfd87daSJ. Bruce Fields 		PAGE_SIZE/2 /* pretty arbitrary */,
7739dfd87daSJ. Bruce Fields 		arg->pages, 0 /* page base */, arg->npages * PAGE_SIZE);
774431f6eb3STrond Myklebust 	req->rq_rcv_buf.flags |= XDRBUF_SPARSE_PAGES;
7751d658336SSimo Sorce done:
7761d658336SSimo Sorce 	if (err)
7771d658336SSimo Sorce 		dprintk("RPC:       gssx_enc_accept_sec_context: %d\n", err);
7781d658336SSimo Sorce }
7791d658336SSimo Sorce 
7801d658336SSimo Sorce int gssx_dec_accept_sec_context(struct rpc_rqst *rqstp,
7811d658336SSimo Sorce 				struct xdr_stream *xdr,
782305c6241SChristoph Hellwig 				void *data)
7831d658336SSimo Sorce {
784305c6241SChristoph Hellwig 	struct gssx_res_accept_sec_context *res = data;
785fb43f11cSJ. Bruce Fields 	u32 value_follows;
7861d658336SSimo Sorce 	int err;
7879507271dSScott Mayhew 	struct page *scratch;
7889507271dSScott Mayhew 
7899507271dSScott Mayhew 	scratch = alloc_page(GFP_KERNEL);
7909507271dSScott Mayhew 	if (!scratch)
7919507271dSScott Mayhew 		return -ENOMEM;
792*0ae4c3e8SChuck Lever 	xdr_set_scratch_page(xdr, scratch);
7931d658336SSimo Sorce 
7941d658336SSimo Sorce 	/* res->status */
7951d658336SSimo Sorce 	err = gssx_dec_status(xdr, &res->status);
7961d658336SSimo Sorce 	if (err)
7979507271dSScott Mayhew 		goto out_free;
7981d658336SSimo Sorce 
7991d658336SSimo Sorce 	/* res->context_handle */
800fb43f11cSJ. Bruce Fields 	err = gssx_dec_bool(xdr, &value_follows);
801fb43f11cSJ. Bruce Fields 	if (err)
8029507271dSScott Mayhew 		goto out_free;
803fb43f11cSJ. Bruce Fields 	if (value_follows) {
8041d658336SSimo Sorce 		err = gssx_dec_ctx(xdr, res->context_handle);
8051d658336SSimo Sorce 		if (err)
8069507271dSScott Mayhew 			goto out_free;
8071d658336SSimo Sorce 	} else {
8081d658336SSimo Sorce 		res->context_handle = NULL;
8091d658336SSimo Sorce 	}
8101d658336SSimo Sorce 
8111d658336SSimo Sorce 	/* res->output_token */
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_buffer(xdr, res->output_token);
8171d658336SSimo Sorce 		if (err)
8189507271dSScott Mayhew 			goto out_free;
8191d658336SSimo Sorce 	} else {
8201d658336SSimo Sorce 		res->output_token = NULL;
8211d658336SSimo Sorce 	}
8221d658336SSimo Sorce 
8231d658336SSimo Sorce 	/* res->delegated_cred_handle */
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 		/* we do not support upcall servers sending this data. */
8299507271dSScott Mayhew 		err = -EINVAL;
8309507271dSScott Mayhew 		goto out_free;
8311d658336SSimo Sorce 	}
8321d658336SSimo Sorce 
8331d658336SSimo Sorce 	/* res->options */
8341d658336SSimo Sorce 	err = gssx_dec_option_array(xdr, &res->options);
8351d658336SSimo Sorce 
8369507271dSScott Mayhew out_free:
8379507271dSScott Mayhew 	__free_page(scratch);
8381d658336SSimo Sorce 	return err;
8391d658336SSimo Sorce }
840