Adding dep files and dependencies.

This commit is contained in:
Renan DelValle 2018-09-30 18:02:42 -07:00
parent 45f9efa578
commit b341c0a0e4
No known key found for this signature in database
GPG key ID: 3895800E03F17676
539 changed files with 313111 additions and 0 deletions

View file

@ -0,0 +1,361 @@
package sasl
import (
"errors"
"fmt"
"sync/atomic"
"github.com/gogo/protobuf/proto"
log "github.com/golang/glog"
"github.com/mesos/mesos-go/api/v0/auth"
"github.com/mesos/mesos-go/api/v0/auth/callback"
"github.com/mesos/mesos-go/api/v0/auth/sasl/mech"
mesos "github.com/mesos/mesos-go/api/v0/mesosproto"
"github.com/mesos/mesos-go/api/v0/mesosutil/process"
"github.com/mesos/mesos-go/api/v0/messenger"
"github.com/mesos/mesos-go/api/v0/upid"
"golang.org/x/net/context"
)
var (
UnexpectedAuthenticationMechanisms = errors.New("Unexpected authentication 'mechanisms' received")
UnexpectedAuthenticationStep = errors.New("Unexpected authentication 'step' received")
UnexpectedAuthenticationCompleted = errors.New("Unexpected authentication 'completed' received")
UnexpectedAuthenticatorPid = errors.New("Unexpected authentator pid") // authenticator pid changed mid-process
UnsupportedMechanism = errors.New("failed to identify a compatible mechanism")
)
type statusType int32
const (
statusReady statusType = iota
statusStarting
statusStepping
_statusTerminal // meta status, should never be assigned: all status types following are "terminal"
statusCompleted
statusFailed
statusError
statusDiscarded
// this login provider name is automatically registered with the auth package; see init()
ProviderName = "SASL"
)
type authenticateeProcess struct {
transport messenger.Messenger
client upid.UPID
status statusType
done chan struct{}
err error
mech mech.Interface
stepFn mech.StepFunc
from *upid.UPID
handler callback.Handler
}
type authenticateeConfig struct {
client upid.UPID // pid of the client we're attempting to authenticate
handler callback.Handler
transport messenger.Messenger // mesos communications transport
}
type transportFactory interface {
makeTransport() messenger.Messenger
}
type transportFactoryFunc func() messenger.Messenger
func (f transportFactoryFunc) makeTransport() messenger.Messenger {
return f()
}
func init() {
factory := func(ctx context.Context) transportFactoryFunc {
return transportFactoryFunc(func() messenger.Messenger {
parent := auth.ParentUPID(ctx)
if parent == nil {
log.Fatal("expected to have a parent UPID in context")
}
process := process.New("sasl_authenticatee")
tpid := upid.UPID{
ID: process.Label(),
Host: parent.Host,
Port: BindingPortFrom(ctx),
}
return messenger.NewHttpWithBindingAddress(tpid, BindingAddressFrom(ctx))
})
}
delegate := auth.AuthenticateeFunc(func(ctx context.Context, handler callback.Handler) error {
if impl, err := makeAuthenticatee(handler, factory(ctx)); err != nil {
return err
} else {
return impl.Authenticate(ctx, handler)
}
})
if err := auth.RegisterAuthenticateeProvider(ProviderName, delegate); err != nil {
log.Error(err)
}
}
func (s *statusType) get() statusType {
return statusType(atomic.LoadInt32((*int32)(s)))
}
func (s *statusType) swap(old, new statusType) bool {
return old != new && atomic.CompareAndSwapInt32((*int32)(s), int32(old), int32(new))
}
// build a new authenticatee implementation using the given callbacks and a new transport instance
func makeAuthenticatee(handler callback.Handler, factory transportFactory) (auth.Authenticatee, error) {
ip := callback.NewInterprocess()
if err := handler.Handle(ip); err != nil {
return nil, err
}
config := &authenticateeConfig{
client: ip.Client(),
handler: handler,
transport: factory.makeTransport(),
}
return auth.AuthenticateeFunc(func(ctx context.Context, handler callback.Handler) error {
ctx, auth := newAuthenticatee(ctx, config)
auth.authenticate(ctx, ip.Server())
select {
case <-ctx.Done():
return auth.discard(ctx)
case <-auth.done:
return auth.err
}
}), nil
}
// Terminate the authentication process upon context cancellation;
// only to be called if/when ctx.Done() has been signalled.
func (self *authenticateeProcess) discard(ctx context.Context) error {
err := ctx.Err()
status := statusFrom(ctx)
for ; status < _statusTerminal; status = (&self.status).get() {
if self.terminate(status, statusDiscarded, err) {
break
}
}
return err
}
func newAuthenticatee(ctx context.Context, config *authenticateeConfig) (context.Context, *authenticateeProcess) {
initialStatus := statusReady
proc := &authenticateeProcess{
transport: config.transport,
client: config.client,
handler: config.handler,
status: initialStatus,
done: make(chan struct{}),
}
ctx = withStatus(ctx, initialStatus)
err := proc.installHandlers(ctx)
if err == nil {
err = proc.startTransport()
}
if err != nil {
proc.terminate(initialStatus, statusError, err)
}
return ctx, proc
}
func (self *authenticateeProcess) startTransport() error {
if err := self.transport.Start(); err != nil {
return err
} else {
go func() {
// stop the authentication transport upon termination of the
// authenticator process
select {
case <-self.done:
log.V(2).Infof("stopping authenticator transport: %v", self.transport.UPID())
self.transport.Stop()
}
}()
}
return nil
}
// returns true when handlers are installed without error, otherwise terminates the
// authentication process.
func (self *authenticateeProcess) installHandlers(ctx context.Context) error {
type handlerFn func(ctx context.Context, from *upid.UPID, pbMsg proto.Message)
withContext := func(f handlerFn) messenger.MessageHandler {
return func(from *upid.UPID, m proto.Message) {
status := (&self.status).get()
if self.from != nil && !self.from.Equal(from) {
self.terminate(status, statusError, UnexpectedAuthenticatorPid)
} else {
f(withStatus(ctx, status), from, m)
}
}
}
// Anticipate mechanisms and steps from the server
handlers := []struct {
f handlerFn
m proto.Message
}{
{self.mechanisms, &mesos.AuthenticationMechanismsMessage{}},
{self.step, &mesos.AuthenticationStepMessage{}},
{self.completed, &mesos.AuthenticationCompletedMessage{}},
{self.failed, &mesos.AuthenticationFailedMessage{}},
{self.errored, &mesos.AuthenticationErrorMessage{}},
}
for _, h := range handlers {
if err := self.transport.Install(withContext(h.f), h.m); err != nil {
return err
}
}
return nil
}
// return true if the authentication status was updated (if true, self.done will have been closed)
func (self *authenticateeProcess) terminate(old, new statusType, err error) bool {
if (&self.status).swap(old, new) {
self.err = err
if self.mech != nil {
self.mech.Discard()
}
close(self.done)
return true
}
return false
}
func (self *authenticateeProcess) authenticate(ctx context.Context, pid upid.UPID) {
status := statusFrom(ctx)
if status != statusReady {
return
}
message := &mesos.AuthenticateMessage{
Pid: proto.String(self.client.String()),
}
if err := self.transport.Send(ctx, &pid, message); err != nil {
self.terminate(status, statusError, err)
} else {
(&self.status).swap(status, statusStarting)
}
}
func (self *authenticateeProcess) mechanisms(ctx context.Context, from *upid.UPID, pbMsg proto.Message) {
status := statusFrom(ctx)
if status != statusStarting {
self.terminate(status, statusError, UnexpectedAuthenticationMechanisms)
return
}
msg, ok := pbMsg.(*mesos.AuthenticationMechanismsMessage)
if !ok {
self.terminate(status, statusError, fmt.Errorf("Expected AuthenticationMechanismsMessage, not %T", pbMsg))
return
}
mechanisms := msg.GetMechanisms()
log.Infof("Received SASL authentication mechanisms: %v", mechanisms)
selectedMech, factory := mech.SelectSupported(mechanisms)
if selectedMech == "" {
self.terminate(status, statusError, UnsupportedMechanism)
return
}
if m, f, err := factory(self.handler); err != nil {
self.terminate(status, statusError, err)
return
} else {
self.mech = m
self.stepFn = f
self.from = from
}
// execute initialization step...
nextf, data, err := self.stepFn(self.mech, nil)
if err != nil {
self.terminate(status, statusError, err)
return
} else {
self.stepFn = nextf
}
message := &mesos.AuthenticationStartMessage{
Mechanism: proto.String(selectedMech),
Data: data, // may be nil, depends on init step
}
if err := self.transport.Send(ctx, from, message); err != nil {
self.terminate(status, statusError, err)
} else {
(&self.status).swap(status, statusStepping)
}
}
func (self *authenticateeProcess) step(ctx context.Context, from *upid.UPID, pbMsg proto.Message) {
status := statusFrom(ctx)
if status != statusStepping {
self.terminate(status, statusError, UnexpectedAuthenticationStep)
return
}
log.Info("Received SASL authentication step")
msg, ok := pbMsg.(*mesos.AuthenticationStepMessage)
if !ok {
self.terminate(status, statusError, fmt.Errorf("Expected AuthenticationStepMessage, not %T", pbMsg))
return
}
input := msg.GetData()
fn, output, err := self.stepFn(self.mech, input)
if err != nil {
self.terminate(status, statusError, fmt.Errorf("failed to perform authentication step: %v", err))
return
}
self.stepFn = fn
// We don't start the client with SASL_SUCCESS_DATA so we may
// need to send one more "empty" message to the server.
message := &mesos.AuthenticationStepMessage{}
if len(output) > 0 {
message.Data = output
}
if err := self.transport.Send(ctx, from, message); err != nil {
self.terminate(status, statusError, err)
}
}
func (self *authenticateeProcess) completed(ctx context.Context, from *upid.UPID, pbMsg proto.Message) {
status := statusFrom(ctx)
if status != statusStepping {
self.terminate(status, statusError, UnexpectedAuthenticationCompleted)
return
}
log.Info("Authentication success")
self.terminate(status, statusCompleted, nil)
}
func (self *authenticateeProcess) failed(ctx context.Context, from *upid.UPID, pbMsg proto.Message) {
status := statusFrom(ctx)
self.terminate(status, statusFailed, auth.AuthenticationFailed)
}
func (self *authenticateeProcess) errored(ctx context.Context, from *upid.UPID, pbMsg proto.Message) {
var err error
if msg, ok := pbMsg.(*mesos.AuthenticationErrorMessage); !ok {
err = fmt.Errorf("Expected AuthenticationErrorMessage, not %T", pbMsg)
} else {
err = fmt.Errorf("Authentication error: %s", msg.GetError())
}
status := statusFrom(ctx)
self.terminate(status, statusError, err)
}

