diff --git a/internal/outpost/radius/eap/handler.go b/internal/outpost/radius/eap/handler.go index 096b37989c..73cbca9998 100644 --- a/internal/outpost/radius/eap/handler.go +++ b/internal/outpost/radius/eap/handler.go @@ -59,7 +59,7 @@ func (p *Packet) HandleRadiusPacket(w radius.ResponseWriter, r *radius.Request) sendErrorResponse(w, r) return } - log.WithField("length", len(eapEncoded)).WithField("type", fmt.Sprintf("%T", rp.eap.Payload)).Debug("EAP: encapsulated challenge") + log.WithField("length", len(eapEncoded)).WithField("type", fmt.Sprintf("%T", rp.eap.Payload)).Debug("Root-EAP: encapsulated challenge") rfc2869.EAPMessage_Set(rres, eapEncoded) err = p.setMessageAuthenticator(rres) if err != nil { @@ -76,7 +76,7 @@ func (p *Packet) HandleRadiusPacket(w radius.ResponseWriter, r *radius.Request) func (p *Packet) handleInner(r *radius.Request) (*eap.Payload, error) { st := p.stm.GetEAPState(p.state) if st == nil { - log.Debug("EAP: blank state") + log.Debug("Root-EAP: blank state") st = BlankState(p.stm.GetEAPSettings()) } @@ -96,7 +96,7 @@ func (p *Packet) handleInner(r *radius.Request) (*eap.Payload, error) { } if _, ok := p.eap.Payload.(*legacy_nak.Payload); ok { - log.Debug("EAP: received NAK, trying next protocol") + log.Debug("Root-EAP: received NAK, trying next protocol") p.eap.Payload = nil return next() } @@ -111,10 +111,10 @@ func (p *Packet) handleInner(r *radius.Request) (*eap.Payload, error) { settings: p.stm.GetEAPSettings().ProtocolSettings[t], } if !np.Offerable() { - ctx.log.Debug("EAP: protocol not offerable, skipping") + ctx.log.Debug("Root-EAP: protocol not offerable, skipping") return next() } - ctx.log.Debug("EAP: Passing to protocol") + ctx.log.Debug("Root-EAP: Passing to protocol") res := p.GetChallengeForType(ctx, np, t) p.stm.SetEAPState(p.state, st) @@ -131,7 +131,7 @@ func (p *Packet) handleInner(r *radius.Request) (*eap.Payload, error) { res.Code = protocol.CodeFailure res.ID -= 1 case protocol.StatusNextProtocol: - ctx.log.Debug("EAP: Protocol ended, starting next protocol") + ctx.log.Debug("Root-EAP: Protocol ended, starting next protocol") return next() case protocol.StatusUnknown: } diff --git a/internal/outpost/radius/eap/protocol/eap/payload.go b/internal/outpost/radius/eap/protocol/eap/payload.go index 4d578f93b7..f7e9096f66 100644 --- a/internal/outpost/radius/eap/protocol/eap/payload.go +++ b/internal/outpost/radius/eap/protocol/eap/payload.go @@ -2,7 +2,6 @@ package eap import ( "encoding/binary" - "errors" "fmt" log "github.com/sirupsen/logrus" @@ -38,16 +37,16 @@ func (packet *Payload) Decode(raw []byte) error { packet.ID = raw[1] packet.Length = binary.BigEndian.Uint16(raw[2:]) if packet.Length != uint16(len(raw)) { - return errors.New("mismatched packet length") + return fmt.Errorf("mismatched packet length; got %d, expected %d", packet.Length, uint16(len(raw))) } if len(raw) > 4 && (packet.Code == protocol.CodeRequest || packet.Code == protocol.CodeResponse) { packet.MsgType = protocol.Type(raw[4]) } + log.WithField("raw", debug.FormatBytes(raw)).WithField("payload", fmt.Sprintf("%T", packet.Payload)).Trace("EAP: decode raw") packet.RawPayload = raw[5:] if packet.Payload == nil { return nil } - log.WithField("raw", debug.FormatBytes(raw)).WithField("payload", fmt.Sprintf("%T", packet.Payload)).Trace("EAP: decode raw") err := packet.Payload.Decode(raw[5:]) if err != nil { return err diff --git a/internal/outpost/radius/eap/protocol/peap/payload.go b/internal/outpost/radius/eap/protocol/peap/payload.go index 3fe0e89663..358b181d4d 100644 --- a/internal/outpost/radius/eap/protocol/peap/payload.go +++ b/internal/outpost/radius/eap/protocol/peap/payload.go @@ -1,6 +1,8 @@ package peap import ( + "encoding/binary" + log "github.com/sirupsen/logrus" "goauthentik.io/internal/outpost/radius/eap/debug" "goauthentik.io/internal/outpost/radius/eap/protocol" @@ -45,6 +47,28 @@ func (p *Payload) Encode() ([]byte, error) { return p.eap.Encode() } +// Inner EAP packets in PEAP may not include the header, hence we need a custom decoder +// https://datatracker.ietf.org/doc/html/draft-kamath-pppext-peapv0-00.txt#section-1.1 +func (p *Payload) eapInnerDecode(ctx protocol.Context) (*eap.Payload, error) { + ep := &eap.Payload{} + rootEap := ctx.RootPayload().(*eap.Payload) + fixedRaw := []byte{ + byte(rootEap.Code), + rootEap.ID, + // 2 byte space for length + 0, + 0, + } + fullLength := len(p.raw) + len(fixedRaw) + binary.BigEndian.PutUint16(fixedRaw[2:], uint16(fullLength)) + fixedRaw = append(fixedRaw, p.raw...) + err := ep.Decode(fixedRaw) + if err != nil { + return nil, err + } + return ep, nil +} + func (p *Payload) Handle(ctx protocol.Context) protocol.Payload { defer func() { ctx.SetProtocolState(TypePEAP, p.st) @@ -64,13 +88,15 @@ func (p *Payload) Handle(ctx protocol.Context) protocol.Payload { } p.st = ctx.GetProtocolState(TypePEAP).(*State) - ep := &eap.Payload{} - err := ep.Decode(p.raw) + ep, err := p.eapInnerDecode(ctx) if err != nil { ctx.Log().WithError(err).Warning("PEAP: failed to decode inner EAP") - return &Payload{} + return &eap.Payload{ + Code: protocol.CodeFailure, + ID: rootEap.ID + 1, + } } - return &Payload{} + return ep } func (p *Payload) Offerable() bool { diff --git a/internal/outpost/radius/eap/protocol/tls/inner.go b/internal/outpost/radius/eap/protocol/tls/inner.go index 9c56a68d86..1a5a28ce8f 100644 --- a/internal/outpost/radius/eap/protocol/tls/inner.go +++ b/internal/outpost/radius/eap/protocol/tls/inner.go @@ -5,9 +5,15 @@ import ( ) func (p *Payload) innerHandler(ctx protocol.Context) { - // p.st.TLS.read - // d, _ := io.ReadAll(p.st.TLS) - err := p.Inner.Decode([]byte{}) + d := make([]byte, 1024) + if !ctx.IsProtocolStart(p.Inner.Type()) { + ctx.Log().Debug("TLS: Reading from TLS for inner protocol") + _, err := p.st.TLS.Read(d) + if err != nil { + ctx.Log().WithError(err).Warning("TLS: Failed to read from TLS connection") + } + } + err := p.Inner.Decode(d) if err != nil { ctx.Log().WithError(err).Warning("TLS: failed to decode inner protocol") ctx.EndInnerProtocol(protocol.StatusError, nil) @@ -16,14 +22,14 @@ func (p *Payload) innerHandler(ctx protocol.Context) { pl := p.Inner.Handle(ctx) enc, err := pl.Encode() if err != nil { - ctx.Log().WithError(err).Warning("failed to encode inner protocol") + ctx.Log().WithError(err).Warning("TLS: failed to encode inner protocol") + ctx.EndInnerProtocol(protocol.StatusError, nil) + return } - // p.st.Conn.expectedWriterByteCount = len(enc) _, err = p.st.TLS.Write(enc) if err != nil { - ctx.Log().WithError(err).Warning("failed to write to TLS") + ctx.Log().WithError(err).Warning("TLS: failed to write to TLS") + ctx.EndInnerProtocol(protocol.StatusError, nil) + return } - // return &Payload{ - // Data: enc, - // } } diff --git a/internal/outpost/radius/eap/protocol/tls/payload.go b/internal/outpost/radius/eap/protocol/tls/payload.go index d9a06571e2..fda9c784b9 100644 --- a/internal/outpost/radius/eap/protocol/tls/payload.go +++ b/internal/outpost/radius/eap/protocol/tls/payload.go @@ -36,9 +36,6 @@ type Payload struct { } func (p *Payload) Type() protocol.Type { - // if p.inner != nil { - // return p.inner.Type() - // } return TypeTLS } @@ -109,7 +106,7 @@ func (p *Payload) Handle(ctx protocol.Context) protocol.Payload { p.st.Conn.expectedWriterByteCount = 0 } p.st.Conn.UpdateData(p.Data) - if !p.st.Conn.NeedsMoreData() { + if !p.st.Conn.NeedsMoreData() && !p.st.HandshakeDone { // Wait for outbound data to be available p.st.Conn.OutboundData() } @@ -126,12 +123,12 @@ func (p *Payload) Handle(ctx protocol.Context) protocol.Payload { return p.sendNextChunk() } if p.st.Conn.writer.Len() == 0 && p.st.HandshakeDone { - defer p.st.ContextCancel() if p.Inner != nil { ctx.Log().Debug("TLS: Handshake is done, delegating to inner protocol") p.innerHandler(ctx) return p.startChunkedTransfer(p.st.Conn.OutboundData()) } + defer p.st.ContextCancel() // If we don't have a final status from the handshake finished function, stall for time pst, _ := retry.DoWithData( func() (protocol.Status, error) {