Fix accessing RTMP stream without token or streamkey for anonymous users

This commit is contained in:
Ingo Oppermann
2024-02-29 14:30:24 +01:00
parent 2514feec23
commit aaec24b898
3 changed files with 33 additions and 14 deletions

View File

@@ -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()

View File

@@ -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

View File

@@ -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())