Git Inbox Mirror of the ffmpeg-devel mailing list - see https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
 help / color / mirror / Atom feed
* [FFmpeg-devel] [PATCH] avformat/httpauth: handle multiple Digest authentication headers (MD5 and SHA-256)
@ 2025-12-12  6:36 Zhigong Liu via ffmpeg-devel
  2025-12-14  8:30 ` [FFmpeg-devel] " Rémi Denis-Courmont via ffmpeg-devel
  0 siblings, 1 reply; 2+ messages in thread
From: Zhigong Liu via ffmpeg-devel @ 2025-12-12  6:36 UTC (permalink / raw)
  To: ffmpeg-devel; +Cc: Zhigong Liu

Some RTSP servers return multiple WWW-Authenticate: Digest headers
containing both MD5 and SHA-256 challenges (example below). FFmpeg previously parsed
only the last header, causing authentication failure. This patch adds
support for parsing all Digest headers and selecting a valid algorithm.

Real Time Streaming Protocol
    Response: RTSP/1.0 401 Unauthorized\r\n
    CSeq:  2\r\n
    Date: Thu, Dec 11 2025 05:36:10 GMT\r\n
    WWW-Authenticate: Digest realm="RtspServerLibrary", nonce="pJMYo4EfZcpH5ly0eJunZr8F9s1AY1c9", algorithm="MD5"\r\n
    WWW-Authenticate: Digest realm="RtspServerLibrary", nonce="pJMYo4EfZcpH5ly0eJunZr8F9s1AY1c9", algorithm="SHA-256"\r\n
    \r\n

Signed-off-by: Zhigong Liu <lzhigong@gmail.com>
---
 libavformat/httpauth.c | 41 +++++++++++++++++++++++++++++++----------
 1 file changed, 31 insertions(+), 10 deletions(-)

diff --git a/libavformat/httpauth.c b/libavformat/httpauth.c
index 9048362509..2f9c600842 100644
--- a/libavformat/httpauth.c
+++ b/libavformat/httpauth.c
@@ -101,16 +101,34 @@ void ff_http_auth_handle_header(HTTPAuthState *state, const char *key,
                                state);
         } else if (av_stristart(value, "Digest ", &p) &&
                    state->auth_type <= HTTP_AUTH_DIGEST) {
-            state->auth_type = HTTP_AUTH_DIGEST;
-            memset(&state->digest_params, 0, sizeof(DigestParams));
-            state->realm[0] = 0;
-            state->stale = 0;
-            ff_parse_key_value(p, (ff_parse_key_val_cb) handle_digest_params,
-                               state);
-            choose_qop(state->digest_params.qop,
-                       sizeof(state->digest_params.qop));
-            if (!av_strcasecmp(state->digest_params.stale, "true"))
-                state->stale = 1;
+            /* Handle multiple Digest authentication headers by preferring MD5 over SHA-256
+             * or updating if we haven't set digest auth yet */
+            const char *alg_start = strstr(p, "algorithm=");
+            int is_md5 = 1; /* Default to MD5 if no algorithm specified */
+            
+            if (alg_start) {
+                alg_start += 10; /* Skip "algorithm=" */
+                if (av_strncasecmp(alg_start, "\"MD5\"", 5) == 0 || av_strncasecmp(alg_start, "MD5", 3) == 0) {
+                    is_md5 = 1;
+                } else if (av_strncasecmp(alg_start, "\"SHA-256\"", 9) == 0 || av_strncasecmp(alg_start, "SHA-256", 7) == 0) {
+                    is_md5 = 0;
+                }
+            }
+            
+            /* Prefer MD5 over SHA-256, or set if not already set */
+            if (state->auth_type < HTTP_AUTH_DIGEST || 
+                (is_md5 && av_strcasecmp(state->digest_params.algorithm, "MD5") != 0)) {
+                state->auth_type = HTTP_AUTH_DIGEST;
+                memset(&state->digest_params, 0, sizeof(DigestParams));
+                state->realm[0] = 0;
+                state->stale = 0;
+                ff_parse_key_value(p, (ff_parse_key_val_cb) handle_digest_params,
+                                    state);
+                choose_qop(state->digest_params.qop,
+                            sizeof(state->digest_params.qop));
+                if (!av_strcasecmp(state->digest_params.stale, "true"))
+                    state->stale = 1;
+            }
         }
     } else if (!av_strcasecmp(key, "Authentication-Info")) {
         ff_parse_key_value(value, (ff_parse_key_val_cb) handle_digest_update,
@@ -221,8 +239,11 @@ static char *make_digest_auth(HTTPAuthState *state, const char *username,
     av_strlcatf(authstr, len, ", response=\"%s\"",  response);
 
     // we are violating the RFC and use "" because all others seem to do that too.
+	// Always include algorithm for better compatibility
     if (digest->algorithm[0])
         av_strlcatf(authstr, len, ", algorithm=\"%s\"",  digest->algorithm);
+    else
+        av_strlcatf(authstr, len, ", algorithm=\"MD5\"");
 
     if (digest->opaque[0])
         av_strlcatf(authstr, len, ", opaque=\"%s\"", digest->opaque);
-- 
2.49.0.windows.1

_______________________________________________
ffmpeg-devel mailing list -- ffmpeg-devel@ffmpeg.org
To unsubscribe send an email to ffmpeg-devel-leave@ffmpeg.org

^ permalink raw reply	[flat|nested] 2+ messages in thread

* [FFmpeg-devel] Re: [PATCH] avformat/httpauth: handle multiple Digest authentication headers (MD5 and SHA-256)
  2025-12-12  6:36 [FFmpeg-devel] [PATCH] avformat/httpauth: handle multiple Digest authentication headers (MD5 and SHA-256) Zhigong Liu via ffmpeg-devel
@ 2025-12-14  8:30 ` Rémi Denis-Courmont via ffmpeg-devel
  0 siblings, 0 replies; 2+ messages in thread
