go-gssapi is a pure Golang implementation of the GSS-API version 2 specification (RFC 2743). It uses the pure Golang Kerberos implementation to obtain Kerberos tickets and perform cryptographic operations.
Documentation at https://pkg.go.dev/github.com/golang-auth/go-gssapi/v2
The current version implements the Kerberos V5 authentication mechanism (RFC 4121) We plan to support SPNEGO in a future release.
The following features are currently available:
- Initiator (client) and Acceptor (server)
- Mutual authentication
- Message Integrity and Confidentiality
- GSS MIC and Wrap tokens
- Basic support for detecting out-of-sequence and duplicate messages
- Channel binding (for initators)
The following functionality is currently not available:
- GSS-API v1 message tokens (RFC1964)
- Delegation
- Acceptor channel binding
Currently Unix platforms. Support for the Windows SSPI will be added in future.
go-gssapi is not yet considered to be production ready, and the interface is not yet considered stable. We started at v2 to support the new Go versioning standrd. A v2.0.0 tag will be created after which we will follow normal semantic versioning conventions.
The Kerberos implementiaton behing the GSS-API functionality is hidden form the caller. The name of the krb5.conf file, credential cache (by an Initiator) and keytab file (by an Acceptor) are identified from the environment or using default paths if not specified in the environment :
Configuration item | Environment variable | Default |
---|---|---|
Kerberos client configuartion file | KRB5_CONFIG |
/etc/krb5.conf |
Credential cache file | KRB5CCNAME |
/etc/krb5cc_%{UID} |
Keytab file | KRB5_KTNAME |
/var/kerberos/krb5/user/%{UID}/client.keytab |
.. where %{UID}
is the current username as returned by id -u
💧 A future releaes will choose defaults based on the platform we're running on, and may provide more complete control of the gokrb client. Please file a new Github issue if you have a use-case.
Import the base go-gssapi
package where you will use GSS-API, and the mechanism specific package somewhere (usually the main package) to register the mechanism :
package myclient
import "github.com/golang-auth/go-gssapi/v2"
package main
// register the Kerberos GSS-API mechsnism
import _ "github.com/golang-auth/go-gssapi/v2/krb5"
We will maintain major versions of the library in separate Git branches. To use the latest version in a branch, use go get
with the branch name, eg :
go get github.com/golang-auth/go-gssapi/v2
This will result in a concrete version being added to go.mod
:
module test
go 1.15
require github.com/golang-auth/go-gssapi/v2 v2.2.2 // indirect
Obtain an instance (context) of a mechanism-specif implementation:
ctx := gssapi.NewMech("kerberos_v5)
then configure the context as an Initiator (client):
service := "ldap/ldap.example.com"
var flags gssapi.ContextFlag = gssapi.ContextFlagInteg |
gssapi.ContextFlagConf |
gssapi.ContextFlagReplay |
gssapi.ContextFlagSequence |
gssapi.ContextFlagMutual
err := ctx.Initiate(service, flags, nil)
or Acceptor (server):
err := ctx.Accept("")
Note: go-gssapi uses the Kebreros V5 principal name format (primary/instance), not the NT_HOSTBASED_SERVICE format (service@host) used by other GSS-API implementations
Once the context is initialized as an Initiator or Acceptor, the client and server enter a loop until IsEstablished
returns true. Each pass through the loop, the Continue
method is called on the context. Continue
may return an opaque token that should be sent to the peer. The peer passes the token to the Continue
method and the process is repeated.
var inToken, outToken []byte
for !ctx.IsEstablished() {
outToken, err = ctx.Continue(inToken)
if err != nil {
break
}
if len(outToken) > 0 {
if sendErr := sendToken(conn, outToken); sendErr != nil {
err = sendErr
break
}
}
if !ctx.IsEstablished() {
inToken, err = recvToken(conn)
if err != nil {
break
}
}
}
if err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
var inToken, outToken []byte
for !ctx.IsEstablished() {
inToken, err = recvToken(conn)
if err != nil {
fmt.Fprintln(os.Stderr, err)
break
}
outToken, err = ctx.Continue(inToken)
if len(outToken) > 0 {
if sendErr := sendToken(conn, outToken); sendErr != nil {
err = sendErr
break
}
}
}
if err != nil {
fmt.Fprintln(os.Stderr, err)
return
}
GSS-API defines two message types :
- Wrap messages encapsulate a message payload plus either a signature, or with the payload encrypted (if confidentially is requested)
- Message Integrity Code (MIC) messages convey a signature of a payload but do not encapsulate the actual payload
The context Wrap
and MakeSignature
methods are used to generate serialized GSS-API message tokens for a given payload. MIC tokens can be communicated separately from the payload, for example as a part of an acknowledgement or via a separate channel from the data.
The receiver of a message passes the token to the context Unwrap
and VerifySignature
methods. Unwrap verifies the message payload signature or decrypts the message payload (if confidentially is in use). VerifySignature verifies the signature of a payload passed to the method.
msg := "Hello go-gssapi!"
seal := true // ask for confidentiality
// Wrap the message
outToken, err = ctx.Wrap([]byte(msg), seal)
if err != nil {
return err
}
// send it to the GSS-API peer
if err = sendToken(conn, outToken); err != nil {
retirm err
}
inToken, err := recvToken(conn)
if err != nil {
return err
}
msg, isSealed, err := ctx.Unwrap(inToken)
if err != nil {
return err
}
protStr := "signed"
if isSealed {
protStr = "sealed"
}
fmt.Printf(`Received %s message: "%s"`+"\n", protStr, msg)
Examples implemented in C (for use with the MIT or Heimdal GSS-API library) and Go are included in the examples
directory. The C samples are provided to demonstrate go-gssapi interoperatbility.