RFC 2744

Generic Security Service API Version 2 : C-bindings

πŸ“… published January 2000, from ietf

Okay bestie, RFC 2744 is here and it's giving security API realness πŸ’–βœ¨

This one is called Generic Security Service API Version 2 : C-bindings πŸŽ€. It was written by J. Wray over at Iris Associates, and it's dated January 2000. It's on the Standards Track, so it's an actual standards doc and not just vibes πŸ’…. It obsoletes RFC 1509, which means this is the glow-up version that replaces it πŸ¦‹. The Network Working Group is the crew that put it out 🌸.

Status of This Memo

Okay so this section is the official "what is this doc" bit 🧚. It says this memo specifies an Internet standards track protocol for the Internet community, and it's asking for discussion and suggestions for improvements 🫢. If you want to know where this protocol stands, check the current edition of the "Internet Official Protocol Standards" (STD 1) πŸ“š. Distribution of this memo is unlimited, so share it with everyone πŸ’œ.

Copyright (C) The Internet Society (2000). All Rights Reserved. πŸ”’

Abstract

Okay the abstract is giving summary of the whole vibe ✨. This doc specifies C language bindings for Version 2, Update 1 of the Generic Security Service Application Program Interface (GSS-API) πŸ”₯. The language-independent, conceptual version of GSS-API is described in RFC-2743 [GSSAPI] 🌈. This memo obsoletes RFC-1509 and makes specific incremental changes based on implementation experience and liaison requests πŸ“. So it's intended that this memo, or a successor version of it, will become the basis for later progression of the GSS-API specification on the standards track πŸ‘‘.

The Generic Security Service Application Programming Interface gives security services to its callers, and it's meant to run on top of a variety of underlying cryptographic mechanisms πŸ¦‹. Usually, GSS-API callers are application protocols that get security enhancements by calling the services GSS-API provides πŸ’…. The GSS-API lets a caller application authenticate a principal identity tied to a peer application, delegate rights to a peer, and apply security services like confidentiality and integrity on a per-message basis πŸ”. It's literally the whole security toolkit, bestie 😭.

1. Introduction ✨

Okay so the intro is where it all starts πŸŽ€. The Generic Security Service Application Programming Interface [GSSAPI] gives security services to calling applications πŸ’–. It lets a communicating application authenticate the user associated with another application, delegate rights to another application, and apply security services such as confidentiality and integrity on a per-message basis 🫢.

There are four stages to using the GSS-API, and I am obsessed with how clean they are πŸ‘‘:

a) The application gets a set of credentials that it can use to prove its identity to other processes πŸͺͺ. The application's credentials vouch for its global identity, which may or may not be related to any local username it might be running under 🌸.

b) A pair of communicating applications sets up a joint security context using their credentials πŸ”₯. The security context is a pair of GSS-API data structures holding shared state information, which is required so that per-message security services can be provided 🧠. Examples of state that might be shared between applications as part of a security context are cryptographic keys and message sequence numbers πŸ”‘. When the context gets established, the context initiator is authenticated to the responder, and the initiator may require that the responder is authenticated in turn 🀝. The initiator may optionally give the responder the right to initiate further security contexts, acting as an agent or delegate of the initiator 🧚. This transfer of rights is called delegation, and it's done by creating a set of credentials that are similar to the ones the initiating application uses, but that the responder can use πŸ¦‹.

To set up and keep the shared information that makes up the security context, certain GSS-API calls will return a token data structure πŸŽ€. A token is an opaque data type that may contain cryptographically protected data πŸ”. The caller of such a GSS-API routine is responsible for sending the token to the peer application, encapsulated if necessary in an application-application protocol πŸ’Œ. When the peer application receives such a token, it should pass it to a corresponding GSS-API routine, which will decode the token, extract the information, and update the security context state accordingly πŸ’….

c) Per-message services are invoked to apply either:

integrity and data origin authentication, or confidentiality, integrity and data origin authentication, to application data. GSS-API treats that application data as arbitrary octet-strings πŸ“. An application sending a message it wants to protect will call the appropriate GSS-API routine (gss_get_mic or gss_wrap) to apply protection, specifying the right security context, and then send the resulting token to the receiving application πŸš€. The receiver passes the received token (and, for data protected by gss_get_mic, the accompanying message-data) to the matching decoding routine (gss_verify_mic or gss_unwrap) to remove the protection and validate the data βœ….

d) At the end of a communications session (which may stretch across several transport connections), each application calls a GSS-API routine to delete the security context πŸŒ™. Multiple contexts may also be used, either one after another or at the same time, within a single communications association, depending on what the applications choose 🫢.

2. GSS-API Routines 🌈

This section lists the routines that make up the GSS-API and gives a quick description of what each routine is for πŸŽ€. The detailed descriptions are listed in alphabetical order in section 5 πŸ“š.

Here are the credential-management routines πŸͺͺ:

Table 2-1  GSS-API Credential-management Routines

Routine                Section              Function
-------                -------              --------
gss_acquire_cred           5.2  Assume a global identity; Obtain
                                   a GSS-API credential handle for
                                   pre-existing credentials.
gss_add_cred               5.3  Construct credentials
                                   incrementally
gss_inquire_cred           5.21 Obtain information about a
                                   credential
gss_inquire_cred_by_mech   5.22 Obtain per-mechanism information
                                   about a credential.
gss_release_cred           5.27 Discard a credential handle.

Here are the context-level routines 🧚:

Table 2-2  GSS-API Context-Level Routines

Routine                 Section              Function
-------                 -------              --------
gss_init_sec_context       5.19 Initiate a security context with
                                   a peer application
gss_accept_sec_context     5.1  Accept a security context
                                   initiated by a
                                   peer application
gss_delete_sec_context     5.9  Discard a security context
gss_process_context_token  5.25 Process a token on a security
                                   context from a peer application
gss_context_time           5.7  Determine for how long a context
                                   will remain valid
gss_inquire_context        5.20 Obtain information about a
                                   security context
gss_wrap_size_limit        5.34 Determine token-size limit for
                                   gss_wrap on a context
gss_export_sec_context     5.14 Transfer a security context to
                                   another process
gss_import_sec_context     5.17 Import a transferred context

Here are the per-message routines πŸ”:

Table 2-3  GSS-API Per-message Routines

Routine                 Section              Function
-------                 -------              --------
gss_get_mic                5.15 Calculate a cryptographic message
                                   integrity code (MIC) for a
                                   message; integrity service
gss_verify_mic             5.32 Check a MIC against a message;
                                   verify integrity of a received
                                   message
gss_wrap                   5.33 Attach a MIC to a message, and
                                   optionally encrypt the message
                                   content;
                                   confidentiality service
gss_unwrap                 5.31 Verify a message with attached
                                   MIC, and decrypt message content
                                   if necessary.

And the name manipulation routines 🏷️:

Table 2-4  GSS-API Name manipulation Routines

Routine                 Section              Function
-------                 -------              --------
gss_import_name            5.16 Convert a contiguous string name
                                   to internal-form
gss_display_name           5.10 Convert internal-form name to
                                   text
gss_compare_name           5.6  Compare two internal-form names

gss_release_name           5.28 Discard an internal-form name
gss_inquire_names_for_mech 5.24 List the name-types supported by
                                   the specified mechanism
gss_inquire_mechs_for_name 5.23 List mechanisms that support the
                                   specified name-type
gss_canonicalize_name      5.5  Convert an internal name to an MN
gss_export_name            5.13 Convert an MN to export form
gss_duplicate_name         5.12 Create a copy of an internal name

Last but not least, the miscellaneous routines πŸ’…:

Table 2-5  GSS-API Miscellaneous Routines

Routine                Section              Function
-------                -------              --------
gss_add_oid_set_member    5.4  Add an object identifier to
                               a set
gss_display_status        5.11 Convert a GSS-API status code
                               to text
gss_indicate_mechs        5.18 Determine available underlying
                               authentication mechanisms
gss_release_buffer        5.26 Discard a buffer
gss_release_oid_set       5.29 Discard a set of object
                               identifiers
gss_create_empty_oid_set  5.8  Create a set containing no
                               object identifiers
gss_test_oid_set_member   5.30 Determines whether an object
                                    identifier is a member of a set.

Okay so individual GSS-API implementations can augment these routines by providing extra mechanism-specific routines if the required functionality isn't available from the generic forms 🧩. Applications are encouraged to use the generic routines wherever possible, on portability grounds 🫢.

3. Data Types and Calling Conventions πŸŽ€

The following conventions are used by the GSS-API C-language bindings πŸ’–:

3.1. Integer types πŸ”’

GSS-API uses the following integer data type:

OM_uint32 32-bit unsigned integer

Where a guaranteed minimum bit-count matters, this portable data type is used by the GSS-API routine definitions πŸ’…. Individual GSS-API implementations will include appropriate typedef definitions to map this type onto a built-in data type ✨. If the platform supports the X/Open xom.h header file, the OM_uint32 definition contained in it should be used. The GSS-API header file in Appendix A has logic that detects the prior inclusion of xom.h, so it won't try to re-declare OM_uint32 πŸ™ˆ. If the X/Open header file isn't available on the platform, the GSS-API implementation should use the smallest natural unsigned integer type that provides at least 32 bits of precision 🌸.

Okay that's a wrap on the first chunk, bestie! πŸŒˆπŸ’– More parts are coming, so stay tuned πŸ¦‹βœ¨

3.2. String and similar data ✨

Lots of GSS-API routines pass around contiguous octet-strings 🧡, and all of that data moves between the GSS-API and the caller as a gss_buffer_t. It's a pointer to a buffer descriptor with two fields: length, which is the total number of bytes in the datum, and value, which points at the actual datum:

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

When a GSS-API routine hands data back to the app through the gss_buffer_t conventions, the GSS-API routine does the allocating πŸ’…. The app can free that storage by calling gss_release_buffer. The gss_buffer_desc object itself is always the app's job to allocate, and unused gss_buffer_desc objects can be set to GSS_C_EMPTY_BUFFER 🌸.

3.2.1. Opaque data types

Some multi-word data items are opaque πŸ”’. Neither the GSS-API nor the caller cares what's inside them, so their internal structure has no significance to either side. Two examples: the input_token parameter of gss_init_sec_context (opaque to the caller) and the input_message parameter of gss_wrap (opaque to the GSS-API). Opaque data goes between the GSS-API and the app using the gss_buffer_t datatype.

3.2.2. Character strings

Some multi-word data items can be treated as plain ISO Latin-1 character strings πŸ“. For example, the printable strings you pass to gss_import_name through input_name_buffer. A few GSS-API routines also return character strings. All of these travel between the app and the GSS-API implementation using gss_buffer_t, which points to a gss_buffer_desc object.

When a gss_buffer_desc describes a printable string, the length field should only count the printable characters πŸŽ€. A trailing NUL character should NOT be included in the length count, and neither the GSS-API implementation nor the application should assume there's an uncounted trailing NUL hiding in there πŸ‘€.

3.3. Object Identifiers πŸ¦‹

Some GSS-API procedures take parameters of type gss_OID, which is an Object Identifier. It's a type holding ISO-defined, tree-structured values, and the caller uses it to pick an underlying security mechanism and to specify namespaces 🌳. A gss_OID looks like this:

typedef struct gss_OID_desc_struct {
   OM_uint32   length;
   void        *elements;
} gss_OID_desc, *gss_OID;

The elements field points to the first byte of an octet string containing the ASN.1 BER encoding of the value portion of the normal BER TLV encoding of the gss_OID. The length field holds the number of bytes in that value. For example, the gss_OID for {iso(1) identified-organization(3) icd-ecma(12) member-company(2) dec(1011) cryptoAlgorithms(7) DASS(5)} is the DASS X.509 authentication mechanism πŸ”‘. Its length is 7, and its elements field points to seven octets with these octal values: 53,14,2,207,163,7,5.

GSS-API implementations should provide constant gss_OID values so apps can request any supported mechanism. Apps are encouraged to accept the default mechanism anyway, for portability reasons 🫢. gss_OID values should also be provided so apps can specify particular name types (see section 3.10). Apps should treat gss_OID_desc values returned by GSS-API routines as read-only, and in particular the app should not try to deallocate them with free() 🚫. The gss_OID_desc datatype is equivalent to the X/Open OM_object_identifier datatype[XOM].

3.4. Object Identifier Sets 🌸

Some GSS-API procedures take parameters of type gss_OID_set. This type holds one or more object identifiers (section 2.3) πŸ’œ. A gss_OID_set looks like this:

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

The count field is the number of OIDs in the set. The elements field points to an array of gss_OID_desc objects, and each one describes a single OID. gss_OID_set values are used to name the mechanisms GSS-API supports, to request specific mechanisms, and to show which mechanisms a given credential supports ✨.

Every OID set returned to the app by GSS-API is a dynamic object. That covers the gss_OID_set_desc itself, the set's "elements" array, and the "elements" array of each member OID, all of which are dynamically allocated. This storage must be deallocated by the app using the gss_release_oid_set() routine 🧹.

3.5. Credentials πŸ’Ž

A credential handle is a caller-opaque atomic datum that identifies a GSS-API credential data structure πŸͺͺ. It's represented by the caller-opaque type gss_cred_id_t, which should be implemented as a pointer or an arithmetic type. If you go with a pointer implementation, you have to make sure two gss_cred_id_t values can be compared with the == operator.

GSS-API credentials can hold mechanism-specific principal authentication data for multiple mechanisms 🌈. A GSS-API credential is made up of a set of credential-elements, and each element applies to one mechanism. A credential may contain at most one credential-element for each supported mechanism. A credential-element identifies the data one mechanism needs to authenticate one principal. Conceptually, it holds two credential-references that describe the actual mechanism-specific authentication data: one for initiating contexts and one for accepting them. For mechanisms that don't distinguish between acceptor and initiator credentials, both references point to the same underlying mechanism-specific authentication data.

Credentials describe a set of mechanism-specific principals and give their holder the power to act as any of those principals πŸ‘‘. All principal identities asserted by a single GSS-API credential should belong to the same entity, though enforcing that is an implementation-specific matter. The GSS-API doesn't hand the actual credentials to applications. Instead, a credential handle identifies a particular credential, which is held internally by GSS-API. The combination of a GSS-API credential handle and a mechanism identifies the principal whose identity the credential will assert when used with that mechanism 🎭.

The gss_init_sec_context and gss_accept_sec_context routines let you pass GSS_C_NO_CREDENTIAL as their credential handle parameter πŸ™ˆ. That special handle means the app wants to act as a default principal. Each GSS-API implementation is free to decide what that default behavior looks like for its mechanism, but the following default behavior for these routines is recommended for portability:

gss_init_sec_context

  1. If there is only a single principal capable of initiating security contexts for the chosen mechanism that the application is authorized to act on behalf of, then that principal shall be used, otherwise
  2. If the platform maintains a concept of a default network-identity for the chosen mechanism, and if the application is authorized to act on behalf of that identity for the purpose of initiating security contexts, then the principal corresponding to that identity shall be used, otherwise
  3. If the platform maintains a concept of a default local identity, and provides a means to map local identities into network-identities for the chosen mechanism, and if the application is authorized to act on behalf of the network-identity image of the default local identity for the purpose of initiating security contexts using the chosen mechanism, then the principal corresponding to that identity shall be used, otherwise
  4. A user-configurable default identity should be used.

gss_accept_sec_context

  1. If there is only a single authorized principal identity capable of accepting security contexts for the chosen mechanism, then that principal shall be used, otherwise
  2. If the mechanism can determine the identity of the target principal by examining the context-establishment token, and if the accepting application is authorized to act as that principal for the purpose of accepting security contexts using the chosen mechanism, then that principal identity shall be used, otherwise
  3. If the mechanism supports context acceptance by any principal, and if mutual authentication was not requested, any principal that the application is authorized to accept security contexts under using the chosen mechanism may be used, otherwise
  4. A user-configurable default identity shall be used.

The point of all these rules is to let both the initiator and the acceptor establish security contexts using default behavior wherever that's possible 🧚. Apps that request default behavior are likely to be more portable across mechanisms and platforms than apps that use gss_acquire_cred to ask for one specific identity πŸ’–.

3.6. Contexts 🫧

The gss_ctx_id_t datatype holds a caller-opaque atomic value that identifies one end of a GSS-API security context. It should be implemented as a pointer or an arithmetic type. If you pick a pointer type, you should make sure two gss_ctx_id_t values can be compared with the == operator.

A security context keeps state information about each end of a peer communication, including cryptographic state πŸ”.

3.7. Authentication tokens πŸͺ™

A token is a caller-opaque type that GSS-API uses to keep the context data structures at each end of a GSS-API security context in sync πŸ”„. It's a cryptographically protected octet-string, generated by the underlying mechanism at one end of the context for the peer mechanism at the other end to use. Encapsulating the token (if required) and transferring it is the job of the peer applications πŸ“¦. Tokens pass between the GSS-API and the application using the gss_buffer_t conventions.

3.8. Interprocess tokens 🧩

Some GSS-API routines are meant to move data between processes in multi-process programs πŸƒβ€β™€οΈ. These routines use a caller-opaque octet-string that the GSS-API generates in one process for use by the GSS-API in another process. The calling application is responsible for carrying these tokens between processes in whatever OS-specific way it uses.

GSS-API implementors are encouraged to keep sensitive information out of interprocess tokens or to cryptographically protect them. Even so, many implementations won't be able to avoid putting key material or other sensitive data inside them 😭.

So it's the application's responsibility to make sure interprocess tokens are protected in transit, and that they're only transferred to processes that are trustworthy πŸ›‘οΈ. An interprocess token passes between the GSS-API and the application using the gss_buffer_t conventions.

3.9. Status values ✨

okay so every GSS-API routine hands back two separate status values to tell the caller what happened: GSS status codes and Mechanism status codes πŸ’…

3.9.1. GSS status codes πŸŽ€

GSS-API routines return GSS status codes as their OM_uint32 function value. These codes flag errors that have nothing to do with which underlying mechanism is doing the security work 🧚 The errors you can see through a GSS status code come in two flavours: generic API routine errors (the ones defined in the GSS-API spec itself) or calling errors (the ones specific to these language bindings) πŸ¦‹

one GSS status code can carry a single fatal generic API error from the routine and a single calling error. On top of that, extra status info can get tucked into the supplementary info field by setting bits in it ✨

here's how the 32 bits get split up πŸ‘‡

      MSB                                                        LSB
      |------------------------------------------------------------|
      |  Calling Error | Routine Error  |    Supplementary Info    |
      |------------------------------------------------------------|
   Bit 31            24 23            16 15                       0

so if a routine gives back a GSS status code whose upper 16 bits aren't zero, the call failed 😭 if the calling error field is non-zero, the app that made the call was doing it wrong (rude, but fixable) πŸ™ˆ the calling errors are listed in table 5-1. if the routine error field is non-zero, the routine failed for one of the routine-specific reasons in table 5-2. and whether the upper 16 bits say pass or fail, the routine can still drop extra info into the supplementary info field. what each bit means is in table 5-3 πŸ’–

Table 3-1 Calling Errors

