The HTTP Digest authentication code only supported MD5 and MD5-sess algorithms. Servers that offer SHA-256 challenges (e.g. Milestone XProtect VMS, which sends dual MD5 + SHA-256 WWW-Authenticate headers) could not be authenticated against, because make_digest_auth() returned NULL for any algorithm it did not recognize.
Add native SHA-256 and SHA-256-sess digest authentication using the existing libavutil/sha API: - Include libavutil/sha.h and add update_sha_strings(), a variadic helper analogous to the existing update_md5_strings(). - Branch make_digest_auth() on the parsed algorithm: SHA-256 / SHA-256-sess use AVSHA with 32-byte digests (64 hex chars), while MD5 / MD5-sess continue to use AVMD5 with 16-byte digests (32 hex chars). - Widen the hash and hex-string buffers (A1hash, A2hash, response, hash) to accommodate the larger SHA-256 output. - Widen DigestParams.algorithm from 10 to 16 bytes in httpauth.h so that "SHA-256-sess" (12 chars + NUL) is not silently truncated. - When multiple Digest challenges are received, prefer SHA-256 over MD5: if a SHA-256 challenge has already been parsed, subsequent weaker challenges are skipped; if the current challenge is MD5 and a new SHA-256 challenge arrives, the SHA-256 one replaces it. This follows RFC 7616, which defines SHA-256 as a stronger alternative to MD5 for HTTP Digest authentication. Signed-off-by: Patrik <[email protected]> --- libavformat/httpauth.c | 120 ++++++++++++++++++++++++++++++----------- libavformat/httpauth.h | 2 +- 2 files changed, 90 insertions(+), 32 deletions(-) diff --git a/libavformat/httpauth.c b/libavformat/httpauth.c index 4bc746dbf3..8aa281fe0e 100644 --- a/libavformat/httpauth.c +++ b/libavformat/httpauth.c @@ -26,6 +26,7 @@ #include "internal.h" #include "libavutil/random_seed.h" #include "libavutil/md5.h" +#include "libavutil/sha.h" #include "urldecode.h" static void handle_basic_params(void *context, const char *key, @@ -103,6 +104,15 @@ void ff_http_auth_handle_header(HTTPAuthState *state, const char *key, ff_parse_key_value(p, handle_basic_params, state); } else if (av_stristart(value, "Digest ", &p) && state->auth_type <= HTTP_AUTH_DIGEST) { + if (state->auth_type == HTTP_AUTH_DIGEST) { + int current_is_sha256 = !av_strcasecmp(state->digest_params.algorithm, "SHA-256") || + !av_strcasecmp(state->digest_params.algorithm, "SHA-256-sess"); + int new_is_sha256 = av_stristr(p, "SHA-256") != NULL; + if (current_is_sha256 || !new_is_sha256) { + return; + } + } + state->auth_type = HTTP_AUTH_DIGEST; memset(&state->digest_params, 0, sizeof(DigestParams)); state->realm[0] = 0; @@ -133,6 +143,20 @@ static void update_md5_strings(struct AVMD5 *md5ctx, ...) va_end(vl); } +static void update_sha_strings(struct AVSHA *shactx, ...) +{ + va_list vl; + + va_start(vl, shactx); + while (1) { + const char* str = va_arg(vl, const char*); + if (!str) + break; + av_sha_update(shactx, str, strlen(str)); + } + va_end(vl); +} + /* Generate a digest reply, according to RFC 2617. */ static char *make_digest_auth(HTTPAuthState *state, const char *username, const char *password, const char *uri, @@ -144,10 +168,10 @@ static char *make_digest_auth(HTTPAuthState *state, const char *username, char cnonce[17]; char nc[9]; int i; - char A1hash[33], A2hash[33], response[33]; - struct AVMD5 *md5ctx; - uint8_t hash[16]; + char A1hash[65], A2hash[65], response[65]; + uint8_t hash[32]; char *authstr; + int is_sha256 = !av_strcasecmp(digest->algorithm, "SHA-256") || !av_strcasecmp(digest->algorithm, "SHA-256-sess"); digest->nc++; snprintf(nc, sizeof(nc), "%08x", digest->nc); @@ -157,42 +181,76 @@ static char *make_digest_auth(HTTPAuthState *state, const char *username, cnonce_buf[i] = av_get_random_seed(); ff_data_to_hex(cnonce, (const uint8_t*) cnonce_buf, sizeof(cnonce_buf), 1); - md5ctx = av_md5_alloc(); - if (!md5ctx) - return NULL; + if (is_sha256) { + struct AVSHA *shactx = av_sha_alloc(); + if (!shactx) + return NULL; + + av_sha_init(shactx, 256); + update_sha_strings(shactx, username, ":", state->realm, ":", password, NULL); + av_sha_final(shactx, hash); + ff_data_to_hex(A1hash, hash, 32, 1); - av_md5_init(md5ctx); - update_md5_strings(md5ctx, username, ":", state->realm, ":", password, NULL); - av_md5_final(md5ctx, hash); - ff_data_to_hex(A1hash, hash, 16, 1); + if (!av_strcasecmp(digest->algorithm, "SHA-256-sess")) { + av_sha_init(shactx, 256); + update_sha_strings(shactx, A1hash, ":", digest->nonce, ":", cnonce, NULL); + av_sha_final(shactx, hash); + ff_data_to_hex(A1hash, hash, 32, 1); + } + + av_sha_init(shactx, 256); + update_sha_strings(shactx, method, ":", uri, NULL); + av_sha_final(shactx, hash); + ff_data_to_hex(A2hash, hash, 32, 1); + + av_sha_init(shactx, 256); + update_sha_strings(shactx, A1hash, ":", digest->nonce, NULL); + if (!strcmp(digest->qop, "auth") || !strcmp(digest->qop, "auth-int")) { + update_sha_strings(shactx, ":", nc, ":", cnonce, ":", digest->qop, NULL); + } + update_sha_strings(shactx, ":", A2hash, NULL); + av_sha_final(shactx, hash); + ff_data_to_hex(response, hash, 32, 1); + + av_free(shactx); + } else { + struct AVMD5 *md5ctx = av_md5_alloc(); + if (!md5ctx) + return NULL; - if (!strcmp(digest->algorithm, "") || !strcmp(digest->algorithm, "MD5")) { - } else if (!strcmp(digest->algorithm, "MD5-sess")) { av_md5_init(md5ctx); - update_md5_strings(md5ctx, A1hash, ":", digest->nonce, ":", cnonce, NULL); + update_md5_strings(md5ctx, username, ":", state->realm, ":", password, NULL); av_md5_final(md5ctx, hash); ff_data_to_hex(A1hash, hash, 16, 1); - } else { - /* Unsupported algorithm */ - av_free(md5ctx); - return NULL; - } - av_md5_init(md5ctx); - update_md5_strings(md5ctx, method, ":", uri, NULL); - av_md5_final(md5ctx, hash); - ff_data_to_hex(A2hash, hash, 16, 1); + if (!strcmp(digest->algorithm, "") || !strcmp(digest->algorithm, "MD5")) { + } else if (!strcmp(digest->algorithm, "MD5-sess")) { + av_md5_init(md5ctx); + update_md5_strings(md5ctx, A1hash, ":", digest->nonce, ":", cnonce, NULL); + av_md5_final(md5ctx, hash); + ff_data_to_hex(A1hash, hash, 16, 1); + } else { + /* Unsupported algorithm */ + av_free(md5ctx); + return NULL; + } + + av_md5_init(md5ctx); + update_md5_strings(md5ctx, method, ":", uri, NULL); + av_md5_final(md5ctx, hash); + ff_data_to_hex(A2hash, hash, 16, 1); - av_md5_init(md5ctx); - update_md5_strings(md5ctx, A1hash, ":", digest->nonce, NULL); - if (!strcmp(digest->qop, "auth") || !strcmp(digest->qop, "auth-int")) { - update_md5_strings(md5ctx, ":", nc, ":", cnonce, ":", digest->qop, NULL); - } - update_md5_strings(md5ctx, ":", A2hash, NULL); - av_md5_final(md5ctx, hash); - ff_data_to_hex(response, hash, 16, 1); + av_md5_init(md5ctx); + update_md5_strings(md5ctx, A1hash, ":", digest->nonce, NULL); + if (!strcmp(digest->qop, "auth") || !strcmp(digest->qop, "auth-int")) { + update_md5_strings(md5ctx, ":", nc, ":", cnonce, ":", digest->qop, NULL); + } + update_md5_strings(md5ctx, ":", A2hash, NULL); + av_md5_final(md5ctx, hash); + ff_data_to_hex(response, hash, 16, 1); - av_free(md5ctx); + av_free(md5ctx); + } if (!strcmp(digest->qop, "") || !strcmp(digest->qop, "auth")) { } else if (!strcmp(digest->qop, "auth-int")) { diff --git a/libavformat/httpauth.h b/libavformat/httpauth.h index 0e7085901c..b7e1f82db1 100644 --- a/libavformat/httpauth.h +++ b/libavformat/httpauth.h @@ -34,7 +34,7 @@ typedef enum HTTPAuthType { typedef struct DigestParams { char nonce[300]; /**< Server specified nonce */ - char algorithm[10]; /**< Server specified digest algorithm */ + char algorithm[16]; /**< Server specified digest algorithm */ char qop[30]; /**< Quality of protection, containing the one * that we've chosen to use, from the * alternatives that the server offered. */ -- 2.43.0 _______________________________________________ ffmpeg-devel mailing list -- [email protected] To unsubscribe send an email to [email protected]
