[rtmpdump] [PATCH] Add adobe authentication support

HONGLY drquake at sina.com
Mon Mar 19 08:10:51 CET 2012


Thank all for making the patch.

Just a problem. It doesn't work when publishing to wowza server with 
authentication.

Also I think adobe authentication is crap. If flashver not set, it just let 
any publishing pass without authentication.


--------------------------------------------------
From: "Martin Storsjo" <martin at martin.st>
Sent: Friday, March 09, 2012 17:00
To: <rtmpdump at mplayerhq.hu>
Subject: [rtmpdump] [PATCH] Add adobe authentication support

> From: Sergiy <piratfm at gmail.com>
>
> To use this, add flashver=FMLE/3.0\20(compatible;\20FMSc/1.0)
> and pubUser=<username> pubUser=<password> to the RTMP url.
>
> This should both work for generic adobe authentication, and for
> publishing to akamai.
> ---
> The last few modifications are untested - if you have access to
> publishing to akamai or some other server with adobe authentication,
> please try it (with all three codepaths, openssl, gnutls and polarssl)
> to make sure I haven't messed things up while cleaning up the patch.
>
> Compared to the original version by Sergiy, this doesn't use
> gnutls-openssl (which is GPL), and has fixed some memory leaks and
> tweaked the auth scheme to work with Akamai (when I had access to it
> in June last year).
>
> librtmp/rtmp.c |  331 
> +++++++++++++++++++++++++++++++++++++++++++++++++++++++-
> librtmp/rtmp.h |    9 ++
> 2 files changed, 337 insertions(+), 3 deletions(-)
>
> diff --git a/librtmp/rtmp.c b/librtmp/rtmp.c
> index 52d0254..9578404 100644
> --- a/librtmp/rtmp.c
> +++ b/librtmp/rtmp.c
> @@ -27,6 +27,7 @@
> #include <stdlib.h>
> #include <string.h>
> #include <assert.h>
> +#include <time.h>
>
> #include "rtmp_sys.h"
> #include "log.h"
> @@ -34,11 +35,20 @@
> #ifdef CRYPTO
> #ifdef USE_POLARSSL
> #include <polarssl/havege.h>
> +#include <polarssl/md5.h>
> +#include <polarssl/base64.h>
> +#define MD5_DIGEST_LENGTH 16
> #elif defined(USE_GNUTLS)
> #include <gnutls/gnutls.h>
> +#define MD5_DIGEST_LENGTH 16
> +#include <nettle/base64.h>
> +#include <nettle/md5.h>
> #else /* USE_OPENSSL */
> #include <openssl/ssl.h>
> #include <openssl/rc4.h>
> +#include <openssl/md5.h>
> +#include <openssl/bio.h>
> +#include <openssl/buffer.h>
> #endif
> TLS_CTX RTMP_TLS_ctx;
> #endif
> @@ -499,6 +509,10 @@ static struct urlopt {
>   "Buffer time in milliseconds" },
>   { AVC("timeout"),   OFF(Link.timeout),       OPT_INT, 0,
>   "Session timeout in seconds" },
> +  { AVC("pubUser"),   OFF(Link.pubUser),       OPT_STR, 0,
> +        "Publisher username" },
> +  { AVC("pubPasswd"), OFF(Link.pubPasswd),     OPT_STR, 0,
> +        "Publisher password" },
>   { {NULL,0}, 0, 0}
> };
>
> @@ -2305,6 +2319,239 @@ AV_clear(RTMP_METHOD *vals, int num)
>   free(vals);
> }
>
> +
> +#ifdef CRYPTO
> +static int
> +b64enc(const unsigned char *input, int length, char *output, int maxsize)
> +{
> +#ifdef USE_POLARSSL
> +  int buf_size = maxsize;
> +  if(base64_encode((unsigned char *) output, &buf_size, input, length) == 
> 0)
> +    {
> +      output[buf_size] = '\0';
> +      return 1;
> +    }
> +  else
> +    {
> +      RTMP_Log(RTMP_LOGDEBUG, "%s, error", __FUNCTION__);
> +      return 0;
> +    }
> +#elif defined(USE_GNUTLS)
> +  if (BASE64_ENCODE_RAW_LENGTH(length) <= maxsize)
> +    base64_encode_raw((uint8_t*) output, length, input);
> +  else
> +    {
> +      RTMP_Log(RTMP_LOGDEBUG, "%s, error", __FUNCTION__);
> +      return 0;
> +    }
> +#else   /* USE_OPENSSL */
> +  BIO *bmem, *b64;
> +  BUF_MEM *bptr;
> +
> +  b64 = BIO_new(BIO_f_base64());
> +  bmem = BIO_new(BIO_s_mem());
> +  b64 = BIO_push(b64, bmem);
> +  BIO_write(b64, input, length);
> +  if (BIO_flush(b64) == 1)
> +    {
> +      BIO_get_mem_ptr(b64, &bptr);
> +      memcpy(output, bptr->data, bptr->length-1);
> +      output[bptr->length-1] = '\0';
> +    }
> +  else
> +    {
> +      RTMP_Log(RTMP_LOGDEBUG, "%s, error", __FUNCTION__);
> +      return 0;
> +    }
> +  BIO_free_all(b64);
> +#endif
> +  return 1;
> +}
> +
> +#ifdef USE_POLARSSL
> +#define md5sum(x,y,z)  md5(x,y,z);
> +#elif defined(USE_GNUTLS)
> +static void md5sum(const unsigned char *data, int len, unsigned char 
> *out)
> +{
> +    struct md5_ctx ctx;
> +    md5_init(&ctx);
> +    md5_update(&ctx, len, data);
> +    md5_digest(&ctx, MD5_DIGEST_LENGTH, out);
> +}
> +#else
> +#define md5sum(x,y,z)  MD5(x,y,z);
> +#endif
> +
> +static const AVal av_authmod_adobe = AVC("authmod=adobe");
> +
> +static int
> +PublisherAuth(RTMP *r, AVal *description)
> +{
> +  char *token_in = NULL;
> +  char *ptr;
> +  unsigned char md5sum_val[MD5_DIGEST_LENGTH+1];
> +  int challenge2_data;
> +#define RESPONSE_LEN 32
> +#define CHALLENGE2_LEN 16
> +#define SALTED2_LEN (32+8+8+8)
> +  char response[RESPONSE_LEN];
> +  char challenge2[CHALLENGE2_LEN];
> +  char salted2[SALTED2_LEN];
> +  AVal pubToken;
> +
> +  if (strstr(description->av_val, av_authmod_adobe.av_val) != NULL)
> +    {
> +      if(strstr(description->av_val, "code=403 need auth") != NULL)
> +        {
> +            if (strstr(r->Link.app.av_val, av_authmod_adobe.av_val) != 
> NULL) {
> +              RTMP_Log(RTMP_LOGERROR, "%s, wrong pubUser & pubPasswd for 
> publisher auth", __FUNCTION__);
> +              r->Link.pFlags |= RTMP_PUB_CLEAN;
> +              return 0;
> +            } else if(r->Link.pubUser.av_len && r->Link.pubPasswd.av_len) 
> {
> +              pubToken.av_val = malloc(r->Link.pubUser.av_len + 
> av_authmod_adobe.av_len + 8);
> +              pubToken.av_len = sprintf(pubToken.av_val, "?%s&user=%s",
> +                      av_authmod_adobe.av_val,
> +                      r->Link.pubUser.av_val);
> +              RTMP_Log(RTMP_LOGDEBUG, "%s, pubToken1: %s", __FUNCTION__, 
> pubToken.av_val);
> +              r->Link.pFlags |= RTMP_PUB_NAME;
> +            } else {
> +              RTMP_Log(RTMP_LOGERROR, "%s, need to set pubUser & 
> pubPasswd for publisher auth", __FUNCTION__);
> +              r->Link.pFlags |= RTMP_PUB_CLEAN;
> +              return 0;
> +            }
> +        }
> +      else if((token_in = strstr(description->av_val, 
> "?reason=needauth")) != NULL)
> +        {
> +          char *par, *val = NULL, *orig_ptr;
> +          char *user = NULL;
> +          char *salt = NULL;
> +          char *opaque = NULL;
> +          char *challenge = NULL;
> +          char *salted1;
> +
> +          ptr = orig_ptr = strdup(token_in);
> +          while (ptr)
> +            {
> +              par = ptr;
> +              ptr = strchr(par, '&');
> +              if(ptr)
> +                  *ptr++ = '\0';
> +
> +              val =  strchr(par, '=');
> +              if(val)
> +                  *val++ = '\0';
> +
> +              if (strcmp(par, "user") == 0){
> +                  user = val;
> +              } else if (strcmp(par, "salt") == 0){
> +                  salt = val;
> +              } else if (strcmp(par, "opaque") == 0){
> +                  opaque = val;
> +              } else if (strcmp(par, "challenge") == 0){
> +                  challenge = val;
> +              }
> +
> +              RTMP_Log(RTMP_LOGDEBUG, "%s, par:\"%s\" = val:\"%s\"", 
> __FUNCTION__, par, val);
> +            }
> +
> +            /* hash1 = base64enc(md5(user + _aodbeAuthSalt + password)) 
> */
> +            salted1 = 
> malloc(strlen(user)+strlen(salt)+r->Link.pubPasswd.av_len+1);
> +            strcpy(salted1, user);
> +            strcat(salted1, salt);
> +            strcat(salted1, r->Link.pubPasswd.av_val);
> +            md5sum((unsigned char*) salted1, strlen(salted1), 
> md5sum_val);
> +            RTMP_Log(RTMP_LOGDEBUG, "%s, md5(%s) =>", __FUNCTION__, 
> salted1);
> +            free(salted1);
> +
> +            RTMP_LogHexString(RTMP_LOGDEBUG, md5sum_val, 
> MD5_DIGEST_LENGTH);
> +
> +            b64enc(md5sum_val, MD5_DIGEST_LENGTH, salted2, SALTED2_LEN);
> +            RTMP_Log(RTMP_LOGDEBUG, "%s, b64(md5_1) = %s", __FUNCTION__, 
> salted2);
> +
> +            srand( time(NULL) );
> +            challenge2_data = rand();
> +
> +            b64enc((unsigned char *) &challenge2_data, sizeof(int), 
> challenge2, CHALLENGE2_LEN);
> +            RTMP_Log(RTMP_LOGDEBUG, "%s, b64(%d) = %s", __FUNCTION__, 
> challenge2_data, challenge2);
> +
> +            /* response = base64enc(md5(hash1 + opaque + challenge2)) */
> +            if (opaque)
> +                strcat(salted2, opaque);
> +            if (challenge)
> +                strcat(salted2, challenge);
> +            strcat(salted2, challenge2);
> +
> +            md5sum((unsigned char*) salted2, strlen(salted2), 
> md5sum_val);
> +            RTMP_Log(RTMP_LOGDEBUG, "%s, md5(%s) =>", __FUNCTION__, 
> salted2);
> +            RTMP_LogHexString(RTMP_LOGDEBUG, md5sum_val, 
> MD5_DIGEST_LENGTH);
> +
> +            b64enc(md5sum_val, MD5_DIGEST_LENGTH, response, 
> RESPONSE_LEN);
> +            RTMP_Log(RTMP_LOGDEBUG, "%s, b64(md5_2) = %s", __FUNCTION__, 
> response);
> +
> +            /* have all hashes, create auth token for the end of app */
> +            pubToken.av_val = malloc(32 + strlen(challenge2) + 
> strlen(response) + (opaque ? strlen(opaque) : 0));
> +            pubToken.av_len = sprintf(pubToken.av_val,
> +                    "&challenge=%s&response=%s&opaque=%s",
> +                    challenge2,
> +                    response,
> +                    opaque ? opaque : "");
> +            RTMP_Log(RTMP_LOGDEBUG, "%s, pubToken2: %s", __FUNCTION__, 
> pubToken.av_val);
> +            free(orig_ptr);
> +            r->Link.pFlags |= RTMP_PUB_RESP|RTMP_PUB_CLATE;
> +        }
> +      else if(strstr(description->av_val, "?reason=authfailed") != NULL)
> +        {
> +          RTMP_Log(RTMP_LOGERROR, "%s, Authentication failed: wrong 
> password", __FUNCTION__);
> +          r->Link.pFlags |= RTMP_PUB_CLEAN;
> +          return 0;
> +        }
> +      else if(strstr(description->av_val, "?reason=nosuchuser") != NULL)
> +        {
> +          RTMP_Log(RTMP_LOGERROR, "%s, Authentication failed: no such 
> user", __FUNCTION__);
> +          r->Link.pFlags |= RTMP_PUB_CLEAN;
> +          return 0;
> +        }
> +      else
> +        {
> +          RTMP_Log(RTMP_LOGERROR, "%s, Authentication failed: unknown 
> auth mode: %s",
> +                  __FUNCTION__, description->av_val);
> +          r->Link.pFlags |= RTMP_PUB_CLEAN;
> +          return 0;
> +        }
> +
> +      ptr = malloc(r->Link.app.av_len + pubToken.av_len);
> +      strncpy(ptr, r->Link.app.av_val, r->Link.app.av_len);
> +      strncpy(ptr + r->Link.app.av_len, pubToken.av_val, 
> pubToken.av_len);
> +      r->Link.app.av_len += pubToken.av_len;
> +      if(r->Link.pFlags & RTMP_PUB_ALLOC)
> +          free(r->Link.app.av_val);
> +      r->Link.app.av_val = ptr;
> +
> +      ptr = malloc(r->Link.tcUrl.av_len + pubToken.av_len);
> +      strncpy(ptr, r->Link.tcUrl.av_val, r->Link.tcUrl.av_len);
> +      strncpy(ptr + r->Link.tcUrl.av_len, pubToken.av_val, 
> pubToken.av_len);
> +      r->Link.tcUrl.av_len += pubToken.av_len;
> +      if(r->Link.pFlags & RTMP_PUB_ALLOC)
> +          free(r->Link.tcUrl.av_val);
> +      r->Link.tcUrl.av_val = ptr;
> +
> +      free(pubToken.av_val);
> +      r->Link.pFlags |= RTMP_PUB_ALLOC;
> +
> +      RTMP_Log(RTMP_LOGDEBUG, "%s, new app: %.*s tcUrl: %.*s playpath: 
> %s", __FUNCTION__,
> +              r->Link.app.av_len, r->Link.app.av_val,
> +              r->Link.tcUrl.av_len, r->Link.tcUrl.av_val,
> +              r->Link.playpath.av_val);
> +    }
> +  else
> +    {
> +      return 0;
> +    }
> +  return 1;
> +}
> +#endif
> +
> +
> SAVC(onBWDone);
> SAVC(onFCSubscribe);
> SAVC(onFCUnsubscribe);
> @@ -2314,6 +2561,7 @@ SAVC(_error);
> SAVC(close);
> SAVC(code);
> SAVC(level);
> +SAVC(description);
> SAVC(onStatus);
> SAVC(playlist_ready);
> static const AVal av_NetStream_Failed = AVC("NetStream.Failed");
> @@ -2332,6 +2580,8 @@ AVC("NetStream.Play.PublishNotify");
> static const AVal av_NetStream_Play_UnpublishNotify =
> AVC("NetStream.Play.UnpublishNotify");
> static const AVal av_NetStream_Publish_Start = 
> AVC("NetStream.Publish.Start");
> +static const AVal av_NetConnection_Connect_Rejected =
> +AVC("NetConnection.Connect.Rejected");
>
> /* Returns 0 for OK/Failed/error, 1 for 'Stop or Complete' */
> static int
> @@ -2473,12 +2723,73 @@ HandleInvoke(RTMP *r, const char *body, unsigned 
> int nBodySize)
>     }
>   else if (AVMATCH(&method, &av__error))
>     {
> +#ifdef CRYPTO
> +      AVal methodInvoked = {0};
> +      int i;
> +
> +      if (r->Link.protocol & RTMP_FEATURE_WRITE)
> +        {
> +          for (i=0; i<r->m_numCalls; i++)
> +            {
> +              if (r->m_methodCalls[i].num == txn)
> +                {
> +                  methodInvoked = r->m_methodCalls[i].name;
> +                  AV_erase(r->m_methodCalls, &r->m_numCalls, i, FALSE);
> +                  break;
> +                }
> +            }
> +          if (!methodInvoked.av_val)
> +            {
> +              RTMP_Log(RTMP_LOGDEBUG, "%s, received result id %f without 
> matching request",
> +                    __FUNCTION__, txn);
> +              goto leave;
> +            }
> +
> +          RTMP_Log(RTMP_LOGDEBUG, "%s, received error for method call 
> <%s>", __FUNCTION__,
> +          methodInvoked.av_val);
> +
> +          if (AVMATCH(&methodInvoked, &av_connect))
> +            {
> +              AMFObject obj2;
> +              AVal code, level, description;
> +              AMFProp_GetObject(AMF_GetProp(&obj, NULL, 3), &obj2);
> +              AMFProp_GetString(AMF_GetProp(&obj2, &av_code, -1), &code);
> +              AMFProp_GetString(AMF_GetProp(&obj2, &av_level, -1), 
> &level);
> +              AMFProp_GetString(AMF_GetProp(&obj2, &av_description, -1), 
> &description);
> +              RTMP_Log(RTMP_LOGDEBUG, "%s, error description: %s", 
> __FUNCTION__, description.av_val);
> +              /* if PublisherAuth returns 1, then reconnect */
> +              PublisherAuth(r, &description);
> +            }
> +        }
> +      else
> +        {
> +          RTMP_Log(RTMP_LOGERROR, "rtmp server sent error");
> +        }
> +      free(methodInvoked.av_val);
> +#else
>       RTMP_Log(RTMP_LOGERROR, "rtmp server sent error");
> +#endif
>     }
>   else if (AVMATCH(&method, &av_close))
>     {
>       RTMP_Log(RTMP_LOGERROR, "rtmp server requested close");
>       RTMP_Close(r);
> +#ifdef CRYPTO
> +      if ((r->Link.protocol & RTMP_FEATURE_WRITE) &&
> +              !(r->Link.pFlags & RTMP_PUB_CLEAN) &&
> +              (  !(r->Link.pFlags & RTMP_PUB_NAME) ||
> +                 !(r->Link.pFlags & RTMP_PUB_RESP) ||
> +                 (r->Link.pFlags & RTMP_PUB_CLATE) ) )
> +        {
> +          /* clean later */
> +          if(r->Link.pFlags & RTMP_PUB_CLATE)
> +              r->Link.pFlags |= RTMP_PUB_CLEAN;
> +          RTMP_Log(RTMP_LOGERROR, "authenticating publisher");
> +
> +          if (!RTMP_Connect(r, NULL) || !RTMP_ConnectStream(r, 0))
> +              goto leave;
> +       }
> +#endif
>     }
>   else if (AVMATCH(&method, &av_onStatus))
>     {
> @@ -3519,9 +3830,6 @@ RTMP_Close(RTMP *r)
>   r->m_resplen = 0;
>   r->m_unackd = 0;
>
> -  free(r->Link.playpath0.av_val);
> -  r->Link.playpath0.av_val = NULL;
> -
>   if (r->Link.lFlags & RTMP_LF_FTCU)
>     {
>       free(r->Link.tcUrl.av_val);
> @@ -3530,6 +3838,20 @@ RTMP_Close(RTMP *r)
>     }
>
> #ifdef CRYPTO
> +  if (!(r->Link.protocol & RTMP_FEATURE_WRITE) || (r->Link.pFlags & 
> RTMP_PUB_CLEAN))
> +    {
> +      free(r->Link.playpath0.av_val);
> +      r->Link.playpath0.av_val = NULL;
> +    }
> +  if ((r->Link.protocol & RTMP_FEATURE_WRITE) &&
> +      (r->Link.pFlags & RTMP_PUB_CLEAN) &&
> +      (r->Link.pFlags & RTMP_PUB_ALLOC))
> +    {
> +      free(r->Link.app.av_val);
> +      r->Link.app.av_val = NULL;
> +      free(r->Link.tcUrl.av_val);
> +      r->Link.tcUrl.av_val = NULL;
> +    }
>   if (r->Link.dh)
>     {
>       MDH_free(r->Link.dh);
> @@ -3545,6 +3867,9 @@ RTMP_Close(RTMP *r)
>       RC4_free(r->Link.rc4keyOut);
>       r->Link.rc4keyOut = NULL;
>     }
> +#else
> +  free(r->Link.playpath0.av_val);
> +  r->Link.playpath0.av_val = NULL;
> #endif
> }
>
> diff --git a/librtmp/rtmp.h b/librtmp/rtmp.h
> index 6b2ae5b..fcf3699 100644
> --- a/librtmp/rtmp.h
> +++ b/librtmp/rtmp.h
> @@ -157,6 +157,8 @@ extern "C"
>     AVal subscribepath;
>     AVal usherToken;
>     AVal token;
> +    AVal pubUser;
> +    AVal pubPasswd;
>     AMFObject extras;
>     int edepth;
>
> @@ -176,6 +178,13 @@ extern "C"
>     int protocol;
>     int timeout; /* connection timeout in seconds */
>
> +#define RTMP_PUB_NAME   0x0001  /* send login to server */
> +#define RTMP_PUB_RESP   0x0002  /* send salted password hash */
> +#define RTMP_PUB_ALLOC  0x0004  /* allocated data for new tcUrl & app */
> +#define RTMP_PUB_CLEAN  0x0008  /* need to free allocated data for newer 
> tcUrl & app at exit */
> +#define RTMP_PUB_CLATE  0x0010  /* late clean tcUrl & app at exit */
> +    int pFlags;
> +
>     unsigned short socksport;
>     unsigned short port;
>
> -- 
> 1.7.3.1
>
> _______________________________________________
> rtmpdump mailing list
> rtmpdump at mplayerhq.hu
> https://lists.mplayerhq.hu/mailman/listinfo/rtmpdump
> 


More information about the rtmpdump mailing list