Name                   Value in field           Meaning
----                   --------------           -------
GSS_S_CALL_INACCESSIBLE_READ  1       A required input parameter
                                      could not be read
GSS_S_CALL_INACCESSIBLE_WRITE 2       A required output parameter
                                       could not be written.
GSS_S_CALL_BAD_STRUCTURE      3       A parameter was malformed

Table 3-2 Routine Errors

Name                   Value in field           Meaning
----                   --------------           -------
GSS_S_BAD_MECH                1       An unsupported mechanism
                                      was requested
GSS_S_BAD_NAME                2       An invalid name was
                                      supplied
GSS_S_BAD_NAMETYPE            3       A supplied name was of an
                                      unsupported type
GSS_S_BAD_BINDINGS            4       Incorrect channel bindings
                                      were supplied
GSS_S_BAD_STATUS              5       An invalid status code was
                                      supplied
GSS_S_BAD_MIC GSS_S_BAD_SIG   6       A token had an invalid MIC
GSS_S_NO_CRED                 7       No credentials were
                                      supplied, or the
                                      credentials were
                                      unavailable or
                                      inaccessible.
GSS_S_NO_CONTEXT              8       No context has been
                                      established
GSS_S_DEFECTIVE_TOKEN         9       A token was invalid
GSS_S_DEFECTIVE_CREDENTIAL   10       A credential was invalid
GSS_S_CREDENTIALS_EXPIRED    11       The referenced credentials
                                      have expired
GSS_S_CONTEXT_EXPIRED        12       The context has expired
GSS_S_FAILURE                13       Miscellaneous failure (see
                                      text)
GSS_S_BAD_QOP                14       The quality-of-protection
                                      requested could not be
                                      provided
GSS_S_UNAUTHORIZED           15       The operation is forbidden
                                      by local security policy
GSS_S_UNAVAILABLE            16       The operation or option is
                                      unavailable
GSS_S_DUPLICATE_ELEMENT      17       The requested credential
                                      element already exists
GSS_S_NAME_NOT_MN            18       The provided name was not a
                                      mechanism name

Table 3-3 Supplementary Status Bits

Name                   Bit Number           Meaning
----                   ----------           -------
GSS_S_CONTINUE_NEEDED   0 (LSB)   Returned only by
                                  gss_init_sec_context or
                                  gss_accept_sec_context. The
                                  routine must be called again
                                  to complete its function.
                                  See routine documentation for
                                  detailed description
GSS_S_DUPLICATE_TOKEN   1         The token was a duplicate of
                                  an earlier token
GSS_S_OLD_TOKEN         2         The token's validity period
                                  has expired
GSS_S_UNSEQ_TOKEN       3         A later token has already been
                                  processed
GSS_S_GAP_TOKEN         4         An expected per-message token
                                  was not received

the routine docs also mention GSS_S_COMPLETE, which is just zero 🌸 it's the "all good, no API errors, no extra bits" value ✨

here's a fun twist: every GSS_S_xxx symbol is a whole OM_uint32 status code, not just a little bitfield value 🀯 like, GSS_S_BAD_NAMETYPE (that's 3 in the routine error field) actually equals 3<<16. there are three macros, GSS_CALLING_ERROR(), GSS_ROUTINE_ERROR() and GSS_SUPPLEMENTARY_INFO(), and each one takes a GSS status code and strips out everything except its own field. so GSS_ROUTINE_ERROR throws away the calling errors and supplementary info, leaving only the routine errors πŸŽ€ whatever those macros spit out can be compared directly against a GSS_S_xxx symbol of the matching type πŸ’… there's also GSS_ERROR(), which gives a non-zero value if the status code shows a calling or routine error, and zero otherwise. and every macro GSS-API defines evaluates its argument(s) exactly once 🫢

a GSS-API implementation can choose to signal calling errors in a platform-specific way, either instead of or on top of the routine value. but routine errors and supplementary info should come back through major status values only πŸ‘‘

finally, GSS_S_FAILURE is the catch-all for when the underlying mechanism hit an error that doesn't have a specific GSS status code. the mechanism-specific status code is where you'll find the juicy details πŸ”₯

3.9.2. Mechanism-specific status codes πŸ¦‹

GSS-API routines also return a minor_status parameter, which spills the tea on specialized errors coming from the underlying security mechanism πŸ’œ it can hold one mechanism-specific error, represented as an OM_uint32 value ✨

minor_status is always set by a GSS-API routine, even when the routine returns a calling error or one of the generic API errors listed above as fatal. most of the other output parameters might stay unset in those cases though πŸ™ˆ

now, output parameters that are supposed to hand back pointers to storage the routine allocated are a whole different vibe. those must always be set by the routine, even if there's an error 😭 the routine can set the returned parameter to NULL to say "no storage was actually allocated," and any length field tied to those pointers (like the one in a gss_buffer_desc structure) should also be set to zero in those cases 🌈

3.10. Names ✨

okay so a name is basically how we identify a person or entity πŸ¦‹, and GSS-API's whole job here is checking that the name actually belongs to whoever is claiming it. no cap, that's the main character energy πŸ’…

Different authentication mechanisms can use different namespaces for identifying their principals, so GSS-API's naming support is kinda complex in multi-mechanism environments (or even in some single-mechanism environments where the underlying mechanism supports multiple namespaces) πŸ™ˆ

There are two distinct representations for names 🌸:

An internal form. This is the GSS-API "native" format, represented by the implementation-specific gss_name_t type. It's opaque to GSS-API callers πŸ”’. A single gss_name_t object can hold multiple names from different namespaces, but all of those names should refer to the same entity. For example, the name returned from a call to the gss_inquire_cred routine is this kind of name when you apply it to a credential that has credential elements for multiple authentication mechanisms using different namespaces. That gss_name_t object will contain a distinct name for the entity for each authentication mechanism ✨.

For GSS-API implementations that support multiple namespaces, objects of type gss_name_t must contain sufficient information to determine the namespace each primitive name belongs to. It's giving organized πŸ’….

Mechanism-specific contiguous octet-string forms. This is a format that can hold a single name from a single namespace. Contiguous string names are always accompanied by an object identifier specifying the namespace the name belongs to, and their format depends on the authentication mechanism that uses the name. Many (but not all!) contiguous string names are printable, so GSS-API applications can use them to talk to their users πŸ’¬.

Routines (gss_import_name and gss_display_name) convert names between contiguous string representations and the internal gss_name_t type. gss_import_name may support multiple syntaxes for each supported namespace, so users get the freedom to pick their preferred name representation πŸ’–. gss_display_name should use an implementation-chosen printable syntax for each supported name-type.

If an application calls gss_display_name() on the internal name it got from gss_import_name(), there is no guarantee the resulting contiguous string name will be the same as the original imported string name 😭. Name-space identifiers don't necessarily survive unchanged after a trip through the internal name-form either. Picture a mechanism that authenticates X.500 names but does an algorithmic mapping of Internet DNS names into X.500. Its gss_import_name() might, when given a DNS name, generate an internal name that holds both the original DNS name and the equivalent X.500 name. Or it might only store the X.500 name. In that second case, gss_display_name() would most likely generate a printable X.500 name instead of the original DNS name πŸ™ˆ.

The authentication process hands the context acceptor an internal name. Since that name was authenticated by a single mechanism, it contains only a single name (even if the internal name the context initiator presented to gss_init_sec_context had multiple components) πŸŽ€. Names like this are called internal mechanism names, or "MN"s, and the names emitted by gss_accept_sec_context() are always this type. Some applications may need MNs without wanting to take on the overhead of an authentication operation, so there's a second function, gss_canonicalize_name(), that converts a general internal name into an MN 🧚.

Comparing internal-form names can be done with the gss_compare_name() routine, which returns true if the two names being compared refer to the same entity. This means the application doesn't need to understand the syntaxes of all the printable names a given GSS-API implementation might support, which is so relieving 🫢. Since GSS-API assumes all primitive names inside a given internal name refer to the same entity, gss_compare_name() can return true if the two names have at least one primitive name in common. If the implementation knows about equivalence relationships between names from different namespaces, it may also successfully compare internal names that have no overlapping primitive elements πŸ”₯.

When you're working with large access control lists, calling gss_import_name() and gss_compare_name() on every name in the ACL can be prohibitive 😩. So GSS-API defines a special form of the contiguous string name that can be compared directly (e.g. with memcmp()). Contiguous names suitable for comparison are generated by the gss_export_name() routine, which requires an MN as input. Exported names can be re-imported by gss_import_name(), and the resulting internal name will also be an MN. The gss_OID constant GSS_C_NT_EXPORT_NAME identifies the "export name" type, and the value of this constant is given in Appendix A πŸ“œ. Structurally, an exported name object is a header containing an OID that identifies the mechanism that authenticated the name, plus a trailer containing the name itself, where the syntax of the trailer is defined by the individual mechanism specification. The precise format of an export name is defined in the language-independent GSS-API specification [GSSAPI] πŸ’….

⚠️ Heads up, bestie: the results from gss_compare_name() will in general be different from the results you get by calling gss_canonicalize_name() and gss_export_name() and then comparing the exported names. The first series of operations determines whether two (unauthenticated) names identify the same principal. The second determines whether a particular mechanism would authenticate them as the same principal. These two operations will in general only give the same results for MNs πŸ’€.

The gss_name_t datatype should be implemented as a pointer type. To let the compiler help the application programmer by doing type-checking, the use of (void *) is discouraged πŸ™…. A pointer to an implementation-defined type is the preferred choice 🌈.

Storage is allocated by routines that return gss_name_t values. There's a procedure, gss_release_name, that frees the storage associated with an internal-form name 🧹.


3.11. Channel Bindings πŸ¦‹

GSS-API supports the use of user-specified tags to identify a given context to the peer application 🫧. These tags are meant to identify the particular communications channel that carries the context. Channel bindings are communicated to GSS-API using this structure:

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 initiator_addrtype and acceptor_addrtype fields denote the type of addresses contained in the initiator_address and acceptor_address buffers. The address type should be one of the following 🌸:

GSS_C_AF_UNSPEC     Unspecified address type
GSS_C_AF_LOCAL      Host-local address type
GSS_C_AF_INET       Internet address type (e.g. IP)
GSS_C_AF_IMPLINK    ARPAnet IMP address type
GSS_C_AF_PUP        pup protocols (eg BSP) address type
GSS_C_AF_CHAOS      MIT CHAOS protocol address type
GSS_C_AF_NS         XEROX NS address type
GSS_C_AF_NBS        nbs address type
GSS_C_AF_ECMA       ECMA address type
GSS_C_AF_DATAKIT    datakit protocols address type
GSS_C_AF_CCITT      CCITT protocols
GSS_C_AF_SNA        IBM SNA address type
GSS_C_AF_DECnet     DECnet address type
GSS_C_AF_DLI        Direct data link interface address type
GSS_C_AF_LAT        LAT address type
GSS_C_AF_HYLINK     NSC Hyperchannel address type
GSS_C_AF_APPLETALK  AppleTalk address type
GSS_C_AF_BSC        BISYNC 2780/3780 address type
GSS_C_AF_DSS        Distributed system services address type
GSS_C_AF_OSI        OSI TP4 address type
GSS_C_AF_X25        X.25
GSS_C_AF_NULLADDR   No address specified

Note that these symbols name address families rather than specific addressing formats πŸ—ΊοΈ. For address families that have several alternative address forms, the initiator_address and acceptor_address fields must contain sufficient information to determine which address form is used. When nothing else is specified, addresses should be given in network byte-order (that is, native byte-ordering for the address family) πŸ”’.

Conceptually, GSS-API concatenates initiator_addrtype, initiator_address, acceptor_addrtype, acceptor_address and application_data to form an octet string. The mechanism calculates a MIC over this octet string and binds the MIC to the context establishment token emitted by gss_init_sec_context. The context acceptor presents the same bindings to gss_accept_sec_context, and a MIC is calculated in the same way. The calculated MIC is compared with the one found in the token, and if the MICs differ, gss_accept_sec_context returns a GSS_S_BAD_BINDINGS error and the context is not established 🚫. Some mechanisms may include the actual channel binding data in the token (rather than just a MIC), so applications should therefore not use confidential data as channel-binding components 🀫.

Individual mechanisms may impose additional constraints on the addresses and address types that may appear in channel bindings πŸ”. For example, a mechanism may 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 make sure they either provide correct information in the address fields or omit addressing information by specifying GSS_C_AF_NULLADDR as the address-types πŸ’–.


3.12. Optional parameters πŸŽ€

Various parameters are described as optional. This means they follow a convention where a default value can be requested. The following conventions are used for omitted parameters 🧁. These conventions apply only to those parameters that are explicitly documented as optional.

3.12.1. gss_buffer_t types

Specify GSS_C_NO_BUFFER as a value. For an input parameter, this signifies that default behavior is requested. For an output parameter, it indicates that the information that would be returned via the parameter is not required by the application 🫢.

3.12.2. Integer types (input)

Individual parameter documentation lists the values to be used to indicate default actions πŸ“‹.

3.12.3. Integer types (output)

Specify NULL as the value for the pointer πŸ“.

3.12.4. Pointer types

Specify NULL as the value 🌸.

3.12.5. Object IDs

Specify GSS_C_NO_OID as the value πŸ¦‹.

3.12.6. Object ID Sets

Specify GSS_C_NO_OID_SET as the value πŸ’œ.

3.12.7. Channel Bindings πŸ”—

Bestie, this one's short and sweet πŸ“ If you don't want channel bindings, you pass GSS_C_NO_CHANNEL_BINDINGS and GSS-API gets the message that you're opting out πŸ™…β€β™€οΈπŸ’…

4. Additional Controls πŸŽ€

Okay so this section is all about the extra services an initiator can ask GSS-API for while a context is being set up ✨ Each one gets requested by flipping a flag in the req_flags input parameter to gss_init_sec_context πŸ¦‹

These are the OPTIONAL services that are defined so far:

  • Delegation 🀝 - The initiator hands over rights, usually just temporarily, so the acceptor can authenticate as the initiator's agent.
  • Mutual Authentication πŸͺž - Besides the initiator proving who it is to the context acceptor, the context acceptor SHOULD also prove who it is to the initiator.
  • Replay detection πŸ” - Besides keeping messages intact, gss_get_mic and gss_wrap SHOULD add message numbers so gss_verify_mic and gss_unwrap can tell when a message got duplicated.
  • Out-of-sequence detection πŸŒ€ - Besides keeping messages intact, gss_get_mic and gss_wrap SHOULD add sequencing info so gss_verify_mic and gss_unwrap can tell when a message arrived out of order.
  • Anonymous authentication πŸ•ΆοΈ - Setting up the security context SHOULD NOT reveal the initiator's identity to the context acceptor.

Any flag bits that aren't defined yet SHOULD be ignored by GSS-API implementations when an application passes them in, and SHOULD be set to zero when the implementation hands them back to the application 🧸

Not every mechanism supports every OPTIONAL service, and some MAY only offer certain services alongside others. πŸ‘€

Both gss_init_sec_context and gss_accept_sec_context tell the application which services the context actually has once establishment is done, through the ret_flags output parameter πŸ“£ In general, if a mechanism can provide a requested service, it SHOULD do so, even if other services have to be switched on to make that happen. If it can't, it SHOULD carry on without that service, and the application can abort context establishment if it decides the service was mandatory 😀

Some mechanisms MAY say that a service is OPTIONAL and that implementors don't have to build it. This is usually the confidentiality service, often because of legal limits on data encryption, but it MAY apply to any service. Mechanisms like this are REQUIRED to send at least one token from acceptor to initiator during establishment whenever the initiator asks for such a service, so the initiating GSS-API can tell whether the acceptor's GSS-API supports it πŸ’–

4.1. Delegation 🀝

GSS-API lets the initiating application control delegation with a boolean parameter to gss_init_sec_context(), which is the routine that sets up a security context ✨ Some mechanisms can't do delegation at all, and for those, the application's attempts to turn it on just get ignored πŸ™ƒ

The acceptor of a context where the initiator turned on delegation gets a credential handle through the delegated_cred_handle parameter of gss_accept_sec_context. That handle holds the delegated identity and MAY be used to start later GSS-API contexts as the initiator's agent or delegate. So if the initiator is "A" and the delegate is "B", the delegated credential MAY represent either "A" or "B acting for A", depending on the mechanism underneath πŸ¦‹

Real talk, a plain yes/no isn't always enough. A mechanism might also offer controls like how long the delegation lasts, which network addresses it works from, or what tasks the delegate MAY perform πŸ“‹ Those extra controls are outside GSS-API's scope right now. GSS-API implementations that support mechanisms with extra controls SHOULD provide extension routines so those controls can be used, maybe by changing the initiator's GSS-API credential before it's used to set up a context πŸ› οΈ

Here's the important part 🚨 The simple GSS-API delegation control SHOULD always be able to override any mechanism-specific delegation controls. If the application tells gss_init_sec_context() that it doesn't want delegation, the implementation MUST NOT allow delegation to happen. This is the one exception to the rule that a mechanism MAY turn on services nobody asked for. Delegation MAY only happen when the application explicitly requests it πŸ’…

4.2. Mutual authentication πŸͺž

Usually the context acceptor needs the initiator to authenticate itself so it can make an access-control decision before doing anything for the initiator πŸ” Sometimes the initiator also wants the acceptor to prove who it is, and GSS-API lets the initiating application ask for this mutual authentication by setting a flag when it calls gss_init_sec_context πŸ’œ

The initiating application finds out whether the context acceptor actually authenticated itself. Heads up, though: some mechanisms MAY not support mutual authentication, and others MAY always do it whether or not the application asks πŸ™ˆ Also, mutual authentication my be REQUIRED by some mechanisms so they can support replay or out-of-sequence detection. For those mechanisms, asking for either of those services automatically turns on mutual authentication too 🌸

4.3. Replay and out-of-sequence detection πŸ”

Once a security context is up, GSS-API MAY detect messages that were delivered out of order ✨ Either application can protect messages, by calling gss_get_mic or gss_wrap, and the peer can verify them by calling gss_verify_mic or gss_unwrap πŸ“¬

gss_get_mic computes a cryptographic MIC over an application message and returns that MIC in a token. The application SHOULD send both the token and the message to the peer, which hands them to gss_verify_mic πŸ“¦

gss_wrap computes a cryptographic MIC over an application message and puts both the MIC and the message in one token. The Application SHOULD send that token to the peer, which passes it to gss_unwrap to pull out the message and check the MIC 🎁

Either pair of routines MAY be able to detect messages arriving out of sequence or being duplicated. When that happens, the details show up as supplementary status bits in the major status code returned by gss_verify_mic or gss_unwrap πŸ‘€ These are the bits:

  • GSS_S_DUPLICATE_TOKEN - The token is a copy of one that was already received and processed. Only contexts that claim replay detection MAY set this bit πŸ™…β€β™€οΈ
  • GSS_S_OLD_TOKEN - The token is too old to tell whether it's a duplicate. A context that does out-of-sequence detection but not replay detection SHOULD always set this bit whenever GSS_S_UNSEQ_TOKEN is set. A context that does replay detection SHOULD only set it if the token is so old it can't be checked for duplication πŸ•°οΈ
  • GSS_S_UNSEQ_TOKEN - A later token has already been processed πŸƒβ€β™€οΈ
  • GSS_S_GAP_TOKEN - An earlier token hasn't arrived yet πŸ•³οΈ

