mirror of
https://github.com/facebook/sapling.git
synced 2024-10-09 08:18:15 +03:00
fbdb46f5cb
Reviewed By: chadaustin Differential Revision: D17872966 fbshipit-source-id: cd60a364a2146f0dadbeca693b1d4a5d7c97ff63
115 lines
3.2 KiB
C++
115 lines
3.2 KiB
C++
/*
|
|
* Copyright (c) Facebook, Inc. and its affiliates.
|
|
*
|
|
* This software may be used and distributed according to the terms of the
|
|
* GNU General Public License version 2.
|
|
*/
|
|
|
|
#include "eden/fs/service/ThriftPermissionChecker.h"
|
|
|
|
#include <thrift/lib/cpp2/server/Cpp2ConnContext.h>
|
|
#include "eden/fs/inodes/ServerState.h"
|
|
|
|
namespace {
|
|
/**
|
|
* Any user can call these methods.
|
|
*/
|
|
constexpr folly::StringPiece METHOD_WHITELIST[] = {
|
|
"BaseService.getCounter",
|
|
"BaseService.getCounters",
|
|
"BaseService.getRegexCounters",
|
|
"BaseService.getSelectedCounters",
|
|
};
|
|
|
|
/**
|
|
* A linear scan is faster than a non-lexical binary search until about 10
|
|
* entries, and faster than a hash (libstdc++ uses murmurhash32) lookup until
|
|
* about 30 entries, and a linear scan does not require a global constructor.
|
|
*
|
|
* All of the implementations I benchmarked finish in under 100 ns, so perhaps
|
|
* it doesn't matter.
|
|
*/
|
|
bool isWhitelisted(folly::StringPiece methodName) {
|
|
for (auto& name : METHOD_WHITELIST) {
|
|
if (methodName == name) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
} // namespace
|
|
|
|
namespace facebook {
|
|
namespace eden {
|
|
|
|
ThriftPermissionChecker::ThriftPermissionChecker(
|
|
std::shared_ptr<ServerState> serverState)
|
|
: serverState_{std::move(serverState)} {}
|
|
|
|
void* ThriftPermissionChecker::getContext(
|
|
const char* /*fn_name*/,
|
|
apache::thrift::TConnectionContext* connectionContext) {
|
|
return connectionContext;
|
|
}
|
|
|
|
void ThriftPermissionChecker::freeContext(
|
|
void* /*ctx*/,
|
|
const char* /*fn_name*/) {
|
|
// We don't own the connectionContext.
|
|
}
|
|
|
|
void ThriftPermissionChecker::preRead(void* ctx, const char* fn_name) {
|
|
if (isWhitelisted(fn_name)) {
|
|
return;
|
|
}
|
|
|
|
auto* requestContext = dynamic_cast<apache::thrift::Cpp2RequestContext*>(
|
|
reinterpret_cast<apache::thrift::TConnectionContext*>(ctx));
|
|
if (!requestContext) {
|
|
throw NotAuthorized{"unknown request context"};
|
|
}
|
|
auto* connectionContext = requestContext->getConnectionContext();
|
|
|
|
auto* peerAddress = connectionContext->getPeerAddress();
|
|
if (!peerAddress) {
|
|
throw NotAuthorized{"unknown peer address"};
|
|
}
|
|
|
|
if (AF_UNIX != peerAddress->getFamily()) {
|
|
throw NotAuthorized{
|
|
"Permission checking on non-unix sockets is not implemented"};
|
|
}
|
|
|
|
#ifdef _WIN32
|
|
// There is no way to retrieve peer credentials on Windows, so assume all
|
|
// AF_UNIX connections are okay.
|
|
return;
|
|
#else
|
|
auto maybePeerCreds = connectionContext->getPeerEffectiveCreds();
|
|
if (!maybePeerCreds) {
|
|
if (auto error = connectionContext->getPeerCredError()) {
|
|
throw NotAuthorized{folly::to<std::string>(
|
|
"error retrieving unix domain socket peer: ", *error)};
|
|
} else {
|
|
// Either not a unix domain socket, or platform does not support
|
|
// retrieving peer credentials.
|
|
throw NotAuthorized{"unknown peer user for unix domain socket"};
|
|
}
|
|
}
|
|
const auto& peerCreds = *maybePeerCreds;
|
|
|
|
uid_t processOwner = serverState_->getUserInfo().getUid();
|
|
|
|
if (peerCreds.uid == 0 || peerCreds.uid == processOwner) {
|
|
return;
|
|
}
|
|
|
|
throw NotAuthorized{folly::to<std::string>(
|
|
"user ", peerCreds.uid, " not authorized to invoke method ", fn_name)};
|
|
#endif
|
|
}
|
|
|
|
} // namespace eden
|
|
} // namespace facebook
|