Check out the new USENIX Web site.

Home About USENIX Events Membership Publications Students
Sixth USENIX Security Symposium

Pp. 51–66 of the Proceedings


Security Mechanism Independence in ONC RPC

Mike Eisler, SunSoft, Inc., mre@eng.sun.com
Roland J. Schemers III, Stanford University, schemers@leland.stanford.edu
Raj Srinivasan, ControlNet, Inc., rajsrin@aol.com
(Note: Roland Schemers and Raj Srinivasan contributed to this work while employed at SunSoft, Inc.)

ABSTRACT

Generic Security Services API (GSS-API) [4] provides a framework for security services. It allows source level portability. It allows applications to run independently of the underlying security mechanisms and technologies.

To provide security mechanism independence in ONC RPC [1, 2, 3], this paper proposes a new security flavor, RPCSEC_GSS. RPCSEC_GSS incorporates services offered by the GSS-API into ONC RPC. Using the programming interface for the RPCSEC_GSS flavor, ONC RPC applications can specify a GSS-API security mechanism to be used with an RPC session, and also request security services, such as integrity and privacy.

CONTENTS

INTRODUCTION

This document describes the proposed security architecture for ONC RPC. The reader of this document should understand the GSS-API and be familiar with ONC RPC.

REQUIREMENTS

The security framework proposed in this document is driven by the following requirements:

ONC RPC FLAVORS

The Solaris 2.x Network Interfaces Programming Guide describes how security flavors work in ONC RPC.

To date, security flavors have provided only authentication services of varying strengths. The AUTH_DES and AUTH_KERB (version 4) security flavors use cryptography to provide stronger authentication than the AUTH_SYS security flavor, which does not use cryptography and therefore can be more easily spoofed.

Historically, security flavors have been called authentication flavors. This is probably because integrity and privacy were not considered requirements. Also, the computational overhead of integrity and privacy was considered prohibitive for an application like the NFS environment (which drove RPC architecture for the main part). Today, no fundamental reason exists for restricting new security flavors in the services they perform.

Security Mechanisms vs. Security Flavors

Examples of security mechanisms are Kerberos V5, RSA public key, Diffie-Hellman public key, PGP, etc. A security mechanism specifies cryptographic techniques to achieve data authentication or confidentiality. Usually, a security mechanism does not specify transport protocols or conventions; security mechanisms can be used in conjunction with different data transport protocols. For example, the Kerberos V5 [6, 9] mechanism specifies certain conventions and formats that allow it to be used with ONC RPC, FTP, TELNET, or DCE RPC.

Security flavors are specific to ONC RPC and are administered by iana@isi.edu. Each security flavor is represented by a unique integer and is carried in the RPC protocol header. A security flavor indicates how to interpret RPC security headers (the credential and verifier fields) and client data. Until now, since integrity and privacy have not been used, client data formats have been the same for all security flavors. Also, existing security flavors embody different security mechanisms, and each security flavor has its own API. Currently, ONC RPC applications that require security services use security flavors that embody specific security mechanisms. These applications have to be modified to make use of other security mechanisms.

THE GSS-API FRAMEWORK

The GSS-API framework defines a generic security service standard and an API that offers the same security services to applications, while using multiple underlying security mechanisms. The GSS-API framework is described in [4] and its C language bindings are described in [5]. This API framework has some similarities to TLI. It ``normalizes'' accesses to different security mechanisms, so that applications using the GSS-API need not be aware of underlying security mechanisms.

Figure 1 illustrates this concept:

The basic services offered by the GSS-API are integrity and privacy. In the integrity service, the GSS-API uses the underlying mechanism to authenticate messages exchanged between applications. Cryptographic checksums establish the identity of the data originator to the receiver, the identity of the receiver to the originator (if mutual authentication is requested), and the authenticity of the transmitted data. The privacy service includes the integrity service. In addition, the transmitted data is encrypted to protect it from any eavesdroppers.

The GSS-API does not specify any transport mechanisms. Calls to the GSS-API primitives return tokens, which the application must convey to its peer. For example, if the application is ONC RPC, clients will convey the tokens created by the GSS-API to servers and vice-versa, in RPC messages. These tokens may represent entities such as cryptographic checksums or encrypted data.

Before an application begins to exchange data with a peer, it must first establish a context for the exchange. The GSS-API primitive that creates the context requires a parameter that specifies the security mechanism to be used for that context. Applications that wish to be independent of the security mechanism must provide this parameter transparently, by obtaining it either from a configuration file, or the name service.

Once a context is established, applications can begin to exchange data units. For each data unit, the application can specify whether the integrity service or the privacy service or neither applies. The GSS-API defines these services. They are independent of the security mechanism. The services may be determined dynamically or hard-coded by the application without compromising security mechanism independence.

For each data unit, the application can also specify the Quality of Protection (QOP) to be used. The QOP parameter determines the cryptographic algorithms to be used in conjunction with the integrity or privacy service. This parameter is security mechanism dependent. Therefore, to be independent of the underlying security mechanism, an application has to either obtain the QOP parameter from a configuration file or name service, or use the default QOP for the security mechanism.

RPCSEC_GSS

This paper proposes a new security flavor, RPCSEC_GSS, to meet the requirements stated earlier. The RPCSEC_GSS security flavor incorporates services offered by the GSS-API into ONC RPC. Figure 2 illustrates the layering of the RPCSEC_GSS security flavor interfaces above the GSS-API:

Using the programming interface for the RPCSEC_GSS security flavor, ONC RPC applications can specify a security mechanism to be used on an RPC session, and request desired security services (integrity, privacy, or none) for each RPC exchange. Applications can also specify the desired QOP for each RPC exchange. As noted before, if the application needs to be independent of the security mechanism, it should not hard code security mechanism names or QOP values. These parameters will be passed through the RPCSEC_GSS layer to the GSS-API layer.

Since the RPCSEC_GSS security flavor uses the GSS-API interface, any security mechanism supported by the GSS-API will be available to ONC RPC applications via the RPCSEC_GSS security flavor interface. Thus, in some sense, this will be the ``last security flavor'' that ONC RPC needs to support to use multiple underlying security mechanisms. Moreover, adding a new security mechanism to the existing set supported by the GSS-API will not require an ONC RPC application to be modified for use with the new security mechanism. Of course, if a security mechanism is created that offers different services than those provided by the GSS-API, and that cannot be supported under the GSS-API, it also will not be available with the RPCSEC_GSS flavor. The new security mechanism will require a different API to capture the sense of the different services it offers.

