This section covers the following important GSS-API concepts: GSS-API data types, including principals or names, status codes, and tokens.
The following sections explain the major GSS-API data types. For information about all GSS-API data types, seeGSS-API Data Types and Values.
Because the size of anint can vary from platform to platform, GSS-API provides the following integer data type:OM_uint32which is a 32-bit unsigned integer.
Because GSS-API handles all data in internal formats, strings must be converted to a GSS-API format before being passed to GSS-API functions. GSS-API handles strings with the gss_buffer_desc structure:
typedef struct gss_buffer_desc_struct { size_t length; void *value;} gss_buffer_desc *gss_buffer_t;
gss_buffer_t is a pointer to such a structure. Strings must be put into a gss_buffer_desc structure before being passed to functions that use them. In the following example, a generic GSS-API function applies protection to a message before sending that message.
Example 8 Using Strings in GSS-APIchar *message_string;gss_buffer_desc input_msg_buffer;input_msg_buffer.value = message_string;input_msg_buffer.length = strlen(input_msg_buffer.value) + 1;gss_generic_function(arg1, &input_msg_buffer, arg2...);gss_release_buffer(input_msg_buffer);
Note that input_msg_buffer must be deallocated withgss_release_buffer() when you are finished with input_msg_buffer.
The gss_buffer_desc object is not just for character strings. For example, tokens are manipulated as gss_buffer_desc objects. SeeGSS-API Tokens for more information.
Aname refers to a principal. In network-security terminology, aprincipal is a user, a program, or a system. Principals can be either clients or servers.
Some examples of principals are:
A user, such asuser@system, who logs into another system
A network service, such asnfs@system
A system, such asmysystem@example.com, that runs an application
In GSS-API, names are stored as a gss_name_t object, which is opaque to the application. Names are converted from gss_buffer_t objects to the gss_name_t form by thegss_import_name() function. Every imported name has an associatedname type, which indicates the format of the name. SeeGSS-API OIDs for more about name types. SeeGSS-API Name Types for a list of valid name types.
gss_import_name() has the following syntax:
OM_uint32 gss_import_name ( OM_uint32 *minor-status, const gss_buffer_t input-name-buffer, const gss_OID input-name-type, gss_name_t *output-name)
Status code returned by the underlying mechanism. SeeGSS-API Status Codes.
The gss_buffer_desc structure containing the name to be imported. The application must allocate this structure explicitly. SeeStrings and Similar Data in GSS-API as well asExample 9, Using gss_import_name. This argument must be deallocated withgss_release_buffer() when the application is finished with the space.
A gss_OID that specifies the format ofinput-name-buffer. SeeName Types in GSS-API. Also,GSS-API Name Types contains a table of valid name types.
The gss_name_t structure to receive the name.
A minor modification of the generic example shown inExample 8, Using Strings in GSS-API illustrates howgss_import_name() can be used. First, the regular string is inserted into a gss_buffer_desc structure. Thengss_import_name() places the string into a gss_name_t structure.
Example 9 Usinggss_import_name()char *name_string;gss_buffer_desc input_name_buffer;gss_name_t output_name_buffer;input_name_buffer.value = name_string;input_name_buffer.length = strlen(input_name_buffer.value) + 1;gss_import_name(&minor_status, input_name_buffer, GSS_C_NT_HOSTBASED_SERVICE, &output_name);gss_release_buffer(input_name_buffer);
An imported name can be put back into a gss_buffer_t object for display in human-readable form withgss_display_name(). However,gss_display_name() does not guarantee that the resulting string will be the same as the original due to the way the underlying mechanisms store names. GSS-API includes several other functions for manipulating names. SeeGSS-API Functions.
A gss_name_t structure can contain several versions of a single name. One version is produced for each mechanism that is supported by GSS-API. That is, a gss_name_t structure foruser@company might contain one version of that name as rendered by Kerberos v5 and another version that was given by a different mechanism. The functiongss_canonicalize_name() takes as input an internal name and a mechanism.gss_canonicalize_name() yields a second internal name that contains a single version of the name that is specific to that mechanism.
Such a mechanism-specific name is called amechanism name (MN). A mechanism name does not refer to the name of a mechanism, but to the name of a principal as produced by a given mechanism. This process is illustrated in the following figure.
Figure 4 GSS-API Internal Names and Mechanism Names
Consider the case where a server has received a name from a client and needs to look up that name in an access control list. Anaccess control list, or ACL, is a list of principals with particular access permissions.
One way to do the lookup would be as follows:
Import the client name into GSS-API internal format withgss_import_name(), if the name has not already been imported.
In some cases, the server will receive a name in internal format, so this step will not be necessary. For example, a server might look up the client's own name. During context initiation, the client's own name is passed in internal format.
Import each name in the ACL withgss_import_name().
Compare each imported ACL name with the imported client's name, usinggss_compare_name().
This process is shown inComparing GSSAPI Names (Slow). In this case, Step 1 is assumed to be needed.
Figure 5 Comparing GSSAPI Names (Slow)
The previous approach of comparing names individually is acceptable when there are only a few names. When there are a large number of names, using thegss_canonicalize_name() function is more efficient.
This approach uses the following steps:
Import the client's name withgss_import_name(), if the name has not already been imported.
As with the previous method of comparing names, if the name is already in internal format, this step is unnecessary.
Usegss_canonicalize_name() to produce a mechanism name version of the client's name.
Usegss_export_name() to produce an exported name, which is the client's name as a contiguous string.
Compare the exported client's name with each name in the ACL by usingmemcmp(), which is a fast, low-overhead function.
This process is shown inComparing GSSAPI Names (Fast). Again, assume that the server needs to import the name that is received from the client.
Figure 6 Comparing GSSAPI Names (Fast)
Becausegss_export_name() expects a mechanism name (MN), youmust rungss_canonicalize_name() on the client's name first.
See thegss_export_name(3gss),gss_import_name(3gss), andgss_canonicalize_name(3gss) for more information.
Object identifiers (OIDs) are used to store the following kinds of data:
Security mechanisms
Name types
OIDs are stored in GSS-API gss_OID_desc structure. GSS-API provides a pointer to the structure, gss_OID, as shown in the following example.
Example 10 OIDs Structuretypedef struct gss_OID_desc_struct { OM_uint32 length; void *elements; } gss_OID_desc, *gss_OID;
Further, one or more OIDs might be contained in a gss_OID_set_desc structure.
Example 11 OID Set Structuretypedef struct gss_OID_set_desc_struct { size_t count; gss_OID elements; } gss_OID_set_desc, *gss_OID_set;
Although GSS-API allows applications to choose underlying security mechanisms, applications should use the default mechanism that has been selected by GSS-API if possible. Similarly, although GSS-API lets an application specify a Quality of Protection level for protecting data, the default QOP should be used if possible. Acceptance of the default mechanism is indicated by passing the value GSS_C_NULL_OID to functions that expect a mechanism or QOP as an argument.
Caution - Specifying a security mechanism or QOP explicitly defeats the purpose of using GSS-API. Such a specific selection limits the portability of an application. Other implementations of GSS-API might not support that QOP or mechanism in the intended manner. Nonetheless,Specifying an OID briefly discusses how to find out which mechanisms and QOPs are available, and how to choose one. |
Besides QOPs and security mechanisms, OIDs are also used to indicate name types, which indicate the format for an associated name. For example, the functiongss_import_name(), which converts the name of a principal from a string to a gss_name_t type, takes as one argument the format of the string to be converted. If the name type is, for example, GSS_C_NT_HOSTBASED_SERVICE, then the function knows that the name being input is of the formservice@host. If the name type is GSS_C_NT_EXPORT_NAME, then the function expects a GSS-API exported name. Applications can find out which name types are available for a given mechanism with thegss_inquire_names_for_mech() function. A list of name types used by GSS-API is provided inGSS-API Name Types.
All GSS-API functions return two types of codes that provide information about the function's success or failure. Both types of status codes are returned asOM_uint32 values.
The two types of return codes are as follows:
Major status codes
Major status codes indicate the following errors:
Generic GSS-API routine errors, such as giving a routine an invalid mechanism
Call errors that are specific to a particular GSS-API language binding, such as a function argument that cannot be read, cannot be written, or is malformed
Both types of errors
Additionally, major status codes can provide supplementary information about a routine's status. For example, a code might indicate that an operation is not finished, or that a token has been sent out of order. If no errors occur, the routine returns a major status value of GSS_S_COMPLETE.
Major status codes are returned as follows:
OM_uint32 major_status ; /* status returned by GSS-API */major_status = gss_generic_function(arg1, arg2 ...);
Major status return codes can be processed like any otherOM_uint32. For example, consider the following code.
OM_uint32 maj_stat;maj_sta = gss_generic_function(arg1, arg2 ...);if (maj_stat == GSS_CREDENTIALS_EXPIRED)<do something...>
Major status codes can be processed with the macrosGSS_ROUTINE_ERROR(),GSS_CALLING_ERROR(), andGSS_SUPPLEMENTARY_INFO().GSS-API Status Codes explains how to read major status codes and contains a list of GSS-API status codes.
Minor status codes
Minor status codes are returned by the underlying mechanism. These codes are not specifically documented in this guide.
Every GSS-API function has as a first argument anOM_uint32 type for the minor code status. The minor status code is stored in theOM_uint32 argument when the function returns to the calling function. Consider the following code.
OM_uint32 *minor_status ; /* status returned by mech */major_status = gss_generic_function(&minor_status, arg1, arg2 ...);
Theminor_status parameter is always set by a GSS-API routine, even if a fatal major status code error is returned. Note that most other output parameters can remain unset. However, output parameters that are expected to return pointers to storage that has been allocated by the routine are set to NULL. NULL indicates that no storage was actually allocated. Any length field associated with such pointers, as in a gss_buffer_desc structure, are set to zero. In such cases, applications do not need to release these buffers.
The basic unit of "currency" in GSS-API is thetoken. Applications that use GSS-API communicate with each other by using tokens. Tokens are used for exchanging data and for making security arrangements. Tokens are declared as gss_buffer_t data types. Tokens are opaque to applications.
Two types of tokens arecontext-level tokens andper-message tokens. Context-level tokens are used primarily when a context is established, that is, initiated and accepted. Context-level tokens can also be passed afterward to manage a context.
Per-message tokens are used after a context has been established. Per-message tokens are used to provide protection services on data. For example, consider an application that wants to send a message to another application. That application might use GSS-API to generate a cryptographic identifier to go along with that message. The identifier would be stored in a token.
Per-message tokens can be considered with regard to messages as follows. Amessage is a piece of data that an application sends to a peer. For example, thels command could be a message that is sent to anftp server. Aper-message token is an object generated by GSS-API for that message. A per-message token could be a cryptographic tag or the encrypted form of the message. Note that this last example is mildly inaccurate. An encrypted message is still a message and not a token. A token isonly GSSAPI-generated information. However, informally,message andper-message token are often used interchangeably.
An application is responsible for the following activities:
Sending and receiving tokens. The developer usually needs to write generalized read and write functions for performing these actions. Thesend_token() andrecv_token() functions inMiscellaneous GSS-API Sample Functions.
Distinguishing between types of tokens and manipulating the tokens accordingly.
Because tokens are opaque to applications, the application does not distinguish between one token and another. Without knowing a token's contents, an application must be able to distinguish the token's type to pass that token to an appropriate GSS-API function.
An application can distinguish token types through the following methods:
By state. Through the control-flow of a program. For example, an application that is waiting to accept a context might assume that any received tokens are related to context establishment. Peers are expected to wait until the context is fully established before sending message tokens, that is, data. After the context is established, the application assumes that new tokens are message tokens. This approach to handling tokens is a fairly common way to handle tokens. The sample programs in this book use this method.
By flags. For example, if an application has a function for sending tokens to peers, that application can include a flag to indicate the kind of token. Consider the following code:
gss_buffer_t token; /* declare the token */OM_uint32 token_flag /* flag for describing the type of token */<get token from a GSS-API function>token_flag = MIC_TOKEN; /* specify what kind of token it is */send_a_token(&token, token_flag);
The receiving application would have a receiving function, for example,get_a_token(), that would check thetoken_flag argument.
Through explicit tagging. Applications can usemeta-tokens. A meta-token is a user-defined structure that contain tokens that have been received from GSS-API functions. A meta-token includes user-defined fields that signal how the tokens that are provided by GSS-API are to be used.
GSS-API permits a security context to be passed from one process to another in a multiprocess application. Typically, a application has accepted a client's context. The application then shares the context among that application's processes. SeeExporting and Importing Contexts in GSS-API for information about multiprocess applications.
Thegss_export_context() function creates an interprocess token. This token contains information that enables the context to be reconstituted by a second process. The application is responsible for passing the interprocess token from one process to the other. This situation is similar to the application's responsibility for passing tokens to other applications.
The interprocess token might contain keys or other sensitive information. Not all GSS-API implementations cryptographically protect interprocess tokens. Therefore, the application must protect interprocess tokens before an exchange takes place. This protection might involve encrypting the tokens withgss_wrap(), if encryption is available.