A mechanism doesn't need to keep a list of every token it has processed to support these codes 🧠 A typical mechanism might just remember the most recent "N" tokens, which is enough to spot duplicates and missing tokens among those "N" messages. If a token older than the most recent "N" shows up, it gets GSS_S_OLD_TOKEN πŸ’–

4.4. Anonymous Authentication πŸ•ΆοΈ

Sometimes an application wants to start authentication to verify a peer without revealing who it is 🫣 Picture an app that gives access to a medical information database with unrestricted access. A client might want to verify the service so it can trust the info it gets back, but not want the service to learn its identity, maybe for privacy reasons about what it's searching for, or just to avoid getting stuck on mailing lists πŸ“§

Normally, the acceptor learns the initiator's identity as part of context establishment. Context initiators MAY ask that their identity not be revealed to the context acceptor. Lots of mechanisms don't support anonymous authentication, and for those, the request just gets ignored πŸ™ƒ An authentication token still gets generated, but the application is always told when a requested service isn't available, and it can choose to abort establishment if anonymity matters more than the other services that need the context 🚦

Along with telling the application the context is anonymous (through the ret_flags outputs of gss_init_sec_context and gss_accept_sec_context), the OPTIONAL src_name output from gss_accept_sec_context and gss_inquire_context will, for these contexts, return a reserved internal-form name that the implementation defines 🎭

When that reserved name goes to gss_display_name, it turns into a printable name that looks nothing like any valid principal name the implementation supports. It's tied to the name-type object identifier GSS_C_NT_ANONYMOUS, whose value us given in Appendix A πŸͺͺ The printable form SHOULD clearly imply anonymity, since it might end up in audit logs, for example πŸ“ A good pick could be the string "<anonymous>", as long as no valid printable names the implementation supports can start with "<" and end with ">" πŸ’…

4.5. Confidentiality πŸ”’

If a context supports the confidentiality service, gss_wrap MAY be used to encrypt application messages 🀫 Encryption is selective, controlled by the conf_req_flag input parameter to gss_wrap ✨

4.6. Inter-process context transfer ✨

okay bestie, so GSS-API V2 has two routines, gss_export_sec_context and gss_import_sec_context πŸ¦‹, that let a security context get passed between processes on a single machine πŸ’…. The most common setup is a client-server design where the server is one process that accepts incoming security contexts, then launches child processes to handle the data on those contexts πŸŽ€. The children need access to the security context data structure that the parent made when it called gss_accept_sec_context, so they can use the per-message protection services and delete the context when the communication session ends 🫢.

Since the context data structure holds sequencing information, sharing one between processes is generally impractical πŸ™ˆ. So GSS-API gives you gss_export_sec_context. The process that currently owns the context calls it to say it has no plans to use the context anymore, and it creates an inter-process token with the info the adopting process needs to import the context successfully 🧚. Once gss_export_sec_context completes successfully, GSS-API makes the original security context inaccessible to the calling process, and any context handles referring to it are no longer valid πŸ’€. The originating process hands the token to the adopting process, which passes it to gss_import_sec_context, and a fresh gss_ctx_id_t gets created that is functionally identical to the original ✨.

The inter-process token may contain sensitive data from the original security context, including cryptographic keys πŸ”. Applications using inter-process tokens to transfer security contexts must take appropriate steps to protect these tokens in transit 🚨.

Implementations are not required to support inter-process transfer of security contexts πŸ€·β€β™€οΈ. You can tell whether the ability is there when the context gets created: gss_init_sec_context or gss_accept_sec_context sets the GSS_C_TRANS_FLAG bit in their ret_flags parameter πŸ’œ.

4.7. The use of incomplete contexts

Some mechanisms may let the per-message services be used before context establishment is finished πŸ‘€. For example, a mechanism may include enough info in its initial context-level token for the context acceptor to decode messages protected with gss_wrap or gss_get_mic right away 🌈. For a mechanism like that, the initiating application need not wait until the subsequent context-level tokens have been sent and received before invoking the per-message protection services ⭐.

A context can provide per-message services before it's fully established if the GSS_C_PROT_READY_FLAG bit is set in the ret_flags parameter from gss_init_sec_context and gss_accept_sec_context πŸŽ€. If you want per-message protection on a partially-established context, check this flag before you try to invoke gss_wrap or gss_get_mic 🫢.

5. GSS-API Routine Descriptions

Besides the explicit major status codes documented here, any routine may return GSS_S_FAILURE πŸ’₯. That means there's an implementation-specific or mechanism-specific error, and the details get reported through the minor_status parameter πŸ”.

5.1. gss_accept_sec_context

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_rec,
  gss_cred_id_t       *delegated_cred_handle)

Purpose:

This is the routine that lets a remotely initiated security context between your application and a remote peer get established πŸ’–. It may return an output_token that should be transferred to the peer application, which then presents it to gss_init_sec_context πŸ“¨. If no token needs to be sent, gss_accept_sec_context says so by setting the length field of the output_token argument to zero 0️⃣. To finish establishing the context, one or more reply tokens may be required from the peer application. If so, gss_accept_sec_context returns the status flag GSS_S_CONTINUE_NEEDED, and you should call it again when the reply token comes back from the peer, passing that token in through the input_token parameters πŸ”.

Portable applications should use the token length and return status to figure out whether a token needs to be sent or waited for 🧠. So a typical portable caller should always invoke gss_accept_sec_context inside a loop, like this:

gss_ctx_id_t context_hdl = GSS_C_NO_CONTEXT;

do {
  receive_token_from_peer(input_token);
  maj_stat = gss_accept_sec_context(&min_stat,
                                    &context_hdl,
                                    cred_hdl,
                                    input_token,
                                    input_bindings,
                                    &client_name,
                                    &mech_type,
                                    output_token,
                                    &ret_flags,
                                    &time_rec,
                                    &deleg_cred);
  if (GSS_ERROR(maj_stat)) {
    report_error(maj_stat, min_stat);
  };
  if (output_token->length != 0) {
    send_token_to_peer(output_token);

    gss_release_buffer(&min_stat, output_token);
  };
  if (GSS_ERROR(maj_stat)) {
    if (context_hdl != GSS_C_NO_CONTEXT)
      gss_delete_sec_context(&min_stat,
                             &context_hdl,
                             GSS_C_NO_BUFFER);
    break;
  };
} while (maj_stat & GSS_S_CONTINUE_NEEDED);

Whenever the routine returns a major status that includes GSS_S_CONTINUE_NEEDED, the context isn't fully established yet, and these rules apply to the output parameters 🚧:

The value returned via the time_rec parameter is undefined 🌫️. Unless the accompanying ret_flags parameter contains the GSS_C_PROT_READY_FLAG bit (which says per-message services may be applied before a successful completion status), the value returned via the mech_type parameter may be undefined until the routine returns GSS_S_COMPLETE 🎯.

The GSS_C_DELEG_FLAG, GSS_C_MUTUAL_FLAG, GSS_C_REPLAY_FLAG, GSS_C_SEQUENCE_FLAG, GSS_C_CONF_FLAG, GSS_C_INTEG_FLAG and GSS_C_ANON_FLAG bits returned via ret_flags should contain the values the implementation expects would be valid if context establishment succeeded 🀞.

The GSS_C_PROT_READY_FLAG and GSS_C_TRANS_FLAG bits within ret_flags should show the actual state at the moment gss_accept_sec_context returns, whether or not the context is fully established πŸ“.

This requires GSS-API implementations to set GSS_C_PROT_READY_FLAG in the final ret_flags returned to a caller (that is, when it comes with a GSS_S_COMPLETE status code). Even so, applications should not rely on this behavior, since the flag wasn't defined in Version 1 of the GSS-API πŸ™…β€β™€οΈ. Instead, applications should be ready to use per-message services after a successful context establishment, based on the GSS_C_INTEG_FLAG and GSS_C_CONF_FLAG values πŸ”.

All other bits within the ret_flags argument should be set to zero 0️⃣. While the routine returns GSS_S_CONTINUE_NEEDED, the values returned via ret_flags tell you which services the implementation expects to be available from the established context πŸŽ€.

If the initial call to gss_accept_sec_context() fails, the implementation should not create a context object, and should leave context_handle set to GSS_C_NO_CONTEXT to show that πŸ™…β€β™€οΈ. If a later call fails, the implementation is permitted to delete the "half-built" security context (in which case it should set context_handle to GSS_C_NO_CONTEXT). But the preferred behavior is to leave the security context, and context_handle, untouched so the application can delete it with gss_delete_sec_context 🧹.

During context establishment, the informational status bits GSS_S_OLD_TOKEN and GSS_S_DUPLICATE_TOKEN indicate fatal errors 🚫. GSS-API mechanisms should always return them along with a routine error of GSS_S_FAILURE πŸ’€. This pairing requirement didn't exist in version 1 of the GSS-API specification, so applications that want to run over version 1 implementations must special-case these codes 🫠.

