RFC 2203

RPCSEC_GSS Protocol Specification

πŸ“… published September 1997, from ietf

RPCSEC_GSS Protocol Specification πŸŽ€

Okay bestie, this is RFC 2203, and it's giving standards track πŸ’… It was written by M. Eisler, A. Chiu, and L. Ling, and it's dated September 1997. The title block doesn't list anything this RFC obsoletes or updates, so it's a clean slate ✨

Status of This Memo 🌸

This document is an Internet standards track protocol for the Internet community, and it's asking for discussion and suggestions for improvements. For the standardization state and status of this protocol, check the current edition of the "Internet Official Protocol Standards" (STD 1). Distribution of this memo is unlimited πŸ¦‹

Abstract 🧚

So this memo describes an ONC/RPC security flavor that lets RPC protocols access the Generic Security Services Application Programming Interface (we'll call it GSS-API from here on) πŸ”₯

Table of Contents πŸ“œ

1.  Introduction . . . . . . . . . . . . . . . . . . . . . . . . . 2
2.  The ONC RPC Message Protocol . . . . . . . . . . . . . . . . . 2
3.  Flavor Number Assignment . . . . . . . . . . . . . . . . . . . 3
4.  New auth_stat Values . . . . . . . . . . . . . . . . . . . . . 3
5.  Elements of the RPCSEC_GSS Security Protocol . . . . . . . . . 3
5.1.  Version Selection  . . . . . . . . . . . . . . . . . . . . . 5
5.2.  Context Creation . . . . . . . . . . . . . . . . . . . . . . 5
5.2.1.  Mechanism and QOP Selection  . . . . . . . . . . . . . . . 5
5.2.2.  Context Creation Requests  . . . . . . . . . . . . . . . . 6
5.2.3.  Context Creation Responses . . . . . . . . . . . . . . . . 8
5.2.3.1.  Context Creation Response - Successful Acceptance  . . . 8
5.2.3.1.1.  Client Processing of Successful Context Creation
            Responses  . . . . . . . . . . . . . . . . . . . . . . 9
5.2.3.2.  Context Creation Response - Unsuccessful Cases . . . . . 9
5.3.  RPC Data Exchange  . . . . . . . . . . . . . . . . . . . .  10
5.3.1.  RPC Request Header . . . . . . . . . . . . . . . . . . .  10
5.3.2.  RPC Request Data . . . . . . . . . . . . . . . . . . . .  11
5.3.2.1.  RPC Request Data - No Data Integrity . . . . . . . . .  11
5.3.2.2.  RPC Request Data - With Data Integrity . . . . . . . .  11
5.3.2.3.  RPC Request Data - With Data Privacy . . . . . . . . .  12
5.3.3.  Server Processing of RPC Data Requests . . . . . . . . .  12
5.3.3.1.  Context Management . . . . . . . . . . . . . . . . . .  12
5.3.3.2.  Server Reply - Request Accepted  . . . . . . . . . . .  14
5.3.3.3.  Server Reply - Request Denied  . . . . . . . . . . . .  15
5.3.3.4.  Mapping of GSS-API Errors to Server Responses  . . . .  16
5.3.3.4.1.  GSS_GetMIC() Failure . . . . . . . . . . . . . . . .  16
5.3.3.4.2.  GSS_VerifyMIC() Failure  . . . . . . . . . . . . . .  16
5.3.3.4.3.  GSS_Unwrap() Failure . . . . . . . . . . . . . . . .  16
5.3.3.4.4.  GSS_Wrap() Failure . . . . . . . . . . . . . . . . .  16
5.4.  Context Destruction  . . . . . . . . . . . . . . . . . . .  17
6.  Set of GSS-API Mechanisms  . . . . . . . . . . . . . . . . .  17
7.  Security Considerations  . . . . . . . . . . . . . . . . . .  18
7.1.  Privacy of Call Header . . . . . . . . . . . . . . . . . .  18
7.2.  Sequence Number Attacks  . . . . . . . . . . . . . . . . .  18
7.2.1.  Sequence Numbers Above the Window  . . . . . . . . . . .  18
7.2.2.  Sequence Numbers Within or Below the Window  . . . . . .  18
7.3.  Message Stealing Attacks . . . . . . . . . . . . . . . . .  19
Appendix A. GSS-API Major Status Codes . . . . . . . . . . . . .  20
Acknowledgements . . . . . . . . . . . . . . . . . . . . . . . .  22
Authors' Addresses . . . . . . . . . . . . . . . . . . . . . . .  23

1. Introduction ✨

This document describes the protocol the RPCSEC_GSS security flavor uses. Security flavors used to be called authentication flavors, for historical reasons. This memo points out that there are two other security services besides authentication, integrity and privacy, so it defines a brand new RPCSEC_GSS security flavor πŸ’–

The protocol is described using the XDR language [Srinivasan-xdr]. It assumes you already know ONC RPC and the security flavor mechanism [Srinivasan-rpc], and that you know the GSS-API framework [Linn]. RPCSEC_GSS uses GSS-API interfaces to deliver security services that don't depend on the underlying security mechanism 🫢

2. The ONC RPC Message Protocol 🌈

This memo refers to the following XDR types of the ONC RPC protocol, which are described in the document entitled Remote Procedure Call Protocol Specification Version 2 [Srinivasan-rpc]:

   msg_type
   reply_stat
   auth_flavor
   accept_stat
   reject_stat
   auth_stat
   opaque_auth
   rpc_msg
   call_body
   reply_body
   accepted_reply
   rejected_reply

3. Flavor Number Assignment πŸ”’

The RPCSEC_GSS security flavor has been assigned the value of 6:

   enum auth_flavor {
       ...
       RPCSEC_GSS = 6      /* RPCSEC_GSS security flavor */
   };

4. New auth_stat Values πŸ’œ

RPCSEC_GSS needs two new values added to the auth_stat enumerated type definition:

   enum auth_stat {
           ...
           /*
            * RPCSEC_GSS errors
            */
           RPCSEC_GSS_CREDPROBLEM = 13,
           RPCSEC_GSS_CTXPROBLEM = 14
   };

The descriptions of these two new values come later in this memo, so hang tight 🧚

5. Elements of the RPCSEC_GSS Security Protocol πŸ‘‘

An RPC session based on the RPCSEC_GSS security flavor has three phases: context creation, RPC data exchange, and context destruction. The next bit of this memo walks through the protocol elements for each of those three phases πŸ¦‹

The RPCSEC_GSS protocol description uses some of the definitions from the XDR language description of the RPC protocol.

Context creation and destruction use control messages that aren't dispatched to service procedures registered by an RPC server. The program and version numbers in these control messages are the same as the RPC service's program and version numbers. The procedure number used is NULLPROC (zero). A field in the credential information, the gss_proc field defined in the rpc_gss_cred_t structure below, tells you whether a message is a control message or a regular RPC message. If this field is set to RPCSEC_GSS_DATA, no control action is implied, so it's a regular data message. If it's set to any other value, a control action is implied. The following sections describe this πŸ™ˆ

Just like with normal RPC data exchange messages, the transaction identifier (the xid field in struct rpc_msg) should be set to unique values on each call for context creation and context destruction πŸ’…

Here are the definitions used for describing the protocol:

   /* RPCSEC_GSS control procedures */


   enum rpc_gss_proc_t {
           RPCSEC_GSS_DATA = 0,
           RPCSEC_GSS_INIT = 1,
           RPCSEC_GSS_CONTINUE_INIT = 2,
           RPCSEC_GSS_DESTROY = 3
   };

   /* RPCSEC_GSS services */

   enum rpc_gss_service_t {
       /* Note: the enumerated value for 0 is reserved. */
       rpc_gss_svc_none = 1,
       rpc_gss_svc_integrity = 2,
       rpc_gss_svc_privacy = 3
   };

   /* Credential */

   /*
    * Note: version 0 is reserved for possible future
    * definition of a version negotiation protocol
    *
    */
   #define RPCSEC_GSS_VERS_1 1

   struct rpc_gss_cred_t {
       union switch (unsigned int version) { /* version of
                                                   RPCSEC_GSS */
       case RPCSEC_GSS_VERS_1:
           struct {
               rpc_gss_proc_t gss_proc;  /* control procedure */
               unsigned int seq_num;   /* sequence number */
               rpc_gss_service_t service; /* service used */
               opaque handle<>;       /* context handle */
           } rpc_gss_cred_vers_1_t;

       }
   };

   /* Maximum sequence number value */

   #define MAXSEQ 0x80000000

5.1. Version Selection πŸŽ€

This document defines just one protocol version, RPCSEC_GSS_VERS_1. The client should assume the server supports RPCSEC_GSS_VERS_1 and issue a Context Creation message (as described in the section RPCSEC_GSS_VERS_1, the RPC response will have a reply_stat of MSG_DENIED, a rejection status of AUTH_ERROR, and an auth_stat of AUTH_REJECTED_CRED.

5.2. Context Creation 🌸

Before RPC data gets exchanged on a session using the RPCSEC_GSS flavor, a context has to be set up between the client and the server. Context creation may involve zero or more RPC exchanges. How many exchanges happen depends on the security mechanism 🧚

5.2.1. Mechanism and QOP Selection πŸ”₯

There's no facility in the RPCSEC_GSS protocol to negotiate GSS-API mechanism identifiers or QOP values. At minimum, implementations of the RPCSEC_GSS protocol are expected to provide a means to:

  • specify mechanism identifiers, QOP values, and RPCSEC_GSS service values on the client side, and to
  • enforce mechanism identifiers, QOP values, and RPCSEC_GSS service values on a per-request basis on the server side.

Those capabilities need to exist so that applications can conform to the required set of <mechanism, QOP, service> tuples (see the section entitled Set of GSS-API Mechanisms). An application may negotiate <mechanism, QOP, service> selection within its own protocol or through an out-of-band protocol. So it may be necessary for RPCSEC_GSS implementations to provide programming interfaces for specifying and enforcing <mechanism, QOP, service> 🫢

Implementations may also depend on negotiation schemes built as pseudo-mechanisms under the GSS-API. Because those schemes sit below the GSS-API layer, the RPCSEC_GSS protocol, as specified in this document, can make use of them πŸ’–

5.2.2. Context Creation Requests

okay so the very first RPC request the client sends is what kicks off context creation ✨ inside the call_body structure, rpcvers gets set to 2 πŸŽ€ prog and vers are always the ones for the service being accessed 🧚 and proc is always NULLPROC (zero) πŸ’…

in the cred structure, flavor is set to RPCSEC_GSS (6) πŸ¦‹ the opaque body of cred (the body field) is where the rpc_gss_cred_t structure we looked at earlier gets encoded 🫢

now for the rpc_gss_cred_t fields, bestie, pay attention πŸ‘‘ the version field is set to whatever version of RPCSEC_GSS the client wants to use. this memo documents version RPCSEC_GSS_VERS_1, so version would be set to RPCSEC_GSS_VERS_1 πŸ’– the gss_proc field must be set to RPCSEC_GSS_INIT for the first creation request, and in subsequent creation requests it must be set to RPCSEC_GSS_CONTINUE_INIT 🌸 in a creation request, the seq_num and service fields are undefined, and the server must ignore both of them πŸ™ˆ in the first creation request, handle is NULL (opaque data of zero length) πŸ“ in subsequent creation requests, handle must be equal to the value the server returned πŸ’œ the handle is the identifier for the context, and it does not change for the whole life of the context, including through responses to RPCSEC_GSS_CONTINUE_INIT πŸ”₯

the verifier field in the RPC message header is also described by the opaque_auth structure πŸŽ€ every creation request has a NULL verifier (AUTH_NONE flavor with zero length opaque data) ✨

next up is the call data (the procedure specific parameters) 🌈 here's the twist: proc is set to NULLPROC, so normally there would be zero octets after the verifier 😭 but since no RPC data gets exchanged during context creation, it's safe to transfer information after the verifier πŸ¦‹ this "overloading" of the call data is needed, instead of stuffing the GSS-API token into the RPC header, because RPC Version 2 limits how much data can be sent in the header πŸ’… the opaque body of the credential and verifier fields can each be at most 400 octets long, and GSS tokens can be longer than 800 octets πŸ’€

the call data for a context creation request uses this structure for all creation requests:

   struct rpc_gss_init_arg {
       opaque gss_token<>;
   };

gss_token is the token returned by the call to GSS-API's GSS_Init_sec_context() routine, opaquely encoded 🧚 its value will probably be different in each creation request if there's more than one 🌸 if no token comes back from GSS_Init_sec_context(), the context must have been created (assuming no errors), and there won't be any more creation requests πŸŽ€

when GSS_Init_sec_context() gets called, the parameters replay_det_req_flag and sequence_req_flag must be turned off πŸ™ˆ here's why, no cap:

  • ONC RPC can run over unreliable transports and doesn't give you a layer to reliably reassemble messages 😭 so gaps in message sequencing can happen, and so can out of order messages πŸ’€
  • RPC servers can be multi-threaded 🧡 which means the order in which GSS-API messages get signed or wrapped can be different from the order they get verified or unwrapped, even when the requests go over reliable transports πŸ”₯
  • to maximize implementation convenience, the order in which an ONC RPC entity verifies the header and verifies or unwraps the body of an RPC call or reply is left unspecified 🫢

the RPCSEC_GSS protocol protects against replay attacks, yet it still tolerates out of order delivery or processing of messages and tolerates dropped requests πŸ‘‘ it's giving resilience, periodt πŸ’…


5.2.3. Context Creation Responses

5.2.3.1. Context Creation Response - Successful Acceptance

a successful creation request gets an MSG_ACCEPTED response with a status of SUCCESS ✨ the results field carries a response with this structure:

   struct rpc_gss_init_res {
           opaque handle<>;
           unsigned int gss_major;
           unsigned int gss_minor;
           unsigned int seq_window;
           opaque gss_token<>;
   };

handle is non-NULL opaque data that works as the context identifier πŸ’– the client must use this value in all subsequent requests, whether they're control messages or otherwise 🌸 the gss_major and gss_minor fields hold the results of the GSS_Accept_sec_context() call the server ran 🧚 the values for gss_major are defined in Appendix A of this document πŸŽ€ the values for gss_minor are specific to the GSS-API mechanism and are defined in that mechanism's specification πŸ¦‹ if gss_major is not one of GSS_S_COMPLETE or GSS_S_CONTINUE_NEEDED, the context setup has failed, and in that 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 being used πŸ’œ the gss_token field contains any token returned by the GSS_Accept_sec_context() call the server ran 🫢 a token may come back for both successful values of gss_major ✨ if the value is GSS_S_COMPLETE, the server isn't expecting any more tokens, and the RPC Data Exchange phase has to begin on the client's next request πŸ”₯ if the value is GSS_S_CONTINUE_NEEDED, the server is expecting another token, so the client must send at least one more creation request (with gss_proc set to RPCSEC_GSS_CONTINUE_INIT in the request's credential) carrying the required token πŸ‘‘

in a successful response, the seq_window field is set to the sequence window length the server supports for this context 🌈 this window says how many client requests can be outstanding for this context at once πŸ’… the server will accept "seq_window" requests at a time, and those may arrive out of order πŸ™ˆ the client can use this number to figure out how many threads can send requests on this context at the same time 🧡

if gss_major is GSS_S_COMPLETE, the verifier's (the verf element in the response) flavor field is set to RPCSEC_GSS, and the body field is set to the checksum of seq_window (in network order) πŸŽ€ the QOP used for this checksum is 0 (zero), which is the default QOP ✨ for all other values of gss_major, a NULL verifier (AUTH_NONE flavor with zero-length opaque data) gets used πŸ¦‹

5.2.3.1.1. Client Processing of Successful Context Creation Responses

if the value of gss_major in the response is GSS_S_CONTINUE_NEEDED, then the client, following the GSS-API specification, must invoke GSS_Init_sec_context() using the token returned in gss_token in the context creation response 😀 the client must then generate a context creation request with gss_proc set to RPCSEC_GSS_CONTINUE_INIT πŸ’–

if the value of gss_major in the response is GSS_S_COMPLETE, and the client's previous invocation of GSS_Init_sec_context() returned a gss_major value of GSS_S_CONTINUE_NEEDED, then the client, following the GSS-API specification, must invoke GSS_Init_sec_context() using the token returned in gss_token in the context creation response 🫢 if GSS_Init_sec_context() returns GSS_S_COMPLETE, the context is successfully set up, and the RPC data exchange phase must begin on the client's next request πŸŽ‰

5.2.3.2. Context Creation Response - Unsuccessful Cases

an MSG_ACCEPTED reply (to a creation request) with an acceptance status other than SUCCESS has a NULL verifier (flavor set to AUTH_NONE, and zero length opaque data in the body field), and it's formulated as usual for the different status values πŸ™ˆ

an MSG_DENIED reply (to a creation request) is also formulated as usual πŸ’… but here's the tea 🍡: MSG_DENIED could come back because the server's RPC implementation doesn't recognize the RPCSEC_GSS security flavor 😭 RFC 1831 doesn't specify the right reply status for this case, but common implementation practice seems to be returning a rejection status of AUTH_ERROR with an auth_stat of AUTH_REJECTEDCRED 🌸 even though two new values (RPCSEC_GSS_CREDPROBLEM and RPCSEC_GSS_CTXPROBLEM) have been defined for the auth_stat type, neither of them can be returned in responses to context creation requests πŸ’€ the new auth_stat values can be used for responses to normal (data) requests, and that's covered later 🧚

MSG_DENIED might also be returned if the RPCSEC_GSS version number in the credential isn't supported on the server πŸ¦‹ in that case, the server returns a rejection status of AUTH_ERROR, with an auth_stat of

AUTH_REJECTED_CRED.


5.3. RPC Data Exchange

the data exchange phase is entered after a context has been successfully set up πŸŽ€ the format of the exchanged data depends on the security service used for the request ✨ clients can change the security service and QOP on a per-request basis, but that might not be acceptable to every RPC service πŸ™ˆ some RPC services may "lock" the data exchange phase into the QOP and service used on the first data exchange message πŸ”’ for all three modes of service (no data integrity, data integrity, data privacy), the RPC request header has the same format πŸ’–

5.3.1. 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 an octet stream, and then that octet stream is opaquely encoded as the body field 🧚

the rpc_gss_cred_t fields get set like this πŸ’… the version field is set to the same version value used to create the context, which in the scope of this memo will always be RPCSEC_GSS_VERS_1 πŸ‘‘ the gss_proc field is set to RPCSEC_GSS_DATA πŸ¦‹ the service field is set to indicate the desired service (one of rpc_gss_svc_none, rpc_gss_svc_integrity, or rpc_gss_svc_privacy) πŸ”₯ the handle field is set to the context handle value the client received from the RPC server during context creation πŸ’œ the seq_num field can start at any value below MAXSEQ, and it must be incremented (by one or more) for successive requests πŸŽ€ how sequence numbers get used is described in detail when server processing of the request is discussed πŸ™ˆ

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_GetMIC() call with the desired QOP 🧚 that returns the checksum as an opaque octet stream and its length, and this gets encoded into the body field πŸ¦‹ note that the QOP isn't explicitly specified anywhere in the request πŸ’… it's 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 😀

5.3.2. RPC Request Data

5.3.2.1. RPC Request Data - No Data Integrity

if the service specified is rpc_gss_svc_none, the data (procedure arguments) aren't integrity or privacy protected πŸ™ˆ they get sent in exactly the same way they would be if the AUTH_NONE flavor were used (following the verifier) 🌈 but here's the vibe check πŸ’…: since the RPC header is integrity protected, the sender will still be authenticated in this case πŸ”βœ¨

5.3.2.2. RPC Request Data - With Data Integrity πŸ”βœ¨

okay so when data integrity is turned on, the request data gets wrapped up like this πŸ’…

struct rpc_gss_integ_data {
    opaque databody_integ<>;
    opaque checksum<>;
};

The databody_integ field is built in a specific order. First you make a structure with a sequence number, then the procedure arguments right after it 🧁. That structure is called rpc_gss_data_t here:

struct rpc_gss_data_t {
    unsigned int seq_num;
    proc_req_arg_t arg;
};

seq_num has to be the same value as the one in the credential 🫢. The proc_req_arg_t type is the procedure-specific XDR type that describes the arguments, so it isn't defined in this doc. The octet stream you get from XDR encoding the rpc_gss_data_t structure, plus its length, goes into databody_integ. Heads up though, because the XDR type of databody_integ is opaque, its XDR encoding starts with a four octet length field, and then comes the XDR encoded octet stream of rpc_gss_data_t πŸ“¦.

Now the checksum field. It's the checksum of the XDR encoded octet stream for the rpc_gss_data_t structure. It's NOT the checksum of databody_integ itself, so don't mix those up πŸ™ˆ. You get it by calling GSS_GetMIC() with the same QOP that was used for the header checksum in the verifier. GSS_GetMIC() hands back the checksum as an opaque octet stream along with its length. Since the checksum field is opaque in XDR, the length from GSS_GetMIC() is encoded as a four octet length field, followed by the checksum padded to a multiple of four octets 🌸.

5.3.2.3. RPC Request Data - With Data Privacy πŸŽ€

when data privacy is on, the request data looks like this instead πŸ¦‹

struct rpc_gss_priv_data {
    opaque databody_priv<>
};

To build databody_priv, you construct the rpc_gss_data_t structure again, the same way you did for data integrity πŸ”. Then you call GSS_Wrap() to encrypt the octet stream of that rpc_gss_data_t structure. You use the same QOP value (the qop_req argument to GSS_Wrap()) as the one for the header checksum in the verifier, and you set conf_req_flag (also an argument to GSS_Wrap()) to TRUE πŸ”. GSS_Wrap() returns an opaque octet stream, which is the encrypted rpc_gss_data_t structure, plus its length. That's what gets encoded as databody_priv. Since databody_priv is opaque in XDR, the length GSS_Wrap() returns gets encoded as a four octet length, followed by the encrypted octet stream padded to a multiple of four octets ✨.

5.3.3. Server Processing of RPC Data Requests πŸ–₯οΈπŸ’œ

5.3.3.1. Context Management πŸ—‚οΈ

when a request shows up at the server, the server checks that these things are acceptable πŸ‘€:

  • the version number in the credential
  • the service specified in the credential
  • the context handle specified in the credential
  • the header checksum in the verifier (via GSS_VerifyMIC())
  • the sequence number (seq_num) specified in the credential (more on this follows)

the gss_proc field in the credential must be set to RPCSEC_GSS_DATA for data requests. Otherwise the message gets treated as a control message, and that's a whole different vibe πŸ™…β€β™€οΈ.

the server keeps a window of "seq_window" sequence numbers. It starts at the last sequence number it saw and extends backwards πŸͺŸ. If a sequence number higher than the last one seen comes in (AND if GSS_VerifyMIC() on the header checksum from the verifier returns GSS_S_COMPLETE), the window slides forward to that new number. Say the last sequence number seen is N. Then the server is ready to take requests with sequence numbers from N through (N - seq_window + 1), both included 🎯. If a sequence number falls below that range, it's silently discarded πŸ«₯. If it's inside the range and the server hasn't seen it yet, the request gets accepted, and the server flips a bit to "remember" that it has now seen that number 🧠. If the server finds it has already seen a sequence number inside the window, the request is silently discarded too. The server should pick a seq_window value based on how many requests it expects to handle at the same time. For example, in a threaded implementation, seq_window might equal the number of server threads 🧡. There are no known security issues with picking a large window. The real question is how much space the server is willing to spend tracking requests inside the window πŸ’­.

So why is it silent? Because the server can't tell whether a duplicate or out-of-range request came from a sequencing problem in the client, the network, or the operating system, or from some weird routing quirk, or from a replay attack by an intruder πŸ‘». Dropping the request quietly lets the client recover after it times out, in case the duplicate was accidental or well-meaning 🫢. One side effect of that silent discard is that clients may bump the seq_num by more than one. That makes the window slide forward faster. It isn't believed that doing this gives any benefit, though πŸ€·β€β™€οΈ.

Also, the sequence number algorithm requires the client to increment the sequence number even when it's retrying a request with the same RPC transaction identifier πŸ”’. It's pretty common for clients to end up sending two or more attempts, and then a slow server sends back the reply for the first attempt 🐒. With RPCSEC_GSS, every request and reply gets its own unique sequence number. If the client wants a faster turnaround on the RPC call, it can cache the RPCSEC_GSS sequence number of each request it sends ⚑. Then, when a response with a matching RPC transaction identifier comes back, the client can compute the checksum of each cached sequence number and try to match it against the checksum in the reply's verifier πŸ”.

The data gets decoded according to the service specified in the credential. For integrity or privacy, the server makes sure the QOP value is acceptable and that it's the same one used for the header checksum in the verifier πŸ”’. Also, for integrity or privacy, the server rejects the message (with a reply status of MSG_ACCEPTED and an acceptance status of GARBAGE_ARGS) if the sequence number inside the request body doesn't match the sequence number in the credential 🚫.

5.3.3.2. Server Reply - Request Accepted βœ…

An MSG_ACCEPTED reply to a request in the data exchange phase has its verifier (the verf element in the response) flavor field set to RPCSEC_GSS πŸ’Œ. The body field holds the checksum, which is the output of GSS_GetMIC(), of the sequence number (in network order) from the matching request. The QOP is the same one used for that request πŸŽ€.

If the reply status isn't SUCCESS, the rest of the message is formatted as usual πŸ“.

If the message status is SUCCESS, the rest of the message depends on the service specified in the matching request. Basically, what comes after the verifier is the procedure results, and they're formatted differently depending on which service was requested 🧩.

If no data integrity was requested, the procedure results are formatted as they would be for the AUTH_NONE security flavor 🫧.

If data integrity was requested, the results get encoded in exactly the same way as the procedure arguments were in the matching request. Check out the section 'RPC Request Data - With Data Integrity.' The one difference is that the structure for the procedure's result, proc_res_arg_t, gets swapped in for the request argument structure proc_req_arg_t. The QOP used for the checksum has to be the same one used to build the reply verifier πŸ”.

If data privacy was requested, the results get encoded in exactly the same way as the procedure arguments were in the matching request. Check out the section 'RPC Request Data - With Data Privacy.' The QOP used for encryption has to be the same one used to build the reply verifier πŸ’–.

5.3.3.3. Server Reply - Request Denied πŸ™…β€β™€οΈ

An MSG_DENIED reply (to a data request) is formulated as usual. Two new values, RPCSEC_GSS_CREDPROBLEM and RPCSEC_GSS_CTXPROBLEM, have been defined for the auth_stat type πŸ†•. When the reason for denying the request is a reject_stat of AUTH_ERROR, one of these two new auth_stat values can get returned, in addition to the existing ones. These two new values carry special meaning that's different from the existing reasons for denying a request πŸ’….

The server keeps a list of contexts for the clients that are currently in session with it πŸ“‹. Normally, a context gets destroyed when the client ends the session that goes with it. But because of resource constraints, the server may destroy a context early (on an LRU basis, or if the server machine gets rebooted, for example) πŸ’€. In that case, when a client request comes in, there may not be a context that matches its handle. The server rejects the request with the reason RPCSEC_GSS_CREDPROBLEM in that case. When the client gets this error, it must refresh the context, which means reestablishing it after destroying the old one, and then try the request again πŸ”„. This error also comes back if the context handle matches that of a different context that got allocated after the client's context was destroyed. That gets caught by a failure when verifying the header checksum πŸ•΅οΈβ€β™€οΈ.

If the GSS_VerifyMIC() call on the header checksum (contained in the verifier) doesn't return GSS_S_COMPLETE, the server rejects the request and returns an auth_stat of RPCSEC_GSS_CREDPROBLEM 🚫.

When the client's sequence number goes over the maximum the server will allow, the server rejects the request with the reason RPCSEC_GSS_CTXPROBLEM πŸ“ˆ. Also, if security credentials go stale while they're in use (due to ticket expiry in the case of the Kerberos V5 mechanism, for example), the failures that result cause the RPCSEC_GSS_CTXPROBLEM reason to come back 🎫. In these cases too, the client must refresh the context and retry the request ✨.

For other errors, retrying won't fix anything. The client must not refresh the context until the problem that got its request denied has been fixed πŸ› οΈ.

If the version field in the credential doesn't match the version of RPCSEC_GSS that was used when the context got created, the AUTH_BADCRED value is returned πŸ”’.

If there's a problem with the credential, such as a bad length, an illegal control procedure, or an illegal service, the matching auth_stat status is AUTH_BADCRED ❌.

Other errors can be returned as appropriate 🌈.

5.3.3.4. Mapping of GSS-API Errors to Server Responses πŸ—ΊοΈ

During the data exchange phase, the server may invoke GSS_GetMIC(), GSS_VerifyMIC(), GSS_Unwrap(), and GSS_Wrap(). If any of these routines fail to return GSS_S_COMPLETE, then various unsuccessful responses can be returned. The are described as follows for each of the aforementioned four interfaces. πŸ‘€

5.3.3.4.1. GSS_GetMIC() Failure ✨

When GSS_GetMIC() runs to make the verifier in the response and it fails, you get an RPC response with a reply status of MSG_DENIED, a reject status of AUTH_ERROR, and an auth status of RPCSEC_GSS_CTXPROBLEM. πŸ’…

When GSS_GetMIC() runs to sign the call results (service is rpc_gss_svc_integrity) and it fails, no RPC response gets sent at all 😭. Since ONC RPC server apps are usually the ones deciding when a response goes out, the failure gets handed back to the server application, and it can do something about it, like logging the error πŸ“.

5.3.3.4.2. GSS_VerifyMIC() Failure πŸ¦‹

When GSS_VerifyMIC() runs to check the verifier in the request and it fails, you get an RPC response with a reply status of MSG_DENIED, a reject status of AUTH_ERROR, and an auth status of RPCSEC_GSS_CREDPROBLEM.

When GSS_VerifyMIC() runs to check the call arguments (service is rpc_gss_svc_integrity) and it fails, you get an RPC response with a reply status of MSG_ACCEPTED and an acceptance status of GARBAGE_ARGS. Not the vibe, but it's clear! πŸ™ˆ

5.3.3.4.3. GSS_Unwrap() Failure 🌸

When GSS_Unwrap() runs to decrypt the call arguments (service is rpc_gss_svc_privacy) and it fails, you get an RPC response with a reply status of MSG_ACCEPTED and an acceptance status of GARBAGE_ARGS.

5.3.3.4.4. GSS_Wrap() Failure πŸ“

When GSS_Wrap() runs to encrypt the call results (service is rpc_gss_svc_privacy) and it fails, no RPC response gets sent πŸ’€. Same deal as before: ONC RPC server apps usually control when a response goes out, so the failure indication goes back to the application, and it can take action, like logging the error.

5.4. Context Destruction 🧚

When the client is done with the session, it must send a control message telling the server it doesn't need the context anymore. This message is built just like a data request packet, with a few differences: the credential has gss_proc set to RPCSEC_GSS_DESTROY, the procedure in the header is NULLPROC, and there are no procedure arguments. The sequence number in the request must be valid, and the header checksum in the verifier must be valid too, for the server to accept the message. The server sends back a response the same way it would for a data request. After that, the client and server must both destroy the context for the session πŸ‘‘.

If the request to destroy the context fails for some reason, the client doesn't need to do anything special. The server, though, must be ready for clients that never tell it they're finished with the session, and so never need it to keep a context around πŸ₯Ί. An LRU mechanism or an aging mechanism should be used by the server to clean up in those cases ✨.

6. Set of GSS-API Mechanisms 🌈

RPCSEC_GSS is basically a "pass-through" to the GSS-API layer, so it would be totally inappropriate for the RPCSEC_GSS spec to list a minimum set of required security mechanisms and/or quality of protections. Not its job, bestie πŸ’….

If an application protocol spec references RPCSEC_GSS, that protocol spec must list a mandatory set of { mechanism, QOP, service } triples. That way, an implementation can't claim it conforms to the protocol spec unless it implements the whole set of triples. Inside each triple, mechanism is a GSS-API security mechanism, QOP is a valid quality-of-protection within that mechanism, and service is either rpc_gss_svc_integrity or rpc_gss_svc_privacy πŸ”₯.

For example, a network filing protocol built on RPC that depends on RPCSEC_GSS for security might require that Kerberos V5 with the default QOP using the rpc_gss_svc_integrity service be supported by implementations conforming to that network filing protocol spec πŸ¦‹.


7. Security Considerations πŸ”

7.1. Privacy of Call Header πŸ™ˆ

Okay, so you'll notice that for the privacy option, only the call arguments and results get encrypted. Info about the application, like the RPC program number, program version number, and program procedure number, is sent in the clear 😬. Encrypting those fields in the RPC call header would have changed the size and format of the call header, and that would have meant revising the RPC protocol, which was beyond the scope of this proposal. Storing the encrypted numbers in the credential would have avoided a protocol change, but it would have piled on more overloading of fields and made RPC implementations more complex 🀯. Even if the fields were encrypted somehow, in most cases an attacker can figure out the program number and version number by looking at the destination address of the request and querying the rpcbind service on the destination host [Srinivasan-bind]. Anyway, even without encrypting those three numbers, RPCSEC_GSS still improves security compared to what existing RPC services had before πŸ’–. Implementors of new RPC services who are worried about this risk may choose to design in a "sub-procedure" field that's included in the service-specific call arguments 🌷.

7.2. Sequence Number Attacks πŸŽ€

7.2.1. Sequence Numbers Above the Window

An attacker can't coax the server into raising the sequence number beyond the range the legit client knows about (and so engineer a denial of server attack) without building an RPC request that passes the header checksum πŸ›‘οΈ. If verifying the header checksum is expensive enough (depending on how fast the processor doing the checksum is and how costly the checksum algorithm is), it's possible to picture a denial of service attack (vandalism, in the form of wasting processing resources) where the attacker sends requests that are above the window 😀. The simplest method might be for the attacker to watch network traffic and then pick a sequence number way above the current one. Then the attacker can send bogus requests using that above-window sequence number 🚨.

7.2.2. Sequence Numbers Within or Below the Window

If the attacker sends requests that are within or below the window, then even if the header checksum verifies successfully, the server will silently discard the requests, because the server assumes it has already processed them 🀫. In this case, a server can optimize by skipping the header checksum verification if the sequence number is below the window, or, if it's within the window, by not attempting the checksum verification if the sequence number has already been seen ⚑.

7.3. Message Stealing Attacks πŸ•΅οΈ

This proposal doesn't address attacks where an attacker can block or steal messages without the server noticing. Protecting against that would basically mean assuming a state in the RPC service, which is a whole thing πŸ™ƒ. RPCSEC_GSS doesn't make this situation any worse πŸ’œ.


Appendix A. GSS-API Major Status Codes πŸ“š

The GSS-API definition [Linn] doesn't include numerical values for the various GSS-API major status codes. It's expected that this will be addressed in a future RFC. Until then, this appendix defines the values for each GSS-API major status code listed in the GSS-API definition. If the GSS-API definition defines values for the codes that differ from what follows in the future, then implementors of RPCSEC_GSS will be obliged to map them into the values defined below. If the GSS-API definition defines additional status codes in the future that aren't defined below, then the RPCSEC_GSS definition will subsume those additional values 🌈.

Here are the definitions of each GSS_S_* major status that the implementor of RPCSEC_GSS can expect in the gss_major field of rpc_gss_init_res. These definitions aren't in RPC description language form. The numbers are in base 16 (hexadecimal):

   GSS_S_COMPLETE                  0x00000000
   GSS_S_CONTINUE_NEEDED           0x00000001
   GSS_S_DUPLICATE_TOKEN           0x00000002
   GSS_S_OLD_TOKEN                 0x00000004
   GSS_S_UNSEQ_TOKEN               0x00000008
   GSS_S_GAP_TOKEN                 0x00000010
   GSS_S_BAD_MECH                  0x00010000
   GSS_S_BAD_NAME                  0x00020000
   GSS_S_BAD_NAMETYPE              0x00030000
   GSS_S_BAD_BINDINGS              0x00040000
   GSS_S_BAD_STATUS                0x00050000
   GSS_S_BAD_MIC                   0x00060000
   GSS_S_BAD_SIG                   0x00060000
   GSS_S_NO_CRED                   0x00070000
   GSS_S_NO_CONTEXT                0x00080000
   GSS_S_DEFECTIVE_TOKEN           0x00090000
   GSS_S_DEFECTIVE_CREDENTIAL      0x000a0000
   GSS_S_CREDENTIALS_EXPIRED       0x000b0000
   GSS_S_CONTEXT_EXPIRED           0x000c0000
   GSS_S_FAILURE                   0x000d0000
   GSS_S_BAD_QOP                   0x000e0000
   GSS_S_UNAUTHORIZED              0x000f0000
   GSS_S_UNAVAILABLE               0x00100000
   GSS_S_DUPLICATE_ELEMENT         0x00110000
   GSS_S_NAME_NOT_MN               0x00120000
   GSS_S_CALL_INACCESSIBLE_READ    0x01000000
   GSS_S_CALL_INACCESSIBLE_WRITE   0x02000000
   GSS_S_CALL_BAD_STRUCTURE        0x03000000

Heads up, the GSS-API major status is split into three fields like this πŸ‘€:

     Most Significant Bit                     Least Significant Bit
     |------------------------------------------------------------|
     | Calling Error | Routine Error  |    Supplementary Info     |
     |------------------------------------------------------------|
   Bit 31           24 23            16 15                        0

Up to one status in the Calling Error field can be logically ORed with up to one status in the Routine Error field, which in turn can be logically ORed with zero or more statuses in the Supplementary Info field. If the resulting major status has a non-zero Calling Error and/or a non-zero Routine Error, then the applicable GSS-API operation has failed. For purposes of RPCSEC_GSS, that means the GSS_Accept_sec_context() call run by the server has failed 😡.

If the major status equals GSS_S_COMPLETE, that indicates there are no Errors or Supplementary Info. πŸŽ‰

The meanings of most of the GSS_S_* statuses are defined in the GSS-API definition, with these exceptions:

  • GSS_S_BAD_MIC: This code has the same meaning as GSS_S_BAD_SIG.
  • GSS_S_CALL_INACCESSIBLE_READ: A required input parameter could not be read.
  • GSS_S_CALL_INACCESSIBLE_WRITE: A required input parameter could not be written.
  • GSS_S_CALL_BAD_STRUCTURE: A parameter was malformed.

Acknowledgements 🫢

Much of the protocol was based on the AUTH_GSSAPI security flavor developed by Open Vision Technologies [Jaspan]. Special thanks to Barry Jaspan, Marc Horowitz, John Linn, and Ellen McDermott πŸ’–.

Raj Srinivasan designed RPCSEC_GSS [Eisler] with input from Mike Eisler. Raj, Roland Schemers, Lin Ling, and Alex Chiu contributed to Sun Microsystems' implementation of RPCSEC_GSS πŸ‘‘.

Brent Callaghan, Marc Horowitz, Barry Jaspan, John Linn, Hilarie Orman, Martin Rex, Ted Ts'o, and John Wroclawski analyzed the specification and gave valuable feedback πŸ™Œ.

Steve Nahm and Kathy Slattery reviewed various drafts of this specification 🌸.

Much of the content of Appendix A was excerpted from John Wray's Work in Progress on GSS-API Version 2 C-bindings ✨.

References πŸ“š

Okay bestie, here are the receipts! 🧾 These are all the sources this spec leans on, and every single one is kept exactly as written so nobody gets lost πŸ’–

  • [Eisler] Eisler, M., Schemers, R., and Srinivasan, R. (1996). "Security Mechanism Independence in ONC RPC," Proceedings of the Sixth Annual USENIX Security Symposium, pp. 51-65.
  • [Jaspan] 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.
  • [Linn] Linn, J., "Generic Security Service Application Program Interface, Version 2", RFC 2078, January 1997.
  • [Srinivasan-bind] Srinivasan, R., "Binding Protocols for ONC RPC Version 2", RFC 1833, August 1995.
  • [Srinivasan-rpc] Srinivasan, R., "RPC: Remote Procedure Call Protocol Specification Version 2", RFC 1831, August 1995.
  • [Srinivasan-xdr] Srinivasan, R., "XDR: External Data Representation Standard", RFC 1832, August 1995.

Authors' Addresses 🌸

And finally, who to slide into the DMs πŸ’Œ Here are the authors' contact details, copied exactly as they were:

   Michael Eisler
   Sun Microsystems, Inc.
   M/S UCOS03
   2550 Garcia Avenue
   Mountain View, CA 94043

   Phone: +1 (719) 599-9026
   EMail: mre@eng.sun.com


   Alex Chiu
   Sun Microsystems, Inc.
   M/S UMPK17-203
   2550 Garcia Avenue
   Mountain View, CA 94043

   Phone: +1 (415) 786-6465
   EMail: hacker@eng.sun.com


   Lin Ling
   Sun Microsystems, Inc.
   M/S UMPK17-201
   2550 Garcia Avenue
   Mountain View, CA 94043

   Phone: +1 (415) 786-5084
   EMail: lling@eng.sun.com
πŸ“¦ where this page comes from