more mschap v2, start peap extension type 33
Signed-off-by: Jens Langhammer <jens@goauthentik.io>
This commit is contained in:
@ -44,7 +44,7 @@ func (p *Payload) Decode(raw []byte) error {
|
||||
if len(raw) > 4 && (p.Code == protocol.CodeRequest || p.Code == protocol.CodeResponse) {
|
||||
p.MsgType = protocol.Type(raw[4])
|
||||
}
|
||||
log.WithField("raw", debug.FormatBytes(raw)).WithField("payload", fmt.Sprintf("%T", p.Payload)).Trace("EAP: decode raw")
|
||||
log.WithField("raw", debug.FormatBytes(raw)).Trace("EAP: decode raw")
|
||||
p.RawPayload = raw[5:]
|
||||
pp, _, err := EmptyPayload(p.Settings, p.MsgType)
|
||||
if err != nil {
|
||||
|
26
internal/outpost/radius/eap/protocol/mschapv2/auth.go
Normal file
26
internal/outpost/radius/eap/protocol/mschapv2/auth.go
Normal file
@ -0,0 +1,26 @@
|
||||
package mschapv2
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
|
||||
"layeh.com/radius/rfc2759"
|
||||
)
|
||||
|
||||
func (p *Payload) checkChapPassword(res *Response) ([]byte, error) {
|
||||
byteUser := []byte("foo")
|
||||
bytePwd := []byte("bar")
|
||||
ntResponse, err := rfc2759.GenerateNTResponse(p.st.Challenge, p.st.PeerChallenge, byteUser, bytePwd)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if !bytes.Equal(ntResponse, res.NTResponse) {
|
||||
return nil, errors.New("nt response mismatch")
|
||||
}
|
||||
authenticatorResponse, err := rfc2759.GenerateAuthenticatorResponse(p.st.Challenge, p.st.PeerChallenge, ntResponse, byteUser, bytePwd)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return []byte(authenticatorResponse), nil
|
||||
}
|
23
internal/outpost/radius/eap/protocol/mschapv2/op_response.go
Normal file
23
internal/outpost/radius/eap/protocol/mschapv2/op_response.go
Normal file
@ -0,0 +1,23 @@
|
||||
package mschapv2
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
)
|
||||
|
||||
type Response struct {
|
||||
Challenge []byte
|
||||
NTResponse []byte
|
||||
Flags uint8
|
||||
}
|
||||
|
||||
func ParseResponse(raw []byte) (*Response, error) {
|
||||
res := &Response{}
|
||||
res.Challenge = raw[:challengeValueSize]
|
||||
if !bytes.Equal(raw[challengeValueSize:challengeValueSize+responseReservedSize], make([]byte, 8)) {
|
||||
return nil, errors.New("MSCHAPv2: Reserved bytes not empty?")
|
||||
}
|
||||
res.NTResponse = raw[challengeValueSize+responseReservedSize : challengeValueSize+responseReservedSize+responseNTResponseSize]
|
||||
res.Flags = (raw[challengeValueSize+responseReservedSize+responseNTResponseSize])
|
||||
return res, nil
|
||||
}
|
23
internal/outpost/radius/eap/protocol/mschapv2/op_success.go
Normal file
23
internal/outpost/radius/eap/protocol/mschapv2/op_success.go
Normal file
@ -0,0 +1,23 @@
|
||||
package mschapv2
|
||||
|
||||
import "encoding/binary"
|
||||
|
||||
type SuccessRequest struct {
|
||||
*Payload
|
||||
Authenticator []byte
|
||||
}
|
||||
|
||||
// A success request is encoded slightly differently, it doesn't have a challenge and as such
|
||||
// doesn't need to encode the length of it
|
||||
func (sr *SuccessRequest) Encode() ([]byte, error) {
|
||||
encoded := []byte{
|
||||
byte(sr.OpCode),
|
||||
sr.MSCHAPv2ID,
|
||||
0,
|
||||
0,
|
||||
}
|
||||
encoded = append(encoded, sr.Authenticator...)
|
||||
sr.MSLength = uint16(len(encoded))
|
||||
binary.BigEndian.PutUint16(encoded[2:], sr.MSLength)
|
||||
return encoded, nil
|
||||
}
|
@ -1,7 +1,15 @@
|
||||
package mschapv2
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
|
||||
"github.com/gorilla/securecookie"
|
||||
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/eap"
|
||||
"goauthentik.io/internal/outpost/radius/eap/protocol/peap"
|
||||
)
|
||||
|
||||
const TypeMSCHAPv2 protocol.Type = 26
|
||||
@ -10,7 +18,33 @@ func Protocol() protocol.Payload {
|
||||
return &Payload{}
|
||||
}
|
||||
|
||||
const (
|
||||
challengeValueSize = 16
|
||||
responseValueSize = 49
|
||||
responseReservedSize = 8
|
||||
responseNTResponseSize = 24
|
||||
)
|
||||
|
||||
type OpCode uint8
|
||||
|
||||
const (
|
||||
OpChallenge OpCode = 1
|
||||
OpResponse OpCode = 2
|
||||
OpSuccess OpCode = 3
|
||||
)
|
||||
|
||||
type Payload struct {
|
||||
OpCode OpCode
|
||||
MSCHAPv2ID uint8
|
||||
MSLength uint16
|
||||
ValueSize uint8
|
||||
|
||||
Challenge []byte
|
||||
Response []byte
|
||||
|
||||
Name []byte
|
||||
|
||||
st *State
|
||||
}
|
||||
|
||||
func (p *Payload) Type() protocol.Type {
|
||||
@ -18,18 +52,100 @@ func (p *Payload) Type() protocol.Type {
|
||||
}
|
||||
|
||||
func (p *Payload) Decode(raw []byte) error {
|
||||
log.WithField("raw", debug.FormatBytes(raw)).Debugf("MSCHAPv2: decode raw")
|
||||
p.OpCode = OpCode(raw[0])
|
||||
if p.OpCode == OpSuccess {
|
||||
return nil
|
||||
}
|
||||
// TODO: Validate against root EAP packet
|
||||
p.MSCHAPv2ID = raw[1]
|
||||
p.MSLength = binary.BigEndian.Uint16(raw[2:])
|
||||
|
||||
p.ValueSize = raw[4]
|
||||
if p.ValueSize != responseValueSize {
|
||||
return fmt.Errorf("mschapv2: incorrect value size: %d", p.ValueSize)
|
||||
}
|
||||
p.Response = raw[5 : p.ValueSize+5]
|
||||
p.Name = raw[5+p.ValueSize:]
|
||||
if int(p.MSLength) != len(raw) {
|
||||
return fmt.Errorf("MSCHAPv2: incorrect MS-Length: %d, should be %d", p.MSLength, len(raw))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *Payload) Encode() ([]byte, error) {
|
||||
return []byte{}, nil
|
||||
encoded := []byte{
|
||||
byte(p.OpCode),
|
||||
p.MSCHAPv2ID,
|
||||
0,
|
||||
0,
|
||||
byte(len(p.Challenge)),
|
||||
}
|
||||
encoded = append(encoded, p.Challenge...)
|
||||
encoded = append(encoded, p.Name...)
|
||||
p.MSLength = uint16(len(encoded))
|
||||
binary.BigEndian.PutUint16(encoded[2:], p.MSLength)
|
||||
return encoded, nil
|
||||
}
|
||||
|
||||
func (p *Payload) Handle(ctx protocol.Context) protocol.Payload {
|
||||
defer func() {
|
||||
ctx.SetProtocolState(TypeMSCHAPv2, p.st)
|
||||
}()
|
||||
|
||||
rootEap := ctx.RootPayload().(*eap.Payload)
|
||||
|
||||
if ctx.IsProtocolStart(TypeMSCHAPv2) {
|
||||
ctx.EndInnerProtocol(protocol.StatusError, nil)
|
||||
ctx.Log().Debug("MSCHAPv2: Empty state, starting")
|
||||
p.st = &State{
|
||||
Challenge: securecookie.GenerateRandomKey(challengeValueSize),
|
||||
}
|
||||
return &Payload{
|
||||
OpCode: OpChallenge,
|
||||
MSCHAPv2ID: rootEap.ID + 1,
|
||||
Challenge: p.st.Challenge,
|
||||
Name: []byte("authentik"),
|
||||
}
|
||||
}
|
||||
return nil
|
||||
p.st = ctx.GetProtocolState(TypeMSCHAPv2).(*State)
|
||||
|
||||
response := &Payload{
|
||||
MSCHAPv2ID: rootEap.ID + 1,
|
||||
}
|
||||
|
||||
ctx.Log().Debugf("MSCHAPv2: OpCode: %d", p.OpCode)
|
||||
if p.OpCode == OpResponse {
|
||||
res, err := ParseResponse(p.Response)
|
||||
if err != nil {
|
||||
ctx.Log().WithError(err).Warning("MSCHAPv2: failed to parse response")
|
||||
return nil
|
||||
}
|
||||
p.st.PeerChallenge = res.Challenge
|
||||
auth, err := p.checkChapPassword(res)
|
||||
if err != nil {
|
||||
ctx.Log().WithError(err).Warning("MSCHAPv2: failed to check password")
|
||||
return nil
|
||||
}
|
||||
ctx.Log().Info("MSCHAPv2: Successfully checked password")
|
||||
succ := &SuccessRequest{
|
||||
Payload: &Payload{
|
||||
OpCode: OpSuccess,
|
||||
},
|
||||
Authenticator: auth,
|
||||
}
|
||||
return succ
|
||||
} else if p.OpCode == OpSuccess {
|
||||
return &peap.ExtensionPayload{
|
||||
AVPs: []peap.ExtensionAVP{
|
||||
{
|
||||
Mandatory: true,
|
||||
Type: peap.AVPAckResult,
|
||||
Value: []byte{0, 1},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
return response
|
||||
}
|
||||
|
||||
func (p *Payload) Offerable() bool {
|
||||
@ -37,5 +153,9 @@ func (p *Payload) Offerable() bool {
|
||||
}
|
||||
|
||||
func (p *Payload) String() string {
|
||||
return "<MSCHAPv2 Packet >"
|
||||
return fmt.Sprintf(
|
||||
"<MSCHAPv2 Packet OpCode=%d, MSCHAPv2ID=%d>",
|
||||
p.OpCode,
|
||||
p.MSCHAPv2ID,
|
||||
)
|
||||
}
|
||||
|
6
internal/outpost/radius/eap/protocol/mschapv2/state.go
Normal file
6
internal/outpost/radius/eap/protocol/mschapv2/state.go
Normal file
@ -0,0 +1,6 @@
|
||||
package mschapv2
|
||||
|
||||
type State struct {
|
||||
Challenge []byte
|
||||
PeerChallenge []byte
|
||||
}
|
37
internal/outpost/radius/eap/protocol/peap/extension.go
Normal file
37
internal/outpost/radius/eap/protocol/peap/extension.go
Normal file
@ -0,0 +1,37 @@
|
||||
package peap
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"goauthentik.io/internal/outpost/radius/eap/protocol"
|
||||
)
|
||||
|
||||
const TypePEAPExtension protocol.Type = 33
|
||||
|
||||
type ExtensionPayload struct {
|
||||
AVPs []ExtensionAVP
|
||||
}
|
||||
|
||||
func (ep *ExtensionPayload) Decode(raw []byte) error {
|
||||
return errors.New("PEAP: Extension Payload does not support decoding")
|
||||
}
|
||||
|
||||
func (ep *ExtensionPayload) Encode() ([]byte, error) {
|
||||
return []byte{}, nil
|
||||
}
|
||||
|
||||
func (ep *ExtensionPayload) Handle(protocol.Context) protocol.Payload {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ep *ExtensionPayload) Offerable() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (ep *ExtensionPayload) String() string {
|
||||
return "<PEAP Extension Payload>"
|
||||
}
|
||||
|
||||
func (ep *ExtensionPayload) Type() protocol.Type {
|
||||
return TypePEAPExtension
|
||||
}
|
35
internal/outpost/radius/eap/protocol/peap/extension_avp.go
Normal file
35
internal/outpost/radius/eap/protocol/peap/extension_avp.go
Normal file
@ -0,0 +1,35 @@
|
||||
package peap
|
||||
|
||||
import "encoding/binary"
|
||||
|
||||
type AVPType uint16
|
||||
|
||||
const (
|
||||
AVPAckResult AVPType = 3
|
||||
)
|
||||
|
||||
type ExtensionAVP struct {
|
||||
Mandatory bool
|
||||
Type AVPType // 14-bit field
|
||||
Length uint16
|
||||
Value []byte
|
||||
}
|
||||
|
||||
func (eavp ExtensionAVP) Encode() []byte {
|
||||
buff := []byte{
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
}
|
||||
t := uint16(eavp.Type)
|
||||
// Type is a 14-bit number, the highest bit is the mandatory flag
|
||||
if eavp.Mandatory {
|
||||
t = t | 0b1000000000000000
|
||||
}
|
||||
// The next bit is reserved and should always be set to 0
|
||||
t = t & 0b1011111111111111
|
||||
binary.BigEndian.AppendUint16(buff, t)
|
||||
binary.BigEndian.AppendUint16(buff[2:], uint16(len(eavp.Value)))
|
||||
return append(buff, eavp.Value...)
|
||||
}
|
Reference in New Issue
Block a user