2019-02-11 19:25:15 +03:00
// SPDX-License-Identifier: GPL-2.0+
2012-05-26 02:09:55 +04:00
/*
* linux / net / sunrpc / gss_rpc_upcall . c
*
* Copyright ( C ) 2012 Simo Sorce < simo @ redhat . com >
*/
# include <linux/types.h>
# include <linux/un.h>
# include <linux/sunrpc/svcauth.h>
# include "gss_rpc_upcall.h"
# define GSSPROXY_SOCK_PATHNAME " / var / run / gssproxy.sock"
# define GSSPROXY_PROGRAM (400112u)
# define GSSPROXY_VERS_1 (1u)
/*
* Encoding / Decoding functions
*/
enum {
GSSX_NULL = 0 , /* Unused */
GSSX_INDICATE_MECHS = 1 ,
GSSX_GET_CALL_CONTEXT = 2 ,
GSSX_IMPORT_AND_CANON_NAME = 3 ,
GSSX_EXPORT_CRED = 4 ,
GSSX_IMPORT_CRED = 5 ,
GSSX_ACQUIRE_CRED = 6 ,
GSSX_STORE_CRED = 7 ,
GSSX_INIT_SEC_CONTEXT = 8 ,
GSSX_ACCEPT_SEC_CONTEXT = 9 ,
GSSX_RELEASE_HANDLE = 10 ,
GSSX_GET_MIC = 11 ,
GSSX_VERIFY = 12 ,
GSSX_WRAP = 13 ,
GSSX_UNWRAP = 14 ,
GSSX_WRAP_SIZE_LIMIT = 15 ,
} ;
# define PROC(proc, name) \
[ GSSX_ # # proc ] = { \
. p_proc = GSSX_ # # proc , \
2017-05-08 15:54:06 +03:00
. p_encode = gssx_enc_ # # name , \
2017-05-08 16:03:02 +03:00
. p_decode = gssx_dec_ # # name , \
2012-05-26 02:09:55 +04:00
. p_arglen = GSSX_ARG_ # # name # # _sz , \
. p_replen = GSSX_RES_ # # name # # _sz , \
. p_statidx = GSSX_ # # proc , \
. p_name = # proc , \
}
2017-05-12 16:36:49 +03:00
static const struct rpc_procinfo gssp_procedures [ ] = {
2012-05-26 02:09:55 +04:00
PROC ( INDICATE_MECHS , indicate_mechs ) ,
PROC ( GET_CALL_CONTEXT , get_call_context ) ,
PROC ( IMPORT_AND_CANON_NAME , import_and_canon_name ) ,
PROC ( EXPORT_CRED , export_cred ) ,
PROC ( IMPORT_CRED , import_cred ) ,
PROC ( ACQUIRE_CRED , acquire_cred ) ,
PROC ( STORE_CRED , store_cred ) ,
PROC ( INIT_SEC_CONTEXT , init_sec_context ) ,
PROC ( ACCEPT_SEC_CONTEXT , accept_sec_context ) ,
PROC ( RELEASE_HANDLE , release_handle ) ,
PROC ( GET_MIC , get_mic ) ,
PROC ( VERIFY , verify ) ,
PROC ( WRAP , wrap ) ,
PROC ( UNWRAP , unwrap ) ,
PROC ( WRAP_SIZE_LIMIT , wrap_size_limit ) ,
} ;
/*
* Common transport functions
*/
static const struct rpc_program gssp_program ;
static int gssp_rpc_create ( struct net * net , struct rpc_clnt * * _clnt )
{
static const struct sockaddr_un gssp_localaddr = {
. sun_family = AF_LOCAL ,
. sun_path = GSSPROXY_SOCK_PATHNAME ,
} ;
struct rpc_create_args args = {
. net = net ,
. protocol = XPRT_TRANSPORT_LOCAL ,
. address = ( struct sockaddr * ) & gssp_localaddr ,
. addrsize = sizeof ( gssp_localaddr ) ,
. servername = " localhost " ,
. program = & gssp_program ,
. version = GSSPROXY_VERS_1 ,
. authflavor = RPC_AUTH_NULL ,
/*
* Note we want connection to be done in the caller ' s
* filesystem namespace . We therefore turn off the idle
* timeout , which would result in reconnections being
* done without the correct namespace :
*/
2022-05-07 20:48:21 +03:00
. flags = RPC_CLNT_CREATE_NOPING |
2022-05-07 20:53:59 +03:00
RPC_CLNT_CREATE_CONNECTED |
2012-05-26 02:09:55 +04:00
RPC_CLNT_CREATE_NO_IDLE_TIMEOUT
} ;
struct rpc_clnt * clnt ;
int result = 0 ;
clnt = rpc_create ( & args ) ;
if ( IS_ERR ( clnt ) ) {
dprintk ( " RPC: failed to create AF_LOCAL gssproxy "
" client (errno %ld). \n " , PTR_ERR ( clnt ) ) ;
2013-06-11 00:06:44 +04:00
result = PTR_ERR ( clnt ) ;
2012-05-26 02:09:55 +04:00
* _clnt = NULL ;
goto out ;
}
dprintk ( " RPC: created new gssp local client (gssp_local_clnt: "
" %p) \n " , clnt ) ;
* _clnt = clnt ;
out :
return result ;
}
void init_gssp_clnt ( struct sunrpc_net * sn )
{
mutex_init ( & sn - > gssp_lock ) ;
sn - > gssp_clnt = NULL ;
}
int set_gssp_clnt ( struct net * net )
{
struct sunrpc_net * sn = net_generic ( net , sunrpc_net_id ) ;
struct rpc_clnt * clnt ;
int ret ;
mutex_lock ( & sn - > gssp_lock ) ;
ret = gssp_rpc_create ( net , & clnt ) ;
if ( ! ret ) {
if ( sn - > gssp_clnt )
rpc_shutdown_client ( sn - > gssp_clnt ) ;
sn - > gssp_clnt = clnt ;
}
mutex_unlock ( & sn - > gssp_lock ) ;
return ret ;
}
void clear_gssp_clnt ( struct sunrpc_net * sn )
{
mutex_lock ( & sn - > gssp_lock ) ;
if ( sn - > gssp_clnt ) {
rpc_shutdown_client ( sn - > gssp_clnt ) ;
sn - > gssp_clnt = NULL ;
}
mutex_unlock ( & sn - > gssp_lock ) ;
}
static struct rpc_clnt * get_gssp_clnt ( struct sunrpc_net * sn )
{
struct rpc_clnt * clnt ;
mutex_lock ( & sn - > gssp_lock ) ;
clnt = sn - > gssp_clnt ;
if ( clnt )
2021-07-26 15:01:27 +03:00
refcount_inc ( & clnt - > cl_count ) ;
2012-05-26 02:09:55 +04:00
mutex_unlock ( & sn - > gssp_lock ) ;
return clnt ;
}
static int gssp_call ( struct net * net , struct rpc_message * msg )
{
struct sunrpc_net * sn = net_generic ( net , sunrpc_net_id ) ;
struct rpc_clnt * clnt ;
int status ;
clnt = get_gssp_clnt ( sn ) ;
if ( ! clnt )
return - EIO ;
status = rpc_call_sync ( clnt , msg , 0 ) ;
if ( status < 0 ) {
dprintk ( " gssp: rpc_call returned error %d \n " , - status ) ;
switch ( status ) {
case - EPROTONOSUPPORT :
status = - EINVAL ;
break ;
case - ECONNREFUSED :
case - ETIMEDOUT :
case - ENOTCONN :
status = - EAGAIN ;
break ;
case - ERESTARTSYS :
if ( signalled ( ) )
status = - EINTR ;
break ;
default :
break ;
}
}
rpc_release_client ( clnt ) ;
return status ;
}
2013-08-21 02:13:27 +04:00
static void gssp_free_receive_pages ( struct gssx_arg_accept_sec_context * arg )
{
2020-11-24 23:48:01 +03:00
unsigned int i ;
2013-08-21 02:13:27 +04:00
for ( i = 0 ; i < arg - > npages & & arg - > pages [ i ] ; i + + )
__free_page ( arg - > pages [ i ] ) ;
2015-02-14 00:11:51 +03:00
kfree ( arg - > pages ) ;
2013-08-21 02:13:27 +04:00
}
static int gssp_alloc_receive_pages ( struct gssx_arg_accept_sec_context * arg )
{
2020-11-24 23:48:01 +03:00
unsigned int i ;
2013-08-21 02:13:27 +04:00
arg - > npages = DIV_ROUND_UP ( NGROUPS_MAX * 4 , PAGE_SIZE ) ;
treewide: kzalloc() -> kcalloc()
The kzalloc() function has a 2-factor argument form, kcalloc(). This
patch replaces cases of:
kzalloc(a * b, gfp)
with:
kcalloc(a * b, gfp)
as well as handling cases of:
kzalloc(a * b * c, gfp)
with:
kzalloc(array3_size(a, b, c), gfp)
as it's slightly less ugly than:
kzalloc_array(array_size(a, b), c, gfp)
This does, however, attempt to ignore constant size factors like:
kzalloc(4 * 1024, gfp)
though any constants defined via macros get caught up in the conversion.
Any factors with a sizeof() of "unsigned char", "char", and "u8" were
dropped, since they're redundant.
The Coccinelle script used for this was:
// Fix redundant parens around sizeof().
@@
type TYPE;
expression THING, E;
@@
(
kzalloc(
- (sizeof(TYPE)) * E
+ sizeof(TYPE) * E
, ...)
|
kzalloc(
- (sizeof(THING)) * E
+ sizeof(THING) * E
, ...)
)
// Drop single-byte sizes and redundant parens.
@@
expression COUNT;
typedef u8;
typedef __u8;
@@
(
kzalloc(
- sizeof(u8) * (COUNT)
+ COUNT
, ...)
|
kzalloc(
- sizeof(__u8) * (COUNT)
+ COUNT
, ...)
|
kzalloc(
- sizeof(char) * (COUNT)
+ COUNT
, ...)
|
kzalloc(
- sizeof(unsigned char) * (COUNT)
+ COUNT
, ...)
|
kzalloc(
- sizeof(u8) * COUNT
+ COUNT
, ...)
|
kzalloc(
- sizeof(__u8) * COUNT
+ COUNT
, ...)
|
kzalloc(
- sizeof(char) * COUNT
+ COUNT
, ...)
|
kzalloc(
- sizeof(unsigned char) * COUNT
+ COUNT
, ...)
)
// 2-factor product with sizeof(type/expression) and identifier or constant.
@@
type TYPE;
expression THING;
identifier COUNT_ID;
constant COUNT_CONST;
@@
(
- kzalloc
+ kcalloc
(
- sizeof(TYPE) * (COUNT_ID)
+ COUNT_ID, sizeof(TYPE)
, ...)
|
- kzalloc
+ kcalloc
(
- sizeof(TYPE) * COUNT_ID
+ COUNT_ID, sizeof(TYPE)
, ...)
|
- kzalloc
+ kcalloc
(
- sizeof(TYPE) * (COUNT_CONST)
+ COUNT_CONST, sizeof(TYPE)
, ...)
|
- kzalloc
+ kcalloc
(
- sizeof(TYPE) * COUNT_CONST
+ COUNT_CONST, sizeof(TYPE)
, ...)
|
- kzalloc
+ kcalloc
(
- sizeof(THING) * (COUNT_ID)
+ COUNT_ID, sizeof(THING)
, ...)
|
- kzalloc
+ kcalloc
(
- sizeof(THING) * COUNT_ID
+ COUNT_ID, sizeof(THING)
, ...)
|
- kzalloc
+ kcalloc
(
- sizeof(THING) * (COUNT_CONST)
+ COUNT_CONST, sizeof(THING)
, ...)
|
- kzalloc
+ kcalloc
(
- sizeof(THING) * COUNT_CONST
+ COUNT_CONST, sizeof(THING)
, ...)
)
// 2-factor product, only identifiers.
@@
identifier SIZE, COUNT;
@@
- kzalloc
+ kcalloc
(
- SIZE * COUNT
+ COUNT, SIZE
, ...)
// 3-factor product with 1 sizeof(type) or sizeof(expression), with
// redundant parens removed.
@@
expression THING;
identifier STRIDE, COUNT;
type TYPE;
@@
(
kzalloc(
- sizeof(TYPE) * (COUNT) * (STRIDE)
+ array3_size(COUNT, STRIDE, sizeof(TYPE))
, ...)
|
kzalloc(
- sizeof(TYPE) * (COUNT) * STRIDE
+ array3_size(COUNT, STRIDE, sizeof(TYPE))
, ...)
|
kzalloc(
- sizeof(TYPE) * COUNT * (STRIDE)
+ array3_size(COUNT, STRIDE, sizeof(TYPE))
, ...)
|
kzalloc(
- sizeof(TYPE) * COUNT * STRIDE
+ array3_size(COUNT, STRIDE, sizeof(TYPE))
, ...)
|
kzalloc(
- sizeof(THING) * (COUNT) * (STRIDE)
+ array3_size(COUNT, STRIDE, sizeof(THING))
, ...)
|
kzalloc(
- sizeof(THING) * (COUNT) * STRIDE
+ array3_size(COUNT, STRIDE, sizeof(THING))
, ...)
|
kzalloc(
- sizeof(THING) * COUNT * (STRIDE)
+ array3_size(COUNT, STRIDE, sizeof(THING))
, ...)
|
kzalloc(
- sizeof(THING) * COUNT * STRIDE
+ array3_size(COUNT, STRIDE, sizeof(THING))
, ...)
)
// 3-factor product with 2 sizeof(variable), with redundant parens removed.
@@
expression THING1, THING2;
identifier COUNT;
type TYPE1, TYPE2;
@@
(
kzalloc(
- sizeof(TYPE1) * sizeof(TYPE2) * COUNT
+ array3_size(COUNT, sizeof(TYPE1), sizeof(TYPE2))
, ...)
|
kzalloc(
- sizeof(TYPE1) * sizeof(THING2) * (COUNT)
+ array3_size(COUNT, sizeof(TYPE1), sizeof(TYPE2))
, ...)
|
kzalloc(
- sizeof(THING1) * sizeof(THING2) * COUNT
+ array3_size(COUNT, sizeof(THING1), sizeof(THING2))
, ...)
|
kzalloc(
- sizeof(THING1) * sizeof(THING2) * (COUNT)
+ array3_size(COUNT, sizeof(THING1), sizeof(THING2))
, ...)
|
kzalloc(
- sizeof(TYPE1) * sizeof(THING2) * COUNT
+ array3_size(COUNT, sizeof(TYPE1), sizeof(THING2))
, ...)
|
kzalloc(
- sizeof(TYPE1) * sizeof(THING2) * (COUNT)
+ array3_size(COUNT, sizeof(TYPE1), sizeof(THING2))
, ...)
)
// 3-factor product, only identifiers, with redundant parens removed.
@@
identifier STRIDE, SIZE, COUNT;
@@
(
kzalloc(
- (COUNT) * STRIDE * SIZE
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
|
kzalloc(
- COUNT * (STRIDE) * SIZE
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
|
kzalloc(
- COUNT * STRIDE * (SIZE)
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
|
kzalloc(
- (COUNT) * (STRIDE) * SIZE
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
|
kzalloc(
- COUNT * (STRIDE) * (SIZE)
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
|
kzalloc(
- (COUNT) * STRIDE * (SIZE)
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
|
kzalloc(
- (COUNT) * (STRIDE) * (SIZE)
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
|
kzalloc(
- COUNT * STRIDE * SIZE
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
)
// Any remaining multi-factor products, first at least 3-factor products,
// when they're not all constants...
@@
expression E1, E2, E3;
constant C1, C2, C3;
@@
(
kzalloc(C1 * C2 * C3, ...)
|
kzalloc(
- (E1) * E2 * E3
+ array3_size(E1, E2, E3)
, ...)
|
kzalloc(
- (E1) * (E2) * E3
+ array3_size(E1, E2, E3)
, ...)
|
kzalloc(
- (E1) * (E2) * (E3)
+ array3_size(E1, E2, E3)
, ...)
|
kzalloc(
- E1 * E2 * E3
+ array3_size(E1, E2, E3)
, ...)
)
// And then all remaining 2 factors products when they're not all constants,
// keeping sizeof() as the second factor argument.
@@
expression THING, E1, E2;
type TYPE;
constant C1, C2, C3;
@@
(
kzalloc(sizeof(THING) * C2, ...)
|
kzalloc(sizeof(TYPE) * C2, ...)
|
kzalloc(C1 * C2 * C3, ...)
|
kzalloc(C1 * C2, ...)
|
- kzalloc
+ kcalloc
(
- sizeof(TYPE) * (E2)
+ E2, sizeof(TYPE)
, ...)
|
- kzalloc
+ kcalloc
(
- sizeof(TYPE) * E2
+ E2, sizeof(TYPE)
, ...)
|
- kzalloc
+ kcalloc
(
- sizeof(THING) * (E2)
+ E2, sizeof(THING)
, ...)
|
- kzalloc
+ kcalloc
(
- sizeof(THING) * E2
+ E2, sizeof(THING)
, ...)
|
- kzalloc
+ kcalloc
(
- (E1) * E2
+ E1, E2
, ...)
|
- kzalloc
+ kcalloc
(
- (E1) * (E2)
+ E1, E2
, ...)
|
- kzalloc
+ kcalloc
(
- E1 * E2
+ E1, E2
, ...)
)
Signed-off-by: Kees Cook <keescook@chromium.org>
2018-06-13 00:03:40 +03:00
arg - > pages = kcalloc ( arg - > npages , sizeof ( struct page * ) , GFP_KERNEL ) ;
2013-08-24 01:26:28 +04:00
if ( ! arg - > pages )
return - ENOMEM ;
2020-11-24 23:48:01 +03:00
for ( i = 0 ; i < arg - > npages ; i + + ) {
arg - > pages [ i ] = alloc_page ( GFP_KERNEL ) ;
if ( ! arg - > pages [ i ] ) {
gssp_free_receive_pages ( arg ) ;
return - ENOMEM ;
}
}
2013-08-21 02:13:27 +04:00
return 0 ;
}
2012-05-26 02:09:55 +04:00
2018-08-16 19:05:59 +03:00
static char * gssp_stringify ( struct xdr_netobj * netobj )
{
2020-05-08 15:40:00 +03:00
return kmemdup_nul ( netobj - > data , netobj - > len , GFP_KERNEL ) ;
2018-08-16 19:05:59 +03:00
}
static void gssp_hostbased_service ( char * * principal )
{
char * c ;
if ( ! * principal )
return ;
/* terminate and remove realm part */
c = strchr ( * principal , ' @ ' ) ;
if ( c ) {
* c = ' \0 ' ;
/* change service-hostname delimiter */
c = strchr ( * principal , ' / ' ) ;
if ( c )
* c = ' @ ' ;
}
if ( ! c ) {
/* not a service principal */
kfree ( * principal ) ;
* principal = NULL ;
}
}
2012-05-26 02:09:55 +04:00
/*
* Public functions
*/
/* numbers somewhat arbitrary but large enough for current needs */
# define GSSX_MAX_OUT_HANDLE 128
# define GSSX_MAX_SRC_PRINC 256
# define GSSX_KMEMBUF (GSSX_max_output_handle_sz + \
GSSX_max_oid_sz + \
GSSX_max_princ_sz + \
sizeof ( struct svc_cred ) )
int gssp_accept_sec_context_upcall ( struct net * net ,
struct gssp_upcall_data * data )
{
struct gssx_ctx ctxh = {
. state = data - > in_handle
} ;
struct gssx_arg_accept_sec_context arg = {
. input_token = data - > in_token ,
} ;
struct gssx_ctx rctxh = {
/*
* pass in the max length we expect for each of these
* buffers but let the xdr code kmalloc them :
*/
. exported_context_token . len = GSSX_max_output_handle_sz ,
2013-04-29 22:03:30 +04:00
. mech . len = GSS_OID_MAX_LEN ,
2018-08-16 19:05:59 +03:00
. targ_name . display_name . len = GSSX_max_princ_sz ,
2012-05-26 02:09:55 +04:00
. src_name . display_name . len = GSSX_max_princ_sz
} ;
struct gssx_res_accept_sec_context res = {
. context_handle = & rctxh ,
. output_token = & data - > out_token
} ;
struct rpc_message msg = {
. rpc_proc = & gssp_procedures [ GSSX_ACCEPT_SEC_CONTEXT ] ,
. rpc_argp = & arg ,
. rpc_resp = & res ,
. rpc_cred = NULL , /* FIXME ? */
} ;
struct xdr_netobj client_name = { 0 , NULL } ;
2018-08-16 19:05:59 +03:00
struct xdr_netobj target_name = { 0 , NULL } ;
2012-05-26 02:09:55 +04:00
int ret ;
if ( data - > in_handle . len ! = 0 )
arg . context_handle = & ctxh ;
res . output_token - > len = GSSX_max_output_token_sz ;
2013-08-21 02:13:27 +04:00
ret = gssp_alloc_receive_pages ( & arg ) ;
if ( ret )
return ret ;
2012-05-26 02:09:55 +04:00
ret = gssp_call ( net , & msg ) ;
2013-08-21 02:13:27 +04:00
gssp_free_receive_pages ( & arg ) ;
2012-05-26 02:09:55 +04:00
/* we need to fetch all data even in case of error so
* that we can free special strctures is they have been allocated */
data - > major_status = res . status . major_status ;
data - > minor_status = res . status . minor_status ;
if ( res . context_handle ) {
data - > out_handle = rctxh . exported_context_token ;
2013-04-29 22:03:30 +04:00
data - > mech_oid . len = rctxh . mech . len ;
2018-05-19 00:13:04 +03:00
if ( rctxh . mech . data ) {
2013-10-08 23:33:53 +04:00
memcpy ( data - > mech_oid . data , rctxh . mech . data ,
2013-04-29 22:03:30 +04:00
data - > mech_oid . len ) ;
2018-05-19 00:13:04 +03:00
kfree ( rctxh . mech . data ) ;
}
2012-05-26 02:09:55 +04:00
client_name = rctxh . src_name . display_name ;
2018-08-16 19:05:59 +03:00
target_name = rctxh . targ_name . display_name ;
2012-05-26 02:09:55 +04:00
}
if ( res . options . count = = 1 ) {
gssx_buffer * value = & res . options . data [ 0 ] . value ;
/* Currently we only decode CREDS_VALUE, if we add
* anything else we ' ll have to loop and match on the
* option name */
if ( value - > len = = 1 ) {
/* steal group info from struct svc_cred */
data - > creds = * ( struct svc_cred * ) value - > data ;
data - > found_creds = 1 ;
}
/* whether we use it or not, free data */
kfree ( value - > data ) ;
}
if ( res . options . count ! = 0 ) {
kfree ( res . options . data ) ;
}
/* convert to GSS_NT_HOSTBASED_SERVICE form and set into creds */
2018-08-16 19:05:59 +03:00
if ( data - > found_creds ) {
if ( client_name . data ) {
data - > creds . cr_raw_principal =
gssp_stringify ( & client_name ) ;
data - > creds . cr_principal =
gssp_stringify ( & client_name ) ;
gssp_hostbased_service ( & data - > creds . cr_principal ) ;
}
if ( target_name . data ) {
data - > creds . cr_targ_princ =
gssp_stringify ( & target_name ) ;
gssp_hostbased_service ( & data - > creds . cr_targ_princ ) ;
2012-05-26 02:09:55 +04:00
}
}
kfree ( client_name . data ) ;
2018-08-16 19:05:59 +03:00
kfree ( target_name . data ) ;
2012-05-26 02:09:55 +04:00
return ret ;
}
void gssp_free_upcall_data ( struct gssp_upcall_data * data )
{
kfree ( data - > in_handle . data ) ;
kfree ( data - > out_handle . data ) ;
kfree ( data - > out_token . data ) ;
free_svc_cred ( & data - > creds ) ;
}
/*
* Initialization stuff
*/
2017-05-09 00:27:10 +03:00
static unsigned int gssp_version1_counts [ ARRAY_SIZE ( gssp_procedures ) ] ;
2012-05-26 02:09:55 +04:00
static const struct rpc_version gssp_version1 = {
. number = GSSPROXY_VERS_1 ,
. nrprocs = ARRAY_SIZE ( gssp_procedures ) ,
. procs = gssp_procedures ,
2017-05-09 00:27:10 +03:00
. counts = gssp_version1_counts ,
2012-05-26 02:09:55 +04:00
} ;
static const struct rpc_version * gssp_version [ ] = {
NULL ,
& gssp_version1 ,
} ;
static struct rpc_stat gssp_stats ;
static const struct rpc_program gssp_program = {
. name = " gssproxy " ,
. number = GSSPROXY_PROGRAM ,
. nrvers = ARRAY_SIZE ( gssp_version ) ,
. version = gssp_version ,
. stats = & gssp_stats ,
} ;