mostly working

Signed-off-by: Jens Langhammer <jens@goauthentik.io>
This commit is contained in:
Jens Langhammer
2025-05-24 14:50:16 +02:00
parent f1101e0c01
commit 67f627a925
10 changed files with 86 additions and 29 deletions

View File

@ -1,6 +1,8 @@
package eap package eap
import ( import (
"fmt"
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
"goauthentik.io/internal/outpost/radius/eap/protocol" "goauthentik.io/internal/outpost/radius/eap/protocol"
"layeh.com/radius" "layeh.com/radius"
@ -12,6 +14,7 @@ type context struct {
typeState map[protocol.Type]any typeState map[protocol.Type]any
log *log.Entry log *log.Entry
settings interface{} settings interface{}
parent *context
endStatus protocol.Status endStatus protocol.Status
endModifier func(p *radius.Packet) *radius.Packet endModifier func(p *radius.Packet) *radius.Packet
handleInner func(protocol.Payload, protocol.StateManager) (protocol.Payload, error) handleInner func(protocol.Payload, protocol.StateManager) (protocol.Payload, error)
@ -27,19 +30,23 @@ func (ctx *context) Log() *log.Entry { return ctx.log }
func (ctx *context) HandleInnerEAP(p protocol.Payload, st protocol.StateManager) (protocol.Payload, error) { func (ctx *context) HandleInnerEAP(p protocol.Payload, st protocol.StateManager) (protocol.Payload, error) {
return ctx.handleInner(p, st) return ctx.handleInner(p, st)
} }
func (ctx *context) Inner(p protocol.Payload, t protocol.Type) protocol.Context {
func (ctx *context) ForInnerProtocol(p protocol.Type) protocol.Context {
return &context{ return &context{
req: ctx.req, req: ctx.req,
rootPayload: ctx.rootPayload,
typeState: ctx.typeState, typeState: ctx.typeState,
log: ctx.log, log: ctx.log.WithField("type", fmt.Sprintf("%T", p)).WithField("code", t),
settings: ctx.settings, settings: ctx.settings,
endStatus: ctx.endStatus, parent: ctx,
endModifier: ctx.endModifier, handleInner: ctx.handleInner,
} }
} }
func (ctx *context) EndInnerProtocol(st protocol.Status, mf func(p *radius.Packet) *radius.Packet) { func (ctx *context) EndInnerProtocol(st protocol.Status, mf func(p *radius.Packet) *radius.Packet) {
ctx.log.Info("Ending protocol")
if ctx.parent != nil {
ctx.parent.EndInnerProtocol(st, mf)
return
}
if ctx.endStatus != protocol.StatusUnknown { if ctx.endStatus != protocol.StatusUnknown {
return return
} }
@ -51,3 +58,13 @@ func (ctx *context) EndInnerProtocol(st protocol.Status, mf func(p *radius.Packe
} }
ctx.endModifier = mf ctx.endModifier = mf
} }
func (ctx *context) callEndModifier(p *radius.Packet) *radius.Packet {
if ctx.parent != nil {
p = ctx.parent.callEndModifier(p)
}
if ctx.endModifier != nil {
p = ctx.endModifier(p)
}
return p
}

View File

@ -34,7 +34,7 @@ func (p *Packet) HandleRadiusPacket(w radius.ResponseWriter, r *radius.Request)
p.state = rst p.state = rst
rp := &Packet{r: r} rp := &Packet{r: r}
rep, err := p.handleEAP(p.eap, p.stm) rep, err := p.handleEAP(p.eap, p.stm, nil)
rp.eap = rep rp.eap = rep
rres := r.Response(radius.CodeAccessReject) rres := r.Response(radius.CodeAccessReject)
@ -74,7 +74,7 @@ func (p *Packet) HandleRadiusPacket(w radius.ResponseWriter, r *radius.Request)
} }
} }
func (p *Packet) handleEAP(pp protocol.Payload, stm protocol.StateManager) (*eap.Payload, error) { func (p *Packet) handleEAP(pp protocol.Payload, stm protocol.StateManager, parentContext *context) (*eap.Payload, error) {
st := stm.GetEAPState(p.state) st := stm.GetEAPState(p.state)
if st == nil { if st == nil {
log.Debug("Root-EAP: blank state") log.Debug("Root-EAP: blank state")
@ -93,7 +93,7 @@ func (p *Packet) handleEAP(pp protocol.Payload, stm protocol.StateManager) (*eap
st.ProtocolIndex += 1 st.ProtocolIndex += 1
st.TypeState = map[protocol.Type]any{} st.TypeState = map[protocol.Type]any{}
stm.SetEAPState(p.state, st) stm.SetEAPState(p.state, st)
return p.handleEAP(pp, stm) return p.handleEAP(pp, stm, nil)
} }
if n, ok := p.eap.Payload.(*legacy_nak.Payload); ok { if n, ok := p.eap.Payload.(*legacy_nak.Payload); ok {
@ -104,21 +104,26 @@ func (p *Packet) handleEAP(pp protocol.Payload, stm protocol.StateManager) (*eap
np, t, _ := eap.EmptyPayload(stm.GetEAPSettings(), nextChallengeToOffer) np, t, _ := eap.EmptyPayload(stm.GetEAPSettings(), nextChallengeToOffer)
ctx := &context{ var ctx *context
req: p.r, if parentContext != nil {
rootPayload: p.eap, ctx = parentContext.Inner(np, t).(*context)
typeState: st.TypeState, } else {
log: log.WithField("type", fmt.Sprintf("%T", np)).WithField("code", t), ctx = &context{
settings: stm.GetEAPSettings().ProtocolSettings[t], req: p.r,
handleInner: func(pp protocol.Payload, sm protocol.StateManager) (protocol.Payload, error) { rootPayload: p.eap,
return p.handleEAP(pp, sm) typeState: st.TypeState,
}, log: log.WithField("type", fmt.Sprintf("%T", np)).WithField("code", t),
settings: stm.GetEAPSettings().ProtocolSettings[t],
}
ctx.handleInner = func(pp protocol.Payload, sm protocol.StateManager) (protocol.Payload, error) {
return p.handleEAP(pp, sm, ctx.Inner(pp, pp.Type()).(*context))
}
} }
if !np.Offerable() { if !np.Offerable() {
ctx.log.Debug("Root-EAP: protocol not offerable, skipping") ctx.Log().Debug("Root-EAP: protocol not offerable, skipping")
return next() return next()
} }
ctx.log.Debug("Root-EAP: Passing to protocol") ctx.Log().Debug("Root-EAP: Passing to protocol")
res := &eap.Payload{ res := &eap.Payload{
Code: protocol.CodeRequest, Code: protocol.CodeRequest,
@ -137,7 +142,7 @@ func (p *Packet) handleEAP(pp protocol.Payload, stm protocol.StateManager) (*eap
stm.SetEAPState(p.state, st) stm.SetEAPState(p.state, st)
if ctx.endModifier != nil { if ctx.endModifier != nil {
p.endModifier = ctx.endModifier p.endModifier = ctx.callEndModifier
} }
switch ctx.endStatus { switch ctx.endStatus {

View File

@ -25,6 +25,7 @@ type Context interface {
IsProtocolStart(p Type) bool IsProtocolStart(p Type) bool
HandleInnerEAP(Payload, StateManager) (Payload, error) HandleInnerEAP(Payload, StateManager) (Payload, error)
Inner(Payload, Type) Context
EndInnerProtocol(Status, func(p *radius.Packet) *radius.Packet) EndInnerProtocol(Status, func(p *radius.Packet) *radius.Packet)
Log() *log.Entry Log() *log.Entry

View File

@ -5,6 +5,7 @@ import (
"errors" "errors"
"layeh.com/radius/rfc2759" "layeh.com/radius/rfc2759"
"layeh.com/radius/rfc3079"
) )
func (p *Payload) checkChapPassword(res *Response) ([]byte, error) { func (p *Payload) checkChapPassword(res *Response) ([]byte, error) {
@ -18,6 +19,17 @@ func (p *Payload) checkChapPassword(res *Response) ([]byte, error) {
if !bytes.Equal(ntResponse, res.NTResponse) { if !bytes.Equal(ntResponse, res.NTResponse) {
return nil, errors.New("nt response mismatch") return nil, errors.New("nt response mismatch")
} }
p.st.recvKey, err = rfc3079.MakeKey(ntResponse, bytePwd, false)
if err != nil {
return nil, err
}
p.st.sendKey, err = rfc3079.MakeKey(ntResponse, bytePwd, true)
if err != nil {
return nil, err
}
authenticatorResponse, err := rfc2759.GenerateAuthenticatorResponse(p.st.Challenge, p.st.PeerChallenge, ntResponse, byteUser, bytePwd) authenticatorResponse, err := rfc2759.GenerateAuthenticatorResponse(p.st.Challenge, p.st.PeerChallenge, ntResponse, byteUser, bytePwd)
if err != nil { if err != nil {
return nil, err return nil, err

View File

@ -10,6 +10,8 @@ import (
"goauthentik.io/internal/outpost/radius/eap/protocol" "goauthentik.io/internal/outpost/radius/eap/protocol"
"goauthentik.io/internal/outpost/radius/eap/protocol/eap" "goauthentik.io/internal/outpost/radius/eap/protocol/eap"
"goauthentik.io/internal/outpost/radius/eap/protocol/peap" "goauthentik.io/internal/outpost/radius/eap/protocol/peap"
"layeh.com/radius"
"layeh.com/radius/vendors/microsoft"
) )
const TypeMSCHAPv2 protocol.Type = 26 const TypeMSCHAPv2 protocol.Type = 26
@ -63,7 +65,7 @@ func (p *Payload) Decode(raw []byte) error {
p.ValueSize = raw[4] p.ValueSize = raw[4]
if p.ValueSize != responseValueSize { if p.ValueSize != responseValueSize {
return fmt.Errorf("mschapv2: incorrect value size: %d", p.ValueSize) return fmt.Errorf("MSCHAPv2: incorrect value size: %d", p.ValueSize)
} }
p.Response = raw[5 : p.ValueSize+5] p.Response = raw[5 : p.ValueSize+5]
p.Name = raw[5+p.ValueSize:] p.Name = raw[5+p.ValueSize:]
@ -136,7 +138,7 @@ func (p *Payload) Handle(ctx protocol.Context) protocol.Payload {
} }
return succ return succ
} else if p.OpCode == OpSuccess && p.st.Authenticated { } else if p.OpCode == OpSuccess && p.st.Authenticated {
return &peap.ExtensionPayload{ ep := &peap.ExtensionPayload{
AVPs: []peap.ExtensionAVP{ AVPs: []peap.ExtensionAVP{
{ {
Mandatory: true, Mandatory: true,
@ -145,6 +147,19 @@ func (p *Payload) Handle(ctx protocol.Context) protocol.Payload {
}, },
}, },
} }
p.st.IsProtocolEnded = true
return ep
} else if p.st.IsProtocolEnded {
ctx.EndInnerProtocol(protocol.StatusSuccess, func(r *radius.Packet) *radius.Packet {
if len(microsoft.MSMPPERecvKey_Get(r, ctx.Packet().Packet)) < 1 {
microsoft.MSMPPERecvKey_Set(r, p.st.recvKey)
}
if len(microsoft.MSMPPESendKey_Get(r, ctx.Packet().Packet)) < 1 {
microsoft.MSMPPESendKey_Set(r, p.st.sendKey)
}
return r
})
return &Payload{}
} }
return response return response
} }

View File

@ -1,7 +1,10 @@
package mschapv2 package mschapv2
type State struct { type State struct {
Challenge []byte Challenge []byte
PeerChallenge []byte PeerChallenge []byte
Authenticated bool Authenticated bool
IsProtocolEnded bool
recvKey []byte
sendKey []byte
} }

View File

@ -4,6 +4,7 @@ import (
"encoding/binary" "encoding/binary"
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
"goauthentik.io/internal/outpost/radius/eap/debug"
"goauthentik.io/internal/outpost/radius/eap/protocol" "goauthentik.io/internal/outpost/radius/eap/protocol"
) )
@ -14,6 +15,7 @@ type ExtensionPayload struct {
} }
func (ep *ExtensionPayload) Decode(raw []byte) error { func (ep *ExtensionPayload) Decode(raw []byte) error {
log.WithField("raw", debug.FormatBytes(raw)).Debugf("PEAP-Extension: decode raw")
ep.AVPs = []ExtensionAVP{} ep.AVPs = []ExtensionAVP{}
offset := 0 offset := 0
for { for {
@ -32,7 +34,7 @@ func (ep *ExtensionPayload) Decode(raw []byte) error {
} }
func (ep *ExtensionPayload) Encode() ([]byte, error) { func (ep *ExtensionPayload) Encode() ([]byte, error) {
log.Debug("PEAP: Extension encode") log.Debug("PEAP-Extension: encode")
buff := []byte{} buff := []byte{}
for _, avp := range ep.AVPs { for _, avp := range ep.AVPs {
buff = append(buff, avp.Encode()...) buff = append(buff, avp.Encode()...)

View File

@ -51,7 +51,7 @@ func (p *Payload) Decode(raw []byte) error {
func (p *Payload) Encode() ([]byte, error) { func (p *Payload) Encode() ([]byte, error) {
log.Debug("PEAP: Encoding inner EAP") log.Debug("PEAP: Encoding inner EAP")
if p.eap.Payload == nil { if p.eap.Payload == nil {
return []byte{}, errors.New("peap: no payload in response eap packet") return []byte{}, errors.New("PEAP: no payload in response eap packet")
} }
payload, err := p.eap.Payload.Encode() payload, err := p.eap.Payload.Encode()
if err != nil { if err != nil {
@ -129,6 +129,7 @@ func (p *Payload) Handle(ctx protocol.Context) protocol.Payload {
res, err := ctx.HandleInnerEAP(ep, p) res, err := ctx.HandleInnerEAP(ep, p)
if err != nil { if err != nil {
ctx.Log().WithError(err).Warning("PEAP: failed to handle inner EAP") ctx.Log().WithError(err).Warning("PEAP: failed to handle inner EAP")
return nil
} }
// Normal payloads need to be wrapped in PEAP to use the correct encoding (see Encode() above) // Normal payloads need to be wrapped in PEAP to use the correct encoding (see Encode() above)
// Extension payloads handle encoding differently // Extension payloads handle encoding differently

View File

@ -23,7 +23,7 @@ func (p *Payload) innerHandler(ctx protocol.Context) {
ctx.EndInnerProtocol(protocol.StatusError, nil) ctx.EndInnerProtocol(protocol.StatusError, nil)
return return
} }
pl := p.Inner.Handle(ctx) pl := p.Inner.Handle(ctx.Inner(p.Inner, p.Inner.Type()))
enc, err := pl.Encode() enc, err := pl.Encode()
if err != nil { if err != nil {
ctx.Log().WithError(err).Warning("TLS: failed to encode inner protocol") ctx.Log().WithError(err).Warning("TLS: failed to encode inner protocol")

View File

@ -145,6 +145,7 @@ func (p *Payload) Handle(ctx protocol.Context) protocol.Payload {
retry.Attempts(0), retry.Attempts(0),
) )
ctx.EndInnerProtocol(pst, func(r *radius.Packet) *radius.Packet { ctx.EndInnerProtocol(pst, func(r *radius.Packet) *radius.Packet {
ctx.Log().Debug("TLS: Adding MPPE Keys")
microsoft.MSMPPERecvKey_Set(r, p.st.MPPEKey[:32]) microsoft.MSMPPERecvKey_Set(r, p.st.MPPEKey[:32])
microsoft.MSMPPESendKey_Set(r, p.st.MPPEKey[64:64+32]) microsoft.MSMPPESendKey_Set(r, p.st.MPPEKey[64:64+32])
return r return r