gssapi
import "github.com/TheManticoreProject/Manticore/network/kerberos/v5/gssapi"
Package gssapi implements the Kerberos V5 GSS-API mechanism (RFC 1964 / RFC 4121) context-establishment tokens: the GSS InitialContextToken framing, the 0x8003 authenticator checksum that carries channel bindings and service flags, building the KRB_AP_REQ token from a service ticket (InitSecContext), and verifying the KRB_AP_REP mutual-authentication reply.
This is the mechanism that plugs into SPNEGO (as the Kerberos mechToken) to authenticate SMB, RPC, and LDAP. Per-message tokens (MIC/Wrap, RFC 4121 §4.2) are a separate layer built on the SecContext produced here.
Index
- Constants
- Variables
- func DecryptDelegatedCredPart(cred *messages.KRBCred, subKey messages.EncryptionKey) (*messages.EncKrbCredPart, error)
- func ExtractDelegatedCred(auth *messages.Authenticator) (*messages.KRBCred, error)
- func ExtractDelegation(checksumValue []byte) ([]byte, error)
- func GSSChannelBindings(appData []byte) []byte
- func GSSChecksumValue(flags uint32, channelBindings []byte) []byte
- func GSSChecksumValueWithDelegation(flags uint32, channelBindings, krbCred []byte) []byte
- func UnwrapToken(data []byte) (tokID [2]byte, krbMessage []byte, err error)
- func WrapToken(tokID [2]byte, krbMessage []byte) ([]byte, error)
- type AcceptOptions
- type InitOptions
- type ReplayCache
- type SecContext
- func AcceptSecContext(token []byte, opts AcceptOptions) (outputToken []byte, ctx *SecContext, err error)
- func InitSecContext(opts InitOptions) ([]byte, *SecContext, error)
- func (ctx *SecContext) AcceptAPRep(token []byte) error
- func (ctx *SecContext) AcceptAPRepRaw(apRep []byte) error
- func (ctx *SecContext) Authenticator() *messages.Authenticator
- func (ctx *SecContext) ClientPrincipal() (messages.PrincipalName, string)
- func (ctx *SecContext) HasPAC() bool
- func (ctx *SecContext) MICTokenLen() int
- func (ctx *SecContext) MakeAPRep() ([]byte, error)
- func (ctx *SecContext) MakeMIC(data []byte) ([]byte, error)
- func (ctx *SecContext) PAC() (*pac.PAC, error)
- func (ctx *SecContext) PACBytes() []byte
- func (ctx *SecContext) ResetSendSeq(seq uint64)
- func (ctx *SecContext) Seal(data []byte) (sealed, token []byte, err error)
- func (ctx *SecContext) Unseal(sealed, token []byte) ([]byte, error)
- func (ctx *SecContext) Unwrap(token []byte) (data []byte, sealed bool, err error)
- func (ctx *SecContext) VerifyMIC(data, token []byte) error
- func (ctx *SecContext) Wrap(data []byte, seal bool) ([]byte, error)
- func (ctx *SecContext) WrapTokenLen(dataLen int) int
- type ServiceKey
Constants
GSS context-establishment flags (RFC 1964 §1.1.1), as they appear in the 0x8003 checksum Flags field.
const (
GSSDelegFlag = 1
GSSMutualFlag = 2
GSSReplayFlag = 4
GSSSequenceFlag = 8
GSSConfFlag = 16
GSSIntegFlag = 32
// GSSDCEStyleFlag (GSS_C_DCE_STYLE) selects the DCE RPC three-leg mutual
// authentication style. Windows RPC requires it for per-message protection
// (PKT_INTEGRITY / PKT_PRIVACY); with it the acceptor's AP-REP is followed by
// a third leg carrying the initiator's own AP-REP.
GSSDCEStyleFlag = 0x1000
)
ChecksumTypeGSSAPI is the Kerberos checksum type (0x8003) used for the GSS-API authenticator checksum.
const ChecksumTypeGSSAPI = 0x8003
DefaultClockSkew is the maximum difference the acceptor tolerates between the authenticator timestamp and its own clock (RFC 4120 §5.3.2 recommends five minutes).
const DefaultClockSkew = 5 * time.Minute
Variables
GSS-API token identifiers (RFC 1964 §1.1): the 2-byte TOK_ID prefixing the Kerberos message inside the InitialContextToken.
var (
TokIDAPReq = [2]byte{0x01, 0x00} // KRB_AP_REQ
TokIDAPRep = [2]byte{0x02, 0x00} // KRB_AP_REP
TokIDError = [2]byte{0x03, 0x00} // KRB_ERROR
)
Distinct receive-side rejection reasons for per-message tokens, analogous to the GSS-API major-status codes of RFC 2743 §1.2.1.1. VerifyMIC / Unwrap / Unseal return one of these (after the token’s integrity and direction have already been verified) so a caller can tell a genuine replay apart from a mere ordering anomaly.
var (
// ErrDuplicateToken reports a peer sequence number already seen inside the
// replay window: a replayed token (GSS_S_DUPLICATE_TOKEN).
ErrDuplicateToken = errors.New("gssapi: duplicate per-message token (replay)")
// ErrOldToken reports a peer sequence number that has fallen below the replay
// window, so its freshness can no longer be proven (GSS_S_OLD_TOKEN).
ErrOldToken = errors.New("gssapi: per-message token below replay window (too old)")
// ErrUnseqToken reports a fresh token that arrived out of order within the
// window (GSS_S_UNSEQ_TOKEN); only raised when sequence enforcement is on.
ErrUnseqToken = errors.New("gssapi: per-message token out of sequence")
// ErrGapToken reports a token that skipped ahead of the expected sequence
// number (GSS_S_GAP_TOKEN); only raised when sequence enforcement is on.
ErrGapToken = errors.New("gssapi: per-message token sequence gap")
)
MechOIDKerberos5 is the GSS-API mechanism OID for Kerberos V5 (1.2.840.113554.1.2.2), used as thisMech in the InitialContextToken and as a mechType in SPNEGO negotiation.
var MechOIDKerberos5 = asn1.ObjectIdentifier{1, 2, 840, 113554, 1, 2, 2}
func DecryptDelegatedCredPart
func DecryptDelegatedCredPart(cred *messages.KRBCred, subKey messages.EncryptionKey) (*messages.EncKrbCredPart, error)
DecryptDelegatedCredPart decrypts the enc-part of a forwarded KRB-CRED with the authenticator subkey (or session key) that keyed the AP-REQ, using KRB-CRED key usage 14, and returns the parsed EncKrbCredPart. That enc-part holds the forwarded ticket’s session key and lifetimes, which together with the ticket in cred.Tickets form a reusable TGT (a “.kirbi” for pass-the-ticket).
func ExtractDelegatedCred
func ExtractDelegatedCred(auth *messages.Authenticator) (*messages.KRBCred, error)
ExtractDelegatedCred parses the GSS 0x8003 checksum of a decrypted AP-REQ authenticator and returns the forwarded KRB-CRED it carries. The authenticator must already be decrypted (with the service’s long-term key, key usage 11). It returns (nil, nil) when the authenticator carries no GSS delegation.
func ExtractDelegation
func ExtractDelegation(checksumValue []byte) ([]byte, error)
ExtractDelegation parses a 0x8003 authenticator checksum value and returns the embedded KRB-CRED bytes (the forwarded credential). It returns (nil, nil) when the checksum is well-formed but carries no delegation (GSS_C_DELEG_FLAG clear), and an error when the checksum is malformed or truncated.
func GSSChannelBindings
func GSSChannelBindings(appData []byte) []byte
GSSChannelBindings marshals a gss_channel_bindings_struct (RFC 2744 §3.11) the way MIT/Heimdal krb5 do when feeding it to the RFC 4121 §4.1.1 authenticator checksum: little-endian initiator/acceptor address-type and address-length fields (all empty here, so four zero uint32s) followed by the application-data length and bytes. The returned buffer is what GSSChecksumValue MD5-hashes into the checksum Bnd field. appData carries the channel-binding token itself, e.g. “tls-server-end-point:” || certificate-hash for LDAP/GSSAPI over TLS.
func GSSChecksumValue
func GSSChecksumValue(flags uint32, channelBindings []byte) []byte
GSSChecksumValue builds the 0x8003 authenticator checksum value (RFC 1964 §1.1.1): Lgth (16, little-endian) | Bnd | Flags (little-endian). Bnd is the MD5 of the channel bindings, or 16 zero bytes for GSS_C_NO_BINDINGS (nil).
func GSSChecksumValueWithDelegation
func GSSChecksumValueWithDelegation(flags uint32, channelBindings, krbCred []byte) []byte
GSSChecksumValueWithDelegation builds a 0x8003 authenticator checksum value that carries a forwarded credential (RFC 4121 Section 4.1.1): the fixed Lgth | Bnd | Flags header (with GSS_C_DELEG_FLAG forced on) followed by DlgOpt (1) | Dlgth | Deleg, where Deleg is a marshaled KRB-CRED. It is the inverse of ExtractDelegation and lets the extraction path be exercised without a live KDC.
func UnwrapToken
func UnwrapToken(data []byte) (tokID [2]byte, krbMessage []byte, err error)
UnwrapToken parses a GSS-API InitialContextToken, verifies the kerberos5 mech OID, and returns the TOK_ID and the enclosed Kerberos message bytes.
func WrapToken
func WrapToken(tokID [2]byte, krbMessage []byte) ([]byte, error)
WrapToken wraps a Kerberos message in the GSS-API InitialContextToken framing (RFC 1964 §1.1): [APPLICATION 0] { kerberos5-OID, TOK_ID | krbMessage }.
type AcceptOptions
AcceptOptions configures AcceptSecContext.
type AcceptOptions struct {
// Keytab supplies candidate service long-term keys (the preferred source):
// every key whose enctype matches the ticket enc-part is tried at key usage 2.
Keytab *keytab.Keytab
// Keys supplies explicit candidate service keys when no keytab is available.
// They are tried after the keytab keys.
Keys []ServiceKey
// ChannelBindings, when non-nil, are the acceptor's channel bindings: the
// authenticator's 0x8003 Bnd field must equal MD5(ChannelBindings) or the
// AP-REQ is rejected (GSS_C_BAD_BINDINGS). When nil the initiator's channel
// bindings are not verified (RFC 4121 §4.1.1 permits the acceptor to ignore
// them).
ChannelBindings []byte
// ClockSkew is the maximum tolerated difference between the authenticator
// timestamp and the acceptor clock. Zero selects DefaultClockSkew.
ClockSkew time.Duration
// ReplayCache detects replayed authenticators. When nil a fresh single-use
// cache is created, giving no cross-call replay protection; callers accepting
// more than one context should pass a shared cache.
ReplayCache *ReplayCache
// MintSubkey makes the acceptor generate its own sub-session key, return it in
// the AP-REP (setting the AcceptorSubkey per-message flag), and key per-message
// tokens with it. Requires mutual authentication so the initiator learns the
// key. When false the authenticator subkey (if any) or the ticket session key
// keys the per-message tokens.
MintSubkey bool
// Now overrides the acceptor's notion of the current time (for testing). Zero
// uses time.Now().
Now time.Time
// DisableReplayDetection turns off the per-message receive-side replay window
// (see InitOptions); replay detection is on by default.
DisableReplayDetection bool
// EnforceSequence additionally rejects per-message tokens delivered out of
// order (see InitOptions); off by default.
EnforceSequence bool
}
type InitOptions
InitOptions configures InitSecContext.
type InitOptions struct {
// TicketRaw is the raw APPLICATION[1] service ticket (from GetTGS).
TicketRaw []byte
// SessionKey / SessionEType are that ticket's session key.
SessionKey []byte
SessionEType int
// ClientName / ClientRealm identify the initiator (the ticket's client).
ClientName messages.PrincipalName
ClientRealm string
// Flags are the GSS context-establishment flags for the 0x8003 checksum.
Flags uint32
// ChannelBindings is hashed into the checksum Bnd field (nil = no bindings).
ChannelBindings []byte
// Mutual requests mutual authentication (sets AP-options mutual-required and
// the GSS mutual flag), so the acceptor returns an AP-REP.
Mutual bool
// SubKey optionally supplies a client subkey (used to key per-message tokens);
// if set, SubKeyEType must be set too.
SubKey []byte
SubKeyEType int
// ZeroSeqNumber sets the authenticator sequence number to 0 instead of a
// random value. DCE/RPC requires it (the acceptor's per-message receive
// sequence is seeded from it, and the per-PDU counter starts at 0).
ZeroSeqNumber bool
// DisableReplayDetection turns off receive-side replay rejection of incoming
// per-message tokens. Replay detection is ON by default: a sliding 64-token
// window (RFC 2743 §1.2.1.1 / RFC 4121 §4.2.6) rejects duplicated and
// below-window tokens with ErrDuplicateToken / ErrOldToken.
DisableReplayDetection bool
// EnforceSequence additionally rejects per-message tokens delivered out of
// order or skipping ahead (ErrUnseqToken / ErrGapToken). It is OFF by default:
// the DCE/RPC three-leg and other AD peers do not guarantee that per-message
// tokens arrive in strict GSS sequence, so strict enforcement would break
// interoperability, whereas replay detection alone still delivers the
// anti-replay guarantee these consumers rely on.
EnforceSequence bool
}
type ReplayCache
ReplayCache is a minimal in-memory authenticator replay cache (RFC 4120 §3.2.3): it remembers the (client, ctime, cusec) tuple of every AP-REQ authenticator seen within the clock-skew window and rejects a repeat. It is safe for concurrent use; a single cache should be shared across all AcceptSecContext calls for one service so replays are caught across contexts.
type ReplayCache struct {
// contains filtered or unexported fields
}
func NewReplayCache
func NewReplayCache() *ReplayCache
NewReplayCache returns an empty replay cache ready for use.
type SecContext
SecContext holds the state a context initiator needs to verify the AP-REP and to produce/verify per-message tokens.
type SecContext struct {
// SessionKey / SessionEType are the service-ticket session key.
SessionKey []byte
SessionEType int
// SubKey is the client-chosen subkey placed in the authenticator (optional).
SubKey []byte
SubKeyEType int
// SeqNumber is the initial sequence number sent in the authenticator.
SeqNumber int
// contains filtered or unexported fields
}
func AcceptSecContext
func AcceptSecContext(token []byte, opts AcceptOptions) (outputToken []byte, ctx *SecContext, err error)
AcceptSecContext consumes an initiator’s KRB_AP_REQ GSS token and establishes the acceptor (service) side of the context. It parses the InitialContextToken wrapper and the AP-REQ, decrypts the ticket enc-part with a service long-term key (key usage 2, trying keytab and explicit keys), decrypts and validates the authenticator with the ticket session key (key usage 11), enforces client identity, clock skew, replay and — when requested — channel bindings, and adopts a per-message base key. When the AP-REQ set the mutual-required option it returns the KRB_AP_REP GSS token (echoing ctime/cusec, key usage 12); the output token is nil otherwise.
The returned SecContext exposes the authenticated client identity (ClientPrincipal), the extracted PAC (PAC), the negotiated session key (SessionKey / SessionEType) and the validated authenticator (Authenticator), and drives the per-message MIC/Wrap layer as the acceptor.
func InitSecContext
func InitSecContext(opts InitOptions) ([]byte, *SecContext, error)
InitSecContext builds the initiator’s KRB_AP_REQ GSS token from a service ticket, returning the wrapped token and the SecContext for AP-REP/per-message handling. The authenticator carries the 0x8003 checksum and is encrypted with the ticket session key at key usage 11 (AP-REQ authenticator).
func (*SecContext) AcceptAPRep
func (ctx *SecContext) AcceptAPRep(token []byte) error
AcceptAPRep verifies a KRB_AP_REP GSS token returned by the acceptor for mutual authentication: it unwraps the token, decrypts the EncAPRepPart with the ticket session key (key usage 12), and checks that the echoed ctime/cusec match those sent in the initiator’s authenticator. On success it adopts an acceptor subkey into the context if one was supplied.
func (*SecContext) AcceptAPRepRaw
func (ctx *SecContext) AcceptAPRepRaw(apRep []byte) error
AcceptAPRepRaw verifies a bare KRB_AP_REP (APPLICATION[15]) that is not wrapped in a GSS InitialContextToken. DCE RPC (GSS_C_DCE_STYLE) carries the acceptor’s AP-REP this way, since the GSS framing OID appears only on the first token.
func (*SecContext) Authenticator
func (ctx *SecContext) Authenticator() *messages.Authenticator
Authenticator returns the decrypted, validated AP-REQ authenticator (acceptor side), or nil on an initiator context. It lets a caller inspect the 0x8003 checksum, e.g. via ExtractDelegatedCred for an unconstrained-delegation KRB-CRED.
func (*SecContext) ClientPrincipal
func (ctx *SecContext) ClientPrincipal() (messages.PrincipalName, string)
ClientPrincipal returns the authenticated client’s principal name and realm, taken from the validated AP-REQ authenticator (acceptor side). It is empty on an initiator context.
func (*SecContext) HasPAC
func (ctx *SecContext) HasPAC() bool
HasPAC reports whether the decrypted ticket carried a PAC (acceptor side).
func (*SecContext) MICTokenLen
func (ctx *SecContext) MICTokenLen() int
MICTokenLen returns the length of a MIC token this context emits: for RC4-HMAC the fixed RFC 4757 token length, otherwise the 16-byte CFX header plus the base key’s etype checksum.
func (*SecContext) MakeAPRep
func (ctx *SecContext) MakeAPRep() ([]byte, error)
MakeAPRep builds the initiator’s own KRB_AP_REP as a bare APPLICATION[15] message (no GSS wrapper), for the third leg of the DCE-style mutual-auth handshake (GSS_C_DCE_STYLE). Following the Windows/MIT behaviour, it uses a fresh timestamp (not the echoed authenticator time), carries the sequence number the acceptor sent in its AP-REP, has no subkey, and is encrypted with the ticket session key (key usage 12).
func (*SecContext) MakeMIC
func (ctx *SecContext) MakeMIC(data []byte) ([]byte, error)
MakeMIC produces a MIC token over data as the context initiator. For RC4-HMAC it is the RFC 4757 §7.3 token; otherwise the RFC 4121 §4.2.6.1 CFX token (checksum(data | header) keyed with the initiator-sign usage).
func (*SecContext) PAC
func (ctx *SecContext) PAC() (*pac.PAC, error)
PAC parses and returns the PAC extracted from the decrypted ticket (acceptor side). It returns (nil, nil) when the ticket carried no PAC.
func (*SecContext) PACBytes
func (ctx *SecContext) PACBytes() []byte
PACBytes returns the raw AD-WIN2K-PAC bytes extracted from the decrypted ticket (acceptor side), or nil when the ticket carried no PAC.
func (*SecContext) ResetSendSeq
func (ctx *SecContext) ResetSendSeq(seq uint64)
ResetSendSeq overrides the running per-message send sequence. DCE/RPC uses a zero-based per-PDU counter for its GSS tokens rather than the AP-REQ authenticator sequence.
func (*SecContext) Seal
func (ctx *SecContext) Seal(data []byte) (sealed, token []byte, err error)
Seal produces a DCE-style GSS Wrap token that seals data as the context initiator, returning the encrypted stub (sealed in place) and the Wrap token for the auth_value. It is used by DCE/RPC PKT_PRIVACY; the RPC header and sec_trailer are not covered by the token. RC4-HMAC uses the RFC 4757 token, every other enctype the RFC 4121 CFX Wrap token.
func (*SecContext) Unseal
func (ctx *SecContext) Unseal(sealed, token []byte) ([]byte, error)
Unseal decrypts and verifies a DCE-style GSS Wrap token received from the acceptor, returning the recovered plaintext (including any RPC auth pad the caller strips). RC4-HMAC uses the RFC 4757 token, every other enctype the RFC 4121 CFX Wrap token.
func (*SecContext) Unwrap
func (ctx *SecContext) Unwrap(token []byte) (data []byte, sealed bool, err error)
Unwrap decodes a Wrap token received from the acceptor, returning the plaintext data and whether it was sealed (confidential). For RC4-HMAC it parses the RFC 4757 §7.4 token (tok_id 02 01), otherwise the RFC 4121 CFX token.
func (*SecContext) VerifyMIC
func (ctx *SecContext) VerifyMIC(data, token []byte) error
VerifyMIC verifies a MIC token received from the acceptor over data.
func (*SecContext) Wrap
func (ctx *SecContext) Wrap(data []byte, seal bool) ([]byte, error)
Wrap produces a Wrap token over data as the context initiator. For RC4-HMAC it is the contiguous RFC 4757 §7.4 token (tok_id 02 01); otherwise the RFC 4121 §4.2.6.2 CFX token (tok_id 05 04). With seal=true the token provides confidentiality+integrity; otherwise integrity only. For the CFX path EC and RRC are 0 (no filler, no rotation).
func (*SecContext) WrapTokenLen
func (ctx *SecContext) WrapTokenLen(dataLen int) int
WrapTokenLen returns the auth_value length of a DCE-style Wrap token this context emits when sealing a stub of dataLen bytes. For RC4-HMAC it is the fixed RFC 4757 token (the sealed data is expanded in the stub, not the token, so dataLen is irrelevant). For an AES CFX Wrap token (RFC 4121 §4.2), the sealed data replaces the stub in place at the same length and the token carries the 16-byte header, the encrypted copy of that header, the checksum, the confounder and EC filler octets — so the length is 48 + EC + checksum, where EC pads the plaintext to the cipher block size. A negative dataLen requests the maximum possible length (largest EC), used to size the auth trailer before the exact stub length is known.
type ServiceKey
ServiceKey is one candidate long-term key the acceptor can try when decrypting the ticket enc-part (key usage 2). It is the explicit-key alternative to a keytab.
type ServiceKey struct {
// EType is the key's Kerberos encryption type (see iana.EType*).
EType int
// Key is the raw long-term key bytes.
Key []byte
}