GSS-API Programming Guide

Programming Using the GSS-API

This section is designed to show, in general steps, how to implement secure data exchange using the GSS-API. It does not explain every GSS-API function. Instead, it concentrates on the half-dozen or so functions that are most central to using the GSS-API. For more information, see Appendix B, GSS-API Reference, which contains a list of all GSS-API functions (as well as GSS-API status codes and data types). Additionally, you can find out more about any GSS-API function by checking its man page.

To make things easier, this manual follows a simple model: A client application sends data to a remote server. The client does so directly — that is, without mediation by transport protocol layers such as RPC. A set of sample programs (client and server) are shown in Appendix A, Sample C–Based GSS-API Programs. Chapter 2, A Walk–Through of the Sample GSS-API Programs takes you step-by-step through these programs.

Overview

These are the basic steps in using the GSS-API:

  1. Each application, sender and recipient, acquires credentials explicitly, if credentials have not been acquired automatically.

  2. The sender initiates a security context and the recipient accepts it.

  3. The sender applies security protection to the message (data) it wants to transmit. This means that it either encrypts the message or stamps it with an identification tag. The sender transmits the protected message.

    (The sender can choose not to apply either security protection, in which case the message has only the default GSS-API security service associated with it. That is authentication, in which the recipient knows that the sender is who it claims to be.)

  4. The recipient decrypts the message (if needed) and verifies it (if appropriate).

  5. (Optional) The recipient returns an identification tag to the sender for confirmation.

  6. Both applications destroy the shared security context. If necessary, they can also deallocate any “leftover” GSS-API data.

Applications that use the GSS-API should include the file gssapi.h.

A general schema of this process is presented in Figure 1–6, which shows one way that the GSS-API can be used; other scenarios are possible.

Figure 1–6 Using the GSS-API: An Overview

Diagram shows how GSS-API establishes context and transfers data.

Credentials

A credential is a data structure that provides proof of an application's claim to a principal name. An application uses a credential to establish its global identity. You can think of a credential as a principal's “identity badge,” a set of information that proves that a person, machine, or program is who it claims to be and, often, what privileges it has.

The GSS-API does not provide credentials. Credentials are created by the security mechanisms that underly the GSS-API, before GSS-API functions are called. In many cases, for example, users receive credentials when they log in to a system.

A given GSS-API credential is valid for a single principal. A single credential can contain several elements for that principal, each created by a different mechanism, as shown in Figure 1–7. This means that a credential acquired on a machine with several security mechanisms will be valid if transferred to a machine that has only a subset of those mechanisms. The GSS-API accesses credentials through the gss_cred_id_t structure; this structure is called a credential handle. Credentials are opaque to applications; you don't need to know the specifics of a given credential.

Figure 1–7 Generalized GSS-API Credential

Diagram shows that GSS-API credential can be made up of one or more mechanisms.

Credentials come in three forms:

Acquiring Credentials

Before a security context can be established, both the server and the client must acquire their respective credentials. Once acquired, credentials can be re-used until they expire, at which time they must be re-acquired. Credentials used by the client and those used by the server might have different lifetimes.

GSS-API-based applications acquire credentials in one of two ways:

In most cases, gss_acquire_cred() is called only by a context acceptor (server), because the context initiator (client) is likely to have received its credentials at login. The initiator, therefore, can usually specify only the default credential. The context acceptor can also bypass using gss_acquire_cred() and use its default credential instead.

The initiator's credential proves its identity to other processes, while the acceptor acquires a credential to enable it to accept a security context. Consider the case of a client making an ftp request to a server. The client already has a credential, from login, and the GSS-API is automatically retrieves that credential when the client attempts to initiate a context. The server program, however, explicitly acquires credentials for the requested service (ftp).

gss_acquire_cred() takes the syntax shown below:

OM_uint32 gss_acquire_cred (
OM_uint32         *minor_status,
const gss_name_t  desired_name,
OM_uint32         time_req,
const gss_OID_set desired_mechs,
gss_cred_usage_t  cred_usage,
gss_cred_id_t     *output_cred_handle,
gss_OID_set       *actual_mechs,
OM_uint32         *time_rec)
minor_status

The status code given by the underlying mechanism upon return.

desired_name

The name of the principal whose credential should be acquired. In our example above, it would be “ftp.” This argument would be created with gss_import_name() (see Names).

Note that if desired_name is set to GSS_C_NO_NAME, then the credential returned is a generic one that causes the GSS-API context-initiation and context-acceptance routines to use their default behavior with regard to credentials. In other words, passing GSS_C_NO_NAME to gss_acquire_cred() returns a credential that, when passed to gss_init_sec_context() or gss_accept_sec_context(), is equivalent to passing them the default credential request (GSS_C_NO_CREDENTIAL). See Context Initiation (Client) and Context Acceptance (Server) for more information.

time_req

The length of time (in seconds) for which the credential should remain valid. Specify GSS_C_INDEFINITE to request the maximum permitted lifetime.

desired_mechs

The set of underlying mechanisms that the application wants to use with this credential. This is a gss_OID_set data structure containing one or more gss_OID structures, each representing an appropriate mechanism. If possible, specify GSS_C_NO_OID_SET to get a default set from the GSS-API.

cred_usage

A flag that indicates how this credential should be used: for context initiation (GSS_C_INITIATE), acceptance (GSS_C_ACCEPT), or both (GSS_C_BOTH).

output_cred_handle

The credential handle returned by this function.

actual_mechs

A set of mechanisms that can be used with this credential. If you don't need to know what the mechanisms are, set this to NULL.

time_rec