The programming paradigm for the RPCSEC_GSS security flavor is similar to that for the existing security flavors. The additional complexity is due to the extra services and options available.

THE RPCSEC_GSS PROGRAMMING INTERFACE

The following section describes the programming interface for the RPCSEC_GSS security flavor, followed by some usage examples.

This programming interface is the same for kernel and user-level RPC implementations, unless indicated otherwise.

The following definitions are used:

#define MAX_GSS_MECH 64
#define MAX_GSS_NAME 128

/* security service types */
typedef enum {
	rpc_gss_svc_default = 0,
	rpc_gss_svc_none = 1,
	rpc_gss_svc_integrity = 2,
	rpc_gss_svc_privacy = 3
} rpc_gss_service_t;

/* GSS input options */
typedef struct {
	int req_flags;
	int time_req;
#ifdef _KERNEL
	cred_t *my_cred;
#else
	gss_cred_id_t my_cred;
#endif
	gss_channel_bindings_t 	
	     input_channel_bindings;
} rpc_gss_options_req_t;

/* GSS output options */
typedef struct {
	int major_status;
	int minor_status;
	int rpcsec_version;
	int ret_flags;
	int time_ret;
	gss_ctx_id_t gss_context;
	char actual_mechanism[MAX_GSS_MECH];
} rpc_gss_options_ret_t;

Creating a Security Context

The rpc_gss_seccreate function shown below is used to create a security context.

AUTH *rpc_gss_seccreate(
CLIENT *clnt, /* in */
	char *principal,     /* in */
	char *mechanism,     /* in */
	rpc_gss_service_t service_type, /* in */
	char  *qop,          /* in */
	rpc_gss_options_req_t
	     *options_req,						/*in */
	rpc_gss_options_ret_t
	     *options_ret);	/* out */

rpc_gss_seccreate() returns a security context handle (the authentication handle). The parameters of this function are:

Destroying a Security Context

The usual call:

    void auth_destroy(AUTH *auth);
is used to destroy a context.

Setting the Server Principal Names

The server needs to be told the names of the principals it will be representing when it starts up. A server may act as more than one principal. The following call sets the server's principal name:

bool_t
rpc_gss_set_svc_name(
	char    *principal;
	char    *mechanism;
	u_int   req_time;
	u_int   program;
	u_int   version);
The parameters are as follows:

If a server wishes to act as multiple principals, it may invoke rpc_gss_set_svc_name as many times as necessary.

Receiving Credentials at the Server

The rpc_gss_getcred function is used to obtain the client's credentials.

/* pointer to (opaque) principal type */
typedef struct {
	int    len;
	char   name[1];
} *rpc_gss_principal_t;

/* raw credentials */
typedef struct {
	int    version;
	char   *mechanism;
	char   *qop;
	rpc_gss_principal_t client_principal;
	char   *svc_principal;
	rpc_gss_service_t service;
} rpc_gss_rawcred_t;

/* unix credentials */
typedef struct {
	uid_t   uid;
	gid_t   gid;
	short   gidlen;
	gid_t   *gidlist;
} rpc_gss_ucred_t;

int rpc_gss_getcred(
	struct svc_req *req, /* in */
	struct rpc_gss_rawcred_t **rcred, /*out*/
	struct rpc_gss_ucred_t **ucred, /* out */
	void **cookie); /* out */
rpc_gss_getcred() allows the server to fetch the credentials of the client. Its parameters are:

Note that the credentials returned need not be destroyed or deallocated because they are pointers to entities within the security context. The application should not modify them.

Server Callbacks

A server may need to specify a callback so that it knows when a context gets established. This callback may be specified through the rpc_gss_set_callback() call:

typedef struct {
	u_int		program;
	u_int		version;
	bool_t		(*callback)();
} rpc_gss_callback_t;

bool_t 
rpc_gss_set_callback(rpc_gss_callback_t *cb);
The callback routine will be invoked any time a context is established for the specified program and version. This routine must be MT safe, since contexts can be established concurrently. This routine should return TRUE if the context is to be accepted, and FALSE otherwise:

bool_t callback(
	struct svc_req *req,
	gss_cred_id_t  deleg,
	gss_ctx_id_t   gss_context;
	rpc_gss_lock_t *lock,
	void           **cookie);
The rpc_gss_lock_t structure is defined as follows:

typedef struct {
	bool_t            locked;
	rpc_gss_rawcred_t *raw_cred;
} rpc_gss_lock_t;
The server can receive any delegated credentials at this point. The server also gets a pointer to the GSS context in case it wants to do GSS operations on the context to test for acceptance criteria. The per-message information in the raw_cred structure (qop, service) will be set to the values specified by the initiator for these parameters in the rpc_gss_seccreate() call.

The server also gets a pointer to the RPC request associated with the context initiation request. This will enable the server to determine the network address of the initiator, for example, by using existing calls.

The lock parameter enables the server to enforce a particular QOP and service for the session. When the callback is invoked, the lock parameter will point to a structure whose type is rpc_gss_lock_t. The locked field in this structure will be set to FALSE. The server should set locked to TRUE, to lock the context. A context that is ``locked'' in this way will reject all requests that have different values for service or QOP than those specified in the raw_cred structure. (These should not be changed by the server.)

The server can also specify a cookie that will be returned along with the caller's credentials for each request (in the rpc_gss_getcred() call). This cookie can be used in anyway appropriate for the application - RPC will not interpret this quantity. For example, the cookie may be a pointer or index to a structure that represents the context initiator. Instead of computing this for every request, the server does this at context creation time, and saves on request processing time.

If the server does not specify a callback, all incoming contexts will be accepted.

Changing Default Parameters for a Session

The rpc_gss_set_defaults function changes certain default parameters for a session.

int rpc_gss_set_defaults(
	AUTH *auth,
	rpc_gss_service_t service,
	char *qop);
This call applies to the client side only. The application may choose to switch from integrity protection to privacy protection. The application may also wish to change the QOP. The new values set by the application will apply to the rest of the session (unless changed again).

Principal names

Principal names are required in two different circumstances:

When clients need to specify the server's principal name, and,

When a server needs to operate on a client's principal name (for example, to check access control lists).

A server's principal name is always specified as a NULL-terminated ASCII string in the form <service>@<host>. Here, <service> is the name of a service, for example, nfs, and <host> is the fully qualified DNS name of a host, for example, jurassic.eng.sun.com. The GSS-API Version 2 specification allows such names to be used with any underlying security mechanism.