From: Rémi Denis-Courmont via ffmpeg-devel @ 2025-12-14  8:30 UTC (permalink / raw)
  To: ffmpeg-devel; +Cc: Rémi Denis-Courmont

Le perjantaina 12. joulukuuta 2025, 8.36.52 Itä-Euroopan normaaliaika Zhigong 
Liu via ffmpeg-devel a écrit :
> Some RTSP servers return multiple WWW-Authenticate: Digest headers
> containing both MD5 and SHA-256 challenges (example below). FFmpeg
> previously parsed only the last header, causing authentication failure.
> This patch adds support for parsing all Digest headers and selecting a
> valid algorithm.
> 
> Real Time Streaming Protocol
>     Response: RTSP/1.0 401 Unauthorized\r\n
>     CSeq:  2\r\n
>     Date: Thu, Dec 11 2025 05:36:10 GMT\r\n
>     WWW-Authenticate: Digest realm="RtspServerLibrary",
> nonce="pJMYo4EfZcpH5ly0eJunZr8F9s1AY1c9", algorithm="MD5"\r\n
> WWW-Authenticate: Digest realm="RtspServerLibrary",
> nonce="pJMYo4EfZcpH5ly0eJunZr8F9s1AY1c9", algorithm="SHA-256"\r\n \r\n
> 
> Signed-off-by: Zhigong Liu <lzhigong@gmail.com>
> ---
>  libavformat/httpauth.c | 41 +++++++++++++++++++++++++++++++----------
>  1 file changed, 31 insertions(+), 10 deletions(-)
> 
> diff --git a/libavformat/httpauth.c b/libavformat/httpauth.c
> index 9048362509..2f9c600842 100644
> --- a/libavformat/httpauth.c
> +++ b/libavformat/httpauth.c
> @@ -101,16 +101,34 @@ void ff_http_auth_handle_header(HTTPAuthState *state,
> const char *key, state);
>          } else if (av_stristart(value, "Digest ", &p) &&
>                     state->auth_type <= HTTP_AUTH_DIGEST) {
> -            state->auth_type = HTTP_AUTH_DIGEST;
> -            memset(&state->digest_params, 0, sizeof(DigestParams));
> -            state->realm[0] = 0;
> -            state->stale = 0;
> -            ff_parse_key_value(p, (ff_parse_key_val_cb)
> handle_digest_params, -                               state);
> -            choose_qop(state->digest_params.qop,
> -                       sizeof(state->digest_params.qop));
> -            if (!av_strcasecmp(state->digest_params.stale, "true"))
> -                state->stale = 1;
> +            /* Handle multiple Digest authentication headers by preferring
> MD5 over SHA-256 +             * or updating if we haven't set digest auth
> yet */
> +            const char *alg_start = strstr(p, "algorithm=");
> +            int is_md5 = 1; /* Default to MD5 if no algorithm specified */
> +
> +            if (alg_start) {
> +                alg_start += 10; /* Skip "algorithm=" */
> +                if (av_strncasecmp(alg_start, "\"MD5\"", 5) == 0 ||
> av_strncasecmp(alg_start, "MD5", 3) == 0) { +                    is_md5 =
> 1;
> +                } else if (av_strncasecmp(alg_start, "\"SHA-256\"", 9) == 0
> || av_strncasecmp(alg_start, "SHA-256", 7) == 0) { +                   
> is_md5 = 0;
> +                }
> +            }
> +
> +            /* Prefer MD5 over SHA-256, or set if not already set */

AFAIK, the UA is supposed to try authentication algorithms in the order 
specified by the server. Digest authentication is a stupid choice, that is 
essentially worse than Basic in almost every respect [1]. But regardless you 
should definitely not prefer Digest-MD5 over Digest-SHA2, but follow the 
specified order.

[1] If you don't use TLS (or link-layer security), only protects against 
passive MITM, not active MITM, so TLS should be used anyway. And then, Digest 
requires storing the authentication secrets, the user name and the A1 value, 
on the server from where they can be lifted. This is not a problem with Basic. 
where the server can store a nonreversible salted hash of the password.

-- 
ヅニ-クーモン・レミ
https://www.remlab.net/



_______________________________________________
ffmpeg-devel mailing list -- ffmpeg-devel@ffmpeg.org
To unsubscribe send an email to ffmpeg-devel-leave@ffmpeg.org

^ permalink raw reply	[flat|nested] 2+ messages in thread

end of thread, other threads:[~2025-12-14  8:31 UTC | newest]

Thread overview: 2+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2025-12-12  6:36 [FFmpeg-devel] [PATCH] avformat/httpauth: handle multiple Digest authentication headers (MD5 and SHA-256) Zhigong Liu via ffmpeg-devel
2025-12-14  8:30 ` [FFmpeg-devel] " Rémi Denis-Courmont via ffmpeg-devel

Git Inbox Mirror of the ffmpeg-devel mailing list - see https://ffmpeg.org/mailman/listinfo/ffmpeg-devel

This inbox may be cloned and mirrored by anyone:

	git clone --mirror https://master.gitmailbox.com/ffmpegdev/0 ffmpegdev/git/0.git

	# If you have public-inbox 1.1+ installed, you may
	# initialize and index your mirror using the following commands:
	public-inbox-init -V2 ffmpegdev ffmpegdev/ https://master.gitmailbox.com/ffmpegdev \
		ffmpegdev@gitmailbox.com
	public-inbox-index ffmpegdev

Example config snippet for mirrors.


AGPL code for this site: git clone https://public-inbox.org/public-inbox.git