Parameters:

  • context_handle: gss_ctx_id_t, read/modify context handle for the new context 🧾. Supply GSS_C_NO_CONTEXT for the first call, and use the value returned in subsequent calls. Once gss_accept_sec_context() has returned a value via this parameter, resources have been assigned to the corresponding context, and the application must free them after use with a call to gss_delete_sec_context() πŸ—‘οΈ.
  • acceptor_cred_handle: gss_cred_id_t, read, credential handle claimed by the context acceptor πŸͺͺ. Specify GSS_C_NO_CREDENTIAL to accept the context as a default principal. If GSS_C_NO_CREDENTIAL is specified but no default acceptor principal is defined, GSS_S_NO_CRED will be returned 😬.
  • input_token_buffer: buffer, opaque, read, the token obtained from the remote application πŸ“₯.
  • input_chan_bindings: channel bindings, read, optional πŸ”—. These are application-specified bindings that let the application securely bind channel identification information to the security context. If channel bindings aren't used, specify GSS_C_NO_CHANNEL_BINDINGS πŸ™Œ.
  • src_name: gss_name_t, modify, optional πŸ‘€. This is the authenticated name of the context initiator. After use, deallocate this name by passing it to gss_release_name(). If it isn't required, specify NULL 🫑.
  • mech_type: Object ID, modify, optional 🧬. This is the security mechanism used. The returned OID value is a pointer into static storage and should be treated as read-only by the caller (so it doesn't need to be freed). If it isn't required, specify NULL πŸ“Œ.
  • output_token: buffer, opaque, modify, the token to be passed to the peer application πŸ“€. If the length field of the returned token buffer is 0, no token needs to be passed to the peer application βœ…. If a non-zero length field is returned, the associated storage must be freed after use by the application with a call to gss_release_buffer() 🧽.

Okay so ret_flags is a bit-mask πŸŽ€ (it's modify, and it's optional, so you can pass NULL if you don't care). Basically it's a bunch of little yes/no stickers ✨ telling you what the context can actually do. To check one, you AND your value with its symbolic name, and any bits you don't use should be zero πŸ’….

  • GSS_C_DELEG_FLAG: True means delegated credentials are available through the delegated_cred_handle parameter πŸ”‘. False means no credentials were delegated.
  • GSS_C_MUTUAL_FLAG: True means the remote peer asked for mutual authentication 🀝. False means they didn't.
  • GSS_C_REPLAY_FLAG: True means replayed protected messages will be detected πŸ•΅οΈβ€β™€οΈ. False means replayed messages won't be detected.
  • GSS_C_SEQUENCE_FLAG: True means out-of-sequence protected messages will be detected πŸ”’. False means out-of-sequence messages won't be caught.
  • GSS_C_CONF_FLAG: True means you can invoke confidentiality by calling gss_wrap πŸ”. False means there's no confidentiality through gss_wrap at all. It'll still do message encapsulation, data-origin authentication, and integrity, just no secrecy πŸ™ˆ.
  • GSS_C_INTEG_FLAG: True means integrity can be invoked by calling either gss_get_mic or gss_wrap πŸ›‘οΈ. False means per-message integrity is unavailable.
  • GSS_C_ANON_FLAG: True means the initiator doesn't want to be authenticated πŸ‘», so src_name (if you asked for it) holds an anonymous internal name. False means the initiator was authenticated normally.
  • GSS_C_PROT_READY_FLAG: True means the protection services (set by GSS_C_CONF_FLAG and GSS_C_INTEG_FLAG) are ready as long as the major status is either GSS_S_COMPLETE or GSS_S_CONTINUE_NEEDED 🚦. False means they're only ready when the major status is GSS_S_COMPLETE.
  • GSS_C_TRANS_FLAG: True means the context can be handed off to other processes with gss_export_sec_context() 🧳. False means it's not transferable.

time_rec is an Integer, modify, and optional ⏳. It's the number of seconds the context stays valid. Pass NULL if you don't need it.

delegated_cred_handle is a gss_cred_id_t, modify, and optional πŸ‘‘. It's the credential handle for the credentials the initiator handed over. It only matters if deleg_flag in ret_flags is true. In that case you get an explicit handle (not GSS_C_NO_CREDENTIAL). If deleg_flag is false, gss_accept_context() sets this parameter to GSS_C_NO_CREDENTIAL. If you do get a handle, you have to release its resources yourself after use with gss_release_cred() 🧹. Pass NULL if you don't need it.

minor_status is an Integer, and it's modify πŸ”§. It's a mechanism-specific status code.

Now the major status codes 🚨:

  • GSS_S_CONTINUE_NEEDED: the context isn't finished yet. You need a token from the peer application, and then gss_accept_sec_context has to be called again with that token πŸ”.
  • GSS_S_DEFECTIVE_TOKEN: the consistency checks on the input_token failed πŸ’€.
  • GSS_S_DEFECTIVE_CREDENTIAL: the consistency checks on the credential failed πŸ™…β€β™€οΈ.
  • GSS_S_NO_CRED: the supplied credentials weren't valid for accepting a context, or the credential handle didn't point to any credentials πŸ₯².
  • GSS_S_CREDENTIALS_EXPIRED: the referenced credentials have expired βŒ›.
  • GSS_S_BAD_BINDINGS: the input_token has different channel bindings than the ones you passed in through input_chan_bindings 🚫.
  • GSS_S_NO_CONTEXT: the context handle you gave didn't refer to a valid context πŸ«₯.
  • GSS_S_BAD_SIG: the input_token contains an invalid MIC 😀.
  • GSS_S_OLD_TOKEN: the input_token was too old. This one is a fatal error during context establishment ☠️.
  • GSS_S_DUPLICATE_TOKEN: the input_token is valid, but it's a duplicate of one that was already processed. This one is also fatal during context establishment πŸ‘―.
  • GSS_S_BAD_MECH: the received token asked for a mechanism that the implementation or the provided credential doesn't support 🧩.

5.2. gss_acquire_cred ✨

OM_uint32 gss_acquire_cred (
  OM_uint32         *minor_status,
  const gss_name_t  desired_name,
  OM_uint32         time_req,
  const gss_OID_set desired_mechs,
  gss_cred_usage_t  cred_usage,
  gss_cred_id_t     *output_cred_handle,
  gss_OID_set       *actual_mechs,
  OM_uint32         *time_rec)

Purpose:

okay so this one is how an app grabs a handle to a credential that already exists, looked up by name πŸŽ€ no cap, the access rules are a big deal here πŸ”’. GSS-API implementations MUST impose a local access-control policy on callers so that random people can't snag credentials they aren't entitled to πŸ™…β€β™€οΈ.

This is NOT a "login to the network" button πŸ’€. A login would create new credentials, and this routine only gets you a handle to ones that already exist. If you need a login function, it SHOULD be defined in implementation-specific extensions to the API ✨.

If desired_name is GSS_C_NO_NAME, the call is treated as a request for a credential handle that will invoke default behavior when passed to gss_init_sec_context() (if cred_usage is GSS_C_INITIATE or GSS_C_BOTH) or gss_accept_sec_context() (if cred_usage is GSS_C_ACCEPT or GSS_C_BOTH) 🧚.

Mechanisms SHOULD honor the desired_mechs parameter and return a credential that is suitable to use only with the requested mechanisms πŸ’…. The exception is when one underlying credential element can be shared by multiple mechanisms. In that case it is permissible for an implementation to indicate all mechanisms with which the credential element MAY be used πŸ¦‹. If desired_mechs is an empty set, behavior is undefined πŸ™ˆ.

This routine is expected to be used mainly by context acceptors, since implementations are likely to provide mechanism-specific ways of getting GSS-API initiator credentials from the system login process 🌸. So some implementations MAY not support acquiring GSS_C_INITIATE or GSS_C_BOTH credentials via gss_acquire_cred for any name other than GSS_C_NO_NAME. The same goes for a name produced by applying gss_inquire_cred to a valid credential, or gss_inquire_context to an active context πŸ“.

If credential acquisition is time-consuming for a mechanism, the mechanism MAY choose to delay the actual acquisition until the credential is required (for example by gss_init_sec_context or gss_accept_sec_context) ⏳. Mechanism-specific implementation decisions like this SHOULD be invisible to the calling application. That means a call to gss_inquire_cred right after gss_acquire_cred MUST return valid credential data, and it MAY therefore take on the overhead of a deferred credential acquisition 🫢.

Parameters:

desired_name      gss_name_t, read
                  Name of principal whose credential
                  should be acquired

time_req          Integer, read, optional
                  number of seconds that credentials
                  should remain valid. Specify GSS_C_INDEFINITE
                  to request that the credentials have the maximum
                  permitted lifetime.

desired_mechs     Set of Object IDs, read, optional
                  set of underlying security mechanisms that
                  may be used.  GSS_C_NO_OID_SET may be used
                  to obtain an implementation-specific default.

cred_usage        gss_cred_usage_t, read
                  GSS_C_BOTH - Credentials may be used
                     either to initiate or accept
                     security contexts.
                  GSS_C_INITIATE - Credentials will only be
                     used to initiate security contexts.
                  GSS_C_ACCEPT - Credentials will only be used to
                     accept security contexts.

output_cred_handle  gss_cred_id_t, modify
                    The returned credential handle.  Resources
                    associated with this credential handle must
                    be released by the application after use
                    with a call to gss_release_cred().

actual_mechs      Set of Object IDs, modify, optional
                  The set of mechanisms for which the
                  credential is valid.  Storage associated with the
                  returned OID-set must be released by
                  the application after use with a call to
                  gss_release_oid_set().  Specify NULL if not
                  required.

time_rec          Integer, modify, optional
                  Actual number of seconds for which the
                  returned credentials will remain valid.  If the
                  implementation does not support expiration of
                  credentials, the value GSS_C_INDEFINITE will
                  be returned. Specify NULL if not required

minor_status      Integer, modify
                  Mechanism specific status code.

Function value: GSS status code 🌈

GSS_S_COMPLETE    Successful completion

GSS_S_BAD_MECH    Unavailable mechanism requested

GSS_S_BAD_NAMETYPE Type contained within desired_name parameter
                   is not supported

GSS_S_BAD_NAME    Value supplied for desired_name parameter is ill
                  formed.

GSS_S_CREDENTIALS_EXPIRED The credentials could not be acquired
                          Because they have expired.

GSS_S_NO_CRED     No credentials were found for the specified name.

5.3. gss_add_cred ✨

OM_uint32 gss_add_cred (
  OM_uint32           *minor_status,
  const gss_cred_id_t input_cred_handle,
  const gss_name_t    desired_name,
  const gss_OID       desired_mech,
  gss_cred_usage_t    cred_usage,
  OM_uint32           initiator_time_req,
  OM_uint32           acceptor_time_req,
  gss_cred_id_t       *output_cred_handle,
  gss_OID_set         *actual_mechs,
  OM_uint32           *initiator_time_rec,
  OM_uint32           *acceptor_time_rec)

Purpose

okay bestie, so gss_add_cred adds a credential-element to a credential πŸŽ€ The element is identified by the name of the principal it belongs to. GSS-API implementations must impose a local access-control policy on callers of this routine, so unauthorized callers can't grab credential-elements they aren't entitled to πŸ”’ This is NOT a "login to the network" function, ngl. A login would create brand-new mechanism-specific authentication data, whereas this routine just gets you a GSS-API handle to data that already exists πŸ’… If you need a login-type function, it should be defined in implementation-specific extensions to the API.

If desired_name is GSS_C_NO_NAME, the call is treated as a request for a credential element that invokes default behavior when it's passed to gss_init_sec_context() (if cred_usage is GSS_C_INITIATE or GSS_C_BOTH) or to gss_accept_sec_context() (if cred_usage is GSS_C_ACCEPT or GSS_C_BOTH) ✨

This routine is mostly for context acceptors, since implementations usually have mechanism-specific ways to get initiator credentials from the system login process 🧚 So some implementations may not support acquiring GSS_C_INITIATE or GSS_C_BOTH credentials through gss_acquire_cred for any name other than GSS_C_NO_NAME. The other allowed options are a name produced by applying gss_inquire_cred to a valid credential, or by applying gss_inquire_context to an active context.

If acquiring a credential is slow for a mechanism, the mechanism may delay the real acquisition until the credential is actually needed, for example by gss_init_sec_context or gss_accept_sec_context 🫢 That kind of decision should be invisible to the calling application. So a call to gss_inquire_cred right after gss_add_cred must return valid credential data, even if that means paying the cost of the deferred acquisition.

This routine can either compose a brand-new credential containing all the elements of the original plus the newly acquired one, or just add the new element to an existing credential 🌈 If NULL is given for output_cred_handle, the new element gets added to the credential named by input_cred_handle. If a valid pointer is given, you get a new credential handle instead.

If GSS_C_NO_CREDENTIAL is passed as input_cred_handle, gss_add_cred composes a credential (and sets output_cred_handle to match) using default behavior. In other words, it does the same thing as if you had first called gss_acquire_cred() with the same usage and GSS_C_NO_NAME as desired_name to get an explicit credential handle with default behavior, passed that handle to gss_add_cred(), and then called gss_release_cred() on the first handle πŸ’–

If GSS_C_NO_CREDENTIAL is passed as the input_cred_handle parameter, a non-NULL output_cred_handle must be supplied. No skipping this one, bestie πŸ™ˆ

Parameters

  • minor_status: Integer, modify. Mechanism-specific status code.
  • input_cred_handle: gss_cred_id_t, read, optional. The credential that gets a new credential-element. If GSS_C_NO_CREDENTIAL is given, the routine composes the new credential from default behavior (see above). The credential handle itself isn't modified by gss_add_cred(), but the underlying credential will be modified if output_credential_handle is NULL.
  • desired_name: gss_name_t, read. The name of the principal whose credential should be acquired.
  • desired_mech: Object ID, read. The underlying security mechanism the credential may be used with.
  • cred_usage: gss_cred_usage_t, read. One of:
    • GSS_C_BOTH: the credential may be used to either initiate or accept security contexts.
    • GSS_C_INITIATE: the credential will only be used to initiate security contexts.
    • GSS_C_ACCEPT: the credential will only be used to accept security contexts.
  • initiator_time_req: Integer, read, optional. The number of seconds the credential should remain valid for initiating security contexts. This argument is ignored if the composed credentials are of type GSS_C_ACCEPT. Specify GSS_C_INDEFINITE to request the maximum permitted initiator lifetime.
  • acceptor_time_req: Integer, read, optional. The number of seconds the credential should remain valid for accepting security contexts. This argument is ignored if the composed credentials are of type GSS_C_INITIATE. Specify GSS_C_INDEFINITE to request the maximum permitted initiator lifetime.
  • output_cred_handle: gss_cred_id_t, modify, optional. The returned credential handle, holding the new credential-element and every credential-element from input_cred_handle. If a valid pointer to a gss_cred_id_t is supplied, gss_add_cred creates a new credential handle containing all the elements from input_cred_handle plus the newly acquired element. If NULL is specified, the newly acquired element gets added to the credential identified by input_cred_handle. The application must release any resources tied to a credential handle returned through this parameter, using gss_release_cred() after use.
  • actual_mechs: Set of Object IDs, modify, optional. The complete set of mechanisms the new credential is valid for. The application must free the storage for the returned OID-set after use, using gss_release_oid_set(). Specify NULL if this isn't needed.
  • initiator_time_rec: Integer, modify, optional. The actual number of seconds the returned credentials will remain valid for initiating contexts with the specified mechanism. If the implementation or mechanism doesn't support credential expiration, GSS_C_INDEFINITE is returned. Specify NULL if not required.
  • acceptor_time_rec: Integer, modify, optional. The actual number of seconds the returned credentials will remain valid for accepting security contexts with the specified mechanism. If the implementation or mechanism doesn't support credential expiration, GSS_C_INDEFINITE is returned. Specify NULL if not required.

Function value

GSS status code:

  • GSS_S_COMPLETE: successful completion πŸŽ‰
  • GSS_S_BAD_MECH: the requested mechanism is unavailable 😬
  • GSS_S_BAD_NAMETYPE: the type contained within the desired_name parameter isn't supported
  • GSS_S_BAD_NAME: the value supplied for desired_name is ill-formed
  • GSS_S_DUPLICATE_ELEMENT: the credential already has an element for the requested mechanism with overlapping usage and validity period πŸ’€
  • GSS_S_CREDENTIALS_EXPIRED: the required credentials couldn't be added because they've expired
  • GSS_S_NO_CRED: no credentials were found for the specified name

5.4. gss_add_oid_set_member πŸ¦‹

OM_uint32 gss_add_oid_set_member (
  OM_uint32       *minor_status,
  const gss_OID   member_oid,
  gss_OID_set     *oid_set)

Purpose

so this routine adds an Object Identifier to an Object Identifier set 🌸 It's meant to be used together with gss_create_empty_oid_set when you're building a set of mechanism OIDs to feed into gss_acquire_cred. The oid_set parameter has to refer to an OID-set that GSS-API created (for example, one returned by gss_create_empty_oid_set()). GSS-API makes a copy of member_oid and puts that copy into the set, growing the storage for the set's elements array if it needs to 🧁 The routine may add the new member anywhere within the elements array. Implementations should check that member_oid isn't already in the elements array. If it is already there, the oid_set stays unchanged, no drama πŸ’œ

Parameters

  • minor_status: Integer, modify. Mechanism-specific status code.
  • member_oid: Object ID, read. The object identifier to be copied into the set.
  • oid_set: Set of Object ID, modify. The set the object identifier should be inserted into.

Function value

GSS status code:

  • GSS_S_COMPLETE: successful completion ✨

5.5. gss_canonicalize_name ✨

OM_uint32 gss_canonicalize_name (
  OM_uint32        *minor_status,
  const gss_name_t input_name,
  const gss_OID    mech_type,
  gss_name_t       *output_name)

Purpose: okay bestie, this one makes a canonical mechanism name (MN) out of any internal name πŸ¦‹ The MN is the name a context acceptor would get back after a successful authentication, if the initiator used the input_name in a successful gss_acquire_cred call (with an OID set whose only member is <mech_type>), followed by a gss_init_sec_context call that names <mech_type> as the authentication mechanism. It's giving consistency πŸ’…

Parameters:

  • minor_status (Integer, modify): mechanism-specific status code 🌸
  • input_name (gss_name_t, read): the name you want a canonical form of 🧚
  • mech_type (Object ID, read): the authentication mechanism you want the canonical form for. You have to name it explicitly, because there's no default πŸ™ˆ
  • output_name (gss_name_t, modify): the resulting canonical name. You have to free its storage yourself after you're done with it, using gss_release_name() 🫢

Function value: GSS status code

  • GSS_S_COMPLETE: successful completion ✨
  • GSS_S_BAD_MECH: the mechanism you named isn't supported πŸ’€
  • GSS_S_BAD_NAMETYPE: the internal name has no elements the specified mechanism can process 😭
  • GSS_S_BAD_NAME: the internal name is ill-formed πŸ₯€

5.6. gss_compare_name πŸ’œ

OM_uint32 gss_compare_name (
  OM_uint32        *minor_status,
  const gss_name_t name1,
  const gss_name_t name2,
  int              *name_equal)

Purpose: this lets an app check whether two internal-form names point to the same entity πŸ‘€

Heads up: if either name is for an anonymous principal, the routines should indicate that the two names do not refer to the same identity. Anonymous means anonymous, no cap πŸ™ˆ

Parameters:

  • minor_status (Integer, modify): mechanism-specific status code 🌸
  • name1 (gss_name_t, read): an internal-form name
  • name2 (gss_name_t, read): an internal-form name
  • name_equal (boolean, modify): non-zero means the names refer to the same entity. zero means they refer to different entities (or, strictly speaking, the names aren't known to refer to the same identity) πŸŽ€

Function value: GSS status code

  • GSS_S_COMPLETE: successful completion ✨
  • GSS_S_BAD_NAMETYPE: the two names are of incomparable types πŸ’€
  • GSS_S_BAD_NAME: one or both of name1 or name2 is ill-formed 😭

5.7. gss_context_time ⭐

OM_uint32 gss_context_time (
  OM_uint32          *minor_status,
  const gss_ctx_id_t context_handle,
  OM_uint32          *time_rec)

Purpose: this tells you how many seconds the given context is going to stay valid for ⏰ It's basically a countdown, bestie.

Parameters:

  • minor_status (Integer, modify): implementation-specific status code 🌸
  • context_handle (gss_ctx_id_t, read): identifies the context you want to check πŸ”
  • time_rec (Integer, modify): the number of seconds the context will remain valid. If it has already expired, you get zero back πŸ’”

Function value: GSS status code

  • GSS_S_COMPLETE: successful completion ✨
  • GSS_S_CONTEXT_EXPIRED: the context has already expired πŸ₯Ί
  • GSS_S_NO_CONTEXT: the context_handle parameter didn't identify a valid context πŸ™…β€β™€οΈ

5.8. gss_create_empty_oid_set 🌈

OM_uint32 gss_create_empty_oid_set (
  OM_uint32    *minor_status,
  gss_OID_set  *oid_set)

Purpose: this makes an object-identifier set with nothing in it yet 🫧 You can add members later with gss_add_oid_set_member(). These routines are built for putting together sets of mechanism object identifiers to hand to gss_acquire_cred. Main character setup, honestly πŸ‘‘

Parameters:

  • minor_status (Integer, modify): mechanism-specific status code 🌸
  • oid_set (Set of Object IDs, modify): the empty object identifier set. The routine allocates the gss_OID_set_desc object, and the application has to free it afterwards with gss_release_oid_set() 🧹

Function value: GSS status code

  • GSS_S_COMPLETE: successful completion ✨

5.9. gss_delete_sec_context πŸ¦‹

OM_uint32 gss_delete_sec_context (
  OM_uint32    *minor_status,
  gss_ctx_id_t *context_handle,
  gss_buffer_t output_token)

Purpose: this deletes a security context. gss_delete_sec_context wipes the local data structures for that context, and it may spit out an output_token. If you pass that token to your peer's gss_process_context_token, it'll do the same cleanup on its end 🧼 If the mechanism doesn't need a token, the GSS-API should set the length field of output_token (if you gave one) to zero. After this, no more security services can be obtained through context_handle 🚫

Also, gss_delete_sec_context has to be able to delete "half-built" security contexts, the ones left over from an incomplete run of gss_init_sec_context()/gss_accept_sec_context() calls. Iykyk πŸ™ˆ

The output_token parameter is only kept around for compatibility with version 1 of the GSS-API. It is recommended that both peer applications call gss_delete_sec_context passing GSS_C_NO_BUFFER for output_token, which means "no token needed," and that gss_delete_sec_context just deletes the local context data structures. If an app does pass a valid buffer, mechanisms are encouraged to return a zero-length token. That means no peer action is needed and the app shouldn't transfer any token πŸ’…

Parameters:

  • minor_status (Integer, modify): mechanism-specific status code 🌸
  • context_handle (gss_ctx_id_t, modify): the handle for the context you want to delete. Once it's deleted, the GSS-API sets this handle to GSS_C_NO_CONTEXT 🫑
  • output_token (buffer, opaque, modify, optional): a token to send to the remote application so it also deletes the context. It is recommended that apps specify GSS_C_NO_BUFFER here, which asks for local deletion only. If you do give a buffer, the mechanism may return a token in it. Mechanisms that only do local deletion should set the length field of this token to zero, which tells the app not to send anything to the peer πŸ§ƒ

Function value: GSS status code

  • GSS_S_COMPLETE: successful completion ✨
  • GSS_S_NO_CONTEXT: no valid context was supplied πŸ’”

5.10. gss_display_name πŸŽ€

OM_uint32 gss_display_name (
  OM_uint32        *minor_status,
  const gss_name_t input_name,
  gss_buffer_t     output_name_buffer,
  gss_OID          *output_name_type)

Purpose: this gives an app a readable text version of an opaque internal-form name, just for showing to people πŸ‘€ The syntax of a printable name is up to the GSS-API implementation, so it can look however that implementation wants 🌸

If input_name is for an anonymous principal, the implementation should return GSS_C_NT_ANONYMOUS as the output_name_type, plus a text name in output_name_buffer that's syntactically distinct from every valid printable name the implementation supports. Anon energy, but make it recognizable πŸ™ˆ

If input_name was made by calling gss_import_name with GSS_C_NO_OID as the name-type, implementations that use lazy conversion between name types may return GSS_C_NO_OID through output_name_type. Lazy but make it make sense 😴

Parameters:

  • minor_status (Integer, modify): mechanism-specific status code 🌸
  • input_name (gss_name_t, read): the name you want to display 🧚
  • output_name_buffer (buffer, character-string, modify): the buffer that receives the text name. The app has to free its storage afterwards with gss_release_buffer() 🧹
  • output_name_type (Object ID, modify, optional): the type of the returned name. The returned gss_OID points into static storage, so treat it as read-only and don't try to free it. Pass NULL if you don't need it 🫢

Function value: GSS status code

  • GSS_S_COMPLETE: successful completion ✨
  • GSS_S_BAD_NAME: input_name was ill-formed πŸ’€

5.11. gss_display_status

Purpose:

Okay so this one lets an app turn a GSS-API status code into text it can show a user or write to a log πŸ“βœ¨ Since a single status value can mean more than one thing, you might need to call gss_display_status several times, and each call gives you one text string 🧚 The message_context parameter is basically the bookkeeping πŸ¦‹ It tracks which messages have already been pulled out of a given status_value. You have to set message_context to 0 yourself before the first call, and then gss_display_status will hand back a non-zero value in it if there are more messages left to grab πŸ’…

Also, message_context holds all the state gss_display_status needs to pull out the rest of the messages from status_value. Even when it comes back non-zero, you are not required to call again unless you actually want those extra messages 🌸 Here's a snippet that pulls out every message from a status code and prints them to stderr πŸ’–

OM_uint32 message_context;
OM_uint32 status_code;
OM_uint32 maj_status;
OM_uint32 min_status;
gss_buffer_desc status_string;

       ...

message_context = 0;

do {

  maj_status = gss_display_status (
                  &min_status,
                  status_code,
                  GSS_C_GSS_CODE,
                  GSS_C_NO_OID,
                  &message_context,
                  &status_string)

  fprintf(stderr,
          "%.*s\n",
         (int)status_string.length,

         (char *)status_string.value);

  gss_release_buffer(&min_status, &status_string);

} while (message_context != 0);

Parameters:

  • minor_status (Integer, modify): Mechanism specific status code. πŸ”§
  • status_value (Integer, read): The status value to be converted. πŸŽ€
  • status_type (Integer, read): Pick one of these:
    • GSS_C_GSS_CODE means status_value is a GSS status code.
    • GSS_C_MECH_CODE means status_value is a mechanism status code.
  • mech_type (Object ID, read, optional): The underlying mechanism, which is used to interpret a minor status value. Supply GSS_C_NO_OID to get the system default. 🌈
  • message_context (Integer, read/modify): Should be initialized to zero by the application prior to the first call. On return from gss_display_status(), a non-zero status_value parameter indicates that additional messages may be extracted from the status code via subsequent calls to gss_display_status(), passing the same status_value, status_type, mech_type, and message_context parameters. πŸ¦‹
  • status_string (buffer, character string, modify): A textual interpretation of the status_value. The storage behind this parameter must be freed by the application after use with a call to gss_release_buffer(). 🧹

Function value: GSS status code πŸ’œ

  • GSS_S_COMPLETE: Successful completion ✨
  • GSS_S_BAD_MECH: Indicates that translation in accordance with an unsupported mechanism type was requested 😭
  • GSS_S_BAD_STATUS: The status value was not recognized, or the status type was neither GSS_C_GSS_CODE nor GSS_C_MECH_CODE. πŸ’€

5.12. gss_duplicate_name

OM_uint32 gss_duplicate_name (
  OM_uint32        *minor_status,
  const gss_name_t src_name,
  gss_name_t       *dest_name)

Purpose:

Make an exact duplicate of the existing internal name src_name. πŸ‘―β€β™€οΈ The new dest_name is totally independent of src_name. That means both src_name and dest_name have to be released, and releasing one shall not affect whether the other is still valid 🫢

Parameters:

  • minor_status (Integer, modify): Mechanism specific status code. πŸ”§
  • src_name (gss_name_t, read): The internal name to be duplicated. πŸŽ€
  • dest_name (gss_name_t, modify): The resultant copy of <src_name>. The storage for this name must be freed by the application after use with a call to gss_release_name(). 🧹

Function value: GSS status code πŸ’œ

  • GSS_S_COMPLETE: Successful completion ✨
  • GSS_S_BAD_NAME: The src_name parameter was ill-formed. πŸ™ˆ

5.13. gss_export_name

OM_uint32 gss_export_name (
  OM_uint32        *minor_status,
  const gss_name_t input_name,
  gss_buffer_t     exported_name)

Purpose:

This one makes a canonical, contiguous string version of a mechanism name (MN) πŸ¦‹ It's built so you can compare two of them directly (think memcmp), which is super handy for authorization stuff, like matching entries in an access-control list πŸ” The input_name parameter must specify a valid MN, which means an internal name made by either gss_accept_sec_context or gss_canonicalize_name. No other names qualify 🌸

Parameters:

  • minor_status (Integer, modify): Mechanism specific status code πŸ”§
  • input_name (gss_name_t, read): The MN to be exported πŸŽ€
  • exported_name (gss_buffer_t, octet-string, modify): The canonical contiguous string form of <input_name>. The storage for this string must freed by the application after use with gss_release_buffer(). 🧹

Function value: GSS status code πŸ’œ

  • GSS_S_COMPLETE: Successful completion ✨
  • GSS_S_NAME_NOT_MN: The provided internal name was not a mechanism name. 🚫
  • GSS_S_BAD_NAME: The provided internal name was ill-formed. πŸ™ˆ
  • GSS_S_BAD_NAMETYPE: The internal name was of a type not supported by the GSS-API implementation. 😡

5.14. gss_export_sec_context

OM_uint32 gss_export_sec_context (
  OM_uint32    *minor_status,
  gss_ctx_id_t *context_handle,
  gss_buffer_t interprocess_token)

Purpose:

This is for sharing the work between multiple processes πŸ‘‘ The usual setup: one process is the context-acceptor, meaning it receives incoming connection requests and accepts security contexts over them. Then it passes the established context off to one or more other processes that do the message exchange 🧚 gss_export_sec_context() deactivates the security context for the process that calls it, and it creates an interprocess token. When that token gets passed to gss_import_sec_context in another process, the context wakes back up in that second process ✨ Only one instance of a given context can be active at any one time. If a context exporter tries to access the exported security context after that, it will fail πŸ™…β€β™€οΈ

The implementation may limit which processes are allowed to import the interprocess token. That can come from local security policy or from implementation decisions. For instance, some implementations only let contexts be passed between processes running under the same account, or processes in the same process group 🌈

Heads up, the interprocess token may contain security-sensitive stuff, like cryptographic keys πŸ”‘ Mechanisms are encouraged to either keep that sensitive info out of interprocess tokens or encrypt the token before handing it back to the application. But in a typical object-library GSS-API implementation, that may not be possible. So the application must take care to protect the interprocess token and make sure any process the token gets transferred to is trustworthy πŸ›‘οΈ

If creating the interprocess token works, the implementation shall deallocate all process-wide resources tied to the security context, and set the context_handle to GSS_C_NO_CONTEXT. If an error makes it impossible to finish exporting the security context, the implementation must not return an interprocess token, and should strive to leave the security context referenced by the context_handle parameter untouched. If that's impossible, it is permissible for the implementation to delete the security context, as long as it also sets the context_handle parameter to GSS_C_NO_CONTEXT. 😱

Parameters:

  • minor_status (Integer, modify): Mechanism specific status code πŸ”§
  • context_handle (gss_ctx_id_t, modify): The context handle identifying the context to transfer. πŸŽ€
  • interprocess_token (buffer, opaque, modify): The token to be transferred to the target process. The storage for this token must be freed by the application after use with a call to gss_release_buffer(). 🧹

Function value: GSS status code πŸ’œ

  • GSS_S_COMPLETE: Successful completion ✨
  • GSS_S_CONTEXT_EXPIRED: The context has expired ⏰
  • GSS_S_NO_CONTEXT: The context was invalid 🫠
  • GSS_S_UNAVAILABLE: The operation is not supported. 🚫

5.15. gss_get_mic

OM_uint32 gss_get_mic (
  OM_uint32          *minor_status,
  const gss_ctx_id_t context_handle,
  gss_qop_t             qop_req,
  const gss_buffer_t message_buffer,
  gss_buffer_t       msg_token)

Purpose:

This generates a cryptographic MIC for the message you hand it, then puts that MIC into a token so it can be sent to the peer application πŸ” The qop_req parameter lets you choose between several cryptographic algorithms, as long as the chosen mechanism supports them πŸ¦‹

Some application-level protocols want to use tokens emitted by gss_wrap() to get "secure framing" πŸŽ€ Because of that, implementations must support deriving MICs from zero-length messages πŸ’–

Parameters:

  • minor_status (Integer, modify): Implementation specific status code. πŸ”§
  • context_handle (gss_ctx_id_t, read): Identifies the context on which the message will be sent. πŸŽ€
  • qop_req (gss_qop_t, read, optional): Specifies the requested quality of protection. For portability reasons, callers are encouraged to accept the default quality of protection offered by the chosen mechanism. You can do that by specifying GSS_C_QOP_DEFAULT for this parameter. If an unsupported protection strength is requested, gss_get_mic will return a major_status of GSS_S_BAD_QOP. 🌸
  • message_buffer (buffer, opaque, read): The message to be protected. πŸ’Œ
  • msg_token (buffer, opaque, modify): The buffer to receive the token. The application must free the storage associated with this buffer after use with a call to gss_release_buffer(). 🧹

Function value: GSS status code πŸ’œ

  • GSS_S_COMPLETE: Successful completion ✨
  • GSS_S_CONTEXT_EXPIRED: The context has already expired ⏰
  • GSS_S_NO_CONTEXT: The context_handle parameter did not identify a valid context 🫠
  • GSS_S_BAD_QOP: The specified QOP is not supported by the mechanism. πŸ™…β€β™€οΈ

5.16. gss_import_name ✨

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)

Purpose:

okay so this one takes a plain string name and turns it into the internal form πŸ’– In general, the internal name you get back in output_name will NOT be an MN πŸ™…β€β™€οΈ The one exception is when input_name_type says the string is GSS_C_NT_EXPORT_NAME. Then the internal name comes back as an MN for whichever mechanism exported it πŸ¦‹

Parameters:

  • minor_status (Integer, modify): mechanism-specific status code 🌸
  • input_name_buffer (buffer, octet-string, read): the buffer holding the contiguous string name you want to convert πŸŽ€
  • input_name_type (Object ID, read, optional): the OID that tells the function what kind of printable name it's looking at. You can pass GSS_C_NO_OID to use the mechanism's default printable syntax, or pass an OID the GSS-API implementation recognizes to pick a specific namespace 🧚
  • output_name (gss_name_t, modify): the name in internal form. The application has to free this storage after use by calling gss_release_name() πŸ’…

Function value: GSS status code

  • GSS_S_COMPLETE: successful completion ✨
  • GSS_S_BAD_NAMETYPE: the input_name_type wasn't recognized πŸ’€
  • GSS_S_BAD_NAME: the input_name parameter couldn't be read as a name of the specified type 😭
  • GSS_S_BAD_MECH: the name type was GSS_C_NT_EXPORT_NAME, but the mechanism inside the input name isn't supported πŸ™ˆ

5.17. gss_import_sec_context

OM_uint32 gss_import_sec_context (
  OM_uint32          *minor_status,
  const gss_buffer_t interprocess_token,
  gss_ctx_id_t       *context_handle)

Purpose:

this lets one process import a security context that a different process set up πŸ”₯ A given interprocess token can only be imported ONCE, so no double-dipping πŸ™…β€β™€οΈ See gss_export_sec_context.

Parameters:

  • minor_status (Integer, modify): mechanism-specific status code 🌸
  • interprocess_token (buffer, opaque, modify): the token the exporting process handed over 🎁
  • context_handle (gss_ctx_id_t, modify): the context handle for the newly reactivated context. The application must release the resources tied to this handle after use by calling gss_delete_sec_context() 🧹

Function value: GSS status code

  • GSS_S_COMPLETE: successful completion πŸ’–
  • GSS_S_NO_CONTEXT: the token didn't contain a valid context reference πŸ₯€
  • GSS_S_DEFECTIVE_TOKEN: the token was invalid πŸ’€
  • GSS_S_UNAVAILABLE: the operation is unavailable πŸ˜”
  • GSS_S_UNAUTHORIZED: local policy stops this process from importing the context 🚫

5.18. gss_indicate_mechs

OM_uint32 gss_indicate_mechs (
  OM_uint32   *minor_status,
  gss_OID_set *mech_set)

Purpose:

this is how an application figures out which underlying security mechanisms are actually available on the system πŸ‘‘

Parameters:

  • minor_status (Integer, modify): mechanism-specific status code. 🌸
  • mech_set (set of Object IDs, modify): the set of mechanisms the implementation supports. The OID set that comes back is dynamically allocated, so the caller should release it after use by calling gss_release_oid_set() 🫢

Function value: GSS status code

  • GSS_S_COMPLETE: successful completion ✨

5.19. gss_init_sec_context

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 )

Purpose:

okay this is the big one, bestie πŸ¦‹ It kicks off establishing a security context between your app and a remote peer πŸ’… At the start, input_token should be either GSS_C_NO_BUFFER or a pointer to a gss_buffer_desc whose length field is zero 🌸

The routine might hand back an output_token, which you send over to the peer. The peer then gives it to gss_accept_sec_context. If nothing needs to be sent, gss_init_sec_context says so by setting the length field of output_token to zero πŸŽ€

To finish setting up the context, you might need one or more reply tokens from the peer. When that's the case, gss_init_sec_context returns a status with the supplementary info bit GSS_S_CONTINUE_NEEDED πŸ” You call it again once the reply token shows up, passing that reply in through input_token ✨

Portable apps should check the token length and the return status to figure out whether to send something or wait for something. So a typical portable caller runs gss_init_sec_context inside a loop, like this:

int context_established = 0;
gss_ctx_id_t context_hdl = GSS_C_NO_CONTEXT;
       ...
input_token->length = 0;

while (!context_established) {
  maj_stat = gss_init_sec_context(&min_stat,
                                  cred_hdl,
                                  &context_hdl,
                                  target_name,
                                  desired_mech,
                                  desired_services,
                                  desired_time,
                                  input_bindings,
                                  input_token,
                                  &actual_mech,
                                  output_token,
                                  &actual_services,
                                  &actual_time);
  if (GSS_ERROR(maj_stat)) {
    report_error(maj_stat, min_stat);
  };

  if (output_token->length != 0) {
    send_token_to_peer(output_token);
    gss_release_buffer(&min_stat, output_token)
  };
  if (GSS_ERROR(maj_stat)) {

    if (context_hdl != GSS_C_NO_CONTEXT)
      gss_delete_sec_context(&min_stat,
                             &context_hdl,
                             GSS_C_NO_BUFFER);
    break;
  };
  if (maj_stat & GSS_S_CONTINUE_NEEDED) {
    receive_token_from_peer(input_token);
  } else {
    context_established = 1;
  };
};

Now, if the routine returns a major status that includes GSS_S_CONTINUE_NEEDED, the context isn't fully established yet πŸ₯Ί That means these rules apply to the output parameters:

  • The value in time_rec is undefined, unless ret_flags has the bit GSS_C_PROT_READY_FLAG set. That bit says per-message services can be applied before a successful completion status shows up πŸ” Otherwise, the value in actual_mech_type stays undefined until the routine returns GSS_S_COMPLETE.
  • The GSS_C_DELEG_FLAG, GSS_C_MUTUAL_FLAG, GSS_C_REPLAY_FLAG, GSS_C_SEQUENCE_FLAG, GSS_C_CONF_FLAG, GSS_C_INTEG_FLAG and GSS_C_ANON_FLAG bits in ret_flags should hold whatever values the implementation expects to be valid if context establishment succeeds. Say the app asked for delegation or anonymous auth in req_flags, but the underlying mechanism can't offer it. Then gss_init_sec_context should generate a token that won't provide that service, and use ret_flags to say the service won't be supported πŸ™ƒ The app can then call gss_delete_sec_context to abort if it can't survive without the service, or it can send the token and keep going if the service was just nice-to-have πŸ’–
  • The GSS_C_PROT_READY_FLAG and GSS_C_TRANS_FLAG bits in ret_flags should show the actual state at the moment gss_init_sec_context returns, whether or not the context is fully established ✨
  • Implementations that support per-message protection are encouraged to set GSS_C_PROT_READY_FLAG in the final ret_flags they return to a caller (that is, when it comes with a GSS_S_COMPLETE status code). But apps shouldn't rely on this, because the flag wasn't defined in Version 1 of the GSS-API πŸ™ˆ Instead, apps should figure out which per-message services are available after successful context establishment by checking the GSS_C_INTEG_FLAG and GSS_C_CONF_FLAG values.
  • Every other bit in the ret_flags argument should be set to zero 🧼

If the very first call to gss_init_sec_context() fails, the implementation should not create a context object, and should leave context_handle set to GSS_C_NO_CONTEXT to show that πŸ’€ If a later call fails, the implementation is permitted to delete the "half-built" security context (in which case it should set context_handle to GSS_C_NO_CONTEXT). But the preferred behavior is to leave the context alone so the application can delete it itself, using gss_delete_sec_context 🫢

During context establishment, the informational status bits GSS_S_OLD_TOKEN and GSS_S_DUPLICATE_TOKEN mean fatal errors. GSS-API mechanisms should always return them together with a routine error of GSS_S_FAILURE. This pairing rule didn't exist in version 1 of the GSS-API spec, so apps that want to run on version 1 implementations need to special-case these codes 😭

Parameters:

  • minor_status (Integer, modify): mechanism-specific status code 🌸
  • initiator_cred_handle (gss_cred_id_t, read, optional): the handle for the credentials you're claiming. Pass GSS_C_NO_CREDENTIAL to act as the default initiator principal. If there's no default initiator defined, the function returns GSS_S_NO_CRED πŸŽ€
  • context_handle (gss_ctx_id_t, read/modify): the context handle for the new context. Pass GSS_C_NO_CONTEXT on the first call, then use the value the first call returned for continuation calls. The app must release the resources tied to this handle after use by calling gss_delete_sec_context() 🧹
  • target_name (gss_name_t, read): the name of the target πŸ¦‹

okay so we're still in gss_init_sec_context land, bestie πŸ’… let's keep going with the inputs and outputs πŸŽ€

  • mech_type (OID, read, optional) πŸ¦‹ this is the mechanism you want. pass GSS_C_NO_OID and you get the implementation specific default ✨
  • req_flags (bit-mask, read) πŸ’– this is where you ask for service options. every flag is its own independent thing, and the required flags should be logically-ORed together to build the bit-mask value. here's the lineup:
    • GSS_C_DELEG_FLAG πŸŽ€
      • True: delegate credentials to the remote peer
      • False: don't delegate
    • GSS_C_MUTUAL_FLAG 🀝
      • True: ask the remote peer to authenticate itself
      • False: you only authenticate yourself to the remote peer
    • GSS_C_REPLAY_FLAG πŸ”
      • True: turn on replay detection for messages protected with gss_wrap or gss_get_mic
      • False: don't try to detect replayed messages
    • GSS_C_SEQUENCE_FLAG πŸ”’
      • True: turn on detection of out-of-sequence protected messages
      • False: don't try to detect out-of-sequence messages
    • GSS_C_CONF_FLAG πŸ”’
      • True: ask for the confidentiality service to be available (via gss_wrap)
      • False: no per-message confidentiality service is required
    • GSS_C_INTEG_FLAG 🧷
      • True: ask for the integrity service to be available (via gss_wrap or gss_get_mic)
      • False: no per-message integrity service is required
    • GSS_C_ANON_FLAG πŸ™ˆ
      • True: don't reveal the initiator's identity to the acceptor
      • False: authenticate normally
  • time_req (Integer, read, optional) ⏰ how many seconds you'd like the context to stay valid. pass 0 and you get a default validity period.
  • input_chan_bindings (channel bindings, read, optional) 🧚 this is where the app securely binds channel identification info to the security context. if you're not using channel bindings, specify GSS_C_NO_CHANNEL_BINDINGS.
  • input_token (buffer, opaque, read, optional (see text)) πŸ’Œ the token that came in from your peer app. on the initial call, supply GSS_C_NO_BUFFER, or a pointer to a buffer holding GSS_C_EMPTY_BUFFER.
  • actual_mech_type (OID, modify, optional) 🫢 the mechanism that actually got used. the OID comes back as a pointer to static storage that's read-only, so the app should not try to free it. specify NULL if you don't need it.
  • output_token (buffer, opaque, modify) 🌸 the token to send to the peer app. if the length field of the returned buffer is zero, no token needs to go to the peer. the app has to free the storage for this buffer after use, by calling gss_release_buffer().
  • ret_flags (bit-mask, modify, optional) πŸ‘‘ this tells you which service options the context actually supports. specify NULL if you don't need it. to check whether an option is supported, logically-AND the matching symbolic names with the ret_flags value. here are the flags:
    • GSS_C_DELEG_FLAG πŸŽ€
      • True: credentials were delegated to the remote peer
      • False: no credentials were delegated
    • GSS_C_MUTUAL_FLAG 🀝
      • True: the remote peer has authenticated itself
      • False: the remote peer has not authenticated itself
    • GSS_C_REPLAY_FLAG πŸ”
      • True: replay of protected messages will be detected
      • False: replayed messages will not be detected
    • GSS_C_SEQUENCE_FLAG πŸ”’
      • True: out-of-sequence protected messages will be detected
      • False: out-of-sequence messages will not be detected
    • GSS_C_CONF_FLAG πŸ”’
      • True: the confidentiality service may be invoked by calling gss_wrap
      • False: no confidentiality service (via gss_wrap) is available. gss_wrap still gives you message encapsulation, data-origin authentication and integrity services only
    • GSS_C_INTEG_FLAG 🧷
      • True: the integrity service may be invoked by calling either gss_get_mic or gss_wrap
      • False: per-message integrity service is unavailable
    • GSS_C_ANON_FLAG πŸ™ˆ
      • True: the initiator's identity has not been revealed, and won't be revealed if any emitted token is passed to the acceptor
      • False: the initiator's identity has been, or will be, authenticated normally
    • GSS_C_PROT_READY_FLAG 🚦
      • True: the protection services (as set by GSS_C_CONF_FLAG and GSS_C_INTEG_FLAG) are ready to use if the accompanying major status return value is either GSS_S_COMPLETE or GSS_S_CONTINUE_NEEDED
      • False: the protection services (as set by GSS_C_CONF_FLAG and GSS_C_INTEG_FLAG) are only available if the accompanying major status return value is GSS_S_COMPLETE
    • GSS_C_TRANS_FLAG 🧳
      • True: the resulting security context may be transferred to other processes via a call to gss_export_sec_context()
      • False: the security context is not transferable

all other bits should be set to zero 🧹

  • time_rec (Integer, modify, optional) βŒ› the number of seconds the context will stay valid. if the implementation doesn't support context expiration, you get GSS_C_INDEFINITE back. specify NULL if you don't need it.

Function value: GSS status code πŸ“£

  • GSS_S_COMPLETE βœ… successful completion
  • GSS_S_CONTINUE_NEEDED πŸ” a token from the peer app is needed to finish the context, and gss_init_sec_context has to be called again with that token
  • GSS_S_DEFECTIVE_TOKEN πŸ’€ the consistency checks on input_token failed
  • GSS_S_DEFECTIVE_CREDENTIAL πŸ’€ the consistency checks on the credential failed
  • GSS_S_NO_CRED 🚫 the supplied credentials weren't valid for context initiation, or the credential handle didn't reference any credentials
  • GSS_S_CREDENTIALS_EXPIRED βŒ› the referenced credentials have expired
  • GSS_S_BAD_BINDINGS πŸ™… the input_token has different channel bindings from the ones given in input_chan_bindings
  • GSS_S_BAD_SIG πŸ₯€ the input_token has an invalid MIC, or a MIC that couldn't be verified
  • GSS_S_OLD_TOKEN πŸ•°οΈ the input_token was too old. this is a fatal error during context establishment
  • GSS_S_DUPLICATE_TOKEN πŸ‘― the input_token is valid, but it's a duplicate of a token that was already processed. this is a fatal error during context establishment
  • GSS_S_NO_CONTEXT πŸ«₯ the supplied context handle didn't refer to a valid context
  • GSS_S_BAD_NAMETYPE 🏷️ the target_name parameter contained an invalid or unsupported type of name
  • GSS_S_BAD_NAME 🧩 the target_name parameter was ill-formed
  • GSS_S_BAD_MECH πŸ™ƒ the specified mechanism isn't supported by the provided credential, or isn't recognized by the implementation

5.20. gss_inquire_context πŸ’…βœ¨

OM_uint32 gss_inquire_context (
  OM_uint32          *minor_status,
  const gss_ctx_id_t context_handle,
  gss_name_t         *src_name,
  gss_name_t         *targ_name,
  OM_uint32          *lifetime_rec,
  gss_OID            *mech_type,
  OM_uint32          *ctx_flags,
  int                *locally_initiated,
  int                *open )

Purpose:

okay so this call is how you ask a security context what it's been up to πŸ•΅οΈβ€β™€οΈ You need a handle that points at the context first, but the context doesn't have to be fully set up yet. Lowkey iconic that it works mid-handshake too 🌸

Parameters:

  • minor_status (Integer, modify): the mechanism-specific status code πŸ”§
  • context_handle (gss_ctx_id_t, read): the handle that points at the security context πŸŽ€
  • src_name (gss_name_t, modify, optional): the name of the context initiator 🧚
    • If the context was set up with anonymous authentication, and the app calling gss_inquire_context is the context acceptor, you get back an anonymous name. πŸ™ˆ
    • You have to free the storage for this name yourself after you're done, using gss_release_name(). 🧹
    • Pass NULL if you don't want it.
  • targ_name (gss_name_t, modify, optional): the name of the context acceptor πŸ‘‘
    • You free this one yourself too, with gss_release_name(). 🧹
    • If the acceptor didn't authenticate itself, and the initiator didn't give a target name in its gss_init_sec_context() call, you get GSS_C_NO_NAME back. πŸ’€
    • Pass NULL if you don't need it.
  • lifetime_rec (Integer, modify, optional): how many seconds the context stays valid ⏳
    • If the context has already expired, this comes back as zero. 😭
    • If the implementation doesn't do context expiration at all, you get GSS_C_INDEFINITE. ♾️
    • Pass NULL if not required.
  • mech_type (gss_OID, modify, optional): the security mechanism that's providing the context πŸ¦‹
    • The OID you get back points at static storage. Treat it as read-only. Do NOT try to free it, bestie πŸ™…β€β™€οΈ
    • Pass NULL if not required.
  • ctx_flags (bit-mask, modify, optional): a bunch of independent flags, each one saying the context supports (or, if ctx_open is false, is expected to support) a specific service option πŸŽ€
    • If you don't need them, pass NULL.
    • Each flag has a symbolic name. AND the flags you care about with the ret_flags value to check whether that option is supported.
    • The flags are:
      • GSS_C_DELEG_FLAG 🎁
        • True: credentials were delegated from the initiator to the acceptor.
        • False: no credentials were delegated.
      • GSS_C_MUTUAL_FLAG 🀝
        • True: the acceptor was authenticated to the initiator.
        • False: the acceptor didn't authenticate itself.
      • GSS_C_REPLAY_FLAG πŸ”
        • True: replays of protected messages will be detected.
        • False: replayed messages won't be detected.
      • GSS_C_SEQUENCE_FLAG πŸ”’
        • True: out-of-sequence protected messages will be detected.
        • False: out-of-sequence messages won't be detected.
      • GSS_C_CONF_FLAG πŸ”’
        • True: the confidentiality service can be invoked by calling the gss_wrap routine.
        • False: there's no confidentiality service through gss_wrap. gss_wrap only gives you message encapsulation, data-origin authentication and integrity. 🫢
      • GSS_C_INTEG_FLAG 🧱
        • True: the integrity service can be invoked by calling either gss_get_mic or gss_wrap.
        • False: per-message integrity isn't available.
      • GSS_C_ANON_FLAG πŸ•ΆοΈ
        • True: the initiator's identity won't be revealed to the acceptor. If you asked for src_name, it holds an anonymous internal name.
        • False: the initiator was authenticated normally.
      • GSS_C_PROT_READY_FLAG 🚦
        • True: the protection services (as set by GSS_C_CONF_FLAG and GSS_C_INTEG_FLAG) are ready to use.
        • False: those protection services are only available once the context is fully established (that is, when the open parameter is non-zero).
      • GSS_C_TRANS_FLAG 🚚
        • True: the resulting security context can be transferred to other processes with a call to gss_export_sec_context().
        • False: the security context can't be transferred.
  • locally_initiated (Boolean, modify): non-zero if the app calling this is the context initiator πŸ™‹β€β™€οΈ Pass NULL if not required.
  • open (Boolean, modify): non-zero if the context is fully established. Zero if the app is still waiting on a context-establishment token from its peer 🫧 Pass NULL if not required.

Function value: GSS status code

  • GSS_S_COMPLETE: successful completion βœ…
  • GSS_S_NO_CONTEXT: the referenced context couldn't be accessed 😡

5.21. gss_inquire_cred πŸ”πŸ’–

OM_uint32 gss_inquire_cred (
  OM_uint32           *minor_status,
  const gss_cred_id_t cred_handle,
  gss_name_t          *name,
  OM_uint32           *lifetime,
  gss_cred_usage_t    *cred_usage,
  gss_OID_set         *mechanisms )

Purpose:

this one lets you peek at a credential and see what it's all about πŸ‘€

Parameters:

  • minor_status (Integer, modify): the mechanism-specific status code πŸ”§
  • cred_handle (gss_cred_id_t, read): the handle that points at the credential you want to check 🎟️
    • Pass GSS_C_NO_CREDENTIAL if you want to ask about the default initiator principal.
  • name (gss_name_t, modify, optional): the name whose identity the credential is asserting πŸͺͺ
    • When you're finished, free the storage with a call to gss_release_name(). 🧹
    • Pass NULL if not required.
  • lifetime (Integer, modify, optional): how many seconds the credential stays valid ⏳
    • If it has already expired, this comes back as zero.
    • If the implementation doesn't do credential expiration, you get GSS_C_INDEFINITE. ♾️
    • Pass NULL if not required.
  • cred_usage (gss_cred_usage_t, modify, optional): how the credential can be used. It's one of these:
    • GSS_C_INITIATE
    • GSS_C_ACCEPT
    • GSS_C_BOTH
    • Pass NULL if not required.
  • mechanisms (gss_OID_set, modify, optional): the set of mechanisms the credential supports 🧩
    • Free the storage for this OID set with a call to gss_release_oid_set() once you're done. 🧹
    • Pass NULL if not required.

Function value: GSS status code

  • GSS_S_COMPLETE: successful completion βœ…
  • GSS_S_NO_CRED: the referenced credentials couldn't be accessed πŸ™ƒ
  • GSS_S_DEFECTIVE_CREDENTIAL: the referenced credentials were invalid πŸ’€
  • GSS_S_CREDENTIALS_EXPIRED: the referenced credentials have expired 😭
    • If you didn't pass lifetime as NULL, it gets set to 0.

5.22. gss_inquire_cred_by_mech πŸ¦‹

Signature πŸ’…:

OM_uint32 gss_inquire_cred_by_mech (
  OM_uint32           *minor_status,
  const gss_cred_id_t cred_handle,
  const gss_OID       mech_type,
  gss_name_t          *name,
  OM_uint32           *initiator_lifetime,
  OM_uint32           *acceptor_lifetime,
  gss_cred_usage_t    *cred_usage )

Purpose πŸŽ€: okay so this one lets you ask a credential for per-mechanism details. Think of it as checking one specific mechanism's receipts for your credential ✨

Parameters 🧚:

  • minor_status (Integer, modify): mechanism-specific status code.
  • cred_handle (gss_cred_id_t, read): a handle that refers to the target credential. Pass GSS_C_NO_CREDENTIAL if you want to ask about the default initiator principal 🫢
  • mech_type (gss_OID, read): the mechanism you want info about.
  • name (gss_name_t, modify, optional): the name whose identity the credential asserts. The app has to free this storage after use by calling gss_release_name() 🧹. Pass NULL if you don't need it.
  • initiator_lifetime (Integer, modify, optional): how many seconds the credential can still start security contexts under the given mechanism. If it can't be used to initiate anymore, or if the usage for this mechanism is GSS_C_ACCEPT, this comes back as zero πŸ’€. If the implementation doesn't support expiring initiator credentials, you get GSS_C_INDEFINITE back. Pass NULL if you don't need it.
  • acceptor_lifetime (Integer, modify, optional): same energy, but for accepting security contexts. If it can't accept anymore, or the usage for this mechanism is GSS_C_INITIATE, it's zero. If the implementation doesn't support expiring acceptor credentials, the value is GSS_C_INDEFINITE. Pass NULL if you don't need it.
  • cred_usage (gss_cred_usage_t, modify, optional): how the credential may be used with the specified mechanism. It's one of GSS_C_INITIATE, GSS_C_ACCEPT, or GSS_C_BOTH. Pass NULL if you don't need it.

Function value πŸ‘‘: the GSS status code you get back:

  • GSS_S_COMPLETE: successful completion 🌈
  • GSS_S_NO_CRED: the referenced credentials couldn't be accessed 😭
  • GSS_S_DEFECTIVE_CREDENTIAL: the referenced credentials were invalid πŸ™ˆ
  • GSS_S_CREDENTIALS_EXPIRED: the referenced credentials have expired. If you didn't pass NULL for the lifetime parameter, it gets set to 0 ⭐

5.23. gss_inquire_mechs_for_name 🌸

Signature πŸ’–:

OM_uint32 gss_inquire_mechs_for_name (
  OM_uint32        *minor_status,
  const gss_name_t input_name,
  gss_OID_set      *mech_types )

Purpose πŸ“: this gives you the list of mechanisms your GSS-API implementation supports that might be able to handle a given name. Every mechanism on the list will recognize at least one element in the name, ngl that's a solid guarantee πŸ”₯

It's totally fine for this routine to live in a mechanism-independent GSS-API layer. It can use the type info already in the name, plus registration info that each mechanism provides 🧠. Here's the tea though: a mechanism might be listed and still refuse the name in gss_canonicalize_name, gss_init_sec_context, gss_acquire_cred or gss_add_cred. Why? Because of some property of that specific name, not just its name type 😀. So treat this routine as a pre-filter before you call the mechanism-specific routine, not as a guarantee πŸ’…

Parameters πŸŽ€:

  • minor_status (Integer, modify): implementation-specific status code.
  • input_name (gss_name_t, read): the name the inquiry is about.
  • mech_types (gss_OID_set, modify): the set of mechanisms that may support the specified name. The caller must free the returned OID set after use by calling gss_release_oid_set() 🧹

Function value 🫧:

  • GSS_S_COMPLETE: successful completion ✨
  • GSS_S_BAD_NAME: the input_name parameter was ill-formed πŸ’€
  • GSS_S_BAD_NAMETYPE: the input_name parameter contained an invalid or unsupported type of name πŸ™…β€β™€οΈ

5.24. gss_inquire_names_for_mech 🧚

Signature πŸ¦‹:

OM_uint32 gss_inquire_names_for_mech (
  OM_uint32     *minor_status,
  const gss_OID mechanism,
  gss_OID_set   *name_types)

Purpose πŸ’œ: this one returns the set of name types a specific mechanism supports. Basically, "what kinds of names do you even take?" πŸ€·β€β™€οΈ

Parameters 🌈:

  • minor_status (Integer, modify): implementation-specific status code.
  • mechanism (gss_OID, read): the mechanism to be interrogated.
  • name_types (gss_OID_set, modify): the set of name types the specified mechanism supports. The application must free the returned OID set after use by calling gss_release_oid_set() 🧹

Function value πŸ“:

  • GSS_S_COMPLETE: successful completion πŸ’–

5.25. gss_process_context_token 🫢

Signature πŸ”₯:

OM_uint32 gss_process_context_token (
  OM_uint32          *minor_status,
  const gss_ctx_id_t context_handle,
  const gss_buffer_t token_buffer)

Purpose ✨: this is how you hand an asynchronous token to the security service. Most context-level tokens get sent and handled synchronously by gss_init_sec_context and gss_accept_sec_context, and the app finds out whether more tokens are expected from the GSS_C_CONTINUE_NEEDED major status bit πŸ”

But sometimes a mechanism has to emit a context-level token when the peer isn't expecting one πŸ‘€. Example: the initiator's final call to gss_init_sec_context might emit a token and return GSS_S_COMPLETE, but then the acceptor's call to gss_accept_sec_context fails πŸ’”. The acceptor's mechanism might want to send an error token back to the initiator, but the initiator thinks the context is fully established, so it's not waiting for anything 😭. That's exactly what Gss_process_context_token is for: it lets you pass that kind of token to the mechanism at any time πŸ¦‹

Parameters 🌸:

  • minor_status (Integer, modify): implementation-specific status code.
  • context_handle (gss_ctx_id_t, read): the context handle of the context on which the token is to be processed.
  • token_buffer (buffer, opaque, read): the token to process.

Function value πŸ’…:

  • GSS_S_COMPLETE: successful completion 🌈
  • GSS_S_DEFECTIVE_TOKEN: the consistency checks performed on the token failed πŸ™ˆ
  • GSS_S_NO_CONTEXT: the context_handle didn't refer to a valid context πŸ’€

5.26. gss_release_buffer 🧹

Signature 🌷:

OM_uint32 gss_release_buffer (
  OM_uint32    *minor_status,
  gss_buffer_t buffer)

Purpose 🫧: this frees the storage tied to a buffer. That storage has to have been allocated by a GSS-API routine ✨. Along with freeing it, the routine zeroes the length field in the descriptor the buffer parameter points to. Implementations are encouraged to also set the pointer field in that descriptor to NULL πŸ’–. Any buffer object returned by a GSS-API routine can be passed to gss_release_buffer, even if there's no storage attached to it πŸ™Œ

Parameters πŸŽ€:

  • minor_status (Integer, modify): mechanism-specific status code.
  • buffer (buffer, modify): the storage associated with the buffer gets deleted. The gss_buffer_desc object itself won't be freed, but its length field gets zeroed 🧼

Function value πŸ“:

  • GSS_S_COMPLETE: successful completion ⭐

5.27. gss_release_cred 🧚

Signature πŸ’œ:

OM_uint32 gss_release_cred (
  OM_uint32     *minor_status,
  gss_cred_id_t *cred_handle)

Purpose 🌈: this tells GSS-API the app doesn't need this credential handle anymore, and frees the resources tied to it 🧹. Implementations are encouraged to set cred_handle to GSS_C_NO_CREDENTIAL when the call completes successfully πŸ’…

Parameters πŸ¦‹:

  • cred_handle (gss_cred_id_t, modify, optional): the opaque handle identifying the credential to be released. If GSS_C_NO_CREDENTIAL is supplied, the routine still completes successfully but does nothing 🫢
  • minor_status (Integer, modify): mechanism-specific status code.

Function value ✨:

  • GSS_S_COMPLETE: successful completion πŸ”₯
  • GSS_S_NO_CRED: credentials couldn't be accessed 😭

5.28. gss_release_name 🌸

Signature πŸ”₯:

OM_uint32 gss_release_name (
  OM_uint32  *minor_status,
  gss_name_t *name)

Purpose πŸ’–: this frees GSSAPI-allocated storage tied to an internal-form name. Implementations are encouraged to set the name to GSS_C_NO_NAME once the call completes successfully πŸ¦‹

Parameters πŸ“:

  • minor_status (Integer, modify): mechanism-specific status code.
  • name (gss_name_t, modify): the name to be deleted πŸ—‘οΈ

Function value πŸ‘‘:

  • GSS_S_COMPLETE: successful completion 🌈
  • GSS_S_BAD_NAME: the name parameter didn't contain a valid name πŸ’€

5.29. gss_release_oid_set 🫧

Signature πŸŽ€:

OM_uint32 gss_release_oid_set (
  OM_uint32   *minor_status,
  gss_OID_set *set)

Purpose 🧠: this frees storage tied to a GSSAPI-generated gss_OID_set object. The set parameter has to refer to an OID set that was returned from a GSS-API routine πŸ“¦. gss_release_oid_set() frees the storage for each individual member OID, the OID set's elements array, and the gss_OID_set_desc itself 🧹

Implementations are encouraged to set the gss_OID_set parameter to GSS_C_NO_OID_SET when this routine completes successfully πŸ’œ

Parameters 🌷:

  • minor_status (Integer, modify): mechanism-specific status code.
  • set (Set of Object IDs, modify): the storage associated with the gss_OID_set gets deleted πŸ—‘οΈ

Function value 🌈:

  • GSS_S_COMPLETE: successful completion ✨

5.30. gss_test_oid_set_member 🧚

Purpose: okay so this one is literally the bouncer at the OID club πŸ’… You hand it an Object Identifier and a set, and it tells you whether that OID is on the list. It's meant for sets that come back from gss_indicate_mechs(), gss_acquire_cred(), and gss_inquire_cred(), but it works just fine on sets you build yourself 🫢

OM_uint32 gss_test_oid_set_member (
  OM_uint32         *minor_status,
  const gss_OID     member,
  const gss_OID_set set,
  int               *present)
minor_status      Integer, modify
                  Mechanism specific status code

member            Object ID, read
                  The object identifier whose presence
                  is to be tested.

set               Set of Object ID, read
                  The Object Identifier set.

present           Boolean, modify
                  non-zero if the specified OID is a member
                  of the set, zero if not.

Function value:   GSS status code

GSS_S_COMPLETE    Successful completion

5.31. gss_unwrap πŸ”“

Purpose: this is the glow-up reversal of gss_wrap πŸ¦‹ It takes a message that was previously protected and turns it back into something you can actually use, while checking the embedded MIC along the way. The conf_state output tells you whether encryption was involved, and the qop_state output tells you how strong the protection was for the confidentiality and integrity services πŸ”

Some application protocols like to use the tokens gss_wrap() spits out as "secure framing" 🧚 So implementations must support wrapping and unwrapping zero-length messages, no exceptions πŸ’œ

OM_uint32 gss_unwrap (
  OM_uint32          *minor_status,
  const gss_ctx_id_t context_handle,
  const gss_buffer_t input_message_buffer,
  gss_buffer_t       output_message_buffer,
  int                *conf_state,
  gss_qop_t          *qop_state)
minor_status      Integer, modify
                  Mechanism specific status code.

context_handle    gss_ctx_id_t, read
                  Identifies the context on which the message
                  arrived

input_message_buffer  buffer, opaque, read
                  protected message

output_message_buffer  buffer, opaque, modify
                  Buffer to receive unwrapped message.
                  Storage associated with this buffer must
                  be freed by the application after use use
                  with a call to gss_release_buffer().

conf_state        boolean, modify, optional
                  Non-zero - Confidentiality and integrity
                             protection were used
                  Zero - Integrity service only was used
                  Specify NULL if not required

qop_state         gss_qop_t, modify, optional
                  Quality of protection provided.
                  Specify NULL if not required

Function value:   GSS status code

GSS_S_COMPLETE    Successful completion

GSS_S_DEFECTIVE_TOKEN The token failed consistency checks

GSS_S_BAD_SIG     The MIC was incorrect

GSS_S_DUPLICATE_TOKEN The token was valid, and contained a correct
                      MIC for the message, but it had already been
                      processed

GSS_S_OLD_TOKEN   The token was valid, and contained a correct MIC
                  for the message, but it is too old to check for
                  duplication.

GSS_S_UNSEQ_TOKEN The token was valid, and contained a correct MIC
                  for the message, but has been verified out of
                  sequence; a later token has already been
                  received.

GSS_S_GAP_TOKEN   The token was valid, and contained a correct MIC
                  for the message, but has been verified out of
                  sequence; an earlier expected token has not yet
                  been received.

GSS_S_CONTEXT_EXPIRED The context has already expired

GSS_S_NO_CONTEXT  The context_handle parameter did not identify
                  a valid context

5.32. gss_verify_mic πŸ•΅οΈβ€β™€οΈ

Purpose: ngl this one is giving detective energy πŸ•΅οΈβ€β™€οΈ It checks that a cryptographic MIC, sitting in the token parameter, actually matches the message you were handed. The qop_state output lets the recipient see how strong the protection on that message was 🌸

Same deal as gss_unwrap: some application protocols use gss_wrap() tokens for "secure framing", so implementations must support calculating and verifying MICs over zero-length messages 🧚

OM_uint32 gss_verify_mic (
  OM_uint32          *minor_status,
  const gss_ctx_id_t context_handle,
  const gss_buffer_t message_buffer,
  const gss_buffer_t token_buffer,
  gss_qop_t          *qop_state)
minor_status      Integer, modify
                  Mechanism specific status code.

context_handle    gss_ctx_id_t, read
                  Identifies the context on which the message
                  arrived

message_buffer    buffer, opaque, read
                  Message to be verified

token_buffer      buffer, opaque, read
                  Token associated with message

qop_state         gss_qop_t, modify, optional
                  quality of protection gained from MIC
                  Specify NULL if not required

Function value:   GSS status code

GSS_S_COMPLETE    Successful completion

GSS_S_DEFECTIVE_TOKEN The token failed consistency checks

GSS_S_BAD_SIG     The MIC was incorrect

GSS_S_DUPLICATE_TOKEN The token was valid, and contained a correct
                  MIC for the message, but it had already been
                  processed

GSS_S_OLD_TOKEN   The token was valid, and contained a correct MIC
                  for the message, but it is too old to check for
                  duplication.

GSS_S_UNSEQ_TOKEN The token was valid, and contained a correct MIC
                  for the message, but has been verified out of
                  sequence; a later token has already been received.

GSS_S_GAP_TOKEN   The token was valid, and contained a correct MIC
                  for the message, but has been verified out of
                  sequence; an earlier expected token has not yet
                  been received.

GSS_S_CONTEXT_EXPIRED The context has already expired

GSS_S_NO_CONTEXT  The context_handle parameter did not identify a
                  valid context

5.33. gss_wrap πŸŽ€

Purpose: this is the main event πŸ’– gss_wrap attaches a cryptographic MIC to your message and can optionally encrypt it too. The output_message holds both the MIC and the message together. The qop_req parameter lets you pick between several cryptographic algorithms, as long as the chosen mechanism supports them ✨

Once again, the "secure framing" crowd is a thing, so implementations must support wrapping zero-length messages 🫢

OM_uint32 gss_wrap (
  OM_uint32          *minor_status,
  const gss_ctx_id_t context_handle,
  int               conf_req_flag,
  gss_qop_t          qop_req
  const gss_buffer_t input_message_buffer,
  int                *conf_state,
  gss_buffer_t       output_message_buffer )
minor_status      Integer, modify
                  Mechanism specific status code.

context_handle    gss_ctx_id_t, read
                  Identifies the context on which the message
                  will be sent

conf_req_flag     boolean, read
                  Non-zero - Both confidentiality and integrity
                             services are requested
                  Zero - Only integrity service is requested

qop_req           gss_qop_t, read, optional
                  Specifies required quality of protection.  A
                  mechanism-specific default may be requested by
                  setting qop_req to GSS_C_QOP_DEFAULT.  If an
                  unsupported protection strength is requested,
                  gss_wrap will return a major_status of
                  GSS_S_BAD_QOP.

input_message_buffer  buffer, opaque, read
                  Message to be protected

conf_state        boolean, modify, optional
                  Non-zero - Confidentiality, data origin
                             authentication and integrity
                             services have been applied
                  Zero - Integrity and data origin services only
                         has been applied.
                  Specify NULL if not required

output_message_buffer  buffer, opaque, modify
                  Buffer to receive protected message.
                  Storage associated with this message must
                  be freed by the application after use with
                  a call to gss_release_buffer().

Function value:   GSS status code

GSS_S_COMPLETE    Successful completion

GSS_S_CONTEXT_EXPIRED The context has already expired

GSS_S_NO_CONTEXT  The context_handle parameter did not identify a
                  valid context

GSS_S_BAD_QOP     The specified QOP is not supported by the
                  mechanism.

5.34. gss_wrap_size_limit πŸ“

Purpose: this one is the size-planning queen πŸ‘‘ It lets an application figure out the biggest message it can hand to gss_wrap, using the same conf_req_flag and qop_req values, and still get an output token that's no more than req_output_size bytes πŸ“

It's built for apps that talk over protocols with a maximum message size. You ask first, then chop your messages into fragments before protecting them 🧚

Implementations are recommended but not required to detect invalid QOP values when gss_wrap_size_limit() is called. This routine only guarantees a maximum message size, not that any specific QOP value will be available for protecting messages 🫠

Heads up, though: a successful call doesn't promise that gss_wrap will actually protect a message of max_input_size bytes πŸ’€ That depends on system resources being available when gss_wrap runs. And if the implementation has its own upper limit on message length for gss_wrap, the implementation should not return a max_input_bytes value bigger than that limit ✨

OM_uint32 gss_wrap_size_limit (
  OM_uint32          *minor_status,
  const gss_ctx_id_t context_handle,
  int                conf_req_flag,
  gss_qop_t          qop_req,
  OM_uint32          req_output_size,
  OM_uint32          *max_input_size)
minor_status      Integer, modify
                  Mechanism specific status code

context_handle    gss_ctx_id_t, read
                  A handle that refers to the security over
                  which the messages will be sent.

conf_req_flag     Boolean, read
                  Indicates whether gss_wrap will be asked
                  to apply confidentiality protection in
                  addition to integrity protection.  See
                  the routine description for gss_wrap
                  for more details.

qop_req           gss_qop_t, read
                  Indicates the level of protection that
                  gss_wrap will be asked to provide.  See
                  the routine description for gss_wrap for
                  more details.

req_output_size   Integer, read
                  The desired maximum size for tokens emitted
                  by gss_wrap.
   max_input_size    Integer, modify
                     The maximum input message size that may
                     be presented to gss_wrap in order to
                     guarantee that the emitted token shall
                     be no larger than req_output_size bytes.

   Function value:   GSS status code

   GSS_S_COMPLETE    Successful completion

   GSS_S_NO_CONTEXT  The referenced context could not be accessed.

   GSS_S_CONTEXT_EXPIRED The context has expired.

   GSS_S_BAD_QOP     The specified QOP is not supported by the
                     mechanism.

6. Security Considerations ✨

okay bestie, this part is SO important 🫢 GSS-API is a service interface for security stuff, so security considerations pop up all over the whole spec. But there are a few points that are extra important for anyone implementing GSS-API or building apps on top of it, so let's go through them πŸ”

Using the GSS-API interface doesn't give you security services or assurance by itself πŸ™ˆ Those properties depend on the underlying mechanism(s) that the GSS-API implementation runs on. So the mechanism is doing the heavy lifting, not the interface.

Callers need to be super attentive to the requests they make to GSS-API calls and to the status indicators that come back, because those tell you exactly which security service characteristics GSS-API is going to provide. Don't just vibe with it, check the status πŸ‘€

And when the interprocess context transfer facility is used, appropriate local controls should be applied to constrain access to interprocess tokens and to the sensitive data they contain. Lock that down, periodt πŸ”’πŸ’€


Appendix A. GSS-API C header file gssapi.h ✨

C-language GSS-API implementations should include a copy of the following header file. Here's the whole header, verbatim, because implementers need every single character πŸ’…

   #ifndef GSSAPI_H_
   #define GSSAPI_H_



   /*
    * First, include stddef.h to get size_t defined.
    */
   #include <stddef.h>

   /*
    * If the platform supports the xom.h header file, it should be
    * included here.
    */
   #include <xom.h>


   /*
    * Now define the three implementation-dependent types.
    */
   typedef <platform-specific> gss_ctx_id_t;
   typedef <platform-specific> gss_cred_id_t;
   typedef <platform-specific> gss_name_t;

   /*
    * The following type must be defined as the smallest natural
    * unsigned integer supported by the platform that has at least
    * 32 bits of precision.
    */
   typedef <platform-specific> gss_uint32;


   #ifdef OM_STRING
   /*
    * We have included the xom.h header file.  Verify that OM_uint32
    * is defined correctly.
    */

   #if sizeof(gss_uint32) != sizeof(OM_uint32)
   #error Incompatible definition of OM_uint32 from xom.h
   #endif

   typedef OM_object_identifier gss_OID_desc, *gss_OID;
   #else

   /*
    * We can't use X/Open definitions, so roll our own.
    */

   typedef gss_uint32 OM_uint32;

   typedef struct gss_OID_desc_struct {
     OM_uint32 length;
     void      *elements;
   } gss_OID_desc, *gss_OID;

   #endif

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

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

   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;

   /*
    * For now, define a QOP-type as an OM_uint32
    */
   typedef OM_uint32 gss_qop_t;

   typedef int gss_cred_usage_t;

   /*
    * Flag bits for context-level services.
    */

   #define GSS_C_DELEG_FLAG      1
   #define GSS_C_MUTUAL_FLAG     2
   #define GSS_C_REPLAY_FLAG     4
   #define GSS_C_SEQUENCE_FLAG   8
   #define GSS_C_CONF_FLAG       16
   #define GSS_C_INTEG_FLAG      32
   #define GSS_C_ANON_FLAG       64
   #define GSS_C_PROT_READY_FLAG 128
   #define GSS_C_TRANS_FLAG      256

   /*
    * Credential usage options
    */
   #define GSS_C_BOTH     0
   #define GSS_C_INITIATE 1
   #define GSS_C_ACCEPT   2

   /*
    * Status code types for gss_display_status
    */
   #define GSS_C_GSS_CODE  1
   #define GSS_C_MECH_CODE 2

   /*
    * The constant definitions for channel-bindings address families
    */
   #define GSS_C_AF_UNSPEC     0
   #define GSS_C_AF_LOCAL      1
   #define GSS_C_AF_INET       2
   #define GSS_C_AF_IMPLINK    3
   #define GSS_C_AF_PUP        4
   #define GSS_C_AF_CHAOS      5
   #define GSS_C_AF_NS         6
   #define GSS_C_AF_NBS        7
   #define GSS_C_AF_ECMA       8
   #define GSS_C_AF_DATAKIT    9
   #define GSS_C_AF_CCITT      10
   #define GSS_C_AF_SNA        11
   #define GSS_C_AF_DECnet     12
   #define GSS_C_AF_DLI        13
   #define GSS_C_AF_LAT        14
   #define GSS_C_AF_HYLINK     15
   #define GSS_C_AF_APPLETALK  16
   #define GSS_C_AF_BSC        17
   #define GSS_C_AF_DSS        18
   #define GSS_C_AF_OSI        19
   #define GSS_C_AF_X25        21
   #define GSS_C_AF_NULLADDR   255

   /*
    * Various Null values
    */
   #define GSS_C_NO_NAME ((gss_name_t) 0)
   #define GSS_C_NO_BUFFER ((gss_buffer_t) 0)
   #define GSS_C_NO_OID ((gss_OID) 0)
   #define GSS_C_NO_OID_SET ((gss_OID_set) 0)
   #define GSS_C_NO_CONTEXT ((gss_ctx_id_t) 0)
   #define GSS_C_NO_CREDENTIAL ((gss_cred_id_t) 0)
   #define GSS_C_NO_CHANNEL_BINDINGS ((gss_channel_bindings_t) 0)
   #define GSS_C_EMPTY_BUFFER {0, NULL}

   /*
    * Some alternate names for a couple of the above
    * values.  These are defined for V1 compatibility.
    */
   #define GSS_C_NULL_OID GSS_C_NO_OID
   #define GSS_C_NULL_OID_SET GSS_C_NO_OID_SET

   /*
    * Define the default Quality of Protection for per-message
    * services.  Note that an implementation that offers multiple
    * levels of QOP may define GSS_C_QOP_DEFAULT to be either zero
    * (as done here) to mean "default protection", or to a specific
    * explicit QOP value.  However, a value of 0 should always be
    * interpreted by a GSS-API implementation as a request for the
    * default protection level.
    */
   #define GSS_C_QOP_DEFAULT 0

   /*
    * Expiration time of 2^32-1 seconds means infinite lifetime for a
    * credential or security context
    */
   #define GSS_C_INDEFINITE 0xfffffffful

Next up are the OID constants, which are the fun part where every byte matters 🧚 Read these carefully, babe πŸ’œ

   /*
    * The implementation must reserve static storage for a
    * gss_OID_desc object containing the value
    * {10, (void *)"\x2a\x86\x48\x86\xf7\x12"
    * "\x01\x02\x01\x01"},
    * corresponding to an object-identifier value of
    * {iso(1) member-body(2) United States(840) mit(113554)
    * infosys(1) gssapi(2) generic(1) user_name(1)}.  The constant
    * GSS_C_NT_USER_NAME should be initialized to point
    * to that gss_OID_desc.

    */
   extern gss_OID GSS_C_NT_USER_NAME;

   /*
    * The implementation must reserve static storage for a
    * gss_OID_desc object containing the value
    * {10, (void *)"\x2a\x86\x48\x86\xf7\x12"
    *              "\x01\x02\x01\x02"},
    * corresponding to an object-identifier value of
    * {iso(1) member-body(2) United States(840) mit(113554)
    * infosys(1) gssapi(2) generic(1) machine_uid_name(2)}.
    * The constant GSS_C_NT_MACHINE_UID_NAME should be
    * initialized to point to that gss_OID_desc.
    */
   extern gss_OID GSS_C_NT_MACHINE_UID_NAME;

   /*
    * The implementation must reserve static storage for a
    * gss_OID_desc object containing the value
    * {10, (void *)"\x2a\x86\x48\x86\xf7\x12"
    *              "\x01\x02\x01\x03"},
    * corresponding to an object-identifier value of
    * {iso(1) member-body(2) United States(840) mit(113554)
    * infosys(1) gssapi(2) generic(1) string_uid_name(3)}.
    * The constant GSS_C_NT_STRING_UID_NAME should be
    * initialized to point to that gss_OID_desc.
    */
   extern gss_OID GSS_C_NT_STRING_UID_NAME;

   /*
    * The implementation must reserve static storage for a
    * gss_OID_desc object containing the value
    * {6, (void *)"\x2b\x06\x01\x05\x06\x02"},
    * corresponding to an object-identifier value of
    * {iso(1) org(3) dod(6) internet(1) security(5)
    * nametypes(6) gss-host-based-services(2)).  The constant
    * GSS_C_NT_HOSTBASED_SERVICE_X should be initialized to point
    * to that gss_OID_desc.  This is a deprecated OID value, and
    * implementations wishing to support hostbased-service names
    * should instead use the GSS_C_NT_HOSTBASED_SERVICE OID,
    * defined below, to identify such names;
    * GSS_C_NT_HOSTBASED_SERVICE_X should be accepted a synonym
    * for GSS_C_NT_HOSTBASED_SERVICE when presented as an input
    * parameter, but should not be emitted by GSS-API
    * implementations
    */
   extern gss_OID GSS_C_NT_HOSTBASED_SERVICE_X;
   /*
    * The implementation must reserve static storage for a
    * gss_OID_desc object containing the value
    * {10, (void *)"\x2a\x86\x48\x86\xf7\x12"
    *              "\x01\x02\x01\x04"}, corresponding to an
    * object-identifier value of {iso(1) member-body(2)
    * Unites States(840) mit(113554) infosys(1) gssapi(2)
    * generic(1) service_name(4)}.  The constant
    * GSS_C_NT_HOSTBASED_SERVICE should be initialized
    * to point to that gss_OID_desc.
    */
   extern gss_OID GSS_C_NT_HOSTBASED_SERVICE;

   /*
    * The implementation must reserve static storage for a
    * gss_OID_desc object containing the value
    * {6, (void *)"\x2b\x06\01\x05\x06\x03"},
    * corresponding to an object identifier value of
    * {1(iso), 3(org), 6(dod), 1(internet), 5(security),
    * 6(nametypes), 3(gss-anonymous-name)}.  The constant
    * and GSS_C_NT_ANONYMOUS should be initialized to point
    * to that gss_OID_desc.
    */
   extern gss_OID GSS_C_NT_ANONYMOUS;


   /*
    * The implementation must reserve static storage for a
    * gss_OID_desc object containing the value
    * {6, (void *)"\x2b\x06\x01\x05\x06\x04"},
    * corresponding to an object-identifier value of
    * {1(iso), 3(org), 6(dod), 1(internet), 5(security),
    * 6(nametypes), 4(gss-api-exported-name)}.  The constant
    * GSS_C_NT_EXPORT_NAME should be initialized to point
    * to that gss_OID_desc.
    */
   extern gss_OID GSS_C_NT_EXPORT_NAME;


   /* Major status codes */

   #define GSS_S_COMPLETE 0

   /*
    * Some "helper" definitions to make the status code macros obvious.
    */
   #define GSS_C_CALLING_ERROR_OFFSET 24
   #define GSS_C_ROUTINE_ERROR_OFFSET 16
   #define GSS_C_SUPPLEMENTARY_OFFSET 0
   #define GSS_C_CALLING_ERROR_MASK 0377ul
   #define GSS_C_ROUTINE_ERROR_MASK 0377ul
   #define GSS_C_SUPPLEMENTARY_MASK 0177777ul

   /*
    * The macros that test status codes for error conditions.
    * Note that the GSS_ERROR() macro has changed slightly from
    * the V1 GSS-API so that it now evaluates its argument
    * only once.
    */
   #define GSS_CALLING_ERROR(x) \
    (x & (GSS_C_CALLING_ERROR_MASK << GSS_C_CALLING_ERROR_OFFSET))
   #define GSS_ROUTINE_ERROR(x) \
    (x & (GSS_C_ROUTINE_ERROR_MASK << GSS_C_ROUTINE_ERROR_OFFSET))
   #define GSS_SUPPLEMENTARY_INFO(x) \
    (x & (GSS_C_SUPPLEMENTARY_MASK << GSS_C_SUPPLEMENTARY_OFFSET))
   #define GSS_ERROR(x) \
    (x & ((GSS_C_CALLING_ERROR_MASK << GSS_C_CALLING_ERROR_OFFSET) | \
          (GSS_C_ROUTINE_ERROR_MASK << GSS_C_ROUTINE_ERROR_OFFSET)))

   /*
    * Now the actual status code definitions
    */

   /*
    * Calling errors:

    */
   #define GSS_S_CALL_INACCESSIBLE_READ \
   (1ul << GSS_C_CALLING_ERROR_OFFSET)
   #define GSS_S_CALL_INACCESSIBLE_WRITE \
   (2ul << GSS_C_CALLING_ERROR_OFFSET)
   #define GSS_S_CALL_BAD_STRUCTURE \
   (3ul << GSS_C_CALLING_ERROR_OFFSET)

/*

  • Routine errors: */ #define GSS_S_BAD_MECH (1ul << GSS_C_ROUTINE_ERROR_OFFSET) #define GSS_S_BAD_NAME (2ul << GSS_C_ROUTINE_ERROR_OFFSET) #define GSS_S_BAD_NAMETYPE (3ul << GSS_C_ROUTINE_ERROR_OFFSET) #define GSS_S_BAD_BINDINGS (4ul << GSS_C_ROUTINE_ERROR_OFFSET) #define GSS_S_BAD_STATUS (5ul << GSS_C_ROUTINE_ERROR_OFFSET) #define GSS_S_BAD_SIG (6ul << GSS_C_ROUTINE_ERROR_OFFSET) #define GSS_S_BAD_MIC GSS_S_BAD_SIG #define GSS_S_NO_CRED (7ul << GSS_C_ROUTINE_ERROR_OFFSET) #define GSS_S_NO_CONTEXT (8ul << GSS_C_ROUTINE_ERROR_OFFSET) #define GSS_S_DEFECTIVE_TOKEN (9ul << GSS_C_ROUTINE_ERROR_OFFSET) #define GSS_S_DEFECTIVE_CREDENTIAL (10ul << GSS_C_ROUTINE_ERROR_OFFSET) #define GSS_S_CREDENTIALS_EXPIRED (11ul << GSS_C_ROUTINE_ERROR_OFFSET) #define GSS_S_CONTEXT_EXPIRED (12ul << GSS_C_ROUTINE_ERROR_OFFSET) #define GSS_S_FAILURE (13ul << GSS_C_ROUTINE_ERROR_OFFSET) #define GSS_S_BAD_QOP (14ul << GSS_C_ROUTINE_ERROR_OFFSET) #define GSS_S_UNAUTHORIZED (15ul << GSS_C_ROUTINE_ERROR_OFFSET) #define GSS_S_UNAVAILABLE (16ul << GSS_C_ROUTINE_ERROR_OFFSET) #define GSS_S_DUPLICATE_ELEMENT (17ul << GSS_C_ROUTINE_ERROR_OFFSET) #define GSS_S_NAME_NOT_MN (18ul << GSS_C_ROUTINE_ERROR_OFFSET)

/*

  • Supplementary info bits: */ #define GSS_S_CONTINUE_NEEDED \ (1ul << (GSS_C_SUPPLEMENTARY_OFFSET + 0)) #define GSS_S_DUPLICATE_TOKEN \ (1ul << (GSS_C_SUPPLEMENTARY_OFFSET + 1)) #define GSS_S_OLD_TOKEN \ (1ul << (GSS_C_SUPPLEMENTARY_OFFSET + 2)) #define GSS_S_UNSEQ_TOKEN \ (1ul << (GSS_C_SUPPLEMENTARY_OFFSET + 3)) #define GSS_S_GAP_TOKEN \ (1ul << (GSS_C_SUPPLEMENTARY_OFFSET + 4))

/*

  • Finally, function prototypes for the GSS-API routines. */

OM_uint32 gss_acquire_cred (OM_uint32 , /* minor_status / const gss_name_t, / desired_name / OM_uint32, / time_req / const gss_OID_set, / desired_mechs / gss_cred_usage_t, / cred_usage / gss_cred_id_t , / output_cred_handle / gss_OID_set , / actual_mechs / OM_uint32 * / time_rec */ );

OM_uint32 gss_release_cred (OM_uint32 , /* minor_status / gss_cred_id_t * / cred_handle */ );

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 */ );

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 / gss_name_t , / src_name / gss_OID , / mech_type / gss_buffer_t, / output_token / OM_uint32 , / ret_flags / OM_uint32 , / time_rec / gss_cred_id_t * / delegated_cred_handle */ );

