diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/mongo/db/auth/SConscript | 20 | ||||
-rw-r--r-- | src/mongo/db/auth/address_restriction.cpp | 38 | ||||
-rw-r--r-- | src/mongo/db/auth/address_restriction.h | 153 | ||||
-rw-r--r-- | src/mongo/db/auth/address_restriction_test.cpp | 167 | ||||
-rw-r--r-- | src/mongo/util/net/sockaddr.cpp | 4 | ||||
-rw-r--r-- | src/mongo/util/net/sockaddr.h | 2 |
6 files changed, 384 insertions, 0 deletions
diff --git a/src/mongo/db/auth/SConscript b/src/mongo/db/auth/SConscript index 968771fb8d2..68cb4fc0c30 100644 --- a/src/mongo/db/auth/SConscript +++ b/src/mongo/db/auth/SConscript @@ -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=[ diff --git a/src/mongo/db/auth/address_restriction.cpp b/src/mongo/db/auth/address_restriction.cpp new file mode 100644 index 00000000000..a92dd4f07bd --- /dev/null +++ b/src/mongo/db/auth/address_restriction.cpp @@ -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 diff --git a/src/mongo/db/auth/address_restriction.h b/src/mongo/db/auth/address_restriction.h new file mode 100644 index 00000000000..de504638cd8 --- /dev/null +++ b/src/mongo/db/auth/address_restriction.h @@ -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 diff --git a/src/mongo/db/auth/address_restriction_test.cpp b/src/mongo/db/auth/address_restriction_test.cpp new file mode 100644 index 00000000000..e4e6186206c --- /dev/null +++ b/src/mongo/db/auth/address_restriction_test.cpp @@ -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 diff --git a/src/mongo/util/net/sockaddr.cpp b/src/mongo/util/net/sockaddr.cpp index 6ca41d3d797..f9111744b7e 100644 --- a/src/mongo/util/net/sockaddr.cpp +++ b/src/mongo/util/net/sockaddr.cpp @@ -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: diff --git a/src/mongo/util/net/sockaddr.h b/src/mongo/util/net/sockaddr.h index 56332ae597f..0f32c48846d 100644 --- a/src/mongo/util/net/sockaddr.h +++ b/src/mongo/util/net/sockaddr.h @@ -89,6 +89,8 @@ struct SockAddr { return _isValid; } + bool isIP() const; + /** * @return one of AF_INET, AF_INET6, or AF_UNIX */ |