Skip to content

Golang GSSAPI bindings specification

Jake Scott edited this page Jul 30, 2024 · 11 revisions

This document is a proposed specification for Go language bindings for the GSSAPI Version 2, described in the language-neutral RFC 2743. It does not aim to be quite as thorough as an RFC but should provide enough detail to allow for co-compatible implementations to be developed. In particular there is not much benefit repeating generic information about how GSSAPI works, its features and how it is used.

Calling conventions and data types

The language-neutral specification lists a set of routines that comprise the API. Like Java (RFC 2853 § 4), Go provides an environment that obviates the need for a number of the support calls (RFC 2743 § 2.4), specifically related to memory management. Additionally, GSSAPI primitives and the calls that operate on them are wrapped by Go interfaces, leading to a simplified and idiomatic API that is familiar to experienced Go developers.

Integer types

The Go GSSAPI bindings make use of the int32 type.

Boolean type

The native Go bool type is specified for boolean values.

Strings

The native Go string type should be used to represent any textual data. Go strings include a length which obviates the need for a separate data structure as used by the C bindings.

Opaque data types

Slices of bytes should be used to carry any opaque data (such as tokens) to or from GSSAPI. Go slices include a length that obviates the need for a separate data structure as specified by the C bindings.

Object Identifiers (OIDs)

Implementations should define the Oid type:

type Oid []byte

Elements of the byte slice represent the DER encoding of the object identifier, without the ASN.1 header (two bytes: tag value 0x06 and length) as per this Microsoft document.

Other implementations provide constant OID values for supported mechanisms and names. This specification instead calls for concrete types for mechansisms and names, and methods for translating between those types and associated OIDs. As such, the empty or nil Oid value does not have a special meaning.

Object Identifier sets

The Go bindings represent OID sets as slices or arrays of Oid types, ([]Oid).

Credentials

TBD..

Status values

RFC 2743 § 1.2.1 specifies two status code return vales (major and minor) from each GSSAPI call. The major value is used to convey fatal errs and informational codes making use of different bit ranges within the 32 bit value; the use of the minor code is left to the implementation.

The Go bindings instead make use of Go's standard error interface. Implementations should define two types (InfoStatus and FatalStatus) implementing the error interface. InfoStatus objects are to be returned when an informational code is available but a function otherwise succeeded. FatalStatus objects are to be returned when a function fails. Callers can use the Go errors package to determine whether a response is fatal or not:

  err := gssapi.SomeFunction()
  info := gssapi.InfoStatus{}
  if errors.As(err, &info) {
    log.Printf("Warning: %s", info)
  } else {
    panic(err)
  }

The InfoStatus and FatalStatus type should implement the Unwrap() method:

Unwrap() []error

.. as defined in the standard error package. The unwrap method of InfoStatus should return a list of informational error objects. The unwrap method of FatalStatus should return a list of error and infomrational error objects.

Implementaitons should define the following variables, correlating to the fatal and infromational codes defined by RFC 2743. The value of these variables must implement the error interface:

Variable RFC 2743 value
ErrBadBindings GSS_S_BAD_BINDINGS
ErrBadMech GSS_S_BAD_MECH
ErrBadName GSS_S_BAD_NAME
ErrBadNameType GSS_S_BAD_NAMETYPE
ErrBadStatus GSS_S_BAD_STATUS
ErrBadMic GSS_S_BAD_SIG
ErrBadSig =ErrBadMic GSS_S_BAD_MIC = GSS_S_BAD_SIG
ErrContextExpired GSS_S_CONTEXT_EXPIRED
ErrCredentialsExpired GSS_S_CREDENTIALS_EXPIRED
ErrDefectiveCredential GSS_S_DEFECTIVE_CREDENTIAL
ErrDefectiveToken GSS_S_DEFECTIVE_TOKEN
ErrFailure GSS_S_FAILURE
ErrNoContext GSS_S_NO_CONTEXT
ErrNoCred GSS_S_NO_CRED
ErrBadQop GSS_S_BAD_QOP
ErrUnauthorized GSS_S_UNAUTHORIZED
ErrUnavailable GSS_S_UNAVAILABLE
ErrDuplicateElement GSS_S_DUPLICATE_ELEMENT
ErrNameNotMn GSS_S_NAME_NOT_MN
InfoContinueNeeded GSS_S_CONTINUE_NEEDED
InfoDuplicateToken GSS_S_DUPLICATE_TOKEN
InfoOldToken GSS_S_OLD_TOKEN
InfoUnseqToken GSS_S_UNSEQ_TOKEN
InfoGapToken GSS_S_GAP_TOKEN