OM_uint32 gss_process_context_token (OM_uint32 , /* minor_status / const gss_ctx_id_t, / context_handle / const gss_buffer_t / token_buffer */ );

OM_uint32 gss_delete_sec_context (OM_uint32 , /* minor_status / gss_ctx_id_t , / context_handle / gss_buffer_t / output_token */ );

OM_uint32 gss_context_time (OM_uint32 , /* minor_status / const gss_ctx_id_t, / context_handle / OM_uint32 * / time_rec */ );

OM_uint32 gss_get_mic (OM_uint32 , /* minor_status / const gss_ctx_id_t, / context_handle / gss_qop_t, / qop_req / const gss_buffer_t, / message_buffer / gss_buffer_t / message_token */ );

OM_uint32 gss_verify_mic (OM_uint32 , /* minor_status / const gss_ctx_id_t, / context_handle / const gss_buffer_t, / message_buffer / const gss_buffer_t, / token_buffer / gss_qop_t * / qop_state */ );

OM_uint32 gss_wrap (OM_uint32 , /* minor_status / const gss_ctx_id_t, / context_handle / int, / conf_req_flag / gss_qop_t, / qop_req / const gss_buffer_t, / input_message_buffer / int , / conf_state / gss_buffer_t / output_message_buffer */ );

