Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

RTSP Basic Authorization #8941

Closed
Pitel opened this issue May 14, 2021 · 17 comments
Closed

RTSP Basic Authorization #8941

Pitel opened this issue May 14, 2021 · 17 comments
Assignees

Comments

@Pitel
Copy link

Pitel commented May 14, 2021

Use case description

I'm trying to connect to RTSP stream with URI: rtsp://admin:heslo123@1.2.3.4:554/Streaming/Channels/101?transportmode=unicast&profile=Profile_1. It doesn't work. I get the following error:

2021-05-14 15:39:05.812 21279-21443 E/ExoPlayerImplInternal: Playback error
      com.google.android.exoplayer2.ExoPlaybackException: Source error
        at com.google.android.exoplayer2.ExoPlayerImplInternal.handleMessage(ExoPlayerImplInternal.java:580)
        at android.os.Handler.dispatchMessage(Handler.java:102)
        at android.os.Looper.loop(Looper.java:223)
        at android.os.HandlerThread.run(HandlerThread.java:67)
     Caused by: com.google.android.exoplayer2.source.rtsp.RtspMediaSource$RtspPlaybackException: DESCRIBE 401
        at com.google.android.exoplayer2.source.rtsp.RtspMediaSource$SessionInfoListenerImpl.onSessionTimelineRequestFailed(RtspMediaSource.java:219)
        at com.google.android.exoplayer2.source.rtsp.RtspClient$MessageListener.dispatchRtspError(RtspClient.java:529)
        at com.google.android.exoplayer2.source.rtsp.RtspClient$MessageListener.onRtspMessageReceived(RtspClient.java:366)
        at com.google.android.exoplayer2.source.rtsp.RtspMessageChannel$Receiver.lambda$handleRtspMessage$0$RtspMessageChannel$Receiver(RtspMessageChannel.java:291)
        at com.google.android.exoplayer2.source.rtsp.-$$Lambda$RtspMessageChannel$Receiver$HitD0FATwe-gLFkoSjlAUnzETfA.run(Unknown Source:4)
        at android.os.Handler.handleCallback(Handler.java:938)
        at android.os.Handler.dispatchMessage(Handler.java:99)
        at android.os.Looper.loop(Looper.java:223) 
        at android.os.HandlerThread.run(HandlerThread.java:67) 
     Caused by: com.google.android.exoplayer2.source.rtsp.RtspMediaSource$RtspPlaybackException: DESCRIBE 401
        at com.google.android.exoplayer2.source.rtsp.RtspClient$MessageListener.onRtspMessageReceived(RtspClient.java:368)
        at com.google.android.exoplayer2.source.rtsp.RtspMessageChannel$Receiver.lambda$handleRtspMessage$0$RtspMessageChannel$Receiver(RtspMessageChannel.java:291) 
        at com.google.android.exoplayer2.source.rtsp.-$$Lambda$RtspMessageChannel$Receiver$HitD0FATwe-gLFkoSjlAUnzETfA.run(Unknown Source:4) 
        at android.os.Handler.handleCallback(Handler.java:938) 
        at android.os.Handler.dispatchMessage(Handler.java:99) 
        at android.os.Looper.loop(Looper.java:223) 
        at android.os.HandlerThread.run(HandlerThread.java:67) 

Indeed, when I debuigged the requests and responses, the Authorization: Basic header is missing. And I couldn't find any way to provide custom headers.

Proposed solution

Automaticaly generate the header based on the URI.

Alternatives considered

Provide a way to add custom headers.

@lcf87
Copy link

lcf87 commented May 14, 2021

Hello Pitel,

Thanks for your interest in RTSP, right now ExoPlayer does not support RTSP auth, but it is something that we have planned. Please stay tuned.

If you are looking to implement auth yourself, take a look at RtspClient.MessageListener. You can customize RtspRequest's header section with RtspHeaders.Builder

@lcf87 lcf87 self-assigned this May 14, 2021
@claincly claincly assigned claincly and unassigned lcf87 May 21, 2021
@claincly
Copy link
Contributor

Hi Pitel,

Wondered if you could provide a sample source to help us test?

@Pitel
Copy link
Author

Pitel commented May 21, 2021

Hi,
sorry, unfortunately I can't. Our testing camera is on the intranet and I'm not aware of any public cams required for testing.

But I can show you the requests (but it should be just the same header as HTTP uses) and reposnses and help you test it.

@claincly
Copy link
Contributor

The requests are useful too, thanks!

You can send us an email at dev.exoplayer@gmail.com using a subject in the format "Issue #8941", if you find it uncomfortable sharing that on Github.

