Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Add GSS-TSIG server functionality #119

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
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
256 changes: 204 additions & 52 deletions gss/apcera.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,54 @@ import (
"github.com/openshift/gssapi"
)

func generate(lib *gssapi.Lib, ctx *gssapi.CtxId, msg []byte) ([]byte, error) {
message, err := lib.MakeBufferBytes(msg)
if err != nil {
return nil, err
}

defer func() {
err = multierror.Append(err, message.Release()).ErrorOrNil()
}()

token, err := ctx.GetMIC(gssapi.GSS_C_QOP_DEFAULT, message)
if err != nil {
return nil, err
}

defer func() {
err = multierror.Append(err, token.Release()).ErrorOrNil()
}()

return token.Bytes(), nil
}

func verify(lib *gssapi.Lib, ctx *gssapi.CtxId, stripped, mac []byte) error {
message, err := lib.MakeBufferBytes(stripped)
if err != nil {
return err
}

defer func() {
err = multierror.Append(err, message.Release()).ErrorOrNil()
}()

token, err := lib.MakeBufferBytes(mac)
if err != nil {
return err
}

defer func() {
err = multierror.Append(err, token.Release()).ErrorOrNil()
}()

if _, err = ctx.VerifyMIC(message, token); err != nil {
return err
}

return nil
}

