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) {
|
if len(raw) > 4 && (p.Code == protocol.CodeRequest || p.Code == protocol.CodeResponse) {
|
||||||
p.MsgType = protocol.Type(raw[4])
|
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:]
|
p.RawPayload = raw[5:]
|
||||||
pp, _, err := EmptyPayload(p.Settings, p.MsgType)
|
pp, _, err := EmptyPayload(p.Settings, p.MsgType)
|
||||||
if err != nil {
|
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
|
package mschapv2
|
||||||
|
|
||||||
import (
|
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"
|
||||||
|
"goauthentik.io/internal/outpost/radius/eap/protocol/eap"
|
||||||
|
"goauthentik.io/internal/outpost/radius/eap/protocol/peap"
|
||||||
)
|
)
|
||||||
|
|
||||||
const TypeMSCHAPv2 protocol.Type = 26
|
const TypeMSCHAPv2 protocol.Type = 26
|
||||||
@ -10,7 +18,33 @@ func Protocol() protocol.Payload {
|
|||||||
return &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 {
|
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 {
|
func (p *Payload) Type() protocol.Type {
|
||||||
@ -18,18 +52,100 @@ func (p *Payload) Type() protocol.Type {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (p *Payload) Decode(raw []byte) error {
|
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
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *Payload) Encode() ([]byte, error) {
|
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 {
|
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) {
|
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 {
|
func (p *Payload) Offerable() bool {
|
||||||
@ -37,5 +153,9 @@ func (p *Payload) Offerable() bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (p *Payload) String() string {
|
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