View file

@ -0,0 +1,56 @@
package sasl
import (
"net"
"strconv"
"golang.org/x/net/context"
)
// unexported to prevent collisions with context keys defined in
// other packages.
type _key int
// If this package defined other context keys, they would have
// different integer values.
const (
statusKey _key = iota
bindingAddressKey // bind address for login-related network ops
bindingPortKey // port to bind auth listener if a specific port is needed
)
func withStatus(ctx context.Context, s statusType) context.Context {
return context.WithValue(ctx, statusKey, s)
}
func statusFrom(ctx context.Context) statusType {
s, ok := ctx.Value(statusKey).(statusType)
if !ok {
panic("missing status in context")
}
return s
}
func WithBindingAddress(ctx context.Context, address net.IP) context.Context {
return context.WithValue(ctx, bindingAddressKey, address)
}
func BindingAddressFrom(ctx context.Context) net.IP {
obj := ctx.Value(bindingAddressKey)
if addr, ok := obj.(net.IP); ok {
return addr
} else {
return nil
}
}
func WithBindingPort(ctx context.Context, port uint16) context.Context {
return context.WithValue(ctx, bindingPortKey, port)
}
func BindingPortFrom(ctx context.Context) string {
if port, ok := ctx.Value(bindingPortKey).(uint16); ok {
return strconv.Itoa(int(port))
}
return "0"
}

