Skip to content

Commit

Permalink
Security: Enable CIDR for allow/deny play/publish (#2914)
Browse files Browse the repository at this point in the history
* protocol/utility: add srs_ipv4_to_num

* protocol/utility: add srs_ipv4_within_mask

* protocol/utility: add srs_get_cidr_mask and CIDR_VALUES static struct

* protocol/utility: add srs_get_cidr_ipv4

* app/security: support cidr ip ranges in allow/deny rules

* conf: update security example with cidr ranges

* Security: Enable CIDR for allow/deny play/publish

* Security: Enable CIDR for allow/deny play/publish

* Security: Enable CIDR for allow/deny play/publish

* fix compile error on centos6

Co-authored-by: Matheus Macabu <macabu.matheus@gmail.com>
  • Loading branch information
Haibo Chen and macabu authored Mar 11, 2022
1 parent 451b010 commit 67ccd58
Show file tree
Hide file tree
Showing 6 changed files with 219 additions and 1 deletion.
6 changes: 5 additions & 1 deletion trunk/conf/full.conf
Original file line number Diff line number Diff line change
Expand Up @@ -967,16 +967,20 @@ vhost security.srs.com {
# default: off
enabled on;
# the security list, each item format as:
# allow|deny publish|play all|<ip>
# allow|deny publish|play all|<ip or cidr>
# for example:
# allow publish all;
# deny publish all;
# allow publish 127.0.0.1;
# deny publish 127.0.0.1;
# allow publish 10.0.0.0/8;
# deny publish 10.0.0.0/8;
# allow play all;
# deny play all;
# allow play 127.0.0.1;
# deny play 127.0.0.1;
# allow play 10.0.0.0/8;
# deny play 10.0.0.0/8;
# SRS apply the following simple strategies one by one:
# 1. allow all if security disabled.
# 2. default to deny all when security enabled.
Expand Down
18 changes: 18 additions & 0 deletions trunk/src/app/srs_app_security.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,9 @@ srs_error_t SrsSecurity::allow_check(SrsConfDirective* rules, SrsRtmpConnType ty
}
allow_rules++;

string cidr_ipv4 = srs_get_cidr_ipv4(rule->arg1());
string cidr_mask = srs_get_cidr_mask(rule->arg1());

switch (type) {
case SrsRtmpConnPlay:
case SrsRtcConnPlay:
Expand All @@ -79,6 +82,9 @@ srs_error_t SrsSecurity::allow_check(SrsConfDirective* rules, SrsRtmpConnType ty
if (rule->arg1() == "all" || rule->arg1() == ip) {
return srs_success; // OK
}
if (srs_is_ipv4(cidr_ipv4) && cidr_mask != "" && srs_ipv4_within_mask(ip, cidr_ipv4, cidr_mask)) {
return srs_success; // OK
}
break;
case SrsRtmpConnFMLEPublish:
case SrsRtmpConnFlashPublish:
Expand All @@ -90,6 +96,9 @@ srs_error_t SrsSecurity::allow_check(SrsConfDirective* rules, SrsRtmpConnType ty
if (rule->arg1() == "all" || rule->arg1() == ip) {
return srs_success; // OK
}
if (srs_is_ipv4(cidr_ipv4) && cidr_mask != "" && srs_ipv4_within_mask(ip, cidr_ipv4, cidr_mask)) {
return srs_success; // OK
}
break;
case SrsRtmpConnUnknown:
default:
Expand All @@ -111,6 +120,9 @@ srs_error_t SrsSecurity::deny_check(SrsConfDirective* rules, SrsRtmpConnType typ
if (rule->name != "deny") {
continue;
}

string cidr_ipv4 = srs_get_cidr_ipv4(rule->arg1());
string cidr_mask = srs_get_cidr_mask(rule->arg1());

switch (type) {
case SrsRtmpConnPlay:
Expand All @@ -121,6 +133,9 @@ srs_error_t SrsSecurity::deny_check(SrsConfDirective* rules, SrsRtmpConnType typ
if (rule->arg1() == "all" || rule->arg1() == ip) {
return srs_error_new(ERROR_SYSTEM_SECURITY_DENY, "deny by rule<%s>", rule->arg1().c_str());
}
if (srs_is_ipv4(cidr_ipv4) && cidr_mask != "" && srs_ipv4_within_mask(ip, cidr_ipv4, cidr_mask)) {
return srs_error_new(ERROR_SYSTEM_SECURITY_DENY, "deny by rule<%s>", rule->arg1().c_str());
}
break;
case SrsRtmpConnFMLEPublish:
case SrsRtmpConnFlashPublish:
Expand All @@ -132,6 +147,9 @@ srs_error_t SrsSecurity::deny_check(SrsConfDirective* rules, SrsRtmpConnType typ
if (rule->arg1() == "all" || rule->arg1() == ip) {
return srs_error_new(ERROR_SYSTEM_SECURITY_DENY, "deny by rule<%s>", rule->arg1().c_str());
}
if (srs_is_ipv4(cidr_ipv4) && cidr_mask != "" && srs_ipv4_within_mask(ip, cidr_ipv4, cidr_mask)) {
return srs_error_new(ERROR_SYSTEM_SECURITY_DENY, "deny by rule<%s>", rule->arg1().c_str());
}
break;
case SrsRtmpConnUnknown:
default:
Expand Down
1 change: 1 addition & 0 deletions trunk/src/app/srs_app_security.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
#include <string>

#include <srs_rtmp_stack.hpp>
#include <srs_protocol_utility.hpp>

class SrsConfDirective;

Expand Down
136 changes: 136 additions & 0 deletions trunk/src/protocol/srs_protocol_utility.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#include <unistd.h>
#endif

#include <arpa/inet.h>
#include <stdlib.h>
#include <sstream>
using namespace std;
Expand Down Expand Up @@ -397,3 +398,138 @@ bool srs_is_ipv4(string domain)
return true;
}

uint32_t srs_ipv4_to_num(string ip) {
uint32_t addr = 0;
if (inet_pton(AF_INET, ip.c_str(), &addr) <= 0) {
return 0;
}

return ntohl(addr);
}

bool srs_ipv4_within_mask(string ip, string network, string mask) {
uint32_t ip_addr = srs_ipv4_to_num(ip);
uint32_t mask_addr = srs_ipv4_to_num(mask);
uint32_t network_addr = srs_ipv4_to_num(network);

return (ip_addr & mask_addr) == (network_addr & mask_addr);
}

static struct CIDR_VALUE {
size_t length;
std::string mask;
} CIDR_VALUES[32] = {
{ 1, "128.0.0.0" },
{ 2, "192.0.0.0" },
{ 3, "224.0.0.0" },
{ 4, "240.0.0.0" },
{ 5, "248.0.0.0" },
{ 6, "252.0.0.0" },
{ 7, "254.0.0.0" },
{ 8, "255.0.0.0" },
{ 9, "255.128.0.0" },
{ 10, "255.192.0.0" },
{ 11, "255.224.0.0" },
{ 12, "255.240.0.0" },
{ 13, "255.248.0.0" },
{ 14, "255.252.0.0" },
{ 15, "255.254.0.0" },
{ 16, "255.255.0.0" },
{ 17, "255.255.128.0" },
{ 18, "255.255.192.0" },
{ 19, "255.255.224.0" },
{ 20, "255.255.240.0" },
{ 21, "255.255.248.0" },
{ 22, "255.255.252.0" },
{ 23, "255.255.254.0" },
{ 24, "255.255.255.0" },
{ 25, "255.255.255.128" },
{ 26, "255.255.255.192" },
{ 27, "255.255.255.224" },
{ 28, "255.255.255.240" },
{ 29, "255.255.255.248" },
{ 30, "255.255.255.252" },
{ 31, "255.255.255.254" },
{ 32, "255.255.255.255" },
};

string srs_get_cidr_mask(string network_address) {
string delimiter = "/";

size_t delimiter_position = network_address.find(delimiter);
if (delimiter_position == string::npos) {
// Even if it does not have "/N", it can be a valid IP, by default "/32".
if (srs_is_ipv4(network_address)) {
return CIDR_VALUES[32-1].mask;
}
return "";
}

// Change here to include IPv6 support.
string is_ipv4_address = network_address.substr(0, delimiter_position);
if (!srs_is_ipv4(is_ipv4_address)) {
return "";
}

size_t cidr_length_position = delimiter_position + delimiter.length();
if (cidr_length_position >= network_address.length()) {
return "";
}

string cidr_length = network_address.substr(cidr_length_position, network_address.length());
if (cidr_length.length() <= 0) {
return "";
}

size_t cidr_length_num = 31;
try {
cidr_length_num = atoi(cidr_length.c_str());
if (cidr_length_num <= 0) {
return "";
}
} catch (...) {
return "";
}

return CIDR_VALUES[cidr_length_num-1].mask;
}

string srs_get_cidr_ipv4(string network_address) {
string delimiter = "/";

size_t delimiter_position = network_address.find(delimiter);
if (delimiter_position == string::npos) {
// Even if it does not have "/N", it can be a valid IP, by default "/32".
if (srs_is_ipv4(network_address)) {
return network_address;
}
return "";
}

// Change here to include IPv6 support.
string ipv4_address = network_address.substr(0, delimiter_position);
if (!srs_is_ipv4(ipv4_address)) {
return "";
}

size_t cidr_length_position = delimiter_position + delimiter.length();
if (cidr_length_position >= network_address.length()) {
return "";
}

string cidr_length = network_address.substr(cidr_length_position, network_address.length());
if (cidr_length.length() <= 0) {
return "";
}

try {
size_t cidr_length_num = atoi(cidr_length.c_str());
if (cidr_length_num <= 0) {
return "";
}
} catch (...) {
return "";
}

return ipv4_address;
}
12 changes: 12 additions & 0 deletions trunk/src/protocol/srs_protocol_utility.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -112,5 +112,17 @@ std::string srs_join_vector_string(std::vector<T>& vs, std::string separator)
// Whether domain is an IPv4 address.
extern bool srs_is_ipv4(std::string domain);

// Convert an IPv4 from string to uint32_t.
extern uint32_t srs_ipv4_to_num(std::string ip);

// Whether the IPv4 is in an IP mask.
extern bool srs_ipv4_within_mask(std::string ip, std::string network, std::string mask);

// Get the CIDR (Classless Inter-Domain Routing) mask for a network address.
extern std::string srs_get_cidr_mask(std::string network_address);

// Get the CIDR (Classless Inter-Domain Routing) IPv4 for a network address.
extern std::string srs_get_cidr_ipv4(std::string network_address);

#endif

47 changes: 47 additions & 0 deletions trunk/src/utest/srs_utest_rtmp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3197,6 +3197,53 @@ VOID TEST(ProtocolRTMPTest, OthersAll)
EXPECT_FALSE(srs_is_ipv4("2.3.4.ossrs"));
}

if (true) {
EXPECT_EQ((uint32_t)0, srs_ipv4_to_num("not.a.valid.ip"));
}

if (true) {
EXPECT_EQ((uint32_t)2130706433, srs_ipv4_to_num("127.0.0.1"));
EXPECT_NE((uint32_t)16777343, srs_ipv4_to_num("127.0.0.1")); // Big-Endian
}

if (true) {
EXPECT_TRUE(srs_ipv4_within_mask("192.168.1.1", "192.168.1.0", "255.255.255.0"));
EXPECT_TRUE(srs_ipv4_within_mask("220.1.1.22", "220.1.1.22", "255.255.255.255"));
EXPECT_TRUE(srs_ipv4_within_mask("0.0.0.1", "0.0.0.0", "0.0.0.0"));
EXPECT_TRUE(srs_ipv4_within_mask("10.2.13.243", "10.0.0.0", "255.0.0.0"));
}

if (true) {
EXPECT_FALSE(srs_ipv4_within_mask("192.168.1.1", "192.168.1.2", "255.255.255.255"));
EXPECT_FALSE(srs_ipv4_within_mask("192.168.1.3", "192.168.1.2", "255.255.255.255"));
EXPECT_FALSE(srs_ipv4_within_mask("220.1.1.22", "192.168.1.0", "255.255.255.0"));
EXPECT_FALSE(srs_ipv4_within_mask("220.1.1.22", "220.1.1.23", "255.255.255.255"));
EXPECT_FALSE(srs_ipv4_within_mask("220.1.1.22", "220.1.1.21", "255.255.255.255"));
EXPECT_FALSE(srs_ipv4_within_mask("192.168.1.2", "10.0.0.1", "255.255.255.255"));
}

if (true) {
EXPECT_STREQ("255.255.255.255", srs_get_cidr_mask("127.0.0.1").c_str());
EXPECT_STREQ("255.240.0.0", srs_get_cidr_mask("127.0.0.1/12").c_str());
}

if (true) {
EXPECT_STREQ("", srs_get_cidr_mask("my.custom.domain").c_str());
EXPECT_STREQ("", srs_get_cidr_mask("my.custom.domain/12").c_str());
EXPECT_STREQ("", srs_get_cidr_mask("127.0.0.1/invalid/netmask").c_str());
}

if (true) {
EXPECT_STREQ("127.0.0.1", srs_get_cidr_ipv4("127.0.0.1").c_str());
EXPECT_STREQ("127.0.0.1", srs_get_cidr_ipv4("127.0.0.1/12").c_str());
}

if (true) {
EXPECT_STREQ("", srs_get_cidr_ipv4("my.custom.domain").c_str());
EXPECT_STREQ("", srs_get_cidr_ipv4("my.custom.domain/12").c_str());
EXPECT_STREQ("", srs_get_cidr_ipv4("127.0.0.1/invalid/netmask").c_str());
}

if (true) {
SrsMessageArray h(10);
h.msgs[0] = new SrsSharedPtrMessage();
Expand Down

0 comments on commit 67ccd58

Please sign in to comment.