\documentstyle[fullpage,12pt]{article} \title{GSS-API Extensions to Sun RPC} \date{Draft---\today} \author{Barry Jaspan} \setlength{\parskip}{.7\baselineskip} \setlength{\parindent}{0pt} \makeatletter \newcount\savecnt\savecnt=0 \def\saveenum#1{\global\savecnt=\csname c@enum#1\endcsname} \def\restoreenum#1{\csname c@enum#1\endcsname=\savecnt} \makeatother %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% Make _ actually generate an _, and allow line-breaking after it. \let\underscore=\_ \catcode`_=13 \def_{\underscore\penalty75\relax} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% \begin{document} {\setlength{\parskip}{0pt}\maketitle\tableofcontents} \section{Introduction} This document describes the integration of GSS-API authentication and security with Sun RPC. \section{Requirements} The requirements of the GSS-API authentication system for Sun RPC are: \begin{enumerate} \item It must provide mutual authentication between RPC clients and servers. \item It must provide for integrity checking and encryption of all procedure arguments and results passed over the network. \saveenum{i} \end{enumerate} The following features are desired, but not mandatory: \begin{enumerate} \restoreenum{i} \item It should provide for integrity checking and encryption of all ``header information'' that specifies the program and procedure being called. \item It should obey the Sun RPC protocol so that clients using it can interoperate with existing servers. In this case, ``interoperate'' means that existing servers will return an error code indicating that they do not understand the authentication flavor, but not that they do not understand the request at all. \item It should require minimal or no changes to the standard Sun RPC programming paradigm for either clients or servers so that existing code can use it with little or no effort. \item It should operate correctly with all the standard Sun RPC transport mechansims (e.g. UDP and TCP). \saveenum{i} \end{enumerate} \section{Functional Specification} This section describes the programmer's interface to the GSS-API authentication flavor. Knowledge of standard Sun RPC programming is assumed. \subsection{Client Side} A RPC client can select the GSS-API authentication flavor in the same way it can select any other authentication flavor, by setting the cl_auth field of the CLIENT structure to the appropriate value: \begin{verbatim} clnt = clnt_create(server_host, PROG_NUM, PROG_VERS, protocol); clnt->cl_auth = auth_gssapi_create(clnt, ...); \end{verbatim} There are two functions that create GSS-API authentication flavor structures for the cl_auth field, auth_gssapi_create and auth_gssapi_create_default. \begin{verbatim} AUTH *auth_gssapi_create(CLIENT *clnt, OM_uint32 *major_status, OM_uint32 *minor_status, gss_cred_id_t claimant_cred_handle, gss_name_t target_name, gss_OID mech_type, int req_flags, int time_req, gss_OID *actual_mech_type, int *ret_flags, OM_uint32 *time_rec); \end{verbatim} auth_gssapi_create creates a GSS-API authentication structure and provides most of the flexibility of gss_init_sec_context. The arguments have the same interpretation as those of gss_init_sec_context with the same name, except: \begin{description} \item[clnt] The CLIENT structure returned by client_create or one of its relatives. It is not modified. \end{description} auth_gssapi_create calls gss_init_sec_context as needed, passing each generated token to and processing each token returned from the RPC server specified by the RPC handle clnt. On return, if major_status is GSS_S_COMPLETE, the context has been established, the returned AUTH structure is valid, and all of the arguments filled in by gss_init_sec_context have the correct values. If major_status is not GSS_S_COMPLETE then it and minor_status contain error codes that can be passed to gss_display_status and the returned value is NULL. \begin{verbatim} AUTH *auth_gssapi_create_default(CLIENT *clnt, char *service_name); \end{verbatim} auth_gssapi_create_default is a shorthand for auth_gssapi_create that attempts to create a context that provides procedure call and result integrity, using the default credentials and GSS-API mechanism. service_name is parsed as a GSS-API ``service'' name and used as the target name. The other arguments to auth_gssapi_create are as follows: \begin{verbatim} auth_gssapi_create(clnt, &dummy, &dummy, GSS_C_NO_CREDENTIAL, target_name, GSS_C_NULL_OID, GSS_C_MUTUAL_FLAG | GSS_C_REPLAY_FLAG, 0, NULL, NULL, NULL); \end{verbatim} Note that if the underlying default mechanism does not support data integrity (e.g. the trust mechanism), this function will fail. The GSS-API major and minor status codes can be interpreted with auth_gssapi_display_status: \begin{verbatim} void auth_gssapi_display_status(char *msg, OM_uint32 major, OM_uint32 minor); \end{verbatim} All of the error messages associated with the major and minor status are displated on the standard error output, preceeded by the message ``GSS-API authentication error $<$msg$>$:''. \subsection{Server Side} \subsubsection{Service Name Registration} An application server must register the service name(s) that it will use for GSS-API connections before any AUTH_GSSAPI requests will succeed. \begin{verbatim} typedef struct _auth_gssapi_name { char *name; gss_OID type; } auth_gssapi_name; bool_t _svcauth_gssapi_set_names(auth_gssapi_name *names, int num); \end{verbatim} names is an array of name specifications, each of which consists of a null-terminated ASCII representation of a name and the GSS-API name type that should be used to import it with gss_import_name. The name type ``gss_nt_service_name'' is recommended. \subsubsection{Calling Client and Service Identification} Each application server's dispatch function is passed two arguments, the transport mechanism (transp) and the RPC service request (rqstp). If the service request's credential flavor (rq_cred.oa_flavor) is AUTH_GSSAPI (300001)\footnote{The value 4 was originally used, but 300001 has been officially assigned by the IETF.}, then the call has been authenticated. The rq_clntcred field of transp contains the gss_name_t of the authenticated caller and can be passed to gss_display_name to obtain a string represtation or gss_compare_name to compare it with other names. The rq_svccred field of transp contains the GSS-API context established with the caller and can be passed to gss_inquire_context. \subsubsection{Error Logging} An application server can register a function to be called when a failure occurs during GSS-API context establishment with _svcauth_set_log_badauth_func. \begin{verbatim} typedef void (*auth_gssapi_log_badauth_func)(OM_uint32 major, OM_uint32 minor, struct sockaddr_in *raddr, caddr_t data); void _svcauth_set_log_badauth_func(auth_gssapi_log_badauth_func func, caddr_t data); \end{verbatim} The function func is called each time gss_accept_sec_context fails. The major and minor arguments indicate the GSS-API major and minor status codes returned. The raddr field contains the INET socket that the request came from, and the data field contains the data argument of _svcauth_gssapi_set_log_badauth_func. An application server can register a function to be called when an RPC request with an invalid verifier arrives with _svcauth_set_log_badverf_func. \begin{verbatim} typedef void (*auth_gssapi_log_badverf_func)(gss_name_t client, gss_name_t server, struct svc_req *rqst, struct rpc_msg *msg, caddr_t data); void _svcauth_set_log_badverf_func(auth_gssapi_log_badverf_func func, caddr_t data); \end{verbatim} The function specified in func is called each time an invalid verifier is received. The client and server fields identify the (falsely claimed) originating client and the server it originally authenticated to. The raddr and addrlen fields contain the INET socket that the request (claims to have) come from, and data contains the data argument of _svcauth_set_log_badverf_func. \section{Modifications to Sun RPC} The Sun RPC extensible authentication mechanism is designed to allow different authentication systems to be integrated into Sun RPC easily. Unfortunately, it has two drawbacks. First, the existing system has a number of non-general design properties that are intended specifically for Sun's Secure RPC, and second, the existing system has no concept of or ability to perform authentication-flavor-specific operations on function arguments and results passed over the wire. The first problem merely makes the system confusing, since a number of features touted as ``general'' do not make any sense for arbitrary authentication systems. The second problem is more substantial, and can only be corrected by modifications to Sun RPC internals. The following sections describe the necessary modifications to Sun RPC. \subsection{Client Side Authentication, AUTH Structure} The AUTH structure (figure \ref{fig:auth}) encapsulates the data and function pointers for an authentication flavor instance. It has been changed in two ways. \begin{figure}[htbp] \begin{verbatim} typedef struct { struct opaque_auth ah_cred; struct opaque_auth ah_verf; union des_block ah_key; struct auth_ops { void (*ah_nextverf)(); int (*ah_marshal)(); /* nextverf & serialize */ int (*ah_validate)(); /* validate varifier */ int (*ah_refresh)(); /* refresh credentials */ int (*ah_wrap)(); /* encode data for wire */ int (*ah_unwrap)(); /* decode data from wire */ void (*ah_destroy)(); /* destroy this structure */ } *ah_ops; caddr_t ah_private; } AUTH; \end{verbatim} \caption{The AUTH structure, with the new function pointers ah_wrap and ah_unwrap.} \label{fig:auth} \end{figure} First, the new functions ah_wrap and ah_unwrap prepare function arguments and results for transmission over the wire. The authentication mechanism can use them to sign, encrypt, or perform any other operation on the data. Their prototype is: \begin{verbatim} bool_t ah_wrap(AUTH *auth, XDR *out_xdrs, xdrproc_t func, caddr_t ptr); bool_t ah_unwrap(AUTH *auth, XDR *in_xdrs, xdrproc_t func, caddr_t ptr); \end{verbatim} ah_wrap encodes function arguments for transmission. func and ptr are the XDR procedure and pointer that serialize the arguments, and out_xdrs is the xdr stream that the encoded arguments should be written to. ah_unwrap decodes function arguments received from the network. Its arguments are the converse of those to ah_wrap. It is the responsibility of RPC transport mechanisms to call an authorization flavor's ah_wrap and ah_unwrap functions when function arguments or results would normally be written to or read from the wire. Authorization flavors that do not need to perform any encoding or decoding can use the provided function authany_wrap for ah_wrap and ah_unwrap; it consists of the single statement ``return (*func)(out_xdrs, ptr)'' (or in_xdrs, as appropriate). Second, the function ah_refresh has been changed to take the RPC error message that resulted in its being called as an argument. This is necessary since the contents of the error message may dictate how ah_refresh should go about correcting the authentication failure. \subsection{Client Side Transport Mechanisms} Each client side transport mechanism must be modified to call the ah_wrap and ah_unwrap functions from the cl_auth field of the CLIENT structure during the call and reply process. The modification is fairly simple. For example, the UDP transport mechanism used to encode procedure calls like this: \begin{verbatim} if ((! XDR_PUTLONG(xdrs, (long *)&proc)) || (! AUTH_MARSHALL(cl->cl_auth, xdrs)) || (! (*xargs)(xdrs, argsp))) return (cu->cu_error.re_status = RPC_CANTENCODEARGS); \end{verbatim} The last function call in the conditional serializes the arguments onto the xdr stream. This must be replaced with a call to the appropriate ah_wrap function: \begin{verbatim} if ((! XDR_PUTLONG(xdrs, (long *)&proc)) || (! AUTH_MARSHALL(cl->cl_auth, xdrs)) || (! AUTH_WRAP(cl->cl_auth, xdrs, xargs, argsp))) return (cu->cu_error.re_status = RPC_CANTENCODEARGS); \end{verbatim} AUTH_WRAP is a macro that takes the four arguments for an ah_wrap function and extracts and calls the function pointer from the cl_auth structure with the specified arguments. Similarly, the transport mechanism must unwrap procedure results. Again, the UDP mechanism will be instructive. It used to deserialize function results like this: \begin{verbatim} reply_msg.acpted_rply.ar_results.where = resultsp; reply_msg.acpted_rply.ar_results.proc = xresults; ok = xdr_replymsg(&reply_xdrs, &reply_msg); \end{verbatim} The problem here is that xdr_replymsg deserializes an entire reply message, including the results. Since xresults and resultsp are the function and pointer to decode the results, they will be automatically deserialized {\it without} ah_unwrap being invoked. The simplest solution (which is also the normal method used by the TCP mechanism) is to arrange to deserialize the function results explicitly: \begin{verbatim} reply_msg.acpted_rply.ar_results.where = NULL; reply_msg.acpted_rply.ar_results.proc = xdr_void; if ((! xdr_replymsg(&reply_xdrs, &reply_msg)) || (! AUTH_UNWRAP(cl->cl_auth, reply_xdrs, xresults, resultsp))) { return (cu->cu_error.re_status = RPC_CANTENCODEARGS); } \end{verbatim} Since xdr_void does not read any data from the XDR stream, the function results are still available when AUTH_UNWRAP is called. Note that AUTH_UNWRAP should only be called on {\it successful} calls; if the reply message status is not RPC_SUCCESS there are no arguments to read. Currently, the UDP and TCP transport mechanisms has been converted.\footnote{The ``raw'' mechanism, for direct connections, has not been.} \subsection{Service Side Authentication, SVCAUTH and XPRT} Standard Sun RPC service-side authentication consists of a single function per authentication flavor; there is no concept of an AUTH structure containing function pointers and private data as with the client side. Previously, nothing else was necessary, because each flavor only did a single thing (authenticated individual calls in a stateless manner). More functions and state are now required, however; they are stored in the SVCAUTH structure, see figure \ref{fig:svcauth}. \begin{figure}[htbp] \begin{verbatim} typedef struct { struct svc_auth_ops { int (*svc_ah_wrap)(); int (*svc_ah_unwrap)(); } *svc_ah_ops; caddr_t svc_ah_private; } SVCAUTH; \end{verbatim} \caption{The new SVCAUTH structure.} \label{fig:svcauth} \end{figure} There is one SVCAUTH structure per authentication flavor (there is a default, svc_auth_any, for existing authentication flavors that do not need the extra functionality). The svc_ah_wrap and svc_ah_unwrap perform the same logical function as their client-side counterparts. Just as with the client side, it is the responsibility of the transport mechanism to call the svc_ah_wrap and svc_ah_unwrap functions associated with the authentication flavor associated with each RPC call at the appropriate time. Unfortunately, the transport mechanism code does not have access to the RPC call structure containing the authenticator flavor because the RPC call structure itself is not passed as an argument to the necessary functions. The present solution is to add another argument to the transport mechanism structure, xp_auth, that stores the SVCAUTH of the {\it current} call on that mechanism; see figure \ref{fig:xprt}. xp_auth is initialized to svc_auth_any so that existing authentication mechanisms that do not set the field will still operate correctly. \footnote{This is not an great solution, because it forces each transport mechanism to be single threaded. The correct solution is to store the SVCAUTH associated with each RPC call in the RPC call structure; however, doing so would require changing a lot of code to pass around the RPC call structure that currently does not do so. Since other parts of Sun RPC use the XPRT structure in a non-reentrant way, the present solution does not make the situation any worse.}$^{\mbox{,}}$\footnote{A somewhat irrelevant side effect of adding SVCAUTH to XPRT is that the standard include file $<$rpc/rpc.h$>$ had to be changed to include $<$rpc/svc_auth$>$ before $<$rpc/svc.h$>$, whereas they used to be in the opposite order.} \begin{figure}[htbp] \begin{verbatim} typedef struct { int xp_sock; u_short xp_port; /* associated port number */ struct xp_ops { bool_t (*xp_recv)(); /* receive incomming requests */ enum xprt_stat (*xp_stat)(); /* get transport status */ bool_t (*xp_getargs)(); /* get arguments */ bool_t (*xp_reply)(); /* send reply */ bool_t (*xp_freeargs)();/* free mem allocated for args */ void (*xp_destroy)(); /* destroy this struct */ } *xp_ops; int xp_addrlen; /* length of remote address */ struct sockaddr_in xp_raddr; /* remote address */ struct opaque_auth xp_verf; /* raw response verifier */ SVCAUTH *xp_auth; /* auth flavor of current req */ caddr_t xp_p1; /* private */ caddr_t xp_p2; /* private */ } SVCXPRT; \end{verbatim} \caption{The modified XPRT structure, with the xp_auth field.} \label{fig:xprt} \end{figure} Finally, with the modified XPRT structure carrying around the authentication flavor structure, the functions that serialize and deserialize function arguments and results must be modified to use the svc_ah_wrap and svc_ah_unwrap functions. Each service-side transport mechanism has getargs and reply functions that must be modified to use the SVCAUTH_UNWRAP and SVCAUTH_WRAP macros, respectively, in a manner completely parallel to the client side. \subsection{Authenticated Service Identification, svc_req} Sun RPC provides the authenticated credentials of a client to the application server via rq_clntcred (``cooked credentials'') field of the service request (svc_req) structure. In many authentication systems, services are also named entities, and there is no reason that an RPC should be restricted to accepting connections as a single authenticated service name. However, access control decisions may be based on the service name a client authenticated to, so that information must be available to the application server. Figure \ref{fig:svc-req} shows the modified service request structure that contains a single new field, rq_svccred. Like rq_clntcred, the authentication flavor is responsible for setting rq_svccred to the ``cooked'' service credentials associated with a given RPC call. Authentication flavors that do not have the concept of service names can of course leave this field blank. \begin{figure}[htbp] \begin{verbatim} struct svc_req { u_long rq_prog; /* service program number */ u_long rq_vers; /* service protocol version */ u_long rq_proc; /* the desired procedure */ struct opaque_auth rq_cred; /* raw creds from the wire */ caddr_t rq_clntcred; /* read only cooked client cred */ caddr_t rq_svccred; /* read only cooked svc cred */ SVCXPRT *rq_xprt; /* associated transport */ }; \end{verbatim} \caption{The modified svc_req structure, with the rq_svccred field.} \label{fig:svc-req} \end{figure} \subsection{Authentication Negotiation, no_dispatch} In order to avoid having to transmit a full set of authentication information with every call, the service-side authentication mechanism must save state between calls. Establishing that state may require multiple messages between the client-side and service-side authentication mechanisms. The client-side authentication mechanism can make arbitrary RPC calls to the server simply by requiring the programmer to specify the CLIENT structure to the authentication flavor initialization routine. The service side, however, is more complex. In the normal course of events, an RPC call comes in, is authenticated, and is then dispatched to the appropriate procedure. For client- and service-side authentication flavors to communicate indepedent of the server implemented above the RPC layer, the service-side flavor must be able to send a reply to the client directly and {\it prevent} the call from being dispatched. This is implemented by a simple modification to the _authenticate routine, which dispatches each RPC call to the appropriate authentication flavor; see figure \ref{fig:authenticate}. It takes an additional argument, no_dispatch, that instructs the mechanism not to dispatch the RPC call to the specified procedure. \begin{figure}[htbp] \begin{verbatim} why=_authenticate(&r, &msg, &no_dispatch); if (why != AUTH_OK) { svcerr_auth(xprt, why); goto call_done; } else if (no_dispatch) { goto call_done; } \end{verbatim} \caption{A call to the modified _authenticate.} \label{fig:authenticate} \end{figure} If _authenticate sets no_dispatch to true, the call is considered finished and no procedure dispatch takes place. Presumably, an authentication flavor that sets no_dispatch to true also replies to the RPC call with svc_sendreply. Authentication flavors that do not modify no_dispatch implicitly leave it set to false, so the normal dispatch takes place. \subsection{Affected Files} Table \ref{tab:modfiles} lists the files that were affected for each of the modifications described in previous sections. \begin{table}[htbp] \centering \caption{Files modified for each change to Sun RPC.} \label{tab:modfiles} \begin{tabular}{ll} AUTH structure & auth.h \\ & auth_none.c \\ & auth_exit.c \\ & auth_any.c \\ Client Transport Mechanisms & clnt_udp.c \\ & clnt_tcp.c \\ SVCAUTH and XPRT structures & rpc.h \\ & svc.h \\ & svc_auth.h \\ & svc.c \\ & svc_auth.c \\ & svc_auth_any.c \\ & svc_auth_unix.c \\ Server Transport Mechanisms & svc_udp.c \\ & svc_tcp.c \end{tabular} \end{table} \section{GSS-API Authentication Flavor} The following sections describe the implemetation of the GSS-API authentication flavor for Sun RPC. \subsection{Authentication Algorithms} \label{sec:algorithms} \subsubsection{Context Initiation} The client creates a GSS-API context with the server each time it calls auth_gssapi_create. The context is created using the standard gss_init_sec_context and gss_accept_sec_context function calls. The generated tokens are passed between the client and server as arguments and results of normal RPC calls. The client side, in auth_gssapi_create, performs the following steps to initiate a context: \begin{enumerate} \item\label{item:process-token} The client calls gss_init_sec_context. On the first such call, no input token is provided; on subsequent calls, the token received from the server is provided. \item If gss_init_sec_context produces an output token: \begin{enumerate} \item The client transmits the token to the server, identifying itself with client_handle if it has already been received (see next step). The return value from the server will contain a client_handle and one or both of a token and a signed initial sequence number. \item If this is the first response from the server, the client_handle is stored for subsequent calls. Otherwise, the client_handle should be the same as returned on the previous call. \item If the response contains a signed initian sequence number but the context is not yet established, then the response also contains a token that will established the context. The signed initial sequence number is stored. \item If the response contains a token, step \ref{item:process-token} repeated. \end{enumerate} \item The signed initial sequence number is verified using the established context. \end{enumerate} The server side, in _svcauth_gssapi, performs the following steps to initiate a context: \begin{enumerate} \item If a call arrives with no client_handle, a new client_handle is allocated and stored in the database. Otherwise, the client's previous state is is looked up in the database. \item The received token is passed to gss_accept_sec_context. If an output token is generated, it is returned to the client. Note that since the application server may have registered multiple service names and there is no way to determine {\it a priori} which service a token is for, _svcauth_gssapi calls gss_accept_sec_context once for each registered credential until one of them succeedes. The code assumes that GSS_S_FAILURE is the only error that can result from a credential mismatch, so any other error terminates the loop immediately. \item If the context is established, the server signs an initial sequence number and returns it to the client. \end{enumerate} Note that these algorithms require context establishment to be synchronous. If gss_init_sec_context returns GSS_S_COMPLETE upon processing a token, it will either produce a token or not. If it does, then gss_accept_sec_context will return GSS_S_COMPLETE when that token is processed; if it does not, then gss_accept_sec_context already returned GSS_S_COMPLETE (and presumably returned the token that caused gss_init_sec_context to return GSS_S_COMPLETE when processed). The reverse is also true. \subsubsection{RPC Calls} After the GSS-API context is established, both the server and the client posess a client handle and a corresponding sequence number. Each call from the client contains the client handle as the ``credential'' so that the server can identify which context to apply to the call. Each client call and server response includes a ``verifier'' that contains the sealed current sequence number.\footnote{In a future version, the verifier will also contain a signature block for the call header, including the procedure number called.} The sequence number prevents replay attacks\footnote{Although some GSS-API mechanisms provide replay detection themselves, not all of them do; explicitly including the sequence number in the RPC therefore provides better end-to-end security}, but by itself it does not prevent splicing attacks. Each procedure argument and result block consists of the current sequence number and the actual serialized argument string, all sealed with gss_seal. Combining the sequence number with the argument/result data prevents splicing attacks. The sequence number is incremented by one for each RPC call and by one for each response. The client and server will both reject messages that do not contain the expected sequence number. Packets retransmitted by the client should use the {\it same} sequence number as the original packet, since even if the server receives multiple copies only one will be honored. \subsection{RPC Call Credential Structure} Every message transmitted from the client to the server has a credentials (cb_cred) field of the type auth_gssapi_creds: \begin{verbatim} typedef struct _auth_gssapi_creds { bool_t auth_msg; gss_buffer_desc client_handle; }; \end{verbatim} The auth_msg field indicates whether the message is intended for the authentication mechanism for the actual server. Any message whose auth_msg field is true is processed by the authentication mechanism; any message whose auth_msg is false is passed to the application server's dispatch function if authentication suceeds. All messages must have an auth_msg of true until the context is established, since authentication cannot succeed until it is. The client_handle field contains the client handle obtained from the first call to the server. On the first call, this field is empty. \subsection{GSS-API Authentication Flavor Procedures} The GSS-API authentication flavor uses standard RPC calls over the client handle it is provided for the interactions described in \ref{sec:algorithms}. All of the following procedures require the auth_msg field in the credentials to be true; otherwise, the server-side authentication flavor will simply attempt to authenticate the caller and pass the call to the application server. The server-side authentication flavor uses the no_dispatch variable to indicate that it has handled the call. \subsubsection{AUTH_GSSAPI_INIT, AUTH_GSSAPI_CONTINUE_INIT} Context initiation is performed via AUTH_GSSAPI_INIT and AUTH_GSSAPI_CONTINUE_INIT. The former is used to transfer the first token generated by gss_init_sec_context, when no client handle is included in the credentials; the latter is used on subsequent calls, when a client handle is included. Both procedures take an argument of type auth_gssapi_init_arg and return results of the type auth_gssapi_init_res. \begin{verbatim} typedef struct _auth_gssapi_init_arg { u_long version; gss_buffer_desc token; } auth_gssapi_init_arg; \end{verbatim} \begin{description} \item[version] Three versions are presently defined. \begin{description} \item[1] The original version, as described in this document \item[2] In earlier versions of Secure there was a bug in the GSS-API library that affected the contents of accept_sec_context output tokens. A client specifies version 2 to indicate that it expects the correct (fixed) behavior. If the server indicates AUTH_BADCRED or AUTH_FAILED it does not understand this version, so the client should fall back to version 1. \item[3] Version three indicates that channel bindings are in use. The client must specify channel bindings with the version, and the server will as well. If the server indicates AUTH_BADCRED or AUTH_FAILED it does not understand this version, so the client should fall back to version 2 (and cease specifying channel bindings). \item[4] The previous versions all used the old GSS-API krb5 mechanism oid; this version uses the new one specified in the RFC. \end{description} \item[token] The token field contains the token generated by gss_init_sec_context. \end{description} \begin{verbatim} typedef struct _auth_gssapi_init_res { u_long version; gss_buffer_desc client_handle; gss_buffer_desc token; OM_uint32 gss_major, gss_minor; gss_buffer_desc signed_isn; } auth_gssapi_init_res; \end{verbatim} \begin{description} \item[version] There are two versions currently defined. \begin{description} \item[1] The original version, as described in this document. This is the response version for {\it both} versions 1 and 2. The Secure 1.1 server will always return this version. \item[3] Version three indicates that the server specified channel bindings in response to a call arg version number of three. The server must not specify this version unless the client does. \end{description} \item[client_handle] The client_handle field contains the client handle that the client must use in the credentials field in all subsequent RPC calls. In response to AUTH_GSSAPI_CONTINUE_INIT, it is the same client handle that arrived in the credentials. \item[gss_major, gss_minor] The GSS-API error codes that resulted from processing the auth_gssapi_init_arg. If gss_major is GSS_S_COMPLETE, the argument token was processed successfully. Otherwise, gss_major and gss_minor contain the relevant major and minor status codes, and the context currently being negotiated is no longer valid. \item[token] In any response that the client is expecting another token (i.e.: gss_init_sec_context last returned GSS_S_CONTINUE), the token field contains the output token from gss_accept_sec_context. If the client is not expecting a token and this field is not empty, an error has occurred. \item[signed_isn] If the client is not expecting another token (i.e.: the previous call to gss_init_sec_context yielded a token and returned GSS_S_COMPLETE) or the supplied token completes the context, the signed_isn field contains the signed initial sequence number. The server expects the first RPC call to have a sequence number one greater than the initial sequence number (so that the signed_isn block cannot be replayed). If the client is expecting another token and the signed_isn field is not empty, an error has occurred. \end{description} \subsubsection{AUTH_GSSAPI_DESTROY} Context tear-down is performed via AUTH_GSSAPI_DESTROY. This procedure takes no arguments and returns no results; it merely informs the server that the client wishes to destroy the established context. When a client wishes to tear down an established context between itself and a server, auth_gssapi_destroy first calls the AUTH_GSSAPI_DESTROY procedure. The server authenticates the message and immediately sends a ``success'' response with no results. The client and server then both independently call gss_delete_sec_context and discard the context-destruction token that is generated. No RPC error checking is performed by either the client or the server. The client waits a brief time for a success response from the server, but if none arrives it destroys the context anyway since presumably the user is waiting for the application to exit. The server similar ignores any RPC errors since it knows that the client will ignore any errors that are reported. \subsection{RPC Call Authentication Implementation} Once the context has been established, all subsequent RPC calls are authenticated via the verifier described in section \ref{sec:algorithms}. auth_gssapi_marshall, invoked via AUTH_MARSHALL while the RPC call is being created on the client side, serializes the client_handle obtained during context initiation {\it in plaintext} as the credentials and serializes the current sequence number, sealed with gss_seal, as the verifier. auth_gssapi_wrap, invoked next via AUTH_WRAP, serializes a sealed token containing both the sequence number of the current call and the serialized arguments. _svcauth_gssapi, invoked on the server side by _authenticate, uses the client_handle contained in the credentials to look up the correct context and verifies the sequence number provided in the verifier; if the sequence number is not correct, it declares a potential replay attack.\footnote{Retransmitted packets will appear as replay attacks, of course.} The response verifier is set to the serialized sealed incremented sequence number. svc_auth_gssapi_unwrap, invoked when either the application server or _svcauth_gssapi (in response to an AUTH_GSSAPI authentication flavor message) attempts to read its arguments, deserialzes and unseals the block containing the current sequence number and serialized arguments. If the sequence number is incorrect, it declares a splicing attack; otherwise, it unserializes the arguments into the original structure. svc_auth_gssapi_wrap, invoked when either the application server or _svcauth_gssapi attempts to write its response, performs the same operation as auth_gssapi_wrap. auth_gssapi_validate, invoked by the client-side RPC mechanism when an RPC_SUCCESS response is received, verifies that the returned sequence number is one greater than the previous value sent by auth_gssapi_marshall. Finally, auth_gssapi_unwrap, invokved by the client-side RPC mechanism after auth_gssapi_validate succeeds, performs the same operation as svc_auth_gssapi_unwrap. If an RPC request generates an error message (a status of other than RPC_SUCCESS), auth_gssapi_refresh is called. If the error status is AUTH_REJECTEDVERF, then the server rejected the sequence number as invalid or replayed. The client guesses that, on some previous call, the server received a message but the server's response did not make it back to the client; this could happen if the packet got lost or if the server was being debugged and the client timed out waiting for it. As a result, the server is expected a higher sequence number than the client sent. auth_gssapi_refresh increments the sequence number and returns true so that the call will be tried again. The transport mechanism will only call auth_gssapi_refresh twice for each RPC request, so if some other error occurred an infinite loop will not result; however, it is unlikely the the client and server will be able to resynchronize after such an event. \subsection{Client State Information} The client-side GSS-API authentication flavor maintains an auth_gssapi_data structure for each authentication instance: \begin{verbatim} struct auth_gssapi_data { bool_t established; CLIENT *clnt; gss_ctx_id_t context; gss_buffer_desc client_handle; u_long seq_num; int def_cred; /* pre-serialized ah_cred */ u_char cred_buf[MAX_AUTH_BYTES]; u_long cred_len; }; \end{verbatim} The established field indicates whether the authentication context between the client and server has been established. It is set to true when gss_init_sec_context returns GSS_S_COM\-PLETE. When this field is false, the auth_gssapi functions marshall, validate, wrap, and unwrap mimic the ``no authentication'' flavor since there is no context with which to perform authentication functions.\footnote{This field is necessary because, when auth_gssapi_create calls clnt_call to make an RPC call, it has to have set the client's authentication flavor to AUTH_GSSAPI; otherwise, the service-side RPC mechanism will not know to dispatch the call to _svcauth_gssapi. However, with the client's authentication flavor set, all of the authentication flavor's functions will be automatically invoked, even though they are not ready to operate.} The clnt field contains the RPC client structure that can be used to communicate with the GSS-API authentication flavor on the server. The context field contains the context structure created by gss_init_sec_context. The client_handle field contains the client handle used on all RPC calls except the first one; the handle is obtained as the result of the first call. The sequence_number field contains the sequence number that will be used when transmitting RPC calls to the server and verifing the server's responses after the context is initialized. The def_cred field is true if gss_init_sec_context created a default credential, in which case the authentication mechanism is responsible for releasing the default credential that gets automatically allocated. The cred_buf and cred_len fields contain the pre-serialized credentials structure used in each call. This provides a small performance enhancement since the credentials structure does not change very often; the same pre-serialized version can be used on virtually every call. \subsection{Server State Information} \label{sec:server-state} The server-side GSS-API authentication flavor maintains an svcauth_gssapi_data structure for each established or partially established context: \begin{verbatim} typedef struct _svc_auth_gssapi_data { bool_t established; gss_ctx_id_t context; gss_name_t client_name, server_name; gss_cred_id_t server_creds; u_long expiration; u_long seq_num; u_long key; SVCAUTH svcauth; } svc_auth_gssapi_data; \end{verbatim} The established field indicates whether the context is fully established. The context field contains the context created by gss_accept_sec_context. The client_name field contains the client's authenticated name, as returned by gss_accept_sec_context. _svcauth_gssapi sets the ``cooked credentials'' field of the RPC call structure to this value after the call is authenticated; the application server can use it to perform authorization. The server_name field contains the service name that the client established a context with. This is useful if the application server registered more than one service name with the library; it allows the server to determine which service the client chose. The server_creds field contains the service credentials that the client established a context with. It is used to avoid having to scan through the server_creds_list multiple times in the case that context establishment requires more than one round-trip to the server. The expiration field contains the expiration time of the context, as a Unix timestamp. If a context has no expiration (time_rec is GSS_C_INDEFINITE), the expiration time is set to 24 hours in the future. When the structure is created, before the context is established, the expiration time is initialized to small duration (currently 5 minutes) so that partially created and abandoned contexts will be expired quickly. The seq_num field is the current sequence number for the client. The key field is the client's key into the hash table (see below). The client_handle field sent to the client is the key treated as an arbitrary four-byte string. The svcauth field is a kludge that allows the svc_auth_gssapi functions to access the per-client data structure while processing a call. One SVCAUTH structure is allocated for each client structure, and the svc_ah_private field is set to the corresponding client. The client's svcauth field is then set to the new SVCAUTH structure, so that client_data->svcauth->svc_ah_private == client_data. As each request is processed, the transport mechanism's xp_auth field is set to the client's svcauth field; thus, the server-side functions that dispatch to server-side authentication flavors can access an appropriate SVCAUTH structure, and the server-side authentication function that is called can determine the appropriate per-client structure from the SVCAUTH structure. The per-client structures are all stored both in a BSD 4.4 db library hash table and b-tree. The hash table maps client handles (key fields) the client structures, and is used to look up client structures based on the client_handle field of a call's credentials structure. The b-tree stores the client structures as keys, sorted by their expiration time. Each time _svcauth_gssapi is activated, it traverses the tree and destroys all client structures that have expired. \end{document}