GSS-API Programming Guide

GSS-API Data Types

The following sections explain the more important and visible GSS-API data types; see GSS-API Data Types and Values for more information.


Caution – Caution –

It is the responsibility of the calling application to free all data space that has been allocated.


Integers

Because the size of an int can vary from platform to platform, the GSS-API provides the following integer data type:


OM_uint32

which is a 32–bit unsigned integer.


Strings and Similar Data

Since the GSS-API handles all data in internal formats, strings must be converted to a GSS-API format before being passed to GSS-API functions. The GSS-API handles strings with the gss_buffer_desc structure; gss_buffer_t is a pointer to such a structure.


typedef struct gss_buffer_desc_struct {
     size_t     length;
     void       *value;
}  gss_buffer_desc *gss_buffer_t;

Therefore, strings must be put into a gss_buffer_desc structure before being passed to functions that use them. Consider a generic GSS-API function that takes a message and processes it in some way (for example, applies protection to it before it is transmitted), as follows:


Example 1–1 Using Strings

char *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 with gss_release_buffer() when you are finished with it.

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.)

Names

A name refers to a principal — that is, a person, a machine, or an application, such as joe@company or nfs@machinename. In the 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 what kind of format the name is in. (See under OIDs for more about name types, and see Name Types for a list of valid name types).

gss_import_name() looks like this:

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)

minor_status

Status code returned by the underlying mechanism. (See Status Codes.)

input_name_buffer

The gss_buffer_desc structure containing the name to be imported. The application must allocate this explicitly (see Strings and Similar Data as well as Example 1–2.) This argument must be deallocated with gss_release_buffer() when the application is finished with it.

input_name_type

A gss_OID that specifies the format that the input_name_buffer is in. (See Name Types; also, Name Types contains a table of valid name types.)

output_name

The gss_name_t structure to receive the name.

Slightly modifying the generic example shown in Example 1–1, here is how you can use gss_import_name(). First, the regular string is inserted into a gss_buffer_desc structure, and then gss_import_name() places it into a gss_name_t structure.


Example 1–2 Using gss_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 with gss_display_name(); however, gss_display_name() does not guarantee that the resulting string will be the same as the original, because of the way the underlying mechanisms store names. The 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 produced for each mechanism supported by the GSS-API. That is, a gss_name_t structure for “joe@company” might contain one version of that name as rendered by Kerberos v5, another version as given by a different mechanism, and so on. The GSS-API provides a function, gss_canonicalize_name(), that takes as its input an internal name (that is, a gss_name_t structure) and a mechanism and yields a second internal name (also a gss_name_t) that contains only a single version of the name, specific to that mechanism.

Such a mechanism-specific name is called a Mechanism Name (MN). “Mechanism Name” is a slightly confusing label, since it refers not to the name of a mechanism, but to the name of a principal as produced by a given mechanism. This process is illustrated by Figure 1–3.

Figure 1–3 Internal Names and Mechanism Names

Diagram shows how mechanism names are derived.

Comparing Names

Why is such a function useful? Consider the case where a server has received a name from a client and wants 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 this would be as follows:

  1. Import the client name into GSS-API internal format with gss_import_name(), if it hasn't already been imported.

    In some cases, the server will receive a name in internal format, so this step will not be necessary — in particular, if the server is looking up the client's own name. (During context initiation, the client's own name is passed in internal format.)

  2. Import each name in the ACL with gss_import_name().

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

This process is shown in Figure 1–4; in this case, we assume that Step 1 is needed.

Figure 1–4 Comparing Names (Slow)

Diagram shows how internal client names are compared using the gss_compare_name function.

That procedure is fine if you only need to compare the client's name with a few names. However, it is a very slow way to check a large list! Running gss_import_name() and gss_compare_name() for every name in the ACL might require a lot of CPU cycles. This is a better way:

  1. Import the client's name with gss_import_name() (if it hasn't already been imported).

    As with the previous method of comparing names, in some cases the server receives a name in internal format and so this step is not necessary.

  2. Use gss_canonicalize_name() to produce an MN of the client's name.

  3. Use gss_export_name() to produce an “exported name,” a contiguous-string version of the client's name.

  4. 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 Figure 1–5; again, assume the server needs to import the name received from the client.

Figure 1–5 Comparing Names (Fast)

Diagram shows how internal client names are compared using the memcmp function.

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

See the gss_canonicalize_name(3GSS), gss_export_name(3GSS), and gss_import_name(3GSS) man pages for more information.

OIDs

Object Identifiers (OIDs) are used to store the following kinds of data: security mechanisms, QOPs (Quality of Protection values), and name types. OIDs are stored in the GSS-API gss_OID_desc structure; the GSS-API provides a pointer to the structure, gss_OID, as shown here.


Example 1–3 OIDs

typedef 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 1–4 OID Sets

typedef struct gss_OID_set_desc_struct {
        size_t    count;
        gss_OID   elements;
     } gss_OID_set_desc, *gss_OID_set;


Caution – Caution –

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


Mechanisms and QOPs

Although the GSS-API allows applications to choose which underlying security mechanism to use, applications should use the default mechanism selected by the GSS-API if possible. Likewise, the GSS-API allows an application to specify the QOP it wants for protecting data — a QOP (Quality of Protection) is the algorithm used for encrypting data or generating a cryptographic identification tag — the default QOP should be used if possible. The default mechanism is represented by passing the value GSS_C_NULL_OID to functions that expect a mechanism or QOP as an argument.


Caution – Caution –

Specifying a security mechanism or QOP explicitly more or less defeats the purpose of using the GSS-API, because it limits the portability of an application. Other implementations of the GSS-API may not support that QOP or mechanism, or they may support it in limited or unexpected ways. Nonetheless, Appendix C, Specifying an OID briefly discusses how to find out which mechanisms and QOPs are available, and how to choose one.


Name Types

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”, as in “nfs@swim2birds”; if it's equal to, for instance, GSS_C_NT_EXPORT_NAME, then the function knows that it's 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 the GSS-API is given in Name Types.