SERVER-29168 ClientSourceRestriction

This commit is contained in:
Sara Golemon 2017-06-08 16:00:29 +00:00
parent 7e5ab840e8
commit 2725293934
6 changed files with 384 additions and 0 deletions

View File

@ -247,6 +247,26 @@ env.CppUnitTest(
]
)
env.Library(
target='address_restriction',
source='address_restriction.cpp',
LIBDEPS=[
'authentication_restriction',
'$BUILD_DIR/mongo/base',
'$BUILD_DIR/mongo/util/net/cidr',
],
)
env.CppUnitTest(
target='address_restriction_test',
source='address_restriction_test.cpp',
LIBDEPS=[
'address_restriction',
'$BUILD_DIR/mongo/base',
'$BUILD_DIR/mongo/util/net/cidr',
],
)
env.CppUnitTest('sasl_scramsha1_test',
'sasl_scramsha1_test.cpp',
LIBDEPS=[

View File

@ -0,0 +1,38 @@
/**
* Copyright (C) 2017 MongoDB Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* As a special exception, the copyright holders give permission to link the
* code of portions of this program with the OpenSSL library under certain
* conditions as described in each individual source file and distribute
* linked combinations including the program with the OpenSSL library. You
* must comply with the GNU Affero General Public License in all respects for
* all of the code used other than as permitted herein. If you modify file(s)
* with this exception, you may extend this exception to your version of the
* file(s), but you are not obligated to do so. If you do not wish to do so,
* delete this exception statement from your version. If you delete this
* exception statement from all source files in the program, then also delete
* it in the license file.
*/
#include "mongo/db/auth/address_restriction.h"
namespace mongo {
namespace address_restriction_detail {
constexpr StringData ClientSource::label;
constexpr StringData ClientSource::field;
} // address_restriction_detail
} // mongo

View File

@ -0,0 +1,153 @@
/**
* Copyright (C) 2017 MongoDB Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* As a special exception, the copyright holders give permission to link the
* code of portions of this program with the OpenSSL library under certain
* conditions as described in each individual source file and distribute
* linked combinations including the program with the OpenSSL library. You
* must comply with the GNU Affero General Public License in all respects for
* all of the code used other than as permitted herein. If you modify file(s)
* with this exception, you may extend this exception to your version of the
* file(s), but you are not obligated to do so. If you do not wish to do so,
* delete this exception statement from your version. If you delete this
* exception statement from all source files in the program, then also delete
* it in the license file.
*/
#pragma once
#include "mongo/base/status_with.h"
#include "mongo/bson/bsonelement.h"
#include "mongo/bson/bsonmisc.h"
#include "mongo/db/auth/restriction.h"
#include "mongo/db/auth/restriction_environment.h"
#include "mongo/util/net/cidr.h"
#include <string>
namespace mongo {
namespace address_restriction_detail {
using mongo::operator""_sd;
struct ClientSource {
static constexpr auto label = "Client source "_sd;
static constexpr auto field = "clientSource"_sd;
static auto addr(const RestrictionEnvironment& environment) {
return environment.getClientSource();
}
};
// Represents a restriction based on client or server address
template <typename T>
class AddressRestriction : public Restriction {
public:
/**
* Construct an AddressRestriction based on a CIDR spec
*/
explicit AddressRestriction(CIDR cidr) noexcept : _cidr(std::move(cidr)) {}
/**
* Construct an AddressRestriction based on a human readable subnet spec
*/
explicit AddressRestriction(const std::string& cidr) : _cidr(CIDR(cidr)) {}
/**
* If the given BSONElement represents a valid CIDR range,
* constructs and returns the AddressRestriction.
* Otherwise returns an error.
*/
static StatusWith<AddressRestriction<T>> parse(BSONElement from) noexcept {
auto cidr = CIDR::parse(from);
if (cidr.isOK()) {
return AddressRestriction<T>(std::move(cidr.getValue()));
}
return cidr.getStatus();
}
/**
* If the given string represents a valid CIDR range,
* constructs and returns the AddressRestriction.
* Otherwise returns an error.
*/
static StatusWith<AddressRestriction<T>> parse(const std::string& from) noexcept {
auto cidr = CIDR::parse(from);
if (cidr.isOK()) {
return AddressRestriction<T>(std::move(cidr.getValue()));
}
return cidr.getStatus();
}
/**
* Returns true if the Environment's client/server's address
* satisfies this restriction set.
*/
Status validate(const RestrictionEnvironment& environment) const noexcept override {
auto const addr = T::addr(environment);
if (!addr.isIP()) {
std::ostringstream s;
s << T::label << " is not an IP address: " << addr.getAddr();
return {ErrorCodes::AuthenticationRestrictionUnmet, s.str()};
}
if (!_cidr.contains(CIDR(addr.getAddr()))) {
std::ostringstream s;
s << T::label << " does not fall within: " << addr.getAddr();
return {ErrorCodes::AuthenticationRestrictionUnmet, s.str()};
}
return Status::OK();
}
/**
* Append to builder as string element with the human-readable CIDR range.
*/
void appendToBuilder(BSONObjBuilder* builder) const {
builder->append(T::field, _cidr.toString());
}
friend bool operator==(const AddressRestriction<T>& lhs, const AddressRestriction<T>& rhs) {
return lhs.equalityLens() == rhs.equalityLens();
}
friend bool operator!=(const AddressRestriction<T>& lhs, const AddressRestriction<T>& rhs) {
return !(lhs._cidr == rhs._cidr);
}
private:
auto equalityLens() const {
return std::tie(_cidr);
}
void serialize(std::ostream& os) const override {
os << "{\"" << T::field << "\": \"" << _cidr << "\"}";
}
CIDR _cidr;
};
} // namespace address_restriction_detail
using ClientSourceRestriction =
address_restriction_detail::AddressRestriction<address_restriction_detail::ClientSource>;
template <>
inline BSONObjBuilder& BSONObjBuilderValueStream::operator<<<ClientSourceRestriction>(
ClientSourceRestriction value) {
BSONObjBuilder b;
value.appendToBuilder(&b);
_builder->append(_fieldName, b.obj());
_fieldName = StringData();
return *_builder;
}
} // namespace mongo

View File

@ -0,0 +1,167 @@
/**
* Copyright (C) 2017 MongoDB Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* As a special exception, the copyright holders give permission to link the
* code of portions of this program with the OpenSSL library under certain
* conditions as described in each individual source file and distribute
* linked combinations including the program with the OpenSSL library. You
* must comply with the GNU Affero General Public License in all respects for
* all of the code used other than as permitted herein. If you modify file(s)
* with this exception, you may extend this exception to your version of the
* file(s), but you are not obligated to do so. If you do not wish to do so,
* delete this exception statement from your version. If you delete this
* exception statement from all source files in the program, then also delete
* it in the license file.
*/
#include "mongo/platform/basic.h"
#include "mongo/db/auth/address_restriction.h"
#include "mongo/unittest/unittest.h"
#include "mongo/util/net/sock.h"
#include "mongo/util/net/sockaddr.h"
namespace mongo {
namespace {
TEST(AddressRestrictionTest, toAndFromString) {
const struct {
std::string input;
std::string output;
} strings[] = {
{"127.0.0.1/8", "127.0.0.1/8"},
{"127.0.0.1", "127.0.0.1/32"},
{"::1", "::1/128"},
{"169.254.0.0/16", "169.254.0.0/16"},
{"fe80::/10", "fe80::/10"},
};
for (auto&& p : strings) {
{
const ClientSourceRestriction csr(CIDR(p.input));
std::ostringstream actual;
actual << csr;
std::ostringstream expected;
expected << "{\"clientSource\": \"" << p.output << "\"}";
ASSERT_EQUALS(actual.str(), expected.str());
}
}
}
TEST(AddressRestrictionTest, contains) {
enableIPv6(true);
const struct {
std::string range;
std::string address;
bool valid;
} contains[] = {
{"127.0.0.1/8", "0.0.0.0", false},
{"127.0.0.1/8", "0.0.0.1", false},
{"127.0.0.1/8", "126.255.255.255", false},
{"127.0.0.1/8", "127.0.0.0", true},
{"127.0.0.1/8", "127.0.0.1", true},
{"127.0.0.1/8", "127.0.0.127", true},
{"127.0.0.1/8", "127.0.0.128", true},
{"127.0.0.1/8", "127.0.0.255", true},
{"127.0.0.1/8", "127.255.255.255", true},
{"127.0.0.1/8", "127.127.128.1", true},
{"127.0.0.1/8", "127.128.127.0", true},
{"127.0.0.1/8", "128.0.0.1", false},
{"127.0.0.1/8", "169.254.13.37", false},
{"127.0.0.1/8", "255.0.0.1", false},
{"127.0.0.1/8", "255.255.255.254", false},
{"127.0.0.1/8", "255.255.255.255", false},
{"127.0.0.1", "126.255.255.255", false},
{"127.0.0.1", "127.0.0.0", false},
{"127.0.0.1", "126.0.0.1", false},
{"127.0.0.1", "127.0.0.1", true},
{"127.0.0.1", "127.0.0.2", false},
{"127.0.0.1", "127.0.0.127", false},
{"127.0.0.1", "127.0.0.128", false},
{"127.0.0.1", "127.0.0.255", false},
{"127.0.0.1", "127.1.0.1", false},
{"127.0.0.1", "127.128.0.1", false},
{"127.0.0.1", "128.0.0.1", false},
{"127.0.0.2/31", "127.0.0.0", false},
{"127.0.0.2/31", "127.0.0.1", false},
{"127.0.0.2/31", "127.0.0.2", true},
{"127.0.0.2/31", "127.0.0.3", true},
{"127.0.0.2/31", "127.0.0.4", false},
{"127.0.0.2/31", "127.0.1.1", false},
{"169.254.80.0/20", "169.254.79.255", false},
{"169.254.80.0/20", "169.254.80.0", true},
{"169.254.80.0/20", "169.254.95.255", true},
{"169.254.80.0/20", "169.254.96.0", false},
{"169.254.0.160/28", "169.254.0.159", false},
{"169.254.0.160/28", "169.254.0.160", true},
{"169.254.0.160/28", "169.254.0.175", true},
{"169.254.0.160/28", "169.254.0.176", false},
{"169.254.0.0/16", "8.8.8.8", false},
{"169.254.0.0/16", "127.0.0.1", false},
{"169.254.0.0/16", "169.254.0.0", true},
{"169.254.0.0/16", "169.254.0.1", true},
{"169.254.0.0/16", "169.254.31.17", true},
{"169.254.0.0/16", "169.254.255.254", true},
{"169.254.0.0/16", "169.254.255.255", true},
{"169.254.0.0/16", "240.0.0.1", false},
{"169.254.0.0/16", "255.255.255.255", false},
{"::1", "::", false},
{"::1", "::0", false},
{"::1", "::1", true},
{"::1", "::2", false},
{"::1", "::1:0", false},
{"::1", "::1:0:0", false},
{"::1", "1::", false},
{"::1", "1::1", false},
{"::1", "8000::1", false},
{"::1/96", "::1", true},
{"::1/96", "::1:0:0", false},
{"::1/96", "::DEAD:BEEF", true},
{"::1/96", "DEAD::BEEF", false},
{"::2/127", "::0", false},
{"::2/127", "::1", false},
{"::2/127", "::2", true},
{"::2/127", "::3", true},
{"::2/127", "::4", false},
{"::2/127", "::5", false},
{"::1/128", "::", false},
{"::1/128", "::0", false},
{"::1/128", "::1", true},
{"::1/128", "::2", false},
{"::1/128", "8000::", false},
};
for (const auto& p : contains) {
const SockAddr dummy;
const SockAddr addr(p.address, 1024);
const RestrictionEnvironment rec(addr, dummy);
const RestrictionEnvironment res(dummy, addr);
const ClientSourceRestriction csr(CIDR(p.range));
ASSERT_EQ(csr.validate(rec).isOK(), p.valid);
ASSERT_FALSE(csr.validate(res).isOK());
}
}
} // namespace
} // namespace mongo

View File

@ -138,6 +138,10 @@ SockAddr::SockAddr(struct sockaddr_storage& other, socklen_t size)
_hostOrIp = toString(true);
}
bool SockAddr::isIP() const {
return (getType() == AF_INET) || (getType() == AF_INET6);
}
bool SockAddr::isLocalHost() const {
switch (getType()) {
case AF_INET:

View File

@ -89,6 +89,8 @@ struct SockAddr {
return _isValid;
}
bool isIP() const;
/**
* @return one of AF_INET, AF_INET6, or AF_UNIX
*/