The principal name of a client as received by a server has the structure corresponding to the rpc_gss_principal_t defined in Receiving Credentials at the Server on page 4. This is a counted, opaque byte string. In general, the format of this string will be mechanism specific. These strings can be used either as database indices for looking up a UNIX credential (if one exists), or directly in access control lists. Now a server may wish to compare a principal name it has received with the principal name of a known entity. To do so, the server needs to generate these principal names for known entities.

The rpc_gss_get_principal_name() call takes as input a mechanism type, and parameters that uniquely identify an individual in a network. It returns a principal name in mechanism-specific format, which may be byte-compared with received principal names (for example, as part of a client's network credentials).

It is hard to pin down the criteria for all security mechanisms that will uniquely identify an individual in a network. For now, the following parameters seem to be sufficient: an individual or service name, a node name, and a security domain. An individual name could be a UNIX login identifier. An example of a service name is ``nfs.'' An example node name is a UNIX machine name, such as jurassic. A security domain could be a DNS, NIS, or NIS+ domain name.

The principal name constructor will accept NULLs for one or more of the three parameters (name, node, security domain) that identify an individual in a network. For each security mechanism, the parameters necessary to construct principal names will be specified. For example, Kerberos V5 will require: a user name; optionally, a fully qualified host name; and optionally, a realm name. The construction of the principal name is mechanism dependent. Some mechanisms may do this algorithmically, and some may look up the principal name in a name service or database.

bool_t rpc_gss_get_principal_name(
	rpc_gss_principal_t *principal,
	char *mechanism,
	char *user_name,
	char *node,
	char *secdomain);
The type rpc_gss_principal_t has been defined earlier.

Principal names are freed using the free() library call. A principal name need only be freed in those instances where it was constructed by the application. Values returned by other routines point to structures already existing in a context, and need not be freed.

Returning Errors

The following routine is used to fetch the error code when one of the rpc_gss_* routines fails:

typedef struct {
	int rpc_gss_error;
	int system_error;
} rpc_gss_error_t;

void rpc_gss_get_error(rpc_gss_error_t
	*error);

/*
* error code definitions
*/
#define RPC_GSS_ER_SUCCESS 0
#define RPC_GSS_ER_SYSTEMERROR 1
/*
* more to be defined later
*/
If the error is RPC_GSS_ER_SYSTEMERROR, the error encountered was a system error. The field system_error is then set to one of the possible values for errno. Note that unless the invoked function indicates a failure, rpc_gss_get_error() will not return a meaningful value.

Miscellaneous Convenience Calls

The following functions are used to obtain information about installed mechanisms and the version of RPCSEC_GSS supported.

char **rpc_gss_get_mechanisms();
This function fetches the list of supported security mechanisms as a NULL terminated list of character strings.

char **rpc_gss_get_mech_info(
	char *mechanism,
	rpc_gss_service_t *service);
This function fetches the relevant mechanism information. Supported QOPs are returned as a NULL terminated list of character strings. The returned value for service is one of the values of the rpc_gss_service_t type.

bool_t rpc_gss_get_versions(
	int *vers_hi,
	int *vers_lo);
This returns the highest and lowest RPCSEC_GSS versions supported.

bool_t rpc_gss_is_installed(
	char *mechanism);
This function returns TRUE if the mechanism is installed, and FALSE otherwise.

Programming Examples

This section provides an indication of how clients and servers will use the interfaces described in this document.

The Client Side

The following is a very simple client program that makes one secure call to a server. Details such as error checking are not shown.

CLIENT *clnt; /* client handle */
char server_host[] = "foo";
char service_name[]= "bar@foo.eng.sun.com";
char mech[] = "kerberos_v5"; 

clnt = clnt_create(server_host, SERV_PROG, 
SERV_VERS, "netpath");
clnt->cl_auth = 
rpc_gss_seccreate(clnt, service_name,
mech, rpc_gss_svc_integrity,
NULL, NULL, NULL);
clnt_call(clnt, SERV_PROC1, 
xdr_arg,arg, xdr_res, res, timeout);
AUTH_DESTROY(clnt->cl_auth); 
clnt_destroy(clnt);

The Server Side

The server should set the name of the principal it is acting as in the main program. Error checking details are not shown.

char mech[] = "kerberos_v5";
char service_name[] =
	"bar@foo.eng.sun.com";
int res;
res = rpc_gss_set_svc_name(service_name,
mech, 0,SERV_PROG, SERV_VERS);
The following is a simple server side dispatch procedure. Details such as error checking are not shown.

static void
server_prog(struct svc_req *rqstp,
	SVCXPRT *xprt)
{
	rpc_gss_ucred_t *ucred;
	rpc_gss_rawcred_t *rcred;

	if (rqstp->rq_proc == NULLPROC) {
	    svc_sendreply(xprt, xdr_void, NULL);
	    return;
	}
	/*
* authenticate all other requests
	 */
	switch (rqstp->rq_cred.oa_flavor) {
	case RPCSEC_GSS:
	  /*
	   * get credential information
	   */
	   rpc_gss_getcred(rqstp, &rcred,
			&ucred, NULL);
	  /*
	   * verify that user is allowed to access
	   * using received security parameters by
	   * peeking into my config file
	   */
	   if (!authenticate_user(ucred->uid,
	       rcred->mechanism, rcred->qop,
	       rcred->service)) {
				svcerr_weakauth(xprt);
	           return;
	   }
	   break;  /* allow the user in */
	default:
	   svcerr_weakauth(xprt);
	   return;
	} /* end switch */

	switch (rqstp->rq_proc) {
	case SERV_PROC1:
	   ...
	}

	   /* usual request processing */
	   /* send response */

	   return;
	}

THE RPCSEC_GSS SECURITY PROTOCOL

An RPC session based on the RPCSEC_GSS security flavor consists of three phases: context creation, RPC data exchange, and context destruction. The following section discusses protocol elements for these three phases.

For convenience, APPENDIX B of this document reproduces the XDR language description of the RPC protocol. The following description of the protocol uses some of these definitions.

Context creation and destruction use control messages that are not dispatched to service procedures registered by an RPC server. The program and version numbers used in these control messages are the same as the service program and version numbers. The procedure number used is NULLPROC. A field in the credential information (the gss_proc field) specifies whether a message is to be interpreted as a control message or a regular RPC message. If this field is set to RPCSEC_GSS_NULL, no control action is implied; in this case, it is a regular data message. If this field is set to any other value, a control action is implied. This behavior is described in the following sections.

The following definitions are used for describing the protocol.

/* RPCSEC_GSS control procedures */
#define RPCSEC_GSS_NULL 0
#define RPCSEC_GSS_INIT 1
#define RPCSEC_GSS_CONTINUE_INIT 2
#define RPCSEC_GSS_DESTROY 3
/* RPCSEC_GSS services */
enum rpc_gss_service_t {
rpc_gss_svc_default = 0,
rpc_gss_svc_none = 1,
rpc_gss_svc_integrity = 2,
rpc_gss_svc_privacy = 3
};
/* Credential */
struct rpc_gss_cred_t {
unsigned int version;
unsigned int gss_proc;
unsigned int seq_num;
rpc_gss_service_t service;
opaque handle<>;
};
/* Maximum sequence number value */
#define MAXSEQ 0x80000000

Context Creation

Before RPC data is exchanged on a session using the RPCSEC_GSS flavor, a context must be set up between the client and the server. Context creation may involve zero or more RPC exchanges. The number of exchanges depends on the security mechanism. For KerberosV5, context creation involves one RPC exchange.

Context Creation Requests

The first RPC request from the client to the server initiates context creation for those mechanisms that require context creation messages. The program and version used in the RPC message are always those for the service being accessed. The procedure is set to NULLPROC.

The credential field in the RPC message header has the following structure (reproduced from RPC protocol definition):

struct opaque_auth {
	sec_flavor flavor;
	opaque body<400>;
};
The credential uses the RPCSEC_GSS flavor. The credential body is created by XDR encoding the rpc_gss_cred_t structure listed earlier into a byte stream, and then opaquely encoding this byte stream as the body field.

The values of the fields contained in the rpc_gss_cred_t structure are set as follows: The version field is set to the current RPCSEC_GSS version (1). The gss_proc field must be set to RPCSEC_GSS_INIT for the first creation request. In subsequent creation requests, the gss_proc field must be set to RPCSEC_GSS_CONTINUE_INIT. The server must ignore the seq_num field in all creation requests. The server must also ignore the service field in all creation requests. In the first creation request, the handle field is NULL (opaque data of zero length). In subsequent creation requests, handle must be equal to the value returned by the server. handle serves as the identifier for the context, and will not change for the duration of the context.

The opaque_auth structure also describes the verifier field in the RPC message header. All creation requests have the NULL verifier (AUTH_NONE flavor with zero length opaque data).

The following structure describes the call argument for all creation requests:

struct rpc_gss_init_arg {
	opaque gss_token<>;
	unsigned int qop;
	rpc_gss_service_t service;
};
Here, gss_token is the token returned by the call to gss_init_sec_context(), opaquely encoded. The value of this field will probably differ in each creation request, if there is more than one creation request. If the call to gss_init_sec_context() does not return a token, the context should have been created (assuming no errors). No more creation requests will occur.

One reason for ``overloading'' the call data in this way, rather than packing the GSS token into the RPC header, is that RPC Version 2 restricts the amount of data that can be sent in the header. The opaque body of the credential and verifier fields can be each at most 400 bytes long, and GSS tokens can be longer.

The qop and service fields must be set to the quality-of-protection (QOP) [4] and service the client wishes to use for the session. Some services will need this information before they will allow the context to be set up. Although the client can change the QOP and service used on a per request basis, this may not be acceptable to all RPC-based services. These services may choose to enforce the QOP and service specified during context creation for the rest of the session. If there is more than one creation request, all of them should use the same values for qop and service. The GSS-API interprets a value of 0 (zero) for QOP as a request to use the default QOP level. Since QOP values are mechanism-specific, 0 should be used whenever possible, to be mechanism-independent.

Context Creation Response - Successful Acceptance

The response to a successful creation request is an ACCEPTED response with a status of SUCCESS. The response contains a result argument that has the following structure:

struct rpc_gss_init_res {
	opaque handle<>;
	unsigned int gss_major;
	unsigned int gss_minor;
	unsigned int seq_window;
	opaque gss_token<>;
};
Here, handle is non-NULL opaque data that serves as the context identifier. The client must use this value in all subsequent requests, whether control messages or otherwise. The gss_major and gss_minor fields contain the results of the call to gss_accept_sec_context() executed by the server. If gss_major is not one of GSS_S_COMPLETE or GSS_S_CONTINUE_NEEDED, the context setup has failed. In this case, the server must set handle and gss_token to NULL. The value of gss_minor depends on the value of gss_major and the security mechanism used. The gss_token field contains any token returned by the gss_accept_sec_context() call executed by the server. A token may be returned for both successful values of gss_major. If the value is GSS_S_COMPLETE, it indicates that the server does not expect any more tokens. If the value is GSS_S_CONTINUE_NEEDED, the server expects another token, and, therefore, at least one more creation request carrying the required token is needed.

In a successful response, the seq_window field is set to the sequence window length supported by the server for this context. This window specifies the maximum number of client requests that may be outstanding for this context. The server will accept seq_window requests at a time. These may be out of order. The client may use this number to determine the number of threads that can simultaneously send requests on this context.

The verifier used for a context creation response is the NULL verifier (AUTH_NONE flavor with zero-length opaque data).

Context Creation Response - Unsuccessful Cases

An ACCEPTED reply to a creation request whose status is other than SUCCESS also has a NULL verifier, and is formulated as usual for different status values.

A DENIED reply to a creation request is also formulated as usual. Two new values, RPCSEC_GSS_NOCRED and RPCSEC_GSS_FAILED, have been defined for the auth_stat type. When the reason for denial of the request is AUTH_ERROR, one of the two new values could be returned in addition to the existing values. These values don't have any special significance for responses to creation requests, but they do for responses to normal (data) requests, as described later.

RPC Data Exchange

The data exchange phase is entered after a context has been successfully set up. The format of the data exchanged depends on the security service used for the request. Although clients can change the security service and QOP used on a per-request basis, this may not be acceptable to all RPC services. For all three modes of service (no data integrity, data integrity, data privacy), the RPC request header has the same format.

RPC Request Header

For the RPC request header, the credential has the opaque_auth structure described earlier. The flavor field is set to RPCSEC_GSS. The credential body is created by XDR encoding the rpc_gss_cred_t structure listed earlier into a byte stream, and then opaquely encoding this byte stream as the body field.

Values of the fields contained in the rpc_gss_cred_t structure are set as follows:

Use of sequence numbers is described in detail later in this paper.

The verifier has the opaque_auth structure described earlier. The flavor field is set to RPCSEC_GSS. The body field is set as follows: The checksum of the RPC header (up to and including the credential) is computed using the gss_sign() call with the desired QOP. gss_sign returns the checksum as an opaque byte stream and its length. This is encoded into the body field. Note that the QOP is not explicitly specified anywhere in the request. It is implicit in the checksum or encrypted data. The same QOP value used for the header checksum must also be used for the data (for checksumming or encrypting), unless the service used for the request is rpc_gss_svc_none.

RPC Request Data - No Data Integrity

If the service specified is rpc_gss_svc_none, the data (procedure arguments) are not integrity or privacy protected. They are sent in exactly the same way as they would be if the AUTH_NONE flavor were used (following the verifier). Note, however, that because the RPC header is integrity protected, the sender will still be authenticated in this case.

RPC Request Data - With Data Integrity

When data integrity is used, the request data is represented as follows:

struct rpc_gss_integ_data {
	opaque databody_integ<>;
	opaque checksum<>;
};
The databody_integ field is created as follows: First, a structure consisting of a sequence number, followed by the procedure arguments, is constructed, as shown below as the type rpc_gss_data_t:

struct rpc_gss_data_t {
	unsigned int seq_num;
	proc_req_arg_t arg;
};
Here, seq_num must have the same value as the credential. The type proc_req_arg_t is the procedure- specific XDR type describing the procedure arguments, thus is not specified here. The byte stream corresponding to the rpc_gss_data_t structure and its length is encoded as the databody_integ field.

The checksum field represents the checksum of the byte stream corresponding to the rpc_gss_data_t structure. (Note that this is not the checksum of the databody_integ field.) This checksum is obtained using the gss_sign() call, with the same QOP as was used to compute the header checksum (in the verifier). The gss_sign() call returns the checksum as an opaque byte stream and its length. The checksum and its length are opaquely encoded as the checksum field.

RPC Request Data - With Data Privacy

When data privacy is used, the request data is represented as follows:

struct rpc_gss_priv_data {
	opaque databody_priv<>
};
The databody_priv field is created as follows: the rpc_gss_data_t structure is reconstructed in the same way as for data integrity. Next, the gss_seal() call is invoked to encrypt the byte stream corresponding to the rpc_gss_data_t structure, using the same value for QOP as was used for the header checksum (in the verifier). The gss_seal() call returns an opaque byte stream (representing the encrypted rpc_gss_data_t structure) and its length. These values are used to opaquely encode the RPC request data as the databody_priv field.

Server Processing of RPC Data Requests - Context Management

When the server receives a request, the following are verified as acceptable:

The gss_proc field in the credential must be set to RPCSEC_GSS_NULL for data requests. Otherwise, the message will be interpreted as a control message, as discussed later.

The server maintains a window of seq_window sequence numbers, starting with the last sequence number seen and extending backwards. If the server receives a sequence number higher than the last number seen, the window moves forward to the new sequence number. If the last sequence number seen is n, the server can receive requests with sequence numbers in the range n through (n - seq_window + 1), both inclusive. If the sequence number received falls below this range, the server silently discards it. If the sequence number is within this range, and the server has not seen it, the request is accepted. Then, the server turns on a bit to ``remember'' that it has seen this sequence number. If the server determines that it has already seen a sequence number within the window, it silently discards the request.

The reason for discarding requests silently is because the server cannot determine why the duplicate or out of range request occurred. It could be due to a sequencing problem in the client, network, or operating system; to some quirk in routing; or to a replay attack by an intruder. Discarding the request allows the client to recover after timing out, if indeed the duplication was unintentional or well intended.

The data is decoded according to the service specified in the credential. In the case of integrity or privacy, the server ensures that the QOP value is acceptable, and that it is the same as that used for the header checksum in the verifier. For the same reason, the server will reject the message if the sequence number embedded in the request body is different from the sequence number in the credential. This prevents splicing attacks. If the sequence numbers were not compared, an attacker could capture previous RPC requests, and splice in the header of one with the body of another.

Server Reply - Request Accepted

An ACCEPTED reply to a request in the data exchange phase will have the verifier set to the checksum of the sequence number (in network order) of the corresponding request. The QOP used is the same as the QOP for the corresponding request.

If the status of the reply is not SUCCESS, the rest of the message is formatted as usual, as for the AUTH_NULL security flavor.

If the status of the message is SUCCESS, the format of the rest of the message depends on the service specified in the corresponding request message. Basically, what follows the verifier in this case are the procedure results, formatted in different ways depending on the requested service.

If no data integrity was requested, the procedure results are formatted as for the AUTH_NULL security flavor.

If data integrity was requested, the results are encoded exactly as the procedure arguments were in the corresponding request. (Discussed previously in the section under the heading RPC Request Data - With Data Integrity.) The only difference is that the structure representing the procedure's result - proc_res_arg_t - must be substituted in place of the request argument structure proc_req_arg_t. The QOP used for the checksum must be the same as that used for constructing the reply verifier.

If data privacy was requested, the results are encoded in exactly the same way as the procedure arguments were in the corresponding request. See the section RPC Request Data - With Data Privacy. The QOP used for encryption must be the same as that used for constructing the reply verifier.

Server Reply - Request Denied

A DENIED reply to a data request is formulated as usual. Two new values, RPCSEC_GSS_NOCRED and RPCSEC_GSS_FAILED, have been defined for the auth_stat type. When AUTH_ERROR is the reason for denial of the request, one of the two new values could be returned in addition to the existing values. These two new values have special significance, differing from the existing reasons for denial of a request.

The server maintains a list of contexts for the clients, with which it is currently in session. Normally, a context is destroyed when the client ends its corresponding session. However, due to resource constraints, the server may destroy a context prematurely (for example, on an LRU basis, or if the server machine is rebooted). In this case, when a client request comes in, there may not be a context corresponding to its handle. The server rejects the request, with the reason RPCSEC_GSS_NOCRED. Upon receiving this error, the client must refresh the context -- that is, reestablish it after destroying the old one -- and try the request again. This error is also returned if the context handle matches that of a different context that was allocated after the client's context was destroyed. (This will be detected by a failure in verifying the header checksum.)

When the client's sequence number exceeds the maximum the server will allow, the server will reject the request with the reason RPCSEC_GSS_FAILED. Also, if security credentials become stale while in use (for example, ticket expiration in the case of the Kerberos V5 mechanism), the resulting failures cause the RPCSEC_GSS_FAILED reason to be returned. In these cases, the client must refresh the context, and retry the request.

For other errors, retrying will not rectify the problem. The client should not refresh the context until the problem causing request denial is rectified.

Context Destruction

When the client is done using the session, it should send a control message informing the server that it no longer requires the context. This message is formulated just like a data request packet, with the following differences: the credential has gss_proc set to RPCSEC_GSS_DESTROY, the procedure specified in the header is NULLPROC, and there are no procedure arguments. For the server to accept the message, the sequence number in the request should be valid, and the header checksum in the verifier should be valid.

The server sends a response as it would for a data request. The client and server should then destroy the context for the session.

If the request to destroy the context fails for some reason, the client need not take any special action. The server should handle situations where clients never inform when they no longer are in session and no longer need the server to maintain a context. The server should use an LRU mechanism or an aging mechanism to clean up in such cases.

COMPARISON TO OPENVISION'S AUTH_GSSAPI

Sun used OpenVision's AUTH_GSSAPI as a prototype for RPCSEC_GSS. In the paper ``GSS-API Security for ONC RPC,'' [7] the goal was to implement a no-frills RPC authentication system with minimal effort.

As B. Jaspan mentioned in this paper, the implementation had the following limitations:

RPCSEC_GSS solved the first limitation by using the gss_sign() primitive to sign the RPC call header (up to and including the credential) and placing the result in the RPC message's verifier.

RPCSEC_GSS solved the second limitation by using the sliding sequence window. This allows a client to have multiple threads making requests on the same client handle. The use of the sequence window also simplified replay detection on the server side. Note that in the current implementation, only the kernel level client and server code is thread-safe. In the user level version, only the server side is safe because the current user level, client side of ONC RPC is not multithreaded.

The AUTH_GSSAPI implementation did not allow selective use of integrity or encryption on call data; all call data was encrypted. Since RPCSEC_GSS was envisioned for a wide variety of applications (such as NFS and NIS+), always encrypting the call data was considered too restrictive. Also, United States export control laws provided sufficient motivation for an exportable implementation that does not encrypt call data.

This limitation was solved by adding the service field to the RPCSEC_GSS credential. This allows the client to change the service requested on each request. Therefore, servers can require that a particular service to be used, and enforce that policy by ``locking'' the service for a session.

INTEROPERABILITY ISSUES

An RPC client executing in a run time environment without support for RPCSEC_GSS can continue to interoperate with an RPC server that has RPCSEC_GSS support, provided of course the RPC server is not implemented or configured to insist on requests using RPCSEC_GSS. An RPC client that uses RPCSEC_GSS to attempt procedure calls with a server in a non-RPCSEC_GSS environment will not be able to interoperate. An NFS ([11]) implementation is an example of an application that has to cope with these interoperability issues. Version 3 of the NFS protocol ([10]) provides a way to handle this via the security flavor negotiation feature of the corresponding version of the MOUNT protocol.

PERFORMANCE

Extensive performance testing has not been done at this point. Some preliminary tests on a prototype have been run. The results are graphed in Figure 3, with raw data in Table 1. These results were obtained by sending messages of varying length of opaque data (the horizontal axis) from an SS5 to an SS20 over UDP, and taking the average time (the vertical axis, in milliseconds) over 1000 calls. The GSS-API mechanism used with RPCSEC_GSS was Kerberos V5 with mutual authentication and replay detection enabled.

The results match expectations. Using rpc_gss_svc_none (call header integrity), as opposed to AUTH_NONE, adds overhead to each message. The overhead is a constant. It is not dependent on the size of the data being sent (because the header size doesn't change). The overhead imposed by rpc_gss_svc_integrity and rpc_gss_svc_privacy does increase with the size of the data, also as expected. Some profiling work is planned to see if the overhead can been lessened.

Adding data integrity and privacy to ONC RPC is not cheap. There is a trade-off between security and performance. RPCSEC_GSS gives one the ability to enable integrity and privacy of data when needed. Sites that do not need integrity and privacy can still benefit from the strong authentication gained by using RPCSEC_GSS.

CONCLUSIONS

A working prototype of the user-level and kernel-level versions of RPCSEC_GSS is available. Plans are well underway for adding RPCSEC_GSS support to NFS, to provide a version of NFS with strong authentication, as well as data integrity and privacy.

ACKNOWLEDGMENTS

Much of this work was based on an earlier prototype by OpenVision Technologies. Without their efforts, RPCSEC_GSS may never have happened. In particular, thanks are extended to Barry Jaspan, Mark Horowitz, John Linn, Ellen McDermott, and the rest of the OpenVision crew at the time the prototype was developed.

On the Sun side, Raj Srinivasan evolved OpenVision's prototype into RPCSEC_GSS, along with help from Mike Eisler. Roland Schemers took Raj Srinivasan's prototype and put the finishing touches on it, along with modifying snoop to understand about RPCSEC_GSS.

Dan Nessett and Derek Atkins developed an in-kernel GSS-API library. Lin Ling ported user level RPCSEC_GSS into the kernel and thus achieved the milestone of encrypted NFS traffic on the network.

The authors appreciate the efforts by Rosalind HaLevi, Lin Ling, and Kathy Slattery in reviewing this paper on short notice.

REFERENCES

1. Srinivasan, R. (1995). RFC 1831, ``RPC: Remote Procedure Call Protocol Specification Version 2.''

2. Srinivasan, R. (1995). RFC 1832, ``XDR: External Data Representation Standard.''

3. Srinivasan, R. (1995). RFC 1833, ``Binding Protocols for ONC RPC Version 2.''

4. Linn, J. (1993). RFC 1508, ``Generic Security Service Application Program Interface.''

5. Wray, J. (1993). RFC 1509, ``Generic Security Service API: C-bindings.''

6. Kohl, J and Neuman, C. (1993). RFC 1510, ``The Kerberos Network Authentication Service (V5).''

7. Jaspan, B. (1995). ``GSS-API Security for ONC RPC,'' `95 Proceedings of The Internet Society Symposium on Network and Distributed System Security, pp. 144-151.

8. Neuman, C and Ts'o, T. (1994). ``Kerberos: An Authentication System for Computer Networks, IEEE Communications, 32.

9. Linn, J. (1996). ``The Kerberos Version 5 GSS-API Mechanism,'' IETF Internet-Draft.

10. B. Callaghan, B. Pawlowski, and P. Staubach, (1995). RFC 1813, ``NFS Version 3 Protocol Specification.''

11. Sun Microsystems, Inc., (1989). RFC 1094, ``NFS: Network File System Protocol specification.''

APPENDIX A: SNOOP

The Solaris snoop program has been modified so that it understands the RPCSEC_GSS security flavor. The following example uses snoop to illustrate the RPCSEC_GSS protocol:

Address List Program

The following RPC Language specification defines an address list server. The server maintains a database of name to address mappings. The following three procedure calls are defined: addrlist_set, addrlist_get, and addrlist_del.

const MAX_NAME_LEN = 128;

const MAX_ADDR_LEN = 256;

typedef string name_t<MAX_NAME_LEN>;

typedef string addr_t<MAX_ADDR_LEN>;

struct addr_entry {

name_t name;

addr_t address;

};

program ADDRLISTPROG {

version ADDRLISTVERS {

bool addrlist_set(addr_entry) = 1;

addr_entry addrlist_get(name_t) = 2;

bool addrlist_del(name_t) = 3;

} = 1;

} = 620756992;

Create Context

When the client program calls rpc_gss_seccreate, a RPCSEC_GSS_INIT control message is sent to the server, and the server sends a reply.

Create Context Request

RPC: ----- SUN RPC Header -----

RPC:

RPC: Transaction id = 822070204

RPC: Type = 0 (Call)

RPC: RPC version = 2

RPC: Program = 620756992 (GSSTEST), version = 1, procedure = 0

RPC: Credentials: Flavor = 15 (RPCSEC_GSS), len = 20

RPC: version = 1

RPC: gss control procedure = 1 (RPCSEC_GSS_INIT)

RPC: sequence num = 0

RPC: service = 2 (integrity)

RPC: handle: length = 0, data = []

RPC: Verifier : Flavor = 0 (None), len = 0 bytes

RPC:

RPC: RPCSEC_GSS_INIT args:

RPC: gss token: length = 491,data = [491 bytes]

RPC: quality of protection (qop) = 0

RPC: service = 2 (integrity)

RPC:

Create Context Reply

RPC: ----- SUN RPC Header -----

RPC:

RPC: Transaction id = 822070204

RPC: Type = 1 (Reply)

RPC: This is a reply to frame 1

RPC: Status = 0 (Accepted)

RPC: Verifier : Flavor = 0 (None), len = 0 bytes

RPC: Accept status = 0 (Success)

RPC:

RPC: RPCSEC_GSS_INIT result:

RPC: handle: length = 4, data = [0000000B]

RPC: gss_major status = 0

RPC: gss_minor status = 0

RPC: sequence window = 128

RPC: gss token: length = 102, data = [102 bytes]

RPC:

Note the value of the handle returned by the server. This value will be used with all future communications with the server.

Data Exchange

Once the context is established, the normal call/reply exchange occurs. The next example shows three exchanges between the client and server. The first uses the integrity service (rpc_gss_svc_integrity), the second uses the privacy service (rpc_gss_svc_privacy), and the third uses no service (rpc_gss_svc_none).

Data Exchange - Integrity (call)

RPC: ----- SUN RPC Header -----

RPC:

RPC: Transaction id = 822070205

RPC: Type = 0 (Call)

RPC: RPC version = 2

RPC: Program = 620756992 (GSSTEST), version = 1, procedure = 1

RPC: Credentials: Flavor = 15 (RPCSEC_GSS), len = 24

RPC: version = 1

RPC: gss control procedure = 0 (RPCSEC_GSS_NULL)

RPC: sequence num = 2

RPC: service = 2 (integrity)

RPC: handle: length = 4, data = [0000000B]

RPC: Verifier : Flavor = 15 (RPCSEC_GSS), len = 33

RPC: [601F06052B0501050201010000...4170507FD073EE6BAC1]

RPC:

RPC: RPCSEC_GSS data seq_num = 2

RPC:

GSS: ----- Sun GSS TEST -----

GSS:

GSS: Proc = 1 (Set address value)

GSS: name = schemers

GSS: value = roland.schemers@eng.sun.com

GSS:

RPC: ----- RPCSEC_GSS -----

RPC:

RPC: checksum: len = 33

RPC: [601F06052B0501050201010...440D6D951CF8126DE80C3]

RPC:

Note that since integrity (and not privacy) was used, call arguments are visible to snoop. However, since they are integrity protected, an attacker is unable to modify them. Also note that the sequence number (2 in this case) is the same in both the call header and body. As mentioned earlier, this prevents a splicing attack.

Data Exchange - Integrity (reply)

RPC: ----- SUN RPC Header -----

RPC:

RPC: Transaction id = 822070205

RPC: Type = 1 (Reply)

RPC: This is a reply to frame 3

RPC: Status = 0 (Accepted)

RPC: Verifier : Flavor = 15 (RPCSEC_GSS), len = 33

RPC: [601F06052B0501050201010000...8099DD1E993391D5F]

RPC: Accept status = 0 (Success)

RPC:

RPC: RPCSEC_GSS data seq_num = 2

RPC:

GSS: ----- Sun GSS TEST -----

GSS:

GSS: Proc = 1 (Set address value)

GSS: result = True

GSS:

RPC: ----- RPCSEC_GSS -----

RPC:

RPC: checksum: len = 33

RPC: [601F06052B0501001010000...CBB5F757967F47307610]

RPC:

Note that the reply arguments are also integrity protected.

Data Exchange - Privacy (call)

RPC: ----- SUN RPC Header -----

RPC:

RPC: Transaction id = 822070206

RPC: Type = 0 (Call)

RPC: RPC version = 2

RPC: Program = 620756992 (GSSTEST), version = 1, procedure = 2

RPC: Credentials: Flavor = 15 (RPCSEC_GSS), len = 24

RPC: version = 1

RPC: gss control procedure = 0 (RPCSEC_GSS_NULL)

RPC: sequence num = 3

RPC: service = 3 (privacy)

RPC: handle: length = 4, data = [0000000B]

RPC: Verifier : Flavor = 15 (RPCSEC_GSS), len = 33 bytes

RPC: [601F06052B0501050201010000...D641560DD62123393]

RPC:

RPC: RPCSEC_GSS (CALL args encrypted)

RPC:

Data Exchange - Privacy (reply)

RPC: ----- SUN RPC Header -----

RPC:

RPC: Transaction id = 822070206

RPC: Type = 1 (Reply)

RPC: This is a reply to frame 5

RPC: Status = 0 (Accepted)

RPC: Verifier : Flavor = 15 (RPCSEC_GSS), len = 33

RPC: [601F06052B0501050201010000...F624B44EAE9008C492E]

RPC: Accept status = 0 (Success)

RPC:

RPC: RPCSEC_GSS (REPLY args encrypted)

RPC:

Since the call and reply arguments are encrypted, snoop can't display them. It could display the encrypted data as hexadecimal. But, without knowing the encryption key display, the hexadecimal data is not very useful.

Data Exchange - Service None (call)

RPC: ----- SUN RPC Header -----

RPC:

RPC: Transaction id = 822070207

RPC: Type = 0 (Call)

RPC: RPC version = 2

RPC: Program = 620756992 (GSSTEST), version = 1, procedure = 3

RPC: Credentials: Flavor = 15 (RPCSEC_GSS), len = 24

RPC: version = 1

RPC: gss control procedure = 0 (RPCSEC_GSS_NULL)

RPC: sequence num = 4

RPC: service = 1 (none)

RPC: handle: length = 4, data = [0000000B]

RPC: Verifier : Flavor = 15 (RPCSEC_GSS), len = 33

RPC: [601F06052B0501050201010000...FF1005156F24570E20A]

RPC:

GSS: ----- Sun GSS TEST -----

GSS:

GSS: Proc = 3 (Delete address value)

GSS: name = schemers

GSS:

Data Exchange - Service None (reply)

RPC: ----- SUN RPC Header -----

RPC:

RPC: Transaction id = 822070207

RPC: Type = 1 (Reply)

RPC: This is a reply to frame 7

RPC: Status = 0 (Accepted)

RPC: Verifier : Flavor = 15 (RPCSEC_GSS), len = 33 bytes

RPC: [601F06052B0501050201010000...C6409E5707D34AB]

RPC: Accept status = 0 (Success)

RPC:

GSS: ----- Sun GSS TEST -----

GSS:

GSS: Proc = 3 (Delete address value)

GSS: result = True

GSS:

When rpc_gss_svc_none is used, the call/reply arguments are not integrity protected or encrypted. The call/reply header is still integrity protected though, providing strong authentication.

Context Destruction

The context will be destroyed when the client calls the standard ONC RPC auth_destroy function.

Context Destruction - Request

RPC: ----- SUN RPC Header -----

RPC:

RPC: Transaction id = 822070208

RPC: Type = 0 (Call)

RPC: RPC version = 2

RPC: Program = 620756992 (GSSTEST), version = 1, procedure = 0

RPC: Credentials: Flavor = 15 (RPCSEC_GSS), len = 24

RPC: version = 1

RPC: gss control procedure = 3 (RPCSEC_GSS_DESTROY)

RPC: sequence num = 5

RPC: service = 1 (none)

RPC: handle: length = 4, data = [0000000B]

RPC: Verifier : Flavor = 15 (RPCSEC_GSS), len = 33

RPC: [601F06052B0501050201010000...A065CC80B17751779]

RPC:

Context Destruction - Reply

RPC: ----- SUN RPC Header -----

RPC:

RPC: Transaction id = 822070208

RPC: Type = 1 (Reply)

RPC: This is a reply to frame 9

RPC: Status = 0 (Accepted)

RPC: Verifier : Flavor = 15 (RPCSEC_GSS), len = 33

RPC: [601F06052B0501050201010000...10A97BCF979D24DFEA]

RPC: Accept status = 0 (Success)

RPC:

APPENDIX B: THE RPC MESSAGE PROTOCOL

For convenience, the XDR language description of the RPC message protocol is reproduced here. For explanations of these constructs, refer to RFC-1831, ``RPC Remote Procedure Call Protocol Specification Version 2.''

/* RPC message type */
enum msg_type {
CALL = 0,
REPLY = 1
};
/* Reply types */
enum reply_stat {
MSG_ACCEPTED = 0,
MSG_DENIED = 1
};
/* Security flavors */
enum sec_flavor {
AUTH_NONE = 0,
AUTH_SYS = 1,
AUTH_SHORT = 2,
AUTH_DH = 3,
AUTH_KERB = 4,
RPCSEC_GSS = 6
};

/* Status of accepted messages */
enum accept_stat {
SUCCESS = 0,
PROG_UNAVAIL = 1,
PROG_MISMATCH = 2,
PROC_UNAVAIL = 3,
GARBAGE_ARGS = 4,
SYSTEM_ERR = 5
};
/* Status of rejected messages */
enum reject_stat {
RPC_MISMATCH = 0,
AUTH_ERROR = 1
};
/* Why authentication failed */
enum auth_stat {
AUTH_OK = 0,
    /* failed at remote end */
    AUTH_BADCRED = 1,
AUTH_REJECTEDCRED = 2,
AUTH_BADVERF = 3,
AUTH_REJECTEDVERF = 4,
AUTH_TOOWEAK = 5,
    /* failed locally */
    AUTH_INVALIDRESP = 6,
AUTH_FAILED = 7,
    /* kerberos v4 errors */
    AUTH_KERB_GENERIC = 8,
AUTH_TIMEEXPIRE = 9,
AUTH_TKT_FILE = 10,
AUTH_DECODE = 11,
AUTH_NET_ADDR = 12,
    /* RPCSEC_GSS errors */
    RPCSEC_GSS_NOCRED = 13,
RPCSEC_GSS_FAILED = 14
};
/* Opaque structure of
	    credential and verifier */
struct opaque_auth {
sec_flavor flavor;
opaque body<400>;
};
/* The RPC message */
struct rpc_msg {
unsigned int xid;
union switch (msg_type mtype) {
case CALL:
call_body cbody;
case REPLY:
reply_body rbody;
} body;
};
/* Body of RPC call */
struct call_body {
unsigned int rpcvers;
unsigned int prog;
unsigned int vers;
unsigned int proc;
opaque_auth cred;
opaque_auth verf;
	/* procedure specific
		parameters start here */
};
/* Body of RPC reply */
union reply_body switch (reply_stat stat) {
case MSG_ACCEPTED:
accepted_reply areply;
case MSG_DENIED:
rejected_reply rreply;
} reply;
/* Accepted reply */
struct accepted_reply {
opaque_auth verf;
union switch (accept_stat stat) {
case SUCCESS:
opaque results[0];
        /* procedure-specific
	                results start here */
    case PROG_MISMATCH:
struct {
unsigned int low;
unsigned int high;
} mismatch_info;
    default:
		/*
* Void. Cases include PROG_UNAVAIL,
		 * PROC_UNAVAIL, GARBAGE_ARGS, and
		 * SYSTEM_ERR.
		 */
		void;
} reply_data;
};
/* Rejected reply */
union rejected_reply switch(reject_stat stat) 
{
case RPC_MISMATCH:
struct {
unsigned int low;
unsigned int high;
} mismatch_info;
case AUTH_ERROR:
auth_stat stat;
};


Last Modified: 11:27pm PDT, June 11, 1996

This paper was originally published in the proceedings of The Sixth USENIX Security Symposium, July 22–25, 1996, San Jose, California, USA
Last changed: 10 Jan 2003 aw
Conference Index
USENIX home