View file

@ -0,0 +1,72 @@
package crammd5
import (
"crypto/hmac"
"crypto/md5"
"encoding/hex"
"errors"
"io"
log "github.com/golang/glog"
"github.com/mesos/mesos-go/api/v0/auth/callback"
"github.com/mesos/mesos-go/api/v0/auth/sasl/mech"
)
var (
Name = "CRAM-MD5" // name this mechanism is registered with
//TODO(jdef) is this a generic SASL error? if so, move it up to mech
challengeDataRequired = errors.New("challenge data may not be empty")
)
func init() {
mech.Register(Name, newInstance)
}
type mechanism struct {
handler callback.Handler
}
func (m *mechanism) Handler() callback.Handler {
return m.handler
}
func (m *mechanism) Discard() {
// noop
}
func newInstance(h callback.Handler) (mech.Interface, mech.StepFunc, error) {
m := &mechanism{
handler: h,
}
fn := func(m mech.Interface, data []byte) (mech.StepFunc, []byte, error) {
// noop: no initialization needed
return challengeResponse, nil, nil
}
return m, fn, nil
}
// algorithm lifted from wikipedia: http://en.wikipedia.org/wiki/CRAM-MD5
// except that the SASL mechanism used by Mesos doesn't leverage base64 encoding
func challengeResponse(m mech.Interface, data []byte) (mech.StepFunc, []byte, error) {
if len(data) == 0 {
return mech.IllegalState, nil, challengeDataRequired
}
decoded := string(data)
log.V(4).Infof("challenge(decoded): %s", decoded) // for deep debugging only
username := callback.NewName()
secret := callback.NewPassword()
if err := m.Handler().Handle(username, secret); err != nil {
return mech.IllegalState, nil, err
}
hash := hmac.New(md5.New, secret.Get())
if _, err := io.WriteString(hash, decoded); err != nil {
return mech.IllegalState, nil, err
}
codes := hex.EncodeToString(hash.Sum(nil))
msg := username.Get() + " " + codes
return nil, []byte(msg), nil
}

