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
import (
"fmt"
log "github.com/sirupsen/logrus"
"goauthentik.io/internal/outpost/radius/eap/protocol"
"layeh.com/radius"
@ -12,6 +14,7 @@ type context struct {
typeState map[protocol.Type]any
log *log.Entry
settings interface{}
parent *context
endStatus protocol.Status
endModifier func(p *radius.Packet) *radius.Packet
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) {
return ctx.handleInner(p, st)
}
func (ctx *context) ForInnerProtocol(p protocol.Type) protocol.Context {
func (ctx *context) Inner(p protocol.Payload, t protocol.Type) protocol.Context {
return &context{
req: ctx.req,
rootPayload: ctx.rootPayload,
typeState: ctx.typeState,
log: ctx.log,
log: ctx.log.WithField("type", fmt.Sprintf("%T", p)).WithField("code", t),
settings: ctx.settings,
endStatus: ctx.endStatus,
endModifier: ctx.endModifier,
parent: ctx,
handleInner: ctx.handleInner,
}
}
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 {
return
}
@ -51,3 +58,13 @@ func (ctx *context) EndInnerProtocol(st protocol.Status, mf func(p *radius.Packe
}
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
rp := &Packet{r: r}
rep, err := p.handleEAP(p.eap, p.stm)
rep, err := p.handleEAP(p.eap, p.stm, nil)
rp.eap = rep
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)
if st == nil {
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.TypeState = map[protocol.Type]any{}
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 {
@ -104,21 +104,26 @@ func (p *Packet) handleEAP(pp protocol.Payload, stm protocol.StateManager) (*eap
np, t, _ := eap.EmptyPayload(stm.GetEAPSettings(), nextChallengeToOffer)
ctx := &context{
req: p.r,
rootPayload: p.eap,
typeState: st.TypeState,
log: log.WithField("type", fmt.Sprintf("%T", np)).WithField("code", t),
settings: stm.GetEAPSettings().ProtocolSettings[t],
handleInner: func(pp protocol.Payload, sm protocol.StateManager) (protocol.Payload, error) {
return p.handleEAP(pp, sm)
},
var ctx *context
if parentContext != nil {
ctx = parentContext.Inner(np, t).(*context)
} else {
ctx = &context{
req: p.r,
rootPayload: p.eap,
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() {
ctx.log.Debug("Root-EAP: protocol not offerable, skipping")
ctx.Log().Debug("Root-EAP: protocol not offerable, skipping")
return next()
}
ctx.log.Debug("Root-EAP: Passing to protocol")
ctx.Log().Debug("Root-EAP: Passing to protocol")
res := &eap.Payload{
Code: protocol.CodeRequest,
@ -137,7 +142,7 @@ func (p *Packet) handleEAP(pp protocol.Payload, stm protocol.StateManager) (*eap
stm.SetEAPState(p.state, st)
if ctx.endModifier != nil {
p.endModifier = ctx.endModifier
p.endModifier = ctx.callEndModifier
}
switch ctx.endStatus {

View File

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

View File

@ -5,6 +5,7 @@ import (
"errors"
"layeh.com/radius/rfc2759"
"layeh.com/radius/rfc3079"
)
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) {
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)
if err != nil {
return nil, err

View File

@ -10,6 +10,8 @@ import (
"goauthentik.io/internal/outpost/radius/eap/protocol"
"goauthentik.io/internal/outpost/radius/eap/protocol/eap"
"goauthentik.io/internal/outpost/radius/eap/protocol/peap"
"layeh.com/radius"
"layeh.com/radius/vendors/microsoft"
)
const TypeMSCHAPv2 protocol.Type = 26
@ -63,7 +65,7 @@ func (p *Payload) Decode(raw []byte) error {
p.ValueSize = raw[4]
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.Name = raw[5+p.ValueSize:]
@ -136,7 +138,7 @@ func (p *Payload) Handle(ctx protocol.Context) protocol.Payload {
}
return succ
} else if p.OpCode == OpSuccess && p.st.Authenticated {
return &peap.ExtensionPayload{
ep := &peap.ExtensionPayload{
AVPs: []peap.ExtensionAVP{
{
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
}

View File

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

View File

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

View File

@ -51,7 +51,7 @@ func (p *Payload) Decode(raw []byte) error {
func (p *Payload) Encode() ([]byte, error) {
log.Debug("PEAP: Encoding inner EAP")
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()
if err != nil {
@ -129,6 +129,7 @@ func (p *Payload) Handle(ctx protocol.Context) protocol.Payload {
res, err := ctx.HandleInnerEAP(ep, p)
if err != nil {
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)
// Extension payloads handle encoding differently

View File

@ -23,7 +23,7 @@ func (p *Payload) innerHandler(ctx protocol.Context) {
ctx.EndInnerProtocol(protocol.StatusError, nil)
return
}
pl := p.Inner.Handle(ctx)
pl := p.Inner.Handle(ctx.Inner(p.Inner, p.Inner.Type()))
enc, err := pl.Encode()
if err != nil {
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),
)
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.MSMPPESendKey_Set(r, p.st.MPPEKey[64:64+32])
return r