From ab71f225606aae43b88e8ef4e095d2c8f65750ca Mon Sep 17 00:00:00 2001 From: aler9 <46489434+aler9@users.noreply.github.com> Date: Mon, 27 Sep 2021 15:45:51 +0200 Subject: [PATCH] move decoding of more configuration into JSON decoding --- internal/conf/credential.go | 37 ++++++++++++++++++++++++++ internal/conf/path.go | 50 +++++++---------------------------- internal/core/api.go | 12 ++++----- internal/core/hls_muxer.go | 2 +- internal/core/path.go | 6 ++--- internal/core/path_manager.go | 6 ++--- internal/core/rtmp_conn.go | 12 ++++----- internal/core/rtsp_conn.go | 14 +++++----- internal/core/rtsp_session.go | 4 +-- 9 files changed, 74 insertions(+), 69 deletions(-) create mode 100644 internal/conf/credential.go diff --git a/internal/conf/credential.go b/internal/conf/credential.go new file mode 100644 index 00000000..25a77556 --- /dev/null +++ b/internal/conf/credential.go @@ -0,0 +1,37 @@ +package conf + +import ( + "encoding/json" + "fmt" + "regexp" + "strings" +) + +var reCredential = regexp.MustCompile(`^[a-zA-Z0-9!\$\(\)\*\+\.;<=>\[\]\^_\-\{\}]+$`) + +const credentialSupportedChars = "A-Z,0-9,!,$,(,),*,+,.,;,<,=,>,[,],^,_,-,{,}" + +// Credential is a parameter that is used as username or password. +type Credential string + +// MarshalJSON marshals a Credential into JSON. +func (d Credential) MarshalJSON() ([]byte, error) { + return json.Marshal(string(d)) +} + +// UnmarshalJSON unmarshals a Credential from JSON. +func (d *Credential) UnmarshalJSON(b []byte) error { + var in string + if err := json.Unmarshal(b, &in); err != nil { + return err + } + + if in != "" && + !strings.HasPrefix(in, "sha256:") && + !reCredential.MatchString(in) { + return fmt.Errorf("contains unsupported characters (supported are %s)", credentialSupportedChars) + } + + *d = Credential(in) + return nil +} diff --git a/internal/conf/path.go b/internal/conf/path.go index 72da6a3d..b11b2c6b 100644 --- a/internal/conf/path.go +++ b/internal/conf/path.go @@ -11,10 +11,6 @@ import ( "github.com/aler9/gortsplib/pkg/base" ) -const userPassSupportedChars = "A-Z,0-9,!,$,(,),*,+,.,;,<,=,>,[,],^,_,-,{,}" - -var reUserPass = regexp.MustCompile(`^[a-zA-Z0-9!\$\(\)\*\+\.;<=>\[\]\^_\-\{\}]+$`) - var rePathName = regexp.MustCompile(`^[0-9a-zA-Z_\-/\.~]+$`) // IsValidPathName checks if a path name is valid. @@ -55,12 +51,12 @@ type PathConf struct { Fallback string `json:"fallback"` // authentication - PublishUser string `json:"publishUser"` - PublishPass string `json:"publishPass"` - PublishIPs IPsOrNets `json:"publishIPs"` - ReadUser string `json:"readUser"` - ReadPass string `json:"readPass"` - ReadIPs IPsOrNets `json:"readIPs"` + PublishUser Credential `json:"publishUser"` + PublishPass Credential `json:"publishPass"` + PublishIPs IPsOrNets `json:"publishIPs"` + ReadUser Credential `json:"readUser"` + ReadPass Credential `json:"readPass"` + ReadIPs IPsOrNets `json:"readIPs"` // custom commands RunOnInit string `json:"runOnInit"` @@ -211,25 +207,9 @@ func (pconf *PathConf) checkAndFillMissing(name string) error { return fmt.Errorf("read username and password must be both filled") } - if pconf.PublishUser != "" { - if pconf.Source != "publisher" { - return fmt.Errorf("'publishUser' is useless when source is not 'publisher'") - } - - if !strings.HasPrefix(pconf.PublishUser, "sha256:") && !reUserPass.MatchString(pconf.PublishUser) { - return fmt.Errorf("publish username contains unsupported characters (supported are %s)", userPassSupportedChars) - } - } - - if pconf.PublishPass != "" { - if pconf.Source != "publisher" { - return fmt.Errorf("'publishPass' is useless when source is not 'publisher', since " + - "the stream is not provided by a publisher, but by a fixed source") - } - - if !strings.HasPrefix(pconf.PublishPass, "sha256:") && !reUserPass.MatchString(pconf.PublishPass) { - return fmt.Errorf("publish password contains unsupported characters (supported are %s)", userPassSupportedChars) - } + if pconf.PublishUser != "" && pconf.Source != "publisher" { + return fmt.Errorf("'publishUser' is useless when source is not 'publisher', since " + + "the stream is not provided by a publisher, but by a fixed source") } if len(pconf.PublishIPs) > 0 && pconf.Source != "publisher" { @@ -242,18 +222,6 @@ func (pconf *PathConf) checkAndFillMissing(name string) error { return fmt.Errorf("read username and password must be both filled") } - if pconf.ReadUser != "" { - if !strings.HasPrefix(pconf.ReadUser, "sha256:") && !reUserPass.MatchString(pconf.ReadUser) { - return fmt.Errorf("read username contains unsupported characters (supported are %s)", userPassSupportedChars) - } - } - - if pconf.ReadPass != "" { - if !strings.HasPrefix(pconf.ReadPass, "sha256:") && !reUserPass.MatchString(pconf.ReadPass) { - return fmt.Errorf("read password contains unsupported characters (supported are %s)", userPassSupportedChars) - } - } - if pconf.RunOnInit != "" && pconf.Regexp != nil { return fmt.Errorf("a path with a regular expression does not support option 'runOnInit'; use another path") } diff --git a/internal/core/api.go b/internal/core/api.go index d002fe14..da99966a 100644 --- a/internal/core/api.go +++ b/internal/core/api.go @@ -112,12 +112,12 @@ func loadConfPathData(ctx *gin.Context) (interface{}, error) { Fallback *string `json:"fallback"` // authentication - PublishUser *string `json:"publishUser"` - PublishPass *string `json:"publishPass"` - PublishIPs *conf.IPsOrNets `json:"publishIPs"` - ReadUser *string `json:"readUser"` - ReadPass *string `json:"readPass"` - ReadIPs *conf.IPsOrNets `json:"readIPs"` + PublishUser *conf.Credential `json:"publishUser"` + PublishPass *conf.Credential `json:"publishPass"` + PublishIPs *conf.IPsOrNets `json:"publishIPs"` + ReadUser *conf.Credential `json:"readUser"` + ReadPass *conf.Credential `json:"readPass"` + ReadIPs *conf.IPsOrNets `json:"readIPs"` // custom commands RunOnInit *string `json:"runOnInit"` diff --git a/internal/core/hls_muxer.go b/internal/core/hls_muxer.go index 44dd72d9..adebd3e9 100644 --- a/internal/core/hls_muxer.go +++ b/internal/core/hls_muxer.go @@ -424,7 +424,7 @@ func (r *hlsMuxer) handleRequest(req hlsMuxerRequest) { if conf.ReadUser != "" { user, pass, ok := req.Req.BasicAuth() - if !ok || user != conf.ReadUser || pass != conf.ReadPass { + if !ok || user != string(conf.ReadUser) || pass != string(conf.ReadPass) { req.W.Header().Set("WWW-Authenticate", `Basic realm="rtsp-simple-server"`) req.W.WriteHeader(http.StatusUnauthorized) req.Res <- nil diff --git a/internal/core/path.go b/internal/core/path.go index 8104880f..66abf30d 100644 --- a/internal/core/path.go +++ b/internal/core/path.go @@ -123,7 +123,7 @@ type pathDescribeReq struct { PathName string URL *base.URL IP net.IP - ValidateCredentials func(pathUser string, pathPass string) error + ValidateCredentials func(pathUser conf.Credential, pathPass conf.Credential) error Res chan pathDescribeRes } @@ -137,7 +137,7 @@ type pathReaderSetupPlayReq struct { Author reader PathName string IP net.IP - ValidateCredentials func(pathUser string, pathPass string) error + ValidateCredentials func(pathUser conf.Credential, pathPass conf.Credential) error Res chan pathReaderSetupPlayRes } @@ -150,7 +150,7 @@ type pathPublisherAnnounceReq struct { Author publisher PathName string IP net.IP - ValidateCredentials func(pathUser string, pathPass string) error + ValidateCredentials func(pathUser conf.Credential, pathPass conf.Credential) error Res chan pathPublisherAnnounceRes } diff --git a/internal/core/path_manager.go b/internal/core/path_manager.go index 0614c82b..c3d2efd5 100644 --- a/internal/core/path_manager.go +++ b/internal/core/path_manager.go @@ -312,11 +312,11 @@ func (pm *pathManager) findPathConf(name string) (string, *conf.PathConf, error) func (pm *pathManager) authenticate( ip net.IP, - validateCredentials func(pathUser string, pathPass string) error, + validateCredentials func(pathUser conf.Credential, pathPass conf.Credential) error, pathName string, pathIPs []interface{}, - pathUser string, - pathPass string, + pathUser conf.Credential, + pathPass conf.Credential, ) error { // validate ip if pathIPs != nil && ip != nil { diff --git a/internal/core/rtmp_conn.go b/internal/core/rtmp_conn.go index 81dce672..21540b12 100644 --- a/internal/core/rtmp_conn.go +++ b/internal/core/rtmp_conn.go @@ -205,7 +205,7 @@ func (c *rtmpConn) runRead(ctx context.Context) error { Author: c, PathName: pathName, IP: c.ip(), - ValidateCredentials: func(pathUser string, pathPass string) error { + ValidateCredentials: func(pathUser conf.Credential, pathPass conf.Credential) error { return c.validateCredentials(pathUser, pathPass, query) }, }) @@ -440,7 +440,7 @@ func (c *rtmpConn) runPublish(ctx context.Context) error { Author: c, PathName: pathName, IP: c.ip(), - ValidateCredentials: func(pathUser string, pathPass string) error { + ValidateCredentials: func(pathUser conf.Credential, pathPass conf.Credential) error { return c.validateCredentials(pathUser, pathPass, query) }, }) @@ -548,12 +548,12 @@ func (c *rtmpConn) runPublish(ctx context.Context) error { } func (c *rtmpConn) validateCredentials( - pathUser string, - pathPass string, + pathUser conf.Credential, + pathPass conf.Credential, query url.Values, ) error { - if query.Get("user") != pathUser || - query.Get("pass") != pathPass { + if query.Get("user") != string(pathUser) || + query.Get("pass") != string(pathPass) { return pathErrAuthCritical{ Message: "wrong username or password", } diff --git a/internal/core/rtsp_conn.go b/internal/core/rtsp_conn.go index 8de0df8e..998c9edc 100644 --- a/internal/core/rtsp_conn.go +++ b/internal/core/rtsp_conn.go @@ -99,16 +99,16 @@ func (c *rtspConn) ip() net.IP { } func (c *rtspConn) validateCredentials( - pathUser string, - pathPass string, + pathUser conf.Credential, + pathPass conf.Credential, pathName string, req *base.Request, ) error { // reset authValidator every time the credentials change - if c.authValidator == nil || c.authUser != pathUser || c.authPass != pathPass { - c.authUser = pathUser - c.authPass = pathPass - c.authValidator = auth.NewValidator(pathUser, pathPass, c.authMethods) + if c.authValidator == nil || c.authUser != string(pathUser) || c.authPass != string(pathPass) { + c.authUser = string(pathUser) + c.authPass = string(pathPass) + c.authValidator = auth.NewValidator(string(pathUser), string(pathPass), c.authMethods) } // VLC strips the control attribute @@ -193,7 +193,7 @@ func (c *rtspConn) OnDescribe(ctx *gortsplib.ServerHandlerOnDescribeCtx, PathName: ctx.Path, URL: ctx.Req.URL, IP: c.ip(), - ValidateCredentials: func(pathUser string, pathPass string) error { + ValidateCredentials: func(pathUser conf.Credential, pathPass conf.Credential) error { return c.validateCredentials(pathUser, pathPass, ctx.Path, ctx.Req) }, }) diff --git a/internal/core/rtsp_session.go b/internal/core/rtsp_session.go index 4c561810..c5f619ea 100644 --- a/internal/core/rtsp_session.go +++ b/internal/core/rtsp_session.go @@ -134,7 +134,7 @@ func (s *rtspSession) OnAnnounce(c *rtspConn, ctx *gortsplib.ServerHandlerOnAnno Author: s, PathName: ctx.Path, IP: ctx.Conn.NetConn().RemoteAddr().(*net.TCPAddr).IP, - ValidateCredentials: func(pathUser string, pathPass string) error { + ValidateCredentials: func(pathUser conf.Credential, pathPass conf.Credential) error { return c.validateCredentials(pathUser, pathPass, ctx.Path, ctx.Req) }, }) @@ -198,7 +198,7 @@ func (s *rtspSession) OnSetup(c *rtspConn, ctx *gortsplib.ServerHandlerOnSetupCt Author: s, PathName: ctx.Path, IP: ctx.Conn.NetConn().RemoteAddr().(*net.TCPAddr).IP, - ValidateCredentials: func(pathUser string, pathPass string) error { + ValidateCredentials: func(pathUser conf.Credential, pathPass conf.Credential) error { return c.validateCredentials(pathUser, pathPass, ctx.Path, ctx.Req) }, })