[FFmpeg-devel] libsrt: add password ACL support using new streamID specification

Submitted by Aaron Boxer on Sept. 9, 2019, 3:48 p.m.

Details

Message ID CAK8GPyDAZtMTLHb1EZCzfGiPxCb1spAA8=bxjT=oanMhBFObTA@mail.gmail.com
State New
Headers show

Commit Message

Aaron Boxer Sept. 9, 2019, 3:48 p.m.
Hello!
SRT has added a new stream ID specification allowing extraction of various
fields from the user password. This patch adds support for using an ACL to
check user credentials - base on this Haivision sample code:
https://github.com/Haivision/srt/blob/master/testing/srt-test-live.cpp

Thanks,
Aaron

Patch hide | download patch | download mbox

From 8a11e0d673872049b8260cb08f3c7cb40f9a1297 Mon Sep 17 00:00:00 2001
From: Aaron Boxer <aaron.boxer@collabora.com>
Date: Wed, 14 Aug 2019 08:26:17 -0400
Subject: [PATCH] libsrt: support streamid spec

---
 libavformat/libsrt.c | 135 +++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 135 insertions(+)

diff --git a/libavformat/libsrt.c b/libavformat/libsrt.c
index b5568089fa..d597e5fce4 100644
--- a/libavformat/libsrt.c
+++ b/libavformat/libsrt.c
@@ -34,6 +34,9 @@ 
 #include "os_support.h"
 #include "url.h"
 
+
+static srt_listen_callback_fn libsrt_listen_callback;
+
 /* This is for MPEG-TS and it's a default SRTO_PAYLOADSIZE for SRTT_LIVE (8 TS packets) */
 #ifndef SRT_LIVE_DEFAULT_PAYLOAD_SIZE
 #define SRT_LIVE_DEFAULT_PAYLOAD_SIZE 1316
