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
	 Jens Langhammer
					Jens Langhammer