View file

@ -0,0 +1,33 @@
package mech
import (
"errors"
"github.com/mesos/mesos-go/api/v0/auth/callback"
)
var (
IllegalStateErr = errors.New("illegal mechanism state")
)
type Interface interface {
Handler() callback.Handler
Discard() // clean up resources or sensitive information; idempotent
}
// return a mechanism and it's initialization step (may be a noop that returns
// a nil data blob and handle to the first "real" challenge step).
type Factory func(h callback.Handler) (Interface, StepFunc, error)
// StepFunc implementations should never return a nil StepFunc result. This
// helps keep the logic in the SASL authticatee simpler: step functions are
// never nil. Mechanisms that end up an error state (for example, some decoding
// logic fails...) should return a StepFunc that represents an error state.
// Some mechanisms may be able to recover from such.
type StepFunc func(m Interface, data []byte) (StepFunc, []byte, error)
// reflects an unrecoverable, illegal mechanism state; always returns IllegalState
// as the next step along with an IllegalStateErr
func IllegalState(m Interface, data []byte) (StepFunc, []byte, error) {
return IllegalState, nil, IllegalStateErr
}

View file

@ -0,0 +1,49 @@
package mech
import (
"fmt"
"sync"
log "github.com/golang/glog"
)
var (
mechLock sync.Mutex
supportedMechs = make(map[string]Factory)
)
func Register(name string, f Factory) error {
mechLock.Lock()
defer mechLock.Unlock()
if _, found := supportedMechs[name]; found {
return fmt.Errorf("Mechanism registered twice: %s", name)
}
supportedMechs[name] = f
log.V(1).Infof("Registered mechanism %s", name)
return nil
}
func ListSupported() (list []string) {
mechLock.Lock()
defer mechLock.Unlock()
for mechname := range supportedMechs {
list = append(list, mechname)
}
return list
}
func SelectSupported(mechanisms []string) (selectedMech string, factory Factory) {
mechLock.Lock()
defer mechLock.Unlock()
for _, m := range mechanisms {
if f, ok := supportedMechs[m]; ok {
selectedMech = m
factory = f
break
}
}
return
}