The actual number of seconds for which the credential is valid. Set to NULL if this value doesn't interest you.

gss_acquire_cred() returns GSS_S_COMPLETE if it completes successfully. If it cannot return a valid credential, it returns GSS_S_NO_CRED; see the gss_acquire_cred(3GSS) man page for other error codes. An example of acquiring a credential can be found in Acquiring Credentials (program listing in server_acquire_creds()).

gss_add_cred() is similar to gss_acquire_cred(), but allows an application to either create a new credential handle based on an existing credential or to add a new credential element to the existing one. If GSS_C_NO_CREDENTIAL is specified as the existing credential, then gss_add_cred() will create a new credential based on default behavior. See the gss_add_cred(3GSS) man page for more information.

Context Establishment

As stated earlier, the two most significant things that the GSS-API does in providing security are to create security contexts and to protect data. After an application has the credential(s) it needs, it's time to establish a security context. To do this, one application (typically a client) initiates the context, and another (usually a server) accepts it. Multiple contexts between peers are allowed.

The communicating applications establish a joint security context by exchanging authentication tokens. The security context is a pair of GSS-API data structures that contain information shared between the two applications. This information describes the state of each application (in terms of security). A security context is required for protection of data.

Context Initiation (Client)

Security context initiation between an application and a remote peer is done using the gss_init_sec_context() function. If successful, this function returns a context handle for the context being established and a context-level token to send to the acceptor. Before calling gss_init_sec_context(), however, the client should:

  1. Acquire credentials, if necessary, with gss_acquire_cred(). Commonly, however, the client has received credentials at login, and can skip this step.

  2. Import the name of the server into GSS-API internal format with gss_import_name(). See Names for more about names and gss_import_name().

When calling gss_init_sec_context(), the client typically passes the following argument values:

Applications are not bound to use these default values. Additionally, the client will specify its requirements for other security parameters with the req_flags argument. The full set of gss_init_sec_context() arguments is described below.

The context acceptor can require several “handshakes” in order to establish a context; that is, it can require the initiator to send more than one piece of context information before it considers the context fully established. Therefore, for portability, context initiation should always be done as part of a loop that checks whether the context has been fully established.

If the context is not complete, gss_init_sec_context() returns a major-status code of GSS_C_CONTINUE_NEEDED. Thus a loop should use gss_init_sec_context()'s return value to test whether to continue the initiation loop.

The client passes context information to the server in the form of the output token returned by gss_init_sec_context(). The client receives information back from the server as an input token, which can then be passed as an argument to subsequent calls of gss_init_sec_context(). If the received input token has a length of zero, however, then no more output tokens are required by the server.

Therefore, in addition to checking for the return status of gss_init_sec_context(), the loop should check the input token's length to see if a further token needs to be sent to the server. (Before the loop begins, the input token's length should be initialized to zero, either by setting the input token to GSS_C_NO_BUFFER or by setting the structure's length field to a value of zero.)

This is what such a loop can look like, highly generalized:

context = GSS_C_NO_CONTEXT
input token = GSS_C_NO_BUFFER