OM_uint32 gss_unwrap (OM_uint32 , /* minor_status / const gss_ctx_id_t, / context_handle / const gss_buffer_t, / input_message_buffer / gss_buffer_t, / output_message_buffer / int , / conf_state / gss_qop_t * / qop_state */ );

OM_uint32 gss_display_status (OM_uint32 , /* minor_status / OM_uint32, / status_value / int, / status_type / const gss_OID, / mech_type / OM_uint32 , / message_context / gss_buffer_t / status_string */ );

OM_uint32 gss_indicate_mechs (OM_uint32 , /* minor_status / gss_OID_set * / mech_set */ );

OM_uint32 gss_compare_name (OM_uint32 , /* minor_status / const gss_name_t, / name1 / const gss_name_t, / name2 / int * / name_equal */ );

OM_uint32 gss_display_name (OM_uint32 , /* minor_status / const gss_name_t, / input_name / gss_buffer_t, / output_name_buffer / gss_OID * / output_name_type */ );

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 */ );

OM_uint32 gss_export_name (OM_uint32, /* minor_status / const gss_name_t, / input_name / gss_buffer_t / exported_name */ );

OM_uint32 gss_release_name (OM_uint32 , / minor_status / gss_name_t * / input_name */ );

OM_uint32 gss_release_buffer (OM_uint32 , /* minor_status / gss_buffer_t / buffer */ );

