GSS-API Data Types

GSS-API Integers

Because the size of an int can vary from platform to platform, GSS-API
provides the following integer data type:OM_uint32which is a 32–bit unsigned integer.

Strings and Similar Data in GSS-API

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:

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.

Note that input_msg_buffer must be deallocated with gss_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. See GSS-API Tokens for more information.

Names in GSS-API

A name refers to a principal. In network-security terminology, a principal is a
user, a program, or a machine. Principals can be either clients or servers.

Some examples of principals are:

A user, such as user@machine, who logs into another machine

A network service, such as nfs@machine

A machine, such as myHost@eng.company.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 the gss_import_name() function. Every imported name has an associated name type, which
indicates the format of the name. See GSS-API OIDs for more about name
types. See Name Types for a list of valid name types.

The gss_buffer_desc structure containing the name to be imported. The application must allocate this structure explicitly. See Strings and Similar Data in GSS-API as well as Example 4-2. This argument must be deallocated with gss_release_buffer() when the application is finished with the space.

A minor modification of the generic example shown in Example 4-1 illustrates how
gss_import_name() can be used. First, the regular string is inserted into a gss_buffer_desc
structure. Then gss_import_name() places the string into a gss_name_t structure.

An imported name can be put back into a gss_buffer_t object for display
in human-readable form with gss_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. See GSS-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 for user@company might contain one version of that name as rendered by
Kerberos v5 and another version that was given by a different mechanism. The
function gss_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 a mechanism 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-3 Internal Names and Mechanism Names

Comparing Names in GSS-API

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. An
access 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 with gss_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 with gss_import_name().

Compare each imported ACL name with the imported client's name, using gss_compare_name().

This process is shown in the following figure. In this case, Step
1 is assumed to be needed.

Figure 4-4 Comparing 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 the
gss_canonicalize_name() function is more efficient.

This approach uses the following steps:

Import the client's name with gss_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.

Use gss_canonicalize_name() to produce a mechanism name version of the client's name.

Use gss_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 using memcmp(), which is a fast, low-overhead function.

This process is shown in the following figure. Again, assume that the server
needs to import the name that is received from the client.

Figure 4-5 Comparing Names (Fast)

Because gss_export_name() expects a mechanism name (MN), you must run gss_canonicalize_name() on the client's
name first.

Caution - Applications should not attempt to deallocate OIDs with free().

Mechanisms and QOPs in GSS-API

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,
Appendix D, Specifying an OID briefly discusses how to find out which mechanisms and QOPs are available,
and how to choose one.

Name Types in GSS-API

Besides QOPs and security mechanisms, OIDs are also used to indicate name types,
which indicate the format for an associated name. For example, the function gss_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 form service@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 the gss_inquire_names_for_mech() function. A
list of name types used by GSS-API is provided in Name Types.

GSS-API Status Codes

All GSS-API functions return two types of codes that provide information on the
function's success or failure. Both types of status codes are returned as OM_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 can be processed with the macros GSS_ROUTINE_ERROR(), GSS_CALLING_ERROR(), and GSS_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 manual.

Every GSS-API function has as a first argument an OM_uint32 type for the minor code status. The minor status code is stored in the OM_uint32 argument when the function returns to the calling function. Consider the following code.

The minor_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.

GSS-API Tokens

The basic unit of “currency” in GSS-API is the token. 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 are context-level tokens and per-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. A message
is a piece of data that an application sends to a peer. For
example, the ls command could be a message that is sent to
an ftp server. A per-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 is only
GSS-API-generated information. However, informally, message and per-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. The send_token() and recv_token() functions in Miscellaneous 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:

The receiving application would have a receiving function, for example, get_a_token(), that would check the token_flag argument.

Through explicit tagging. Applications can use meta-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.

Interprocess Tokens in GSS-API

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. See Exporting and Importing Contexts in GSS-API for information
on multiprocess applications.

The gss_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 with gss_wrap(),
if encryption is available.

Note - Do not assume that interprocess tokens are transferable across different GSS-API implementations.