From 36298f8bc8496596ff759c0b0d42db04409efc70 Mon Sep 17 00:00:00 2001 From: Alessandro Ros Date: Sun, 23 Jul 2023 19:31:34 +0200 Subject: [PATCH] webrtc: send session ID to external auth server (#1981) (#2098) --- internal/core/hls_http_server.go | 2 +- internal/core/hls_manager_test.go | 7 +++-- internal/core/path.go | 2 +- internal/core/webrtc_http_server.go | 47 ++++++++++++++++------------- internal/core/webrtc_manager.go | 3 ++ internal/core/webrtc_session.go | 33 +++++++++++++++++--- 6 files changed, 65 insertions(+), 29 deletions(-) diff --git a/internal/core/hls_http_server.go b/internal/core/hls_http_server.go index 729ec5c9..8c56c19f 100644 --- a/internal/core/hls_http_server.go +++ b/internal/core/hls_http_server.go @@ -161,7 +161,7 @@ func (s *hlsHTTPServer) onRequest(ctx *gin.Context) { return } - s.Log(logger.Info, "authentication error: %v", terr.wrapped) + s.Log(logger.Info, "authentication failed: %v", terr.wrapped) ctx.Writer.WriteHeader(http.StatusUnauthorized) return } diff --git a/internal/core/hls_manager_test.go b/internal/core/hls_manager_test.go index 798a2ead..75e2ad24 100644 --- a/internal/core/hls_manager_test.go +++ b/internal/core/hls_manager_test.go @@ -21,7 +21,8 @@ type testHTTPAuthenticator struct { protocol string action string - s *http.Server + s *http.Server + firstReceived bool } func newTestHTTPAuthenticator(t *testing.T, protocol string, action string) *testHTTPAuthenticator { @@ -75,7 +76,7 @@ func (ts *testHTTPAuthenticator) onAuth(ctx *gin.Context) { in.Password != "testpass" || in.Path != "teststream" || in.Protocol != ts.protocol || - // in.ID == "" || + (ts.firstReceived && in.ID == "") || in.Action != ts.action || (in.Query != "user=testreader&pass=testpass¶m=value" && in.Query != "user=testpublisher&pass=testpass¶m=value" && @@ -83,6 +84,8 @@ func (ts *testHTTPAuthenticator) onAuth(ctx *gin.Context) { ctx.AbortWithStatus(http.StatusBadRequest) return } + + ts.firstReceived = true } func httpPullFile(t *testing.T, hc *http.Client, u string) []byte { diff --git a/internal/core/path.go b/internal/core/path.go index e3675e42..c6c1bf4b 100644 --- a/internal/core/path.go +++ b/internal/core/path.go @@ -30,7 +30,7 @@ type pathErrAuth struct { // Error implements the error interface. func (e pathErrAuth) Error() string { - return "authentication error" + return "authentication failed" } type pathErrNoOnePublishing struct { diff --git a/internal/core/webrtc_http_server.go b/internal/core/webrtc_http_server.go index 2feb2f20..1c48ed5b 100644 --- a/internal/core/webrtc_http_server.go +++ b/internal/core/webrtc_http_server.go @@ -293,35 +293,37 @@ func (s *webRTCHTTPServer) onRequest(ctx *gin.Context) { ip := ctx.ClientIP() _, port, _ := net.SplitHostPort(ctx.Request.RemoteAddr) - user, pass, hasCredentials := ctx.Request.BasicAuth() - res := s.pathManager.getConfForPath(pathGetConfForPathReq{ - name: dir, - publish: publish, - credentials: authCredentials{ - query: ctx.Request.URL.RawQuery, - ip: net.ParseIP(ip), - user: user, - pass: pass, - proto: authProtocolWebRTC, - }, - }) - if res.err != nil { - if terr, ok := res.err.(pathErrAuth); ok { - if !hasCredentials { - ctx.Header("WWW-Authenticate", `Basic realm="mediamtx"`) + // if request doesn't belong to a session, check authentication here + if !isWHIPorWHEP || ctx.Request.Method == http.MethodOptions { + res := s.pathManager.getConfForPath(pathGetConfForPathReq{ + name: dir, + publish: publish, + credentials: authCredentials{ + query: ctx.Request.URL.RawQuery, + ip: net.ParseIP(ip), + user: user, + pass: pass, + proto: authProtocolWebRTC, + }, + }) + if res.err != nil { + if terr, ok := res.err.(pathErrAuth); ok { + if !hasCredentials { + ctx.Header("WWW-Authenticate", `Basic realm="mediamtx"`) + ctx.Writer.WriteHeader(http.StatusUnauthorized) + return + } + + s.Log(logger.Info, "authentication failed: %v", terr.wrapped) ctx.Writer.WriteHeader(http.StatusUnauthorized) return } - s.Log(logger.Info, "authentication error: %v", terr.wrapped) - ctx.Writer.WriteHeader(http.StatusUnauthorized) + ctx.Writer.WriteHeader(http.StatusNotFound) return } - - ctx.Writer.WriteHeader(http.StatusNotFound) - return } switch fname { @@ -357,6 +359,9 @@ func (s *webRTCHTTPServer) onRequest(ctx *gin.Context) { res := s.parent.sessionNew(webRTCSessionNewReq{ pathName: dir, remoteAddr: net.JoinHostPort(ip, port), + query: ctx.Request.URL.RawQuery, + user: user, + pass: pass, offer: offer, publish: (fname == "whip"), }) diff --git a/internal/core/webrtc_manager.go b/internal/core/webrtc_manager.go index 696ad8b2..c9ae1230 100644 --- a/internal/core/webrtc_manager.go +++ b/internal/core/webrtc_manager.go @@ -97,6 +97,9 @@ type webRTCSessionNewRes struct { type webRTCSessionNewReq struct { pathName string remoteAddr string + query string + user string + pass string offer []byte publish bool res chan webRTCSessionNewRes diff --git a/internal/core/webrtc_session.go b/internal/core/webrtc_session.go index 6bbbce37..fcfa170e 100644 --- a/internal/core/webrtc_session.go +++ b/internal/core/webrtc_session.go @@ -4,6 +4,7 @@ import ( "context" "encoding/hex" "fmt" + "net" "net/http" "strings" "sync" @@ -223,13 +224,25 @@ func (s *webRTCSession) runInner2() (int, error) { } func (s *webRTCSession) runPublish() (int, error) { + ip, _, _ := net.SplitHostPort(s.req.remoteAddr) + res := s.pathManager.publisherAdd(pathPublisherAddReq{ author: s, pathName: s.req.pathName, - skipAuth: true, + credentials: authCredentials{ + query: s.req.query, + ip: net.ParseIP(ip), + user: s.req.user, + pass: s.req.pass, + proto: authProtocolWebRTC, + id: &s.uuid, + }, }) if res.err != nil { - return http.StatusInternalServerError, res.err + if _, ok := res.err.(pathErrAuth); ok { + return http.StatusUnauthorized, res.err + } + return http.StatusBadRequest, res.err } defer res.path.publisherRemove(pathPublisherRemoveReq{author: s}) @@ -334,16 +347,28 @@ func (s *webRTCSession) runPublish() (int, error) { } func (s *webRTCSession) runRead() (int, error) { + ip, _, _ := net.SplitHostPort(s.req.remoteAddr) + res := s.pathManager.readerAdd(pathReaderAddReq{ author: s, pathName: s.req.pathName, - skipAuth: true, + credentials: authCredentials{ + query: s.req.query, + ip: net.ParseIP(ip), + user: s.req.user, + pass: s.req.pass, + proto: authProtocolWebRTC, + id: &s.uuid, + }, }) if res.err != nil { + if _, ok := res.err.(pathErrAuth); ok { + return http.StatusUnauthorized, res.err + } if strings.HasPrefix(res.err.Error(), "no one is publishing") { return http.StatusNotFound, res.err } - return http.StatusInternalServerError, res.err + return http.StatusBadRequest, res.err } defer res.path.readerRemove(pathReaderRemoveReq{author: s})