summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorSara Golemon <sara.golemon@mongodb.com>2017-06-08 16:00:29 +0000
committerSara Golemon <sara.golemon@mongodb.com>2017-06-20 22:19:35 -0400
commit2725293934a7d67726309eacc91e6d40926ee02a (patch)
tree227b3517199d9f1c010e1d0b9edf2a7ca91d3ed5 /src
parent7e5ab840e8aa7627d5969ca53ee46d3fb615bc6a (diff)
downloadmongo-2725293934a7d67726309eacc91e6d40926ee02a.tar.gz
SERVER-29168 ClientSourceRestriction
Diffstat (limited to 'src')
-rw-r--r--src/mongo/db/auth/SConscript20
-rw-r--r--src/mongo/db/auth/address_restriction.cpp38
-rw-r--r--src/mongo/db/auth/address_restriction.h153
-rw-r--r--src/mongo/db/auth/address_restriction_test.cpp167
-rw-r--r--src/mongo/util/net/sockaddr.cpp4
-rw-r--r--src/mongo/util/net/sockaddr.h2
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
*/