mirror of
https://github.com/datarhei/core.git
synced 2025-10-05 16:07:07 +08:00
Fix accessing RTMP stream without token or streamkey for anonymous users
This commit is contained in:
@@ -124,7 +124,7 @@ func (g *rewrite) rtmpURL(u *url.URL, mode Access, identity iamidentity.Verifier
|
|||||||
token := identity.GetServiceToken()
|
token := identity.GetServiceToken()
|
||||||
|
|
||||||
// Remove the existing token from the path
|
// Remove the existing token from the path
|
||||||
path, _ := rtmp.GetToken(u)
|
path, _, _ := rtmp.GetToken(u)
|
||||||
u.Path = path
|
u.Path = path
|
||||||
|
|
||||||
q := u.Query()
|
q := u.Query()
|
||||||
|
43
rtmp/rtmp.go
43
rtmp/rtmp.go
@@ -203,21 +203,22 @@ func (s *server) log(who, handler, action, resource, message string, client net.
|
|||||||
}).Log(message)
|
}).Log(message)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetToken returns the path without the token and the token found in the URL. If the token
|
// GetToken returns the path without the token and the token found in the URL and whether
|
||||||
// was part of the path, the token is removed from the path. The token in the query string
|
// it was found in the path. If the token was part of the path, the token is removed from
|
||||||
// takes precedence. The token in the path is assumed to be the last path element.
|
// the path. The token in the query string takes precedence. The token in the path is
|
||||||
func GetToken(u *url.URL) (string, string) {
|
// assumed to be the last path element.
|
||||||
|
func GetToken(u *url.URL) (string, string, bool) {
|
||||||
q := u.Query()
|
q := u.Query()
|
||||||
if q.Has("token") {
|
if q.Has("token") {
|
||||||
// The token was in the query. Return the unmomdified path and the token.
|
// The token was in the query. Return the unmomdified path and the token.
|
||||||
return u.Path, q.Get("token")
|
return u.Path, q.Get("token"), false
|
||||||
}
|
}
|
||||||
|
|
||||||
pathElements := splitPath(u.EscapedPath())
|
pathElements := splitPath(u.EscapedPath())
|
||||||
nPathElements := len(pathElements)
|
nPathElements := len(pathElements)
|
||||||
|
|
||||||
if nPathElements <= 1 {
|
if nPathElements <= 1 {
|
||||||
return u.Path, ""
|
return u.Path, "", false
|
||||||
}
|
}
|
||||||
|
|
||||||
rawPath := "/" + strings.Join(pathElements[:nPathElements-1], "/")
|
rawPath := "/" + strings.Join(pathElements[:nPathElements-1], "/")
|
||||||
@@ -234,7 +235,7 @@ func GetToken(u *url.URL) (string, string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Return the path without the token
|
// Return the path without the token
|
||||||
return path, token
|
return path, token, true
|
||||||
}
|
}
|
||||||
|
|
||||||
func splitPath(path string) []string {
|
func splitPath(path string) []string {
|
||||||
@@ -261,7 +262,7 @@ func (s *server) handlePlay(conn *rtmp.Conn) {
|
|||||||
defer conn.Close()
|
defer conn.Close()
|
||||||
|
|
||||||
remote := conn.NetConn().RemoteAddr()
|
remote := conn.NetConn().RemoteAddr()
|
||||||
playpath, token := GetToken(conn.URL)
|
playpath, token, isStreamkey := GetToken(conn.URL)
|
||||||
|
|
||||||
playpath, _ = removePathPrefix(playpath, s.app)
|
playpath, _ = removePathPrefix(playpath, s.app)
|
||||||
|
|
||||||
@@ -272,6 +273,11 @@ func (s *server) handlePlay(conn *rtmp.Conn) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if identity == "$anon" && isStreamkey {
|
||||||
|
// If the token was part of the path, we add it back
|
||||||
|
playpath = filepath.Join(playpath, token)
|
||||||
|
}
|
||||||
|
|
||||||
domain := s.findDomainFromPlaypath(playpath)
|
domain := s.findDomainFromPlaypath(playpath)
|
||||||
resource := playpath
|
resource := playpath
|
||||||
|
|
||||||
@@ -384,7 +390,7 @@ func (s *server) handlePublish(conn *rtmp.Conn) {
|
|||||||
defer conn.Close()
|
defer conn.Close()
|
||||||
|
|
||||||
remote := conn.NetConn().RemoteAddr()
|
remote := conn.NetConn().RemoteAddr()
|
||||||
playpath, token := GetToken(conn.URL)
|
playpath, token, isStreamkey := GetToken(conn.URL)
|
||||||
|
|
||||||
playpath, app := removePathPrefix(playpath, s.app)
|
playpath, app := removePathPrefix(playpath, s.app)
|
||||||
|
|
||||||
@@ -395,6 +401,11 @@ func (s *server) handlePublish(conn *rtmp.Conn) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if identity == "$anon" && isStreamkey {
|
||||||
|
// If the token was part of the path, we add it back
|
||||||
|
playpath = filepath.Join(playpath, token)
|
||||||
|
}
|
||||||
|
|
||||||
// Check the app patch
|
// Check the app patch
|
||||||
if app != s.app {
|
if app != s.app {
|
||||||
s.log(identity, "PUBLISH", "FORBIDDEN", playpath, "invalid app", remote)
|
s.log(identity, "PUBLISH", "FORBIDDEN", playpath, "invalid app", remote)
|
||||||
@@ -483,12 +494,16 @@ func (s *server) findIdentityFromStreamKey(key string) (string, error) {
|
|||||||
|
|
||||||
var identity iamidentity.Verifier = nil
|
var identity iamidentity.Verifier = nil
|
||||||
var err error = nil
|
var err error = nil
|
||||||
|
var isDefaultIdentity bool = false
|
||||||
|
|
||||||
var token string
|
var token string
|
||||||
|
|
||||||
username, token := enctoken.Unmarshal(key)
|
username, token := enctoken.Unmarshal(key)
|
||||||
if len(username) == 0 {
|
if len(username) == 0 {
|
||||||
|
// Legacy compatibility. If the key is only the token, then it
|
||||||
|
// is assumed that it belongs to the superuser.
|
||||||
identity = s.iam.GetDefaultVerifier()
|
identity = s.iam.GetDefaultVerifier()
|
||||||
|
isDefaultIdentity = true
|
||||||
} else {
|
} else {
|
||||||
identity, err = s.iam.GetVerifier(username)
|
identity, err = s.iam.GetVerifier(username)
|
||||||
}
|
}
|
||||||
@@ -500,11 +515,15 @@ func (s *server) findIdentityFromStreamKey(key string) (string, error) {
|
|||||||
if ok, err := identity.VerifyServiceToken(token); !ok {
|
if ok, err := identity.VerifyServiceToken(token); !ok {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
err = fmt.Errorf("invalid token: %w", err)
|
err = fmt.Errorf("invalid token: %w", err)
|
||||||
} else {
|
|
||||||
err = fmt.Errorf("invalid token")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return "$anon", err
|
if isDefaultIdentity {
|
||||||
|
// If verifying the token fails and if this is the superuser,
|
||||||
|
// then assume anonymous access
|
||||||
|
return "$anon", nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return identity.Name(), err
|
||||||
}
|
}
|
||||||
|
|
||||||
return identity.Name(), nil
|
return identity.Name(), nil
|
||||||
|
@@ -20,7 +20,7 @@ func TestToken(t *testing.T) {
|
|||||||
u, err := url.Parse(d[0])
|
u, err := url.Parse(d[0])
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
path, token := GetToken(u)
|
path, token, _ := GetToken(u)
|
||||||
|
|
||||||
require.Equal(t, d[1], path, "url=%s", u.String())
|
require.Equal(t, d[1], path, "url=%s", u.String())
|
||||||
require.Equal(t, d[2], token, "url=%s", u.String())
|
require.Equal(t, d[2], token, "url=%s", u.String())
|
||||||
|
Reference in New Issue
Block a user