diff --git a/cmd/main.go b/cmd/main.go index 8f0bdb4..2f30a2c 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -9,9 +9,12 @@ package main import ( + "context" "os" + "os/signal" "path/filepath" "runtime/debug" + "syscall" "github.com/urfave/cli" @@ -60,19 +63,30 @@ func action(cliCtx *cli.Context) error { logger.MainLog.Infoln("AUSF version: ", version.GetVersion()) + ctx, cancel := context.WithCancel(context.Background()) + sigCh := make(chan os.Signal, 1) + signal.Notify(sigCh, os.Interrupt, syscall.SIGTERM) + + go func() { + <-sigCh // Wait for interrupt signal to gracefully shutdown + cancel() // Notify each goroutine and wait them stopped + }() + cfg, err := factory.ReadConfig(cliCtx.String("config")) if err != nil { + sigCh <- nil return err } factory.AusfConfig = cfg - ausf, err := service.NewApp(cfg) + ausf, err := service.NewApp(ctx, cfg, tlsKeyLogPath) if err != nil { + sigCh <- nil return err } AUSF = ausf - ausf.Start(tlsKeyLogPath) + ausf.Start() return nil } diff --git a/go.mod b/go.mod index 95e5734..37e0437 100644 --- a/go.mod +++ b/go.mod @@ -13,7 +13,6 @@ require ( github.com/google/uuid v1.3.0 github.com/pkg/errors v0.9.1 github.com/sirupsen/logrus v1.8.1 - github.com/stretchr/testify v1.8.3 github.com/urfave/cli v1.22.5 gopkg.in/yaml.v2 v2.4.0 ) @@ -22,7 +21,6 @@ require ( github.com/bytedance/sonic v1.9.1 // indirect github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d // indirect - github.com/davecgh/go-spew v1.1.1 // indirect github.com/gabriel-vasile/mimetype v1.4.2 // indirect github.com/gin-contrib/sse v0.1.0 // indirect github.com/go-playground/locales v0.14.1 // indirect @@ -40,7 +38,6 @@ require ( github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect github.com/pelletier/go-toml/v2 v2.0.8 // indirect - github.com/pmezard/go-difflib v1.0.0 // indirect github.com/russross/blackfriday/v2 v2.0.1 // indirect github.com/shurcooL/sanitized_anchor_name v1.0.0 // indirect github.com/tim-ywliu/nested-logrus-formatter v1.3.2 // indirect diff --git a/internal/logger/logger.go b/internal/logger/logger.go index 90993e0..76327e9 100644 --- a/internal/logger/logger.go +++ b/internal/logger/logger.go @@ -13,6 +13,7 @@ var ( InitLog *logrus.Entry CfgLog *logrus.Entry CtxLog *logrus.Entry + SBILog *logrus.Entry GinLog *logrus.Entry ConsumerLog *logrus.Entry UeAuthLog *logrus.Entry @@ -33,6 +34,7 @@ func init() { InitLog = NfLog.WithField(logger_util.FieldCategory, "Init") CfgLog = NfLog.WithField(logger_util.FieldCategory, "CFG") CtxLog = NfLog.WithField(logger_util.FieldCategory, "CTX") + SBILog = NfLog.WithField(logger_util.FieldCategory, "SBI") GinLog = NfLog.WithField(logger_util.FieldCategory, "GIN") ConsumerLog = NfLog.WithField(logger_util.FieldCategory, "Consumer") UeAuthLog = NfLog.WithField(logger_util.FieldCategory, "UeAuth") diff --git a/internal/sbi/api_sorprotection.go b/internal/sbi/api_sorprotection.go new file mode 100644 index 0000000..ff5ebf3 --- /dev/null +++ b/internal/sbi/api_sorprotection.go @@ -0,0 +1,26 @@ +package sbi + +import ( + "net/http" + + "github.com/gin-gonic/gin" +) + +func (s *Server) getSorprotectionRoutes() []Route { + return []Route{ + { + Method: http.MethodGet, + Pattern: "/", + APIFunc: Index, + }, + { + Method: http.MethodPost, + Pattern: "/:supi/ue-sor", + APIFunc: s.SupiUeSorPost, + }, + } +} + +func (s *Server) SupiUeSorPost(c *gin.Context) { + c.JSON(http.StatusNotImplemented, gin.H{}) +} diff --git a/internal/sbi/api_ueauthentication.go b/internal/sbi/api_ueauthentication.go new file mode 100644 index 0000000..073a6b1 --- /dev/null +++ b/internal/sbi/api_ueauthentication.go @@ -0,0 +1,153 @@ +/* + * Nausf_UeAuthentication + * + * UeAuthentication Service + * © 2021, 3GPP Organizational Partners (ARIB, ATIS, CCSA, ETSI, TSDSI, TTA, TTC). + * All rights reserved. + * + * API version: 3.0.3 + * Generated by: OpenAPI Generator (https://openapi-generator.tech) + */ + +package sbi + +import ( + "net/http" + + "github.com/gin-gonic/gin" + + "github.com/free5gc/ausf/internal/logger" + "github.com/free5gc/openapi" + "github.com/free5gc/openapi/models" +) + +// Index is the index handler. +func Index(c *gin.Context) { + c.String(http.StatusOK, "Hello World!") +} + +func (s *Server) getUeAuthenticationRoutes() []Route { + return []Route{ + { + Method: http.MethodGet, + Pattern: "/", + APIFunc: Index, + }, + { + Method: http.MethodPost, + Pattern: "/ue-authentications/:authCtxId/eap-session", + APIFunc: s.EapAuthMethodPost, + }, + { + Method: http.MethodPost, + Pattern: "/ue-authentications", + APIFunc: s.UeAuthenticationsPost, + }, + { + Method: http.MethodPut, + Pattern: "/ue-authentications/:authCtxId/5g-aka-confirmation", + APIFunc: s.UeAuthenticationsAuthCtxID5gAkaConfirmationPut, + }, + } +} + +// EapAuthMethodPost - +func (s *Server) EapAuthMethodPost(c *gin.Context) { + var eapSessionReq models.EapSession + + requestBody, err := c.GetRawData() + if err != nil { + problemDetail := models.ProblemDetails{ + Title: "System failure", + Status: http.StatusInternalServerError, + Detail: err.Error(), + Cause: "SYSTEM_FAILURE", + } + logger.Auth5gAkaLog.Errorf("Get Request Body error: %+v", err) + c.JSON(http.StatusInternalServerError, problemDetail) + return + } + + err = openapi.Deserialize(&eapSessionReq, requestBody, "application/json") + if err != nil { + problemDetail := "[Request Body] " + err.Error() + rsp := models.ProblemDetails{ + Title: "Malformed request syntax", + Status: http.StatusBadRequest, + Detail: problemDetail, + } + logger.Auth5gAkaLog.Errorln(problemDetail) + c.JSON(http.StatusBadRequest, rsp) + return + } + eapSessionId := c.Param("authCtxId") + + s.Processor().HandleEapAuthComfirmRequest(c, eapSessionReq, eapSessionId) +} + +// UeAuthenticationsPost +func (s *Server) UeAuthenticationsPost(c *gin.Context) { + var authInfo models.AuthenticationInfo + + requestBody, err := c.GetRawData() + if err != nil { + problemDetail := models.ProblemDetails{ + Title: "System failure", + Status: http.StatusInternalServerError, + Detail: err.Error(), + Cause: "SYSTEM_FAILURE", + } + logger.UeAuthLog.Errorf("Get Request Body error: %+v", err) + c.JSON(http.StatusInternalServerError, problemDetail) + return + } + + err = openapi.Deserialize(&authInfo, requestBody, "application/json") + if err != nil { + problemDetail := "[Request Body] " + err.Error() + rsp := models.ProblemDetails{ + Title: "Malformed request syntax", + Status: http.StatusBadRequest, + Detail: problemDetail, + } + logger.UeAuthLog.Errorln(problemDetail) + c.JSON(http.StatusBadRequest, rsp) + return + } + + s.Processor().HandleUeAuthPostRequest(c, authInfo) +} + +// UeAuthenticationsAuthCtxID5gAkaConfirmationPut +func (s *Server) UeAuthenticationsAuthCtxID5gAkaConfirmationPut(c *gin.Context) { + var confirmationData models.ConfirmationData + + requestBody, err := c.GetRawData() + if err != nil { + problemDetail := models.ProblemDetails{ + Title: "System failure", + Status: http.StatusInternalServerError, + Detail: err.Error(), + Cause: "SYSTEM_FAILURE", + } + logger.Auth5gAkaLog.Errorf("Get Request Body error: %+v", err) + c.JSON(http.StatusInternalServerError, problemDetail) + return + } + + err = openapi.Deserialize(&confirmationData, requestBody, "application/json") + if err != nil { + problemDetail := "[Request Body] " + err.Error() + rsp := models.ProblemDetails{ + Title: "Malformed request syntax", + Status: http.StatusBadRequest, + Detail: problemDetail, + } + logger.Auth5gAkaLog.Errorln(problemDetail) + c.JSON(http.StatusBadRequest, rsp) + return + } + confirmationDataResponseId := c.Param("authCtxId") + + s.Processor().HandleAuth5gAkaComfirmRequest(c, confirmationData, confirmationDataResponseId) +} diff --git a/internal/sbi/api_upuprotection.go b/internal/sbi/api_upuprotection.go new file mode 100644 index 0000000..856ebf1 --- /dev/null +++ b/internal/sbi/api_upuprotection.go @@ -0,0 +1,26 @@ +package sbi + +import ( + "net/http" + + "github.com/gin-gonic/gin" +) + +func (s *Server) getUpuprotectionRoutes() []Route { + return []Route{ + { + Method: http.MethodGet, + Pattern: "/", + APIFunc: Index, + }, + { + Method: http.MethodPost, + Pattern: "/:supi/ue-upu", + APIFunc: s.SupiUeUpuPost, + }, + } +} + +func (s *Server) SupiUeUpuPost(c *gin.Context) { + c.JSON(http.StatusNotImplemented, gin.H{}) +} diff --git a/internal/sbi/consumer/consumer.go b/internal/sbi/consumer/consumer.go new file mode 100644 index 0000000..a0cd457 --- /dev/null +++ b/internal/sbi/consumer/consumer.go @@ -0,0 +1,38 @@ +package consumer + +import ( + "github.com/free5gc/ausf/pkg/app" + "github.com/free5gc/openapi/Nnrf_NFDiscovery" + "github.com/free5gc/openapi/Nnrf_NFManagement" + "github.com/free5gc/openapi/Nudm_UEAuthentication" +) + +type ConsumerAusf interface { + app.App +} + +type Consumer struct { + ConsumerAusf + + *nnrfService + *nudmService +} + +func NewConsumer(ausf ConsumerAusf) (*Consumer, error) { + c := &Consumer{ + ConsumerAusf: ausf, + } + + c.nnrfService = &nnrfService{ + consumer: c, + nfMngmntClients: make(map[string]*Nnrf_NFManagement.APIClient), + nfDiscClients: make(map[string]*Nnrf_NFDiscovery.APIClient), + } + + c.nudmService = &nudmService{ + consumer: c, + ueauClients: make(map[string]*Nudm_UEAuthentication.APIClient), + } + + return c, nil +} diff --git a/internal/sbi/consumer/nf_discovery.go b/internal/sbi/consumer/nf_discovery.go deleted file mode 100644 index 3c97013..0000000 --- a/internal/sbi/consumer/nf_discovery.go +++ /dev/null @@ -1,40 +0,0 @@ -package consumer - -import ( - "fmt" - "net/http" - - ausf_context "github.com/free5gc/ausf/internal/context" - "github.com/free5gc/ausf/internal/logger" - "github.com/free5gc/openapi/Nnrf_NFDiscovery" - "github.com/free5gc/openapi/models" -) - -func SendSearchNFInstances(nrfUri string, targetNfType, requestNfType models.NfType, - param Nnrf_NFDiscovery.SearchNFInstancesParamOpts, -) (*models.SearchResult, error) { - ctx, _, err := ausf_context.GetSelf().GetTokenCtx(models.ServiceName_NNRF_DISC, models.NfType_NRF) - if err != nil { - return nil, err - } - - configuration := Nnrf_NFDiscovery.NewConfiguration() - configuration.SetBasePath(nrfUri) - client := Nnrf_NFDiscovery.NewAPIClient(configuration) - - result, rsp, rspErr := client.NFInstancesStoreApi.SearchNFInstances(ctx, - targetNfType, requestNfType, ¶m) - - if rspErr != nil { - return nil, fmt.Errorf("NFInstancesStoreApi Response error: %+w", rspErr) - } - defer func() { - if rspCloseErr := rsp.Body.Close(); rspCloseErr != nil { - logger.ConsumerLog.Errorf("NFInstancesStoreApi Response cannot close: %v", rspCloseErr) - } - }() - if rsp != nil && rsp.StatusCode == http.StatusTemporaryRedirect { - return nil, fmt.Errorf("Temporary Redirect For Non NRF Consumer") - } - return &result, nil -} diff --git a/internal/sbi/consumer/nf_management.go b/internal/sbi/consumer/nf_management.go deleted file mode 100644 index 816ae73..0000000 --- a/internal/sbi/consumer/nf_management.go +++ /dev/null @@ -1,125 +0,0 @@ -package consumer - -import ( - "fmt" - "net/http" - "strings" - "time" - - ausf_context "github.com/free5gc/ausf/internal/context" - "github.com/free5gc/ausf/internal/logger" - "github.com/free5gc/openapi" - "github.com/free5gc/openapi/Nnrf_NFManagement" - "github.com/free5gc/openapi/models" -) - -func BuildNFInstance(ausfContext *ausf_context.AUSFContext) (profile models.NfProfile, err error) { - profile.NfInstanceId = ausfContext.NfId - profile.NfType = models.NfType_AUSF - profile.NfStatus = models.NfStatus_REGISTERED - profile.Ipv4Addresses = append(profile.Ipv4Addresses, ausfContext.RegisterIPv4) - services := []models.NfService{} - for _, nfService := range ausfContext.NfService { - services = append(services, nfService) - } - if len(services) > 0 { - profile.NfServices = &services - } - var ausfInfo models.AusfInfo - ausfInfo.GroupId = ausfContext.GroupID - profile.AusfInfo = &ausfInfo - profile.PlmnList = &ausfContext.PlmnList - return -} - -// func SendRegisterNFInstance(nrfUri, nfInstanceId string, profile models.NfProfile, -// ) (resouceNrfUri string,retrieveNfInstanceID string, err error) { -func SendRegisterNFInstance(nrfUri, nfInstanceId string, profile models.NfProfile) (string, string, error) { - configuration := Nnrf_NFManagement.NewConfiguration() - configuration.SetBasePath(nrfUri) - client := Nnrf_NFManagement.NewAPIClient(configuration) - - ctx, _, err := ausf_context.GetSelf().GetTokenCtx(models.ServiceName_NNRF_NFM, models.NfType_NRF) - if err != nil { - return "", "", err - } - - var res *http.Response - for { - nf, resTmp, err := client.NFInstanceIDDocumentApi.RegisterNFInstance(ctx, nfInstanceId, profile) - if err != nil || resTmp == nil { - logger.ConsumerLog.Errorf("AUSF register to NRF Error[%v]", err) - time.Sleep(2 * time.Second) - continue - } else { - res = resTmp - } - defer func() { - if resCloseErr := res.Body.Close(); resCloseErr != nil { - logger.ConsumerLog.Errorf("AUSF NFInstanceIDDocumentApi response body cannot close: %+v", resCloseErr) - } - }() - status := res.StatusCode - if status == http.StatusOK { - // NFUpdate - break - } else if status == http.StatusCreated { - // NFRegister - resourceUri := res.Header.Get("Location") - resourceNrfUri := resourceUri[:strings.Index(resourceUri, "/nnrf-nfm/")] - retrieveNfInstanceID := resourceUri[strings.LastIndex(resourceUri, "/")+1:] - - oauth2 := false - if nf.CustomInfo != nil { - v, ok := nf.CustomInfo["oauth2"].(bool) - if ok { - oauth2 = v - logger.MainLog.Infoln("OAuth2 setting receive from NRF:", oauth2) - } - } - ausf_context.GetSelf().OAuth2Required = oauth2 - if oauth2 && ausf_context.GetSelf().NrfCertPem == "" { - logger.CfgLog.Error("OAuth2 enable but no nrfCertPem provided in config.") - } - - return resourceNrfUri, retrieveNfInstanceID, nil - } else { - fmt.Println(fmt.Errorf("handler returned wrong status code %d", status)) - fmt.Println(fmt.Errorf("NRF return wrong status code %d", status)) - } - } - return "", "", nil -} - -func SendDeregisterNFInstance() (*models.ProblemDetails, error) { - logger.ConsumerLog.Infof("Send Deregister NFInstance") - - ctx, pd, err := ausf_context.GetSelf().GetTokenCtx(models.ServiceName_NNRF_NFM, models.NfType_NRF) - if err != nil { - return pd, err - } - - ausfSelf := ausf_context.GetSelf() - // Set client and set url - configuration := Nnrf_NFManagement.NewConfiguration() - configuration.SetBasePath(ausfSelf.NrfUri) - client := Nnrf_NFManagement.NewAPIClient(configuration) - - res, err := client.NFInstanceIDDocumentApi.DeregisterNFInstance(ctx, ausfSelf.NfId) - if err == nil { - return nil, err - } else if res != nil { - defer func() { - if resCloseErr := res.Body.Close(); resCloseErr != nil { - logger.ConsumerLog.Errorf("NFInstanceIDDocumentApi response body cannot close: %+v", resCloseErr) - } - }() - if res.Status != err.Error() { - return nil, err - } - problem := err.(openapi.GenericOpenAPIError).Model().(models.ProblemDetails) - return &problem, err - } else { - return nil, openapi.ReportError("server no response") - } -} diff --git a/internal/sbi/consumer/nrf_service.go b/internal/sbi/consumer/nrf_service.go new file mode 100644 index 0000000..0a5a01b --- /dev/null +++ b/internal/sbi/consumer/nrf_service.go @@ -0,0 +1,250 @@ +package consumer + +import ( + "context" + "fmt" + "net/http" + "strconv" + "strings" + "sync" + "time" + + "github.com/antihax/optional" + "github.com/pkg/errors" + + ausf_context "github.com/free5gc/ausf/internal/context" + "github.com/free5gc/ausf/internal/logger" + "github.com/free5gc/openapi" + "github.com/free5gc/openapi/Nnrf_NFDiscovery" + "github.com/free5gc/openapi/Nnrf_NFManagement" + "github.com/free5gc/openapi/models" +) + +type nnrfService struct { + consumer *Consumer + + nfMngmntMu sync.RWMutex + nfDiscMu sync.RWMutex + + nfMngmntClients map[string]*Nnrf_NFManagement.APIClient + nfDiscClients map[string]*Nnrf_NFDiscovery.APIClient +} + +func (s *nnrfService) getNFManagementClient(uri string) *Nnrf_NFManagement.APIClient { + if uri == "" { + return nil + } + s.nfMngmntMu.RLock() + client, ok := s.nfMngmntClients[uri] + if ok { + s.nfMngmntMu.RUnlock() + return client + } + + configuration := Nnrf_NFManagement.NewConfiguration() + configuration.SetBasePath(uri) + client = Nnrf_NFManagement.NewAPIClient(configuration) + + s.nfMngmntMu.RUnlock() + s.nfMngmntMu.Lock() + defer s.nfMngmntMu.Unlock() + s.nfMngmntClients[uri] = client + return client +} + +func (s *nnrfService) getNFDiscClient(uri string) *Nnrf_NFDiscovery.APIClient { + if uri == "" { + return nil + } + s.nfDiscMu.RLock() + client, ok := s.nfDiscClients[uri] + if ok { + s.nfDiscMu.RUnlock() + return client + } + + configuration := Nnrf_NFDiscovery.NewConfiguration() + configuration.SetBasePath(uri) + client = Nnrf_NFDiscovery.NewAPIClient(configuration) + + s.nfDiscMu.RUnlock() + s.nfDiscMu.Lock() + defer s.nfDiscMu.Unlock() + s.nfDiscClients[uri] = client + return client +} + +func (s *nnrfService) SendSearchNFInstances( + nrfUri string, targetNfType, requestNfType models.NfType, param *Nnrf_NFDiscovery.SearchNFInstancesParamOpts) ( + *models.SearchResult, error, +) { + // Set client and set url + client := s.getNFDiscClient(nrfUri) + if client == nil { + return nil, openapi.ReportError("nrf not found") + } + + ctx, _, err := ausf_context.GetSelf().GetTokenCtx(models.ServiceName_NNRF_DISC, models.NfType_NRF) + if err != nil { + return nil, err + } + + result, res, err := client.NFInstancesStoreApi.SearchNFInstances(ctx, targetNfType, requestNfType, param) + + if res != nil && res.StatusCode == http.StatusTemporaryRedirect { + return nil, fmt.Errorf("temporary Redirect For Non NRF Consumer") + } + if res == nil || res.Body == nil { + return &result, err + } + defer func() { + if res != nil { + if bodyCloseErr := res.Body.Close(); bodyCloseErr != nil { + err = fmt.Errorf("SearchNFInstances' response body cannot close: %+w", bodyCloseErr) + } + } + }() + return &result, err +} + +func (s *nnrfService) SendDeregisterNFInstance() (problemDetails *models.ProblemDetails, err error) { + logger.ConsumerLog.Infof("Send Deregister NFInstance") + + ctx, pd, err := ausf_context.GetSelf().GetTokenCtx(models.ServiceName_NNRF_NFM, models.NfType_NRF) + if err != nil { + return pd, err + } + + ausfContext := s.consumer.Context() + client := s.getNFManagementClient(ausfContext.NrfUri) + + var res *http.Response + + res, err = client.NFInstanceIDDocumentApi.DeregisterNFInstance(ctx, ausfContext.NfId) + if err == nil { + return problemDetails, err + } else if res != nil { + defer func() { + if resCloseErr := res.Body.Close(); resCloseErr != nil { + logger.ConsumerLog.Errorf("DeregisterNFInstance response cannot close: %+v", resCloseErr) + } + }() + if res.Status != err.Error() { + return problemDetails, err + } + problem := err.(openapi.GenericOpenAPIError).Model().(models.ProblemDetails) + problemDetails = &problem + } else { + err = openapi.ReportError("server no response") + } + return problemDetails, err +} + +func (s *nnrfService) RegisterNFInstance(ctx context.Context) ( + resouceNrfUri string, retrieveNfInstanceID string, err error, +) { + ausfContext := s.consumer.Context() + + client := s.getNFManagementClient(ausfContext.NrfUri) + nfProfile, err := s.buildNfProfile(ausfContext) + if err != nil { + return "", "", errors.Wrap(err, "RegisterNFInstance buildNfProfile()") + } + + var nf models.NfProfile + var res *http.Response + for { + nf, res, err = client.NFInstanceIDDocumentApi.RegisterNFInstance(ctx, ausfContext.NfId, nfProfile) + if err != nil || res == nil { + logger.ConsumerLog.Errorf("AUSF register to NRF Error[%v]", err) + time.Sleep(2 * time.Second) + continue + } + defer func() { + if resCloseErr := res.Body.Close(); resCloseErr != nil { + logger.ConsumerLog.Errorf("RegisterNFInstance response body cannot close: %+v", resCloseErr) + } + }() + status := res.StatusCode + if status == http.StatusOK { + // NFUpdate + break + } else if status == http.StatusCreated { + // NFRegister + resourceUri := res.Header.Get("Location") + resouceNrfUri = resourceUri[:strings.Index(resourceUri, "/nnrf-nfm/")] + retrieveNfInstanceID = resourceUri[strings.LastIndex(resourceUri, "/")+1:] + + oauth2 := false + if nf.CustomInfo != nil { + v, ok := nf.CustomInfo["oauth2"].(bool) + if ok { + oauth2 = v + logger.MainLog.Infoln("OAuth2 setting receive from NRF:", oauth2) + } + } + ausf_context.GetSelf().OAuth2Required = oauth2 + if oauth2 && ausf_context.GetSelf().NrfCertPem == "" { + logger.CfgLog.Error("OAuth2 enable but no nrfCertPem provided in config.") + } + + break + } else { + logger.ConsumerLog.Errorln("NRF return wrong status code", status) + } + } + return resouceNrfUri, retrieveNfInstanceID, err +} + +func (s *nnrfService) buildNfProfile(ausfContext *ausf_context.AUSFContext) (profile models.NfProfile, err error) { + profile.NfInstanceId = ausfContext.NfId + profile.NfType = models.NfType_AUSF + profile.NfStatus = models.NfStatus_REGISTERED + profile.Ipv4Addresses = append(profile.Ipv4Addresses, ausfContext.RegisterIPv4) + services := []models.NfService{} + for _, nfService := range ausfContext.NfService { + services = append(services, nfService) + } + if len(services) > 0 { + profile.NfServices = &services + } + profile.AusfInfo = &models.AusfInfo{ + // Todo + // SupiRanges: &[]models.SupiRange{ + // { + // //from TS 29.510 6.1.6.2.9 example2 + // //no need to set supirange in this moment 2019/10/4 + // Start: "123456789040000", + // End: "123456789059999", + // Pattern: "^imsi-12345678904[0-9]{4}$", + // }, + // }, + } + return +} + +func (s *nnrfService) GetUdmUrl(nrfUri string) string { + udmUrl := "https://localhost:29503" // default + nfDiscoverParam := &Nnrf_NFDiscovery.SearchNFInstancesParamOpts{ + ServiceNames: optional.NewInterface([]models.ServiceName{models.ServiceName_NUDM_UEAU}), + } + res, err := s.SendSearchNFInstances( + nrfUri, + models.NfType_UDM, + models.NfType_AUSF, + nfDiscoverParam, + ) + if err != nil { + logger.ConsumerLog.Errorln("[Search UDM UEAU] ", err.Error(), "use defalt udmUrl", udmUrl) + } else if len(res.NfInstances) > 0 { + udmInstance := res.NfInstances[0] + if len(udmInstance.Ipv4Addresses) > 0 && udmInstance.NfServices != nil { + ueauService := (*udmInstance.NfServices)[0] + ueauEndPoint := (*ueauService.IpEndPoints)[0] + udmUrl = string(ueauService.Scheme) + "://" + ueauEndPoint.Ipv4Address + ":" + strconv.Itoa(int(ueauEndPoint.Port)) + } + } else { + logger.ConsumerLog.Errorln("[Search UDM UEAU] len(NfInstances) = 0") + } + return udmUrl +} diff --git a/internal/sbi/consumer/udm_service.go b/internal/sbi/consumer/udm_service.go new file mode 100644 index 0000000..69e2603 --- /dev/null +++ b/internal/sbi/consumer/udm_service.go @@ -0,0 +1,107 @@ +package consumer + +import ( + "sync" + "time" + + ausf_context "github.com/free5gc/ausf/internal/context" + "github.com/free5gc/ausf/internal/logger" + Nudm_UEAU "github.com/free5gc/openapi/Nudm_UEAuthentication" + "github.com/free5gc/openapi/models" +) + +type nudmService struct { + consumer *Consumer + + ueauMu sync.RWMutex + + ueauClients map[string]*Nudm_UEAU.APIClient +} + +func (s *nudmService) getUdmUeauClient(uri string) *Nudm_UEAU.APIClient { + if uri == "" { + return nil + } + s.ueauMu.RLock() + client, ok := s.ueauClients[uri] + if ok { + s.ueauMu.RUnlock() + return client + } + + configuration := Nudm_UEAU.NewConfiguration() + configuration.SetBasePath(uri) + client = Nudm_UEAU.NewAPIClient(configuration) + + s.ueauMu.RUnlock() + s.ueauMu.Lock() + defer s.ueauMu.Unlock() + s.ueauClients[uri] = client + return client +} + +func (s *nudmService) SendAuthResultToUDM( + id string, + authType models.AuthType, + success bool, + servingNetworkName, udmUrl string, +) error { + timeNow := time.Now() + timePtr := &timeNow + + self := s.consumer.Context() + + authEvent := models.AuthEvent{ + TimeStamp: timePtr, + AuthType: authType, + Success: success, + ServingNetworkName: servingNetworkName, + NfInstanceId: self.GetSelfID(), + } + + client := s.getUdmUeauClient(udmUrl) + + ctx, _, err := ausf_context.GetSelf().GetTokenCtx(models.ServiceName_NUDM_UEAU, models.NfType_UDM) + if err != nil { + return err + } + + _, rsp, confirmAuthErr := client.ConfirmAuthApi.ConfirmAuth(ctx, id, authEvent) + defer func() { + if rspCloseErr := rsp.Body.Close(); rspCloseErr != nil { + logger.ConsumerLog.Errorf("ConfirmAuth Response cannot close: %v", rspCloseErr) + } + }() + return confirmAuthErr +} + +func (s *nudmService) GenerateAuthDataApi( + udmUrl string, + supiOrSuci string, + authInfoReq models.AuthenticationInfoRequest, +) (*models.AuthenticationInfoResult, error, *models.ProblemDetails) { + client := s.getUdmUeauClient(udmUrl) + + ctx, pd, err := ausf_context.GetSelf().GetTokenCtx(models.ServiceName_NUDM_UEAU, models.NfType_UDM) + if err != nil { + return nil, err, pd + } + + authInfoResult, rsp, err := client.GenerateAuthDataApi.GenerateAuthData(ctx, supiOrSuci, authInfoReq) + if err != nil { + var problemDetails models.ProblemDetails + if authInfoResult.AuthenticationVector == nil { + problemDetails.Cause = "AV_GENERATION_PROBLEM" + } else { + problemDetails.Cause = "UPSTREAM_SERVER_ERROR" + } + problemDetails.Status = int32(rsp.StatusCode) + return nil, err, &problemDetails + } + defer func() { + if rspCloseErr := rsp.Body.Close(); rspCloseErr != nil { + logger.UeAuthLog.Errorf("GenerateAuthDataApi response body cannot close: %+v", rspCloseErr) + } + }() + return &authInfoResult, nil, nil +} diff --git a/internal/sbi/processor/processor.go b/internal/sbi/processor/processor.go new file mode 100644 index 0000000..610fbc6 --- /dev/null +++ b/internal/sbi/processor/processor.go @@ -0,0 +1,23 @@ +package processor + +import ( + "github.com/free5gc/ausf/internal/sbi/consumer" + "github.com/free5gc/ausf/pkg/app" +) + +type ProcessorAusf interface { + app.App + + Consumer() *consumer.Consumer +} + +type Processor struct { + ProcessorAusf +} + +func NewProcessor(ausf ProcessorAusf) (*Processor, error) { + p := &Processor{ + ProcessorAusf: ausf, + } + return p, nil +} diff --git a/internal/sbi/processor/ue_authentication.go b/internal/sbi/processor/ue_authentication.go new file mode 100644 index 0000000..57dc802 --- /dev/null +++ b/internal/sbi/processor/ue_authentication.go @@ -0,0 +1,835 @@ +package processor + +import ( + "bytes" + "crypto/hmac" + "crypto/sha256" + "encoding/base64" + "encoding/binary" + "encoding/hex" + "fmt" + "hash" + "math/rand" + "net/http" + "strconv" + "strings" + "time" + + "github.com/bronze1man/radius" + "github.com/gin-gonic/gin" + "github.com/google/gopacket" + "github.com/google/gopacket/layers" + + ausf_context "github.com/free5gc/ausf/internal/context" + "github.com/free5gc/ausf/internal/logger" + "github.com/free5gc/ausf/pkg/factory" + "github.com/free5gc/openapi/models" + "github.com/free5gc/util/ueauth" +) + +func (p *Processor) HandleEapAuthComfirmRequest(c *gin.Context, eapSession models.EapSession, eapSessionId string) { + logger.Auth5gAkaLog.Infof("EapAuthComfirmRequest") + + p.EapAuthComfirmRequestProcedure(c, eapSession, eapSessionId) +} + +func (p *Processor) EapAuthComfirmRequestProcedure( + c *gin.Context, + updateEapSession models.EapSession, + eapSessionID string, +) { + var eapSession models.EapSession + + if !ausf_context.CheckIfSuciSupiPairExists(eapSessionID) { + logger.AuthELog.Infoln("supiSuciPair does not exist, confirmation failed") + problemDetails := models.ProblemDetails{ + Status: http.StatusNotFound, + Cause: "USER_NOT_FOUND", + } + c.JSON(int(problemDetails.Status), problemDetails) + return + } + + currentSupi := ausf_context.GetSupiFromSuciSupiMap(eapSessionID) + if !ausf_context.CheckIfAusfUeContextExists(currentSupi) { + logger.AuthELog.Infoln("SUPI does not exist, confirmation failed") + problemDetails := models.ProblemDetails{ + Status: http.StatusNotFound, + Cause: "USER_NOT_FOUND", + } + c.JSON(int(problemDetails.Status), problemDetails) + return + } + + ausfCurrentContext := ausf_context.GetAusfUeContext(currentSupi) + servingNetworkName := ausfCurrentContext.ServingNetworkName + + if ausfCurrentContext.AuthStatus == models.AuthResult_FAILURE { + logger.AuthELog.Warnf("Authentication failed with status: %s", ausfCurrentContext.AuthStatus) + eapFailPkt := ConstructEapNoTypePkt(radius.EapCodeFailure, 0) + eapSession.EapPayload = eapFailPkt + eapSession.AuthResult = models.AuthResult_FAILURE + c.JSON(http.StatusUnauthorized, eapSession) + return + } + + var eapPayload []byte + if eapPayloadTmp, err := base64.StdEncoding.DecodeString(updateEapSession.EapPayload); err != nil { + logger.AuthELog.Warnf("EAP Payload decode failed: %+v", err) + } else { + eapPayload = eapPayloadTmp + } + + eapGoPkt := gopacket.NewPacket(eapPayload, layers.LayerTypeEAP, gopacket.Default) + eapLayer := eapGoPkt.Layer(layers.LayerTypeEAP) + eapContent, _ := eapLayer.(*layers.EAP) + eapOK := true + var eapErrStr string + + if eapContent.Code != layers.EAPCodeResponse { + eapOK = false + eapErrStr = "eap packet code error" + } else if eapContent.Type != ausf_context.EAP_AKA_PRIME_TYPENUM { + eapOK = false + eapErrStr = "eap packet type error" + } else if decodeEapAkaPrimePkt, err := decodeEapAkaPrime(eapContent.Contents); err != nil { + logger.AuthELog.Warnf("EAP-AKA' decode failed: %+v", err) + eapOK = false + eapErrStr = "eap packet error" + } else { + switch decodeEapAkaPrimePkt.Subtype { + case ausf_context.AKA_CHALLENGE_SUBTYPE: + K_autStr := ausfCurrentContext.K_aut + var K_aut []byte + if K_autTmp, err := hex.DecodeString(K_autStr); err != nil { + logger.AuthELog.Warnf("K_aut decode error: %+v", err) + } else { + K_aut = K_autTmp + } + XMAC := CalculateAtMAC(K_aut, decodeEapAkaPrimePkt.MACInput) + MAC := decodeEapAkaPrimePkt.Attributes[ausf_context.AT_MAC_ATTRIBUTE].Value + XRES := ausfCurrentContext.XRES + RES := hex.EncodeToString(decodeEapAkaPrimePkt.Attributes[ausf_context.AT_RES_ATTRIBUTE].Value) + + if !bytes.Equal(MAC, XMAC) { + eapOK = false + eapErrStr = "EAP-AKA' integrity check fail" + } else if XRES == RES { + logger.AuthELog.Infoln("Correct RES value, EAP-AKA' auth succeed") + eapSession.KSeaf = ausfCurrentContext.Kseaf + eapSession.Supi = currentSupi + eapSession.AuthResult = models.AuthResult_SUCCESS + eapSuccPkt := ConstructEapNoTypePkt(radius.EapCodeSuccess, eapContent.Id) + eapSession.EapPayload = eapSuccPkt + udmUrl := ausfCurrentContext.UdmUeauUrl + if sendErr := p.Consumer().SendAuthResultToUDM( + eapSessionID, + models.AuthType_EAP_AKA_PRIME, + true, + servingNetworkName, + udmUrl); sendErr != nil { + logger.AuthELog.Infoln(sendErr.Error()) + problemDetails := models.ProblemDetails{ + Cause: "UPSTREAM_SERVER_ERROR", + } + c.JSON(http.StatusInternalServerError, problemDetails) + return + } + ausfCurrentContext.AuthStatus = models.AuthResult_SUCCESS + } else { + eapOK = false + eapErrStr = "Wrong RES value, EAP-AKA' auth failed" + } + case ausf_context.AKA_AUTHENTICATION_REJECT_SUBTYPE: + ausfCurrentContext.AuthStatus = models.AuthResult_FAILURE + case ausf_context.AKA_SYNCHRONIZATION_FAILURE_SUBTYPE: + logger.AuthELog.Warnf("EAP-AKA' synchronziation failure") + if ausfCurrentContext.Resynced { + eapOK = false + eapErrStr = "2 consecutive Synch Failure, terminate authentication procedure" + } else { + var authInfo models.AuthenticationInfo + AUTS := decodeEapAkaPrimePkt.Attributes[ausf_context.AT_AUTS_ATTRIBUTE].Value + resynchronizationInfo := &models.ResynchronizationInfo{ + Auts: hex.EncodeToString(AUTS[:]), + } + authInfo.SupiOrSuci = eapSessionID + authInfo.ServingNetworkName = servingNetworkName + authInfo.ResynchronizationInfo = resynchronizationInfo + p.UeAuthPostRequestProcedure(c, authInfo) + return + } + case ausf_context.AKA_NOTIFICATION_SUBTYPE: + ausfCurrentContext.AuthStatus = models.AuthResult_FAILURE + case ausf_context.AKA_CLIENT_ERROR_SUBTYPE: + logger.AuthELog.Warnf("EAP-AKA' failure: receive client-error") + ausfCurrentContext.AuthStatus = models.AuthResult_FAILURE + default: + ausfCurrentContext.AuthStatus = models.AuthResult_FAILURE + } + } + + if !eapOK { + logger.AuthELog.Warnf("EAP-AKA' failure: %s", eapErrStr) + if sendErr := p.Consumer().SendAuthResultToUDM(eapSessionID, models.AuthType_EAP_AKA_PRIME, false, servingNetworkName, + ausfCurrentContext.UdmUeauUrl); sendErr != nil { + logger.AuthELog.Infoln(sendErr.Error()) + problemDetails := models.ProblemDetails{ + Status: http.StatusInternalServerError, + Cause: "UPSTREAM_SERVER_ERROR", + } + c.JSON(http.StatusInternalServerError, problemDetails) + return + } + + ausfCurrentContext.AuthStatus = models.AuthResult_FAILURE + eapSession.AuthResult = models.AuthResult_ONGOING + failEapAkaNoti := ConstructFailEapAkaNotification(eapContent.Id) + eapSession.EapPayload = failEapAkaNoti + self := ausf_context.GetSelf() + linkUrl := self.Url + factory.AusfAuthResUriPrefix + "/ue-authentications/" + eapSessionID + "/eap-session" + linksValue := models.LinksValueSchema{Href: linkUrl} + eapSession.Links = make(map[string]models.LinksValueSchema) + eapSession.Links["eap-session"] = linksValue + } else if ausfCurrentContext.AuthStatus == models.AuthResult_FAILURE { + if sendErr := p.Consumer().SendAuthResultToUDM(eapSessionID, models.AuthType_EAP_AKA_PRIME, false, servingNetworkName, + ausfCurrentContext.UdmUeauUrl); sendErr != nil { + logger.AuthELog.Infoln(sendErr.Error()) + var problemDetails models.ProblemDetails + problemDetails.Status = http.StatusInternalServerError + problemDetails.Cause = "UPSTREAM_SERVER_ERROR" + } + + eapFailPkt := ConstructEapNoTypePkt(radius.EapCodeFailure, eapPayload[1]) + eapSession.EapPayload = eapFailPkt + eapSession.AuthResult = models.AuthResult_FAILURE + } + + c.JSON(http.StatusOK, eapSession) +} + +func (p *Processor) HandleUeAuthPostRequest(c *gin.Context, authenticationInfo models.AuthenticationInfo) { + logger.UeAuthLog.Infof("HandleUeAuthPostRequest") + p.UeAuthPostRequestProcedure(c, authenticationInfo) +} + +func (p *Processor) UeAuthPostRequestProcedure(c *gin.Context, updateAuthenticationInfo models.AuthenticationInfo) { + var responseBody models.UeAuthenticationCtx + var authInfoReq models.AuthenticationInfoRequest + + supiOrSuci := updateAuthenticationInfo.SupiOrSuci + + snName := updateAuthenticationInfo.ServingNetworkName + servingNetworkAuthorized := ausf_context.IsServingNetworkAuthorized(snName) + if !servingNetworkAuthorized { + problemDetails := models.ProblemDetails{ + Cause: "SERVING_NETWORK_NOT_AUTHORIZED", + Status: http.StatusForbidden, + } + logger.UeAuthLog.Infoln("403 forbidden: serving network NOT AUTHORIZED") + c.JSON(http.StatusForbidden, problemDetails) + return + } + logger.UeAuthLog.Infoln("Serving network authorized") + + responseBody.ServingNetworkName = snName + authInfoReq.ServingNetworkName = snName + self := ausf_context.GetSelf() + authInfoReq.AusfInstanceId = self.GetSelfID() + + var lastEapID uint8 + if updateAuthenticationInfo.ResynchronizationInfo != nil { + logger.UeAuthLog.Warningln("Auts: ", updateAuthenticationInfo.ResynchronizationInfo.Auts) + ausfCurrentSupi := ausf_context.GetSupiFromSuciSupiMap(supiOrSuci) + logger.UeAuthLog.Warningln(ausfCurrentSupi) + ausfCurrentContext := ausf_context.GetAusfUeContext(ausfCurrentSupi) + logger.UeAuthLog.Warningln(ausfCurrentContext.Rand) + if updateAuthenticationInfo.ResynchronizationInfo.Rand == "" { + updateAuthenticationInfo.ResynchronizationInfo.Rand = ausfCurrentContext.Rand + } + logger.UeAuthLog.Warningln("Rand: ", updateAuthenticationInfo.ResynchronizationInfo.Rand) + authInfoReq.ResynchronizationInfo = updateAuthenticationInfo.ResynchronizationInfo + lastEapID = ausfCurrentContext.EapID + } + + udmUrl := p.Consumer().GetUdmUrl(self.NrfUri) + + result, err, pd := p.Consumer().GenerateAuthDataApi(udmUrl, supiOrSuci, authInfoReq) + if err != nil { + logger.UeAuthLog.Infof("GenerateAuthDataApi error: %+v", err) + c.JSON(http.StatusInternalServerError, pd) + return + } + authInfoResult := *result + + ueid := authInfoResult.Supi + ausfUeContext := ausf_context.NewAusfUeContext(ueid) + ausfUeContext.ServingNetworkName = snName + ausfUeContext.AuthStatus = models.AuthResult_ONGOING + ausfUeContext.UdmUeauUrl = udmUrl + ausf_context.AddAusfUeContextToPool(ausfUeContext) + + logger.UeAuthLog.Infof("Add SuciSupiPair (%s, %s) to map.\n", supiOrSuci, ueid) + ausf_context.AddSuciSupiPairToMap(supiOrSuci, ueid) + + locationURI := self.Url + factory.AusfAuthResUriPrefix + "/ue-authentications/" + supiOrSuci + putLink := locationURI + if authInfoResult.AuthType == models.AuthType__5_G_AKA { + logger.UeAuthLog.Infoln("Use 5G AKA auth method") + putLink += "/5g-aka-confirmation" + + // Derive HXRES* from XRES* + concat := authInfoResult.AuthenticationVector.Rand + authInfoResult.AuthenticationVector.XresStar + var hxresStarBytes []byte + if bytes, err := hex.DecodeString(concat); err != nil { + logger.Auth5gAkaLog.Errorf("decode concat error: %+v", err) + problemDetails := models.ProblemDetails{ + Title: "Concat Decode Problem", + Cause: "CONCAT_DECODE_PROBLEM", + Detail: err.Error(), + Status: http.StatusInternalServerError, + } + c.JSON(http.StatusInternalServerError, problemDetails) + return + } else { + hxresStarBytes = bytes + } + hxresStarAll := sha256.Sum256(hxresStarBytes) + hxresStar := hex.EncodeToString(hxresStarAll[16:]) // last 128 bits + logger.Auth5gAkaLog.Infof("XresStar = %x\n", authInfoResult.AuthenticationVector.XresStar) + + // Derive Kseaf from Kausf + Kausf := authInfoResult.AuthenticationVector.Kausf + var KausfDecode []byte + if ausfDecode, err := hex.DecodeString(Kausf); err != nil { + logger.Auth5gAkaLog.Errorf("decode Kausf failed: %+v", err) + problemDetails := models.ProblemDetails{ + Title: "Kausf Decode Problem", + Cause: "KAUSF_DECODE_PROBLEM", + Detail: err.Error(), + Status: http.StatusInternalServerError, + } + c.JSON(http.StatusInternalServerError, problemDetails) + return + } else { + KausfDecode = ausfDecode + } + P0 := []byte(snName) + Kseaf, err := ueauth.GetKDFValue(KausfDecode, ueauth.FC_FOR_KSEAF_DERIVATION, P0, ueauth.KDFLen(P0)) + if err != nil { + logger.Auth5gAkaLog.Errorf("GetKDFValue failed: %+v", err) + problemDetails := models.ProblemDetails{ + Title: "Kseaf Derivation Problem", + Cause: "KSEAF_DERIVATION_PROBLEM", + Detail: err.Error(), + Status: http.StatusInternalServerError, + } + c.JSON(http.StatusInternalServerError, problemDetails) + return + } + ausfUeContext.XresStar = authInfoResult.AuthenticationVector.XresStar + ausfUeContext.Kausf = Kausf + ausfUeContext.Kseaf = hex.EncodeToString(Kseaf) + ausfUeContext.Rand = authInfoResult.AuthenticationVector.Rand + + var av5gAka models.Av5gAka + av5gAka.Rand = authInfoResult.AuthenticationVector.Rand + av5gAka.Autn = authInfoResult.AuthenticationVector.Autn + av5gAka.HxresStar = hxresStar + responseBody.Var5gAuthData = av5gAka + + linksValue := models.LinksValueSchema{Href: putLink} + responseBody.Links = make(map[string]models.LinksValueSchema) + responseBody.Links["5g-aka"] = linksValue + } else if authInfoResult.AuthType == models.AuthType_EAP_AKA_PRIME { + logger.UeAuthLog.Infoln("Use EAP-AKA' auth method") + putLink += "/eap-session" + + var identity string + // TODO support more SUPI type + if ueid[:4] == "imsi" { + if !self.EapAkaSupiImsiPrefix { + // 33.501 v15.9.0 or later + identity = ueid[5:] + } else { + // 33.501 v15.8.0 or earlier + identity = ueid + } + } + ikPrime := authInfoResult.AuthenticationVector.IkPrime + ckPrime := authInfoResult.AuthenticationVector.CkPrime + RAND := authInfoResult.AuthenticationVector.Rand + AUTN := authInfoResult.AuthenticationVector.Autn + XRES := authInfoResult.AuthenticationVector.Xres + ausfUeContext.XRES = XRES + + ausfUeContext.Rand = authInfoResult.AuthenticationVector.Rand + + _, K_aut, _, _, EMSK := eapAkaPrimePrf(ikPrime, ckPrime, identity) + logger.AuthELog.Tracef("K_aut: %x", K_aut) + ausfUeContext.K_aut = hex.EncodeToString(K_aut) + Kausf := EMSK[0:32] + ausfUeContext.Kausf = hex.EncodeToString(Kausf) + P0 := []byte(snName) + Kseaf, err := ueauth.GetKDFValue(Kausf, ueauth.FC_FOR_KSEAF_DERIVATION, P0, ueauth.KDFLen(P0)) + if err != nil { + logger.AuthELog.Errorf("GetKDFValue failed: %+v", err) + } + ausfUeContext.Kseaf = hex.EncodeToString(Kseaf) + + var eapPkt radius.EapPacket + eapPkt.Code = radius.EapCode(1) + if updateAuthenticationInfo.ResynchronizationInfo == nil { + src := rand.NewSource(time.Now().UnixNano()) + r := rand.New(src) + randIdentifier := r.Intn(256) + ausfUeContext.EapID = uint8(randIdentifier) + } else { + ausfUeContext.EapID = lastEapID + 1 + } + eapPkt.Identifier = ausfUeContext.EapID + eapPkt.Type = radius.EapType(50) // according to RFC5448 6.1 + + var eapAKAHdr, atRand, atAutn, atKdf, atKdfInput, atMAC string + eapAKAHdrBytes := make([]byte, 3) // RFC4187 8.1 + eapAKAHdrBytes[0] = ausf_context.AKA_CHALLENGE_SUBTYPE + eapAKAHdr = string(eapAKAHdrBytes) + if atRandTmp, err := EapEncodeAttribute("AT_RAND", RAND); err != nil { + logger.AuthELog.Errorf("EAP encode RAND failed: %+v", err) + } else { + atRand = atRandTmp + } + if atAutnTmp, err := EapEncodeAttribute("AT_AUTN", AUTN); err != nil { + logger.AuthELog.Errorf("EAP encode AUTN failed: %+v", err) + } else { + atAutn = atAutnTmp + } + if atKdfTmp, err := EapEncodeAttribute("AT_KDF", snName); err != nil { + logger.AuthELog.Errorf("EAP encode KDF failed: %+v", err) + } else { + atKdf = atKdfTmp + } + if atKdfInputTmp, err := EapEncodeAttribute("AT_KDF_INPUT", snName); err != nil { + logger.AuthELog.Errorf("EAP encode KDF failed: %+v", err) + } else { + atKdfInput = atKdfInputTmp + } + if atMACTmp, err := EapEncodeAttribute("AT_MAC", ""); err != nil { + logger.AuthELog.Errorf("EAP encode MAC failed: %+v", err) + } else { + atMAC = atMACTmp + } + + dataArrayBeforeMAC := eapAKAHdr + atRand + atAutn + atKdf + atKdfInput + atMAC + eapPkt.Data = []byte(dataArrayBeforeMAC) + encodedPktBeforeMAC := eapPkt.Encode() + + MacValue := CalculateAtMAC(K_aut, encodedPktBeforeMAC) + atMAC = atMAC[:4] + string(MacValue) + + dataArrayAfterMAC := eapAKAHdr + atRand + atAutn + atKdf + atKdfInput + atMAC + + eapPkt.Data = []byte(dataArrayAfterMAC) + encodedPktAfterMAC := eapPkt.Encode() + responseBody.Var5gAuthData = base64.StdEncoding.EncodeToString(encodedPktAfterMAC) + + linksValue := models.LinksValueSchema{Href: putLink} + responseBody.Links = make(map[string]models.LinksValueSchema) + responseBody.Links["eap-session"] = linksValue + } + + responseBody.AuthType = authInfoResult.AuthType + + c.Header("Location", locationURI) + c.JSON(http.StatusCreated, responseBody) +} + +func (p *Processor) HandleAuth5gAkaComfirmRequest( + c *gin.Context, + confirmationData models.ConfirmationData, + confirmationDataResponseId string, +) { + logger.Auth5gAkaLog.Infof("Auth5gAkaComfirmRequest") + p.Auth5gAkaComfirmRequestProcedure(c, confirmationData, confirmationDataResponseId) +} + +func (p *Processor) Auth5gAkaComfirmRequestProcedure(c *gin.Context, updateConfirmationData models.ConfirmationData, + ConfirmationDataResponseID string, +) { + var confirmDataRsp models.ConfirmationDataResponse + success := false + confirmDataRsp.AuthResult = models.AuthResult_FAILURE + + if !ausf_context.CheckIfSuciSupiPairExists(ConfirmationDataResponseID) { + logger.Auth5gAkaLog.Infof("supiSuciPair does not exist, confirmation failed (queried by %s)\n", + ConfirmationDataResponseID) + problemDetails := models.ProblemDetails{ + Cause: "USER_NOT_FOUND", + Status: http.StatusBadRequest, + } + c.JSON(http.StatusBadRequest, problemDetails) + return + } + + currentSupi := ausf_context.GetSupiFromSuciSupiMap(ConfirmationDataResponseID) + if !ausf_context.CheckIfAusfUeContextExists(currentSupi) { + logger.Auth5gAkaLog.Infof("SUPI does not exist, confirmation failed (queried by %s)\n", currentSupi) + problemDetails := models.ProblemDetails{ + Cause: "USER_NOT_FOUND", + Status: http.StatusBadRequest, + } + c.JSON(http.StatusBadRequest, problemDetails) + return + } + + ausfCurrentContext := ausf_context.GetAusfUeContext(currentSupi) + servingNetworkName := ausfCurrentContext.ServingNetworkName + + // Compare the received RES* with the stored XRES* + logger.Auth5gAkaLog.Infof("res*: %x\nXres*: %x\n", updateConfirmationData.ResStar, ausfCurrentContext.XresStar) + if strings.EqualFold(updateConfirmationData.ResStar, ausfCurrentContext.XresStar) { + ausfCurrentContext.AuthStatus = models.AuthResult_SUCCESS + confirmDataRsp.AuthResult = models.AuthResult_SUCCESS + success = true + logger.Auth5gAkaLog.Infoln("5G AKA confirmation succeeded") + confirmDataRsp.Supi = currentSupi + confirmDataRsp.Kseaf = ausfCurrentContext.Kseaf + } else { + ausfCurrentContext.AuthStatus = models.AuthResult_FAILURE + confirmDataRsp.AuthResult = models.AuthResult_FAILURE + p.logConfirmFailureAndInformUDM(ConfirmationDataResponseID, models.AuthType__5_G_AKA, servingNetworkName, + "5G AKA confirmation failed", ausfCurrentContext.UdmUeauUrl) + } + + if sendErr := p.Consumer().SendAuthResultToUDM(currentSupi, models.AuthType__5_G_AKA, success, servingNetworkName, + ausfCurrentContext.UdmUeauUrl); sendErr != nil { + logger.Auth5gAkaLog.Infoln(sendErr.Error()) + problemDetails := models.ProblemDetails{ + Status: http.StatusInternalServerError, + Cause: "UPSTREAM_SERVER_ERROR", + } + c.JSON(http.StatusInternalServerError, problemDetails) + return + } + + c.JSON(http.StatusOK, confirmDataRsp) +} + +func KDF5gAka(param ...string) hash.Hash { + s := param[0] + s += param[1] + if p0len, err := strconv.Atoi(param[2]); err != nil { + logger.AuthELog.Warnf("atoi failed: %+v", err) + } else { + s += strconv.FormatInt(int64(p0len), 16) + } + h := hmac.New(sha256.New, []byte(s)) + + return h +} + +func intToByteArray(i int) []byte { + r := make([]byte, 2) + binary.BigEndian.PutUint16(r, uint16(i)) + return r +} + +func padZeros(byteArray []byte, size int) []byte { + l := len(byteArray) + if l == size { + return byteArray + } + r := make([]byte, size) + copy(r[size-l:], byteArray) + return r +} + +func CalculateAtMAC(key []byte, input []byte) []byte { + // keyed with K_aut + h := hmac.New(sha256.New, key) + if _, err := h.Write(input); err != nil { + logger.AuthELog.Errorln(err.Error()) + } + sum := h.Sum(nil) + return sum[:16] +} + +func EapEncodeAttribute(attributeType string, data string) (string, error) { + var attribute string + var length int + + switch attributeType { + case "AT_RAND": + length = len(data)/8 + 1 + if length != 5 { + return "", fmt.Errorf("[eapEncodeAttribute] AT_RAND Length Error") + } + attrNum := fmt.Sprintf("%02x", ausf_context.AT_RAND_ATTRIBUTE) + attribute = attrNum + "05" + "0000" + data + + case "AT_AUTN": + length = len(data)/8 + 1 + if length != 5 { + return "", fmt.Errorf("[eapEncodeAttribute] AT_AUTN Length Error") + } + attrNum := fmt.Sprintf("%02x", ausf_context.AT_AUTN_ATTRIBUTE) + attribute = attrNum + "05" + "0000" + data + + case "AT_KDF_INPUT": + var byteName []byte + nLength := len(data) + length := (nLength+3)/4 + 1 + b := make([]byte, length*4) + byteNameLength := intToByteArray(nLength) + byteName = []byte(data) + pad := padZeros(byteName, (length-1)*4) + b[0] = 23 + b[1] = byte(length) + copy(b[2:4], byteNameLength) + copy(b[4:], pad) + return string(b[:]), nil + + case "AT_KDF": + // Value 1 default key derivation function for EAP-AKA' + attrNum := fmt.Sprintf("%02x", ausf_context.AT_KDF_ATTRIBUTE) + attribute = attrNum + "01" + "0001" + + case "AT_MAC": + // Pad MAC value with 16 bytes of 0 since this is just for the calculation of MAC + attrNum := fmt.Sprintf("%02x", ausf_context.AT_MAC_ATTRIBUTE) + attribute = attrNum + "05" + "0000" + "00000000000000000000000000000000" + + case "AT_RES": + var byteName []byte + nLength := len(data) + length := (nLength+3)/4 + 1 + b := make([]byte, length*4) + byteNameLength := intToByteArray(nLength) + byteName = []byte(data) + pad := padZeros(byteName, (length-1)*4) + b[0] = 3 + b[1] = byte(length) + copy(b[2:4], byteNameLength) + copy(b[4:], pad) + return string(b[:]), nil + + default: + logger.AuthELog.Errorf("UNKNOWN attributeType %s\n", attributeType) + return "", nil + } + + if r, err := hex.DecodeString(attribute); err != nil { + return "", err + } else { + return string(r), nil + } +} + +func eapAkaPrimePrf(ikPrime string, ckPrime string, identity string) ([]byte, []byte, []byte, []byte, []byte) { + keyAp := ikPrime + ckPrime + + var key []byte + if keyTmp, err := hex.DecodeString(keyAp); err != nil { + logger.AuthELog.Warnf("Decode key AP failed: %+v", err) + } else { + key = keyTmp + } + sBase := []byte("EAP-AKA'" + identity) + + MK := []byte("") + prev := []byte("") + prfRounds := 208/32 + 1 + for i := 0; i < prfRounds; i++ { + // Create a new HMAC by defining the hash type and the key (as byte array) + h := hmac.New(sha256.New, key) + + hexNum := (byte)(i + 1) + ap := append(sBase, hexNum) + s := append(prev, ap...) + + // Write Data to it + if _, err := h.Write(s); err != nil { + logger.AuthELog.Errorln(err.Error()) + } + + // Get result + sha := h.Sum(nil) + MK = append(MK, sha...) + prev = sha + } + + K_encr := MK[0:16] // 0..127 + K_aut := MK[16:48] // 128..383 + K_re := MK[48:80] // 384..639 + MSK := MK[80:144] // 640..1151 + EMSK := MK[144:208] // 1152..1663 + return K_encr, K_aut, K_re, MSK, EMSK +} + +func decodeEapAkaPrime(eapPkt []byte) (*ausf_context.EapAkaPrimePkt, error) { + var decodePkt ausf_context.EapAkaPrimePkt + var attrLen int + var decodeAttr ausf_context.EapAkaPrimeAttribute + attributes := make(map[uint8]ausf_context.EapAkaPrimeAttribute) + data := eapPkt[5:] + decodePkt.Subtype = data[0] + dataLen := len(data) + + // decode attributes + for i := 3; i < dataLen; i += attrLen { + attrType := data[i] + attrLen = int(data[i+1]) * 4 + if attrLen == 0 { + return nil, fmt.Errorf("attribute length equal to zero") + } + if i+attrLen > dataLen { + return nil, fmt.Errorf("packet length out of range") + } + switch attrType { + case ausf_context.AT_RES_ATTRIBUTE: + logger.AuthELog.Tracef("Decoding AT_RES\n") + accLen := int(data[i+3] >> 3) + if accLen > 16 || accLen < 4 || accLen+4 > attrLen { + return nil, fmt.Errorf("attribute AT_RES decode err") + } + + decodeAttr.Type = attrType + decodeAttr.Length = data[i+1] + decodeAttr.Value = data[i+4 : i+4+accLen] + attributes[attrType] = decodeAttr + case ausf_context.AT_MAC_ATTRIBUTE: + logger.AuthELog.Tracef("Decoding AT_MAC\n") + if attrLen != 20 { + return nil, fmt.Errorf("attribute AT_MAC decode err") + } + decodeAttr.Type = attrType + decodeAttr.Length = data[i+1] + Mac := make([]byte, attrLen-4) + copy(Mac, data[i+4:i+attrLen]) + decodeAttr.Value = Mac + attributes[attrType] = decodeAttr + + // clean AT_MAC value for integrity check later + zeros := make([]byte, attrLen-4) + copy(data[i+4:i+attrLen], zeros) + decodePkt.MACInput = eapPkt + case ausf_context.AT_KDF_ATTRIBUTE: + logger.AuthELog.Tracef("Decoding AT_KDF\n") + if attrLen != 4 { + return nil, fmt.Errorf("attribute AT_KDF decode err") + } + decodeAttr.Type = attrType + decodeAttr.Length = data[i+1] + decodeAttr.Value = data[i+2 : i+attrLen] + attributes[attrType] = decodeAttr + case ausf_context.AT_AUTS_ATTRIBUTE: + logger.AuthELog.Tracef("Decoding AT_AUTS\n") + if attrLen != 16 { + return nil, fmt.Errorf("attribute AT_AUTS decode err") + } + decodeAttr.Type = attrType + decodeAttr.Length = data[i+1] + decodeAttr.Value = data[i+2 : i+attrLen] + attributes[attrType] = decodeAttr + case ausf_context.AT_CLIENT_ERROR_CODE_ATTRIBUTE: + logger.AuthELog.Tracef("Decoding AT_CLIENT_ERROR_CODE\n") + if attrLen != 4 { + return nil, fmt.Errorf("attribute AT_CLIENT_ERROR_CODE decode err") + } + decodeAttr.Type = attrType + decodeAttr.Length = data[i+1] + decodeAttr.Value = data[i+2 : i+attrLen] + attributes[attrType] = decodeAttr + default: + logger.AuthELog.Tracef("attribute type %x skipped\n", attrType) + } + } + + switch decodePkt.Subtype { + case ausf_context.AKA_CHALLENGE_SUBTYPE: + logger.AuthELog.Tracef("Subtype AKA-Challenge\n") + if _, ok := attributes[ausf_context.AT_RES_ATTRIBUTE]; !ok { + return nil, fmt.Errorf("AKA-Challenge attributes error") + } else if _, ok := attributes[ausf_context.AT_MAC_ATTRIBUTE]; !ok { + return nil, fmt.Errorf("AKA-Challenge attributes error") + } + case ausf_context.AKA_AUTHENTICATION_REJECT_SUBTYPE: + logger.AuthELog.Tracef("Subtype AKA-Authentication-Reject\n") + if len(attributes) != 0 { + return nil, fmt.Errorf("AKA-Authentication-Reject attributes error") + } + case ausf_context.AKA_SYNCHRONIZATION_FAILURE_SUBTYPE: + logger.AuthELog.Tracef("Subtype AKA-Synchronization-Failure\n") + if len(attributes) != 2 { + return nil, fmt.Errorf("AKA-Synchornization-Failure attributes error") + } else if _, ok := attributes[ausf_context.AT_AUTS_ATTRIBUTE]; !ok { + return nil, fmt.Errorf("AKA-Synchornization-Failure attributes error") + } else if _, ok := attributes[ausf_context.AT_KDF_ATTRIBUTE]; !ok { + return nil, fmt.Errorf("AKA-Synchornization-Failure attributes error") + } else if kdfVal := attributes[ausf_context.AT_KDF_ATTRIBUTE].Value; !(kdfVal[0] == 0 && kdfVal[1] == 1) { + return nil, fmt.Errorf("AKA-Synchornization-Failure attributes error") + } + case ausf_context.AKA_NOTIFICATION_SUBTYPE: + logger.AuthELog.Tracef("Subtype AKA-Notification\n") + case ausf_context.AKA_CLIENT_ERROR_SUBTYPE: + logger.AuthELog.Tracef("Subtype AKA-Client-Error\n") + if len(attributes) != 1 { + return nil, fmt.Errorf("AKA-Client-Error attributes error") + } else if _, ok := attributes[ausf_context.AT_CLIENT_ERROR_CODE_ATTRIBUTE]; !ok { + return nil, fmt.Errorf("AKA-Client-Error attributes error") + } + default: + logger.AuthELog.Tracef("subtype %x skipped\n", decodePkt.Subtype) + } + + decodePkt.Attributes = attributes + + return &decodePkt, nil +} + +func ConstructFailEapAkaNotification(oldPktId uint8) string { + var eapPkt radius.EapPacket + eapPkt.Code = radius.EapCodeRequest + eapPkt.Identifier = oldPktId + 1 + eapPkt.Type = ausf_context.EAP_AKA_PRIME_TYPENUM + + eapAkaHdrBytes := make([]byte, 3) + eapAkaHdrBytes[0] = ausf_context.AKA_NOTIFICATION_SUBTYPE + + attrNum := fmt.Sprintf("%02x", ausf_context.AT_NOTIFICATION_ATTRIBUTE) + attribute := attrNum + "01" + "4000" + var attrHex []byte + if attrHexTmp, err := hex.DecodeString(attribute); err != nil { + logger.AuthELog.Warnf("Decode attribute failed: %+v", err) + } else { + attrHex = attrHexTmp + } + + eapPkt.Data = append(eapAkaHdrBytes, attrHex...) + eapPktEncode := eapPkt.Encode() + return base64.StdEncoding.EncodeToString(eapPktEncode) +} + +func ConstructEapNoTypePkt(code radius.EapCode, pktID uint8) string { + b := make([]byte, 4) + b[0] = byte(code) + b[1] = pktID + binary.BigEndian.PutUint16(b[2:4], uint16(4)) + return base64.StdEncoding.EncodeToString(b) +} + +func (p *Processor) logConfirmFailureAndInformUDM( + id string, authType models.AuthType, servingNetworkName, errStr, udmUrl string, +) { + if authType == models.AuthType__5_G_AKA { + logger.Auth5gAkaLog.Infoln(servingNetworkName, errStr) + if sendErr := p.Consumer().SendAuthResultToUDM(id, authType, false, "", udmUrl); sendErr != nil { + logger.Auth5gAkaLog.Infoln(sendErr.Error()) + } + } else if authType == models.AuthType_EAP_AKA_PRIME { + logger.AuthELog.Infoln(errStr) + if sendErr := p.Consumer().SendAuthResultToUDM(id, authType, false, "", udmUrl); sendErr != nil { + logger.AuthELog.Infoln(sendErr.Error()) + } + } +} diff --git a/internal/sbi/producer/eapAkaPrimeKeyGen_test.go b/internal/sbi/producer/eapAkaPrimeKeyGen_test.go deleted file mode 100644 index 7434308..0000000 --- a/internal/sbi/producer/eapAkaPrimeKeyGen_test.go +++ /dev/null @@ -1,156 +0,0 @@ -package producer - -import ( - "encoding/hex" - "fmt" - "testing" - - "github.com/stretchr/testify/assert" - - "github.com/free5gc/util/ueauth" -) - -type testEapAkaPrimeCase struct { - Identity string `json:"Identity"` - NetworkName string `json:"Networkname"` - RAND string `json:"RAND"` - AUTN string `json:"AUTN"` - IK string `json:"IK"` - CK string `json:"CK"` - RES string `json:"RES"` - CKPrime string `json:"CKPrime"` - IKPrime string `json:"IKPrime"` - K_encr string `json:"K_encr"` - K_aut string `json:"K_aut"` - K_re string `json:"K_re"` - MSK string `json:"MSK"` - EMSK string `json:"EMSK"` -} - -func EapAkaPrimeKeyGenAll(data testEapAkaPrimeCase) ([]byte, []byte, []byte, []byte, []byte, []byte, []byte) { - var CK, IK, AUTN []byte - if CKtmp, err := hex.DecodeString(data.CK); err != nil { - fmt.Println(err) - } else { - CK = CKtmp - } - if IKtmp, err := hex.DecodeString(data.IK); err != nil { - fmt.Println(err) - } else { - IK = IKtmp - } - if AUTNtmp, err := hex.DecodeString(data.AUTN); err != nil { - fmt.Println(err) - } else { - AUTN = AUTNtmp - } - SQNxorAK := AUTN[:6] - key := append(CK, IK...) - FC := ueauth.FC_FOR_CK_PRIME_IK_PRIME_DERIVATION - P0 := []byte(data.NetworkName) - P1 := SQNxorAK - - // Generate CK' IK' - kdfVal, err := ueauth.GetKDFValue(key, FC, P0, ueauth.KDFLen(P0), P1, ueauth.KDFLen(P1)) - if err != nil { - fmt.Println(err) - } - CKPrime := kdfVal[:len(kdfVal)/2] - IKPrime := kdfVal[len(kdfVal)/2:] - CKPrimeHex := hex.EncodeToString(CKPrime) - IKPrimeHex := hex.EncodeToString(IKPrime) - - // Generate K_encr K_aut K_re MSK EMSK - K_encr, K_aut, K_re, MSK, EMSK := eapAkaPrimePrf(IKPrimeHex, CKPrimeHex, data.Identity) - return CKPrime, IKPrime, K_encr, K_aut, K_re, MSK, EMSK -} - -func TestEapAkaPrimeKeyGen(t *testing.T) { - // From RFC 5448 Appendix C - testCases := []testEapAkaPrimeCase{ - { - "0555444333222111", - "WLAN", - "81e92b6c0ee0e12ebceba8d92a99dfa5", - "bb52e91c747ac3ab2a5c23d15ee351d5", - "9744871ad32bf9bbd1dd5ce54e3e2e5a", - "5349fbe098649f948f5d2e973a81c00f", - "28d7b0f2a2ec3de5", - "0093962d0dd84aa5684b045c9edffa04", - "ccfc230ca74fcc96c0a5d61164f5a76c", - "766fa0a6c317174b812d52fbcd11a179", - "0842ea722ff6835bfa2032499fc3ec23c2f0e388b4f07543ffc677f1696d71ea", - "cf83aa8bc7e0aced892acc98e76a9b2095b558c7795c7094715cb3393aa7d17a", - "67c42d9aa56c1b79e295e3459fc3d187d42be0bf818d3070e362c5e967a4d544" + - "e8ecfe19358ab3039aff03b7c930588c055babee58a02650b067ec4e9347c75a", - "f861703cd775590e16c7679ea3874ada866311de290764d760cf76df647ea01c" + - "313f69924bdd7650ca9bac141ea075c4ef9e8029c0e290cdbad5638b63bc23fb", - }, - { - "0555444333222111", - "HRPD", - "81e92b6c0ee0e12ebceba8d92a99dfa5", - "bb52e91c747ac3ab2a5c23d15ee351d5", - "9744871ad32bf9bbd1dd5ce54e3e2e5a", - "5349fbe098649f948f5d2e973a81c00f", - "28d7b0f2a2ec3de5", - "3820f0277fa5f77732b1fb1d90c1a0da", - "db94a0ab557ef6c9ab48619ca05b9a9f", - "05ad73ac915fce89ac77e1520d82187b", - "5b4acaef62c6ebb8882b2f3d534c4b35277337a00184f20ff25d224c04be2afd", - "3f90bf5c6e5ef325ff04eb5ef6539fa8cca8398194fbd00be425b3f40dba10ac", - "87b321570117cd6c95ab6c436fb5073ff15cf85505d2bc5bb7355fc21ea8a757" + - "57e8f86a2b138002e05752913bb43b82f868a96117e91a2d95f526677d572900", - "c891d5f20f148a1007553e2dea555c9cb672e9675f4a66b4bafa027379f93aee" + - "539a5979d0a0042b9d2ae28bed3b17a31dc8ab75072b80bd0c1da612466e402c", - }, - { - "0555444333222111", - "WLAN", - "e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0", - "a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0", - "b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0", - "c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0", - "d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0", - "cd4c8e5c68f57dd1d7d7dfd0c538e577", - "3ece6b705dbbf7dfc459a11280c65524", - "897d302fa2847416488c28e20dcb7be4", - "c40700e7722483ae3dc7139eb0b88bb558cb3081eccd057f9207d1286ee7dd53", - "0a591a22dd8b5b1cf29e3d508c91dbbdb4aee23051892c42b6a2de66ea504473", - "9f7dca9e37bb22029ed986e7cd09d4a70d1ac76d95535c5cac40a7504699bb89" + - "61a29ef6f3e90f183de5861ad1bedc81ce9916391b401aa006c98785a5756df7", - "724de00bdb9e568187be3fe746114557d5018779537ee37f4d3c6c738cb97b9d" + - "c651bc19bfadc344ffe2b52ca78bd8316b51dacc5f2b1440cb9515521cc7ba23", - }, - { - "0555444333222111", - "WLAN", - "e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0", - "a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0", - "b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0", - "c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0", - "d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0", - "cd4c8e5c68f57dd1d7d7dfd0c538e577", - "3ece6b705dbbf7dfc459a11280c65524", - "897d302fa2847416488c28e20dcb7be4", - "c40700e7722483ae3dc7139eb0b88bb558cb3081eccd057f9207d1286ee7dd53", - "0a591a22dd8b5b1cf29e3d508c91dbbdb4aee23051892c42b6a2de66ea504473", - "9f7dca9e37bb22029ed986e7cd09d4a70d1ac76d95535c5cac40a7504699bb89" + - "61a29ef6f3e90f183de5861ad1bedc81ce9916391b401aa006c98785a5756df7", - "724de00bdb9e568187be3fe746114557d5018779537ee37f4d3c6c738cb97b9d" + - "c651bc19bfadc344ffe2b52ca78bd8316b51dacc5f2b1440cb9515521cc7ba23", - }, - } - - for idx, testData := range testCases { - fmt.Printf("Case %d\n", idx+1) - CKPrime, IKPrime, K_encr, K_aut, K_re, MSK, EMSK := EapAkaPrimeKeyGenAll(testData) - assert.True(t, testData.IKPrime == hex.EncodeToString(IKPrime)) - assert.True(t, testData.CKPrime == hex.EncodeToString(CKPrime)) - assert.True(t, testData.K_encr == hex.EncodeToString(K_encr)) - assert.True(t, testData.K_aut == hex.EncodeToString(K_aut)) - assert.True(t, testData.K_re == hex.EncodeToString(K_re)) - assert.True(t, testData.MSK == hex.EncodeToString(MSK)) - assert.True(t, testData.EMSK == hex.EncodeToString(EMSK)) - } -} diff --git a/internal/sbi/producer/functions.go b/internal/sbi/producer/functions.go deleted file mode 100644 index 00aba64..0000000 --- a/internal/sbi/producer/functions.go +++ /dev/null @@ -1,401 +0,0 @@ -package producer - -import ( - "crypto/hmac" - "crypto/sha256" - "encoding/base64" - "encoding/binary" - "encoding/hex" - "fmt" - "hash" - "strconv" - "time" - - "github.com/antihax/optional" - "github.com/bronze1man/radius" - - ausf_context "github.com/free5gc/ausf/internal/context" - "github.com/free5gc/ausf/internal/logger" - "github.com/free5gc/ausf/internal/sbi/consumer" - "github.com/free5gc/openapi/Nnrf_NFDiscovery" - Nudm_UEAU "github.com/free5gc/openapi/Nudm_UEAuthentication" - "github.com/free5gc/openapi/models" -) - -func KDF5gAka(param ...string) hash.Hash { - s := param[0] - s += param[1] - if p0len, err := strconv.Atoi(param[2]); err != nil { - logger.AuthELog.Warnf("atoi failed: %+v", err) - } else { - s += strconv.FormatInt(int64(p0len), 16) - } - h := hmac.New(sha256.New, []byte(s)) - - return h -} - -func intToByteArray(i int) []byte { - r := make([]byte, 2) - binary.BigEndian.PutUint16(r, uint16(i)) - return r -} - -func padZeros(byteArray []byte, size int) []byte { - l := len(byteArray) - if l == size { - return byteArray - } - r := make([]byte, size) - copy(r[size-l:], byteArray) - return r -} - -func CalculateAtMAC(key []byte, input []byte) []byte { - // keyed with K_aut - h := hmac.New(sha256.New, key) - if _, err := h.Write(input); err != nil { - logger.AuthELog.Errorln(err.Error()) - } - sum := h.Sum(nil) - return sum[:16] -} - -// func EapEncodeAttribute(attributeType string, data string) (returnStr string, err error) { -func EapEncodeAttribute(attributeType string, data string) (string, error) { - var attribute string - var length int - - switch attributeType { - case "AT_RAND": - length = len(data)/8 + 1 - if length != 5 { - return "", fmt.Errorf("[eapEncodeAttribute] AT_RAND Length Error") - } - attrNum := fmt.Sprintf("%02x", ausf_context.AT_RAND_ATTRIBUTE) - attribute = attrNum + "05" + "0000" + data - - case "AT_AUTN": - length = len(data)/8 + 1 - if length != 5 { - return "", fmt.Errorf("[eapEncodeAttribute] AT_AUTN Length Error") - } - attrNum := fmt.Sprintf("%02x", ausf_context.AT_AUTN_ATTRIBUTE) - attribute = attrNum + "05" + "0000" + data - - case "AT_KDF_INPUT": - var byteName []byte - nLength := len(data) - length := (nLength+3)/4 + 1 - b := make([]byte, length*4) - byteNameLength := intToByteArray(nLength) - byteName = []byte(data) - pad := padZeros(byteName, (length-1)*4) - b[0] = 23 - b[1] = byte(length) - copy(b[2:4], byteNameLength) - copy(b[4:], pad) - return string(b[:]), nil - - case "AT_KDF": - // Value 1 default key derivation function for EAP-AKA' - attrNum := fmt.Sprintf("%02x", ausf_context.AT_KDF_ATTRIBUTE) - attribute = attrNum + "01" + "0001" - - case "AT_MAC": - // Pad MAC value with 16 bytes of 0 since this is just for the calculation of MAC - attrNum := fmt.Sprintf("%02x", ausf_context.AT_MAC_ATTRIBUTE) - attribute = attrNum + "05" + "0000" + "00000000000000000000000000000000" - - case "AT_RES": - var byteName []byte - nLength := len(data) - length := (nLength+3)/4 + 1 - b := make([]byte, length*4) - byteNameLength := intToByteArray(nLength) - byteName = []byte(data) - pad := padZeros(byteName, (length-1)*4) - b[0] = 3 - b[1] = byte(length) - copy(b[2:4], byteNameLength) - copy(b[4:], pad) - return string(b[:]), nil - - default: - logger.AuthELog.Errorf("UNKNOWN attributeType %s\n", attributeType) - return "", nil - } - - if r, err := hex.DecodeString(attribute); err != nil { - return "", err - } else { - return string(r), nil - } -} - -// func eapAkaPrimePrf(ikPrime string, ckPrime string, identity string) (K_encr string, K_aut string, K_re string, -// MSK string, EMSK string) { -func eapAkaPrimePrf(ikPrime string, ckPrime string, identity string) ([]byte, []byte, []byte, []byte, []byte) { - keyAp := ikPrime + ckPrime - - var key []byte - if keyTmp, err := hex.DecodeString(keyAp); err != nil { - logger.AuthELog.Warnf("Decode key AP failed: %+v", err) - } else { - key = keyTmp - } - sBase := []byte("EAP-AKA'" + identity) - - MK := []byte("") - prev := []byte("") - //_ = prev - prfRounds := 208/32 + 1 - for i := 0; i < prfRounds; i++ { - // Create a new HMAC by defining the hash type and the key (as byte array) - h := hmac.New(sha256.New, key) - - hexNum := (byte)(i + 1) - ap := append(sBase, hexNum) - s := append(prev, ap...) - - // Write Data to it - if _, err := h.Write(s); err != nil { - logger.AuthELog.Errorln(err.Error()) - } - - // Get result - sha := h.Sum(nil) - MK = append(MK, sha...) - prev = sha - } - - K_encr := MK[0:16] // 0..127 - K_aut := MK[16:48] // 128..383 - K_re := MK[48:80] // 384..639 - MSK := MK[80:144] // 640..1151 - EMSK := MK[144:208] // 1152..1663 - return K_encr, K_aut, K_re, MSK, EMSK -} - -func decodeEapAkaPrime(eapPkt []byte) (*ausf_context.EapAkaPrimePkt, error) { - var decodePkt ausf_context.EapAkaPrimePkt - var attrLen int - var decodeAttr ausf_context.EapAkaPrimeAttribute - attributes := make(map[uint8]ausf_context.EapAkaPrimeAttribute) - data := eapPkt[5:] - decodePkt.Subtype = data[0] - dataLen := len(data) - - // decode attributes - for i := 3; i < dataLen; i += attrLen { - attrType := data[i] - attrLen = int(data[i+1]) * 4 - if attrLen == 0 { - return nil, fmt.Errorf("attribute length equal to zero") - } - if i+attrLen > dataLen { - return nil, fmt.Errorf("packet length out of range") - } - switch attrType { - case ausf_context.AT_RES_ATTRIBUTE: - logger.AuthELog.Tracef("Decoding AT_RES\n") - accLen := int(data[i+3] >> 3) - if accLen > 16 || accLen < 4 || accLen+4 > attrLen { - return nil, fmt.Errorf("attribute AT_RES decode err") - } - - decodeAttr.Type = attrType - decodeAttr.Length = data[i+1] - decodeAttr.Value = data[i+4 : i+4+accLen] - attributes[attrType] = decodeAttr - case ausf_context.AT_MAC_ATTRIBUTE: - logger.AuthELog.Tracef("Decoding AT_MAC\n") - if attrLen != 20 { - return nil, fmt.Errorf("attribute AT_MAC decode err") - } - decodeAttr.Type = attrType - decodeAttr.Length = data[i+1] - Mac := make([]byte, attrLen-4) - copy(Mac, data[i+4:i+attrLen]) - decodeAttr.Value = Mac - attributes[attrType] = decodeAttr - - // clean AT_MAC value for integrity check later - zeros := make([]byte, attrLen-4) - copy(data[i+4:i+attrLen], zeros) - decodePkt.MACInput = eapPkt - case ausf_context.AT_KDF_ATTRIBUTE: - logger.AuthELog.Tracef("Decoding AT_KDF\n") - if attrLen != 4 { - return nil, fmt.Errorf("attribute AT_KDF decode err") - } - decodeAttr.Type = attrType - decodeAttr.Length = data[i+1] - decodeAttr.Value = data[i+2 : i+attrLen] - attributes[attrType] = decodeAttr - case ausf_context.AT_AUTS_ATTRIBUTE: - logger.AuthELog.Tracef("Decoding AT_AUTS\n") - if attrLen != 16 { - return nil, fmt.Errorf("attribute AT_AUTS decode err") - } - decodeAttr.Type = attrType - decodeAttr.Length = data[i+1] - decodeAttr.Value = data[i+2 : i+attrLen] - attributes[attrType] = decodeAttr - case ausf_context.AT_CLIENT_ERROR_CODE_ATTRIBUTE: - logger.AuthELog.Tracef("Decoding AT_CLIENT_ERROR_CODE\n") - if attrLen != 4 { - return nil, fmt.Errorf("attribute AT_CLIENT_ERROR_CODE decode err") - } - decodeAttr.Type = attrType - decodeAttr.Length = data[i+1] - decodeAttr.Value = data[i+2 : i+attrLen] - attributes[attrType] = decodeAttr - default: - logger.AuthELog.Tracef("attribute type %x skipped\n", attrType) - } - } - - switch decodePkt.Subtype { - case ausf_context.AKA_CHALLENGE_SUBTYPE: - logger.AuthELog.Tracef("Subtype AKA-Challenge\n") - if _, ok := attributes[ausf_context.AT_RES_ATTRIBUTE]; !ok { - return nil, fmt.Errorf("AKA-Challenge attributes error") - } else if _, ok := attributes[ausf_context.AT_MAC_ATTRIBUTE]; !ok { - return nil, fmt.Errorf("AKA-Challenge attributes error") - } - case ausf_context.AKA_AUTHENTICATION_REJECT_SUBTYPE: - logger.AuthELog.Tracef("Subtype AKA-Authentication-Reject\n") - if len(attributes) != 0 { - return nil, fmt.Errorf("AKA-Authentication-Reject attributes error") - } - case ausf_context.AKA_SYNCHRONIZATION_FAILURE_SUBTYPE: - logger.AuthELog.Tracef("Subtype AKA-Synchronization-Failure\n") - if len(attributes) != 2 { - return nil, fmt.Errorf("AKA-Synchornization-Failure attributes error") - } else if _, ok := attributes[ausf_context.AT_AUTS_ATTRIBUTE]; !ok { - return nil, fmt.Errorf("AKA-Synchornization-Failure attributes error") - } else if _, ok := attributes[ausf_context.AT_KDF_ATTRIBUTE]; !ok { - return nil, fmt.Errorf("AKA-Synchornization-Failure attributes error") - } else if kdfVal := attributes[ausf_context.AT_KDF_ATTRIBUTE].Value; !(kdfVal[0] == 0 && kdfVal[1] == 1) { - return nil, fmt.Errorf("AKA-Synchornization-Failure attributes error") - } - case ausf_context.AKA_NOTIFICATION_SUBTYPE: - logger.AuthELog.Tracef("Subtype AKA-Notification\n") - case ausf_context.AKA_CLIENT_ERROR_SUBTYPE: - logger.AuthELog.Tracef("Subtype AKA-Client-Error\n") - if len(attributes) != 1 { - return nil, fmt.Errorf("AKA-Client-Error attributes error") - } else if _, ok := attributes[ausf_context.AT_CLIENT_ERROR_CODE_ATTRIBUTE]; !ok { - return nil, fmt.Errorf("AKA-Client-Error attributes error") - } - default: - logger.AuthELog.Tracef("subtype %x skipped\n", decodePkt.Subtype) - } - - decodePkt.Attributes = attributes - - return &decodePkt, nil -} - -func ConstructFailEapAkaNotification(oldPktId uint8) string { - var eapPkt radius.EapPacket - eapPkt.Code = radius.EapCodeRequest - eapPkt.Identifier = oldPktId + 1 - eapPkt.Type = ausf_context.EAP_AKA_PRIME_TYPENUM - - eapAkaHdrBytes := make([]byte, 3) - eapAkaHdrBytes[0] = ausf_context.AKA_NOTIFICATION_SUBTYPE - - attrNum := fmt.Sprintf("%02x", ausf_context.AT_NOTIFICATION_ATTRIBUTE) - attribute := attrNum + "01" + "4000" - var attrHex []byte - if attrHexTmp, err := hex.DecodeString(attribute); err != nil { - logger.AuthELog.Warnf("Decode attribute failed: %+v", err) - } else { - attrHex = attrHexTmp - } - - eapPkt.Data = append(eapAkaHdrBytes, attrHex...) - eapPktEncode := eapPkt.Encode() - return base64.StdEncoding.EncodeToString(eapPktEncode) -} - -func ConstructEapNoTypePkt(code radius.EapCode, pktID uint8) string { - b := make([]byte, 4) - b[0] = byte(code) - b[1] = pktID - binary.BigEndian.PutUint16(b[2:4], uint16(4)) - return base64.StdEncoding.EncodeToString(b) -} - -func getUdmUrl(nrfUri string) string { - udmUrl := "https://localhost:29503" // default - nfDiscoverParam := Nnrf_NFDiscovery.SearchNFInstancesParamOpts{ - ServiceNames: optional.NewInterface([]models.ServiceName{models.ServiceName_NUDM_UEAU}), - } - res, err := consumer.SendSearchNFInstances(nrfUri, models.NfType_UDM, models.NfType_AUSF, nfDiscoverParam) - if err != nil { - logger.UeAuthLog.Errorln("[Search UDM UEAU] ", err.Error()) - } else if len(res.NfInstances) > 0 { - udmInstance := res.NfInstances[0] - if len(udmInstance.Ipv4Addresses) > 0 && udmInstance.NfServices != nil { - ueauService := (*udmInstance.NfServices)[0] - ueauEndPoint := (*ueauService.IpEndPoints)[0] - udmUrl = string(ueauService.Scheme) + "://" + ueauEndPoint.Ipv4Address + ":" + strconv.Itoa(int(ueauEndPoint.Port)) - } - } else { - logger.UeAuthLog.Errorln("[Search UDM UEAU] len(NfInstances) = 0") - } - return udmUrl -} - -func createClientToUdmUeau(udmUrl string) *Nudm_UEAU.APIClient { - cfg := Nudm_UEAU.NewConfiguration() - cfg.SetBasePath(udmUrl) - clientAPI := Nudm_UEAU.NewAPIClient(cfg) - return clientAPI -} - -func sendAuthResultToUDM(id string, authType models.AuthType, success bool, servingNetworkName, udmUrl string) error { - timeNow := time.Now() - timePtr := &timeNow - - self := ausf_context.GetSelf() - - var authEvent models.AuthEvent - authEvent.TimeStamp = timePtr - authEvent.AuthType = authType - authEvent.Success = success - authEvent.ServingNetworkName = servingNetworkName - authEvent.NfInstanceId = self.GetSelfID() - - client := createClientToUdmUeau(udmUrl) - - ctx, _, err := ausf_context.GetSelf().GetTokenCtx(models.ServiceName_NUDM_UEAU, models.NfType_UDM) - if err != nil { - return err - } - - _, rsp, confirmAuthErr := client.ConfirmAuthApi.ConfirmAuth(ctx, id, authEvent) - defer func() { - if rspCloseErr := rsp.Body.Close(); rspCloseErr != nil { - logger.ConsumerLog.Errorf("ConfirmAuth Response cannot close: %v", rspCloseErr) - } - }() - return confirmAuthErr -} - -func logConfirmFailureAndInformUDM(id string, authType models.AuthType, servingNetworkName, errStr, udmUrl string) { - if authType == models.AuthType__5_G_AKA { - logger.Auth5gAkaLog.Infoln(errStr) - if sendErr := sendAuthResultToUDM(id, authType, false, "", udmUrl); sendErr != nil { - logger.Auth5gAkaLog.Infoln(sendErr.Error()) - } - } else if authType == models.AuthType_EAP_AKA_PRIME { - logger.AuthELog.Infoln(errStr) - if sendErr := sendAuthResultToUDM(id, authType, false, "", udmUrl); sendErr != nil { - logger.AuthELog.Infoln(sendErr.Error()) - } - } -} diff --git a/internal/sbi/producer/ue_authentication.go b/internal/sbi/producer/ue_authentication.go deleted file mode 100644 index 6e9384b..0000000 --- a/internal/sbi/producer/ue_authentication.go +++ /dev/null @@ -1,559 +0,0 @@ -package producer - -import ( - "bytes" - "crypto/sha256" - "encoding/base64" - "encoding/hex" - "math/rand" - "net/http" - "strings" - "time" - - "github.com/bronze1man/radius" - "github.com/google/gopacket" - "github.com/google/gopacket/layers" - - ausf_context "github.com/free5gc/ausf/internal/context" - "github.com/free5gc/ausf/internal/logger" - "github.com/free5gc/ausf/pkg/factory" - "github.com/free5gc/openapi/models" - "github.com/free5gc/util/httpwrapper" - "github.com/free5gc/util/ueauth" -) - -func HandleEapAuthComfirmRequest(request *httpwrapper.Request) *httpwrapper.Response { - logger.Auth5gAkaLog.Infof("EapAuthComfirmRequest") - - updateEapSession := request.Body.(models.EapSession) - eapSessionID := request.Params["authCtxId"] - - response, problemDetails := EapAuthComfirmRequestProcedure(updateEapSession, eapSessionID) - - if response != nil { - return httpwrapper.NewResponse(http.StatusOK, nil, response) - } else if problemDetails != nil { - return httpwrapper.NewResponse(int(problemDetails.Status), nil, problemDetails) - } - problemDetails = &models.ProblemDetails{ - Status: http.StatusForbidden, - Cause: "UNSPECIFIED", - } - return httpwrapper.NewResponse(http.StatusForbidden, nil, problemDetails) -} - -func HandleAuth5gAkaComfirmRequest(request *httpwrapper.Request) *httpwrapper.Response { - logger.Auth5gAkaLog.Infof("Auth5gAkaComfirmRequest") - updateConfirmationData := request.Body.(models.ConfirmationData) - ConfirmationDataResponseID := request.Params["authCtxId"] - - response, problemDetails := Auth5gAkaComfirmRequestProcedure(updateConfirmationData, ConfirmationDataResponseID) - if response != nil { - return httpwrapper.NewResponse(http.StatusOK, nil, response) - } else if problemDetails != nil { - return httpwrapper.NewResponse(int(problemDetails.Status), nil, problemDetails) - } - problemDetails = &models.ProblemDetails{ - Status: http.StatusForbidden, - Cause: "UNSPECIFIED", - } - return httpwrapper.NewResponse(http.StatusForbidden, nil, problemDetails) -} - -func HandleUeAuthPostRequest(request *httpwrapper.Request) *httpwrapper.Response { - logger.UeAuthLog.Infof("HandleUeAuthPostRequest") - updateAuthenticationInfo := request.Body.(models.AuthenticationInfo) - - response, locationURI, problemDetails := UeAuthPostRequestProcedure(updateAuthenticationInfo) - respHeader := make(http.Header) - respHeader.Set("Location", locationURI) - - if response != nil { - return httpwrapper.NewResponse(http.StatusCreated, respHeader, response) - } else if problemDetails != nil { - return httpwrapper.NewResponse(int(problemDetails.Status), nil, problemDetails) - } - problemDetails = &models.ProblemDetails{ - Status: http.StatusForbidden, - Cause: "UNSPECIFIED", - } - return httpwrapper.NewResponse(http.StatusForbidden, nil, problemDetails) -} - -// func UeAuthPostRequestProcedure(updateAuthenticationInfo models.AuthenticationInfo, -// ) (response *models.UeAuthenticationCtx, locationURI string, problemDetails *models.ProblemDetails) { -func UeAuthPostRequestProcedure(updateAuthenticationInfo models.AuthenticationInfo) (*models.UeAuthenticationCtx, - string, *models.ProblemDetails, -) { - var responseBody models.UeAuthenticationCtx - var authInfoReq models.AuthenticationInfoRequest - - supiOrSuci := updateAuthenticationInfo.SupiOrSuci - - snName := updateAuthenticationInfo.ServingNetworkName - servingNetworkAuthorized := ausf_context.IsServingNetworkAuthorized(snName) - if !servingNetworkAuthorized { - var problemDetails models.ProblemDetails - problemDetails.Cause = "SERVING_NETWORK_NOT_AUTHORIZED" - problemDetails.Status = http.StatusForbidden - logger.UeAuthLog.Infoln("403 forbidden: serving network NOT AUTHORIZED") - return nil, "", &problemDetails - } - logger.UeAuthLog.Infoln("Serving network authorized") - - responseBody.ServingNetworkName = snName - authInfoReq.ServingNetworkName = snName - self := ausf_context.GetSelf() - authInfoReq.AusfInstanceId = self.GetSelfID() - - var lastEapID uint8 - if updateAuthenticationInfo.ResynchronizationInfo != nil { - logger.UeAuthLog.Warningln("Auts: ", updateAuthenticationInfo.ResynchronizationInfo.Auts) - ausfCurrentSupi := ausf_context.GetSupiFromSuciSupiMap(supiOrSuci) - logger.UeAuthLog.Warningln(ausfCurrentSupi) - ausfCurrentContext := ausf_context.GetAusfUeContext(ausfCurrentSupi) - logger.UeAuthLog.Warningln(ausfCurrentContext.Rand) - if updateAuthenticationInfo.ResynchronizationInfo.Rand == "" { - updateAuthenticationInfo.ResynchronizationInfo.Rand = ausfCurrentContext.Rand - } - logger.UeAuthLog.Warningln("Rand: ", updateAuthenticationInfo.ResynchronizationInfo.Rand) - authInfoReq.ResynchronizationInfo = updateAuthenticationInfo.ResynchronizationInfo - lastEapID = ausfCurrentContext.EapID - } - - udmUrl := getUdmUrl(self.NrfUri) - client := createClientToUdmUeau(udmUrl) - - ctx, _, err := ausf_context.GetSelf().GetTokenCtx(models.ServiceName_NUDM_UEAU, models.NfType_UDM) - if err != nil { - return nil, "", nil - } - - authInfoResult, rsp, err := client.GenerateAuthDataApi.GenerateAuthData(ctx, supiOrSuci, authInfoReq) - if err != nil { - logger.UeAuthLog.Infoln(err.Error()) - var problemDetails models.ProblemDetails - if authInfoResult.AuthenticationVector == nil { - problemDetails.Cause = "AV_GENERATION_PROBLEM" - } else { - problemDetails.Cause = "UPSTREAM_SERVER_ERROR" - } - problemDetails.Status = int32(rsp.StatusCode) - return nil, "", &problemDetails - } - defer func() { - if rspCloseErr := rsp.Body.Close(); rspCloseErr != nil { - logger.UeAuthLog.Errorf("GenerateAuthDataApi response body cannot close: %+v", rspCloseErr) - } - }() - - ueid := authInfoResult.Supi - ausfUeContext := ausf_context.NewAusfUeContext(ueid) - ausfUeContext.ServingNetworkName = snName - ausfUeContext.AuthStatus = models.AuthResult_ONGOING - ausfUeContext.UdmUeauUrl = udmUrl - ausf_context.AddAusfUeContextToPool(ausfUeContext) - - logger.UeAuthLog.Infof("Add SuciSupiPair (%s, %s) to map.\n", supiOrSuci, ueid) - ausf_context.AddSuciSupiPairToMap(supiOrSuci, ueid) - - locationURI := self.Url + factory.AusfAuthResUriPrefix + "/ue-authentications/" + supiOrSuci - putLink := locationURI - if authInfoResult.AuthType == models.AuthType__5_G_AKA { - logger.UeAuthLog.Infoln("Use 5G AKA auth method") - putLink += "/5g-aka-confirmation" - - // Derive HXRES* from XRES* - concat := authInfoResult.AuthenticationVector.Rand + authInfoResult.AuthenticationVector.XresStar - var hxresStarBytes []byte - if bytes, err := hex.DecodeString(concat); err != nil { - logger.Auth5gAkaLog.Errorf("decode concat error: %+v", err) - return nil, "", - &models.ProblemDetails{ - Title: "Concat Decode Problem", - Cause: "CONCAT_DECODE_PROBLEM", - Detail: err.Error(), - Status: http.StatusInternalServerError, - } - } else { - hxresStarBytes = bytes - } - hxresStarAll := sha256.Sum256(hxresStarBytes) - hxresStar := hex.EncodeToString(hxresStarAll[16:]) // last 128 bits - logger.Auth5gAkaLog.Infof("XresStar = %x\n", authInfoResult.AuthenticationVector.XresStar) - - // Derive Kseaf from Kausf - Kausf := authInfoResult.AuthenticationVector.Kausf - var KausfDecode []byte - if ausfDecode, err := hex.DecodeString(Kausf); err != nil { - logger.Auth5gAkaLog.Errorf("decode Kausf failed: %+v", err) - return nil, "", - &models.ProblemDetails{ - Title: "Kausf Decode Problem", - Cause: "KAUSF_DECODE_PROBLEM", - Detail: err.Error(), - Status: http.StatusInternalServerError, - } - } else { - KausfDecode = ausfDecode - } - P0 := []byte(snName) - Kseaf, err := ueauth.GetKDFValue(KausfDecode, ueauth.FC_FOR_KSEAF_DERIVATION, P0, ueauth.KDFLen(P0)) - if err != nil { - logger.Auth5gAkaLog.Errorf("GetKDFValue failed: %+v", err) - return nil, "", - &models.ProblemDetails{ - Title: "Kseaf Derivation Problem", - Cause: "KSEAF_DERIVATION_PROBLEM", - Detail: err.Error(), - Status: http.StatusInternalServerError, - } - } - ausfUeContext.XresStar = authInfoResult.AuthenticationVector.XresStar - ausfUeContext.Kausf = Kausf - ausfUeContext.Kseaf = hex.EncodeToString(Kseaf) - ausfUeContext.Rand = authInfoResult.AuthenticationVector.Rand - - var av5gAka models.Av5gAka - av5gAka.Rand = authInfoResult.AuthenticationVector.Rand - av5gAka.Autn = authInfoResult.AuthenticationVector.Autn - av5gAka.HxresStar = hxresStar - responseBody.Var5gAuthData = av5gAka - - linksValue := models.LinksValueSchema{Href: putLink} - responseBody.Links = make(map[string]models.LinksValueSchema) - responseBody.Links["5g-aka"] = linksValue - } else if authInfoResult.AuthType == models.AuthType_EAP_AKA_PRIME { - logger.UeAuthLog.Infoln("Use EAP-AKA' auth method") - putLink += "/eap-session" - - var identity string - // TODO support more SUPI type - if ueid[:4] == "imsi" { - if !self.EapAkaSupiImsiPrefix { - // 33.501 v15.9.0 or later - identity = ueid[5:] - } else { - // 33.501 v15.8.0 or earlier - identity = ueid - } - } - ikPrime := authInfoResult.AuthenticationVector.IkPrime - ckPrime := authInfoResult.AuthenticationVector.CkPrime - RAND := authInfoResult.AuthenticationVector.Rand - AUTN := authInfoResult.AuthenticationVector.Autn - XRES := authInfoResult.AuthenticationVector.Xres - ausfUeContext.XRES = XRES - - ausfUeContext.Rand = authInfoResult.AuthenticationVector.Rand - - _, K_aut, _, _, EMSK := eapAkaPrimePrf(ikPrime, ckPrime, identity) - logger.AuthELog.Tracef("K_aut: %x", K_aut) - ausfUeContext.K_aut = hex.EncodeToString(K_aut) - Kausf := EMSK[0:32] - ausfUeContext.Kausf = hex.EncodeToString(Kausf) - P0 := []byte(snName) - Kseaf, err := ueauth.GetKDFValue(Kausf, ueauth.FC_FOR_KSEAF_DERIVATION, P0, ueauth.KDFLen(P0)) - if err != nil { - logger.AuthELog.Errorf("GetKDFValue failed: %+v", err) - } - ausfUeContext.Kseaf = hex.EncodeToString(Kseaf) - - var eapPkt radius.EapPacket - eapPkt.Code = radius.EapCode(1) - if updateAuthenticationInfo.ResynchronizationInfo == nil { - src := rand.NewSource(time.Now().UnixNano()) - r := rand.New(src) - randIdentifier := r.Intn(256) - ausfUeContext.EapID = uint8(randIdentifier) - } else { - ausfUeContext.EapID = lastEapID + 1 - } - eapPkt.Identifier = ausfUeContext.EapID - eapPkt.Type = radius.EapType(50) // according to RFC5448 6.1 - - var eapAKAHdr, atRand, atAutn, atKdf, atKdfInput, atMAC string - eapAKAHdrBytes := make([]byte, 3) // RFC4187 8.1 - eapAKAHdrBytes[0] = ausf_context.AKA_CHALLENGE_SUBTYPE - eapAKAHdr = string(eapAKAHdrBytes) - if atRandTmp, err := EapEncodeAttribute("AT_RAND", RAND); err != nil { - logger.AuthELog.Errorf("EAP encode RAND failed: %+v", err) - } else { - atRand = atRandTmp - } - if atAutnTmp, err := EapEncodeAttribute("AT_AUTN", AUTN); err != nil { - logger.AuthELog.Errorf("EAP encode AUTN failed: %+v", err) - } else { - atAutn = atAutnTmp - } - if atKdfTmp, err := EapEncodeAttribute("AT_KDF", snName); err != nil { - logger.AuthELog.Errorf("EAP encode KDF failed: %+v", err) - } else { - atKdf = atKdfTmp - } - if atKdfInputTmp, err := EapEncodeAttribute("AT_KDF_INPUT", snName); err != nil { - logger.AuthELog.Errorf("EAP encode KDF failed: %+v", err) - } else { - atKdfInput = atKdfInputTmp - } - if atMACTmp, err := EapEncodeAttribute("AT_MAC", ""); err != nil { - logger.AuthELog.Errorf("EAP encode MAC failed: %+v", err) - } else { - atMAC = atMACTmp - } - - dataArrayBeforeMAC := eapAKAHdr + atRand + atAutn + atKdf + atKdfInput + atMAC - eapPkt.Data = []byte(dataArrayBeforeMAC) - encodedPktBeforeMAC := eapPkt.Encode() - - MacValue := CalculateAtMAC(K_aut, encodedPktBeforeMAC) - atMAC = atMAC[:4] + string(MacValue) - - dataArrayAfterMAC := eapAKAHdr + atRand + atAutn + atKdf + atKdfInput + atMAC - - eapPkt.Data = []byte(dataArrayAfterMAC) - encodedPktAfterMAC := eapPkt.Encode() - responseBody.Var5gAuthData = base64.StdEncoding.EncodeToString(encodedPktAfterMAC) - - linksValue := models.LinksValueSchema{Href: putLink} - responseBody.Links = make(map[string]models.LinksValueSchema) - responseBody.Links["eap-session"] = linksValue - } - - responseBody.AuthType = authInfoResult.AuthType - - return &responseBody, locationURI, nil -} - -// func Auth5gAkaComfirmRequestProcedure(updateConfirmationData models.ConfirmationData, -// ConfirmationDataResponseID string) (response *models.ConfirmationDataResponse, -// problemDetails *models.ProblemDetails) { - -func Auth5gAkaComfirmRequestProcedure(updateConfirmationData models.ConfirmationData, - ConfirmationDataResponseID string, -) (*models.ConfirmationDataResponse, *models.ProblemDetails) { - var responseBody models.ConfirmationDataResponse - success := false - responseBody.AuthResult = models.AuthResult_FAILURE - - if !ausf_context.CheckIfSuciSupiPairExists(ConfirmationDataResponseID) { - logger.Auth5gAkaLog.Infof("supiSuciPair does not exist, confirmation failed (queried by %s)\n", - ConfirmationDataResponseID) - var problemDetails models.ProblemDetails - problemDetails.Cause = "USER_NOT_FOUND" - problemDetails.Status = http.StatusBadRequest - return nil, &problemDetails - } - - currentSupi := ausf_context.GetSupiFromSuciSupiMap(ConfirmationDataResponseID) - if !ausf_context.CheckIfAusfUeContextExists(currentSupi) { - logger.Auth5gAkaLog.Infof("SUPI does not exist, confirmation failed (queried by %s)\n", currentSupi) - var problemDetails models.ProblemDetails - problemDetails.Cause = "USER_NOT_FOUND" - problemDetails.Status = http.StatusBadRequest - return nil, &problemDetails - } - - ausfCurrentContext := ausf_context.GetAusfUeContext(currentSupi) - servingNetworkName := ausfCurrentContext.ServingNetworkName - - // Compare the received RES* with the stored XRES* - logger.Auth5gAkaLog.Infof("res*: %x\nXres*: %x\n", updateConfirmationData.ResStar, ausfCurrentContext.XresStar) - if strings.EqualFold(updateConfirmationData.ResStar, ausfCurrentContext.XresStar) { - ausfCurrentContext.AuthStatus = models.AuthResult_SUCCESS - responseBody.AuthResult = models.AuthResult_SUCCESS - success = true - logger.Auth5gAkaLog.Infoln("5G AKA confirmation succeeded") - responseBody.Supi = currentSupi - responseBody.Kseaf = ausfCurrentContext.Kseaf - } else { - ausfCurrentContext.AuthStatus = models.AuthResult_FAILURE - responseBody.AuthResult = models.AuthResult_FAILURE - logConfirmFailureAndInformUDM(ConfirmationDataResponseID, models.AuthType__5_G_AKA, servingNetworkName, - "5G AKA confirmation failed", ausfCurrentContext.UdmUeauUrl) - } - - if sendErr := sendAuthResultToUDM(currentSupi, models.AuthType__5_G_AKA, success, servingNetworkName, - ausfCurrentContext.UdmUeauUrl); sendErr != nil { - logger.Auth5gAkaLog.Infoln(sendErr.Error()) - var problemDetails models.ProblemDetails - problemDetails.Status = http.StatusInternalServerError - problemDetails.Cause = "UPSTREAM_SERVER_ERROR" - - return nil, &problemDetails - } - - return &responseBody, nil -} - -// return response, problemDetails -func EapAuthComfirmRequestProcedure(updateEapSession models.EapSession, eapSessionID string) (*models.EapSession, - *models.ProblemDetails, -) { - var responseBody models.EapSession - - if !ausf_context.CheckIfSuciSupiPairExists(eapSessionID) { - logger.AuthELog.Infoln("supiSuciPair does not exist, confirmation failed") - var problemDetails models.ProblemDetails - problemDetails.Cause = "USER_NOT_FOUND" - return nil, &problemDetails - } - - currentSupi := ausf_context.GetSupiFromSuciSupiMap(eapSessionID) - if !ausf_context.CheckIfAusfUeContextExists(currentSupi) { - logger.AuthELog.Infoln("SUPI does not exist, confirmation failed") - var problemDetails models.ProblemDetails - problemDetails.Cause = "USER_NOT_FOUND" - return nil, &problemDetails - } - - ausfCurrentContext := ausf_context.GetAusfUeContext(currentSupi) - servingNetworkName := ausfCurrentContext.ServingNetworkName - - if ausfCurrentContext.AuthStatus == models.AuthResult_FAILURE { - eapFailPkt := ConstructEapNoTypePkt(radius.EapCodeFailure, 0) - responseBody.EapPayload = eapFailPkt - responseBody.AuthResult = models.AuthResult_FAILURE - return &responseBody, nil - } - - var eapPayload []byte - if eapPayloadTmp, err := base64.StdEncoding.DecodeString(updateEapSession.EapPayload); err != nil { - logger.AuthELog.Warnf("EAP Payload decode failed: %+v", err) - } else { - eapPayload = eapPayloadTmp - } - - eapGoPkt := gopacket.NewPacket(eapPayload, layers.LayerTypeEAP, gopacket.Default) - eapLayer := eapGoPkt.Layer(layers.LayerTypeEAP) - eapContent, _ := eapLayer.(*layers.EAP) - eapOK := true - var eapErrStr string - - if eapContent.Code != layers.EAPCodeResponse { - eapOK = false - eapErrStr = "eap packet code error" - } else if eapContent.Type != ausf_context.EAP_AKA_PRIME_TYPENUM { - eapOK = false - eapErrStr = "eap packet type error" - } else if decodeEapAkaPrimePkt, err := decodeEapAkaPrime(eapContent.Contents); err != nil { - logger.AuthELog.Warnf("EAP-AKA' decode failed: %+v", err) - eapOK = false - eapErrStr = "eap packet error" - } else { - switch decodeEapAkaPrimePkt.Subtype { - case ausf_context.AKA_CHALLENGE_SUBTYPE: - K_autStr := ausfCurrentContext.K_aut - var K_aut []byte - if K_autTmp, err := hex.DecodeString(K_autStr); err != nil { - logger.AuthELog.Warnf("K_aut decode error: %+v", err) - } else { - K_aut = K_autTmp - } - XMAC := CalculateAtMAC(K_aut, decodeEapAkaPrimePkt.MACInput) - MAC := decodeEapAkaPrimePkt.Attributes[ausf_context.AT_MAC_ATTRIBUTE].Value - XRES := ausfCurrentContext.XRES - RES := hex.EncodeToString(decodeEapAkaPrimePkt.Attributes[ausf_context.AT_RES_ATTRIBUTE].Value) - - if !bytes.Equal(MAC, XMAC) { - eapOK = false - eapErrStr = "EAP-AKA' integrity check fail" - } else if XRES == RES { - logger.AuthELog.Infoln("Correct RES value, EAP-AKA' auth succeed") - responseBody.KSeaf = ausfCurrentContext.Kseaf - responseBody.Supi = currentSupi - responseBody.AuthResult = models.AuthResult_SUCCESS - eapSuccPkt := ConstructEapNoTypePkt(radius.EapCodeSuccess, eapContent.Id) - responseBody.EapPayload = eapSuccPkt - udmUrl := ausfCurrentContext.UdmUeauUrl - if sendErr := sendAuthResultToUDM( - eapSessionID, - models.AuthType_EAP_AKA_PRIME, - true, - servingNetworkName, - udmUrl); sendErr != nil { - logger.AuthELog.Infoln(sendErr.Error()) - var problemDetails models.ProblemDetails - problemDetails.Cause = "UPSTREAM_SERVER_ERROR" - return nil, &problemDetails - } - ausfCurrentContext.AuthStatus = models.AuthResult_SUCCESS - } else { - eapOK = false - eapErrStr = "Wrong RES value, EAP-AKA' auth failed" - } - case ausf_context.AKA_AUTHENTICATION_REJECT_SUBTYPE: - ausfCurrentContext.AuthStatus = models.AuthResult_FAILURE - case ausf_context.AKA_SYNCHRONIZATION_FAILURE_SUBTYPE: - logger.AuthELog.Warnf("EAP-AKA' synchronziation failure") - if ausfCurrentContext.Resynced { - eapOK = false - eapErrStr = "2 consecutive Synch Failure, terminate authentication procedure" - } else { - var authInfo models.AuthenticationInfo - AUTS := decodeEapAkaPrimePkt.Attributes[ausf_context.AT_AUTS_ATTRIBUTE].Value - resynchronizationInfo := &models.ResynchronizationInfo{ - Auts: hex.EncodeToString(AUTS[:]), - } - authInfo.SupiOrSuci = eapSessionID - authInfo.ServingNetworkName = servingNetworkName - authInfo.ResynchronizationInfo = resynchronizationInfo - response, _, problemDetails := UeAuthPostRequestProcedure(authInfo) - if problemDetails != nil { - return nil, problemDetails - } - ausfCurrentContext.Resynced = true - - responseBody.EapPayload = response.Var5gAuthData.(string) - responseBody.Links = response.Links - responseBody.AuthResult = models.AuthResult_ONGOING - } - case ausf_context.AKA_NOTIFICATION_SUBTYPE: - ausfCurrentContext.AuthStatus = models.AuthResult_FAILURE - case ausf_context.AKA_CLIENT_ERROR_SUBTYPE: - logger.AuthELog.Warnf("EAP-AKA' failure: receive client-error") - ausfCurrentContext.AuthStatus = models.AuthResult_FAILURE - default: - ausfCurrentContext.AuthStatus = models.AuthResult_FAILURE - } - } - - if !eapOK { - logger.AuthELog.Warnf("EAP-AKA' failure: %s", eapErrStr) - if sendErr := sendAuthResultToUDM(eapSessionID, models.AuthType_EAP_AKA_PRIME, false, servingNetworkName, - ausfCurrentContext.UdmUeauUrl); sendErr != nil { - logger.AuthELog.Infoln(sendErr.Error()) - var problemDetails models.ProblemDetails - problemDetails.Status = http.StatusInternalServerError - problemDetails.Cause = "UPSTREAM_SERVER_ERROR" - - return nil, &problemDetails - } - - ausfCurrentContext.AuthStatus = models.AuthResult_FAILURE - responseBody.AuthResult = models.AuthResult_ONGOING - failEapAkaNoti := ConstructFailEapAkaNotification(eapContent.Id) - responseBody.EapPayload = failEapAkaNoti - self := ausf_context.GetSelf() - linkUrl := self.Url + factory.AusfAuthResUriPrefix + "/ue-authentications/" + eapSessionID + "/eap-session" - linksValue := models.LinksValueSchema{Href: linkUrl} - responseBody.Links = make(map[string]models.LinksValueSchema) - responseBody.Links["eap-session"] = linksValue - } else if ausfCurrentContext.AuthStatus == models.AuthResult_FAILURE { - if sendErr := sendAuthResultToUDM(eapSessionID, models.AuthType_EAP_AKA_PRIME, false, servingNetworkName, - ausfCurrentContext.UdmUeauUrl); sendErr != nil { - logger.AuthELog.Infoln(sendErr.Error()) - var problemDetails models.ProblemDetails - problemDetails.Status = http.StatusInternalServerError - problemDetails.Cause = "UPSTREAM_SERVER_ERROR" - - return nil, &problemDetails - } - - eapFailPkt := ConstructEapNoTypePkt(radius.EapCodeFailure, eapPayload[1]) - responseBody.EapPayload = eapFailPkt - responseBody.AuthResult = models.AuthResult_FAILURE - } - - return &responseBody, nil -} diff --git a/internal/sbi/routes.go b/internal/sbi/routes.go new file mode 100644 index 0000000..124bbf8 --- /dev/null +++ b/internal/sbi/routes.go @@ -0,0 +1,26 @@ +package sbi + +import "github.com/gin-gonic/gin" + +type Route struct { + Method string + Pattern string + APIFunc gin.HandlerFunc +} + +func applyRoutes(group *gin.RouterGroup, routes []Route) { + for _, route := range routes { + switch route.Method { + case "GET": + group.GET(route.Pattern, route.APIFunc) + case "POST": + group.POST(route.Pattern, route.APIFunc) + case "PUT": + group.PUT(route.Pattern, route.APIFunc) + case "PATCH": + group.PATCH(route.Pattern, route.APIFunc) + case "DELETE": + group.DELETE(route.Pattern, route.APIFunc) + } + } +} diff --git a/internal/sbi/server.go b/internal/sbi/server.go new file mode 100644 index 0000000..90b3c09 --- /dev/null +++ b/internal/sbi/server.go @@ -0,0 +1,155 @@ +package sbi + +import ( + "context" + "fmt" + "log" + "net/http" + "runtime/debug" + "sync" + "time" + + "github.com/gin-gonic/gin" + "github.com/sirupsen/logrus" + + ausf_context "github.com/free5gc/ausf/internal/context" + "github.com/free5gc/ausf/internal/logger" + "github.com/free5gc/ausf/internal/sbi/consumer" + "github.com/free5gc/ausf/internal/sbi/processor" + "github.com/free5gc/ausf/internal/util" + "github.com/free5gc/ausf/pkg/app" + "github.com/free5gc/ausf/pkg/factory" + "github.com/free5gc/openapi/models" + "github.com/free5gc/util/httpwrapper" + logger_util "github.com/free5gc/util/logger" +) + +type ServerAusf interface { + app.App + + Consumer() *consumer.Consumer + Processor() *processor.Processor +} + +type Server struct { + ServerAusf + + httpServer *http.Server + router *gin.Engine +} + +func NewServer(ausf ServerAusf, tlsKeyLogPath string) (*Server, error) { + s := &Server{ + ServerAusf: ausf, + } + + s.router = newRouter(s) + + cfg := s.Config() + bindAddr := cfg.GetSbiBindingAddr() + logger.SBILog.Infof("Binding addr: [%s]", bindAddr) + var err error + if s.httpServer, err = httpwrapper.NewHttp2Server(bindAddr, tlsKeyLogPath, s.router); err != nil { + logger.InitLog.Errorf("Initialize HTTP server failed: %v", err) + return nil, err + } + s.httpServer.ErrorLog = log.New(logger.SBILog.WriterLevel(logrus.ErrorLevel), "HTTP2: ", 0) + + return s, nil +} + +func newRouter(s *Server) *gin.Engine { + router := logger_util.NewGinWithLogrus(logger.GinLog) + + for _, serverName := range factory.AusfConfig.Configuration.ServiceNameList { + switch models.ServiceName(serverName) { + case models.ServiceName_NAUSF_AUTH: + ausfUeAuthenticationGroup := router.Group(factory.AusfAuthResUriPrefix) + ausfUeAuthenticationRoutes := s.getUeAuthenticationRoutes() + routerAuthorizationCheck := util.NewRouterAuthorizationCheck(models.ServiceName_NAUSF_AUTH) + ausfUeAuthenticationGroup.Use(func(c *gin.Context) { + routerAuthorizationCheck.Check(c, ausf_context.GetSelf()) + }) + applyRoutes(ausfUeAuthenticationGroup, ausfUeAuthenticationRoutes) + case models.ServiceName_NAUSF_SORPROTECTION: + ausfSorprotectionGroup := router.Group(factory.AusfSorprotectionResUriPrefix) + ausfSorprotectionRoutes := s.getSorprotectionRoutes() + routerAuthorizationCheck := util.NewRouterAuthorizationCheck(models.ServiceName_NAUSF_SORPROTECTION) + ausfSorprotectionGroup.Use(func(c *gin.Context) { + routerAuthorizationCheck.Check(c, ausf_context.GetSelf()) + }) + applyRoutes(ausfSorprotectionGroup, ausfSorprotectionRoutes) + case models.ServiceName_NAUSF_UPUPROTECTION: + ausfUpuprotectionGroup := router.Group(factory.AusfUpuprotectionResUriPrefix) + ausfUpuprotectionRoutes := s.getUpuprotectionRoutes() + routerAuthorizationCheck := util.NewRouterAuthorizationCheck(models.ServiceName_NAUSF_UPUPROTECTION) + ausfUpuprotectionGroup.Use(func(c *gin.Context) { + routerAuthorizationCheck.Check(c, ausf_context.GetSelf()) + }) + applyRoutes(ausfUpuprotectionGroup, ausfUpuprotectionRoutes) + } + } + + return router +} + +func (s *Server) Run(traceCtx context.Context, wg *sync.WaitGroup) error { + var err error + _, s.Context().NfId, err = s.Consumer().RegisterNFInstance(context.Background()) + if err != nil { + logger.InitLog.Errorf("AUSF register to NRF Error[%s]", err.Error()) + } + + wg.Add(1) + go s.startServer(wg) + + return nil +} + +func (s *Server) Shutdown() { + s.shutdownHttpServer() +} + +func (s *Server) shutdownHttpServer() { + const defaultShutdownTimeout time.Duration = 2 * time.Second + + if s.httpServer != nil { + logger.SBILog.Infof("Stop SBI server (listen on %s)", s.httpServer.Addr) + toCtx, cancel := context.WithTimeout(context.Background(), defaultShutdownTimeout) + defer cancel() + if err := s.httpServer.Shutdown(toCtx); err != nil { + logger.SBILog.Errorf("Could not close SBI server: %#v", err) + } + } +} + +func (s *Server) startServer(wg *sync.WaitGroup) { + defer func() { + if p := recover(); p != nil { + // Print stack for panic to log. Fatalf() will let program exit. + logger.SBILog.Fatalf("panic: %v\n%s", p, string(debug.Stack())) + s.Terminate() + } + wg.Done() + }() + + logger.SBILog.Infof("Start SBI server (listen on %s)", s.httpServer.Addr) + + var err error + cfg := s.Config() + scheme := cfg.GetSbiScheme() + if scheme == "http" { + err = s.httpServer.ListenAndServe() + } else if scheme == "https" { + err = s.httpServer.ListenAndServeTLS( + cfg.GetCertPemPath(), + cfg.GetCertKeyPath()) + } else { + err = fmt.Errorf("No support this scheme[%s]", scheme) + } + + if err != nil && err != http.ErrServerClosed { + logger.SBILog.Errorf("SBI server error: %v", err) + } + logger.SBILog.Infof("SBI server (listen on %s) stopped", s.httpServer.Addr) +} diff --git a/internal/sbi/sorprotection/api_default.go b/internal/sbi/sorprotection/api_default.go deleted file mode 100644 index e0849b3..0000000 --- a/internal/sbi/sorprotection/api_default.go +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Nausf_SoRProtection Service - * - * AUSF SoR Protection Service - * - * API version: 1.0.1 - * Generated by: OpenAPI Generator (https://openapi-generator.tech) - */ - -package sorprotection - -import ( - "net/http" - - "github.com/gin-gonic/gin" -) - -// SupiUeSorPost - -func SupiUeSorPost(c *gin.Context) { - c.JSON(http.StatusOK, gin.H{}) -} diff --git a/internal/sbi/sorprotection/routers.go b/internal/sbi/sorprotection/routers.go deleted file mode 100644 index 45264b2..0000000 --- a/internal/sbi/sorprotection/routers.go +++ /dev/null @@ -1,92 +0,0 @@ -/* - * Nausf_SoRProtection Service - * - * AUSF SoR Protection Service - * - * API version: 1.0.1 - * Generated by: OpenAPI Generator (https://openapi-generator.tech) - */ - -package sorprotection - -import ( - "net/http" - "strings" - - "github.com/gin-gonic/gin" - - ausf_context "github.com/free5gc/ausf/internal/context" - "github.com/free5gc/ausf/internal/logger" - "github.com/free5gc/ausf/internal/util" - "github.com/free5gc/ausf/pkg/factory" - "github.com/free5gc/openapi/models" - logger_util "github.com/free5gc/util/logger" -) - -// Route is the information for every URI. -type Route struct { - // Name is the name of this Route. - Name string - // Method is the string for the HTTP method. ex) GET, POST etc.. - Method string - // Pattern is the pattern of the URI. - Pattern string - // HandlerFunc is the handler function of this route. - HandlerFunc gin.HandlerFunc -} - -// Routes is the list of the generated Route. -type Routes []Route - -// NewRouter returns a new router. -func NewRouter() *gin.Engine { - router := logger_util.NewGinWithLogrus(logger.GinLog) - AddService(router) - return router -} - -func AddService(engine *gin.Engine) *gin.RouterGroup { - group := engine.Group(factory.AusfSorprotectionResUriPrefix) - - routerAuthorizationCheck := util.NewRouterAuthorizationCheck(models.ServiceName_NAUSF_SORPROTECTION) - group.Use(func(c *gin.Context) { - routerAuthorizationCheck.Check(c, ausf_context.GetSelf()) - }) - - for _, route := range routes { - switch route.Method { - case "GET": - group.GET(route.Pattern, route.HandlerFunc) - case "POST": - group.POST(route.Pattern, route.HandlerFunc) - case "PUT": - group.PUT(route.Pattern, route.HandlerFunc) - case "DELETE": - group.DELETE(route.Pattern, route.HandlerFunc) - case "PATCH": - group.PATCH(route.Pattern, route.HandlerFunc) - } - } - return group -} - -// Index is the index handler. -func Index(c *gin.Context) { - c.String(http.StatusOK, "Hello World!") -} - -var routes = Routes{ - { - "Index", - "GET", - "/", - Index, - }, - - { - "SupiUeSorPost", - strings.ToUpper("Post"), - "/:supi/ue-sor", - SupiUeSorPost, - }, -} diff --git a/internal/sbi/ueauthentication/api_default.go b/internal/sbi/ueauthentication/api_default.go deleted file mode 100644 index b2db8a0..0000000 --- a/internal/sbi/ueauthentication/api_default.go +++ /dev/null @@ -1,171 +0,0 @@ -/* - * AUSF API - * - * OpenAPI specification for AUSF - * - * API version: 1.0.0 - * Generated by: OpenAPI Generator (https://openapi-generator.tech) - */ - -package ueauthentication - -import ( - "net/http" - - "github.com/gin-gonic/gin" - - "github.com/free5gc/ausf/internal/logger" - "github.com/free5gc/ausf/internal/sbi/producer" - "github.com/free5gc/openapi" - "github.com/free5gc/openapi/models" - "github.com/free5gc/util/httpwrapper" -) - -// HTTPEapAuthMethod - -func HTTPEapAuthMethod(ctx *gin.Context) { - var eapSessionReq models.EapSession - - requestBody, err := ctx.GetRawData() - if err != nil { - problemDetail := models.ProblemDetails{ - Title: "System failure", - Status: http.StatusInternalServerError, - Detail: err.Error(), - Cause: "SYSTEM_FAILURE", - } - logger.Auth5gAkaLog.Errorf("Get Request Body error: %+v", err) - ctx.JSON(http.StatusInternalServerError, problemDetail) - return - } - - err = openapi.Deserialize(&eapSessionReq, requestBody, "application/json") - if err != nil { - problemDetail := "[Request Body] " + err.Error() - rsp := models.ProblemDetails{ - Title: "Malformed request syntax", - Status: http.StatusBadRequest, - Detail: problemDetail, - } - logger.Auth5gAkaLog.Errorln(problemDetail) - ctx.JSON(http.StatusBadRequest, rsp) - return - } - - req := httpwrapper.NewRequest(ctx.Request, eapSessionReq) - req.Params["authCtxId"] = ctx.Param("authCtxId") - - rsp := producer.HandleEapAuthComfirmRequest(req) - - responseBody, err := openapi.Serialize(rsp.Body, "application/json") - if err != nil { - logger.Auth5gAkaLog.Errorln(err) - problemDetails := models.ProblemDetails{ - Status: http.StatusInternalServerError, - Cause: "SYSTEM_FAILURE", - Detail: err.Error(), - } - ctx.JSON(http.StatusInternalServerError, problemDetails) - } else { - ctx.Data(rsp.Status, "application/json", responseBody) - } -} - -// HTTPUeAuthenticationsAuthCtxID5gAkaConfirmationPut - -func HTTPUeAuthenticationsAuthCtxID5gAkaConfirmationPut(ctx *gin.Context) { - var confirmationData models.ConfirmationData - - requestBody, err := ctx.GetRawData() - if err != nil { - problemDetail := models.ProblemDetails{ - Title: "System failure", - Status: http.StatusInternalServerError, - Detail: err.Error(), - Cause: "SYSTEM_FAILURE", - } - logger.Auth5gAkaLog.Errorf("Get Request Body error: %+v", err) - ctx.JSON(http.StatusInternalServerError, problemDetail) - return - } - - err = openapi.Deserialize(&confirmationData, requestBody, "application/json") - if err != nil { - problemDetail := "[Request Body] " + err.Error() - rsp := models.ProblemDetails{ - Title: "Malformed request syntax", - Status: http.StatusBadRequest, - Detail: problemDetail, - } - logger.Auth5gAkaLog.Errorln(problemDetail) - ctx.JSON(http.StatusBadRequest, rsp) - return - } - - req := httpwrapper.NewRequest(ctx.Request, confirmationData) - req.Params["authCtxId"] = ctx.Param("authCtxId") - - rsp := producer.HandleAuth5gAkaComfirmRequest(req) - - responseBody, err := openapi.Serialize(rsp.Body, "application/json") - if err != nil { - logger.Auth5gAkaLog.Errorln(err) - problemDetails := models.ProblemDetails{ - Status: http.StatusInternalServerError, - Cause: "SYSTEM_FAILURE", - Detail: err.Error(), - } - ctx.JSON(http.StatusInternalServerError, problemDetails) - } else { - ctx.Data(rsp.Status, "application/json", responseBody) - } -} - -// HTTPUeAuthenticationsPost - -func HTTPUeAuthenticationsPost(ctx *gin.Context) { - var authInfo models.AuthenticationInfo - - requestBody, err := ctx.GetRawData() - if err != nil { - problemDetail := models.ProblemDetails{ - Title: "System failure", - Status: http.StatusInternalServerError, - Detail: err.Error(), - Cause: "SYSTEM_FAILURE", - } - logger.UeAuthLog.Errorf("Get Request Body error: %+v", err) - ctx.JSON(http.StatusInternalServerError, problemDetail) - return - } - - err = openapi.Deserialize(&authInfo, requestBody, "application/json") - if err != nil { - problemDetail := "[Request Body] " + err.Error() - rsp := models.ProblemDetails{ - Title: "Malformed request syntax", - Status: http.StatusBadRequest, - Detail: problemDetail, - } - logger.UeAuthLog.Errorln(problemDetail) - ctx.JSON(http.StatusBadRequest, rsp) - return - } - - req := httpwrapper.NewRequest(ctx.Request, authInfo) - - rsp := producer.HandleUeAuthPostRequest(req) - - for key, value := range rsp.Header { - ctx.Header(key, value[0]) - } - responseBody, err := openapi.Serialize(rsp.Body, "application/json") - if err != nil { - logger.UeAuthLog.Errorln(err) - problemDetails := models.ProblemDetails{ - Status: http.StatusInternalServerError, - Cause: "SYSTEM_FAILURE", - Detail: err.Error(), - } - ctx.JSON(http.StatusInternalServerError, problemDetails) - } else { - ctx.Data(rsp.Status, "application/json", responseBody) - } -} diff --git a/internal/sbi/ueauthentication/routers.go b/internal/sbi/ueauthentication/routers.go deleted file mode 100644 index 948f8db..0000000 --- a/internal/sbi/ueauthentication/routers.go +++ /dev/null @@ -1,106 +0,0 @@ -/* - * AUSF API - * - * OpenAPI specification for AUSF - * - * API version: 1.0.0 - * Generated by: OpenAPI Generator (https://openapi-generator.tech) - */ - -package ueauthentication - -import ( - "net/http" - "strings" - - "github.com/gin-gonic/gin" - - ausf_context "github.com/free5gc/ausf/internal/context" - "github.com/free5gc/ausf/internal/logger" - "github.com/free5gc/ausf/internal/util" - "github.com/free5gc/ausf/pkg/factory" - "github.com/free5gc/openapi/models" - logger_util "github.com/free5gc/util/logger" -) - -// Route is the information for every URI. -type Route struct { - // Name is the name of this Route. - Name string - // Method is the string for the HTTP method. ex) GET, POST etc.. - Method string - // Pattern is the pattern of the URI. - Pattern string - // HandlerFunc is the handler function of this route. - HandlerFunc gin.HandlerFunc -} - -// Routes is the list of the generated Route. -type Routes []Route - -// NewRouter returns a new router. -func NewRouter() *gin.Engine { - router := logger_util.NewGinWithLogrus(logger.GinLog) - AddService(router) - return router -} - -func AddService(engine *gin.Engine) *gin.RouterGroup { - group := engine.Group(factory.AusfAuthResUriPrefix) - - routerAuthorizationCheck := util.NewRouterAuthorizationCheck(models.ServiceName_NAUSF_AUTH) - group.Use(func(c *gin.Context) { - routerAuthorizationCheck.Check(c, ausf_context.GetSelf()) - }) - - for _, route := range routes { - switch route.Method { - case "GET": - group.GET(route.Pattern, route.HandlerFunc) - case "POST": - group.POST(route.Pattern, route.HandlerFunc) - case "PUT": - group.PUT(route.Pattern, route.HandlerFunc) - case "DELETE": - group.DELETE(route.Pattern, route.HandlerFunc) - case "PATCH": - group.PATCH(route.Pattern, route.HandlerFunc) - } - } - return group -} - -// Index is the index handler. -func Index(c *gin.Context) { - c.String(http.StatusOK, "Hello World!") -} - -var routes = Routes{ - { - "Index", - "GET", - "/", - Index, - }, - - { - "HTTPEapAuthMethod", - strings.ToUpper("Post"), - "/ue-authentications/:authCtxId/eap-session", - HTTPEapAuthMethod, - }, - - { - "HTTPUeAuthenticationsAuthCtxID5gAkaConfirmationPut", - strings.ToUpper("Put"), - "/ue-authentications/:authCtxId/5g-aka-confirmation", - HTTPUeAuthenticationsAuthCtxID5gAkaConfirmationPut, - }, - - { - "HTTPUeAuthenticationsPost", - strings.ToUpper("Post"), - "/ue-authentications", - HTTPUeAuthenticationsPost, - }, -} diff --git a/internal/sbi/upuprotection/api_default.go b/internal/sbi/upuprotection/api_default.go deleted file mode 100644 index 0b79c7f..0000000 --- a/internal/sbi/upuprotection/api_default.go +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Nausf_UPUProtection Service - * - * AUSF UPU Protection Service - * - * API version: 1.0.0 - * Generated by: OpenAPI Generator (https://openapi-generator.tech) - */ - -package upuprotection - -import ( - "net/http" - - "github.com/gin-gonic/gin" -) - -// SupiUeUpuPost - -func SupiUeUpuPost(c *gin.Context) { - c.JSON(http.StatusOK, gin.H{}) -} diff --git a/internal/sbi/upuprotection/routers.go b/internal/sbi/upuprotection/routers.go deleted file mode 100644 index f3affde..0000000 --- a/internal/sbi/upuprotection/routers.go +++ /dev/null @@ -1,92 +0,0 @@ -/* - * Nausf_UPUProtection Service - * - * AUSF UPU Protection Service - * - * API version: 1.0.0 - * Generated by: OpenAPI Generator (https://openapi-generator.tech) - */ - -package upuprotection - -import ( - "net/http" - "strings" - - "github.com/gin-gonic/gin" - - ausf_context "github.com/free5gc/ausf/internal/context" - "github.com/free5gc/ausf/internal/logger" - "github.com/free5gc/ausf/internal/util" - "github.com/free5gc/ausf/pkg/factory" - "github.com/free5gc/openapi/models" - logger_util "github.com/free5gc/util/logger" -) - -// Route is the information for every URI. -type Route struct { - // Name is the name of this Route. - Name string - // Method is the string for the HTTP method. ex) GET, POST etc.. - Method string - // Pattern is the pattern of the URI. - Pattern string - // HandlerFunc is the handler function of this route. - HandlerFunc gin.HandlerFunc -} - -// Routes is the list of the generated Route. -type Routes []Route - -// NewRouter returns a new router. -func NewRouter() *gin.Engine { - router := logger_util.NewGinWithLogrus(logger.GinLog) - AddService(router) - return router -} - -func AddService(engine *gin.Engine) *gin.RouterGroup { - group := engine.Group(factory.AusfAuthResUriPrefix) - - routerAuthorizationCheck := util.NewRouterAuthorizationCheck(models.ServiceName_NAUSF_UPUPROTECTION) - group.Use(func(c *gin.Context) { - routerAuthorizationCheck.Check(c, ausf_context.GetSelf()) - }) - - for _, route := range routes { - switch route.Method { - case "GET": - group.GET(route.Pattern, route.HandlerFunc) - case "POST": - group.POST(route.Pattern, route.HandlerFunc) - case "PUT": - group.PUT(route.Pattern, route.HandlerFunc) - case "DELETE": - group.DELETE(route.Pattern, route.HandlerFunc) - case "PATCH": - group.PATCH(route.Pattern, route.HandlerFunc) - } - } - return group -} - -// Index is the index handler. -func Index(c *gin.Context) { - c.String(http.StatusOK, "Hello World!") -} - -var routes = Routes{ - { - "Index", - "GET", - "/", - Index, - }, - - { - "SupiUeUpuPost", - strings.ToUpper("Post"), - "/:supi/ue-upu", - SupiUeUpuPost, - }, -} diff --git a/pkg/app/app.go b/pkg/app/app.go new file mode 100644 index 0000000..ef2cbe9 --- /dev/null +++ b/pkg/app/app.go @@ -0,0 +1,18 @@ +package app + +import ( + ausf_context "github.com/free5gc/ausf/internal/context" + "github.com/free5gc/ausf/pkg/factory" +) + +type App interface { + SetLogEnable(enable bool) + SetLogLevel(level string) + SetReportCaller(reportCaller bool) + + Start() + Terminate() + + Context() *ausf_context.AUSFContext + Config() *factory.Config +} diff --git a/pkg/factory/config.go b/pkg/factory/config.go index 983da78..e61b852 100644 --- a/pkg/factory/config.go +++ b/pkg/factory/config.go @@ -7,6 +7,7 @@ package factory import ( "errors" "fmt" + "os" "strconv" "sync" @@ -235,3 +236,56 @@ func (c *Config) GetLogReportCaller() bool { } return c.Logger.ReportCaller } + +func (c *Config) GetSbiBindingAddr() string { + c.RLock() + defer c.RUnlock() + return c.GetSbiBindingIP() + ":" + strconv.Itoa(c.GetSbiPort()) +} + +func (c *Config) GetSbiBindingIP() string { + c.RLock() + defer c.RUnlock() + bindIP := "0.0.0.0" + if c.Configuration == nil || c.Configuration.Sbi == nil { + return bindIP + } + if c.Configuration.Sbi.BindingIPv4 != "" { + if bindIP = os.Getenv(c.Configuration.Sbi.BindingIPv4); bindIP != "" { + logger.CfgLog.Infof("Parsing ServerIPv4 [%s] from ENV Variable", bindIP) + } else { + bindIP = c.Configuration.Sbi.BindingIPv4 + } + } + return bindIP +} + +func (c *Config) GetSbiPort() int { + c.RLock() + defer c.RUnlock() + if c.Configuration != nil && c.Configuration.Sbi != nil && c.Configuration.Sbi.Port != 0 { + return c.Configuration.Sbi.Port + } + return AusfSbiDefaultPort +} + +func (c *Config) GetSbiScheme() string { + c.RLock() + defer c.RUnlock() + if c.Configuration != nil && c.Configuration.Sbi != nil && c.Configuration.Sbi.Scheme != "" { + return c.Configuration.Sbi.Scheme + } + return AusfSbiDefaultScheme +} + +func (c *Config) GetCertPemPath() string { + c.RLock() + defer c.RUnlock() + return c.Configuration.Sbi.Tls.Pem +} + +func (c *Config) GetCertKeyPath() string { + c.RLock() + defer c.RUnlock() + return c.Configuration.Sbi.Tls.Key +} diff --git a/pkg/service/init.go b/pkg/service/init.go index ac478e8..bb01b5d 100644 --- a/pkg/service/init.go +++ b/pkg/service/init.go @@ -1,40 +1,93 @@ package service import ( - "fmt" + "context" "io" "os" - "os/signal" "runtime/debug" - "syscall" + "sync" "github.com/sirupsen/logrus" ausf_context "github.com/free5gc/ausf/internal/context" "github.com/free5gc/ausf/internal/logger" + "github.com/free5gc/ausf/internal/sbi" "github.com/free5gc/ausf/internal/sbi/consumer" - "github.com/free5gc/ausf/internal/sbi/ueauthentication" + "github.com/free5gc/ausf/internal/sbi/processor" + "github.com/free5gc/ausf/pkg/app" "github.com/free5gc/ausf/pkg/factory" - "github.com/free5gc/util/httpwrapper" - logger_util "github.com/free5gc/util/logger" ) +var AUSF *AusfApp + +var _ app.App = &AusfApp{} + type AusfApp struct { - cfg *factory.Config ausfCtx *ausf_context.AUSFContext + cfg *factory.Config + + ctx context.Context + cancel context.CancelFunc + wg sync.WaitGroup + + sbiServer *sbi.Server + consumer *consumer.Consumer + processor *processor.Processor } -func NewApp(cfg *factory.Config) (*AusfApp, error) { - ausf := &AusfApp{cfg: cfg} +func NewApp(ctx context.Context, cfg *factory.Config, tlsKeyLogPath string) (*AusfApp, error) { + ausf := &AusfApp{ + cfg: cfg, + wg: sync.WaitGroup{}, + } ausf.SetLogEnable(cfg.GetLogEnable()) ausf.SetLogLevel(cfg.GetLogLevel()) ausf.SetReportCaller(cfg.GetLogReportCaller()) - ausf_context.Init() + + processor, err_p := processor.NewProcessor(ausf) + if err_p != nil { + return ausf, err_p + } + ausf.processor = processor + + consumer, err := consumer.NewConsumer(ausf) + if err != nil { + return ausf, err + } + ausf.consumer = consumer + + ausf.ctx, ausf.cancel = context.WithCancel(ctx) ausf.ausfCtx = ausf_context.GetSelf() + + if ausf.sbiServer, err = sbi.NewServer(ausf, tlsKeyLogPath); err != nil { + return nil, err + } + AUSF = ausf + return ausf, nil } +func (a *AusfApp) CancelContext() context.Context { + return a.ctx +} + +func (a *AusfApp) Consumer() *consumer.Consumer { + return a.consumer +} + +func (a *AusfApp) Processor() *processor.Processor { + return a.processor +} + +func (a *AusfApp) Context() *ausf_context.AUSFContext { + return a.ausfCtx +} + +func (a *AusfApp) Config() *factory.Config { + return a.cfg +} + func (a *AusfApp) SetLogEnable(enable bool) { logger.MainLog.Infof("Log enable is set to [%v]", enable) if enable && logger.Log.Out == os.Stderr { @@ -43,7 +96,7 @@ func (a *AusfApp) SetLogEnable(enable bool) { return } - a.cfg.SetLogEnable(enable) + a.Config().SetLogEnable(enable) if enable { logger.Log.SetOutput(os.Stderr) } else { @@ -63,7 +116,7 @@ func (a *AusfApp) SetLogLevel(level string) { return } - a.cfg.SetLogLevel(level) + a.Config().SetLogLevel(level) logger.Log.SetLevel(lvl) } @@ -73,85 +126,63 @@ func (a *AusfApp) SetReportCaller(reportCaller bool) { return } - a.cfg.SetLogReportCaller(reportCaller) + a.Config().SetLogReportCaller(reportCaller) logger.Log.SetReportCaller(reportCaller) } -func (a *AusfApp) Start(tlsKeyLogPath string) { +func (a *AusfApp) Start() { logger.InitLog.Infoln("Server started") - router := logger_util.NewGinWithLogrus(logger.GinLog) - ueauthentication.AddService(router) - - pemPath := factory.AusfDefaultCertPemPath - keyPath := factory.AusfDefaultPrivateKeyPath - sbi := factory.AusfConfig.Configuration.Sbi - if sbi.Tls != nil { - pemPath = sbi.Tls.Pem - keyPath = sbi.Tls.Key - } + a.wg.Add(1) + go a.listenShutdownEvent() - self := a.ausfCtx - // Register to NRF - profile, err := consumer.BuildNFInstance(self) - if err != nil { - logger.InitLog.Error("Build AUSF Profile Error") - } - _, self.NfId, err = consumer.SendRegisterNFInstance(self.NrfUri, self.NfId, profile) - if err != nil { - logger.InitLog.Errorf("AUSF register to NRF Error[%s]", err.Error()) + if err := a.sbiServer.Run(context.Background(), &a.wg); err != nil { + logger.MainLog.Fatalf("Run SBI server failed: %+v", err) } + a.WaitRoutineStopped() +} - addr := fmt.Sprintf("%s:%d", self.BindingIPv4, self.SBIPort) - - signalChannel := make(chan os.Signal, 1) - signal.Notify(signalChannel, os.Interrupt, syscall.SIGTERM) - go func() { - defer func() { - if p := recover(); p != nil { - // Print stack for panic to log. Fatalf() will let program exit. - logger.InitLog.Fatalf("panic: %v\n%s", p, string(debug.Stack())) - } - }() - - <-signalChannel - a.Terminate() - os.Exit(0) +func (a *AusfApp) listenShutdownEvent() { + defer func() { + if p := recover(); p != nil { + // Print stack for panic to log. Fatalf() will let program exit. + logger.MainLog.Fatalf("panic: %v\n%s", p, string(debug.Stack())) + } + a.wg.Done() }() - server, err := httpwrapper.NewHttp2Server(addr, tlsKeyLogPath, router) - if server == nil { - logger.InitLog.Errorf("Initialize HTTP server failed: %+v", err) - return - } - - if err != nil { - logger.InitLog.Warnf("Initialize HTTP server: +%v", err) - } - - serverScheme := factory.AusfConfig.Configuration.Sbi.Scheme - if serverScheme == "http" { - err = server.ListenAndServe() - } else if serverScheme == "https" { - err = server.ListenAndServeTLS(pemPath, keyPath) - } - - if err != nil { - logger.InitLog.Fatalf("HTTP server setup failed: %+v", err) - } + <-a.ctx.Done() + a.terminateProcedure() } func (a *AusfApp) Terminate() { - logger.InitLog.Infof("Terminating AUSF...") + a.cancel() +} + +func (a *AusfApp) terminateProcedure() { + logger.MainLog.Infof("Terminating AUSF...") + // deregister with NRF - problemDetails, err := consumer.SendDeregisterNFInstance() + problemDetails, err := a.Consumer().SendDeregisterNFInstance() if problemDetails != nil { - logger.InitLog.Errorf("Deregister NF instance Failed Problem[%+v]", problemDetails) + logger.MainLog.Errorf("Deregister NF instance Failed Problem[%+v]", problemDetails) } else if err != nil { - logger.InitLog.Errorf("Deregister NF instance Error[%+v]", err) + logger.MainLog.Errorf("Deregister NF instance Error[%+v]", err) } else { - logger.InitLog.Infof("Deregister from NRF successfully") + logger.MainLog.Infof("Deregister from NRF successfully") + } + logger.MainLog.Infof("AUSF SBI Server terminated") + + a.CallServerStop() +} + +func (a *AusfApp) CallServerStop() { + if a.sbiServer != nil { + a.sbiServer.Shutdown() } +} - logger.InitLog.Infof("AUSF terminated") +func (a *AusfApp) WaitRoutineStopped() { + a.wg.Wait() + logger.MainLog.Infof("AUSF App is terminated") }