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