-
Notifications
You must be signed in to change notification settings - Fork 6
Golang GSSAPI bindings specification
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.
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.
The Go GSSAPI bindings make use of the int32
type.
The native Go bool
type is specified for boolean values.
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.
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.
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.
The Go bindings represent OID sets as slices or arrays of Oid
types, ([]Oid
).
TBD..
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.
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)
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)