linux/net/sunrpc/auth_gss/gss_rpc_xdr.c
<<
>>
Prefs
   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}
 839