Repository: bluenviron/mediamtx

Issue:
Using password as a hashed string problem. Worked on v1.9.1 and stopped working on v.1.12.3
### Which version are you using?

1.12.3

### Which operating system are you using?

Windows amd64 standard

### Describe how to replicate the issue

1.
When I use plain credentials, everything works as expected (1.12.3)
I can connect to mediaMTX and receive stream from cameras
```
authInternalUsers:
- user: rtsp
  pass: rtsp
  ips: []
  permissions:
  - action: publish
    path:
  - action: read
    path:
  - action: playback
    path:
```

2.
On **v1.9.1** I had plain username and **_hashed_** password. 
**It worked file before**. Now, I am trying to move to the latest v1.12.3 expecting the feature with **_hashed password_** still works. 

3.
When I use **_hashed password_** on **v1.12.3** I have 401 response. 
Again, plain password works as expected.
```
authInternalUsers:
- user: rtsp
  pass: sha256:q9A3LaO+agZNkYBCYFnA0+jDLxXMykcBgHxhi08te+U=
  ips: []
  permissions:
  - action: publish
    path:
  - action: read
    path:
  - action: playback
    path:
```




### Server logs

_No response_

### Network dump

_No response_

Discussion:
@aler9: This is fixed by #4700. To contextualize, this bug only happens when reading with RTSP and credentials are hashed.@github-actions: This issue is mentioned in release v1.13.1 🚀
Check out the entire changelog by [clicking here](https://github.com/bluenviron/mediamtx/releases/tag/v1.13.1)

Solution patch:
diff --git a/internal/servers/rtsp/session.go b/internal/servers/rtsp/session.go
index dadfa5f4..cdbc50ae 100644
--- a/internal/servers/rtsp/session.go
+++ b/internal/servers/rtsp/session.go
@@ -215,18 +215,25 @@ func (s *session) onSetup(c *conn, ctx *gortsplib.ServerHandlerOnSetupCtx,
 		}
 	}
 
+	// CustomVerifyFunc prevents hashed credentials from working.
+	// Use it only when strictly needed.
+	var customVerifyFunc func(expectedUser, expectedPass string) bool
+	if contains(c.authMethods, rtspauth.VerifyMethodDigestMD5) {
+		customVerifyFunc = func(expectedUser, expectedPass string) bool {
+			return c.rconn.VerifyCredentials(ctx.Request, expectedUser, expectedPass)
+		}
+	}
+
 	switch s.rsession.State() {
 	case gortsplib.ServerSessionStateInitial, gortsplib.ServerSessionStatePrePlay: // play
 		req := defs.PathAccessRequest{
-			Name:        ctx.Path,
-			Query:       ctx.Query,
-			Proto:       auth.ProtocolRTSP,
-			ID:          &c.uuid,
-			Credentials: rtsp.Credentials(ctx.Request),
-			IP:          c.ip(),
-			CustomVerifyFunc: func(expectedUser, expectedPass string) bool {
-				return c.rconn.VerifyCredentials(ctx.Request, expectedUser, expectedPass)
-			},
+			Name:             ctx.Path,
+			Query:            ctx.Query,
+			Proto:            auth.ProtocolRTSP,
+			ID:               &c.uuid,
+			Credentials:      rtsp.Credentials(ctx.Request),
+			IP:               c.ip(),
+			CustomVerifyFunc: customVerifyFunc,
 		}
 
 		path, stream, err := s.pathManager.AddReader(defs.PathAddReaderReq{



Test patch:
diff --git a/internal/servers/rtsp/server_test.go b/internal/servers/rtsp/server_test.go
index 9ece2ff7..b0d1d7b6 100644
--- a/internal/servers/rtsp/server_test.go
+++ b/internal/servers/rtsp/server_test.go
@@ -75,6 +75,8 @@ func TestServerPublish(t *testing.T) {
 			pathManager := &test.PathManager{
 				AddPublisherImpl: func(req defs.PathAddPublisherReq) (defs.Path, error) {
 					if ca == "basic" {
+						require.Nil(t, req.AccessRequest.CustomVerifyFunc)
+
 						if req.AccessRequest.Credentials.User == "" && req.AccessRequest.Credentials.Pass == "" {
 							return nil, auth.Error{Message: "", AskCredentials: true}
 						}
@@ -91,6 +93,7 @@ func TestServerPublish(t *testing.T) {
 						}
 						require.True(t, ok)
 					}
+
 					return path, nil
 				},
 			}
@@ -191,6 +194,8 @@ func TestServerRead(t *testing.T) {
 			pathManager := &test.PathManager{
 				DescribeImpl: func(req defs.PathDescribeReq) defs.PathDescribeRes {
 					if ca == "basic" {
+						require.Nil(t, req.AccessRequest.CustomVerifyFunc)
+
 						if req.AccessRequest.Credentials.User == "" && req.AccessRequest.Credentials.Pass == "" {
 							return defs.PathDescribeRes{Err: auth.Error{Message: "", AskCredentials: true}}
 						}
@@ -216,6 +221,8 @@ func TestServerRead(t *testing.T) {
 				},
 				AddReaderImpl: func(req defs.PathAddReaderReq) (defs.Path, *stream.Stream, error) {
 					if ca == "basic" {
+						require.Nil(t, req.AccessRequest.CustomVerifyFunc)
+
 						require.Equal(t, "teststream", req.AccessRequest.Name)
 						require.Equal(t, "param=value", req.AccessRequest.Query)
 						require.Equal(t, "myuser", req.AccessRequest.Credentials.User)