OM_uint32 gss_release_oid_set (OM_uint32 , /* minor_status / gss_OID_set * / set */ );

OM_uint32 gss_inquire_cred (OM_uint32 , /* minor_status / const gss_cred_id_t, / cred_handle / gss_name_t , / name / OM_uint32 , / lifetime / gss_cred_usage_t , / cred_usage / gss_OID_set * / mechanisms */ );

OM_uint32 gss_inquire_context ( OM_uint32 , /* minor_status / const gss_ctx_id_t, / context_handle / gss_name_t , / src_name / gss_name_t , / targ_name / OM_uint32 , / lifetime_rec / gss_OID , / mech_type / OM_uint32 , / ctx_flags / int , / locally_initiated / int * / open */ );

OM_uint32 gss_wrap_size_limit ( OM_uint32 , /* minor_status / const gss_ctx_id_t, / context_handle / int, / conf_req_flag / gss_qop_t, / qop_req / OM_uint32, / req_output_size / OM_uint32 * / max_input_size */ );

OM_uint32 gss_add_cred ( OM_uint32 , /* minor_status / const gss_cred_id_t, / input_cred_handle / const gss_name_t, / desired_name / const gss_OID, / desired_mech / gss_cred_usage_t, / cred_usage / OM_uint32, / initiator_time_req / OM_uint32, / acceptor_time_req / gss_cred_id_t , / output_cred_handle / gss_OID_set , / actual_mechs / OM_uint32 , / initiator_time_rec / OM_uint32 * / acceptor_time_rec */ );

