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
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
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
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
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:
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.
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:
Integrity -- The message is given a Mechanism Integrity Code (MIC) that can be checked by the recipient to ensure that the received message is the same as the one sent. The GSS-API function gss_get_mic() generates a MIC.
Confidentiality -- In addition to receiving a MIC, the message is encrypted. The GSS-API function gss_wrap() performs the encryption.
The difference between the two functions is shown in Figure 1-11.
Figure 1-11 gss_get_mic() vs. gss_wrap()
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().