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.
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:
Acquire credentials, if necessary, with gss_acquire_cred(). Commonly, however, the client has received credentials at login, and can skip this step.
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:
GSS_C_NO_CREDENTIAL
for the cred_handle argument, to indicate the default credential.
GSS_C_NULL_OID
for the mech_type argument, to indicate the default mechanism.
GSS_C_NO_CONTEXT
for the context_handle argument, to indicate an initial null context. Because gss_init_sec_context() is usually called in a loop, subsequent calls should pass the context handle returned by previous calls.
GSS_C_NO_BUFFER
for the input_token argument, to indicate an initially empty token. Alternatively, the application can pass a pointer to a gss_buffer_desc object whose length field has been set to zero.
The name of the server, imported into internal GSS-API format with gss_import_name().
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 isn't 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.
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 )
The status code returned by the underlying mechanism.
The credential handle for the application. This should be initialized to GSS_C_NO_CREDENTIAL
to indicate the default credential to use.
The context handle to be returned. This should be set to GSS_C_NO_CONTEXT
before the loop begins.
The name of the principal to connect to; for example, “nfs@machinename.”
The security mechanism to use. Set this to GSS_C_NO_OID
to get the default provided by the GSS-API.
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
Requests that delegation of the initiator's credentials be permitted. See Delegation.
Requests mutual authentication. See Mutual Authentication.
Requests detection of repeated messages. See Out-of-Sequence Detection and Replay Detection.
Requests detection of out-of-sequence messages. See Out-of-Sequence Detection and Replay Detection.
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).
Requests that the integrity service be applicable to messages; that is, that messages may be stamped with a MIC to ensure their validity.
Requests that the initiator remain anonymous. See Anonymous Authentication.
The number of seconds for which the context should remain valid. Set this to zero (0) to request the default.
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.
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).
The mechanism actually used in the context. Specify NULL if you don't need to know.
The token to send to the acceptor.
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;
If true, indicates that the initiator's credentials can be delegated. See Delegation.
If true, indicates that mutual authentication is allowed. See Mutual Authentication.
If true, indicates that detection of repeated messages is in effect. See Out-of-Sequence Detection and Replay Detection.
If true, indicates that detection of out-of-sequence messages is in effect. See Out-of-Sequence Detection and Replay Detection.
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).
If true, the integrity service can be applied to messages; that is, that messages can be stamped with a MIC to ensure their validity.
If true, indicates that the context initiator will remain anonymous. See Anonymous Authentication.
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).
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.
This flag indicates whether this context can be exported. For more information on importing and exporting contexts, see Context Export and Import.
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.
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 credential handle returned by gss_acquire_cred(), or GSS_C_NO_CREDENTIAL
to indicate the default credential, for the cred_handle argument.
GSS_C_NO_CONTEXT
for the context_handle argument, to indicate an initial null context. Note that since gss_init_sec_context() is usually called in a loop, subsequent calls should pass the context handle returned by previous calls.
The context token received from the client for the input_token argument.
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.
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)
The status code returned by the underlying mechanism.
The context handle to return to the initiator. This argument should be set to GSS_C_NO_CONTEXT
before the loop begins.
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.)
Token received from the context initiator.
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.
The name of the initiating principal; for example, nfs@machinename. If you don't care, set to NULL.
The security mechanism used. Set to NULL if you don't care which mechanism is used.
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.
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;
Indicates the initiator's credentials may be delegated via the delegated_cred_handle argument. See Delegation.
Indicates that mutual authentication is available. See Mutual Authentication.
Indicates that detection of repeated messages is available. See Out-of-Sequence Detection and Replay Detection.
Indicates that detection of out-of-sequence messages is available. See Out-of-Sequence Detection and Replay Detection.
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).
If true, the integrity service can be applied to messages; that is, that messages can be stamped with a MIC to ensure their validity.
Indicates that the context initiator will be anonymous. See Anonymous Authentication.
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).
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.
If true, this context can be exported. For more information on importing and exporting contexts, see Context Export and Import.
Number of seconds for which the context will remain valid. Specify NULL if you're not interested in this value.
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.
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.
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.”)
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.
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.
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.
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().
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.
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.
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–8:
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.”
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.
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.
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:
The name of the context initiator.
The name of the context acceptor.
The number of seconds for which the context will remain valid.
The security mechanism used with the context.
Several context-parameter flags. These flags are the same as the ret_flags argument of the gss_accept_sec_context() function (see Context Acceptance (Server)), covering delegation, mutual authentication, and so on.
A flag indicating whether or not the inquiring application was the context initiator.
A flag indicating whether or not the context is fully established.
For more information, see the gss_inquire_context(3GSS) man page.