@Pitel
Copy link
Author

Pitel commented May 24, 2021

I guess it's alright sharing it there. It's just a testing camera on the intranet with dummy password.

After a bit of debugging, I find you probably has 2 options:


The first one is to simply use the URI with username and password in it, like this (notice the admin:heslo123@ part):

DESCRIBE rtsp://admin:heslo123@10.2.33.226:554/Streaming/Channels/101?transportmode=unicast&profile=Profile_1 RTSP/1.0
CSeq: 2
User-Agent: ExoPlayerLib/2.10.4 (Media Player for Android)
Accept: application/sdp


RTSP/1.0 200 OK
CSeq: 2
Content-Base: rtsp://admin:heslo123@10.2.33.226:554/Streaming/Channels/101/
Content-Type: application/sdp
Content-Length: 901

<< SDP IS HERE >>

This is what I see when I'm using ExoPlayer from #3854.


Another option is what ffmpeg is doing. It sends the plain request, the camera responds with 401 and some WWW-Authenticate headers which should be used in the following repeated request:

DESCRIBE rtsp://10.2.33.226:554/Streaming/Channels/101?transportmode=unicast&profile=Profile_1 RTSP/1.0
Accept: application/sdp
CSeq: 2
User-Agent: libmpv


RTSP/1.0 401 Unauthorized
CSeq: 2
WWW-Authenticate: Digest realm="2857be52f47f", nonce="f4cba07ad14b5bf181ac77c5a92ba65f", stale="FALSE"
WWW-Authenticate: Basic realm="2857be52f47f"
Date:  Tue, Apr 25 1972 20:27:07 GMT


DESCRIBE rtsp://10.2.33.226:554/Streaming/Channels/101?transportmode=unicast&profile=Profile_1 RTSP/1.0
Accept: application/sdp
CSeq: 3
User-Agent: libmpv
Authorization: Digest username="admin", realm="2857be52f47f", nonce="f4cba07ad14b5bf181ac77c5a92ba65f", uri="rtsp://10.2.33.226:554/Streaming/Channels/101?transportmode=unicast&profile=Profile_1", response="50e0c1696fbccefe96b3a8d02379fad9"    


RTSP/1.0 200 OK
CSeq: 3
Content-Type: application/sdp
Content-Base: rtsp://10.2.33.226:554/Streaming/Channels/101/
Content-Length: 856

<< SDP IS HERE >>

I'm not the expert here, but I suppose the second option is more secure. However, when testing it, I found out that our camera responds 200 OK disregarding the nonce parameter 🤷.

@EduardAblekimov
Copy link

EduardAblekimov commented May 27, 2021

Hey @claincly. I've seen your commit on dev-v2 on basic and digest auth. So, I tried it with my camera that is located on my local network. It just streams an h.264 stream on rtsp. It correctly parses the WWW-Authenticate header and then sends DESCRIBE signal again, but then it just stucks.

After some debugging I found out that it stucks in RtspMessageChannel.handleRtspMessageLine. It just iterates through the InputStream trying to find \r\n (CRLF) until it hits EOF. Therefore there is no video stream.

Now, I am not sure if it's "Test Ready" or not, but if there is anything i can provide, please tell.

I also attached logs below

opening message channel on socket Socket[address=/192.168.1.114,port=554,localPort=43808]

OPTIONS rtsp://192.168.1.114:554/videoMain RTSP/1.0
cseq: 0
user-agent: ExoPlayerLib/2.14.0

RTSP/1.0 200 OK
Allow: OPTIONS, DESCRIBE, PLAY, PAUSE, GET_PARAMETER, SET_PARAMETER, TEARDOWN
CSeq: 0
Content-Length: 0


DESCRIBE rtsp://192.168.1.114:554/videoMain RTSP/1.0
cseq: 1
user-agent: ExoPlayerLib/2.14.0

RTSP/1.0 401 Unauthorized
WWW-Authenticate: Digest realm="MyRealm",nonce="90189dc995f6644d"
CSeq: 1
Content-Length: 0


DESCRIBE rtsp://192.168.1.114:554/videoMain RTSP/1.0
cseq: 2
user-agent: ExoPlayerLib/2.14.0
authorization: Digest username="admin", realm="MyRealm", nonce="90189dc995f6644d", uri="rtsp://192.168.1.114:554/videoMain", response="4bc524332a8cc8248cb4dec75b554ebb"

RTSP/1.0 200 OK
Content-Base: rtsp://192.168.1.114:554/videoMain/
Content-Type: application/sdp
CSeq: 2
Content-Length: 303

@claincly
Copy link
Contributor

@k1llrogg Is the log complete?

