Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 17 additions & 1 deletion internal/common/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

//go:build windows
// +build windows

package common
Expand Down Expand Up @@ -41,19 +42,34 @@ func BuildAuthIdentity(domain, username, password string) (*sspi.SEC_WINNT_AUTH_
}

func UpdateContext(c *sspi.Context, dst, src []byte, targetName *uint16) (authCompleted bool, n int, err error) {
var inBuf, outBuf [1]sspi.SecBuffer
return UpdateContextWithChannelBindings(c, dst, src, nil, targetName)
}

// UpdateContextWithChannelBindings performs SSPI context update with optional channel binding tokens.
func UpdateContextWithChannelBindings(c *sspi.Context, dst, src, channelBindings []byte, targetName *uint16) (authCompleted bool, n int, err error) {
var inBuf [2]sspi.SecBuffer

inBuf[0].Set(sspi.SECBUFFER_TOKEN, src)
inBufs := &sspi.SecBufferDesc{
Version: sspi.SECBUFFER_VERSION,
BuffersCount: 1,
Buffers: &inBuf[0],
}

if len(channelBindings) > 0 {
// With channel bindings: TOKEN buffer + CHANNEL_BINDINGS buffer
inBuf[1].Set(sspi.SECBUFFER_CHANNEL_BINDINGS, channelBindings)
inBufs.BuffersCount = 2
}

var outBuf [1]sspi.SecBuffer
outBuf[0].Set(sspi.SECBUFFER_TOKEN, dst)
outBufs := &sspi.SecBufferDesc{
Version: sspi.SECBUFFER_VERSION,
BuffersCount: 1,
Buffers: &outBuf[0],
}

ret := c.Update(targetName, outBufs, inBufs)
switch ret {
case sspi.SEC_E_OK:
Expand Down
21 changes: 15 additions & 6 deletions kerberos/kerberos.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

//go:build windows
// +build windows

// Package kerberos provides access to the Microsoft Kerberos SSP Package.
//
package kerberos

import (
Expand Down Expand Up @@ -74,8 +74,9 @@ func AcquireServerCredentials(principalName string) (*sspi.Credentials, error) {

// ClientContext is used by the client to manage all steps of Kerberos negotiation.
type ClientContext struct {
sctxt *sspi.Context
targetName *uint16
sctxt *sspi.Context
targetName *uint16
channelBindings []byte
}

// NewClientContext creates a new client context. It uses client
Expand All @@ -97,6 +98,13 @@ func NewClientContext(cred *sspi.Credentials, targetName string) (*ClientContext
// (for example sspi.ISC_REQ_CONFIDENTIALITY|sspi.ISC_REQ_REPLAY_DETECT)
// NewClientContextWithFlags returns a new token to be sent to the server.
func NewClientContextWithFlags(cred *sspi.Credentials, targetName string, flags uint32) (*ClientContext, bool, []byte, error) {
return NewClientContextWithChannelBindings(cred, targetName, flags, nil)
}

// NewClientContextWithChannelBindings creates a new client context with channel binding support.
// channelBindings should contain the channel binding token (e.g., TLS channel binding data).
// https://learn.microsoft.com/en-us/windows/win32/secauthn/epa-support-in-service
func NewClientContextWithChannelBindings(cred *sspi.Credentials, targetName string, flags uint32, channelBindings []byte) (*ClientContext, bool, []byte, error) {
var tname *uint16
if len(targetName) > 0 {
p, err := syscall.UTF16FromString(targetName)
Expand All @@ -110,7 +118,7 @@ func NewClientContextWithFlags(cred *sspi.Credentials, targetName string, flags
otoken := make([]byte, PackageInfo.MaxToken)
c := sspi.NewClientContext(cred, flags)

authCompleted, n, err := common.UpdateContext(c, otoken, nil, tname)
authCompleted, n, err := common.UpdateContextWithChannelBindings(c, otoken, nil, channelBindings, tname)
if err != nil {
return nil, false, nil, err
}
Expand All @@ -119,7 +127,7 @@ func NewClientContextWithFlags(cred *sspi.Credentials, targetName string, flags
return nil, false, nil, errors.New("kerberos token should not be empty")
}
otoken = otoken[:n]
return &ClientContext{sctxt: c, targetName: tname}, authCompleted, otoken, nil
return &ClientContext{sctxt: c, targetName: tname, channelBindings: channelBindings}, authCompleted, otoken, nil
}

// Release free up resources associated with client context c.
Expand All @@ -141,7 +149,8 @@ func (c *ClientContext) Expiry() time.Time {
// sent to the server.
func (c *ClientContext) Update(token []byte) (bool, []byte, error) {
otoken := make([]byte, PackageInfo.MaxToken)
authDone, n, err := common.UpdateContext(c.sctxt, otoken, token, c.targetName)

authDone, n, err := common.UpdateContextWithChannelBindings(c.sctxt, otoken, token, c.channelBindings, c.targetName)
if err != nil {
return false, nil, err
}
Expand Down
1 change: 1 addition & 0 deletions kerberos/kerberos_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

//go:build windows
// +build windows

package kerberos_test
Expand Down
28 changes: 15 additions & 13 deletions syscall.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

//go:build windows
// +build windows

package sspi
Expand Down Expand Up @@ -96,19 +97,20 @@ type CredHandle struct {
const (
SECURITY_NATIVE_DREP = 16

SECBUFFER_DATA = 1
SECBUFFER_TOKEN = 2
SECBUFFER_PKG_PARAMS = 3
SECBUFFER_MISSING = 4
SECBUFFER_EXTRA = 5
SECBUFFER_STREAM_TRAILER = 6
SECBUFFER_STREAM_HEADER = 7
SECBUFFER_PADDING = 9
SECBUFFER_STREAM = 10
SECBUFFER_READONLY = 0x80000000
SECBUFFER_ATTRMASK = 0xf0000000
SECBUFFER_VERSION = 0
SECBUFFER_EMPTY = 0
SECBUFFER_DATA = 1
SECBUFFER_TOKEN = 2
SECBUFFER_PKG_PARAMS = 3
SECBUFFER_MISSING = 4
SECBUFFER_EXTRA = 5
SECBUFFER_STREAM_TRAILER = 6
SECBUFFER_STREAM_HEADER = 7
SECBUFFER_PADDING = 9
SECBUFFER_STREAM = 10
SECBUFFER_CHANNEL_BINDINGS = 14
SECBUFFER_READONLY = 0x80000000
SECBUFFER_ATTRMASK = 0xf0000000
SECBUFFER_VERSION = 0
SECBUFFER_EMPTY = 0

ISC_REQ_DELEGATE = 1
ISC_REQ_MUTUAL_AUTH = 2
Expand Down