Files
authentik/internal/outpost/radius/eap/handler.go
Jens Langhammer 8da54d5811 more refactor
Signed-off-by: Jens Langhammer <jens@goauthentik.io>
2025-07-01 22:43:00 +02:00

170 lines
4.3 KiB
Go

package eap
import (
"crypto/hmac"
"crypto/md5"
"encoding/base64"
"fmt"
"github.com/gorilla/securecookie"
log "github.com/sirupsen/logrus"
"goauthentik.io/internal/outpost/radius/eap/protocol"
"goauthentik.io/internal/outpost/radius/eap/protocol/eap"
"goauthentik.io/internal/outpost/radius/eap/protocol/legacy_nak"
"layeh.com/radius"
"layeh.com/radius/rfc2865"
"layeh.com/radius/rfc2869"
)
func sendErrorResponse(w radius.ResponseWriter, r *radius.Request) {
rres := r.Response(radius.CodeAccessReject)
err := w.Write(rres)
if err != nil {
log.WithError(err).Warning("failed to send response")
}
}
func (p *Packet) HandleRadiusPacket(w radius.ResponseWriter, r *radius.Request) {
rst := rfc2865.State_GetString(r.Packet)
if rst == "" {
rst = base64.StdEncoding.EncodeToString(securecookie.GenerateRandomKey(12))
}
p.state = rst
rep, err := p.handleInner(r)
rp := &Packet{
eap: rep,
}
rres := r.Response(radius.CodeAccessReject)
if err == nil {
rres = p.endModifier(rres)
switch rp.eap.Code {
case protocol.CodeRequest:
rres.Code = radius.CodeAccessChallenge
case protocol.CodeFailure:
rres.Code = radius.CodeAccessReject
case protocol.CodeSuccess:
rres.Code = radius.CodeAccessAccept
}
} else {
rres.Code = radius.CodeAccessReject
log.WithError(err).Debug("Rejecting request")
}
rfc2865.State_SetString(rres, p.state)
eapEncoded, err := rp.Encode()
if err != nil {
log.WithError(err).Warning("failed to encode response")
sendErrorResponse(w, r)
return
}
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 {
log.WithError(err).Warning("failed to send message authenticator")
sendErrorResponse(w, r)
return
}
err = w.Write(rres)
if err != nil {
log.WithError(err).Warning("failed to send response")
}
}
func (p *Packet) handleInner(r *radius.Request) (*eap.Payload, error) {
st := p.stm.GetEAPState(p.state)
if st == nil {
log.Debug("Root-EAP: blank state")
st = protocol.BlankState(p.stm.GetEAPSettings())
}
nextChallengeToOffer, err := st.GetNextProtocol()
if err != nil {
return &eap.Payload{
Code: protocol.CodeFailure,
ID: p.eap.ID,
}, err
}
next := func() (*eap.Payload, error) {
st.ProtocolIndex += 1
st.TypeState = map[protocol.Type]any{}
p.stm.SetEAPState(p.state, st)
return p.handleInner(r)
}
if _, ok := p.eap.Payload.(*legacy_nak.Payload); ok {
log.Debug("Root-EAP: received NAK, trying next protocol")
p.eap.Payload = nil
return next()
}
np, t, _ := emptyPayload(p.stm, nextChallengeToOffer)
ctx := &context{
req: r,
rootPayload: p.eap,
typeState: st.TypeState,
log: log.WithField("type", fmt.Sprintf("%T", np)).WithField("code", t),
settings: p.stm.GetEAPSettings().ProtocolSettings[t],
}
if !np.Offerable() {
ctx.log.Debug("Root-EAP: protocol not offerable, skipping")
return next()
}
ctx.log.Debug("Root-EAP: Passing to protocol")
res := p.GetChallengeForType(ctx, np, t)
p.stm.SetEAPState(p.state, st)
if ctx.endModifier != nil {
p.endModifier = ctx.endModifier
}
switch ctx.endStatus {
case protocol.StatusSuccess:
res.Code = protocol.CodeSuccess
res.ID -= 1
case protocol.StatusError:
res.Code = protocol.CodeFailure
res.ID -= 1
case protocol.StatusNextProtocol:
ctx.log.Debug("Root-EAP: Protocol ended, starting next protocol")
return next()
case protocol.StatusUnknown:
}
return res, nil
}
func (p *Packet) GetChallengeForType(ctx *context, np protocol.Payload, t protocol.Type) *eap.Payload {
res := &eap.Payload{
Code: protocol.CodeRequest,
ID: p.eap.ID + 1,
MsgType: t,
}
var payload any
if ctx.IsProtocolStart(t) {
p.eap.Payload = np
p.eap.Payload.Decode(p.eap.RawPayload)
}
payload = p.eap.Payload.Handle(ctx)
if payload != nil {
res.Payload = payload.(protocol.Payload)
}
return res
}
func (p *Packet) setMessageAuthenticator(rp *radius.Packet) error {
_ = rfc2869.MessageAuthenticator_Set(rp, make([]byte, 16))
hash := hmac.New(md5.New, rp.Secret)
encode, err := rp.MarshalBinary()
if err != nil {
return err
}
hash.Write(encode)
_ = rfc2869.MessageAuthenticator_Set(rp, hash.Sum(nil))
return nil
}