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>
... | ... |
@@ -242,6 +242,12 @@ Name of live stream to subscribe to. By default no value will be sent. |
242 | 242 |
It is only sent if the option is specified or if rtmp_live |
243 | 243 |
is set to live. |
244 | 244 |
|
245 |
+@item rtmp_swfhash |
|
246 |
+SHA256 hash of the decompressed SWF file (32 bytes). |
|
247 |
+ |
|
248 |
+@item rtmp_swfsize |
|
249 |
+Size of the decompressed SWF file, required for SWFVerification. |
|
250 |
+ |
|
245 | 251 |
@item rtmp_swfurl |
246 | 252 |
URL of the SWF player for the media. By default no value will be sent. |
247 | 253 |
|
... | ... |
@@ -91,7 +91,11 @@ typedef struct RTMPContext { |
91 | 91 |
int nb_invokes; ///< keeps track of invoke messages |
92 | 92 |
char* tcurl; ///< url of the target stream |
93 | 93 |
char* flashver; ///< version of the flash plugin |
94 |
+ char* swfhash; ///< SHA256 hash of the decompressed SWF file (32 bytes) |
|
95 |
+ int swfhash_len; ///< length of the SHA256 hash |
|
96 |
+ int swfsize; ///< size of the decompressed SWF file |
|
94 | 97 |
char* swfurl; ///< url of the swf player |
98 |
+ char swfverification[42]; ///< hash of the SWF verification |
|
95 | 99 |
char* pageurl; ///< url of the web page |
96 | 100 |
char* subscribe; ///< name of live stream to subscribe |
97 | 101 |
int server_bw; ///< server bandwidth |
... | ... |
@@ -593,6 +597,27 @@ static int gen_pong(URLContext *s, RTMPContext *rt, RTMPPacket *ppkt) |
593 | 593 |
} |
594 | 594 |
|
595 | 595 |
/** |
596 |
+ * Generate SWF verification message and send it to the server. |
|
597 |
+ */ |
|
598 |
+static int gen_swf_verification(URLContext *s, RTMPContext *rt) |
|
599 |
+{ |
|
600 |
+ RTMPPacket pkt; |
|
601 |
+ uint8_t *p; |
|
602 |
+ int ret; |
|
603 |
+ |
|
604 |
+ av_log(s, AV_LOG_DEBUG, "Sending SWF verification...\n"); |
|
605 |
+ if ((ret = ff_rtmp_packet_create(&pkt, RTMP_NETWORK_CHANNEL, RTMP_PT_PING, |
|
606 |
+ 0, 44)) < 0) |
|
607 |
+ return ret; |
|
608 |
+ |
|
609 |
+ p = pkt.data; |
|
610 |
+ bytestream_put_be16(&p, 27); |
|
611 |
+ memcpy(p, rt->swfverification, 42); |
|
612 |
+ |
|
613 |
+ return rtmp_send_packet(rt, &pkt, 0); |
|
614 |
+} |
|
615 |
+ |
|
616 |
+/** |
|
596 | 617 |
* Generate server bandwidth message and send it to the server. |
597 | 618 |
*/ |
598 | 619 |
static int gen_server_bw(URLContext *s, RTMPContext *rt) |
... | ... |
@@ -776,6 +801,30 @@ static int rtmp_validate_digest(uint8_t *buf, int off) |
776 | 776 |
return 0; |
777 | 777 |
} |
778 | 778 |
|
779 |
+static int rtmp_calc_swf_verification(URLContext *s, RTMPContext *rt, |
|
780 |
+ uint8_t *buf) |
|
781 |
+{ |
|
782 |
+ uint8_t *p; |
|
783 |
+ int ret; |
|
784 |
+ |
|
785 |
+ if (rt->swfhash_len != 32) { |
|
786 |
+ av_log(s, AV_LOG_ERROR, |
|
787 |
+ "Hash of the decompressed SWF file is not 32 bytes long.\n"); |
|
788 |
+ return AVERROR(EINVAL); |
|
789 |
+ } |
|
790 |
+ |
|
791 |
+ p = &rt->swfverification[0]; |
|
792 |
+ bytestream_put_byte(&p, 1); |
|
793 |
+ bytestream_put_byte(&p, 1); |
|
794 |
+ bytestream_put_be32(&p, rt->swfsize); |
|
795 |
+ bytestream_put_be32(&p, rt->swfsize); |
|
796 |
+ |
|
797 |
+ if ((ret = ff_rtmp_calc_digest(rt->swfhash, 32, 0, buf, 32, p)) < 0) |
|
798 |
+ return ret; |
|
799 |
+ |
|
800 |
+ return 0; |
|
801 |
+} |
|
802 |
+ |
|
779 | 803 |
/** |
780 | 804 |
* Perform handshake with the server by means of exchanging pseudorandom data |
781 | 805 |
* signed with HMAC-SHA2 digest. |
... | ... |
@@ -866,6 +915,14 @@ static int rtmp_handshake(URLContext *s, RTMPContext *rt) |
866 | 866 |
} |
867 | 867 |
} |
868 | 868 |
|
869 |
+ /* Generate SWFVerification token (SHA256 HMAC hash of decompressed SWF, |
|
870 |
+ * key are the last 32 bytes of the server handshake. */ |
|
871 |
+ if (rt->swfsize) { |
|
872 |
+ if ((ret = rtmp_calc_swf_verification(s, rt, serverdata + 1 + |
|
873 |
+ RTMP_HANDSHAKE_PACKET_SIZE - 32)) < 0) |
|
874 |
+ return ret; |
|
875 |
+ } |
|
876 |
+ |
|
869 | 877 |
ret = ff_rtmp_calc_digest(tosend + 1 + client_pos, 32, 0, |
870 | 878 |
rtmp_server_key, sizeof(rtmp_server_key), |
871 | 879 |
digest); |
... | ... |
@@ -1001,6 +1058,13 @@ static int handle_ping(URLContext *s, RTMPPacket *pkt) |
1001 | 1001 |
if (t == 6) { |
1002 | 1002 |
if ((ret = gen_pong(s, rt, pkt)) < 0) |
1003 | 1003 |
return ret; |
1004 |
+ } else if (t == 26) { |
|
1005 |
+ if (rt->swfsize) { |
|
1006 |
+ if ((ret = gen_swf_verification(s, rt)) < 0) |
|
1007 |
+ return ret; |
|
1008 |
+ } else { |
|
1009 |
+ av_log(s, AV_LOG_WARNING, "Ignoring SWFVerification request.\n"); |
|
1010 |
+ } |
|
1004 | 1011 |
} |
1005 | 1012 |
|
1006 | 1013 |
return 0; |
... | ... |
@@ -1717,6 +1781,8 @@ static const AVOption rtmp_options[] = { |
1717 | 1717 |
{"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}, |
1718 | 1718 |
{"rtmp_playpath", "Stream identifier to play or to publish", OFFSET(playpath), AV_OPT_TYPE_STRING, {.str = NULL }, 0, 0, DEC|ENC}, |
1719 | 1719 |
{"rtmp_subscribe", "Name of live stream to subscribe to. Defaults to rtmp_playpath.", OFFSET(subscribe), AV_OPT_TYPE_STRING, {.str = NULL }, 0, 0, DEC}, |
1720 |
+ {"rtmp_swfhash", "SHA256 hash of the decompressed SWF file (32 bytes).", OFFSET(swfhash), AV_OPT_TYPE_BINARY, .flags = DEC}, |
|
1721 |
+ {"rtmp_swfsize", "Size of the decompressed SWF file, required for SWFVerification.", OFFSET(swfsize), AV_OPT_TYPE_INT, {0}, 0, INT_MAX, DEC}, |
|
1720 | 1722 |
{"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}, |
1721 | 1723 |
{"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}, |
1722 | 1724 |
{ NULL }, |
... | ... |
@@ -31,7 +31,7 @@ |
31 | 31 |
|
32 | 32 |
#define LIBAVFORMAT_VERSION_MAJOR 54 |
33 | 33 |
#define LIBAVFORMAT_VERSION_MINOR 13 |
34 |
-#define LIBAVFORMAT_VERSION_MICRO 2 |
|
34 |
+#define LIBAVFORMAT_VERSION_MICRO 3 |
|
35 | 35 |
|
36 | 36 |
#define LIBAVFORMAT_VERSION_INT AV_VERSION_INT(LIBAVFORMAT_VERSION_MAJOR, \ |
37 | 37 |
LIBAVFORMAT_VERSION_MINOR, \ |