[FFmpeg-cvslog] rtmp: Add support for limelight authentication

Martin Storsjö git at videolan.org
Tue Jan 1 14:09:55 CET 2013


ffmpeg | branch: master | Martin Storsjö <martin at martin.st> | Mon Dec 31 00:46:14 2012 +0200| [c1ea44c54d837a69d8285601cfba7aa8df16e053] | committer: Martin Storsjö

rtmp: Add support for limelight authentication

Limelight is a not too uncommon CDN. The authentication scheme is
pretty similar to the adobe authentication, but is even closer to
normal http digest authentication (but not close enough to warrant
sharing code) than the adobe version.

Signed-off-by: Martin Storsjö <martin at martin.st>

> http://git.videolan.org/gitweb.cgi/ffmpeg.git/?a=commit;h=c1ea44c54d837a69d8285601cfba7aa8df16e053
---

 Changelog               |    2 +-
 libavformat/rtmpproto.c |   81 +++++++++++++++++++++++++++++++++++++++++++----
 libavformat/version.h   |    2 +-
 3 files changed, 77 insertions(+), 8 deletions(-)

diff --git a/Changelog b/Changelog
index fa8cd48..f9f0f7e 100644
--- a/Changelog
+++ b/Changelog
@@ -3,7 +3,7 @@ releases are sorted from youngest to oldest.
 
 version <next>:
 - av_basename and av_dirname
-- adobe publisher authentication in RTMP
+- adobe and limelight publisher authentication in RTMP
 
 version 9_beta3:
 - ashowinfo audio filter
diff --git a/libavformat/rtmpproto.c b/libavformat/rtmpproto.c
index 1ffa748..f25a79b 100644
--- a/libavformat/rtmpproto.c
+++ b/libavformat/rtmpproto.c
@@ -1561,19 +1561,81 @@ static int do_adobe_auth(RTMPContext *rt, const char *user, const char *salt,
     return 0;
 }
 
+static int do_llnw_auth(RTMPContext *rt, const char *user, const char *nonce)
+{
+    uint8_t hash[16];
+    char hashstr1[33], hashstr2[33];
+    const char *realm = "live";
+    const char *method = "publish";
+    const char *qop = "auth";
+    const char *nc = "00000001";
+    char cnonce[10];
+    struct AVMD5 *md5 = av_md5_alloc();
+    if (!md5)
+        return AVERROR(ENOMEM);
+
+    snprintf(cnonce, sizeof(cnonce), "%08x", av_get_random_seed());
+
+    av_md5_init(md5);
+    av_md5_update(md5, user, strlen(user));
+    av_md5_update(md5, ":", 1);
+    av_md5_update(md5, realm, strlen(realm));
+    av_md5_update(md5, ":", 1);
+    av_md5_update(md5, rt->password, strlen(rt->password));
+    av_md5_final(md5, hash);
+    ff_data_to_hex(hashstr1, hash, 16, 1);
+    hashstr1[32] = '\0';
+
+    av_md5_init(md5);
+    av_md5_update(md5, method, strlen(method));
+    av_md5_update(md5, ":/", 2);
+    av_md5_update(md5, rt->app, strlen(rt->app));
+    av_md5_final(md5, hash);
+    ff_data_to_hex(hashstr2, hash, 16, 1);
+    hashstr2[32] = '\0';
+
+    av_md5_init(md5);
+    av_md5_update(md5, hashstr1, strlen(hashstr1));
+    av_md5_update(md5, ":", 1);
+    if (nonce)
+        av_md5_update(md5, nonce, strlen(nonce));
+    av_md5_update(md5, ":", 1);
+    av_md5_update(md5, nc, strlen(nc));
+    av_md5_update(md5, ":", 1);
+    av_md5_update(md5, cnonce, strlen(cnonce));
+    av_md5_update(md5, ":", 1);
+    av_md5_update(md5, qop, strlen(qop));
+    av_md5_update(md5, ":", 1);
+    av_md5_update(md5, hashstr2, strlen(hashstr2));
+    av_md5_final(md5, hash);
+    ff_data_to_hex(hashstr1, hash, 16, 1);
+
+    snprintf(rt->auth_params, sizeof(rt->auth_params),
+             "?authmod=%s&user=%s&nonce=%s&cnonce=%s&nc=%s&response=%s",
+             "llnw", user, nonce, cnonce, nc, hashstr1);
+
+    av_free(md5);
+    return 0;
+}
+
 static int handle_connect_error(URLContext *s, const char *desc)
 {
     RTMPContext *rt = s->priv_data;
-    char buf[300], *ptr;
+    char buf[300], *ptr, authmod[15];
     int i = 0, ret = 0;
     const char *user = "", *salt = "", *opaque = NULL,
-               *challenge = NULL, *cptr = NULL;
+               *challenge = NULL, *cptr = NULL, *nonce = NULL;
 
-    if (!(cptr = strstr(desc, "authmod=adobe"))) {
+    if (!(cptr = strstr(desc, "authmod=adobe")) &&
+        !(cptr = strstr(desc, "authmod=llnw"))) {
         av_log(s, AV_LOG_ERROR,
                "Unknown connect error (unsupported authentication method?)\n");
         return AVERROR_UNKNOWN;
     }
+    cptr += strlen("authmod=");
+    while (*cptr && *cptr != ' ' && i < sizeof(authmod) - 1)
+        authmod[i++] = *cptr++;
+    authmod[i] = '\0';
 
     if (!rt->username[0] || !rt->password[0]) {
         av_log(s, AV_LOG_ERROR, "No credentials set\n");
@@ -1597,7 +1659,7 @@ static int handle_connect_error(URLContext *s, const char *desc)
 
     if (strstr(desc, "code=403 need auth")) {
         snprintf(rt->auth_params, sizeof(rt->auth_params),
-                 "?authmod=%s&user=%s", "adobe", rt->username);
+                 "?authmod=%s&user=%s", authmod, rt->username);
         return 0;
     }
 
@@ -1624,12 +1686,19 @@ static int handle_connect_error(URLContext *s, const char *desc)
             opaque = value;
         } else if (!strcmp(ptr, "challenge")) {
             challenge = value;
+        } else if (!strcmp(ptr, "nonce")) {
+            nonce = value;
         }
         ptr = next;
     }
 
-    if ((ret = do_adobe_auth(rt, user, salt, challenge, opaque)) < 0)
-        return ret;
+    if (!strcmp(authmod, "adobe")) {
+        if ((ret = do_adobe_auth(rt, user, salt, challenge, opaque)) < 0)
+            return ret;
+    } else {
+        if ((ret = do_llnw_auth(rt, user, nonce)) < 0)
+            return ret;
+    }
 
     rt->auth_tried = 1;
     return 0;
diff --git a/libavformat/version.h b/libavformat/version.h
index 5130979..89af0ba 100644
--- a/libavformat/version.h
+++ b/libavformat/version.h
@@ -31,7 +31,7 @@
 
 #define LIBAVFORMAT_VERSION_MAJOR 54
 #define LIBAVFORMAT_VERSION_MINOR 20
-#define LIBAVFORMAT_VERSION_MICRO  1
+#define LIBAVFORMAT_VERSION_MICRO  2
 
 #define LIBAVFORMAT_VERSION_INT AV_VERSION_INT(LIBAVFORMAT_VERSION_MAJOR, \
                                                LIBAVFORMAT_VERSION_MINOR, \



More information about the ffmpeg-cvslog mailing list