2012-05-25 18:09:55 -04:00
/*
* GSS Proxy upcall module
*
* Copyright ( C ) 2012 Simo Sorce < simo @ redhat . com >
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation ; either version 2 of the License , or
* ( at your option ) any later version .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* You should have received a copy of the GNU General Public License
* along with this program ; if not , write to the Free Software
* Foundation , Inc . , 675 Mass Ave , Cambridge , MA 0213 9 , USA .
*/
# include <linux/sunrpc/svcauth.h>
# include "gss_rpc_xdr.h"
static int gssx_enc_bool ( struct xdr_stream * xdr , int v )
{
__be32 * p ;
p = xdr_reserve_space ( xdr , 4 ) ;
if ( unlikely ( p = = NULL ) )
return - ENOSPC ;
* p = v ? xdr_one : xdr_zero ;
return 0 ;
}
static int gssx_dec_bool ( struct xdr_stream * xdr , u32 * v )
{
__be32 * p ;
p = xdr_inline_decode ( xdr , 4 ) ;
if ( unlikely ( p = = NULL ) )
return - ENOSPC ;
* v = be32_to_cpu ( * p ) ;
return 0 ;
}
static int gssx_enc_buffer ( struct xdr_stream * xdr ,
gssx_buffer * buf )
{
__be32 * p ;
p = xdr_reserve_space ( xdr , sizeof ( u32 ) + buf - > len ) ;
if ( ! p )
return - ENOSPC ;
xdr_encode_opaque ( p , buf - > data , buf - > len ) ;
return 0 ;
}
static int gssx_enc_in_token ( struct xdr_stream * xdr ,
struct gssp_in_token * in )
{
__be32 * p ;
p = xdr_reserve_space ( xdr , 4 ) ;
if ( ! p )
return - ENOSPC ;
* p = cpu_to_be32 ( in - > page_len ) ;
/* all we need to do is to write pages */
xdr_write_pages ( xdr , in - > pages , in - > page_base , in - > page_len ) ;
return 0 ;
}
static int gssx_dec_buffer ( struct xdr_stream * xdr ,
gssx_buffer * buf )
{
u32 length ;
__be32 * p ;
p = xdr_inline_decode ( xdr , 4 ) ;
if ( unlikely ( p = = NULL ) )
return - ENOSPC ;
length = be32_to_cpup ( p ) ;
p = xdr_inline_decode ( xdr , length ) ;
if ( unlikely ( p = = NULL ) )
return - ENOSPC ;
if ( buf - > len = = 0 ) {
/* we intentionally are not interested in this buffer */
return 0 ;
}
if ( length > buf - > len )
return - ENOSPC ;
if ( ! buf - > data ) {
buf - > data = kmemdup ( p , length , GFP_KERNEL ) ;
if ( ! buf - > data )
return - ENOMEM ;
} else {
memcpy ( buf - > data , p , length ) ;
}
buf - > len = length ;
return 0 ;
}
static int gssx_enc_option ( struct xdr_stream * xdr ,
struct gssx_option * opt )
{
int err ;
err = gssx_enc_buffer ( xdr , & opt - > option ) ;
if ( err )
return err ;
err = gssx_enc_buffer ( xdr , & opt - > value ) ;
return err ;
}
static int gssx_dec_option ( struct xdr_stream * xdr ,
struct gssx_option * opt )
{
int err ;
err = gssx_dec_buffer ( xdr , & opt - > option ) ;
if ( err )
return err ;
err = gssx_dec_buffer ( xdr , & opt - > value ) ;
return err ;
}
static int dummy_enc_opt_array ( struct xdr_stream * xdr ,
struct gssx_option_array * oa )
{
__be32 * p ;
if ( oa - > count ! = 0 )
return - EINVAL ;
p = xdr_reserve_space ( xdr , 4 ) ;
if ( ! p )
return - ENOSPC ;
* p = 0 ;
return 0 ;
}
static int dummy_dec_opt_array ( struct xdr_stream * xdr ,
struct gssx_option_array * oa )
{
struct gssx_option dummy ;
u32 count , i ;
__be32 * p ;
p = xdr_inline_decode ( xdr , 4 ) ;
if ( unlikely ( p = = NULL ) )
return - ENOSPC ;
count = be32_to_cpup ( p + + ) ;
memset ( & dummy , 0 , sizeof ( dummy ) ) ;
for ( i = 0 ; i < count ; i + + ) {
gssx_dec_option ( xdr , & dummy ) ;
}
oa - > count = 0 ;
oa - > data = NULL ;
return 0 ;
}
2013-08-23 11:17:53 -04:00
static int get_host_u32 ( struct xdr_stream * xdr , u32 * res )
2012-05-25 18:09:55 -04:00
{
2013-08-21 10:32:52 -04:00
__be32 * p ;
p = xdr_inline_decode ( xdr , 4 ) ;
if ( ! p )
2012-05-25 18:09:55 -04:00
return - EINVAL ;
2013-08-23 11:17:53 -04:00
/* Contents of linux creds are all host-endian: */
memcpy ( res , p , sizeof ( u32 ) ) ;
2012-05-25 18:09:55 -04:00
return 0 ;
}
static int gssx_dec_linux_creds ( struct xdr_stream * xdr ,
struct svc_cred * creds )
{
u32 length ;
__be32 * p ;
2013-08-23 11:17:53 -04:00
u32 tmp ;
u32 N ;
int i , err ;
2012-05-25 18:09:55 -04:00
p = xdr_inline_decode ( xdr , 4 ) ;
if ( unlikely ( p = = NULL ) )
return - ENOSPC ;
length = be32_to_cpup ( p ) ;
2013-08-21 10:32:52 -04:00
if ( length > ( 3 + NGROUPS_MAX ) * sizeof ( u32 ) )
2012-05-25 18:09:55 -04:00
return - ENOSPC ;
/* uid */
2013-08-23 11:17:53 -04:00
err = get_host_u32 ( xdr , & tmp ) ;
2012-05-25 18:09:55 -04:00
if ( err )
return err ;
2013-04-29 18:21:29 -04:00
creds - > cr_uid = make_kuid ( & init_user_ns , tmp ) ;
2012-05-25 18:09:55 -04:00
/* gid */
2013-08-23 11:17:53 -04:00
err = get_host_u32 ( xdr , & tmp ) ;
2012-05-25 18:09:55 -04:00
if ( err )
return err ;
2013-04-29 18:21:29 -04:00
creds - > cr_gid = make_kgid ( & init_user_ns , tmp ) ;
2012-05-25 18:09:55 -04:00
/* number of additional gid's */
2013-08-23 11:17:53 -04:00
err = get_host_u32 ( xdr , & tmp ) ;
2012-05-25 18:09:55 -04:00
if ( err )
return err ;
N = tmp ;
2013-08-21 10:32:52 -04:00
if ( ( 3 + N ) * sizeof ( u32 ) ! = length )
return - EINVAL ;
2012-05-25 18:09:55 -04:00
creds - > cr_group_info = groups_alloc ( N ) ;
if ( creds - > cr_group_info = = NULL )
return - ENOMEM ;
/* gid's */
for ( i = 0 ; i < N ; i + + ) {
2013-04-29 18:21:29 -04:00
kgid_t kgid ;
2013-08-23 11:17:53 -04:00
err = get_host_u32 ( xdr , & tmp ) ;
2013-04-29 18:21:29 -04:00
if ( err )
goto out_free_groups ;
err = - EINVAL ;
kgid = make_kgid ( & init_user_ns , tmp ) ;
if ( ! gid_valid ( kgid ) )
goto out_free_groups ;
GROUP_AT ( creds - > cr_group_info , i ) = kgid ;
2012-05-25 18:09:55 -04:00
}
return 0 ;
2013-04-29 18:21:29 -04:00
out_free_groups :
groups_free ( creds - > cr_group_info ) ;
return err ;
2012-05-25 18:09:55 -04:00
}
static int gssx_dec_option_array ( struct xdr_stream * xdr ,
struct gssx_option_array * oa )
{
struct svc_cred * creds ;
u32 count , i ;
__be32 * p ;
int err ;
p = xdr_inline_decode ( xdr , 4 ) ;
if ( unlikely ( p = = NULL ) )
return - ENOSPC ;
count = be32_to_cpup ( p + + ) ;
2013-05-06 09:21:03 +02:00
if ( ! count )
return 0 ;
2012-05-25 18:09:55 -04:00
2013-05-06 09:21:03 +02:00
/* we recognize only 1 currently: CREDS_VALUE */
oa - > count = 1 ;
2012-05-25 18:09:55 -04:00
2013-05-06 09:21:03 +02:00
oa - > data = kmalloc ( sizeof ( struct gssx_option ) , GFP_KERNEL ) ;
if ( ! oa - > data )
return - ENOMEM ;
2012-05-25 18:09:55 -04:00
2013-05-06 09:21:03 +02:00
creds = kmalloc ( sizeof ( struct svc_cred ) , GFP_KERNEL ) ;
if ( ! creds ) {
kfree ( oa - > data ) ;
return - ENOMEM ;
2012-05-25 18:09:55 -04:00
}
2013-05-06 09:21:03 +02:00
oa - > data [ 0 ] . option . data = CREDS_VALUE ;
oa - > data [ 0 ] . option . len = sizeof ( CREDS_VALUE ) ;
oa - > data [ 0 ] . value . data = ( void * ) creds ;
oa - > data [ 0 ] . value . len = 0 ;
2012-05-25 18:09:55 -04:00
for ( i = 0 ; i < count ; i + + ) {
gssx_buffer dummy = { 0 , NULL } ;
u32 length ;
/* option buffer */
p = xdr_inline_decode ( xdr , 4 ) ;
if ( unlikely ( p = = NULL ) )
return - ENOSPC ;
length = be32_to_cpup ( p ) ;
p = xdr_inline_decode ( xdr , length ) ;
if ( unlikely ( p = = NULL ) )
return - ENOSPC ;
if ( length = = sizeof ( CREDS_VALUE ) & &
memcmp ( p , CREDS_VALUE , sizeof ( CREDS_VALUE ) ) = = 0 ) {
/* We have creds here. parse them */
err = gssx_dec_linux_creds ( xdr , creds ) ;
if ( err )
return err ;
oa - > data [ 0 ] . value . len = 1 ; /* presence */
} else {
/* consume uninteresting buffer */
err = gssx_dec_buffer ( xdr , & dummy ) ;
if ( err )
return err ;
}
}
return 0 ;
}
static int gssx_dec_status ( struct xdr_stream * xdr ,
struct gssx_status * status )
{
__be32 * p ;
int err ;
/* status->major_status */
p = xdr_inline_decode ( xdr , 8 ) ;
if ( unlikely ( p = = NULL ) )
return - ENOSPC ;
p = xdr_decode_hyper ( p , & status - > major_status ) ;
/* status->mech */
err = gssx_dec_buffer ( xdr , & status - > mech ) ;
if ( err )
return err ;
/* status->minor_status */
p = xdr_inline_decode ( xdr , 8 ) ;
if ( unlikely ( p = = NULL ) )
return - ENOSPC ;
p = xdr_decode_hyper ( p , & status - > minor_status ) ;
/* status->major_status_string */
err = gssx_dec_buffer ( xdr , & status - > major_status_string ) ;
if ( err )
return err ;
/* status->minor_status_string */
err = gssx_dec_buffer ( xdr , & status - > minor_status_string ) ;
if ( err )
return err ;
/* status->server_ctx */
err = gssx_dec_buffer ( xdr , & status - > server_ctx ) ;
if ( err )
return err ;
/* we assume we have no options for now, so simply consume them */
/* status->options */
err = dummy_dec_opt_array ( xdr , & status - > options ) ;
return err ;
}
static int gssx_enc_call_ctx ( struct xdr_stream * xdr ,
struct gssx_call_ctx * ctx )
{
struct gssx_option opt ;
__be32 * p ;
int err ;
/* ctx->locale */
err = gssx_enc_buffer ( xdr , & ctx - > locale ) ;
if ( err )
return err ;
/* ctx->server_ctx */
err = gssx_enc_buffer ( xdr , & ctx - > server_ctx ) ;
if ( err )
return err ;
/* we always want to ask for lucid contexts */
/* ctx->options */
p = xdr_reserve_space ( xdr , 4 ) ;
* p = cpu_to_be32 ( 2 ) ;
/* we want a lucid_v1 context */
opt . option . data = LUCID_OPTION ;
opt . option . len = sizeof ( LUCID_OPTION ) ;
opt . value . data = LUCID_VALUE ;
opt . value . len = sizeof ( LUCID_VALUE ) ;
err = gssx_enc_option ( xdr , & opt ) ;
/* ..and user creds */
opt . option . data = CREDS_OPTION ;
opt . option . len = sizeof ( CREDS_OPTION ) ;
opt . value . data = CREDS_VALUE ;
opt . value . len = sizeof ( CREDS_VALUE ) ;
err = gssx_enc_option ( xdr , & opt ) ;
return err ;
}
static int gssx_dec_name_attr ( struct xdr_stream * xdr ,
struct gssx_name_attr * attr )
{
int err ;
/* attr->attr */
err = gssx_dec_buffer ( xdr , & attr - > attr ) ;
if ( err )
return err ;
/* attr->value */
err = gssx_dec_buffer ( xdr , & attr - > value ) ;
if ( err )
return err ;
/* attr->extensions */
err = dummy_dec_opt_array ( xdr , & attr - > extensions ) ;
return err ;
}
static int dummy_enc_nameattr_array ( struct xdr_stream * xdr ,
struct gssx_name_attr_array * naa )
{
__be32 * p ;
if ( naa - > count ! = 0 )
return - EINVAL ;
p = xdr_reserve_space ( xdr , 4 ) ;
if ( ! p )
return - ENOSPC ;
* p = 0 ;
return 0 ;
}
static int dummy_dec_nameattr_array ( struct xdr_stream * xdr ,
struct gssx_name_attr_array * naa )
{
2013-06-07 10:11:19 -04:00
struct gssx_name_attr dummy = { . attr = { . len = 0 } } ;
2012-05-25 18:09:55 -04:00
u32 count , i ;
__be32 * p ;
p = xdr_inline_decode ( xdr , 4 ) ;
if ( unlikely ( p = = NULL ) )
return - ENOSPC ;
count = be32_to_cpup ( p + + ) ;
for ( i = 0 ; i < count ; i + + ) {
gssx_dec_name_attr ( xdr , & dummy ) ;
}
naa - > count = 0 ;
naa - > data = NULL ;
return 0 ;
}
static struct xdr_netobj zero_netobj = { } ;
static struct gssx_name_attr_array zero_name_attr_array = { } ;
static struct gssx_option_array zero_option_array = { } ;
static int gssx_enc_name ( struct xdr_stream * xdr ,
struct gssx_name * name )
{
int err ;
/* name->display_name */
err = gssx_enc_buffer ( xdr , & name - > display_name ) ;
if ( err )
return err ;
/* name->name_type */
err = gssx_enc_buffer ( xdr , & zero_netobj ) ;
if ( err )
return err ;
/* name->exported_name */
err = gssx_enc_buffer ( xdr , & zero_netobj ) ;
if ( err )
return err ;
/* name->exported_composite_name */
err = gssx_enc_buffer ( xdr , & zero_netobj ) ;
if ( err )
return err ;
/* leave name_attributes empty for now, will add once we have any
* to pass up at all */
/* name->name_attributes */
err = dummy_enc_nameattr_array ( xdr , & zero_name_attr_array ) ;
if ( err )
return err ;
/* leave options empty for now, will add once we have any options
* to pass up at all */
/* name->extensions */
err = dummy_enc_opt_array ( xdr , & zero_option_array ) ;
return err ;
}
2013-06-07 10:11:19 -04:00
2012-05-25 18:09:55 -04:00
static int gssx_dec_name ( struct xdr_stream * xdr ,
struct gssx_name * name )
{
2013-06-07 10:11:19 -04:00
struct xdr_netobj dummy_netobj = { . len = 0 } ;
struct gssx_name_attr_array dummy_name_attr_array = { . count = 0 } ;
struct gssx_option_array dummy_option_array = { . count = 0 } ;
2012-05-25 18:09:55 -04:00
int err ;
/* name->display_name */
err = gssx_dec_buffer ( xdr , & name - > display_name ) ;
if ( err )
return err ;
/* name->name_type */
err = gssx_dec_buffer ( xdr , & dummy_netobj ) ;
if ( err )
return err ;
/* name->exported_name */
err = gssx_dec_buffer ( xdr , & dummy_netobj ) ;
if ( err )
return err ;
/* name->exported_composite_name */
err = gssx_dec_buffer ( xdr , & dummy_netobj ) ;
if ( err )
return err ;
/* we assume we have no attributes for now, so simply consume them */
/* name->name_attributes */
err = dummy_dec_nameattr_array ( xdr , & dummy_name_attr_array ) ;
if ( err )
return err ;
/* we assume we have no options for now, so simply consume them */
/* name->extensions */
err = dummy_dec_opt_array ( xdr , & dummy_option_array ) ;
return err ;
}
static int dummy_enc_credel_array ( struct xdr_stream * xdr ,
struct gssx_cred_element_array * cea )
{
__be32 * p ;
if ( cea - > count ! = 0 )
return - EINVAL ;
p = xdr_reserve_space ( xdr , 4 ) ;
if ( ! p )
return - ENOSPC ;
* p = 0 ;
return 0 ;
}
static int gssx_enc_cred ( struct xdr_stream * xdr ,
struct gssx_cred * cred )
{
int err ;
/* cred->desired_name */
err = gssx_enc_name ( xdr , & cred - > desired_name ) ;
if ( err )
return err ;
/* cred->elements */
err = dummy_enc_credel_array ( xdr , & cred - > elements ) ;
/* cred->cred_handle_reference */
err = gssx_enc_buffer ( xdr , & cred - > cred_handle_reference ) ;
if ( err )
return err ;
/* cred->needs_release */
err = gssx_enc_bool ( xdr , cred - > needs_release ) ;
return err ;
}
static int gssx_enc_ctx ( struct xdr_stream * xdr ,
struct gssx_ctx * ctx )
{
__be32 * p ;
int err ;
/* ctx->exported_context_token */
err = gssx_enc_buffer ( xdr , & ctx - > exported_context_token ) ;
if ( err )
return err ;
/* ctx->state */
err = gssx_enc_buffer ( xdr , & ctx - > state ) ;
if ( err )
return err ;
/* ctx->need_release */
err = gssx_enc_bool ( xdr , ctx - > need_release ) ;
if ( err )
return err ;
/* ctx->mech */
err = gssx_enc_buffer ( xdr , & ctx - > mech ) ;
if ( err )
return err ;
/* ctx->src_name */
err = gssx_enc_name ( xdr , & ctx - > src_name ) ;
if ( err )
return err ;
/* ctx->targ_name */
err = gssx_enc_name ( xdr , & ctx - > targ_name ) ;
if ( err )
return err ;
/* ctx->lifetime */
p = xdr_reserve_space ( xdr , 8 + 8 ) ;
if ( ! p )
return - ENOSPC ;
p = xdr_encode_hyper ( p , ctx - > lifetime ) ;
/* ctx->ctx_flags */
p = xdr_encode_hyper ( p , ctx - > ctx_flags ) ;
/* ctx->locally_initiated */
err = gssx_enc_bool ( xdr , ctx - > locally_initiated ) ;
if ( err )
return err ;
/* ctx->open */
err = gssx_enc_bool ( xdr , ctx - > open ) ;
if ( err )
return err ;
/* leave options empty for now, will add once we have any options
* to pass up at all */
/* ctx->options */
err = dummy_enc_opt_array ( xdr , & ctx - > options ) ;
return err ;
}
static int gssx_dec_ctx ( struct xdr_stream * xdr ,
struct gssx_ctx * ctx )
{
__be32 * p ;
int err ;
/* ctx->exported_context_token */
err = gssx_dec_buffer ( xdr , & ctx - > exported_context_token ) ;
if ( err )
return err ;
/* ctx->state */
err = gssx_dec_buffer ( xdr , & ctx - > state ) ;
if ( err )
return err ;
/* ctx->need_release */
err = gssx_dec_bool ( xdr , & ctx - > need_release ) ;
if ( err )
return err ;
/* ctx->mech */
err = gssx_dec_buffer ( xdr , & ctx - > mech ) ;
if ( err )
return err ;
/* ctx->src_name */
err = gssx_dec_name ( xdr , & ctx - > src_name ) ;
if ( err )
return err ;
/* ctx->targ_name */
err = gssx_dec_name ( xdr , & ctx - > targ_name ) ;
if ( err )
return err ;
/* ctx->lifetime */
p = xdr_inline_decode ( xdr , 8 + 8 ) ;
if ( unlikely ( p = = NULL ) )
return - ENOSPC ;
p = xdr_decode_hyper ( p , & ctx - > lifetime ) ;
/* ctx->ctx_flags */
p = xdr_decode_hyper ( p , & ctx - > ctx_flags ) ;
/* ctx->locally_initiated */
err = gssx_dec_bool ( xdr , & ctx - > locally_initiated ) ;
if ( err )
return err ;
/* ctx->open */
err = gssx_dec_bool ( xdr , & ctx - > open ) ;
if ( err )
return err ;
/* we assume we have no options for now, so simply consume them */
/* ctx->options */
err = dummy_dec_opt_array ( xdr , & ctx - > options ) ;
return err ;
}
static int gssx_enc_cb ( struct xdr_stream * xdr , struct gssx_cb * cb )
{
__be32 * p ;
int err ;
/* cb->initiator_addrtype */
p = xdr_reserve_space ( xdr , 8 ) ;
if ( ! p )
return - ENOSPC ;
p = xdr_encode_hyper ( p , cb - > initiator_addrtype ) ;
/* cb->initiator_address */
err = gssx_enc_buffer ( xdr , & cb - > initiator_address ) ;
if ( err )
return err ;
/* cb->acceptor_addrtype */
p = xdr_reserve_space ( xdr , 8 ) ;
if ( ! p )
return - ENOSPC ;
p = xdr_encode_hyper ( p , cb - > acceptor_addrtype ) ;
/* cb->acceptor_address */
err = gssx_enc_buffer ( xdr , & cb - > acceptor_address ) ;
if ( err )
return err ;
/* cb->application_data */
err = gssx_enc_buffer ( xdr , & cb - > application_data ) ;
return err ;
}
void gssx_enc_accept_sec_context ( struct rpc_rqst * req ,
struct xdr_stream * xdr ,
struct gssx_arg_accept_sec_context * arg )
{
int err ;
err = gssx_enc_call_ctx ( xdr , & arg - > call_ctx ) ;
if ( err )
goto done ;
/* arg->context_handle */
if ( arg - > context_handle ) {
err = gssx_enc_ctx ( xdr , arg - > context_handle ) ;
if ( err )
goto done ;
} else {
err = gssx_enc_bool ( xdr , 0 ) ;
}
/* arg->cred_handle */
if ( arg - > cred_handle ) {
err = gssx_enc_cred ( xdr , arg - > cred_handle ) ;
if ( err )
goto done ;
} else {
err = gssx_enc_bool ( xdr , 0 ) ;
}
/* arg->input_token */
err = gssx_enc_in_token ( xdr , & arg - > input_token ) ;
if ( err )
goto done ;
/* arg->input_cb */
if ( arg - > input_cb ) {
err = gssx_enc_cb ( xdr , arg - > input_cb ) ;
if ( err )
goto done ;
} else {
err = gssx_enc_bool ( xdr , 0 ) ;
}
err = gssx_enc_bool ( xdr , arg - > ret_deleg_cred ) ;
if ( err )
goto done ;
/* leave options empty for now, will add once we have any options
* to pass up at all */
/* arg->options */
err = dummy_enc_opt_array ( xdr , & arg - > options ) ;
2013-08-20 18:13:27 -04:00
xdr_inline_pages ( & req - > rq_rcv_buf ,
PAGE_SIZE / 2 /* pretty arbitrary */ ,
arg - > pages , 0 /* page base */ , arg - > npages * PAGE_SIZE ) ;
2012-05-25 18:09:55 -04:00
done :
if ( err )
dprintk ( " RPC: gssx_enc_accept_sec_context: %d \n " , err ) ;
}
int gssx_dec_accept_sec_context ( struct rpc_rqst * rqstp ,
struct xdr_stream * xdr ,
struct gssx_res_accept_sec_context * res )
{
2013-05-07 17:45:20 -04:00
u32 value_follows ;
2012-05-25 18:09:55 -04:00
int err ;
/* res->status */
err = gssx_dec_status ( xdr , & res - > status ) ;
if ( err )
return err ;
/* res->context_handle */
2013-05-07 17:45:20 -04:00
err = gssx_dec_bool ( xdr , & value_follows ) ;
if ( err )
return err ;
if ( value_follows ) {
2012-05-25 18:09:55 -04:00
err = gssx_dec_ctx ( xdr , res - > context_handle ) ;
if ( err )
return err ;
} else {
res - > context_handle = NULL ;
}
/* res->output_token */
2013-05-07 17:45:20 -04:00
err = gssx_dec_bool ( xdr , & value_follows ) ;
if ( err )
return err ;
if ( value_follows ) {
2012-05-25 18:09:55 -04:00
err = gssx_dec_buffer ( xdr , res - > output_token ) ;
if ( err )
return err ;
} else {
res - > output_token = NULL ;
}
/* res->delegated_cred_handle */
2013-05-07 17:45:20 -04:00
err = gssx_dec_bool ( xdr , & value_follows ) ;
if ( err )
return err ;
if ( value_follows ) {
2012-05-25 18:09:55 -04:00
/* we do not support upcall servers sending this data. */
return - EINVAL ;
}
/* res->options */
err = gssx_dec_option_array ( xdr , & res - > options ) ;
return err ;
}