Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Security: Enable CIDR for allow/deny play/publish #1753

Closed
wants to merge 6 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion trunk/conf/full.conf
Original file line number Diff line number Diff line change
Expand Up @@ -962,16 +962,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 @@ -87,6 +87,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:
if (rule->arg0() != "play") {
Expand All @@ -95,6 +98,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 @@ -105,6 +111,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 @@ -126,6 +135,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 @@ -135,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 SrsRtmpConnFMLEPublish:
case SrsRtmpConnFlashPublish:
Expand All @@ -145,6 +160,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 @@ -29,6 +29,7 @@
#include <string>

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

class SrsConfDirective;

Expand Down
152 changes: 152 additions & 0 deletions trunk/src/protocol/srs_protocol_utility.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -391,3 +391,155 @@ bool srs_is_ipv4(string domain)
return true;
}

uint32_t srs_ipv4_to_num(string ip) {
int a, b, c, d;
uint32_t addr = 0;

if (!srs_is_ipv4(ip)) {
return 0;
}

if (sscanf(ip.c_str(), "%d.%d.%d.%d", &a, &b, &c, &d) != 4) {
return 0;
}

addr = a << 24;
addr |= b << 16;
addr |= c << 8;
addr |= d;

return 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);

uint32_t net_lower = (network_addr & mask_addr);
uint32_t net_upper = (net_lower | (~mask_addr));

if (ip_addr >= net_lower && ip_addr <= net_upper) {
return true;
}
return false;
}

static struct CIDR_VALUE {
size_t length;
std::string mask;
} CIDR_VALUES[32] = {
{ .length = 1, .mask = "128.0.0.0" },
{ .length = 2, .mask = "192.0.0.0" },
{ .length = 3, .mask = "224.0.0.0" },
{ .length = 4, .mask = "240.0.0.0" },
{ .length = 5, .mask = "248.0.0.0" },
{ .length = 6, .mask = "252.0.0.0" },
{ .length = 7, .mask = "254.0.0.0" },
{ .length = 8, .mask = "255.0.0.0" },
{ .length = 9, .mask = "255.128.0.0" },
{ .length = 10, .mask = "255.192.0.0" },
{ .length = 11, .mask = "255.224.0.0" },
{ .length = 12, .mask = "255.240.0.0" },
{ .length = 13, .mask = "255.248.0.0" },
{ .length = 14, .mask = "255.252.0.0" },
{ .length = 15, .mask = "255.254.0.0" },
{ .length = 16, .mask = "255.255.0.0" },
{ .length = 17, .mask = "255.255.128.0" },
{ .length = 18, .mask = "255.255.192.0" },
{ .length = 19, .mask = "255.255.224.0" },
{ .length = 20, .mask = "255.255.240.0" },
{ .length = 21, .mask = "255.255.248.0" },
{ .length = 22, .mask = "255.255.252.0" },
{ .length = 23, .mask = "255.255.254.0" },
{ .length = 24, .mask = "255.255.255.0" },
{ .length = 25, .mask = "255.255.255.128" },
{ .length = 26, .mask = "255.255.255.192" },
{ .length = 27, .mask = "255.255.255.224" },
{ .length = 28, .mask = "255.255.255.240" },
{ .length = 29, .mask = "255.255.255.248" },
{ .length = 30, .mask = "255.255.255.252" },
{ .length = 31, .mask = "255.255.255.254" },
{ .length = 32, .mask = "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 @@ extern std::string srs_join_vector_string(std::vector<std::string>& vs, std::str
// Whether domain is an IPv4 address.
extern bool srs_is_ipv4(std::string domain);

// Convert an IPv4 from string to uint32_t. Extracted from https://www.stev.org/post/ccheckanipaddressisinaipmask
extern uint32_t srs_ipv4_to_num(std::string ip);

// Whether the IPv4 is in an IP mask. Extracted from https://www.stev.org/post/ccheckanipaddressisinaipmask
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

46 changes: 46 additions & 0 deletions trunk/src/utest/srs_utest_rtmp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3210,6 +3210,52 @@ 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"));
}

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