124 lines
3.2 KiB
Go
124 lines
3.2 KiB
Go
package eap
|
|
|
|
import (
|
|
"crypto/hmac"
|
|
"crypto/md5"
|
|
"encoding/base64"
|
|
|
|
"github.com/gorilla/securecookie"
|
|
log "github.com/sirupsen/logrus"
|
|
"goauthentik.io/internal/outpost/radius/eap/protocol"
|
|
"goauthentik.io/internal/outpost/radius/eap/tls"
|
|
"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) Handle(stm StateManager, w radius.ResponseWriter, r *radius.Request) {
|
|
rst := rfc2865.State_GetString(r.Packet)
|
|
if rst == "" {
|
|
rst = base64.StdEncoding.EncodeToString(securecookie.GenerateRandomKey(12))
|
|
}
|
|
st := stm.GetEAPState(rst)
|
|
if st == nil {
|
|
log.Debug("EAP: blank state")
|
|
st = BlankState(stm.GetEAPSettings())
|
|
}
|
|
if len(st.ChallengesToOffer) < 1 {
|
|
log.Error("No more challenges to offer")
|
|
sendErrorResponse(w, r)
|
|
return
|
|
}
|
|
nextChallengeToOffer := st.ChallengesToOffer[0]
|
|
|
|
ctx := &context{
|
|
req: r,
|
|
state: st.TypeState[nextChallengeToOffer],
|
|
log: log.WithField("type", nextChallengeToOffer),
|
|
settings: stm.GetEAPSettings().ProtocolSettings[nextChallengeToOffer],
|
|
}
|
|
|
|
res := p.GetChallengeForType(ctx, nextChallengeToOffer)
|
|
st.TypeState[nextChallengeToOffer] = ctx.GetProtocolState(nil)
|
|
stm.SetEAPState(rst, st)
|
|
|
|
rres := r.Response(radius.CodeAccessChallenge)
|
|
switch ctx.endStatus {
|
|
case protocol.StatusSuccess:
|
|
res.code = CodeSuccess
|
|
res.id -= 1
|
|
rres = ctx.endModifier(rres)
|
|
st.ChallengesToOffer = st.ChallengesToOffer[1:]
|
|
if len(st.ChallengesToOffer) < 1 {
|
|
rres.Code = radius.CodeAccessAccept
|
|
}
|
|
case protocol.StatusError:
|
|
res.code = CodeFailure
|
|
res.id -= 1
|
|
st.ChallengesToOffer = st.ChallengesToOffer[1:]
|
|
rres = ctx.endModifier(rres)
|
|
if len(st.ChallengesToOffer) < 1 {
|
|
rres.Code = radius.CodeAccessReject
|
|
}
|
|
case protocol.StatusUnknown:
|
|
}
|
|
rfc2865.State_SetString(rres, rst)
|
|
eapEncoded, err := res.Encode()
|
|
if err != nil {
|
|
log.WithError(err).Warning("failed to encode response")
|
|
sendErrorResponse(w, r)
|
|
}
|
|
log.WithField("length", len(eapEncoded)).Debug("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)
|
|
}
|
|
err = w.Write(rres)
|
|
if err != nil {
|
|
log.WithError(err).Warning("failed to send response")
|
|
}
|
|
}
|
|
|
|
func (p *Packet) GetChallengeForType(ctx *context, t protocol.Type) *Packet {
|
|
res := &Packet{
|
|
code: CodeRequest,
|
|
id: p.id + 1,
|
|
msgType: t,
|
|
}
|
|
var payload any
|
|
switch t {
|
|
case tls.TypeTLS:
|
|
if _, ok := p.Payload.(*tls.Payload); !ok {
|
|
p.Payload = &tls.Payload{}
|
|
p.Payload.Decode(p.rawPayload)
|
|
}
|
|
payload = p.Payload.(*tls.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
|
|
}
|