cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

gss_rpc_xdr.c (16896B)


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