@@ -62,6 +65,7 @@  typedef struct SRTContext {
     int64_t maxbw;
     int pbkeylen;
     char *passphrase;
+    char* user_passphrase_list;
     int mss;
     int ffs;
     int ipttl;
@@ -101,6 +105,7 @@  static const AVOption libsrt_options[] = {
     { "maxbw",          "Maximum bandwidth (bytes per second) that the connection can use",     OFFSET(maxbw),            AV_OPT_TYPE_INT64,    { .i64 = -1 }, -1, INT64_MAX, .flags = D|E },
     { "pbkeylen",       "Crypto key len in bytes {16,24,32} Default: 16 (128-bit)",             OFFSET(pbkeylen),         AV_OPT_TYPE_INT,      { .i64 = -1 }, -1, 32,        .flags = D|E },
     { "passphrase",     "Crypto PBKDF2 Passphrase size[0,10..64] 0:disable crypto",             OFFSET(passphrase),       AV_OPT_TYPE_STRING,   { .str = NULL },              .flags = D|E },
+    { "user_passphrase_list", "Comma separated list users and passphrases, of form usrr1=pass1,usr2=pass2,...", OFFSET(user_passphrase_list),       AV_OPT_TYPE_STRING,   { .str = NULL },              .flags = D|E },
     { "mss",            "The Maximum Segment Size",                                             OFFSET(mss),              AV_OPT_TYPE_INT,      { .i64 = -1 }, -1, 1500,      .flags = D|E },
     { "ffs",            "Flight flag size (window size) (in bytes)",                            OFFSET(ffs),              AV_OPT_TYPE_INT,      { .i64 = -1 }, -1, INT_MAX,   .flags = D|E },
     { "ipttl",          "IP Time To Live",                                                      OFFSET(ipttl),            AV_OPT_TYPE_INT,      { .i64 = -1 }, -1, 255,       .flags = D|E },
@@ -196,10 +201,134 @@  static int libsrt_network_wait_fd_timeout(URLContext *h, int eid, int fd, int wr
     }
 }
 
+typedef struct _libsrt_parsed_param {
+    char *key;
+    char *val;
+} libsrt_parsed_param;
+
+
+/**
+ * Parse a key-value string into an array of key/value structs.
+ *
+ * The key-value string should be a null terminated string of parameters separated
+ * by a delimiter. Each parameter are checked for the equal sign character.
+ * If the equal sign character appears in the parameter, it will be used as a null terminator
+ * and the part that comes after it will be the value of the parameter.
+ *
+ *
+ * param: keyvalue:     the key-value string to parse. The string will be modified.
+ * param: delimiter:    the character that separates the key/value pairs
+ *                      from each other.
+ * param: params:       an array of parsed_param structs to hold the result.
+ * param: max_params:     maximum number of parameters to parse.
+ *
+ * Return:                the number of parsed items. -1 if there was an error.
+ */
+static int libsrt_parse_key_value(char *keyvalue, const char* delimiter,
+        libsrt_parsed_param *params, int max_params)
+{
+    int i = 0;
+    char *token = NULL;
+
+    if (!keyvalue || *keyvalue == '\0')
+        return -1;
+    if (!params || max_params == 0)
+        return 0;
+
+    token = strtok( keyvalue, delimiter );
+    while (token != NULL && i < max_params) {
+        params[i].key = token;
+        params[i].val = NULL;
+        if ((params[i].val = strchr( params[i].key, '=' )) != NULL) {
+            size_t val_len = strlen( params[i].val );
+            /* make key into a zero-delimited string */
+            *(params[i].val) = '\0';
+            /* make sure val is not empty */
+            if (val_len > 1) {
+                params[i].val++;
+                /* make sure key is not empty */
+                if (params[i].key[0])
+                    i++;
+            };
+        }
+        token = strtok( NULL, delimiter );
+    }
+
+    return i;
+}
+
+/* callback to parse streamid */
+static int libsrt_listen_callback(void* opaq, SRTSOCKET ns, int hsversion, const struct sockaddr* peeraddr, const char* streamid)
+{
+    const char* username = NULL;
+    const char* expected_passphrase = NULL;
+    static const char stdhdr [] = "#!::";
+    uint32_t* pattern = (uint32_t*)stdhdr;
+    uint8_t found = 0;
+    libsrt_parsed_param key_values[256];
+    URLContext *h = (URLContext*)opaq;
+    int num_key_values;
+    int i;
+
+    if (hsversion != 5) {
+        av_log(h, AV_LOG_ERROR,"ERROR: hsversion expected 5\n");
+        return -1;
+    }
+    if (!peeraddr) {
+        av_log(h, AV_LOG_ERROR,"ERROR: null peeraddr\n");
+        return -1;
+    }
+
+    /* Try the "standard interpretation" with username at key 'u' */
+    if (strlen(streamid) > 4 && *(uint32_t*)streamid == *pattern){
+        num_key_values = libsrt_parse_key_value((char*)(streamid-4), ",", key_values, sizeof(key_values));
+        if (num_key_values == -1) {
+            av_log(h, AV_LOG_ERROR,"ERROR: null streamid \n");
+            return -1;
+        }
+        for (int i = 0; i < num_key_values; ++i){
+            if (key_values[i].key && strcmp(key_values[i].key,"u")== 0){
+                username = key_values[i].val;
+                found = 1;
+            }
+        }
+        if (!found) {
+            av_log(h, AV_LOG_ERROR,"User not found, returning false.\n");
+            return -1;
+        }
+    }
+    else {
+        /* By default the username is the whole streamid */
+        username = streamid;
+    }
+
+    num_key_values = libsrt_parse_key_value(( (SRTContext*)h->priv_data)->user_passphrase_list, ",", key_values, sizeof(key_values));
+    for (i = 0; i < num_key_values; ++i){
+        if (key_values[i].key && strcmp(key_values[i].key, username) == 0){
+            expected_passphrase = key_values[i].val;
+            break;
+        }
+    }
+    if (!expected_passphrase) {
+        av_log(h, AV_LOG_ERROR,"Cannot find password for user '%s'\n", username );
+        return -1;
+    }
+
+    av_log(h, AV_LOG_INFO,"Setting password '%s' for user '%s'\n", expected_passphrase, username );
+    if (srt_setsockflag(ns, SRTO_PASSPHRASE, expected_passphrase, strlen(expected_passphrase))){
+        av_log(h, AV_LOG_WARNING, "setsockopt(SRTO_PASSPHRASE) failed\n");
+    }
+
+    return 0;
+
+}
+
 static int libsrt_listen(int eid, int fd, const struct sockaddr *addr, socklen_t addrlen, URLContext *h, int timeout)
 {
     int ret;
     int reuse = 1;
+    SRTContext    *c   = h->priv_data;
+
     if (srt_setsockopt(fd, SOL_SOCKET, SRTO_REUSEADDR, &reuse, sizeof(reuse))) {
         av_log(h, AV_LOG_WARNING, "setsockopt(SRTO_REUSEADDR) failed\n");
     }
@@ -210,6 +339,9 @@  static int libsrt_listen(int eid, int fd, const struct sockaddr *addr, socklen_t
     ret = srt_listen(fd, 1);
     if (ret)
         return libsrt_neterrno(h);
+    /* add callback if there is a user passphrase list */
+    if (c->user_passphrase_list)
+    	(void)srt_listen_callback(fd, &libsrt_listen_callback, h);
 
     while ((ret = libsrt_network_wait_fd_timeout(h, eid, fd, 1, timeout, &h->interrupt_callback))) {
         switch (ret) {
@@ -495,6 +627,9 @@  static int libsrt_open(URLContext *h, const char *uri, int flags)
         if (av_find_info_tag(buf, sizeof(buf), "passphrase", p)) {
             s->passphrase = av_strndup(buf, strlen(buf));
         }
+        if (av_find_info_tag(buf, sizeof(buf), "user_passphrase_list", p)) {
+            s->user_passphrase_list = av_strndup(buf, strlen(buf));
+        }
         if (av_find_info_tag(buf, sizeof(buf), "mss", p)) {
             s->mss = strtol(buf, NULL, 10);
         }
-- 
2.20.1