rtmp: Add support for SWFVerification

Specifies how the server verifies client SWF files before allowing the
files to connect to an application. Verifying SWF files is a security
measure that prevents someone from creating their own SWF files that can
attempt to stream your resources.

Signed-off-by: Martin Storsjö <martin@martin.st>
This commit is contained in:
Samuel Pitoiset 2012-08-13 17:05:00 +02:00 committed by Martin Storsjö
parent 661454aa28
commit 635ac8e1be
3 changed files with 73 additions and 1 deletions

View File

@ -242,6 +242,12 @@ Name of live stream to subscribe to. By default no value will be sent.
It is only sent if the option is specified or if rtmp_live It is only sent if the option is specified or if rtmp_live
is set to live. is set to live.
@item rtmp_swfhash
SHA256 hash of the decompressed SWF file (32 bytes).
@item rtmp_swfsize
Size of the decompressed SWF file, required for SWFVerification.
@item rtmp_swfurl @item rtmp_swfurl
URL of the SWF player for the media. By default no value will be sent. URL of the SWF player for the media. By default no value will be sent.

View File

@ -91,7 +91,11 @@ typedef struct RTMPContext {
int nb_invokes; ///< keeps track of invoke messages int nb_invokes; ///< keeps track of invoke messages
char* tcurl; ///< url of the target stream char* tcurl; ///< url of the target stream
char* flashver; ///< version of the flash plugin char* flashver; ///< version of the flash plugin
char* swfhash; ///< SHA256 hash of the decompressed SWF file (32 bytes)
int swfhash_len; ///< length of the SHA256 hash
int swfsize; ///< size of the decompressed SWF file
char* swfurl; ///< url of the swf player char* swfurl; ///< url of the swf player
char swfverification[42]; ///< hash of the SWF verification
char* pageurl; ///< url of the web page char* pageurl; ///< url of the web page
char* subscribe; ///< name of live stream to subscribe char* subscribe; ///< name of live stream to subscribe
int server_bw; ///< server bandwidth int server_bw; ///< server bandwidth
@ -592,6 +596,27 @@ static int gen_pong(URLContext *s, RTMPContext *rt, RTMPPacket *ppkt)
return rtmp_send_packet(rt, &pkt, 0); return rtmp_send_packet(rt, &pkt, 0);
} }
/**
* Generate SWF verification message and send it to the server.
*/
static int gen_swf_verification(URLContext *s, RTMPContext *rt)
{
RTMPPacket pkt;
uint8_t *p;
int ret;
av_log(s, AV_LOG_DEBUG, "Sending SWF verification...\n");
if ((ret = ff_rtmp_packet_create(&pkt, RTMP_NETWORK_CHANNEL, RTMP_PT_PING,
0, 44)) < 0)
return ret;
p = pkt.data;
bytestream_put_be16(&p, 27);
memcpy(p, rt->swfverification, 42);
return rtmp_send_packet(rt, &pkt, 0);
}
/** /**
* Generate server bandwidth message and send it to the server. * Generate server bandwidth message and send it to the server.
*/ */
@ -776,6 +801,30 @@ static int rtmp_validate_digest(uint8_t *buf, int off)
return 0; return 0;
} }
static int rtmp_calc_swf_verification(URLContext *s, RTMPContext *rt,
uint8_t *buf)
{
uint8_t *p;
int ret;
if (rt->swfhash_len != 32) {
av_log(s, AV_LOG_ERROR,
"Hash of the decompressed SWF file is not 32 bytes long.\n");
return AVERROR(EINVAL);
}
p = &rt->swfverification[0];
bytestream_put_byte(&p, 1);
bytestream_put_byte(&p, 1);
bytestream_put_be32(&p, rt->swfsize);
bytestream_put_be32(&p, rt->swfsize);
if ((ret = ff_rtmp_calc_digest(rt->swfhash, 32, 0, buf, 32, p)) < 0)
return ret;
return 0;
}
/** /**
* Perform handshake with the server by means of exchanging pseudorandom data * Perform handshake with the server by means of exchanging pseudorandom data
* signed with HMAC-SHA2 digest. * signed with HMAC-SHA2 digest.
@ -866,6 +915,14 @@ static int rtmp_handshake(URLContext *s, RTMPContext *rt)
} }
} }
/* Generate SWFVerification token (SHA256 HMAC hash of decompressed SWF,
* key are the last 32 bytes of the server handshake. */
if (rt->swfsize) {
if ((ret = rtmp_calc_swf_verification(s, rt, serverdata + 1 +
RTMP_HANDSHAKE_PACKET_SIZE - 32)) < 0)
return ret;
}
ret = ff_rtmp_calc_digest(tosend + 1 + client_pos, 32, 0, ret = ff_rtmp_calc_digest(tosend + 1 + client_pos, 32, 0,
rtmp_server_key, sizeof(rtmp_server_key), rtmp_server_key, sizeof(rtmp_server_key),
digest); digest);
@ -1001,6 +1058,13 @@ static int handle_ping(URLContext *s, RTMPPacket *pkt)
if (t == 6) { if (t == 6) {
if ((ret = gen_pong(s, rt, pkt)) < 0) if ((ret = gen_pong(s, rt, pkt)) < 0)
return ret; return ret;
} else if (t == 26) {
if (rt->swfsize) {
if ((ret = gen_swf_verification(s, rt)) < 0)
return ret;
} else {
av_log(s, AV_LOG_WARNING, "Ignoring SWFVerification request.\n");
}
} }
return 0; return 0;
@ -1717,6 +1781,8 @@ static const AVOption rtmp_options[] = {
{"rtmp_pageurl", "URL of the web page in which the media was embedded. By default no value will be sent.", OFFSET(pageurl), AV_OPT_TYPE_STRING, {.str = NULL }, 0, 0, DEC}, {"rtmp_pageurl", "URL of the web page in which the media was embedded. By default no value will be sent.", OFFSET(pageurl), AV_OPT_TYPE_STRING, {.str = NULL }, 0, 0, DEC},
{"rtmp_playpath", "Stream identifier to play or to publish", OFFSET(playpath), AV_OPT_TYPE_STRING, {.str = NULL }, 0, 0, DEC|ENC}, {"rtmp_playpath", "Stream identifier to play or to publish", OFFSET(playpath), AV_OPT_TYPE_STRING, {.str = NULL }, 0, 0, DEC|ENC},
{"rtmp_subscribe", "Name of live stream to subscribe to. Defaults to rtmp_playpath.", OFFSET(subscribe), AV_OPT_TYPE_STRING, {.str = NULL }, 0, 0, DEC}, {"rtmp_subscribe", "Name of live stream to subscribe to. Defaults to rtmp_playpath.", OFFSET(subscribe), AV_OPT_TYPE_STRING, {.str = NULL }, 0, 0, DEC},
{"rtmp_swfhash", "SHA256 hash of the decompressed SWF file (32 bytes).", OFFSET(swfhash), AV_OPT_TYPE_BINARY, .flags = DEC},
{"rtmp_swfsize", "Size of the decompressed SWF file, required for SWFVerification.", OFFSET(swfsize), AV_OPT_TYPE_INT, {0}, 0, INT_MAX, DEC},
{"rtmp_swfurl", "URL of the SWF player. By default no value will be sent", OFFSET(swfurl), AV_OPT_TYPE_STRING, {.str = NULL }, 0, 0, DEC|ENC}, {"rtmp_swfurl", "URL of the SWF player. By default no value will be sent", OFFSET(swfurl), AV_OPT_TYPE_STRING, {.str = NULL }, 0, 0, DEC|ENC},
{"rtmp_tcurl", "URL of the target stream. Defaults to proto://host[:port]/app.", OFFSET(tcurl), AV_OPT_TYPE_STRING, {.str = NULL }, 0, 0, DEC|ENC}, {"rtmp_tcurl", "URL of the target stream. Defaults to proto://host[:port]/app.", OFFSET(tcurl), AV_OPT_TYPE_STRING, {.str = NULL }, 0, 0, DEC|ENC},
{ NULL }, { NULL },

View File

@ -31,7 +31,7 @@
#define LIBAVFORMAT_VERSION_MAJOR 54 #define LIBAVFORMAT_VERSION_MAJOR 54
#define LIBAVFORMAT_VERSION_MINOR 13 #define LIBAVFORMAT_VERSION_MINOR 13
#define LIBAVFORMAT_VERSION_MICRO 2 #define LIBAVFORMAT_VERSION_MICRO 3
#define LIBAVFORMAT_VERSION_INT AV_VERSION_INT(LIBAVFORMAT_VERSION_MAJOR, \ #define LIBAVFORMAT_VERSION_INT AV_VERSION_INT(LIBAVFORMAT_VERSION_MAJOR, \
LIBAVFORMAT_VERSION_MINOR, \ LIBAVFORMAT_VERSION_MINOR, \