From d8f50310c8e21497b7ea82077cc3e3d0ec555239 Mon Sep 17 00:00:00 2001 From: TeoV Date: Tue, 24 Mar 2020 18:01:07 +0200 Subject: [PATCH] Update server with mschapv2 auth --- .idea/.gitignore | 5 ++ .idea/cgrates.iml | 17 +++++ .idea/misc.xml | 6 ++ .idea/modules.xml | 8 +++ .idea/vcs.xml | 6 ++ .idea/watcherTasks.xml | 8 +++ agents/librad.go | 49 +++++++++++++-- agents/radagent.go | 10 +-- agents/radagent_it_test.go | 126 ++++++++++++++++++++++++++++++++++++- go.mod | 2 +- go.sum | 26 -------- 11 files changed, 225 insertions(+), 38 deletions(-) create mode 100644 .idea/.gitignore create mode 100644 .idea/cgrates.iml create mode 100644 .idea/misc.xml create mode 100644 .idea/modules.xml create mode 100644 .idea/vcs.xml create mode 100644 .idea/watcherTasks.xml diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 000000000..3cd2ed320 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,5 @@ +# Default ignored files +/workspace.xml +# Project exclude paths +/. + diff --git a/.idea/cgrates.iml b/.idea/cgrates.iml new file mode 100644 index 000000000..d675c01bf --- /dev/null +++ b/.idea/cgrates.iml @@ -0,0 +1,17 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 000000000..28a804d89 --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 000000000..6f72045fc --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 000000000..94a25f7f4 --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/.idea/watcherTasks.xml b/.idea/watcherTasks.xml new file mode 100644 index 000000000..cb1a78370 --- /dev/null +++ b/.idea/watcherTasks.xml @@ -0,0 +1,8 @@ + + + + + + + \ No newline at end of file diff --git a/agents/librad.go b/agents/librad.go index 14a30110a..c6e63aaca 100644 --- a/agents/librad.go +++ b/agents/librad.go @@ -122,7 +122,7 @@ func (pk *radiusDP) RemoteHost() net.Addr { //radauthReq is used to authorize a request //if User-Password avp is present use PAP auth //if CHAP-Password is presented use CHAP auth -func radauthReq(req *radigo.Packet, aReq *AgentRequest) (bool, error) { +func radauthReq(req *radigo.Packet, aReq *AgentRequest, rpl *radigo.Packet) (bool, error) { // try to get UserPassword from Vars as slice of NMItems nmItems, err := aReq.Vars.FieldAsInterface([]string{utils.UserPassword}) if err != nil { @@ -130,16 +130,57 @@ func radauthReq(req *radigo.Packet, aReq *AgentRequest) (bool, error) { } userPassAvps := req.AttributesWithName("User-Password", utils.EmptyString) chapAVPs := req.AttributesWithName("CHAP-Password", utils.EmptyString) - if len(userPassAvps) == 0 && len(chapAVPs) == 0 { + msChallenge := req.AttributesWithName("MS-CHAP-Challenge", "Microsoft") + msResponse := req.AttributesWithName("MS-CHAP-Response", "Microsoft") + if len(userPassAvps) == 0 && len(chapAVPs) == 0 && len(msChallenge) == 0 && len(msResponse) == 0 { return false, fmt.Errorf("cannot find User-Password or CHAP-Password AVP in request") } - if len(userPassAvps) != 0 { + switch { + case len(userPassAvps) != 0: if userPassAvps[0].StringValue != nmItems.([]*config.NMItem)[0].Data { return false, nil } - } else { + case len(chapAVPs) != 0: return radigo.AuthenticateCHAP([]byte(utils.IfaceAsString(nmItems.([]*config.NMItem)[0].Data)), req.Authenticator[:], chapAVPs[0].RawValue), nil + case len(msChallenge) != 0 && len(msResponse) != 0: + vsaMSResponde := msResponse[0].Value.(*radigo.VSA) + vsaMSChallange := msChallenge[0].Value.(*radigo.VSA) + + userName := req.AttributesWithName("User-Name", utils.EmptyString)[0].StringValue + passwordFromAttributes := utils.IfaceAsString(nmItems.([]*config.NMItem)[0].Data) + + if len(vsaMSChallange.RawValue) != 16 || len(vsaMSResponde.RawValue) != 50 { + return false, nil + } + ident := vsaMSResponde.RawValue[0] + peerChallenge := vsaMSResponde.RawValue[2:18] + peerResponse := vsaMSResponde.RawValue[26:50] + ntResponse, err := radigo.GenerateNTResponse(vsaMSChallange.RawValue, + peerChallenge, userName, passwordFromAttributes) + if err != nil { + return false, err + } + if len(ntResponse) != len(peerResponse) { + return false, nil + } + for i := range ntResponse { + if ntResponse[i] != peerResponse[i] { + return false, nil + } + } + + authenticatorResponse, err := radigo.GenerateAuthenticatorResponse(vsaMSChallange.RawValue, peerChallenge, + ntResponse, userName, passwordFromAttributes) + if err != nil { + return false, err + } + success := make([]byte, 43) + success[0] = ident + copy(success[1:], authenticatorResponse) + // this AVP need to be added to be verified on the client side + rpl.AddAVPWithName("MS-CHAP2-Success", string(success), "Microsoft") } + return true, nil } diff --git a/agents/radagent.go b/agents/radagent.go index ff8710289..201979d4a 100644 --- a/agents/radagent.go +++ b/agents/radagent.go @@ -87,7 +87,7 @@ func (ra *RadiusAgent) handleAuth(req *radigo.Packet) (rpl *radigo.Packet, err e ra.filterS, nil, nil) agReq.Vars.Set([]string{MetaRadReqType}, utils.StringToInterface(MetaRadAuth), false, true) var lclProcessed bool - if lclProcessed, err = ra.processRequest(req, reqProcessor, agReq); lclProcessed { + if lclProcessed, err = ra.processRequest(req, reqProcessor, agReq, rpl); lclProcessed { processed = lclProcessed } if err != nil || (lclProcessed && !reqProcessor.Flags.GetBool(utils.MetaContinue)) { @@ -131,7 +131,7 @@ func (ra *RadiusAgent) handleAcct(req *radigo.Packet) (rpl *radigo.Packet, err e config.CgrConfig().GeneralCfg().DefaultTimezone), ra.filterS, nil, nil) var lclProcessed bool - if lclProcessed, err = ra.processRequest(req, reqProcessor, agReq); lclProcessed { + if lclProcessed, err = ra.processRequest(req, reqProcessor, agReq, rpl); lclProcessed { processed = lclProcessed } if err != nil || (lclProcessed && !reqProcessor.Flags.GetBool(utils.MetaContinue)) { @@ -157,7 +157,7 @@ func (ra *RadiusAgent) handleAcct(req *radigo.Packet) (rpl *radigo.Packet, err e // processRequest represents one processor processing the request func (ra *RadiusAgent) processRequest(req *radigo.Packet, reqProcessor *config.RequestProcessor, - agReq *AgentRequest) (processed bool, err error) { + agReq *AgentRequest, rpl *radigo.Packet) (processed bool, err error) { if pass, err := ra.filterS.Pass(agReq.Tenant, reqProcessor.Filters, agReq); err != nil || !pass { return pass, err @@ -304,9 +304,9 @@ func (ra *RadiusAgent) processRequest(req *radigo.Packet, reqProcessor *config.R } case utils.MetaCDRs: // allow this method case utils.MetaRadauth: - if ok, err := radauthReq(req, agReq); err != nil { + if pass, err := radauthReq(req, agReq, rpl); err != nil { return false, err - } else if !ok { + } else if !pass { agReq.CGRReply.Set([]string{utils.Error}, utils.RadauthFailed, false, false) } } diff --git a/agents/radagent_it_test.go b/agents/radagent_it_test.go index a7ea35d50..b4834496d 100644 --- a/agents/radagent_it_test.go +++ b/agents/radagent_it_test.go @@ -54,6 +54,8 @@ var ( testRAitAuthPAPFail, testRAitAuthCHAPSuccess, testRAitAuthCHAPFail, + testRAitAuthMSCHAPV2Success, + testRAitAuthMSCHAPV2Fail, testRAitAcctStart, testRAitAcctStop, testRAitStopCgrEngine, @@ -62,7 +64,7 @@ var ( // Test start here func TestRAit(t *testing.T) { - engine.KillEngine(0) + //engine.KillEngine(0) switch *dbType { case utils.MetaInternal: raonfigDIR = "radagent_internal" @@ -83,6 +85,7 @@ func TestRAit(t *testing.T) { } } +/* func TestRAitDispatcher(t *testing.T) { if *encoding == utils.MetaGOB { t.SkipNow() @@ -99,6 +102,7 @@ func TestRAitDispatcher(t *testing.T) { engine.KillEngine(100) isDispatcherActive = false } +*/ func testRAitInitCfg(t *testing.T) { raCfgPath = path.Join(*dataDir, "conf", "samples", raonfigDIR) @@ -339,7 +343,6 @@ func testRAitAuthCHAPFail(t *testing.T) { if err := authReq.AddAVPWithName("CHAP-Password", "CGRateSPassword2", ""); err != nil { t.Error(err) } - authReq.AVPs[1].RawValue = radigo.EncodeCHAPPassword([]byte("CGRateSPassword2"), authReq.Authenticator[:]) if err := authReq.AddAVPWithName("Service-Type", "SIP-Caller-AVPs", ""); err != nil { t.Error(err) @@ -373,6 +376,125 @@ func testRAitAuthCHAPFail(t *testing.T) { } } +func testRAitAuthMSCHAPV2Success(t *testing.T) { + for _, dictPath := range raCfg.RadiusAgentCfg().ClientDictionaries { + if dictRad, err = radigo.NewDictionaryFromFolderWithRFC2865(dictPath); err != nil { + return + } + } + if raAuthClnt, err = radigo.NewClient("udp", "127.0.0.1:1812", "CGRateS.org", dictRad, 1, nil); err != nil { + t.Fatal(err) + } + authReq := raAuthClnt.NewRequest(radigo.AccessRequest, 1) // emulates Kamailio packet out of radius_load_caller_avps() + if err := authReq.AddAVPWithName("User-Name", "1001", ""); err != nil { + t.Error(err) + } + if err := authReq.AddAVPWithName("MS-CHAP-Challenge", string(authReq.Authenticator[:]), "Microsoft"); err != nil { + t.Error(err) + } + + respVal, err := radigo.GenerateClientMSCHAPResponse(authReq.Authenticator, "1001", "CGRateSPassword1") + if err != nil { + t.Error(err) + } + if err := authReq.AddAVPWithName("MS-CHAP-Response", string(respVal), "Microsoft"); err != nil { + t.Error(err) + } + if err := authReq.AddAVPWithName("Service-Type", "SIP-Caller-AVPs", ""); err != nil { + t.Error(err) + } + if err := authReq.AddAVPWithName("Called-Station-Id", "1002", ""); err != nil { + t.Error(err) + } + if err := authReq.AddAVPWithName("Acct-Session-Id", "e4921177ab0e3586c37f6a185864b71a@0:0:0:0:0:0:0:0", ""); err != nil { + t.Error(err) + } + if err := authReq.AddAVPWithName("Sip-From-Tag", "51585361", ""); err != nil { + t.Error(err) + } + if err := authReq.AddAVPWithName("NAS-IP-Address", "127.0.0.1", ""); err != nil { + t.Error(err) + } + if err := authReq.AddAVPWithName("Event-Timestamp", "1497106115", ""); err != nil { + t.Error(err) + } + reply, err := raAuthClnt.SendRequest(authReq) + if err != nil { + t.Fatal(err) + } + if reply.Code != radigo.AccessAccept { + t.Errorf("Received reply: %+v", utils.ToJSON(reply)) + } + if len(reply.AVPs) != 2 { // make sure max duration is received + t.Errorf("Received AVPs: %+v", utils.ToJSON(reply.AVPs)) + } else { + for _, avp := range reply.AVPs { + if avp.Number == 26 && len(avp.RawValue) != 49 { + t.Errorf("Unexpected lenght of MS-CHAP2-Success AVP: %+v", len(avp.RawValue)) + } + if avp.Number == 225 && !reflect.DeepEqual([]byte("session_max_time#10800"), reply.AVPs[1].RawValue) { + t.Errorf("Received: %s", string(reply.AVPs[0].RawValue)) + } + } + } + +} + +func testRAitAuthMSCHAPV2Fail(t *testing.T) { + for _, dictPath := range raCfg.RadiusAgentCfg().ClientDictionaries { + if dictRad, err = radigo.NewDictionaryFromFolderWithRFC2865(dictPath); err != nil { + return + } + } + if raAuthClnt, err = radigo.NewClient("udp", "127.0.0.1:1812", "CGRateS.org", dictRad, 1, nil); err != nil { + t.Fatal(err) + } + authReq := raAuthClnt.NewRequest(radigo.AccessRequest, 1) // emulates Kamailio packet out of radius_load_caller_avps() + if err := authReq.AddAVPWithName("User-Name", "1001", ""); err != nil { + t.Error(err) + } + if err := authReq.AddAVPWithName("MS-CHAP-Challenge", string(authReq.Authenticator[:]), "Microsoft"); err != nil { + t.Error(err) + } + respVal, err := radigo.GenerateClientMSCHAPResponse(authReq.Authenticator, "1001", "CGRateSPassword2") + if err != nil { + t.Error(err) + } + if err := authReq.AddAVPWithName("MS-CHAP-Response", string(respVal), "Microsoft"); err != nil { + t.Error(err) + } + if err := authReq.AddAVPWithName("Service-Type", "SIP-Caller-AVPs", ""); err != nil { + t.Error(err) + } + if err := authReq.AddAVPWithName("Called-Station-Id", "1002", ""); err != nil { + t.Error(err) + } + if err := authReq.AddAVPWithName("Acct-Session-Id", "e4921177ab0e3586c37f6a185864b71a@0:0:0:0:0:0:0:0", ""); err != nil { + t.Error(err) + } + if err := authReq.AddAVPWithName("Sip-From-Tag", "51585361", ""); err != nil { + t.Error(err) + } + if err := authReq.AddAVPWithName("NAS-IP-Address", "127.0.0.1", ""); err != nil { + t.Error(err) + } + if err := authReq.AddAVPWithName("Event-Timestamp", "1497106115", ""); err != nil { + t.Error(err) + } + reply, err := raAuthClnt.SendRequest(authReq) + if err != nil { + t.Fatal(err) + } + if reply.Code != radigo.AccessReject { + t.Errorf("Received reply: %+v", reply) + } + if len(reply.AVPs) != 1 { // make sure max duration is received + t.Errorf("Received AVPs: %+v", reply.AVPs) + } else if !reflect.DeepEqual(utils.RadauthFailed, string(reply.AVPs[0].RawValue)) { + t.Errorf("Received: %s", string(reply.AVPs[0].RawValue)) + } +} + func testRAitAcctStart(t *testing.T) { if raAcctClnt, err = radigo.NewClient("udp", "127.0.0.1:1813", "CGRateS.org", dictRad, 1, nil); err != nil { t.Fatal(err) diff --git a/go.mod b/go.mod index 4d5a98818..27fc75e47 100644 --- a/go.mod +++ b/go.mod @@ -2,7 +2,7 @@ module github.com/cgrates/cgrates go 1.14 -replace github.com/cgrates/radigo => ../radigo +// replace github.com/cgrates/radigo => ../radigo // replace github.com/cgrates/rpcclient => ../rpcclient diff --git a/go.sum b/go.sum index 4349bc91c..11d61bd34 100644 --- a/go.sum +++ b/go.sum @@ -55,38 +55,12 @@ github.com/cgrates/aringo v0.0.0-20181115151919-f996da7890ea h1:KvikDlQoQdSWBc2E github.com/cgrates/aringo v0.0.0-20181115151919-f996da7890ea/go.mod h1:rGqp3Xbv+cXkzlJQ12SEz7a0wx5k7wVsVPAMFszuyCA= github.com/cgrates/fsock v0.0.0-20190623100231-317895b42f1a h1:GTXhk4FSzdyLkJgK5J8l2g1MR6nMnl0KPZKoLlLuEk8= github.com/cgrates/fsock v0.0.0-20190623100231-317895b42f1a/go.mod h1:uWcTZ01eKJdWcoYBhsEXKgSlO6Vv8wT9qcR5JeJovBs= -github.com/cgrates/kamevapi v0.0.0-20190605144325-ec679e017654 h1:D2NqOOYKha71Ab6WfZBCJpWZOyr8BPpAm3q5a1OWFBI= -github.com/cgrates/kamevapi v0.0.0-20190605144325-ec679e017654/go.mod h1:pgHqPlPcEDIQbbs9wyBk7YZTcaVdxMqf3v04XU+mngI= -github.com/cgrates/kamevapi v0.0.0-20191001070356-51d06bab2926 h1:eb9F8blPHqBnAyLkW5U0eTTV0/j2tMrVTIBVxcm8bXY= -github.com/cgrates/kamevapi v0.0.0-20191001070356-51d06bab2926/go.mod h1:pgHqPlPcEDIQbbs9wyBk7YZTcaVdxMqf3v04XU+mngI= github.com/cgrates/kamevapi v0.0.0-20191001125829-7dbc3ad58817 h1:1Tdv6H/usqmkQVeQbd+x87L5xo6DFmbYpbI00qfrWGw= github.com/cgrates/kamevapi v0.0.0-20191001125829-7dbc3ad58817/go.mod h1:pgHqPlPcEDIQbbs9wyBk7YZTcaVdxMqf3v04XU+mngI= github.com/cgrates/ltcache v0.0.0-20181016092649-92fb7fa77cca h1:Ejj4m0Ccl8dMMVnoHk4nQMlbR3w24llqQDy66DO9E0A= github.com/cgrates/ltcache v0.0.0-20181016092649-92fb7fa77cca/go.mod h1:q7c996DUu8OrJRnewVSQzM+y/bRcxZAHoo+zCD8bFBo= -github.com/cgrates/radigo v0.0.0-20181207143118-e5c8f3272ccc h1:vOvPAyI9pNhUM5k/Wc3mYvwmGpDrMg41BMkz4AW1wfg= -github.com/cgrates/radigo v0.0.0-20181207143118-e5c8f3272ccc/go.mod h1:cMU/VXvC9YH2kXbhpgnkppYRjpqS8XgkKb8dI8HpU1I= -github.com/cgrates/radigo v0.0.0-20200101201328-55baf0f8c432 h1:JlaJ1vGsR9EmvamLffFoW5AILBeaoCOhuLLANIUSgpk= -github.com/cgrates/radigo v0.0.0-20200101201328-55baf0f8c432/go.mod h1:mTCzHAYfgZlRe0HorDz+jy2JTrNvNuKkHBAUjDZBWq8= -github.com/cgrates/radigo v0.0.0-20200102144505-ef98592ff532 h1:Lyuz+ROAx4d4ep5HKeiBFFC2OaK/yyJ5KTFRquFOyrQ= -github.com/cgrates/radigo v0.0.0-20200102144505-ef98592ff532/go.mod h1:mTCzHAYfgZlRe0HorDz+jy2JTrNvNuKkHBAUjDZBWq8= -github.com/cgrates/radigo v0.0.0-20200306160903-17b28bb0e1bb h1:LnoYQFohxLduxNFZHucwM8OKOjw59Gr78zk9y5XKhbw= -github.com/cgrates/radigo v0.0.0-20200306160903-17b28bb0e1bb/go.mod h1:mTCzHAYfgZlRe0HorDz+jy2JTrNvNuKkHBAUjDZBWq8= -github.com/cgrates/radigo v0.0.0-20200309151443-bb470a5a5c8d h1:4dDI8QG+rkQTNWwsRmeAQWLaofRvVRd3JgG/h4o9VG0= -github.com/cgrates/radigo v0.0.0-20200309151443-bb470a5a5c8d/go.mod h1:mTCzHAYfgZlRe0HorDz+jy2JTrNvNuKkHBAUjDZBWq8= -github.com/cgrates/radigo v0.0.0-20200318092814-07da25249ae6 h1:NLRfnSh1TGtCrgxVnpaSOEiwprmf/sQ+aOTM1kiEvaE= -github.com/cgrates/radigo v0.0.0-20200318092814-07da25249ae6/go.mod h1:mTCzHAYfgZlRe0HorDz+jy2JTrNvNuKkHBAUjDZBWq8= github.com/cgrates/radigo v0.0.0-20200321121249-9e416fdf1479 h1:UNareDs0BcycjiSy2ltRlsiaeoohDdt9OQOg55ak5Jc= github.com/cgrates/radigo v0.0.0-20200321121249-9e416fdf1479/go.mod h1:mTCzHAYfgZlRe0HorDz+jy2JTrNvNuKkHBAUjDZBWq8= -github.com/cgrates/rpcclient v0.0.0-20190505150825-8fcc68b2c38b h1:GC+/hEDN/2Frh8Tjkf7u1XFxj0Z2XtwjBxj0OH6Mzhw= -github.com/cgrates/rpcclient v0.0.0-20190505150825-8fcc68b2c38b/go.mod h1:Jy5Lv0y57OlxlNATKrkyAxgftYLHqXuxONgd4qsAC1U= -github.com/cgrates/rpcclient v0.0.0-20191115092211-732f09b356e3 h1:Hr038ZfPZz87OKLV4pRSzf3U06lZ8zjl/cXpwrv7hCM= -github.com/cgrates/rpcclient v0.0.0-20191115092211-732f09b356e3/go.mod h1:Jy5Lv0y57OlxlNATKrkyAxgftYLHqXuxONgd4qsAC1U= -github.com/cgrates/rpcclient v0.0.0-20191209100218-70f91dc30ac6 h1:g1LZmbYvqYkGAxM4/bNfTcBooCFf0lK2mgrp3yZERL0= -github.com/cgrates/rpcclient v0.0.0-20191209100218-70f91dc30ac6/go.mod h1:xXLqAKVvcdWeDYwHJYwDgAI3ZOg5LZYxzb72kLjsLZU= -github.com/cgrates/rpcclient v0.0.0-20191212101551-ff9c136f66a7 h1:szJPaMz/49gP0X6M1DhjhImDP3rLN8ul7rio2tzorNc= -github.com/cgrates/rpcclient v0.0.0-20191212101551-ff9c136f66a7/go.mod h1:xXLqAKVvcdWeDYwHJYwDgAI3ZOg5LZYxzb72kLjsLZU= -github.com/cgrates/rpcclient v0.0.0-20200107085551-6be8ad1df845 h1:bzvXBBqUqf2q4nW8RAwy+BXwxzlxFSGUAkW8W0TNLk8= -github.com/cgrates/rpcclient v0.0.0-20200107085551-6be8ad1df845/go.mod h1:xXLqAKVvcdWeDYwHJYwDgAI3ZOg5LZYxzb72kLjsLZU= github.com/cgrates/rpcclient v0.0.0-20200107134035-188454eb71b3 h1:esWCSaLD2j+zfK9JpyPXFSe3c97MQFhfKp9jDiss1mU= github.com/cgrates/rpcclient v0.0.0-20200107134035-188454eb71b3/go.mod h1:xXLqAKVvcdWeDYwHJYwDgAI3ZOg5LZYxzb72kLjsLZU= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=