diff --git a/trunk/conf/full.conf b/trunk/conf/full.conf index c74b7aa09..1e975ade8 100644 --- a/trunk/conf/full.conf +++ b/trunk/conf/full.conf @@ -967,16 +967,20 @@ vhost security.srs.com { # default: off enabled on; # the security list, each item format as: - # allow|deny publish|play all| + # allow|deny publish|play all| # 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. diff --git a/trunk/src/app/srs_app_security.cpp b/trunk/src/app/srs_app_security.cpp index 690707581..af8c386c3 100644 --- a/trunk/src/app/srs_app_security.cpp +++ b/trunk/src/app/srs_app_security.cpp @@ -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: @@ -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: @@ -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: @@ -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: @@ -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: @@ -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: diff --git a/trunk/src/app/srs_app_security.hpp b/trunk/src/app/srs_app_security.hpp index f52e4b01a..7a2697c2a 100644 --- a/trunk/src/app/srs_app_security.hpp +++ b/trunk/src/app/srs_app_security.hpp @@ -12,6 +12,7 @@ #include #include +#include class SrsConfDirective; diff --git a/trunk/src/protocol/srs_protocol_utility.cpp b/trunk/src/protocol/srs_protocol_utility.cpp index a448f068d..88af17e7c 100644 --- a/trunk/src/protocol/srs_protocol_utility.cpp +++ b/trunk/src/protocol/srs_protocol_utility.cpp @@ -10,6 +10,7 @@ #include #endif +#include #include #include using namespace std; @@ -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; +} diff --git a/trunk/src/protocol/srs_protocol_utility.hpp b/trunk/src/protocol/srs_protocol_utility.hpp index a57cd63e8..6f7173697 100644 --- a/trunk/src/protocol/srs_protocol_utility.hpp +++ b/trunk/src/protocol/srs_protocol_utility.hpp @@ -112,5 +112,17 @@ std::string srs_join_vector_string(std::vector& 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 diff --git a/trunk/src/utest/srs_utest_rtmp.cpp b/trunk/src/utest/srs_utest_rtmp.cpp index 6eb3355bd..df453df5f 100644 --- a/trunk/src/utest/srs_utest_rtmp.cpp +++ b/trunk/src/utest/srs_utest_rtmp.cpp @@ -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();