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