On a rough look, the DESCRIBE response did not carry the actual SDP media description. I'm not sure if that's because how you logged the message.

Where did you insert the log lines? Is it where I mentioned in the pull request?

@EduardAblekimov
Copy link

EduardAblekimov commented May 27, 2021

@claincly
I did it in the
RtspMessageChannel.addLine(byte[]).
More specifically after
String line = new String(lineBytes, /* offset= */ 0, /* length= */ lineBytes.length - 2, CHARSET);

Here i just log the lines.

I can also attach a kind of "sequence of actions":

  1. Digest extravaganza, we sent the second DESCRIBE request with the correct authorization header.
  2. We start to parse response in the RtspMessageChannel.addLine(byte[]). We parsed status code, then content-base, content-type, cseq, content-length.
  3. Next line is empty line, therefore we set state = STATE_READING_RTSP_BODY; and return messageLines = null
  4. In RtspMessageChannel.handleRtspMessage(byte) messageLines is still null, because there is also body which is not parsed.
  5. And then it stucks in RtspMessageChannel.handleRtspMessageLine, trying to find \r\n.

@claincly
Copy link
Contributor

claincly commented May 27, 2021

Thanks for the detailed comment. Can you try printing out the received bytes in handleRtspMessageLine(), after every dataInputStream.readByte()? My suspicion is your camera (RTSP server) did not insert a CRLF (\r\n) at the end of the message body, although it should (not necessarily a must).

@EduardAblekimov
Copy link

EduardAblekimov commented May 27, 2021

I am pretty confident that it doesn't have CRLF at the end.

But for additional information, I logged the bytes I am receiving. There are 700 bytes, but I will include last 10 of them.
114 111 108 58 118 105 100 101 111 10. There is LF, but no CR.

Probably have to use dataInputStream.read() instead and then also check for -1 as EOF signal besides checking for CRLF

@EduardAblekimov
Copy link

EduardAblekimov commented May 27, 2021

I advanced a little bit, changed DESCRIBE parser so it splits by LF instead of CRLF, but now it crashes on SessionDescription.Builder.build(), because there is no session name. Now, I am not particularly good with RTSP, so I can't tell if this parameter is really mandatory, but just for the sake of testing, I am sending you the DESCRIBE lines I am receiving.

v=0
o=- 3831122695 3831122695 IN IP4 192.168.1.114
c=IN IP4 192.168.1.114
t=0 0
a=tool:*confidential*
a=recvonly
a=type:broadcast
a=charset:UTF-8
a=control:*
m=video 0 RTP/AVP 96
a=rtpmap:96 H264/90000
a=fmtp:96 packetization-mode=1;sprop-parameter-sets=Z00AKpY1QPAET8s3AQEBAg==,aO48gA==
a=control:video

@claincly
Copy link
Contributor

claincly commented Jun 1, 2021

So there are two things:

  1. According to the byte you posted, the last three chars are 'e', 'o', and a LF '\n' (that match the DESCRIBE response you posted in the second comment), indeed the receiver will freeze if it's constantly looking for CRLF. I'll see what we can do on our side.

  2. The SDP (RFC2327) requires a session name (under a s= tag). I can see that the tool attribute in the DESCRIBE is confidential, I am assuming you have control over the server, I would suggest tweak the server a bit to include a session name.

@claincly
Copy link
Contributor

claincly commented Jun 1, 2021

Another thing that pops to my mind,

You said in one of the comments that you received over 700 bytes, but the Content-Length header you posted is only 303 bytes long. Are they the same media?

@EduardAblekimov
Copy link

Another thing that pops to my mind,

You said in one of the comments that you received over 700 bytes, but the Content-Length header you posted is only 303 bytes long. Are they the same media?

I was wrong, it equals to content length.

So there are two things:

  1. The SDP (RFC2327) requires a session name (under a s= tag). I can see that the tool attribute in the DESCRIBE is confidential, I am assuming you have control over the server, I would suggest tweak the server a bit to include a session name.

I don't have an exact access to the server, since my server is my client's camera, but i notified my client about it. meanwhile, I will probably drop the session name or assign some arbitrary value

@EduardAblekimov
Copy link

EduardAblekimov commented Jun 2, 2021

@claincly There is one more thing. As you can see in my SDP, there is no profile-level-id in fmtp attribute, which is strictly required in exoplayer. Referring to RFC6184:

If no profile-level-id is present, the Baseline profile, without additional constraints at Level 1, MUST be inferred.

But even better solution would be to parse it from sprop-parameter-sets. If you decode my sprop-parameter-sets to hexa, you will get 67 4d 00 2a 96 35 40 f0 04 4f cb 37 01 01 01 02, where 4d 00 2a is actually profile-level-id