OM_uint32 gss_inquire_cred_by_mech ( OM_uint32 , /* minor_status / const gss_cred_id_t, / cred_handle / const gss_OID, / mech_type / gss_name_t , / name / OM_uint32 , / initiator_lifetime / OM_uint32 , / acceptor_lifetime / gss_cred_usage_t * / cred_usage */ );

OM_uint32 gss_export_sec_context ( OM_uint32 , /* minor_status / gss_ctx_id_t , / context_handle / gss_buffer_t / interprocess_token */ );

   OM_uint32 gss_import_sec_context (
                  OM_uint32 ,             /* minor_status */
                  const gss_buffer_t,     /* interprocess_token */
                  gss_ctx_id_t *          /* context_handle */
                 );

   OM_uint32 gss_create_empty_oid_set (
                  OM_uint32 ,             /* minor_status */
                  gss_OID_set *           /* oid_set */
                 );

   OM_uint32 gss_add_oid_set_member (
                  OM_uint32 ,             /* minor_status */
                  const gss_OID,          /* member_oid */
                  gss_OID_set *           /* oid_set */
                 );

   OM_uint32 gss_test_oid_set_member (
                  OM_uint32 ,             /* minor_status */
                  const gss_OID,          /* member */
                  const gss_OID_set,      /* set */
                  int *                   /* present */
                 );

   OM_uint32 gss_inquire_names_for_mech (
                  OM_uint32 ,             /* minor_status */
                  const gss_OID,          /* mechanism */
                  gss_OID_set *           /* name_types */
                 );

   OM_uint32 gss_inquire_mechs_for_name (
                  OM_uint32 ,             /* minor_status */
                  const gss_name_t,       /* input_name */
                  gss_OID_set *           /* mech_types */
                 );

   OM_uint32 gss_canonicalize_name (
                  OM_uint32 ,             /* minor_status */
                  const gss_name_t,       /* input_name */
                  const gss_OID,          /* mech_type */
                  gss_name_t *            /* output_name */
                 );

   OM_uint32 gss_duplicate_name (
                  OM_uint32 ,             /* minor_status */
                  const gss_name_t,       /* src_name */
                  gss_name_t *            /* dest_name */
                 );

   /*
    * The following routines are obsolete variants of gss_get_mic,
    * gss_verify_mic, gss_wrap and gss_unwrap.  They should be
    * provided by GSS-API V2 implementations for backwards
    * compatibility with V1 applications.  Distinct entrypoints
    * (as opposed to #defines) should be provided, both to allow
    * GSS-API V1 applications to link against GSS-API V2
      implementations,
    * and to retain the slight parameter type differences between the
    * obsolete versions of these routines and their current forms.
    */

   OM_uint32 gss_sign
                 (OM_uint32 ,        /* minor_status */
                  gss_ctx_id_t,      /* context_handle */
                  int,               /* qop_req */
                  gss_buffer_t,      /* message_buffer */
                  gss_buffer_t       /* message_token */
                 );


   OM_uint32 gss_verify
                 (OM_uint32 ,        /* minor_status */
                  gss_ctx_id_t,      /* context_handle */
                  gss_buffer_t,      /* message_buffer */
                  gss_buffer_t,      /* token_buffer */
                  int *              /* qop_state */
                 );

   OM_uint32 gss_seal
                 (OM_uint32 ,        /* minor_status */
                  gss_ctx_id_t,      /* context_handle */
                  int,               /* conf_req_flag */
                  int,               /* qop_req */
                  gss_buffer_t,      /* input_message_buffer */
                  int ,              /* conf_state */
                  gss_buffer_t       /* output_message_buffer */
                 );


   OM_uint32 gss_unseal
                 (OM_uint32 ,        /* minor_status */
                  gss_ctx_id_t,      /* context_handle */
                  gss_buffer_t,      /* input_message_buffer */
                  gss_buffer_t,      /* output_message_buffer */
                  int ,              /* conf_state */
                  int *              /* qop_state */
                 );

   #endif /* GSSAPI_H_ */

Appendix B. Additional constraints for application binary portability ✨

okay so this appendix is all about binary portability, which is a whole other level of "it works on my machine" πŸ’…

The purpose of this C-bindings document is to encourage source-level portability of applications across GSS-API implementations on different platforms and atop different mechanisms. Additional goals that have not been explicitly addressed by this document are link-time and run-time portability. 🌸

Link-time portability provides the ability to compile an application against one implementation of GSS-API, and then link it against a different implementation on the same platform. It is a stricter requirement than source-level portability. πŸŽ€

Run-time portability differs from link-time portability only on those platforms that implement dynamically loadable GSS-API implementations, but do not offer load-time symbol resolution. On such platforms, run-time portability is a stricter requirement than link-time portability, and will typically include the precise placement of the various GSS-API routines within library entrypoint vectors. 🧚

Individual platforms will impose their own rules that must be followed to achieve link-time (and run-time, if different) portability. In order to ensure either form of binary portability, an ABI specification must be written for GSS-API implementations on that platform. However, it is recognized that there are some issues that are likely to be common to all such ABI specifications. This appendix is intended to be a repository for such common issues, and contains some suggestions that individual ABI specifications may choose to reference. Since machine architectures vary greatly, it may not be possible or desirable to follow these suggestions on all platforms. πŸ¦‹

B.1. Pointers πŸ’–

While ANSI-C provides a single pointer type for each declared type, plus a single (void *) type, some platforms (notably those using segmented memory architectures) augment this with various modified pointer types (e.g. far pointers, near pointers). These language bindings assume ANSI-C, and thus do not address such non-standard implementations. GSS-API implementations for such platforms must choose an appropriate memory model, and should use it consistently throughout. For example, if a memory model is chosen that requires the use of far pointers when passing routine parameters, then far pointers should also be used within the structures defined by GSS-API. πŸ‘‘

B.2. Internal structure alignment πŸ“

GSS-API defines several data-structures containing differently-sized fields. An ABI specification should include a detailed description of how the fields of such structures are aligned, and if there is any internal padding in these data structures. The use of compiler defaults for the platform is recommended. ✨

B.3. Handle types 🫢

The C bindings specify that the gss_cred_id_t and gss_ctx_id_t types should be implemented as either pointer or arithmetic types, and that if pointer types are used, care should be taken to ensure that two handles may be compared with the == operator. Note that ANSI-C does not guarantee that two pointer values may be compared with the == operator unless either the two pointers point to members of a single array, or at least one of the pointers contains a NULL value. πŸ™ˆ

For binary portability, additional constraints are required. The following is an attempt at defining platform-independent constraints. 🌈

The size of the handle type must be the same as sizeof(void *), using the appropriate memory model.

The == operator for the chosen type must be a simple bit-wise comparison. That is, for two in-memory handle objects h1 and h2, the boolean value of the expression

(h1 == h2)

should always be the same as the boolean value of the expression

(memcmp(&h1, &h2, sizeof(h1)) == 0)

The actual use of the type (void *) for handle types is discouraged, not for binary portability reasons, but since it effectively disables much of the compile-time type-checking that the compiler can otherwise perform, and is therefore not "programmer-friendly". If a pointer implementation is desired, and if the platform's implementation of pointers permits, the handles should be implemented as pointers to distinct implementation-defined types. πŸ’œ

B.4. The gss_name_t type πŸ“

The gss_name_t type, representing the internal name object, should be implemented as a pointer type. The use of the (void *) type is discouraged as it does not allow the compiler to perform strong type-checking. However, the pointer type chosen should be of the same size as the (void *) type. Provided this rule is obeyed, ABI specifications need not further constrain the implementation of gss_name_t objects. πŸ’…

B.5. The int and size_t types πŸ”₯

Some platforms may support differently sized implementations of the "int" and "size_t" types, perhaps chosen through compiler switches, and perhaps dependent on memory model. An ABI specification for such a platform should include required implementations for these types. It is recommended that the default implementation (for the chosen memory model, if appropriate) is chosen. ⭐

B.6. Procedure-calling conventions 🌸

Some platforms support a variety of different binary conventions for calling procedures. Such conventions cover things like the format of the stack frame, the order in which the routine parameters are pushed onto the stack, whether or not a parameter count is pushed onto the stack, whether some argument(s) or return values are to be passed in registers, and whether the called routine or the caller is responsible for removing the stack frame on return. For such platforms, an ABI specification should specify which calling convention is to be used for GSS-API implementations. 🧚

References πŸ“š

  • [GSSAPI] Linn, J., "Generic Security Service Application Program Interface Version 2, Update 1", RFC 2743, January 2000.
  • [XOM] OSI Object Management API Specification, Version 2.0 t", X.400 API Association & X/Open Company Limited, August 24, 1990 Specification of datatypes and routines for manipulating information objects.

Author's Address πŸ¦‹

John Wray
Iris Associates
5 Technology Park Drive,
Westford, MA  01886
USA

Phone: +1-978-392-6689
EMail: John_Wray@Iris.com

Copyright (C) The Internet Society (2000). All Rights Reserved. πŸ’–

Okay bestie, so the Internet Society owns the copyright here, and they're being really generous about sharing it. 🫢 You can copy this document and pass it around to your friends. You can also write derivative works that comment on it, explain it, or help people actually build with it, and you can publish and distribute those too, in whole or in part, with no real restrictions. ✨ The one big catch is that the copyright notice and this paragraph have to show up on every copy and every derivative work. πŸ™ˆ

The original document itself is a different story. You can't edit it in any way. That means no deleting the copyright notice, and no swapping out the references to the Internet Society or other Internet organizations. πŸ’… The only exceptions are when you need it to develop Internet standards, where you have to follow the copyright procedures in the Internet Standards process, or when you need to translate it into a language other than English. 🌈

These permissions are perpetual. The Internet Society and its successors or assigns will never take them back. πŸ¦‹

Now the legal part, and I promise it's short. πŸ’€ This document and the information in it are provided "AS IS." THE INTERNET SOCIETY AND THE INTERNET ENGINEERING TASK FORCE DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, including, but not limited to, any warranty that using the information here won't infringe anyone's rights, and any implied warranties of merchantability or fitness for a particular purpose. So basically they're not promising it'll work for everything, and that's just how it is. 😭


Acknowledgement 🌸

Funding for the RFC Editor function is currently provided by the Internet Society. That's the whole acknowledgement, and it's giving thank-you-for-the-snacks energy. 🧚

πŸ“¦ where this page comes from