// Client maps the TKEY name to the context that negotiated it as
// well as any other internal state.
type Client struct {
Expand All @@ -28,16 +76,14 @@ type Client struct {
}

// WithConfig sets the Kerberos configuration used.
func WithConfig(_ string) func(*Client) error {
return func(c *Client) error {
return errNotSupported
}
func WithConfig[T Client](_ string) Option[T] {
return unsupportedOption[T]
}

// NewClient performs any library initialization necessary.
// It returns a context handle for any further functions along with any error
// that occurred.
func NewClient(dnsClient *dns.Client, options ...func(*Client) error) (*Client, error) {
func NewClient(dnsClient *dns.Client, options ...Option[Client]) (*Client, error) {
client, err := util.CopyDNSClient(dnsClient)
if err != nil {
return nil, err
Expand All @@ -57,8 +103,10 @@ func NewClient(dnsClient *dns.Client, options ...func(*Client) error) (*Client,
logger: logr.Discard(),
}

if err := c.setOption(options...); err != nil {
return nil, multierror.Append(err, c.lib.Unload())
for _, option := range options {
if err := option(c); err != nil {
return nil, multierror.Append(err, c.lib.Unload())
}
}

return c, nil
Expand All @@ -72,54 +120,11 @@ func (c *Client) Close() error {
}

func (c *Client) generate(ctx *gssapi.CtxId, msg []byte) ([]byte, error) {
message, err := c.lib.MakeBufferBytes(msg)
if err != nil {
return nil, err
}

defer func() {
err = multierror.Append(err, message.Release()).ErrorOrNil()
}()

token, err := ctx.GetMIC(gssapi.GSS_C_QOP_DEFAULT, message)
if err != nil {
return nil, err
}

defer func() {
err = multierror.Append(err, token.Release()).ErrorOrNil()
}()

return token.Bytes(), nil
return generate(c.lib, ctx, msg)
}

func (c *Client) verify(ctx *gssapi.CtxId, stripped, mac []byte) error {
// Turn the TSIG-stripped message bytes into a *gssapi.Buffer
message, err := c.lib.MakeBufferBytes(stripped)
if err != nil {
return err
}

defer func() {
err = multierror.Append(err, message.Release()).ErrorOrNil()
}()

// Turn the TSIG MAC bytes into a *gssapi.Buffer
token, err := c.lib.MakeBufferBytes(mac)
if err != nil {
return err
}

defer func() {
err = multierror.Append(err, token.Release()).ErrorOrNil()
}()

// This is the actual verification bit
if _, err = ctx.VerifyMIC(message, token); err != nil {
return err
}

return nil
return verify(c.lib, ctx, stripped, mac)
}

// NegotiateContext exchanges RFC 2930 TKEY records with the indicated DNS
Expand Down Expand Up @@ -258,3 +263,150 @@ func (c *Client) DeleteContext(keyname string) error {

return nil
}

// Server maps the TKEY name to the context that negotiated it as
// well as any other internal state.
type Server struct {
m sync.RWMutex
lib *gssapi.Lib
ctx map[string]*gssapi.CtxId
logger logr.Logger
}

// NewServer performs any library initialization necessary.
// It returns a context handle for any further functions along with any error
// that occurred.
func NewServer(options ...Option[Server]) (*Server, error) {
lib, err := gssapi.Load(nil)
if err != nil {
return nil, err
}

s := &Server{
lib: lib,
ctx: make(map[string]*gssapi.CtxId),
logger: logr.Discard(),
}

for _, option := range options {
if err := option(s); err != nil {
return nil, multierror.Append(err, s.lib.Unload())
}
}

return s, nil
}

// Close deletes any active contexts and unloads any underlying libraries as
// necessary.
// It returns any error that occurred.
func (s *Server) Close() error {
return multierror.Append(s.close(true), s.lib.Unload()).ErrorOrNil()
}

func (s *Server) newContext() (*gssapi.CtxId, error) {
//nolint:nilnil
return nil, nil
}

//nolint:funlen
func (s *Server) update(ctx *gssapi.CtxId, input []byte) (*gssapi.CtxId, []byte, error) {
/*var cred *gssapi.CredId

// equivalent of GSSAPIStrictAcceptorCheck
if s.strict { //nolint:nestif
hostname, err := osHostname()
if err != nil {
return nil, "", false, err
}

buffer, err := s.lib.MakeBufferString("host@" + hostname)
if err != nil {
return nil, "", false, err
}

defer func() {
err = multierror.Append(err, buffer.Release()).ErrorOrNil()
}()

service, err := buffer.Name(s.lib.GSS_C_NT_HOSTBASED_SERVICE)
if err != nil {
return nil, "", false, err
}

defer func() {
err = multierror.Append(err, service.Release()).ErrorOrNil()
}()

oids, err := s.lib.MakeOIDSet(s.lib.GSS_MECH_KRB5)
if err != nil {
return nil, "", false, err
}

defer func() {
err = multierror.Append(err, oids.Release()).ErrorOrNil()
}()

cred, _, _, err = s.lib.AcquireCred(service, gssapi.GSS_C_INDEFINITE, oids, gssapi.GSS_C_ACCEPT)
if err != nil {
return nil, "", false, err
}

defer func() {
err = multierror.Append(err, cred.Release()).ErrorOrNil()
}()
} else {*/
cred := s.lib.GSS_C_NO_CREDENTIAL
//}

token, err := s.lib.MakeBufferBytes(input)
if err != nil {
return nil, nil, err
}

defer func() {
err = multierror.Append(err, token.Release()).ErrorOrNil()
}()

//nolint:dogsled
nctx, _, _, output, _, _, _, err := s.lib.AcceptSecContext(ctx, cred, token, s.lib.GSS_C_NO_CHANNEL_BINDINGS)
if err != nil && !s.lib.LastStatus.Major.ContinueNeeded() {
return nil, nil, err
}

defer func() {
err = multierror.Append(err, output.Release()).ErrorOrNil()
}()

return nctx, output.Bytes(), nil
}

func (s *Server) generate(ctx *gssapi.CtxId, msg []byte) ([]byte, error) {
return generate(s.lib, ctx, msg)
}

func (s *Server) verify(ctx *gssapi.CtxId, stripped, mac []byte) error {
return verify(s.lib, ctx, stripped, mac)
}

func (s *Server) established(ctx *gssapi.CtxId) (established bool, err error) {
if ctx != nil {
_, _, _, _, _, _, established, err = ctx.InquireContext()
}

return
}

func (s *Server) expired(ctx *gssapi.CtxId) (expired bool, err error) {
if ctx != nil {
var duration time.Duration
_, _, duration, _, _, _, _, err = ctx.InquireContext()
expired = duration <= 0
}

return
}

func (s *Server) delete(ctx *gssapi.CtxId) error {
return ctx.DeleteSecContext()
}
8 changes: 8 additions & 0 deletions gss/apcera_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,3 +29,11 @@ func TestNewClientWithConfig(t *testing.T) {
_, err := gss.NewClient(new(dns.Client), gss.WithConfig(""))
assert.NotNil(t, err)
}

func TestNewServer(t *testing.T) {
t.Parallel()

if err := testNewServer(t); err != nil {
t.Fatal(err)
}
}
21 changes: 8 additions & 13 deletions gss/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,12 +68,12 @@ func (c *Client) close() error {

c.m.RUnlock()

var errs error
var err *multierror.Error
for _, k := range keys {
errs = multierror.Append(errs, c.DeleteContext(k))
err = multierror.Append(err, c.DeleteContext(k))
}

return errs
return err.ErrorOrNil()
}

func (c *Client) setOption(options ...func(*Client) error) error {
Expand All @@ -87,20 +87,15 @@ func (c *Client) setOption(options ...func(*Client) error) error {
}

// SetConfig sets the Kerberos configuration used by c.
//
// Deprecated: FIXME.
func (c *Client) SetConfig(config string) error {
return c.setOption(WithConfig(config))
}

// WithLogger sets the logger used.
func WithLogger(logger logr.Logger) func(*Client) error {
return func(c *Client) error {
c.logger = logger.WithName("client")

return nil
}
}

// SetLogger sets the logger used by c.
//
// Deprecated: FIXME.
func (c *Client) SetLogger(logger logr.Logger) error {
return c.setOption(WithLogger(logger))
return c.setOption(WithLogger[Client](logger))
}
Loading