@claincly
Copy link
Contributor

claincly commented Jun 2, 2021

@k1llrogg Thanks for pointing out. Could you post another issue on the profile-level-id though, it is out of this issue's scope.

As for the CRLF line terminator thing, we are working on a solution.

marcbaechinger pushed a commit that referenced this issue Jun 8, 2021
Related to Issue: #8941.

RTSP message body's format is not regulated by the RTSP spec, meaning it can
use either CRLF or LF as its line terminator. The old code assumes every line
ends with CRLF (RTSP message and the message body); the new code will rely on
the Content-Length information to receive the bytes for the message body.

#minor-release

PiperOrigin-RevId: 377475565
@claincly claincly closed this as completed Jun 8, 2021
ojw28 pushed a commit that referenced this issue Jun 10, 2021
Related to Issue: #8941.

RTSP message body's format is not regulated by the RTSP spec, meaning it can
use either CRLF or LF as its line terminator. The old code assumes every line
ends with CRLF (RTSP message and the message body); the new code will rely on
the Content-Length information to receive the bytes for the message body.

#minor-release

PiperOrigin-RevId: 377475565
@ishaq1994
Copy link

Use case description

I'm trying to connect to RTSP stream with URI: rtsp://admin:heslo123@1.2.3.4:554/Streaming/Channels/101?transportmode=unicast&profile=Profile_1. It doesn't work. I get the following error:

2021-05-14 15:39:05.812 21279-21443 E/ExoPlayerImplInternal: Playback error
      com.google.android.exoplayer2.ExoPlaybackException: Source error
        at com.google.android.exoplayer2.ExoPlayerImplInternal.handleMessage(ExoPlayerImplInternal.java:580)
        at android.os.Handler.dispatchMessage(Handler.java:102)
        at android.os.Looper.loop(Looper.java:223)
        at android.os.HandlerThread.run(HandlerThread.java:67)
     Caused by: com.google.android.exoplayer2.source.rtsp.RtspMediaSource$RtspPlaybackException: DESCRIBE 401
        at com.google.android.exoplayer2.source.rtsp.RtspMediaSource$SessionInfoListenerImpl.onSessionTimelineRequestFailed(RtspMediaSource.java:219)
        at com.google.android.exoplayer2.source.rtsp.RtspClient$MessageListener.dispatchRtspError(RtspClient.java:529)
        at com.google.android.exoplayer2.source.rtsp.RtspClient$MessageListener.onRtspMessageReceived(RtspClient.java:366)
        at com.google.android.exoplayer2.source.rtsp.RtspMessageChannel$Receiver.lambda$handleRtspMessage$0$RtspMessageChannel$Receiver(RtspMessageChannel.java:291)
        at com.google.android.exoplayer2.source.rtsp.-$$Lambda$RtspMessageChannel$Receiver$HitD0FATwe-gLFkoSjlAUnzETfA.run(Unknown Source:4)
        at android.os.Handler.handleCallback(Handler.java:938)
        at android.os.Handler.dispatchMessage(Handler.java:99)
        at android.os.Looper.loop(Looper.java:223) 
        at android.os.HandlerThread.run(HandlerThread.java:67) 
     Caused by: com.google.android.exoplayer2.source.rtsp.RtspMediaSource$RtspPlaybackException: DESCRIBE 401
        at com.google.android.exoplayer2.source.rtsp.RtspClient$MessageListener.onRtspMessageReceived(RtspClient.java:368)
        at com.google.android.exoplayer2.source.rtsp.RtspMessageChannel$Receiver.lambda$handleRtspMessage$0$RtspMessageChannel$Receiver(RtspMessageChannel.java:291) 
        at com.google.android.exoplayer2.source.rtsp.-$$Lambda$RtspMessageChannel$Receiver$HitD0FATwe-gLFkoSjlAUnzETfA.run(Unknown Source:4) 
        at android.os.Handler.handleCallback(Handler.java:938) 
        at android.os.Handler.dispatchMessage(Handler.java:99) 
        at android.os.Looper.loop(Looper.java:223) 
        at android.os.HandlerThread.run(HandlerThread.java:67) 

Indeed, when I debuigged the requests and responses, the Authorization: Basic header is missing. And I couldn't find any way to provide custom headers.

Proposed solution

Automaticaly generate the header based on the URI.

Alternatives considered

Provide a way to add custom headers.

Hi ,
I 'm also facing the same issue , so what is the solution . please help !

@google google locked and limited conversation to collaborators Aug 8, 2021
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

5 participants