do

     call gss_init_sec_context(credential, context, name, input token, 
                                           output token, other args...)

     if (there's an output token to send to the acceptor)
          send the output token to the acceptor
          release the output token

     if (the context is not complete)
          receive an input token from the acceptor

     if (there's a GSS-API error)
          delete the context

until the context is complete

Naturally, a real loop will be more complete, with, for example, much more extensive error-checking. See Establishing a Context (program listing in client_establish_context()) for a real example of such a context-initiation loop. Additionally, the gss_init_sec_context(3GSS) man page gives a less generic example than this.

Again, the GSS-API does not send or receive tokens; these must be handled by the application. Examples of token-transferring functions are found in send_token() and recv_token().

Here is a synopsis of gss_init_sec_context(). For more information, see the gss_init_sec_context(3GSS) man page.


Example 1–5 gss_init_sec_context()

     OM_uint32 gss_init_sec_context (
       OM_uint32                    *minor_status,
       const gss_cred_id_t          initiator_cred_handle,
       gss_ctx_id_t                 *context_handle,
       const gss_name_t             target_name,
       const gss_OID                mech_type,
       OM_uint32                    req_flags,
       OM_uint32                    time_req,
       const gss_channel_bindings_t input_chan_bindings,
       const gss_buffer_t           input_token
       gss_OID                      *actual_mech_type,
       gss_buffer_t                 output_token,
       OM_uint32                    *ret_flags,
       OM_uint32                    *time_rec )

minor_status

The status code returned by the underlying mechanism.

initiator_cred_handle

The credential handle for the application. This should be initialized to GSS_C_NO_CREDENTIAL to indicate the default credential to use.

context_handle

The context handle to be returned. This should be set to GSS_C_NO_CONTEXT before the loop begins.

target_name

The name of the principal to connect to; for example, “nfs@machinename.”

mech_type

The security mechanism to use. Set this to GSS_C_NO_OID to get the default provided by the GSS-API.

req_flags

Flags indicating additional services or parameters requested for this context. req_flags flags should be logically OR'd to make the desired bit-mask value, as in:


GSS_C_MUTUAL_FLAG | GSS_C_DELEG_FLAG

GSS_C_DELEG_FLAG

Requests that delegation of the initiator's credentials be permitted. See Delegation.

GSS_C_MUTUAL_FLAG

Requests mutual authentication. See Mutual Authentication.

GSS_C_REPLAY_FLAG

Requests detection of repeated messages. See Out-of-Sequence Detection and Replay Detection.

GSS_C_SEQUENCE_FLAG

Requests detection of out-of-sequence messages. See Out-of-Sequence Detection and Replay Detection.

GSS_C_CONF_FLAG

Requests that the confidentiality service be allowed for transferred messages; that is, that messages be encrypted. If confidentiality is not allowed, then only data-origin authentication and integrity services can be applied (this last only if GSS_C_INTEG_FLAG is not false).

GSS_C_INTEG_FLAG

Requests that the integrity service be applicable to messages; that is, that messages may be stamped with a MIC to ensure their validity.

GSS_C_ANON_FLAG

Requests that the initiator remain anonymous. See Anonymous Authentication.

time_req

The number of seconds for which the context should remain valid. Set this to zero (0) to request the default.

input_chan_bindings

Specific peer-to-peer channel identification information connected with the security context. See Channel Bindings for more information about channel bindings. Set to GSS_C_NO_CHANNEL_BINDINGS if you don't want to use channel bindings.

input_token

Token received from the context acceptor, if any. Should be initialized to GSS_C_NO_BUFFER before the function is called (or its length field set to zero).

actual_mech_type

The mechanism actually used in the context. Specify NULL if you don't need to know.

output_token

The token to send to the acceptor.

ret_flags

Flags indicating additional services or parameters requested for this context. ret_flags flags should be logically AND'd to test the returned bit-mask value, as in:


if (ret_flags & GSS_C_CONF_FLAG)
     confidentiality = TRUE;

GSS_C_DELEG_FLAG

If true, indicates that the initiator's credentials can be delegated. See Delegation.

GSS_C_MUTUAL_FLAG

If true, indicates that mutual authentication is allowed. See Mutual Authentication.

GSS_C_REPLAY_FLAG

If true, indicates that detection of repeated messages is in effect. See Out-of-Sequence Detection and Replay Detection.

GSS_C_SEQUENCE_FLAG

If true, indicates that detection of out-of-sequence messages is in effect. See Out-of-Sequence Detection and Replay Detection.

GSS_C_CONF_FLAG

If true, confidentiality service is allowed for transferred messages; that is, that messages can be encrypted. If confidentiality is not allowed, then only data-origin authentication, and integrity services can be applied (this last only if GSS_C_INTEG_FLAG is not returned as false).

GSS_C_INTEG_FLAG

If true, the integrity service can be applied to messages; that is, that messages can be stamped with a MIC to ensure their validity.

GSS_C_ANON_FLAG

If true, indicates that the context initiator will remain anonymous. See Anonymous Authentication.

GSS_C_PROT_READY_FLAG

Sometimes context establishment can take several passes, and sometimes the client might have to wait before it's complete. Even though a context is not fully established, gss_init_sec_context() can indicate what protection services, if any, will be available after the context is complete. An application can therefore buffer its data, sending it when the context is eventually fully established.

If ret_flags indicates GSS_C_PROT_READY_FLAG, the protection services indicated by the GSS_C_CONF_FLAG and GSS_C_INTEG_FLAG flags are available even if the context has not been fully established (that is, if gss_init_sec_context() returns GSS_S_CONTINUE_NEEDED). An application can then call the appropriate wrapping functions, gss_wrap() or gss_get_mic(), with the preferred protection services, and buffer the output for transfer when the context is complete.

If GSS_C_PROT_READY_FLAG is false, then the application cannot make any assumptions about data protection, and must wait until the context is complete (that is, when gss_init_sec_context() returns GSS_S_COMPLETE).


Note –

Earlier versions of the GSS-API did not support the GSS_C_PROT_READY_FLAG argument, so developers wanting to maximize portability should determine which per-message services are available by looking at the GSS_C_CONF_FLAG and GSS_C_INTEG_FLAG flags after a context has been successfully established.


GSS_C_TRANS_FLAG

This flag indicates whether this context can be exported. For more information on importing and exporting contexts, see Context Export and Import.

time_rec

Number of seconds for which the context will remain valid. Specify NULL if you're not interested in this value.

In general, the parameter values returned when a context is not fully established are those that would be returned when the context is complete. See the gss_init_sec_context() man page for more information.

gss_init_sec_context() returns GSS_S_COMPLETE if it completes successfully. If a context-establishment token is required from the peer application, it returns GSS_S_CONTINUE_NEEDED. If there are errors, it returns error codes, which can be found on the gss_init_sec_context(3GSS) man page.

If context initiation fails, the client should disconnect from the server.

Context Acceptance (Server)

The other half of context establishment is context acceptance, which is done through the gss_accept_sec_context() function. In a typical scenario, a server accepts a context initiated (with gss_init_sec_context()) by a client.

gss_accept_sec_context() takes as its main input an input token sent by the initiator. It returns a context handle as well as an output token to be returned to the initiator. Before gss_accept_sec_context() can be called, however, the server should acquire credentials for the service requested by the client. The server acquires these credentials with the gss_acquire_cred() function. Alternatively, the server can bypass acquiring credentials explicitly and instead specify the default credential (indicated by GSS_C_NO_CREDENTIAL) when calling gss_accept_sec_context().

When calling gss_accept_sec_context(), the server passes the following argument values:

The full set of gss_accept_sec_context() arguments is described in the following paragraphs.

Security context establishment may require several “handshakes”; that is, the initiator and acceptor may have to send more than one piece of context information before the context is fully established. Therefore, for portability, context acceptance should always be done as part of a loop that checks whether the context has been fully established. If it hasn't, gss_accept_sec_context() returns a major-status code of GSS_C_CONTINUE_NEEDED. Thus a loop should use the value returned by gss_accept_sec_context() to test whether to continue the acceptance loop.

The context acceptor returns context information to the context initiator in the form of the output token returned by gss_accept_sec_context(). Subsequently, the acceptor can receive further information from the initiator as an input token, which is then passed as an argument to subsequent calls of gss_accept_sec_context(). When gss_accept_sec_context() has no more tokens to send to the initiator, it returns an output token with a length of zero. Therefore, in addition to checking for the return status of gss_accept_sec_context(), the loop should check the output token's length to see if a further token must be sent. Before the loop begins, the output token's length should be initialized to zero, either by setting the output token to GSS_C_NO_BUFFER or by setting the structure's length field to a value of zero.

This is what such a loop might look like, highly generalized:

context = GSS_C_NO_CONTEXT
output token = GSS_C_NO_BUFFER

do

     receive an input token from the initiator

     call gss_accept_sec_context(context, cred handle, input token,
                                                output token, other args...)

     if (there's an output token to send to the initiator)
          send the output token to the initiator
          release the output token

     if (there's a GSS-API error)
          delete the context

until the context is complete

Naturally, a real loop will be more complete, doing much more extensive error-checking. See Accepting a Context (listing in server_establish_context()) for a real example of such a context-acceptance loop. Additionally, the gss_accept_sec_context() man page gives a somewhat less generic example than this.

Again, the GSS-API does not send or receive tokens; these must be handled by the application. Examples of token-transferring functions are found in send_token() and recv_token().

Here is a synopsis of gss_accept_sec_context(). For more information, see the gss_accept_sec_context(3GSS) man page.


Example 1–6 gss_accept_sec_context()

     OM_uint32 gss_accept_sec_context (
       OM_uint32                    *minor_status,
       gss_ctx_id_t                 *context_handle,
       const gss_cred_id_t          acceptor_cred_handle,
       const gss_buffer_t           input_token_buffer,
       const gss_channel_bindings_t input_chan_bindings,
       const gss_name_t             *src_name,
       gss_OID                      *mech_type,
       gss_buffer_t                 output_token,
       OM_uint32                    *ret_flags,
       OM_uint32                    *time_req,
       gss_cred_id_t                *delegated_cred_handle)

minor_status

The status code returned by the underlying mechanism.

context_handle

The context handle to return to the initiator. This argument should be set to GSS_C_NO_CONTEXT before the loop begins.

acceptor_cred_handle

The handle for the credentials acquired by the acceptor, typically through gss_acquire_cred()). Can be initialized to GSS_C_NO_CREDENTIAL to indicate a default credential to use. If no default credential is defined, the function returns GSS_C_NO_CRED.

(Note: if gss_acquire_cred() was passed GSS_C_NO_NAME as a principal name, it produces a credential that will cause gss_accept_sec_context() to treat it as a default credential.)

input_token_buffer

Token received from the context initiator.

input_chan_bindings

Specific peer-to-peer channel identification information connected with the security context. See Channel Bindings for more information about channel bindings. Set to GSS_C_NO_CHANNEL_BINDINGS if you don't want to use channel bindings.

src_name

The name of the initiating principal; for example, nfs@machinename. If you don't care, set to NULL.

mech_type

The security mechanism used. Set to NULL if you don't care which mechanism is used.

output_token

The token to send to the initiator. Should be initialized to GSS_C_NO_BUFFER before the function is called (or its length field set to zero). If the length is zero, no token needs to be sent.

ret_flags

Flags indicating additional services or parameters requested for this context. ret_flags flags should be logically AND'd to test the returned bit-mask value, as in:


if (ret_flags & GSS_C_CONF_FLAG)
     confidentiality = TRUE;

GSS_C_DELEG_FLAG

Indicates the initiator's credentials may be delegated via the delegated_cred_handle argument. See Delegation.

GSS_C_MUTUAL_FLAG

Indicates that mutual authentication is available. See Mutual Authentication.

GSS_C_REPLAY_FLAG

Indicates that detection of repeated messages is available. See Out-of-Sequence Detection and Replay Detection.

GSS_C_SEQUENCE_FLAG

Indicates that detection of out-of-sequence messages is available. See Out-of-Sequence Detection and Replay Detection.

GSS_C_CONF_FLAG

If true, confidentiality service is allowed for transferred messages; that is, that messages can be encrypted. If confidentiality is not allowed, then only data-origin authentication and integrity services can be applied (this last only if GSS_C_INTEG_FLAG is not returned as false).

GSS_C_INTEG_FLAG

If true, the integrity service can be applied to messages; that is, that messages can be stamped with a MIC to ensure their validity.

GSS_C_ANON_FLAG

Indicates that the context initiator will be anonymous. See Anonymous Authentication.

GSS_C_PROT_READY_FLAG

Sometimes context establishment can take several passes, and sometimes the client can send enough information on the initial passes to allow the acceptor to process context-related data, even though the context is incomplete. In those circumstances the acceptor needs to know in which way, if any, the information has been protected.

If true, GSS_C_PROT_READY_FLAG indicates that the protection services indicated by the GSS_C_CONF_FLAG and GSS_C_INTEG_FLAG flags are available. The acceptor can therefore call the appropriate data–reception functions, gss_unwrap() or gss_verify_mic(), with these services in mind.

(Additionally, as with the context initiator, the acceptor can use these flags in buffering any data it might want to send to the initiator, transmitting it when the context is fully established.)

If GSS_C_PROT_READY_FLAG is false, then the acceptor cannot make any assumptions about data protection, and must wait until the context is complete (that is, when gss_accept_sec_context() returns GSS_S_COMPLETE).


Note –

Earlier versions of the GSS-API did not support the GSS_C_PROT_READY_FLAG argument, so developers wanting to maximize portability should determine which per-message services are available by looking at the GSS_C_CONF_FLAG and GSS_C_INTEG_FLAG flags after a context has been successfully established.


GSS_C_TRANS_FLAG

If true, this context can be exported. For more information on importing and exporting contexts, see Context Export and Import.

time_rec

Number of seconds for which the context will remain valid. Specify NULL if you're not interested in this value.

delegated_cred_handle

The credential handle for credentials received from the context initiator, that is, the client's credentials. Valid only if the initiator has requested that the acceptor act as a proxy: that is, if the ret_flags argument resolves to GSS_C_DELEG_FLAG. See Delegation for more about delegation.

gss_accept_sec_context() returns GSS_S_COMPLETE if it completes successfully. If the context is not complete, it returns GSS_S_CONTINUE_NEEDED. If there are errors, it returns error codes; for more information, see the gss_accept_sec_context(3GSS) man page.

Additional Context Services

The gss_init_sec_context() function (see Context Initiation (Client)) allows an application to request certain additional data protection services beyond basic context establishment. These services, discussed below, are requested through the req_flags argument to gss_init_sec_context().

Because not all mechanisms offer all these services, gss_init_sec_context()'s ret_flags argument indicates which of them are available in a given context. Similarly, the context acceptor can determine which services are available by looking at the ret_flags value returned by the gss_accept_sec_context() function. The additional services are explained in the following sections.

Delegation

If permitted, a context initiator can request that the context acceptor act as a proxy, in which case the acceptor can initiate further contexts on behalf of the initiator. An example of such delegation would be where someone on Machine A wanted to rlogin to Machine B, and then rlogin from Machine B to Machine C, as shown in Figure 1–8. (Depending on the mechanism, the delegated credential identifies B either as A or “B acting for A.”)

Figure 1–8 Credential Delegation

Diagram shows how an intermediate server can be used as a proxy to access a second server securely.

If delegation is permitted, ret_flags will be set to GSS_C_DELEG_FLAG; the acceptor receives a delegated credential as the delegated_cred_handle argument of gss_accept_sec_context(). Delegating a credential is not the same as exporting a context (see Context Export and Import). One difference is that an application can delegate its credentials multiple times simultaneously, while a context can only be held by one process at a time.

Mutual Authentication

If you are using ftpto download files into a public ftp site, you probably don't require that the site prove its identity, even if it requires proof of your own. On the other hand, if you are providing a password or credit card number to an application, you probably want to be sure of the receiver's bona fides. In these cases, mutual authentication is required — that is, both the context initiator and the acceptor must prove their identities.

A context initiator can request mutual authentication by setting gss_init_sec_context()'s req_flags argument to the value GSS_C_MUTUAL_FLAG. If mutual authentication has been authorized, the function indicates authorization by setting the ret_flags argument to this value. If mutual authentication is requested but not available, it is the initiating application's responsibility to respond accordingly — the GSS-API will not terminate a context for this reason. Some mechanisms will perform mutual authentication regardless of whether it has been requested.

Out-of-Sequence Detection and Replay Detection

In the common case where a context initiator is transmitting several sequential data packets to the acceptor, some mechanisms allow the context acceptor to check whether or not the packets are arriving as they should: in the right order, and with no unwanted duplication of packets (shown in Figure 1–9). The acceptor checks for these two conditions when it verifies a packet's validity or when it unwraps a packet; see Unwrapping and Verification for more information.

Figure 1–9 Message Replay and Message Out-of-Sequence

Diagram shows duplicate and out of sequence error conditions.

To request that these two conditions be looked for, the initiator should logically OR the req_flags argument with the values GSS_C_REPLAY_FLAG or GSS_C_SEQUENCE_FLAG when initiating the context with gss_init_sec_context().

Anonymous Authentication

In normal use of the GSS-API, the initiator's identity is made available to the acceptor as a result of the context establishment process. However, context initiators can request that their identity not be revealed to the context acceptor.

As an example, consider an application providing access to a database containing medical information, and offering unrestricted access to the service. A client of such a service might want to authenticate the service (in order to establish trust in any information retrieved from it), but might not want the service to be able to obtain the client's identity (perhaps due to privacy concerns about the specific inquiries, or perhaps to avoid being placed on mailing lists).

To request anonymity, set the req_flags argument of gss_init_sec_context() to GSS_C_ANON_FLAG; to check if anonymity is available, check the ret_flags argument to gss_init_sec_context() or gss_accept_sec_context() to see if this same value is returned.

If anonymity is in effect and gss_display_name() is called on a client name returned by gss_accept_sec_context() or gss_inquire_context(), gss_display_name() will produce a generic anonymous name.


Note –

It is the application's responsibility to take appropriate action if anonymity is requested but not permitted — the GSS-API will not terminate a context on these grounds.


Channel Bindings

For many applications, basic context establishment is sufficient to assure proper authentication of a context initiator. In cases where additional security is desired, the GSS-API offers the use of channel bindings. Channel bindings are tags that identify the particular data channel being used — that is, the origin and endpoint (initiator and acceptor) of the context. Because these tags are specific to the originator and recipient applications, they offer more proof of a valid identity.

Channel bindings are pointed to by the gss_channel_bindings_t data type, which is a pointer to a gss_channel_bindings_struct structure as shown in Example 1–7:


Example 1–7 gss_channel_bindings_t

typedef struct gss_channel_bindings_struct {
OM_uint32       initiator_addrtype;
gss_buffer_desc initiator_address;
OM_uint32       acceptor_addrtype;
gss_buffer_desc acceptor_address;
gss_buffer_desc application_data;
} *gss_channel_bindings_t;

The first two fields are the address of the initiator along with an address type that identifies the format in which the initiator's address is being sent. For example, the inititiator_addrtype might be sent to GSS_C_AF_INET to indicate that the initiator_address is in the form of an Internet address — that is, an IP address. Similarly, the third and fourth fields indicate the address and address type of the acceptor. The final field, application_data, can be used by the application as it wants (it's good programming practice to set it to GSS_C_NO_BUFFER if you're not going to use it). If an application does not want to specify an address, it should set its address type field to GSS_C_AF_NULLADDR. Address Types for Channel Bindings has a list of valid address type values.

These address types indicate address families, rather than specific addressing formats. For address families that contain several alternative address forms, the initiator_address and acceptor_address fields must contain sufficient information to determine which address form is used. When not otherwise specified, addresses should be specified in network byte-order (that is, native byte-ordering for the address family).

To establish a context using channel bindings, the input_chan_bindings argument for gss_init_sec_context() should point to an allocated channel bindings structure. The function concatenates the structure's fields into an octet string, calculates a MIC over this string, and binds the MIC to the output token produced by gss_init_sec_context(). The application then sends the token to the context acceptor, which receives it and calls gss_accept_sec_context(). (See Context Acceptance (Server).) gss_accept_sec_context() calculates a MIC on the received channel bindings and returns GSS_C_BAD_BINDINGS if the MIC does not match.

Because gss_accept_sec_context() returns the transmitted channel bindings, an acceptor can do its own security checking based on the received channel binding values. For example, it might check the value of application_data against code words kept in a secure database. However, in many cases this is “overkill.”


Note –

An underlying mechanism might or might not provide confidentiality for channel binding information. Therefore, an application should not include sensitive information as part of channel bindings unless it knows that confidentiality is ensured. The application might check the ret_flags argument of gss_init_sec_context() or gss_accept_sec_context(), especially for the values GSS_C_CONF_FLAG and GSS_C_PROT_READY_FLAG in order to determine if confidentiality is available. See Context Initiation (Client) or Context Acceptance (Server) for information on ret_flags.


Individual mechanisms can impose additional constraints on addresses and address types that can appear in channel bindings. For example, a mechanism can verify that the initiator_address field of the channel bindings presented to gss_init_sec_context() contains the correct network address of the host system. Portable applications should therefore ensure that they either provide correct information for the address fields or omit addressing information, specifying GSS_C_AF_NULLADDR as the address types.

Context Export and Import

The GSS-API provides a means for exporting and importing a context. The primary reason for this ability is to allow a multiprocess application (usually the context acceptor) to transfer a context from one process to another. For example, an acceptor might have one process that listens for context initiators and another that processes data sent in a context. (test_import_export_context() shows how a context can be saved and restored with these functions.)

The function gss_export_sec_context() creates an interprocess token that contains information about the exported context. (See Interprocess Tokens. This buffer to receive the token should be set to GSS_C_NO_BUFFER before gss_export_sec_context() is called.)

The application then passes the token on to the other process, which accepts it and passes it to gss_import_sec_context(). The same functions used to pass tokens between applications can often be used to pass them between processes as well.

Only one instantiation of a security process can exist at a time. gss_export_sec_context() deactivates the exported context and sets its context handle to GSS_C_NO_CONTEXT. It also deallocates any and all process-wide resources associated with that context. In the event that context exportation cannot be completed, gss_export_sec_context() does not return an interprocess token, but leaves the existing security context unchanged.

Not all mechanisms permit contexts to be exported. An application can determine whether a context can be exported by checking the ret_flags argument to gss_accept_sec_context() or gss_init_sec_context(). If this flag is set to GSS_C_TRANS_FLAG, then the context can be exported. (See Context Acceptance (Server) and Context Initiation (Client).)

Figure 1–10 shows how a multiprocess acceptor might use context exporting to multitask. In this case, Process 1 receives and processes tokens, separating the context-level tokens from the data tokens, and passes the tokens on to Process 2, which deals with data in an application-specific way. In this illustration, the clients have already gotten export tokens from gss_init_sec_context(); they pass them to a user-defined function, send_a_token(), which indicates whether the token it's transmitting is a context-level token or a message token. send_a_token() transmits the tokens to the server. Although not shown here, send_a_token() would presumably be used to pass tokens between threads as well.

Figure 1–10 Exporting Contexts: Multithreaded Acceptor Example

Diagram shows how a multiprocess acceptor can separate context and data tokens, and pass them on to a second process.

Context Information

The GSS-API provides a function, gss_inquire_context(), that obtains information about a given security context (even an incomplete one). Given a context handle, gss_inquire_context() provides the following information about it:

For more information, see the gss_inquire_context(3GSS) man page.

Data Protection

After a context has been established between two peers — say, a client and a server — messages can be protected before being sent.

If you only establish a context and then send a message, you are utilizing the most basic GSS-API protection: authentication, wherein the recipient knows that the message comes from the principal claiming to be the sender. Depending on the underlying security mechanism being used, the GSS-API provides two other levels of protection:

The difference between the two functions is shown in Figure 1–11.

Figure 1–11 gss_get_mic() vs. gss_wrap()

Diagram compares the gss_get_mic and gss_wrap functions.

Which function you use depends on your needs. Because gss_wrap() includes the integrity service, many programs use gss_wrap(). They can test for the availability of the confidentiality service, calling gss_wrap() with or without it, depending on whether it's available. An example is Sending the Data (program listing in call_server()). However, since messages protected with gss_get_mic() don't need to be unwrapped by a recipient, there is a savings in CPU cycles over using gss_wrap(). Thus a program that doesn't need confidentiality may prefer to protect messages with gss_get_mic().

Message Tagging With gss_get_mic()

Programs can use gss_get_mic() to add a cryptographic MIC to a message; the recipient can check this MIC to see if the received message is the same as the one that was sent by calling gss_verify_mic(). gss_get_mic() has the following form:


OM_uint32 gss_get_mic (
OM_uint32          *minor_status,
const gss_ctx_id_t context_handle,
gss_qop_t          qop_req,
const gss_buffer_t message_buffer,
gss_buffer_t       msg_token)

minor_status

The status code returned by the underlying mechanism.

context_handle

The context under which the message will be sent.

qop_req

A requested QOP (Quality of Protection). This is the cryptographic algorithm used in generating the MIC. For portability's sake, applications should specify the default QOP by setting this argument to GSS_C_QOP_DEFAULT whenever possible. (See Appendix C, Specifying an OID on specifying a non-default QOP.)

message_buffer

The message to be tagged with a MIC. This argument must be in the form of a gss_buffer_desc object; see Strings and Similar Data. Must be freed up with gss_release_buffer() when you have finished with it.

msg_token

The token containing the message and its MIC. This must be freed up with gss_release_buffer() when you have finished with it.

Note that gss_get_mic() produces separate output for the message and the MIC. (This is different from gss_wrap(), which bundles them together as output.) This separation means that a sender application must arrange to send both the message and its MIC. More significantly, the receiving application must be able to receive and distinguish the message and the MIC. Ways to ensure the proper processing of message and MIC include:

gss_get_mic() returns GSS_S_COMPLETE if it completes successfully. If the specified QOP is not valid, it returns GSS_S_BAD_QOP. For more information, see the gss_get_mic(3GSS) man page.

Message Wrapping With gss_wrap()

Messages can also be “wrapped” by the gss_wrap() function. Like gss_get_mic(), gss_wrap() provides a MIC; it also encrypts a given message, if confidentiality is requested (and permitted by the underlying mechanism). The message receiver “unwraps” the message with gss_unwrap(). gss_wrap() looks like this:


OM_uint32 gss_wrap (
OM_uint32          *minor_status,
const gss_ctx_id_t context_handle,
int                conf_req_flag,
gss_qop_t          qop_req
const gss_buffer_t input_message_buffer,
int                *conf_state,
gss_buffer_t       output_message_buffer )

minor_status

The status code returned by the underlying security mechanism.

context_handle

The context under which this message will be sent.

conf_req_flag

A flag for requesting the confidentiality service (encryption). If non-zero, both confidentiality and integrity are requested; if zero, only the integrity service is requested.

qop_req

A requested QOP (Quality of Protection). This is the cryptographic algorithm used in generating the MIC and doing the encryption. For portability's sake, applications should specify the default QOP by setting this argument to GSS_C_QOP_DEFAULT whenever possible. (See Appendix C, Specifying an OID on specifying a non-default QOP.)

input_message_buffer

The message to be wrapped. This argument must be in the form of a gss_buffer_desc object; see Strings and Similar Data. Must be freed up with gss_release_buffer() when you have finished with it.

conf_state

A flag that, on the function's return, indicates whether confidentiality was applied or not. If non-zero, confidentiality, message origin authentication, and integrity services were applied. If zero, only message-origin authentication and integrity were applied. Specify NULL if not required.

output_message_buffer

The buffer for the wrapped message. After the application is done with the message, it must release this buffer with gss_release_buffer().

Unlike gss_get_mic(), gss_wrap() wraps the message and its MIC together in the outgoing message, so the function that transmits them need be called only once. On the other end, gss_unwrap() will extract the message (the MIC is not visible to the application).

gss_wrap() returns GSS_S_COMPLETE if the message was successfully wrapped. If the requested QOP was not valid, it returns GSS_S_BAD_QOP. Sending the Data (listing in call_server()) shows an example of gss_wrap() being used. For more information, see the gss_wrap(3GSS) man page.

Wrap Size

Wrapping a message with gss_wrap() increases its size. Because the protected message packet must not be too big to “fit through” a given transportation protocol, the GSS-API provides a function, gss_wrap_size_limit(), that calculates the maximum size of a message that can be wrapped without becoming too large. The application can break up messages that exceed this size before calling gss_wrap(). It's a good idea to check the wrap-size limit before actually wrapping the message.

The amount of the size increase depends on two things:

gss_wrap_size_limit() looks like this:


OM_uint32 gss_wrap_size_limit (
OM_uint32          *minor_status,
const gss_ctx_id_t context_handle,
int                conf_req_flag,
gss_qop_t          qop_req,
OM_uint32          req_output_size,
OM_uint32          *max_input_size)

minor_status

The status code returned by the underlying mechanism.

context_handle

The context under which the data is transmitted.

conf_req_flag

A flag for requesting the confidentiality service (encryption). If non-zero, both confidentiality and integrity are requested; if zero, only the integrity service is requested.

qop_req

A requested QOP (Quality of Protection). This is the cryptographic algorithm used in generating the MIC and doing the encryption. For portability's sake, applications should specify the default QOP by setting this argument to GSS_C_QOP_DEFAULT whenever possible. (See Appendix C, Specifying an OID on specifying a non-default QOP.)

req_output_size

The maximum size (as an int) of a data chunk that a given transport protocol can handle. You must provide this information yourself; since the GSS-API is protocol-independent, it has no way of knowing which protocol is being used.

max_input_size

Returned by the function, this is the maximum size of an unwrapped message that, when wrapped, will not exceed req_output_size.

gss_wrap_size_limit() returns GSS_S_COMPLETE if it completes successfully. If the specified QOP was not valid, it returns GSS_S_BAD_QOP. call_server() includes an example of gss_wrap_size_limit() being used to return the maximum original message size, both if confidentiality is used and if it is not used.

Successful completion of this call does not necessarily guarantee that gss_wrap() will be able to protect a message of length max_input_size bytes, since this ability can depend on the availability of system resources at the time that gss_wrap() is called. For more information, see the gss_wrap_size_limit(3GSS) man page.

Unwrapping and Verification

Once it has been received, a wrapped message must be unwrapped with gss_unwrap(). gss_unwrap() automatically verifies the message against the MIC that is embedded with the wrapped message. If the sender did not wrap the message but used gss_get_mic() to produce a MIC, then the received message can be verified against that MIC with gss_verify_mic(). In this latter case the acceptor must arrange to receive both the message and its MIC.

gss_unwrap()

gss_unwrap() looks like this:


OM_uint32 gss_unwrap (
OM_uint32          *minor_status,
const gss_ctx_id_t context_handle,
const gss_buffer_t input_message_buffer,
gss_buffer_t       output_message_buffer,
int                *conf_state
gss_qop_t          *qop_state)

minor_status

The status code returned by the underlying security mechanism.

context_handle

The context under which this message will be sent.

input_message_buffer

The wrapped message. This argument must be in the form of a gss_buffer_desc object; see Strings and Similar Data. Must be freed up with gss_release_buffer() when you have finished with it.

output_message_buffer

The buffer for the unwrapped wrapped message. After the application is done with the unwrapped message, it must release this buffer with gss_release_buffer(). This argument is also a gss_buffer_desc object.

conf_state

A flag that indicates whether confidentiality was applied or not. If non-zero, then confidentiality, message origin authentication, and integrity services were applied. If zero, only message-origin authentication and integrity were applied. Specify NULL if not required.

qop_state

The QOP (Quality of Protection) used. This is the cryptographic algorithm used in generating the MIC and doing the encryption. Specify NULL if not required.

gss_unwrap() returns GSS_S_COMPLETE if the message was successfully unwrapped. If it cannot verify the message against its MIC, it returns GSS_S_BAD_SIG.

gss_verify_mic()

If a message has been unwrapped, or if it was never wrapped in the first place, it can be verified with gss_verify_mic(). gss_verify_mic() looks like this:


OM_uint32 gss_verify_mic (
OM_uint32          *minor_status,
const gss_ctx_id_t context_handle,
const gss_buffer_t message_buffer,
const gss_buffer_t token_buffer,
gss_qop_t          qop_state)

minor_status

The status code returned by the underlying mechanism.

context_handle

The context under which the message will be sent.

message_buffer

The received message. This argument must be in the form of a gss_buffer_desc object; see Strings and Similar Data. Must be freed up with gss_release_buffer() when you have finished with it.

token_buffer

The token containing the received MIC. This argument must be in the form of a gss_buffer_desc object; see Strings and Similar Data. This buffer must be freed up with gss_release_buffer() when the application has finished with it.

qop_state

The QOP (Quality of Protection) that was applied in generating the MIC. Specify NULL if not required.

gss_verify_mic() returns GSS_S_COMPLETE if the message was successfully verified. If it cannot verify the message against its MIC, it returns GSS_S_BAD_SIG.

Transmission Confirmation (Optional)

After the recipient has unwrapped or verified the transmitted message, it might want to send a confirmation to the sender. This means sending back a MIC for that message. Consider the case of a message that was not wrapped by the sender, but only tagged with a MIC with gss_get_mic(). The process, illustrated in Figure 1–14, is as follows:

  1. The initiator tags the message with gss_get_mic().

  2. The initiator sends the message and MIC to the acceptor.

  3. The acceptor verifies the message with gss_verify_mic().

  4. The acceptor sends the MIC back to the initiator.

  5. The initiator verifies the received MIC against the original message with gss_verify_mic().

Figure 1–14 Confirming MIC'd Data

Diagram shows how message integrity codes are confirmed.

In the case of wrapped data, the gss_unwrap() function never produces a separate MIC, so the recipient must generate it from the received (and unwrapped) message. The process, illustrated in Figure 1–15, is as follows:

  1. The initiator wraps the message with gss_wrap().

  2. The initiator sends the wrapped message.

  3. The acceptor unwraps the message with gss_unwrap().

  4. The acceptor calls gss_get_mic() to produce a MIC for the unwrapped message.

  5. The acceptor sends the derived MIC to the initiator.

  6. The initiator compares the received MIC against the original message with gss_verify_mic().

Figure 1–15 Confirming Wrapped Data

Diagram shows how wrapped messages with message integrity codes are confirmed.

Context Deletion and Data Deallocation

After all messages have been sent and received, and the initiator and acceptor applications have finished, both applications should call gss_delete_sec_context() to destroy their shared context. gss_delete_sec_context() deletes local data structures associated with the context. gss_delete_sec_context() looks like this:


OM_uint32 gss_delete_sec_context (
OM_uint32    *minor_status,
gss_ctx_id_t *context_handle,
gss_buffer_t output_token)

minor_status

The status code returned by the underlying mechanism.

context_handle

The context to delete.

output_token

Should be set to GSS_C_NO_BUFFER.

See the gss_delete_sec_context(3GSS) man page for more information.

For good measure, applications should be sure to deallocate any data space they have allocated for GSS-API data. The functions that do this are gss_release_buffer(), gss_release_cred(), gss_release_name(), and gss_release_oid_set(). See their man pages for more information.