It is these values that should be returned by InfoStatus.Unwrap() and FatalStatus.Unwrap().

These error variables can be used by callers to inspect the error stack, eg:

  err := gssapi.SomeFunction()
  if err != nil {
    if errors.Is(err, gssapi.ErrContextExpired) {
        doLogin()
    } else {
        return err
    }
  }

The standard library errors.Is() makes use of the Unwrap() method on the InfoStatus and FatalStatus types to provide this functionality.

Informational codes are only returned from a small number of methods (Context.VerifyMIC() and Context.Unwrap()). Therefore except in these cases, a non-nil error return value can be considered fatal.

The nil value represents GSS_S_COMPLETE - ie. success.

Names

Name Types

The Go bindings should define the GssNameType type, and a set of constants of that type corresponding to the name types defined in RFC 2743 § 4:

Go bindings constants RFC 2743 reference
GSS_NT_HOSTBASED_SERVICE § 4.1: Host-Based Service Name Form
GSS_NT_USER_NAME § 4.2: User Name Form
GSS_NT_MACHINE_UID_NAME § 4.3: Machine UID Form
GSS_NT_STRING_UID_NAME § 4.4: String UID Form
GSS_NT_ANONYMOUS § 4.5: Anonymous Nametype
GSS_NO_OID § 4.6: GSS_C_NO_OID
GSS_NT_EXPORT_NAME § 4.7: Exported Name Object
GSS_NO_NAME § 4.8: GSS_C_NO_NAME

Additionally the following Kerberos specific name type constants should be defined:

  • GSS_KRB5_NT_PRINCIPAL_NAME (from RFC 1964 § 2.1.1)
  • GSS_KRB5_NT_ENTERPRISE_NAME (from RFC 8606 § 5)

The GssNameType type should implement the following interface:

type GssNameTypeInterface interface {
    // Oid() should return an OID object that corresponds to the GssNameType
    Oid() Oid

    // String() should return a printable version of the GssNameType
    // eg. string("GSS_NT_ANONYMOUS")
    String() string
}

as well as implementing the function:

    // NameFromOid returns the name type associated with an OID, or
    // an error if the OID is unknown
    func NameFromOid(oid Oid) (GssNameType, error)

Names

RFC 2743 defines a number of calls related to names. Ths Go bindings instead define an interface. Implementations should provide a type that implements the following GssName interface:

type GssName interface {
        Compare(other GssName) (bool, error)   // RFC 2743 § 2.4.3
        Display() (string, GssNameType, error) // RFC 2743 § 2.4.3
        Release() error                        // RFC 2743 § 2.4.6
        //InquireMechs() ([]Oid, error)        // RFC 2743 § 2.4.13
        //Canonicalize()                       // RFC 2743 § 2.4.14
        Export() ([]byte, error)               // RFC 2743 § 2.4.15
        Duplicate() (GssName, error)           // RFC 2743 § 2.4.16
}

.. as well implementing the following function

// (RFC 2743 § 2.4.5)
// ImportName returns a GSSAPI Internal Name corresponding to the
// specified name type and name type specific name.
ImportName(name string, nameType GssNameType) (*GssName, error)
Clone this wiki locally